@opusgamelabs/snapshotter
v0.0.6
Published
Solana token snapshot and merkle proof generation utilities
Readme
Solana Token Holders Fetcher with Committee Verification
Get all holders of a Solana token using the Helius API and Bun runtime, with optional Multi-Party Computation (MPC) committee verification for decentralized, trustless snapshots.
Overview
This tool fetches all token holders for any SPL token on Solana using the Helius getTokenAccounts API method. It handles pagination automatically and can process tokens with millions of holders.
🆕 Committee Verification (MPC/TSS)
NEW: Snapshots can now be cryptographically signed by multiple committee members using Multi-Party Computation (MPC) and Threshold Signature Schemes (TSS). This ensures:
- ✅ Decentralization: No single party controls the snapshot
- ✅ Trustless Verification: Cryptographic proofs instead of trust
- ✅ Threshold Security: Requires m-of-n signatures (e.g., 2-of-3)
- ✅ Tamper-Proof: SHA-256 hashing ensures data integrity
Features
- ✅ Fetch all holders of any Solana token
- ✅ Token2022 (Token Extensions) support with automatic detection
- ✅ Automatic pagination handling
- ✅ Track holder balances (handles multiple token accounts per owner)
- ✅ Jupiter API integration for token metadata (price, decimals, market cap)
- ✅ USD value calculations for each holder
- ✅ Filtering by minimum USD holdings (configurable threshold)
- ✅ Market cap filtering (skip tokens below threshold)
- ✅ Sort holders by balance
- ✅ Export to JSON files
- ✅ Built with Bun for blazing fast performance
- ✅ TypeScript for type safety
- ✅ Committee-based verification with MPC/TSS
- ✅ Cryptographic snapshot signatures
- ✅ Configurable threshold requirements
Prerequisites
- Bun installed on your system
- A free Helius API key
Installation
Clone this repository or download the files
Install Bun if you haven't already:
curl -fsSL https://bun.sh/install | bash- Install dependencies:
bun install- Copy
.env.exampleto.envand add your Helius API key:
cp .env.example .env- Edit
.envand add your API key and optional filters:
HELIUS_API_KEY=your_actual_api_key_here
TOKEN_MINT=DezXAZ8z7PnrnRJjz3wXBoRgixCa6xjnB7YaB1pPB263
# Optional: Filter by minimum USD holdings (e.g., $10)
MIN_USD_HOLDINGS=10
# Optional: Skip tokens below market cap threshold (e.g., $100,000)
MIN_MCAP=100000Usage
Option 1: Monitored Snapshot (Standard)
Run a snapshot with real-time transfer monitoring for accuracy:
# Basic usage
bun run start
# Custom token
HELIUS_API_KEY=your_key TOKEN_MINT=your_mint_address bun run start
# With USD holdings filter (only holders with $50+)
MIN_USD_HOLDINGS=50 bun run start
# With market cap filter (skip tokens below $1M market cap)
MIN_MCAP=1000000 bun run start
# Development mode with auto-reload
bun run devThis will:
- Start WebSocket monitoring to track transfers in real-time
- Fetch all token holders via paginated API
- Reconcile any balance changes that occurred during the fetch
- Generate accurate snapshot at a specific slot
Output: snapshot-monitored.json
Option 2: Committee-Verified Snapshot (Production Recommended)
For decentralized, tamper-proof snapshots with multi-party verification:
# Basic usage
bun run committee
# Custom token and network
HELIUS_API_KEY=your_key TOKEN_MINT=your_mint_address NETWORK=mainnet-beta bun run committee
# With filters
MIN_USD_HOLDINGS=100 MIN_MCAP=500000 bun run committee
# Development mode with auto-reload
bun run committee:devThis will:
- Start WebSocket monitoring (same as Option 1)
- Fetch all token holders with reconciliation
- Initialize a committee of validators
- Create cryptographic signatures from multiple parties
- Generate a verified snapshot with proof of authenticity
Output: verified-snapshot.json, committee-info.json, token-holders.json
Using Environment Variables
Set your API key as an environment variable:
export HELIUS_API_KEY=your_api_key_here
bun run start # For simple fetch
# or
bun run committee # For committee-verified snapshotPopular Token Mint Addresses
- USDC:
EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v - BONK:
DezXAZ8z7PnrnRJjz3wXBoRgixCa6xjnB7YaB1pPB263 - SOL (Wrapped):
So11111111111111111111111111111111111111112 - USDT:
Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB
Note: The system automatically detects whether a token uses the standard Token program or Token2022 (Token Extensions) program.
Output
Monitored Snapshot Output
The script generates snapshot-monitored.json:
{
"metadata": {
"tokenMint": "DezXAZ8z7PnrnRJjz3wXBoRgixCa6xjnB7YaB1pPB263",
"targetSlot": 245678901,
"startSlot": 245678800,
"slotDifference": 101,
"totalHolders": 2543,
"totalSupply": 1000000000000,
"totalUsdValue": 42500.50,
"stateChanges": 15,
"timestamp": "2024-01-15T10:30:00.000Z",
"tokenInfo": {
"name": "Bonk",
"symbol": "BONK",
"decimals": 5,
"usdPrice": 0.00000425,
"mcap": 425000
},
"filters": {
"minUsdHoldings": 10,
"minMcap": 100000
}
},
"holders": [
{
"owner": "111An9SVxuPpgjnuXW9Ub7hcVmZpYNrYZF4edsGwJEW",
"totalBalance": 1000000000,
"usdValue": 4250.00
},
...
]
}Committee Mode Output
The committee-verified snapshot generates three JSON files:
verified-snapshot.json: Complete verified snapshot with signatures
{
"metadata": {
"tokenMint": "DezXAZ8z7PnrnRJjz3wXBoRgixCa6xjnB7YaB1pPB263",
"timestamp": 1698765432000,
"blockHeight": 245678901,
"totalHolders": 2543,
"totalSupply": 1000000000000,
"snapshotHash": "a1b2c3d4...",
"signatures": [
{
"memberId": "member-1",
"signature": "abc123...",
"publicKey": "9WzDXwBbmkg..."
}
]
},
"holders": [...],
"isVerified": true,
"verificationThreshold": 2,
"signatureCount": 2
}committee-info.json: Committee member information
{
"members": [
{
"id": "member-1",
"name": "Primary Validator",
"publicKey": "9WzDXwBbmkg..."
}
],
"threshold": 2,
"totalMembers": 3,
"aggregatedPublicKey": "3HSYXeGc3Lj..."
}token-holders.json: Simple list of holder addresses
How It Works
Monitored Snapshot Mode
- Fetch Metadata: Retrieve token information from Jupiter API (decimals, price, market cap)
- Market Cap Filter: Skip tokens below minimum market cap threshold (if configured)
- Start Monitoring: WebSocket connection subscribes to all token account changes
- Automatically detects Token vs Token2022 program
- Fetch Pages: API pagination fetches token accounts in batches of 1000
- Track Changes: During fetch, any transfers are recorded with slot numbers
- Reconcile: Applies monitored state changes to accounts fetched earlier
- Calculate USD Values: Convert raw balances to real amounts and USD values
- Apply Filters: Remove holders below minimum USD holdings threshold (if configured)
- Target Slot: Final snapshot represents state at the END of the fetch operation
- Export: Save accurate snapshot with metadata
Why Monitoring? During pagination (which can take seconds or minutes for large tokens), balances can change. Monitoring ensures we capture those changes and reconcile to a specific slot for accuracy.
Why Jupiter Integration? Provides essential token metadata:
- Decimals: Convert raw amounts to human-readable values
- USD Price: Calculate holder values in dollars
- Market Cap: Filter out low-quality or scam tokens
Committee Mode (MPC/TSS)
Built on top of the monitored snapshot, adds cryptographic verification:
- Fetch & Filter: Same as monitored mode - includes metadata, USD calculations, and filtering
- Initialize Committee: Generate keypairs for each committee member using TSS
- Create Snapshot Hash: Generate SHA-256 hash of the snapshot data (holders + metadata)
- Multi-Party Signing:
- Step 1: Each member creates a signing commitment (public nonce)
- Step 2: Members exchange nonces and create partial signatures
- Step 3: Partial signatures are aggregated into a valid multi-sig
- Verification: Anyone can verify the snapshot using the committee's aggregated public key
- Export: Save verified snapshot with cryptographic proofs
Why Committee Verification?
Problem: Traditional snapshots are centralized - one party controls the data
- Single point of failure
- Potential for manipulation
- Requires trust in operator
Solution: Multi-Party Computation (MPC) + Threshold Signatures (TSS)
- Requires multiple independent parties to sign
- No single party can manipulate the snapshot
- Cryptographically verifiable
- Threshold requirement (e.g., 2-of-3 signatures needed)
Example: For a 2-of-3 committee:
- 3 independent validators are selected
- Snapshot is only valid with 2+ signatures
- Even if 1 validator is compromised, snapshot remains secure
API Rate Limits
Helius free tier includes:
- 10 requests per second
- 100,000 requests per day
For larger tokens, consider:
- Adding rate limiting delays
- Using a paid Helius plan
- Implementing retry logic
Project Structure
src/
├── shared/
│ └── snapshotCore.ts # Core snapshot logic (monitoring, fetching, reconciliation)
├── snapshotWithMonitoring.ts # Standard monitored snapshot
├── committeeSnapshot.ts # Committee-verified snapshot
├── committeeCoordinator.ts # MPC/TSS coordination logic
└── committeeConfig.ts # Committee configuration
Additional Documentation:
├── COMMITTEE_GUIDE.md # Detailed committee setup guide
├── TODO.md # Development roadmap
└── SNAPSHOT_UNWINDING.md # Unwinding protocol documentationShared Architecture
Both snapshot modes use the same core components from src/shared/snapshotCore.ts:
- AccountMonitor: WebSocket-based real-time transfer monitoring
- fetchAllAccounts(): Paginated account fetching with slot tracking
- reconcileToTargetSlot(): Balance reconciliation logic
This ensures consistency and maintainability across different snapshot types.
Use Cases
Monitored Snapshot Mode
- Quick Analytics: Fast, accurate token holder analysis
- Development Testing: Test airdrops or features with precise snapshots
- Personal Use: Small-scale holder tracking with real-time accuracy
- Integration: Embed in applications needing current holder data
Committee Mode (Recommended for Production)
- Airdrops: Provably fair token distribution with verifiable snapshots
- Governance: Trustless snapshot for DAO voting eligibility
- Compliance: Auditable holder records with cryptographic proof
- Token Distribution: Fair launch with multi-party verification
- Transparency: Public verification of snapshot integrity
- Legal Requirements: Tamper-proof records for regulatory compliance
Real-World Example:
A DAO wants to airdrop tokens to existing holders. Using committee verification:
- 3 independent validators create a snapshot
- Requires 2-of-3 signatures to be valid
- Community can verify the snapshot hash
- No single party can manipulate holder list
- Cryptographic proof ensures fairness
Committee Configuration
You can customize the committee setup by editing committeeConfig.ts:
export const DEFAULT_COMMITTEE: CommitteeConfig = {
threshold: 2, // Require 2 signatures
totalMembers: 3, // Out of 3 total members
members: [
{
id: 'member-1',
name: 'Primary Validator',
publicKey: '', // Generated at runtime
endpoint: 'https://validator1.example.com',
},
// Add more members...
],
network: 'devnet', // 'mainnet-beta' | 'devnet' | 'testnet'
};Security Best Practices
- Distribute Committee Members: Use independent validators from different organizations
- Secure Key Storage: Store member private keys in secure, isolated environments
- Regular Key Rotation: Periodically regenerate committee keys
- Monitor Signatures: Track which members sign each snapshot
- Backup Keys: Securely backup committee member keys
- Use Hardware Wallets: For production, consider hardware wallet integration
Troubleshooting
"Cannot find module 'bun'" error
This is a TypeScript error that can be safely ignored when running with Bun. The code will work correctly.
API Key Error
Make sure your .env file exists and contains a valid API key:
cat .envNo Results
- Verify the token mint address is correct
- Check that the token has been minted and has holders
- Ensure your API key is valid
Committee Initialization Fails
- Check network connectivity to Solana RPC
- Verify you're using a supported network (mainnet-beta, devnet, testnet)
- Ensure sufficient SOL for transaction fees (devnet/testnet only)
Insufficient Signatures
- Verify threshold is set correctly (must be ≤ total members)
- Check that enough committee members are participating
- Review committee configuration in
committeeConfig.ts
Contributing
Contributions are welcome! Feel free to:
- Report bugs
- Suggest features
- Submit pull requests
License
MIT
Technical Architecture
Core Snapshot Engine
Shared Components (src/shared/snapshotCore.ts):
┌─────────────────────────────────────────────────────┐
│ AccountMonitor │
│ WebSocket subscription to track real-time changes │
└────────────┬────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────┐
│ fetchAllAccounts() │
│ Paginated API calls with slot tracking │
└────────────┬────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────┐
│ reconcileToTargetSlot() │
│ Apply monitored changes to fetched accounts │
└────────────┬────────────────────────────────────────┘
│
▼
Accurate SnapshotMonitoring Flow
Time: ────────────────────────────────────────►
[Monitor Start]
│
├──► Page 1 fetch (slot 100)
├──► Transfer detected! (slot 105)
├──► Page 2 fetch (slot 110)
├──► Page 3 fetch (slot 115)
│
[Monitor Stop] → Target Slot: 115
│
▼
Reconcile accounts:
- Accounts from slot 100-110: apply changes up to slot 115
- Accounts from slot 115: already current
│
▼
Final snapshot at slot 115 ✓MPC/TSS Implementation
This project uses the solana-mpc-tss-lib package which implements:
- Ed25519 TSS: Threshold signatures for Solana
- ZenGo-X Compatible: 100% API compatibility with ZenGo-X/solana-tss
- WASM Optimized: High-performance cryptography with automatic fallback
Committee Verification Flow
┌──────────────────┐
│ Monitored Fetch │
│ (Shared Code) │
└────────┬─────────┘
│
▼
┌──────────────────┐
│ Initialize │
│ Committee │
│ (Generate Keys) │
└────────┬─────────┘
│
▼
┌──────────────────┐
│ Create Hash │
│ (SHA-256) │
└────────┬─────────┘
│
▼
┌──────────────────┐ ┌──────────────┐
│ Step 1: Nonce │◄────►│ Member 1 │
│ Exchange │ │ Member 2 │
└────────┬─────────┘ │ Member 3 │
│ └──────────────┘
▼
┌──────────────────┐
│ Step 2: Sign │
│ (Partial Sigs) │
└────────┬─────────┘
│
▼
┌──────────────────┐
│ Step 3: Verify │
│ & Aggregate │
└────────┬─────────┘
│
▼
┌──────────────────┐
│ Verified Output │
│ (JSON files) │
└──────────────────┘Advanced Features
Token2022 Support
The system automatically detects and supports both:
- Token Program:
TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA - Token2022 Program:
TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb
No configuration needed - detection is automatic by checking the mint's owner program.
Filtering Options
Minimum USD Holdings
Filter out holders below a certain dollar value:
MIN_USD_HOLDINGS=50 bun run startThis is useful for:
- Focusing on significant holders
- Reducing airdrop costs
- Filtering dust accounts
- Compliance requirements
Minimum Market Cap
Skip tokens below a market cap threshold:
MIN_MCAP=100000 bun run startThis helps:
- Avoid scam tokens
- Focus on established projects
- Automate quality filtering
- Prevent wasted API calls
Jupiter API Integration
Token metadata is automatically fetched from Jupiter API:
- Decimals: For converting raw to real amounts
- USD Price: Current market price
- Market Cap: Total value
- Symbol & Name: Token identification
- Total Supply: Circulating supply
Example calculation:
Raw balance: 1000000000
Decimals: 9
Real amount: 1000000000 / 10^9 = 1.0 tokens
USD price: $0.50
USD value: 1.0 × $0.50 = $0.50Resources
- Helius Documentation
- Solana Token Program
- Token2022 (Token Extensions)
- Jupiter API
- Bun Documentation
- Original Guide
- solana-mpc-tss-lib
- Multi-Party Computation (MPC)
- Threshold Signatures
Acknowledgments
Based on the guide by Owen Venter at Helius.
