Skip to content

Commit 0871d9d

Browse files
authored
testing: ix: settle perp to dlp, insufficient balance edge case and improvements (#1688)
* finish edge case test * aum check also passes * prettify
1 parent a4a2f02 commit 0871d9d

File tree

11 files changed

+392
-217
lines changed

11 files changed

+392
-217
lines changed

programs/drift/src/instructions/admin.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1141,12 +1141,12 @@ pub fn handle_update_init_amm_cache_info<'c: 'info, 'info>(
11411141
.amm
11421142
.historical_oracle_data
11431143
.last_oracle_price_twap;
1144-
cache.last_fee_pool_balance = get_token_amount(
1144+
cache.last_fee_pool_token_amount = get_token_amount(
11451145
perp_market.amm.fee_pool.scaled_balance,
11461146
&quote_market,
11471147
perp_market.amm.fee_pool.balance_type(),
11481148
)?;
1149-
cache.last_net_pnl_pool_balance = get_token_amount(
1149+
cache.last_net_pnl_pool_token_amount = get_token_amount(
11501150
perp_market.pnl_pool.scaled_balance,
11511151
&quote_market,
11521152
perp_market.pnl_pool.balance_type(),

programs/drift/src/instructions/keeper.rs

Lines changed: 90 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ use anchor_lang::Discriminator;
66
use anchor_spl::associated_token::get_associated_token_address_with_program_id;
77
use anchor_spl::token_interface::Mint;
88
use anchor_spl::token_interface::{TokenAccount, TokenInterface};
9-
use num_integer::Integer;
109
use solana_program::instruction::Instruction;
1110
use solana_program::pubkey;
1211
use solana_program::sysvar::instructions::{
@@ -36,7 +35,6 @@ use crate::math::oracle::is_oracle_valid_for_action;
3635
use crate::math::oracle::DriftAction;
3736
use crate::math::orders::{estimate_price_from_side, find_bids_and_asks_from_users};
3837
use crate::math::position::calculate_base_asset_value_and_pnl_with_oracle_price;
39-
use crate::math::safe_math::SafeDivFloor;
4038
use crate::math::safe_math::SafeMath;
4139
use crate::math::spot_balance::get_token_amount;
4240
use crate::math::spot_withdraw::validate_spot_market_vault_amount;
@@ -3008,12 +3006,14 @@ pub fn handle_settle_perp_to_lp_pool<'c: 'info, 'info>(
30083006
.cast::<i128>()?
30093007
.safe_sub(calculate_net_user_pnl(&perp_market.amm, oracle_data.price)?)?;
30103008

3011-
let amount_available =
3009+
let amm_amount_available =
30123010
net_pnl_pool_token_amount.safe_add(fee_pool_token_amount.cast::<i128>()?)?;
30133011

3014-
if cached_info.last_net_pnl_pool_balance == 0 && cached_info.last_fee_pool_balance == 0 {
3015-
cached_info.last_fee_pool_balance = fee_pool_token_amount;
3016-
cached_info.last_net_pnl_pool_balance = net_pnl_pool_token_amount;
3012+
if cached_info.last_net_pnl_pool_token_amount == 0
3013+
&& cached_info.last_fee_pool_token_amount == 0
3014+
{
3015+
cached_info.last_fee_pool_token_amount = fee_pool_token_amount;
3016+
cached_info.last_net_pnl_pool_token_amount = net_pnl_pool_token_amount;
30173017
continue;
30183018
}
30193019

@@ -3090,16 +3090,26 @@ pub fn handle_settle_perp_to_lp_pool<'c: 'info, 'info>(
30903090
continue;
30913091
}
30923092

3093-
let amount_to_send = amount_available
3093+
let amount_to_send = amm_amount_available
30943094
.abs_diff(cached_info.get_last_available_amm_balance()?)
3095-
.safe_div_ceil(perp_market.lp_fee_transfer_scalar as u128)?;
3096-
if amount_available < 0 {
3097-
controller::token::receive(
3095+
.safe_div_ceil(perp_market.lp_fee_transfer_scalar as u128)?
3096+
.cast::<u64>()?;
3097+
if amm_amount_available < cached_info.get_last_available_amm_balance()? {
3098+
let amount_to_send = if amount_to_send > constituent_token_account.amount {
3099+
cached_info.quote_owed_from_lp +=
3100+
amount_to_send.saturating_sub(constituent_token_account.amount) as i64;
3101+
constituent_token_account.amount
3102+
} else {
3103+
amount_to_send
3104+
};
3105+
3106+
controller::token::send_from_program_vault(
30983107
&ctx.accounts.token_program,
30993108
constituent_token_account,
31003109
&ctx.accounts.quote_token_vault,
31013110
&ctx.accounts.drift_signer,
3102-
amount_to_send.cast::<u64>()?,
3111+
state.signer_nonce,
3112+
amount_to_send,
31033113
&Some(mint),
31043114
)?;
31053115

@@ -3115,58 +3125,78 @@ pub fn handle_settle_perp_to_lp_pool<'c: 'info, 'info>(
31153125
lp_pool.last_aum = lp_pool
31163126
.last_aum
31173127
.saturating_sub(amount_to_send.cast::<u128>()?);
3118-
} else {
3119-
controller::token::send_from_program_vault(
3120-
&ctx.accounts.token_program,
3121-
&ctx.accounts.quote_token_vault,
3122-
constituent_token_account,
3123-
&ctx.accounts.drift_signer,
3124-
state.signer_nonce,
3125-
amount_to_send.cast::<u64>()?,
3126-
&Some(mint),
3127-
)?;
31283128

3129-
// If both fees and pnl are up, take from both equally
3130-
let precision_increase = SPOT_BALANCE_PRECISION.safe_div(QUOTE_PRECISION)?;
3131-
if fee_pool_token_amount > cached_info.last_fee_pool_balance
3132-
&& net_pnl_pool_token_amount > cached_info.last_net_pnl_pool_balance
3133-
{
3134-
let abs_scaled_fee_pool_token_delta = fee_pool_token_amount
3135-
.abs_diff(cached_info.last_fee_pool_balance)
3136-
.safe_div_ceil(perp_market.lp_fee_transfer_scalar as u128)?;
3137-
3138-
let abs_scaled_pnl_pool_token_delta = net_pnl_pool_token_amount
3139-
.abs_diff(cached_info.last_net_pnl_pool_balance)
3140-
.safe_div_ceil(perp_market.lp_fee_transfer_scalar as u128)?;
3141-
3142-
msg!(
3143-
"abs scaled fee pool token delta = {} abs scaled pnl pool token delta = {}",
3144-
abs_scaled_fee_pool_token_delta,
3145-
abs_scaled_pnl_pool_token_delta
3146-
);
3147-
perp_market.amm.fee_pool.decrease_balance(
3148-
abs_scaled_fee_pool_token_delta.safe_mul(precision_increase)?,
3149-
)?;
3150-
perp_market.pnl_pool.decrease_balance(
3151-
abs_scaled_pnl_pool_token_delta.safe_mul(precision_increase)?,
3129+
cached_info.last_fee_pool_token_amount =
3130+
fee_pool_token_amount.safe_add(amount_to_send as u128)?;
3131+
} else {
3132+
let amount_to_send = if cached_info.quote_owed_from_lp > 0 {
3133+
if amount_to_send > cached_info.quote_owed_from_lp as u64 {
3134+
cached_info.quote_owed_from_lp = 0;
3135+
amount_to_send - cached_info.quote_owed_from_lp as u64
3136+
} else {
3137+
cached_info.quote_owed_from_lp -= amount_to_send as i64;
3138+
0
3139+
}
3140+
} else {
3141+
amount_to_send
3142+
};
3143+
3144+
if amount_to_send > 0 {
3145+
controller::token::send_from_program_vault(
3146+
&ctx.accounts.token_program,
3147+
&ctx.accounts.quote_token_vault,
3148+
constituent_token_account,
3149+
&ctx.accounts.drift_signer,
3150+
state.signer_nonce,
3151+
amount_to_send.cast::<u64>()?,
3152+
&Some(mint),
31523153
)?;
3153-
} else if fee_pool_token_amount > cached_info.last_fee_pool_balance {
3154-
perp_market
3155-
.amm
3156-
.fee_pool
3157-
.decrease_balance((amount_to_send as u128).safe_mul(precision_increase)?)?;
3158-
} else if net_pnl_pool_token_amount > cached_info.last_net_pnl_pool_balance {
3159-
perp_market
3160-
.pnl_pool
3161-
.decrease_balance((amount_to_send as u128).safe_mul(precision_increase)?)?;
3162-
}
3154+
// If both fees and pnl are up, take from both equally
3155+
let precision_increase = SPOT_BALANCE_PRECISION.safe_div(QUOTE_PRECISION)?;
3156+
if fee_pool_token_amount > cached_info.last_fee_pool_token_amount
3157+
&& net_pnl_pool_token_amount > cached_info.last_net_pnl_pool_token_amount
3158+
{
3159+
let abs_scaled_fee_pool_token_delta = fee_pool_token_amount
3160+
.abs_diff(cached_info.last_fee_pool_token_amount)
3161+
.safe_div_ceil(perp_market.lp_fee_transfer_scalar as u128)?;
31633162

3164-
lp_pool.cumulative_usdc_received_from_perp_markets = lp_pool
3165-
.cumulative_usdc_received_from_perp_markets
3166-
.saturating_add(amount_to_send.cast::<u128>()?);
3167-
lp_pool.last_aum = lp_pool
3168-
.last_aum
3169-
.saturating_add(amount_to_send.cast::<u128>()?);
3163+
let abs_scaled_pnl_pool_token_delta = net_pnl_pool_token_amount
3164+
.abs_diff(cached_info.last_net_pnl_pool_token_amount)
3165+
.safe_div_ceil(perp_market.lp_fee_transfer_scalar as u128)?;
3166+
3167+
perp_market.amm.fee_pool.decrease_balance(
3168+
abs_scaled_fee_pool_token_delta.safe_mul(precision_increase)?,
3169+
)?;
3170+
perp_market.pnl_pool.decrease_balance(
3171+
abs_scaled_pnl_pool_token_delta.safe_mul(precision_increase)?,
3172+
)?;
3173+
3174+
cached_info.last_fee_pool_token_amount =
3175+
fee_pool_token_amount.safe_sub(abs_scaled_fee_pool_token_delta)?;
3176+
cached_info.last_net_pnl_pool_token_amount = net_pnl_pool_token_amount
3177+
.safe_sub(abs_scaled_pnl_pool_token_delta.cast::<i128>()?)?;
3178+
} else if fee_pool_token_amount > cached_info.last_fee_pool_token_amount {
3179+
perp_market
3180+
.amm
3181+
.fee_pool
3182+
.decrease_balance((amount_to_send as u128).safe_mul(precision_increase)?)?;
3183+
cached_info.last_fee_pool_token_amount =
3184+
fee_pool_token_amount.safe_sub(amount_to_send as u128)?;
3185+
} else if net_pnl_pool_token_amount > cached_info.last_net_pnl_pool_token_amount {
3186+
perp_market
3187+
.pnl_pool
3188+
.decrease_balance((amount_to_send as u128).safe_mul(precision_increase)?)?;
3189+
cached_info.last_net_pnl_pool_token_amount =
3190+
net_pnl_pool_token_amount.safe_sub(amount_to_send.cast::<i128>()?)?;
3191+
}
3192+
3193+
lp_pool.cumulative_usdc_received_from_perp_markets = lp_pool
3194+
.cumulative_usdc_received_from_perp_markets
3195+
.saturating_add(amount_to_send.cast::<u128>()?);
3196+
lp_pool.last_aum = lp_pool
3197+
.last_aum
3198+
.saturating_add(amount_to_send.cast::<u128>()?);
3199+
}
31703200
}
31713201

31723202
lp_pool.last_aum_ts = clock.unix_timestamp;
@@ -3175,8 +3205,6 @@ pub fn handle_settle_perp_to_lp_pool<'c: 'info, 'info>(
31753205
constituent_token_account.reload()?;
31763206
constituent.sync_token_balance(constituent_token_account.amount);
31773207

3178-
cached_info.last_fee_pool_balance = fee_pool_token_amount;
3179-
cached_info.last_net_pnl_pool_balance = net_pnl_pool_token_amount;
31803208
cached_info.last_settle_amount = amount_to_send.cast::<u64>()?;
31813209
cached_info.last_settle_ts = Clock::get()?.unix_timestamp;
31823210
}
@@ -3197,8 +3225,6 @@ pub fn handle_update_amm_cache<'c: 'info, 'info>(
31973225
let mut amm_cache: AccountZeroCopyMut<'_, CacheInfo, _> =
31983226
ctx.accounts.amm_cache.load_zc_mut()?;
31993227

3200-
let quote_market = &ctx.accounts.quote_market.load()?;
3201-
32023228
let expected_pda = &Pubkey::create_program_address(
32033229
&[
32043230
AMM_POSITIONS_CACHE.as_ref(),

programs/drift/src/instructions/lp_pool.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,8 @@ use anchor_spl::token_interface::{Mint, TokenAccount, TokenInterface};
55

66
use crate::{
77
controller::{
8-
self,
8+
self, lp,
99
spot_balance::update_spot_balances,
10-
lp,
1110
token::{burn_tokens, mint_tokens},
1211
},
1312
error::ErrorCode,

programs/drift/src/state/lp_pool.rs

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ pub const CONSTITUENT_TARGET_BASE_PDA_SEED: &str = "constituent_target_base";
2323
pub const CONSTITUENT_VAULT_PDA_SEED: &str = "CONSTITUENT_VAULT";
2424
pub const LP_POOL_TOKEN_VAULT_PDA_SEED: &str = "LP_POOL_TOKEN_VAULT";
2525

26+
pub const MAX_SWAP_FEE: i64 = 75_000; // 0.75% in PERCENTAGE_PRECISION
27+
2628
#[cfg(test)]
2729
mod tests;
2830

@@ -292,7 +294,7 @@ impl LPPool {
292294
out_spot_market,
293295
out_oracle,
294296
out_constituent,
295-
out_amount.cast::<i64>()?,
297+
out_amount.cast::<i64>()?.safe_mul(-1_i64)?,
296298
out_target_weight,
297299
)?;
298300
let out_fee_amount = out_amount
@@ -326,15 +328,26 @@ impl LPPool {
326328
target_weight
327329
);
328330

331+
let adjusted_aum = self
332+
.last_aum
333+
.cast::<i128>()?
334+
.safe_add(
335+
amount
336+
.cast::<i128>()?
337+
.safe_mul(oracle.price.cast::<i128>()?)?
338+
.safe_div(10_i128.pow(spot_market.decimals as u32))?,
339+
)?
340+
.cast::<u128>()?;
329341
let weight_after =
330-
constituent.get_weight(oracle.price, spot_market, amount, self.last_aum)?;
342+
constituent.get_weight(oracle.price, spot_market, amount, adjusted_aum)?;
331343
msg!(
332344
"constituent {}: weight_after: {} target_weight: {}",
333345
constituent.constituent_index,
334346
weight_after,
335347
target_weight
336348
);
337349
let fee = constituent.get_fee_to_charge(weight_after, target_weight)?;
350+
let fee = fee.min(MAX_SWAP_FEE);
338351

339352
Ok(fee)
340353
}

programs/drift/src/state/perp_market.rs

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1691,22 +1691,23 @@ pub struct AmmCache {
16911691
#[derive(AnchorSerialize, AnchorDeserialize, Debug)]
16921692
#[repr(C)]
16931693
pub struct CacheInfo {
1694+
pub last_fee_pool_token_amount: u128,
1695+
pub last_net_pnl_pool_token_amount: i128,
16941696
/// BASE PRECISION
16951697
pub position: i64,
16961698
pub slot: u64,
16971699
pub max_confidence_interval_multiplier: u64,
16981700
pub last_oracle_price_twap: i64,
1701+
pub last_settle_amount: u64,
1702+
pub last_settle_ts: i64,
1703+
pub quote_owed_from_lp: i64,
16991704
pub oracle_price: i64,
17001705
pub oracle_confidence: u64,
17011706
pub oracle_delay: i64,
17021707
pub oracle_slot: u64,
1703-
pub oracle: Pubkey,
1704-
pub last_fee_pool_balance: u128,
1705-
pub last_net_pnl_pool_balance: i128,
1706-
pub last_settle_amount: u64,
1707-
pub last_settle_ts: i64,
17081708
pub oracle_source: u8,
17091709
_padding: [u8; 7],
1710+
pub oracle: Pubkey,
17101711
}
17111712

17121713
impl Size for CacheInfo {
@@ -1724,13 +1725,14 @@ impl Default for CacheInfo {
17241725
oracle_confidence: 0u64,
17251726
oracle_delay: 0i64,
17261727
oracle_slot: 0u64,
1728+
_padding: [0u8; 7],
17271729
oracle: Pubkey::default(),
1728-
last_fee_pool_balance: 0u128,
1729-
last_net_pnl_pool_balance: 0i128,
1730+
last_fee_pool_token_amount: 0u128,
1731+
last_net_pnl_pool_token_amount: 0i128,
17301732
last_settle_amount: 0u64,
17311733
last_settle_ts: 0i64,
17321734
oracle_source: 0u8,
1733-
_padding: [0u8; 7],
1735+
quote_owed_from_lp: 0i64,
17341736
}
17351737
}
17361738
}
@@ -1747,9 +1749,9 @@ impl CacheInfo {
17471749

17481750
pub fn get_last_available_amm_balance(&self) -> DriftResult<i128> {
17491751
let last_available_balance = self
1750-
.last_fee_pool_balance
1752+
.last_fee_pool_token_amount
17511753
.cast::<i128>()?
1752-
.safe_add(self.last_net_pnl_pool_balance)?;
1754+
.safe_add(self.last_net_pnl_pool_token_amount)?;
17531755
Ok(last_available_balance)
17541756
}
17551757
}

0 commit comments

Comments
 (0)