Skip to content

Commit 3fa3ef0

Browse files
crispheaney0xbigz
andauthored
program: add taker_speed_bump_override and amm_spread_adjustment (#1665)
* program: add taker_speed_bump_override and amm_spread_adjustment * add admin client * cargo test * add impl for amm_spread_adjustment * ensure no overflows * CHANGELOG * cargo fmt -- * sdk types * prettify --------- Co-authored-by: 0xbigz <[email protected]>
1 parent 58de186 commit 3fa3ef0

File tree

11 files changed

+315
-11
lines changed

11 files changed

+315
-11
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1111

1212
- program: simplify user can skip duration ([#1668](https://github.com/drift-labs/protocol-v2/pull/1668))
1313
- program: allow limit orders without auctions in swift ([#1661](https://github.com/drift-labs/protocol-v2/pull/1661))
14+
- program: add taker_speed_bump_override and amm_spread_adjustment ([#1665](https://github.com/drift-labs/protocol-v2/pull/1665))
1415

1516
### Fixes
1617

programs/drift/src/controller/amm.rs

Lines changed: 39 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@ use crate::math::amm::calculate_quote_asset_amount_swapped;
1515
use crate::math::amm_spread::{calculate_spread_reserves, get_spread_reserves};
1616
use crate::math::casting::Cast;
1717
use crate::math::constants::{
18-
CONCENTRATION_PRECISION, FEE_POOL_TO_REVENUE_POOL_THRESHOLD, K_BPS_UPDATE_SCALE,
19-
MAX_CONCENTRATION_COEFFICIENT, MAX_K_BPS_INCREASE, MAX_SQRT_K,
18+
CONCENTRATION_PRECISION, FEE_ADJUSTMENT_MAX, FEE_POOL_TO_REVENUE_POOL_THRESHOLD,
19+
K_BPS_UPDATE_SCALE, MAX_CONCENTRATION_COEFFICIENT, MAX_K_BPS_INCREASE, MAX_SQRT_K,
2020
};
2121
use crate::math::cp_curve::get_update_k_result;
2222
use crate::math::repeg::get_total_fee_lower_bound;
@@ -209,7 +209,7 @@ pub fn update_spreads(market: &mut PerpMarket, reserve_price: u64) -> DriftResul
209209
0
210210
};
211211

212-
let (long_spread, short_spread) = if market.amm.curve_update_intensity > 0 {
212+
let (mut long_spread, mut short_spread) = if market.amm.curve_update_intensity > 0 {
213213
amm_spread::calculate_spread(
214214
market.amm.base_spread,
215215
market.amm.last_oracle_reserve_price_spread_pct,
@@ -236,6 +236,42 @@ pub fn update_spreads(market: &mut PerpMarket, reserve_price: u64) -> DriftResul
236236
(half_base_spread, half_base_spread)
237237
};
238238

239+
if market.amm.amm_spread_adjustment < 0 {
240+
long_spread = long_spread
241+
.saturating_sub(
242+
long_spread
243+
.safe_mul(market.amm.amm_spread_adjustment.unsigned_abs().cast()?)
244+
.unwrap_or(u32::MAX)
245+
.safe_div(100)?,
246+
)
247+
.max(1);
248+
short_spread = short_spread
249+
.saturating_sub(
250+
short_spread
251+
.safe_mul(market.amm.amm_spread_adjustment.unsigned_abs().cast()?)
252+
.unwrap_or(u32::MAX)
253+
.safe_div(100)?,
254+
)
255+
.max(1);
256+
} else if market.amm.amm_spread_adjustment > 0 {
257+
long_spread = long_spread
258+
.saturating_add(
259+
long_spread
260+
.safe_mul(market.amm.amm_spread_adjustment.cast()?)
261+
.unwrap_or(u32::MAX)
262+
.safe_div_ceil(100)?,
263+
)
264+
.max(1);
265+
short_spread = short_spread
266+
.saturating_add(
267+
short_spread
268+
.safe_mul(market.amm.amm_spread_adjustment.cast()?)
269+
.unwrap_or(u32::MAX)
270+
.safe_div_ceil(100)?,
271+
)
272+
.max(1);
273+
}
274+
239275
market.amm.long_spread = long_spread;
240276
market.amm.short_spread = short_spread;
241277
market.amm.reference_price_offset = reference_price_offset;

programs/drift/src/controller/orders.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1057,6 +1057,7 @@ pub fn fill_perp_order(
10571057
let amm_lp_allowed_to_jit_make: bool;
10581058
let oracle_valid_for_amm_fill: bool;
10591059
let oracle_stale_for_margin: bool;
1060+
let min_auction_duration: u8;
10601061
let mut amm_is_available = !state.amm_paused()?;
10611062
{
10621063
let market = &mut perp_market_map.get_ref_mut(&market_index)?;
@@ -1105,6 +1106,9 @@ pub fn fill_perp_order(
11051106
.last_oracle_price_twap_5min;
11061107
oracle_validity = _oracle_validity;
11071108
perp_market_index = market.market_index;
1109+
1110+
min_auction_duration =
1111+
market.get_min_perp_auction_duration(state.min_perp_auction_duration);
11081112
}
11091113

11101114
// allow oracle price to be used to calculate limit price if it's valid or stale for amm
@@ -1280,7 +1284,7 @@ pub fn fill_perp_order(
12801284
valid_oracle_price,
12811285
now,
12821286
slot,
1283-
state.min_perp_auction_duration,
1287+
min_auction_duration,
12841288
amm_availability,
12851289
fill_mode,
12861290
oracle_stale_for_margin,

programs/drift/src/instructions/admin.rs

Lines changed: 44 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1041,7 +1041,8 @@ pub fn handle_initialize_perp_market(
10411041
target_base_asset_amount_per_lp: 0,
10421042
per_lp_base: 0,
10431043
padding1: 0,
1044-
padding2: 0,
1044+
taker_speed_bump_override: 0,
1045+
amm_spread_adjustment: 0,
10451046
total_fee_earned_per_lp: 0,
10461047
net_unsettled_funding_pnl: 0,
10471048
quote_asset_amount_with_unsettled_lp: 0,
@@ -3910,7 +3911,7 @@ pub fn handle_update_perp_market_number_of_users(
39103911
}
39113912

39123913
pub fn handle_update_perp_market_fuel(
3913-
ctx: Context<AdminUpdatePerpMarketFuel>,
3914+
ctx: Context<HotAdminUpdatePerpMarket>,
39143915
fuel_boost_taker: Option<u8>,
39153916
fuel_boost_maker: Option<u8>,
39163917
fuel_boost_position: Option<u8>,
@@ -3987,6 +3988,46 @@ pub fn handle_update_perp_market_protected_maker_params(
39873988
Ok(())
39883989
}
39893990

3991+
#[access_control(
3992+
perp_market_valid(&ctx.accounts.perp_market)
3993+
)]
3994+
pub fn handle_update_perp_market_taker_speed_bump_override(
3995+
ctx: Context<HotAdminUpdatePerpMarket>,
3996+
taker_speed_bump_override: i8,
3997+
) -> Result<()> {
3998+
let perp_market = &mut load_mut!(ctx.accounts.perp_market)?;
3999+
msg!("perp market {}", perp_market.market_index);
4000+
4001+
msg!(
4002+
"perp_market.amm.taker_speed_bump_override: {:?} -> {:?}",
4003+
perp_market.amm.taker_speed_bump_override,
4004+
taker_speed_bump_override
4005+
);
4006+
4007+
perp_market.amm.taker_speed_bump_override = taker_speed_bump_override;
4008+
Ok(())
4009+
}
4010+
4011+
#[access_control(
4012+
perp_market_valid(&ctx.accounts.perp_market)
4013+
)]
4014+
pub fn handle_update_perp_market_amm_spread_adjustment(
4015+
ctx: Context<HotAdminUpdatePerpMarket>,
4016+
amm_spread_adjustment: i8,
4017+
) -> Result<()> {
4018+
let perp_market = &mut load_mut!(ctx.accounts.perp_market)?;
4019+
msg!("perp market {}", perp_market.market_index);
4020+
4021+
msg!(
4022+
"perp_market.amm.amm_spread_adjustment: {:?} -> {:?}",
4023+
perp_market.amm.amm_spread_adjustment,
4024+
amm_spread_adjustment
4025+
);
4026+
4027+
perp_market.amm.amm_spread_adjustment = amm_spread_adjustment;
4028+
Ok(())
4029+
}
4030+
39904031
#[access_control(
39914032
spot_market_valid(&ctx.accounts.spot_market)
39924033
)]
@@ -4865,7 +4906,7 @@ pub struct AdminUpdatePerpMarket<'info> {
48654906
}
48664907

48674908
#[derive(Accounts)]
4868-
pub struct AdminUpdatePerpMarketFuel<'info> {
4909+
pub struct HotAdminUpdatePerpMarket<'info> {
48694910
#[account(
48704911
constraint = admin.key() == admin_hot_wallet::id() || admin.key() == state.admin
48714912
)]

programs/drift/src/lib.rs

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1513,7 +1513,7 @@ pub mod drift {
15131513
}
15141514

15151515
pub fn update_perp_market_fuel(
1516-
ctx: Context<AdminUpdatePerpMarketFuel>,
1516+
ctx: Context<HotAdminUpdatePerpMarket>,
15171517
fuel_boost_taker: Option<u8>,
15181518
fuel_boost_maker: Option<u8>,
15191519
fuel_boost_position: Option<u8>,
@@ -1533,6 +1533,20 @@ pub mod drift {
15331533
)
15341534
}
15351535

1536+
pub fn update_perp_market_taker_speed_bump_override(
1537+
ctx: Context<HotAdminUpdatePerpMarket>,
1538+
taker_speed_bump_override: i8,
1539+
) -> Result<()> {
1540+
handle_update_perp_market_taker_speed_bump_override(ctx, taker_speed_bump_override)
1541+
}
1542+
1543+
pub fn update_perp_market_amm_spread_adjustment(
1544+
ctx: Context<HotAdminUpdatePerpMarket>,
1545+
amm_spread_adjustment: i8,
1546+
) -> Result<()> {
1547+
handle_update_perp_market_amm_spread_adjustment(ctx, amm_spread_adjustment)
1548+
}
1549+
15361550
pub fn update_spot_market_fuel(
15371551
ctx: Context<AdminUpdateSpotMarketFuel>,
15381552
fuel_boost_deposits: Option<u8>,

programs/drift/src/math/cp_curve/tests.rs

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -312,6 +312,64 @@ fn calculate_k_tests_wrapper_fcn() {
312312
assert_eq!(pct_change_in_k, 10007); // k was increased .07%
313313
}
314314

315+
#[test]
316+
fn amm_spread_adj_logic() {
317+
let mut market = PerpMarket {
318+
amm: AMM {
319+
base_asset_reserve: 100 * AMM_RESERVE_PRECISION,
320+
quote_asset_reserve: 100 * AMM_RESERVE_PRECISION,
321+
terminal_quote_asset_reserve: 999900009999000 * AMM_RESERVE_PRECISION,
322+
sqrt_k: 100 * AMM_RESERVE_PRECISION,
323+
peg_multiplier: 50_000_000_000,
324+
base_asset_amount_with_amm: (AMM_RESERVE_PRECISION / 10) as i128,
325+
base_asset_amount_long: (AMM_RESERVE_PRECISION / 10) as i128,
326+
order_step_size: 5,
327+
base_spread: 100,
328+
max_spread: 10000,
329+
..AMM::default_test()
330+
},
331+
margin_ratio_initial: 1000,
332+
..PerpMarket::default()
333+
};
334+
// let (t_price, _t_qar, _t_bar) = calculate_terminal_price_and_reserves(&market.amm).unwrap();
335+
// market.amm.terminal_quote_asset_reserve = _t_qar;
336+
337+
let mut position = PerpPosition {
338+
..PerpPosition::default()
339+
};
340+
341+
mint_lp_shares(&mut position, &mut market, BASE_PRECISION_U64).unwrap();
342+
343+
market.amm.base_asset_amount_per_lp = 1;
344+
market.amm.quote_asset_amount_per_lp = -QUOTE_PRECISION_I64 as i128;
345+
346+
let reserve_price = market.amm.reserve_price().unwrap();
347+
update_spreads(&mut market, reserve_price).unwrap();
348+
349+
assert_eq!(market.amm.long_spread, 50);
350+
assert_eq!(market.amm.short_spread, 50);
351+
352+
market.amm.amm_spread_adjustment = -100;
353+
update_spreads(&mut market, reserve_price).unwrap();
354+
assert_eq!(market.amm.long_spread, 1);
355+
assert_eq!(market.amm.short_spread, 1);
356+
357+
market.amm.amm_spread_adjustment = 100;
358+
update_spreads(&mut market, reserve_price).unwrap();
359+
assert_eq!(market.amm.long_spread, 100);
360+
assert_eq!(market.amm.short_spread, 100);
361+
362+
market.amm.amm_spread_adjustment = 20;
363+
update_spreads(&mut market, reserve_price).unwrap();
364+
assert_eq!(market.amm.long_spread, 60);
365+
assert_eq!(market.amm.short_spread, 60);
366+
367+
market.amm.amm_spread_adjustment = 120;
368+
update_spreads(&mut market, reserve_price).unwrap();
369+
assert_eq!(market.amm.long_spread, 110);
370+
assert_eq!(market.amm.short_spread, 110);
371+
}
372+
315373
#[test]
316374
fn calculate_k_with_lps_tests() {
317375
let mut market = PerpMarket {

programs/drift/src/math/orders.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ use crate::math::casting::Cast;
1212
use crate::state::fill_mode::FillMode;
1313
use crate::state::protected_maker_mode_config::ProtectedMakerParams;
1414
use crate::state::user::OrderBitFlag;
15-
use crate::BASE_PRECISION_U64;
1615
use crate::{
1716
load, math, FeeTier, State, BASE_PRECISION_I128, FEE_ADJUSTMENT_MAX,
1817
MAX_PREDICTION_MARKET_PRICE, MAX_PREDICTION_MARKET_PRICE_I64, OPEN_ORDER_MARGIN_REQUIREMENT,

programs/drift/src/state/perp_market.rs

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -735,6 +735,14 @@ impl PerpMarket {
735735
tick_size: self.amm.order_tick_size,
736736
}
737737
}
738+
739+
pub fn get_min_perp_auction_duration(&self, default_min_auction_duration: u8) -> u8 {
740+
if self.amm.taker_speed_bump_override != 0 {
741+
self.amm.taker_speed_bump_override.max(0).unsigned_abs()
742+
} else {
743+
default_min_auction_duration
744+
}
745+
}
738746
}
739747

740748
#[cfg(test)]
@@ -1034,8 +1042,12 @@ pub struct AMM {
10341042
pub target_base_asset_amount_per_lp: i32,
10351043
/// expo for unit of per_lp, base 10 (if per_lp_base=X, then per_lp unit is 10^X)
10361044
pub per_lp_base: i8,
1045+
/// the override for the state.min_perp_auction_duration
1046+
/// 0 is no override, -1 is disable speed bump, 1-100 is literal speed bump
1047+
pub taker_speed_bump_override: i8,
1048+
/// signed scale amm_spread similar to fee_adjustment logic (-100 = 0, 100 = double)
1049+
pub amm_spread_adjustment: i8,
10371050
pub padding1: u8,
1038-
pub padding2: u16,
10391051
pub total_fee_earned_per_lp: u64,
10401052
pub net_unsettled_funding_pnl: i64,
10411053
pub quote_asset_amount_with_unsettled_lp: i64,
@@ -1123,8 +1135,9 @@ impl Default for AMM {
11231135
last_oracle_valid: false,
11241136
target_base_asset_amount_per_lp: 0,
11251137
per_lp_base: 0,
1138+
taker_speed_bump_override: 0,
1139+
amm_spread_adjustment: 0,
11261140
padding1: 0,
1127-
padding2: 0,
11281141
total_fee_earned_per_lp: 0,
11291142
net_unsettled_funding_pnl: 0,
11301143
quote_asset_amount_with_unsettled_lp: 0,

programs/drift/src/state/perp_market/tests.rs

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,3 +101,58 @@ mod get_margin_ratio {
101101
assert_eq!(margin_ratio_maintenance, MARGIN_PRECISION / 100);
102102
}
103103
}
104+
105+
mod get_min_perp_auction_duration {
106+
use crate::state::perp_market::{PerpMarket, AMM};
107+
use crate::State;
108+
109+
#[test]
110+
fn test_get_speed_bump() {
111+
let perp_market = PerpMarket {
112+
amm: AMM {
113+
taker_speed_bump_override: 0,
114+
..AMM::default()
115+
},
116+
..PerpMarket::default()
117+
};
118+
119+
let state = State {
120+
min_perp_auction_duration: 10,
121+
..State::default()
122+
};
123+
124+
// no override uses state value
125+
assert_eq!(
126+
perp_market.get_min_perp_auction_duration(state.min_perp_auction_duration),
127+
10
128+
);
129+
130+
let perp_market = PerpMarket {
131+
amm: AMM {
132+
taker_speed_bump_override: -1,
133+
..AMM::default()
134+
},
135+
..PerpMarket::default()
136+
};
137+
138+
// -1 override disables speed bump
139+
assert_eq!(
140+
perp_market.get_min_perp_auction_duration(state.min_perp_auction_duration),
141+
0
142+
);
143+
144+
let perp_market = PerpMarket {
145+
amm: AMM {
146+
taker_speed_bump_override: 20,
147+
..AMM::default()
148+
},
149+
..PerpMarket::default()
150+
};
151+
152+
// positive override uses override value
153+
assert_eq!(
154+
perp_market.get_min_perp_auction_duration(state.min_perp_auction_duration),
155+
20
156+
);
157+
}
158+
}

0 commit comments

Comments
 (0)