Algorand TEAL Tutorial Advanced: Stateless Smart Contracts, Inner Transactions & Opcode Optimization

11 min read

Algorand Virtual Machine AVM chip with TEAL smart contract code representing advanced TEAL tutorial concepts
  • TEAL’s stateless mode (Smart Signatures / LogicSigs) validates transactions without storing any on-chain state — making it fast, cheap, and perfect for escrow-style rules.
  • Advanced TEAL uses gtxngroup_size, and atomic transfer checks to guard multi-transaction flows.
  • Inner transactions (TEAL v5+) let a stateful contract act like its own account — sending ALGO, minting ASAs, and calling other apps.
  • The AVM enforces a 700-opcode budget per app call; pooling across grouped transactions and subroutines with callsub/retsub are your main tools for working around it.
  • Chialisp developers will find TEAL’s stack model familiar: both languages evaluate conditions before signing off on a spend, and both treat “no state change” as the default for signature-mode contracts.

An advanced Algorand TEAL tutorial covers the AVM’s execution model, stateless smart signatures, atomic group validation, inner transactions, opcode budgeting, and how to combine these patterns into production-ready contracts. This guide picks up where the ASA tutorial left off and walks you through every concept with real code.

What Advanced Algorand TEAL Actually Means

If you already know how to create, freeze, and claw back Algorand Standard Assets (ASAs), you have cleared the basics. Advanced TEAL means you are now working at the level of the Algorand Virtual Machine (AVM) itself — understanding how its stack works, why opcode cost matters, and how to write contracts that are both correct and efficient.

The AVM is a stack-based interpreter. Every line in a TEAL program either pushes a value onto the stack or pops one off and does something with it. The program finishes by leaving a single non-zero integer on the stack to signal approval, or by leaving zero (or nothing) to reject the transaction. That clean binary outcome is exactly why TEAL maps so well to transaction gating: there is no ambiguity about whether a contract approved or denied a spend.

The Two Modes You Must Know

TEAL runs in two completely different contexts, and confusing them is the most common beginner mistake at the advanced level. Stateless mode (also called LogicSig or Smart Signature mode) is used to authorize transactions — think of it as a programmable signature. The logic is bundled with the transaction and evaluated by every validation node. It cannot read or write any persistent on-chain data. Stateful mode (Application or Smart Contract mode) runs when someone calls your deployed application. It can read and write global state, local account state, and box storage. It can also issue inner transactions, which stateless programs cannot.

If you come from Chialisp, this maps cleanly: TEAL’s stateless mode is your puzzle-and-solution pair — the program checks conditions and either approves or rejects the spend, no persistent state involved. Stateful TEAL is closer to a singleton with access to a persistent key-value store. Either way, the AVM runs the code and makes the call.

“Algorand Smart Contracts are small programs that serve various functions on the blockchain and operate on layer-1. Smart contracts are separated into two main categories — smart contracts and smart signatures, also referred to as stateful and stateless contracts respectively. The type of contract determines when and how the logic of the program is evaluated.”

— Algorand Developer Documentation, Algorand Foundation

Building an Advanced Stateless Smart Contract in Algorand TEAL

A stateless TEAL contract (Smart Signature) is used to gate spending from a contract account or to delegate signing authority. The most powerful real-world pattern at the advanced level is the atomic swap validator — a stateless program that only approves a payment transaction if a matching ASA transfer also appears in the same atomic group. No trusted third party, no off-chain coordination, just math and stack logic.

Understanding the AVM Stack Model Before You Write a Line

The AVM stack holds two types of values: unsigned 64-bit integers (uint64) and byte strings. Every opcode you write either pushes, pops, or transforms values on that stack. You also have 256 scratch-space slots — temporary storage within the execution of one program. Scratch space is reset between calls, so it is not persistent state; it is more like variables in a function. Getting comfortable with which opcodes manipulate the stack versus scratch space is the first real mental leap in advanced TEAL.

Subroutines, introduced in TEAL v4, let you encapsulate reusable logic. Use callsub label to jump to a subroutine and retsub to return. This is not just a code-quality improvement — it is an opcode budget technique. By factoring repeated logic into subroutines, you reduce redundant operations and keep your program cost under the AVM’s execution limit.

Atomic Group Validation: The Core Advanced Pattern for Stateless TEAL

This is the heart of an advanced algorand TEAL tutorial. Atomic transfers let you bundle up to 16 transactions together so that all of them either succeed or all of them fail. From within a TEAL program, you check the group using gtxn <index> <field>. The global GroupSize opcode tells you how many transactions are in the group. Your stateless contract can verify that the second transaction in the group is an ASA transfer of the exact right asset ID, to the right receiver, for the right amount — and only approve the payment if all those conditions hold.

Here is what an advanced TEAL stateless atomic swap validator looks like in raw TEAL:

#pragma version 8

// Verify this is a 2-transaction atomic group
global GroupSize
int 2
==
assert

// Check Txn 0 is a payment from the buyer
txn TypeEnum
int pay
==
assert

// Verify the payment receiver is the seller
txn Receiver
addr SELLER_ALGORAND_ADDRESS
==
assert

// Check the payment amount (1 ALGO = 1,000,000 microALGO)
txn Amount
int 1000000
>=
assert

// Now check Txn 1 (the ASA transfer back to the buyer)
gtxn 1 TypeEnum
int axfer
==
assert

gtxn 1 XferAsset
int ASSET_ID_HERE
==
assert

gtxn 1 AssetReceiver
txn Sender
==
assert

gtxn 1 AssetAmount
int 1
>=
assert

// No RekeyTo allowed
txn RekeyTo
global ZeroAddress
==
assert

// Approve
int 1

Every assert call pops the top of the stack and fails the program immediately if it is zero. This tight fail-fast pattern keeps both the logic and the opcode cost clean. Notice the RekeyTo check at the end — this is a security requirement for any v2+ TEAL contract. If you skip it, someone could attach a rekey transaction and take control of the contract account. Always check it.

GoalUse Stateless (Smart Signature)Use Stateful (Smart Contract)
Escrow / atomic swap✅ Ideal — no state neededPossible via inner txn but heavier
Approve spend with rules✅ Natural fitOverkill unless you need stored state
Track balances / counters❌ No persistent storage✅ Use global/local state
Mint or send an ASA from contract logic❌ Cannot issue inner txn✅ Inner transactions (v5+)
Call another smart contract❌ Not composable✅ App-to-app calls (v6+)
Delegate signing to a program✅ Delegated LogicSig❌ Not applicable

Inner Transactions: How Stateful TEAL Contracts Act Like Accounts

Inner transactions are the single biggest leap from beginner to advanced TEAL. Introduced in TEAL v5, they let a deployed smart contract generate and submit transactions on its own behalf. Before v5, contracts that needed to move funds or mint assets had to rely on separate escrow accounts set up as stateless contracts. That pattern is now legacy. Today, a stateful contract has its own application address, can hold ALGO and ASAs, and can send transactions mid-execution using the itxn_beginitxn_field, and itxn_submit opcodes.

Writing Your First Inner Transaction in TEAL

The pattern is always three phases: begin, set fields, submit. Here is a raw TEAL inner payment transaction that sends 5,000 microALGO to the caller:

#pragma version 8

// Start the inner transaction
itxn_begin

// Set the transaction type to payment
int pay
itxn_field TypeEnum

// Set amount: 5000 microALGO
int 5000
itxn_field Amount

// Set receiver to the caller of this contract
txn Sender
itxn_field Receiver

// Fee = 0 means the outer transaction covers the fee
int 0
itxn_field Fee

// Submit the inner transaction
itxn_submit

int 1

Setting the fee to zero on inner transactions is standard practice. The outer application call transaction pools the fee, so the inner transaction does not need its own. The sender of an inner transaction is always the contract’s application address — you do not specify it, the AVM sets it automatically.

Minting an ASA via Inner Transaction

This is where things get powerful. A stateful contract can create an ASA mid-execution and store the resulting asset ID in global state for later use. This completely removes the need for a human-signed asset creation step in your dApp flow:

#pragma version 8

// Create an ASA via inner transaction
itxn_begin

int acfg
itxn_field TypeEnum

int 1000          // Total supply
itxn_field ConfigAssetTotal

int 0             // Decimals
itxn_field ConfigAssetDecimals

byte "MY TOKEN"
itxn_field ConfigAssetName

byte "MYT"
itxn_field ConfigAssetUnitName

int 0
itxn_field Fee

itxn_submit

// Store the newly created asset ID in global state
byte "asset_id"
itxn CreatedAssetID
app_global_put

int 1

After itxn_submit, the itxn CreatedAssetID opcode retrieves the asset ID of the asset just created. You can then write it straight to global state with app_global_put. This two-step pattern — create via inner transaction, store the ID — is the foundation of any advanced DeFi contract on Algorand.

Opcode Budgeting: Why Your Contract Runs Out of Gas and What to Do

The AVM limits each application transaction to a maximum opcode cost of 700. Most opcodes cost 1 each. A few expensive operations — like sha256 (cost 35) or ed25519verify (cost 1900) — can burn through that budget fast. The good news: if you include multiple application calls in a single atomic group, the budget pools. Three app calls in one group means a total budget of 2,100 opcode units shared across all of them.

Pooled opcode budgets across grouped application transactions are what make complex DeFi contracts on Algorand practical. If you need to do heavy computation — say, a batch NFT verification with multiple signature checks — group the calls and let the budget pool rather than trying to squeeze everything into one transaction.

Checking Budget at Runtime with OpcodeBudget

TEAL v6 added the global OpcodeBudget opcode, which pushes the remaining execution budget onto the stack so your contract can inspect it at runtime. You can use this to build budget-aware logic — branching to a simpler code path if you are running low, or failing gracefully rather than hitting the hard limit mid-execution:

#pragma version 8

// Check remaining opcode budget
global OpcodeBudget
int 200                 // We need at least 200 ops left
>
bz budget_too_low       // Branch if budget is not enough

// ... expensive logic here ...

b end

budget_too_low:
  byte "Budget insufficient for this operation"
  log
  int 0
  return

end:
  int 1

This runtime budget check pattern is especially useful in contracts that handle variable-length inputs, such as batch processing or contracts that loop over arrays of accounts.

Using Subroutines to Cut Opcode Waste

Every time you repeat a validation block — say, checking that the sender is authorized — without a subroutine, you pay for each duplicated opcode. Factoring that check into a callsub/retsub subroutine means the AVM runs the same opcodes once per call rather than once per location in the code. Here is a clean subroutine pattern for a sender authorization check:

#pragma version 8

// Main logic
txn Sender
callsub check_authorized
bz reject

// ... rest of contract logic ...
int 1
return

reject:
  int 0
  return

check_authorized:
  // Stack: [..., address]
  addr ADMIN_ADDRESS
  ==
  retsub

Keep your subroutines focused and document the expected stack state at entry and exit. The AVM does not enforce a calling convention — it is up to you to keep the stack discipline clean. Mismatched stack depths between callsub and retsub are a common source of subtle bugs in advanced TEAL.

OperationOpcode(s)CostNotes
Standard push/compare/branchint==bnz1 eachMost ops fall here
SHA256 hashsha25635Budget-heavy; batch carefully
Ed25519 signature verifyed25519verify1,900Requires pooled budget in groups
Submit inner transactionitxn_submitvariesEach inner app call adds 700 to pool
Subroutine callcallsub / retsub1 eachReduces code duplication cost
Global/local state readapp_global_get1Cheap — use freely
Global/local state writeapp_global_put1Limited by schema (64 key-value slots max)

How TEAL Compares to Chialisp at the Advanced Level

If you have gone through Chialisp’s smart contract model, you already think in terms of puzzles and solutions. TEAL’s stateless mode is structurally identical to that mental model: the “puzzle” is your TEAL program, the “solution” is the transaction (and its arguments), and the AVM is the evaluator. Neither TEAL stateless contracts nor Chialisp puzzles maintain persistent state — they simply approve or reject a spend based on the conditions presented at that moment.

Where TEAL diverges significantly is in its stateful mode. Chialisp’s singleton pattern approximates stateful behavior by chaining coins, but TEAL’s smart contract mode is a direct key-value store on-chain — no coin chaining required. The inner transaction pattern in TEAL v5+ is somewhat analogous to Chialisp’s ability to create new coins with custom puzzles from within a spend, but the implementation is much more explicit and directly tied to Algorand’s transaction types.

Case Study: An Algorand NFT Marketplace Using Inner Transactions

A production Algorand NFT marketplace built on TEAL v8 demonstrates the inner transaction pattern at scale: the approval program mints NFTs via inner acfg transactions, stores each asset ID in a box-storage array, and uses atomic groups of up to four transactions — the application call, a payment covering the mint fee, an opt-in ASA transfer from the buyer, and an inner payment returning change — all validated and executed in a single atomic bundle. The entire flow requires zero off-chain coordination between buyer and seller once the smart contract is deployed.

Global and Local State: Structured Data Management in Advanced TEAL

Global state is shared across all interactions with your smart contract — think of it as the contract’s own data. Local state is per-account data stored for each account that has opted into your application. Both are key-value stores with defined schemas set at deployment time. At the advanced level, you need to think carefully about schema allocation: each key-value slot costs minimum balance (MBR) from the contract creator’s account, so wasting slots is not just inefficient, it is expensive.

Reading and writing state is straightforward — app_global_getapp_global_putapp_local_get, and app_local_put — but the advanced challenge is managing the schema within TEAL v8+’s 64 key-value limit for global storage and the separate limits for local storage. For large datasets, TEAL v8 added box storage, which allows arbitrary-length binary data per box, with each box stored at a separate MBR cost. Box storage is now the right tool for storing arrays, structs, or any data that exceeds what fits comfortably in 64 global key-value slots.

TEAL v5 to v8: What Changed and Why It Matters for Advanced Developers

Knowing which opcode or feature was introduced in which TEAL version is not just trivia — it determines your #pragma version and whether your contract runs on all nodes or only newer ones. TEAL v5 brought inner transactions for pay, axfer, acfg, and afrz types. TEAL v6 added inner application calls (app-to-app), grouped inner transactions with itxn_next, and the global OpcodeBudget runtime check. TEAL v7 added block-field access and the falcon_verify opcode. TEAL v8 introduced box storage for large structured data, significantly changing how advanced developers think about on-chain data management. The current AVM version is v12 as of the latest Algorand protocol upgrade, with all prior versions remaining executable under their original semantics.

Conclusion: Putting Advanced Algorand TEAL to Work

You now have the building blocks for production-grade TEAL: stateless atomic swap validators, stateful inner transaction patterns for minting and payments, opcode budget management with pooling and subroutines, and a clear view of how global, local, and box storage each fit into your design. The next step is to build these patterns in a local Algorand Sandbox environment, inspect the compiled TEAL output from AlgoKit, and stress-test your opcode budget with complex transaction groups. TEAL rewards careful, deliberate coding — and the payoff is contracts that run entirely on-chain with no off-chain trust assumptions. Bookmark the Algorand TEAL reference and the opcode spec as your permanent companions. Now go write something that ships.

For a broader look at how Chialisp’s smart contract philosophy compares to what TEAL is doing here, see our deep-dive on ChiaLisp Transforming Blockchain: Unlocking Next-Gen Smart Contracts.

algorand teal tutorial advanced FAQs

What is an advanced Algorand TEAL tutorial trying to teach you beyond the basics?

An advanced Algorand TEAL tutorial moves past simple approval logic into the AVM execution model itself — covering stateless atomic group validation, inner transactions, opcode budget management, subroutines, and structured state storage. The goal is writing contracts that can handle real dApp workflows without off-chain coordination.

What is the difference between stateless and stateful TEAL contracts?

Stateless TEAL (Smart Signatures) authorize transactions using program logic but store no persistent on-chain data; they run once per transaction and are evaluated at submission time. Stateful TEAL (Smart Contracts) are deployed applications that maintain global and local state, can issue inner transactions, and are invoked via application call transactions.

How do inner transactions work in Algorand TEAL?

Inner transactions allow a deployed stateful TEAL smart contract to generate and submit its own transactions — payments, ASA transfers, asset creations, or calls to other contracts — during the execution of its approval program using the itxn_beginitxn_field, and itxn_submit opcodes. This feature was introduced in TEAL v5 and replaced the older escrow account pattern.

Why does my advanced Algorand TEAL contract run out of opcode budget?

The AVM caps each application call at 700 opcode units, and some operations like sha256 (cost 35) or ed25519verify (cost 1,900) consume budget quickly. To solve this, group multiple application calls in one atomic transaction so their budgets pool (n calls = n×700), and use subroutines to eliminate duplicated logic.

How does TEAL’s stateless mode compare to Chialisp puzzles in an advanced algorand teal tutorial context?

TEAL’s stateless Smart Signature mode and Chialisp puzzles share the same fundamental design: a program evaluates spending conditions without maintaining persistent state, either approving or rejecting a transaction based purely on the inputs presented. The key difference is that TEAL’s stateful mode adds a persistent key-value store and inner transaction capability that has no direct equivalent in Chialisp’s coin-chaining model.

algorand teal tutorial advanced Citations

  1. Algorand TEAL Language Overview — Algorand Developer Portal (dev.algorand.co)
  2. Algorand Virtual Machine (AVM) — Algorand Developer Portal (dev.algorand.co)
  3. AVM Opcodes Overview — Algorand Developer Portal (dev.algorand.co)
  4. TEAL Specification — Algorand Developer Portal
  5. Inner Transactions — Algorand Developer Portal
  6. Modern Smart Contract Guidelines (AVM Best Practices) — Algorand Developer Portal
  7. Contract-to-Contract Calls and ABI on Algorand — Algorand Developer Portal
  8. The Smart Contract Language (TEAL) — Algorand Developer Portal
  9. ChiaLisp Transforming Blockchain: Unlocking Next-Gen Smart Contracts — ChiaTribe