Understanding Algorand Smart Contracts

Jason Weathersby
Algorand
Published in
12 min readDec 10, 2019

As part of the Algorand Version 2.0 release, the network now supports Algorand Smart Contracts (ASC1), which are layer-1 smart contracts. Not only do these contracts work as part of the protocol, but they also can interact with all the additional layer 1 features in the 2.0 release. This includes Atomic Transfers and Algorand Standard Assets (ASA).

Brian Olson Explains Algorand Smart Contracts

What are Algorand Smart Contracts?

Algorand Smart Contracts are small programs written in an assembly-like language that can be used as a replacement for signatures within a transaction. This language is named Transaction Execution Approval Langauge or TEAL. More on the TEAL language specification in a bit.

TEAL programs have one primary function and that is to return true or false and are used to analyze and approve transactions. Standard transactions are signed with either a private key or multisig set of private keys. With the introduction of ASC1, they can now be signed with a TEAL program. This is called a logic signature.

TEAL code has two basic usage scenarios; as a contract account or as a delegated signature.

When used as a contract account the TEAL code is compiled, which returns an Algorand address. The contract account address can be sent Algos or Algorand Assets from any account using a standard transaction. When sending from a contract account the logic in the TEAL program determines if the transaction is approved. Contract accounts are great for setting up escrow style accounts where say you want to limit withdrawals or you want to do periodic payments, etc.

You can also use TEAL to do delegated signatures, which essentially means you sign a TEAL program with your private key or multisig keys and you can save this logic signature to give to someone else to submit transactions with your authority. The TEAL program can limit the amount of authority you delegate. For example, you can create a delegated signature that allows a utility company to remove up to x Algos every 50000 blocks from your account.

How can you call Algorand Smart Contracts

TEAL programs can be written with any editor, but currently can only be compiled using the goal command-line tool. The command-line tool provides the ability to use these compiled programs within transactions. In addition, the command-line tool also provides the ability to actually test the program before you use it with a real transaction. The simplest program you can write in TEAL is probably the following:

// simple.teal
// Do not use this in a real application
int 1

While very simple, this program should never be used in production as it will always return true authenticating a transaction when used. We use it here for illustration purposes of how to call TEAL programs.

You would compile this program with goal using the following command:

goal clerk compile simple.teal

This would produce the following output:

$ goal clerk compile simple.teal
simple.teal: 6Z3C3LDVWGMX23BMSYMANACQOSINPFIRF77H7N3AWJZYV6OH6GWTJKVMXY

Notice that we have an address. This is the address you want to use if you plan on using this as a contract account. In this case, we want to use it to create a delegated signature, so we actually need to sign the teal program with our private key. The following example shows using goal to compile and sign in one operation.

goal clerk compile simple.teal -o mydelegatedsig.lsig -s -a C3MKH24QL3GHSD5CDQ47ZNQZMNZRX4MUTV6LVPAXMWAXMIISYSOWPGH674 -d ~/node/data

In this case, we are signing the logic with the address specified with the -a parameter. Note that if you do not have a default wallet assigned you will also need to specify the wallet with the -w parameter.

Now we have the file mydelegatedsig.lsig which contains a logic signature that can be used in a transaction by anyone that has this file. Because the logic always returns true, this essentially gives anyone full access to your account!

The created logic signature could be used in a transaction like:

goal clerk send -f C3MKH24QL3GHSD5CDQ47ZNQZMNZRX4MUTV6LVPAXMWAXMIISYSOWPGH674 -a 1000000 -t STF6TH6PKINM4CDIQHNSC7QEA4DM5OJKKSACAPWGTG776NWSQOMAYVGOQE -L mydelegatedsig.lsig -d ~/node/data

Note in this case the -f is the account that signed the logic signature and the -t option is the receiver of the transaction. Also, note that the transaction fee is paid by the account that signed the logic signature.

You can also use the -o option in the above example to write the signed transaction out to file and it will not be submitted to the network. You can use the output with the dryrun command to see how the TEAL is actually processed.

$ goal clerk send -f C3MKH24QL3GHSD5CDQ47ZNQZMNZRX4MUTV6LVPAXMWAXMIISYSOWPGH674 -a 1000000 -t STF6TH6PKINM4CDIQHNSC7QEA4DM5OJKKSACAPWGTG776NWSQOMAYVGOQE -L mydelegatedsig.lsig -d ~/node/data -o out.stxn$ goal clerk dryrun -t out.stxn
tx[0] cost=2 trace:
1 intcblock => <empty stack>
4 intc_0 => 1 0x1
- pass -

For more examples of using goal with TEAL see the ASC1 Tutorial. This tutorial walks you through many of the goal commands and what you can do with a TEAL program. See the TEAL Language Specification section of this post to get more details on how to write a TEAL program. The Algorand SDK also provides methods for calling TEAL programs as well. That topic is covered in the next section.

Using the Algorand SDKs with Algorand Smart Contracts

The binary files created by compiling a TEAL program can be loaded and used in any of the Algorand SDKs, which supports Java, JavaScript, Go and Python. The ASC1 SDK Usage page covers each language in detail. For illustrative purposes, we can show using the simple.teal program above to call a transaction with the delegated signature. This program replicates the commands in the previous section and is implemented to run with Node.js.

const algosdk = require('algosdk');
const fs = require('fs');
// Retrieve the token, server and port
// values for your installation in the algod.net
// and algod.token files within the data directory
const token = "yournodetoken";
const server = "http://127.0.0.1";
const port = 8080;
// Recover the account
// C3MKH...
var mnemonic = "awake climb practice ten mutual calm " +
"expand danger twelve inhale near harvest ensure " +
"life heart okay spend priority spare target " +
"flock wrong essence absent poet";
var recoveredAccount = algosdk.mnemonicToSecretKey(mnemonic);
console.log(recoveredAccount.addr);
// instantiate the algod wrapper
let algodclient = new algosdk.Algod(token, server, port);
(async() => {
// Get the relevant params from algod
// for suggested parameters
let params = await algodclient.getTransactionParams();
let endRound = params.lastRound + parseInt(1000);
let fee = await algodclient.suggestedFee();
// These are the base64 dump of the
// compile simple.teal program
// Used command line $ cat simple.teal.tok | base64
let program = new Uint8Array(Buffer.from("ASABASI=", "base64"));
// makeLogicSig method takes the program and parameters
// in this example we have no parameters
let lsig = algosdk.makeLogicSig(program);
// sign the logic with your accounts secret
// key. This is essentially giving your
// key authority to anyone with the lsig
// and if the logic returns true
// exercise extreme care
// If this were a contract account usage
// you would not do this sign operation
lsig.sign(recoveredAccount.sk);
// At this point you can save the lsig off and share
// as your delegated signature.
// The LogicSig class supports serialization and
// provides the lsig.toByte and fromByte methods
// to easily convert for file saving and
// reconstituting and LogicSig object
// Create a transaction
let txn = {
"from": recoveredAccount.addr,
"to": "STF6TH6PKINM4CDIQHNSC7QEA4DM5OJKKSACAPWGTG776NWSQOMAYVGOQE",
"fee": params.fee,
"amount": 200000,
"firstRound": params.lastRound,
"lastRound": endRound,
"genesisID": params.genesisID,
"genesisHash": params.genesishashb64
};
// Create logic signed transaction.
// Had this been a contract account the lsig would not contain the
// signature but would be submitted the same way
let rawSignedTxn = algosdk.signLogicSigTransaction(txn, lsig);
// Save the signed transaction file for debugging purposes
// Use with goal clerk dryrun -t simple.stxn to
// see how the logic is processed
// Comment out if you do not plan on using dryrun
fs.writeFileSync("simple.stxn", rawSignedTxn.blob);
// Submit the lsig signed transaction
let tx = (await algodclient.sendRawTransaction(rawSignedTxn.blob));
console.log("Transaction : " + tx.txId);
})().catch(e => {
console.log(e);
});

TEAL Language Specification

Brian Olson covers TEAL extensively in the Video attached earlier in this post.

As stated above TEAL is an assembly-like language and is processed with a stack machine.

The language is a non-turing-complete language that does not support looping but does support forward branches. TEAL programs are processed one line at a time that push and pop values from the stack. These stack values are either unsigned 64 bit integers or byte strings. TEAL provides a set of operators that operate on the values within the stack. TEAL also allows arguments to be passed into the program from a transaction, a scratch space to temporarily store values for use later in the program, access to grouped or single transaction properties, global values, a couple of pseudo operators, constants and some flow control functions like bnz for branching.

TEAL Architecture Overview

Getting Transaction Properties

The primary purpose of a TEAL program is to return either true or false. When the program completes if there is a non-zero value on the stack then it will return true and if it has a zero value or the stack is empty it will return false. If the stack has more than one value currently it the program will also return false. So looking at the diagram above, the first couple of commands would be processed like:

Program line number 1:

Retrieving Transaction Properties

Notice that we are using txnto reference the current transaction lists of properties. If we were using this program with a grouped transaction we would reference the group of transactions using gtxn. You would use global Group Sizeto get the number of transactions in the group. You could then use gtxn 0 Receiverto get the receiver of the first transaction.

Pseudo Opcodes

Continuing from the previous example.

Program line number 2:

Pseudo Opcodes

Here we are using the addr pseudo opcode to convert a hardcoded address to a byte constant to put on the stack. See the TEAL Overview documentation for additional pseudo opcodes.

Operations

TEAL provides many operators to work with data that is on the stack. See the TEAL Overview documentation for a full list of operators. Most operators act on the last two values on the stack. Some operators act on either one or more than two values from the stack. The TEAL Opcodes documentation explains the number of arguments and if anything is pushed back to the stack as a result of the operation.

Program line number 3:

Operators

Argument Passing

Later in the code we have a call to load the first argument on to the stack.

Program line number 13:

Parameters

All argument parameters to a TEAL program are byte arrays. The order you pass them in is also specific. In this case, we are loading the first parameter on to the stack. Most of the SDKs provide standard language functions that allow you to convert parameters to a byte array. If you are using the goal command line then the parameters must be passed as base64 encoded strings. So you will need to convert your parameters before passing them in. For example, if you wanted to pass “mystringargument” you could use a echo command like the following to convert to a base64 string for goal.

$ echo -n mystringargument | base64

Which will return the following result.

bXlzdHJpbmdhcmd1bWVudA==

This result can be used in conjunction with the — b64args parameter for the goal clerk sendcommand.

goal clerk send -a 1000 -c fromaddr --to toaddr --from-program myteal.teal --argb64 "bXlzdHJpbmdhcmd1bWVudA==" -d ~/node/data

If you are trying to pass an integer value of 123 in you could use a python command like the following to get the base64 encoded string. Note that currently, TEAL does not support negative numbers.

$ python3 -c "import base64;print(base64.b64encode((123).to_bytes(8,'big')))"

Which will output the following.

'AAAAAAAAAHs='

In SDKs you can use the native language functions. For example to pass in a string argument to the Python SDK you would use code similar to the example below.

arg_str = "my string"
arg1 = arg_str.encode()
lsig = transaction.LogicSig(program, args=[arg1])

For the Integer argument you would use (remember TEAL only supports positive values):

arg1 = (123).to_bytes(8, 'big')
lsig = transaction.LogicSig(program, args=[arg1])

Storing and Loading from Scratchspace

TEAL provides a scratch space as a way of temporarily storing values for use later in your code. In the example below we first load 12 onto the stack and then duplicate it. Next, we multiply those two numbers which results in 144 being at the top of the stack. The store command is used to store this value in the scratch space 1 slot.

Store a Value in Scratch Space

Later we can retrieve this value using the load command. Note that this operation does not clear the scratch space slot. The value can be reloaded again, later in the program.

Retrieve a Value from Scratch Space

Using the Lease Parameter

One other thing to be aware of the existence of the new transaction lease parameter. This parameter is very useful for preventing anyone from double submitting a transaction. This parameter is a 32-byte string that you create (goal expects it to be base64 encoded) which combined with the sender field makes the transaction unique. If anyone resubmits the transaction from the same sender and the same lease parameter it will be rejected, up until the last valid round for the transaction. Once the last valid round passes the old lease expires, and a new transaction with the same sender and lease can be sent again. You can access the lease parameter in TEAL by using the txn Leasecall.

Operational Cost of TEAL Opcodes

TEAL programs are limited to 1000 bytes in size. This is the size of the compiled program plus the arguments. In addition, in order to maintain speed, they are also limited by the opcode cost. This limit is set to 20000 for the entire program, where most opcodes cost 1 some cost more. For example to use the SHA256 function which gets the SHA256 value of the last value on the stack costs 7. Some opcodes are very expensive like using the ed25519verify function which costs 1900. The TEAL Opcodes page lists the cost of all opcodes.

The TEAL Language Specification is fully described in the Transaction Execution Approval Language reference page and the full list of opcodes supported by TEAL are described in the TEAL Opcodes reference page.

For a documented line by line example of a TEAL program see the ASC1 Escrow Example which covers how the stack is processed.

What a teal program can not do

TEAL while very powerful is limited by design in several areas. The reason for this is to continue to guarantee fast throughput on the Algorand network. While TEAL can analyze a transaction or group of transactions, it can not change a transaction. TEAL can not look up Algo or Algorand Standard Asset balances. TEAL can not look up information in previous blocks or transactions. TEAL does not know what round the current transaction will actually commit to the ledger, although it will know the round range. TEAL will also not know the time a transaction is committed. Also as stated earlier TEAL can not recurse and can only loop forward.

Guidelines

Because TEAL is very powerful, it requires that developers be very careful to test for certain constraints, like making sure to test that the transaction fee is not astronomical or verify that all required parameters are set. To see a list of guidelines as well as common tips and patters see the TEAL Overview page.

Algorand Smart Contract Templates

TEAL is a powerful language as it replaces signature authority on transactions. To help developers get started and to provide common functionality on using TEAL, Algorand is developing a set of TEAL templates that can be configured using a template approach. These templates are also going to be available with the individual SDKs. As of version 2.0 of Algorand we currently have 2 main templates. These are the Hash Time Lock Contract template and the Split Contract. As we develop more they will be added to this list on the developer site. If you want to follow along with the development or help with the project, take a look at the template location within the Algorand Github Repository. This directory contains the primary code for the templates and the docs directory contains a line by line description of the TEAL code and the use of these smart contracts.

As part of each smart contract’s documentation, we provide an overview of the ASC and an SDK page on how to instantiate and use the template within code. For example, you can review the overview of the HTLC template here and see how it is used within each of the SDKs here.

This list will continue to grow over time, so be sure to check back often.

--

--