Airdrops
Airdrops
A PirateCrew airdrop is a keccak-256 merkle distribution anchored to an SPL mint. The creator computes a merkle tree off-chain, writes the root into an on-chain config PDA, and claimers redeem their slice by submitting a proof. The hash function is keccak-256 (Ethereum-compatible) — not SHA-256 — so the leaves match the on-chain verify_merkle_proof instruction in the Pirates program exactly.
A single mint can host many independent airdrops; the airdrop_count field (a u16, 0–65535) disambiguates them. The first airdrop for a mint is typically 0, the next 1, and so on. Each airdrop_count has its own root, its own vault, and its own claim state.
Lifecycle
Trees are first-class resources: POST /v2/merkle-trees computes and persists the tree, returning a tree_id that claimers later use to look up their own proof by wallet. The persisted leaves never leave the server, so distributors don't have to host them.
The optional swap_to_sol: true flag on claim appends a Jupiter swap instruction so the claimer immediately receives SOL instead of the airdropped token — useful for incentive flows where the user wants gas, not a bag.
Endpoints
| Method | Path | Mode | Scope | Notes |
|---|---|---|---|---|
POST | /v2/merkle-trees | n/a | airdrop:write | Compute root; persist 1–50,000 leaves |
POST | /v2/airdrops | unsigned | signed | airdrop:admin (for signed) | Write root to on-chain config PDA |
GET | /v2/merkle-trees/{id}/proofs/{wallet} | n/a | read | Fetch a wallet's proof from a persisted tree |
POST | /v2/airdrops/{id}/claims | unsigned only | n/a (signed rejected) | Claimer wallet must sign |
POST | /v2/airdrops/{id}/vault-withdrawals | unsigned | signed | airdrop:admin (for signed) | Admin withdraws unclaimed tokens |
mode=signed is rejected on POST /v2/airdrops/{id}/claims because only the claimer's wallet has authority over the destination token account. See Transaction Modes for the broader split.
1. Build the tree
curl -X POST https://api.piratecrew.fun/v2/merkle-trees \
-H "Authorization: Bearer $PIRATE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"mint": "GoLDDqDRHcGZBiGPeXAYi5ougndqBNQSNXdNeT3re6gr",
"airdrop_count": 0,
"decimals": 6,
"entries": [
{ "wallet": "8mP3...AaB1", "amount": "1000000" },
{ "wallet": "9qZ2...CcD3", "amount": "2500000" },
{ "wallet": "4xR7...EeF5", "amount": "500000" }
]
}'{
"data": {
"root": "5ka...base58...",
"root_hex": "0x9e3f7c8b2a1d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8091a2b3c4d5e6f7a8b9c0d1",
"mint": "GoLDDqDRHcGZBiGPeXAYi5ougndqBNQSNXdNeT3re6gr",
"leaf_count": 3,
"tree_id": "tree_01HXYZ..."
},
"meta": { "request_id": "req_…" }
}amount is a string of raw base units (multiply by 10^decimals if you're working in display units). Save root_hex — you'll feed it to POST /v2/airdrops next.
2. Initialize the airdrop on-chain
curl -X POST https://api.piratecrew.fun/v2/airdrops \
-H "Authorization: Bearer $PIRATE_API_KEY" \
-H "Idempotency-Key: 5f3e2c8a-..." \
-H "Content-Type: application/json" \
-d '{
"payer": "YOUR_WALLET_PUBKEY",
"platform_id": "pirate-crew",
"mint": "GoLDDqDRHcGZBiGPeXAYi5ougndqBNQSNXdNeT3re6gr",
"airdrop_count": 0,
"merkle_root": "0x9e3f7c8b2a1d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8091a2b3c4d5e6f7a8b9c0d1",
"mode": "unsigned"
}'The merkle_root accepts a hex string with or without the 0x prefix. If you have the airdrop:admin scope on your API key, use mode: "signed" to have the platform wallet sign and broadcast in one round-trip.
After this transaction confirms, the airdrop resource flips to status: "initialized" and the vault is ready to be funded.
3. Claim
A claimer first looks up their proof against the persisted tree:
curl "https://api.piratecrew.fun/v2/merkle-trees/tree_01HXYZ.../proofs/8mP3...AaB1" \
-H "Authorization: Bearer $PIRATE_API_KEY"{
"data": {
"wallet": "8mP3...AaB1",
"mint": "GoLDDqDRHcGZBiGPeXAYi5ougndqBNQSNXdNeT3re6gr",
"amount": "1000000",
"root": "5ka...",
"proof": ["0x1a2b...", "0x3c4d...", "0x5e6f..."]
},
"meta": { "request_id": "req_…" }
}Then build the claim transaction. You can omit proof — the server will fetch it from the persisted tree:
curl -X POST https://api.piratecrew.fun/v2/airdrops/$AIRDROP_ID/claims \
-H "Authorization: Bearer $PIRATE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"claimer": "8mP3...AaB1",
"amount": "1000000",
"platform_fee": 0,
"swap_to_sol": false,
"mode": "unsigned"
}'The response is a base64 VersionedTransaction. Have the claimer sign it locally, then submit via POST /v2/transactions. mode: "signed" is rejected — there's no server-signing path for end-user claims.
Set swap_to_sol: true to receive the claim as SOL via a Jupiter swap appended to the same transaction. platform_fee (lamports) is an optional flat fee routed to the platform wallet inside the same tx.
Reference
- Transaction Modes —
unsignedvssignedper endpoint. - Authentication — scopes, rate limits, idempotency.
- OpenAPI — full request/response schemas at
/v2/openapi.jsonor interactively at/v2/docs.