diff --git a/programs/drift/src/controller/amm.rs b/programs/drift/src/controller/amm.rs index e2ba43d736..558d0a9e36 100644 --- a/programs/drift/src/controller/amm.rs +++ b/programs/drift/src/controller/amm.rs @@ -421,8 +421,7 @@ pub fn formulaic_update_k( let new_sqrt_k = bn::U192::from(market.amm.sqrt_k) .safe_mul(bn::U192::from(k_scale_numerator))? - .safe_div(bn::U192::from(k_scale_denominator))? - .max(bn::U192::from(market.amm.user_lp_shares.safe_add(1)?)); + .safe_div(bn::U192::from(k_scale_denominator))?; let update_k_result = get_update_k_result(market, new_sqrt_k, true)?; diff --git a/programs/drift/src/controller/amm/tests.rs b/programs/drift/src/controller/amm/tests.rs index c62e2959d5..a2a33fd6d5 100644 --- a/programs/drift/src/controller/amm/tests.rs +++ b/programs/drift/src/controller/amm/tests.rs @@ -255,62 +255,6 @@ fn iterative_no_bounds_formualic_k_tests() { assert_eq!(market.amm.total_fee_minus_distributions, 985625029); } -#[test] -fn decrease_k_up_to_user_lp_shares() { - let mut market = PerpMarket { - amm: AMM { - base_asset_reserve: 512295081967, - quote_asset_reserve: 488 * AMM_RESERVE_PRECISION, - sqrt_k: 500 * AMM_RESERVE_PRECISION, - user_lp_shares: 150 * AMM_RESERVE_PRECISION, - peg_multiplier: 50000000, - concentration_coef: MAX_CONCENTRATION_COEFFICIENT, - base_asset_amount_with_amm: -12295081967, - total_fee_minus_distributions: -100 * QUOTE_PRECISION as i128, - total_fee_withdrawn: 100 * QUOTE_PRECISION, - curve_update_intensity: 100, - ..AMM::default() - }, - ..PerpMarket::default() - }; - // let prev_sqrt_k = market.amm.sqrt_k; - let (new_terminal_quote_reserve, new_terminal_base_reserve) = - amm::calculate_terminal_reserves(&market.amm).unwrap(); - market.amm.terminal_quote_asset_reserve = new_terminal_quote_reserve; - let (min_base_asset_reserve, max_base_asset_reserve) = - amm::calculate_bid_ask_bounds(market.amm.concentration_coef, new_terminal_base_reserve) - .unwrap(); - market.amm.min_base_asset_reserve = min_base_asset_reserve; - market.amm.max_base_asset_reserve = max_base_asset_reserve; - - // let reserve_price = market.amm.reserve_price().unwrap(); - let now = 10000; - let oracle_price_data = OraclePriceData { - price: 50 * PRICE_PRECISION_I64, - confidence: 0, - delay: 2, - has_sufficient_number_of_data_points: true, - }; - - // negative funding cost - let mut count = 0; - let mut prev_k = market.amm.sqrt_k; - let mut new_k = 0; - while prev_k != new_k && count < 100000 { - let funding_cost = (QUOTE_PRECISION * 100000) as i128; - prev_k = market.amm.sqrt_k; - formulaic_update_k(&mut market, &oracle_price_data, funding_cost, now).unwrap(); - new_k = market.amm.sqrt_k; - msg!("quote_asset_reserve:{}", market.amm.quote_asset_reserve); - msg!("new_k:{}", new_k); - count += 1 - } - - assert_eq!(market.amm.base_asset_amount_with_amm, -12295081967); - assert_eq!(market.amm.sqrt_k, 162234889619); - assert_eq!(market.amm.total_fee_minus_distributions, 29796232175); -} - #[test] fn update_pool_balances_test_high_util_borrow() { let mut market = PerpMarket { diff --git a/programs/drift/src/controller/liquidation.rs b/programs/drift/src/controller/liquidation.rs index 057f4b8168..92d241dbc7 100644 --- a/programs/drift/src/controller/liquidation.rs +++ b/programs/drift/src/controller/liquidation.rs @@ -5,7 +5,6 @@ use anchor_lang::prelude::*; use crate::controller::amm::get_fee_pool_tokens; use crate::controller::funding::settle_funding_payment; -use crate::controller::lp::burn_lp_shares; use crate::controller::orders; use crate::controller::orders::{cancel_order, fill_perp_order, place_perp_order}; use crate::controller::position::{ @@ -181,8 +180,7 @@ pub fn liquidate_perp( let position_index = get_position_index(&user.perp_positions, market_index)?; validate!( user.perp_positions[position_index].is_open_position() - || user.perp_positions[position_index].has_open_order() - || user.perp_positions[position_index].is_lp(), + || user.perp_positions[position_index].has_open_order(), ErrorCode::PositionDoesntHaveOpenPositionOrOrders )?; @@ -227,27 +225,7 @@ pub fn liquidate_perp( drop(market); // burning lp shares = removing open bids/asks - let lp_shares = user.perp_positions[position_index].lp_shares; - if lp_shares > 0 { - let (position_delta, pnl) = burn_lp_shares( - &mut user.perp_positions[position_index], - perp_market_map.get_ref_mut(&market_index)?.deref_mut(), - lp_shares, - oracle_price, - )?; - - // emit LP record for shares removed - emit_stack::<_, { LPRecord::SIZE }>(LPRecord { - ts: now, - action: LPAction::RemoveLiquidity, - user: *user_key, - n_shares: lp_shares, - market_index, - delta_base_asset_amount: position_delta.base_asset_amount, - delta_quote_asset_amount: position_delta.quote_asset_amount, - pnl, - })?; - } + let lp_shares = 0; // check if user exited liquidation territory let intermediate_margin_calculation = if !canceled_order_ids.is_empty() || lp_shares > 0 { @@ -832,8 +810,7 @@ pub fn liquidate_perp_with_fill( let position_index = get_position_index(&user.perp_positions, market_index)?; validate!( user.perp_positions[position_index].is_open_position() - || user.perp_positions[position_index].has_open_order() - || user.perp_positions[position_index].is_lp(), + || user.perp_positions[position_index].has_open_order(), ErrorCode::PositionDoesntHaveOpenPositionOrOrders )?; @@ -878,27 +855,7 @@ pub fn liquidate_perp_with_fill( drop(market); // burning lp shares = removing open bids/asks - let lp_shares = user.perp_positions[position_index].lp_shares; - if lp_shares > 0 { - let (position_delta, pnl) = burn_lp_shares( - &mut user.perp_positions[position_index], - perp_market_map.get_ref_mut(&market_index)?.deref_mut(), - lp_shares, - oracle_price, - )?; - - // emit LP record for shares removed - emit_stack::<_, { LPRecord::SIZE }>(LPRecord { - ts: now, - action: LPAction::RemoveLiquidity, - user: *user_key, - n_shares: lp_shares, - market_index, - delta_base_asset_amount: position_delta.base_asset_amount, - delta_quote_asset_amount: position_delta.quote_asset_amount, - pnl, - })?; - } + let lp_shares = 0; // check if user exited liquidation territory let intermediate_margin_calculation = if !canceled_order_ids.is_empty() || lp_shares > 0 { @@ -2448,12 +2405,6 @@ pub fn liquidate_borrow_for_perp_pnl( base_asset_amount )?; - validate!( - !user_position.is_lp(), - ErrorCode::InvalidPerpPositionToLiquidate, - "user is an lp. must call liquidate_perp first" - )?; - let pnl = user_position.quote_asset_amount.cast::()?; validate!( @@ -2983,12 +2934,6 @@ pub fn liquidate_perp_pnl_for_deposit( base_asset_amount )?; - validate!( - !user_position.is_lp(), - ErrorCode::InvalidPerpPositionToLiquidate, - "user is an lp. must call liquidate_perp first" - )?; - let unsettled_pnl = user_position.quote_asset_amount.cast::()?; validate!( diff --git a/programs/drift/src/controller/lp/tests.rs b/programs/drift/src/controller/lp/tests.rs deleted file mode 100644 index 2ab426bc76..0000000000 --- a/programs/drift/src/controller/lp/tests.rs +++ /dev/null @@ -1,1818 +0,0 @@ -use crate::controller::lp::*; -use crate::controller::pnl::settle_pnl; -use crate::state::perp_market::AMM; -use crate::state::user::PerpPosition; -use crate::PRICE_PRECISION; -use crate::{SettlePnlMode, BASE_PRECISION_I64}; -use std::str::FromStr; - -use anchor_lang::Owner; -use solana_program::pubkey::Pubkey; - -use crate::create_account_info; -use crate::create_anchor_account_info; -use crate::math::casting::Cast; -use crate::math::constants::{ - AMM_RESERVE_PRECISION, BASE_PRECISION_I128, BASE_PRECISION_U64, LIQUIDATION_FEE_PRECISION, - PEG_PRECISION, QUOTE_PRECISION_I128, QUOTE_SPOT_MARKET_INDEX, SPOT_BALANCE_PRECISION, - SPOT_BALANCE_PRECISION_U64, SPOT_CUMULATIVE_INTEREST_PRECISION, SPOT_WEIGHT_PRECISION, -}; -use crate::math::margin::{ - calculate_margin_requirement_and_total_collateral_and_liability_info, - calculate_perp_position_value_and_pnl, meets_maintenance_margin_requirement, - MarginRequirementType, -}; -use crate::math::position::{ - // get_new_position_amounts, - get_position_update_type, - PositionUpdateType, -}; -use crate::state::margin_calculation::{MarginCalculation, MarginContext}; -use crate::state::oracle::{HistoricalOracleData, OracleSource}; -use crate::state::oracle::{OraclePriceData, StrictOraclePrice}; -use crate::state::oracle_map::OracleMap; -use crate::state::perp_market::{MarketStatus, PerpMarket, PoolBalance}; -use crate::state::perp_market_map::PerpMarketMap; -use crate::state::spot_market::{SpotBalanceType, SpotMarket}; -use crate::state::spot_market_map::SpotMarketMap; -use crate::state::state::{OracleGuardRails, State, ValidityGuardRails}; -use crate::state::user::{SpotPosition, User}; -use crate::test_utils::*; -use crate::test_utils::{get_positions, get_pyth_price, get_spot_positions}; -use anchor_lang::prelude::Clock; - -#[test] -fn test_lp_wont_collect_improper_funding() { - let mut position = PerpPosition { - base_asset_amount: 1, - ..PerpPosition::default() - }; - - let amm = AMM { - order_step_size: 1, - ..AMM::default_test() - }; - let mut market = PerpMarket { - amm, - ..PerpMarket::default_test() - }; - mint_lp_shares(&mut position, &mut market, BASE_PRECISION_U64).unwrap(); - - market.amm.base_asset_amount_per_lp = 10; - market.amm.quote_asset_amount_per_lp = -10; - market.amm.base_asset_amount_with_unsettled_lp = -10; - market.amm.base_asset_amount_short = -10; - market.amm.cumulative_funding_rate_long = -10; - market.amm.cumulative_funding_rate_long = -10; - - let result = settle_lp_position(&mut position, &mut market); - assert_eq!(result, Err(ErrorCode::InvalidPerpPositionDetected)); -} - -#[test] -fn test_full_long_settle() { - let mut position = PerpPosition { - ..PerpPosition::default() - }; - - let amm = AMM { - order_step_size: 1, - ..AMM::default_test() - }; - let mut market = PerpMarket { - amm, - ..PerpMarket::default_test() - }; - let og_market = market; - - mint_lp_shares(&mut position, &mut market, BASE_PRECISION_U64).unwrap(); - - market.amm.base_asset_amount_per_lp = 10; - market.amm.quote_asset_amount_per_lp = -10; - market.amm.base_asset_amount_with_unsettled_lp = -10; - market.amm.base_asset_amount_short = -10; - - settle_lp_position(&mut position, &mut market).unwrap(); - - assert_eq!(position.last_base_asset_amount_per_lp, 10); - assert_eq!(position.last_quote_asset_amount_per_lp, -10); - assert_eq!(position.base_asset_amount, 10); - assert_eq!(position.quote_asset_amount, -10); - assert_eq!(market.amm.base_asset_amount_with_unsettled_lp, 0); - // net baa doesnt change - assert_eq!( - og_market.amm.base_asset_amount_with_amm, - market.amm.base_asset_amount_with_amm - ); - - // burn - let lp_shares = position.lp_shares; - burn_lp_shares(&mut position, &mut market, lp_shares, 0).unwrap(); - assert_eq!(position.lp_shares, 0); - assert_eq!(og_market.amm.sqrt_k, market.amm.sqrt_k); -} - -#[test] -fn test_full_short_settle() { - let mut position = PerpPosition { - ..PerpPosition::default() - }; - - let amm = AMM { - peg_multiplier: 1, - user_lp_shares: 100 * AMM_RESERVE_PRECISION, - order_step_size: 1, - ..AMM::default_test() - }; - - let mut market = PerpMarket { - amm, - ..PerpMarket::default_test() - }; - - mint_lp_shares(&mut position, &mut market, 100 * BASE_PRECISION_U64).unwrap(); - - market.amm.base_asset_amount_per_lp = -10; - market.amm.quote_asset_amount_per_lp = 10; - - settle_lp_position(&mut position, &mut market).unwrap(); - - assert_eq!(position.last_base_asset_amount_per_lp, -10); - assert_eq!(position.last_quote_asset_amount_per_lp, 10); - assert_eq!(position.base_asset_amount, -10 * 100); - assert_eq!(position.quote_asset_amount, 10 * 100); -} - -#[test] -fn test_partial_short_settle() { - let mut position = PerpPosition { - ..PerpPosition::default() - }; - - let amm = AMM { - order_step_size: 3, - ..AMM::default_test() - }; - - let mut market = PerpMarket { - amm, - ..PerpMarket::default_test() - }; - - mint_lp_shares(&mut position, &mut market, BASE_PRECISION_U64).unwrap(); - - market.amm.base_asset_amount_per_lp = -10; - market.amm.quote_asset_amount_per_lp = 10; - - market.amm.base_asset_amount_with_unsettled_lp = 9; - market.amm.base_asset_amount_long = 9; - - settle_lp_position(&mut position, &mut market).unwrap(); - - assert_eq!(position.base_asset_amount, -9); - assert_eq!(position.quote_asset_amount, 10); - assert_eq!(position.remainder_base_asset_amount, -1); - assert_eq!(position.last_base_asset_amount_per_lp, -10); - assert_eq!(position.last_quote_asset_amount_per_lp, 10); - - // burn - let _position = position; - let lp_shares = position.lp_shares; - burn_lp_shares(&mut position, &mut market, lp_shares, 0).unwrap(); - assert_eq!(position.lp_shares, 0); -} - -#[test] -fn test_partial_long_settle() { - let mut position = PerpPosition { - lp_shares: BASE_PRECISION_U64, - ..PerpPosition::default() - }; - - let amm = AMM { - base_asset_amount_per_lp: -10, - quote_asset_amount_per_lp: 10, - order_step_size: 3, - ..AMM::default_test() - }; - - let mut market = PerpMarket { - amm, - ..PerpMarket::default_test() - }; - - settle_lp_position(&mut position, &mut market).unwrap(); - - assert_eq!(position.base_asset_amount, -9); - assert_eq!(position.quote_asset_amount, 10); - assert_eq!(position.remainder_base_asset_amount, -1); - assert_eq!(position.last_base_asset_amount_per_lp, -10); - assert_eq!(position.last_quote_asset_amount_per_lp, 10); -} - -#[test] -fn test_remainder_long_settle_too_large_order_step_size() { - let mut position = PerpPosition { - ..PerpPosition::default() - }; - - let amm = AMM { - order_step_size: 5 * BASE_PRECISION_U64, - ..AMM::default_test() - }; - let mut market = PerpMarket { - amm, - ..PerpMarket::default_test() - }; - let og_market = market; - - mint_lp_shares(&mut position, &mut market, BASE_PRECISION_U64).unwrap(); - - market.amm.base_asset_amount_per_lp = 10; - market.amm.quote_asset_amount_per_lp = -10; - market.amm.base_asset_amount_with_unsettled_lp = -10; - market.amm.base_asset_amount_with_amm = 10; - - settle_lp_position(&mut position, &mut market).unwrap(); - - assert_eq!(position.last_base_asset_amount_per_lp, 10); - assert_eq!(position.last_quote_asset_amount_per_lp, -10); - assert_eq!(position.base_asset_amount, 0); - assert_eq!(position.quote_asset_amount, -10); - assert_eq!(position.remainder_base_asset_amount, 10); - assert_eq!(market.amm.base_asset_amount_with_unsettled_lp, -10); - // net baa doesnt change after settle_lp_position - assert_eq!(market.amm.base_asset_amount_with_amm, 10); - - // burn - let lp_shares = position.lp_shares; - assert_eq!(lp_shares, BASE_PRECISION_U64); - burn_lp_shares(&mut position, &mut market, lp_shares, 22).unwrap(); - assert_eq!(position.lp_shares, 0); - assert_eq!(og_market.amm.sqrt_k, market.amm.sqrt_k); - assert_eq!(position.quote_asset_amount, -11); - assert_eq!(position.base_asset_amount, 0); - assert_eq!(position.remainder_base_asset_amount, 0); -} - -#[test] -fn test_remainder_overflows_too_large_order_step_size() { - let mut position = PerpPosition { - ..PerpPosition::default() - }; - - let amm = AMM { - order_step_size: 5 * BASE_PRECISION_U64, - ..AMM::default_test() - }; - let mut market = PerpMarket { - amm, - ..PerpMarket::default_test() - }; - let og_market = market; - - mint_lp_shares(&mut position, &mut market, BASE_PRECISION_U64).unwrap(); - - market.amm.base_asset_amount_per_lp = 10; - market.amm.quote_asset_amount_per_lp = -10; - market.amm.base_asset_amount_with_unsettled_lp = -10; - market.amm.base_asset_amount_with_amm = 10; - market.amm.base_asset_amount_short = 0; - - settle_lp_position(&mut position, &mut market).unwrap(); - - assert_eq!(position.last_base_asset_amount_per_lp, 10); - assert_eq!(position.last_quote_asset_amount_per_lp, -10); - assert_eq!(position.base_asset_amount, 0); - assert_eq!(position.quote_asset_amount, -10); - assert_eq!(position.remainder_base_asset_amount, 10); - assert_eq!(market.amm.base_asset_amount_with_unsettled_lp, -10); - // net baa doesnt change after settle_lp_position - assert_eq!(market.amm.base_asset_amount_with_amm, 10); - - market.amm.base_asset_amount_per_lp += BASE_PRECISION_I128 + 1; - market.amm.quote_asset_amount_per_lp += -16900000000; - market.amm.base_asset_amount_with_unsettled_lp += -(BASE_PRECISION_I128 + 1); - // market.amm.base_asset_amount_short ; - market.amm.base_asset_amount_with_amm += BASE_PRECISION_I128 + 1; - - settle_lp_position(&mut position, &mut market).unwrap(); - - assert_eq!(position.last_base_asset_amount_per_lp, 1000000011); - assert_eq!(position.last_quote_asset_amount_per_lp, -16900000010); - assert_eq!(position.quote_asset_amount, -16900000010); - assert_eq!(position.base_asset_amount, 0); - assert_eq!(position.remainder_base_asset_amount, 1000000011); - assert_eq!( - (position.remainder_base_asset_amount as u64) < market.amm.order_step_size, - true - ); - - // might break i32 limit - market.amm.base_asset_amount_per_lp = 3 * BASE_PRECISION_I128 + 1; - market.amm.quote_asset_amount_per_lp = -(3 * 16900000000); - market.amm.base_asset_amount_with_unsettled_lp = -(3 * BASE_PRECISION_I128 + 1); - market.amm.base_asset_amount_short = -(3 * BASE_PRECISION_I128 + 1); - - // not allowed to settle when remainder is above i32 but below order size - assert!(settle_lp_position(&mut position, &mut market).is_err()); - - // assert_eq!(position.last_base_asset_amount_per_lp, 1000000001); - // assert_eq!(position.last_quote_asset_amount_per_lp, -16900000000); - assert_eq!(position.quote_asset_amount, -16900000010); - assert_eq!(position.base_asset_amount, 0); - // assert_eq!(position.remainder_base_asset_amount, 1000000001); - assert_eq!( - (position.remainder_base_asset_amount as u64) < market.amm.order_step_size, - true - ); - - // past order_step_size on market - market.amm.base_asset_amount_per_lp = 5 * BASE_PRECISION_I128 + 1; - market.amm.quote_asset_amount_per_lp = -116900000000; - market.amm.base_asset_amount_with_unsettled_lp = -(5 * BASE_PRECISION_I128 + 1); - market.amm.base_asset_amount_short = -(5 * BASE_PRECISION_I128); - market.amm.base_asset_amount_with_amm = 1; - - settle_lp_position(&mut position, &mut market).unwrap(); - assert_eq!(market.amm.base_asset_amount_with_unsettled_lp, -1); - assert_eq!(market.amm.base_asset_amount_short, -5000000000); - assert_eq!(market.amm.base_asset_amount_long, 5 * BASE_PRECISION_I128); - assert_eq!(market.amm.base_asset_amount_with_amm, 1); - - assert_eq!(position.last_base_asset_amount_per_lp, 5000000001); - assert_eq!(position.last_quote_asset_amount_per_lp, -116900000000); - assert_eq!(position.quote_asset_amount, -116900000000); - assert_eq!(position.base_asset_amount, 5000000000); - assert_eq!(position.remainder_base_asset_amount, 1); - assert_eq!( - (position.remainder_base_asset_amount as u64) < market.amm.order_step_size, - true - ); - - // burn - let lp_shares = position.lp_shares; - assert_eq!(lp_shares, BASE_PRECISION_U64); - burn_lp_shares(&mut position, &mut market, lp_shares, 22).unwrap(); - assert_eq!(position.lp_shares, 0); - assert_eq!(og_market.amm.sqrt_k, market.amm.sqrt_k); - assert_eq!(position.quote_asset_amount, -116900000001); - assert_eq!(position.base_asset_amount, 5000000000); - assert_eq!(position.remainder_base_asset_amount, 0); - - assert_eq!(market.amm.base_asset_amount_with_amm, 0); - assert_eq!(market.amm.base_asset_amount_with_unsettled_lp, 0); - assert_eq!(market.amm.base_asset_amount_short, -5000000000); - assert_eq!(market.amm.base_asset_amount_long, 5000000000); -} - -#[test] -fn test_remainder_burn_large_order_step_size() { - let mut position = PerpPosition { - ..PerpPosition::default() - }; - - let amm = AMM { - order_step_size: 2 * BASE_PRECISION_U64, - ..AMM::default_test() - }; - let mut market = PerpMarket { - amm, - ..PerpMarket::default_test() - }; - let og_market = market; - - mint_lp_shares(&mut position, &mut market, BASE_PRECISION_U64).unwrap(); - - market.amm.base_asset_amount_per_lp = 10; - market.amm.quote_asset_amount_per_lp = -10; - market.amm.base_asset_amount_with_unsettled_lp = -10; - market.amm.base_asset_amount_with_amm += 10; - - settle_lp_position(&mut position, &mut market).unwrap(); - - assert_eq!(position.last_base_asset_amount_per_lp, 10); - assert_eq!(position.last_quote_asset_amount_per_lp, -10); - assert_eq!(position.base_asset_amount, 0); - assert_eq!(position.quote_asset_amount, -10); - assert_eq!(position.remainder_base_asset_amount, 10); - assert_eq!(market.amm.base_asset_amount_with_unsettled_lp, -10); - // net baa doesnt change after settle_lp_position - assert_eq!(market.amm.base_asset_amount_with_amm, 10); - - market.amm.base_asset_amount_per_lp = BASE_PRECISION_I128 + 1; - market.amm.quote_asset_amount_per_lp = -16900000000; - market.amm.base_asset_amount_with_unsettled_lp += -(BASE_PRECISION_I128 + 1); - market.amm.base_asset_amount_with_amm += BASE_PRECISION_I128 + 1; - - settle_lp_position(&mut position, &mut market).unwrap(); - - assert_eq!(position.last_base_asset_amount_per_lp, 1000000001); - assert_eq!(position.last_quote_asset_amount_per_lp, -16900000000); - assert_eq!(position.quote_asset_amount, -16900000000); - assert_eq!(position.base_asset_amount, 0); - assert_eq!(position.remainder_base_asset_amount, 1000000001); - assert_eq!( - (position.remainder_base_asset_amount as u64) < market.amm.order_step_size, - true - ); - - // burn with overflowed remainder - let lp_shares = position.lp_shares; - assert_eq!(lp_shares, BASE_PRECISION_U64); - burn_lp_shares(&mut position, &mut market, lp_shares, 22).unwrap(); - assert_eq!(position.lp_shares, 0); - assert_eq!(og_market.amm.sqrt_k, market.amm.sqrt_k); - assert_eq!(position.quote_asset_amount, -16900000023); - assert_eq!(position.base_asset_amount, 0); - assert_eq!(position.remainder_base_asset_amount, 0); -} - -#[test] -pub fn test_lp_settle_pnl() { - let mut position = PerpPosition { - ..PerpPosition::default() - }; - position.last_cumulative_funding_rate = 1337; - - let mut oracle_price = get_pyth_price(100, 6); - let oracle_price_key = - Pubkey::from_str("J83w4HKfqxwcq3BEMMkPFSppX3gqekLyLJBexebFVkix").unwrap(); - let pyth_program = crate::ids::pyth_program::id(); - create_account_info!( - oracle_price, - &oracle_price_key, - &pyth_program, - oracle_account_info - ); - let clock = Clock { - slot: 0, - epoch_start_timestamp: 0, - epoch: 0, - leader_schedule_epoch: 0, - unix_timestamp: 0, - }; - let mut oracle_map = OracleMap::load_one(&oracle_account_info, clock.slot, None).unwrap(); - - let mut market = PerpMarket { - amm: AMM { - base_asset_reserve: 100 * AMM_RESERVE_PRECISION, - quote_asset_reserve: 100 * AMM_RESERVE_PRECISION, - bid_base_asset_reserve: 101 * AMM_RESERVE_PRECISION, - bid_quote_asset_reserve: 99 * AMM_RESERVE_PRECISION, - ask_base_asset_reserve: 99 * AMM_RESERVE_PRECISION, - ask_quote_asset_reserve: 101 * AMM_RESERVE_PRECISION, - sqrt_k: 100 * AMM_RESERVE_PRECISION, - peg_multiplier: 100 * PEG_PRECISION, - max_slippage_ratio: 50, - max_fill_reserve_fraction: 100, - order_step_size: 2 * BASE_PRECISION_U64 / 100, - quote_asset_amount: -150 * QUOTE_PRECISION_I128, - base_asset_amount_with_amm: BASE_PRECISION_I128, - base_asset_amount_long: BASE_PRECISION_I128, - oracle: oracle_price_key, - concentration_coef: 1000001, - historical_oracle_data: HistoricalOracleData { - last_oracle_price: oracle_price.agg.price, - last_oracle_price_twap_5min: oracle_price.agg.price, - last_oracle_price_twap: oracle_price.agg.price, - ..HistoricalOracleData::default() - }, - ..AMM::default() - }, - margin_ratio_initial: 1000, - margin_ratio_maintenance: 500, - number_of_users_with_base: 1, - number_of_users: 1, - status: MarketStatus::Active, - liquidator_fee: LIQUIDATION_FEE_PRECISION / 100, - pnl_pool: PoolBalance { - scaled_balance: (50 * SPOT_BALANCE_PRECISION), - market_index: QUOTE_SPOT_MARKET_INDEX, - ..PoolBalance::default() - }, - unrealized_pnl_maintenance_asset_weight: SPOT_WEIGHT_PRECISION.cast().unwrap(), - ..PerpMarket::default() - }; - - mint_lp_shares(&mut position, &mut market, BASE_PRECISION_U64).unwrap(); - - market.amm.base_asset_amount_per_lp = 10; - market.amm.quote_asset_amount_per_lp = -10; - market.amm.base_asset_amount_with_unsettled_lp = -10; - market.amm.base_asset_amount_with_amm += 10; - market.amm.cumulative_funding_rate_long = 169; - market.amm.cumulative_funding_rate_short = 169; - - settle_lp_position(&mut position, &mut market).unwrap(); - create_anchor_account_info!(market, PerpMarket, market_account_info); - let market_map = PerpMarketMap::load_one(&market_account_info, true).unwrap(); - - let mut spot_market = SpotMarket { - market_index: 0, - oracle_source: OracleSource::QuoteAsset, - cumulative_deposit_interest: SPOT_CUMULATIVE_INTEREST_PRECISION, - decimals: 6, - initial_asset_weight: SPOT_WEIGHT_PRECISION, - deposit_balance: 100 * SPOT_BALANCE_PRECISION, - ..SpotMarket::default() - }; - - create_anchor_account_info!(spot_market, SpotMarket, spot_market_account_info); - let spot_market_map = SpotMarketMap::load_one(&spot_market_account_info, true).unwrap(); - let user_key = Pubkey::default(); - let authority = Pubkey::default(); - - let mut user = User { - perp_positions: get_positions(position), - spot_positions: get_spot_positions(SpotPosition { - market_index: 0, - balance_type: SpotBalanceType::Deposit, - scaled_balance: 50 * SPOT_BALANCE_PRECISION_U64, - ..SpotPosition::default() - }), - ..User::default() - }; - - let state = State { - oracle_guard_rails: OracleGuardRails { - validity: ValidityGuardRails { - slots_before_stale_for_amm: 10, // 5s - slots_before_stale_for_margin: 120, // 60s - confidence_interval_max_size: 1000, - too_volatile_ratio: 5, - }, - ..OracleGuardRails::default() - }, - ..State::default() - }; - - let MarginCalculation { - total_collateral: total_collateral1, - margin_requirement: margin_requirement1, - .. - } = calculate_margin_requirement_and_total_collateral_and_liability_info( - &user, - &market_map, - &spot_market_map, - &mut oracle_map, - MarginContext::standard(MarginRequirementType::Initial), - ) - .unwrap(); - - assert_eq!(total_collateral1, 49999988); - assert_eq!(margin_requirement1, 2099020); // $2+ for margin req - - let result = settle_pnl( - 0, - &mut user, - &authority, - &user_key, - &market_map, - &spot_market_map, - &mut oracle_map, - &clock, - &state, - None, - SettlePnlMode::MustSettle, - ); - - assert_eq!(result, Ok(())); - // assert_eq!(result, Err(ErrorCode::InsufficientCollateralForSettlingPNL)) -} - -#[test] -fn test_lp_margin_calc() { - let mut position = PerpPosition { - ..PerpPosition::default() - }; - position.last_cumulative_funding_rate = 1337; - - let mut oracle_price = get_pyth_price(100, 6); - let oracle_price_key = - Pubkey::from_str("J83w4HKfqxwcq3BEMMkPFSppX3gqekLyLJBexebFVkix").unwrap(); - let pyth_program = crate::ids::pyth_program::id(); - create_account_info!( - oracle_price, - &oracle_price_key, - &pyth_program, - oracle_account_info - ); - let slot = 0; - let mut oracle_map = OracleMap::load_one(&oracle_account_info, slot, None).unwrap(); - - let mut market = PerpMarket { - amm: AMM { - base_asset_reserve: 100 * AMM_RESERVE_PRECISION, - quote_asset_reserve: 100 * AMM_RESERVE_PRECISION, - bid_base_asset_reserve: 101 * AMM_RESERVE_PRECISION, - bid_quote_asset_reserve: 99 * AMM_RESERVE_PRECISION, - ask_base_asset_reserve: 99 * AMM_RESERVE_PRECISION, - ask_quote_asset_reserve: 101 * AMM_RESERVE_PRECISION, - sqrt_k: 100 * AMM_RESERVE_PRECISION, - peg_multiplier: 100 * PEG_PRECISION, - max_slippage_ratio: 50, - max_fill_reserve_fraction: 100, - order_step_size: 2 * BASE_PRECISION_U64 / 100, - quote_asset_amount: -150 * QUOTE_PRECISION_I128, - base_asset_amount_with_amm: BASE_PRECISION_I128, - base_asset_amount_long: BASE_PRECISION_I128, - oracle: oracle_price_key, - concentration_coef: 1000001, - historical_oracle_data: HistoricalOracleData { - last_oracle_price: oracle_price.agg.price, - last_oracle_price_twap_5min: oracle_price.agg.price, - last_oracle_price_twap: oracle_price.agg.price, - ..HistoricalOracleData::default() - }, - ..AMM::default() - }, - margin_ratio_initial: 1000, - margin_ratio_maintenance: 500, - number_of_users_with_base: 1, - number_of_users: 1, - status: MarketStatus::Active, - liquidator_fee: LIQUIDATION_FEE_PRECISION / 100, - pnl_pool: PoolBalance { - scaled_balance: (50 * SPOT_BALANCE_PRECISION), - market_index: QUOTE_SPOT_MARKET_INDEX, - ..PoolBalance::default() - }, - unrealized_pnl_maintenance_asset_weight: SPOT_WEIGHT_PRECISION.cast().unwrap(), - ..PerpMarket::default() - }; - - mint_lp_shares(&mut position, &mut market, BASE_PRECISION_U64).unwrap(); - - market.amm.base_asset_amount_per_lp = 100 * BASE_PRECISION_I128; - market.amm.quote_asset_amount_per_lp = -BASE_PRECISION_I128; - market.amm.base_asset_amount_with_unsettled_lp = -100 * BASE_PRECISION_I128; - market.amm.base_asset_amount_short = -100 * BASE_PRECISION_I128; - market.amm.cumulative_funding_rate_long = 169 * 100000000; - market.amm.cumulative_funding_rate_short = 169 * 100000000; - - settle_lp_position(&mut position, &mut market).unwrap(); - create_anchor_account_info!(market, PerpMarket, market_account_info); - let market_map = PerpMarketMap::load_one(&market_account_info, true).unwrap(); - - let mut spot_market = SpotMarket { - market_index: 0, - oracle_source: OracleSource::QuoteAsset, - cumulative_deposit_interest: SPOT_CUMULATIVE_INTEREST_PRECISION, - decimals: 6, - initial_asset_weight: SPOT_WEIGHT_PRECISION, - deposit_balance: 100 * SPOT_BALANCE_PRECISION, - ..SpotMarket::default() - }; - - create_anchor_account_info!(spot_market, SpotMarket, spot_market_account_info); - let spot_market_map = SpotMarketMap::load_one(&spot_market_account_info, true).unwrap(); - - let mut user = User { - perp_positions: get_positions(position), - spot_positions: get_spot_positions(SpotPosition { - market_index: 0, - balance_type: SpotBalanceType::Deposit, - scaled_balance: 5000 * SPOT_BALANCE_PRECISION_U64, - ..SpotPosition::default() - }), - ..User::default() - }; - user.perp_positions[0].base_asset_amount = BASE_PRECISION_I128 as i64; - - // user has lp shares + long and last cumulative funding doesnt match - assert_eq!(user.perp_positions[0].lp_shares, 1000000000); - assert_eq!( - user.perp_positions[0].base_asset_amount, - BASE_PRECISION_I128 as i64 - ); - assert!( - user.perp_positions[0].last_cumulative_funding_rate != market.amm.last_funding_rate_long - ); - - let result = - meets_maintenance_margin_requirement(&user, &market_map, &spot_market_map, &mut oracle_map); - - assert_eq!(result.unwrap(), true); - - // add move lower - let oracle_price_data = OraclePriceData { - price: oracle_price.agg.price, - confidence: 100000, - delay: 1, - has_sufficient_number_of_data_points: true, - }; - - assert_eq!(market.amm.base_asset_amount_per_lp, 100000000000); - assert_eq!(market.amm.quote_asset_amount_per_lp, -1000000000); - assert_eq!(market.amm.cumulative_funding_rate_long, 16900000000); - assert_eq!(market.amm.cumulative_funding_rate_short, 16900000000); - - assert_eq!(user.perp_positions[0].lp_shares, 1000000000); - assert_eq!(user.perp_positions[0].base_asset_amount, 1000000000); - assert_eq!( - user.perp_positions[0].last_base_asset_amount_per_lp, - 100000000000 - ); - assert_eq!( - user.perp_positions[0].last_quote_asset_amount_per_lp, - -1000000000 - ); - assert_eq!( - user.perp_positions[0].last_cumulative_funding_rate, - 16900000000 - ); - - // increase markets so user has to settle lp - market.amm.base_asset_amount_per_lp *= 2; - market.amm.quote_asset_amount_per_lp *= 20; - - // update funding so user has unsettled funding - market.amm.cumulative_funding_rate_long *= 2; - market.amm.cumulative_funding_rate_short *= 2; - - apply_lp_rebase_to_perp_market(&mut market, 1).unwrap(); - - let sim_user_pos = user.perp_positions[0] - .simulate_settled_lp_position(&market, oracle_price_data.price) - .unwrap(); - assert_ne!( - sim_user_pos.base_asset_amount, - user.perp_positions[0].base_asset_amount - ); - assert_eq!(sim_user_pos.base_asset_amount, 101000000000); - assert_eq!(sim_user_pos.quote_asset_amount, -20000000000); - assert_eq!(sim_user_pos.last_cumulative_funding_rate, 16900000000); - - let strict_quote_price = StrictOraclePrice::test(1000000); - // ensure margin calc doesnt incorrectly count funding rate (funding pnl MUST come before settling lp) - let ( - margin_requirement, - weighted_unrealized_pnl, - worse_case_base_asset_value, - _open_order_fraction, - _base_asset_value, - ) = calculate_perp_position_value_and_pnl( - &user.perp_positions[0], - &market, - &oracle_price_data, - &strict_quote_price, - crate::math::margin::MarginRequirementType::Initial, - 0, - false, - false, - ) - .unwrap(); - - assert_eq!(margin_requirement, 1012000000); // $1010 + $2 mr for lp_shares - assert_eq!(weighted_unrealized_pnl, -9916900000); // $-9900000000 upnl (+ -16900000 from old funding) - assert_eq!(worse_case_base_asset_value, 10100000000); //$10100 -} - -#[test] -fn test_lp_has_correct_entry_be_price() { - let mut position = PerpPosition { - ..PerpPosition::default() - }; - - let amm = AMM { - order_step_size: BASE_PRECISION_U64 / 10, - ..AMM::default_test() - }; - let mut market = PerpMarket { - amm, - ..PerpMarket::default_test() - }; - - assert_eq!(market.amm.user_lp_shares, 0); - assert_eq!(market.amm.sqrt_k, 100000000000); - - mint_lp_shares(&mut position, &mut market, BASE_PRECISION_U64).unwrap(); - assert_eq!(market.amm.user_lp_shares, 1000000000); - assert_eq!(market.amm.sqrt_k, 101000000000); - assert_eq!(position.get_entry_price().unwrap(), 0); - - market.amm.base_asset_amount_per_lp = BASE_PRECISION_I128; - market.amm.quote_asset_amount_per_lp = -99_999_821; - market.amm.base_asset_amount_with_unsettled_lp = BASE_PRECISION_I128; - market.amm.base_asset_amount_long = BASE_PRECISION_I128; - - settle_lp_position(&mut position, &mut market).unwrap(); - assert_eq!(position.get_entry_price().unwrap(), 99999821); - - assert_eq!(position.quote_entry_amount, -99999821); - assert_eq!(position.quote_break_even_amount, -99999821); - assert_eq!(position.quote_asset_amount, -99999821); - - market.amm.base_asset_amount_per_lp -= BASE_PRECISION_I128 / 2; - market.amm.quote_asset_amount_per_lp += 97_999_821; - market.amm.base_asset_amount_with_unsettled_lp = BASE_PRECISION_I128 / 2; - market.amm.base_asset_amount_long = BASE_PRECISION_I128 / 2; - - settle_lp_position(&mut position, &mut market).unwrap(); - assert_eq!(position.get_entry_price().unwrap(), 99999822); - - assert_eq!(position.remainder_base_asset_amount, 0); - assert_eq!(position.quote_entry_amount, -49999911); - assert_eq!(position.quote_break_even_amount, -49999911); - assert_eq!(position.quote_asset_amount, -2000000); - assert_eq!(position.base_asset_amount, 500_000_000); - - let base_delta = -BASE_PRECISION_I128 / 4; - market.amm.base_asset_amount_per_lp += base_delta; - market.amm.quote_asset_amount_per_lp += 98_999_821 / 4; - let (update_base_delta, _) = - crate::math::orders::standardize_base_asset_amount_with_remainder_i128( - base_delta, - market.amm.order_step_size as u128, - ) - .unwrap(); - - market.amm.base_asset_amount_with_unsettled_lp += update_base_delta; - market.amm.base_asset_amount_long += update_base_delta; - - settle_lp_position(&mut position, &mut market).unwrap(); - assert_eq!(position.get_entry_price().unwrap(), 99999824); - assert_eq!(position.get_cost_basis().unwrap(), -75833183); - - assert_eq!(position.base_asset_amount, 300000000); - assert_eq!(position.remainder_base_asset_amount, -50000000); - assert_eq!(position.quote_entry_amount, -24999956); - assert_eq!(position.quote_break_even_amount, -24999956); - assert_eq!(position.quote_asset_amount, 22749955); -} - -#[test] -fn test_lp_has_correct_entry_be_price_sim_no_remainders() { - let mut position = PerpPosition { - ..PerpPosition::default() - }; - - let amm = AMM { - order_step_size: BASE_PRECISION_U64 / 10, - sqrt_k: BASE_PRECISION_U64 as u128, - ..AMM::default_test() - }; - let mut market = PerpMarket { - amm, - ..PerpMarket::default_test() - }; - - assert_eq!(market.amm.user_lp_shares, 0); - assert_eq!(market.amm.sqrt_k, 1000000000); - - mint_lp_shares(&mut position, &mut market, BASE_PRECISION_U64).unwrap(); - assert_eq!(market.amm.user_lp_shares, 1000000000); - assert_eq!(market.amm.sqrt_k, 2000000000); - assert_eq!(position.get_entry_price().unwrap(), 0); - assert_eq!(position.get_cost_basis().unwrap(), 0); - assert_eq!(position.get_breakeven_price().unwrap(), 0); - assert_eq!(position.remainder_base_asset_amount, 0); - assert_eq!(position.base_asset_amount, 0); - let mut num_position_flips = 0; - let mut flip_indexes: Vec = Vec::new(); - - for i in 0..3000 { - if i % 3 == 0 { - let px = 100_000_000 - i; - let multi = i % 19 + 1; - let divisor = 10; - let base_delta = -BASE_PRECISION_I128 * multi / divisor; - market.amm.base_asset_amount_per_lp += base_delta; - market.amm.quote_asset_amount_per_lp += px * multi / divisor; - market.amm.base_asset_amount_with_unsettled_lp += base_delta; - market.amm.base_asset_amount_short += base_delta; - } else { - // buy - let px = 99_199_821 + i; - let multi = i % 5 + 1; - let divisor = 5; - let base_delta = BASE_PRECISION_I128 * multi / divisor; - market.amm.base_asset_amount_per_lp += base_delta; - market.amm.quote_asset_amount_per_lp -= px * multi / divisor; - market.amm.base_asset_amount_with_unsettled_lp += base_delta; - market.amm.base_asset_amount_long += base_delta; - } - - let position_base_before = position.base_asset_amount; - - settle_lp_position(&mut position, &mut market).unwrap(); - - if position_base_before.signum() != position.base_asset_amount.signum() { - num_position_flips += 1; - flip_indexes.push(i); - } - - let entry = position.get_entry_price().unwrap(); - let be = position.get_breakeven_price().unwrap(); - let cb = position.get_cost_basis().unwrap(); - - let iii = position - .base_asset_amount - .safe_add(position.remainder_base_asset_amount as i64) - .unwrap(); - msg!( - "{}: entry: {}, be: {} cb:{} ({}/{})", - i, - entry, - be, - cb, - iii, - position.base_asset_amount, - ); - assert_eq!(position.remainder_base_asset_amount, 0); - - if position.get_base_asset_amount_with_remainder_abs().unwrap() != 0 { - assert!(entry <= 100 * PRICE_PRECISION as i128); - assert!(entry >= 99 * PRICE_PRECISION as i128); - } - } - let entry = position.get_entry_price().unwrap(); - let be = position.get_breakeven_price().unwrap(); - let cb = position.get_cost_basis().unwrap(); - - assert_eq!(position.base_asset_amount, 200500000000); - assert_eq!(entry, 99202392); - assert_eq!(be, 99202392); - assert_eq!(cb, 95227357); - assert_eq!(num_position_flips, 4); - assert_eq!(flip_indexes, [0, 1, 18, 19]); -} - -#[test] -fn test_lp_remainder_position_updates() { - let mut position = PerpPosition { - ..PerpPosition::default() - }; - let amm = AMM { - order_step_size: BASE_PRECISION_U64 / 10, - sqrt_k: BASE_PRECISION_U64 as u128, - ..AMM::default_test() - }; - let mut market = PerpMarket { - amm, - ..PerpMarket::default_test() - }; - - assert_eq!(market.amm.user_lp_shares, 0); - assert_eq!(market.amm.sqrt_k, 1000000000); - - mint_lp_shares(&mut position, &mut market, BASE_PRECISION_U64).unwrap(); - assert_eq!(market.amm.user_lp_shares, 1000000000); - - let position_delta = PositionDelta { - quote_asset_amount: 0, - base_asset_amount: 0, - remainder_base_asset_amount: Some(880), - }; - - let pnl: i64 = update_position_and_market(&mut position, &mut market, &position_delta).unwrap(); - assert_eq!(pnl, 0); - - let position_delta = PositionDelta { - quote_asset_amount: 0, - base_asset_amount: 0, - remainder_base_asset_amount: Some(-881), - }; - - let pnl: i64 = update_position_and_market(&mut position, &mut market, &position_delta).unwrap(); - assert_eq!(pnl, 0); - assert_eq!(position.base_asset_amount, 0); - assert_eq!(position.remainder_base_asset_amount, -1); - crate::validation::perp_market::validate_perp_market(&market).unwrap(); - crate::validation::position::validate_perp_position_with_perp_market(&position, &market) - .unwrap(); - - let position_delta = PositionDelta { - quote_asset_amount: -199 * 1000000, - base_asset_amount: BASE_PRECISION_I64, - remainder_base_asset_amount: Some(-BASE_PRECISION_I64 / 22), - }; - - let pnl: i64 = update_position_and_market(&mut position, &mut market, &position_delta).unwrap(); - assert_eq!(pnl, 0); - assert_eq!(position.base_asset_amount, 1000000000); - assert_eq!(position.remainder_base_asset_amount, -45454546); - - crate::validation::perp_market::validate_perp_market(&market).unwrap(); - crate::validation::position::validate_perp_position_with_perp_market(&position, &market) - .unwrap(); - - let position_delta = PositionDelta { - quote_asset_amount: 199 * 1000000, - base_asset_amount: -BASE_PRECISION_I64 * 2, - remainder_base_asset_amount: Some(BASE_PRECISION_I64 / 23), - }; - - let pnl: i64 = update_position_and_market(&mut position, &mut market, &position_delta).unwrap(); - assert_eq!(pnl, -101912122); - assert_eq!(position.base_asset_amount, -1000000000); - assert_eq!(position.remainder_base_asset_amount, -1976286); - - crate::validation::perp_market::validate_perp_market(&market).unwrap(); - crate::validation::position::validate_perp_position_with_perp_market(&position, &market) - .unwrap(); -} - -#[test] -fn test_lp_remainder_position_updates_2() { - let mut position = PerpPosition { - ..PerpPosition::default() - }; - let amm = AMM { - order_step_size: BASE_PRECISION_U64 / 10, - sqrt_k: BASE_PRECISION_U64 as u128, - ..AMM::default_test() - }; - let mut market = PerpMarket { - amm, - ..PerpMarket::default_test() - }; - - assert_eq!(market.amm.user_lp_shares, 0); - assert_eq!(market.amm.sqrt_k, 1000000000); - - mint_lp_shares(&mut position, &mut market, BASE_PRECISION_U64).unwrap(); - assert_eq!(market.amm.user_lp_shares, 1000000000); - - let position_delta = PositionDelta { - quote_asset_amount: 0, - base_asset_amount: 300000000, - remainder_base_asset_amount: Some(33333333), - }; - - let pnl: i64 = update_position_and_market(&mut position, &mut market, &position_delta).unwrap(); - assert_eq!(pnl, 0); - - crate::validation::perp_market::validate_perp_market(&market).unwrap(); - crate::validation::position::validate_perp_position_with_perp_market(&position, &market) - .unwrap(); - - let position_delta = PositionDelta { - quote_asset_amount: 0, - base_asset_amount: 500000000, - remainder_base_asset_amount: Some(0), - }; - - let pnl: i64 = update_position_and_market(&mut position, &mut market, &position_delta).unwrap(); - assert_eq!(pnl, 0); - assert_eq!(position.base_asset_amount, 800000000); - assert_eq!(position.remainder_base_asset_amount, 33333333); - crate::validation::perp_market::validate_perp_market(&market).unwrap(); - crate::validation::position::validate_perp_position_with_perp_market(&position, &market) - .unwrap(); - - let position_delta = PositionDelta { - quote_asset_amount: 199 * 10000, - base_asset_amount: -300000000, - remainder_base_asset_amount: Some(-63636363), - }; - - let pnl: i64 = update_position_and_market(&mut position, &mut market, &position_delta).unwrap(); - assert_eq!(pnl, 1990000); - assert_eq!(position.base_asset_amount, 500000000); - assert_eq!(position.remainder_base_asset_amount, -30303030); - assert_eq!(market.amm.base_asset_amount_long, 500000000); - assert_eq!(market.amm.base_asset_amount_short, 0); - assert_eq!(market.amm.base_asset_amount_with_unsettled_lp, 500000000); - assert_eq!(market.amm.base_asset_amount_with_amm, 0); - - crate::validation::perp_market::validate_perp_market(&market).unwrap(); - crate::validation::position::validate_perp_position_with_perp_market(&position, &market) - .unwrap(); -} - -#[test] -fn test_lp_has_correct_entry_be_price_sim() { - let mut position = PerpPosition { - ..PerpPosition::default() - }; - - let amm = AMM { - order_step_size: BASE_PRECISION_U64 / 10, - sqrt_k: BASE_PRECISION_U64 as u128, - ..AMM::default_test() - }; - let mut market = PerpMarket { - amm, - ..PerpMarket::default_test() - }; - - assert_eq!(market.amm.user_lp_shares, 0); - assert_eq!(market.amm.sqrt_k, 1000000000); - - mint_lp_shares(&mut position, &mut market, BASE_PRECISION_U64).unwrap(); - assert_eq!(market.amm.user_lp_shares, 1000000000); - assert_eq!(market.amm.sqrt_k, 2000000000); - assert_eq!(position.get_entry_price().unwrap(), 0); - assert_eq!(position.get_cost_basis().unwrap(), 0); - assert_eq!(position.get_breakeven_price().unwrap(), 0); - assert_eq!(position.remainder_base_asset_amount, 0); - assert_eq!(position.base_asset_amount, 0); - let mut num_position_flips = 0; - let mut flip_indexes: Vec = Vec::new(); - - let mut total_remainder = 0; - for i in 0..3000 { - if i % 3 == 0 { - let px = 100_000_000 - i; - let multi = i % 19 + 1; - let divisor = 11; - let base_delta = -BASE_PRECISION_I128 * multi / divisor; - market.amm.base_asset_amount_per_lp += base_delta; - market.amm.quote_asset_amount_per_lp += px * multi / divisor; - - let (update_base_delta, rr) = - crate::math::orders::standardize_base_asset_amount_with_remainder_i128( - base_delta, - market.amm.order_step_size as u128, - ) - .unwrap(); - total_remainder += rr; - - let (total_remainder_f, _rr) = - crate::math::orders::standardize_base_asset_amount_with_remainder_i128( - total_remainder, - market.amm.order_step_size as u128, - ) - .unwrap(); - if total_remainder_f != 0 { - total_remainder -= total_remainder_f; - msg!("total_remainder update {}", total_remainder); - } - - market.amm.base_asset_amount_with_unsettled_lp += update_base_delta; - market.amm.base_asset_amount_long += update_base_delta; - } else { - // buy - let px = 99_199_821 + i; - let multi = i % 5 + 1; - let divisor = 6; - let base_delta = BASE_PRECISION_I128 * multi / divisor; - market.amm.base_asset_amount_per_lp += base_delta; - market.amm.quote_asset_amount_per_lp -= px * multi / divisor; - - let (update_base_delta, rr) = - crate::math::orders::standardize_base_asset_amount_with_remainder_i128( - base_delta, - market.amm.order_step_size as u128, - ) - .unwrap(); - total_remainder += rr; - - let (total_remainder_f, _rr) = - crate::math::orders::standardize_base_asset_amount_with_remainder_i128( - total_remainder, - market.amm.order_step_size as u128, - ) - .unwrap(); - if total_remainder_f != 0 { - total_remainder -= total_remainder_f; - } - - market.amm.base_asset_amount_with_unsettled_lp += update_base_delta; - market.amm.base_asset_amount_short += update_base_delta; - } - - let position_base_before = position.base_asset_amount; - crate::validation::perp_market::validate_perp_market(&market).unwrap(); - crate::validation::position::validate_perp_position_with_perp_market(&position, &market) - .unwrap(); - - settle_lp_position(&mut position, &mut market).unwrap(); - - if position_base_before.signum() != position.base_asset_amount.signum() { - num_position_flips += 1; - flip_indexes.push(i); - } - - let entry = position.get_entry_price().unwrap(); - let be = position.get_breakeven_price().unwrap(); - let cb = position.get_cost_basis().unwrap(); - - let iii = position - .base_asset_amount - .safe_add(position.remainder_base_asset_amount as i64) - .unwrap(); - msg!( - "{}: entry: {}, be: {} cb:{} ({}/{})", - i, - entry, - be, - cb, - iii, - position.base_asset_amount, - ); - // assert_ne!(position.remainder_base_asset_amount, 0); - - if position.get_base_asset_amount_with_remainder_abs().unwrap() != 0 { - assert!(entry <= 100 * PRICE_PRECISION as i128); - assert!(entry >= 99 * PRICE_PRECISION as i128); - } - } - let entry = position.get_entry_price().unwrap(); - let be = position.get_breakeven_price().unwrap(); - let cb = position.get_cost_basis().unwrap(); - - assert_eq!(entry, 99202570); - assert_eq!(be, 99202570); - assert_eq!(cb, 91336780); - assert_eq!(num_position_flips, 5); - assert_eq!(flip_indexes, [1, 18, 19, 36, 37]); - assert_eq!(position.base_asset_amount, 91300000000); -} - -#[test] -fn test_lp_has_correct_entry_be_price_sim_more_flips() { - let mut position = PerpPosition { - ..PerpPosition::default() - }; - - let amm = AMM { - order_step_size: BASE_PRECISION_U64 / 10, - sqrt_k: BASE_PRECISION_U64 as u128, - ..AMM::default_test() - }; - let mut market = PerpMarket { - amm, - ..PerpMarket::default_test() - }; - - assert_eq!(market.amm.user_lp_shares, 0); - assert_eq!(market.amm.sqrt_k, 1000000000); - - mint_lp_shares(&mut position, &mut market, BASE_PRECISION_U64).unwrap(); - assert_eq!(market.amm.user_lp_shares, 1000000000); - assert_eq!(market.amm.sqrt_k, 2000000000); - assert_eq!(position.get_entry_price().unwrap(), 0); - assert_eq!(position.get_cost_basis().unwrap(), 0); - assert_eq!(position.get_breakeven_price().unwrap(), 0); - assert_eq!(position.remainder_base_asset_amount, 0); - assert_eq!(position.base_asset_amount, 0); - let mut num_position_flips = 0; - let mut flip_indexes: Vec = Vec::new(); - - for i in 0..3000 { - if i % 2 == 0 { - let px = 99_800_000 - i * i % 4; - let multi = i % 7 + 1 + i; - let divisor = 10; - let amt2 = -BASE_PRECISION_I128 * multi / divisor; - market.amm.base_asset_amount_per_lp += amt2; - market.amm.quote_asset_amount_per_lp += px * multi / divisor; - market.amm.base_asset_amount_with_unsettled_lp += amt2; - market.amm.base_asset_amount_short += amt2; - } else { - // buy - let px = 99_199_821 + i * i % 4; - let multi = i % 7 + 1 + i; - let divisor = 10; - let base_delta = BASE_PRECISION_I128 * multi / divisor; - market.amm.base_asset_amount_per_lp += base_delta; - market.amm.quote_asset_amount_per_lp -= px * multi / divisor; - market.amm.base_asset_amount_with_unsettled_lp += base_delta; - market.amm.base_asset_amount_long += base_delta; - } - - let position_base_before = position.base_asset_amount; - - settle_lp_position(&mut position, &mut market).unwrap(); - - if position_base_before.signum() != position.base_asset_amount.signum() { - num_position_flips += 1; - flip_indexes.push(i); - } - assert_eq!(position.remainder_base_asset_amount, 0); - - let entry = position.get_entry_price().unwrap(); - let be = position.get_breakeven_price().unwrap(); - let cb = position.get_cost_basis().unwrap(); - - let iii = position - .base_asset_amount - .safe_add(position.remainder_base_asset_amount as i64) - .unwrap(); - msg!( - "{}: entry: {}, be: {} cb:{} ({}/{})", - i, - entry, - be, - cb, - iii, - position.base_asset_amount, - ); - - if position.get_base_asset_amount_with_remainder_abs().unwrap() != 0 { - assert!(entry <= 99_800_000_i128); - assert!(entry >= 99_199_820_i128); - } - } - - assert_eq!(num_position_flips, 3000); - // assert_eq!(flip_indexes, [0, 1, 18, 19]); - - let entry = position.get_entry_price().unwrap(); - let be = position.get_breakeven_price().unwrap(); - let cb = position.get_cost_basis().unwrap(); - - assert_eq!(position.base_asset_amount, 150200000000); - assert_eq!(position.remainder_base_asset_amount, 0); - - assert_eq!(entry, 99199822); - assert_eq!(be, 99199822); - assert_eq!(cb, -801664962); -} - -#[test] -fn test_get_position_update_type_lp_opens() { - // position is empty, every inc must be open - let position = PerpPosition { - ..PerpPosition::default() - }; - - let delta = PositionDelta { - quote_asset_amount: 0, - base_asset_amount: 0, - remainder_base_asset_amount: Some(0), - }; - assert_eq!( - get_position_update_type(&position, &delta).unwrap(), - PositionUpdateType::Open - ); - - let position = PerpPosition { - ..PerpPosition::default() - }; - - let delta = PositionDelta { - quote_asset_amount: 10000, - base_asset_amount: -898989, - remainder_base_asset_amount: Some(0), - }; - assert_eq!( - get_position_update_type(&position, &delta).unwrap(), - PositionUpdateType::Open - ); - - let delta = PositionDelta { - quote_asset_amount: 10000, - base_asset_amount: 0, - remainder_base_asset_amount: Some(0), - }; - assert_eq!( - get_position_update_type(&position, &delta).unwrap(), - PositionUpdateType::Open - ); - - let delta = PositionDelta { - quote_asset_amount: 0, - base_asset_amount: 0, - remainder_base_asset_amount: Some(1000), - }; - assert_eq!( - get_position_update_type(&position, &delta).unwrap(), - PositionUpdateType::Open - ); - - let delta = PositionDelta { - quote_asset_amount: 0, - base_asset_amount: 0, - remainder_base_asset_amount: Some(-88881000), - }; - assert_eq!( - get_position_update_type(&position, &delta).unwrap(), - PositionUpdateType::Open - ); - - let delta = PositionDelta { - quote_asset_amount: 1899990, - base_asset_amount: -8989898, - remainder_base_asset_amount: Some(-88881000), - }; - assert_eq!( - get_position_update_type(&position, &delta).unwrap(), - PositionUpdateType::Open - ); -} - -#[test] -fn test_get_position_update_type_lp_negative_position() { - // $119 short - let position = PerpPosition { - base_asset_amount: -1000000000 * 2, - quote_asset_amount: 119000000 * 2, - quote_entry_amount: 119000000 * 2, - quote_break_even_amount: 119000000 * 2, - ..PerpPosition::default() - }; - - assert_eq!(position.get_cost_basis().unwrap(), 119000000); - assert_eq!(position.get_breakeven_price().unwrap(), 119000000); - assert_eq!(position.get_entry_price().unwrap(), 119000000); - - let delta = PositionDelta { - quote_asset_amount: 0, - base_asset_amount: 0, - remainder_base_asset_amount: Some(0), - }; - assert_eq!( - get_position_update_type(&position, &delta).unwrap(), - PositionUpdateType::Reduce - ); // different signum but smaller - - let delta = PositionDelta { - quote_asset_amount: 10000, - base_asset_amount: -898989, - remainder_base_asset_amount: Some(0), - }; - assert_eq!( - get_position_update_type(&position, &delta).unwrap(), - PositionUpdateType::Increase - ); // more negative - - let delta = PositionDelta { - quote_asset_amount: 10000, - base_asset_amount: 0, - remainder_base_asset_amount: Some(0), - }; - assert_eq!( - get_position_update_type(&position, &delta).unwrap(), - PositionUpdateType::Reduce - ); - - let delta = PositionDelta { - quote_asset_amount: 0, - base_asset_amount: 0, - remainder_base_asset_amount: Some(1000), - }; - assert_eq!( - get_position_update_type(&position, &delta).unwrap(), - PositionUpdateType::Reduce - ); - - let delta = PositionDelta { - quote_asset_amount: 0, - base_asset_amount: 0, - remainder_base_asset_amount: Some(-88881000), - }; - assert_eq!( - get_position_update_type(&position, &delta).unwrap(), - PositionUpdateType::Increase - ); - - let delta = PositionDelta { - quote_asset_amount: 1899990, - base_asset_amount: -8989898, - remainder_base_asset_amount: Some(-88881000), - }; - assert_eq!( - get_position_update_type(&position, &delta).unwrap(), - PositionUpdateType::Increase - ); - - // opposite sign remainder/base - let delta = PositionDelta { - quote_asset_amount: -88888, - base_asset_amount: 81, - remainder_base_asset_amount: Some(-81000), - }; - assert_eq!( - get_position_update_type(&position, &delta).unwrap(), - PositionUpdateType::Increase - ); - - // opposite sign remainder/base - let delta = PositionDelta { - quote_asset_amount: -88888, - base_asset_amount: 81000, - remainder_base_asset_amount: Some(-81), - }; - assert_eq!( - get_position_update_type(&position, &delta).unwrap(), - PositionUpdateType::Reduce - ); -} - -#[test] -fn test_get_position_update_type_lp_positive_position() { - // $119 long - let position = PerpPosition { - base_asset_amount: 1000000000 * 2, - quote_asset_amount: -119000000 * 2, - quote_entry_amount: -119000000 * 2, - quote_break_even_amount: -119000000 * 2, - ..PerpPosition::default() - }; - - assert_eq!(position.get_cost_basis().unwrap(), 119000000); - assert_eq!(position.get_breakeven_price().unwrap(), 119000000); - assert_eq!(position.get_entry_price().unwrap(), 119000000); - - let delta = PositionDelta { - quote_asset_amount: 0, - base_asset_amount: 0, - remainder_base_asset_amount: Some(0), - }; - assert_eq!( - get_position_update_type(&position, &delta).unwrap(), - PositionUpdateType::Reduce - ); // different signum but smaller - - let delta = PositionDelta { - quote_asset_amount: 10000, - base_asset_amount: -898989, - remainder_base_asset_amount: Some(0), - }; - assert_eq!( - get_position_update_type(&position, &delta).unwrap(), - PositionUpdateType::Reduce - ); - - let delta = PositionDelta { - quote_asset_amount: 10000, - base_asset_amount: 0, - remainder_base_asset_amount: Some(0), - }; - assert_eq!( - get_position_update_type(&position, &delta).unwrap(), - PositionUpdateType::Reduce - ); // no base/remainder is reduce (should be skipped earlier) - - let delta = PositionDelta { - quote_asset_amount: 0, - base_asset_amount: 0, - remainder_base_asset_amount: Some(1000), - }; - assert_eq!( - get_position_update_type(&position, &delta).unwrap(), - PositionUpdateType::Increase - ); - - let delta = PositionDelta { - quote_asset_amount: 0, - base_asset_amount: 0, - remainder_base_asset_amount: Some(-88881000), - }; - assert_eq!( - get_position_update_type(&position, &delta).unwrap(), - PositionUpdateType::Reduce - ); - - let delta = PositionDelta { - quote_asset_amount: 1899990, - base_asset_amount: -8989898, - remainder_base_asset_amount: Some(-88881000), - }; - assert_eq!( - get_position_update_type(&position, &delta).unwrap(), - PositionUpdateType::Reduce - ); - - // opposite sign remainder/base - let delta = PositionDelta { - quote_asset_amount: -88888, - base_asset_amount: 81, - remainder_base_asset_amount: Some(-81000), - }; - assert_eq!( - get_position_update_type(&position, &delta).unwrap(), - PositionUpdateType::Reduce - ); - - // opposite sign remainder/base - let delta = PositionDelta { - quote_asset_amount: -88888, - base_asset_amount: 81000, - remainder_base_asset_amount: Some(-81), - }; - assert_eq!( - get_position_update_type(&position, &delta).unwrap(), - PositionUpdateType::Increase - ); -} - -#[test] -fn test_get_position_update_type_lp_positive_position_with_positive_remainder() { - // $119 long - let position = PerpPosition { - base_asset_amount: 1000000000 * 2, - remainder_base_asset_amount: 7809809, - quote_asset_amount: -119000000 * 2, - quote_entry_amount: -119000000 * 2, - quote_break_even_amount: -119000000 * 2, - ..PerpPosition::default() - }; - - assert_eq!(position.get_cost_basis().unwrap(), 119000000); - assert_eq!(position.get_breakeven_price().unwrap(), 118537123); - assert_eq!(position.get_entry_price().unwrap(), 118537123); - - let delta = PositionDelta { - quote_asset_amount: 0, - base_asset_amount: -1000000001 * 2, - remainder_base_asset_amount: Some(0), - }; - assert_eq!( - get_position_update_type(&position, &delta).unwrap(), - PositionUpdateType::Reduce - ); // different signum but smaller - - let delta = PositionDelta { - quote_asset_amount: 0, - base_asset_amount: 0, - remainder_base_asset_amount: Some(-7809809), - }; - assert_eq!( - get_position_update_type(&position, &delta).unwrap(), - PositionUpdateType::Reduce - ); - - let delta = PositionDelta { - quote_asset_amount: 0, - base_asset_amount: -1000000000 * 2, - remainder_base_asset_amount: Some(-7809809 - 1), - }; - assert_eq!( - get_position_update_type(&position, &delta).unwrap(), - PositionUpdateType::Flip - ); // different signum but smaller - - let delta = PositionDelta { - quote_asset_amount: 0, - base_asset_amount: 0, - remainder_base_asset_amount: Some(0), - }; - assert_eq!( - get_position_update_type(&position, &delta).unwrap(), - PositionUpdateType::Reduce - ); // different signum but smaller - - let delta = PositionDelta { - quote_asset_amount: 10000, - base_asset_amount: -898989, - remainder_base_asset_amount: Some(0), - }; - assert_eq!( - get_position_update_type(&position, &delta).unwrap(), - PositionUpdateType::Reduce - ); - - let delta = PositionDelta { - quote_asset_amount: 10000, - base_asset_amount: 0, - remainder_base_asset_amount: Some(0), - }; - assert_eq!( - get_position_update_type(&position, &delta).unwrap(), - PositionUpdateType::Reduce - ); // no base/remainder is reduce (should be skipped earlier) - - let delta = PositionDelta { - quote_asset_amount: 0, - base_asset_amount: 0, - remainder_base_asset_amount: Some(1000), - }; - assert_eq!( - get_position_update_type(&position, &delta).unwrap(), - PositionUpdateType::Increase - ); - - let delta = PositionDelta { - quote_asset_amount: 0, - base_asset_amount: 0, - remainder_base_asset_amount: Some(-88881000), - }; - assert_eq!( - get_position_update_type(&position, &delta).unwrap(), - PositionUpdateType::Reduce - ); - - let delta = PositionDelta { - quote_asset_amount: 1899990, - base_asset_amount: -8989898, - remainder_base_asset_amount: Some(-88881000), - }; - assert_eq!( - get_position_update_type(&position, &delta).unwrap(), - PositionUpdateType::Reduce - ); - - // opposite sign remainder/base - let delta = PositionDelta { - quote_asset_amount: -88888, - base_asset_amount: 81, - remainder_base_asset_amount: Some(-81000), - }; - assert_eq!( - get_position_update_type(&position, &delta).unwrap(), - PositionUpdateType::Reduce - ); - - // opposite sign remainder/base - let delta = PositionDelta { - quote_asset_amount: -88888, - base_asset_amount: 81000, - remainder_base_asset_amount: Some(-81), - }; - assert_eq!( - get_position_update_type(&position, &delta).unwrap(), - PositionUpdateType::Increase - ); -} - -#[test] -fn test_get_position_update_type_positive_remainder() { - // $119 long (only a remainder size) - let position = PerpPosition { - base_asset_amount: 0, - remainder_base_asset_amount: 7809809, - quote_asset_amount: -119000000 * 7809809 / BASE_PRECISION_I64, - quote_entry_amount: -119000000 * 7809809 / BASE_PRECISION_I64, - quote_break_even_amount: -119000000 * 7809809 / BASE_PRECISION_I64, - ..PerpPosition::default() - }; - - assert_eq!(position.get_cost_basis().unwrap(), 0); - assert_eq!(position.get_breakeven_price().unwrap(), 118999965); - assert_eq!(position.get_entry_price().unwrap(), 118999965); - - let delta = PositionDelta { - quote_asset_amount: 0, - base_asset_amount: -1000000001 * 2, - remainder_base_asset_amount: Some(0), - }; - assert_eq!( - get_position_update_type(&position, &delta).unwrap(), - PositionUpdateType::Flip - ); - - let delta = PositionDelta { - quote_asset_amount: 0, - base_asset_amount: 0, - remainder_base_asset_amount: Some(1), - }; - assert_eq!( - get_position_update_type(&position, &delta).unwrap(), - PositionUpdateType::Increase - ); // different signum but smaller - - let delta = PositionDelta { - quote_asset_amount: 0, - base_asset_amount: 0, - remainder_base_asset_amount: Some(-8791), - }; - assert_eq!( - get_position_update_type(&position, &delta).unwrap(), - PositionUpdateType::Reduce - ); // different signum but smaller - - let delta = PositionDelta { - quote_asset_amount: 0, - base_asset_amount: 0, - remainder_base_asset_amount: Some(-7809809), - }; - assert_eq!( - get_position_update_type(&position, &delta).unwrap(), - PositionUpdateType::Close - ); // different signum but smaller - - let delta = PositionDelta { - quote_asset_amount: 0, - base_asset_amount: 0, - remainder_base_asset_amount: Some(-7809809 - 1), - }; - assert_eq!( - get_position_update_type(&position, &delta).unwrap(), - PositionUpdateType::Flip - ); // different signum but smaller - - let delta = PositionDelta { - quote_asset_amount: 0, - base_asset_amount: -1000000000 * 2, - remainder_base_asset_amount: Some(-7809809 - 1), - }; - assert_eq!( - get_position_update_type(&position, &delta).unwrap(), - PositionUpdateType::Flip - ); // different signum but smaller - - let delta = PositionDelta { - quote_asset_amount: 0, - base_asset_amount: -1000000000 * 2, - remainder_base_asset_amount: Some(0), - }; - assert_eq!( - get_position_update_type(&position, &delta).unwrap(), - PositionUpdateType::Flip - ); // different signum but smaller - - let delta = PositionDelta { - quote_asset_amount: 0, - base_asset_amount: -1000000000 * 2, - remainder_base_asset_amount: None, - }; - assert_eq!( - get_position_update_type(&position, &delta).unwrap(), - PositionUpdateType::Flip - ); // different signum but smaller -} diff --git a/programs/drift/src/controller/mod.rs b/programs/drift/src/controller/mod.rs index ecd9a3a6ab..1565eb1174 100644 --- a/programs/drift/src/controller/mod.rs +++ b/programs/drift/src/controller/mod.rs @@ -2,7 +2,6 @@ pub mod amm; pub mod funding; pub mod insurance; pub mod liquidation; -pub mod lp; pub mod orders; pub mod pda; pub mod pnl; diff --git a/programs/drift/src/controller/orders.rs b/programs/drift/src/controller/orders.rs index 84c2010c66..63c23e7058 100644 --- a/programs/drift/src/controller/orders.rs +++ b/programs/drift/src/controller/orders.rs @@ -8,12 +8,10 @@ use crate::state::high_leverage_mode_config::HighLeverageModeConfig; use anchor_lang::prelude::*; use crate::controller::funding::settle_funding_payment; -use crate::controller::lp::burn_lp_shares; use crate::controller::position; use crate::controller::position::{ add_new_position, decrease_open_bids_and_asks, get_position_index, increase_open_bids_and_asks, - update_lp_market_position, update_position_and_market, update_quote_asset_amount, - PositionDirection, + update_position_and_market, update_quote_asset_amount, PositionDirection, }; use crate::controller::spot_balance::{ update_spot_balances, update_spot_market_cumulative_interest, @@ -37,7 +35,6 @@ use crate::math::fulfillment::{ determine_perp_fulfillment_methods, determine_spot_fulfillment_methods, }; use crate::math::liquidation::validate_user_not_being_liquidated; -use crate::math::lp::calculate_lp_shares_to_burn_for_risk_reduction; use crate::math::matching::{ are_orders_same_market_but_different_sides, calculate_fill_for_matched_orders, calculate_filler_multiplier_for_matched_orders, do_orders_cross, is_maker_for_taker, @@ -1015,7 +1012,7 @@ pub fn fill_perp_order( // settle lp position so its tradeable let mut market = perp_market_map.get_ref_mut(&market_index)?; - controller::lp::settle_funding_payment_then_lp(user, &user_key, &mut market, now)?; + settle_funding_payment(user, &user_key, &mut market, now)?; validate!( matches!( @@ -1135,6 +1132,7 @@ pub fn fill_perp_order( amm_lp_allowed_to_jit_make = market .amm .amm_lp_allowed_to_jit_make(amm_wants_to_jit_make)?; + // TODO what do do here? amm_can_skip_duration = market.can_skip_auction_duration(&state, amm_lp_allowed_to_jit_make)?; @@ -1897,7 +1895,6 @@ fn fulfill_perp_order( fee_structure, oracle_map, fill_mode.is_liquidation(), - None, )?; if maker_fill_base_asset_amount != 0 { @@ -2271,29 +2268,6 @@ pub fn fulfill_perp_order_with_amm( let user_position_delta = get_position_delta_for_fill(base_asset_amount, quote_asset_amount, order_direction)?; - if liquidity_split != AMMLiquiditySplit::ProtocolOwned { - update_lp_market_position( - market, - &user_position_delta, - fee_to_market_for_lp.cast()?, - liquidity_split, - )?; - } - - if market.amm.user_lp_shares > 0 { - let (new_terminal_quote_reserve, new_terminal_base_reserve) = - crate::math::amm::calculate_terminal_reserves(&market.amm)?; - market.amm.terminal_quote_asset_reserve = new_terminal_quote_reserve; - - let (min_base_asset_reserve, max_base_asset_reserve) = - crate::math::amm::calculate_bid_ask_bounds( - market.amm.concentration_coef, - new_terminal_base_reserve, - )?; - market.amm.min_base_asset_reserve = min_base_asset_reserve; - market.amm.max_base_asset_reserve = max_base_asset_reserve; - } - // Increment the protocol's total fee variables market.amm.total_fee = market.amm.total_fee.safe_add(fee_to_market.cast()?)?; market.amm.total_exchange_fee = market.amm.total_exchange_fee.safe_add(user_fee.cast()?)?; @@ -2523,7 +2497,6 @@ pub fn fulfill_perp_order_with_match( fee_structure: &FeeStructure, oracle_map: &mut OracleMap, is_liquidation: bool, - amm_lp_allowed_to_jit_make: Option, ) -> DriftResult<(u64, u64, u64)> { if !are_orders_same_market_but_different_sides( &maker.orders[maker_order_index], @@ -2610,7 +2583,6 @@ pub fn fulfill_perp_order_with_match( taker_base_asset_amount, maker_base_asset_amount, taker.orders[taker_order_index].has_limit_price(slot)?, - amm_lp_allowed_to_jit_make, )?; if jit_base_asset_amount > 0 { @@ -3338,129 +3310,6 @@ pub fn can_reward_user_with_perp_pnl(user: &mut Option<&mut User>, market_index: } } -pub fn attempt_burn_user_lp_shares_for_risk_reduction( - state: &State, - user: &mut User, - user_key: Pubkey, - margin_calc: MarginCalculation, - perp_market_map: &PerpMarketMap, - spot_market_map: &SpotMarketMap, - oracle_map: &mut OracleMap, - clock: &Clock, - market_index: u16, -) -> DriftResult { - let now = clock.unix_timestamp; - let time_since_last_liquidity_change: i64 = now.safe_sub(user.last_add_perp_lp_shares_ts)?; - // avoid spamming update if orders have already been set - if time_since_last_liquidity_change >= state.lp_cooldown_time.cast()? { - burn_user_lp_shares_for_risk_reduction( - state, - user, - user_key, - market_index, - margin_calc, - perp_market_map, - spot_market_map, - oracle_map, - clock, - )?; - user.last_add_perp_lp_shares_ts = now; - } - - Ok(()) -} - -pub fn burn_user_lp_shares_for_risk_reduction( - state: &State, - user: &mut User, - user_key: Pubkey, - market_index: u16, - margin_calc: MarginCalculation, - perp_market_map: &PerpMarketMap, - spot_market_map: &SpotMarketMap, - oracle_map: &mut OracleMap, - clock: &Clock, -) -> DriftResult { - let position_index = get_position_index(&user.perp_positions, market_index)?; - let is_lp = user.perp_positions[position_index].is_lp(); - if !is_lp { - return Ok(()); - } - - let mut market = perp_market_map.get_ref_mut(&market_index)?; - - let quote_oracle_id = spot_market_map - .get_ref(&market.quote_spot_market_index)? - .oracle_id(); - let quote_oracle_price = oracle_map.get_price_data("e_oracle_id)?.price; - - let oracle_price_data = oracle_map.get_price_data(&market.oracle_id())?; - - let oracle_price = if market.status == MarketStatus::Settlement { - market.expiry_price - } else { - oracle_price_data.price - }; - - let user_custom_margin_ratio = user.max_margin_ratio; - let (lp_shares_to_burn, base_asset_amount_to_close) = - calculate_lp_shares_to_burn_for_risk_reduction( - &user.perp_positions[position_index], - &market, - oracle_price, - quote_oracle_price, - margin_calc.margin_shortage()?, - user_custom_margin_ratio, - user.is_high_leverage_mode(MarginRequirementType::Initial), - )?; - - let (position_delta, pnl) = burn_lp_shares( - &mut user.perp_positions[position_index], - &mut market, - lp_shares_to_burn, - oracle_price, - )?; - - // emit LP record for shares removed - emit_stack::<_, { LPRecord::SIZE }>(LPRecord { - ts: clock.unix_timestamp, - action: LPAction::RemoveLiquidityDerisk, - user: user_key, - n_shares: lp_shares_to_burn, - market_index, - delta_base_asset_amount: position_delta.base_asset_amount, - delta_quote_asset_amount: position_delta.quote_asset_amount, - pnl, - })?; - - let direction_to_close = user.perp_positions[position_index].get_direction_to_close(); - - let params = OrderParams::get_close_perp_params( - &market, - direction_to_close, - base_asset_amount_to_close, - )?; - - drop(market); - - if user.has_room_for_new_order() { - controller::orders::place_perp_order( - state, - user, - user_key, - perp_market_map, - spot_market_map, - oracle_map, - &None, - clock, - params, - PlaceOrderOptions::default().explanation(OrderActionExplanation::DeriskLp), - )?; - } - - Ok(()) -} - pub fn pay_keeper_flat_reward_for_perps( user: &mut User, filler: Option<&mut User>, diff --git a/programs/drift/src/controller/orders/amm_jit_tests.rs b/programs/drift/src/controller/orders/amm_jit_tests.rs index 8f79f5dcd5..2385a9a546 100644 --- a/programs/drift/src/controller/orders/amm_jit_tests.rs +++ b/programs/drift/src/controller/orders/amm_jit_tests.rs @@ -1309,235 +1309,6 @@ pub mod amm_jit { assert!(quote_asset_amount_surplus > 0); } - #[test] - fn fulfill_with_amm_jit_taker_short_with_split_lps() { - let now = 0_i64; - let slot = 0_u64; - - let mut oracle_price = get_pyth_price(100, 6); - let oracle_price_key = - Pubkey::from_str("J83w4HKfqxwcq3BEMMkPFSppX3gqekLyLJBexebFVkix").unwrap(); - let pyth_program = crate::ids::pyth_program::id(); - create_account_info!( - oracle_price, - &oracle_price_key, - &pyth_program, - oracle_account_info - ); - let mut oracle_map = OracleMap::load_one(&oracle_account_info, slot, None).unwrap(); - - // net users are short - let mut market = PerpMarket { - amm: AMM { - base_asset_reserve: 100 * AMM_RESERVE_PRECISION, - quote_asset_reserve: 100 * AMM_RESERVE_PRECISION, - base_spread: 250, - long_spread: 125, - short_spread: 125, - max_spread: 20000, - base_asset_amount_with_amm: (AMM_RESERVE_PRECISION / 2) as i128, - base_asset_amount_long: (AMM_RESERVE_PRECISION / 2) as i128, - user_lp_shares: 20 * AMM_RESERVE_PRECISION, - sqrt_k: 100 * AMM_RESERVE_PRECISION, - peg_multiplier: 100 * PEG_PRECISION, - max_slippage_ratio: 50, - max_fill_reserve_fraction: 100, - order_step_size: 10000000, - order_tick_size: 1, - oracle: oracle_price_key, - amm_jit_intensity: 100, - concentration_coef: CONCENTRATION_PRECISION + 1, - historical_oracle_data: HistoricalOracleData { - last_oracle_price: (100 * PRICE_PRECISION) as i64, - last_oracle_price_twap: (100 * PRICE_PRECISION) as i64, - last_oracle_price_twap_5min: (100 * PRICE_PRECISION) as i64, - - ..HistoricalOracleData::default() - }, - - ..AMM::default() - }, - margin_ratio_initial: 1000, - margin_ratio_maintenance: 500, - status: MarketStatus::Initialized, - ..PerpMarket::default_test() - }; - market.amm.max_base_asset_reserve = u64::MAX as u128; - market.amm.min_base_asset_reserve = 0; - - let (new_ask_base_asset_reserve, new_ask_quote_asset_reserve) = - crate::math::amm_spread::calculate_spread_reserves(&market, PositionDirection::Long) - .unwrap(); - let (new_bid_base_asset_reserve, new_bid_quote_asset_reserve) = - crate::math::amm_spread::calculate_spread_reserves(&market, PositionDirection::Short) - .unwrap(); - market.amm.ask_base_asset_reserve = new_ask_base_asset_reserve; - market.amm.bid_base_asset_reserve = new_bid_base_asset_reserve; - market.amm.ask_quote_asset_reserve = new_ask_quote_asset_reserve; - market.amm.bid_quote_asset_reserve = new_bid_quote_asset_reserve; - - create_anchor_account_info!(market, PerpMarket, market_account_info); - let market_map = PerpMarketMap::load_one(&market_account_info, true).unwrap(); - - let mut spot_market = SpotMarket { - market_index: 0, - oracle_source: OracleSource::QuoteAsset, - cumulative_deposit_interest: SPOT_CUMULATIVE_INTEREST_PRECISION, - decimals: 6, - initial_asset_weight: SPOT_WEIGHT_PRECISION, - maintenance_asset_weight: SPOT_WEIGHT_PRECISION, - historical_oracle_data: HistoricalOracleData::default_price(QUOTE_PRECISION_I64), - ..SpotMarket::default() - }; - create_anchor_account_info!(spot_market, SpotMarket, spot_market_account_info); - let spot_market_map = SpotMarketMap::load_one(&spot_market_account_info, true).unwrap(); - - // taker wants to go long (would improve balance) - let mut taker = User { - orders: get_orders(Order { - market_index: 0, - status: OrderStatus::Open, - order_type: OrderType::Market, - direction: PositionDirection::Short, - base_asset_amount: BASE_PRECISION_U64, - slot: 0, - auction_start_price: 0, - auction_end_price: 100 * PRICE_PRECISION_I64, - auction_duration: 0, - ..Order::default() - }), - perp_positions: get_positions(PerpPosition { - market_index: 0, - open_orders: 1, - open_asks: -BASE_PRECISION_I64, - ..PerpPosition::default() - }), - spot_positions: get_spot_positions(SpotPosition { - market_index: 0, - balance_type: SpotBalanceType::Deposit, - scaled_balance: 100 * SPOT_BALANCE_PRECISION_U64, - ..SpotPosition::default() - }), - ..User::default() - }; - - let maker_key = Pubkey::from_str("My11111111111111111111111111111111111111113").unwrap(); - let maker_authority = - Pubkey::from_str("J83w4HKfqxwcq3BEMMkPFSppX3gqekLyLJBexebFVkix").unwrap(); - let mut maker = User { - authority: maker_authority, - orders: get_orders(Order { - market_index: 0, - post_only: true, - order_type: OrderType::Limit, - direction: PositionDirection::Long, - base_asset_amount: BASE_PRECISION_U64 / 2, - price: 100 * PRICE_PRECISION_U64, - ..Order::default() - }), - perp_positions: get_positions(PerpPosition { - market_index: 0, - open_orders: 1, - open_bids: BASE_PRECISION_I64 / 2, - ..PerpPosition::default() - }), - spot_positions: get_spot_positions(SpotPosition { - market_index: 0, - balance_type: SpotBalanceType::Deposit, - scaled_balance: 100 * 100 * SPOT_BALANCE_PRECISION_U64, - ..SpotPosition::default() - }), - ..User::default() - }; - - create_anchor_account_info!(maker, &maker_key, User, maker_account_info); - let makers_and_referrers = UserMap::load_one(&maker_account_info).unwrap(); - - let mut filler = User::default(); - - let fee_structure = get_fee_structure(); - - let (taker_key, _, filler_key) = get_user_keys(); - - let mut taker_stats = UserStats::default(); - - let mut maker_stats = UserStats { - authority: maker_authority, - ..UserStats::default() - }; - create_anchor_account_info!(maker_stats, UserStats, maker_stats_account_info); - let maker_and_referrer_stats = UserStatsMap::load_one(&maker_stats_account_info).unwrap(); - let mut filler_stats = UserStats::default(); - - assert_eq!(market.amm.total_fee, 0); - assert_eq!(market.amm.total_fee_minus_distributions, 0); - assert_eq!(market.amm.net_revenue_since_last_funding, 0); - assert_eq!(market.amm.total_mm_fee, 0); - assert_eq!(market.amm.total_fee_withdrawn, 0); - - let (base_asset_amount, _) = fulfill_perp_order( - &mut taker, - 0, - &taker_key, - &mut taker_stats, - &makers_and_referrers, - &maker_and_referrer_stats, - &[(maker_key, 0, 100 * PRICE_PRECISION_U64)], - &mut Some(&mut filler), - &filler_key, - &mut Some(&mut filler_stats), - None, - &spot_market_map, - &market_map, - &mut oracle_map, - &fee_structure, - 0, - Some(market.amm.historical_oracle_data.last_oracle_price), - now, - slot, - 0, - crate::state::perp_market::AMMAvailability::AfterMinDuration, - FillMode::Fill, - false, - ) - .unwrap(); - - // base is filled - assert!(base_asset_amount > 0); - assert_eq!(base_asset_amount, 1000000000); // 1 base - // let makers_and_referrers = UserMap::load_one(&maker_account_info).unwrap(); - - let market_after = market_map.get_ref(&0).unwrap(); - assert!( - market_after.amm.base_asset_amount_with_amm.abs() - < market.amm.base_asset_amount_with_amm.abs() - ); - - let quote_asset_amount_surplus = market_after.amm.total_mm_fee - market.amm.total_mm_fee; - assert!(quote_asset_amount_surplus > 0); - assert_eq!(market_after.amm.base_asset_amount_with_amm, 100000000); - assert_eq!(market_after.amm.base_asset_amount_long, 1000000000); - assert_eq!(market_after.amm.base_asset_amount_short, -1000000000); - - assert_eq!(market.amm.base_asset_amount_with_unsettled_lp, 0); - assert_eq!( - market_after.amm.base_asset_amount_with_unsettled_lp, - -100000000 - ); - - assert_eq!(market.amm.quote_asset_amount_per_lp, 0); - assert_eq!(market_after.amm.quote_asset_amount_per_lp, -497272); - - assert_eq!(market.amm.base_asset_amount_per_lp, 0); - assert_eq!(market_after.amm.base_asset_amount_per_lp, 5000000); - - assert_eq!(market_after.amm.total_exchange_fee, 32373); - assert_eq!(market_after.amm.total_fee_minus_distributions, 36039); - - crate::validation::perp_market::validate_perp_market(&market).unwrap(); - crate::validation::perp_market::validate_perp_market(&market_after).unwrap(); - } - #[test] fn fulfill_with_amm_jit_taker_short_unavailable_amm() { let now = 0_i64; diff --git a/programs/drift/src/controller/orders/amm_lp_jit_tests.rs b/programs/drift/src/controller/orders/amm_lp_jit_tests.rs index 2ef6a02bb7..e8346432b4 100644 --- a/programs/drift/src/controller/orders/amm_lp_jit_tests.rs +++ b/programs/drift/src/controller/orders/amm_lp_jit_tests.rs @@ -326,146 +326,10 @@ pub mod amm_lp_jit { BASE_PRECISION_U64, BASE_PRECISION_U64, false, - None, - ) - .unwrap(); - assert_eq!(amm_liquidity_split, AMMLiquiditySplit::ProtocolOwned); - assert_eq!(jit_base_asset_amount, 500000000); - } - - #[test] - fn amm_lp_jit_amm_lp_same_side_imbalanced() { - let oracle_price_key = - Pubkey::from_str("J83w4HKfqxwcq3BEMMkPFSppX3gqekLyLJBexebFVkix").unwrap(); - - let mut market = PerpMarket { - amm: AMM { - base_asset_reserve: 100 * AMM_RESERVE_PRECISION, - quote_asset_reserve: 100 * AMM_RESERVE_PRECISION, - base_asset_amount_per_lp: -505801343, // lps are long vs target, wants shorts - quote_asset_amount_per_lp: 10715933, - target_base_asset_amount_per_lp: -1000000000, - base_asset_amount_with_amm: -((AMM_RESERVE_PRECISION / 2) as i128), // amm is too long vs target, wants shorts - base_asset_amount_short: -((AMM_RESERVE_PRECISION / 2) as i128), - sqrt_k: 100 * AMM_RESERVE_PRECISION, - peg_multiplier: 100 * PEG_PRECISION, - max_slippage_ratio: 50, - max_fill_reserve_fraction: 100, - order_step_size: 1000, - order_tick_size: 1, - oracle: oracle_price_key, - amm_jit_intensity: 200, - base_spread: 20000, - long_spread: 20000, - short_spread: 20000, - historical_oracle_data: HistoricalOracleData { - last_oracle_price: (100 * PRICE_PRECISION) as i64, - last_oracle_price_twap: (100 * PRICE_PRECISION) as i64, - last_oracle_price_twap_5min: (100 * PRICE_PRECISION) as i64, - - ..HistoricalOracleData::default() - }, - user_lp_shares: 10 * AMM_RESERVE_PRECISION, // some lps exist - concentration_coef: CONCENTRATION_PRECISION + 1, - ..AMM::default() - }, - margin_ratio_initial: 1000, - margin_ratio_maintenance: 500, - status: MarketStatus::Initialized, - ..PerpMarket::default_test() - }; - market.amm.max_base_asset_reserve = u64::MAX as u128; - market.amm.min_base_asset_reserve = 0; - - // lp needs nearly 5 base to get to target - assert_eq!( - market.amm.imbalanced_base_asset_amount_with_lp().unwrap(), - 4_941_986_570 - ); - - let (new_ask_base_asset_reserve, new_ask_quote_asset_reserve) = - crate::math::amm_spread::calculate_spread_reserves(&market, PositionDirection::Long) - .unwrap(); - let (new_bid_base_asset_reserve, new_bid_quote_asset_reserve) = - crate::math::amm_spread::calculate_spread_reserves(&market, PositionDirection::Short) - .unwrap(); - market.amm.ask_base_asset_reserve = new_ask_base_asset_reserve; - market.amm.bid_base_asset_reserve = new_bid_base_asset_reserve; - market.amm.ask_quote_asset_reserve = new_ask_quote_asset_reserve; - market.amm.bid_quote_asset_reserve = new_bid_quote_asset_reserve; - - let amm_inventory_pct = calculate_inventory_liquidity_ratio( - market.amm.base_asset_amount_with_amm, - market.amm.base_asset_reserve, - market.amm.min_base_asset_reserve, - market.amm.max_base_asset_reserve, - ) - .unwrap(); - assert_eq!(amm_inventory_pct, PERCENTAGE_PRECISION_I128 / 200); // .5% of amm inventory is in position - - // maker order satisfies taker, vAMM doing match - let (jit_base_asset_amount, amm_liquidity_split) = calculate_amm_jit_liquidity( - &mut market, - PositionDirection::Long, - 100 * PRICE_PRECISION_U64, - Some(100 * PRICE_PRECISION_I64), - BASE_PRECISION_U64, - BASE_PRECISION_U64, - BASE_PRECISION_U64, - false, - None, - ) - .unwrap(); - assert_eq!(amm_liquidity_split, AMMLiquiditySplit::Shared); - assert_eq!(jit_base_asset_amount, 500000000); - - // taker order is heading to vAMM - let (jit_base_asset_amount, amm_liquidity_split) = calculate_amm_jit_liquidity( - &mut market, - PositionDirection::Long, - 100 * PRICE_PRECISION_U64, - Some(100 * PRICE_PRECISION_I64), - BASE_PRECISION_U64, - BASE_PRECISION_U64 * 2, - BASE_PRECISION_U64, - false, - None, ) .unwrap(); assert_eq!(amm_liquidity_split, AMMLiquiditySplit::ProtocolOwned); - assert_eq!(jit_base_asset_amount, 0); // its coming anyways - - // no jit for additional long (more shorts for amm) - let (jit_base_asset_amount, amm_liquidity_split) = calculate_amm_jit_liquidity( - &mut market, - PositionDirection::Long, - 100 * PRICE_PRECISION_U64, - Some(100 * PRICE_PRECISION_I64), - BASE_PRECISION_U64, - BASE_PRECISION_U64 * 100, - BASE_PRECISION_U64 * 100, - false, - None, - ) - .unwrap(); - assert_eq!(amm_liquidity_split, AMMLiquiditySplit::Shared); assert_eq!(jit_base_asset_amount, 500000000); - - // wrong direction (increases lp and vamm inventory) - let (jit_base_asset_amount, amm_liquidity_split) = calculate_amm_jit_liquidity( - &mut market, - PositionDirection::Short, - 100 * PRICE_PRECISION_U64, - Some(100 * PRICE_PRECISION_I64), - BASE_PRECISION_U64, - BASE_PRECISION_U64, - BASE_PRECISION_U64, - false, - None, - ) - .unwrap(); - assert_eq!(amm_liquidity_split, AMMLiquiditySplit::ProtocolOwned); - assert_eq!(jit_base_asset_amount, 0); } #[test] @@ -676,654 +540,6 @@ pub mod amm_lp_jit { assert_eq!(market_after.amm.total_exchange_fee, 7500); } - #[test] - fn fulfill_with_amm_lp_jit_small_maker_order() { - let now = 0_i64; - let slot = 5_u64; - - let mut oracle_price = get_pyth_price(100, 6); - let oracle_price_key = - Pubkey::from_str("J83w4HKfqxwcq3BEMMkPFSppX3gqekLyLJBexebFVkix").unwrap(); - let pyth_program = crate::ids::pyth_program::id(); - create_account_info!( - oracle_price, - &oracle_price_key, - &pyth_program, - oracle_account_info - ); - let mut oracle_map = OracleMap::load_one(&oracle_account_info, slot, None).unwrap(); - - // net users are short - let mut market = PerpMarket { - amm: AMM { - base_asset_reserve: 100 * AMM_RESERVE_PRECISION, - quote_asset_reserve: 100 * AMM_RESERVE_PRECISION, - base_asset_amount_per_lp: -505801343, - quote_asset_amount_per_lp: 10715933, - target_base_asset_amount_per_lp: -1000000000, - bid_base_asset_reserve: 101 * AMM_RESERVE_PRECISION, - bid_quote_asset_reserve: 99 * AMM_RESERVE_PRECISION, - ask_base_asset_reserve: 99 * AMM_RESERVE_PRECISION, - ask_quote_asset_reserve: 101 * AMM_RESERVE_PRECISION, - base_asset_amount_with_amm: -((AMM_RESERVE_PRECISION / 2) as i128), - base_asset_amount_short: -((AMM_RESERVE_PRECISION / 2) as i128), - sqrt_k: 100 * AMM_RESERVE_PRECISION, - peg_multiplier: 90 * PEG_PRECISION, - max_slippage_ratio: 50, - max_fill_reserve_fraction: 100, - order_step_size: 10000000, - order_tick_size: 1, - oracle: oracle_price_key, - amm_jit_intensity: 200, - historical_oracle_data: HistoricalOracleData { - last_oracle_price: (100 * PRICE_PRECISION) as i64, - last_oracle_price_twap: (100 * PRICE_PRECISION) as i64, - last_oracle_price_twap_5min: (100 * PRICE_PRECISION) as i64, - - ..HistoricalOracleData::default() - }, - user_lp_shares: 10 * AMM_RESERVE_PRECISION, // some lps exist - concentration_coef: CONCENTRATION_PRECISION + 1, - ..AMM::default() - }, - margin_ratio_initial: 1000, - margin_ratio_maintenance: 500, - status: MarketStatus::Initialized, - ..PerpMarket::default_test() - }; - market.amm.max_base_asset_reserve = u64::MAX as u128; - market.amm.min_base_asset_reserve = 0; - - create_anchor_account_info!(market, PerpMarket, market_account_info); - let market_map = PerpMarketMap::load_one(&market_account_info, true).unwrap(); - - let mut spot_market = SpotMarket { - market_index: 0, - oracle_source: OracleSource::QuoteAsset, - cumulative_deposit_interest: SPOT_CUMULATIVE_INTEREST_PRECISION, - decimals: 6, - initial_asset_weight: SPOT_WEIGHT_PRECISION, - maintenance_asset_weight: SPOT_WEIGHT_PRECISION, - historical_oracle_data: HistoricalOracleData::default_price(QUOTE_PRECISION_I64), - ..SpotMarket::default() - }; - create_anchor_account_info!(spot_market, SpotMarket, spot_market_account_info); - let spot_market_map = SpotMarketMap::load_one(&spot_market_account_info, true).unwrap(); - - // taker wants to go long (would improve balance) - let mut taker = User { - orders: get_orders(Order { - market_index: 0, - status: OrderStatus::Open, - order_type: OrderType::Market, - direction: PositionDirection::Long, - base_asset_amount: BASE_PRECISION_U64 / 2 + BASE_PRECISION_U64 * 2, // if amm takes half it would flip - slot: 0, - price: 100 * PRICE_PRECISION as u64, - auction_start_price: 0, - auction_end_price: 200 * PRICE_PRECISION_I64, - auction_duration: 10, - ..Order::default() - }), - perp_positions: get_positions(PerpPosition { - market_index: 0, - open_orders: 1, - open_bids: BASE_PRECISION_I64 / 2 + BASE_PRECISION_I64 * 2, - ..PerpPosition::default() - }), - spot_positions: get_spot_positions(SpotPosition { - market_index: 0, - balance_type: SpotBalanceType::Deposit, - scaled_balance: 1000 * SPOT_BALANCE_PRECISION_U64, - ..SpotPosition::default() - }), - ..User::default() - }; - - let maker_key = Pubkey::from_str("My11111111111111111111111111111111111111113").unwrap(); - let maker_authority = - Pubkey::from_str("J83w4HKfqxwcq3BEMMkPFSppX3gqekLyLJBexebFVkix").unwrap(); - let mut maker = User { - authority: maker_authority, - orders: get_orders(Order { - market_index: 0, - post_only: true, - order_type: OrderType::Limit, - direction: PositionDirection::Short, - base_asset_amount: BASE_PRECISION_U64 + BASE_PRECISION_U64 / 2, // maker wants full = amm wants BASE_PERCISION - price: 99 * PRICE_PRECISION_U64, - ..Order::default() - }), - perp_positions: get_positions(PerpPosition { - market_index: 0, - open_orders: 1, - open_asks: -(BASE_PRECISION_I64 + BASE_PRECISION_I64 / 2), - ..PerpPosition::default() - }), - spot_positions: get_spot_positions(SpotPosition { - market_index: 0, - balance_type: SpotBalanceType::Deposit, - scaled_balance: 100 * 100 * SPOT_BALANCE_PRECISION_U64, - ..SpotPosition::default() - }), - ..User::default() - }; - create_anchor_account_info!(maker, &maker_key, User, maker_account_info); - let makers_and_referrers = UserMap::load_one(&maker_account_info).unwrap(); - - let mut filler = User::default(); - - let fee_structure = get_fee_structure(); - - let (taker_key, _, filler_key) = get_user_keys(); - - let mut taker_stats = UserStats::default(); - - let mut maker_stats = UserStats { - authority: maker_authority, - ..UserStats::default() - }; - create_anchor_account_info!(maker_stats, UserStats, maker_stats_account_info); - let maker_and_referrer_stats = UserStatsMap::load_one(&maker_stats_account_info).unwrap(); - - let mut filler_stats = UserStats::default(); - - assert_eq!(market.amm.total_fee, 0); - assert_eq!(market.amm.total_fee_minus_distributions, 0); - assert_eq!(market.amm.net_revenue_since_last_funding, 0); - assert_eq!(market.amm.total_mm_fee, 0); - assert_eq!(market.amm.total_fee_withdrawn, 0); - - fulfill_perp_order( - &mut taker, - 0, - &taker_key, - &mut taker_stats, - &makers_and_referrers, - &maker_and_referrer_stats, - &[(maker_key, 0, 99 * PRICE_PRECISION_U64)], - &mut Some(&mut filler), - &filler_key, - &mut Some(&mut filler_stats), - None, - &spot_market_map, - &market_map, - &mut oracle_map, - &fee_structure, - 0, - Some(PRICE_PRECISION_I64), - now, - slot, - 10, - crate::state::perp_market::AMMAvailability::AfterMinDuration, - FillMode::Fill, - false, - ) - .unwrap(); - - // maker got full size - let maker = makers_and_referrers.get_ref_mut(&maker_key).unwrap(); - let maker_position = &maker.perp_positions[0]; - assert_eq!( - maker_position.base_asset_amount, - -(BASE_PRECISION_I64 + BASE_PRECISION_I64 / 2) - ); - - // nets to zero - let market_after = market_map.get_ref(&0).unwrap(); - - // make sure lps got more - assert_eq!(market_after.amm.base_asset_amount_per_lp, -510801343); - assert_eq!(market_after.amm.base_asset_amount_with_amm, -50000000); - assert_eq!( - market_after.amm.base_asset_amount_with_unsettled_lp, - 50000000 - ); - - assert!(market_after.amm.base_asset_amount_per_lp != market.amm.base_asset_amount_per_lp); - assert!(market_after.amm.quote_asset_amount_per_lp != market.amm.quote_asset_amount_per_lp); - assert_eq!(market_after.amm.total_fee_minus_distributions, 2488712); //2510987 would-be w/o LP - assert_eq!(market_after.amm.total_exchange_fee, 47025); - } - - #[test] - fn fulfill_with_amm_lp_jit_taker_long_max_amount() { - let now = 0_i64; - let slot = 0_u64; - - let mut oracle_price = get_pyth_price(100, 6); - let oracle_price_key = - Pubkey::from_str("J83w4HKfqxwcq3BEMMkPFSppX3gqekLyLJBexebFVkix").unwrap(); - let pyth_program = crate::ids::pyth_program::id(); - create_account_info!( - oracle_price, - &oracle_price_key, - &pyth_program, - oracle_account_info - ); - let mut oracle_map = OracleMap::load_one(&oracle_account_info, slot, None).unwrap(); - - // net users are short - let mut market = PerpMarket { - amm: AMM { - base_asset_reserve: 100 * AMM_RESERVE_PRECISION, - quote_asset_reserve: 100 * AMM_RESERVE_PRECISION, - base_asset_amount_per_lp: -505801343, - quote_asset_amount_per_lp: 10715933, - target_base_asset_amount_per_lp: -1000000000, - bid_base_asset_reserve: 101 * AMM_RESERVE_PRECISION, - bid_quote_asset_reserve: 99 * AMM_RESERVE_PRECISION, - ask_base_asset_reserve: 99 * AMM_RESERVE_PRECISION, - ask_quote_asset_reserve: 101 * AMM_RESERVE_PRECISION, - base_asset_amount_with_amm: -((AMM_RESERVE_PRECISION / 2) as i128), - base_asset_amount_short: -((AMM_RESERVE_PRECISION / 2) as i128), - sqrt_k: 100 * AMM_RESERVE_PRECISION, - peg_multiplier: 100 * PEG_PRECISION, - max_slippage_ratio: 50, - max_fill_reserve_fraction: 100, - order_step_size: 10000000, - order_tick_size: 1, - oracle: oracle_price_key, - amm_jit_intensity: 200, - historical_oracle_data: HistoricalOracleData { - last_oracle_price: (100 * PRICE_PRECISION) as i64, - last_oracle_price_twap: (100 * PRICE_PRECISION) as i64, - last_oracle_price_twap_5min: (100 * PRICE_PRECISION) as i64, - - ..HistoricalOracleData::default() - }, - user_lp_shares: 10 * AMM_RESERVE_PRECISION, // some lps exist - concentration_coef: CONCENTRATION_PRECISION + 1, - ..AMM::default() - }, - margin_ratio_initial: 1000, - margin_ratio_maintenance: 500, - status: MarketStatus::Initialized, - ..PerpMarket::default_test() - }; - market.amm.max_base_asset_reserve = u64::MAX as u128; - market.amm.min_base_asset_reserve = 0; - - create_anchor_account_info!(market, PerpMarket, market_account_info); - let market_map = PerpMarketMap::load_one(&market_account_info, true).unwrap(); - - let mut spot_market = SpotMarket { - market_index: 0, - oracle_source: OracleSource::QuoteAsset, - cumulative_deposit_interest: SPOT_CUMULATIVE_INTEREST_PRECISION, - decimals: 6, - initial_asset_weight: SPOT_WEIGHT_PRECISION, - maintenance_asset_weight: SPOT_WEIGHT_PRECISION, - historical_oracle_data: HistoricalOracleData::default_price(QUOTE_PRECISION_I64), - ..SpotMarket::default() - }; - create_anchor_account_info!(spot_market, SpotMarket, spot_market_account_info); - let spot_market_map = SpotMarketMap::load_one(&spot_market_account_info, true).unwrap(); - - // taker wants to go long (would improve balance) - let mut taker = User { - orders: get_orders(Order { - market_index: 0, - status: OrderStatus::Open, - order_type: OrderType::Market, - direction: PositionDirection::Long, - base_asset_amount: BASE_PRECISION_U64 * 2, // if amm takes half it would flip - slot: 0, - price: 100 * PRICE_PRECISION as u64, - auction_start_price: 0, - auction_end_price: 100 * PRICE_PRECISION_I64, - auction_duration: 0, - ..Order::default() - }), - perp_positions: get_positions(PerpPosition { - market_index: 0, - open_orders: 1, - open_bids: BASE_PRECISION_I64 * 2, - ..PerpPosition::default() - }), - spot_positions: get_spot_positions(SpotPosition { - market_index: 0, - balance_type: SpotBalanceType::Deposit, - scaled_balance: 100 * SPOT_BALANCE_PRECISION_U64, - ..SpotPosition::default() - }), - ..User::default() - }; - - let maker_key = Pubkey::from_str("My11111111111111111111111111111111111111113").unwrap(); - let maker_authority = - Pubkey::from_str("J83w4HKfqxwcq3BEMMkPFSppX3gqekLyLJBexebFVkix").unwrap(); - let mut maker = User { - authority: maker_authority, - orders: get_orders(Order { - market_index: 0, - post_only: true, - order_type: OrderType::Limit, - direction: PositionDirection::Short, - base_asset_amount: BASE_PRECISION_U64 * 2, // maker wants full = amm wants BASE_PERCISION - price: 99 * PRICE_PRECISION_U64, - ..Order::default() - }), - perp_positions: get_positions(PerpPosition { - market_index: 0, - open_orders: 1, - open_asks: -BASE_PRECISION_I64 * 2, - ..PerpPosition::default() - }), - spot_positions: get_spot_positions(SpotPosition { - market_index: 0, - balance_type: SpotBalanceType::Deposit, - scaled_balance: 100 * 100 * SPOT_BALANCE_PRECISION_U64, - ..SpotPosition::default() - }), - ..User::default() - }; - create_anchor_account_info!(maker, &maker_key, User, maker_account_info); - let makers_and_referrers = UserMap::load_one(&maker_account_info).unwrap(); - - let mut filler = User::default(); - - let fee_structure = get_fee_structure(); - - let (taker_key, _, filler_key) = get_user_keys(); - - let mut taker_stats = UserStats::default(); - - let mut maker_stats = UserStats { - authority: maker_authority, - ..UserStats::default() - }; - create_anchor_account_info!(maker_stats, UserStats, maker_stats_account_info); - let maker_and_referrer_stats = UserStatsMap::load_one(&maker_stats_account_info).unwrap(); - - let mut filler_stats = UserStats::default(); - - assert_eq!(market.amm.total_fee, 0); - assert_eq!(market.amm.total_fee_minus_distributions, 0); - assert_eq!(market.amm.net_revenue_since_last_funding, 0); - assert_eq!(market.amm.total_mm_fee, 0); - assert_eq!(market.amm.total_fee_withdrawn, 0); - - fulfill_perp_order( - &mut taker, - 0, - &taker_key, - &mut taker_stats, - &makers_and_referrers, - &maker_and_referrer_stats, - &[(maker_key, 0, 99 * PRICE_PRECISION_U64)], - &mut Some(&mut filler), - &filler_key, - &mut Some(&mut filler_stats), - None, - &spot_market_map, - &market_map, - &mut oracle_map, - &fee_structure, - 0, - Some(PRICE_PRECISION_I64), - now, - slot, - 10, - crate::state::perp_market::AMMAvailability::AfterMinDuration, - FillMode::Fill, - false, - ) - .unwrap(); - - assert_eq!(market.amm.base_asset_amount_with_amm, -500000000); - assert_eq!(market.amm.base_asset_amount_per_lp, -505801343); - - let market_after = market_map.get_ref(&0).unwrap(); - - // make sure moves closer TODO - assert_eq!(market_after.amm.base_asset_amount_per_lp, -510801343); - - // nets to zero - assert_eq!(market_after.amm.base_asset_amount_with_amm, -50000000); - assert_eq!( - market_after.amm.base_asset_amount_with_unsettled_lp, - 50000000 - ); - - let maker = makers_and_referrers.get_ref_mut(&maker_key).unwrap(); - let maker_position: &PerpPosition = &maker.perp_positions[0]; - // maker got (full - net_baa) - assert_eq!( - maker_position.base_asset_amount as i128, - -BASE_PRECISION_I128 * 2 - market.amm.base_asset_amount_with_amm - ); - - let taker_position: &PerpPosition = &taker.perp_positions[0]; - assert_eq!( - taker_position.base_asset_amount as i128, - BASE_PRECISION_I128 * 2 - ); - } - - #[test] - fn fulfill_with_amm_lp_only_jit_taker_long_max_amount() { - let now = 0_i64; - let slot = 0_u64; - - let mut oracle_price = get_pyth_price(100, 6); - let oracle_price_key = - Pubkey::from_str("J83w4HKfqxwcq3BEMMkPFSppX3gqekLyLJBexebFVkix").unwrap(); - let pyth_program = crate::ids::pyth_program::id(); - create_account_info!( - oracle_price, - &oracle_price_key, - &pyth_program, - oracle_account_info - ); - let mut oracle_map = OracleMap::load_one(&oracle_account_info, slot, None).unwrap(); - - // net users are short - let mut market = PerpMarket { - amm: AMM { - base_asset_reserve: 100 * AMM_RESERVE_PRECISION, - quote_asset_reserve: 100 * AMM_RESERVE_PRECISION, - terminal_quote_asset_reserve: 100 * AMM_RESERVE_PRECISION, - base_asset_amount_per_lp: -505801343, - quote_asset_amount_per_lp: 10715933, - target_base_asset_amount_per_lp: -1000000000, - bid_base_asset_reserve: 101 * AMM_RESERVE_PRECISION, - bid_quote_asset_reserve: 99 * AMM_RESERVE_PRECISION, - ask_base_asset_reserve: 99 * AMM_RESERVE_PRECISION, - ask_quote_asset_reserve: 101 * AMM_RESERVE_PRECISION, - base_asset_amount_with_amm: 0, - base_asset_amount_long: ((166 * AMM_RESERVE_PRECISION / 2) as i128), - base_asset_amount_short: -((166 * AMM_RESERVE_PRECISION / 2) as i128), - sqrt_k: 100 * AMM_RESERVE_PRECISION, - peg_multiplier: 100 * PEG_PRECISION, - max_slippage_ratio: 50, - max_fill_reserve_fraction: 100, - order_step_size: 10000000, - order_tick_size: 1, - oracle: oracle_price_key, - amm_jit_intensity: 200, - historical_oracle_data: HistoricalOracleData { - last_oracle_price: (100 * PRICE_PRECISION) as i64, - last_oracle_price_twap: (100 * PRICE_PRECISION) as i64, - last_oracle_price_twap_5min: (100 * PRICE_PRECISION) as i64, - - ..HistoricalOracleData::default() - }, - user_lp_shares: 10 * AMM_RESERVE_PRECISION, // some lps exist - concentration_coef: CONCENTRATION_PRECISION + 1, - ..AMM::default() - }, - margin_ratio_initial: 1000, - margin_ratio_maintenance: 500, - status: MarketStatus::Initialized, - ..PerpMarket::default_test() - }; - market.amm.max_base_asset_reserve = u64::MAX as u128; - market.amm.min_base_asset_reserve = 0; - - create_anchor_account_info!(market, PerpMarket, market_account_info); - let market_map = PerpMarketMap::load_one(&market_account_info, true).unwrap(); - - let mut spot_market = SpotMarket { - market_index: 0, - oracle_source: OracleSource::QuoteAsset, - cumulative_deposit_interest: SPOT_CUMULATIVE_INTEREST_PRECISION, - decimals: 6, - initial_asset_weight: SPOT_WEIGHT_PRECISION, - maintenance_asset_weight: SPOT_WEIGHT_PRECISION, - historical_oracle_data: HistoricalOracleData::default_price(QUOTE_PRECISION_I64), - ..SpotMarket::default() - }; - create_anchor_account_info!(spot_market, SpotMarket, spot_market_account_info); - let spot_market_map = SpotMarketMap::load_one(&spot_market_account_info, true).unwrap(); - - // taker wants to go long (would improve balance) - let mut taker = User { - orders: get_orders(Order { - market_index: 0, - status: OrderStatus::Open, - order_type: OrderType::Market, - direction: PositionDirection::Long, - base_asset_amount: BASE_PRECISION_U64 * 2, // if amm takes half it would flip - slot: 0, - price: 100 * PRICE_PRECISION as u64, - auction_start_price: 0, - auction_end_price: 100 * PRICE_PRECISION_I64, - auction_duration: 0, - ..Order::default() - }), - perp_positions: get_positions(PerpPosition { - market_index: 0, - open_orders: 1, - open_bids: BASE_PRECISION_I64 * 2, - ..PerpPosition::default() - }), - spot_positions: get_spot_positions(SpotPosition { - market_index: 0, - balance_type: SpotBalanceType::Deposit, - scaled_balance: 100 * SPOT_BALANCE_PRECISION_U64, - ..SpotPosition::default() - }), - ..User::default() - }; - - let maker_key = Pubkey::from_str("My11111111111111111111111111111111111111113").unwrap(); - let maker_authority = - Pubkey::from_str("J83w4HKfqxwcq3BEMMkPFSppX3gqekLyLJBexebFVkix").unwrap(); - let mut maker = User { - authority: maker_authority, - orders: get_orders(Order { - market_index: 0, - post_only: true, - order_type: OrderType::Limit, - direction: PositionDirection::Short, - base_asset_amount: BASE_PRECISION_U64 * 2, // maker wants full = amm wants BASE_PERCISION - price: 99 * PRICE_PRECISION_U64, - ..Order::default() - }), - perp_positions: get_positions(PerpPosition { - market_index: 0, - open_orders: 1, - open_asks: -BASE_PRECISION_I64 * 2, - ..PerpPosition::default() - }), - spot_positions: get_spot_positions(SpotPosition { - market_index: 0, - balance_type: SpotBalanceType::Deposit, - scaled_balance: 100 * 100 * SPOT_BALANCE_PRECISION_U64, - ..SpotPosition::default() - }), - ..User::default() - }; - create_anchor_account_info!(maker, &maker_key, User, maker_account_info); - let makers_and_referrers = UserMap::load_one(&maker_account_info).unwrap(); - - let mut filler = User::default(); - - let fee_structure = get_fee_structure(); - - let (taker_key, _, filler_key) = get_user_keys(); - - let mut taker_stats = UserStats::default(); - - let mut maker_stats = UserStats { - authority: maker_authority, - ..UserStats::default() - }; - create_anchor_account_info!(maker_stats, UserStats, maker_stats_account_info); - let maker_and_referrer_stats = UserStatsMap::load_one(&maker_stats_account_info).unwrap(); - - let mut filler_stats = UserStats::default(); - - assert_eq!(market.amm.total_fee, 0); - assert_eq!(market.amm.total_fee_minus_distributions, 0); - assert_eq!(market.amm.net_revenue_since_last_funding, 0); - assert_eq!(market.amm.total_mm_fee, 0); - assert_eq!(market.amm.total_fee_withdrawn, 0); - - fulfill_perp_order( - &mut taker, - 0, - &taker_key, - &mut taker_stats, - &makers_and_referrers, - &maker_and_referrer_stats, - &[(maker_key, 0, 99 * PRICE_PRECISION_U64)], - &mut Some(&mut filler), - &filler_key, - &mut Some(&mut filler_stats), - None, - &spot_market_map, - &market_map, - &mut oracle_map, - &fee_structure, - 0, - Some(PRICE_PRECISION_I64), - now, - slot, - 10, - crate::state::perp_market::AMMAvailability::AfterMinDuration, - FillMode::Fill, - false, - ) - .unwrap(); - - assert_eq!(market.amm.base_asset_amount_per_lp, -505801343); - assert_eq!(market.amm.quote_asset_amount_per_lp, 10715933); - - let market_after = market_map.get_ref(&0).unwrap(); - - // make sure moves closer - assert_eq!(market_after.amm.base_asset_amount_per_lp, -605801343); - assert_eq!(market_after.amm.quote_asset_amount_per_lp, 20619497); - - // nets to zero - assert_eq!(market_after.amm.base_asset_amount_with_amm, 0); - assert_eq!(market_after.amm.base_asset_amount_long, 85000000000); - assert_eq!(market_after.amm.base_asset_amount_short, -84000000000); - - assert_eq!( - market_after.amm.base_asset_amount_with_unsettled_lp, - 1000000000 - ); - validate_perp_market(&market).unwrap(); - validate_perp_market(&market_after).unwrap(); - - let maker = makers_and_referrers.get_ref_mut(&maker_key).unwrap(); - let maker_position: &PerpPosition = &maker.perp_positions[0]; - // maker got (full - net_unsettled_lp) - assert_eq!( - maker_position.base_asset_amount as i128, - -(BASE_PRECISION_I128 * 2) + market_after.amm.base_asset_amount_with_unsettled_lp - ); - - let taker_position: &PerpPosition = &taker.perp_positions[0]; - assert_eq!( - taker_position.base_asset_amount as i128, - BASE_PRECISION_I128 * 2 - ); - } - #[test] fn fulfill_with_amm_lp_jit_taker_short_max_amount() { let now = 0_i64; diff --git a/programs/drift/src/controller/orders/tests.rs b/programs/drift/src/controller/orders/tests.rs index 96038e98c7..b5e681f090 100644 --- a/programs/drift/src/controller/orders/tests.rs +++ b/programs/drift/src/controller/orders/tests.rs @@ -486,7 +486,6 @@ pub mod fulfill_order_with_maker_order { &fee_structure, &mut get_oracle_map(), false, - None, ) .unwrap(); @@ -610,7 +609,6 @@ pub mod fulfill_order_with_maker_order { &fee_structure, &mut get_oracle_map(), false, - None, ) .unwrap(); @@ -734,7 +732,6 @@ pub mod fulfill_order_with_maker_order { &fee_structure, &mut get_oracle_map(), false, - None, ) .unwrap(); @@ -858,7 +855,6 @@ pub mod fulfill_order_with_maker_order { &fee_structure, &mut get_oracle_map(), false, - None, ) .unwrap(); @@ -981,7 +977,6 @@ pub mod fulfill_order_with_maker_order { &fee_structure, &mut get_oracle_map(), false, - None, ) .unwrap(); @@ -1071,7 +1066,6 @@ pub mod fulfill_order_with_maker_order { &fee_structure, &mut get_oracle_map(), false, - None, ) .unwrap(); @@ -1162,7 +1156,6 @@ pub mod fulfill_order_with_maker_order { &fee_structure, &mut get_oracle_map(), false, - None, ) .unwrap(); @@ -1253,7 +1246,6 @@ pub mod fulfill_order_with_maker_order { &fee_structure, &mut get_oracle_map(), false, - None, ) .unwrap(); @@ -1344,7 +1336,6 @@ pub mod fulfill_order_with_maker_order { &fee_structure, &mut get_oracle_map(), false, - None, ) .unwrap(); @@ -1455,7 +1446,6 @@ pub mod fulfill_order_with_maker_order { &fee_structure, &mut get_oracle_map(), false, - None, ) .unwrap(); @@ -1571,7 +1561,6 @@ pub mod fulfill_order_with_maker_order { &fee_structure, &mut get_oracle_map(), false, - None, ) .unwrap(); @@ -1692,7 +1681,6 @@ pub mod fulfill_order_with_maker_order { &fee_structure, &mut get_oracle_map(), false, - None, ) .unwrap(); @@ -1814,7 +1802,6 @@ pub mod fulfill_order_with_maker_order { &fee_structure, &mut get_oracle_map(), false, - None, ) .unwrap(); @@ -1960,7 +1947,6 @@ pub mod fulfill_order_with_maker_order { &fee_structure, &mut get_oracle_map(), false, - None, ) .unwrap(); @@ -2081,7 +2067,6 @@ pub mod fulfill_order_with_maker_order { &fee_structure, &mut get_oracle_map(), false, - None, ) .unwrap(); @@ -2212,7 +2197,6 @@ pub mod fulfill_order_with_maker_order { &fee_structure, &mut oracle_map, false, - None, ) .unwrap(); @@ -2364,7 +2348,6 @@ pub mod fulfill_order_with_maker_order { &fee_structure, &mut oracle_map, false, - None, ) .unwrap(); @@ -2514,7 +2497,6 @@ pub mod fulfill_order_with_maker_order { &fee_structure, &mut oracle_map, false, - None, ) .unwrap(); @@ -2665,7 +2647,6 @@ pub mod fulfill_order_with_maker_order { &fee_structure, &mut oracle_map, false, - None, ) .unwrap(); @@ -2797,7 +2778,6 @@ pub mod fulfill_order_with_maker_order { &fee_structure, &mut get_oracle_map(), false, - None, ) .unwrap(); @@ -2928,7 +2908,6 @@ pub mod fulfill_order_with_maker_order { &fee_structure, &mut get_oracle_map(), false, - None, ) .unwrap(); diff --git a/programs/drift/src/controller/pnl.rs b/programs/drift/src/controller/pnl.rs index 5514123de8..166030d6ee 100644 --- a/programs/drift/src/controller/pnl.rs +++ b/programs/drift/src/controller/pnl.rs @@ -1,9 +1,6 @@ use crate::controller::amm::{update_pnl_pool_and_user_balance, update_pool_balances}; use crate::controller::funding::settle_funding_payment; -use crate::controller::orders::{ - attempt_burn_user_lp_shares_for_risk_reduction, cancel_orders, - validate_market_within_price_band, -}; +use crate::controller::orders::{cancel_orders, validate_market_within_price_band}; use crate::controller::position::{ get_position_index, update_position_and_market, update_quote_asset_amount, update_quote_asset_and_break_even_amount, update_settled_pnl, PositionDelta, @@ -81,7 +78,7 @@ pub fn settle_pnl( validate_market_within_price_band(&market, state, oracle_price)?; - crate::controller::lp::settle_funding_payment_then_lp(user, user_key, &mut market, now)?; + settle_funding_payment(user, user_key, &mut market, now)?; drop(market); @@ -89,48 +86,7 @@ pub fn settle_pnl( let unrealized_pnl = user.perp_positions[position_index].get_unrealized_pnl(oracle_price)?; // cannot settle negative pnl this way on a user who is in liquidation territory - if user.perp_positions[position_index].is_lp() && !user.is_advanced_lp() { - let margin_calc = calculate_margin_requirement_and_total_collateral_and_liability_info( - user, - perp_market_map, - spot_market_map, - oracle_map, - MarginContext::standard(MarginRequirementType::Initial) - .margin_buffer(state.liquidation_margin_buffer_ratio), - )?; - - if !margin_calc.meets_margin_requirement() { - msg!("market={} lp does not meet initial margin requirement, attempting to burn shares for risk reduction", - market_index); - attempt_burn_user_lp_shares_for_risk_reduction( - state, - user, - *user_key, - margin_calc, - perp_market_map, - spot_market_map, - oracle_map, - clock, - market_index, - )?; - - // if the unrealized pnl is negative, return early after trying to burn shares - if unrealized_pnl < 0 - && !(meets_settle_pnl_maintenance_margin_requirement( - user, - perp_market_map, - spot_market_map, - oracle_map, - )?) - { - msg!( - "Unable to settle market={} negative pnl as user is in liquidation territory", - market_index - ); - return Ok(()); - } - } - } else if unrealized_pnl < 0 { + if unrealized_pnl < 0 { // may already be cached let meets_margin_requirement = match meets_margin_requirement { Some(meets_margin_requirement) => meets_margin_requirement, @@ -462,12 +418,6 @@ pub fn settle_expired_position( "User must first cancel open orders for expired market" )?; - validate!( - user.perp_positions[position_index].lp_shares == 0, - ErrorCode::PerpMarketSettlementUserHasActiveLP, - "User must first burn lp shares for expired market" - )?; - let base_asset_value = calculate_base_asset_value_with_expiry_price( &user.perp_positions[position_index], perp_market.expiry_price, @@ -479,7 +429,6 @@ pub fn settle_expired_position( let position_delta = PositionDelta { quote_asset_amount: base_asset_value, base_asset_amount: -user.perp_positions[position_index].base_asset_amount, - remainder_base_asset_amount: None, }; update_position_and_market( diff --git a/programs/drift/src/controller/position.rs b/programs/drift/src/controller/position.rs index 53b76188d4..d96bb10f16 100644 --- a/programs/drift/src/controller/position.rs +++ b/programs/drift/src/controller/position.rs @@ -74,21 +74,11 @@ pub fn get_position_index(user_positions: &PerpPositions, market_index: u16) -> pub struct PositionDelta { pub quote_asset_amount: i64, pub base_asset_amount: i64, - pub remainder_base_asset_amount: Option, } impl PositionDelta { - pub fn get_delta_base_with_remainder_abs(&self) -> DriftResult { - let delta_base_i128 = - if let Some(remainder_base_asset_amount) = self.remainder_base_asset_amount { - self.base_asset_amount - .safe_add(remainder_base_asset_amount.cast()?)? - .abs() - .cast::()? - } else { - self.base_asset_amount.abs().cast::()? - }; - Ok(delta_base_i128) + pub fn get_delta_base_abs(&self) -> DriftResult { + self.base_asset_amount.abs().cast::() } } @@ -97,7 +87,7 @@ pub fn update_position_and_market( market: &mut PerpMarket, delta: &PositionDelta, ) -> DriftResult { - if delta.base_asset_amount == 0 && delta.remainder_base_asset_amount.unwrap_or(0) == 0 { + if delta.base_asset_amount == 0 { update_quote_asset_amount(position, market, delta.quote_asset_amount)?; return Ok(delta.quote_asset_amount); } @@ -105,14 +95,8 @@ pub fn update_position_and_market( let update_type = get_position_update_type(position, delta)?; // Update User - let ( - new_base_asset_amount, - new_settled_base_asset_amount, - new_quote_asset_amount, - new_remainder_base_asset_amount, - ) = get_new_position_amounts(position, delta, market)?; - - market.update_market_with_counterparty(delta, new_settled_base_asset_amount)?; + let (new_base_asset_amount, new_quote_asset_amount) = + get_new_position_amounts(position, delta)?; let (new_quote_entry_amount, new_quote_break_even_amount, pnl) = match update_type { PositionUpdateType::Open | PositionUpdateType::Increase => { @@ -127,8 +111,8 @@ pub fn update_position_and_market( (new_quote_entry_amount, new_quote_break_even_amount, 0_i64) } PositionUpdateType::Reduce | PositionUpdateType::Close => { - let current_base_i128 = position.get_base_asset_amount_with_remainder_abs()?; - let delta_base_i128 = delta.get_delta_base_with_remainder_abs()?; + let current_base_i128 = position.get_base_asset_amount_abs()?; + let delta_base_i128 = delta.get_delta_base_abs()?; let new_quote_entry_amount = position.quote_entry_amount.safe_sub( position @@ -156,8 +140,8 @@ pub fn update_position_and_market( (new_quote_entry_amount, new_quote_break_even_amount, pnl) } PositionUpdateType::Flip => { - let current_base_i128 = position.get_base_asset_amount_with_remainder_abs()?; - let delta_base_i128 = delta.get_delta_base_with_remainder_abs()?; + let current_base_i128 = position.get_base_asset_amount_abs()?; + let delta_base_i128 = delta.get_delta_base_abs()?; // same calculation for new_quote_entry_amount let new_quote_break_even_amount = delta.quote_asset_amount.safe_sub( @@ -209,7 +193,7 @@ pub fn update_position_and_market( market.amm.base_asset_amount_long = market .amm .base_asset_amount_long - .safe_add(new_settled_base_asset_amount.cast()?)?; + .safe_add(delta.base_asset_amount.cast()?)?; market.amm.quote_entry_amount_long = market .amm .quote_entry_amount_long @@ -223,7 +207,7 @@ pub fn update_position_and_market( market.amm.base_asset_amount_short = market .amm .base_asset_amount_short - .safe_add(new_settled_base_asset_amount.cast()?)?; + .safe_add(delta.base_asset_amount.cast()?)?; market.amm.quote_entry_amount_short = market .amm .quote_entry_amount_short @@ -239,7 +223,7 @@ pub fn update_position_and_market( market.amm.base_asset_amount_long = market .amm .base_asset_amount_long - .safe_add(new_settled_base_asset_amount.cast()?)?; + .safe_add(delta.base_asset_amount.cast()?)?; market.amm.quote_entry_amount_long = market.amm.quote_entry_amount_long.safe_sub( position .quote_entry_amount @@ -257,7 +241,7 @@ pub fn update_position_and_market( market.amm.base_asset_amount_short = market .amm .base_asset_amount_short - .safe_add(new_settled_base_asset_amount.cast()?)?; + .safe_add(delta.base_asset_amount.cast()?)?; market.amm.quote_entry_amount_short = market.amm.quote_entry_amount_short.safe_sub( position @@ -358,9 +342,6 @@ pub fn update_position_and_market( _ => {} } - let new_position_base_with_remainder = - new_base_asset_amount.safe_add(new_remainder_base_asset_amount)?; - // Update user position if let PositionUpdateType::Close = update_type { position.last_cumulative_funding_rate = 0; @@ -368,7 +349,7 @@ pub fn update_position_and_market( update_type, PositionUpdateType::Open | PositionUpdateType::Increase | PositionUpdateType::Flip ) { - if new_position_base_with_remainder > 0 { + if new_base_asset_amount > 0 { position.last_cumulative_funding_rate = market.amm.cumulative_funding_rate_long.cast()?; } else { @@ -388,7 +369,6 @@ pub fn update_position_and_market( new_base_asset_amount )?; - position.remainder_base_asset_amount = new_remainder_base_asset_amount.cast::()?; position.base_asset_amount = new_base_asset_amount; position.quote_asset_amount = new_quote_asset_amount; @@ -398,62 +378,6 @@ pub fn update_position_and_market( Ok(pnl) } -pub fn update_lp_market_position( - market: &mut PerpMarket, - delta: &PositionDelta, - fee_to_market: i128, - liquidity_split: AMMLiquiditySplit, -) -> DriftResult { - if market.amm.user_lp_shares == 0 || liquidity_split == AMMLiquiditySplit::ProtocolOwned { - return Ok(0); // no need to split with LP - } - - let base_unit: i128 = market.amm.get_per_lp_base_unit()?; - - let (per_lp_delta_base, per_lp_delta_quote, per_lp_fee) = - market - .amm - .calculate_per_lp_delta(delta, fee_to_market, liquidity_split, base_unit)?; - - market.amm.base_asset_amount_per_lp = market - .amm - .base_asset_amount_per_lp - .safe_add(-per_lp_delta_base)?; - - market.amm.quote_asset_amount_per_lp = market - .amm - .quote_asset_amount_per_lp - .safe_add(-per_lp_delta_quote)?; - - // update per lp position - market.amm.quote_asset_amount_per_lp = - market.amm.quote_asset_amount_per_lp.safe_add(per_lp_fee)?; - - let lp_delta_base = market - .amm - .calculate_lp_base_delta(per_lp_delta_base, base_unit)?; - let lp_delta_quote = market - .amm - .calculate_lp_base_delta(per_lp_delta_quote, base_unit)?; - - market.amm.base_asset_amount_with_amm = market - .amm - .base_asset_amount_with_amm - .safe_sub(lp_delta_base)?; - - market.amm.base_asset_amount_with_unsettled_lp = market - .amm - .base_asset_amount_with_unsettled_lp - .safe_add(lp_delta_base)?; - - market.amm.quote_asset_amount_with_unsettled_lp = market - .amm - .quote_asset_amount_with_unsettled_lp - .safe_add(lp_delta_quote.cast()?)?; - - Ok(lp_delta_base) -} - pub fn update_position_with_base_asset_amount( base_asset_amount: u64, direction: PositionDirection, @@ -549,10 +473,7 @@ pub fn update_quote_asset_amount( return Ok(()); } - if position.quote_asset_amount == 0 - && position.base_asset_amount == 0 - && position.remainder_base_asset_amount == 0 - { + if position.quote_asset_amount == 0 && position.base_asset_amount == 0 { market.number_of_users = market.number_of_users.safe_add(1)?; } @@ -560,10 +481,7 @@ pub fn update_quote_asset_amount( market.amm.quote_asset_amount = market.amm.quote_asset_amount.safe_add(delta.cast()?)?; - if position.quote_asset_amount == 0 - && position.base_asset_amount == 0 - && position.remainder_base_asset_amount == 0 - { + if position.quote_asset_amount == 0 && position.base_asset_amount == 0 { market.number_of_users = market.number_of_users.saturating_sub(1); } diff --git a/programs/drift/src/controller/position/tests.rs b/programs/drift/src/controller/position/tests.rs index 71d7520efc..a78048e8dd 100644 --- a/programs/drift/src/controller/position/tests.rs +++ b/programs/drift/src/controller/position/tests.rs @@ -1,10 +1,7 @@ use crate::controller::amm::{ calculate_base_swap_output_with_spread, move_price, recenter_perp_market_amm, swap_base_asset, }; -use crate::controller::lp::{apply_lp_rebase_to_perp_market, settle_lp_position}; -use crate::controller::position::{ - update_lp_market_position, update_position_and_market, PositionDelta, -}; +use crate::controller::position::{update_position_and_market, PositionDelta}; use crate::controller::repeg::_update_amm; use crate::math::amm::calculate_market_open_bids_asks; @@ -13,7 +10,6 @@ use crate::math::constants::{ PRICE_PRECISION_I64, PRICE_PRECISION_U64, QUOTE_PRECISION_I128, SPOT_CUMULATIVE_INTEREST_PRECISION, SPOT_WEIGHT_PRECISION, }; -use crate::math::lp::calculate_settle_lp_metrics; use crate::math::oracle::OracleValidity; use crate::math::position::swap_direction_to_close_position; use crate::math::repeg; @@ -44,34 +40,6 @@ use anchor_lang::Owner; use solana_program::pubkey::Pubkey; use std::str::FromStr; -#[test] -fn full_amm_split() { - let delta = PositionDelta { - base_asset_amount: 10 * BASE_PRECISION_I64, - quote_asset_amount: -10 * BASE_PRECISION_I64, - remainder_base_asset_amount: None, - }; - - let amm = AMM { - user_lp_shares: 0, - sqrt_k: 100 * AMM_RESERVE_PRECISION, - base_asset_amount_with_amm: 10 * AMM_RESERVE_PRECISION_I128, - ..AMM::default_test() - }; - let mut market = PerpMarket { - amm, - ..PerpMarket::default_test() - }; - - update_lp_market_position(&mut market, &delta, 0, AMMLiquiditySplit::Shared).unwrap(); - - assert_eq!(market.amm.base_asset_amount_with_unsettled_lp, 0); - assert_eq!( - market.amm.base_asset_amount_with_amm, - 10 * AMM_RESERVE_PRECISION_I128 - ); -} - #[test] fn amm_pool_balance_liq_fees_example() { let perp_market_str = String::from("Ct8MLGv1N/dquEe6RHLCjPXRFs689/VXwfnq/aHEADtX6J/C8GaZXDKZ6iACt2rxmu8p8Fh+gR3ERNNiw5jAdKhvts0jU4yP8/YGAAAAAAAAAAAAAAAAAAEAAAAAAAAAYOoGAAAAAAD08AYAAAAAAFDQ0WcAAAAAU20cou///////////////zqG0jcAAAAAAAAAAAAAAACyy62lmssEAAAAAAAAAAAAAAAAAAAAAACuEBLjOOAUAAAAAAAAAAAAiQqZJDPTFAAAAAAAAAAAANiFEAAAAAAAAAAAAAAAAABEI0dQmUcTAAAAAAAAAAAAxIkaBDObFgAAAAAAAAAAAD4fkf+02RQAAAAAAAAAAABN+wYAAAAAAAAAAAAAAAAAy1BRbfXSFAAAAAAAAAAAAADOOHkhTQcAAAAAAAAAAAAAFBriILP4////////////SMyW3j0AAAAAAAAAAAAAALgVvHwEAAAAAAAAAAAAAAAAADQm9WscAAAAAAAAAAAAURkvFjoAAAAAAAAAAAAAAHIxjo/f/f/////////////TuoG31QEAAAAAAAAAAAAAP8QC+7L9/////////////3SO4oj1AQAAAAAAAAAAAAAAgFcGo5wAAAAAAAAAAAAAzxUAAAAAAADPFQAAAAAAAM8VAAAAAAAAPQwAAAAAAABk1DIXBgEAAAAAAAAAAAAAKqQCt7MAAAAAAAAAAAAAAP0Q55dSAAAAAAAAAAAAAACS+qA0KQEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALB5hg2UAAAAAAAAAAAAAAAnMANRAAAAAAAAAAAAAAAAmdj/UAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB+LAqY3t8UAAAAAAAAAAAAhk/TOI3TFAAAAAAAAAAAAG1uRreN4BQAAAAAAAAAAABkKKeG3tIUAAAAAAAAAAAA8/YGAAAAAAD+/////////2DqBgAAAAAA5OoGAAAAAACi6gYAAAAAAKzxBgAAAAAAMj1zEwAAAABIAgAAAAAAAIy24v//////tMvRZwAAAAAQDgAAAAAAAADKmjsAAAAAZAAAAAAAAAAA8gUqAQAAAAAAAAAAAAAAs3+BskEAAADIfXYRAAAAAIIeqQIAAAAAdb7RZwAAAABxDAAAAAAAAJMMAAAAAAAAUNDRZwAAAAD6AAAA1DAAAIQAAAB9AAAAfgAAAAAAAABkADIAZGQMAQAAAAADAAAAX79DBQAAAABIC9oEAwAAAK3TwZwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFdJRi1QRVJQICAgICAgICAgICAgICAgICAgICAgICAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADd4BgAAAAAAlCUAAAAAAAAcCgAAAAAAAGQAAABkAAAAqGEAAFDDAADECQAA4gQAAAAAAAAQJwAA2QAAAIgBAAAXAAEAAwAAAAAAAAEBAOgD9AEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="); @@ -1144,6 +1112,8 @@ fn amm_perp_ref_offset() { AccountLoader::try_from(&perp_market_account_info).unwrap(); let mut perp_market = perp_market_loader.load_mut().unwrap(); + perp_market.amm.base_asset_amount_with_amm = 40000000000; // override old LP related fields + let reserve_price = perp_market.amm.reserve_price().unwrap(); let (b1, a1) = perp_market.amm.bid_ask_price(reserve_price).unwrap(); assert_eq!(reserve_price, 7101599); @@ -1306,573 +1276,12 @@ fn amm_perp_ref_offset() { assert_eq!(a3, 7138903); } -#[test] -fn amm_split_large_k() { - let perp_market_str = String::from("Ct8MLGv1N/dvAH3EF67yBqaUQerctpm4yqpK+QNSrXCQz76p+B+ka+8Ni2/aLOukHaFdQJXR2jkqDS+O0MbHvA9M+sjCgLVtQwhkAQAAAAAAAAAAAAAAAAIAAAAAAAAAkI1kAQAAAAB6XWQBAAAAAO8yzWQAAAAAnJ7I3f///////////////2dHvwAAAAAAAAAAAAAAAABGiVjX6roAAAAAAAAAAAAAAAAAAAAAAAB1tO47J+xiAAAAAAAAAAAAGD03Fis3mgAAAAAAAAAAAJxiDwAAAAAAAAAAAAAAAABxqRCIGRxiAAAAAAAAAAAAEy8wZfK9YwAAAAAAAAAAAGZeZCE+g3sAAAAAAAAAAAAKYeQAAAAAAAAAAAAAAAAAlIvoyyc3mgAAAAAAAAAAAADQdQKjbgAAAAAAAAAAAAAAwu8g05H/////////////E6tNHAIAAAAAAAAAAAAAAO3mFwd0AAAAAAAAAAAAAAAAgPQg5rUAAAAAAAAAAAAAGkDtXR4AAAAAAAAAAAAAAEv0WeZW/f////////////9kUidaqAIAAAAAAAAAAAAA0ZMEr1H9/////////////w5/U3uqAgAAAAAAAAAAAAAANfbqfCd3AAAAAAAAAAAAIhABAAAAAAAiEAEAAAAAACIQAQAAAAAAY1QBAAAAAAA5f3WMVAAAAAAAAAAAAAAAFhkiihsAAAAAAAAAAAAAAO2EfWc5AAAAAAAAAAAAAACM/5CAQgAAAAAAAAAAAAAAvenX0SsAAAAAAAAAAAAAALgPUogZAAAAAAAAAAAAAAC01x97AAAAAAAAAAAAAAAAOXzVbgAAAAAAAAAAAAAAAMG4+QwBAAAAAAAAAAAAAABwHI3fLeJiAAAAAAAAAAAABvigOblGmgAAAAAAAAAAALeRnZsi9mIAAAAAAAAAAAAqgs3ynCeaAAAAAAAAAAAAQwhkAQAAAAAAAAAAAAAAAJOMZAEAAAAAFKJkAQAAAABTl2QBAAAAALFuZAEAAAAAgrx7DAAAAAAUAwAAAAAAAAN1TAYAAAAAuC7NZAAAAAAQDgAAAAAAAADh9QUAAAAAZAAAAAAAAAAA4fUFAAAAAAAAAAAAAAAAn2HvyMABAADGV6rZFwAAAE5Qg2oPAAAA8zHNZAAAAAAdYAAAAAAAAE2FAAAAAAAA6zLNZAAAAAD6AAAAaEIAABQDAAAUAwAAAAAAANcBAABkADIAZGQAAcDIUt4AAAAA0QQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAI9qQbynsAAAAAAAAAAAAAAAAAAAAAAAAFNPTC1QRVJQICAgICAgICAgICAgICAgICAgICAgICAghuS1//////8A4fUFAAAAAAB0O6QLAAAAR7PdeQMAAAD+Mc1kAAAAAADKmjsAAAAAAAAAAAAAAAAAAAAAAAAAAOULDwAAAAAAUBkAAAAAAADtAQAAAAAAAMgAAAAAAAAAECcAAKhhAADoAwAA9AEAAAAAAAAQJwAAZAIAAGQCAAAAAAEAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="); - let mut decoded_bytes = base64::decode(perp_market_str).unwrap(); - let perp_market_bytes = decoded_bytes.as_mut_slice(); - - let key = Pubkey::default(); - let owner = Pubkey::from_str("dRiftyHA39MWEi3m9aunc5MzRF1JYuBsbn6VPcn33UH").unwrap(); - let mut lamports = 0; - let perp_market_account_info = - create_account_info(&key, true, &mut lamports, perp_market_bytes, &owner); - - let perp_market_loader: AccountLoader = - AccountLoader::try_from(&perp_market_account_info).unwrap(); - let mut perp_market = perp_market_loader.load_mut().unwrap(); - - assert_eq!(perp_market.amm.base_asset_amount_per_lp, -574054756); - assert_eq!(perp_market.amm.quote_asset_amount_per_lp, 12535655); - - let og_baapl = perp_market.amm.base_asset_amount_per_lp; - let og_qaapl = perp_market.amm.quote_asset_amount_per_lp; - - // msg!("perp_market: {:?}", perp_market); - - // min long order for $2.3 - let delta = PositionDelta { - base_asset_amount: BASE_PRECISION_I64 / 10, - quote_asset_amount: -2300000, - remainder_base_asset_amount: None, - }; - - update_lp_market_position(&mut perp_market, &delta, 0, AMMLiquiditySplit::Shared).unwrap(); - - assert_eq!(perp_market.amm.base_asset_amount_per_lp, -574054758); - assert_eq!(perp_market.amm.quote_asset_amount_per_lp, 12535655); - - // min short order for $2.3 - let delta = PositionDelta { - base_asset_amount: -BASE_PRECISION_I64 / 10, - quote_asset_amount: 2300000, - remainder_base_asset_amount: None, - }; - - update_lp_market_position(&mut perp_market, &delta, 0, AMMLiquiditySplit::Shared).unwrap(); - - assert_eq!(perp_market.amm.base_asset_amount_per_lp, -574054756); - assert_eq!(perp_market.amm.quote_asset_amount_per_lp, 12535654); - - let mut existing_position = PerpPosition { - market_index: 0, - base_asset_amount: 0, - quote_asset_amount: 0, - lp_shares: perp_market.amm.user_lp_shares as u64, - last_base_asset_amount_per_lp: og_baapl as i64, - last_quote_asset_amount_per_lp: og_qaapl as i64, - per_lp_base: 0, - ..PerpPosition::default() - }; - - settle_lp_position(&mut existing_position, &mut perp_market).unwrap(); - - assert_eq!(existing_position.base_asset_amount, 0); - assert_eq!(existing_position.remainder_base_asset_amount, 0); - assert_eq!(existing_position.quote_asset_amount, -33538939); // out of favor rounding - - assert_eq!(existing_position.per_lp_base, perp_market.amm.per_lp_base); - - // long order for $230 - let delta = PositionDelta { - base_asset_amount: BASE_PRECISION_I64 * 10, - quote_asset_amount: -230000000, - remainder_base_asset_amount: None, - }; - - update_lp_market_position(&mut perp_market, &delta, 0, AMMLiquiditySplit::Shared).unwrap(); - - assert_eq!(perp_market.amm.base_asset_amount_per_lp, -574055043); - assert_eq!(perp_market.amm.quote_asset_amount_per_lp, 12535660); - - assert_eq!( - (perp_market.amm.sqrt_k as i128) * (og_baapl - perp_market.amm.base_asset_amount_per_lp) - / AMM_RESERVE_PRECISION_I128, - 9977763076 - ); - // assert_eq!((perp_market.amm.sqrt_k as i128) * (og_baapl-perp_market.amm.base_asset_amount_per_lp) / AMM_RESERVE_PRECISION_I128, 104297175); - assert_eq!( - (perp_market.amm.sqrt_k as i128) * (og_qaapl - perp_market.amm.quote_asset_amount_per_lp) - / QUOTE_PRECISION_I128, - -173828625034 - ); - assert_eq!( - (perp_market.amm.sqrt_k as i128) - * (og_qaapl - perp_market.amm.quote_asset_amount_per_lp - 1) - / QUOTE_PRECISION_I128, - -208594350041 - ); - // assert_eq!(243360075047/9977763076 < 23, true); // ensure rounding in favor - - // long order for $230 - let delta = PositionDelta { - base_asset_amount: -BASE_PRECISION_I64 * 10, - quote_asset_amount: 230000000, - remainder_base_asset_amount: None, - }; - - let og_baapl = perp_market.amm.base_asset_amount_per_lp; - let og_qaapl = perp_market.amm.quote_asset_amount_per_lp; - - update_lp_market_position(&mut perp_market, &delta, 0, AMMLiquiditySplit::Shared).unwrap(); - - assert_eq!(perp_market.amm.base_asset_amount_per_lp, -574054756); - assert_eq!(perp_market.amm.quote_asset_amount_per_lp, 12535653); - - assert_eq!( - (perp_market.amm.sqrt_k as i128) * (og_baapl - perp_market.amm.base_asset_amount_per_lp) - / AMM_RESERVE_PRECISION_I128, - -9977763076 - ); - // assert_eq!((perp_market.amm.sqrt_k as i128) * (og_baapl-perp_market.amm.base_asset_amount_per_lp) / AMM_RESERVE_PRECISION_I128, 104297175); - assert_eq!( - (perp_market.amm.sqrt_k as i128) * (og_qaapl - perp_market.amm.quote_asset_amount_per_lp) - / QUOTE_PRECISION_I128, - 243360075047 - ); - // assert_eq!(243360075047/9977763076 < 23, true); // ensure rounding in favor -} - -#[test] -fn test_quote_unsettled_lp() { - let perp_market_str = String::from("Ct8MLGv1N/dvAH3EF67yBqaUQerctpm4yqpK+QNSrXCQz76p+B+ka+8Ni2/aLOukHaFdQJXR2jkqDS+O0MbHvA9M+sjCgLVtzjkqCQAAAAAAAAAAAAAAAAIAAAAAAAAAl44wCQAAAAD54C0JAAAAAGJ4JmYAAAAAyqMxdXz//////////////wV1ZyH9//////////////8Uy592jFYPAAAAAAAAAAAAAAAAAAAAAAD6zIP0/dAIAAAAAAAAAAAA+srqThjtHwAAAAAAAAAAAJxiDwAAAAAAAAAAAAAAAAByWgjyVb4IAAAAAAAAAAAAOpuf9pLjCAAAAAAAAAAAAMRfA6LzxhAAAAAAAAAAAABs6IcCAAAAAAAAAAAAAAAAeXyo6oHtHwAAAAAAAAAAAABngilYXAEAAAAAAAAAAAAAZMIneaP+////////////GeN71uL//////////////+fnyHru//////////////8AIA8MEgUDAAAAAAAAAAAAv1P8g/EBAAAAAAAAAAAAACNQgLCty/////////////+KMQ7JGjMAAAAAAAAAAAAA4DK7xH3K/////////////2grSsB0NQAAAAAAAAAAAACsBC7WWDkCAAAAAAAAAAAAsis3AAAAAACyKzcAAAAAALIrNwAAAAAATGc8AAAAAADH51Hn/wYAAAAAAAAAAAAANXNbBAgCAAAAAAAAAAAAAPNHO0UKBQAAAAAAAAAAAABiEweaqQUAAAAAAAAAAAAAg16F138BAAAAAAAAAAAAAFBZFMk0AQAAAAAAAAAAAACoA6JpBwAAAAAAAAAAAAAALahXXQcAAAAAAAAAAAAAAMG4+QwBAAAAAAAAAAAAAADr9qfqkdAIAAAAAAAAAAAAlBk2nZ/uHwAAAAAAAAAAAHPdcUR+0QgAAAAAAAAAAAAF+03DR+sfAAAAAAAAAAAAzjkqCQAAAAAAAAAAAAAAAJXnMAkAAAAAT9IxCQAAAADyXDEJAAAAAKlJLgkAAAAAyg2YDwAAAABfBwAAAAAAANVPrUEAAAAAZW0mZgAAAAAQDgAAAAAAAADh9QUAAAAAZAAAAAAAAAAA4fUFAAAAAAAAAAAAAAAAj0W2KSYpAABzqJhf6gAAAOD5o985AQAAS3gmZgAAAADxKQYAAAAAAMlUBgAAAAAAS3gmZgAAAADuAgAA7CwAAHcBAAC9AQAAAAAAAH0AAADECTIAZMgAAcDIUt4DAAAAFJMfEQAAAADBogAAAAAAAIneROQcpf//AAAAAAAAAAAAAAAAAAAAAFe4ynNxUwoAAAAAAAAAAAAAAAAAAAAAAFNPTC1QRVJQICAgICAgICAgICAgICAgICAgICAgICAgAJsy4v////8AZc0dAAAAAP8PpdToAAAANOVq3RYAAAB7cyZmAAAAAADh9QUAAAAAAAAAAAAAAAAAAAAAAAAAAEyBWwAAAAAA2DEAAAAAAABzBQAAAAAAAMgAAAAAAAAATB0AANQwAADoAwAA9AEAAAAAAAAQJwAAASoAACtgAAAAAAEAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="); - let mut decoded_bytes = base64::decode(perp_market_str).unwrap(); - let perp_market_bytes = decoded_bytes.as_mut_slice(); - - let key = Pubkey::default(); - let owner = Pubkey::from_str("dRiftyHA39MWEi3m9aunc5MzRF1JYuBsbn6VPcn33UH").unwrap(); - let mut lamports = 0; - let perp_market_account_info = - create_account_info(&key, true, &mut lamports, perp_market_bytes, &owner); - - let perp_market_loader: AccountLoader = - AccountLoader::try_from(&perp_market_account_info).unwrap(); - let mut perp_market = perp_market_loader.load_mut().unwrap(); - perp_market.amm.quote_asset_amount_with_unsettled_lp = 0; - - let mut existing_position: PerpPosition = PerpPosition::default(); - assert_eq!(perp_market.amm.quote_asset_amount_per_lp, -12324473595); - assert_eq!(perp_market.amm.base_asset_amount_per_lp, -564969495606); - - existing_position.last_quote_asset_amount_per_lp = - perp_market.amm.quote_asset_amount_per_lp as i64; - existing_position.last_base_asset_amount_per_lp = - perp_market.amm.base_asset_amount_per_lp as i64; - - let pos_delta = PositionDelta { - quote_asset_amount: QUOTE_PRECISION_I64 * 150, - base_asset_amount: -BASE_PRECISION_I64, - remainder_base_asset_amount: Some(-881), - }; - assert_eq!(perp_market.amm.quote_asset_amount_with_unsettled_lp, 0); - let fee_to_market = 1000000; // uno doll - let liq_split = AMMLiquiditySplit::Shared; - let base_unit: i128 = perp_market.amm.get_per_lp_base_unit().unwrap(); - assert_eq!(base_unit, 1_000_000_000_000); // 10^4 * base_precision - - let (per_lp_delta_base, per_lp_delta_quote, per_lp_fee) = perp_market - .amm - .calculate_per_lp_delta(&pos_delta, fee_to_market, liq_split, base_unit) - .unwrap(); - - assert_eq!(per_lp_delta_base, -211759); - assert_eq!(per_lp_delta_quote, 31764); - assert_eq!(per_lp_fee, 169); - - let pos_delta2 = PositionDelta { - quote_asset_amount: -QUOTE_PRECISION_I64 * 150, - base_asset_amount: BASE_PRECISION_I64, - remainder_base_asset_amount: Some(0), - }; - let (per_lp_delta_base, per_lp_delta_quote, per_lp_fee) = perp_market - .amm - .calculate_per_lp_delta(&pos_delta2, fee_to_market, liq_split, base_unit) - .unwrap(); - - assert_eq!(per_lp_delta_base, 211759); - assert_eq!(per_lp_delta_quote, -31763); - assert_eq!(per_lp_fee, 169); - - let expected_base_asset_amount_with_unsettled_lp = -75249424409; - assert_eq!( - perp_market.amm.base_asset_amount_with_unsettled_lp, - // 0 - expected_base_asset_amount_with_unsettled_lp // ~-75 - ); - // let lp_delta_quote = perp_market - // .amm - // .calculate_lp_base_delta(per_lp_delta_quote, QUOTE_PRECISION_I128) - // .unwrap(); - // assert_eq!(lp_delta_quote, -19883754464333); - - let delta_base = - update_lp_market_position(&mut perp_market, &pos_delta, fee_to_market, liq_split).unwrap(); - assert_eq!( - perp_market.amm.user_lp_shares * 1000000 / perp_market.amm.sqrt_k, - 132561 - ); // 13.2 % of amm - assert_eq!( - perp_market.amm.quote_asset_amount_with_unsettled_lp, - 19884380 - ); // 19.884380/.132 ~= 150 (+ fee) - assert_eq!(delta_base, -132_561_910); // ~13% - assert_eq!( - perp_market.amm.base_asset_amount_with_unsettled_lp, - // 0 - -75381986319 // ~-75 - ); - - // settle lp and quote with unsettled should go back to zero - existing_position.lp_shares = perp_market.amm.user_lp_shares as u64; - existing_position.per_lp_base = 3; - - let lp_metrics: crate::math::lp::LPMetrics = - calculate_settle_lp_metrics(&perp_market.amm, &existing_position).unwrap(); - - let position_delta = PositionDelta { - base_asset_amount: lp_metrics.base_asset_amount as i64, - quote_asset_amount: lp_metrics.quote_asset_amount as i64, - remainder_base_asset_amount: Some(lp_metrics.remainder_base_asset_amount as i64), - }; - - assert_eq!(position_delta.base_asset_amount, 100000000); - - assert_eq!( - position_delta.remainder_base_asset_amount.unwrap_or(0), - 32561910 - ); - - assert_eq!(position_delta.quote_asset_amount, -19778585); - - let pnl = update_position_and_market(&mut existing_position, &mut perp_market, &position_delta) - .unwrap(); - - //.132561*1e6*.8 = 106048.8 - assert_eq!(perp_market.amm.quote_asset_amount_with_unsettled_lp, 105795); //? - assert_eq!( - perp_market.amm.base_asset_amount_with_unsettled_lp, - expected_base_asset_amount_with_unsettled_lp - 32561910 - ); - - assert_eq!(pnl, 0); -} - -#[test] -fn amm_split_large_k_with_rebase() { - let perp_market_str = String::from("Ct8MLGv1N/dvAH3EF67yBqaUQerctpm4yqpK+QNSrXCQz76p+B+ka+8Ni2/aLOukHaFdQJXR2jkqDS+O0MbHvA9M+sjCgLVtQwhkAQAAAAAAAAAAAAAAAAIAAAAAAAAAkI1kAQAAAAB6XWQBAAAAAO8yzWQAAAAAnJ7I3f///////////////2dHvwAAAAAAAAAAAAAAAABGiVjX6roAAAAAAAAAAAAAAAAAAAAAAAB1tO47J+xiAAAAAAAAAAAAGD03Fis3mgAAAAAAAAAAAJxiDwAAAAAAAAAAAAAAAABxqRCIGRxiAAAAAAAAAAAAEy8wZfK9YwAAAAAAAAAAAGZeZCE+g3sAAAAAAAAAAAAKYeQAAAAAAAAAAAAAAAAAlIvoyyc3mgAAAAAAAAAAAADQdQKjbgAAAAAAAAAAAAAAwu8g05H/////////////E6tNHAIAAAAAAAAAAAAAAO3mFwd0AAAAAAAAAAAAAAAAgPQg5rUAAAAAAAAAAAAAGkDtXR4AAAAAAAAAAAAAAEv0WeZW/f////////////9kUidaqAIAAAAAAAAAAAAA0ZMEr1H9/////////////w5/U3uqAgAAAAAAAAAAAAAANfbqfCd3AAAAAAAAAAAAIhABAAAAAAAiEAEAAAAAACIQAQAAAAAAY1QBAAAAAAA5f3WMVAAAAAAAAAAAAAAAFhkiihsAAAAAAAAAAAAAAO2EfWc5AAAAAAAAAAAAAACM/5CAQgAAAAAAAAAAAAAAvenX0SsAAAAAAAAAAAAAALgPUogZAAAAAAAAAAAAAAC01x97AAAAAAAAAAAAAAAAOXzVbgAAAAAAAAAAAAAAAMG4+QwBAAAAAAAAAAAAAABwHI3fLeJiAAAAAAAAAAAABvigOblGmgAAAAAAAAAAALeRnZsi9mIAAAAAAAAAAAAqgs3ynCeaAAAAAAAAAAAAQwhkAQAAAAAAAAAAAAAAAJOMZAEAAAAAFKJkAQAAAABTl2QBAAAAALFuZAEAAAAAgrx7DAAAAAAUAwAAAAAAAAN1TAYAAAAAuC7NZAAAAAAQDgAAAAAAAADh9QUAAAAAZAAAAAAAAAAA4fUFAAAAAAAAAAAAAAAAn2HvyMABAADGV6rZFwAAAE5Qg2oPAAAA8zHNZAAAAAAdYAAAAAAAAE2FAAAAAAAA6zLNZAAAAAD6AAAAaEIAABQDAAAUAwAAAAAAANcBAABkADIAZGQAAcDIUt4AAAAA0QQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAI9qQbynsAAAAAAAAAAAAAAAAAAAAAAAAFNPTC1QRVJQICAgICAgICAgICAgICAgICAgICAgICAghuS1//////8A4fUFAAAAAAB0O6QLAAAAR7PdeQMAAAD+Mc1kAAAAAADKmjsAAAAAAAAAAAAAAAAAAAAAAAAAAOULDwAAAAAAUBkAAAAAAADtAQAAAAAAAMgAAAAAAAAAECcAAKhhAADoAwAA9AEAAAAAAAAQJwAAZAIAAGQCAAAAAAEAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="); - let mut decoded_bytes = base64::decode(perp_market_str).unwrap(); - let perp_market_bytes = decoded_bytes.as_mut_slice(); - - let key = Pubkey::default(); - let owner = Pubkey::from_str("dRiftyHA39MWEi3m9aunc5MzRF1JYuBsbn6VPcn33UH").unwrap(); - let mut lamports = 0; - let perp_market_account_info = - create_account_info(&key, true, &mut lamports, perp_market_bytes, &owner); - - let perp_market_loader: AccountLoader = - AccountLoader::try_from(&perp_market_account_info).unwrap(); - let mut perp_market = perp_market_loader.load_mut().unwrap(); - - assert_eq!(perp_market.amm.base_asset_amount_per_lp, -574054756); - assert_eq!(perp_market.amm.quote_asset_amount_per_lp, 12535655); - assert_eq!( - perp_market.amm.base_asset_amount_with_unsettled_lp, - 498335213293 - ); - - let og_baawul = perp_market.amm.base_asset_amount_with_unsettled_lp; - let og_baapl = perp_market.amm.base_asset_amount_per_lp; - let og_qaapl = perp_market.amm.quote_asset_amount_per_lp; - - // update base - let base_change = 5; - apply_lp_rebase_to_perp_market(&mut perp_market, base_change).unwrap(); - - // noop delta - let delta = PositionDelta { - base_asset_amount: 0, - quote_asset_amount: 0, - remainder_base_asset_amount: None, - }; - - update_lp_market_position(&mut perp_market, &delta, 0, AMMLiquiditySplit::Shared).unwrap(); - - assert_eq!(perp_market.amm.quote_asset_amount_per_lp, og_qaapl * 100000); - assert_eq!(perp_market.amm.base_asset_amount_per_lp, og_baapl * 100000); - assert_eq!( - perp_market.amm.base_asset_amount_with_unsettled_lp, - og_baawul - ); - - // min long order for $2.3 - let delta = PositionDelta { - base_asset_amount: BASE_PRECISION_I64 / 10, - quote_asset_amount: -2300000, - remainder_base_asset_amount: None, - }; - - let u1 = - update_lp_market_position(&mut perp_market, &delta, 0, AMMLiquiditySplit::Shared).unwrap(); - assert_eq!(u1, 96471070); - - assert_eq!( - perp_market.amm.base_asset_amount_with_unsettled_lp, - 498431684363 - ); - - assert_eq!( - perp_market.amm.base_asset_amount_per_lp - og_baapl * 100000, - -287639 - ); - assert_eq!( - perp_market.amm.quote_asset_amount_per_lp - og_qaapl * 100000, - 6615 - ); - assert_eq!( - perp_market.amm.base_asset_amount_with_unsettled_lp - og_baawul, - 96471070 - ); - assert_eq!(perp_market.amm.base_asset_amount_per_lp, -57405475887639); - assert_eq!(perp_market.amm.quote_asset_amount_per_lp, 1253565506615); - - let num = perp_market.amm.quote_asset_amount_per_lp - (og_qaapl * 100000); - let denom = perp_market.amm.base_asset_amount_per_lp - (og_baapl * 100000); - assert_eq!(-num * 1000000 / denom, 22997); // $22.997 cost basis for short (vs $23 actual) - - // min short order for $2.3 - let delta = PositionDelta { - base_asset_amount: -BASE_PRECISION_I64 / 10, - quote_asset_amount: 2300000, - remainder_base_asset_amount: None, - }; - - update_lp_market_position(&mut perp_market, &delta, 0, AMMLiquiditySplit::Shared).unwrap(); - - assert_eq!(perp_market.amm.base_asset_amount_per_lp, -57405475600000); - assert_eq!(perp_market.amm.quote_asset_amount_per_lp, 1253565499999); - assert_eq!( - (og_qaapl * 100000) - perp_market.amm.quote_asset_amount_per_lp, - 1 - ); - - let mut existing_position = PerpPosition { - market_index: 0, - base_asset_amount: 0, - quote_asset_amount: 0, - lp_shares: perp_market.amm.user_lp_shares as u64, - last_base_asset_amount_per_lp: og_baapl as i64, - last_quote_asset_amount_per_lp: og_qaapl as i64, - per_lp_base: 0, - ..PerpPosition::default() - }; - - settle_lp_position(&mut existing_position, &mut perp_market).unwrap(); - - assert_eq!(existing_position.base_asset_amount, 0); - assert_eq!(existing_position.remainder_base_asset_amount, 0); - assert_eq!(existing_position.quote_asset_amount, -335); // out of favor rounding... :/ - - assert_eq!(existing_position.per_lp_base, perp_market.amm.per_lp_base); - - assert_eq!( - perp_market - .amm - .imbalanced_base_asset_amount_with_lp() - .unwrap(), - -303686915482213 - ); - - assert_eq!(perp_market.amm.target_base_asset_amount_per_lp, -565000000); - - // update base back - let base_change = -2; - apply_lp_rebase_to_perp_market(&mut perp_market, base_change).unwrap(); - // noop delta - let delta = PositionDelta { - base_asset_amount: 0, - quote_asset_amount: 0, - remainder_base_asset_amount: None, - }; - - // unchanged with rebase (even when target!=0) - assert_eq!( - perp_market - .amm - .imbalanced_base_asset_amount_with_lp() - .unwrap(), - -303686915482213 - ); - - update_lp_market_position(&mut perp_market, &delta, 0, AMMLiquiditySplit::Shared).unwrap(); - - assert_eq!( - perp_market.amm.quote_asset_amount_per_lp, - og_qaapl * 1000 - 1 - ); // down only rounding - assert_eq!(perp_market.amm.base_asset_amount_per_lp, og_baapl * 1000); - - // 1 long order for $23 before lp position does rebasing - let delta = PositionDelta { - base_asset_amount: BASE_PRECISION_I64, - quote_asset_amount: -23000000, - remainder_base_asset_amount: None, - }; - assert_eq!(perp_market.amm.base_asset_amount_per_lp, -574054756000); - - update_lp_market_position(&mut perp_market, &delta, 0, AMMLiquiditySplit::Shared).unwrap(); - - let now = 110; - let clock_slot = 111; - let state = State::default(); - let oracle_price_data = OraclePriceData { - price: 23 * PRICE_PRECISION_I64, - confidence: PRICE_PRECISION_U64 / 100, - delay: 14, - has_sufficient_number_of_data_points: true, - }; - let mut mm_oracle_price = perp_market - .get_mm_oracle_price_data( - oracle_price_data, - clock_slot, - &state.oracle_guard_rails.validity, - ) - .unwrap(); - - let cost = _update_amm( - &mut perp_market, - &mut mm_oracle_price, - &state, - now, - clock_slot, - ) - .unwrap(); - assert_eq!(cost, -3017938); - - assert_eq!(perp_market.amm.quote_asset_amount_per_lp, 12535655660); - assert_eq!(perp_market.amm.base_asset_amount_per_lp, -574054784763); - assert_eq!( - existing_position.last_base_asset_amount_per_lp, - -57405475600000 - ); - assert_eq!(existing_position.per_lp_base, 5); - assert_ne!(existing_position.per_lp_base, perp_market.amm.per_lp_base); - - assert_eq!(perp_market.amm.base_asset_amount_long, 121646400000000); - assert_eq!(perp_market.amm.base_asset_amount_short, -121139000000000); - assert_eq!(perp_market.amm.base_asset_amount_with_amm, 8100106185); - assert_eq!( - perp_market.amm.base_asset_amount_with_unsettled_lp, - 499299893815 - ); - let prev_with_unsettled_lp = perp_market.amm.base_asset_amount_with_unsettled_lp; - settle_lp_position(&mut existing_position, &mut perp_market).unwrap(); - - assert_eq!(perp_market.amm.base_asset_amount_long, 121646400000000); - assert_eq!(perp_market.amm.base_asset_amount_short, -121139900000000); - assert_eq!(perp_market.amm.base_asset_amount_with_amm, 8100106185); - assert_eq!( - perp_market.amm.base_asset_amount_with_unsettled_lp, - 498399893815 - ); - assert_eq!( - perp_market.amm.base_asset_amount_with_unsettled_lp, - 498399893815 - ); - assert!(perp_market.amm.base_asset_amount_with_unsettled_lp < prev_with_unsettled_lp); - - // 96.47% owned - assert_eq!(perp_market.amm.user_lp_shares, 33538939700000000); - assert_eq!(perp_market.amm.sqrt_k, 34765725006847590); - - assert_eq!(existing_position.per_lp_base, perp_market.amm.per_lp_base); - - assert_eq!(existing_position.base_asset_amount, -900000000); - assert_eq!(existing_position.remainder_base_asset_amount, -64680522); - assert_eq!(existing_position.quote_asset_amount, 22168904); // out of favor rounding... :/ - assert_eq!( - existing_position.last_base_asset_amount_per_lp, - perp_market.amm.base_asset_amount_per_lp as i64 - ); // out of favor rounding... :/ - assert_eq!( - existing_position.last_quote_asset_amount_per_lp, - perp_market.amm.quote_asset_amount_per_lp as i64 - ); // out of favor rounding... :/ -} - -#[test] -fn full_lp_split() { - let delta = PositionDelta { - base_asset_amount: 10 * BASE_PRECISION_I64, - quote_asset_amount: -10 * BASE_PRECISION_I64, - remainder_base_asset_amount: None, - }; - - let amm = AMM { - user_lp_shares: 100 * AMM_RESERVE_PRECISION, - sqrt_k: 100 * AMM_RESERVE_PRECISION, - base_asset_amount_with_amm: 10 * AMM_RESERVE_PRECISION_I128, - ..AMM::default_test() - }; - let mut market = PerpMarket { - amm, - ..PerpMarket::default_test() - }; - - update_lp_market_position(&mut market, &delta, 0, AMMLiquiditySplit::Shared).unwrap(); - - assert_eq!( - market.amm.base_asset_amount_per_lp as i64, - -10 * BASE_PRECISION_I64 / 100 - ); - assert_eq!( - market.amm.quote_asset_amount_per_lp as i64, - 10 * BASE_PRECISION_I64 / 100 - ); - assert_eq!(market.amm.base_asset_amount_with_amm, 0); - assert_eq!( - market.amm.base_asset_amount_with_unsettled_lp, - 10 * AMM_RESERVE_PRECISION_I128 - ); -} - -#[test] -fn half_half_amm_lp_split() { - let delta = PositionDelta { - base_asset_amount: 10 * BASE_PRECISION_I64, - quote_asset_amount: -10 * BASE_PRECISION_I64, - remainder_base_asset_amount: None, - }; - - let amm = AMM { - user_lp_shares: 100 * AMM_RESERVE_PRECISION, - sqrt_k: 200 * AMM_RESERVE_PRECISION, - base_asset_amount_with_amm: 10 * AMM_RESERVE_PRECISION_I128, - ..AMM::default_test() - }; - let mut market = PerpMarket { - amm, - ..PerpMarket::default_test() - }; - - update_lp_market_position(&mut market, &delta, 0, AMMLiquiditySplit::Shared).unwrap(); - - assert_eq!( - market.amm.base_asset_amount_with_amm, - 5 * AMM_RESERVE_PRECISION_I128 - ); - assert_eq!( - market.amm.base_asset_amount_with_unsettled_lp, - 5 * AMM_RESERVE_PRECISION_I128 - ); -} - #[test] fn test_position_entry_sim() { let mut existing_position: PerpPosition = PerpPosition::default(); let position_delta = PositionDelta { base_asset_amount: BASE_PRECISION_I64 / 2, quote_asset_amount: -99_345_000 / 2, - remainder_base_asset_amount: None, }; let mut market = PerpMarket { amm: AMM { @@ -1894,7 +1303,6 @@ fn test_position_entry_sim() { let position_delta_to_reduce = PositionDelta { base_asset_amount: -BASE_PRECISION_I64 / 5, quote_asset_amount: 99_245_000 / 5, - remainder_base_asset_amount: None, }; let pnl = update_position_and_market( @@ -1912,7 +1320,6 @@ fn test_position_entry_sim() { let position_delta_to_flip = PositionDelta { base_asset_amount: -BASE_PRECISION_I64, quote_asset_amount: 99_345_000, - remainder_base_asset_amount: None, }; let pnl = @@ -1931,7 +1338,6 @@ fn increase_long_from_no_position() { let position_delta = PositionDelta { base_asset_amount: 1, quote_asset_amount: -1, - remainder_base_asset_amount: None, }; let mut market = PerpMarket { amm: AMM { @@ -1971,7 +1377,6 @@ fn increase_short_from_no_position() { let position_delta = PositionDelta { base_asset_amount: -1, quote_asset_amount: 1, - remainder_base_asset_amount: None, }; let mut market = PerpMarket { amm: AMM { @@ -2015,7 +1420,6 @@ fn increase_long() { let position_delta = PositionDelta { base_asset_amount: 1, quote_asset_amount: -1, - remainder_base_asset_amount: None, }; let mut market = PerpMarket { amm: AMM { @@ -2067,7 +1471,6 @@ fn increase_short() { let position_delta = PositionDelta { base_asset_amount: -1, quote_asset_amount: 1, - remainder_base_asset_amount: None, }; let mut market = PerpMarket { amm: AMM { @@ -2116,7 +1519,6 @@ fn reduce_long_profitable() { let position_delta = PositionDelta { base_asset_amount: -1, quote_asset_amount: 5, - remainder_base_asset_amount: None, }; let mut market = PerpMarket { amm: AMM { @@ -2167,7 +1569,6 @@ fn reduce_long_unprofitable() { let position_delta = PositionDelta { base_asset_amount: -1, quote_asset_amount: 5, - remainder_base_asset_amount: None, }; let mut market = PerpMarket { amm: AMM { @@ -2218,7 +1619,6 @@ fn flip_long_to_short_profitable() { let position_delta = PositionDelta { base_asset_amount: -11, quote_asset_amount: 22, - remainder_base_asset_amount: None, }; let mut market = PerpMarket { amm: AMM { @@ -2270,7 +1670,6 @@ fn flip_long_to_short_unprofitable() { let position_delta = PositionDelta { base_asset_amount: -11, quote_asset_amount: 10, - remainder_base_asset_amount: None, }; let mut market = PerpMarket { amm: AMM { @@ -2323,7 +1722,6 @@ fn reduce_short_profitable() { let position_delta = PositionDelta { base_asset_amount: 1, quote_asset_amount: -5, - remainder_base_asset_amount: None, }; let mut market = PerpMarket { amm: AMM { @@ -2372,7 +1770,6 @@ fn decrease_short_unprofitable() { let position_delta = PositionDelta { base_asset_amount: 1, quote_asset_amount: -15, - remainder_base_asset_amount: None, }; let mut market = PerpMarket { amm: AMM { @@ -2421,7 +1818,6 @@ fn flip_short_to_long_profitable() { let position_delta = PositionDelta { base_asset_amount: 11, quote_asset_amount: -60, - remainder_base_asset_amount: None, }; let mut market = PerpMarket { amm: AMM { @@ -2473,7 +1869,6 @@ fn flip_short_to_long_unprofitable() { let position_delta = PositionDelta { base_asset_amount: 11, quote_asset_amount: -120, - remainder_base_asset_amount: None, }; let mut market = PerpMarket { amm: AMM { @@ -2525,7 +1920,6 @@ fn close_long_profitable() { let position_delta = PositionDelta { base_asset_amount: -10, quote_asset_amount: 15, - remainder_base_asset_amount: None, }; let mut market = PerpMarket { amm: AMM { @@ -2576,7 +1970,6 @@ fn close_long_unprofitable() { let position_delta = PositionDelta { base_asset_amount: -10, quote_asset_amount: 5, - remainder_base_asset_amount: None, }; let mut market = PerpMarket { amm: AMM { @@ -2626,7 +2019,6 @@ fn close_short_profitable() { let position_delta = PositionDelta { base_asset_amount: 10, quote_asset_amount: -5, - remainder_base_asset_amount: None, }; let mut market = PerpMarket { amm: AMM { @@ -2674,7 +2066,6 @@ fn close_short_unprofitable() { let position_delta = PositionDelta { base_asset_amount: 10, quote_asset_amount: -15, - remainder_base_asset_amount: None, }; let mut market = PerpMarket { amm: AMM { @@ -2722,7 +2113,6 @@ fn close_long_with_quote_break_even_amount_less_than_quote_asset_amount() { let position_delta = PositionDelta { base_asset_amount: -10, quote_asset_amount: 5, - remainder_base_asset_amount: None, }; let mut market = PerpMarket { amm: AMM { @@ -2773,7 +2163,6 @@ fn close_short_with_quote_break_even_amount_more_than_quote_asset_amount() { let position_delta = PositionDelta { base_asset_amount: 10, quote_asset_amount: -15, - remainder_base_asset_amount: None, }; let mut market = PerpMarket { amm: AMM { @@ -2842,7 +2231,15 @@ fn update_amm_near_boundary() { OracleMap::load_one(&jto_market_account_info, slot, None).unwrap(); let mut perp_market = perp_market_loader.load_mut().unwrap(); + assert_eq!(perp_market.amm.base_asset_amount_with_amm, 23831444927173); + assert_eq!( + perp_market.amm.base_asset_amount_with_unsettled_lp, + 562555072827 + ); + perp_market.amm.base_asset_amount_with_amm += + perp_market.amm.base_asset_amount_with_unsettled_lp; + perp_market.amm.base_asset_amount_with_unsettled_lp = 0; println!("perp_market: {:?}", perp_market.amm.last_update_slot); let oracle_price_data = oracle_map.get_price_data(&perp_market.oracle_id()).unwrap(); @@ -2851,9 +2248,12 @@ fn update_amm_near_boundary() { .unwrap(); let state = State::default(); + // perp_market.amm.sqrt_k -= perp_market.amm.user_lp_shares; + // perp_market.amm.user_lp_shares = 0; let cost = _update_amm(&mut perp_market, &mm_oracle_price_data, &state, now, slot).unwrap(); - + // assert_eq!(perp_market.amm.sqrt_k, 3295995551718929); + // assert_eq!(perp_market.amm.user_lp_shares, 267371000000000); assert_eq!(cost, 18803837952); } @@ -3165,7 +2565,7 @@ fn test_move_amm() { // let perp_market_old = market_map.get_ref(&4).unwrap(); let mut perp_market = market_map.get_ref_mut(&9).unwrap(); - + perp_market.amm.base_asset_amount_with_amm = -3092 * BASE_PRECISION as i128; println!( "perp_market latest slot: {:?}", perp_market.amm.last_update_slot @@ -3194,7 +2594,7 @@ fn test_move_amm() { assert_eq!(cost, 0); let inv = perp_market.amm.base_asset_amount_with_amm; - assert_eq!(inv, -291516212); + assert_eq!(inv, -3092000000000); let (_, _, r1_orig, r2_orig) = calculate_base_swap_output_with_spread( &perp_market.amm, @@ -3203,8 +2603,8 @@ fn test_move_amm() { ) .unwrap(); - assert_eq!(r1_orig, 326219); - assert_eq!(r2_orig, 20707); + assert_eq!(r1_orig, 3489128798); + assert_eq!(r2_orig, 215737299); let current_bar = perp_market.amm.base_asset_reserve; let _current_qar = perp_market.amm.quote_asset_reserve; let current_k = perp_market.amm.sqrt_k; diff --git a/programs/drift/src/controller/repeg.rs b/programs/drift/src/controller/repeg.rs index aad0a5db5c..c8dce61000 100644 --- a/programs/drift/src/controller/repeg.rs +++ b/programs/drift/src/controller/repeg.rs @@ -348,7 +348,7 @@ pub fn settle_expired_market( )?; validate!( - market.amm.base_asset_amount_with_unsettled_lp == 0 && market.amm.user_lp_shares == 0, + market.amm.base_asset_amount_with_unsettled_lp == 0, ErrorCode::MarketSettlementRequiresSettledLP, "Outstanding LP in market" )?; diff --git a/programs/drift/src/instructions/admin.rs b/programs/drift/src/instructions/admin.rs index a019e58a57..a4ecc2f8d1 100644 --- a/programs/drift/src/instructions/admin.rs +++ b/programs/drift/src/instructions/admin.rs @@ -2351,14 +2351,6 @@ pub fn handle_update_k(ctx: Context, sqrt_k: u128) -> Result<()> { perp_market.amm.sqrt_k )?; - validate!( - perp_market.amm.sqrt_k > perp_market.amm.user_lp_shares, - ErrorCode::InvalidUpdateK, - "cannot decrease sqrt_k={} below user_lp_shares={}", - perp_market.amm.sqrt_k, - perp_market.amm.user_lp_shares - )?; - perp_market.amm.total_fee_minus_distributions = perp_market .amm .total_fee_minus_distributions @@ -3432,59 +3424,6 @@ pub fn handle_update_perp_market_curve_update_intensity( Ok(()) } -#[access_control( - perp_market_valid(&ctx.accounts.perp_market) -)] -pub fn handle_update_perp_market_target_base_asset_amount_per_lp( - ctx: Context, - target_base_asset_amount_per_lp: i32, -) -> Result<()> { - let perp_market = &mut load_mut!(ctx.accounts.perp_market)?; - msg!("perp market {}", perp_market.market_index); - - msg!( - "perp_market.amm.target_base_asset_amount_per_lp: {} -> {}", - perp_market.amm.target_base_asset_amount_per_lp, - target_base_asset_amount_per_lp - ); - - perp_market.amm.target_base_asset_amount_per_lp = target_base_asset_amount_per_lp; - Ok(()) -} - -pub fn handle_update_perp_market_per_lp_base( - ctx: Context, - per_lp_base: i8, -) -> Result<()> { - let perp_market = &mut load_mut!(ctx.accounts.perp_market)?; - msg!("perp market {}", perp_market.market_index); - - let old_per_lp_base = perp_market.amm.per_lp_base; - msg!( - "updated perp_market per_lp_base {} -> {}", - old_per_lp_base, - per_lp_base - ); - - let expo_diff = per_lp_base.safe_sub(old_per_lp_base)?; - - validate!( - expo_diff.abs() == 1, - ErrorCode::DefaultError, - "invalid expo update (must be 1)", - )?; - - validate!( - per_lp_base.abs() <= 9, - ErrorCode::DefaultError, - "only consider lp_base within range of AMM_RESERVE_PRECISION", - )?; - - controller::lp::apply_lp_rebase_to_perp_market(perp_market, expo_diff)?; - - Ok(()) -} - pub fn handle_update_lp_cooldown_time( ctx: Context, lp_cooldown_time: u64, diff --git a/programs/drift/src/instructions/keeper.rs b/programs/drift/src/instructions/keeper.rs index c34b8dda82..1d29f0d46c 100644 --- a/programs/drift/src/instructions/keeper.rs +++ b/programs/drift/src/instructions/keeper.rs @@ -1073,37 +1073,6 @@ pub fn handle_settle_funding_payment<'c: 'info, 'info>( Ok(()) } -#[access_control( - amm_not_paused(&ctx.accounts.state) -)] -pub fn handle_settle_lp<'c: 'info, 'info>( - ctx: Context<'_, '_, 'c, 'info, SettleLP>, - market_index: u16, -) -> Result<()> { - let user_key = ctx.accounts.user.key(); - let user = &mut load_mut!(ctx.accounts.user)?; - - let state = &ctx.accounts.state; - let clock = Clock::get()?; - let now = clock.unix_timestamp; - - let AccountMaps { - perp_market_map, .. - } = load_maps( - &mut ctx.remaining_accounts.iter().peekable(), - &get_writable_perp_market_set(market_index), - &MarketSet::new(), - clock.slot, - Some(state.oracle_guard_rails), - )?; - - let market = &mut perp_market_map.get_ref_mut(&market_index)?; - controller::lp::settle_funding_payment_then_lp(user, &user_key, market, now)?; - user.update_last_active_slot(clock.slot); - - Ok(()) -} - #[access_control( liq_not_paused(&ctx.accounts.state) )] diff --git a/programs/drift/src/instructions/user.rs b/programs/drift/src/instructions/user.rs index 3f11a30c10..86229bc1bd 100644 --- a/programs/drift/src/instructions/user.rs +++ b/programs/drift/src/instructions/user.rs @@ -11,6 +11,7 @@ use anchor_spl::{ use solana_program::program::invoke; use solana_program::system_instruction::transfer; +use crate::controller::funding::settle_funding_payment; use crate::controller::orders::{cancel_orders, ModifyOrderId}; use crate::controller::position::update_position_and_market; use crate::controller::position::PositionDirection; @@ -1611,14 +1612,14 @@ pub fn handle_transfer_perp_position<'c: 'info, 'info>( &clock, )?; - controller::lp::settle_funding_payment_then_lp( + settle_funding_payment( &mut from_user, &from_user_key, perp_market_map.get_ref_mut(&market_index)?.deref_mut(), now, )?; - controller::lp::settle_funding_payment_then_lp( + settle_funding_payment( &mut to_user, &to_user_key, perp_market_map.get_ref_mut(&market_index)?.deref_mut(), @@ -2924,211 +2925,6 @@ pub fn handle_place_and_make_spot_order<'c: 'info, 'info>( Ok(()) } -#[access_control( - amm_not_paused(&ctx.accounts.state) -)] -pub fn handle_add_perp_lp_shares<'c: 'info, 'info>( - ctx: Context<'_, '_, 'c, 'info, AddRemoveLiquidity<'info>>, - n_shares: u64, - market_index: u16, -) -> Result<()> { - let user_key = ctx.accounts.user.key(); - let user = &mut load_mut!(ctx.accounts.user)?; - let state = &ctx.accounts.state; - let clock = Clock::get()?; - let now = clock.unix_timestamp; - - #[cfg(not(feature = "anchor-test"))] - { - msg!("add_perp_lp_shares is disabled"); - return Err(ErrorCode::DefaultError.into()); - } - - let AccountMaps { - perp_market_map, - spot_market_map, - mut oracle_map, - } = load_maps( - &mut ctx.remaining_accounts.iter().peekable(), - &get_writable_perp_market_set(market_index), - &MarketSet::new(), - clock.slot, - Some(state.oracle_guard_rails), - )?; - - validate!(!user.is_bankrupt(), ErrorCode::UserBankrupt)?; - math::liquidation::validate_user_not_being_liquidated( - user, - &perp_market_map, - &spot_market_map, - &mut oracle_map, - state.liquidation_margin_buffer_ratio, - )?; - - { - let mut market = perp_market_map.get_ref_mut(&market_index)?; - - validate!( - matches!(market.status, MarketStatus::Active), - ErrorCode::MarketStatusInvalidForNewLP, - "Market Status doesn't allow for new LP liquidity" - )?; - - validate!( - !matches!(market.contract_type, ContractType::Prediction), - ErrorCode::MarketStatusInvalidForNewLP, - "Contract Type doesn't allow for LP liquidity" - )?; - - validate!( - !market.is_operation_paused(PerpOperation::AmmFill), - ErrorCode::MarketStatusInvalidForNewLP, - "Market amm fills paused" - )?; - - validate!( - n_shares >= market.amm.order_step_size, - ErrorCode::NewLPSizeTooSmall, - "minting {} shares is less than step size {}", - n_shares, - market.amm.order_step_size, - )?; - - controller::funding::settle_funding_payment(user, &user_key, &mut market, now)?; - - // standardize n shares to mint - let n_shares = crate::math::orders::standardize_base_asset_amount( - n_shares.cast()?, - market.amm.order_step_size, - )? - .cast::()?; - - controller::lp::mint_lp_shares( - user.force_get_perp_position_mut(market_index)?, - &mut market, - n_shares, - )?; - - user.last_add_perp_lp_shares_ts = now; - } - - // check margin requirements - meets_place_order_margin_requirement( - user, - &perp_market_map, - &spot_market_map, - &mut oracle_map, - true, - )?; - - user.update_last_active_slot(clock.slot); - - emit!(LPRecord { - ts: now, - action: LPAction::AddLiquidity, - user: user_key, - n_shares, - market_index, - ..LPRecord::default() - }); - - Ok(()) -} - -pub fn handle_remove_perp_lp_shares_in_expiring_market<'c: 'info, 'info>( - ctx: Context<'_, '_, 'c, 'info, RemoveLiquidityInExpiredMarket<'info>>, - shares_to_burn: u64, - market_index: u16, -) -> Result<()> { - let user_key = ctx.accounts.user.key(); - let user = &mut load_mut!(ctx.accounts.user)?; - - let state = &ctx.accounts.state; - let clock = Clock::get()?; - let now = clock.unix_timestamp; - - let AccountMaps { - perp_market_map, - mut oracle_map, - .. - } = load_maps( - &mut ctx.remaining_accounts.iter().peekable(), - &get_writable_perp_market_set(market_index), - &MarketSet::new(), - clock.slot, - Some(state.oracle_guard_rails), - )?; - - // additional validate - { - let signer_is_admin = ctx.accounts.signer.key() == admin_hot_wallet::id(); - let market = perp_market_map.get_ref(&market_index)?; - validate!( - market.is_reduce_only()? || signer_is_admin, - ErrorCode::PerpMarketNotInReduceOnly, - "Can only permissionless burn when market is in reduce only" - )?; - } - - controller::lp::remove_perp_lp_shares( - perp_market_map, - &mut oracle_map, - state, - user, - user_key, - shares_to_burn, - market_index, - now, - )?; - - user.update_last_active_slot(clock.slot); - - Ok(()) -} - -#[access_control( - amm_not_paused(&ctx.accounts.state) -)] -pub fn handle_remove_perp_lp_shares<'c: 'info, 'info>( - ctx: Context<'_, '_, 'c, 'info, AddRemoveLiquidity<'info>>, - shares_to_burn: u64, - market_index: u16, -) -> Result<()> { - let user_key = ctx.accounts.user.key(); - let user = &mut load_mut!(ctx.accounts.user)?; - - let state = &ctx.accounts.state; - let clock = Clock::get()?; - let now = clock.unix_timestamp; - - let AccountMaps { - perp_market_map, - mut oracle_map, - .. - } = load_maps( - &mut ctx.remaining_accounts.iter().peekable(), - &get_writable_perp_market_set(market_index), - &MarketSet::new(), - clock.slot, - Some(state.oracle_guard_rails), - )?; - - controller::lp::remove_perp_lp_shares( - perp_market_map, - &mut oracle_map, - state, - user, - user_key, - shares_to_burn, - market_index, - now, - )?; - - user.update_last_active_slot(clock.slot); - - Ok(()) -} - pub fn handle_update_user_name( ctx: Context, _sub_account_id: u16, @@ -4622,25 +4418,6 @@ pub struct PlaceAndMatchRFQOrders<'info> { pub ix_sysvar: AccountInfo<'info>, } -#[derive(Accounts)] -pub struct AddRemoveLiquidity<'info> { - pub state: Box>, - #[account( - mut, - constraint = can_sign_for_user(&user, &authority)?, - )] - pub user: AccountLoader<'info, User>, - pub authority: Signer<'info>, -} - -#[derive(Accounts)] -pub struct RemoveLiquidityInExpiredMarket<'info> { - pub state: Box>, - #[account(mut)] - pub user: AccountLoader<'info, User>, - pub signer: Signer<'info>, -} - #[derive(Accounts)] #[instruction( sub_account_id: u16, diff --git a/programs/drift/src/lib.rs b/programs/drift/src/lib.rs index 92229f7e6a..9a14a80e59 100644 --- a/programs/drift/src/lib.rs +++ b/programs/drift/src/lib.rs @@ -351,30 +351,6 @@ pub mod drift { ) } - pub fn add_perp_lp_shares<'c: 'info, 'info>( - ctx: Context<'_, '_, 'c, 'info, AddRemoveLiquidity<'info>>, - n_shares: u64, - market_index: u16, - ) -> Result<()> { - handle_add_perp_lp_shares(ctx, n_shares, market_index) - } - - pub fn remove_perp_lp_shares<'c: 'info, 'info>( - ctx: Context<'_, '_, 'c, 'info, AddRemoveLiquidity<'info>>, - shares_to_burn: u64, - market_index: u16, - ) -> Result<()> { - handle_remove_perp_lp_shares(ctx, shares_to_burn, market_index) - } - - pub fn remove_perp_lp_shares_in_expiring_market<'c: 'info, 'info>( - ctx: Context<'_, '_, 'c, 'info, RemoveLiquidityInExpiredMarket<'info>>, - shares_to_burn: u64, - market_index: u16, - ) -> Result<()> { - handle_remove_perp_lp_shares_in_expiring_market(ctx, shares_to_burn, market_index) - } - pub fn update_user_name( ctx: Context, _sub_account_id: u16, @@ -567,13 +543,6 @@ pub mod drift { handle_settle_funding_payment(ctx) } - pub fn settle_lp<'c: 'info, 'info>( - ctx: Context<'_, '_, 'c, 'info, SettleLP>, - market_index: u16, - ) -> Result<()> { - handle_settle_lp(ctx, market_index) - } - pub fn settle_expired_market<'c: 'info, 'info>( ctx: Context<'_, '_, 'c, 'info, AdminUpdatePerpMarket<'info>>, market_index: u16, @@ -1397,23 +1366,6 @@ pub mod drift { handle_update_perp_market_curve_update_intensity(ctx, curve_update_intensity) } - pub fn update_perp_market_target_base_asset_amount_per_lp( - ctx: Context, - target_base_asset_amount_per_lp: i32, - ) -> Result<()> { - handle_update_perp_market_target_base_asset_amount_per_lp( - ctx, - target_base_asset_amount_per_lp, - ) - } - - pub fn update_perp_market_per_lp_base( - ctx: Context, - per_lp_base: i8, - ) -> Result<()> { - handle_update_perp_market_per_lp_base(ctx, per_lp_base) - } - pub fn update_lp_cooldown_time( ctx: Context, lp_cooldown_time: u64, diff --git a/programs/drift/src/math/amm_jit.rs b/programs/drift/src/math/amm_jit.rs index 17179b24b3..8614402ab9 100644 --- a/programs/drift/src/math/amm_jit.rs +++ b/programs/drift/src/math/amm_jit.rs @@ -133,19 +133,11 @@ pub fn calculate_clamped_jit_base_asset_amount( .cast::()?; // bound it; dont flip the net_baa - let max_amm_base_asset_amount = if liquidity_split != AMMLiquiditySplit::LPOwned { - market - .amm - .base_asset_amount_with_amm - .unsigned_abs() - .cast::()? - } else { - market - .amm - .imbalanced_base_asset_amount_with_lp()? - .unsigned_abs() - .cast::()? - }; + let max_amm_base_asset_amount = market + .amm + .base_asset_amount_with_amm + .unsigned_abs() + .cast::()?; let jit_base_asset_amount = jit_base_asset_amount.min(max_amm_base_asset_amount); @@ -161,10 +153,9 @@ pub fn calculate_amm_jit_liquidity( taker_base_asset_amount: u64, maker_base_asset_amount: u64, taker_has_limit_price: bool, - amm_lp_allowed_to_jit_make: Option, ) -> DriftResult<(u64, AMMLiquiditySplit)> { let mut jit_base_asset_amount: u64 = 0; - let mut liquidity_split: AMMLiquiditySplit = AMMLiquiditySplit::ProtocolOwned; + let liquidity_split: AMMLiquiditySplit = AMMLiquiditySplit::ProtocolOwned; // taker has_limit_price = false means (limit price = 0 AND auction is complete) so // market order will always land and fill on amm next round @@ -177,33 +168,7 @@ pub fn calculate_amm_jit_liquidity( } let amm_wants_to_jit_make = market.amm.amm_wants_to_jit_make(taker_direction)?; - let amm_lp_wants_to_jit_make = market.amm.amm_lp_wants_to_jit_make(taker_direction)?; - let amm_lp_allowed_to_jit_make = match amm_lp_allowed_to_jit_make { - Some(allowed) => allowed, - None => market - .amm - .amm_lp_allowed_to_jit_make(amm_wants_to_jit_make)?, - }; - let split_with_lps = amm_lp_allowed_to_jit_make && amm_lp_wants_to_jit_make; - if amm_wants_to_jit_make { - liquidity_split = if split_with_lps { - AMMLiquiditySplit::Shared - } else { - AMMLiquiditySplit::ProtocolOwned - }; - - jit_base_asset_amount = calculate_jit_base_asset_amount( - market, - base_asset_amount, - maker_price, - valid_oracle_price, - taker_direction, - liquidity_split, - )?; - } else if split_with_lps { - liquidity_split = AMMLiquiditySplit::LPOwned; - jit_base_asset_amount = calculate_jit_base_asset_amount( market, base_asset_amount, diff --git a/programs/drift/src/math/bankruptcy.rs b/programs/drift/src/math/bankruptcy.rs index 400c6504e8..287b103060 100644 --- a/programs/drift/src/math/bankruptcy.rs +++ b/programs/drift/src/math/bankruptcy.rs @@ -22,7 +22,6 @@ pub fn is_user_bankrupt(user: &User) -> bool { if perp_position.base_asset_amount != 0 || perp_position.quote_asset_amount > 0 || perp_position.has_open_order() - || perp_position.is_lp() { return false; } diff --git a/programs/drift/src/math/cp_curve/tests.rs b/programs/drift/src/math/cp_curve/tests.rs index 9448a6cd40..ca7de7e987 100644 --- a/programs/drift/src/math/cp_curve/tests.rs +++ b/programs/drift/src/math/cp_curve/tests.rs @@ -1,7 +1,4 @@ use crate::controller::amm::update_spreads; -use crate::controller::lp::burn_lp_shares; -use crate::controller::lp::mint_lp_shares; -use crate::controller::lp::settle_lp_position; use crate::controller::position::PositionDirection; use crate::math::amm::calculate_bid_ask_bounds; use crate::math::constants::BASE_PRECISION; @@ -338,7 +335,7 @@ fn amm_spread_adj_logic() { ..PerpPosition::default() }; - mint_lp_shares(&mut position, &mut market, BASE_PRECISION_U64).unwrap(); + // todo fix this market.amm.base_asset_amount_per_lp = 1; market.amm.quote_asset_amount_per_lp = -QUOTE_PRECISION_I64 as i128; @@ -369,173 +366,3 @@ fn amm_spread_adj_logic() { assert_eq!(market.amm.long_spread, 110); assert_eq!(market.amm.short_spread, 110); } - -#[test] -fn calculate_k_with_lps_tests() { - let mut market = PerpMarket { - amm: AMM { - base_asset_reserve: 100 * AMM_RESERVE_PRECISION, - quote_asset_reserve: 100 * AMM_RESERVE_PRECISION, - terminal_quote_asset_reserve: 999900009999000 * AMM_RESERVE_PRECISION, - sqrt_k: 100 * AMM_RESERVE_PRECISION, - peg_multiplier: 50_000_000_000, - base_asset_amount_with_amm: (AMM_RESERVE_PRECISION / 10) as i128, - base_asset_amount_long: (AMM_RESERVE_PRECISION / 10) as i128, - order_step_size: 5, - max_spread: 1000, - ..AMM::default_test() - }, - margin_ratio_initial: 1000, - ..PerpMarket::default() - }; - // let (t_price, _t_qar, _t_bar) = calculate_terminal_price_and_reserves(&market.amm).unwrap(); - // market.amm.terminal_quote_asset_reserve = _t_qar; - - let mut position = PerpPosition { - ..PerpPosition::default() - }; - - mint_lp_shares(&mut position, &mut market, BASE_PRECISION_U64).unwrap(); - - market.amm.base_asset_amount_per_lp = 1; - market.amm.quote_asset_amount_per_lp = -QUOTE_PRECISION_I64 as i128; - - let reserve_price = market.amm.reserve_price().unwrap(); - update_spreads(&mut market, reserve_price, None).unwrap(); - - settle_lp_position(&mut position, &mut market).unwrap(); - - assert_eq!(position.base_asset_amount, 0); - assert_eq!(position.quote_asset_amount, -QUOTE_PRECISION_I64); - assert_eq!(position.last_base_asset_amount_per_lp, 1); - assert_eq!( - position.last_quote_asset_amount_per_lp, - -QUOTE_PRECISION_I64 - ); - - // increase k by 1% - let update_k_up = - get_update_k_result(&market, bn::U192::from(102 * AMM_RESERVE_PRECISION), false).unwrap(); - let (t_price, _t_qar, _t_bar) = - amm::calculate_terminal_price_and_reserves(&market.amm).unwrap(); - - // new terminal reserves are balanced, terminal price = peg) - // assert_eq!(t_qar, 999900009999000); - // assert_eq!(t_bar, 1000100000000000); - assert_eq!(t_price, 49901136949); // - // assert_eq!(update_k_up.sqrt_k, 101 * AMM_RESERVE_PRECISION); - - let cost = adjust_k_cost(&mut market, &update_k_up).unwrap(); - assert_eq!( - market.amm.base_asset_amount_with_amm, - (AMM_RESERVE_PRECISION / 10) as i128 - ); - assert_eq!(cost, 49400); //0.05 - - // lp whale adds - let lp_whale_amount = 1000 * BASE_PRECISION_U64; - mint_lp_shares(&mut position, &mut market, lp_whale_amount).unwrap(); - - // ensure same cost - let update_k_up = - get_update_k_result(&market, bn::U192::from(1102 * AMM_RESERVE_PRECISION), false).unwrap(); - let cost = adjust_k_cost(&mut market, &update_k_up).unwrap(); - assert_eq!( - market.amm.base_asset_amount_with_amm, - (AMM_RESERVE_PRECISION / 10) as i128 - ); - assert_eq!(cost, 49450); //0.05 - - let update_k_down = - get_update_k_result(&market, bn::U192::from(1001 * AMM_RESERVE_PRECISION), false).unwrap(); - let cost = adjust_k_cost(&mut market, &update_k_down).unwrap(); - assert_eq!(cost, -4995004950); //amm rug - - // lp whale removes - burn_lp_shares(&mut position, &mut market, lp_whale_amount, 0).unwrap(); - - // ensure same cost - let update_k_up = - get_update_k_result(&market, bn::U192::from(102 * AMM_RESERVE_PRECISION), false).unwrap(); - let cost = adjust_k_cost(&mut market, &update_k_up).unwrap(); - assert_eq!( - market.amm.base_asset_amount_with_amm, - (AMM_RESERVE_PRECISION / 10) as i128 - 1 - ); - assert_eq!(cost, 49450); //0.05 - - let update_k_down = - get_update_k_result(&market, bn::U192::from(79 * AMM_RESERVE_PRECISION), false).unwrap(); - let cost = adjust_k_cost(&mut market, &update_k_down).unwrap(); - assert_eq!(cost, -1407000); //0.05 - - // lp owns 50% of vAMM, same k - position.lp_shares = 50 * BASE_PRECISION_U64; - market.amm.user_lp_shares = 50 * AMM_RESERVE_PRECISION; - // cost to increase k is always positive when imbalanced - let cost = adjust_k_cost(&mut market, &update_k_up).unwrap(); - assert_eq!( - market.amm.base_asset_amount_with_amm, - (AMM_RESERVE_PRECISION / 10) as i128 - 1 - ); - assert_eq!(cost, 187800); //0.19 - - // lp owns 99% of vAMM, same k - position.lp_shares = 99 * BASE_PRECISION_U64; - market.amm.user_lp_shares = 99 * AMM_RESERVE_PRECISION; - let cost2 = adjust_k_cost(&mut market, &update_k_up).unwrap(); - assert!(cost2 > cost); - assert_eq!(cost2, 76804900); //216.45 - - // lp owns 100% of vAMM, same k - position.lp_shares = 100 * BASE_PRECISION_U64; - market.amm.user_lp_shares = 100 * AMM_RESERVE_PRECISION; - let cost3 = adjust_k_cost(&mut market, &update_k_up).unwrap(); - assert!(cost3 > cost); - assert!(cost3 > cost2); - assert_eq!(cost3, 216450200); - - // // todo: support this - // market.amm.base_asset_amount_with_amm = -(AMM_RESERVE_PRECISION as i128); - // let cost2 = adjust_k_cost(&mut market, &update_k_up).unwrap(); - // assert!(cost2 > cost); - // assert_eq!(cost2, 249999999999850000000001); -} - -#[test] -fn calculate_bid_ask_per_lp_token() { - let (bound1_s, bound2_s) = - calculate_bid_ask_bounds(MAX_CONCENTRATION_COEFFICIENT, 24704615072091).unwrap(); - - assert_eq!(bound1_s, 17468968372288); - assert_eq!(bound2_s, 34937266634951); - - let (bound1, bound2) = calculate_bid_ask_bounds( - MAX_CONCENTRATION_COEFFICIENT, - 24704615072091 + BASE_PRECISION, - ) - .unwrap(); - - assert_eq!(bound1 - bound1_s, 707113563); - assert_eq!(bound2 - bound2_s, 1414200000); - - let more_conc = - CONCENTRATION_PRECISION + (MAX_CONCENTRATION_COEFFICIENT - CONCENTRATION_PRECISION) / 20; - - let (bound1_s, bound2_s) = calculate_bid_ask_bounds(more_conc, 24704615072091).unwrap(); - - assert_eq!(bound1_s, 24203363415750); - assert_eq!(bound2_s, 25216247650234); - - let (bound1, bound2) = - calculate_bid_ask_bounds(more_conc, 24704615072091 + BASE_PRECISION).unwrap(); - - assert_eq!(bound1 - bound1_s, 979710202); - assert_eq!(bound2 - bound2_s, 1020710000); - - let (bound1_3, bound2_3) = - calculate_bid_ask_bounds(more_conc, 24704615072091 + 2 * BASE_PRECISION).unwrap(); - - assert_eq!(bound1_3 - bound1_s, 979710202 * 2); - assert_eq!(bound2_3 - bound2_s, 1020710000 * 2); -} diff --git a/programs/drift/src/math/funding.rs b/programs/drift/src/math/funding.rs index 5fc9d78a3a..c723cb37bb 100644 --- a/programs/drift/src/math/funding.rs +++ b/programs/drift/src/math/funding.rs @@ -27,10 +27,7 @@ pub fn calculate_funding_rate_long_short( ) -> DriftResult<(i128, i128, i128)> { // Calculate the funding payment owed by the net_market_position if funding is not capped // If the net market position owes funding payment, the protocol receives payment - let settled_net_market_position = market - .amm - .base_asset_amount_with_amm - .safe_add(market.amm.base_asset_amount_with_unsettled_lp)?; + let settled_net_market_position = market.amm.base_asset_amount_with_amm; let net_market_position_funding_payment = calculate_funding_payment_in_quote_precision(funding_rate, settled_net_market_position)?; diff --git a/programs/drift/src/math/funding/tests.rs b/programs/drift/src/math/funding/tests.rs index 704355bd07..3fb3c37514 100644 --- a/programs/drift/src/math/funding/tests.rs +++ b/programs/drift/src/math/funding/tests.rs @@ -251,246 +251,6 @@ fn capped_sym_funding_test() { assert_eq!(new_fees, 1012295); // made over $.50 } -#[test] -fn funding_unsettled_lps_amm_win_test() { - // more shorts than longs, positive funding - - // positive base_asset_amount_with_unsettled_lp = - // 1) lots of long users who have lp as counterparty - // 2) the lps should be short but its unsettled... - // 3) amm takes on the funding revenu/cost of those short LPs - - let mut market = PerpMarket { - amm: AMM { - base_asset_reserve: 512295081967, - quote_asset_reserve: 488 * AMM_RESERVE_PRECISION, - sqrt_k: 500 * AMM_RESERVE_PRECISION, - peg_multiplier: 50000000, - base_asset_amount_with_amm: -12295081967, //~12 - base_asset_amount_long: 12295081967, - base_asset_amount_short: -12295081967 * 2, - base_asset_amount_with_unsettled_lp: (AMM_RESERVE_PRECISION * 500) as i128, //wowsers - total_exchange_fee: QUOTE_PRECISION / 2, - total_fee_minus_distributions: (QUOTE_PRECISION as i128) / 2, - - last_mark_price_twap: 50 * PRICE_PRECISION_U64, - historical_oracle_data: HistoricalOracleData { - last_oracle_price_twap: (49 * PRICE_PRECISION) as i64, - - ..HistoricalOracleData::default() - }, - funding_period: 3600, - - ..AMM::default() - }, - ..PerpMarket::default() - }; - - let balanced_funding = calculate_funding_rate( - market.amm.last_mark_price_twap as u128, - market.amm.historical_oracle_data.last_oracle_price_twap as i128, - market.amm.funding_period, - ) - .unwrap(); - - assert_eq!(balanced_funding, 41666666); - assert_eq!(market.amm.total_fee_minus_distributions, 500000); - - let (long_funding, short_funding, _) = - calculate_funding_rate_long_short(&mut market, balanced_funding).unwrap(); - - let settled_net_market_position = market - .amm - .base_asset_amount_with_amm - .checked_add(market.amm.base_asset_amount_with_unsettled_lp) - .unwrap(); - - let net_market_position_funding_payment = - calculate_funding_payment_in_quote_precision(balanced_funding, settled_net_market_position) - .unwrap(); - let uncapped_funding_pnl = -net_market_position_funding_payment; - - assert_eq!(market.amm.base_asset_amount_with_amm, -12295081967); - assert_eq!(market.amm.base_asset_amount_with_unsettled_lp, 500000000000); - assert_eq!(settled_net_market_position, 487704918033); - assert_eq!(net_market_position_funding_payment, -20321037); - assert_eq!(uncapped_funding_pnl, 20321037); //protocol revenue - - assert_eq!(long_funding, balanced_funding); - assert_eq!(short_funding, balanced_funding); - - assert!(long_funding == short_funding); - - // making money off unsettled lps - assert_eq!(market.amm.total_fee_minus_distributions, 20821037); - - // more longs than shorts, positive funding, amm earns funding - market = PerpMarket { - amm: AMM { - base_asset_reserve: 512295081967, - quote_asset_reserve: 488 * AMM_RESERVE_PRECISION, - sqrt_k: 500 * AMM_RESERVE_PRECISION, - peg_multiplier: 50000000, - base_asset_amount_with_amm: 12295081967, - base_asset_amount_long: 12295081967 * 2, - base_asset_amount_short: -12295081967, - base_asset_amount_with_unsettled_lp: (AMM_RESERVE_PRECISION * 500) as i128, //wowsers - total_exchange_fee: QUOTE_PRECISION / 2, - total_fee_minus_distributions: (QUOTE_PRECISION as i128) / 2, - last_mark_price_twap: 50 * PRICE_PRECISION_U64, - historical_oracle_data: HistoricalOracleData { - last_oracle_price_twap: (49 * PRICE_PRECISION) as i64, - - ..HistoricalOracleData::default() - }, - funding_period: 3600, - - ..AMM::default() - }, - ..PerpMarket::default() - }; - - let balanced_funding = calculate_funding_rate( - market.amm.last_mark_price_twap as u128, - market.amm.historical_oracle_data.last_oracle_price_twap as i128, - market.amm.funding_period, - ) - .unwrap(); - assert_eq!(balanced_funding, 41666666); - - let (long_funding, short_funding, drift_pnl) = - calculate_funding_rate_long_short(&mut market, balanced_funding).unwrap(); - - assert_eq!(drift_pnl, 21345628); - assert_eq!(long_funding, balanced_funding); - assert_eq!(long_funding, short_funding); - let new_fees = market.amm.total_fee_minus_distributions; - assert!(new_fees > QUOTE_PRECISION as i128 / 2); - assert_eq!(new_fees, 21845628); // made more -} - -#[test] -fn funding_unsettled_lps_amm_lose_test() { - // more shorts than longs, positive funding - - // positive base_asset_amount_with_unsettled_lp = - // 1) lots of long users who have lp as counterparty - // 2) the lps should be short but its unsettled... - // 3) amm takes on the funding revenu/cost of those short LPs - - let mut market = PerpMarket { - amm: AMM { - base_asset_reserve: 512295081967, - quote_asset_reserve: 488 * AMM_RESERVE_PRECISION, - sqrt_k: 500 * AMM_RESERVE_PRECISION, - peg_multiplier: 50000000, - base_asset_amount_with_amm: -12295081967, //~12 - base_asset_amount_long: 12295081967, - base_asset_amount_short: -12295081967 * 2, - base_asset_amount_with_unsettled_lp: -((AMM_RESERVE_PRECISION * 500) as i128), //wowsers - total_exchange_fee: QUOTE_PRECISION / 2, - total_fee_minus_distributions: ((QUOTE_PRECISION * 99999) as i128), - - last_mark_price_twap: 50 * PRICE_PRECISION_U64, - historical_oracle_data: HistoricalOracleData { - last_oracle_price_twap: (49 * PRICE_PRECISION) as i64, - - ..HistoricalOracleData::default() - }, - funding_period: 3600, - - ..AMM::default() - }, - ..PerpMarket::default() - }; - - let balanced_funding = calculate_funding_rate( - market.amm.last_mark_price_twap as u128, - market.amm.historical_oracle_data.last_oracle_price_twap as i128, - market.amm.funding_period, - ) - .unwrap(); - - assert_eq!(balanced_funding, 41666666); - assert_eq!(market.amm.total_fee_minus_distributions, 99999000000); - - let (long_funding, short_funding, _) = - calculate_funding_rate_long_short(&mut market, balanced_funding).unwrap(); - - let settled_net_market_position = market - .amm - .base_asset_amount_with_amm - .checked_add(market.amm.base_asset_amount_with_unsettled_lp) - .unwrap(); - - let net_market_position_funding_payment = - calculate_funding_payment_in_quote_precision(balanced_funding, settled_net_market_position) - .unwrap(); - let uncapped_funding_pnl = -net_market_position_funding_payment; - - assert_eq!(market.amm.base_asset_amount_with_amm, -12295081967); - assert_eq!( - market.amm.base_asset_amount_with_unsettled_lp, - -500000000000 - ); - assert_eq!(settled_net_market_position, -512295081967); - assert_eq!(net_market_position_funding_payment, 21345628); - assert_eq!(uncapped_funding_pnl, -21345628); //protocol loses $21 - - assert_eq!(long_funding, balanced_funding); - assert_eq!(short_funding, balanced_funding); - - assert!(long_funding == short_funding); - - // making money off unsettled lps - assert_eq!(market.amm.total_fee_minus_distributions, 99977654372); - - // more longs than shorts, positive funding, amm earns funding - market = PerpMarket { - amm: AMM { - base_asset_reserve: 512295081967, - quote_asset_reserve: 488 * AMM_RESERVE_PRECISION, - sqrt_k: 500 * AMM_RESERVE_PRECISION, - peg_multiplier: 50000000, - base_asset_amount_with_amm: 12295081967, - base_asset_amount_long: 12295081967 * 2, - base_asset_amount_short: -12295081967, - base_asset_amount_with_unsettled_lp: -((AMM_RESERVE_PRECISION * 500) as i128), //wowsers - total_exchange_fee: QUOTE_PRECISION / 2, - total_fee_minus_distributions: (QUOTE_PRECISION as i128) / 2, - last_mark_price_twap: 50 * PRICE_PRECISION_U64, - historical_oracle_data: HistoricalOracleData { - last_oracle_price_twap: (49 * PRICE_PRECISION) as i64, - - ..HistoricalOracleData::default() - }, - funding_period: 3600, - - ..AMM::default() - }, - ..PerpMarket::default() - }; - - let balanced_funding = calculate_funding_rate( - market.amm.last_mark_price_twap as u128, - market.amm.historical_oracle_data.last_oracle_price_twap as i128, - market.amm.funding_period, - ) - .unwrap(); - assert_eq!(balanced_funding, 41666666); - - let (long_funding, short_funding, drift_pnl) = - calculate_funding_rate_long_short(&mut market, balanced_funding).unwrap(); - - assert_eq!(drift_pnl, -20321037); - assert_eq!(long_funding, balanced_funding); - assert_eq!(short_funding, 90110989); - assert_eq!(long_funding < short_funding, true); - - let new_fees = market.amm.total_fee_minus_distributions; - assert_eq!(new_fees, 416667); // lost -} - #[test] fn max_funding_rates() { let now = 0_i64; @@ -616,10 +376,9 @@ fn unsettled_funding_pnl() { quote_asset_reserve: 488 * AMM_RESERVE_PRECISION, sqrt_k: 500 * AMM_RESERVE_PRECISION, peg_multiplier: 50000000, - base_asset_amount_with_amm: -12295081967, //~12 + base_asset_amount_with_amm: -12295081967 + -((AMM_RESERVE_PRECISION * 500) as i128), //~ 12 - 500 base_asset_amount_long: 12295081967, base_asset_amount_short: -12295081967 * 2, - base_asset_amount_with_unsettled_lp: -((AMM_RESERVE_PRECISION * 500) as i128), //wowsers total_exchange_fee: QUOTE_PRECISION / 2, total_fee_minus_distributions: ((QUOTE_PRECISION * 99999) as i128), diff --git a/programs/drift/src/math/lp.rs b/programs/drift/src/math/lp.rs deleted file mode 100644 index a65ae33adb..0000000000 --- a/programs/drift/src/math/lp.rs +++ /dev/null @@ -1,191 +0,0 @@ -use crate::error::{DriftResult, ErrorCode}; -use crate::msg; -use crate::{ - validate, MARGIN_PRECISION_U128, PRICE_PRECISION, PRICE_TIMES_AMM_TO_QUOTE_PRECISION_RATIO, -}; -use std::u64; - -use crate::math::amm::calculate_market_open_bids_asks; -use crate::math::casting::Cast; -use crate::math::helpers; -use crate::math::margin::MarginRequirementType; -use crate::math::orders::{ - standardize_base_asset_amount, standardize_base_asset_amount_ceil, - standardize_base_asset_amount_with_remainder_i128, -}; -use crate::math::safe_math::SafeMath; - -use crate::state::perp_market::PerpMarket; -use crate::state::perp_market::AMM; -use crate::state::user::PerpPosition; - -#[cfg(test)] -mod tests; - -#[derive(Debug)] -pub struct LPMetrics { - pub base_asset_amount: i128, - pub quote_asset_amount: i128, - pub remainder_base_asset_amount: i128, -} - -pub fn calculate_settle_lp_metrics(amm: &AMM, position: &PerpPosition) -> DriftResult { - let (base_asset_amount, quote_asset_amount) = calculate_settled_lp_base_quote(amm, position)?; - - // stepsize it - let (standardized_base_asset_amount, remainder_base_asset_amount) = - standardize_base_asset_amount_with_remainder_i128( - base_asset_amount, - amm.order_step_size.cast()?, - )?; - - let lp_metrics = LPMetrics { - base_asset_amount: standardized_base_asset_amount, - quote_asset_amount, - remainder_base_asset_amount: remainder_base_asset_amount.cast()?, - }; - - Ok(lp_metrics) -} - -pub fn calculate_settled_lp_base_quote( - amm: &AMM, - position: &PerpPosition, -) -> DriftResult<(i128, i128)> { - let n_shares = position.lp_shares; - let base_unit: i128 = amm.get_per_lp_base_unit()?; - - validate!( - amm.per_lp_base == position.per_lp_base, - ErrorCode::InvalidPerpPositionDetected, - "calculate_settled_lp_base_quote :: position/market per_lp_base unequal {} != {}", - position.per_lp_base, - amm.per_lp_base - )?; - - let n_shares_i128 = n_shares.cast::()?; - - // give them slice of the damm market position - let amm_net_base_asset_amount_per_lp = amm - .base_asset_amount_per_lp - .safe_sub(position.last_base_asset_amount_per_lp.cast()?)?; - - let base_asset_amount = amm_net_base_asset_amount_per_lp - .cast::()? - .safe_mul(n_shares_i128)? - .safe_div(base_unit)?; - - let amm_net_quote_asset_amount_per_lp = amm - .quote_asset_amount_per_lp - .safe_sub(position.last_quote_asset_amount_per_lp.cast()?)?; - - let quote_asset_amount = amm_net_quote_asset_amount_per_lp - .cast::()? - .safe_mul(n_shares_i128)? - .safe_div(base_unit)?; - - Ok((base_asset_amount, quote_asset_amount)) -} - -pub fn calculate_lp_open_bids_asks( - market_position: &PerpPosition, - market: &PerpMarket, -) -> DriftResult<(i64, i64)> { - let total_lp_shares = market.amm.sqrt_k; - let lp_shares = market_position.lp_shares; - - let (max_bids, max_asks) = calculate_market_open_bids_asks(&market.amm)?; - let open_asks = helpers::get_proportion_i128(max_asks, lp_shares.cast()?, total_lp_shares)?; - let open_bids = helpers::get_proportion_i128(max_bids, lp_shares.cast()?, total_lp_shares)?; - - Ok((open_bids.cast()?, open_asks.cast()?)) -} - -pub fn calculate_lp_shares_to_burn_for_risk_reduction( - perp_position: &PerpPosition, - market: &PerpMarket, - oracle_price: i64, - quote_oracle_price: i64, - margin_shortage: u128, - user_custom_margin_ratio: u32, - user_high_leverage_mode: bool, -) -> DriftResult<(u64, u64)> { - let settled_lp_position = perp_position.simulate_settled_lp_position(market, oracle_price)?; - - let worse_case_base_asset_amount = - settled_lp_position.worst_case_base_asset_amount(oracle_price, market.contract_type)?; - - let open_orders_from_lp_shares = if worse_case_base_asset_amount >= 0 { - worse_case_base_asset_amount.safe_sub( - settled_lp_position - .base_asset_amount - .safe_add(perp_position.open_bids)? - .cast()?, - )? - } else { - worse_case_base_asset_amount.safe_sub( - settled_lp_position - .base_asset_amount - .safe_add(perp_position.open_asks)? - .cast()?, - )? - }; - - let margin_ratio = market - .get_margin_ratio( - worse_case_base_asset_amount.unsigned_abs(), - MarginRequirementType::Initial, - user_high_leverage_mode, - )? - .max(user_custom_margin_ratio); - - let base_asset_amount_to_cover = margin_shortage - .safe_mul(PRICE_TIMES_AMM_TO_QUOTE_PRECISION_RATIO)? - .safe_div( - oracle_price - .cast::()? - .safe_mul(quote_oracle_price.cast()?)? - .safe_div(PRICE_PRECISION)? - .safe_mul(margin_ratio.cast()?)? - .safe_div(MARGIN_PRECISION_U128)?, - )? - .cast::()?; - - let current_base_asset_amount = settled_lp_position.base_asset_amount.unsigned_abs(); - - // if closing position is enough to cover margin shortage, then only a small % of lp shares need to be burned - if base_asset_amount_to_cover < current_base_asset_amount { - let base_asset_amount_to_close = standardize_base_asset_amount_ceil( - base_asset_amount_to_cover, - market.amm.order_step_size, - )? - .min(current_base_asset_amount); - let lp_shares_to_burn = standardize_base_asset_amount( - settled_lp_position.lp_shares / 10, - market.amm.order_step_size, - )? - .max(market.amm.order_step_size); - return Ok((lp_shares_to_burn, base_asset_amount_to_close)); - } - - let base_asset_amount_to_cover = - base_asset_amount_to_cover.safe_sub(current_base_asset_amount)?; - - let percent_to_burn = base_asset_amount_to_cover - .cast::()? - .safe_mul(100)? - .safe_div_ceil(open_orders_from_lp_shares.unsigned_abs())?; - - let lp_shares_to_burn = settled_lp_position - .lp_shares - .cast::()? - .safe_mul(percent_to_burn.cast()?)? - .safe_div_ceil(100)? - .cast::()?; - - let standardized_lp_shares_to_burn = - standardize_base_asset_amount_ceil(lp_shares_to_burn, market.amm.order_step_size)? - .clamp(market.amm.order_step_size, settled_lp_position.lp_shares); - - Ok((standardized_lp_shares_to_burn, current_base_asset_amount)) -} diff --git a/programs/drift/src/math/lp/tests.rs b/programs/drift/src/math/lp/tests.rs deleted file mode 100644 index c55253e0a5..0000000000 --- a/programs/drift/src/math/lp/tests.rs +++ /dev/null @@ -1,451 +0,0 @@ -use crate::math::constants::AMM_RESERVE_PRECISION; -use crate::math::lp::*; -use crate::state::user::PerpPosition; - -mod calculate_get_proportion_u128 { - use crate::math::helpers::get_proportion_u128; - - use super::*; - - pub fn get_proportion_u128_safe( - value: u128, - numerator: u128, - denominator: u128, - ) -> DriftResult { - if numerator == 0 { - return Ok(0); - } - - let proportional_value = if numerator <= denominator { - let ratio = denominator.safe_mul(10000)?.safe_div(numerator)?; - value.safe_mul(10000)?.safe_div(ratio)? - } else { - value.safe_mul(numerator)?.safe_div(denominator)? - }; - - Ok(proportional_value) - } - - #[test] - fn test_safe() { - let sqrt_k = AMM_RESERVE_PRECISION * 10_123; - let max_reserve = sqrt_k * 14121 / 10000; - let max_asks = max_reserve - sqrt_k; - - // let ans1 = get_proportion_u128_safe(max_asks, sqrt_k - sqrt_k / 100, sqrt_k).unwrap(); - // let ans2 = get_proportion_u128(max_asks, sqrt_k - sqrt_k / 100, sqrt_k).unwrap(); - // assert_eq!(ans1, ans2); //fails - - let ans1 = get_proportion_u128_safe(max_asks, sqrt_k / 2, sqrt_k).unwrap(); - let ans2 = get_proportion_u128(max_asks, sqrt_k / 2, sqrt_k).unwrap(); - assert_eq!(ans1, ans2); - - let ans1 = get_proportion_u128_safe(max_asks, AMM_RESERVE_PRECISION, sqrt_k).unwrap(); - let ans2 = get_proportion_u128(max_asks, AMM_RESERVE_PRECISION, sqrt_k).unwrap(); - assert_eq!(ans1, ans2); - - let ans1 = get_proportion_u128_safe(max_asks, 0, sqrt_k).unwrap(); - let ans2 = get_proportion_u128(max_asks, 0, sqrt_k).unwrap(); - assert_eq!(ans1, ans2); - - let ans1 = get_proportion_u128_safe(max_asks, 1325324, sqrt_k).unwrap(); - let ans2 = get_proportion_u128(max_asks, 1325324, sqrt_k).unwrap(); - assert_eq!(ans1, ans2); - - // let ans1 = get_proportion_u128(max_asks, sqrt_k, sqrt_k).unwrap(); - // assert_eq!(ans1, max_asks); - } -} - -mod calculate_lp_open_bids_asks { - use super::*; - - #[test] - fn test_simple_lp_bid_ask() { - let position = PerpPosition { - lp_shares: 100, - ..PerpPosition::default() - }; - - let amm = AMM { - base_asset_reserve: 10, - max_base_asset_reserve: 100, - min_base_asset_reserve: 0, - sqrt_k: 200, - ..AMM::default_test() - }; - let market = PerpMarket { - amm, - ..PerpMarket::default_test() - }; - - let (open_bids, open_asks) = calculate_lp_open_bids_asks(&position, &market).unwrap(); - - assert_eq!(open_bids, 10 * 100 / 200); - assert_eq!(open_asks, -90 * 100 / 200); - } - - #[test] - fn test_max_ask() { - let position = PerpPosition { - lp_shares: 100, - ..PerpPosition::default() - }; - - let amm = AMM { - base_asset_reserve: 0, - max_base_asset_reserve: 100, - min_base_asset_reserve: 0, - sqrt_k: 200, - ..AMM::default_test() - }; - let market = PerpMarket { - amm, - ..PerpMarket::default_test() - }; - - let (open_bids, open_asks) = calculate_lp_open_bids_asks(&position, &market).unwrap(); - - assert_eq!(open_bids, 0); // wont go anymore short - assert_eq!(open_asks, -100 * 100 / 200); - } - - #[test] - fn test_max_bid() { - let position = PerpPosition { - lp_shares: 100, - ..PerpPosition::default() - }; - - let amm = AMM { - base_asset_reserve: 10, - max_base_asset_reserve: 10, - min_base_asset_reserve: 0, - sqrt_k: 200, - ..AMM::default_test() - }; - let market = PerpMarket { - amm, - ..PerpMarket::default_test() - }; - - let (open_bids, open_asks) = calculate_lp_open_bids_asks(&position, &market).unwrap(); - - assert_eq!(open_bids, 10 * 100 / 200); - assert_eq!(open_asks, 0); // no more long - } -} - -mod calculate_settled_lp_base_quote { - use crate::math::constants::BASE_PRECISION_U64; - - use super::*; - - #[test] - fn test_long_settle() { - let position = PerpPosition { - lp_shares: 100 * BASE_PRECISION_U64, - ..PerpPosition::default() - }; - - let amm = AMM { - base_asset_amount_per_lp: 10, - quote_asset_amount_per_lp: -10, - ..AMM::default_test() - }; - - let (baa, qaa) = calculate_settled_lp_base_quote(&amm, &position).unwrap(); - - assert_eq!(baa, 10 * 100); - assert_eq!(qaa, -10 * 100); - } - - #[test] - fn test_short_settle() { - let position = PerpPosition { - lp_shares: 100 * BASE_PRECISION_U64, - ..PerpPosition::default() - }; - - let amm = AMM { - base_asset_amount_per_lp: -10, - quote_asset_amount_per_lp: 10, - ..AMM::default_test() - }; - - let (baa, qaa) = calculate_settled_lp_base_quote(&amm, &position).unwrap(); - - assert_eq!(baa, -10 * 100); - assert_eq!(qaa, 10 * 100); - } -} - -mod calculate_settle_lp_metrics { - use crate::math::constants::BASE_PRECISION_U64; - - use super::*; - - #[test] - fn test_long_settle() { - let position = PerpPosition { - lp_shares: 100 * BASE_PRECISION_U64, - ..PerpPosition::default() - }; - - let amm = AMM { - base_asset_amount_per_lp: 10, - quote_asset_amount_per_lp: -10, - order_step_size: 1, - ..AMM::default_test() - }; - - let lp_metrics = calculate_settle_lp_metrics(&amm, &position).unwrap(); - - assert_eq!(lp_metrics.base_asset_amount, 10 * 100); - assert_eq!(lp_metrics.quote_asset_amount, -10 * 100); - assert_eq!(lp_metrics.remainder_base_asset_amount, 0); - } - - #[test] - fn test_all_remainder() { - let position = PerpPosition { - lp_shares: 100 * BASE_PRECISION_U64, - ..PerpPosition::default() - }; - - let amm = AMM { - base_asset_amount_per_lp: 10, - quote_asset_amount_per_lp: -10, - order_step_size: 50 * 100, - ..AMM::default_test() - }; - - let lp_metrics = calculate_settle_lp_metrics(&amm, &position).unwrap(); - - assert_eq!(lp_metrics.base_asset_amount, 0); - assert_eq!(lp_metrics.quote_asset_amount, -10 * 100); - assert_eq!(lp_metrics.remainder_base_asset_amount, 10 * 100); - } - - #[test] - fn test_portion_remainder() { - let position = PerpPosition { - lp_shares: BASE_PRECISION_U64, - ..PerpPosition::default() - }; - - let amm = AMM { - base_asset_amount_per_lp: 10, - quote_asset_amount_per_lp: -10, - order_step_size: 3, - ..AMM::default_test() - }; - - let lp_metrics = calculate_settle_lp_metrics(&amm, &position).unwrap(); - - assert_eq!(lp_metrics.base_asset_amount, 9); - assert_eq!(lp_metrics.quote_asset_amount, -10); - assert_eq!(lp_metrics.remainder_base_asset_amount, 1); - } -} - -mod calculate_lp_shares_to_burn_for_risk_reduction { - use crate::math::lp::calculate_lp_shares_to_burn_for_risk_reduction; - use crate::state::perp_market::PerpMarket; - use crate::state::user::User; - use crate::test_utils::create_account_info; - use crate::{PRICE_PRECISION_I64, QUOTE_PRECISION}; - use anchor_lang::prelude::AccountLoader; - use solana_program::pubkey::Pubkey; - use std::str::FromStr; - - #[test] - fn test() { - let user_str = String::from("n3Vf4++XOuwuqzjlmLoHfrMxu0bx1zK4CI3jhlcn84aSUBauaSLU4gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAARHJpZnQgTGlxdWlkaXR5IFByb3ZpZGVyICAgICAgICAbACHcCQAAAAAAAAAAAAAAAAAAAAAAAACcpMgCAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2ssqwBAAAAAATnHP3///+9awAIAAAAAKnr8AcAAAAAqufxBwAAAAAAAAAAAAAAAAAAAAAAAAAAuITI//////8AeTlTJwAAANxGF1tu/P//abUakBEAAACBFNL6BAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA+hUCAAAAAAAAAAAAAAAAACC8EHuk9f//1uYrCQMAAAAAAAAACQAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAANv7p2UAAAAAnKTIAgAAAAAAAAAAAAAAAAAAAAAAAAAAsprK//////8AAAAAAAAAAPeGAgAAAAAAAAAAAAAAAAAzkaIOAAAAAA8AAACIEwAAAQACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="); - let mut decoded_bytes = base64::decode(user_str).unwrap(); - let user_bytes = decoded_bytes.as_mut_slice(); - - let key = Pubkey::default(); - let owner = Pubkey::from_str("dRiftyHA39MWEi3m9aunc5MzRF1JYuBsbn6VPcn33UH").unwrap(); - let mut lamports = 0; - let user_account_info = create_account_info(&key, true, &mut lamports, user_bytes, &owner); - - let user_loader: AccountLoader = AccountLoader::try_from(&user_account_info).unwrap(); - let mut user = user_loader.load_mut().unwrap(); - let position = &mut user.perp_positions[0]; - - let perp_market_str = String::from("Ct8MLGv1N/cU6tVVkVpIHdjrXil5+Blo7M7no01SEzFkvCN2nSnel3KwISF8o/5okioZqvmQEJy52E6a0AS00gJa1vUpMUQZgG2jAAAAAAAAAAAAAAAAAAMAAAAAAAAAiKOiAAAAAAATRqMAAAAAAEr2u2UAAAAA3EYXW278/////////////2m1GpARAAAAAAAAAAAAAACRgrV0qi0BAAAAAAAAAAAAAAAAAAAAAABFREBhQ1YEAAAAAAAAAAAA9sh+SuuHBwAAAAAAAAAAACaTDwAAAAAAAAAAAAAAAADvHx32D0IEAAAAAAAAAAAA67nFJa5vBAAAAAAAAAAAAHMxOUELtwUAAAAAAAAAAACqHV4AAAAAAAAAAAAAAAAApw4iE86DBwAAAAAAAAAAAADzSoISXwAAAAAAAAAAAAAAHtBmbKP/////////////CreY1F8CAAAAAAAAAAAAAPZZghQfAAAAAAAAAAAAAAAAQGNSv8YBAAAAAAAAAAAAUdkndDAAAAAAAAAAAAAAAEEeAcSS/v/////////////0bAXnbQEAAAAAAAAAAAAAPuj0I3f+/////////////6felr+KAQAAAAAAAAAAAABX2/mMhMQCAAAAAAAAAAAALukbAAAAAAAu6RsAAAAAAC7pGwAAAAAAqPUJAAAAAADkPmeWogAAAAAAAAAAAAAAsD8vhpIAAAAAAAAAAAAAACibCEwQAAAAAAAAAAAAAAAr/d/xbQAAAAAAAAAAAAAAwY+XFgAAAAAAAAAAAAAAAMyF/KFFAAAAAAAAAAAAAAA9rLKsAQAAAAAAAAAAAAAAPayyrAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB+6JzGf04EAAAAAAAAAAAAtqLk+X6VBwAAAAAAAAAAAPDUDdGDVwQAAAAAAAAAAABeb5d+v4UHAAAAAAAAAAAAgG2jAAAAAAAAAAAAAAAAACJ6ogAAAAAAE0qkAAAAAAAaYqMAAAAAAIF1pAAAAAAArJmiDgAAAAAlBwAAAAAAAN5ukP7/////veq7ZQAAAAAQDgAAAAAAAADh9QUAAAAAZAAAAAAAAAAAZc0dAAAAAAAAAAAAAAAAiuqcc0QAAAA8R6NuAQAAAIyqSgkAAAAAt+27ZQAAAAATCAEAAAAAAPjJAAAAAAAASva7ZQAAAACUEQAAoIYBALQ2AADKCAAASQEAAH0AAAD0ATIAZMgEAQAAAAAEAAAAfRuiDQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAhv4EJ8hQEAAAAAAAAAAAAAAAAAAAAAADFNQk9OSy1QRVJQICAgICAgICAgICAgICAgICAgICAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAG8VAwAAAAAA+x4AAAAAAACFAwAAAAAAACYCAADuAgAAqGEAAFDDAADECQAA3AUAAAAAAAAQJwAABwQAAA0GAAAEAAEAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="); - let mut decoded_bytes = base64::decode(perp_market_str).unwrap(); - let perp_market_bytes = decoded_bytes.as_mut_slice(); - - let key = Pubkey::default(); - let owner = Pubkey::from_str("dRiftyHA39MWEi3m9aunc5MzRF1JYuBsbn6VPcn33UH").unwrap(); - let mut lamports = 0; - let perp_market_account_info = - create_account_info(&key, true, &mut lamports, perp_market_bytes, &owner); - - let perp_market_loader: AccountLoader = - AccountLoader::try_from(&perp_market_account_info).unwrap(); - let perp_market = perp_market_loader.load_mut().unwrap(); - - let oracle_price = 10 * PRICE_PRECISION_I64; - let quote_oracle_price = PRICE_PRECISION_I64; - - let margin_shortage = 40 * QUOTE_PRECISION; - - let (lp_shares_to_burn, base_asset_amount) = - calculate_lp_shares_to_burn_for_risk_reduction( - position, - &perp_market, - oracle_price, - quote_oracle_price, - margin_shortage, - 0, - false, - ) - .unwrap(); - - assert_eq!(lp_shares_to_burn, 168900000000); - assert_eq!(base_asset_amount, 12400000000); - - let margin_shortage = 20 * QUOTE_PRECISION; - - let (lp_shares_to_burn, base_asset_amount) = - calculate_lp_shares_to_burn_for_risk_reduction( - position, - &perp_market, - oracle_price, - quote_oracle_price, - margin_shortage, - 0, - false, - ) - .unwrap(); - - assert_eq!(lp_shares_to_burn, 16800000000); - assert_eq!(base_asset_amount, 8000000000); - - let margin_shortage = 5 * QUOTE_PRECISION; - - let (lp_shares_to_burn, base_asset_amount) = - calculate_lp_shares_to_burn_for_risk_reduction( - position, - &perp_market, - oracle_price, - quote_oracle_price, - margin_shortage, - 0, - false, - ) - .unwrap(); - - assert_eq!(lp_shares_to_burn, 16800000000); - assert_eq!(base_asset_amount, 2000000000); - - // flip existing position the other direction - position.base_asset_amount = -position.base_asset_amount; - - let margin_shortage = 40 * QUOTE_PRECISION; - - let (lp_shares_to_burn, base_asset_amount) = - calculate_lp_shares_to_burn_for_risk_reduction( - position, - &perp_market, - oracle_price, - quote_oracle_price, - margin_shortage, - 0, - false, - ) - .unwrap(); - - assert_eq!(lp_shares_to_burn, 168900000000); - assert_eq!(base_asset_amount, 12400000000); - - let margin_shortage = 20 * QUOTE_PRECISION; - - let (lp_shares_to_burn, base_asset_amount) = - calculate_lp_shares_to_burn_for_risk_reduction( - position, - &perp_market, - oracle_price, - quote_oracle_price, - margin_shortage, - 0, - false, - ) - .unwrap(); - - assert_eq!(lp_shares_to_burn, 16800000000); - assert_eq!(base_asset_amount, 8000000000); - - let margin_shortage = 5 * QUOTE_PRECISION; - - let (lp_shares_to_burn, base_asset_amount) = - calculate_lp_shares_to_burn_for_risk_reduction( - position, - &perp_market, - oracle_price, - quote_oracle_price, - margin_shortage, - 0, - false, - ) - .unwrap(); - - assert_eq!(lp_shares_to_burn, 16800000000); - assert_eq!(base_asset_amount, 2000000000); - } - - #[test] - fn custom_margin_ratio() { - let user_str = String::from("n3Vf4++XOuwIrD1jL22rz6RZlEfmZHqxneDBS0Mflxjd93h2f2ldQwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAdGl0c29jY2VyICAgICAgICAgICAgICAgICAgICAgICDnqurCZBgAAAAAAAAAAAAAAAAAAAAAAADOM8akAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgO0UfBAAAAPeaGv//////SM9HIAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAyRaK3v////8AAAAAAAAAAPeaGv//////SM9HIAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGq0wmUAAAAATspepQEAAACAlpgAAAAAAAAAAAAAAAAAGn3imQQAAAAAAAAAAAAAACMV2Pf/////AAAAAAAAAAB7Ro0QAAAAACYAAAAQJwAAAQADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="); - let mut decoded_bytes = base64::decode(user_str).unwrap(); - let user_bytes = decoded_bytes.as_mut_slice(); - - let key = Pubkey::default(); - let owner = Pubkey::from_str("dRiftyHA39MWEi3m9aunc5MzRF1JYuBsbn6VPcn33UH").unwrap(); - let mut lamports = 0; - let user_account_info = create_account_info(&key, true, &mut lamports, user_bytes, &owner); - - let user_loader: AccountLoader = AccountLoader::try_from(&user_account_info).unwrap(); - let mut user = user_loader.load_mut().unwrap(); - let user_custom_margin_ratio = user.max_margin_ratio; - let position = &mut user.perp_positions[0]; - - let perp_market_str = String::from("Ct8MLGv1N/cV6vWLwJY+18dY2GsrmrNldgnISB7pmbcf7cn9S4FZ4PnAFyuhDfpNGQiNlPW/YdO1TVvXSDoyKpguE3PujqMbEGDwDQoAAAAAAAAAAAAAAAIAAAAAAAAAC7rzDQoAAABst/MNCgAAACK5wmUAAAAAceZN/////////////////3v2pRcAAAAAAAAAAAAAAADlXWMfRwMAAAAAAAAAAAAAAAAAAAAAAABkI6UNRQAAAAAAAAAAAAAAqfLzd0UAAAAAAAAAAAAAACaTDwAAAAAAAAAAAAAAAAAPPu6ERAAAAAAAAAAAAAAA9NX/YkcAAAAAAAAAAAAAAI0luEJFAAAAAAAAAAAAAAD9eI3+CQAAAAAAAAAAAAAAIZTqlkQAAAAAAAAAAAAAAIBENlMCAAAAAAAAAAAAAADgfcyL/v//////////////meiO4gAAAAAAAAAAAAAAAMfZc/z///////////////8AoHJOGAkAAAAAAAAAAAAAnURpyQAAAAAAAAAAAAAAAOYK1g3F//////////////9I7emQMwAAAAAAAAAAAAAAKoCi9MT//////////////3cTS98zAAAAAAAAAAAAAAAgO0UfBAAAAAAAAAAAAAAAGV4SEgAAAAAZXhISAAAAABleEhIAAAAAA0itQgAAAAAhNkO9CQAAAAAAAAAAAAAAMTlM9gcAAAAAAAAAAAAAAGJujdoBAAAAAAAAAAAAAADvEtDaAgAAAAAAAAAAAAAAOLU20gIAAAAAAAAAAAAAALu3wLsCAAAAAAAAAAAAAABdkcJWBgUAAAAAAAAAAAAANhdFnLwEAAAAAAAAAAAAAEDj5IkCAAAAAAAAAAAAAACCV2gFRQAAAAAAAAAAAAAAPWo+gEUAAAAAAAAAAAAAAIIDPw5FAAAAAAAAAAAAAAAAJ1l3RQAAAAAAAAAAAAAAEGDwDQoAAAAAAAAAAAAAAE05yg0KAAAAUbrzDQoAAADP+d4NCgAAAC3a3g0KAAAAGVONEAAAAAACAQAAAAAAAGDUKbz/////G7nCZQAAAAAQDgAAAAAAAKCGAQAAAAAAoIYBAAAAAABAQg8AAAAAAAAAAAAAAAAA3+0VvzsAAAAAAAAAAAAAACbWIwUKAAAAIbnCZQAAAABNyBIAAAAAAPtZAwAAAAAAIbnCZQAAAAAKAAAA6AMAAKQDAABEAAAAAAAAAP0pAABkADIAZMgAAQDKmjsAAAAA1jQGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB0ezvzkkgAAAAAAAAAAAAAAAAAAAAAAAEJUQy1QRVJQICAgICAgICAgICAgICAgICAgICAgICAg6AMAAAAAAADoAwAAAAAAAOgDAAAAAAAA6AMAAAAAAAAiucJlAAAAABAnAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJsXAwAAAAAAIhAAAAAAAACHAQAAAAAAABAnAAAQJwAAECcAABAnAAD0AQAAkAEAAAAAAAABAAAAFwAAABsAAAABAAEAAgAAALX/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="); - let mut decoded_bytes = base64::decode(perp_market_str).unwrap(); - let perp_market_bytes = decoded_bytes.as_mut_slice(); - - let key = Pubkey::default(); - let owner = Pubkey::from_str("dRiftyHA39MWEi3m9aunc5MzRF1JYuBsbn6VPcn33UH").unwrap(); - let mut lamports = 0; - let perp_market_account_info = - create_account_info(&key, true, &mut lamports, perp_market_bytes, &owner); - - let perp_market_loader: AccountLoader = - AccountLoader::try_from(&perp_market_account_info).unwrap(); - let perp_market = perp_market_loader.load_mut().unwrap(); - - let oracle_price = 43174 * PRICE_PRECISION_I64; - let quote_oracle_price = PRICE_PRECISION_I64; - - let margin_shortage = 2077 * QUOTE_PRECISION; - - let (lp_shares_to_burn, base_asset_amount) = - calculate_lp_shares_to_burn_for_risk_reduction( - position, - &perp_market, - oracle_price, - quote_oracle_price, - margin_shortage, - user_custom_margin_ratio, - false, - ) - .unwrap(); - - assert_eq!(lp_shares_to_burn, 1770400000); - assert_eq!(base_asset_amount, 48200000); - assert_eq!(position.lp_shares, 17704500000); - } -} diff --git a/programs/drift/src/math/margin.rs b/programs/drift/src/math/margin.rs index 6aee60be04..f62bbb6122 100644 --- a/programs/drift/src/math/margin.rs +++ b/programs/drift/src/math/margin.rs @@ -119,8 +119,6 @@ pub fn calculate_perp_position_value_and_pnl( market_position, )?; - let market_position = market_position.simulate_settled_lp_position(market, valuation_price)?; - let (base_asset_value, unrealized_pnl) = calculate_base_asset_value_and_pnl_with_oracle_price(&market_position, valuation_price)?; @@ -149,12 +147,8 @@ pub fn calculate_perp_position_value_and_pnl( }; // add small margin requirement for every open order - margin_requirement = margin_requirement - .safe_add(market_position.margin_requirement_for_open_orders()?)? - .safe_add( - market_position - .margin_requirement_for_lp_shares(market.amm.order_step_size, valuation_price)?, - )?; + margin_requirement = + margin_requirement.safe_add(market_position.margin_requirement_for_open_orders()?)?; let unrealized_asset_weight = market.get_unrealized_asset_weight(total_unrealized_pnl, margin_requirement_type)?; @@ -584,8 +578,7 @@ pub fn calculate_margin_requirement_and_total_collateral_and_liability_info( let has_perp_liability = market_position.base_asset_amount != 0 || market_position.quote_asset_amount < 0 - || market_position.has_open_order() - || market_position.is_lp(); + || market_position.has_open_order(); if has_perp_liability { calculation.add_perp_liability()?; @@ -978,9 +971,6 @@ pub fn calculate_user_equity( market_position, )?; - let market_position = - market_position.simulate_settled_lp_position(market, valuation_price)?; - let (_, unrealized_pnl) = calculate_base_asset_value_and_pnl_with_oracle_price( &market_position, valuation_price, diff --git a/programs/drift/src/math/margin/tests.rs b/programs/drift/src/math/margin/tests.rs index 33763abd05..7a256b65db 100644 --- a/programs/drift/src/math/margin/tests.rs +++ b/programs/drift/src/math/margin/tests.rs @@ -394,154 +394,6 @@ mod test { let ans = (0).nth_root(2); assert_eq!(ans, 0); } - - #[test] - fn test_lp_user_short() { - let mut market = PerpMarket { - market_index: 0, - amm: AMM { - base_asset_reserve: 5 * AMM_RESERVE_PRECISION, - quote_asset_reserve: 5 * AMM_RESERVE_PRECISION, - sqrt_k: 5 * AMM_RESERVE_PRECISION, - user_lp_shares: 10 * AMM_RESERVE_PRECISION, - max_base_asset_reserve: 10 * AMM_RESERVE_PRECISION, - ..AMM::default_test() - }, - margin_ratio_initial: 1000, - margin_ratio_maintenance: 500, - imf_factor: 1000, // 1_000/1_000_000 = .001 - unrealized_pnl_initial_asset_weight: 10000, - unrealized_pnl_maintenance_asset_weight: 10000, - ..PerpMarket::default() - }; - - let position = PerpPosition { - lp_shares: market.amm.user_lp_shares as u64, - ..PerpPosition::default() - }; - - let oracle_price_data = OraclePriceData { - price: (2 * PRICE_PRECISION) as i64, - confidence: 0, - delay: 2, - has_sufficient_number_of_data_points: true, - }; - - let strict_oracle_price = StrictOraclePrice::test(QUOTE_PRECISION_I64); - let (pmr, _, _, _, _) = calculate_perp_position_value_and_pnl( - &position, - &market, - &oracle_price_data, - &strict_oracle_price, - MarginRequirementType::Initial, - 0, - false, - false, - ) - .unwrap(); - - // make the market unbalanced - - let trade_size = 3 * AMM_RESERVE_PRECISION; - let (new_qar, new_bar) = calculate_swap_output( - trade_size, - market.amm.base_asset_reserve, - SwapDirection::Add, // user shorts - market.amm.sqrt_k, - ) - .unwrap(); - market.amm.quote_asset_reserve = new_qar; - market.amm.base_asset_reserve = new_bar; - - let (pmr2, _, _, _, _) = calculate_perp_position_value_and_pnl( - &position, - &market, - &oracle_price_data, - &strict_oracle_price, - MarginRequirementType::Initial, - 0, - false, - false, - ) - .unwrap(); - - // larger margin req in more unbalanced market - assert!(pmr2 > pmr) - } - - #[test] - fn test_lp_user_long() { - let mut market = PerpMarket { - market_index: 0, - amm: AMM { - base_asset_reserve: 5 * AMM_RESERVE_PRECISION, - quote_asset_reserve: 5 * AMM_RESERVE_PRECISION, - sqrt_k: 5 * AMM_RESERVE_PRECISION, - user_lp_shares: 10 * AMM_RESERVE_PRECISION, - max_base_asset_reserve: 10 * AMM_RESERVE_PRECISION, - ..AMM::default_test() - }, - margin_ratio_initial: 1000, - margin_ratio_maintenance: 500, - imf_factor: 1000, // 1_000/1_000_000 = .001 - unrealized_pnl_initial_asset_weight: 10000, - unrealized_pnl_maintenance_asset_weight: 10000, - ..PerpMarket::default() - }; - - let position = PerpPosition { - lp_shares: market.amm.user_lp_shares as u64, - ..PerpPosition::default() - }; - - let oracle_price_data = OraclePriceData { - price: (2 * PRICE_PRECISION) as i64, - confidence: 0, - delay: 2, - has_sufficient_number_of_data_points: true, - }; - - let strict_oracle_price = StrictOraclePrice::test(QUOTE_PRECISION_I64); - let (pmr, _, _, _, _) = calculate_perp_position_value_and_pnl( - &position, - &market, - &oracle_price_data, - &strict_oracle_price, - MarginRequirementType::Initial, - 0, - false, - false, - ) - .unwrap(); - - // make the market unbalanced - let trade_size = 3 * AMM_RESERVE_PRECISION; - let (new_qar, new_bar) = calculate_swap_output( - trade_size, - market.amm.base_asset_reserve, - SwapDirection::Remove, // user longs - market.amm.sqrt_k, - ) - .unwrap(); - market.amm.quote_asset_reserve = new_qar; - market.amm.base_asset_reserve = new_bar; - - let strict_oracle_price = StrictOraclePrice::test(QUOTE_PRECISION_I64); - let (pmr2, _, _, _, _) = calculate_perp_position_value_and_pnl( - &position, - &market, - &oracle_price_data, - &strict_oracle_price, - MarginRequirementType::Initial, - 0, - false, - false, - ) - .unwrap(); - - // larger margin req in more unbalanced market - assert!(pmr2 > pmr) - } } #[cfg(test)] diff --git a/programs/drift/src/math/mod.rs b/programs/drift/src/math/mod.rs index aa7ec7f196..89edbdafc5 100644 --- a/programs/drift/src/math/mod.rs +++ b/programs/drift/src/math/mod.rs @@ -16,7 +16,6 @@ pub mod funding; pub mod helpers; pub mod insurance; pub mod liquidation; -pub mod lp; pub mod margin; pub mod matching; pub mod oracle; diff --git a/programs/drift/src/math/orders.rs b/programs/drift/src/math/orders.rs index 3628d7f231..8f3aff9140 100644 --- a/programs/drift/src/math/orders.rs +++ b/programs/drift/src/math/orders.rs @@ -353,7 +353,6 @@ pub fn get_position_delta_for_fill( PositionDirection::Long => base_asset_amount.cast()?, PositionDirection::Short => -base_asset_amount.cast()?, }, - remainder_base_asset_amount: None, }) } diff --git a/programs/drift/src/math/position.rs b/programs/drift/src/math/position.rs index b16c7c1c34..998970480d 100644 --- a/programs/drift/src/math/position.rs +++ b/programs/drift/src/math/position.rs @@ -43,23 +43,17 @@ pub fn calculate_base_asset_value(base_asset_amount: i128, amm: &AMM) -> DriftRe let (base_asset_reserve, quote_asset_reserve) = (amm.base_asset_reserve, amm.quote_asset_reserve); - let amm_lp_shares = amm.sqrt_k.safe_sub(amm.user_lp_shares)?; - - let base_asset_reserve_proportion = - get_proportion_u128(base_asset_reserve, amm_lp_shares, amm.sqrt_k)?; - - let quote_asset_reserve_proportion = - get_proportion_u128(quote_asset_reserve, amm_lp_shares, amm.sqrt_k)?; + let amm_lp_shares = amm.sqrt_k; let (new_quote_asset_reserve, _new_base_asset_reserve) = amm::calculate_swap_output( base_asset_amount.unsigned_abs(), - base_asset_reserve_proportion, + base_asset_reserve, swap_direction, amm_lp_shares, )?; let base_asset_value = calculate_quote_asset_amount_swapped( - quote_asset_reserve_proportion, + quote_asset_reserve, new_quote_asset_reserve, swap_direction, amm.peg_multiplier, @@ -174,32 +168,19 @@ pub fn get_position_update_type( position: &PerpPosition, delta: &PositionDelta, ) -> DriftResult { - if position.base_asset_amount == 0 && position.remainder_base_asset_amount == 0 { + if position.base_asset_amount == 0 { return Ok(PositionUpdateType::Open); } - let position_base_with_remainder = if position.remainder_base_asset_amount != 0 { - position - .base_asset_amount - .safe_add(position.remainder_base_asset_amount.cast::()?)? - } else { - position.base_asset_amount - }; + let position_base = position.base_asset_amount; - let delta_base_with_remainder = - if let Some(remainder_base_asset_amount) = delta.remainder_base_asset_amount { - delta - .base_asset_amount - .safe_add(remainder_base_asset_amount.cast()?)? - } else { - delta.base_asset_amount - }; + let delta_base = delta.base_asset_amount; - if position_base_with_remainder.signum() == delta_base_with_remainder.signum() { + if position_base.signum() == delta_base.signum() { Ok(PositionUpdateType::Increase) - } else if position_base_with_remainder.abs() > delta_base_with_remainder.abs() { + } else if position_base.abs() > delta_base.abs() { Ok(PositionUpdateType::Reduce) - } else if position_base_with_remainder.abs() == delta_base_with_remainder.abs() { + } else if position_base.abs() == delta_base.abs() { Ok(PositionUpdateType::Close) } else { Ok(PositionUpdateType::Flip) @@ -209,8 +190,7 @@ pub fn get_position_update_type( pub fn get_new_position_amounts( position: &PerpPosition, delta: &PositionDelta, - market: &PerpMarket, -) -> DriftResult<(i64, i64, i64, i64)> { +) -> DriftResult<(i64, i64)> { let new_quote_asset_amount = position .quote_asset_amount .safe_add(delta.quote_asset_amount)?; @@ -219,48 +199,5 @@ pub fn get_new_position_amounts( .base_asset_amount .safe_add(delta.base_asset_amount)?; - let mut new_remainder_base_asset_amount = position - .remainder_base_asset_amount - .cast::()? - .safe_add( - delta - .remainder_base_asset_amount - .unwrap_or(0) - .cast::()?, - )?; - let mut new_settled_base_asset_amount = delta.base_asset_amount; - - if delta.remainder_base_asset_amount.is_some() { - if new_remainder_base_asset_amount.unsigned_abs() >= market.amm.order_step_size { - let (standardized_remainder_base_asset_amount, remainder_base_asset_amount) = - crate::math::orders::standardize_base_asset_amount_with_remainder_i128( - new_remainder_base_asset_amount.cast()?, - market.amm.order_step_size.cast()?, - )?; - - new_base_asset_amount = - new_base_asset_amount.safe_add(standardized_remainder_base_asset_amount.cast()?)?; - - new_settled_base_asset_amount = new_settled_base_asset_amount - .safe_add(standardized_remainder_base_asset_amount.cast()?)?; - - new_remainder_base_asset_amount = remainder_base_asset_amount.cast()?; - } else { - new_remainder_base_asset_amount = new_remainder_base_asset_amount.cast()?; - } - - validate!( - new_remainder_base_asset_amount.abs() <= i32::MAX as i64, - ErrorCode::InvalidPositionDelta, - "new_remainder_base_asset_amount={} > i32 max", - new_remainder_base_asset_amount - )?; - } - - Ok(( - new_base_asset_amount, - new_settled_base_asset_amount, - new_quote_asset_amount, - new_remainder_base_asset_amount, - )) + Ok((new_base_asset_amount, new_quote_asset_amount)) } diff --git a/programs/drift/src/state/perp_market.rs b/programs/drift/src/state/perp_market.rs index 3e9ffc2be0..44ffa61a44 100644 --- a/programs/drift/src/state/perp_market.rs +++ b/programs/drift/src/state/perp_market.rs @@ -595,29 +595,6 @@ impl PerpMarket { Ok(depth) } - pub fn update_market_with_counterparty( - &mut self, - delta: &PositionDelta, - new_settled_base_asset_amount: i64, - ) -> DriftResult { - // indicates that position delta is settling lp counterparty - if delta.remainder_base_asset_amount.is_some() { - // todo: name for this is confusing, but adding is correct as is - // definition: net position of users in the market that has the LP as a counterparty (which have NOT settled) - self.amm.base_asset_amount_with_unsettled_lp = self - .amm - .base_asset_amount_with_unsettled_lp - .safe_add(new_settled_base_asset_amount.cast()?)?; - - self.amm.quote_asset_amount_with_unsettled_lp = self - .amm - .quote_asset_amount_with_unsettled_lp - .safe_add(delta.quote_asset_amount.cast()?)?; - } - - Ok(()) - } - pub fn is_price_divergence_ok_for_settle_pnl(&self, oracle_price: i64) -> DriftResult { let oracle_divergence = oracle_price .safe_sub(self.amm.historical_oracle_data.last_oracle_price_twap_5min)? @@ -1354,18 +1331,13 @@ impl AMM { } pub fn get_lower_bound_sqrt_k(self) -> DriftResult { - Ok(self.sqrt_k.min( - self.user_lp_shares - .safe_add(self.user_lp_shares.safe_div(1000)?)? - .max(self.min_order_size.cast()?) - .max(self.base_asset_amount_with_amm.unsigned_abs().cast()?), - )) + Ok(self + .sqrt_k + .min((self.min_order_size.cast::()?).max(self.base_asset_amount_with_amm.unsigned_abs()))) } pub fn get_protocol_owned_position(self) -> DriftResult { - self.base_asset_amount_with_amm - .safe_add(self.base_asset_amount_with_unsettled_lp)? - .cast::() + self.base_asset_amount_with_amm.cast::() } pub fn get_max_reference_price_offset(self) -> DriftResult { @@ -1384,109 +1356,6 @@ impl AMM { Ok(max_offset) } - pub fn get_per_lp_base_unit(self) -> DriftResult { - let scalar: i128 = 10_i128.pow(self.per_lp_base.abs().cast()?); - - if self.per_lp_base > 0 { - AMM_RESERVE_PRECISION_I128.safe_mul(scalar) - } else { - AMM_RESERVE_PRECISION_I128.safe_div(scalar) - } - } - - pub fn calculate_lp_base_delta( - &self, - per_lp_delta_base: i128, - base_unit: i128, - ) -> DriftResult { - // calculate dedicated for user lp shares - let lp_delta_base = - get_proportion_i128(per_lp_delta_base, self.user_lp_shares, base_unit.cast()?)?; - - Ok(lp_delta_base) - } - - pub fn calculate_per_lp_delta( - &self, - delta: &PositionDelta, - fee_to_market: i128, - liquidity_split: AMMLiquiditySplit, - base_unit: i128, - ) -> DriftResult<(i128, i128, i128)> { - let total_lp_shares = if liquidity_split == AMMLiquiditySplit::LPOwned { - self.user_lp_shares - } else { - self.sqrt_k - }; - - // update Market per lp position - let per_lp_delta_base = get_proportion_i128( - delta.base_asset_amount.cast()?, - base_unit.cast()?, - total_lp_shares, //.safe_div_ceil(rebase_divisor.cast()?)?, - )?; - - let mut per_lp_delta_quote = get_proportion_i128( - delta.quote_asset_amount.cast()?, - base_unit.cast()?, - total_lp_shares, //.safe_div_ceil(rebase_divisor.cast()?)?, - )?; - - // user position delta is short => lp position delta is long - if per_lp_delta_base < 0 { - // add one => lp subtract 1 - per_lp_delta_quote = per_lp_delta_quote.safe_add(1)?; - } - - // 1/5 of fee auto goes to market - // the rest goes to lps/market proportional - let per_lp_fee: i128 = if fee_to_market > 0 { - get_proportion_i128( - fee_to_market, - LP_FEE_SLICE_NUMERATOR, - LP_FEE_SLICE_DENOMINATOR, - )? - .safe_mul(base_unit)? - .safe_div(total_lp_shares.cast::()?)? - } else { - 0 - }; - - Ok((per_lp_delta_base, per_lp_delta_quote, per_lp_fee)) - } - - pub fn get_target_base_asset_amount_per_lp(&self) -> DriftResult { - if self.target_base_asset_amount_per_lp == 0 { - return Ok(0_i128); - } - - let target_base_asset_amount_per_lp: i128 = if self.per_lp_base > 0 { - let rebase_divisor = 10_i128.pow(self.per_lp_base.abs().cast()?); - self.target_base_asset_amount_per_lp - .cast::()? - .safe_mul(rebase_divisor)? - } else if self.per_lp_base < 0 { - let rebase_divisor = 10_i128.pow(self.per_lp_base.abs().cast()?); - self.target_base_asset_amount_per_lp - .cast::()? - .safe_div(rebase_divisor)? - } else { - self.target_base_asset_amount_per_lp.cast::()? - }; - - Ok(target_base_asset_amount_per_lp) - } - - pub fn imbalanced_base_asset_amount_with_lp(&self) -> DriftResult { - let target_lp_gap = self - .base_asset_amount_per_lp - .safe_sub(self.get_target_base_asset_amount_per_lp()?)?; - - let base_unit = self.get_per_lp_base_unit()?.cast()?; - - get_proportion_i128(target_lp_gap, self.user_lp_shares, base_unit) - } - pub fn amm_wants_to_jit_make(&self, taker_direction: PositionDirection) -> DriftResult { let amm_wants_to_jit_make = match taker_direction { PositionDirection::Long => { @@ -1499,25 +1368,6 @@ impl AMM { Ok(amm_wants_to_jit_make && self.amm_jit_is_active()) } - pub fn amm_lp_wants_to_jit_make( - &self, - taker_direction: PositionDirection, - ) -> DriftResult { - if self.user_lp_shares == 0 { - return Ok(false); - } - - let amm_lp_wants_to_jit_make = match taker_direction { - PositionDirection::Long => { - self.base_asset_amount_per_lp > self.get_target_base_asset_amount_per_lp()? - } - PositionDirection::Short => { - self.base_asset_amount_per_lp < self.get_target_base_asset_amount_per_lp()? - } - }; - Ok(amm_lp_wants_to_jit_make && self.amm_lp_jit_is_active()) - } - pub fn amm_lp_allowed_to_jit_make(&self, amm_wants_to_jit_make: bool) -> DriftResult { // only allow lps to make when the amm inventory is below a certain level of available liquidity // i.e. 10% @@ -1529,12 +1379,7 @@ impl AMM { self.max_base_asset_reserve, )?; - let min_side_liquidity = max_bids.min(max_asks.abs()); - let protocol_owned_min_side_liquidity = get_proportion_i128( - min_side_liquidity, - self.sqrt_k.safe_sub(self.user_lp_shares)?, - self.sqrt_k, - )?; + let protocol_owned_min_side_liquidity = max_bids.min(max_asks.abs()); Ok(self.base_asset_amount_with_amm.abs() < protocol_owned_min_side_liquidity.safe_div(10)?) @@ -1547,10 +1392,6 @@ impl AMM { self.amm_jit_intensity > 0 } - pub fn amm_lp_jit_is_active(&self) -> bool { - self.amm_jit_intensity > 100 - } - pub fn reserve_price(&self) -> DriftResult { amm::calculate_price( self.quote_asset_reserve, @@ -1616,7 +1457,7 @@ impl AMM { .base_asset_amount_with_amm .unsigned_abs() .max(min_order_size_u128) - < self.sqrt_k.safe_sub(self.user_lp_shares)?) + < self.sqrt_k) && (min_order_size_u128 < max_bids.unsigned_abs().max(max_asks.unsigned_abs())); Ok(can_lower) diff --git a/programs/drift/src/state/user.rs b/programs/drift/src/state/user.rs index 4bcb6fb2f4..87dd21bab4 100644 --- a/programs/drift/src/state/user.rs +++ b/programs/drift/src/state/user.rs @@ -1,4 +1,3 @@ -use crate::controller::lp::apply_lp_rebase_to_perp_position; use crate::controller::position::{add_new_position, get_position_index, PositionDirection}; use crate::error::{DriftResult, ErrorCode}; use crate::math::auction::{calculate_auction_price, is_auction_complete}; @@ -7,14 +6,12 @@ use crate::math::constants::{ EPOCH_DURATION, FUEL_OVERFLOW_THRESHOLD_U32, FUEL_START_TS, OPEN_ORDER_MARGIN_REQUIREMENT, PRICE_TIMES_AMM_TO_QUOTE_PRECISION_RATIO, QUOTE_PRECISION, QUOTE_SPOT_MARKET_INDEX, THIRTY_DAY, }; -use crate::math::lp::{calculate_lp_open_bids_asks, calculate_settle_lp_metrics}; use crate::math::margin::MarginRequirementType; use crate::math::orders::{ apply_protected_maker_limit_price_offset, standardize_base_asset_amount, standardize_price, }; use crate::math::position::{ - calculate_base_asset_value_and_pnl_with_oracle_price, - calculate_base_asset_value_with_oracle_price, calculate_perp_liability_value, + calculate_base_asset_value_and_pnl_with_oracle_price, calculate_perp_liability_value, }; use crate::math::safe_math::SafeMath; use crate::math::spot_balance::{ @@ -23,10 +20,10 @@ use crate::math::spot_balance::{ use crate::math::stats::calculate_rolling_sum; use crate::msg; use crate::state::oracle::StrictOraclePrice; -use crate::state::perp_market::{ContractType, PerpMarket}; +use crate::state::perp_market::ContractType; use crate::state::spot_market::{SpotBalance, SpotBalanceType, SpotMarket}; use crate::state::traits::Size; -use crate::{get_then_update_id, ID, PERCENTAGE_PRECISION_I64, QUOTE_PRECISION_U64}; +use crate::{get_then_update_id, ID, QUOTE_PRECISION_U64}; use crate::{math_error, SPOT_WEIGHT_PRECISION_I128}; use crate::{safe_increment, SPOT_WEIGHT_PRECISION}; use crate::{validate, MAX_PREDICTION_MARKET_PRICE}; @@ -978,10 +975,7 @@ impl PerpPosition { } pub fn is_available(&self) -> bool { - !self.is_open_position() - && !self.has_open_order() - && !self.has_unsettled_pnl() - && !self.is_lp() + !self.is_open_position() && !self.has_open_order() && !self.has_unsettled_pnl() } pub fn is_open_position(&self) -> bool { @@ -992,103 +986,12 @@ impl PerpPosition { self.open_orders != 0 || self.open_bids != 0 || self.open_asks != 0 } - pub fn margin_requirement_for_lp_shares( - &self, - order_step_size: u64, - valuation_price: i64, - ) -> DriftResult { - if !self.is_lp() { - return Ok(0); - } - Ok(QUOTE_PRECISION.max( - order_step_size - .cast::()? - .safe_mul(valuation_price.cast()?)? - .safe_div(PRICE_TIMES_AMM_TO_QUOTE_PRECISION_RATIO)?, - )) - } - pub fn margin_requirement_for_open_orders(&self) -> DriftResult { self.open_orders .cast::()? .safe_mul(OPEN_ORDER_MARGIN_REQUIREMENT) } - pub fn is_lp(&self) -> bool { - self.lp_shares > 0 - } - - pub fn simulate_settled_lp_position( - &self, - market: &PerpMarket, - valuation_price: i64, - ) -> DriftResult { - let mut settled_position = *self; - - if !settled_position.is_lp() { - return Ok(settled_position); - } - - apply_lp_rebase_to_perp_position(market, &mut settled_position)?; - - // compute lp metrics - let mut lp_metrics = calculate_settle_lp_metrics(&market.amm, &settled_position)?; - - // compute settled position - let base_asset_amount = settled_position - .base_asset_amount - .safe_add(lp_metrics.base_asset_amount.cast()?)?; - - let mut quote_asset_amount = settled_position - .quote_asset_amount - .safe_add(lp_metrics.quote_asset_amount.cast()?)?; - - let mut new_remainder_base_asset_amount = settled_position - .remainder_base_asset_amount - .cast::()? - .safe_add(lp_metrics.remainder_base_asset_amount.cast()?)?; - - if new_remainder_base_asset_amount.unsigned_abs() >= market.amm.order_step_size { - let (standardized_remainder_base_asset_amount, remainder_base_asset_amount) = - crate::math::orders::standardize_base_asset_amount_with_remainder_i128( - new_remainder_base_asset_amount.cast()?, - market.amm.order_step_size.cast()?, - )?; - - lp_metrics.base_asset_amount = lp_metrics - .base_asset_amount - .safe_add(standardized_remainder_base_asset_amount)?; - - new_remainder_base_asset_amount = remainder_base_asset_amount.cast()?; - } else { - new_remainder_base_asset_amount = new_remainder_base_asset_amount.cast()?; - } - - // dust position in baa/qaa - if new_remainder_base_asset_amount != 0 { - let dust_base_asset_value = calculate_base_asset_value_with_oracle_price( - new_remainder_base_asset_amount.cast()?, - valuation_price, - )? - .safe_add(1)?; - - quote_asset_amount = quote_asset_amount.safe_sub(dust_base_asset_value.cast()?)?; - } - - let (lp_bids, lp_asks) = calculate_lp_open_bids_asks(&settled_position, market)?; - - let open_bids = settled_position.open_bids.safe_add(lp_bids)?; - - let open_asks = settled_position.open_asks.safe_add(lp_asks)?; - - settled_position.base_asset_amount = base_asset_amount; - settled_position.quote_asset_amount = quote_asset_amount; - settled_position.open_bids = open_bids; - settled_position.open_asks = open_asks; - - Ok(settled_position) - } - pub fn has_unsettled_pnl(&self) -> bool { self.base_asset_amount == 0 && self.quote_asset_amount != 0 } @@ -1164,18 +1067,12 @@ impl PerpPosition { Ok(unrealized_pnl) } - pub fn get_base_asset_amount_with_remainder(&self) -> DriftResult { - if self.remainder_base_asset_amount != 0 { - self.base_asset_amount - .cast::()? - .safe_add(self.remainder_base_asset_amount.cast::()?) - } else { - self.base_asset_amount.cast::() - } + pub fn get_base_asset_amount(&self) -> DriftResult { + self.base_asset_amount.cast::() } - pub fn get_base_asset_amount_with_remainder_abs(&self) -> DriftResult { - Ok(self.get_base_asset_amount_with_remainder()?.abs()) + pub fn get_base_asset_amount_abs(&self) -> DriftResult { + Ok(self.get_base_asset_amount()?.abs()) } pub fn get_claimable_pnl(&self, oracle_price: i64, pnl_pool_excess: i128) -> DriftResult { @@ -1233,7 +1130,7 @@ use super::protected_maker_mode_config::ProtectedMakerParams; #[cfg(test)] impl PerpPosition { pub fn get_breakeven_price(&self) -> DriftResult { - let base_with_remainder = self.get_base_asset_amount_with_remainder()?; + let base_with_remainder = self.get_base_asset_amount()?; if base_with_remainder == 0 { return Ok(0); } @@ -1245,7 +1142,7 @@ impl PerpPosition { } pub fn get_entry_price(&self) -> DriftResult { - let base_with_remainder = self.get_base_asset_amount_with_remainder()?; + let base_with_remainder = self.get_base_asset_amount()?; if base_with_remainder == 0 { return Ok(0); } diff --git a/programs/drift/src/validation/perp_market.rs b/programs/drift/src/validation/perp_market.rs index e15008805a..e55257659f 100644 --- a/programs/drift/src/validation/perp_market.rs +++ b/programs/drift/src/validation/perp_market.rs @@ -32,19 +32,16 @@ pub fn validate_perp_market(market: &PerpMarket) -> DriftResult { )?; validate!( (market.amm.base_asset_amount_long + market.amm.base_asset_amount_short) - == market.amm.base_asset_amount_with_amm - + market.amm.base_asset_amount_with_unsettled_lp, + == market.amm.base_asset_amount_with_amm, ErrorCode::InvalidAmmDetected, "Market NET_BAA Error: market.amm.base_asset_amount_long={}, + market.amm.base_asset_amount_short={} != - market.amm.base_asset_amount_with_amm={} - + market.amm.base_asset_amount_with_unsettled_lp={}", + market.amm.base_asset_amount_with_amm={}", market.amm.base_asset_amount_long, market.amm.base_asset_amount_short, market.amm.base_asset_amount_with_amm, - market.amm.base_asset_amount_with_unsettled_lp, )?; validate!( @@ -84,15 +81,6 @@ pub fn validate_perp_market(market: &PerpMarket) -> DriftResult { market.amm.quote_asset_reserve )?; - validate!( - market.amm.sqrt_k >= market.amm.user_lp_shares, - ErrorCode::InvalidAmmDetected, - "market {} market.amm.sqrt_k < market.amm.user_lp_shares: {} < {}", - market.market_index, - market.amm.sqrt_k, - market.amm.user_lp_shares, - )?; - let invariant_sqrt_u192 = crate::bn::U192::from(market.amm.sqrt_k); let invariant = invariant_sqrt_u192.safe_mul(invariant_sqrt_u192)?; let quote_asset_reserve = invariant @@ -229,22 +217,6 @@ pub fn validate_perp_market(market: &PerpMarket) -> DriftResult { market.insurance_claim.revenue_withdraw_since_last_settle.unsigned_abs() )?; - validate!( - market.amm.base_asset_amount_per_lp < MAX_BASE_ASSET_AMOUNT_WITH_AMM as i128, - ErrorCode::InvalidAmmDetected, - "{} market.amm.base_asset_amount_per_lp too large: {}", - market.market_index, - market.amm.base_asset_amount_per_lp - )?; - - validate!( - market.amm.quote_asset_amount_per_lp < MAX_BASE_ASSET_AMOUNT_WITH_AMM as i128, - ErrorCode::InvalidAmmDetected, - "{} market.amm.quote_asset_amount_per_lp too large: {}", - market.market_index, - market.amm.quote_asset_amount_per_lp - )?; - Ok(()) } diff --git a/programs/drift/src/validation/position.rs b/programs/drift/src/validation/position.rs index e8f527de63..3d36698583 100644 --- a/programs/drift/src/validation/position.rs +++ b/programs/drift/src/validation/position.rs @@ -11,14 +11,6 @@ pub fn validate_perp_position_with_perp_market( position: &PerpPosition, market: &PerpMarket, ) -> DriftResult { - if position.lp_shares != 0 { - validate!( - position.per_lp_base == market.amm.per_lp_base, - ErrorCode::InvalidPerpPositionDetected, - "position/market per_lp_base unequal" - )?; - } - validate!( position.market_index == market.market_index, ErrorCode::InvalidPerpPositionDetected, diff --git a/sdk/src/math/bankruptcy.ts b/sdk/src/math/bankruptcy.ts index 22cca5813b..a83789ba6c 100644 --- a/sdk/src/math/bankruptcy.ts +++ b/sdk/src/math/bankruptcy.ts @@ -21,8 +21,7 @@ export function isUserBankrupt(user: User): boolean { if ( !position.baseAssetAmount.eq(ZERO) || position.quoteAssetAmount.gt(ZERO) || - hasOpenOrders(position) || - position.lpShares.gt(ZERO) + hasOpenOrders(position) ) { return false; } diff --git a/sdk/src/user.ts b/sdk/src/user.ts index 4f1c33cd16..a312fe83a1 100644 --- a/sdk/src/user.ts +++ b/sdk/src/user.ts @@ -215,6 +215,14 @@ export class User { return this.getPerpPositionForUserAccount(userAccount, marketIndex); } + public getPerpPositionOrEmpty(marketIndex: number): PerpPosition { + const userAccount = this.getUserAccount(); + return ( + this.getPerpPositionForUserAccount(userAccount, marketIndex) ?? + this.getEmptyPosition(marketIndex) + ); + } + public getPerpPositionAndSlot( marketIndex: number ): DataAndSlot { @@ -418,243 +426,12 @@ export class User { public getPerpBidAsks(marketIndex: number): [BN, BN] { const position = this.getPerpPosition(marketIndex); - const [lpOpenBids, lpOpenAsks] = this.getLPBidAsks(marketIndex); - - const totalOpenBids = lpOpenBids.add(position.openBids); - const totalOpenAsks = lpOpenAsks.add(position.openAsks); + const totalOpenBids = position.openBids; + const totalOpenAsks = position.openAsks; return [totalOpenBids, totalOpenAsks]; } - /** - * calculates the open bids and asks for an lp - * optionally pass in lpShares to see what bid/asks a user *would* take on - * @returns : lp open bids - * @returns : lp open asks - */ - public getLPBidAsks(marketIndex: number, lpShares?: BN): [BN, BN] { - const position = this.getPerpPosition(marketIndex); - - const lpSharesToCalc = lpShares ?? position?.lpShares; - - if (!lpSharesToCalc || lpSharesToCalc.eq(ZERO)) { - return [ZERO, ZERO]; - } - - const market = this.driftClient.getPerpMarketAccount(marketIndex); - const [marketOpenBids, marketOpenAsks] = calculateMarketOpenBidAsk( - market.amm.baseAssetReserve, - market.amm.minBaseAssetReserve, - market.amm.maxBaseAssetReserve, - market.amm.orderStepSize - ); - - const lpOpenBids = marketOpenBids.mul(lpSharesToCalc).div(market.amm.sqrtK); - const lpOpenAsks = marketOpenAsks.mul(lpSharesToCalc).div(market.amm.sqrtK); - - return [lpOpenBids, lpOpenAsks]; - } - - /** - * calculates the market position if the lp position was settled - * @returns : the settled userPosition - * @returns : the dust base asset amount (ie, < stepsize) - * @returns : pnl from settle - */ - public getPerpPositionWithLPSettle( - marketIndex: number, - originalPosition?: PerpPosition, - burnLpShares = false, - includeRemainderInBaseAmount = false - ): [PerpPosition, BN, BN] { - originalPosition = - originalPosition ?? - this.getPerpPosition(marketIndex) ?? - this.getEmptyPosition(marketIndex); - - if (originalPosition.lpShares.eq(ZERO)) { - return [originalPosition, ZERO, ZERO]; - } - - const position = this.getClonedPosition(originalPosition); - const market = this.driftClient.getPerpMarketAccount(position.marketIndex); - - if (market.amm.perLpBase != position.perLpBase) { - // perLpBase = 1 => per 10 LP shares, perLpBase = -1 => per 0.1 LP shares - const expoDiff = market.amm.perLpBase - position.perLpBase; - const marketPerLpRebaseScalar = new BN(10 ** Math.abs(expoDiff)); - - if (expoDiff > 0) { - position.lastBaseAssetAmountPerLp = - position.lastBaseAssetAmountPerLp.mul(marketPerLpRebaseScalar); - position.lastQuoteAssetAmountPerLp = - position.lastQuoteAssetAmountPerLp.mul(marketPerLpRebaseScalar); - } else { - position.lastBaseAssetAmountPerLp = - position.lastBaseAssetAmountPerLp.div(marketPerLpRebaseScalar); - position.lastQuoteAssetAmountPerLp = - position.lastQuoteAssetAmountPerLp.div(marketPerLpRebaseScalar); - } - - position.perLpBase = position.perLpBase + expoDiff; - } - - const nShares = position.lpShares; - - // incorp unsettled funding on pre settled position - const quoteFundingPnl = calculateUnsettledFundingPnl(market, position); - - let baseUnit = AMM_RESERVE_PRECISION; - if (market.amm.perLpBase == position.perLpBase) { - if ( - position.perLpBase >= 0 && - position.perLpBase <= AMM_RESERVE_PRECISION_EXP.toNumber() - ) { - const marketPerLpRebase = new BN(10 ** market.amm.perLpBase); - baseUnit = baseUnit.mul(marketPerLpRebase); - } else if ( - position.perLpBase < 0 && - position.perLpBase >= -AMM_RESERVE_PRECISION_EXP.toNumber() - ) { - const marketPerLpRebase = new BN(10 ** Math.abs(market.amm.perLpBase)); - baseUnit = baseUnit.div(marketPerLpRebase); - } else { - throw 'cannot calc'; - } - } else { - throw 'market.amm.perLpBase != position.perLpBase'; - } - - const deltaBaa = market.amm.baseAssetAmountPerLp - .sub(position.lastBaseAssetAmountPerLp) - .mul(nShares) - .div(baseUnit); - const deltaQaa = market.amm.quoteAssetAmountPerLp - .sub(position.lastQuoteAssetAmountPerLp) - .mul(nShares) - .div(baseUnit); - - function sign(v: BN) { - return v.isNeg() ? new BN(-1) : new BN(1); - } - - function standardize(amount: BN, stepSize: BN) { - const remainder = amount.abs().mod(stepSize).mul(sign(amount)); - const standardizedAmount = amount.sub(remainder); - return [standardizedAmount, remainder]; - } - - const [standardizedBaa, remainderBaa] = standardize( - deltaBaa, - market.amm.orderStepSize - ); - - position.remainderBaseAssetAmount += remainderBaa.toNumber(); - - if ( - Math.abs(position.remainderBaseAssetAmount) > - market.amm.orderStepSize.toNumber() - ) { - const [newStandardizedBaa, newRemainderBaa] = standardize( - new BN(position.remainderBaseAssetAmount), - market.amm.orderStepSize - ); - position.baseAssetAmount = - position.baseAssetAmount.add(newStandardizedBaa); - position.remainderBaseAssetAmount = newRemainderBaa.toNumber(); - } - - let dustBaseAssetValue = ZERO; - if (burnLpShares && position.remainderBaseAssetAmount != 0) { - const oraclePriceData = this.driftClient.getOracleDataForPerpMarket( - position.marketIndex - ); - dustBaseAssetValue = new BN(Math.abs(position.remainderBaseAssetAmount)) - .mul(oraclePriceData.price) - .div(AMM_RESERVE_PRECISION) - .add(ONE); - } - - let updateType; - if (position.baseAssetAmount.eq(ZERO)) { - updateType = 'open'; - } else if (sign(position.baseAssetAmount).eq(sign(deltaBaa))) { - updateType = 'increase'; - } else if (position.baseAssetAmount.abs().gt(deltaBaa.abs())) { - updateType = 'reduce'; - } else if (position.baseAssetAmount.abs().eq(deltaBaa.abs())) { - updateType = 'close'; - } else { - updateType = 'flip'; - } - - let newQuoteEntry; - let pnl; - if (updateType == 'open' || updateType == 'increase') { - newQuoteEntry = position.quoteEntryAmount.add(deltaQaa); - pnl = ZERO; - } else if (updateType == 'reduce' || updateType == 'close') { - newQuoteEntry = position.quoteEntryAmount.sub( - position.quoteEntryAmount - .mul(deltaBaa.abs()) - .div(position.baseAssetAmount.abs()) - ); - pnl = position.quoteEntryAmount.sub(newQuoteEntry).add(deltaQaa); - } else { - newQuoteEntry = deltaQaa.sub( - deltaQaa.mul(position.baseAssetAmount.abs()).div(deltaBaa.abs()) - ); - pnl = position.quoteEntryAmount.add(deltaQaa.sub(newQuoteEntry)); - } - position.quoteEntryAmount = newQuoteEntry; - position.baseAssetAmount = position.baseAssetAmount.add(standardizedBaa); - position.quoteAssetAmount = position.quoteAssetAmount - .add(deltaQaa) - .add(quoteFundingPnl) - .sub(dustBaseAssetValue); - position.quoteBreakEvenAmount = position.quoteBreakEvenAmount - .add(deltaQaa) - .add(quoteFundingPnl) - .sub(dustBaseAssetValue); - - // update open bids/asks - const [marketOpenBids, marketOpenAsks] = calculateMarketOpenBidAsk( - market.amm.baseAssetReserve, - market.amm.minBaseAssetReserve, - market.amm.maxBaseAssetReserve, - market.amm.orderStepSize - ); - const lpOpenBids = marketOpenBids - .mul(position.lpShares) - .div(market.amm.sqrtK); - const lpOpenAsks = marketOpenAsks - .mul(position.lpShares) - .div(market.amm.sqrtK); - position.openBids = lpOpenBids.add(position.openBids); - position.openAsks = lpOpenAsks.add(position.openAsks); - - // eliminate counting funding on settled position - if (position.baseAssetAmount.gt(ZERO)) { - position.lastCumulativeFundingRate = market.amm.cumulativeFundingRateLong; - } else if (position.baseAssetAmount.lt(ZERO)) { - position.lastCumulativeFundingRate = - market.amm.cumulativeFundingRateShort; - } else { - position.lastCumulativeFundingRate = ZERO; - } - - const remainderBeforeRemoval = new BN(position.remainderBaseAssetAmount); - - if (includeRemainderInBaseAmount) { - position.baseAssetAmount = position.baseAssetAmount.add( - remainderBeforeRemoval - ); - position.remainderBaseAssetAmount = 0; - } - - return [position, remainderBeforeRemoval, pnl]; - } - /** * calculates Buying Power = free collateral / initial margin ratio * @returns : Precision QUOTE_PRECISION @@ -664,11 +441,7 @@ export class User { collateralBuffer = ZERO, enterHighLeverageMode = undefined ): BN { - const perpPosition = this.getPerpPositionWithLPSettle( - marketIndex, - undefined, - true - )[0]; + const perpPosition = this.getPerpPositionOrEmpty(marketIndex); const perpMarket = this.driftClient.getPerpMarketAccount(marketIndex); const oraclePriceData = this.getOracleDataForPerpMarket(marketIndex); @@ -781,8 +554,7 @@ export class User { (pos) => !pos.baseAssetAmount.eq(ZERO) || !pos.quoteAssetAmount.eq(ZERO) || - !(pos.openOrders == 0) || - !pos.lpShares.eq(ZERO) + !(pos.openOrders == 0) ); } @@ -854,14 +626,6 @@ export class User { market.quoteSpotMarketIndex ); - if (perpPosition.lpShares.gt(ZERO)) { - perpPosition = this.getPerpPositionWithLPSettle( - perpPosition.marketIndex, - undefined, - !!withWeightMarginCategory - )[0]; - } - let positionUnrealizedPnl = calculatePositionPNL( market, perpPosition, @@ -1438,15 +1202,6 @@ export class User { perpPosition.marketIndex ); - if (perpPosition.lpShares.gt(ZERO)) { - // is an lp, clone so we dont mutate the position - perpPosition = this.getPerpPositionWithLPSettle( - market.marketIndex, - this.getClonedPosition(perpPosition), - !!marginCategory - )[0]; - } - let valuationPrice = this.getOracleDataForPerpMarket( market.marketIndex ).price; @@ -1525,19 +1280,6 @@ export class User { liabilityValue = liabilityValue.add( new BN(perpPosition.openOrders).mul(OPEN_ORDER_MARGIN_REQUIREMENT) ); - - if (perpPosition.lpShares.gt(ZERO)) { - liabilityValue = liabilityValue.add( - BN.max( - QUOTE_PRECISION, - valuationPrice - .mul(market.amm.orderStepSize) - .mul(QUOTE_PRECISION) - .div(AMM_RESERVE_PRECISION) - .div(PRICE_PRECISION) - ) - ); - } } } @@ -1601,13 +1343,7 @@ export class User { oraclePriceData: Pick, includeOpenOrders = false ): BN { - const userPosition = - this.getPerpPositionWithLPSettle( - marketIndex, - undefined, - false, - true - )[0] || this.getEmptyPosition(marketIndex); + const userPosition = this.getPerpPositionOrEmpty(marketIndex); const market = this.driftClient.getPerpMarketAccount( userPosition.marketIndex ); @@ -1628,13 +1364,7 @@ export class User { oraclePriceData: OraclePriceData, includeOpenOrders = false ): BN { - const userPosition = - this.getPerpPositionWithLPSettle( - marketIndex, - undefined, - false, - true - )[0] || this.getEmptyPosition(marketIndex); + const userPosition = this.getPerpPositionOrEmpty(marketIndex); const market = this.driftClient.getPerpMarketAccount( userPosition.marketIndex ); @@ -2185,11 +1915,9 @@ export class User { const oraclePrice = this.driftClient.getOracleDataForSpotMarket(marketIndex).price; if (perpMarketWithSameOracle) { - const perpPosition = this.getPerpPositionWithLPSettle( - perpMarketWithSameOracle.marketIndex, - undefined, - true - )[0]; + const perpPosition = this.getPerpPositionOrEmpty( + perpMarketWithSameOracle.marketIndex + ); if (perpPosition) { let freeCollateralDeltaForPerp = this.calculateFreeCollateralDeltaForPerp( @@ -2275,9 +2003,7 @@ export class User { this.driftClient.getOracleDataForPerpMarket(marketIndex).price; const market = this.driftClient.getPerpMarketAccount(marketIndex); - const currentPerpPosition = - this.getPerpPositionWithLPSettle(marketIndex, undefined, true)[0] || - this.getEmptyPosition(marketIndex); + const currentPerpPosition = this.getPerpPositionOrEmpty(marketIndex); positionBaseSizeChange = standardizeBaseAssetAmount( positionBaseSizeChange, @@ -2575,12 +2301,7 @@ export class User { closeQuoteAmount: BN, estimatedEntryPrice: BN = ZERO ): BN { - const currentPosition = - this.getPerpPositionWithLPSettle( - positionMarketIndex, - undefined, - true - )[0] || this.getEmptyPosition(positionMarketIndex); + const currentPosition = this.getPerpPositionOrEmpty(positionMarketIndex); const closeBaseAmount = currentPosition.baseAssetAmount .mul(closeQuoteAmount) @@ -2646,9 +2367,7 @@ export class User { ): { tradeSize: BN; oppositeSideTradeSize: BN } { let tradeSize = ZERO; let oppositeSideTradeSize = ZERO; - const currentPosition = - this.getPerpPositionWithLPSettle(targetMarketIndex, undefined, true)[0] || - this.getEmptyPosition(targetMarketIndex); + const currentPosition = this.getPerpPositionOrEmpty(targetMarketIndex); const targetSide = isVariant(tradeSide, 'short') ? 'short' : 'long'; @@ -3421,9 +3140,7 @@ export class User { return newLeverage; } - const currentPosition = - this.getPerpPositionWithLPSettle(targetMarketIndex)[0] || - this.getEmptyPosition(targetMarketIndex); + const currentPosition = this.getPerpPositionOrEmpty(targetMarketIndex); const perpMarket = this.driftClient.getPerpMarketAccount(targetMarketIndex); const oracleData = this.getOracleDataForPerpMarket(targetMarketIndex); @@ -3836,10 +3553,6 @@ export class User { oraclePriceData?: OraclePriceData; quoteOraclePriceData?: OraclePriceData; }): HealthComponent { - const settledLpPosition = this.getPerpPositionWithLPSettle( - perpPosition.marketIndex, - perpPosition - )[0]; const perpMarket = this.driftClient.getPerpMarketAccount( perpPosition.marketIndex ); @@ -3851,7 +3564,7 @@ export class User { worstCaseBaseAssetAmount: worstCaseBaseAmount, worstCaseLiabilityValue, } = calculateWorstCasePerpLiabilityValue( - settledLpPosition, + perpPosition, perpMarket, oraclePrice ); @@ -3880,19 +3593,6 @@ export class User { new BN(perpPosition.openOrders).mul(OPEN_ORDER_MARGIN_REQUIREMENT) ); - if (perpPosition.lpShares.gt(ZERO)) { - marginRequirement = marginRequirement.add( - BN.max( - QUOTE_PRECISION, - oraclePrice - .mul(perpMarket.amm.orderStepSize) - .mul(QUOTE_PRECISION) - .div(AMM_RESERVE_PRECISION) - .div(PRICE_PRECISION) - ) - ); - } - return { marketIndex: perpMarket.marketIndex, size: worstCaseBaseAmount, @@ -3940,14 +3640,9 @@ export class User { perpMarket.quoteSpotMarketIndex ); - const settledPerpPosition = this.getPerpPositionWithLPSettle( - perpPosition.marketIndex, - perpPosition - )[0]; - const positionUnrealizedPnl = calculatePositionPNL( perpMarket, - settledPerpPosition, + perpPosition, true, oraclePriceData ); @@ -4099,12 +3794,7 @@ export class User { liquidationBuffer?: BN, includeOpenOrders?: boolean ): BN { - const currentPerpPosition = - this.getPerpPositionWithLPSettle( - marketToIgnore, - undefined, - !!marginCategory - )[0] || this.getEmptyPosition(marketToIgnore); + const currentPerpPosition = this.getPerpPositionOrEmpty(marketToIgnore); const oracleData = this.getOracleDataForPerpMarket(marketToIgnore); diff --git a/test-scripts/run-anchor-tests.sh b/test-scripts/run-anchor-tests.sh index 5b03f289eb..e712bba8dd 100644 --- a/test-scripts/run-anchor-tests.sh +++ b/test-scripts/run-anchor-tests.sh @@ -44,7 +44,6 @@ test_files=( liquidatePerpPnlForDeposit.ts liquidateSpot.ts liquidateSpotSocialLoss.ts - liquidityProvider.ts marketOrder.ts marketOrderBaseAssetAmount.ts maxDeposit.ts @@ -60,8 +59,6 @@ test_files=( ordersWithSpread.ts pauseExchange.ts pauseDepositWithdraw.ts - perpLpJit.ts - perpLpRiskMitigation.ts phoenixTest.ts placeAndMakePerp.ts placeAndMakeSignedMsgBankrun.ts @@ -86,7 +83,6 @@ test_files=( surgePricing.ts switchboardTxCus.ts switchOracle.ts - tradingLP.ts triggerOrders.ts triggerSpotOrder.ts transferPerpPosition.ts diff --git a/tests/tradingLP.ts b/tests/tradingLP.ts deleted file mode 100644 index 565178183e..0000000000 --- a/tests/tradingLP.ts +++ /dev/null @@ -1,281 +0,0 @@ -import * as anchor from '@coral-xyz/anchor'; -import { assert } from 'chai'; -import { BN, User, OracleSource, Wallet, MARGIN_PRECISION } from '../sdk'; - -import { Program } from '@coral-xyz/anchor'; - -import * as web3 from '@solana/web3.js'; - -import { - TestClient, - PRICE_PRECISION, - PositionDirection, - ZERO, - OracleGuardRails, -} from '../sdk/src'; - -import { - initializeQuoteSpotMarket, - mockOracleNoProgram, - mockUSDCMint, - mockUserUSDCAccount, -} from './testHelpers'; -import { startAnchor } from 'solana-bankrun'; -import { TestBulkAccountLoader } from '../sdk/src/accounts/testBulkAccountLoader'; -import { BankrunContextWrapper } from '../sdk/src/bankrun/bankrunConnection'; - -async function createNewUser( - program, - context: BankrunContextWrapper, - usdcMint, - usdcAmount, - oracleInfos, - wallet, - bulkAccountLoader -) { - let walletFlag = true; - if (wallet == undefined) { - const kp = new web3.Keypair(); - await context.fundKeypair(kp, 10 ** 9); - wallet = new Wallet(kp); - walletFlag = false; - } - - console.log('wallet:', walletFlag); - const usdcAta = await mockUserUSDCAccount( - usdcMint, - usdcAmount, - context, - wallet.publicKey - ); - - const driftClient = new TestClient({ - connection: context.connection.toConnection(), - wallet: wallet, - programID: program.programId, - opts: { - commitment: 'confirmed', - }, - activeSubAccountId: 0, - perpMarketIndexes: [0, 1], - spotMarketIndexes: [0], - subAccountIds: [], - oracleInfos, - accountSubscription: { - type: 'polling', - accountLoader: bulkAccountLoader, - }, - }); - - if (walletFlag) { - await driftClient.initialize(usdcMint.publicKey, true); - await driftClient.subscribe(); - await initializeQuoteSpotMarket(driftClient, usdcMint.publicKey); - } else { - await driftClient.subscribe(); - } - - await driftClient.initializeUserAccountAndDepositCollateral( - usdcAmount, - usdcAta.publicKey - ); - - const driftClientUser = new User({ - // @ts-ignore - driftClient, - userAccountPublicKey: await driftClient.getUserAccountPublicKey(), - accountSubscription: { - type: 'polling', - accountLoader: bulkAccountLoader, - }, - }); - driftClientUser.subscribe(); - - return [driftClient, driftClientUser]; -} - -describe('trading liquidity providing', () => { - const chProgram = anchor.workspace.Drift as Program; - - // ammInvariant == k == x * y - const ammInitialBaseAssetReserve = new BN(300).mul(new BN(1e13)); - const ammInitialQuoteAssetReserve = new BN(300).mul(new BN(1e13)); - - const mantissaSqrtScale = new BN(Math.sqrt(PRICE_PRECISION.toNumber())); - const stableAmmInitialQuoteAssetReserve = new anchor.BN(1 * 10 ** 13).mul( - mantissaSqrtScale - ); - const stableAmmInitialBaseAssetReserve = new anchor.BN(1 * 10 ** 13).mul( - mantissaSqrtScale - ); - - const usdcAmount = new BN(1_000_000_000 * 1e6); - - let driftClient: TestClient; - - let bulkAccountLoader: TestBulkAccountLoader; - - let bankrunContextWrapper: BankrunContextWrapper; - - let usdcMint: web3.Keypair; - - let driftClientUser: User; - let traderDriftClient: TestClient; - let traderDriftClientUser: User; - - let solusdc; - let solusdc2; - - before(async () => { - const context = await startAnchor('', [], []); - - bankrunContextWrapper = new BankrunContextWrapper(context); - - bulkAccountLoader = new TestBulkAccountLoader( - bankrunContextWrapper.connection, - 'processed', - 1 - ); - - usdcMint = await mockUSDCMint(bankrunContextWrapper); - - solusdc2 = await mockOracleNoProgram(bankrunContextWrapper, 1, -7); // make invalid - solusdc = await mockOracleNoProgram(bankrunContextWrapper, 1, -7); // make invalid - const oracleInfos = [ - { publicKey: solusdc, source: OracleSource.PYTH }, - { publicKey: solusdc2, source: OracleSource.PYTH }, - ]; - [driftClient, driftClientUser] = await createNewUser( - chProgram, - bankrunContextWrapper, - usdcMint, - usdcAmount, - oracleInfos, - bankrunContextWrapper.provider.wallet, - bulkAccountLoader - ); - // used for trading / taking on baa - await driftClient.initializePerpMarket( - 0, - solusdc, - ammInitialBaseAssetReserve, - ammInitialQuoteAssetReserve, - new BN(60 * 60) - ); - await driftClient.updateLpCooldownTime(new BN(0)); - await driftClient.updatePerpMarketMaxFillReserveFraction(0, 1); - await driftClient.updatePerpMarketStepSizeAndTickSize( - 0, - new BN(1), - new BN(1) - ); - const oracleGuardRails: OracleGuardRails = { - priceDivergence: { - markOraclePercentDivergence: new BN(1000000), - oracleTwap5MinPercentDivergence: new BN(1000000), - }, - validity: { - slotsBeforeStaleForAmm: new BN(10), - slotsBeforeStaleForMargin: new BN(10), - confidenceIntervalMaxSize: new BN(100), - tooVolatileRatio: new BN(100), - }, - }; - await driftClient.updateOracleGuardRails(oracleGuardRails); - - // second market -- used for funding .. - await driftClient.initializePerpMarket( - 1, - solusdc2, - stableAmmInitialBaseAssetReserve, - stableAmmInitialQuoteAssetReserve, - new BN(0) - ); - await driftClient.updatePerpAuctionDuration(new BN(0)); - await driftClient.updatePerpMarketMarginRatio( - 0, - MARGIN_PRECISION.toNumber() / 2, - MARGIN_PRECISION.toNumber() / 4 - ); - - [traderDriftClient, traderDriftClientUser] = await createNewUser( - chProgram, - bankrunContextWrapper, - usdcMint, - usdcAmount, - oracleInfos, - undefined, - bulkAccountLoader - ); - }); - - after(async () => { - await driftClient.unsubscribe(); - await driftClientUser.unsubscribe(); - - await traderDriftClient.unsubscribe(); - await traderDriftClientUser.unsubscribe(); - }); - - it('lp trades with short', async () => { - let market = driftClient.getPerpMarketAccount(0); - - console.log('adding liquidity...'); - const _sig = await driftClient.addPerpLpShares( - new BN(100 * 1e13), - market.marketIndex - ); - - // some user goes long (lp should get a short) - console.log('user trading...'); - const tradeSize = new BN(40 * 1e13); - const _txsig = await traderDriftClient.openPosition( - PositionDirection.LONG, - tradeSize, - market.marketIndex - ); - - await traderDriftClient.fetchAccounts(); - const position = traderDriftClient.getUserAccount().perpPositions[0]; - console.log( - 'trader position:', - position.baseAssetAmount.toString(), - position.quoteAssetAmount.toString() - ); - assert(position.baseAssetAmount.gt(ZERO)); - - // settle says the lp would take on a short - const lpPosition = driftClientUser.getPerpPositionWithLPSettle(0)[0]; - console.log( - 'sdk settled lp position:', - lpPosition.baseAssetAmount.toString(), - lpPosition.quoteAssetAmount.toString() - ); - assert(lpPosition.baseAssetAmount.lt(ZERO)); - assert(lpPosition.quoteAssetAmount.gt(ZERO)); - - // lp trades a big long - await driftClient.openPosition( - PositionDirection.LONG, - tradeSize, - market.marketIndex - ); - await driftClient.fetchAccounts(); - await driftClientUser.fetchAccounts(); - - // lp now has a long - const newLpPosition = driftClientUser.getUserAccount().perpPositions[0]; - console.log( - 'lp position:', - newLpPosition.baseAssetAmount.toString(), - newLpPosition.quoteAssetAmount.toString() - ); - assert(newLpPosition.baseAssetAmount.gt(ZERO)); - assert(newLpPosition.quoteAssetAmount.lt(ZERO)); - // is still an lp - assert(newLpPosition.lpShares.gt(ZERO)); - market = driftClient.getPerpMarketAccount(0); - - console.log('done!'); - }); -});