Claim tokens

Token Claim Process

Claiming tokens requires proof of eligibility and proper setup of the recipient's token account. This is a two-phase operation:

  1. Prepare the claimant's token account
  2. Verify and execute the claim
1. Generate Merkle Proof
import { getMerkleProof } from "./merkleUtils";

// Get proof for specific claim (must match original claim list)
const claimProof = getMerkleProof(
  merkleTree, 
  claimer.address, 
  claimAmount, 
  claimIndex
);

Create the cryptographic proof of eligibility

2. Find Claimant's Token Account
import {findAssociatedTokenPda, TOKEN_PROGRAM_ADDRESS } from "@solana-program/token";

const [destinationTokenAccount] = await findAssociatedTokenPda({
  owner: claimer.address,   
  mint: mint,     //mint address 
  tokenProgram: TOKEN_PROGRAM_ADDRESS,   // or TOKEN_2022 
});

Locate or create the destination for claimed tokens

3. Create Claimant's ATA (If Needed)
import { getCreateAssociatedTokenIdempotentInstructionAsync } from "@solana-program/token";

const destinationATACreateIx = await getCreateAssociatedTokenIdempotentInstructionAsync({
  ata: destinationTokenAccount,
  mint: mint,
  payer: claimer,
  owner: claimer.address,
});

Idempotent account creation

4. Get Claim Instruction
import { getClaimTokensInstruction } from "@dropsy/airdrop";
const instruction = getClaimTokensInstruction({
  vault: vault,                     // Airdrop's token vault
  destinationTokenAccount,          // Claimant's ATA
  airdrop: airdropPda,              // Airdrop address
  bitmap: claimMap,                 // Claim tracking account
  mint: mint,                       // Token mint
  claimer: claimer,                 // Claimant's wallet (signer)
  tokenProgram: TOKEN_PROGRAM_ADDRESS,   // or TOKEN_2022 
  index: claimIndex,                // Must match original list
  amount: claimAmount,              // Must match original list
  proof: claimProof                 // Generated Merkle proof
});

Create the instruction to verify and transfer tokens

5. Build Claim Transaction
const transactionMessage = await createTransactionMessageFromInstructions(
  client.rpc,
  claimer,   //signer
  [destinationATACreateIx, instruction] // order matters if you decide to implement a transaction for each instruction
);

const signedTransaction = await signTransactionMessageWithSigners(
  transactionMessage
);

Build a Transaction from both instructions and Sign it

6. Send and Confirm Transaction
await client.sendAndConfirmTransaction(signedTransaction, { commitment: "confirmed" });
const signature = getSignatureFromTransaction(signedTransaction);

console.log("signature:", signature);

Submits the signed transaction to the network and waits for confirmation.

Critical Requirements

  • Exact Match: Index, amount, and address must exactly match original claim list
  • Claim Window: Must be within starts_at and ends_at period
  • One-Time Claim: Bitmap prevents duplicate claims