Skip to main content
FHERC20 is a token whose balances exist only as encrypted values. It implements the confidential token interface from ERC-7984 and keeps a read-only ERC-20 surface so wallets and explorers still recognize it. Read this page to build an FHERC20 token and to understand what it reveals. If your token must also keep working as a normal ERC-20, use ERC20Confidential instead. To make an existing ERC-20 or ETH confidential, use a wrapper.

What is encrypted and what is public?

Each balance is an euint64 handle (a reference to an encrypted value). The total supply is an euint64 too. Transfer amounts are encrypted inputs or handles, never plaintext. What anyone can still see:
  • The sender and recipient of every transfer, from the ConfidentialTransfer event.
  • That an account has never held the token. A transfer or burn from such an account reverts with FHERC20ZeroBalance, because its balance handle was never created.
  • Transfer activity per account: the indicator values below move by one step per transfer received or sent.
  • Amounts you pass in plaintext, such as the uint64 argument of a mint function you write.

What do the ERC-20 functions return?

balanceOf and totalSupply return an indicator, not a balance. The indicator tells a wallet that something changed without saying how much. The values read as 7984.xxxx when decimals is 4 or more. A token with 6 decimals reports a first-time recipient as 7984000100 raw, which a wallet shows as 7984.0001. Every transfer, mint, and burn also emits the standard ERC-20 Transfer event with a fixed value of 7984.0001, so explorers index the activity. The real amount is in ConfidentialTransfer, as a handle. An account can call resetIndicatedBalance() to set its own indicator back to 0.

Who can decrypt a balance?

The token grants access to every new balance handle for two accounts: the token itself and the holder. Nobody else can decrypt a balance unless your contract grants access with FHE.allow. To show a balance in your app, read the handle and decrypt it with the SDK:
decryptForView signs with an Access Control Permission (ACP) for the connected account. See decrypt to view for setting one up. confidentialBalanceOf returns a handle, not an amount. A handle you read once goes stale after the next transfer, because every balance change creates a new handle.

How do you mint and burn?

FHERC20 exposes no public mint or burn. Your contract calls the internal functions and decides who may use them: Both return the encrypted amount that actually moved. Neither reverts on an encrypted condition:
  • A burn larger than the balance burns zero.
  • A mint that would overflow the 64-bit total supply mints zero.
This token mints and burns from encrypted inputs, so the amounts stay private:
RewardPoints.sol
The constructor takes the name, symbol, decimals, and a contract URI (ERC-7572). Pass an empty string if you have no URI. FHERC20 does not need the shared library, so it deploys like any other contract.
A mint function that takes a plaintext uint64 works too, but the amount is visible in the transaction’s calldata.

Which variants exist?

supportsInterface reports IFHERC20, IERC7984, IERC20, and IERC165.

Next steps