Deposit
How to deposit tokens into the privacy pool
Deposit
Section titled “Deposit”Depositing tokens into the privacy pool is the first step in a private transfer. This guide covers the complete deposit flow.
Overview
Section titled “Overview”Step-by-Step Guide
Section titled “Step-by-Step Guide”-
Generate Secrets
Generate cryptographically secure random values for your nullifier and secret:
import { randomBytes } from 'crypto';// Generate 32 random bytes for eachconst nullifier = randomBytes(32);const secret = randomBytes(32); -
Compute Commitment
Hash the nullifier and secret using Poseidon:
import { poseidon } from '[PLACEHOLDER: POSEIDON_NPM_PACKAGE]';// Commitment = poseidon(nullifier, secret)const commitment = poseidon([nullifier, secret]); -
Build Deposit Instruction
Create the Anchor instruction for depositing:
import { Program, BN } from '@coral-xyz/anchor';import { PublicKey, SystemProgram, SYSVAR_RENT_PUBKEY } from '@solana/web3.js';import { TOKEN_PROGRAM_ID, getAssociatedTokenAddress } from '@solana/spl-token';const PROGRAM_ID = new PublicKey('FGKoWNsvTTDCGW9JyR2DJWNzSXpejWk7yXcKsFFj9GQp');const DEPTH = 32;async function buildDepositInstruction(program: Program,payer: PublicKey,mint: PublicKey,commitment: Uint8Array,amount: BN) {// Derive PDAsconst [settings] = PublicKey.findProgramAddressSync([Buffer.from('Settings')],PROGRAM_ID);const [merkle] = PublicKey.findProgramAddressSync([Buffer.from('Merkle'), mint.toBuffer(), Buffer.from([DEPTH])],PROGRAM_ID);const [merkleToken] = PublicKey.findProgramAddressSync([Buffer.from('MerkleToken'), mint.toBuffer(), Buffer.from([DEPTH])],PROGRAM_ID);const payerTokenAccount = await getAssociatedTokenAddress(mint, payer);return program.methods.deposit({commitment: Array.from(commitment),amount,}).accounts({signer: payer,signerTokenAccount: payerTokenAccount,settings,merkle,merkleTokenAccount: merkleToken,mint,systemProgram: SystemProgram.programId,tokenProgram: TOKEN_PROGRAM_ID,rent: SYSVAR_RENT_PUBKEY,}).instruction();} -
Sign and Send Transaction
import { Transaction, sendAndConfirmTransaction } from '@solana/web3.js';async function deposit(connection: Connection,payer: Keypair,mint: PublicKey,amount: BN) {// 1. Generate secretsconst nullifier = randomBytes(32);const secret = randomBytes(32);// 2. Compute commitmentconst commitment = poseidon([nullifier, secret]);// 3. Build instructionconst ix = await buildDepositInstruction(program,payer.publicKey,mint,commitment,amount);// 4. Send transactionconst tx = new Transaction().add(ix);const signature = await sendAndConfirmTransaction(connection, tx, [payer]);return {signature,nullifier,secret,commitment,};} -
Store Secrets Securely
After a successful deposit, you MUST store:
interface DepositRecord {nullifier: Uint8Array;secret: Uint8Array;commitment: Uint8Array;leafIndex: number; // Get from transaction eventsmint: string;amount: string;txSignature: string;timestamp: number;}// Store securely - this is just an examplefunction storeDeposit(record: DepositRecord) {const encrypted = encrypt(JSON.stringify({nullifier: Array.from(record.nullifier),secret: Array.from(record.secret),// ... other fields}));localStorage.setItem(`deposit_${record.txSignature}`, encrypted);}
Complete Example
Section titled “Complete Example”import { Connection, Keypair, PublicKey } from '@solana/web3.js';import { Program, AnchorProvider, BN } from '@coral-xyz/anchor';import { randomBytes } from 'crypto';import { poseidon } from '[PLACEHOLDER: POSEIDON_NPM_PACKAGE]';
const PROGRAM_ID = new PublicKey('FGKoWNsvTTDCGW9JyR2DJWNzSXpejWk7yXcKsFFj9GQp');const DEPTH = 32;
async function performDeposit( connection: Connection, wallet: { publicKey: PublicKey; signTransaction: Function }, mint: PublicKey, amountInBaseUnits: number): Promise<DepositResult> { // Generate secrets (CLIENT-SIDE ONLY) const nullifier = randomBytes(32); const secret = randomBytes(32); const commitment = poseidon([nullifier, secret]);
// Build and send transaction // ... (use instruction builder from above)
// Get leaf index from transaction logs/events const leafIndex = await parseLeafIndexFromTx(signature);
// Return secrets for storage return { nullifier, secret, commitment, leafIndex, signature, };}Important Considerations
Section titled “Important Considerations”Deposit Sizes
Section titled “Deposit Sizes”Tokens are deposited into pools based on amount. Check available deposit sizes for each token:
// Common deposit sizes (in base units)// Actual sizes depend on token and configurationconst depositSizes = [ 1_000_000, // 1 USDC 10_000_000, // 10 USDC 100_000_000, // 100 USDC];A deposit fee is deducted from your deposit:
- Fee: [PLACEHOLDER: FEE_DEPOSIT_PCT]
See Fee Structure for details.
Getting Leaf Index
Section titled “Getting Leaf Index”The leaf index is emitted in transaction logs. Parse it for later use:
async function parseLeafIndexFromTx( connection: Connection, signature: string): Promise<number> { const tx = await connection.getTransaction(signature, { maxSupportedTransactionVersion: 0, });
// Parse program logs for leaf index // Implementation depends on how events are emitted // ...}Error Handling
Section titled “Error Handling”| Error | Cause | Solution |
|---|---|---|
InsufficientFunds | Not enough tokens | Check balance before deposit |
InvalidDepositSize | Amount not in allowed sizes | Use a valid deposit size |
MerkleTreeFull | Tree has max deposits | Wait for new tree or use different size |
Security Checklist
Section titled “Security Checklist”- Generate secrets client-side only
- Use cryptographically secure random
- Store secrets encrypted
- Provide backup mechanism to users
- Never log or transmit secrets