@@ -30,6 +30,7 @@ use crate::math::casting::Cast;
30
30
use crate::math::constants::QUOTE_PRECISION;
31
31
use crate::math::constants::QUOTE_SPOT_MARKET_INDEX;
32
32
use crate::math::constants::SPOT_BALANCE_PRECISION;
33
+ use crate::math::lp_pool::perp_lp_pool_settlement;
33
34
use crate::math::margin::{calculate_user_equity, meets_settle_pnl_maintenance_margin_requirement};
34
35
use crate::math::orders::{estimate_price_from_side, find_bids_and_asks_from_users};
35
36
use crate::math::position::calculate_base_asset_value_and_pnl_with_oracle_price;
@@ -2944,12 +2945,17 @@ pub fn handle_pause_spot_market_deposit_withdraw(
2944
2945
Ok(())
2945
2946
}
2946
2947
2948
+ // Refactored main function
2947
2949
pub fn handle_settle_perp_to_lp_pool<'c: 'info, 'info>(
2948
2950
ctx: Context<'_, '_, 'c, 'info, SettleAmmPnlToLp<'info>>,
2949
2951
) -> Result<()> {
2950
- let slot = Clock::get()?.slot ;
2952
+ use perp_lp_pool_settlement::* ;
2951
2953
2954
+ let slot = Clock::get()?.slot;
2955
+ let timestamp = Clock::get()?.unix_timestamp;
2952
2956
let state = &ctx.accounts.state;
2957
+
2958
+ // Validation and setup code (unchanged)
2953
2959
let amm_cache_key = &ctx.accounts.amm_cache.key();
2954
2960
let mut amm_cache: AccountZeroCopyMut<'_, CacheInfo, _> =
2955
2961
ctx.accounts.amm_cache.load_zc_mut()?;
@@ -2958,8 +2964,7 @@ pub fn handle_settle_perp_to_lp_pool<'c: 'info, 'info>(
2958
2964
let constituent_token_account = &mut ctx.accounts.constituent_quote_token_account;
2959
2965
let mut lp_pool = ctx.accounts.lp_pool.load_mut()?;
2960
2966
2961
- let clock = Clock::get()?;
2962
-
2967
+ // PDA validation (unchanged)
2963
2968
let expected_pda = &Pubkey::create_program_address(
2964
2969
&[
2965
2970
AMM_POSITIONS_CACHE.as_ref(),
@@ -2987,12 +2992,19 @@ pub fn handle_settle_perp_to_lp_pool<'c: 'info, 'info>(
2987
2992
None,
2988
2993
)?;
2989
2994
2995
+ let precision_increase = SPOT_BALANCE_PRECISION.safe_div(QUOTE_PRECISION)?;
2996
+ let mint = Some(*ctx.accounts.mint.clone());
2997
+
2990
2998
for (_, perp_market_loader) in perp_market_map.0.iter() {
2991
2999
let mut perp_market = perp_market_loader.load_mut()?;
3000
+ if perp_market.lp_status == 0 {
3001
+ continue;
3002
+ }
3003
+
2992
3004
let cached_info = amm_cache.get_mut(perp_market.market_index as u32);
2993
3005
3006
+ // Early validation checks (unchanged)
2994
3007
if slot.saturating_sub(cached_info.oracle_slot) > SETTLE_AMM_ORACLE_MAX_DELAY {
2995
- // If the oracle slot is not up to date, skip this market
2996
3008
msg!(
2997
3009
"Skipping settling perp market {} to dlp because oracle slot is not up to date",
2998
3010
perp_market.market_index
@@ -3001,6 +3013,7 @@ pub fn handle_settle_perp_to_lp_pool<'c: 'info, 'info>(
3001
3013
}
3002
3014
3003
3015
validate_market_within_price_band(&perp_market, state, cached_info.oracle_price)?;
3016
+
3004
3017
if perp_market.is_operation_paused(PerpOperation::SettlePnl) {
3005
3018
msg!(
3006
3019
"Cannot settle pnl under current market = {} status",
@@ -3011,207 +3024,99 @@ pub fn handle_settle_perp_to_lp_pool<'c: 'info, 'info>(
3011
3024
3012
3025
if cached_info.slot != slot {
3013
3026
msg!("Skipping settling perp market {} to lp pool because amm cache was not updated in the same slot",
3014
- perp_market.market_index
3015
- );
3027
+ perp_market.market_index);
3016
3028
return Err(ErrorCode::AMMCacheStale.into());
3017
3029
}
3018
3030
3019
- let mint = *ctx.accounts.mint.clone();
3020
- // Transfer balance if it's available
3021
- if cached_info.quote_owed_from_lp > 0 {
3022
- if quote_constituent.token_balance == 0 {
3023
- msg!("LP Pool has no usdc to settle",);
3024
- continue;
3025
- }
3026
- let amount_to_send =
3027
- if cached_info.quote_owed_from_lp > quote_constituent.token_balance as i64 {
3028
- quote_constituent.token_balance
3029
- } else {
3030
- cached_info.quote_owed_from_lp as u64
3031
- };
3032
-
3033
- cached_info.quote_owed_from_lp = cached_info
3034
- .quote_owed_from_lp
3035
- .safe_sub(amount_to_send as i64)?;
3036
-
3037
- controller::token::send_from_program_vault(
3038
- &ctx.accounts.token_program,
3039
- constituent_token_account,
3040
- &ctx.accounts.quote_token_vault,
3041
- &ctx.accounts.drift_signer,
3042
- state.signer_nonce,
3043
- amount_to_send,
3044
- &Some(mint),
3045
- )?;
3046
-
3047
- // Send all revenues to the perp market fee pool
3048
- let precision_increase = SPOT_BALANCE_PRECISION.safe_div(QUOTE_PRECISION)?;
3049
- perp_market
3050
- .amm
3051
- .fee_pool
3052
- .increase_balance((amount_to_send as u128).safe_mul(precision_increase)?)?;
3053
-
3054
- // Update LP Pool Stats
3055
- lp_pool.cumulative_usdc_sent_to_perp_markets = lp_pool
3056
- .cumulative_usdc_sent_to_perp_markets
3057
- .saturating_add(amount_to_send.cast::<u128>()?);
3058
-
3059
- // Decrement cached fee pool token amount
3060
- cached_info.last_fee_pool_token_amount = cached_info
3061
- .last_fee_pool_token_amount
3062
- .safe_add(amount_to_send as u128)?;
3063
-
3064
- // Sync the constituent token account balance
3065
- constituent_token_account.reload()?;
3066
- quote_constituent.sync_token_balance(constituent_token_account.amount);
3067
-
3068
- // Update the last settle info
3069
- cached_info.last_settle_amount = amount_to_send.cast::<u64>()?;
3070
- cached_info.last_settle_ts = Clock::get()?.unix_timestamp;
3071
-
3072
- // Update LP Pool Stats
3073
- lp_pool.cumulative_usdc_sent_to_perp_markets = lp_pool
3074
- .cumulative_usdc_sent_to_perp_markets
3075
- .saturating_add(amount_to_send.cast::<u128>()?);
3076
- } else if cached_info.quote_owed_from_lp < 0 {
3077
- // We now send from the perp market to dlp and wipe out the amount owed from the perp market
3078
- let amount_to_send = cached_info.quote_owed_from_lp.abs() as u64;
3079
-
3080
- // Take from the fee pool if it can cover the whole balance, otherwise also take from pnl pool
3081
- let precision_increase = SPOT_BALANCE_PRECISION.safe_div(QUOTE_PRECISION)?;
3082
- let fee_pool_token_amount = get_token_amount(
3031
+ // Create settlement context
3032
+ let settlement_ctx = SettlementContext {
3033
+ quote_owed_from_lp: cached_info.quote_owed_from_lp_pool,
3034
+ quote_constituent_token_balance: quote_constituent.token_balance,
3035
+ fee_pool_balance: get_token_amount(
3083
3036
perp_market.amm.fee_pool.scaled_balance,
3084
- & quote_market,
3037
+ quote_market,
3085
3038
&SpotBalanceType::Deposit,
3086
- )?;
3087
- if fee_pool_token_amount > amount_to_send as u128 {
3088
- perp_market
3089
- .amm
3090
- .fee_pool
3091
- .decrease_balance((amount_to_send as u128).safe_mul(precision_increase)?)?;
3092
- cached_info.last_fee_pool_token_amount = cached_info
3093
- .last_fee_pool_token_amount
3094
- .safe_sub(amount_to_send as u128)?;
3095
- cached_info.quote_owed_from_lp = 0;
3096
-
3097
- controller::token::send_from_program_vault(
3039
+ )?,
3040
+ pnl_pool_balance: get_token_amount(
3041
+ perp_market.pnl_pool.scaled_balance,
3042
+ quote_market,
3043
+ &SpotBalanceType::Deposit,
3044
+ )?,
3045
+ quote_market,
3046
+ };
3047
+
3048
+ // Calculate settlement
3049
+ let settlement_result = calculate_settlement_amount(&settlement_ctx)?;
3050
+
3051
+ if settlement_result.direction == SettlementDirection::None {
3052
+ continue;
3053
+ }
3054
+
3055
+ // Execute token transfer
3056
+ match settlement_result.direction {
3057
+ SettlementDirection::FromLpPool => {
3058
+ execute_token_transfer(
3059
+ &ctx.accounts.token_program,
3060
+ constituent_token_account,
3061
+ &ctx.accounts.quote_token_vault,
3062
+ &ctx.accounts.drift_signer,
3063
+ state.signer_nonce,
3064
+ settlement_result.amount_transferred,
3065
+ &mint,
3066
+ )?;
3067
+ }
3068
+ SettlementDirection::ToLpPool => {
3069
+ execute_token_transfer(
3098
3070
&ctx.accounts.token_program,
3099
3071
&ctx.accounts.quote_token_vault,
3100
3072
constituent_token_account,
3101
3073
&ctx.accounts.drift_signer,
3102
3074
state.signer_nonce,
3103
- amount_to_send.cast::<u64>()? ,
3104
- &Some( mint) ,
3075
+ settlement_result.amount_transferred ,
3076
+ &mint,
3105
3077
)?;
3078
+ }
3079
+ SettlementDirection::None => unreachable!(),
3080
+ }
3106
3081
3107
- // Sync the constituent token account balance
3108
- constituent_token_account.reload()?;
3109
- quote_constituent.sync_token_balance(constituent_token_account.amount);
3082
+ // Update market pools
3083
+ update_perp_market_pools(&mut perp_market, &settlement_result, precision_increase)?;
3084
+
3085
+ // Calculate new quote owed amount
3086
+ let new_quote_owed = match settlement_result.direction {
3087
+ SettlementDirection::FromLpPool => cached_info
3088
+ .quote_owed_from_lp_pool
3089
+ .safe_sub(settlement_result.amount_transferred as i64)?,
3090
+ SettlementDirection::ToLpPool => cached_info
3091
+ .quote_owed_from_lp_pool
3092
+ .safe_add(settlement_result.amount_transferred as i64)?,
3093
+ SettlementDirection::None => cached_info.quote_owed_from_lp_pool,
3094
+ };
3110
3095
3111
- // Update the last settle info
3112
- cached_info.last_settle_amount = amount_to_send.cast::<u64>()?;
3113
- cached_info.last_settle_ts = Clock::get()?.unix_timestamp;
3096
+ // Update cache info
3097
+ update_cache_info(cached_info, &settlement_result, new_quote_owed, timestamp)?;
3114
3098
3115
- // Update LP Pool Stats
3099
+ // Update LP pool stats
3100
+ match settlement_result.direction {
3101
+ SettlementDirection::FromLpPool => {
3102
+ lp_pool.cumulative_usdc_sent_to_perp_markets = lp_pool
3103
+ .cumulative_usdc_sent_to_perp_markets
3104
+ .saturating_add(settlement_result.amount_transferred as u128);
3105
+ }
3106
+ SettlementDirection::ToLpPool => {
3116
3107
lp_pool.cumulative_usdc_received_from_perp_markets = lp_pool
3117
3108
.cumulative_usdc_received_from_perp_markets
3118
- .saturating_add(amount_to_send.cast::<u128>()?);
3119
- } else {
3120
- // If the fee pool cannot cover the whole amount, we take the rest from the pnl pool and set the
3121
- // fee pool balances to 0
3122
-
3123
- let remaining_amount_to_send =
3124
- (amount_to_send as u128).safe_sub(fee_pool_token_amount)?;
3125
-
3126
- perp_market
3127
- .amm
3128
- .fee_pool
3129
- .decrease_balance(fee_pool_token_amount.safe_mul(precision_increase)?)?;
3130
- cached_info.last_fee_pool_token_amount = 0;
3131
- cached_info.quote_owed_from_lp += fee_pool_token_amount.cast::<i64>()?;
3132
-
3133
- // Similarly, can the pnl pool cover the rest?
3134
- let pnl_pool_token_amount = get_token_amount(
3135
- perp_market.pnl_pool.scaled_balance,
3136
- "e_market,
3137
- &SpotBalanceType::Deposit,
3138
- )?;
3139
- if remaining_amount_to_send > pnl_pool_token_amount {
3140
- let transfer_amount = if pnl_pool_token_amount == 0 {
3141
- fee_pool_token_amount
3142
- } else {
3143
- perp_market.pnl_pool.decrease_balance(
3144
- pnl_pool_token_amount.safe_mul(precision_increase)?,
3145
- )?;
3146
- cached_info.last_net_pnl_pool_token_amount = cached_info
3147
- .last_net_pnl_pool_token_amount
3148
- .safe_sub(pnl_pool_token_amount.cast::<i128>()?)?;
3149
- cached_info.quote_owed_from_lp += pnl_pool_token_amount.cast::<i64>()?;
3150
-
3151
- fee_pool_token_amount.safe_add(pnl_pool_token_amount)?
3152
- };
3153
-
3154
- controller::token::send_from_program_vault(
3155
- &ctx.accounts.token_program,
3156
- &ctx.accounts.quote_token_vault,
3157
- constituent_token_account,
3158
- &ctx.accounts.drift_signer,
3159
- state.signer_nonce,
3160
- transfer_amount.cast::<u64>()?,
3161
- &Some(mint),
3162
- )?;
3163
-
3164
- // Sync the constituent token account balance
3165
- constituent_token_account.reload()?;
3166
- quote_constituent.sync_token_balance(constituent_token_account.amount);
3167
-
3168
- // Update the last settle info
3169
- cached_info.last_settle_amount = transfer_amount.cast::<u64>()?;
3170
- cached_info.last_settle_ts = Clock::get()?.unix_timestamp;
3171
-
3172
- // Update LP Pool Stats
3173
- lp_pool.cumulative_usdc_received_from_perp_markets = lp_pool
3174
- .cumulative_usdc_received_from_perp_markets
3175
- .saturating_add(transfer_amount.cast::<u128>()?);
3176
- } else {
3177
- perp_market
3178
- .pnl_pool
3179
- .decrease_balance(remaining_amount_to_send.safe_mul(precision_increase)?)?;
3180
- cached_info
3181
- .last_net_pnl_pool_token_amount
3182
- .safe_sub(remaining_amount_to_send.cast::<i128>()?)?;
3183
- cached_info.quote_owed_from_lp += remaining_amount_to_send.cast::<i64>()?;
3184
-
3185
- controller::token::send_from_program_vault(
3186
- &ctx.accounts.token_program,
3187
- &ctx.accounts.quote_token_vault,
3188
- constituent_token_account,
3189
- &ctx.accounts.drift_signer,
3190
- state.signer_nonce,
3191
- amount_to_send.cast::<u64>()?,
3192
- &Some(mint),
3193
- )?;
3194
-
3195
- // Sync the constituent token account balance
3196
- constituent_token_account.reload()?;
3197
- quote_constituent.sync_token_balance(constituent_token_account.amount);
3198
-
3199
- // Update the last settle info
3200
- cached_info.last_settle_amount = amount_to_send.cast::<u64>()?;
3201
- cached_info.last_settle_ts = Clock::get()?.unix_timestamp;
3202
-
3203
- // Update LP Pool Stats
3204
- lp_pool.cumulative_usdc_received_from_perp_markets = lp_pool
3205
- .cumulative_usdc_received_from_perp_markets
3206
- .saturating_add(amount_to_send.cast::<u128>()?);
3207
- }
3109
+ .saturating_add(settlement_result.amount_transferred as u128);
3208
3110
}
3209
- } else {
3210
- // nothing owed to settle
3211
- continue;
3111
+ SettlementDirection::None => {}
3212
3112
}
3113
+
3114
+ // Sync constituent token balance
3115
+ constituent_token_account.reload()?;
3116
+ quote_constituent.sync_token_balance(constituent_token_account.amount);
3213
3117
}
3214
3118
3119
+ // Final validation
3215
3120
math::spot_withdraw::validate_spot_market_vault_amount(
3216
3121
quote_market,
3217
3122
ctx.accounts.quote_token_vault.amount,
0 commit comments