# 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. 