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