Building a ZK Solana program requires:
- Nullifiers to prevent double spending
- Proof verification
- A Merkle tree to store state
- An indexer to serve Merkle proofs
- Encrypted state
Nullifiers on Solana
A nullifier is a deterministically derived hash to ensure an action can only be performed once. The nullifier cannot be linked to the action or user. For example Zcash uses nullifiers to prevent double spending. To implement nullifiers we need a data structure that ensures every nullifier is only created once and never deleted. On Solana a straight forward way to implement nullifiers is to create a PDA account with the nullifier as seed.- PDA accounts cannot be closed and permanently lock 890,880 lamports (per nullifier rent-exemption).
- Compressed PDAs are derived similar to Solana PDAs and cost 15,000 lamports to create (no rent-exemption).
| Storage | Cost per nullifier |
|---|---|
| PDA | 890,880 lamports |
| Compressed PDA | 15,000 lamports |
Groth16 Proof Verification on Solana
Groth16’s small proof size and fast verification (~200k compute units) make it the practical choice for Solana.Merklelized State with Indexer Support
ZK applications on Solana can use existing state Merkle trees to store state in rent-free accounts.- This way you don’t need to maintain your own Merkle tree and indexer.
- RPCs that support ZK Compression (Helius, Triton) index state changes.
| Creation | Regular PDA Account | Compressed PDA |
|---|---|---|
| 100-byte PDA | ~1,600,000 lamports | 15,000 lamports |
Your circuit must include compressed accounts. Find guides to compressed accounts in the documentation and the full example with zk implementation here.