Quick Start
Integrate private payments with the ZERA SDK
The @zera-labs/sdk TypeScript package provides everything you need: Poseidon hashing, Merkle tree management, Groth16 proof generation, Solana transaction builders, and note storage backends.
Architecture Overview
The SDK is organized into four layers, each buildable independently:
Layer 1 — zera-core (Rust)Portable crypto primitives: Poseidon X5 hashing (BN254),incremental Merkle tree, note types, PDA derivation,Groth16 proof formatting. Zero Solana dependency.Layer 2 — zera-neon (Rust → Node.js via napi-rs)Native bindings for high-performance Poseidon hashingand Merkle operations in JavaScript environments.Layer 3 — @zera-labs/sdk (TypeScript)Primary integration surface. Proof generators, tx builders,tree state client, note storage, voucher system.Layer 4 — @zera-labs/mcp-serverModel Context Protocol server exposing SDK operationsas tools for AI agents (Claude, GPT, etc.)
Shielded Deposit
A complete deposit flow in ~15 lines:
import { createNote, computeCommitment } from '@zera-labs/sdk'import { generateDepositProof } from '@zera-labs/sdk/prover'import { buildDepositTransaction } from '@zera-labs/sdk/tx'// 1. Create a shielded note (random secret + blinding)const note = createNote(amount, assetMint, memo)// 2. Compute Poseidon commitment (Merkle leaf)const commitment = computeCommitment(note)// commitment = Poseidon(amount, secret, blinding, asset, memo[0..3])// 3. Generate Groth16 proofconst { proof } = await generateDepositProof(note, wasmPath, zkeyPath)// 4. Build and send Solana transactionconst tx = await buildDepositTransaction({commitment, proof, mint, payer, amount})// On-chain: verify proof → insert leaf → update Merkle root
Shielded Withdrawal
Withdraw from the shielded pool by proving Merkle membership and revealing a nullifier:
import { computeNullifier } from '@zera-labs/sdk'import { generateWithdrawProof } from '@zera-labs/sdk/prover'import { buildWithdrawTransaction } from '@zera-labs/sdk/tx'import { MerkleTree } from '@zera-labs/sdk/merkle-tree'// 1. Sync Merkle tree from chainconst tree = MerkleTree.create(24) // height=24, 16M capacity// ... insert on-chain leaves// 2. Compute nullifier (single-spend tag)const nullifier = computeNullifier(note.secret, commitment)// nullifier = Poseidon(secret, commitment)// 3. Generate Groth16 proofconst { proof, nullifierHash } = await generateWithdrawProof(note, leafIndex, tree, recipientHash, wasmPath, zkeyPath)// 4. Build transactionconst tx = await buildWithdrawTransaction({proof, nullifierHash, recipientHash, mint, payer})// On-chain: verify proof → check nullifier PDA doesn't exist// → create nullifier PDA → transfer tokens
Shielded Transfer (Note Splitting)
Split one note into two output notes — the 1-input 2-output circuit enforces value conservation:
import { generateTransferProof } from '@zera-labs/sdk/prover'import { buildTransferTransaction } from '@zera-labs/sdk/tx'// Split a $100 note into $30 + $70const outputNote1 = createNote(30_000_000n, usdcMint)const outputNote2 = createNote(70_000_000n, usdcMint)const { proof, nullifierHash, outputCommitment1, outputCommitment2 }= await generateTransferProof(inputNote, inputLeafIndex, tree,outputNote1, outputNote2, wasmPath, zkeyPath)const tx = await buildTransferTransaction({proof, nullifierHash,outputCommitment1, outputCommitment2,mint, payer})// On-chain: verify proof → nullify input → insert 2 new leaves
Key Primitives
// Poseidon hashing (BN254 scalar field)import { poseidonHash, poseidonHash2 } from '@zera-labs/sdk/crypto'const hash = poseidonHash([a, b, c]) // arbitrary-lengthconst node = poseidonHash2(left, right) // Merkle node// PDA derivationimport { derivePoolConfig, deriveVault, deriveNullifier }from '@zera-labs/sdk/pda'const poolConfig = derivePoolConfig(mint, programId)const vault = deriveVault(mint, programId)const nullifierPDA = deriveNullifier(nullifierHash, programId)// Note storageimport { MemoryNoteStore, FileNoteStore } from '@zera-labs/sdk'const store = new FileNoteStore('./notes.enc')await store.save(note, leafIndex)// Tree state sync (on-chain → local)import { TreeStateClient } from '@zera-labs/sdk'const client = new TreeStateClient({ rpcUrl, cacheEndpoint })const leaves = await client.fetchAllLeaves()
On-Chain Programs
Two Solana programs are deployed:
// Shielded Pool (full Groth16 ZK)// Deposit, withdraw, relayed withdraw, shielded transferProgram ID: B83jSQx1CT1hPRBupinaJaEkCjrfeo6Ktncu31q3ZNeX// Private Cash (lightweight Keccak-256 vouchers)// Commitment-based voucher system for simpler use casesProgram ID: ESQxpH9XkBQT6EVeWwAzTZD1e9NrLMrY8RPQ9SidYsgF
Source & Contribution
The SDK is open source. Follow progress and contribute:
https://github.com/zera-labs
