Integrate
If you already run a bot against Polymarket’s CLOB, you are two changes away: a base URL and an EIP-712 domain. The order struct is byte-identical to the Polymarket CTF Exchange’s. The difference that matters is the unit: prices are in shares of the stock, not dollars, and a winning share pays one share of the stock.
What is different
- Domain:
OTHERWISE CTF Exchange / 1 / 8453, oneverifyingContractper collateral — /clob/domain lists them. - Units:
makerAmount/takerAmountare in the collateral’s decimals (18 for every collateral listed here). A price of 0.018 is 0.018 wtTSM per YES share. - Fees: maker 0%, taker 1% (see /fees). An order that fills on arrival must be signed with
feeRateBps = 100; one that rests signs 0. The contract charges the signed rate, scaled by min(p, 1−p). - Auth: none. Orders are self-authenticating. Polymarket’s L2 API-key headers are ignored; cancels take an EIP-191 signature over
cancel <orderID>. - Signature types: EOA (0) and EIP-1271 (3). Polymarket proxy/Safe types are rejected.
- Matching: Polymarket’s — a YES bid and a NO bid that sum to ≥ 1 share mint a pair; two asks that sum to ≤ 1 merge one. You can quote one side and be filled by the other side’s bidders.
Endpoints
# markets, Polymarket shape
curl https://otherwise-api.fly.dev/clob/markets
# book for a token (prices in wtTSM per share, sizes in shares)
curl "https://otherwise-api.fly.dev/clob/book?token_id=315663…4607"
# midpoint / best price / spread
curl "https://otherwise-api.fly.dev/clob/midpoint?token_id=…"
curl "https://otherwise-api.fly.dev/clob/price?token_id=…&side=buy"
# fee schedule
curl https://otherwise-api.fly.dev/feesNative endpoints (same data, our shapes): /markets, /markets/:id/book?token=yes|, /markets/:id/trades, /orders?maker=, /positions/:address, POST /order, DELETE /order/:hash. Every market row carries oracle — { state: open| — the UMA question’s lifecycle; POST /order refuses orders from the deadline on (code past_deadline) and once resolved; cancels, merges and redeems stay open.
Live feed
One WebSocket at wss://otherwise-api.fly.dev/ws. Subscribe by market id, or "*" for every market (it snapshots every market’s books and streams them all); you get a book snapshot per leg immediately, then a new snapshot whenever that book changes, fill frames when a match confirms on chain, marks on every refresh, and order events (accepted / cancelled / rejected / pruned). Send {"type":"ping"} every ~30 s and reconnect if no pong comes back: a cut socket can go silent without closing.
const ws = new WebSocket('wss://otherwise-api.fly.dev/ws');
ws.onopen = () => ws.send(JSON.stringify({ type: 'subscribe', markets: ['wtTSM-if-China-invades-Taiwan'] }));
ws.onmessage = (e) => {
const f = JSON.parse(e.data);
// f.type: 'book' | 'fill' | 'marks' | 'order' | 'subscribed' | 'pong'
if (f.type === 'book') console.log(f.token, 'best ask', f.asks[0]?.price, 'best bid', f.bids[0]?.price);
if (f.type === 'fill') console.log('fill', f.leg, f.side, f.price, f.tokens, f.txHash);
};Limits: a client that stops reading (over 256 KiB unsent) is dropped with no close frame, so read continuously; more than 60 messages per 10 s closes the socket with 1008; 10 sockets per address (1008); 500 in all (1013, “feed is full”). After any cut, reconnect with backoff and resubscribe.
Book prices are in the collateral per share; sizes in shares; reflected is the part of a level that comes from the other leg’s orders.
Sign and post an order
import { createWalletClient, http } from 'viem';
import { privateKeyToAccount } from 'viem/accounts';
import { base } from 'viem/chains';
// 1. Which exchange? One per collateral. GET https://otherwise-api.fly.dev/clob/domain
const domain = { name: 'OTHERWISE CTF Exchange', version: '1', chainId: 8453,
verifyingContract: '0x1a86024c80C1776B9E4Fb3D8333626af79CD4785' }; // wtTSM, from /clob/domain
// 2. Same order struct as Polymarket's CTF Exchange.
const types = { Order: [
{ name: 'salt', type: 'uint256' }, { name: 'maker', type: 'address' }, { name: 'signer', type: 'address' },
{ name: 'taker', type: 'address' }, { name: 'tokenId', type: 'uint256' }, { name: 'makerAmount', type: 'uint256' },
{ name: 'takerAmount', type: 'uint256' }, { name: 'expiration', type: 'uint256' }, { name: 'nonce', type: 'uint256' },
{ name: 'feeRateBps', type: 'uint256' }, { name: 'side', type: 'uint8' }, { name: 'signatureType', type: 'uint8' } ] };
// 3. Buy 1 YES share at 0.018 wtTSM. Amounts are in the collateral's decimals (18 for wtTSM).
const account = privateKeyToAccount(process.env.PK as `0x${string}`);
const wallet = createWalletClient({ account, chain: base, transport: http() });
const order = {
salt: BigInt(Date.now()), maker: account.address, signer: account.address,
taker: '0x0000000000000000000000000000000000000000',
// wtTSM-if-China-invades-Taiwan YES, from /clob/markets
tokenId: 31566390476372170483964876412747946047718647924514053889738405460469647794607n,
makerAmount: 18_000_000_000_000_000n, // 0.018 wtTSM you pay
takerAmount: 1_000_000_000_000_000_000n, // 1 YES share you get
expiration: 0n, nonce: 0n, // nonce: GET /positions/<you> → nonce
feeRateBps: 100n, // 100 if this will fill on arrival, 0 if it will rest
side: 0, signatureType: 0 }; // 3 (EIP-1271) if the maker is a contract account
const signature = await wallet.signTypedData({ domain, types, primaryType: 'Order', message: order });
// 4. Post it. Polymarket envelope on /clob/order, or the bare wire order on /order.
await fetch('https://otherwise-api.fly.dev/clob/order', { method: 'POST', headers: { 'content-type': 'application/json' },
body: JSON.stringify({ order: { ...order, side: 'BUY', signature,
salt: order.salt.toString(), tokenId: order.tokenId.toString(), makerAmount: order.makerAmount.toString(),
takerAmount: order.takerAmount.toString(), expiration: '0', nonce: '0', feeRateBps: '100' }, orderType: 'GTC' }) });Making markets here
The natural market maker already holds the stock: a YES ask at p and a NO ask at 1 − p from one wallet, funded by splitting one share, is a two-sided YES book with no collateral locked in bids. Hedging is holding the stock you were going to hold anyway. Makers pay no fee. There is a ready-made quoter in the repo: PK=0x… node --conditions=development --experimental-strip-types scripts/quote.ts wtTSM-if-China-invades-Taiwan 0.5 0.05 splits 0.05 wtTSM and rests a YES ask and a NO ask ±12% around your view (here: TSM worth 0.5× spot if it happens). Re-running replaces your quotes. If you want to quote size, tell us — Telegram.
Run the example bot
scripts/mm-bot.ts is a complete maker you can copy: it reads the market’s reference probability P from /markets/<id>, sets YES fair = clamp(view × P, 0.01, 0.99) and NO fair = 1 − YES fair, and rests a YES ask and a NO ask one half-spread above each (default 0.03). Every cycle (default 60 s) it re-centres: it cancels its own orders, splits more collateral if the wallet is short, and re-posts, but only when a target moved at least one price point (--min-move) or a quote was filled. An ask that would cross the book is signed with the taker rate from /fees and the log says so. Everything it needs about the market comes from the public API.
PK=0x… node --conditions=development --experimental-strip-types scripts/mm-bot.ts wtTSM-if-China-invades-Taiwan 1.1 --size 0.05
# --dry prints the quotes it would place and sends nothing; --once runs a single cycle
# --spread=0.03 --interval=60 --min-move=0.01 --api=https://otherwise-api.fly.devCtrl-C cancels the resting quotes before it exits. The pure parts (fair-price math, quantisation, crossing and min-move logic) are unit-tested in scripts/mm-bot.test.ts.
Embed a market
A card with the question, what YES and NO pay, and a button to trade. Any site may frame it. Add ?theme=light or dark to fix the scheme.
<iframe src="https://otherwise-web.fly.dev/embed/m/wtTSM-if-China-invades-Taiwan" width="100%" height="240" style="max-width:400px;border:0" loading="lazy" title="Otherwise market"></iframe>All markets: embed URLs
- SPYM if Republicans win the Senate in 2026
- MSTR if Bitcoin reaches $95,000 in 2026
- MSTR if MSCI removes MicroStrategy from its World or USA index in 2026
- COIN if the CLARITY Act is signed into law in 2026
- TSM if China invades Taiwan in 2026
- SPCX if a Tesla–SpaceX merger is announced in 2026
- MSTR if Bitcoin reaches $88,000 between Sep 28 and Oct 4, 2026
- SPYM if the Fed raises rates 25 bps at its October 2026 meeting
- MSTR if Bitcoin dips to $70,000 in 2026
- COIN if Bitcoin dips to $75,000 in October 2026
- SPYM if the US enters a recession by end of 2026
- TSM if China blockades Taiwan in 2026
- COIN if Ethereum dips to $1,500 in 2026
- COIN if Bitcoin reaches $100,000 in 2026
- COIN if Bitcoin dips to $55,000 in 2026
- COIN if Ethereum reaches $2,800 in October 2026
- SPYM if the 10-year Treasury yield reaches 5.5% in 2026