Skip to content
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
8 changes: 8 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,14 @@ members = [
"playground/rust-sdk-playground",
]

[workspace.lints.clippy]
unwrap_used = "deny"
expect_used = "deny"
panic = "deny"
todo = "deny"
unimplemented = "deny"
unreachable = "deny"

[workspace.dependencies]
serde = { version = "1", features = ["derive"] }
serde_json = "1.0"
Expand Down
3 changes: 3 additions & 0 deletions clippy.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
allow-unwrap-in-tests = true
allow-expect-in-tests = true
allow-panic-in-tests = true
3 changes: 3 additions & 0 deletions crates/cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -34,3 +34,6 @@ rand = "0.8.5"

[features]
jemalloc = ["dep:tikv-jemallocator", "dep:tikv-jemalloc-ctl"]

[lints]
workspace = true
11 changes: 8 additions & 3 deletions crates/cli/src/commands/network.rs
Original file line number Diff line number Diff line change
Expand Up @@ -94,11 +94,16 @@ async fn handle_add(project_path: &ProjectLocation) -> Result<(), NetworkError>
.allow_empty(true)
.interact_text()?;

let first_provider_url = provider_urls.first().ok_or_else(|| {
NetworkError::InvalidConfig("At least one provider URL is required".to_string())
})?;
let chain_id = get_chain_id(first_provider_url).await.map_err(|e| {
NetworkError::ConnectionFailed(format!("Could not read from rpc for the chain id: {}", e))
})?;

setup_config.networks.push(NetworkSetupConfig {
name: network_name.clone(),
chain_id: get_chain_id(provider_urls.first().unwrap())
.await
.expect("Could not read from rpc for the chain id"),
chain_id,
signing_provider: None,
provider_urls,
block_explorer_url: if block_explorer.is_empty() { None } else { Some(block_explorer) },
Expand Down
15 changes: 13 additions & 2 deletions crates/cli/src/commands/tx.rs
Original file line number Diff line number Diff line change
Expand Up @@ -395,7 +395,9 @@ async fn handle_fund(
.into();

let network = client.network().get_all().await?;
let network = network.into_iter().find(|n| n.chain_id == chain_id).expect("Network not found");
let network = network.into_iter().find(|n| n.chain_id == chain_id).ok_or_else(|| {
TransactionError::CommandFailed(format!("Network not found: {}", chain_id))
})?;

println!("┌─────────────────────────────────────────────────────────────────────");
println!("│ FUNDING RELAYER");
Expand Down Expand Up @@ -445,7 +447,16 @@ async fn handle_fund(

let wallet = EthereumWallet::from(signer);

let rpc_url = network.provider_urls.first().expect("Providers not found").to_string();
let rpc_url = network
.provider_urls
.first()
.ok_or_else(|| {
TransactionError::CommandFailed(format!(
"Providers not found for network {}",
network.name
))
})?
.to_string();
let provider =
ProviderBuilder::new().wallet(wallet).connect(&rpc_url).await.map_err(|e| {
TransactionError::CommandFailed(format!("Failed to connect to RPC: {}", e))
Expand Down
3 changes: 3 additions & 0 deletions crates/core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -68,3 +68,6 @@ rsa = "0.9"

[build-dependencies]
chrono = "0.4.19"

[lints]
workspace = true
27 changes: 18 additions & 9 deletions crates/core/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,11 @@ fn branch_name_from_ref(branch_ref: &str) -> Option<&str> {
}
}

fn epoch_to_iso_timestamp(epoch: i64) -> String {
Utc.timestamp_opt(epoch, 0).unwrap().to_rfc3339_opts(SecondsFormat::Secs, true)
fn epoch_to_iso_timestamp(epoch: i64) -> Result<String, String> {
Utc.timestamp_opt(epoch, 0)
.single()
.map(|timestamp| timestamp.to_rfc3339_opts(SecondsFormat::Secs, true))
.ok_or_else(|| format!("invalid epoch timestamp: {epoch}"))
}

fn main() -> Result<(), Box<dyn std::error::Error>> {
Expand All @@ -79,8 +82,12 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {

set_env_if_needed(
"BUILD_COMMIT_TIMESTAMP_ISO",
&command_output("git", &["log", "-1", "--format=%ct"])
.map(|epoch| epoch_to_iso_timestamp(epoch.parse().unwrap())),
&command_output("git", &["log", "-1", "--format=%ct"]).and_then(|epoch| {
let epoch = epoch
.parse()
.map_err(|e| format!("could not parse commit timestamp `{epoch}`: {e}"))?;
epoch_to_iso_timestamp(epoch)
}),
);

// TODO: get commit labels
Expand Down Expand Up @@ -136,11 +143,13 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
);

// https://reproducible-builds.org/docs/source-date-epoch/#rust
let build_timestamp_iso = epoch_to_iso_timestamp(
option_env!("SOURCE_DATE_EPOCH")
.map(|epoch| epoch.parse().unwrap())
.unwrap_or_else(|| Utc::now().timestamp()),
);
let build_epoch = match option_env!("SOURCE_DATE_EPOCH") {
Some(epoch) => epoch
.parse()
.map_err(|e| format!("could not parse SOURCE_DATE_EPOCH `{epoch}`: {e}"))?,
None => Utc::now().timestamp(),
};
let build_timestamp_iso = epoch_to_iso_timestamp(build_epoch)?;
println!("cargo:rustc-env=BUILD_TIMESTAMP_ISO={build_timestamp_iso}");

Ok(())
Expand Down
6 changes: 3 additions & 3 deletions crates/core/src/authentication/basic_auth.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use axum::response::Response;
use axum::{
async_trait,
extract::FromRequestParts,
http::{request::Parts, HeaderMap, StatusCode},
http::{request::Parts, HeaderMap, HeaderValue, StatusCode},
};
use base64::{engine::general_purpose, Engine as _};
use subtle::ConstantTimeEq;
Expand Down Expand Up @@ -128,9 +128,9 @@ pub async fn inject_basic_auth_status(
let basic_auth_valid = validate_basic_auth(req.headers()).is_ok();

if basic_auth_valid {
req.headers_mut().insert("x-rrelayer-basic-auth-valid", "true".parse().unwrap());
req.headers_mut().insert("x-rrelayer-basic-auth-valid", HeaderValue::from_static("true"));
} else {
req.headers_mut().insert("x-rrelayer-basic-auth-valid", "false".parse().unwrap());
req.headers_mut().insert("x-rrelayer-basic-auth-valid", HeaderValue::from_static("false"));
}

Ok(next.run(req).await)
Expand Down
11 changes: 6 additions & 5 deletions crates/core/src/background_tasks/balance_monitor.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use alloy::primitives::utils::{format_ether, parse_ether};
use alloy::primitives::utils::{format_ether, parse_ether, UnitsError};
use alloy::primitives::U256;
use std::{sync::Arc, time::Duration};
use tokio::sync::Mutex;
Expand All @@ -10,11 +10,11 @@ use crate::{
shutdown::subscribe_to_shutdown, webhooks::WebhookManager,
};

fn get_minimum_balance_threshold(chain_id: &ChainId) -> U256 {
fn get_minimum_balance_threshold(chain_id: &ChainId) -> Result<U256, UnitsError> {
if chain_id.u64() == 1 {
parse_ether("0.005").expect("Failed to parse native token threshold")
parse_ether("0.005")
} else {
parse_ether("0.001").expect("Failed to parse native token threshold")
parse_ether("0.001")
}
}

Expand Down Expand Up @@ -59,7 +59,8 @@ async fn check_balances_for_chain(
webhook_manager: &Option<Arc<Mutex<WebhookManager>>>,
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
let chain_id = provider.chain_id;
let min_balance = get_minimum_balance_threshold(&chain_id);
let min_balance = get_minimum_balance_threshold(&chain_id)
.map_err(|e| format!("Failed to parse native token threshold: {}", e))?;
let min_balance_formatted = format_ether(min_balance);

info!("Checking balances for chain {} (minimum: {} ETH)", chain_id, min_balance_formatted);
Expand Down
2 changes: 1 addition & 1 deletion crates/core/src/gas/fee_estimator/etherscan.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ impl EtherscanGasOracleResult {

// Calculate priority fees by subtracting base fee from total
// Ensure priority fee is at least 1 gwei to avoid zero or negative values
let min_priority_fee = parse_units("1", "gwei").unwrap().try_into().unwrap(); // 1 gwei minimum
let min_priority_fee = Self::parse_gwei_to_wei("1")?; // 1 gwei minimum

let safe_priority = std::cmp::max(safe_total.saturating_sub(base_fee), min_priority_fee);
let propose_priority =
Expand Down
28 changes: 19 additions & 9 deletions crates/core/src/gas/fee_estimator/fallback.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,22 @@ impl FallbackGasFeeEstimator {
.into(),
};

let default_priority_fee: u128 = if ethereum_or_ethereum_testnet {
// 2 gwei default for Ethereum
u128::try_from(
parse_units("2", "gwei")
.map_err(|e| GasEstimatorError::CustomError(e.to_string()))?,
)
.map_err(|e| GasEstimatorError::CustomError(e.to_string()))?
} else {
// 0.01 gwei default for other chains
u128::try_from(
parse_units("0.01", "gwei")
.map_err(|e| GasEstimatorError::CustomError(e.to_string()))?,
)
.map_err(|e| GasEstimatorError::CustomError(e.to_string()))?
};

let priority_fee = if let Some(rewards) = &fee_history.reward {
if !rewards.is_empty() {
let mut all_rewards: Vec<u128> = rewards
Expand All @@ -62,20 +78,14 @@ impl FallbackGasFeeEstimator {
all_rewards.sort();
let median_idx = all_rewards.len() / 2;
all_rewards[median_idx]
} else if ethereum_or_ethereum_testnet {
parse_units("2", "gwei").unwrap().try_into().unwrap() // 2 gwei default for Ethereum
} else {
parse_units("0.01", "gwei").unwrap().try_into().unwrap()
default_priority_fee
}
} else if ethereum_or_ethereum_testnet {
parse_units("2", "gwei").unwrap().try_into().unwrap() // 2 gwei default for Ethereum
} else {
parse_units("0.01", "gwei").unwrap().try_into().unwrap() // 0.01 gwei default for other chains
default_priority_fee
}
} else if ethereum_or_ethereum_testnet {
parse_units("2", "gwei").unwrap().try_into().unwrap() // 2 gwei default for Ethereum
} else {
parse_units("0.01", "gwei").unwrap().try_into().unwrap() // 0.01 gwei default for other chains
default_priority_fee
};

let max_fee = if chain_id.u64() == 1 {
Expand Down
16 changes: 4 additions & 12 deletions crates/core/src/gas/fee_estimator/tenderly.rs
Original file line number Diff line number Diff line change
Expand Up @@ -426,18 +426,10 @@ impl TenderlyGasFeeEstimator {
async fn request_gas_estimate(
&self,
chain_id: &ChainId,
) -> Result<TenderlyGasEstimatePriceResult, reqwest::Error> {
let url = match self.build_suggested_gas_price_endpoint(chain_id) {
Ok(url) => url,
Err(_) => {
let client = reqwest::Client::new();
let result = client.get("http://").send().await;
match result {
Err(error) => return Err(error),
Ok(_) => unreachable!("This should always fail"),
}
}
};
) -> Result<TenderlyGasEstimatePriceResult, GasEstimatorError> {
let url = self
.build_suggested_gas_price_endpoint(chain_id)
.map_err(GasEstimatorError::CustomError)?;
println!("Tenderly gas estimate url: {}", url);
let client = reqwest::Client::new();

Expand Down
2 changes: 1 addition & 1 deletion crates/core/src/provider/evm_provider.rs
Original file line number Diff line number Diff line change
Expand Up @@ -652,7 +652,7 @@ mod tests {
&self,
_chain_id: &ChainId,
) -> Result<GasEstimatorResult, GasEstimatorError> {
unreachable!("clone_wallet does not estimate gas")
panic!("clone_wallet does not estimate gas")
}

fn is_chain_supported(&self, _chain_id: &ChainId) -> bool {
Expand Down
11 changes: 10 additions & 1 deletion crates/core/src/rate_limiting/rate_limiter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -497,7 +497,16 @@ impl RateLimiter {
}

fn calculate_window_start(&self, current_time: SystemTime, window_seconds: u32) -> SystemTime {
let current_timestamp = current_time.duration_since(UNIX_EPOCH).unwrap().as_secs();
let current_timestamp = match current_time.duration_since(UNIX_EPOCH) {
Ok(duration) => duration.as_secs(),
Err(e) => {
error!(
"System time is before UNIX_EPOCH ({}); defaulting rate limit window start to epoch",
e
);
0
}
};
let window_start_timestamp =
(current_timestamp / window_seconds as u64) * window_seconds as u64;
UNIX_EPOCH + std::time::Duration::from_secs(window_start_timestamp)
Expand Down
3 changes: 2 additions & 1 deletion crates/core/src/relayer/cache.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ fn build_relayer_cache_key(id: &RelayerId) -> String {

pub async fn get_relayer_cache(cache: &Arc<Cache>, relayer_id: &RelayerId) -> Option<Relayer> {
if let Some(cached_result) = cache.get(&build_relayer_cache_key(relayer_id).to_string()).await {
return cached_result.to_relayer();
// flatten: a cached `None` relayer and an unexpected cache variant both yield `None`
return cached_result.to_relayer().flatten();
}

None
Expand Down
Loading
Loading