From 4eb81a5cec3dfed60d3e0a7b201b5edcaa0b19d0 Mon Sep 17 00:00:00 2001 From: Nour Alharithi Date: Mon, 25 Aug 2025 10:22:16 -0700 Subject: [PATCH] add price for lp validates --- programs/drift/src/instructions/lp_pool.rs | 58 ++++++++++++++-------- programs/drift/src/state/lp_pool.rs | 14 +++--- 2 files changed, 43 insertions(+), 29 deletions(-) diff --git a/programs/drift/src/instructions/lp_pool.rs b/programs/drift/src/instructions/lp_pool.rs index 8b26323d9e..4ce8416c4a 100644 --- a/programs/drift/src/instructions/lp_pool.rs +++ b/programs/drift/src/instructions/lp_pool.rs @@ -2,7 +2,7 @@ use anchor_lang::{prelude::*, Accounts, Key, Result}; use anchor_spl::token_interface::{Mint, TokenAccount, TokenInterface}; use crate::ids::DLP_WHITELIST; -use crate::math::constants::PRICE_PRECISION_I64; +use crate::math::constants::{PERCENTAGE_PRECISION, PRICE_PRECISION_I64}; use crate::{ controller::{ self, @@ -15,7 +15,7 @@ use crate::{ math::{ self, casting::Cast, - constants::{PERCENTAGE_PRECISION_I64, PRICE_PRECISION}, + constants::PERCENTAGE_PRECISION_I64, oracle::{is_oracle_valid_for_action, oracle_validity, DriftAction, LogMode}, safe_math::SafeMath, }, @@ -626,6 +626,8 @@ pub fn handle_lp_pool_add_liquidity<'c: 'info, 'info>( let state = &ctx.accounts.state; let mut lp_pool = ctx.accounts.lp_pool.load_mut()?; + let lp_price_before = lp_pool.get_price(ctx.accounts.lp_mint.supply)?; + if slot.saturating_sub(lp_pool.last_aum_slot) > LP_POOL_SWAP_AUM_UPDATE_DELAY { msg!( "Must update LP pool AUM before swap, last_aum_slot: {}, current slot: {}", @@ -776,15 +778,20 @@ pub fn handle_lp_pool_add_liquidity<'c: 'info, 'info>( in_constituent.sync_token_balance(ctx.accounts.constituent_in_token_account.amount); - let dlp_total_supply = ctx.accounts.lp_mint.supply; - let lp_price = if dlp_total_supply > 0 { - lp_pool - .last_aum - .safe_mul(PRICE_PRECISION)? - .safe_div(dlp_total_supply as u128)? - } else { - 0 - }; + ctx.accounts.lp_mint.reload()?; + let lp_price_after = lp_pool.get_price(ctx.accounts.lp_mint.supply)?; + if lp_price_before != 0 { + let price_diff_percent = lp_price_after + .abs_diff(lp_price_before) + .safe_mul(PERCENTAGE_PRECISION)? + .safe_div(lp_price_before)?; + + validate!( + price_diff_percent <= PERCENTAGE_PRECISION / 5, + ErrorCode::LpInvariantFailed, + "Removing liquidity resulted in DLP token difference of > 5%" + )?; + } let mint_redeem_id = get_then_update_id!(lp_pool, next_mint_redeem_id); emit_stack::<_, { LPMintRedeemRecord::SIZE }>(LPMintRedeemRecord { @@ -800,7 +807,7 @@ pub fn handle_lp_pool_add_liquidity<'c: 'info, 'info>( mint: in_constituent.mint, lp_amount, lp_fee: lp_fee_amount, - lp_price, + lp_price: lp_price_after, mint_redeem_id, last_aum: lp_pool.last_aum, last_aum_slot: lp_pool.last_aum_slot, @@ -923,6 +930,8 @@ pub fn handle_lp_pool_remove_liquidity<'c: 'info, 'info>( let state = &ctx.accounts.state; let mut lp_pool = ctx.accounts.lp_pool.load_mut()?; + let lp_price_before = lp_pool.get_price(ctx.accounts.lp_mint.supply)?; + // Verify previous settle let amm_cache: AccountZeroCopy<'_, CacheInfo, _> = ctx.accounts.amm_cache.load_zc()?; for (i, _) in amm_cache.iter().enumerate() { @@ -1088,15 +1097,20 @@ pub fn handle_lp_pool_remove_liquidity<'c: 'info, 'info>( out_constituent.sync_token_balance(ctx.accounts.constituent_out_token_account.amount); - let dlp_total_supply = ctx.accounts.lp_mint.supply; - let lp_price = if dlp_total_supply > 0 { - lp_pool - .last_aum - .safe_mul(PRICE_PRECISION)? - .safe_div(dlp_total_supply as u128)? - } else { - 0 - }; + ctx.accounts.lp_mint.reload()?; + let lp_price_after = lp_pool.get_price(ctx.accounts.lp_mint.supply)?; + + if lp_price_after != 0 { + let price_diff_percent = lp_price_after + .abs_diff(lp_price_before) + .safe_mul(PERCENTAGE_PRECISION)? + .safe_div(lp_price_before)?; + validate!( + price_diff_percent <= PERCENTAGE_PRECISION / 5, + ErrorCode::LpInvariantFailed, + "Removing liquidity resulted in DLP token difference of > 5%" + )?; + } let mint_redeem_id = get_then_update_id!(lp_pool, next_mint_redeem_id); emit_stack::<_, { LPMintRedeemRecord::SIZE }>(LPMintRedeemRecord { @@ -1112,7 +1126,7 @@ pub fn handle_lp_pool_remove_liquidity<'c: 'info, 'info>( mint: out_constituent.mint, lp_amount: lp_burn_amount, lp_fee: lp_fee_amount, - lp_price, + lp_price: lp_price_after, mint_redeem_id, last_aum: lp_pool.last_aum, last_aum_slot: lp_pool.last_aum_slot, diff --git a/programs/drift/src/state/lp_pool.rs b/programs/drift/src/state/lp_pool.rs index f809422be1..c484f5e293 100644 --- a/programs/drift/src/state/lp_pool.rs +++ b/programs/drift/src/state/lp_pool.rs @@ -4,7 +4,7 @@ use crate::error::{DriftResult, ErrorCode}; use crate::math::casting::Cast; use crate::math::constants::{ BASE_PRECISION_I128, PERCENTAGE_PRECISION, PERCENTAGE_PRECISION_I128, PERCENTAGE_PRECISION_I64, - PERCENTAGE_PRECISION_U64, PRICE_PRECISION_I128, QUOTE_PRECISION_I128, + PERCENTAGE_PRECISION_U64, PRICE_PRECISION, PRICE_PRECISION_I128, QUOTE_PRECISION_I128, }; use crate::math::safe_math::SafeMath; use crate::math::spot_balance::get_token_amount; @@ -12,7 +12,6 @@ use crate::state::constituent_map::ConstituentMap; use crate::state::perp_market::{AmmCacheFixed, CacheInfo}; use crate::state::spot_market_map::SpotMarketMap; use anchor_lang::prelude::*; -use anchor_spl::token::Mint; use borsh::{BorshDeserialize, BorshSerialize}; use super::oracle::OraclePriceData; @@ -119,14 +118,15 @@ impl Size for LPPool { } impl LPPool { - pub fn get_price(&self, mint: &Mint) -> Result { - match mint.supply { + pub fn get_price(&self, mint_supply: u64) -> Result { + match mint_supply { 0 => Ok(0), supply => { // TODO: assuming mint decimals = quote decimals = 6 - (supply as u128) - .checked_div(self.last_aum) - .ok_or(ErrorCode::MathError.into()) + Ok(self + .last_aum + .safe_mul(PRICE_PRECISION)? + .safe_div(supply as u128)?) } } }