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_consensus::TxType;
5use alloy_evm::block::BlockExecutorFactory;
6use alloy_primitives::{TxKind, U256};
7use alloy_rpc_types::TransactionRequest;
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 = if request.authorization_list.is_some() {
61            TxType::Eip7702
62        } else if request.sidecar.is_some() || request.max_fee_per_blob_gas.is_some() {
63            TxType::Eip4844
64        } else if request.max_fee_per_gas.is_some() || request.max_priority_fee_per_gas.is_some() {
65            TxType::Eip1559
66        } else if request.access_list.is_some() {
67            TxType::Eip2930
68        } else {
69            TxType::Legacy
70        } as u8;
71
72        let TransactionRequest {
73            from,
74            to,
75            gas_price,
76            max_fee_per_gas,
77            max_priority_fee_per_gas,
78            gas,
79            value,
80            input,
81            nonce,
82            access_list,
83            chain_id,
84            blob_versioned_hashes,
85            max_fee_per_blob_gas,
86            authorization_list,
87            transaction_type: _,
88            sidecar: _,
89        } = request;
90
91        let CallFees { max_priority_fee_per_gas, gas_price, max_fee_per_blob_gas } =
92            CallFees::ensure_fees(
93                gas_price.map(U256::from),
94                max_fee_per_gas.map(U256::from),
95                max_priority_fee_per_gas.map(U256::from),
96                U256::from(evm_env.block_env.basefee),
97                blob_versioned_hashes.as_deref(),
98                max_fee_per_blob_gas.map(U256::from),
99                evm_env.block_env.blob_gasprice().map(U256::from),
100            )?;
101
102        let gas_limit = gas.unwrap_or(
103            // Use maximum allowed gas limit. The reason for this
104            // is that both Erigon and Geth use pre-configured gas cap even if
105            // it's possible to derive the gas limit from the block:
106            // <https://github.com/ledgerwatch/erigon/blob/eae2d9a79cb70dbe30b3a6b79c436872e4605458/cmd/rpcdaemon/commands/trace_adhoc.go#L956
107            // https://github.com/ledgerwatch/erigon/blob/eae2d9a79cb70dbe30b3a6b79c436872e4605458/eth/ethconfig/config.go#L94>
108            evm_env.block_env.gas_limit,
109        );
110
111        let chain_id = chain_id.unwrap_or(evm_env.cfg_env.chain_id);
112
113        let caller = from.unwrap_or_default();
114
115        let nonce = if let Some(nonce) = nonce {
116            nonce
117        } else {
118            db.basic(caller).map_err(Into::into)?.map(|acc| acc.nonce).unwrap_or_default()
119        };
120
121        let env = TxEnv {
122            tx_type,
123            gas_limit,
124            nonce,
125            caller,
126            gas_price: gas_price.saturating_to(),
127            gas_priority_fee: max_priority_fee_per_gas.map(|v| v.saturating_to()),
128            kind: to.unwrap_or(TxKind::Create),
129            value: value.unwrap_or_default(),
130            data: input
131                .try_into_unique_input()
132                .map_err(Self::Error::from_eth_err)?
133                .unwrap_or_default(),
134            chain_id: Some(chain_id),
135            access_list: access_list.unwrap_or_default(),
136            // EIP-4844 fields
137            blob_hashes: blob_versioned_hashes.unwrap_or_default(),
138            max_fee_per_blob_gas: max_fee_per_blob_gas
139                .map(|v| v.saturating_to())
140                .unwrap_or_default(),
141            // EIP-7702 fields
142            authorization_list: authorization_list.unwrap_or_default(),
143        };
144
145        Ok(env)
146    }
147}
148
149impl<Provider, Pool, Network, EvmConfig> EstimateCall for EthApi<Provider, Pool, Network, EvmConfig>
150where
151    Self: Call,
152    Provider: BlockReader,
153{
154}