Registry contract
This document explains the Confidential Token Wrappers Registry, an onchain directory that maps ERC-20 tokens to their corresponding ERC-7984 confidential token wrappers. Use it to discover, validate, and integrate confidential wrappers within the FHEVM ecosystem.
Terminology
Token: An ERC-20 token.
Confidential Wrapper: An ERC-7984 confidential token wrapper. Also called "confidential token".
Underlying Token: The ERC-20 token that the confidential wrapper is associated with.
TokenWrapperPair: A pair of a token and its confidential wrapper.
Valid: A valid confidential wrapper has been verified by the registry owner and can be used to wrap and unwrap tokens from the underlying token.
Invalid: An invalid confidential wrapper has been revoked by the registry owner and should not be used to wrap and unwrap tokens from the underlying token.
Owner: The owner of the registry. In the FHEVM protocol, this is a DAO governance contract handled by Zama.
Mainnet address
The official registry address on Ethereum mainnet is 0xeb5015fF021DB115aCe010f23F55C2591059bBA0.
Quick Start
A token can only be associated with one confidential wrapper. A confidential wrapper can only be associated with one token.
Always check validity: A non-zero wrapper address may be revoked by the owner. Always verify the isValid flag associated with the (token, wrapper) pair before use.
Find the confidential wrapper of a token
If the token has been registered with a confidential wrapper:
isValidwill betrueconfidentialTokenwill be the address of the confidential wrapper.
If the token has never been registered with a confidential wrapper:
isValidwill befalseconfidentialTokenwill beaddress(0).
If the confidential wrapper has been revoked:
isValidwill befalseconfidentialTokenwill be the address of the (revoked) confidential wrapper.
Find the underlying token of a confidential wrapper
If the confidential wrapper has been registered with a token:
isValidwill betruetokenwill be the address of its underlying token.
If the confidential wrapper has never been registered with a token:
isValidwill befalsetokenwill beaddress(0).
If the confidential wrapper has been revoked:
isValidwill befalsetokenwill be the address of its underlying token.
Check if a confidential wrapper is valid
If the confidential wrapper has been revoked,
isValidwill befalse.
If the confidential wrapper has not been revoked (it is still valid),
isValidwill betrue.
Integration patterns
Token and confidential wrapper pairs are stored in the registry as TokenWrapperPair structs. Each struct contains:
tokenAddress: the address of the underlying token.confidentialTokenAddress: the address of the confidential wrapper.isValid:trueif the confidential wrapper is valid,falseif it has been revoked.
Get all valid confidential (token, wrapper) pairs
It returns all confidential wrappers (including revoked ones).
Get the total number of confidential (token, wrapper) pairs
Get the index of a token
tokenAddress must be a registered token. Otherwise, it will revert with TokenNotRegistered.
Get a valid confidential (token, wrapper) pair by index
It returns a single confidential (token, wrapper) pair (including revoked ones).
Get a slice of confidential (token, wrapper) pairs
It returns a slice of confidential (token, wrapper) pairs (including revoked ones). fromIndex is included and toIndex is excluded.
Data Structures
TokenWrapperPair
Events
ConfidentialTokenRegistered(tokenAddress, confidentialTokenAddress)
Emitted when a new pair is registered
ConfidentialTokenRevoked(tokenAddress, confidentialTokenAddress)
Emitted when a wrapper is revoked
Errors
TokenZeroAddress()
Attempted to register with zero token address
ConfidentialTokenZeroAddress()
Attempted to register/revoke with zero wrapper address
NotERC7984(confidentialTokenAddress)
Wrapper doesn't support ERC-7984 interface
ConfidentialTokenDoesNotSupportERC165(confidentialTokenAddress)
Wrapper doesn't implement ERC-165
ConfidentialTokenAlreadyAssociatedWithToken(tokenAddress, existingConfidentialTokenAddress)
Wrapper already registered to another token
TokenAlreadyAssociatedWithConfidentialToken(tokenAddress, existingConfidentialTokenAddress)
Token already has a registered wrapper
RevokedConfidentialToken(confidentialTokenAddress)
Attempting to revoke an already-revoked wrapper
NoTokenAssociatedWithConfidentialToken(confidentialTokenAddress)
Attempting to revoke unregistered wrapper
FromIndexGreaterOrEqualToIndex(fromIndex, toIndex)
Invalid slice range
TokenNotRegistered(tokenAddress)
Token has not been registered
Owner Administration
All administrative actions are restricted to the registry owner.
Register a confidential token
Validation performed:
Neither address can be zero
Confidential token must implement ERC-165 (
supportsInterfacefunction) and support the ERC-7984 interface (0x4958f2a4)Token must not already have an associated wrapper
Confidential token must not already be associated with another token
Revoke a confidential token
Important: Revocation does NOT delete the mapping—it only sets isValid = false. This preserves historical records and prevents re-registration of malicious addresses.
Revoking is currently permanent. When a wrapper is revoked:
isValidis set tofalseThe mapping entries remain in storage
The token cannot be registered with a new wrapper
Upgradeability
The contract uses UUPS (Universal Upgradeable Proxy Standard) with 2-step ownership transfer. Only the owner can upgrade the contract.
Last updated