Skip to main content

reth_rpc_convert/
transaction.rs

1//! Compatibility functions for rpc `Transaction` type.
2use crate::{
3    CallFees, RpcHeader, RpcLog, RpcReceipt, RpcTransaction, RpcTxReq, RpcTypes, SignableTxRequest,
4};
5use alloy_consensus::{
6    error::ValueError,
7    transaction::{Recovered, TxEip4844Variant},
8    TxEip1559, TxEip2930, TxEip4844, TxEip7702, TxLegacy, TxType,
9};
10use alloy_primitives::{Address, TxKind, U256};
11use alloy_rpc_types_eth::{
12    request::TransactionInputError, Log, TransactionInfo, TransactionRequest,
13};
14use core::error;
15use dyn_clone::DynClone;
16use evm2::ethereum::{RecoveredTxEnvelope, TxEnvelope};
17use reth_evm::{ConfigureEvm, EvmEnv, EvmEnvFor, TxEnvFor};
18use reth_primitives_traits::{
19    BlockTy, HeaderTy, NodePrimitives, SealedBlock, SealedHeader, SealedHeaderFor, TransactionMeta,
20    TxTy,
21};
22use reth_rpc_traits::{FromConsensusHeader, FromConsensusTx, TryIntoSimTx, TxInfoMapper};
23use std::{convert::Infallible, error::Error, fmt, fmt::Debug, marker::PhantomData};
24
25/// Input for [`RpcConvert::convert_receipts`].
26#[derive(Debug, Clone)]
27pub struct ConvertReceiptInput<'a, N: NodePrimitives> {
28    /// Primitive receipt.
29    pub receipt: N::Receipt,
30    /// Transaction the receipt corresponds to.
31    pub tx: Recovered<&'a N::SignedTx>,
32    /// Gas used by the transaction.
33    pub gas_used: u64,
34    /// Number of logs emitted before this transaction.
35    pub next_log_index: usize,
36    /// Metadata for the transaction.
37    pub meta: TransactionMeta,
38}
39
40/// A type that knows how to convert primitive receipts to RPC representations.
41pub trait ReceiptConverter<N: NodePrimitives>: Debug + 'static {
42    /// RPC receipt representation.
43    type RpcReceipt;
44
45    /// RPC log representation.
46    type RpcLog;
47
48    /// Error that may occur during conversion.
49    type Error;
50
51    /// Converts an RPC log using its primitive receipt and block header.
52    fn convert_log(
53        &self,
54        log: Log,
55        receipt: &N::Receipt,
56        header: &SealedHeaderFor<N>,
57    ) -> Result<Self::RpcLog, Self::Error>;
58
59    /// Converts a set of primitive receipts to RPC representations. It is guaranteed that all
60    /// receipts are from the same block.
61    fn convert_receipts(
62        &self,
63        receipts: Vec<ConvertReceiptInput<'_, N>>,
64    ) -> Result<Vec<Self::RpcReceipt>, Self::Error>;
65
66    /// Converts primitive receipts from `block` to RPC representations.
67    fn convert_receipts_with_block(
68        &self,
69        receipts: Vec<ConvertReceiptInput<'_, N>>,
70        _block: &SealedBlock<N::Block>,
71    ) -> Result<Vec<Self::RpcReceipt>, Self::Error> {
72        self.convert_receipts(receipts)
73    }
74}
75
76/// A type that knows how to convert a consensus header into an RPC header.
77pub trait HeaderConverter<Consensus, Rpc>: Send + Sync + Unpin + Clone + 'static {
78    /// An associated RPC conversion error.
79    type Err: error::Error;
80
81    /// Converts a consensus header into an RPC header. `block_size` is set for block responses.
82    fn convert_header(
83        &self,
84        header: SealedHeader<Consensus>,
85        block_size: Option<usize>,
86    ) -> Result<Rpc, Self::Err>;
87}
88
89/// Default implementation of [`HeaderConverter`] that uses [`FromConsensusHeader`] to convert
90/// headers.
91impl<Consensus, Rpc> HeaderConverter<Consensus, Rpc> for ()
92where
93    Rpc: FromConsensusHeader<Consensus>,
94{
95    type Err = Infallible;
96
97    fn convert_header(
98        &self,
99        header: SealedHeader<Consensus>,
100        block_size: Option<usize>,
101    ) -> Result<Rpc, Self::Err> {
102        Ok(Rpc::from_consensus_header(header, block_size))
103    }
104}
105
106impl<Consensus, Rpc, F> HeaderConverter<Consensus, Rpc> for F
107where
108    F: Fn(SealedHeader<Consensus>, Option<usize>) -> Rpc + Send + Sync + Unpin + Clone + 'static,
109{
110    type Err = Infallible;
111
112    fn convert_header(
113        &self,
114        header: SealedHeader<Consensus>,
115        block_size: Option<usize>,
116    ) -> Result<Rpc, Self::Err> {
117        Ok(self(header, block_size))
118    }
119}
120
121/// Responsible for the conversions from and into RPC requests and responses.
122///
123/// The JSON-RPC schema and the Node primitives are configurable using the [`RpcConvert::Network`]
124/// and [`RpcConvert::Primitives`] associated types respectively.
125///
126/// A generic implementation [`RpcConverter`] should be preferred over a manual implementation. As
127/// long as its trait bound requirements are met, the implementation is created automatically and
128/// can be used in RPC method handlers for all the conversions.
129#[auto_impl::auto_impl(&, Box, Arc)]
130pub trait RpcConvert: Send + Sync + Unpin + Debug + DynClone + 'static {
131    /// Associated lower layer consensus types to convert from and into types of [`Self::Network`].
132    type Primitives: NodePrimitives;
133
134    /// The EVM configuration.
135    type Evm: ConfigureEvm<Primitives = Self::Primitives>;
136
137    /// Associated upper layer JSON-RPC API network requests and responses to convert from and into
138    /// types of [`Self::Primitives`].
139    type Network: RpcTypes<TransactionRequest: SignableTxRequest<TxTy<Self::Primitives>>>;
140
141    /// An associated RPC conversion error.
142    type Error: error::Error + Into<jsonrpsee_types::ErrorObject<'static>>;
143
144    /// Wrapper for `fill()` with default `TransactionInfo`
145    /// Create a new rpc transaction result for a _pending_ signed transaction, setting block
146    /// environment related fields to `None`.
147    fn fill_pending(
148        &self,
149        tx: Recovered<TxTy<Self::Primitives>>,
150    ) -> Result<RpcTransaction<Self::Network>, Self::Error> {
151        self.fill(tx, TransactionInfo::default())
152    }
153
154    /// Create a new rpc transaction result for a mined transaction, using the given block hash,
155    /// number, and tx index fields to populate the corresponding fields in the rpc result.
156    ///
157    /// The block hash, number, and tx index fields should be from the original block where the
158    /// transaction was mined.
159    fn fill(
160        &self,
161        tx: Recovered<TxTy<Self::Primitives>>,
162        tx_info: TransactionInfo,
163    ) -> Result<RpcTransaction<Self::Network>, Self::Error>;
164
165    /// Builds a fake transaction from a transaction request for inclusion into block built in
166    /// `eth_simulateV1`.
167    fn build_simulate_v1_transaction(
168        &self,
169        request: RpcTxReq<Self::Network>,
170    ) -> Result<TxTy<Self::Primitives>, Self::Error>;
171
172    /// Creates a transaction environment for execution based on `request` with corresponding
173    /// `cfg_env` and `block_env`.
174    fn tx_env(
175        &self,
176        request: RpcTxReq<Self::Network>,
177        evm_env: &EvmEnvFor<Self::Evm>,
178    ) -> Result<TxEnvFor<Self::Evm>, Self::Error>;
179
180    /// Converts an RPC log using its primitive receipt and block header.
181    fn convert_log(
182        &self,
183        log: Log,
184        receipt: &<Self::Primitives as NodePrimitives>::Receipt,
185        header: &SealedHeaderFor<Self::Primitives>,
186    ) -> Result<RpcLog<Self::Network>, Self::Error>;
187
188    /// Converts a set of primitive receipts to RPC representations. It is guaranteed that all
189    /// receipts are from the same block.
190    fn convert_receipts(
191        &self,
192        receipts: Vec<ConvertReceiptInput<'_, Self::Primitives>>,
193    ) -> Result<Vec<RpcReceipt<Self::Network>>, Self::Error>;
194
195    /// Converts primitive receipts from `block` to RPC representations.
196    fn convert_receipts_with_block(
197        &self,
198        receipts: Vec<ConvertReceiptInput<'_, Self::Primitives>>,
199        block: &SealedBlock<BlockTy<Self::Primitives>>,
200    ) -> Result<Vec<RpcReceipt<Self::Network>>, Self::Error>;
201
202    /// Converts a primitive header to an RPC header.
203    fn convert_header(
204        &self,
205        header: SealedHeaderFor<Self::Primitives>,
206        block_size: Option<usize>,
207    ) -> Result<RpcHeader<Self::Network>, Self::Error>;
208}
209
210dyn_clone::clone_trait_object!(
211    <Primitives, Network, Error, Evm>
212    RpcConvert<Primitives = Primitives, Network = Network, Error = Error, Evm = Evm>
213);
214
215/// Converts `Tx` into `RpcTx`
216///
217/// Where:
218/// * `Tx` is a transaction from the consensus layer.
219/// * `RpcTx` is a transaction response object of the RPC API
220///
221/// The conversion function is accompanied by `signer`'s address and `tx_info` providing extra
222/// context about a transaction in a block.
223///
224/// The `RpcTxConverter` has two blanket implementations:
225/// * `()` assuming `RpcTx` implements [`FromConsensusTx`] and is used as default for
226///   [`RpcConverter`].
227/// * `Fn(Tx, Address, TxInfo) -> RpcTx` and can be applied using
228///   [`RpcConverter::with_rpc_tx_converter`].
229///
230/// One should prefer to implement [`FromConsensusTx`] for `RpcTx` to get the `RpcTxConverter`
231/// implementation for free, thanks to the blanket implementation, unless the conversion requires
232/// more context. For example, some configuration parameters or access handles to database, network,
233/// etc.
234pub trait RpcTxConverter<Tx, RpcTx, TxInfo>: Clone + Unpin + Send + Sync + 'static {
235    /// An associated error that can happen during the conversion.
236    type Err;
237
238    /// Performs the conversion of `tx` from `Tx` into `RpcTx`.
239    ///
240    /// See [`RpcTxConverter`] for more information.
241    fn convert_rpc_tx(&self, tx: Tx, signer: Address, tx_info: TxInfo) -> Result<RpcTx, Self::Err>;
242}
243
244impl<Tx, RpcTx> RpcTxConverter<Tx, RpcTx, <RpcTx as FromConsensusTx<Tx>>::TxInfo> for ()
245where
246    RpcTx: FromConsensusTx<Tx>,
247{
248    type Err = RpcTx::Err;
249
250    fn convert_rpc_tx(
251        &self,
252        tx: Tx,
253        signer: Address,
254        tx_info: <RpcTx as FromConsensusTx<Tx>>::TxInfo,
255    ) -> Result<RpcTx, Self::Err> {
256        RpcTx::from_consensus_tx(tx, signer, tx_info)
257    }
258}
259
260impl<Tx, RpcTx, F, TxInfo, E> RpcTxConverter<Tx, RpcTx, TxInfo> for F
261where
262    F: Fn(Tx, Address, TxInfo) -> Result<RpcTx, E> + Clone + Unpin + Send + Sync + 'static,
263{
264    type Err = E;
265
266    fn convert_rpc_tx(&self, tx: Tx, signer: Address, tx_info: TxInfo) -> Result<RpcTx, Self::Err> {
267        self(tx, signer, tx_info)
268    }
269}
270
271/// Converts `TxReq` into `SimTx`.
272///
273/// Where:
274/// * `TxReq` is a transaction request received from an RPC API
275/// * `SimTx` is the corresponding consensus layer transaction for execution simulation
276///
277/// The `SimTxConverter` has two blanket implementations:
278/// * `()` assuming `TxReq` implements [`TryIntoSimTx`] and is used as default for [`RpcConverter`].
279/// * `Fn(TxReq) -> Result<SimTx, ValueError<TxReq>>` and can be applied using
280///   [`RpcConverter::with_sim_tx_converter`].
281///
282/// One should prefer to implement [`TryIntoSimTx`] for `TxReq` to get the `SimTxConverter`
283/// implementation for free, thanks to the blanket implementation, unless the conversion requires
284/// more context. For example, some configuration parameters or access handles to database, network,
285/// etc.
286pub trait SimTxConverter<TxReq, SimTx>: Clone + Unpin + Send + Sync + 'static {
287    /// An associated error that can occur during the conversion.
288    type Err: Error;
289
290    /// Performs the conversion from `tx_req` into `SimTx`.
291    ///
292    /// See [`SimTxConverter`] for more information.
293    fn convert_sim_tx(&self, tx_req: TxReq) -> Result<SimTx, Self::Err>;
294}
295
296impl<TxReq, SimTx> SimTxConverter<TxReq, SimTx> for ()
297where
298    TxReq: TryIntoSimTx<SimTx> + Debug,
299{
300    type Err = ValueError<TxReq>;
301
302    fn convert_sim_tx(&self, tx_req: TxReq) -> Result<SimTx, Self::Err> {
303        tx_req.try_into_sim_tx()
304    }
305}
306
307impl<TxReq, SimTx, F, E> SimTxConverter<TxReq, SimTx> for F
308where
309    TxReq: Debug,
310    E: Error,
311    F: Fn(TxReq) -> Result<SimTx, E> + Clone + Unpin + Send + Sync + 'static,
312{
313    type Err = E;
314
315    fn convert_sim_tx(&self, tx_req: TxReq) -> Result<SimTx, Self::Err> {
316        self(tx_req)
317    }
318}
319
320/// Converts `TxReq` into `TxEnv`.
321///
322/// Where:
323/// * `TxReq` is a transaction request received from an RPC API
324/// * `TxEnv` is the corresponding transaction environment for execution
325///
326/// The `TxEnvConverter` has two blanket implementations:
327/// * `()` assuming `TxReq` implements [`TryIntoTxEnv`] and is used as default for [`RpcConverter`].
328/// * `Fn(TxReq, &EvmEnvFor<Evm>) -> Result<TxEnv, E>` and can be applied using
329///   [`RpcConverter::with_tx_env_converter`].
330///
331/// One should prefer to implement [`TryIntoTxEnv`] for `TxReq` to get the `TxEnvConverter`
332/// implementation for free, thanks to the blanket implementation, unless the conversion requires
333/// more context. For example, some configuration parameters or access handles to database, network,
334/// etc.
335pub trait TxEnvConverter<TxReq, Evm: ConfigureEvm>:
336    Debug + Send + Sync + Unpin + Clone + 'static
337{
338    /// An associated error that can occur during conversion.
339    type Error;
340
341    /// Converts a rpc transaction request into a transaction environment.
342    ///
343    /// See [`TxEnvConverter`] for more information.
344    fn convert_tx_env(
345        &self,
346        tx_req: TxReq,
347        evm_env: &EvmEnvFor<Evm>,
348    ) -> Result<TxEnvFor<Evm>, Self::Error>;
349}
350
351/// Converts a transaction request into an executable transaction environment.
352pub trait TryIntoTxEnv<T, EvmEnv> {
353    /// Error returned by the conversion.
354    type Err;
355
356    /// Performs the conversion.
357    fn try_into_tx_env(self, evm_env: &EvmEnv) -> Result<T, Self::Err>;
358}
359
360/// Ethereum transaction environment conversion error.
361#[derive(Debug, thiserror::Error)]
362pub enum EthTxEnvError {
363    /// Error while validating transaction fees.
364    #[error(transparent)]
365    CallFees(#[from] crate::CallFeesError),
366    /// Both data and input fields are set and differ.
367    #[error(transparent)]
368    Input(#[from] TransactionInputError),
369    /// Blob transaction has no call target.
370    #[error("blob transaction is a create transaction")]
371    BlobTransactionIsCreate,
372    /// EIP-7702 transaction has no call target.
373    #[error("EIP-7702 authorization list has invalid fields")]
374    AuthorizationListInvalidFields,
375}
376
377impl<Env> TryIntoTxEnv<RecoveredTxEnvelope, Env> for TransactionRequest
378where
379    Env: EvmEnv,
380{
381    type Err = EthTxEnvError;
382
383    fn try_into_tx_env(self, evm_env: &Env) -> Result<RecoveredTxEnvelope, Self::Err> {
384        transaction_request_to_evm2(self, evm_env)
385    }
386}
387
388impl<TxReq, Evm> TxEnvConverter<TxReq, Evm> for ()
389where
390    TxReq: TryIntoTxEnv<TxEnvFor<Evm>, EvmEnvFor<Evm>>,
391    Evm: ConfigureEvm,
392{
393    type Error = TxReq::Err;
394
395    fn convert_tx_env(
396        &self,
397        tx_req: TxReq,
398        evm_env: &EvmEnvFor<Evm>,
399    ) -> Result<TxEnvFor<Evm>, Self::Error> {
400        tx_req.try_into_tx_env(evm_env)
401    }
402}
403
404/// Fills missing fields for simulation using the selected EVM environment.
405///
406/// Explicit fee caps and flat `gasPrice` values are preserved. The returned fees describe
407/// execution pricing, which may be lower than the request's maximum fee cap. No state lookup,
408/// gas estimation, or fee suggestion is performed; callers should resolve the account nonce
409/// before calling this helper when it is needed.
410///
411/// `to` is left unchanged so callers with their own call representation can use this helper.
412/// Recover signatures from the original request before filling defaults.
413pub fn normalize_transaction_request(
414    request: &mut TransactionRequest,
415    evm_env: &impl EvmEnv,
416) -> Result<CallFees, EthTxEnvError> {
417    if request.blob_versioned_hashes.as_ref().is_some_and(Vec::is_empty) {
418        return Err(crate::CallFeesError::BlobTransactionMissingBlobHashes.into())
419    }
420    let input = request.input.clone().try_into_unique_input()?;
421    let fees = CallFees::ensure_fees(
422        request.gas_price.map(U256::from),
423        request.max_fee_per_gas.map(U256::from),
424        request.max_priority_fee_per_gas.map(U256::from),
425        U256::from(evm_env.block_base_fee()),
426        request.blob_versioned_hashes.as_deref(),
427        request.max_fee_per_blob_gas.map(U256::from),
428        Some(evm_env.block_env().blob_basefee),
429    )?;
430
431    if request.gas_price.is_none() {
432        if matches!(request.minimal_tx_type(), TxType::Legacy | TxType::Eip2930) {
433            request.gas_price = Some(fees.gas_price.saturating_to());
434        } else {
435            request.max_fee_per_gas.get_or_insert_with(|| fees.gas_price.saturating_to());
436            request.max_priority_fee_per_gas.get_or_insert(0);
437        }
438    }
439    if let Some(blob_fee) = fees.max_fee_per_blob_gas {
440        request.max_fee_per_blob_gas.get_or_insert_with(|| blob_fee.saturating_to());
441    }
442    request.from.get_or_insert(Address::ZERO);
443    request.nonce.get_or_insert(0);
444    request.gas.get_or_insert_with(|| evm_env.block_env().gas_limit.saturating_to());
445    request.chain_id.get_or_insert_with(|| evm_env.chain_id());
446    request.value.get_or_insert(U256::ZERO);
447    request.input = alloy_rpc_types_eth::TransactionInput::new(input.unwrap_or_default());
448    Ok(fees)
449}
450
451fn transaction_request_to_evm2(
452    mut request: TransactionRequest,
453    evm_env: &impl EvmEnv,
454) -> Result<RecoveredTxEnvelope, EthTxEnvError> {
455    let tx_type = request.minimal_tx_type();
456    let CallFees { max_priority_fee_per_gas, gas_price, max_fee_per_blob_gas } =
457        normalize_transaction_request(&mut request, evm_env)?;
458    let TransactionRequest {
459        from,
460        to,
461        gas_price: _,
462        max_fee_per_gas: _,
463        max_priority_fee_per_gas: _,
464        gas,
465        value,
466        input,
467        nonce,
468        access_list,
469        chain_id,
470        blob_versioned_hashes,
471        max_fee_per_blob_gas: _,
472        authorization_list,
473        transaction_type: _,
474        sidecar: _,
475    } = request;
476    let input = input.try_into_unique_input()?.unwrap_or_default();
477    let caller = from.unwrap_or_default();
478    let to = to.unwrap_or(TxKind::Create);
479    let gas_limit = gas.expect("gas limit normalized");
480    let nonce = nonce.unwrap_or_default();
481    let chain_id = chain_id.expect("chain ID normalized");
482    let value = value.unwrap_or_default();
483    let access_list = access_list.unwrap_or_default();
484    let gas_price = gas_price.saturating_to();
485    // Preserve flat pricing when the RPC request has no priority fee.
486    let max_priority_fee_per_gas =
487        max_priority_fee_per_gas.map(|fee| fee.saturating_to()).unwrap_or(gas_price);
488
489    let tx = match tx_type {
490        TxType::Legacy => TxEnvelope::Legacy(TxLegacy {
491            chain_id: Some(chain_id),
492            nonce,
493            gas_price,
494            gas_limit,
495            to,
496            value,
497            input,
498        }),
499        TxType::Eip2930 => TxEnvelope::Eip2930(TxEip2930 {
500            chain_id,
501            nonce,
502            gas_price,
503            gas_limit,
504            to,
505            value,
506            access_list,
507            input,
508        }),
509        TxType::Eip1559 => TxEnvelope::Eip1559(TxEip1559 {
510            chain_id,
511            nonce,
512            gas_limit,
513            max_fee_per_gas: gas_price,
514            max_priority_fee_per_gas,
515            to,
516            value,
517            access_list,
518            input,
519        }),
520        TxType::Eip4844 => TxEnvelope::Eip4844(TxEip4844Variant::TxEip4844(TxEip4844 {
521            chain_id,
522            nonce,
523            gas_limit,
524            max_fee_per_gas: gas_price,
525            max_priority_fee_per_gas,
526            to: to.to().copied().ok_or(EthTxEnvError::BlobTransactionIsCreate)?,
527            value,
528            access_list,
529            blob_versioned_hashes: blob_versioned_hashes.unwrap_or_default(),
530            max_fee_per_blob_gas: max_fee_per_blob_gas.unwrap_or_default().saturating_to(),
531            input,
532        })),
533        TxType::Eip7702 => TxEnvelope::Eip7702(
534            TxEip7702 {
535                chain_id,
536                nonce,
537                gas_limit,
538                max_fee_per_gas: gas_price,
539                max_priority_fee_per_gas,
540                to: to.to().copied().ok_or(EthTxEnvError::AuthorizationListInvalidFields)?,
541                value,
542                access_list,
543                authorization_list: authorization_list.unwrap_or_default(),
544                input,
545            }
546            .into(),
547        ),
548    };
549    Ok(Recovered::new_unchecked(tx, caller))
550}
551
552/// Converts rpc transaction requests into transaction environment using a closure.
553impl<F, TxReq, E, Evm> TxEnvConverter<TxReq, Evm> for F
554where
555    F: Fn(TxReq, &EvmEnvFor<Evm>) -> Result<TxEnvFor<Evm>, E>
556        + Debug
557        + Send
558        + Sync
559        + Unpin
560        + Clone
561        + 'static,
562    TxReq: Clone,
563    Evm: ConfigureEvm,
564    E: error::Error + Send + Sync + 'static,
565{
566    type Error = E;
567
568    fn convert_tx_env(
569        &self,
570        tx_req: TxReq,
571        evm_env: &EvmEnvFor<Evm>,
572    ) -> Result<TxEnvFor<Evm>, Self::Error> {
573        self(tx_req, evm_env)
574    }
575}
576
577/// Conversion into transaction RPC response failed.
578#[derive(Debug, thiserror::Error)]
579pub enum TransactionConversionError {
580    /// Required fields are missing from the transaction request.
581    #[error("Failed to convert transaction into RPC response: {0}")]
582    FromTxReq(String),
583
584    /// Other conversion errors.
585    #[error("{0}")]
586    Other(String),
587}
588/// Generic RPC response object converter for `Evm` and network `Network`.
589///
590/// The main purpose of this struct is to provide an implementation of [`RpcConvert`] for generic
591/// associated types. This struct can then be used for conversions in RPC method handlers.
592///
593/// An [`RpcConvert`] implementation is generated if the following traits are implemented for the
594/// network and EVM associated primitives:
595/// * [`FromConsensusTx`]: from signed transaction into RPC response object.
596/// * [`TryIntoSimTx`]: from RPC transaction request into a simulated transaction.
597/// * [`TryIntoTxEnv`] or [`TxEnvConverter`]: from RPC transaction request into an executable
598///   transaction.
599/// * [`TxInfoMapper`]: from [`TransactionInfo`] into [`FromConsensusTx::TxInfo`]. Should be
600///   implemented for a dedicated struct that is assigned to `Map`. If [`FromConsensusTx::TxInfo`]
601///   is [`TransactionInfo`] then `()` can be used as `Map` which trivially passes over the input
602///   object.
603pub struct RpcConverter<
604    Network,
605    Evm,
606    Receipt,
607    Header = (),
608    Map = (),
609    SimTx = (),
610    RpcTx = (),
611    TxEnv = (),
612> {
613    network: PhantomData<Network>,
614    evm: PhantomData<Evm>,
615    receipt_converter: Receipt,
616    header_converter: Header,
617    mapper: Map,
618    tx_env_converter: TxEnv,
619    sim_tx_converter: SimTx,
620    rpc_tx_converter: RpcTx,
621}
622
623impl<Network, Evm, Receipt, Header, Map, SimTx, RpcTx, TxEnv> fmt::Debug
624    for RpcConverter<Network, Evm, Receipt, Header, Map, SimTx, RpcTx, TxEnv>
625{
626    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
627        f.debug_struct("RpcConverter").finish_non_exhaustive()
628    }
629}
630
631impl<Network, Evm, Receipt> RpcConverter<Network, Evm, Receipt> {
632    /// Creates a new [`RpcConverter`] with `receipt_converter` and `mapper`.
633    pub const fn new(receipt_converter: Receipt) -> Self {
634        Self {
635            network: PhantomData,
636            evm: PhantomData,
637            receipt_converter,
638            header_converter: (),
639            mapper: (),
640            tx_env_converter: (),
641            sim_tx_converter: (),
642            rpc_tx_converter: (),
643        }
644    }
645}
646
647impl<Network, Evm, Receipt, Header, Map, SimTx, RpcTx, TxEnv>
648    RpcConverter<Network, Evm, Receipt, Header, Map, SimTx, RpcTx, TxEnv>
649{
650    /// Converts the network type
651    pub fn with_network<N>(
652        self,
653    ) -> RpcConverter<N, Evm, Receipt, Header, Map, SimTx, RpcTx, TxEnv> {
654        let Self {
655            receipt_converter,
656            header_converter,
657            mapper,
658            evm,
659            sim_tx_converter,
660            rpc_tx_converter,
661            tx_env_converter,
662            ..
663        } = self;
664        RpcConverter {
665            receipt_converter,
666            header_converter,
667            mapper,
668            network: Default::default(),
669            evm,
670            sim_tx_converter,
671            rpc_tx_converter,
672            tx_env_converter,
673        }
674    }
675
676    /// Converts the transaction environment type.
677    pub fn with_tx_env_converter<TxEnvNew>(
678        self,
679        tx_env_converter: TxEnvNew,
680    ) -> RpcConverter<Network, Evm, Receipt, Header, Map, SimTx, RpcTx, TxEnvNew> {
681        let Self {
682            receipt_converter,
683            header_converter,
684            mapper,
685            network,
686            evm,
687            sim_tx_converter,
688            rpc_tx_converter,
689            ..
690        } = self;
691        RpcConverter {
692            receipt_converter,
693            header_converter,
694            mapper,
695            network,
696            evm,
697            sim_tx_converter,
698            rpc_tx_converter,
699            tx_env_converter,
700        }
701    }
702
703    /// Configures the header converter.
704    pub fn with_header_converter<HeaderNew>(
705        self,
706        header_converter: HeaderNew,
707    ) -> RpcConverter<Network, Evm, Receipt, HeaderNew, Map, SimTx, RpcTx, TxEnv> {
708        let Self {
709            receipt_converter,
710            header_converter: _,
711            mapper,
712            network,
713            evm,
714            sim_tx_converter,
715            rpc_tx_converter,
716            tx_env_converter,
717        } = self;
718        RpcConverter {
719            receipt_converter,
720            header_converter,
721            mapper,
722            network,
723            evm,
724            sim_tx_converter,
725            rpc_tx_converter,
726            tx_env_converter,
727        }
728    }
729
730    /// Configures the mapper.
731    pub fn with_mapper<MapNew>(
732        self,
733        mapper: MapNew,
734    ) -> RpcConverter<Network, Evm, Receipt, Header, MapNew, SimTx, RpcTx, TxEnv> {
735        let Self {
736            receipt_converter,
737            header_converter,
738            mapper: _,
739            network,
740            evm,
741            sim_tx_converter,
742            rpc_tx_converter,
743            tx_env_converter,
744        } = self;
745        RpcConverter {
746            receipt_converter,
747            header_converter,
748            mapper,
749            network,
750            evm,
751            sim_tx_converter,
752            rpc_tx_converter,
753            tx_env_converter,
754        }
755    }
756
757    /// Swaps the simulate transaction converter with `sim_tx_converter`.
758    pub fn with_sim_tx_converter<SimTxNew>(
759        self,
760        sim_tx_converter: SimTxNew,
761    ) -> RpcConverter<Network, Evm, Receipt, Header, Map, SimTxNew, RpcTx, TxEnv> {
762        let Self {
763            receipt_converter,
764            header_converter,
765            mapper,
766            network,
767            evm,
768            rpc_tx_converter,
769            tx_env_converter,
770            ..
771        } = self;
772        RpcConverter {
773            receipt_converter,
774            header_converter,
775            mapper,
776            network,
777            evm,
778            sim_tx_converter,
779            rpc_tx_converter,
780            tx_env_converter,
781        }
782    }
783
784    /// Swaps the RPC transaction converter with `rpc_tx_converter`.
785    pub fn with_rpc_tx_converter<RpcTxNew>(
786        self,
787        rpc_tx_converter: RpcTxNew,
788    ) -> RpcConverter<Network, Evm, Receipt, Header, Map, SimTx, RpcTxNew, TxEnv> {
789        let Self {
790            receipt_converter,
791            header_converter,
792            mapper,
793            network,
794            evm,
795            sim_tx_converter,
796            tx_env_converter,
797            ..
798        } = self;
799        RpcConverter {
800            receipt_converter,
801            header_converter,
802            mapper,
803            network,
804            evm,
805            sim_tx_converter,
806            rpc_tx_converter,
807            tx_env_converter,
808        }
809    }
810
811    /// Converts `self` into a boxed converter.
812    pub fn erased(
813        self,
814    ) -> Box<
815        dyn RpcConvert<
816            Primitives = <Self as RpcConvert>::Primitives,
817            Network = <Self as RpcConvert>::Network,
818            Error = <Self as RpcConvert>::Error,
819            Evm = <Self as RpcConvert>::Evm,
820        >,
821    >
822    where
823        Self: RpcConvert,
824    {
825        Box::new(self)
826    }
827}
828
829impl<Network, Evm, Receipt, Header, Map, SimTx, RpcTx, TxEnv> Default
830    for RpcConverter<Network, Evm, Receipt, Header, Map, SimTx, RpcTx, TxEnv>
831where
832    Receipt: Default,
833    Header: Default,
834    Map: Default,
835    SimTx: Default,
836    RpcTx: Default,
837    TxEnv: Default,
838{
839    fn default() -> Self {
840        Self {
841            network: Default::default(),
842            evm: Default::default(),
843            receipt_converter: Default::default(),
844            header_converter: Default::default(),
845            mapper: Default::default(),
846            sim_tx_converter: Default::default(),
847            rpc_tx_converter: Default::default(),
848            tx_env_converter: Default::default(),
849        }
850    }
851}
852
853impl<
854        Network,
855        Evm,
856        Receipt: Clone,
857        Header: Clone,
858        Map: Clone,
859        SimTx: Clone,
860        RpcTx: Clone,
861        TxEnv: Clone,
862    > Clone for RpcConverter<Network, Evm, Receipt, Header, Map, SimTx, RpcTx, TxEnv>
863{
864    fn clone(&self) -> Self {
865        Self {
866            network: Default::default(),
867            evm: Default::default(),
868            receipt_converter: self.receipt_converter.clone(),
869            header_converter: self.header_converter.clone(),
870            mapper: self.mapper.clone(),
871            sim_tx_converter: self.sim_tx_converter.clone(),
872            rpc_tx_converter: self.rpc_tx_converter.clone(),
873            tx_env_converter: self.tx_env_converter.clone(),
874        }
875    }
876}
877
878impl<N, Network, Evm, Receipt, Header, Map, SimTx, RpcTx, TxEnv> RpcConvert
879    for RpcConverter<Network, Evm, Receipt, Header, Map, SimTx, RpcTx, TxEnv>
880where
881    N: NodePrimitives,
882    Network: RpcTypes<TransactionRequest: SignableTxRequest<N::SignedTx>>,
883    Evm: ConfigureEvm<Primitives = N> + 'static,
884    Receipt: ReceiptConverter<
885            N,
886            RpcReceipt = RpcReceipt<Network>,
887            RpcLog = RpcLog<Network>,
888            Error: From<TransactionConversionError>
889                       + From<TxEnv::Error>
890                       + From<<Map as TxInfoMapper<TxTy<N>>>::Err>
891                       + From<RpcTx::Err>
892                       + From<Header::Err>
893                       + Error
894                       + Unpin
895                       + Sync
896                       + Send
897                       + Into<jsonrpsee_types::ErrorObject<'static>>,
898        > + Send
899        + Sync
900        + Unpin
901        + Clone,
902    Header: HeaderConverter<HeaderTy<N>, RpcHeader<Network>>,
903    Map: TxInfoMapper<TxTy<N>> + Clone + Debug + Unpin + Send + Sync + 'static,
904    SimTx: SimTxConverter<RpcTxReq<Network>, TxTy<N>>,
905    RpcTx:
906        RpcTxConverter<TxTy<N>, Network::TransactionResponse, <Map as TxInfoMapper<TxTy<N>>>::Out>,
907    TxEnv: TxEnvConverter<RpcTxReq<Network>, Evm>,
908{
909    type Primitives = N;
910    type Evm = Evm;
911    type Network = Network;
912    type Error = Receipt::Error;
913
914    fn fill(
915        &self,
916        tx: Recovered<TxTy<N>>,
917        tx_info: TransactionInfo,
918    ) -> Result<Network::TransactionResponse, Self::Error> {
919        let (tx, signer) = tx.into_parts();
920        let tx_info = self.mapper.try_map(&tx, tx_info)?;
921
922        self.rpc_tx_converter.convert_rpc_tx(tx, signer, tx_info).map_err(Into::into)
923    }
924
925    fn build_simulate_v1_transaction(
926        &self,
927        request: RpcTxReq<Network>,
928    ) -> Result<TxTy<N>, Self::Error> {
929        Ok(self
930            .sim_tx_converter
931            .convert_sim_tx(request)
932            .map_err(|e| TransactionConversionError::FromTxReq(e.to_string()))?)
933    }
934
935    fn tx_env(
936        &self,
937        request: RpcTxReq<Network>,
938        evm_env: &EvmEnvFor<Evm>,
939    ) -> Result<TxEnvFor<Evm>, Self::Error> {
940        self.tx_env_converter.convert_tx_env(request, evm_env).map_err(Into::into)
941    }
942
943    fn convert_log(
944        &self,
945        log: Log,
946        receipt: &<Self::Primitives as NodePrimitives>::Receipt,
947        header: &SealedHeaderFor<Self::Primitives>,
948    ) -> Result<RpcLog<Self::Network>, Self::Error> {
949        self.receipt_converter.convert_log(log, receipt, header)
950    }
951
952    fn convert_receipts(
953        &self,
954        receipts: Vec<ConvertReceiptInput<'_, Self::Primitives>>,
955    ) -> Result<Vec<RpcReceipt<Self::Network>>, Self::Error> {
956        self.receipt_converter.convert_receipts(receipts)
957    }
958
959    fn convert_receipts_with_block(
960        &self,
961        receipts: Vec<ConvertReceiptInput<'_, Self::Primitives>>,
962        block: &SealedBlock<BlockTy<Self::Primitives>>,
963    ) -> Result<Vec<RpcReceipt<Self::Network>>, Self::Error> {
964        self.receipt_converter.convert_receipts_with_block(receipts, block)
965    }
966
967    fn convert_header(
968        &self,
969        header: SealedHeaderFor<Self::Primitives>,
970        block_size: Option<usize>,
971    ) -> Result<RpcHeader<Self::Network>, Self::Error> {
972        Ok(self.header_converter.convert_header(header, block_size)?)
973    }
974}