Evaluating transactions
Checking the transaction type in a smart contract¶
The ApplicationCall
transaction types defined in The Lifecycle of a Smart Contract can be checked within the TEAL code by examining the OnCompletion
transaction property.
program = OnComplete.NoOp == Txn.on_completion()
print(compileTeal(program, Mode.Application))
txn OnCompletion
int NoOp // OptIn, CloseOut, UpdateApplication, or DeleteApplication
==
Global values in smart contracts¶
Smart contracts have access to many global variables. These variables are set for the blockchain, like the minimum transaction fee (MinTxnFee). As another example of Global variable use, in the Atomic Transfers and Transaction Properties section of this guide, GroupSize
is used to show how to get the number of transactions that are grouped within a smart contract call. Smart contracts also have access to the LatestTimestamp
global which represents the latest confirmed block's Unix timestamp. This is not the current time, but the time when the last block was confirmed. This can be used to set times on when the contract is allowed to do certain things. For example, a contract may only allow accounts to opt in after a start date, which is set when the contract is created and stored in global storage.
program = Global.latest_timestamp() >= App.globalGet(Bytes("StartDate"))
print(compileTeal(program, Mode.Application))
Atomic transfers and transaction properties¶
The TEAL opcodes documentation describes all transaction properties that are available within a TEAL program. These properties can be retrieved using the following contract code.
In many common patterns, the smart contract will be combined with other Algorand technologies such as assets, atomic transfers, or smart signatures to build a complete application. In the case of atomic transfers, more than one transaction’s properties can be checked within the smart contract. The number of transactions can be checked using the GroupSize
global property. If the value is greater than 1, then the call to the smart contract is grouped with more than one transaction.
program = Global.group_size() == Int(2)
print(compileTeal(program, Mode.Application))
The above contract code will be true if there are two transactions submitted at once using an atomic transfer. To access the properties of a specific transaction in the atomic group use the gtxn
opcode.
program = Gtxn[1].type_enum() == TxnType.Payment
print(compileTeal(program, Mode.Application))
In the above example, the second transaction’s type is checked, where the int pay
references a payment transaction. See the opcodes documentation for all transaction types. Note that the gtxn
call is a zero-based index into the atomic group of transactions. The gtxns
opcode could also have been used to retrieve the index into the atomic group from the top of the stack instead of hard coding the index. If the TEAL program fails, all transactions in the group will fail.
If any transaction in a group of transactions is a call to a smart contract, the opcodes gtxna
and gtxnsa
can be used to access any of the transactions array values.
program = Gtxn[Txn.group_index() - Int(1)].application_args[0]
print(compileTeal(program, Mode.Application))
Using assets in smart contracts¶
Smart contract applications can work in conjunction with assets. In addition to normal asset transaction properties, such as asset amount, sender, and receiver, TEAL provides an opcode to interrogate an account’s asset balance and whether the asset is frozen. This opcode asset_holding_get
can be used to retrieve an asset balance or check whether the asset is frozen for any account in the transaction accounts array. The asset must also be in the assets array. See Reference arrays for more details.
asset_balance = AssetHolding.balance(Txn.sender(), Int(123456))
program = Seq(
asset_balance, If(asset_balance.hasValue(), Return(Int(1)), Return(Int(0)))
)
print(compileTeal(program, Mode.Application))
txn Sender
int 123456
asset_holding_get AssetBalance
bnz has_balance
// Reject transaction if no asset balance
int 0
return
has_balance:
//balance value is now on top of the stack
This opcode takes two parameters. The first parameter represents the account to check. The second parameter is the Asset ID of the asset to examine. The asset must be in the assets array and the account in the accounts array for the call to be successful. See Reference arrays for more details.
This opcode supports getting the asset balance and the frozen state of the asset for the specific account. To get the frozen state, replace AssetBalance
above with AssetFrozen
. This opcode also returns two values to the top of the stack. The first is a 0 or 1, where 0 means the asset balance was not found and 1 means an asset balance was found in the accounts balance record.
It is also possible to get an Asset’s configuration information within a smart contract if the asset ID is passed with the transaction in the assets array. To read the configuration, the asset_params_get
opcode must be used. This opcode should be supplied with one parameter, which is the index into the assets array or the actual asset ID.
program = AssetParam.total(Int(123456))
print(compileTeal(program, Mode.Application))
int 123456
asset_params_get AssetTotal
This call returns two values. The first is a 0 or 1 indicating if the parameter was found and the second contains the value of the parameter. See the opcodes documentation for more details on what additional parameters can be read.
Creating an asset or contract within a group of transactions¶
The Algorand Protocol assigns an identifier (ID) when creating an asset (ASA) or a smart contract. 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 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. In an atomic group, TEAL can retrieve the ID of a previous group transaction which created an asset or contract, enabling a smart contract to store the asset ID or another smart contract ID in its state for later usage.
The ID retrieval operation can be performed by using one of two 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
Sharing data between contracts¶
In addition to reading another contract's state, data can be passed 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 allows other contracts to read this scratch space. Scratch space can only be read by other transactions that the specific transaction is grouped with atomically. Grouped transactions execute in the order they are grouped, contracts can not read scratch space for transactions that occur after the current contract transaction.
This operation can be performed by using one of two 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.
For example, with two grouped smart contracts the following code can be used.
Store an integer in scratch space in the first transaction.
In the second transaction read the stored value.