Overview
ERC20Confidential bridges its two balances with three operations. Shielding (public → confidential) is synchronous. Unshielding (confidential → public) is a two-step, asynchronous flow because FHE decryption happens off-chain.
shield()
Move public tokens into the confidential pool and mint yourself an equal encrypted balance — in a single transaction.
unshield() → claimUnshielded()
Burn confidential tokens, decrypt the burned amount off-chain, then claim the equivalent public tokens back out of the pool.
_rate() public base units equal one confidential unit.
Shielding (Public → 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 → Public)
Unshielding is asynchronous.unshield burns the confidential tokens and marks the burned ciphertext publicly decryptable; the network decrypts it off-chain; 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 off-chain
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 off-chain with the Client SDK — Decrypt to Transaction