From 2f502c9bf60746a52c4a6fb47f31de801ca3e4bc Mon Sep 17 00:00:00 2001 From: Nour Alharithi Date: Wed, 9 Jul 2025 11:32:10 -0700 Subject: [PATCH 1/2] refactor settle pnl to modularize and add tests --- programs/drift/src/instructions/keeper.rs | 275 +++++++--------------- programs/drift/src/math/lp_pool.rs | 183 ++++++++++++++ programs/drift/src/math/mod.rs | 1 + programs/drift/src/state/lp_pool/tests.rs | 157 ++++++++++++ sdk/src/idl/drift.json | 17 ++ 5 files changed, 448 insertions(+), 185 deletions(-) create mode 100644 programs/drift/src/math/lp_pool.rs diff --git a/programs/drift/src/instructions/keeper.rs b/programs/drift/src/instructions/keeper.rs index 0f06d7d223..bfd57b7596 100644 --- a/programs/drift/src/instructions/keeper.rs +++ b/programs/drift/src/instructions/keeper.rs @@ -30,6 +30,7 @@ use crate::math::casting::Cast; use crate::math::constants::QUOTE_PRECISION; use crate::math::constants::QUOTE_SPOT_MARKET_INDEX; use crate::math::constants::SPOT_BALANCE_PRECISION; +use crate::math::lp_pool::perp_lp_pool_settlement; use crate::math::margin::{calculate_user_equity, meets_settle_pnl_maintenance_margin_requirement}; use crate::math::orders::{estimate_price_from_side, find_bids_and_asks_from_users}; use crate::math::position::calculate_base_asset_value_and_pnl_with_oracle_price; @@ -2944,12 +2945,17 @@ pub fn handle_pause_spot_market_deposit_withdraw( Ok(()) } +// Refactored main function pub fn handle_settle_perp_to_lp_pool<'c: 'info, 'info>( ctx: Context<'_, '_, 'c, 'info, SettleAmmPnlToLp<'info>>, ) -> Result<()> { - let slot = Clock::get()?.slot; + use perp_lp_pool_settlement::*; + let slot = Clock::get()?.slot; + let timestamp = Clock::get()?.unix_timestamp; let state = &ctx.accounts.state; + + // Validation and setup code (unchanged) let amm_cache_key = &ctx.accounts.amm_cache.key(); let mut amm_cache: AccountZeroCopyMut<'_, CacheInfo, _> = ctx.accounts.amm_cache.load_zc_mut()?; @@ -2958,8 +2964,7 @@ pub fn handle_settle_perp_to_lp_pool<'c: 'info, 'info>( let constituent_token_account = &mut ctx.accounts.constituent_quote_token_account; let mut lp_pool = ctx.accounts.lp_pool.load_mut()?; - let clock = Clock::get()?; - + // PDA validation (unchanged) let expected_pda = &Pubkey::create_program_address( &[ AMM_POSITIONS_CACHE.as_ref(), @@ -2987,12 +2992,19 @@ pub fn handle_settle_perp_to_lp_pool<'c: 'info, 'info>( None, )?; + let precision_increase = SPOT_BALANCE_PRECISION.safe_div(QUOTE_PRECISION)?; + let mint = Some(*ctx.accounts.mint.clone()); + for (_, perp_market_loader) in perp_market_map.0.iter() { let mut perp_market = perp_market_loader.load_mut()?; + if perp_market.lp_status == 0 { + continue; + } + let cached_info = amm_cache.get_mut(perp_market.market_index as u32); + // Early validation checks (unchanged) if slot.saturating_sub(cached_info.oracle_slot) > SETTLE_AMM_ORACLE_MAX_DELAY { - // If the oracle slot is not up to date, skip this market msg!( "Skipping settling perp market {} to dlp because oracle slot is not up to date", perp_market.market_index @@ -3001,6 +3013,7 @@ pub fn handle_settle_perp_to_lp_pool<'c: 'info, 'info>( } validate_market_within_price_band(&perp_market, state, cached_info.oracle_price)?; + if perp_market.is_operation_paused(PerpOperation::SettlePnl) { msg!( "Cannot settle pnl under current market = {} status", @@ -3011,207 +3024,99 @@ pub fn handle_settle_perp_to_lp_pool<'c: 'info, 'info>( if cached_info.slot != slot { msg!("Skipping settling perp market {} to lp pool because amm cache was not updated in the same slot", - perp_market.market_index - ); + perp_market.market_index); return Err(ErrorCode::AMMCacheStale.into()); } - let mint = *ctx.accounts.mint.clone(); - // Transfer balance if it's available - if cached_info.quote_owed_from_lp > 0 { - if quote_constituent.token_balance == 0 { - msg!("LP Pool has no usdc to settle",); - continue; - } - let amount_to_send = - if cached_info.quote_owed_from_lp > quote_constituent.token_balance as i64 { - quote_constituent.token_balance - } else { - cached_info.quote_owed_from_lp as u64 - }; - - cached_info.quote_owed_from_lp = cached_info - .quote_owed_from_lp - .safe_sub(amount_to_send as i64)?; - - controller::token::send_from_program_vault( - &ctx.accounts.token_program, - constituent_token_account, - &ctx.accounts.quote_token_vault, - &ctx.accounts.drift_signer, - state.signer_nonce, - amount_to_send, - &Some(mint), - )?; - - // Send all revenues to the perp market fee pool - let precision_increase = SPOT_BALANCE_PRECISION.safe_div(QUOTE_PRECISION)?; - perp_market - .amm - .fee_pool - .increase_balance((amount_to_send as u128).safe_mul(precision_increase)?)?; - - // Update LP Pool Stats - lp_pool.cumulative_usdc_sent_to_perp_markets = lp_pool - .cumulative_usdc_sent_to_perp_markets - .saturating_add(amount_to_send.cast::()?); - - // Decrement cached fee pool token amount - cached_info.last_fee_pool_token_amount = cached_info - .last_fee_pool_token_amount - .safe_add(amount_to_send as u128)?; - - // Sync the constituent token account balance - constituent_token_account.reload()?; - quote_constituent.sync_token_balance(constituent_token_account.amount); - - // Update the last settle info - cached_info.last_settle_amount = amount_to_send.cast::()?; - cached_info.last_settle_ts = Clock::get()?.unix_timestamp; - - // Update LP Pool Stats - lp_pool.cumulative_usdc_sent_to_perp_markets = lp_pool - .cumulative_usdc_sent_to_perp_markets - .saturating_add(amount_to_send.cast::()?); - } else if cached_info.quote_owed_from_lp < 0 { - // We now send from the perp market to dlp and wipe out the amount owed from the perp market - let amount_to_send = cached_info.quote_owed_from_lp.abs() as u64; - - // Take from the fee pool if it can cover the whole balance, otherwise also take from pnl pool - let precision_increase = SPOT_BALANCE_PRECISION.safe_div(QUOTE_PRECISION)?; - let fee_pool_token_amount = get_token_amount( + // Create settlement context + let settlement_ctx = SettlementContext { + quote_owed_from_lp: cached_info.quote_owed_from_lp, + quote_constituent_token_balance: quote_constituent.token_balance, + fee_pool_balance: get_token_amount( perp_market.amm.fee_pool.scaled_balance, - "e_market, + quote_market, &SpotBalanceType::Deposit, - )?; - if fee_pool_token_amount > amount_to_send as u128 { - perp_market - .amm - .fee_pool - .decrease_balance((amount_to_send as u128).safe_mul(precision_increase)?)?; - cached_info.last_fee_pool_token_amount = cached_info - .last_fee_pool_token_amount - .safe_sub(amount_to_send as u128)?; - cached_info.quote_owed_from_lp = 0; - - controller::token::send_from_program_vault( + )?, + pnl_pool_balance: get_token_amount( + perp_market.pnl_pool.scaled_balance, + quote_market, + &SpotBalanceType::Deposit, + )?, + quote_market, + }; + + // Calculate settlement + let settlement_result = calculate_settlement_amount(&settlement_ctx)?; + + if settlement_result.direction == SettlementDirection::None { + continue; + } + + // Execute token transfer + match settlement_result.direction { + SettlementDirection::FromLpPool => { + execute_token_transfer( + &ctx.accounts.token_program, + constituent_token_account, + &ctx.accounts.quote_token_vault, + &ctx.accounts.drift_signer, + state.signer_nonce, + settlement_result.amount_transferred, + &mint, + )?; + } + SettlementDirection::ToLpPool => { + execute_token_transfer( &ctx.accounts.token_program, &ctx.accounts.quote_token_vault, constituent_token_account, &ctx.accounts.drift_signer, state.signer_nonce, - amount_to_send.cast::()?, - &Some(mint), + settlement_result.amount_transferred, + &mint, )?; + } + SettlementDirection::None => unreachable!(), + } + + // Update market pools + update_perp_market_pools(&mut perp_market, &settlement_result, precision_increase)?; - // Sync the constituent token account balance - constituent_token_account.reload()?; - quote_constituent.sync_token_balance(constituent_token_account.amount); + // Calculate new quote owed amount + let new_quote_owed = match settlement_result.direction { + SettlementDirection::FromLpPool => cached_info + .quote_owed_from_lp + .safe_sub(settlement_result.amount_transferred as i64)?, + SettlementDirection::ToLpPool => cached_info + .quote_owed_from_lp + .safe_add(settlement_result.amount_transferred as i64)?, + SettlementDirection::None => cached_info.quote_owed_from_lp, + }; - // Update the last settle info - cached_info.last_settle_amount = amount_to_send.cast::()?; - cached_info.last_settle_ts = Clock::get()?.unix_timestamp; + // Update cache info + update_cache_info(cached_info, &settlement_result, new_quote_owed, timestamp)?; - // Update LP Pool Stats + // Update LP pool stats + match settlement_result.direction { + SettlementDirection::FromLpPool => { + lp_pool.cumulative_usdc_sent_to_perp_markets = lp_pool + .cumulative_usdc_sent_to_perp_markets + .saturating_add(settlement_result.amount_transferred as u128); + } + SettlementDirection::ToLpPool => { lp_pool.cumulative_usdc_received_from_perp_markets = lp_pool .cumulative_usdc_received_from_perp_markets - .saturating_add(amount_to_send.cast::()?); - } else { - // If the fee pool cannot cover the whole amount, we take the rest from the pnl pool and set the - // fee pool balances to 0 - - let remaining_amount_to_send = - (amount_to_send as u128).safe_sub(fee_pool_token_amount)?; - - perp_market - .amm - .fee_pool - .decrease_balance(fee_pool_token_amount.safe_mul(precision_increase)?)?; - cached_info.last_fee_pool_token_amount = 0; - cached_info.quote_owed_from_lp += fee_pool_token_amount.cast::()?; - - // Similarly, can the pnl pool cover the rest? - let pnl_pool_token_amount = get_token_amount( - perp_market.pnl_pool.scaled_balance, - "e_market, - &SpotBalanceType::Deposit, - )?; - if remaining_amount_to_send > pnl_pool_token_amount { - let transfer_amount = if pnl_pool_token_amount == 0 { - fee_pool_token_amount - } else { - perp_market.pnl_pool.decrease_balance( - pnl_pool_token_amount.safe_mul(precision_increase)?, - )?; - cached_info.last_net_pnl_pool_token_amount = cached_info - .last_net_pnl_pool_token_amount - .safe_sub(pnl_pool_token_amount.cast::()?)?; - cached_info.quote_owed_from_lp += pnl_pool_token_amount.cast::()?; - - fee_pool_token_amount.safe_add(pnl_pool_token_amount)? - }; - - controller::token::send_from_program_vault( - &ctx.accounts.token_program, - &ctx.accounts.quote_token_vault, - constituent_token_account, - &ctx.accounts.drift_signer, - state.signer_nonce, - transfer_amount.cast::()?, - &Some(mint), - )?; - - // Sync the constituent token account balance - constituent_token_account.reload()?; - quote_constituent.sync_token_balance(constituent_token_account.amount); - - // Update the last settle info - cached_info.last_settle_amount = transfer_amount.cast::()?; - cached_info.last_settle_ts = Clock::get()?.unix_timestamp; - - // Update LP Pool Stats - lp_pool.cumulative_usdc_received_from_perp_markets = lp_pool - .cumulative_usdc_received_from_perp_markets - .saturating_add(transfer_amount.cast::()?); - } else { - perp_market - .pnl_pool - .decrease_balance(remaining_amount_to_send.safe_mul(precision_increase)?)?; - cached_info - .last_net_pnl_pool_token_amount - .safe_sub(remaining_amount_to_send.cast::()?)?; - cached_info.quote_owed_from_lp += remaining_amount_to_send.cast::()?; - - controller::token::send_from_program_vault( - &ctx.accounts.token_program, - &ctx.accounts.quote_token_vault, - constituent_token_account, - &ctx.accounts.drift_signer, - state.signer_nonce, - amount_to_send.cast::()?, - &Some(mint), - )?; - - // Sync the constituent token account balance - constituent_token_account.reload()?; - quote_constituent.sync_token_balance(constituent_token_account.amount); - - // Update the last settle info - cached_info.last_settle_amount = amount_to_send.cast::()?; - cached_info.last_settle_ts = Clock::get()?.unix_timestamp; - - // Update LP Pool Stats - lp_pool.cumulative_usdc_received_from_perp_markets = lp_pool - .cumulative_usdc_received_from_perp_markets - .saturating_add(amount_to_send.cast::()?); - } + .saturating_add(settlement_result.amount_transferred as u128); } - } else { - // nothing owed to settle - continue; + SettlementDirection::None => {} } + + // Sync constituent token balance + constituent_token_account.reload()?; + quote_constituent.sync_token_balance(constituent_token_account.amount); } + // Final validation math::spot_withdraw::validate_spot_market_vault_amount( quote_market, ctx.accounts.quote_token_vault.amount, diff --git a/programs/drift/src/math/lp_pool.rs b/programs/drift/src/math/lp_pool.rs new file mode 100644 index 0000000000..23e808a7ab --- /dev/null +++ b/programs/drift/src/math/lp_pool.rs @@ -0,0 +1,183 @@ +pub mod perp_lp_pool_settlement { + use crate::{ + math::safe_math::SafeMath, + state::{ + perp_market::{CacheInfo, PerpMarket}, + spot_market::{SpotBalance, SpotMarket}, + }, + *, + }; + use anchor_spl::token_interface::{Mint, TokenAccount, TokenInterface}; + + #[derive(Debug, Clone)] + pub struct SettlementResult { + pub amount_transferred: u64, + pub direction: SettlementDirection, + pub fee_pool_used: u128, + pub pnl_pool_used: u128, + } + + #[derive(Debug, Clone, PartialEq)] + pub enum SettlementDirection { + ToLpPool, + FromLpPool, + None, + } + + pub struct SettlementContext<'a> { + pub quote_owed_from_lp: i64, + pub quote_constituent_token_balance: u64, + pub fee_pool_balance: u128, + pub pnl_pool_balance: u128, + pub quote_market: &'a SpotMarket, + } + + pub fn calculate_settlement_amount(ctx: &SettlementContext) -> Result { + if ctx.quote_owed_from_lp > 0 { + calculate_lp_to_perp_settlement(ctx) + } else if ctx.quote_owed_from_lp < 0 { + calculate_perp_to_lp_settlement(ctx) + } else { + Ok(SettlementResult { + amount_transferred: 0, + direction: SettlementDirection::None, + fee_pool_used: 0, + pnl_pool_used: 0, + }) + } + } + + fn calculate_lp_to_perp_settlement(ctx: &SettlementContext) -> Result { + if ctx.quote_constituent_token_balance == 0 { + return Ok(SettlementResult { + amount_transferred: 0, + direction: SettlementDirection::None, + fee_pool_used: 0, + pnl_pool_used: 0, + }); + } + + let amount_to_send = if ctx.quote_owed_from_lp > ctx.quote_constituent_token_balance as i64 + { + ctx.quote_constituent_token_balance + } else { + ctx.quote_owed_from_lp as u64 + }; + + Ok(SettlementResult { + amount_transferred: amount_to_send, + direction: SettlementDirection::FromLpPool, + fee_pool_used: 0, + pnl_pool_used: 0, + }) + } + + fn calculate_perp_to_lp_settlement(ctx: &SettlementContext) -> Result { + let amount_to_send = ctx.quote_owed_from_lp.abs() as u64; + + if ctx.fee_pool_balance >= amount_to_send as u128 { + // Fee pool can cover entire amount + Ok(SettlementResult { + amount_transferred: amount_to_send, + direction: SettlementDirection::ToLpPool, + fee_pool_used: amount_to_send as u128, + pnl_pool_used: 0, + }) + } else { + // Need to use both fee pool and pnl pool + let remaining_amount = (amount_to_send as u128).safe_sub(ctx.fee_pool_balance)?; + let pnl_pool_used = remaining_amount.min(ctx.pnl_pool_balance); + let actual_transfer = ctx.fee_pool_balance.safe_add(pnl_pool_used)?; + + Ok(SettlementResult { + amount_transferred: actual_transfer as u64, + direction: SettlementDirection::ToLpPool, + fee_pool_used: ctx.fee_pool_balance, + pnl_pool_used, + }) + } + } + + pub fn execute_token_transfer<'info>( + token_program: &Interface<'info, TokenInterface>, + from_vault: &InterfaceAccount<'info, TokenAccount>, + to_vault: &InterfaceAccount<'info, TokenAccount>, + signer: &AccountInfo<'info>, + signer_nonce: u8, + amount: u64, + mint: &Option>, + ) -> Result<()> { + controller::token::send_from_program_vault( + token_program, + from_vault, + to_vault, + signer, + signer_nonce, + amount, + mint, + ) + } + + // Market state updates + pub fn update_perp_market_pools( + perp_market: &mut PerpMarket, + result: &SettlementResult, + precision_increase: u128, + ) -> Result<()> { + match result.direction { + SettlementDirection::FromLpPool => { + perp_market.amm.fee_pool.increase_balance( + (result.amount_transferred as u128).safe_mul(precision_increase)?, + )?; + } + SettlementDirection::ToLpPool => { + if result.fee_pool_used > 0 { + perp_market + .amm + .fee_pool + .decrease_balance(result.fee_pool_used.safe_mul(precision_increase)?)?; + } + if result.pnl_pool_used > 0 { + perp_market + .pnl_pool + .decrease_balance(result.pnl_pool_used.safe_mul(precision_increase)?)?; + } + } + SettlementDirection::None => {} + } + Ok(()) + } + + pub fn update_cache_info( + cache_info: &mut CacheInfo, + result: &SettlementResult, + new_quote_owed: i64, + timestamp: i64, + ) -> Result<()> { + cache_info.quote_owed_from_lp = new_quote_owed; + cache_info.last_settle_amount = result.amount_transferred; + cache_info.last_settle_ts = timestamp; + + match result.direction { + SettlementDirection::FromLpPool => { + cache_info.last_fee_pool_token_amount = cache_info + .last_fee_pool_token_amount + .safe_add(result.amount_transferred as u128)?; + } + SettlementDirection::ToLpPool => { + if result.fee_pool_used > 0 { + cache_info.last_fee_pool_token_amount = cache_info + .last_fee_pool_token_amount + .safe_sub(result.fee_pool_used)?; + } + if result.pnl_pool_used > 0 { + cache_info.last_net_pnl_pool_token_amount = cache_info + .last_net_pnl_pool_token_amount + .safe_sub(result.pnl_pool_used as i128)?; + } + } + SettlementDirection::None => {} + } + Ok(()) + } +} diff --git a/programs/drift/src/math/mod.rs b/programs/drift/src/math/mod.rs index aa7ec7f196..d97eafcc6e 100644 --- a/programs/drift/src/math/mod.rs +++ b/programs/drift/src/math/mod.rs @@ -17,6 +17,7 @@ pub mod helpers; pub mod insurance; pub mod liquidation; pub mod lp; +pub mod lp_pool; pub mod margin; pub mod matching; pub mod oracle; diff --git a/programs/drift/src/state/lp_pool/tests.rs b/programs/drift/src/state/lp_pool/tests.rs index be2fcc4fad..ef75736a51 100644 --- a/programs/drift/src/state/lp_pool/tests.rs +++ b/programs/drift/src/state/lp_pool/tests.rs @@ -1722,3 +1722,160 @@ mod swap_fee_tests { assert_eq!(fee_out, -6 * PERCENTAGE_PRECISION_I128 / 100000); // -0.6 bps } } + +#[cfg(test)] +mod settle_tests { + use crate::math::lp_pool::perp_lp_pool_settlement::{ + calculate_settlement_amount, SettlementContext, SettlementDirection, + }; + use crate::state::spot_market::SpotMarket; + + fn create_mock_spot_market() -> SpotMarket { + // Create a mock spot market for testing + SpotMarket::default() // You'll need to implement this or use a builder pattern + } + + #[test] + fn test_calculate_settlement_no_amount_owed() { + let ctx = SettlementContext { + quote_owed_from_lp: 0, + quote_constituent_token_balance: 1000, + fee_pool_balance: 500, + pnl_pool_balance: 300, + quote_market: &create_mock_spot_market(), + }; + + let result = calculate_settlement_amount(&ctx).unwrap(); + assert_eq!(result.direction, SettlementDirection::None); + assert_eq!(result.amount_transferred, 0); + } + + #[test] + fn test_lp_to_perp_settlement_sufficient_balance() { + let ctx = SettlementContext { + quote_owed_from_lp: 500, + quote_constituent_token_balance: 1000, + fee_pool_balance: 300, + pnl_pool_balance: 200, + quote_market: &create_mock_spot_market(), + }; + + let result = calculate_settlement_amount(&ctx).unwrap(); + assert_eq!(result.direction, SettlementDirection::FromLpPool); + assert_eq!(result.amount_transferred, 500); + assert_eq!(result.fee_pool_used, 0); + assert_eq!(result.pnl_pool_used, 0); + } + + #[test] + fn test_lp_to_perp_settlement_insufficient_balance() { + let ctx = SettlementContext { + quote_owed_from_lp: 1500, + quote_constituent_token_balance: 1000, + fee_pool_balance: 300, + pnl_pool_balance: 200, + quote_market: &create_mock_spot_market(), + }; + + let result = calculate_settlement_amount(&ctx).unwrap(); + assert_eq!(result.direction, SettlementDirection::FromLpPool); + assert_eq!(result.amount_transferred, 1000); // Limited by LP balance + } + + #[test] + fn test_lp_to_perp_settlement_no_lp_balance() { + let ctx = SettlementContext { + quote_owed_from_lp: 500, + quote_constituent_token_balance: 0, + fee_pool_balance: 300, + pnl_pool_balance: 200, + quote_market: &create_mock_spot_market(), + }; + + let result = calculate_settlement_amount(&ctx).unwrap(); + assert_eq!(result.direction, SettlementDirection::None); + assert_eq!(result.amount_transferred, 0); + } + + #[test] + fn test_perp_to_lp_settlement_fee_pool_sufficient() { + let ctx = SettlementContext { + quote_owed_from_lp: -500, + quote_constituent_token_balance: 1000, + fee_pool_balance: 800, + pnl_pool_balance: 200, + quote_market: &create_mock_spot_market(), + }; + + let result = calculate_settlement_amount(&ctx).unwrap(); + assert_eq!(result.direction, SettlementDirection::ToLpPool); + assert_eq!(result.amount_transferred, 500); + assert_eq!(result.fee_pool_used, 500); + assert_eq!(result.pnl_pool_used, 0); + } + + #[test] + fn test_perp_to_lp_settlement_needs_both_pools() { + let ctx = SettlementContext { + quote_owed_from_lp: -1000, + quote_constituent_token_balance: 2000, + fee_pool_balance: 300, + pnl_pool_balance: 800, + quote_market: &create_mock_spot_market(), + }; + + let result = calculate_settlement_amount(&ctx).unwrap(); + assert_eq!(result.direction, SettlementDirection::ToLpPool); + assert_eq!(result.amount_transferred, 1000); + assert_eq!(result.fee_pool_used, 300); + assert_eq!(result.pnl_pool_used, 700); + } + + #[test] + fn test_perp_to_lp_settlement_insufficient_pools() { + let ctx = SettlementContext { + quote_owed_from_lp: -1500, + quote_constituent_token_balance: 2000, + fee_pool_balance: 300, + pnl_pool_balance: 200, + quote_market: &create_mock_spot_market(), + }; + + let result = calculate_settlement_amount(&ctx).unwrap(); + assert_eq!(result.direction, SettlementDirection::ToLpPool); + assert_eq!(result.amount_transferred, 500); // Limited by pool balances + assert_eq!(result.fee_pool_used, 300); + assert_eq!(result.pnl_pool_used, 200); + } + + #[test] + fn test_settlement_edge_cases() { + // Test with zero fee pool + let ctx = SettlementContext { + quote_owed_from_lp: -500, + quote_constituent_token_balance: 1000, + fee_pool_balance: 0, + pnl_pool_balance: 800, + quote_market: &create_mock_spot_market(), + }; + + let result = calculate_settlement_amount(&ctx).unwrap(); + assert_eq!(result.amount_transferred, 500); + assert_eq!(result.fee_pool_used, 0); + assert_eq!(result.pnl_pool_used, 500); + + // Test with zero pnl pool + let ctx = SettlementContext { + quote_owed_from_lp: -500, + quote_constituent_token_balance: 1000, + fee_pool_balance: 300, + pnl_pool_balance: 0, + quote_market: &create_mock_spot_market(), + }; + + let result = calculate_settlement_amount(&ctx).unwrap(); + assert_eq!(result.amount_transferred, 300); + assert_eq!(result.fee_pool_used, 300); + assert_eq!(result.pnl_pool_used, 0); + } +} diff --git a/sdk/src/idl/drift.json b/sdk/src/idl/drift.json index 030f30c01f..2b637098df 100644 --- a/sdk/src/idl/drift.json +++ b/sdk/src/idl/drift.json @@ -13973,6 +13973,23 @@ ] } }, + { + "name": "SettlementDirection", + "type": { + "kind": "enum", + "variants": [ + { + "name": "ToLpPool" + }, + { + "name": "FromLpPool" + }, + { + "name": "None" + } + ] + } + }, { "name": "MarginRequirementType", "type": { From 56830962199bfbb3162ffbc3ff15e22532b2386d Mon Sep 17 00:00:00 2001 From: Nour Alharithi Date: Wed, 9 Jul 2025 12:06:31 -0700 Subject: [PATCH 2/2] more cargo tests --- programs/drift/src/instructions/keeper.rs | 8 +- programs/drift/src/instructions/lp_pool.rs | 2 +- programs/drift/src/math/lp_pool.rs | 6 +- programs/drift/src/state/lp_pool/tests.rs | 490 ++++++++++++++++++++- programs/drift/src/state/perp_market.rs | 14 +- sdk/src/idl/drift.json | 2 +- sdk/src/types.ts | 2 +- tests/lpPool.ts | 24 +- 8 files changed, 516 insertions(+), 32 deletions(-) diff --git a/programs/drift/src/instructions/keeper.rs b/programs/drift/src/instructions/keeper.rs index bfd57b7596..0901795431 100644 --- a/programs/drift/src/instructions/keeper.rs +++ b/programs/drift/src/instructions/keeper.rs @@ -3030,7 +3030,7 @@ pub fn handle_settle_perp_to_lp_pool<'c: 'info, 'info>( // Create settlement context let settlement_ctx = SettlementContext { - quote_owed_from_lp: cached_info.quote_owed_from_lp, + quote_owed_from_lp: cached_info.quote_owed_from_lp_pool, quote_constituent_token_balance: quote_constituent.token_balance, fee_pool_balance: get_token_amount( perp_market.amm.fee_pool.scaled_balance, @@ -3085,12 +3085,12 @@ pub fn handle_settle_perp_to_lp_pool<'c: 'info, 'info>( // Calculate new quote owed amount let new_quote_owed = match settlement_result.direction { SettlementDirection::FromLpPool => cached_info - .quote_owed_from_lp + .quote_owed_from_lp_pool .safe_sub(settlement_result.amount_transferred as i64)?, SettlementDirection::ToLpPool => cached_info - .quote_owed_from_lp + .quote_owed_from_lp_pool .safe_add(settlement_result.amount_transferred as i64)?, - SettlementDirection::None => cached_info.quote_owed_from_lp, + SettlementDirection::None => cached_info.quote_owed_from_lp_pool, }; // Update cache info diff --git a/programs/drift/src/instructions/lp_pool.rs b/programs/drift/src/instructions/lp_pool.rs index 8a4d1e768e..e03bde2009 100644 --- a/programs/drift/src/instructions/lp_pool.rs +++ b/programs/drift/src/instructions/lp_pool.rs @@ -325,7 +325,7 @@ pub fn handle_update_lp_pool_aum<'c: 'info, 'info>( let mut aum_i128 = aum.cast::()?; for cache_datum in amm_cache.iter() { - aum_i128 -= cache_datum.quote_owed_from_lp as i128; + aum_i128 -= cache_datum.quote_owed_from_lp_pool as i128; } aum = aum_i128.max(0i128).cast::()?; diff --git a/programs/drift/src/math/lp_pool.rs b/programs/drift/src/math/lp_pool.rs index 23e808a7ab..a39aa71a98 100644 --- a/programs/drift/src/math/lp_pool.rs +++ b/programs/drift/src/math/lp_pool.rs @@ -9,7 +9,7 @@ pub mod perp_lp_pool_settlement { }; use anchor_spl::token_interface::{Mint, TokenAccount, TokenInterface}; - #[derive(Debug, Clone)] + #[derive(Debug, Clone, Copy)] pub struct SettlementResult { pub amount_transferred: u64, pub direction: SettlementDirection, @@ -17,7 +17,7 @@ pub mod perp_lp_pool_settlement { pub pnl_pool_used: u128, } - #[derive(Debug, Clone, PartialEq)] + #[derive(Debug, Clone, Copy, PartialEq)] pub enum SettlementDirection { ToLpPool, FromLpPool, @@ -154,7 +154,7 @@ pub mod perp_lp_pool_settlement { new_quote_owed: i64, timestamp: i64, ) -> Result<()> { - cache_info.quote_owed_from_lp = new_quote_owed; + cache_info.quote_owed_from_lp_pool = new_quote_owed; cache_info.last_settle_amount = result.amount_transferred; cache_info.last_settle_ts = timestamp; diff --git a/programs/drift/src/state/lp_pool/tests.rs b/programs/drift/src/state/lp_pool/tests.rs index ef75736a51..00257317c8 100644 --- a/programs/drift/src/state/lp_pool/tests.rs +++ b/programs/drift/src/state/lp_pool/tests.rs @@ -1726,13 +1726,14 @@ mod swap_fee_tests { #[cfg(test)] mod settle_tests { use crate::math::lp_pool::perp_lp_pool_settlement::{ - calculate_settlement_amount, SettlementContext, SettlementDirection, + calculate_settlement_amount, update_cache_info, SettlementContext, SettlementDirection, + SettlementResult, }; + use crate::state::perp_market::CacheInfo; use crate::state::spot_market::SpotMarket; fn create_mock_spot_market() -> SpotMarket { - // Create a mock spot market for testing - SpotMarket::default() // You'll need to implement this or use a builder pattern + SpotMarket::default() } #[test] @@ -1878,4 +1879,487 @@ mod settle_tests { assert_eq!(result.fee_pool_used, 300); assert_eq!(result.pnl_pool_used, 0); } + + #[test] + fn test_update_cache_info_to_lp_pool() { + let mut cache = CacheInfo { + quote_owed_from_lp_pool: -400, + last_fee_pool_token_amount: 2_000, + last_net_pnl_pool_token_amount: 500, + last_settle_amount: 0, + last_settle_ts: 0, + ..Default::default() + }; + + let result = SettlementResult { + amount_transferred: 200, + direction: SettlementDirection::ToLpPool, + fee_pool_used: 120, + pnl_pool_used: 80, + }; + let new_quote_owed = cache.quote_owed_from_lp_pool + result.amount_transferred as i64; + let ts = 99; + + update_cache_info(&mut cache, &result, new_quote_owed, ts).unwrap(); + + // quote_owed updated + assert_eq!(cache.quote_owed_from_lp_pool, new_quote_owed); + // settle fields updated + assert_eq!(cache.last_settle_amount, 200); + assert_eq!(cache.last_settle_ts, ts); + // fee pool decreases by fee_pool_used + assert_eq!(cache.last_fee_pool_token_amount, 2_000 - 120); + // pnl pool decreases by pnl_pool_used + assert_eq!(cache.last_net_pnl_pool_token_amount, 500 - 80); + } + + #[test] + fn test_update_cache_info_from_lp_pool() { + let mut cache = CacheInfo { + quote_owed_from_lp_pool: 500, + last_fee_pool_token_amount: 1_000, + last_net_pnl_pool_token_amount: 200, + last_settle_amount: 0, + last_settle_ts: 0, + ..Default::default() + }; + + let result = SettlementResult { + amount_transferred: 150, + direction: SettlementDirection::FromLpPool, + fee_pool_used: 0, + pnl_pool_used: 0, + }; + let new_quote_owed = cache.quote_owed_from_lp_pool - result.amount_transferred as i64; + let ts = 42; + + update_cache_info(&mut cache, &result, new_quote_owed, ts).unwrap(); + + // quote_owed updated + assert_eq!(cache.quote_owed_from_lp_pool, new_quote_owed); + // settle fields updated + assert_eq!(cache.last_settle_amount, 150); + assert_eq!(cache.last_settle_ts, ts); + // fee pool increases by amount_transferred + assert_eq!(cache.last_fee_pool_token_amount, 1_000 + 150); + // pnl pool untouched + assert_eq!(cache.last_net_pnl_pool_token_amount, 200); + } + + #[test] + fn test_large_settlement_amounts() { + // Test with very large amounts to check for overflow + let ctx = SettlementContext { + quote_owed_from_lp: i64::MAX / 2, + quote_constituent_token_balance: u64::MAX / 2, + fee_pool_balance: u128::MAX / 4, + pnl_pool_balance: u128::MAX / 4, + quote_market: &create_mock_spot_market(), + }; + + let result = calculate_settlement_amount(&ctx).unwrap(); + assert_eq!(result.direction, SettlementDirection::FromLpPool); + assert!(result.amount_transferred > 0); + } + + #[test] + fn test_negative_large_settlement_amounts() { + let ctx = SettlementContext { + quote_owed_from_lp: i64::MIN / 2, + quote_constituent_token_balance: u64::MAX / 2, + fee_pool_balance: u128::MAX / 4, + pnl_pool_balance: u128::MAX / 4, + quote_market: &create_mock_spot_market(), + }; + + let result = calculate_settlement_amount(&ctx).unwrap(); + assert_eq!(result.direction, SettlementDirection::ToLpPool); + assert!(result.amount_transferred > 0); + } + + #[test] + fn test_exact_boundary_settlements() { + // Test when quote_owed exactly equals LP balance + let ctx = SettlementContext { + quote_owed_from_lp: 1000, + quote_constituent_token_balance: 1000, + fee_pool_balance: 500, + pnl_pool_balance: 300, + quote_market: &create_mock_spot_market(), + }; + + let result = calculate_settlement_amount(&ctx).unwrap(); + assert_eq!(result.direction, SettlementDirection::FromLpPool); + assert_eq!(result.amount_transferred, 1000); + + // Test when negative quote_owed exactly equals total pool balance + let ctx = SettlementContext { + quote_owed_from_lp: -800, + quote_constituent_token_balance: 2000, + fee_pool_balance: 500, + pnl_pool_balance: 300, + quote_market: &create_mock_spot_market(), + }; + + let result = calculate_settlement_amount(&ctx).unwrap(); + assert_eq!(result.direction, SettlementDirection::ToLpPool); + assert_eq!(result.amount_transferred, 800); + assert_eq!(result.fee_pool_used, 500); + assert_eq!(result.pnl_pool_used, 300); + } + + #[test] + fn test_minimal_settlement_amounts() { + // Test with minimal positive amount + let ctx = SettlementContext { + quote_owed_from_lp: 1, + quote_constituent_token_balance: 1, + fee_pool_balance: 1, + pnl_pool_balance: 1, + quote_market: &create_mock_spot_market(), + }; + + let result = calculate_settlement_amount(&ctx).unwrap(); + assert_eq!(result.direction, SettlementDirection::FromLpPool); + assert_eq!(result.amount_transferred, 1); + + // Test with minimal negative amount + let ctx = SettlementContext { + quote_owed_from_lp: -1, + quote_constituent_token_balance: 1, + fee_pool_balance: 1, + pnl_pool_balance: 0, + quote_market: &create_mock_spot_market(), + }; + + let result = calculate_settlement_amount(&ctx).unwrap(); + assert_eq!(result.direction, SettlementDirection::ToLpPool); + assert_eq!(result.amount_transferred, 1); + assert_eq!(result.fee_pool_used, 1); + assert_eq!(result.pnl_pool_used, 0); + } + + #[test] + fn test_all_zero_balances() { + let ctx = SettlementContext { + quote_owed_from_lp: -500, + quote_constituent_token_balance: 0, + fee_pool_balance: 0, + pnl_pool_balance: 0, + quote_market: &create_mock_spot_market(), + }; + + let result = calculate_settlement_amount(&ctx).unwrap(); + assert_eq!(result.direction, SettlementDirection::ToLpPool); + assert_eq!(result.amount_transferred, 0); + assert_eq!(result.fee_pool_used, 0); + assert_eq!(result.pnl_pool_used, 0); + } + + #[test] + fn test_cache_info_update_none_direction() { + let mut cache = CacheInfo { + quote_owed_from_lp_pool: 100, + last_fee_pool_token_amount: 1000, + last_net_pnl_pool_token_amount: 500, + last_settle_amount: 50, + last_settle_ts: 12345, + ..Default::default() + }; + + let result = SettlementResult { + amount_transferred: 0, + direction: SettlementDirection::None, + fee_pool_used: 0, + pnl_pool_used: 0, + }; + let new_quote_owed = 100; // No change + let ts = 67890; + + update_cache_info(&mut cache, &result, new_quote_owed, ts).unwrap(); + + // quote_owed unchanged + assert_eq!(cache.quote_owed_from_lp_pool, 100); + // settle fields updated with new timestamp but zero amount + assert_eq!(cache.last_settle_amount, 0); + assert_eq!(cache.last_settle_ts, ts); + // pool amounts unchanged + assert_eq!(cache.last_fee_pool_token_amount, 1000); + assert_eq!(cache.last_net_pnl_pool_token_amount, 500); + } + + #[test] + fn test_cache_info_update_maximum_values() { + let mut cache = CacheInfo { + quote_owed_from_lp_pool: i64::MAX / 2, + last_fee_pool_token_amount: u128::MAX / 2, + last_net_pnl_pool_token_amount: i128::MAX / 2, + last_settle_amount: 0, + last_settle_ts: 0, + ..Default::default() + }; + + let result = SettlementResult { + amount_transferred: u64::MAX / 4, + direction: SettlementDirection::FromLpPool, + fee_pool_used: 0, + pnl_pool_used: 0, + }; + let new_quote_owed = cache.quote_owed_from_lp_pool - (result.amount_transferred as i64); + let ts = i64::MAX / 2; + + let update_result = update_cache_info(&mut cache, &result, new_quote_owed, ts); + assert!(update_result.is_ok()); + } + + #[test] + fn test_cache_info_update_minimum_values() { + let mut cache = CacheInfo { + quote_owed_from_lp_pool: i64::MIN / 2, + last_fee_pool_token_amount: 1000, + last_net_pnl_pool_token_amount: i128::MIN / 2, + last_settle_amount: 0, + last_settle_ts: 0, + ..Default::default() + }; + + let result = SettlementResult { + amount_transferred: 500, + direction: SettlementDirection::ToLpPool, + fee_pool_used: 200, + pnl_pool_used: 300, + }; + let new_quote_owed = cache.quote_owed_from_lp_pool + (result.amount_transferred as i64); + let ts = 42; + + let update_result = update_cache_info(&mut cache, &result, new_quote_owed, ts); + assert!(update_result.is_ok()); + } + + #[test] + fn test_sequential_settlement_updates() { + let mut cache = CacheInfo { + quote_owed_from_lp_pool: 1000, + last_fee_pool_token_amount: 5000, + last_net_pnl_pool_token_amount: 3000, + last_settle_amount: 0, + last_settle_ts: 0, + ..Default::default() + }; + + // First settlement: From LP pool + let result1 = SettlementResult { + amount_transferred: 300, + direction: SettlementDirection::FromLpPool, + fee_pool_used: 0, + pnl_pool_used: 0, + }; + let new_quote_owed1 = cache.quote_owed_from_lp_pool - (result1.amount_transferred as i64); + update_cache_info(&mut cache, &result1, new_quote_owed1, 100).unwrap(); + + assert_eq!(cache.quote_owed_from_lp_pool, 700); + assert_eq!(cache.last_fee_pool_token_amount, 5300); + assert_eq!(cache.last_net_pnl_pool_token_amount, 3000); + + // Second settlement: To LP pool + let result2 = SettlementResult { + amount_transferred: 400, + direction: SettlementDirection::ToLpPool, + fee_pool_used: 250, + pnl_pool_used: 150, + }; + let new_quote_owed2 = cache.quote_owed_from_lp_pool + (result2.amount_transferred as i64); + update_cache_info(&mut cache, &result2, new_quote_owed2, 200).unwrap(); + + assert_eq!(cache.quote_owed_from_lp_pool, 1100); + assert_eq!(cache.last_fee_pool_token_amount, 5050); + assert_eq!(cache.last_net_pnl_pool_token_amount, 2850); + assert_eq!(cache.last_settle_ts, 200); + } + + #[test] + fn test_perp_to_lp_with_only_pnl_pool() { + let ctx = SettlementContext { + quote_owed_from_lp: -1000, + quote_constituent_token_balance: 2000, + fee_pool_balance: 0, // No fee pool + pnl_pool_balance: 1200, + quote_market: &create_mock_spot_market(), + }; + + let result = calculate_settlement_amount(&ctx).unwrap(); + assert_eq!(result.direction, SettlementDirection::ToLpPool); + assert_eq!(result.amount_transferred, 1000); + assert_eq!(result.fee_pool_used, 0); + assert_eq!(result.pnl_pool_used, 1000); + } + + #[test] + fn test_perp_to_lp_with_only_fee_pool() { + let ctx = SettlementContext { + quote_owed_from_lp: -800, + quote_constituent_token_balance: 1500, + fee_pool_balance: 1000, + pnl_pool_balance: 0, // No PnL pool + quote_market: &create_mock_spot_market(), + }; + + let result = calculate_settlement_amount(&ctx).unwrap(); + assert_eq!(result.direction, SettlementDirection::ToLpPool); + assert_eq!(result.amount_transferred, 800); + assert_eq!(result.fee_pool_used, 800); + assert_eq!(result.pnl_pool_used, 0); + } + + #[test] + fn test_fractional_settlement_coverage() { + // Test when pools can only partially cover the needed amount + let ctx = SettlementContext { + quote_owed_from_lp: -2000, + quote_constituent_token_balance: 5000, + fee_pool_balance: 300, + pnl_pool_balance: 500, + quote_market: &create_mock_spot_market(), + }; + + let result = calculate_settlement_amount(&ctx).unwrap(); + assert_eq!(result.direction, SettlementDirection::ToLpPool); + assert_eq!(result.amount_transferred, 800); // Only what pools can provide + assert_eq!(result.fee_pool_used, 300); + assert_eq!(result.pnl_pool_used, 500); + } + + #[test] + fn test_settlement_direction_consistency() { + // Positive quote_owed should always result in FromLpPool or None + for quote_owed in [1, 100, 1000, 10000] { + let ctx = SettlementContext { + quote_owed_from_lp: quote_owed, + quote_constituent_token_balance: 500, + fee_pool_balance: 300, + pnl_pool_balance: 200, + quote_market: &create_mock_spot_market(), + }; + + let result = calculate_settlement_amount(&ctx).unwrap(); + assert!( + result.direction == SettlementDirection::FromLpPool + || result.direction == SettlementDirection::None + ); + } + + // Negative quote_owed should always result in ToLpPool or None + for quote_owed in [-1, -100, -1000, -10000] { + let ctx = SettlementContext { + quote_owed_from_lp: quote_owed, + quote_constituent_token_balance: 500, + fee_pool_balance: 300, + pnl_pool_balance: 200, + quote_market: &create_mock_spot_market(), + }; + + let result = calculate_settlement_amount(&ctx).unwrap(); + assert!( + result.direction == SettlementDirection::ToLpPool + || result.direction == SettlementDirection::None + ); + } + } + + #[test] + fn test_cache_info_timestamp_progression() { + let mut cache = CacheInfo::default(); + + let timestamps = [1000, 2000, 3000, 1500, 5000]; // Including out-of-order + + for (_, &ts) in timestamps.iter().enumerate() { + let result = SettlementResult { + amount_transferred: 100, + direction: SettlementDirection::FromLpPool, + fee_pool_used: 0, + pnl_pool_used: 0, + }; + + update_cache_info(&mut cache, &result, 0, ts).unwrap(); + assert_eq!(cache.last_settle_ts, ts); + assert_eq!(cache.last_settle_amount, 100); + } + } + + #[test] + fn test_settlement_amount_conservation() { + // Test that fee_pool_used + pnl_pool_used = amount_transferred for ToLpPool + let test_cases = [ + (-500, 1000, 300, 400), // Normal case + (-1000, 2000, 600, 500), // Uses both pools + (-200, 500, 0, 300), // Only PnL pool + (-150, 400, 200, 0), // Only fee pool + ]; + + for (quote_owed, lp_balance, fee_pool, pnl_pool) in test_cases { + let ctx = SettlementContext { + quote_owed_from_lp: quote_owed, + quote_constituent_token_balance: lp_balance, + fee_pool_balance: fee_pool, + pnl_pool_balance: pnl_pool, + quote_market: &create_mock_spot_market(), + }; + + let result = calculate_settlement_amount(&ctx).unwrap(); + + if result.direction == SettlementDirection::ToLpPool { + assert_eq!( + result.amount_transferred as u128, + result.fee_pool_used + result.pnl_pool_used, + "Amount transferred should equal sum of pool usage for case: {:?}", + (quote_owed, lp_balance, fee_pool, pnl_pool) + ); + } + } + } + + #[test] + fn test_cache_pool_balance_tracking() { + let mut cache = CacheInfo { + last_fee_pool_token_amount: 1000, + last_net_pnl_pool_token_amount: 500, + ..Default::default() + }; + + // Multiple settlements that should maintain balance consistency + let settlements = [ + (SettlementDirection::ToLpPool, 200, 120, 80), // Uses both pools + (SettlementDirection::FromLpPool, 150, 0, 0), // Adds to fee pool + (SettlementDirection::ToLpPool, 100, 100, 0), // Uses only fee pool + (SettlementDirection::ToLpPool, 50, 30, 20), // Uses both pools again + ]; + + let mut expected_fee_pool = cache.last_fee_pool_token_amount; + let mut expected_pnl_pool = cache.last_net_pnl_pool_token_amount; + + for (direction, amount, fee_used, pnl_used) in settlements { + let result = SettlementResult { + amount_transferred: amount, + direction, + fee_pool_used: fee_used, + pnl_pool_used: pnl_used, + }; + + match direction { + SettlementDirection::FromLpPool => { + expected_fee_pool += amount as u128; + } + SettlementDirection::ToLpPool => { + expected_fee_pool -= fee_used; + expected_pnl_pool -= pnl_used as i128; + } + SettlementDirection::None => {} + } + + update_cache_info(&mut cache, &result, 0, 1000).unwrap(); + + assert_eq!(cache.last_fee_pool_token_amount, expected_fee_pool); + assert_eq!(cache.last_net_pnl_pool_token_amount, expected_pnl_pool); + } + } } diff --git a/programs/drift/src/state/perp_market.rs b/programs/drift/src/state/perp_market.rs index 8f6d186ee0..b061e8a7af 100644 --- a/programs/drift/src/state/perp_market.rs +++ b/programs/drift/src/state/perp_market.rs @@ -1736,13 +1736,13 @@ pub struct CacheInfo { pub last_oracle_price_twap: i64, pub last_settle_amount: u64, pub last_settle_ts: i64, - pub quote_owed_from_lp: i64, + pub quote_owed_from_lp_pool: i64, pub oracle_price: i64, pub oracle_confidence: u64, pub oracle_delay: i64, pub oracle_slot: u64, pub oracle_source: u8, - _padding: [u8; 7], + pub _padding: [u8; 7], pub oracle: Pubkey, } @@ -1768,7 +1768,7 @@ impl Default for CacheInfo { last_settle_amount: 0u64, last_settle_ts: 0i64, oracle_source: 0u8, - quote_owed_from_lp: 0i64, + quote_owed_from_lp_pool: 0i64, } } } @@ -1911,12 +1911,12 @@ impl<'a> AccountZeroCopyMut<'a, CacheInfo, AmmCacheFixed> { .cast::()?; if amm_amount_available < cached_info.get_last_available_amm_balance()? { - cached_info.quote_owed_from_lp = cached_info - .quote_owed_from_lp + cached_info.quote_owed_from_lp_pool = cached_info + .quote_owed_from_lp_pool .safe_add(amount_to_send.cast::()?)?; } else { - cached_info.quote_owed_from_lp = cached_info - .quote_owed_from_lp + cached_info.quote_owed_from_lp_pool = cached_info + .quote_owed_from_lp_pool .safe_sub(amount_to_send.cast::()?)?; } diff --git a/sdk/src/idl/drift.json b/sdk/src/idl/drift.json index 2b637098df..8cb17db9eb 100644 --- a/sdk/src/idl/drift.json +++ b/sdk/src/idl/drift.json @@ -13079,7 +13079,7 @@ "type": "i64" }, { - "name": "quoteOwedFromLp", + "name": "quoteOwedFromLpPool", "type": "i64" }, { diff --git a/sdk/src/types.ts b/sdk/src/types.ts index dbd1248700..39e7b59a6c 100644 --- a/sdk/src/types.ts +++ b/sdk/src/types.ts @@ -1709,7 +1709,7 @@ export type CacheInfo = { lastNetPnlPoolTokenAmount: BN; lastSettleAmount: BN; lastSettleTs: BN; - quoteOwedFromLp: BN; + quoteOwedFromLpPool: BN; }; export type AmmCache = { diff --git a/tests/lpPool.ts b/tests/lpPool.ts index 8053bd98d6..6012cc4a65 100644 --- a/tests/lpPool.ts +++ b/tests/lpPool.ts @@ -881,15 +881,15 @@ describe('LP Pool', () => { assert(ammCache.cache[0].lastFeePoolTokenAmount.eq(new BN(100000000))); assert(ammCache.cache[1].lastFeePoolTokenAmount.eq(new BN(100000000))); assert( - ammCache.cache[0].quoteOwedFromLp.eq( - ammCacheBeforeAdjust.cache[0].quoteOwedFromLp.sub( + ammCache.cache[0].quoteOwedFromLpPool.eq( + ammCacheBeforeAdjust.cache[0].quoteOwedFromLpPool.sub( new BN(100).mul(QUOTE_PRECISION) ) ) ); assert( - ammCache.cache[1].quoteOwedFromLp.eq( - ammCacheBeforeAdjust.cache[1].quoteOwedFromLp.sub( + ammCache.cache[1].quoteOwedFromLpPool.eq( + ammCacheBeforeAdjust.cache[1].quoteOwedFromLpPool.sub( new BN(100).mul(QUOTE_PRECISION) ) ) @@ -934,11 +934,11 @@ describe('LP Pool', () => { // Expected transfers per pool are capital constrained by the actual balances const expectedTransfer0 = BN.min( - ammCache.cache[0].quoteOwedFromLp.muln(-1), + ammCache.cache[0].quoteOwedFromLpPool.muln(-1), pnlPoolBalance0.add(feePoolBalance0) ); const expectedTransfer1 = BN.min( - ammCache.cache[1].quoteOwedFromLp.muln(-1), + ammCache.cache[1].quoteOwedFromLpPool.muln(-1), pnlPoolBalance1.add(feePoolBalance1) ); const expectedTransferAmount = expectedTransfer0.add(expectedTransfer1); @@ -1107,7 +1107,7 @@ describe('LP Pool', () => { ) ); assert( - ammCache.cache[0].quoteOwedFromLp.eq( + ammCache.cache[0].quoteOwedFromLpPool.eq( expectedTransferAmount.sub( new BN(constituentUSDCBalanceBefore.toString()) ) @@ -1135,7 +1135,7 @@ describe('LP Pool', () => { let ammCache = (await adminClient.program.account.ammCache.fetch( getAmmCachePublicKey(program.programId) )) as AmmCache; - const owedAmount = ammCache.cache[0].quoteOwedFromLp; + const owedAmount = ammCache.cache[0].quoteOwedFromLpPool; // Give the perp market half of its owed amount const perpMarket = adminClient.getPerpMarketAccount(0); @@ -1173,7 +1173,7 @@ describe('LP Pool', () => { lpPoolKey )) as LPPoolAccount; - assert(ammCache.cache[0].quoteOwedFromLp.eq(owedAmount.divn(2))); + assert(ammCache.cache[0].quoteOwedFromLpPool.eq(owedAmount.divn(2))); assert(constituent.tokenBalance.eq(ZERO)); assert(lpPool.lastAum.eq(ZERO)); @@ -1218,7 +1218,7 @@ describe('LP Pool', () => { getAmmCachePublicKey(program.programId) )) as AmmCache; for (let i = 0; i <= ammCache.cache.length - 1; i++) { - aum = aum.sub(ammCache.cache[i].quoteOwedFromLp); + aum = aum.sub(ammCache.cache[i].quoteOwedFromLpPool); } assert(lpPool.lastAum.eq(aum)); }); @@ -1237,7 +1237,7 @@ describe('LP Pool', () => { )) as AmmCache; const balanceBefore = constituent.tokenBalance; - const owedAmount = ammCache.cache[0].quoteOwedFromLp; + const owedAmount = ammCache.cache[0].quoteOwedFromLpPool; // Give the perp market half of its owed amount const perpMarket = adminClient.getPerpMarketAccount(0); @@ -1276,7 +1276,7 @@ describe('LP Pool', () => { lpPoolKey )) as LPPoolAccount; - assert(ammCache.cache[0].quoteOwedFromLp.eq(ZERO)); + assert(ammCache.cache[0].quoteOwedFromLpPool.eq(ZERO)); assert(constituent.tokenBalance.eq(balanceBefore.add(owedAmount))); assert(lpPool.lastAum.eq(aumBefore.add(owedAmount.muln(2)))); });