sharedEuint64, a handle tagged with who shared it and with whom. This page shows the pattern on a vault, and it applies to FHERC20 and ERC20Confidential alike.
Why amounts cross as shared values
FHE operations check the permission of the contract performing them, not of whoever called it. Suppose a token accepted a bareeuint64 from any caller. That caller could pass a handle only the token may use, such as someone’s balance, and get back a value derived from it. A share records the sharer and the intended receiver, and lasts one transaction, so the receiver can check who handed the value over. Passing encrypted values between contracts covers the mechanism.
For a token call, that means two steps around every call:
- Share the amount with the token:
FHE.shareEuint64(amount, address(token)). - Receive the result from the token:
FHE.receiveEuint64FromCall(result, address(token)).
receiveEuint64FromCall, the address must be the contract you called in that same expression. Naming any other trusted address checks who created the share, not who handed it to you.
A vault that holds deposits
This vault takes deposits withconfidentialTransferFrom and pays out with confidentialTransfer. Each deposit is kept as an encrypted balance the depositor can decrypt:
ConfidentialVault.sol
- Credit what moved, not what was asked. A transfer that exceeds the sender’s balance moves zero instead of reverting. The vault credits
received, the amount the token returns, so a failed deposit credits nothing. - Clamp before you send.
withdrawusesFHE.selectto replace an over-deposit request with zero. The contract cannot revert on an encrypted comparison without revealing it. - Persist with
allowThis. A received handle carries access for this transaction only. Values you store must getFHE.allowThis, andFHE.allowfor the account that should decrypt them. See access control.
IERC7984 is the interface both token families implement, so the vault works with either. The shared overloads of confidentialTransfer and confidentialTransferFrom are selected by the sharedEuint64 argument type.
Call the vault from your app
confidentialTransferFrom runs with the vault as msg.sender, so the depositor must first make the vault an operator. Without it, the deposit reverts with FHERC20UnauthorizedSpender on FHERC20, or ERC20ConfidentialUnauthorizedSpender on ERC20Confidential.
Encrypt the amount with the vault as the consuming contract, because the vault calls FHE.asEuint64, not the token:
InvalidSigner. The same happens when one account encrypts and another sends the transaction: encrypt with the client of the account that calls the vault. See encrypting inputs.
What goes wrong
These errors come from the TaskManager. See common errors.
Receive tokens with a callback
To react when tokens arrive, rather than pulling them, implementIERC7984Receiver. The token calls it during confidentialTransferAndCall with the amount as a sharedEuint64, which you receive with FHE.receiveEuint64Param. See transfer callbacks.