Skip to content

Commit

Permalink
Merge branch 'master' into luke/dpe-1738-priority-fee-improvements
Browse files Browse the repository at this point in the history
  • Loading branch information
lukecaan committed Dec 31, 2023
2 parents 9fddc2e + fc70257 commit aa9eb29
Show file tree
Hide file tree
Showing 14 changed files with 606 additions and 48 deletions.
4 changes: 3 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Fixes

- sdk: use tx params passed into deposit function
- program: improve get_fallback_price([#797](https://github.com/drift-labs/protocol-v2/pull/797))
- program: derive auction for crossing limit with no duration ([#802](https://github.com/drift-labs/protocol-v2/pull/802))
- sdk: use tx params passed into deposit and withdraw functions

### Breaking

Expand Down
48 changes: 31 additions & 17 deletions programs/drift/src/controller/orders.rs
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ pub fn place_perp_order(
spot_market_map: &SpotMarketMap,
oracle_map: &mut OracleMap,
clock: &Clock,
params: OrderParams,
mut params: OrderParams,
mut options: PlaceOrderOptions,
) -> DriftResult {
let now = clock.unix_timestamp;
Expand Down Expand Up @@ -220,6 +220,10 @@ pub fn place_perp_order(
};

let oracle_price_data = oracle_map.get_price_data(&market.amm.oracle)?;

// updates auction params for crossing limit orders w/out auction duration
params.update_perp_auction_params(market, oracle_price_data.price)?;

let (auction_start_price, auction_end_price, auction_duration) = get_auction_params(
&params,
oracle_price_data,
Expand Down Expand Up @@ -407,7 +411,16 @@ fn get_auction_params(
// if auction is non-zero, force it to be at least min_auction_duration
auction_duration.max(min_auction_duration)
};
Ok((auction_start_price, auction_end_price, auction_duration))

Ok((
standardize_price_i64(
auction_start_price,
tick_size.cast()?,
params.direction,
)?,
standardize_price_i64(auction_end_price, tick_size.cast()?, params.direction)?,
auction_duration,
))
}
_ => Ok((0_i64, 0_i64, 0_u8)),
};
Expand Down Expand Up @@ -813,15 +826,19 @@ fn merge_modify_order_params_with_existing_order(
let oracle_price_offset = modify_order_params
.oracle_price_offset
.or(Some(existing_order.oracle_price_offset));
let auction_duration = modify_order_params
.auction_duration
.or(Some(existing_order.auction_duration));
let auction_start_price = modify_order_params
.auction_start_price
.or(Some(existing_order.auction_start_price));
let auction_end_price = modify_order_params
.auction_end_price
.or(Some(existing_order.auction_end_price));
let (auction_duration, auction_start_price, auction_end_price) =
if modify_order_params.auction_duration.is_some()
&& modify_order_params.auction_start_price.is_some()
&& modify_order_params.auction_end_price.is_some()
{
(
modify_order_params.auction_duration,
modify_order_params.auction_start_price,
modify_order_params.auction_end_price,
)
} else {
(None, None, None)
};

Ok(OrderParams {
order_type,
Expand Down Expand Up @@ -2090,22 +2107,19 @@ pub fn fulfill_perp_order_with_match(
return Ok((0_u64, 0_u64, 0_u64));
}

let (bid_price, ask_price) = market.amm.bid_ask_price(market.amm.reserve_price()?)?;

let oracle_price = oracle_map.get_price_data(&market.amm.oracle)?.price;
let taker_direction = taker.orders[taker_order_index].direction;
let taker_direction: PositionDirection = taker.orders[taker_order_index].direction;

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(
market.amm.get_fallback_price(
&taker_direction,
bid_price,
ask_price,
amm_available_liquidity,
oracle_price,
taker.orders[taker_order_index].seconds_til_expiry(now),
)?
};

Expand Down
20 changes: 0 additions & 20 deletions programs/drift/src/math/orders.rs
Original file line number Diff line number Diff line change
Expand Up @@ -693,26 +693,6 @@ pub fn calculate_fill_price(
.cast::<u64>()
}

pub fn get_fallback_price(
direction: &PositionDirection,
bid_price: u64,
ask_price: u64,
amm_available_liquidity: u64,
oracle_price: i64,
) -> DriftResult<u64> {
let oracle_price = oracle_price.unsigned_abs();
match direction {
PositionDirection::Long if amm_available_liquidity > 0 => {
ask_price.safe_add(ask_price / 200)
}
PositionDirection::Long => oracle_price.safe_add(oracle_price / 20),
PositionDirection::Short if amm_available_liquidity > 0 => {
bid_price.safe_sub(bid_price / 200)
}
PositionDirection::Short => oracle_price.safe_sub(oracle_price / 20),
}
}

pub fn get_max_fill_amounts(
user: &User,
user_order_index: usize,
Expand Down
143 changes: 143 additions & 0 deletions programs/drift/src/math/orders/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3718,3 +3718,146 @@ mod select_margin_type_for_perp_maker {
assert_eq!(margin_type, MarginRequirementType::Maintenance);
}
}

mod fallback_price_logic {
use crate::math::constants::{
AMM_RESERVE_PRECISION, PEG_PRECISION, PRICE_PRECISION, PRICE_PRECISION_I64,
};
use crate::state::oracle::HistoricalOracleData;
use crate::state::perp_market::{PerpMarket, AMM};
use crate::{MarketStatus, PositionDirection};

#[test]
fn test() {
let mut market = PerpMarket {
amm: AMM {
base_asset_reserve: 100 * AMM_RESERVE_PRECISION,
quote_asset_reserve: 100 * AMM_RESERVE_PRECISION,
bid_base_asset_reserve: 100 * AMM_RESERVE_PRECISION,
bid_quote_asset_reserve: 100 * AMM_RESERVE_PRECISION,
ask_base_asset_reserve: 100 * AMM_RESERVE_PRECISION,
ask_quote_asset_reserve: 100 * 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: 1000,
order_tick_size: 1,
min_order_size: 1000,
// oracle: oracle_price_key,
base_spread: 0,
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: 2000,
margin_ratio_maintenance: 1000,
status: MarketStatus::Initialized,
..PerpMarket::default_test()
};
market.amm.max_base_asset_reserve = u128::MAX;
market.amm.min_base_asset_reserve = 0;

// fallback are wide from oracle cause twaps arent set on amm
let result = market
.amm
.get_fallback_price(
&PositionDirection::Long,
0,
2012 * PRICE_PRECISION_I64 / 100,
0,
)
.unwrap();
assert_eq!(result, 22132000);

let result = market
.amm
.get_fallback_price(
&PositionDirection::Short,
0,
2012 * PRICE_PRECISION_I64 / 100,
0,
)
.unwrap();
assert_eq!(result, 0);

// make non-zero bid/ask twaps
market.amm.last_ask_price_twap = (101 * PRICE_PRECISION) as u64;
market.amm.last_bid_price_twap = (99 * PRICE_PRECISION) as u64;

// fallback is offset from oracle
let result = market
.amm
.get_fallback_price(
&PositionDirection::Long,
0,
2012 * PRICE_PRECISION_I64 / 100,
0,
)
.unwrap();
assert_eq!(result, 23132000);

let result = market
.amm
.get_fallback_price(
&PositionDirection::Short,
0,
2012 * PRICE_PRECISION_I64 / 100,
0,
)
.unwrap();
assert_eq!(result, 17108000);

// ignores current oracle price and just prices fallback based on amm liquidity
let result = market
.amm
.get_fallback_price(
&PositionDirection::Long,
1000000000,
2012 * PRICE_PRECISION_I64 / 100,
0,
)
.unwrap();
assert_eq!(result, 101000000);

let result = market
.amm
.get_fallback_price(
&PositionDirection::Short,
1000000000,
2012 * PRICE_PRECISION_I64 / 100,
0,
)
.unwrap();
assert_eq!(result, 99000000);

// ignores current oracle price and just prices fallback based on amm liquidity
// tighter when seconds til expiry is long
let result = market
.amm
.get_fallback_price(
&PositionDirection::Long,
1000000000,
2012 * PRICE_PRECISION_I64 / 100,
100,
)
.unwrap();
assert_eq!(result, 100500000);

let result = market
.amm
.get_fallback_price(
&PositionDirection::Short,
1000000000,
2012 * PRICE_PRECISION_I64 / 100,
100,
)
.unwrap();
assert_eq!(result, 99500000);
}
}
81 changes: 80 additions & 1 deletion programs/drift/src/state/order_params.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,18 @@
use crate::controller::position::PositionDirection;
use crate::error::DriftResult;
use crate::math::casting::Cast;
use crate::math::safe_math::SafeMath;
use crate::state::perp_market::PerpMarket;
use crate::state::user::{MarketType, OrderTriggerCondition, OrderType};
use crate::PERCENTAGE_PRECISION_U64;
use anchor_lang::prelude::*;
use borsh::{BorshDeserialize, BorshSerialize};
use std::ops::Div;

#[derive(AnchorSerialize, AnchorDeserialize, Clone, Default, Copy)]
#[cfg(test)]
mod tests;

#[derive(AnchorSerialize, AnchorDeserialize, Clone, Default, Copy, Eq, PartialEq, Debug)]
pub struct OrderParams {
pub order_type: OrderType,
pub market_type: MarketType,
Expand All @@ -24,6 +33,76 @@ pub struct OrderParams {
pub auction_end_price: Option<i64>,
}

impl OrderParams {
pub fn update_perp_auction_params(
&mut self,
perp_market: &PerpMarket,
oracle_price: i64,
) -> DriftResult {
if self.order_type != OrderType::Limit {
return Ok(());
}

if self.auction_duration.is_some() {
return Ok(());
}

if self.post_only != PostOnlyParam::None {
return Ok(());
}

if self.immediate_or_cancel {
return Ok(());
}

if self.oracle_price_offset.is_some() || self.price == 0 {
return Ok(());
}

match self.direction {
PositionDirection::Long => {
let ask_premium = perp_market.amm.last_ask_premium()?;
let est_ask = oracle_price.safe_add(ask_premium)?.cast()?;
if self.price > est_ask {
let auction_duration =
get_auction_duration(self.price.safe_sub(est_ask)?, est_ask)?;
let auction_start_price = est_ask as i64;
let auction_end_price = self.price as i64;
msg!("derived auction params for limit order. duration = {} start_price = {} end_price = {}", auction_duration, auction_start_price, auction_end_price);
self.auction_duration = Some(auction_duration);
self.auction_start_price = Some(auction_start_price);
self.auction_end_price = Some(auction_end_price);
}
}
PositionDirection::Short => {
let bid_discount = perp_market.amm.last_bid_discount()?;
let est_bid = oracle_price.safe_sub(bid_discount)?.cast()?;
if self.price < est_bid {
let auction_duration =
get_auction_duration(est_bid.safe_sub(self.price)?, est_bid)?;
let auction_start_price = est_bid as i64;
let auction_end_price = self.price as i64;
msg!("derived auction params for limit order. duration = {} start_price = {} end_price = {}", auction_duration, auction_start_price, auction_end_price);
self.auction_duration = Some(auction_duration);
self.auction_start_price = Some(auction_start_price);
self.auction_end_price = Some(auction_end_price);
}
}
}

Ok(())
}
}

fn get_auction_duration(price_diff: u64, price: u64) -> DriftResult<u8> {
let percent_diff = price_diff.safe_mul(PERCENTAGE_PRECISION_U64)?.div(price);

Ok(percent_diff
.safe_mul(60)?
.safe_div_ceil(PERCENTAGE_PRECISION_U64 / 100)? // 1% = 60 seconds
.clamp(10, 60) as u8)
}

#[derive(Clone, Copy, BorshSerialize, BorshDeserialize, PartialEq, Debug, Eq)]
pub enum PostOnlyParam {
None,
Expand Down
Loading

0 comments on commit aa9eb29

Please sign in to comment.