From 07ecc777ccd63985f12b6f88de778d3257a5205a Mon Sep 17 00:00:00 2001 From: dancoombs Date: Tue, 8 Oct 2024 14:08:14 -0500 Subject: [PATCH] feat: prep for da oracle with cache - Move da oracle to its own module - Use trait object instead of enum for da oracle - Require a BlockHashOrNumber in calc_da_gas call, for use as future cache key - Clean up some usage of block_hash in codebase --- crates/builder/src/bundle_proposer.rs | 8 +- crates/builder/src/task.rs | 4 +- crates/pool/src/mempool/paymaster.rs | 2 +- crates/pool/src/mempool/uo_pool.rs | 59 ++++++++++++--- crates/pool/src/task.rs | 5 +- .../src/alloy/{entry_point => da}/arbitrum.rs | 52 +++++++++---- crates/provider/src/alloy/da/mod.rs | 73 +++++++++++++++++++ crates/provider/src/alloy/da/optimism.rs | 72 ++++++++++++++++++ crates/provider/src/alloy/entry_point/mod.rs | 48 ------------ .../src/alloy/entry_point/optimism.rs | 47 ------------ crates/provider/src/alloy/entry_point/v0_6.rs | 49 +++++++------ crates/provider/src/alloy/entry_point/v0_7.rs | 49 +++++++------ crates/provider/src/alloy/mod.rs | 1 + crates/provider/src/lib.rs | 4 +- crates/provider/src/traits/entry_point.rs | 8 +- crates/provider/src/traits/test_utils.rs | 8 +- crates/sim/src/estimation/v0_6.rs | 10 ++- crates/sim/src/estimation/v0_7.rs | 4 +- crates/sim/src/gas/gas.rs | 8 +- crates/sim/src/precheck.rs | 30 ++++++-- crates/sim/src/simulation/mod.rs | 6 +- crates/sim/src/simulation/simulator.rs | 18 +---- crates/sim/src/simulation/unsafe_sim.rs | 32 ++------ 23 files changed, 349 insertions(+), 248 deletions(-) rename crates/provider/src/alloy/{entry_point => da}/arbitrum.rs (55%) create mode 100644 crates/provider/src/alloy/da/mod.rs create mode 100644 crates/provider/src/alloy/da/optimism.rs delete mode 100644 crates/provider/src/alloy/entry_point/optimism.rs diff --git a/crates/builder/src/bundle_proposer.rs b/crates/builder/src/bundle_proposer.rs index 71c243437..7f834c4bc 100644 --- a/crates/builder/src/bundle_proposer.rs +++ b/crates/builder/src/bundle_proposer.rs @@ -217,7 +217,7 @@ where // (1) Filter out ops that don't pay enough to be included let fee_futs = ops .into_iter() - .map(|op| self.check_fees(op, base_fee, required_op_fees)) + .map(|op| self.check_fees(op, block_hash, base_fee, required_op_fees)) .collect::>(); let ops = future::join_all(fee_futs) .await @@ -345,6 +345,7 @@ where async fn check_fees( &self, op: PoolOperation, + block_hash: B256, base_fee: u128, required_op_fees: GasFees, ) -> Option { @@ -373,6 +374,7 @@ where &self.settings.chain_spec, &self.entry_point, op.uo.as_ref(), + block_hash.into(), base_fee, ) .await @@ -428,7 +430,7 @@ where .simulator .simulate_validation( op.uo.clone().into(), - Some(block_hash), + block_hash, Some(op.expected_code_hash), ) .await; @@ -2293,7 +2295,7 @@ mod tests { simulator .expect_simulate_validation() .withf(move |_, &block_hash, &code_hash| { - block_hash == Some(current_block_hash) && code_hash == Some(expected_code_hash) + block_hash == current_block_hash && code_hash == Some(expected_code_hash) }) .returning(move |op, _, _| simulations_by_op[&op.hash(entry_point_address, 0)]()); let mut entry_point = MockEntryPointV0_6::new(); diff --git a/crates/builder/src/task.rs b/crates/builder/src/task.rs index 8a2b94b83..5181e6b0b 100644 --- a/crates/builder/src/task.rs +++ b/crates/builder/src/task.rs @@ -225,7 +225,7 @@ where i + ep.bundle_builder_index_offset, self.provider.clone(), ep_v0_6.clone(), - UnsafeSimulator::new(self.provider.clone(), ep_v0_6.clone()), + UnsafeSimulator::new(ep_v0_6.clone()), pk_iter, ) .await? @@ -273,7 +273,7 @@ where i + ep.bundle_builder_index_offset, self.provider.clone(), ep_v0_7.clone(), - UnsafeSimulator::new(self.provider.clone(), ep_v0_7.clone()), + UnsafeSimulator::new(ep_v0_7.clone()), pk_iter, ) .await? diff --git a/crates/pool/src/mempool/paymaster.rs b/crates/pool/src/mempool/paymaster.rs index e03df586f..bf1c0e283 100644 --- a/crates/pool/src/mempool/paymaster.rs +++ b/crates/pool/src/mempool/paymaster.rs @@ -526,9 +526,9 @@ mod tests { valid_time_range: ValidTimeRange::all_time(), expected_code_hash: B256::random(), sim_block_hash: B256::random(), + sim_block_number: 0, account_is_staked: true, entity_infos: EntityInfos::default(), - sim_block_number: 0, } } diff --git a/crates/pool/src/mempool/uo_pool.rs b/crates/pool/src/mempool/uo_pool.rs index b797e241f..1f9e581aa 100644 --- a/crates/pool/src/mempool/uo_pool.rs +++ b/crates/pool/src/mempool/uo_pool.rs @@ -16,7 +16,7 @@ use std::{collections::HashSet, marker::PhantomData, sync::Arc}; use alloy_primitives::{utils::format_units, Address, B256, U256}; use itertools::Itertools; use parking_lot::RwLock; -use rundler_provider::EntryPoint; +use rundler_provider::{EntryPoint, EvmProvider}; use rundler_sim::{Prechecker, Simulator}; use rundler_types::{ pool::{ @@ -44,12 +44,19 @@ use crate::{ /// Wrapper around a pool object that implements thread-safety /// via a RwLock. Safe to call from multiple threads. Methods /// block on write locks. -pub(crate) struct UoPool { +pub(crate) struct UoPool< + UO: UserOperation, + EP: EvmProvider, + P: Prechecker, + S: Simulator, + E: EntryPoint, +> { config: PoolConfig, state: RwLock, paymaster: PaymasterTracker, reputation: Arc, event_sender: broadcast::Sender>, + provider: EP, prechecker: P, simulator: S, _uo_type: PhantomData, @@ -59,13 +66,15 @@ struct UoPoolState { pool: PoolInner, throttled_ops: HashSet, block_number: u64, + block_hash: B256, gas_fees: GasFees, base_fee: u128, } -impl UoPool +impl UoPool where UO: UserOperation, + EP: EvmProvider, P: Prechecker, S: Simulator, E: EntryPoint, @@ -73,6 +82,7 @@ where pub(crate) fn new( config: PoolConfig, event_sender: broadcast::Sender>, + provider: EP, prechecker: P, simulator: S, paymaster: PaymasterTracker, @@ -83,12 +93,14 @@ where pool: PoolInner::new(config.clone().into()), throttled_ops: HashSet::new(), block_number: 0, + block_hash: B256::ZERO, gas_fees: GasFees::default(), base_fee: 0, }), reputation, paymaster, event_sender, + provider, prechecker, simulator, config, @@ -137,9 +149,10 @@ where } #[async_trait] -impl Mempool for UoPool +impl Mempool for UoPool where UO: UserOperation + From + Into, + EP: EvmProvider, P: Prechecker, S: Simulator, E: EntryPoint, @@ -342,6 +355,7 @@ where { let mut state = self.state.write(); state.block_number = update.latest_block_number; + state.block_hash = update.latest_block_hash; state.gas_fees = bundle_fees; state.base_fee = base_fee; } @@ -419,6 +433,18 @@ where ); } + // NOTE: We get the latest block from the provider here to avoid a race condition + // where the pool is still processing the previous block, but the user may have been + // notified of a new block. + // + // This doesn't clear all race conditions, as the pool may need to update its state before + // a UO can be valid, i.e. for replacement. + let (block_hash, block_number) = self + .provider + .get_latest_block_hash_and_number() + .await + .map_err(anyhow::Error::from)?; + // Check if op is already known or replacing another, and if so, ensure its fees are high enough // do this before simulation to save resources let replacement = self.state.read().pool.check_replacement(&op)?; @@ -433,12 +459,14 @@ where // Prechecks let versioned_op = op.clone().into(); - self.prechecker.check(&versioned_op).await?; + self.prechecker + .check(&versioned_op, block_hash.into()) + .await?; // Only let ops with successful simulations through let sim_result = self .simulator - .simulate_validation(versioned_op, None, None) + .simulate_validation(versioned_op, block_hash, None) .await?; // No aggregators supported for now @@ -459,8 +487,8 @@ where aggregator: None, valid_time_range, expected_code_hash: sim_result.code_hash, - sim_block_hash: sim_result.block_hash, - sim_block_number: sim_result.block_number.unwrap(), // simulation always returns a block number when called without a specified block_hash + sim_block_hash: block_hash, + sim_block_number: block_number, account_is_staked: sim_result.account_is_staked, entity_infos: sim_result.entity_infos, }; @@ -733,7 +761,7 @@ mod tests { use alloy_primitives::Bytes; use mockall::Sequence; - use rundler_provider::{DepositInfo, MockEntryPointV0_6}; + use rundler_provider::{DepositInfo, MockEntryPointV0_6, MockEvmProvider}; use rundler_sim::{ MockPrechecker, MockSimulator, PrecheckError, PrecheckSettings, SimulationError, SimulationResult, SimulationSettings, ViolationError, @@ -1517,6 +1545,7 @@ mod tests { ops: Vec, ) -> UoPool< UserOperation, + impl EvmProvider, impl Prechecker, impl Simulator, impl EntryPoint, @@ -1530,6 +1559,7 @@ mod tests { entrypoint: MockEntryPointV0_6, ) -> UoPool< UserOperation, + impl EvmProvider, impl Prechecker, impl Simulator, impl EntryPoint, @@ -1555,6 +1585,11 @@ mod tests { drop_min_num_blocks: 10, }; + let mut provider = MockEvmProvider::new(); + provider + .expect_get_latest_block_hash_and_number() + .returning(|| Ok((B256::ZERO, 0))); + let mut simulator = MockSimulator::new(); let mut prechecker = MockPrechecker::new(); @@ -1585,7 +1620,7 @@ mod tests { }); for op in ops { - prechecker.expect_check().returning(move |_| { + prechecker.expect_check().returning(move |_, _| { if let Some(error) = &op.precheck_error { Err(PrecheckError::Violations(vec![error.clone()])) } else { @@ -1603,7 +1638,6 @@ mod tests { } else { Ok(SimulationResult { account_is_staked: op.staked, - block_number: Some(0), valid_time_range: op.valid_time_range, entity_infos: EntityInfos { sender: EntityInfo { @@ -1623,6 +1657,7 @@ mod tests { UoPool::new( args, event_sender, + provider, prechecker, simulator, paymaster, @@ -1636,6 +1671,7 @@ mod tests { ) -> ( UoPool< UserOperation, + impl EvmProvider, impl Prechecker, impl Simulator, impl EntryPoint, @@ -1655,6 +1691,7 @@ mod tests { ) -> ( UoPool< UserOperation, + impl EvmProvider, impl Prechecker, impl Simulator, impl EntryPoint, diff --git a/crates/pool/src/task.rs b/crates/pool/src/task.rs index a70494107..67324ef87 100644 --- a/crates/pool/src/task.rs +++ b/crates/pool/src/task.rs @@ -208,7 +208,7 @@ where .context("entry point v0.6 not supplied")?; if unsafe_mode { - let simulator = UnsafeSimulator::new(self.provider.clone(), ep.clone()); + let simulator = UnsafeSimulator::new(ep.clone()); Self::create_mempool( task_spawner, chain_spec, @@ -251,7 +251,7 @@ where .context("entry point v0.7 not supplied")?; if unsafe_mode { - let simulator = UnsafeSimulator::new(self.provider.clone(), ep.clone()); + let simulator = UnsafeSimulator::new(ep.clone()); Self::create_mempool( task_spawner, chain_spec, @@ -340,6 +340,7 @@ where let uo_pool = UoPool::new( pool_config.clone(), event_sender, + provider, prechecker, simulator, paymaster, diff --git a/crates/provider/src/alloy/entry_point/arbitrum.rs b/crates/provider/src/alloy/da/arbitrum.rs similarity index 55% rename from crates/provider/src/alloy/entry_point/arbitrum.rs rename to crates/provider/src/alloy/da/arbitrum.rs index 52e63256e..637109b1e 100644 --- a/crates/provider/src/alloy/entry_point/arbitrum.rs +++ b/crates/provider/src/alloy/da/arbitrum.rs @@ -15,8 +15,10 @@ use alloy_primitives::{Address, Bytes}; use alloy_provider::Provider as AlloyProvider; use alloy_sol_types::sol; use alloy_transport::Transport; +use NodeInterface::NodeInterfaceInstance; -use crate::ProviderResult; +use super::DAGasOracle; +use crate::{BlockHashOrNumber, ProviderResult}; // From https://github.com/OffchainLabs/nitro-contracts/blob/fbbcef09c95f69decabaced3da683f987902f3e2/src/node-interface/NodeInterface.sol#L112 sol! { @@ -37,19 +39,41 @@ sol! { } } -pub(crate) async fn estimate_da_gas, T: Transport + Clone>( - provider: AP, - oracle_address: Address, - to_address: Address, - data: Bytes, -) -> ProviderResult { - let inst = NodeInterface::NodeInterfaceInstance::new(oracle_address, provider); +pub(super) struct ArbitrumNitroDAGasOracle { + node_interface: NodeInterfaceInstance, +} - // assume contract creation - let ret = inst - .gasEstimateL1Component(to_address, true, data) - .call() - .await?; +impl ArbitrumNitroDAGasOracle +where + AP: AlloyProvider, + T: Transport + Clone, +{ + pub(crate) fn new(oracle_address: Address, provider: AP) -> Self { + Self { + node_interface: NodeInterfaceInstance::new(oracle_address, provider), + } + } +} - Ok(ret.gasEstimateForL1 as u128) +#[async_trait::async_trait] +impl DAGasOracle for ArbitrumNitroDAGasOracle +where + AP: AlloyProvider, + T: Transport + Clone, +{ + async fn estimate_da_gas( + &self, + to_address: Address, + data: Bytes, + block: BlockHashOrNumber, + _gas_price: u128, + ) -> ProviderResult { + let ret = self + .node_interface + .gasEstimateL1Component(to_address, true, data) + .block(block.into()) + .call() + .await?; + Ok(ret.gasEstimateForL1 as u128) + } } diff --git a/crates/provider/src/alloy/da/mod.rs b/crates/provider/src/alloy/da/mod.rs new file mode 100644 index 000000000..115a45ff3 --- /dev/null +++ b/crates/provider/src/alloy/da/mod.rs @@ -0,0 +1,73 @@ +// This file is part of Rundler. +// +// Rundler is free software: you can redistribute it and/or modify it under the +// terms of the GNU Lesser General Public License as published by the Free Software +// Foundation, either version 3 of the License, or (at your option) any later version. +// +// Rundler is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// See the GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along with Rundler. +// If not, see https://www.gnu.org/licenses/. + +use alloy_primitives::{Address, Bytes}; +use alloy_provider::Provider as AlloyProvider; +use alloy_transport::Transport; +use rundler_types::chain::{ChainSpec, DAGasOracleContractType}; + +use crate::{BlockHashOrNumber, ProviderResult}; + +mod arbitrum; +use arbitrum::ArbitrumNitroDAGasOracle; +mod optimism; +use optimism::OptimismBedrockDAGasOracle; + +/// Trait for a DA gas oracle +#[async_trait::async_trait] +#[auto_impl::auto_impl(&, &mut, Rc, Arc, Box)] +pub(crate) trait DAGasOracle: Send + Sync { + async fn estimate_da_gas( + &self, + to: Address, + data: Bytes, + block: BlockHashOrNumber, + gas_price: u128, + ) -> ProviderResult; +} + +struct ZeroDAGasOracle; + +#[async_trait::async_trait] +impl DAGasOracle for ZeroDAGasOracle { + async fn estimate_da_gas( + &self, + _to: Address, + _data: Bytes, + _block: BlockHashOrNumber, + _gas_price: u128, + ) -> ProviderResult { + Ok(0) + } +} + +pub(crate) fn da_gas_oracle_for_chain<'a, AP, T>( + chain_spec: &ChainSpec, + provider: AP, +) -> Box +where + AP: AlloyProvider + 'a, + T: Transport + Clone, +{ + match chain_spec.da_gas_oracle_contract_type { + DAGasOracleContractType::ArbitrumNitro => Box::new(ArbitrumNitroDAGasOracle::new( + chain_spec.da_gas_oracle_contract_address, + provider, + )), + DAGasOracleContractType::OptimismBedrock => Box::new(OptimismBedrockDAGasOracle::new( + chain_spec.da_gas_oracle_contract_address, + provider, + )), + DAGasOracleContractType::None => Box::new(ZeroDAGasOracle), + } +} diff --git a/crates/provider/src/alloy/da/optimism.rs b/crates/provider/src/alloy/da/optimism.rs new file mode 100644 index 000000000..ed8758ee9 --- /dev/null +++ b/crates/provider/src/alloy/da/optimism.rs @@ -0,0 +1,72 @@ +// This file is part of Rundler. +// +// Rundler is free software: you can redistribute it and/or modify it under the +// terms of the GNU Lesser General Public License as published by the Free Software +// Foundation, either version 3 of the License, or (at your option) any later version. +// +// Rundler is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// See the GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along with Rundler. +// If not, see https://www.gnu.org/licenses/. + +use alloy_primitives::{Address, Bytes}; +use alloy_provider::Provider as AlloyProvider; +use alloy_sol_types::sol; +use alloy_transport::Transport; +use anyhow::Context; +use GasPriceOracle::GasPriceOracleInstance; + +use super::DAGasOracle; +use crate::{BlockHashOrNumber, ProviderResult}; + +// From https://github.com/ethereum-optimism/optimism/blob/f93f9f40adcd448168c6ea27820aeee5da65fcbd/packages/contracts-bedrock/src/L2/GasPriceOracle.sol#L54 +sol! { + #[sol(rpc)] + interface GasPriceOracle { + function getL1Fee(bytes memory _data) external view returns (uint256); + } +} + +pub(super) struct OptimismBedrockDAGasOracle { + oracle: GasPriceOracleInstance, +} + +impl OptimismBedrockDAGasOracle +where + AP: AlloyProvider, + T: Transport + Clone, +{ + pub(crate) fn new(oracle_address: Address, provider: AP) -> Self { + let oracle = GasPriceOracleInstance::new(oracle_address, provider); + Self { oracle } + } +} + +#[async_trait::async_trait] +impl DAGasOracle for OptimismBedrockDAGasOracle +where + AP: AlloyProvider, + T: Transport + Clone, +{ + async fn estimate_da_gas( + &self, + _to: Address, + data: Bytes, + block: BlockHashOrNumber, + gas_price: u128, + ) -> ProviderResult { + let l1_fee: u128 = self + .oracle + .getL1Fee(data) + .block(block.into()) + .call() + .await? + ._0 + .try_into() + .context("failed to convert DA fee to u128")?; + + Ok(l1_fee.checked_div(gas_price).unwrap_or(u128::MAX)) + } +} diff --git a/crates/provider/src/alloy/entry_point/mod.rs b/crates/provider/src/alloy/entry_point/mod.rs index 6f7c43527..19f9c8c9a 100644 --- a/crates/provider/src/alloy/entry_point/mod.rs +++ b/crates/provider/src/alloy/entry_point/mod.rs @@ -13,60 +13,12 @@ use alloy_consensus::{transaction::SignableTransaction, TxEnvelope, TypedTransaction}; use alloy_primitives::{address, Address, Bytes, Parity, Signature, U256}; -use alloy_provider::Provider as AlloyProvider; use alloy_rlp::Encodable; use alloy_rpc_types_eth::TransactionRequest; -use alloy_transport::Transport; -use rundler_types::chain::{ChainSpec, DAGasOracleContractType}; - -use crate::ProviderResult; pub(crate) mod v0_6; pub(crate) mod v0_7; -mod arbitrum; -mod optimism; - -#[derive(Debug, Default, Clone)] -enum DAGasOracle { - ArbitrumNitro(Address), - OptimismBedrock(Address), - #[default] - None, -} - -impl DAGasOracle { - fn new(chain_spec: &ChainSpec) -> DAGasOracle { - match chain_spec.da_gas_oracle_contract_type { - DAGasOracleContractType::ArbitrumNitro => { - DAGasOracle::ArbitrumNitro(chain_spec.da_gas_oracle_contract_address) - } - DAGasOracleContractType::OptimismBedrock => { - DAGasOracle::OptimismBedrock(chain_spec.da_gas_oracle_contract_address) - } - DAGasOracleContractType::None => DAGasOracle::None, - } - } - - async fn estimate_da_gas, T: Transport + Clone>( - &self, - provider: AP, - to_address: Address, - data: Bytes, - gas_price: u128, - ) -> ProviderResult { - match self { - DAGasOracle::ArbitrumNitro(oracle_address) => { - arbitrum::estimate_da_gas(provider, *oracle_address, to_address, data).await - } - DAGasOracle::OptimismBedrock(oracle_address) => { - optimism::estimate_da_gas(provider, *oracle_address, data, gas_price).await - } - DAGasOracle::None => Ok(0), - } - } -} - fn max_bundle_transaction_data(to_address: Address, data: Bytes, gas_price: u128) -> Bytes { // Fill in max values for unknown or varying fields let gas_price_ceil = gas_price.next_power_of_two() - 1; // max out bits of gas price, assume same power of 2 diff --git a/crates/provider/src/alloy/entry_point/optimism.rs b/crates/provider/src/alloy/entry_point/optimism.rs deleted file mode 100644 index 2594380f7..000000000 --- a/crates/provider/src/alloy/entry_point/optimism.rs +++ /dev/null @@ -1,47 +0,0 @@ -// This file is part of Rundler. -// -// Rundler is free software: you can redistribute it and/or modify it under the -// terms of the GNU Lesser General Public License as published by the Free Software -// Foundation, either version 3 of the License, or (at your option) any later version. -// -// Rundler is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; -// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -// See the GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License along with Rundler. -// If not, see https://www.gnu.org/licenses/. - -use alloy_primitives::{Address, Bytes}; -use alloy_provider::Provider as AlloyProvider; -use alloy_sol_types::sol; -use alloy_transport::Transport; -use anyhow::Context; - -use crate::ProviderResult; - -// From https://github.com/ethereum-optimism/optimism/blob/f93f9f40adcd448168c6ea27820aeee5da65fcbd/packages/contracts-bedrock/src/L2/GasPriceOracle.sol#L54 -sol! { - #[sol(rpc)] - interface GasPriceOracle { - function getL1Fee(bytes memory _data) external view returns (uint256); - } -} - -pub(crate) async fn estimate_da_gas, T: Transport + Clone>( - provider: AP, - oracle_address: Address, - data: Bytes, - gas_price: u128, -) -> ProviderResult { - let oracle = GasPriceOracle::GasPriceOracleInstance::new(oracle_address, provider); - - let l1_fee: u128 = oracle - .getL1Fee(data) - .call() - .await? - ._0 - .try_into() - .context("failed to convert DA fee to u128")?; - - Ok(l1_fee.checked_div(gas_price).unwrap_or(u128::MAX)) -} diff --git a/crates/provider/src/alloy/entry_point/v0_6.rs b/crates/provider/src/alloy/entry_point/v0_6.rs index 6e34a3742..3317128ff 100644 --- a/crates/provider/src/alloy/entry_point/v0_6.rs +++ b/crates/provider/src/alloy/entry_point/v0_6.rs @@ -11,6 +11,8 @@ // You should have received a copy of the GNU General Public License along with Rundler. // If not, see https://www.gnu.org/licenses/. +use std::sync::Arc; + use alloy_contract::Error as ContractError; use alloy_primitives::{Address, Bytes, U256}; use alloy_provider::Provider as AlloyProvider; @@ -33,28 +35,28 @@ use rundler_types::{ ValidationOutput, ValidationRevert, }; -use super::DAGasOracle; use crate::{ - AggregatorOut, AggregatorSimOut, BundleHandler, DAGasProvider, DepositInfo, EntryPoint, - EntryPointProvider as EntryPointProviderTrait, EvmCall, ExecutionResult, HandleOpsOut, - ProviderResult, SignatureAggregator, SimulationProvider, + alloy::da::{self, DAGasOracle}, + AggregatorOut, AggregatorSimOut, BlockHashOrNumber, BundleHandler, DAGasProvider, DepositInfo, + EntryPoint, EntryPointProvider as EntryPointProviderTrait, EvmCall, ExecutionResult, + HandleOpsOut, ProviderResult, SignatureAggregator, SimulationProvider, }; /// Entry point provider for v0.6 #[derive(Clone)] -pub struct EntryPointProvider { +pub struct EntryPointProvider<'a, AP, T> { i_entry_point: IEntryPointInstance, - da_gas_oracle: DAGasOracle, + da_gas_oracle: Arc, max_verification_gas: u64, max_simulate_handle_op_gas: u64, max_aggregation_gas: u64, chain_spec: ChainSpec, } -impl EntryPointProvider +impl<'a, AP, T> EntryPointProvider<'a, AP, T> where T: Transport + Clone, - AP: AlloyProvider, + AP: AlloyProvider + Clone + 'a, { /// Create a new `EntryPoint` instance for v0.6 pub fn new( @@ -65,8 +67,11 @@ where provider: AP, ) -> Self { Self { - i_entry_point: IEntryPointInstance::new(chain_spec.entry_point_address_v0_6, provider), - da_gas_oracle: DAGasOracle::new(&chain_spec), + i_entry_point: IEntryPointInstance::new( + chain_spec.entry_point_address_v0_6, + provider.clone(), + ), + da_gas_oracle: Arc::from(da::da_gas_oracle_for_chain(&chain_spec, provider)), max_verification_gas, max_simulate_handle_op_gas, max_aggregation_gas, @@ -76,7 +81,7 @@ where } #[async_trait::async_trait] -impl EntryPoint for EntryPointProvider +impl<'a, AP, T> EntryPoint for EntryPointProvider<'a, AP, T> where T: Transport + Clone, AP: AlloyProvider, @@ -132,7 +137,7 @@ where } #[async_trait::async_trait] -impl SignatureAggregator for EntryPointProvider +impl<'a, AP, T> SignatureAggregator for EntryPointProvider<'a, AP, T> where T: Transport + Clone, AP: AlloyProvider, @@ -210,7 +215,7 @@ where } #[async_trait::async_trait] -impl BundleHandler for EntryPointProvider +impl<'a, AP, T> BundleHandler for EntryPointProvider<'a, AP, T> where T: Transport + Clone, AP: AlloyProvider, @@ -300,7 +305,7 @@ where } #[async_trait::async_trait] -impl DAGasProvider for EntryPointProvider +impl<'a, AP, T> DAGasProvider for EntryPointProvider<'a, AP, T> where T: Transport + Clone, AP: AlloyProvider, @@ -309,8 +314,8 @@ where async fn calc_da_gas( &self, - entry_point_address: Address, user_op: UserOperation, + block: BlockHashOrNumber, gas_price: u128, ) -> ProviderResult { let data = self @@ -321,21 +326,17 @@ where .into_input() .unwrap(); - let bundle_data = super::max_bundle_transaction_data(entry_point_address, data, gas_price); + let bundle_data = + super::max_bundle_transaction_data(*self.i_entry_point.address(), data, gas_price); self.da_gas_oracle - .estimate_da_gas( - self.i_entry_point.provider(), - entry_point_address, - bundle_data, - gas_price, - ) + .estimate_da_gas(*self.i_entry_point.address(), bundle_data, block, gas_price) .await } } #[async_trait::async_trait] -impl SimulationProvider for EntryPointProvider +impl<'a, AP, T> SimulationProvider for EntryPointProvider<'a, AP, T> where T: Transport + Clone, AP: AlloyProvider, @@ -498,7 +499,7 @@ where } } -impl EntryPointProviderTrait for EntryPointProvider +impl<'a, AP, T> EntryPointProviderTrait for EntryPointProvider<'a, AP, T> where T: Transport + Clone, AP: AlloyProvider, diff --git a/crates/provider/src/alloy/entry_point/v0_7.rs b/crates/provider/src/alloy/entry_point/v0_7.rs index f84c1aa0a..df48c2b10 100644 --- a/crates/provider/src/alloy/entry_point/v0_7.rs +++ b/crates/provider/src/alloy/entry_point/v0_7.rs @@ -11,6 +11,8 @@ // You should have received a copy of the GNU General Public License along with Rundler. // If not, see https://www.gnu.org/licenses/. +use std::sync::Arc; + use alloy_contract::Error as ContractError; use alloy_json_rpc::ErrorPayload; use alloy_primitives::{Address, Bytes, U256}; @@ -40,28 +42,28 @@ use rundler_types::{ ValidationOutput, ValidationRevert, }; -use super::DAGasOracle; use crate::{ - AggregatorOut, AggregatorSimOut, BundleHandler, DAGasProvider, DepositInfo, EntryPoint, - EntryPointProvider as EntryPointProviderTrait, EvmCall, ExecutionResult, HandleOpsOut, - ProviderResult, SignatureAggregator, SimulationProvider, + alloy::da::{self, DAGasOracle}, + AggregatorOut, AggregatorSimOut, BlockHashOrNumber, BundleHandler, DAGasProvider, DepositInfo, + EntryPoint, EntryPointProvider as EntryPointProviderTrait, EvmCall, ExecutionResult, + HandleOpsOut, ProviderResult, SignatureAggregator, SimulationProvider, }; /// Entry point provider for v0.7 #[derive(Clone)] -pub struct EntryPointProvider { +pub struct EntryPointProvider<'a, AP, T> { i_entry_point: IEntryPointInstance, - da_gas_oracle: DAGasOracle, + da_gas_oracle: Arc, max_verification_gas: u64, max_simulate_handle_ops_gas: u64, max_aggregation_gas: u64, chain_spec: ChainSpec, } -impl EntryPointProvider +impl<'a, AP, T> EntryPointProvider<'a, AP, T> where T: Transport + Clone, - AP: AlloyProvider, + AP: AlloyProvider + Clone + 'a, { /// Create a new `EntryPoint` instance for v0.7 pub fn new( @@ -72,8 +74,11 @@ where provider: AP, ) -> Self { Self { - i_entry_point: IEntryPointInstance::new(chain_spec.entry_point_address_v0_7, provider), - da_gas_oracle: DAGasOracle::new(&chain_spec), + i_entry_point: IEntryPointInstance::new( + chain_spec.entry_point_address_v0_7, + provider.clone(), + ), + da_gas_oracle: Arc::from(da::da_gas_oracle_for_chain(&chain_spec, provider)), max_verification_gas, max_simulate_handle_ops_gas, max_aggregation_gas, @@ -83,7 +88,7 @@ where } #[async_trait::async_trait] -impl EntryPoint for EntryPointProvider +impl<'a, AP, T> EntryPoint for EntryPointProvider<'a, AP, T> where T: Transport + Clone, AP: AlloyProvider, @@ -140,7 +145,7 @@ where } #[async_trait::async_trait] -impl SignatureAggregator for EntryPointProvider +impl<'a, AP, T> SignatureAggregator for EntryPointProvider<'a, AP, T> where T: Transport + Clone, AP: AlloyProvider, @@ -220,7 +225,7 @@ where } #[async_trait::async_trait] -impl BundleHandler for EntryPointProvider +impl<'a, AP, T> BundleHandler for EntryPointProvider<'a, AP, T> where T: Transport + Clone, AP: AlloyProvider, @@ -289,7 +294,7 @@ where } #[async_trait::async_trait] -impl DAGasProvider for EntryPointProvider +impl<'a, AP, T> DAGasProvider for EntryPointProvider<'a, AP, T> where T: Transport + Clone, AP: AlloyProvider, @@ -298,8 +303,8 @@ where async fn calc_da_gas( &self, - entry_point_address: Address, user_op: UserOperation, + block: BlockHashOrNumber, gas_price: u128, ) -> ProviderResult { let data = self @@ -310,21 +315,17 @@ where .into_input() .unwrap(); - let bundle_data = super::max_bundle_transaction_data(entry_point_address, data, gas_price); + let bundle_data = + super::max_bundle_transaction_data(*self.i_entry_point.address(), data, gas_price); self.da_gas_oracle - .estimate_da_gas( - self.i_entry_point.provider(), - entry_point_address, - bundle_data, - gas_price, - ) + .estimate_da_gas(*self.i_entry_point.address(), bundle_data, block, gas_price) .await } } #[async_trait::async_trait] -impl SimulationProvider for EntryPointProvider +impl<'a, AP, T> SimulationProvider for EntryPointProvider<'a, AP, T> where T: Transport + Clone, AP: AlloyProvider, @@ -450,7 +451,7 @@ where } } -impl EntryPointProviderTrait for EntryPointProvider +impl<'a, AP, T> EntryPointProviderTrait for EntryPointProvider<'a, AP, T> where T: Transport + Clone, AP: AlloyProvider, diff --git a/crates/provider/src/alloy/mod.rs b/crates/provider/src/alloy/mod.rs index 4f894364a..6709e0905 100644 --- a/crates/provider/src/alloy/mod.rs +++ b/crates/provider/src/alloy/mod.rs @@ -23,6 +23,7 @@ use url::Url; use crate::EvmProvider; +pub(crate) mod da; pub(crate) mod entry_point; pub(crate) mod evm; pub(crate) mod metrics; diff --git a/crates/provider/src/lib.rs b/crates/provider/src/lib.rs index 817b0f703..dc8f52484 100644 --- a/crates/provider/src/lib.rs +++ b/crates/provider/src/lib.rs @@ -39,8 +39,8 @@ mod traits; pub use alloy_json_rpc::{RpcParam, RpcReturn}; pub use alloy_rpc_types_eth::{ state::{AccountOverride, StateOverride}, - Block, BlockId, BlockNumberOrTag, FeeHistory, Filter, FilterBlockOption, Header as BlockHeader, - Log, ReceiptEnvelope as TransactionReceiptEnvelope, + Block, BlockHashOrNumber, BlockId, BlockNumberOrTag, FeeHistory, Filter, FilterBlockOption, + Header as BlockHeader, Log, ReceiptEnvelope as TransactionReceiptEnvelope, ReceiptWithBloom as TransactionReceiptWithBloom, RpcBlockHash, Transaction, TransactionReceipt, TransactionRequest, }; diff --git a/crates/provider/src/traits/entry_point.rs b/crates/provider/src/traits/entry_point.rs index 3a1fbeb91..0538359c0 100644 --- a/crates/provider/src/traits/entry_point.rs +++ b/crates/provider/src/traits/entry_point.rs @@ -16,7 +16,9 @@ use rundler_types::{ GasFees, Timestamp, UserOperation, UserOpsPerAggregator, ValidationOutput, ValidationRevert, }; -use crate::{BlockId, EvmCall, ProviderResult, StateOverride, TransactionRequest}; +use crate::{ + BlockHashOrNumber, BlockId, EvmCall, ProviderResult, StateOverride, TransactionRequest, +}; /// Output of a successful signature aggregator simulation call #[derive(Clone, Debug, Default)] @@ -165,8 +167,8 @@ pub trait DAGasProvider: Send + Sync { /// Returns zero for operations that do not require DA gas async fn calc_da_gas( &self, - entry_point_address: Address, op: Self::UO, + block: BlockHashOrNumber, gas_price: u128, ) -> ProviderResult; } @@ -201,7 +203,7 @@ pub trait SimulationProvider: Send + Sync { op: Self::UO, target: Address, target_call_data: Bytes, - block_hash: BlockId, + block_id: BlockId, state_override: StateOverride, ) -> ProviderResult>; diff --git a/crates/provider/src/traits/test_utils.rs b/crates/provider/src/traits/test_utils.rs index 548973542..17dc55774 100644 --- a/crates/provider/src/traits/test_utils.rs +++ b/crates/provider/src/traits/test_utils.rs @@ -27,8 +27,8 @@ use rundler_types::{ use super::error::ProviderResult; use crate::{ - AggregatorOut, BundleHandler, DAGasProvider, DepositInfo, EntryPoint, EvmCall, - EvmProvider as EvmProviderTrait, ExecutionResult, HandleOpsOut, SignatureAggregator, + AggregatorOut, BlockHashOrNumber, BundleHandler, DAGasProvider, DepositInfo, EntryPoint, + EvmCall, EvmProvider as EvmProviderTrait, ExecutionResult, HandleOpsOut, SignatureAggregator, SimulationProvider, }; @@ -173,8 +173,8 @@ mockall::mock! { type UO = v0_6::UserOperation; async fn calc_da_gas( &self, - entry_point_address: Address, op: v0_6::UserOperation, + block: BlockHashOrNumber, gas_price: u128, ) -> ProviderResult; } @@ -261,8 +261,8 @@ mockall::mock! { type UO = v0_7::UserOperation; async fn calc_da_gas( &self, - entry_point_address: Address, op: v0_7::UserOperation, + block: BlockHashOrNumber, gas_price: u128, ) -> ProviderResult; } diff --git a/crates/sim/src/estimation/v0_6.rs b/crates/sim/src/estimation/v0_6.rs index 19ff3bdd7..8827dbd72 100644 --- a/crates/sim/src/estimation/v0_6.rs +++ b/crates/sim/src/estimation/v0_6.rs @@ -78,7 +78,7 @@ where .await .map_err(anyhow::Error::from)?; - let pre_verification_gas = self.estimate_pre_verification_gas(&op).await?; + let pre_verification_gas = self.estimate_pre_verification_gas(&op, block_hash).await?; let full_op = op .clone() @@ -265,6 +265,7 @@ where async fn estimate_pre_verification_gas( &self, optional_op: &UserOperationOptionalGas, + block_hash: B256, ) -> Result { if let Some(pvg) = optional_op.pre_verification_gas { if pvg != 0 { @@ -293,6 +294,7 @@ where &self.entry_point, &optional_op.max_fill(&self.chain_spec), &optional_op.random_fill(&self.chain_spec), + block_hash.into(), gas_price, ) .await?) @@ -599,7 +601,7 @@ mod tests { let (estimator, _) = create_estimator(entry, provider); let user_op = demo_user_op_optional_gas(None); let estimation = estimator - .estimate_pre_verification_gas(&user_op) + .estimate_pre_verification_gas(&user_op, B256::ZERO) .await .unwrap(); @@ -671,7 +673,7 @@ mod tests { let user_op = demo_user_op_optional_gas(None); let estimation = estimator - .estimate_pre_verification_gas(&user_op) + .estimate_pre_verification_gas(&user_op, B256::ZERO) .await .unwrap(); @@ -739,7 +741,7 @@ mod tests { let user_op = demo_user_op_optional_gas(None); let estimation = estimator - .estimate_pre_verification_gas(&user_op) + .estimate_pre_verification_gas(&user_op, B256::ZERO) .await .unwrap(); diff --git a/crates/sim/src/estimation/v0_7.rs b/crates/sim/src/estimation/v0_7.rs index f66c85294..37803707a 100644 --- a/crates/sim/src/estimation/v0_7.rs +++ b/crates/sim/src/estimation/v0_7.rs @@ -80,7 +80,7 @@ where .await .map_err(anyhow::Error::from)?; - let pre_verification_gas = self.estimate_pre_verification_gas(&op).await?; + let pre_verification_gas = self.estimate_pre_verification_gas(&op, block_hash).await?; let full_op = op .clone() @@ -339,6 +339,7 @@ where async fn estimate_pre_verification_gas( &self, optional_op: &UserOperationOptionalGas, + block_hash: B256, ) -> Result { if let Some(pvg) = optional_op.pre_verification_gas { if pvg != 0 { @@ -367,6 +368,7 @@ where &self.entry_point, &optional_op.max_fill(&self.chain_spec), &optional_op.random_fill(&self.chain_spec), + block_hash.into(), gas_price, ) .await?) diff --git a/crates/sim/src/gas/gas.rs b/crates/sim/src/gas/gas.rs index 6d6b304d2..dbffd795b 100644 --- a/crates/sim/src/gas/gas.rs +++ b/crates/sim/src/gas/gas.rs @@ -16,7 +16,7 @@ use std::{cmp, fmt::Debug}; use anyhow::{bail, Context}; #[cfg(feature = "test-utils")] use mockall::automock; -use rundler_provider::{DAGasProvider, EntryPoint, EvmProvider}; +use rundler_provider::{BlockHashOrNumber, DAGasProvider, EntryPoint, EvmProvider}; use rundler_types::{chain::ChainSpec, GasFees, UserOperation}; use rundler_utils::math; use tokio::try_join; @@ -44,11 +44,12 @@ pub async fn estimate_pre_verification_gas< entry_point: &E, full_op: &UO, random_op: &UO, + block: BlockHashOrNumber, gas_price: u128, ) -> anyhow::Result { let da_gas = if chain_spec.da_pre_verification_gas { entry_point - .calc_da_gas(*entry_point.address(), random_op.clone(), gas_price) + .calc_da_gas(random_op.clone(), block, gas_price) .await? } else { 0 @@ -68,6 +69,7 @@ pub async fn calc_required_pre_verification_gas< chain_spec: &ChainSpec, entry_point: &E, op: &UO, + block: BlockHashOrNumber, base_fee: u128, ) -> anyhow::Result { let da_gas = if chain_spec.da_pre_verification_gas { @@ -81,7 +83,7 @@ pub async fn calc_required_pre_verification_gas< } entry_point - .calc_da_gas(*entry_point.address(), op.clone(), gas_price) + .calc_da_gas(op.clone(), block, gas_price) .await? } else { 0 diff --git a/crates/sim/src/precheck.rs b/crates/sim/src/precheck.rs index acaa7f2d4..bd538f6f2 100644 --- a/crates/sim/src/precheck.rs +++ b/crates/sim/src/precheck.rs @@ -18,7 +18,7 @@ use anyhow::Context; use arrayvec::ArrayVec; #[cfg(feature = "test-utils")] use mockall::automock; -use rundler_provider::{DAGasProvider, EntryPoint, EvmProvider}; +use rundler_provider::{BlockHashOrNumber, DAGasProvider, EntryPoint, EvmProvider}; use rundler_types::{ chain::ChainSpec, pool::{MempoolError, PrecheckViolation}, @@ -43,8 +43,11 @@ pub trait Prechecker: Send + Sync { type UO: UserOperation; /// Run the precheck on the given operation and return an error if it fails. - async fn check(&self, op: &Self::UO) -> Result<(), PrecheckError>; + async fn check(&self, op: &Self::UO, block: BlockHashOrNumber) -> Result<(), PrecheckError>; + /// Update and return the bundle fees. + /// + /// This MUST be called at block boundaries before checking any operations. async fn update_fees(&self) -> anyhow::Result<(GasFees, u128)>; } @@ -143,8 +146,8 @@ where { type UO = UO; - async fn check(&self, op: &Self::UO) -> Result<(), PrecheckError> { - let async_data = self.load_async_data(op).await?; + async fn check(&self, op: &Self::UO, block: BlockHashOrNumber) -> Result<(), PrecheckError> { + let async_data = self.load_async_data(op, block).await?; let mut violations: Vec = vec![]; violations.extend(self.check_init_code(op, async_data)); violations.extend(self.check_gas(op, async_data)); @@ -323,7 +326,11 @@ where None } - async fn load_async_data(&self, op: &UO) -> anyhow::Result { + async fn load_async_data( + &self, + op: &UO, + block: BlockHashOrNumber, + ) -> anyhow::Result { let (_, base_fee) = self.get_fees().await?; let ( @@ -337,7 +344,7 @@ where self.is_contract(Some(op.sender())), self.is_contract(op.paymaster()), self.get_payer_funds(op), - self.get_required_pre_verification_gas(op.clone(), base_fee) + self.get_required_pre_verification_gas(op.clone(), block, base_fee) )?; Ok(AsyncData { factory_exists, @@ -399,10 +406,17 @@ where async fn get_required_pre_verification_gas( &self, op: UO, + block: BlockHashOrNumber, base_fee: u128, ) -> anyhow::Result { - gas::calc_required_pre_verification_gas(&self.chain_spec, &self.entry_point, &op, base_fee) - .await + gas::calc_required_pre_verification_gas( + &self.chain_spec, + &self.entry_point, + &op, + block, + base_fee, + ) + .await } } diff --git a/crates/sim/src/simulation/mod.rs b/crates/sim/src/simulation/mod.rs index 020947beb..7c00c8a51 100644 --- a/crates/sim/src/simulation/mod.rs +++ b/crates/sim/src/simulation/mod.rs @@ -48,10 +48,6 @@ use crate::{ExpectedStorage, ViolationError}; pub struct SimulationResult { /// The mempool IDs that support this operation pub mempools: Vec, - /// Block hash this operation was simulated against - pub block_hash: B256, - /// Block number this operation was simulated against - pub block_number: Option, /// Gas used in the pre-op phase of simulation measured /// by the entry point pub pre_op_gas: u128, @@ -154,7 +150,7 @@ pub trait Simulator: Send + Sync { async fn simulate_validation( &self, op: Self::UO, - block_hash: Option, + block_hash: B256, expected_code_hash: Option, ) -> Result; } diff --git a/crates/sim/src/simulation/simulator.rs b/crates/sim/src/simulation/simulator.rs index 9e7f3a871..5c3a415a8 100644 --- a/crates/sim/src/simulation/simulator.rs +++ b/crates/sim/src/simulation/simulator.rs @@ -465,21 +465,9 @@ where async fn simulate_validation( &self, op: UO, - block_hash: Option, + block_hash: B256, expected_code_hash: Option, ) -> Result { - let (block_hash, block_number) = match block_hash { - // If we are given a block_hash, we return a None block number, avoiding an extra call - Some(block_hash) => (block_hash, None), - None => { - let hash_and_num = self - .provider - .get_latest_block_hash_and_number() - .await - .map_err(anyhow::Error::from)?; - (hash_and_num.0, Some(hash_and_num.1)) - } - }; let block_id = block_hash.into(); let mut context = match self .validation_context_provider @@ -540,8 +528,6 @@ where Ok(SimulationResult { mempools, - block_hash, - block_number, pre_op_gas, valid_time_range: ValidTimeRange::new(valid_after, valid_until), aggregator, @@ -910,7 +896,7 @@ mod tests { let simulator = create_simulator(provider, entry_point, context); let res = simulator - .simulate_validation(user_operation, None, None) + .simulate_validation(user_operation, B256::ZERO, None) .await; assert!(res.is_ok()); } diff --git a/crates/sim/src/simulation/unsafe_sim.rs b/crates/sim/src/simulation/unsafe_sim.rs index 7bff702d0..8209f0969 100644 --- a/crates/sim/src/simulation/unsafe_sim.rs +++ b/crates/sim/src/simulation/unsafe_sim.rs @@ -14,9 +14,7 @@ use std::marker::PhantomData; use alloy_primitives::B256; -use rundler_provider::{ - AggregatorOut, EntryPoint, EvmProvider, SignatureAggregator, SimulationProvider, -}; +use rundler_provider::{AggregatorOut, EntryPoint, SignatureAggregator, SimulationProvider}; use rundler_types::{pool::SimulationViolation, EntityInfos, UserOperation, ValidTimeRange}; use crate::{SimulationError, SimulationResult, Simulator, ViolationError}; @@ -27,17 +25,15 @@ use crate::{SimulationError, SimulationResult, Simulator, ViolationError}; /// /// WARNING: This is "unsafe" for a reason. None of the ERC-7562 checks are /// performed. -pub struct UnsafeSimulator { - provider: P, +pub struct UnsafeSimulator { entry_point: E, _uo_type: PhantomData, } -impl UnsafeSimulator { +impl UnsafeSimulator { /// Creates a new unsafe simulator - pub fn new(provider: P, entry_point: E) -> Self { + pub fn new(entry_point: E) -> Self { Self { - provider, entry_point, _uo_type: PhantomData, } @@ -45,10 +41,9 @@ impl UnsafeSimulator { } #[async_trait::async_trait] -impl Simulator for UnsafeSimulator +impl Simulator for UnsafeSimulator where UO: UserOperation, - P: EvmProvider, E: EntryPoint + SimulationProvider + SignatureAggregator + Clone, { type UO = UO; @@ -59,24 +54,11 @@ where async fn simulate_validation( &self, op: UO, - block_hash: Option, + block_hash: B256, _expected_code_hash: Option, ) -> Result { tracing::info!("Performing unsafe simulation"); - let (block_hash, block_number) = match block_hash { - // If we are given a block_hash, we return a None block number, avoiding an extra call - Some(block_hash) => (block_hash, None), - None => { - let hash_and_num = self - .provider - .get_latest_block_hash_and_number() - .await - .map_err(anyhow::Error::from)?; - (hash_and_num.0, Some(hash_and_num.1)) - } - }; - // simulate the validation let validation_result = self .entry_point @@ -150,8 +132,6 @@ where } else { Ok(SimulationResult { mempools: vec![B256::ZERO], - block_hash, - block_number, pre_op_gas, valid_time_range, requires_post_op,