From 9a1865d0b54be8e2ea670daed1a9bd74d7333a5f Mon Sep 17 00:00:00 2001 From: Hardhat Chad Date: Thu, 8 Aug 2024 17:17:52 +0000 Subject: [PATCH] simplify dynamic fee ux --- Cargo.lock | 1 + Cargo.toml | 1 + src/dynamic_fee.rs | 161 +++++++++++++++++++++------------------- src/main.rs | 23 +++--- src/send_and_confirm.rs | 12 ++- 5 files changed, 106 insertions(+), 92 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f8561843..14fb060b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2799,6 +2799,7 @@ dependencies = [ "spl-associated-token-account", "spl-token", "tokio", + "url", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 9cca6a6f..621f3cc4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -46,6 +46,7 @@ spl-associated-token-account = { version = "^2.3", features = [ "no-entrypoint", ] } tokio = "1.35.1" +url = "2.5" # [patch.crates-io] # drillx = { path = "../drillx/drillx" } diff --git a/src/dynamic_fee.rs b/src/dynamic_fee.rs index b7001054..b178b0b7 100644 --- a/src/dynamic_fee.rs +++ b/src/dynamic_fee.rs @@ -3,89 +3,100 @@ use crate::Miner; use ore_api::consts::BUS_ADDRESSES; use reqwest::Client; use serde_json::{json, Value}; +use url::Url; + +enum FeeStrategy { + Helius, + Triton, +} impl Miner { - pub async fn dynamic_fee(&self) -> u64 { + pub async fn dynamic_fee(&self) -> Option { + // Get url + let rpc_url = self + .dynamic_fee_url + .clone() + .unwrap_or(self.rpc_client.url()); + + // Select fee estiamte strategy + let host = Url::parse(&rpc_url) + .unwrap() + .host_str() + .unwrap() + .to_string(); + let strategy = if host.contains("helius-rpc.com") { + FeeStrategy::Helius + } else if host.contains("rpcpool.com") { + FeeStrategy::Triton + } else { + return None; + }; + + // Build fee estimate request + let client = Client::new(); let ore_addresses: Vec = std::iter::once(ore_api::ID.to_string()) .chain(BUS_ADDRESSES.iter().map(|pubkey| pubkey.to_string())) .collect(); + let body = match strategy { + FeeStrategy::Helius => { + json!({ + "jsonrpc": "2.0", + "id": "priority-fee-estimate", + "method": "getPriorityFeeEstimate", + "params": [{ + "accountKeys": ore_addresses, + "options": { + "recommended": true + } + }] + }) + } + FeeStrategy::Triton => { + json!({ + "jsonrpc": "2.0", + "id": "priority-fee-estimate", + "method": "getRecentPrioritizationFees", + "params": [ + ore_addresses, + { + "percentile": 5000, + } + ] + }) + } + }; - match &self.dynamic_fee_strategy { - None => self.priority_fee.unwrap_or(0), - Some(strategy) => { - let client = Client::new(); - let body = match strategy.as_str() { - "helius" => { - json!({ - "jsonrpc": "2.0", - "id": "priority-fee-estimate", - "method": "getPriorityFeeEstimate", - "params": [{ - "accountKeys": ore_addresses, - "options": { - "recommended": true - } - }] - }) - } - "triton" => { - json!({ - "jsonrpc": "2.0", - "id": "priority-fee-estimate", - "method": "getRecentPrioritizationFees", - "params": [ - ore_addresses, - { - "percentile": 5000, - } - ] - }) - } - _ => return self.priority_fee.unwrap_or(0), - }; - - // Send request - let url = self - .dynamic_fee_url - .clone() - .unwrap_or(self.rpc_client.url()); - let response: Value = client - .post(url) - .json(&body) - .send() - .await - .unwrap() - .json() - .await - .unwrap(); + // Send request + let response: Value = client + .post(rpc_url) + .json(&body) + .send() + .await + .unwrap() + .json() + .await + .unwrap(); - // Parse fee - let calculated_fee = match strategy.as_str() { - "helius" => response["result"]["priorityFeeEstimate"] - .as_f64() - .map(|fee| fee as u64) - .ok_or_else(|| { - format!("Failed to parse priority fee. Response: {:?}", response) - }) - .unwrap(), - "triton" => response["result"] - .as_array() - .and_then(|arr| arr.last()) - .and_then(|last| last["prioritizationFee"].as_u64()) - .ok_or_else(|| { - format!("Failed to parse priority fee. Response: {:?}", response) - }) - .unwrap(), - _ => return self.priority_fee.unwrap_or(0), - }; + // Parse response + let calculated_fee = match strategy { + FeeStrategy::Helius => response["result"]["priorityFeeEstimate"] + .as_f64() + .map(|fee| fee as u64) + .ok_or_else(|| format!("Failed to parse priority fee. Response: {:?}", response)) + .unwrap(), + FeeStrategy::Triton => response["result"] + .as_array() + .and_then(|arr| arr.last()) + .and_then(|last| last["prioritizationFee"].as_u64()) + .ok_or_else(|| format!("Failed to parse priority fee. Response: {:?}", response)) + .unwrap(), + }; - // Check if the calculated fee is higher than max - if let Some(max_fee) = self.priority_fee { - calculated_fee.min(max_fee) - } else { - calculated_fee - } - } + // Check if the calculated fee is higher than max + if let Some(max_fee) = self.priority_fee { + Some(calculated_fee.min(max_fee)) + } else { + Some(calculated_fee) } } } diff --git a/src/main.rs b/src/main.rs index efbaa752..2dfa3ecd 100644 --- a/src/main.rs +++ b/src/main.rs @@ -32,7 +32,7 @@ struct Miner { pub keypair_filepath: Option, pub priority_fee: Option, pub dynamic_fee_url: Option, - pub dynamic_fee_strategy: Option, + pub dynamic_fee: bool, pub rpc_client: Arc, pub fee_payer_filepath: Option, } @@ -100,7 +100,7 @@ struct Args { #[arg( long, value_name = "KEYPAIR_FILEPATH", - help = "Filepath to keypair to use", + help = "Filepath to keypair to use.", global = true )] keypair: Option, @@ -108,7 +108,7 @@ struct Args { #[arg( long, value_name = "FEE_PAYER_FILEPATH", - help = "Filepath to keypair to use as transaction fee payer", + help = "Filepath to keypair to use as transaction fee payer.", global = true )] fee_payer: Option, @@ -116,7 +116,7 @@ struct Args { #[arg( long, value_name = "MICROLAMPORTS", - help = "Price to pay for compute unit. If dynamic fee url is also set, this value will be the max.", + help = "Price to pay for compute units. If dynamic fees are being used, this value will be the max.", default_value = "500000", global = true )] @@ -130,13 +130,8 @@ struct Args { )] dynamic_fee_url: Option, - #[arg( - long, - value_name = "DYNAMIC_FEE_STRATEGY", - help = "Strategy to use for dynamic fee estimation. Must be one of 'helius', or 'triton'.", - global = true - )] - dynamic_fee_strategy: Option, + #[arg(long, help = "Use dynamic priority fees", global = true)] + dynamic_fee: bool, #[command(subcommand)] command: Commands, @@ -169,7 +164,7 @@ async fn main() { args.priority_fee, Some(default_keypair), args.dynamic_fee_url, - args.dynamic_fee_strategy, + args.dynamic_fee, Some(fee_payer_filepath), )); @@ -221,7 +216,7 @@ impl Miner { priority_fee: Option, keypair_filepath: Option, dynamic_fee_url: Option, - dynamic_fee_strategy: Option, + dynamic_fee: bool, fee_payer_filepath: Option, ) -> Self { Self { @@ -229,7 +224,7 @@ impl Miner { keypair_filepath, priority_fee, dynamic_fee_url, - dynamic_fee_strategy, + dynamic_fee, fee_payer_filepath, } } diff --git a/src/send_and_confirm.rs b/src/send_and_confirm.rs index 41132793..503eccd9 100644 --- a/src/send_and_confirm.rs +++ b/src/send_and_confirm.rs @@ -88,11 +88,17 @@ impl Miner { // Sign tx with a new blockhash if attempts % 5 == 0 { // Reset the compute unit price - if self.dynamic_fee_strategy.is_some() { - let fee = self.dynamic_fee().await; + if self.dynamic_fee { + let fee = if let Some(fee) = self.dynamic_fee().await { + progress_bar.println(format!(" Priority fee: {} microlamports", fee)); + fee + } else { + let fee = self.priority_fee.unwrap_or(0); + progress_bar.println(format!(" {} Dynamic fees not supported by this RPC. Falling back to static value: {} microlamports", "WARNING".bold().yellow(), fee)); + fee + }; final_ixs.remove(1); final_ixs.insert(1, ComputeBudgetInstruction::set_compute_unit_price(fee)); - progress_bar.println(format!(" Priority fee: {} microlamports", fee)); } // Resign the tx