1use crate::{
4 fees::{CallFees, CallFeesError},
5 RpcHeader, RpcReceipt, RpcTransaction, RpcTxReq, RpcTypes,
6};
7use alloy_consensus::{
8 error::ValueError, transaction::Recovered, EthereumTxEnvelope, Sealable, TxEip4844,
9};
10use alloy_network::Network;
11use alloy_primitives::{Address, TxKind, U256};
12use alloy_rpc_types_eth::{
13 request::{TransactionInputError, TransactionRequest},
14 Transaction, TransactionInfo,
15};
16use core::error;
17use dyn_clone::DynClone;
18use reth_evm::{
19 revm::context_interface::{either::Either, Block},
20 ConfigureEvm, SpecFor, TxEnvFor,
21};
22use reth_primitives_traits::{
23 BlockTy, HeaderTy, NodePrimitives, SealedBlock, SealedHeader, SealedHeaderFor, TransactionMeta,
24 TxTy,
25};
26use revm_context::{BlockEnv, CfgEnv, TxEnv};
27use std::{convert::Infallible, error::Error, fmt::Debug, marker::PhantomData};
28use thiserror::Error;
29
30#[derive(Debug, Clone)]
32pub struct ConvertReceiptInput<'a, N: NodePrimitives> {
33 pub receipt: N::Receipt,
35 pub tx: Recovered<&'a N::SignedTx>,
37 pub gas_used: u64,
39 pub next_log_index: usize,
41 pub meta: TransactionMeta,
43}
44
45pub trait ReceiptConverter<N: NodePrimitives>: Debug + 'static {
47 type RpcReceipt;
49
50 type Error;
52
53 fn convert_receipts(
56 &self,
57 receipts: Vec<ConvertReceiptInput<'_, N>>,
58 ) -> Result<Vec<Self::RpcReceipt>, Self::Error>;
59
60 fn convert_receipts_with_block(
63 &self,
64 receipts: Vec<ConvertReceiptInput<'_, N>>,
65 _block: &SealedBlock<N::Block>,
66 ) -> Result<Vec<Self::RpcReceipt>, Self::Error> {
67 self.convert_receipts(receipts)
68 }
69}
70
71pub trait HeaderConverter<Consensus, Rpc>: Debug + Send + Sync + Unpin + Clone + 'static {
73 type Err: error::Error;
75
76 fn convert_header(
78 &self,
79 header: SealedHeader<Consensus>,
80 block_size: usize,
81 ) -> Result<Rpc, Self::Err>;
82}
83
84impl<Consensus, Rpc> HeaderConverter<Consensus, Rpc> for ()
87where
88 Rpc: FromConsensusHeader<Consensus>,
89{
90 type Err = Infallible;
91
92 fn convert_header(
93 &self,
94 header: SealedHeader<Consensus>,
95 block_size: usize,
96 ) -> Result<Rpc, Self::Err> {
97 Ok(Rpc::from_consensus_header(header, block_size))
98 }
99}
100
101pub trait FromConsensusHeader<T> {
103 fn from_consensus_header(header: SealedHeader<T>, block_size: usize) -> Self;
105}
106
107impl<T: Sealable> FromConsensusHeader<T> for alloy_rpc_types_eth::Header<T> {
108 fn from_consensus_header(header: SealedHeader<T>, block_size: usize) -> Self {
109 Self::from_consensus(header.into(), None, Some(U256::from(block_size)))
110 }
111}
112
113#[auto_impl::auto_impl(&, Box, Arc)]
122pub trait RpcConvert: Send + Sync + Unpin + Debug + DynClone + 'static {
123 type Primitives: NodePrimitives;
125
126 type Network: RpcTypes + Send + Sync + Unpin + Clone + Debug;
129
130 type TxEnv;
132
133 type Error: error::Error + Into<jsonrpsee_types::ErrorObject<'static>>;
135
136 type Spec;
138
139 fn fill_pending(
143 &self,
144 tx: Recovered<TxTy<Self::Primitives>>,
145 ) -> Result<RpcTransaction<Self::Network>, Self::Error> {
146 self.fill(tx, TransactionInfo::default())
147 }
148
149 fn fill(
155 &self,
156 tx: Recovered<TxTy<Self::Primitives>>,
157 tx_info: TransactionInfo,
158 ) -> Result<RpcTransaction<Self::Network>, Self::Error>;
159
160 fn build_simulate_v1_transaction(
163 &self,
164 request: RpcTxReq<Self::Network>,
165 ) -> Result<TxTy<Self::Primitives>, Self::Error>;
166
167 fn tx_env(
170 &self,
171 request: RpcTxReq<Self::Network>,
172 cfg_env: &CfgEnv<Self::Spec>,
173 block_env: &BlockEnv,
174 ) -> Result<Self::TxEnv, Self::Error>;
175
176 fn convert_receipts(
179 &self,
180 receipts: Vec<ConvertReceiptInput<'_, Self::Primitives>>,
181 ) -> Result<Vec<RpcReceipt<Self::Network>>, Self::Error>;
182
183 fn convert_receipts_with_block(
188 &self,
189 receipts: Vec<ConvertReceiptInput<'_, Self::Primitives>>,
190 block: &SealedBlock<BlockTy<Self::Primitives>>,
191 ) -> Result<Vec<RpcReceipt<Self::Network>>, Self::Error>;
192
193 fn convert_header(
195 &self,
196 header: SealedHeaderFor<Self::Primitives>,
197 block_size: usize,
198 ) -> Result<RpcHeader<Self::Network>, Self::Error>;
199}
200
201dyn_clone::clone_trait_object!(
202 <Primitives, Network, Error, TxEnv, Spec>
203 RpcConvert<Primitives = Primitives, Network = Network, Error = Error, TxEnv = TxEnv, Spec = Spec>
204);
205
206pub trait IntoRpcTx<T> {
218 type TxInfo;
221 type Err: error::Error;
223
224 fn into_rpc_tx(self, signer: Address, tx_info: Self::TxInfo) -> Result<T, Self::Err>;
227}
228
229pub trait FromConsensusTx<T>: Sized {
241 type TxInfo;
244 type Err: error::Error;
246
247 fn from_consensus_tx(tx: T, signer: Address, tx_info: Self::TxInfo) -> Result<Self, Self::Err>;
250}
251
252impl<TxIn: alloy_consensus::Transaction, T: alloy_consensus::Transaction + From<TxIn>>
253 FromConsensusTx<TxIn> for Transaction<T>
254{
255 type TxInfo = TransactionInfo;
256 type Err = Infallible;
257
258 fn from_consensus_tx(
259 tx: TxIn,
260 signer: Address,
261 tx_info: Self::TxInfo,
262 ) -> Result<Self, Self::Err> {
263 Ok(Self::from_transaction(Recovered::new_unchecked(tx.into(), signer), tx_info))
264 }
265}
266
267impl<ConsensusTx, RpcTx> IntoRpcTx<RpcTx> for ConsensusTx
268where
269 ConsensusTx: alloy_consensus::Transaction,
270 RpcTx: FromConsensusTx<Self>,
271 <RpcTx as FromConsensusTx<ConsensusTx>>::Err: Debug,
272{
273 type TxInfo = RpcTx::TxInfo;
274 type Err = <RpcTx as FromConsensusTx<ConsensusTx>>::Err;
275
276 fn into_rpc_tx(self, signer: Address, tx_info: Self::TxInfo) -> Result<RpcTx, Self::Err> {
277 RpcTx::from_consensus_tx(self, signer, tx_info)
278 }
279}
280
281pub trait RpcTxConverter<Tx, RpcTx, TxInfo>: Clone + Debug + Unpin + Send + Sync + 'static {
299 type Err;
301
302 fn convert_rpc_tx(&self, tx: Tx, signer: Address, tx_info: TxInfo) -> Result<RpcTx, Self::Err>;
306}
307
308impl<Tx, RpcTx> RpcTxConverter<Tx, RpcTx, Tx::TxInfo> for ()
309where
310 Tx: IntoRpcTx<RpcTx>,
311{
312 type Err = Tx::Err;
313
314 fn convert_rpc_tx(
315 &self,
316 tx: Tx,
317 signer: Address,
318 tx_info: Tx::TxInfo,
319 ) -> Result<RpcTx, Self::Err> {
320 tx.into_rpc_tx(signer, tx_info)
321 }
322}
323
324impl<Tx, RpcTx, F, TxInfo, E> RpcTxConverter<Tx, RpcTx, TxInfo> for F
325where
326 F: Fn(Tx, Address, TxInfo) -> Result<RpcTx, E> + Clone + Debug + Unpin + Send + Sync + 'static,
327{
328 type Err = E;
329
330 fn convert_rpc_tx(&self, tx: Tx, signer: Address, tx_info: TxInfo) -> Result<RpcTx, Self::Err> {
331 self(tx, signer, tx_info)
332 }
333}
334
335pub trait SimTxConverter<TxReq, SimTx>: Clone + Debug + Unpin + Send + Sync + 'static {
351 type Err: Error;
353
354 fn convert_sim_tx(&self, tx_req: TxReq) -> Result<SimTx, Self::Err>;
358}
359
360impl<TxReq, SimTx> SimTxConverter<TxReq, SimTx> for ()
361where
362 TxReq: TryIntoSimTx<SimTx> + Debug,
363{
364 type Err = ValueError<TxReq>;
365
366 fn convert_sim_tx(&self, tx_req: TxReq) -> Result<SimTx, Self::Err> {
367 tx_req.try_into_sim_tx()
368 }
369}
370
371impl<TxReq, SimTx, F, E> SimTxConverter<TxReq, SimTx> for F
372where
373 TxReq: Debug,
374 E: Error,
375 F: Fn(TxReq) -> Result<SimTx, E> + Clone + Debug + Unpin + Send + Sync + 'static,
376{
377 type Err = E;
378
379 fn convert_sim_tx(&self, tx_req: TxReq) -> Result<SimTx, Self::Err> {
380 self(tx_req)
381 }
382}
383
384pub trait TryIntoSimTx<T>
388where
389 Self: Sized,
390{
391 fn try_into_sim_tx(self) -> Result<T, ValueError<Self>>;
399}
400
401pub trait TxInfoMapper<T> {
403 type Out;
405 type Err;
407
408 fn try_map(&self, tx: &T, tx_info: TransactionInfo) -> Result<Self::Out, Self::Err>;
410}
411
412impl<T> TxInfoMapper<T> for () {
413 type Out = TransactionInfo;
414 type Err = Infallible;
415
416 fn try_map(&self, _tx: &T, tx_info: TransactionInfo) -> Result<Self::Out, Self::Err> {
417 Ok(tx_info)
418 }
419}
420
421impl TryIntoSimTx<EthereumTxEnvelope<TxEip4844>> for TransactionRequest {
422 fn try_into_sim_tx(self) -> Result<EthereumTxEnvelope<TxEip4844>, ValueError<Self>> {
423 Self::build_typed_simulate_transaction(self)
424 }
425}
426
427pub trait TxEnvConverter<TxReq, TxEnv, Spec>:
443 Debug + Send + Sync + Unpin + Clone + 'static
444{
445 type Error;
447
448 fn convert_tx_env(
452 &self,
453 tx_req: TxReq,
454 cfg_env: &CfgEnv<Spec>,
455 block_env: &BlockEnv,
456 ) -> Result<TxEnv, Self::Error>;
457}
458
459impl<TxReq, TxEnv, Spec> TxEnvConverter<TxReq, TxEnv, Spec> for ()
460where
461 TxReq: TryIntoTxEnv<TxEnv>,
462{
463 type Error = TxReq::Err;
464
465 fn convert_tx_env(
466 &self,
467 tx_req: TxReq,
468 cfg_env: &CfgEnv<Spec>,
469 block_env: &BlockEnv,
470 ) -> Result<TxEnv, Self::Error> {
471 tx_req.try_into_tx_env(cfg_env, block_env)
472 }
473}
474
475impl<F, TxReq, TxEnv, E, Spec> TxEnvConverter<TxReq, TxEnv, Spec> for F
477where
478 F: Fn(TxReq, &CfgEnv<Spec>, &BlockEnv) -> Result<TxEnv, E>
479 + Debug
480 + Send
481 + Sync
482 + Unpin
483 + Clone
484 + 'static,
485 TxReq: Clone,
486 E: error::Error + Send + Sync + 'static,
487{
488 type Error = E;
489
490 fn convert_tx_env(
491 &self,
492 tx_req: TxReq,
493 cfg_env: &CfgEnv<Spec>,
494 block_env: &BlockEnv,
495 ) -> Result<TxEnv, Self::Error> {
496 self(tx_req, cfg_env, block_env)
497 }
498}
499
500pub trait TryIntoTxEnv<T> {
504 type Err;
506
507 fn try_into_tx_env<Spec>(
509 self,
510 cfg_env: &CfgEnv<Spec>,
511 block_env: &BlockEnv,
512 ) -> Result<T, Self::Err>;
513}
514
515#[derive(Debug, Error)]
518pub enum EthTxEnvError {
519 #[error(transparent)]
521 CallFees(#[from] CallFeesError),
522 #[error(transparent)]
524 Input(#[from] TransactionInputError),
525}
526
527impl TryIntoTxEnv<TxEnv> for TransactionRequest {
528 type Err = EthTxEnvError;
529
530 fn try_into_tx_env<Spec>(
531 self,
532 cfg_env: &CfgEnv<Spec>,
533 block_env: &BlockEnv,
534 ) -> Result<TxEnv, Self::Err> {
535 if self.blob_versioned_hashes.as_ref().is_some_and(|hashes| hashes.is_empty()) {
537 return Err(CallFeesError::BlobTransactionMissingBlobHashes.into())
538 }
539
540 let tx_type = self.minimal_tx_type() as u8;
541
542 let Self {
543 from,
544 to,
545 gas_price,
546 max_fee_per_gas,
547 max_priority_fee_per_gas,
548 gas,
549 value,
550 input,
551 nonce,
552 access_list,
553 chain_id,
554 blob_versioned_hashes,
555 max_fee_per_blob_gas,
556 authorization_list,
557 transaction_type: _,
558 sidecar: _,
559 } = self;
560
561 let CallFees { max_priority_fee_per_gas, gas_price, max_fee_per_blob_gas } =
562 CallFees::ensure_fees(
563 gas_price.map(U256::from),
564 max_fee_per_gas.map(U256::from),
565 max_priority_fee_per_gas.map(U256::from),
566 U256::from(block_env.basefee),
567 blob_versioned_hashes.as_deref(),
568 max_fee_per_blob_gas.map(U256::from),
569 block_env.blob_gasprice().map(U256::from),
570 )?;
571
572 let gas_limit = gas.unwrap_or(
573 block_env.gas_limit,
579 );
580
581 let chain_id = chain_id.unwrap_or(cfg_env.chain_id);
582
583 let caller = from.unwrap_or_default();
584
585 let nonce = nonce.unwrap_or_default();
586
587 let env = TxEnv {
588 tx_type,
589 gas_limit,
590 nonce,
591 caller,
592 gas_price: gas_price.saturating_to(),
593 gas_priority_fee: max_priority_fee_per_gas.map(|v| v.saturating_to()),
594 kind: to.unwrap_or(TxKind::Create),
595 value: value.unwrap_or_default(),
596 data: input.try_into_unique_input().map_err(EthTxEnvError::from)?.unwrap_or_default(),
597 chain_id: Some(chain_id),
598 access_list: access_list.unwrap_or_default(),
599 blob_hashes: blob_versioned_hashes.unwrap_or_default(),
601 max_fee_per_blob_gas: max_fee_per_blob_gas
602 .map(|v| v.saturating_to())
603 .unwrap_or_default(),
604 authorization_list: authorization_list
606 .unwrap_or_default()
607 .into_iter()
608 .map(Either::Left)
609 .collect(),
610 };
611
612 Ok(env)
613 }
614}
615
616#[derive(Debug, Clone, Error)]
618#[error("Failed to convert transaction into RPC response: {0}")]
619pub struct TransactionConversionError(String);
620
621#[derive(Debug)]
637pub struct RpcConverter<
638 Network,
639 Evm,
640 Receipt,
641 Header = (),
642 Map = (),
643 SimTx = (),
644 RpcTx = (),
645 TxEnv = (),
646> {
647 network: PhantomData<Network>,
648 evm: PhantomData<Evm>,
649 receipt_converter: Receipt,
650 header_converter: Header,
651 mapper: Map,
652 tx_env_converter: TxEnv,
653 sim_tx_converter: SimTx,
654 rpc_tx_converter: RpcTx,
655}
656
657impl<Network, Evm, Receipt> RpcConverter<Network, Evm, Receipt> {
658 pub const fn new(receipt_converter: Receipt) -> Self {
660 Self {
661 network: PhantomData,
662 evm: PhantomData,
663 receipt_converter,
664 header_converter: (),
665 mapper: (),
666 tx_env_converter: (),
667 sim_tx_converter: (),
668 rpc_tx_converter: (),
669 }
670 }
671}
672
673impl<Network, Evm, Receipt, Header, Map, SimTx, RpcTx, TxEnv>
674 RpcConverter<Network, Evm, Receipt, Header, Map, SimTx, RpcTx, TxEnv>
675{
676 pub fn with_network<N>(
678 self,
679 ) -> RpcConverter<N, Evm, Receipt, Header, Map, SimTx, RpcTx, TxEnv> {
680 let Self {
681 receipt_converter,
682 header_converter,
683 mapper,
684 evm,
685 sim_tx_converter,
686 rpc_tx_converter,
687 tx_env_converter,
688 ..
689 } = self;
690 RpcConverter {
691 receipt_converter,
692 header_converter,
693 mapper,
694 network: Default::default(),
695 evm,
696 sim_tx_converter,
697 rpc_tx_converter,
698 tx_env_converter,
699 }
700 }
701
702 pub fn with_tx_env_converter<TxEnvNew>(
704 self,
705 tx_env_converter: TxEnvNew,
706 ) -> RpcConverter<Network, Evm, Receipt, Header, Map, SimTx, RpcTx, TxEnvNew> {
707 let Self {
708 receipt_converter,
709 header_converter,
710 mapper,
711 network,
712 evm,
713 sim_tx_converter,
714 rpc_tx_converter,
715 tx_env_converter: _,
716 ..
717 } = self;
718 RpcConverter {
719 receipt_converter,
720 header_converter,
721 mapper,
722 network,
723 evm,
724 sim_tx_converter,
725 rpc_tx_converter,
726 tx_env_converter,
727 }
728 }
729
730 pub fn with_header_converter<HeaderNew>(
732 self,
733 header_converter: HeaderNew,
734 ) -> RpcConverter<Network, Evm, Receipt, HeaderNew, Map, SimTx, RpcTx, TxEnv> {
735 let Self {
736 receipt_converter,
737 header_converter: _,
738 mapper,
739 network,
740 evm,
741 sim_tx_converter,
742 rpc_tx_converter,
743 tx_env_converter,
744 } = self;
745 RpcConverter {
746 receipt_converter,
747 header_converter,
748 mapper,
749 network,
750 evm,
751 sim_tx_converter,
752 rpc_tx_converter,
753 tx_env_converter,
754 }
755 }
756
757 pub fn with_mapper<MapNew>(
759 self,
760 mapper: MapNew,
761 ) -> RpcConverter<Network, Evm, Receipt, Header, MapNew, SimTx, RpcTx, TxEnv> {
762 let Self {
763 receipt_converter,
764 header_converter,
765 mapper: _,
766 network,
767 evm,
768 sim_tx_converter,
769 rpc_tx_converter,
770 tx_env_converter,
771 } = self;
772 RpcConverter {
773 receipt_converter,
774 header_converter,
775 mapper,
776 network,
777 evm,
778 sim_tx_converter,
779 rpc_tx_converter,
780 tx_env_converter,
781 }
782 }
783
784 pub fn with_sim_tx_converter<SimTxNew>(
786 self,
787 sim_tx_converter: SimTxNew,
788 ) -> RpcConverter<Network, Evm, Receipt, Header, Map, SimTxNew, RpcTx, TxEnv> {
789 let Self {
790 receipt_converter,
791 header_converter,
792 mapper,
793 network,
794 evm,
795 rpc_tx_converter,
796 tx_env_converter,
797 ..
798 } = self;
799 RpcConverter {
800 receipt_converter,
801 header_converter,
802 mapper,
803 network,
804 evm,
805 sim_tx_converter,
806 rpc_tx_converter,
807 tx_env_converter,
808 }
809 }
810
811 pub fn with_rpc_tx_converter<RpcTxNew>(
813 self,
814 rpc_tx_converter: RpcTxNew,
815 ) -> RpcConverter<Network, Evm, Receipt, Header, Map, SimTx, RpcTxNew, TxEnv> {
816 let Self {
817 receipt_converter,
818 header_converter,
819 mapper,
820 network,
821 evm,
822 sim_tx_converter,
823 tx_env_converter,
824 ..
825 } = self;
826 RpcConverter {
827 receipt_converter,
828 header_converter,
829 mapper,
830 network,
831 evm,
832 sim_tx_converter,
833 rpc_tx_converter,
834 tx_env_converter,
835 }
836 }
837
838 #[expect(clippy::type_complexity)]
840 pub fn erased(
841 self,
842 ) -> Box<
843 dyn RpcConvert<
844 Primitives = <Self as RpcConvert>::Primitives,
845 Network = <Self as RpcConvert>::Network,
846 Error = <Self as RpcConvert>::Error,
847 TxEnv = <Self as RpcConvert>::TxEnv,
848 Spec = <Self as RpcConvert>::Spec,
849 >,
850 >
851 where
852 Self: RpcConvert,
853 {
854 Box::new(self)
855 }
856}
857
858impl<Network, Evm, Receipt, Header, Map, SimTx, RpcTx, TxEnv> Default
859 for RpcConverter<Network, Evm, Receipt, Header, Map, SimTx, RpcTx, TxEnv>
860where
861 Receipt: Default,
862 Header: Default,
863 Map: Default,
864 SimTx: Default,
865 RpcTx: Default,
866 TxEnv: Default,
867{
868 fn default() -> Self {
869 Self {
870 network: Default::default(),
871 evm: Default::default(),
872 receipt_converter: Default::default(),
873 header_converter: Default::default(),
874 mapper: Default::default(),
875 sim_tx_converter: Default::default(),
876 rpc_tx_converter: Default::default(),
877 tx_env_converter: Default::default(),
878 }
879 }
880}
881
882impl<
883 Network,
884 Evm,
885 Receipt: Clone,
886 Header: Clone,
887 Map: Clone,
888 SimTx: Clone,
889 RpcTx: Clone,
890 TxEnv: Clone,
891 > Clone for RpcConverter<Network, Evm, Receipt, Header, Map, SimTx, RpcTx, TxEnv>
892{
893 fn clone(&self) -> Self {
894 Self {
895 network: Default::default(),
896 evm: Default::default(),
897 receipt_converter: self.receipt_converter.clone(),
898 header_converter: self.header_converter.clone(),
899 mapper: self.mapper.clone(),
900 sim_tx_converter: self.sim_tx_converter.clone(),
901 rpc_tx_converter: self.rpc_tx_converter.clone(),
902 tx_env_converter: self.tx_env_converter.clone(),
903 }
904 }
905}
906
907impl<N, Network, Evm, Receipt, Header, Map, SimTx, RpcTx, TxEnv> RpcConvert
908 for RpcConverter<Network, Evm, Receipt, Header, Map, SimTx, RpcTx, TxEnv>
909where
910 N: NodePrimitives,
911 Network: RpcTypes + Send + Sync + Unpin + Clone + Debug,
912 Evm: ConfigureEvm<Primitives = N> + 'static,
913 Receipt: ReceiptConverter<
914 N,
915 RpcReceipt = RpcReceipt<Network>,
916 Error: From<TransactionConversionError>
917 + From<TxEnv::Error>
918 + From<<Map as TxInfoMapper<TxTy<N>>>::Err>
919 + From<RpcTx::Err>
920 + From<Header::Err>
921 + Error
922 + Unpin
923 + Sync
924 + Send
925 + Into<jsonrpsee_types::ErrorObject<'static>>,
926 > + Send
927 + Sync
928 + Unpin
929 + Clone
930 + Debug,
931 Header: HeaderConverter<HeaderTy<N>, RpcHeader<Network>>,
932 Map: TxInfoMapper<TxTy<N>> + Clone + Debug + Unpin + Send + Sync + 'static,
933 SimTx: SimTxConverter<RpcTxReq<Network>, TxTy<N>>,
934 RpcTx:
935 RpcTxConverter<TxTy<N>, Network::TransactionResponse, <Map as TxInfoMapper<TxTy<N>>>::Out>,
936 TxEnv: TxEnvConverter<RpcTxReq<Network>, TxEnvFor<Evm>, SpecFor<Evm>>,
937{
938 type Primitives = N;
939 type Network = Network;
940 type TxEnv = TxEnvFor<Evm>;
941 type Error = Receipt::Error;
942 type Spec = SpecFor<Evm>;
943
944 fn fill(
945 &self,
946 tx: Recovered<TxTy<N>>,
947 tx_info: TransactionInfo,
948 ) -> Result<Network::TransactionResponse, Self::Error> {
949 let (tx, signer) = tx.into_parts();
950 let tx_info = self.mapper.try_map(&tx, tx_info)?;
951
952 self.rpc_tx_converter.convert_rpc_tx(tx, signer, tx_info).map_err(Into::into)
953 }
954
955 fn build_simulate_v1_transaction(
956 &self,
957 request: RpcTxReq<Network>,
958 ) -> Result<TxTy<N>, Self::Error> {
959 Ok(self
960 .sim_tx_converter
961 .convert_sim_tx(request)
962 .map_err(|e| TransactionConversionError(e.to_string()))?)
963 }
964
965 fn tx_env(
966 &self,
967 request: RpcTxReq<Network>,
968 cfg_env: &CfgEnv<SpecFor<Evm>>,
969 block_env: &BlockEnv,
970 ) -> Result<Self::TxEnv, Self::Error> {
971 self.tx_env_converter.convert_tx_env(request, cfg_env, block_env).map_err(Into::into)
972 }
973
974 fn convert_receipts(
975 &self,
976 receipts: Vec<ConvertReceiptInput<'_, Self::Primitives>>,
977 ) -> Result<Vec<RpcReceipt<Self::Network>>, Self::Error> {
978 self.receipt_converter.convert_receipts(receipts)
979 }
980
981 fn convert_receipts_with_block(
982 &self,
983 receipts: Vec<ConvertReceiptInput<'_, Self::Primitives>>,
984 block: &SealedBlock<BlockTy<Self::Primitives>>,
985 ) -> Result<Vec<RpcReceipt<Self::Network>>, Self::Error> {
986 self.receipt_converter.convert_receipts_with_block(receipts, block)
987 }
988
989 fn convert_header(
990 &self,
991 header: SealedHeaderFor<Self::Primitives>,
992 block_size: usize,
993 ) -> Result<RpcHeader<Self::Network>, Self::Error> {
994 Ok(self.header_converter.convert_header(header, block_size)?)
995 }
996}
997
998#[cfg(feature = "op")]
1000pub mod op {
1001 use super::*;
1002 use alloy_consensus::SignableTransaction;
1003 use alloy_primitives::{Address, Bytes, Signature};
1004 use op_alloy_consensus::{
1005 transaction::{OpDepositInfo, OpTransactionInfo},
1006 OpTxEnvelope,
1007 };
1008 use op_alloy_rpc_types::OpTransactionRequest;
1009 use op_revm::OpTransaction;
1010 use reth_optimism_primitives::DepositReceipt;
1011 use reth_primitives_traits::SignedTransaction;
1012 use reth_storage_api::{errors::ProviderError, ReceiptProvider};
1013
1014 pub fn try_into_op_tx_info<Tx, T>(
1017 provider: &T,
1018 tx: &Tx,
1019 tx_info: TransactionInfo,
1020 ) -> Result<OpTransactionInfo, ProviderError>
1021 where
1022 Tx: op_alloy_consensus::OpTransaction + SignedTransaction,
1023 T: ReceiptProvider<Receipt: DepositReceipt>,
1024 {
1025 let deposit_meta = if tx.is_deposit() {
1026 provider.receipt_by_hash(*tx.tx_hash())?.and_then(|receipt| {
1027 receipt.as_deposit_receipt().map(|receipt| OpDepositInfo {
1028 deposit_receipt_version: receipt.deposit_receipt_version,
1029 deposit_nonce: receipt.deposit_nonce,
1030 })
1031 })
1032 } else {
1033 None
1034 }
1035 .unwrap_or_default();
1036
1037 Ok(OpTransactionInfo::new(tx_info, deposit_meta))
1038 }
1039
1040 impl<T: op_alloy_consensus::OpTransaction + alloy_consensus::Transaction> FromConsensusTx<T>
1041 for op_alloy_rpc_types::Transaction<T>
1042 {
1043 type TxInfo = OpTransactionInfo;
1044 type Err = Infallible;
1045
1046 fn from_consensus_tx(
1047 tx: T,
1048 signer: Address,
1049 tx_info: Self::TxInfo,
1050 ) -> Result<Self, Self::Err> {
1051 Ok(Self::from_transaction(Recovered::new_unchecked(tx, signer), tx_info))
1052 }
1053 }
1054
1055 impl TryIntoSimTx<OpTxEnvelope> for OpTransactionRequest {
1056 fn try_into_sim_tx(self) -> Result<OpTxEnvelope, ValueError<Self>> {
1057 let tx = self
1058 .build_typed_tx()
1059 .map_err(|request| ValueError::new(request, "Required fields missing"))?;
1060
1061 let signature = Signature::new(Default::default(), Default::default(), false);
1063
1064 Ok(tx.into_signed(signature).into())
1065 }
1066 }
1067
1068 impl TryIntoTxEnv<OpTransaction<TxEnv>> for OpTransactionRequest {
1069 type Err = EthTxEnvError;
1070
1071 fn try_into_tx_env<Spec>(
1072 self,
1073 cfg_env: &CfgEnv<Spec>,
1074 block_env: &BlockEnv,
1075 ) -> Result<OpTransaction<TxEnv>, Self::Err> {
1076 Ok(OpTransaction {
1077 base: self.as_ref().clone().try_into_tx_env(cfg_env, block_env)?,
1078 enveloped_tx: Some(Bytes::new()),
1079 deposit: Default::default(),
1080 })
1081 }
1082 }
1083}
1084
1085pub trait TryFromTransactionResponse<N: Network> {
1087 type Error: core::error::Error + Send + Sync + Unpin;
1089
1090 fn from_transaction_response(
1096 transaction_response: N::TransactionResponse,
1097 ) -> Result<Self, Self::Error>
1098 where
1099 Self: Sized;
1100}
1101
1102impl TryFromTransactionResponse<alloy_network::Ethereum>
1103 for reth_ethereum_primitives::TransactionSigned
1104{
1105 type Error = Infallible;
1106
1107 fn from_transaction_response(transaction_response: Transaction) -> Result<Self, Self::Error> {
1108 Ok(transaction_response.into_inner().into())
1109 }
1110}
1111
1112#[cfg(feature = "op")]
1113impl TryFromTransactionResponse<op_alloy_network::Optimism>
1114 for reth_optimism_primitives::OpTransactionSigned
1115{
1116 type Error = Infallible;
1117
1118 fn from_transaction_response(
1119 transaction_response: op_alloy_rpc_types::Transaction,
1120 ) -> Result<Self, Self::Error> {
1121 Ok(transaction_response.inner.into_inner())
1122 }
1123}
1124
1125#[cfg(test)]
1126mod transaction_response_tests {
1127 use super::*;
1128 use alloy_consensus::{transaction::Recovered, EthereumTxEnvelope, Signed, TxLegacy};
1129 use alloy_network::Ethereum;
1130 use alloy_primitives::{Address, Signature, B256, U256};
1131 use alloy_rpc_types_eth::Transaction;
1132
1133 #[test]
1134 fn test_ethereum_transaction_conversion() {
1135 let signed_tx = Signed::new_unchecked(
1136 TxLegacy::default(),
1137 Signature::new(U256::ONE, U256::ONE, false),
1138 B256::ZERO,
1139 );
1140 let envelope = EthereumTxEnvelope::Legacy(signed_tx);
1141
1142 let tx_response = Transaction {
1143 inner: Recovered::new_unchecked(envelope, Address::ZERO),
1144 block_hash: None,
1145 block_number: None,
1146 transaction_index: None,
1147 effective_gas_price: None,
1148 };
1149
1150 let result = <reth_ethereum_primitives::TransactionSigned as TryFromTransactionResponse<
1151 Ethereum,
1152 >>::from_transaction_response(tx_response);
1153 assert!(result.is_ok());
1154 }
1155
1156 #[cfg(feature = "op")]
1157 mod op {
1158 use super::*;
1159 use crate::transaction::TryIntoTxEnv;
1160 use revm_context::{BlockEnv, CfgEnv};
1161
1162 #[test]
1163 fn test_optimism_transaction_conversion() {
1164 use op_alloy_consensus::OpTxEnvelope;
1165 use op_alloy_network::Optimism;
1166 use reth_optimism_primitives::OpTransactionSigned;
1167
1168 let signed_tx = Signed::new_unchecked(
1169 TxLegacy::default(),
1170 Signature::new(U256::ONE, U256::ONE, false),
1171 B256::ZERO,
1172 );
1173 let envelope = OpTxEnvelope::Legacy(signed_tx);
1174
1175 let inner_tx = Transaction {
1176 inner: Recovered::new_unchecked(envelope, Address::ZERO),
1177 block_hash: None,
1178 block_number: None,
1179 transaction_index: None,
1180 effective_gas_price: None,
1181 };
1182
1183 let tx_response = op_alloy_rpc_types::Transaction {
1184 inner: inner_tx,
1185 deposit_nonce: None,
1186 deposit_receipt_version: None,
1187 };
1188
1189 let result = <OpTransactionSigned as TryFromTransactionResponse<Optimism>>::from_transaction_response(tx_response);
1190
1191 assert!(result.is_ok());
1192 }
1193
1194 #[test]
1195 fn test_op_into_tx_env() {
1196 use op_alloy_rpc_types::OpTransactionRequest;
1197 use op_revm::{transaction::OpTxTr, OpSpecId};
1198 use revm_context::Transaction;
1199
1200 let s = r#"{"from":"0x0000000000000000000000000000000000000000","to":"0x6d362b9c3ab68c0b7c79e8a714f1d7f3af63655f","input":"0x1626ba7ec8ee0d506e864589b799a645ddb88b08f5d39e8049f9f702b3b61fa15e55fc73000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000550000002d6db27c52e3c11c1cf24072004ac75cba49b25bf45f513902e469755e1f3bf2ca8324ad16930b0a965c012a24bb1101f876ebebac047bd3b6bf610205a27171eaaeffe4b5e5589936f4e542d637b627311b0000000000000000000000","data":"0x1626ba7ec8ee0d506e864589b799a645ddb88b08f5d39e8049f9f702b3b61fa15e55fc73000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000550000002d6db27c52e3c11c1cf24072004ac75cba49b25bf45f513902e469755e1f3bf2ca8324ad16930b0a965c012a24bb1101f876ebebac047bd3b6bf610205a27171eaaeffe4b5e5589936f4e542d637b627311b0000000000000000000000","chainId":"0x7a69"}"#;
1201
1202 let req: OpTransactionRequest = serde_json::from_str(s).unwrap();
1203
1204 let cfg = CfgEnv::<OpSpecId>::default();
1205 let block_env = BlockEnv::default();
1206 let tx_env = req.try_into_tx_env(&cfg, &block_env).unwrap();
1207 assert_eq!(tx_env.gas_limit(), block_env.gas_limit);
1208 assert_eq!(tx_env.gas_price(), 0);
1209 assert!(tx_env.enveloped_tx().unwrap().is_empty());
1210 }
1211 }
1212}