liquidator: Allow overriding compute limit for potentially costly ix (#669)

This commit is contained in:
Christian Kamm 2023-08-09 15:26:13 +02:00 committed by GitHub
parent e4a9a56f89
commit a7705a2a1b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 61 additions and 23 deletions

View File

@ -47,6 +47,7 @@ more advanced parameters
- `REBALANCE` - if rebalancing should happen (default true)
- `REBALANCE_SLIPPAGE_BPS` - slippage liquidator should tolerate when offloading tokens (default 100)
- `PRIORITIZATION_MICRO_LAMPORTS` - how much priority fee to pay (default 0)
- `COMPUTE_LIMIT_FOR_LIQUIDATION` - compute to request for liq instructions (default 250k)
- `SNAPSHOT_INTERVAL_SECS` - how frequently to request a full on-chain snapshot (default 5min)
- `PARALLEL_RPC_REQUESTS` - number of allowed parallel rpc calls (default 10)
- `TELEMETRY` - report the liquidator's existence and pubkey occasionally (default true)

View File

@ -14,10 +14,12 @@ use {anyhow::Context, fixed::types::I80F48, solana_sdk::pubkey::Pubkey};
use crate::util;
#[derive(Clone)]
pub struct Config {
pub min_health_ratio: f64,
pub refresh_timeout: Duration,
pub mock_jupiter: bool,
pub compute_limit_for_liq_ix: u32,
}
pub async fn jupiter_market_can_buy(
@ -88,6 +90,7 @@ struct LiquidateHelper<'a> {
liqor_min_health_ratio: I80F48,
allowed_asset_tokens: HashSet<Pubkey>,
allowed_liab_tokens: HashSet<Pubkey>,
config: Config,
}
impl<'a> LiquidateHelper<'a> {
@ -160,6 +163,12 @@ impl<'a> LiquidateHelper<'a> {
Ok(Some(txsig))
}
fn liq_compute_limit_instruction(&self) -> solana_sdk::instruction::Instruction {
solana_sdk::compute_budget::ComputeBudgetInstruction::set_compute_unit_limit(
self.config.compute_limit_for_liq_ix,
)
}
async fn perp_liq_base_or_positive_pnl(&self) -> anyhow::Result<Option<Signature>> {
let all_perp_base_positions: anyhow::Result<
Vec<Option<(PerpMarketIndex, i64, I80F48, I80F48)>>,
@ -258,15 +267,21 @@ impl<'a> LiquidateHelper<'a> {
"computed transfer maximums"
);
let txsig = self
let liq_ix = self
.client
.perp_liq_base_or_positive_pnl(
.perp_liq_base_or_positive_pnl_instruction(
(self.pubkey, &self.liqee),
*perp_market_index,
side_signum * max_base_transfer_abs,
max_pnl_transfer,
)
.await?;
.await
.context("creating perp_liq_base_or_positive_pnl_instruction")?;
let txsig = self
.client
.send_and_confirm_owner_tx(vec![self.liq_compute_limit_instruction(), liq_ix])
.await
.context("sending perp_liq_base_or_positive_pnl_instruction")?;
info!(
perp_market_index,
%txsig,
@ -297,15 +312,21 @@ impl<'a> LiquidateHelper<'a> {
}
let (perp_market_index, _) = perp_negative_pnl.first().unwrap();
let txsig = self
let liq_ix = self
.client
.perp_liq_negative_pnl_or_bankruptcy(
.perp_liq_negative_pnl_or_bankruptcy_instruction(
(self.pubkey, &self.liqee),
*perp_market_index,
// Always use the max amount, since the health effect is >= 0
u64::MAX,
)
.await?;
.await
.context("creating perp_liq_negative_pnl_or_bankruptcy_instruction")?;
let txsig = self
.client
.send_and_confirm_owner_tx(vec![self.liq_compute_limit_instruction(), liq_ix])
.await
.context("sending perp_liq_negative_pnl_or_bankruptcy_instruction")?;
info!(
perp_market_index,
%txsig,
@ -413,15 +434,20 @@ impl<'a> LiquidateHelper<'a> {
// TODO: log liqor's assets in UI form
// TODO: log liquee's liab_needed, need to refactor program code to be able to be accessed from client side
//
let txsig = self
let liq_ix = self
.client
.token_liq_with_token(
.token_liq_with_token_instruction(
(self.pubkey, &self.liqee),
asset_token_index,
liab_token_index,
max_liab_transfer,
)
.await
.context("creating liq_token_with_token ix")?;
let txsig = self
.client
.send_and_confirm_owner_tx(vec![self.liq_compute_limit_instruction(), liq_ix])
.await
.context("sending liq_token_with_token")?;
info!(
asset_token_index,
@ -467,15 +493,20 @@ impl<'a> LiquidateHelper<'a> {
.max_token_liab_transfer(liab_token_index, quote_token_index)
.await?;
let txsig = self
let liq_ix = self
.client
.token_liq_bankruptcy(
.token_liq_bankruptcy_instruction(
(self.pubkey, &self.liqee),
liab_token_index,
max_liab_transfer,
)
.await
.context("sending liq_token_bankruptcy")?;
.context("creating liq_token_bankruptcy")?;
let txsig = self
.client
.send_and_confirm_owner_tx(vec![self.liq_compute_limit_instruction(), liq_ix])
.await
.context("sending liq_token_with_token")?;
info!(
liab_token_index,
%txsig,
@ -619,6 +650,7 @@ pub async fn maybe_liquidate_account(
liqor_min_health_ratio,
allowed_asset_tokens: all_token_mints.clone(),
allowed_liab_tokens: all_token_mints,
config: config.clone(),
}
.send_liq_tx()
.await?;

View File

@ -91,6 +91,10 @@ struct Cli {
#[clap(long, env, default_value = "0")]
prioritization_micro_lamports: u64,
/// compute limit requested for liquidation instructions
#[clap(long, env, default_value = "250000")]
compute_limit_for_liquidation: u32,
/// use a jupiter mock instead of actual queries
///
/// This is required for devnet testing.
@ -247,6 +251,7 @@ async fn main() -> anyhow::Result<()> {
let liq_config = liquidate::Config {
min_health_ratio: cli.min_health_ratio,
mock_jupiter: cli.mock_jupiter == BoolArg::True,
compute_limit_for_liq_ix: cli.compute_limit_for_liquidation,
// TODO: config
refresh_timeout: Duration::from_secs(30),
};

View File

@ -1052,13 +1052,13 @@ impl MangoClient {
self.send_and_confirm_permissionless_tx(vec![ix]).await
}
pub async fn perp_liq_base_or_positive_pnl(
pub async fn perp_liq_base_or_positive_pnl_instruction(
&self,
liqee: (&Pubkey, &MangoAccountValue),
market_index: PerpMarketIndex,
max_base_transfer: i64,
max_pnl_transfer: u64,
) -> anyhow::Result<Signature> {
) -> anyhow::Result<Instruction> {
let perp = self.context.perp(market_index);
let settle_token_info = self.context.token(perp.market.settle_token_index);
@ -1094,15 +1094,15 @@ impl MangoClient {
},
),
};
self.send_and_confirm_owner_tx(vec![ix]).await
Ok(ix)
}
pub async fn perp_liq_negative_pnl_or_bankruptcy(
pub async fn perp_liq_negative_pnl_or_bankruptcy_instruction(
&self,
liqee: (&Pubkey, &MangoAccountValue),
market_index: PerpMarketIndex,
max_liab_transfer: u64,
) -> anyhow::Result<Signature> {
) -> anyhow::Result<Instruction> {
let group = account_fetcher_fetch_anchor_account::<Group>(
&*self.account_fetcher,
&self.context.group,
@ -1151,20 +1151,20 @@ impl MangoClient {
&mango_v4::instruction::PerpLiqNegativePnlOrBankruptcyV2 { max_liab_transfer },
),
};
self.send_and_confirm_owner_tx(vec![ix]).await
Ok(ix)
}
//
// Liquidation
//
pub async fn token_liq_with_token(
pub async fn token_liq_with_token_instruction(
&self,
liqee: (&Pubkey, &MangoAccountValue),
asset_token_index: TokenIndex,
liab_token_index: TokenIndex,
max_liab_transfer: I80F48,
) -> anyhow::Result<Signature> {
) -> anyhow::Result<Instruction> {
let health_remaining_ams = self
.derive_liquidation_health_check_remaining_account_metas(
liqee.1,
@ -1195,15 +1195,15 @@ impl MangoClient {
max_liab_transfer,
}),
};
self.send_and_confirm_owner_tx(vec![ix]).await
Ok(ix)
}
pub async fn token_liq_bankruptcy(
pub async fn token_liq_bankruptcy_instruction(
&self,
liqee: (&Pubkey, &MangoAccountValue),
liab_token_index: TokenIndex,
max_liab_transfer: I80F48,
) -> anyhow::Result<Signature> {
) -> anyhow::Result<Instruction> {
let quote_token_index = 0;
let quote_info = self.context.token(quote_token_index);
@ -1255,7 +1255,7 @@ impl MangoClient {
max_liab_transfer,
}),
};
self.send_and_confirm_owner_tx(vec![ix]).await
Ok(ix)
}
pub async fn token_conditional_swap_trigger(