reth_rpc_eth_api/helpers/
call.rs

1//! Loads a pending block from database. Helper trait for `eth_` transaction, call and trace RPC
2//! methods.
3
4use core::fmt;
5
6use super::{LoadBlock, LoadPendingBlock, LoadState, LoadTransaction, SpawnBlocking, Trace};
7use crate::{
8    helpers::estimate::EstimateCall, FromEvmError, FullEthApiTypes, RpcBlock, RpcNodeCore,
9};
10use alloy_consensus::{transaction::TxHashRef, BlockHeader};
11use alloy_eips::eip2930::AccessListResult;
12use alloy_evm::overrides::{apply_block_overrides, apply_state_overrides, OverrideBlockHashes};
13use alloy_network::TransactionBuilder;
14use alloy_primitives::{Bytes, B256, U256};
15use alloy_rpc_types_eth::{
16    simulate::{SimBlock, SimulatePayload, SimulatedBlock},
17    state::{EvmOverrides, StateOverride},
18    BlockId, Bundle, EthCallResponse, StateContext, TransactionInfo,
19};
20use futures::Future;
21use reth_errors::{ProviderError, RethError};
22use reth_evm::{
23    env::BlockEnvironment, ConfigureEvm, Evm, EvmEnvFor, HaltReasonFor, InspectorFor,
24    TransactionEnv, TxEnvFor,
25};
26use reth_node_api::BlockBody;
27use reth_primitives_traits::Recovered;
28use reth_revm::{
29    cancelled::CancelOnDrop,
30    database::StateProviderDatabase,
31    db::{bal::EvmDatabaseError, State},
32};
33use reth_rpc_convert::{RpcConvert, RpcTxReq};
34use reth_rpc_eth_types::{
35    cache::db::StateProviderTraitObjWrapper,
36    error::{AsEthApiError, FromEthApiError},
37    simulate::{self, EthSimulateError},
38    EthApiError, StateCacheDb,
39};
40use reth_storage_api::{BlockIdReader, ProviderTx, StateProviderBox};
41use revm::{
42    context::Block,
43    context_interface::{result::ResultAndState, Transaction},
44    Database, DatabaseCommit,
45};
46use revm_inspectors::{access_list::AccessListInspector, transfer::TransferInspector};
47use tracing::{trace, warn};
48
49/// Result type for `eth_simulateV1` RPC method.
50pub type SimulatedBlocksResult<N, E> = Result<Vec<SimulatedBlock<RpcBlock<N>>>, E>;
51
52/// Execution related functions for the [`EthApiServer`](crate::EthApiServer) trait in
53/// the `eth_` namespace.
54pub trait EthCall: EstimateCall + Call + LoadPendingBlock + LoadBlock + FullEthApiTypes {
55    /// Estimate gas needed for execution of the `request` at the [`BlockId`].
56    fn estimate_gas_at(
57        &self,
58        request: RpcTxReq<<Self::RpcConvert as RpcConvert>::Network>,
59        at: BlockId,
60        state_override: Option<StateOverride>,
61    ) -> impl Future<Output = Result<U256, Self::Error>> + Send {
62        EstimateCall::estimate_gas_at(self, request, at, state_override)
63    }
64
65    /// `eth_simulateV1` executes an arbitrary number of transactions on top of the requested state.
66    /// The transactions are packed into individual blocks. Overrides can be provided.
67    ///
68    /// See also: <https://github.com/ethereum/go-ethereum/pull/27720>
69    fn simulate_v1(
70        &self,
71        payload: SimulatePayload<RpcTxReq<<Self::RpcConvert as RpcConvert>::Network>>,
72        block: Option<BlockId>,
73    ) -> impl Future<Output = SimulatedBlocksResult<Self::NetworkTypes, Self::Error>> + Send {
74        async move {
75            if payload.block_state_calls.len() > self.max_simulate_blocks() as usize {
76                return Err(EthApiError::InvalidParams("too many blocks.".to_string()).into())
77            }
78
79            let block = block.unwrap_or_default();
80
81            let SimulatePayload {
82                block_state_calls,
83                trace_transfers,
84                validation,
85                return_full_transactions,
86            } = payload;
87
88            if block_state_calls.is_empty() {
89                return Err(EthApiError::InvalidParams(String::from("calls are empty.")).into())
90            }
91
92            let base_block =
93                self.recovered_block(block).await?.ok_or(EthApiError::HeaderNotFound(block))?;
94            let mut parent = base_block.sealed_header().clone();
95
96            self.spawn_with_state_at_block(block, move |this, mut db| {
97                let mut blocks: Vec<SimulatedBlock<RpcBlock<Self::NetworkTypes>>> =
98                    Vec::with_capacity(block_state_calls.len());
99                for block in block_state_calls {
100                    let mut evm_env = this
101                        .evm_config()
102                        .next_evm_env(&parent, &this.next_env_attributes(&parent)?)
103                        .map_err(RethError::other)
104                        .map_err(Self::Error::from_eth_err)?;
105
106                    // Always disable EIP-3607
107                    evm_env.cfg_env.disable_eip3607 = true;
108
109                    if !validation {
110                        // If not explicitly required, we disable nonce check <https://github.com/paradigmxyz/reth/issues/16108>
111                        evm_env.cfg_env.disable_nonce_check = true;
112                        evm_env.cfg_env.disable_base_fee = true;
113                        evm_env.cfg_env.tx_gas_limit_cap = Some(u64::MAX);
114                        evm_env.block_env.inner_mut().basefee = 0;
115                    }
116
117                    let SimBlock { block_overrides, state_overrides, calls } = block;
118
119                    if let Some(block_overrides) = block_overrides {
120                        // ensure we don't allow uncapped gas limit per block
121                        if let Some(gas_limit_override) = block_overrides.gas_limit &&
122                            gas_limit_override > evm_env.block_env.gas_limit() &&
123                            gas_limit_override > this.call_gas_limit()
124                        {
125                            return Err(EthApiError::other(EthSimulateError::GasLimitReached).into())
126                        }
127                        apply_block_overrides(
128                            block_overrides,
129                            &mut db,
130                            evm_env.block_env.inner_mut(),
131                        );
132                    }
133                    if let Some(state_overrides) = state_overrides {
134                        apply_state_overrides(state_overrides, &mut db)
135                            .map_err(Self::Error::from_eth_err)?;
136                    }
137
138                    let block_gas_limit = evm_env.block_env.gas_limit();
139                    let chain_id = evm_env.cfg_env.chain_id;
140
141                    let default_gas_limit = {
142                        let total_specified_gas =
143                            calls.iter().filter_map(|tx| tx.as_ref().gas_limit()).sum::<u64>();
144                        let txs_without_gas_limit =
145                            calls.iter().filter(|tx| tx.as_ref().gas_limit().is_none()).count();
146
147                        if total_specified_gas > block_gas_limit {
148                            return Err(EthApiError::Other(Box::new(
149                                EthSimulateError::BlockGasLimitExceeded,
150                            ))
151                            .into())
152                        }
153
154                        if txs_without_gas_limit > 0 {
155                            (block_gas_limit - total_specified_gas) / txs_without_gas_limit as u64
156                        } else {
157                            0
158                        }
159                    };
160
161                    let ctx = this
162                        .evm_config()
163                        .context_for_next_block(&parent, this.next_env_attributes(&parent)?)
164                        .map_err(RethError::other)
165                        .map_err(Self::Error::from_eth_err)?;
166                    let map_err = |e: EthApiError| -> Self::Error {
167                        match e.as_simulate_error() {
168                            Some(sim_err) => Self::Error::from_eth_err(EthApiError::other(sim_err)),
169                            None => Self::Error::from_eth_err(e),
170                        }
171                    };
172
173                    let (result, results) = if trace_transfers {
174                        // prepare inspector to capture transfer inside the evm so they are recorded
175                        // and included in logs
176                        let inspector = TransferInspector::new(false).with_logs(true);
177                        let evm = this
178                            .evm_config()
179                            .evm_with_env_and_inspector(&mut db, evm_env, inspector);
180                        let builder = this.evm_config().create_block_builder(evm, &parent, ctx);
181                        simulate::execute_transactions(
182                            builder,
183                            calls,
184                            default_gas_limit,
185                            chain_id,
186                            this.converter(),
187                        )
188                        .map_err(map_err)?
189                    } else {
190                        let evm = this.evm_config().evm_with_env(&mut db, evm_env);
191                        let builder = this.evm_config().create_block_builder(evm, &parent, ctx);
192                        simulate::execute_transactions(
193                            builder,
194                            calls,
195                            default_gas_limit,
196                            chain_id,
197                            this.converter(),
198                        )
199                        .map_err(map_err)?
200                    };
201
202                    parent = result.block.clone_sealed_header();
203
204                    let block = simulate::build_simulated_block::<Self::Error, _>(
205                        result.block,
206                        results,
207                        return_full_transactions.into(),
208                        this.converter(),
209                    )?;
210
211                    blocks.push(block);
212                }
213
214                Ok(blocks)
215            })
216            .await
217        }
218    }
219
220    /// Executes the call request (`eth_call`) and returns the output
221    fn call(
222        &self,
223        request: RpcTxReq<<Self::RpcConvert as RpcConvert>::Network>,
224        block_number: Option<BlockId>,
225        overrides: EvmOverrides,
226    ) -> impl Future<Output = Result<Bytes, Self::Error>> + Send {
227        async move {
228            let _permit = self.acquire_owned_blocking_io().await;
229            let res =
230                self.transact_call_at(request, block_number.unwrap_or_default(), overrides).await?;
231
232            Self::Error::ensure_success(res.result)
233        }
234    }
235
236    /// Simulate arbitrary number of transactions at an arbitrary blockchain index, with the
237    /// optionality of state overrides
238    fn call_many(
239        &self,
240        bundles: Vec<Bundle<RpcTxReq<<Self::RpcConvert as RpcConvert>::Network>>>,
241        state_context: Option<StateContext>,
242        mut state_override: Option<StateOverride>,
243    ) -> impl Future<Output = Result<Vec<Vec<EthCallResponse>>, Self::Error>> + Send {
244        async move {
245            // Check if the vector of bundles is empty
246            if bundles.is_empty() {
247                return Err(EthApiError::InvalidParams(String::from("bundles are empty.")).into());
248            }
249
250            let StateContext { transaction_index, block_number } =
251                state_context.unwrap_or_default();
252            let transaction_index = transaction_index.unwrap_or_default();
253
254            let mut target_block = block_number.unwrap_or_default();
255            let is_block_target_pending = target_block.is_pending();
256
257            // if it's not pending, we should always use block_hash over block_number to ensure that
258            // different provider calls query data related to the same block.
259            if !is_block_target_pending {
260                target_block = self
261                    .provider()
262                    .block_hash_for_id(target_block)
263                    .map_err(|_| EthApiError::HeaderNotFound(target_block))?
264                    .ok_or_else(|| EthApiError::HeaderNotFound(target_block))?
265                    .into();
266            }
267
268            let ((evm_env, _), block) = futures::try_join!(
269                self.evm_env_at(target_block),
270                self.recovered_block(target_block)
271            )?;
272
273            let block = block.ok_or(EthApiError::HeaderNotFound(target_block))?;
274
275            // we're essentially replaying the transactions in the block here, hence we need the
276            // state that points to the beginning of the block, which is the state at
277            // the parent block
278            let mut at = block.parent_hash();
279            let mut replay_block_txs = true;
280
281            let num_txs =
282                transaction_index.index().unwrap_or_else(|| block.body().transactions().len());
283            // but if all transactions are to be replayed, we can use the state at the block itself,
284            // however only if we're not targeting the pending block, because for pending we can't
285            // rely on the block's state being available
286            if !is_block_target_pending && num_txs == block.body().transactions().len() {
287                at = block.hash();
288                replay_block_txs = false;
289            }
290
291            self.spawn_with_state_at_block(at, move |this, mut db| {
292                let mut all_results = Vec::with_capacity(bundles.len());
293
294                if replay_block_txs {
295                    // only need to replay the transactions in the block if not all transactions are
296                    // to be replayed
297                    let block_transactions = block.transactions_recovered().take(num_txs);
298                    for tx in block_transactions {
299                        let tx_env = RpcNodeCore::evm_config(&this).tx_env(tx);
300                        let res = this.transact(&mut db, evm_env.clone(), tx_env)?;
301                        db.commit(res.state);
302                    }
303                }
304
305                // transact all bundles
306                for (bundle_index, bundle) in bundles.into_iter().enumerate() {
307                    let Bundle { transactions, block_override } = bundle;
308                    if transactions.is_empty() {
309                        // Skip empty bundles
310                        continue;
311                    }
312
313                    let mut bundle_results = Vec::with_capacity(transactions.len());
314                    let block_overrides = block_override.map(Box::new);
315
316                    // transact all transactions in the bundle
317                    for (tx_index, tx) in transactions.into_iter().enumerate() {
318                        // Apply overrides, state overrides are only applied for the first tx in the
319                        // request
320                        let overrides =
321                            EvmOverrides::new(state_override.take(), block_overrides.clone());
322
323                        let (current_evm_env, prepared_tx) = this
324                            .prepare_call_env(evm_env.clone(), tx, &mut db, overrides)
325                            .map_err(|err| {
326                                Self::Error::from_eth_err(EthApiError::call_many_error(
327                                    bundle_index,
328                                    tx_index,
329                                    err.into(),
330                                ))
331                            })?;
332                        let res = this.transact(&mut db, current_evm_env, prepared_tx).map_err(
333                            |err| {
334                                Self::Error::from_eth_err(EthApiError::call_many_error(
335                                    bundle_index,
336                                    tx_index,
337                                    err.into(),
338                                ))
339                            },
340                        )?;
341
342                        match Self::Error::ensure_success(res.result) {
343                            Ok(output) => {
344                                bundle_results
345                                    .push(EthCallResponse { value: Some(output), error: None });
346                            }
347                            Err(err) => {
348                                bundle_results.push(EthCallResponse {
349                                    value: None,
350                                    error: Some(err.to_string()),
351                                });
352                            }
353                        }
354
355                        // Commit state changes after each transaction to allow subsequent calls to
356                        // see the updates
357                        db.commit(res.state);
358                    }
359
360                    all_results.push(bundle_results);
361                }
362
363                Ok(all_results)
364            })
365            .await
366        }
367    }
368
369    /// Creates [`AccessListResult`] for the [`RpcTxReq`] at the given
370    /// [`BlockId`], or latest block.
371    fn create_access_list_at(
372        &self,
373        request: RpcTxReq<<Self::RpcConvert as RpcConvert>::Network>,
374        block_number: Option<BlockId>,
375        state_override: Option<StateOverride>,
376    ) -> impl Future<Output = Result<AccessListResult, Self::Error>> + Send
377    where
378        Self: Trace,
379    {
380        async move {
381            let block_id = block_number.unwrap_or_default();
382            let (evm_env, at) = self.evm_env_at(block_id).await?;
383
384            self.spawn_blocking_io_fut(move |this| async move {
385                this.create_access_list_with(evm_env, at, request, state_override).await
386            })
387            .await
388        }
389    }
390
391    /// Creates [`AccessListResult`] for the [`RpcTxReq`] at the given
392    /// [`BlockId`].
393    fn create_access_list_with(
394        &self,
395        mut evm_env: EvmEnvFor<Self::Evm>,
396        at: BlockId,
397        request: RpcTxReq<<Self::RpcConvert as RpcConvert>::Network>,
398        state_override: Option<StateOverride>,
399    ) -> impl Future<Output = Result<AccessListResult, Self::Error>> + Send
400    where
401        Self: Trace,
402    {
403        self.spawn_blocking_io_fut(move |this| async move {
404            let state = this.state_at_block_id(at).await?;
405            let mut db = State::builder().with_database(StateProviderDatabase::new(state)).build();
406
407            if let Some(state_overrides) = state_override {
408                apply_state_overrides(state_overrides, &mut db)
409                    .map_err(Self::Error::from_eth_err)?;
410            }
411
412            let mut tx_env = this.create_txn_env(&evm_env, request.clone(), &mut db)?;
413
414            // we want to disable this in eth_createAccessList, since this is common practice used
415            // by other node impls and providers <https://github.com/foundry-rs/foundry/issues/4388>
416            evm_env.cfg_env.disable_block_gas_limit = true;
417
418            // The basefee should be ignored for eth_createAccessList
419            // See:
420            // <https://github.com/ethereum/go-ethereum/blob/8990c92aea01ca07801597b00c0d83d4e2d9b811/internal/ethapi/api.go#L1476-L1476>
421            evm_env.cfg_env.disable_base_fee = true;
422
423            // Disabled because eth_createAccessList is sometimes used with non-eoa senders
424            evm_env.cfg_env.disable_eip3607 = true;
425
426            if request.as_ref().gas_limit().is_none() && tx_env.gas_price() > 0 {
427                let cap = this.caller_gas_allowance(&mut db, &evm_env, &tx_env)?;
428                // no gas limit was provided in the request, so we need to cap the request's gas
429                // limit
430                tx_env.set_gas_limit(cap.min(evm_env.block_env.gas_limit()));
431            }
432
433            // can consume the list since we're not using the request anymore
434            let initial = request.as_ref().access_list().cloned().unwrap_or_default();
435
436            let mut inspector = AccessListInspector::new(initial);
437
438            let result = this.inspect(&mut db, evm_env.clone(), tx_env.clone(), &mut inspector)?;
439            let access_list = inspector.into_access_list();
440            let gas_used = result.result.gas_used();
441            tx_env.set_access_list(access_list.clone());
442            if let Err(err) = Self::Error::ensure_success(result.result) {
443                return Ok(AccessListResult {
444                    access_list,
445                    gas_used: U256::from(gas_used),
446                    error: Some(err.to_string()),
447                });
448            }
449
450            // transact again to get the exact gas used
451            let result = this.transact(&mut db, evm_env, tx_env)?;
452            let gas_used = result.result.gas_used();
453            let error = Self::Error::ensure_success(result.result).err().map(|e| e.to_string());
454
455            Ok(AccessListResult { access_list, gas_used: U256::from(gas_used), error })
456        })
457    }
458}
459
460/// Executes code on state.
461pub trait Call:
462    LoadState<
463        RpcConvert: RpcConvert<Evm = Self::Evm>,
464        Error: FromEvmError<Self::Evm>
465                   + From<<Self::RpcConvert as RpcConvert>::Error>
466                   + From<ProviderError>,
467    > + SpawnBlocking
468{
469    /// Returns default gas limit to use for `eth_call` and tracing RPC methods.
470    ///
471    /// Data access in default trait method implementations.
472    fn call_gas_limit(&self) -> u64;
473
474    /// Returns the maximum number of blocks accepted for `eth_simulateV1`.
475    fn max_simulate_blocks(&self) -> u64;
476
477    /// Returns the maximum memory the EVM can allocate per RPC request.
478    fn evm_memory_limit(&self) -> u64;
479
480    /// Returns the max gas limit that the caller can afford given a transaction environment.
481    fn caller_gas_allowance(
482        &self,
483        mut db: impl Database<Error: Into<EthApiError>>,
484        _evm_env: &EvmEnvFor<Self::Evm>,
485        tx_env: &TxEnvFor<Self::Evm>,
486    ) -> Result<u64, Self::Error> {
487        alloy_evm::call::caller_gas_allowance(&mut db, tx_env).map_err(Self::Error::from_eth_err)
488    }
489
490    /// Executes the closure with the state that corresponds to the given [`BlockId`].
491    fn with_state_at_block<F, R>(
492        &self,
493        at: BlockId,
494        f: F,
495    ) -> impl Future<Output = Result<R, Self::Error>> + Send
496    where
497        R: Send + 'static,
498        F: FnOnce(Self, StateProviderBox) -> Result<R, Self::Error> + Send + 'static,
499    {
500        self.spawn_blocking_io_fut(move |this| async move {
501            let state = this.state_at_block_id(at).await?;
502            f(this, state)
503        })
504    }
505
506    /// Executes the `TxEnv` against the given [Database] without committing state
507    /// changes.
508    fn transact<DB>(
509        &self,
510        db: DB,
511        evm_env: EvmEnvFor<Self::Evm>,
512        tx_env: TxEnvFor<Self::Evm>,
513    ) -> Result<ResultAndState<HaltReasonFor<Self::Evm>>, Self::Error>
514    where
515        DB: Database<Error = EvmDatabaseError<ProviderError>> + fmt::Debug,
516    {
517        let mut evm = self.evm_config().evm_with_env(db, evm_env);
518        let res = evm.transact(tx_env).map_err(Self::Error::from_evm_err)?;
519
520        Ok(res)
521    }
522
523    /// Executes the [`reth_evm::EvmEnv`] against the given [Database] without committing state
524    /// changes.
525    fn transact_with_inspector<DB, I>(
526        &self,
527        db: DB,
528        evm_env: EvmEnvFor<Self::Evm>,
529        tx_env: TxEnvFor<Self::Evm>,
530        inspector: I,
531    ) -> Result<ResultAndState<HaltReasonFor<Self::Evm>>, Self::Error>
532    where
533        DB: Database<Error = EvmDatabaseError<ProviderError>> + fmt::Debug,
534        I: InspectorFor<Self::Evm, DB>,
535    {
536        let mut evm = self.evm_config().evm_with_env_and_inspector(db, evm_env, inspector);
537        let res = evm.transact(tx_env).map_err(Self::Error::from_evm_err)?;
538
539        Ok(res)
540    }
541
542    /// Executes the call request at the given [`BlockId`].
543    ///
544    /// This spawns a new task that obtains the state for the given [`BlockId`] and then transacts
545    /// the call [`Self::transact`]. If the future is dropped before the (blocking) transact
546    /// call is invoked, then the task is cancelled early, (for example if the request is terminated
547    /// early client-side).
548    fn transact_call_at(
549        &self,
550        request: RpcTxReq<<Self::RpcConvert as RpcConvert>::Network>,
551        at: BlockId,
552        overrides: EvmOverrides,
553    ) -> impl Future<Output = Result<ResultAndState<HaltReasonFor<Self::Evm>>, Self::Error>> + Send
554    where
555        Self: LoadPendingBlock,
556    {
557        async move {
558            let guard = CancelOnDrop::default();
559            let cancel = guard.clone();
560            let this = self.clone();
561
562            let res = self
563                .spawn_with_call_at(request, at, overrides, move |db, evm_env, tx_env| {
564                    if cancel.is_cancelled() {
565                        // callsite dropped the guard
566                        return Err(EthApiError::InternalEthError.into())
567                    }
568                    this.transact(db, evm_env, tx_env)
569                })
570                .await;
571            drop(guard);
572            res
573        }
574    }
575
576    /// Executes the closure with the state that corresponds to the given [`BlockId`] on a new task
577    fn spawn_with_state_at_block<F, R>(
578        &self,
579        at: impl Into<BlockId>,
580        f: F,
581    ) -> impl Future<Output = Result<R, Self::Error>> + Send
582    where
583        F: FnOnce(Self, StateCacheDb) -> Result<R, Self::Error> + Send + 'static,
584        R: Send + 'static,
585    {
586        let at = at.into();
587        self.spawn_blocking_io_fut(move |this| async move {
588            let state = this.state_at_block_id(at).await?;
589            let db = State::builder()
590                .with_database(StateProviderDatabase::new(StateProviderTraitObjWrapper(state)))
591                .build();
592            f(this, db)
593        })
594    }
595
596    /// Prepares the state and env for the given [`RpcTxReq`] at the given [`BlockId`] and
597    /// executes the closure on a new task returning the result of the closure.
598    ///
599    /// This returns the configured [`reth_evm::EvmEnv`] for the given [`RpcTxReq`] at
600    /// the given [`BlockId`] and with configured call settings: `prepare_call_env`.
601    ///
602    /// This is primarily used by `eth_call`.
603    ///
604    /// # Blocking behaviour
605    ///
606    /// This assumes executing the call is relatively more expensive on IO than CPU because it
607    /// transacts a single transaction on an empty in memory database. Because `eth_call`s are
608    /// usually allowed to consume a lot of gas, this also allows a lot of memory operations so
609    /// we assume this is not primarily CPU bound and instead spawn the call on a regular tokio task
610    /// instead, where blocking IO is less problematic.
611    fn spawn_with_call_at<F, R>(
612        &self,
613        request: RpcTxReq<<Self::RpcConvert as RpcConvert>::Network>,
614        at: BlockId,
615        overrides: EvmOverrides,
616        f: F,
617    ) -> impl Future<Output = Result<R, Self::Error>> + Send
618    where
619        Self: LoadPendingBlock,
620        F: FnOnce(
621                &mut StateCacheDb,
622                EvmEnvFor<Self::Evm>,
623                TxEnvFor<Self::Evm>,
624            ) -> Result<R, Self::Error>
625            + Send
626            + 'static,
627        R: Send + 'static,
628    {
629        async move {
630            let (evm_env, at) = self.evm_env_at(at).await?;
631            self.spawn_with_state_at_block(at, move |this, mut db| {
632                let (evm_env, tx_env) =
633                    this.prepare_call_env(evm_env, request, &mut db, overrides)?;
634
635                f(&mut db, evm_env, tx_env)
636            })
637            .await
638        }
639    }
640
641    /// Retrieves the transaction if it exists and executes it.
642    ///
643    /// Before the transaction is executed, all previous transaction in the block are applied to the
644    /// state by executing them first.
645    /// The callback `f` is invoked with the [`ResultAndState`] after the transaction was executed
646    /// and the database that points to the beginning of the transaction.
647    ///
648    /// Note: Implementers should use a threadpool where blocking is allowed, such as
649    /// [`BlockingTaskPool`](reth_tasks::pool::BlockingTaskPool).
650    fn spawn_replay_transaction<F, R>(
651        &self,
652        hash: B256,
653        f: F,
654    ) -> impl Future<Output = Result<Option<R>, Self::Error>> + Send
655    where
656        Self: LoadBlock + LoadTransaction,
657        F: FnOnce(
658                TransactionInfo,
659                ResultAndState<HaltReasonFor<Self::Evm>>,
660                StateCacheDb,
661            ) -> Result<R, Self::Error>
662            + Send
663            + 'static,
664        R: Send + 'static,
665    {
666        async move {
667            let (transaction, block) = match self.transaction_and_block(hash).await? {
668                None => return Ok(None),
669                Some(res) => res,
670            };
671            let (tx, tx_info) = transaction.split();
672
673            let evm_env = self.evm_env_for_header(block.sealed_block().sealed_header())?;
674
675            // we need to get the state of the parent block because we're essentially replaying the
676            // block the transaction is included in
677            let parent_block = block.parent_hash();
678
679            self.spawn_with_state_at_block(parent_block, move |this, mut db| {
680                let block_txs = block.transactions_recovered();
681
682                // replay all transactions prior to the targeted transaction
683                this.replay_transactions_until(&mut db, evm_env.clone(), block_txs, *tx.tx_hash())?;
684
685                let tx_env = RpcNodeCore::evm_config(&this).tx_env(tx);
686
687                let res = this.transact(&mut db, evm_env, tx_env)?;
688                f(tx_info, res, db)
689            })
690            .await
691            .map(Some)
692        }
693    }
694
695    /// Replays all the transactions until the target transaction is found.
696    ///
697    /// All transactions before the target transaction are executed and their changes are written to
698    /// the _runtime_ db ([`State`]).
699    ///
700    /// Note: This assumes the target transaction is in the given iterator.
701    /// Returns the index of the target transaction in the given iterator.
702    fn replay_transactions_until<'a, DB, I>(
703        &self,
704        db: &mut DB,
705        evm_env: EvmEnvFor<Self::Evm>,
706        transactions: I,
707        target_tx_hash: B256,
708    ) -> Result<usize, Self::Error>
709    where
710        DB: Database<Error = EvmDatabaseError<ProviderError>> + DatabaseCommit + core::fmt::Debug,
711        I: IntoIterator<Item = Recovered<&'a ProviderTx<Self::Provider>>>,
712    {
713        let mut evm = self.evm_config().evm_with_env(db, evm_env);
714        let mut index = 0;
715        for tx in transactions {
716            if *tx.tx_hash() == target_tx_hash {
717                // reached the target transaction
718                break
719            }
720
721            let tx_env = self.evm_config().tx_env(tx);
722            evm.transact_commit(tx_env).map_err(Self::Error::from_evm_err)?;
723            index += 1;
724        }
725        Ok(index)
726    }
727
728    ///
729    /// All `TxEnv` fields are derived from the given [`RpcTxReq`], if fields are
730    /// `None`, they fall back to the [`reth_evm::EvmEnv`]'s settings.
731    fn create_txn_env(
732        &self,
733        evm_env: &EvmEnvFor<Self::Evm>,
734        mut request: RpcTxReq<<Self::RpcConvert as RpcConvert>::Network>,
735        mut db: impl Database<Error: Into<EthApiError>>,
736    ) -> Result<TxEnvFor<Self::Evm>, Self::Error> {
737        if request.as_ref().nonce().is_none() {
738            let nonce = db
739                .basic(request.as_ref().from().unwrap_or_default())
740                .map_err(Into::into)?
741                .map(|acc| acc.nonce)
742                .unwrap_or_default();
743            request.as_mut().set_nonce(nonce);
744        }
745
746        Ok(self.converter().tx_env(request, evm_env)?)
747    }
748
749    /// Prepares the [`reth_evm::EvmEnv`] for execution of calls.
750    ///
751    /// Does not commit any changes to the underlying database.
752    ///
753    /// ## EVM settings
754    ///
755    /// This modifies certain EVM settings to mirror geth's `SkipAccountChecks` when transacting requests, see also: <https://github.com/ethereum/go-ethereum/blob/380688c636a654becc8f114438c2a5d93d2db032/core/state_transition.go#L145-L148>:
756    ///
757    ///  - `disable_eip3607` is set to `true`
758    ///  - `disable_base_fee` is set to `true`
759    ///  - `nonce` is set to `None`
760    ///
761    /// In addition, this changes the block's gas limit to the configured [`Self::call_gas_limit`].
762    #[expect(clippy::type_complexity)]
763    fn prepare_call_env<DB>(
764        &self,
765        mut evm_env: EvmEnvFor<Self::Evm>,
766        mut request: RpcTxReq<<Self::RpcConvert as RpcConvert>::Network>,
767        db: &mut DB,
768        overrides: EvmOverrides,
769    ) -> Result<(EvmEnvFor<Self::Evm>, TxEnvFor<Self::Evm>), Self::Error>
770    where
771        DB: Database + DatabaseCommit + OverrideBlockHashes,
772        EthApiError: From<<DB as Database>::Error>,
773    {
774        // track whether the request has a gas limit set
775        let request_has_gas_limit = request.as_ref().gas_limit().is_some();
776
777        if let Some(requested_gas) = request.as_ref().gas_limit() {
778            let global_gas_cap = self.call_gas_limit();
779            if global_gas_cap != 0 && global_gas_cap < requested_gas {
780                warn!(target: "rpc::eth::call", ?request, ?global_gas_cap, "Capping gas limit to global gas cap");
781                request.as_mut().set_gas_limit(global_gas_cap);
782            }
783        } else {
784            // cap request's gas limit to call gas limit
785            request.as_mut().set_gas_limit(self.call_gas_limit());
786        }
787
788        // Disable block gas limit check to allow executing transactions with higher gas limit (call
789        // gas limit): https://github.com/paradigmxyz/reth/issues/18577
790        evm_env.cfg_env.disable_block_gas_limit = true;
791
792        // Disabled because eth_call is sometimes used with eoa senders
793        // See <https://github.com/paradigmxyz/reth/issues/1959>
794        evm_env.cfg_env.disable_eip3607 = true;
795
796        // The basefee should be ignored for eth_call
797        // See:
798        // <https://github.com/ethereum/go-ethereum/blob/ee8e83fa5f6cb261dad2ed0a7bbcde4930c41e6c/internal/ethapi/api.go#L985>
799        evm_env.cfg_env.disable_base_fee = true;
800
801        // Disable EIP-7825 transaction gas limit to support larger transactions
802        evm_env.cfg_env.tx_gas_limit_cap = Some(u64::MAX);
803
804        // Disable additional fee charges, e.g. opstack operator fee charge
805        // See:
806        // <https://github.com/paradigmxyz/reth/issues/18470>
807        evm_env.cfg_env.disable_fee_charge = true;
808
809        evm_env.cfg_env.memory_limit = self.evm_memory_limit();
810
811        // set nonce to None so that the correct nonce is chosen by the EVM
812        request.as_mut().take_nonce();
813
814        if let Some(block_overrides) = overrides.block {
815            apply_block_overrides(*block_overrides, db, evm_env.block_env.inner_mut());
816        }
817        if let Some(state_overrides) = overrides.state {
818            apply_state_overrides(state_overrides, db)
819                .map_err(EthApiError::from_state_overrides_err)?;
820        }
821
822        let mut tx_env = self.create_txn_env(&evm_env, request, &mut *db)?;
823
824        // lower the basefee to 0 to avoid breaking EVM invariants (basefee < gasprice): <https://github.com/ethereum/go-ethereum/blob/355228b011ef9a85ebc0f21e7196f892038d49f0/internal/ethapi/api.go#L700-L704>
825        if tx_env.gas_price() == 0 {
826            evm_env.block_env.inner_mut().basefee = 0;
827        }
828
829        if !request_has_gas_limit {
830            // No gas limit was provided in the request, so we need to cap the transaction gas limit
831            if tx_env.gas_price() > 0 {
832                // If gas price is specified, cap transaction gas limit with caller allowance
833                trace!(target: "rpc::eth::call", ?tx_env, "Applying gas limit cap with caller allowance");
834                let cap = self.caller_gas_allowance(db, &evm_env, &tx_env)?;
835                // ensure we cap gas_limit to the block's
836                tx_env.set_gas_limit(cap.min(evm_env.block_env.gas_limit()));
837            }
838        }
839
840        Ok((evm_env, tx_env))
841    }
842}