From ed081cc7a7a9e3260550e03e5c639ca91e1b95f9 Mon Sep 17 00:00:00 2001 From: Chris Heaney Date: Fri, 9 May 2025 09:25:21 -0400 Subject: [PATCH 01/13] program: add quote entry amount to order records --- programs/drift/src/controller/liquidation.rs | 2 + programs/drift/src/controller/orders.rs | 75 ++++++++++++++++++-- programs/drift/src/instructions/user.rs | 2 + programs/drift/src/state/events.rs | 8 +++ 4 files changed, 81 insertions(+), 6 deletions(-) diff --git a/programs/drift/src/controller/liquidation.rs b/programs/drift/src/controller/liquidation.rs index d22fa2b4f6..9766c908f2 100644 --- a/programs/drift/src/controller/liquidation.rs +++ b/programs/drift/src/controller/liquidation.rs @@ -666,6 +666,8 @@ pub fn liquidate_perp( maker_order_cumulative_quote_asset_amount_filled: Some(base_asset_value), oracle_price, bit_flags: 0, + taker_quote_entry_amount: None, + maker_quote_entry_amount: None, }; emit!(fill_record); diff --git a/programs/drift/src/controller/orders.rs b/programs/drift/src/controller/orders.rs index ca2b3ffd04..8c563b2478 100644 --- a/programs/drift/src/controller/orders.rs +++ b/programs/drift/src/controller/orders.rs @@ -424,6 +424,8 @@ pub fn place_perp_order( maker_order, oracle_map.get_price_data(&market.oracle_id())?.price, bit_flags, + None, + None, )?; emit_stack::<_, { OrderActionRecord::SIZE }>(order_action_record)?; @@ -699,6 +701,8 @@ pub fn cancel_order( maker_order, oracle_map.get_price_data(&oracle_id)?.price, 0, + None, + None, )?; emit_stack::<_, { OrderActionRecord::SIZE }>(order_action_record)?; } @@ -2165,6 +2169,12 @@ pub fn fulfill_perp_order_with_amm( validation::perp_market::validate_amm_account_for_fill(&market.amm, order_direction)?; + let quote_entry_amount = if user.perp_positions[position_index].get_direction_to_close() == order_direction { + Some(user.perp_positions[position_index].quote_entry_amount) + } else { + None + }; + let market_side_price = match order_direction { PositionDirection::Long => market.amm.ask_price(reserve_price_before)?, PositionDirection::Short => market.amm.bid_price(reserve_price_before)?, @@ -2355,6 +2365,16 @@ pub fn fulfill_perp_order_with_amm( order_action_bit_flags, user.orders[order_index].is_signed_msg(), ); + let (taker_quote_entry_amount, maker_quote_entry_amount) = if let Some(quote_entry_amount) = quote_entry_amount { + let quote_entry_amount = quote_entry_amount.unsigned_abs().safe_mul(BASE_PRECISION_U64)?.safe_div(base_asset_amount)?; + if taker.is_some() { + (Some(quote_entry_amount), None) + } else { + (None, Some(quote_entry_amount)) + } + } else { + (None, None) + }; let order_action_record = get_order_action_record( now, OrderAction::Fill, @@ -2380,6 +2400,8 @@ pub fn fulfill_perp_order_with_amm( maker_order, oracle_map.get_price_data(&market.oracle_id())?.price, order_action_bit_flags, + taker_quote_entry_amount, + maker_quote_entry_amount, )?; emit_stack::<_, { OrderActionRecord::SIZE }>(order_action_record)?; @@ -2480,9 +2502,18 @@ pub fn fulfill_perp_order_with_match( .get_base_asset_amount_unfilled(Some(taker_existing_position))?; let maker_direction = maker.orders[maker_order_index].direction; - let maker_existing_position = maker - .get_perp_position(market.market_index)? - .base_asset_amount; + let (maker_existing_position, maker_quote_entry_amount) = { + let maker_position = maker + .get_perp_position(market.market_index)?; + + let quoute_entry_amount = if maker_position.get_direction_to_close() == maker_direction { + Some(maker_position.quote_entry_amount) + } else { + None + }; + + (maker_position.base_asset_amount, quoute_entry_amount) + }; let maker_base_asset_amount = maker.orders[maker_order_index] .get_base_asset_amount_unfilled(Some(maker_existing_position))?; @@ -2564,9 +2595,19 @@ pub fn fulfill_perp_order_with_match( total_quote_asset_amount = quote_asset_amount_filled_by_amm } - let taker_existing_position = taker - .get_perp_position(market.market_index)? - .base_asset_amount; + + let (taker_existing_position, taker_quote_entry_amount) = { + let taker_position = taker + .get_perp_position(market.market_index)?; + + let quoute_entry_amount = if taker_position.get_direction_to_close() == taker_direction { + Some(taker_position.quote_entry_amount) + } else { + None + }; + + (taker_position.base_asset_amount, quoute_entry_amount) + }; let taker_base_asset_amount = taker.orders[taker_order_index] .get_base_asset_amount_unfilled(Some(taker_existing_position))?; @@ -2780,6 +2821,16 @@ pub fn fulfill_perp_order_with_match( order_action_bit_flags, taker.orders[taker_order_index].is_signed_msg(), ); + let taker_quote_entry_amount = if let Some(taker_quote_entry_amount) = taker_quote_entry_amount { + Some(taker_quote_entry_amount.unsigned_abs().safe_mul(BASE_PRECISION_U64)?.safe_div(base_asset_amount_fulfilled_by_maker)?) + } else { + None + }; + let maker_quote_entry_amount = if let Some(maker_quote_entry_amount) = maker_quote_entry_amount { + Some(maker_quote_entry_amount.unsigned_abs().safe_mul(BASE_PRECISION_U64)?.safe_div(base_asset_amount_fulfilled_by_maker)?) + } else { + None + }; let order_action_record = get_order_action_record( now, OrderAction::Fill, @@ -2801,6 +2852,8 @@ pub fn fulfill_perp_order_with_match( Some(maker.orders[maker_order_index]), oracle_map.get_price_data(&market.oracle_id())?.price, order_action_bit_flags, + taker_quote_entry_amount, + maker_quote_entry_amount, )?; emit_stack::<_, { OrderActionRecord::SIZE }>(order_action_record)?; @@ -3018,6 +3071,8 @@ pub fn trigger_order( None, oracle_price, 0, + None, + None, )?; emit!(order_action_record); @@ -3689,6 +3744,8 @@ pub fn place_spot_order( maker_order, oracle_price_data.price, 0, + None, + None, )?; emit_stack::<_, { OrderActionRecord::SIZE }>(order_action_record)?; @@ -4916,6 +4973,8 @@ pub fn fulfill_spot_order_with_match( Some(maker.orders[maker_order_index]), oracle_map.get_price_data(&base_market.oracle_id())?.price, 0, + None, + None, )?; emit_stack::<_, { OrderActionRecord::SIZE }>(order_action_record)?; @@ -5182,6 +5241,8 @@ pub fn fulfill_spot_order_with_external_market( None, oracle_price, 0, + None, + None, )?; emit_stack::<_, { OrderActionRecord::SIZE }>(order_action_record)?; @@ -5374,6 +5435,8 @@ pub fn trigger_spot_order( None, oracle_price, 0, + None, + None, )?; emit!(order_action_record); diff --git a/programs/drift/src/instructions/user.rs b/programs/drift/src/instructions/user.rs index 9848f1dc43..d6f42a31b2 100644 --- a/programs/drift/src/instructions/user.rs +++ b/programs/drift/src/instructions/user.rs @@ -1842,6 +1842,8 @@ pub fn handle_transfer_perp_position<'c: 'info, 'info>( maker_order_cumulative_quote_asset_amount_filled: Some(base_asset_value), oracle_price, bit_flags: 0, + taker_quote_entry_amount: None, + maker_quote_entry_amount: None, }; emit_stack::<_, { OrderActionRecord::SIZE }>(fill_record)?; diff --git a/programs/drift/src/state/events.rs b/programs/drift/src/state/events.rs index e99fe6a819..ecccd02da7 100644 --- a/programs/drift/src/state/events.rs +++ b/programs/drift/src/state/events.rs @@ -242,6 +242,10 @@ pub struct OrderActionRecord { /// Bit flags: /// 0: is_signed_message pub bit_flags: u8, + /// precision: QUOTE_PRECISION + pub taker_quote_entry_amount: Option, + /// precision: QUOTE_PRECISION + pub maker_quote_entry_amount: Option, } impl Size for OrderActionRecord { @@ -269,6 +273,8 @@ pub fn get_order_action_record( maker_order: Option, oracle_price: i64, bit_flags: u8, + taker_quote_entry_amount: Option, + maker_quote_entry_amount: Option, ) -> DriftResult { Ok(OrderActionRecord { ts, @@ -317,6 +323,8 @@ pub fn get_order_action_record( .map(|order| order.quote_asset_amount_filled), oracle_price, bit_flags, + taker_quote_entry_amount, + maker_quote_entry_amount, }) } From cf2bafdf7d262a3c28ec2783ea7387fca17b8f9a Mon Sep 17 00:00:00 2001 From: Chris Heaney Date: Tue, 13 May 2025 08:43:13 -0400 Subject: [PATCH 02/13] fix cargo fmt and test --- programs/drift/src/controller/orders.rs | 60 +++++++++++++++---------- programs/drift/src/state/events.rs | 2 +- 2 files changed, 38 insertions(+), 24 deletions(-) diff --git a/programs/drift/src/controller/orders.rs b/programs/drift/src/controller/orders.rs index 8c563b2478..3c89021a07 100644 --- a/programs/drift/src/controller/orders.rs +++ b/programs/drift/src/controller/orders.rs @@ -2169,11 +2169,12 @@ pub fn fulfill_perp_order_with_amm( validation::perp_market::validate_amm_account_for_fill(&market.amm, order_direction)?; - let quote_entry_amount = if user.perp_positions[position_index].get_direction_to_close() == order_direction { - Some(user.perp_positions[position_index].quote_entry_amount) - } else { - None - }; + let quote_entry_amount = + if user.perp_positions[position_index].get_direction_to_close() == order_direction { + Some(user.perp_positions[position_index].quote_entry_amount) + } else { + None + }; let market_side_price = match order_direction { PositionDirection::Long => market.amm.ask_price(reserve_price_before)?, @@ -2365,16 +2366,20 @@ pub fn fulfill_perp_order_with_amm( order_action_bit_flags, user.orders[order_index].is_signed_msg(), ); - let (taker_quote_entry_amount, maker_quote_entry_amount) = if let Some(quote_entry_amount) = quote_entry_amount { - let quote_entry_amount = quote_entry_amount.unsigned_abs().safe_mul(BASE_PRECISION_U64)?.safe_div(base_asset_amount)?; - if taker.is_some() { - (Some(quote_entry_amount), None) + let (taker_quote_entry_amount, maker_quote_entry_amount) = + if let Some(quote_entry_amount) = quote_entry_amount { + let quote_entry_amount = quote_entry_amount + .unsigned_abs() + .safe_mul(BASE_PRECISION_U64)? + .safe_div(base_asset_amount)?; + if taker.is_some() { + (Some(quote_entry_amount), None) + } else { + (None, Some(quote_entry_amount)) + } } else { - (None, Some(quote_entry_amount)) - } - } else { - (None, None) - }; + (None, None) + }; let order_action_record = get_order_action_record( now, OrderAction::Fill, @@ -2503,8 +2508,7 @@ pub fn fulfill_perp_order_with_match( let maker_direction = maker.orders[maker_order_index].direction; let (maker_existing_position, maker_quote_entry_amount) = { - let maker_position = maker - .get_perp_position(market.market_index)?; + let maker_position = maker.get_perp_position(market.market_index)?; let quoute_entry_amount = if maker_position.get_direction_to_close() == maker_direction { Some(maker_position.quote_entry_amount) @@ -2595,10 +2599,8 @@ pub fn fulfill_perp_order_with_match( total_quote_asset_amount = quote_asset_amount_filled_by_amm } - let (taker_existing_position, taker_quote_entry_amount) = { - let taker_position = taker - .get_perp_position(market.market_index)?; + let taker_position = taker.get_perp_position(market.market_index)?; let quoute_entry_amount = if taker_position.get_direction_to_close() == taker_direction { Some(taker_position.quote_entry_amount) @@ -2821,13 +2823,25 @@ pub fn fulfill_perp_order_with_match( order_action_bit_flags, taker.orders[taker_order_index].is_signed_msg(), ); - let taker_quote_entry_amount = if let Some(taker_quote_entry_amount) = taker_quote_entry_amount { - Some(taker_quote_entry_amount.unsigned_abs().safe_mul(BASE_PRECISION_U64)?.safe_div(base_asset_amount_fulfilled_by_maker)?) + let taker_quote_entry_amount = if let Some(taker_quote_entry_amount) = taker_quote_entry_amount + { + Some( + taker_quote_entry_amount + .unsigned_abs() + .safe_mul(BASE_PRECISION_U64)? + .safe_div(base_asset_amount_fulfilled_by_maker)?, + ) } else { None }; - let maker_quote_entry_amount = if let Some(maker_quote_entry_amount) = maker_quote_entry_amount { - Some(maker_quote_entry_amount.unsigned_abs().safe_mul(BASE_PRECISION_U64)?.safe_div(base_asset_amount_fulfilled_by_maker)?) + let maker_quote_entry_amount = if let Some(maker_quote_entry_amount) = maker_quote_entry_amount + { + Some( + maker_quote_entry_amount + .unsigned_abs() + .safe_mul(BASE_PRECISION_U64)? + .safe_div(base_asset_amount_fulfilled_by_maker)?, + ) } else { None }; diff --git a/programs/drift/src/state/events.rs b/programs/drift/src/state/events.rs index ecccd02da7..ad8a1b7053 100644 --- a/programs/drift/src/state/events.rs +++ b/programs/drift/src/state/events.rs @@ -249,7 +249,7 @@ pub struct OrderActionRecord { } impl Size for OrderActionRecord { - const SIZE: usize = 384; + const SIZE: usize = 416; } pub fn get_order_action_record( From 343e56b8aeff446be00a7456a8e90d8bb924f973 Mon Sep 17 00:00:00 2001 From: Chris Heaney Date: Tue, 13 May 2025 09:07:47 -0400 Subject: [PATCH 03/13] more reusable code --- programs/drift/src/controller/liquidation.rs | 23 +++++- programs/drift/src/controller/orders.rs | 81 +++++++------------- programs/drift/src/math/orders.rs | 20 +++++ programs/drift/src/state/user.rs | 15 ++++ 4 files changed, 80 insertions(+), 59 deletions(-) diff --git a/programs/drift/src/controller/liquidation.rs b/programs/drift/src/controller/liquidation.rs index 9766c908f2..35bc81ae10 100644 --- a/programs/drift/src/controller/liquidation.rs +++ b/programs/drift/src/controller/liquidation.rs @@ -42,8 +42,9 @@ use crate::math::margin::{ }; use crate::math::oracle::DriftAction; use crate::math::orders::{ - get_position_delta_for_fill, is_multiple_of_step_size, is_oracle_too_divergent_with_twap_5min, - standardize_base_asset_amount, standardize_base_asset_amount_ceil, + calculate_quote_entry_amount_for_fill, get_position_delta_for_fill, is_multiple_of_step_size, + is_oracle_too_divergent_with_twap_5min, standardize_base_asset_amount, + standardize_base_asset_amount_ceil, }; use crate::math::position::calculate_base_asset_value_with_oracle_price; use crate::math::safe_math::SafeMath; @@ -497,13 +498,17 @@ pub fn liquidate_perp( let ( user_existing_position_direction, user_position_direction_to_close, + user_quote_entry_amount, liquidator_existing_position_direction, + liquidator_quote_entry_amount, ) = { let mut market = perp_market_map.get_ref_mut(&market_index)?; let user_position = user.get_perp_position_mut(market_index)?; let user_existing_position_direction = user_position.get_direction(); let user_position_direction_to_close = user_position.get_direction_to_close(); + let user_quote_entry_amount = user_position + .get_quote_entry_amount_for_order_action_record(user_position_direction_to_close); update_position_and_market(user_position, &mut market, &user_position_delta)?; update_quote_asset_and_break_even_amount(user_position, &mut market, liquidator_fee)?; update_quote_asset_and_break_even_amount(user_position, &mut market, if_fee)?; @@ -521,6 +526,8 @@ pub fn liquidate_perp( let liquidator_position = liquidator.force_get_perp_position_mut(market_index)?; let liquidator_existing_position_direction = liquidator_position.get_direction(); + let liquidator_quote_entry_amount = liquidator_position + .get_quote_entry_amount_for_order_action_record(user_existing_position_direction); update_position_and_market(liquidator_position, &mut market, &liquidator_position_delta)?; update_quote_asset_and_break_even_amount( liquidator_position, @@ -547,7 +554,9 @@ pub fn liquidate_perp( ( user_existing_position_direction, user_position_direction_to_close, + user_quote_entry_amount, liquidator_existing_position_direction, + liquidator_quote_entry_amount, ) }; @@ -632,6 +641,12 @@ pub fn liquidate_perp( order: liquidator_order }); + let taker_quote_entry_amount = + calculate_quote_entry_amount_for_fill(base_asset_amount, user_quote_entry_amount)?; + + let maker_quote_entry_amount = + calculate_quote_entry_amount_for_fill(base_asset_amount, liquidator_quote_entry_amount)?; + let fill_record = OrderActionRecord { ts: now, action: OrderAction::Fill, @@ -666,8 +681,8 @@ pub fn liquidate_perp( maker_order_cumulative_quote_asset_amount_filled: Some(base_asset_value), oracle_price, bit_flags: 0, - taker_quote_entry_amount: None, - maker_quote_entry_amount: None, + taker_quote_entry_amount, + maker_quote_entry_amount, }; emit!(fill_record); diff --git a/programs/drift/src/controller/orders.rs b/programs/drift/src/controller/orders.rs index 3c89021a07..49e220fd6d 100644 --- a/programs/drift/src/controller/orders.rs +++ b/programs/drift/src/controller/orders.rs @@ -2169,12 +2169,8 @@ pub fn fulfill_perp_order_with_amm( validation::perp_market::validate_amm_account_for_fill(&market.amm, order_direction)?; - let quote_entry_amount = - if user.perp_positions[position_index].get_direction_to_close() == order_direction { - Some(user.perp_positions[position_index].quote_entry_amount) - } else { - None - }; + let quote_entry_amount = user.perp_positions[position_index] + .get_quote_entry_amount_for_order_action_record(order_direction); let market_side_price = match order_direction { PositionDirection::Long => market.amm.ask_price(reserve_price_before)?, @@ -2366,20 +2362,15 @@ pub fn fulfill_perp_order_with_amm( order_action_bit_flags, user.orders[order_index].is_signed_msg(), ); - let (taker_quote_entry_amount, maker_quote_entry_amount) = - if let Some(quote_entry_amount) = quote_entry_amount { - let quote_entry_amount = quote_entry_amount - .unsigned_abs() - .safe_mul(BASE_PRECISION_U64)? - .safe_div(base_asset_amount)?; - if taker.is_some() { - (Some(quote_entry_amount), None) - } else { - (None, Some(quote_entry_amount)) - } + let (taker_quote_entry_amount, maker_quote_entry_amount) = { + let quote_entry_amount = + calculate_quote_entry_amount_for_fill(base_asset_amount, quote_entry_amount)?; + if taker.is_some() { + (quote_entry_amount, None) } else { - (None, None) - }; + (None, quote_entry_amount) + } + }; let order_action_record = get_order_action_record( now, OrderAction::Fill, @@ -2510,13 +2501,10 @@ pub fn fulfill_perp_order_with_match( let (maker_existing_position, maker_quote_entry_amount) = { let maker_position = maker.get_perp_position(market.market_index)?; - let quoute_entry_amount = if maker_position.get_direction_to_close() == maker_direction { - Some(maker_position.quote_entry_amount) - } else { - None - }; - - (maker_position.base_asset_amount, quoute_entry_amount) + ( + maker_position.base_asset_amount, + maker_position.get_quote_entry_amount_for_order_action_record(maker_direction), + ) }; let maker_base_asset_amount = maker.orders[maker_order_index] .get_base_asset_amount_unfilled(Some(maker_existing_position))?; @@ -2602,13 +2590,10 @@ pub fn fulfill_perp_order_with_match( let (taker_existing_position, taker_quote_entry_amount) = { let taker_position = taker.get_perp_position(market.market_index)?; - let quoute_entry_amount = if taker_position.get_direction_to_close() == taker_direction { - Some(taker_position.quote_entry_amount) - } else { - None - }; - - (taker_position.base_asset_amount, quoute_entry_amount) + ( + taker_position.base_asset_amount, + taker_position.get_quote_entry_amount_for_order_action_record(taker_direction), + ) }; let taker_base_asset_amount = taker.orders[taker_order_index] @@ -2823,28 +2808,14 @@ pub fn fulfill_perp_order_with_match( order_action_bit_flags, taker.orders[taker_order_index].is_signed_msg(), ); - let taker_quote_entry_amount = if let Some(taker_quote_entry_amount) = taker_quote_entry_amount - { - Some( - taker_quote_entry_amount - .unsigned_abs() - .safe_mul(BASE_PRECISION_U64)? - .safe_div(base_asset_amount_fulfilled_by_maker)?, - ) - } else { - None - }; - let maker_quote_entry_amount = if let Some(maker_quote_entry_amount) = maker_quote_entry_amount - { - Some( - maker_quote_entry_amount - .unsigned_abs() - .safe_mul(BASE_PRECISION_U64)? - .safe_div(base_asset_amount_fulfilled_by_maker)?, - ) - } else { - None - }; + let taker_quote_entry_amount = calculate_quote_entry_amount_for_fill( + base_asset_amount_fulfilled_by_maker, + taker_quote_entry_amount, + )?; + let maker_quote_entry_amount = calculate_quote_entry_amount_for_fill( + base_asset_amount_fulfilled_by_maker, + maker_quote_entry_amount, + )?; let order_action_record = get_order_action_record( now, OrderAction::Fill, diff --git a/programs/drift/src/math/orders.rs b/programs/drift/src/math/orders.rs index 8ef924fd1a..2854548678 100644 --- a/programs/drift/src/math/orders.rs +++ b/programs/drift/src/math/orders.rs @@ -12,6 +12,7 @@ use crate::math::casting::Cast; use crate::state::fill_mode::FillMode; use crate::state::protected_maker_mode_config::ProtectedMakerParams; use crate::state::user::OrderBitFlag; +use crate::BASE_PRECISION_U64; use crate::{ load, math, FeeTier, State, BASE_PRECISION_I128, FEE_ADJUSTMENT_MAX, MAX_PREDICTION_MARKET_PRICE, MAX_PREDICTION_MARKET_PRICE_I64, OPEN_ORDER_MARGIN_REQUIREMENT, @@ -1407,3 +1408,22 @@ pub fn set_is_signed_msg_flag(mut flags: u8, value: bool) -> u8 { } flags } + +pub fn calculate_quote_entry_amount_for_fill( + base_asset_amount: u64, + quote_entry_amount: Option, +) -> DriftResult> { + if let Some(quote_entry_amount) = quote_entry_amount { + if base_asset_amount == 0 { + return Ok(None); + } + + Ok(Some( + quote_entry_amount + .safe_mul(BASE_PRECISION_U64)? + .safe_div(base_asset_amount)?, + )) + } else { + Ok(None) + } +} diff --git a/programs/drift/src/state/user.rs b/programs/drift/src/state/user.rs index 60795f4870..6cbcba60ab 100644 --- a/programs/drift/src/state/user.rs +++ b/programs/drift/src/state/user.rs @@ -1241,6 +1241,21 @@ impl PerpPosition { Ok(unrealized_pnl) } } + + pub fn get_quote_entry_amount_for_order_action_record( + &self, + fill_direction: PositionDirection, + ) -> Option { + if self.base_asset_amount == 0 { + return None; + } + + if self.get_direction_to_close() == fill_direction { + Some(self.quote_entry_amount.unsigned_abs()) + } else { + None + } + } } pub(crate) type PerpPositions = [PerpPosition; 8]; From 2a262e9fa74fb9e9ecae3bb7fa29b24bcace9a8e Mon Sep 17 00:00:00 2001 From: Chris Heaney Date: Tue, 13 May 2025 09:18:52 -0400 Subject: [PATCH 04/13] more reusable code --- programs/drift/src/instructions/user.rs | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/programs/drift/src/instructions/user.rs b/programs/drift/src/instructions/user.rs index d6f42a31b2..bf8f11cd6d 100644 --- a/programs/drift/src/instructions/user.rs +++ b/programs/drift/src/instructions/user.rs @@ -40,6 +40,7 @@ use crate::math::margin::{ }; use crate::math::oracle::is_oracle_valid_for_action; use crate::math::oracle::DriftAction; +use crate::math::orders::calculate_quote_entry_amount_for_fill; use crate::math::orders::get_position_delta_for_fill; use crate::math::orders::is_multiple_of_step_size; use crate::math::orders::standardize_price_i64; @@ -1707,20 +1708,33 @@ pub fn handle_transfer_perp_position<'c: 'info, 'info>( .force_get_perp_position_mut(market_index) .map(|position| position.get_direction())?; - { + let (from_quote_entry_amount, to_quote_entry_amount) = { let mut market = perp_market_map.get_ref_mut(&market_index)?; let from_user_position = from_user.force_get_perp_position_mut(market_index)?; + let from_quote_entry_amount = calculate_quote_entry_amount_for_fill( + transfer_amount_abs, + from_user_position.get_quote_entry_amount_for_order_action_record(direction_to_close), + )?; + update_position_and_market(from_user_position, &mut market, &from_user_position_delta)?; let to_user_position = to_user.force_get_perp_position_mut(market_index)?; + let to_quote_entry_amount = calculate_quote_entry_amount_for_fill( + transfer_amount_abs, + to_user_position + .get_quote_entry_amount_for_order_action_record(direction_to_close.opposite()), + )?; + update_position_and_market(to_user_position, &mut market, &to_user_position_delta)?; validate_perp_position_with_perp_market(from_user_position, &market)?; validate_perp_position_with_perp_market(to_user_position, &market)?; - } + + (from_quote_entry_amount, to_quote_entry_amount) + }; let from_user_margin_calculation = calculate_margin_requirement_and_total_collateral_and_liability_info( @@ -1842,8 +1856,8 @@ pub fn handle_transfer_perp_position<'c: 'info, 'info>( maker_order_cumulative_quote_asset_amount_filled: Some(base_asset_value), oracle_price, bit_flags: 0, - taker_quote_entry_amount: None, - maker_quote_entry_amount: None, + taker_quote_entry_amount: to_quote_entry_amount, + maker_quote_entry_amount: from_quote_entry_amount, }; emit_stack::<_, { OrderActionRecord::SIZE }>(fill_record)?; From b789c7b6259ec5d21ef709668f22ca824a2e4471 Mon Sep 17 00:00:00 2001 From: Chris Heaney Date: Tue, 13 May 2025 09:19:58 -0400 Subject: [PATCH 05/13] add another comment --- programs/drift/src/state/events.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/programs/drift/src/state/events.rs b/programs/drift/src/state/events.rs index ad8a1b7053..6d0b16d918 100644 --- a/programs/drift/src/state/events.rs +++ b/programs/drift/src/state/events.rs @@ -243,8 +243,10 @@ pub struct OrderActionRecord { /// 0: is_signed_message pub bit_flags: u8, /// precision: QUOTE_PRECISION + /// Only Some if the taker reduced position pub taker_quote_entry_amount: Option, /// precision: QUOTE_PRECISION + /// Only Some if the maker reduced position pub maker_quote_entry_amount: Option, } From f322baf5dc9c5aa94921e11d8e62ff2b65e10e51 Mon Sep 17 00:00:00 2001 From: Chris Heaney Date: Tue, 13 May 2025 12:35:57 -0400 Subject: [PATCH 06/13] fix math --- programs/drift/src/controller/liquidation.rs | 20 ++++++++++---------- programs/drift/src/controller/orders.rs | 14 +++++++------- programs/drift/src/instructions/user.rs | 4 ++-- programs/drift/src/math/orders.rs | 8 ++++---- programs/drift/src/state/user.rs | 6 +++--- 5 files changed, 26 insertions(+), 26 deletions(-) diff --git a/programs/drift/src/controller/liquidation.rs b/programs/drift/src/controller/liquidation.rs index 35bc81ae10..08af19da01 100644 --- a/programs/drift/src/controller/liquidation.rs +++ b/programs/drift/src/controller/liquidation.rs @@ -498,17 +498,17 @@ pub fn liquidate_perp( let ( user_existing_position_direction, user_position_direction_to_close, - user_quote_entry_amount, + user_quote_entry_amount_params, liquidator_existing_position_direction, - liquidator_quote_entry_amount, + liquidator_quote_entry_amount_params, ) = { let mut market = perp_market_map.get_ref_mut(&market_index)?; let user_position = user.get_perp_position_mut(market_index)?; let user_existing_position_direction = user_position.get_direction(); let user_position_direction_to_close = user_position.get_direction_to_close(); - let user_quote_entry_amount = user_position - .get_quote_entry_amount_for_order_action_record(user_position_direction_to_close); + let user_quote_entry_amount_params = user_position + .get_quote_entry_amount_params_for_order_action_record(user_position_direction_to_close); update_position_and_market(user_position, &mut market, &user_position_delta)?; update_quote_asset_and_break_even_amount(user_position, &mut market, liquidator_fee)?; update_quote_asset_and_break_even_amount(user_position, &mut market, if_fee)?; @@ -526,8 +526,8 @@ pub fn liquidate_perp( let liquidator_position = liquidator.force_get_perp_position_mut(market_index)?; let liquidator_existing_position_direction = liquidator_position.get_direction(); - let liquidator_quote_entry_amount = liquidator_position - .get_quote_entry_amount_for_order_action_record(user_existing_position_direction); + let liquidator_quote_entry_amount_params = liquidator_position + .get_quote_entry_amount_params_for_order_action_record(user_existing_position_direction); update_position_and_market(liquidator_position, &mut market, &liquidator_position_delta)?; update_quote_asset_and_break_even_amount( liquidator_position, @@ -554,9 +554,9 @@ pub fn liquidate_perp( ( user_existing_position_direction, user_position_direction_to_close, - user_quote_entry_amount, + user_quote_entry_amount_params, liquidator_existing_position_direction, - liquidator_quote_entry_amount, + liquidator_quote_entry_amount_params, ) }; @@ -642,10 +642,10 @@ pub fn liquidate_perp( }); let taker_quote_entry_amount = - calculate_quote_entry_amount_for_fill(base_asset_amount, user_quote_entry_amount)?; + calculate_quote_entry_amount_for_fill(base_asset_amount, user_quote_entry_amount_params)?; let maker_quote_entry_amount = - calculate_quote_entry_amount_for_fill(base_asset_amount, liquidator_quote_entry_amount)?; + calculate_quote_entry_amount_for_fill(base_asset_amount, liquidator_quote_entry_amount_params)?; let fill_record = OrderActionRecord { ts: now, diff --git a/programs/drift/src/controller/orders.rs b/programs/drift/src/controller/orders.rs index 49e220fd6d..b4f8d499c9 100644 --- a/programs/drift/src/controller/orders.rs +++ b/programs/drift/src/controller/orders.rs @@ -2170,7 +2170,7 @@ pub fn fulfill_perp_order_with_amm( validation::perp_market::validate_amm_account_for_fill(&market.amm, order_direction)?; let quote_entry_amount = user.perp_positions[position_index] - .get_quote_entry_amount_for_order_action_record(order_direction); + .get_quote_entry_amount_params_for_order_action_record(order_direction); let market_side_price = match order_direction { PositionDirection::Long => market.amm.ask_price(reserve_price_before)?, @@ -2498,12 +2498,12 @@ pub fn fulfill_perp_order_with_match( .get_base_asset_amount_unfilled(Some(taker_existing_position))?; let maker_direction = maker.orders[maker_order_index].direction; - let (maker_existing_position, maker_quote_entry_amount) = { + let (maker_existing_position, maker_quote_entry_amount_params) = { let maker_position = maker.get_perp_position(market.market_index)?; ( maker_position.base_asset_amount, - maker_position.get_quote_entry_amount_for_order_action_record(maker_direction), + maker_position.get_quote_entry_amount_params_for_order_action_record(maker_direction), ) }; let maker_base_asset_amount = maker.orders[maker_order_index] @@ -2587,12 +2587,12 @@ pub fn fulfill_perp_order_with_match( total_quote_asset_amount = quote_asset_amount_filled_by_amm } - let (taker_existing_position, taker_quote_entry_amount) = { + let (taker_existing_position, taker_quote_entry_amount_params) = { let taker_position = taker.get_perp_position(market.market_index)?; ( taker_position.base_asset_amount, - taker_position.get_quote_entry_amount_for_order_action_record(taker_direction), + taker_position.get_quote_entry_amount_params_for_order_action_record(taker_direction), ) }; @@ -2810,11 +2810,11 @@ pub fn fulfill_perp_order_with_match( ); let taker_quote_entry_amount = calculate_quote_entry_amount_for_fill( base_asset_amount_fulfilled_by_maker, - taker_quote_entry_amount, + taker_quote_entry_amount_params, )?; let maker_quote_entry_amount = calculate_quote_entry_amount_for_fill( base_asset_amount_fulfilled_by_maker, - maker_quote_entry_amount, + maker_quote_entry_amount_params, )?; let order_action_record = get_order_action_record( now, diff --git a/programs/drift/src/instructions/user.rs b/programs/drift/src/instructions/user.rs index bf8f11cd6d..1e6b10fdc2 100644 --- a/programs/drift/src/instructions/user.rs +++ b/programs/drift/src/instructions/user.rs @@ -1715,7 +1715,7 @@ pub fn handle_transfer_perp_position<'c: 'info, 'info>( let from_quote_entry_amount = calculate_quote_entry_amount_for_fill( transfer_amount_abs, - from_user_position.get_quote_entry_amount_for_order_action_record(direction_to_close), + from_user_position.get_quote_entry_amount_params_for_order_action_record(direction_to_close), )?; update_position_and_market(from_user_position, &mut market, &from_user_position_delta)?; @@ -1725,7 +1725,7 @@ pub fn handle_transfer_perp_position<'c: 'info, 'info>( let to_quote_entry_amount = calculate_quote_entry_amount_for_fill( transfer_amount_abs, to_user_position - .get_quote_entry_amount_for_order_action_record(direction_to_close.opposite()), + .get_quote_entry_amount_params_for_order_action_record(direction_to_close.opposite()), )?; update_position_and_market(to_user_position, &mut market, &to_user_position_delta)?; diff --git a/programs/drift/src/math/orders.rs b/programs/drift/src/math/orders.rs index 2854548678..e3b3b3441f 100644 --- a/programs/drift/src/math/orders.rs +++ b/programs/drift/src/math/orders.rs @@ -1410,17 +1410,17 @@ pub fn set_is_signed_msg_flag(mut flags: u8, value: bool) -> u8 { } pub fn calculate_quote_entry_amount_for_fill( - base_asset_amount: u64, - quote_entry_amount: Option, + base_asset_amount_filled: u64, + quote_entry_amount_params: Option<(u64, u64)>, ) -> DriftResult> { - if let Some(quote_entry_amount) = quote_entry_amount { + if let Some((quote_entry_amount, base_asset_amount)) = quote_entry_amount_params { if base_asset_amount == 0 { return Ok(None); } Ok(Some( quote_entry_amount - .safe_mul(BASE_PRECISION_U64)? + .safe_mul(base_asset_amount_filled)? .safe_div(base_asset_amount)?, )) } else { diff --git a/programs/drift/src/state/user.rs b/programs/drift/src/state/user.rs index 6cbcba60ab..9060997129 100644 --- a/programs/drift/src/state/user.rs +++ b/programs/drift/src/state/user.rs @@ -1242,16 +1242,16 @@ impl PerpPosition { } } - pub fn get_quote_entry_amount_for_order_action_record( + pub fn get_quote_entry_amount_params_for_order_action_record( &self, fill_direction: PositionDirection, - ) -> Option { + ) -> Option<(u64, u64)> { if self.base_asset_amount == 0 { return None; } if self.get_direction_to_close() == fill_direction { - Some(self.quote_entry_amount.unsigned_abs()) + Some((self.quote_entry_amount.unsigned_abs(), self.base_asset_amount.unsigned_abs())) } else { None } From da8673467560ccf344c0956db9afd2c6f55244cd Mon Sep 17 00:00:00 2001 From: Chris Heaney Date: Wed, 14 May 2025 10:07:23 -0400 Subject: [PATCH 07/13] account for pos flip --- programs/drift/src/controller/liquidation.rs | 32 +++++++------- programs/drift/src/controller/orders.rs | 46 ++++++++++++++------ programs/drift/src/instructions/user.rs | 20 +++++---- programs/drift/src/math/orders.rs | 27 +++++++----- programs/drift/src/state/events.rs | 22 +++++++--- programs/drift/src/state/user.rs | 2 +- 6 files changed, 93 insertions(+), 56 deletions(-) diff --git a/programs/drift/src/controller/liquidation.rs b/programs/drift/src/controller/liquidation.rs index 08af19da01..bd339e38b5 100644 --- a/programs/drift/src/controller/liquidation.rs +++ b/programs/drift/src/controller/liquidation.rs @@ -42,7 +42,7 @@ use crate::math::margin::{ }; use crate::math::oracle::DriftAction; use crate::math::orders::{ - calculate_quote_entry_amount_for_fill, get_position_delta_for_fill, is_multiple_of_step_size, + calculate_existing_position_fields_for_order_action_record, get_position_delta_for_fill, is_multiple_of_step_size, is_oracle_too_divergent_with_twap_5min, standardize_base_asset_amount, standardize_base_asset_amount_ceil, }; @@ -497,18 +497,18 @@ pub fn liquidate_perp( let ( user_existing_position_direction, - user_position_direction_to_close, + user_exisiting_position_params_for_order_action, user_quote_entry_amount_params, liquidator_existing_position_direction, - liquidator_quote_entry_amount_params, + liquidator_existing_position_params_for_order_action, ) = { let mut market = perp_market_map.get_ref_mut(&market_index)?; let user_position = user.get_perp_position_mut(market_index)?; let user_existing_position_direction = user_position.get_direction(); let user_position_direction_to_close = user_position.get_direction_to_close(); - let user_quote_entry_amount_params = user_position - .get_quote_entry_amount_params_for_order_action_record(user_position_direction_to_close); + let user_existing_position_params = user_position + .get_existing_position_params_for_order_action_record(user_position_direction_to_close); update_position_and_market(user_position, &mut market, &user_position_delta)?; update_quote_asset_and_break_even_amount(user_position, &mut market, liquidator_fee)?; update_quote_asset_and_break_even_amount(user_position, &mut market, if_fee)?; @@ -527,7 +527,7 @@ pub fn liquidate_perp( let liquidator_position = liquidator.force_get_perp_position_mut(market_index)?; let liquidator_existing_position_direction = liquidator_position.get_direction(); let liquidator_quote_entry_amount_params = liquidator_position - .get_quote_entry_amount_params_for_order_action_record(user_existing_position_direction); + .get_existing_position_params_for_order_action_record(user_existing_position_direction); update_position_and_market(liquidator_position, &mut market, &liquidator_position_delta)?; update_quote_asset_and_break_even_amount( liquidator_position, @@ -554,7 +554,7 @@ pub fn liquidate_perp( ( user_existing_position_direction, user_position_direction_to_close, - user_quote_entry_amount_params, + user_existing_position_params, liquidator_existing_position_direction, liquidator_quote_entry_amount_params, ) @@ -602,7 +602,7 @@ pub fn liquidate_perp( status: OrderStatus::Open, order_type: OrderType::Market, market_type: MarketType::Perp, - direction: user_position_direction_to_close, + direction: user_exisiting_position_params_for_order_action, existing_position_direction: user_existing_position_direction, ..Order::default() }; @@ -641,11 +641,11 @@ pub fn liquidate_perp( order: liquidator_order }); - let taker_quote_entry_amount = - calculate_quote_entry_amount_for_fill(base_asset_amount, user_quote_entry_amount_params)?; + let (taker_existing_quote_entry_amount, taker_existing_base_asset_amount) = + calculate_existing_position_fields_for_order_action_record(base_asset_amount, user_quote_entry_amount_params)?; - let maker_quote_entry_amount = - calculate_quote_entry_amount_for_fill(base_asset_amount, liquidator_quote_entry_amount_params)?; + let (maker_existing_quote_entry_amount, maker_existing_base_asset_amount) = + calculate_existing_position_fields_for_order_action_record(base_asset_amount, liquidator_existing_position_params_for_order_action)?; let fill_record = OrderActionRecord { ts: now, @@ -669,7 +669,7 @@ pub fn liquidate_perp( spot_fulfillment_method_fee: None, taker: Some(*user_key), taker_order_id: Some(user_order_id), - taker_order_direction: Some(user_position_direction_to_close), + taker_order_direction: Some(user_exisiting_position_params_for_order_action), taker_order_base_asset_amount: Some(base_asset_amount), taker_order_cumulative_base_asset_amount_filled: Some(base_asset_amount), taker_order_cumulative_quote_asset_amount_filled: Some(base_asset_value), @@ -681,8 +681,10 @@ pub fn liquidate_perp( maker_order_cumulative_quote_asset_amount_filled: Some(base_asset_value), oracle_price, bit_flags: 0, - taker_quote_entry_amount, - maker_quote_entry_amount, + taker_existing_quote_entry_amount: taker_existing_quote_entry_amount, + taker_existing_base_asset_amount: taker_existing_base_asset_amount, + maker_existing_quote_entry_amount: maker_existing_quote_entry_amount, + maker_existing_base_asset_amount: maker_existing_base_asset_amount, }; emit!(fill_record); diff --git a/programs/drift/src/controller/orders.rs b/programs/drift/src/controller/orders.rs index b4f8d499c9..8d2a22d7f7 100644 --- a/programs/drift/src/controller/orders.rs +++ b/programs/drift/src/controller/orders.rs @@ -426,6 +426,8 @@ pub fn place_perp_order( bit_flags, None, None, + None, + None, )?; emit_stack::<_, { OrderActionRecord::SIZE }>(order_action_record)?; @@ -703,6 +705,8 @@ pub fn cancel_order( 0, None, None, + None, + None, )?; emit_stack::<_, { OrderActionRecord::SIZE }>(order_action_record)?; } @@ -2170,7 +2174,7 @@ pub fn fulfill_perp_order_with_amm( validation::perp_market::validate_amm_account_for_fill(&market.amm, order_direction)?; let quote_entry_amount = user.perp_positions[position_index] - .get_quote_entry_amount_params_for_order_action_record(order_direction); + .get_existing_position_params_for_order_action_record(order_direction); let market_side_price = match order_direction { PositionDirection::Long => market.amm.ask_price(reserve_price_before)?, @@ -2362,13 +2366,13 @@ pub fn fulfill_perp_order_with_amm( order_action_bit_flags, user.orders[order_index].is_signed_msg(), ); - let (taker_quote_entry_amount, maker_quote_entry_amount) = { - let quote_entry_amount = - calculate_quote_entry_amount_for_fill(base_asset_amount, quote_entry_amount)?; + let (taker_existing_quote_entry_amount, taker_existing_base_asset_amount, maker_existing_quote_entry_amount, maker_existing_base_asset_amount) = { + let (existing_quote_entry_amount, existing_base_asset_amount) = + calculate_existing_position_fields_for_order_action_record(base_asset_amount, quote_entry_amount)?; if taker.is_some() { - (quote_entry_amount, None) + (existing_quote_entry_amount, existing_base_asset_amount, None, None) } else { - (None, quote_entry_amount) + (None, None, existing_quote_entry_amount, existing_base_asset_amount) } }; let order_action_record = get_order_action_record( @@ -2396,8 +2400,10 @@ pub fn fulfill_perp_order_with_amm( maker_order, oracle_map.get_price_data(&market.oracle_id())?.price, order_action_bit_flags, - taker_quote_entry_amount, - maker_quote_entry_amount, + taker_existing_quote_entry_amount, + taker_existing_base_asset_amount, + maker_existing_quote_entry_amount, + maker_existing_base_asset_amount, )?; emit_stack::<_, { OrderActionRecord::SIZE }>(order_action_record)?; @@ -2503,7 +2509,7 @@ pub fn fulfill_perp_order_with_match( ( maker_position.base_asset_amount, - maker_position.get_quote_entry_amount_params_for_order_action_record(maker_direction), + maker_position.get_existing_position_params_for_order_action_record(maker_direction), ) }; let maker_base_asset_amount = maker.orders[maker_order_index] @@ -2592,7 +2598,7 @@ pub fn fulfill_perp_order_with_match( ( taker_position.base_asset_amount, - taker_position.get_quote_entry_amount_params_for_order_action_record(taker_direction), + taker_position.get_existing_position_params_for_order_action_record(taker_direction), ) }; @@ -2808,11 +2814,11 @@ pub fn fulfill_perp_order_with_match( order_action_bit_flags, taker.orders[taker_order_index].is_signed_msg(), ); - let taker_quote_entry_amount = calculate_quote_entry_amount_for_fill( + let (taker_existing_quote_entry_amount, taker_existing_base_asset_amount) = calculate_existing_position_fields_for_order_action_record( base_asset_amount_fulfilled_by_maker, taker_quote_entry_amount_params, )?; - let maker_quote_entry_amount = calculate_quote_entry_amount_for_fill( + let (maker_existing_quote_entry_amount, maker_existing_base_asset_amount) = calculate_existing_position_fields_for_order_action_record( base_asset_amount_fulfilled_by_maker, maker_quote_entry_amount_params, )?; @@ -2837,8 +2843,10 @@ pub fn fulfill_perp_order_with_match( Some(maker.orders[maker_order_index]), oracle_map.get_price_data(&market.oracle_id())?.price, order_action_bit_flags, - taker_quote_entry_amount, - maker_quote_entry_amount, + taker_existing_quote_entry_amount, + taker_existing_base_asset_amount, + maker_existing_quote_entry_amount, + maker_existing_base_asset_amount, )?; emit_stack::<_, { OrderActionRecord::SIZE }>(order_action_record)?; @@ -3058,6 +3066,8 @@ pub fn trigger_order( 0, None, None, + None, + None, )?; emit!(order_action_record); @@ -3731,6 +3741,8 @@ pub fn place_spot_order( 0, None, None, + None, + None, )?; emit_stack::<_, { OrderActionRecord::SIZE }>(order_action_record)?; @@ -4960,6 +4972,8 @@ pub fn fulfill_spot_order_with_match( 0, None, None, + None, + None, )?; emit_stack::<_, { OrderActionRecord::SIZE }>(order_action_record)?; @@ -5228,6 +5242,8 @@ pub fn fulfill_spot_order_with_external_market( 0, None, None, + None, + None, )?; emit_stack::<_, { OrderActionRecord::SIZE }>(order_action_record)?; @@ -5422,6 +5438,8 @@ pub fn trigger_spot_order( 0, None, None, + None, + None, )?; emit!(order_action_record); diff --git a/programs/drift/src/instructions/user.rs b/programs/drift/src/instructions/user.rs index 1e6b10fdc2..35961cc88b 100644 --- a/programs/drift/src/instructions/user.rs +++ b/programs/drift/src/instructions/user.rs @@ -40,7 +40,7 @@ use crate::math::margin::{ }; use crate::math::oracle::is_oracle_valid_for_action; use crate::math::oracle::DriftAction; -use crate::math::orders::calculate_quote_entry_amount_for_fill; +use crate::math::orders::calculate_existing_position_fields_for_order_action_record; use crate::math::orders::get_position_delta_for_fill; use crate::math::orders::is_multiple_of_step_size; use crate::math::orders::standardize_price_i64; @@ -1708,24 +1708,24 @@ pub fn handle_transfer_perp_position<'c: 'info, 'info>( .force_get_perp_position_mut(market_index) .map(|position| position.get_direction())?; - let (from_quote_entry_amount, to_quote_entry_amount) = { + let (from_existing_quote_entry_amount, from_existing_base_asset_amount, to_existing_quote_entry_amount, to_existing_base_asset_amount) = { let mut market = perp_market_map.get_ref_mut(&market_index)?; let from_user_position = from_user.force_get_perp_position_mut(market_index)?; - let from_quote_entry_amount = calculate_quote_entry_amount_for_fill( + let (from_existing_quote_entry_amount, from_existing_base_asset_amount) = calculate_existing_position_fields_for_order_action_record( transfer_amount_abs, - from_user_position.get_quote_entry_amount_params_for_order_action_record(direction_to_close), + from_user_position.get_existing_position_params_for_order_action_record(direction_to_close), )?; update_position_and_market(from_user_position, &mut market, &from_user_position_delta)?; let to_user_position = to_user.force_get_perp_position_mut(market_index)?; - let to_quote_entry_amount = calculate_quote_entry_amount_for_fill( + let (to_existing_quote_entry_amount, to_existing_base_asset_amount) = calculate_existing_position_fields_for_order_action_record( transfer_amount_abs, to_user_position - .get_quote_entry_amount_params_for_order_action_record(direction_to_close.opposite()), + .get_existing_position_params_for_order_action_record(direction_to_close.opposite()), )?; update_position_and_market(to_user_position, &mut market, &to_user_position_delta)?; @@ -1733,7 +1733,7 @@ pub fn handle_transfer_perp_position<'c: 'info, 'info>( validate_perp_position_with_perp_market(from_user_position, &market)?; validate_perp_position_with_perp_market(to_user_position, &market)?; - (from_quote_entry_amount, to_quote_entry_amount) + (from_existing_quote_entry_amount, from_existing_base_asset_amount, to_existing_quote_entry_amount, to_existing_base_asset_amount) }; let from_user_margin_calculation = @@ -1856,8 +1856,10 @@ pub fn handle_transfer_perp_position<'c: 'info, 'info>( maker_order_cumulative_quote_asset_amount_filled: Some(base_asset_value), oracle_price, bit_flags: 0, - taker_quote_entry_amount: to_quote_entry_amount, - maker_quote_entry_amount: from_quote_entry_amount, + taker_existing_quote_entry_amount: to_existing_quote_entry_amount, + taker_existing_base_asset_amount: to_existing_base_asset_amount, + maker_existing_quote_entry_amount: from_existing_quote_entry_amount, + maker_existing_base_asset_amount: from_existing_base_asset_amount, }; emit_stack::<_, { OrderActionRecord::SIZE }>(fill_record)?; diff --git a/programs/drift/src/math/orders.rs b/programs/drift/src/math/orders.rs index e3b3b3441f..736a7ea330 100644 --- a/programs/drift/src/math/orders.rs +++ b/programs/drift/src/math/orders.rs @@ -1409,21 +1409,26 @@ pub fn set_is_signed_msg_flag(mut flags: u8, value: bool) -> u8 { flags } -pub fn calculate_quote_entry_amount_for_fill( +pub fn calculate_existing_position_fields_for_order_action_record( base_asset_amount_filled: u64, - quote_entry_amount_params: Option<(u64, u64)>, -) -> DriftResult> { - if let Some((quote_entry_amount, base_asset_amount)) = quote_entry_amount_params { + existing_posiiton_params: Option<(u64, u64)>, +) -> DriftResult<(Option, Option)> { + if let Some((quote_entry_amount, base_asset_amount)) = existing_posiiton_params { if base_asset_amount == 0 { - return Ok(None); + return Ok((None, None)); } - Ok(Some( - quote_entry_amount - .safe_mul(base_asset_amount_filled)? - .safe_div(base_asset_amount)?, - )) + if base_asset_amount_filled > base_asset_amount { + return Ok((Some(quote_entry_amount), Some(base_asset_amount))); + } else { + return Ok(( + Some(quote_entry_amount + .safe_mul(base_asset_amount_filled)? + .safe_div(base_asset_amount)?), + None, + )); + } } else { - Ok(None) + Ok((None, None)) } } diff --git a/programs/drift/src/state/events.rs b/programs/drift/src/state/events.rs index 6d0b16d918..a247d5607b 100644 --- a/programs/drift/src/state/events.rs +++ b/programs/drift/src/state/events.rs @@ -244,10 +244,16 @@ pub struct OrderActionRecord { pub bit_flags: u8, /// precision: QUOTE_PRECISION /// Only Some if the taker reduced position - pub taker_quote_entry_amount: Option, + pub taker_existing_quote_entry_amount: Option, /// precision: QUOTE_PRECISION /// Only Some if the maker reduced position - pub maker_quote_entry_amount: Option, + pub maker_existing_quote_entry_amount: Option, + /// precision: BASE_PRECISION + /// Only Some if the taker flipped position direction + pub taker_existing_base_asset_amount: Option, + /// precision: BASE_PRECISION + /// Only Some if the maker flipped position direction + pub maker_existing_base_asset_amount: Option, } impl Size for OrderActionRecord { @@ -275,8 +281,10 @@ pub fn get_order_action_record( maker_order: Option, oracle_price: i64, bit_flags: u8, - taker_quote_entry_amount: Option, - maker_quote_entry_amount: Option, + taker_existing_quote_entry_amount: Option, + maker_existing_quote_entry_amount: Option, + taker_existing_base_asset_amount: Option, + maker_existing_base_asset_amount: Option, ) -> DriftResult { Ok(OrderActionRecord { ts, @@ -325,8 +333,10 @@ pub fn get_order_action_record( .map(|order| order.quote_asset_amount_filled), oracle_price, bit_flags, - taker_quote_entry_amount, - maker_quote_entry_amount, + taker_existing_quote_entry_amount, + maker_existing_quote_entry_amount, + taker_existing_base_asset_amount, + maker_existing_base_asset_amount, }) } diff --git a/programs/drift/src/state/user.rs b/programs/drift/src/state/user.rs index 9060997129..a6193d15dc 100644 --- a/programs/drift/src/state/user.rs +++ b/programs/drift/src/state/user.rs @@ -1242,7 +1242,7 @@ impl PerpPosition { } } - pub fn get_quote_entry_amount_params_for_order_action_record( + pub fn get_existing_position_params_for_order_action_record( &self, fill_direction: PositionDirection, ) -> Option<(u64, u64)> { From 9fe05170dff454932960a795c840b0fde5024f78 Mon Sep 17 00:00:00 2001 From: Chris Heaney Date: Wed, 14 May 2025 10:09:39 -0400 Subject: [PATCH 08/13] fix typo --- programs/drift/src/controller/liquidation.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/programs/drift/src/controller/liquidation.rs b/programs/drift/src/controller/liquidation.rs index bd339e38b5..f918e352a1 100644 --- a/programs/drift/src/controller/liquidation.rs +++ b/programs/drift/src/controller/liquidation.rs @@ -497,8 +497,8 @@ pub fn liquidate_perp( let ( user_existing_position_direction, - user_exisiting_position_params_for_order_action, - user_quote_entry_amount_params, + user_position_direction_to_close, + user_existing_position_params_for_order_action, liquidator_existing_position_direction, liquidator_existing_position_params_for_order_action, ) = { @@ -602,7 +602,7 @@ pub fn liquidate_perp( status: OrderStatus::Open, order_type: OrderType::Market, market_type: MarketType::Perp, - direction: user_exisiting_position_params_for_order_action, + direction: user_position_direction_to_close, existing_position_direction: user_existing_position_direction, ..Order::default() }; @@ -642,7 +642,7 @@ pub fn liquidate_perp( }); let (taker_existing_quote_entry_amount, taker_existing_base_asset_amount) = - calculate_existing_position_fields_for_order_action_record(base_asset_amount, user_quote_entry_amount_params)?; + calculate_existing_position_fields_for_order_action_record(base_asset_amount, user_existing_position_params_for_order_action)?; let (maker_existing_quote_entry_amount, maker_existing_base_asset_amount) = calculate_existing_position_fields_for_order_action_record(base_asset_amount, liquidator_existing_position_params_for_order_action)?; @@ -669,7 +669,7 @@ pub fn liquidate_perp( spot_fulfillment_method_fee: None, taker: Some(*user_key), taker_order_id: Some(user_order_id), - taker_order_direction: Some(user_exisiting_position_params_for_order_action), + taker_order_direction: Some(user_position_direction_to_close), taker_order_base_asset_amount: Some(base_asset_amount), taker_order_cumulative_base_asset_amount_filled: Some(base_asset_amount), taker_order_cumulative_quote_asset_amount_filled: Some(base_asset_value), From d89dd7f55f40fb311649cef6c55e5d9438f0b125 Mon Sep 17 00:00:00 2001 From: Chris Heaney Date: Wed, 14 May 2025 10:11:05 -0400 Subject: [PATCH 09/13] missed commit --- programs/drift/src/controller/orders.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/programs/drift/src/controller/orders.rs b/programs/drift/src/controller/orders.rs index 8d2a22d7f7..f5847030d8 100644 --- a/programs/drift/src/controller/orders.rs +++ b/programs/drift/src/controller/orders.rs @@ -2173,7 +2173,7 @@ pub fn fulfill_perp_order_with_amm( validation::perp_market::validate_amm_account_for_fill(&market.amm, order_direction)?; - let quote_entry_amount = user.perp_positions[position_index] + let existing_position_params_for_order_action = user.perp_positions[position_index] .get_existing_position_params_for_order_action_record(order_direction); let market_side_price = match order_direction { @@ -2368,7 +2368,7 @@ pub fn fulfill_perp_order_with_amm( ); let (taker_existing_quote_entry_amount, taker_existing_base_asset_amount, maker_existing_quote_entry_amount, maker_existing_base_asset_amount) = { let (existing_quote_entry_amount, existing_base_asset_amount) = - calculate_existing_position_fields_for_order_action_record(base_asset_amount, quote_entry_amount)?; + calculate_existing_position_fields_for_order_action_record(base_asset_amount, existing_position_params_for_order_action)?; if taker.is_some() { (existing_quote_entry_amount, existing_base_asset_amount, None, None) } else { @@ -2504,7 +2504,7 @@ pub fn fulfill_perp_order_with_match( .get_base_asset_amount_unfilled(Some(taker_existing_position))?; let maker_direction = maker.orders[maker_order_index].direction; - let (maker_existing_position, maker_quote_entry_amount_params) = { + let (maker_existing_position, maker_existing_position_params_for_order_action) = { let maker_position = maker.get_perp_position(market.market_index)?; ( @@ -2593,7 +2593,7 @@ pub fn fulfill_perp_order_with_match( total_quote_asset_amount = quote_asset_amount_filled_by_amm } - let (taker_existing_position, taker_quote_entry_amount_params) = { + let (taker_existing_position, taker_existing_position_params_for_order_action) = { let taker_position = taker.get_perp_position(market.market_index)?; ( @@ -2816,11 +2816,11 @@ pub fn fulfill_perp_order_with_match( ); let (taker_existing_quote_entry_amount, taker_existing_base_asset_amount) = calculate_existing_position_fields_for_order_action_record( base_asset_amount_fulfilled_by_maker, - taker_quote_entry_amount_params, + taker_existing_position_params_for_order_action, )?; let (maker_existing_quote_entry_amount, maker_existing_base_asset_amount) = calculate_existing_position_fields_for_order_action_record( base_asset_amount_fulfilled_by_maker, - maker_quote_entry_amount_params, + maker_existing_position_params_for_order_action, )?; let order_action_record = get_order_action_record( now, From 1ad87723d7f94b9c268dcf512a718031adbbfcbb Mon Sep 17 00:00:00 2001 From: Chris Heaney Date: Wed, 14 May 2025 10:16:49 -0400 Subject: [PATCH 10/13] more fixes --- programs/drift/src/state/events.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/programs/drift/src/state/events.rs b/programs/drift/src/state/events.rs index a247d5607b..18accc9d59 100644 --- a/programs/drift/src/state/events.rs +++ b/programs/drift/src/state/events.rs @@ -245,12 +245,12 @@ pub struct OrderActionRecord { /// precision: QUOTE_PRECISION /// Only Some if the taker reduced position pub taker_existing_quote_entry_amount: Option, - /// precision: QUOTE_PRECISION - /// Only Some if the maker reduced position - pub maker_existing_quote_entry_amount: Option, /// precision: BASE_PRECISION /// Only Some if the taker flipped position direction pub taker_existing_base_asset_amount: Option, + /// precision: QUOTE_PRECISION + /// Only Some if the maker reduced position + pub maker_existing_quote_entry_amount: Option, /// precision: BASE_PRECISION /// Only Some if the maker flipped position direction pub maker_existing_base_asset_amount: Option, @@ -282,8 +282,8 @@ pub fn get_order_action_record( oracle_price: i64, bit_flags: u8, taker_existing_quote_entry_amount: Option, - maker_existing_quote_entry_amount: Option, taker_existing_base_asset_amount: Option, + maker_existing_quote_entry_amount: Option, maker_existing_base_asset_amount: Option, ) -> DriftResult { Ok(OrderActionRecord { @@ -334,8 +334,8 @@ pub fn get_order_action_record( oracle_price, bit_flags, taker_existing_quote_entry_amount, - maker_existing_quote_entry_amount, taker_existing_base_asset_amount, + maker_existing_quote_entry_amount, maker_existing_base_asset_amount, }) } From ffd706843a45886eb74597856c81592f09aa3443 Mon Sep 17 00:00:00 2001 From: Chris Heaney Date: Wed, 14 May 2025 14:05:52 -0400 Subject: [PATCH 11/13] align naming --- programs/drift/src/controller/liquidation.rs | 14 +++++++------- programs/drift/src/controller/orders.rs | 12 ++++++------ programs/drift/src/instructions/user.rs | 10 +++++----- programs/drift/src/math/orders.rs | 2 +- programs/drift/src/state/user.rs | 2 +- 5 files changed, 20 insertions(+), 20 deletions(-) diff --git a/programs/drift/src/controller/liquidation.rs b/programs/drift/src/controller/liquidation.rs index f918e352a1..dcc3fe4e1e 100644 --- a/programs/drift/src/controller/liquidation.rs +++ b/programs/drift/src/controller/liquidation.rs @@ -42,7 +42,7 @@ use crate::math::margin::{ }; use crate::math::oracle::DriftAction; use crate::math::orders::{ - calculate_existing_position_fields_for_order_action_record, get_position_delta_for_fill, is_multiple_of_step_size, + calculate_existing_position_fields_for_order_action, get_position_delta_for_fill, is_multiple_of_step_size, is_oracle_too_divergent_with_twap_5min, standardize_base_asset_amount, standardize_base_asset_amount_ceil, }; @@ -508,7 +508,7 @@ pub fn liquidate_perp( let user_existing_position_direction = user_position.get_direction(); let user_position_direction_to_close = user_position.get_direction_to_close(); let user_existing_position_params = user_position - .get_existing_position_params_for_order_action_record(user_position_direction_to_close); + .get_existing_position_params_for_order_action(user_position_direction_to_close); update_position_and_market(user_position, &mut market, &user_position_delta)?; update_quote_asset_and_break_even_amount(user_position, &mut market, liquidator_fee)?; update_quote_asset_and_break_even_amount(user_position, &mut market, if_fee)?; @@ -526,8 +526,8 @@ pub fn liquidate_perp( let liquidator_position = liquidator.force_get_perp_position_mut(market_index)?; let liquidator_existing_position_direction = liquidator_position.get_direction(); - let liquidator_quote_entry_amount_params = liquidator_position - .get_existing_position_params_for_order_action_record(user_existing_position_direction); + let liquidator_existing_position_params = liquidator_position + .get_existing_position_params_for_order_action(user_existing_position_direction); update_position_and_market(liquidator_position, &mut market, &liquidator_position_delta)?; update_quote_asset_and_break_even_amount( liquidator_position, @@ -556,7 +556,7 @@ pub fn liquidate_perp( user_position_direction_to_close, user_existing_position_params, liquidator_existing_position_direction, - liquidator_quote_entry_amount_params, + liquidator_existing_position_params, ) }; @@ -642,10 +642,10 @@ pub fn liquidate_perp( }); let (taker_existing_quote_entry_amount, taker_existing_base_asset_amount) = - calculate_existing_position_fields_for_order_action_record(base_asset_amount, user_existing_position_params_for_order_action)?; + calculate_existing_position_fields_for_order_action(base_asset_amount, user_existing_position_params_for_order_action)?; let (maker_existing_quote_entry_amount, maker_existing_base_asset_amount) = - calculate_existing_position_fields_for_order_action_record(base_asset_amount, liquidator_existing_position_params_for_order_action)?; + calculate_existing_position_fields_for_order_action(base_asset_amount, liquidator_existing_position_params_for_order_action)?; let fill_record = OrderActionRecord { ts: now, diff --git a/programs/drift/src/controller/orders.rs b/programs/drift/src/controller/orders.rs index f5847030d8..071dce2aad 100644 --- a/programs/drift/src/controller/orders.rs +++ b/programs/drift/src/controller/orders.rs @@ -2174,7 +2174,7 @@ pub fn fulfill_perp_order_with_amm( validation::perp_market::validate_amm_account_for_fill(&market.amm, order_direction)?; let existing_position_params_for_order_action = user.perp_positions[position_index] - .get_existing_position_params_for_order_action_record(order_direction); + .get_existing_position_params_for_order_action(order_direction); let market_side_price = match order_direction { PositionDirection::Long => market.amm.ask_price(reserve_price_before)?, @@ -2368,7 +2368,7 @@ pub fn fulfill_perp_order_with_amm( ); let (taker_existing_quote_entry_amount, taker_existing_base_asset_amount, maker_existing_quote_entry_amount, maker_existing_base_asset_amount) = { let (existing_quote_entry_amount, existing_base_asset_amount) = - calculate_existing_position_fields_for_order_action_record(base_asset_amount, existing_position_params_for_order_action)?; + calculate_existing_position_fields_for_order_action(base_asset_amount, existing_position_params_for_order_action)?; if taker.is_some() { (existing_quote_entry_amount, existing_base_asset_amount, None, None) } else { @@ -2509,7 +2509,7 @@ pub fn fulfill_perp_order_with_match( ( maker_position.base_asset_amount, - maker_position.get_existing_position_params_for_order_action_record(maker_direction), + maker_position.get_existing_position_params_for_order_action(maker_direction), ) }; let maker_base_asset_amount = maker.orders[maker_order_index] @@ -2598,7 +2598,7 @@ pub fn fulfill_perp_order_with_match( ( taker_position.base_asset_amount, - taker_position.get_existing_position_params_for_order_action_record(taker_direction), + taker_position.get_existing_position_params_for_order_action(taker_direction), ) }; @@ -2814,11 +2814,11 @@ pub fn fulfill_perp_order_with_match( order_action_bit_flags, taker.orders[taker_order_index].is_signed_msg(), ); - let (taker_existing_quote_entry_amount, taker_existing_base_asset_amount) = calculate_existing_position_fields_for_order_action_record( + let (taker_existing_quote_entry_amount, taker_existing_base_asset_amount) = calculate_existing_position_fields_for_order_action( base_asset_amount_fulfilled_by_maker, taker_existing_position_params_for_order_action, )?; - let (maker_existing_quote_entry_amount, maker_existing_base_asset_amount) = calculate_existing_position_fields_for_order_action_record( + let (maker_existing_quote_entry_amount, maker_existing_base_asset_amount) = calculate_existing_position_fields_for_order_action( base_asset_amount_fulfilled_by_maker, maker_existing_position_params_for_order_action, )?; diff --git a/programs/drift/src/instructions/user.rs b/programs/drift/src/instructions/user.rs index 35961cc88b..db1b5657ca 100644 --- a/programs/drift/src/instructions/user.rs +++ b/programs/drift/src/instructions/user.rs @@ -40,7 +40,7 @@ use crate::math::margin::{ }; use crate::math::oracle::is_oracle_valid_for_action; use crate::math::oracle::DriftAction; -use crate::math::orders::calculate_existing_position_fields_for_order_action_record; +use crate::math::orders::calculate_existing_position_fields_for_order_action; use crate::math::orders::get_position_delta_for_fill; use crate::math::orders::is_multiple_of_step_size; use crate::math::orders::standardize_price_i64; @@ -1713,19 +1713,19 @@ pub fn handle_transfer_perp_position<'c: 'info, 'info>( let from_user_position = from_user.force_get_perp_position_mut(market_index)?; - let (from_existing_quote_entry_amount, from_existing_base_asset_amount) = calculate_existing_position_fields_for_order_action_record( + let (from_existing_quote_entry_amount, from_existing_base_asset_amount) = calculate_existing_position_fields_for_order_action( transfer_amount_abs, - from_user_position.get_existing_position_params_for_order_action_record(direction_to_close), + from_user_position.get_existing_position_params_for_order_action(direction_to_close), )?; update_position_and_market(from_user_position, &mut market, &from_user_position_delta)?; let to_user_position = to_user.force_get_perp_position_mut(market_index)?; - let (to_existing_quote_entry_amount, to_existing_base_asset_amount) = calculate_existing_position_fields_for_order_action_record( + let (to_existing_quote_entry_amount, to_existing_base_asset_amount) = calculate_existing_position_fields_for_order_action( transfer_amount_abs, to_user_position - .get_existing_position_params_for_order_action_record(direction_to_close.opposite()), + .get_existing_position_params_for_order_action(direction_to_close.opposite()), )?; update_position_and_market(to_user_position, &mut market, &to_user_position_delta)?; diff --git a/programs/drift/src/math/orders.rs b/programs/drift/src/math/orders.rs index 736a7ea330..13114c94e8 100644 --- a/programs/drift/src/math/orders.rs +++ b/programs/drift/src/math/orders.rs @@ -1409,7 +1409,7 @@ pub fn set_is_signed_msg_flag(mut flags: u8, value: bool) -> u8 { flags } -pub fn calculate_existing_position_fields_for_order_action_record( +pub fn calculate_existing_position_fields_for_order_action( base_asset_amount_filled: u64, existing_posiiton_params: Option<(u64, u64)>, ) -> DriftResult<(Option, Option)> { diff --git a/programs/drift/src/state/user.rs b/programs/drift/src/state/user.rs index a6193d15dc..8fb8474e92 100644 --- a/programs/drift/src/state/user.rs +++ b/programs/drift/src/state/user.rs @@ -1242,7 +1242,7 @@ impl PerpPosition { } } - pub fn get_existing_position_params_for_order_action_record( + pub fn get_existing_position_params_for_order_action( &self, fill_direction: PositionDirection, ) -> Option<(u64, u64)> { From 550eb8d4609946859f32b6f1032ae0629f643cac Mon Sep 17 00:00:00 2001 From: Chris Heaney Date: Thu, 5 Jun 2025 15:50:33 -0400 Subject: [PATCH 12/13] fix typo --- programs/drift/src/math/orders.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/programs/drift/src/math/orders.rs b/programs/drift/src/math/orders.rs index 13114c94e8..4cbd912a29 100644 --- a/programs/drift/src/math/orders.rs +++ b/programs/drift/src/math/orders.rs @@ -1411,9 +1411,9 @@ pub fn set_is_signed_msg_flag(mut flags: u8, value: bool) -> u8 { pub fn calculate_existing_position_fields_for_order_action( base_asset_amount_filled: u64, - existing_posiiton_params: Option<(u64, u64)>, + existing_position_params: Option<(u64, u64)>, ) -> DriftResult<(Option, Option)> { - if let Some((quote_entry_amount, base_asset_amount)) = existing_posiiton_params { + if let Some((quote_entry_amount, base_asset_amount)) = existing_position_params { if base_asset_amount == 0 { return Ok((None, None)); } From 3921345623b6bf21290480b705a1a8ff4340eecd Mon Sep 17 00:00:00 2001 From: Chris Heaney Date: Thu, 5 Jun 2025 15:53:15 -0400 Subject: [PATCH 13/13] CHANGELOG --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e0a1b09388..ce528214ec 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixes - sdk: fix to getMaxTradeSizeUSDCForPerp which was previously overshooting max allowed size due to IMF factor +- program: add existing position fields to order records ([#1614](https://github.com/drift-labs/protocol-v2/pull/1614)) ### Breaking