Developer Guide
CipherOps is a production reference implementation for Zama ERC-7984 workflows — from registry discovery through private reveal, unwrap, and TokenOps Disperse.
1 — Architecture Overview
Full lifecycle demonstrated by CipherOps
Read live ERC-20 ↔ ERC-7984 pairs from Zama's on-chain registry contracts
Mint Sepolia test tokens via ERC20.mint(address, uint256) on official mock tokens
ERC20.approve(wrapper, amount) — grant the wrapper spending rights
wrapper.wrap(address, uint256) — seal ERC-20 into an FHE-encrypted ERC-7984 token
confidentialBalanceOf → EIP-712 user-decrypt via Zama relayer → plaintext in browser only
wrapper.unwrap(…, encHandle, inputProof) + publicDecrypt → finalizeUnwrap — two-step Zama Gateway flow
register(token) + setOperator(singleton) + FHE-encrypt amounts → Disperse singleton on Sepolia + Mainnet
2 — Key SDKs & Packages
wagmi v2 + viem
wagmi@^2.15 / viem@^2.31Contract reads, writes, wallet connection, multicall, chain config. All registry reads and lifecycle write calls use wagmi hooks.
@zama-fhe/relayer-sdk
^0.4.3Original Zama FHE SDK used for Private Reveal and Unwrap flows. Provides userDecrypt / publicDecrypt / createFhevmInstance.
@zama-fhe/sdk
3.0.1Newer Zama unified SDK required by @tokenops/sdk peer dep. Provides RelayerWeb (browser Web Worker), ViemSigner, and SepoliaConfig.
@tokenops/sdk
1.0.0TokenOps typed viem-first SDK. Provides ConfidentialDisperseClient, useDisperse, useRegister, usePreflightDisperse, and all React hooks for fhe-disperse / fhe-airdrop / fhe-vesting.
RainbowKit v2
^2.2.5Wallet connection UI (MetaMask, Coinbase Wallet, WalletConnect). Custom dark-themed ConnectButton.
Next.js 15
^15.3App Router, server components, static prerendering. All write-flow components are client components; informational pages are static.
Full analysis in docs/TOKENOPS_CLIENT_ANALYSIS.md
3 — Implementation Notes
// From src/hooks/useRegistryPairs.ts
const { data } = useReadContract({
address: REGISTRY_ADDRESS, // Zama on-chain registry
abi: REGISTRY_ABI,
functionName: "getTokenConfidentialTokenPairs",
chainId: 11155111, // Sepolia
});
// Returns { tokenAddress, confidentialTokenAddress, isValid }[]// From src/components/registry/TokenActionPanel.tsx
const { data: handle } = useReadContract({
address: wrapperAddress, // ERC-7984 confidential token
abi: WRAPPER_ABI,
functionName: "confidentialBalanceOf",
args: [userAddress],
});
// Returns bytes32 — FHE-encrypted handle, NOT plaintext// From src/lib/fhevm/react/useFHEDecrypt.ts
const { decrypt, results } = useFHEDecrypt({
instance, // FhevmInstance from useFhevm()
ethersSigner, // ethers.JsonRpcSigner (from wagmi)
fhevmDecryptionSignatureStorage, // in-memory storage
chainId,
requests: [{ handle, contractAddress }],
});
decrypt(); // prompts EIP-712 sign → Zama relayer → plaintext in results[handle]// From src/components/operations/DisperseForm.tsx
// 1. One-time registration per token
useRegister().mutate({ token: erc7984Address });
// 2. Approve singleton as ERC-7984 operator
writeContract({ abi: erc7984OperatorAbi, functionName: "setOperator",
args: [DISPERSE_SINGLETON, ERC7984_OPERATOR_MAX_DEADLINE] });
// 3. FHE-encrypt amounts and disperse
useDisperse({ encryptor: encryptorFactory }).mutate({
token: erc7984Address,
mode: "direct",
recipients: ["0xAlice", "0xBob"],
amounts: [parseUnits("10", 18), parseUnits("20", 18)],
});
// SDK encrypts each amount locally before broadcast// From src/hooks/useUnwrapAction.ts
// Step 1: encrypt amount + submit unwrap
const { handles, inputProof } = await instance
.createEncryptedInput(wrapperAddress, userAddress).add64(amount).encrypt();
writeContract({ functionName: "unwrap",
args: [from, to, handles[0], inputProof] });
// Step 2: Zama Gateway public decrypt
const { clearValues, decryptionProof } =
await instance.publicDecrypt([unwrapRequestId]);
// Step 3: finalize — releases underlying ERC-20
writeContract({ functionName: "finalizeUnwrap",
args: [unwrapRequestId, clearValues[unwrapRequestId], decryptionProof] });4 — Routes
Product showcase — hero video, lifecycle strip, protocol coverage, feature menu
Live lifecycle app — registry explorer, Faucet/Approve/Wrap/Private Reveal/Unwrap
TokenOps Disperse — register, allow, CSV import, send FHE-encrypted payouts
Recipient education — how to reveal an encrypted payout via Private Reveal
Proof center — verified tx receipts, lifecycle checklist, privacy guarantees
This page — architecture, packages, code snippets, route map, limitations
5 — Safety & Limitations
6 — External Resources
Explore the implementation
Every feature is live on Sepolia. All source is in this repository.