From 6366cd4784fc9e2f728538e825d765cd503151bd Mon Sep 17 00:00:00 2001 From: MartinquaXD Date: Thu, 18 Dec 2025 14:13:52 +0000 Subject: [PATCH 1/2] Simplify tx gas price logic --- .../driver/src/domain/competition/auction.rs | 2 +- .../domain/competition/solution/settlement.rs | 2 +- crates/driver/src/domain/mempools.rs | 8 ++-- .../driver/src/infra/api/routes/gasprice.rs | 2 +- crates/driver/src/infra/blockchain/gas.rs | 31 +++++------- crates/driver/src/infra/blockchain/mod.rs | 8 ++-- crates/driver/src/tests/setup/solver.rs | 2 +- .../shared/src/gas_price_estimation/alloy.rs | 47 +++++-------------- 8 files changed, 36 insertions(+), 66 deletions(-) diff --git a/crates/driver/src/domain/competition/auction.rs b/crates/driver/src/domain/competition/auction.rs index b7de0e3715..c91779d029 100644 --- a/crates/driver/src/domain/competition/auction.rs +++ b/crates/driver/src/domain/competition/auction.rs @@ -60,7 +60,7 @@ impl Auction { id, orders, tokens, - gas_price: eth.gas_price(None).await?, + gas_price: eth.gas_price().await?, deadline, surplus_capturing_jit_order_owners, }) diff --git a/crates/driver/src/domain/competition/solution/settlement.rs b/crates/driver/src/domain/competition/solution/settlement.rs index 99b5316111..26a4327fb5 100644 --- a/crates/driver/src/domain/competition/solution/settlement.rs +++ b/crates/driver/src/domain/competition/solution/settlement.rs @@ -169,7 +169,7 @@ impl Settlement { simulator, ) .await?; - let price = eth.gas_price(None).await?; + let price = eth.gas_price().await?; let gas = Gas::new(gas, eth.block_gas_limit())?; // Ensure that the solver has sufficient balance for the settlement to be mined diff --git a/crates/driver/src/domain/mempools.rs b/crates/driver/src/domain/mempools.rs index 2803b043ed..e96c879abc 100644 --- a/crates/driver/src/domain/mempools.rs +++ b/crates/driver/src/domain/mempools.rs @@ -133,13 +133,11 @@ impl Mempools { // deadline let current_gas_price = self .ethereum - .gas_price(None) + .gas_price() .await .context("failed to compute current gas price")?; let submission_block = self.ethereum.current_block().borrow().number; let blocks_until_deadline = submission_deadline.saturating_sub(submission_block); - let estimated_gas_price = - current_gas_price * GAS_PRICE_BUMP.powi(blocks_until_deadline as i32); // if there is still a tx pending we also have to make sure we outbid that one // enough to make the node replace it in the mempool @@ -148,11 +146,11 @@ impl Mempools { .await; let final_gas_price = match &replacement_gas_price { Ok(Some(replacement_gas_price)) - if replacement_gas_price.max() > estimated_gas_price.max() => + if replacement_gas_price.max() > current_gas_price.max() => { *replacement_gas_price } - _ => estimated_gas_price, + _ => current_gas_price, }; tracing::debug!( diff --git a/crates/driver/src/infra/api/routes/gasprice.rs b/crates/driver/src/infra/api/routes/gasprice.rs index a89097be3a..2c66f40d7a 100644 --- a/crates/driver/src/infra/api/routes/gasprice.rs +++ b/crates/driver/src/infra/api/routes/gasprice.rs @@ -32,7 +32,7 @@ async fn route( eth: axum::extract::State, ) -> Result, (hyper::StatusCode, axum::Json)> { // For simplicity we use the default time limit (None) - let gas_price = eth.gas_price(None).await?; + let gas_price = eth.gas_price().await?; Ok(Json(GasPriceResponse { max_fee_per_gas: gas_price.max().0.0, diff --git a/crates/driver/src/infra/blockchain/gas.rs b/crates/driver/src/infra/blockchain/gas.rs index 57bd2c3350..371e851379 100644 --- a/crates/driver/src/infra/blockchain/gas.rs +++ b/crates/driver/src/infra/blockchain/gas.rs @@ -10,13 +10,11 @@ use { }, ethrpc::Web3, gas_estimation::{ - DEFAULT_GAS_LIMIT, - DEFAULT_TIME_LIMIT, GasPriceEstimating, nativegasestimator::{NativeGasEstimator, Params}, }, shared::gas_price_estimation::alloy::AlloyGasPriceEstimator, - std::{sync::Arc, time::Duration}, + std::sync::Arc, }; type MaxAdditionalTip = eth::U256; @@ -94,14 +92,10 @@ impl GasPriceEstimator { /// If additional tip is configured, it will be added to the gas price. This /// is to increase the chance of a transaction being included in a block, in /// case private submission networks are used. - pub async fn estimate(&self, time_limit: Option) -> Result { - let estimate = self - .gas - .estimate_with_limits(DEFAULT_GAS_LIMIT, time_limit.unwrap_or(DEFAULT_TIME_LIMIT)) - .await - .map_err(Error::GasPrice)?; + pub async fn estimate(&self) -> Result { + let estimate = self.gas.estimate().await.map_err(Error::GasPrice)?; - let max_priority_fee_per_gas = { + let bumped_max_priority_fee_per_gas = { // the driver supports tweaking the tx gas price tip in case the gas // price estimator is systematically too low => compute configured tip bump let (max_additional_tip, tip_percentage_increase) = self.additional_tip; @@ -114,13 +108,14 @@ impl GasPriceEstimator { eth::U256::from(estimate.max_priority_fee_per_gas + additional_tip), ) }; + let priority_fee_increase = bumped_max_priority_fee_per_gas + .saturating_sub(eth::U256::from(estimate.max_priority_fee_per_gas)); - // make sure the used max fee per gas is at least big enough to cover the tip - - // otherwise the tx will be rejected by the node immediately - let suggested_max_fee_per_gas = eth::U256::from(estimate.max_fee_per_gas); - let suggested_max_fee_per_gas = - std::cmp::max(suggested_max_fee_per_gas, max_priority_fee_per_gas); - if suggested_max_fee_per_gas > self.max_fee_per_gas { + // increase `max_fee_per_gas` to account for the increase in the priority fee + let max_fee_per_gas = eth::U256::from(estimate.max_fee_per_gas) + priority_fee_increase; + + // ensure that the full max_fee_per_gas is still below the configure maximum + if max_fee_per_gas > self.max_fee_per_gas { return Err(Error::GasPrice(anyhow::anyhow!( "suggested gas price is higher than maximum allowed gas price (network is too \ congested)" @@ -128,8 +123,8 @@ impl GasPriceEstimator { } Ok(eth::GasPrice::new( - suggested_max_fee_per_gas.into(), - max_priority_fee_per_gas.into(), + max_fee_per_gas.into(), + bumped_max_priority_fee_per_gas.into(), eth::U256::from(estimate.base_fee_per_gas).into(), )) } diff --git a/crates/driver/src/infra/blockchain/mod.rs b/crates/driver/src/infra/blockchain/mod.rs index 47fa795519..9f4c659594 100644 --- a/crates/driver/src/infra/blockchain/mod.rs +++ b/crates/driver/src/infra/blockchain/mod.rs @@ -18,7 +18,7 @@ use { BalanceOverriding, }, }, - std::{fmt, sync::Arc, time::Duration}, + std::{fmt, sync::Arc}, thiserror::Error, tracing::{Level, instrument}, url::Url, @@ -237,8 +237,8 @@ impl Ethereum { /// The gas price is determined based on the deadline by which the /// transaction must be included on-chain. A shorter deadline requires a /// higher gas price to increase the likelihood of timely inclusion. - pub async fn gas_price(&self, time_limit: Option) -> Result { - self.inner.gas.estimate(time_limit).await + pub async fn gas_price(&self) -> Result { + self.inner.gas.estimate().await } pub fn block_gas_limit(&self) -> eth::Gas { @@ -304,7 +304,7 @@ impl Ethereum { // the node specific fallback value instead of failing the whole call. self.inner .gas - .estimate(None) + .estimate() .await .ok() .map(|gas| gas.effective().0.0) diff --git a/crates/driver/src/tests/setup/solver.rs b/crates/driver/src/tests/setup/solver.rs index c24f206772..7008b35f18 100644 --- a/crates/driver/src/tests/setup/solver.rs +++ b/crates/driver/src/tests/setup/solver.rs @@ -499,7 +499,7 @@ impl Solver { move |axum::extract::State(state): axum::extract::State, axum::extract::Json(req): axum::extract::Json| async move { let effective_gas_price = eth - .gas_price(None) + .gas_price() .await .unwrap() .effective() diff --git a/crates/shared/src/gas_price_estimation/alloy.rs b/crates/shared/src/gas_price_estimation/alloy.rs index db4e25aa2a..dff89fa142 100644 --- a/crates/shared/src/gas_price_estimation/alloy.rs +++ b/crates/shared/src/gas_price_estimation/alloy.rs @@ -6,12 +6,12 @@ //! for the implementation details. use { - alloy::{consensus::BlockHeader, providers::Provider}, + alloy::providers::Provider, anyhow::{Context, Result}, ethrpc::AlloyProvider, futures::TryFutureExt, gas_estimation::{GasPrice1559, GasPriceEstimating}, - std::{ops::Mul, time::Duration}, + std::time::Duration, tracing::instrument, }; @@ -31,37 +31,21 @@ impl GasPriceEstimating for AlloyGasPriceEstimator { _gas_limit: f64, _time_limit: Duration, ) -> Result { - let estimate_fees = self + let fees = self .0 .estimate_eip1559_fees() - .map_err(|err| anyhow::anyhow!("could not estimate EIP 1559 fees: {err:?}")); - let get_block = self - .0 - .get_block(alloy::eips::BlockId::Number( - alloy::eips::BlockNumberOrTag::Latest, - )) - .into_future() - .map_err(|err| anyhow::anyhow!("could not fetch latest block: {err:?}")); - let (fees, block) = tokio::try_join!(estimate_fees, get_block)?; - - /// `Alloy`'s constant growth factor to estimate the base_fee - /// of the next block. () - const MAX_GAS_PRICE_INCREASE_PER_BLOCK: f64 = 2.; + .map_err(|err| anyhow::anyhow!("could not estimate EIP 1559 fees: {err:?}")) + .await?; - let base_fee_per_gas = u64_to_f64( - block - .context("latest block is missing")? - .into_consensus_header() - .base_fee_per_gas() - .context("no base_fee_per_gas")?, - ) - .context("could not convert base_fee_per_gas to f64")? - .mul(MAX_GAS_PRICE_INCREASE_PER_BLOCK); + let max_fee_per_gas = u128_to_f64(fees.max_fee_per_gas) + .context("could not convert max_fee_per_gas to f64")?; Ok(GasPrice1559 { - base_fee_per_gas, - max_fee_per_gas: u128_to_f64(fees.max_fee_per_gas) - .context("could not convert max_fee_per_gas to f64")?, + // We reuse `max_fee_per_gas` since the base fee only actually + // exists in a mined block. For price estimates used to configure + // the gas price of a transaction the base fee doesn't matter. + base_fee_per_gas: max_fee_per_gas, + max_fee_per_gas, max_priority_fee_per_gas: u128_to_f64(fees.max_priority_fee_per_gas) .context("could not convert max_priority_fee_per_gas to f64")?, }) @@ -74,10 +58,3 @@ fn u128_to_f64(val: u128) -> Result { } Ok(val as f64) } - -fn u64_to_f64(val: u64) -> Result { - if val > 2u64.pow(f64::MANTISSA_DIGITS) { - anyhow::bail!(format!("could not convert u64 to f64: {val}")); - } - Ok(val as f64) -} From a4861980dca67d48ab773dc03f193c534b665a7d Mon Sep 17 00:00:00 2001 From: MartinquaXD Date: Thu, 18 Dec 2025 14:28:56 +0000 Subject: [PATCH 2/2] revert incremental increase of `max_fee_per_gas` --- crates/driver/src/infra/blockchain/gas.rs | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/crates/driver/src/infra/blockchain/gas.rs b/crates/driver/src/infra/blockchain/gas.rs index 371e851379..9cc27680cb 100644 --- a/crates/driver/src/infra/blockchain/gas.rs +++ b/crates/driver/src/infra/blockchain/gas.rs @@ -95,7 +95,7 @@ impl GasPriceEstimator { pub async fn estimate(&self) -> Result { let estimate = self.gas.estimate().await.map_err(Error::GasPrice)?; - let bumped_max_priority_fee_per_gas = { + let max_priority_fee_per_gas = { // the driver supports tweaking the tx gas price tip in case the gas // price estimator is systematically too low => compute configured tip bump let (max_additional_tip, tip_percentage_increase) = self.additional_tip; @@ -108,14 +108,13 @@ impl GasPriceEstimator { eth::U256::from(estimate.max_priority_fee_per_gas + additional_tip), ) }; - let priority_fee_increase = bumped_max_priority_fee_per_gas - .saturating_sub(eth::U256::from(estimate.max_priority_fee_per_gas)); - // increase `max_fee_per_gas` to account for the increase in the priority fee - let max_fee_per_gas = eth::U256::from(estimate.max_fee_per_gas) + priority_fee_increase; - - // ensure that the full max_fee_per_gas is still below the configure maximum - if max_fee_per_gas > self.max_fee_per_gas { + // make sure the used max fee per gas is at least big enough to cover the tip - + // otherwise the tx will be rejected by the node immediately + let suggested_max_fee_per_gas = eth::U256::from(estimate.max_fee_per_gas); + let suggested_max_fee_per_gas = + std::cmp::max(suggested_max_fee_per_gas, max_priority_fee_per_gas); + if suggested_max_fee_per_gas > self.max_fee_per_gas { return Err(Error::GasPrice(anyhow::anyhow!( "suggested gas price is higher than maximum allowed gas price (network is too \ congested)" @@ -123,8 +122,8 @@ impl GasPriceEstimator { } Ok(eth::GasPrice::new( - max_fee_per_gas.into(), - bumped_max_priority_fee_per_gas.into(), + suggested_max_fee_per_gas.into(), + max_priority_fee_per_gas.into(), eth::U256::from(estimate.base_fee_per_gas).into(), )) }