Skip to content
This repository was archived by the owner on Jun 1, 2026. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions Cargo.lock

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

2 changes: 1 addition & 1 deletion apps/api/src/devices/guard.rs
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ impl<'r> FromRequest<'r> for AuthenticatedDeviceWallet {
return auth_error_outcome(req, DeviceError::DeviceNotFound);
};

let Ok(wallet_row) = WalletsStore::get_wallet(&mut db_client, &wallet_id_str) else {
let Ok(wallet_row) = WalletsStore::get_wallet(&mut db_client, wallet_id_str) else {
return auth_error_outcome(req, DeviceError::WalletNotFound);
};

Expand Down
3 changes: 3 additions & 0 deletions bin/gas-bench/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,12 @@ tokio = { workspace = true }
num-bigint = { workspace = true }
reqwest = { workspace = true, features = ["json"] }
serde = { workspace = true, features = ["derive"] }
serde_json = { workspace = true }
prettytable-rs = "^0.10"

primitives = { path = "../../crates/primitives" }
gemstone = { path = "../../gemstone", features = ["reqwest_provider"] }
gem_evm = { path = "../../crates/gem_evm" }
gem_solana = { path = "../../crates/gem_solana", features = ["reqwest"] }
gem_jsonrpc = { path = "../../crates/gem_jsonrpc", features = ["client"] }
serde_serializers = { path = "../../crates/serde_serializers" }
39 changes: 39 additions & 0 deletions bin/gas-bench/src/helius/client.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
use super::{HeliusPriorityFeeOptions, HeliusPriorityFeeParams, HeliusPriorityFeeRequest, HeliusPriorityFeeResponse, HeliusPriorityFees};
use std::error::Error;

pub struct HeliusClient {
client: reqwest::Client,
endpoint: String,
}

impl HeliusClient {
pub fn new(api_key: &str) -> Self {
Self {
client: reqwest::Client::new(),
endpoint: format!("https://mainnet.helius-rpc.com/?api-key={}", api_key),
}
}

pub async fn fetch_priority_fee_estimate(&self, account_keys: Option<Vec<String>>) -> Result<HeliusPriorityFees, Box<dyn Error + Send + Sync>> {
let request = HeliusPriorityFeeRequest {
jsonrpc: "2.0",
id: "1",
method: "getPriorityFeeEstimate",
params: vec![HeliusPriorityFeeParams {
account_keys,
options: HeliusPriorityFeeOptions {
include_all_priority_fee_levels: true,
lookback_slots: 150,
},
}],
};

let response = self.client.post(&self.endpoint).json(&request).send().await?;

let result: HeliusPriorityFeeResponse = response.json().await?;

let levels = result.result.priority_fee_levels.ok_or("No priority fee levels in response")?;

Ok(HeliusPriorityFees::from_levels(&levels))
}
}
5 changes: 5 additions & 0 deletions bin/gas-bench/src/helius/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
mod client;
mod model;

pub use client::*;
pub use model::*;
60 changes: 60 additions & 0 deletions bin/gas-bench/src/helius/model.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
use serde::{Deserialize, Serialize};

#[derive(Debug, Serialize)]
pub struct HeliusPriorityFeeRequest {
pub jsonrpc: &'static str,
pub id: &'static str,
pub method: &'static str,
pub params: Vec<HeliusPriorityFeeParams>,
}

#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct HeliusPriorityFeeParams {
#[serde(skip_serializing_if = "Option::is_none")]
pub account_keys: Option<Vec<String>>,
pub options: HeliusPriorityFeeOptions,
}

#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct HeliusPriorityFeeOptions {
pub include_all_priority_fee_levels: bool,
pub lookback_slots: u32,
}

#[derive(Debug, Deserialize)]
pub struct HeliusPriorityFeeResponse {
pub result: HeliusPriorityFeeResult,
}

#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct HeliusPriorityFeeResult {
pub priority_fee_levels: Option<HeliusPriorityFeeLevels>,
}

#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct HeliusPriorityFeeLevels {
pub low: f64,
pub medium: f64,
pub high: f64,
}

#[derive(Debug)]
pub struct HeliusPriorityFees {
pub low: u64,
pub medium: u64,
pub high: u64,
}

impl HeliusPriorityFees {
pub fn from_levels(levels: &HeliusPriorityFeeLevels) -> Self {
Self {
low: levels.low as u64,
medium: levels.medium as u64,
high: levels.high as u64,
}
}
}
22 changes: 22 additions & 0 deletions bin/gas-bench/src/jito/client.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
use super::{JitoTipFloor, JitoTipFloorEntry};
use std::error::Error;

const JITO_TIP_FLOOR_URL: &str = "https://bundles.jito.wtf/api/v1/bundles/tip_floor";

#[derive(Default)]
pub struct JitoClient {
client: reqwest::Client,
}

impl JitoClient {
pub fn new() -> Self {
Self::default()
}

pub async fn fetch_tip_floor(&self) -> Result<JitoTipFloor, Box<dyn Error + Send + Sync>> {
let response = self.client.get(JITO_TIP_FLOOR_URL).send().await?;
let entries: Vec<JitoTipFloorEntry> = response.json().await?;
let entry = entries.first().ok_or("No tip floor data returned from Jito API")?;
Ok(JitoTipFloor::from_entry(entry))
}
}
5 changes: 5 additions & 0 deletions bin/gas-bench/src/jito/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
mod client;
mod model;

pub use client::*;
pub use model::*;
57 changes: 57 additions & 0 deletions bin/gas-bench/src/jito/model.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
use serde::Deserialize;

const LAMPORTS_PER_SOL: u64 = 1_000_000_000;
const MICRO_LAMPORTS_PER_LAMPORT: u64 = 1_000_000;

#[derive(Debug, Deserialize)]
pub struct JitoTipFloorEntry {
pub landed_tips_25th_percentile: f64,
pub landed_tips_50th_percentile: f64,
pub landed_tips_75th_percentile: f64,
}

#[derive(Debug)]
pub struct JitoTipFloor {
pub p25_lamports: u64,
pub p50_lamports: u64,
pub p75_lamports: u64,
}

impl JitoTipFloor {
pub fn from_entry(entry: &JitoTipFloorEntry) -> Self {
Self {
p25_lamports: sol_to_lamports(entry.landed_tips_25th_percentile),
p50_lamports: sol_to_lamports(entry.landed_tips_50th_percentile),
p75_lamports: sol_to_lamports(entry.landed_tips_75th_percentile),
}
}
}

pub fn sol_to_lamports(sol: f64) -> u64 {
(sol * LAMPORTS_PER_SOL as f64) as u64
}

pub fn lamports_to_sol(lamports: u64) -> String {
let sol = lamports as f64 / LAMPORTS_PER_SOL as f64;
if sol < 0.000001 {
format!("{:.9}", sol)
} else if sol < 0.001 {
format!("{:.6}", sol)
} else {
format!("{:.4}", sol)
}
}

pub fn priority_fee_to_lamports(micro_lamports_per_cu: u64, compute_units: u64) -> u64 {
(micro_lamports_per_cu as u128 * compute_units as u128 / MICRO_LAMPORTS_PER_LAMPORT as u128) as u64
}

pub fn format_micro_lamports(micro_lamports: u64) -> String {
if micro_lamports >= MICRO_LAMPORTS_PER_LAMPORT {
format!("{:.2}M", micro_lamports as f64 / MICRO_LAMPORTS_PER_LAMPORT as f64)
} else if micro_lamports >= 1_000 {
format!("{:.1}K", micro_lamports as f64 / 1_000.0)
} else {
format!("{}", micro_lamports)
}
}
Loading
Loading