Skip to content

Commit

Permalink
program: protect maker oracle limit orders (#1351)
Browse files Browse the repository at this point in the history
* program: protect maker oracle limit orders

* add protected maker flag

* use maker flag in get makers order info

* add protected maker mode config

* look at config in ix to turn on mode

* fix taker_order_age

* use state min perp auction duration

* more strict logic and fix tests

* add test

* add protected maker to dlob

* smol tweak and formmating

* let hot wallet init config

* fix build

* cargo fmt
  • Loading branch information
crispheaney authored Dec 5, 2024
1 parent c2a8aa1 commit 3670516
Show file tree
Hide file tree
Showing 16 changed files with 351 additions and 34 deletions.
68 changes: 54 additions & 14 deletions programs/drift/src/controller/orders.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1180,11 +1180,13 @@ pub fn fill_perp_order(
let reserve_price_before: u64;
let oracle_validity: OracleValidity;
let oracle_price: i64;
let oracle_delay: i64;
let oracle_twap_5min: i64;
let perp_market_index: u16;
let user_can_skip_duration: bool;
let amm_can_skip_duration: bool;
let amm_lp_allowed_to_jit_make: bool;
let oracle_valid_for_amm_fill: bool;

let mut amm_is_available = !state.amm_paused()? && !fill_mode.is_rfq();
{
Expand All @@ -1204,8 +1206,10 @@ pub fn fill_perp_order(
market.get_max_confidence_interval_multiplier()?,
)?;

amm_is_available &=
oracle_valid_for_amm_fill =
is_oracle_valid_for_action(_oracle_validity, Some(DriftAction::FillOrderAmm))?;

amm_is_available &= oracle_valid_for_amm_fill;
amm_is_available &= !market.is_operation_paused(PerpOperation::AmmFill);
amm_is_available &= !market.has_too_much_drawdown()?;

Expand All @@ -1216,19 +1220,15 @@ pub fn fill_perp_order(
amm_can_skip_duration =
market.can_skip_auction_duration(&state, amm_lp_allowed_to_jit_make)?;

user_can_skip_duration = if amm_can_skip_duration && amm_is_available {
user.can_skip_auction_duration(
user_stats,
order_auction_duration > 0,
fill_mode.is_ioc(),
order_direction,
order_price,
order_oracle_price_offset,
oracle_price_data.price,
)?
} else {
false
};
user_can_skip_duration = user.can_skip_auction_duration(
user_stats,
order_auction_duration > 0,
fill_mode.is_ioc(),
order_direction,
order_price,
order_oracle_price_offset,
oracle_price_data.price,
)?;

reserve_price_before = market.amm.reserve_price()?;
oracle_price = oracle_price_data.price;
Expand All @@ -1237,6 +1237,7 @@ pub fn fill_perp_order(
.historical_oracle_data
.last_oracle_price_twap_5min;
oracle_validity = _oracle_validity;
oracle_delay = oracle_price_data.delay;
perp_market_index = market.market_index;
}

Expand Down Expand Up @@ -1285,6 +1286,10 @@ pub fn fill_perp_order(
now,
slot,
fill_mode,
oracle_valid_for_amm_fill,
oracle_delay,
user_can_skip_duration,
state.min_perp_auction_duration as u64,
)?;

// no referrer bonus for liquidations
Expand Down Expand Up @@ -1588,11 +1593,17 @@ fn get_maker_orders_info(
now: i64,
slot: u64,
fill_mode: FillMode,
oracle_valid_for_amm_fill: bool,
oracle_delay: i64,
user_can_skip_duration: bool,
protected_maker_min_age: u64,
) -> DriftResult<Vec<(Pubkey, usize, u64)>> {
let maker_direction = taker_order.direction.opposite();

let mut maker_orders_info = Vec::with_capacity(16);

let taker_order_age = slot.safe_sub(taker_order.slot)?;

for (maker_key, user_account_loader) in makers_and_referrer.0.iter() {
if maker_key == taker_key {
continue;
Expand Down Expand Up @@ -1629,6 +1640,8 @@ fn get_maker_orders_info(

drop(market);

let is_protected_maker = maker.is_protected_maker();

for (maker_order_index, maker_order_price) in maker_order_price_and_indexes.iter() {
let maker_order_index = *maker_order_index;
let maker_order_price = *maker_order_price;
Expand Down Expand Up @@ -1711,6 +1724,19 @@ fn get_maker_orders_info(
continue;
}

if is_protected_maker && maker_order.has_oracle_price_offset() {
if !protected_maker_oracle_limit_can_fill(
oracle_valid_for_amm_fill,
oracle_delay,
user_can_skip_duration,
taker_order_age,
protected_maker_min_age,
) {
msg!("Protected maker oracle limit cannot fill. oracle delay = {}, user can skip duration = {}, taker order age = {}", oracle_delay, user_can_skip_duration, taker_order_age);
continue;
}
}

insert_maker_order_info(
&mut maker_orders_info,
(*maker_key, maker_order_index, maker_order_price),
Expand All @@ -1722,6 +1748,20 @@ fn get_maker_orders_info(
Ok(maker_orders_info)
}

#[inline(always)]
fn protected_maker_oracle_limit_can_fill(
oracle_valid_for_amm_fill: bool,
oracle_delay: i64,
user_can_skip_duration: bool,
taker_order_age: u64,
protected_maker_min_age: u64,
) -> bool {
oracle_valid_for_amm_fill
&& (oracle_delay == 0
|| user_can_skip_duration
|| taker_order_age > protected_maker_min_age)
}

#[inline(always)]
fn insert_maker_order_info(
maker_orders_info: &mut Vec<(Pubkey, usize, u64)>,
Expand Down
42 changes: 42 additions & 0 deletions programs/drift/src/controller/orders/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9992,6 +9992,10 @@ pub mod get_maker_orders_info {
clock.unix_timestamp,
clock.slot,
FillMode::Fill,
true,
0,
true,
10,
)
.unwrap();

Expand Down Expand Up @@ -10182,6 +10186,10 @@ pub mod get_maker_orders_info {
clock.unix_timestamp,
clock.slot,
FillMode::Fill,
true,
0,
true,
10,
)
.unwrap();

Expand Down Expand Up @@ -10361,6 +10369,10 @@ pub mod get_maker_orders_info {
clock.unix_timestamp,
clock.slot,
FillMode::Fill,
true,
0,
true,
10,
)
.unwrap();

Expand Down Expand Up @@ -10603,6 +10615,10 @@ pub mod get_maker_orders_info {
clock.unix_timestamp,
clock.slot,
FillMode::Fill,
true,
0,
true,
10,
)
.unwrap();

Expand Down Expand Up @@ -10800,6 +10816,10 @@ pub mod get_maker_orders_info {
clock.unix_timestamp,
clock.slot,
FillMode::PlaceAndTake(false, 0),
true,
0,
true,
10,
)
.unwrap();

Expand Down Expand Up @@ -11019,6 +11039,10 @@ pub mod get_maker_orders_info {
clock.unix_timestamp,
clock.slot,
FillMode::Fill,
true,
0,
true,
10,
)
.unwrap();

Expand Down Expand Up @@ -12217,3 +12241,21 @@ mod update_maker_fills_map {
assert_eq!(*map.get(&maker_key).unwrap(), -2 * fill as i64);
}
}

pub mod protected_maker_oracle_limit_can_fill {
use crate::controller::orders::protected_maker_oracle_limit_can_fill;

#[test]
fn test() {
assert!(protected_maker_oracle_limit_can_fill(true, 0, true, 10, 10)); // all cases
assert!(protected_maker_oracle_limit_can_fill(true, 0, false, 9, 10)); // oracle delay is 0
assert!(protected_maker_oracle_limit_can_fill(true, 1, false, 10, 9)); // min age passed
assert!(protected_maker_oracle_limit_can_fill(true, 1, true, 9, 10)); // user exempt
assert!(!protected_maker_oracle_limit_can_fill(
true, 1, false, 10, 11
)); // no condition met
assert!(!protected_maker_oracle_limit_can_fill(
false, 0, true, 10, 10
)); // oracle valid for amm fill is false
}
}
2 changes: 2 additions & 0 deletions programs/drift/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -611,6 +611,8 @@ pub enum ErrorCode {
InvalidSwiftOrderId,
#[msg("Invalid pool id")]
InvalidPoolId,
#[msg("Invalid Protected Maker Mode Config")]
InvalidProtectedMakerModeConfig,
}

#[macro_export]
Expand Down
66 changes: 66 additions & 0 deletions programs/drift/src/instructions/admin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ use crate::state::perp_market::{
ContractTier, ContractType, InsuranceClaim, MarketStatus, PerpMarket, PoolBalance, AMM,
};
use crate::state::perp_market_map::get_writable_perp_market_set;
use crate::state::protected_maker_mode_config::ProtectedMakerModeConfig;
use crate::state::spot_market::{
AssetTier, InsuranceFund, SpotBalanceType, SpotFulfillmentConfigStatus, SpotMarket,
};
Expand Down Expand Up @@ -4198,6 +4199,32 @@ pub fn handle_update_high_leverage_mode_config(
Ok(())
}

pub fn handle_initialize_protected_maker_mode_config(
ctx: Context<InitializeProtectedMakerModeConfig>,
max_users: u32,
) -> Result<()> {
let mut config = ctx.accounts.protected_maker_mode_config.load_init()?;

config.max_users = max_users;

Ok(())
}

pub fn handle_update_protected_maker_mode_config(
ctx: Context<UpdateProtectedMakerModeConfig>,
max_users: u32,
reduce_only: bool,
) -> Result<()> {
let mut config = load_mut!(ctx.accounts.protected_maker_mode_config)?;

config.max_users = max_users;
config.reduce_only = reduce_only as u8;

config.validate()?;

Ok(())
}

#[derive(Accounts)]
pub struct Initialize<'info> {
#[account(mut)]
Expand Down Expand Up @@ -4848,3 +4875,42 @@ pub struct UpdateHighLeverageModeConfig<'info> {
)]
pub state: Box<Account<'info, State>>,
}

#[derive(Accounts)]
pub struct InitializeProtectedMakerModeConfig<'info> {
#[account(
mut,
constraint = admin.key() == admin_hot_wallet::id() || admin.key() == state.admin
)]
pub admin: Signer<'info>,
#[account(
init,
seeds = [b"protected_maker_mode_config".as_ref()],
space = ProtectedMakerModeConfig::SIZE,
bump,
payer = admin
)]
pub protected_maker_mode_config: AccountLoader<'info, ProtectedMakerModeConfig>,
#[account(
has_one = admin
)]
pub state: Box<Account<'info, State>>,
pub rent: Sysvar<'info, Rent>,
pub system_program: Program<'info, System>,
}

#[derive(Accounts)]
pub struct UpdateProtectedMakerModeConfig<'info> {
#[account(mut)]
pub admin: Signer<'info>,
#[account(
mut,
seeds = [b"protected_maker_mode_config".as_ref()],
bump,
)]
pub protected_maker_mode_config: AccountLoader<'info, ProtectedMakerModeConfig>,
#[account(
has_one = admin
)]
pub state: Box<Account<'info, State>>,
}
Loading

0 comments on commit 3670516

Please sign in to comment.