RaycashDocs
Protocol

Counterfactual Deposits

Counterfactual CREATE2-based deposit system integrated into the RaycashWrapper.

Counterfactual deposits allow users to deposit tokens by sending them to a pre-computed CREATE2 address — no smart contract interaction required from the user. The wrapper's operator later calls initWrap(), which atomically deploys a RaycashDepositor contract at that address, sweeps the tokens, and records a deposit with the FHE-encrypted recipient.

Deposit Flow

Key Properties

  • FHE-encrypted recipient — The recipient is an encrypted address. The encrypted recipient handle doubles as the CREATE2 salt, cryptographically binding the depositor address to the recipient.
  • Operator-gated — Only the OPERATOR_ROLE holder can call initWrap(). This is the single entry point for deposits — there is no external deposit() function.
  • Sweep, not safeTransferFrom — The wrapper calls sweep() on the depositor to pull tokens. No allowance/approval needed.
  • Persistent contract — The depositor stays on-chain. If tokens are sent after deployment, a withdraw() call can recover them.
  • Batch supportinitWrap(DeployParams[]) accepts an array to process multiple deposits atomically.

CREATE2 Address Computation

The deposit address is deterministic and can be computed off-chain from:

  • Wrapper address (the CREATE2 deployer)
  • Salt (the FHE-encrypted recipient handle)
  • Init code hash (depositor bytecode + ABI-encoded wrapper address)

Each unique encrypted recipient produces a unique deposit address. See src/utils/compute-depositor-address.ts for the canonical off-chain implementation.

Permissionless User Withdrawal

This provides a permissionless escape hatch for users whose tokens are stuck at a depositor address — either because the operator has not called initWrap(), or because new tokens were sent after deployment. The flow is non-custodial: the depositor address never belongs to Raycash, and the owner can always recover their cleartext tokens without operator cooperation.

withdraw(depositorAddr, secret, recipient, token, handle)

Withdrawal uses a hash-preimage scheme instead of the old two-phase FHE-based flow. The caller provides the depositor address, a secret whose hash matches the committed hash for that depositor, and a recipient address. In a single transaction, the wrapper verifies the preimage, sweeps tokens from the depositor to the recipient, and emits a WithdrawalCompleted event. Tokens never touch the wrapper's balance.

This works for both undeployed depositors (where the operator hasn't called initWrap) and existing depositors (where new tokens arrived after deployment).

Was this page helpful?

On this page