Skip to main content
ConfidentialWrapper locks a standard ERC-20 and issues confidential wrapped tokens (cTokens) with encrypted balances. The wrapper handles decimal normalization — all cTokens use 6 decimals regardless of the underlying token’s decimals. Deployed instances on mainnet: New wrappers are created via WrapperFactory.

Key properties

PropertyValue
cToken decimals6 (always)
Unwrap timeout1 day (UNWRAP_TIMEOUT)
Underlying tokenImmutable, set at deploy

wrap

function wrap(uint256 amount) external
Deposit ERC-20 tokens and receive an equivalent encrypted cToken balance. You must approve the wrapper contract before calling this.
amount
uint256
required
Amount in the underlying token’s native decimals (e.g. 1e18 for 1 WETH).
The contract adjusts decimals automatically: a 1e18 WETH deposit becomes 1_000_000 encrypted cWETH units (6 decimals).
Your account is blocked from wrapping if isRestricted[msg.sender] is true — i.e. you have a pending unwrap request. Wait for the unwrap to finalize or cancel it first.

unwrap

function unwrap(uint64 amount) external returns (uint256 requestId)
Request to convert encrypted cTokens back to plain ERC-20. This is step 1 of a 2-step async process.
amount
uint64
required
Amount of cTokens to unwrap (6 decimals). For example, 1_000_000 = 1 token.
Returns a requestId you use in finalizeUnwrap. The contract marks your account as restricted (isRestricted = true) until finalized or cancelled. Internally, FHE.le(amount, balance) creates an encrypted boolean (canUnwrap) and calls FHE.makePubliclyDecryptable(canUnwrap) to request KMS threshold decryption.

finalizeUnwrap

function finalizeUnwrap(
    uint256 requestId,
    bytes32[] calldata handlesList,
    bytes calldata cleartexts,
    bytes calldata decryptionProof
) external
Step 2 — anyone can call this once the KMS has signed the decryption proof. Typically called by the relayer.
requestId
uint256
required
The request ID returned by unwrap().
handlesList
bytes32[]
required
The FHE ciphertext handles that were decrypted (must match the canUnwrap handle).
cleartexts
bytes
required
ABI-encoded decrypted values. Decoded as bool canUnwrap.
decryptionProof
bytes
required
KMS threshold signatures verified via FHE.checkSignatures().
If canUnwrap == true, the cToken balance is reduced and the underlying ERC-20 is transferred to the requester. The account restriction is cleared regardless of outcome.

cancelUnwrap

function cancelUnwrap(uint256 requestId) external
Cancel a stuck unwrap request. Only callable by the request owner, and only after UNWRAP_TIMEOUT (1 day) has elapsed. Clears the account restriction without transferring any underlying tokens.

transferPlaintext

function transferPlaintext(address to, uint64 amount) external returns (bool)
Transfer cTokens by providing a plaintext amount that is encrypted on-chain via FHE.asEuint64. Used by ConfidentialPaymentRouterV2 internally.

balanceOf

function balanceOf(address account) external view returns (euint64)
Returns the encrypted cToken balance handle. Use the Zama decryption flow to read the actual value.

UnwrapRequest struct

account
address
Address that requested the unwrap.
amount
uint64
Amount requested to unwrap (6 decimals).
handle
bytes32
The ebool ciphertext handle for the canUnwrap boolean.
createdAt
uint256
Block timestamp when the unwrap was requested. Used to enforce the 1-day timeout.

Events

EventParametersNotes
Wrappedaccount, amountcToken amount (6 decimals)
UnwrapRequestedrequestId, account, amount, handle
UnwrapFinalizedrequestId, account, amount, success
UnwrapCancelledrequestId, account
Transferfrom, to, amountamount always 0

Custom errors

ErrorWhen thrown
ZeroAddressZero address passed
ZeroAmountamount is 0
AmountTooHighAdjusted amount exceeds uint64 max
AccountRestrictedCaller has a pending unwrap request
RequestNotFoundrequestId does not exist
RequestNotReadyTimeout not yet elapsed (for cancelUnwrap)
NotRequestOwnerCaller is not the request owner
RequestExpired(reserved)

WrapperFactory

How to deploy a new wrapper for any ERC-20.

Private Portfolio

Using wrap/unwrap from the frontend.

Build docs developers (and LLMs) love