Airdrop

Merkle-proof whitelist token distribution

Pending Anchor 0.32
Program ID CNcG4AK4uUXsqAjKQiFk5i9zU75MdHmgdJDXa5cCgYDH
Binary Size 296K
Cluster Devnet

Instructions

IX create_airdrop

Creates a new airdrop campaign with a merkle root for whitelist verification.

ParameterTypeDescription
amount_per_claimu64Token amount each eligible wallet can claim
max_claimsu64Maximum total number of claims allowed
merkle_root[u8; 32]Root hash of the merkle tree containing eligible wallets
IX fund_airdrop

Deposits tokens into the airdrop vault to fund the distribution.

ParameterTypeDescription
amountu64Number of tokens to deposit
IX claim

Claims tokens from the airdrop. Requires a valid merkle proof to verify eligibility.

ParameterTypeDescription
proofVec<[u8; 32]>Merkle proof path from leaf to root
IX close_airdrop

Closes the airdrop and returns remaining tokens to the authority. Only callable by the authority.

ParameterTypeDescription
No parameters

Accounts

Airdrop

FieldTypeDescription
authorityPubkeyAirdrop creator and admin
mintPubkeyToken mint being distributed
amount_per_claimu64Tokens per eligible claim
max_claimsu64Maximum number of claims allowed
total_claimedu64Number of claims made so far
merkle_root[u8; 32]Merkle root for whitelist verification
activeboolWhether the airdrop is currently active
bumpu8PDA bump seed

ClaimRecord

FieldTypeDescription
airdropPubkeyThe airdrop this claim belongs to
claimerPubkeyWallet that claimed
amountu64Amount of tokens claimed
claimed_ati64Timestamp of the claim
bumpu8PDA bump seed

PDA Derivation

airdrop
seeds = [b"airdrop", authority, mint]
claim
seeds = [b"claim", airdrop, claimer]
vault
seeds = [b"vault", airdrop]

Error Codes

NotActive The airdrop has been closed and is no longer accepting claims
MaxClaimsReached All available claim slots have been used
InvalidProof The provided merkle proof does not verify against the root
Overflow Arithmetic overflow in claim tracking

Usage Example

import { Program, BN } from "@coral-xyz/anchor";
import { PublicKey } from "@solana/web3.js";
import { MerkleTree } from "merkletreejs";
import { keccak256 } from "js-sha3";

// Build merkle tree from whitelist
const whitelist = [wallet1, wallet2, wallet3];
const leaves = whitelist.map(w => keccak256(w.toBuffer()));
const tree = new MerkleTree(leaves, keccak256, { sortPairs: true });
const root = tree.getRoot();

// Create airdrop with 1000 tokens per claim, 500 max claims
const [airdropPda] = PublicKey.findProgramAddressSync(
  [Buffer.from("airdrop"), authority.publicKey.toBuffer(), mint.toBuffer()],
  program.programId
);

await program.methods
  .createAirdrop(new BN(1_000_000_000), new BN(500), [...root])
  .accounts({
    airdrop: airdropPda,
    authority: authority.publicKey,
    mint,
  })
  .signers([authority])
  .rpc();

// Claim with merkle proof
const proof = tree.getProof(keccak256(claimer.publicKey.toBuffer()))
  .map(p => [...p.data]);

await program.methods
  .claim(proof)
  .accounts({
    airdrop: airdropPda,
    claimer: claimer.publicKey,
    mint,
  })
  .signers([claimer])
  .rpc();