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