Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

bigz/add-unsettled-to-net-user-pnl #912

Merged
merged 14 commits into from
Apr 20, 2024
Merged
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]

### Features
- program: admin amm summary stats update and/or reset ([#912](https://github.com/drift-labs/protocol-v2/pull/912))

### Fixes

Expand Down
30 changes: 30 additions & 0 deletions programs/drift/src/controller/amm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -818,3 +818,33 @@ pub fn recenter_perp_market_amm(amm: &mut AMM, peg_multiplier: u128, sqrt_k: u12

Ok(())
}

// recalculate and update summary stats on amm which are prone too accumulating integer math errors
pub fn calculate_perp_market_amm_summary_stats(
perp_market: &PerpMarket,
spot_market: &SpotMarket,
perp_market_oracle_price: i64,
) -> DriftResult<i128> {
let pnl_pool_token_amount = get_token_amount(
perp_market.pnl_pool.scaled_balance,
spot_market,
perp_market.pnl_pool.balance_type(),
)?;

let fee_pool_token_amount = get_token_amount(
perp_market.amm.fee_pool.scaled_balance,
spot_market,
perp_market.amm.fee_pool.balance_type(),
)?;

let pnl_tokens_available: i128 = pnl_pool_token_amount
.safe_add(fee_pool_token_amount)?
.cast()?;

let net_user_pnl = amm::calculate_net_user_pnl(&perp_market.amm, perp_market_oracle_price)?;

// amm's mm_fee can be incorrect with drifting integer math error
let new_total_fee_minus_distributions = pnl_tokens_available.safe_sub(net_user_pnl)?;

Ok(new_total_fee_minus_distributions)
}
14 changes: 14 additions & 0 deletions programs/drift/src/controller/funding.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,10 @@ pub fn settle_funding_payment(

market_position.last_cumulative_funding_rate = amm_cumulative_funding_rate.cast()?;
update_quote_asset_and_break_even_amount(market_position, market, market_funding_payment)?;
market.amm.net_unsettled_funding_pnl = market
.amm
.net_unsettled_funding_pnl
.safe_sub(market_funding_payment)?;
}

Ok(())
Expand Down Expand Up @@ -138,6 +142,10 @@ pub fn settle_funding_payments(
market,
market_funding_payment,
)?;
market.amm.net_unsettled_funding_pnl = market
.amm
.net_unsettled_funding_pnl
.safe_sub(market_funding_payment)?;
}
}

Expand Down Expand Up @@ -270,6 +278,12 @@ pub fn update_funding_rate(
market.amm.last_funding_rate_ts,
TWENTY_FOUR_HOUR,
)?;

market.amm.net_unsettled_funding_pnl = market
.amm
.net_unsettled_funding_pnl
.safe_sub(funding_imbalance_revenue.cast()?)?;

market.amm.last_funding_rate_ts = now;

emit!(FundingRateRecord {
Expand Down
4 changes: 3 additions & 1 deletion programs/drift/src/controller/pnl/delisting.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2831,7 +2831,9 @@ pub mod delisting_test {
let oracle_price_data = oracle_map.get_price_data(&market.amm.oracle).unwrap();
assert_eq!(oracle_price_data.price, 100 * PRICE_PRECISION_I64);
let net_pnl = calculate_net_user_pnl(&market.amm, oracle_price_data.price).unwrap();
assert_eq!(net_pnl, 0);
assert_eq!(net_pnl, 3449991000);
assert_eq!(market.amm.net_unsettled_funding_pnl, 3449991000); //todo?
assert_eq!(market.amm.quote_asset_amount_per_lp, 0);

drop(market);

Expand Down
19 changes: 13 additions & 6 deletions programs/drift/src/controller/position.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,7 @@ use crate::controller;
use crate::controller::amm::SwapDirection;
use crate::error::{DriftResult, ErrorCode};
use crate::math::casting::Cast;
use crate::math::constants::{MAX_BASE_ASSET_AMOUNT_WITH_AMM, PERP_DECIMALS};
// use crate::math::helpers::get_proportion_i128;
use crate::math::constants::{MAX_BASE_ASSET_AMOUNT_WITH_AMM, PERP_DECIMALS, QUOTE_PRECISION_I128};
use crate::math::orders::{
calculate_quote_asset_amount_for_maker_order, get_position_delta_for_fill,
is_multiple_of_step_size,
Expand Down Expand Up @@ -422,10 +421,6 @@ pub fn update_lp_market_position(
.amm
.calculate_per_lp_delta(delta, fee_to_market, liquidity_split, base_unit)?;

let lp_delta_base = market
.amm
.calculate_lp_base_delta(per_lp_delta_base, base_unit)?;

market.amm.base_asset_amount_per_lp = market
.amm
.base_asset_amount_per_lp
Expand All @@ -446,6 +441,13 @@ pub fn update_lp_market_position(
market.amm.quote_asset_amount_per_lp =
market.amm.quote_asset_amount_per_lp.safe_add(per_lp_fee)?;

let lp_delta_base = market
.amm
.calculate_lp_base_delta(per_lp_delta_base, base_unit)?;
let lp_delta_quote = market
.amm
.calculate_lp_base_delta(per_lp_delta_quote, QUOTE_PRECISION_I128)?;

market.amm.base_asset_amount_with_amm = market
.amm
.base_asset_amount_with_amm
Expand All @@ -456,6 +458,11 @@ pub fn update_lp_market_position(
.base_asset_amount_with_unsettled_lp
.safe_add(lp_delta_base)?;

market.amm.quote_asset_amount_with_unsettled_lp = market
.amm
.quote_asset_amount_with_unsettled_lp
.safe_add(lp_delta_quote.cast()?)?;

Ok(lp_delta_base)
}

Expand Down
101 changes: 98 additions & 3 deletions programs/drift/src/instructions/admin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -650,7 +650,7 @@ pub fn handle_initialize_perp_market(
liquidator_fee,
if_liquidation_fee: LIQUIDATION_FEE_PRECISION / 100, // 1%
paused_operations: 0,
quote_spot_market_index: 0,
quote_spot_market_index: QUOTE_SPOT_MARKET_INDEX,
fee_adjustment: 0,
padding: [0; 46],
amm: AMM {
Expand Down Expand Up @@ -899,14 +899,91 @@ pub fn handle_recenter_perp_market_amm(
Ok(())
}

#[derive(Debug, Clone, Copy, AnchorSerialize, AnchorDeserialize, PartialEq, Eq)]
pub struct UpdatePerpMarketSummaryStatsParams {
// new aggregate unsettled user stats
pub quote_asset_amount_with_unsettled_lp: Option<i64>,
pub net_unsettled_funding_pnl: Option<i64>,
pub update_amm_summary_stats: bool,
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Option instead?

}

#[access_control(
perp_market_valid(&ctx.accounts.perp_market)
valid_oracle_for_perp_market(&ctx.accounts.oracle, &ctx.accounts.perp_market)
)]
pub fn handle_update_perp_market_amm_summary_stats(
ctx: Context<AdminUpdatePerpMarketAmmSummaryStats>,
params: UpdatePerpMarketSummaryStatsParams,
) -> Result<()> {
let perp_market = &mut load_mut!(ctx.accounts.perp_market)?;
let spot_market = &mut load_mut!(ctx.accounts.spot_market)?;

let clock = Clock::get()?;
let price_oracle = &ctx.accounts.oracle;

let OraclePriceData {
price: oracle_price,
..
} = get_oracle_price(&perp_market.amm.oracle_source, price_oracle, clock.slot)?;

if let Some(quote_asset_amount_with_unsettled_lp) = params.quote_asset_amount_with_unsettled_lp
{
msg!(
"quote_asset_amount_with_unsettled_lp {} -> {}",
perp_market.amm.quote_asset_amount_with_unsettled_lp,
quote_asset_amount_with_unsettled_lp
);
perp_market.amm.quote_asset_amount_with_unsettled_lp = quote_asset_amount_with_unsettled_lp;
}

if let Some(net_unsettled_funding_pnl) = params.net_unsettled_funding_pnl {
msg!(
"net_unsettled_funding_pnl {} -> {}",
perp_market.amm.net_unsettled_funding_pnl,
net_unsettled_funding_pnl
);
perp_market.amm.net_unsettled_funding_pnl = net_unsettled_funding_pnl;
}

if params.update_amm_summary_stats {
let new_total_fee_minus_distributions =
controller::amm::calculate_perp_market_amm_summary_stats(
perp_market,
spot_market,
oracle_price,
)?;

msg!(
"updating amm summary stats for market index = {}",
perp_market.market_index,
);

msg!(
"total_fee_minus_distributions: {:?} -> {:?}",
perp_market.amm.total_fee_minus_distributions,
new_total_fee_minus_distributions,
);
let fee_difference = new_total_fee_minus_distributions
.safe_sub(perp_market.amm.total_fee_minus_distributions)?;

perp_market.amm.total_fee = perp_market.amm.total_fee.saturating_add(fee_difference);
perp_market.amm.total_mm_fee = perp_market.amm.total_mm_fee.saturating_add(fee_difference);
perp_market.amm.total_fee_minus_distributions = new_total_fee_minus_distributions;
}
validate_perp_market(perp_market)?;

Ok(())
}

#[access_control(
perp_market_valid(&ctx.accounts.perp_market)
)]
pub fn handle_settle_expired_market_pools_to_revenue_pool(
ctx: Context<SettleExpiredMarketPoolsToRevenuePool>,
) -> Result<()> {
let perp_market = &mut load_mut!(ctx.accounts.perp_market)?;
let spot_market = &mut load_mut!(ctx.accounts.spot_market)?;
let spot_market: &mut std::cell::RefMut<'_, SpotMarket> =
0xbigz marked this conversation as resolved.
Show resolved Hide resolved
&mut load_mut!(ctx.accounts.spot_market)?;
let state = &ctx.accounts.state;

let clock = Clock::get()?;
Expand Down Expand Up @@ -2716,11 +2793,29 @@ pub struct DeleteInitializedPerpMarket<'info> {
pub struct AdminUpdatePerpMarket<'info> {
pub admin: Signer<'info>,
#[account(
has_one = admin
has_one = admin
)]
pub state: Box<Account<'info, State>>,
#[account(mut)]
pub perp_market: AccountLoader<'info, PerpMarket>,
}

#[derive(Accounts)]
pub struct AdminUpdatePerpMarketAmmSummaryStats<'info> {
pub admin: Signer<'info>,
#[account(
has_one = admin
)]
pub state: Box<Account<'info, State>>,
#[account(mut)]
pub perp_market: AccountLoader<'info, PerpMarket>,
#[account(
seeds = [b"spot_market", perp_market.load()?.quote_spot_market_index.to_le_bytes().as_ref()],
bump,
)]
pub spot_market: AccountLoader<'info, SpotMarket>,
/// CHECK: checked in `admin_update_perp_market_summary_stats` ix constraint
pub oracle: AccountInfo<'info>,
}

#[derive(Accounts)]
Expand Down
2 changes: 1 addition & 1 deletion programs/drift/src/instructions/keeper.rs
Original file line number Diff line number Diff line change
Expand Up @@ -955,7 +955,7 @@ pub fn handle_resolve_perp_bankruptcy(
)?;

validate!(
quote_spot_market_index == 0,
quote_spot_market_index == QUOTE_SPOT_MARKET_INDEX,
ErrorCode::InvalidSpotMarketAccount
)?;

Expand Down
7 changes: 7 additions & 0 deletions programs/drift/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -647,6 +647,13 @@ pub mod drift {
handle_recenter_perp_market_amm(ctx, peg_multiplier, sqrt_k)
}

pub fn update_perp_market_amm_summary_stats(
ctx: Context<AdminUpdatePerpMarketAmmSummaryStats>,
params: UpdatePerpMarketSummaryStatsParams,
) -> Result<()> {
handle_update_perp_market_amm_summary_stats(ctx, params)
}

pub fn update_perp_market_expiry(
ctx: Context<AdminUpdatePerpMarket>,
expiry_ts: i64,
Expand Down
5 changes: 4 additions & 1 deletion programs/drift/src/math/amm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -822,7 +822,9 @@ pub fn calculate_amm_available_liquidity(
}

pub fn calculate_net_user_cost_basis(amm: &AMM) -> DriftResult<i128> {
Ok(amm.quote_asset_amount)
amm.quote_asset_amount
.safe_add(amm.quote_asset_amount_with_unsettled_lp.cast()?)?
.safe_add(amm.net_unsettled_funding_pnl.cast()?)
}

pub fn calculate_net_user_pnl(amm: &AMM, oracle_price: i64) -> DriftResult<i128> {
Expand All @@ -834,6 +836,7 @@ pub fn calculate_net_user_pnl(amm: &AMM, oracle_price: i64) -> DriftResult<i128>

let net_user_base_asset_value = amm
.base_asset_amount_with_amm
.safe_add(amm.base_asset_amount_with_unsettled_lp)?
.safe_mul(oracle_price.cast()?)?
.safe_div(PRICE_TIMES_AMM_TO_QUOTE_PRECISION_RATIO.cast()?)?;

Expand Down
Loading