Skip to main content

ConfidentialUSDCWrapper API

Contract for wrapping USDC into confidential (encrypted) tokens.

Address (Sepolia): 0xf99376BE228E8212C3C9b8B746683C96C1517e8B

Write Functions

wrap

Convert regular USDC to confidential USDC.

function wrap(uint256 amount) external
ParameterTypeDescription
amountuint256Amount of USDC to wrap (6 decimals)

Requirements:

  • Caller must have approved this contract for amount of USDC
  • Caller must have sufficient USDC balance

Emits: Wrapped(account, amount)

Example:

// First approve
await usdc.approve(wrapperAddress, 100_000000n);

// Then wrap
await wrapper.wrap(100_000000n); // Wrap 100 USDC

unwrap

Convert confidential USDC back to regular USDC.

function unwrap(
einput encryptedAmount,
bytes calldata inputProof
) external
ParameterTypeDescription
encryptedAmounteinputEncrypted amount to unwrap
inputProofbytesEncryption proof

Requirements:

  • Caller must have sufficient encrypted balance

Emits: Unwrapped(account, amount)

Example:

const { handles, inputProof } = await encryptAmount(50_000000n);
await wrapper.unwrap(handles[0], inputProof);

transferEncrypted

Transfer confidential tokens to another address.

function transferEncrypted(
address to,
einput encryptedAmount,
bytes calldata inputProof
) external returns (bool)
ParameterTypeDescription
toaddressRecipient address
encryptedAmounteinputEncrypted transfer amount
inputProofbytesEncryption proof

Returns: bool — Success status

Emits: ConfidentialTransfer(from, to)


approveEncrypted

Approve another address to spend your confidential tokens.

function approveEncrypted(
address spender,
einput encryptedAmount,
bytes calldata inputProof
) external returns (bool)
ParameterTypeDescription
spenderaddressAddress to approve
encryptedAmounteinputEncrypted allowance
inputProofbytesEncryption proof

Returns: bool — Success status

Emits: EncryptedApproval(owner, spender)


transferFromEncrypted

Transfer from another account (requires approval).

function transferFromEncrypted(
address from,
address to,
einput encryptedAmount,
bytes calldata inputProof
) external returns (bool)
ParameterTypeDescription
fromaddressSource address
toaddressDestination address
encryptedAmounteinputEncrypted amount
inputProofbytesEncryption proof

Requirements:

  • Caller must have sufficient allowance from from

Read Functions

balanceOfEncrypted

Get encrypted balance of an account.

function balanceOfEncrypted(
address account
) external view returns (euint64)
ParameterTypeDescription
accountaddressAccount to check

Returns: euint64 — Encrypted balance (must be decrypted client-side)

Example:

const encryptedBalance = await wrapper.balanceOfEncrypted(userAddress);
// Decrypt using fhevmjs
const balance = await fhevm.decrypt(encryptedBalance, ...);

allowanceEncrypted

Get encrypted allowance.

function allowanceEncrypted(
address owner,
address spender
) external view returns (euint64)
ParameterTypeDescription
owneraddressToken owner
spenderaddressApproved spender

Returns: euint64 — Encrypted allowance


totalWrapped

Get total amount of USDC wrapped (plaintext).

function totalWrapped() external view returns (uint256)

Returns: uint256 — Total USDC held in contract


underlyingToken

Get address of underlying USDC token.

function underlyingToken() external view returns (address)

Returns: address — USDC contract address


decimals

Get token decimals.

function decimals() external view returns (uint8)

Returns: uint8 — Always 6 (matching USDC)


name

Get token name.

function name() external view returns (string memory)

Returns: string — "Confidential USDC"


symbol

Get token symbol.

function symbol() external view returns (string memory)

Returns: string — "cUSDC"


Events

Wrapped

Emitted when USDC is wrapped.

event Wrapped(
address indexed account,
uint256 amount
);
ParameterTypeDescription
accountaddressUser who wrapped
amountuint256Amount wrapped

Unwrapped

Emitted when confidential USDC is unwrapped.

event Unwrapped(
address indexed account,
uint256 amount
);
ParameterTypeDescription
accountaddressUser who unwrapped
amountuint256Amount unwrapped

ConfidentialTransfer

Emitted on encrypted transfer.

event ConfidentialTransfer(
address indexed from,
address indexed to
);

Note: Amount is not included — it's confidential!

EncryptedApproval

Emitted when approval is set.

event EncryptedApproval(
address indexed owner,
address indexed spender
);

Decryption

To read an encrypted balance, you need to decrypt it:

Using fhevmjs

import { createInstance } from 'fhevmjs';

async function getBalance(userAddress: string) {
// 1. Get encrypted balance
const encryptedBalance = await wrapper.balanceOfEncrypted(userAddress);

// 2. Initialize fhevm
const fhevm = await createInstance({
networkUrl: 'https://rpc.sepolia.org',
});

// 3. Request reencryption (decryption)
const { publicKey, privateKey } = fhevm.generateKeypair();
const eip712 = fhevm.createEIP712(publicKey, wrapperAddress);
const signature = await signer.signTypedData(eip712);

const decrypted = await fhevm.reencrypt(
encryptedBalance,
privateKey,
publicKey,
signature,
wrapperAddress,
userAddress
);

return decrypted;
}

Decryption Permissions

Only the balance owner can decrypt their balance. This requires:

  1. Ownership proof (signature)
  2. Valid keypair for reencryption
  3. Request to the Zama Gateway

Error Codes

ErrorDescription
InsufficientBalance()Not enough tokens
InsufficientAllowance()Not enough allowance
InvalidAmount()Amount is zero
TransferFailed()USDC transfer failed

Integration with PaymentGateway

The PaymentGateway uses this wrapper internally:

// AruviPaymentGateway.sol
function send(address recipient, externalEuint64 encryptedAmount, bytes calldata proof) {
// Transfers confidential tokens via wrapper
wrapper.confidentialTransferFrom(msg.sender, recipient, amount, proof);
// ...
}

Users typically interact via PaymentGateway, not directly with the wrapper.