Zero-Knowledge Verified Prediction Markets on Chainlink CRE
Chainlink Convergence 2026 Hackathon | Prediction Markets Track
https://dark-oracle-pred-market-176028355587.us-central1.run.app
Watch the full narrated demo — includes frontend walkthrough + CRE workflow simulation with all 7 pipeline steps
Prediction markets hold $1.2B+ in TVL, yet users have zero mathematical proof that resolution is honest. Polymarket relies on economic incentives (UMA token holder voting). Kalshi relies on institutional trust (a company decides). Both assume honesty rather than proving it. Oracle manipulation is invisible theft with no recourse.
Dark Oracle replaces trust with proof. Chainlink CRE fetches price data from 3 independent APIs across a Decentralized Oracle Network with BFT consensus. A Groth16 zero-knowledge proof cryptographically binds the market ID, outcome, data sources, and timestamp together — tamper with any input and the on-chain pairing check fails. Double verification: DON cryptographic signature + on-chain ZK proof. Anyone can verify. Nobody can manipulate.
"Polymarket trusts that nobody will dispute the result. Kalshi trusts that the company is honest. Dark Oracle trusts math."
+-----------------------+
| React dApp |
| (Next.js 16 + wagmi)|
+-----------+-----------+
|
v
+-----------+-----------+
| DarkOracleMarket.sol |
| (Base Sepolia) |
+-----------+-----------+
^
| onReport (DON-signed)
+-----------+-----------+
| KeystoneForwarder |
+-----------+-----------+
^
+-----------+-----------+
| Chainlink CRE DON |
| 7-step resolution |
+-----------+-----------+
/ | \
v v v
+-----------+ +---------+ +-----------+
| CoinGecko | | Binance | | Coinbase |
+-----------+ +---------+ +-----------+
|
v
+-----------+-----------+
| External ZK Prover |
| (Groth16 proof gen) |
+-----------+-----------+
|
v
+-----------+-----------+
| DarkOracleVerifier.sol|
| BN254 pairing check |
| (236k gas) |
+-----------------------+
Flow: CRE DON fetches prices from 3 sources -> calculates consensus -> calls external prover for Groth16 proof -> delivers DON-signed report to contract -> on-chain BN254 pairing verification -> market resolved.
- Multi-source data aggregation — CoinGecko, Binance, and Coinbase fetched independently via CRE
- Real Groth16 ZK proof verification — 771-constraint circuit, BN254 pairing check on-chain (236k gas)
- 7-screen React dApp with Proof Inspector page for visual ZK verification
- Parimutuel market model with USDC collateral and proportional payouts
- CRE 7-step resolution pipeline — compiled to WASM, simulation-verified
- Bypass mode toggle — testing vs production, currently disabled (real Groth16 active)
- Double verification — DON cryptographic signature + on-chain ZK proof must both pass
| Layer | Technology |
|---|---|
| Smart Contracts | Solidity 0.8.19, Foundry, OpenZeppelin (ReentrancyGuard, Ownable) |
| ZK Circuits | Circom 2.1, Groth16, BN254 curve, snarkjs, Poseidon hashing |
| CRE Workflow | Chainlink CRE SDK, TypeScript, WASM (Javy-compiled) |
| Prover Service | Express.js, snarkjs, Docker |
| Frontend | Next.js 16, React 19, wagmi v2, RainbowKit 2.2, Tailwind v4 |
| Chain | Base Sepolia (84532) |
| Contract | Address | Verified |
|---|---|---|
| MockUSDC | 0x1a3065F54a56760aB45499Eb166eE9D3b997fd49 |
Yes |
| DarkOracleVerifier | 0xF0029EC0836173806b3c26C383C55436b41959BC |
Yes |
| DarkOracleMarket | 0xE79Aff29c29D3CfD93e1eF647Ba4220f0e6CF5b2 |
Yes |
bypassVerification is false — real Groth16 pairing verification is active on-chain.
# Clone
git clone https://github.com/jamallwinn/dark-oracle.git
cd dark-oracle
# Smart Contracts
cd contracts
forge build
forge test -v # 87/87 tests pass
# Frontend
cd frontend
npm install
npm run dev # http://localhost:3000
# ZK Circuit (optional — artifacts included)
cd circuits
./build.sh # Compile circuit + trusted setup
# Prover Service (optional)
cd prover-service
npm install
npm start # http://localhost:3001dark-oracle/
├── contracts/ # Solidity smart contracts (Foundry)
│ ├── src/
│ │ ├── DarkOracleMarket.sol # Core market lifecycle + parimutuel payouts
│ │ ├── DarkOracleVerifier.sol # Groth16 BN254 on-chain verifier
│ │ ├── MockUSDC.sol # Testnet ERC-20 (6 decimals)
│ │ └── interfaces/ # IDarkOracleVerifier
│ ├── test/ # 87 unit tests (69 market + 18 verifier)
│ └── script/ # Foundry deploy scripts
├── circuits/ # Circom ZK circuit
│ ├── resolution_proof.circom # 771 constraints, 3-source threshold proof
│ └── build.sh # Compile + trusted setup + test
├── prover-service/ # Express + snarkjs REST API
│ ├── src/ # /health, /prove endpoints
│ ├── test/ # 7 integration tests
│ └── Dockerfile # Container deployment
├── workflows/ # Chainlink CRE resolution workflow ★
│ └── dark-oracle-resolve/
│ ├── main.ts # 7-step pipeline (447 lines) ★
│ ├── config.staging.json # Data sources + contract addresses
│ └── workflow.yaml # CRE deployment config
├── frontend/ # Next.js 16 React dApp
│ └── src/
│ ├── app/ # 5 routes (/, /create, /market/[id], /market/[id]/proof)
│ ├── hooks/ # 11 wagmi hooks
│ ├── components/ # MarketCard, PositionForm, ClaimButton, ProofInspector
│ ├── config/ # Contract ABIs + addresses
│ └── lib/ # Formatting + utility helpers
├── video/
│ └── output/
│ └── dark-oracle-demo-v4-full.mp4 # 5:12 narrated demo
├── Dockerfile # Cloud Run deployment
└── project.yaml # CRE global config
| Component | Tests | Status |
|---|---|---|
| Smart Contracts (Forge) | 87/87 | PASS |
| Frontend TypeScript (tsc --noEmit) | 0 errors | PASS |
| Frontend ESLint | 0 errors | PASS |
| Frontend Production Build | 5 routes | PASS |
| E2E Lifecycle (Base Sepolia) | 11/11 steps | PASS |
| Groth16 Verification (on-chain, bypass=false) | Valid proof: true | PASS |
| Groth16 Negative Test (invalid proof) | Returns false | PASS |
| Prover Service (proof gen) | ~800ms | PASS |
| CRE Workflow Simulation | Compile + simulate | PASS |
Watch the full narrated demo (5:12)
The video is split into two sections:
Part 1 — Frontend Walkthrough (0:00–3:11)
- Landing page with live market cards and implied probability prices
- How It Works — 4-step resolution pipeline visualization
- Create Market with 3 independent data sources
- Resolved market with on-chain proof hash
- Proof Inspector — ZK public inputs, proof hash, and live on-chain re-verification (236k gas)
Part 2 — CRE Workflow Simulation (3:11–5:12)
cre workflow simulaterunning all 7 pipeline steps in terminal- Live API calls to CoinGecko, Binance, Coinbase
- Consensus calculation and threshold comparison
- Groth16 proof generation (1,176ms)
- DON report signing (ECDSA + keccak256)
- On-chain write with verifyProof() — ecAdd, ecMul, ecPairing
Dark Oracle uses 7+ Chainlink CRE capabilities across a single resolution workflow:
| # | CRE Capability | Usage |
|---|---|---|
| 1 | CronTrigger | Hourly resolution checks for pending markets |
| 2 | HTTPClient (source 1) | Fetch BTC price from CoinGecko API |
| 3 | HTTPClient (source 2) | Fetch BTC price from Binance API |
| 4 | HTTPClient (source 3) | Fetch BTC price from Coinbase API |
| 5 | HTTPClient (prover) | POST to external ZK prover for Groth16 proof generation |
| 6 | EVMClient (read) | Call getPendingResolutions() on DarkOracleMarket |
| 7 | Consensus | DON-wide agreement on fetched data via report signing |
| 8 | Report Signing | DON cryptographic signature on encoded report |
| 9 | writeReport | Deliver signed report to onReport() via KeystoneForwarder |
1. CronTrigger fires (hourly)
2. EVMClient reads getPendingResolutions() from contract
3. HTTPClient fetches prices from 3 independent APIs
4. Calculate consensus: average, threshold comparison, confidence score
5. HTTPClient POSTs to external ZK prover → receives Groth16 proof
6. ABI-encode report: [marketId, outcome, resolvedValue, timestamp,
confidence, sourceCount, proofA, proofB, proofC, publicInputs]
7. DON signs report → writeReport → onReport() on-chain
QuickJS (CRE's WASM sandbox) cannot run nested WASM modules. snarkjs requires WASM for proof generation. Solution: external prover microservice called via HTTPClient, with self-verification before returning the proof.
The ZK circuit (resolution_proof.circom, 771 constraints) creates a Groth16 proof that binds together:
| Public Input | Purpose |
|---|---|
marketId |
Which market this resolves |
outcome |
YES (1) or NO (0) |
dataSourcesHash |
Poseidon hash of source IDs — proves which data was used |
resolutionTimestamp |
When the data was fetched |
1. CRE nodes fetch prices from 3 independent APIs
2. Consensus determines outcome: average >= threshold → YES, else → NO
3. External prover generates Groth16 proof (~800ms)
- Binds: marketId + outcome + sources + timestamp
- Uses Poseidon hashing for source commitment
- Threshold comparison: sum >= threshold * numSources (avoids field division)
4. Proof delivered on-chain in DON-signed report
5. DarkOracleVerifier.sol verifies BN254 elliptic curve pairing
- ecAdd (precompile 0x06)
- ecMul (precompile 0x07)
- ecPairing (precompile 0x08)
- Cost: 236,483 gas
6. Both DON signature AND ZK proof must pass → market resolves
7. proofHash stored on-chain permanently — anyone can re-verify
If the proof is invalid, the pairing check fails and the market does not resolve. Tested on-chain: valid proofs return true, modified proofs return false.
| Dark Oracle | Polymarket | Kalshi | |
|---|---|---|---|
| Resolution trust | Cryptographic (ZK proof) | Economic (dispute/vote) | Institutional (CFTC) |
| Proof of honesty | On-chain ZK proof (verifiable) | Economic bonds (challengeable) | Legal filings (trust-based) |
| Resolution speed | Automatic at resolution time | Hours-days (dispute window) | Hours (manual) |
| Verifiability | Anyone, on-chain, any time | UMA token holders only | CFTC filings only |
| Liquidity | Testnet MVP | Deep ($B+ volume) | Deep (regulated) |
| Market types | Binary YES/NO | Binary + multi-outcome | Binary events |
| Order book | Parimutuel pool | CLOB (limit orders) | CLOB |
Dark Oracle leads on trust model, resolution speed, and public verifiability. Competitors lead on liquidity, market variety, and regulatory status.
Full create -> bet -> resolve -> verify -> claim tested on-chain:
| Step | Description | Gas |
|---|---|---|
| 1 | Create market | 151,971 |
| 2 | Mint testnet USDC | 34,180 |
| 3 | Approve USDC | 29,231 |
| 4 | Place YES position (10 USDC) | 116,837 |
| 5 | onReport (DON-signed resolution) | 124,115 |
| 6 | Claim winnings (+10 USDC returned) | 56,484 |
All on-chain state verified: auto-transition (H-5), public input binding (C-2), outcome mapping (H-4), payout math.
- ReentrancyGuard on
claimWinnings(OpenZeppelin) - Collateral whitelist — only approved tokens accepted
- Public input binding —
publicInputs[0]must matchmarketId,publicInputs[1]must match outcome - Custom errors — 22 gas-efficient error types (no string reverts)
- Emergency resolution — owner-only 24h-delayed fallback
- Position close guard — rejects positions after resolution time
- Proof self-verification — prover service verifies proof locally before returning
- Code review: 6 audit rounds, all Critical and High findings resolved
| Hackathon | Chainlink Convergence 2026 |
| Track | Prediction Markets |
| Deadline | March 1, 2026 |
| Differentiator | Only submission combining ZK proofs + Chainlink CRE for verifiable market resolution |
All Chainlink CRE integration code:
| File | Description |
|---|---|
workflows/dark-oracle-resolve/main.ts |
Primary CRE workflow — 7-step resolution pipeline (447 lines) |
workflows/dark-oracle-resolve/workflow.yaml |
CRE workflow settings (staging/production) |
workflows/dark-oracle-resolve/config.staging.json |
Data sources, contract addresses, prover URL |
workflows/dark-oracle-resolve/package.json |
@chainlink/cre-sdk v1.0.9 dependency |
project.yaml |
CRE global project configuration |
secrets.yaml |
CRE secrets configuration |
MIT