use anchor_lang::prelude::*;
use crate::drift::components::*;
pub fn get_usdc_amount_drift_data(
user_data: &[u8],
spot_market_data: &[u8]
) -> Result<u64> {
let user_positions: UserSpots = get_user_position_data(user_data)?;
let mut buf = &spot_market_data[8..];
let spot_market = SpotMarket::deserialize(&mut buf)?;
let usdc_amount: u128 = get_token_amount(
user_positions.spot_positions[0].scaled_balance.cast()?,
&spot_market,
&SpotBalanceType::Deposit,
)?;
Ok(usdc_amount as u64)
}
#[inline(never)]
pub fn get_user_position_data(user_data: &[u8]) -> Result<UserSpots> {
let spot_positions_data = &user_data[ANCHOR_DISCRIMINATOR_SIZE + SPOT_POSITIONS_OFFSET..
ANCHOR_DISCRIMINATOR_SIZE + PERP_POSITIONS_OFFSET];
let spot_positions: [SpotPosition; SPOT_POSITION_COUNT] =
AnchorDeserialize::deserialize(&mut &spot_positions_data[..])
.map_err(|_| ProgramError::InvalidAccountData)?;
Ok(UserSpots { spot_positions })
}
#[cfg(test)]
mod tests {
use super::*;
use std::fs;
use std::path::PathBuf;
fn get_test_assets_dir() -> PathBuf {
let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
path.push("test_assets");
path
}
#[test]
fn get_usdc_amount() {
let assets_dir = get_test_assets_dir();
let test_files = vec![
("spot_market_raw.bin", "user_account_raw.bin", "Local"),
("spot_market_usdc_mainnet.bin", "user_account_usdc_mainnet.bin", "Mainnet"),
];
for (spot_filename, user_filename, label) in test_files {
let market_path = assets_dir.join(spot_filename);
let user_path = assets_dir.join(user_filename);
if !market_path.exists() {
println!("Skipping {} - file doesn't exist", spot_filename);
continue;
}
if !user_path.exists() {
println!("Skipping {} - file doesn't exist", user_filename);
continue;
}
let spot_market = fs::read(&market_path).expect(&format!("Failed to read {}", spot_filename));
let user_account = fs::read(&user_path).expect(&format!("Failed to read {}", user_filename));
get_usdc_amount_drift_data(&user_account, &spot_market).map(|amount| {
println!("{} USDC amount: {}", label, amount);
}).unwrap_or_else(|err| {
println!("Error getting USDC amount for {}: {:?}", label, err);
});
}
}
#[test]
fn test_spot_market_comprehensive_validation() {
let assets_dir = get_test_assets_dir();
let test_files = vec![
("spot_market_raw.bin", "Local"),
("spot_market_usdc_mainnet.bin", "Mainnet"),
];
for (filename, label) in test_files {
let market_path = assets_dir.join(filename);
if !market_path.exists() {
println!("Skipping {} - file doesn't exist", filename);
continue;
}
let data = fs::read(&market_path).expect(&format!("Failed to read {}", filename));
let mut buf = &data[8..];
let spot_market = SpotMarket::deserialize(&mut buf).expect(&format!("Failed to deserialize {}", filename));
println!("\n=== {} SpotMarket Field Validation ({}) ===\n", label, filename);
println!("Basic fields:");
println!(" Decimals: {} (expect 6 for USDC)", spot_market.decimals);
println!(" Market index: {} (expect 0 for USDC)", spot_market.market_index);
println!(" Orders enabled: {}", spot_market.orders_enabled);
println!(" Status: {:?}", spot_market.status);
println!("\nFinancial fields:");
println!(" Deposit balance: {}", spot_market.deposit_balance);
println!(" Borrow balance: {}", spot_market.borrow_balance);
println!(" Cumulative deposit interest: {}", spot_market.cumulative_deposit_interest);
println!(" Cumulative borrow interest: {}", spot_market.cumulative_borrow_interest);
println!("\nAsset weights:");
println!(" Initial asset weight: {}", spot_market.initial_asset_weight);
println!(" Maintenance asset weight: {}", spot_market.maintenance_asset_weight);
println!(" Initial liability weight: {}", spot_market.initial_liability_weight);
println!(" Maintenance liability weight: {}", spot_market.maintenance_liability_weight);
println!("\nOrder parameters:");
println!(" Order step size: {}", spot_market.order_step_size);
println!(" Min order size: {}", spot_market.min_order_size);
println!(" Max position size: {}", spot_market.max_position_size);
println!("\nPubkeys:");
println!(" Mint: {}", spot_market.mint);
println!(" Oracle: {}", spot_market.oracle);
println!(" Vault: {}", spot_market.vault);
let name_str = String::from_utf8_lossy(&spot_market.name)
.trim_end_matches('\0')
.to_string();
assert_eq!(spot_market.decimals, 6, "{}: USDC should have 6 decimals", label);
assert_eq!(spot_market.market_index, 0, "{}: USDC should be market 0", label);
assert!(spot_market.initial_asset_weight > 0, "{}: Initial asset weight should be non-zero", label);
assert!(spot_market.maintenance_asset_weight > 0, "{}: Maintenance asset weight should be non-zero", label);
let usdc_mint_bytes = bs58::decode("EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v")
.into_vec()
.unwrap();
let usdc_mint = Pubkey::new_from_array(usdc_mint_bytes.try_into().unwrap());
if spot_market.mint == usdc_mint {
println!("\n✓ {} Mint matches mainnet USDC", label);
} else {
println!("\n⚠️ {} Mint doesn't match mainnet USDC, might be devnet or different", label);
}
let expected_deposit_interest = 11_722_589_531u128;
let expected_borrow_interest = 13_485_551_200u128;
assert!(
spot_market.cumulative_deposit_interest > (expected_deposit_interest * 85 / 100) &&
spot_market.cumulative_deposit_interest < (expected_deposit_interest * 115 / 100),
"{}: Cumulative deposit interest {} outside 15% range of expected {}",
label,
spot_market.cumulative_deposit_interest,
expected_deposit_interest
);
assert!(
spot_market.cumulative_borrow_interest > (expected_borrow_interest * 85 / 100) &&
spot_market.cumulative_borrow_interest < (expected_borrow_interest * 115 / 100),
"{}: Cumulative borrow interest {} outside 15% range of expected {}",
label,
spot_market.cumulative_borrow_interest,
expected_borrow_interest
);
println!("\n{} validations passed!", label);
}
}
}