The ZRC-20 Standard
To make native privacy assets a practical and developer-friendly reality, ZeroLayer introduces the ZRC-20 standard. ZRC-20 is to private tokens what the ubiquitous ERC-20 standard is to public tokens: a predictable and standardized interface for creating and interacting with fungible assets.
What is ZRC-20?
ZRC-20 is a token standard for native privacy assets on EVM-compatible blockchains. It defines a unified interface that allows anyone to create privacy-preserving tokens as easily as deploying an ERC-20 contract. Like ERC-20, ZRC-20 provides standardized functions for token creation, transfer, and management—but with complete transaction confidentiality built into the core design.
Key Characteristics
- Permissionless Creation: Anyone can create a ZRC-20 token through the PrivacyTokenFactory
- Privacy-First Design: All balances, amounts, and transaction details are encrypted by default
- Cryptographically Secured: Privacy guarantees are enforced by zero-knowledge proofs, not obscurity
- Developer-Friendly: Familiar interface similar to ERC-20, but with privacy primitives
- Composable: Can be integrated into DeFi protocols, DAOs, and other Web3 applications
A New Paradigm for Tokens
While a ZRC-20 token might feel like an ERC-20 from a user's perspective (it has a name, a symbol, and can be transferred), its underlying mechanics are fundamentally different and built entirely around privacy.
| Feature | ERC-20 (Public) | ZRC-20 (Private) |
|---|---|---|
| State Model | A public ledger of address -> balance |
An encrypted Merkle tree of "notes" (commitments) |
| Balance Check | balanceOf(address) returns a public number |
No public balance function. Balance is computed locally by the owner. |
| Transfer Logic | transfer(recipient, amount) |
transfer(proof, encryptedNotes) |
| Privacy | None. All data is public. | Full. Sender, receiver, and amount are shielded. |
| Security | Based on public key cryptography. | Based on zero-knowledge proofs and nullifiers. |
| State Storage | Simple balance mapping | Dual-layer Merkle tree (active + archived) |
| Double-Spend Prevention | Account balance check | Nullifier tracking system |
Core Components of ZRC-20
Instead of tracking balances in a simple mapping, the ZRC-20 standard operates on three core cryptographic primitives that work together to provide privacy:
1. Commitments (Notes)
A commitment, or "note," is the fundamental unit of ownership in the ZRC-20 model. It is a cryptographic hash that binds together:
- The token amount
- The owner's stealth public key (a Baby Jubjub curve point)
- A random salt value (for uniqueness)
Technical Construction:
commitment = Poseidon(stealthPublicKey_x, amount, salt)
Properties:
- Hiding: The commitment reveals nothing about its contents to external observers
- Binding: Cannot be opened to different values (prevents fraud)
- Unique: Each commitment is distinct due to the random salt
- Compact: Stored as a single 32-byte hash on-chain
On-Chain Storage: Commitments are stored as leaves in a dual-layer Merkle tree structure:
- Active Subtree: Current working tree (16 levels, 65,536 capacity)
- Finalized Root Tree: Archive of completed subtrees (20 levels, 1M+ capacity)
2. Nullifiers
To prevent a user from spending the same note twice (double-spending), the protocol uses nullifiers. A nullifier is a unique serial number cryptographically derived from the note's secrets and the owner's private key.
Technical Construction:
nullifier = Poseidon(commitment, stealthPrivateKey)
Properties:
- Uniqueness: Each note has exactly one nullifier
- Unlinkability: Cannot be linked back to the original commitment without the private key
- Permanent: Once recorded on-chain, a nullifier marks a note as spent forever
- Unforgeable: Only the legitimate owner can compute the correct nullifier (proven via ZK-SNARK)
Double-Spend Prevention: When a note is spent, its nullifier is revealed and recorded in the smart contract's nullifier set. Any subsequent attempt to spend the same note (with the same nullifier) will be rejected by the contract.
3. Zero-Knowledge Proofs
The zero-knowledge proof is the cryptographic engine that makes the entire system work securely and privately. When a user wants to perform any operation (mint, transfer, unshield), they generate a zk-SNARK proof that demonstrates the validity of the transaction without revealing any sensitive information.
What the Proof Demonstrates:
For a Transfer operation, the proof cryptographically proves:
- ✅ Input notes exist in the Merkle tree (membership proof)
- ✅ The user possesses the secret keys to spend those notes (ownership proof)
- ✅ Nullifiers are correctly computed from the private keys (prevents forgery)
- ✅ Total input value equals total output value (conservation of value)
- ✅ Output notes are properly constructed (integrity check)
- ✅ All cryptographic operations are correctly performed
Proof System:
- Type: Groth16 zk-SNARKs on the BN254 curve
- Circuits: Written in Circom language
- Verification: Performed by specialized on-chain verifier contracts
- Constraints: 20K-50K constraints depending on operation type
- Generation Time: 1-4 seconds (client-side)
- Verification Time: <100ms (on-chain)
The ZRC-20 Interface
Standard Functions
The ZRC-20 interface provides three core operations:
mint(proofType, proof, encryptedNote)
Purpose: Convert public assets (ERC-20 or ETH) into private ZRC-20 notes (also called "shielding")
Parameters:
proofType: 0 for regular mint, 1 for rollover mint (when subtree is full)proof: The ZK-SNARK proof with public signalsencryptedNote: Encrypted note data for the recipient's wallet to decrypt
Process:
- User transfers public tokens to the contract
- Submits ZK proof demonstrating ownership and commitment construction
- Contract verifies proof and adds commitment to Merkle tree
- Emits
CommitmentAppendedandMintedevents
transfer(proofType, proof, encryptedNotes)
Purpose: Execute a private transfer between ZeroLayer users
Parameters:
proofType: 0 (active), 1 (finalized), or 2 (rollover) depending on input sourceproof: ZK-SNARK proof demonstrating valid state transitionencryptedNotes: Array of encrypted notes for recipients
Process:
- User generates proof consuming input notes and creating output notes
- Contract verifies proof
- Marks input nullifiers as spent
- Adds output commitments to Merkle tree
- Emits
NullifierSpent,CommitmentAppended, andTransactionevents
unshield(proofType, proof, encryptedNotes, recipient)
Purpose: Convert private ZRC-20 notes back to public assets (withdrawal)
Parameters:
proofType: Transfer proof typeproof: ZK proof with special burn address outputencryptedNotes: Encrypted note datarecipient: Public Ethereum address to receive tokens
Process:
- User creates transfer proof where first output goes to special burn address
- Contract verifies proof and extracts withdrawal amount
- Burns privacy notes by marking nullifiers as spent
- Sends underlying ERC-20/ETH to recipient (minus protocol fee)
- Emits
Unshieldedevent
Metadata Functions
Like ERC-20, ZRC-20 provides standard metadata:
name(): Token name (e.g., "Privacy USDC")symbol(): Token symbol (e.g., "pUSDC")decimals(): Token decimals (inherited from underlying asset)totalSupply(): Total shielded supply (public)
State Query Functions
Additional functions for querying the protocol state:
activeSubtreeRoot(): Current active Merkle tree rootfinalizedRoot(): Archived subtrees rootnullifiers(bytes32): Check if a nullifier has been spent
How Notes Work
Note Structure
Each note contains the following information (encrypted):
{
amount: uint256, // Token quantity
stealthPublicKey: [uint256, uint256], // Owner's Baby Jubjub public key
salt: uint256 // Random value for uniqueness
}
Stealth Addresses
To protect recipient privacy, ZRC-20 uses stealth address technology:
- Sender generates an ephemeral key pair
- Sender performs ECDH key exchange with recipient's public scan key
- Sender derives a one-time stealth public key for this transaction
- Recipient can detect and decrypt notes using their private scan key
This ensures that even if someone knows your ZeroLayer address, they cannot see which transactions are sent to you by analyzing the blockchain.
View Tags
To make transaction scanning efficient, each transaction includes a view tag—a single-byte identifier derived from the shared secret. Recipients can:
- Quickly filter transactions by view tag (reduces scanning by 256x)
- Only attempt decryption on matching transactions
- Achieve ~40% reduction in client-side scanning costs
Note Encryption
Notes are encrypted using:
- ECDH: Key exchange using Baby Jubjub elliptic curve
- AES-GCM: Authenticated encryption with 128-bit security
- Semantic Security: Each note encryption is indistinguishable from random data
Only the intended recipient (with the correct private key) can decrypt and read the note.
The Dual-Layer Merkle Tree
ZRC-20 uses a sophisticated two-tier Merkle tree architecture for scalability:
Active Subtree (Layer 1)
- Purpose: Store recent notes for fast access
- Height: 16 levels
- Capacity: 65,536 leaves
- Usage: Used for low-cost "active transfers"
Root Tree (Layer 2)
- Purpose: Archive completed subtrees
- Height: 20 levels
- Capacity: 1,048,576 subtrees (68 billion notes total)
- Usage: Provides historical note access via "finalized transfers"
Rollover Mechanism
When the active subtree reaches capacity (65,536 notes):
- The current active root is archived into the root tree
- A new empty active subtree is created
- New notes are added to the fresh subtree
- Old notes remain accessible via finalized transfer proofs
This design enables:
- Horizontal Scalability: Millions of transactions supported
- Cost Optimization: Recent notes have lower verification costs
- Historical Access: All notes remain spendable indefinitely
Privacy Guarantees
What is Hidden
When using ZRC-20 tokens, the following information is completely private:
- ✅ Transaction amounts: How much was sent
- ✅ Sender identity: Who initiated the transaction
- ✅ Recipient identity: Who received the funds
- ✅ Account balances: How much any user holds
- ✅ Transaction graph: Links between senders and receivers
- ✅ Transaction history: Past spending patterns
What is Public
To enable verification and prevent fraud, some information remains public:
- 📊 Commitment hashes: Opaque identifiers for notes
- 📊 Nullifier hashes: Opaque identifiers for spent notes
- 📊 Merkle tree roots: Cryptographic state commitments
- 📊 Total supply: Aggregate shielded amount
- 📊 Transaction count: Number of operations performed
Important: All public data is cryptographically opaque—it cannot be used to infer amounts, identities, or relationships.
Security Properties
Cryptographic Foundations
ZRC-20's security relies on:
- Elliptic Curve Security: Baby Jubjub curve based on Twisted Edwards curves
- Hash Function Security: Poseidon hash optimized for zero-knowledge proofs
- Proof System Security: Groth16 zk-SNARKs with 128-bit security level
- Encryption Security: AES-GCM with semantic security guarantees
Protocol Guarantees
The ZRC-20 standard provides:
- Double-Spending Prevention: Enforced by on-chain nullifier tracking
- Value Conservation: Proven cryptographically (no inflation possible)
- Ownership Proof: Only true owners can generate valid proofs
- Replay Protection: Each proof is bound to specific inputs
- State Integrity: Merkle trees provide tamper-evident history
Trust Model
- Non-Custodial: Users always control their funds via private keys
- Trustless Verification: Smart contracts verify proofs mathematically
- Transparent Logic: All code is open-source and auditable
- Decentralized: No central authority or admin keys
Creating a ZRC-20 Token
Using the Factory
Anyone can create a new ZRC-20 token through the PrivacyTokenFactory:
// For ERC-20 tokens
factory.createToken(underlyingTokenAddress)
// For native ETH
factory.createToken(address(0))
The factory automatically:
- Reads metadata from the underlying token (name, symbol, decimals)
- Deploys a new ZRC-20 privacy token contract
- Configures all verifier contracts
- Initializes the Merkle tree structure
- Sets up the burn address for unshielding
Creation Fee: Small fee (e.g., 0.0005 ETH) to prevent spam deployments
Token Naming Convention
Created tokens follow a consistent naming pattern:
- Underlying: "USD Coin" (USDC)
- ZRC-20: "Privacy USD Coin" (pUSDC)
This makes it easy for users to identify privacy-wrapped versions of familiar tokens.
Comparison with Other Privacy Solutions
vs. Mixers (Tornado Cash)
| Feature | Mixers | ZRC-20 |
|---|---|---|
| Privacy Source | Anonymity set size | Mathematical proofs |
| Asset Type | Obscures public tokens | Creates new private assets |
| User Flow | Deposit → Wait → Withdraw | Shield → Transfer freely → Unshield |
| Composability | Limited | High (can be used in DeFi) |
| Privacy Guarantee | Statistical | Cryptographic |
vs. Confidential Transactions
| Feature | Confidential Transactions | ZRC-20 |
|---|---|---|
| Amounts Hidden | ✅ Yes | ✅ Yes |
| Senders Hidden | ❌ No | ✅ Yes |
| Recipients Hidden | ❌ No | ✅ Yes (stealth addresses) |
| Implementation | Requires chain modifications | Works on any EVM chain |
vs. Private Blockchains
| Feature | Private Chains | ZRC-20 |
|---|---|---|
| Privacy | Full | Full |
| Decentralization | ❌ Permissioned | ✅ Permissionless |
| Composability | ❌ Isolated | ✅ Interoperable with public DeFi |
| Trust Model | Federation | Smart contracts |
Composability with DeFi
How ZRC-20 Works with Existing DeFi
One of the most powerful features of ZRC-20 is its seamless integration with the existing DeFi ecosystem. Unlike privacy solutions that create isolated pools, ZRC-20 tokens can be converted back to standard ERC-20 tokens at any time, allowing users to leverage the full DeFi landscape while maintaining privacy when needed.
The Shield-Use-Unshield Cycle
ZRC-20 enables a flexible privacy workflow:
┌─────────────────────────────────────────────────────┐
│ Private World (ZRC-20) │
│ ┌─────────────────────────────────────────┐ │
│ │ User holds pUSDC │ │
│ │ • Balance: Private │ │
│ │ • Transfers: Anonymous │ │
│ │ • History: Encrypted │ │
│ └─────────────────────────────────────────┘ │
│ ↕ SHIELD/UNSHIELD │
└─────────────────────────────────────────────────────┘
↕
┌─────────────────────────────────────────────────────┐
│ Public DeFi World (ERC-20) │
│ ┌─────────────────────────────────────────┐ │
│ │ • Uniswap: Trade USDC for other tokens │ │
│ │ • Aave: Lend/Borrow USDC │ │
│ │ • Compound: Earn interest on USDC │ │
│ │ • Curve: Provide liquidity │ │
│ └─────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────┘
Practical Integration Examples
Example 1: Private Trading on Uniswap
Scenario: Alice wants to trade USDC for ETH without revealing her portfolio size.
Process:
- Start: Alice holds 10,000 pUSDC (private)
- Unshield: Alice unshields 5,000 pUSDC → 5,000 USDC (public)
- Trade: Alice swaps 5,000 USDC for 2 ETH on Uniswap
- Shield: Alice shields 2 ETH → 2 pETH (private again)
- Result: Alice now has 5,000 pUSDC + 2 pETH, remaining balance (5,000 pUSDC) stayed private
Privacy Benefits:
- Her total holdings remain hidden
- Only the specific trading amount is temporarily public
- Transaction cannot be linked to her other private transfers
Example 2: Private Lending with Aave
Scenario: Bob wants to earn yield on his tokens while keeping his wealth private.
Process:
- Start: Bob holds 50,000 pUSDC (private)
- Unshield: Bob unshields 30,000 pUSDC → 30,000 USDC
- Deposit: Bob deposits 30,000 USDC into Aave
- Earn: Bob earns interest for 6 months → receives 31,500 USDC
- Withdraw: Bob withdraws 31,500 USDC from Aave
- Shield: Bob shields 31,500 USDC → 31,500 pUSDC
- Result: Bob now has 51,500 pUSDC total (20,000 never left privacy + 31,500 returned)
Privacy Benefits:
- His total wealth remains private
- Only the lending position is temporarily visible
- Yield is re-shielded immediately after withdrawal
Example 3: Private Liquidity Provision
Scenario: Carol wants to provide liquidity to a DEX without exposing her holdings.
Process:
- Start: Carol holds 100,000 pUSDC + 50 pETH (private)
- Unshield: Carol unshields portions → 50,000 USDC + 25 ETH
- Add Liquidity: Carol adds to Uniswap USDC/ETH pool
- Earn Fees: Carol earns trading fees over time
- Remove Liquidity: Carol withdraws her position + earned fees
- Shield: Carol shields all proceeds back to pUSDC + pETH
- Result: Carol's total holdings remain mostly private, only the LP position was public
Privacy Benefits:
- Main wealth stays private
- Only LP position is visible
- Fees can be re-shielded immediately
Composability Advantages
ZRC-20's design provides unique composability benefits:
✅ Best of Both Worlds
- Privacy: When holding funds in ZRC-20 form
- Functionality: When temporarily converting to ERC-20 for DeFi
✅ Time-Limited Exposure
- Funds only become public for the duration of DeFi interaction
- Immediately re-shield after completing operations
- Minimizes blockchain footprint
✅ Selective Transparency
- Choose which transactions to make public (DeFi interactions)
- Keep everything else private (holdings, transfers, savings)
- Granular control over privacy vs. functionality
✅ No Protocol Lock-In
- Not limited to ZeroLayer ecosystem
- Can use any ERC-20-compatible protocol
- Freedom to leverage entire Ethereum DeFi stack
✅ Future-Proof Design
- As more protocols add native ZRC-20 support, privacy improves
- No need to change user behavior or holdings
- Gradual ecosystem evolution towards privacy
Conclusion
The ZRC-20 standard represents a breakthrough in blockchain privacy technology. By combining zero-knowledge proofs, stealth addresses, and efficient cryptographic primitives, it delivers:
- True Privacy: Cryptographically guaranteed confidentiality
- Full Functionality: Complete token lifecycle (mint, transfer, burn)
- Ease of Use: Familiar interface for developers
- Scalability: Supports up to 68,719,476,736 notes (1,048,576 subtrees × 65,536 leaves per subtree)
- Composability: Works with existing DeFi infrastructure
The ZRC-20 standard makes privacy a fundamental right, not a luxury feature. It empowers developers to build the next generation of privacy-preserving financial applications while maintaining the openness and decentralization that make blockchain technology valuable.
With ZRC-20, anyone can create a fully private token as easily as deploying an ERC-20 contract—opening the door to a new wave of confidential, yet verifiable, on-chain economies.