Skip to content
IlltrapxPublic

About

Confidential binary prediction market built on Zama FHEVM — bets stay encrypted until resolution

Resources

Contributing

Stars

1 star

Watchers

0 watching

Forks

Latest commit

 

History

19 Commits

Folders and files

Repository files navigation

( • ) PredIQ

Confidential prediction markets powered by Fully Homomorphic Encryption

Bet without revealing your hand.

Live Demo

CI Sepolia FHEVM Solidity React 19 TypeScript License

PredIQ — encrypt-a-bet demo

Table of Contents


The idea

Public prediction markets leak the exact thing they need to work well — every stake and every position is visible, so whales get copy-traded and order flow gets front-run the moment it lands.

PredIQ encrypts the two fields that leak an edge — stake amount and chosen side — while keeping the market outcome public. Every bet is FHE ciphertext; the contract tallies pools and pays out winners without ever seeing an individual bet. Only the aggregate pools are revealed, and only at resolution.

Public markets PredIQ
Bet amount 👁️ public 🔒 encrypted
Chosen side 👁️ public 🔒 encrypted
Per-user position 👁️ public 🔒 encrypted
Aggregate pools 👁️ public 🔒 until resolution
Question / outcome public public

Features

  • Fully encrypted bets — amount and side encrypted client-side via the Zama relayer SDK; the chain only sees ciphertext
  • Indistinguishable writes — every bet() updates both pools and both stakes (FHE.select adds an encrypted 0 to the non-chosen side), so storage writes never leak which side you took
  • ACL-gated decryption — decrypt rights are granted only to a winner at claim time
  • On-chain reveal integrity — pool totals are decrypted via the Zama KMS relayer and verified on-chain with FHE.checkSignatures before any payout math runs
  • ERC-7984 confidential token (CST) — bets are funded with an encrypted token end-to-end
  • Testnet faucet — one click for 1,000 CST, via a serverless function so the deployer key never reaches the client
  • Dynamic OG share cards, Polymarket trending feed, leaderboard, and category filtering

Demo

Live at prediq-umber.vercel.app — no real funds, everything runs on Sepolia.

  1. Connect wallet — RainbowKit, auto-switches to Sepolia.
  2. Get test CST — one-click faucet sends 1,000 CST.
  3. Approve once — ERC-7984 uses time-bounded operators, not ERC-20 allowances.
  4. Pick or create a market — creating one makes you its resolver.
  5. Bet privately — choose YES/NO, enter a stake, hit Encrypt & bet. Encrypted in-browser before broadcast.
  6. Resolve — after the deadline, the resolver declares the winning side.
  7. Finalize — pools are decrypted off-chain and submitted back with a proof (finalizePools); only now are totals public.
  8. Claim — winners claim; payout is computed on ciphertext and revealed to you alone via userDecrypt.

Architecture

┌─────────────── BROWSER ───────────────┐        ┌──────────── SEPOLIA ──────────────────┐
│  React 19 + Vite + RainbowKit         │        │                                        │
│                                       │  bet   │  PredictionMarket.sol                  │
│  relayer-sdk: encrypt                 │ ─────► │   fromExternal(amount, side)            │
│    add64(amount).addBool(side)        │ (ct +  │   allowTransient(amt, CST) ────────────┼──► ConfidentialStakeToken.sol
│    → handles + inputProof             │ proof) │   CST.confidentialTransferFrom()        │    (ERC7984, euint64 balances)
│                                       │        │   FHE.select → both pools + stakes      │
│                                       │        │   FHE.allowThis(handles)                │
│  Get test CST ──────────────────────► │        │                                        │
│    POST /api/faucet                   │        │  resolve(): makePubliclyDecryptable     │
│          │                            │        │              ▲                          │
└──────────┼────────────────────────────┘        └──────────────┼───────────────────────── ┘
           │                                                     │
           ▼  Vercel serverless (deployer wallet)                │ publicDecrypt([yesPool, noPool])
     relayer-sdk node: encrypt 1000 CST                  ┌───────┴──────────┐
     → CST.confidentialTransfer(user)                    │  Zama KMS /      │
                                                         │  relayer         │
     finalizePools(clears, proof) ◄──────────────────────┤  + checkSig      │
     claim(): mul/div on ct → CST.transfer               └──────────────────┘

Indistinguishable write pattern: bet() writes to both pools and both stake slots on every call — the non-chosen side just adds an encrypted 0. No write pattern leaks which side you took.

Cross-contract ACL: the market calls FHE.allowTransient(amt, CST) so the token contract can operate on the handle for exactly one transaction, then calls confidentialTransferFrom. The funded amount returned (clamped by ERC-7984 to your real balance) becomes the stake, not the requested amount.

Reveal flow: resolve → makePubliclyDecryptable → off-chain publicDecrypt → finalizePools verifies the relayer's proof with FHE.checkSignatures before decoding pool totals.


Contract API

createMarket(question, deadline)         → caller becomes resolver
bet(id, encAmount, encSide, proof)       → both pools + both stakes update (side stays hidden)
resolve(id, winningSide)                 → resolver-only, after deadline; flags pools decryptable
finalizePools(id, clears, proof)         → publicDecrypt output → checkSignatures → decode pools
claim(id)                                → payout = stake × totalPool / winningPool (paid in CST)

Views: getMarket · getPools · getStakes · getPayout · hasClaimed · stakeToken


Deployed contracts (Sepolia)

Contract Address
PredictionMarket 0x5B6Cb01B6AcEBa5148e16fBEa3d1f77e434004d9
ConfidentialStakeToken (CST) 0xF9B73bF34D8EAb58D3d3498714BF22C2a927463B

Both verified on Etherscan. Full lifecycle exercised end-to-end on real Sepolia (test/PredictionMarketSepolia.ts) — the publicDecrypt → checkSignatures round-trip and a CST-funded payout both pass on-chain.


Tech stack

Layer Technology
Smart contracts Solidity 0.8.27, @fhevm/solidity 0.11, @openzeppelin/confidential-contracts (ERC-7984)
FHE types euint64, ebool, FHE.select, euint128 (payout arithmetic)
FHE runtime Zama FHEVM (Sepolia co-processor), @zama-fhe/relayer-sdk, Zama KMS relayer
Contract tooling Hardhat 2, @fhevm/hardhat-plugin, TypeChain, hardhat-deploy, hardhat-verify, solidity-coverage
Frontend React 19, TypeScript 6, Vite 7, Tailwind CSS v4, framer-motion
Web3 ethers v6, wagmi v2, viem, RainbowKit
Off-chain Supabase (categories, faucet claims), Vercel (serverless + edge), @vercel/og

Getting started

Prerequisites: Node.js ≥ 20, npm ≥ 10

git clone https://github.com/Illtrapx/prediq.git
cd prediq
npm install                 # contract tooling
cd frontend && npm install  # frontend

Frontend env — frontend/.env.local:

Variable Required Description
VITE_SUPABASE_URL yes Supabase project URL
VITE_SUPABASE_ANON_KEY yes Supabase publishable anon key
VITE_DEMO_TRADES no Set off to disable synthetic showcase activity

Serverless API env — Vercel project settings:

Variable Required Description
FAUCET_PRIVATE_KEY yes Deployer wallet key. Testnet-only. Never commit.
SUPABASE_URL no Falls back to VITE_SUPABASE_URL
SUPABASE_KEY no Falls back to VITE_SUPABASE_ANON_KEY
SEPOLIA_RPC_URL no Defaults to a public Sepolia RPC
APP_ORIGIN no Defaults to https://prediq-umber.vercel.app

Hardhat uses npx hardhat vars set MNEMONIC|INFURA_API_KEY|ETHERSCAN_API_KEY instead of .env; defaults to the throwaway test … junk mnemonic so local runs need no setup.

# Contracts
npm run compile          # compile + generate TypeChain types
npm test                 # full suite on mock FHEVM
npm run test:sepolia     # real-network E2E
npm run deploy:sepolia   # deploy CST then PredictionMarket
npm run verify:sepolia   # verify both on Etherscan

# Frontend
cd frontend
npm run dev              # http://localhost:5173, uses the deployed Sepolia contracts
npm run build            # type-check + production build
npx vercel --prod        # deploy (or connect the repo, root dir frontend/)

vercel.json sets security headers (COOP, X-Frame-Options, CSP scoped to Sepolia RPC + Supabase + Zama relayer) and rewrites /m/:id to /api/share?id=:id for short bet links.


API routes

All routes live in frontend/api/ and run as Vercel functions.

Route Method Purpose
/api/faucet POST Sends 1,000 CST to { "address": "0x…" }, IP + address rate-limited
/api/og?q=&deadline= GET Edge-rendered OG image for a market
/api/share?id= GET Share card HTML for /m/:id short links

Security & known limitations

Hidden: each bet's amount and side, every per-user stake. Public by design: question, deadline, resolver, winning side, and aggregate pools after finalizePools.

  • ACL discipline — every persisted ciphertext gets FHE.allowThis; cross-contract calls use FHE.allowTransient
  • Reveal integrity — finalizePools verifies the relayer's proof on-chain (FHE.checkSignatures) and is replay-guarded
  • Input guards — overflow, empty-market, and zero-pool checks reject unsafe states before they can execute

Known limitations:

  • Trusted resolver — outcome is set unilaterally; the 24 h review window is informational, not yet enforced on-chain
  • Division dust — integer payout division can leave a tiny CST remainder in the contract
  • Faucet abuse — rate-limited by wallet + IP, but a determined attacker rotating IPs can still farm testnet CST

Roadmap

  • Oracle-backed resolution (Chainlink / UMA) so resolvers can't manipulate outcomes
  • On-chain dispute window enforcing the resolution-review period
  • Multi-choice markets beyond binary YES/NO
  • Batch finalization bot
  • Mainnet CST supply with confidential, ETH-gated minting

Contributing

See CONTRIBUTING.md. Short version:

git checkout -b feat/your-feature
npm run lint && npm test                      # contracts
cd frontend && npm run lint && npm run build  # frontend

Open an issue first for non-trivial changes.


License

BSD-3-Clause-Clear


Built with Zama FHEVM

About

Confidential binary prediction market built on Zama FHEVM — bets stay encrypted until resolution

Resources

Contributing

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages