PYTEAL
CONTRACTS.
Design and deploy stateful smart contracts on Algorand using PyTeal — a Python DSL that compiles to TEAL bytecode executed by the Algorand Virtual Machine.
Execution Environment
Algorand smart contracts run on algod (Algorand daemon), which executes transactions, validates blocks, and maintains blockchain state.
TEAL
Transaction Execution Approval Language — a low-level, stack-based bytecode language.
PyTeal
A Python library that provides a high-level abstraction for writing TEAL programs safely.
PyTeal is NOT executed on the blockchain. It compiles to TEAL, and only the generated TEAL runs on-chain.
Python DSL for contract logic
TEAL Stack-Based Execution
TEAL is a stack-based language. Operations push/pop values from the stack. All operations are deterministic and bounded.
Same input always produces same output. No randomness on-chain.
Bounded execution. No infinite loops. Cost is predictable.
Contract returns 0 (reject) or non-zero (approve). Binary outcome.
Contract Types
Stateless (LogicSig)
Purely validation logic. Signs transactions if conditions are met. Cannot store data.
Stateful (Applications)
Can store and modify on-chain state. Has global and local storage. Our focus.
Global State
Shared across all users — 64 key-value slots
Local State
Per-user storage — 16 key-value slots per user
User B: { 'balance': 250 }
Approval & Clear Programs
Every stateful smart contract requires two programs: an Approval Program that handles all logic, and a Clear Program that executes when users opt-out.
The Approval Program determines whether a transaction should be accepted or rejected. It contains the main business logic.
from pyteal import *
def approval_program():
# Global state key
counter_key = Bytes("counter")
# Handle application creation
on_create = Seq([
App.globalPut(counter_key, Int(0)),
Approve()
])
# Handle NoOp calls (increment)
on_increment = Seq([
App.globalPut(
counter_key,
App.globalGet(counter_key) + Int(1)
),
Approve()
])
# Main router
program = Cond(
[Txn.application_id() == Int(0), on_create],
[Txn.on_completion() == OnComplete.NoOp, on_increment],
[Txn.on_completion() == OnComplete.OptIn, Approve()],
[Txn.on_completion() == OnComplete.CloseOut, Approve()],
[Txn.on_completion() == OnComplete.DeleteApplication,
Return(Txn.sender() == Global.creator_address())],
)
return program
# Compile to TEAL
if __name__ == "__main__":
print(compileTeal(
approval_program(),
mode=Mode.Application,
version=8
))Application Call Types
Algorand applications respond to different OnComplete actions, each triggering specific logic paths in your contract.
Create
app_id == 0Deploy a new application. Runs once during initial creation.
NoOp
NoOpStandard function call. Used for most contract interactions.
OptIn
OptInUser opts into the app. Allocates local state storage.
CloseOut
CloseOutUser opts out gracefully. Clears local state.
ClearState
ClearStateForce opt-out via Clear Program. Always succeeds.
Delete
DeleteApplicationRemove the application. Usually restricted to creator.
Deployment
Deploy your contract using the Algorand SDK. This involves compiling TEAL, defining state schemas, and submitting a creation transaction.
Algorand Sandbox
For development, use Algorand Sandbox — Docker containers running a local Algorand node with KMD wallet service.
from algosdk import account, mnemonic
from algosdk.v2client import algod
from algosdk.transaction import ApplicationCreateTxn, StateSchema
import base64
# Connect to Algorand node
algod_client = algod.AlgodClient(
algod_token="",
algod_address="https://testnet-api.algonode.cloud"
)
# Load compiled TEAL
with open("approval.teal", "r") as f:
approval_source = f.read()
with open("clear.teal", "r") as f:
clear_source = f.read()
# Compile TEAL to bytecode
approval_program = base64.b64decode(
algod_client.compile(approval_source)["result"]
)
clear_program = base64.b64decode(
algod_client.compile(clear_source)["result"]
)
# Define state schema
global_schema = StateSchema(num_uints=1, num_byte_slices=0)
local_schema = StateSchema(num_uints=0, num_byte_slices=0)
# Create application transaction
params = algod_client.suggested_params()
txn = ApplicationCreateTxn(
sender=creator_address,
sp=params,
on_complete=OnComplete.NoOpOC,
approval_program=approval_program,
clear_program=clear_program,
global_schema=global_schema,
local_schema=local_schema,
)
# Sign and submit
signed_txn = txn.sign(creator_private_key)
txid = algod_client.send_transaction(signed_txn)
print(f"Application ID: {wait_for_confirmation(txid)}")Contract Interaction
Once deployed, interact with your contract using ApplicationNoOpTxn for function calls, and read state directly from the application info.
from algosdk.transaction import ApplicationNoOpTxn
# Call the counter contract (increment)
params = algod_client.suggested_params()
txn = ApplicationNoOpTxn(
sender=user_address,
sp=params,
index=app_id, # Application ID from deployment
)
# Sign and submit
signed_txn = txn.sign(user_private_key)
txid = algod_client.send_transaction(signed_txn)
result = wait_for_confirmation(txid)
# Read global state
app_info = algod_client.application_info(app_id)
state = app_info["params"]["global-state"]
for item in state:
key = base64.b64decode(item["key"]).decode()
value = item["value"]["uint"]
print(f"{key}: {value}") # counter: 1Deployment Checklist
Define state schemas before deployment (cannot be changed)
Ensure Clear Program always approves for safe opt-out
Handle all OnComplete types in Approval Program
Test on Algorand Sandbox or TestNet before MainNet
Verify minimum balance requirements for users
Consider upgradeability patterns if logic may change
Complete Development Workflow
Key Learnings
Only TEAL runs on-chain
PyTeal is a development tool that compiles to TEAL bytecode
Transaction-driven execution
Smart contracts are only executed when triggered by transactions
Immediate finality
State changes are atomic — once confirmed, they're permanent
Explicit state typing
State is stored efficiently with explicit types (uint, bytes)
Portable logic
Same code works on DevNet, TestNet, and MainNet
Bounded computation
No infinite loops — all execution has predictable cost