Skip to main content

Contract Information

DeepBook Predict runs on Sui Mainnet and Sui Testnet as 2 separate deployments, deepbook-predict-mainnet and deepbook-predict-testnet. Version 2.3.0 or later of @mysten/deepbook-v3 carries both, so getConfig('mainnet') and getConfig('testnet') each return the identifiers on this page. Both deployments build from the deepbook-predict-mainnet branch of the DeepBookV3 repository. The Testnet sources are identical to the Mainnet source commit apart from 2 lines of documentation comment and attribute in packages/propbook/sources/feeds/pyth_feed.move, so one source pin serves both networks.

caution

Every identifier on this page belongs to one of the 2 current deployments. The current deployments supersede every earlier Testnet deployment, and the accounts, positions, and markets on those earlier deployments are not visible on the current ones, so ignore identifiers from them in local configuration files and scripts.

As of 2026-09-10 the Mainnet deployment is onchain with its configuration in place, its pool holds only the bootstrap minimum, and no market has been tradable there yet. Read read.markets(), read.pool(), and the ProtocolConfig object rather than assuming either network's state.

Mainnet deployment

The deployment manifest is the stable identity record. It names the chain, the source commit, and the checkpoint from which an indexer would start:

ParameterValue
Deploymentdeepbook-predict-mainnet
NetworkMainnet
Chain ID35834a8a
Source commit14a7e8f822e0397df2d61fbfbce3ef21086891c2
Source branchdeepbook-predict-mainnet
Indexing start checkpoint320360057

Mainnet packages

Predict is 7 packages rather than one. Move call targets, event type prefixes, and the PLP coin type all derive from these IDs. On Mainnet the usdc entry is Circle's native USDC package, which Predict links against rather than publishes:

PackageIDContents
predict0x774f9555865a7b96e31ec982f62d5bdf9d74bf57a6468b5ed41340c231afd7dfMarkets, trading, protocol configuration, registry, and the PLP pool
propbook0x2a63e378ab0138cdcb12651a093f3799f8b8e4f6c927ce6de0e8bc7ded0ec49dOracle registry, Pyth feeds, and Block Scholes stores
account0xa6f1b22aaeb429f6fd8f01c13f605256e00876457fd122da8a0e2c1045c75929Account custody, balances, and the Auth authorization type
fixedMath0xeb8212402af172cac87ef533a61a899334a75a745489432aaa31872fdc5ecb42Fixed-point arithmetic used by the pricing math
deepbookCoreAccount0xebcd5b403ee78021e3f78d1c77e2117a676caa8a9754dedb99e7b102b0d81872DeepBook core account app type
sessions0xebfa125baee571c1f8c903c1dd54a5bc57949db82469b836c32eca251756c82aSession support
usdc0xdba34672e30cb065b1f93e3ab55318768fd6fef66c15942c9f7cb846e2f900e7Circle native USDC, linked as an existing dependency

Mainnet shared objects

Predict has no single top-level object. It splits state across shared objects that you pass by ID into moveCall arguments:

Manifest keyObject IDWhat it holds
registry0xc98e430d82bed9f777288061faa1988da705a97db7f0da9f215ab372c53a2362deepbook_predict::registry::Registry: market lookup, cadence configuration, and capability allowlists
protocolConfig0x55e8800bcb31b792683ca4385bd533716e2a9721364236a69ee2c3bee61dd8f2deepbook_predict::protocol_config::ProtocolConfig: fee rates, freshness and trading windows, pause and freeze flags, and the version watermark
poolVault0x3f80653bf9324162254d89724276aae7b36ba7c83d4650214afbf266da98f492deepbook_predict::plp::PoolVault: PLP custody, the supply and withdrawal queues, per-expiry accounting, and the in-flight valuation
oracleRegistry0x1ff67a8499b0af7c0fbb26ba82c319be25413af93d9a961ee60f8d1229e7de7cpropbook::registry::OracleRegistry: the canonical oracle bindings for every underlying
accountRegistry0x210ba485d973b5356e9078318837137efabdd5ffb9eeb3705ba7eef3340324fcaccount::account_registry::AccountRegistry: account creation and app authorization
sessionsConfig0x0c5f64365b3bf1f67827d6975893190079cc048247f9ff0b08390aa9ee697c5cConfiguration object of the sessions package
deepbookRegistry0xaf16199a2dff736e9f07a845f23c5da6df6f756eddb631aed9d24a93efc4549dDeepBook core registry that the deepbookCoreAccount app authorization references
usdcCurrency0x75cfbbf8c962d542e99a1d15731e6069f60a00db895407785b15d14f606f2b4aNative USDC's existing shared coin_registry::Currency record
plpCurrency0x1abd3bdc52345ad87467c06fd2d6e49cc1e5d6262bf488a3b814e98f9adfa607The PLP coin_registry::Currency record
accumulatorRoot0x0000000000000000000000000000000000000000000000000000000000000accFramework funds accumulator that delivers payouts, builder fees, referral fees, and liquidity fills
clock0x0000000000000000000000000000000000000000000000000000000000000006Framework clock

Mainnet coin types

CoinTypeDecimals
USDC0xdba34672e30cb065b1f93e3ab55318768fd6fef66c15942c9f7cb846e2f900e7::usdc::USDC6
PLP0x774f9555865a7b96e31ec982f62d5bdf9d74bf57a6468b5ed41340c231afd7df::plp::PLP6
DEEP0xdeeb7a4662eec9f2f3def03fb937a663dddaa2e215b8078a284d026b7946c270::deep::DEEP6

Testnet deployment

ParameterValue
Deploymentdeepbook-predict-testnet
NetworkTestnet
Chain ID4c78adac
Source commita928bd2da4b71abf7c4ea6f66abec2763308331e
Source branchdeepbook-predict-testnet
Indexing start checkpoint381301489

Testnet packages

On Testnet the usdc entry is a test coin that Predict publishes itself. It shares the usdc::USDC module path with native USDC, displays with the symbol DUSDC and the name DeepBook USDC, and is mintable:

PackageIDContents
predict0x25d075d2de915feda7ab9b8f855afe59cbcd4b2ce96f75324940c810f84da018Markets, trading, protocol configuration, registry, and the PLP pool
propbook0xa04a43dbbfb123feec96691a22809d8a87e9ac37ae9356079c439750145aa92bOracle registry, Pyth feeds, and Block Scholes stores
account0x543139156cb90d1a73df33b5dca37d7c8bdce62506c7874ebc854afd319b98f1Account custody, balances, and the Auth authorization type
fixedMath0x5d3b5af5cb48685e7641d4b0b0293f7af709151ac76367a1e791d545d422134aFixed-point arithmetic used by the pricing math
deepbookCoreAccount0x2dbb8e6a5b9a8ac6201c7aeff6de0c7e7bca5dcd8d6c16538cb1a7447a298071DeepBook core account app type
sessions0x10c91d168dc4a04357f9d23795a204092ca46e5b97f8b9ef26fea95664d5bb38Session support
usdc0xc028557a1ed49e42ed091e115aedefd70a442b184c18fbec5c48d5b6c0b8c184Test USDC, which the deployment publishes

Testnet shared objects

Manifest keyObject IDWhat it holds
registry0xd21fab5444128eb65c5f6299564f583db72ad84d17581a4febe68878127100d5deepbook_predict::registry::Registry
protocolConfig0xfeda745dfdef2cd9721c2ff1e1de538d103bc6012469d0eec10ea5de67f636d0deepbook_predict::protocol_config::ProtocolConfig
poolVault0xd6b96a77d5acfd4f4a660cbd1098ed62abf2317e85a01274d1a4eeef19603767deepbook_predict::plp::PoolVault
oracleRegistry0xda71af88a8b9d01b6913937a84c5a63bb4e1015a7a2142185ec9ef73f675615fpropbook::registry::OracleRegistry
accountRegistry0xb889caefe327cbdea58e529e3b9e10dc93f1e661ddef32824d28527edf8d6385account::account_registry::AccountRegistry
sessionsConfig0x63cde7c7d846f15c51802ec3d44ba44918658e3155b0d2e0cb26d6fc1b4dadd8Configuration object of the sessions package
deepbookRegistry0x7c256edbda983a2cd6f946655f4bf3f00a41043993781f8674a7046e8c0e11d1DeepBook core registry that the deepbookCoreAccount app authorization references
usdcCurrency0x3122f737aa36d0a3a55a6b15a94578b52829d60d45b8d0f48c37f1739aed3ca2The test USDC coin_registry::Currency record
plpCurrency0x45677450205be53f4e8bebf520694e580aced3fb13016f279961f6891fceed13The PLP coin_registry::Currency record
accumulatorRoot0x0000000000000000000000000000000000000000000000000000000000000accFramework funds accumulator
clock0x0000000000000000000000000000000000000000000000000000000000000006Framework clock

Testnet coin types

CoinTypeDecimals
Test USDC, displayed as DUSDC0xc028557a1ed49e42ed091e115aedefd70a442b184c18fbec5c48d5b6c0b8c184::usdc::USDC6
PLP0x25d075d2de915feda7ab9b8f855afe59cbcd4b2ce96f75324940c810f84da018::plp::PLP6
DEEP0x36dbef866a1d62bf7328989a10fb2f07d769f4ee587c0de4a0a256e57e0a58a8::deep::DEEP6

Shared facts across both networks

Each ExpiryMarket is its own shared object, one per underlying and expiry. Market IDs are not deployment configuration: read them from registry::expiry_market_id onchain or from read.markets() at runtime. Each network's Predict package defines the PLP type, so it differs per network and changes with every redeployment. The quote coin type differs per network too, so always take it from getConfig(network).quoteCoinType rather than from a symbol.

Underlyings and oracle objects

One underlying is live on each network. Oracle data lives in the separate propbook package, and every priced call takes concrete oracle objects as arguments:

FieldMainnetTestnet
SymbolBTCBTC
NameBTC_USDBTC_USD
Propbook underlying ID11
Pyth Lazer feed ID11
Block Scholes source ID11
PythFeed0x44bd2e00549973bb6a58785836e97ed80b4979e67dc3bf312c997e56053e53760x7179188b3a27758c53668a4ec09a81be114dea5ffabcf367f4e720a26db8ee59
BlockScholesValueStore0xe7c83ee4ac0c300d68d8d4c3d265d78287b4767dcbc5f279cd6f210b6248d4930x77dee47fb870753c740439f436e0c30c00c60f13f171c901125b3d60f89518e5
BlockScholesSVIStore0x8e5568aebf490c2adfd4e010a9b4ddcf7c774183f58c98fcd438bae1fc68f5610xe8aa80965d201e2cb18359c3aa315e3abc5bbf807a10a59d6e011ef41be5db7a

Passing an oracle object that is not the current canonical binding aborts rather than pricing against the wrong feed, so resolve the 3 oracle IDs from OracleRegistry at runtime instead of trusting a cached copy. See Oracle for the binding accessors, the freshness windows, and which call needs which object.

App authorization

The deepbookCoreAccount package declares the account_data::DeepbookCoreAccountApp type under its own package ID on each network. App authorization is live registry state rather than deployment configuration: a manifest's externalAuthorizations entry records only whether the authorization was in place when the deployment ran, and an admin grants or revokes it in a separate transaction afterward.

On both networks, the AccountRegistry authorizes PredictApp, SessionsApp, and DeepbookCoreAccountApp as of 2026-09-10. That is live registry state, so read it with account_registry::is_app_authorized<App> rather than assuming it.

The DeepBook core registry is a separate authorization, and it differs by network as of 2026-09-10. On Testnet, the DeepBook registry 0x7c256edbda983a2cd6f946655f4bf3f00a41043993781f8674a7046e8c0e11d1 authorizes DeepbookCoreAccountApp. On Mainnet, the DeepBook registry 0xaf16199a2dff736e9f07a845f23c5da6df6f756eddb631aed9d24a93efc4549d does not, so the session spot wrappers cannot work there yet. Either state can change, and the authorization belongs to that registry's administrator rather than to Predict. Check it with deepbook::registry::assert_app_is_authorized<DeepbookCoreAccountApp>, which aborts when the app is not authorized. Sessions covers what that authorization gates.

Units and constants

Predict mixes 2 scales, and confusing them is the fastest way to misprice a trade. Probabilities, prices, and rates are fixed-point integers, while coin amounts use the coin's own decimals. The units are identical on both networks:

ConstantValueApplies to
Fixed-point scale1000000000 (1e9)Probabilities, prices, tick sizes, and rates
Quote coin decimals6USDC amounts, premiums, fees, and payouts
PLP coin decimals6PLP share amounts
DEEP coin decimals6DEEP amounts
Position quantity decimals6Position quantities
Position lot size10000Position quantities, which must be whole multiples
Minimum premium1000000The premium of every mint, in USDC base units
Timestamp unitUnix millisecondsOnchain timestamps, expiries, and event windows

A probability of 500000000 is 0.5. A price of 110000000000000 is 110,000.00. An amount of 1000000 is 1 USDC. A position quantity of 10000 is one lot, 0.01 contracts.

The lot size is the granularity of a quantity, not a tradable minimum. strike_exposure_config::assert_mint_admission rejects a mint whose premium falls below 1000000 base units, which is 1 USDC, with abort code 3, EPremiumBelowMinimum. The premium is the quantity multiplied by the quoted entry probability, and max_entry_probability caps that probability at 990000000, so the smallest quantity any market accepts is about 1.02 contracts, and the floor rises as the strike moves further out of the money. Size a mint by the rule that predicts admission: quantity multiplied by the quoted entryProbability must reach 1 USDC. A quantity of 1 is below the floor on every live market.

Read every quantity that arrives as a JSON string with decimal or bigint tooling. Postgres NUMERIC columns serialize as strings, and parseFloat silently loses precision on them.

Configuration

Both deployments ship the same initial configuration, and a chain read on 2026-09-10 showed both ProtocolConfig objects matching it field for field. Configuration is mutable protocol state, so read the live values from ProtocolConfig and registry::cadence_configs rather than pinning the ones below.

Cadences

Each underlying has 6 cadence slots, and a cadence ID addresses each one. A cadence counts as enabled when its window size is greater than zero, and only the 1m and 5m cadences qualify:

IDNamePeriod (ms)EnabledTick sizeAdmission tick sizeMaximum expiry allocationInitial expiry cashWindow size
01m60000Yes1000000010000000001000000000020000000002
15m300000Yes1000000010000000001000000000020000000002
21h3600000No00000
31d86400000No00000
41w604800000No00000
51mo2592000000No00000

Read each column as follows:

  • Tick size: The price-axis resolution, in fixed-point price units. Both enabled cadences use 10000000, which is 0.01 USD.
  • Admission tick size: The coarser grid a finite position boundary must land on, also in fixed-point price units. Both enabled cadences use 1000000000, which is 1 USD, or every 100 ticks. A boundary tick must be a multiple of admission_tick_size / tick_size ticks.
  • Maximum expiry allocation: The cadence cap on cash allocated to one expiry market, in USDC base units: 10,000 USDC. This same value also scales the market's inventory-impact curve.
  • Initial expiry cash: The cash the vault seeds a new market with, in USDC base units: 2,000 USDC.
  • Window size: How many expiries of this cadence can be live at once, so 4 markets are live at any time across the 2 enabled cadences. Zero disables the cadence.

The only finite boundary a market admits off its admission grid is its own reference tick. Read admission_tick_size from the market rather than assuming a grid, because cadence terms are mutable. Market creation on a disabled cadence aborts.

Protocol values

ValueDeployedNotes
Pyth spot freshness2000 msLive pricing aborts on an older observation
Block Scholes price freshness2000 msSpot and forward
Block Scholes SVI freshness60000 msThe stochastic volatility inspired (SVI) surface changes more slowly
no_trade_window_ms2000Live mints and live redeems abort with ETradeWindowClosed inside the last 2 seconds before expiry
max_valuation_window_ms300000A pool flush must finish within 5 minutes of its snapshot, or a cap holder restarts it
max_lp_pool_value500000000000The pool accepts at most 500,000 USDC of liquidity
lp_request_limit_flush_attempts1The first flush whose mark misses a queued request's floor cancels and refunds it
PLP supply fee0
PLP withdraw fee20000000.2 percent
Protocol reserve profit share10000000010 percent
Referral fee rate10000000010 percent
Exponentially weighted moving average (EWMA) congestion penaltyDisabled

The strike-exposure template matches earlier deployments:

  • Base fee of 10 percent
  • Minimum fee of 2.2 percent
  • Entry probability band of 1 percent to 99 percent
  • Backing buffer lambda of 0.31
  • Inventory impact rate of 0
  • Expiry fee window of 1 day with a 1x maximum multiplier

Every package is at version 1 on both networks.

Public read APIs for the previous Testnet deployment

As of 2026-09-10 no indexed read service exists for deepbook-predict-testnet or for deepbook-predict-mainnet. The 3 public read services below still index the previous-generation predict-8-21 Testnet deployment, so their responses carry that deployment's package and object IDs and none of the identifiers above. Nothing on the SDK path needs them: every client.predict.read method runs against a full node, and the event decoders parse a transaction result locally.

The tables stay here for readers who still work against predict-8-21, and because the shape of these services is what a future service for the current deployments is likely to follow. Each exposes read-only JSON GET endpoints with permissive browser cross-origin resource sharing (CORS), and none accepts writes:

ServiceBase URLPrimary data
Predicthttps://predict-server-v4.testnet.mystenlabs.comMarkets, market state, positions, vault state, protocol events, and indexed configuration
Propbookhttps://propbook-server-v4.testnet.mystenlabs.comOracle bindings, Pyth observations, and Block Scholes spot, forward, and SVI observations
Accounthttps://account-server-v4.testnet.mystenlabs.comAccount custody state, balances, activity, portfolio, and app authorizations

These hostnames are operational Testnet endpoints. They are not part of any audited manifest, and their operators can retire or repoint them independently of the onchain packages. Confirm which deployment a service indexes before you build on it: its /markets response names the Predict package through the market IDs it returns.

Market and event data

EndpointUse
GET /statusLatest onchain checkpoint, per-pipeline lag, and maximum lag
GET /markets?active=true&limit=50Future unsettled market-creation records, ordered by expiry
GET /markets/:expiry_market_id/stateCreation record plus reference tick, mint-pause state, and settlement when present
GET /markets/:expiry_market_id/open-interestOpen interest for one market
GET /accounts/:account_id/positions?status=open&limit=100Positions one account holds
GET /vaults/:pool_vault_id/statePool vault state: a current block with idle_balance_after, total_supply, pool_value, and active_market_nav, plus latest_flush
GET /configIndexed protocol configuration: trading_paused, protocol_config_id, max_lp_pool_value, and max_lp_pool_value_is_unbounded
GET /eventsAllowlisted event resources and their filter profiles
GET /events/:resource?limit=100One raw event resource, paged oldest-first

The positions path keys on the canonical account ID rather than the AccountWrapper ID, the same as the account service paths. Account and portfolio data covers the 2 ways to get that ID and what a wrapper ID returns instead.

The GET /events listing includes liquidated-order-redeemed, deep-staked, deep-unstaked, and trading-loss-rebate-claimed, which correspond to contract features that no longer exist. No deployment emits them and requesting one returns 503. The event modules under Live onchain events are what the packages actually emit.

Market discovery returns the creation record for each live market. The following sample shows only the market's identity and cadence terms, and its values are the previous deployment's:

$ curl 'https://predict-server-v4.testnet.mystenlabs.com/markets?active=true&limit=1'
[
{
"expiry_market_id": "0x4c3f93d201c09bbd0523f6f698ad6bacee23760a4a884592c4df86511166f5f0",
"pool_vault_id": "0x2a31f592d8fd3d0781e2233770d02d67797890ac82c3d18796d7eb0997896602",
"propbook_underlying_id": 1,
"expiry": 1788378840000,
"tick_size": "10000000",
"admission_tick_size": "1000000000",
"max_expiry_allocation": "50000000000",
"initial_expiry_cash": "10000000000",
"base_fee": "100000000",
"min_fee": "22000000",
"min_entry_probability": "10000000",
"max_entry_probability": "990000000",
"kind": "market_created"
}
]

Use expiry_market_id for market-scoped reads, pool_vault_id for vault reads, and propbook_underlying_id to join a market to its oracle objects. Identifiers in sample responses are illustrative: the market above settled long ago, so its ID resolves to nothing useful.

Oracle data

EndpointUse
GET /statusIndexer freshness for the Propbook pipelines
GET /oracle-bindingsCanonical oracle object IDs per underlying and oracle kind
GET /oracles/:pyth_feed_id/pyth/latestLatest indexed Pyth observation
GET /oracles/:value_store_id/block-scholes/spot?limit=10Block Scholes spot observations
GET /oracles/:value_store_id/block-scholes/forward?limit=10Block Scholes forward observations
GET /oracles/:svi_store_id/block-scholes/svi?limit=10Block Scholes SVI observations

Judge vendor freshness from each observation's source timestamp and landing time from its checkpoint timestamp. Do not sort observations by source timestamp alone, because an older source observation can land later.

Account and portfolio data

Account and position paths key on the canonical account ID, not on the shared AccountWrapper object ID and not on the owner address. An owner has 2 derived addresses, and only one of them answers here:

EndpointUse
GET /statusIndexer freshness for the account pipelines
GET /accounts/:account_id/portfolioPortfolio summary for one account
GET /accounts/:account_id/balancesCustodied balances for one account
GET /accounts/:account_id/activityAccount activity history
GET /appsApp authorizations that the account registry records

You have 2 routes to that ID:

  • Derive it from the owner address: new SessionsContract(getSessionsConfig(network)).deriveAccountId(owner), from the @mysten/deepbook-v3/sessions subpath, computes it offchain with no chain read. client.predict exposes only wrapperIdFor, which returns the wrapper, so the Predict client alone cannot produce this ID.
  • Read it off an event: Every Predict order event carries the account ID in its account_id field, and decode.createManager(result) returns it as accountId.
caution

A wrapper ID passed where an account ID belongs fails silently. The services answer 200 with an empty array rather than an error, so a portfolio renders as empty and an automated redeemer reads it as nothing to close. Confirm the ID against an account you know holds positions before you trust an empty response.

The wrapper ID stays correct onchain. Every Move call that takes the shared AccountWrapper object takes it, and client.predict.wrapperIdFor(owner) derives it. Only the read service paths key on the account ID. Accounts and Custody describes the 2 derived objects, and Sessions walks through deriving each one.

Take every path from the tables on this page rather than guessing at one. The plausible variants /vault, /pool, /protocol-config, and /accounts/:account_id/apps all return 404.

Freshness and pagination

Check /status on each service before relying on recent data:

$ curl 'https://predict-server-v4.testnet.mystenlabs.com/status'
$ curl 'https://propbook-server-v4.testnet.mystenlabs.com/status'
$ curl 'https://account-server-v4.testnet.mystenlabs.com/status'

Each response reports status, the latest onchain checkpoint, and every pipeline's indexed checkpoint and time lag. Treat a non-OK status, or an unexpectedly old pipeline you depend on, as incomplete recent data rather than as an empty application state.

Raw event pages need care, because they page differently from the named history feeds:

  • The service orders raw event pages oldest-first by (checkpoint_timestamp_ms, checkpoint, tx_index, event_index). Preserve that whole tuple when merging resources.
  • The first page fixes a checkpoint snapshot. Continue with the opaque page.next_cursor, repeating every resource filter unchanged, until page.has_next_page is false.
  • Raw event windows use from_ms, an inclusive lower bound, and to_ms, an exclusive upper bound, both in Unix milliseconds.
  • Named history endpoints use start_time and end_time in Unix seconds instead, so never reuse one family's timestamps in the other.
  • Named history feeds are generally newest-first, the opposite of raw event pages.
  • An unknown identifier generally returns null components, an empty array, or a null point lookup rather than a 404.

Manage configuration across networks

Never inline these values at a call site. Each one is deployment-specific, and all of them differ between Mainnet and Testnet. Keep them in one network-keyed record, resolve it once at startup, and pass the result down. The examples do exactly that: a single NETWORK constant selects 'testnet' or 'mainnet', and everything else derives from it:

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);

getConfig, getDeployment, getUnits, getAccountConfig, and getSessionsConfig all accept 'mainnet' or 'testnet' and throw a plain Error on any other value, so a misconfigured environment 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. Assert the deployment name at startup as well, because a later SDK release can intentionally move a network to a newer deployment. 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.
ValueWhy it changes per deployment
packages.predict, packages.account, packages.propbookEach deployment publishes its own packages. Move call targets and event type prefixes derive from them.
objects.registry, objects.protocolConfig, objects.poolVaultEach deployment shares its own Predict state objects, which every trading and liquidity call takes as arguments.
objects.oracleRegistry, objects.accountRegistryThe Propbook and account packages share their own registries per deployment.
quoteCoinTypeMainnet quotes in native USDC. Testnet quotes in a mintable test coin with the same module path and decimals but a different package.
coinTypes.plpDerived from the Predict package ID, so it moves with every republish.
unitsThe SDK records lot size, fixed-point scale, and decimals per deployment rather than assuming them.
underlyingsEach underlying carries its propbook ID and its 3 oracle object IDs, and rebinding can change them.

Read market IDs, expiries, reference ticks, cadence terms, and oracle observations at runtime rather than adding them to this record. They are live protocol state, not deployment configuration, and they change as markets roll.

Testnet and Mainnet differences

The 2 deployments run the same contracts and the same initial configuration. What differs is around them:

  • Quote asset and funding: Testnet quotes in a test coin you can request for free, and Testnet SUI is free. Mainnet quotes in native USDC, there is no faucet, and any top-up path that assumes free replenishment needs replacing.
  • Activity: Testnet creates markets every minute and settles them. On Mainnet, as of 2026-09-10, no market has been tradable and the pool holds only the bootstrap minimum. read.markets() lists live-and-unsettled markets, which includes an expired market nobody has settled, so check each entry's expiry before you quote against it.
  • Read services: None exists for either current deployment as of 2026-09-10.
  • DeepBook core authorization: The session spot wrappers work on Testnet and not yet on Mainnet, per App authorization.

Structure your code so none of these become assumptions:

  • Lifecycle state: Read it, do not infer it from time. Check is_settled or try_settlement_price on the market rather than deriving settlement from an expiry timestamp. A market settles when someone calls try_settle successfully, and the gap after expiry has no fixed duration.
  • Decimals and scale: Take them from the configured deployment. An amount scaled by a hardcoded exponent silently misprices every mint.
  • Admission grid: Read it from the market. Admission tick size is mutable protocol state.
  • Testnet timing: Treat timing you measure on Testnet as an observation rather than a guarantee. Oracle update frequency and settlement latency are properties of the relayers, not of the contracts.

Live onchain events

Stream events directly from a full node when a client needs them as they land, and back them with your own store for history. Predict emits its events from 4 modules under the Predict package: config_events for lifecycle and configuration, order_events for trading, vault_events for the pool and its queues, and builder_code_events for builder attribution. The following table lists the ones an integrator subscribes to most often, not every event each module declares:

Event typeEmitted when
config_events::MarketCreatedA lifecycle-cap holder creates and shares a new ExpiryMarket
config_events::ReferenceTickSetA market records its reference tick
config_events::MarketSettledA market settles, with settlement_source 0 for Pyth and 1 for Block Scholes
config_events::ExpiryMarketMintPausedUpdatedAn admin pauses or unpauses minting on one market
config_events::NoTradeWindowUpdatedAn admin changes no_trade_window_ms
order_events::OrderMintedA trader opens a position
order_events::LiveOrderRedeemedA trader closes or reduces a live position before expiry
order_events::SettledOrderRedeemedThe owner, or a permissionless caller on the owner's behalf, claims a settled position
vault_events::SupplyRequestedAn account queues a liquidity supply request
vault_events::SupplyFilledThe flush fills a queued supply at its mark and mints PLP
vault_events::WithdrawFilledThe flush fills a queued withdrawal at its mark and burns PLP
vault_events::RequestCancelledThe owner cancels a queued request, or the flush refunds it
vault_events::FlushExecutedThe flush drains the liquidity queues at one frozen net asset value (NAV)
vault_events::FlushRestartedA new pool valuation supersedes one that never finished
builder_code_events::BuilderCodeCreatedA caller creates a derived BuilderCode
builder_code_events::BuilderCodeSetAn account changes its sticky builder code attribution
builder_code_events::BuilderFeesClaimedA builder code owner claims accumulated builder fees

The SupplyFilled and WithdrawFilled payloads name their quote amounts usdc_amount, fee_usdc, and usdc_remaining, and WithdrawRequested carries min_usdc_out.

Stream events over gRPC

Stream Predict events with SubscriptionService.SubscribeEvents, filtering on the Predict package ID, and page the same events historically with LedgerService.ListEvents under an identical filter. Sui full nodes no longer serve the older WebSocket subscription API, so do not build on sui_subscribeEvent.

Subscribe to market creation and settlement on Testnet. For Mainnet, swap in the Mainnet Predict package ID and fullnode.mainnet.sui.io:443:

$ PACKAGE=0x25d075d2de915feda7ab9b8f855afe59cbcd4b2ce96f75324940c810f84da018
$ ENDPOINT=fullnode.testnet.sui.io:443
$ METHOD=sui.rpc.v2.SubscriptionService/SubscribeEvents

$ grpcurl -format text -d "
read_mask {
paths: \"event_type\"
paths: \"json\"
paths: \"sender\"
paths: \"checkpoint\"
paths: \"transaction_digest\"
paths: \"transaction_index\"
paths: \"event_index\"
}
filter {
terms {
literals {
event_type {
event_type: \"${PACKAGE}::config_events::MarketCreated\"
}
}
}
terms {
literals {
event_type {
event_type: \"${PACKAGE}::config_events::MarketSettled\"
}
}
}
}
" "$ENDPOINT" "$METHOD"

Swap the method for sui.rpc.v2.LedgerService/ListEvents to page the same events historically. The filter is disjunctive normal form, so the server ORs the terms blocks: the request above matches either event type and scopes both to one deployment.

Subscriptions begin at the current tip and accept no resume point, so recovering events missed during a disconnect always requires a ListEvents backfill. Those mechanics are the same for every gRPC event consumer, and the gRPC pages document them once, for all of them:

MarketSettled is the correct trigger for an automated claim worker, because it fires when the contract actually records a settlement price rather than when a market expires. After settlement, expiry_market::redeem_settled_permissionless lets anyone claim a settled position on the owner's behalf, and the payout still lands in the owner's account, so a worker closes out user positions without holding their keys. Make that worker idempotent: at-least-once delivery is a property of the backfill-and-subscribe pattern, so the same event can reach a handler twice after a reconnect.

Source pointers

The links below pin every module to the Mainnet source commit 14a7e8f822e0397df2d61fbfbce3ef21086891c2, which the deepbook-predict-mainnet branch carries. The Testnet deployment's sources are identical apart from 2 lines in pyth_feed.move, so the same links serve both networks. There is no entry fun in any Predict package: every integrator call is a public fun invoked as a moveCall, and several of them return values that a later command in the same transaction must consume.

AreaSource
Markets, quoting, trading, and settlementpackages/predict/sources/expiry_market.move
Market catalog, cadences, and capabilitiespackages/predict/sources/registry/registry.move
Protocol configuration and gatingpackages/predict/sources/config/protocol_config.move
PLP pool, liquidity queues, and valuationpackages/predict/sources/plp/plp.move
Pool valuation capabilitypackages/predict/sources/capabilities/pool_valuation_cap.move
Account custody and authorizationpackages/account/sources/account.move
Oracle registry and bindingspackages/propbook/sources/registry.move
Pyth and Block Scholes feedspackages/propbook/sources/feeds

Find the page for a Move abort

A Move abort names the module it came from and a number. Error numbers restart at 0 in every module, so a code looked up in the wrong table reads as an unrelated failure. Resolve the module first, then read that page's error table:

ModuleReference page
expiry_market, strike_exposure_configPredict
order, strike_exposureStrikes and Ticks
plp, lp_book, pool_accounting, expiry_cashVault
pricing and the propbook modulesOracle
registry, market_managerRegistry
account, account_registry, predict_accountAccounts and Custody
sessionsSessions
protocol_config, whose gates run ahead of every callRegistry

These deployments renumbered or extended several tables, so a code memorized from an earlier deployment can point at the wrong constant:

ModuleCodes on these deployments
plpRenumbered: EMissingExpiryValuation 0, ENotBootstrapped 1, EAlreadyBootstrapped 2, EBelowMinBootstrapLiquidity 3, EBelowMinFeeIncentiveSponsorship 4, EMaxLiveExpiryMarketsExceeded 5. New: EValuationSnapshotNotSealed 6, EExpiryPricerAlreadySnapshotted 7, EIncompleteValuationSnapshot 8, EExpiredMarketNotSettled 9, EValuationWindowExpired 10, ESnapshotStageOpen 11.
protocol_configCodes 0 to 5 unchanged. New: ESnapshotInProgress 6, ETradeWindowClosed 7.
expiry_marketCodes 0 to 9 unchanged. New: EMarketNotPendingValuation 10, EMintCostAboveMaxPayout 11.
registryNew: EPoolValuationCapNotValid 3, EPoolValuationCapNotFound 4.
config_constantsNew: EInvalidMaxValuationWindowMs 23, EInvalidNoTradeWindowMs 24.
strike_payout_treeNew: EStaleValuationSnapshot 3, ESnapshotSeqNotIncreasing 4.

PredictMoveError from the TypeScript SDK carries module and code for exactly this lookup, plus abortName when the client speaks gRPC or GraphQL. A first mint most often aborts in strike_exposure_config, whose code 3 is EPremiumBelowMinimum, or in protocol_config with code 7, ETradeWindowClosed, when you took the quote inside the last 2 seconds before expiry.