reth_rpc_convert/
transaction.rs

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