reth_rpc/eth/helpers/
call.rs

1//! Contains RPC handler implementations specific to endpoints that call/execute within evm.
2
3use crate::EthApi;
4use alloy_evm::block::BlockExecutorFactory;
5use alloy_primitives::{TxKind, U256};
6use alloy_rpc_types::TransactionRequest;
7use alloy_signer::Either;
8use reth_evm::{ConfigureEvm, EvmEnv, EvmFactory, SpecFor};
9use reth_node_api::NodePrimitives;
10use reth_rpc_eth_api::{
11    helpers::{estimate::EstimateCall, Call, EthCall, LoadPendingBlock, LoadState, SpawnBlocking},
12    FromEthApiError, FromEvmError, FullEthApiTypes, IntoEthApiError,
13};
14use reth_rpc_eth_types::{revm_utils::CallFees, EthApiError, RpcInvalidTransactionError};
15use reth_storage_api::{BlockReader, ProviderHeader, ProviderTx};
16use revm::{context::TxEnv, context_interface::Block, Database};
17
18impl<Provider, Pool, Network, EvmConfig> EthCall for EthApi<Provider, Pool, Network, EvmConfig>
19where
20    Self: EstimateCall + LoadPendingBlock + FullEthApiTypes,
21    Provider: BlockReader,
22{
23}
24
25impl<Provider, Pool, Network, EvmConfig> Call for EthApi<Provider, Pool, Network, EvmConfig>
26where
27    Self: LoadState<
28            Evm: ConfigureEvm<
29                BlockExecutorFactory: BlockExecutorFactory<EvmFactory: EvmFactory<Tx = TxEnv>>,
30                Primitives: NodePrimitives<
31                    BlockHeader = ProviderHeader<Self::Provider>,
32                    SignedTx = ProviderTx<Self::Provider>,
33                >,
34            >,
35            Error: FromEvmError<Self::Evm>,
36        > + SpawnBlocking,
37    Provider: BlockReader,
38{
39    #[inline]
40    fn call_gas_limit(&self) -> u64 {
41        self.inner.gas_cap()
42    }
43
44    #[inline]
45    fn max_simulate_blocks(&self) -> u64 {
46        self.inner.max_simulate_blocks()
47    }
48
49    fn create_txn_env(
50        &self,
51        evm_env: &EvmEnv<SpecFor<Self::Evm>>,
52        request: TransactionRequest,
53        mut db: impl Database<Error: Into<EthApiError>>,
54    ) -> Result<TxEnv, Self::Error> {
55        // Ensure that if versioned hashes are set, they're not empty
56        if request.blob_versioned_hashes.as_ref().is_some_and(|hashes| hashes.is_empty()) {
57            return Err(RpcInvalidTransactionError::BlobTransactionMissingBlobHashes.into_eth_err())
58        }
59
60        let tx_type = request.minimal_tx_type() as u8;
61
62        let TransactionRequest {
63            from,
64            to,
65            gas_price,
66            max_fee_per_gas,
67            max_priority_fee_per_gas,
68            gas,
69            value,
70            input,
71            nonce,
72            access_list,
73            chain_id,
74            blob_versioned_hashes,
75            max_fee_per_blob_gas,
76            authorization_list,
77            transaction_type: _,
78            sidecar: _,
79        } = request;
80
81        let CallFees { max_priority_fee_per_gas, gas_price, max_fee_per_blob_gas } =
82            CallFees::ensure_fees(
83                gas_price.map(U256::from),
84                max_fee_per_gas.map(U256::from),
85                max_priority_fee_per_gas.map(U256::from),
86                U256::from(evm_env.block_env.basefee),
87                blob_versioned_hashes.as_deref(),
88                max_fee_per_blob_gas.map(U256::from),
89                evm_env.block_env.blob_gasprice().map(U256::from),
90            )?;
91
92        let gas_limit = gas.unwrap_or(
93            // Use maximum allowed gas limit. The reason for this
94            // is that both Erigon and Geth use pre-configured gas cap even if
95            // it's possible to derive the gas limit from the block:
96            // <https://github.com/ledgerwatch/erigon/blob/eae2d9a79cb70dbe30b3a6b79c436872e4605458/cmd/rpcdaemon/commands/trace_adhoc.go#L956
97            // https://github.com/ledgerwatch/erigon/blob/eae2d9a79cb70dbe30b3a6b79c436872e4605458/eth/ethconfig/config.go#L94>
98            evm_env.block_env.gas_limit,
99        );
100
101        let chain_id = chain_id.unwrap_or(evm_env.cfg_env.chain_id);
102
103        let caller = from.unwrap_or_default();
104
105        let nonce = if let Some(nonce) = nonce {
106            nonce
107        } else {
108            db.basic(caller).map_err(Into::into)?.map(|acc| acc.nonce).unwrap_or_default()
109        };
110
111        let env = TxEnv {
112            tx_type,
113            gas_limit,
114            nonce,
115            caller,
116            gas_price: gas_price.saturating_to(),
117            gas_priority_fee: max_priority_fee_per_gas.map(|v| v.saturating_to()),
118            kind: to.unwrap_or(TxKind::Create),
119            value: value.unwrap_or_default(),
120            data: input
121                .try_into_unique_input()
122                .map_err(Self::Error::from_eth_err)?
123                .unwrap_or_default(),
124            chain_id: Some(chain_id),
125            access_list: access_list.unwrap_or_default(),
126            // EIP-4844 fields
127            blob_hashes: blob_versioned_hashes.unwrap_or_default(),
128            max_fee_per_blob_gas: max_fee_per_blob_gas
129                .map(|v| v.saturating_to())
130                .unwrap_or_default(),
131            // EIP-7702 fields
132            authorization_list: authorization_list
133                .unwrap_or_default()
134                .into_iter()
135                .map(Either::Left)
136                .collect(),
137        };
138
139        Ok(env)
140    }
141}
142
143impl<Provider, Pool, Network, EvmConfig> EstimateCall for EthApi<Provider, Pool, Network, EvmConfig>
144where
145    Self: Call,
146    Provider: BlockReader,
147{
148}