Skip to content

Commit

Permalink
Improve priority fee selection
Browse files Browse the repository at this point in the history
  • Loading branch information
mvines committed Jun 4, 2024
1 parent 73eec90 commit 740ac37
Show file tree
Hide file tree
Showing 5 changed files with 167 additions and 59 deletions.
72 changes: 72 additions & 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 @@ -70,4 +70,5 @@ thiserror = "1.0"
tokio = { version = "1", features = ["macros", "time"] }
#tulipv2-sdk-common = "0.9.5"
uint = "0.9.5"
criterion-stats = "0.3.0"

36 changes: 27 additions & 9 deletions src/bin/sys-lend.rs
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,15 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
.default_value(default_json_rpc_url)
.help("JSON RPC URL for the cluster"),
)
.arg(
Arg::with_name("send_json_rpc_url")
.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]"),
)
.arg(
Arg::with_name("priority_fee_exact")
.long("priority-fee-exact")
Expand Down Expand Up @@ -439,19 +448,24 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
);

let app_matches = app.get_matches();
let rpc_client = RpcClient::new_with_commitment(
normalize_to_url_if_moniker(value_t_or_exit!(app_matches, "json_rpc_url", String)),
CommitmentConfig::confirmed(),
);

let json_rpc_url =
normalize_to_url_if_moniker(value_t_or_exit!(app_matches, "json_rpc_url", String));
let rpc_client = RpcClient::new_with_commitment(&json_rpc_url, CommitmentConfig::confirmed());
let send_json_rpc_url = match value_t!(app_matches, "send_json_rpc_url", String).ok() {
Some(send_json_rpc_url) => normalize_to_url_if_moniker(send_json_rpc_url),
None => json_rpc_url,
};
let send_rpc_client =
RpcClient::new_with_commitment(send_json_rpc_url, CommitmentConfig::confirmed());

let priority_fee = if let Ok(ui_priority_fee) = value_t!(app_matches, "priority_fee_exact", f64)
{
PriorityFee::Exact {
lamports: sol_to_lamports(ui_priority_fee),
}
} else if let Ok(ui_priority_fee) = value_t!(app_matches, "priority_fee_auto", f64) {
PriorityFee::Auto {
max_lamports: sol_to_lamports(ui_priority_fee),
}
PriorityFee::default_auto_percentile(sol_to_lamports(ui_priority_fee))
} else {
PriorityFee::default_auto()
};
Expand Down Expand Up @@ -988,7 +1002,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
&vec![signer],
)?
};
let simulation_result = rpc_client.simulate_transaction(&transaction)?.value;
let simulation_result = send_rpc_client.simulate_transaction(&transaction)?.value;
if simulation_result.err.is_some() {
return Err(format!("Simulation failure: {simulation_result:?}").into());
}
Expand Down Expand Up @@ -1026,7 +1040,11 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
),
};

if !send_transaction_until_expired(&rpc_client, &transaction, last_valid_block_height) {
if !send_transaction_until_expired(
&send_rpc_client,
&transaction,
last_valid_block_height,
) {
let msg = format!("Transaction failed: {signature}");
notifier.send(&msg).await;
return Err(msg.into());
Expand Down
4 changes: 1 addition & 3 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5926,9 +5926,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
lamports: sol_to_lamports(ui_priority_fee),
}
} else if let Ok(ui_priority_fee) = value_t!(app_matches, "priority_fee_auto", f64) {
PriorityFee::Auto {
max_lamports: sol_to_lamports(ui_priority_fee),
}
PriorityFee::default_auto_percentile(sol_to_lamports(ui_priority_fee))
} else {
PriorityFee::default_auto()
};
Expand Down
113 changes: 66 additions & 47 deletions src/priority_fee.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,22 +11,31 @@ use {

#[derive(Debug, Clone, Copy)]
pub enum PriorityFee {
Auto { max_lamports: u64 },
Exact { lamports: u64 },
Auto {
max_lamports: u64,
fee_percentile: u8,
},
Exact {
lamports: u64,
},
}

impl PriorityFee {
pub fn default_auto() -> Self {
Self::default_auto_percentile(sol_to_lamports(0.005)) // Same max as the Jupiter V6 Swap API
}
pub fn default_auto_percentile(max_lamports: u64) -> Self {
Self::Auto {
max_lamports: sol_to_lamports(0.005), // Same max as the Jupiter V6 Swap API
max_lamports,
fee_percentile: 90, // Pay at this percentile of recent fees
}
}
}

impl PriorityFee {
pub fn max_lamports(&self) -> u64 {
match self {
Self::Auto { max_lamports } => *max_lamports,
Self::Auto { max_lamports, .. } => *max_lamports,
Self::Exact { lamports } => *lamports,
}
}
Expand Down Expand Up @@ -98,52 +107,62 @@ pub fn apply_priority_fee(
compute_unit_limit: u32,
priority_fee: PriorityFee,
) -> Result<(), Box<dyn std::error::Error>> {
let compute_budget = if let Some(exact_lamports) = priority_fee.exact_lamports() {
ComputeBudget::new(compute_unit_limit, exact_lamports)
} else {
let recent_compute_unit_prices =
get_recent_priority_fees_for_instructions(rpc_client, instructions)?;

let mean_compute_unit_price_micro_lamports =
recent_compute_unit_prices.iter().copied().sum::<u64>()
/ recent_compute_unit_prices.len() as u64;

/*
let max_compute_unit_price_micro_lamports = recent_compute_unit_prices
.iter()
.max()
.copied()
.unwrap_or_default();
println!("{recent_compute_unit_prices:?}: mean {mean_compute_unit_price_micro_lamports}, max {max_compute_unit_price_micro_lamports}");
*/

let compute_unit_price_micro_lamports = mean_compute_unit_price_micro_lamports;

if let Ok(priority_fee_estimate) = helius_rpc::get_priority_fee_estimate_for_instructions(
rpc_client,
helius_rpc::HeliusPriorityLevel::High,
instructions,
) {
let compute_budget = match priority_fee {
PriorityFee::Exact { lamports } => ComputeBudget::new(compute_unit_limit, lamports),
PriorityFee::Auto {
max_lamports,
fee_percentile,
} => {
let recent_compute_unit_prices =
get_recent_priority_fees_for_instructions(rpc_client, instructions)?
.into_iter()
// .skip_while(|fee| *fee == 0) // Skip 0 fee payers
.map(|f| f as f64)
.collect::<Vec<_>>();

let dist =
criterion_stats::Distribution::from(recent_compute_unit_prices.into_boxed_slice());
print!("Recent CU prices: mean={:.0}", dist.mean());
let percentiles = dist.percentiles();
for i in [50., 75., 85., 90., 95., 100.] {
print!(", {i}th={:.0}", percentiles.at(i));
}

let compute_unit_price_micro_lamports = percentiles.at(fee_percentile as f64) as u64;
println!(
"Note: helius compute unit price (high) estimate is {priority_fee_estimate}. \
`sys` computed {compute_unit_price_micro_lamports}"
"\nUsing the {fee_percentile}th percentile recent CU price of {:.0}",
compute_unit_price_micro_lamports
);
}

let compute_budget = ComputeBudget {
compute_unit_price_micro_lamports,
compute_unit_limit,
};

if compute_budget.priority_fee_lamports() > priority_fee.max_lamports() {
println!(
"Note: Computed priority fee of {} exceeds the maximum priority fee",
Sol(compute_budget.priority_fee_lamports())
);
ComputeBudget::new(compute_unit_limit, priority_fee.max_lamports())
} else {
compute_budget
todo!();

if let Ok(priority_fee_estimate) =
helius_rpc::get_priority_fee_estimate_for_instructions(
rpc_client,
helius_rpc::HeliusPriorityLevel::High,
instructions,
)
{
println!(
"Note: helius compute unit price (high) estimate is {priority_fee_estimate}. \
`sys` computed {compute_unit_price_micro_lamports}"
);
}

let compute_budget = ComputeBudget {
compute_unit_price_micro_lamports,
compute_unit_limit,
};

if compute_budget.priority_fee_lamports() > max_lamports {
println!(
"Note: Computed priority fee of {} exceeds the maximum priority fee",
Sol(compute_budget.priority_fee_lamports())
);
ComputeBudget::new(compute_unit_limit, max_lamports)
} else {
compute_budget
}
}
};

Expand Down

0 comments on commit 740ac37

Please sign in to comment.