Skip to content

Commit

Permalink
Merge branch 'master' into bigz/withdraw-fee-use-last-active-slot
Browse files Browse the repository at this point in the history
  • Loading branch information
crispheaney authored Dec 12, 2023
2 parents 97e3906 + c10f8b6 commit ed41489
Show file tree
Hide file tree
Showing 30 changed files with 929 additions and 82 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Features

- program: only consider recent last_active_slot in qualifies_for_withdraw_feen([#756](https://github.com/drift-labs/protocol-v2/pull/756))
- program: amm can use reference price offset from oracle price based on clamped inventory and persist market premiums ([#681](https://github.com/drift-labs/protocol-v2/pull/681))

### Fixes

- program: add update last slot for filler in pay_keeper_flat_reward_for_spot

### Breaking

## [2.51.0] - 2023-12-09
Expand Down
11 changes: 11 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions programs/drift/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ serum_dex = { git = "https://github.com/project-serum/serum-dex", rev = "85b4f14
enumflags2 = "0.6.4"
phoenix-v1 = { git = "https://github.com/drift-labs/phoenix-v1", rev = "4c65c9", version = "0.2.4", features = ["no-entrypoint"] }
solana-security-txt = "1.1.0"
static_assertions = "1.1.0"
drift-macros = { git = "https://github.com/drift-labs/drift-macros.git", rev = "c57d87" }

[dev-dependencies]
bytes = "1.2.0"
Expand Down
26 changes: 26 additions & 0 deletions programs/drift/src/controller/amm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,31 @@ pub fn update_spread_reserves(amm: &mut AMM) -> DriftResult {
}

pub fn update_spreads(amm: &mut AMM, reserve_price: u64) -> DriftResult<(u32, u32)> {
let max_offset = amm.get_max_reference_price_offset()?;

let reference_price_offset = if amm.curve_update_intensity > 0 {
let liquidity_ratio = amm_spread::calculate_inventory_liquidity_ratio(
amm.base_asset_amount_with_amm,
amm.base_asset_reserve,
amm.max_base_asset_reserve,
amm.min_base_asset_reserve,
)?;

amm_spread::calculate_reference_price_offset(
reserve_price,
amm.last_24h_avg_funding_rate,
liquidity_ratio,
amm.min_order_size,
amm.historical_oracle_data.last_oracle_price_twap_5min,
amm.last_mark_price_twap_5min,
amm.historical_oracle_data.last_oracle_price_twap,
amm.last_mark_price_twap,
max_offset,
)?
} else {
0
};

let (long_spread, short_spread) = if amm.curve_update_intensity > 0 {
amm_spread::calculate_spread(
amm.base_spread,
Expand Down Expand Up @@ -196,6 +221,7 @@ pub fn update_spreads(amm: &mut AMM, reserve_price: u64) -> DriftResult<(u32, u3

amm.long_spread = long_spread;
amm.short_spread = short_spread;
amm.reference_price_offset = reference_price_offset;

update_spread_reserves(amm)?;

Expand Down
4 changes: 2 additions & 2 deletions programs/drift/src/controller/insurance/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1534,7 +1534,7 @@ fn test_transfer_protocol_owned_stake() {
.unwrap();
if_balance -= amount_returned;

assert_eq!(amount_returned, 99500000000);
assert_eq!(amount_returned, 99500000000_u64);
assert_eq!(spot_market.insurance_fund.user_shares, 0);
assert_eq!(spot_market.insurance_fund.total_shares, 21105203599);

Expand Down Expand Up @@ -1597,7 +1597,7 @@ fn test_transfer_protocol_owned_stake() {

let mut expected_if_stake_2 = InsuranceFundStake::new(Pubkey::default(), 0, 0);
expected_if_stake_2
.increase_if_shares(21105203599, &spot_market)
.increase_if_shares(21105203599_u128, &spot_market)
.unwrap();

assert_eq!(user_stats_2.if_staked_quote_asset_amount, 99500000000);
Expand Down
8 changes: 8 additions & 0 deletions programs/drift/src/controller/orders.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2785,6 +2785,7 @@ pub fn force_cancel_orders(
Some(filler),
spot_market_map.get_quote_spot_market_mut()?.deref_mut(),
total_fee,
slot,
)?;

user.update_last_active_slot(slot);
Expand Down Expand Up @@ -2839,6 +2840,7 @@ pub fn pay_keeper_flat_reward_for_spot(
filler: Option<&mut User>,
quote_market: &mut SpotMarket,
filler_reward: u64,
slot: u64,
) -> DriftResult<u64> {
let filler_reward = if let Some(filler) = filler {
update_spot_balances(
Expand All @@ -2849,6 +2851,8 @@ pub fn pay_keeper_flat_reward_for_spot(
false,
)?;

filler.update_last_active_slot(slot);

filler.update_cumulative_spot_fees(filler_reward.cast()?)?;

update_spot_balances(
Expand Down Expand Up @@ -3312,6 +3316,7 @@ pub fn fill_spot_order(
filler.as_deref_mut(),
&mut quote_market,
state.spot_fee_structure.flat_filler_fee,
slot,
)?
};

Expand Down Expand Up @@ -3419,6 +3424,7 @@ pub fn fill_spot_order(
filler.as_deref_mut(),
&mut quote_market,
state.spot_fee_structure.flat_filler_fee,
slot,
)?
};

Expand Down Expand Up @@ -3572,6 +3578,7 @@ fn get_spot_maker_order<'a>(
filler.as_deref_mut(),
&mut quote_market,
filler_reward,
slot,
)?
};

Expand Down Expand Up @@ -4582,6 +4589,7 @@ pub fn trigger_spot_order(
filler.as_deref_mut(),
&mut quote_market,
state.spot_fee_structure.flat_filler_fee,
slot,
)?;

let order_action_record = get_order_action_record(
Expand Down
5 changes: 4 additions & 1 deletion programs/drift/src/instructions/admin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -727,7 +727,10 @@ pub fn handle_initialize_perp_market(
padding1: 0,
padding2: 0,
total_fee_earned_per_lp: 0,
padding: [0; 32],
net_unsettled_funding_pnl: 0,
quote_asset_amount_with_unsettled_lp: 0,
reference_price_offset: 0,
padding: [0; 12],
},
};

Expand Down
104 changes: 90 additions & 14 deletions programs/drift/src/math/amm_spread.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,15 @@ use crate::math::constants::{
AMM_TIMES_PEG_TO_QUOTE_PRECISION_RATIO_I128, AMM_TO_QUOTE_PRECISION_RATIO_I128,
BID_ASK_SPREAD_PRECISION, BID_ASK_SPREAD_PRECISION_I128, BID_ASK_SPREAD_PRECISION_U128,
DEFAULT_LARGE_BID_ASK_FACTOR, DEFAULT_REVENUE_SINCE_LAST_FUNDING_SPREAD_RETREAT,
MAX_BID_ASK_INVENTORY_SKEW_FACTOR, PEG_PRECISION, PERCENTAGE_PRECISION,
PERCENTAGE_PRECISION_U64, PRICE_PRECISION, PRICE_PRECISION_I128,
FUNDING_RATE_BUFFER, MAX_BID_ASK_INVENTORY_SKEW_FACTOR, PEG_PRECISION, PERCENTAGE_PRECISION,
PERCENTAGE_PRECISION_I128, PERCENTAGE_PRECISION_U64, PRICE_PRECISION, PRICE_PRECISION_I128,
PRICE_PRECISION_I64,
};
use crate::math::safe_math::SafeMath;

use crate::state::perp_market::AMM;
use crate::validate;

use super::constants::PERCENTAGE_PRECISION_I128;

#[cfg(test)]
mod tests;

Expand Down Expand Up @@ -328,8 +327,10 @@ pub fn calculate_spread(
volume_24h,
)?;

let mut long_spread = max((base_spread / 2) as u64, long_vol_spread);
let mut short_spread = max((base_spread / 2) as u64, short_vol_spread);
let half_base_spread_u64 = (base_spread / 2) as u64;

let mut long_spread = max(half_base_spread_u64, long_vol_spread);
let mut short_spread = max(half_base_spread_u64, short_vol_spread);

let max_target_spread = max_spread
.cast::<u64>()?
Expand Down Expand Up @@ -448,20 +449,23 @@ pub fn calculate_spread_reserves(
PositionDirection::Short => amm.short_spread,
};

let spread_with_offset: i32 = spread.cast::<i32>()?.safe_add(amm.reference_price_offset)?;

let quote_asset_reserve_delta = if spread > 0 {
amm.quote_asset_reserve
.safe_div(BID_ASK_SPREAD_PRECISION_U128 / (spread.cast::<u128>()? / 2))?
.safe_div(BID_ASK_SPREAD_PRECISION_U128 / (spread_with_offset.cast::<u128>()? / 2))?
} else {
0
};

let quote_asset_reserve = match direction {
PositionDirection::Long => amm
.quote_asset_reserve
.safe_add(quote_asset_reserve_delta)?,
PositionDirection::Short => amm
.quote_asset_reserve
.safe_sub(quote_asset_reserve_delta)?,
let quote_asset_reserve = if spread_with_offset >= 0 && direction == PositionDirection::Long
|| spread_with_offset <= 0 && direction == PositionDirection::Short
{
amm.quote_asset_reserve
.safe_add(quote_asset_reserve_delta)?
} else {
amm.quote_asset_reserve
.safe_sub(quote_asset_reserve_delta)?
};

let invariant_sqrt_u192 = U192::from(amm.sqrt_k);
Expand All @@ -473,3 +477,75 @@ pub fn calculate_spread_reserves(

Ok((base_asset_reserve, quote_asset_reserve))
}

#[allow(clippy::comparison_chain)]
pub fn calculate_reference_price_offset(
reserve_price: u64,
last_24h_avg_funding_rate: i64,
liquidity_fraction: i128,
_min_order_size: u64,
oracle_twap_fast: i64,
mark_twap_fast: u64,
oracle_twap_slow: i64,
mark_twap_slow: u64,
max_offset_pct: i64,
) -> DriftResult<i32> {
if last_24h_avg_funding_rate == 0 {
return Ok(0);
}

let max_offset_in_price = max_offset_pct
.safe_mul(reserve_price.cast()?)?
.safe_div(PERCENTAGE_PRECISION.cast()?)?;

// calculate quote denominated market premium
let mark_premium_minute: i64 = mark_twap_fast
.cast::<i64>()?
.safe_sub(oracle_twap_fast)?
.clamp(-max_offset_in_price, max_offset_in_price);
let mark_premium_hour: i64 = mark_twap_slow
.cast::<i64>()?
.safe_sub(oracle_twap_slow)?
.clamp(-max_offset_in_price, max_offset_in_price);
// convert last_24h_avg_funding_rate to quote denominated premium
let mark_premium_day: i64 = last_24h_avg_funding_rate
.safe_div(FUNDING_RATE_BUFFER.cast()?)?
.safe_mul(24)?
.clamp(-max_offset_in_price, max_offset_in_price); // todo: look at how 24h funding is calc w.r.t. the funding_period

// take average clamped premium as the price-based offset
let mark_premium_avg = mark_premium_minute
.safe_add(mark_premium_hour)?
.safe_add(mark_premium_day)?
.safe_div(3_i64)?;

let mark_premium_avg_pct: i64 = mark_premium_avg
.safe_mul(PRICE_PRECISION_I64)?
.safe_div(reserve_price.cast()?)?;

let inventory_pct = liquidity_fraction
.cast::<i64>()?
.safe_mul(max_offset_pct)?
.safe_div(PERCENTAGE_PRECISION.cast::<i64>()?)?
.clamp(-max_offset_pct, max_offset_pct);

// only apply when inventory is consistent with recent and 24h market premium
let offset_pct = if (mark_premium_avg_pct >= 0 && inventory_pct >= 0)
|| (mark_premium_avg_pct <= 0 && inventory_pct <= 0)
{
mark_premium_avg_pct.safe_add(inventory_pct)?
} else {
0
};

let clamped_offset_pct = offset_pct.clamp(-max_offset_pct, max_offset_pct);

validate!(
clamped_offset_pct.abs() <= max_offset_pct,
ErrorCode::InvalidAmmDetected,
"clamp offset pct failed {}",
clamped_offset_pct
)?;

clamped_offset_pct.cast()
}
Loading

0 comments on commit ed41489

Please sign in to comment.