use std::str::FromStr;
use super::{helpers::fetch_table_accounts, instructions::get_liquidate_ix};
use anchor_client::{
solana_client::{nonblocking::rpc_client::RpcClient, rpc_config::RpcSimulateTransactionConfig},
solana_sdk::{
commitment_config::CommitmentConfig,
compute_budget::ComputeBudgetInstruction,
message::{v0::Message, VersionedMessage},
signer::Signer,
transaction::VersionedTransaction,
},
Cluster,
};
use anchor_lang::prelude::Pubkey;
use regex::Regex;
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct VaultLiquidation {
pub amt_out: u64,
pub amt_in: u64,
pub top_tick: i32,
}
pub async fn get_vault_liquidations(
vault_id: u16,
col_per_unit_debt: u128,
absorb: bool,
signer: Option<Pubkey>,
cluster: Cluster,
) -> anyhow::Result<Vec<VaultLiquidation>> {
let signer = signer.unwrap_or(Pubkey::from_str(
"HEyJLdMfZhhQ7FHCtjD5DWDFNFQhaeAVAsHeWqoY6dSD",
)?);
let liquidate = get_liquidate_ix(
vault_id,
(2_u128.pow(64) - 1) as u64,
col_per_unit_debt,
absorb,
Pubkey::default(),
signer,
cluster.clone(),
)
.await?;
let rpc =
RpcClient::new_with_commitment(cluster.url().to_string(), CommitmentConfig::confirmed());
let (recent_blockhash, address_lookup_table_accounts) = tokio::join!(
rpc.get_latest_blockhash(),
fetch_table_accounts(&rpc, liquidate.address_lookup_table_addresses)
);
let recent_blockhash = recent_blockhash?;
let compute_ix = ComputeBudgetInstruction::set_compute_unit_limit(1_000_000u32);
let mut ixs = vec![compute_ix];
ixs.extend(liquidate.ixs);
let message = Message::try_compile(
&signer,
&ixs,
&address_lookup_table_accounts,
recent_blockhash,
)?;
let transaction =
VersionedTransaction::try_new(VersionedMessage::V0(message), &[&Keypair::new(signer)])?;
let simulation = rpc
.simulate_transaction_with_config(
&transaction,
RpcSimulateTransactionConfig {
replace_recent_blockhash: true,
sig_verify: false,
..Default::default()
},
)
.await?;
Ok(parse_vault_liquidations(
simulation.value.logs.unwrap_or_default(),
))
}
#[derive(Debug, Clone)]
struct Keypair {
pubkey: Pubkey,
}
impl Keypair {
fn new(pubkey: Pubkey) -> Self {
Keypair { pubkey }
}
}
impl Signer for Keypair {
fn pubkey(&self) -> Pubkey {
self.pubkey
}
fn try_pubkey(&self) -> Result<Pubkey, anchor_client::solana_sdk::signer::SignerError> {
Ok(self.pubkey())
}
fn try_sign_message(
&self,
_message: &[u8],
) -> Result<
anchor_client::solana_sdk::signature::Signature,
anchor_client::solana_sdk::signer::SignerError,
> {
Ok(anchor_client::solana_sdk::signature::Signature::default())
}
fn is_interactive(&self) -> bool {
true
}
}
fn parse_vault_liquidations(logs: Vec<String>) -> Vec<VaultLiquidation> {
let mut results = Vec::new();
let re = Regex::new(r"VaultLiquidationResult: \[([^\]]+)\]").unwrap();
for log in logs {
if let Some(caps) = re.captures(&log) {
if let Some(m) = caps.get(1) {
let values: Vec<&str> = m.as_str().split(", ").map(|v| v.trim()).collect();
if values.len() == 3 {
let amt_out = values[0].parse::<u64>().unwrap_or(0);
let amt_in = values[1].parse::<u64>().unwrap_or(0);
let top_tick = values[2].parse::<i32>().unwrap_or(0);
if amt_in != 0 && amt_out != 0 {
results.push(VaultLiquidation {
amt_out,
amt_in,
top_tick,
});
}
}
}
}
}
results
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_parse_vault_liquidations() {
let logs = vec![
"Program ComputeBudget111111111111111111111111111111 invoke [1]".to_string(),
"Program ComputeBudget111111111111111111111111111111 success".to_string(),
"Program jupr81YtYssSyPt8jbnGuiWon5f6x9TcDEFxYe3Bdzi invoke [1]".to_string(),
"Program log: Instruction: Liquidate".to_string(),
"Program ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL invoke [2]".to_string(),
"Program log: Create".to_string(),
"Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA invoke [3]".to_string(),
"Program log: Instruction: GetAccountDataSize".to_string(),
"VaultLiquidationResult: [100, 200, 300]".to_string(),
"VaultLiquidationResult: [300, 400, 500]".to_string(),
];
let expected = vec![
VaultLiquidation {
amt_out: 100,
amt_in: 200,
top_tick: 300,
},
VaultLiquidation {
amt_out: 300,
amt_in: 400,
top_tick: 500,
},
];
assert_eq!(parse_vault_liquidations(logs), expected);
}
}