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