reth_seismic_rpc/eth/
call.rs

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