liquidator: Allow overriding compute limit for potentially costly ix (#669)
This commit is contained in:
parent
e4a9a56f89
commit
a7705a2a1b
|
@ -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)
|
||||||
|
|
|
@ -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?;
|
||||||
|
|
|
@ -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),
|
||||||
};
|
};
|
||||||
|
|
|
@ -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(
|
||||||
|
|
Loading…
Reference in New Issue