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.5.0 or later of @mysten/deepbook-v3 carries both, so getConfig returns the identifiers on this page for either network. The Mainnet publish comes from DeepBookV3 source commit 7b169bde, and the Testnet publish from source commit 4d752fb8. The 2 source commits carry byte-identical Move sources, 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.

Both deployments went live on 2026-09-12. As of 2026-09-14 the 4 markets each network created at deployment have expired unsettled, no market has opened since, and no oracle update has landed since deployment. The Mainnet pool holds only the bootstrap minimum. 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 commit7b169bde2c2a25fee3ac9aa4686e346309bfe570
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
predict0x89aea622e7bb3bdd598bde87dde40ee31c9eed4971b9546e23ec83de3c48bbbaMarkets, trading, protocol configuration, registry, and the PLP pool
propbook0xa6c8f32015b5b41d34ee09995a2e9d7a21cdecf0e1910a50b264fd252cd33831Oracle registry, Pyth feeds, and Block Scholes stores
account0x4e1dd01465713c9d832313fed5f45c222a4d5c62a533d6da96764c8b2a245d58Account custody, balances, and the Auth authorization type
fixedMath0x52ec2d263bb9ad545d1be1c00f6779cc577ed7c0408cdea8d589c2664adc22dbFixed-point arithmetic used by the pricing math
deepbookCoreAccount0xd71b5a341dc8dc7e187517849edf61e54670b60fe496e07186c09a65fa7afdb7DeepBook core account app type
sessions0x9a068beffa019ae756f4a6d2611e9899d3c6dd888812dd9eca87535d2ac55e2aSession 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
registry0x94327dbc256a2cfabd164c2bf4b3fe033bfaa4771f7a6f716954e07cb1b4bebadeepbook_predict::registry::Registry: market lookup, cadence configuration, and capability allowlists
protocolConfig0x5fa6efbe7691809565a660adde23f0c184ca1d19cefafa4e1c88d655fe279580deepbook_predict::protocol_config::ProtocolConfig: fee rates, freshness and trading windows, pause and freeze flags, and the version watermark
poolVault0x4d227a1baf8cfcaf6e652a9182d0e63f85f9ce82cd542fdf3356bb3581be3b99deepbook_predict::plp::PoolVault: PLP custody, the supply and withdrawal queues, per-expiry accounting, and the in-flight valuation
oracleRegistry0x5d62c0d9be43efa769a708ade53fbe5bea98c15c865b64ca7c498c87b045a698propbook::registry::OracleRegistry: the canonical oracle bindings for every underlying
accountRegistry0x8c52c6f5b2374e33f4a726d9c32337b1aee3a71410d41c18faa91d938d66e4bcaccount::account_registry::AccountRegistry: account creation and app authorization
sessionsConfig0xb2fba483748aac7508676020d4e691d68239b66f8bf7c5e7634dd1a76a329317Configuration object of the sessions package
deepbookRegistry0xaf16199a2dff736e9f07a845f23c5da6df6f756eddb631aed9d24a93efc4549dDeepBook core registry that the deepbookCoreAccount app authorization references
usdcCurrency0x75cfbbf8c962d542e99a1d15731e6069f60a00db895407785b15d14f606f2b4aNative USDC's existing shared coin_registry::Currency record
plpCurrency0x28189f5a4eafbe81ea1b043db2cc00efe7067fbecf1e518531aae86a7890f8f2The 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
PLP0x89aea622e7bb3bdd598bde87dde40ee31c9eed4971b9546e23ec83de3c48bbba::plp::PLP6
DEEP0xdeeb7a4662eec9f2f3def03fb937a663dddaa2e215b8078a284d026b7946c270::deep::DEEP6

Testnet deployment​

ParameterValue
Deploymentdeepbook-predict-testnet
NetworkTestnet
Chain ID4c78adac
Source commit4d752fb82d909a821c85bcc5d3963725efb546f4
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
predict0x59d71119e990573a738dd3ff9c4c7d28d6893af69c87c1a7f3a2e90e280ce2f4Markets, trading, protocol configuration, registry, and the PLP pool
propbook0xa83f9d7651de09672a40cea371c387bf954e0f3092947670d71428d4fbc9edd9Oracle registry, Pyth feeds, and Block Scholes stores
account0x1e57d6554b99e4ca68330322c3e5c409ba1b681642726b603bf1be92c1840ca9Account custody, balances, and the Auth authorization type
fixedMath0xf3a38ba24fd40173af692cef151a43850f5935a3e026ae037fc19bb81974c6a8Fixed-point arithmetic used by the pricing math
deepbookCoreAccount0x2a3a5f51e1c566e0a77c667596a8212d036b44aeac32790991c9eb97580fabb1DeepBook core account app type
sessions0x1908eee49d7a08d74a537d7f23766b363a145517fe0d3e0d85635d1682831ffdSession support
usdc0xc028557a1ed49e42ed091e115aedefd70a442b184c18fbec5c48d5b6c0b8c184Test USDC, which the deployment publishes

Testnet shared objects​

Manifest keyObject IDWhat it holds
registry0x85cfaa857f9dfca75f6ba624eead063c4a1bc9b701201b8f21e1f9c15833ccc4deepbook_predict::registry::Registry
protocolConfig0x422a45c31128e69d0310ab3bed28bd14f730acf9f5681f70936d20c60d26bfb0deepbook_predict::protocol_config::ProtocolConfig
poolVault0x6fd178a49848387d60ccfd3e53bf06969929970246a3a7983ae5bb2a5ed52ee3deepbook_predict::plp::PoolVault
oracleRegistry0xf4ad65184b2e4e9d8e50d44b02a76c64e9607f63e3dbc362e110c99e20a17161propbook::registry::OracleRegistry
accountRegistry0x511e11f7f5d5c1043d795180657603d59e06e90fbfcafd3f90c67ff0836bbd0caccount::account_registry::AccountRegistry
sessionsConfig0xc096b97289821b27badfdf758d614bbbfa386f93d3faa409ecc1fcc6075d1186Configuration object of the sessions package
deepbookRegistry0x7c256edbda983a2cd6f946655f4bf3f00a41043993781f8674a7046e8c0e11d1DeepBook core registry that the deepbookCoreAccount app authorization references
usdcCurrency0x3122f737aa36d0a3a55a6b15a94578b52829d60d45b8d0f48c37f1739aed3ca2The test USDC coin_registry::Currency record
plpCurrency0xac6109cb41e5aa3fcfd5e76f45a23fed559f560b2b991a20c1116472de2ea50aThe PLP coin_registry::Currency record
accumulatorRoot0x0000000000000000000000000000000000000000000000000000000000000accFramework funds accumulator
clock0x0000000000000000000000000000000000000000000000000000000000000006Framework clock

Testnet coin types​

CoinTypeDecimals
Test USDC, displayed as DUSDC0xc028557a1ed49e42ed091e115aedefd70a442b184c18fbec5c48d5b6c0b8c184::usdc::USDC6
PLP0x59d71119e990573a738dd3ff9c4c7d28d6893af69c87c1a7f3a2e90e280ce2f4::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
PythFeed0x4c5d2b4c8238f04b7687c3a8df915b69c50d5f4d291fc49aea44bdb6b52e1a200x8ef99c3d14e57612d09a67bcc9b5bff568a80992df6beca2bfcd922973192faa
BlockScholesValueStore0xe83f843b15ae63045b2cea445fb8ea7ad85c5fc2a05f66db122a511e1f2c67bc0x1e5142471311505a7428b072230c9ffe8a747b3b9392720e39246af4aea08216
BlockScholesSVIStore0xfdd447be39d847747caf18ecf3355031bb33979c472581f1a5481375d3748bfb0x0f1cbefd1dd2ba08ae700a7067c32e0718edf926fa18cea80bd231c02d509ece

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-14. 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-14. 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-14 showed both ProtocolConfig objects and both registries' cadence configurations 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

The contract charges the fee, and applies its minimum, separately at each finite boundary of a range, so a two-sided range pays at least twice the minimum fee. It also applies the entry-probability band to each finite boundary as well as to the range. Design explains the fee.

Every package is at version 1 on both networks.

Public read APIs for the predict-8-21 Testnet deployment​

As of 2026-09-14 no indexed read service exists for deepbook-predict-testnet or for deepbook-predict-mainnet. The 3 public read services below still index predict-8-21, an earlier 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 come from an earlier deployment:

$ 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​

The SDK carries both deployments' identifiers, so keep them in a single network-keyed record that you resolve once at startup rather than inlining an ID at a call site. DeepBook Predict SDK shows that record, the startup deployment check, and which values change per deployment.

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: As of 2026-09-14 no market has opened or settled on either network since the markets created at deployment expired on 2026-09-12. The Mainnet 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-14.
  • 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=0x59d71119e990573a738dd3ff9c4c7d28d6893af69c87c1a7f3a2e90e280ce2f4
$ 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 DeepBookV3 main at 76b11922f3144be5bdaacb66c38c7c3524a02ff3, whose Move sources are byte-identical to both deployed source commits, 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, and its abortName stays null for these packages, because they declare their error constants as plain u64 codes rather than #[error] constants. 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.