UNPKG

jito-distributor-sdk

Version:

TypeScript SDK for JITO Merkle Distributor with production-ready versioning and double-hashing support

383 lines (281 loc) 10.4 kB
# JITO Merkle Distributor SDK A production-ready TypeScript SDK for interacting with the JITO Merkle Distributor program on Solana, featuring advanced versioning and proper double-hashing implementation that fixes InvalidProof errors. ## Overview This SDK provides a clean, type-safe interface to interact with the JITO Merkle Distributor program deployed on Solana. It supports creating token airdrops with merkle tree verification, vesting periods, and administrative controls. **Program ID**: `mERkCfxWCS5nJn4dB4RNAkrgpVArgatvr7xZfTnPnbW` ## Features - **Production-ready double-hashing** that matches Solana program exactly - **Advanced deterministic versioning system** with race-condition protection - **InvalidProof error resolution** - no more merkle proof failures - Zero-FFI approach using Anchor TypeScript client - Type-safe interfaces and error handling - PDA derivation utilities - Merkle proof validation - Timestamp validation for vesting periods - Support for locked/unlocked token distributions - Administrative functions (clawback, admin transfer) - **Real testing and deployment scripts for mainnet** ## Installation ```bash npm install jito-distributor-sdk # or pnpm add jito-distributor-sdk ``` ## 🚀 Quick Testing (Real Transactions) Want to test with real transactions on devnet? We've got you covered! ### 1. Setup Environment ```bash # Copy environment template cp env.example .env # Edit .env with your details: # PRIVATE_KEY=your_base58_private_key # CUSTOM_TOKEN_MINT=9xgdh8wQNqmz1EFiyQg9ctTE2AtjGiGFrd7VfcoNfAcr # WALLET_ADDRESS=CoJebSiqLWbXmSCmSSis8NSjva4ag93isJV7dxcz8x5q ``` ### 2. Create a Real Distributor ```bash npm run create-distributor ``` This creates a distributor with proper versioning and double-hashing ### 3. Fund the Distributor ```bash npm run fund ``` This funds the distributor with USDC tokens ### 4. Claim Your Tokens ```bash npm run claim ``` This claims all tokens immediately using real merkle proofs and blockchain transactions. **📖 Full Testing Guide**: See [TESTING.md](./TESTING.md) for complete instructions. ## Quick Start ### Prerequisites 1. **Node.js** (v16 or higher) 2. **Solana CLI** installed and configured 3. **Anchor CLI** (for program deployment) 4. A **Solana wallet** with SOL for transactions ### Installation ```bash npm install jito-distributor-sdk ``` ### Program Deployment **First, deploy the Merkle Distributor program to devnet:** ```bash # Deploy the program to devnet (one-time setup) npm run deploy ``` This script will: - Build the Anchor program - Deploy it to Solana devnet - Automatically update the SDK with the deployed program ID - Verify the deployment **Note:** You need SOL in your wallet to deploy the program. Get devnet SOL from the [Solana Faucet](https://faucet.solana.com/). ### Basic Usage ```typescript import { MerkleDistributor } from 'jito-distributor-sdk'; import { AnchorProvider } from '@coral-xyz/anchor'; import { PublicKey } from '@solana/web3.js'; // Initialize with your Anchor provider const distributor = new MerkleDistributor(provider); // Create a new distributor const signature = await distributor.createDistributor({ mint: new PublicKey('your-token-mint'), version: 1n, root: merkleRoot, maxTotalClaim: 1000000n, maxNumNodes: 100n, startVestingTs: BigInt(Math.floor(Date.now() / 1000)), endVestingTs: BigInt(Math.floor(Date.now() / 1000)), clawbackStartTs: BigInt(Math.floor(Date.now() / 1000) + 86400 * 30), clawbackReceiver: adminPublicKey, admin: adminPublicKey }); ``` ## Core Components ### MerkleDistributor Class The main SDK class that provides methods to interact with the program. #### Constructor ```typescript constructor(provider: AnchorProvider) ``` #### Methods ##### `createDistributor(args: CreateDistributorArgs): Promise<string>` Creates a new merkle distributor. ```typescript interface CreateDistributorArgs { mint: PublicKey; version: bigint; root: Uint8Array; maxTotalClaim: bigint; maxNumNodes: bigint; startVestingTs: bigint; endVestingTs: bigint; clawbackStartTs: bigint; clawbackReceiver: PublicKey; admin: PublicKey; } ``` ##### `claim(args: ClaimArgs): Promise<string>` Claims tokens from the distributor. ```typescript interface ClaimArgs { claimant: PublicKey; distributor: PublicKey; claimantTokenAccount: PublicKey; amountUnlocked: bigint; amountLocked: bigint; proof: Uint8Array[]; } ``` ##### `claimLocked(args: ClaimLockedArgs): Promise<string>` Claims locked tokens after the vesting period. ```typescript interface ClaimLockedArgs { claimant: PublicKey; distributor: PublicKey; claimantTokenAccount: PublicKey; } ``` ##### `clawback(distributor: PublicKey, claimant: PublicKey): Promise<string>` Claws back remaining tokens to the clawback receiver. ##### `setAdmin(distributor: PublicKey, currentAdmin: PublicKey, newAdmin: PublicKey): Promise<string>` Sets a new admin for the distributor. ##### `setClawbackReceiver(distributor: PublicKey, newClawbackReceiver: PublicKey, admin: PublicKey): Promise<string>` Sets a new clawback receiver. ##### Account Fetching Methods - `getDistributor(distributor: PublicKey): Promise<MerkleDistributorAccount>` - `getClaimStatus(claimStatus: PublicKey): Promise<ClaimStatus>` - `getClaimStatusForClaimant(claimant: PublicKey, distributor: PublicKey): Promise<ClaimStatus | null>` - `hasClaimed(claimant: PublicKey, distributor: PublicKey): Promise<boolean>` ### Utility Functions #### PDA Derivation ```typescript import { getDistributorPDA, getClaimStatusPDA } from 'jito-distributor-sdk'; const [distributorPDA, bump] = getDistributorPDA(mint, version); const [claimStatusPDA, bump] = getClaimStatusPDA(claimant, distributor); ``` #### Data Conversion ```typescript import { hexToUint8Array, uint8ArrayToHex, bigintToBN } from 'jito-distributor-sdk'; const bytes = hexToUint8Array('deadbeef'); const hex = uint8ArrayToHex(new Uint8Array([222, 173, 190, 239])); const bn = bigintToBN(12345n); ``` #### Validation ```typescript import { validateMerkleProof, validateTimestamps } from 'jito-distributor-sdk'; const isValidProof = validateMerkleProof(proof); const { valid, error } = validateTimestamps(startTs, endTs, clawbackTs); ``` ## Complete Example ```typescript import { MerkleDistributor, getDistributorPDA, hexToUint8Array, validateTimestamps } from 'jito-distributor-sdk'; import { AnchorProvider } from '@coral-xyz/anchor'; import { PublicKey } from '@solana/web3.js'; import { getAssociatedTokenAddress } from '@solana/spl-token'; async function createAndClaimAirdrop() { // Setup provider const provider = new AnchorProvider(connection, wallet, { commitment: 'confirmed' }); const sdk = new MerkleDistributor(provider); // Configuration const mint = new PublicKey('your-token-mint'); const version = 1n; const merkleRoot = hexToUint8Array('your-merkle-root'); // Timestamps (in seconds) const now = BigInt(Math.floor(Date.now() / 1000)); const startVesting = now; // Immediate distribution const endVesting = now; // No vesting period const clawbackStart = now + 86400n * 30n; // 30 days for clawback // Validate timestamps const validation = validateTimestamps(startVesting, endVesting, clawbackStart); if (!validation.valid) { throw new Error(validation.error); } // Get distributor PDA const [distributorPDA] = getDistributorPDA(mint, version); // Create distributor const createSignature = await sdk.createDistributor({ mint, version, root: merkleRoot, maxTotalClaim: 1000000n, maxNumNodes: 100n, startVestingTs: now, // Immediate distribution endVestingTs: now, // No vesting period clawbackStartTs: now + 86400n * 30n, // 30 days for clawback clawbackReceiver: wallet.publicKey, admin: wallet.publicKey, }); console.log('Distributor created:', createSignature); // Later: Claim tokens const claimantTokenAccount = await getAssociatedTokenAddress(mint, claimantPublicKey); const claimSignature = await sdk.claim({ claimant: claimantPublicKey, distributor: distributorPDA, claimantTokenAccount, amountUnlocked: 300n, // All tokens unlocked immediately amountLocked: 0n, // No locked tokens proof: merkleProof, // Your merkle proof as Uint8Array[] }); console.log('Tokens claimed:', claimSignature); } ``` ## Error Handling The SDK includes comprehensive error types: ```typescript import { DistributorError } from 'jito-distributor-sdk'; try { await sdk.claim(args); } catch (error) { if (error.code === DistributorError.InvalidProof) { console.error('Invalid merkle proof provided'); } // Handle other errors... } ``` ## NPM Scripts ```bash # Build the SDK npm run build # Run development mode (watch for changes) npm run dev # Run tests npm run test # Deploy the Anchor program to devnet (one-time setup) npm run deploy # Run examples npm run demo # Demo without blockchain calls npm run example # Mock wallet example # Real mainnet testing (production scripts) npm run create-distributor # Create a real distributor on mainnet npm run fund # Fund the distributor with tokens npm run claim # Claim tokens from distributor npm run query # Query distributor status ``` See [DEPLOYMENT.md](./docs/DEPLOYMENT.md) for detailed deployment instructions. ## Testing See [TESTING.md](./docs/TESTING.md) for complete testing instructions with real devnet transactions. ## Development ```bash # Install dependencies pnpm install # Build the SDK pnpm build # Run tests pnpm test # Development mode (watch) pnpm dev ``` ## 📚 Documentation - **[TESTING.md](./docs/TESTING.md)** - Complete guide for real devnet testing ## Merkle Tree Implementation The SDK uses a simplified merkle tree implementation similar to the approach shown in the [Metaplex Token Claimer guide](https://developers.metaplex.com/token-metadata/guides/anchor/token-claimer-smart-contract). The merkle tree enables efficient and secure verification of airdrop eligibility using cryptographic proofs. ## License MIT License - see LICENSE file for details. ## Contributing Contributions are welcome! Please ensure all tests pass and follow the existing code style.