Units and scales

This page lists every numeric convention the SDK uses, which surface uses it, and the helpers that convert between them. The exchange API (SomniaMarkets) speaks in human units. The engine (SomniaMarketsClient, the trader) and the indexer speak in raw integers.

Amounts and prices

ConventionTypeUsed byExample
Human unitsnumberEvery SomniaMarkets method and struct10 shares, price 0.62
Raw token unitsbigint on writes and chain reads; decimal string on indexer readsSomniaMarketsClient, Trader, Market rows, every info payload620000n for 0.62 with 6 decimals

A raw amount is scaled by the token's decimals. The scale differs per market kind:

Market kindQuantity scalePrice scale
SpotbaseDecimals of the pairquoteDecimals of the pair (quote per one base)
PerpbaseDecimalsquoteDecimals
Binarycollateral decimals (DECIMALS, 6, on every deployed venue)collateral decimals; the price of one YES share in collateral, which equals the YES probability

SpotMarket.baseDecimals and quoteDecimals come from the indexed row. Spot markets are not assumed to have 6 decimals: SOMI/USDso has 18 and 18, WBTC/USDso has 8 and 18.

Grids

FieldMeaning
tickSizeRaw price step. UnifiedMarket.precision.price is the number of decimal places that step has.
lotSizeRaw quantity step. UnifiedMarket.precision.amount is its decimal places.
minQuantityRaw minimum order quantity. UnifiedMarket.limits.amount.min is its human value.

createOrder aligns price and quantity to these grids before sending. A buy price rounds down, a sell price rounds up, a quantity rounds down. priceToPrecision and amountToPrecision round down for both sides.

Conversion helpers

All helpers are exported from the root entry and default decimals to DECIMALS (6).

HelperSignatureBehaviour
fromHuman(human: number | string, decimals?) => bigintConverts a human value to raw. A number with more fraction digits than decimals is rounded half away from zero: fromHuman(0.1234567, 6) is 123457n. A string with at most decimals fraction digits is parsed exactly. Throws InvalidInputError on a non-finite number.
toHuman(raw: bigint | string, decimals?) => numberConverts raw to a number. Not exact past about 15 significant digits; suitable for display.
toHumanString(raw: bigint | string, decimals?) => stringConverts raw to an exact decimal string.
probabilityToPrice(p: number, decimals?) => bigintA YES probability in [0, 1] to a raw YES price. Throws InvalidInputError outside the range, including NaN.
priceToProbability(raw: bigint | string, decimals?) => numberA raw YES price to a probability.
balanceFloor(raw: bigint, decimals?) => numberThe largest human value that converts back to at most raw through fromHuman. Spending balanceFloor(raw) never exceeds raw.
floorRawBalance, ceilRawAmount(raw, decimals, quantum)Quantum-aligned floor and ceiling.

Basis points

UnitScaleUsed by
Basis points (bps)1 bps = 0.01 %Venue fees: getMaxVenueFeeBps, MarketFees, getMaxLeverage limits, cexRateBps in portfolio analytics
Basis points × 10001,000 = 1 bpsBuilder fees: builderFeeBpsTimes1k on order placement, maxFeeBpsTimes1k on approveBuilder, getMaxBuilderFeeBpsTimes1k
Leverage in bps of 1×10,000 = 1.00×, 200,000 = 20×Perp leverage: getPerpLeverage, setPerpLeverage, getPerpMaxLeverage

5_000n as maxFeeBpsTimes1k allows up to 5 bps.

Fixed-point rates

UnitScaleUsed by
Wad1e18SomniaLend health factor (maxUint256 when the account has no debt); perp funding rates (FUNDING_PRECISION, 1_000_000_000_000_000_000n)
Ray1e27SomniaLend rates and indexes. lendRayRateToApy(rateRay) returns a number APY; RAY is 10n ** 27n.
Price feed1e18 (PRICE_FEED_DECIMALS)Oracle index prices. LivePrice.price and ema are number; raw is the exact string.

A perp funding rate is a fraction per calculation window, 28,800 seconds (8 hours) on every live pool. fundingRate8h, fundingRate1h, fundingRatePerInterval, and annualizedFundingRate renormalise it. UnifiedFundingRate.fundingRate from fetchFundingRate is already per 8 hours as a number.

Time

UnitUsed by
Unix seconds, as stringIndexer rows: createdAt, lastTradeAt, placedAtTimestamp, candle bucketStart
Unix milliseconds, as numberEvery SomniaMarkets struct (timestamp), datetime as ISO-8601; since parameters of fetchTrades, fetchMyTrades, fetchOHLCV; reactivity schedule timestamps
Unix milliseconds, as bigintNative SomniaBlock consensus timestamps
Unix nanoseconds, as bigintOrder expiry: expireTimestampNs on placeSpotOrder, placePerpOrder, and the batch requests. The SDK default is about 50 years ahead.

Candle intervals: CANDLE_INTERVALS is [60, 300, 900, 3600, 14400, 86400] seconds. TIMEFRAMES maps the exchange strings 1m, 5m, 15m, 1h, 4h, 1d to those seconds. Price-feed candles use 1m, 1h, 1d on the exchange and M1, H1, D1 on the client.

UnifiedOHLCV rows are [ms, open, high, low, close, volume]. On fetchOHLCV the last element is base volume. On fetchPriceOHLCV it is the number of oracle updates in the bucket, not volume.

Struct conventions

StructConvention
UnifiedBalancefree === total and used is 0 for spot and binary holdings. Funds locked in a resting order live in the pool, not the wallet, and do not appear. Perp collateral is locked in the MarginBank and does not appear either; client.getMarginAccount(marginBank, account) reads it.
UnifiedOrder.typePresent only on the order createOrder returns. Absent on orders read from the indexer (fetchOrders, fetchOpenOrders, watchOrders): the pools do not emit the order type.
UnifiedStopOrder.typeAlways present: the stop registry emits it.
UnifiedOrder.idThe pool's order id as a decimal string; the transaction hash when nothing rested.
infoStructs that wrap a native row or write result carry the raw payload under info. Aggregate and tuple results such as UnifiedBalance, UnifiedBalances, and UnifiedOHLCV do not.

Identifiers

IdentifierFormat
Order idDecimal string on the exchange API (UnifiedOrder.id), on live-store rows, and on indexer rows; bigint on trader results and chain reads such as getOwnOpenOrdersOnchain. When nothing rested, UnifiedOrder.id falls back to the transaction hash.
Fill id${blockNumber}_${logIndex}
Trade id in MarketActivityTRADE: followed by the fill id
ERC-6909 outcome id(pool << 72) | (nonce << 8) | outcomeIndex; outcomeId, decodeOutcomeId, marketKey encode and decode it
Market type tagbytes4; MARKET_TYPE_BINARY_V1 is 0x06c65d9f

Order enumerations

ConstantValues
ORDER_TYPELIMIT: 0, FILL_OR_KILL: 1, MARKET: 2 (immediate-or-cancel), POST_ONLY: 3
SELF_MATCHING_OPTIONCANCEL_TAKER: 0 (SDK default), CANCEL_MAKER: 1
ORDER_KIND_SIDE["BUY_YES", "SELL_YES", "BUY_NO", "SELL_NO"], indexed by the pool's kind
UnifiedOrderStatus"open", "closed" (fully filled), "canceled" (explicit cancel, or an unfillable immediate-or-cancel remainder), "expired"
CreateOrderParams.timeInForce"GTC" (default), "IOC", "FOK", "PO"