1use super::SignableTransaction;
2use crate::{
3 error::ValueError,
4 transaction::{
5 eip4844::{TxEip4844, TxEip4844Variant},
6 RlpEcdsaEncodableTx, TxHashRef,
7 },
8 Signed, TransactionEnvelope, TxEip1559, TxEip2930, TxEip4844WithSidecar, TxEip7702, TxLegacy,
9};
10use alloy_eips::{eip2718::Encodable2718, eip7594::Encodable7594};
11use alloy_primitives::{Bytes, Signature, B256};
12
13pub type TxEnvelope = EthereumTxEnvelope<TxEip4844Variant>;
25
26impl<T: Encodable7594> EthereumTxEnvelope<TxEip4844Variant<T>> {
27 pub fn try_into_pooled(
32 self,
33 ) -> Result<EthereumTxEnvelope<TxEip4844WithSidecar<T>>, ValueError<Self>> {
34 match self {
35 Self::Legacy(tx) => Ok(tx.into()),
36 Self::Eip2930(tx) => Ok(tx.into()),
37 Self::Eip1559(tx) => Ok(tx.into()),
38 Self::Eip4844(tx) => EthereumTxEnvelope::try_from(tx).map_err(ValueError::convert),
39 Self::Eip7702(tx) => Ok(tx.into()),
40 }
41 }
42}
43
44impl EthereumTxEnvelope<TxEip4844> {
45 pub fn try_into_pooled<T>(
50 self,
51 ) -> Result<EthereumTxEnvelope<TxEip4844WithSidecar<T>>, ValueError<Self>> {
52 match self {
53 Self::Legacy(tx) => Ok(tx.into()),
54 Self::Eip2930(tx) => Ok(tx.into()),
55 Self::Eip1559(tx) => Ok(tx.into()),
56 Self::Eip4844(tx) => {
57 Err(ValueError::new(tx.into(), "pooled transaction requires 4844 sidecar"))
58 }
59 Self::Eip7702(tx) => Ok(tx.into()),
60 }
61 }
62
63 pub fn try_into_pooled_eip4844<T>(
69 self,
70 sidecar: T,
71 ) -> Result<EthereumTxEnvelope<TxEip4844WithSidecar<T>>, ValueError<Self>> {
72 match self {
73 Self::Eip4844(tx) => {
74 Ok(EthereumTxEnvelope::Eip4844(tx.map(|tx| tx.with_sidecar(sidecar))))
75 }
76 this => Err(ValueError::new_static(this, "Expected 4844 transaction")),
77 }
78 }
79}
80
81impl<T> EthereumTxEnvelope<T> {
82 pub fn new_unchecked(
86 transaction: EthereumTypedTransaction<T>,
87 signature: Signature,
88 hash: B256,
89 ) -> Self
90 where
91 T: RlpEcdsaEncodableTx,
92 {
93 Signed::new_unchecked(transaction, signature, hash).into()
94 }
95
96 pub fn new_unhashed(transaction: EthereumTypedTransaction<T>, signature: Signature) -> Self
101 where
102 T: RlpEcdsaEncodableTx + SignableTransaction<Signature>,
103 {
104 transaction.into_signed(signature).into()
105 }
106
107 #[inline]
109 pub fn into_typed_transaction(self) -> EthereumTypedTransaction<T>
110 where
111 T: RlpEcdsaEncodableTx,
112 {
113 match self {
114 Self::Legacy(tx) => EthereumTypedTransaction::Legacy(tx.into_parts().0),
115 Self::Eip2930(tx) => EthereumTypedTransaction::Eip2930(tx.into_parts().0),
116 Self::Eip1559(tx) => EthereumTypedTransaction::Eip1559(tx.into_parts().0),
117 Self::Eip4844(tx) => EthereumTypedTransaction::Eip4844(tx.into_parts().0),
118 Self::Eip7702(tx) => EthereumTypedTransaction::Eip7702(tx.into_parts().0),
119 }
120 }
121
122 #[doc(hidden)]
124 pub fn input_mut(&mut self) -> &mut Bytes
125 where
126 T: AsMut<TxEip4844>,
127 {
128 match self {
129 Self::Eip1559(tx) => &mut tx.tx_mut().input,
130 Self::Eip2930(tx) => &mut tx.tx_mut().input,
131 Self::Legacy(tx) => &mut tx.tx_mut().input,
132 Self::Eip7702(tx) => &mut tx.tx_mut().input,
133 Self::Eip4844(tx) => &mut tx.tx_mut().as_mut().input,
134 }
135 }
136}
137
138impl<T> EthereumTypedTransaction<TxEip4844Variant<T>> {
139 pub fn strip_eip4844_sidecar(self) -> (Self, Option<T>) {
170 self.strip_eip4844_sidecar_into()
171 }
172
173 pub fn strip_eip4844_sidecar_into<U>(
209 self,
210 ) -> (EthereumTypedTransaction<TxEip4844Variant<U>>, Option<T>) {
211 match self {
212 Self::Legacy(tx) => (EthereumTypedTransaction::Legacy(tx), None),
213 Self::Eip2930(tx) => (EthereumTypedTransaction::Eip2930(tx), None),
214 Self::Eip1559(tx) => (EthereumTypedTransaction::Eip1559(tx), None),
215 Self::Eip4844(tx) => {
216 let (tx_variant, sidecar) = tx.strip_sidecar_into();
217 (EthereumTypedTransaction::Eip4844(tx_variant), sidecar)
218 }
219 Self::Eip7702(tx) => (EthereumTypedTransaction::Eip7702(tx), None),
220 }
221 }
222
223 pub fn drop_eip4844_sidecar(self) -> Self {
243 self.strip_eip4844_sidecar().0
244 }
245
246 pub fn drop_eip4844_sidecar_into<U>(self) -> EthereumTypedTransaction<TxEip4844Variant<U>> {
270 self.strip_eip4844_sidecar_into().0
271 }
272}
273
274#[cfg(feature = "kzg")]
275impl EthereumTxEnvelope<TxEip4844WithSidecar<alloy_eips::eip4844::BlobTransactionSidecar>> {
276 pub fn try_into_7594(
301 self,
302 ) -> Result<
303 EthereumTxEnvelope<
304 TxEip4844WithSidecar<alloy_eips::eip7594::BlobTransactionSidecarEip7594>,
305 >,
306 c_kzg::Error,
307 > {
308 self.try_into_7594_with_settings(
309 alloy_eips::eip4844::env_settings::EnvKzgSettings::Default.get(),
310 )
311 }
312
313 pub fn try_into_7594_with_settings(
346 self,
347 settings: &c_kzg::KzgSettings,
348 ) -> Result<
349 EthereumTxEnvelope<
350 TxEip4844WithSidecar<alloy_eips::eip7594::BlobTransactionSidecarEip7594>,
351 >,
352 c_kzg::Error,
353 > {
354 self.try_map_eip4844(|tx| tx.try_into_7594_with_settings(settings))
355 }
356}
357
358#[cfg(feature = "kzg")]
359impl EthereumTxEnvelope<TxEip4844Variant<alloy_eips::eip4844::BlobTransactionSidecar>> {
360 pub fn try_into_7594(
385 self,
386 ) -> Result<
387 EthereumTxEnvelope<TxEip4844Variant<alloy_eips::eip7594::BlobTransactionSidecarEip7594>>,
388 c_kzg::Error,
389 > {
390 self.try_into_7594_with_settings(
391 alloy_eips::eip4844::env_settings::EnvKzgSettings::Default.get(),
392 )
393 }
394
395 pub fn try_into_7594_with_settings(
429 self,
430 settings: &c_kzg::KzgSettings,
431 ) -> Result<
432 EthereumTxEnvelope<TxEip4844Variant<alloy_eips::eip7594::BlobTransactionSidecarEip7594>>,
433 c_kzg::Error,
434 > {
435 self.try_map_eip4844(|tx| tx.try_into_7594_with_settings(settings))
436 }
437}
438
439#[cfg(feature = "kzg")]
440impl TryFrom<EthereumTxEnvelope<TxEip4844WithSidecar<alloy_eips::eip4844::BlobTransactionSidecar>>>
441 for EthereumTxEnvelope<TxEip4844WithSidecar<alloy_eips::eip7594::BlobTransactionSidecarEip7594>>
442{
443 type Error = c_kzg::Error;
444
445 fn try_from(
446 value: EthereumTxEnvelope<
447 TxEip4844WithSidecar<alloy_eips::eip4844::BlobTransactionSidecar>,
448 >,
449 ) -> Result<Self, Self::Error> {
450 value.try_into_7594()
451 }
452}
453
454#[cfg(feature = "kzg")]
455impl TryFrom<EthereumTxEnvelope<TxEip4844Variant<alloy_eips::eip4844::BlobTransactionSidecar>>>
456 for EthereumTxEnvelope<TxEip4844Variant<alloy_eips::eip7594::BlobTransactionSidecarEip7594>>
457{
458 type Error = c_kzg::Error;
459
460 fn try_from(
461 value: EthereumTxEnvelope<TxEip4844Variant<alloy_eips::eip4844::BlobTransactionSidecar>>,
462 ) -> Result<Self, Self::Error> {
463 value.try_into_7594()
464 }
465}
466
467#[derive(Clone, Debug, TransactionEnvelope)]
479#[envelope(
480 alloy_consensus = crate,
481 tx_type_name = TxType,
482 typed = EthereumTypedTransaction,
483 arbitrary_cfg(feature = "arbitrary")
484)]
485#[doc(alias = "TransactionEnvelope")]
486pub enum EthereumTxEnvelope<Eip4844> {
487 #[envelope(ty = 0)]
489 Legacy(Signed<TxLegacy>),
490 #[envelope(ty = 1)]
492 Eip2930(Signed<TxEip2930>),
493 #[envelope(ty = 2)]
495 Eip1559(Signed<TxEip1559>),
496 #[envelope(ty = 3)]
504 Eip4844(Signed<Eip4844>),
505 #[envelope(ty = 4)]
507 Eip7702(Signed<TxEip7702>),
508}
509
510impl<T, Eip4844> From<Signed<T>> for EthereumTxEnvelope<Eip4844>
511where
512 EthereumTypedTransaction<Eip4844>: From<T>,
513 T: RlpEcdsaEncodableTx,
514{
515 fn from(v: Signed<T>) -> Self {
516 let (tx, sig, hash) = v.into_parts();
517 let typed = EthereumTypedTransaction::from(tx);
518 match typed {
519 EthereumTypedTransaction::Legacy(tx_legacy) => {
520 let tx = Signed::new_unchecked(tx_legacy, sig, hash);
521 Self::Legacy(tx)
522 }
523 EthereumTypedTransaction::Eip2930(tx_eip2930) => {
524 let tx = Signed::new_unchecked(tx_eip2930, sig, hash);
525 Self::Eip2930(tx)
526 }
527 EthereumTypedTransaction::Eip1559(tx_eip1559) => {
528 let tx = Signed::new_unchecked(tx_eip1559, sig, hash);
529 Self::Eip1559(tx)
530 }
531 EthereumTypedTransaction::Eip4844(tx_eip4844_variant) => {
532 let tx = Signed::new_unchecked(tx_eip4844_variant, sig, hash);
533 Self::Eip4844(tx)
534 }
535 EthereumTypedTransaction::Eip7702(tx_eip7702) => {
536 let tx = Signed::new_unchecked(tx_eip7702, sig, hash);
537 Self::Eip7702(tx)
538 }
539 }
540 }
541}
542
543impl<Eip4844: RlpEcdsaEncodableTx> From<EthereumTxEnvelope<Eip4844>>
544 for Signed<EthereumTypedTransaction<Eip4844>>
545where
546 EthereumTypedTransaction<Eip4844>: From<Eip4844>,
547{
548 fn from(value: EthereumTxEnvelope<Eip4844>) -> Self {
549 value.into_signed()
550 }
551}
552
553impl<Eip4844> From<(EthereumTypedTransaction<Eip4844>, Signature)> for EthereumTxEnvelope<Eip4844>
554where
555 Eip4844: RlpEcdsaEncodableTx + SignableTransaction<Signature>,
556{
557 fn from(value: (EthereumTypedTransaction<Eip4844>, Signature)) -> Self {
558 value.0.into_signed(value.1).into()
559 }
560}
561
562impl<T> From<EthereumTxEnvelope<TxEip4844WithSidecar<T>>> for EthereumTxEnvelope<TxEip4844> {
563 fn from(value: EthereumTxEnvelope<TxEip4844WithSidecar<T>>) -> Self {
564 value.map_eip4844(|eip4844| eip4844.into())
565 }
566}
567
568impl<T> From<EthereumTxEnvelope<TxEip4844Variant<T>>> for EthereumTxEnvelope<TxEip4844> {
569 fn from(value: EthereumTxEnvelope<TxEip4844Variant<T>>) -> Self {
570 value.map_eip4844(|eip4844| eip4844.into())
571 }
572}
573
574impl<T> From<EthereumTxEnvelope<TxEip4844>> for EthereumTxEnvelope<TxEip4844Variant<T>> {
575 fn from(value: EthereumTxEnvelope<TxEip4844>) -> Self {
576 value.map_eip4844(|eip4844| eip4844.into())
577 }
578}
579
580impl<Eip4844> EthereumTxEnvelope<Eip4844> {
581 pub fn map_eip4844<U>(self, f: impl FnMut(Eip4844) -> U) -> EthereumTxEnvelope<U> {
586 match self {
587 Self::Legacy(tx) => EthereumTxEnvelope::Legacy(tx),
588 Self::Eip2930(tx) => EthereumTxEnvelope::Eip2930(tx),
589 Self::Eip1559(tx) => EthereumTxEnvelope::Eip1559(tx),
590 Self::Eip4844(tx) => EthereumTxEnvelope::Eip4844(tx.map(f)),
591 Self::Eip7702(tx) => EthereumTxEnvelope::Eip7702(tx),
592 }
593 }
594
595 pub fn try_map_eip4844<U, E>(
598 self,
599 f: impl FnOnce(Eip4844) -> Result<U, E>,
600 ) -> Result<EthereumTxEnvelope<U>, E> {
601 match self {
602 Self::Legacy(tx) => Ok(EthereumTxEnvelope::Legacy(tx)),
603 Self::Eip2930(tx) => Ok(EthereumTxEnvelope::Eip2930(tx)),
604 Self::Eip1559(tx) => Ok(EthereumTxEnvelope::Eip1559(tx)),
605 Self::Eip4844(tx) => tx.try_map(f).map(EthereumTxEnvelope::Eip4844),
606 Self::Eip7702(tx) => Ok(EthereumTxEnvelope::Eip7702(tx)),
607 }
608 }
609
610 #[doc(alias = "transaction_type")]
612 pub const fn tx_type(&self) -> TxType {
613 match self {
614 Self::Legacy(_) => TxType::Legacy,
615 Self::Eip2930(_) => TxType::Eip2930,
616 Self::Eip1559(_) => TxType::Eip1559,
617 Self::Eip4844(_) => TxType::Eip4844,
618 Self::Eip7702(_) => TxType::Eip7702,
619 }
620 }
621
622 pub fn into_signed(self) -> Signed<EthereumTypedTransaction<Eip4844>>
624 where
625 EthereumTypedTransaction<Eip4844>: From<Eip4844>,
626 {
627 match self {
628 Self::Legacy(tx) => tx.convert(),
629 Self::Eip2930(tx) => tx.convert(),
630 Self::Eip1559(tx) => tx.convert(),
631 Self::Eip4844(tx) => tx.convert(),
632 Self::Eip7702(tx) => tx.convert(),
633 }
634 }
635}
636
637impl<Eip4844: RlpEcdsaEncodableTx> EthereumTxEnvelope<Eip4844> {
638 #[inline]
640 pub const fn is_legacy(&self) -> bool {
641 matches!(self, Self::Legacy(_))
642 }
643
644 #[inline]
646 pub const fn is_eip2930(&self) -> bool {
647 matches!(self, Self::Eip2930(_))
648 }
649
650 #[inline]
652 pub const fn is_eip1559(&self) -> bool {
653 matches!(self, Self::Eip1559(_))
654 }
655
656 #[inline]
658 pub const fn is_eip4844(&self) -> bool {
659 matches!(self, Self::Eip4844(_))
660 }
661
662 #[inline]
664 pub const fn is_eip7702(&self) -> bool {
665 matches!(self, Self::Eip7702(_))
666 }
667
668 #[inline]
677 pub const fn is_replay_protected(&self) -> bool {
678 match self {
679 Self::Legacy(tx) => tx.tx().chain_id.is_some(),
680 _ => true,
681 }
682 }
683
684 pub const fn as_legacy(&self) -> Option<&Signed<TxLegacy>> {
686 match self {
687 Self::Legacy(tx) => Some(tx),
688 _ => None,
689 }
690 }
691
692 pub const fn as_eip2930(&self) -> Option<&Signed<TxEip2930>> {
694 match self {
695 Self::Eip2930(tx) => Some(tx),
696 _ => None,
697 }
698 }
699
700 pub const fn as_eip1559(&self) -> Option<&Signed<TxEip1559>> {
702 match self {
703 Self::Eip1559(tx) => Some(tx),
704 _ => None,
705 }
706 }
707
708 pub const fn as_eip4844(&self) -> Option<&Signed<Eip4844>> {
710 match self {
711 Self::Eip4844(tx) => Some(tx),
712 _ => None,
713 }
714 }
715
716 pub const fn as_eip7702(&self) -> Option<&Signed<TxEip7702>> {
718 match self {
719 Self::Eip7702(tx) => Some(tx),
720 _ => None,
721 }
722 }
723
724 pub fn try_into_legacy(self) -> Result<Signed<TxLegacy>, ValueError<Self>> {
727 match self {
728 Self::Legacy(tx) => Ok(tx),
729 _ => Err(ValueError::new_static(self, "Expected legacy transaction")),
730 }
731 }
732
733 pub fn try_into_eip2930(self) -> Result<Signed<TxEip2930>, ValueError<Self>> {
736 match self {
737 Self::Eip2930(tx) => Ok(tx),
738 _ => Err(ValueError::new_static(self, "Expected EIP-2930 transaction")),
739 }
740 }
741
742 pub fn try_into_eip1559(self) -> Result<Signed<TxEip1559>, ValueError<Self>> {
745 match self {
746 Self::Eip1559(tx) => Ok(tx),
747 _ => Err(ValueError::new_static(self, "Expected EIP-1559 transaction")),
748 }
749 }
750
751 pub fn try_into_eip4844(self) -> Result<Signed<Eip4844>, ValueError<Self>> {
754 match self {
755 Self::Eip4844(tx) => Ok(tx),
756 _ => Err(ValueError::new_static(self, "Expected EIP-4844 transaction")),
757 }
758 }
759
760 pub fn try_into_eip7702(self) -> Result<Signed<TxEip7702>, ValueError<Self>> {
763 match self {
764 Self::Eip7702(tx) => Ok(tx),
765 _ => Err(ValueError::new_static(self, "Expected EIP-7702 transaction")),
766 }
767 }
768
769 pub fn signature_hash(&self) -> B256
771 where
772 Eip4844: SignableTransaction<Signature>,
773 {
774 match self {
775 Self::Legacy(tx) => tx.signature_hash(),
776 Self::Eip2930(tx) => tx.signature_hash(),
777 Self::Eip1559(tx) => tx.signature_hash(),
778 Self::Eip4844(tx) => tx.signature_hash(),
779 Self::Eip7702(tx) => tx.signature_hash(),
780 }
781 }
782
783 pub const fn signature(&self) -> &Signature {
785 match self {
786 Self::Legacy(tx) => tx.signature(),
787 Self::Eip2930(tx) => tx.signature(),
788 Self::Eip1559(tx) => tx.signature(),
789 Self::Eip4844(tx) => tx.signature(),
790 Self::Eip7702(tx) => tx.signature(),
791 }
792 }
793
794 #[doc(alias = "transaction_hash")]
796 pub fn tx_hash(&self) -> &B256 {
797 match self {
798 Self::Legacy(tx) => tx.hash(),
799 Self::Eip2930(tx) => tx.hash(),
800 Self::Eip1559(tx) => tx.hash(),
801 Self::Eip4844(tx) => tx.hash(),
802 Self::Eip7702(tx) => tx.hash(),
803 }
804 }
805
806 pub fn hash(&self) -> &B256 {
808 match self {
809 Self::Legacy(tx) => tx.hash(),
810 Self::Eip2930(tx) => tx.hash(),
811 Self::Eip1559(tx) => tx.hash(),
812 Self::Eip7702(tx) => tx.hash(),
813 Self::Eip4844(tx) => tx.hash(),
814 }
815 }
816
817 pub fn eip2718_encoded_length(&self) -> usize {
819 match self {
820 Self::Legacy(t) => t.eip2718_encoded_length(),
821 Self::Eip2930(t) => t.eip2718_encoded_length(),
822 Self::Eip1559(t) => t.eip2718_encoded_length(),
823 Self::Eip4844(t) => t.eip2718_encoded_length(),
824 Self::Eip7702(t) => t.eip2718_encoded_length(),
825 }
826 }
827}
828
829impl<Eip4844: RlpEcdsaEncodableTx> TxHashRef for EthereumTxEnvelope<Eip4844> {
830 fn tx_hash(&self) -> &B256 {
831 Self::tx_hash(self)
832 }
833}
834
835#[cfg(any(feature = "secp256k1", feature = "k256"))]
836impl<Eip4844> crate::transaction::SignerRecoverable for EthereumTxEnvelope<Eip4844>
837where
838 Eip4844: RlpEcdsaEncodableTx + SignableTransaction<Signature>,
839{
840 fn recover_signer(&self) -> Result<alloy_primitives::Address, crate::crypto::RecoveryError> {
841 match self {
842 Self::Legacy(tx) => crate::transaction::SignerRecoverable::recover_signer(tx),
843 Self::Eip2930(tx) => crate::transaction::SignerRecoverable::recover_signer(tx),
844 Self::Eip1559(tx) => crate::transaction::SignerRecoverable::recover_signer(tx),
845 Self::Eip4844(tx) => crate::transaction::SignerRecoverable::recover_signer(tx),
846 Self::Eip7702(tx) => crate::transaction::SignerRecoverable::recover_signer(tx),
847 }
848 }
849
850 fn recover_signer_unchecked(
851 &self,
852 ) -> Result<alloy_primitives::Address, crate::crypto::RecoveryError> {
853 match self {
854 Self::Legacy(tx) => crate::transaction::SignerRecoverable::recover_signer_unchecked(tx),
855 Self::Eip2930(tx) => {
856 crate::transaction::SignerRecoverable::recover_signer_unchecked(tx)
857 }
858 Self::Eip1559(tx) => {
859 crate::transaction::SignerRecoverable::recover_signer_unchecked(tx)
860 }
861 Self::Eip4844(tx) => {
862 crate::transaction::SignerRecoverable::recover_signer_unchecked(tx)
863 }
864 Self::Eip7702(tx) => {
865 crate::transaction::SignerRecoverable::recover_signer_unchecked(tx)
866 }
867 }
868 }
869
870 fn recover_with_buf(
871 &self,
872 buf: &mut alloc::vec::Vec<u8>,
873 ) -> Result<alloy_primitives::Address, crate::crypto::RecoveryError> {
874 match self {
875 Self::Legacy(tx) => crate::transaction::SignerRecoverable::recover_with_buf(tx, buf),
876 Self::Eip2930(tx) => crate::transaction::SignerRecoverable::recover_with_buf(tx, buf),
877 Self::Eip1559(tx) => crate::transaction::SignerRecoverable::recover_with_buf(tx, buf),
878 Self::Eip4844(tx) => crate::transaction::SignerRecoverable::recover_with_buf(tx, buf),
879 Self::Eip7702(tx) => crate::transaction::SignerRecoverable::recover_with_buf(tx, buf),
880 }
881 }
882
883 fn recover_unchecked_with_buf(
884 &self,
885 buf: &mut alloc::vec::Vec<u8>,
886 ) -> Result<alloy_primitives::Address, crate::crypto::RecoveryError> {
887 match self {
888 Self::Legacy(tx) => {
889 crate::transaction::SignerRecoverable::recover_unchecked_with_buf(tx, buf)
890 }
891 Self::Eip2930(tx) => {
892 crate::transaction::SignerRecoverable::recover_unchecked_with_buf(tx, buf)
893 }
894 Self::Eip1559(tx) => {
895 crate::transaction::SignerRecoverable::recover_unchecked_with_buf(tx, buf)
896 }
897 Self::Eip4844(tx) => {
898 crate::transaction::SignerRecoverable::recover_unchecked_with_buf(tx, buf)
899 }
900 Self::Eip7702(tx) => {
901 crate::transaction::SignerRecoverable::recover_unchecked_with_buf(tx, buf)
902 }
903 }
904 }
905}
906
907#[cfg(all(feature = "serde", feature = "serde-bincode-compat"))]
909pub mod serde_bincode_compat {
910 use crate::{EthereumTypedTransaction, Signed};
911 use alloc::borrow::Cow;
912 use alloy_primitives::Signature;
913 use serde::{Deserialize, Deserializer, Serialize, Serializer};
914 use serde_with::{DeserializeAs, SerializeAs};
915
916 #[derive(Debug, Serialize, Deserialize)]
932 pub struct EthereumTxEnvelope<'a, Eip4844: Clone = crate::transaction::TxEip4844> {
933 signature: Signature,
935 transaction:
937 crate::serde_bincode_compat::transaction::EthereumTypedTransaction<'a, Eip4844>,
938 }
939
940 impl<'a, T: Clone> From<&'a super::EthereumTxEnvelope<T>> for EthereumTxEnvelope<'a, T> {
941 fn from(value: &'a super::EthereumTxEnvelope<T>) -> Self {
942 match value {
943 super::EthereumTxEnvelope::Legacy(tx) => Self {
944 signature: *tx.signature(),
945 transaction:
946 crate::serde_bincode_compat::transaction::EthereumTypedTransaction::Legacy(
947 tx.tx().into(),
948 ),
949 },
950 super::EthereumTxEnvelope::Eip2930(tx) => Self {
951 signature: *tx.signature(),
952 transaction:
953 crate::serde_bincode_compat::transaction::EthereumTypedTransaction::Eip2930(
954 tx.tx().into(),
955 ),
956 },
957 super::EthereumTxEnvelope::Eip1559(tx) => Self {
958 signature: *tx.signature(),
959 transaction:
960 crate::serde_bincode_compat::transaction::EthereumTypedTransaction::Eip1559(
961 tx.tx().into(),
962 ),
963 },
964 super::EthereumTxEnvelope::Eip4844(tx) => Self {
965 signature: *tx.signature(),
966 transaction:
967 crate::serde_bincode_compat::transaction::EthereumTypedTransaction::Eip4844(
968 Cow::Borrowed(tx.tx()),
969 ),
970 },
971 super::EthereumTxEnvelope::Eip7702(tx) => Self {
972 signature: *tx.signature(),
973 transaction:
974 crate::serde_bincode_compat::transaction::EthereumTypedTransaction::Eip7702(
975 tx.tx().into(),
976 ),
977 },
978 }
979 }
980 }
981
982 impl<'a, T: Clone> From<EthereumTxEnvelope<'a, T>> for super::EthereumTxEnvelope<T> {
983 fn from(value: EthereumTxEnvelope<'a, T>) -> Self {
984 let EthereumTxEnvelope { signature, transaction } = value;
985 let transaction: crate::transaction::typed::EthereumTypedTransaction<T> =
986 transaction.into();
987 match transaction {
988 EthereumTypedTransaction::Legacy(tx) => Signed::new_unhashed(tx, signature).into(),
989 EthereumTypedTransaction::Eip2930(tx) => Signed::new_unhashed(tx, signature).into(),
990 EthereumTypedTransaction::Eip1559(tx) => Signed::new_unhashed(tx, signature).into(),
991 EthereumTypedTransaction::Eip4844(tx) => {
992 Self::Eip4844(Signed::new_unhashed(tx, signature))
993 }
994 EthereumTypedTransaction::Eip7702(tx) => Signed::new_unhashed(tx, signature).into(),
995 }
996 }
997 }
998
999 impl<T: Serialize + Clone> SerializeAs<super::EthereumTxEnvelope<T>> for EthereumTxEnvelope<'_, T> {
1000 fn serialize_as<S>(
1001 source: &super::EthereumTxEnvelope<T>,
1002 serializer: S,
1003 ) -> Result<S::Ok, S::Error>
1004 where
1005 S: Serializer,
1006 {
1007 EthereumTxEnvelope::<'_, T>::from(source).serialize(serializer)
1008 }
1009 }
1010
1011 impl<'de, T: Deserialize<'de> + Clone> DeserializeAs<'de, super::EthereumTxEnvelope<T>>
1012 for EthereumTxEnvelope<'de, T>
1013 {
1014 fn deserialize_as<D>(deserializer: D) -> Result<super::EthereumTxEnvelope<T>, D::Error>
1015 where
1016 D: Deserializer<'de>,
1017 {
1018 EthereumTxEnvelope::<'_, T>::deserialize(deserializer).map(Into::into)
1019 }
1020 }
1021
1022 #[cfg(test)]
1023 mod tests {
1024 use super::super::{serde_bincode_compat, EthereumTxEnvelope};
1025 use crate::TxEip4844;
1026 use arbitrary::Arbitrary;
1027 use bincode::config;
1028 use rand::Rng;
1029 use serde::{Deserialize, Serialize};
1030 use serde_with::serde_as;
1031
1032 #[test]
1033 fn test_typed_tx_envelope_bincode_roundtrip() {
1034 #[serde_as]
1035 #[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
1036 struct Data {
1037 #[serde_as(as = "serde_bincode_compat::EthereumTxEnvelope<'_>")]
1038 transaction: EthereumTxEnvelope<TxEip4844>,
1039 }
1040
1041 let mut bytes = [0u8; 1024];
1042 rand::thread_rng().fill(bytes.as_mut_slice());
1043 let data = Data {
1044 transaction: EthereumTxEnvelope::arbitrary(&mut arbitrary::Unstructured::new(
1045 &bytes,
1046 ))
1047 .unwrap(),
1048 };
1049
1050 let encoded = bincode::serde::encode_to_vec(&data, config::legacy()).unwrap();
1051 let (decoded, _) =
1052 bincode::serde::decode_from_slice::<Data, _>(&encoded, config::legacy()).unwrap();
1053 assert_eq!(decoded, data);
1054 }
1055 }
1056}
1057
1058#[cfg(test)]
1059mod tests {
1060 use super::*;
1061 use crate::{
1062 transaction::{Recovered, SignableTransaction, SignerRecoverable},
1063 Transaction, TxEip4844, TxEip4844WithSidecar,
1064 };
1065 use alloc::vec::Vec;
1066 use alloy_eips::{
1067 eip2930::{AccessList, AccessListItem},
1068 eip4844::BlobTransactionSidecar,
1069 eip7594::BlobTransactionSidecarVariant,
1070 eip7702::Authorization,
1071 };
1072 #[allow(unused_imports)]
1073 use alloy_primitives::{b256, Bytes, TxKind};
1074 use alloy_primitives::{hex, Address, Signature, U256};
1075 use alloy_rlp::Decodable;
1076 use std::{fs, path::PathBuf, str::FromStr, vec};
1077
1078 #[test]
1079 fn assert_encodable() {
1080 fn assert_encodable<T: Encodable2718>() {}
1081
1082 assert_encodable::<EthereumTxEnvelope<TxEip4844>>();
1083 assert_encodable::<Recovered<EthereumTxEnvelope<TxEip4844>>>();
1084 assert_encodable::<Recovered<EthereumTxEnvelope<TxEip4844Variant>>>();
1085 }
1086
1087 #[test]
1088 #[cfg(feature = "k256")]
1089 fn test_decode_live_1559_tx() {
1091 use alloy_primitives::address;
1092
1093 let raw_tx = alloy_primitives::hex::decode("02f86f0102843b9aca0085029e7822d68298f094d9e1459a7a482635700cbc20bbaf52d495ab9c9680841b55ba3ac080a0c199674fcb29f353693dd779c017823b954b3c69dffa3cd6b2a6ff7888798039a028ca912de909e7e6cdef9cdcaf24c54dd8c1032946dfa1d85c206b32a9064fe8").unwrap();
1094 let res = TxEnvelope::decode(&mut raw_tx.as_slice()).unwrap();
1095
1096 assert_eq!(res.tx_type(), TxType::Eip1559);
1097
1098 let tx = match res {
1099 TxEnvelope::Eip1559(tx) => tx,
1100 _ => unreachable!(),
1101 };
1102
1103 assert_eq!(tx.tx().to, TxKind::Call(address!("D9e1459A7A482635700cBc20BBAF52D495Ab9C96")));
1104 let from = tx.recover_signer().unwrap();
1105 assert_eq!(from, address!("001e2b7dE757bA469a57bF6b23d982458a07eFcE"));
1106 }
1107
1108 #[test]
1109 fn test_is_replay_protected_v() {
1110 let sig = Signature::test_signature();
1111 assert!(!&TxEnvelope::Legacy(Signed::new_unchecked(
1112 TxLegacy::default(),
1113 sig,
1114 Default::default(),
1115 ))
1116 .is_replay_protected());
1117 let r = b256!("840cfc572845f5786e702984c2a582528cad4b49b2a10b9db1be7fca90058565");
1118 let s = b256!("25e7109ceb98168d95b09b18bbf6b685130e0562f233877d492b94eee0c5b6d1");
1119 let v = false;
1120 let valid_sig = Signature::from_scalars_and_parity(r, s, v);
1121 assert!(!&TxEnvelope::Legacy(Signed::new_unchecked(
1122 TxLegacy::default(),
1123 valid_sig,
1124 Default::default(),
1125 ))
1126 .is_replay_protected());
1127 assert!(&TxEnvelope::Eip2930(Signed::new_unchecked(
1128 TxEip2930::default(),
1129 sig,
1130 Default::default(),
1131 ))
1132 .is_replay_protected());
1133 assert!(&TxEnvelope::Eip1559(Signed::new_unchecked(
1134 TxEip1559::default(),
1135 sig,
1136 Default::default(),
1137 ))
1138 .is_replay_protected());
1139 assert!(&TxEnvelope::Eip4844(Signed::new_unchecked(
1140 TxEip4844Variant::TxEip4844(TxEip4844::default()),
1141 sig,
1142 Default::default(),
1143 ))
1144 .is_replay_protected());
1145 assert!(&TxEnvelope::Eip7702(Signed::new_unchecked(
1146 TxEip7702::default(),
1147 sig,
1148 Default::default(),
1149 ))
1150 .is_replay_protected());
1151 }
1152
1153 #[test]
1154 #[cfg(feature = "k256")]
1155 fn test_decode_live_legacy_tx() {
1157 use alloy_primitives::address;
1158
1159 let raw_tx = alloy_primitives::bytes!("f9015482078b8505d21dba0083022ef1947a250d5630b4cf539739df2c5dacb4c659f2488d880c46549a521b13d8b8e47ff36ab50000000000000000000000000000000000000000000066ab5a608bd00a23f2fe000000000000000000000000000000000000000000000000000000000000008000000000000000000000000048c04ed5691981c42154c6167398f95e8f38a7ff00000000000000000000000000000000000000000000000000000000632ceac70000000000000000000000000000000000000000000000000000000000000002000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000006c6ee5e31d828de241282b9606c8e98ea48526e225a0c9077369501641a92ef7399ff81c21639ed4fd8fc69cb793cfa1dbfab342e10aa0615facb2f1bcf3274a354cfe384a38d0cc008a11c2dd23a69111bc6930ba27a8");
1160 let res = TxEnvelope::decode_2718(&mut raw_tx.as_ref()).unwrap();
1161 assert_eq!(res.tx_type(), TxType::Legacy);
1162
1163 let tx = match res {
1164 TxEnvelope::Legacy(tx) => tx,
1165 _ => unreachable!(),
1166 };
1167
1168 assert_eq!(tx.tx().chain_id(), Some(1));
1169
1170 assert_eq!(tx.tx().to, TxKind::Call(address!("7a250d5630B4cF539739dF2C5dAcb4c659F2488D")));
1171 assert_eq!(
1172 tx.hash().to_string(),
1173 "0x280cde7cdefe4b188750e76c888f13bd05ce9a4d7767730feefe8a0e50ca6fc4"
1174 );
1175 let from = tx.recover_signer().unwrap();
1176 assert_eq!(from, address!("a12e1462d0ceD572f396F58B6E2D03894cD7C8a4"));
1177 }
1178
1179 #[test]
1180 #[cfg(feature = "k256")]
1181 fn test_decode_live_4844_tx() {
1184 use crate::Transaction;
1185 use alloy_primitives::{address, b256};
1186
1187 let raw_tx = alloy_primitives::hex::decode("0x03f9011d83aa36a7820fa28477359400852e90edd0008252089411e9ca82a3a762b4b5bd264d4173a242e7a770648080c08504a817c800f8a5a0012ec3d6f66766bedb002a190126b3549fce0047de0d4c25cffce0dc1c57921aa00152d8e24762ff22b1cfd9f8c0683786a7ca63ba49973818b3d1e9512cd2cec4a0013b98c6c83e066d5b14af2b85199e3d4fc7d1e778dd53130d180f5077e2d1c7a001148b495d6e859114e670ca54fb6e2657f0cbae5b08063605093a4b3dc9f8f1a0011ac212f13c5dff2b2c6b600a79635103d6f580a4221079951181b25c7e654901a0c8de4cced43169f9aa3d36506363b2d2c44f6c49fc1fd91ea114c86f3757077ea01e11fdd0d1934eda0492606ee0bb80a7bf8f35cc5f86ec60fe5031ba48bfd544").unwrap();
1189
1190 let res = TxEnvelope::decode_2718(&mut raw_tx.as_slice()).unwrap();
1191 assert_eq!(res.tx_type(), TxType::Eip4844);
1192
1193 let tx = match res {
1194 TxEnvelope::Eip4844(tx) => tx,
1195 _ => unreachable!(),
1196 };
1197
1198 assert_eq!(
1199 tx.tx().kind(),
1200 TxKind::Call(address!("11E9CA82A3a762b4B5bd264d4173a242e7a77064"))
1201 );
1202
1203 assert!(matches!(tx.tx(), TxEip4844Variant::TxEip4844(_)));
1205
1206 assert_eq!(
1207 tx.tx().tx().blob_versioned_hashes,
1208 vec![
1209 b256!("012ec3d6f66766bedb002a190126b3549fce0047de0d4c25cffce0dc1c57921a"),
1210 b256!("0152d8e24762ff22b1cfd9f8c0683786a7ca63ba49973818b3d1e9512cd2cec4"),
1211 b256!("013b98c6c83e066d5b14af2b85199e3d4fc7d1e778dd53130d180f5077e2d1c7"),
1212 b256!("01148b495d6e859114e670ca54fb6e2657f0cbae5b08063605093a4b3dc9f8f1"),
1213 b256!("011ac212f13c5dff2b2c6b600a79635103d6f580a4221079951181b25c7e6549")
1214 ]
1215 );
1216
1217 let from = tx.recover_signer().unwrap();
1218 assert_eq!(from, address!("0xA83C816D4f9b2783761a22BA6FADB0eB0606D7B2"));
1219 }
1220
1221 fn test_encode_decode_roundtrip<T: SignableTransaction<Signature>>(
1222 tx: T,
1223 signature: Option<Signature>,
1224 ) where
1225 Signed<T>: Into<TxEnvelope>,
1226 {
1227 let signature = signature.unwrap_or_else(Signature::test_signature);
1228 let tx_signed = tx.into_signed(signature);
1229 let tx_envelope: TxEnvelope = tx_signed.into();
1230 let encoded = tx_envelope.encoded_2718();
1231 let mut slice = encoded.as_slice();
1232 let decoded = TxEnvelope::decode_2718(&mut slice).unwrap();
1233 assert_eq!(encoded.len(), tx_envelope.encode_2718_len());
1234 assert_eq!(decoded, tx_envelope);
1235 assert_eq!(slice.len(), 0);
1236 }
1237
1238 #[test]
1239 fn test_encode_decode_legacy() {
1240 let tx = TxLegacy {
1241 chain_id: None,
1242 nonce: 2,
1243 gas_limit: 1000000,
1244 gas_price: 10000000000,
1245 to: Address::left_padding_from(&[6]).into(),
1246 value: U256::from(7_u64),
1247 ..Default::default()
1248 };
1249 test_encode_decode_roundtrip(tx, Some(Signature::test_signature().with_parity(true)));
1250 }
1251
1252 #[test]
1253 fn test_encode_decode_eip1559() {
1254 let tx = TxEip1559 {
1255 chain_id: 1u64,
1256 nonce: 2,
1257 max_fee_per_gas: 3,
1258 max_priority_fee_per_gas: 4,
1259 gas_limit: 5,
1260 to: Address::left_padding_from(&[6]).into(),
1261 value: U256::from(7_u64),
1262 input: vec![8].into(),
1263 access_list: Default::default(),
1264 };
1265 test_encode_decode_roundtrip(tx, None);
1266 }
1267
1268 #[test]
1269 fn test_encode_decode_eip1559_parity_eip155() {
1270 let tx = TxEip1559 {
1271 chain_id: 1u64,
1272 nonce: 2,
1273 max_fee_per_gas: 3,
1274 max_priority_fee_per_gas: 4,
1275 gas_limit: 5,
1276 to: Address::left_padding_from(&[6]).into(),
1277 value: U256::from(7_u64),
1278 input: vec![8].into(),
1279 access_list: Default::default(),
1280 };
1281 let signature = Signature::test_signature().with_parity(true);
1282
1283 test_encode_decode_roundtrip(tx, Some(signature));
1284 }
1285
1286 #[test]
1287 fn test_encode_decode_eip2930_parity_eip155() {
1288 let tx = TxEip2930 {
1289 chain_id: 1u64,
1290 nonce: 2,
1291 gas_price: 3,
1292 gas_limit: 4,
1293 to: Address::left_padding_from(&[5]).into(),
1294 value: U256::from(6_u64),
1295 input: vec![7].into(),
1296 access_list: Default::default(),
1297 };
1298 let signature = Signature::test_signature().with_parity(true);
1299 test_encode_decode_roundtrip(tx, Some(signature));
1300 }
1301
1302 #[test]
1303 fn test_encode_decode_eip4844_parity_eip155() {
1304 let tx = TxEip4844 {
1305 chain_id: 1,
1306 nonce: 100,
1307 max_fee_per_gas: 50_000_000_000,
1308 max_priority_fee_per_gas: 1_000_000_000_000,
1309 gas_limit: 1_000_000,
1310 to: Address::random(),
1311 value: U256::from(10e18),
1312 input: Bytes::new(),
1313 access_list: AccessList(vec![AccessListItem {
1314 address: Address::random(),
1315 storage_keys: vec![B256::random()],
1316 }]),
1317 blob_versioned_hashes: vec![B256::random()],
1318 max_fee_per_blob_gas: 0,
1319 };
1320 let signature = Signature::test_signature().with_parity(true);
1321 test_encode_decode_roundtrip(tx, Some(signature));
1322 }
1323
1324 #[test]
1325 fn test_encode_decode_eip4844_sidecar_parity_eip155() {
1326 let tx = TxEip4844 {
1327 chain_id: 1,
1328 nonce: 100,
1329 max_fee_per_gas: 50_000_000_000,
1330 max_priority_fee_per_gas: 1_000_000_000_000,
1331 gas_limit: 1_000_000,
1332 to: Address::random(),
1333 value: U256::from(10e18),
1334 input: Bytes::new(),
1335 access_list: AccessList(vec![AccessListItem {
1336 address: Address::random(),
1337 storage_keys: vec![B256::random()],
1338 }]),
1339 blob_versioned_hashes: vec![B256::random()],
1340 max_fee_per_blob_gas: 0,
1341 };
1342 let sidecar = BlobTransactionSidecar {
1343 blobs: vec![[2; 131072].into()],
1344 commitments: vec![[3; 48].into()],
1345 proofs: vec![[4; 48].into()],
1346 };
1347 let tx = TxEip4844WithSidecar { tx, sidecar };
1348 let signature = Signature::test_signature().with_parity(true);
1349
1350 let tx_signed = tx.into_signed(signature);
1351 let tx_envelope: TxEnvelope = tx_signed.into();
1352
1353 let mut out = Vec::new();
1354 tx_envelope.network_encode(&mut out);
1355 let mut slice = out.as_slice();
1356 let decoded = TxEnvelope::network_decode(&mut slice).unwrap();
1357 assert_eq!(slice.len(), 0);
1358 assert_eq!(out.len(), tx_envelope.network_len());
1359 assert_eq!(decoded, tx_envelope);
1360 }
1361
1362 #[test]
1363 fn test_encode_decode_eip4844_variant_parity_eip155() {
1364 let tx = TxEip4844 {
1365 chain_id: 1,
1366 nonce: 100,
1367 max_fee_per_gas: 50_000_000_000,
1368 max_priority_fee_per_gas: 1_000_000_000_000,
1369 gas_limit: 1_000_000,
1370 to: Address::random(),
1371 value: U256::from(10e18),
1372 input: Bytes::new(),
1373 access_list: AccessList(vec![AccessListItem {
1374 address: Address::random(),
1375 storage_keys: vec![B256::random()],
1376 }]),
1377 blob_versioned_hashes: vec![B256::random()],
1378 max_fee_per_blob_gas: 0,
1379 };
1380 let tx: TxEip4844Variant = tx.into();
1381 let signature = Signature::test_signature().with_parity(true);
1382 test_encode_decode_roundtrip(tx, Some(signature));
1383 }
1384
1385 #[test]
1386 fn test_encode_decode_eip2930() {
1387 let tx = TxEip2930 {
1388 chain_id: 1u64,
1389 nonce: 2,
1390 gas_price: 3,
1391 gas_limit: 4,
1392 to: Address::left_padding_from(&[5]).into(),
1393 value: U256::from(6_u64),
1394 input: vec![7].into(),
1395 access_list: AccessList(vec![AccessListItem {
1396 address: Address::left_padding_from(&[8]),
1397 storage_keys: vec![B256::left_padding_from(&[9])],
1398 }]),
1399 };
1400 test_encode_decode_roundtrip(tx, None);
1401 }
1402
1403 #[test]
1404 fn test_encode_decode_eip7702() {
1405 let tx = TxEip7702 {
1406 chain_id: 1u64,
1407 nonce: 2,
1408 gas_limit: 3,
1409 max_fee_per_gas: 4,
1410 max_priority_fee_per_gas: 5,
1411 to: Address::left_padding_from(&[5]),
1412 value: U256::from(6_u64),
1413 input: vec![7].into(),
1414 access_list: AccessList(vec![AccessListItem {
1415 address: Address::left_padding_from(&[8]),
1416 storage_keys: vec![B256::left_padding_from(&[9])],
1417 }]),
1418 authorization_list: vec![(Authorization {
1419 chain_id: U256::from(1),
1420 address: Address::left_padding_from(&[10]),
1421 nonce: 1u64,
1422 })
1423 .into_signed(Signature::from_str("48b55bfa915ac795c431978d8a6a992b628d557da5ff759b307d495a36649353efffd310ac743f371de3b9f7f9cb56c0b28ad43601b4ab949f53faa07bd2c8041b").unwrap())],
1424 };
1425 test_encode_decode_roundtrip(tx, None);
1426 }
1427
1428 #[test]
1429 fn test_encode_decode_transaction_list() {
1430 let signature = Signature::test_signature();
1431 let tx = TxEnvelope::Eip1559(
1432 TxEip1559 {
1433 chain_id: 1u64,
1434 nonce: 2,
1435 max_fee_per_gas: 3,
1436 max_priority_fee_per_gas: 4,
1437 gas_limit: 5,
1438 to: Address::left_padding_from(&[6]).into(),
1439 value: U256::from(7_u64),
1440 input: vec![8].into(),
1441 access_list: Default::default(),
1442 }
1443 .into_signed(signature),
1444 );
1445 let transactions = vec![tx.clone(), tx];
1446 let encoded = alloy_rlp::encode(&transactions);
1447 let decoded = Vec::<TxEnvelope>::decode(&mut &encoded[..]).unwrap();
1448 assert_eq!(transactions, decoded);
1449 }
1450
1451 #[test]
1452 fn decode_encode_known_rpc_transaction() {
1453 let network_data_path =
1455 PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("testdata/rpc_blob_transaction.rlp");
1456 let data = fs::read_to_string(network_data_path).expect("Unable to read file");
1457 let hex_data = hex::decode(data.trim()).unwrap();
1458
1459 let tx: TxEnvelope = TxEnvelope::decode_2718(&mut hex_data.as_slice()).unwrap();
1460 let encoded = tx.encoded_2718();
1461 assert_eq!(encoded, hex_data);
1462 assert_eq!(tx.encode_2718_len(), hex_data.len());
1463 }
1464
1465 #[cfg(feature = "serde")]
1466 fn test_serde_roundtrip<T: SignableTransaction<Signature>>(tx: T)
1467 where
1468 Signed<T>: Into<TxEnvelope>,
1469 {
1470 let signature = Signature::test_signature();
1471 let tx_envelope: TxEnvelope = tx.into_signed(signature).into();
1472
1473 let serialized = serde_json::to_string(&tx_envelope).unwrap();
1474
1475 let deserialized: TxEnvelope = serde_json::from_str(&serialized).unwrap();
1476
1477 assert_eq!(tx_envelope, deserialized);
1478 }
1479
1480 #[test]
1481 #[cfg(feature = "serde")]
1482 fn test_serde_roundtrip_legacy() {
1483 let tx = TxLegacy {
1484 chain_id: Some(1),
1485 nonce: 100,
1486 gas_price: 3_000_000_000,
1487 gas_limit: 50_000,
1488 to: Address::default().into(),
1489 value: U256::from(10e18),
1490 input: Bytes::new(),
1491 };
1492 test_serde_roundtrip(tx);
1493 }
1494
1495 #[test]
1496 #[cfg(feature = "serde")]
1497 fn test_serde_roundtrip_eip1559() {
1498 let tx = TxEip1559 {
1499 chain_id: 1,
1500 nonce: 100,
1501 max_fee_per_gas: 50_000_000_000,
1502 max_priority_fee_per_gas: 1_000_000_000_000,
1503 gas_limit: 1_000_000,
1504 to: TxKind::Create,
1505 value: U256::from(10e18),
1506 input: Bytes::new(),
1507 access_list: AccessList(vec![AccessListItem {
1508 address: Address::random(),
1509 storage_keys: vec![B256::random()],
1510 }]),
1511 };
1512 test_serde_roundtrip(tx);
1513 }
1514
1515 #[test]
1516 #[cfg(feature = "serde")]
1517 fn test_serde_roundtrip_eip2930() {
1518 let tx = TxEip2930 {
1519 chain_id: u64::MAX,
1520 nonce: u64::MAX,
1521 gas_price: u128::MAX,
1522 gas_limit: u64::MAX,
1523 to: Address::random().into(),
1524 value: U256::MAX,
1525 input: Bytes::new(),
1526 access_list: Default::default(),
1527 };
1528 test_serde_roundtrip(tx);
1529 }
1530
1531 #[test]
1532 #[cfg(feature = "serde")]
1533 fn test_serde_roundtrip_eip4844() {
1534 let tx: TxEip4844Variant = TxEip4844 {
1535 chain_id: 1,
1536 nonce: 100,
1537 max_fee_per_gas: 50_000_000_000,
1538 max_priority_fee_per_gas: 1_000_000_000_000,
1539 gas_limit: 1_000_000,
1540 to: Address::random(),
1541 value: U256::from(10e18),
1542 input: Bytes::new(),
1543 access_list: AccessList(vec![AccessListItem {
1544 address: Address::random(),
1545 storage_keys: vec![B256::random()],
1546 }]),
1547 blob_versioned_hashes: vec![B256::random()],
1548 max_fee_per_blob_gas: 0,
1549 }
1550 .into();
1551 test_serde_roundtrip(tx);
1552
1553 let tx = TxEip4844Variant::TxEip4844WithSidecar(TxEip4844WithSidecar {
1554 tx: TxEip4844 {
1555 chain_id: 1,
1556 nonce: 100,
1557 max_fee_per_gas: 50_000_000_000,
1558 max_priority_fee_per_gas: 1_000_000_000_000,
1559 gas_limit: 1_000_000,
1560 to: Address::random(),
1561 value: U256::from(10e18),
1562 input: Bytes::new(),
1563 access_list: AccessList(vec![AccessListItem {
1564 address: Address::random(),
1565 storage_keys: vec![B256::random()],
1566 }]),
1567 blob_versioned_hashes: vec![B256::random()],
1568 max_fee_per_blob_gas: 0,
1569 },
1570 sidecar: BlobTransactionSidecarVariant::Eip4844(Default::default()),
1571 });
1572 test_serde_roundtrip(tx);
1573 }
1574
1575 #[test]
1576 #[cfg(feature = "serde")]
1577 fn test_serde_roundtrip_eip7702() {
1578 let tx = TxEip7702 {
1579 chain_id: u64::MAX,
1580 nonce: u64::MAX,
1581 gas_limit: u64::MAX,
1582 max_fee_per_gas: u128::MAX,
1583 max_priority_fee_per_gas: u128::MAX,
1584 to: Address::random(),
1585 value: U256::MAX,
1586 input: Bytes::new(),
1587 access_list: AccessList(vec![AccessListItem {
1588 address: Address::random(),
1589 storage_keys: vec![B256::random()],
1590 }]),
1591 authorization_list: vec![(Authorization {
1592 chain_id: U256::from(1),
1593 address: Address::left_padding_from(&[1]),
1594 nonce: 1u64,
1595 })
1596 .into_signed(Signature::from_str("48b55bfa915ac795c431978d8a6a992b628d557da5ff759b307d495a36649353efffd310ac743f371de3b9f7f9cb56c0b28ad43601b4ab949f53faa07bd2c8041b").unwrap())],
1597 };
1598 test_serde_roundtrip(tx);
1599 }
1600
1601 #[test]
1602 #[cfg(feature = "serde")]
1603 fn serde_tx_from_contract_call() {
1604 let rpc_tx = r#"{"hash":"0x018b2331d461a4aeedf6a1f9cc37463377578244e6a35216057a8370714e798f","nonce":"0x1","blockHash":"0x3ca295f1dcaf8ac073c543dc0eccf18859f411206df181731e374e9917252931","blockNumber":"0x2","transactionIndex":"0x0","from":"0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266","to":"0x5fbdb2315678afecb367f032d93f642f64180aa3","value":"0x0","gasPrice":"0x3a29f0f8","gas":"0x1c9c380","maxFeePerGas":"0xba43b7400","maxPriorityFeePerGas":"0x5f5e100","input":"0xd09de08a","r":"0xd309309a59a49021281cb6bb41d164c96eab4e50f0c1bd24c03ca336e7bc2bb7","s":"0x28a7f089143d0a1355ebeb2a1b9f0e5ad9eca4303021c1400d61bc23c9ac5319","v":"0x0","yParity":"0x0","chainId":"0x7a69","accessList":[],"type":"0x2"}"#;
1605
1606 let te = serde_json::from_str::<TxEnvelope>(rpc_tx).unwrap();
1607
1608 assert_eq!(
1609 *te.tx_hash(),
1610 alloy_primitives::b256!(
1611 "018b2331d461a4aeedf6a1f9cc37463377578244e6a35216057a8370714e798f"
1612 )
1613 );
1614 }
1615
1616 #[test]
1617 #[cfg(feature = "k256")]
1618 fn test_arbitrary_envelope() {
1619 use crate::transaction::SignerRecoverable;
1620 use arbitrary::Arbitrary;
1621 let mut unstructured = arbitrary::Unstructured::new(b"arbitrary tx envelope");
1622 let tx = TxEnvelope::arbitrary(&mut unstructured).unwrap();
1623
1624 assert!(tx.recover_signer().is_ok());
1625 }
1626
1627 #[test]
1628 #[cfg(feature = "serde")]
1629 fn test_serde_untagged_legacy() {
1630 let data = r#"{
1631 "hash": "0x97efb58d2b42df8d68ab5899ff42b16c7e0af35ed86ae4adb8acaad7e444220c",
1632 "input": "0x",
1633 "r": "0x5d71a4a548503f2916d10c6b1a1557a0e7352eb041acb2bac99d1ad6bb49fd45",
1634 "s": "0x2627bf6d35be48b0e56c61733f63944c0ebcaa85cb4ed6bc7cba3161ba85e0e8",
1635 "v": "0x1c",
1636 "gas": "0x15f90",
1637 "from": "0x2a65aca4d5fc5b5c859090a6c34d164135398226",
1638 "to": "0x8fbeb4488a08d60979b5aa9e13dd00b2726320b2",
1639 "value": "0xf606682badd7800",
1640 "nonce": "0x11f398",
1641 "gasPrice": "0x4a817c800"
1642 }"#;
1643
1644 let tx: TxEnvelope = serde_json::from_str(data).unwrap();
1645
1646 assert!(matches!(tx, TxEnvelope::Legacy(_)));
1647
1648 let data_with_wrong_type = r#"{
1649 "hash": "0x97efb58d2b42df8d68ab5899ff42b16c7e0af35ed86ae4adb8acaad7e444220c",
1650 "input": "0x",
1651 "r": "0x5d71a4a548503f2916d10c6b1a1557a0e7352eb041acb2bac99d1ad6bb49fd45",
1652 "s": "0x2627bf6d35be48b0e56c61733f63944c0ebcaa85cb4ed6bc7cba3161ba85e0e8",
1653 "v": "0x1c",
1654 "gas": "0x15f90",
1655 "from": "0x2a65aca4d5fc5b5c859090a6c34d164135398226",
1656 "to": "0x8fbeb4488a08d60979b5aa9e13dd00b2726320b2",
1657 "value": "0xf606682badd7800",
1658 "nonce": "0x11f398",
1659 "gasPrice": "0x4a817c800",
1660 "type": "0x12"
1661 }"#;
1662
1663 assert!(serde_json::from_str::<TxEnvelope>(data_with_wrong_type).is_err());
1664 }
1665
1666 #[test]
1667 fn test_tx_type_try_from_u8() {
1668 assert_eq!(TxType::try_from(0u8).unwrap(), TxType::Legacy);
1669 assert_eq!(TxType::try_from(1u8).unwrap(), TxType::Eip2930);
1670 assert_eq!(TxType::try_from(2u8).unwrap(), TxType::Eip1559);
1671 assert_eq!(TxType::try_from(3u8).unwrap(), TxType::Eip4844);
1672 assert_eq!(TxType::try_from(4u8).unwrap(), TxType::Eip7702);
1673 assert!(TxType::try_from(5u8).is_err()); }
1675
1676 #[test]
1677 fn test_tx_type_try_from_u64() {
1678 assert_eq!(TxType::try_from(0u64).unwrap(), TxType::Legacy);
1679 assert_eq!(TxType::try_from(1u64).unwrap(), TxType::Eip2930);
1680 assert_eq!(TxType::try_from(2u64).unwrap(), TxType::Eip1559);
1681 assert_eq!(TxType::try_from(3u64).unwrap(), TxType::Eip4844);
1682 assert_eq!(TxType::try_from(4u64).unwrap(), TxType::Eip7702);
1683 assert!(TxType::try_from(10u64).is_err()); }
1685
1686 #[test]
1687 fn test_tx_type_from_conversions() {
1688 let legacy_tx = Signed::new_unchecked(
1689 TxLegacy::default(),
1690 Signature::test_signature(),
1691 Default::default(),
1692 );
1693 let eip2930_tx = Signed::new_unchecked(
1694 TxEip2930::default(),
1695 Signature::test_signature(),
1696 Default::default(),
1697 );
1698 let eip1559_tx = Signed::new_unchecked(
1699 TxEip1559::default(),
1700 Signature::test_signature(),
1701 Default::default(),
1702 );
1703 let eip4844_variant = Signed::new_unchecked(
1704 TxEip4844Variant::<BlobTransactionSidecarVariant>::TxEip4844(TxEip4844::default()),
1705 Signature::test_signature(),
1706 Default::default(),
1707 );
1708 let eip7702_tx = Signed::new_unchecked(
1709 TxEip7702::default(),
1710 Signature::test_signature(),
1711 Default::default(),
1712 );
1713
1714 assert!(matches!(TxEnvelope::from(legacy_tx), TxEnvelope::Legacy(_)));
1715 assert!(matches!(TxEnvelope::from(eip2930_tx), TxEnvelope::Eip2930(_)));
1716 assert!(matches!(TxEnvelope::from(eip1559_tx), TxEnvelope::Eip1559(_)));
1717 assert!(matches!(TxEnvelope::from(eip4844_variant), TxEnvelope::Eip4844(_)));
1718 assert!(matches!(TxEnvelope::from(eip7702_tx), TxEnvelope::Eip7702(_)));
1719 }
1720
1721 #[test]
1722 fn test_tx_type_is_methods() {
1723 let legacy_tx = TxEnvelope::Legacy(Signed::new_unchecked(
1724 TxLegacy::default(),
1725 Signature::test_signature(),
1726 Default::default(),
1727 ));
1728 let eip2930_tx = TxEnvelope::Eip2930(Signed::new_unchecked(
1729 TxEip2930::default(),
1730 Signature::test_signature(),
1731 Default::default(),
1732 ));
1733 let eip1559_tx = TxEnvelope::Eip1559(Signed::new_unchecked(
1734 TxEip1559::default(),
1735 Signature::test_signature(),
1736 Default::default(),
1737 ));
1738 let eip4844_tx = TxEnvelope::Eip4844(Signed::new_unchecked(
1739 TxEip4844Variant::TxEip4844(TxEip4844::default()),
1740 Signature::test_signature(),
1741 Default::default(),
1742 ));
1743 let eip7702_tx = TxEnvelope::Eip7702(Signed::new_unchecked(
1744 TxEip7702::default(),
1745 Signature::test_signature(),
1746 Default::default(),
1747 ));
1748
1749 assert!(legacy_tx.is_legacy());
1750 assert!(!legacy_tx.is_eip2930());
1751 assert!(!legacy_tx.is_eip1559());
1752 assert!(!legacy_tx.is_eip4844());
1753 assert!(!legacy_tx.is_eip7702());
1754
1755 assert!(eip2930_tx.is_eip2930());
1756 assert!(!eip2930_tx.is_legacy());
1757 assert!(!eip2930_tx.is_eip1559());
1758 assert!(!eip2930_tx.is_eip4844());
1759 assert!(!eip2930_tx.is_eip7702());
1760
1761 assert!(eip1559_tx.is_eip1559());
1762 assert!(!eip1559_tx.is_legacy());
1763 assert!(!eip1559_tx.is_eip2930());
1764 assert!(!eip1559_tx.is_eip4844());
1765 assert!(!eip1559_tx.is_eip7702());
1766
1767 assert!(eip4844_tx.is_eip4844());
1768 assert!(!eip4844_tx.is_legacy());
1769 assert!(!eip4844_tx.is_eip2930());
1770 assert!(!eip4844_tx.is_eip1559());
1771 assert!(!eip4844_tx.is_eip7702());
1772
1773 assert!(eip7702_tx.is_eip7702());
1774 assert!(!eip7702_tx.is_legacy());
1775 assert!(!eip7702_tx.is_eip2930());
1776 assert!(!eip7702_tx.is_eip1559());
1777 assert!(!eip7702_tx.is_eip4844());
1778 }
1779
1780 #[test]
1781 fn test_tx_type() {
1782 let legacy_tx = TxEnvelope::Legacy(Signed::new_unchecked(
1783 TxLegacy::default(),
1784 Signature::test_signature(),
1785 Default::default(),
1786 ));
1787 let eip2930_tx = TxEnvelope::Eip2930(Signed::new_unchecked(
1788 TxEip2930::default(),
1789 Signature::test_signature(),
1790 Default::default(),
1791 ));
1792 let eip1559_tx = TxEnvelope::Eip1559(Signed::new_unchecked(
1793 TxEip1559::default(),
1794 Signature::test_signature(),
1795 Default::default(),
1796 ));
1797 let eip4844_tx = TxEnvelope::Eip4844(Signed::new_unchecked(
1798 TxEip4844Variant::TxEip4844(TxEip4844::default()),
1799 Signature::test_signature(),
1800 Default::default(),
1801 ));
1802 let eip7702_tx = TxEnvelope::Eip7702(Signed::new_unchecked(
1803 TxEip7702::default(),
1804 Signature::test_signature(),
1805 Default::default(),
1806 ));
1807
1808 assert_eq!(legacy_tx.tx_type(), TxType::Legacy);
1809 assert_eq!(eip2930_tx.tx_type(), TxType::Eip2930);
1810 assert_eq!(eip1559_tx.tx_type(), TxType::Eip1559);
1811 assert_eq!(eip4844_tx.tx_type(), TxType::Eip4844);
1812 assert_eq!(eip7702_tx.tx_type(), TxType::Eip7702);
1813 }
1814
1815 #[test]
1816 fn test_try_into_legacy_success() {
1817 let legacy_tx = TxEnvelope::Legacy(Signed::new_unchecked(
1818 TxLegacy::default(),
1819 Signature::test_signature(),
1820 Default::default(),
1821 ));
1822
1823 let result = legacy_tx.try_into_legacy();
1824 assert!(result.is_ok());
1825 }
1826
1827 #[test]
1828 fn test_try_into_legacy_failure() {
1829 let eip1559_tx = TxEnvelope::Eip1559(Signed::new_unchecked(
1830 TxEip1559::default(),
1831 Signature::test_signature(),
1832 Default::default(),
1833 ));
1834
1835 let result = eip1559_tx.try_into_legacy();
1836 assert!(result.is_err());
1837 let error = result.unwrap_err();
1838 assert!(error.to_string().contains("Expected legacy transaction"));
1839 let recovered_envelope = error.into_value();
1841 assert!(recovered_envelope.is_eip1559());
1842 }
1843
1844 #[test]
1845 fn test_try_into_eip2930_success() {
1846 let eip2930_tx = TxEnvelope::Eip2930(Signed::new_unchecked(
1847 TxEip2930::default(),
1848 Signature::test_signature(),
1849 Default::default(),
1850 ));
1851
1852 let result = eip2930_tx.try_into_eip2930();
1853 assert!(result.is_ok());
1854 }
1855
1856 #[test]
1857 fn test_try_into_eip2930_failure() {
1858 let legacy_tx = TxEnvelope::Legacy(Signed::new_unchecked(
1859 TxLegacy::default(),
1860 Signature::test_signature(),
1861 Default::default(),
1862 ));
1863
1864 let result = legacy_tx.try_into_eip2930();
1865 assert!(result.is_err());
1866 let error = result.unwrap_err();
1867 assert!(error.to_string().contains("Expected EIP-2930 transaction"));
1868 let recovered_envelope = error.into_value();
1869 assert!(recovered_envelope.is_legacy());
1870 }
1871
1872 #[test]
1873 fn test_try_into_eip1559_success() {
1874 let eip1559_tx = TxEnvelope::Eip1559(Signed::new_unchecked(
1875 TxEip1559::default(),
1876 Signature::test_signature(),
1877 Default::default(),
1878 ));
1879
1880 let result = eip1559_tx.try_into_eip1559();
1881 assert!(result.is_ok());
1882 }
1883
1884 #[test]
1885 fn test_try_into_eip1559_failure() {
1886 let eip2930_tx = TxEnvelope::Eip2930(Signed::new_unchecked(
1887 TxEip2930::default(),
1888 Signature::test_signature(),
1889 Default::default(),
1890 ));
1891
1892 let result = eip2930_tx.try_into_eip1559();
1893 assert!(result.is_err());
1894 let error = result.unwrap_err();
1895 assert!(error.to_string().contains("Expected EIP-1559 transaction"));
1896 let recovered_envelope = error.into_value();
1897 assert!(recovered_envelope.is_eip2930());
1898 }
1899
1900 #[test]
1901 fn test_try_into_eip4844_success() {
1902 let eip4844_tx = TxEnvelope::Eip4844(Signed::new_unchecked(
1903 TxEip4844Variant::TxEip4844(TxEip4844::default()),
1904 Signature::test_signature(),
1905 Default::default(),
1906 ));
1907
1908 let result = eip4844_tx.try_into_eip4844();
1909 assert!(result.is_ok());
1910 }
1911
1912 #[test]
1913 fn test_try_into_eip4844_failure() {
1914 let eip1559_tx = TxEnvelope::Eip1559(Signed::new_unchecked(
1915 TxEip1559::default(),
1916 Signature::test_signature(),
1917 Default::default(),
1918 ));
1919
1920 let result = eip1559_tx.try_into_eip4844();
1921 assert!(result.is_err());
1922 let error = result.unwrap_err();
1923 assert!(error.to_string().contains("Expected EIP-4844 transaction"));
1924 let recovered_envelope = error.into_value();
1925 assert!(recovered_envelope.is_eip1559());
1926 }
1927
1928 #[test]
1929 fn test_try_into_eip7702_success() {
1930 let eip7702_tx = TxEnvelope::Eip7702(Signed::new_unchecked(
1931 TxEip7702::default(),
1932 Signature::test_signature(),
1933 Default::default(),
1934 ));
1935
1936 let result = eip7702_tx.try_into_eip7702();
1937 assert!(result.is_ok());
1938 }
1939
1940 #[test]
1941 fn test_try_into_eip7702_failure() {
1942 let eip4844_tx = TxEnvelope::Eip4844(Signed::new_unchecked(
1943 TxEip4844Variant::TxEip4844(TxEip4844::default()),
1944 Signature::test_signature(),
1945 Default::default(),
1946 ));
1947
1948 let result = eip4844_tx.try_into_eip7702();
1949 assert!(result.is_err());
1950 let error = result.unwrap_err();
1951 assert!(error.to_string().contains("Expected EIP-7702 transaction"));
1952 let recovered_envelope = error.into_value();
1953 assert!(recovered_envelope.is_eip4844());
1954 }
1955
1956 #[test]
1958 fn decode_raw_legacy() {
1959 let raw = hex!("f8aa0285018ef61d0a832dc6c094cb33aa5b38d79e3d9fa8b10aff38aa201399a7e380b844af7b421018842e4628f3d9ee0e2c7679e29ed5dbaa75be75efecd392943503c9c68adce800000000000000000000000000000000000000000000000000000000000000641ca05e28679806caa50d25e9cb16aef8c0c08b235241b8f6e9d86faadf70421ba664a02353bba82ef2c7ce4dd6695942399163160000272b14f9aa6cbadf011b76efa4");
1960 let tx = TxEnvelope::decode_2718(&mut raw.as_ref()).unwrap();
1961 assert!(tx.chain_id().is_none());
1962 }
1963
1964 #[test]
1965 #[cfg(feature = "serde")]
1966 fn can_deserialize_system_transaction_with_zero_signature_envelope() {
1967 let raw_tx = r#"{
1968 "blockHash": "0x5307b5c812a067f8bc1ed1cc89d319ae6f9a0c9693848bd25c36b5191de60b85",
1969 "blockNumber": "0x45a59bb",
1970 "from": "0x0000000000000000000000000000000000000000",
1971 "gas": "0x1e8480",
1972 "gasPrice": "0x0",
1973 "hash": "0x16ef68aa8f35add3a03167a12b5d1268e344f6605a64ecc3f1c3aa68e98e4e06",
1974 "input": "0xcbd4ece900000000000000000000000032155c9d39084f040ba17890fe8134dbe2a0453f0000000000000000000000004a0126ee88018393b1ad2455060bc350ead9908a000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000469f700000000000000000000000000000000000000000000000000000000000000644ff746f60000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002043e908a4e862aebb10e7e27db0b892b58a7e32af11d64387a414dabc327b00e200000000000000000000000000000000000000000000000000000000",
1975 "nonce": "0x469f7",
1976 "to": "0x4200000000000000000000000000000000000007",
1977 "transactionIndex": "0x0",
1978 "value": "0x0",
1979 "v": "0x0",
1980 "r": "0x0",
1981 "s": "0x0",
1982 "queueOrigin": "l1",
1983 "l1TxOrigin": "0x36bde71c97b33cc4729cf772ae268934f7ab70b2",
1984 "l1BlockNumber": "0xfd1a6c",
1985 "l1Timestamp": "0x63e434ff",
1986 "index": "0x45a59ba",
1987 "queueIndex": "0x469f7",
1988 "rawTransaction": "0xcbd4ece900000000000000000000000032155c9d39084f040ba17890fe8134dbe2a0453f0000000000000000000000004a0126ee88018393b1ad2455060bc350ead9908a000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000469f700000000000000000000000000000000000000000000000000000000000000644ff746f60000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002043e908a4e862aebb10e7e27db0b892b58a7e32af11d64387a414dabc327b00e200000000000000000000000000000000000000000000000000000000"
1989 }"#;
1990
1991 let tx = serde_json::from_str::<TxEnvelope>(raw_tx).unwrap();
1992
1993 assert_eq!(tx.signature().r(), U256::ZERO);
1994 assert_eq!(tx.signature().s(), U256::ZERO);
1995 assert!(!tx.signature().v());
1996
1997 assert_eq!(
1998 tx.hash(),
1999 &b256!("0x16ef68aa8f35add3a03167a12b5d1268e344f6605a64ecc3f1c3aa68e98e4e06"),
2000 "hash should match the transaction hash"
2001 );
2002 }
2003
2004 #[test]
2006 #[cfg(feature = "serde")]
2007 fn serde_block_tx() {
2008 let rpc_tx = r#"{
2009 "blockHash": "0xc0c3190292a82c2ee148774e37e5665f6a205f5ef0cd0885e84701d90ebd442e",
2010 "blockNumber": "0x6edcde",
2011 "transactionIndex": "0x7",
2012 "hash": "0x2cb125e083d6d2631e3752bd2b3d757bf31bf02bfe21de0ffa46fbb118d28b19",
2013 "from": "0x03e5badf3bb1ade1a8f33f94536c827b6531948d",
2014 "to": "0x3267e72dc8780a1512fa69da7759ec66f30350e3",
2015 "input": "0x62e4c545000000000000000000000000464c8ec100f2f42fb4e42e07e203da2324f9fc6700000000000000000000000003e5badf3bb1ade1a8f33f94536c827b6531948d000000000000000000000000a064bfb5c7e81426647dc20a0d854da1538559dc00000000000000000000000000000000000000000000000000c6f3b40b6c0000",
2016 "nonce": "0x2a8",
2017 "value": "0x0",
2018 "gas": "0x28afd",
2019 "gasPrice": "0x23ec5dbc2",
2020 "accessList": [],
2021 "chainId": "0xaa36a7",
2022 "type": "0x0",
2023 "v": "0x1546d71",
2024 "r": "0x809b9f0a1777e376cd1ee5d2f551035643755edf26ea65b7a00c822a24504962",
2025 "s": "0x6a57bb8e21fe85c7e092868ee976fef71edca974d8c452fcf303f9180c764f64"
2026 }"#;
2027
2028 let _ = serde_json::from_str::<TxEnvelope>(rpc_tx).unwrap();
2029 }
2030
2031 #[test]
2033 #[cfg(feature = "serde")]
2034 fn serde_block_tx_legacy_chain_id() {
2035 let rpc_tx = r#"{
2036 "blockHash": "0xc0c3190292a82c2ee148774e37e5665f6a205f5ef0cd0885e84701d90ebd442e",
2037 "blockNumber": "0x6edcde",
2038 "transactionIndex": "0x8",
2039 "hash": "0xe5b458ba9de30b47cb7c0ea836bec7b072053123a7416c5082c97f959a4eebd6",
2040 "from": "0x8b87f0a788cc14b4f0f374da59920f5017ff05de",
2041 "to": "0xcb33aa5b38d79e3d9fa8b10aff38aa201399a7e3",
2042 "input": "0xaf7b421018842e4628f3d9ee0e2c7679e29ed5dbaa75be75efecd392943503c9c68adce80000000000000000000000000000000000000000000000000000000000000064",
2043 "nonce": "0x2",
2044 "value": "0x0",
2045 "gas": "0x2dc6c0",
2046 "gasPrice": "0x18ef61d0a",
2047 "accessList": [],
2048 "chainId": "0xaa36a7",
2049 "type": "0x0",
2050 "v": "0x1c",
2051 "r": "0x5e28679806caa50d25e9cb16aef8c0c08b235241b8f6e9d86faadf70421ba664",
2052 "s": "0x2353bba82ef2c7ce4dd6695942399163160000272b14f9aa6cbadf011b76efa4"
2053 }"#;
2054
2055 let _ = serde_json::from_str::<TxEnvelope>(rpc_tx).unwrap();
2056 }
2057
2058 #[test]
2059 #[cfg(feature = "k256")]
2060 fn test_recover_with_buf_eip1559() {
2061 use alloy_primitives::address;
2062
2063 let raw_tx = alloy_primitives::hex::decode("02f86f0102843b9aca0085029e7822d68298f094d9e1459a7a482635700cbc20bbaf52d495ab9c9680841b55ba3ac080a0c199674fcb29f353693dd779c017823b954b3c69dffa3cd6b2a6ff7888798039a028ca912de909e7e6cdef9cdcaf24c54dd8c1032946dfa1d85c206b32a9064fe8").unwrap();
2065 let tx = TxEnvelope::decode(&mut raw_tx.as_slice()).unwrap();
2066
2067 let from_standard = tx.recover_signer().unwrap();
2069 assert_eq!(from_standard, address!("001e2b7dE757bA469a57bF6b23d982458a07eFcE"));
2070
2071 let mut buf = alloc::vec::Vec::new();
2073 let from_with_buf = tx.recover_with_buf(&mut buf).unwrap();
2074 assert_eq!(from_with_buf, from_standard);
2075
2076 assert!(!buf.is_empty());
2078
2079 buf.clear();
2081 buf.extend_from_slice(b"some garbage data that should be cleared");
2082 let from_with_buf_reuse = tx.recover_with_buf(&mut buf).unwrap();
2083 assert_eq!(from_with_buf_reuse, from_standard);
2084 }
2085
2086 #[test]
2087 #[cfg(feature = "k256")]
2088 fn test_recover_unchecked_with_buf_legacy() {
2089 use alloy_primitives::address;
2090
2091 let raw_tx = alloy_primitives::bytes!("f9015482078b8505d21dba0083022ef1947a250d5630b4cf539739df2c5dacb4c659f2488d880c46549a521b13d8b8e47ff36ab50000000000000000000000000000000000000000000066ab5a608bd00a23f2fe000000000000000000000000000000000000000000000000000000000000008000000000000000000000000048c04ed5691981c42154c6167398f95e8f38a7ff00000000000000000000000000000000000000000000000000000000632ceac70000000000000000000000000000000000000000000000000000000000000002000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000006c6ee5e31d828de241282b9606c8e98ea48526e225a0c9077369501641a92ef7399ff81c21639ed4fd8fc69cb793cfa1dbfab342e10aa0615facb2f1bcf3274a354cfe384a38d0cc008a11c2dd23a69111bc6930ba27a8");
2093 let tx = TxEnvelope::decode_2718(&mut raw_tx.as_ref()).unwrap();
2094
2095 let from_standard = tx.recover_signer_unchecked().unwrap();
2097 assert_eq!(from_standard, address!("a12e1462d0ceD572f396F58B6E2D03894cD7C8a4"));
2098
2099 let mut buf = alloc::vec::Vec::new();
2101 let from_with_buf = tx.recover_unchecked_with_buf(&mut buf).unwrap();
2102 assert_eq!(from_with_buf, from_standard);
2103
2104 assert!(!buf.is_empty());
2106
2107 let original_len = buf.len();
2109 buf.extend_from_slice(&[0xFF; 100]); let from_with_buf_reuse = tx.recover_unchecked_with_buf(&mut buf).unwrap();
2111 assert_eq!(from_with_buf_reuse, from_standard);
2112 assert_eq!(buf.len(), original_len);
2114 }
2115
2116 #[test]
2117 #[cfg(feature = "k256")]
2118 fn test_recover_with_buf_multiple_tx_types() {
2119 use alloy_primitives::address;
2120
2121 let raw_legacy = alloy_primitives::bytes!("f9015482078b8505d21dba0083022ef1947a250d5630b4cf539739df2c5dacb4c659f2488d880c46549a521b13d8b8e47ff36ab50000000000000000000000000000000000000000000066ab5a608bd00a23f2fe000000000000000000000000000000000000000000000000000000000000008000000000000000000000000048c04ed5691981c42154c6167398f95e8f38a7ff00000000000000000000000000000000000000000000000000000000632ceac70000000000000000000000000000000000000000000000000000000000000002000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000006c6ee5e31d828de241282b9606c8e98ea48526e225a0c9077369501641a92ef7399ff81c21639ed4fd8fc69cb793cfa1dbfab342e10aa0615facb2f1bcf3274a354cfe384a38d0cc008a11c2dd23a69111bc6930ba27a8");
2123 let tx_legacy = TxEnvelope::decode_2718(&mut raw_legacy.as_ref()).unwrap();
2124
2125 let raw_eip1559 = alloy_primitives::hex::decode("02f86f0102843b9aca0085029e7822d68298f094d9e1459a7a482635700cbc20bbaf52d495ab9c9680841b55ba3ac080a0c199674fcb29f353693dd779c017823b954b3c69dffa3cd6b2a6ff7888798039a028ca912de909e7e6cdef9cdcaf24c54dd8c1032946dfa1d85c206b32a9064fe8").unwrap();
2127 let tx_eip1559 = TxEnvelope::decode(&mut raw_eip1559.as_slice()).unwrap();
2128
2129 let mut buf = alloc::vec::Vec::new();
2131
2132 let from_legacy = tx_legacy.recover_with_buf(&mut buf).unwrap();
2133 assert_eq!(from_legacy, address!("a12e1462d0ceD572f396F58B6E2D03894cD7C8a4"));
2134
2135 let from_eip1559 = tx_eip1559.recover_with_buf(&mut buf).unwrap();
2136 assert_eq!(from_eip1559, address!("001e2b7dE757bA469a57bF6b23d982458a07eFcE"));
2137
2138 assert!(!buf.is_empty());
2140 }
2141}