BlindSwap is a fully homomorphic encryption (FHE) powered swap that mints confidential tokens directly from ETH. The contracts issue mUSDC and mZama at fixed rates (1 ETH → 4,000,000,000 base units of mUSDC; 1 ETH → 10,000,000,000 base units of mZama), while the frontend lets users view encrypted balances and selectively decrypt them through the Zama relayer flow.
- Private balances: holdings stay encrypted on-chain via ERC7984 tokens and are only revealed when a user explicitly decrypts.
- Simple on-ramp: turn ETH into FHE-native assets without a price oracle or order book.
- Developer reference: end-to-end pipeline showing how to pair Zama FHEVM contracts with a React/Vite/RainbowKit UI and on-chain/off-chain decryption.
- Predictable economics: fixed swap rates remove slippage and simplify demos on Sepolia.
- Fully confidential tokens minted at purchase time; no plaintext balances are stored on-chain.
- Minimal attack surface: a single BlindSwap contract plus faucet-based ERC7984 tokens, both inheriting ZamaEthereumConfig.
- Batteries included: deploy script, Hardhat tasks, tests, and a production-style frontend (ethers for writes, viem for reads).
- Frontend decryption UX that demonstrates the Zama relayer/KMS flow without mock data.
contracts/BlindSwap.sol: accepts ETH, mints mUSDC or mZama through token faucets, and forwards encrypted transfers to buyers; owner can withdraw collected ETH.contracts/MockUSDC.solandcontracts/MockZama.sol: ERC7984 confidential tokens configured for Ethereum; include a faucet used by BlindSwap to mint encrypted balances.deploy/deploy.ts: Hardhat Deploy script that boots both tokens and BlindSwap with the deployer as owner.tasks/blindSwap.ts: utility tasks to read deployed addresses, perform swaps, and decrypt balances via the FHEVM CLI.test/BlindSwap.ts: coverage for swap paths, fractional payments, revert cases, and owner-only withdrawals (uses the FHEVM mock for unit tests).ui/: React + Vite app that surfaces swap, encrypted balance display, and user-side decryption. Reads use viem/wagmi; writes use ethers; RainbowKit handles wallet connect. Contract data lives inui/src/config/contracts.ts(no frontend env vars).
- Contracts: Solidity 0.8.27, Zama FHEVM Solidity libs, ERC7984 confidential tokens, OpenZeppelin Ownable.
- Tooling: Hardhat + hardhat-deploy, TypeScript, TypeChain (ethers v6), Hardhat FHEVM plugin, Chai, solidity-coverage, gas reporter.
- Frontend: React 18, Vite, TypeScript, wagmi + viem, ethers v6, RainbowKit, plain CSS (no Tailwind).
contracts/smart contracts for BlindSwap and confidential tokens.deploy/deployment scripts for local and Sepolia.tasks/Hardhat tasks for swaps and balance decryption.test/unit tests using the FHEVM mock.deployments/auto-generated artifacts and ABIs after deployments (use these ABIs in the frontend).ui/frontend application (homepage) with wallet connect, swap, encrypted balance view, and decryption UX.docs/Zama-specific guides for contracts and relayer integration.
- Node.js 20+
- npm
# Root (contracts + tasks + tests)
npm install
# Frontend
cd ui
npm installCreate a .env in the repo root with:
INFURA_API_KEY=<your_infura_key>
PRIVATE_KEY=<hex_private_key_with_or_without_0x>
ETHERSCAN_API_KEY=<optional_for_verification>
The config normalizes the private key with 0x if needed. MNEMONIC is intentionally unused.
- Compile:
npm run compile - Test (FHEVM mock):
npm run test - Coverage:
npm run coverage - Local node:
npx hardhat node(for iterative testing with the mock)
- Ensure
.envhasINFURA_API_KEYandPRIVATE_KEY. - Deploy locally (optional):
npx hardhat deploy --network hardhat - Deploy to Sepolia:
npx hardhat deploy --network sepolia - After deploying, grab addresses and ABIs from
deployments/sepolia/BlindSwap.json,MockUSDC.json, andMockZama.json. - Owner withdrawal:
npx hardhat console --network sepoliathen callwithdraw(receiver, amount)from the owner account if needed.
- Print addresses:
npx hardhat task:swap-addresses --network sepolia - Swap mUSDC:
npx hardhat task:swap-musdc --eth 0.5 --network sepolia - Swap mZama:
npx hardhat task:swap-mzama --eth 0.1 --network sepolia - Decrypt balance via CLI:
npx hardhat task:decrypt-balance --token musdc --network sepolia
- Update contract data: paste deployed addresses and ABIs from
deployments/sepolia/*.jsonintoui/src/config/contracts.ts(frontend avoids env vars and JSON imports). - Run dev server:
cd ui && npm run dev - Build:
cd ui && npm run build(preview withnpm run previewif desired)
- Connect wallet (RainbowKit, Sepolia network).
- Pick token (mUSDC or mZama), enter ETH amount, and swap (writes through ethers).
- View encrypted handles for each token (reads through viem).
- Trigger “Decrypt balance” to create a user-side decrypt request through the Zama relayer flow; decrypted amounts stay client-side.
- Rates are fixed on-chain: 1 ETH → 4,000 mUSDC (6 decimals) or 10,000 mZama (6 decimals).
- Contracts reject zero-value payments and guard against overflow; only the owner can withdraw ETH.
- View functions never rely on
msg.sender; addresses are provided explicitly where required. - Frontend does not touch
localStorageor frontend env variables.
- Dynamic pricing using oracle feeds and time-weighted quotes.
- Support for additional confidential assets and multi-hop swaps.
- Optional allow lists and policy toggles for enterprise deployments.
- In-app ABI/address syncing by consuming the
deployments/artifacts automatically. - Gas optimizations and circuit-level benchmarks for FHE operations.
- Optional on-chain proofs for rate changes and off-chain audit hooks.