From ec24b377ffe6337cb8be6847fb38ae4eef50174b Mon Sep 17 00:00:00 2001 From: Marcin Szymczak Date: Wed, 15 Oct 2025 18:28:15 +0100 Subject: [PATCH 1/5] Make tx gas limit configurable in driver Required to fix Sepolia after fusaka hard fork. --- crates/driver/src/infra/blockchain/mod.rs | 51 +++++++++++++---------- crates/driver/src/infra/cli.rs | 6 +++ crates/driver/src/run.rs | 5 ++- crates/driver/src/tests/setup/solver.rs | 1 + 4 files changed, 38 insertions(+), 25 deletions(-) diff --git a/crates/driver/src/infra/blockchain/mod.rs b/crates/driver/src/infra/blockchain/mod.rs index 52ce057aae..e430600d5e 100644 --- a/crates/driver/src/infra/blockchain/mod.rs +++ b/crates/driver/src/infra/blockchain/mod.rs @@ -2,7 +2,7 @@ use { self::contracts::ContractAt, crate::{boundary, domain::eth}, chain::Chain, - ethcontract::errors::ExecutionError, + ethcontract::{U256, errors::ExecutionError}, ethrpc::{Web3, block_stream::CurrentBlockWatcher}, shared::{ account_balances::{BalanceSimulator, SimulationError}, @@ -82,6 +82,7 @@ struct Inner { current_block: CurrentBlockWatcher, balance_simulator: BalanceSimulator, balance_overrider: Arc, + tx_gas_limit: Option, } impl Ethereum { @@ -96,6 +97,7 @@ impl Ethereum { addresses: contracts::Addresses, gas: Arc, archive_node_url: Option<&Url>, + tx_gas_limit: Option, ) -> Self { let Rpc { web3, chain, args } = rpc; @@ -136,6 +138,7 @@ impl Ethereum { gas, balance_simulator, balance_overrider, + tx_gas_limit, }), web3, } @@ -193,28 +196,30 @@ impl Ethereum { // Specifically set high gas because some nodes don't pick a sensible value if // omitted. And since we are only interested in access lists a very high // value is fine. - tx.gas = Some(match self.inner.chain { - // Arbitrum has an exceptionally high block gas limit (1,125,899,906,842,624), - // making it unsuitable for this use case. To address this, we use a - // fixed gas limit of 100,000,000, which is sufficient - // for all solution types, while avoiding the "insufficient funds for gas * price + - // value" error that could occur when a large amount of ETH is - // needed to simulate the transaction, due to high transaction gas limit. - // - // If a new network is added, ensure its block gas limit is checked and handled - // appropriately to maintain compatibility with this logic. - Chain::ArbitrumOne => 100_000_000.into(), - Chain::Mainnet => self.block_gas_limit().0, - Chain::Goerli => self.block_gas_limit().0, - Chain::Gnosis => self.block_gas_limit().0, - Chain::Sepolia => self.block_gas_limit().0, - Chain::Base => self.block_gas_limit().0, - Chain::Bnb => self.block_gas_limit().0, - Chain::Optimism => self.block_gas_limit().0, - Chain::Avalanche => self.block_gas_limit().0, - Chain::Polygon => self.block_gas_limit().0, - Chain::Lens => self.block_gas_limit().0, - Chain::Hardhat => self.block_gas_limit().0, + tx.gas = self.inner.tx_gas_limit.or_else(|| { + Some(match self.inner.chain { + // Arbitrum has an exceptionally high block gas limit (1,125,899,906,842,624), + // making it unsuitable for this use case. To address this, we use a + // fixed gas limit of 100,000,000, which is sufficient + // for all solution types, while avoiding the "insufficient funds for gas * price + + // value" error that could occur when a large amount of ETH is + // needed to simulate the transaction, due to high transaction gas limit. + // + // If a new network is added, ensure its block gas limit is checked and handled + // appropriately to maintain compatibility with this logic. + Chain::ArbitrumOne => 100_000_000.into(), + Chain::Mainnet => self.block_gas_limit().0, + Chain::Goerli => self.block_gas_limit().0, + Chain::Gnosis => self.block_gas_limit().0, + Chain::Sepolia => self.block_gas_limit().0, + Chain::Base => self.block_gas_limit().0, + Chain::Bnb => self.block_gas_limit().0, + Chain::Optimism => self.block_gas_limit().0, + Chain::Avalanche => self.block_gas_limit().0, + Chain::Polygon => self.block_gas_limit().0, + Chain::Lens => self.block_gas_limit().0, + Chain::Hardhat => self.block_gas_limit().0, + }) }); tx.gas_price = self.simulation_gas_price().await; diff --git a/crates/driver/src/infra/cli.rs b/crates/driver/src/infra/cli.rs index 79ad6f190d..577b5db410 100644 --- a/crates/driver/src/infra/cli.rs +++ b/crates/driver/src/infra/cli.rs @@ -1,4 +1,5 @@ use { + ethcontract::U256, reqwest::Url, shared::arguments::TracingArguments, std::{net::SocketAddr, path::PathBuf}, @@ -46,4 +47,9 @@ pub struct Args { /// https://github.com/cowprotocol/services/blob/main/crates/driver/example.toml. #[clap(long, env)] pub config: PathBuf, + + /// Transaction gas limit + /// If not specified, uses current block's gas limit + #[clap(long, env)] + pub tx_gas_limit: Option, } diff --git a/crates/driver/src/run.rs b/crates/driver/src/run.rs index f33e686a89..362df7fa50 100644 --- a/crates/driver/src/run.rs +++ b/crates/driver/src/run.rs @@ -63,7 +63,7 @@ async fn run_with(args: cli::Args, addr_sender: Option blockchain::Rpc { .expect("connect ethereum RPC") } -async fn ethereum(config: &infra::Config, ethrpc: blockchain::Rpc) -> Ethereum { +async fn ethereum(args: &cli::Args, config: &infra::Config, ethrpc: blockchain::Rpc) -> Ethereum { let gas = Arc::new( blockchain::GasPriceEstimator::new(ethrpc.web3(), &config.gas_estimator, &config.mempools) .await @@ -171,6 +171,7 @@ async fn ethereum(config: &infra::Config, ethrpc: blockchain::Rpc) -> Ethereum { config.contracts.clone(), gas, config.archive_node_url.as_ref(), + args.tx_gas_limit, ) .await } diff --git a/crates/driver/src/tests/setup/solver.rs b/crates/driver/src/tests/setup/solver.rs index 8a2c8baf22..23bdf236d7 100644 --- a/crates/driver/src/tests/setup/solver.rs +++ b/crates/driver/src/tests/setup/solver.rs @@ -484,6 +484,7 @@ impl Solver { }, gas, None, + None ) .await; From 5c6f1f137ed5db6e398305e2e37ef3489dbb4f1d Mon Sep 17 00:00:00 2001 From: Marcin Szymczak Date: Thu, 16 Oct 2025 15:20:05 +0100 Subject: [PATCH 2/5] Make the tx gas fee mandatory --- crates/driver/src/infra/blockchain/mod.rs | 33 +++-------------------- crates/driver/src/infra/cli.rs | 2 +- crates/driver/src/tests/setup/solver.rs | 2 +- 3 files changed, 5 insertions(+), 32 deletions(-) diff --git a/crates/driver/src/infra/blockchain/mod.rs b/crates/driver/src/infra/blockchain/mod.rs index e430600d5e..7eca82b195 100644 --- a/crates/driver/src/infra/blockchain/mod.rs +++ b/crates/driver/src/infra/blockchain/mod.rs @@ -82,7 +82,7 @@ struct Inner { current_block: CurrentBlockWatcher, balance_simulator: BalanceSimulator, balance_overrider: Arc, - tx_gas_limit: Option, + tx_gas_limit: U256, } impl Ethereum { @@ -97,7 +97,7 @@ impl Ethereum { addresses: contracts::Addresses, gas: Arc, archive_node_url: Option<&Url>, - tx_gas_limit: Option, + tx_gas_limit: U256, ) -> Self { let Rpc { web3, chain, args } = rpc; @@ -193,34 +193,7 @@ impl Ethereum { CallRequest: From, { let mut tx: CallRequest = tx.into(); - // Specifically set high gas because some nodes don't pick a sensible value if - // omitted. And since we are only interested in access lists a very high - // value is fine. - tx.gas = self.inner.tx_gas_limit.or_else(|| { - Some(match self.inner.chain { - // Arbitrum has an exceptionally high block gas limit (1,125,899,906,842,624), - // making it unsuitable for this use case. To address this, we use a - // fixed gas limit of 100,000,000, which is sufficient - // for all solution types, while avoiding the "insufficient funds for gas * price + - // value" error that could occur when a large amount of ETH is - // needed to simulate the transaction, due to high transaction gas limit. - // - // If a new network is added, ensure its block gas limit is checked and handled - // appropriately to maintain compatibility with this logic. - Chain::ArbitrumOne => 100_000_000.into(), - Chain::Mainnet => self.block_gas_limit().0, - Chain::Goerli => self.block_gas_limit().0, - Chain::Gnosis => self.block_gas_limit().0, - Chain::Sepolia => self.block_gas_limit().0, - Chain::Base => self.block_gas_limit().0, - Chain::Bnb => self.block_gas_limit().0, - Chain::Optimism => self.block_gas_limit().0, - Chain::Avalanche => self.block_gas_limit().0, - Chain::Polygon => self.block_gas_limit().0, - Chain::Lens => self.block_gas_limit().0, - Chain::Hardhat => self.block_gas_limit().0, - }) - }); + tx.gas = Some(self.inner.tx_gas_limit); tx.gas_price = self.simulation_gas_price().await; let json = self diff --git a/crates/driver/src/infra/cli.rs b/crates/driver/src/infra/cli.rs index 577b5db410..9f03d846b7 100644 --- a/crates/driver/src/infra/cli.rs +++ b/crates/driver/src/infra/cli.rs @@ -51,5 +51,5 @@ pub struct Args { /// Transaction gas limit /// If not specified, uses current block's gas limit #[clap(long, env)] - pub tx_gas_limit: Option, + pub tx_gas_limit: U256, } diff --git a/crates/driver/src/tests/setup/solver.rs b/crates/driver/src/tests/setup/solver.rs index 23bdf236d7..ce715550ab 100644 --- a/crates/driver/src/tests/setup/solver.rs +++ b/crates/driver/src/tests/setup/solver.rs @@ -484,7 +484,7 @@ impl Solver { }, gas, None, - None + 45_000_000.into(), ) .await; From 70e54aa228fcf9303d5c9ae7ecc10e486ba2c748 Mon Sep 17 00:00:00 2001 From: Marcin Szymczak Date: Thu, 16 Oct 2025 15:57:19 +0100 Subject: [PATCH 3/5] Move tx gas limit to driver config --- crates/driver/src/infra/cli.rs | 6 ------ crates/driver/src/infra/config/file/load.rs | 1 + crates/driver/src/infra/config/file/mod.rs | 2 ++ crates/driver/src/infra/config/mod.rs | 1 + crates/driver/src/run.rs | 6 +++--- 5 files changed, 7 insertions(+), 9 deletions(-) diff --git a/crates/driver/src/infra/cli.rs b/crates/driver/src/infra/cli.rs index 9f03d846b7..79ad6f190d 100644 --- a/crates/driver/src/infra/cli.rs +++ b/crates/driver/src/infra/cli.rs @@ -1,5 +1,4 @@ use { - ethcontract::U256, reqwest::Url, shared::arguments::TracingArguments, std::{net::SocketAddr, path::PathBuf}, @@ -47,9 +46,4 @@ pub struct Args { /// https://github.com/cowprotocol/services/blob/main/crates/driver/example.toml. #[clap(long, env)] pub config: PathBuf, - - /// Transaction gas limit - /// If not specified, uses current block's gas limit - #[clap(long, env)] - pub tx_gas_limit: U256, } diff --git a/crates/driver/src/infra/config/file/load.rs b/crates/driver/src/infra/config/file/load.rs index 840a1c7d20..ffea52ec69 100644 --- a/crates/driver/src/infra/config/file/load.rs +++ b/crates/driver/src/infra/config/file/load.rs @@ -407,5 +407,6 @@ pub async fn load(chain: Chain, path: &Path) -> infra::Config { archive_node_url: config.archive_node_url, simulation_bad_token_max_age: config.simulation_bad_token_max_age, app_data_fetching: config.app_data_fetching, + tx_gas_limit: config.tx_gas_limit, } } diff --git a/crates/driver/src/infra/config/file/mod.rs b/crates/driver/src/infra/config/file/mod.rs index 913c701d0d..42cdc6c496 100644 --- a/crates/driver/src/infra/config/file/mod.rs +++ b/crates/driver/src/infra/config/file/mod.rs @@ -85,6 +85,8 @@ struct Config { /// Whether the flashloans feature is enabled. #[serde(default)] flashloans_enabled: bool, + + tx_gas_limit: eth::U256, } #[serde_as] diff --git a/crates/driver/src/infra/config/mod.rs b/crates/driver/src/infra/config/mod.rs index c71623f995..389857b6f5 100644 --- a/crates/driver/src/infra/config/mod.rs +++ b/crates/driver/src/infra/config/mod.rs @@ -33,4 +33,5 @@ pub struct Config { pub archive_node_url: Option, pub simulation_bad_token_max_age: Duration, pub app_data_fetching: AppDataFetching, + pub tx_gas_limit: eth::U256, } diff --git a/crates/driver/src/run.rs b/crates/driver/src/run.rs index 362df7fa50..ed7f8b7083 100644 --- a/crates/driver/src/run.rs +++ b/crates/driver/src/run.rs @@ -63,7 +63,7 @@ async fn run_with(args: cli::Args, addr_sender: Option blockchain::Rpc { .expect("connect ethereum RPC") } -async fn ethereum(args: &cli::Args, config: &infra::Config, ethrpc: blockchain::Rpc) -> Ethereum { +async fn ethereum(config: &infra::Config, ethrpc: blockchain::Rpc) -> Ethereum { let gas = Arc::new( blockchain::GasPriceEstimator::new(ethrpc.web3(), &config.gas_estimator, &config.mempools) .await @@ -171,7 +171,7 @@ async fn ethereum(args: &cli::Args, config: &infra::Config, ethrpc: blockchain:: config.contracts.clone(), gas, config.archive_node_url.as_ref(), - args.tx_gas_limit, + config.tx_gas_limit, ) .await } From 9d7a7281d76151ba01eea51bcf4109ce12d5f012 Mon Sep 17 00:00:00 2001 From: Marcin Szymczak Date: Fri, 17 Oct 2025 10:01:33 +0100 Subject: [PATCH 4/5] Fix gas limit parsing --- configs/local/driver.toml | 2 ++ crates/driver/src/infra/config/file/mod.rs | 2 ++ crates/e2e/src/setup/colocation.rs | 1 + 3 files changed, 5 insertions(+) diff --git a/configs/local/driver.toml b/configs/local/driver.toml index 3f58c4f155..5309fd6c10 100644 --- a/configs/local/driver.toml +++ b/configs/local/driver.toml @@ -1,3 +1,5 @@ +tx-gas-limit = 45000000 + [[solver]] name = "baseline" # Arbitrary name given to this solver, must be unique endpoint = "http://baseline" diff --git a/crates/driver/src/infra/config/file/mod.rs b/crates/driver/src/infra/config/file/mod.rs index 42cdc6c496..27898f578e 100644 --- a/crates/driver/src/infra/config/file/mod.rs +++ b/crates/driver/src/infra/config/file/mod.rs @@ -2,6 +2,7 @@ pub use load::load; use { crate::{domain::eth, infra, util::serialize}, alloy::primitives::Address, + number::serialization::HexOrDecimalU256, reqwest::Url, serde::{Deserialize, Deserializer, Serialize}, serde_with::serde_as, @@ -86,6 +87,7 @@ struct Config { #[serde(default)] flashloans_enabled: bool, + #[serde_as(as = "HexOrDecimalU256")] tx_gas_limit: eth::U256, } diff --git a/crates/e2e/src/setup/colocation.rs b/crates/e2e/src/setup/colocation.rs index a7425f0973..d705bbbc71 100644 --- a/crates/e2e/src/setup/colocation.rs +++ b/crates/e2e/src/setup/colocation.rs @@ -200,6 +200,7 @@ factory = "{:?}" app-data-fetching-enabled = true orderbook-url = "http://localhost:8080" flashloans-enabled = true +tx-gas-limit = "45000000" [gas-estimator] estimator = "web3" From 0baa33805f01bb3fa3c36ef578665126d3daabf3 Mon Sep 17 00:00:00 2001 From: Marcin Szymczak Date: Fri, 17 Oct 2025 11:24:35 +0100 Subject: [PATCH 5/5] Fix driver tests --- crates/driver/example.toml | 1 + crates/driver/src/tests/setup/driver.rs | 1 + 2 files changed, 2 insertions(+) diff --git a/crates/driver/example.toml b/crates/driver/example.toml index 06a294a813..41573385ea 100644 --- a/crates/driver/example.toml +++ b/crates/driver/example.toml @@ -1,3 +1,4 @@ +tx-gas-limit = "45000000" [[solver]] name = "mysolver" # Arbitrary name given to this solver, must be unique endpoint = "http://0.0.0.0:7872" diff --git a/crates/driver/src/tests/setup/driver.rs b/crates/driver/src/tests/setup/driver.rs index 94ba6729cc..f40b55c7da 100644 --- a/crates/driver/src/tests/setup/driver.rs +++ b/crates/driver/src/tests/setup/driver.rs @@ -221,6 +221,7 @@ async fn create_config_file( ) .unwrap(); writeln!(file, "flashloans-enabled = true").unwrap(); + writeln!(file, "tx-gas-limit = \"45000000\"").unwrap(); write!( file, r#"[contracts]