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