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