Skip to content

Commit

Permalink
program: admin amm summary stats update and/or reset (#912)
Browse files Browse the repository at this point in the history
* bigz/add-unsettled-to-net-user-pnl

* add handle_update_perp_market_amm_summary_stats

* update market state for funding settle

* add reset to handle_update_perp_market_amm_summary_stats

* sdk: add admin function

* add funding test

* fix delisting test

* cleanup/rm unused code, add update_amm_summary_stats bool

* update sdk/changelog

* switch params for ix

* optional bool

---------

Co-authored-by: Chris Heaney <[email protected]>
  • Loading branch information
0xbigz and crispheaney committed Apr 20, 2024
1 parent 1611eda commit e6ee568
Show file tree
Hide file tree
Showing 12 changed files with 415 additions and 12 deletions.
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 @@ -673,7 +673,7 @@ pub fn handle_initialize_perp_market(
liquidator_fee,
if_liquidation_fee,
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 @@ -1002,14 +1002,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: Option<bool>,
}

#[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 == Some(true) {
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> =
&mut load_mut!(ctx.accounts.spot_market)?;
let state = &ctx.accounts.state;

let clock = Clock::get()?;
Expand Down Expand Up @@ -2819,11 +2896,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 @@ -946,7 +946,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 @@ -696,6 +696,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

0 comments on commit e6ee568

Please sign in to comment.