Create Publication

We are looking for publications that demonstrate building dApps or smart contracts!
See the full list of Gitcoin bounties that are eligible for rewards.

Article Thumbnail

Introducing Algorand Virtual Machine: AVM 0.9 Release

Article Quick Glance

Algorand Virtual Machine (AVM) 0.9

  • The AVM now supports looping and subroutines.
  • When combing contract calls in Atomic Transfers, you can share data between them!
  • One party can now pay the transaction fees of another, using pooled transaction fees.
  • The AVM now uses a dynamic opcode cost evaluation, which should allow larger more modular smart contracts.
  • Stateful Smart Contracts can now be much larger.
  • Application transaction array indexes are now more versatile.
  • Global and local state KV pairs can now be customized for maximizing storage.
  • Assets now support larger URLs.
  • 512 bit math operations are now supported using byteslice arithmetic.
  • Many additional opcodes have been added to TEAL.

Today’s Algorand software release 2.7.1 represents a major improvement to the Algorand blockchain with the introduction of the Algorand Virtual Machine (AVM) which supports a Turing-complete language! In addition to now allowing looping and recursion, the AVM offers developers the ability to create subroutines, supports larger program sizes, and many new additional opcodes. The protocol has also been enhanced and includes many new features. This post will summarize many of these features and capabilities.

Looping and Sub Routines

AVM supports the TEAL language and with this release, the version has been upgraded to version 4 of the specification. In prior versions of TEAL branching-forward opcodes were provided to support skipping sections of non-relevant code. With this release, all three branching opcodes (b, bnz and bz) support branching in both directions. This allows loops to be performed in your smart contracts.

#pragma version 4
// loop 1 - 10
// init loop var
int 0 
loop:
int 1
+
// implement loop code
// ...
// check upper bound
int 10
<=
bnz loop

In addition to looping, the AVM also supports the two new opcodes (callsub and retsub) that provide subroutine functionality. When callsub is called, the current location in the program is saved and immediately jumps to the label passed to the opcode. When the retsub opcode is called, the AVM will resume execution at the previous saved point.

#pragma version 4
// jump to main loop
b main

// subroutine
my_subroutine:
// implement subroutine code
// with the two args
retsub


main:
int 1
int 5
callsub my_subroutine
return

Using loops with subroutines allows more sophisticated smart contracts to be written. For example, the following contract uses a simplified calculation to check a number’s primality.

#pragma version 4
// Simplistic primality test
// Uses this formula
// number_to_check < 2 fail
// number_to_check == 2 pass
// number_to_check % 2 == 0 fail
// for( i=3 < sqrt(number_to_check); i+=2){
//      if( number_to_check % i == 0) fail;
// }


// check if just creating app 
txn ApplicationID
bz creation

// load number_to_check
txn ApplicationArgs 0
btoi // it must be an integer
dup // make a copy
store 0 // store number_to_check
sqrt // take the square root of the number
store 1 // save the square root of the number
load 0

// number_to_check == 2 pass
int 2 
==
bnz finished

// number_to_check < 2 fail
load 0
int 2
<
bnz fail

// number_to_check % 2 == 0 fail
load 0 
int 2
%
int 0
==
bnz fail

// branch to main loop
b main

// subroutine
// if( number_to_check % i == 0) fail
// i is on top of stack
check_prime:
load 0 
swap
%
int 0
==
bnz fail
retsub

// main loop
// for( i < sqrt(number_to_check); i+=2){}
main:
int 1 // initialize i
loop:
int 2
+
dup //make a copy of i
// call subroutine for specific i
callsub check_prime
// i < sqrt(number_to_check)
dup // keep i on stack
load 1 // load sqrt of number_to_check
<=
bnz loop
// the number is a prime
return 

creation:
int 1
return
finished:
int 1
return
fail:
int 0
return

Sharing Data Between Contracts

Algorand’s AVM gives smart contracts access to scratch space that can be used to temporarily store values, while the contract is executing. TEAL has been enhanced to allow other contracts to read this scratch space. Scratch space can only be read by other transactions that the specific transaction is grouped with atomically. Additionally, because grouped transactions execute in the order they are grouped, contracts can not read scratch space for transactions that occur after the current contract.

EditorImages/2021/06/29 18:10/scratch.png

This operation can be performed by using one of two new opcodes (gload and gloads). With the gload opcode, the specific transaction to read and the slot number must be passed to the command. The gloads opcode will use the last value on the stack as the transaction index and must be passed the slot number to read.

// read the first 
// transactions 10th
// slot of scratch space
gload 0 10
// read the second
// transactions 20th
// slot of scratch space
int 1
gloads 20

Pooled Transaction Fees

The minimum transaction fee on Algorand is currently 1000 micro Algos. This means that any user wishing to submit a transaction must pass this amount with their submission. In some instances, some dapps may want to cover the cost of their clients’ transactions. With this release, the protocol now supports pooled fees where one transaction can pay the fees of other transactions within an atomic group. For atomic transactions, the protocol sums the number of transactions and calculates the total amount of required fees, then calculates the amount of fees submitted by all transactions. If the collected fees are greater than or equal to the required amount, the transaction fee requirement will be met.

EditorImages/2021/06/29 18:13/pooled.png

The example below illustrates grouping two transactions, setting fees in one transaction for both transactions.

#create two transactions (smart contract call and asset xfer)
$ goal app call --app-id [APPID] --fee 2000  --app-arg "str:check"  --from [ACCOUNT1] --out=unsginedtransaction1.tx
$ goal asset send -a 1 --fee 0 --assetid [ASSETID] -f [ACCOUNT1] -t [ACCOUNT2]  --out=unsginedtransaction2.tx


# combine and group the two transactions
$ cat unsginedtransaction1.tx unsginedtransaction2.tx  > combinedtransactions.tx
$ goal clerk group -i combinedtransactions.tx -o groupedtransactions.tx 

# Split grouped files and sign individual ones
$ goal clerk split -i groupedtransactions.tx -o split.tx 
$ goal clerk sign -i split-0.tx -o signout-0.tx
$ goal clerk sign -i split-1.tx -o signout-1.tx 

# combine signed txs and send
$ cat signout-0.tx signout-1.tx > signout.tx
$ goal clerk rawsend -f signout.tx

Dynamic Opcode Cost Evaluation

In order to maintain high transaction throughput, Algorand limits the amount of processing time a smart contract can use while executing. Previously this was based on a static analysis of the code where each opcode has an associated cost. These values were totaled and if the amount used was greater than the limit, the contract could not be deployed.

If using version 4 of TEAL or higher, opcode costs are now evaluated dynamically instead of statically. Version 3 or lower is still evaluated statically. The current limits are set to 20000 for stateless and 700 for each the approval and clear program of a stateful contract.

With the new capabilities of the AVM, Algorand moved to a dynamic opcode evaluation algorithm. Because of subroutines and loops, many parts of a smart contract will not be executed on every call. With the dynamic evaluation of the opcodes actually executed, developers can write larger, more modular smart contracts.

Stateful Smart Contract Limits Increased

Stateful smart contracts in prior versions of the protocol were limited to 1KB each for the approval and clear programs. With this release, the max length of these two programs has been combined. Meaning you have 2KB for the total of both programs by default. In addition, you may request up to 3 ExtraProgramPages of 2KB each, which results in an 8KB limit for both programs. Allocating extra program pages will increase the minimum balance requirement for creating the application. Requesting additional program pages is available when creating the stateful smart contract application using goal and the SDKs.

goal app create --creator [CREATOR_ACCOUNT]  --approval-prog ./mysmartcontract.teal --global-byteslices 0 --global-ints 0 --local-byteslices 0 --local-ints 0  --clear-prog ./myclear.teal  --extra-pages 2

Transaction Array Changes

Algorand stateful smart contracts are referred to as Applications. When deployed they are referenced by an Application ID. These contracts are communicated with by using Application Transactions. The primary Application Transaction provides additional information that can be passed and processed by the stateful smart contract’s TEAL code. On a per-transaction basis, a set of arrays can be passed with the transaction, which instructs the protocol to load additional data for use in the contract. These arrays are the application array, the accounts array, the assets array, and the arguments array. The application array is used to pass other stateful smart contract IDs that the called smart contract may need to use in a lookup capacity. The accounts array allows additional accounts to be passed to the contract for balance information and local storage. The assets array is used to pass a list of asset IDs that can be used to retrieve configuration and asset balance information. The arguments array is used to pass standard arguments to the contract. With this release, no changes have been made to the arguments array. It is still limited to 16 arguments with a size limit of 2k for the total of arguments. The other three arrays’ size limits have been changed. In prior versions, the accounts array was limited to 4 accounts, the assets array and the application array were limited to 2 each. The AVM still limits the accounts array to no more than 4, the assets and application arrays combined and totaled with the accounts array can not exceed 8. For example, you can have 8 applications in the array as long as you do not pass anything in the accounts or assets arrays with the specific transaction.

EditorImages/2021/06/29 18:26/arrays.png

When accessing any of these arrays within your TEAL code, you normally specify an index into those arrays. With TEAL version 4 and above you can specify a specific value as well. With the accounts array, you can specify a specific address and with applications and assets, you can specify the specific ID (uint64). Note that when specifying the specific values you must still verify the account, asset, or application is actually in the array. For example, using the opcode balance an accounts Algo balance can be checked in the following ways.

int 1 // first account in the accounts array
balance
int 1000000 
> // greater than one Algo

// specify the address of an account that is in the accounts array
addr QMDK7R4W54DKR7ISCAOQSBWVEDI2NYD35VO36BVQFBZ2BR3WU3BURY35QE
balance
int 1000000 
> // greater than one Algo
&&

More Versatile Global and Local Storage

Algorand stateful smart contracts have the capability to read and write storage variables globally for the contract and locally for accounts that have opted into the stateful smart contract. Currently, these are limited to 64 key-value pairs for global state and 16 key-value pairs for every account opted into the contract. Each of these key-value pairs is limited to 64 bytes for the key and 64 bytes for the value. For many applications, this limitation is not an ideal usage scenario. This limitation has now been altered to allow the combination of key-value pairs to consume 128 bytes. This allows developers to create smaller key names and have more storage for the value of the key. The key is still restricted to 64 bytes or less.

Asset URL Change

When creating an Algorand Standard Asset (ASA), the creator can specify an asset URL. This is often used to reference off-chain resources such as IPFS resources. This field had been limited to 32 bytes which is not ideal for many off-chain links. With this release, this limit is increased to 96 bytes allowing much longer URLs.

Knowable Creatable IDs

The Algorand Protocol assigns an identifier (ID) when creating Algorand Standard Assets (ASA) or Stateful Smart Contracts. These IDs are used to refer to the asset or the contract later when either is used in a transaction or a call to the stateful smart contract. Because these IDs are assigned when the asset or the contract is created, the ID is not available until after the creation transaction is fully executed. Many times in a contract, you may need the ID of another contract or asset that is being created within an atomic group of transactions. For example you may want to store the asset ID or another smart contract ID in the contract’s state for later usage.

This operation can be performed by using one of two new opcodes (gaid and gaids). With the gaid opcode, the specific transaction to read must be passed to the command. The gaids opcode will use the last value on the stack as the transaction index.

// Get the created id of the asset created in the first tx
gaid 0
// Get the created id of the asset created in the second tx
int 1
gaids

Byteslice Arithmetic

A new set of opcodes are available that allow mathematical operations on byte slices. The byte slices are interpreted as big-endian unsigned integers. Each operand can be up to 64 bytes which can produce a 512-bit unsigned integer. These opcodes (b+, b-, b*, b/, b%, b<, b>, b<=, b>=, b== and b!=) support addition, subtraction, multiplication, division, modulo, and a set of comparison opcodes.

// byte slice 1
byte 0x12f0
// byte slice 2
byte 0x3C
// multiply
b*
byte 0x047040
// compare byte slices
b==
assert

Additionally, opcodes (b\, b&, b^, b~) are available to perform bitwise OR, AND, XOR, and INVERT operations. The AVM also provides an opcode (bzero) to produce a zeroed-out byte slice of a specific length and the bitlen opcode that returns the highest order bit set.

byte 0xFFFE
dup
// bitwise invert
b~ 
// bitwise and
b&
// 2 byte empty byte array
int 2
bzero
b==
assert
// determine highest bit set
byte 0xFFFE
bitlen
int 16
==
assert

Additional Mathematical Opcodes

The divmodw opcode is now available to support 128 bit numbers produced by mulw and addw opcodes.

#pragma version 4
// Demonstrate a "function" that expects three u64s on stack,
// and calculates B*C/A. (Following opcode documentation
// convention, C is top-of-stack, B is below it, and A is
// below B.

// check if just creating app 
txn ApplicationID
bz creation

// load A
txn ApplicationArgs 0
btoi // it must be an integer
store 0 // store A
// load B
txn ApplicationArgs 1
btoi // it must be an integer
store 1 // store B
// load C
txn ApplicationArgs 2
btoi // it must be an integer
store 2 // store C

b main

muldiv:
mulw        // multiply B*C. puts TWO u64s on stack
int 0       // high word of C as a double-word
dig 3       // pull C to TOS
divmodw
pop     // pop unneeded remainder low word
pop     // pop unneeded remainder high word
swap
int 0
==
assert  // ensure high word of quotient was 0
swap    // bring C to surface
pop     // in order to get rid of it
retsub

main:
load 0
load 1
load 2
callsub muldiv
int 16
== 
return

creation:
int 1
return
finished:
int 1
return
fail:
int 0
return

The AVM also supports several new mathematical opcodes including exp and expw which allow variable A to be raised to the Bth power. The shl and shr opcodes for shifting bits left and right and the sqrt opcode that returns the largest integer that is less than or equal to the argument squared.

Stateful Smart Contract Minimum Balance Calculation

With the increase in the allowable size of stateful smart contracts, a new minimum balance calculation is going into effect. The formula for calculating this minimum balance is shown below.

100000(1+ExtraProgramPages)+ (28500)schema.NumUint + (50000)*schema.NumByteSlice

Any user that opts into the stateful smart contract will have their minimum balance increased by the following amount.

100000 + (28500)schema.NumUint + (50000schema.NumByteSlice)

The opt-in minimum balance calculation has not changed in this release.