Building on Event Contracts

Event Contracts trade on the Somnia Markets on-chain order book. The developer surface is the @somnia-chain/markets-sdk (TypeScript) — the HTTP API covers spot only and has no event-contract endpoints.

With the SDK you can:

  • Discover live markets and stream order books, fills, and candles in real time
  • Place and cancel orders by symbol in human units (prices are Up probabilities in (0, 1))
  • Mint and merge complete sets (1 USDso ⇄ 1 Up + 1 Down) for sell-side inventory
  • Redeem winning positions after settlement

Install

The SDK is public on npm. Nothing else to configure:

bash
npm install @somnia-chain/markets-sdk viem

Use version 0.25.0 or newer. Anything below 0.23.0 no longer reads markets at all: the indexer dropped the longOpenInterest column those versions still ask for, so loadMarkets and listBinaryMarkets both fail. The examples here are TypeScript, so run them with a TypeScript runner such as tsx (npx tsx bot.ts).

A minimal loop

Discover a market, gate on its live on-chain state, read the book, take a position:

ts
import { SomniaMarkets, isBinaryMarket, type PlaceOrderResult } from "@somnia-chain/markets-sdk";

const exchange = new SomniaMarkets({ indexerUrl, chain, wsRpcUrl, addresses, privateKey });
const markets = Object.values(await exchange.loadMarkets(true));

for (const m of markets) {
  // `info` is a union across market kinds; isBinaryMarket narrows it.
  if (!m.active || !isBinaryMarket(m.info)) continue;

  // The indexer lags: gate every write on the live on-chain status (1 = Trading).
  // Row ids are plain strings; the client wants them hex-typed.
  const onchain = await exchange.client.getMarketOnchain(m.info.marketId as `0x${string}`);
  if (onchain.status !== 1) continue;

  const upSymbol = m.outcomes?.[0]?.symbol;   // e.g. "BTC-0-12AUG26-1600/USDso#YES"
  if (!upSymbol) continue;
  const book = await exchange.fetchOrderBook(upSymbol, 5);
  const ask = book.asks[0]?.[0];
  if (ask === undefined) continue;                    // no resting liquidity yet

  // Cross the touch; IOC so the unfilled remainder never rests silently.
  // From 0.23.0 a reverted write throws a decoded revert error, so let it
  // propagate or catch it here rather than testing a status flag.
  const order = await exchange.createOrder(upSymbol, "limit", "buy", 5, ask + 0.02, { timeInForce: "IOC" });

  // The receipt rides on `info`; the order itself has no `receipt` field.
  const { receipt } = order.info as PlaceOrderResult;
  console.log("filled in", receipt.transactionHash);
}

The package README on npm covers the rest of the surface: realtime watches, the React hooks, and the raw trader tier. Types ship with the package, so an editor with TypeScript will autocomplete the whole API.

Go deeper: Recipes has a snippet for every action a bot needs, from resting a quote to redeeming after settlement; Market Structure & Lifecycle explains the contract family, the four fill paths, and escrow; Contracts & Addresses lists the deployed core.

There are no API rate limits: market data is the chain itself, and the public RPC endpoints are unthrottled. A trading system should snapshot once and stay current from on-chain events — the SDK's live watches do exactly this.

Two mechanics worth understanding before you build:

  • One book, two sides. Up and Down trade on a single order book; a Down price is always 1 minus the Up price. Two opposite-side buyers can cross with no seller at all — the pool mints a fresh Up/Down pair from their combined collateral (so you can quote both sides with zero inventory).
  • Markets die on schedule and respawn. Every window has a hard expiry; the venue rolls a successor automatically. Track the successor via the market list, and note that a settled market leaves the live list — winnings are claimed by scanning recently settled markets.

Read the Gotchas before sending a real order.