Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

program: protect maker oracle limit orders #1351

Merged
merged 15 commits into from
Dec 5, 2024
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
Loading