1use crate::{
2 error::ValueError,
3 transaction::{
4 eip4844::{TxEip4844, TxEip4844Variant},
5 PooledTransaction, RlpEcdsaDecodableTx, RlpEcdsaEncodableTx,
6 },
7 EthereumTypedTransaction, Signed, Transaction, TxEip1559, TxEip2930, TxEip7702, TxLegacy,
8};
9use alloy_eips::{
10 eip2718::{Decodable2718, Eip2718Error, Eip2718Result, Encodable2718},
11 eip2930::AccessList,
12 Typed2718,
13};
14use alloy_primitives::{Bytes, ChainId, Signature, TxKind, B256, U256, U64, U8};
15use alloy_rlp::{Decodable, Encodable};
16use core::{
17 fmt::{self, Debug},
18 hash::{Hash, Hasher},
19};
20
21use super::SignableTransaction;
22
23pub type TxEnvelope = EthereumTxEnvelope<TxEip4844Variant>;
35
36impl TxEnvelope {
37 pub fn try_into_pooled(self) -> Result<PooledTransaction, ValueError<Self>> {
42 match self {
43 Self::Legacy(tx) => Ok(tx.into()),
44 Self::Eip2930(tx) => Ok(tx.into()),
45 Self::Eip1559(tx) => Ok(tx.into()),
46 Self::Eip4844(tx) => PooledTransaction::try_from(tx).map_err(ValueError::convert),
47 Self::Eip7702(tx) => Ok(tx.into()),
48 }
49 }
50
51 #[inline]
53 pub fn into_typed_transaction(self) -> EthereumTypedTransaction<TxEip4844Variant> {
54 match self {
55 Self::Legacy(tx) => EthereumTypedTransaction::Legacy(tx.into_parts().0),
56 Self::Eip2930(tx) => EthereumTypedTransaction::Eip2930(tx.into_parts().0),
57 Self::Eip1559(tx) => EthereumTypedTransaction::Eip1559(tx.into_parts().0),
58 Self::Eip4844(tx) => EthereumTypedTransaction::Eip4844(tx.into_parts().0),
59 Self::Eip7702(tx) => EthereumTypedTransaction::Eip7702(tx.into_parts().0),
60 }
61 }
62}
63impl<T> EthereumTxEnvelope<T> {
64 #[doc(hidden)]
66 pub fn input_mut(&mut self) -> &mut Bytes
67 where
68 T: AsMut<TxEip4844>,
69 {
70 match self {
71 Self::Eip1559(tx) => &mut tx.tx_mut().input,
72 Self::Eip2930(tx) => &mut tx.tx_mut().input,
73 Self::Legacy(tx) => &mut tx.tx_mut().input,
74 Self::Eip7702(tx) => &mut tx.tx_mut().input,
75 Self::Eip4844(tx) => &mut tx.tx_mut().as_mut().input,
76 }
77 }
78}
79
80#[repr(u8)]
104#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
105#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
106#[cfg_attr(feature = "serde", serde(into = "U8", try_from = "U64"))]
107#[doc(alias = "TransactionType")]
108pub enum TxType {
109 #[default]
111 Legacy = 0,
112 Eip2930 = 1,
114 Eip1559 = 2,
116 Eip4844 = 3,
118 Eip7702 = 4,
120}
121
122impl From<TxType> for u8 {
123 fn from(value: TxType) -> Self {
124 value as Self
125 }
126}
127
128impl From<TxType> for U8 {
129 fn from(tx_type: TxType) -> Self {
130 Self::from(u8::from(tx_type))
131 }
132}
133impl TxType {
134 #[inline]
136 pub const fn is_legacy(&self) -> bool {
137 matches!(self, Self::Legacy)
138 }
139
140 #[inline]
142 pub const fn is_eip2930(&self) -> bool {
143 matches!(self, Self::Eip2930)
144 }
145
146 #[inline]
148 pub const fn is_eip1559(&self) -> bool {
149 matches!(self, Self::Eip1559)
150 }
151
152 #[inline]
154 pub const fn is_eip4844(&self) -> bool {
155 matches!(self, Self::Eip4844)
156 }
157
158 #[inline]
160 pub const fn is_eip7702(&self) -> bool {
161 matches!(self, Self::Eip7702)
162 }
163}
164
165impl fmt::Display for TxType {
166 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
167 match self {
168 Self::Legacy => write!(f, "Legacy"),
169 Self::Eip2930 => write!(f, "EIP-2930"),
170 Self::Eip1559 => write!(f, "EIP-1559"),
171 Self::Eip4844 => write!(f, "EIP-4844"),
172 Self::Eip7702 => write!(f, "EIP-7702"),
173 }
174 }
175}
176
177impl PartialEq<u8> for TxType {
178 fn eq(&self, other: &u8) -> bool {
179 (*self as u8) == *other
180 }
181}
182
183impl PartialEq<TxType> for u8 {
184 fn eq(&self, other: &TxType) -> bool {
185 *self == *other as Self
186 }
187}
188
189#[cfg(any(test, feature = "arbitrary"))]
190impl arbitrary::Arbitrary<'_> for TxType {
191 fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
192 Ok(u.int_in_range(0u8..=4)?.try_into().unwrap())
193 }
194}
195
196impl TryFrom<u8> for TxType {
197 type Error = Eip2718Error;
198
199 fn try_from(value: u8) -> Result<Self, Self::Error> {
200 Ok(match value {
201 0 => Self::Legacy,
202 1 => Self::Eip2930,
203 2 => Self::Eip1559,
204 3 => Self::Eip4844,
205 4 => Self::Eip7702,
206 _ => return Err(Eip2718Error::UnexpectedType(value)),
207 })
208 }
209}
210
211impl TryFrom<u64> for TxType {
212 type Error = &'static str;
213
214 fn try_from(value: u64) -> Result<Self, Self::Error> {
215 let err = || "invalid tx type";
216 let value: u8 = value.try_into().map_err(|_| err())?;
217 Self::try_from(value).map_err(|_| err())
218 }
219}
220
221impl TryFrom<U8> for TxType {
222 type Error = Eip2718Error;
223
224 fn try_from(value: U8) -> Result<Self, Self::Error> {
225 value.to::<u8>().try_into()
226 }
227}
228
229impl TryFrom<U64> for TxType {
230 type Error = &'static str;
231
232 fn try_from(value: U64) -> Result<Self, Self::Error> {
233 value.to::<u64>().try_into()
234 }
235}
236
237impl Encodable for TxType {
238 fn encode(&self, out: &mut dyn alloy_rlp::BufMut) {
239 (*self as u8).encode(out);
240 }
241
242 fn length(&self) -> usize {
243 1
244 }
245}
246
247impl Decodable for TxType {
248 fn decode(buf: &mut &[u8]) -> alloy_rlp::Result<Self> {
249 let ty = u8::decode(buf)?;
250 Self::try_from(ty).map_err(|_| alloy_rlp::Error::Custom("invalid transaction type"))
251 }
252}
253
254impl Typed2718 for TxType {
255 fn ty(&self) -> u8 {
256 (*self).into()
257 }
258}
259
260#[derive(Clone, Debug)]
272#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
273#[cfg_attr(
274 feature = "serde",
275 serde(
276 into = "serde_from::TaggedTxEnvelope<Eip4844>",
277 from = "serde_from::MaybeTaggedTxEnvelope<Eip4844>",
278 bound = "Eip4844: Clone + RlpEcdsaEncodableTx + serde::Serialize + serde::de::DeserializeOwned"
279 )
280)]
281#[cfg_attr(all(any(test, feature = "arbitrary"), feature = "k256"), derive(arbitrary::Arbitrary))]
282#[cfg_attr(
283 all(any(test, feature = "arbitrary"), feature = "k256"),
284 arbitrary(
285 bound = "Eip4844: for<'a> arbitrary::Arbitrary<'a> + RlpEcdsaEncodableTx + SignableTransaction<Signature>"
286 )
287)]
288#[doc(alias = "TransactionEnvelope")]
289pub enum EthereumTxEnvelope<Eip4844> {
290 Legacy(Signed<TxLegacy>),
292 Eip2930(Signed<TxEip2930>),
294 Eip1559(Signed<TxEip1559>),
296 Eip4844(Signed<Eip4844>),
304 Eip7702(Signed<TxEip7702>),
306}
307
308impl<Eip4844: RlpEcdsaEncodableTx + PartialEq> PartialEq for EthereumTxEnvelope<Eip4844>
309where
310 Eip4844: PartialEq,
311{
312 fn eq(&self, other: &Self) -> bool {
313 match (self, other) {
314 (Self::Legacy(f0_self), Self::Legacy(f0_other)) => f0_self.eq(f0_other),
315 (Self::Eip2930(f0_self), Self::Eip2930(f0_other)) => f0_self.eq(f0_other),
316 (Self::Eip1559(f0_self), Self::Eip1559(f0_other)) => f0_self.eq(f0_other),
317 (Self::Eip4844(f0_self), Self::Eip4844(f0_other)) => f0_self.eq(f0_other),
318 (Self::Eip7702(f0_self), Self::Eip7702(f0_other)) => f0_self.eq(f0_other),
319 _unused => false,
320 }
321 }
322}
323
324impl<Eip4844: RlpEcdsaEncodableTx + PartialEq> Eq for EthereumTxEnvelope<Eip4844> {}
325
326impl<Eip4844> Hash for EthereumTxEnvelope<Eip4844>
327where
328 Self: Encodable2718,
329{
330 fn hash<H: Hasher>(&self, state: &mut H) {
331 self.trie_hash().hash(state);
332 }
333}
334
335impl<T, Eip4844> From<Signed<T>> for EthereumTxEnvelope<Eip4844>
336where
337 EthereumTypedTransaction<Eip4844>: From<T>,
338 T: RlpEcdsaEncodableTx,
339{
340 fn from(v: Signed<T>) -> Self {
341 let (tx, sig, hash) = v.into_parts();
342 let typed = EthereumTypedTransaction::from(tx);
343 match typed {
344 EthereumTypedTransaction::Legacy(tx_legacy) => {
345 let tx = Signed::new_unchecked(tx_legacy, sig, hash);
346 Self::Legacy(tx)
347 }
348 EthereumTypedTransaction::Eip2930(tx_eip2930) => {
349 let tx = Signed::new_unchecked(tx_eip2930, sig, hash);
350 Self::Eip2930(tx)
351 }
352 EthereumTypedTransaction::Eip1559(tx_eip1559) => {
353 let tx = Signed::new_unchecked(tx_eip1559, sig, hash);
354 Self::Eip1559(tx)
355 }
356 EthereumTypedTransaction::Eip4844(tx_eip4844_variant) => {
357 let tx = Signed::new_unchecked(tx_eip4844_variant, sig, hash);
358 Self::Eip4844(tx)
359 }
360 EthereumTypedTransaction::Eip7702(tx_eip7702) => {
361 let tx = Signed::new_unchecked(tx_eip7702, sig, hash);
362 Self::Eip7702(tx)
363 }
364 }
365 }
366}
367
368impl<Eip4844: RlpEcdsaEncodableTx> From<EthereumTxEnvelope<Eip4844>>
369 for Signed<EthereumTypedTransaction<Eip4844>>
370where
371 EthereumTypedTransaction<Eip4844>: From<Eip4844>,
372{
373 fn from(value: EthereumTxEnvelope<Eip4844>) -> Self {
374 value.into_signed()
375 }
376}
377
378impl<Eip4844: RlpEcdsaEncodableTx> EthereumTxEnvelope<Eip4844> {
379 #[inline]
381 pub const fn is_legacy(&self) -> bool {
382 matches!(self, Self::Legacy(_))
383 }
384
385 #[inline]
387 pub const fn is_eip2930(&self) -> bool {
388 matches!(self, Self::Eip2930(_))
389 }
390
391 #[inline]
393 pub const fn is_eip1559(&self) -> bool {
394 matches!(self, Self::Eip1559(_))
395 }
396
397 #[inline]
399 pub const fn is_eip4844(&self) -> bool {
400 matches!(self, Self::Eip4844(_))
401 }
402
403 #[inline]
405 pub const fn is_eip7702(&self) -> bool {
406 matches!(self, Self::Eip7702(_))
407 }
408
409 pub fn into_signed(self) -> Signed<EthereumTypedTransaction<Eip4844>>
411 where
412 EthereumTypedTransaction<Eip4844>: From<Eip4844>,
413 {
414 match self {
415 Self::Legacy(tx) => tx.convert(),
416 Self::Eip2930(tx) => tx.convert(),
417 Self::Eip1559(tx) => tx.convert(),
418 Self::Eip4844(tx) => tx.convert(),
419 Self::Eip7702(tx) => tx.convert(),
420 }
421 }
422
423 #[inline]
432 pub const fn is_replay_protected(&self) -> bool {
433 match self {
434 Self::Legacy(tx) => tx.tx().chain_id.is_some(),
435 _ => true,
436 }
437 }
438
439 pub const fn as_legacy(&self) -> Option<&Signed<TxLegacy>> {
441 match self {
442 Self::Legacy(tx) => Some(tx),
443 _ => None,
444 }
445 }
446
447 pub const fn as_eip2930(&self) -> Option<&Signed<TxEip2930>> {
449 match self {
450 Self::Eip2930(tx) => Some(tx),
451 _ => None,
452 }
453 }
454
455 pub const fn as_eip1559(&self) -> Option<&Signed<TxEip1559>> {
457 match self {
458 Self::Eip1559(tx) => Some(tx),
459 _ => None,
460 }
461 }
462
463 pub const fn as_eip4844(&self) -> Option<&Signed<Eip4844>> {
465 match self {
466 Self::Eip4844(tx) => Some(tx),
467 _ => None,
468 }
469 }
470
471 pub const fn as_eip7702(&self) -> Option<&Signed<TxEip7702>> {
473 match self {
474 Self::Eip7702(tx) => Some(tx),
475 _ => None,
476 }
477 }
478
479 #[cfg(feature = "k256")]
481 pub fn recover_signer(
482 &self,
483 ) -> Result<alloy_primitives::Address, alloy_primitives::SignatureError>
484 where
485 Eip4844: SignableTransaction<Signature>,
486 {
487 match self {
488 Self::Legacy(tx) => tx.recover_signer(),
489 Self::Eip2930(tx) => tx.recover_signer(),
490 Self::Eip1559(tx) => tx.recover_signer(),
491 Self::Eip4844(tx) => tx.recover_signer(),
492 Self::Eip7702(tx) => tx.recover_signer(),
493 }
494 }
495
496 #[cfg(feature = "k256")]
498 pub fn try_into_recovered(
499 self,
500 ) -> Result<crate::transaction::Recovered<Self>, alloy_primitives::SignatureError>
501 where
502 Eip4844: SignableTransaction<Signature>,
503 {
504 let signer = self.recover_signer()?;
505 Ok(crate::transaction::Recovered::new_unchecked(self, signer))
506 }
507
508 pub fn signature_hash(&self) -> B256
510 where
511 Eip4844: SignableTransaction<Signature>,
512 {
513 match self {
514 Self::Legacy(tx) => tx.signature_hash(),
515 Self::Eip2930(tx) => tx.signature_hash(),
516 Self::Eip1559(tx) => tx.signature_hash(),
517 Self::Eip4844(tx) => tx.signature_hash(),
518 Self::Eip7702(tx) => tx.signature_hash(),
519 }
520 }
521
522 pub const fn signature(&self) -> &Signature {
524 match self {
525 Self::Legacy(tx) => tx.signature(),
526 Self::Eip2930(tx) => tx.signature(),
527 Self::Eip1559(tx) => tx.signature(),
528 Self::Eip4844(tx) => tx.signature(),
529 Self::Eip7702(tx) => tx.signature(),
530 }
531 }
532
533 #[doc(alias = "transaction_hash")]
535 pub fn tx_hash(&self) -> &B256 {
536 match self {
537 Self::Legacy(tx) => tx.hash(),
538 Self::Eip2930(tx) => tx.hash(),
539 Self::Eip1559(tx) => tx.hash(),
540 Self::Eip4844(tx) => tx.hash(),
541 Self::Eip7702(tx) => tx.hash(),
542 }
543 }
544
545 pub fn hash(&self) -> &B256 {
547 match self {
548 Self::Legacy(tx) => tx.hash(),
549 Self::Eip2930(tx) => tx.hash(),
550 Self::Eip1559(tx) => tx.hash(),
551 Self::Eip7702(tx) => tx.hash(),
552 Self::Eip4844(tx) => tx.hash(),
553 }
554 }
555
556 #[doc(alias = "transaction_type")]
558 pub const fn tx_type(&self) -> TxType {
559 match self {
560 Self::Legacy(_) => TxType::Legacy,
561 Self::Eip2930(_) => TxType::Eip2930,
562 Self::Eip1559(_) => TxType::Eip1559,
563 Self::Eip4844(_) => TxType::Eip4844,
564 Self::Eip7702(_) => TxType::Eip7702,
565 }
566 }
567
568 pub fn eip2718_encoded_length(&self) -> usize {
570 match self {
571 Self::Legacy(t) => t.eip2718_encoded_length(),
572 Self::Eip2930(t) => t.eip2718_encoded_length(),
573 Self::Eip1559(t) => t.eip2718_encoded_length(),
574 Self::Eip4844(t) => t.eip2718_encoded_length(),
575 Self::Eip7702(t) => t.eip2718_encoded_length(),
576 }
577 }
578}
579
580impl<Eip4844> Encodable for EthereumTxEnvelope<Eip4844>
581where
582 Self: Encodable2718,
583{
584 fn encode(&self, out: &mut dyn alloy_rlp::BufMut) {
585 self.network_encode(out)
586 }
587
588 fn length(&self) -> usize {
589 self.network_len()
590 }
591}
592
593impl<Eip4844: RlpEcdsaDecodableTx> Decodable for EthereumTxEnvelope<Eip4844> {
594 fn decode(buf: &mut &[u8]) -> alloy_rlp::Result<Self> {
595 Ok(Self::network_decode(buf)?)
596 }
597}
598
599impl<Eip4844: RlpEcdsaDecodableTx> Decodable2718 for EthereumTxEnvelope<Eip4844> {
600 fn typed_decode(ty: u8, buf: &mut &[u8]) -> Eip2718Result<Self> {
601 match ty.try_into().map_err(|_| alloy_rlp::Error::Custom("unexpected tx type"))? {
602 TxType::Eip2930 => Ok(TxEip2930::rlp_decode_signed(buf)?.into()),
603 TxType::Eip1559 => Ok(TxEip1559::rlp_decode_signed(buf)?.into()),
604 TxType::Eip4844 => Ok(Self::Eip4844(Eip4844::rlp_decode_signed(buf)?)),
605 TxType::Eip7702 => Ok(TxEip7702::rlp_decode_signed(buf)?.into()),
606 TxType::Legacy => Err(Eip2718Error::UnexpectedType(0)),
607 }
608 }
609
610 fn fallback_decode(buf: &mut &[u8]) -> Eip2718Result<Self> {
611 TxLegacy::rlp_decode_signed(buf).map(Into::into).map_err(Into::into)
612 }
613}
614
615impl<T> Typed2718 for Signed<T>
616where
617 T: RlpEcdsaEncodableTx + Send + Sync + Typed2718,
618{
619 fn ty(&self) -> u8 {
620 self.tx().ty()
621 }
622}
623
624impl<T> Encodable2718 for Signed<T>
625where
626 T: RlpEcdsaEncodableTx + Typed2718 + Send + Sync,
627{
628 fn encode_2718_len(&self) -> usize {
629 self.eip2718_encoded_length()
630 }
631
632 fn encode_2718(&self, out: &mut dyn alloy_rlp::BufMut) {
633 self.eip2718_encode(out)
634 }
635
636 fn trie_hash(&self) -> B256 {
637 *self.hash()
638 }
639}
640
641impl<T> Decodable2718 for Signed<T>
642where
643 T: RlpEcdsaDecodableTx + Typed2718 + Send + Sync,
644{
645 fn typed_decode(ty: u8, buf: &mut &[u8]) -> Eip2718Result<Self> {
646 let decoded = T::rlp_decode_signed(buf)?;
647
648 if decoded.ty() != ty {
649 return Err(Eip2718Error::UnexpectedType(ty));
650 }
651
652 Ok(decoded)
653 }
654
655 fn fallback_decode(buf: &mut &[u8]) -> Eip2718Result<Self> {
656 T::rlp_decode_signed(buf).map_err(Into::into)
657 }
658}
659
660impl<Eip4844> Encodable2718 for EthereumTxEnvelope<Eip4844>
661where
662 Eip4844: RlpEcdsaEncodableTx + Typed2718 + Send + Sync,
663{
664 fn encode_2718_len(&self) -> usize {
665 self.eip2718_encoded_length()
666 }
667
668 fn encode_2718(&self, out: &mut dyn alloy_rlp::BufMut) {
669 match self {
670 Self::Legacy(tx) => tx.eip2718_encode(out),
672 Self::Eip2930(tx) => {
673 tx.eip2718_encode(out);
674 }
675 Self::Eip1559(tx) => {
676 tx.eip2718_encode(out);
677 }
678 Self::Eip4844(tx) => {
679 tx.eip2718_encode(out);
680 }
681 Self::Eip7702(tx) => {
682 tx.eip2718_encode(out);
683 }
684 }
685 }
686
687 fn trie_hash(&self) -> B256 {
688 match self {
689 Self::Legacy(tx) => *tx.hash(),
690 Self::Eip2930(tx) => *tx.hash(),
691 Self::Eip1559(tx) => *tx.hash(),
692 Self::Eip4844(tx) => *tx.hash(),
693 Self::Eip7702(tx) => *tx.hash(),
694 }
695 }
696}
697
698impl<Eip4844> Transaction for EthereumTxEnvelope<Eip4844>
699where
700 Self: Typed2718,
701 Eip4844: Transaction + Send + Sync,
702{
703 #[inline]
704 fn chain_id(&self) -> Option<ChainId> {
705 match self {
706 Self::Legacy(tx) => tx.tx().chain_id(),
707 Self::Eip2930(tx) => tx.tx().chain_id(),
708 Self::Eip1559(tx) => tx.tx().chain_id(),
709 Self::Eip4844(tx) => tx.tx().chain_id(),
710 Self::Eip7702(tx) => tx.tx().chain_id(),
711 }
712 }
713
714 #[inline]
715 fn nonce(&self) -> u64 {
716 match self {
717 Self::Legacy(tx) => tx.tx().nonce(),
718 Self::Eip2930(tx) => tx.tx().nonce(),
719 Self::Eip1559(tx) => tx.tx().nonce(),
720 Self::Eip4844(tx) => tx.tx().nonce(),
721 Self::Eip7702(tx) => tx.tx().nonce(),
722 }
723 }
724
725 #[inline]
726 fn gas_limit(&self) -> u64 {
727 match self {
728 Self::Legacy(tx) => tx.tx().gas_limit(),
729 Self::Eip2930(tx) => tx.tx().gas_limit(),
730 Self::Eip1559(tx) => tx.tx().gas_limit(),
731 Self::Eip4844(tx) => tx.tx().gas_limit(),
732 Self::Eip7702(tx) => tx.tx().gas_limit(),
733 }
734 }
735
736 #[inline]
737 fn gas_price(&self) -> Option<u128> {
738 match self {
739 Self::Legacy(tx) => tx.tx().gas_price(),
740 Self::Eip2930(tx) => tx.tx().gas_price(),
741 Self::Eip1559(tx) => tx.tx().gas_price(),
742 Self::Eip4844(tx) => tx.tx().gas_price(),
743 Self::Eip7702(tx) => tx.tx().gas_price(),
744 }
745 }
746
747 #[inline]
748 fn max_fee_per_gas(&self) -> u128 {
749 match self {
750 Self::Legacy(tx) => tx.tx().max_fee_per_gas(),
751 Self::Eip2930(tx) => tx.tx().max_fee_per_gas(),
752 Self::Eip1559(tx) => tx.tx().max_fee_per_gas(),
753 Self::Eip4844(tx) => tx.tx().max_fee_per_gas(),
754 Self::Eip7702(tx) => tx.tx().max_fee_per_gas(),
755 }
756 }
757
758 #[inline]
759 fn max_priority_fee_per_gas(&self) -> Option<u128> {
760 match self {
761 Self::Legacy(tx) => tx.tx().max_priority_fee_per_gas(),
762 Self::Eip2930(tx) => tx.tx().max_priority_fee_per_gas(),
763 Self::Eip1559(tx) => tx.tx().max_priority_fee_per_gas(),
764 Self::Eip4844(tx) => tx.tx().max_priority_fee_per_gas(),
765 Self::Eip7702(tx) => tx.tx().max_priority_fee_per_gas(),
766 }
767 }
768
769 #[inline]
770 fn max_fee_per_blob_gas(&self) -> Option<u128> {
771 match self {
772 Self::Legacy(tx) => tx.tx().max_fee_per_blob_gas(),
773 Self::Eip2930(tx) => tx.tx().max_fee_per_blob_gas(),
774 Self::Eip1559(tx) => tx.tx().max_fee_per_blob_gas(),
775 Self::Eip4844(tx) => tx.tx().max_fee_per_blob_gas(),
776 Self::Eip7702(tx) => tx.tx().max_fee_per_blob_gas(),
777 }
778 }
779
780 #[inline]
781 fn priority_fee_or_price(&self) -> u128 {
782 match self {
783 Self::Legacy(tx) => tx.tx().priority_fee_or_price(),
784 Self::Eip2930(tx) => tx.tx().priority_fee_or_price(),
785 Self::Eip1559(tx) => tx.tx().priority_fee_or_price(),
786 Self::Eip4844(tx) => tx.tx().priority_fee_or_price(),
787 Self::Eip7702(tx) => tx.tx().priority_fee_or_price(),
788 }
789 }
790
791 fn effective_gas_price(&self, base_fee: Option<u64>) -> u128 {
792 match self {
793 Self::Legacy(tx) => tx.tx().effective_gas_price(base_fee),
794 Self::Eip2930(tx) => tx.tx().effective_gas_price(base_fee),
795 Self::Eip1559(tx) => tx.tx().effective_gas_price(base_fee),
796 Self::Eip4844(tx) => tx.tx().effective_gas_price(base_fee),
797 Self::Eip7702(tx) => tx.tx().effective_gas_price(base_fee),
798 }
799 }
800
801 #[inline]
802 fn is_dynamic_fee(&self) -> bool {
803 match self {
804 Self::Legacy(tx) => tx.tx().is_dynamic_fee(),
805 Self::Eip2930(tx) => tx.tx().is_dynamic_fee(),
806 Self::Eip1559(tx) => tx.tx().is_dynamic_fee(),
807 Self::Eip4844(tx) => tx.tx().is_dynamic_fee(),
808 Self::Eip7702(tx) => tx.tx().is_dynamic_fee(),
809 }
810 }
811
812 #[inline]
813 fn kind(&self) -> TxKind {
814 match self {
815 Self::Legacy(tx) => tx.tx().kind(),
816 Self::Eip2930(tx) => tx.tx().kind(),
817 Self::Eip1559(tx) => tx.tx().kind(),
818 Self::Eip4844(tx) => tx.tx().kind(),
819 Self::Eip7702(tx) => tx.tx().kind(),
820 }
821 }
822
823 #[inline]
824 fn is_create(&self) -> bool {
825 match self {
826 Self::Legacy(tx) => tx.tx().is_create(),
827 Self::Eip2930(tx) => tx.tx().is_create(),
828 Self::Eip1559(tx) => tx.tx().is_create(),
829 Self::Eip4844(tx) => tx.tx().is_create(),
830 Self::Eip7702(tx) => tx.tx().is_create(),
831 }
832 }
833
834 #[inline]
835 fn value(&self) -> U256 {
836 match self {
837 Self::Legacy(tx) => tx.tx().value(),
838 Self::Eip2930(tx) => tx.tx().value(),
839 Self::Eip1559(tx) => tx.tx().value(),
840 Self::Eip4844(tx) => tx.tx().value(),
841 Self::Eip7702(tx) => tx.tx().value(),
842 }
843 }
844
845 #[inline]
846 fn input(&self) -> &Bytes {
847 match self {
848 Self::Legacy(tx) => tx.tx().input(),
849 Self::Eip2930(tx) => tx.tx().input(),
850 Self::Eip1559(tx) => tx.tx().input(),
851 Self::Eip4844(tx) => tx.tx().input(),
852 Self::Eip7702(tx) => tx.tx().input(),
853 }
854 }
855
856 #[inline]
857 fn access_list(&self) -> Option<&AccessList> {
858 match self {
859 Self::Legacy(tx) => tx.tx().access_list(),
860 Self::Eip2930(tx) => tx.tx().access_list(),
861 Self::Eip1559(tx) => tx.tx().access_list(),
862 Self::Eip4844(tx) => tx.tx().access_list(),
863 Self::Eip7702(tx) => tx.tx().access_list(),
864 }
865 }
866
867 #[inline]
868 fn blob_versioned_hashes(&self) -> Option<&[B256]> {
869 match self {
870 Self::Legacy(tx) => tx.tx().blob_versioned_hashes(),
871 Self::Eip2930(tx) => tx.tx().blob_versioned_hashes(),
872 Self::Eip1559(tx) => tx.tx().blob_versioned_hashes(),
873 Self::Eip4844(tx) => tx.tx().blob_versioned_hashes(),
874 Self::Eip7702(tx) => tx.tx().blob_versioned_hashes(),
875 }
876 }
877
878 fn authorization_list(&self) -> Option<&[alloy_eips::eip7702::SignedAuthorization]> {
879 match self {
880 Self::Legacy(tx) => tx.tx().authorization_list(),
881 Self::Eip2930(tx) => tx.tx().authorization_list(),
882 Self::Eip1559(tx) => tx.tx().authorization_list(),
883 Self::Eip4844(tx) => tx.tx().authorization_list(),
884 Self::Eip7702(tx) => tx.tx().authorization_list(),
885 }
886 }
887}
888
889impl<Eip4844: Typed2718> Typed2718 for EthereumTxEnvelope<Eip4844> {
890 fn ty(&self) -> u8 {
891 match self {
892 Self::Legacy(tx) => tx.tx().ty(),
893 Self::Eip2930(tx) => tx.tx().ty(),
894 Self::Eip1559(tx) => tx.tx().ty(),
895 Self::Eip4844(tx) => tx.tx().ty(),
896 Self::Eip7702(tx) => tx.tx().ty(),
897 }
898 }
899}
900
901#[cfg(feature = "serde")]
902mod serde_from {
903 use crate::{
913 transaction::RlpEcdsaEncodableTx, EthereumTxEnvelope, Signed, TxEip1559, TxEip2930,
914 TxEip7702, TxLegacy,
915 };
916
917 #[derive(Debug, serde::Deserialize)]
918 pub(crate) struct UntaggedLegacy {
919 #[serde(default, rename = "type", deserialize_with = "alloy_serde::reject_if_some")]
920 pub _ty: Option<()>,
921 #[serde(flatten, with = "crate::transaction::signed_legacy_serde")]
922 pub tx: Signed<TxLegacy>,
923 }
924
925 #[derive(Debug)]
926 pub(crate) enum MaybeTaggedTxEnvelope<Eip4844> {
927 Tagged(TaggedTxEnvelope<Eip4844>),
928 Untagged(UntaggedLegacy),
929 }
930
931 impl<'de, Eip4844> serde::Deserialize<'de> for MaybeTaggedTxEnvelope<Eip4844>
934 where
935 Eip4844: Clone + RlpEcdsaEncodableTx + serde::Serialize + serde::de::DeserializeOwned,
936 {
937 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
938 where
939 D: serde::Deserializer<'de>,
940 {
941 let content = serde::__private::de::Content::deserialize(deserializer)?;
942 let deserializer =
943 serde::__private::de::ContentRefDeserializer::<D::Error>::new(&content);
944
945 let tagged_res =
946 TaggedTxEnvelope::deserialize(deserializer).map(MaybeTaggedTxEnvelope::Tagged);
947
948 if tagged_res.is_ok() {
949 return tagged_res;
951 }
952
953 if let Ok(val) =
955 UntaggedLegacy::deserialize(deserializer).map(MaybeTaggedTxEnvelope::Untagged)
956 {
957 return Ok(val);
958 }
959
960 tagged_res
963 }
964 }
965
966 #[derive(Debug, serde::Serialize, serde::Deserialize)]
967 #[serde(
968 tag = "type",
969 bound = "Eip4844: Clone + RlpEcdsaEncodableTx + serde::Serialize + serde::de::DeserializeOwned"
970 )]
971 pub(crate) enum TaggedTxEnvelope<Eip4844> {
972 #[serde(rename = "0x0", alias = "0x00", with = "crate::transaction::signed_legacy_serde")]
973 Legacy(Signed<TxLegacy>),
974 #[serde(rename = "0x1", alias = "0x01")]
975 Eip2930(Signed<TxEip2930>),
976 #[serde(rename = "0x2", alias = "0x02")]
977 Eip1559(Signed<TxEip1559>),
978 #[serde(rename = "0x3", alias = "0x03")]
979 Eip4844(Signed<Eip4844>),
980 #[serde(rename = "0x4", alias = "0x04")]
981 Eip7702(Signed<TxEip7702>),
982 }
983
984 impl<Eip4844> From<MaybeTaggedTxEnvelope<Eip4844>> for EthereumTxEnvelope<Eip4844> {
985 fn from(value: MaybeTaggedTxEnvelope<Eip4844>) -> Self {
986 match value {
987 MaybeTaggedTxEnvelope::Tagged(tagged) => tagged.into(),
988 MaybeTaggedTxEnvelope::Untagged(UntaggedLegacy { tx, .. }) => Self::Legacy(tx),
989 }
990 }
991 }
992
993 impl<Eip4844> From<TaggedTxEnvelope<Eip4844>> for EthereumTxEnvelope<Eip4844> {
994 fn from(value: TaggedTxEnvelope<Eip4844>) -> Self {
995 match value {
996 TaggedTxEnvelope::Legacy(signed) => Self::Legacy(signed),
997 TaggedTxEnvelope::Eip2930(signed) => Self::Eip2930(signed),
998 TaggedTxEnvelope::Eip1559(signed) => Self::Eip1559(signed),
999 TaggedTxEnvelope::Eip4844(signed) => Self::Eip4844(signed),
1000 TaggedTxEnvelope::Eip7702(signed) => Self::Eip7702(signed),
1001 }
1002 }
1003 }
1004
1005 impl<Eip4844> From<EthereumTxEnvelope<Eip4844>> for TaggedTxEnvelope<Eip4844> {
1006 fn from(value: EthereumTxEnvelope<Eip4844>) -> Self {
1007 match value {
1008 EthereumTxEnvelope::Legacy(signed) => Self::Legacy(signed),
1009 EthereumTxEnvelope::Eip2930(signed) => Self::Eip2930(signed),
1010 EthereumTxEnvelope::Eip1559(signed) => Self::Eip1559(signed),
1011 EthereumTxEnvelope::Eip4844(signed) => Self::Eip4844(signed),
1012 EthereumTxEnvelope::Eip7702(signed) => Self::Eip7702(signed),
1013 }
1014 }
1015 }
1016
1017 #[test]
1019 fn serde_block_tx() {
1020 let rpc_tx = r#"{
1021 "blockHash": "0xc0c3190292a82c2ee148774e37e5665f6a205f5ef0cd0885e84701d90ebd442e",
1022 "blockNumber": "0x6edcde",
1023 "transactionIndex": "0x7",
1024 "hash": "0x2cb125e083d6d2631e3752bd2b3d757bf31bf02bfe21de0ffa46fbb118d28b19",
1025 "from": "0x03e5badf3bb1ade1a8f33f94536c827b6531948d",
1026 "to": "0x3267e72dc8780a1512fa69da7759ec66f30350e3",
1027 "input": "0x62e4c545000000000000000000000000464c8ec100f2f42fb4e42e07e203da2324f9fc6700000000000000000000000003e5badf3bb1ade1a8f33f94536c827b6531948d000000000000000000000000a064bfb5c7e81426647dc20a0d854da1538559dc00000000000000000000000000000000000000000000000000c6f3b40b6c0000",
1028 "nonce": "0x2a8",
1029 "value": "0x0",
1030 "gas": "0x28afd",
1031 "gasPrice": "0x23ec5dbc2",
1032 "accessList": [],
1033 "chainId": "0xaa36a7",
1034 "type": "0x0",
1035 "v": "0x1546d71",
1036 "r": "0x809b9f0a1777e376cd1ee5d2f551035643755edf26ea65b7a00c822a24504962",
1037 "s": "0x6a57bb8e21fe85c7e092868ee976fef71edca974d8c452fcf303f9180c764f64"
1038 }"#;
1039
1040 let _ = serde_json::from_str::<MaybeTaggedTxEnvelope<crate::TxEip4844>>(rpc_tx).unwrap();
1041 }
1042
1043 #[test]
1045 fn serde_block_tx_legacy_chain_id() {
1046 let rpc_tx = r#"{
1047 "blockHash": "0xc0c3190292a82c2ee148774e37e5665f6a205f5ef0cd0885e84701d90ebd442e",
1048 "blockNumber": "0x6edcde",
1049 "transactionIndex": "0x8",
1050 "hash": "0xe5b458ba9de30b47cb7c0ea836bec7b072053123a7416c5082c97f959a4eebd6",
1051 "from": "0x8b87f0a788cc14b4f0f374da59920f5017ff05de",
1052 "to": "0xcb33aa5b38d79e3d9fa8b10aff38aa201399a7e3",
1053 "input": "0xaf7b421018842e4628f3d9ee0e2c7679e29ed5dbaa75be75efecd392943503c9c68adce80000000000000000000000000000000000000000000000000000000000000064",
1054 "nonce": "0x2",
1055 "value": "0x0",
1056 "gas": "0x2dc6c0",
1057 "gasPrice": "0x18ef61d0a",
1058 "accessList": [],
1059 "chainId": "0xaa36a7",
1060 "type": "0x0",
1061 "v": "0x1c",
1062 "r": "0x5e28679806caa50d25e9cb16aef8c0c08b235241b8f6e9d86faadf70421ba664",
1063 "s": "0x2353bba82ef2c7ce4dd6695942399163160000272b14f9aa6cbadf011b76efa4"
1064 }"#;
1065
1066 let _ = serde_json::from_str::<TaggedTxEnvelope<crate::TxEip4844>>(rpc_tx).unwrap();
1067 }
1068}
1069
1070#[cfg(all(feature = "serde", feature = "serde-bincode-compat"))]
1072pub mod serde_bincode_compat {
1073 use crate::{EthereumTypedTransaction, Signed};
1074 use alloc::borrow::Cow;
1075 use alloy_primitives::Signature;
1076 use serde::{Deserialize, Deserializer, Serialize, Serializer};
1077 use serde_with::{DeserializeAs, SerializeAs};
1078
1079 #[derive(Debug, Serialize, Deserialize)]
1095 pub struct EthereumTxEnvelope<'a, Eip4844: Clone = crate::transaction::TxEip4844> {
1096 signature: Signature,
1098 transaction:
1100 crate::serde_bincode_compat::transaction::EthereumTypedTransaction<'a, Eip4844>,
1101 }
1102
1103 impl<'a, T: Clone> From<&'a super::EthereumTxEnvelope<T>> for EthereumTxEnvelope<'a, T> {
1104 fn from(value: &'a super::EthereumTxEnvelope<T>) -> Self {
1105 match value {
1106 super::EthereumTxEnvelope::Legacy(tx) => Self {
1107 signature: *tx.signature(),
1108 transaction:
1109 crate::serde_bincode_compat::transaction::EthereumTypedTransaction::Legacy(
1110 tx.tx().into(),
1111 ),
1112 },
1113 super::EthereumTxEnvelope::Eip2930(tx) => Self {
1114 signature: *tx.signature(),
1115 transaction:
1116 crate::serde_bincode_compat::transaction::EthereumTypedTransaction::Eip2930(
1117 tx.tx().into(),
1118 ),
1119 },
1120 super::EthereumTxEnvelope::Eip1559(tx) => Self {
1121 signature: *tx.signature(),
1122 transaction:
1123 crate::serde_bincode_compat::transaction::EthereumTypedTransaction::Eip1559(
1124 tx.tx().into(),
1125 ),
1126 },
1127 super::EthereumTxEnvelope::Eip4844(tx) => Self {
1128 signature: *tx.signature(),
1129 transaction:
1130 crate::serde_bincode_compat::transaction::EthereumTypedTransaction::Eip4844(
1131 Cow::Borrowed(tx.tx()),
1132 ),
1133 },
1134 super::EthereumTxEnvelope::Eip7702(tx) => Self {
1135 signature: *tx.signature(),
1136 transaction:
1137 crate::serde_bincode_compat::transaction::EthereumTypedTransaction::Eip7702(
1138 tx.tx().into(),
1139 ),
1140 },
1141 }
1142 }
1143 }
1144
1145 impl<'a, T: Clone> From<EthereumTxEnvelope<'a, T>> for super::EthereumTxEnvelope<T> {
1146 fn from(value: EthereumTxEnvelope<'a, T>) -> Self {
1147 let EthereumTxEnvelope { signature, transaction } = value;
1148 let transaction: crate::transaction::typed::EthereumTypedTransaction<T> =
1149 transaction.into();
1150 match transaction {
1151 EthereumTypedTransaction::Legacy(tx) => Signed::new_unhashed(tx, signature).into(),
1152 EthereumTypedTransaction::Eip2930(tx) => Signed::new_unhashed(tx, signature).into(),
1153 EthereumTypedTransaction::Eip1559(tx) => Signed::new_unhashed(tx, signature).into(),
1154 EthereumTypedTransaction::Eip4844(tx) => {
1155 Self::Eip4844(Signed::new_unhashed(tx, signature))
1156 }
1157 EthereumTypedTransaction::Eip7702(tx) => Signed::new_unhashed(tx, signature).into(),
1158 }
1159 }
1160 }
1161
1162 impl<T: Serialize + Clone> SerializeAs<super::EthereumTxEnvelope<T>> for EthereumTxEnvelope<'_, T> {
1163 fn serialize_as<S>(
1164 source: &super::EthereumTxEnvelope<T>,
1165 serializer: S,
1166 ) -> Result<S::Ok, S::Error>
1167 where
1168 S: Serializer,
1169 {
1170 EthereumTxEnvelope::<'_, T>::from(source).serialize(serializer)
1171 }
1172 }
1173
1174 impl<'de, T: Deserialize<'de> + Clone> DeserializeAs<'de, super::EthereumTxEnvelope<T>>
1175 for EthereumTxEnvelope<'de, T>
1176 {
1177 fn deserialize_as<D>(deserializer: D) -> Result<super::EthereumTxEnvelope<T>, D::Error>
1178 where
1179 D: Deserializer<'de>,
1180 {
1181 EthereumTxEnvelope::<'_, T>::deserialize(deserializer).map(Into::into)
1182 }
1183 }
1184
1185 #[cfg(test)]
1186 mod tests {
1187 use super::super::{serde_bincode_compat, EthereumTxEnvelope};
1188 use crate::TxEip4844;
1189 use arbitrary::Arbitrary;
1190 use bincode::config;
1191 use rand::Rng;
1192 use serde::{Deserialize, Serialize};
1193 use serde_with::serde_as;
1194
1195 #[test]
1196 fn test_typed_tx_envelope_bincode_roundtrip() {
1197 #[serde_as]
1198 #[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
1199 struct Data {
1200 #[serde_as(as = "serde_bincode_compat::EthereumTxEnvelope<'_>")]
1201 transaction: EthereumTxEnvelope<TxEip4844>,
1202 }
1203
1204 let mut bytes = [0u8; 1024];
1205 rand::thread_rng().fill(bytes.as_mut_slice());
1206 let data = Data {
1207 transaction: EthereumTxEnvelope::arbitrary(&mut arbitrary::Unstructured::new(
1208 &bytes,
1209 ))
1210 .unwrap(),
1211 };
1212
1213 let encoded = bincode::serde::encode_to_vec(&data, config::legacy()).unwrap();
1214 let (decoded, _) =
1215 bincode::serde::decode_from_slice::<Data, _>(&encoded, config::legacy()).unwrap();
1216 assert_eq!(decoded, data);
1217 }
1218 }
1219}
1220
1221#[cfg(test)]
1222mod tests {
1223 use super::*;
1224 use crate::{transaction::SignableTransaction, TxEip4844, TxEip4844WithSidecar};
1225 use alloc::vec::Vec;
1226 use alloy_eips::{
1227 eip2930::{AccessList, AccessListItem},
1228 eip4844::BlobTransactionSidecar,
1229 eip7702::Authorization,
1230 };
1231 #[allow(unused_imports)]
1232 use alloy_primitives::{b256, Bytes, TxKind};
1233 use alloy_primitives::{hex, Address, Signature, U256};
1234 use std::{fs, path::PathBuf, str::FromStr, vec};
1235
1236 #[test]
1237 fn check_u8_id() {
1238 assert_eq!(TxType::Legacy, TxType::Legacy as u8);
1239 assert_eq!(TxType::Eip2930, TxType::Eip2930 as u8);
1240 assert_eq!(TxType::Eip1559, TxType::Eip1559 as u8);
1241 assert_eq!(TxType::Eip7702, TxType::Eip7702 as u8);
1242 assert_eq!(TxType::Eip4844, TxType::Eip4844 as u8);
1243 }
1244
1245 #[test]
1246 #[cfg(feature = "k256")]
1247 fn test_decode_live_1559_tx() {
1249 use alloy_primitives::address;
1250
1251 let raw_tx = alloy_primitives::hex::decode("02f86f0102843b9aca0085029e7822d68298f094d9e1459a7a482635700cbc20bbaf52d495ab9c9680841b55ba3ac080a0c199674fcb29f353693dd779c017823b954b3c69dffa3cd6b2a6ff7888798039a028ca912de909e7e6cdef9cdcaf24c54dd8c1032946dfa1d85c206b32a9064fe8").unwrap();
1252 let res = TxEnvelope::decode(&mut raw_tx.as_slice()).unwrap();
1253
1254 assert_eq!(res.tx_type(), TxType::Eip1559);
1255
1256 let tx = match res {
1257 TxEnvelope::Eip1559(tx) => tx,
1258 _ => unreachable!(),
1259 };
1260
1261 assert_eq!(tx.tx().to, TxKind::Call(address!("D9e1459A7A482635700cBc20BBAF52D495Ab9C96")));
1262 let from = tx.recover_signer().unwrap();
1263 assert_eq!(from, address!("001e2b7dE757bA469a57bF6b23d982458a07eFcE"));
1264 }
1265
1266 #[test]
1267 fn test_is_replay_protected_v() {
1268 let sig = Signature::test_signature();
1269 assert!(!&TxEnvelope::Legacy(Signed::new_unchecked(
1270 TxLegacy::default(),
1271 sig,
1272 Default::default(),
1273 ))
1274 .is_replay_protected());
1275 let r = b256!("840cfc572845f5786e702984c2a582528cad4b49b2a10b9db1be7fca90058565");
1276 let s = b256!("25e7109ceb98168d95b09b18bbf6b685130e0562f233877d492b94eee0c5b6d1");
1277 let v = false;
1278 let valid_sig = Signature::from_scalars_and_parity(r, s, v);
1279 assert!(!&TxEnvelope::Legacy(Signed::new_unchecked(
1280 TxLegacy::default(),
1281 valid_sig,
1282 Default::default(),
1283 ))
1284 .is_replay_protected());
1285 assert!(&TxEnvelope::Eip2930(Signed::new_unchecked(
1286 TxEip2930::default(),
1287 sig,
1288 Default::default(),
1289 ))
1290 .is_replay_protected());
1291 assert!(&TxEnvelope::Eip1559(Signed::new_unchecked(
1292 TxEip1559::default(),
1293 sig,
1294 Default::default(),
1295 ))
1296 .is_replay_protected());
1297 assert!(&TxEnvelope::Eip4844(Signed::new_unchecked(
1298 TxEip4844Variant::TxEip4844(TxEip4844::default()),
1299 sig,
1300 Default::default(),
1301 ))
1302 .is_replay_protected());
1303 assert!(&TxEnvelope::Eip7702(Signed::new_unchecked(
1304 TxEip7702::default(),
1305 sig,
1306 Default::default(),
1307 ))
1308 .is_replay_protected());
1309 }
1310
1311 #[test]
1312 #[cfg(feature = "k256")]
1313 fn test_decode_live_legacy_tx() {
1315 use alloy_primitives::address;
1316
1317 let raw_tx = alloy_primitives::bytes!("f9015482078b8505d21dba0083022ef1947a250d5630b4cf539739df2c5dacb4c659f2488d880c46549a521b13d8b8e47ff36ab50000000000000000000000000000000000000000000066ab5a608bd00a23f2fe000000000000000000000000000000000000000000000000000000000000008000000000000000000000000048c04ed5691981c42154c6167398f95e8f38a7ff00000000000000000000000000000000000000000000000000000000632ceac70000000000000000000000000000000000000000000000000000000000000002000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000006c6ee5e31d828de241282b9606c8e98ea48526e225a0c9077369501641a92ef7399ff81c21639ed4fd8fc69cb793cfa1dbfab342e10aa0615facb2f1bcf3274a354cfe384a38d0cc008a11c2dd23a69111bc6930ba27a8");
1318 let res = TxEnvelope::decode_2718(&mut raw_tx.as_ref()).unwrap();
1319 assert_eq!(res.tx_type(), TxType::Legacy);
1320
1321 let tx = match res {
1322 TxEnvelope::Legacy(tx) => tx,
1323 _ => unreachable!(),
1324 };
1325
1326 assert_eq!(tx.tx().chain_id(), Some(1));
1327
1328 assert_eq!(tx.tx().to, TxKind::Call(address!("7a250d5630B4cF539739dF2C5dAcb4c659F2488D")));
1329 assert_eq!(
1330 tx.hash().to_string(),
1331 "0x280cde7cdefe4b188750e76c888f13bd05ce9a4d7767730feefe8a0e50ca6fc4"
1332 );
1333 let from = tx.recover_signer().unwrap();
1334 assert_eq!(from, address!("a12e1462d0ceD572f396F58B6E2D03894cD7C8a4"));
1335 }
1336
1337 #[test]
1338 #[cfg(feature = "k256")]
1339 fn test_decode_live_4844_tx() {
1342 use crate::Transaction;
1343 use alloy_primitives::{address, b256};
1344
1345 let raw_tx = alloy_primitives::hex::decode("0x03f9011d83aa36a7820fa28477359400852e90edd0008252089411e9ca82a3a762b4b5bd264d4173a242e7a770648080c08504a817c800f8a5a0012ec3d6f66766bedb002a190126b3549fce0047de0d4c25cffce0dc1c57921aa00152d8e24762ff22b1cfd9f8c0683786a7ca63ba49973818b3d1e9512cd2cec4a0013b98c6c83e066d5b14af2b85199e3d4fc7d1e778dd53130d180f5077e2d1c7a001148b495d6e859114e670ca54fb6e2657f0cbae5b08063605093a4b3dc9f8f1a0011ac212f13c5dff2b2c6b600a79635103d6f580a4221079951181b25c7e654901a0c8de4cced43169f9aa3d36506363b2d2c44f6c49fc1fd91ea114c86f3757077ea01e11fdd0d1934eda0492606ee0bb80a7bf8f35cc5f86ec60fe5031ba48bfd544").unwrap();
1347
1348 let res = TxEnvelope::decode_2718(&mut raw_tx.as_slice()).unwrap();
1349 assert_eq!(res.tx_type(), TxType::Eip4844);
1350
1351 let tx = match res {
1352 TxEnvelope::Eip4844(tx) => tx,
1353 _ => unreachable!(),
1354 };
1355
1356 assert_eq!(
1357 tx.tx().kind(),
1358 TxKind::Call(address!("11E9CA82A3a762b4B5bd264d4173a242e7a77064"))
1359 );
1360
1361 assert!(matches!(tx.tx(), TxEip4844Variant::TxEip4844(_)));
1363
1364 assert_eq!(
1365 tx.tx().tx().blob_versioned_hashes,
1366 vec![
1367 b256!("012ec3d6f66766bedb002a190126b3549fce0047de0d4c25cffce0dc1c57921a"),
1368 b256!("0152d8e24762ff22b1cfd9f8c0683786a7ca63ba49973818b3d1e9512cd2cec4"),
1369 b256!("013b98c6c83e066d5b14af2b85199e3d4fc7d1e778dd53130d180f5077e2d1c7"),
1370 b256!("01148b495d6e859114e670ca54fb6e2657f0cbae5b08063605093a4b3dc9f8f1"),
1371 b256!("011ac212f13c5dff2b2c6b600a79635103d6f580a4221079951181b25c7e6549")
1372 ]
1373 );
1374
1375 let from = tx.recover_signer().unwrap();
1376 assert_eq!(from, address!("A83C816D4f9b2783761a22BA6FADB0eB0606D7B2"));
1377 }
1378
1379 fn test_encode_decode_roundtrip<T: SignableTransaction<Signature>>(
1380 tx: T,
1381 signature: Option<Signature>,
1382 ) where
1383 Signed<T>: Into<TxEnvelope>,
1384 {
1385 let signature = signature.unwrap_or_else(Signature::test_signature);
1386 let tx_signed = tx.into_signed(signature);
1387 let tx_envelope: TxEnvelope = tx_signed.into();
1388 let encoded = tx_envelope.encoded_2718();
1389 let mut slice = encoded.as_slice();
1390 let decoded = TxEnvelope::decode_2718(&mut slice).unwrap();
1391 assert_eq!(encoded.len(), tx_envelope.encode_2718_len());
1392 assert_eq!(decoded, tx_envelope);
1393 assert_eq!(slice.len(), 0);
1394 }
1395
1396 #[test]
1397 fn test_encode_decode_legacy() {
1398 let tx = TxLegacy {
1399 chain_id: None,
1400 nonce: 2,
1401 gas_limit: 1000000,
1402 gas_price: 10000000000,
1403 to: Address::left_padding_from(&[6]).into(),
1404 value: U256::from(7_u64),
1405 ..Default::default()
1406 };
1407 test_encode_decode_roundtrip(tx, Some(Signature::test_signature().with_parity(true)));
1408 }
1409
1410 #[test]
1411 fn test_encode_decode_eip1559() {
1412 let tx = TxEip1559 {
1413 chain_id: 1u64,
1414 nonce: 2,
1415 max_fee_per_gas: 3,
1416 max_priority_fee_per_gas: 4,
1417 gas_limit: 5,
1418 to: Address::left_padding_from(&[6]).into(),
1419 value: U256::from(7_u64),
1420 input: vec![8].into(),
1421 access_list: Default::default(),
1422 };
1423 test_encode_decode_roundtrip(tx, None);
1424 }
1425
1426 #[test]
1427 fn test_encode_decode_eip1559_parity_eip155() {
1428 let tx = TxEip1559 {
1429 chain_id: 1u64,
1430 nonce: 2,
1431 max_fee_per_gas: 3,
1432 max_priority_fee_per_gas: 4,
1433 gas_limit: 5,
1434 to: Address::left_padding_from(&[6]).into(),
1435 value: U256::from(7_u64),
1436 input: vec![8].into(),
1437 access_list: Default::default(),
1438 };
1439 let signature = Signature::test_signature().with_parity(true);
1440
1441 test_encode_decode_roundtrip(tx, Some(signature));
1442 }
1443
1444 #[test]
1445 fn test_encode_decode_eip2930_parity_eip155() {
1446 let tx = TxEip2930 {
1447 chain_id: 1u64,
1448 nonce: 2,
1449 gas_price: 3,
1450 gas_limit: 4,
1451 to: Address::left_padding_from(&[5]).into(),
1452 value: U256::from(6_u64),
1453 input: vec![7].into(),
1454 access_list: Default::default(),
1455 };
1456 let signature = Signature::test_signature().with_parity(true);
1457 test_encode_decode_roundtrip(tx, Some(signature));
1458 }
1459
1460 #[test]
1461 fn test_encode_decode_eip4844_parity_eip155() {
1462 let tx = TxEip4844 {
1463 chain_id: 1,
1464 nonce: 100,
1465 max_fee_per_gas: 50_000_000_000,
1466 max_priority_fee_per_gas: 1_000_000_000_000,
1467 gas_limit: 1_000_000,
1468 to: Address::random(),
1469 value: U256::from(10e18),
1470 input: Bytes::new(),
1471 access_list: AccessList(vec![AccessListItem {
1472 address: Address::random(),
1473 storage_keys: vec![B256::random()],
1474 }]),
1475 blob_versioned_hashes: vec![B256::random()],
1476 max_fee_per_blob_gas: 0,
1477 };
1478 let signature = Signature::test_signature().with_parity(true);
1479 test_encode_decode_roundtrip(tx, Some(signature));
1480 }
1481
1482 #[test]
1483 fn test_encode_decode_eip4844_sidecar_parity_eip155() {
1484 let tx = TxEip4844 {
1485 chain_id: 1,
1486 nonce: 100,
1487 max_fee_per_gas: 50_000_000_000,
1488 max_priority_fee_per_gas: 1_000_000_000_000,
1489 gas_limit: 1_000_000,
1490 to: Address::random(),
1491 value: U256::from(10e18),
1492 input: Bytes::new(),
1493 access_list: AccessList(vec![AccessListItem {
1494 address: Address::random(),
1495 storage_keys: vec![B256::random()],
1496 }]),
1497 blob_versioned_hashes: vec![B256::random()],
1498 max_fee_per_blob_gas: 0,
1499 };
1500 let sidecar = BlobTransactionSidecar {
1501 blobs: vec![[2; 131072].into()],
1502 commitments: vec![[3; 48].into()],
1503 proofs: vec![[4; 48].into()],
1504 };
1505 let tx = TxEip4844WithSidecar { tx, sidecar };
1506 let signature = Signature::test_signature().with_parity(true);
1507
1508 let tx_signed = tx.into_signed(signature);
1509 let tx_envelope: TxEnvelope = tx_signed.into();
1510
1511 let mut out = Vec::new();
1512 tx_envelope.network_encode(&mut out);
1513 let mut slice = out.as_slice();
1514 let decoded = TxEnvelope::network_decode(&mut slice).unwrap();
1515 assert_eq!(slice.len(), 0);
1516 assert_eq!(out.len(), tx_envelope.network_len());
1517 assert_eq!(decoded, tx_envelope);
1518 }
1519
1520 #[test]
1521 fn test_encode_decode_eip4844_variant_parity_eip155() {
1522 let tx = TxEip4844 {
1523 chain_id: 1,
1524 nonce: 100,
1525 max_fee_per_gas: 50_000_000_000,
1526 max_priority_fee_per_gas: 1_000_000_000_000,
1527 gas_limit: 1_000_000,
1528 to: Address::random(),
1529 value: U256::from(10e18),
1530 input: Bytes::new(),
1531 access_list: AccessList(vec![AccessListItem {
1532 address: Address::random(),
1533 storage_keys: vec![B256::random()],
1534 }]),
1535 blob_versioned_hashes: vec![B256::random()],
1536 max_fee_per_blob_gas: 0,
1537 };
1538 let tx = TxEip4844Variant::TxEip4844(tx);
1539 let signature = Signature::test_signature().with_parity(true);
1540 test_encode_decode_roundtrip(tx, Some(signature));
1541 }
1542
1543 #[test]
1544 fn test_encode_decode_eip2930() {
1545 let tx = TxEip2930 {
1546 chain_id: 1u64,
1547 nonce: 2,
1548 gas_price: 3,
1549 gas_limit: 4,
1550 to: Address::left_padding_from(&[5]).into(),
1551 value: U256::from(6_u64),
1552 input: vec![7].into(),
1553 access_list: AccessList(vec![AccessListItem {
1554 address: Address::left_padding_from(&[8]),
1555 storage_keys: vec![B256::left_padding_from(&[9])],
1556 }]),
1557 };
1558 test_encode_decode_roundtrip(tx, None);
1559 }
1560
1561 #[test]
1562 fn test_encode_decode_eip7702() {
1563 let tx = TxEip7702 {
1564 chain_id: 1u64,
1565 nonce: 2,
1566 gas_limit: 3,
1567 max_fee_per_gas: 4,
1568 max_priority_fee_per_gas: 5,
1569 to: Address::left_padding_from(&[5]),
1570 value: U256::from(6_u64),
1571 input: vec![7].into(),
1572 access_list: AccessList(vec![AccessListItem {
1573 address: Address::left_padding_from(&[8]),
1574 storage_keys: vec![B256::left_padding_from(&[9])],
1575 }]),
1576 authorization_list: vec![(Authorization {
1577 chain_id: U256::from(1),
1578 address: Address::left_padding_from(&[10]),
1579 nonce: 1u64,
1580 })
1581 .into_signed(Signature::from_str("48b55bfa915ac795c431978d8a6a992b628d557da5ff759b307d495a36649353efffd310ac743f371de3b9f7f9cb56c0b28ad43601b4ab949f53faa07bd2c8041b").unwrap())],
1582 };
1583 test_encode_decode_roundtrip(tx, None);
1584 }
1585
1586 #[test]
1587 fn test_encode_decode_transaction_list() {
1588 let signature = Signature::test_signature();
1589 let tx = TxEnvelope::Eip1559(
1590 TxEip1559 {
1591 chain_id: 1u64,
1592 nonce: 2,
1593 max_fee_per_gas: 3,
1594 max_priority_fee_per_gas: 4,
1595 gas_limit: 5,
1596 to: Address::left_padding_from(&[6]).into(),
1597 value: U256::from(7_u64),
1598 input: vec![8].into(),
1599 access_list: Default::default(),
1600 }
1601 .into_signed(signature),
1602 );
1603 let transactions = vec![tx.clone(), tx];
1604 let encoded = alloy_rlp::encode(&transactions);
1605 let decoded = Vec::<TxEnvelope>::decode(&mut &encoded[..]).unwrap();
1606 assert_eq!(transactions, decoded);
1607 }
1608
1609 #[test]
1610 fn decode_encode_known_rpc_transaction() {
1611 let network_data_path =
1613 PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("testdata/rpc_blob_transaction.rlp");
1614 let data = fs::read_to_string(network_data_path).expect("Unable to read file");
1615 let hex_data = hex::decode(data.trim()).unwrap();
1616
1617 let tx: TxEnvelope = TxEnvelope::decode_2718(&mut hex_data.as_slice()).unwrap();
1618 let encoded = tx.encoded_2718();
1619 assert_eq!(encoded, hex_data);
1620 assert_eq!(tx.encode_2718_len(), hex_data.len());
1621 }
1622
1623 #[cfg(feature = "serde")]
1624 fn test_serde_roundtrip<T: SignableTransaction<Signature>>(tx: T)
1625 where
1626 Signed<T>: Into<TxEnvelope>,
1627 {
1628 let signature = Signature::test_signature();
1629 let tx_envelope: TxEnvelope = tx.into_signed(signature).into();
1630
1631 let serialized = serde_json::to_string(&tx_envelope).unwrap();
1632
1633 let deserialized: TxEnvelope = serde_json::from_str(&serialized).unwrap();
1634
1635 assert_eq!(tx_envelope, deserialized);
1636 }
1637
1638 #[test]
1639 #[cfg(feature = "serde")]
1640 fn test_serde_roundtrip_legacy() {
1641 let tx = TxLegacy {
1642 chain_id: Some(1),
1643 nonce: 100,
1644 gas_price: 3_000_000_000,
1645 gas_limit: 50_000,
1646 to: Address::default().into(),
1647 value: U256::from(10e18),
1648 input: Bytes::new(),
1649 };
1650 test_serde_roundtrip(tx);
1651 }
1652
1653 #[test]
1654 #[cfg(feature = "serde")]
1655 fn test_serde_roundtrip_eip1559() {
1656 let tx = TxEip1559 {
1657 chain_id: 1,
1658 nonce: 100,
1659 max_fee_per_gas: 50_000_000_000,
1660 max_priority_fee_per_gas: 1_000_000_000_000,
1661 gas_limit: 1_000_000,
1662 to: TxKind::Create,
1663 value: U256::from(10e18),
1664 input: Bytes::new(),
1665 access_list: AccessList(vec![AccessListItem {
1666 address: Address::random(),
1667 storage_keys: vec![B256::random()],
1668 }]),
1669 };
1670 test_serde_roundtrip(tx);
1671 }
1672
1673 #[test]
1674 #[cfg(feature = "serde")]
1675 fn test_serde_roundtrip_eip2930() {
1676 let tx = TxEip2930 {
1677 chain_id: u64::MAX,
1678 nonce: u64::MAX,
1679 gas_price: u128::MAX,
1680 gas_limit: u64::MAX,
1681 to: Address::random().into(),
1682 value: U256::MAX,
1683 input: Bytes::new(),
1684 access_list: Default::default(),
1685 };
1686 test_serde_roundtrip(tx);
1687 }
1688
1689 #[test]
1690 #[cfg(feature = "serde")]
1691 fn test_serde_roundtrip_eip4844() {
1692 let tx = TxEip4844Variant::TxEip4844(TxEip4844 {
1693 chain_id: 1,
1694 nonce: 100,
1695 max_fee_per_gas: 50_000_000_000,
1696 max_priority_fee_per_gas: 1_000_000_000_000,
1697 gas_limit: 1_000_000,
1698 to: Address::random(),
1699 value: U256::from(10e18),
1700 input: Bytes::new(),
1701 access_list: AccessList(vec![AccessListItem {
1702 address: Address::random(),
1703 storage_keys: vec![B256::random()],
1704 }]),
1705 blob_versioned_hashes: vec![B256::random()],
1706 max_fee_per_blob_gas: 0,
1707 });
1708 test_serde_roundtrip(tx);
1709
1710 let tx = TxEip4844Variant::TxEip4844WithSidecar(TxEip4844WithSidecar {
1711 tx: TxEip4844 {
1712 chain_id: 1,
1713 nonce: 100,
1714 max_fee_per_gas: 50_000_000_000,
1715 max_priority_fee_per_gas: 1_000_000_000_000,
1716 gas_limit: 1_000_000,
1717 to: Address::random(),
1718 value: U256::from(10e18),
1719 input: Bytes::new(),
1720 access_list: AccessList(vec![AccessListItem {
1721 address: Address::random(),
1722 storage_keys: vec![B256::random()],
1723 }]),
1724 blob_versioned_hashes: vec![B256::random()],
1725 max_fee_per_blob_gas: 0,
1726 },
1727 sidecar: Default::default(),
1728 });
1729 test_serde_roundtrip(tx);
1730 }
1731
1732 #[test]
1733 #[cfg(feature = "serde")]
1734 fn test_serde_roundtrip_eip7702() {
1735 let tx = TxEip7702 {
1736 chain_id: u64::MAX,
1737 nonce: u64::MAX,
1738 gas_limit: u64::MAX,
1739 max_fee_per_gas: u128::MAX,
1740 max_priority_fee_per_gas: u128::MAX,
1741 to: Address::random(),
1742 value: U256::MAX,
1743 input: Bytes::new(),
1744 access_list: AccessList(vec![AccessListItem {
1745 address: Address::random(),
1746 storage_keys: vec![B256::random()],
1747 }]),
1748 authorization_list: vec![(Authorization {
1749 chain_id: U256::from(1),
1750 address: Address::left_padding_from(&[1]),
1751 nonce: 1u64,
1752 })
1753 .into_signed(Signature::from_str("48b55bfa915ac795c431978d8a6a992b628d557da5ff759b307d495a36649353efffd310ac743f371de3b9f7f9cb56c0b28ad43601b4ab949f53faa07bd2c8041b").unwrap())],
1754 };
1755 test_serde_roundtrip(tx);
1756 }
1757
1758 #[test]
1759 #[cfg(feature = "serde")]
1760 fn serde_tx_from_contract_call() {
1761 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"}"#;
1762
1763 let te = serde_json::from_str::<TxEnvelope>(rpc_tx).unwrap();
1764
1765 assert_eq!(
1766 *te.tx_hash(),
1767 alloy_primitives::b256!(
1768 "018b2331d461a4aeedf6a1f9cc37463377578244e6a35216057a8370714e798f"
1769 )
1770 );
1771 }
1772
1773 #[test]
1774 #[cfg(feature = "k256")]
1775 fn test_arbitrary_envelope() {
1776 use arbitrary::Arbitrary;
1777 let mut unstructured = arbitrary::Unstructured::new(b"arbitrary tx envelope");
1778 let tx = TxEnvelope::arbitrary(&mut unstructured).unwrap();
1779
1780 assert!(tx.recover_signer().is_ok());
1781 }
1782
1783 #[test]
1784 #[cfg(feature = "serde")]
1785 fn test_serde_untagged_legacy() {
1786 let data = r#"{
1787 "hash": "0x97efb58d2b42df8d68ab5899ff42b16c7e0af35ed86ae4adb8acaad7e444220c",
1788 "input": "0x",
1789 "r": "0x5d71a4a548503f2916d10c6b1a1557a0e7352eb041acb2bac99d1ad6bb49fd45",
1790 "s": "0x2627bf6d35be48b0e56c61733f63944c0ebcaa85cb4ed6bc7cba3161ba85e0e8",
1791 "v": "0x1c",
1792 "gas": "0x15f90",
1793 "from": "0x2a65aca4d5fc5b5c859090a6c34d164135398226",
1794 "to": "0x8fbeb4488a08d60979b5aa9e13dd00b2726320b2",
1795 "value": "0xf606682badd7800",
1796 "nonce": "0x11f398",
1797 "gasPrice": "0x4a817c800"
1798 }"#;
1799
1800 let tx: TxEnvelope = serde_json::from_str(data).unwrap();
1801
1802 assert!(matches!(tx, TxEnvelope::Legacy(_)));
1803
1804 let data_with_wrong_type = r#"{
1805 "hash": "0x97efb58d2b42df8d68ab5899ff42b16c7e0af35ed86ae4adb8acaad7e444220c",
1806 "input": "0x",
1807 "r": "0x5d71a4a548503f2916d10c6b1a1557a0e7352eb041acb2bac99d1ad6bb49fd45",
1808 "s": "0x2627bf6d35be48b0e56c61733f63944c0ebcaa85cb4ed6bc7cba3161ba85e0e8",
1809 "v": "0x1c",
1810 "gas": "0x15f90",
1811 "from": "0x2a65aca4d5fc5b5c859090a6c34d164135398226",
1812 "to": "0x8fbeb4488a08d60979b5aa9e13dd00b2726320b2",
1813 "value": "0xf606682badd7800",
1814 "nonce": "0x11f398",
1815 "gasPrice": "0x4a817c800",
1816 "type": "0x12"
1817 }"#;
1818
1819 assert!(serde_json::from_str::<TxEnvelope>(data_with_wrong_type).is_err());
1820 }
1821
1822 #[test]
1823 fn test_tx_type_try_from_u8() {
1824 assert_eq!(TxType::try_from(0u8).unwrap(), TxType::Legacy);
1825 assert_eq!(TxType::try_from(1u8).unwrap(), TxType::Eip2930);
1826 assert_eq!(TxType::try_from(2u8).unwrap(), TxType::Eip1559);
1827 assert_eq!(TxType::try_from(3u8).unwrap(), TxType::Eip4844);
1828 assert_eq!(TxType::try_from(4u8).unwrap(), TxType::Eip7702);
1829 assert!(TxType::try_from(5u8).is_err()); }
1831
1832 #[test]
1833 fn test_tx_type_try_from_u64() {
1834 assert_eq!(TxType::try_from(0u64).unwrap(), TxType::Legacy);
1835 assert_eq!(TxType::try_from(1u64).unwrap(), TxType::Eip2930);
1836 assert_eq!(TxType::try_from(2u64).unwrap(), TxType::Eip1559);
1837 assert_eq!(TxType::try_from(3u64).unwrap(), TxType::Eip4844);
1838 assert_eq!(TxType::try_from(4u64).unwrap(), TxType::Eip7702);
1839 assert!(TxType::try_from(10u64).is_err()); }
1841
1842 #[test]
1843 fn test_tx_type_from_conversions() {
1844 let legacy_tx = Signed::new_unchecked(
1845 TxLegacy::default(),
1846 Signature::test_signature(),
1847 Default::default(),
1848 );
1849 let eip2930_tx = Signed::new_unchecked(
1850 TxEip2930::default(),
1851 Signature::test_signature(),
1852 Default::default(),
1853 );
1854 let eip1559_tx = Signed::new_unchecked(
1855 TxEip1559::default(),
1856 Signature::test_signature(),
1857 Default::default(),
1858 );
1859 let eip4844_variant = Signed::new_unchecked(
1860 TxEip4844Variant::TxEip4844(TxEip4844::default()),
1861 Signature::test_signature(),
1862 Default::default(),
1863 );
1864 let eip7702_tx = Signed::new_unchecked(
1865 TxEip7702::default(),
1866 Signature::test_signature(),
1867 Default::default(),
1868 );
1869
1870 assert!(matches!(TxEnvelope::from(legacy_tx), TxEnvelope::Legacy(_)));
1871 assert!(matches!(TxEnvelope::from(eip2930_tx), TxEnvelope::Eip2930(_)));
1872 assert!(matches!(TxEnvelope::from(eip1559_tx), TxEnvelope::Eip1559(_)));
1873 assert!(matches!(TxEnvelope::from(eip4844_variant), TxEnvelope::Eip4844(_)));
1874 assert!(matches!(TxEnvelope::from(eip7702_tx), TxEnvelope::Eip7702(_)));
1875 }
1876
1877 #[test]
1878 fn test_tx_type_is_methods() {
1879 let legacy_tx = TxEnvelope::Legacy(Signed::new_unchecked(
1880 TxLegacy::default(),
1881 Signature::test_signature(),
1882 Default::default(),
1883 ));
1884 let eip2930_tx = TxEnvelope::Eip2930(Signed::new_unchecked(
1885 TxEip2930::default(),
1886 Signature::test_signature(),
1887 Default::default(),
1888 ));
1889 let eip1559_tx = TxEnvelope::Eip1559(Signed::new_unchecked(
1890 TxEip1559::default(),
1891 Signature::test_signature(),
1892 Default::default(),
1893 ));
1894 let eip4844_tx = TxEnvelope::Eip4844(Signed::new_unchecked(
1895 TxEip4844Variant::TxEip4844(TxEip4844::default()),
1896 Signature::test_signature(),
1897 Default::default(),
1898 ));
1899 let eip7702_tx = TxEnvelope::Eip7702(Signed::new_unchecked(
1900 TxEip7702::default(),
1901 Signature::test_signature(),
1902 Default::default(),
1903 ));
1904
1905 assert!(legacy_tx.is_legacy());
1906 assert!(!legacy_tx.is_eip2930());
1907 assert!(!legacy_tx.is_eip1559());
1908 assert!(!legacy_tx.is_eip4844());
1909 assert!(!legacy_tx.is_eip7702());
1910
1911 assert!(eip2930_tx.is_eip2930());
1912 assert!(!eip2930_tx.is_legacy());
1913 assert!(!eip2930_tx.is_eip1559());
1914 assert!(!eip2930_tx.is_eip4844());
1915 assert!(!eip2930_tx.is_eip7702());
1916
1917 assert!(eip1559_tx.is_eip1559());
1918 assert!(!eip1559_tx.is_legacy());
1919 assert!(!eip1559_tx.is_eip2930());
1920 assert!(!eip1559_tx.is_eip4844());
1921 assert!(!eip1559_tx.is_eip7702());
1922
1923 assert!(eip4844_tx.is_eip4844());
1924 assert!(!eip4844_tx.is_legacy());
1925 assert!(!eip4844_tx.is_eip2930());
1926 assert!(!eip4844_tx.is_eip1559());
1927 assert!(!eip4844_tx.is_eip7702());
1928
1929 assert!(eip7702_tx.is_eip7702());
1930 assert!(!eip7702_tx.is_legacy());
1931 assert!(!eip7702_tx.is_eip2930());
1932 assert!(!eip7702_tx.is_eip1559());
1933 assert!(!eip7702_tx.is_eip4844());
1934 }
1935
1936 #[test]
1937 fn test_tx_type() {
1938 let legacy_tx = TxEnvelope::Legacy(Signed::new_unchecked(
1939 TxLegacy::default(),
1940 Signature::test_signature(),
1941 Default::default(),
1942 ));
1943 let eip2930_tx = TxEnvelope::Eip2930(Signed::new_unchecked(
1944 TxEip2930::default(),
1945 Signature::test_signature(),
1946 Default::default(),
1947 ));
1948 let eip1559_tx = TxEnvelope::Eip1559(Signed::new_unchecked(
1949 TxEip1559::default(),
1950 Signature::test_signature(),
1951 Default::default(),
1952 ));
1953 let eip4844_tx = TxEnvelope::Eip4844(Signed::new_unchecked(
1954 TxEip4844Variant::TxEip4844(TxEip4844::default()),
1955 Signature::test_signature(),
1956 Default::default(),
1957 ));
1958 let eip7702_tx = TxEnvelope::Eip7702(Signed::new_unchecked(
1959 TxEip7702::default(),
1960 Signature::test_signature(),
1961 Default::default(),
1962 ));
1963
1964 assert_eq!(legacy_tx.tx_type(), TxType::Legacy);
1965 assert_eq!(eip2930_tx.tx_type(), TxType::Eip2930);
1966 assert_eq!(eip1559_tx.tx_type(), TxType::Eip1559);
1967 assert_eq!(eip4844_tx.tx_type(), TxType::Eip4844);
1968 assert_eq!(eip7702_tx.tx_type(), TxType::Eip7702);
1969 }
1970
1971 #[test]
1973 fn decode_raw_legacy() {
1974 let raw = hex!("f8aa0285018ef61d0a832dc6c094cb33aa5b38d79e3d9fa8b10aff38aa201399a7e380b844af7b421018842e4628f3d9ee0e2c7679e29ed5dbaa75be75efecd392943503c9c68adce800000000000000000000000000000000000000000000000000000000000000641ca05e28679806caa50d25e9cb16aef8c0c08b235241b8f6e9d86faadf70421ba664a02353bba82ef2c7ce4dd6695942399163160000272b14f9aa6cbadf011b76efa4");
1975 let tx = TxEnvelope::decode_2718(&mut raw.as_ref()).unwrap();
1976 assert!(tx.chain_id().is_none());
1977 }
1978}