claimy
solana airdrop distribution toolkit. cli for operators, sdk for integrators, react widget that ships a claim page in 10 lines. no custom on-chain program — we target jupiter's merkle-distributor, so the riskiest layer is already battle-tested.
quickstart
two paths. operators build trees from a mint snapshot. integrators drop a widget into any react app.
one terminal, one airdrop.
snapshot a mint, weight the holders, build a jupiter-compatible merkle tree. every step is deterministic and inspectable — you can verify any proof locally before you touch the chain.
✔ installed claimy@0.4.0✔ fetched 12,847 holders via getProgramAccounts✔ filtered 47 cex wallets, 312 dust (< min balance)✔ allocated 1,000,000.00 across 12,488 claimants✔ merkle root: 0x9f2a…c4e1ℹ wrote 2 files:- proofs.json (2.4 mb)- root.txt# install cli
npm i -g claimy
# snapshot holders of a mint
export SOLANA_RPC_URL=https://api.mainnet-beta.solana.com
airdrop snapshot --mint <MINT_ADDRESS> -o holders.csv
# weight into allocations
airdrop weight -i holders.csv --mode sqrt -o allocations.csv
# build a jupiter-compatible merkle tree
airdrop tree -i allocations.csv --mint <MINT_ADDRESS> -o proofs.jsonoutputs proofs.json (full proofs) + root.txt (on-chain merkle root).
npm i @claimy/widget @solana/wallet-adapter-reactimport { AirdropClaim } from '@claimy/widget';
import '@claimy/widget/dist/styles.css';
export default function ClaimPage() {
return (
<AirdropClaim
proofsUrl="/proofs.json"
distributor="<DISTRIBUTOR_PUBKEY>"
/>
);
}| SOLANA_RPC_URL | rpc endpoint for cli. helius / triton / any standard. api keys in url are redacted in logs. |
| HELIUS_RPC_URL | alias, same resolution |
| — | no secrets on the client. widget reads proofs.json statically. |
snippets
import { buildTree } from '@claimy/sdk';
const { root, proofs } = buildTree({
mint: 'TOK3N...',
decimals: 9,
allocations: [
{ owner: '9x...abc', amount: '1000000000' },
{ owner: '7y...def', amount: '500000000' },
],
});
// upload proofs as /proofs.json,
// pass root to distributor deployimport { useAirdropClaim } from '@claimy/widget';
function CustomClaim({ proofsUrl, distributor }) {
const { allocation, claim, isClaimed, status } =
useAirdropClaim({ proofsUrl, distributor });
if (!allocation) return <p>no allocation for this wallet.</p>;
if (isClaimed) return <p>already claimed.</p>;
return (
<button onClick={claim} disabled={status === 'pending'}>
claim {allocation.amount}
</button>
);
}cli reference
bin name is airdrop. claimy is the package name.
airdrop snapshot --mint <M> | fetch every spl / token-2022 holder via one getProgramAccounts. filters: --min, --max, --exclude, --exclude-cex, --token2022. |
airdrop weight -i holders.csv --mode <m> | apply allocation strategy. modes: linear, sqrt, tiered, uniform. |
airdrop tree -i allocations.csv --mint <M> | build jupiter-compatible merkle tree. leaf = keccak256(index_le_u64 || owner_pubkey_32 || amount_le_u64). |
airdrop verify -p proofs.json --owner <O> | locally verify a single proof against the recorded root. |
airdrop stats -p proofs.json | print summary: root, claimant count, total allocated. |
airdrop format -p proofs.json --pretty | reformat proofs.json (--pretty or --minify). |
sdk surface
no http/grpc api — everything is client-side. three layers, one direction: widget → sdk ← cli. sdk has no react, cli has no dom.
- fetchProofs(url)
- lookupAllocation(proofs, owner)
- isClaimed(connection, claimant, distributor)
- buildClaimInstruction({ claimant, distributor, allocation })
- buildClaimTransaction({ connection, claimant, distributor, allocation })
- buildFundVaultTransaction({ connection, payer, vault, mint, amountRaw, decimals })
- buildTree({ mint, decimals, allocations })
- computeLeaf(index, owner, amount)
- verifyProof(root, leaf, proof)
- deriveDistributorAddress({ base, mint, version })
- deriveClaimStatusAddress({ claimant, distributor })
- formatAmount
- shortAddress
- shortSignature
- solscanTx
- solscanAccount
architecture
operator (laptop)
│
├─ claimy cli ── snapshot ─► getProgramAccounts ──► holders.csv
│ weight ─────────────────────────► allocations.csv
│ tree ── uses @claimy/sdk ─────► proofs.json + root.txt
│
└─ deploy distributor (jupiter merkle-distributor) with root
│
└─ fund vault (TransferChecked, via @claimy/sdk)
host app (browser)
│
├─ @claimy/widget <AirdropClaim />
│ │
│ └─ @claimy/sdk
│ ├─ fetchProofs(/proofs.json)
│ ├─ lookupAllocation(wallet)
│ ├─ isClaimed() ─► jupiter distributor program
│ └─ buildClaimTransaction ─► wallet-adapter signs ─► rpc
│
└─ @solana/wallet-adapter-react (user brings the wallet)