From a7e0f612bb15db7003c248cbdc579252447e820d Mon Sep 17 00:00:00 2001 From: Chris Heaney Date: Wed, 11 Oct 2023 08:19:58 -0400 Subject: [PATCH] init --- programs/drift/src/controller/orders.rs | 58 +++++++++++++---------- programs/drift/src/instructions/keeper.rs | 2 + programs/drift/src/instructions/user.rs | 3 ++ programs/drift/src/math/fulfillment.rs | 8 ++-- programs/drift/src/math/orders.rs | 21 ++++---- programs/drift/src/state/fill_mode.rs | 41 ++++++++++++++++ programs/drift/src/state/mod.rs | 1 + 7 files changed, 91 insertions(+), 43 deletions(-) create mode 100644 programs/drift/src/state/fill_mode.rs diff --git a/programs/drift/src/controller/orders.rs b/programs/drift/src/controller/orders.rs index 9af736891..d552aac1e 100644 --- a/programs/drift/src/controller/orders.rs +++ b/programs/drift/src/controller/orders.rs @@ -57,6 +57,7 @@ use crate::math::spot_swap::select_margin_type_for_swap; use crate::print_error; use crate::state::events::{emit_stack, get_order_action_record, OrderActionRecord, OrderRecord}; use crate::state::events::{OrderAction, OrderActionExplanation}; +use crate::state::fill_mode::FillMode; use crate::state::fulfillment::{PerpFulfillmentMethod, SpotFulfillmentMethod}; use crate::state::margin_calculation::MarginContext; use crate::state::oracle::{OraclePriceData, StrictOraclePrice}; @@ -852,6 +853,7 @@ pub fn fill_perp_order( makers_and_referrer_stats: &UserStatsMap, jit_maker_order_id: Option, clock: &Clock, + fill_mode: FillMode, ) -> DriftResult { let now = clock.unix_timestamp; let slot = clock.slot; @@ -1091,6 +1093,7 @@ pub fn fill_perp_order( slot, state.min_perp_auction_duration, amm_is_available, + fill_mode, )?; if base_asset_amount != 0 { @@ -1475,12 +1478,20 @@ fn fulfill_perp_order( slot: u64, min_auction_duration: u8, amm_is_available: bool, + fill_mode: FillMode, ) -> DriftResult<(u64, u64)> { let market_index = user.orders[user_order_index].market_index; let user_order_position_decreasing = determine_if_user_order_is_position_decreasing(user, market_index, user_order_index)?; + let limit_price = fill_mode.get_limit_price( + &user.orders[user_order_index], + valid_oracle_price, + slot, + perp_market_map.get_ref(&market_index)?.amm.order_tick_size, + )?; + let fulfillment_methods = { let market = perp_market_map.get_ref(&market_index)?; let oracle_price = oracle_map.get_price_data(&market.amm.oracle)?.price; @@ -1491,6 +1502,7 @@ fn fulfill_perp_order( &market.amm, reserve_price_before, Some(oracle_price), + limit_price, amm_is_available, slot, min_auction_duration, @@ -1530,7 +1542,6 @@ fn fulfill_perp_order( reserve_price_before, now, slot, - valid_oracle_price, user_key, filler_key, filler, @@ -1538,6 +1549,7 @@ fn fulfill_perp_order( &mut referrer.as_deref_mut(), &mut referrer_stats.as_deref_mut(), fee_structure, + limit_price, None, *maker_price, AMMLiquiditySplit::Shared, @@ -1578,6 +1590,7 @@ fn fulfill_perp_order( &mut referrer_stats.as_deref_mut(), reserve_price_before, valid_oracle_price, + limit_price, now, slot, fee_structure, @@ -1704,7 +1717,6 @@ pub fn fulfill_perp_order_with_amm( reserve_price_before: u64, now: i64, slot: u64, - valid_oracle_price: Option, user_key: &Pubkey, filler_key: &Pubkey, filler: &mut Option<&mut User>, @@ -1712,6 +1724,7 @@ pub fn fulfill_perp_order_with_amm( referrer: &mut Option<&mut User>, referrer_stats: &mut Option<&mut UserStats>, fee_structure: &FeeStructure, + limit_price: Option, override_base_asset_amount: Option, override_fill_price: Option, liquidity_split: AMMLiquiditySplit, @@ -1721,13 +1734,6 @@ pub fn fulfill_perp_order_with_amm( // Determine the base asset amount the market can fill let (base_asset_amount, limit_price, fill_price) = match override_base_asset_amount { Some(override_base_asset_amount) => { - let limit_price = user.orders[order_index].get_limit_price( - valid_oracle_price, - None, - slot, - market.amm.order_tick_size, - )?; - (override_base_asset_amount, limit_price, override_fill_price) } None => { @@ -1735,8 +1741,7 @@ pub fn fulfill_perp_order_with_amm( let (base_asset_amount, limit_price) = calculate_base_asset_amount_for_amm_to_fulfill( &user.orders[order_index], market, - valid_oracle_price, - slot, + limit_price, override_fill_price, existing_base_asset_amount, )?; @@ -1987,6 +1992,7 @@ pub fn fulfill_perp_order_with_match( referrer_stats: &mut Option<&mut UserStats>, reserve_price_before: u64, valid_oracle_price: Option, + taker_limit_price: Option, now: i64, slot: u64, fee_structure: &FeeStructure, @@ -2003,20 +2009,20 @@ pub fn fulfill_perp_order_with_match( let oracle_price = oracle_map.get_price_data(&market.amm.oracle)?.price; let taker_direction = taker.orders[taker_order_index].direction; - let amm_available_liquidity = calculate_amm_available_liquidity(&market.amm, &taker_direction)?; - let taker_fallback_price = get_fallback_price( - &taker_direction, - bid_price, - ask_price, - amm_available_liquidity, - oracle_price, - )?; - let taker_price = taker.orders[taker_order_index].force_get_limit_price( - Some(oracle_price), - Some(taker_fallback_price), - slot, - market.amm.order_tick_size, - )?; + + let taker_price = if let Some(taker_limit_price) = taker_limit_price { + taker_limit_price + } else { + let amm_available_liquidity = + calculate_amm_available_liquidity(&market.amm, &taker_direction)?; + get_fallback_price( + &taker_direction, + bid_price, + ask_price, + amm_available_liquidity, + oracle_price, + )? + }; let taker_existing_position = taker .get_perp_position(market.market_index)? @@ -2094,7 +2100,6 @@ pub fn fulfill_perp_order_with_match( reserve_price_before, now, slot, - valid_oracle_price, taker_key, filler_key, filler, @@ -2102,6 +2107,7 @@ pub fn fulfill_perp_order_with_match( &mut None, &mut None, fee_structure, + taker_limit_price, Some(jit_base_asset_amount), Some(maker_price), // match the makers price amm_liquidity_split, diff --git a/programs/drift/src/instructions/keeper.rs b/programs/drift/src/instructions/keeper.rs index 6e1cb6d12..2b58160c4 100644 --- a/programs/drift/src/instructions/keeper.rs +++ b/programs/drift/src/instructions/keeper.rs @@ -10,6 +10,7 @@ use crate::math::constants::QUOTE_SPOT_MARKET_INDEX; use crate::math::insurance::if_shares_to_vault_amount; use crate::math::orders::{estimate_price_from_side, find_bids_and_asks_from_users}; use crate::math::spot_withdraw::validate_spot_market_vault_amount; +use crate::state::fill_mode::FillMode; use crate::state::fulfillment_params::drift::MatchFulfillmentParams; use crate::state::fulfillment_params::phoenix::PhoenixFulfillmentParams; use crate::state::fulfillment_params::serum::SerumFulfillmentParams; @@ -107,6 +108,7 @@ fn fill_order(ctx: Context, order_id: u32, market_index: u16) -> Resu &makers_and_referrer_stats, None, clock, + FillMode::Fill, )?; Ok(()) diff --git a/programs/drift/src/instructions/user.rs b/programs/drift/src/instructions/user.rs index a23e5314b..68046b7f1 100644 --- a/programs/drift/src/instructions/user.rs +++ b/programs/drift/src/instructions/user.rs @@ -39,6 +39,7 @@ use crate::state::events::{ DepositDirection, DepositExplanation, DepositRecord, LPAction, LPRecord, NewUserRecord, OrderActionExplanation, SwapRecord, }; +use crate::state::fill_mode::FillMode; use crate::state::fulfillment_params::drift::MatchFulfillmentParams; use crate::state::fulfillment_params::phoenix::PhoenixFulfillmentParams; use crate::state::fulfillment_params::serum::SerumFulfillmentParams; @@ -1225,6 +1226,7 @@ pub fn handle_place_and_take_perp_order<'info>( &makers_and_referrer_stats, None, &Clock::get()?, + FillMode::PlaceAndTake, )?; let order_exists = load!(ctx.accounts.user)? @@ -1322,6 +1324,7 @@ pub fn handle_place_and_make_perp_order<'a, 'b, 'c, 'info>( &makers_and_referrer_stats, Some(order_id), clock, + FillMode::PlaceAndMake, )?; let order_exists = load!(ctx.accounts.user)? diff --git a/programs/drift/src/math/fulfillment.rs b/programs/drift/src/math/fulfillment.rs index b9fd43fbb..638257c0c 100644 --- a/programs/drift/src/math/fulfillment.rs +++ b/programs/drift/src/math/fulfillment.rs @@ -16,6 +16,7 @@ pub fn determine_perp_fulfillment_methods( amm: &AMM, amm_reserve_price: u64, valid_oracle_price: Option, + limit_price: Option, amm_is_available: bool, slot: u64, min_auction_duration: u8, @@ -26,15 +27,12 @@ pub fn determine_perp_fulfillment_methods( && valid_oracle_price.is_some() && is_amm_available_liquidity_source(taker_order, min_auction_duration, slot)?; - let taker_price = - taker_order.get_limit_price(valid_oracle_price, None, slot, amm.order_tick_size)?; - let maker_direction = taker_order.direction.opposite(); let (mut amm_bid_price, mut amm_ask_price) = amm.bid_ask_price(amm_reserve_price)?; for (maker_key, maker_order_index, maker_price) in maker_orders_info.iter() { - let taker_crosses_maker = match taker_price { + let taker_crosses_maker = match limit_price { Some(taker_price) => do_orders_cross(maker_direction, *maker_price, taker_price), None => true, }; @@ -75,7 +73,7 @@ pub fn determine_perp_fulfillment_methods( PositionDirection::Short => amm_ask_price, }; - let taker_crosses_maker = match taker_price { + let taker_crosses_maker = match limit_price { Some(taker_price) => do_orders_cross(maker_direction, amm_price, taker_price), None => true, }; diff --git a/programs/drift/src/math/orders.rs b/programs/drift/src/math/orders.rs index a822c4f35..a01b7833c 100644 --- a/programs/drift/src/math/orders.rs +++ b/programs/drift/src/math/orders.rs @@ -44,29 +44,26 @@ mod tests; pub fn calculate_base_asset_amount_for_amm_to_fulfill( order: &Order, market: &PerpMarket, - valid_oracle_price: Option, - slot: u64, - override_limit_price: Option, + limit_price: Option, + override_fill_price: Option, existing_base_asset_amount: i64, ) -> DriftResult<(u64, Option)> { - let limit_price = if let Some(override_limit_price) = override_limit_price { - if let Some(limit_price) = - order.get_limit_price(valid_oracle_price, None, slot, market.amm.order_tick_size)? - { + let limit_price = if let Some(override_fill_price) = override_fill_price { + if let Some(limit_price) = limit_price { validate!( - (limit_price >= override_limit_price && order.direction == PositionDirection::Long) - || (limit_price <= override_limit_price + (limit_price >= override_fill_price && order.direction == PositionDirection::Long) + || (limit_price <= override_fill_price && order.direction == PositionDirection::Short), ErrorCode::InvalidAmmLimitPriceOverride, "override_limit_price={} not better than order_limit_price={}", - override_limit_price, + override_fill_price, limit_price )?; } - Some(override_limit_price) + Some(override_fill_price) } else { - order.get_limit_price(valid_oracle_price, None, slot, market.amm.order_tick_size)? + limit_price }; if order.must_be_triggered() && !order.triggered() { diff --git a/programs/drift/src/state/fill_mode.rs b/programs/drift/src/state/fill_mode.rs new file mode 100644 index 000000000..1a08a8d0b --- /dev/null +++ b/programs/drift/src/state/fill_mode.rs @@ -0,0 +1,41 @@ +use crate::error::DriftResult; +use crate::math::auction::calculate_auction_price; +use crate::math::casting::Cast; +use crate::math::safe_math::SafeMath; +use crate::state::user::Order; + +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub enum FillMode { + Fill, + PlaceAndMake, + PlaceAndTake, +} + +impl FillMode { + pub fn get_limit_price( + &self, + order: &Order, + valid_oracle_price: Option, + slot: u64, + tick_size: u64, + ) -> DriftResult> { + match self { + FillMode::Fill | FillMode::PlaceAndMake => { + order.get_limit_price(valid_oracle_price, None, slot, tick_size) + } + FillMode::PlaceAndTake => { + if order.has_auction() { + calculate_auction_price( + order, + slot.safe_add(order.auction_duration.cast()?)?, + tick_size, + valid_oracle_price, + ) + .map(Some) + } else { + order.get_limit_price(valid_oracle_price, None, slot, tick_size) + } + } + } + } +} diff --git a/programs/drift/src/state/mod.rs b/programs/drift/src/state/mod.rs index 8df0507c4..8a604afcc 100644 --- a/programs/drift/src/state/mod.rs +++ b/programs/drift/src/state/mod.rs @@ -1,4 +1,5 @@ pub mod events; +pub mod fill_mode; pub mod fulfillment; pub mod fulfillment_params; pub mod insurance_fund_stake;