jito-distributor-sdk
Version:
TypeScript SDK for JITO Merkle Distributor with production-ready versioning and double-hashing support
214 lines (179 loc) ⢠7.41 kB
text/typescript
import { config } from 'dotenv';
import { Connection, PublicKey, Keypair } from '@solana/web3.js';
import { AnchorProvider, Wallet } from '@coral-xyz/anchor';
import bs58 from 'bs58';
import { MerkleDistributor } from '../src/index';
// Load environment variables
config();
export interface QueryDistributionsParams {
rpcEndpoint?: string;
mint: string;
maxVersion?: number;
checkVersions?: number[]; // Specific versions to check
showDetails?: boolean; // Show detailed info for each distributor
}
async function queryDistributions(params: QueryDistributionsParams) {
console.log('š Querying Distributions\n');
const {
rpcEndpoint = 'https://api.mainnet-beta.solana.com',
maxVersion = 100,
checkVersions = [],
showDetails = false
} = params;
console.log('š Query Configuration:');
console.log(`RPC Endpoint: ${rpcEndpoint}`);
console.log(`Token Mint: ${params.mint}`);
console.log(`Max Version Check: ${maxVersion}`);
console.log(`Show Details: ${showDetails}\n`);
// Setup connection (no wallet needed for queries)
const connection = new Connection(rpcEndpoint, 'confirmed');
// Create a dummy wallet for SDK initialization (not needed for queries)
const dummyKeypair = Keypair.generate();
const dummyWallet = new Wallet(dummyKeypair);
const provider = new AnchorProvider(connection, dummyWallet, { commitment: 'confirmed' });
// Initialize SDK
const sdk = new MerkleDistributor(provider);
const mint = new PublicKey(params.mint);
try {
// Get comprehensive overview
console.log('š Getting Distribution Overview...');
const overview = await sdk.getDistributionOverview(mint, maxVersion);
console.log('\nšÆ Distribution Overview:');
console.log(` Mint: ${overview.mint.toString()}`);
console.log(` Total Distributors: ${overview.totalDistributors}`);
console.log(` Used Versions: [${overview.usedVersions.join(', ')}]`);
console.log(` Next Available Version: ${overview.nextAvailableVersion}`);
console.log(` Total Claimed: ${overview.totalClaimed} base units`);
console.log(` Total Unclaimed: ${overview.totalUnclaimed} base units`);
if (overview.totalDistributors === 0) {
console.log('\n⨠No distributors found for this mint!');
console.log(`š” You can create the first distributor using version 0`);
return;
}
// Show individual distributors
console.log('\nš Individual Distributors:');
for (const [version, info] of overview.distributors) {
console.log(`\n š¦ Version ${version}:`);
console.log(` PDA: ${info.pda.toString()}`);
console.log(` Admin: ${info.account.admin.toString()}`);
console.log(` Max Claim: ${info.account.maxTotalClaim.toString()} base units`);
console.log(` Claimed: ${info.claimedAmount} base units`);
console.log(` Remaining: ${info.remainingAmount} base units`);
console.log(` Clawback Receiver: ${info.account.clawbackReceiver.toString()}`);
if (showDetails) {
console.log(` Start Vesting: ${new Date(Number(info.account.startTs.toString()) * 1000).toLocaleString()}`);
console.log(` End Vesting: ${new Date(Number(info.account.endTs.toString()) * 1000).toLocaleString()}`);
console.log(` Clawback Start: ${new Date(Number(info.account.clawbackStartTs.toString()) * 1000).toLocaleString()}`);
console.log(` Max Nodes: ${info.account.maxNumNodes.toString()}`);
console.log(` Merkle Root: ${Buffer.from(info.account.root).toString('hex')}`);
}
}
// Check specific versions if requested
if (checkVersions.length > 0) {
console.log('\nš Checking Specific Versions:');
const versionResults = await sdk.batchCheckVersions(mint, checkVersions.map(v => BigInt(v)));
for (const [version, available] of versionResults) {
const status = available ? 'ā
Available' : 'ā Taken';
console.log(` Version ${version}: ${status}`);
}
}
// Show next few available versions
console.log('\nš® Next Available Versions:');
let nextVersion = overview.nextAvailableVersion;
for (let i = 0; i < 5; i++) {
console.log(` ${nextVersion + BigInt(i)}: Available`);
}
console.log('\nš” Usage Tips:');
console.log(' ⢠Use the next available version for new distributions');
console.log(' ⢠Check remaining amounts to see if funds are available');
console.log(' ⢠Monitor claimed amounts to track distribution progress');
} catch (error) {
console.error('\nā Error querying distributions:', error);
if (error instanceof Error) {
if (error.message.includes('could not find account')) {
console.log('š” This might mean no distributors exist for this mint yet');
} else if (error.message.includes('Invalid public key')) {
console.log('š” Check that the mint address is valid');
}
}
throw error;
}
}
// CLI interface when run directly
if (require.main === module) {
const args = process.argv.slice(2);
if (args.includes('--help') || args.includes('-h')) {
console.log(`
Usage: npx ts-node query-distributions.ts [options]
Options:
--mint <address> Token mint address (required)
--max-version <number> Maximum version to check (default: 100)
--check-version <number> Check specific version (can be used multiple times)
--details Show detailed information for each distributor
--rpc <endpoint> Custom RPC endpoint
Examples:
# Query all USDC distributors
npx ts-node query-distributions.ts \\
--mint EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v
# Check specific versions with details
npx ts-node query-distributions.ts \\
--mint EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v \\
--check-version 0 --check-version 5 --check-version 10 \\
--details
# Quick check with limited scope
npx ts-node query-distributions.ts \\
--mint EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v \\
--max-version 20
`);
process.exit(0);
}
// Parse CLI arguments
const parseCliArgs = (): QueryDistributionsParams => {
const params: QueryDistributionsParams = {
mint: '',
checkVersions: []
};
for (let i = 0; i < args.length; i++) {
const arg = args[i];
const next = args[i + 1];
switch (arg) {
case '--mint':
params.mint = next;
i++;
break;
case '--max-version':
params.maxVersion = parseInt(next);
i++;
break;
case '--check-version':
params.checkVersions!.push(parseInt(next));
i++;
break;
case '--details':
params.showDetails = true;
break;
case '--rpc':
params.rpcEndpoint = next;
i++;
break;
}
}
return params;
};
const params = parseCliArgs();
if (!params.mint) {
console.error('Error: --mint is required');
console.error('Use --help for usage information');
process.exit(1);
}
queryDistributions(params)
.then(() => {
console.log('\nā
Query completed successfully');
process.exit(0);
})
.catch((error) => {
console.error('Query failed:', error);
process.exit(1);
});
}
export { queryDistributions };