Privacy Pool SDK for Solana - Shield and Unshield SOL privately
npm install whistle-sol-privacybash
npm install whistle-sol-privacy @solana/web3.js
`
`bash
yarn add whistle-sol-privacy @solana/web3.js
`
`bash
pnpm add whistle-sol-privacy @solana/web3.js
`
Quick Start
`typescript
import { WhistleZK } from 'whistle-zk-privacy';
import { useWallet } from '@solana/wallet-adapter-react';
// Initialize the SDK
const zk = new WhistleZK();
// Shield 1 SOL
const { note, signature } = await zk.shield(wallet, 1);
// ⚠️ IMPORTANT: Save the note securely!
console.log('Save this note:', note.note);
// Later: Unshield to any address
const result = await zk.unshield(note, 'RecipientAddress...');
console.log('Withdrawn:', result.amountReceived, 'SOL');
`
Configuration
`typescript
import { WhistleZK } from 'whistle-zk-privacy';
const zk = new WhistleZK({
// Custom RPC endpoint
rpcUrl: 'https://your-rpc.com',
// Custom relayer (for self-hosted)
relayerUrl: 'https://your-relayer.com',
// Transaction commitment level
commitment: 'confirmed', // 'processed' | 'confirmed' | 'finalized'
});
`
API Reference
$3
Main SDK class for interacting with the ZK Privacy Pool.
#### Constructor
`typescript
new WhistleZK(config?: WhistleZKConfig)
`
#### Methods
##### shield(wallet, amount)
Deposit SOL into the privacy pool.
`typescript
const { note, signature } = await zk.shield(wallet, 1);
`
| Parameter | Type | Description |
|-----------|------|-------------|
| wallet | WalletAdapter | Wallet with signTransaction |
| amount | number | Amount in SOL (0.01, 0.1, 1, 10, or 100) |
Returns: Promise
##### unshield(note, recipient)
Withdraw SOL from the privacy pool.
`typescript
const result = await zk.unshield(savedNote, recipientAddress);
`
| Parameter | Type | Description |
|-----------|------|-------------|
| note | PrivateNote \| string | The private note |
| recipient | string \| PublicKey | Recipient address |
Returns: Promise
##### getPoolStats()
Get current pool statistics.
`typescript
const stats = await zk.getPoolStats();
console.log('TVL:', stats.tvl, 'SOL');
`
Returns: Promise
##### isNoteSpent(note)
Check if a note has already been spent.
`typescript
const spent = await zk.isNoteSpent(note);
`
Returns: Promise
##### parseNote(noteString)
Parse a note string into components.
`typescript
const { amount, nullifier, secret } = zk.parseNote('whistle_1sol_abc...');
`
##### isValidNote(noteString)
Validate a note string format.
`typescript
if (zk.isValidNote(userInput)) {
// Valid note
}
`
##### getDenominations()
Get supported denomination amounts.
`typescript
const amounts = zk.getDenominations(); // [0.01, 0.1, 1, 10, 100]
`
Types
$3
`typescript
interface PrivateNote {
id: string;
note: string; // The note string to save
amount: number; // Amount in SOL
commitment: string; // Commitment hash
nullifier: string; // Nullifier (used to prevent double-spend)
secret: string; // Keep private!
leafIndex: number; // Position in Merkle tree
timestamp: number; // Creation timestamp
spent: boolean; // Whether note has been spent
}
`
$3
`typescript
interface ShieldResult {
note: PrivateNote; // Save this securely!
signature: string; // Transaction signature
transaction: Transaction;
}
`
$3
`typescript
interface UnshieldResult {
signature: string; // Transaction signature
amountReceived: number; // Amount after fees
relayerFee: number; // Fee paid to relayer
}
`
$3
`typescript
interface PoolStats {
tvl: number; // Total value locked (SOL)
totalDeposits: number; // Number of deposits
totalWithdrawals: number;
merkleRoot: string; // Current Merkle root
nextIndex: number; // Next leaf index
}
`
React Integration
`tsx
import { useMemo, useState } from 'react';
import { useWallet } from '@solana/wallet-adapter-react';
import { WhistleZK, PrivateNote } from 'whistle-sol-privacy';
function PrivacyPool() {
const wallet = useWallet();
const zk = useMemo(() => new WhistleZK(), []);
const [notes, setNotes] = useState([]);
const handleShield = async (amount: number) => {
if (!wallet.publicKey) return;
const { note } = await zk.shield(wallet, amount);
// Save note to storage
setNotes(prev => [...prev, note]);
localStorage.setItem('zk-notes', JSON.stringify([...notes, note]));
};
const handleUnshield = async (note: PrivateNote) => {
const result = await zk.unshield(note, wallet.publicKey!);
// Mark note as spent
setNotes(prev => prev.map(n =>
n.id === note.id ? { ...n, spent: true } : n
));
};
return (
{/ Your UI /}
);
}
``