mango-v4/programs/mango-v4/src/instructions/liq_token_bankruptcy.rs

113 lines
3.8 KiB
Rust
Raw Normal View History

use anchor_lang::prelude::*;
use fixed::types::I80F48;
use crate::accounts_zerocopy::*;
use crate::error::*;
use crate::state::ScanningAccountRetriever;
use crate::state::*;
use crate::util::checked_math as cm;
// Remaining accounts:
// - all banks for liab_token_index (writable)
// - merged health accounts for liqor+liqee
#[derive(Accounts)]
#[instruction(liab_token_index: TokenIndex)]
pub struct LiqTokenBankruptcy<'info> {
pub group: AccountLoader<'info, Group>,
#[account(
mut,
has_one = group,
)]
pub liqor: AccountLoader<'info, MangoAccount>,
#[account(address = liqor.load()?.owner)]
pub liqor_owner: Signer<'info>,
#[account(
mut,
has_one = group,
)]
pub liqee: AccountLoader<'info, MangoAccount>,
#[account(
has_one = group,
constraint = liab_mint_info.load()?.token_index == liab_token_index,
)]
pub liab_mint_info: AccountLoader<'info, MintInfo>,
}
pub fn liq_token_bankruptcy(
ctx: Context<LiqTokenBankruptcy>,
//asset_token_index: TokenIndex,
liab_token_index: TokenIndex,
//max_liab_transfer: I80F48,
) -> Result<()> {
let group_pk = &ctx.accounts.group.key();
// split remaining accounts into banks and health
let liab_mint_info = ctx.accounts.liab_mint_info.load()?;
let bank_pks = liab_mint_info.banks();
let (bank_ais, health_ais) = &ctx.remaining_accounts.split_at(bank_pks.len());
require!(
bank_ais.iter().map(|ai| ai.key).eq(bank_pks.iter()),
MangoError::SomeError
);
//require!(asset_token_index != liab_token_index, MangoError::SomeError);
//let mut liqor = ctx.accounts.liqor.load_mut()?;
//require!(!liqor.is_bankrupt(), MangoError::IsBankrupt);
let mut liqee = ctx.accounts.liqee.load_mut()?;
require!(liqee.is_bankrupt(), MangoError::SomeError);
// find the total deposits
let mut indexed_total_deposits = I80F48::ZERO;
for bank_ai in bank_ais.iter() {
let bank = bank_ai.load::<Bank>()?;
indexed_total_deposits = cm!(indexed_total_deposits + bank.indexed_deposits);
}
let liab_bank = bank_ais[0].load::<Bank>()?;
let (liqee_position, liqee_raw_token_index, _) =
liqee.tokens.get_mut_or_create(liab_token_index)?;
let abs_native_loss = -liqee_position.native(&liab_bank);
require_gt!(abs_native_loss, I80F48::ZERO);
// TODO: what if loss is greater than entire deposits?
// total_indexed_deposits * (deposit_index - new_deposit_index) = abs_native_loss
let new_deposit_index = cm!(liab_bank.deposit_index - abs_native_loss / indexed_total_deposits);
drop(liab_bank);
let mut amount_to_credit = abs_native_loss;
let mut position_active = false;
for bank_ai in bank_ais.iter() {
let mut bank = bank_ai.load_mut::<Bank>()?;
bank.deposit_index = new_deposit_index;
// credit liqee on each bank where we can offset borrows
let amount_for_bank = amount_to_credit.min(bank.native_borrows());
if amount_for_bank.is_positive() {
position_active = bank.deposit(liqee_position, amount_for_bank)?;
amount_to_credit = cm!(amount_to_credit - amount_for_bank);
if amount_to_credit.is_zero() {
break;
}
}
}
// If the account has no more borrows then it's no longer bankrupt
let account_retriever = ScanningAccountRetriever::new(health_ais, group_pk)?;
let liqee_health_cache = new_health_cache(&liqee, &account_retriever)?;
liqee.set_bankrupt(liqee_health_cache.has_borrows());
// Check liqor's health
//let liqor_health = compute_health(&liqor, HealthType::Init, &account_retriever)?;
//require!(liqor_health >= 0, MangoError::HealthMustBePositive);
require!(!position_active, MangoError::SomeError);
liqee.tokens.deactivate(liqee_raw_token_index);
Ok(())
}