RaycashDocs
Protocol

Confidential Tokens

FHE-encrypted ERC-20 token base — ERC7984, and amount scaling via ERC7984ERC20Wrapper.

Raycash uses Confidential Tokens — an FHE-encrypted wrapper that can be applied to any ERC-20. The underlying standard is OpenZeppelin's ERC7984, which defines confidential ERC-20 tokens with FHE-encrypted balances. See the OpenZeppelin ERC7984 documentation for the canonical reference.

The RaycashWrapper inherits from OpenZeppelin's ERC7984. The codebase also defines a ConfidentialERC20 interface and abstract contract that specifies the dual-overload API surface.

Privacy Invariant

Transfers never revert on insufficient balance. Instead, FHE.select conditionally zeros the transfer amount:

canTransfer = FHE.le(amount, balance)
transferValue = FHE.select(canTransfer, amount, 0)

An observer cannot distinguish a successful transfer from an insufficient-balance no-op. This is a fundamental privacy property — in a traditional ERC-20, a revert reveals that the sender lacks funds.

Architecture

State

FieldTypeDescription
_totalSupplyuint64Cleartext total supply
_balancesmapping(address => euint64)Encrypted balances
_allowancesmapping(address => mapping(address => euint64))Encrypted allowances

The total supply is intentionally cleartext — it represents the total amount of underlying tokens locked in the wrapper, which is already publicly visible on-chain.

Dual-Overload Pattern

The IConfidentialERC20 interface defines two overloads for each function:

  • transfer(address to, externalEuint64 encryptedAmount, bytes inputProof) — For external callers who encrypt amounts off-chain
  • transfer(address to, euint64 amount) — For contract-to-contract calls where the encrypted handle already exists in the FHE domain

The first overload calls FHE.fromExternal to verify the encrypted input proof, then delegates to the second.

Amount Scaling

The wrapper inherits from OZ's ERC7984ERC20Wrapper, which provides a rate() function to convert between the underlying ERC-20 amount (arbitrary decimals) and the confidential uint64 domain.

The rate() and decimals() functions from ERC7984ERC20Wrapper determine the scaling factor. For 6-decimal stablecoins (e.g. USDC), the rate is 1 — compress and expand become identity operations with no precision loss. For 18-decimal tokens, the rate is set to trade precision for range so the amount fits in uint64.

View Functions

FunctionReturnsDescription
balanceOf(account)euint64Encrypted balance handle
allowance(owner, spender)euint64Encrypted allowance handle
totalSupply()uint64Cleartext total supply
decimals()uint8Returns 6 (overridable)
name()stringToken name
symbol()stringToken symbol

Design Decisions

  • ERC7984 base — The wrapper inherits from OpenZeppelin's ERC7984. The ConfidentialERC20 abstract contract in the codebase defines the IConfidentialERC20 interface. Derived contracts define their own supply management.
  • _PLACEHOLDER in Approval events — EIP-20 requires an Approval event with a uint256 value. Since the actual allowance is encrypted, type(uint256).max serves as a placeholder.
  • rate() from ERC7984ERC20Wrapper — Amount scaling is handled by OZ's ERC7984ERC20Wrapper base contract, keeping conversion logic standardized.

Was this page helpful?

On this page