Token
Smart contract token utilities and implementations
This set of interfaces, contracts, and utilities are all related to the evolving Confidential Token Standard. The standard utilizes the Zama fhEVM co-processor for manipulating FHE values. All amounts are stored on-chain as ciphertext handles (or pointers) to values stored on the co-processor.
ConfidentialFungibleToken
: Implementation ofIConfidentialFungibleToken
.ConfidentialFungibleTokenERC20Wrapper
: Extension ofConfidentialFungibleToken
which wraps anERC20
into a confidential token. The wrapper allows for free conversion in both directions at a fixed rate.ConfidentialFungibleTokenUtils
: A library that provides the on-transfer callback check used byConfidentialFungibleToken
.
Core
Extensions
ConfidentialFungibleTokenERC20Wrapper
Utilities
ConfidentialFungibleTokenUtils
import "@openzeppelin/confidential-contracts/token/ConfidentialFungibleToken.sol";
Reference implementation for IConfidentialFungibleToken
.
This contract implements a fungible token where balances and transfers are encrypted using the Zama fhEVM,
providing confidentiality to users. Token amounts are stored as encrypted, unsigned integers (euint64
)
that can only be decrypted by authorized parties.
Key features:
- All balances are encrypted
- Transfers happen without revealing amounts
- Support for operators (delegated transfer capabilities with time bounds)
- Transfer and call pattern
- Safe overflow/underflow handling for FHE operations
Functions
- constructor(name_, symbol_, tokenURI_)
- name()
- symbol()
- decimals()
- tokenURI()
- confidentialTotalSupply()
- confidentialBalanceOf(account)
- isOperator(holder, spender)
- setOperator(operator, until)
- confidentialTransfer(to, encryptedAmount, inputProof)
- confidentialTransfer(to, amount)
- confidentialTransferFrom(from, to, encryptedAmount, inputProof)
- confidentialTransferFrom(from, to, amount)
- confidentialTransferAndCall(to, encryptedAmount, inputProof, data)
- confidentialTransferAndCall(to, amount, data)
- confidentialTransferFromAndCall(from, to, encryptedAmount, inputProof, data)
- confidentialTransferFromAndCall(from, to, amount, data)
- discloseEncryptedAmount(encryptedAmount)
- finalizeDiscloseEncryptedAmount(requestId, amount, signatures)
- _setOperator(holder, operator, until)
- _mint(to, amount)
- _burn(from, amount)
- _transfer(from, to, amount)
- _transferAndCall(from, to, amount, data)
- _update(from, to, amount)
IConfidentialFungibleToken
Events
Errors
- ConfidentialFungibleTokenInvalidReceiver(receiver)
- ConfidentialFungibleTokenInvalidSender(sender)
- ConfidentialFungibleTokenUnauthorizedSpender(holder, spender)
- ConfidentialFungibleTokenZeroBalance(holder)
- ConfidentialFungibleTokenUnauthorizedUseOfEncryptedAmount(amount, user)
- ConfidentialFungibleTokenUnauthorizedCaller(caller)
- ConfidentialFungibleTokenInvalidGatewayRequest(requestId)
IConfidentialFungibleToken
constructor(string name_, string symbol_, string tokenURI_)
internal
#name() → string
public
#Returns the name of the token.
symbol() → string
public
#Returns the symbol of the token.
decimals() → uint8
public
#Returns the number of decimals of the token. Recommended to be 6.
tokenURI() → string
public
#Returns the token URI.
confidentialTotalSupply() → euint64
public
#Returns the confidential total supply of the token.
confidentialBalanceOf(address account) → euint64
public
#Returns the confidential balance of the account account
.
isOperator(address holder, address spender) → bool
public
#Returns true if spender
is currently an operator for holder
.
setOperator(address operator, uint48 until)
public
#Sets operator
as an operator for holder
until the timestamp until
.
An operator may transfer any amount of tokens on behalf of a holder while approved.
confidentialTransfer(address to, externalEuint64 encryptedAmount, bytes inputProof) → euint64
public
#Transfers the encrypted amount encryptedAmount
to to
with the given input proof inputProof
.
Returns the encrypted amount that was actually transferred.
confidentialTransfer(address to, euint64 amount) → euint64
public
#Similar to interfaces#IConfidentialFungibleToken-confidentialTransfer-address-externalEuint64-bytes- but without an input proof. The caller
must already be allowed by ACL for the given amount
.
confidentialTransferFrom(address from, address to, externalEuint64 encryptedAmount, bytes inputProof) → euint64 transferred
public
#Transfers the encrypted amount encryptedAmount
from from
to to
with the given input proof
inputProof
. msg.sender
must be either from
or an operator for from
.
Returns the encrypted amount that was actually transferred.
confidentialTransferFrom(address from, address to, euint64 amount) → euint64 transferred
public
#Similar to interfaces#IConfidentialFungibleToken-confidentialTransferFrom-address-address-externalEuint64-bytes- but without an input proof.
The caller must be already allowed by ACL for the given amount
.
confidentialTransferAndCall(address to, externalEuint64 encryptedAmount, bytes inputProof, bytes data) → euint64 transferred
public
#Similar to interfaces#IConfidentialFungibleToken-confidentialTransfer-address-externalEuint64-bytes- but with a callback to to
after
the transfer.
The callback is made to the IConfidentialFungibleTokenReceiver.onConfidentialTransferReceived
function on the
to address with the actual transferred amount (may differ from the given encryptedAmount
) and the given
data data
.
confidentialTransferAndCall(address to, euint64 amount, bytes data) → euint64 transferred
public
#Similar to interfaces#IConfidentialFungibleToken-confidentialTransfer-address-euint64- but with a callback to to
after the transfer.
confidentialTransferFromAndCall(address from, address to, externalEuint64 encryptedAmount, bytes inputProof, bytes data) → euint64 transferred
public
#Similar to interfaces#IConfidentialFungibleToken-confidentialTransferFrom-address-address-externalEuint64-bytes- but with a callback to to
after the transfer.
confidentialTransferFromAndCall(address from, address to, euint64 amount, bytes data) → euint64 transferred
public
#Similar to interfaces#IConfidentialFungibleToken-confidentialTransferFrom-address-address-euint64- but with a callback to to
after the transfer.
discloseEncryptedAmount(euint64 encryptedAmount)
public
#Discloses an encrypted amount encryptedAmount
publicly via an IConfidentialFungibleToken.AmountDisclosed
event. The caller and this contract must be authorized to use the encrypted amount on the ACL.
This is an asynchronous operation where the actual decryption happens off-chain and
ConfidentialFungibleToken.finalizeDiscloseEncryptedAmount
is called with the result.
finalizeDiscloseEncryptedAmount(uint256 requestId, uint64 amount, bytes[] signatures)
public
#Finalizes a disclose encrypted amount request.
_setOperator(address holder, address operator, uint48 until)
internal
#_mint(address to, euint64 amount) → euint64 transferred
internal
#_burn(address from, euint64 amount) → euint64 transferred
internal
#_transfer(address from, address to, euint64 amount) → euint64 transferred
internal
#_transferAndCall(address from, address to, euint64 amount, bytes data) → euint64 transferred
internal
#_update(address from, address to, euint64 amount) → euint64 transferred
internal
#ConfidentialFungibleTokenInvalidReceiver(address receiver)
error
#The given receiver receiver
is invalid for transfers.
ConfidentialFungibleTokenInvalidSender(address sender)
error
#The given sender sender
is invalid for transfers.
ConfidentialFungibleTokenUnauthorizedSpender(address holder, address spender)
error
#The given holder holder
is not authorized to spend on behalf of spender
.
ConfidentialFungibleTokenZeroBalance(address holder)
error
#The holder holder
is trying to send tokens but has a balance of 0.
ConfidentialFungibleTokenUnauthorizedUseOfEncryptedAmount(euint64 amount, address user)
error
#The caller user
does not have access to the encrypted amount amount
.
Try using the equivalent transfer function with an input proof.
ConfidentialFungibleTokenUnauthorizedCaller(address caller)
error
#The given caller caller
is not authorized for the current operation.
ConfidentialFungibleTokenInvalidGatewayRequest(uint256 requestId)
error
#The given gateway request ID requestId
is invalid.
import "@openzeppelin/confidential-contracts/token/extensions/ConfidentialFungibleTokenERC20Wrapper.sol";
A wrapper contract built on top of ConfidentialFungibleToken
that allows wrapping an ERC20
token
into a confidential fungible token. The wrapper contract implements the IERC1363Receiver
interface
which allows users to transfer ERC1363
tokens directly to the wrapper with a callback to wrap the tokens.
Minting assumes the full amount of the underlying token transfer has been received, hence some non-standard tokens such as fee-on-transfer or other deflationary-type tokens are not supported by this wrapper.
Functions
- constructor(underlying_)
- decimals()
- rate()
- underlying()
- onTransferReceived(, from, amount, data)
- wrap(to, amount)
- unwrap(from, to, amount)
- unwrap(from, to, encryptedAmount, inputProof)
- finalizeUnwrap(requestID, amount, signatures)
- _unwrap(from, to, amount)
- _fallbackUnderlyingDecimals()
- _maxDecimals()
IERC1363Receiver
ConfidentialFungibleToken
- name()
- symbol()
- tokenURI()
- confidentialTotalSupply()
- confidentialBalanceOf(account)
- isOperator(holder, spender)
- setOperator(operator, until)
- confidentialTransfer(to, encryptedAmount, inputProof)
- confidentialTransfer(to, amount)
- confidentialTransferFrom(from, to, encryptedAmount, inputProof)
- confidentialTransferFrom(from, to, amount)
- confidentialTransferAndCall(to, encryptedAmount, inputProof, data)
- confidentialTransferAndCall(to, amount, data)
- confidentialTransferFromAndCall(from, to, encryptedAmount, inputProof, data)
- confidentialTransferFromAndCall(from, to, amount, data)
- discloseEncryptedAmount(encryptedAmount)
- finalizeDiscloseEncryptedAmount(requestId, amount, signatures)
- _setOperator(holder, operator, until)
- _mint(to, amount)
- _burn(from, amount)
- _transfer(from, to, amount)
- _transferAndCall(from, to, amount, data)
- _update(from, to, amount)
IConfidentialFungibleToken
Events
Errors
IERC1363Receiver
ConfidentialFungibleToken
- ConfidentialFungibleTokenInvalidReceiver(receiver)
- ConfidentialFungibleTokenInvalidSender(sender)
- ConfidentialFungibleTokenUnauthorizedSpender(holder, spender)
- ConfidentialFungibleTokenZeroBalance(holder)
- ConfidentialFungibleTokenUnauthorizedUseOfEncryptedAmount(amount, user)
- ConfidentialFungibleTokenUnauthorizedCaller(caller)
- ConfidentialFungibleTokenInvalidGatewayRequest(requestId)
IConfidentialFungibleToken
constructor(contract IERC20 underlying_)
internal
#decimals() → uint8
public
#Returns the number of decimals of the token. Recommended to be 6.
rate() → uint256
public
#Returns the rate at which the underlying token is converted to the wrapped token.
For example, if the rate
is 1000, then 1000 units of the underlying token equal 1 unit of the wrapped token.
underlying() → contract IERC20
public
#Returns the address of the underlying ERC-20 token that is being wrapped.
onTransferReceived(address, address from, uint256 amount, bytes data) → bytes4
public
#ERC1363
callback function which wraps tokens to the address specified in data
or
the address from
(if no address is specified in data
). This function refunds any excess tokens
sent beyond the nearest multiple of ConfidentialFungibleTokenERC20Wrapper.rate
. See ConfidentialFungibleTokenERC20Wrapper.wrap
from more details on wrapping tokens.
wrap(address to, uint256 amount)
public
#Wraps amount amount
of the underlying token into a confidential token and sends it to
to
. Tokens are exchanged at a fixed rate specified by ConfidentialFungibleTokenERC20Wrapper.rate
such that amount / rate()
confidential
tokens are sent. Amount transferred in is rounded down to the nearest multiple of ConfidentialFungibleTokenERC20Wrapper.rate
.
unwrap(address from, address to, euint64 amount)
public
#Unwraps tokens from from
and sends the underlying tokens to to
. The caller must be from
or be an approved operator for from
. amount * rate()
underlying tokens are sent to to
.
This is an asynchronous function and waits for decryption to be completed off-chain before disbursing tokens.
The caller must already be approved by ACL for the given amount
.
unwrap(address from, address to, externalEuint64 encryptedAmount, bytes inputProof)
public
#Variant of ConfidentialFungibleTokenERC20Wrapper.unwrap
that passes an inputProof
which approves the caller for the encryptedAmount
in the ACL.
finalizeUnwrap(uint256 requestID, uint64 amount, bytes[] signatures)
public
#Fills an unwrap request for a given request id related to a decrypted unwrap amount.
_unwrap(address from, address to, euint64 amount)
internal
#_fallbackUnderlyingDecimals() → uint8
internal
#Returns the default number of decimals of the underlying ERC-20 token that is being wrapped.
Used as a default fallback when ConfidentialFungibleTokenERC20Wrapper._tryGetAssetDecimals
fails to fetch decimals of the underlying
ERC-20 token.
_maxDecimals() → uint8
internal
#Returns the maximum number that will be used for IConfidentialFungibleToken.decimals
by the wrapper.
import "@openzeppelin/confidential-contracts/token/extensions/ConfidentialFungibleTokenVotes.sol";
Extension of ConfidentialFungibleToken
supporting confidential votes tracking and delegation.
The amount of confidential voting units an account has is equal to the confidential token balance of that account. Voing power is taken into account when an account delegates votes to itself or to another account.
Functions
VotesConfidential
- clock()
- CLOCK_MODE()
- getVotes(account)
- getPastVotes(account, timepoint)
- getPastTotalSupply(timepoint)
- delegates(account)
- delegate(delegatee)
- delegateBySig(delegatee, nonce, expiry, v, r, s)
- _delegate(account, delegatee)
- _transferVotingUnits(from, to, amount)
- _moveDelegateVotes(from, to, amount)
- _validateTimepoint(timepoint)
HandleAccessManager
IERC6372
EIP712
IERC5267
Nonces
ConfidentialFungibleToken
- name()
- symbol()
- decimals()
- tokenURI()
- confidentialBalanceOf(account)
- isOperator(holder, spender)
- setOperator(operator, until)
- confidentialTransfer(to, encryptedAmount, inputProof)
- confidentialTransfer(to, amount)
- confidentialTransferFrom(from, to, encryptedAmount, inputProof)
- confidentialTransferFrom(from, to, amount)
- confidentialTransferAndCall(to, encryptedAmount, inputProof, data)
- confidentialTransferAndCall(to, amount, data)
- confidentialTransferFromAndCall(from, to, encryptedAmount, inputProof, data)
- confidentialTransferFromAndCall(from, to, amount, data)
- discloseEncryptedAmount(encryptedAmount)
- finalizeDiscloseEncryptedAmount(requestId, amount, signatures)
- _setOperator(holder, operator, until)
- _mint(to, amount)
- _burn(from, amount)
- _transfer(from, to, amount)
- _transferAndCall(from, to, amount, data)
IConfidentialFungibleToken
Events
Errors
VotesConfidential
HandleAccessManager
IERC6372
EIP712
IERC5267
Nonces
ConfidentialFungibleToken
- ConfidentialFungibleTokenInvalidReceiver(receiver)
- ConfidentialFungibleTokenInvalidSender(sender)
- ConfidentialFungibleTokenUnauthorizedSpender(holder, spender)
- ConfidentialFungibleTokenZeroBalance(holder)
- ConfidentialFungibleTokenUnauthorizedUseOfEncryptedAmount(amount, user)
- ConfidentialFungibleTokenUnauthorizedCaller(caller)
- ConfidentialFungibleTokenInvalidGatewayRequest(requestId)
IConfidentialFungibleToken
confidentialTotalSupply() → euint64
public
#Returns the confidential total supply of the token.
_update(address from, address to, euint64 amount) → euint64 transferred
internal
#_getVotingUnits(address account) → euint64
internal
#import "@openzeppelin/confidential-contracts/token/utils/ConfidentialFungibleTokenUtils.sol";
Library that provides common ConfidentialFungibleToken
utility functions.
checkOnTransferReceived(address operator, address from, address to, euint64 amount, bytes data) → ebool
internal
#Performs a transfer callback to the recipient of the transfer to
. Should be invoked
after all transfers "withCallback" on a ConfidentialFungibleToken
.
The transfer callback is not invoked on the recipient if the recipient has no code (i.e. is an EOA). If the
recipient has non-zero code, it must implement
IConfidentialFungibleTokenReceiver.onConfidentialTransferReceived
and return an ebool
indicating
whether the transfer was accepted or not. If the ebool
is false
, the transfer will be reversed.