Intermediate · LESSON 06 OF 06
6. Builder capstone: design and test a token escrow
By the end, you can design and test a client-to-program escrow workflow with explicit account, authority, and recovery rules.
Read the workflow
The maker creates an escrow with explicit terms and deposits the exact token amount into a PDA-controlled vault. The beneficiary can claim once; the maker may refund only after the deadline, or cancel before funding under the defined rules. Terminal vaults are safely closed after recovery. The maker’s monotonic sequence prevents reuse after escrow cleanup.
- AWAITING DEPOSIT
- FUNDED
- RELEASED
- REFUNDED
- CANCELLED
FIELD NOTE 01
Choose narrow escrow rules
Build a small escrow for one asset and one agreed condition. Specify who can create it, who deposits, who receives on release, and what happens on refund or expiry. State the invariants before writing instructions: only the recorded mint enters the vault, only the authorized party can release it, and the same deposit cannot be released twice. The teaching lab uses a fixed legacy SPL Token Program, but lets the maker choose a mint and does not assess that mint’s issuer, freeze, or other authority policy. Use synthetic lab tokens; a real application must define its accepted-mint and authority policy before taking deposits. Use synthetic test accounts in LiteSVM or an isolated local validator. Avoid arbitrary token support or Mainnet deployment in the first version.
FIELD NOTE 02
Assess accepted-mint policy
The capstone uses synthetic classic SPL tokens and a maker-selected mint to test escrow mechanics; it does not certify a real token or issuer. Before a real application accepts deposits, define an explicit mint allow-list and inspect the mint account’s program, mint authority, freeze authority, and enabled Token-2022 extensions. A live mint authority may issue more supply, and a live freeze authority may freeze token accounts. Record which controls the application accepts and reject unsupported configurations before funds enter escrow.
FIELD NOTE 03
Map escrow and token accounts
Draw the escrow state account, PDA authority, token mint, vault token account, depositor token account, beneficiary account, program ID, and fee payer. Record which program owns each account and which signer controls each transition. Derive the escrow state and vault authority from documented seeds, and verify canonical addresses in every instruction. For token movement, validate the mint, token program, source, destination, and authority together; a valid address in isolation does not establish the intended relationship.
FIELD NOTE 04
Specify create, deposit, release, and refund
Define the state machine and exact account changes for every instruction. Creation records the parties, mint, amount limit, and release condition. Deposit moves the correct tokens into the vault and records the deposit once. Release transfers only to the agreed beneficiary; refund follows its own authorization and timing rule. Close is permitted only when the escrow reaches a terminal state and protected balances are zero. Check arithmetic bounds and reject stale, duplicate, or out-of-order operations.
FIELD NOTE 05
Build a client that handles real transaction states
The client obtains a recent blockhash, constructs the expected instructions, simulates when appropriate, and displays the asset, amount, destination, and fee payer before asking the wallet to sign. Preserve the signature and blockhash lifetime after submission. A rejected prompt is cancellation; an RPC timeout is uncertainty; a confirmed or finalized status is a network observation. On refresh, recover the same escrow and signature before offering a retry. Do not automatically submit a second deposit because the first response was lost.
FIELD NOTE 06
Protect the program boundary
Treat account addresses and instruction data as untrusted. Verify the escrow PDA, state owner, mint, vault authority, token account owner, beneficiary, signer, and state transition. If a token CPI is used, pass only the required accounts and target the expected token program. Prevent a caller from substituting a mint or destination while keeping the same escrow state. Define errors that explain the rejected precondition without leaking private account data.
FIELD NOTE 07
Test negative cases and recovery
Run the compiled SBF program in LiteSVM for the normal flow and for a wrong signer, wrong mint, substituted vault, wrong beneficiary, duplicate deposit, duplicate release, early refund, and unsolicited vault transfers. For rejected program calls, assert both the error and unchanged protected balances and state. A submitted on-chain transaction can still charge its fee payer when execution fails, so report network fees separately from protected escrow state; local harness cost is not a live fee quote. For client failures, separately mock delayed, null, and stale responses. A mocked RPC test does not prove program enforcement, and a local SBF test does not prove a deployed program has the same bytes or authorities.
FIELD NOTE 08
Package evidence and disclose limits
Deliver the source revision, account map, instruction table, threat model, test commands, client-state diagram, and recovery instructions. A reviewer should reproduce the project from a clean checkout using local test assets. Label Devnet or Mainnet observations separately from local execution. The course exam measures the published assessment; this capstone exercise is not independently reviewed unless a separate reviewer evaluates the actual artifact against a published rubric. Do not describe it as a security audit or production-ready financial software.
FIELD NOTE 09
Token-2022 hooks change the transfer workflow
The included escrow lab intentionally uses the classic SPL Token Program; its passing tests do not establish Token-2022 compatibility. A Token-2022 mint may configure a Transfer Hook, which runs its hook program on every transfer through a cross-program invocation. The transaction must include the extra accounts listed by the hook’s on-chain ExtraAccountMetaList. A transfer that omits or supplies stale hook accounts fails onchain; the hook is not silently skipped. Before signing, a compatible client resolves the current hook and extra accounts for that transfer and simulates the complete transaction. The hook program and its required accounts can change, so do not reuse a stale resolution. Until the escrow and client test that full path, restrict accepted mints explicitly.
MAKE THE CONNECTION
A detail worth keeping
Trace a deposit through the wallet, client message, fee payer, escrow program, token CPI, vault account, and stored state. Then substitute one account at a time and show which program invariant rejects the attempt. For a hook-enabled token, trace how the client detects the extension, resolves its ExtraAccountMetaList accounts, simulates, and preserves those accounts in the exact transaction the wallet approves.
Work through an example
Simulate an RPC timeout after the deposit transaction is submitted. Reconcile the original signature and vault state before retrying. Prove that a repeated request cannot increase the deposit twice or release funds to a substituted beneficiary.
Produce a token escrow package containing a state diagram, canonical PDA seeds, account constraints, client signing flow, five adversarial test cases, and a recovery report for a lost submission response.
GUIDED PRACTICE · INTERMEDIATE
Do the work, one step at a time
Use fictional addresses and disposable test data. Do not enter a recovery phrase or private key. Unless a lesson explicitly says “read-only Devnet,” use paper or a local test and do not connect to a public network.
- Write escrow invariants and draw the state, token accounts, signers, PDA authority, and fee payer.
- Specify create, deposit, release, refund, surplus recovery, cancellation, and close transitions with exact account constraints.
- Build and run the fixed-mint escrow as SBF in the included local LiteSVM suite with synthetic accounts and the SPL Token Program.
- Test wrong signer, wrong mint, substituted vault, wrong token destination, duplicate release, early refund, surplus recovery, and close invariants; document which client recovery cases still need RPC mocks.
- Package reproducible evidence and mark which claims remain unproven without Devnet or an independent reviewer.
- Write a supported-mint matrix for legacy SPL Token, Token-2022 without a hook, and hook-enabled Token-2022. Mark which cases the included fixed-token lab actually executes; specify an explicit reject or support decision for each other case without claiming unrun behavior.
Before you move on, check your reasoning
- Document accounts, authorities, and allowed state transitions.
- Implement or specify one complete escrow lifecycle with local tests.
- Present adversarial results and recovery evidence.
OPEN SELF-CHECK · UNGRADED
Pause and test your reasoning
A transfer-hook mint requires extra accounts, but a client submits a transfer without them. What should the application expect?
- The hook is skipped automatically because token transfers are atomic.
- The transfer can fail; the client should resolve the required accounts for that transfer and simulate the complete instruction set.
- The token account owner can bypass the hook by changing the transaction label.
Reveal one best answer and its explanation
One best answer: The transfer can fail; the client should resolve the required accounts for that transfer and simulate the complete instruction set.
A configured transfer hook participates in the token transfer and may require additional accounts. Resolve the hook’s current account list and simulate the exact transfer before asking a wallet to sign; missing or stale accounts are not silently ignored.
Sources: Transfer Hook Integration Guide ↗ · Transactions ↗
This practice is not scored, saved, or part of a certificate. Account-based lesson checkpoints and level exams remain separate.
Scored checkpoints and level exams are online and require a free account. They are not included in this pack.
Optional lab files
These files are included with the pack. Follow the safety and toolchain notes in the lesson before running a lab; a Devnet read requires an internet connection.
READ THE SOURCE
Go deeper in the official docs
Reviewed on 2026-09-29. Protocol documentation can change; check the linked page’s current version when you reconnect.
- Core Concepts ↗
- Program Derived Addresses (PDAs) ↗
- Cross Program Invocation ↗
- SPL Token Basics ↗
- Create a Token Mint ↗
- Freeze Account ↗
- Calling the Token Program via CPI ↗
- LiteSVM ↗
- Transfer Tokens ↗
- Account Constraints ↗
- Sealevel Attacks ↗
- Fuzz Testing ↗
- Transactions ↗
- Transaction Fees ↗
- Transfer Hook Integration Guide ↗
- LiteSVM Time and Sysvars ↗