Skip to content

Commit 3773193

Browse files
authored
program: update fee tier (#1792)
* program: determine-fee-tier-update (reorg) * update user stats check logic * remove unnecessary clone * less mul operations * rm unwrap * update sdk * fix styling * use satsub * update changelog
1 parent 34f832e commit 3773193

File tree

5 files changed

+278
-59
lines changed

5 files changed

+278
-59
lines changed

CHANGELOG.md

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

1010
### Features
11+
- program: update stake + volume fee tier determination ([#1792](https://github.com/drift-labs/protocol-v2/pull/1792))
1112

1213
### Fixes
1314

programs/drift/src/controller/orders.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2164,7 +2164,7 @@ pub fn fulfill_perp_order_with_amm(
21642164
limit_price,
21652165
override_fill_price,
21662166
existing_base_asset_amount,
2167-
fee_tier,
2167+
&fee_tier,
21682168
)?;
21692169

21702170
let fill_price = if user.orders[order_index].post_only {

programs/drift/src/math/fees.rs

Lines changed: 78 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use crate::math::casting::Cast;
88
use crate::math::constants::{
99
FIFTY_MILLION_QUOTE, FIVE_MILLION_QUOTE, ONE_HUNDRED_MILLION_QUOTE, ONE_HUNDRED_THOUSAND_QUOTE,
1010
ONE_MILLION_QUOTE, ONE_THOUSAND_QUOTE, TEN_BPS, TEN_MILLION_QUOTE, TEN_THOUSAND_QUOTE,
11-
TWENTY_FIVE_THOUSAND_QUOTE,
11+
TWENTY_FIVE_THOUSAND_QUOTE, TWO_HUNDRED_FIFTY_THOUSAND_QUOTE,
1212
};
1313
use crate::math::helpers::get_proportion_u128;
1414
use crate::math::safe_math::SafeMath;
@@ -55,7 +55,7 @@ pub fn calculate_fee_for_fulfillment_with_amm(
5555

5656
// if there was a quote_asset_amount_surplus, the order was a maker order and fee_to_market comes from surplus
5757
if is_post_only {
58-
let maker_rebate = calculate_maker_rebate(quote_asset_amount, fee_tier, fee_adjustment)?;
58+
let maker_rebate = calculate_maker_rebate(quote_asset_amount, &fee_tier, fee_adjustment)?;
5959

6060
let fee = quote_asset_amount_surplus
6161
.cast::<u64>()?
@@ -94,7 +94,7 @@ pub fn calculate_fee_for_fulfillment_with_amm(
9494
referee_discount: 0,
9595
})
9696
} else {
97-
let mut fee = calculate_taker_fee(quote_asset_amount, fee_tier, fee_adjustment)?;
97+
let mut fee = calculate_taker_fee(quote_asset_amount, &fee_tier, fee_adjustment)?;
9898

9999
if user_high_leverage_mode {
100100
fee = fee.safe_mul(2)?;
@@ -103,7 +103,7 @@ pub fn calculate_fee_for_fulfillment_with_amm(
103103
let (fee, referee_discount, referrer_reward) = if reward_referrer {
104104
calculate_referee_fee_and_referrer_reward(
105105
fee,
106-
fee_tier,
106+
&fee_tier,
107107
fee_structure.referrer_reward_epoch_upper_bound,
108108
referrer_stats,
109109
)?
@@ -299,7 +299,7 @@ pub fn calculate_fee_for_fulfillment_with_match(
299299
determine_user_fee_tier(taker_stats, fee_structure, market_type, false)?
300300
};
301301

302-
let mut taker_fee = calculate_taker_fee(quote_asset_amount, taker_fee_tier, fee_adjustment)?;
302+
let mut taker_fee = calculate_taker_fee(quote_asset_amount, &taker_fee_tier, fee_adjustment)?;
303303

304304
if user_high_leverage_mode {
305305
taker_fee = taker_fee.safe_mul(2)?;
@@ -308,15 +308,15 @@ pub fn calculate_fee_for_fulfillment_with_match(
308308
let (taker_fee, referee_discount, referrer_reward) = if reward_referrer {
309309
calculate_referee_fee_and_referrer_reward(
310310
taker_fee,
311-
taker_fee_tier,
311+
&taker_fee_tier,
312312
fee_structure.referrer_reward_epoch_upper_bound,
313313
referrer_stats,
314314
)?
315315
} else {
316316
(taker_fee, 0, 0)
317317
};
318318

319-
let maker_rebate = calculate_maker_rebate(quote_asset_amount, maker_fee_tier, fee_adjustment)?;
319+
let maker_rebate = calculate_maker_rebate(quote_asset_amount, &maker_fee_tier, fee_adjustment)?;
320320

321321
let filler_reward = if filler_multiplier == 0 {
322322
0_u64
@@ -370,7 +370,7 @@ pub fn calculate_fee_for_fulfillment_with_external_market(
370370
let taker_fee_tier =
371371
determine_user_fee_tier(user_stats, fee_structure, &MarketType::Spot, false)?;
372372

373-
let fee = calculate_taker_fee(quote_asset_amount, taker_fee_tier, fee_adjustment)?;
373+
let fee = calculate_taker_fee(quote_asset_amount, &taker_fee_tier, fee_adjustment)?;
374374

375375
let fee_plus_referrer_rebate = external_market_fee.safe_add(unsettled_referrer_rebate)?;
376376

@@ -420,52 +420,91 @@ pub fn determine_user_fee_tier<'a>(
420420
fee_structure: &'a FeeStructure,
421421
market_type: &MarketType,
422422
user_high_leverage_mode: bool,
423-
) -> DriftResult<&'a FeeTier> {
423+
) -> DriftResult<FeeTier> {
424424
match market_type {
425-
MarketType::Perp if user_high_leverage_mode => Ok(&fee_structure.fee_tiers[0]),
425+
MarketType::Perp if user_high_leverage_mode => Ok(fee_structure.fee_tiers[0]),
426426
MarketType::Perp => determine_perp_fee_tier(user_stats, fee_structure),
427-
MarketType::Spot => determine_spot_fee_tier(user_stats, fee_structure),
427+
MarketType::Spot => Ok(*determine_spot_fee_tier(user_stats, fee_structure)?),
428428
}
429429
}
430430

431-
fn determine_perp_fee_tier<'a>(
431+
fn determine_perp_fee_tier(
432432
user_stats: &UserStats,
433-
fee_structure: &'a FeeStructure,
434-
) -> DriftResult<&'a FeeTier> {
433+
fee_structure: &FeeStructure,
434+
) -> DriftResult<FeeTier> {
435435
let total_30d_volume = user_stats.get_total_30d_volume()?;
436436
let staked_gov_token_amount = user_stats.if_staked_gov_token_amount;
437437

438-
if total_30d_volume >= ONE_HUNDRED_MILLION_QUOTE
439-
|| staked_gov_token_amount >= ONE_HUNDRED_THOUSAND_QUOTE + 19_500 * QUOTE_PRECISION_U64
440-
{
441-
return Ok(&fee_structure.fee_tiers[5]);
442-
}
443-
444-
if total_30d_volume >= FIFTY_MILLION_QUOTE
445-
|| staked_gov_token_amount >= ONE_HUNDRED_THOUSAND_QUOTE - QUOTE_PRECISION_U64
446-
{
447-
return Ok(&fee_structure.fee_tiers[4]);
448-
}
449-
450-
if total_30d_volume >= TEN_MILLION_QUOTE
451-
|| staked_gov_token_amount >= TWENTY_FIVE_THOUSAND_QUOTE * 2 - QUOTE_PRECISION_U64
452-
{
453-
return Ok(&fee_structure.fee_tiers[3]);
438+
const TIER_LENGTH: usize = 5;
439+
440+
const VOLUME_THRESHOLDS: [u64; TIER_LENGTH] = [
441+
ONE_MILLION_QUOTE * 2,
442+
FIVE_MILLION_QUOTE * 2,
443+
TEN_MILLION_QUOTE * 2,
444+
FIFTY_MILLION_QUOTE * 2,
445+
ONE_HUNDRED_MILLION_QUOTE * 2,
446+
];
447+
448+
const STAKE_THRESHOLDS: [u64; TIER_LENGTH] = [
449+
ONE_THOUSAND_QUOTE - QUOTE_PRECISION_U64,
450+
TEN_THOUSAND_QUOTE - QUOTE_PRECISION_U64,
451+
(TWENTY_FIVE_THOUSAND_QUOTE * 2) - QUOTE_PRECISION_U64,
452+
ONE_HUNDRED_THOUSAND_QUOTE - QUOTE_PRECISION_U64,
453+
TWO_HUNDRED_FIFTY_THOUSAND_QUOTE - QUOTE_PRECISION_U64 * 5,
454+
];
455+
456+
const STAKE_BENEFIT_FRAC: [u32; TIER_LENGTH + 1] = [0, 5, 10, 20, 30, 40];
457+
458+
let mut fee_tier_index = TIER_LENGTH;
459+
for i in 0..TIER_LENGTH {
460+
if total_30d_volume < VOLUME_THRESHOLDS[i] {
461+
fee_tier_index = i;
462+
break;
463+
}
454464
}
455465

456-
if total_30d_volume >= FIVE_MILLION_QUOTE
457-
|| staked_gov_token_amount >= TEN_THOUSAND_QUOTE - QUOTE_PRECISION_U64
458-
{
459-
return Ok(&fee_structure.fee_tiers[2]);
466+
let mut stake_benefit_index = TIER_LENGTH;
467+
for i in 0..TIER_LENGTH {
468+
if staked_gov_token_amount < STAKE_THRESHOLDS[i] {
469+
stake_benefit_index = i;
470+
break;
471+
}
460472
}
461473

462-
if total_30d_volume >= ONE_MILLION_QUOTE
463-
|| staked_gov_token_amount >= ONE_THOUSAND_QUOTE - QUOTE_PRECISION_U64
464-
{
465-
return Ok(&fee_structure.fee_tiers[1]);
474+
let stake_benefit = STAKE_BENEFIT_FRAC[stake_benefit_index];
475+
476+
let mut tier = fee_structure.fee_tiers[fee_tier_index];
477+
478+
if stake_benefit > 0 {
479+
if let Some(div_scalar) = match stake_benefit {
480+
5 => Some(20),
481+
10 => Some(10),
482+
20 => Some(5),
483+
_ => None,
484+
} {
485+
// Fast path for 5%, 10%, 20% using no mul
486+
tier.fee_numerator = tier
487+
.fee_numerator
488+
.saturating_sub(tier.fee_numerator.safe_div_ceil(div_scalar)?);
489+
490+
tier.maker_rebate_numerator = tier
491+
.maker_rebate_numerator
492+
.safe_add(tier.maker_rebate_numerator.safe_div(div_scalar)?)?;
493+
} else {
494+
// General path with mul/div
495+
tier.fee_numerator = tier
496+
.fee_numerator
497+
.safe_mul(100_u32.saturating_sub(stake_benefit))?
498+
.safe_div_ceil(100_u32)?;
499+
500+
tier.maker_rebate_numerator = tier
501+
.maker_rebate_numerator
502+
.safe_mul(100_u32.saturating_add(stake_benefit))?
503+
.safe_div(100_u32)?;
504+
}
466505
}
467506

468-
Ok(&fee_structure.fee_tiers[0])
507+
Ok(tier)
469508
}
470509

471510
fn determine_spot_fee_tier<'a>(

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

Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -917,3 +917,164 @@ mod calculate_fee_for_fulfillment_with_serum {
917917
assert_eq!(filler_reward, 2000);
918918
}
919919
}
920+
921+
mod calcuate_fee_tiers {
922+
923+
use crate::math::constants::QUOTE_PRECISION_U64;
924+
use crate::math::constants::{
925+
FEE_DENOMINATOR, FEE_PERCENTAGE_DENOMINATOR, MAX_REFERRER_REWARD_EPOCH_UPPER_BOUND,
926+
};
927+
use crate::math::fees::{determine_user_fee_tier, OrderFillerRewardStructure};
928+
use crate::state::state::{FeeStructure, FeeTier};
929+
use crate::state::user::MarketType;
930+
use crate::state::user::UserStats;
931+
932+
#[test]
933+
fn test_calc_taker_tiers() {
934+
let mut taker_stats = UserStats::default();
935+
let mut fee_tiers = [FeeTier::default(); 10];
936+
937+
fee_tiers[0] = FeeTier {
938+
fee_numerator: 35,
939+
fee_denominator: FEE_DENOMINATOR, // 3.5 bps
940+
maker_rebate_numerator: 25,
941+
maker_rebate_denominator: FEE_DENOMINATOR * 10, // .25 bps
942+
referrer_reward_numerator: 10,
943+
referrer_reward_denominator: FEE_PERCENTAGE_DENOMINATOR, // 10% of taker fee
944+
referee_fee_numerator: 5,
945+
referee_fee_denominator: FEE_PERCENTAGE_DENOMINATOR, // 5%
946+
};
947+
fee_tiers[1] = FeeTier {
948+
fee_numerator: 30,
949+
fee_denominator: FEE_DENOMINATOR, // 3 bps
950+
maker_rebate_numerator: 25,
951+
maker_rebate_denominator: FEE_DENOMINATOR * 10, // .25 bps
952+
referrer_reward_numerator: 10,
953+
referrer_reward_denominator: FEE_PERCENTAGE_DENOMINATOR, // 10% of taker fee
954+
referee_fee_numerator: 5,
955+
referee_fee_denominator: FEE_PERCENTAGE_DENOMINATOR, // 5%
956+
};
957+
fee_tiers[2] = FeeTier {
958+
fee_numerator: 275,
959+
fee_denominator: FEE_DENOMINATOR * 10, // 2.75 bps
960+
maker_rebate_numerator: 25,
961+
maker_rebate_denominator: FEE_DENOMINATOR * 10, // .25 bps
962+
referrer_reward_numerator: 10,
963+
referrer_reward_denominator: FEE_PERCENTAGE_DENOMINATOR, // 10% of taker fee
964+
referee_fee_numerator: 5,
965+
referee_fee_denominator: FEE_PERCENTAGE_DENOMINATOR, // 5%
966+
};
967+
fee_tiers[3] = FeeTier {
968+
fee_numerator: 25,
969+
fee_denominator: FEE_DENOMINATOR, // 2.5 bps
970+
maker_rebate_numerator: 25,
971+
maker_rebate_denominator: FEE_DENOMINATOR * 10, // .25 bps
972+
referrer_reward_numerator: 10,
973+
referrer_reward_denominator: FEE_PERCENTAGE_DENOMINATOR, // 10% of taker fee
974+
referee_fee_numerator: 5,
975+
referee_fee_denominator: FEE_PERCENTAGE_DENOMINATOR, // 5%
976+
};
977+
fee_tiers[4] = FeeTier {
978+
fee_numerator: 225,
979+
fee_denominator: FEE_DENOMINATOR * 10, // 2.25 bps
980+
maker_rebate_numerator: 25,
981+
maker_rebate_denominator: FEE_DENOMINATOR * 10, // .25 bps
982+
referrer_reward_numerator: 10,
983+
referrer_reward_denominator: FEE_PERCENTAGE_DENOMINATOR, // 10% of taker fee
984+
referee_fee_numerator: 5,
985+
referee_fee_denominator: FEE_PERCENTAGE_DENOMINATOR, // 5%
986+
};
987+
fee_tiers[5] = FeeTier {
988+
fee_numerator: 20,
989+
fee_denominator: FEE_DENOMINATOR, // 2 bps
990+
maker_rebate_numerator: 25,
991+
maker_rebate_denominator: FEE_DENOMINATOR * 10, // .25 bps
992+
referrer_reward_numerator: 10,
993+
referrer_reward_denominator: FEE_PERCENTAGE_DENOMINATOR, // 10% of taker fee
994+
referee_fee_numerator: 5,
995+
referee_fee_denominator: FEE_PERCENTAGE_DENOMINATOR, // 5%
996+
};
997+
let fee_structure = FeeStructure {
998+
fee_tiers,
999+
filler_reward_structure: OrderFillerRewardStructure {
1000+
reward_numerator: 10,
1001+
reward_denominator: FEE_PERCENTAGE_DENOMINATOR,
1002+
time_based_reward_lower_bound: 10_000, // 1 cent
1003+
},
1004+
flat_filler_fee: 10_000,
1005+
referrer_reward_epoch_upper_bound: MAX_REFERRER_REWARD_EPOCH_UPPER_BOUND,
1006+
};
1007+
1008+
let res = determine_user_fee_tier(&taker_stats, &fee_structure, &MarketType::Perp, false)
1009+
.unwrap();
1010+
assert_eq!(res.fee_numerator, 35);
1011+
assert_eq!(res.fee_denominator, 100000);
1012+
1013+
assert_eq!(res.maker_rebate_numerator, 25);
1014+
assert_eq!(res.maker_rebate_denominator, 1000000);
1015+
1016+
taker_stats.taker_volume_30d = 80_000_000 * QUOTE_PRECISION_U64;
1017+
1018+
let res: FeeTier =
1019+
determine_user_fee_tier(&taker_stats, &fee_structure, &MarketType::Perp, false)
1020+
.unwrap();
1021+
assert_eq!(res.fee_numerator, 25);
1022+
assert_eq!(res.fee_denominator, 100000);
1023+
1024+
assert_eq!(res.maker_rebate_numerator, 25);
1025+
assert_eq!(res.maker_rebate_denominator, 1000000);
1026+
1027+
taker_stats.if_staked_gov_token_amount = 50_000 * QUOTE_PRECISION_U64 - 8970; // still counts for 50K tier
1028+
let res: FeeTier =
1029+
determine_user_fee_tier(&taker_stats, &fee_structure, &MarketType::Perp, false)
1030+
.unwrap();
1031+
1032+
assert_eq!(res.fee_numerator, 20);
1033+
assert_eq!(res.fee_denominator, 100000);
1034+
1035+
assert_eq!(res.maker_rebate_numerator, 30);
1036+
assert_eq!(res.maker_rebate_denominator, 1000000);
1037+
1038+
taker_stats.if_staked_gov_token_amount = 150_000 * QUOTE_PRECISION_U64 - 8970; // still counts for 100K tier
1039+
let res: FeeTier =
1040+
determine_user_fee_tier(&taker_stats, &fee_structure, &MarketType::Perp, false)
1041+
.unwrap();
1042+
1043+
assert_eq!(res.fee_numerator, 18);
1044+
assert_eq!(res.fee_denominator, 100000);
1045+
1046+
assert_eq!(res.maker_rebate_numerator, 32);
1047+
assert_eq!(res.maker_rebate_denominator, 1000000);
1048+
1049+
taker_stats.if_staked_gov_token_amount = 800_000 * QUOTE_PRECISION_U64;
1050+
let res: FeeTier =
1051+
determine_user_fee_tier(&taker_stats, &fee_structure, &MarketType::Perp, false)
1052+
.unwrap();
1053+
1054+
assert_eq!(res.fee_numerator, 15);
1055+
assert_eq!(res.fee_denominator, 100000);
1056+
1057+
assert_eq!(res.maker_rebate_numerator, 35);
1058+
assert_eq!(res.maker_rebate_denominator, 1000000);
1059+
1060+
taker_stats.taker_volume_30d = 280_000_000 * QUOTE_PRECISION_U64;
1061+
let res: FeeTier =
1062+
determine_user_fee_tier(&taker_stats, &fee_structure, &MarketType::Perp, false)
1063+
.unwrap();
1064+
1065+
assert_eq!(res.fee_numerator, 12);
1066+
assert_eq!(res.fee_denominator, 100000);
1067+
1068+
assert_eq!(res.maker_rebate_numerator, 35);
1069+
assert_eq!(res.maker_rebate_denominator, 1000000);
1070+
1071+
let res: FeeTier =
1072+
determine_user_fee_tier(&taker_stats, &fee_structure, &MarketType::Perp, true).unwrap();
1073+
1074+
assert_eq!(res.fee_numerator, 35);
1075+
assert_eq!(res.fee_denominator, 100000);
1076+
1077+
assert_eq!(res.maker_rebate_numerator, 25);
1078+
assert_eq!(res.maker_rebate_denominator, 1000000);
1079+
}
1080+
}

0 commit comments

Comments
 (0)