Sending Actions
Once you have UI state, you can send actions using the Drift SDK. There are two approaches: high-level methods that handle transaction construction for you, and instruction builders that give you full control.
When to use which
| Approach | Use when |
|---|---|
High-level methods (placePerpOrder, cancelOrder) | Simple flows , one action per transaction, default compute budget |
Instruction builders (getPlacePerpOrderIx, getCancelOrderIx) | You need multiple instructions in one transaction, custom compute budget, or atomic cancel-and-replace |
Most ecosystem apps start with high-level methods and move to instruction builders when they need atomic multi-step transactions or fine-grained priority fee control.
Step 1: Initialize a Drift client
Follow SDK setup:
Step 2: Place and cancel orders
Use the SDK’s order helpers for common flows:
Step 3: Manage collateral and accounts
Deposits, withdrawals, and account lifecycle are covered in:
Step 4: Monitor risk and health
UI parity requires the same risk/health calculations:
Step 5: Build custom transactions
For advanced use cases, you can build transactions with multiple instructions. This is essential when you need atomicity , either all instructions succeed or none do.
import { ComputeBudgetProgram } from "@solana/web3.js";
import { getMarketOrderParams, PositionDirection } from "@drift-labs/sdk";
// Get instruction for placing order (doesn't send tx yet)
const orderParams = getMarketOrderParams({
marketIndex: 0,
direction: PositionDirection.LONG,
baseAssetAmount: driftClient.convertToPerpPrecision(1),
});
const placeOrderIx = await driftClient.getPlacePerpOrderIx(orderParams);
// Build transaction with compute budget + order instruction
const tx = await driftClient.txSender.getVersionedTransaction(
[
ComputeBudgetProgram.setComputeUnitLimit({ units: 400000 }),
ComputeBudgetProgram.setComputeUnitPrice({ microLamports: 50000 }),
placeOrderIx,
],
[], // lookup tables (if any)
driftClient.wallet.publicKey
);
// Sign and send
const signature = await driftClient.txSender.sendVersionedTransaction(
tx,
[],
driftClient.opts
);
console.log("Transaction:", signature);Method DriftClient.getPlacePerpOrderIxReference ↗
Method DriftClient.getPlacePerpOrderIxReference ↗| Parameter | Type | Required |
|---|---|---|
orderParams | OptionalOrderParams | Yes |
subAccountId | number | No |
depositToTradeArgs | { isMakingNewAccount: boolean; depositMarketIndex: number; } | No |
| Returns |
|---|
Promise<TransactionInstruction> |
Step 6: Atomic cancel-and-place (order replacement)
A common pattern for trading UIs is atomically canceling existing orders and placing new ones in a single transaction. This prevents the window where you have no orders resting while updating prices.
import { ComputeBudgetProgram } from "@solana/web3.js";
import { getLimitOrderParams, PositionDirection } from "@drift-labs/sdk";
// Cancel all existing perp orders for this market, then place new ones
const cancelIx = await driftClient.getCancelOrdersIx(
null, // marketType (null = all)
null, // marketIndex (null = all)
null // direction (null = both)
);
const bidParams = getLimitOrderParams({
marketIndex: 0,
direction: PositionDirection.LONG,
baseAssetAmount: driftClient.convertToPerpPrecision(1),
price: driftClient.convertToPricePrecision(120),
postOnly: true,
});
const askParams = getLimitOrderParams({
marketIndex: 0,
direction: PositionDirection.SHORT,
baseAssetAmount: driftClient.convertToPerpPrecision(1),
price: driftClient.convertToPricePrecision(125),
postOnly: true,
});
const bidIx = await driftClient.getPlacePerpOrderIx(bidParams);
const askIx = await driftClient.getPlacePerpOrderIx(askParams);
// All three instructions in one atomic transaction
const tx = await driftClient.txSender.getVersionedTransaction(
[
ComputeBudgetProgram.setComputeUnitLimit({ units: 600000 }),
ComputeBudgetProgram.setComputeUnitPrice({ microLamports: 50000 }),
cancelIx,
bidIx,
askIx,
],
[],
driftClient.wallet.publicKey
);
const signature = await driftClient.txSender.sendVersionedTransaction(
tx,
[],
driftClient.opts
);
console.log("Cancel + place transaction:", signature);Method DriftClient.cancelAndPlaceOrdersReference ↗
Method DriftClient.cancelAndPlaceOrdersReference ↗| Parameter | Type | Required |
|---|---|---|
cancelOrderParams | { marketType?: MarketType; marketIndex?: number; direction?: PositionDirection; } | Yes |
placeOrderParams | OrderParams[] | Yes |
txParams | TxParams | No |
subAccountId | number | No |
| Returns |
|---|
Promise<string> |
Step 7: Keep transaction performance healthy
If you’re batching or sending frequent transactions, consider:
- Priority fees , Set
ComputeUnitPriceto land transactions faster during congestion - Compute budget , Use
setComputeUnitLimitto allocate enough CU for multi-instruction transactions - Address Lookup Tables (ALTs) , Reduce transaction size when referencing many accounts. See SDK documentation for details.
- Retry logic , Transactions can be dropped; implement retries with backoff for production systems