This standard defines IZRC20, a minimal interface for privacy-preserving fungible tokens on Ethereum. It uses zero-knowledge proofs to enable confidential transfers where transaction amounts, sender, and recipient identities remain hidden. The core mechanism relies on cryptographic commitments stored in a Merkle tree, with nullifiers preventing double-spending.
This interface serves as a foundational building block for both wrapper protocols (adding privacy to existing ERC-20 tokens) and dual-mode tokens (single tokens supporting both transparent and private transfers).
While building privacy solutions for Ethereum, we identified recurring patterns:
Wrapper Protocols (ERC-20 → Privacy → ERC-20):
DAI (transparent) → zDAI (private) → DAI (transparent)
Dual-Mode Tokens (Public ↔ Private in one token):
Single Token: Public mode (ERC-20) ↔ Private mode (ZK-based)
The Solution: Standardize the privacy primitive to enable:
This standard is not a replacement for Wrapper Protocols or Dual-Mode Protocol. It is the privacy foundation they can build upon:
Ecosystem Stack: ┌─────────────────────────────────────┐ │ Applications (DeFi, DAO, Gaming) │ ├─────────────────────────────────────┤ │ Dual-Mode Tokens │ ← Optional privacy │ Wrapper Protocols │ ← Add privacy to existing ├─────────────────────────────────────┤ │ Native Privacy Token Interface │ ← This standard (foundation) ├─────────────────────────────────────┤ │ Ethereum L1 / L2s │ └─────────────────────────────────────┘
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in RFC 2119 and RFC 8174.
(amount, publicKey, randomness) for recipientSince this standard defines a minimal interface, different implementations may use different:
To enable client interoperability across different implementations, this standard defines an OPTIONAL but RECOMMENDED Privacy Configuration File mechanism, inspired by ERC-8004's Agent Registration File pattern.
Implementations SHOULD provide:
privacyConfigURI(): Returns the URI pointing to the configuration filesetPrivacyConfigURI(string): Sets/updates the configuration URI (typically owner-restricted)The configuration file MUST be a valid JSON document. The following shows the schema structure with field descriptions:
type (REQUIRED)Purpose: Schema identifier URL for version detection and format validation.
Format: URL string pointing to the specification version.
Example: https:// ... #privacy-config-v1
Client Usage: Clients SHOULD check this field first to ensure they can parse the configuration format. Unknown types SHOULD be rejected.
version (REQUIRED)Purpose: Semantic version of this specific configuration file.
Format: Semantic versioning string (MAJOR.MINOR.PATCH).
Example: "1.0.0"
Client Usage: Clients MAY cache configurations and use version for cache invalidation.
proofSystem (REQUIRED)Purpose: Specifies the zero-knowledge proof system parameters.
| Subfield | Required | Description |
|---|---|---|
protocol | YES | ZK protocol name. Common values:groth16, plonk, fflonk, stark |
curve | YES | Elliptic curve for the proof system. Common values:bn128 (alt_bn128), bls12-381 |
fieldSize | YES | The scalar field prime as a decimal string. This is the maximum value for any public signal. |
Example:
How to obtain fieldSize:
bn128: This is the BN254 scalar field prime (Fr), a 254-bit primebls12-381: Use the BLS12-381 scalar field primeClient Usage: Clients MUST validate that all public signals are less than fieldSize. The protocol determines which proof verification library to use.
treeConfig (REQUIRED)Purpose: Specifies the Merkle tree structure for storing commitments.
| Subfield | Required | Description |
|---|---|---|
type | YES | Tree architecture:single or dual-layer |
levels | CONDITIONAL | Total tree height (required for single type) |
subtreeLevels | CONDITIONAL | Active subtree height (required for dual-layer type) |
rootTreeLevels | CONDITIONAL | Root tree height (required for dual-layer type) |
Example (single tree):
Example (dual-layer tree):
Client Usage: Clients use this to build correct Merkle proofs. Tree capacity = 2^levels (or 2^subtreeLevels × 2^rootTreeLevels for dual-layer).
circuits (REQUIRED)Purpose: Maps operation types to their circuit artifacts and public signal schemas.
Each circuit entry contains:
| Subfield | Required | Description |
|---|---|---|
proofType | YES | The uint8 value to pass to the contract's mint() or transfer() function |
wasmUrl | YES | URL to the circuit's WASM file for proof generation |
zkeyUrl | YES | URL to the proving key file |
vkeyUrl | NO | URL to the verification key (optional, verification happens on-chain) |
publicSignals | YES | Number of public signals in the proof |
publicSignalsSchema | YES | Array describing each public signal's name, type, and position |
Example:
Client Usage:
publicSignalsSchema to correctly encode/decode proof public inputsproofType value to contract function callsnoteEncryption (REQUIRED)Purpose: Specifies the encryption algorithm and parameters for encrypted notes.
| Subfield | Required | Description |
|---|---|---|
algorithm | YES | Encryption algorithm chain (e.g.,ECDH+HKDF+AES-GCM) |
curve | YES | Elliptic curve for ECDH key exchange |
curveParams | YES | Curve-specific parameters |
curveParams.subgroupOrder | YES | The curve's subgroup order as decimal string |
curveParams.baseField | NO | The base field the curve is defined over |
domainSeparator | YES | Domain separator string for HKDF salt |
aadTag | YES | Additional Authenticated Data tag for AES-GCM |
noteFormat | YES | Format identifier for serialized encrypted notes |
noteSchema | NO | Schema describing the plaintext note structure |
Example:
Algorithm Format: <ECDH-variant>+<KDF>+<AEAD>
BJJ-ECDH (Baby Jubjub), secp256k1-ECDH, etc.HKDF-SHA256, HKDF-SHA512, etc.AES-256-GCM, ChaCha20-Poly1305, etc.How to obtain subgroupOrder:
Client Usage:
curve and curveParams for ECDH key exchangedomainSeparator as HKDF saltaadTag as AES-GCM additional authenticated datanoteFormat to identify encrypted note serialization formathashFunction (REQUIRED)Purpose: Specifies the hash function used for commitments, nullifiers, and Merkle tree.
| Subfield | Required | Description |
|---|---|---|
name | YES | Hash function name:Poseidon, MiMC, Pedersen, etc. |
parameters | NO | Hash-specific parameters (e.g., number of rounds, t-value) |
Example:
Client Usage: Clients use this hash function for computing commitments, nullifiers, and Merkle tree nodes locally.
keyDerivation (OPTIONAL)Purpose: Describes how users derive privacy keys from their wallet.
| Subfield | Required | Description |
|---|---|---|
method | NO | Key derivation method: EIP-712 Signature , BIP-32, etc. |
addressFormat | NO | Stealth address format:PV1, custom format identifier |
Example:
Client Usage: Guides wallet integration for key derivation. This is informational and clients MAY use different methods.
endpoints (OPTIONAL)Purpose: Service discovery for auxiliary infrastructure.
| Subfield | Required | Description |
|---|---|---|
indexer | NO | URL to transaction indexing service API |
relayer | NO | URL to gas relayer service API |
Example:
Client Usage: Clients MAY use these endpoints for enhanced functionality (faster sync, gas-free transfers).
The privacyConfigURI() function MAY return URIs using these schemes:
ipfs:// - IPFS content addressing (RECOMMENDED for immutability)https:// - HTTPS URLs (for dynamic updates)ar:// - Arweave permanent storagedata: - Base64 encoded inline data (for small configs)type field matches supported schema versionPurpose: Different proof types may be needed for:
Implementations MUST ensure:
balanceOf(address) queries (completely private)Option 1: Single Merkle Tree
subtreeIndex = 0 in eventsOption 2: Dual-Layer Tree (RECOMMENDED for scalability)
Implementations MUST document their architecture choice.
name(), symbol(), decimals() are OPTIONAL but RECOMMENDED for:
totalSupply() is OPTIONAL:
Ecosystem Benefits:
totalSupply()?Different use cases have different transparency requirements.
Use Cases Requiring totalSupply():
Design Decision: Make it OPTIONAL—let each implementation choose based on:
This flexibility enables the standard to serve both transparent-leaning (wrapper protocols) and privacy-maximalist (pure privacy tokens) use cases.
The proofType parameter is a key design decision for interface stability and implementation flexibility.
The Challenge:
Different implementations may need different proof strategies:
Design Decision: Single parameter routes to appropriate verifiers
This standard intentionally defines a minimal interface to maximize implementation flexibility. However, this creates a challenge:
The Problem:
Different implementations may use:
Without standardization:
The Solution: Privacy Configuration File (inspired by ERC-8004)
┌─────────────────────────────────────────────────────────────┐ │ On-chain (IZRC20 Contract) │ │ - Minimal interface │ │ - privacyConfigURI() → "ipfs://Qm..." │ └────────────────────────┬────────────────────────────────────┘ │ points to ▼ ┌─────────────────────────────────────────────────────────────┐ │ Off-chain (Privacy Configuration File) │ │ - Complete implementation details │ │ - Circuit artifacts (WASM, ZKEY) │ │ - Public signals schema │ │ - Encryption algorithm │ │ - Tree structure │ │ - All client-needed parameters │ └─────────────────────────────────────────────────────────────┘
Benefits:
Design Choices:
These fields appear in the Transaction event, which is emitted for all privacy transfers.
The Client Synchronization Challenge:
For privacy tokens to work, clients must:
Without standardization, each implementation would use incompatible formats, fragmenting the ecosystem.
Encrypted Notes:
View Tags (OPTIONAL but RECOMMENDED):
This standard is designed as a foundation layer, enabling higher-level protocols without prescribing their exact form.
Wrapper Protocols: Add privacy to existing ERC-20 tokens
Conceptual pattern:
Benefits of standardization:
Dual-Mode Tokens Protocols: Single token with both modes
Conceptual pattern:
Benefits of standardization:
This standard defines a minimal interface for native privacy assets. It is an independent interface implementation that does not depend on other protocols.
As described in the Motivation section, this standard serves as a foundational building block for higher-level protocols to rapidly implement privacy capabilities:
ERC-8086 Reference Implementation
Attack Vector: Reusing the same nullifier allows spending a commitment multiple times (double-spending).
Example:
Mitigation: Implementations MUST permanently track spent nullifiers and reject duplicates:
Each nullifier can only be used once. Nullifiers MUST never expire or be removed.
Attack: Submitting invalid proofs to create unauthorized commitments or spend notes without proper authorization.
Mitigation: Implementations MUST:
proofTypeAttack: Modifying or deleting commitments from the Merkle tree breaks proof validity and allows erasing transaction history.
Mitigation: Implementations MUST:
Any modification to historical commitments would invalidate all proofs referencing them.
Attack: Malicious circuits that don't enforce proper constraints allow minting tokens or stealing funds.
Critical Requirements: Zero-knowledge circuits MUST enforce:
Σ input amounts = Σ output amountsImplementations MUST use audited circuits and trusted setup ceremonies (or transparent setup schemes).
Copyright and related rights waived via CC0.