Encrypted prediction markets on Solana

Predict.
Stay private.
Win big.

The first prediction market where every position is encrypted until settlement. Nobody can see your bet — not even us. Powered by Arcium MPC + ZK proofs on Solana.

3
Market types
$1–$100
Accuracy tiers
100%
Pre-settlement private
ZK
Verified payouts

Three ways to play

Each market type has different stake rules and payout math. Same privacy guarantee across all — your prediction is encrypted the moment you submit it. All bets go into a single unified pool per market — the pool address reveals nothing.

01
Binary marketvariable stake

Yes or No

“Will BTC close above $100k on Friday?”

Encrypt: { side: 0 or 1 }
Stake: any amount — bigger stake = bigger share
Payout: stake_net + (stake_net / winner_pool) × loser_pool
Unified pool — side hidden
02
Accuracy marketfixed tiers

Closest wins

“What will SOL price be on Jan 15?”

Encrypt: { value: predicted_number × 1000 }
Cutoff: median error — top ~50% win (strict <)
Weight: (SCALE/(SCALE+rel_err))^6
Single pool per tier — value hidden
$1
$10
$100
03
Multi-outcomevariable stake

Pick the winner

“Who wins Champions League 2025?”

Encrypt: { outcome_index: 0-3 }
Stake: any amount — minority wins most
Payout: winning pool splits all losing pools proportionally
Unified pool — outcome hidden
BINARY + MULTI — VARIABLE STAKES
Bet $5, $50, or $500. Bigger stake on winning side = bigger share of loser pool. 2% fee on total: 1.5% creator + 0.5% protocol.
ACCURACY — FIXED TIERS ONLY
Everyone in the same tier pays the exact same fee. Stake size has zero effect — only prediction accuracy determines payout. 20% platform fee on loser pool.

Accuracy market.
Three isolated lobbies.

Accuracy markets only. Same question, separate pools. $1 bettors compete against $1 bettors. Skill wins — not wallet size.

$1
Micro lobby
Low stakes, high volume
Learn the platform
Isolated from $10/$100
1_000_000 USDC (6 decimals)
$10
Standard lobby
Core accuracy product
Deepest participation
Most active pools
10_000_000 USDC (6 decimals)
$100
Whale lobby
High conviction
Bigger prize pool
Isolated from lower tiers
100_000_000 USDC (6 decimals)

From bet to payout

Every step for all three market types. What the user sends, what goes on-chain, what the backend stores, what the contract executes.

YesNo + MultiOutcome: 1 unified pool · Accuracy: 3 pools (one per tier $1/$10/$100) · Pool address reveals nothing about prediction
👤 creator — admin setup (once)
// run once at deploy
await program.methods.initialize(50, 150, 2000).rpc()
//   50  = 0.5% protocol fee (YesNo/Multi on total pool)
//   150 = 1.5% LP fee      (YesNo/Multi on total pool)
//   2000 = 20% accuracy fee (accuracy on loser pool only)
await program.methods.initYesnoCompDef().rpc()
await program.methods.initMultioutcomeCompDef().rpc()
await program.methods.initAccuracyCompDef().rpc()
// YesNo market
👤 creator — YesNo
await program.methods.createMarketGroup(MarketType.YesNo, MarketCategory.Crypto,
  "Will BTC close above $100k on Friday?",
  OracleType.Pyth, lock_timestamp, resolve_deadline, []
).rpc()                                           // locks $10 bond
await program.methods.createFlatMarket().rpc()    // bet_size=0, variable stakes
await program.methods.createPool(0, PoolType.Unified).rpc() // ONE pool — all bettors
// MultiOutcome market
👤 creator — MultiOutcome
await program.methods.createMarketGroup(MarketType.MultiOutcome, ...,
  outcome_labels: ["Real Madrid","Arsenal","Bayern","PSG"]).rpc()
await program.methods.createFlatMarket().rpc()    // same as YesNo
await program.methods.createPool(0, PoolType.Unified).rpc() // ONE pool — all outcomes
// Accuracy market
👤 creator — Accuracy
await program.methods.createMarketGroup(MarketType.Accuracy, ...,
  "SOL price at Friday close?").rpc()
// 3 tier markets — each has fixed bet_size, called 3 times
await program.methods.createTierMarket(Tier.Micro).rpc()    // $1  bet_size=1_000_000
await program.methods.createTierMarket(Tier.Standard).rpc() // $10
await program.methods.createTierMarket(Tier.Whale).rpc()    // $100
// each tier gets its own pool — user picks tier, NOT prediction
await program.methods.createPool(0, PoolType.Accuracy).rpc() // $1 pool
await program.methods.createPool(0, PoolType.Accuracy).rpc() // $10 pool
await program.methods.createPool(0, PoolType.Accuracy).rpc() // $100 pool
⛓ on-chain after creation
MarketGroup PDA  status: Open  bond: $10 locked

Pool PDA  seeds: ["pool", market, 0]
  pool_type:  Unified        // same for all bettors — side hidden
  vault:      UNIFIED_VAULT  // all USDC escrowed here
  total_staked: 0   participant_count: 0   status: Open
🤖 backend DB
INSERT INTO markets (pubkey, question, type, status, lock_timestamp, creator)
VALUES ('GROUP_PK', 'Will BTC close above $100k?', 'yesno', 'open', 1748890000, 'CREATOR')
📱 frontend GET /markets
{ question: 'Will BTC close above $100k?', status: 'open', total_volume: '$0' }
// UI: "Betting open · locks in 2h 14m · $0 in pool"
// no YES/NO split — sides are private until settlement

Every moving part

All 25 on-chain instructions, the complete backend surface, and every account the protocol creates.

frontendbackendarciumadminanyone
admin — run once at deploy (4)
#instructioncallerwhat it does
1initializeadminCreates global CyperMarket PDA. Sets fee bps, treasury, authority.
2init_yesno_comp_defadminRegisters YesNo equality-check circuit with Arcium.
3init_multioutcome_comp_defadminRegisters MultiOutcome circuit.
4init_accuracy_comp_defadminRegisters Accuracy error circuit.
market creation — creator via frontend (5)
#instructioncallerwhat it does
5create_market_groupfrontendCreates MarketGroup PDA. Locks $10 USDC bond. Sets question, oracle, timestamps.
6create_flat_marketfrontendYesNo + MultiOutcome only. Creates Market PDA with bet_size=0 (variable stakes). Called once per group.
7create_tier_marketfrontendAccuracy only. Called 3× (Micro/Standard/Whale). Each creates Market PDA with fixed bet_size.
8create_poolfrontendCreates Pool PDA + USDC vault. YesNo/Multi: 1 unified pool. Accuracy: 1 per tier = 3 calls.
9cancel_marketfrontendCreator cancels before any bets placed. Returns bond. Only if participants == 0.
betting — user via frontend (2)
#instructioncallerwhat it does
10place_betfrontendYesNo + MultiOutcome. User passes encrypted_payload + stake_amount. USDC → vault. Creates Position PDA.
11place_bet_accuracyfrontendAccuracy only. No stake param — enforces exact tier bet_size. Creates Position PDA.
lifecycle — backend + permissionless (3)
#instructioncallerwhat it does
12lock_marketanyoneCloses betting after lock_timestamp. Backend cron calls first. Any user can call as fallback.
13post_resolutionbe/fePyth: oracle service signs. Manual: creator signs from frontend. Starts 1hr dispute window.
14init_settlement_registrybackendCreates SettlementRegistry PDA after dispute window. Sets total_shards = ceil(count/8).
settlement — backend queues, arcium executes (6)
#instructioncallerwhat it does
15queue_settlement_yesnobackendSends one shard (≤8 encrypted sides) to Arcium. Called N times in parallel. One per shard.
16queue_settlement_multioutcomebackendSame pattern as YesNo. Passes encrypted outcome indices. All shards in parallel.
17queue_settlement_accuracybackendSame pattern. Circuit outputs errors not winner flags.
18settle_yesno_callbackarciumAuto-called by MXE. Verifies ZK proof. Emits winner_mask. Updates registry. Sends LP+protocol fees.
19settle_multioutcome_callbackarciumIdentical to YesNo callback.
20settle_accuracy_callbackarciumEmits errors (not winner flags). Updates registry. No fees — sent via accuracy_send_fees.
payout + cleanup (5)
#instructioncallerwhat it does
21accuracy_send_feesbackendAccuracy only. Sends 20% platform fee of loser pool to treasury after backend computes loser count.
22write_position_payoutbackendWrites computed payout to one Position PDA. Called once per position. Idempotent — safe to retry.
23claim_payoutfrontendUser pulls USDC from pool vault to wallet. Only works if position.status == Settled.
24return_bondfrontendCreator reclaims $10 bond after group.status == Settled.
25slash_bondanyonePermissionless. Sends bond to treasury if creator missed resolve_deadline.

What lives where

Four layers. Solana handles money and state. Arcium decrypts only what needs to be secret. Backend handles coordination and math. Your device handles encryption.

// hybrid architecture — Arcium equality checks only
user device + oracle
User wallet
Encrypts prediction with MXE pubkey. Never sent as plaintext.
Oracle
Pyth · Chainlink · manual. Dispute window. Multisig 3 of 5.
↓ encrypted tx                              ↓ resolved_value
solana on-chain — trustless · immutable
Market + Position PDA
status · stake · encrypted payload
USDC vault
escrow · program-owned
Oracle result PDA
resolved_value · dispute window
Settlement Registry PDA ★ new
total_shards · settled_shards · status: InProgress → Finalizing
Arcium mempool + Settlement IX
queues shard jobs · verifies ZK proof · updates registry
↓ queue N shard jobs parallel         ↑ winner flags (callback)         ↑ write payouts
ARCIUM MXE
Equality checks only ⚡ changed
was full scoring. now: is_winner = prediction == outcome?
Winner flags out
is_winner[8] · ZK proof verified on-chain
BACKEND
Payout math ★ new
public stakes + winner flags → proportional payouts
Shard coordinator ★ new
groups positions into shards · fires all jobs in parallel
🧑 Creator — frontend
create_market_group
Posts $10 bond. Creates event. Then: create_flat_market or create_tier_market×3.
👤 User — frontend
place_bet / place_bet_accuracy
Encrypts prediction. Variable stake (Binary/Multi) or fixed fee (Accuracy). All go to unified pool.
⏰ Anyone — permissionless
lock_market
After lock_timestamp. Backend cron calls first.
🔮 Oracle / Creator
post_resolution
Pyth or manual. Starts 1hr dispute window.
🤖 Backend (after dispute)
init_settlement_registry
total_shards = ceil(N/8). Creates registry PDA.
★ new
🤖 Backend (parallel)
queue_settlement_* × N
All shards fire simultaneously. Each = 8 encrypted positions → Arcium.
🔐 Arcium — auto callback
settle_*_callback
ZK verified. Returns winner_mask. Updates registry. When all shards done → Finalizing.
🤖 Backend — payout math
write_position_payout
Public stakes + winner_mask → payout per position. 1 tx each.
★ math in backend
👤 User — frontend
claim_payout
USDC vault → wallet. Works for all 3 market types.

How your bet stays private

Three steps. Encrypted client-side. Arcium checks if it matches outcome. Backend computes payouts from public data.

01 — Your device
prediction: YES
stake: $50
plaintext in browser only
never sent unencrypted
02 — On-chain (Position PDA)
8f4a2c9e1b7d3f5a0e6c8b4a2f9d1e3b7c5a0f2e4d8b6c3a1f9e7d5b3c1a9f...
encrypted blob on-chain
stake=$50 public · prediction hidden
03 — Arcium MXE
is_winner: true ✓
ZK proof: verified
equality check only
no payout math in circuit
ALWAYS PRIVATE
Your prediction (YES/NO/number/outcome)
Which side you chose — before settlement
Your entry timing
ALWAYS PUBLIC
Your stake amount (BetPlaced event)
Pool participant count + total volume
Winner/loser status — after settlement only
ARCIUM GUARANTEE
MPC: key shards split across nodes
ZK proof: computation verifiable
Even Cypher cannot read your prediction

The math

Binary and Multi: 2% fee on total pool. Accuracy: 20% platform fee on loser pool only. Try every formula in the math simulator →

Binaryvariable stakes
fee = total × 0.02
net = total − fee
winner_pool = Σ stake_net (winners)
loser_pool = Σ stake_net (losers)

// bigger stake = bigger share:
payout_i = stake_net_i
    + (stake_net_i / winner_pool) × loser_pool
Accuracyfixed tiers
error_i = |predict_i − actual|
sort → median = sorted[floor((N+1)/2)]

won_i = error_i < median (strict)
loser_pool = losers × F
platform_fee = loser_pool × 0.20
prize = loser_pool − platform_fee

w_i = (SCALE/(SCALE+rel_err_i))^6
payout_i = F + (w_i / Σw) × prize
Multi-outcomevariable stakes
2 to 4 outcomes — same formula as Binary

// winning pool takes all losers:
loser_pool = Σ all non-winning stakes (net)

payout_i = stake_net_i
    + (stake_net_i / winner_pool) × loser_pool

// minority wins more

Common questions

Why can't anyone see other people's bets?+

Every prediction is encrypted with the Arcium MXE public key client-side in your browser before it's submitted. The encrypted blob is stored on-chain. Nobody — not other users, not validators, not even Cypher — can decrypt it without the MXE's private key shards, which are split across multiple MPC nodes and only combined inside the secure computation at settlement.

What stops a creator from posting a wrong resolution?+

Three things: a $10 USDC bond (slashed if they misbehave), a 1-hour dispute window after any resolution, and Pyth on-chain price feeds for crypto markets which require no human input at all. For custom markets, the bond + dispute window is the protection layer.

How many outcomes can a multi-outcome market have?+

Between 2 and 4 outcomes in v1. All bettors go into one unified pool — the pool address reveals nothing about which outcome you picked. Only Arcium decrypts the outcome at settlement.

Can I bet any amount on binary and multi-outcome markets?+

Yes — binary and multi-outcome markets have variable stakes. You choose any amount above a small minimum. The more you stake on the winning side, the larger your proportional share of the loser pool. Accuracy markets are different — they use fixed entry fees per tier ($1/$10/$100) so skill, not wallet size, determines your payout share.

What token is used?+

USDC only, across all market types. Stable value means your $10 bet is worth $10 at settlement. If you only have SOL, swap on Jupiter first — one click, 5 seconds.

How does settlement scale to many users?+

Settlement uses parallel sharding. Positions are grouped into shards of 8. Each shard runs as a separate Arcium job simultaneously. A SettlementRegistry on-chain accumulates results as jobs complete. For 10,000 users that's 1,250 parallel jobs — wall-clock settlement time stays roughly constant.

What happens if nobody bets on the winning side?+

Edge case handled — if winner_count is 0, the entire net pool goes to protocol treasury to prevent permanent fund lock. For accuracy markets this cannot happen — the top ~50% always win by definition of the median cutoff.

Is the contract upgradeable?+

Yes, with a multisig upgrade authority. No single key can upgrade the program. All accounts have reserved padding bytes so upgrades don't require migrating existing market data.

How do I run the project locally?+

Install Bun from bun.sh, then: bun install && bun run dev. The math simulator runs at localhost:3000 and the site at localhost:3000/site. No environment variables needed for the simulator.