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 reth_evm::{
18 revm::context_interface::{either::Either, Block},
19 ConfigureEvm, SpecFor, TxEnvFor,
20};
21use reth_primitives_traits::{
22 HeaderTy, NodePrimitives, SealedHeader, SealedHeaderFor, TransactionMeta, TxTy,
23};
24use revm_context::{BlockEnv, CfgEnv, TxEnv};
25use std::{borrow::Cow, convert::Infallible, error::Error, fmt::Debug, marker::PhantomData};
26use thiserror::Error;
27
28#[derive(Debug, Clone)]
30pub struct ConvertReceiptInput<'a, N: NodePrimitives> {
31 pub receipt: Cow<'a, N::Receipt>,
33 pub tx: Recovered<&'a N::SignedTx>,
35 pub gas_used: u64,
37 pub next_log_index: usize,
39 pub meta: TransactionMeta,
41}
42
43pub trait ReceiptConverter<N: NodePrimitives>: Debug + 'static {
45 type RpcReceipt;
47
48 type Error;
50
51 fn convert_receipts(
54 &self,
55 receipts: Vec<ConvertReceiptInput<'_, N>>,
56 ) -> Result<Vec<Self::RpcReceipt>, Self::Error>;
57}
58
59pub trait HeaderConverter<Consensus, Rpc>: Debug + Send + Sync + Unpin + Clone + 'static {
61 fn convert_header(&self, header: SealedHeader<Consensus>, block_size: usize) -> Rpc;
63}
64
65impl<Consensus, Rpc> HeaderConverter<Consensus, Rpc> for ()
68where
69 Rpc: FromConsensusHeader<Consensus>,
70{
71 fn convert_header(&self, header: SealedHeader<Consensus>, block_size: usize) -> Rpc {
72 Rpc::from_consensus_header(header, block_size)
73 }
74}
75
76pub trait FromConsensusHeader<T> {
78 fn from_consensus_header(header: SealedHeader<T>, block_size: usize) -> Self;
80}
81
82impl<T: Sealable> FromConsensusHeader<T> for alloy_rpc_types_eth::Header<T> {
83 fn from_consensus_header(header: SealedHeader<T>, block_size: usize) -> Self {
84 Self::from_consensus(header.into(), None, Some(U256::from(block_size)))
85 }
86}
87
88pub trait RpcConvert: Send + Sync + Unpin + Clone + Debug + 'static {
97 type Primitives: NodePrimitives;
99
100 type Network: RpcTypes + Send + Sync + Unpin + Clone + Debug;
103
104 type TxEnv;
106
107 type Error: error::Error + Into<jsonrpsee_types::ErrorObject<'static>>;
109
110 type Spec;
112
113 fn fill_pending(
117 &self,
118 tx: Recovered<TxTy<Self::Primitives>>,
119 ) -> Result<RpcTransaction<Self::Network>, Self::Error> {
120 self.fill(tx, TransactionInfo::default())
121 }
122
123 fn fill(
129 &self,
130 tx: Recovered<TxTy<Self::Primitives>>,
131 tx_info: TransactionInfo,
132 ) -> Result<RpcTransaction<Self::Network>, Self::Error>;
133
134 fn build_simulate_v1_transaction(
137 &self,
138 request: RpcTxReq<Self::Network>,
139 ) -> Result<TxTy<Self::Primitives>, Self::Error>;
140
141 fn tx_env(
144 &self,
145 request: RpcTxReq<Self::Network>,
146 cfg_env: &CfgEnv<Self::Spec>,
147 block_env: &BlockEnv,
148 ) -> Result<Self::TxEnv, Self::Error>;
149
150 fn convert_receipts(
153 &self,
154 receipts: Vec<ConvertReceiptInput<'_, Self::Primitives>>,
155 ) -> Result<Vec<RpcReceipt<Self::Network>>, Self::Error>;
156
157 fn convert_header(
159 &self,
160 header: SealedHeaderFor<Self::Primitives>,
161 block_size: usize,
162 ) -> Result<RpcHeader<Self::Network>, Self::Error>;
163}
164
165pub trait IntoRpcTx<T> {
177 type TxInfo;
180
181 fn into_rpc_tx(self, signer: Address, tx_info: Self::TxInfo) -> T;
184}
185
186pub trait FromConsensusTx<T> {
198 type TxInfo;
201
202 fn from_consensus_tx(tx: T, signer: Address, tx_info: Self::TxInfo) -> Self;
205}
206
207impl<TxIn: alloy_consensus::Transaction, T: alloy_consensus::Transaction + From<TxIn>>
208 FromConsensusTx<TxIn> for Transaction<T>
209{
210 type TxInfo = TransactionInfo;
211
212 fn from_consensus_tx(tx: TxIn, signer: Address, tx_info: Self::TxInfo) -> Self {
213 Self::from_transaction(Recovered::new_unchecked(tx.into(), signer), tx_info)
214 }
215}
216
217impl<ConsensusTx, RpcTx> IntoRpcTx<RpcTx> for ConsensusTx
218where
219 ConsensusTx: alloy_consensus::Transaction,
220 RpcTx: FromConsensusTx<Self>,
221{
222 type TxInfo = RpcTx::TxInfo;
223
224 fn into_rpc_tx(self, signer: Address, tx_info: Self::TxInfo) -> RpcTx {
225 RpcTx::from_consensus_tx(self, signer, tx_info)
226 }
227}
228
229pub trait RpcTxConverter<Tx, RpcTx, TxInfo>: Clone + Debug + Unpin + Send + Sync + 'static {
247 type Err;
249
250 fn convert_rpc_tx(&self, tx: Tx, signer: Address, tx_info: TxInfo) -> Result<RpcTx, Self::Err>;
254}
255
256impl<Tx, RpcTx> RpcTxConverter<Tx, RpcTx, Tx::TxInfo> for ()
257where
258 Tx: IntoRpcTx<RpcTx>,
259{
260 type Err = Infallible;
261
262 fn convert_rpc_tx(
263 &self,
264 tx: Tx,
265 signer: Address,
266 tx_info: Tx::TxInfo,
267 ) -> Result<RpcTx, Self::Err> {
268 Ok(tx.into_rpc_tx(signer, tx_info))
269 }
270}
271
272impl<Tx, RpcTx, F, TxInfo, E> RpcTxConverter<Tx, RpcTx, TxInfo> for F
273where
274 F: Fn(Tx, Address, TxInfo) -> Result<RpcTx, E> + Clone + Debug + Unpin + Send + Sync + 'static,
275{
276 type Err = E;
277
278 fn convert_rpc_tx(&self, tx: Tx, signer: Address, tx_info: TxInfo) -> Result<RpcTx, Self::Err> {
279 self(tx, signer, tx_info)
280 }
281}
282
283pub trait SimTxConverter<TxReq, SimTx>: Clone + Debug + Unpin + Send + Sync + 'static {
299 type Err: Error;
301
302 fn convert_sim_tx(&self, tx_req: TxReq) -> Result<SimTx, Self::Err>;
306}
307
308impl<TxReq, SimTx> SimTxConverter<TxReq, SimTx> for ()
309where
310 TxReq: TryIntoSimTx<SimTx> + Debug,
311{
312 type Err = ValueError<TxReq>;
313
314 fn convert_sim_tx(&self, tx_req: TxReq) -> Result<SimTx, Self::Err> {
315 tx_req.try_into_sim_tx()
316 }
317}
318
319impl<TxReq, SimTx, F, E> SimTxConverter<TxReq, SimTx> for F
320where
321 TxReq: Debug,
322 E: Error,
323 F: Fn(TxReq) -> Result<SimTx, E> + Clone + Debug + Unpin + Send + Sync + 'static,
324{
325 type Err = E;
326
327 fn convert_sim_tx(&self, tx_req: TxReq) -> Result<SimTx, Self::Err> {
328 self(tx_req)
329 }
330}
331
332pub trait TryIntoSimTx<T>
336where
337 Self: Sized,
338{
339 fn try_into_sim_tx(self) -> Result<T, ValueError<Self>>;
347}
348
349pub trait TxInfoMapper<T> {
351 type Out;
353 type Err;
355
356 fn try_map(&self, tx: &T, tx_info: TransactionInfo) -> Result<Self::Out, Self::Err>;
358}
359
360impl<T> TxInfoMapper<T> for () {
361 type Out = TransactionInfo;
362 type Err = Infallible;
363
364 fn try_map(&self, _tx: &T, tx_info: TransactionInfo) -> Result<Self::Out, Self::Err> {
365 Ok(tx_info)
366 }
367}
368
369impl TryIntoSimTx<EthereumTxEnvelope<TxEip4844>> for TransactionRequest {
370 fn try_into_sim_tx(self) -> Result<EthereumTxEnvelope<TxEip4844>, ValueError<Self>> {
371 Self::build_typed_simulate_transaction(self)
372 }
373}
374
375pub trait TxEnvConverter<TxReq, TxEnv, Spec>:
391 Debug + Send + Sync + Unpin + Clone + 'static
392{
393 type Error;
395
396 fn convert_tx_env(
400 &self,
401 tx_req: TxReq,
402 cfg_env: &CfgEnv<Spec>,
403 block_env: &BlockEnv,
404 ) -> Result<TxEnv, Self::Error>;
405}
406
407impl<TxReq, TxEnv, Spec> TxEnvConverter<TxReq, TxEnv, Spec> for ()
408where
409 TxReq: TryIntoTxEnv<TxEnv>,
410{
411 type Error = TxReq::Err;
412
413 fn convert_tx_env(
414 &self,
415 tx_req: TxReq,
416 cfg_env: &CfgEnv<Spec>,
417 block_env: &BlockEnv,
418 ) -> Result<TxEnv, Self::Error> {
419 tx_req.try_into_tx_env(cfg_env, block_env)
420 }
421}
422
423impl<F, TxReq, TxEnv, E, Spec> TxEnvConverter<TxReq, TxEnv, Spec> for F
425where
426 F: Fn(TxReq, &CfgEnv<Spec>, &BlockEnv) -> Result<TxEnv, E>
427 + Debug
428 + Send
429 + Sync
430 + Unpin
431 + Clone
432 + 'static,
433 TxReq: Clone,
434 E: error::Error + Send + Sync + 'static,
435{
436 type Error = E;
437
438 fn convert_tx_env(
439 &self,
440 tx_req: TxReq,
441 cfg_env: &CfgEnv<Spec>,
442 block_env: &BlockEnv,
443 ) -> Result<TxEnv, Self::Error> {
444 self(tx_req, cfg_env, block_env)
445 }
446}
447
448pub trait TryIntoTxEnv<T> {
452 type Err;
454
455 fn try_into_tx_env<Spec>(
457 self,
458 cfg_env: &CfgEnv<Spec>,
459 block_env: &BlockEnv,
460 ) -> Result<T, Self::Err>;
461}
462
463#[derive(Debug, Error)]
466pub enum EthTxEnvError {
467 #[error(transparent)]
469 CallFees(#[from] CallFeesError),
470 #[error(transparent)]
472 Input(#[from] TransactionInputError),
473}
474
475impl TryIntoTxEnv<TxEnv> for TransactionRequest {
476 type Err = EthTxEnvError;
477
478 fn try_into_tx_env<Spec>(
479 self,
480 cfg_env: &CfgEnv<Spec>,
481 block_env: &BlockEnv,
482 ) -> Result<TxEnv, Self::Err> {
483 if self.blob_versioned_hashes.as_ref().is_some_and(|hashes| hashes.is_empty()) {
485 return Err(CallFeesError::BlobTransactionMissingBlobHashes.into())
486 }
487
488 let tx_type = self.minimal_tx_type() as u8;
489
490 let Self {
491 from,
492 to,
493 gas_price,
494 max_fee_per_gas,
495 max_priority_fee_per_gas,
496 gas,
497 value,
498 input,
499 nonce,
500 access_list,
501 chain_id,
502 blob_versioned_hashes,
503 max_fee_per_blob_gas,
504 authorization_list,
505 transaction_type: _,
506 sidecar: _,
507 } = self;
508
509 let CallFees { max_priority_fee_per_gas, gas_price, max_fee_per_blob_gas } =
510 CallFees::ensure_fees(
511 gas_price.map(U256::from),
512 max_fee_per_gas.map(U256::from),
513 max_priority_fee_per_gas.map(U256::from),
514 U256::from(block_env.basefee),
515 blob_versioned_hashes.as_deref(),
516 max_fee_per_blob_gas.map(U256::from),
517 block_env.blob_gasprice().map(U256::from),
518 )?;
519
520 let gas_limit = gas.unwrap_or(
521 block_env.gas_limit,
527 );
528
529 let chain_id = chain_id.unwrap_or(cfg_env.chain_id);
530
531 let caller = from.unwrap_or_default();
532
533 let nonce = nonce.unwrap_or_default();
534
535 let env = TxEnv {
536 tx_type,
537 gas_limit,
538 nonce,
539 caller,
540 gas_price: gas_price.saturating_to(),
541 gas_priority_fee: max_priority_fee_per_gas.map(|v| v.saturating_to()),
542 kind: to.unwrap_or(TxKind::Create),
543 value: value.unwrap_or_default(),
544 data: input.try_into_unique_input().map_err(EthTxEnvError::from)?.unwrap_or_default(),
545 chain_id: Some(chain_id),
546 access_list: access_list.unwrap_or_default(),
547 blob_hashes: blob_versioned_hashes.unwrap_or_default(),
549 max_fee_per_blob_gas: max_fee_per_blob_gas
550 .map(|v| v.saturating_to())
551 .unwrap_or_default(),
552 authorization_list: authorization_list
554 .unwrap_or_default()
555 .into_iter()
556 .map(Either::Left)
557 .collect(),
558 };
559
560 Ok(env)
561 }
562}
563
564#[derive(Debug, Clone, Error)]
566#[error("Failed to convert transaction into RPC response: {0}")]
567pub struct TransactionConversionError(String);
568
569#[derive(Debug)]
585pub struct RpcConverter<
586 Network,
587 Evm,
588 Receipt,
589 Header = (),
590 Map = (),
591 SimTx = (),
592 RpcTx = (),
593 TxEnv = (),
594> {
595 network: PhantomData<Network>,
596 evm: PhantomData<Evm>,
597 receipt_converter: Receipt,
598 header_converter: Header,
599 mapper: Map,
600 tx_env_converter: TxEnv,
601 sim_tx_converter: SimTx,
602 rpc_tx_converter: RpcTx,
603}
604
605impl<Network, Evm, Receipt> RpcConverter<Network, Evm, Receipt> {
606 pub const fn new(receipt_converter: Receipt) -> Self {
608 Self {
609 network: PhantomData,
610 evm: PhantomData,
611 receipt_converter,
612 header_converter: (),
613 mapper: (),
614 tx_env_converter: (),
615 sim_tx_converter: (),
616 rpc_tx_converter: (),
617 }
618 }
619}
620
621impl<Network, Evm, Receipt, Header, Map, SimTx, RpcTx, TxEnv>
622 RpcConverter<Network, Evm, Receipt, Header, Map, SimTx, RpcTx, TxEnv>
623{
624 pub fn with_network<N>(
626 self,
627 ) -> RpcConverter<N, Evm, Receipt, Header, Map, SimTx, RpcTx, TxEnv> {
628 let Self {
629 receipt_converter,
630 header_converter,
631 mapper,
632 evm,
633 sim_tx_converter,
634 rpc_tx_converter,
635 tx_env_converter,
636 ..
637 } = self;
638 RpcConverter {
639 receipt_converter,
640 header_converter,
641 mapper,
642 network: Default::default(),
643 evm,
644 sim_tx_converter,
645 rpc_tx_converter,
646 tx_env_converter,
647 }
648 }
649
650 pub fn with_tx_env_converter<TxEnvNew>(
652 self,
653 tx_env_converter: TxEnvNew,
654 ) -> RpcConverter<Network, Evm, Receipt, Header, Map, SimTx, RpcTx, TxEnvNew> {
655 let Self {
656 receipt_converter,
657 header_converter,
658 mapper,
659 network,
660 evm,
661 sim_tx_converter,
662 rpc_tx_converter,
663 tx_env_converter: _,
664 ..
665 } = self;
666 RpcConverter {
667 receipt_converter,
668 header_converter,
669 mapper,
670 network,
671 evm,
672 sim_tx_converter,
673 rpc_tx_converter,
674 tx_env_converter,
675 }
676 }
677
678 pub fn with_header_converter<HeaderNew>(
680 self,
681 header_converter: HeaderNew,
682 ) -> RpcConverter<Network, Evm, Receipt, HeaderNew, Map, SimTx, RpcTx, TxEnv> {
683 let Self {
684 receipt_converter,
685 header_converter: _,
686 mapper,
687 network,
688 evm,
689 sim_tx_converter,
690 rpc_tx_converter,
691 tx_env_converter,
692 } = self;
693 RpcConverter {
694 receipt_converter,
695 header_converter,
696 mapper,
697 network,
698 evm,
699 sim_tx_converter,
700 rpc_tx_converter,
701 tx_env_converter,
702 }
703 }
704
705 pub fn with_mapper<MapNew>(
707 self,
708 mapper: MapNew,
709 ) -> RpcConverter<Network, Evm, Receipt, Header, MapNew, SimTx, RpcTx, TxEnv> {
710 let Self {
711 receipt_converter,
712 header_converter,
713 mapper: _,
714 network,
715 evm,
716 sim_tx_converter,
717 rpc_tx_converter,
718 tx_env_converter,
719 } = self;
720 RpcConverter {
721 receipt_converter,
722 header_converter,
723 mapper,
724 network,
725 evm,
726 sim_tx_converter,
727 rpc_tx_converter,
728 tx_env_converter,
729 }
730 }
731
732 pub fn with_sim_tx_converter<SimTxNew>(
734 self,
735 sim_tx_converter: SimTxNew,
736 ) -> RpcConverter<Network, Evm, Receipt, Header, Map, SimTxNew, RpcTx, TxEnv> {
737 let Self {
738 receipt_converter,
739 header_converter,
740 mapper,
741 network,
742 evm,
743 rpc_tx_converter,
744 tx_env_converter,
745 ..
746 } = self;
747 RpcConverter {
748 receipt_converter,
749 header_converter,
750 mapper,
751 network,
752 evm,
753 sim_tx_converter,
754 rpc_tx_converter,
755 tx_env_converter,
756 }
757 }
758
759 pub fn with_rpc_tx_converter<RpcTxNew>(
761 self,
762 rpc_tx_converter: RpcTxNew,
763 ) -> RpcConverter<Network, Evm, Receipt, Header, Map, SimTx, RpcTxNew, TxEnv> {
764 let Self {
765 receipt_converter,
766 header_converter,
767 mapper,
768 network,
769 evm,
770 sim_tx_converter,
771 tx_env_converter,
772 ..
773 } = self;
774 RpcConverter {
775 receipt_converter,
776 header_converter,
777 mapper,
778 network,
779 evm,
780 sim_tx_converter,
781 rpc_tx_converter,
782 tx_env_converter,
783 }
784 }
785}
786
787impl<Network, Evm, Receipt, Header, Map, SimTx, RpcTx, TxEnv> Default
788 for RpcConverter<Network, Evm, Receipt, Header, Map, SimTx, RpcTx, TxEnv>
789where
790 Receipt: Default,
791 Header: Default,
792 Map: Default,
793 SimTx: Default,
794 RpcTx: Default,
795 TxEnv: Default,
796{
797 fn default() -> Self {
798 Self {
799 network: Default::default(),
800 evm: Default::default(),
801 receipt_converter: Default::default(),
802 header_converter: Default::default(),
803 mapper: Default::default(),
804 sim_tx_converter: Default::default(),
805 rpc_tx_converter: Default::default(),
806 tx_env_converter: Default::default(),
807 }
808 }
809}
810
811impl<
812 Network,
813 Evm,
814 Receipt: Clone,
815 Header: Clone,
816 Map: Clone,
817 SimTx: Clone,
818 RpcTx: Clone,
819 TxEnv: Clone,
820 > Clone for RpcConverter<Network, Evm, Receipt, Header, Map, SimTx, RpcTx, TxEnv>
821{
822 fn clone(&self) -> Self {
823 Self {
824 network: Default::default(),
825 evm: Default::default(),
826 receipt_converter: self.receipt_converter.clone(),
827 header_converter: self.header_converter.clone(),
828 mapper: self.mapper.clone(),
829 sim_tx_converter: self.sim_tx_converter.clone(),
830 rpc_tx_converter: self.rpc_tx_converter.clone(),
831 tx_env_converter: self.tx_env_converter.clone(),
832 }
833 }
834}
835
836impl<N, Network, Evm, Receipt, Header, Map, SimTx, RpcTx, TxEnv> RpcConvert
837 for RpcConverter<Network, Evm, Receipt, Header, Map, SimTx, RpcTx, TxEnv>
838where
839 N: NodePrimitives,
840 Network: RpcTypes + Send + Sync + Unpin + Clone + Debug,
841 Evm: ConfigureEvm<Primitives = N> + 'static,
842 Receipt: ReceiptConverter<
843 N,
844 RpcReceipt = RpcReceipt<Network>,
845 Error: From<TransactionConversionError>
846 + From<TxEnv::Error>
847 + From<<Map as TxInfoMapper<TxTy<N>>>::Err>
848 + From<RpcTx::Err>
849 + Error
850 + Unpin
851 + Sync
852 + Send
853 + Into<jsonrpsee_types::ErrorObject<'static>>,
854 > + Send
855 + Sync
856 + Unpin
857 + Clone
858 + Debug,
859 Header: HeaderConverter<HeaderTy<N>, RpcHeader<Network>>,
860 Map: TxInfoMapper<TxTy<N>> + Clone + Debug + Unpin + Send + Sync + 'static,
861 SimTx: SimTxConverter<RpcTxReq<Network>, TxTy<N>>,
862 RpcTx:
863 RpcTxConverter<TxTy<N>, Network::TransactionResponse, <Map as TxInfoMapper<TxTy<N>>>::Out>,
864 TxEnv: TxEnvConverter<RpcTxReq<Network>, TxEnvFor<Evm>, SpecFor<Evm>>,
865{
866 type Primitives = N;
867 type Network = Network;
868 type TxEnv = TxEnvFor<Evm>;
869 type Error = Receipt::Error;
870 type Spec = SpecFor<Evm>;
871
872 fn fill(
873 &self,
874 tx: Recovered<TxTy<N>>,
875 tx_info: TransactionInfo,
876 ) -> Result<Network::TransactionResponse, Self::Error> {
877 let (tx, signer) = tx.into_parts();
878 let tx_info = self.mapper.try_map(&tx, tx_info)?;
879
880 Ok(self.rpc_tx_converter.convert_rpc_tx(tx, signer, tx_info)?)
881 }
882
883 fn build_simulate_v1_transaction(
884 &self,
885 request: RpcTxReq<Network>,
886 ) -> Result<TxTy<N>, Self::Error> {
887 Ok(self
888 .sim_tx_converter
889 .convert_sim_tx(request)
890 .map_err(|e| TransactionConversionError(e.to_string()))?)
891 }
892
893 fn tx_env(
894 &self,
895 request: RpcTxReq<Network>,
896 cfg_env: &CfgEnv<SpecFor<Evm>>,
897 block_env: &BlockEnv,
898 ) -> Result<Self::TxEnv, Self::Error> {
899 self.tx_env_converter.convert_tx_env(request, cfg_env, block_env).map_err(Into::into)
900 }
901
902 fn convert_receipts(
903 &self,
904 receipts: Vec<ConvertReceiptInput<'_, Self::Primitives>>,
905 ) -> Result<Vec<RpcReceipt<Self::Network>>, Self::Error> {
906 self.receipt_converter.convert_receipts(receipts)
907 }
908
909 fn convert_header(
910 &self,
911 header: SealedHeaderFor<Self::Primitives>,
912 block_size: usize,
913 ) -> Result<RpcHeader<Self::Network>, Self::Error> {
914 Ok(self.header_converter.convert_header(header, block_size))
915 }
916}
917
918#[cfg(feature = "op")]
920pub mod op {
921 use super::*;
922 use alloy_consensus::SignableTransaction;
923 use alloy_primitives::{Address, Bytes, Signature};
924 use op_alloy_consensus::{
925 transaction::{OpDepositInfo, OpTransactionInfo},
926 OpTxEnvelope,
927 };
928 use op_alloy_rpc_types::OpTransactionRequest;
929 use op_revm::OpTransaction;
930 use reth_optimism_primitives::DepositReceipt;
931 use reth_primitives_traits::SignedTransaction;
932 use reth_storage_api::{errors::ProviderError, ReceiptProvider};
933
934 pub fn try_into_op_tx_info<Tx, T>(
937 provider: &T,
938 tx: &Tx,
939 tx_info: TransactionInfo,
940 ) -> Result<OpTransactionInfo, ProviderError>
941 where
942 Tx: op_alloy_consensus::OpTransaction + SignedTransaction,
943 T: ReceiptProvider<Receipt: DepositReceipt>,
944 {
945 let deposit_meta = if tx.is_deposit() {
946 provider.receipt_by_hash(*tx.tx_hash())?.and_then(|receipt| {
947 receipt.as_deposit_receipt().map(|receipt| OpDepositInfo {
948 deposit_receipt_version: receipt.deposit_receipt_version,
949 deposit_nonce: receipt.deposit_nonce,
950 })
951 })
952 } else {
953 None
954 }
955 .unwrap_or_default();
956
957 Ok(OpTransactionInfo::new(tx_info, deposit_meta))
958 }
959
960 impl<T: op_alloy_consensus::OpTransaction + alloy_consensus::Transaction> FromConsensusTx<T>
961 for op_alloy_rpc_types::Transaction<T>
962 {
963 type TxInfo = OpTransactionInfo;
964
965 fn from_consensus_tx(tx: T, signer: Address, tx_info: Self::TxInfo) -> Self {
966 Self::from_transaction(Recovered::new_unchecked(tx, signer), tx_info)
967 }
968 }
969
970 impl TryIntoSimTx<OpTxEnvelope> for OpTransactionRequest {
971 fn try_into_sim_tx(self) -> Result<OpTxEnvelope, ValueError<Self>> {
972 let tx = self
973 .build_typed_tx()
974 .map_err(|request| ValueError::new(request, "Required fields missing"))?;
975
976 let signature = Signature::new(Default::default(), Default::default(), false);
978
979 Ok(tx.into_signed(signature).into())
980 }
981 }
982
983 impl TryIntoTxEnv<OpTransaction<TxEnv>> for OpTransactionRequest {
984 type Err = EthTxEnvError;
985
986 fn try_into_tx_env<Spec>(
987 self,
988 cfg_env: &CfgEnv<Spec>,
989 block_env: &BlockEnv,
990 ) -> Result<OpTransaction<TxEnv>, Self::Err> {
991 Ok(OpTransaction {
992 base: self.as_ref().clone().try_into_tx_env(cfg_env, block_env)?,
993 enveloped_tx: Some(Bytes::new()),
994 deposit: Default::default(),
995 })
996 }
997 }
998}
999
1000pub trait TryFromTransactionResponse<N: Network> {
1002 type Error: core::error::Error + Send + Sync + Unpin;
1004
1005 fn from_transaction_response(
1011 transaction_response: N::TransactionResponse,
1012 ) -> Result<Self, Self::Error>
1013 where
1014 Self: Sized;
1015}
1016
1017impl TryFromTransactionResponse<alloy_network::Ethereum>
1018 for reth_ethereum_primitives::TransactionSigned
1019{
1020 type Error = Infallible;
1021
1022 fn from_transaction_response(transaction_response: Transaction) -> Result<Self, Self::Error> {
1023 Ok(transaction_response.into_inner().into())
1024 }
1025}
1026
1027#[cfg(feature = "op")]
1028impl TryFromTransactionResponse<op_alloy_network::Optimism>
1029 for reth_optimism_primitives::OpTransactionSigned
1030{
1031 type Error = Infallible;
1032
1033 fn from_transaction_response(
1034 transaction_response: op_alloy_rpc_types::Transaction,
1035 ) -> Result<Self, Self::Error> {
1036 Ok(transaction_response.inner.into_inner())
1037 }
1038}
1039
1040#[cfg(test)]
1041mod transaction_response_tests {
1042 use super::*;
1043 use alloy_consensus::{transaction::Recovered, EthereumTxEnvelope, Signed, TxLegacy};
1044 use alloy_network::Ethereum;
1045 use alloy_primitives::{Address, Signature, B256, U256};
1046 use alloy_rpc_types_eth::Transaction;
1047
1048 #[test]
1049 fn test_ethereum_transaction_conversion() {
1050 let signed_tx = Signed::new_unchecked(
1051 TxLegacy::default(),
1052 Signature::new(U256::ONE, U256::ONE, false),
1053 B256::ZERO,
1054 );
1055 let envelope = EthereumTxEnvelope::Legacy(signed_tx);
1056
1057 let tx_response = Transaction {
1058 inner: Recovered::new_unchecked(envelope, Address::ZERO),
1059 block_hash: None,
1060 block_number: None,
1061 transaction_index: None,
1062 effective_gas_price: None,
1063 };
1064
1065 let result = <reth_ethereum_primitives::TransactionSigned as TryFromTransactionResponse<
1066 Ethereum,
1067 >>::from_transaction_response(tx_response);
1068 assert!(result.is_ok());
1069 }
1070
1071 #[cfg(feature = "op")]
1072 #[test]
1073 fn test_optimism_transaction_conversion() {
1074 use op_alloy_consensus::OpTxEnvelope;
1075 use op_alloy_network::Optimism;
1076 use reth_optimism_primitives::OpTransactionSigned;
1077
1078 let signed_tx = Signed::new_unchecked(
1079 TxLegacy::default(),
1080 Signature::new(U256::ONE, U256::ONE, false),
1081 B256::ZERO,
1082 );
1083 let envelope = OpTxEnvelope::Legacy(signed_tx);
1084
1085 let inner_tx = Transaction {
1086 inner: Recovered::new_unchecked(envelope, Address::ZERO),
1087 block_hash: None,
1088 block_number: None,
1089 transaction_index: None,
1090 effective_gas_price: None,
1091 };
1092
1093 let tx_response = op_alloy_rpc_types::Transaction {
1094 inner: inner_tx,
1095 deposit_nonce: None,
1096 deposit_receipt_version: None,
1097 };
1098
1099 let result = <OpTransactionSigned as TryFromTransactionResponse<Optimism>>::from_transaction_response(tx_response);
1100
1101 assert!(result.is_ok());
1102 }
1103}