AVM 8 New Features
Algorand has now released Boxes, which give applications immense storage capability and versatility! Boxes represent a huge amount of work that the engineering team has put into making Algorand the best blockchain out there. This feature is the primary feature released with version 8 of the AVM and it is fully detailed with code samples in this post. In addition to boxes, a few other things have been released with AVM 8. This post explains these additional opcodes.
Frame Opcodes to simplify subroutines
The AVM has supported calling subroutines since version 4. While this feature is very handy when building an application, often it is difficult to deal with passing parameters and handling return values within a TEAL program. The primary concern is setting up the stack for parameters that are passed to the subroutine and handling the return values in the subroutine with values that have been added to the stack while executing the subroutine. In an effort to improve this, the AVM has been enhanced with several new opcodes to better support subroutines. These opcodes make use of the frame which is populated when a subroutine is called. The frame holds the height of the stack when the subroutine was called, the number of arguments a subroutine expects, and the number of results to be returned. Calling code pushes parameters to the stack before calling the subroutine. Within the subroutine, the code can easily access the parameters and add additional values to the stack. When returning all the added stack values and parameters will be automatically cleared leaving only the expected return values. This allows the stack to revert back to the original stack with added return values with no additional cleanup code.
If you are using PyTeal, frames are handled automatically by the library.
The opcode for defining parameters or return values is called
proto. The opcode takes two immediate arguments (A and R) which represent the number of arguments and the number of expected return values. A and R are defined as uint8’s meaning up to 255, but frames only support up to 128 parameters and 127 return values. The
proto opcode should be the first opcode in a subroutine. Defining a simple routine may look something like:
my_subroutine: proto 2 1 //passes two arguments and returns 1 value
This informs the AVM that two parameters must be loaded on the stack before calling the subroutine. The subroutine must push an additional parameter on the stack as the return value before returning to execute properly. If the subroutine pushes even more items onto the stack during execution, they will be automatically removed when the subroutine returns, leaving only the one return value.
The frame parameters and return values can be accessed using the new
frame_dig opcode which takes a signed int8 (-128 to 127) as an immediate argument.
frame_dig -1 will access the last parameter placed on the stack,
frame_dig -2 will access the second to last parameter placed on the stack.
frame_dig 0 should be the last return value but will be empty until a value is pushed to the stack in the subroutine. Calling without a valid value on the stack will fail the application call. The following is an example simple subroutine that multiplies two numbers.
multiply: proto 2 1 frame_dig -1 frame_dig -2 * retsub
After the above subroutine is called the two parameters are automatically removed from the stack and one return value is moved down to replace them, cleaning up everything that was used to call the subroutine. In the above example after the multiplication occurs
frame_dig 0 will access the return value.
frame_dig can be used to read existing frame values,
frame_bury can be used to replace/modify an existing value in the frame. It takes one parameter which is a signed int 8 and is similar to dig, but instead of reading a value, it takes the value on the top of the stack and replaces it in the frame at the proper location. For example, inside a subroutine that takes at least one parameter
int 5 frame_bury -1
Would replace the last parameter pushed onto the stack before calling this subroutine.
New Account Parameter Values
Version 6 of TEAL introduced the opcode to retrieve a couple of account values. For example, to retrieve an account’s minimum balance the following TEAL could be used.
txn sender acct_params_get AcctMinBalance
This opcode could be used to check to see if the account had been rekeyed or to check the Algo balance of the account. AVM 8 adds many new values that can be retrieved using this opcode. This list includes the total amount of Global and Local state the account has allocated, the number of apps created or opted into, the number of assets created or opted into, the number of boxes created, and the total amount of storage for created boxes. For example, the total number of boxes created by a contract can quickly be checked by using the following TEAL.
global CurrentApplicationAddress acct_params_get AcctTotalBoxes
The full list of available values is listed in the opcode documentation.
AVM8 offers two new opcodes for branching to labels based on a value. These are the
match opcodes. Each of these opcodes is followed by a list of immediate branch labels. The
switch opcode compares the integer value on the top of the stack and chooses the label that matches its numeric order (zero-based). For example:
int 2 switch label1 label2 label3 label1: . label2: . label3: .
The switch statement would jump to label3 because it is the third in the list. If the value on top of the stack is more than the number of labels, the program will continue executing on the next line.
match opcode compares the value on the top of the stack to a variable number of values lower in the stack and selects the nth label depending on how deep in the stack a match is made. The deepest match will jump to the 0th label. These can be integers or string matches. If no matches are found, execution continues on the line following the
match opcode is great for branching on a method selector when used with ABI methods.
//get method selector method "add(uint8,uint8)uint16" method "subtract(uint8,uint8)uint8" method "multiply(uint8,uint8)uint16" txna ApplicationArgs 0 //Abi method selector match add subtract multiply divide err // fail if selector doesn’t match add: . subtract: . multiply: . divide: .
New opcodes have been added for manipulating values on the stack. These include the
and ‘dupn opcodes. Each of these opcodes is followed by an integer. The
popn opcode can be used to pop multiple items off the stack. For example:
int 1 int 2 int 3 popn 2
popn 2 opcode in this example will pop off the 2 and 3 from the top of the stack. The
dupn opccode will copy what is on the top of the stack a number of times.
int 1 dupn 2
The TEAL in this example will leave three integers with a value of 1 on the stack. The
bury opcode will pop the top value off the stack and push it into the stack.
int 1 int 2 int 3 int 4 bury 3
The above TEAL will result in a stack with the following values.
4 2 3