Account Model
This page dives deep into how Drift tracks positions, manages orders, and calculates collateral onchain. For account type definitions and structure, see Program Structure.
Overview
Drift’s account model is built around a few key principles:
Key Design Principles
- Cross-margin - All positions within a wallet share collateral
- Subaccounts - Each wallet can have multiple isolated trading accounts (0, 1, 2…)
- Interest-bearing - Spot balances accrue interest via scaled balance mechanism
- Inline orders - Orders are stored directly in UserAccount (no separate order accounts)
- Account size limits - Fixed limits (8 perp, 8 spot, 32 orders) keep transactions predictable
Position accounting
PerpPosition
Tracks long/short exposure in perpetual futures markets.
Key mechanics:
- Position size:
baseAssetAmount(positive = long, negative = short) - Entry price:
quoteEntryAmount / baseAssetAmount - Unrealized PnL:
(oraclePrice - entryPrice) x baseAssetAmount - Funding payments: Tracked via
lastCumulativeFundingRate
Order locking:
openBids- Notional value locked in buy ordersopenAsks- Notional value locked in sell orders
Liquidity provision:
lpShares- If providing liquidity to the AMM
SpotPosition
Represents deposits (positive) or borrows (negative) in spot markets.
Interest accrual:
- Uses
scaledBalancemechanism - Real balance =
scaledBalance x cumulativeIndex - Avoids per-user interest calculations onchain
- Interest compounds automatically
Balance types:
DepositBalance- You’ve deposited/lent tokensBorrowBalance- You’ve borrowed tokens
Order locking:
- Tokens locked in open spot orders tracked separately
Order mechanics
Orders are stored inline in UserAccount (no separate order accounts). Up to 32 orders per user including both resting orders and trigger orders.
Order types
Limit orders - Resting orders at a specific price
postOnly- Only execute as maker (reject if would take)immediateOrCancel- Fill immediately or cancel
Market orders - Execute immediately at best available price
Oracle orders - Price moves with oracle: oraclePrice + oraclePriceOffset
- Useful for market makers who want to maintain a spread without constant updates
- Price automatically adjusts as oracle updates
Trigger orders - Activate when oracle price crosses trigger price
triggerMarket- Becomes market order when triggeredtriggerLimit- Becomes limit order when triggered
JIT auctions
Every order goes through a Just-In-Time (JIT) auction to improve price:
auctionStartPrice- Most aggressive priceauctionEndPrice- Least aggressive price (limit price)auctionDuration- Slots for auction to run- Price linearly improves from start to end over the duration
This gives better prices while maintaining MEV resistance. See JIT Auctions for details.
Order flags
reduceOnly- Order can only reduce position size, not increase or flip itpostOnly- Order must be maker, rejected if would match immediatelyimmediateOrCancel- Fill what you can immediately, cancel the rest
Collateral and margin
Total collateral calculation
Your collateral is calculated across all spot positions:
Total Collateral =
Σ (spot deposits x asset weight)
- Σ (spot borrows x liability weight)
+ unrealized perp PnLAsset weights (< 1.0)
- Safety buffer for deposits used as collateral
- Example: SOL might have 0.9 weight (90% of value counts)
- Protects against price volatility
Liability weights (> 1.0)
- Extra buffer for borrowed funds
- Example: Borrowed SOL might have 1.1 weight (110% of debt counts)
- Accounts for repayment risk
Margin requirement
Position margin requirements sum across all positions:
Margin Requirement = Σ (position notional x initial margin ratio)Initial margin ratio
- Determines minimum collateral to open a position
- Varies by market (e.g., 5% for BTC-PERP = 20x max leverage)
- Higher for riskier/less liquid markets
Maintenance margin ratio
- Lower threshold that triggers liquidation
- Typically ~half of initial margin
- Example: 2.5% maintenance if 5% initial
Account health
Health = Total Collateral / Margin RequirementHealth states:
- > 1.0 - Healthy, can open new positions
- < 1.0 - Liquidation eligible
- < 0.0 - Underwater (negative collateral)
When health drops below 1.0, liquidators can close your positions to bring the account back to health.
Cross-margin and subaccounts
All subaccounts under the same wallet share collateral for cross-margin benefits:
How it works:
- Each wallet can create multiple subaccounts (0, 1, 2, …)
- Deposits in subaccount 0 can back positions in subaccount 1
- Total collateral and margin requirement are calculated across ALL subaccounts
- Liquidation considers all subaccounts together
Benefits:
- Capital efficiency - Don’t need to split funds across accounts
- Flexible organization - Separate strategies in different subaccounts
- Shared margin pool - Winning positions support losing ones
SDK usage (TypeScript example , Python and Rust SDKs have equivalent methods):
// Switch between subaccounts
await driftClient.switchActiveUser(1); // Switch to subaccount 1
await driftClient.placePerpOrder(/* order params */); // Order goes to subaccount 1Important:
- Each subaccount has its own positions and orders
- But they all share the same collateral/margin pool
- Liquidation affects all subaccounts if total health drops below 1.0
State transitions
Deposit
Adds collateral to your account:
- Transfers tokens from wallet to Drift vault
- Increases spot position
scaledBalance - Updates cumulative deposit tracking
- Improves account health
Interest starts accruing immediately on deposits
Withdraw
Removes collateral from your account:
- Checks account health allows withdrawal
- Decreases spot position
scaledBalance - Transfers tokens from vault to wallet
- May require settling PnL first
Can’t withdraw if it would make health < 1.0
Place order
Adds a new order to your account:
- Adds Order to orders array (max 32)
- Increases
openBidsoropenAskson position - Locks collateral for the order
- Checks account health
- Starts JIT auction
Order appears on DLOB immediately
Order fill
When an order matches:
- Updates position
baseAssetAmountandquoteAssetAmount - Reduces
openBidsoropenAsks - Marks order as filled (not removed)
- Emits
OrderFillRecordevent - Updates account health
Filled orders remain for history tracking
Settle PnL
Converts unrealized perp PnL to realized:
- Calculates unrealized PnL from perp positions
- Moves quote from perp to spot balance
- Updates
settledPerpPnltracking - Makes profit/loss withdrawable
Required before withdrawing perp profits
Liquidation
When health < 1.0, liquidators can:
- Close perp positions at oracle price
- Reduce borrows by seizing collateral
- Earn liquidation fee (2-5%)
- Bring account back to health
- Remaining positions stay open
Partial liquidations are common
Account size limits
Drift enforces fixed limits to keep account size manageable and transaction costs predictable:
Perp positions
Maximum: 8 positions
Opening a 9th market fails unless you close an existing position first.
Spot positions
Maximum: 8 positions
Includes both deposits and borrows across all spot markets.
Orders
Maximum: 32 orders
Includes both resting orders and trigger orders combined.
Why limits?
- Keeps account data size predictable
- Ensures transaction compute fits in Solana limits
- Makes liquidation calculations feasible
- Prevents account bloat
Workaround:
- Use multiple subaccounts for more positions
- Each subaccount has its own 8/8/32 limits
- Still shares margin across all subaccounts
Related
- Program Structure - Account types and PDAs
- SDK Internals - How SDK reads and interprets account data