Nyx
X
Whitepaper

How Nyx works

The rules, the contracts, and exactly what is private and what is public.

Deployed contracts

ContractAddress
hasher0xa7744B83495bEde25EdC03575915e05d60f30E87
verifier0x75CB7C620d8652c9091fcEAA6CddB4abef7F6F52
oracle0x67795378c74fcc2c3dEa75D08D622c3aEA173e51
factory0xDB7136C0C4FCEa61462A951BC1859Ef017E5e2d8
notePool_0_10x5c2330398d579adc4f20D21Bf8a3303DC57b6CfD
notePool_10x469B888604A3d2f2Ff0bB053E7428A7F81463AF1
notePool_100x56a42Ec8cD8C9588AaFDe6F84a04c174143617d9
marketImpl0x8c1B57e184fCA6A98118B86de201924674b7b017

Everyone sees the odds move. Nobody sees who bet or how much each person bet.

Deployed contracts

See the table at the top of this page.

(This list is filled at runtime from the deploy record in MongoDB; every address links to the Elysium explorer.)

How it works

1. A user deposits a fixed amount (0.1, 1 or 10 HYPE) into a NotePool from any wallet and keeps a secret note (nullifier, secret). The pool only stores commitment = Poseidon(nullifier, secret) as a leaf of a depth-20 Merkle tree.

2. To bet, the user's browser builds a Groth16 proof that it knows a note in the tree (public inputs: root, nullifierHash = Poseidon(nullifier), extDataHash). extDataHash binds the proof to the target market, the exact bet calldata, the relayer and the relayer fee, so nobody can redirect it.

3. The proof goes to the relayer (POST /api/relay/bet), which checks it with eth_call and sends it from the app's keeper wallet. The pool marks the nullifier spent, pays the relayer fee, and calls Market.bet(outcome, positionOwner) with the rest of the note. positionOwner is a fresh per-bet key made in the browser; it never holds gas.

4. After resolution, the position owner signs an EIP-712 Claim (or Refund) to any fresh recipient address. The relayer submits it (POST /api/relay/claim) and is paid the signed relayer fee from the payout.

5. Unused notes can be withdrawn to any address with a proof (POST /api/relay/withdraw).

Rules (match the deployed code)

RuleValueWhere
Note denominations0.1, 1, 10 HYPE (one pool each)NotePool.denomination
Merkle tree depth / root history20 levels / last 30 roots acceptedNotePool.levels, ROOT_HISTORY_SIZE
Relayer feeone fixed fee per denomination (0.1 → 0.001, 1 → 0.002, 10 → 0.005 HYPE), published in /api/pools; the relayer refuses any other fee, including 0. On-chain it is bound in the proof and must be < denominationsrc/relayer.js, NotePool.spend
Stake per notedenomination − relayer feeNotePool.spend → Market.bet
Who can betonly a NotePool created by the factory (pool factory() is immutable), until deadline − 10 min (bets close before sampling starts)Market.bet
Market kindsABOVE (0 = YES mcap ≥ target, 1 = NO), HYPE_BRACKET (outcome i = number of edges ≤ price), RACE (coin with highest median mcap in HYPE, no USD oracle; ties → lowest index)Market
Max outcomes8MAX_OUTCOMES
Duration1 hour to 30 days from creationMarketFactory.MIN_DURATION/MAX_DURATION
Template markets (anyone)Ascend coins (WHYPE pair) or Montra coins (native HYPE pair), target $1k–$10B, pool liquidity ≥ 1e23createFromTemplate
Keeper marketsdaily: 2x, 5x and $1M market-cap targets on the top active coins (up to 2 Montra and 2 Ascend; HYPE, WHYPE or KNTQ pairs; KNTQ priced through the HYPE/KNTQ pool 0xc96bfd21…), plus daily HYPE/USD brackets. The keeper only spends gas sampling and resolving its own markets, or other markets with stakes on ≥ 2 outcomessrc/keeper.js
Coin market capStateView.getSlot0(poolId) sqrtPriceX96 → coin price in pair (→ HYPE through the quote pool for KNTQ pairs) × totalSupply(); × HYPE/USD for ABOVEMarket.currentValues
HYPE/USDposted by the keeper from Hyperliquid allMids (trusted keeper post), at most one post per 60 s and at most ±10% vs the previous post (larger real moves are followed in steps); raw response stored and served on the market endpointHypeOracle.post
Oracle freshnessa sample needs a post younger than 5 minutesORACLE_MAX_AGE
Samplingtwo steps, from deadline − 10 min to deadline + 10 min: poke() records current values, then sample() at least 5 s and 1 block later (within 60 s) stores the average of the poked and current values. Two tracks: calls from the keeper go to the keeper track, everyone else to the public track. Per track: one pending poke, ≥ 60 s between samples, max 32 samplesMarket.poke/sample, POKE_MIN_DELAY, POKE_TTL, SAMPLE_INTERVAL, MAX_SAMPLES
Resolutionanyone, after deadline + 10 min; uses the median (lower middle) of the keeper track if it has ≥ 3 samples, else of the public track if that has ≥ 3Market.resolve
Payoutstake + stake × (losing pools × 98%) / winning pool (rounded down)Market.claimable
Feelosing pools − floor(losing pools × 9800 / 10000) (≈ 2%), sent to the treasury at resolution; if the push fails, anyone can call collectFee()FEE_BPS = 200
One side empty / winning pool emptyeveryone refunded in full, no feeMarket.resolve
Timeoutif not resolved by deadline + 3 days and no track has ≥ 3 samples, anyone calls enableRefund(); full refunds, no fee (with enough samples, anyone can still call resolve())REFUND_DELAY
ClaimsEIP-712 signature by the position owner (domain "QuietMarket", v1), submitted by anyone; replay rejected. Payout to any address, or claimToNotes: whole notes deposited into a note pool plus the remainder to an addressMarket.claim/claimRefund/claimToNotes
Rounding dustgoes to the treasury after every winner has claimed; after resolution + 365 days only balance − outstanding() can be sweptMarket.sweepDust
Admin powersno owner, no pause, no upgrade, and no key can move funds out of pools or markets. The keeper can create markets and post HYPE/USD within the bounds above, which decides HYPE_BRACKET markets and the USD conversion of ABOVE markets (see the keeper trust section below)all contracts

Relayed bets and claims are broadcast after a random 0–20 s delay.

Zero house capital: every payout comes from other players' stakes. Total payouts can never exceed the stakes minus the fee.

What is private

What still leaks (by design or by limitation)

Public on-chain

Visible to the relayer operator and its host

Proof freshness (root history)

Fixed relayer fees

Trust in the keeper (HYPE/USD)

Coin price samples

Treasury and buyback

Fees accrue to the app-owned treasury wallet. When TOKEN_CA is set (and only then), an hourly keeper job spends the treasury's HYPE above a 0.01 HYPE gas reserve to buy TOKEN_CA on-chain. The treasury keeps the bought tokens. Nothing runs if less than 0.005 HYPE has accrued.