Skip to main content

Overview

The SpotPosition struct represents a user’s position in a spot market. Unlike perpetual positions, spot positions track actual token balances that can be either deposits (assets) or borrows (liabilities).

Structure

Fields

scaled_balance
u64
Scaled balance in the spot market. This value is scaled by the market’s cumulative interest rate and must be converted to token amount using the current interest rate.
open_bids
i64
Total quote asset reserved in open bid orders for this market.
open_asks
i64
Total base asset reserved in open ask orders for this market.
cumulative_deposits
i64
Cumulative deposits made to this market over time. Used for tracking deposit history.
market_index
u16
Index of the spot market for this position.
balance_type
SpotBalanceType
Type of balance: Deposit (asset) or Borrow (liability).
open_orders
u8
Number of open orders in this market.

Balance Types

Spot positions can be either deposits or borrows:

SpotBalanceType Enum

pub enum SpotBalanceType {
    Deposit, // User has deposited tokens (asset)
    Borrow,  // User has borrowed tokens (liability)
}
  • Deposit: The user owns tokens in this market. They can withdraw or use as collateral.
  • Borrow: The user owes tokens in this market. They must repay or maintain collateral.
use drift_rs::types::{SpotPosition, SpotBalanceType};

let is_deposit = position.balance_type == SpotBalanceType::Deposit;
let is_borrow = position.balance_type == SpotBalanceType::Borrow;

if is_deposit {
    println!("User has deposited tokens");
} else {
    println!("User has borrowed tokens");
}

Token Amount Calculation

The scaled_balance must be converted to actual token amount using the market’s cumulative interest rate:
// Get actual token balance
let token_amount = if position.balance_type == SpotBalanceType::Deposit {
    // For deposits, scale up
    (position.scaled_balance as u128 * market.cumulative_deposit_interest) 
        / SPOT_BALANCE_PRECISION
} else {
    // For borrows, scale up  
    (position.scaled_balance as u128 * market.cumulative_borrow_interest) 
        / SPOT_BALANCE_PRECISION
};
Where SPOT_BALANCE_PRECISION is typically 10^9 or 10^10 depending on the market.

Interest Accrual

Spot positions earn interest (deposits) or accrue interest (borrows) over time:

Deposit Interest

// Interest earned on deposits
let initial_deposit = 1000;
let current_value = (position.scaled_balance as u128 
                   * market.cumulative_deposit_interest) 
                   / SPOT_BALANCE_PRECISION;
let interest_earned = current_value - initial_deposit;

Borrow Interest

// Interest owed on borrows
let initial_borrow = 1000;
let current_owed = (position.scaled_balance as u128 
                  * market.cumulative_borrow_interest) 
                  / SPOT_BALANCE_PRECISION;
let interest_owed = current_owed - initial_borrow;

Available Balance

The available balance accounts for tokens reserved in open orders:
// Calculate available balance (not reserved in orders)
let token_amount = calculate_token_amount(&position, &market);

let available_balance = if position.balance_type == SpotBalanceType::Deposit {
    token_amount - position.open_asks.max(0) as u64
} else {
    token_amount
};

println!("Available to withdraw: {}", available_balance);

Collateral Value

Spot deposits can be used as collateral for perpetual positions:
// Calculate collateral value in USD
let token_amount = calculate_token_amount(&position, &market);
let oracle_price = get_oracle_price(&market);

let collateral_value = if position.balance_type == SpotBalanceType::Deposit {
    // Deposits add to collateral
    (token_amount as i64 * oracle_price * market.initial_asset_weight) 
        / (PRICE_PRECISION * WEIGHT_PRECISION)
} else {
    // Borrows reduce collateral
    -(token_amount as i64 * oracle_price * market.initial_liability_weight) 
        / (PRICE_PRECISION * WEIGHT_PRECISION)
};

println!("Collateral contribution: ${:.2}", collateral_value as f64 / 1e6);

Position Status

Check various position states:
use drift_rs::types::SpotPosition;

fn check_position_status(position: &SpotPosition) {
    // Has position?
    let has_position = position.scaled_balance > 0;
    
    // Has open orders?
    let has_orders = position.open_orders > 0;
    
    // Is active (position or orders)?
    let is_active = has_position || has_orders;
    
    println!("Has position: {}", has_position);
    println!("Has orders: {}", has_orders);
    println!("Is active: {}", is_active);
}

Example: Position Summary

use drift_rs::types::{SpotPosition, SpotBalanceType};

fn print_spot_position(
    position: &SpotPosition,
    market: &SpotMarket,
    oracle_price: i64,
) {
    // Skip if no position
    if position.scaled_balance == 0 {
        return;
    }
    
    // Calculate token amount
    let token_amount = if position.balance_type == SpotBalanceType::Deposit {
        (position.scaled_balance as u128 * market.cumulative_deposit_interest) 
            / SPOT_BALANCE_PRECISION
    } else {
        (position.scaled_balance as u128 * market.cumulative_borrow_interest) 
            / SPOT_BALANCE_PRECISION
    };
    
    // Calculate USD value
    let usd_value = (token_amount as i64 * oracle_price) / PRICE_PRECISION;
    
    // Determine type
    let position_type = match position.balance_type {
        SpotBalanceType::Deposit => "Deposit",
        SpotBalanceType::Borrow => "Borrow",
    };
    
    println!("=== Spot Position ===");
    println!("Market Index: {}", position.market_index);
    println!("Type: {}", position_type);
    println!("Token Amount: {}", token_amount);
    println!("USD Value: ${:.2}", usd_value as f64 / 1e6);
    println!("Open Orders: {}", position.open_orders);
    println!("Open Bids: {}", position.open_bids);
    println!("Open Asks: {}", position.open_asks);
    println!("Cumulative Deposits: {}", position.cumulative_deposits);
}

Working with Multiple Positions

Users typically have multiple spot positions across different markets:
use drift_rs::types::{User, SpotPosition, SpotBalanceType};

fn calculate_total_collateral(user: &User, markets: &[SpotMarket]) -> i64 {
    let mut total_collateral = 0i64;
    
    for position in &user.spot_positions {
        if position.scaled_balance == 0 {
            continue;
        }
        
        let market = &markets[position.market_index as usize];
        let token_amount = calculate_token_amount(position, market);
        let oracle_price = get_oracle_price(market);
        
        let contribution = if position.balance_type == SpotBalanceType::Deposit {
            // Assets add to collateral
            (token_amount as i64 * oracle_price * market.initial_asset_weight) 
                / (PRICE_PRECISION * WEIGHT_PRECISION)
        } else {
            // Liabilities subtract from collateral
            -(token_amount as i64 * oracle_price * market.initial_liability_weight) 
                / (PRICE_PRECISION * WEIGHT_PRECISION)
        };
        
        total_collateral += contribution;
    }
    
    total_collateral
}

Build docs developers (and LLMs) love