Skip to content

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

Merged
dev-jodee merged 5 commits into
solana-foundation:mainfrom
brimigs:tokens/merkle-tree-token-claimer
Aug 3, 2026
Merged

tokens: add merkle-tree-token-claimer anchor example#661
dev-jodee merged 5 commits into
solana-foundation:mainfrom
brimigs:tokens/merkle-tree-token-claimer

Conversation

@brimigs

@brimigs brimigs commented Aug 2, 2026

Copy link
Copy Markdown
Contributor

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
Contributor

Greptile Summary

Adds an Anchor-based Merkle token-claiming example.

  • Implements initialization, pre-claim root updates, proof verification, token transfers, and per-index claim receipts.
  • Adds snapshot-to-Merkle-tree generation tooling and LiteSVM coverage for claims and replay protection.
  • Documents the example and registers its standalone crate with repository tooling.

Confidence Score: 5/5

The PR appears safe to merge.

No blocking failure remains.

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 consumes proof-index bits, closing the previously reported alternate-receipt replay path.
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/merkle.ts Provides the off-chain Merkle implementation with zero-hash padding and explicit empty-tree rejection.
tokens/merkle-tree-token-claimer/anchor/tests/litesvm.test.ts Exercises initialization, root updates, claims, duplicate prevention, stolen proofs, alternate-index replays, and empty-tree handling.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    Snapshot["Balance snapshot"] --> Generator["Merkle tree generator"]
    Generator --> Root["Merkle root"]
    Generator --> Proofs["Claimant proofs"]
    Authority["Airdrop authority"] --> Initialize["Initialize airdrop"]
    Root --> Initialize
    Initialize --> State["Airdrop state PDA"]
    Initialize --> Vault["Funded token vault"]
    Claimant["Claimant signer"] --> Claim["Claim instruction"]
    Proofs --> Claim
    State --> Claim
    Vault --> Claim
    Claim --> Verify["Verify signer, amount, proof, and index"]
    Verify --> Receipt["Claim receipt PDA"]
    Verify --> ATA["Claimant token account"]
Loading

Reviews (4): Last reviewed commit: "tokens: migrate merkle-tree-token-claime..." | 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>

@dev-jodee dev-jodee left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we move web3js to kit so it fits with the rest of the repo

Comment thread tokens/merkle-tree-token-claimer/anchor/scripts/generate-merkle-tree.ts Outdated
Replace @solana/web3.js, @solana/spl-token, and the Anchor TS client
with @solana/kit and the kit-native litesvm 1.x. Instructions are built
directly from the IDL (8-byte discriminator + Borsh args via kit
codecs) and accounts decoded with matching struct decoders, since the
Anchor TS client and anchor-litesvm are web3.js-based.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@brimigs
brimigs requested a review from dev-jodee August 3, 2026 15:44
@dev-jodee
dev-jodee merged commit 4774590 into solana-foundation:main Aug 3, 2026
19 checks passed
@brimigs
brimigs deleted the tokens/merkle-tree-token-claimer branch August 3, 2026 16:12
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.

2 participants