Skip to main content

Sessions

The deepbook_sessions package lets the owner of a canonical DeepBook account authorize an ephemeral address to submit trades for that account until a fixed expiration time. An application that would otherwise prompt a wallet on every trade grants one session key, signs locally for the life of the grant, and lets the grant lapse.

Sessions is an account app. It stores grants in the account's app-local data slot and generates account app authorization only inside its own trading wrappers, so a session caller never receives a reusable Auth value and the package exposes no withdrawal or arbitrary mutation call. Every function on this page is a public fun invoked as a moveCall.

The surface spans 2 modules:

  • deepbook_sessions::sessions: Grant storage, the lifecycle calls, and every trading wrapper (source).
  • deepbook_sessions::session_config: The shared SessionsConfig object and the package version floor (source).

Sui Mainnet and Sui Testnet each run a separate deployment of the package from identical sources, and the Move snippets on this page pin DeepBookV3 main at 76b11922, whose sources match both deployments. In TypeScript, getSessionsConfig(network) from @mysten/deepbook-v3/sessions resolves the current Mainnet and Testnet deployments from version 2.5.0 and returns both IDs, plus the account registry that authorizes the app, the Predict protocol config, and the 2 extra IDs the DeepBook spot wrappers take. The full tables are on Contract Information:

ObjectMainnetTestnet
sessions package0x9a068beffa019ae756f4a6d2611e9899d3c6dd888812dd9eca87535d2ac55e2a0x1908eee49d7a08d74a537d7f23766b363a145517fe0d3e0d85635d1682831ffd
SessionsConfig0xb2fba483748aac7508676020d4e691d68239b66f8bf7c5e7634dd1a76a3293170xc096b97289821b27badfdf758d614bbbfa386f93d3faa409ecc1fcc6075d1186

A grant lives on one network's account. Sessions authorized on an earlier deployment are not visible on either current deployment.

Authority model​

A session key is trading authority over the whole account. It is not a scoped permission, and nothing in the package narrows it.

danger

A session key can trade the account's entire balance. Nothing caps notional, restricts which pools or markets are reachable, or bounds loss to adverse pricing. Fund an account that hands out ephemeral session keys with only what you are willing to put at risk, and treat the key material with the same care as the owner key for everything except withdrawal.

The grant itself carries 2 values and nothing else, the session address and its expiration timestamp in milliseconds. There is no market list, no notional ceiling, no price band, and no per-call budget to configure, because none of those fields exists.

What a session can and cannot do:

A session canA session cannot
Mint and redeem Predict positions on any market, at any size the balance covers, with caller-chosen price bounds.Withdraw funds to an address. Value leaving the account still needs owner authority.
Place and cancel DeepBook spot orders in any pool, with a caller-chosen price_limit.Grant a session, revoke a session, or extend its own expiry.
Sweep settled spot proceeds back into account custody.Outlive its expiration timestamp.

The spot wrappers are the widest surface. Each one takes a caller-chosen Pool and routes into deepbook_core_account, which first settles the account's pending accumulator funds for the pool's base asset, quote asset, and DEEP, then withdraws the account's whole balance of all 3 into the embedded balance manager for the duration of the call, and finally sweeps free balances back. The trade sits between those 2 steps, so a single session-signed spot order reaches everything the account holds in those coin types, stored plus unsettled, at whatever price_limit the caller supplies.

Revocation and expiration stop future wrapper calls. Neither unwinds a position, cancels a resting order, or reverses a trade that already executed.

The check every wrapper runs​

Each trading wrapper resolves authority through one private helper before it touches Predict or DeepBook. The helper checks 3 conditions, which must all hold:

  • The executing sessions package version is at or above the shared SessionsConfig.version_watermark.
  • The transaction sender has a stored grant on the supplied account.
  • The current clock timestamp is strictly less than that grant's expiration.

The helper mints the Auth value and the wrapper consumes it inside the same call, so the session key never holds a value it could reuse in a later command or a later transaction.

Deauthorizing the app is a pause, not a kill switch​

The registry administrator must authorize SessionsApp on the account registry before any trading wrapper can generate account authorization. That authorization is package-wide, held by the registry administrator, and it is the emergency stop for the whole lineage: deauthorize_app removes the allowlist entry, and from that moment every version of the sessions package fails inside the trading wrappers, aborting EAppNotAuthorized once a call clears the version and session checks ahead of it.

danger

deauthorize_app does not clear stored grants. Every SessionsData slot survives untouched, so re-authorizing SessionsApp makes every still-unexpired grant live again at once, with no further owner action. Treat deauthorization as a pause. To end a specific delegation, the account owner must call revoke_session.

Until the administrator authorizes SessionsApp, and again after a deauthorization, the split is clean: the trading wrappers abort, while authorize_session, revoke_session, and session_expiration_ms keep working, because they run on owner authority or no authority at all. An owner can therefore still inspect and clean up grants while the pause lasts.

Session lifecycle​

The whole lifecycle takes 3 calls. Owner authority derives from the transaction sender, so the account owner signs authorize_session and revoke_session. That derivation is what makes the flow work for accounts owned by an external address and not for object-owned accounts.

Bounds​

The contract enforces 4 bounds, and the SDK exports the duration ceiling and the address limit as MAX_SESSION_DURATION_MS and MAX_SESSIONS_PER_ACCOUNT:

BoundValueAbort
Maximum duration30 days, in millisecondsEInvalidSessionDuration
Minimum durationGreater than zeroEInvalidSessionDuration
Distinct addresses stored per account20ESessionLimitExceeded
Expiry comparisonStrict, now < expires_at_msESessionNotAuthorized

The way the contract applies those bounds has 4 consequences:

  • Absolute expiry: authorize_session reads the Clock at execution time and stores timestamp_ms + duration_ms, so a queued or retried transaction still gets its full duration rather than a window measured from when you built it.
  • Strict expiry: The grant is dead at its expiration timestamp. The wrapper asserts now < expires_at_ms, not now <= expires_at_ms. A grant whose expiry equals the current timestamp is already unusable.
  • Re-authorization: Calling authorize_session again for an address that already holds a grant overwrites its expiration in place and consumes no additional slot, which is the intended way to extend a running session.
  • Permanent data slot: Once the first grant attaches SessionsData to an account, the slot stays attached even after the map empties.

The transaction result cannot confirm a revocation​

revoke_session removes a stored grant whether it is active or already expired, frees its slot, and emits SessionRevoked. Revoking an address that holds no grant is a silent no-op: the call does not abort and emits no event, so a successful transaction proves nothing about whether the call removed a grant or none was there.

Read before and after when the difference matters. session_expiration_ms returns the stored expiration for a known address and none for one the owner never authorized or has since revoked. It does not classify the timestamp as active or expired, so compare it with the current time yourself.

Revocation deliberately takes no SessionsConfig and has no version gate, so an owner can still remove grants after the watermark retires a package version.

The Predict Sessions SDK page shows how to authorize a session and how to revoke a grant and confirm the result with the TypeScript SDK.

Enumerate an account's grants​

There is no bulk onchain read. session_expiration_ms answers one address at a time, so listing every grant means fetching the account's app-data field and decoding it offchain. The data is a VecMap<address, u64> inside a SessionsData value:

Predict Sessions SDK walks through deriving that field's ID under the canonical account rather than the wrapper, fetching it, and decoding it into a list of grants.

Nothing prunes expired grants. Time passing executes no Move code, so a dead grant counts toward the 20-address limit until the owner revokes it, and the 21st authorize_session aborts ESessionLimitExceeded even when every stored grant is long dead. List, revoke what has expired, then grant.

Predict wrappers​

An active session can call 4 Predict wrappers:

WrapperPredict callReturns
mint_exact_quantityexpiry_market::mint_exact_quantityThe new order ID, as u256.
mint_exact_amountexpiry_market::mint_exact_amountThe new order ID, as u256.
redeem_liveexpiry_market::redeem_liveOption<u256>, the replacement order ID when a partial close leaves quantity open.
redeem_settledexpiry_market::redeem_settledNothing.

Each one validates the package version and the session against the supplied account, generates app authorization internally, and passes it straight into the Predict function of the same name. Every market parameter stays caller-selected, and Predict performs all of the validation it normally would, including the gates a session caller cannot see from this package: mint_exact_quantity, mint_exact_amount, and redeem_live abort with protocol_config::ETradeWindowClosed inside the last no_trade_window_ms before expiry, deployed at 2,000 ms on both networks, and every wrapper aborts with ESnapshotInProgress if you compose it into the transaction that snapshots a pool flush. redeem_settled is outside the trade window, so a session can always close a settled position. Compared with the owner-signed Predict call, the wrapper takes no Auth argument and adds account_registry and sessions_config.

pricer is a programmable transaction block result rather than an object ID. Load it with expiry_market::load_live_pricer in a preceding command of the same transaction, exactly as the owner-signed flow does. See Predict for the market parameters, the tick pair, and the fee model behind the caps.

Slippage bounds differ from the facade​

The @mysten/deepbook-v3/sessions mint builders require the slippage caps that the @mysten/deepbook-v3/predict facade leaves uncapped when you omit them, and the session redeemLive accepts the close-side floors that the facade always sends as 0. Predict Sessions SDK describes both differences.

Click to open
Predict wrapper source

Predict Sessions SDK shows how to mint, close, and claim as a session key with the TypeScript SDK.

DeepBook spot wrappers​

An active session can also call 5 DeepBook spot wrappers that route through deepbook_core_account: place_limit_order, place_market_order, cancel_live_order, cancel_live_orders, and withdraw_settled_amounts. Order parameters stay caller-selected, and the account wrapper and DeepBook core validate them. This package does not duplicate the permissionless settled-amount withdrawal, because it needs no session authority.

The DeepBook admin-cap owner must authorize DeepbookCoreAccountApp in the supplied DeepBook registry before spot order placement works, separately from the SessionsApp authorization these wrappers already require. The 2 networks differ here, verified 2026-09-14. On Testnet the DeepBook registry 0x7c256edbda983a2cd6f946655f4bf3f00a41043993781f8674a7046e8c0e11d1 authorizes the wrapper's DeepbookCoreAccountApp, so the spot wrappers execute. On Mainnet the DeepBook registry 0xaf16199a2dff736e9f07a845f23c5da6df6f756eddb631aed9d24a93efc4549d does not authorize it, so the 5 spot wrappers abort there and the session surface on Mainnet is the 4 Predict wrappers until that changes.

Do not read that state out of the deployment manifest. The manifest is the audited initial-deployment snapshot, and its externalAuthorizations entry records whether the authorization was in place when the deployment ran, not whether it is in place now: both manifests record authorized: false, and Testnet has since gained the authorization. The DeepBook admin-cap owner grants it in a separate transaction after deployment, and the DeepBook registry is the live authority from that point on. Check the registry with deepbook::registry::assert_app_is_authorized<DeepbookCoreAccountApp> in a simulated transaction, using the deepbookRegistry and deepbookCoreAccountPackageId that getSessionsConfig(network) returns, rather than trusting the recorded flag or this page.

The TypeScript SDK does not wrap these calls on SessionsContract, and Predict Sessions SDK shows how to reach them through the generated sessionsMoveCalls bindings.

Click to open
Spot wrapper signatures

Version governance​

Publishing the package creates a shared SessionsConfig at the package's compiled-in version and transfers a SessionsAdminCap to the publisher. authorize_session and every trading wrapper assert that the executing package version is at or above the stored watermark:

Each package upgrade increments a compiled-in version constant. After clients move to the new package, the SessionsAdminCap holder calls bump_version_watermark on that package, which derives the target from the executing package rather than from an argument, so a caller cannot select an arbitrary version or use an older package to retire a newer one:

Advancing the watermark retires authorization and trading in older package versions. Reading expirations and revoking grants stay available, so owners can inspect and remove delegations without a trading-capable package version.

Events​

The lifecycle emits 2 events. Predict and DeepBook core keep emitting their own trading events, and the wrappers do not duplicate them.

authorize_session emits SessionAuthorized for a new grant and for a re-authorization alike, carrying the new expires_at_ms in both cases. revoke_session emits SessionRevoked only when it actually removes an existing grant. Expiration emits nothing, because nothing executes when a deadline passes, and a no-op revocation emits nothing either. An indexer that wants a current view of live grants therefore has to apply expiry itself, using the timestamps the authorization events carry.

Errors​

Error numbers repeat across modules, so resolve an abort against the module named in the abort location rather than against the number alone:

ErrorValueModuleRaised when
EInvalidSessionDuration0sessionsduration_ms is zero or greater than 30 days.
ESessionNotAuthorized1sessionsThe sender holds no grant on the supplied account, or the current timestamp is at or past the grant's expiration.
ESessionLimitExceeded2sessionsThe account already stores 20 distinct addresses and the address in the call is not one of them.
EPackageVersionDisabled0session_configThe executing package version is below the shared configuration's watermark.
EVersionWatermarkNotAdvanced1session_configbump_version_watermark runs from a package whose version is not above the stored watermark.
EAppNotAuthorized1account_registryThe account registry does not authorize SessionsApp, so no trading wrapper can generate authorization.

For the account object these grants attach to, see Accounts and Custody. For the market parameters the Predict wrappers forward, see Predict.