From 71f86f665dd232ee4e8685b7178b41b01c44d9aa Mon Sep 17 00:00:00 2001 From: bigz_Pubkey <83473873+0xbigz@users.noreply.github.com> Date: Tue, 19 Dec 2023 14:19:35 -0500 Subject: [PATCH] bigz/per-market-funding-cap (#783) --- programs/drift/src/controller/funding.rs | 5 +- programs/drift/src/math/funding/tests.rs | 106 ++++++++++++++++++++++- programs/drift/src/state/perp_market.rs | 14 +++ 3 files changed, 121 insertions(+), 4 deletions(-) diff --git a/programs/drift/src/controller/funding.rs b/programs/drift/src/controller/funding.rs index a83cce6ab..45a8a27cb 100644 --- a/programs/drift/src/controller/funding.rs +++ b/programs/drift/src/controller/funding.rs @@ -228,8 +228,9 @@ pub fn update_funding_rate( .safe_div(FUNDING_RATE_OFFSET_DENOMINATOR)?, )?; - // clamp price divergence to 3% for funding rate calculation - let max_price_spread = oracle_price_twap.safe_div(33)?; // 3% + // clamp price divergence based on contract tier for funding rate calculation + let max_price_spread = + market.get_max_price_divergence_for_funding_rate(oracle_price_twap)?; let clamped_price_spread = price_spread_with_offset.clamp(-max_price_spread, max_price_spread); diff --git a/programs/drift/src/math/funding/tests.rs b/programs/drift/src/math/funding/tests.rs index 911ea520a..3fb408b35 100644 --- a/programs/drift/src/math/funding/tests.rs +++ b/programs/drift/src/math/funding/tests.rs @@ -1,11 +1,20 @@ +use crate::controller::funding::update_funding_rate; use crate::math::constants::{ AMM_RESERVE_PRECISION, ONE_HOUR_I128, PRICE_PRECISION, PRICE_PRECISION_U64, QUOTE_PRECISION, }; use crate::math::funding::*; -use crate::state::oracle::HistoricalOracleData; -use crate::state::perp_market::{PerpMarket, AMM}; use std::cmp::min; +use crate::test_utils::get_pyth_price; + +// use crate::create_anchor_account_info; +use crate::state::oracle::HistoricalOracleData; +use crate::state::oracle_map::OracleMap; +use crate::state::perp_market::{ContractTier, PerpMarket, AMM}; +use crate::state::state::{OracleGuardRails, State, ValidityGuardRails}; +use solana_program::pubkey::Pubkey; +use std::str::FromStr; + fn calculate_funding_rate( mid_price_twap: u128, oracle_price_twap: i128, @@ -31,6 +40,12 @@ fn calculate_funding_rate( Ok(funding_rate) } + +#[cfg(test)] +use crate::test_utils::get_account_bytes; +use crate::test_utils::create_account_info; +use crate::create_account_info; + #[test] fn balanced_funding_test() { // balanced market no fees collected @@ -472,3 +487,90 @@ fn funding_unsettled_lps_amm_lose_test() { let new_fees = market.amm.total_fee_minus_distributions; assert_eq!(new_fees, 416667); // lost } + +#[test] +fn max_funding_rates() { + let now = 0_i64; + let slot = 0_u64; + + let state = State { + oracle_guard_rails: OracleGuardRails { + validity: ValidityGuardRails { + slots_before_stale_for_amm: 10, // 5s + slots_before_stale_for_margin: 120, // 60s + confidence_interval_max_size: 1000, + too_volatile_ratio: 5, + }, + ..OracleGuardRails::default() + }, + ..State::default() + }; + + let mut oracle_price = get_pyth_price(51, 6); + let oracle_price_key = + Pubkey::from_str("J83w4HKfqxwcq3BEMMkPFSppX3gqekLyLJBexebFVkix").unwrap(); + let pyth_program = crate::ids::pyth_program::id(); + create_account_info!( + oracle_price, + &oracle_price_key, + &pyth_program, + oracle_account_info + ); + let mut oracle_map = OracleMap::load_one(&oracle_account_info, slot, None).unwrap(); + let mut market = PerpMarket { + market_index: 0, + amm: AMM { + oracle: oracle_price_key, + + base_asset_reserve: 512295081967, + quote_asset_reserve: 488 * AMM_RESERVE_PRECISION, + sqrt_k: 500 * AMM_RESERVE_PRECISION, + peg_multiplier: 50000000, + base_asset_amount_with_amm: -12295081967, //~12 + base_asset_amount_long: 12295081967, + base_asset_amount_short: -12295081967 * 2, + base_asset_amount_with_unsettled_lp: -((AMM_RESERVE_PRECISION * 500) as i128), //wowsers + total_exchange_fee: QUOTE_PRECISION / 2, + total_fee_minus_distributions: ((QUOTE_PRECISION * 99999) as i128), + + last_mark_price_twap: 50 * PRICE_PRECISION_U64, + historical_oracle_data: HistoricalOracleData { + last_oracle_price_twap: (49 * PRICE_PRECISION) as i64, + + ..HistoricalOracleData::default() + }, + funding_period: 3600, + + ..AMM::default() + }, + ..PerpMarket::default() + }; + + let res1 = market + .get_max_price_divergence_for_funding_rate( + market.amm.historical_oracle_data.last_oracle_price_twap, + ) + .unwrap(); + assert_eq!(res1, 4900000); + market.contract_tier = ContractTier::B; + let res1 = market + .get_max_price_divergence_for_funding_rate( + market.amm.historical_oracle_data.last_oracle_price_twap, + ) + .unwrap(); + assert_eq!(res1, 1484848); + + let did_succeed = update_funding_rate( + 0, + &mut market, + &mut oracle_map, + now, + slot, + &state.oracle_guard_rails, + false, + None, + ) + .unwrap(); + + assert!(!did_succeed); +} diff --git a/programs/drift/src/state/perp_market.rs b/programs/drift/src/state/perp_market.rs index 67cc19b5d..6c30d49d5 100644 --- a/programs/drift/src/state/perp_market.rs +++ b/programs/drift/src/state/perp_market.rs @@ -277,6 +277,20 @@ impl PerpMarket { }) } + pub fn get_max_price_divergence_for_funding_rate( + self, + oracle_price_twap: i64, + ) -> DriftResult { + // clamp to to 3% price divergence for safer markets and higher for lower contract tiers + if self.contract_tier.is_as_safe_as_contract(&ContractTier::B) { + oracle_price_twap.safe_div(33) // 3% + } else if self.contract_tier.is_as_safe_as_contract(&ContractTier::C) { + oracle_price_twap.safe_div(20) // 5% + } else { + oracle_price_twap.safe_div(10) // 10% + } + } + pub fn get_margin_ratio( &self, size: u128,