Liq: basic liq_token_bankruptcy use

This commit is contained in:
Christian Kamm 2022-07-13 14:04:20 +02:00
parent 74873ad46b
commit bbca1d8763
2 changed files with 126 additions and 38 deletions

View File

@ -12,7 +12,7 @@ use fixed::types::I80F48;
use itertools::Itertools;
use mango_v4::instructions::{Serum3OrderType, Serum3SelfTradeBehavior, Serum3Side};
use mango_v4::state::{
Bank, MangoAccount, MintInfo, PerpMarket, PerpMarketIndex, Serum3Market, TokenIndex,
Bank, Group, MangoAccount, MintInfo, PerpMarket, PerpMarketIndex, Serum3Market, TokenIndex,
};
use solana_client::rpc_client::RpcClient;
@ -34,6 +34,7 @@ pub struct MangoClient {
pub admin: Keypair,
pub mango_account_cache: (Pubkey, MangoAccount),
pub group: Pubkey,
pub group_cache: Group,
// TODO: future: this may not scale if there's thousands of mints, probably some function
// wrapping getMultipleAccounts is needed (or bettew: we provide this data as a service)
pub banks_cache: HashMap<String, Vec<(Pubkey, Bank)>>,
@ -75,6 +76,8 @@ impl MangoClient {
)
.0;
let group_data = program.account::<Group>(group)?;
// Mango Account
let mut mango_account_tuples = program.accounts::<MangoAccount>(vec![
RpcFilterType::Memcmp(Memcmp {
@ -238,6 +241,7 @@ impl MangoClient {
payer,
mango_account_cache,
group,
group_cache: group_data,
banks_cache,
banks_cache_by_token_index,
mint_infos_cache,
@ -916,6 +920,73 @@ impl MangoClient {
})
.send()
}
pub fn liq_token_bankruptcy(
&self,
liqee: (&Pubkey, &MangoAccount),
liab_token_index: TokenIndex,
max_liab_transfer: I80F48,
) -> Result<Signature, anchor_client::ClientError> {
let quote_token_index = 0;
let (_, quote_mint_info, _) = self
.mint_infos_cache_by_token_index
.get(&quote_token_index)
.unwrap();
let (liab_mint_info_key, liab_mint_info, _) = self
.mint_infos_cache_by_token_index
.get(&liab_token_index)
.unwrap();
let bank_remaining_ams = liab_mint_info
.banks()
.iter()
.map(|bank_pubkey| AccountMeta {
pubkey: *bank_pubkey,
is_signer: false,
is_writable: true,
})
.collect::<Vec<_>>();
let health_remaining_ams = self
.derive_liquidation_health_check_remaining_account_metas(
liqee.1,
quote_token_index,
liab_token_index,
)
.unwrap();
self.program()
.request()
.instruction(Instruction {
program_id: mango_v4::id(),
accounts: {
let mut ams = anchor_lang::ToAccountMetas::to_account_metas(
&mango_v4::accounts::LiqTokenBankruptcy {
group: self.group(),
liqee: *liqee.0,
liqor: self.mango_account_cache.0,
liqor_owner: self.payer.pubkey(),
liab_mint_info: *liab_mint_info_key,
quote_vault: quote_mint_info.first_vault(),
insurance_vault: self.group_cache.insurance_vault,
token_program: Token::id(),
},
None,
);
ams.extend(bank_remaining_ams);
ams.extend(health_remaining_ams);
ams
},
data: anchor_lang::InstructionData::data(
&mango_v4::instruction::LiqTokenBankruptcy {
liab_token_index,
max_liab_transfer,
},
),
})
.send()
}
}
fn from_serum_style_pubkey(d: &[u64; 4]) -> Pubkey {

View File

@ -121,6 +121,7 @@ pub fn process_account(
) -> anyhow::Result<()> {
// TODO: configurable
let min_health_ratio = I80F48::from_num(50.0f64);
let quote_token_index = 0;
let account = load_mango_account_from_chain::<MangoAccount>(chain_data, pubkey)?;
@ -129,49 +130,65 @@ pub fn process_account(
.expect("always ok")
.health(HealthType::Maint);
// find asset and liab tokens
let mut tokens = account
.tokens
.iter_active()
.map(|token| {
let mint_info_pk = mint_infos.get(&token.token_index).expect("always Ok");
let mint_info = load_mango_account_from_chain::<MintInfo>(chain_data, mint_info_pk)?;
let bank = load_mango_account_from_chain::<Bank>(chain_data, &mint_info.first_bank())?;
let oracle = chain_data.account(&mint_info.oracle)?;
let price = oracle_price(
&KeyedAccountSharedData::new(mint_info.oracle, oracle.clone()),
bank.oracle_config.conf_filter,
bank.mint_decimals,
)?;
Ok((token.token_index, bank, token.native(bank) * price))
})
.collect::<anyhow::Result<Vec<(TokenIndex, &Bank, I80F48)>>>()?;
tokens.sort_by(|a, b| a.2.cmp(&b.2));
let get_max_liab_transfer = |source, target| -> anyhow::Result<I80F48> {
let liqor = load_mango_account_from_chain::<MangoAccount>(
chain_data,
&mango_client.mango_account_cache.0,
)?;
let health_cache =
new_health_cache_(chain_data, mint_infos, perp_markets, liqor).expect("always ok");
let amount =
health_cache.max_swap_source_for_health_ratio(source, target, min_health_ratio)?;
Ok(amount)
};
// try liquidating
if maint_health.is_negative() {
// find asset and liab tokens
let mut tokens = account
.tokens
.iter_active()
.map(|token| {
let mint_info_pk = mint_infos.get(&token.token_index).expect("always Ok");
let mint_info =
load_mango_account_from_chain::<MintInfo>(chain_data, mint_info_pk)?;
let bank =
load_mango_account_from_chain::<Bank>(chain_data, &mint_info.first_bank())?;
let oracle = chain_data.account(&mint_info.oracle)?;
let price = oracle_price(
&KeyedAccountSharedData::new(mint_info.oracle, oracle.clone()),
bank.oracle_config.conf_filter,
bank.mint_decimals,
)?;
Ok((token.token_index, bank, token.native(bank) * price))
})
.collect::<anyhow::Result<Vec<(TokenIndex, &Bank, I80F48)>>>()?;
tokens.sort_by(|a, b| a.2.cmp(&b.2));
if account.is_bankrupt() {
if tokens.is_empty() {
anyhow::bail!("mango account {}, is bankrupt has no active tokens", pubkey);
}
let (liab_token_index, _liab_bank, _liab_price) = tokens.first().unwrap();
let max_liab_transfer = get_max_liab_transfer(*liab_token_index, quote_token_index)?;
let sig = mango_client
.liq_token_bankruptcy((pubkey, account), *liab_token_index, max_liab_transfer)
.context("sending liq_token_bankruptcy")?;
log::info!(
"Liquidated bankruptcy for {}..., maint_health was {}, tx sig {:?}",
&pubkey.to_string()[..3],
maint_health,
sig
);
} else if maint_health.is_negative() {
if tokens.len() < 2 {
anyhow::bail!("mango account {}, has less than 2 active tokens", pubkey);
}
let (asset_token_index, _asset_bank, _asset_price) = tokens.last().unwrap();
let (liab_token_index, _liab_bank, _liab_price) = tokens.first().unwrap();
let max_liab_transfer = {
let liqor = load_mango_account_from_chain::<MangoAccount>(
chain_data,
&mango_client.mango_account_cache.0,
)?;
let health_cache =
new_health_cache_(chain_data, mint_infos, perp_markets, liqor).expect("always ok");
health_cache.max_swap_source_for_health_ratio(
*liab_token_index,
*asset_token_index,
min_health_ratio,
)?
};
let max_liab_transfer = get_max_liab_transfer(*liab_token_index, *asset_token_index)?;
//
// TODO: log liqor's assets in UI form
@ -189,7 +206,7 @@ pub fn process_account(
)
.context("sending liq_token_with_token")?;
log::info!(
"Liquidated {}..., maint_health was {}, tx sig {:?}",
"Liquidated token with token for {}..., maint_health was {}, tx sig {:?}",
&pubkey.to_string()[..3],
maint_health,
sig