alloy_consensus/transaction/
tx_type.rs

1//! Contains the Ethereum transaction type identifier.
2
3use alloy_eips::{
4    eip2718::{Eip2718Error, IsTyped2718},
5    Typed2718,
6};
7use alloy_primitives::{U64, U8};
8use alloy_rlp::{Decodable, Encodable};
9use core::fmt;
10
11/// The TxEnvelope enum represents all Ethereum transaction envelope types,
12/// /// Its variants correspond to specific allowed transactions:
13/// 1. Legacy (pre-EIP2718) [`TxLegacy`](crate::TxLegacy)
14/// 2. EIP2930 (state access lists) [`TxEip2930`](crate::TxEip2930)
15/// 3. EIP1559 [`TxEip1559`](crate::TxEip1559)
16/// 4. EIP4844 [`TxEip4844Variant`](crate::TxEip4844Variant)
17///
18/// This type is generic over Eip4844 variant to support the following cases:
19/// 1. Only-[`crate::TxEip4844`] transaction type, such transaction representation is returned by
20///    RPC and stored by nodes internally.
21/// 2. Only-[`crate::TxEip4844WithSidecar`] transactions which are broadcasted over the network,
22///    submitted to RPC and stored in transaction pool.
23/// 3. Dynamic [`TxEip4844Variant`](crate::TxEip4844Variant) transactions to support both of the
24///    above cases via a single type.
25///
26/// Ethereum `TransactionType` flags as specified in EIPs [2718], [1559], [2930], [4844], and
27/// [7702].
28///
29/// [2718]: https://eips.ethereum.org/EIPS/eip-2718
30/// [1559]: https://eips.ethereum.org/EIPS/eip-1559
31/// [2930]: https://eips.ethereum.org/EIPS/eip-2930
32/// [4844]: https://eips.ethereum.org/EIPS/eip-4844
33/// [7702]: https://eips.ethereum.org/EIPS/eip-7702
34#[repr(u8)]
35#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
36#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
37#[cfg_attr(feature = "serde", serde(into = "U8", try_from = "U64"))]
38#[doc(alias = "TransactionType")]
39pub enum TxType {
40    /// Legacy transaction type.
41    #[default]
42    Legacy = 0,
43    /// EIP-2930 transaction type.
44    Eip2930 = 1,
45    /// EIP-1559 transaction type.
46    Eip1559 = 2,
47    /// EIP-4844 transaction type.
48    Eip4844 = 3,
49    /// EIP-7702 transaction type.
50    Eip7702 = 4,
51}
52
53impl From<TxType> for u8 {
54    fn from(value: TxType) -> Self {
55        value as Self
56    }
57}
58
59impl From<TxType> for U8 {
60    fn from(tx_type: TxType) -> Self {
61        Self::from(u8::from(tx_type))
62    }
63}
64
65impl TxType {
66    /// Returns true if the transaction type is Legacy.
67    #[inline]
68    pub const fn is_legacy(&self) -> bool {
69        matches!(self, Self::Legacy)
70    }
71
72    /// Returns true if the transaction type is EIP-2930.
73    #[inline]
74    pub const fn is_eip2930(&self) -> bool {
75        matches!(self, Self::Eip2930)
76    }
77
78    /// Returns true if the transaction type is EIP-1559.
79    #[inline]
80    pub const fn is_eip1559(&self) -> bool {
81        matches!(self, Self::Eip1559)
82    }
83
84    /// Returns true if the transaction type is EIP-4844.
85    #[inline]
86    pub const fn is_eip4844(&self) -> bool {
87        matches!(self, Self::Eip4844)
88    }
89
90    /// Returns true if the transaction type is EIP-7702.
91    #[inline]
92    pub const fn is_eip7702(&self) -> bool {
93        matches!(self, Self::Eip7702)
94    }
95
96    /// Returns true if the transaction type has dynamic fee.
97    #[inline]
98    pub const fn is_dynamic_fee(&self) -> bool {
99        matches!(self, Self::Eip1559 | Self::Eip4844 | Self::Eip7702)
100    }
101}
102
103impl IsTyped2718 for TxType {
104    fn is_type(type_id: u8) -> bool {
105        matches!(type_id, 0x0..=0x04)
106    }
107}
108
109impl fmt::Display for TxType {
110    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
111        match self {
112            Self::Legacy => write!(f, "Legacy"),
113            Self::Eip2930 => write!(f, "EIP-2930"),
114            Self::Eip1559 => write!(f, "EIP-1559"),
115            Self::Eip4844 => write!(f, "EIP-4844"),
116            Self::Eip7702 => write!(f, "EIP-7702"),
117        }
118    }
119}
120
121impl PartialEq<u8> for TxType {
122    fn eq(&self, other: &u8) -> bool {
123        (*self as u8) == *other
124    }
125}
126
127impl PartialEq<TxType> for u8 {
128    fn eq(&self, other: &TxType) -> bool {
129        *self == *other as Self
130    }
131}
132
133#[cfg(any(test, feature = "arbitrary"))]
134impl arbitrary::Arbitrary<'_> for TxType {
135    fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
136        Ok(u.int_in_range(0u8..=4)?.try_into().unwrap())
137    }
138}
139
140impl TryFrom<u8> for TxType {
141    type Error = Eip2718Error;
142
143    fn try_from(value: u8) -> Result<Self, Self::Error> {
144        Ok(match value {
145            0 => Self::Legacy,
146            1 => Self::Eip2930,
147            2 => Self::Eip1559,
148            3 => Self::Eip4844,
149            4 => Self::Eip7702,
150            _ => return Err(Eip2718Error::UnexpectedType(value)),
151        })
152    }
153}
154
155impl TryFrom<u64> for TxType {
156    type Error = &'static str;
157
158    fn try_from(value: u64) -> Result<Self, Self::Error> {
159        let err = || "invalid tx type";
160        let value: u8 = value.try_into().map_err(|_| err())?;
161        Self::try_from(value).map_err(|_| err())
162    }
163}
164
165impl TryFrom<U8> for TxType {
166    type Error = Eip2718Error;
167
168    fn try_from(value: U8) -> Result<Self, Self::Error> {
169        value.to::<u8>().try_into()
170    }
171}
172
173impl TryFrom<U64> for TxType {
174    type Error = &'static str;
175
176    fn try_from(value: U64) -> Result<Self, Self::Error> {
177        value.to::<u64>().try_into()
178    }
179}
180
181impl Encodable for TxType {
182    fn encode(&self, out: &mut dyn alloy_rlp::BufMut) {
183        (*self as u8).encode(out);
184    }
185
186    fn length(&self) -> usize {
187        1
188    }
189}
190
191impl Decodable for TxType {
192    fn decode(buf: &mut &[u8]) -> alloy_rlp::Result<Self> {
193        let ty = u8::decode(buf)?;
194        Self::try_from(ty).map_err(|_| alloy_rlp::Error::Custom("invalid transaction type"))
195    }
196}
197
198impl Typed2718 for TxType {
199    fn ty(&self) -> u8 {
200        (*self).into()
201    }
202}
203
204#[cfg(test)]
205mod tests {
206    use super::*;
207
208    #[test]
209    fn check_u8_id() {
210        assert_eq!(TxType::Legacy, TxType::Legacy as u8);
211        assert_eq!(TxType::Eip2930, TxType::Eip2930 as u8);
212        assert_eq!(TxType::Eip1559, TxType::Eip1559 as u8);
213        assert_eq!(TxType::Eip7702, TxType::Eip7702 as u8);
214        assert_eq!(TxType::Eip4844, TxType::Eip4844 as u8);
215    }
216}