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-fee-pool-insurance #332

Merged
merged 24 commits into from
Jan 29, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
f4d0fff
bigz/amm-fee-pool-insurance
0xbigz Jan 19, 2023
10e26b3
dead import / sign cleanup on losses_remaining etc
0xbigz Jan 23, 2023
aa48329
sdk: calculateEstimatedPerpEntryPrice use getLimitBids/getLimitAsks
crispheaney Jan 19, 2023
9610eb2
program: fix paying fee_pool_delta when filling with open book
crispheaney Jan 19, 2023
86ffc2b
program: better rounding for openbook limit price
crispheaney Jan 20, 2023
ec6f378
sdk: return worst and best price from calculateEstimatedPerpEntryPrice
crispheaney Jan 20, 2023
c6b18d8
sdk: array of users to skip in calculateEstimatedPerpEntryPrice
crispheaney Jan 20, 2023
0be3d4f
sdk: calculateEstimatedPerpEntryPrice accounts for limited vamm liqui…
crispheaney Jan 21, 2023
4f93fc2
sdk: tweak available liquidity calc in calculateEstimatedPerpEntryPrice
crispheaney Jan 21, 2023
21359aa
sdk: add calculateEstimatedSpotEntryPrice
crispheaney Jan 22, 2023
8bc27be
program: only let market orders match against resting limit orders (#…
crispheaney Jan 22, 2023
b679000
program: allow for 2000 users
crispheaney Jan 22, 2023
a46292d
program: add additional logging for casting error in calculate_fee_fo…
crispheaney Jan 22, 2023
34a0318
v2.12.0
crispheaney Jan 22, 2023
5be6773
sdk: add loadDlob example
crispheaney Jan 23, 2023
b55a938
tests: fix multipleMakerOrders.ts
crispheaney Jan 23, 2023
f190c16
merge master
0xbigz Jan 25, 2023
aa27c3c
more progress on cargo test (wip)
0xbigz Jan 27, 2023
c51066b
working cargo test
0xbigz Jan 27, 2023
d567bfb
tests/delistMarketLiq.ts: fix for fee pool as insurance
0xbigz Jan 27, 2023
b80d950
Merge branch 'master' into bigz/amm-fee-pool-insurance
0xbigz Jan 28, 2023
4db11ac
fix tests/liquidatePerp.ts
0xbigz Jan 28, 2023
118b756
fix tests/liquidatePerpAndLp.ts
0xbigz Jan 28, 2023
26a4918
fix tests/liquidatePerpAndLp.ts cumulativeFundingRateDelta
0xbigz Jan 28, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 43 additions & 1 deletion programs/drift/src/controller/liquidation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use std::ops::{Deref, DerefMut};
use anchor_lang::prelude::*;
use solana_program::msg;

use crate::controller::amm::get_fee_pool_tokens;
use crate::controller::funding::settle_funding_payment;
use crate::controller::lp::burn_lp_shares;
use crate::controller::orders;
Expand Down Expand Up @@ -2081,7 +2082,48 @@ pub fn resolve_perp_bankruptcy(
if_payment
};

let loss_to_socialize = loss.safe_add(if_payment.cast::<i128>()?)?;
let losses_remaining: i128 = loss.safe_add(if_payment.cast::<i128>()?)?;
validate!(
losses_remaining <= 0,
ErrorCode::InvalidPerpPositionToLiquidate,
"losses_remaining must be non-positive"
)?;

let fee_pool_payment: i128 = if losses_remaining < 0 {
let perp_market = &mut perp_market_map.get_ref_mut(&market_index)?;
let spot_market = &mut spot_market_map.get_ref_mut(&QUOTE_SPOT_MARKET_INDEX)?;
let fee_pool_tokens = get_fee_pool_tokens(perp_market, spot_market)?;
msg!("fee_pool_tokens={:?}", fee_pool_tokens);

losses_remaining.abs().min(fee_pool_tokens.cast()?)
} else {
0
};
validate!(
fee_pool_payment >= 0,
ErrorCode::InvalidPerpPositionToLiquidate,
"fee_pool_payment must be non-negative"
)?;

if fee_pool_payment > 0 {
let perp_market = &mut perp_market_map.get_ref_mut(&market_index)?;
let spot_market = &mut spot_market_map.get_ref_mut(&QUOTE_SPOT_MARKET_INDEX)?;
msg!("fee_pool_payment={:?}", fee_pool_payment);
update_spot_balances(
fee_pool_payment.unsigned_abs(),
&SpotBalanceType::Borrow,
spot_market,
&mut perp_market.amm.fee_pool,
false,
)?;
}

let loss_to_socialize = losses_remaining.safe_add(fee_pool_payment.cast::<i128>()?)?;
validate!(
loss_to_socialize <= 0,
ErrorCode::InvalidPerpPositionToLiquidate,
"loss_to_socialize must be non-positive"
)?;

let cumulative_funding_rate_delta = calculate_funding_rate_deltas_to_resolve_bankruptcy(
loss_to_socialize,
Expand Down
220 changes: 217 additions & 3 deletions programs/drift/src/controller/liquidation/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5171,12 +5171,13 @@ pub mod resolve_perp_bankruptcy {
use crate::math::constants::{
AMM_RESERVE_PRECISION, BASE_PRECISION_I128, BASE_PRECISION_I64, BASE_PRECISION_U64,
FUNDING_RATE_PRECISION_I128, FUNDING_RATE_PRECISION_I64, LIQUIDATION_FEE_PRECISION,
PEG_PRECISION, QUOTE_PRECISION_I128, QUOTE_PRECISION_I64, SPOT_BALANCE_PRECISION_U64,
SPOT_CUMULATIVE_INTEREST_PRECISION, SPOT_WEIGHT_PRECISION,
PEG_PRECISION, QUOTE_PRECISION_I128, QUOTE_PRECISION_I64, QUOTE_SPOT_MARKET_INDEX,
SPOT_BALANCE_PRECISION, SPOT_BALANCE_PRECISION_U64, SPOT_CUMULATIVE_INTEREST_PRECISION,
SPOT_WEIGHT_PRECISION,
};
use crate::state::oracle::OracleSource;
use crate::state::oracle_map::OracleMap;
use crate::state::perp_market::{MarketStatus, PerpMarket, AMM};
use crate::state::perp_market::{MarketStatus, PerpMarket, PoolBalance, AMM};
use crate::state::perp_market_map::PerpMarketMap;
use crate::state::spot_market::{SpotBalanceType, SpotMarket};
use crate::state::spot_market_map::SpotMarketMap;
Expand Down Expand Up @@ -5390,6 +5391,219 @@ pub mod resolve_perp_bankruptcy {

assert_eq!(expected_affected_short_user, affected_short_user);
}

#[test]
pub fn successful_resolve_perp_bankruptcy_with_fee_pool() {
let now = 0_i64;
let slot = 0_u64;

let mut oracle_price = get_pyth_price(100, 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 {
amm: AMM {
base_asset_reserve: 100 * AMM_RESERVE_PRECISION,
quote_asset_reserve: 100 * AMM_RESERVE_PRECISION,
bid_base_asset_reserve: 101 * AMM_RESERVE_PRECISION,
bid_quote_asset_reserve: 99 * AMM_RESERVE_PRECISION,
ask_base_asset_reserve: 99 * AMM_RESERVE_PRECISION,
ask_quote_asset_reserve: 101 * AMM_RESERVE_PRECISION,
sqrt_k: 100 * AMM_RESERVE_PRECISION,
peg_multiplier: 100 * PEG_PRECISION,
max_slippage_ratio: 50,
max_fill_reserve_fraction: 100,
order_step_size: 10000000,
quote_asset_amount: -150 * QUOTE_PRECISION_I128,
base_asset_amount_long: 5 * BASE_PRECISION_I128,
base_asset_amount_short: -5 * BASE_PRECISION_I128,
base_asset_amount_with_amm: BASE_PRECISION_I128,
oracle: oracle_price_key,
cumulative_funding_rate_long: 1000 * FUNDING_RATE_PRECISION_I128,
cumulative_funding_rate_short: -1000 * FUNDING_RATE_PRECISION_I128,
fee_pool: PoolBalance {
scaled_balance: 50 * SPOT_BALANCE_PRECISION,
market_index: QUOTE_SPOT_MARKET_INDEX,
..PoolBalance::default()
},
..AMM::default()
},
margin_ratio_initial: 1000,
margin_ratio_maintenance: 500,
status: MarketStatus::Initialized,
liquidator_fee: LIQUIDATION_FEE_PRECISION / 100,
number_of_users: 1,
..PerpMarket::default()
};
create_anchor_account_info!(market, PerpMarket, market_account_info);
let market_map = PerpMarketMap::load_one(&market_account_info, true).unwrap();

let mut spot_market = SpotMarket {
market_index: 0,
deposit_balance: 500 * SPOT_BALANCE_PRECISION,
oracle_source: OracleSource::QuoteAsset,
cumulative_deposit_interest: SPOT_CUMULATIVE_INTEREST_PRECISION,
decimals: 6,
initial_asset_weight: SPOT_WEIGHT_PRECISION,
..SpotMarket::default()
};
create_anchor_account_info!(spot_market, SpotMarket, spot_market_account_info);
let spot_market_map = SpotMarketMap::load_one(&spot_market_account_info, true).unwrap();

let mut user = User {
orders: get_orders(Order {
market_index: 0,
status: OrderStatus::Open,
order_type: OrderType::Limit,
direction: PositionDirection::Long,
base_asset_amount: BASE_PRECISION_U64,
slot: 0,
..Order::default()
}),
perp_positions: get_positions(PerpPosition {
market_index: 0,
base_asset_amount: 0,
quote_asset_amount: -100 * QUOTE_PRECISION_I64,
quote_entry_amount: -100 * QUOTE_PRECISION_I64,
quote_break_even_amount: -100 * QUOTE_PRECISION_I64,
open_orders: 1,
open_bids: BASE_PRECISION_I64,
..PerpPosition::default()
}),
spot_positions: [SpotPosition::default(); 8],
status: UserStatus::Bankrupt,
next_liquidation_id: 2,
..User::default()
};

let mut liquidator = User {
spot_positions: get_spot_positions(SpotPosition {
market_index: 0,
balance_type: SpotBalanceType::Deposit,
scaled_balance: 50 * SPOT_BALANCE_PRECISION_U64,
..SpotPosition::default()
}),
..User::default()
};

let user_key = Pubkey::default();
let liquidator_key = Pubkey::default();

let mut expected_user = user;
expected_user.status = UserStatus::Active;
expected_user.perp_positions[0].quote_asset_amount = 0;
expected_user.total_social_loss = 100000000;

let mut expected_market = market;
expected_market.amm.cumulative_funding_rate_long = 1005 * FUNDING_RATE_PRECISION_I128;
expected_market.amm.cumulative_funding_rate_short = -1005 * FUNDING_RATE_PRECISION_I128;
expected_market.amm.total_social_loss = 50000000;
expected_market.amm.quote_asset_amount = -50 * QUOTE_PRECISION_I128;
expected_market.number_of_users = 0;
expected_market.amm.fee_pool.scaled_balance = 0;

resolve_perp_bankruptcy(
0,
&mut user,
&user_key,
&mut liquidator,
&liquidator_key,
&market_map,
&spot_market_map,
&mut oracle_map,
now,
0,
)
.unwrap();

assert_eq!(user.total_social_loss, 100000000);
assert_eq!(expected_user, user);
assert_eq!(expected_market, market_map.get_ref(&0).unwrap().clone());

let mut affected_long_user = User {
orders: [Order::default(); 32],
perp_positions: get_positions(PerpPosition {
market_index: 0,
base_asset_amount: 5 * BASE_PRECISION_I64,
quote_asset_amount: -500 * QUOTE_PRECISION_I64,
quote_break_even_amount: -500 * QUOTE_PRECISION_I64,
quote_entry_amount: -500 * QUOTE_PRECISION_I64,
open_bids: BASE_PRECISION_I64,
last_cumulative_funding_rate: 1000 * FUNDING_RATE_PRECISION_I64,
..PerpPosition::default()
}),
spot_positions: [SpotPosition::default(); 8],
..User::default()
};

let mut expected_affected_long_user = affected_long_user;
expected_affected_long_user.perp_positions[0].quote_asset_amount =
-525 * QUOTE_PRECISION_I64; // loses $50
expected_affected_long_user.perp_positions[0].quote_break_even_amount =
-525 * QUOTE_PRECISION_I64; // loses $50
expected_affected_long_user.perp_positions[0].last_cumulative_funding_rate =
1005 * FUNDING_RATE_PRECISION_I64;
expected_affected_long_user.cumulative_perp_funding = -25 * QUOTE_PRECISION_I64;

{
let mut market = market_map.get_ref_mut(&0).unwrap();
settle_funding_payment(
&mut affected_long_user,
&Pubkey::default(),
&mut market,
now,
)
.unwrap()
}

assert_eq!(expected_affected_long_user, affected_long_user);

let mut affected_short_user = User {
orders: [Order::default(); 32],
perp_positions: get_positions(PerpPosition {
market_index: 0,
base_asset_amount: -5 * BASE_PRECISION_I64,
quote_asset_amount: 500 * QUOTE_PRECISION_I64,
quote_entry_amount: 500 * QUOTE_PRECISION_I64,
quote_break_even_amount: 500 * QUOTE_PRECISION_I64,
open_bids: BASE_PRECISION_I64,
last_cumulative_funding_rate: -1000 * FUNDING_RATE_PRECISION_I64,
..PerpPosition::default()
}),
spot_positions: [SpotPosition::default(); 8],
..User::default()
};

let mut expected_affected_short_user = affected_short_user;
expected_affected_short_user.perp_positions[0].quote_asset_amount =
475 * QUOTE_PRECISION_I64; // loses $50
expected_affected_short_user.perp_positions[0].quote_break_even_amount =
475 * QUOTE_PRECISION_I64; // loses $50
expected_affected_short_user.perp_positions[0].last_cumulative_funding_rate =
-1005 * FUNDING_RATE_PRECISION_I64;
expected_affected_short_user.cumulative_perp_funding = -25 * QUOTE_PRECISION_I64;

{
let mut market = market_map.get_ref_mut(&0).unwrap();
settle_funding_payment(
&mut affected_short_user,
&Pubkey::default(),
&mut market,
now,
)
.unwrap()
}

assert_eq!(expected_affected_short_user, affected_short_user);
}
}

pub mod resolve_spot_bankruptcy {
Expand Down
7 changes: 4 additions & 3 deletions tests/delistMarketLiq.ts
Original file line number Diff line number Diff line change
Expand Up @@ -735,8 +735,8 @@ describe('delist market, liquidation of expired position', () => {
assert(marketAfter0.numberOfUsersWithBase === 0);

// old 1415296436
const finalPnlResultMin0 = new BN(2270702294000 - 11090000);
const finalPnlResultMax0 = new BN(2270702294000 + 11109000);
const finalPnlResultMin0 = new BN(2266346249000 - 11090000);
const finalPnlResultMax0 = new BN(2266346249000 + 11109000);

console.log(marketAfter0.pnlPool.scaledBalance.toString());
assert(marketAfter0.pnlPool.scaledBalance.gt(finalPnlResultMin0));
Expand All @@ -748,7 +748,8 @@ describe('delist market, liquidation of expired position', () => {
'totalExchangeFee:',
marketAfter0.amm.totalExchangeFee.toString()
);
assert(marketAfter0.amm.feePool.scaledBalance.eq(new BN(4356250000)));
assert(marketAfter0.amm.feePool.scaledBalance.eq(ZERO));
assert(marketAfter0.amm.totalExchangeFee.eq(new BN(8712501)));
await liquidatorDriftClientUser.unsubscribe();
});
});
12 changes: 8 additions & 4 deletions tests/liquidatePerp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -451,7 +451,11 @@ describe('liquidate perp (no open orders)', () => {
assert(
marketAfterBankruptcy.insuranceClaim.quoteMaxInsurance.eq(QUOTE_PRECISION)
);
assert(marketAfterBankruptcy.amm.totalSocialLoss.eq(new BN(5785008)));
console.log(
'marketAfterBankruptcy.amm.totalSocialLoss:',
marketAfterBankruptcy.amm.totalSocialLoss.toString()
);
assert(marketAfterBankruptcy.amm.totalSocialLoss.eq(new BN(5776257)));

// assert(!driftClient.getUserAccount().isBankrupt);
// assert(!driftClient.getUserAccount().isBeingLiquidated);
Expand All @@ -474,7 +478,7 @@ describe('liquidate perp (no open orders)', () => {
);
assert(
perpBankruptcyRecord.perpBankruptcy.cumulativeFundingRateDelta.eq(
new BN(330572000)
new BN(330072000)
)
);

Expand All @@ -483,7 +487,7 @@ describe('liquidate perp (no open orders)', () => {
market.amm.cumulativeFundingRateLong.toString(),
market.amm.cumulativeFundingRateShort.toString()
);
assert(market.amm.cumulativeFundingRateLong.eq(new BN(330572000)));
assert(market.amm.cumulativeFundingRateShort.eq(new BN(-330572000)));
assert(market.amm.cumulativeFundingRateLong.eq(new BN(330072000)));
assert(market.amm.cumulativeFundingRateShort.eq(new BN(-330072000)));
});
});
12 changes: 8 additions & 4 deletions tests/liquidatePerpAndLp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -456,7 +456,11 @@ describe('liquidate perp and lp', () => {
assert(
marketAfterBankruptcy.insuranceClaim.quoteMaxInsurance.eq(QUOTE_PRECISION)
);
assert(marketAfterBankruptcy.amm.totalSocialLoss.eq(new BN(5785008)));
console.log(
'marketAfterBankruptcy.amm.totalSocialLoss:',
marketAfterBankruptcy.amm.totalSocialLoss.toString()
);
assert(marketAfterBankruptcy.amm.totalSocialLoss.eq(new BN(5776257)));

// assert(!driftClient.getUserAccount().isBankrupt);
// assert(!driftClient.getUserAccount().isBeingLiquidated);
Expand All @@ -479,7 +483,7 @@ describe('liquidate perp and lp', () => {
);
assert(
perpBankruptcyRecord.perpBankruptcy.cumulativeFundingRateDelta.eq(
new BN(330572000)
new BN(330072000)
)
);

Expand All @@ -488,7 +492,7 @@ describe('liquidate perp and lp', () => {
market.amm.cumulativeFundingRateLong.toString(),
market.amm.cumulativeFundingRateShort.toString()
);
assert(market.amm.cumulativeFundingRateLong.eq(new BN(330572000)));
assert(market.amm.cumulativeFundingRateShort.eq(new BN(-330572000)));
assert(market.amm.cumulativeFundingRateLong.eq(new BN(330072000)));
assert(market.amm.cumulativeFundingRateShort.eq(new BN(-330072000)));
});
});