Design Decisions

Key architectural choices and why they were made.

FBA over Continuous Orderbook

Decision: Frequency Batch Auctions, not continuous matching.

Why: On EVM, continuous orderbooks create MEV extraction opportunities — bots race for time priority, sandwich trades, and front-run large orders. FBA eliminates intra-batch time priority and gives everyone the same clearing price. The trade-off is ~3s latency per batch, which is acceptable for prediction markets where positions are held for minutes, not milliseconds.

Claim-Based Settlement over Inline Fills

Decision: clearBatch() stores aggregate results; individual fills are claimed separately.

Why: Writing fills per-order during clearing would make gas cost proportional to order count — O(n) storage writes at 5,000-20,000 gas each. Claim-based settlement keeps clearBatch() at O(1) writes regardless of book depth. Users claim their fills in a second transaction using deterministic math against the stored batch result.

Pyth price over ema_price

Decision: Use spot price for settlement, not exponential moving average.

Why: Spot price gives clean, direct market semantics. A market asking "Will BTC be above $X at time T?" should resolve on the actual price at T, not a smoothed average. EMA would introduce lag and make resolution prices diverge from what traders observe on exchanges.

Trading Halt at Final Batch

Decision: Stop accepting orders when timeRemaining < batchInterval. No midpoint lock.

Why: The original PoC used a halfway-point lock (trading stops at 2.5 min in a 5-min market). This was arbitrary and wasted half the market duration. The new rule is simpler and maximizes trading time: the book stays open until the last complete batch can clear, then halts. Deterministic and fair.

ERC-1155 for Outcome Tokens

Decision: Multi-token ERC-1155, not ERC-20 per outcome.

Why: Deploying two ERC-20 contracts per market is expensive and creates address management overhead. ERC-1155 uses a single contract with deterministic token IDs (marketId * 2 for YES, marketId * 2 + 1 for NO). Cheaper deployment, simpler accounting, and tokens are still freely transferable.

Segment Tree for Price Aggregation

Decision: Segment tree over naive iteration or Fenwick tree.

Why: Clearing requires finding where cumulative bid and ask volumes cross. Naive iteration over 99 ticks costs O(99) storage reads. A segment tree provides O(log 99) ≈ O(7) operations for both updates and prefix sum queries. Clober's LOBSTER research validates this approach for on-chain orderbooks with segmented trees that minimize SSTORE operations.

Finality Gate on Resolution

Decision: Two-step resolution with a finality window (n+2 blocks).

Why: A single-transaction resolution on BSC could theoretically be reorganized, changing the market outcome. By splitting into resolveMarket() (submit data) and finalizeResolution() (after finality), we ensure the settlement price is economically final. The challenge window during finality lets anyone submit a better (earlier) Pyth update, enforcing the deterministic "earliest update wins" rule.

Open Submission by Default

Decision: Orders submitted publicly on-chain; private submission optional.

Why: Private transaction channels (BEP-322, NodeReal bundles) introduce infrastructure dependencies and trust assumptions. Open submission is the simplest, most composable baseline. Professional traders who want MEV protection can opt into private channels — the protocol works correctly either way.

BNB Chain over opBNB

Decision: Deploy on BSC mainnet, not opBNB L2.

Why: Gas is already cheap on BSC (~$0.008/order at 0.05 gwei). opBNB adds bridge friction (users must bridge from BSC), has a smaller ecosystem, charges nontrivial Pyth fees, and lacks the MEV infrastructure (BEP-322, bundle APIs) documented for BSC. The marginal gas savings don't justify the UX and ecosystem trade-offs.

Last updated