DeepBook Predict SDK
The DeepBook Predict SDK builds every Predict transaction, reads live Predict state through your Sui client, and decodes the receipts that executed transactions emit. It ships as the /predict subpath of the @mysten/deepbook-v3 TypeScript package, and DeepBook Predict explains the markets, positions, and pool behind each call.
Install
Install @mysten/deepbook-v3 version 2.5.0 or later from npm, together with its @mysten/sui peer dependency at version 2.30.0 or later. The package requires Node.js 22 or later.
- npm
- Yarn
- pnpm
npm install @mysten/deepbook-v3 @mysten/sui
yarn add @mysten/deepbook-v3 @mysten/sui
pnpm add @mysten/deepbook-v3 @mysten/sui
Predict needs no separate package. The DeepBookV3 SDK documents the package root, which covers DeepBook spot and margin.
Subpaths
The package ships 3 subpaths beside its root, each a separate entry in its exports map, and Predict uses all 3. Importing a subpath never loads the spot and margin code at the package root. /predict and /sessions do load the /account module, because Predict accounts and session grants both build on the shared account primitive. The 3 subpaths export these surfaces:
@mysten/deepbook-v3/predict: Thepredictclient extension and thePredictClientclass it registers, plus what the client's inputs and outputs use: deployment configuration, unit and tick helpers, typed errors, and receipt types. It also exports thepricingnamespace for local board pricing and the primitives in Compose your own transactions. Predict Accounts SDK, Predict Markets and Pricing SDK, Predict Positions SDK, and Predict Liquidity SDK document the client's functions by area.@mysten/deepbook-v3/account: The shared account primitive that every Predict account builds on:AccountContract,getAccountConfig, the generatedaccountMoveCalls,accountRegistryMoveCalls, andaccountEventsnamespaces, and theAccountandAccountWrapperBinary Canonical Serialization (BCS) types. See Predict Accounts SDK.@mysten/deepbook-v3/sessions: Time-limited session keys that trade for an account until a fixed expiry:SessionsContract,getSessionsConfig, theMAX_SESSION_DURATION_MSandMAX_SESSIONS_PER_ACCOUNTlimits, and the generatedsessionsMoveCallsandsessionConfigMoveCallsnamespaces. See Predict Sessions SDK.
Create the client
Register the Predict client as an extension on any Sui client that exposes the core API. The examples select the network in a single configuration module, which the other examples reach directly or through the client module:
import { getConfig, getDeployment, getUnits } from '@mysten/deepbook-v3/predict';
// The SDK carries a deployment record for Testnet and for Mainnet, so `getConfig`
// resolves either. This constant is the single place these examples select a
// network. Change it here and every other file follows. They default to Testnet,
// where the quote coin is a mintable test coin; on Mainnet it is native USDC.
export const NETWORK = 'testnet' as 'testnet' | 'mainnet';
export const FULLNODE_URL =
NETWORK === 'mainnet'
? 'https://fullnode.mainnet.sui.io:443'
: 'https://fullnode.testnet.sui.io:443';
// One underlying is live on this deployment.
export const UNDERLYING = 'BTC';
// The SDK carries the IDs of whichever deployments its release was cut against.
// Assert the name at startup, so a later SDK release that moves a network to a
// new deployment fails loudly here rather than quietly trading against a
// deployment these examples were never checked against.
export const EXPECTED_DEPLOYMENT = {
testnet: 'deepbook-predict-testnet',
mainnet: 'deepbook-predict-mainnet',
}[NETWORK];
export const DEPLOYMENT = getDeployment(NETWORK);
if (DEPLOYMENT.deployment !== EXPECTED_DEPLOYMENT) {
throw new Error(
`Expected DeepBook Predict deployment ${EXPECTED_DEPLOYMENT}, got ` +
`${DEPLOYMENT.deployment} (chain ${DEPLOYMENT.chainId}, ` +
`deepbookv3 commit ${DEPLOYMENT.sourceCommit}).`,
);
}
// Package IDs, the shared registry, protocol config, and pool vault objects, the
// quote coin type, and the per-underlying oracle IDs all come from the SDK, so
// no deployment identifier is hardcoded in these examples. Always read the quote
// coin from `CONFIG.quoteCoinType`: on Mainnet it is native USDC, and on Testnet
// it is a test coin with the same `usdc::USDC` module path that displays as DUSDC.
export const CONFIG = getConfig(NETWORK);
// Scale constants the deployment owns: position quantities are whole
// `positionLotSize` lots, amounts are `quoteCoinDecimals`-decimal USDC, and
// probabilities, prices, and rates are fixed point at `fixedPointScale`.
export const UNITS = getUnits(NETWORK);
The client module extends a gRPC client with the Predict client for that network:
import { SuiGrpcClient } from '@mysten/sui/grpc';
import { predict } from '@mysten/deepbook-v3/predict';
import { FULLNODE_URL, NETWORK } from './config.js';
// `$extend` registers the Predict facade on the Sui client, so everything below
// reaches it at `client.predict`. Any client exposing the core API works,
// whether gRPC or JSON-RPC.
export const client = new SuiGrpcClient({
network: NETWORK,
baseUrl: FULLNODE_URL,
}).$extend(predict({ network: NETWORK }));
// Reads run through the client's own transaction simulation and object reads, so
// `client.predict.read.*` needs neither an indexer nor a Predict server.
//
// The SDK never signs and never holds keys. Every `client.predict.tx.*` builder
// returns a `Transaction` for a wallet, dapp-kit, or your own signer to execute.
// Execute with events included whenever you intend to decode a receipt, because
// the decoders read the events' canonical BCS bytes.
Use predict to create the registration that $extend takes. It returns a SuiClientRegistration, and the Predict client lands on client.predict unless you pass another name. It takes these parameters:
network: The recorded deployment to address, Testnet or Mainnet. The SDK resolves it withgetConfig(network).config: An optionalPredictConfigfor a deployment of your own. When you pass it, the SDK uses it in place of the recorded deployment and does not consultnetwork.name: The optional property name the client registers under, which defaults to'predict'.
packages/deepbook-v3/src/predict/client.ts. You probably need to run `pnpm prebuild` and restart the site.To construct the client without $extend, pass network, your Sui client, and an optional config to new PredictClient:
packages/deepbook-v3/src/predict/client.ts. You probably need to run `pnpm prebuild` and restart the site.The Predict client exposes 3 groups of members:
client.predict.tx: Each builder returns a new, unsignedTransactionfor your wallet or signer to execute, and the SDK never signs or holds keys. The 4 builders that resolve a market,mint,mintAmount,redeem, andclaimSettled, return aPromise<Transaction>.client.predict.read: Each read runs a simulated transaction or a core object read against the full node, so reads need neither an indexer nor a Predict server.client.predict.decode: Each decoder turns an executed transaction's events into a typed receipt with no network call. See Decode transaction results.
The client also carries wrapperIdFor(owner), which Predict Accounts SDK covers, and cfg, the PredictConfig that the client uses.
Configuration
The SDK records the Testnet and Mainnet deployments, so you never transcribe a package ID or object ID. getConfig(network) returns the PredictConfig for Testnet or Mainnet, the same record the client resolves from its network option:
packages/deepbook-v3/src/predict/config/index.ts. You probably need to run `pnpm prebuild` and restart the site.PredictConfig holds everything a builder or read needs to address a single deployment:
network: The network name for Testnet or Mainnet, or'custom'for a configuration you build yourself.packages: Thepredict,account, andpropbookpackage IDs.objects: The 5 shared object IDs that builders and reads pass as arguments.quoteCoinType: The coin type that every amount settles in.coinTypes: Theplpanddeepcoin types.units: The lot size, fixed-point scale, and decimals the deployment records.underlyings: One entry per underlying symbol, such asBTC, each with its propbook underlying ID and its 3 oracle object IDs.
packages/deepbook-v3/src/predict/config/types.ts. You probably need to run `pnpm prebuild` and restart the site.Resolve the configuration once at startup and pass the result down, rather than inlining an ID at a call site. Every value in it is deployment-specific, and most differ between Mainnet and Testnet. The config.ts sample in Create the client keeps them in a single network-keyed record: a NETWORK constant selects Testnet or Mainnet, and every other value derives from it.
The following resolvers accept only the Mainnet and Testnet network names and throw a plain Error on any other value:
getConfig,getDeployment, andgetUnitson/predictgetAccountConfigon/accountgetSessionsConfigon/sessions
A misconfigured environment therefore fails at startup instead of sending a transaction to a package that does not exist. Resolving once gives you a single place to switch networks and a single place to fail.
getDeployment(network) returns the deployment's deployment name, network, chainId, and sourceCommit. Assert the deployment name at startup as well, because a later SDK release can intentionally move a network to a newer deployment. /account and /sessions re-export getDeployment and getUnits, so code that imports only one of them can run the same check.
packages/deepbook-v3/src/deployments/index.ts. You probably need to run `pnpm prebuild` and restart the site.The quote coin is the value most worth reading rather than assuming:
import { getConfig, getDeployment } from '@mysten/deepbook-v3/predict';
const network = process.env.PREDICT_NETWORK === 'mainnet' ? 'mainnet' : 'testnet';
const { deployment, chainId } = getDeployment(network);
// 'deepbook-predict-mainnet' on chain 35834a8a, or 'deepbook-predict-testnet' on chain 4c78adac.
const { quoteCoinType } = getConfig(network);
// Mainnet: native USDC. Testnet: the test coin that displays as DUSDC. Same `usdc::USDC` module path.
Each value changes per deployment for its own reason:
| Value | Why it changes per deployment |
|---|---|
packages.predict, packages.account, packages.propbook | Each deployment publishes its own packages. Move call targets and event type prefixes derive from them. |
objects.registry, objects.protocolConfig, objects.poolVault | Each deployment shares its own Predict state objects, which every trading and liquidity call takes as arguments. |
objects.oracleRegistry, objects.accountRegistry | The Propbook and account packages share their own registries per deployment. |
quoteCoinType | Mainnet quotes in native USDC. Testnet quotes in a mintable test coin with the same module path and decimals but a different package. |
coinTypes.plp | Each deployment's Predict package defines its own PLP type. The type keeps the ID of the package that first defined it, while packages.predict moves to the latest package on an upgrade, so read the type rather than building it. |
units | Each deployment records its own lot size, fixed-point scale, and decimals. The 2 current deployments record the same values, and the client reads only positionLotSize. |
underlyings | Each underlying carries its Propbook ID and its 3 oracle object IDs, and rebinding can change them. |
Keep live protocol state out of this record. Market IDs, expiries, and reference ticks change as markets expire and new ones open, and cadence terms and oracle observations change independently of any deployment, so read them at runtime.
For a deployment of your own, pass a PredictConfig as config to predict or to new PredictClient. getConfig covers only the 2 recorded deployments.
Units
The client takes and returns human-readable values: US dollar (USD) amounts and prices as decimals, probabilities from 0 to 1, and PLP shares as raw integers. It converts each one to the onchain integer the contracts expect:
| Value | You pass or receive | Onchain form |
|---|---|---|
Quote coin amounts, such as amountUsdc, maxCost, and read.balance | A USD decimal, such as 12.5 | An integer with 6 implied decimals |
quantity | The maximum payout in USD, at $1 per contract | An integer with 6 implied decimals, in whole $0.01 lots |
Strikes: strike, lower, and upper | A USD price, such as 105000 | An integer with 9 implied decimals, on the market's admission grid |
Probabilities, such as maxProbability and entryProbability | A decimal from 0 to 1, per $1 of payout | An integer with 9 implied decimals |
PLP shares, such as the withdrawPlp argument and read.plpBalance | A raw bigint share count | The same integer, for a 6-decimal coin |
expiryMs | Unix milliseconds: you pass a number or bigint, and the SDK returns a bigint | Unix milliseconds |
Order IDs and queue indexes are bigint values that the SDK passes through unchanged.
The SDK converts inputs with exact string arithmetic. It throws PredictInputError for a negative or malformed value, for a value carrying more decimals than its unit allows, and for a probability outside 0 to 1. Amounts and quantities allow 6 decimals, and strikes and probabilities allow 9, so round a value you derive, such as a maxCost computed from a quote, before you pass it. Parameters typed number | string, such as amountUsdc and minUsdcOut, also accept a decimal string, which avoids binary float residue entirely.
Outputs typed number are display values. The SDK converts them from the raw integer through a JavaScript number, so a raw value above Number.MAX_SAFE_INTEGER loses its lowest digits. Quotes and receipts carry a raw block of exact bigint values beside their display fields, and read.plpBalance and the plpTotalSupply field of read.pool() are raw bigint values already. Use the raw values for accounting and the display values for rendering.
/predict exports the conversions the client uses:
usdcToRawandrawToUsdc: Convert quote coin amounts at 6 decimals.priceToRawandrawToPrice: Convert strikes and prices at 9 decimals.probabilityToRawandrawToProbability: Convert probabilities at 9 decimals.probabilityToRawthrows outside 0 to 1.U64_MAX: The largestu64value, which the SDK sends for a mint cap you omit.
packages/deepbook-v3/src/predict/units.ts. You probably need to run `pnpm prebuild` and restart the site.The helpers use fixed 6-decimal and 9-decimal scales. Of the configuration's units, the client reads only positionLotSize, to check that each quantity is a whole lot.
Errors
Almost every failure you handle in application code surfaces as either of 2 typed errors that /predict exports:
PredictInputError: The SDK rejects an argument before it builds anything. Common causes are a strike off the admission grid, a quantity that is not a whole lot, an amount with more decimals than its coin allows, and an unknown underlying.PredictMoveError: A simulation hits a Move abort. It carries 3 fields:modulenames the aborting module,codeis the exact abort code as abigint, andabortNameis theE-prefixed constant name when the node can decode one.
The node decodes a constant name only for an error constant that its module declares with #[error], and only over gRPC or GraphQL. The Predict, account, and sessions packages declare their error constants as plain u64 codes, so abortName is null for their aborts, and module with code identifies the error. A simulation that fails without a Move abort throws a plain Error carrying the node's message.
Resolve module before you look up code. Error numbers restart at 0 in every module, so the same number means something different depending on where it came from. Find the page for a Move abort maps each module to the reference page carrying its table.
packages/deepbook-v3/src/predict/errors.ts. You probably need to run `pnpm prebuild` and restart the site.Both SDK quote methods, read.quoteMint and read.quoteRedeem, dry-run the same transaction their matching builder produces, against the real account and the real fee path, so they raise the same errors the write would, including insufficient balance. Treat a successful quote as a preflight. read.quoteMint sends only the options you pass. Called with { quantity }, it carries no slippage cap, and an options object that also holds maxCost or maxProbability passes them through, so the quote aborts on a cap as the mint would. The Move expiry_market::quote_mint is a different function with a similar name: it prices only, and it checks no account, no slippage cap, and no exposure capacity.
The SDK never executes a transaction, so a write that fails after your signer submits it arrives as a failed execution result rather than a thrown error. Pass that result's status error to decodeMoveAbort to get the same PredictMoveError, or null when the failure is not a Move abort:
packages/deepbook-v3/src/predict/errors.ts. You probably need to run `pnpm prebuild` and restart the site.The following sample handles both kinds of failure:
import { decodeMoveAbort, PredictInputError, PredictMoveError } from '@mysten/deepbook-v3/predict';
try {
await client.predict.read.quoteMint(owner, descriptor, { quantity: 3 });
} catch (error) {
if (error instanceof PredictMoveError) {
console.error(error.module, error.code, error.abortName);
} else if (error instanceof PredictInputError) {
console.error(error.message);
} else {
throw error;
}
}
const result = await client.core.signAndExecuteTransaction({ transaction: tx, signer: keypair });
if (result.$kind === 'FailedTransaction' && !result.FailedTransaction.status.success) {
const abort = decodeMoveAbort(result.FailedTransaction.status.error);
console.error(abort?.module, abort?.code);
}
Decode transaction results
client.predict.decode turns the events of an executed transaction into typed receipts. Each decoder is pure: it reads only the result you pass and makes no network call.
A decoder needs the result's events, each with its type tag and its BCS payload, so execute with include: { events: true } and pass the transaction result:
const result = await client.core.signAndExecuteTransaction({
transaction: tx,
signer: keypair,
include: { events: true },
});
if (result.$kind === 'FailedTransaction') {
throw new Error('Transaction failed');
}
const { orderId } = client.predict.decode.mint(result.Transaction);
packages/deepbook-v3/src/predict/decode.ts. You probably need to run `pnpm prebuild` and restart the site.The decoders read each event's BCS bytes rather than its JSON rendering, so a result decodes the same from any transport. They match events against the package IDs in the client's configuration, so decode with a client whose configuration matches the network that executed the transaction. An event that matches but carries no BCS payload throws PredictInputError.
Each singular decoder throws PredictInputError unless the result holds exactly 1 matching event, so a result executed without events makes it throw. mints, redeems, and claims are plural forms that return every matching receipt in event order, for a programmable transaction block (PTB) that batches several actions, and they return an empty array for a result that carries no events. The other decoders have no plural form.
The following table lists every decoder, the event it reads, and the page that documents its receipt:
| Decoder | Event | Receipt | Documented in |
|---|---|---|---|
createManager | AccountCreated | CreateManagerReceipt | Predict Accounts SDK |
deposit | Deposited | BalanceChangeReceipt | Predict Accounts SDK |
withdraw | Withdrawn | BalanceChangeReceipt | Predict Accounts SDK |
builderCode | BuilderCodeSet | BuilderCodeReceipt | Predict Accounts SDK |
mint, mints | OrderMinted | MintReceipt | Predict Positions SDK |
redeem, redeems | LiveOrderRedeemed | RedeemReceipt | Predict Positions SDK |
claim, claims | SettledOrderRedeemed | ClaimReceipt | Predict Positions SDK |
plpRequest | SupplyRequested or WithdrawRequested | PlpRequestReceipt | Predict Liquidity SDK |
plpCancel | RequestCancelled | PlpCancelReceipt | Predict Liquidity SDK |
Compose your own transactions
Each client.predict.tx call creates and returns its own finished Transaction, so 2 facade calls cannot share 1 PTB. The only composed flow the client builds is tx.deposit with { create: true }, which creates, funds, and shares an account in a single PTB. To put a Predict call in a PTB with anything else, build that PTB yourself from what version 2.5.0 exports:
loadLivePricer(config, args): Adds theexpiry_market::load_live_pricercommand and returns its result, the market-boundPricerthat every live mint and live redeem borrows. The/sessionsPredict wrappers take this result as theirpricer.generateAuth(cfg): Adds the command that mints owner authority for the transaction sender, anAuthvalue that the next account-loading call in the same PTB must consume.deriveAccountWrapperId(cfg, owner): Returns an owner's wrapper ID from aPredictConfigalone, with no client and no chain read. It matchesclient.predict.wrapperIdFor(owner).toGeneratedConfig(cfg): Projects aPredictConfigonto the flat configuration object thatloadLivePricerand the generated bindings read.- The Predict bindings: a
MoveCallsnamespace for each Predict module with a callable function, such asplpMoveCalls,expiryMarketMoveCalls,predictAccountMoveCalls, andregistryMoveCalls. - The Predict event layouts:
orderEvents,vaultEvents,configEvents, andbuilderCodeEvents. accountMoveCallsandaccountRegistryMoveCalls: The generated bindings for the account package, that/accountexports.sessionsMoveCallsandsessionConfigMoveCalls: The generated bindings for the sessions package, that/sessionsexports. They include the DeepBook spot session calls thatSessionsContractdoes not wrap.
Each generated binding returns a function that adds its Move call to a Transaction, so pass it to tx.add. It takes the Move arguments by parameter name. Pass config: toGeneratedConfig(cfg) and the binding fills in the package ID and the shared objects the call needs. An owner-authorized call takes the Auth that generateAuth(cfg) adds earlier in the same PTB. The following sample queues a PLP supply from an account:
import { Transaction } from '@mysten/sui/transactions';
import { generateAuth, getConfig, plpMoveCalls, toGeneratedConfig } from '@mysten/deepbook-v3/predict';
const cfg = getConfig('testnet');
const tx = new Transaction();
// Owner authority for the sender, which the next account-loading call consumes.
const auth = tx.add(generateAuth(cfg));
// WRAPPER_ID is the account's wrapper ID, such as client.predict.wrapperIdFor(owner).
// Queue 100 USDC, in base units, with no floor on the PLP the flush pays out.
tx.add(
plpMoveCalls.requestSupply({
config: toGeneratedConfig(cfg),
arguments: { wrapper: WRAPPER_ID, auth, amount: 100_000_000n, minPlpOut: 0n },
}),
);
Predict Liquidity SDK covers the floor, the queue, and how the flush fills the request.
The following sample loads a pricer for a single market into a PTB you build yourself:
import { Transaction } from '@mysten/sui/transactions';
import { getConfig, loadLivePricer, toGeneratedConfig } from '@mysten/deepbook-v3/predict';
const cfg = getConfig('testnet');
const tx = new Transaction();
// The market-bound `Pricer`, as a result that later commands in this PTB borrow.
const pricer = tx.add(
loadLivePricer(toGeneratedConfig(cfg), {
expiryMarketId: MARKET_ID,
...cfg.underlyings.BTC,
}),
);
packages/deepbook-v3/src/predict/tx/trade.ts. You probably need to run `pnpm prebuild` and restart the site.The args parameter takes the market ID and the 3 oracle object IDs that cfg.underlyings[symbol] carries. Pass pricer to a /sessions Predict wrapper such as SessionsContract.mintExactQuantity, which Predict Sessions SDK walks through.