Skip to main content
shield moves tokens from a holder’s public balance into their confidential balance in one transaction. Going back takes two: unshield burns the confidential amount and opens a claim, and claimUnshielded pays out public tokens once the amount is decrypted. This page covers both directions and the claim lifecycle. Amounts in this page use the conversion rate: for an 18-decimal token, one confidential unit is 10^12 public base units.

Shield public tokens

shield(uint256 amount) takes amount in public base units from the caller’s public balance:
The token rounds amount down to a multiple of the rate. It moves the rounded amount into the pool and credits amount / rate confidential units to the caller. The remainder never leaves the public balance. If the rounded amount is zero, shield reverts with AmountTooSmallForConfidentialPrecision. shield always acts on the caller’s own tokens and credits the caller. There is no shield for another account and no approval involved. The shielded amount is public: it appears in the call data and in these events.

Unshield confidential tokens

unshield has two overloads. Both burn from the caller’s confidential balance and open a claim that pays the caller. The plaintext overload puts the amount in the call data. The encrypted overload keeps it hidden until the claim, but only a contract can call it, because the caller creates the argument with FHE.shareEuint64(amount, address(token)). See call a token from your contract. The burn follows the same rule as a transfer: if the amount exceeds the confidential balance, nothing is burned and the claim decrypts to zero. unshield emits ConfidentialTransfer(caller, address(0), handle) and TokensUnshielded(caller, handle), where handle is the burned amount. The token makes that handle publicly decryptable, since the claim reveals it anyway.

Claim the public tokens

Each unshield opens a claim with a unique ID. The ID is not the burned handle. Read it from the token: A claim has five fields: id, to (the account paid out), ctHash (the burned handle), decryptedAmount (set when the claim settles), and claimed. Decrypt ctHash with decryptForTx. No ACP is needed, because the handle is publicly decryptable. Then submit the result:
claimUnshielded verifies the signature against the burned handle, then moves decryptedValue * rate public tokens from the pool to claim.to. Anyone can submit the claim, because the payout always goes to claim.to. A relayer can pay the gas for a user. To settle several claims in one transaction, pass parallel arrays:
The batch reverts as a whole if any element is invalid. On success each claim emits UnshieldedTokensClaimed(to, id, handle, decryptedValue), along with the public Transfer from the pool.

Errors

A decryptedValue or signature that does not match the burned handle makes the decryption check revert, and the claim stays pending.