Skip to main content

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