Skip to content

tokens: add merkle-tree-token-claimer anchor example - #661

Open
brimigs wants to merge 4 commits into
solana-foundation:mainfrom
brimigs:tokens/merkle-tree-token-claimer
Open

tokens: add merkle-tree-token-claimer anchor example#661
brimigs wants to merge 4 commits into
solana-foundation:mainfrom
brimigs:tokens/merkle-tree-token-claimer

Conversation

@brimigs

@brimigs brimigs commented Aug 2, 2026

Copy link
Copy Markdown

Problem

The Cosmos app-chain migration guide under review in solana-foundation/solana-com#1251 links a working Merkle token claimer as its reference implementation, but that example currently lives in a personal repo (brimigs/cosmos-migration-guide). Review feedback on that PR asked for the example to move into program-examples so the guide can point at an officially maintained home.

Summary of changes

Adds tokens/merkle-tree-token-claimer (anchor): distribute a snapshot of token balances from one funded vault and a single 32-byte Merkle root, the claim pattern behind large airdrops and chain migrations.

Program (three instructions):

  • initialize_airdrop_data — stores the Merkle root, mints the full claimable supply into a vault ATA owned by the program PDA, then revokes the mint authority so supply is fixed at launch.
  • update_tree — replaces the root, permitted only before the first claim so live proofs never invalidate.
  • claim_airdrop — recomputes the leaf from signer + amount, verifies the sha256 proof against the stored root, transfers via PDA-signed transfer_checked, and writes a per-index claim_receipt PDA that blocks double-claims. Claims are additionally capped by the initialized total.

Tests (tests/litesvm.test.ts, mocha + tsx + anchor-litesvm per repo conventions): initialization state and the mint lock, pre-claim root updates, payouts recording receipts across two claimants, duplicate-claim rejection, stolen-proof rejection (attacker replaying someone else's proof), and the post-claim root freeze. All assertions check real post-state (account fields, token balances, vault deltas).

Tooling: scripts/generate-merkle-tree.ts turns a snapshot JSON into the on-chain root plus a proof per claimant (pnpm generate-tree scripts/sample-snapshot.json out.json), sharing the same tree implementation the tests use (tests/merkle.ts), which matches the on-chain verifier byte for byte.

Also registers the program crate in .github/.workspace-ignore (nested anchor workspace, same as the other tokens examples) and adds the example to the root README.

Verification

  • pnpm install (lockfile committed), anchor build --ignore-keys
  • pnpm exec tsc --noEmit -p tsconfig.json
  • LiteSVM suite: 4 passing ✅
  • cargo fmt --check clean, prettier (root config) clean

🤖 Generated with Claude Code

Distribute a snapshot of balances from one funded vault and a single
32-byte Merkle root: initialize_airdrop_data stores the root, mints the
full supply into a PDA-owned vault, and revokes the mint authority;
update_tree replaces the root only before the first claim; and
claim_airdrop verifies a sha256 proof, pays out through a PDA-signed
transfer_checked, and blocks double-claims with per-index claim receipt
PDAs.

Includes a LiteSVM mocha suite covering initialization and the mint
lock, pre-claim root updates, payouts with receipts, duplicate-claim
and stolen-proof rejection, and the post-claim root freeze, plus an
off-chain generator that turns a snapshot JSON into the on-chain root
and per-claimant proofs.

Requested in review of the Cosmos app-chain migration guide
(solana-foundation/solana-com#1251) so the guide can reference an
officially maintained example.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@brimigs
brimigs requested a review from dev-jodee as a code owner August 2, 2026 21:10
@greptile-apps

greptile-apps Bot commented Aug 2, 2026

Copy link
Copy Markdown

Greptile Summary

Adds a standalone Anchor example for distributing SPL tokens through Merkle-proof claims.

  • Implements airdrop initialization, pre-claim root updates, proof verification, receipt-backed replay protection, and PDA-authorized token transfers.
  • Adds snapshot-to-Merkle-tree generation tooling with validation for empty or entirely invalid snapshots.
  • Adds LiteSVM coverage for initialization, payouts, duplicate and stolen proofs, alternate-index replays, and root freezing.
  • Registers the standalone crate with repository tooling and documents the example.

Confidence Score: 5/5

The PR appears safe to merge.

No blocking failure remains; the receipt-index replay path now rejects both leftover high bits and odd-level alternate indices, while empty or entirely invalid snapshots fail before tree construction.

Important Files Changed

Filename Overview
tokens/merkle-tree-token-claimer/anchor/programs/merkle-tree-token-claimer/src/lib.rs Implements the airdrop lifecycle and now fully authenticates receipt indices by rejecting unconsumed proof-index bits.
tokens/merkle-tree-token-claimer/anchor/tests/merkle.ts Implements the matching SHA-256 Merkle tree with zero-hash padding to keep odd-node proofs index-specific.
tokens/merkle-tree-token-claimer/anchor/scripts/generate-merkle-tree.ts Generates roots and claimant proofs while explicitly rejecting snapshots with no valid entries.
tokens/merkle-tree-token-claimer/anchor/tests/litesvm.test.ts Exercises the complete claim lifecycle and verifies both same-index and alternate-index replay defenses.
tokens/merkle-tree-token-claimer/README.md Documents the distribution model, tree format, operational workflow, and security properties.

Sequence Diagram

sequenceDiagram
    participant Authority
    participant Program
    participant Mint
    participant Vault
    participant Claimant
    participant Receipt

    Authority->>Program: initialize_airdrop_data(root, total)
    Program->>Mint: Mint total supply into vault
    Program->>Mint: Revoke mint authority
    Authority->>Program: update_tree(new root)
    Note over Program: Allowed only before the first claim
    Claimant->>Program: claim_airdrop(amount, proof, index)
    Program->>Program: Hash signer + amount and verify proof
    Program->>Receipt: Create PDA for airdrop + index
    Program->>Vault: Transfer tokens using PDA authority
    Vault-->>Claimant: Claimed allocation
Loading

Reviews (3): Last reviewed commit: "tokens: fix double-claim via duplicated ..." | Re-trigger Greptile

brimigs and others added 3 commits August 2, 2026 17:59
…oken-claimer/src/lib.rs

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
…e-tree.ts

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
Duplicating the last node of an odd level makes its parent
sha256(C || C), which verifies under two indices and therefore two
claim receipt PDAs, letting the last claimant drain a second payout.
Pad odd levels with a zero hash instead, make an empty tree an explicit
error, and add regression tests for equivalent-index proof replay and
out-of-depth index bits.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant