Skip to content

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.

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))
Snippet Source

txn OnCompletion
int NoOp // OptIn, CloseOut, UpdateApplication, or DeleteApplication
==
Snippet Source

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))
Snippet Source

global LatestTimestamp
byte "StateDate"
app_global_get
>=
Snippet Source

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.

    program = Txn.amount()
    print(compileTeal(program, Mode.Application))
Snippet Source

txn Amount
Snippet Source

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))
Snippet Source

global GroupSize
int 2
==
Snippet Source

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))
Snippet Source

gtxn 1 TypeEnum
int pay
==
Snippet Source

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))
Snippet Source

txn GroupIndex
int 1
-
gtxnsa ApplicationArgs 0
Snippet Source

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
Snippet Source

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.

int 777
store 10
Snippet Source

In the second transaction read the stored value.

// read the first
// transaction's 10th
// slot of scratch space
gload 0 10
Snippet Source