RaycashDocs
Protocol

Wrapping

Two-phase wrap flow -- depositing cleartext tokens into the confidential domain.

Wrapping converts cleartext ERC-20 tokens into FHE-encrypted confidential tokens. The RaycashWrapper (which extends the abstract ERC7984AsyncWrapper base) uses a two-phase design to preserve privacy while supporting compliance.

Wrap Flow

Phase 1: initWrap

The operator (holder of OPERATOR_ROLE) calls initWrap(DeployParams) to initiate a deposit. There is no external deposit() function — all deposits go through the operator-gated initWrap() flow.

The wrapper:

  1. Verifies the FHE-encrypted recipient via FHE.fromExternal
  2. Uses the encrypted recipient handle as the CREATE2 salt
  3. Deploys a RaycashDepositor at the deterministic address
  4. Sweeps the expected amount from the depositor to the wrapper
  5. Compresses the amount to euint64 via rate()
  6. Creates a Deposit record with the encrypted recipient
  7. Commits the encrypted recipient for future withdrawal verification

Encrypted Recipients and Proof Binding

The recipient is an FHE-encrypted address (externalEaddress). The inputProof in DeployParams is bound to the wrapper's contract address — it is verified inside the wrapper via FHE.fromExternal(encryptedRecipient, inputProof). Integrators must encrypt the recipient using the wrapper address as the contract context (not the depositor or any other address).

This means the wrapper doesn't know who the tokens are for until finalization. finalizeWrap(depositIndices, recipient) is permissionless — anyone can call it on behalf of any recipient. The FHE.eq check determines whether a given deposit's encrypted amount is credited to the recipient or treated as zero (decoy). Non-matching recipients mint confidential zero, indistinguishable from a real mint.

Deposit Record

struct Deposit {
    address depositor;       // CREATE2 depositor address (cleartext)
    uint256 originalAmount;  // cleartext amount for off-chain tracking
    euint64 amount;          // encrypted amount, consumed by finalizeWrap
    eaddress recipient;      // encrypted recipient
}

Phase 2: finalizeWrap

The recipient (or anyone on their behalf) calls finalizeWrap with a list of deposit indices and the recipient address.

The wrapper:

  1. For each index, computes isMatch = FHE.eq(deposit.recipient, recipient)
  2. Accumulates sum = Σ FHE.select(isMatch, deposit.amount, 0) — non-matching deposits contribute zero
  3. Zeroes matched deposits to prevent double-claiming
  4. Mints sum to the recipient

finalizeWrap is permissionless — anyone can call it with valid deposit indices and a recipient address.

Decoy Mechanism

The caller must include at least minDecoys deposit indices. Non-matching deposits are processed but contribute zero to the sum via FHE.select. This hides which specific deposits belong to the recipient — an observer sees a batch of indices but cannot determine which ones were actually claimed.

Was this page helpful?

On this page