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