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 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.

TEAL Versioning

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.

EditorImages/2021/04/12 17:55/stateful.png

Figure 1 - Stateful Smart Contract Transaction Call Architecture

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 NumApplications and Applications and are available for use with a single transaction using the txn opcode or for grouped transactions using the gtxn opcode.

// 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 Assets property.

// 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 GlobalNumUint, GlobalNumByteSlice, LocalNumUint, and LocalNumByteSlice.

//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.

dig 4

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 (getbit and 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
==

The 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 getbyte and setbyte opcodes. These opcodes only function with byte slices and do not support uint64s.

The 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'
==

The 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 pushint and pushbytes respectively.

// 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 gtxn and 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, gtxns and 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

Conclusion

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.