In lesson 3 you’ll learn how to write and develop Algorand smart signatures
In this tutorial, you’ll learn how to develop Algorand smart signatures. Smart signatures are transactions with attached logic. They’re great for learning PyTeal without adding the complexity of smart contracts. We’ll provide you with different examples that build upon each other to create a complex smart signature.
In summary, the third post will teach you the following skills:
1. Background information about smart contracts, smart signatures, and contract languages.
2. Smart signature examples
3. Example 1: Simple conditions
4. Example 2: Accept user arguments
5. Example 3: Add security measures
6. Example 4: Recurring payments between block rounds (lease)
Requirements
- A running [Algorand sandbox](https://github.com/algorand/sandbox) or Algorand node. More information can be found [here](https://developer.algorand.org/docs/get-started/dapps/pyteal/#install-sandbox)
- At least three funded addresses in the sandbox (default for sandbox)
- `goal` (part of the Algorand sandbox by default)
- `python3`
Background
First, let’s take a quick look at the difference between smart signatures and smart contracts, and which languages you can use to write contact logic.
Smart Signatures and Smart Contracts
The Algorand cryptocurrency supports an advanced and feature-rich programmable interface to write logic programs that execute on the blockchain. There are two types of logic programs supported; smart signatures and smart contracts.
Smart signatures are essentially transactions that transfer Algorand, but with programmable logic that dictates the transfer conditions. Programmable conditions can include lots of things, such as when the transfer of value may carry out, and other aspects such as from whom, to whom, etc.
Smart contracts are sophisticated logic programs that reside and execute on the Algorand blockchain. Unlike smart signatures that are Algorand transactions with added programmable conditions, smart contracts are self-sustaining stateful software programs. A smart contract has persistent global and local storage where many separate users can come together and interact based on the programmed logic of the contract. Advanced Alogrand DApps such as auction houses, decentralized exchanges, etc. are usually written by interacting with both smart signatures and smart contracts to form an architected system.
This tutorial will focus on smart signatures in the workable examples since the logic programs tend to be simple and reasonably understandable. Later tutorials will cover more advanced topics including smart contracts.
TEAL programming language
Both smart signatures and smart contracts are written in an assembly-like language called TEAL. TEAL stands for Transaction Execution Approval Language, which describes the behavior of smart signatures and contracts quite well.
To give a brief overview of TEAL, it has Opcodes and a stack. If you are unfamiliar with what a stack is, more information is available here. Opcodes are instructions that typically pop values off of the stack, perform some operation and then push the result back onto the stack for some subsequent Opcode to read and use. There are also Opcodes that control the program’s flow, essentially jumping around to specific locations of the program. The full list of valid TEAL Opcodes can be found here.
PyTeal
Although simple to explain, it is difficult to directly write big and complicated smart signatures and smart contracts in TEAL. This assembly-like language, with all of the popping and pushing to the stack, makes it difficult to keep track of what is exactly being expressed in a program. This difficulty to understand also opens up many possibilities for unintentional bugs and security vulnerabilities.
PyTeal aims to solve this complexity and understandability problem of TEAL. It is a higher level meta-language within Python which compiles down to TEAL. It enables a programmer to add operators and expressions to program TEAL with a Python-like syntax. It is important to note that PyTeal does not convert Python code to TEAL, but rather is a domain-specific language that runs within Python to generate TEAL. It simply has the Python syntax but compiles the PyTeal expressions into TEAL.
This higher-level support allows for more complex smart signatures and contracts to be written with fewer bugs or errors. Additionally, PyTeal benefits from the comprehensive testing frameworks available in Python. There are already many Python libraries for testing written. Regular TEAL does not have nearly the same testing support.
Nonetheless, it is still useful to understand TEAL since inspecting the compiled PyTeal programs is sometimes necessary to understand at a fine-grained level what is going on, especially during debugging. More information on debugging and reading TEAL directly here.
To motivate the complexity of TEAL vs PyTeal, the following two snippets implement the same logic. One is in TEAL and the other in PyTeal. You do not need to understand everything precisely in these snippets, but the chances are that the PyTeal will be much clearer to read. In short, we check if the fee is below 1000 microAlgos and if the receiver address in the transaction matches the specified address: (`B6Q6ZZOH5IOCG5PJ366WJU26L5Y2EASQK6ZIC7K6H3V62PZTG7HOW4FKAA`).
```
#pragma version 5
txn Fee
int 1000
<=
txn Receiver
addr B6Q6ZZOH5IOCG5PJ366WJU26L5Y2EASQK6ZIC7K6H3V62PZTG7HOW4FKAA
==
&&
return
```
And here is the example that implements the same logic using PyTeal. This is an educational example, don’t use it in production.
```py
from pyteal import *
tmpl_receiver = Addr("B6Q6ZZOH5IOCG5PJ366WJU26L5Y2EASQK6ZIC7K6H3V62PZTG7HOW4FKAA")
tmpl_fee = Int(1000)
def send_if_tx_properties():
fee_cond = Txn.fee() <= tmpl_fee
recv_cond = Txn.receiver() == tmpl_receiver
program = And(fee_cond, recv_cond)
return program
if __name__ == "__main__":
print(compileTeal(send_if_tx_properties(), mode=Mode.Signature, version=5))
```
The explanation and usage of this smart signature will be explored in greater detail later on in this tutorial. For now, this is a sample comparison of TEAL vs PyTeal.
1. PyTeal development environment
This section will go over the basics of setting up a PyTeal development environment. It will cover installing the PyTeal Python module, structuring the file structure of a typical PyTeal project, explaining the typical boilerplate code of a PyTeal Python file, and actually compiling a PyTeal file into TEAL.
1.1 Installing PyTeal
PyTeal is simply a Python module. So the easiest way to install it is with `pip3`.
```sh
pip3 install pyteal
```
The PyTeal documentation explains some custom installation options available here.
1.2 File structure of a PyTeal project
There are no strict file structure requirements of a PyTeal project, but over time, patterns in organizing a project’s files have emerged, which have been proven clean and tidy. Below is a sample project hierarchy of the smart signature example above.
```
├── assets
│ ├── send_tx_pyteal.py
├── artifacts
│ ├── send_tx_compiled.teal
├── scripts
│ ├── invoke_send_tx.py
├── include
│ ├── any_imports.py
├── test
│ ├── any_tests.py
├── LICENSE
├── Makefile
├── README.md
├── requirements.txt
```
To keep things simple, create a more simple file structure within your cloned sandbox directory. This will make it easier to compile and deploy contracts to your sandbox.
```sh
mkdir assets
touch ./assets/001-example.teal
mkdir artifacts
touch Makefile
```
The `assets` directory contains all of the PyTeal logic code of the DApp being programmed. All compiled TEAL files are written into an `artifacts` directory.
Any helper scripts to deploy or invoke the DApp are found in the `scripts` directory. Any included files or libraries to aid those scripts may be found in the `include` directory.
Any test scripts to test the DApp are found in the `test` directory. At the top level, you will find the usual project files, including a `Makefile` to build and control the project.
1.3 Boilerplate code explanation
Let’s repeat the code here again to learn what’s happening.
```py
from pyteal import *
tmpl_receiver = Addr("B6Q6ZZOH5IOCG5PJ366WJU26L5Y2EASQK6ZIC7K6H3V62PZTG7HOW4FKAA")
tmpl_fee = Int(1000)
def send_if_tx_properties():
fee_cond = Txn.fee() <= tmpl_fee
recv_cond = Txn.receiver() == tmpl_receiver
program = And(fee_cond, recv_cond)
return program
if __name__ == "__main__":
print(compileTeal(send_if_tx_properties(), mode=Mode.Signature, version=5))
```
The boilerplate code above consists of three parts.
The import — Since PyTeal is nothing more than a Python module, it must be imported like any other module. For cleanliness when writing a smart signature or contract with many macros, the import is unrestricted: `from pyteal import *`.
The PyTeal program — Any smart signature or contract, in the end, must be a single PyTeal expression. This expression can be the result of many compositions of different types of PyTeal macros, but the result must be a singular expression. This is the PyTeal program to be compiled into TEAL.
The compilation — Compilation happens with the `compileTeal` function. It takes the arguments. The first is the PyTeal program, the singular expression to compile from step 2 just above. The second argument tells the compiler whether this is a smart signature `Mode.Signature` or a smart contract `Mode.Application`.
The reason this is necessary is that some Opcodes are only available in one of the two possible program modes. Lastly is the TEAL version to use. Over time, TEAL has been augmented with more Opcodes and more possibilities. At the time of writing, the latest version of TEAL is version 5.
1.4 PyTeal compilation
Compiling a PyTeal program into TEAL is straightforward. If used correctly, the output of `compileTeal` above should be a large string with the TEAL program. So just running the Python file will generate the TEAL code which, oftentimes, gets printed and then saved to a TEAL file.
Typically, software projects use a Makefile to consolidate their compilation and build procedures. For a PyTeal project, a typical Makefile target could be `python3 ./assets/001-example.py > ./artifacts/001-example.teal`, for example. This will generate the TEAL code and save it to file at `./artifacts/001-example.teal`. We’ll cover this later in the examples.
2. PyTeal by example: Learning smart signatures
This section will include a few examples of smart signature PyTeal programs to illustrate how to express certain logic using PyTeal. It also demonstrates some of the potential of Algorand smart signatures.
In order to understand these examples better, a recap of what a smart signature is useful for will help. Smart signatures add logic to the signing of a monetary transaction. Sending a standard Algorand transaction requires the sender to sign the transaction, authorizing its transmission.
Another way is to authorize this transmission is to sign a smart signature program. Then the transaction may be transmitted by _anyone_ holding this signature, following the logic codified within the smart signature. This mode of operation is called “Delegated Approval”.
There is a secondary mode of operation for smart signatures where they themselves are accounts. Every smart signature can be hashed and have a unique Algorand address created representing its account. So rather than authorizing the use of a sender’s account, the logic of the smart signature can authorize transactions from its own account address. This mode of operation is called Contract Account.
All of the examples were developed on a local network with local addresses. To duplicate the results and run the examples on your own, you will first need to make sure that you have an Algorand node running and `goal` set up with at least three accounts.
To set up these accounts, simply use `goal` to create one with a name. Let’s make three accounts named `alice`, `bob`, and `charlie`.
```
./sandbox goal account new <name-of-account>
```
And then fund it. When using `goal` with the sandbox, you should have three default accounts that you can use to fund other accounts. Simply send from this default account to your created accounts. Remember, the amount listed is in terms of microAlgos (1e-6 of an Algo).
```sh
# Command (sending 1000 ALGO)
./sandbox goal clerk send -f KA7YID6N5GYGSSP2SOQGYHD4MOE6MIT2PTLG6GIDYCAISY4T5WLO4YVRME -t alice -a 1000000000
# Raw command
./sandbox goal clerk send -f <default-algo-account> -t <name-of-account> -a <microAlgos>
```
A realistic usage for creating and funding an account used in this example is:
```sh
./sandbox goal account new alice
```
Repeat this three times in total to set up all of the necessary accounts. To view the balances use:
```sh
./sandbox goal account list
# [offline] alice LZJPRU7XX4JSSF76YGTYP33VCDKRSW6DFWWFJWJ34Q4YTNY757BHLLOTVQ 1000000000 microAlgos
# [offline] bob B6Q6ZZOH5IOCG5PJ366WJU26L5Y2EASQK6ZIC7K6H3V62PZTG7HOW4FKAA 1000000000 microAlgos
# [offline] charlie 6MSKMEVDICVA2UI344ZU7XJUYVJB5PIXQPPIKVUCKMNDQDZJY2PHNWMBOE 1000000000 microAlgos
# [online] KA7YID6N5GYGSSP2SOQGYHD4MOE6MIT2PTLG6GIDYCAISY4T5WLO4YVRME KA7YID6N5GYGSSP2SOQGYHD4MOE6MIT2PTLG6GIDYCAISY4T5WLO4YVRME 10023813363631060 microAlgos
```
As shown above, the addresses and roles of the accounts are:
1. LZJPRU7XX4JSSF76YGTYP33VCDKRSW6DFWWFJWJ34Q4YTNY757BHLLOTVQ — Referred to as Alice. Alice is the creator and sender of these smart signatures
2. B6Q6ZZOH5IOCG5PJ366WJU26L5Y2EASQK6ZIC7K6H3V62PZTG7HOW4FKAA — Referred to as Bob. Bob is the recipient of Alice’s transactions.
3. 6MSKMEVDICVA2UI344ZU7XJUYVJB5PIXQPPIKVUCKMNDQDZJY2PHNWMBOE — Referred to as Charlie. Charlie a third account to showcase the various failure modes.
Let’s dive into the examples now.
Example 1: Simple conditions
Here’s the code again. We are setting the variable `tmpl_receiver` to Bob’s address (`B6Q6ZZOH5IOCG5PJ366WJU26L5Y2EASQK6ZIC7K6H3V62PZTG7HOW4FKAA`).
```py
from pyteal import *
tmpl_receiver = Addr("B6Q6ZZOH5IOCG5PJ366WJU26L5Y2EASQK6ZIC7K6H3V62PZTG7HOW4FKAA")
tmpl_fee = Int(1000)
def send_if_tx_properties():
fee_cond = Txn.fee() <= tmpl_fee
recv_cond = Txn.receiver() == tmpl_receiver
program = And(fee_cond, recv_cond)
return program
if __name__ == "__main__":
print(compileTeal(send_if_tx_properties(), mode=Mode.Signature, version=5))
```
This example is the same example used above to motivate TEAL vs PyTeal, however it was not fully explained what it is doing. The smart signature is encapsulated within the `send_if_tx_properties` function and global variables, `tmpl_receiver` and `tmpl_fee`, defined above it. The global variables define constants used throughout the PyTeal program. Keeping these template variables in one place makes it easy to modify those parameters when needed.
This example is purely for educational purposes since it does not contain the necessary safety checks and best practices listed here. Later examples to follow in this tutorial will include these checks and represent a more realistic smart signature. This example is kept simple for the sake of clarity.
This smart signature program is basically an `And` of two conditions, the `fee_cond` and the `recv_cond`.
The `fee_cond` checks to see that the monetary transaction does not send an excessive fee. The `recv_cond` checks to verify that the receiver of this monetary transaction is the predefined address. If both conditions are met, then the transaction is authorized to be accepted by the Algorand network. Based on how this smart signature is signed, it can be used both for delegated approval or as a contract account.
1.1 How to use a smart signature for delegated approval?
This section will demonstrate how to use the above smart signature to delegate the approval of an Algorand transaction. Alice will be delegating this smart signature to send Bob coins. The first step is to compile the smart signature. Make sure you are at the root of the sandbox folder.
```sh
python3 ./assets/001-example.py > ./artifacts/001-example.teal
```
If you’ve used a different location for creating the file structure explained at the beginning of this tutorial, make sure to pass the correct path to your compiled TEAL file.
Alternatively, you could add the following instruction to the Makefile.
```sh
compile-001:
python3 ./assets/001-example.py > ./artifacts/001-example.teal
```
Now, to compile the contract, you can use:
```sh
make compile-001
```
It depends on your personal preference. Remember that you have to add a line in the Makefile for each new file you create.
Then, after compiling the contract to the `artifacts` directory, we have Alice sign the smart signature. Before we can do so, we have to move the compiled TEAL program inside our sandbox. If you aren’t using the sandbox, you can skip this step. We use the `copyTo` command for this (more info in the sandbox docs.)
```sh
./sandbox copyTo ./artifacts/001-example.teal
```
Next, let’s sign the transaction and compile it to a `lsig` (delegated approval). We don’t need to add the `./artifacts` path because the TEAL program sits in the root of the sandbox. You can verify this by entering the sandbox with `./sandbox enter algod` and executing the `ls` command inside the shell.
Now, sign it.
```sh
./sandbox goal clerk compile 001-example.teal -o 001-example.lsig -s -a alice
```
Now the signed smart signature resides in the sandbox. Anyone with this signature can send Bob any amount of coins from Alice’s account. Let’s see some correct uses and then explore some of the failure modes.
To use the smart signature correctly, the following line will send 1 Algo from Alice to Bob. The amounts are expressed in microAlgos.
```sh
./sandbox goal clerk send -f alice -a 1000000 -t bob -L 001-example.lsig
```
Running this line should output the following, signaling its acceptance into the network.
```sh
# Sent 1000000 MicroAlgos from account LZJPRU7XX4JSSF76YGTYP33VCDKRSW6DFWWFJWJ34Q4YTNY757BHLLOTVQ to address B6Q6ZZOH5IOCG5PJ366WJU26L5Y2EASQK6ZIC7K6H3V62PZTG7HOW4FKAA, transaction ID: IO253VXDENYS7227SEM5PICS4HVHWJMQAGKPWRTHEYTNZINTK3BQ. Fee set to 1000
# Transaction IO253VXDENYS7227SEM5PICS4HVHWJMQAGKPWRTHEYTNZINTK3BQ still pending as of round 101514
# Transaction IO253VXDENYS7227SEM5PICS4HVHWJMQAGKPWRTHEYTNZINTK3BQ still pending as of round 101515
# Transaction IO253VXDENYS7227SEM5PICS4HVHWJMQAGKPWRTHEYTNZINTK3BQ committed in round 101516
```
Viewing the account balances now should also reflect the Algorand transfer.
```sh
./sandbox goal account list
```
Here’s the output for this command.
```sh
# [offline] bob B6Q6ZZOH5IOCG5PJ366WJU26L5Y2EASQK6ZIC7K6H3V62PZTG7HOW4FKAA 1001000000 microAlgos
# [offline] alice LZJPRU7XX4JSSF76YGTYP33VCDKRSW6DFWWFJWJ34Q4YTNY757BHLLOTVQ 998999000 microAlgos
```
Alice’s account was deduced 1 Algorand plus the transaction fee, which is 1000 microAlgos by default. As mentioned, anyone can send this transaction. To demonstrate that fully, one can create a second wallet within `goal`, and still successfully issue this transaction.
```sh
# Create wallet
./sandbox goal wallet new secondary
# Send from Alice to Bob from secondary account
./sandbox goal clerk send -f alice -a 1000000 -t bob -L 001-example.lsig -w secondary
```
Here’s the output and new account overview using `./sandbox goal account list`:
```sh
# Sent 1000000 MicroAlgos from account LZJPRU7XX4JSSF76YGTYP33VCDKRSW6DFWWFJWJ34Q4YTNY757BHLLOTVQ to address B6Q6ZZOH5IOCG5PJ366WJU26L5Y2EASQK6ZIC7K6H3V62PZTG7HOW4FKAA, transaction ID: PWCKCUPUPKLHGOLYYGJKZW2KULKPILMMTMFGNHMXEKHNUCKSU3YQ. Fee set to 1000
# Transaction PWCKCUPUPKLHGOLYYGJKZW2KULKPILMMTMFGNHMXEKHNUCKSU3YQ still pending as of round 101577
# Transaction PWCKCUPUPKLHGOLYYGJKZW2KULKPILMMTMFGNHMXEKHNUCKSU3YQ still pending as of round 101578
# Transaction PWCKCUPUPKLHGOLYYGJKZW2KULKPILMMTMFGNHMXEKHNUCKSU3YQ committed in round 101579
# [offline] bob B6Q6ZZOH5IOCG5PJ366WJU26L5Y2EASQK6ZIC7K6H3V62PZTG7HOW4FKAA 1002000000 microAlgos
# [offline] alice LZJPRU7XX4JSSF76YGTYP33VCDKRSW6DFWWFJWJ34Q4YTNY757BHLLOTVQ 997998000 microAlgos
```
Now the secondary wallet is issuing the transaction and it should pass.
Removing the “-L 001-example.lsig” now tells the goal to try to commit this transaction but without the smart signature. Since the secondary wallet is not the owner of Alice’s account, it will not be able to send the transaction. And consequently, the transaction fails to send.
Other failure modes include sending a transaction with too large a fee or the wrong recipient. These failure modes are exemplified as so:
```sh
# Wrong fee
./sandbox goal clerk send -f alice -a 1000000 -t bob -L 001-example.lsig --fee 2000
# Wrong recipient
./sandbox goal clerk send -f alice -a 1000000 -t charlie -L 001-example.lsig
```
A failed transaction will output something similar to this:
```sh
# Couldn't broadcast tx with algod: HTTP 400 Bad Request: transaction {...} invalid : transaction 5LMFGJHRY4UKQTL45T6UHOUJ5P2TZHSXNTZX4BHE2UTUE6VZOKSQ: rejected by logic
```
Both of these runs will fail.
1.2 How to use a smart signature as a contract account?
Another mode of use is the contract account. This mode of operation is where a smart signature gets a generated address. This address belongs to the smart signature, and any Algos sent to it are now under the control of the logic codified within the smart signature. In this case, any Algos sent to this smart signature may be only sent to Bob.
First, let’s get the smart signature’s account address.
```sh
./sandbox goal clerk compile 001-example.teal
```
The output is:
```sh
# 001-example.teal: MPBIH5ILAMICJRENCT643VMD4IQVOCYG4ZLPWRXZ3B6EODQGYHQ7KW36SU
```
The printed address is the contract account of this smart signature. In this case, it is “MPBIH5ILAMICJRENCT643VMD4IQVOCYG4ZLPWRXZ3B6EODQGYHQ7KW36SU”.
Next, let’s fund this contract account. Alice can simply send a normal transaction to this address.
```sh
./sandbox goal clerk send -f alice -a 201000 -t MPBIH5ILAMICJRENCT643VMD4IQVOCYG4ZLPWRXZ3B6EODQGYHQ7KW36SU
```
Output:
```sh
# Sent 201000 MicroAlgos from account LZJPRU7XX4JSSF76YGTYP33VCDKRSW6DFWWFJWJ34Q4YTNY757BHLLOTVQ to address MPBIH5ILAMICJRENCT643VMD4IQVOCYG4ZLPWRXZ3B6EODQGYHQ7KW36SU, transaction ID: 47F3LXQGYAJRAU7QSBI36WJDJ73VHJMWECNEU6L5H2T7XRH4G6HQ. Fee set to 1000
# Transaction 47F3LXQGYAJRAU7QSBI36WJDJ73VHJMWECNEU6L5H2T7XRH4G6HQ still pending as of round 101654
# Transaction 47F3LXQGYAJRAU7QSBI36WJDJ73VHJMWECNEU6L5H2T7XRH4G6HQ still pending as of round 101655
# Transaction 47F3LXQGYAJRAU7QSBI36WJDJ73VHJMWECNEU6L5H2T7XRH4G6HQ committed in round 101656
```
Now that the contract address is funded, `goal` can be called with the smart signature as the “–from-program” as the sender. The transaction will pull coins from the contract address and send them only if the transaction adheres to the smart signature’s logic.
Here is a proper call using this contract address. As per the Algorand network’s requirements, [any account must have a balance of 100000 microAlgos (0.1 Algos) in order to be executed. The funding call from above sent over 201000 microAlgos, of which 100000 are to satisfy the Algorand account minimum balance requirement, 100000 are to be sent in an example exercise and 1000 are to be used as the transaction fee.
To briefly explain account minimums, in order to prevent spamming the Algorand network with empty accounts, any participating account must hold at least some predefined minimum amount of Algos. Minimum balance requirements apply to both regular user accounts as well as smart signature contract accounts. With the current amount funded in the contract account, a max of 100000 microAlgos may be sent out.
If you try to overspend:
```sh
./sandbox goal clerk send --from-program 001-example.teal -a 200000 -t bob
```
You get the following error message:
```sh
# Couldn't broadcast tx with algod: HTTP 400 Bad Request: TransactionPool.Remember: transaction SDR4W7343ILGIKLDEA5V637VR65GID4TH5TLRKG24RB6SL7JBYIA: account KWFO5QNL3GXLCY2Z736SS6AQ63AZZRQW3CMRM6RJB2WFIENUYNPLBQ3W2U balance 0 below min 100000 (0 assets)
```
Now, let’s send the correct transaction:
```sh
./sandbox goal clerk send --from-program 001-example.teal -a 100000 -t bob
```
Success!
```sh
# Sent 100000 MicroAlgos from account MPBIH5ILAMICJRENCT643VMD4IQVOCYG4ZLPWRXZ3B6EODQGYHQ7KW36SU to address B6Q6ZZOH5IOCG5PJ366WJU26L5Y2EASQK6ZIC7K6H3V62PZTG7HOW4FKAA, transaction ID: FVTNUT2B5JGE7DXE2VYSMURMFQ3DIW4OH7MJGAGYG2FTKNEZMKSQ. Fee set to 1000
# Transaction FVTNUT2B5JGE7DXE2VYSMURMFQ3DIW4OH7MJGAGYG2FTKNEZMKSQ still pending as of round 101658
# Transaction FVTNUT2B5JGE7DXE2VYSMURMFQ3DIW4OH7MJGAGYG2FTKNEZMKSQ still pending as of round 101659
# Transaction FVTNUT2B5JGE7DXE2VYSMURMFQ3DIW4OH7MJGAGYG2FTKNEZMKSQ committed in round 101660
```
A few other failure modes that do not adhere to the smart signature’s logic:
```sh
# Too large fee
./sandbox goal clerk send --from-program 001-example.teal -a 100000 -t bob --fee 2000
# Incorrect receiver
./sandbox goal clerk send --from-program 001-example.teal -a 100000 -t charlie
```
Error message output:
```
# Couldn't broadcast tx with algod: HTTP 400 Bad Request: transaction {...} invalid : transaction H7RR6MWVJLYJY5K4MVFUQH6EDAMXJRRKH3GCCID57ACZMYZ43FPA: rejected by logic
```
Example 2: Accept user-supplied arguments
Smart signatures are capable of accepting user-supplied arguments and more complex logic as well. Starting from example 1, this second example accepts a single argument and limits the transaction amount based on the respective argument.
Create a file `002-example.py` in your `assets` directory.
```sh
touch ./assets/002-example.py
```
And add the following code to the file.
```py
from pyteal import *
tmpl_receiver = Addr("B6Q6ZZOH5IOCG5PJ366WJU26L5Y2EASQK6ZIC7K6H3V62PZTG7HOW4FKAA")
tmpl_fee = Int(1000)
tmpl_small = Int(100_000)
tmpl_big = Int(1_000_000)
def send_with_args():
fee_cond = Txn.fee() <= tmpl_fee
recv_cond = Txn.receiver() == tmpl_receiver
arg0 = Arg(0)
arg_cond = Cond([arg0 == Bytes("small"), Txn.amount() == tmpl_small],
[arg0 == Bytes("big"), Txn.amount() == tmpl_big],
[Int(1), Int(0)])
program = And(fee_cond, recv_cond, arg_cond)
return program
if __name__ == "__main__":
print(compileTeal(send_with_args(), mode=Mode.Signature, version=5))
```
There are many PyTeal macros to handle control and other advanced functionality. Descriptions of macros are listed on the PyTeal documentation website here. PyTeal supports `If`, `Cond`, `For` and `While` loops, among other primitives. The `Cond` macro is commonly used and hence showcased in the above example.
In the above example, the first argument (0th index) is extracted. Cond is a conditional block — basically a series of `If` checks on the left side. The first condition to evaluate to true causes its paired expression on the right to evaluate. If no condition evaluates, then as per the PyTeal specification, the entire smart signature fails. This is not an issue in the above `Cond` since the last condition is `Int(1)`, where anything non-zero is treated as a `true` evaluation. This last condition acts as a backup to prevent the smart contract from failing when the above conditions don’t match.
The behavior of the above `Cond` is as follows:
(If 1): If the first argument provided by the user is the byte string “small”, assign to `arg_cond` the evaluation of `Txn.amount() == tmpl_small`. In other words, assign to `arg_cond` a true/false value depending on whether the transaction’s amount is 0.1 Algos or not.
(If 2): If the first argument is the byte string “big”, then assign to `arg_cond` whether the transaction’s amount is 1 Algo or not.
(If 3): Lastly, the final condition is reached if the first argument is neither “small” nor “big”. In which case, assign `Int(0)` to `arg_cond`. Remember, zero values are treated as false boolean values in TEAL.
The smart signature `program` is assembled by combining all three condition evaluations with an `And`. The first two conditions, `fee_cond` and `recv_cond` are the same as in example 1. The `arg_cond` condition only is true (non-zero) if the transaction argument is passed with the corresponding correct transaction amount (small = 0.1 algo or big = 1 Algo).
2.1 Delegated approval for example 2
To illustrate this, and later examples without being overly repetitive, only the delegated approval mode will be demonstrated. The contract account usage follows practically the same steps except for the funding step and minimum balance restrictions.
```sh
# Compile the PyTeal program to TEAL
python3 ./assets/002-example.py > ./artifacts/002-example.teal
# Move file to sandbox
./sandbox copyTo ./artifacts/002-example.teal
# Sign
./sandbox goal clerk compile 002-example.teal -o 002-example.lsig -s -a alice
```
In order to pass arguments to smart signatures transactions, `goal` requires that the arguments be base64 encoded. Here are the respective base64 encodings for the arguments “small” and “big”. You can do it yourself using the below commands. If you are using Mac, you can’t provide the `-w0` option but the output remains the same.
- small: `echo -n small | base64 -w0` –> `c21hbGw=`
- big: `echo -n big | base64 -w0` –> `Ymln`
Sending a small amount:
```sh
./sandbox goal clerk send -f alice -a 100000 -t bob -L 002-example.lsig --argb64 c21hbGw=
```
Output:
```sh
# Sent 100000 MicroAlgos from account LZJPRU7XX4JSSF76YGTYP33VCDKRSW6DFWWFJWJ34Q4YTNY757BHLLOTVQ to address B6Q6ZZOH5IOCG5PJ366WJU26L5Y2EASQK6ZIC7K6H3V62PZTG7HOW4FKAA, transaction ID: HGC3NSUNNK5IUVCQWIF55IAJ55C7ULH7CHJPHF7R4GDCSWRQFHBA. Fee set to 1000
# Transaction HGC3NSUNNK5IUVCQWIF55IAJ55C7ULH7CHJPHF7R4GDCSWRQFHBA still pending as of round 101741
# Transaction HGC3NSUNNK5IUVCQWIF55IAJ55C7ULH7CHJPHF7R4GDCSWRQFHBA still pending as of round 101742
# Transaction HGC3NSUNNK5IUVCQWIF55IAJ55C7ULH7CHJPHF7R4GDCSWRQFHBA committed in round 101743
```
Sending a big amount:
```sh
./sandbox goal clerk send -f alice -a 1000000 -t bob -L 002-example.lsig --argb64 Ymln
```
Output:
```sh
# Sent 1000000 MicroAlgos from account LZJPRU7XX4JSSF76YGTYP33VCDKRSW6DFWWFJWJ34Q4YTNY757BHLLOTVQ to address B6Q6ZZOH5IOCG5PJ366WJU26L5Y2EASQK6ZIC7K6H3V62PZTG7HOW4FKAA, transaction ID: 5AZJADIMJITXAP3HSYGDSSFKTNVQCNUVTIR6KDTGXFT6AGYJGSOA. Fee set to 1000
# Transaction 5AZJADIMJITXAP3HSYGDSSFKTNVQCNUVTIR6KDTGXFT6AGYJGSOA still pending as of round 101773
# Transaction 5AZJADIMJITXAP3HSYGDSSFKTNVQCNUVTIR6KDTGXFT6AGYJGSOA still pending as of round 101774
# Transaction 5AZJADIMJITXAP3HSYGDSSFKTNVQCNUVTIR6KDTGXFT6AGYJGSOA committed in round 101775
```
All of these calls should state that they have been committed. Viewing the account balances should reflect these successful transactions (`./sandbox goal account list`).
While running these commands, Alice and Bob earned some passive Algorand rewards. This is visible in the messy account balances. Therefore, things might look slightly different for you.
```sh
# [offline] bob B6Q6ZZOH5IOCG5PJ366WJU26L5Y2EASQK6ZIC7K6H3V62PZTG7HOW4FKAA 1010307041 microAlgos
# [offline] alice LZJPRU7XX4JSSF76YGTYP33VCDKRSW6DFWWFJWJ34Q4YTNY757BHLLOTVQ 989593976 microAlgos
```
Failure modes:
1. Sending a big amount, but with the argument for small.
```sh
./sandbox goal clerk send -f alice -a 1000000 -t bob -L 002-example.lsig --argb64 c21hbGw=
```
2. Sending a small amount, but with the argument for big.
```sh
./sandbox goal clerk send -f alice -a 100000 -t bob -L 002-example.lsig --argb64 Ymln
```
3. Sending a transaction without any argument
```sh
./sandbox goal clerk send -f alice -a 1000000 -t bob -L 002-example.lsig
```
Error output for not providing any argument:
```
# Couldn't broadcast tx with algod: HTTP 400 Bad Request: transaction {...} invalid : transaction IU2K4JU2MRRVP3EJSD4NLCI7ZE7HZTP7LNG2JWGHH5Y7CZS2LIAA: rejected by logic
```
All of these calls should fail.
Example 3: Adding safety measures
Example 1 above was intentionally simple for educational purposes, but lacked clear safety and security measures. This example addresses some of the safety measures.
All Algorand transactions have fields to claim remaining balances and also transfer ownership of an account. If a smart signature is signed without checking or restricting these fields, an adversary could steal all of an account’s Algorand balance.
To illustrate this security fault in the first example, Charlie will steal all of Alice’s coins by issuing a 0 value transaction to Bob, and close the remainder of Alice’s account balance (i.e., the entire balance minus the transaction fee) to himself.
The close operation terminates an account and sends any outstanding balance to some other specified account. Documentation regarding closing an account may be found (here).
To accomplish this feat, Charlie could run:
```sh
./sandbox goal clerk send -f alice -a 0 -t bob --close-to charlie -L 001-example.lsig
```
```sh
# Sent 0 MicroAlgos from account LZJPRU7XX4JSSF76YGTYP33VCDKRSW6DFWWFJWJ34Q4YTNY757BHLLOTVQ to address B6Q6ZZOH5IOCG5PJ366WJU26L5Y2EASQK6ZIC7K6H3V62PZTG7HOW4FKAA, transaction ID: NFKZYAJOEAR32QFOA3IGDGWG3B6UOW27GHPN5TM6TYWARWNOCL3Q. Fee set to 1000
# Transaction NFKZYAJOEAR32QFOA3IGDGWG3B6UOW27GHPN5TM6TYWARWNOCL3Q still pending as of round 101851
# Transaction NFKZYAJOEAR32QFOA3IGDGWG3B6UOW27GHPN5TM6TYWARWNOCL3Q still pending as of round 101852
# Transaction NFKZYAJOEAR32QFOA3IGDGWG3B6UOW27GHPN5TM6TYWARWNOCL3Q committed in round 101853
```
Viewing the account balances should show how Alice’s entire balance was transferred to Charlie (`./sandbox goal account list`.)
```sh
# [offline] bob B6Q6ZZOH5IOCG5PJ366WJU26L5Y2EASQK6ZIC7K6H3V62PZTG7HOW4FKAA 1010308051 microAlgos
# [offline] alice LZJPRU7XX4JSSF76YGTYP33VCDKRSW6DFWWFJWJ34Q4YTNY757BHLLOTVQ 0 microAlgos
# [offline] charlie 6MSKMEVDICVA2UI344ZU7XJUYVJB5PIXQPPIKVUCKMNDQDZJY2PHNWMBOE 1989601965 microAlgos
```
Let’s return the funds back to Alice for the rest of this tutorial.
```sh
./sandbox goal clerk send -f charlie -a 1000000000 -t alice
```
Similar damage can be done with a rekey to transfer ownership of Alice’s account to Charlie. Warning: if you are trying this out on your own, it is difficult to undo a rekey operation. This should only be performed on a testnet or private Algorand network, NOT with real Algos.
The `rekey-to` argument only accepts addresses, not account names. This example uses Charlie’s address.
```sh
./sandbox goal clerk send -f alice -a 0 -t bob --rekey-to 6MSKMEVDICVA2UI344ZU7XJUYVJB5PIXQPPIKVUCKMNDQDZJY2PHNWMBOE -L 001-example.lsig
```
Output for rekey transaction:
```sh
# Sent 0 MicroAlgos from account LZJPRU7XX4JSSF76YGTYP33VCDKRSW6DFWWFJWJ34Q4YTNY757BHLLOTVQ to address 6MSKMEVDICVA2UI344ZU7XJUYVJB5PIXQPPIKVUCKMNDQDZJY2PHNWMBOE, transaction ID: FLLZTIWOEY6IMODIIHP2WSU5UXOPA6FSMQL6XEZAQPV3NHC5WNKQ. Fee set to 1000
# Transaction FLLZTIWOEY6IMODIIHP2WSU5UXOPA6FSMQL6XEZAQPV3NHC5WNKQ still pending as of round 102154
# Transaction FLLZTIWOEY6IMODIIHP2WSU5UXOPA6FSMQL6XEZAQPV3NHC5WNKQ still pending as of round 102155
# Transaction FLLZTIWOEY6IMODIIHP2WSU5UXOPA6FSMQL6XEZAQPV3NHC5WNKQ committed in round 102156
```
Dumping Alice’s account should show that Charlie is listed as the spender. Essentially, he has full authority over Alice’s account. Again, this command only accepts addresses, not account names.
```sh
./sandbox goal account dump -a LZJPRU7XX4JSSF76YGTYP33VCDKRSW6DFWWFJWJ34Q4YTNY757BHLLOTVQ
```
Output:
```
{
"addr": "LZJPRU7XX4JSSF76YGTYP33VCDKRSW6DFWWFJWJ34Q4YTNY757BHLLOTVQ", # Alice's address
"algo": 996002983,
"ebase": 2552,
"ern": 2,
"spend": "6MSKMEVDICVA2UI344ZU7XJUYVJB5PIXQPPIKVUCKMNDQDZJY2PHNWMBOE" # Charlie's address
}
```
To seal up these security vulnerabilities, simply add safety checks for those fields.
```py
from pyteal import *
tmpl_receiver = Addr("B6Q6ZZOH5IOCG5PJ366WJU26L5Y2EASQK6ZIC7K6H3V62PZTG7HOW4FKAA")
tmpl_fee = Int(1000)
tmpl_amount = Int(2_000_000)
def send_with_args():
tx_type_cond = Txn.type_enum() == TxnType.Payment
fee_cond = Txn.fee() <= tmpl_fee
recv_cond = Txn.receiver() == tmpl_receiver
amount_cond = Txn.amount() == tmpl_amount
# Combine together all of the parameter conditions
params_conds = And(tx_type_cond,
fee_cond,
recv_cond,
amount_cond)
close_remainder_cond = Txn.close_remainder_to() == Global.zero_address()
rekey_cond = Txn.rekey_to() == Global.zero_address()
# Combine the safety conditions
safety_conds = And(close_remainder_cond,
rekey_cond)
program = And(params_conds, safety_conds)
return program
if __name__ == "__main__":
print(compileTeal(send_with_args(), mode=Mode.Signature, version=5))
```
First of all, we’ve added an extra condition that checks the transaction type. You only want to allow payment transactions. However, that’s not enough because when an attacker adds the rekey property to a transaction, it still is a payment transaction. But it’s a best practice to specify the transaction types you want to accept.
Moreover, the zero address `Global.zero_address()` tells Algorand to not perform any close or rekey operation when evaluating the transaction. With this smart signature, the above commands should now fail.
A check was also added for the amount so that Bob cannot withdraw too much at once. This is not a security patch, but rather an addition to the behavior of the smart signature’s logic.
When using this code, make sure to execute the below steps to create the signed transaction.
```sh
# Compile the PyTeal program to TEAL
python3 ./assets/003-example.py > ./artifacts/003-example.teal
# Move file to sandbox
./sandbox copyTo ./artifacts/003-example.teal
# Sign
./sandbox goal clerk compile 003-example.teal -o 003-example.lsig -s -a alice
```
Now they fail:
```sh
# Close transaction
./sandbox goal clerk send -f alice -a 2000000 -t bob --close-to charlie -L 003-example.lsig
# Rekey transaction
./sandbox goal clerk send -f alice -a 2000000 -t bob --rekey-to charlie -L 003-example.lsig
```
Expected error output:
```
# Couldn't broadcast tx with algod: HTTP 400 Bad Request: transaction {...} invalid : transaction 3SS67WSNJVMNQVVEILJ63EFERNNDUXNDQZNWR3YGZ6GIZ2Q3MYDQ: rejected by logic
```
Now this smart signature resembles a more realistic one. This example is still not complete with the security checks, but just illustrates the main ones that need to be covered. All of the security guidelines may be found here.
Example 4: Recurring payment every 225 seconds
The fourth and final example will build off of example 3 to create a smart signature for a recurring payment transaction. In short, this smart signature will allow Bob to pull from Alice’s account only at some preset interval.
In this example, that is every 50 rounds (with an average round time of 4.5 seconds, that is approx. 225 seconds for the interval). Previously, Bob could resend the smart signature transaction as much and quickly as he could. This puts a limit on that, and has real applications such as for subscriptions to some service or loan to be paid at some predefined interval.
```py
from pyteal import *
tmpl_receiver = Addr("B6Q6ZZOH5IOCG5PJ366WJU26L5Y2EASQK6ZIC7K6H3V62PZTG7HOW4FKAA")
tmpl_fee = Int(1000)
tmpl_period = Int(50)
tmpl_amount = Int(2_000_000)
tmpl_duration = Int(50)
# Base64 encoding for `passwordpasswordpasswordpassword`
tmpl_lease = Bytes("base64", "cGFzc3dvcmRwYXNzd29yZHBhc3N3b3JkcGFzc3dvcmQ=")
def recurring_txns():
tx_type_cond = Txn.type_enum() == TxnType.Payment
fee_cond = Txn.fee() <= tmpl_fee
recv_cond = Txn.receiver() == tmpl_receiver
amount_cond = Txn.amount() == tmpl_amount
# Combine together all of the parameter conditions
params_conds = And(tx_type_cond,
fee_cond,
recv_cond,
amount_cond)
first_valid_cond = Txn.first_valid() % tmpl_period == Int(0)
last_valid_cond = Txn.last_valid() == tmpl_duration + Txn.first_valid()
lease_cond = Txn.lease() == tmpl_lease
# Combine together all of the recurring conditions
recurring_conds = And(first_valid_cond,
last_valid_cond,
lease_cond)
close_remainder_cond = Txn.close_remainder_to() == Global.zero_address()
rekey_cond = Txn.rekey_to() == Global.zero_address()
# Combine the safety conditions
safety_conds = And(close_remainder_cond,
rekey_cond)
program = And(params_conds,
recurring_conds,
safety_conds)
return program
if __name__ == "__main__":
print(compileTeal(recurring_txns(), mode=Mode.Signature, version=5))
```
The main addition which creates the recurring nature is the block for `recurring_conds` composed of `first_valid_cond`, `last_valid_cond`, and `lease_cond`. In order to understand these conditions, it is essential to understand the `first_valid`, `last_valid`, and `lease` fields of a transaction.
The `first_valid` and `last_valid` fields demarcate the rounds for which an Algorand transaction is valid. This way, a smart signature can require that a transaction be sent only during that range of rounds.
The `lease` field is a unique identifier that enforces mutual exclusion of transactions from a sender. If one submits a transaction with a given lease, then until the `last_valid` round passes, that same sender cannot resend another transaction with the same lease. This prevents duplicate sending of a transaction. More information on the `lease` field may be found in this table.
Which this knowledge, let’s explore the recurring nature of this smart signature. Recall that the way this smart signature enables recurring transactions is that Bob can issue a transaction to pull a predefined amount of 2 Algos from Alice’s account only every 50 rounds. This is accomplished because the `first_valid_cond` allows a transaction to be sent every 50 (`tmpl_period`) rounds when the modulo operations equals 0. When a transaction is sent with a correct `first_valid` field, Bob must wait another 50 rounds until he can issue the subsequent transaction. Until then, if he tries to submit a transaction, the `first_valid` will not equal 0 after the modulo and thus be denied.
Not only does Bob get a rate limit to how often he may submit transactions, but he must also adhere to a predefined `lease` field in the transactions he sends. The lease is a 32 character long string. In this example, that is the word “password” repeated 4 times, then base64 encoded.
```sh
echo -n passwordpasswordpasswordpassword | base64 -w0
# --> cGFzc3dvcmRwYXNzd29yZHBhc3N3b3JkcGFzc3dvcmQ=
```
By specification of how the `lease` field works, he may not resend a transaction with the same lease until the `last_valid` round passes. Conveniently, the lease expires after 50 rounds because the `last_valid` is up, right when Bob can issue a new transaction with an increased `first_valid` field.
When using this code, make sure to execute the below steps to create the signed transaction.
```sh
# Compile the PyTeal program to TEAL
python3 ./assets/003-example.py > ./artifacts/003-example.teal
# Move file to sandbox
./sandbox copyTo ./artifacts/003-example.teal
# Sign
./sandbox goal clerk compile 003-example.teal -o 003-example.lsig -s -a alice
```
The proper round values to use are extracted from the current state of the Algorand node running:
```sh
./sandbox goal node status
```
Output:
```
# Last committed block: 102015
# Time since last block: 2.7s
# Sync Time: 0.0s
# Last consensus protocol: https://github.com/algorandfoundation/specs/tree/bc36005dbd776e6d1eaf0c560619bb183215645c
# Next consensus protocol: https://github.com/algorandfoundation/specs/tree/bc36005dbd776e6d1eaf0c560619bb183215645c
# Round for next consensus protocol: 102016
# Next consensus protocol supported: true
# Last Catchpoint: 100000#3JSWWSRMXZKJH4EHPBH3RIZBKV3LFGOXTBZRHN5Y2F4SDKF6PBCA
# Genesis ID: private-v1
# Genesis hash: e53iWHTuWApwriRergYxXuDaF2LATLhPAPoHu43FUXg=
```
The output states that the current round is 102015.
Proper usage, pick a first and last valid round that surround the current round:
```sh
./sandbox goal clerk send -f alice -a 2000000 -t bob -L 004-example.lsig --firstvalid 102000 --lastvalid 102050 --lease cGFzc3dvcmRwYXNzd29yZHBhc3N3b3JkcGFzc3dvcmQ=
```
Output:
```
# Sent 2000000 MicroAlgos from account LZJPRU7XX4JSSF76YGTYP33VCDKRSW6DFWWFJWJ34Q4YTNY757BHLLOTVQ to address B6Q6ZZOH5IOCG5PJ366WJU26L5Y2EASQK6ZIC7K6H3V62PZTG7HOW4FKAA, transaction ID: 6GS67EEXXG5XPZMPWGMULNCYXNBTOTTZFD6LJGQZY4PPJRQCXJVA. Fee set to 1000
# Transaction 6GS67EEXXG5XPZMPWGMULNCYXNBTOTTZFD6LJGQZY4PPJRQCXJVA still pending as of round 102019
# Transaction 6GS67EEXXG5XPZMPWGMULNCYXNBTOTTZFD6LJGQZY4PPJRQCXJVA still pending as of round 102020
# Transaction 6GS67EEXXG5XPZMPWGMULNCYXNBTOTTZFD6LJGQZY4PPJRQCXJVA committed in round 102021
```
Once the Algorand network progresses 50 rounds and the lease from the previous transaction has expired, the same lease value may be used successfully:
```sh
./sandbox goal clerk send -f alice -a 2000000 -t bob -L 004-example.lsig --firstvalid 102050 --lastvalid 102100 --lease cGFzc3dvcmRwYXNzd29yZHBhc3N3b3JkcGFzc3dvcmQ=
```
Output:
```
# Sent 2000000 MicroAlgos from account LZJPRU7XX4JSSF76YGTYP33VCDKRSW6DFWWFJWJ34Q4YTNY757BHLLOTVQ to address B6Q6ZZOH5IOCG5PJ366WJU26L5Y2EASQK6ZIC7K6H3V62PZTG7HOW4FKAA, transaction ID: 27GQ65TJ6QZLVXDMNH2DZLGZUB7JSTLJ637XSCYLTOMKDSNT7DQA. Fee set to 1000
# Transaction 27GQ65TJ6QZLVXDMNH2DZLGZUB7JSTLJ637XSCYLTOMKDSNT7DQA still pending as of round 102051
# Transaction 27GQ65TJ6QZLVXDMNH2DZLGZUB7JSTLJ637XSCYLTOMKDSNT7DQA still pending as of round 102052
# Transaction 27GQ65TJ6QZLVXDMNH2DZLGZUB7JSTLJ637XSCYLTOMKDSNT7DQA committed in round 102053
```
Failure modes:
Trying to resend the same transaction fails before the lease has expired:
```sh
# Couldn't broadcast tx with algod: HTTP 400 Bad Request: TransactionPool.Remember: transaction XUZMUVLJJATYXGIL3DGV47XBYDFKGPMTR2VOU6RIYCI7Y53RP4OA using an overlapping lease OBQXG43XN5ZGI4DBONZXO33SMRYGC43TO5XXEZDQMFZXG53POJSPNJWG3M
```
Trying to send a transaction outside of the round validity window fails as so:
```sh
# Couldn't broadcast tx with algod: HTTP 400 Bad Request: TransactionPool.Remember: txn dead: round 44442 outside of 44450--44500
```
Side notes
A recurring transaction period of 50 rounds is quite quick. Bob must be constantly online in order to be sure to send every transition right on time. If he misses one 50 round window, he may not send a transaction to retroactively claim those coins from Alice’s account. In a real-world setting, the recurring periods would normally be must longer, on the order of days or months. Such a quick period was chosen in the example for illustrative purposes.
Conclusion
PyTeal is a higher-level language to write Algorand smart signatures and contracts. It removes the complexity of writing in an assembly-like language in order to program on Algorand. Using PyTeal macros imported into the Python language, a software engineer can enjoy the tidiness of Python’s syntax to express complex logic in an understandable manner.
PyTeal compiles down to TEAL which is what actually runs on the Algorand network. Nonetheless, it is still beneficial to understand the basics of TEAL to inspect the compiled source for more delicate debugging needs. This article gave an overview of PyTeal by showcasing the capabilities of Algorand smart signature logic programs.
Make sure to check out the following tutorial that covers interacting with smart contracts using AlgoSigner or WalletConnect.
Disclaimer: This solution is intended for learning purposes only. It does not cover error checking and other edge cases. Therefore, should not be used as a production application.
Credits: Created by Damian Barabonkov, [email protected], GitHub: @DamianB-BitFlipper
Disclaimer — This is a sponsored article. DappRadar does not endorse any content or product on this page. DappRadar aims to provide accurate information, but readers should always do their own research before taking action. Articles by DappRadar can not be considered as investment advice.