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 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::TxEip4844(tx);
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(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 test_serde_roundtrip(tx);
1551
1552 let tx = TxEip4844Variant::TxEip4844WithSidecar(TxEip4844WithSidecar {
1553 tx: TxEip4844 {
1554 chain_id: 1,
1555 nonce: 100,
1556 max_fee_per_gas: 50_000_000_000,
1557 max_priority_fee_per_gas: 1_000_000_000_000,
1558 gas_limit: 1_000_000,
1559 to: Address::random(),
1560 value: U256::from(10e18),
1561 input: Bytes::new(),
1562 access_list: AccessList(vec![AccessListItem {
1563 address: Address::random(),
1564 storage_keys: vec![B256::random()],
1565 }]),
1566 blob_versioned_hashes: vec![B256::random()],
1567 max_fee_per_blob_gas: 0,
1568 },
1569 sidecar: Default::default(),
1570 });
1571 test_serde_roundtrip(tx);
1572 }
1573
1574 #[test]
1575 #[cfg(feature = "serde")]
1576 fn test_serde_roundtrip_eip7702() {
1577 let tx = TxEip7702 {
1578 chain_id: u64::MAX,
1579 nonce: u64::MAX,
1580 gas_limit: u64::MAX,
1581 max_fee_per_gas: u128::MAX,
1582 max_priority_fee_per_gas: u128::MAX,
1583 to: Address::random(),
1584 value: U256::MAX,
1585 input: Bytes::new(),
1586 access_list: AccessList(vec![AccessListItem {
1587 address: Address::random(),
1588 storage_keys: vec![B256::random()],
1589 }]),
1590 authorization_list: vec![(Authorization {
1591 chain_id: U256::from(1),
1592 address: Address::left_padding_from(&[1]),
1593 nonce: 1u64,
1594 })
1595 .into_signed(Signature::from_str("48b55bfa915ac795c431978d8a6a992b628d557da5ff759b307d495a36649353efffd310ac743f371de3b9f7f9cb56c0b28ad43601b4ab949f53faa07bd2c8041b").unwrap())],
1596 };
1597 test_serde_roundtrip(tx);
1598 }
1599
1600 #[test]
1601 #[cfg(feature = "serde")]
1602 fn serde_tx_from_contract_call() {
1603 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"}"#;
1604
1605 let te = serde_json::from_str::<TxEnvelope>(rpc_tx).unwrap();
1606
1607 assert_eq!(
1608 *te.tx_hash(),
1609 alloy_primitives::b256!(
1610 "018b2331d461a4aeedf6a1f9cc37463377578244e6a35216057a8370714e798f"
1611 )
1612 );
1613 }
1614
1615 #[test]
1616 #[cfg(feature = "k256")]
1617 fn test_arbitrary_envelope() {
1618 use crate::transaction::SignerRecoverable;
1619 use arbitrary::Arbitrary;
1620 let mut unstructured = arbitrary::Unstructured::new(b"arbitrary tx envelope");
1621 let tx = TxEnvelope::arbitrary(&mut unstructured).unwrap();
1622
1623 assert!(tx.recover_signer().is_ok());
1624 }
1625
1626 #[test]
1627 #[cfg(feature = "serde")]
1628 fn test_serde_untagged_legacy() {
1629 let data = r#"{
1630 "hash": "0x97efb58d2b42df8d68ab5899ff42b16c7e0af35ed86ae4adb8acaad7e444220c",
1631 "input": "0x",
1632 "r": "0x5d71a4a548503f2916d10c6b1a1557a0e7352eb041acb2bac99d1ad6bb49fd45",
1633 "s": "0x2627bf6d35be48b0e56c61733f63944c0ebcaa85cb4ed6bc7cba3161ba85e0e8",
1634 "v": "0x1c",
1635 "gas": "0x15f90",
1636 "from": "0x2a65aca4d5fc5b5c859090a6c34d164135398226",
1637 "to": "0x8fbeb4488a08d60979b5aa9e13dd00b2726320b2",
1638 "value": "0xf606682badd7800",
1639 "nonce": "0x11f398",
1640 "gasPrice": "0x4a817c800"
1641 }"#;
1642
1643 let tx: TxEnvelope = serde_json::from_str(data).unwrap();
1644
1645 assert!(matches!(tx, TxEnvelope::Legacy(_)));
1646
1647 let data_with_wrong_type = r#"{
1648 "hash": "0x97efb58d2b42df8d68ab5899ff42b16c7e0af35ed86ae4adb8acaad7e444220c",
1649 "input": "0x",
1650 "r": "0x5d71a4a548503f2916d10c6b1a1557a0e7352eb041acb2bac99d1ad6bb49fd45",
1651 "s": "0x2627bf6d35be48b0e56c61733f63944c0ebcaa85cb4ed6bc7cba3161ba85e0e8",
1652 "v": "0x1c",
1653 "gas": "0x15f90",
1654 "from": "0x2a65aca4d5fc5b5c859090a6c34d164135398226",
1655 "to": "0x8fbeb4488a08d60979b5aa9e13dd00b2726320b2",
1656 "value": "0xf606682badd7800",
1657 "nonce": "0x11f398",
1658 "gasPrice": "0x4a817c800",
1659 "type": "0x12"
1660 }"#;
1661
1662 assert!(serde_json::from_str::<TxEnvelope>(data_with_wrong_type).is_err());
1663 }
1664
1665 #[test]
1666 fn test_tx_type_try_from_u8() {
1667 assert_eq!(TxType::try_from(0u8).unwrap(), TxType::Legacy);
1668 assert_eq!(TxType::try_from(1u8).unwrap(), TxType::Eip2930);
1669 assert_eq!(TxType::try_from(2u8).unwrap(), TxType::Eip1559);
1670 assert_eq!(TxType::try_from(3u8).unwrap(), TxType::Eip4844);
1671 assert_eq!(TxType::try_from(4u8).unwrap(), TxType::Eip7702);
1672 assert!(TxType::try_from(5u8).is_err()); }
1674
1675 #[test]
1676 fn test_tx_type_try_from_u64() {
1677 assert_eq!(TxType::try_from(0u64).unwrap(), TxType::Legacy);
1678 assert_eq!(TxType::try_from(1u64).unwrap(), TxType::Eip2930);
1679 assert_eq!(TxType::try_from(2u64).unwrap(), TxType::Eip1559);
1680 assert_eq!(TxType::try_from(3u64).unwrap(), TxType::Eip4844);
1681 assert_eq!(TxType::try_from(4u64).unwrap(), TxType::Eip7702);
1682 assert!(TxType::try_from(10u64).is_err()); }
1684
1685 #[test]
1686 fn test_tx_type_from_conversions() {
1687 let legacy_tx = Signed::new_unchecked(
1688 TxLegacy::default(),
1689 Signature::test_signature(),
1690 Default::default(),
1691 );
1692 let eip2930_tx = Signed::new_unchecked(
1693 TxEip2930::default(),
1694 Signature::test_signature(),
1695 Default::default(),
1696 );
1697 let eip1559_tx = Signed::new_unchecked(
1698 TxEip1559::default(),
1699 Signature::test_signature(),
1700 Default::default(),
1701 );
1702 let eip4844_variant = Signed::new_unchecked(
1703 TxEip4844Variant::TxEip4844(TxEip4844::default()),
1704 Signature::test_signature(),
1705 Default::default(),
1706 );
1707 let eip7702_tx = Signed::new_unchecked(
1708 TxEip7702::default(),
1709 Signature::test_signature(),
1710 Default::default(),
1711 );
1712
1713 assert!(matches!(TxEnvelope::from(legacy_tx), TxEnvelope::Legacy(_)));
1714 assert!(matches!(TxEnvelope::from(eip2930_tx), TxEnvelope::Eip2930(_)));
1715 assert!(matches!(TxEnvelope::from(eip1559_tx), TxEnvelope::Eip1559(_)));
1716 assert!(matches!(TxEnvelope::from(eip4844_variant), TxEnvelope::Eip4844(_)));
1717 assert!(matches!(TxEnvelope::from(eip7702_tx), TxEnvelope::Eip7702(_)));
1718 }
1719
1720 #[test]
1721 fn test_tx_type_is_methods() {
1722 let legacy_tx = TxEnvelope::Legacy(Signed::new_unchecked(
1723 TxLegacy::default(),
1724 Signature::test_signature(),
1725 Default::default(),
1726 ));
1727 let eip2930_tx = TxEnvelope::Eip2930(Signed::new_unchecked(
1728 TxEip2930::default(),
1729 Signature::test_signature(),
1730 Default::default(),
1731 ));
1732 let eip1559_tx = TxEnvelope::Eip1559(Signed::new_unchecked(
1733 TxEip1559::default(),
1734 Signature::test_signature(),
1735 Default::default(),
1736 ));
1737 let eip4844_tx = TxEnvelope::Eip4844(Signed::new_unchecked(
1738 TxEip4844Variant::TxEip4844(TxEip4844::default()),
1739 Signature::test_signature(),
1740 Default::default(),
1741 ));
1742 let eip7702_tx = TxEnvelope::Eip7702(Signed::new_unchecked(
1743 TxEip7702::default(),
1744 Signature::test_signature(),
1745 Default::default(),
1746 ));
1747
1748 assert!(legacy_tx.is_legacy());
1749 assert!(!legacy_tx.is_eip2930());
1750 assert!(!legacy_tx.is_eip1559());
1751 assert!(!legacy_tx.is_eip4844());
1752 assert!(!legacy_tx.is_eip7702());
1753
1754 assert!(eip2930_tx.is_eip2930());
1755 assert!(!eip2930_tx.is_legacy());
1756 assert!(!eip2930_tx.is_eip1559());
1757 assert!(!eip2930_tx.is_eip4844());
1758 assert!(!eip2930_tx.is_eip7702());
1759
1760 assert!(eip1559_tx.is_eip1559());
1761 assert!(!eip1559_tx.is_legacy());
1762 assert!(!eip1559_tx.is_eip2930());
1763 assert!(!eip1559_tx.is_eip4844());
1764 assert!(!eip1559_tx.is_eip7702());
1765
1766 assert!(eip4844_tx.is_eip4844());
1767 assert!(!eip4844_tx.is_legacy());
1768 assert!(!eip4844_tx.is_eip2930());
1769 assert!(!eip4844_tx.is_eip1559());
1770 assert!(!eip4844_tx.is_eip7702());
1771
1772 assert!(eip7702_tx.is_eip7702());
1773 assert!(!eip7702_tx.is_legacy());
1774 assert!(!eip7702_tx.is_eip2930());
1775 assert!(!eip7702_tx.is_eip1559());
1776 assert!(!eip7702_tx.is_eip4844());
1777 }
1778
1779 #[test]
1780 fn test_tx_type() {
1781 let legacy_tx = TxEnvelope::Legacy(Signed::new_unchecked(
1782 TxLegacy::default(),
1783 Signature::test_signature(),
1784 Default::default(),
1785 ));
1786 let eip2930_tx = TxEnvelope::Eip2930(Signed::new_unchecked(
1787 TxEip2930::default(),
1788 Signature::test_signature(),
1789 Default::default(),
1790 ));
1791 let eip1559_tx = TxEnvelope::Eip1559(Signed::new_unchecked(
1792 TxEip1559::default(),
1793 Signature::test_signature(),
1794 Default::default(),
1795 ));
1796 let eip4844_tx = TxEnvelope::Eip4844(Signed::new_unchecked(
1797 TxEip4844Variant::TxEip4844(TxEip4844::default()),
1798 Signature::test_signature(),
1799 Default::default(),
1800 ));
1801 let eip7702_tx = TxEnvelope::Eip7702(Signed::new_unchecked(
1802 TxEip7702::default(),
1803 Signature::test_signature(),
1804 Default::default(),
1805 ));
1806
1807 assert_eq!(legacy_tx.tx_type(), TxType::Legacy);
1808 assert_eq!(eip2930_tx.tx_type(), TxType::Eip2930);
1809 assert_eq!(eip1559_tx.tx_type(), TxType::Eip1559);
1810 assert_eq!(eip4844_tx.tx_type(), TxType::Eip4844);
1811 assert_eq!(eip7702_tx.tx_type(), TxType::Eip7702);
1812 }
1813
1814 #[test]
1815 fn test_try_into_legacy_success() {
1816 let legacy_tx = TxEnvelope::Legacy(Signed::new_unchecked(
1817 TxLegacy::default(),
1818 Signature::test_signature(),
1819 Default::default(),
1820 ));
1821
1822 let result = legacy_tx.try_into_legacy();
1823 assert!(result.is_ok());
1824 }
1825
1826 #[test]
1827 fn test_try_into_legacy_failure() {
1828 let eip1559_tx = TxEnvelope::Eip1559(Signed::new_unchecked(
1829 TxEip1559::default(),
1830 Signature::test_signature(),
1831 Default::default(),
1832 ));
1833
1834 let result = eip1559_tx.try_into_legacy();
1835 assert!(result.is_err());
1836 let error = result.unwrap_err();
1837 assert!(error.to_string().contains("Expected legacy transaction"));
1838 let recovered_envelope = error.into_value();
1840 assert!(recovered_envelope.is_eip1559());
1841 }
1842
1843 #[test]
1844 fn test_try_into_eip2930_success() {
1845 let eip2930_tx = TxEnvelope::Eip2930(Signed::new_unchecked(
1846 TxEip2930::default(),
1847 Signature::test_signature(),
1848 Default::default(),
1849 ));
1850
1851 let result = eip2930_tx.try_into_eip2930();
1852 assert!(result.is_ok());
1853 }
1854
1855 #[test]
1856 fn test_try_into_eip2930_failure() {
1857 let legacy_tx = TxEnvelope::Legacy(Signed::new_unchecked(
1858 TxLegacy::default(),
1859 Signature::test_signature(),
1860 Default::default(),
1861 ));
1862
1863 let result = legacy_tx.try_into_eip2930();
1864 assert!(result.is_err());
1865 let error = result.unwrap_err();
1866 assert!(error.to_string().contains("Expected EIP-2930 transaction"));
1867 let recovered_envelope = error.into_value();
1868 assert!(recovered_envelope.is_legacy());
1869 }
1870
1871 #[test]
1872 fn test_try_into_eip1559_success() {
1873 let eip1559_tx = TxEnvelope::Eip1559(Signed::new_unchecked(
1874 TxEip1559::default(),
1875 Signature::test_signature(),
1876 Default::default(),
1877 ));
1878
1879 let result = eip1559_tx.try_into_eip1559();
1880 assert!(result.is_ok());
1881 }
1882
1883 #[test]
1884 fn test_try_into_eip1559_failure() {
1885 let eip2930_tx = TxEnvelope::Eip2930(Signed::new_unchecked(
1886 TxEip2930::default(),
1887 Signature::test_signature(),
1888 Default::default(),
1889 ));
1890
1891 let result = eip2930_tx.try_into_eip1559();
1892 assert!(result.is_err());
1893 let error = result.unwrap_err();
1894 assert!(error.to_string().contains("Expected EIP-1559 transaction"));
1895 let recovered_envelope = error.into_value();
1896 assert!(recovered_envelope.is_eip2930());
1897 }
1898
1899 #[test]
1900 fn test_try_into_eip4844_success() {
1901 let eip4844_tx = TxEnvelope::Eip4844(Signed::new_unchecked(
1902 TxEip4844Variant::TxEip4844(TxEip4844::default()),
1903 Signature::test_signature(),
1904 Default::default(),
1905 ));
1906
1907 let result = eip4844_tx.try_into_eip4844();
1908 assert!(result.is_ok());
1909 }
1910
1911 #[test]
1912 fn test_try_into_eip4844_failure() {
1913 let eip1559_tx = TxEnvelope::Eip1559(Signed::new_unchecked(
1914 TxEip1559::default(),
1915 Signature::test_signature(),
1916 Default::default(),
1917 ));
1918
1919 let result = eip1559_tx.try_into_eip4844();
1920 assert!(result.is_err());
1921 let error = result.unwrap_err();
1922 assert!(error.to_string().contains("Expected EIP-4844 transaction"));
1923 let recovered_envelope = error.into_value();
1924 assert!(recovered_envelope.is_eip1559());
1925 }
1926
1927 #[test]
1928 fn test_try_into_eip7702_success() {
1929 let eip7702_tx = TxEnvelope::Eip7702(Signed::new_unchecked(
1930 TxEip7702::default(),
1931 Signature::test_signature(),
1932 Default::default(),
1933 ));
1934
1935 let result = eip7702_tx.try_into_eip7702();
1936 assert!(result.is_ok());
1937 }
1938
1939 #[test]
1940 fn test_try_into_eip7702_failure() {
1941 let eip4844_tx = TxEnvelope::Eip4844(Signed::new_unchecked(
1942 TxEip4844Variant::TxEip4844(TxEip4844::default()),
1943 Signature::test_signature(),
1944 Default::default(),
1945 ));
1946
1947 let result = eip4844_tx.try_into_eip7702();
1948 assert!(result.is_err());
1949 let error = result.unwrap_err();
1950 assert!(error.to_string().contains("Expected EIP-7702 transaction"));
1951 let recovered_envelope = error.into_value();
1952 assert!(recovered_envelope.is_eip4844());
1953 }
1954
1955 #[test]
1957 fn decode_raw_legacy() {
1958 let raw = hex!("f8aa0285018ef61d0a832dc6c094cb33aa5b38d79e3d9fa8b10aff38aa201399a7e380b844af7b421018842e4628f3d9ee0e2c7679e29ed5dbaa75be75efecd392943503c9c68adce800000000000000000000000000000000000000000000000000000000000000641ca05e28679806caa50d25e9cb16aef8c0c08b235241b8f6e9d86faadf70421ba664a02353bba82ef2c7ce4dd6695942399163160000272b14f9aa6cbadf011b76efa4");
1959 let tx = TxEnvelope::decode_2718(&mut raw.as_ref()).unwrap();
1960 assert!(tx.chain_id().is_none());
1961 }
1962
1963 #[test]
1964 #[cfg(feature = "serde")]
1965 fn can_deserialize_system_transaction_with_zero_signature_envelope() {
1966 let raw_tx = r#"{
1967 "blockHash": "0x5307b5c812a067f8bc1ed1cc89d319ae6f9a0c9693848bd25c36b5191de60b85",
1968 "blockNumber": "0x45a59bb",
1969 "from": "0x0000000000000000000000000000000000000000",
1970 "gas": "0x1e8480",
1971 "gasPrice": "0x0",
1972 "hash": "0x16ef68aa8f35add3a03167a12b5d1268e344f6605a64ecc3f1c3aa68e98e4e06",
1973 "input": "0xcbd4ece900000000000000000000000032155c9d39084f040ba17890fe8134dbe2a0453f0000000000000000000000004a0126ee88018393b1ad2455060bc350ead9908a000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000469f700000000000000000000000000000000000000000000000000000000000000644ff746f60000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002043e908a4e862aebb10e7e27db0b892b58a7e32af11d64387a414dabc327b00e200000000000000000000000000000000000000000000000000000000",
1974 "nonce": "0x469f7",
1975 "to": "0x4200000000000000000000000000000000000007",
1976 "transactionIndex": "0x0",
1977 "value": "0x0",
1978 "v": "0x0",
1979 "r": "0x0",
1980 "s": "0x0",
1981 "queueOrigin": "l1",
1982 "l1TxOrigin": "0x36bde71c97b33cc4729cf772ae268934f7ab70b2",
1983 "l1BlockNumber": "0xfd1a6c",
1984 "l1Timestamp": "0x63e434ff",
1985 "index": "0x45a59ba",
1986 "queueIndex": "0x469f7",
1987 "rawTransaction": "0xcbd4ece900000000000000000000000032155c9d39084f040ba17890fe8134dbe2a0453f0000000000000000000000004a0126ee88018393b1ad2455060bc350ead9908a000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000469f700000000000000000000000000000000000000000000000000000000000000644ff746f60000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002043e908a4e862aebb10e7e27db0b892b58a7e32af11d64387a414dabc327b00e200000000000000000000000000000000000000000000000000000000"
1988 }"#;
1989
1990 let tx = serde_json::from_str::<TxEnvelope>(raw_tx).unwrap();
1991
1992 assert_eq!(tx.signature().r(), U256::ZERO);
1993 assert_eq!(tx.signature().s(), U256::ZERO);
1994 assert!(!tx.signature().v());
1995
1996 assert_eq!(
1997 tx.hash(),
1998 &b256!("0x16ef68aa8f35add3a03167a12b5d1268e344f6605a64ecc3f1c3aa68e98e4e06"),
1999 "hash should match the transaction hash"
2000 );
2001 }
2002
2003 #[test]
2005 #[cfg(feature = "serde")]
2006 fn serde_block_tx() {
2007 let rpc_tx = r#"{
2008 "blockHash": "0xc0c3190292a82c2ee148774e37e5665f6a205f5ef0cd0885e84701d90ebd442e",
2009 "blockNumber": "0x6edcde",
2010 "transactionIndex": "0x7",
2011 "hash": "0x2cb125e083d6d2631e3752bd2b3d757bf31bf02bfe21de0ffa46fbb118d28b19",
2012 "from": "0x03e5badf3bb1ade1a8f33f94536c827b6531948d",
2013 "to": "0x3267e72dc8780a1512fa69da7759ec66f30350e3",
2014 "input": "0x62e4c545000000000000000000000000464c8ec100f2f42fb4e42e07e203da2324f9fc6700000000000000000000000003e5badf3bb1ade1a8f33f94536c827b6531948d000000000000000000000000a064bfb5c7e81426647dc20a0d854da1538559dc00000000000000000000000000000000000000000000000000c6f3b40b6c0000",
2015 "nonce": "0x2a8",
2016 "value": "0x0",
2017 "gas": "0x28afd",
2018 "gasPrice": "0x23ec5dbc2",
2019 "accessList": [],
2020 "chainId": "0xaa36a7",
2021 "type": "0x0",
2022 "v": "0x1546d71",
2023 "r": "0x809b9f0a1777e376cd1ee5d2f551035643755edf26ea65b7a00c822a24504962",
2024 "s": "0x6a57bb8e21fe85c7e092868ee976fef71edca974d8c452fcf303f9180c764f64"
2025 }"#;
2026
2027 let _ = serde_json::from_str::<TxEnvelope>(rpc_tx).unwrap();
2028 }
2029
2030 #[test]
2032 #[cfg(feature = "serde")]
2033 fn serde_block_tx_legacy_chain_id() {
2034 let rpc_tx = r#"{
2035 "blockHash": "0xc0c3190292a82c2ee148774e37e5665f6a205f5ef0cd0885e84701d90ebd442e",
2036 "blockNumber": "0x6edcde",
2037 "transactionIndex": "0x8",
2038 "hash": "0xe5b458ba9de30b47cb7c0ea836bec7b072053123a7416c5082c97f959a4eebd6",
2039 "from": "0x8b87f0a788cc14b4f0f374da59920f5017ff05de",
2040 "to": "0xcb33aa5b38d79e3d9fa8b10aff38aa201399a7e3",
2041 "input": "0xaf7b421018842e4628f3d9ee0e2c7679e29ed5dbaa75be75efecd392943503c9c68adce80000000000000000000000000000000000000000000000000000000000000064",
2042 "nonce": "0x2",
2043 "value": "0x0",
2044 "gas": "0x2dc6c0",
2045 "gasPrice": "0x18ef61d0a",
2046 "accessList": [],
2047 "chainId": "0xaa36a7",
2048 "type": "0x0",
2049 "v": "0x1c",
2050 "r": "0x5e28679806caa50d25e9cb16aef8c0c08b235241b8f6e9d86faadf70421ba664",
2051 "s": "0x2353bba82ef2c7ce4dd6695942399163160000272b14f9aa6cbadf011b76efa4"
2052 }"#;
2053
2054 let _ = serde_json::from_str::<TxEnvelope>(rpc_tx).unwrap();
2055 }
2056
2057 #[test]
2058 #[cfg(feature = "k256")]
2059 fn test_recover_with_buf_eip1559() {
2060 use alloy_primitives::address;
2061
2062 let raw_tx = alloy_primitives::hex::decode("02f86f0102843b9aca0085029e7822d68298f094d9e1459a7a482635700cbc20bbaf52d495ab9c9680841b55ba3ac080a0c199674fcb29f353693dd779c017823b954b3c69dffa3cd6b2a6ff7888798039a028ca912de909e7e6cdef9cdcaf24c54dd8c1032946dfa1d85c206b32a9064fe8").unwrap();
2064 let tx = TxEnvelope::decode(&mut raw_tx.as_slice()).unwrap();
2065
2066 let from_standard = tx.recover_signer().unwrap();
2068 assert_eq!(from_standard, address!("001e2b7dE757bA469a57bF6b23d982458a07eFcE"));
2069
2070 let mut buf = alloc::vec::Vec::new();
2072 let from_with_buf = tx.recover_with_buf(&mut buf).unwrap();
2073 assert_eq!(from_with_buf, from_standard);
2074
2075 assert!(!buf.is_empty());
2077
2078 buf.clear();
2080 buf.extend_from_slice(b"some garbage data that should be cleared");
2081 let from_with_buf_reuse = tx.recover_with_buf(&mut buf).unwrap();
2082 assert_eq!(from_with_buf_reuse, from_standard);
2083 }
2084
2085 #[test]
2086 #[cfg(feature = "k256")]
2087 fn test_recover_unchecked_with_buf_legacy() {
2088 use alloy_primitives::address;
2089
2090 let raw_tx = alloy_primitives::bytes!("f9015482078b8505d21dba0083022ef1947a250d5630b4cf539739df2c5dacb4c659f2488d880c46549a521b13d8b8e47ff36ab50000000000000000000000000000000000000000000066ab5a608bd00a23f2fe000000000000000000000000000000000000000000000000000000000000008000000000000000000000000048c04ed5691981c42154c6167398f95e8f38a7ff00000000000000000000000000000000000000000000000000000000632ceac70000000000000000000000000000000000000000000000000000000000000002000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000006c6ee5e31d828de241282b9606c8e98ea48526e225a0c9077369501641a92ef7399ff81c21639ed4fd8fc69cb793cfa1dbfab342e10aa0615facb2f1bcf3274a354cfe384a38d0cc008a11c2dd23a69111bc6930ba27a8");
2092 let tx = TxEnvelope::decode_2718(&mut raw_tx.as_ref()).unwrap();
2093
2094 let from_standard = tx.recover_signer_unchecked().unwrap();
2096 assert_eq!(from_standard, address!("a12e1462d0ceD572f396F58B6E2D03894cD7C8a4"));
2097
2098 let mut buf = alloc::vec::Vec::new();
2100 let from_with_buf = tx.recover_unchecked_with_buf(&mut buf).unwrap();
2101 assert_eq!(from_with_buf, from_standard);
2102
2103 assert!(!buf.is_empty());
2105
2106 let original_len = buf.len();
2108 buf.extend_from_slice(&[0xFF; 100]); let from_with_buf_reuse = tx.recover_unchecked_with_buf(&mut buf).unwrap();
2110 assert_eq!(from_with_buf_reuse, from_standard);
2111 assert_eq!(buf.len(), original_len);
2113 }
2114
2115 #[test]
2116 #[cfg(feature = "k256")]
2117 fn test_recover_with_buf_multiple_tx_types() {
2118 use alloy_primitives::address;
2119
2120 let raw_legacy = alloy_primitives::bytes!("f9015482078b8505d21dba0083022ef1947a250d5630b4cf539739df2c5dacb4c659f2488d880c46549a521b13d8b8e47ff36ab50000000000000000000000000000000000000000000066ab5a608bd00a23f2fe000000000000000000000000000000000000000000000000000000000000008000000000000000000000000048c04ed5691981c42154c6167398f95e8f38a7ff00000000000000000000000000000000000000000000000000000000632ceac70000000000000000000000000000000000000000000000000000000000000002000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000006c6ee5e31d828de241282b9606c8e98ea48526e225a0c9077369501641a92ef7399ff81c21639ed4fd8fc69cb793cfa1dbfab342e10aa0615facb2f1bcf3274a354cfe384a38d0cc008a11c2dd23a69111bc6930ba27a8");
2122 let tx_legacy = TxEnvelope::decode_2718(&mut raw_legacy.as_ref()).unwrap();
2123
2124 let raw_eip1559 = alloy_primitives::hex::decode("02f86f0102843b9aca0085029e7822d68298f094d9e1459a7a482635700cbc20bbaf52d495ab9c9680841b55ba3ac080a0c199674fcb29f353693dd779c017823b954b3c69dffa3cd6b2a6ff7888798039a028ca912de909e7e6cdef9cdcaf24c54dd8c1032946dfa1d85c206b32a9064fe8").unwrap();
2126 let tx_eip1559 = TxEnvelope::decode(&mut raw_eip1559.as_slice()).unwrap();
2127
2128 let mut buf = alloc::vec::Vec::new();
2130
2131 let from_legacy = tx_legacy.recover_with_buf(&mut buf).unwrap();
2132 assert_eq!(from_legacy, address!("a12e1462d0ceD572f396F58B6E2D03894cD7C8a4"));
2133
2134 let from_eip1559 = tx_eip1559.recover_with_buf(&mut buf).unwrap();
2135 assert_eq!(from_eip1559, address!("001e2b7dE757bA469a57bF6b23d982458a07eFcE"));
2136
2137 assert!(!buf.is_empty());
2139 }
2140}