Skip to main content
Program ID: zQWSrznKgcK8aHA4ry7xbSCdP36FqgUHj766YM3pwre x0-zk-verifier provides on-chain verification of zero-knowledge proofs generated by the x0-zk-proofs WASM library. It validates Groth16 proofs over the Ristretto255 curve for confidential token operations.

Problem Statement

Token-2022’s ConfidentialTransfer extension encrypts balances using Twisted ElGamal, but the protocol still needs to enforce correctness constraints:
  1. Public key validity — The ElGamal public key is correctly derived from a secret scalar
  2. Zero balance — An encrypted balance is actually zero (required for account closure)
  3. Withdraw correctness — A withdrawal amount is valid against the encrypted balance
  4. Transfer correctness — Encrypted sender/receiver ciphertexts are consistent
Each of these requires a zero-knowledge proof that reveals nothing about the underlying amounts.

Proof Types

PubkeyValidityProof

Proves that an ElGamal public key PP is correctly derived: P=sHP = s \cdot H where ss is the secret scalar and HH is the Ristretto255 generator. Size: 64 bytes

ZeroBalanceProof

Proves that an ElGamal ciphertext encrypts the value 0 without revealing the randomness. Size: 96 bytes

WithdrawProof

Proves that withdrawing amount aa from encrypted balance EE is valid — i.e., the remaining balance is non-negative and bounded by 24812^{48} - 1. Size: 160 bytes

TransferProof

Proves that a confidential transfer correctly re-encrypts the amount under the recipient’s public key, and the sender’s remaining balance is non-negative. Size: 288 bytes

Instructions

verify_pubkey_validity

Verifies an ElGamal public key validity proof and stores the result in a ProofContext PDA.

verify_withdraw

Verifies a withdrawal proof and records the verified amount.

verify_zero_balance

Verifies that an encrypted balance is zero.

verify_transfer

Verifies a confidential transfer proof and records the amount and recipient.

State Account

ProofContext

Each verified proof creates a ProofContext PDA that serves as an on-chain receipt:
PDA Seeds: ["proof-context", owner, mint]

Proof Freshness

Proof contexts expire after 300 seconds (PROOF_CONTEXT_FRESHNESS_SECONDS). The is_fresh() method checks: current_timeverified_atΔproof=300s\text{current\_time} - \text{verified\_at} \leq \Delta_{\text{proof}} = 300\text{s} This prevents replay attacks — a proof verified 5 minutes ago cannot be used to authorize a new transfer.

Amount Bounds

All proof amounts are bounded by: 0a2481=281,474,976,710,6550 \leq a \leq 2^{48} - 1 = 281{,}474{,}976{,}710{,}655 This constraint comes from the Token-2022 ConfidentialTransfer extension’s use of 48-bit range proofs.

Verification Flow

Error Codes

Last modified on February 8, 2026