Tokens

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 cypher-text handles (or pointers) to values stored on the co-processor.

Core

ConfidentialFungibleToken

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

Modifiers
  • onlyGateway()

Functions
  • constructor(name_, symbol_, tokenURI_)

  • name()

  • symbol()

  • decimals()

  • tokenURI()

  • totalSupply()

  • balanceOf(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)

  • _setOperator(holder, operator, until)

  • _mint(to, amount)

  • _burn(from, amount)

  • _transfer(from, to, amount)

  • _transferAndCall(from, to, amount, data)

  • _update(from, to, amount)

Events
IConfidentialFungibleToken
  • OperatorSet(holder, operator, until)

  • ConfidentialTransfer(from, to, amount)

  • EncryptedAmountDisclosed(encryptedAmount, amount)

Errors
  • ConfidentialFungibleTokenInvalidReceiver(receiver)

  • ConfidentialFungibleTokenInvalidSender(sender)

  • ConfidentialFungibleTokenUnauthorizedSpender(holder, spender)

  • ConfidentialFungibleTokenZeroBalance(holder)

  • ConfidentialFungibleTokenUnauthorizedUseOfEncryptedAmount(amount, user)

  • ConfidentialFungibleTokenUnauthorizedCaller(caller)

  • ConfidentialFungibleTokenInvalidGatewayRequest(requestId)

onlyGateway() modifier

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 9.

tokenURI() → string public

Returns the token URI.

totalSupply() → euint64 public

Returns the encrypted total supply of the token.

balanceOf(address account) → euint64 public

Returns the encrypted 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, einput 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 confidentialTransfer but without an input proof. The caller must already be allowed by ACL for the given amount.

confidentialTransferFrom(address from, address to, einput 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 confidentialTransferFrom but without an input proof. The caller must be already allowed by ACL for the given amount.

confidentialTransferAndCall(address to, einput encryptedAmount, bytes inputProof, bytes data) → euint64 transferred public

Similar to confidentialTransfer 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 confidentialTransfer but with a callback to to after the transfer.

confidentialTransferFromAndCall(address from, address to, einput encryptedAmount, bytes inputProof, bytes data) → euint64 transferred public

Similar to confidentialTransferFrom but with a callback to to after the transfer.

confidentialTransferFromAndCall(address from, address to, euint64 amount, bytes data) → euint64 transferred public

Similar to confidentialTransferFrom but with a callback to to after the transfer.

discloseEncryptedAmount(euint64 encryptedAmount) public

Discloses an encrypted amount encryptedAmount publicly via an {EncryptedAmountDisclosed} 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 finalizeDiscloseEncryptedAmount is called with the result.

finalizeDiscloseEncryptedAmount(uint256 requestId, uint64 amount) public

May only be called by the gateway contract. 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.

Extensions

ConfidentialFungibleTokenERC20Wrapper

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.

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)

  • _unwrap(from, to, amount)

ConfidentialFungibleToken
  • name()

  • symbol()

  • tokenURI()

  • totalSupply()

  • balanceOf(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)

  • _setOperator(holder, operator, until)

  • _mint(to, amount)

  • _burn(from, amount)

  • _transfer(from, to, amount)

  • _transferAndCall(from, to, amount, data)

  • _update(from, to, amount)

Events
IConfidentialFungibleToken
  • OperatorSet(holder, operator, until)

  • ConfidentialTransfer(from, to, amount)

  • EncryptedAmountDisclosed(encryptedAmount, amount)

Errors
ConfidentialFungibleToken
  • ConfidentialFungibleTokenInvalidReceiver(receiver)

  • ConfidentialFungibleTokenInvalidSender(sender)

  • ConfidentialFungibleTokenUnauthorizedSpender(holder, spender)

  • ConfidentialFungibleTokenZeroBalance(holder)

  • ConfidentialFungibleTokenUnauthorizedUseOfEncryptedAmount(amount, user)

  • ConfidentialFungibleTokenUnauthorizedCaller(caller)

  • ConfidentialFungibleTokenInvalidGatewayRequest(requestId)

constructor(contract IERC20 underlying_) internal

decimals() → uint8 public

Returns the number of decimals of the token. Recommended to be 9.

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 rate. See 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 rate such that amount / rate() confidential tokens are sent. Amount transferred in is rounded down to the nearest multiple of 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. NOTE: The caller must already be approved by ACL for the given amount.

unwrap(address from, address to, einput encryptedAmount, bytes inputProof) public

Variant of unwrap that passes an inputProof which approves the caller for the encryptedAmount in the ACL.

finalizeUnwrap(uint256 requestID, uint64 amount) public

Called by the fhEVM gateway with the decrypted amount amount for a request id requestId. Fills unwrap requests.

_unwrap(address from, address to, euint64 amount) internal

Utilities

ConfidentialFungibleTokenUtils

import "@openzeppelin/confidential-contracts/token/utils/ConfidentialFungibleTokenUtils.sol";

Library that provides common ConfidentialFungibleToken utility functions.

Functions
  • checkOnTransferReceived(operator, from, to, amount, data)

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.