@somnia-chain/markets-sdk / index / SomniaMarkets
Class: SomniaMarkets
Defined in: packages/sdk/src/unified/exchange.ts:207
Constructors
Constructor
new SomniaMarkets(
config):SomniaMarkets
Defined in: packages/sdk/src/unified/exchange.ts:301
Parameters
config
Returns
SomniaMarkets
Properties
client
readonlyclient:SomniaMarketsClient
Defined in: packages/sdk/src/unified/exchange.ts:209
The native engine — bigint-exact, address-keyed. The escape hatch.
markets
markets:
Record<string,UnifiedMarket> ={}
Defined in: packages/sdk/src/unified/exchange.ts:211
Unified markets keyed by MARKET symbol (populated by loadMarkets).
symbols
symbols:
string[] =[]
Defined in: packages/sdk/src/unified/exchange.ts:213
All market symbols (populated by loadMarkets).
has
readonlyhas:object
Defined in: packages/sdk/src/unified/exchange.ts:219
Capability map — which unified verbs this venue supports (the ccxt
exchange.has convention, for capability-probing bot code). Every listed
verb is implemented here, so every flag is true.
fetchMarkets
readonlyfetchMarkets:true=true
fetchOrderBook
readonlyfetchOrderBook:true=true
fetchTrades
readonlyfetchTrades:true=true
fetchOHLCV
readonlyfetchOHLCV:true=true
fetchBalance
readonlyfetchBalance:true=true
fetchOpenOrders
readonlyfetchOpenOrders:true=true
fetchMyTrades
readonlyfetchMyTrades:true=true
fetchStatus
readonlyfetchStatus:true=true
createOrder
readonlycreateOrder:true=true
cancelOrder
readonlycancelOrder:true=true
watchOrderBook
readonlywatchOrderBook:true=true
watchTrades
readonlywatchTrades:true=true
watchOrders
readonlywatchOrders:true=true
watchMyTrades
readonlywatchMyTrades:true=true
fetchPositions
readonlyfetchPositions:true=true
fetchFundingRate
readonlyfetchFundingRate:true=true
SomniaMarkets.fetchFundingRate
fetchFundingRateHistory
readonlyfetchFundingRateHistory:true=true
SomniaMarkets.fetchFundingRateHistory — the key did not previously exist in this map, so it had to be ADDED rather than flipped.
watchPrice
readonlywatchPrice:true=true
fetchPrice
readonlyfetchPrice:true=true
fetchPriceOHLCV
readonlyfetchPriceOHLCV:true=true
Accessors
trader
Get Signature
get trader():
Trader
Defined in: packages/sdk/src/unified/exchange.ts:319
The raw write tier bound to this exchange's signer — bigint-exact
placeOrder/mintSet/faucet/… for anything the unified verbs don't
cover.
Gotchas
Built lazily; throws if no signer was configured.
Returns
walletAddress
Get Signature
get walletAddress():
`0x${string}`|undefined
Defined in: packages/sdk/src/unified/exchange.ts:342
The authenticated wallet address, if a signer was configured.
Returns
`0x${string}` | undefined
perpDiscoveryError
Get Signature
get perpDiscoveryError():
SomniaMarketsError|null
Defined in: packages/sdk/src/unified/exchange.ts:406
Why chain-tier perp discovery did not run on the last loadMarkets, or
null when it ran (or was never applicable).
loadMarkets() contains a discovery failure rather than throwing, because it is
the implicit prerequisite of nearly every symbol-based verb and a chain failure
must not take the SPOT and OUTCOME market lists down with it. This is where that
contained failure is reported, with the underlying error preserved in cause.
When to use
Check it after loadMarkets() whenever a SHORT perp list would be worse than an
error — a market page, an order router, anything that would otherwise present
"this market does not exist". Then either tell the user the list is incomplete, or
retry with loadMarkets(true): a bare loadMarkets() early-returns once any
market is cached, so it never re-runs discovery and this value would stay stale.
Gotchas
- Do NOT infer this from
perpStatusbeing absent. That works only when the indexer already carries a perp row to inspect; with a configured factory and an indexer carrying none, a failed discovery yields an EMPTY perp list with no market to check. This accessor answers in both cases. nulldoes not mean the venue has perps. A chain with no perps plane deployed (local anvil) never attempts discovery and reportsnulltoo.
Example (Refusing to show a possibly-short perp list)
await exchange.loadMarkets();
if (exchange.perpDiscoveryError) {
// A bare loadMarkets() would early-return the cached registry and never retry.
await exchange.loadMarkets(true);
}
if (exchange.perpDiscoveryError) {
const partial = exchange.perpDiscoveryPartialFailure;
throw new Error(
partial
? `perp list incomplete: ${partial.failed} of ${partial.total} markets unread`
: "perp discovery failed; the perp list may be short",
);
}
Returns
SomniaMarketsError | null
perpDiscoveryPartialFailure
Get Signature
get perpDiscoveryPartialFailure(): {
failed:number;total:number; } |null
Defined in: packages/sdk/src/unified/exchange.ts:419
How much of the chain-only perp set was lost when discovery PARTIALLY failed, or
null when it did not.
Typed counts rather than prose in a message, so a consumer can decide on them —
{ failed: 1, total: 4 } reads as "three of four chain-only markets are listed".
Always null when perpDiscoveryError is null, and also null when discovery
failed outright rather than partly (nothing was read, so there is no ratio).
Returns
{ failed: number; total: number; } | null
Methods
setSigner()
setSigner(
signer):void
Defined in: packages/sdk/src/unified/exchange.ts:332
Bind (or replace) the exchange's signer after construction. Browser apps
construct the exchange at boot for public reads, then call this when the
user's wallet connects — and again with {} on disconnect, which returns
the exchange to unauthenticated reads. Replaces the trader every
authenticated verb and walletAddress resolve against; live watches and
market data are unaffected.
Parameters
signer
Pick<TraderConfig, "privateKey" | "account" | "walletClient">
Returns
void
loadMarkets()
loadMarkets(
reload?):Promise<Record<string,UnifiedMarket>>
Defined in: packages/sdk/src/unified/exchange.ts:449
Load (or reload) the market registry: every market as a unified, symbol-keyed market object. Call once before anything symbol-based.
Perp markets come from two sources. The indexer's rows are unioned with the
PerpPoolFactory's, because the indexer's perp set is a curated manifest and a pool
deployed after it was written is live on chain and absent there. A market only the
chain knows carries UnifiedMarket.indexed false; read that field's docs
before touching anything history-derived on it.
A chain failure is contained, not thrown. This method is the implicit prerequisite of nearly every symbol-based verb, so letting a discovery failure out would take the SPOT and OUTCOME lists down with it — lists that need no chain read at all. The indexer read still throws: its failure means there is no registry to return. A contained discovery failure is reported by perpDiscoveryError, and that includes the PARTIAL case where some chain-only markets were read and others were not.
Gotchas
- Early-returns the cached registry unless
reloadis true, so a retry after a discovery failure must passtrue.
Details
reload: Re-read everything, including the live tradeability gates and the binary pools' grids.
Parameters
reload?
boolean = false
Returns
Promise<Record<string, UnifiedMarket>>
market()
market(
ref):Tradable
Defined in: packages/sdk/src/unified/exchange.ts:817
Resolve any handle (symbol, tradable symbol, pool/market address, market id) to its tradable. Requires loadMarkets().
Parameters
ref
string
Returns
priceToPrecision()
priceToPrecision(
ref,price):number
Defined in: packages/sdk/src/unified/exchange.ts:837
Snap a price to the market's tick grid (rounds down; binary prices are also clamped inside (0, 1)).
When to use
Use before createOrder with computed prices.
Spot/perp ticks come from the market row; binary ticks come from the pool,
read once by loadMarkets — so a pool recycled mid-session keeps the
grid captured at load time until loadMarkets(true) refreshes it.
Gotchas
- Throws InvalidInputError if the market is binary and its pool's parameters could not be read — quantizing against a guessed grid is what produced off-tick rejections, so this fails loudly instead.
Parameters
ref
string
price
number
Returns
number
amountToPrecision()
amountToPrecision(
ref,amount):number
Defined in: packages/sdk/src/unified/exchange.ts:858
Snap an amount to the market's lot grid (rounds down).
Spot/perp lots come from the market row; binary lots come from the pool,
read once by loadMarkets — so a pool recycled mid-session keeps the
grid captured at load time until loadMarkets(true) refreshes it.
Gotchas
- Throws InvalidInputError if the market is binary and its pool's parameters could not be read. Previously such a market fell back to a one-whole-token lot, silently flooring every sub-token amount to 0.
Parameters
ref
string
amount
number
Returns
number
fetchMarkets()
fetchMarkets():
Promise<UnifiedMarket[]>
Defined in: packages/sdk/src/unified/exchange.ts:875
Every market as an array — loadMarkets (called if needed), minus the symbol keying.
When to use
Use as the ccxt-shaped sibling for list-style consumers.
Returns
Promise<UnifiedMarket[]>
fetchOrderBook()
fetchOrderBook(
ref,limit?):Promise<UnifiedOrderBook>
Defined in: packages/sdk/src/unified/exchange.ts:1009
One-shot book read from the contract (head-fresh; no watch needed).
When to use
Use when one book snapshot is enough. For a continuously-current zero-round-trip book, use watchOrderBook.
Parameters
ref
string
limit?
number = 10
Returns
Promise<UnifiedOrderBook>
fetchTrades()
fetchTrades(
ref,since?,limit?):Promise<UnifiedTrade[]>
Defined in: packages/sdk/src/unified/exchange.ts:1019
Recent public trades (indexer, newest first).
Parameters
ref
string
since?
number
limit?
number = 50
Returns
Promise<UnifiedTrade[]>
fetchOHLCV()
fetchOHLCV(
ref,timeframe?,since?,limit?):Promise<UnifiedOHLCV[]>
Defined in: packages/sdk/src/unified/exchange.ts:1063
OHLCV candles (indexer), oldest first as [ms,o,h,l,c,vol] rows. Timeframes: 1m 5m 15m 1h 4h 1d.
Example (Reading candles)
The last 24 hourly candles, destructured per row.
const candles = await exchange.fetchOHLCV("SOMI/USDC", "1h", undefined, 24);
for (const [ts, open, high, low, close, volume] of candles) {
console.log(new Date(ts).toISOString(), open, high, low, close, volume);
}
Parameters
ref
string
timeframe?
string = "5m"
since?
number
limit?
number = 500
Returns
Promise<UnifiedOHLCV[]>
fetchTicker()
fetchTicker(
ref):Promise<UnifiedTicker>
Defined in: packages/sdk/src/unified/exchange.ts:1095
Rolling 24h ticker (indexer): OHLC + base/quote volume folded from the
hourly candles, last from the freshest fill. NO-outcome tradables view
prices through the 1−p lens like every other read.
Example (Reading a ticker)
Drive a price strip off one call.
const tk = await exchange.fetchTicker("SOMI/USDC");
console.log(tk.last, tk.percentage, tk.baseVolume);
Parameters
ref
string
Returns
Promise<UnifiedTicker>
fetchBalance()
fetchBalance():
Promise<UnifiedBalances>
Defined in: packages/sdk/src/unified/exchange.ts:1202
Wallet balances for every currency the loaded markets use (+ native).
Gotchas
free === total: funds escrowed in resting orders live in the pools, not
the wallet, so they simply don't appear here.
- Throws SignerRequiredError - balances are per-account, so this needs a signer (or an
account) even though it only reads. - Throws IndexerError -
loadMarkets()or the outcome-holdings read needed the indexer and it was unreachable. Distinct from an empty result: no balances is{}, not a throw. - Throws RpcError - a chain balance read did not complete. A failed read is never reported as a zero balance or a missing key.
- Throws ContractRevertError - a token's
balanceOfreverted.
Example (Reading balances)
ERC-20s key by currency code; binary outcome holdings key by TRADABLE symbol.
const bal = await exchange.fetchBalance();
console.log(bal.USDC?.total); // collateral in the wallet
console.log(bal["BTC-95000-31DEC26/USDC#YES"]?.total); // YES shares held
Returns
Promise<UnifiedBalances>
fetchOpenOrders()
fetchOpenOrders(
ref?,limit?):Promise<UnifiedOrder[]>
Defined in: packages/sdk/src/unified/exchange.ts:1266
Open orders (indexer view).
When to use
Use for an occasional snapshot; a trading loop should prefer watchOrders.
Details
limit is applied BY THE QUERY, per venue — not to the merged result. An
unscoped call reads all three venues, so it can return up to 3 × limit
rows; a ref-scoped call reads only that venue. The default is 200 per
venue.
ref: Restrict to one tradable (symbol or address). Omit for all.limit: Max orders PER VENUE the query returns (default 200).
Gotchas
The indexer view lags the chain slightly.
On SPOT the same limit also bounds the PENDING STOP ORDERS the underlying
portfolio read returns — one query variable caps both sets. That list is
not part of this verb's result, so the coupling is invisible here, but a
caller reading client.getSpotPortfolio directly with a small
ordersLimit will see a correspondingly short pendingStopOrders.
Parameters
ref?
string
limit?
number
Returns
Promise<UnifiedOrder[]>
fetchOrders()
fetchOrders(
ref?,since?,limit?,params?):Promise<UnifiedOrder[]>
Defined in: packages/sdk/src/unified/exchange.ts:1303
The wallet's orders across every lifecycle status (indexer), newest
first — the history counterpart to fetchOpenOrders. Scope to one
tradable with ref; page with limit/params.offset, both forwarded to
the query as a true offset window over one ordered set. (Its siblings page
differently: fetchMyTrades pages a fill tape to satisfy limit,
and fetchOpenOrders applies its limit per venue.)
Example (Reading order history)
The last 50 orders on one book, whatever became of them.
const orders = await exchange.fetchOrders("SOMI/USDC", undefined, 50);
for (const o of orders) console.log(o.status, o.side, o.amount, o.txHash);
Parameters
ref?
string
since?
number
limit?
number = 100
params?
offset?
number
Returns
Promise<UnifiedOrder[]>
fetchPortfolioAnalytics()
fetchPortfolioAnalytics(
timeframe,params?):Promise<PortfolioAnalytics>
Defined in: packages/sdk/src/unified/exchange.ts:1439
The wallet's portfolio metrics plane over a timeframe: a holdings curve, an equity curve, per-bucket PnL, money-weighted return, volume, and fees saved versus a comparison taker rate. Computed client-side from the wallet's indexed fills (avg-cost basis) marked to candle closes — no server aggregate involved.
The two curves measure different things. holdings is what the traded
book is worth at each sample, so it is a level. equity is the window's
cumulative realized and unrealized PnL, so it is a change. Neither counts
a token that arrived without a fill: read balances from the chain for what
the wallet itself holds. Read HoldingsPoint before presenting the level
— it states where its sign and its completeness end.
SPOT-scoped today: binary outcomes settle rather than mark, and the perp
account plane (funding, margin) joins the fold as new event kinds when
perp analytics land. Fills are paged to exhaustion — truncating would
drop the OLDEST fills and silently corrupt the carried-in cost basis,
not just undercount volume. Fills whose taker direction the indexer has
not resolved (takerIsBid null), or where the wallet's role (maker vs
taker) is unknowable, are skipped rather than guessed.
The money-weighted return needs to know what capital the wallet put in.
Fills alone cannot say — capital that never passed through a trade is
invisible to them — so without funding the capital base is a trades-only
proxy that overstates the return for a wallet trading a small part of its
balance. Pass funding to measure against real external capital, and read
mwrr.capitalBasis to see which definition applied.
Example (Measuring portfolio performance)
const p = await exchange.fetchPortfolioAnalytics("7d");
console.log(p.pnl.totalUsd, p.mwrr.return, p.equity.length);
Parameters
timeframe
params?
sessionSince?
number
cexRateBps?
number
funding?
readonly PortfolioFundingEvent[]
External capital movements (deposits/withdrawals) for the wallet, USD
valued at event time. When any of them bears on the window they define
the MWRR capital base (mwrr.capitalBasis: "funding") in place of the
trades-only proxy. They never affect PnL or volume. Venue fills are not
funding — see PortfolioFundingEvent for the sourcing rules.
Returns
Promise<PortfolioAnalytics>
fetchMyTrades()
fetchMyTrades(
ref?,since?,limit?):Promise<UnifiedTrade[]>
Defined in: packages/sdk/src/unified/exchange.ts:1575
My historical trades, newest-first across every venue.
Details
Reads the unified fill tape (getUserFills), so the scope, the window and
the limit are applied by the INDEXER rather than to an already-truncated
page. This is what makes a narrow question answerable: a ref-scoped call
returns that market's fills however old they are, where a per-venue read
would have capped at its newest 50 across all markets first and left
nothing to filter.
limit counts rows YOU receive. Fills whose pool is not in the loaded
registry are unresolvable and are skipped, so the read pages until it has
limit resolvable rows or the tape runs out — asking the query for exactly
limit would under-deliver by however many it then dropped.
ref: Restrict to one tradable (symbol or address). Omit for all.since: Lower time bound, milliseconds — same clock as UnifiedTrade.timestamp, so a value read off a previous row can be passed straight back. Converted to the indexer's unix seconds internally.limit: Max rows to return (default 50).
Parameters
ref?
string
since?
number
limit?
number = 50
Returns
Promise<UnifiedTrade[]>
fetchStatus()
fetchStatus():
Promise<{status:"error"|"ok"|"connecting";updated:number;info:TailStatus; }>
Defined in: packages/sdk/src/unified/exchange.ts:1674
Exchange health.
Details
"ok" unless a live watch is missing its socket — "connecting" while the first WS handshake is still in flight (~1s after a watch opens), "error" once a previously-live socket is lost.
Returns
Promise<{ status: "error" | "ok" | "connecting"; updated: number; info: TailStatus; }>
watchOrderBook()
watchOrderBook(
ref,limit?):Promise<UnifiedOrderBook>
Defined in: packages/sdk/src/unified/exchange.ts:1781
Streaming book off the local store: zero round-trips, current to the last block; each await resolves on the next book change.
Example (Watching the order book)
A quoting loop: wake on every book change, read the touch.
while (true) {
const book = await exchange.watchOrderBook("SOMI/USDC", 5);
const [bestBid] = book.bids[0] ?? [];
const [bestAsk] = book.asks[0] ?? [];
console.log(`bid ${bestBid} / ask ${bestAsk}`);
}
Parameters
ref
string
limit?
number = 10
Returns
Promise<UnifiedOrderBook>
watchTrades()
watchTrades(
ref,limit?):Promise<UnifiedTrade[]>
Defined in: packages/sdk/src/unified/exchange.ts:1808
Streaming public trades (the live tape), newest first.
Example (Watching trades)
Print each fill as it lands ([0] is always the latest).
while (true) {
const [latest] = await exchange.watchTrades("SOMI/USDC", 1);
if (latest) console.log(`${latest.side ?? "?"} ${latest.amount} @ ${latest.price}`);
}
Parameters
ref
string
limit?
number = 50
Returns
Promise<UnifiedTrade[]>
watchOrders()
watchOrders(
ref,limit?):Promise<UnifiedOrder[]>
Defined in: packages/sdk/src/unified/exchange.ts:1861
Streaming view of MY orders on this tradable (authenticated).
When to use
Use to learn that a resting order filled: its status flips to "closed".
Example (Waiting for an order)
Place a limit order, then block until it fully fills (or dies).
const placed = await exchange.createOrder(symbol, "limit", "buy", 10, 0.62);
while (placed.status === "open") {
const orders = await exchange.watchOrders(symbol); // resolves on the next change
const mine = orders.find((o) => o.id === placed.id);
if (!mine || mine.status !== "open") break; // filled, canceled, or expired
}
Parameters
ref
string
limit?
number = 100
Returns
Promise<UnifiedOrder[]>
watchMyTrades()
watchMyTrades(
ref,limit?):Promise<UnifiedTrade[]>
Defined in: packages/sdk/src/unified/exchange.ts:1900
Streaming view of MY fills on this tradable (authenticated).
Parameters
ref
string
limit?
number = 50
Returns
Promise<UnifiedTrade[]>
watchPrice()
watchPrice(
asset):Promise<UnifiedPrice>
Defined in: packages/sdk/src/unified/exchange.ts:1965
Streaming price off the local price store: zero round-trips, current to the last pushed tick; each await resolves on the next price change.
Details
First call hydrates the ref-counted feed watch.
Gotchas
Requires config.priceFeed to be set.
Parameters
asset
string
Returns
Promise<UnifiedPrice>
fetchPrice()
fetchPrice(
asset):Promise<UnifiedPrice|null>
Defined in: packages/sdk/src/unified/exchange.ts:1987
One-shot current price (indexer HTTP read; no watch needed), or null if the feed has no observations yet.
Parameters
asset
string
Returns
Promise<UnifiedPrice | null>
fetchPriceOHLCV()
fetchPriceOHLCV(
asset,timeframe?,since?,limit?):Promise<UnifiedOHLCV[]>
Defined in: packages/sdk/src/unified/exchange.ts:2003
OHLC price candles (EMA oracle), oldest first as [ms,o,h,l,c,vol] rows.
Details
Timeframes: 1m 1h 1d (aliases for the feed's M1/H1/D1).
Gotchas
vol is the oracle update count for the bucket (NOT trade volume).
Parameters
asset
string
timeframe?
string = "1m"
since?
number
limit?
number = 500
Returns
Promise<UnifiedOHLCV[]>
createOrder()
createOrder(
ref,type,side,amount,price?,params?):Promise<UnifiedOrder>
Defined in: packages/sdk/src/unified/exchange.ts:2070
Place an order.
Details
Works identically for every market kind: the tradable symbol carries the
outcome, side is plain buy/sell, prices and amounts are human units in the
tradable's own terms. type: "market" computes a crossing limit from the
best opposite level ± params.slippage (default 1%) and sends it IOC.
Resolves once mined, with fills decoded from the same round-trip.
Gotchas
A NO price is the NO probability — the YES-terms complement is handled internally.
The price and quantity are ALIGNED to the market's tick and lot grids before
they are sent, because the pool rejects an off-grid value outright. Alignment
never moves a value against you: a buy price rounds down, a sell price rounds
up, and a quantity always rounds down, so the order is never larger or worse
priced than you asked for. The returned UnifiedOrder carries what was
actually placed, which may differ from the arguments by up to one tick or lot
— read price and amount back from it rather than assuming your inputs.
Pre-aligning with priceToPrecision / amountToPrecision makes
this a no-op, since aligning an aligned value changes nothing.
Note priceToPrecision always rounds DOWN, for either side; this path is side-aware instead, so for a sell the two can differ by one tick.
A quantity below one whole lot throws InvalidInputError rather than silently placing a zero-quantity order.
- Throws SignerRequiredError - the exchange was built without a
privateKey/account/walletClient. - Throws InvalidInputError - unknown symbol (call
loadMarkets()first), a"limit"order with no price, or a"market"order whose opposite book side is empty so no crossing price exists. - Throws ContractRevertError - the chain rejected the order. Branch on
errorNamefor the protocol's own reason (e.g.InsufficientBalance,ExpiredOrderMustBeCancelled). - Throws RpcError - the send never got an answer from the node.
- Throws IndexerError - a symbol lookup needed the indexer and it was unreachable.
Example (Placing binary orders)
Rest a bid at 62% on YES, then take the NO book at market.
const rested = await exchange.createOrder("BTC-95000-31DEC26/USDC#YES", "limit", "buy", 25, 0.62);
console.log(rested.status, rested.filled); // "open" 0 — or "closed" if it crossed
const taken = await exchange.createOrder("BTC-95000-31DEC26/USDC#NO", "market", "sell", 10, undefined, {
slippage: 0.02, // accept up to 2% past the best bid
});
Parameters
ref
string
type
"limit" | "market"
side
"buy" | "sell"
amount
number
price?
number
params?
CreateOrderParams = {}
Returns
Promise<UnifiedOrder>
cancelOrder()
cancelOrder(
id,ref):Promise<{id:string;symbol:string;status:"canceled";info:unknown; }>
Defined in: packages/sdk/src/unified/exchange.ts:2259
Cancel a resting order by id (from createOrder / watchOrders).
Gotchas
- Throws SignerRequiredError - no signer on this exchange.
- Throws InvalidInputError - unknown symbol.
- Throws ContractRevertError - the cancel did not land;
errorNamesays why (an already-filled or already-canceled order reverts). - Throws RpcError - the send never got an answer from the node.
Example (Cancelling an open order)
const placed = await exchange.createOrder("SOMI/USDC", "limit", "buy", 10, 0.55);
if (placed.status === "open") await exchange.cancelOrder(placed.id, "SOMI/USDC");
Parameters
id
string
ref
string
Returns
Promise<{ id: string; symbol: string; status: "canceled"; info: unknown; }>
createStopOrder()
createStopOrder(
ref,type,side,amount,triggerPrice,price?,params?):Promise<UnifiedStopOrder>
Defined in: packages/sdk/src/unified/exchange.ts:2330
Place a stop / take-profit order: rests OFF the book on the market's
stop registry and fires as a market or limit order when the pool's mark
price crosses triggerPrice. The trigger direction is inferred from
which side of the current mark the trigger sits on; pass
params.triggerDirection to pin it explicitly.
Gotchas
The trigger, limit price and quantity are aligned to the market's grids, and the trigger aligns AWAY from the mark so it cannot land on it (a trigger equal to the mark fires the instant it is armed). The limit price aligns like any order price — a buy down, a sell up — so it never becomes worse than stated.
Those two rules are independent, so a limit set exactly EQUAL to the trigger
can end up one tick inside it: a buy stop at trigger 0.5004, limit 0.5004
on a 0.001 grid arms at 0.501 and rests a 0.500 bid, which may not fill.
That is deliberate — pulling the limit up to meet the trigger would make you
pay more than you asked. Set the limit a tick or two past the trigger when you
want the triggered order to cross.
- Throws SignerRequiredError - the exchange was built without a
privateKey/account/walletClient. - Throws InvalidInputError - unknown symbol, a non-spot market, a market with no stop registry, a
"limit"stop with no price, a sub-lot quantity, or no mark yet to infer the trigger direction from (passparams.triggerDirection). - Throws IndexerError - the pool's mark was needed (to infer the trigger direction, or to price a market stop's protective limit) and the indexer read did not complete. A limit stop with
params.triggerDirectionset needs no mark. - Throws ContractRevertError - the registry or the pool rejected the placement (or the operator grant / escrow approval that precedes it). Branch on
errorName. - Throws RpcError - a send or a pre-placement chain read did not complete.
Example (Placing a stop order)
A stop-loss: sell 5 if the mark drops to 1.10.
const stop = await exchange.createStopOrder("SOMI/USDC", "market", "sell", 5, 1.10);
// …later: await exchange.cancelStopOrder(stop.id, "SOMI/USDC");
Parameters
ref
string
type
"limit" | "market"
side
"buy" | "sell"
amount
number
triggerPrice
number
price?
number
params?
triggerDirection?
"above" | "below"
Returns
Promise<UnifiedStopOrder>
fetchOpenStopOrders()
fetchOpenStopOrders(
ref?):Promise<UnifiedStopOrder[]>
Defined in: packages/sdk/src/unified/exchange.ts:2447
The wallet's pending (armed, untriggered) stop orders, newest first.
Scope to one tradable with ref.
Parameters
ref?
string
Returns
Promise<UnifiedStopOrder[]>
cancelStopOrder()
cancelStopOrder(
id,ref):Promise<{id:string;symbol:string;status:"canceled";info:unknown; }>
Defined in: packages/sdk/src/unified/exchange.ts:2483
Cancel a pending stop order on its registry (refunds the keeper
payment). id comes from fetchOpenStopOrders.
Parameters
id
string
ref
string
Returns
Promise<{ id: string; symbol: string; status: "canceled"; info: unknown; }>
fetchFundingRate()
fetchFundingRate(
ref):Promise<UnifiedFundingRate>
Defined in: packages/sdk/src/unified/exchange.ts:2504
Live funding-rate + mark/index snapshot for a perp market (chain read).
Parameters
ref
string
Returns
Promise<UnifiedFundingRate>
fetchFundingRateHistory()
fetchFundingRateHistory(
ref,since?,limit?):Promise<UnifiedFundingRate[]>
Defined in: packages/sdk/src/unified/exchange.ts:2574
Historical funding rates for a perp market, oldest first (ccxt-standard shape).
Reads the INDEXED series rather than the chain: only one funding value is readable
on chain at a time. Positional (symbol, since, limit) follows the ccxt convention
set by fetchOHLCV, unlike the object-options readers on the client.
fundingRate is normalized to a per-8h fraction using each row's own
fundingWindowSec, so the series stays consistent across a parameter change. The
raw indexed row is on info for anything more specific — including spanStart /
spanEnd, which matter because a row's accrual reaches BACKWARDS from its timestamp
and a lazily-settled one can cover hours.
since is a CURSOR, not just a window bound: passing it walks FORWARD from that
point, so the ccxt pagination idiom terminates.
Details
ref: market symbol or pool addresssince: unix MILLISECONDS (ccxt convention), inclusive; acts as a forward cursorlimit: max rows (default 100)
Example (Paging through funding history)
let since = startOfHistory;
for (;;) {
const page = await exchange.fetchFundingRateHistory("BTC/USDSO:USDSO", since, 100);
if (page.length === 0) break;
consume(page);
since = page[page.length - 1].timestamp + 1; // advances
}
Without the forward ordering this loop spins: the underlying read pages newest-first,
so narrowing the window from below still returns the newest N and since never gets
past the tail. Omitting since keeps the newest-first behaviour, which is what a
"latest funding" read wants — fetchOHLCV has the same split.
Parameters
ref
string
since?
number
limit?
number
Returns
Promise<UnifiedFundingRate[]>
fetchPositions()
fetchPositions(
refs?):Promise<UnifiedPosition[]>
Defined in: packages/sdk/src/unified/exchange.ts:2605
Open perp positions (authenticated; on-chain MarginBank reads). Pass symbols to scope; defaults to every loaded perp market.
Parameters
refs?
string[]
Returns
Promise<UnifiedPosition[]>
depositMargin()
depositMargin(
ref,amount):Promise<{hash:string;info:unknown; }>
Defined in: packages/sdk/src/unified/exchange.ts:2670
Deposit collateral into the perp MarginBank (human quote units, e.g. USDso). One cross-margin balance covers every perp market.
Parameters
ref
string
amount
number
Returns
Promise<{ hash: string; info: unknown; }>
withdrawMargin()
withdrawMargin(
ref,amount):Promise<{hash:string;info:unknown; }>
Defined in: packages/sdk/src/unified/exchange.ts:2689
Withdraw free collateral from the perp MarginBank (human quote units).
Parameters
ref
string
amount
number
Returns
Promise<{ hash: string; info: unknown; }>
mintSet()
mintSet(
ref,amount):Promise<{hash:string;info:unknown; }>
Defined in: packages/sdk/src/unified/exchange.ts:2729
Mint complete sets: amount collateral → amount of EVERY outcome.
Example (Minting complete sets)
Mint 100 sets (100 USDC → 100 YES + 100 NO), then sell the side you don't want.
await exchange.mintSet("BTC-95000-31DEC26/USDC", 100);
await exchange.createOrder("BTC-95000-31DEC26/USDC#NO", "limit", "sell", 100, 0.38);
Parameters
ref
string
amount
number
Returns
Promise<{ hash: string; info: unknown; }>
burnSet()
burnSet(
ref,amount):Promise<{hash:string;info:unknown; }>
Defined in: packages/sdk/src/unified/exchange.ts:2744
Burn complete sets back to collateral.
Parameters
ref
string
amount
number
Returns
Promise<{ hash: string; info: unknown; }>
redeem()
redeem(
ref,amount,options?):Promise<{hash:string;info:unknown; }>
Defined in: packages/sdk/src/unified/exchange.ts:2800
Redeem outcome tokens for collateral after settlement. Settlement-
extraction v2 routes by marketId. When the caller omits the leg, the SDK
verifies the BinaryMarket state and reads its winner on-chain.
Example (Redeeming a winning position)
After resolution, redeem the winning side found in the balance map.
const bal = await exchange.fetchBalance();
const winning = bal["BTC-95000-31DEC26/USDC#YES"]?.total ?? 0;
if (winning > 0) await exchange.redeem("BTC-95000-31DEC26/USDC", winning);
Gotchas
A VOIDED market has no winning outcome — both legs pay — so the auto-lookup
cannot pick one and redeem throws. Pass { outcomeIdx } for the leg you
hold, and call once per leg to claim both. An unresolved market also throws
instead of guessing from an unfinalized payout vector.
Errors
- Throws InvalidInputError for an unknown or non-binary market, an invalid amount, or an omitted leg when the on-chain market is unresolved, voided, or does not store an exact one-hot resolved payout vector.
- Throws NotConfiguredError when the binary module address is not configured.
- Throws SignerRequiredError when the exchange has no usable signer.
- Throws RpcError when a market, approval, submission, or receipt request does not complete.
- Throws ContractRevertError when a market read, operator approval, or redemption transaction is rejected by a contract.
Parameters
ref
string
Market symbol or id.
amount
number
Outcome-token amount to burn, in display units.
options?
Optional leg selection. Omit it on a resolved market to
verify the terminal state and read the winner on-chain. A voided market
requires options.outcomeIdx because both legs are redeemable and only the
caller knows which one they hold.
Returns
Promise<{ hash: string; info: unknown; }>
close()
close():
Promise<void>
Defined in: packages/sdk/src/unified/exchange.ts:2842
Release every watch + channel this exchange holds and stop the client's live machinery.
Details
The instance stays usable for one-shot fetch calls.
Returns
Promise<void>