Introducing TEAL Version 3
The latest version of TEAL is now in the process of being pushed to MainNet. TEAL which is an acronym for Transaction Execution and Approval Language is the primary smart contract language adopted by the Algorand blockchain.
Changes to PyTeal, which is a Python library that compiles to TEAL, are underway and should be available soon. The Reach developer framework now fully supports TEAL version 3, allowing developers to focus on the business logic of an application. The framework then automatically generates formally verified TEAL contracts.
This article explains the changes that TEAL 3 brings to the developer community.
For more information on TEAL see the developer documentation.
As with TEAL Version 2, developers must specify which version of TEAL their current contracts adhere to. This is done in the first line of code in a TEAL program. For example:
// Teal Version 2 #pragma Version 2 // Teal Version 3 #pragma Version 3
If this line is omitted, the Algorand blockchain will assume the contract is using TEAL version 1.
Addition of Transaction and Global Properties
The following sections discuss new transaction properties and one new global variable available in TEAL version 3.
Interacting with Additional Applications
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.
Any application can examine the global state of up to two additional smart contracts per transaction call. The process to do this was described in a previous article.
This is achieved by passing the additional stateful smart contract’s application IDs with the transaction call to the stateful smart contract. In TEAL this is referred to as the Application Array or ForeignApps Array. Currently within TEAL, the developer must know prior to writing the smart contract code how many additional applications are expected to be passed into the contract call.
TEAL Version 3 now provides two additional transaction properties that are available to determine the number of passed application IDs and also provide a convenience property for accessing any passed Application’s ID. These properties are
Applications and are available for use with a single transaction using the
txn opcode or for grouped transactions using the
// Verify current transaction has two Applications // passed with the transaction txn NumApplications int 2 == // Check the second transaction in a group to verify // it has only one application in its application array gtxn 1 NumApplications int 1 == // Check that the first foreign Application // passed in has an Application ID equal to 123456 txn Applications 1 int 123456
For more information on reading global state from other Applications see the developer documentation.
Interacting with Assets
Similar to interacting with additional applications, stateful smart contracts can also interact with up to two assets per transaction call. These asset IDs are passed in the Assets array and with TEAL version 3, developers can reference the number of assets passed using the
NumAssets transaction property and retrieve the AssetID using the
// Verify current transaction has two Assets // passed with the transaction txn NumAssets int 2 == // Check the second transaction in a group to verify // it is passed the Asset ID 123456 as the first asset // in the assets array gtxn 1 Assets 0 int 123456 ==
For more information on interrogating assets in stateful smart contracts see the developer documentation.
Additional Global Variable
In prior versions of TEAL, if a developer wanted to only allow the creator of the contract to do something like possibly delete or update the contract, they would have to store the creator’s address in Global state. Global state is limited and this also required additional code to store and retrieve this state to make use of it. TEAL version 3 now provides an additional Global variable available within a stateful smart contract that records the creator’s address. The following TEAL can be used to check the sender of a transaction is the creator of the contract.
txn Sender global CreatorAddress ==
Global and Local State Usage Checks
When creating a stateful smart contract the Application transaction must specify the number of global byte slices and integers that the smart contract is reserving. Additionally, the transaction must specify the number of local byte slices and integers that will be reserved for any account that opts into the stateful smart contract. Currently, TEAL developers do not have access to these values. With TEAL version 3 developers can examine these creation values with the addition of 4 transaction properties
//this contract is reserving 5 global unsigned integers txn GlobalNumUint int 5 ==
For more information on creating stateful smart contracts see the developer documentation.
New TEAL Opcodes
TEAL version 3 provides additional opcodes that offer many new conveniences for developers. These changes are detailed below.
The assert opcode
The assert opcode is provided to allow immediately failing a transaction if the top of the stack is not a positive integer value. This call is available for both stateful and stateless smart contracts. The assert also pops this value off the stack in the process.
// Assuming an integer argument is passed in, fail if 0 // stateful contract example txna ApplicationArgs 0 btoi assert
The swap opcode
The swap opcode allows the top two elements on the stack to be switched. This is often required to setup parameters for additional opcodes.
// Teal Version 2 incrementing a global integer byte "Total" app_global_get gtxn 1 Amount + store 1 byte "Total" load 1 app_global_put // Teal Version 3 byte "Total" app_global_get gtxn 1 Amount + byte "Total" swap app_global_put
The dig n opcode
The dig n opcode allows TEAL developers to make a copy of any value on the stack and move it to the top of the stack. For example, if there is an integer 5 slots down in the stack, the following TEAL can be used to make a copy and place at the top of the stack. Keep in mind that it makes a copy of the nth slot, leaving the original value where it is as well.
The bit opcodes
TEAL version 3 now offers support for getting and setting specific bits. This is an extremely important opcode to facilitate bit mechanics and for some mathematical operations. Two opcodes (
setbit ) are supplied to handle getting a specific bit or setting a specific bit.
// check that the 6th bit is set in a unit64 int 235 // target int 6 // bit to get getbit int 1 ==
setbit opcode takes three parameters, the target value, the bit to set, and the value to set it to. Valid values for the last parameter are 0 or 1. Any other values will cause a panic and an unsuccessful return of the contract.
// global byte slices can store up to 64 bytes, set // the 400th bit to 1 // this will panic if there is no 400th bit int 0 byte "myglobal64byteglobal" app_global_get_ex assert int 400 // bit to set int 1 // value to set it to setbit // hex values supported int 0x0000 // target int 3 // bit to set int 1 // value to set it to setbit int 0x0008 == byte 0xfffff0 int 21 // target bit int 1 // value to set it to setbit byte 0xfffff4 ==
The byte opcodes
Currently TEAL supports byte slices that can be up to 64 bytes. These byte slices can be stored as global or local storage values and can be manipulated on the stack. Often developers may want to access or alter one of these bytes individually. TEAL version 3 now supports the
setbyte opcodes. These opcodes only function with byte slices and do not support uint64s.
getbyte opcode accepts two arguments, the target byte slice and the byte to retrieve. These arguments are popped from the stack and the specific byte is pushed to the top of the stack. Trying to retrieve a byte that does not exist will cause a panic and an unsuccessful return of the contract.
// Get the third byte in the following string byte "Test the getbyte opcode" // target int 3 // byte to retrieve getbyte int 116 // ASCII value for 't' ==
setbyte opcode call takes three arguments, the target byte slice, the byte to set, and the value to set it to. These are popped from the stack and the new byte slice is pushed to the top of the stack
// Change the third byte in the following byte slice to ASCII 'i' byte "john" // target int 2 // byte to set int 105 // value to set it to "i" setbyte byte "join" ==
The select opcode
TEAL version 3 also now supports conditional selection of results. This can be done with the opcode
select. This opcode expects three arguments to be passed to it and inspects the last argument, which will be on top of the stack. If this value is not equal to 0 the second argument on the stack is pushed to the top of the stack. If the value is 0 the third value on the stack is pushed to the top of the stack. All three initial arguments are popped from the stack and the end result is just the selected return value.
int 1 // 0 value selection byte "this is a select test" // not equal to 0 selection int 1 // condition to check select // finishes with the byte string on the top of the stack
Performance improvement when loading uint64 and byte slices
Teal Version 3 also provides optimized opcodes for pushing uint64s and byte slices to the stack. These two opcodes are
// load int 5 onto the top of the stack pushint 5 // load byte slice onto the top of the stack pushbytes "this is a byte slice"
The min_balance opcode
Often transactions fail because the result of a transaction would reduce an account below the minimum balance required on the Algorand blockchain. This minimum balance is determined by the number assets and applications the account is currently opted into as well as a minimum protocol balance to appear in the ledger. Often when working with smart contracts it is valuable to have this calculated value available within a smart contract to handle this condition. TEAL version 3 now has the
min_balance opcode available in stateful contracts that allows checking the minimum balance of any account in the accounts array with application call. This opcode takes one parameter which is the index into the accounts array (int 0 is the sender of the application call). The opcode pops this value from the stack and pushes the minimum balance for that account onto the top of the stack. This value is specified in microAlgos.
// load application call senders balance int 0 balance // load a spend transaction from the same sender // and subtract gtxn 1 Amount - // load second transaction fee and subtract gtxn 1 Fee - // load first transaction free and subtract txn Fee - // get senders min balance int 0 min_balance // verify result of subtractions is greater than or equal to min balance >=
The gtxns and gtxnsa opcodes
TEAL currently provides the
gtxna opcodes to access various transaction properties within a group of transactions. The specific transaction is specified after the opcode, for example
gtxn 1 Amount references the second transaction’s amount property. The
gtxna opcode provides access to many of the parameters that can be arrays, like the application arguments array for a stateful smart contract. For example
gtxna 0 ApplicationArgs 0 will push the first argument for the first transaction to the top of the stack.
TEAL now provides two new opcodes to reference group transactions using the stack instead of specifying them in the opcode command. These opcodes,
gtxnsa function very similar to the previous call but allow for a level of indirection, and makes it possible to write LogicSigs that are more composable. For example, with this addition a LogicSig can refer to “the following transaction in the group” rather than specifying the exact slot.
The two previous examples are illustrated below using TEAL version 3. Finally getting the next transaction in a group’s amount is also shown.
// get the amount of the second transaction int 1 gtxns Amount // get the first argument of the first transaction // in a stateful smart contract int 0 gtxnsa ApplicationArgs 0 // Assume current transaction depends on the transaction // that is next in the group // get current group index // add one and then get that transaction amount txn GroupIndex int 1 + gtxns Amount
Algorand is continuously updating and improving the protocol and our tools to better support blockchain developers. We hope you find these changes helpful and useful. We are currently working on more exciting features that will be available soon! Make sure to provide your feedback through github issues and the many social channels dedicated to our community of developers.