whistle-zk-privacy
v1.0.3
Published
ZK Privacy Pool SDK for Solana - Shield and Unshield SOL with zero-knowledge proofs
Downloads
365
Maintainers
Readme
whistle-zk-privacy
ZK Privacy Pool SDK for Solana - Shield and Unshield SOL with zero-knowledge proofs
Overview
The Whistle ZK Privacy SDK enables developers to integrate private SOL transactions into their Solana applications. Using zero-knowledge proofs (Groth16), users can:
- Shield - Deposit SOL into an anonymous pool
- Unshield - Withdraw SOL to any address without linking to the original deposit
Installation
npm install whistle-zk-privacy @solana/web3.jsyarn add whistle-zk-privacy @solana/web3.jspnpm add whistle-zk-privacy @solana/web3.jsQuick Start
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
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
WhistleZK
Main SDK class for interacting with the ZK Privacy Pool.
Constructor
new WhistleZK(config?: WhistleZKConfig)Methods
shield(wallet, amount)
Deposit SOL into the privacy pool.
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<ShieldResult>
unshield(note, recipient)
Withdraw SOL from the privacy pool.
const result = await zk.unshield(savedNote, recipientAddress);| Parameter | Type | Description |
|-----------|------|-------------|
| note | PrivateNote \| string | The private note |
| recipient | string \| PublicKey | Recipient address |
Returns: Promise<UnshieldResult>
getPoolStats()
Get current pool statistics.
const stats = await zk.getPoolStats();
console.log('TVL:', stats.tvl, 'SOL');Returns: Promise<PoolStats>
isNoteSpent(note)
Check if a note has already been spent.
const spent = await zk.isNoteSpent(note);Returns: Promise<boolean>
parseNote(noteString)
Parse a note string into components.
const { amount, nullifier, secret } = zk.parseNote('whistle_1sol_abc...');isValidNote(noteString)
Validate a note string format.
if (zk.isValidNote(userInput)) {
// Valid note
}getDenominations()
Get supported denomination amounts.
const amounts = zk.getDenominations(); // [0.01, 0.1, 1, 10, 100]Types
PrivateNote
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
}ShieldResult
interface ShieldResult {
note: PrivateNote; // Save this securely!
signature: string; // Transaction signature
transaction: Transaction;
}UnshieldResult
interface UnshieldResult {
signature: string; // Transaction signature
amountReceived: number; // Amount after fees
relayerFee: number; // Fee paid to relayer
}PoolStats
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
import { useMemo, useState } from 'react';
import { useWallet } from '@solana/wallet-adapter-react';
import { WhistleZK, PrivateNote } from 'whistle-zk-privacy';
function PrivacyPool() {
const wallet = useWallet();
const zk = useMemo(() => new WhistleZK(), []);
const [notes, setNotes] = useState<PrivateNote[]>([]);
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 (
<div>
{/* Your UI */}
</div>
);
}Security Considerations
- Never share your private notes - Anyone with the note can withdraw the funds
- Store notes securely - Use encrypted storage or a password manager
- Verify transactions - Always confirm on-chain before considering deposits final
- Use fixed denominations - This enhances privacy by making deposits indistinguishable
Supported Denominations
| Amount | Privacy Level | |--------|---------------| | 0.01 SOL | Good for micro-transactions | | 0.1 SOL | Common amount, good anonymity | | 1 SOL | Most popular, best anonymity set | | 10 SOL | Large transactions | | 100 SOL | Whale transactions |
Links
- Website: whistle.ninja/privacy
- Documentation: docs.whistle.ninja
- GitHub: github.com/DylanPort/Whistle-----Privacy-Protocol-Demo
- Discord: discord.gg/whistle
License
MIT © Whistle Protocol
