Skip to content

Commit

Permalink
program: account for contract tier in liquidate_perp_pnl_for_deposit (#…
Browse files Browse the repository at this point in the history
…368)

* bigz/add-tiers-for-liq-perp-pnl

* update idl

* improve liquidation.rs order, rename highest->safest

* add basic rust test

* fmt

* separate margin calc w/ safest tier calc

* finish asset tier test, fix liability only spot bug

* contract tier function impl, incorp other cleanup feedback p1

* tests/liquidatePerpPnlForDeposit.ts: fix for new tier rules

* liqudiation.rs: return early instead of erring on contract tier violation if orders got cancelled

* add tests for asset tier, revert liqPerpPnl margin check to top level one

* cleanup

* fix import

* CHANGELOG

---------

Co-authored-by: Chris Heaney <chrisheaney30@gmail.com>
  • Loading branch information
0xbigz and crispheaney authored Feb 24, 2023
1 parent 129c3e2 commit 5667feb
Show file tree
Hide file tree
Showing 10 changed files with 685 additions and 44 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Features

- program: account for contract tier in liquidate_perp_pnl_for_deposit ([#368](https://github.com/drift-labs/protocol-v2/pull/368))
- program: simplifications for order fills ([#370](https://github.com/drift-labs/protocol-v2/pull/370))
- program: block atomic fills ([#369](https://github.com/drift-labs/protocol-v2/pull/369))
- program: allow limit orders to go through auction ([#355](https://github.com/drift-labs/protocol-v2/pull/355))
Expand Down
52 changes: 44 additions & 8 deletions programs/drift/src/controller/liquidation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,8 @@ use crate::math::liquidation::{
validate_transfer_satisfies_limit_price, LiquidationMultiplierType,
};
use crate::math::margin::{
calculate_margin_requirement_and_total_collateral, meets_initial_margin_requirement,
MarginRequirementType,
calculate_margin_requirement_and_total_collateral, calculate_user_safest_position_tiers,
meets_initial_margin_requirement, MarginRequirementType,
};
use crate::math::oracle::DriftAction;
use crate::math::orders::{
Expand Down Expand Up @@ -1551,7 +1551,8 @@ pub fn liquidate_perp_pnl_for_deposit(
) -> DriftResult {
// liquidator takes over remaining negative perpetual pnl in exchange for a user deposit
// can only be done once the perpetual position's size is 0
// blocked when the user deposit oracle is deemed invalid
// blocked when 1) user deposit oracle is deemed invalid
// or 2) user has outstanding liability with higher tier

validate!(
!user.is_bankrupt(),
Expand Down Expand Up @@ -1609,7 +1610,14 @@ pub fn liquidate_perp_pnl_for_deposit(
now,
)?;

let (asset_amount, asset_price, asset_decimals, asset_weight, asset_liquidation_multiplier) = {
let (
asset_amount,
asset_price,
_asset_tier,
asset_decimals,
asset_weight,
asset_liquidation_multiplier,
) = {
let mut asset_market = spot_market_map.get_ref_mut(&asset_market_index)?;
let (asset_price_data, validity_guard_rails) =
oracle_map.get_price_data_and_guard_rails(&asset_market.oracle)?;
Expand Down Expand Up @@ -1643,6 +1651,7 @@ pub fn liquidate_perp_pnl_for_deposit(
(
token_amount,
token_price,
asset_market.asset_tier,
asset_market.decimals,
asset_market.maintenance_asset_weight,
calculate_liquidation_multiplier(
Expand All @@ -1655,6 +1664,7 @@ pub fn liquidate_perp_pnl_for_deposit(
let (
unsettled_pnl,
quote_price,
contract_tier,
quote_decimals,
pnl_liability_weight,
pnl_liquidation_multiplier,
Expand Down Expand Up @@ -1691,6 +1701,7 @@ pub fn liquidate_perp_pnl_for_deposit(
(
unsettled_pnl.unsigned_abs(),
quote_price,
market.contract_tier,
6_u32,
SPOT_WEIGHT_PRECISION,
calculate_liquidation_multiplier(
Expand Down Expand Up @@ -1737,6 +1748,10 @@ pub fn liquidate_perp_pnl_for_deposit(
None,
)?;

let (safest_tier_spot_liability, safest_tier_perp_liability) =
calculate_user_safest_position_tiers(user, perp_market_map, spot_market_map)?;
let is_contract_tier_violation =
!(contract_tier.is_as_safe_as(&safest_tier_perp_liability, &safest_tier_spot_liability));
// check if user exited liquidation territory
let (intermediate_total_collateral, intermediate_margin_requirement_with_buffer) =
if !canceled_order_ids.is_empty() {
Expand All @@ -1762,9 +1777,10 @@ pub fn liquidate_perp_pnl_for_deposit(
.cast::<u64>()?;
user.increment_margin_freed(margin_freed)?;

if intermediate_total_collateral
>= intermediate_margin_requirement_plus_buffer.cast()?
{
let exiting_liq_territory = intermediate_total_collateral
>= intermediate_margin_requirement_plus_buffer.cast()?;

if exiting_liq_territory || is_contract_tier_violation {
let market = perp_market_map.get_ref(&perp_market_index)?;
let market_oracle_price = oracle_map.get_price_data(&market.amm.oracle)?.price;

Expand All @@ -1790,7 +1806,17 @@ pub fn liquidate_perp_pnl_for_deposit(
..LiquidationRecord::default()
});

user.exit_liquidation();
if exiting_liq_territory {
user.exit_liquidation();
} else if is_contract_tier_violation {
msg!(
"return early after cancel orders: liquidating contract tier={:?} pnl is riskier than outstanding {:?} & {:?}",
contract_tier,
safest_tier_perp_liability,
safest_tier_spot_liability
);
}

return Ok(());
}

Expand All @@ -1802,6 +1828,16 @@ pub fn liquidate_perp_pnl_for_deposit(
(total_collateral, margin_requirement_plus_buffer)
};

if is_contract_tier_violation {
msg!(
"liquidating contract tier={:?} pnl is riskier than outstanding {:?} & {:?}",
contract_tier,
safest_tier_perp_liability,
safest_tier_spot_liability
);
return Err(ErrorCode::TierViolationLiquidatingPerpPnl);
}

let margin_shortage = calculate_margin_shortage(
intermediate_margin_requirement_with_buffer,
intermediate_total_collateral,
Expand Down
Loading

0 comments on commit 5667feb

Please sign in to comment.