` 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', 'confirmed');
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', 'confirmed');

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();
  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")

TypeScript

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

Python

Parameter Description Optional Default
connection AsyncClient connection to the Solana cluster No
wallet Wallet object, can be Keypair or Wallet type No
env Drift environment, 'devnet' or 'mainnet' Yes "mainnet"
program_id Drift program identifier Yes DRIFT_PROGRAM_ID
opts Options for transaction confirmation status Yes DEFAULT_TX_OPTIONS
authority Public key of the user account authority Yes None
account_subscription Configuration for account subscriptions (websockets/polling) Yes AccountSubscriptionConfig.default()
perp_market_indexes List of perpetual market indexes to interact with Yes None
spot_market_indexes List of spot market indexes to interact with Yes None
oracle_infos List of OracleInfo objects for market data Yes None
tx_params Additional parameters for transactions Yes None
tx_version Version of the transaction Yes None
tx_sender Object handling the sending of transactions Yes None
active_sub_account_id ID of the initially active sub-account Yes None
sub_account_ids List of sub-account IDs to subscribe to Yes None
market_lookup_table Public key for the market lookup table Yes None

User Initialization

const [txSig, userPublickKey] = await driftClient.initializeUserAccount(
  0,
  "toly"
);
tx_sig = await drift_client.initialize_user(sub_account_id=0, name="toly")

TypeScript

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

Python

Parameter Description Optional Default
sub_account_id The sub account id for the new user account. Yes 0
name Display name for the user account Yes None
referrer_info The address of the referrer and referrer stats accounts Yes None

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() in TypeScript.

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 driftClient.updateUserCustomMarginRatio([
    {
        'marginRatio': MARGIN_PRECISION,
        'subAccountId': subaccountId
    }
]);

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

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

Switching Sub Accounts

driftClient.switchActiveUser(
  1,
);
drift_client.switch_active_user(sub_account_id=1)

<<<<<<< HEAD

TypeScript

======= shell curl http://localhost:8080/v2/resource?subAccountId=1

upstream/main | 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 |

Python

Parameter Description Optional Default
sub_account_id Identifier for the sub-account to be activated No

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);

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

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

tx_sig = await drift_client.deposit(amount, spot_market_index)

TypeScript

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 to Yes active sub account
reduceOnly Whether the deposit should only reduce borrow Yes false

Python

Parameter Description Optional Default
amount The amount to deposit in the spot market's token mint precision No
spot_market_index The index of the spot market where the deposit is made No
user_token_account The public key of the token account from which you are depositing Yes None (will derive ATA)
sub_account_id The sub account to which the deposit is being made Yes Active sub-account
reduce_only Whether the deposit should only reduce borrow Yes false
user_initialized Indicates if the user is already initialized (used internally, typically) Yes true

Withdrawing

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

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

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

# derive user ata to withdraw to
# ...

tx_sig = await drift_client.withdraw(amount, spot_market_index, ata_to_withdraw_to)

TypeScript

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

Python

Parameter Description Optional Default
amount The amount to withdraw in the spot market's token mint precision No
market_index The index of the spot market from which the withdrawal is made No
user_token_account The public key of the token account to which you are withdrawing No
reduce_only Whether the withdrawal should only decrease a deposit, blocking new borrows Yes false
sub_account_id The sub account from which the withdrawal is made Yes None

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;

await driftClient.transferDeposit(
  amount,
  marketIndex,
  fromSubAccountId,
  toSubAccountId,
);
market_index = 0
<<<<<<< HEAD
amount = drift_client.convert_to_spot_precision(100, market_index)
=======
amount = drift_client.convert_to_spot_precision(market_index, 100)
>>>>>>> upstream/main
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,
)

TypeScript

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

Python

Parameter Description Optional Default
amount The amount to transfer in the spot market's token mint precision No
market_index The spot market index you're transferring deposits in No
from_sub_account_id The sub account from which the funds are withdrawn No
to_sub_account_id The sub account to which the funds are deposited No

Order Types

TypeScript

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.

Python

Market(), Limit(), Oracle() orders all support auction parameters.

Type Description
Market() Market order.
Limit() Limit order.
TriggerMarket() Stop / Take-profit market order.
TriggerLimit() Stop / Take-profit limit order.
Oracle() Market order using oracle offset for auction parameters.

Order Params

TypeScript

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

Python

Parameter Description Optional Default
order_type Type of order No
market_index Index of the market to place order in No
direction Direction of the order No
base_asset_amount Amount of base asset to buy or sell No
market_type Type of market Yes Depends on method
price Limit price for the order Yes 0
user_order_id Unique order ID specified by user Yes 0
reduce_only Whether the order is only to reduce positions Yes false
post_only If the order should only be a maker Yes PostOnlyParams.NONE()
immediate_or_cancel Whether the order is immediate or cancel Yes false
max_ts Max timestamp for the order expiry Yes None
trigger_price Trigger price for trigger orders Yes None
trigger_condition Condition for triggering the order Yes OrderTriggerCondition.Above()
oracle_price_offset Offset for oracle-derived limit price Yes None
auction_duration Duration of the auction in slots Yes None
auction_start_price Starting price of the auction Yes None
auction_end_price Ending price of the auction Yes None

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(),
            base_asset_amount=drift_client.convert_to_perp_precision(1),
            market_index=market_index,
            direction=PositionDirection.Long(),
            price=drift_client.convert_to_price_precision(21.88),
            post_only=PostOnlyParams.TryPostOnly(),
        )
await drift_client.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

TypeScript

Parameter Description Optional Default
orderParams The order params No

Python

Parameter Description Optional Default
order_params The order params No

The order type is set to PERP / 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(),
            base_asset_amount=drift_client.convert_to_spot_precision(100, market_index),
            market_index=market_index,
            direction=PositionDirection.Long(),
            price=drift_client.convert_to_price_precision(100),
        )

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

TypeScript

Parameter Description Optional Default
orderParams The order params No

Python

Parameter Description Optional Default
order_params The order params No

The order type is set to SPOT / 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(),
    base_asset_amount=drift_client.convert_to_perp_precision(100),
    market_index=0,
    direction=PositionDirection.Long(),
    market_type=MarketType.Perp(),
    price=drift_client.convert_to_price_precision(21.23),
  ),
   OrderParams(
    order_type=OrderType.Limit(),
    base_asset_amount=drift_client.convert_to_perp_precision(100),
    market_index=0,
    direction=PositionDirection.Short(),
    market_type=MarketType.Perp(),
    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

TypeScript

Parameter Description Optional Default
placeOrderParams Parameters for place order instructions

Python

Parameter Description Optional Default
place_order_params 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(
  order_type=OrderType.Oracle(),
  base_asset_amount=drift_client.convert_to_perp_precision(10),
  market_index=18,
  direction=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);

TypeScript

Parameter Description Optional Default
orderId The order being canceled No

Python

Parameter Description Optional Default
order_id The order being canceled No
sub_account_id Sub account to cancel order under Yes None
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

TypeScript

Parameter Description Optional Default
userOrderId Unique order id specified by user when order was placed No

Python

Parameter Description Optional Default
user_order_id Unique order id specified by user when order was placed No
sub_account_id Sub account to cancel orders under Yes None

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

TypeScript

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

Python

Parameter Description Optional Default
market_type The market type of orders to cancel Yes None
market_index The market index of orders to cancel Yes None
direction The direction of orders to cancel Yes None
sub_account_id The sub account from which to cancel orders Yes None

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(),
    base_asset_amount=drift_client.convert_to_perp_precision(100),
    market_index=0,
    direction=PositionDirection.Long(),
    market_type=MarketType.Perp(),
    price=drift_client.convert_to_price_precision(21.23),
  ),
   OrderParams(
    order_type=OrderType.Limit(),
    base_asset_amount=drift_client.convert_to_perp_precision(100),
    market_index=0,
    direction=PositionDirection.Short(),
    market_type=MarketType.Perp(),
    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

TypeScript

Parameter Description Optional Default
cancelOrderParams Parameters for cancel orders instruction
placeOrderParams Parameters for place order instructions

Python

Parameter Description Optional Default
cancel_params Tuple with optional MarketType, market index, and PositionDirection for canceling orders Yes None
place_order_params List of OrderParams for placing new orders No
sub_account_id The sub account to use Yes None

To cancel all orders, do not set any parameters.

Modify Order Params

TypeScript

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

Python

Parameter Description Optional Default
direction The direction of the order Yes None
base_asset_amount Amount of base asset to buy or sell Yes None
price Limit price for the order Yes None
reduce_only If the order should only reduce positions Yes None
post_only If the order should only be a maker order Yes None
immediate_or_cancel Whether the order is immediate or cancel Yes None
max_ts Max timestamp for order expiry Yes None
trigger_price Trigger price for trigger orders Yes None
trigger_condition Condition for triggering the order Yes None
oracle_price_offset Offset for oracle-derived limit price Yes None
auction_duration Duration of the auction in slots Yes None
auction_start_price Starting price of the auction Yes None
auction_end_price Ending price of the auction Yes None
policy Policy for modifying the order Yes None

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

TypeScript

Parameter Description Optional Default
orderId The order id of order to modify No
modifyOrderParams The modify order params Yes

Python

Parameter Description Optional Default
order_id The order ID of the order to modify No
modify_order_params The parameters for modifying the order No
sub_account_id The sub account to use Yes None

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

TypeScript

Parameter Description Optional Default
userOrderId The user order id of order to modify No
modifyOrderParams The modify order params Yes

Python

Parameter Description Optional Default
user_order_id The user order ID of the order to modify No
modify_order_params The parameters for modifying the order No
sub_account_id The sub-account ID associated with the order Yes None

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
)

TypeScript

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

Python

Parameter Description Optional Default
settlee_user_account_public_key Public key of the user account to settle PNL for No
settlee_user_account User account data for PNL settlement No
market_index Index of the perpetual market for PNL settlement 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);

TypeScript

Parameter Description Optional Default
marketIndex The market index for the spot market No

Python

Parameter Description Optional Default
market_index 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);

TypeScript

Parameter Description Optional Default
marketIndex The market index for the perp market No

Python

Parameter Description Optional Default
market_index The market index for the perp market No

User

Get User

   const user = driftClient.getUser();
  user = drift_client.get_user();

TypeScript

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

Python

Parameter Description Optional Default
sub_account_id The sub account id of user to get Yes active sub account

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

TypeScript

Parameter Description Optional Default
marketIndex Market index for the spot market No

Python

Parameter Description Optional Default
market_index 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

TypeScript

Parameter Description Optional Default
marketIndex Market index for the perp market No

Python

Parameter Description Optional Default
market_index 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)

TypeScript

Parameter Description Optional Default
orderId Order id for the order you're getting No

Python

Parameter Description Optional Default
order_id 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)

TypeScript

Parameter Description Optional Default
userOrderId User order id for the order you're getting No

Python

Parameter Description Optional Default
user_order_id 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()

TypeScript

Parameter Description Optional Default
withFunding Whether to include unsettled funding payments Yes false
marketIndex Index of a specific market for PNL calculation Yes
withWeightMarginCategory To include margin category weighting in PNL calculation Yes
strict Whether the calculation should be strict Yes false

Python

Parameter Description Optional Default
with_funding Whether to include unsettled funding payments Yes false
market_index Index of a specific market for PNL calculation Yes
with_weight_margin_category To include margin category weighting in PNL calculation Yes
strict Whether the calculation should be strict Yes false

Get Unrealized Funding Pnl

  const pnl = user.getUnrealizedFundingPNL();
  pnl = user.get_unrealized_funding_pnl()

TypeScript

Parameter Description Optional Default
marketIndex Whether to only return pnl for specific market Yes

Python

Parameter Description Optional Default
market_index Whether to only return pnl for specific market Yes

Get Total Collateral

  const totalCollateral = user.getTotalCollateral();
  total_collateral = user.get_total_collateral()

TypeScript

Parameter Description Optional Default
marginCategory Initial or Maintenance Yes Initial
strict Whether the calculation should be strict Yes false

Python

Parameter Description Optional Default
margin_category Initial or Maintenance Yes Initial
strict Whether the calculation should be strict Yes false

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 = user.getMarginRequirement();
  margin_requirement = user.get_margin_requirement()

TypeScript

Parameter Description Optional Default
marginCategory The type of margin (Initial or Maintenance) No
liquidationBuffer Buffer value for liquidation calculation Yes
strict Whether the calculation should be strict Yes false

Python

Parameter Description Optional Default
margin_category Type of margin, either Initial or Maintenance Yes MarginCategory.INITIAL
liquidation_buffer Additional buffer value for liquidation calculation Yes 0
strict Whether the calculation should be strict Yes False

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()

TypeScript

Parameter Description Optional Default
marginCategory Initial or Maintenance Yes Initial

Python

Parameter Description Optional Default
marginCategory Type of margin, either Initial or Maintenance Yes MarginCategory.INITIAL

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

Get Leverage

  const leverage = user.getLeverage();
  leverage = user.get_leverage()

TypeScript

Parameter Description Optional Default
includeOpenOrders Whether to factor in open orders in position size Yes true

Python

Parameter Description Optional Default
include_open_orders 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)

TypeScript

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

Python

Parameter Description Optional Default
connection AsyncClient object specifying Solana RPC URL No
program Anchor program object used to deserialize events from transaction logs No
options Configuration options for event subscription Yes EventSubscriptionOptions.default()
- address Address to listen to events for, defaults to Drift program Yes DRIFT_PROGRAM_ID
- event_types Types of events to trigger event callbacks for Yes DEFAULT_EVENT_TYPES
- max_events_per_type Maximum number of events per event type to keep in memory Yes 4096
- order_by Sort order of transactions, by 'blockchain' order or 'client' received order Yes "blockchain"
- order_dir Sorting direction, either most recent ('desc') or oldest ('asc') Yes "asc"
- commitment Transaction commitment level to wait for Yes "confirmed"
- max_tx Maximum number of transactions to keep in memory Yes 4096
- log_provider_config Configuration for log provider, either websocket or polling Yes WebsocketLogProviderConfig()
- until_tx Signature to listen until, used for log provider Yes None

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

const txSig = '3dq5PtQ3VnNTkQRrHhQ1nRACWZaFVvSBKs1RLXM8WvCqLHTzTuVGc7XER5awoLFLTdJ4kqZiNmo7e8b3pXaEGaoo';
const events = eventSubscriber.getEventsByTx(txSig);

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.

For prediction markets, the price used in the margin system depends on the direction of the position. For short positions (betting no), the margin system uses 1 - oracle price. For long position (betting yes), it uses the oracle price. This is the account for the user's worst case loss. E.g. if a user shorts at $.01, their worst case loss is $.99.

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));
perp_market_index = 0 # SOL-PERP
perp_position = drift_client.get_perp_position(market_index)
print(convert_to_number(perp_position.base_asset_amount, 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));
spot_market_index = 0 # USDC
spot_market = drift_client.get_spot_market_account(spot_market_index)
spot_position = drift_client.get_spot_position(spot_market_index)
token_amount = get_token_amount(spot_psoition.scaled_balance, spot_market, spot_position.balance_type)
print(convert_to_number(token_amount, (10 ** spot_market.decimals)))

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
imf weight precision 1e6 SPOT_IMF_PRECISION

Examples: Bots

Typescript

Keeper-bots-v2

Perp Markets

Spot Markets

Python

driftpy examples/

Examples: Atomic Place&Take

Place And Take Orders

Place And Take orders allow you to place an order with an atomic fill in the same transaction. There are a few benefits to using them:

The one caveat to these orders is that there is an extra upfront cost in that you need to know ahead of time which onchain maker order you want to be filled against. Drift has some off-chain infrastructure which provides an API to help you do this.

/**
 * Making a Place And Take with atomic "fill-or-kill" flag to ensure the transaction only succeeds if the entire order is filled
 * 
 **/

import {
    decodeUser,
    PublicKey,
    MarketType,
    BASE_PRECISION,
    PlaceAndTakeOrderSuccessCondition,
    UserAccount,
    isVariant,
    PositionDirection,
    DriftClient,
    getUserStatsAccountPublicKey,
    BN,
    OrderType,
} from '@drift-labs/sdk';

// Method to fetch the current top makers taking advantage of Drift's off-chain infra which can provide these. You may eventually want to keep track of the state of on-chain makers yourself.
async function getTopMakersForPlaceAndTake({
    marketIndex,
    marketType,
    side,
}: {
    marketIndex: number;
    marketType: MarketType;
    side: 'bid' | 'ask';
}): Promise<
    {
        userAccountPubKey: PublicKey;
        userAccount: UserAccount;
    }[]
> {
    const dlobServerUrl = `https://dlob.drift.trade/`;
    const marketTypeStr = isVariant(marketType, 'perp') ? 'perp' : 'spot';
    const limit = 4; // NOTE: This parameter controls the number of top makers that will be returned. It is suggested not to use more than 4, in our current testing the size of the transaction will larger than the current limits if you pass more than 4 makers in.

    const queryParams = `marketIndex=${marketIndex}&marketType=${marketTypeStr}&side=${side}&limit=${limit}&includeAccounts=true`;

    const result = await new Promise<
        {
            userAccountPubKey: string;
            accountBase64: string;
        }[]
    >((res) => {
        fetch(`${dlobServerUrl}/topMakers?${queryParams}`)
            .then(async (response) => {
                if (!response.ok) {
                    // Handle failure
                    return;
                }
                res(await response.json());
            })
            .catch((err) => {
                // Handle failure
            });
    });

    return result.map(
        (value: { userAccountPubKey: string; accountBase64: string }) => {
            return {
                userAccountPubKey: new PublicKey(value.userAccountPubKey),
                userAccount: decodeUser(Buffer.from(value.accountBase64, 'base64')),
            };
        }
    );

    return [];
}

// Fetch and process the top makers result into params for a place and take order
async function getMakerInfoForPlaceAndTake(
    orderDirection: PositionDirection,
    orderMarketIndex: number,
    orderMarketType: MarketType,
    driftClient: DriftClient
) {
    const topMakers = await getTopMakersForPlaceAndTake({
        marketIndex: orderMarketIndex,
        marketType: orderMarketType,
        side: isVariant(orderDirection, 'long') ? 'ask' : 'bid', // If we're going LONG, we want the makers on the ASK side, and vice-versa
    });

    const makerAccountKeys = topMakers.map((maker) => maker.userAccountPubKey);
    const makerStatsAccountKeys = topMakers.map((makerAccount) =>
        getUserStatsAccountPublicKey(
            driftClient.program.programId,
            makerAccount.userAccount.authority
        )
    );
    const makerAccounts = topMakers.map((maker) => maker.userAccount);

    return makerAccountKeys.map((makerUserAccountKey, index) => {
        return {
            maker: makerUserAccountKey,
            makerUserAccount: makerAccounts[index],
            makerStats: makerStatsAccountKeys[index],
        };
    });
}

// Create a PlaceAndTake Instruction. DriftClient also has a `placeAndTakePerpOrder` If you want DriftClient to create+sign+send the transaction if you don't want to manually handle the instruction.
async function makePlaceAndTakePerpOrderIx(
    orderDirection: PositionDirection,
    orderMarketIndex: number,
    orderSizeBase: BN,
    successCondition: PlaceAndTakeOrderSuccessCondition,
    driftClient: DriftClient
) {
    const makerInfos = await getMakerInfoForPlaceAndTake(
        orderDirection,
        orderMarketIndex,
        MarketType.PERP,
        driftClient
    );

    const placeAndTakeIx = await driftClient.getPlaceAndTakePerpOrderIx(
        {
            direction: orderDirection,
            baseAssetAmount: orderSizeBase,
            marketIndex: orderMarketIndex,
            marketType: MarketType.PERP,
            orderType: OrderType.MARKET,
        },
        makerInfos,
        undefined,
        successCondition
    );

    return placeAndTakeIx;
}

// Example: Make a LONG order for 1 SOL-PERP which will "fill-or-kill" if the entire order isn't filled atomically. 
const driftClient = DRIFT_CLIENT; // Assume you have a Drift Client set up and ready to go, see https://drift-labs.github.io/v2-teacher/#client-initialization
const sizeInSol = 1;
await makePlaceAndTakePerpOrderIx(
    PositionDirection.LONG,
    0, // SOL-PERP's market index is 0
    new BN(sizeInSol).mul(BASE_PRECISION),
    PlaceAndTakeOrderSuccessCondition.FullFill, // This ensures the order is completely filled, or the tx fails.
    driftClient,
)

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", 'confirmed');

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", 'confirmed');

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, /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

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.

Data API

The Drift Data API provides public access to various APIs that Drift uses, offering information about markets, contracts, and tokenomics. This API allows developers and users to retrieve data related to the Drift protocol.

mainnet-beta: https://data.api.drift.trade/ devnet: https://master-data.drift.trade/

Rate Limiting

To ensure fair usage and maintain system stability, the Drift Data API implements rate limiting. Users are restricted to a certain number of requests per minute. The exact limit may vary depending on the endpoint and overall system load. If you exceed the rate limit, you'll receive a 429 (Too Many Requests) response. It's recommended to implement exponential backoff in your applications to handle rate limiting gracefully.

Caching

The Drift Data API utilizes caching mechanisms to improve performance and reduce server load. Responses may be cached for a short period, typically ranging from a few seconds to a few minutes, depending on the endpoint and the nature of the data. While this ensures quick response times, it also means that there might be a slight delay in reflecting the most recent updates. Time-sensitive operations should account for this potential delay.

GET /contracts

Returns the contract information for each market. Contract information contains funding rate and open interest (oi).

Example: https://data.api.drift.trade/contracts

GET /fundingRates

Returns the last 30 days of funding rates by marketName or marketIndex.

import requests

def get_funding_rates(market_symbol='SOL-PERP'):
    url = f'https://data.api.drift.trade/fundingRates'
    params = {'marketName': market_symbol}

    response = requests.get(url, params=params)
    return response.json()['fundingRates']

# Example usage, print the funding rates for SOL-PERP
market_symbol = 'SOL-PERP'
rates = get_funding_rates(market_symbol)

print(f"Funding Rates for {market_symbol}:")
for rate in rates:
    funding_rate = float(rate['fundingRate']) / 1e9
    # ... any logic here, for example...
    print(f"Slot: {rate['slot']}, Funding Rate: {funding_rate:.9f}")

interface FundingRate {
  slot: number;
  fundingRate: string;
  // Other fields... (view the response section for the full list)
}

async function getFundingRates(marketSymbol: string = 'SOL-PERP'): Promise<FundingRate[]> {
  const url = `https://data.api.drift.trade/fundingRates?marketName=${marketSymbol}`;

  try {
    const response = await fetch(url);
    if (!response.ok) {
      throw new Error(`HTTP error! status: ${response.status}`);
    }
    const data = await response.json();
    return data.fundingRates;
  } catch (error) {
    console.error('Error fetching funding rates:', error);
    return [];
  }
}

async function main() {
  const marketSymbol = 'SOL-PERP';
  const rates = await getFundingRates(marketSymbol);

  console.log(`Funding Rates for ${marketSymbol}:`);
  rates.forEach(rate => {
    const fundingRate = parseFloat(rate.fundingRate) / 1e9;
    // ... any logic here, for example...
    console.log(`Slot: ${rate.slot}, Funding Rate: ${fundingRate.toFixed(9)}`);
  });
}

main().catch(error => console.error('An error occurred:', error));
Parameter Description Optional Values
marketName or marketIndex The market name or index for the perp market NO

Example: https://data.api.drift.trade/fundingRates?marketName=SOL-PERP

Response

The response is a json object with a fundingRates array. Each funding rate entry contains the following fields:

Field Type Description
txSig string Transaction signature
slot integer Slot number
ts string Timestamp
recordId string Record identifier
marketIndex integer Market index
fundingRate string Funding rate (divide by 1e9 for actual rate)
cumulativeFundingRateLong string Cumulative funding rate for long positions
cumulativeFundingRateShort string Cumulative funding rate for short positions
oraclePriceTwap string Oracle price time-weighted average price
markPriceTwap string Mark price time-weighted average price
fundingRateLong string Funding rate for long positions
fundingRateShort string Funding rate for short positions
periodRevenue string Revenue for the period
baseAssetAmountWithAmm string Base asset amount with AMM
baseAssetAmountWithUnsettledLp string Base asset amount with unsettled LP

GET /DRIFT/

Returns the tokenomics information about the Drift token.

Parameter Description Optional Values
q Metrics related to the drift tokenomics NO circulating-supply,locked-supply,total-supply

Example: https://data.api.drift.trade/DRIFT?q=circulating-supply

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}
swaps user/${accountKey}/swapRecords/${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}
candle-history candle-history/{year}/{marketKey}/{candleResolution}.csv

Variables

variable description example
accountKey user sub account public key (not authority)
authority authority public key
marketSymbol market name. E.g. SOL-PERP for Solana PERP or SOL for Solana SPOT SOL-PERP
year 2023
month 4
day utc time 25
marketKey The key for the market. Format: {marketType}_{marketIndex} perp_0
candleResolution Candle Resolution. See "Available Candle Resolutions" below M

Available Candle Resolutions:

resolution description
1 1 minute
15 15 minute
60 1 hr
240 4 hr
D 1 day
W 1 week

Example: Trades for market

import requests


URL_PREFIX = 'https://drift-historical-data-v2.s3.eu-west-1.amazonaws.com/program/dRiftyHA39MWEi3m9aunc5MzRF1JYuBsbn6VPcn33UH'

market_symbol = 'SOL-PERP'
year = '2024'
month = '01'
day = '01'

# Method 1: using pandas (this is the easiest and fastest way)
import pandas as pd
df = pd.read_csv(f'{URL_PREFIX}/market/{market_symbol}/tradeRecords/{year}/{year}{month}{day}')
print(df)

# Method 2: using csv reader
import csv
from io import StringIO

response = requests.get(f'{URL_PREFIX}/market/{market_symbol}/tradeRecords/{year}/{year}{month}{day}')
response.raise_for_status()

csv_data = StringIO(response.text)
reader = csv.reader(csv_data)
for row in reader:
    print(row)

import { parse } from 'csv-parse/sync';

const URL_PREFIX = 'https://drift-historical-data-v2.s3.eu-west-1.amazonaws.com/program/dRiftyHA39MWEi3m9aunc5MzRF1JYuBsbn6VPcn33UH';

async function fetchAndParseCSV(marketSymbol: string, year: string, month: string, day: string): Promise<void> {
  const url = `${URL_PREFIX}/market/${marketSymbol}/tradeRecords/${year}/${year}${month}${day}`;

  try {
    const response = await fetch(url);
    if (!response.ok) {
      throw new Error(`HTTP error! status: ${response.status}`);
    }

    const csvText = await response.text();
    const records = parse(csvText, {
      skip_empty_lines: true
    });

    records.forEach((row: string[]) => {
      console.log(row);
    });
  } catch (error) {
    console.error('Error fetching or parsing CSV:', error);
  }
}

async function main() {
  const marketSymbol = 'SOL-PERP';
  const year = '2024';
  const month = '01';
  const day = '01';

  await fetchAndParseCSV(marketSymbol, year, month, day);
}

main().catch(error => console.error('An error occurred:', error));

Get historical trades on SOL-PERP for a given date (ex. 2024-01-01): https://drift-historical-data-v2.s3.eu-west-1.amazonaws.com/program/dRiftyHA39MWEi3m9aunc5MzRF1JYuBsbn6VPcn33UH/market/SOL-PERP/tradeRecords/2024/20240101

Example: Trades for date range

import requests
import csv
from io import StringIO
from datetime import date, timedelta
import pandas as pd

URL_PREFIX = 'https://drift-historical-data-v2.s3.eu-west-1.amazonaws.com/program/dRiftyHA39MWEi3m9aunc5MzRF1JYuBsbn6VPcn33UH'

# Method 1: using pandas
def get_trades_for_range_pandas(account_key, start_date, end_date):
    all_trades = []
    current_date = start_date
    while current_date <= end_date:
        year = current_date.year
        month = current_date.month
        day = current_date.day
        url = f"{URL_PREFIX}/user/{account_key}/tradeRecords/{year}/{year}{month:02}{day:02}"

        try:
            df = pd.read_csv(url)
            all_trades.append(df)
        except requests.exceptions.RequestException as e:
            print(f"Error fetching data for {current_date}: {e}")
        except pd.errors.EmptyDataError:
            print(f"No data available for {current_date}")

        current_date += timedelta(days=1)

    if all_trades:
        return pd.concat(all_trades, ignore_index=True)
    else:
        return pd.DataFrame()

# Method 2: using csv reader
def get_trades_for_range_csv(account_key, start_date, end_date):
    all_trades = []
    current_date = start_date
    while current_date <= end_date:
        year = current_date.year
        month = current_date.month
        day = current_date.day
        url = f"{URL_PREFIX}/user/{account_key}/tradeRecords/{year}/{year}{month:02}{day:02}"
        response = requests.get(url)
        response.raise_for_status()

        csv_data = StringIO(response.text)
        reader = csv.reader(csv_data)
        for row in reader:
            all_trades.append(row)

        current_date += timedelta(days=1)

    return all_trades


# Example usage
account_key = "<Some Account Key>"
start_date = date(2024, 1, 24)
end_date = date(2024, 1, 26)

trades = get_trades_for_range(account_key, start_date, end_date)
import { parse } from 'csv-parse/sync';

const URL_PREFIX = 'https://drift-historical-data-v2.s3.eu-west-1.amazonaws.com/program/dRiftyHA39MWEi3m9aunc5MzRF1JYuBsbn6VPcn33UH';

interface Trade {
  // Define the structure of a trade record here
  // For example:
  // date: string;
  // amount: number;
  // price: number;
  [key: string]: any;
}

async function getTradesForRange(accountKey: string, startDate: Date, endDate: Date): Promise<Trade[]> {
  const allTrades: Trade[] = [];
  let currentDate = new Date(startDate);

  while (currentDate <= endDate) {
    const year = currentDate.getFullYear();
    const month = (currentDate.getMonth() + 1).toString().padStart(2, '0');
    const day = currentDate.getDate().toString().padStart(2, '0');

    const url = `${URL_PREFIX}/user/${accountKey}/tradeRecords/${year}/${year}${month}${day}`;

    try {
      const response = await fetch(url);
      if (!response.ok) {
        throw new Error(`HTTP error! status: ${response.status}`);
      }

      const csvText = await response.text();
      const records = parse(csvText, {
        skip_empty_lines: true,
        columns: true
      });

      allTrades.push(...records);
    } catch (error) {
      console.error(`Error fetching or parsing CSV for ${year}-${month}-${day}:`, error);
    }

    currentDate.setDate(currentDate.getDate() + 1);
  }

  return allTrades;
}

async function main() {
  const accountKey = "<Some Account Key>";
  const startDate = new Date(2024, 0, 24); // Note: month is 0-indexed in JavaScript
  const endDate = new Date(2024, 0, 26);

  try {
    const trades = await getTradesForRange(accountKey, startDate, endDate);
    console.log(`Total trades fetched: ${trades.length}`);
    console.log('First few trades:', trades.slice(0, 5));
  } catch (error) {
    console.error('An error occurred:', error);
  }
}

main();

Note: To speed this up, you could download the data in parallel (Promise.all or asyncio).

We can write a script to download all the data, one by one, for a given account in a given date range.

Records Columns

Below are definitions of the columns in each record type.

trades

variable description example
accountKey user sub account public key (not authority)

funding-rates

note: 'rate' is in quote per base, to allow for async settlement

variable description example
fundingRate the quote asset amount (precision=1e6) per base asset amount (precision=1e9)

to convert to the rates seen on the ui, use the following formula: (funding_rate / BASE_PRECISION) / (oracle_twap / QUOTE_PRECISION) * 100

import requests

outcsv = requests.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
6248 InvalidSwap InvalidSwap
6249 SwapLimitPriceBreached SwapLimitPriceBreached
6250 SpotMarketReduceOnly SpotMarketReduceOnly
6251 FundingWasNotUpdated FundingWasNotUpdated
6252 ImpossibleFill ImpossibleFill
6253 CantUpdatePerpBidAskTwap CantUpdatePerpBidAskTwap
6254 UserReduceOnly UserReduceOnly
6255 InvalidMarginCalculation InvalidMarginCalculation
6256 CantPayUserInitFee CantPayUserInitFee
6257 CantReclaimRent CantReclaimRent
6258 InsuranceFundOperationPaused InsuranceFundOperationPaused
6259 NoUnsettledPnl NoUnsettledPnl
6260 PnlPoolCantSettleUser PnlPoolCantSettleUser
6261 OracleNonPositive OracleInvalid
6262 OracleTooVolatile OracleTooVolatile
6263 OracleTooUncertain OracleTooUncertain
6264 OracleStaleForMargin OracleStaleForMargin
6265 OracleInsufficientDataPoints OracleInsufficientDataPoints
6266 OracleStaleForAMM OracleStaleForAMM
6267 UnableToParsePullOracleMessage Unable to parse pull oracle message
6268 MaxBorrows Can not borow more than max borrows
6269 OracleUpdatesNotMonotonic Updates must be monotonically increasing
6270 OraclePriceFeedMessageMismatch Trying to update price feed with the wrong feed id
6271 OracleUnsupportedMessageType The message in the update must be a PriceFeedMessage
6272 OracleDeserializeMessageFailed Could not deserialize the message in the update
6273 OracleWrongGuardianSetOwner Wrong guardian set owner in update price atomic
6274 OracleWrongWriteAuthority Oracle post update atomic price feed account must be drift program
6275 OracleWrongVaaOwner Oracle vaa owner must be wormhole program
6276 OracleTooManyPriceAccountUpdates Multi updates must have 2 or fewer accounts passed in remaining accounts
6277 OracleMismatchedVaaAndPriceUpdates Don't have the same remaining accounts number and merkle price updates left
6278 OracleBadRemainingAccountPublicKey Remaining account passed is not a valid pda
6279 FailedOpenbookV2CPI FailedOpenbookV2CPI
6280 InvalidOpenbookV2Program InvalidOpenbookV2Program
6281 InvalidOpenbookV2Market InvalidOpenbookV2Market
6282 NonZeroTransferFee Non zero transfer fee
6283 LiquidationOrderFailedToFill Liquidation order failed to fill
6284 InvalidPredictionMarketOrder Invalid prediction market order
6285 InvalidVerificationIxIndex Ed25519 Ix must be before place and make swift order ix
6286 SigVerificationFailed Swift message verificaiton failed
6287 MismatchedSwiftOrderParamsMarketIndex Market index mismatched b/w taker and maker swift order params
6288 InvalidSwiftOrderParam Swift only available for market/oracle perp orders
6289 PlaceAndTakeOrderSuccessConditionFailed Place and take order success condition failed
6290 InvalidHighLeverageModeConfig Invalid High Leverage Mode Config
6291 InvalidRFQUserAccount Invalid RFQ User Account
6292 RFQUserAccountWrongMutability RFQUserAccount should be mutable
6293 RFQUserAccountFull RFQUserAccount has too many active RFQs
6294 RFQOrderNotFilled RFQ order not filled as expected
6295 InvalidRFQOrder RFQ orders must be jit makers
6296 InvalidRFQMatch RFQ matches must be valid

Market Indexes/Names

All spot and perp markets contain a market index and name field on chain. You can also find the perp and spot market indexes and names in the typescript sdk constants.

Prediction Markets

Prediction Markets are Perp Markets with the contract_type set as Prediction.

Prediction Markets function the same as Perp Markets with the following additional constraints: