npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@somnia-chain/markets-sdk

v0.25.0

Published

Zero-latency client for the Somnia Markets order books (spot + binary): hydrates a consistent Envio/Hasura snapshot, then materializes subsequent blocks — including the resting order book — from chain logs into a reactive store (with React hooks). Writes

Readme

@somnia-chain/markets-sdk

The TypeScript SDK for building on Somnia Markets — read live market data and place trades on the on-chain order book from your own app.

  • Realtime data, no wallet required. Order books, trades, candles, and a user's positions and open orders stream into your UI the moment they happen on-chain — no polling loops to write or manage.
  • Trading with a signer. Place and cancel orders, mint and redeem outcome shares, and more, through a typed trader bound to your wallet.
  • Works anywhere, with first-class React. Use plain async functions in any environment, or drop in the hooks for components that update themselves.

Install

pnpm add @somnia-chain/markets-sdk viem   # npm / yarn / bun equivalents work too

viem is a peer dependency. react is an optional peer — only needed for the @somnia-chain/markets-sdk/react entry.

Versions up to 0.19.0 were published to GitHub Packages under the private @somnia-chain scope; from 0.20.0 the package is public on npm — no registry configuration or token needed.

Create an exchange

new SomniaMarkets(config) is the single entry point — the exchange owns everything: symbols, market data, watches, and writes. No global setup, no hidden singleton; each exchange is isolated.

import { SomniaMarkets, SOMNIA_TESTNET_ADDRESSES } from "@somnia-chain/markets-sdk";
import { somniaTestnet } from "viem/chains"; // or your own defineChain()

const exchange = new SomniaMarkets({
  indexerUrl: "https://187.124.114.32.nip.io/v1/graphql", // the public testnet indexer
  chain: somniaTestnet,
  wsRpcUrl: "wss://api.infra.testnet.somnia.network/ws",
  addresses: SOMNIA_TESTNET_ADDRESSES, // baked-in per-chain constants (SOMNIA_MAINNET_ADDRESSES for mainnet)
  privateKey, // optional — needed for createOrder & friends
});
await exchange.loadMarkets();

const book  = await exchange.watchOrderBook("BTC-95000-31DEC26/USDC#YES"); // live, zero RTT
const order = await exchange.createOrder("BTC-95000-31DEC26/USDC#YES", "limit", "buy", 10, 0.62);

The raw engine tier — bigint-exact, address-keyed — is reached through the exchange (exchange.client, exchange.trader), never constructed separately. The WebSocket opens lazily on first chain I/O, so an indexer-only exchange (e.g. server-side GraphQL reads) never opens one. Nothing is shared between instances: a bot per chain, per-request servers, parallel tests — just construct another. Two exchanges never share watch state or sockets.

The guides, in reading order:

  • The exchange API — the SomniaMarkets class, the SDK's primary surface: symbols (SOMI/USDC, BTC-95000-31DEC26/USDC#YES), fetch*/watch*/createOrder, human-unit structs. Exchange-bot muscle memory (ccxt included) transfers directly — start here.
  • Binary markets — the binary (YES/NO) CLOB: probability prices, the four sides, mint/burn/redeem, and a maker loop.
  • Spot markets — base/quote books: ticks and lots, native-base escrow, market orders, and stop orders.
  • Perps — live on testnet: cross-margin via the MarginBank, funding, positions, and how perps slot into the marketType union.
  • Price feeds — realtime BTC/ETH index prices from the on-chain EMA oracle: watchPrice/getLivePrice, one-shot history + candles, and the React hooks.
  • The engine (advanced) — the raw tier behind the exchange (exchange.client / exchange.trader): bigint-exact reads, ref-counted watches, React hook wiring, raw writes.
  • Architecture guide — diagrams of the whole machine: the watch seam, event routing, the local order book, the reconnect lifecycle, and the one-round-trip write path.

Two ways to read

| How | What it is | Returns | |---|---|---| | client.list* / client.get* | One-shot read (indexer GraphQL or on-chain) | a Promise | | client.getLive* + client.subscribeLive | Synchronous read off the live store (within a watchMarket scope) | a value, now | | use* hooks (/react) | React bindings over the live store (auto-watching) | re-render on change |

So client.getFills fetches once; client.getLiveFills reads the live tape; useLiveFills re-renders a component as it updates. In React, provide the client once with <SomniaMarketsProvider client={client}> (from @somnia-chain/markets-sdk/react) and the hooks read it from context.

Markets come from one discriminated union — Market = SpotMarket | PerpMarket | BinaryMarket, keyed on marketType — via client.listMarkets / client.getMarket. Binary-only callers can use client.listBinaryMarkets / client.getBinaryMarket, the same query pre-narrowed to BinaryMarket. (Note: binary, not clob — a spot market is an order book too, so "CLOB" was never the right label for the binary surface.)

Money crosses the API as raw integers (bigint on writes, decimal strings from the indexer) scaled by token decimals. Convert at the edges with fromHuman (input) and toHuman / toHumanString (display); for binary prices, probabilityToPrice / priceToProbability map a YES price ↔ a 0–1 probability.

What's included

  • Entry pointnew SomniaMarkets(config) → the exchange (symbols, fetch*/watch*/createOrder, human-unit structs). Its engine tier — exchange.client (SomniaMarketsClient, bigint-exact reads + watches) and exchange.trader (raw writes) — is reached through it; ClientConfig
  • ReactSomniaMarketsProvider, useSomniaMarketsClient, and the hooks useWatchMarket, useWatchUser, useLiveStatus, useIsTailing, useLiveFills, useLiveUserFills, useLiveMarketByPool, useLiveMarketByAddress, useLiveUserOrders, useLiveBinaryOrderBook, useLiveSpotOrderBook — the pool-keyed data hooks watch automatically while mounted
  • Client readsclient.listMarkets/getMarket (the Market union), listBinaryMarkets/getBinaryMarket, getCandles, getBinaryOrderBook, getOpenOrders, getPortfolio, getSyncStatus, getMarketOnchain, getSystemInfo, … (indexer reads throw on failure — an empty result always means "no rows", never "request failed")
  • Order state at chain headgetOrderOnchain(pool, orderId), getOwnOpenOrdersOnchain(pool, owner), getAllOpenOrdersOnchain(pool, { isBid }) answer from the pool contract, so an order is readable the moment its block lands. Use these to read your own writes; use the indexed getOpenOrders / getOrders for history — the chain surface only knows what is open now
  • Live watches (no React)client.watchMarket(pool) / watchMarkets({ discover }) / watchUser(account) → ref-counted handles; getWatchStatus, subscribeLive, getLiveStatus, getLiveMarkets, getLiveMarketByPool/…ByAddress, getLiveFills, getLiveUserFills, getLiveUserOrders, and the locally materialized resting books getLiveBinaryOrderBook (binary, 4-sided) / getLiveSpotOrderBook — synchronous, zero round-trips, scoped to what you watch. Every market kind streams; a discovery watch picks up new markets from the creation events (the MarketCreator's rolling series AND direct BinaryMarketsModule.createMarket markets); binary status/resolution stays current from chain events.
  • Tradingclient.createTrader(...)placeOrder, cancelOrder, approveBuilder (opt a routing/builder frontend in for per-order builder fees), placeSpotOrder, placeSpotStopOrder, mintSet, burnSet, redeem, faucet, resolve, voidMarket. Each write awaits its receipt and resolves to { hash, receipt } (placeOrder adds orderId + fills). With a privateKey/local account the SDK signs locally with fixed fees and a locally-tracked nonce, and sends via Somnia's realtime_sendRawTransaction — send + confirm in one round-trip, zero fee/nonce/gas estimation RPCs. In the browser, pass a walletClient (confirm rides the newHeads subscription).
  • Types & helpersMarket/SpotMarket/BinaryMarket (+ isSpotMarket/ isBinaryMarket), LiveFill, LiveOrder, BinarySide, TailStatus, kindOf, fillKind, fromHuman/toHuman, DECIMALS, …

Every method, hook, and type is listed in the API reference.

How the live feed works

You get instant updates without running your own indexer — scoped to exactly the markets you watch. Opening a watch loads a consistent snapshot of that scope (the one and only indexer touch), then keeps it current by streaming its on-chain events over a WebSocket — so trades, orders, prices, and the resting order book itself update the moment they're final on-chain. There is no polling anywhere: the WebSocket is the only realtime transport, and if it drops the watches heal themselves by reconnecting with backoff and backfilling the missed blocks straight from chain.

Debugging

The SDK is silent by default. To see what a client is doing — every trader call, the sign/broadcast pipeline, live-tail hydration and block application — pass a debug sink in the config. Events are structured data (DebugEvent: log lines plus span start/end pairs with ids, explicit parentId links, durations, and errors), so the sink owns all filtering and formatting. The toggle mechanism belongs to your app, not the SDK:

The bundled consoleDebugSink() renders the stream as an indented span tree (reconstructed from parentId, so it stays correct under concurrency):

[sdk] ▶ trader.placeOrder { params: { pool: "0x…", side: "BUY_YES" } }
[sdk]   ▶ trade.execute { functionName: "placeBinaryOrder", … }
[sdk] liveTail applying logs { received: 3, … }
[sdk]     ▶ trade.signCall
[sdk]     ◀ trade.signCall 2.1ms
[sdk]     · trade.execute { hash: "0x…" }
[sdk]   ◀ trade.execute 38.2ms
[sdk] ◀ trader.placeOrder 41.0ms
import { consoleDebugSink } from "@somnia-chain/markets-sdk";

// Browser (explorer dev) — flip on from devtools with
// localStorage.setItem("sdk-debug", "1") and reload:
const exchange = new SomniaMarkets({
  ...config,
  debug: localStorage.getItem("sdk-debug") ? consoleDebugSink() : undefined,
});

// Node bot — JSON lines behind an env var:
const exchange = new SomniaMarkets({
  ...config,
  debug: process.env.SDK_DEBUG
    ? (e) => console.log(JSON.stringify(e, (_, v) => (typeof v === "bigint" ? v.toString() : v)))
    : undefined,
});

In tests, debugCollector() captures the stream with typed filters:

import { debugCollector } from "@somnia-chain/markets-sdk";

const c = debugCollector();
const exchange = new SomniaMarkets({ ...config, debug: c.sink });
await exchange.trader.placeOrder(params);
expect(c.starts("trade.execute")).toHaveLength(1);

Span events map 1:1 onto OpenTelemetry (name ↔ span name, data ↔ attributes, error ↔ status, parentId ↔ context link), so a real tracer is just a sink that keeps a Map<id, Span>phase: "start" calls tracer.startSpan(...) (linking parentId via OTel context) and phase: "end" calls span.end(). The OTel dependency lives entirely in your app; the SDK stays dependency-free.