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` - if rebalancing should happen (default true)
- `REBALANCE_SLIPPAGE_BPS` - slippage liquidator should tolerate when offloading tokens (default 100) - `REBALANCE_SLIPPAGE_BPS` - slippage liquidator should tolerate when offloading tokens (default 100)
- `PRIORITIZATION_MICRO_LAMPORTS` - how much priority fee to pay (default 0) - `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) - `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) - `PARALLEL_RPC_REQUESTS` - number of allowed parallel rpc calls (default 10)
- `TELEMETRY` - report the liquidator's existence and pubkey occasionally (default true) - `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; use crate::util;
#[derive(Clone)]
pub struct Config { pub struct Config {
pub min_health_ratio: f64, pub min_health_ratio: f64,
pub refresh_timeout: Duration, pub refresh_timeout: Duration,
pub mock_jupiter: bool, pub mock_jupiter: bool,
pub compute_limit_for_liq_ix: u32,
} }
pub async fn jupiter_market_can_buy( pub async fn jupiter_market_can_buy(
@ -88,6 +90,7 @@ struct LiquidateHelper<'a> {
liqor_min_health_ratio: I80F48, liqor_min_health_ratio: I80F48,
allowed_asset_tokens: HashSet<Pubkey>, allowed_asset_tokens: HashSet<Pubkey>,
allowed_liab_tokens: HashSet<Pubkey>, allowed_liab_tokens: HashSet<Pubkey>,
config: Config,
} }
impl<'a> LiquidateHelper<'a> { impl<'a> LiquidateHelper<'a> {
@ -160,6 +163,12 @@ impl<'a> LiquidateHelper<'a> {
Ok(Some(txsig)) 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>> { async fn perp_liq_base_or_positive_pnl(&self) -> anyhow::Result<Option<Signature>> {
let all_perp_base_positions: anyhow::Result< let all_perp_base_positions: anyhow::Result<
Vec<Option<(PerpMarketIndex, i64, I80F48, I80F48)>>, Vec<Option<(PerpMarketIndex, i64, I80F48, I80F48)>>,
@ -258,15 +267,21 @@ impl<'a> LiquidateHelper<'a> {
"computed transfer maximums" "computed transfer maximums"
); );
let txsig = self let liq_ix = self
.client .client
.perp_liq_base_or_positive_pnl( .perp_liq_base_or_positive_pnl_instruction(
(self.pubkey, &self.liqee), (self.pubkey, &self.liqee),
*perp_market_index, *perp_market_index,
side_signum * max_base_transfer_abs, side_signum * max_base_transfer_abs,
max_pnl_transfer, 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!( info!(
perp_market_index, perp_market_index,
%txsig, %txsig,
@ -297,15 +312,21 @@ impl<'a> LiquidateHelper<'a> {
} }
let (perp_market_index, _) = perp_negative_pnl.first().unwrap(); let (perp_market_index, _) = perp_negative_pnl.first().unwrap();
let txsig = self let liq_ix = self
.client .client
.perp_liq_negative_pnl_or_bankruptcy( .perp_liq_negative_pnl_or_bankruptcy_instruction(
(self.pubkey, &self.liqee), (self.pubkey, &self.liqee),
*perp_market_index, *perp_market_index,
// Always use the max amount, since the health effect is >= 0 // Always use the max amount, since the health effect is >= 0
u64::MAX, 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!( info!(
perp_market_index, perp_market_index,
%txsig, %txsig,
@ -413,15 +434,20 @@ impl<'a> LiquidateHelper<'a> {
// TODO: log liqor's assets in UI form // 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 // 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 .client
.token_liq_with_token( .token_liq_with_token_instruction(
(self.pubkey, &self.liqee), (self.pubkey, &self.liqee),
asset_token_index, asset_token_index,
liab_token_index, liab_token_index,
max_liab_transfer, max_liab_transfer,
) )
.await .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")?; .context("sending liq_token_with_token")?;
info!( info!(
asset_token_index, asset_token_index,
@ -467,15 +493,20 @@ impl<'a> LiquidateHelper<'a> {
.max_token_liab_transfer(liab_token_index, quote_token_index) .max_token_liab_transfer(liab_token_index, quote_token_index)
.await?; .await?;
let txsig = self let liq_ix = self
.client .client
.token_liq_bankruptcy( .token_liq_bankruptcy_instruction(
(self.pubkey, &self.liqee), (self.pubkey, &self.liqee),
liab_token_index, liab_token_index,
max_liab_transfer, max_liab_transfer,
) )
.await .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!( info!(
liab_token_index, liab_token_index,
%txsig, %txsig,
@ -619,6 +650,7 @@ pub async fn maybe_liquidate_account(
liqor_min_health_ratio, liqor_min_health_ratio,
allowed_asset_tokens: all_token_mints.clone(), allowed_asset_tokens: all_token_mints.clone(),
allowed_liab_tokens: all_token_mints, allowed_liab_tokens: all_token_mints,
config: config.clone(),
} }
.send_liq_tx() .send_liq_tx()
.await?; .await?;

View File

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

View File

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