From e92cf35ac9233e35854adb582f993ffe088031be Mon Sep 17 00:00:00 2001 From: Arsenii Kulikov Date: Wed, 19 Feb 2025 14:08:49 +0400 Subject: [PATCH] refactor: simplify `SystemCaller` API (#14578) --- .../engine/invalid-block-hooks/src/witness.rs | 3 +- crates/engine/util/src/reorg.rs | 10 +- crates/ethereum/evm/src/execute.rs | 4 +- crates/ethereum/payload/src/lib.rs | 12 +- crates/evm/src/system_calls/eip2935.rs | 6 +- crates/evm/src/system_calls/eip4788.rs | 6 +- crates/evm/src/system_calls/mod.rs | 140 +++--------------- crates/optimism/evm/src/execute.rs | 12 +- crates/optimism/payload/src/builder.rs | 74 +++++---- .../rpc-eth-api/src/helpers/pending_block.rs | 16 +- crates/rpc/rpc-eth-api/src/helpers/trace.rs | 15 +- 11 files changed, 81 insertions(+), 217 deletions(-) diff --git a/crates/engine/invalid-block-hooks/src/witness.rs b/crates/engine/invalid-block-hooks/src/witness.rs index 1f4cd1f19..8257baca3 100644 --- a/crates/engine/invalid-block-hooks/src/witness.rs +++ b/crates/engine/invalid-block-hooks/src/witness.rs @@ -78,8 +78,7 @@ where // Setup EVM let mut evm = self.evm_config.evm_for_block(&mut db, block.header()); - let mut system_caller = - SystemCaller::new(self.evm_config.clone(), self.provider.chain_spec()); + let mut system_caller = SystemCaller::new(self.provider.chain_spec()); // Apply pre-block system contract calls. system_caller.apply_pre_execution_changes(block.header(), &mut evm)?; diff --git a/crates/engine/util/src/reorg.rs b/crates/engine/util/src/reorg.rs index 25ee60460..b79da3f6c 100644 --- a/crates/engine/util/src/reorg.rs +++ b/crates/engine/util/src/reorg.rs @@ -293,14 +293,10 @@ where let mut evm = evm_config.evm_for_block(&mut state, &reorg_target.header); // apply eip-4788 pre block contract call - let mut system_caller = SystemCaller::new(evm_config.clone(), chain_spec.clone()); + let mut system_caller = SystemCaller::new(chain_spec.clone()); - system_caller.apply_beacon_root_contract_call( - reorg_target.timestamp, - reorg_target.number, - reorg_target.parent_beacon_block_root, - &mut evm, - )?; + system_caller + .apply_beacon_root_contract_call(reorg_target.parent_beacon_block_root, &mut evm)?; let mut cumulative_gas_used = 0; let mut sum_blob_gas_used = 0; diff --git a/crates/ethereum/evm/src/execute.rs b/crates/ethereum/evm/src/execute.rs index fc570d01a..f4427dd4a 100644 --- a/crates/ethereum/evm/src/execute.rs +++ b/crates/ethereum/evm/src/execute.rs @@ -90,7 +90,7 @@ where /// Current state for block execution. state: State, /// Utility to call system smart contracts. - system_caller: SystemCaller, + system_caller: SystemCaller>, } impl EthExecutionStrategy @@ -99,7 +99,7 @@ where { /// Creates a new [`EthExecutionStrategy`] pub fn new(state: State, chain_spec: Arc, evm_config: EvmConfig) -> Self { - let system_caller = SystemCaller::new(evm_config.clone(), chain_spec.clone()); + let system_caller = SystemCaller::new(chain_spec.clone()); Self { state, chain_spec, evm_config, system_caller } } } diff --git a/crates/ethereum/payload/src/lib.rs b/crates/ethereum/payload/src/lib.rs index 49ff2d908..d659012d5 100644 --- a/crates/ethereum/payload/src/lib.rs +++ b/crates/ethereum/payload/src/lib.rs @@ -202,11 +202,12 @@ where let block_number = evm_env.block_env.number; let beneficiary = evm_env.block_env.beneficiary; - let mut system_caller = SystemCaller::new(evm_config.clone(), chain_spec.clone()); + let mut evm = evm_config.evm_with_env(&mut db, evm_env); + let mut system_caller = SystemCaller::new(chain_spec.clone()); // apply eip-4788 pre block contract call system_caller - .pre_block_beacon_root_contract_call(&mut db, &evm_env, attributes.parent_beacon_block_root) + .apply_beacon_root_contract_call(attributes.parent_beacon_block_root, &mut evm) .map_err(|err| { warn!(target: "payload_builder", parent_hash=%parent_header.hash(), @@ -217,18 +218,15 @@ where })?; // apply eip-2935 blockhashes update - system_caller.pre_block_blockhashes_contract_call( - &mut db, - &evm_env, + system_caller.apply_blockhashes_contract_call( parent_header.hash(), + &mut evm, ) .map_err(|err| { warn!(target: "payload_builder", parent_hash=%parent_header.hash(), %err, "failed to update parent header blockhashes for payload"); PayloadBuilderError::Internal(err.into()) })?; - let mut evm = evm_config.evm_with_env(&mut db, evm_env); - let mut receipts = Vec::new(); let mut block_blob_count = 0; let blob_params = chain_spec.blob_params_at_timestamp(attributes.timestamp); diff --git a/crates/evm/src/system_calls/eip2935.rs b/crates/evm/src/system_calls/eip2935.rs index b156e7e95..dbd3d0d92 100644 --- a/crates/evm/src/system_calls/eip2935.rs +++ b/crates/evm/src/system_calls/eip2935.rs @@ -23,18 +23,16 @@ use revm::context_interface::result::ResultAndState; #[inline] pub(crate) fn transact_blockhashes_contract_call( chain_spec: impl EthereumHardforks, - block_timestamp: u64, - block_number: u64, parent_block_hash: B256, evm: &mut impl Evm, ) -> Result>, BlockExecutionError> { - if !chain_spec.is_prague_active_at_timestamp(block_timestamp) { + if !chain_spec.is_prague_active_at_timestamp(evm.block().timestamp) { return Ok(None) } // if the block number is zero (genesis block) then no system transaction may occur as per // EIP-2935 - if block_number == 0 { + if evm.block().number == 0 { return Ok(None) } diff --git a/crates/evm/src/system_calls/eip4788.rs b/crates/evm/src/system_calls/eip4788.rs index 457954d7c..f6f7989a8 100644 --- a/crates/evm/src/system_calls/eip4788.rs +++ b/crates/evm/src/system_calls/eip4788.rs @@ -20,12 +20,10 @@ use revm::context_interface::result::ResultAndState; #[inline] pub(crate) fn transact_beacon_root_contract_call( chain_spec: impl EthereumHardforks, - block_timestamp: u64, - block_number: u64, parent_beacon_block_root: Option, evm: &mut impl Evm, ) -> Result>, BlockExecutionError> { - if !chain_spec.is_cancun_active_at_timestamp(block_timestamp) { + if !chain_spec.is_cancun_active_at_timestamp(evm.block().timestamp) { return Ok(None) } @@ -34,7 +32,7 @@ pub(crate) fn transact_beacon_root_contract_call( // if the block number is zero (genesis block) then the parent beacon block root must // be 0x0 and no system transaction may occur as per EIP-4788 - if block_number == 0 { + if evm.block().number == 0 { if !parent_beacon_block_root.is_zero() { return Err(BlockValidationError::CancunGenesisParentBeaconBlockRootNotZero { parent_beacon_block_root, diff --git a/crates/evm/src/system_calls/mod.rs b/crates/evm/src/system_calls/mod.rs index f99e1ca0a..53f194740 100644 --- a/crates/evm/src/system_calls/mod.rs +++ b/crates/evm/src/system_calls/mod.rs @@ -1,7 +1,7 @@ //! System contract call functions. -use crate::{ConfigureEvm, Database, Evm, EvmEnv}; -use alloc::{boxed::Box, sync::Arc}; +use crate::Evm; +use alloc::boxed::Box; use alloy_consensus::BlockHeader; use alloy_eips::{ eip7002::WITHDRAWAL_REQUEST_TYPE, eip7251::CONSOLIDATION_REQUEST_TYPE, eip7685::Requests, @@ -78,18 +78,17 @@ impl OnStateHook for NoopHook { /// /// This can be used to chain system transaction calls. #[allow(missing_debug_implementations)] -pub struct SystemCaller { - evm_config: EvmConfig, - chain_spec: Arc, +pub struct SystemCaller { + chain_spec: ChainSpec, /// Optional hook to be called after each state change. hook: Option>, } -impl SystemCaller { +impl SystemCaller { /// Create a new system caller with the given EVM config, database, and chain spec, and creates /// the EVM with the given initialized config and block environment. - pub const fn new(evm_config: EvmConfig, chain_spec: Arc) -> Self { - Self { evm_config, chain_spec, hook: None } + pub const fn new(chain_spec: ChainSpec) -> Self { + Self { chain_spec, hook: None } } /// Installs a custom hook to be called after each state change. @@ -102,29 +101,18 @@ impl SystemCaller { pub fn finish(self) {} } -impl SystemCaller +impl SystemCaller where - EvmConfig: ConfigureEvm, Chainspec: EthereumHardforks, { /// Apply pre execution changes. pub fn apply_pre_execution_changes( &mut self, - header: &EvmConfig::Header, + header: impl BlockHeader, evm: &mut impl Evm, ) -> Result<(), BlockExecutionError> { - self.apply_blockhashes_contract_call( - header.timestamp(), - header.number(), - header.parent_hash(), - evm, - )?; - self.apply_beacon_root_contract_call( - header.timestamp(), - header.number(), - header.parent_beacon_block_root(), - evm, - )?; + self.apply_blockhashes_contract_call(header.parent_hash(), evm)?; + self.apply_beacon_root_contract_call(header.parent_beacon_block_root(), evm)?; Ok(()) } @@ -151,45 +139,14 @@ where Ok(requests) } - /// Applies the pre-block call to the EIP-2935 blockhashes contract. - pub fn pre_block_blockhashes_contract_call( - &mut self, - db: &mut DB, - evm_env: &EvmEnv, - parent_block_hash: B256, - ) -> Result<(), BlockExecutionError> - where - DB: Database + DatabaseCommit, - DB::Error: Display, - { - let evm_config = self.evm_config.clone(); - let mut evm = evm_config.evm_with_env(db, evm_env.clone()); - - self.apply_blockhashes_contract_call( - evm_env.block_env.timestamp, - evm_env.block_env.number, - parent_block_hash, - &mut evm, - )?; - - Ok(()) - } - /// Applies the pre-block call to the EIP-2935 blockhashes contract. pub fn apply_blockhashes_contract_call( &mut self, - timestamp: u64, - block_number: u64, parent_block_hash: B256, - evm: &mut impl Evm, + evm: &mut impl Evm, ) -> Result<(), BlockExecutionError> { - let result_and_state = eip2935::transact_blockhashes_contract_call( - &self.chain_spec, - timestamp, - block_number, - parent_block_hash, - evm, - )?; + let result_and_state = + eip2935::transact_blockhashes_contract_call(&self.chain_spec, parent_block_hash, evm)?; if let Some(res) = result_and_state { if let Some(ref mut hook) = self.hook { @@ -204,43 +161,15 @@ where Ok(()) } - /// Applies the pre-block call to the EIP-4788 beacon root contract. - pub fn pre_block_beacon_root_contract_call( - &mut self, - db: &mut DB, - evm_env: &EvmEnv, - parent_beacon_block_root: Option, - ) -> Result<(), BlockExecutionError> - where - DB: Database + DatabaseCommit, - DB::Error: Display, - { - let evm_config = self.evm_config.clone(); - let mut evm = evm_config.evm_with_env(db, evm_env.clone()); - - self.apply_beacon_root_contract_call( - evm_env.block_env.timestamp, - evm_env.block_env.number, - parent_beacon_block_root, - &mut evm, - )?; - - Ok(()) - } - /// Applies the pre-block call to the EIP-4788 beacon root contract. pub fn apply_beacon_root_contract_call( &mut self, - timestamp: u64, - block_number: u64, - parent_block_hash: Option, - evm: &mut impl Evm, + parent_beacon_block_root: Option, + evm: &mut impl Evm, ) -> Result<(), BlockExecutionError> { let result_and_state = eip4788::transact_beacon_root_contract_call( &self.chain_spec, - timestamp, - block_number, - parent_block_hash, + parent_beacon_block_root, evm, )?; @@ -257,24 +186,6 @@ where Ok(()) } - /// Applies the post-block call to the EIP-7002 withdrawal request contract. - pub fn post_block_withdrawal_requests_contract_call( - &mut self, - db: &mut DB, - evm_env: &EvmEnv, - ) -> Result - where - DB: Database + DatabaseCommit, - DB::Error: Display, - { - let evm_config = self.evm_config.clone(); - let mut evm = evm_config.evm_with_env(db, evm_env.clone()); - - let result = self.apply_withdrawal_requests_contract_call(&mut evm)?; - - Ok(result) - } - /// Applies the post-block call to the EIP-7002 withdrawal request contract. pub fn apply_withdrawal_requests_contract_call( &mut self, @@ -295,23 +206,6 @@ where eip7002::post_commit(result_and_state.result) } - /// Applies the post-block call to the EIP-7251 consolidation requests contract. - pub fn post_block_consolidation_requests_contract_call( - &mut self, - db: &mut DB, - evm_env: &EvmEnv, - ) -> Result - where - DB: Database + DatabaseCommit, - { - let evm_config = self.evm_config.clone(); - let mut evm = evm_config.evm_with_env(db, evm_env.clone()); - - let res = self.apply_consolidation_requests_contract_call(&mut evm)?; - - Ok(res) - } - /// Applies the post-block call to the EIP-7251 consolidation requests contract. pub fn apply_consolidation_requests_contract_call( &mut self, diff --git a/crates/optimism/evm/src/execute.rs b/crates/optimism/evm/src/execute.rs index 524d73bd3..6e0d94855 100644 --- a/crates/optimism/evm/src/execute.rs +++ b/crates/optimism/evm/src/execute.rs @@ -109,7 +109,7 @@ where /// Current state for block execution. state: State, /// Utility to call system smart contracts. - system_caller: SystemCaller, + system_caller: SystemCaller>, /// Receipt builder. receipt_builder: Arc, Receipt = N::Receipt>>, @@ -130,7 +130,7 @@ where dyn OpReceiptBuilder, Receipt = N::Receipt>, >, ) -> Self { - let system_caller = SystemCaller::new(evm_config.clone(), chain_spec.clone()); + let system_caller = SystemCaller::new(chain_spec.clone()); Self { state, chain_spec, evm_config, system_caller, receipt_builder } } } @@ -158,12 +158,8 @@ where let mut evm = self.evm_config.evm_for_block(&mut self.state, block.header()); - self.system_caller.apply_beacon_root_contract_call( - block.header().timestamp(), - block.header().number(), - block.header().parent_beacon_block_root(), - &mut evm, - )?; + self.system_caller + .apply_beacon_root_contract_call(block.header().parent_beacon_block_root(), &mut evm)?; // Ensure that the create2deployer is force-deployed at the canyon transition. Optimism // blocks will always have at least a single transaction in them (the L1 info transaction), diff --git a/crates/optimism/payload/src/builder.rs b/crates/optimism/payload/src/builder.rs index b7e0066b1..34ff1f715 100644 --- a/crates/optimism/payload/src/builder.rs +++ b/crates/optimism/payload/src/builder.rs @@ -55,7 +55,7 @@ use revm::{ result::{ExecutionResult, ResultAndState}, Block, }, - DatabaseCommit, + Database as _, DatabaseCommit, }; use std::{fmt::Display, sync::Arc}; use tracing::{debug, trace, warn}; @@ -368,19 +368,21 @@ impl OpBuilder<'_, Txs> { let Self { best } = self; debug!(target: "payload_builder", id=%ctx.payload_id(), parent_header = ?ctx.parent().hash(), parent_number = ctx.parent().number, "building new payload"); + let mut evm = ctx.evm_config.evm_with_env(&mut *state, ctx.evm_env.clone()); + // 1. apply eip-4788 pre block contract call - ctx.apply_pre_beacon_root_contract_call(state)?; + ctx.apply_pre_beacon_root_contract_call(&mut evm)?; // 2. ensure create2deployer is force deployed - ctx.ensure_create2_deployer(state)?; + ctx.ensure_create2_deployer(evm.db_mut())?; // 3. execute sequencer transactions - let mut info = ctx.execute_sequencer_transactions(state)?; + let mut info = ctx.execute_sequencer_transactions(&mut evm)?; // 4. if mem pool transactions are requested we execute them if !ctx.attributes().no_tx_pool { let best_txs = best(ctx.best_transaction_attributes()); - if ctx.execute_best_transactions(&mut info, state, best_txs)?.is_some() { + if ctx.execute_best_transactions(&mut info, &mut evm, best_txs)?.is_some() { return Ok(BuildOutcomeKind::Cancelled) } @@ -391,6 +393,8 @@ impl OpBuilder<'_, Txs> { } } + drop(evm); + // merge all transitions into bundle state, this would apply the withdrawal balance changes // and 4788 contract call state.merge_transitions(BundleRetention::Reverts); @@ -822,19 +826,14 @@ where N: OpPayloadPrimitives, { /// apply eip-4788 pre block contract call - pub fn apply_pre_beacon_root_contract_call( + pub fn apply_pre_beacon_root_contract_call( &self, - db: &mut DB, - ) -> Result<(), PayloadBuilderError> - where - DB: Database + DatabaseCommit, - DB::Error: Display, - { - SystemCaller::new(self.evm_config.clone(), self.chain_spec.clone()) - .pre_block_beacon_root_contract_call( - db, - &self.evm_env, + evm: &mut impl Evm, + ) -> Result<(), PayloadBuilderError> { + SystemCaller::new(&self.chain_spec) + .apply_beacon_root_contract_call( self.attributes().payload_attributes.parent_beacon_block_root, + evm, ) .map_err(|err| { warn!(target: "payload_builder", @@ -886,15 +885,15 @@ where } /// Executes all sequencer transactions that are included in the payload attributes. - pub fn execute_sequencer_transactions( + pub fn execute_sequencer_transactions( &self, - db: &mut State, - ) -> Result, PayloadBuilderError> - where - DB: Database, - { + evm: &mut impl Evm< + DB: Database + DatabaseCommit, + Tx = EvmConfig::TxEnv, + HaltReason = HaltReasonFor, + >, + ) -> Result, PayloadBuilderError> { let mut info = ExecutionInfo::with_capacity(self.attributes().transactions.len()); - let mut evm = self.evm_config.evm_with_env(&mut *db, self.evm_env.clone()); for sequencer_tx in &self.attributes().transactions { // A sequencer's block should never contain blob transactions. @@ -920,8 +919,8 @@ where let depositor_nonce = (self.is_regolith_active() && sequencer_tx.is_deposit()) .then(|| { evm.db_mut() - .load_cache_account(sequencer_tx.signer()) - .map(|acc| acc.account_info().unwrap_or_default().nonce) + .basic(sequencer_tx.signer()) + .map(|acc| acc.unwrap_or_default().nonce) }) .transpose() .map_err(|_| { @@ -932,7 +931,7 @@ where let tx_env = self.evm_config.tx_env(sequencer_tx.tx(), sequencer_tx.signer()); - let ResultAndState { result, state } = match evm.transact(tx_env) { + let ResultAndState { result, state: _ } = match evm.transact_commit(tx_env) { Ok(res) => res, Err(err) => { if err.is_invalid_tx_err() { @@ -944,9 +943,6 @@ where } }; - // commit changes - evm.db_mut().commit(state); - let gas_used = result.gas_used(); // add gas used by the transaction to cumulative gas used, before creating the receipt @@ -971,24 +967,23 @@ where /// Executes the given best transactions and updates the execution info. /// /// Returns `Ok(Some(())` if the job was cancelled. - pub fn execute_best_transactions( + pub fn execute_best_transactions( &self, info: &mut ExecutionInfo, - db: &mut State, + evm: &mut impl Evm< + DB: DatabaseCommit, + Tx = EvmConfig::TxEnv, + HaltReason = HaltReasonFor, + >, mut best_txs: impl PayloadTransactions< Transaction: PoolTransaction, >, - ) -> Result, PayloadBuilderError> - where - DB: Database, - { + ) -> Result, PayloadBuilderError> { let block_gas_limit = self.block_gas_limit(); let block_da_limit = self.da_config.max_da_block_size(); let tx_da_limit = self.da_config.max_da_tx_size(); let base_fee = self.base_fee(); - let mut evm = self.evm_config.evm_with_env(&mut *db, self.evm_env.clone()); - while let Some(tx) = best_txs.next(()) { let tx = tx.into_consensus(); if info.is_tx_over_limits(tx.tx(), block_gas_limit, tx_da_limit, block_da_limit) { @@ -1013,7 +1008,7 @@ where // Configure the environment for the tx. let tx_env = self.evm_config.tx_env(tx.tx(), tx.signer()); - let ResultAndState { result, state } = match evm.transact(tx_env) { + let ResultAndState { result, state: _ } = match evm.transact_commit(tx_env) { Ok(res) => res, Err(err) => { if let Some(err) = err.as_invalid_tx_err() { @@ -1034,9 +1029,6 @@ where } }; - // commit changes - evm.db_mut().commit(state); - let gas_used = result.gas_used(); // add gas used by the transaction to cumulative gas used, before creating the diff --git a/crates/rpc/rpc-eth-api/src/helpers/pending_block.rs b/crates/rpc/rpc-eth-api/src/helpers/pending_block.rs index d465810fd..8a62c226b 100644 --- a/crates/rpc/rpc-eth-api/src/helpers/pending_block.rs +++ b/crates/rpc/rpc-eth-api/src/helpers/pending_block.rs @@ -36,7 +36,6 @@ use revm::{ result::{ExecutionResult, ResultAndState}, Block, }, - DatabaseCommit, }; use std::time::{Duration, Instant}; use tokio::sync::Mutex; @@ -277,10 +276,11 @@ pub trait LoadPendingBlock: let chain_spec = self.provider().chain_spec(); - let mut system_caller = SystemCaller::new(self.evm_config().clone(), chain_spec.clone()); + let mut system_caller = SystemCaller::new(chain_spec.clone()); + let mut evm = self.evm_config().evm_with_env(&mut db, evm_env.clone()); system_caller - .pre_block_blockhashes_contract_call(&mut db, &evm_env, parent_hash) + .apply_blockhashes_contract_call(parent_hash, &mut evm) .map_err(|err| EthApiError::Internal(err.into()))?; let mut results = Vec::new(); @@ -337,9 +337,8 @@ pub trait LoadPendingBlock: } let tx_env = self.evm_config().tx_env(tx.tx(), tx.signer()); - let mut evm = self.evm_config().evm_with_env(&mut db, evm_env.clone()); - let ResultAndState { result, state } = match evm.transact(tx_env) { + let ResultAndState { result, state: _ } = match evm.transact_commit(tx_env) { Ok(res) => res, Err(err) => { if let Some(err) = err.as_invalid_tx_err() { @@ -361,10 +360,6 @@ pub trait LoadPendingBlock: return Err(Self::Error::from_evm_err(err)); } }; - // drop evm to release db reference. - drop(evm); - // commit changes - db.commit(state); // add to the total blob gas used if the transaction successfully executed if let Some(tx_blob_gas) = tx.blob_gas_used() { @@ -395,6 +390,9 @@ pub trait LoadPendingBlock: &[], ); + // release db + drop(evm); + // increment account balances for withdrawals db.increment_balances(balance_increments).map_err(Self::Error::from_eth_err)?; diff --git a/crates/rpc/rpc-eth-api/src/helpers/trace.rs b/crates/rpc/rpc-eth-api/src/helpers/trace.rs index d517651be..2e72f8b4f 100644 --- a/crates/rpc/rpc-eth-api/src/helpers/trace.rs +++ b/crates/rpc/rpc-eth-api/src/helpers/trace.rs @@ -468,18 +468,13 @@ pub trait Trace: db: &mut DB, evm_env: &EvmEnv<::Spec>, ) -> Result<(), Self::Error> { - let mut system_caller = - SystemCaller::new(self.evm_config().clone(), self.provider().chain_spec()); - // apply relevant system calls - system_caller - .pre_block_beacon_root_contract_call(db, evm_env, block.parent_beacon_block_root()) - .map_err(|_| EthApiError::EvmCustom("failed to apply 4788 system call".to_string()))?; + let mut system_caller = SystemCaller::new(self.provider().chain_spec()); + // apply relevant system calls + let mut evm = self.evm_config().evm_with_env(db, evm_env.clone()); system_caller - .pre_block_blockhashes_contract_call(db, evm_env, block.parent_hash()) - .map_err(|_| { - EthApiError::EvmCustom("failed to apply blockhashes system call".to_string()) - })?; + .apply_pre_execution_changes(block.header(), &mut evm) + .map_err(|_| EthApiError::EvmCustom("failed to apply 4788 system call".to_string()))?; Ok(()) }