Skip to content

Commit

Permalink
simplify dynamic fee ux
Browse files Browse the repository at this point in the history
  • Loading branch information
HardhatChad committed Aug 8, 2024
1 parent 5c1d8eb commit 9a1865d
Show file tree
Hide file tree
Showing 5 changed files with 106 additions and 92 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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" }
Expand Down
161 changes: 86 additions & 75 deletions src/dynamic_fee.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<u64> {
// 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<String> = 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)
}
}
}
23 changes: 9 additions & 14 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ struct Miner {
pub keypair_filepath: Option<String>,
pub priority_fee: Option<u64>,
pub dynamic_fee_url: Option<String>,
pub dynamic_fee_strategy: Option<String>,
pub dynamic_fee: bool,
pub rpc_client: Arc<RpcClient>,
pub fee_payer_filepath: Option<String>,
}
Expand Down Expand Up @@ -100,23 +100,23 @@ struct Args {
#[arg(
long,
value_name = "KEYPAIR_FILEPATH",
help = "Filepath to keypair to use",
help = "Filepath to keypair to use.",
global = true
)]
keypair: Option<String>,

#[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<String>,

#[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
)]
Expand All @@ -130,13 +130,8 @@ struct Args {
)]
dynamic_fee_url: Option<String>,

#[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<String>,
#[arg(long, help = "Use dynamic priority fees", global = true)]
dynamic_fee: bool,

#[command(subcommand)]
command: Commands,
Expand Down Expand Up @@ -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),
));

Expand Down Expand Up @@ -221,15 +216,15 @@ impl Miner {
priority_fee: Option<u64>,
keypair_filepath: Option<String>,
dynamic_fee_url: Option<String>,
dynamic_fee_strategy: Option<String>,
dynamic_fee: bool,
fee_payer_filepath: Option<String>,
) -> Self {
Self {
rpc_client,
keypair_filepath,
priority_fee,
dynamic_fee_url,
dynamic_fee_strategy,
dynamic_fee,
fee_payer_filepath,
}
}
Expand Down
12 changes: 9 additions & 3 deletions src/send_and_confirm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit 9a1865d

Please sign in to comment.