reth_optimism_rpc/eth/
call.rs

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