A Next.js application for distributing SPL tokens using ZK Compression - making token airdrops ~5000x cheaper than regular SPL tokens.
- ZK Compressed Tokens: Rent-free token accounts stored in Merkle trees
- Cost Efficient: ~5000x cheaper than standard SPL tokens
- Direct Minting: Simple authority-based distribution (no merkle proofs needed)
- Batch Processing: Configurable batch sizes for optimal transaction handling
- Wallet Integration: Connect with Phantom, Solflare, and other Solana wallets
- Framework: Next.js 15 with App Router
- Styling: Tailwind CSS + Shadcn UI
- Solana SDK: Gill + Wallet UI components
- ZK Compression: @lightprotocol/compressed-token + stateless.js
- Node.js (v22 or higher)
- npm (recommended) or npm
- Solana Wallet with devnet SOL
- Helius RPC API Key (free tier works) - Get one here
Why Helius? ZK Compression requires special Photon indexer nodes to query compressed accounts and generate validity proofs. Helius runs these indexers for free on devnet. Regular Solana RPC nodes can't parse compressed account data stored in Merkle trees.
npm installCreate a .env.local file in the project root:
# Helius RPC endpoint (required for ZK compression)
RPC_ENDPOINT=https://devnet.helius-rpc.com?api-key=YOUR_HELIUS_API_KEY
# Your wallet's private key (Base58 encoded) - KEEP THIS SECRET!
DEV_WALLET=your_base58_private_key_hereImportant:
- Get your Helius API key from https://dev.helius.xyz/
- To get your Base58 private key from Phantom: Settings → Show Private Key → Copy
DEV_WALLETworks for both scripts and frontend (via next.config.ts)- Never commit your
.env.localfile!
Make sure your wallet has some devnet SOL:
solana airdrop 2 YOUR_WALLET_ADDRESS --url devnetOr use the Solana Faucet.
This single command will:
- Generate test wallet recipients
- Create a compressed token mint
- Generate the airdrop recipients list
npm run airdrop:setupnpm run devOpen http://localhost:3000 and execute the airdrop!
If you want to run the setup steps individually:
# Generate test wallets for recipients
npm run airdrop:wallets
# Create a new compressed token mint
npm run airdrop:mint
# Generate recipients list from test wallets
npm airdrop:recipientsCreates an SPL token registered with Light Protocol's compression program. The token pool enables rent-free compressed accounts.
Generates Solana keypairs that will receive the airdrop tokens. In production, you'd load real recipient addresses.
The mint authority calls mintTo() in batches, directly minting compressed tokens to recipients. No merkle proofs needed - the authority can mint directly.
Splits recipients across multiple transactions to stay within transaction size limits. Configurable via UI slider (max 50 per batch).
├── scripts/
│ ├── create-compressed-mint.ts # Create compressed token mint
│ ├── generate-test-wallets.ts # Generate recipient wallets
│ └── generate-recipients.ts # Prepare airdrop data
├── src/
│ ├── app/
│ │ └── page.tsx # Main airdrop UI
│ ├── components/
│ │ ├── airdrop/ # Airdrop UI components
│ │ │ ├── airdrop-executor.tsx # Main orchestrator
│ │ │ ├── airdrop-stats.tsx # Display token info
│ │ │ ├── airdrop-progress.tsx # Progress tracking
│ │ │ ├── airdrop-controls.tsx # Batch controls
│ │ │ └── airdrop-alerts.tsx # Status alerts
│ │ └── ui/ # Shadcn components
│ ├── hooks/
│ │ └── use-airdrop.ts # Airdrop execution hook
│ └── lib/
│ └── airdrop.ts # Core airdrop logic
Standard SPL Tokens:
- Each token account costs ~0.002 SOL rent
- 1000 recipients = ~2 SOL in rent fees
- Visible on standard explorers
ZK Compressed Tokens:
- Token accounts stored in Merkle trees
- About 5000x cheaper (no rent!)
- 1000 recipients = ~0.0004 SOL
- Requires ZK Compression indexer to query
Compressed token accounts are stored in on-chain Merkle trees, not as individual accounts. Standard explorers can't decode this data structure. To query compressed accounts:
- Mint address on Solana Explorer (shows the mint, not individual accounts)
- Photon RPC with special methods like
getCompressedTokenAccountsByOwner - RPC providers that run Photon indexers (Helius or run your own)
Edit scripts/create-compressed-mint.ts:
const decimals = 9 // Token decimals
const config = {
name: 'My Airdrop Token',
symbol: 'AIRDROP',
// ... rest of config
}Edit scripts/generate-recipients.ts:
return wallets.map((w, i) => ({
address: address(w.address),
amount: 100 * (i + 1), // Customize amounts here
}))Edit src/components/airdrop/airdrop-executor.tsx:
maxBatchSize={Math.min(airdropData.recipients.length, 50)} // Change 50 to your max| Script | Description |
|---|---|
npm airdrop:setup |
Complete setup: wallets + mint + recipients |
npm airdrop:wallets |
Generate test wallet recipients |
npm airdrop:mint |
Create compressed token mint |
npm airdrop:recipients |
Generate airdrop recipients list |
npm dev |
Start development server |
npm build |
Build for production |
Make sure you've created .env.local and added your Helius RPC endpoint.
Add your Base58 private key to DEV_WALLET in .env.local. It's exposed to both scripts and frontend via next.config.ts.
Your Helius API key is incorrect. Get a new one from https://dev.helius.xyz/
The connected wallet must match the mint authority. Connect the wallet whose private key is in DEV_WALLET.
This is expected! Compressed tokens use Merkle trees. Check the mint address on Solana Explorer instead.
- Update recipient list: Replace test wallets with real addresses
- Secure your keys: Use environment variables, never commit keys
- Use mainnet RPC: Update
RPC_ENDPOINTto Helius mainnet - Test thoroughly: Always test on devnet first
- Monitor transactions: Keep track of successful/failed mints
- ZK Compression Documentation
- Photon Indexer (Terminology)
- ZK Compression RPC Methods
- Run Your Own Indexer
- Light Protocol GitHub
- Gill SDK
- Solana Wallet UI
This project is based on the gill-next-tailwind template from the Solana Foundation.