Skip to content

Commit

Permalink
Add optional support for the proprietary (vomit) helius RPC priority …
Browse files Browse the repository at this point in the history
…fee RPC endpoint
  • Loading branch information
mvines committed Jun 12, 2024
1 parent 93fc7dd commit 27c89c7
Show file tree
Hide file tree
Showing 5 changed files with 121 additions and 52 deletions.
12 changes: 11 additions & 1 deletion src/bin/sys-lend.rs
Original file line number Diff line number Diff line change
Expand Up @@ -349,6 +349,15 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
submit transactions with in addition to --url"),

)
.arg(
Arg::with_name("helius_json_rpc_url")
.long("helius-url")
.value_name("URL")
.takes_value(true)
.global(true)
.validator(is_url)
.help("Helium JSON RPC URL to use only for the proprietary getPriorityFeeEstimate RPC method"),
)
.arg(
Arg::with_name("priority_fee_exact")
.long("priority-fee-exact")
Expand Down Expand Up @@ -650,6 +659,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
let rpc_clients = RpcClients::new(
value_t_or_exit!(app_matches, "json_rpc_url", String),
value_t!(app_matches, "send_json_rpc_urls", String).ok(),
value_t!(app_matches, "helius_json_rpc_url", String).ok(),
);
let rpc_client = rpc_clients.default();
let mut account_data_cache = AccountDataCache::new(rpc_client);
Expand Down Expand Up @@ -1454,7 +1464,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
let mut instructions = instructions;

let priority_fee = apply_priority_fee(
rpc_client,
&rpc_clients,
&mut instructions,
required_compute_units,
priority_fee,
Expand Down
8 changes: 2 additions & 6 deletions src/helius_rpc.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,4 @@
use {
solana_client::rpc_client::RpcClient,
solana_sdk::{instruction::Instruction},
};
use {solana_client::rpc_client::RpcClient, solana_sdk::instruction::Instruction};

#[derive(serde::Serialize, serde::Deserialize, Clone, Copy, Debug, Eq, Hash, PartialEq)]
#[serde(rename_all = "camelCase")]
Expand Down Expand Up @@ -53,7 +50,6 @@ pub fn get_priority_fee_estimate_for_instructions(
priority_level: HeliusPriorityLevel,
instructions: &[Instruction],
) -> Result<u64, String> {

let mut account_keys: Vec<_> = instructions
.iter()
.flat_map(|instruction| {
Expand Down Expand Up @@ -85,7 +81,7 @@ pub fn get_priority_fee_estimate_for_instructions(
request,
)
.map(|response| {
response
response
.priority_fee_estimate
.expect("priority_fee_estimate") as u64
})
Expand Down
16 changes: 15 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,10 +48,15 @@ where

pub struct RpcClients {
clients: Vec<(String, RpcClient)>,
helius: Option<RpcClient>,
}

impl RpcClients {
pub fn new(json_rpc_url: String, send_json_rpc_urls: Option<String>) -> Self {
pub fn new(
json_rpc_url: String,
send_json_rpc_urls: Option<String>,
helius: Option<String>,
) -> 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(',') {
Expand All @@ -70,12 +75,21 @@ impl RpcClients {
)
})
.collect(),
helius: helius.map(|helius_json_rpc_url| {
RpcClient::new_with_commitment(helius_json_rpc_url, CommitmentConfig::confirmed())
}),
}
}

pub fn default(&self) -> &RpcClient {
&self.clients[0].1
}

pub fn helius_or_default(&self) -> &RpcClient {
self.helius
.as_ref()
.map_or_else(|| self.default(), |helius| helius)
}
}

// Assumes `transaction` has already been signed and simulated...
Expand Down
22 changes: 16 additions & 6 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -614,7 +614,7 @@ async fn process_exchange_deposit<T: Signers>(
(instructions, amount, compute_units)
}
};
apply_priority_fee(rpc_client, &mut instructions, compute_units, priority_fee)?;
apply_priority_fee(rpc_clients, &mut instructions, compute_units, priority_fee)?;

if amount == 0 {
return Err("Nothing to deposit".into());
Expand Down Expand Up @@ -3027,7 +3027,7 @@ async fn process_account_merge<T: Signers>(
)
.into());
};
apply_priority_fee(rpc_client, &mut instructions, 10_000, priority_fee)?;
apply_priority_fee(rpc_clients, &mut instructions, 10_000, priority_fee)?;

println!("Merging {from_address} into {into_address}");
if from_address != authority_address {
Expand Down Expand Up @@ -3363,7 +3363,7 @@ async fn process_account_sweep<T: Signers>(

let (signature, maybe_transaction) = match existing_signature {
None => {
apply_priority_fee(rpc_client, &mut instructions, 7_000, priority_fee)?;
apply_priority_fee(rpc_clients, &mut instructions, 7_000, priority_fee)?;

let mut message = Message::new(&instructions, Some(&from_authority_address));
message.recent_blockhash = recent_blockhash;
Expand Down Expand Up @@ -3494,7 +3494,7 @@ async fn process_account_split<T: Signers>(
.get_minimum_balance_for_rent_exemption(solana_sdk::stake::state::StakeStateV2::size_of())?;

let mut instructions = vec![];
apply_priority_fee(rpc_client, &mut instructions, 10_000, priority_fee)?;
apply_priority_fee(rpc_clients, &mut instructions, 10_000, priority_fee)?;

instructions.push(system_instruction::transfer(
&authority_address,
Expand Down Expand Up @@ -3980,7 +3980,7 @@ async fn process_account_wrap<T: Signers>(
spl_token::instruction::sync_native(&spl_token::id(), &wsol_address).unwrap(),
]);

apply_priority_fee(rpc_client, &mut instructions, 30_000, priority_fee)?;
apply_priority_fee(rpc_clients, &mut instructions, 30_000, priority_fee)?;
let message = Message::new(&instructions, Some(&authority_address));

let mut transaction = Transaction::new_unsigned(message);
Expand Down Expand Up @@ -4079,7 +4079,7 @@ async fn process_account_unwrap<T: Signers>(
)
.unwrap(),
];
apply_priority_fee(rpc_client, &mut instructions, 30_000, priority_fee)?;
apply_priority_fee(rpc_clients, &mut instructions, 30_000, priority_fee)?;

let message = Message::new(&instructions, Some(&authority_address));

Expand Down Expand Up @@ -4408,6 +4408,15 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
.help("Optional additional JSON RPC URLs, separated by commas, to \
submit transactions with in addition to --url"),
)
.arg(
Arg::with_name("helius_json_rpc_url")
.long("helius-url")
.value_name("URL")
.takes_value(true)
.global(true)
.validator(is_url)
.help("Helium JSON RPC URL to use only for the proprietary getPriorityFeeEstimate RPC method"),
)
.arg(
Arg::with_name("verbose")
.short("v")
Expand Down Expand Up @@ -5974,6 +5983,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
let rpc_clients = RpcClients::new(
value_t_or_exit!(app_matches, "json_rpc_url", String),
value_t!(app_matches, "send_json_rpc_urls", String).ok(),
value_t!(app_matches, "helius_json_rpc_url", String).ok(),
);

let rpc_client = rpc_clients.default();
Expand Down
115 changes: 77 additions & 38 deletions src/priority_fee.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use {
crate::{helius_rpc, RpcClients},
solana_client::rpc_client::RpcClient,
solana_sdk::{
compute_budget,
Expand Down Expand Up @@ -99,7 +100,7 @@ fn get_recent_priority_fees_for_instructions(
}

pub fn apply_priority_fee(
rpc_client: &RpcClient,
rpc_clients: &RpcClients,
instructions: &mut Vec<Instruction>,
compute_unit_limit: u32,
priority_fee: PriorityFee,
Expand All @@ -110,48 +111,86 @@ pub fn apply_priority_fee(
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 ui_fee_for = |compute_unit_price_micro_lamports: f64| {
Sol(ComputeBudget {
compute_unit_price_micro_lamports: compute_unit_price_micro_lamports as u64,
let helius_compute_budget = if let Ok(helius_priority_fee_estimate) =
helius_rpc::get_priority_fee_estimate_for_instructions(
rpc_clients.helius_or_default(),
helius_rpc::HeliusPriorityLevel::High,
instructions,
) {
let helius_compute_budget = ComputeBudget {
compute_unit_price_micro_lamports: helius_priority_fee_estimate,
compute_unit_limit,
}
.priority_fee_lamports())
};

println!(
"Helius priority fee (high): {}",
Sol(helius_compute_budget.priority_fee_lamports())
);

helius_compute_budget
} else {
ComputeBudget::default()
};

let dist =
criterion_stats::Distribution::from(recent_compute_unit_prices.into_boxed_slice());
print!("Recent priority fees: mean={}", ui_fee_for(dist.mean()));
let percentiles = dist.percentiles();
for i in [50., 75., 85., 90., 95., 100.] {
print!(", {i}th={}", ui_fee_for(percentiles.at(i)));
}
let sys_compute_budget = {
let recent_compute_unit_prices =
get_recent_priority_fees_for_instructions(rpc_clients.default(), instructions)?
.into_iter()
// .skip_while(|fee| *fee == 0) // Skip 0 fee payers
.map(|f| f as f64)
.collect::<Vec<_>>();

let ui_fee_for = |compute_unit_price_micro_lamports: f64| {
Sol(ComputeBudget {
compute_unit_price_micro_lamports: compute_unit_price_micro_lamports as u64,
compute_unit_limit,
}
.priority_fee_lamports())
};

let fee_percentile_compute_unit_price_micro_lamports =
percentiles.at(fee_percentile as f64) as u64;
let mean_compute_unit_price_micro_lamports = dist.mean() as u64;

// Use the greater of the `fee_percentile`th percentile fee or the mean fee
let compute_unit_price_micro_lamports =
if fee_percentile_compute_unit_price_micro_lamports
> mean_compute_unit_price_micro_lamports
{
println!("\nusing {fee_percentile}th percentile fee");
fee_percentile_compute_unit_price_micro_lamports
} else {
println!("\nusing mean fee");
mean_compute_unit_price_micro_lamports
let dist = criterion_stats::Distribution::from(
recent_compute_unit_prices.into_boxed_slice(),
);
let mut verbose_msg = format!("mean={}", ui_fee_for(dist.mean()));
let percentiles = dist.percentiles();
for i in [50., 75., 90., 95., 100.] {
verbose_msg += &format!(", {i}th={}", ui_fee_for(percentiles.at(i)));
}

let fee_percentile_compute_unit_price_micro_lamports =
percentiles.at(fee_percentile as f64) as u64;
let mean_compute_unit_price_micro_lamports = dist.mean() as u64;

// Use the greater of the `fee_percentile`th percentile fee or the mean fee
let compute_unit_price_micro_lamports =
if fee_percentile_compute_unit_price_micro_lamports
> mean_compute_unit_price_micro_lamports
{
verbose_msg += &format!(". Selected {fee_percentile}th percentile");
fee_percentile_compute_unit_price_micro_lamports
} else {
verbose_msg += ". Selected mean)";
mean_compute_unit_price_micro_lamports
};

let sys_compute_budget = ComputeBudget {
compute_unit_price_micro_lamports,
compute_unit_limit,
};

let compute_budget = ComputeBudget {
compute_unit_price_micro_lamports,
compute_unit_limit,
println!(
"Observed priority fee: {}\n ({verbose_msg})",
Sol(sys_compute_budget.priority_fee_lamports())
);
sys_compute_budget
};

let compute_budget = if sys_compute_budget.compute_unit_price_micro_lamports
> helius_compute_budget.compute_unit_price_micro_lamports
{
sys_compute_budget
} else {
helius_compute_budget
};

if compute_budget.priority_fee_lamports() > max_lamports {
Expand All @@ -167,7 +206,7 @@ pub fn apply_priority_fee(
};

println!(
"Priority fee: {}",
"Selected priority fee: {}",
Sol(compute_budget.priority_fee_lamports())
);
assert!(
Expand Down

0 comments on commit 27c89c7

Please sign in to comment.