Skip to main content

Overview

Once value is shielded into the confidential layer, ERC20Confidential exposes the full ERC-7984 surface for moving it privately. This API is identical to FHERC20’s — confidential transfers, a time-based operator system, and safe transfers with receiver callbacks. This page covers each, with the specifics of the ERC20Confidential implementation.
The confidential-transfer API here mirrors FHERC20. If you already know FHERC20’s operators and transfer callbacks, the same mental model applies — only the contract name and errors differ.

Confidential Transfers

Every transfer function comes in two overloads: an InEuint64 overload for encrypted input coming from off-chain users, and an euint64 overload for already-encrypted, contract-to-contract values.

How a transfer is authorized

The euint64 overloads require the caller to already hold FHE access to the value; the InEuint64 overloads accept a fresh encrypted input and convert it in-place.
The internal path validates addresses and routes through the confidential update engine:
Zero-replacement: if the sender’s confidential balance is insufficient, the transfer moves encrypted zero rather than reverting (to avoid leaking balance information). Always use the returned transferred handle — not the requested amount — for any downstream logic.

Usage

The caller is granted transient access (FHE.allowTransient) to the transferred handle — access valid only for the current transaction. Both the sender and recipient receive persistent access to it inside _confidentialUpdate.

The Operator System

Like FHERC20, ERC20Confidential replaces ERC-20 allowances (which would leak amounts) with time-based operators. An operator can move any amount of a holder’s confidential balance until an expiration timestamp.

Self is always an operator

isOperator returns true when holder == spender, so a holder can always move their own tokens via confidentialTransferFrom without granting anything.

Inclusive expiry

Permission is valid while block.timestamp <= until. Set until to block.timestamp to revoke immediately, or type(uint48).max for effectively indefinite access.

Granting and using operators

confidentialTransferFrom checks operator status before moving tokens:
An operator has access to a holder’s entire confidential balance until expiry, not a specific amount. Grant only to trusted addresses and prefer short windows. Expiry is silent — a confidentialTransferFrom after until reverts with ERC20ConfidentialUnauthorizedSpender.
OperatorSet(holder, operator, until) is emitted on every grant or revocation.

Transfer Callbacks

The ...AndCall functions transfer confidential tokens and notify the recipient contract, which can accept or reject the transfer. All four overloads exist:

The accept-or-refund mechanism

ERC20Confidential moves the tokens first, asks the recipient, and refunds whatever the recipient rejects — all under FHE, without revealing amounts:
1

Transfer executes

sent tokens move from from to to via the confidential update engine.
2

Recipient is called

FHERC20Utils.checkOnTransferReceived invokes onConfidentialTransferReceived on the recipient (if it’s a contract) and returns an encrypted success flag.
3

Rejected amount is refunded

FHE.select(success, 0, sent) computes the refund: zero if accepted, the full amount if rejected. That refund moves back from to to from.
4

Net transfer reported

transferred = sent - refund — the actual net amount that stuck with the recipient.

The receiver interface

A recipient contract must implement IERC7984Receiver and return an encrypted bool:
  • Return FHE.asEbool(true) to accept the transfer.
  • Return FHE.asEbool(false) to reject it — the tokens are refunded to the sender under FHE.
Unlike an all-or-nothing revert, the accept/reject decision is evaluated under encryption and settled by refunding the rejected portion. See FHERC20 Transfer Callbacks for receiver patterns, reentrancy guidance, and data-passing examples — the receiver contract is written the same way.

Errors


Events

ConfidentialTransfer carries an encrypted euint64 handle, never a plaintext amount — you cannot index transfer amounts off-chain from events. The sidecar ERC20ConfidentialIndicator also emits a standard Transfer(from, to, 10110000001) so explorers register activity without exposing amounts.