ERC-8086: Privacy Token

Discussion topic for ERC-8086:


Reference Implementation Update

A reference implementation is now available:

GitHub Repository: GitHub - 0xRowan/erc-8086-reference

Live Testnet Deployment (Base Sepolia):

  • Factory: 0x8303A804fa17f40a4725D1b4d9aF9CB63244289c
  • PrivacyToken Implementation: 0xB329Dc91f458350a970Fe998e3322Efb08dDA7d1

Interactive Demo: https://testnative.zkprotocol.xyz/

All contracts are verified on Basescan. Anyone can deploy privacy tokens and test the implementation.


Abstract

This EIP defines a minimal interface standard for native privacy tokens on Ethereum.

While developing privacy solutions for the Ethereum ecosystemβ€”including wrapper protocols (converting ERC-20 ↔ privacy tokens) and dual-mode tokens (combining public and private balances)β€”we identified a recurring need for standardized privacy primitives. Without a common interface, each implementation reinvents commitments, nullifiers, and note encryption, leading to ecosystem fragmentation.

This standard provides that common foundation. It enables:

  • Wrapper protocols: Implement this interface for their privacy layer
  • Dual-mode tokens protocols: Combine standards via contract DMT is ERC20, IZRC20

By unifying the native privacy token interface, we facilitate the development of wrapper and dual-mode protocols, accelerating Ethereum’s privacy ecosystem growth.

Motivation

Privacy Infrastructure Needs Standardization

While building privacy solutions for Ethereum, we identified recurring patterns:

Wrapper Protocols (ERC-20 β†’ Privacy β†’ ERC-20):

DAI (transparent) β†’ zDAI (private) β†’ DAI (transparent)
  • Each protocol implements custom privacy token logic
  • No interoperability between different privacy implementations
  • Duplicated effort, increased security risks

Dual-Mode Tokens (Public ↔ Private in one token):

Single Token: Public mode (ERC-20) ↔ Private mode (ZK-based)
  • Needs a privacy primitive as foundation
  • Current implementations reinvent the wheel

The Solution: Standardize the privacy primitive to enable:

  • Consistent wrapper protocol implementations
  • Reusable dual-mode token architectures
  • Faster ecosystem development

Design Philosophy

This standard is not a replacement for Wrapper Protocols or Dual-Mode Protocols. It is the privacy foundation they can build upon:

Ecosystem Stack:
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  Applications (DeFi, DAO, Gaming)   β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚  Dual-Mode Tokens (ERC-20 + Privacy)β”‚  ← Optional privacy
β”‚  Wrapper Protocols (ERC20β†’Privacy)  β”‚  ← Add privacy to existing
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚  Native Privacy Token Interface     β”‚  ← This standard (foundation)
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚  Ethereum L1 / L2s                  β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Specification

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.

Definitions

  • Native Privacy Asset: A token with privacy as an inherent property from genesis, not achieved through post-hoc mixing
  • Commitment: Cryptographic binding H(amount, publicKey, randomness) hiding value and recipient
  • Nullifier: Unique identifier H(commitment, secretKey) preventing double-spending
  • Note: Off-chain encrypted data (amount, publicKey, randomness) for recipient
  • Merkle Tree: Authenticated structure storing commitments for zero-knowledge membership proofs
  • Proof Type: Parameter routing different proof strategies (active/finalized/rollover transfers)
  • View Tag: Single-byte scanning optimization (OPTIONAL but RECOMMENDED)
  • Stealth Address: One-time recipient address from ephemeral keys (OPTIONAL)

Core Interface

// SPDX-License-Identifier: CC0-1.0
pragma solidity ^0.8.0;

/**
 * @title IZRC20
 * @notice Minimal interface for native privacy assets on Ethereum (ERC-8086)
 * @dev This standard defines the foundation for privacy-preserving tokens
 *      that can be used directly or as building blocks for wrapper protocols
 *      and dual-mode protocols implementations.
 */
interface IZRC20 {

    // ═══════════════════════════════════════════════════════════════════════
    // Events
    // ═══════════════════════════════════════════════════════════════════════

    /**
     * @notice Emitted when a commitment is added to the Merkle tree
     * @param subtreeIndex Subtree index (0 for single-tree implementations)
     * @param commitment The cryptographic commitment hash
     * @param leafIndex Position within subtree (or global index)
     * @param timestamp Block timestamp of insertion
     * @dev For single-tree: subtreeIndex SHOULD be 0, leafIndex is global position
     * @dev For dual-tree: subtreeIndex identifies which subtree, leafIndex is position within it
     */
    event CommitmentAppended(
        uint32 indexed subtreeIndex,
        bytes32 commitment,
        uint32 indexed leafIndex,
        uint256 timestamp
    );

    /**
     * @notice Emitted when a nullifier is spent (note consumed)
     * @param nullifier The unique nullifier hash
     * @dev Once spent, nullifier can never be reused (prevents double-spending)
     */
    event NullifierSpent(bytes32 indexed nullifier);

    /**
     * @notice Emitted when tokens are minted directly into privacy mode
     * @param minter Address that initiated the mint
     * @param commitment The commitment created for minted value
     * @param encryptedNote Encrypted note for recipient
     * @param subtreeIndex Subtree where commitment was added
     * @param leafIndex Position within subtree
     * @param timestamp Block timestamp of mint
     */
    event Minted(
        address indexed minter,
        bytes32 commitment,
        bytes encryptedNote,
        uint32 subtreeIndex,
        uint32 leafIndex,
        uint256 timestamp
    );

    /**
     * @notice Emitted on privacy transfers with public scanning data
     * @param newCommitments Output commitments created (typically 1-2)
     * @param encryptedNotes Encrypted notes for recipients
     * @param ephemeralPublicKey Ephemeral public key for ECDH key exchange (if used)
     * @param viewTag Scanning optimization byte (0 if not used)
     * @dev Provides data for recipients to detect and decrypt their notes
     */
    event Transaction(
        bytes32[2] newCommitments,
        bytes[] encryptedNotes,
        uint256[2] ephemeralPublicKey,
        uint256 viewTag
    );

    // ═══════════════════════════════════════════════════════════════════════
    // Metadata (ERC-20 compatible, OPTIONAL but RECOMMENDED)
    // ═══════════════════════════════════════════════════════════════════════

    /**
     * @notice Returns the token name
     * @return Token name string
     * @dev OPTIONAL but RECOMMENDED for UX and interoperability
     */
    function name() external view returns (string memory);

    /**
     * @notice Returns the token symbol
     * @return Token symbol string
     * @dev OPTIONAL but RECOMMENDED for UX and interoperability
     */
    function symbol() external view returns (string memory);

    /**
     * @notice Returns the number of decimals
     * @return Number of decimals (typically 18)
     * @dev OPTIONAL but RECOMMENDED for amount formatting
     */
    function decimals() external view returns (uint8);

    /**
     * @notice Returns the total supply across all privacy notes
     * @return Total token supply
     * @dev OPTIONAL - May be required for certain economic models (e.g., fixed cap)
     *      Individual balances remain private; only aggregate supply is visible
     */
    function totalSupply() external view returns (uint256);

    // ═══════════════════════════════════════════════════════════════════════
    // Core Functions
    // ═══════════════════════════════════════════════════════════════════════

    /**
     * @notice Mints new privacy tokens
     * @param proofType Type of proof to support multiple proof strategies.
     * @param proof Zero-knowledge proof of valid transfer
     * @param encryptedNote Encrypted note for minter's wallet
     * @dev Proof must demonstrate valid commitment creation and payment
     *      Implementations define minting rules
     */
    function mint(
        uint8 proofType,
        bytes calldata proof,
        bytes calldata encryptedNote
    ) external payable;

    /**
     * @notice Executes a privacy-preserving transfer
     * @param proofType Implementation-specific proof type identifier
     * @param proof Zero-knowledge proof of valid transfer
     * @param encryptedNotes Encrypted output notes (for recipient and/or change)
     * @dev Proof must demonstrate:
     *      1. Input commitments exist in Merkle tree
     *      2. Prover knows private keys
     *      3. Nullifiers not spent
     *      4. Value conservation: sum(inputs) = sum(outputs)
     */
    function transfer(
        uint8 proofType,
        bytes calldata proof,
        bytes[] calldata encryptedNotes
    ) external;

    // ═══════════════════════════════════════════════════════════════════════
    // Query Functions
    // ═══════════════════════════════════════════════════════════════════════

    /**
     * @notice Check if a nullifier has been spent
     * @param nullifier The nullifier to check
     * @return True if nullifier spent, false otherwise
     * @dev Implementations using `mapping(bytes32 => bool) public nullifiers`
     *      will auto-generate this function.
     */
    function nullifiers(bytes32 nullifier) external view returns (bool);

    /**
     * @notice Returns the current active subtree Merkle root
     * @return The root hash of the active subtree
     * @dev The active subtree stores recent commitments for faster proof computation.
     *      For dual-tree implementations, this is the root of the current working subtree.
     */
    function activeSubtreeRoot() external view returns (bytes32);
}
8 Likes

If this proposal is approved, it could help make native privacy capabilities more widely accessible across Ethereum. Compared to standards like ERC-20, privacy technologies typically present a higher technical barrier for most developers, especially when zero-knowledge tools are involved. By defining a minimal interface and providing reference implementations, projects would not need deep ZK expertise to adopt it. Developers could quickly integrate privacy modules and build privacy-related services directly, which may lead to meaningful growth for the ecosystem.

One additional advantage is that a minimal privacy interface also allows downstream tooling (wallets, SDKs, indexers, bridges) to standardize around a common pattern. This could help avoid fragmented implementations and enable a broader ecosystem of native-privacy support beyond the token itself.

1 Like

I went to your official website and carefully studied the work you’ve done β€” it’s amazing. This is also the direction I’ve always wanted to explore. If these standards are established, any project should be able to quickly integrate privacy services into Ethereum.

I think extracting the commonalities between the two approaches is quite challenging β€” one is ultimately a wrapper, while the other is a native token. I hope we can avoid a situation where developers of one approach become constrained by the other.

Thanks for raising this β€” I completely agree that wrapper-based privacy and native privacy tokens must not constrain each other’s design space. These approaches solve different problems, and their unique properties are what make them valuable.

The intent of this proposal is not to unify the systems themselves, but to extract only the minimal primitives both already need.

By standardizing just this lowest layer, we avoid interfering with protocol-level choices while still reducing duplicated work across the ecosystem. This separation allows each category to optimize where it matters most:

wrapper protocols can focus on bridging existing ERC-20 assets and cross-chain liquidity

native or dual-mode tokens can focus on UX, economics, and privacy-first asset design

Beyond those two categories, a common primitive layer offers additional ecosystem-wide benefits:

Wallets, explorers, and SDKs can support an entire class of privacy-enabled tokens with a single integration, instead of fragmented, incompatible implementations.

Auditors and regulated environments can rely on consistent public signals and optional viewing methods, instead of ad-hoc, bespoke disclosures.

So rather than forcing convergence on one implementation style, the proposal aims to prevent fragmentation at the primitive level, while keeping full flexibility for innovation at the protocol level.

3 Likes

Reference Implementation Update

A reference implementation is now available:

GitHub Repository: GitHub - 0xRowan/erc-8086-reference

Live Testnet Deployment (Base Sepolia):

  • Factory: 0x8303A804fa17f40a4725D1b4d9aF9CB63244289c
  • PrivacyToken Implementation: 0xB329Dc91f458350a970Fe998e3322Efb08dDA7d1

Interactive Demo: https://testnative.zkprotocol.xyz/

All contracts are verified on Basescan. Anyone can deploy privacy tokens and test the implementation.

From your proposal, I see two pathways enabled by native private tokens:
(1) dual-mode tokens (public ↔ private), and
(2) wrapping existing ERC-20s into an 8086-style native private token.

I’ve already tried the demo for the dual-mode token β€” it clearly shows how assets can move between transparent and private states.

I’m curious about the second pathway:
do you have a demo for wrapping an existing ERC-20 into an 8086 native private token?

This seems like an important mechanism for giving today’s tokens a seamless upgrade path into the native-privacy model.
If a reference implementation or prototype exists, I’d love to experiment with it.

Thanks for the great question β€” you’re exactly pointing at one of the most important consequences of ERC-8086.

:small_blue_diamond: 8086 is the privacy primitive

ERC-8086 defines a pure native private token.
It is intentionally minimal: private-only, no modes, no assumptions.

Because it’s a clean primitive, it becomes extremely easy for others to build higher-level protocols on top of it.

This is where the two pathways emerge:


(1) Dual-mode tokens (ERC-8085)

8085 is not built into 8086 β€” it is a protocol built on top of the 8086 primitive.

It adds:

  • a public representation, and
  • conversion rules between public ↔ private

But this is an optional extension layer, not part of 8086.


(2) Wrapping existing ERC-20s into 8086 native private tokens

This is another extension layer built on top of 8086.

We’ve already implemented this path:

  • shield: lock ERC-20 β†’ mint 8086 private token
  • unshield: burn 8086 β†’ withdraw ERC-20

Here, the 8086 asset remains purely private β€” no dual-mode behavior.

This gives today’s ERC-20s an immediate upgrade path into the native-privacy world.

You can try our deployed prototype on Base Sepolia:
:point_right: ZeroLayer - Privacy for Base


:jigsaw: Why this design matters

Because 8086 is minimal and purely private, anyone can build:

  • dual-mode tokens (8085-style)
  • wrapping protocols
  • cross-chain private bridges
  • private vaults
  • privacy-preserving DEX liquidity
  • private payment rails

All without modifying the original 8086 standard.

8086 is designed to be the base privacy building block β€” simple, composable, and future-proof.

Happy to share the code or walk through our implementation if you’re interested.

5 Likes

Really like the minimal IZRC20 design as a base privacy primitive for fungible assets.
One question on scope: is ERC-8086 intentionally focused only on ERC20 style fungible tokens, or do you see this interface (commitments/nullifiers/notes) eventually generalising to other standards e.g. NFTs (ERC-721/1155) or more specialized assets like DAT-style data tokens?

In other words, is the goal long-term privacy primitives for all asset types, or is this EIP deliberately optimised around the core ERC-20 / DeFi use case for now?

Thank you for recognizing the minimalist design of ERC-8086 (IZRC20). The questions you raised about the long-term scope of the protocol are exactly the core trade-offs we carefully considered when designing this standard.

The current ERC-8086 specification is intentionally optimized around use cases for fungible assets. This focus is driven by pragmatism and the goal of accelerating ecosystem adoption.

For new asset types like DAT, it would be even better if they could support fungibility.

2 Likes