DeepBook
Local DeepBook deployments, pools, seed liquidity, Pyth price feeds, and known deployments.
Trade on a DeepBook central-limit order-book DEX running
on your local network. Add deepbook(...) to your stack, then read the resolved pool and coin ids
from the generated bindings and drive trades with the DeepBook SDK. It runs in one of three modes:
local— publish the DeepBook Move package onto your local Sui network, create pools, and (optionally) seed them with resting liquidity and local Pyth price feeds. This is the mode you use for development: the whole DEX is created from scratch every boot and torn down with the stack.known— point at an existing DeepBook deployment (testnet / mainnet). The zero-config per-network shortcut (deepbookFor(network).testnet()/.mainnet()) derives the ids from the@mysten/deepbook-v3SDK, so there's nothing to copy and nothing to drift;.known({ packageId, registryId })is the raw-id override. Nothing is published; your app reads and trades against the live DEX.override— wrap deployment ids you manage yourself, without publishing or managing DeepBook.
Local mode
Local mode publishes the DeepBook package, initializes optional Pyth feeds, creates pools, and seeds
those pools. Use the generic coin helper for every pool
coin so funding, generated bindings, and app code all read the same resolved coin metadata.
import {
DEEP_PRICE_FEED_ID,
SUI_PRICE_FEED_ID,
account,
coin,
deepbook,
defineDevstack,
localPackage,
sui,
} from '@mysten-incubation/devstack';
const localnet = sui();
// The publisher both deploys DeepBook and seeds the pool, so fund it with ample
// SUI for the publish + pool-creation gas and the quote-side seed deposit. For
// clarity the example app splits this across dedicated accounts
// (`publisher`/`usdcPublisher`/`pythPublisher`); any funded `account(...)` works.
const publisher = account('publisher', {
kind: 'ephemeral',
funding: [{ coin: 'sui', amount: 1_000_000_000_000n }],
});
// DeepBook's Move tree is pulled straight from upstream and cached host-side per
// (url, rev) — no vendored copy to maintain. Pin `rev` to a concrete SHA for
// reproducible bindings. (You can also use `sourcePath: './move/deepbook'` to
// publish a local checkout instead.)
const deepbookPackage = localPackage('deepbook', {
git: {
url: 'https://github.com/MystenLabs/deepbookv3.git',
subdir: 'packages/deepbook',
rev: '5411ef3aa93f7722409b2a85047baa3d4d830c07', // pin a concrete SHA
},
publisher,
capture: {
registryId: '::registry::Registry',
adminCapId: '::registry::DeepbookAdminCap',
deepTreasuryId: '::deep::ProtectedTreasury',
},
});
const pythPackage = localPackage('pyth', {
git: {
url: 'https://github.com/MystenLabs/deepbook-sandbox.git',
subdir: 'sandbox/packages/pyth',
rev: 'e62fa7df04b444a2ad72362802fd2ad3e8e61408', // pin a concrete SHA
},
publisher,
});
const suiCoin = coin.builtin('sui');
const deep = coin.fromPackage(deepbookPackage, 'DEEP');
const dex = deepbook({
mode: 'local',
publisher,
package: deepbookPackage,
// Surface the captured DEEP treasury id in the bindings so the SDK
// extension can read DEEP_TREASURY_ID (see "Connect the SDK" below).
deepTreasuryIdKey: 'deepTreasuryId',
pyth: {
package: pythPackage,
pusher: publisher,
feeds: [
{ symbol: 'DEEP', feedId: DEEP_PRICE_FEED_ID, initialPrice: 2_000_000n, expo: -8 },
{ symbol: 'SUI', feedId: SUI_PRICE_FEED_ID, initialPrice: 345_000_000n, expo: -8 },
],
},
pools: [
{
name: 'DEEP_SUI',
base: { key: 'DEEP', coin: deep },
quote: { key: 'SUI', coin: suiCoin },
tickSize: 1_000_000n,
lotSize: 1_000_000n,
minSize: 10_000_000n,
seed: {
baseAmount: 1_000_000_000n,
quoteAmount: 10_000_000_000n,
orders: [
{ side: 'ask', price: 6_000_000n, quantity: 1_000_000_000n },
{ side: 'bid', price: 5_000_000n, quantity: 1_000_000_000n },
],
},
},
],
});
export default defineDevstack({ members: [localnet, dex] });Pools
Each entry in pools creates one whitelisted pool. The three sizing fields are DeepBook's on-chain
trading granularity, all expressed in the base/quote coins' smallest units:
tickSize— the minimum price increment. Every orderpricemust be a multiple of it.lotSize— the minimum quantity increment. Every orderquantitymust be a multiple of it.minSize— the smallest order quantity allowed.
Pools are whitelisted: true by default, which means they charge zero fees and don't require DEEP
to trade — ideal for local development. deepbook(...) validates that every seed order is tick- and
lot-aligned and at least minSize, and fails fast with a DeepbookConfigError otherwise.
Seeding liquidity
A freshly created pool has an empty order book — nothing to trade against. The optional seed
field fixes that: it deposits base + quote into a publisher-owned BalanceManager and rests the
listed limit orders, so the book is tradeable the moment the stack finishes booting.
seed: {
baseAmount: 1_000_000_000n, // base (DEEP) deposited into the BalanceManager
quoteAmount: 10_000_000_000n, // quote (SUI) deposited into the BalanceManager
orders: [
{ side: 'ask', price: 6_000_000n, quantity: 1_000_000_000n }, // sell DEEP — needs DEEP inventory
{ side: 'bid', price: 5_000_000n, quantity: 1_000_000_000n }, // buy DEEP — needs SUI inventory
],
},Seeding works because the publisher already owns the coins it deposits:
- DEEP — publishing the DeepBook token package mints the entire DEEP supply to the deployer, so
the
publisherholds DEEP with no extra configuration. You do not need a DEEP faucet, a treasury cap, ordeepTreasuryIdKeyto seed (that capture is only used for SDK bindings). - SUI — the quote side is funded from the publisher's own SUI balance, so fund the publisher generously (the example above uses 1,000 SUI).
For other coins, seeding works whenever the deposited coin has a funding strategy — for example a
local package coin that keeps its TreasuryCap with the publisher, or a SUI-faucet-funded coin. The
resolved dex value exposes hasSeedLiquidity: true once any pool placed its seed orders.
seedis for local demos only. Inknown/overridemode you trade against whatever liquidity the live DEX already has.
Reading pools and prices from your app
deepbook(...) generates bindings into src/generated/deepbook.ts. DeepBook is a per-network
service: deepbook.forNetwork(net).deepbook returns the bindings for the network your wallet is on,
so the pool, coin, and feed ids stay in lockstep with a runtime switchNetwork. Read them to get
the created pool ids and Pyth feed prices without hard-coding anything:
import { useCurrentNetwork } from '@mysten/dapp-kit-react';
import { deepbook } from '@generated/deepbook.js';
const network = useCurrentNetwork();
const dex = deepbook.forNetwork(network).deepbook;
// Pool ids, resolved at boot — pass these to the DeepBook SDK.
const deepSui = dex.pools.find((p) => p.name === 'DEEP_SUI');
// deepSui?.poolId / .baseCoinType / .quoteCoinType
// Pyth prices. `price` is an integer in feed-native scale; apply `expo` to get
// a human number: 345_000_000 * 10**-8 = 3.45.
const sui = dex.pyth?.feeds.find((f) => f.symbol === 'SUI');
const suiPrice = sui ? Number(sui.price) * 10 ** sui.expo : undefined; // 3.45To place orders, build transactions with the
@mysten/deepbook-v3 SDK, using the resolved
poolId and coin types from the bindings, and sign them with your wallet / dapp-kit.
Connect the SDK
To place orders, extend your Sui client with the DeepBook extension built from the same per-network
bindings — deepbook.forNetwork(network).deepbook — so the package, pool, and coin ids flip with a
runtime switchNetwork:
import { useCurrentAccount, useCurrentClient, useCurrentNetwork } from '@mysten/dapp-kit-react';
import { deepbook as deepbookExtension } from '@mysten/deepbook-v3';
import { coins } from '@generated/coins.js';
import { deepbook } from '@generated/deepbook.js';
const currentAccount = useCurrentAccount();
const suiClient = useCurrentClient();
const network = useCurrentNetwork();
const bindings = deepbook.forNetwork(network).deepbook;
// Per-network coin bindings drive the extension's coin map (see the example
// app's `deriveDemoCoinBindings` for the full mapping).
const coinBindings = deriveDemoCoinBindings(coins.forNetwork(network));
const deepbookClient = suiClient.$extend(
deepbookExtension({
address: currentAccount.address,
// `packageIds`, `coins`, and `pools` map the bindings' resolved ids
// (bindings.packageId, bindings.registryId, bindings.pools[].poolId)
// into the extension's shapes.
packageIds: buildPackageIds(bindings),
coins: buildCoinMap(coinBindings),
pools: buildPoolMap(bindings),
}),
);The extension carries DeepBook's package, registry, coin, and pool ids — all read from the bindings,
nothing hard-coded — so deepbookClient.deepbook.deepBook.swapExactQuoteForBase(...) and the rest
of the SDK target the pools your stack just created. See
examples/deepbook-trader
for the deriveDemoCoinBindings / buildPackageIds / buildCoinMap / buildPoolMap helpers and a
complete trading UI — multiple pools, multiple feeds, and swap execution.
Pyth price feeds
Pyth is a price oracle: it publishes signed, frequently-updated prices for
assets like SUI, USDC, and BTC. DeepBook's pyth option wires those feeds into your stack so your
app, UI, or indexer can display prices and your config can reference oracle-priced assets. Pyth is
optional and independent of pools — pools trade fine without it; feeds are consumed off-chain by
your app, not by the on-chain order book.
In local mode the stack publishes the DeepBook sandbox's mock Pyth package and creates a
price object for each feed you list, initialized to the price you give it. You fully control these
prices — there is no live oracle on a local network. In known mode (testnet / mainnet) the
feeds resolve to Pyth's live on-chain deployment, so prices come from the real network.
Configuring a feed
{ symbol: 'SUI', feedId: SUI_PRICE_FEED_ID, initialPrice: 345_000_000n, expo: -8 }symbol— a label you choose; how you'll look the feed up in app code (feeds.find(f => f.symbol === 'SUI')).feedId— Pyth's global price-feed identifier (a 32-byte hex string). It's the same id on every network, so the same feed id you use locally is the one that resolves on mainnet. The package exports the common ones as constants:SUI_PRICE_FEED_ID,USDC_PRICE_FEED_ID,DEEP_PRICE_FEED_ID.initialPrice— the starting price for the local mock feed, as an integer in feed-native scale. Ignored by live (known-mode) feeds, which carry real prices.expo— the decimal exponent. The real-world price isprice * 10 ** expo. Pyth feeds are conventionallyexpo: -8, so345_000_000nwithexpo: -8is3.45. (Also accepts optionalconfidenceandemaPrice, both defaulting sensibly for deterministic local feeds.)
Custom feeds
For an asset not in the well-known set, wrap its Pyth feed id with pythPriceFeedId(...). You can
find feed ids in the Pyth price-feed registry:
import { pythPriceFeedId } from '@mysten-incubation/devstack';
const BTC_PRICE_FEED_ID = pythPriceFeedId(
'e62df6c8b4a85fe1a67db44dc12de5db330f7ac66b72dc658afedf0f4a415b43',
);
// ...then add it to `feeds`:
{ symbol: 'BTC', feedId: BTC_PRICE_FEED_ID, initialPrice: 6_500_000_000_000n, expo: -8 }Known mode
Known mode references an existing DeepBook deployment. The common path is the zero-config
per-network shortcut — deepbookFor(network).testnet() / .mainnet() — which derives the package
and registry ids from the @mysten/deepbook-v3 SDK, so there's nothing to copy in and nothing to
drift. Pyth resolves to the live network deployment automatically — you don't pass a pyth block.
import { chainIdForNetwork, deepbookFor, defineDevstack, sui } from '@mysten-incubation/devstack';
const liveSui = sui({ mode: 'live', network: 'testnet' });
const live = { mode: 'live', chainId: chainIdForNetwork('testnet') } as const;
const dex = deepbookFor(live).testnet();
export default defineDevstack({ members: [liveSui, dex] });To wrap a deployment whose ids aren't in the SDK constants, use the raw-id override
deepbookFor(network).known({ packageId, registryId }) (or
deepbook({ mode: 'known', packageId, registryId })) and pass both ids explicitly.
Override mode
Override mode wraps explicit deployment ids when you already manage the deployment yourself. It does not publish or manage DeepBook locally.
const dex = deepbook({
mode: 'override',
packageId: '0x...',
registryId: '0x...',
adminCapId: '0x...',
});deepbook(...) returns a service member. In a bare stack you can add it directly to members, as
the snippets above do. In a real app, though, you typically put only localnet, your app
host-service, and dashboard() in members, and wire the DeepBook service into the consuming
server's after: ordering list so it boots before the app starts — see
examples/deepbook-trader/devstack.config.ts
(after: [dex, devWallet] on the hostService). Known testnet/mainnet helpers may also contribute
funding strategies for service-owned coins when the mode supports them.