Search projects, hackathons, sponsors and tech

    ← All projects

    Horizon

    A prediction market humans and AI agents pay to ask: curve orders, shared USDC liquidity, no fees.

    Demo

    About

    Every AI model is trained on the past; the data that matters most is about the future, and nobody has it yet. Prediction markets are where people put real money on what happens next. Horizon is a binary prediction market on Ethereum Sepolia built so that a person or an AI agent can ask the world any question and pay for the answer. Creation is a paid service over Hedera x402; a verified World credential tells humans and agents apart, at half price; 1inch Aqua puts real USDC behind every quote, with one balance shared across markets; executable pricing curves let people and agents rest orders that reprice themselves; The Graph and Substreams keep the page true to the chain; and Hedera Consensus Service records the whole creation flow. The rest of this description is how each of those works. Makers publish executable pricing curves instead of resting single-price orders. A curve is a start price, an end price and a size; the chain evaluates it on every fill, so a BUY curve declines as the maker accumulates and a SELL curve rises as inventory leaves. Equal endpoints are just a fixed-price limit order. Fills are partial, exactly accounted, and cancellable at any time. One USDC balance backs quotes in every market at once. Maker funds sit in the wallet and are drawn through 1inch Aqua only at fill time, so a small maker can express views across several markets without pre-funding each one. Within a market, an on-chain per-market budget stops a maker's resting orders from ever committing more than the wallet can currently spend. Markets bootstrap themselves: a YES buyer paying 0.60 USDC and a NO buyer paying 0.40 USDC mint a fully backed pair in one transaction. Nobody has to seed inventory. An off-chain allocator picks up to four fills across direct and complementary legs, and RouteExecutor settles the whole route atomically or reverts it. Stale state, drained balances and slippage produce a clean rejection, never a partial trade. Trading carries no fee of any kind: no maker, taker, routing or protocol fee. The only charge is market creation, offered as a paid service to people and agents alike. A drafting step grounded on live indexed markets from The Graph proposes the question, outcomes, close time and resolution rules and warns about duplicates by market id; the requester approves a hash of the draft before anything is charged. Payment runs over Hedera x402, settled through Blocky402, and is resumable: a retried request never charges twice and never creates two markets. A verified World credential halves the price, at most once per credential per day. Every approved draft, settled payment and deployed market is then stated on a Hedera Consensus Service topic, in order, with a consensus timestamp anyone can read back from a mirror node. Markets, curves, fills, routes, collateral and resolutions are indexed by a Subgraph, mirrored into PostgreSQL for fast reads, and overlaid by a Substreams-fed live layer so a fill or a new market shows up in the open browser tab seconds after its block, before the indexer has it. Several binary markets can be grouped as an event under one review and one payment, optionally importing definitions (question, outcomes, criteria, dates; never prices or liquidity) from a Polymarket page. After close, a disclosed resolver submits YES, NO or INVALID once, with an on-chain evidence reference. Winning tokens redeem 1 USDC; INVALID pays 0.50 per token. Resolution and creation authority are deliberately centralized and stated in the UI; neither role can spend from a user wallet, withdraw collateral or mint unbacked outcomes. Demonstrated live: an atomic two-curve route on Sepolia and its indexed result, an agent completing a paid creation end to end (1 HBAR on Hedera testnet, then a market on Sepolia, then indexed by The Graph), a human verified through World Selfie Check paying the discounted 0.5 HBAR, and the creation audit trail on HCS topic 0.0.10473191 (62 statements at submission) verified byte for byte against the mirror node.

    How it's made

    Contracts (Solidity 0.8.30, Foundry, Cancun). Horizon extends the pinned, unmodified official 1inch Aqua and SwapVM sources; all 329 vendored files are hashed in vendor-lock.json and re-verified in CI. HorizonSwapVM overrides SwapVM's _runOpcode hook to add two application-local instructions in the reserved opcode range, 0xf0 (fixed-price BUY) and 0xf1 (curve). Each reconstructs and hashes the maker's canonical Aqua order against the MarketRegistry, refuses any order the router has not admitted, prices it (the curve through CurveMath: an exact rational integral of p(x) = start + (end - start) * x^alpha, alpha in {1,2,3}, BUY rounds down, SELL rounds up) and tracks filled shares. RouteExecutor performs up to four fills per transaction with a spend limit, a deadline and full rollback; for complementary legs it escrows 1 USDC per pair into BinaryMarket inside SwapVM's pre-transfer-in callback and mints YES to one side and NO to the other. OrderBudget is a ledger the router deploys and owns. The Foundry suite is 54 tests including three fuzz properties, and an Anvil test cross-checks 44 contract vs. TypeScript curve results. Backend (Node 24, TypeScript, Express, Prisma, PostgreSQL 18, pg-boss). The API returns prepared unsigned transactions only; the worker is the sole process holding a key, for createMarket and resolve. Every quote re-reads balances, allowances, fill counters and market status over RPC at a single block, rejects an indexed snapshot more than 64 blocks behind or on a reorged hash, and simulates the full route before returning calldata. AdminJS sits on top for record inspection. Drafting goes through a MarketDraftProvider interface with an Anthropic-backed provider and a deterministic development provider; both read the same live Graph data. The Graph. A Subgraph in Studio (horizon/0.3.2) indexes markets, curves, fills, routes, collateral and resolution, and validates Aqua Shipped bytes against the deployed router rather than trusting them. On top of that a Substreams package (horizon_events, Rust compiled to WASM) extracts the same events per block for a stream consumer that writes a live overlay into Postgres, one block per transaction with the cursor included, and wakes the API through pg_notify. The API exposes a POST /api/live SSE endpoint with Last-Event-ID replay, and reads apply only live changes whose block is newer than the snapshot's, so a lagging sweep can neither hide a market nor duplicate a fill. BlockUndoSignal handling marks trades reverted rather than deleting them. Hedera. Creation is an x402 v2 resource: the server answers 402 with scheme exact, network hedera:testnet, a per-request nonce and the facilitator's advertised fee payer (read from Blocky402's /supported endpoint, never assumed); the signed payload is checked field by field against what was issued before the facilitator is contacted. Browser payers sign over Hedera WalletConnect, agents with their own key. Audit statements (horizon.audit.v1) are written to an outbox table in the same database transaction as the workflow transition, then published to an HCS topic by the worker in sequence, so Hedera being unreachable can never cause a second payment or deployment. Unknown outcomes are reconciled against the mirror node by event id before any resubmission. World. IDKit Selfie Check verifies server-side and lowers the 402 amount from 1 to 0.5 HBAR, once per credential per UTC day; the server re-checks action, environment and signal_hash against the request id before contacting World, and stores only the nullifier. Sandbox access arrived late in the hackathon; a credential has since been verified in Sandbox against the production deployment and a creation was then paid at the discounted price over Hedera x402. Frontend. React 19, Vite, viem, @x402/hedera, served by the API process. Deploys on Railway as three processes (API, worker, stream consumer) and one database. Hacky bits worth mentioning. Aqua's ship has no application callback, so publication is two transactions, Aqua.ship then HorizonSwapVM.admitCurve, and the router refuses to fill anything it has not admitted; a strategy shipped straight to Aqua is inert and filtered from discovery. Aqua's default dispatcher does not enable LimitSwap, so the probe harness overrides _runOpcode to permit it. Idempotency on the paid path comes from deriving creationId = keccak256("horizon-creation:" + requestId) and checking marketByCreationId before every broadcast. INVALID payouts of half a base unit are carried per holder in invalidRemainder so combining or splitting claims never changes total payout.