Overview
Drift Protocol v2 uses BN.js (BigNum) to represent numerical values with high precision. This is essential because Solana tokens use levels of precision that exceed JavaScript’s standard floating-point number capabilities.
All numbers in BN are represented as integers. The precision value indicates how to convert them back to decimal numbers.
How Precision Works
A BigNum value combined with a precision represents a decimal number:
// Example:
// BigNum: 10,500,000 with precision 10^6 = 10.5
// Because: 10,500,000 / 10^6 = 10.5
Standard Precision Constants
Drift uses consistent precision values across the protocol:
| Constant | Value | Exponent | Usage |
|---|
QUOTE_PRECISION | 1,000,000 | 10^6 | Quote asset amounts (USDC) |
PRICE_PRECISION | 1,000,000 | 10^6 | Asset prices |
PEG_PRECISION | 1,000,000 | 10^6 | AMM peg multiplier |
BASE_PRECISION | 1,000,000,000 | 10^9 | Base asset amounts |
AMM_RESERVE_PRECISION | 1,000,000,000 | 10^9 | AMM reserve values |
FUNDING_RATE_BUFFER_PRECISION | 1,000 | 10^3 | Funding rate buffer |
FUNDING_RATE_PRECISION | 1,000,000,000 | 10^9 | Full funding rate precision |
PERCENTAGE_PRECISION | 1,000,000 | 10^6 | Percentage values |
MARGIN_PRECISION | 10,000 | 10^4 | Margin ratios |
BID_ASK_SPREAD_PRECISION | 1,000,000 | 10^6 | Bid-ask spreads |
Precision Relationships
// From numericConstants.ts
const AMM_TO_QUOTE_PRECISION_RATIO =
AMM_RESERVE_PRECISION.div(QUOTE_PRECISION); // 10^3
const PRICE_DIV_PEG =
PRICE_PRECISION.div(PEG_PRECISION); // 10^0 = 1
const AMM_TIMES_PEG_TO_QUOTE_PRECISION_RATIO =
AMM_RESERVE_PRECISION.mul(PEG_PRECISION).div(QUOTE_PRECISION); // 10^9
The BigNum Class
Drift provides a BigNum class that wraps BN with precision-aware operations:
Creating BigNum Values
import { BigNum } from '@drift-labs/sdk';
import { BN } from '@coral-xyz/anchor';
// Create from BN and precision
const price = BigNum.from(new BN(10500000), new BN(6)); // 10.5
// Create from printed string
const amount = BigNum.fromPrint('10.5', new BN(6)); // 10,500,000 with precision 10^6
Arithmetic Operations
const a = BigNum.from(new BN(10000000), new BN(6)); // 10.0
const b = BigNum.from(new BN(5000000), new BN(6)); // 5.0
// Addition/Subtraction (requires same precision)
const sum = a.add(b); // 15.0
const diff = a.sub(b); // 5.0
// Multiplication (precisions add)
const product = a.mul(b); // precision = 10^12
// Scalar multiplication (maintains precision)
const scaled = a.scalarMul(b); // 50.0, precision = 10^6
// Division
const quotient = a.div(b); // 2.0
Shifting Precision
const value = BigNum.from(new BN(1000000), new BN(6)); // 1.0
// Shift up (multiply by 10^3)
const shifted = value.shift(new BN(3)); // 1000.0, precision 10^9
// Shift to specific precision
const converted = value.shiftTo(new BN(9)); // Same as shift(3)
const amount = BigNum.from(new BN(1234567890), new BN(6)); // 1234.56789
// Print full precision
amount.print(); // "1234.56789"
// Print without trailing zeros
amount.printShort(); // "1234.56789"
// Fixed decimal places
amount.toFixed(2); // "1234.56"
// Significant figures
amount.toPrecision(4); // "1234"
// Pretty print with thousand separators
amount.prettyPrint(); // "1,234.56789"
// Format as dollar amount
amount.toNotional(); // "$1,234.56"
// Millified (K, M, B, T)
amount.toMillified(3); // "1.23K"
Division Precision Handling
BN division returns the floor value by default. For exact division, you must handle the modulus.
import { BN } from '@coral-xyz/anchor';
import { convertToNumber } from '@drift-labs/sdk';
// INCORRECT - Gets floor value only
const bad = new BN(10500).div(new BN(1000)).toNumber();
// Result: 10
// CORRECT - Manual handling
const good = new BN(10500).div(new BN(1000)).toNumber() +
new BN(10500).mod(new BN(1000)).toNumber() / 1000;
// Result: 10.5
// BEST - Use helper function
const best = convertToNumber(new BN(10500), new BN(1000));
// Result: 10.5
convertToNumber Helper
The SDK provides a convertToNumber function for safe BN to number conversion:
import { convertToNumber, PRICE_PRECISION, QUOTE_PRECISION } from '@drift-labs/sdk';
// Convert price from BN to number
const priceBN = new BN(10500000); // 10.5 with PRICE_PRECISION
const priceNum = convertToNumber(priceBN, PRICE_PRECISION);
// Result: 10.5
// Convert quote amount
const quoteBN = new BN(1000000000); // 1000 with QUOTE_PRECISION
const quoteNum = convertToNumber(quoteBN, QUOTE_PRECISION);
// Result: 1000.0
Implementation
export const convertToNumber = (
bigNumber: BN,
precision: BN = PRICE_PRECISION
) => {
if (!bigNumber) return 0;
return (
bigNumber.div(precision).toNumber() +
bigNumber.mod(precision).toNumber() / precision.toNumber()
);
};
convertToBN Helper
Convert JavaScript numbers to BN with proper precision:
import { convertToBN, QUOTE_PRECISION } from '@drift-labs/sdk';
const amount = 10.5;
const amountBN = convertToBN(amount, QUOTE_PRECISION);
// Result: BN(10500000)
Implementation
export function convertToBN(value: number, precision: BN): BN {
const wholePart = Math.floor(value);
const decimalPart = Math.round((value - wholePart) * precision.toNumber());
return new BN(wholePart).mul(precision).add(new BN(decimalPart));
}
Common Patterns
Working with Prices
import { PRICE_PRECISION, convertToNumber } from '@drift-labs/sdk';
// Oracle price is in PRICE_PRECISION
const oraclePrice = driftClient.getOracleDataForPerpMarket(0).price;
const priceDecimal = convertToNumber(oraclePrice, PRICE_PRECISION);
console.log(`SOL-PERP price: $${priceDecimal}`);
Working with Position Sizes
import { BASE_PRECISION, QUOTE_PRECISION } from '@drift-labs/sdk';
// Position size in BASE_PRECISION (10^9)
const baseSize = new BN(1).mul(BASE_PRECISION); // 1 SOL
// Convert to decimal
const sizeDecimal = convertToNumber(baseSize, BASE_PRECISION);
// Result: 1.0
Calculating Notional Value
import {
BASE_PRECISION,
PRICE_PRECISION,
QUOTE_PRECISION,
convertToNumber
} from '@drift-labs/sdk';
const baseAmount = new BN(5).mul(BASE_PRECISION); // 5 SOL
const price = new BN(100).mul(PRICE_PRECISION); // $100
// notional = baseAmount * price / BASE_PRECISION
const notionalBN = baseAmount.mul(price).div(BASE_PRECISION);
const notionalUSD = convertToNumber(notionalBN, PRICE_PRECISION);
// Result: $500
Best Practices
- Always use BN for calculations - Never convert to numbers until the final display step
- Match precisions - Ensure values have the same precision before adding/subtracting
- Use helper functions - Prefer
convertToNumber() over manual division
- Be careful with multiplication - Precision accumulates, so adjust accordingly
- Handle division carefully - Remember BN division truncates by default
Examples from SDK
Trading Example
import {
DriftClient,
BASE_PRECISION,
PRICE_PRECISION,
convertToNumber,
calculateBidAskPrice
} from '@drift-labs/sdk';
// Get market data
const marketIndex = 0; // SOL-PERP
const market = driftClient.getPerpMarketAccount(marketIndex);
const oracleData = driftClient.getOracleDataForPerpMarket(marketIndex);
// Get bid/ask prices (returns BN with PRICE_PRECISION)
const [bid, ask] = calculateBidAskPrice(market.amm, oracleData);
const bidPrice = convertToNumber(bid, PRICE_PRECISION);
const askPrice = convertToNumber(ask, PRICE_PRECISION);
console.log(`Bid: $${bidPrice}, Ask: $${askPrice}`);
// Place order for 1 SOL
const orderSize = new BN(1).mul(BASE_PRECISION);