home/skills/defi/drift-protocol

Drift Protocol

SendAI Solana Toolkit / ElizaOS

Perpetuals, spot trading, lending, borrowing, and vaults on Solana. 15+ SAK code examples with full API.

๐ŸŸก warn (80)skillDeFiperps-tradinggithubSource โ†’skill.md โ†’
solanaperpslendingvaultstrading
# Drift Protocol SDK Development Guide

A comprehensive guide for building Solana applications with the Drift Protocol SDK - the leading perpetual futures and spot trading protocol on Solana.

## Overview

Drift Protocol is a decentralized exchange on Solana offering:
- **Perpetual Futures**: Up to 20x leverage on crypto assets
- **Spot Trading**: Borrow/lend and margin trading
- **Cross-Collateral**: Use multiple assets as collateral
- **Vaults**: Delegated trading pools
- **Jupiter Integration**: Direct spot swaps

## Quick Start

### Installation

```bash
npm install @drift-labs/sdk @solana/web3.js @coral-xyz/anchor
```

For Python:
```bash
pip install driftpy
```

### Basic Setup (TypeScript)

```typescript
import { Connection, Keypair } from '@solana/web3.js';
import { Wallet } from '@coral-xyz/anchor';
import {
  DriftClient,
  initialize,
  DriftEnv,
  BulkAccountLoader
} from '@drift-labs/sdk';

// 1. Setup connection and wallet
const connection = new Connection('https://api.mainnet-beta.solana.com');
const keypair = Keypair.fromSecretKey(/* your secret key */);
const wallet = new Wallet(keypair);

// 2. Initialize SDK
const sdkConfig = initialize({ env: 'mainnet-beta' as DriftEnv });

// 3. Create DriftClient
const driftClient = new DriftClient({
  connection,
  wallet,
  env: 'mainnet-beta',
  accountSubscription: {
    type: 'polling',
    accountLoader: new BulkAccountLoader(connection, 'confirmed', 1000),
  },
});

// 4. Subscribe to updates
await driftClient.subscribe();

// 5. Check if user account exists
const user = driftClient.getUser();
const userExists = await user.exists();

if (!userExists) {
  // Initialize user account (costs ~0.035 SOL rent)
  await driftClient.initializeUserAccount();
}
```

### Basic Setup (Python)

```python
import asyncio
from solana.rpc.async_api import AsyncClient
from solders.keypair import Keypair
from driftpy.drift_client import DriftClient
from driftpy.account_subscription_config import AccountSubscriptionConfig
from anchorpy import Wallet

async def main():
    connection = AsyncClient("https://api.mainnet-beta.solana.com")
    keypair = Keypair.from_bytes(secret_key_bytes)
    wallet = Wallet(keypair)

    drift_client = DriftClient(
        connection,
        wallet,
        "mainnet",
        account_subscription=AccountSubscriptionConfig("polling"),
    )

    await drift_client.subscribe()

    user = drift_client.get_user()
    if not await user.exists():
        await drift_client.initialize_user_account()

asyncio.run(main())
```

## Core Concepts

### 1. Precision and BN (BigNumber)

Drift uses BN.js for all numerical values due to token precision exceeding JavaScript float limits. All amounts are integers with designated precision levels.

**Key Precision Constants:**

| Constant | Value | Use Case |
|----------|-------|----------|
| `QUOTE_PRECISION` | 10^6 | USDC amounts |
| `BASE_PRECISION` | 10^9 | Perp base asset amounts |
| `PRICE_PRECISION` | 10^6 | Prices |
| `SPOT_MARKET_BALANCE_PRECISION` | 10^9 | Spot token balances |
| `FUNDING_RATE_PRECISION` | 10^9 | Funding rates |
| `MARGIN_PRECISION` | 10,000 | Margin ratios |
| `PEG_PRECISION` | 10^6 | AMM peg |
| `AMM_RESERVE_PRECISION` | 10^9 | AMM reserves |

**Conversion Helper:**
```typescript
import { convertToNumber } from '@drift-labs/sdk';

// BN division returns floor - use helper for precise division
const result = convertToNumber(new BN(10500), new BN(1000)); // = 10.5

// Converting amounts
const perpAmount = driftClient.convertToPerpPrecision(100);  // 100 base units
const spotAmount = driftClient.convertToSpotPrecision(0, 100); // 100 USDC
const price = driftClient.convertToPricePrecision(21.23);    // $21.23
```

### 2. Market Types

**Perpetual Markets** (`MarketType.PERP`):
- Derivatives with no expiry
- Positions tracked via `baseAssetAmount`
- Positive = Long, Negative = Short

**Spot Markets** (`MarketType.SPOT`):
- Real token deposits/borrows
- Positions tracked via `scaledBalance`
- `SpotBalanceType.DEPOSIT` or `SpotBalanceType.BORROW`

**Common Market Indexes:**
- `0` - USDC (quote asset)
- `1` - SOL
- Market indexes vary - query `getPerpMarketAccounts()` / `getSpotMarketAccounts()`

### 3. Order Types

```typescript
import { OrderType, PositionDirection, OrderTriggerCondition } from '@drift-labs/sdk';

// Available order types
OrderType.MARKET          // Immediate execution
OrderType.LIMIT           // Price-specific orders
OrderType.TRIGGER_MARKET  // Stop-loss/take-profit market
OrderType.TRIGGER_LIMIT   // Stop-loss/take-profit limit
OrderType.ORACLE          // Oracle-based pricing

// Position directions
PositionDirection.LONG    // Buy/bid
PositionDirection.SHORT   // Sell/ask

// Trigger conditions (for stop orders)
OrderTriggerCondition.ABOVE  // Trigger when price > threshold
OrderTriggerCondition.BELOW  // Trigger when price < threshold
```

### 4. Post-Only Parameters

```typescript
import { PostOnlyParams } from '@drift-labs/sdk';

PostOnlyParams.NONE           // No enforcement (can be taker)
PostOnlyParams.MUST_POST_ONLY // Fail if order would cross spread
PostOnlyParams.TRY_POST_ONLY  // Skip order if would cross spread
PostOnlyParams.SLIDE          // Adjust price to be post-only
```

## Trading Operations

### Placing Perpetual Orders

```typescript
// Market Order
await driftClient.placePerpOrder({
  orderType: OrderType.MARKET,
  marketIndex: 0,  // SOL-PERP
  direction: PositionDirection.LONG,
  baseAssetAmount: driftClient.convertToPerpPrecision(1), // 1 SOL
});

// Limit Order
await driftClient.placePerpOrder({
  orderType: OrderType.LIMIT,
  marketIndex: 0,
  direction: PositionDirection.LONG,
  baseAssetAmount: driftClient.convertToPerpPrecision(1),
  price: driftClient.convertToPricePrecision(100), // $100
  postOnly: PostOnlyParams.MUST_POST_ONLY,
});

// Stop-Loss Order
await driftClient.placePerpOrder({
  orderType: OrderType.TRIGGER_MARKET,
  marketIndex: 0,
  direction: PositionDirection.SHORT, // Close long
  baseAssetAmount: driftClient.convertToPerpPrecision(1),
  triggerPrice: driftClient.convertToPricePrecision(90),
  triggerCondition: OrderTriggerCondition.BELOW,
  reduceOnly: true,
});

// Take-Profit Order
await driftClient.placePerpOrder({
  orderType: OrderType.TRIGGER_LIMIT,
  marketIndex: 0,
  direction: PositionDirection.SHORT, // Close long
  baseAssetAmount: driftClient.convertToPerpPrecision(1),
  price: driftClient.convertToPricePrecision(120),
  triggerPrice: driftClient.convertToPricePrecision(120),
  triggerCondition: OrderTriggerCondition.ABOVE,
  reduceOnly: true,
});

// Oracle Order (price relative to oracle)
await driftClient.placePerpOrder({
  orderType: OrderType.ORACLE,
  marketIndex: 0,
  direction: PositionDirection.LONG,
  baseAssetAmount: driftClient.convertToPerpPrecision(1),
  oraclePriceOffset: -100000, // $0.10 below oracle (PRICE_PRECISION)
  auctionDuration: 10,
  auctionStartPrice: new BN(-200000), // Start $0.20 below
  auctionEndPrice: new BN(-50000),    // End $0.05 below
});
```

### Placing Spot Orders

```typescript
// Spot Market Order
await driftClient.placeSpotOrder({
  orderType: OrderType.MARKET,
  marketIndex: 1, // SOL
  direction: PositionDirection.LONG, // Buy
  baseAssetAmount: driftClient.convertToSpotPrecision(1, 1), // 1 SOL
});

// Spot Limit Order
await driftClient.placeSpotOrder({
  orderType: OrderType.LIMIT,
  marketIndex: 1,
  direction: PositionDirection.LONG,
  baseAssetAmount: driftClient.convertToSpotPrecision(1, 1),
  price: driftClient.convertToPricePrecision(100),
});
```

### Multiple Orders

```typescript
// Place multiple orders atomically
await driftClient.placeOrders([
  {
    orderType: OrderType.LIMIT,
    marketType: MarketType.PERP,
    marketIndex: 0,
    direction: PositionDirection.LONG,
    baseAssetAmount: driftClient.convertToPerpPrecision(1),
    price: driftClient.convertToPricePrecision(99),
  },
  {
    orderType: OrderType.LIMIT,
    marketType: MarketType.PERP,
    marketIndex: 0,
    direction: PositionDirection.SHORT,
    baseAssetAmount: driftClient.convertToPerpPrecision(1),
    price: driftClient.convertToPricePrecision(101),
  },
]);
```

### Order Management

```typescript
// Cancel by order ID
await driftClient.cancelOrder(orderId);

// Cancel by user order ID
await driftClient.cancelOrderByUserOrderId(userOrderId);

// Cancel all orders for a market
await driftClient.cancelOrders(MarketType.PERP, 0); // All SOL-PERP orders

// Cancel all orders
await driftClient.cancelOrders();

// Modify existing order
await driftClient.modifyOrder(orderId, {
  price: driftClient.convertToPricePrecision(102),
});

// Cancel and place atomically
await driftClient.cancelAndPlaceOrders({
  cancelOrderParams: { orderId: existingOrderId },
  placeOrderParams: [{
    orderType: OrderType.LIMIT,
    marketType: MarketType.PERP,
    marketIndex: 0,
    direction: PositionDirection.LONG,
    baseAssetAmount: driftClient.convertToPerpPrecision(1),
    price: driftClient.convertToPricePrecision(100),
  }],
});
```

### Get Orders

```typescript
// Get specific order
const order = driftClient.getOrder(orderId);

// Get order by user ID
const order = driftClient.getOrderByUserId(userOrderId);

// Get all open orders
const user = driftClient.getUser();
const openOrders = user.getOpenOrders();
```

## Deposits and Withdrawals

### Deposits

```typescript
// Get associated token account
const associatedTokenAccount = await driftClient.getAssociatedTokenAccount(0); // USDC

// Deposit USDC
const amount = driftClient.convertToSpotPrecision(0, 100); // 100 USDC
await driftClient.deposit(amount, 0, associatedTokenAccount);

// Deposit SOL
const solAccount = await driftClient.getAssociatedTokenAccount(1);
const solAmount = driftClient.convertToSpotPrecision(1, 1); // 1 SOL
await driftClient.deposit(solAmount, 1, solAccount);

// Initialize user and deposit in one transaction
const [txSig, userPubkey] = await driftClient.initializeUserAccountAndDepositCollateral(
  driftClient.convertToSpotPrecision(0, 100),
  await driftClient.getAssociatedTokenAccount(0),
  0, // market index
  0, // sub account ID
  'MyAccount', // name
);
```

### Withdrawals

```typescript
const associatedTokenAccount = await driftClient.getAssociatedTokenAccount(0);
const amount = driftClient.convertToSpotPrecision(0, 50); // 50 USDC

// Withdraw (may create borrow if insufficient deposits)
await driftClient.withdraw(amount, 0, associatedTokenAccount);

// Withdraw with reduce-only (prevents creating borrow)
await driftClient.withdraw(amount, 0, associatedTokenAccount, undefined, true);
```

### Transfers Between Sub-Accounts

```typescript
// Transfer deposits
await driftClient.transferDeposit(
  driftClient.convertToSpotPrecision(0, 100),
  0, // market index
  0, // from sub-account
  1, // to sub-account
);

// Transfer perp positions
await driftClient.transferPerpPosition({
  fromSubAccountId: 0,
  toSubAccountId: 1,
  marketIndex: 0,
  amount: driftClient.convertToPerpPrecision(1),
});
```

## Position Management

### Reading Positions

```typescript
const user = driftClient.getUser();

// Perpetual Position
const perpPosition = user.getPerpPosition(0); // SOL-PERP
if (perpPosition) {
  const baseAmount = perpPosition.baseAssetAmount;
  const isLong = baseAmount.gt(new BN(0));
  const isShort = baseAmount.lt(new BN(0));
  console.log('Position size:', convertToNumber(baseAmount, BASE_PRECISION));
}

// All active perp positions
const activePerpPositions = user.getActivePerpPositions();

// Spot Position
const spotPosition = user.getSpotPosition(0); // USDC
const tokenAmount = user.getTokenAmount(0);
const isDeposit = tokenAmount.gt(new BN(0));
const isBorrow = tokenAmount.lt(new BN(0));

// All active spot positions
const activeSpotPositions = user.getActiveSpotPositions();
```

### Collateral and Margin

```typescript
const user = driftClient.getUser();

// Total collateral value
const totalCollateral = user.getTotalCollateral();

// Free collateral (available for new positions)
const freeCollateral = user.getFreeCollateral();

// Margin requirements
const initialMargin = user.getInitialMarginRequirement();
const maintenanceMargin = user.getMaintenanceMarginRequirement();

// Current leverage
const leverage = user.getLeverage();

// Account health (0-100, liquidation at 0)
const health = user.getHealth();

// Max leverage for a market
const maxLeverage = user.getMaxLeverageForPerp(0); // SOL-PERP

// Buying power
const buyingPower = user.getPerpBuyingPower(0);
```

### PnL Calculations

```typescript
const user = driftClient.getUser();

// Unrealized PnL (all positions)
const unrealizedPnl = user.getUnrealizedPNL();

// Unrealized PnL with funding
const unrealizedPnlWithFunding = user.getUnrealizedPNL(true);

// PnL for specific market
const marketPnl = user.getUnrealizedPNL(false, 0);

// Unrealized funding PnL
const fundingPnl = user.getUnrealizedFundingPNL();

// Settle PnL
await driftClient.settlePNL(
  user.getUserAccountPublicKey(),
  user.getUserAccount(),
  0 // market index
);
```

### Liquidation Price

```typescript
const user = driftClient.getUser();

// Get liquidation price for perp position
const liqPrice = user.liquidationPrice(0); // SOL-PERP

// Check if can be liquidated
const canBeLiquidated = user.canBeLiquidated();
```

## Market Data

### Market Accounts

```typescript
// Perpetual market
const perpMarket = driftClient.getPerpMarketAccount(0);
console.log('Market index:', perpMarket.marketIndex);
console.log('AMM base reserves:', perpMarket.amm.baseAssetReserve.toString());

// All perp markets
const allPerpMarkets = driftClient.getPerpMarketAccounts();

// Spot market
const spotMarket = driftClient.getSpotMarketAccount(0);
console.log('Decimals:', spotMarket.decimals);

// All spot markets
const allSpotMarkets = driftClient.getSpotMarketAccounts();
```

### Oracle Data

```typescript
// Get oracle price for perp market
const oracleData = driftClient.getOracleDataForPerpMarket(0);
const price = oracleData.price; // BN in PRICE_PRECISION

console.log('Oracle price:', convertToNumber(price, PRICE_PRECISION));

// Get oracle for spot market
const spotOracleData = driftClient.getOracleDataForSpotMarket(1);
```

### Calculate Prices from AMM

```typescript
import { calculateBidAskPrice } from '@drift-labs/sdk';

const perpMarket = driftClient.getPerpMarketAccount(0);
const oracleData = driftClient.getOracleDataForPerpMarket(0);

const [bidPrice, askPrice] = calculateBidAskPrice(
  perpMarket.amm,
  oracleData
);
```

## Events

### Event Subscriber

```typescript
import { EventSubscriber } from '@drift-labs/sdk';

const eventSubscriber = new EventSubscriber(connection, driftClient.program, {
  eventTypes: [
    'DepositRecord',
    'FundingPaymentRecord',
    'LiquidationRecord',
    'OrderRecord',
    'OrderActionRecord',
    'FundingRateRecord',
    'SettlePnlRecord',
    'LPRecord',
    'InsuranceFundRecord',
    'SpotInterestRecord',
  ],
  maxTx: 4096,
  maxEventsPerType: 4096,
  commitment: 'confirmed',
  logProviderConfig: { type: 'websocket' },
});

await eventSubscriber.subscribe();

// Listen for events
eventSubscriber.eventEmitter.on('newEvent', (event) => {
  console.log('Event type:', event.eventType);
  console.log('Event data:', event);
});

// Get events by type
const depositEvents = eventSubscriber.getEventsReceived()
  .filter(e => e.eventType === 'DepositRecord');

// Unsubscribe
await eventSubscriber.unsubscribe();
```

## Jupiter Swaps

```typescript
import { JupiterClient } from '@drift-labs/sdk';

// Initialize Jupiter client
const jupiterClient = new JupiterClient({ connection });

// Get quote preview
const quote = await jupiterClient.getQuote({
  inputMint: /* USDC mint */,
  outputMint: /* SOL mint */,
  amount: driftClient.convertToSpotPrecision(0, 100),
  slippageBps: 50,
});

// Execute swap through Drift
const txSig = await driftClient.swap({
  jupiterClient,
  inMarketIndex: 0,   // USDC
  outMarketIndex: 1,  // SOL
  amount: driftClient.convertToSpotPrecision(0, 100),
  slippageBps: 50,
  onlyDirectRoutes: false,
});
```

## Sub-Accounts

```typescript
// Create new sub-account
const [txSig, userPubkey] = await driftClient.initializeUserAccount(
  1, // sub-account ID
  'SubAccount1' // name
);

// Switch active sub-account
await driftClient.switchActiveUser(1);

// Get user for specific sub-account
const user = driftClient.getUser(1);

// Delete sub-account (must have no positions)
await driftClient.deleteUser(1);

// Update delegate (allow another key to trade)
await driftClient.updateUserDelegate(delegatePublicKey, 0);
```

## Advanced: Swift Protocol (Orderless Trades)

Swift allows off-chain order signing without Solana transactions:

```typescript
// Sign order message
const orderMessage = {
  marketIndex: 0,
  marketType: MarketType.PERP,
  direction: PositionDirection.LONG,
  baseAssetAmount: driftClient.convertToPerpPrecision(1),
  price: driftClient.convertToPricePrecision(100),
};

const signature = driftClient.signSignedMsgOrderParamsMessage(orderMessage);

// Submit to Swift server
await axios.post('https://swift.drift.trade/orders', {
  market_index: 0,
  message: signature.message,
  signature: signature.signature,
  taker_authority: wallet.publicKey.toString(),
});
```

## Advanced: Builder Codes (DBC)

For platforms routing trades through Drift to earn fees:

```typescript
// Initialize as builder
await driftClient.initializeRevenueShare(builderAuthority);

// Initialize user's escrow
await driftClient.initializeRevenueShareEscrow(userAuthority, numOrders);

// Approve builder
await driftClient.changeApprovedBuilder(
  builderAuthority,
  maxFeeTenthBps, // max fee in tenth basis points
  true // add (false to remove)
);
```

## Error Handling

```typescript
try {
  await driftClient.placePerpOrder(orderParams);
} catch (error) {
  if (error.message.includes('InsufficientCollateral')) {
    console.error('Not enough collateral for this trade');
  } else if (error.message.includes('MaxLeverageExceeded')) {
    console.error('Would exceed maximum leverage');
  } else if (error.message.includes('OrderWouldCrossMaker')) {
    console.error('Post-only order would cross spread');
  } else {
    throw error;
  }
}
```

## Resources

- [Drift Protocol Docs](https://docs.drift.trade)
- [SDK Documentation](https://drift-labs.github.io/v2-teacher/)
- [TypeScript SDK](https://github.com/drift-labs/protocol-v2/tree/master/sdk)
- [Python SDK (DriftPy)](https://github.com/drift-labs/driftpy)
- [Keeper Bots Examples](https://github.com/drift-labs/keeper-bots-v2)

## Skill Structure

```
drift-protocol/
โ”œโ”€โ”€ SKILL.md                          # This file
โ”œโ”€โ”€ resources/
โ”‚   โ”œโ”€โ”€ precision-constants.md        # All precision constants
โ”‚   โ”œโ”€โ”€ types-reference.md            # TypeScript types and enums
โ”‚   โ”œโ”€โ”€ drift-client-api.md           # DriftClient method reference
โ”‚   โ””โ”€โ”€ user-api.md                   # User class method reference
โ”œโ”€โ”€ examples/
โ”‚   โ”œโ”€โ”€ basic-setup/                  # Client initialization
โ”‚   โ”œโ”€โ”€ orders/                       # Order placement examples
โ”‚   โ”œโ”€โ”€ deposits-withdrawals/         # Collateral management
โ”‚   โ”œโ”€โ”€ positions/                    # Position queries
โ”‚   โ”œโ”€โ”€ jupiter-swaps/                # Swap integration
โ”‚   โ”œโ”€โ”€ vaults/                       # Vault management
โ”‚   โ””โ”€โ”€ events/                       # Event subscription
โ”œโ”€โ”€ docs/
โ”‚   โ”œโ”€โ”€ vaults.md                     # Vault documentation
โ”‚   โ”œโ”€โ”€ market-making.md              # Market making guide
โ”‚   โ””โ”€โ”€ troubleshooting.md            # Common issues
โ””โ”€โ”€ templates/
    โ””โ”€โ”€ trading-bot-template.ts       # Copy-paste starter
```
๐Ÿงช Found this useful?
The $SKILL experiment is building the agent skill distribution layer. Every skill you discover through this directory is part of the experiment.