Skip to content

Commit d812395

Browse files
authored
Merge pull request #103 from 0dotxyz/stage
1.10 + Improvements
2 parents 7347bc1 + a7c640c commit d812395

18 files changed

Lines changed: 394 additions & 181 deletions

Cargo.lock

Lines changed: 9 additions & 9 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ serde = { version = "1.0.228", features = ["derive"] }
5454
serde_json = "1.0.149"
5555
signal-hook = "0.4.3"
5656
tiny_http = "0.12.0"
57-
tokio = { version = "1.50.0", features = ["rt", "macros"] }
57+
tokio = { version = "1.50.0", features = ["rt", "macros", "sync"] }
5858
warp = "0.4.2"
5959

6060
yellowstone-grpc-client = "10.2.0"
@@ -65,16 +65,16 @@ switchboard-on-demand-client = { git = "https://github.com/switchboard-xyz/switc
6565

6666
[dependencies.marginfi_type_crate]
6767
git = "https://github.com/mrgnlabs/marginfi-v2"
68-
tag = "mrgn-0.1.9-rc3"
69-
#rev = "4abf7d0a45e1f719a630487c2d09d3d1e9d36c85"
68+
#tag = "mrgn-0.1.10-rc1"
69+
rev = "e3c9b4e104d22c54aca180c31b90ff44c337f42d"
7070
package = "marginfi-type-crate"
7171
default-features = false
7272
features = ["client"]
7373

7474
[dependencies.marginfi]
7575
git = "https://github.com/mrgnlabs/marginfi-v2"
76-
tag = "mrgn-0.1.9-rc3"
77-
#rev = "4abf7d0a45e1f719a630487c2d09d3d1e9d36c85"
76+
#tag = "mrgn-0.1.10-rc1"
77+
rev = "e3c9b4e104d22c54aca180c31b90ff44c337f42d"
7878
default-features = false
7979
features = ["client", "no-entrypoint", "custom-heap"]
8080

bin/env.template

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ YELLOWSTONE_X_TOKEN=<TOKEN>
44
WALLET_KEYPAIR=<WALLET_KEYPAIR>
55
COMPUTE_UNIT_PRICE_MICRO_LAMPORTS=1000
66
MARGINFI_GROUP_KEY=4qp6Fx6tnZkY5Wropq9wUYgtFxXKwE6viZxFHg3rdAG8
7-
ADDRESS_LOOKUP_TABLES=HGmknUTUmeovMc9ryERNWG6UFZDFDVr9xrum3ZhyL4fC,5FuKF7C1tJji2mXZuJ14U9oDb37is5mmvYLf4KwojoF1,FEFhAFKz48P3w82Ds5VhvyEDwhRqu2FejmnuxEPZ8wNR
87

98
# Minimum liquidation profit in USD
109
MIN_PROFIT=1
@@ -19,14 +18,8 @@ SLIPPAGE_BPS=100
1918
# `${YELLOWSTONE_ENDPOINT}/${YELLOWSTONE_X_TOKEN}` (or just endpoint if token is empty).
2019
# RPC_URL=<RPC_URL>
2120

22-
# Switchboard feeds that should not be cranked (cause they fail too often)
23-
UNSTABLE_SWB_FEEDS=<4YMdFbV8FaUv4FpE2gbZBReSFVzFKNPJZHirHwc2Psza,...>
2421
CROSSBAR_API_URL=<SWITCHBOARD_CROSSBAR_API_URL>
2522

2623
# Port to check liquidator health state via HTTP. Example: http://localhost:3000/healthz
2724
# Defaults to 3000 if not set
2825
PORT=3000
29-
30-
# Liquidation is skipped if there is less than this amount of tokens-to-be-used-for-repayment in the wallet. In USD.
31-
# Defaults to 0.001 if not set
32-
TOKEN_DUST_THRESHOLD=0.001

rust-toolchain.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
[toolchain]
22
channel = "1.90.0"
3-
components = ["rustfmt", "clippy"]
3+
components = ["rustfmt", "clippy", "rust-analyzer"]

src/cache.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ mod tokens;
77
pub use banks::is_switchboard_pull_setup;
88

99
use std::{
10-
collections::HashMap,
10+
collections::{HashMap, HashSet},
1111
sync::{Arc, Mutex},
1212
};
1313

@@ -18,6 +18,7 @@ use banks::BanksCache;
1818
use marginfi_type_crate::{
1919
constants::{ASSET_TAG_DEFAULT, ASSET_TAG_SOL, ASSET_TAG_STAKED, FEE_STATE_SEED},
2020
pdas::derive_kamino_lending_market_authority,
21+
types::MarginfiGroup,
2122
};
2223
use mints::MintsCache;
2324
use oracles::OraclesCache;
@@ -82,6 +83,7 @@ impl GroupedLuts {
8283
pub struct Cache {
8384
pub signer_pk: Pubkey,
8485
pub marginfi_group_address: Pubkey,
86+
pub marginfi_group: MarginfiGroup,
8587
pub marginfi_accounts: MarginfiAccountsCache,
8688
pub banks: BanksCache,
8789
pub mints: MintsCache,
@@ -112,15 +114,17 @@ impl Cache {
112114
signer_pk: Pubkey,
113115
marginfi_group_address: Pubkey,
114116
clock: Arc<Mutex<Clock>>,
117+
excluded_mints: HashSet<Pubkey>,
115118
) -> Self {
116119
let (global_fee_state_key, _) =
117120
Pubkey::find_program_address(&[FEE_STATE_SEED.as_bytes()], &marginfi_type_crate::ID);
118121
Self {
119122
signer_pk,
120123
marginfi_group_address,
124+
marginfi_group: MarginfiGroup::default(),
121125
marginfi_accounts: MarginfiAccountsCache::default(),
122126
banks: BanksCache::default(),
123-
mints: MintsCache::default(),
127+
mints: MintsCache::new(excluded_mints),
124128
oracles: OraclesCache::default(),
125129
tokens: TokensCache::default(),
126130
clock,

src/cache/accounts.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,22 @@ impl MarginfiAccountsCache {
5858
.cloned()
5959
}
6060

61+
pub fn try_get_accounts_with_liabilities(&self) -> Result<Vec<Pubkey>> {
62+
let inner = self.inner.read().map_err(|e| {
63+
anyhow!(
64+
"Failed to lock the marginfi accounts cache for liability lookup: {}",
65+
e
66+
)
67+
})?;
68+
69+
Ok(inner
70+
.accounts
71+
.keys()
72+
.filter(|account| !inner.accounts_without_liabilities.contains(account))
73+
.copied()
74+
.collect())
75+
}
76+
6177
pub fn try_get_accounts_for_bank_with_liabilities(
6278
&self,
6379
bank_address: &Pubkey,

src/cache/mints.rs

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use std::collections::HashMap;
1+
use std::collections::{HashMap, HashSet};
22

33
use anyhow::{anyhow, Result};
44
use solana_sdk::{account::Account, pubkey::Pubkey};
@@ -9,9 +9,17 @@ use crate::wrappers::mint::MintWrapper;
99
pub struct MintsCache {
1010
mints: HashMap<Pubkey, MintWrapper>,
1111
token_to_mint: HashMap<Pubkey, Pubkey>,
12+
excluded_mints: HashSet<Pubkey>,
1213
}
1314

1415
impl MintsCache {
16+
pub fn new(excluded_mints: HashSet<Pubkey>) -> Self {
17+
Self {
18+
excluded_mints,
19+
..Default::default()
20+
}
21+
}
22+
1523
pub fn insert(&mut self, mint_address: Pubkey, mint: Account, token_address: Pubkey) {
1624
self.mints
1725
.insert(mint_address, MintWrapper::new(token_address, mint));
@@ -26,14 +34,10 @@ impl MintsCache {
2634
}
2735

2836
pub fn get_mints(&self) -> Vec<Pubkey> {
29-
// TODO: remove once UXD & sUSD are sunset
3037
self.mints
3138
.keys()
39+
.filter(|mint| !self.excluded_mints.contains(mint))
3240
.cloned()
33-
.filter(|&mint| {
34-
mint != Pubkey::from_str_const("7kbnvuGBxxj8AG9qp8Scn56muWGaRaFqxg1FsRp3PaFT")
35-
&& mint != Pubkey::from_str_const("susdabGDNbhrnCa6ncrYo81u4s9GM8ecK2UwMyZiq4X")
36-
})
3741
.collect()
3842
}
3943

src/cache_loader.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ impl CacheLoader {
6767
let marginfi_group =
6868
bytemuck::from_bytes::<MarginfiGroup>(&marginfi_group_account.data[8..]);
6969
cache.global_fee_wallet = marginfi_group.fee_state_cache.global_fee_wallet;
70+
cache.marginfi_group = *marginfi_group;
7071

7172
self.load_luts(cache)?;
7273
self.load_marginfi_accounts(cache)?;

src/cli/entrypoints.rs

Lines changed: 37 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ use crate::{
1010
},
1111
wrappers::liquidator_account::LiquidatorAccount,
1212
};
13-
use log::{error, info};
13+
use log::{error, info, warn};
1414
use solana_client::rpc_client::RpcClient;
1515
use solana_sdk::{signature::Keypair, signer::Signer};
1616
use std::{
@@ -34,7 +34,12 @@ pub fn run_liquidator(config: Eva01Config, stop_liquidator: Arc<AtomicBool>) ->
3434
let mut clock_manager = ClockManager::new(clock.clone(), config.rpc_url.clone())?;
3535

3636
info!("Loading Cache...");
37-
let mut cache = Cache::new(wallet_pubkey, config.marginfi_group_key, clock.clone());
37+
let mut cache = Cache::new(
38+
wallet_pubkey,
39+
config.marginfi_group_key,
40+
clock.clone(),
41+
config.excluded_liquidation_mints.iter().copied().collect(),
42+
);
3843

3944
let cache_loader = CacheLoader::new(
4045
&config.wallet_keypair,
@@ -69,38 +74,46 @@ pub fn run_liquidator(config: Eva01Config, stop_liquidator: Arc<AtomicBool>) ->
6974
cache.clone(),
7075
)?;
7176

77+
let swb_fetcher_tx = geyser_tx.clone();
78+
let integration_fetcher_tx = geyser_tx.clone();
79+
7280
let geyser_service = GeyserService::new(
7381
config,
7482
accounts_to_track,
7583
geyser_tx,
7684
stop_liquidator.clone(),
7785
)?;
7886

79-
let swb_fetcher_cache = cache.clone();
80-
let swb_fetcher_stop = stop_liquidator.clone();
81-
let integration_fetcher_cache = cache.clone();
82-
let integration_fetcher_stop = stop_liquidator.clone();
87+
let swb_fetcher = SwbPriceFetcher::new(
88+
swb_fetcher_api_url,
89+
swb_fetcher_crossbar_url,
90+
cache.clone(),
91+
swb_fetcher_tx,
92+
stop_liquidator.clone(),
93+
);
94+
let integration_fetcher = IntegrationAccountFetcher::new(
95+
integration_fetcher_rpc_url,
96+
cache.clone(),
97+
integration_fetcher_tx,
98+
stop_liquidator.clone(),
99+
);
83100

84-
info!("Starting services...");
101+
// Prime both fetchers before any service runs: the accounts they own are loaded from chain in
102+
// a state marginfi rejects as stale (Switchboard feeds, Kamino reserves, JupLend lending
103+
// states), so anything pricing a bank before their first cycle — the Liquidator's initial scan
104+
// above all — fails on staleness instead of evaluating the account.
105+
info!("Priming the fetcher-owned accounts...");
106+
if let Err(e) = integration_fetcher.fetch_and_update() {
107+
warn!("Failed to prime the integration accounts: {:?}", e);
108+
}
109+
if let Err(e) = swb_fetcher.fetch_and_update() {
110+
warn!("Failed to prime the Switchboard prices: {:?}", e);
111+
}
85112

86-
thread::spawn(move || {
87-
SwbPriceFetcher::new(
88-
swb_fetcher_api_url,
89-
swb_fetcher_crossbar_url,
90-
swb_fetcher_cache,
91-
swb_fetcher_stop,
92-
)
93-
.start();
94-
});
113+
info!("Starting services...");
95114

96-
thread::spawn(move || {
97-
IntegrationAccountFetcher::new(
98-
integration_fetcher_rpc_url,
99-
integration_fetcher_cache,
100-
integration_fetcher_stop,
101-
)
102-
.start();
103-
});
115+
thread::spawn(move || swb_fetcher.start());
116+
thread::spawn(move || integration_fetcher.start());
104117

105118
let cloned_stop = stop_liquidator.clone();
106119
thread::spawn(move || clock_manager.start(cloned_stop));

src/config.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ pub struct Eva01Config {
1212
pub luts_group1: Vec<Pubkey>,
1313
pub luts_group2: Vec<Pubkey>,
1414
pub luts_group3: Vec<Pubkey>,
15+
pub excluded_liquidation_mints: Vec<Pubkey>,
1516
pub min_profit: f64,
1617
pub healthcheck_port: u16,
1718
pub swb_program_id: Pubkey,
@@ -23,7 +24,6 @@ pub struct Eva01Config {
2324
pub titan_ws_endpoint: String,
2425
pub titan_api_key: String,
2526
pub jupiter_api_key: String,
26-
pub use_fumarole: bool,
2727
/// Jito block-engine `sendBundle` endpoint; `None` uses the executor's built-in default.
2828
pub jito_block_engine_url: Option<String>,
2929
/// Hard cap on the Jito tip per bundle (lamports).
@@ -62,6 +62,8 @@ impl Eva01Config {
6262
let luts_group2 = parse_pubkey_list("ADDRESS_LOOKUP_TABLES_GROUP2").unwrap_or_default();
6363
let luts_group3 = parse_pubkey_list("ADDRESS_LOOKUP_TABLES_GROUP3").unwrap_or_default();
6464

65+
let excluded_liquidation_mints = parse_pubkey_list("EXCLUDED_LIQUIDATION_MINTS")?;
66+
6567
let min_profit: f64 = std::env::var("MIN_PROFIT")
6668
.expect("MIN_PROFIT environment variable is not set")
6769
.parse()
@@ -102,8 +104,6 @@ impl Eva01Config {
102104
let jupiter_api_key = std::env::var("JUP_SWAP_API_KEY")
103105
.expect("JUP_SWAP_API_KEY environment variable is not set");
104106

105-
let use_fumarole = std::env::var("USE_FUMAROLE").is_ok_and(|x| x == "true");
106-
107107
let jito_block_engine_url = std::env::var("JITO_BLOCK_ENGINE_URL").ok();
108108
// Default cap 0.001 SOL (matches Jito's typical max tip floor).
109109
let jito_tip_max_lamports: u64 = std::env::var("JITO_TIP_MAX_LAMPORTS")
@@ -122,6 +122,7 @@ impl Eva01Config {
122122
luts_group1,
123123
luts_group2,
124124
luts_group3,
125+
excluded_liquidation_mints,
125126
min_profit,
126127
healthcheck_port,
127128
swb_program_id,
@@ -133,7 +134,6 @@ impl Eva01Config {
133134
titan_ws_endpoint,
134135
titan_api_key,
135136
jupiter_api_key,
136-
use_fumarole,
137137
jito_block_engine_url,
138138
jito_tip_max_lamports,
139139
bundle_api_key,

0 commit comments

Comments
 (0)