From 0eed4c0caad15fe6a2bc8e989c7805b0647ca358 Mon Sep 17 00:00:00 2001 From: Michael Vines Date: Wed, 5 Jun 2024 23:17:19 -0700 Subject: [PATCH] --send-url now accepts multiple urls to improve odds of transaction inclusion. don't hate the player --- src/bin/sys-lend.rs | 17 +++++----- src/lib.rs | 72 ++++++++++++++++++++++++++++--------------- src/main.rs | 35 ++++++++++----------- src/stake_spreader.rs | 2 +- 4 files changed, 75 insertions(+), 51 deletions(-) diff --git a/src/bin/sys-lend.rs b/src/bin/sys-lend.rs index 1c30483..e78539a 100644 --- a/src/bin/sys-lend.rs +++ b/src/bin/sys-lend.rs @@ -26,7 +26,7 @@ use { }, std::collections::{BTreeMap, HashMap, HashSet}, sys::{ - app_version, metrics, + metrics, notifier::*, priority_fee::{apply_priority_fee, PriorityFee}, send_transaction_until_expired, @@ -35,7 +35,7 @@ use { kamino, marginfi_v2, solend::{self, math::TryMul}, }, - RpcClients, + *, }, }; @@ -197,13 +197,14 @@ async fn main() -> Result<(), Box> { .help("JSON RPC URL for the cluster"), ) .arg( - Arg::with_name("send_json_rpc_url") + Arg::with_name("send_json_rpc_urls") .long("send-url") .value_name("URL") .takes_value(true) - .validator(is_url_or_moniker) - .help("Optional addition JSON RPC URL for the cluster to be used only \ - for submitting transactions [default: same as --url]"), + .validator(is_comma_separated_url_or_moniker_list) + .help("Optional additional JSON RPC URLs, separated by commas, to \ + submit transactions with in addition to --url"), + ) .arg( Arg::with_name("priority_fee_exact") @@ -452,9 +453,9 @@ async fn main() -> Result<(), Box> { let rpc_clients = RpcClients::new( value_t_or_exit!(app_matches, "json_rpc_url", String), - value_t!(app_matches, "send_json_rpc_url", String).ok(), + value_t!(app_matches, "send_json_rpc_urls", String).ok(), ); - let rpc_client = &rpc_clients.default; + let rpc_client = rpc_clients.default(); let priority_fee = if let Ok(ui_priority_fee) = value_t!(app_matches, "priority_fee_exact", f64) { diff --git a/src/lib.rs b/src/lib.rs index d502828..a1451ad 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -34,29 +34,47 @@ pub fn app_version() -> String { }) } -pub struct RpcClients { - pub default: RpcClient, +pub fn is_comma_separated_url_or_moniker_list(string: T) -> Result<(), String> +where + T: AsRef + std::fmt::Display, +{ + for url_or_moniker in string.to_string().split(',') { + solana_clap_utils::input_validators::is_url_or_moniker(url_or_moniker)?; + } - // Optional `RpcClient` for use only for sending transactions. - // If `None` then the `default` client is used for sending transactions - pub send: Option, + Ok(()) +} + +pub struct RpcClients { + clients: Vec<(String, RpcClient)>, } impl RpcClients { - pub fn new(json_rpc_url: String, send_json_rpc_url: Option) -> Self { + pub fn new(json_rpc_url: String, send_json_rpc_urls: Option) -> Self { + let mut json_rpc_urls = vec![json_rpc_url]; + if let Some(send_json_rpc_urls) = send_json_rpc_urls { + for send_json_rpc_url in send_json_rpc_urls.split(',') { + json_rpc_urls.push(send_json_rpc_url.into()); + } + } + Self { - default: RpcClient::new_with_commitment( - normalize_to_url_if_moniker(json_rpc_url), - CommitmentConfig::confirmed(), - ), - send: send_json_rpc_url.map(|send_json_rpc_url| { - RpcClient::new_with_commitment( - normalize_to_url_if_moniker(send_json_rpc_url), - CommitmentConfig::confirmed(), - ) - }), + clients: json_rpc_urls + .into_iter() + .map(|json_rpc_url| { + let json_rpc_url = normalize_to_url_if_moniker(json_rpc_url); + ( + json_rpc_url.clone(), + RpcClient::new_with_commitment(json_rpc_url, CommitmentConfig::confirmed()), + ) + }) + .collect(), } } + + pub fn default(&self) -> &RpcClient { + &self.clients[0].1 + } } // Assumes `transaction` has already been signed and simulated... @@ -65,9 +83,6 @@ pub fn send_transaction_until_expired( transaction: &impl SerializableTransaction, last_valid_block_height: u64, ) -> bool { - let send_rpc_client = rpc_clients.send.as_ref().unwrap_or(&rpc_clients.default); - let rpc_client = &rpc_clients.default; - let mut last_send_attempt = None; let config = RpcSendTransactionConfig { @@ -82,16 +97,25 @@ pub fn send_transaction_until_expired( .as_secs() > 2 { - println!("Sending transaction {}", transaction.get_signature()); - if let Err(err) = send_rpc_client.send_transaction_with_config(transaction, config) { - println!("Unable to send transaction: {err:?}"); + for (json_rpc_url, rpc_client) in rpc_clients.clients.iter().rev() { + println!( + "Sending transaction {} [{json_rpc_url}]", + transaction.get_signature() + ); + + if let Err(err) = rpc_client.send_transaction_with_config(transaction, config) { + println!("Unable to send transaction: {err:?}"); + } } last_send_attempt = Some(Instant::now()); } sleep(Duration::from_millis(500)); - match rpc_client.get_signature_statuses(&[*transaction.get_signature()]) { + match rpc_clients + .default() + .get_signature_statuses(&[*transaction.get_signature()]) + { Ok(rpc_response::Response { context, value }) => { let confirmation_context_slot = context.slot; if let Some(ref transaction_status) = value[0] { @@ -103,7 +127,7 @@ pub fn send_transaction_until_expired( } } } else { - match rpc_client.get_epoch_info() { + match rpc_clients.default().get_epoch_info() { Ok(epoch_info) => { if epoch_info.block_height > last_valid_block_height && epoch_info.absolute_slot >= confirmation_context_slot diff --git a/src/main.rs b/src/main.rs index c5676f1..96998f8 100644 --- a/src/main.rs +++ b/src/main.rs @@ -44,7 +44,6 @@ use { time::Duration, }, sys::{ - app_version, exchange::{self, *}, metrics::{self, dp, MetricsConfig}, notifier::*, @@ -52,7 +51,7 @@ use { send_transaction_until_expired, token::*, //tulip, - RpcClients, + *, }, }; @@ -442,7 +441,7 @@ async fn process_exchange_deposit( lot_numbers: Option>, priority_fee: PriorityFee, ) -> Result<(), Box> { - let rpc_client = &rpc_clients.default; + let rpc_client = rpc_clients.default(); if let Some(if_exchange_balance_less_than) = if_exchange_balance_less_than { let exchange_balance = exchange_client @@ -1036,7 +1035,7 @@ async fn process_jup_swap( priority_fee: PriorityFee, notifier: &Notifier, ) -> Result<(), Box> { - let rpc_client = &rpc_clients.default; + let rpc_client = rpc_clients.default(); let from_account = db .get_account(address, from_token) @@ -2959,7 +2958,7 @@ async fn process_account_merge( priority_fee: PriorityFee, existing_signature: Option, ) -> Result<(), Box> { - let rpc_client = &rpc_clients.default; + let rpc_client = rpc_clients.default(); let token = MaybeToken::SOL(); // TODO: Support merging tokens one day if let Some(existing_signature) = existing_signature { @@ -3086,7 +3085,7 @@ async fn process_account_sweep( priority_fee: PriorityFee, existing_signature: Option, ) -> Result<(), Box> { - let rpc_client = &rpc_clients.default; + let rpc_client = rpc_clients.default(); let (recent_blockhash, last_valid_block_height) = rpc_client.get_latest_blockhash_with_commitment(rpc_client.commitment())?; @@ -3437,7 +3436,7 @@ async fn process_account_split( if_balance_exceeds: Option, priority_fee: PriorityFee, ) -> Result<(), Box> { - let rpc_client = &rpc_clients.default; + let rpc_client = rpc_clients.default(); // TODO: Support splitting two system accounts? Tokens? Otherwise at least error cleanly when it's attempted let token = MaybeToken::SOL(); // TODO: Support splitting tokens one day @@ -3574,7 +3573,7 @@ async fn process_account_redelegate( signers: &T, into_keypair: Option, ) -> Result<(), Box> { - let rpc_client = &rpc_clients.default; + let rpc_client = rpc_clients.default(); let (recent_blockhash, last_valid_block_height) = rpc_client.get_latest_blockhash_with_commitment(rpc_client.commitment())?; @@ -3680,7 +3679,7 @@ async fn process_account_sync( force_rescan_balances: bool, notifier: &Notifier, ) -> Result<(), Box> { - let rpc_client = &rpc_clients.default; + let rpc_client = rpc_clients.default(); process_account_sync_pending_transfers(db, rpc_client).await?; process_account_sync_sweep(db, rpc_clients, notifier).await?; @@ -3914,7 +3913,7 @@ async fn process_account_wrap( signers: T, priority_fee: PriorityFee, ) -> Result<(), Box> { - let rpc_client = &rpc_clients.default; + let rpc_client = rpc_clients.default(); let sol = MaybeToken::SOL(); let wsol = Token::wSOL; let wsol_address = wsol.ata(&address); @@ -4021,7 +4020,7 @@ async fn process_account_unwrap( signers: T, priority_fee: PriorityFee, ) -> Result<(), Box> { - let rpc_client = &rpc_clients.default; + let rpc_client = rpc_clients.default(); let sol = MaybeToken::SOL(); let wsol = Token::wSOL; @@ -4157,7 +4156,7 @@ async fn process_account_sync_sweep( rpc_clients: &RpcClients, _notifier: &Notifier, ) -> Result<(), Box> { - let rpc_client = &rpc_clients.default; + let rpc_client = rpc_clients.default(); let token = MaybeToken::SOL(); let transitory_sweep_stake_addresses = db.get_transitory_sweep_stake_addresses(); @@ -4383,13 +4382,13 @@ async fn main() -> Result<(), Box> { .help("JSON RPC URL for the cluster"), ) .arg( - Arg::with_name("send_json_rpc_url") + Arg::with_name("send_json_rpc_urls") .long("send-url") .value_name("URL") .takes_value(true) - .validator(is_url_or_moniker) - .help("Optional addition JSON RPC URL for the cluster to be used only \ - for submitting transactions [default: same as --url]"), + .validator(is_comma_separated_url_or_moniker_list) + .help("Optional additional JSON RPC URLs, separated by commas, to \ + submit transactions with in addition to --url"), ) .arg( Arg::with_name("verbose") @@ -5956,10 +5955,10 @@ async fn main() -> Result<(), Box> { let rpc_clients = RpcClients::new( value_t_or_exit!(app_matches, "json_rpc_url", String), - value_t!(app_matches, "send_json_rpc_url", String).ok(), + value_t!(app_matches, "send_json_rpc_urls", String).ok(), ); - let rpc_client = &rpc_clients.default; + let rpc_client = rpc_clients.default(); let mut wallet_manager = None; let notifier = Notifier::default(); diff --git a/src/stake_spreader.rs b/src/stake_spreader.rs index 667efdd..5c7ccf4 100644 --- a/src/stake_spreader.rs +++ b/src/stake_spreader.rs @@ -158,7 +158,7 @@ pub async fn run( signers: T, notifier: &Notifier, ) -> Result<(), Box> { - let rpc_client = &rpc_clients.default; + let rpc_client = rpc_clients.default(); let epoch_info = rpc_client.get_epoch_info()?; let current_epoch = epoch_info.epoch;