READ FHE MARKET DOCSLIVE ON SEPOLIAcUSDT · ERC-7984POOL PUBLIC · POSITION PRIVATE
READ FHE MARKET DOCSLIVE ON SEPOLIAcUSDT · ERC-7984POOL PUBLIC · POSITION PRIVATE
PROTOCOL / PRIVACY MODEL

Pool public, position private.

FHE Market keeps the AMM math gas-affordable by leaving pool reserves in cleartext, while every user's share balance lives as a Zama euint64 ciphertext. Only the holder — not the relayer, not the contract, not the indexer — can read the value.

▮ THREAT MODEL
An adversary watching the chain in real time learns the market price and pool size. They never learn which side you took, nor your size — even after settlement.

Overview

FHE Market is a non-custodial, confidential prediction-market protocol. Each market asks a binary question. Users buy YES or NO shares against a Polymarket-style FPMM. Winning shares redeem 1:1 for cUSDT at resolution.

The twist: per-user share balances are encrypted on-chain. Outsiders can see the pool, the price, and the outcome — they cannot see your YES/NO position, your cumulative exposure, or how a particular wallet leans. Only you, holding the right EIP-712 signature, can decrypt your own balance.

What's encrypted

LAYER
PUBLIC
ENCRYPTED
Pool reserves (yesReserve, noReserve)· CPMM math needs cleartext
Market metadata
YES/NO price
Per-user share balance· euint64
Trade size (your tx)· ciphertext input
Position aggregate (PnL)· client-decrypt
Settlement payout· total public · per-user encrypted

The bet amount on a single transaction reveals at the executeBuy step (necessary to update reserves), but your cumulative position across many bets stays opaque — an observer cannot link multiple actions to a single position size without breaking FHE.

FAUCET
Drip 5 cUSDT to your wallet.

One on-chain Faucet.drip(you, 5) bundles USDTMock.mint × 5 + approve + cUSDT.wrap in a single user-signed tx. No relayer round-trip; encrypted balance ready for an EIP-712 reveal in your bet panel.

1 WALLET POP · ~15s · GAS PAID IN ETH

How it works

Each market is a standalone PredictionMarket contract using a Fixed-Product Market Maker (FPMM). Two cleartext reserves — yesReserve and noReserve — sit on the constant-product curve k = yesReserve · noReserve. Buying YES removes YES from the pool, pushing implied probability up; selling does the reverse. Prices stay between $0.00 and $1.00 and sum to one.

  1. Factory deploys a market and seeds it with cUSDT pulled from the treasury.
  2. Trader runs setOperator on cUSDT, then submits buyIntent.
  3. Zama relayer publicly decrypts the transferred ciphertext (~10–60s on Sepolia).
  4. executeBuy runs CPMM math, mints encrypted shares, forwards fee.
  5. At resolve time, factory submits the outcome.
  6. Winners trigger claimIntent → relayer decrypt → executeClaim.

Buy flow · Intent → Execute

Buy, sell, and claim split into intent + execute. The intent locks an encrypted snapshot and marks it publiclyDecryptable; the execute step verifies the relayer's cleartext via FHE.checkSignatures and then settles. Latency between the two is the relayer round-trip, ~10–60s on Sepolia.

01
Submit encrypted intent

Frontend encrypts your trade size client-side. buyIntent calls cUSDT.confidentialTransferFrom, snapshots the actually-transferred ciphertext, marks it publicly decryptable.

const ct = await fhe.encryptU64(amount, market, user);
await market.buyIntent(side, ct.handle, ct.proof, minSharesOut);
02
Relayer decrypt (no read)

Frontend reads pendingBuyHandle(user), calls relayer.publicDecrypt(handle). Relayer returns cleartext + KMS proof.

const r = await fhe.publicDecrypt([handle]);
// { clearValues, decryptionProof }
03
Settle on-chain

Anyone calls executeBuy(user, cleartext, proof). Contract verifies via FHE.checkSignatures, runs CPMM math, mints encrypted shares, forwards the protocol fee.

await market.executeBuy(user, cleartext, proof);

Sells branchlessly clamp the user's input to their encrypted balance via FHE.le + FHE.selectin the intent step — selling more than you own can't leak comparison results because the clamp happens entirely in ciphertext. The clamped value is what gets revealed.

Decryption flows

Two paths take cleartext off-chain.

USER-DECRYPT
"my YES/NO position"

User signs an EIP-712 typed message; relayer returns a re-encryption keyed to the signer. Decryption happens client-side. Powers the Reveal button on every market page.

PUBLIC-DECRYPT
Settlement

Used at executeBuy, executeSell, executeClaim. Cleartext + KMS proof submit on-chain; FHE.checkSignatures verifies before settlement.

Trading

  • Connect wallet on Sepolia, open any market, enter cUSDT amount or shares.
  • First interaction signs cUSDT.setOperator(market, until) — a 24h permission.
  • Minimum bet is 0.01 cUSDT so fees never round to zero.
  • Each action is two wallet pops (intent + execute) plus a relayer wait between.

Fees

Flat platform fee of 1.50% on both buy and sell. Forwarded on-chain to Treasury via cUSDT.confidentialTransfer. No deposit/withdraw fees, no spread.

Capped on-chain by MAX_FEE_BPS = 500 (5%), owner-tunable below that.

Resolution

  • YES / NO: winning shares redeem 1 cUSDT each via the 2-tx claim flow.
  • CANCELLED: all positions refund — both YES and NO 1:1.
  • After all winners claim, factory sweeps residual cUSDT back to treasury.

Contracts (Sepolia)

Stack & versions

Contracts
  @fhevm/solidity         0.11.1
  @fhevm/hardhat-plugin   0.4.2
  @openzeppelin/contracts ^5.1.0
  hardhat                 ^2.28.4
  solc                    0.8.27 + cancun + viaIR

Frontend
  next                    16.2.4
  wagmi                   ^2.19
  viem                    ^2.48
  @rainbow-me/rainbowkit  ^2.2
  @zama-fhe/relayer-sdk   0.4.1

Browser bundle requires Cross-Origin-Opener-Policy: same-origin + Cross-Origin-Embedder-Policy: require-corp for SharedArrayBuffer (configured in next.config.ts).

FAQ

Why are reserves public if shares are encrypted?

Encrypting reserves would force FHE.mul/FHE.div on two ciphertexts per trade — gas explodes by orders of magnitude. Keeping reserves public is the practical FHEVM trade-off.

Why is each action two transactions?

CPMM math needs cleartext numbers. The relayer's public-decryption oracle has to sign the value off-chain (~10–60s). Intent locks the snapshot; execute settles after the signed cleartext lands.

What can my wallet's position leak?

Per-tx amounts leak at execute-time. Cumulative position, total YES vs NO ratio, and final share count don't.

Where do I get cUSDT?

Hit the in-app Faucet — one tx, 5 cUSDT into your wallet.

Support

Source + issues: github.com/joymadhu49/fhe-market. Twitter: @zx_joy_.

FHE Market is provided "as is" on Sepolia for research and evaluation. Trading involves risk of loss; do not deposit funds you cannot afford to lose.

LAST UPDATED MAY 2026← BACK TO MARKETS