ASC1 Escrow Example

The example covered in this tutorial is for an escrow pattern. This means the TEAL logic is compiled and an account is created that can be funded. The logic then determines how funds are removed from the account. In this example, the escrow account is set up where all tokens are removed from the account with one successful transaction and delivered to one of two accounts. Unsuccessful transactions leave the funds in the escrow. This example functions like a hashed time lock contract.

Suppose we have two addresses where addr1 = RFGEHKTFSLPIEGZYNVYALM6J4LJX4RPWERDWYS2PFKNVDWW3NG7MECQTJY and addr2 = SOEI4UA72A7ZL5P25GNISSVWW724YABSGZ7GHW5ERV4QKK2XSXLXGXPG5Y.  Assume addr1 is the creator of the escrow account and funds it. Addr2 is the intended recipient of the funds, but only if addr2 supplies a proper secret key and the transaction must be submitted within a time limit. If addr2 does not submit the transaction in time or can’t supply the proper secret key, addr1 can submit the transaction and retrieve all the tokens. Additionally, we want to add a caveat that the transaction fee for the transaction can not be above 1 algo. From a pseudo script perspective that will look like:

((addr2 and secret) || (addr1 and timeout)) && (ok fee)

Additionally, in this example we use the CloseRemainderTo field to essentially close out the account and move all funds to either addr1 or addr2 on a successful transaction.

First Clause

TEAL is a stack-based language where we push values, constants, and globals onto the stack and use operators to pop off those and push on a resultant value. So for this program, dealing with the first clause in our pseudo code would look something like:


// Are used to comment in TEAL
// htlc.teal
// First we push the CloseRemainderTo property of the current transaction onto the stack
// You can see additional properties for the transaction under loading values on the ASC page
txn CloseRemainderTo

// Next we push addr2 onto the stack as the intended recipient
// We use the constant addr which parses an Algorand account address base32 and 
// converts it to a regular bytes constant. More constants are available 
// under constants on the ASC page 
addr SOEI4UA72A7ZL5P25GNISSVWW724YABSGZ7GHW5ERV4QKK2XSXLXGXPG5Y

// We use the == operator to verify that both of these are the same
// This pops the two values off the stack and pushes the result 0 or 1
// Look under operators on the ASC page for additional operators
==

// Next we push the current transaction’s Receiver property onto the stack
// In this example it should be addr2
txn Receiver

// We then push addr2 on the stack using the addr constant
addr SOEI4UA72A7ZL5P25GNISSVWW724YABSGZ7GHW5ERV4QKK2XSXLXGXPG5Y

// Use the == to verify that these are equal which pops off the top two values of the stack
// and returns 0 or 1
==

// The stack should currently have two values from the two top conditions
// These will be either 0s or 1s
// We then push the && operator which will and the two previous conditions and return
// either 0 or 1, which will leave one value on the stack either a 0 or a 1
&&

// Next we push the first argument to the transaction onto the stack
// you can set these using goal with the --argb64
// flag. For multiple args you can use the same flag but order is important
// See execution environment on the ASC page for more details.
arg 0

// We then use the len operator which pops the arg off the stack and 
// pushes the length of the argument onto the stack
len

// Next we push a constant int of value 32 onto the stack
int 32

// We then use the == operator to see if the length of the argument
// is equal to 32. This will pop the two values and return a 0 or 1
// The stack will now contain two values with a value of 0 or 1
==

// We then use the && operator to and the two values in the stack
// which pops both values off the stack and return a 0 or 1
// The stack should now only have one value on the stack, 0 or 1
&&

// We then push arg 0 back onto the stack
arg 0

// We then use the sha256 operator which pops the arg 0 off the stack
// and pushes the hash value of the argument onto the stack
sha256

// we then push the constant byte array of the base64 version of our secret onto the stack
byte base64 VeU//+0DS6KY6Bx/ANFINUTAW9Eg6+LfRXjd6qWzohI=

// Next we use the == operator to pop the two values and push 0 or 1 on to the stack
// If arg0 is equal to the secret this value will be 1 and if not it will be 0
==

// Since we now have two values on the stack, we then use the && operator 
// to pop the last two values and push either 0 or 1
// This just essentially ands all previous conditions to this one
// the stack should only have a 0 or 1 value now
&&

This is the end of our first clause.

((addr2 and secret) || (addr1 and timeout)) && (ok fee)

Second Clause

We can now work on the second clause of our operation.


// The next six lines just check if the transaction is being submitted to send to add1 or 
// original creator.
// Once completed the stack will have the 0 or 1 from the previous clause and a 1 or 0 from the 
// the beginning of the second clause. 
txn CloseRemainderTo
addr RFGEHKTFSLPIEGZYNVYALM6J4LJX4RPWERDWYS2PFKNVDWW3NG7MECQTJY
==
txn Receiver
addr RFGEHKTFSLPIEGZYNVYALM6J4LJX4RPWERDWYS2PFKNVDWW3NG7MECQTJY
==
&&

// We then push the FirstValid parameter from the transaction onto the stack
txn FirstValid

// Next we push the constant int value of 67240 onto the stack
// This is a hard coded round number and is only used here as an example
int 67240

// We then use the > operator to check if First Valid is greater than 67240
// We are using this to see if the transaction is timed out and if so addr1 can
// Submit the transaction to return the funds.
// This pops the last two values and returns a 0 or 1
// At the end of this operation, we should have 
// three values on the stack. One for the first clause, and two for the second clause
>

// We then use the && operator to and the last two options in the second clause which pops the
// last two values and pushes a 1 or 0. This will leave only two values on the stack
&&

This completes the second clause of the operation.

((addr2 and secret) || (addr1 and timeout)) && (ok fee)
// We then push the || operator onto the stack which ors the first two clauses 
// and pops the two values and pushes a 0 or 1 
|| 
((addr2 and secret) || (addr1 and timeout)) && (ok fee) 

Third Clause


// Next we push the current transaction fee onto the stack
txn Fee

// We then push the constant integer of value 1000000
// onto the stack, which is specified in micro algos
// In this example this equates to 1 algo
int 1000000 

//Next we use the < operator to pop those last two values and replace with a 
// 0 or 1. This just verifies that the fee is not greater than 1 algo
// At this point there will be two values on the stack 
<

This complets the third clause of our operation.

((addr2 and secret) || (addr1 and timeout)) && (ok fee)

// We then use the && operator to pop those to values by anding them and pushing either 
// a 1 or 0
// Since this is the end of the program this value represents the return value
// and determines if the transaction executed successfully 
&&

// This represents the final and in our operation.

((addr2 and secret) || (addr1 and timeout)) && (ok fee)

Complete Example

This program can be compiled using the following goal command.

goal clerk compile htlc.teal

Which will compile and return the address of the escrow that the addr1 account can then fund. The transaction can then be submitted with the from account being set the escrow account and the logic will determine if the transaction is successful. See the complete tutorial for additional details on goal commands and the ways TEAL logic can be used.

The complete program without comments is presented below.


txn CloseRemainderTo
addr SOEI4UA72A7ZL5P25GNISSVWW724YABSGZ7GHW5ERV4QKK2XSXLXGXPG5Y
==
txn Receiver
addr SOEI4UA72A7ZL5P25GNISSVWW724YABSGZ7GHW5ERV4QKK2XSXLXGXPG5Y
==
&&
arg 0
len
int 32
==
&&
arg 0
sha256
byte base64 VeU//+0DS6KY6Bx/ANFINUTAW9Eg6+LfRXjd6qWzohI=
==
&&
txn CloseRemainderTo
addr RFGEHKTFSLPIEGZYNVYALM6J4LJX4RPWERDWYS2PFKNVDWW3NG7MECQTJY
==
txn Receiver
addr RFGEHKTFSLPIEGZYNVYALM6J4LJX4RPWERDWYS2PFKNVDWW3NG7MECQTJY
==
&&
txn FirstValid
int 67240
>
&&
||
txn Fee
int 1000000
<
&&