Remove is_bankrupt
Instead, check for any liquidatable assets in liq_token_bankruptcy. Bankrupt accounts may use the same operations as any other negative-health account.
This commit is contained in:
parent
52660f1476
commit
92a37f23ed
|
@ -108,11 +108,12 @@ pub fn maybe_liquidate_account(
|
||||||
let quote_token_index = 0;
|
let quote_token_index = 0;
|
||||||
|
|
||||||
let account = account_fetcher.fetch_mango_account(pubkey)?;
|
let account = account_fetcher.fetch_mango_account(pubkey)?;
|
||||||
let maint_health = new_health_cache_(&mango_client.context, account_fetcher, &account)
|
let health_cache =
|
||||||
.expect("always ok")
|
new_health_cache_(&mango_client.context, account_fetcher, &account).expect("always ok");
|
||||||
.health(HealthType::Maint);
|
let maint_health = health_cache.health(HealthType::Maint);
|
||||||
|
let is_bankrupt = !health_cache.has_liquidatable_assets();
|
||||||
|
|
||||||
if maint_health >= 0 && !account.is_bankrupt() {
|
if maint_health >= 0 && !is_bankrupt {
|
||||||
return Ok(false);
|
return Ok(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -121,16 +122,17 @@ pub fn maybe_liquidate_account(
|
||||||
pubkey,
|
pubkey,
|
||||||
account.fixed.owner,
|
account.fixed.owner,
|
||||||
maint_health,
|
maint_health,
|
||||||
account.is_bankrupt(),
|
is_bankrupt,
|
||||||
);
|
);
|
||||||
|
|
||||||
// Fetch a fresh account and re-compute
|
// Fetch a fresh account and re-compute
|
||||||
// This is -- unfortunately -- needed because the websocket streams seem to not
|
// This is -- unfortunately -- needed because the websocket streams seem to not
|
||||||
// be great at providing timely updates to the account data.
|
// be great at providing timely updates to the account data.
|
||||||
let account = account_fetcher.fetch_fresh_mango_account(pubkey)?;
|
let account = account_fetcher.fetch_fresh_mango_account(pubkey)?;
|
||||||
let maint_health = new_health_cache_(&mango_client.context, account_fetcher, &account)
|
let health_cache =
|
||||||
.expect("always ok")
|
new_health_cache_(&mango_client.context, account_fetcher, &account).expect("always ok");
|
||||||
.health(HealthType::Maint);
|
let maint_health = health_cache.health(HealthType::Maint);
|
||||||
|
let is_bankrupt = !health_cache.has_liquidatable_assets();
|
||||||
|
|
||||||
// find asset and liab tokens
|
// find asset and liab tokens
|
||||||
let mut tokens = account
|
let mut tokens = account
|
||||||
|
@ -172,7 +174,7 @@ pub fn maybe_liquidate_account(
|
||||||
};
|
};
|
||||||
|
|
||||||
// try liquidating
|
// try liquidating
|
||||||
let txsig = if account.is_bankrupt() {
|
let txsig = if is_bankrupt {
|
||||||
if tokens.is_empty() {
|
if tokens.is_empty() {
|
||||||
anyhow::bail!("mango account {}, is bankrupt has no active tokens", pubkey);
|
anyhow::bail!("mango account {}, is bankrupt has no active tokens", pubkey);
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,7 +33,6 @@ pub fn account_close(ctx: Context<AccountClose>) -> Result<()> {
|
||||||
// don't perform checks if group is just testing
|
// don't perform checks if group is just testing
|
||||||
if group.testing == 0 {
|
if group.testing == 0 {
|
||||||
require!(!account.fixed.being_liquidated(), MangoError::SomeError);
|
require!(!account.fixed.being_liquidated(), MangoError::SomeError);
|
||||||
require!(!account.fixed.is_bankrupt(), MangoError::SomeError);
|
|
||||||
require_eq!(account.fixed.delegate, Pubkey::default());
|
require_eq!(account.fixed.delegate, Pubkey::default());
|
||||||
for ele in account.token_iter() {
|
for ele in account.token_iter() {
|
||||||
require_eq!(ele.is_active(), false);
|
require_eq!(ele.is_active(), false);
|
||||||
|
|
|
@ -48,7 +48,6 @@ pub fn account_create(
|
||||||
account.fixed.bump = *ctx.bumps.get("account").ok_or(MangoError::SomeError)?;
|
account.fixed.bump = *ctx.bumps.get("account").ok_or(MangoError::SomeError)?;
|
||||||
account.fixed.delegate = Pubkey::default();
|
account.fixed.delegate = Pubkey::default();
|
||||||
account.fixed.set_being_liquidated(false);
|
account.fixed.set_being_liquidated(false);
|
||||||
account.fixed.set_bankrupt(false);
|
|
||||||
|
|
||||||
account.expand_dynamic_content(token_count, serum3_count, perp_count, perp_oo_count)?;
|
account.expand_dynamic_content(token_count, serum3_count, perp_count, perp_oo_count)?;
|
||||||
|
|
||||||
|
|
|
@ -184,8 +184,6 @@ pub fn flash_loan_end<'key, 'accounts, 'remaining, 'info>(
|
||||||
let mut account = ctx.accounts.account.load_mut()?;
|
let mut account = ctx.accounts.account.load_mut()?;
|
||||||
let group = account.fixed.group;
|
let group = account.fixed.group;
|
||||||
|
|
||||||
require!(!account.fixed.is_bankrupt(), MangoError::IsBankrupt);
|
|
||||||
|
|
||||||
// Find index at which vaults start
|
// Find index at which vaults start
|
||||||
let vaults_index = ctx
|
let vaults_index = ctx
|
||||||
.remaining_accounts
|
.remaining_accounts
|
||||||
|
|
|
@ -90,23 +90,27 @@ pub fn liq_token_bankruptcy(
|
||||||
.is_owner_or_delegate(ctx.accounts.liqor_owner.key()),
|
.is_owner_or_delegate(ctx.accounts.liqor_owner.key()),
|
||||||
MangoError::SomeError
|
MangoError::SomeError
|
||||||
);
|
);
|
||||||
require!(!liqor.fixed.is_bankrupt(), MangoError::IsBankrupt);
|
|
||||||
|
|
||||||
let mut liqee = ctx.accounts.liqee.load_mut()?;
|
|
||||||
require!(liqee.fixed.is_bankrupt(), MangoError::IsBankrupt);
|
|
||||||
|
|
||||||
let liab_bank = bank_ais[0].load::<Bank>()?;
|
|
||||||
let liab_deposit_index = liab_bank.deposit_index;
|
|
||||||
let (liqee_liab, liqee_raw_token_index) = liqee.token_get_mut(liab_token_index)?;
|
|
||||||
let mut remaining_liab_loss = -liqee_liab.native(&liab_bank);
|
|
||||||
require_gt!(remaining_liab_loss, I80F48::ZERO);
|
|
||||||
drop(liab_bank);
|
|
||||||
|
|
||||||
let mut account_retriever = ScanningAccountRetriever::new(health_ais, group_pk)?;
|
let mut account_retriever = ScanningAccountRetriever::new(health_ais, group_pk)?;
|
||||||
|
|
||||||
// find insurance transfer amount
|
let mut liqee = ctx.accounts.liqee.load_mut()?;
|
||||||
|
let mut liqee_health_cache = new_health_cache(&liqee.borrow(), &account_retriever)
|
||||||
|
.context("create liqee health cache")?;
|
||||||
|
require!(
|
||||||
|
!liqee_health_cache.has_liquidatable_assets(),
|
||||||
|
MangoError::IsNotBankrupt
|
||||||
|
);
|
||||||
|
liqee.fixed.set_being_liquidated(true);
|
||||||
|
|
||||||
let (liab_bank, liab_price, opt_quote_bank_and_price) =
|
let (liab_bank, liab_price, opt_quote_bank_and_price) =
|
||||||
account_retriever.banks_mut_and_oracles(liab_token_index, QUOTE_TOKEN_INDEX)?;
|
account_retriever.banks_mut_and_oracles(liab_token_index, QUOTE_TOKEN_INDEX)?;
|
||||||
|
let liab_deposit_index = liab_bank.deposit_index;
|
||||||
|
let (liqee_liab, liqee_raw_token_index) = liqee.token_get_mut(liab_token_index)?;
|
||||||
|
let initial_liab_native = liqee_liab.native(&liab_bank);
|
||||||
|
let mut remaining_liab_loss = -initial_liab_native;
|
||||||
|
require_gt!(remaining_liab_loss, I80F48::ZERO);
|
||||||
|
|
||||||
|
// find insurance transfer amount
|
||||||
let liab_fee_factor = if liab_token_index == QUOTE_TOKEN_INDEX {
|
let liab_fee_factor = if liab_token_index == QUOTE_TOKEN_INDEX {
|
||||||
I80F48::ONE
|
I80F48::ONE
|
||||||
} else {
|
} else {
|
||||||
|
@ -226,14 +230,16 @@ pub fn liq_token_bankruptcy(
|
||||||
liqee_liab_active = false;
|
liqee_liab_active = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the account has no more borrows then it's no longer bankrupt
|
let liab_bank = bank_ais[0].load::<Bank>()?;
|
||||||
// and should (always?) no longer be liquidated.
|
let end_liab_native = liqee_liab.native(&liab_bank);
|
||||||
let account_retriever = ScanningAccountRetriever::new(health_ais, group_pk)?;
|
liqee_health_cache
|
||||||
let liqee_health_cache = new_health_cache(&liqee.borrow(), &account_retriever)?;
|
.adjust_token_balance(liab_token_index, cm!(end_liab_native - initial_liab_native))?;
|
||||||
liqee.fixed.set_bankrupt(liqee_health_cache.has_borrows());
|
|
||||||
if !liqee.is_bankrupt() && liqee_health_cache.health(HealthType::Init) >= 0 {
|
// Check liqee health again
|
||||||
liqee.fixed.set_being_liquidated(false);
|
let liqee_init_health = liqee_health_cache.health(HealthType::Init);
|
||||||
}
|
liqee
|
||||||
|
.fixed
|
||||||
|
.maybe_recover_from_being_liquidated(liqee_init_health);
|
||||||
|
|
||||||
if !liqee_liab_active {
|
if !liqee_liab_active {
|
||||||
liqee.token_deactivate(liqee_raw_token_index);
|
liqee.token_deactivate(liqee_raw_token_index);
|
||||||
|
|
|
@ -47,10 +47,8 @@ pub fn liq_token_with_token(
|
||||||
.is_owner_or_delegate(ctx.accounts.liqor_owner.key()),
|
.is_owner_or_delegate(ctx.accounts.liqor_owner.key()),
|
||||||
MangoError::SomeError
|
MangoError::SomeError
|
||||||
);
|
);
|
||||||
require!(!liqor.fixed.is_bankrupt(), MangoError::IsBankrupt);
|
|
||||||
|
|
||||||
let mut liqee = ctx.accounts.liqee.load_mut()?;
|
let mut liqee = ctx.accounts.liqee.load_mut()?;
|
||||||
require!(!liqee.fixed.is_bankrupt(), MangoError::IsBankrupt);
|
|
||||||
|
|
||||||
// Initial liqee health check
|
// Initial liqee health check
|
||||||
let mut liqee_health_cache = new_health_cache(&liqee.borrow(), &account_retriever)
|
let mut liqee_health_cache = new_health_cache(&liqee.borrow(), &account_retriever)
|
||||||
|
@ -236,18 +234,10 @@ pub fn liq_token_with_token(
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check liqee health again
|
// Check liqee health again
|
||||||
let maint_health = liqee_health_cache.health(HealthType::Maint);
|
let liqee_init_health = liqee_health_cache.health(HealthType::Init);
|
||||||
if maint_health < I80F48::ZERO {
|
liqee
|
||||||
liqee
|
.fixed
|
||||||
.fixed
|
.maybe_recover_from_being_liquidated(liqee_init_health);
|
||||||
.set_bankrupt(!liqee_health_cache.has_liquidatable_assets());
|
|
||||||
} else {
|
|
||||||
let init_health = liqee_health_cache.health(HealthType::Init);
|
|
||||||
|
|
||||||
// this is equivalent to one native USDC or 1e-6 USDC
|
|
||||||
// This is used as threshold to flip flag instead of 0 because of dust issues
|
|
||||||
liqee.fixed.set_being_liquidated(init_health < -I80F48::ONE);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check liqor's health
|
// Check liqor's health
|
||||||
let liqor_health = compute_health(&liqor.borrow(), HealthType::Init, &account_retriever)
|
let liqor_health = compute_health(&liqor.borrow(), HealthType::Init, &account_retriever)
|
||||||
|
|
|
@ -31,8 +31,6 @@ pub fn perp_cancel_all_orders(ctx: Context<PerpCancelAllOrders>, limit: u8) -> R
|
||||||
MangoError::SomeError
|
MangoError::SomeError
|
||||||
);
|
);
|
||||||
|
|
||||||
require!(!account.fixed.is_bankrupt(), MangoError::IsBankrupt);
|
|
||||||
|
|
||||||
let mut perp_market = ctx.accounts.perp_market.load_mut()?;
|
let mut perp_market = ctx.accounts.perp_market.load_mut()?;
|
||||||
let bids = ctx.accounts.bids.load_mut()?;
|
let bids = ctx.accounts.bids.load_mut()?;
|
||||||
let asks = ctx.accounts.asks.load_mut()?;
|
let asks = ctx.accounts.asks.load_mut()?;
|
||||||
|
|
|
@ -36,8 +36,6 @@ pub fn perp_cancel_all_orders_by_side(
|
||||||
MangoError::SomeError
|
MangoError::SomeError
|
||||||
);
|
);
|
||||||
|
|
||||||
require!(!account.fixed.is_bankrupt(), MangoError::IsBankrupt);
|
|
||||||
|
|
||||||
let mut perp_market = ctx.accounts.perp_market.load_mut()?;
|
let mut perp_market = ctx.accounts.perp_market.load_mut()?;
|
||||||
let bids = ctx.accounts.bids.load_mut()?;
|
let bids = ctx.accounts.bids.load_mut()?;
|
||||||
let asks = ctx.accounts.asks.load_mut()?;
|
let asks = ctx.accounts.asks.load_mut()?;
|
||||||
|
|
|
@ -31,8 +31,6 @@ pub fn perp_cancel_order(ctx: Context<PerpCancelOrder>, order_id: i128) -> Resul
|
||||||
MangoError::SomeError
|
MangoError::SomeError
|
||||||
);
|
);
|
||||||
|
|
||||||
require!(!account.fixed.is_bankrupt(), MangoError::IsBankrupt);
|
|
||||||
|
|
||||||
let perp_market = ctx.accounts.perp_market.load_mut()?;
|
let perp_market = ctx.accounts.perp_market.load_mut()?;
|
||||||
let bids = ctx.accounts.bids.load_mut()?;
|
let bids = ctx.accounts.bids.load_mut()?;
|
||||||
let asks = ctx.accounts.asks.load_mut()?;
|
let asks = ctx.accounts.asks.load_mut()?;
|
||||||
|
|
|
@ -34,8 +34,6 @@ pub fn perp_cancel_order_by_client_order_id(
|
||||||
MangoError::SomeError
|
MangoError::SomeError
|
||||||
);
|
);
|
||||||
|
|
||||||
require!(!account.fixed.is_bankrupt(), MangoError::IsBankrupt);
|
|
||||||
|
|
||||||
let perp_market = ctx.accounts.perp_market.load_mut()?;
|
let perp_market = ctx.accounts.perp_market.load_mut()?;
|
||||||
let bids = ctx.accounts.bids.load_mut()?;
|
let bids = ctx.accounts.bids.load_mut()?;
|
||||||
let asks = ctx.accounts.asks.load_mut()?;
|
let asks = ctx.accounts.asks.load_mut()?;
|
||||||
|
|
|
@ -81,7 +81,6 @@ pub fn perp_place_order(
|
||||||
MangoError::SomeError
|
MangoError::SomeError
|
||||||
);
|
);
|
||||||
|
|
||||||
require!(!account.fixed.is_bankrupt(), MangoError::IsBankrupt);
|
|
||||||
let account_pk = ctx.accounts.account.key();
|
let account_pk = ctx.accounts.account.key();
|
||||||
|
|
||||||
{
|
{
|
||||||
|
|
|
@ -51,8 +51,6 @@ pub fn serum3_cancel_all_orders(ctx: Context<Serum3CancelAllOrders>, limit: u8)
|
||||||
MangoError::SomeError
|
MangoError::SomeError
|
||||||
);
|
);
|
||||||
|
|
||||||
require!(!account.fixed.is_bankrupt(), MangoError::IsBankrupt);
|
|
||||||
|
|
||||||
let serum_market = ctx.accounts.serum_market.load()?;
|
let serum_market = ctx.accounts.serum_market.load()?;
|
||||||
|
|
||||||
// Validate open_orders
|
// Validate open_orders
|
||||||
|
|
|
@ -64,8 +64,6 @@ pub fn serum3_cancel_order(
|
||||||
MangoError::SomeError
|
MangoError::SomeError
|
||||||
);
|
);
|
||||||
|
|
||||||
require!(!account.fixed.is_bankrupt(), MangoError::IsBankrupt);
|
|
||||||
|
|
||||||
// Validate open_orders
|
// Validate open_orders
|
||||||
require!(
|
require!(
|
||||||
account
|
account
|
||||||
|
|
|
@ -41,8 +41,6 @@ pub fn serum3_close_open_orders(ctx: Context<Serum3CloseOpenOrders>) -> Result<(
|
||||||
MangoError::SomeError
|
MangoError::SomeError
|
||||||
);
|
);
|
||||||
|
|
||||||
require!(!account.fixed.is_bankrupt(), MangoError::IsBankrupt);
|
|
||||||
|
|
||||||
let serum_market = ctx.accounts.serum_market.load()?;
|
let serum_market = ctx.accounts.serum_market.load()?;
|
||||||
|
|
||||||
// Validate open_orders
|
// Validate open_orders
|
||||||
|
|
|
@ -53,8 +53,6 @@ pub fn serum3_create_open_orders(ctx: Context<Serum3CreateOpenOrders>) -> Result
|
||||||
MangoError::SomeError
|
MangoError::SomeError
|
||||||
);
|
);
|
||||||
|
|
||||||
require!(!account.fixed.is_bankrupt(), MangoError::IsBankrupt);
|
|
||||||
|
|
||||||
let serum_account = account.serum3_create(serum_market.market_index)?;
|
let serum_account = account.serum3_create(serum_market.market_index)?;
|
||||||
serum_account.open_orders = ctx.accounts.open_orders.key();
|
serum_account.open_orders = ctx.accounts.open_orders.key();
|
||||||
serum_account.base_token_index = serum_market.base_token_index;
|
serum_account.base_token_index = serum_market.base_token_index;
|
||||||
|
|
|
@ -70,7 +70,6 @@ pub fn serum3_liq_force_cancel_orders(
|
||||||
//
|
//
|
||||||
{
|
{
|
||||||
let account = ctx.accounts.account.load()?;
|
let account = ctx.accounts.account.load()?;
|
||||||
require!(!account.fixed.is_bankrupt(), MangoError::IsBankrupt);
|
|
||||||
let serum_market = ctx.accounts.serum_market.load()?;
|
let serum_market = ctx.accounts.serum_market.load()?;
|
||||||
|
|
||||||
// Validate open_orders
|
// Validate open_orders
|
||||||
|
|
|
@ -168,7 +168,6 @@ pub fn serum3_place_order(
|
||||||
account.fixed.is_owner_or_delegate(ctx.accounts.owner.key()),
|
account.fixed.is_owner_or_delegate(ctx.accounts.owner.key()),
|
||||||
MangoError::SomeError
|
MangoError::SomeError
|
||||||
);
|
);
|
||||||
require!(!account.fixed.is_bankrupt(), MangoError::IsBankrupt);
|
|
||||||
|
|
||||||
// Validate open_orders
|
// Validate open_orders
|
||||||
require!(
|
require!(
|
||||||
|
|
|
@ -78,8 +78,6 @@ pub fn serum3_settle_funds(ctx: Context<Serum3SettleFunds>) -> Result<()> {
|
||||||
MangoError::SomeError
|
MangoError::SomeError
|
||||||
);
|
);
|
||||||
|
|
||||||
require!(!account.fixed.is_bankrupt(), MangoError::IsBankrupt);
|
|
||||||
|
|
||||||
// Validate open_orders
|
// Validate open_orders
|
||||||
require!(
|
require!(
|
||||||
account
|
account
|
||||||
|
|
|
@ -58,7 +58,6 @@ pub fn token_deposit(ctx: Context<TokenDeposit>, amount: u64) -> Result<()> {
|
||||||
|
|
||||||
// Get the account's position for that token index
|
// Get the account's position for that token index
|
||||||
let mut account = ctx.accounts.account.load_mut()?;
|
let mut account = ctx.accounts.account.load_mut()?;
|
||||||
require!(!account.fixed.is_bankrupt(), MangoError::IsBankrupt);
|
|
||||||
|
|
||||||
let (position, raw_token_index, active_token_index) =
|
let (position, raw_token_index, active_token_index) =
|
||||||
account.token_get_mut_or_create(token_index)?;
|
account.token_get_mut_or_create(token_index)?;
|
||||||
|
@ -94,12 +93,11 @@ pub fn token_deposit(ctx: Context<TokenDeposit>, amount: u64) -> Result<()> {
|
||||||
|
|
||||||
//
|
//
|
||||||
// Health computation
|
// Health computation
|
||||||
// TODO: This will be used to disable is_bankrupt or being_liquidated
|
|
||||||
// when health recovers sufficiently
|
|
||||||
//
|
//
|
||||||
let health = compute_health(&account.borrow(), HealthType::Init, &retriever)
|
let health = compute_health(&account.borrow(), HealthType::Init, &retriever)
|
||||||
.context("post-deposit init health")?;
|
.context("post-deposit init health")?;
|
||||||
msg!("health: {}", health);
|
msg!("health: {}", health);
|
||||||
|
account.fixed.maybe_recover_from_being_liquidated(health);
|
||||||
|
|
||||||
//
|
//
|
||||||
// Deactivate the position only after the health check because the user passed in
|
// Deactivate the position only after the health check because the user passed in
|
||||||
|
|
|
@ -61,7 +61,6 @@ pub fn token_withdraw(ctx: Context<TokenWithdraw>, amount: u64, allow_borrow: bo
|
||||||
// Get the account's position for that token index
|
// Get the account's position for that token index
|
||||||
let mut account = ctx.accounts.account.load_mut()?;
|
let mut account = ctx.accounts.account.load_mut()?;
|
||||||
|
|
||||||
require!(!account.fixed.is_bankrupt(), MangoError::IsBankrupt);
|
|
||||||
let (position, raw_token_index, active_token_index) =
|
let (position, raw_token_index, active_token_index) =
|
||||||
account.token_get_mut_or_create(token_index)?;
|
account.token_get_mut_or_create(token_index)?;
|
||||||
|
|
||||||
|
|
|
@ -16,7 +16,7 @@ use crate::util::checked_math as cm;
|
||||||
|
|
||||||
use super::MangoAccountRef;
|
use super::MangoAccountRef;
|
||||||
|
|
||||||
const BANKRUPTCY_DUST_THRESHOLD: I80F48 = I80F48!(0.000001);
|
const ONE_NATIVE_USDC_IN_USD: I80F48 = I80F48!(0.000001);
|
||||||
|
|
||||||
/// This trait abstracts how to find accounts needed for the health computation.
|
/// This trait abstracts how to find accounts needed for the health computation.
|
||||||
///
|
///
|
||||||
|
@ -565,13 +565,14 @@ impl HealthCache {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn has_liquidatable_assets(&self) -> bool {
|
pub fn has_liquidatable_assets(&self) -> bool {
|
||||||
let spot_liquidatable = self.token_infos.iter().any(|ti| {
|
let spot_liquidatable = self
|
||||||
ti.balance > BANKRUPTCY_DUST_THRESHOLD || ti.serum3_max_reserved.is_positive()
|
.token_infos
|
||||||
});
|
.iter()
|
||||||
|
.any(|ti| ti.balance.is_positive() || ti.serum3_max_reserved.is_positive());
|
||||||
let perp_liquidatable = self
|
let perp_liquidatable = self
|
||||||
.perp_infos
|
.perp_infos
|
||||||
.iter()
|
.iter()
|
||||||
.any(|p| p.base != 0 || p.quote > BANKRUPTCY_DUST_THRESHOLD);
|
.any(|p| p.base != 0 || p.quote > ONE_NATIVE_USDC_IN_USD);
|
||||||
spot_liquidatable || perp_liquidatable
|
spot_liquidatable || perp_liquidatable
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -54,8 +54,7 @@ pub struct MangoAccount {
|
||||||
/// This account cannot open new positions or borrow until `init_health >= 0`
|
/// This account cannot open new positions or borrow until `init_health >= 0`
|
||||||
being_liquidated: u8,
|
being_liquidated: u8,
|
||||||
|
|
||||||
/// This account cannot do anything except go through `resolve_bankruptcy`
|
padding5: u8,
|
||||||
is_bankrupt: u8,
|
|
||||||
|
|
||||||
pub bump: u8,
|
pub bump: u8,
|
||||||
|
|
||||||
|
@ -97,7 +96,7 @@ impl Default for MangoAccount {
|
||||||
owner: Pubkey::default(),
|
owner: Pubkey::default(),
|
||||||
delegate: Pubkey::default(),
|
delegate: Pubkey::default(),
|
||||||
being_liquidated: 0,
|
being_liquidated: 0,
|
||||||
is_bankrupt: 0,
|
padding5: 0,
|
||||||
account_num: 0,
|
account_num: 0,
|
||||||
bump: 0,
|
bump: 0,
|
||||||
padding: Default::default(),
|
padding: Default::default(),
|
||||||
|
@ -193,7 +192,7 @@ pub struct MangoAccountFixed {
|
||||||
pub delegate: Pubkey,
|
pub delegate: Pubkey,
|
||||||
pub account_num: u32,
|
pub account_num: u32,
|
||||||
being_liquidated: u8,
|
being_liquidated: u8,
|
||||||
is_bankrupt: u8,
|
padding2: u8,
|
||||||
pub bump: u8,
|
pub bump: u8,
|
||||||
pub padding: [u8; 1],
|
pub padding: [u8; 1],
|
||||||
pub net_deposits: f32,
|
pub net_deposits: f32,
|
||||||
|
@ -214,14 +213,6 @@ impl MangoAccountFixed {
|
||||||
self.owner == ix_signer || self.delegate == ix_signer
|
self.owner == ix_signer || self.delegate == ix_signer
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_bankrupt(&self) -> bool {
|
|
||||||
self.is_bankrupt != 0
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_bankrupt(&mut self, b: bool) {
|
|
||||||
self.is_bankrupt = if b { 1 } else { 0 };
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn being_liquidated(&self) -> bool {
|
pub fn being_liquidated(&self) -> bool {
|
||||||
self.being_liquidated != 0
|
self.being_liquidated != 0
|
||||||
}
|
}
|
||||||
|
@ -229,6 +220,14 @@ impl MangoAccountFixed {
|
||||||
pub fn set_being_liquidated(&mut self, b: bool) {
|
pub fn set_being_liquidated(&mut self, b: bool) {
|
||||||
self.being_liquidated = if b { 1 } else { 0 };
|
self.being_liquidated = if b { 1 } else { 0 };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn maybe_recover_from_being_liquidated(&mut self, init_health: I80F48) {
|
||||||
|
// This is used as threshold to flip flag instead of 0 because of dust issues
|
||||||
|
let one_native_usdc = I80F48::ONE;
|
||||||
|
if self.being_liquidated() && init_health > -one_native_usdc {
|
||||||
|
self.set_being_liquidated(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DynamicAccountType for MangoAccount {
|
impl DynamicAccountType for MangoAccount {
|
||||||
|
@ -522,10 +521,6 @@ impl<
|
||||||
self.fixed().being_liquidated()
|
self.fixed().being_liquidated()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_bankrupt(&self) -> bool {
|
|
||||||
self.fixed().is_bankrupt()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn borrow(&self) -> DynamicAccountRef<MangoAccount> {
|
pub fn borrow(&self) -> DynamicAccountRef<MangoAccount> {
|
||||||
DynamicAccount {
|
DynamicAccount {
|
||||||
header: self.header(),
|
header: self.header(),
|
||||||
|
|
|
@ -220,7 +220,6 @@ async fn test_bankrupt_tokens_socialize_loss() -> Result<(), TransportError> {
|
||||||
);
|
);
|
||||||
let liqee = get_mango_account(solana, account).await;
|
let liqee = get_mango_account(solana, account).await;
|
||||||
assert!(liqee.being_liquidated());
|
assert!(liqee.being_liquidated());
|
||||||
assert!(!liqee.is_bankrupt());
|
|
||||||
|
|
||||||
// eat collateral2, leaving the account bankrupt
|
// eat collateral2, leaving the account bankrupt
|
||||||
send_tx(
|
send_tx(
|
||||||
|
@ -246,7 +245,6 @@ async fn test_bankrupt_tokens_socialize_loss() -> Result<(), TransportError> {
|
||||||
);
|
);
|
||||||
let liqee = get_mango_account(solana, account).await;
|
let liqee = get_mango_account(solana, account).await;
|
||||||
assert!(liqee.being_liquidated());
|
assert!(liqee.being_liquidated());
|
||||||
assert!(liqee.is_bankrupt());
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// TEST: socialize loss on borrow1 and 2
|
// TEST: socialize loss on borrow1 and 2
|
||||||
|
@ -272,7 +270,6 @@ async fn test_bankrupt_tokens_socialize_loss() -> Result<(), TransportError> {
|
||||||
);
|
);
|
||||||
let liqee = get_mango_account(solana, account).await;
|
let liqee = get_mango_account(solana, account).await;
|
||||||
assert!(liqee.being_liquidated());
|
assert!(liqee.being_liquidated());
|
||||||
assert!(liqee.is_bankrupt());
|
|
||||||
assert!(account_position_closed(solana, account, borrow_token1.bank).await);
|
assert!(account_position_closed(solana, account, borrow_token1.bank).await);
|
||||||
// both bank's borrows were completely wiped: no one else borrowed
|
// both bank's borrows were completely wiped: no one else borrowed
|
||||||
let borrow1_bank0: Bank = solana.get_account(borrow_token1.bank).await;
|
let borrow1_bank0: Bank = solana.get_account(borrow_token1.bank).await;
|
||||||
|
@ -299,7 +296,6 @@ async fn test_bankrupt_tokens_socialize_loss() -> Result<(), TransportError> {
|
||||||
);
|
);
|
||||||
let liqee = get_mango_account(solana, account).await;
|
let liqee = get_mango_account(solana, account).await;
|
||||||
assert!(!liqee.being_liquidated());
|
assert!(!liqee.being_liquidated());
|
||||||
assert!(!liqee.is_bankrupt());
|
|
||||||
assert!(account_position_closed(solana, account, borrow_token2.bank).await);
|
assert!(account_position_closed(solana, account, borrow_token2.bank).await);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -531,7 +527,6 @@ async fn test_bankrupt_tokens_insurance_fund() -> Result<(), TransportError> {
|
||||||
assert!(account_position_closed(solana, account, collateral_token1.bank).await);
|
assert!(account_position_closed(solana, account, collateral_token1.bank).await);
|
||||||
let liqee = get_mango_account(solana, account).await;
|
let liqee = get_mango_account(solana, account).await;
|
||||||
assert!(liqee.being_liquidated());
|
assert!(liqee.being_liquidated());
|
||||||
assert!(!liqee.is_bankrupt());
|
|
||||||
|
|
||||||
// eat collateral2, leaving the account bankrupt
|
// eat collateral2, leaving the account bankrupt
|
||||||
send_tx(
|
send_tx(
|
||||||
|
@ -552,7 +547,6 @@ async fn test_bankrupt_tokens_insurance_fund() -> Result<(), TransportError> {
|
||||||
assert!(account_position_closed(solana, account, collateral_token2.bank).await,);
|
assert!(account_position_closed(solana, account, collateral_token2.bank).await,);
|
||||||
let liqee = get_mango_account(solana, account).await;
|
let liqee = get_mango_account(solana, account).await;
|
||||||
assert!(liqee.being_liquidated());
|
assert!(liqee.being_liquidated());
|
||||||
assert!(liqee.is_bankrupt());
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// TEST: use the insurance fund to liquidate borrow1 and borrow2
|
// TEST: use the insurance fund to liquidate borrow1 and borrow2
|
||||||
|
@ -577,7 +571,6 @@ async fn test_bankrupt_tokens_insurance_fund() -> Result<(), TransportError> {
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let liqee = get_mango_account(solana, account).await;
|
let liqee = get_mango_account(solana, account).await;
|
||||||
assert!(liqee.being_liquidated());
|
assert!(liqee.being_liquidated());
|
||||||
assert!(liqee.is_bankrupt());
|
|
||||||
assert!(account_position_closed(solana, account, borrow_token1.bank).await);
|
assert!(account_position_closed(solana, account, borrow_token1.bank).await);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
solana.token_account_balance(insurance_vault).await,
|
solana.token_account_balance(insurance_vault).await,
|
||||||
|
@ -610,7 +603,6 @@ async fn test_bankrupt_tokens_insurance_fund() -> Result<(), TransportError> {
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let liqee = get_mango_account(solana, account).await;
|
let liqee = get_mango_account(solana, account).await;
|
||||||
assert!(liqee.being_liquidated());
|
assert!(liqee.being_liquidated());
|
||||||
assert!(liqee.is_bankrupt());
|
|
||||||
assert!(account_position_closed(solana, account, borrow_token1.bank).await);
|
assert!(account_position_closed(solana, account, borrow_token1.bank).await);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
account_position(solana, account, borrow_token2.bank).await,
|
account_position(solana, account, borrow_token2.bank).await,
|
||||||
|
@ -645,7 +637,6 @@ async fn test_bankrupt_tokens_insurance_fund() -> Result<(), TransportError> {
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let liqee = get_mango_account(solana, account).await;
|
let liqee = get_mango_account(solana, account).await;
|
||||||
assert!(!liqee.being_liquidated());
|
assert!(!liqee.being_liquidated());
|
||||||
assert!(!liqee.is_bankrupt());
|
|
||||||
assert!(account_position_closed(solana, account, borrow_token1.bank).await);
|
assert!(account_position_closed(solana, account, borrow_token1.bank).await);
|
||||||
assert!(account_position_closed(solana, account, borrow_token2.bank).await);
|
assert!(account_position_closed(solana, account, borrow_token2.bank).await);
|
||||||
assert_eq!(solana.token_account_balance(insurance_vault).await, 0);
|
assert_eq!(solana.token_account_balance(insurance_vault).await, 0);
|
||||||
|
|
|
@ -406,7 +406,6 @@ async fn test_liq_tokens_with_token() -> Result<(), TransportError> {
|
||||||
assert!(account_position_closed(solana, account, collateral_token2.bank).await,);
|
assert!(account_position_closed(solana, account, collateral_token2.bank).await,);
|
||||||
let liqee = get_mango_account(solana, account).await;
|
let liqee = get_mango_account(solana, account).await;
|
||||||
assert!(liqee.being_liquidated());
|
assert!(liqee.being_liquidated());
|
||||||
assert!(!liqee.is_bankrupt());
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// TEST: liquidate the remaining borrow2 against collateral1,
|
// TEST: liquidate the remaining borrow2 against collateral1,
|
||||||
|
@ -436,7 +435,6 @@ async fn test_liq_tokens_with_token() -> Result<(), TransportError> {
|
||||||
);
|
);
|
||||||
let liqee = get_mango_account(solana, account).await;
|
let liqee = get_mango_account(solana, account).await;
|
||||||
assert!(liqee.being_liquidated());
|
assert!(liqee.being_liquidated());
|
||||||
assert!(!liqee.is_bankrupt());
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// TEST: liquidate borrow1 with collateral1, but place a limit
|
// TEST: liquidate borrow1 with collateral1, but place a limit
|
||||||
|
@ -468,7 +466,6 @@ async fn test_liq_tokens_with_token() -> Result<(), TransportError> {
|
||||||
);
|
);
|
||||||
let liqee = get_mango_account(solana, account).await;
|
let liqee = get_mango_account(solana, account).await;
|
||||||
assert!(liqee.being_liquidated());
|
assert!(liqee.being_liquidated());
|
||||||
assert!(!liqee.is_bankrupt());
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// TEST: liquidate borrow1 with collateral1, making the account healthy again
|
// TEST: liquidate borrow1 with collateral1, making the account healthy again
|
||||||
|
@ -503,7 +500,6 @@ async fn test_liq_tokens_with_token() -> Result<(), TransportError> {
|
||||||
);
|
);
|
||||||
let liqee = get_mango_account(solana, account).await;
|
let liqee = get_mango_account(solana, account).await;
|
||||||
assert!(!liqee.being_liquidated());
|
assert!(!liqee.being_liquidated());
|
||||||
assert!(!liqee.is_bankrupt());
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// TEST: bankruptcy when collateral is dusted
|
// TEST: bankruptcy when collateral is dusted
|
||||||
|
@ -567,11 +563,12 @@ async fn test_liq_tokens_with_token() -> Result<(), TransportError> {
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
// Liqee's remaining collateral got dusted, only borrows remain: bankrupt
|
// Liqee's remaining collateral got dusted, only borrows remain
|
||||||
|
// but the borrow amount is so tiny, that being_liquidated is already switched off
|
||||||
let liqee = get_mango_account(solana, account).await;
|
let liqee = get_mango_account(solana, account).await;
|
||||||
assert_eq!(liqee.token_iter_active().count(), 1);
|
assert_eq!(liqee.token_iter_active().count(), 1);
|
||||||
assert!(liqee.is_bankrupt());
|
assert!(account_position_f64(solana, account, borrow_token1.bank).await > -1.0);
|
||||||
assert!(liqee.being_liquidated());
|
assert!(!liqee.being_liquidated());
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,7 +27,6 @@ export class MangoAccount {
|
||||||
name: number[];
|
name: number[];
|
||||||
delegate: PublicKey;
|
delegate: PublicKey;
|
||||||
beingLiquidated: number;
|
beingLiquidated: number;
|
||||||
isBankrupt: number;
|
|
||||||
accountNum: number;
|
accountNum: number;
|
||||||
bump: number;
|
bump: number;
|
||||||
netDeposits: number;
|
netDeposits: number;
|
||||||
|
@ -46,7 +45,6 @@ export class MangoAccount {
|
||||||
obj.name,
|
obj.name,
|
||||||
obj.delegate,
|
obj.delegate,
|
||||||
obj.beingLiquidated,
|
obj.beingLiquidated,
|
||||||
obj.isBankrupt,
|
|
||||||
obj.accountNum,
|
obj.accountNum,
|
||||||
obj.bump,
|
obj.bump,
|
||||||
obj.netDeposits,
|
obj.netDeposits,
|
||||||
|
@ -67,7 +65,6 @@ export class MangoAccount {
|
||||||
name: number[],
|
name: number[],
|
||||||
public delegate: PublicKey,
|
public delegate: PublicKey,
|
||||||
beingLiquidated: number,
|
beingLiquidated: number,
|
||||||
isBankrupt: number,
|
|
||||||
public accountNum: number,
|
public accountNum: number,
|
||||||
bump: number,
|
bump: number,
|
||||||
netDeposits: number,
|
netDeposits: number,
|
||||||
|
|
|
@ -3007,10 +3007,7 @@ export type MangoV4 = {
|
||||||
"type": "u8"
|
"type": "u8"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "isBankrupt",
|
"name": "padding5",
|
||||||
"docs": [
|
|
||||||
"This account cannot do anything except go through `resolve_bankruptcy`"
|
|
||||||
],
|
|
||||||
"type": "u8"
|
"type": "u8"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -8054,10 +8051,7 @@ export const IDL: MangoV4 = {
|
||||||
"type": "u8"
|
"type": "u8"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "isBankrupt",
|
"name": "padding5",
|
||||||
"docs": [
|
|
||||||
"This account cannot do anything except go through `resolve_bankruptcy`"
|
|
||||||
],
|
|
||||||
"type": "u8"
|
"type": "u8"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in New Issue