Discussion topic for ERC-8085:
Abstract
This EIP defines an interface for fungible tokens that operate in two modes: transparent mode (fully compatible with ERC-20) and privacy mode (using ERC-8086 privacy primitives). Token holders can convert balances between modes. The transparent mode uses account-based balances, while the privacy mode uses the standardized IZRC20 interface from ERC-8086. Total supply is maintained as the sum of both modes.
Motivation
The Privacy Dilemma for New Token Projects
When launching a new token, projects face a fundamental choice:
- ERC-20: Full DeFi composability but zero privacy
- Pure privacy protocols: Strong privacy but limited ecosystem integration
This creates real-world problems:
- DAOs need public treasury transparency but want anonymous governance voting
- Businesses require auditable accounting but need private payroll transactions
- Users want DeFi participation but need privacy for personal holdings
Existing solutions require trade-offs that limit adoption.
Current Approaches and Their Limitations
Wrapper-Based Privacy (e.g., Privacy Pools)
Mechanism: Wrap existing tokens (DAI, ETH) into a privacy pool contract.
DAI (public) β deposit β Privacy Pool β withdraw β DAI (public)
Strengths:
Works with any existing ERC-20 token
Permissionless deployment
No changes to underlying token required
Limitations for New Token Projects:
Creates two separate tokens (Token A vs. Wrapped Token B)
Splits liquidity between public and wrapped versions
Requires managing two separate contract addresses
Users must unwrap to access DeFi (additional friction)
Best suited for: Adding privacy to existing deployed tokens (DAI, USDC, etc.)
Our Approach: Integrated Dual-Mode for New Tokens
This standard provides a alternative option specifically designed for new token deployments that want privacy as a core feature from day one.
Target Use Case: Projects launching new tokens (governance tokens, protocol tokens, app tokens) that need both DeFi integration and optional privacy.
Mechanism:
Single Token Contract
β
Public Mode (ERC-20) ββ Privacy Mode (ZK-SNARK)
β β
DeFi/DEX Trading Private Holdings
Key Advantages:
-
Unified Token Economics
- No liquidity split between public/private versions
- One token address, one market price
- Simplified token distribution and airdrops
-
Seamless Mode Switching
- Convert to privacy mode for holdings:
toPrivacy() - Convert to public mode for DeFi:
toPublic() - Users choose privacy per transaction, not per token
- Convert to privacy mode for holdings:
-
Full ERC-20 Compatibility
- Works with existing wallets, DEXs, and DeFi protocols
- No special support needed for public mode operations
- Standard
totalSupply()accounting tracks both modes
-
Transparent Supply Tracking
totalSupply()includes both public and privacy mode balancestotalPrivacySupply()reveals aggregate privacy supply (no individual balances)- Prevents hidden inflation
- Regulatory visibility into aggregate metrics
-
Application-Layer Deployment
- Deploy today on any EVM chain (Ethereum, L2s, sidechains)
- No protocol changes or governance votes required
- No coordination with core developers needed
Real-World Use Cases
1. DAO Governance Token
Public Mode:
- Treasury management (transparent)
- Grant distributions (auditable)
- DEX trading (liquidity)
Privacy Mode:
- Anonymous voting (no vote buying)
- Private delegation (confidential strategy)
- Personal holdings (no public scrutiny)
2. Privacy-Aware Business Token
Public Mode:
- Investor reporting (compliance)
- Exchange listings (liquidity)
- Public fundraising (transparency)
Privacy Mode:
- Employee compensation (confidential)
- Supplier payments (competitive advantage)
- Strategic reserves (private holdings)
3. Protocol Token with Optional Privacy
Public Mode:
- Staking (DeFi integration)
- Liquidity provision (AMM pools)
- Trading (price discovery)
Privacy Mode:
- Long-term holdings (privacy)
- Over-the-counter transfers (confidential)
- Strategic positions (no front-running)
Design Philosophy
This standard embraces a core principle: βPrivacy is a mode, not a separate token.β
Rather than forcing users to choose between incompatible assets (Token A vs. Privacy Token B), we enable contextual privacy within a single fungible token. Users select the appropriate mode for each use case, maintaining capital efficiency and unified liquidity.
This approach acknowledges that privacy and composability serve different purposes, and most users need both at different timesβnot a forced choice between them.
Interface
/**
* @title IDualModeToken
* @notice Interface for dual-mode tokens (ERC-8085) combining ERC-20 and [ERC-8086](./eip-8086) (IZRC20)
* @dev Implementations MUST inherit both IERC20 and IZRC20
* Privacy events and core functions are inherited from IZRC20 (ERC-8086)
* This interface only defines mode conversion logic - the core value of ERC-8085
*
* Architecture:
* - Public Mode: Standard ERC-20 (transparent balances and transfers)
* - Privacy Mode: ERC-8086 IZRC20 (ZK-SNARK protected balances and transfers)
* - Mode Conversion: toPrivacy (public β private) and toPublic (private β public)
*/
interface IDualModeToken is IERC20, IZRC20 {
// βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
// Events (Mode Conversion Specific)
// βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
/// @notice Emitted when value is converted from transparent to privacy mode
/// @param account The address converting tokens
/// @param amount The amount converted
/// @param commitment The cryptographic commitment created
/// @param timestamp Block timestamp of conversion
event ConvertToPrivacy(
address indexed account,
uint256 amount,
bytes32 indexed commitment,
uint256 timestamp
);
/// @notice Emitted when value is converted from privacy to transparent mode
/// @param initiator The address initiating the conversion
/// @param recipient The address receiving transparent tokens
/// @param amount The amount converted
/// @param timestamp Block timestamp of conversion
event ConvertToPublic(
address indexed initiator,
address indexed recipient,
uint256 amount,
uint256 timestamp
);
// βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
// Mode Conversion Functions (Core of ERC-8085)
// βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
/**
* @notice Convert transparent balance to privacy mode
* @dev Burns ERC-20 tokens and creates privacy commitment via IZRC20
* @param amount Amount to convert (must match proof)
* @param proofType Type of proof to support multiple proof strategies.
* @param proof ZK-SNARK proof of valid commitment creation
* @param encryptedNote Encrypted note data for recipient wallet
*/
function toPrivacy(
uint256 amount,
uint8 proofType,
bytes calldata proof,
bytes calldata encryptedNote
) external;
/**
* @notice Convert privacy balance to transparent mode
* @dev Spends privacy notes and mints ERC-20 tokens to recipient
* @param recipient Address to receive public tokens
* @param proofType Type of proof to support multiple proof strategies.
* @param proof ZK-SNARK proof of note ownership and spending
* @param encryptedNotes Encrypted notes for change outputs (if any)
*/
function toPublic(
address recipient,
uint8 proofType,
bytes calldata proof,
bytes[] calldata encryptedNotes
) external;
// βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
// Supply Tracking
// βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
// Note: Privacy transfers use IZRC20.transfer(uint8, bytes, bytes[])
// which is inherited from IZRC20 (ERC-8086)
/**
* @notice Total supply across both modes (overrides IERC20 and IZRC20)
* @return Total supply = publicSupply + privacySupply
*/
function totalSupply() external view override(IERC20, IZRC20) returns (uint256);
/**
* @notice Get total supply in privacy mode
* @dev Tracked by increments/decrements during mode conversions
* @return Total privacy supply
*/
function totalPrivacySupply() external view returns (uint256);
/**
* @notice Check if a nullifier has been spent
* @dev Alias for IZRC20.nullifiers() with different naming convention
* @param nullifier The nullifier hash to check
* @return True if spent, false otherwise
*/
function isNullifierSpent(bytes32 nullifier) external view returns (bool);
}
Proof Type Parameter
The proofType parameter in toPrivacy, toPublic, and privacyTransfer functions allows implementations to support multiple proof strategies.
Purpose: Different proof types may be needed for:
- Different data structures (e.g., active vs. archived state in dual-tree implementations)
- Different optimization strategies (e.g., activeTree proofs vs. finalizedTree proofs)
ERC-20 Compatibility
Implementations MUST implement the ERC-20 interface. All ERC-20 functions operate exclusively on transparent mode balances:
balanceOf(account)MUST return the transparent mode balance only- Privacy mode balances are NOT included (they are hidden by design)
transfer(to, amount)MUST transfer transparent balance onlyapprove(spender, amount)MUST approve transparent balance spendingtransferFrom(from, to, amount)MUST transfer transparent balance with allowancetotalSupply()MUST return the sum of all public balances plustotalPrivacySupply()- This represents the total token supply across both modes
Implementations MUST emit standard ERC-20 Transfer events for transparent mode operations.
Supply Invariant
Implementations MUST maintain the following invariant at all times:
totalSupply() == sum(all balanceOf(account)) + totalPrivacySupply()
Where:
totalSupply(): Inherited from ERC-20, represents total token supply across both modessum(all balanceOf(account)): Sum of all transparent mode balancestotalPrivacySupply(): Aggregate privacy mode supply, tracked by:- Incrementing on
toPrivacy()(public β private conversion) - Decrementing on
toPublic()(private β public conversion) - NOT computed from Merkle tree (commitment values are encrypted)
- Incrementing on
Note: The public mode supply can be derived as totalSupply() - totalPrivacySupply() if needed, eliminating the need for a separate totalPublicSupply() function.