liquidator: Call liq_perp_bankruptcy
This commit is contained in:
parent
12864e15a6
commit
984695e8d0
|
@ -315,8 +315,7 @@ impl MangoClient {
|
|||
pub fn derive_liquidation_health_check_remaining_account_metas(
|
||||
&self,
|
||||
liqee: &MangoAccountValue,
|
||||
asset_token_index: TokenIndex,
|
||||
liab_token_index: TokenIndex,
|
||||
writable_banks: &[TokenIndex],
|
||||
) -> anyhow::Result<Vec<AccountMeta>> {
|
||||
// figure out all the banks/oracles that need to be passed for the health check
|
||||
let mut banks = vec![];
|
||||
|
@ -331,7 +330,7 @@ impl MangoClient {
|
|||
|
||||
for token_index in token_indexes {
|
||||
let mint_info = self.context.mint_info(token_index);
|
||||
let writable_bank = token_index == asset_token_index || token_index == liab_token_index;
|
||||
let writable_bank = writable_banks.iter().contains(&token_index);
|
||||
banks.push((mint_info.first_bank(), writable_bank));
|
||||
oracles.push(mint_info.oracle);
|
||||
}
|
||||
|
@ -928,8 +927,7 @@ impl MangoClient {
|
|||
let perp = self.context.perp(market_index);
|
||||
|
||||
let health_remaining_ams = self
|
||||
.context
|
||||
.derive_health_check_remaining_account_metas(liqee.1, vec![], false)
|
||||
.derive_liquidation_health_check_remaining_account_metas(liqee.1, &[])
|
||||
.unwrap();
|
||||
|
||||
self.program()
|
||||
|
@ -960,6 +958,57 @@ impl MangoClient {
|
|||
.map_err(prettify_client_error)
|
||||
}
|
||||
|
||||
pub fn perp_liq_bankruptcy(
|
||||
&self,
|
||||
liqee: (&Pubkey, &MangoAccountValue),
|
||||
market_index: PerpMarketIndex,
|
||||
max_liab_transfer: u64,
|
||||
) -> anyhow::Result<Signature> {
|
||||
let quote_token_index = 0;
|
||||
let quote_info = self.context.token(quote_token_index);
|
||||
|
||||
let group = account_fetcher_fetch_anchor_account::<Group>(
|
||||
&*self.account_fetcher,
|
||||
&self.context.group,
|
||||
)?;
|
||||
|
||||
let perp = self.context.perp(market_index);
|
||||
|
||||
let health_remaining_ams = self
|
||||
.derive_liquidation_health_check_remaining_account_metas(liqee.1, &[])
|
||||
.unwrap();
|
||||
|
||||
self.program()
|
||||
.request()
|
||||
.instruction(Instruction {
|
||||
program_id: mango_v4::id(),
|
||||
accounts: {
|
||||
let mut ams = anchor_lang::ToAccountMetas::to_account_metas(
|
||||
&mango_v4::accounts::PerpLiqBankruptcy {
|
||||
group: self.group(),
|
||||
perp_market: perp.address,
|
||||
liqor: self.mango_account_address,
|
||||
liqor_owner: self.owner(),
|
||||
liqee: *liqee.0,
|
||||
quote_bank: quote_info.mint_info.first_bank(),
|
||||
quote_vault: quote_info.mint_info.first_vault(),
|
||||
insurance_vault: group.insurance_vault,
|
||||
token_program: Token::id(),
|
||||
},
|
||||
None,
|
||||
);
|
||||
ams.extend(health_remaining_ams.into_iter());
|
||||
ams
|
||||
},
|
||||
data: anchor_lang::InstructionData::data(
|
||||
&mango_v4::instruction::PerpLiqBankruptcy { max_liab_transfer },
|
||||
),
|
||||
})
|
||||
.signer(&self.owner)
|
||||
.send()
|
||||
.map_err(prettify_client_error)
|
||||
}
|
||||
|
||||
//
|
||||
// Liquidation
|
||||
//
|
||||
|
@ -974,8 +1023,7 @@ impl MangoClient {
|
|||
let health_remaining_ams = self
|
||||
.derive_liquidation_health_check_remaining_account_metas(
|
||||
liqee.1,
|
||||
asset_token_index,
|
||||
liab_token_index,
|
||||
&[asset_token_index, liab_token_index],
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
|
@ -1030,8 +1078,7 @@ impl MangoClient {
|
|||
let health_remaining_ams = self
|
||||
.derive_liquidation_health_check_remaining_account_metas(
|
||||
liqee.1,
|
||||
quote_token_index,
|
||||
liab_token_index,
|
||||
&[quote_token_index, liab_token_index],
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
|
|
|
@ -148,7 +148,7 @@ impl<'a> LiquidateHelper<'a> {
|
|||
Ok(Some(sig))
|
||||
}
|
||||
|
||||
fn liq_perp_base_position(&self) -> anyhow::Result<Option<Signature>> {
|
||||
fn perp_liq_base_position(&self) -> anyhow::Result<Option<Signature>> {
|
||||
let mut perp_base_positions = self
|
||||
.liqee
|
||||
.active_perp_positions()
|
||||
|
@ -225,14 +225,14 @@ impl<'a> LiquidateHelper<'a> {
|
|||
Ok(Some(sig))
|
||||
}
|
||||
|
||||
fn settle_perp_pnl(&self) -> anyhow::Result<Option<Signature>> {
|
||||
fn perp_settle_pnl(&self) -> anyhow::Result<Option<Signature>> {
|
||||
let spot_health = self.health_cache.spot_health(HealthType::Maint);
|
||||
let mut perp_settleable_pnl = self
|
||||
.liqee
|
||||
.active_perp_positions()
|
||||
.map(|pp| {
|
||||
.filter_map(|pp| {
|
||||
if pp.base_position_lots() != 0 {
|
||||
return Ok(None);
|
||||
return None;
|
||||
}
|
||||
let pnl = pp.quote_position_native();
|
||||
let settleable_pnl = if pnl > 0 {
|
||||
|
@ -240,12 +240,11 @@ impl<'a> LiquidateHelper<'a> {
|
|||
} else if pnl < 0 && spot_health > 0 {
|
||||
pnl.max(-spot_health)
|
||||
} else {
|
||||
return Ok(None);
|
||||
return None;
|
||||
};
|
||||
Ok(Some((pp.market_index, settleable_pnl)))
|
||||
Some((pp.market_index, settleable_pnl))
|
||||
})
|
||||
.filter_map_ok(|v| v)
|
||||
.collect::<anyhow::Result<Vec<(PerpMarketIndex, I80F48)>>>()?;
|
||||
.collect::<Vec<(PerpMarketIndex, I80F48)>>();
|
||||
// sort by pnl, descending
|
||||
perp_settleable_pnl.sort_by(|a, b| b.1.cmp(&a.1));
|
||||
|
||||
|
@ -293,6 +292,44 @@ impl<'a> LiquidateHelper<'a> {
|
|||
return Ok(None);
|
||||
}
|
||||
|
||||
fn perp_liq_bankruptcy(&self) -> anyhow::Result<Option<Signature>> {
|
||||
if self.health_cache.has_liquidatable_assets() {
|
||||
return Ok(None);
|
||||
}
|
||||
let mut perp_bankruptcies = self
|
||||
.liqee
|
||||
.active_perp_positions()
|
||||
.filter_map(|pp| {
|
||||
let quote = pp.quote_position_native();
|
||||
if quote >= 0 {
|
||||
return None;
|
||||
}
|
||||
Some((pp.market_index, quote))
|
||||
})
|
||||
.collect::<Vec<(PerpMarketIndex, I80F48)>>();
|
||||
perp_bankruptcies.sort_by(|a, b| a.1.cmp(&b.1));
|
||||
|
||||
if perp_bankruptcies.is_empty() {
|
||||
return Ok(None);
|
||||
}
|
||||
let (perp_market_index, _) = perp_bankruptcies.first().unwrap();
|
||||
|
||||
let sig = self.client.perp_liq_bankruptcy(
|
||||
(self.pubkey, &self.liqee),
|
||||
*perp_market_index,
|
||||
// Always use the max amount, since the health effect is always positive
|
||||
u64::MAX,
|
||||
)?;
|
||||
log::info!(
|
||||
"Liquidated bankruptcy for perp market on account {}, market index {}, maint_health was {}, tx sig {:?}",
|
||||
self.pubkey,
|
||||
perp_market_index,
|
||||
self.maint_health,
|
||||
sig
|
||||
);
|
||||
Ok(Some(sig))
|
||||
}
|
||||
|
||||
fn tokens(&self) -> anyhow::Result<Vec<(TokenIndex, I80F48, I80F48)>> {
|
||||
let mut tokens = self
|
||||
.liqee
|
||||
|
@ -354,7 +391,7 @@ impl<'a> LiquidateHelper<'a> {
|
|||
Ok(amount)
|
||||
}
|
||||
|
||||
fn liq_spot(&self) -> anyhow::Result<Option<Signature>> {
|
||||
fn token_liq(&self) -> anyhow::Result<Option<Signature>> {
|
||||
if !self.health_cache.has_borrows() || self.health_cache.can_call_spot_bankruptcy() {
|
||||
return Ok(None);
|
||||
}
|
||||
|
@ -417,7 +454,7 @@ impl<'a> LiquidateHelper<'a> {
|
|||
Ok(Some(sig))
|
||||
}
|
||||
|
||||
fn bankrupt_spot(&self) -> anyhow::Result<Option<Signature>> {
|
||||
fn token_liq_bankruptcy(&self) -> anyhow::Result<Option<Signature>> {
|
||||
if !self.health_cache.can_call_spot_bankruptcy() {
|
||||
return Ok(None);
|
||||
}
|
||||
|
@ -484,7 +521,7 @@ impl<'a> LiquidateHelper<'a> {
|
|||
return Ok(txsig);
|
||||
}
|
||||
|
||||
if let Some(txsig) = self.liq_perp_base_position()? {
|
||||
if let Some(txsig) = self.perp_liq_base_position()? {
|
||||
return Ok(txsig);
|
||||
}
|
||||
|
||||
|
@ -493,21 +530,26 @@ impl<'a> LiquidateHelper<'a> {
|
|||
// It's possible that some positive pnl can't be settled (if there's
|
||||
// no liquid counterparty) and that some negative pnl can't be settled
|
||||
// (if the liqee isn't liquid enough).
|
||||
if let Some(txsig) = self.settle_perp_pnl()? {
|
||||
if let Some(txsig) = self.perp_settle_pnl()? {
|
||||
return Ok(txsig);
|
||||
}
|
||||
|
||||
if let Some(txsig) = self.liq_spot()? {
|
||||
if let Some(txsig) = self.token_liq()? {
|
||||
return Ok(txsig);
|
||||
}
|
||||
|
||||
// TODO: socialize unsettleable negative pnl
|
||||
// if let Some(txsig) = self.bankrupt_perp()? {
|
||||
// return Ok(txsig);
|
||||
// }
|
||||
if let Some(txsig) = self.bankrupt_spot()? {
|
||||
// Socialize/insurance fund unsettleable negative pnl
|
||||
if let Some(txsig) = self.perp_liq_bankruptcy()? {
|
||||
return Ok(txsig);
|
||||
}
|
||||
|
||||
// Socialize/insurance fund unliquidatable borrows
|
||||
if let Some(txsig) = self.token_liq_bankruptcy()? {
|
||||
return Ok(txsig);
|
||||
}
|
||||
|
||||
// TODO: What about unliquidatable positive perp pnl?
|
||||
|
||||
anyhow::bail!(
|
||||
"Don't know what to do with liquidatable account {}, maint_health was {}",
|
||||
self.pubkey,
|
||||
|
|
|
@ -792,7 +792,7 @@ impl HealthCache {
|
|||
// can use perp_liq_base_position
|
||||
p.base != 0
|
||||
// can use perp_settle_pnl
|
||||
|| p.quote > ONE_NATIVE_USDC_IN_USD
|
||||
|| p.quote > ONE_NATIVE_USDC_IN_USD // TODO: we're not guaranteed to be able to settle positive perp pnl!
|
||||
// can use perp_liq_force_cancel_orders
|
||||
|| p.has_open_orders
|
||||
});
|
||||
|
|
Loading…
Reference in New Issue