Skip to content

Commit 59a6443

Browse files
authored
program: trigger price based on oracle and basis (#1716)
* init * shrink for time since last funding * add max diff * use trigger price isntead of oracle * add event * more tests * update for contract tier C * add ts code * return early instead of throw error * sdk: rename oraclePrice to triggerPrice * feature flag * styling * address comments * parentheses * casts * CHANGELOG
1 parent 5b064c9 commit 59a6443

File tree

19 files changed

+588
-78
lines changed

19 files changed

+588
-78
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
99

1010
### Features
1111

12+
- program: new median price for trigger orders ([#1716](https://github.com/drift-labs/protocol-v2/pull/1716))
13+
1214
### Fixes
1315

1416
### Breaking

programs/drift/src/controller/funding.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -271,6 +271,7 @@ pub fn update_funding_rate(
271271
.safe_add(funding_rate_short)?;
272272

273273
market.amm.last_funding_rate = funding_rate;
274+
market.amm.last_funding_oracle_twap = oracle_price_twap;
274275
market.amm.last_funding_rate_long = funding_rate_long.cast()?;
275276
market.amm.last_funding_rate_short = funding_rate_short.cast()?;
276277
market.amm.last_24h_avg_funding_rate = calculate_new_twap(

programs/drift/src/controller/liquidation.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -698,6 +698,7 @@ pub fn liquidate_perp(
698698
taker_existing_base_asset_amount: taker_existing_base_asset_amount,
699699
maker_existing_quote_entry_amount: maker_existing_quote_entry_amount,
700700
maker_existing_base_asset_amount: maker_existing_base_asset_amount,
701+
trigger_price: None,
701702
};
702703
emit!(fill_record);
703704

programs/drift/src/controller/orders.rs

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -442,6 +442,7 @@ pub fn place_perp_order(
442442
None,
443443
None,
444444
None,
445+
None,
445446
)?;
446447
emit_stack::<_, { OrderActionRecord::SIZE }>(order_action_record)?;
447448

@@ -721,6 +722,7 @@ pub fn cancel_order(
721722
None,
722723
None,
723724
None,
725+
None,
724726
)?;
725727
emit_stack::<_, { OrderActionRecord::SIZE }>(order_action_record)?;
726728
}
@@ -1324,7 +1326,7 @@ pub fn fill_perp_order(
13241326
let fill_price =
13251327
calculate_fill_price(quote_asset_amount, base_asset_amount, BASE_PRECISION_U64)?;
13261328

1327-
let perp_market = perp_market_map.get_ref(&market_index)?;
1329+
let mut perp_market = perp_market_map.get_ref_mut(&market_index)?;
13281330
validate_fill_price_within_price_bands(
13291331
fill_price,
13301332
oracle_price,
@@ -1335,6 +1337,8 @@ pub fn fill_perp_order(
13351337
.max_oracle_twap_5min_percent_divergence(),
13361338
perp_market.is_prediction_market(),
13371339
)?;
1340+
1341+
perp_market.last_fill_price = fill_price;
13381342
}
13391343

13401344
let base_asset_amount_after = user.perp_positions[position_index].base_asset_amount;
@@ -2449,6 +2453,7 @@ pub fn fulfill_perp_order_with_amm(
24492453
taker_existing_base_asset_amount,
24502454
maker_existing_quote_entry_amount,
24512455
maker_existing_base_asset_amount,
2456+
None,
24522457
)?;
24532458
emit_stack::<_, { OrderActionRecord::SIZE }>(order_action_record)?;
24542459

@@ -2897,6 +2902,7 @@ pub fn fulfill_perp_order_with_match(
28972902
taker_existing_base_asset_amount,
28982903
maker_existing_quote_entry_amount,
28992904
maker_existing_base_asset_amount,
2905+
None,
29002906
)?;
29012907
emit_stack::<_, { OrderActionRecord::SIZE }>(order_action_record)?;
29022908

@@ -2989,11 +2995,10 @@ pub fn trigger_order(
29892995
"Order is not triggerable"
29902996
)?;
29912997

2992-
validate!(
2993-
!user.orders[order_index].triggered(),
2994-
ErrorCode::OrderNotTriggerable,
2995-
"Order is already triggered"
2996-
)?;
2998+
if user.orders[order_index].triggered() {
2999+
msg!("Order is already triggered");
3000+
return Ok(());
3001+
}
29973002

29983003
validate!(
29993004
market_type == MarketType::Perp,
@@ -3049,10 +3054,9 @@ pub fn trigger_order(
30493054
"oracle price vs twap too divergent"
30503055
)?;
30513056

3052-
let can_trigger = order_satisfies_trigger_condition(
3053-
&user.orders[order_index],
3054-
oracle_price.unsigned_abs().cast()?,
3055-
)?;
3057+
let trigger_price =
3058+
perp_market.get_trigger_price(oracle_price, now, state.use_median_trigger_price())?;
3059+
let can_trigger = order_satisfies_trigger_condition(&user.orders[order_index], trigger_price)?;
30563060
validate!(can_trigger, ErrorCode::OrderDidNotSatisfyTriggerCondition)?;
30573061

30583062
let (_, worst_case_liability_value_before) = user
@@ -3125,6 +3129,7 @@ pub fn trigger_order(
31253129
None,
31263130
None,
31273131
None,
3132+
Some(trigger_price),
31283133
)?;
31293134
emit!(order_action_record);
31303135

@@ -3810,6 +3815,7 @@ pub fn place_spot_order(
38103815
None,
38113816
None,
38123817
None,
3818+
None,
38133819
)?;
38143820
emit_stack::<_, { OrderActionRecord::SIZE }>(order_action_record)?;
38153821

@@ -5046,6 +5052,7 @@ pub fn fulfill_spot_order_with_match(
50465052
None,
50475053
None,
50485054
None,
5055+
None,
50495056
)?;
50505057
emit_stack::<_, { OrderActionRecord::SIZE }>(order_action_record)?;
50515058

@@ -5319,6 +5326,7 @@ pub fn fulfill_spot_order_with_external_market(
53195326
None,
53205327
None,
53215328
None,
5329+
None,
53225330
)?;
53235331
emit_stack::<_, { OrderActionRecord::SIZE }>(order_action_record)?;
53245332

@@ -5522,6 +5530,7 @@ pub fn trigger_spot_order(
55225530
None,
55235531
None,
55245532
None,
5533+
Some(oracle_price.unsigned_abs()),
55255534
)?;
55265535

55275536
emit!(order_action_record);

programs/drift/src/instructions/admin.rs

Lines changed: 40 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use std::convert::{identity, TryInto};
22
use std::mem::size_of;
33

4-
use crate::msg;
4+
use crate::{msg, FeatureBitFlags};
55
use anchor_lang::prelude::*;
66
use anchor_spl::token_2022::Token2022;
77
use anchor_spl::token_interface::{Mint, TokenAccount, TokenInterface};
@@ -114,7 +114,7 @@ pub fn handle_initialize(ctx: Context<Initialize>) -> Result<()> {
114114
initial_pct_to_liquidate: 0,
115115
max_number_of_sub_accounts: 0,
116116
max_initialize_user_fee: 0,
117-
disable_bit_flags: 0,
117+
feature_bit_flags: 0,
118118
padding: [0; 9],
119119
};
120120

@@ -965,7 +965,9 @@ pub fn handle_initialize_perp_market(
965965
high_leverage_margin_ratio_maintenance: 0,
966966
protected_maker_limit_price_divisor: 0,
967967
protected_maker_dynamic_divisor: 0,
968-
padding: [0; 36],
968+
padding1: 0,
969+
last_fill_price: 0,
970+
padding: [0; 24],
969971
amm: AMM {
970972
oracle: *ctx.accounts.oracle.key,
971973
oracle_source,
@@ -1061,7 +1063,8 @@ pub fn handle_initialize_perp_market(
10611063
quote_asset_amount_with_unsettled_lp: 0,
10621064
reference_price_offset: 0,
10631065
amm_inventory_spread_adjustment: 0,
1064-
padding: [0; 11],
1066+
padding: [0; 3],
1067+
last_funding_oracle_twap: 0,
10651068
},
10661069
};
10671070

@@ -4853,7 +4856,7 @@ pub fn handle_zero_mm_oracle_fields(ctx: Context<HotAdminUpdatePerpMarket>) -> R
48534856
pub fn handle_update_mm_oracle_native(accounts: &[AccountInfo], data: &[u8]) -> Result<()> {
48544857
// Verify this ix is allowed
48554858
let state = &accounts[3].data.borrow();
4856-
assert!(state[982] & 1 == 0, "ix disabled by admin state");
4859+
assert!(state[982] & 1 > 0, "ix disabled by admin state");
48574860

48584861
let signer_account = &accounts[1];
48594862
#[cfg(not(feature = "anchor-test"))]
@@ -4898,22 +4901,47 @@ pub fn handle_update_amm_spread_adjustment_native(
48984901
Ok(())
48994902
}
49004903

4901-
pub fn handle_update_disable_bitflags_mm_oracle(
4904+
pub fn handle_update_feature_bit_flags_mm_oracle(
49024905
ctx: Context<HotAdminUpdateState>,
4903-
disable: bool,
4906+
enable: bool,
49044907
) -> Result<()> {
49054908
let state = &mut ctx.accounts.state;
4906-
if disable {
4907-
msg!("Setting first bit to 1, disabling mm oracle update");
4908-
state.disable_bit_flags = state.disable_bit_flags | 1;
4909+
if enable {
4910+
validate!(
4911+
ctx.accounts.admin.key().eq(&state.admin),
4912+
ErrorCode::DefaultError,
4913+
"Only state admin can re-enable after kill switch"
4914+
)?;
4915+
4916+
msg!("Setting first bit to 1, enabling mm oracle update");
4917+
state.feature_bit_flags = state.feature_bit_flags | (FeatureBitFlags::MmOracleUpdate as u8);
49094918
} else {
4919+
msg!("Setting first bit to 0, disabling mm oracle update");
4920+
state.feature_bit_flags =
4921+
state.feature_bit_flags & !(FeatureBitFlags::MmOracleUpdate as u8);
4922+
}
4923+
Ok(())
4924+
}
4925+
4926+
pub fn handle_update_feature_bit_flags_median_trigger_price(
4927+
ctx: Context<HotAdminUpdateState>,
4928+
enable: bool,
4929+
) -> Result<()> {
4930+
let state = &mut ctx.accounts.state;
4931+
if enable {
49104932
validate!(
49114933
ctx.accounts.admin.key().eq(&state.admin),
49124934
ErrorCode::DefaultError,
49134935
"Only state admin can re-enable after kill switch"
49144936
)?;
4915-
msg!("Setting first bit to 0, enabling mm oracle update");
4916-
state.disable_bit_flags = state.disable_bit_flags & !1;
4937+
4938+
msg!("Setting second bit to 1, enabling median trigger price");
4939+
state.feature_bit_flags =
4940+
state.feature_bit_flags | (FeatureBitFlags::MedianTriggerPrice as u8);
4941+
} else {
4942+
msg!("Setting second bit to 0, disabling median trigger price");
4943+
state.feature_bit_flags =
4944+
state.feature_bit_flags & !(FeatureBitFlags::MedianTriggerPrice as u8);
49174945
}
49184946
Ok(())
49194947
}

programs/drift/src/instructions/user.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1888,6 +1888,7 @@ pub fn handle_transfer_perp_position<'c: 'info, 'info>(
18881888
taker_existing_base_asset_amount: to_existing_base_asset_amount,
18891889
maker_existing_quote_entry_amount: from_existing_quote_entry_amount,
18901890
maker_existing_base_asset_amount: from_existing_base_asset_amount,
1891+
trigger_price: None,
18911892
};
18921893

18931894
emit_stack::<_, { OrderActionRecord::SIZE }>(fill_record)?;

programs/drift/src/lib.rs

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1810,16 +1810,23 @@ pub mod drift {
18101810
handle_update_if_rebalance_config(ctx, params)
18111811
}
18121812

1813-
pub fn update_disable_bitflags_mm_oracle(
1813+
pub fn update_feature_bit_flags_mm_oracle(
18141814
ctx: Context<HotAdminUpdateState>,
1815-
disable: bool,
1815+
enable: bool,
18161816
) -> Result<()> {
1817-
handle_update_disable_bitflags_mm_oracle(ctx, disable)
1817+
handle_update_feature_bit_flags_mm_oracle(ctx, enable)
18181818
}
18191819

18201820
pub fn zero_mm_oracle_fields(ctx: Context<HotAdminUpdatePerpMarket>) -> Result<()> {
18211821
handle_zero_mm_oracle_fields(ctx)
18221822
}
1823+
1824+
pub fn update_feature_bit_flags_median_trigger_price(
1825+
ctx: Context<HotAdminUpdateState>,
1826+
enable: bool,
1827+
) -> Result<()> {
1828+
handle_update_feature_bit_flags_median_trigger_price(ctx, enable)
1829+
}
18231830
}
18241831

18251832
#[cfg(not(feature = "no-entrypoint"))]

programs/drift/src/math/constants.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,8 @@ pub const DEFAULT_QUOTE_ASSET_AMOUNT_TICK_SIZE: u64 =
204204

205205
// FUNDING
206206
pub const FUNDING_RATE_OFFSET_DENOMINATOR: i64 = 5000; // 5000 => 7.3% annualized rate for hourly funding
207+
pub const FUNDING_RATE_OFFSET_PERCENTAGE: i64 =
208+
FUNDING_RATE_PRECISION_I64 / FUNDING_RATE_OFFSET_DENOMINATOR;
207209

208210
// ORDERS
209211
pub const AUCTION_DERIVE_PRICE_FRACTION: i64 = 200;

programs/drift/src/state/events.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -254,10 +254,12 @@ pub struct OrderActionRecord {
254254
/// precision: BASE_PRECISION
255255
/// Only Some if the maker flipped position direction
256256
pub maker_existing_base_asset_amount: Option<u64>,
257+
/// precision: PRICE_PRECISION
258+
pub trigger_price: Option<u64>,
257259
}
258260

259261
impl Size for OrderActionRecord {
260-
const SIZE: usize = 448;
262+
const SIZE: usize = 464;
261263
}
262264

263265
pub fn get_order_action_record(
@@ -285,6 +287,7 @@ pub fn get_order_action_record(
285287
taker_existing_base_asset_amount: Option<u64>,
286288
maker_existing_quote_entry_amount: Option<u64>,
287289
maker_existing_base_asset_amount: Option<u64>,
290+
trigger_price: Option<u64>,
288291
) -> DriftResult<OrderActionRecord> {
289292
Ok(OrderActionRecord {
290293
ts,
@@ -337,6 +340,7 @@ pub fn get_order_action_record(
337340
taker_existing_base_asset_amount,
338341
maker_existing_quote_entry_amount,
339342
maker_existing_base_asset_amount,
343+
trigger_price,
340344
})
341345
}
342346

0 commit comments

Comments
 (0)