Overview
ERC20Confidential bridges its two balances with three operations. Shielding (public to confidential) is synchronous. Unshielding (confidential to public) is a two-step, asynchronous flow because FHE decryption happens offchain.
shield()
Move public tokens into the confidential pool and mint yourself an equal encrypted balance, in a single transaction.
unshield() to claimUnshielded()
Burn confidential tokens, decrypt the burned amount offchain, then claim the equivalent public tokens back out of the pool.
_rate() public base units equal one confidential unit.
Shielding (Public to Confidential)
1
Round down to precision
amount is rounded down to the nearest whole multiple of _rate(). If that leaves zero, the call reverts with AmountTooSmallForConfidentialPrecision.2
Move public tokens to the pool
The rounded amount is transferred from the caller’s public balance into
CONFIDENTIAL_POOL via the standard ERC-20 _transfer.3
Mint the confidential balance
An equal (rate-scaled) encrypted amount is credited to the caller’s confidential balance through
_confidentialUpdate(address(0), msg.sender, ...).4
Emit TokensShielded
TokensShielded(msg.sender, amountToShield) records the public amount that was shielded.Shielding requires no approval, it moves the caller’s own public balance. The caller must already hold at least
amountToShield public tokens.Example
Unshielding (Confidential to Public)
Unshielding is asynchronous.unshield burns the confidential tokens and marks the burned ciphertext publicly decryptable; the network decrypts it offchain; claimUnshielded then verifies the decryption proof and releases the public tokens.
Step 1: Burn and create a claim
There are two overloads: one takes a plaintextuint64, the other an already-encrypted euint64 handle.
Step 2: Decrypt offchain
unshield returns the burned euint64 handle and calls FHE.allowPublic(burned), so anyone can decrypt it, no permit needed. Retrieve the claim’s ctHash and request decryption:
Step 3: Claim the public tokens
Submit the plaintext and proof. The contract verifies the proof (viaFHE.verifyDecryptResult), then transfers the rate-scaled public amount out of the pool.
The public payout goes to the claim’s stored
to (the address that called unshield), regardless of who submits the claimUnshielded transaction. The proof, not the sender, authorizes the release.Batch Claiming
Claim several pending unshields in one transaction:Claim Lifecycle
Claims are tracked by the inheritedFHERC20WrapperClaimHelper.
Querying claims
getUserClaims returns only pending claims, a claim is removed from the user’s set once it is successfully claimed. Use it to drive a “claimable balance” view in your UI.Claim errors
Full Round-Trip Example
Events
Related Topics
- Understand the backing pool and rate in The Dual-Balance Model
- Move tokens privately with Confidential Operations
- Compare with the FHERC20 wrapper unshield flow
- Decrypt handles offchain with the Client SDK. Decrypt to Transaction