` NAV
typescript python http

Terms of Use

By using any API provided by Drift Labs, you agree to the Terms of Use. If you do not agree to the foregoing, then do not use any such API.

Introduction

Drift Protocol is an open-sourced, decentralised exchange built on the Solana blockchain, enabling transparent and non-custodial trading on cryptocurrencies.

There are language bindings in Typescript and Python! You can view code examples in the dark area to the right, and you can switch the programming language of the examples with the tabs in the top right.

This API documentation page is open sourced and available here was created with Slate. Feel free to submit questions/comments in Issues or suggest changes as a PR.

Program Addresses

Enviroment Program ID User Interface
mainnet-beta dRiftyHA39MWEi3m9aunc5MzRF1JYuBsbn6VPcn33UH app
devnet dRiftyHA39MWEi3m9aunc5MzRF1JYuBsbn6VPcn33UH app

Authentication

To access and interact with a blockchain, such as Solana, you need a keypair, which consists of a public key and a private key. The private key should be kept secure and not shared with anyone else.

To generate a new keypair, you can use the Solana Tool Suite by running the following command on the command line:

solana-keygen new --outfile ~/.config/solana/my-keypair.json

This command will create a new keypair and store it in a JSON file located at ~/.config/solana/my-keypair.json.

To allow SDK code to use this keypair for authentication, you need to set the ANCHOR_WALLET environment variable to the path of the JSON file that contains the keypair. You can do this by running the following command:

export ANCHOR_WALLET=~/.config/solana/my-keypair.json

This command sets the ANCHOR_WALLET environment variable to the path of the JSON file that contains your keypair, so that SDK code can access it when needed.

Client

Typescript

Install @drift-labs/sdk from npm using yarn:

yarn add @drift-labs/sdk

auto-generated documentation here: [https://drift-labs.github.io/protocol-v2/sdk/]

Python

Install driftpy from PyPI using pip:

pip install driftpy

auto-generated documentation here: [https://drift-labs.github.io/driftpy/]

HTTP

Use the self-hosted HTTP gateway

Connection

import {Connection} from "@solana/web3.js";

// the default RPC for devnet is `https://api.devnet.solana.com`
const connection = new Connection('https://api.mainnet-beta.solana.com');
from solana.rpc.async_api import AsyncClient

url = 'https://api.mainnet-beta.solana.com' # replace w/ any rpc
connection = AsyncClient(url)
drift-gateway https://api.mainnet-beta.solana.com --port 8080

The connection object is used to send transactions to the Solana blockchain. It is used by the DriftClient to send transactions to the blockchain.

Wallet

import {Wallet, loadKeypair} from "@drift-labs/sdk";

const keyPairFile = `${process.env.HOME}/.config/solana/my-keypair.json`;
const wallet = new Wallet(loadKeypair(keyPairFile));
import os
from anchorpy import Wallet
from driftpy.keypair import load_keypair

keypair_file = os.path.expanduser('~/.config/solana/my-keypair.json')
keypair = load_keypair(keypair_file)
wallet = Wallet(kp)
# use `DRIFT_GATEWAY_KEY` environment variable to configure the gateway wallet
# either path to .json keypair or base58 seed string
export DRIFT_GATEWAY_KEY="</path/to/my-keypair.json|<KEYPAIR_BASE58_SEED>"

The wallet used to sign solana transactions. The wallet can be created from a private key or from a keypair file.

Make sure this wallet has some SOL first. SOL is used to pay for transactions and is required as rent for account intializations.

Client Initialization

import {Connection} from "@solana/web3.js";
import {Wallet, loadKeypair, DriftClient} from "@drift-labs/sdk";

const connection = new Connection('https://api.mainnet-beta.solana.com');

const keyPairFile = `${process.env.HOME}/.config/solana/my-keypair.json`;
const wallet = new Wallet(loadKeypair(keyPairFile))

const driftClient = new DriftClient({
  connection,
  wallet,
  env: 'mainnet-beta',
});

driftClient.subscribe();
  from anchorpy import Wallet
  from driftpy.drift_client import DriftClient
  from solana.rpc.async_api import AsyncClient

  # set connection and wallet
  # ...
  drift_client = DriftClient(connection, wallet, "mainnet")
Parameter Description Optional Default
connection Connection object specifying solana rpc url No
wallet The wallet used to sign transactions sent to solana blockchain No
programId Drift program id Yes dRiftyHA39MWEi3m9aunc5MzRF1JYuBsbn6VPcn33UH
env devnet or mainnet-beta. Used to automatically derive market accounts to subscribe to if they're not explicitly set Yes
perpMarketIndexes Which perp markets accounts to subscribe to. Yes Derived based on env
spotMarketIndexes Which spot markets accounts to subscribe to. Yes Derived based on env
oracleInfos Which oracles accounts to subscribe to. Yes Derived based on env
accountSubscription Whether to use websocket or polling to subscribe to on-chain accounts e.g. markets, users, oracle. Yes Websockets
opts Transaction confirmation status options Yes {preflightCommitment: "processed", commitment: "processed"}
activeSubAccountId Which sub account to use initially Yes 0
subAccountIds All the sub account ids to subscribe to. If this and authoritySubAccountMap are empty, subscribes to all sub account ids. Yes []
authority Which user account authority you're signing for. Only set if you're signing for delegated account. Yes wallet.publicKey
authoritySubAccountMap Map of authority to sub account ids to subscribe to. Only necessary if using multiple delegate accounts. If this and subAccountIds are empty, subscribes to all sub account ids. Yes {}
includeDelegates Whether or not to subscribe to delegates when subAccountIds and authoritySubAccountMap are empty Yes false
userStats Whether or not to listen subscribe to user stats account. Yes false

User Initialization

const [txSig, userPublickKey] = await driftClient.initializeUserAccount(
  0,
  "toly"
);
tx_sig = await drift_client.initialize_user(sub_account_id=0, name="toly")
Parameter Description Optional Default
subAccountId The sub account id for the new user account. Yes 0
name Display name for the user account Yes Main Account
referrerInfo The address of the referrer and referrer stats accounts Yes

Sub account ids are monotonic. The first user account created will have sub account id 0, the second will have sub account id 1, etc. The next sub account id can be found by calling driftClient.getNextSubAccountId().

Updating User

Users accounts can update names, set custom max intial margin ratio, enable margin trading, and add a delegate account.

const subaccountId = 0;

// set max 1x intiial leverage
await this.driftClient.updateUserCustomMarginRatio(
  MARGIN_PRECISION,
  subaccountId
);

// enable spot margin trading
await this.driftClient.updateUserMarginTradingEnabled(
  true,
  subaccountId
);

// add a delegate this user account
await this.driftClient.updateUserDelegate(
  new PublicKey('satoshi'),
  subaccountId
);

Switching Sub Accounts

driftClient.switchActiveUser(
  1,
);
drift_client.switch_active_user(sub_account_id=1)
curl http://localhost:8080/v2/resource?subAccountId=1
Parameter Description Optional Default
subAccountId The sub account to switch to No 0
authority The authority of the sub account you're signing for. Only needed for delegate accounts Yes Current authority

Deleting User Account

If an account contains no assets or liabilites, a user account can be deleted to reclaim rent.

driftClient.deleteUser(
  1,
);

Depositing

const marketIndex = 0; // USDC
const amount = driftClient.convertToSpotPrecision(marketIndex, 100); // $100
const associatedTokenAccount = await driftClient.getAssociatedTokenAccount(marketIndex);

driftClient.deposit(
  amount,
  marketIndex,
  associatedTokenAccount,
);

spot_market_index = 0 # USDC
amount = drift_client.convert_to_spot_precision(spot_market_index, 100) # $100

tx_sig = await drift_client.deposit(amount, spot_market_index)
Parameter Description Optional Default
amount The amount to deposit in spot market's token mint precision No
marketIndex The spot market index you're depositing into No
associatedTokenAccount The public key of the token account you're depositing from. For sol, it can be the wallet's public key No
subAccountId The sub account you're depositing too Yes active sub account
reduceOnly Whether the deposit should only reduce borrow Yes false

Withdrawing

const marketIndex = 0;
const amount = driftClient.convertToSpotPrecision(marketIndex, 100);
const associatedTokenAccount = await driftClient.getAssociatedTokenAccount(marketIndex);

driftClient.withdraw(
  amount,
  marketIndex,
  associatedTokenAccount,
);

market_index = 0 # USDC
amount = drift_client.convert_to_spot_precision(market_index, 100) # $100

tx_sig = await drift_client.withdraw(amount, spot_market_index)
Parameter Description Optional Default
amount The amount to withdraw in spot market's token mint precision No
marketIndex The spot market index you're withdrawing from No
associatedTokenAccount The public key of the token account you're withdrawing to. For sol, it can be the wallet's public key No
reduceOnly Whether the withdraw should only decrease a deposit and block a new borrow Yes false

Withdrawing can lead to a borrow if the user has no deposits in the market and the user has enough margin to cover it.

Transferring Deposits

const marketIndex = 0;
const amount = driftClient.convertToSpotPrecision(marketIndex, 100);
const fromSubAccountId = 0;
const toSubAccountId = 1;

driftClient.transferDeposit(
  amount,
  marketIndex,
  fromSubAccountId,
  toSubAccountId,
);
market_index = 0
amount = drift_client.convert_to_spot_precision(market_index, 100)
from_sub_account_id = 0
to_sub_account_id = 0

await drift_client.transfer_deposit(
  amount,
  market_index,
  from_sub_account_id,
  to_sub_account_id,
)
Parameter Description Optional Default
amount The amount to transfer in spot market's token mint precision No
marketIndex The spot market index you're transferring deposits in No
fromSubAccountId The sub account you're withdrawing from No
toSubAccountId The sub account you're depositing too No

Order Types

MARKET, LIMIT, ORACLE orders all support auction parameters.

Type Description
MARKET Market order.
LIMIT Limit order.
TRIGGER_MARKET Stop / Take-profit market order.
TRIGGER_LIMIT Stop / Take-profit limit order.
ORACLE Market order using oracle offset for auction parameters.

Order Params

Parameter Description Optional Default
orderType The type of order e.g. market, limit No
marketIndex The market to place order in No
direction The direction of order e.g. long (bid) or short (ask) No
baseAssetAmount The amount of base asset to buy or sell No
marketType The type of market order is for e.g. PERP or SPOT Yes Depends on method
price The limit price for order Yes 0
userOrderId Unique order id specified by user Yes 0
reduceOnly If the order can only reduce positions Yes false
postOnly If the order can only be a maker PostOnlyParam None
triggerPrice at what price order is triggered. only applicable for triggerMarket and triggerLimit orders Yes
triggerCondition whether order is triggered above or below triggerPrice. only applicable for triggerMarket and triggerLimit orders Yes
oraclePriceOffset priceOffset for oracle derived limit price. only applicable for limit and oracle orders Yes
auctionDuration how many slots the auction lasts. only applicable for market and oracle orders Yes
auctionStartPrice the price the auction starts at Yes
auctionEndPrice the price the auction ends at Yes
maxTs the max timestamp (on-chain unix timestamp) before the order expires Yes

Post Only Params

Drift orderbook is not a strict clob that enforces price-time priority. This is to maximize the parallelization of placing orders to take advantage of the solana runtime. To force an order to always be a maker, users most set the post only params. If a user order is set to post only, drift will check that an order does not cross the vamm spread, similar to how a traditional clob would check that an order doesn't cross the book's best bid/ask. If the post only is not used, a limit order can end up being a taker or maker.

Parameter Description
None Does not enforce being maker
MustPostOnly Tx fails if order crosses the vamm
TryPostOnly Order is skipped (not placed) and tx succeeds if order crosses the vamm
Slide Order price is modified to be one tick below/above the vamm ask/bid

Placing Perp Order


// market buy for 100 SOL-PERP @ $21.20->$21.30 over 60 slots (~30 seconds)
// after 60 slots, market buy 100 SOL-PERP @ $21.35 until maxTs
const orderParams = {
  orderType: OrderType.MARKET,
  marketIndex: 0,
  direction: PositionDirection.LONG,
  baseAssetAmount: driftClient.convertToPerpPrecision(100),
  auctionStartPrice: driftClient.convertToPricePrecision(21.20),
  auctionEndPrice: driftClient.convertToPricePrecision(21.30),
  price: driftClient.convertToPricePrecision(21.35),
  auctionDuration: 60,
  maxTs: now + 100,
}
await driftClient.placePerpOrder(orderParams);

// bid for 100 SOL-PERP @ $21.23
const orderParams = {
  orderType: OrderType.LIMIT,
  marketIndex: 0,
  direction: PositionDirection.LONG,
  baseAssetAmount: driftClient.convertToPerpPrecision(100),
  price: driftClient.convertToPricePrecision(21.23),
}
await driftClient.placePerpOrder(orderParams);

// ask for 100 SOL-PERP @ ${OraclePrice} + .05
const orderParams = {
  orderType: OrderType.LIMIT,
  marketIndex: 0,
  direction: PositionDirection.SHORT,
  baseAssetAmount: driftClient.convertToPerpPrecision(100),
  oraclePriceOffset: driftClient.convertToPricePrecision(.05).toNumber(),
}
await driftClient.placePerpOrder(orderParams);
from driftpy.types import *
from driftpy.constants.numeric_constants import BASE_PRECISION, PRICE_PRECISION

market_index = 0

# place order to long 1 SOL-PERP @ $21.88 (post only)
order_params = OrderParams(
            order_type=OrderType.LIMIT(),
            direction=PositionDirection.LONG(),
            base_asset_amount=drift_client.convert_to_perp_precision(1),
            price=drift_client.convert_to_price_precision(21.88),
            market_index=market_index,
            post_only=PostOnlyParams.TRY_POST_ONLY(),
        )
await drift_client.get_place_perp_order(order_params)
curl -X POST -H 'content-type: application/json' \
  -d '{
    "orders": [{
        "marketIndex": 0,
        "marketType": "perp",
        "amount": 1.23,
        "price": 80.0,
        "postOnly": true,
        "orderType": "limit",
        "immediateOrCancel": false,
        "reduceOnly": false
    }]
   }' \
localhost:8080/v2/orders
Parameter Description Optional Default
orderParams The order params No

The order type is set to PERP by default.

Placing Spot Order


const orderParams = {
  orderType: OrderType.LIMIT,
  marketIndex: 1,
  direction: PositionDirection.LONG,
  baseAssetAmount: driftClient.convertToSpotPrecision(1, 100),
  price: driftClient.convertToPricePrecision(100),
}

await driftClient.placeSpotOrder(orderParams);

market_index = 1

order_params = OrderParams(
            order_type=OrderType.LIMIT(),
            direction=PositionDirection.LONG(),
            base_asset_amount=drift_client.convert_to_spot_precision(market_index, 100),
            price=drift_client.convert_to_price_precision(100),
            market_index=market_index,
        )

await driftClient.place_spot_order(order_params);
curl -X POST -H 'content-type: application/json' \
  -d '{
    "orders": [{
        "marketIndex": 0,
        "marketType": "spot",
        "amount": -1.23,
        "price": 80.0,
        "postOnly": true,
        "orderType": "limit",
        "immediateOrCancel": false,
        "reduceOnly": false
    }]
   }' \
localhost:8080/v2/orders
Parameter Description Optional Default
orderParams The order params No

The order type is set to SPOT by default.

Placing Multiple Orders


const placeOrderParams = [
    {
     orderType: OrderType.LIMIT,
     marketType: MarketType.PERP,
     marketIndex: 0,
     direction: PositionDirection.LONG,
     baseAssetAmount: driftClient.convertToPerpPrecision(100),
     price: driftClient.convertToPricePrecision(21.23),
   },
   {
     orderType: OrderType.LIMIT,
     marketType: MarketType.PERP,
     marketIndex: 0,
     direction: PositionDirection.SHORT,
     baseAssetAmount: driftClient.convertToPerpPrecision(100),
     oraclePriceOffset: driftClient.convertToPricePrecision(.05).toNumber(),
   }
];

await driftClient.placeOrders(placeOrderParams);

place_order_params = [
    OrderParams(
     order_type=OrderType.LIMIT(),
     market_type=MarketType.PERP(),
     market_index=0,
     direction=PositionDirection.LONG(),
     base_asset_amount=drift_client.convert_to_perp_precision(100),
     price=drift_client.convert_to_price_precision(21.23),
   ),
   OrderParams(
     order_type=OrderType.LIMIT(),
     market_type=MarketType.PERP(),
     market_index=0,
     direction=PositionDirection.SHORT(),
     base_asset_amount=drift_client.convert_to_perp_precision(100),
     oracle_price_offset=drift_client.convert_to_price_precision(.05),
   )
]

await drift_client.place_orders(place_order_params);
curl -X POST -H 'content-type: application/json' \
  -d '{
      "orders": [{
          "marketIndex": 0,
          "marketType": "spot",
          "amount": 1.23,
          "price": 80.0,
          "postOnly": true,
          "orderType": "limit",
          "immediateOrCancel": false,
          "reduceOnly": false
      },
      {
          "marketIndex": 2,
          "marketType": "perp",
          "amount": 0.1,
          "price": 10000.0,
          "orderType": "market",
      }]
    }' \
localhost:8080/v2/orders
Parameter Description Optional Default
placeOrderParams Parameters for place order instructions

Placing multiple orders in one tx can be cheaper than placing them in separate tx.

Placing Oracle Market Orders

const oraclePrice = driftClient.getOracleDataForPerpMarket(18).price;
const auctionStartPrice = oraclePrice.neg().divn(1000); // start auction 10bps below oracle
const auctionEndPrice = oraclePrice.divn(1000); // end auction 10bps above oracle
const oraclePriceOffset = oraclePrice.divn(500); // limit price after auction 20bps above oracle
const auctionDuration = 30; // 30 slots
const orderParams = {
  orderType: OrderType.ORACLE,
  baseAssetAmount: driftClient.convertToPerpPrecision(10),
  direction: PositionDirection.LONG,
  marketIndex: 18,
  auctionStartPrice: auctionStartPrice,
  auctionEndPrice: auctionEndPrice,
  oraclePriceOffset: oraclePriceOffset,
  auctionDuration: auctionDuration,
};
await driftClient.placePerpOrder(orderParams)
oracle_price = drift_client.get_oracle_price_data_for_perp_market(18).price
auction_start_price = -oracle_price // 1000 # start auction 10bps below oracle
auction_end_price = oracle_price // 1000 # end auction 10bps above oracle
oracle_price_offset = oracle_price // 500 # limit price after auction 20bps above oracle
auction_duration = 30 # 30 slots
order_params = OrderParams(
  OrderType.ORACLE(),
  drift_client.convert_to_perp_precision(10),
  18,
  PositionDirection.LONG(),
  auction_start_price=auction_start_price,
  auction_end_price=auction_end_price,
  oracle_price_offset=oracle_price_offset,
  auction_duration=auction_duration
)
await drift_client.place_perp_order(order_params)

Oracle market orders enable a user to define their auction params as an offset (or relative to) the oracle price.

Canceling Order


const orderId = 1;
await driftClient.cancelOrder(orderId);

order_dd = 1;
await drift_client.cancel_order(order_id);
Parameter Description Optional Default
orderId The order being canceled No
curl -X DELETE -H 'content-type: application/json' \
  -d '{ "ids": [1] }' \
  localhost:8080/v2/orders

Canceling Order By User Order Id


const userOrderId = 1;
await driftClient.cancelOrderByUserOrderId(userOrderId);

const user_order_id = 1;
await drift_client.cancel_order_by_user_order_id(user_order_id);
curl -X DELETE -H 'content-type: application/json' \
  -d '{ "userIds": [1] }' \
  localhost:8080/v2/orders
Parameter Description Optional Default
userOrderId Unique order id specified by user when order was placed No

Cancel Orders


const marketType = MarketType.PERP;
const marketIndex = 0;
const direction = PositionDirection.LONG;
await driftClient.cancelOrders(marketType, marketIndex, direction);
market_type = MarketType.PERP
market_index = 0
direction = PositionDirection.LONG
await drift_client.cancel_orders(market_type, market_index, direction) # cancel bids in perp market 0

await drift_client.cancel_orders() # cancels all orders
curl -X DELETE -H 'content-type: application/json' \
  -d '{ "marketIndex": 1, "marketType": "spot" }' \
  localhost:8080/v2/orders
Parameter Description Optional Default
marketType The market type of orders to cancel. Must be set if marketIndex set Yes
marketIndex The market index of orders to cancel. Must be set if marketType set Yes
direction The direction of orders to cancel. Yes

To cancel all orders, do not set any parameters.

Cancel and Place Orders


const cancelOrderParams = {
   marketType: MarketType.PERP,
   marketIndex: 0,
};

const placeOrderParams = [
    {
     orderType: OrderType.LIMIT,
     marketIndex: 0,
     direction: PositionDirection.LONG,
     baseAssetAmount: driftClient.convertToPerpPrecision(100),
     price: driftClient.convertToPricePrecision(21.23),
   },
   {
     orderType: OrderType.LIMIT,
     marketIndex: 0,
     direction: PositionDirection.SHORT,
     baseAssetAmount: driftClient.convertToPerpPrecision(100),
     oraclePriceOffset: driftClient.convertToPricePrecision(.05).toNumber(),
   }
];

await driftClient.cancelAndPlaceOrders(cancelOrderParams, placeOrderParams);

canel_order_params = (MarketType.PERP(), 0, None) # cancel all orders in perp market 0
place_order_params = [
    OrderParams(
     order_type=OrderType.LIMIT(),
     market_type=MarketType.PERP(),
     market_index=0,
     direction=PositionDirection.LONG(),
     base_asset_amount=drift_client.convert_to_perp_precision(100),
     price=drift_client.convert_to_price_precision(21.23),
   ),
   OrderParams(
     order_type=OrderType.LIMIT(),
     market_type=MarketType.PERP(),
     market_index=0,
     direction=PositionDirection.SHORT(),
     base_asset_amount=drift_client.convert_to_perp_precision(100),
     oracle_price_offset=drift_client.convert_to_price_precision(.05),
   )
]

await drift_client.cancel_and_place_orders(canel_order_params, place_order_params);
curl -X POST -H 'content-type: application/json' \
-d '{
      "cancel": {
        "marketIndex": 0,
        "marketType": "perp"
      },
      "place": {
        "orders": [{
            "marketIndex": 0,
            "marketType": "perp",
            "amount": -1.23,
            "price": 80.0,
            "postOnly": true,
            "orderType": "limit",
            "immediateOrCancel": false,
            "reduceOnly": false
        }]
      }
   }' \
localhost:8080/v2/orders/cancelAndPlace
Parameter Description Optional Default
cancelOrderParams Parameters for cancel orders instruction
placeOrderParams Parameters for place order instructions

To cancel all orders, do not set any parameters.

Modify Order Params

Parameter Description Optional Default
orderId The order id of order to modify No
baseAssetAmount The amount of base asset to buy or sell Yes
direction The direction of order e.g. long (bid) or short (ask) Yes
limitPrice The limit price for order Yes
reduceOnly If the order can only reduce positions Yes
postOnly If the order can only be a maker Yes
triggerPrice at what price order is triggered. only applicable for triggerMarket and triggerLimit orders Yes
triggerCondition whether order is triggered above or below triggerPrice. only applicable for triggerMarket and triggerLimit orders Yes
oraclePriceOffset priceOffset for oracle derived limit price. only applicable for limit and oracle orders Yes
auctionDuration how many slots the auction lasts. only applicable for market and oracle orders Yes
auctionStartPrice the price the auction starts at Yes
auctionEndPrice the price the auction ends at Yes
maxTs the max timestamp before the order expires Yes

Modifying Order


const updateParams = {
  orderId: 1,
  newBaseAssetAmount: driftClient.convertToPerpPrecision(200),
}

await driftClient.modifyOrder(orderParams);

order_id = 1

modify_order_params = ModifyOrderParams(
  base_asset_amount=drift_client.convert_to_perp_precision(1),
  price=drift_client.convert_to_price_precision(20),
)

await drift_client.modify_order(order_id, modify_order_params);
curl -X PATCH -H 'content-type: application/json' \
  -d '{
    "orders": [{
        "orderId": 32,
        "amount": 1.05,
        "price": 61.0
    }]
   }' \
localhost:8080/v2/orders
Parameter Description Optional Default
orderId The order id of order to modify No
modifyOrderParams The modify order params Yes

Modify cancels and places a new order

For typescript, the orderId and modifyOrderParams are merged into a single object and some properties are prefixed with new e.g. newBaseAssetAmount

Modifying Order By User Order Id


const updateParams = {
  userOrderId: 1,
  newBaseAssetAmount: driftClient.convertToPerpPrecision(200),
}

await driftClient.modifyOrderByUserOrderId(orderParams);

user_order_id = 1

modify_order_params = ModifyOrderParams(
  base_asset_amount=drift_client.convert_to_perp_precision(1),
  price=drift_client.convert_to_price_precision(20),
)

await drift_client.modify_order_by_user_id(user_order_id, modify_order_params);
curl -X PATCH -H 'content-type: application/json' \
  -d '{
    "orders": [{
        "userOrderId": 69,
        "amount": 1.05,
        "price": 61.0
    }]
   }' \
localhost:8080/v2/orders
Parameter Description Optional Default
userOrderId The user order id of order to modify No
modifyOrderParams The modify order params Yes

Modify cancels and places a new order

For typescript, the userOrderId and modifyOrderParams are merged into a single object and some properties are prefixed with new e.g. newBaseAssetAmount

Settle Perp PNL

const marketIndex = 0;
const user =  driftClient.getUser();
await driftClient.settlePNL(
   user.userAccountPublicKey,
   user.getUserAccount(),
   marketIndex
);
market_index = 0
user =  drift_client.get_user()
await drift_client.settle_pnl(
   user.user_public_key,
   user.get_user_account(),
   market_index
)
Parameter Description Optional Default
settleeUserAccountPublicKey User address you're settling pnl for No
settleeUserAccount User account data you're settling pnl for No
marketIndex Market index for the perp market No

Get Spot Market Account

const marketIndex = 1;
const spotMarketAccount = driftClient.getSpotMarketAccount(marketIndex);
market_index = 0;

spot_market_account = drift_client.get_spot_market_account(market_index);
Parameter Description Optional Default
marketIndex The market index for the spot market No

Get Perp Market Account

const marketIndex = 0;
const perpMarketAccount = driftClient.getPerpMarketAccount(marketIndex);
market_index = 0;

perp_market_account = drift_client.get_perp_market_account(market_index);
Parameter Description Optional Default
marketIndex The market index for the perp market No

User

Get User

   const user = driftClient.getUser();
  user = drift_client.get_user();
Parameter Description Optional Default
subAccountId The sub account id of user to get Yes active sub account
authority The authority of user to get. Only necessary if using multiple delegate accounts Yes current authority

Getting Deposit/Borrow Amounts

const marketIndex = 0;

const tokenAmount = user.getTokenAmount(
  marketIndex,
);

const isDeposit = tokenAmount.gte(new BN(0));
const isBorrow = tokenAmount.lt(new BN(0));
market_index = 0

token_amount = user.get_token_amount(marketIndex)

is_deposit = token_amount > 0
is_borrow = token_amount < 0
 curl -X GET \
  -H 'content-type: application/json' \
  -d '{"marketIndex":0,"marketType":"spot"}' \
localhost:8080/v2/positions
Parameter Description Optional Default
marketIndex Market index for the spot market No

If token amount is greater than 0, it is a deposit. If less than zero, it is a borrow.

Get Perp Position

const marketIndex = 0;

const baseAssetAmount = user.getPerpPosition(
  marketIndex,
)?.baseAssetAmount;

const isLong = baseAssetAmount.gte(new BN(0));
const isShort = baseAssetAmount.lt(new BN(0));
market_index = 0

perp_position = user.get_perp_position(market_index)

base_asset_amount = perp_position.base_asset_amount if perp_position is not None else 0

is_long = base_asset_amount > 0
is_short = base_asset_amount < 0
curl -X GET \
  -H 'content-type: application/json' \
  -d '{"marketIndex":0,"marketType":"perp"}' \
localhost:8080/v2/positions
Parameter Description Optional Default
marketIndex Market index for the perp market No

If base amount is greater than 0, it is a long. If less than zero, it is a short.

Get Order

const orderId = 1;

const order = user.getOrder(
  orderId,
);
order_id = 1

order = user.get_order(order_id)
Parameter Description Optional Default
orderId Order id for the order you're getting No

Get Order By User Order Id

const userOrderId = 1;

const order = user.getOrderByUserOrderId(
  userOrderId,
);
user_order_id = 1

order = user.get_order_by_user_order_id(user_order_id)
Parameter Description Optional Default
userOrderId User order id for the order you're getting No

Get Open Orders

const orders = user.getOpenOrders();
orders = user.get_open_orders()
curl localhost:8080/v2/orders

Get Unrealized Perp Pnl

  const pnl = user.getUnrealizedPNL();
  pnl = user.get_unrealized_pnl()
Parameter Description Optional Default
withFunding Whether to included unsettled funding payments Yes false
marketIndex Whether to only return pnl for specific market Yes

Get Unrealized Funding Pnl

  const pnl = await user.getUnrealizedFundingPNL();
  pnl = user.get_unrealized_funding_pnl()
Parameter Description Optional Default
marketIndex Whether to only return pnl for specific market Yes

Get Total Collateral

  const totalCollateral = await user.getTotalCollateral();
  total_collateral = await user.get_total_collateral()
Parameter Description Optional Default
marginCategory Initial or Maintenance Yes Initial

Asset weights vary based on whether you're checking the initial or maintenance margin requirement. Initial is used for initial leverage extension, maintenance for determining liquidations.

Get Margin Requirement

  const marginRequirement = await user.getMarginRequirement();
  margin_requirement = await user.get_margin_requirement()
Parameter Description Optional Default
marginCategory Initial or Maintenance Yes Initial

Liability weights (for borrows) and margin ratios (for perp positions) vary based on whether you're checking the initial or maintenance margin requirement. Initial is used for initial leverage extension, maintenance for determining liquidations.

Get Free Collateral

  const freeCollateral = user.getFreeCollateral();
  free_collateral = user.get_free_collateral()
Parameter Description Optional Default
marginCategory Initial or Maintenance Yes Initial

Free collateral is the difference between your total collateral and your margin requirement.

Get Leverage

  const leverage = user.getLeverage();
  leverage = user.get_leverage()
Parameter Description Optional Default
includeOpenOrders Whether to factor in open orders in position size Yes true

Leverage is the total liability value (borrows plus total perp position) divided by net asset value (total assets plus total liabilities)

Events

Event Subscription

import {EventSubscriber} from "@drift-labs/sdk";

const options = {
  eventTypes: [
    'DepositRecord',
    'FundingPaymentRecord',
    'LiquidationRecord',
    'OrderRecord',
    'OrderActionRecord',
    'FundingRateRecord',
    'NewUserRecord',
    'SettlePnlRecord',
    'LPRecord',
    'InsuranceFundRecord',
    'SpotInterestRecord',
    'InsuranceFundStakeRecord',
    'CurveRecord',
  ],
  maxTx: 4096,
  maxEventsPerType: 4096,
  orderBy: 'blockchain',
  orderDir: 'asc',
  commitment: 'confirmed',
  logProviderConfig: {
    type: 'websocket',
  },
}

const eventSubscriber = new EventSubscriber(connection, driftClient.program, options);
await eventSubscriber.subscribe();

eventSubscriber.eventEmitter.on('newEvent', (event) => {
  console.log(event);
});
from driftpy.events.event_subscriber import EventSubscriber
from driftpy.events.types import WrappedEvent, EventSubscriptionOptions, WebsocketLogProviderConfig

options = EventSubscriptionOptions(
    event_types = (
        'DepositRecord',
        'FundingPaymentRecord',
        'LiquidationRecord',
        'OrderRecord',
        'OrderActionRecord',
        'FundingRateRecord',
        'NewUserRecord',
        'SettlePnlRecord',
        'LPRecord',
        'InsuranceFundRecord',
        'SpotInterestRecord',
        'InsuranceFundStakeRecord',
        'CurveRecord',
    ),
    max_tx= 4096,
    max_events_per_type=4096,
    order_by="blockchain",
    order_dir="asc",
    commitment="processed",
    log_provider_config=WebsocketLogProviderConfig()
)

event_subscriber = EventSubscriber(connection, drift_client.program, options)
event_subscriber.subscribe()

event_subscriber.event_emitter.new_event += lambda event: print(event)
Parameter Description Optional Default
connection Connection object specifying solana rpc url No
program Anchor program object used to deserialize events from transaction logs No
options.eventTypes Which events types to trigger event callbacks for Yes All events
options.maxTx Max number of transactions to keep in memory Yes 4096
options.maxEventsPerType Max number of events per event type to keep in memory Yes 4096
options.orderBy Whether to sort the tx in memory by the order they occurred on chain ('blockchain') or received by client ('client') Yes 'blockchain'
options.orderDir Whether to sort the tx in memory to be most recent ('desc') or oldest ('asc') Yes 'asc'
options.commitment What transaction commitment to wait for Yes 'confirmed'
options.logProviderConfig Whether to use websocket or polling to listen for tx logs Yes {type: "websocket"}
options.address Which address to listen to events for. Defaults to drift program. Yes dRiftyHA39MWEi3m9aunc5MzRF1JYuBsbn6VPcn33UH

Protocol events are recorded in transactions logs. To listen for events, one must subscribe to the drift program's transaction logs.

Event Types

Event Type Description
DepositRecord A record of a user depositing or withdrawing funds from protocol
FundingPaymentRecord A record of a user paying/receiving funding payments
LiquidationRecord A record of a user being liquidated
OrderRecord A record of a user placing an order, including all of its parameters
OrderActionRecord A record of a user action on an order, including place, cancel and fill
FundingRateRecord A record of the funding rate changing
NewUserRecord A record of a new user
SettlePnlRecord A record of a user settling their pnl
LPRecord A record of a user adding or removing passive perp liquidity
InsuranceFundRecord A record of the insurance fund changing
SpotInterestRecord A record of the spot interest changing
InsuranceFundStakeRecord A record of a user staking or unstaking from the insurance fund
CurveRecord A record of the amm curve updating

Listening to Perp Market Fills


const marketIndex = 0;
const isPerpMarketFill = (event) => {
  if (event.eventType !== 'OrderActionRecord') {
    return false;
  }

  if (event.marketIndex !== marketIndex) {
    return false;
  }

  if (!isVariant(event.marketType, 'perp')) {
    return false;
  }

  if (!isVariant(event.action, 'fill')) {
    return false;
  }

  return true;
};

const fillCallback = (event) => {
  console.log(event);
}

eventSubscriber.eventEmitter.on('newEvent', (event) => {
  if (isPerpMarketFill(event)) {
    fillCallback(event);
  }
});

market_index = 0

def fill_callback(event: WrappedEvent):
    if event.event_type != "OrderActionRecord":
        return

    if event.data.market_index != market_index:
        return

    if not is_variant(event.data.market_type, "Perp"):
        return

    if not is_variant(event.data.action, "Fill"):
        return

    print(event)


event_subscriber.event_emitter.new_event += fill_callback

Listening to User Fills

options = EventSubscriptionOptions(
    address=drift_client.get_user_account_public_key(),
)

event_subscriber = EventSubscriber(connection, drift_client.program, options)
event_subscriber.subscribe()

def fill_callback(event: WrappedEvent):
    if event.event_type != "OrderActionRecord":
        return

    if not is_variant(event.data.action, "Fill"):
        return

    is_taker = event.data.taker == drift_client.get_user_account_public_key()
    is_maker = event.data.taker == drift_client.get_user_account_public_key()
    if not is_taker and not is_maker:
        return

    print(event)


event_subscriber.event_emitter.new_event += fill_callback

Getting Events Received By Type


const eventType = 'OrderActionRecord';
const events = eventSubscriber.getEventsReceived(eventType);

event_type = 'OrderActionRecord'
events = event_subscriber.get_events_array(event_type)

This returns all the events that the event subscriber currently has stored in memory.

Getting Events By Transaction


tx_sig = '3dq5PtQ3VnNTkQRrHhQ1nRACWZaFVvSBKs1RLXM8WvCqLHTzTuVGc7XER5awoLFLTdJ4kqZiNmo7e8b3pXaEGaoo'
events = event_subscriber.get_events_by_tx(tx_sig)

This returns the events that the event subscriber currently has stored in memory for a given transaction.

Margin System

Drift offers a cross-collateral margining system, allowing users to utilize multiple assets as trading collateral.

The margining system tracks each user's total collateral, the weighted sum of the user's deposits and perp pnl, as well as their margin requirement, the weighted value out the user's outstanding borrow and perp positions.

Total collateral is calculated as:

\[\sum_{i=1}^n d_i \cdot p_i \cdot w_i^a\ +\ \sum_{j=1}^n pnl_j \cdot qp_j \cdot w_j^{pnl} \]

Where

Margin requirement is calculated as:

\[ \sum_{i=1}^n b_i \cdot p_i \cdot w_i^l\ +\ \sum_{j=1}^n ba_j \cdot o_j \cdot qp_j \cdot m_j \]

Where

The weights and margin ratios depend on whether you're calculating the initial or maintenance values.

The initial maintenance check governs leverage extension. To open a new perp position or borrow, a user's initial total collateral must be greater than their initial margin requirement.

The maintenance check governs when a user's position must be liquidated. If a user's maintenance total collateral drops below their maintenance margin requirement, a user's position can be liquidated to reduce their risk.

The prices used for deposits, borrows and perp quote assets differ between the initial and maintenance checks. The maintenance check uses the current oracle price. The initial check uses the min(oracle_price, oracle_twap) for deposits and positive perp pnl and max(oracle_price, oracle_twap) for borrows, negative perp pnl and perp base amount.

Numerical Precisions

To maintain numerical precision, the on-chain program stores all values as integers.

Getting a Current Perp Position


const perpMarketIndex = 0; // SOL-PERP
const perpPosition = driftClient.getPerpPosition(perpMarketIndex);
console.log(convertToNumber(perpPosition.baseAssetAmount, BASE_PRECISION));

This prints the size of the current perp position in perp market index 0 (SOL-PERP)

Getting a Current Spot Position


const spotMarketIndex = 0; // USDC
const spotConfig = SpotMarkets['mainnet-beta'][spotMarketIndex];
const spotMarket = driftClient.getSpotMarketAccount(spotMarketIndex);
const spotPosition = driftClient.getSpotPosition(spotMarketIndex);
const tokenAmount = getTokenAmount(spotPosition.scaledBalance, spotMarket, .spotPosition.balanceType);
console.log(convertToNumber(tokenAmount, spotConfig.precision));

This prints the current spot position in spot market index 0 (USDC). This value is the same as the value shown on the UI, it includes any accumulated interest.

Common precision values

Value Precision Constant
perp base asset amount 1e9 BASE_PRECISION
perp quote asset amount 1e6 QUOTE_PRECISION
price 1e6 PRICE_PRECISION
funding rate 1e9 FUNDING_RATE_PRECISION
spot token amount derived from token mint's decimals (USDC is 1e6, SOL is 1e9) SpotMarketConfig.precision
spot token balance 1e9 SPOT_MARKET_BALANCE_PRECISION
margin ratio 1e4 MARGIN_PRECISION
asset/liability weight 1e4 SPOT_WEIGHT_PRECISION

Examples Bots

Typescript

Keeper-bots-v2

Perp Markets

Spot Markets

Python

driftpy examples/

Orderbook (Blockchain)

The drift orderbook is a collection of all open orders on the Drift protocol. There is no single source of truth for the orderbook. The most up to date view of the orderbook is one where you track user orders and maintain the orderbook structure yourself. This section shows how to do that with the various SDKs.

Dlob Source

This is the main source of orders for maintaing the orderbook.

Dlob Source - UserMap

UserMap stores a complete map of all user accounts (idle users are commonly filtered ou).

import {Connection} from "@solana/web3.js";
import {DriftClient, UserMap, Wallet, loadKeypair} from "@drift-labs/sdk";

const connection = new Connection("https://api.mainnet-beta.solana.com");

const keyPairFile = `${process.env.HOME}/.config/solana/my-keypair.json`;
const wallet = new Wallet(loadKeypair(keyPairFile))

const driftClient = new DriftClient({
  connection,
  wallet,
  env: 'mainnet-beta',
});
await driftClient.subscribe();

// polling keep users updated with periodic calls to getProgramAccounts
// websocket keep users updated via programSubscribe
const subscriptionConfig:
  | {
    type: 'polling';
    frequency: number;
    commitment?: Commitment;
  } | {
    type: 'websocket';
    resubTimeoutMs?: number;
    commitment?: Commitment;
  } = {
    type: 'websocket',
    resubTimeoutMs: 30_000,
    commitment: stateCommitment,
  };

const userMap = new UserMap({
  driftClient,
  connection,
  subscriptionConfig,
  skipInitialLoad: false, // skips initial load of user accounts
  includeIdle: false, // filters out idle users
});

await userMap.subscribe();
from solana.rpc.async_api import AsyncClient
from solders.keypair import Keypair
from anchorpy import Wallet
from driftpy.drift_client import DriftClient
from driftpy.keypair import load_keypair
from driftpy.user_map.user_map import UserMap
from driftpy.user_map.user_map_config import UserMapConfig, WebsocketConfig

connection = AsyncClient("https://api.mainnet-beta.solana.com")  # switch for your own rpc

keypair_file = ""  # path to keypair file

wallet = Wallet(load_keypair(keypair_file))

drift_client = DriftClient(
  connection,
  wallet,
  "mainnet"
)

await drift_client.subscribe()

user_map_config = UserMapConfig(drift_client, WebsocketConfig())
user_map = UserMap(user_map_config)

await user_map.subscribe()

Dlob Source - OrderSubscriber

OrderSubscriber is a more efficient version of UserMap, only tracking user accounts that have orders.

import {Connection} from "@solana/web3.js";
import {DriftClient, OrderSubscriber, Wallet, loadKeypair} from "@drift-labs/sdk";

const connection = new Connection("https://api.mainnet-beta.solana.com");

const keyPairFile = `${process.env.HOME}/.config/solana/my-keypair.json`;
const wallet = new Wallet(loadKeypair(keyPairFile))

const driftClient = new DriftClient({
  connection,
  wallet,
  env: 'mainnet-beta',
});
await driftClient.subscribe();

const subscriptionConfig:
  | {
    type: 'polling',
    frequency: ORDERBOOK_UPDATE_INTERVAL,
    commitment: stateCommitment,
  }
  | {
    type: 'websocket',
    commitment: stateCommitment,
    resyncIntervalMs: WS_FALLBACK_FETCH_INTERVAL,
  } = {
    type: 'websocket',
    commitment: stateCommitment,
    resyncIntervalMs: WS_FALLBACK_FETCH_INTERVAL, // periodically resyncs the orders in case of missed websocket messages
  };

const orderSubscriber = new OrderSubscriber({
  driftClient,
  subscriptionConfig,
});

await orderSubscriber.subscribe();

Orderbook Subscription

With a DlobSource you can then subscribe to the orderbook.

import {DLOBSubscriber} from "@drift-labs/sdk";

 const dlobSubscriber = new DLOBSubscriber({
    driftClient,
    dlobSource: orderSubscriber, // or UserMap
    slotSource: orderSubscriber, // or UserMap
    updateFrequency: 1000,
 });

await dlobSubscriber.subscribe();

TypeScript

Parameter Description Optional Default
driftClient DriftClient object No
dlobSource Where to build the orderbook from. Can subscribe to user accounts on-chain using UserMap or request DLOB from api using DLOBApiClient No
slotSource Where to get slot from No
updateFrequency How often to rebuild the orderbook from the dlobSource in milliseconds No
from driftpy.dlob.dlob_subscriber import DLOBSubscriber
from driftpy.dlob.client_types import DLOBClientConfig
from driftpy.slot.slot_subscriber import SlotSubscriber

slot_subscriber = SlotSubscriber(drift_client)

config = DLOBClientConfig(drift_client, user_map, slot_subscriber, 1)
dlob_subscriber = DLOBSubscriber(config = config)

await dlob_subscriber.subscribe()

Python

Parameter Description Optional Default
drift_client DriftClient object No
dlob_source Where to build the orderbook from. Can subscribe to user accounts on-chain using UserMap. No
slot_source Where to get slot from No
update_frequency How often to rebuild the orderbook from the dlob_source in milliseconds No

Get L2 Orderbook

const l2 = dlobSubscriber.getL2({
  marketName: 'SOL-PERP',
  depth: 50,
});

TypeScript

Parameter Description Optional Default
marketName The market name of the orderbook to get. If not set, marketIndex and marketType must be set Yes
marketIndex The market index of the orderbook to get. If not set, marketName must be set Yes
marketType The market type of the orderbook to get. If not set, marketName must be set Yes
depth The depth of the orderbook to get Yes 10
includeVamm Whether to include vAMM Yes false
fallbackL2Generators L2OrderbookGenerators for fallback liquidity e.g. vAmm, openbook, phoenix. Unnecessary if includeVamm is true Yes
l2 = dlob_subscriber.get_l2_orderbook_sync("SOL-PERP")

Python

Parameter Description Optional Default
market_name The market name of the orderbook to get. If not set, market_index and market_type must be set Yes
market_index The market index of the orderbook to get. If not set, market_name must be set Yes
market_type The market type of the orderbook to get. If not set, market_name must be set Yes
depth The depth of the orderbook to get Yes 10
include_vamm Whether to include vAMM Yes false
num_vamm_orders Number of orders to include from the vAMM. If not provided, depth is used. Yes
fallback_l2_generators L2OrderbookGenerators for fallback liquidity e.g. vAmm, openbook, phoenix. Unnecessary if includeVamm is true Yes

The L2 orderbook is an aggregate of drift dlob orders and, optionally, fallback liquidity.

Get L3 Orderbook

const l3 = dlobSubscriber.getL3({
  marketName: 'SOL-PERP',
});

TypeScript

Parameter Description Optional Default
marketName The market name of the orderbook to get. If not set, marketIndex and marketType must be set Yes
marketIndex The market index of the orderbook to get. If not set, marketName must be set Yes
marketType The market type of the orderbook to get. If not set, marketName must be set Yes
l3 = dlob_subscriber.get_l3_orderbook_sync("SOL-PERP")

Python

Parameter Description Optional Default
market_name The market name of the orderbook to get. If not set, market_index and market_type must be set Yes
market_index The market index of the orderbook to get. If not set, market_name must be set Yes
market_type The market type of the orderbook to get. If not set, market_name must be set Yes

The L3 orderbook contains every maker order on drift dlob, including the address for the user that placed the order.

Orderbook/Trades (DLOB Server)

Drift runs a dlob-server to reduce the RPC load on UI users and traders. You can access this server (or run your own!) instead of maintaing an order book from the blockchain.

mainnet-beta: https://dlob.drift.trade/ devnet: https://master.dlob.drift.trade/

All endpoints follow the same query parameter scheme to specify a market:

Parameter Description Optional Default
marketName The market name of the orderbook to get. If not set, marketIndex and marketType must be set Yes
marketIndex The market index of the orderbook to get. If not set, marketName must be set Yes
marketType The market type of the orderbook to get. If not set, marketName must be set Yes

GET /l2

GET /l3

Returns an L2 (aggregate price levels) or L3 (individual orders) orderbook for the specificed market.

Parameter Description Optional Default L2 only
depth Number of records to return per side Yes all orders Yes
includeVamm true to include vAMM liquidity in the response Yes false Yes
includeOracle true to include oracle data with the response Yes false No

Example: https://dlob.drift.trade/l2?marketName=JTO-PERP&depth=10&includeOracle=true&includeVamm=true

Example: https://dlob.drift.trade/l3?marketName=JTO-PERP&includeOracle=true

GET /topMakers

Returns the top makers (currently returns an exhaustive list) for a given market (useful for place_and_take orders).

Parameter Description Optional Default
side Side to return makers for (bid or ask) No
limit Limit number of makers to return Yes all
includeUserStats true to include full UserStats Yes false

Example: https://dlob.drift.trade/topMakers?marketName=JTO-PERP&side=bid&limit=5

Websocket

The mainnet-beta websocket endpoint is: wss://dlob.drift.trade/ws

Drift currently offers websocket streaming for two data streams: orderbooks and trades. The data streams are constructed from blockhain data. More data sources and user specific feeds will be added as this functionality is expanded.

mainnet-beta: wss://dlob.drift.trade/ws

devnet: wss://master.dlob.drift.trade/ws

Websocket - Subscribing

Clients must send subscribe messages over the websocket connection in order to start receiving messages.

Orderbook

Each market currently requires its own subscribe message. The two examples below show subscribe messages for a perp and spot market:

Perp markets

{ "type": "subscribe", "marketType": "perp", "channel": "orderbook", "market": "SOL-PERP" }

Response

Field Description
channel Identifies the type of data being streamed. orderbook_perp_0 indicates data for perpetual market 0 order book (SOL-PERP).
data Contains the actual order book data in JSON format. This field is a JSON string that needs to be parsed.
bids A list of bid orders in the order book, each representing an offer to buy.
asks A list of ask orders in the order book, each representing an offer to sell.
price The price at which the bid or ask is made.
size The size of the bid or ask, indicating the quantity of the asset the buyer or seller wishes to transact.
sources Indicates the origin or source of the bid or ask, such as vamm (Virtual Automated Market Maker) or dlob (Decentralized Limit Order Book).
marketName Name of the market, e.g., SOL-PERP.
marketType Type of the market, e.g., perp for perpetual.
marketIndex Index of the market, used to identify specific markets within a market type.
slot Solana slot.
oracle The reported price from the oracle for this market.
oracleData Contains detailed information from the oracle, including price, slot, confidence, etc.
oracleData.price The price reported by the oracle.
oracleData.slot The slot number associated with the oracle data.
oracleData.confidence The confidence interval for the oracle price.
oracleData.hasSufficientNumberOfDataPoints Indicates whether the oracle has sufficient data points for reliability.
oracleData.twap The time-weighted average price as reported by the oracle.
oracleData.twapConfidence The confidence interval for the time-weighted average price.
marketSlot Slot number associated with the market data.

Spot markets

{ "type": "subscribe", "marketType": "spot", "channel": "orderbook", "market": "SOL" }

Response

Field Description
channel Identifies the type of data being streamed. i.e orderbook_spot_1 indicates data for the spot market order book (SOL/USDC).
data Contains the actual order book data in JSON format. This field is a JSON string that needs to be parsed.
bids A list of bid orders in the order book, each representing an offer to buy.
asks A list of ask orders in the order book, each representing an offer to sell.
price The price at which the bid or ask is made.
size The size of the bid or ask, indicating the quantity of the asset the buyer or seller wishes to transact.
sources Indicates the origin or source of the bid or ask, such as phoenix or serum, representing different liquidity providers.
slot Solana slot.
marketName Name of the market, e.g., SOL.
marketType Type of the market, e.g., spot.
marketIndex Index of the market, used to identify specific markets within a market type.
oracle The reported price from the oracle for this market.
oracleData Contains detailed information from the oracle, including price, slot, confidence, etc.
oracleData.price The price reported by the oracle.
oracleData.slot The slot number associated with the oracle data.
oracleData.confidence The confidence interval for the oracle price.
oracleData.hasSufficientNumberOfDataPoints Indicates whether the oracle has sufficient data points for reliability.
oracleData.twap The time-weighted average price as reported by the oracle.
oracleData.twapConfidence The confidence interval for the time-weighted average price.

import WebSocket from 'ws';
const ws = new WebSocket('wss://dlob.drift.trade/ws');

ws.on('open', async () => {
  console.log('Connected to the server');

  // Subscribe to orderbook data
  ws.send(JSON.stringify({ type: 'subscribe', marketType: 'perp', channel: 'orderbook', market: 'SOL-PERP' }));
  ws.send(JSON.stringify({ type: 'subscribe', marketType: 'spot', channel: 'orderbook', market: 'SOL' }));


  // Subscribe to trades data
  ws.send(JSON.stringify({ type: 'subscribe', marketType: 'perp', channel: 'trades', market: 'SOL-PERP' }));
});

ws.on('message', (data: WebSocket.Data) => {
  try {
    const message = JSON.parse(data.toString());
    console.log(`Received data from channel: ${JSON.stringify(message.channel)}`);
    // book and trades data is in message.data
  } catch (e) {
    console.error('Invalid message:', data);
  }
});

import json
import websocket
import ssl

def on_message(ws, message):
    try:
        message = json.loads(message)
        print(f"Received data from channel: {json.dumps(message['channel'])}")
        # book and trades data is in message['data']
    except ValueError:
        print(f"Invalid message: {message}")

def on_error(ws, error):
    print(f"Error: {error}")

def on_close(ws, close_status_code, close_msg):
    print("### Closed ###")

def on_open(ws):
    print("Connected to the server")

    # Subscribe to orderbook data
    ws.send(json.dumps({'type': 'subscribe', 'marketType': 'perp', 'channel': 'orderbook', 'market': 'SOL-PERP'}))
    ws.send(json.dumps({'type': 'subscribe', 'marketType': 'spot', 'channel': 'orderbook', 'market': 'SOL'}))

    # Subscribe to trades data
    ws.send(json.dumps({'type': 'subscribe', 'marketType': 'perp', 'channel': 'trades', 'market': 'SOL-PERP'}))

if __name__ == "__main__":
    websocket.enableTrace(True)
    ws = websocket.WebSocketApp("wss://dlob.drift.trade/ws",
                                on_open=on_open,
                                on_message=on_message,
                                on_error=on_error,
                                on_close=on_close)

    ws.run_forever(sslopt={"cert_reqs": ssl.CERT_NONE})

Update frequency

Currently, orderbook websockets send messages every 1000ms. Connections that contain frequent updates are coming soon.

Trades

Trades feed subscribe messages take a similar form to orderbook data, just change the channel.

Perp markets

{ "type": "subscribe", "marketType": "perp", "channel": "trades", "market": "SOL-PERP" }

Response

Field Description
channel Identifies the type of data being streamed. trades_perp_0 indicates data for trades in perp market 0. (SOL-PERP)
ts Timestamp of the trade.
marketIndex Index of the market where the trade occurred.
marketType Type of the market, here it's perp for perpetual.
filler The address or identifier of the filler (taker) in the trade.
takerFee Fee paid by the taker in the trade.
makerFee Fee paid or received by the maker in the trade.
quoteAssetAmountSurplus Surplus amount in quote asset.
baseAssetAmountFilled The amount of the base asset that was filled in this trade.
quoteAssetAmountFilled The amount of the quote asset that was filled in this trade.
takerOrderId Order ID of the taker's order, if available.
takerOrderBaseAssetAmount Base asset amount specified in the taker's order.
takerOrderCumulativeBaseAssetAmountFilled Cumulative base asset amount filled in the taker's order.
takerOrderCumulativeQuoteAssetAmountFilled Cumulative quote asset amount filled in the taker's order.
maker The address or identifier of the maker in the trade.
makerOrderId Order ID of the maker's order.
makerOrderDirection Direction of the maker's order (e.g., 'short' or 'long').
makerOrderBaseAssetAmount Base asset amount specified in the maker's order.
makerOrderCumulativeBaseAssetAmountFilled Cumulative base asset amount filled in the maker's order.
makerOrderCumulativeQuoteAssetAmountFilled Cumulative quote asset amount filled in the maker's order.
oraclePrice The oracle price at the time of the trade.
txSig Transaction signature.
slot Slot number in which the trade occurred.
action fill.
actionExplanation Explanation of the action (e.g., 'orderFilledWithAmm' indicating order filled with Automated Market Maker).
referrerReward Reward amount for the referrer, if applicable.

Spot markets

{ "type": "subscribe", "marketType": "spot", "channel": "trades", "market": "SOL" }

Response

Field Description
channel Identifies the type of data being streamed. trades_spot_1 indicates data for trades in spot market 1 (SOL/USDC).
ts Timestamp of the trade.
marketIndex Index of the market where the trade occurred.
marketType Type of the market, here it's spot.
filler The address or identifier of the filler (taker) in the trade.
takerFee Fee paid by the taker in the trade.
makerFee Fee paid or received by the maker in the trade.
quoteAssetAmountSurplus Surplus amount in quote asset.
baseAssetAmountFilled The amount of the base asset that was filled in this trade.
quoteAssetAmountFilled The amount of the quote asset that was filled in this trade.
taker The address or identifier of the taker in the trade.
takerOrderId Order ID of the taker's order, if available.
takerOrderDirection Direction of the taker's order (e.g., 'long').
takerOrderBaseAssetAmount Base asset amount specified in the taker's order.
takerOrderCumulativeBaseAssetAmountFilled Cumulative base asset amount filled in the taker's order.
takerOrderCumulativeQuoteAssetAmountFilled Cumulative quote asset amount filled in the taker's order.
maker The address or identifier of the maker in the trade.
makerOrderId Order ID of the maker's order.
makerOrderDirection Direction of the maker's order (e.g., 'short').
makerOrderBaseAssetAmount Base asset amount specified in the maker's order.
makerOrderCumulativeBaseAssetAmountFilled Cumulative base asset amount filled in the maker's order.
makerOrderCumulativeQuoteAssetAmountFilled Cumulative quote asset amount filled in the maker's order.
oraclePrice The oracle price at the time of the trade.
txSig Transaction signature.
slot Slot number in which the trade occurred.
action Type of action that occurred (e.g., 'fill').
actionExplanation Explanation of the action (e.g., 'orderFilledWithMatch' indicating order filled with a matching order).
referrerReward Reward amount for the referrer, if applicable.

Websocket - Unsubscribing

To unsubscribe to a channel, send a subscribe-like message, with the message type set to unsubscribe. Unsubscribe requests to channels not previously subscribed to will have no impact. { "type": "unsubscribe", "marketType": "perp", "channel": "orderbook", "market": "SOL-PERP" }

Websocket - Liveness measure

To alleviate backpressure on websocket servers, drift websockets stop sending messages to clients that have more than 50 messages unproccessed in their buffer, until the buffer is cleared and the messages are processed. We recommend listening for heartbeat messages from the server to determine if your client is still receiving messages.

Heartbeat messages are sent every 5 seconds and take the following form: {"channel": "heartbeat"}

Ping/pong

No ping messages are sent from the websocket server. Unsolicited ping messages are allowed and will receive pongs back.

Historical Data

Snapshots are collected by parsing on-chain transaction logs. For convience the below are parsed logs collected, stored as a CSV, and stored off-chain (~99% of records). Records are updated once per day.

Please share any transaction signatures or time ranges you believe might be missing in Drift Protocol Discord.

URL Prefix

mainnet-beta: https://drift-historical-data-v2.s3.eu-west-1.amazonaws.com/program/dRiftyHA39MWEi3m9aunc5MzRF1JYuBsbn6VPcn33UH/

URL Suffix

Schema

recordType url suffix
trades user/${accountKey}/tradeRecords/${year}/${year}${month}${day}
market-trades market/${marketSymbol}/tradeRecords/${year}/${year}${month}${day}
funding-rates market/${marketSymbol}/fundingRateRecords/${year}/${year}${month}${day}
funding-payments user/${accountKey}/fundingPaymentRecords/${year}/${year}${month}${day}
deposits user/${accountKey}/depositRecords/${year}/${year}${month}${day}
liquidations user/${accountKey}/liquidationRecords/${year}/${year}${month}${day}
settle-pnl user/${accountKey}/settlePnlRecords/${year}/${year}${month}${day}
lp user/${accountKey}/lpRecord/${year}/${year}${month}${day}
insurance-fund market/${marketSymbol}/insuranceFundRecords/${year}/${year}${month}${day}
insurance-fund-stake authority/${authorityAccountKey}/insuranceFundStakeRecords/${year}/${year}${month}${day}

Variables

variable description example
accountKey user sub account public key (not authority)
authority authority public key
marketSymbol market name SOL-PERP
year 2023
month 4
day utc time 25
candleResolution 1M

Examples

Get historical trades on SOL-PERP for August 5, 2023: https://drift-historical-data-v2.s3.eu-west-1.amazonaws.com/program/dRiftyHA39MWEi3m9aunc5MzRF1JYuBsbn6VPcn33UH/market/SOL-PERP/tradeRecords/2024/20240101

import requests

outcsv = requets.get('https://drift-historical-data-v2.s3.eu-west-1.amazonaws.com/program/dRiftyHA39MWEi3m9aunc5MzRF1JYuBsbn6VPcn33UH/user/FrEFAwxdrzHxgc7S4cuFfsfLmcg8pfbxnkCQW83euyCS/tradeRecords/2023/20230201')'
curl https://drift-historical-data-v2.s3.eu-west-1.amazonaws.com/program/dRiftyHA39MWEi3m9aunc5MzRF1JYuBsbn6VPcn33UH/user/FrEFAwxdrzHxgc7S4cuFfsfLmcg8pfbxnkCQW83euyCS/tradeRecords/2023/20230201 --output out.csv.gz

Errors

Drift Protocol uses has following error codes:

code name msg
6000 InvalidSpotMarketAuthority Invalid Spot Market Authority
6001 InvalidInsuranceFundAuthority Clearing house not insurance fund authority
6002 InsufficientDeposit Insufficient deposit
6003 InsufficientCollateral Insufficient collateral
6004 SufficientCollateral Sufficient collateral
6005 MaxNumberOfPositions Max number of positions taken
6006 AdminControlsPricesDisabled Admin Controls Prices Disabled
6007 MarketDelisted Market Delisted
6008 MarketIndexAlreadyInitialized Market Index Already Initialized
6009 UserAccountAndUserPositionsAccountMismatch User Account And User Positions Account Mismatch
6010 UserHasNoPositionInMarket User Has No Position In Market
6011 InvalidInitialPeg Invalid Initial Peg
6012 InvalidRepegRedundant AMM repeg already configured with amt given
6013 InvalidRepegDirection AMM repeg incorrect repeg direction
6014 InvalidRepegProfitability AMM repeg out of bounds pnl
6015 SlippageOutsideLimit Slippage Outside Limit Price
6016 OrderSizeTooSmall Order Size Too Small
6017 InvalidUpdateK Price change too large when updating K
6018 AdminWithdrawTooLarge Admin tried to withdraw amount larger than fees collected
6019 MathError Math Error
6020 BnConversionError Conversion to u128/u64 failed with an overflow or underflow
6021 ClockUnavailable Clock unavailable
6022 UnableToLoadOracle Unable To Load Oracles
6023 PriceBandsBreached Price Bands Breached
6024 ExchangePaused Exchange is paused
6025 InvalidWhitelistToken Invalid whitelist token
6026 WhitelistTokenNotFound Whitelist token not found
6027 InvalidDiscountToken Invalid discount token
6028 DiscountTokenNotFound Discount token not found
6029 ReferrerNotFound Referrer not found
6030 ReferrerStatsNotFound ReferrerNotFound
6031 ReferrerMustBeWritable ReferrerMustBeWritable
6032 ReferrerStatsMustBeWritable ReferrerMustBeWritable
6033 ReferrerAndReferrerStatsAuthorityUnequal ReferrerAndReferrerStatsAuthorityUnequal
6034 InvalidReferrer InvalidReferrer
6035 InvalidOracle InvalidOracle
6036 OracleNotFound OracleNotFound
6037 LiquidationsBlockedByOracle Liquidations Blocked By Oracle
6038 MaxDeposit Can not deposit more than max deposit
6039 CantDeleteUserWithCollateral Can not delete user that still has collateral
6040 InvalidFundingProfitability AMM funding out of bounds pnl
6041 CastingFailure Casting Failure
6042 InvalidOrder InvalidOrder
6043 InvalidOrderMaxTs InvalidOrderMaxTs
6044 InvalidOrderMarketType InvalidOrderMarketType
6045 InvalidOrderForInitialMarginReq InvalidOrderForInitialMarginReq
6046 InvalidOrderNotRiskReducing InvalidOrderNotRiskReducing
6047 InvalidOrderSizeTooSmall InvalidOrderSizeTooSmall
6048 InvalidOrderNotStepSizeMultiple InvalidOrderNotStepSizeMultiple
6049 InvalidOrderBaseQuoteAsset InvalidOrderBaseQuoteAsset
6050 InvalidOrderIOC InvalidOrderIOC
6051 InvalidOrderPostOnly InvalidOrderPostOnly
6052 InvalidOrderIOCPostOnly InvalidOrderIOCPostOnly
6053 InvalidOrderTrigger InvalidOrderTrigger
6054 InvalidOrderAuction InvalidOrderAuction
6055 InvalidOrderOracleOffset InvalidOrderOracleOffset
6056 InvalidOrderMinOrderSize InvalidOrderMinOrderSize
6057 PlacePostOnlyLimitFailure Failed to Place Post-Only Limit Order
6058 UserHasNoOrder User has no order
6059 OrderAmountTooSmall Order Amount Too Small
6060 MaxNumberOfOrders Max number of orders taken
6061 OrderDoesNotExist Order does not exist
6062 OrderNotOpen Order not open
6063 FillOrderDidNotUpdateState FillOrderDidNotUpdateState
6064 ReduceOnlyOrderIncreasedRisk Reduce only order increased risk
6065 UnableToLoadAccountLoader Unable to load AccountLoader
6066 TradeSizeTooLarge Trade Size Too Large
6067 UserCantReferThemselves User cant refer themselves
6068 DidNotReceiveExpectedReferrer Did not receive expected referrer
6069 CouldNotDeserializeReferrer Could not deserialize referrer
6070 CouldNotDeserializeReferrerStats Could not deserialize referrer stats
6071 UserOrderIdAlreadyInUse User Order Id Already In Use
6072 NoPositionsLiquidatable No positions liquidatable
6073 InvalidMarginRatio Invalid Margin Ratio
6074 CantCancelPostOnlyOrder Cant Cancel Post Only Order
6075 InvalidOracleOffset InvalidOracleOffset
6076 CantExpireOrders CantExpireOrders
6077 CouldNotLoadMarketData CouldNotLoadMarketData
6078 PerpMarketNotFound PerpMarketNotFound
6079 InvalidMarketAccount InvalidMarketAccount
6080 UnableToLoadPerpMarketAccount UnableToLoadMarketAccount
6081 MarketWrongMutability MarketWrongMutability
6082 UnableToCastUnixTime UnableToCastUnixTime
6083 CouldNotFindSpotPosition CouldNotFindSpotPosition
6084 NoSpotPositionAvailable NoSpotPositionAvailable
6085 InvalidSpotMarketInitialization InvalidSpotMarketInitialization
6086 CouldNotLoadSpotMarketData CouldNotLoadSpotMarketData
6087 SpotMarketNotFound SpotMarketNotFound
6088 InvalidSpotMarketAccount InvalidSpotMarketAccount
6089 UnableToLoadSpotMarketAccount UnableToLoadSpotMarketAccount
6090 SpotMarketWrongMutability SpotMarketWrongMutability
6091 SpotMarketInterestNotUpToDate SpotInterestNotUpToDate
6092 SpotMarketInsufficientDeposits SpotMarketInsufficientDeposits
6093 UserMustSettleTheirOwnPositiveUnsettledPNL UserMustSettleTheirOwnPositiveUnsettledPNL
6094 CantUpdatePoolBalanceType CantUpdatePoolBalanceType
6095 InsufficientCollateralForSettlingPNL InsufficientCollateralForSettlingPNL
6096 AMMNotUpdatedInSameSlot AMMNotUpdatedInSameSlot
6097 AuctionNotComplete AuctionNotComplete
6098 MakerNotFound MakerNotFound
6099 MakerStatsNotFound MakerNotFound
6100 MakerMustBeWritable MakerMustBeWritable
6101 MakerStatsMustBeWritable MakerMustBeWritable
6102 MakerOrderNotFound MakerOrderNotFound
6103 CouldNotDeserializeMaker CouldNotDeserializeMaker
6104 CouldNotDeserializeMakerStats CouldNotDeserializeMaker
6105 AuctionPriceDoesNotSatisfyMaker AuctionPriceDoesNotSatisfyMaker
6106 MakerCantFulfillOwnOrder MakerCantFulfillOwnOrder
6107 MakerOrderMustBePostOnly MakerOrderMustBePostOnly
6108 CantMatchTwoPostOnlys CantMatchTwoPostOnlys
6109 OrderBreachesOraclePriceLimits OrderBreachesOraclePriceLimits
6110 OrderMustBeTriggeredFirst OrderMustBeTriggeredFirst
6111 OrderNotTriggerable OrderNotTriggerable
6112 OrderDidNotSatisfyTriggerCondition OrderDidNotSatisfyTriggerCondition
6113 PositionAlreadyBeingLiquidated PositionAlreadyBeingLiquidated
6114 PositionDoesntHaveOpenPositionOrOrders PositionDoesntHaveOpenPositionOrOrders
6115 AllOrdersAreAlreadyLiquidations AllOrdersAreAlreadyLiquidations
6116 CantCancelLiquidationOrder CantCancelLiquidationOrder
6117 UserIsBeingLiquidated UserIsBeingLiquidated
6118 LiquidationsOngoing LiquidationsOngoing
6119 WrongSpotBalanceType WrongSpotBalanceType
6120 UserCantLiquidateThemself UserCantLiquidateThemself
6121 InvalidPerpPositionToLiquidate InvalidPerpPositionToLiquidate
6122 InvalidBaseAssetAmountForLiquidatePerp InvalidBaseAssetAmountForLiquidatePerp
6123 InvalidPositionLastFundingRate InvalidPositionLastFundingRate
6124 InvalidPositionDelta InvalidPositionDelta
6125 UserBankrupt UserBankrupt
6126 UserNotBankrupt UserNotBankrupt
6127 UserHasInvalidBorrow UserHasInvalidBorrow
6128 DailyWithdrawLimit DailyWithdrawLimit
6129 DefaultError DefaultError
6130 InsufficientLPTokens Insufficient LP tokens
6131 CantLPWithPerpPosition Cant LP with a market position
6132 UnableToBurnLPTokens Unable to burn LP tokens
6133 TryingToRemoveLiquidityTooFast Trying to remove liqudity too fast after adding it
6134 InvalidSpotMarketVault Invalid Spot Market Vault
6135 InvalidSpotMarketState Invalid Spot Market State
6136 InvalidSerumProgram InvalidSerumProgram
6137 InvalidSerumMarket InvalidSerumMarket
6138 InvalidSerumBids InvalidSerumBids
6139 InvalidSerumAsks InvalidSerumAsks
6140 InvalidSerumOpenOrders InvalidSerumOpenOrders
6141 FailedSerumCPI FailedSerumCPI
6142 FailedToFillOnExternalMarket FailedToFillOnExternalMarket
6143 InvalidFulfillmentConfig InvalidFulfillmentConfig
6144 InvalidFeeStructure InvalidFeeStructure
6145 InsufficientIFShares Insufficient IF shares
6146 MarketActionPaused the Market has paused this action
6147 MarketPlaceOrderPaused the Market status doesnt allow placing orders
6148 MarketFillOrderPaused the Market status doesnt allow filling orders
6149 MarketWithdrawPaused the Market status doesnt allow withdraws
6150 ProtectedAssetTierViolation Action violates the Protected Asset Tier rules
6151 IsolatedAssetTierViolation Action violates the Isolated Asset Tier rules
6152 UserCantBeDeleted User Cant Be Deleted
6153 ReduceOnlyWithdrawIncreasedRisk Reduce Only Withdraw Increased Risk
6154 MaxOpenInterest Max Open Interest
6155 CantResolvePerpBankruptcy Cant Resolve Perp Bankruptcy
6156 LiquidationDoesntSatisfyLimitPrice Liquidation Doesnt Satisfy Limit Price
6157 MarginTradingDisabled Margin Trading Disabled
6158 InvalidMarketStatusToSettlePnl Invalid Market Status to Settle Perp Pnl
6159 PerpMarketNotInSettlement PerpMarketNotInSettlement
6160 PerpMarketNotInReduceOnly PerpMarketNotInReduceOnly
6161 PerpMarketSettlementBufferNotReached PerpMarketSettlementBufferNotReached
6162 PerpMarketSettlementUserHasOpenOrders PerpMarketSettlementUserHasOpenOrders
6163 PerpMarketSettlementUserHasActiveLP PerpMarketSettlementUserHasActiveLP
6164 UnableToSettleExpiredUserPosition UnableToSettleExpiredUserPosition
6165 UnequalMarketIndexForSpotTransfer UnequalMarketIndexForSpotTransfer
6166 InvalidPerpPositionDetected InvalidPerpPositionDetected
6167 InvalidSpotPositionDetected InvalidSpotPositionDetected
6168 InvalidAmmDetected InvalidAmmDetected
6169 InvalidAmmForFillDetected InvalidAmmForFillDetected
6170 InvalidAmmLimitPriceOverride InvalidAmmLimitPriceOverride
6171 InvalidOrderFillPrice InvalidOrderFillPrice
6172 SpotMarketBalanceInvariantViolated SpotMarketBalanceInvariantViolated
6173 SpotMarketVaultInvariantViolated SpotMarketVaultInvariantViolated
6174 InvalidPDA InvalidPDA
6175 InvalidPDASigner InvalidPDASigner
6176 RevenueSettingsCannotSettleToIF RevenueSettingsCannotSettleToIF
6177 NoRevenueToSettleToIF NoRevenueToSettleToIF
6178 NoAmmPerpPnlDeficit NoAmmPerpPnlDeficit
6179 SufficientPerpPnlPool SufficientPerpPnlPool
6180 InsufficientPerpPnlPool InsufficientPerpPnlPool
6181 PerpPnlDeficitBelowThreshold PerpPnlDeficitBelowThreshold
6182 MaxRevenueWithdrawPerPeriodReached MaxRevenueWithdrawPerPeriodReached
6183 MaxIFWithdrawReached InvalidSpotPositionDetected
6184 NoIFWithdrawAvailable NoIFWithdrawAvailable
6185 InvalidIFUnstake InvalidIFUnstake
6186 InvalidIFUnstakeSize InvalidIFUnstakeSize
6187 InvalidIFUnstakeCancel InvalidIFUnstakeCancel
6188 InvalidIFForNewStakes InvalidIFForNewStakes
6189 InvalidIFRebase InvalidIFRebase
6190 InvalidInsuranceUnstakeSize InvalidInsuranceUnstakeSize
6191 InvalidOrderLimitPrice InvalidOrderLimitPrice
6192 InvalidIFDetected InvalidIFDetected
6193 InvalidAmmMaxSpreadDetected InvalidAmmMaxSpreadDetected
6194 InvalidConcentrationCoef InvalidConcentrationCoef
6195 InvalidSrmVault InvalidSrmVault
6196 InvalidVaultOwner InvalidVaultOwner
6197 InvalidMarketStatusForFills InvalidMarketStatusForFills
6198 IFWithdrawRequestInProgress IFWithdrawRequestInProgress
6199 NoIFWithdrawRequestInProgress NoIFWithdrawRequestInProgress
6200 IFWithdrawRequestTooSmall IFWithdrawRequestTooSmall
6201 IncorrectSpotMarketAccountPassed IncorrectSpotMarketAccountPassed
6202 BlockchainClockInconsistency BlockchainClockInconsistency
6203 InvalidIFSharesDetected InvalidIFSharesDetected
6204 NewLPSizeTooSmall NewLPSizeTooSmall
6205 MarketStatusInvalidForNewLP MarketStatusInvalidForNewLP
6206 InvalidMarkTwapUpdateDetected InvalidMarkTwapUpdateDetected
6207 MarketSettlementAttemptOnActiveMarket MarketSettlementAttemptOnActiveMarket
6208 MarketSettlementRequiresSettledLP MarketSettlementRequiresSettledLP
6209 MarketSettlementAttemptTooEarly MarketSettlementAttemptTooEarly
6210 MarketSettlementTargetPriceInvalid MarketSettlementTargetPriceInvalid
6211 UnsupportedSpotMarket UnsupportedSpotMarket
6212 SpotOrdersDisabled SpotOrdersDisabled
6213 MarketBeingInitialized Market Being Initialized
6214 InvalidUserSubAccountId Invalid Sub Account Id
6215 InvalidTriggerOrderCondition Invalid Trigger Order Condition
6216 InvalidSpotPosition Invalid Spot Position
6217 CantTransferBetweenSameUserAccount Cant transfer between same user account
6218 InvalidPerpPosition Invalid Perp Position
6219 UnableToGetLimitPrice Unable To Get Limit Price
6220 InvalidLiquidation Invalid Liquidation
6221 SpotFulfillmentConfigDisabled Spot Fulfullment Config Disabled
6222 InvalidMaker Invalid Maker
6223 FailedUnwrap Failed Unwrap
6224 MaxNumberOfUsers Max Number Of Users
6225 InvalidOracleForSettlePnl InvalidOracleForSettlePnl
6226 MarginOrdersOpen MarginOrdersOpen
6227 TierViolationLiquidatingPerpPnl TierViolationLiquidatingPerpPnl
6228 CouldNotLoadUserData CouldNotLoadUserData
6229 UserWrongMutability UserWrongMutability
6230 InvalidUserAccount InvalidUserAccount
6231 CouldNotLoadUserStatsData CouldNotLoadUserData
6232 UserStatsWrongMutability UserWrongMutability
6233 InvalidUserStatsAccount InvalidUserAccount
6234 UserNotFound UserNotFound
6235 UnableToLoadUserAccount UnableToLoadUserAccount
6236 UserStatsNotFound UserStatsNotFound
6237 UnableToLoadUserStatsAccount UnableToLoadUserStatsAccount
6238 UserNotInactive User Not Inactive
6239 RevertFill RevertFill
6240 InvalidMarketAccountforDeletion Invalid MarketAccount for Deletion
6241 InvalidSpotFulfillmentParams Invalid Spot Fulfillment Params
6242 FailedToGetMint Failed to Get Mint
6243 FailedPhoenixCPI FailedPhoenixCPI
6244 FailedToDeserializePhoenixMarket FailedToDeserializePhoenixMarket
6245 InvalidPricePrecision InvalidPricePrecision
6246 InvalidPhoenixProgram InvalidPhoenixProgram
6247 InvalidPhoenixMarket InvalidPhoenixMarket