This project implements a Confidential OTC (Over-The-Counter) Escrow smart contract that combines:
- OpenZeppelin Confidential Contracts (ERC-7984) for confidential token balances and transfers.
- Zama FHEVM library for encrypted values (
euint64,eaddress) and confidential operations. - Gateway / Relayer SDK for encrypted input generation, attestations, and finalization of trades.
- Confidential order creation: maker posts an order with encrypted amountIn, amountOut, and optional allowlisted taker.
- Confidential escrow: tokens are moved into the contract using ERC-7984
confidentialTransferFrom. - Encrypted taker payments: taker pays in using confidential tokens.
- Off-chain equality checks: encrypted equality (
amountIn == takerPay) is validated by the Gateway. - Gateway finalization: once transfers are valid, the Gateway calls
finalizeFill(...)on-chain. - Optional post-trade reveal: maker can choose to make amounts/taker publicly decryptable for audit.
Maker OTC Escrow Contract Taker
│ createEncryptedInput │ │
│ ───────────────────────▶│ OrderCreated (encrypted) │
│ confidentialTransferOut │ │
│ ───────────────────────▶│ │
│ │ │ createEncryptedInput
│ │◀───────────────────────────────── │
│ │ FillRequested (takerPay handle) │
│ │ │
│ ▼ │
│ FHE Execution Layer / Gateway │
│ - validate attestations │
│ - check encrypted equality │
│ - transfer confidential balances │
│ - call finalizeFill on-chain │
│ │
- Maker encrypts order terms (amountIn, amountOut, optional taker) and escrows
amountOuttokens. - Maker calls
createOrder(...)with external handles + attestation. - Taker encrypts payment (
takerPayEnc) and callsfillOrder(...). - Contract records taker handle, emits
FillRequested. - Gateway validates
amountInEnc == takerPayEnc, performs confidential transfers:takerPay→ makeramountOut→ taker
- Gateway calls
finalizeFill(orderId, taker). - Maker may reveal terms post-trade.
- Issue attestations for encrypted inputs created with the Relayer SDK.
- Validate ciphertext equality (e.g.,
amountIn == takerPay). - Update confidential balances in ERC-7984 tokens.
- Finalize escrow fills by calling back into the smart contract with
finalizeFill.
# install dependencies
npm install --save-dev hardhat @nomiclabs/hardhat-ethers ethers typescript ts-node
npm install @zama-fhe/relayer-sdk @openzeppelin/confidential-contractsUpdate hardhat.config.ts:
networks: {
base: {
url: process.env.BASE_RPC,
chainId: 8453,
accounts: [process.env.DEPLOYER_PRIVATE_KEY]
},
baseSepolia: {
url: process.env.BASE_SEPOLIA_RPC,
chainId: 84532,
accounts: [process.env.DEPLOYER_PRIVATE_KEY]
}
}Deploy:
npx hardhat run scripts/deploy.ts --network baseconst enc = await createEncryptedInput(relayer);
enc.addUint64(amountIn);
enc.addUint64(amountOut);
enc.addAddress(taker);
const { handles, attestation } = enc.build();
await otc.createOrder(
tokenIn,
tokenOut,
handles[0], // amountIn
handles[1], // amountOut
handles[2], // taker
attestation,
deadline,
true // doTransferOut
);const enc = await createEncryptedInput(relayer);
enc.addUint64(payIn);
const { handles, attestation } = enc.build();
await otc.fillOrder(
orderId,
handles[0], // takerPay
attestation,
true // doTransferIn
);// Only callable by gateway
function finalizeFill(uint256 id, address taker) external;function revealTerms(uint256 id) external;- Never try to
requireencrypted booleans on-chain — comparisons must be validated by the Gateway. - Ensure the
gatewayaddress is properly governed (multisig or DAO). - Always audit before mainnet deployment.

