Predict Markets and Pricing SDK
The markets and pricing surface of @mysten/deepbook-v3/predict finds live expiry markets, turns a position you describe in US dollars (USD) into the tick pair the contract takes, and prices strikes onchain or locally. Strikes and Ticks explains the tick grid and the admission grid, and Oracle explains the pricer that every price read loads.
The market and pricing methods belong to client.predict, the extension that DeepBook Predict SDK registers. Reads run as simulated transactions against the full node for the configured network, so they need no indexer, no account, and no signature.
Market reads
Both market reads convert the contract's raw integers into decimals. Treat those decimals as display values, and see DeepBook Predict SDK for the raw scales behind them.
read.markets
Use read.markets to list the pool's active expiry markets with the state you need to render a board and build a mint. It returns a Promise<ActiveMarket[]>, and it takes no parameters. It reads the market IDs, then reads every market's state in a single batched simulation.
The list is the pool's active set as plp::active_expiry_markets reports it, not a tradable set; Vault describes that accessor. Settlement is a separate permissionless call, so an expired market that nobody has settled is still in the list, and quoting against it aborts. Compare each entry's expiryMs with the clock and check mintPaused before you quote.
packages/deepbook-v3/src/predict/client.ts. You probably need to run `pnpm prebuild` and restart the site.Each ActiveMarket carries these fields:
id: TheExpiryMarketshared object ID.expiryMs: The expiry as a Unix millisecond timestamp, typedbigint.tickSize: The fine strike grid in USD, which the SDK converts from the market's rawtick_sizeat the 1e9 price scale.admissionTickSize: The coarser grid in USD that every new finite mint strike must land on, unless the strike equalsreferencePrice.mintPaused: Whether the market rejects new mints. The builders do not check it, and the contract aborts a mint while the flag istrue.referencePrice: The market's reference strike in USD, which is its recordedreference_tickmultiplied bytickSize, ornulluntil someone records that tick.
packages/deepbook-v3/src/predict/client.ts. You probably need to run `pnpm prebuild` and restart the site.read.market
Use read.market to read a single market's current state, including its net asset value (NAV). It returns a Promise<MarketSummary | null>, and it resolves to null when the registry holds no market for that underlying and expiry.
It takes this parameter:
m: The market coordinates, an object with these fields:underlying: The underlying symbol, a key ofgetConfig(network).underlyings.expiryMs: The expiry as a Unix millisecond timestamp, as anumberor abigint.
packages/deepbook-v3/src/predict/client.ts. You probably need to run `pnpm prebuild` and restart the site.read.market always looks the market up in the registry rather than reading through the client's market cache, and it refreshes that cache with what it reads so later builders use the same state. It does not accept marketId. An unknown underlying throws PredictInputError. The SDK computes nav from a freshly loaded live pricer, so the call throws the PredictMoveError the chain raises when it cannot load one, for example at or after the market's expiry or while an oracle input is stale.
MarketSummary carries every ActiveMarket field plus 1 more:
nav: The market's current NAV in USD fromexpiry_market::current_nav, which is free expiry cash minus the marked liability of the exposure book, with a floor of 0.
packages/deepbook-v3/src/predict/client.ts. You probably need to run `pnpm prebuild` and restart the site.The MarketDescriptor type
Builders and reads that act on a market take a MarketDescriptor, which names the market by underlying and expiry and describes the position's strikes in USD. The SDK resolves the market and converts each strike to a tick, so you pass neither an ExpiryMarket object ID nor a tick unless you choose to pin the market.
Every descriptor carries these fields:
underlying: The underlying symbol, a key ofgetConfig(network).underlyings. An unknown symbol throwsPredictInputError.expiryMs: The market's expiry as a Unix millisecond timestamp, as anumberor abigint.marketId: An optionalExpiryMarketobject ID that the SDK uses instead of looking the market up in the registry. The SDK throwsPredictInputErrorwhen the value is not a valid Sui object ID or when that market's expiry differs fromexpiryMs. It does not compare the pinned market's underlying withunderlying, which still selects the oracle feeds the transaction passes.
The rest of the descriptor is 1 of 2 arms:
- Binary arm: Sets
sideto'up'or'down'andstriketo a USD number or the literal'reference', which resolves to the market's reference price when the SDK builds. An up position wins when settlement lands above the strike, and a down position wins when settlement lands at or below it. - Range arm: Sets
sideto'range'and bothlowerandupperto finite USD numbers, withlowerbelowupper. The position wins when settlement lands in(lower, upper], and the arm has no'reference'form.
packages/deepbook-v3/src/predict/client.ts. You probably need to run `pnpm prebuild` and restart the site.Each call reads a different part of the descriptor:
| Call | What it takes | What it reads |
|---|---|---|
tx.mint, tx.mintAmount, read.quoteMint | A full MarketDescriptor | Every field. The SDK resolves each strike to a tick and admission-checks it. |
tx.redeem, read.quoteRedeem | A full MarketDescriptor | Only underlying, expiryMs, and marketId, because the order ID identifies the position |
tx.claimSettled | underlying, expiryMs, and an optional marketId | All of them |
read.price | underlying, expiryMs, an optional marketId, and strike | All of them. The SDK checks the strike against the fine grid only. |
read.market, read.pricer | underlying and expiryMs | Both of them |
Predict Positions SDK documents the mint, redeem, and claim builders.
Strikes and the admission grid
A new mint's numeric strike has to pass the same grid checks the contract applies, and the SDK runs them before it builds the transaction, so an off-grid strike fails with a typed error instead of an onchain abort. The tick grid and the admission grid explains both grids.
For each finite boundary of a mint, the SDK runs these checks in order and throws PredictInputError at the first one that fails:
- It converts the USD value to the 1e9 raw price scale, which fails on a negative value or on a value carrying more than 9 decimals.
- It divides by the market's raw
tick_size, which fails when the strike is not a whole multiple oftickSize. - It checks that the tick falls inside the finite domain,
1throughPOS_INF_TICK - 1. - It checks that the tick lands on the admission grid, a whole multiple of
admissionTickSize, or equals the market's recorded reference tick. The SDK reads the reference tick only on this failing path.
The 2 sentinel ticks skip the admission check, so the open end of an up or down position never fails it. For a range, the SDK first throws when lower is not below upper, then runs every check on both bounds. A range bound that equals the market's referencePrice passes the admission check, because the chain admits the reference tick at any finite boundary. read.price runs the first 3 checks and skips the admission check, so it prices any strike on the fine grid.
Read the step from the market rather than hardcoding it: the cadence configuration sets it, and each market keeps the value it received at creation. Snap a target price with plain arithmetic, then trim the floating-point residue a sub-dollar step leaves behind, because the SDK throws on a value with more than 9 decimals:
const [market] = await client.predict.read.markets();
const step = market.admissionTickSize;
// Round onto the admission grid, then trim residue such as 96519.90000000001.
const strike = Number((Math.round(target / step) * step).toFixed(9));
strike: 'reference' trades at the market's reference price. The SDK reads the reference tick fresh on every build rather than from its market cache, and uses it as the finite boundary directly, so it passes every grid check by construction. While the market has no reference tick recorded, the SDK throws PredictInputError. Recording one is permissionless, so you can call expiry_market::set_reference_tick yourself rather than wait; see Reference ticks and pause control. With strike: 'reference', the SDK does not validate side at runtime and treats any value other than 'up' as down, so validate side yourself when it comes from untyped input such as JSON.
Tick helpers
The descriptor hides the tick arithmetic, and the SDK also exports the tick primitives for code that builds its own transactions or checks a position's shape. Each descriptor arm maps onto the tick pair the contract takes, where K, L, and H are USD strikes and tickSize is the market's fine grid:
| Descriptor | lowerTick | higherTick |
|---|---|---|
{ side: 'up', strike: K } | K / tickSize | POS_INF_TICK |
{ side: 'down', strike: K } | 0 | K / tickSize |
{ side: 'up', strike: 'reference' } | The market's reference tick | POS_INF_TICK |
{ side: 'down', strike: 'reference' } | 0 | The market's reference tick |
{ side: 'range', lower: L, upper: H } | L / tickSize | H / tickSize |
Tick 0 is the negative-infinity sentinel and POS_INF_TICK is the positive-infinity sentinel, as Sentinel ticks describes. The following snippet converts an up strike with the exported helpers, taking the tick size from a live market:
import { POS_INF_TICK, binaryRangeTicks, priceToRaw } from '@mysten/deepbook-v3/predict';
const [market] = await client.predict.read.markets();
const { lowerTick, higherTick } = binaryRangeTicks(
priceToRaw(105_000),
'up',
priceToRaw(market.tickSize),
);
// An up position is (strike tick, positive infinity], so higherTick is the sentinel.
console.log(lowerTick, higherTick === POS_INF_TICK);
binaryRangeTicks
Use binaryRangeTicks to convert a raw strike and a side into the tick pair an up or down mint takes. It returns { lowerTick: bigint; higherTick: bigint }, where an up side yields (tick, POS_INF_TICK) and a down side yields (0, tick).
It takes these parameters:
strikeRaw: The strike at the 1e9 raw price scale as abigint, whichpriceToRawproduces from a USD value.side: The position side, either'up'or'down'.tickSize: The market's rawtick_sizeas abigint.
packages/deepbook-v3/src/predict/ticks.ts. You probably need to run `pnpm prebuild` and restart the site.It throws PredictInputError when side is any value other than 'up' or 'down', when the strike is not a whole multiple of tickSize, or when the tick falls outside 1 through POS_INF_TICK - 1. The runtime side check catches a value from JSON or storage that the TypeScript type cannot. It checks the fine grid only, so apply the admission grid yourself as Strikes and the admission grid describes.
POS_INF_TICK
POS_INF_TICK is the positive-infinity sentinel tick, a bigint equal to 1073741823n, which is the largest value the 30-bit tick field holds. Use it as the higherTick of an up position or of any range with an open upper end. The matching onchain constant is not callable from your own package, so this export is the SDK's source for the value.
packages/deepbook-v3/src/predict/ticks.ts. You probably need to run `pnpm prebuild` and restart the site.Side
Side is the string union that binaryRangeTicks and the binary descriptor arm take for direction. An up position wins when settlement lands above the strike, and a down position wins when settlement lands at or below it. The range arm sets side: 'range' instead, which Side does not include.
packages/deepbook-v3/src/predict/ticks.ts. You probably need to run `pnpm prebuild` and restart the site.