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/amm-drawdown-guardrail #865

Merged
merged 10 commits into from
Feb 15, 2024
1 change: 1 addition & 0 deletions programs/drift/src/controller/orders.rs
Original file line number Diff line number Diff line change
Expand Up @@ -968,6 +968,7 @@ pub fn fill_perp_order(
{
let market = &mut perp_market_map.get_ref_mut(&market_index)?;
amm_is_available &= !market.is_operation_paused(PerpOperation::AmmFill);
amm_is_available &= market.has_too_much_drawdown()?;
0xbigz marked this conversation as resolved.
Show resolved Hide resolved
validation::perp_market::validate_perp_market(market)?;
validate!(
!market.is_in_settlement(now),
Expand Down
54 changes: 53 additions & 1 deletion programs/drift/src/state/perp_market.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,10 @@ use crate::state::events::OrderActionExplanation;
use crate::state::oracle::{HistoricalOracleData, OracleSource};
use crate::state::spot_market::{AssetTier, SpotBalance, SpotBalanceType};
use crate::state::traits::{MarketIndexOffset, Size};
use crate::{AMM_TO_QUOTE_PRECISION_RATIO, PRICE_PRECISION};
use crate::{
AMM_TO_QUOTE_PRECISION_RATIO, DEFAULT_REVENUE_SINCE_LAST_FUNDING_SPREAD_RETREAT,
PRICE_PRECISION,
};
use borsh::{BorshDeserialize, BorshSerialize};

use crate::state::paused_operations::PerpOperation;
Expand Down Expand Up @@ -292,6 +295,55 @@ impl PerpMarket {
PerpOperation::is_operation_paused(self.paused_operations, operation)
}

pub fn has_too_much_drawdown(&self) -> DriftResult<bool> {
0xbigz marked this conversation as resolved.
Show resolved Hide resolved
let quote_drawdown_limit_breached = match self.contract_tier {
ContractTier::A => {
self.amm.net_revenue_since_last_funding
<= DEFAULT_REVENUE_SINCE_LAST_FUNDING_SPREAD_RETREAT * 2000
}
ContractTier::B => {
self.amm.net_revenue_since_last_funding
<= DEFAULT_REVENUE_SINCE_LAST_FUNDING_SPREAD_RETREAT * 2000
}
ContractTier::C => {
self.amm.net_revenue_since_last_funding
<= DEFAULT_REVENUE_SINCE_LAST_FUNDING_SPREAD_RETREAT * 200
}
_ => {
self.amm.net_revenue_since_last_funding
<= DEFAULT_REVENUE_SINCE_LAST_FUNDING_SPREAD_RETREAT * 200
}
};

if quote_drawdown_limit_breached {
let percent_drawdown = self
.amm
.net_revenue_since_last_funding
0xbigz marked this conversation as resolved.
Show resolved Hide resolved
.safe_mul(PERCENTAGE_PRECISION.cast::<i64>()?)?
.safe_div(
self.amm
.total_fee_minus_distributions
.cast::<i64>()
.unwrap_or(i64::MAX)
.max(1),
)?;

let percent_drawdown_limit_breached = match self.contract_tier {
ContractTier::A => percent_drawdown <= -(PERCENTAGE_PRECISION as i64) / 33,
ContractTier::B => percent_drawdown <= -(PERCENTAGE_PRECISION as i64) / 25,
ContractTier::C => percent_drawdown <= -(PERCENTAGE_PRECISION as i64) / 20,
_ => percent_drawdown <= -(PERCENTAGE_PRECISION as i64) / 10,
};

if percent_drawdown_limit_breached {
msg!("AMM has too much on-the-hour drawdown to accept fills");
0xbigz marked this conversation as resolved.
Show resolved Hide resolved
return Ok(true);
}
}

Ok(false)
}

pub fn get_sanitize_clamp_denominator(self) -> DriftResult<Option<i64>> {
Ok(match self.contract_tier {
ContractTier::A => Some(10_i64), // 10%
Expand Down
Loading