Skip to content

Build on one price.

A TypeScript SDK, the clearing engine bit-exact with the contracts, a gasless relayer, the tape and its live stream, and an MCP server for agents. Everything Unison runs on, open.

Watch the tape.

Every batch, every market, as it clears: one uniform price, the reference it cleared on, the band, and the receipt hash that chains it to the last.
stream.ts
import { TapeClient } from "@unison/sdk";

const tape = new TapeClient(TAPE_URL);

// one uniform price per batch, as it clears
tape.stream(["prints:0"], {
  print: (p) => console.log(p.upTo, p.price, p.receiptHash),
});

// or ask: prints, candles, fairness, an account's fills
const fairness = await tape.fairness(0, { window: "24h" });

Trade from code.

Sign an order with a session key and the relayer submits it. Every order in a batch gets one price, so a script gets the same price as a desk.
order.ts
import {
  buildOrder, RelayerClient, Side, signAsSession, tickOfPrice,
} from "@unison/sdk";

const order = buildOrder({
  account,              // the account this key trades for
  marketId: 0n,         // aNVDA/AUSD
  side: Side.BID,
  tick: tickOfPrice(180_250_000n, 10_000n), // $180.25, never worse
  qty: 10n ** 18n,      // 1 share
});
const sig = await signAsSession(sessionKey, chainId, gateway, order);
const relayer = new RelayerClient(RELAYER_URL);
const { id } = await relayer.postOrder(order, sig); // no gas

Know the price before it prints.

@unison/engine is the auction itself, ported from Solidity integer for integer and diff-tested against it. Run the batch now forming and see the price it would clear at.
indicative.ts
import { compute } from "@unison/engine";

// the batch now forming: per tick in the band, depth + pending orders
const r = compute({ lo, hi, refTick, bidAbove, askBelow, bids, asks });

r.traded;  // did demand meet supply inside the band?
r.tick;    // the one price everyone in the batch gets
r.volume;  // and how much changed hands

Don't trust. Re-walk.

Each print commits to the one before it. Recompute the chain from the tape, or from the chain's own logs, and any rewritten batch stands out. The record does this live.
verify.ts
import { encodeAbiParameters, keccak256 } from "viem";

const T = [
  "bytes32", "uint256", "uint64", "uint256", "uint256",
  "uint256", "uint64", "uint8", "uint256",
].map((type) => ({ type }));

let prev = "0x" + "0".repeat(64);
const prints = await tape.prints(0, { limit: 1000 });
for (const p of prints.reverse()) {
  const h = keccak256(encodeAbiParameters(T, [
    prev, 0n, BigInt(p.upTo), BigInt(p.tick), BigInt(p.volume),
    BigInt(p.refPrice), BigInt(p.refTimeMs), p.status,
    BigInt(p.ts / 1000),
  ]));
  if (p.prevReceiptHash === prev && h !== p.receiptHash)
    throw new Error(`batch ${p.upTo} doesn't recompute`);
  prev = p.receiptHash;
}

Hand an agent the keys.

Mint a session key with limits on Agents, then give it to any agent that speaks the Model Context Protocol. It can place and cancel within those limits, and can never withdraw.
Claude Code
claude mcp add unison \
  -e DEPLOYMENT=deployments/monad-testnet.json \
  -e RELAYER_URL=$RELAYER_URL -e RPC_URL=$RPC_URL \
  -e AGENT_ACCOUNT=0x… -e AGENT_PRIVATE_KEY=0x… \
  -- node --conditions=development services/mcp/src/main.ts

The surface.

Tape (read)

GET /v1/markets
every market: reference, regime, last print, 24 h volume
GET /v1/markets/:id/prints
uniform prints, newest first; .csv for all of them
GET /v1/markets/:id/fairness
distance from the reference, reference lag, chain check
GET /v1/accounts/:addr/orders
orders with per-auction fills at each batch's price
GET /v1/receipts/:market/:account/:slot
a fill's receipt, recomputed
GET /v1/stream
SSE: heads, prints, regime, account:0x…
GET /v1/stats
who trades: accounts with fills, the team counted apart

Relayer (write, no gas)

POST /v1/orders
a signed order: session key, passkey or wallet
POST /v1/cancels
a signed cancel
POST /v1/withdrawals
the account's own signature only
POST /v1/sessions
grant or revoke a key, with caps
POST /v1/passkeys
register a passkey account
GET /v1/jobs/:id
where a relayed action is

Every route, field and error code: docs/API.md.