Skip to main content

Integrating DeepBook Margin

DeepBook Margin builds on the spot order book, but integrating it is not the same job as integrating spot. The structural differences an integrator meets fall into five areas: the object model, the isolated lending pools, the trading path, liquidation, and the events to index. For the objects in isolation see DeepBook Margin design; for a hands-on run see the Leveraged Position Workflow.

caution

Margin trading borrows funds against collateral, and anyone can liquidate a borrowed position. Any position your integration opens is subject to the pool's risk parameters and permissionless liquidation. Read Margin Risks before building.

The margin object model

Spot trading uses a BalanceManager: it holds your balances, you hold its TradeCap, and you trade a Pool directly. A single BalanceManager works across every pool.

Margin wraps that. A MarginManager is a shared object that holds a BalanceManager inside itself, along with the caps that authorize action on it:

Two consequences drive the rest of integration:

  • You never hold the wrapped manager's caps. The deposit_cap, withdraw_cap, and trade_cap live inside the MarginManager, which generates the trade proof that authorizes orders internally. So every deposit, withdraw, and trade goes through the MarginManager, not through the BalanceManager API you would use for spot.
  • A manager is bound to one pool. The deepbook_pool field fixes the manager to a single DeepBook pool, and margin_pool_id records the one margin pool a current loan is from. This is isolated margin, not a cross-pool account.

Isolated pools

Each borrowable asset has exactly one MarginPool, and the MarginRegistry enforces that uniqueness. A MarginManager borrows from one side of its pool at a time, base or quote, so risk stays on a single axis. A DeepBook pool is tradeable on margin only when the registry has enabled it and mapped its base and quote margin pools.

Before integrating against a pool, preflight it: confirm margin is enabled, resolve the two margin pool IDs a position can borrow from, and read the per-pool risk parameters. Do not assume a pool supports margin or hardcode its parameters.

import type { DeepBookMarginClient } from './client.js';
import { readRiskParams, type RiskParams } from './risk-params.js';

// Preflight a DeepBook pool before integrating margin against it. A pool is
// tradeable on margin only if the registry has enabled it, and each side (base,
// quote) borrows from a separate isolated MarginPool. This reports whether the
// pool is enabled, the two margin pool IDs a position can borrow from, and the
// per-pool risk parameters, so you can gate your integration on real on-chain
// state instead of assuming a pool supports margin.
export interface MarginPoolProfile {
poolKey: string;
marginEnabled: boolean;
baseMarginPoolId?: string;
quoteMarginPoolId?: string;
riskParams?: RiskParams;
}

export async function describeMarginPool(
client: DeepBookMarginClient,
poolKey: string,
): Promise<MarginPoolProfile> {
const db = client.deepbook;
const marginEnabled = await db.isPoolEnabledForMargin(poolKey);
if (!marginEnabled) return { poolKey, marginEnabled };
const [baseMarginPoolId, quoteMarginPoolId, riskParams] = await Promise.all([
db.getBaseMarginPoolId(poolKey),
db.getQuoteMarginPoolId(poolKey),
readRiskParams(client, poolKey),
]);
return { poolKey, marginEnabled, baseMarginPoolId, quoteMarginPoolId, riskParams };
}

Observe: for an enabled pool such as SUI_DBUSDC, this returns marginEnabled: true, a base and a quote margin pool ID, and the risk parameters. At the time of writing the risk parameters read as a liquidation risk ratio of 1.1, min borrow 1.2499, min withdraw 2, target 1.25, and rewards of 0.02 (user) and 0.03 (pool). Treat those as example values and read them onchain: they are set per pool by governance and are not the protocol defaults. See Risk ratio.

The trading path

Margin orders do not use the spot order entry. They go through the pool proxy, which places the order on the DeepBook pool on the manager's behalf and enforces the risk checks that keep a borrow safe. The current entry points are the _v2 functions (place_limit_order_v2, place_market_order_v2, and the reduce-only variants); the unsuffixed originals are deprecated, so integrate against the _v2 functions only.

Every pool proxy order, and every operation that values a position (borrow, withdraw, risk read), takes Pyth PriceInfoObject arguments and reverts on a price older than the pool's maximum age. Spot orders take no oracle. This is the largest day-to-day difference: a margin integration keeps price feeds fresh, and any valuing call fails if they are stale. See Oracle risk.

To monitor a position's health, read its oracle-priced risk ratio:

import type { DeepBookMarginClient } from './client.js';
import type { RiskParams } from './risk-params.js';

// Read your live risk ratio and see how close it is to liquidation.
// `getMarginManagerState` values your collateral and debt through the pool's
// Pyth oracles and returns the resulting risk ratio in one call, along with the
// oracle prices it used. Because it is oracle-priced, it moves whenever SUI
// moves, and it drifts down on its own as interest accrues on the debt.
export interface RiskStatus {
riskRatio: number;
baseDebt: string;
quoteDebt: string;
// Distance to the liquidation threshold, as a fraction of the current ratio.
// Small and shrinking is the danger sign.
marginToLiquidation: number;
liquidatable: boolean;
}

export async function readRiskStatus(
client: DeepBookMarginClient,
marginManagerKey: string,
params: RiskParams,
): Promise<RiskStatus> {
const state = await client.deepbook.getMarginManagerState(marginManagerKey);
const marginToLiquidation =
(state.riskRatio - params.liquidationRiskRatio) / state.riskRatio;
return {
riskRatio: state.riskRatio,
baseDebt: state.baseDebt,
quoteDebt: state.quoteDebt,
marginToLiquidation,
liquidatable: state.riskRatio <= params.liquidationRiskRatio,
};
}

Liquidation from the integrator's view

Anyone can liquidate any position your integration opens once its risk ratio falls to the pool's liquidation risk ratio. Liquidation is permissionless and, in the current deployment, runs through the separate margin_liquidation package: an entry callable by anyone checks eligibility with the registry, sources repay capital from a shared liquidation vault, and calls the manager's inner settlement to repay part of the debt and pay a reward out of the collateral, restoring the position to the target risk ratio. The Leveraged Position Workflow shows the onchain entry point, and Margin Risks works the consequences through with numbers.

For an integrator this means two things. First, no grace period protects the positions you build, so surface the risk ratio and its distance to the liquidation threshold to your users, and act early. Second, if you run a liquidator, it is a standard permissionless keeper: watch positions, supply oracles and repay capital, and receive the reward.

Events to index

Margin activity emits its own events, distinct from the spot order events. The DeepBook Margin Indexer exposes these through the public indexer; the names below are the onchain event structs to key on.

  • Manager lifecycle (margin_manager): MarginManagerCreatedEvent, DepositCollateralEvent, WithdrawCollateralEvent, LoanBorrowedEvent, LoanRepaidEvent, LiquidationEvent.
  • Liquidation vault (margin_liquidation): LiquidationByVault.
  • Pool supply and borrow (margin_pool): MarginPoolCreated, AssetSupplied, AssetWithdrawn, plus fee and referral events for suppliers.
  • Registry and governance (margin_registry): DeepbookPoolRegistered, DeepbookPoolConfigUpdated, MaxPriceAgeUpdated, PriceToleranceUpdated, MinOpenRiskRatioUpdated, and CurrentPriceUpdated. Watch these if your integration caches per-pool risk parameters, because governance can change them.

Differences from the fully collateralized spot path

Every row below reflects current source or an existing reference page.

AspectSpot (BalanceManager)Margin (MarginManager)Reference
Fund custodyHolds your balances directlyWraps a BalanceManager inside the manager objectObject model
Trade authorizationYou hold the TradeCap and trade the pool directlyThe manager holds the deposit, withdraw, and trade caps; proofs are internalObject model
Pool scopeOne manager works across all poolsBound to a single DeepBook poolDesign
BorrowingNoneBorrow one side from one isolated MarginPool at a timeDesign
Order entryplaceLimitOrder on the poolplace_limit_order_v2 through the pool proxy, risk-checkedTrading path
Oracle dependencyNonePyth prices required for borrow, withdraw, trade, and valuationOracle risk
WithdrawalWithdraw settled funds anytimeGated by the Min Withdraw Risk RatioRisk ratio
CostDEEP trading fee per fillDEEP trading fee plus continuously accruing borrow interestInterest rates
LiquidationNonePermissionless at the liquidation threshold, routed through the liquidation vaultMargin Risks
New failure modesInsufficient balance, off-tick or below min sizeAdds risk-ratio aborts, stale-oracle reverts, and empty-pool borrow failuresWorkflow troubleshooting