Skip to content

Commit e9510bf

Browse files
authored
subtract exchange fees from amount settled (#1849)
* subtract exchange fees from amount settled * add exchange fee scalar to settling * use percents isntead of scalars
1 parent 4b751fd commit e9510bf

File tree

7 files changed

+115
-40
lines changed

7 files changed

+115
-40
lines changed

programs/drift/src/instructions/admin.rs

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -990,7 +990,8 @@ pub fn handle_initialize_perp_market(
990990
lp_status: 0,
991991
padding1: 0,
992992
last_fill_price: 0,
993-
padding: [0; 24],
993+
lp_exchange_fee_excluscion_scalar: 1,
994+
padding: [0; 23],
994995
amm: AMM {
995996
oracle: *ctx.accounts.oracle.key,
996997
oracle_source,
@@ -3970,17 +3971,32 @@ pub fn handle_update_perp_market_min_order_size(
39703971
)]
39713972
pub fn handle_update_perp_market_lp_pool_fee_transfer_scalar(
39723973
ctx: Context<AdminUpdatePerpMarket>,
3973-
lp_fee_transfer_scalar: u8,
3974+
optional_lp_fee_transfer_scalar: Option<u8>,
3975+
optional_lp_net_pnl_transfer_scalar: Option<u8>,
39743976
) -> Result<()> {
39753977
let perp_market = &mut load_mut!(ctx.accounts.perp_market)?;
39763978
msg!("perp market {}", perp_market.market_index);
3977-
msg!(
3978-
"perp_market.: {:?} -> {:?}",
3979-
perp_market.lp_fee_transfer_scalar,
3980-
lp_fee_transfer_scalar
3981-
);
39823979

3983-
perp_market.lp_fee_transfer_scalar = lp_fee_transfer_scalar;
3980+
if let Some(lp_fee_transfer_scalar) = optional_lp_fee_transfer_scalar {
3981+
msg!(
3982+
"perp_market.: {:?} -> {:?}",
3983+
perp_market.lp_fee_transfer_scalar,
3984+
lp_fee_transfer_scalar
3985+
);
3986+
3987+
perp_market.lp_fee_transfer_scalar = lp_fee_transfer_scalar;
3988+
}
3989+
3990+
if let Some(lp_net_pnl_transfer_scalar) = optional_lp_net_pnl_transfer_scalar {
3991+
msg!(
3992+
"perp_market.: {:?} -> {:?}",
3993+
perp_market.lp_exchange_fee_excluscion_scalar,
3994+
lp_net_pnl_transfer_scalar
3995+
);
3996+
3997+
perp_market.lp_exchange_fee_excluscion_scalar = lp_net_pnl_transfer_scalar;
3998+
}
3999+
39844000
Ok(())
39854001
}
39864002

programs/drift/src/lib.rs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1064,9 +1064,14 @@ pub mod drift {
10641064

10651065
pub fn update_perp_market_lp_pool_fee_transfer_scalar(
10661066
ctx: Context<AdminUpdatePerpMarket>,
1067-
lp_fee_transfer_scalar: u8,
1067+
optional_lp_fee_transfer_scalar: Option<u8>,
1068+
optional_lp_net_pnl_transfer_scalar: Option<u8>,
10681069
) -> Result<()> {
1069-
handle_update_perp_market_lp_pool_fee_transfer_scalar(ctx, lp_fee_transfer_scalar)
1070+
handle_update_perp_market_lp_pool_fee_transfer_scalar(
1071+
ctx,
1072+
optional_lp_fee_transfer_scalar,
1073+
optional_lp_net_pnl_transfer_scalar,
1074+
)
10701075
}
10711076

10721077
pub fn settle_expired_market_pools_to_revenue_pool(

programs/drift/src/state/perp_market.rs

Lines changed: 32 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -245,7 +245,8 @@ pub struct PerpMarket {
245245
pub lp_status: u8,
246246
pub padding1: u16,
247247
pub last_fill_price: u64,
248-
pub padding: [u8; 24],
248+
pub lp_exchange_fee_excluscion_scalar: u8,
249+
pub padding: [u8; 23],
249250
}
250251

251252
impl Default for PerpMarket {
@@ -287,11 +288,12 @@ impl Default for PerpMarket {
287288
high_leverage_margin_ratio_maintenance: 0,
288289
protected_maker_limit_price_divisor: 0,
289290
protected_maker_dynamic_divisor: 0,
290-
lp_fee_transfer_scalar: 1,
291+
lp_fee_transfer_scalar: 0,
291292
lp_status: 0,
292293
padding1: 0,
293294
last_fill_price: 0,
294-
padding: [0; 24],
295+
lp_exchange_fee_excluscion_scalar: 0,
296+
padding: [0; 23],
295297
}
296298
}
297299
}
@@ -1709,6 +1711,7 @@ pub struct AmmCache {
17091711
pub struct CacheInfo {
17101712
pub last_fee_pool_token_amount: u128,
17111713
pub last_net_pnl_pool_token_amount: i128,
1714+
pub last_exchange_fees: u128,
17121715
/// BASE PRECISION
17131716
pub position: i64,
17141717
pub slot: u64,
@@ -1745,6 +1748,7 @@ impl Default for CacheInfo {
17451748
oracle: Pubkey::default(),
17461749
last_fee_pool_token_amount: 0u128,
17471750
last_net_pnl_pool_token_amount: 0i128,
1751+
last_exchange_fees: 0u128,
17481752
last_settle_amount: 0u64,
17491753
last_settle_slot: 0u64,
17501754
oracle_source: 0u8,
@@ -1856,9 +1860,11 @@ impl<'a> AccountZeroCopyMut<'a, CacheInfo, AmmCacheFixed> {
18561860
perp_market: &PerpMarket,
18571861
quote_market: &SpotMarket,
18581862
) -> DriftResult<()> {
1859-
if perp_market.lp_fee_transfer_scalar == 0 {
1863+
if perp_market.lp_fee_transfer_scalar == 0
1864+
&& perp_market.lp_exchange_fee_excluscion_scalar == 0
1865+
{
18601866
msg!(
1861-
"lp_fee_transfer_scalar is 0 for perp market {}. not updating quote amount owed in cache",
1867+
"lp_fee_transfer_scalar and lp_net_pnl_transfer_scalar are 0 for perp market {}. not updating quote amount owed in cache",
18621868
perp_market.market_index
18631869
);
18641870
return Ok(());
@@ -1888,29 +1894,37 @@ impl<'a> AccountZeroCopyMut<'a, CacheInfo, AmmCacheFixed> {
18881894

18891895
if cached_info.last_net_pnl_pool_token_amount == 0
18901896
&& cached_info.last_fee_pool_token_amount == 0
1897+
&& cached_info.last_exchange_fees == 0
18911898
{
18921899
cached_info.last_fee_pool_token_amount = fee_pool_token_amount;
18931900
cached_info.last_net_pnl_pool_token_amount = net_pnl_pool_token_amount;
1901+
cached_info.last_exchange_fees = perp_market.amm.total_exchange_fee;
18941902
return Ok(());
18951903
}
18961904

1897-
let amount_to_send = amm_amount_available
1898-
.abs_diff(cached_info.get_last_available_amm_balance()?)
1899-
.safe_div_ceil(perp_market.lp_fee_transfer_scalar as u128)?
1900-
.cast::<u64>()?;
1905+
let exchange_fee_delta = perp_market
1906+
.amm
1907+
.total_exchange_fee
1908+
.saturating_sub(cached_info.last_exchange_fees);
1909+
1910+
let amount_to_send_to_lp_pool = amm_amount_available
1911+
.safe_sub(cached_info.get_last_available_amm_balance()?)?
1912+
.safe_mul(perp_market.lp_fee_transfer_scalar as i128)?
1913+
.safe_div_ceil(100)?
1914+
.safe_sub(
1915+
exchange_fee_delta
1916+
.cast::<i128>()?
1917+
.safe_mul(perp_market.lp_exchange_fee_excluscion_scalar as i128)?
1918+
.safe_div_ceil(100)?,
1919+
)?;
19011920

1902-
if amm_amount_available < cached_info.get_last_available_amm_balance()? {
1903-
cached_info.quote_owed_from_lp_pool = cached_info
1904-
.quote_owed_from_lp_pool
1905-
.safe_add(amount_to_send.cast::<i64>()?)?;
1906-
} else {
1907-
cached_info.quote_owed_from_lp_pool = cached_info
1908-
.quote_owed_from_lp_pool
1909-
.safe_sub(amount_to_send.cast::<i64>()?)?;
1910-
}
1921+
cached_info.quote_owed_from_lp_pool = cached_info
1922+
.quote_owed_from_lp_pool
1923+
.safe_sub(amount_to_send_to_lp_pool.cast::<i64>()?)?;
19111924

19121925
cached_info.last_fee_pool_token_amount = fee_pool_token_amount;
19131926
cached_info.last_net_pnl_pool_token_amount = net_pnl_pool_token_amount;
1927+
cached_info.last_exchange_fees = perp_market.amm.total_exchange_fee;
19141928

19151929
Ok(())
19161930
}

sdk/src/adminClient.ts

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6017,11 +6017,13 @@ export class AdminClient extends DriftClient {
60176017

60186018
public async updatePerpMarketLpPoolFeeTransferScalar(
60196019
marketIndex: number,
6020-
lpFeeTransferScalar: number
6020+
lpFeeTransferScalar?: number,
6021+
lpExchangeFeeExcluscionScalar?: number
60216022
) {
60226023
const ix = await this.getUpdatePerpMarketLpPoolFeeTransferScalarIx(
60236024
marketIndex,
6024-
lpFeeTransferScalar
6025+
lpFeeTransferScalar,
6026+
lpExchangeFeeExcluscionScalar
60256027
);
60266028
const tx = await this.buildTransaction(ix);
60276029
const { txSig } = await this.sendTransaction(tx, [], this.opts);
@@ -6030,10 +6032,12 @@ export class AdminClient extends DriftClient {
60306032

60316033
public async getUpdatePerpMarketLpPoolFeeTransferScalarIx(
60326034
marketIndex: number,
6033-
lpFeeTransferScalar: number
6035+
lpFeeTransferScalar?: number,
6036+
lpExchangeFeeExcluscionScalar?: number
60346037
): Promise<TransactionInstruction> {
6035-
return await this.program.instruction.updatePerpMarketLpPoolFeeTransferScalar(
6036-
lpFeeTransferScalar,
6038+
return this.program.instruction.updatePerpMarketLpPoolFeeTransferScalar(
6039+
lpFeeTransferScalar ?? null,
6040+
lpExchangeFeeExcluscionScalar ?? null,
60376041
{
60386042
accounts: {
60396043
admin: this.isSubscribed

sdk/src/idl/drift.json

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4748,8 +4748,16 @@
47484748
],
47494749
"args": [
47504750
{
4751-
"name": "lpFeeTransferScalar",
4752-
"type": "u8"
4751+
"name": "optionalLpFeeTransferScalar",
4752+
"type": {
4753+
"option": "u8"
4754+
}
4755+
},
4756+
{
4757+
"name": "optionalLpNetPnlTransferScalar",
4758+
"type": {
4759+
"option": "u8"
4760+
}
47534761
}
47544762
]
47554763
},
@@ -10295,12 +10303,16 @@
1029510303
"name": "lastFillPrice",
1029610304
"type": "u64"
1029710305
},
10306+
{
10307+
"name": "lpExchangeFeeExcluscionScalar",
10308+
"type": "u8"
10309+
},
1029810310
{
1029910311
"name": "padding",
1030010312
"type": {
1030110313
"array": [
1030210314
"u8",
10303-
24
10315+
23
1030410316
]
1030510317
}
1030610318
}
@@ -13478,6 +13490,10 @@
1347813490
"name": "lastNetPnlPoolTokenAmount",
1347913491
"type": "i128"
1348013492
},
13493+
{
13494+
"name": "lastExchangeFees",
13495+
"type": "u128"
13496+
},
1348113497
{
1348213498
"name": "position",
1348313499
"docs": [

sdk/src/types.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -864,6 +864,7 @@ export type PerpMarketAccount = {
864864
lastFillPrice: BN;
865865

866866
lpFeeTransferScalar: number;
867+
lpExchangeFeeExcluscionScalar: number;
867868
lpStatus: number;
868869
};
869870

@@ -1732,6 +1733,7 @@ export type CacheInfo = {
17321733
oraclePrice: BN;
17331734
oracleDelay: BN;
17341735
oracleConfidence: BN;
1736+
lastExchangeFees: BN;
17351737
lastFeePoolTokenAmount: BN;
17361738
lastNetPnlPoolTokenAmount: BN;
17371739
lastSettleAmount: BN;

tests/lpPool.ts

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -846,6 +846,22 @@ describe('LP Pool', () => {
846846
lpPoolKey
847847
)) as LPPoolAccount;
848848

849+
// Exclude 25% of exchange fees, put 100 dollars there to make sure that the
850+
await adminClient.updatePerpMarketLpPoolFeeTransferScalar(0, 100, 25);
851+
await adminClient.updatePerpMarketLpPoolFeeTransferScalar(1, 100, 0);
852+
await adminClient.updatePerpMarketLpPoolFeeTransferScalar(2, 100, 0);
853+
854+
const perpMarket = adminClient.getPerpMarketAccount(0);
855+
perpMarket.amm.totalExchangeFee = perpMarket.amm.totalExchangeFee.add(
856+
QUOTE_PRECISION.muln(100)
857+
);
858+
await overWritePerpMarket(
859+
adminClient,
860+
bankrunContextWrapper,
861+
perpMarket.pubkey,
862+
perpMarket
863+
);
864+
849865
await adminClient.depositIntoPerpMarketFeePool(
850866
0,
851867
new BN(100).mul(QUOTE_PRECISION),
@@ -880,7 +896,7 @@ describe('LP Pool', () => {
880896
assert(
881897
ammCache.cache[0].quoteOwedFromLpPool.eq(
882898
ammCacheBeforeAdjust.cache[0].quoteOwedFromLpPool.sub(
883-
new BN(100).mul(QUOTE_PRECISION)
899+
new BN(75).mul(QUOTE_PRECISION)
884900
)
885901
)
886902
);
@@ -901,7 +917,7 @@ describe('LP Pool', () => {
901917
const lpAumAfterUpdateCacheBeforeSettle = lpPool.lastAum;
902918
assert(
903919
lpAumAfterUpdateCacheBeforeSettle.eq(
904-
lpAumAfterDeposit.add(new BN(200).mul(QUOTE_PRECISION))
920+
lpAumAfterDeposit.add(new BN(175).mul(QUOTE_PRECISION))
905921
)
906922
);
907923

@@ -932,7 +948,7 @@ describe('LP Pool', () => {
932948
// Expected transfers per pool are capital constrained by the actual balances
933949
const expectedTransfer0 = BN.min(
934950
ammCache.cache[0].quoteOwedFromLpPool.muln(-1),
935-
pnlPoolBalance0.add(feePoolBalance0)
951+
pnlPoolBalance0.add(feePoolBalance0).sub(QUOTE_PRECISION.muln(25))
936952
);
937953
const expectedTransfer1 = BN.min(
938954
ammCache.cache[1].quoteOwedFromLpPool.muln(-1),
@@ -1174,7 +1190,9 @@ describe('LP Pool', () => {
11741190
lpPoolKey
11751191
)) as LPPoolAccount;
11761192

1177-
assert(ammCache.cache[0].quoteOwedFromLpPool.eq(owedAmount.divn(2)));
1193+
expect(
1194+
ammCache.cache[0].quoteOwedFromLpPool.toNumber()
1195+
).to.be.approximately(owedAmount.divn(2).toNumber(), 1);
11781196
assert(constituent.tokenBalance.eq(ZERO));
11791197
assert(lpPool.lastAum.eq(ZERO));
11801198

0 commit comments

Comments
 (0)