|
1 | | -use crate::{UnknownTxEnvelope, UnknownTypedTransaction}; |
2 | | -use alloy_consensus::{Transaction as TransactionTrait, TxEnvelope, TypedTransaction}; |
| 1 | +use core::fmt; |
| 2 | + |
| 3 | +use crate::UnknownTxEnvelope; |
| 4 | +use alloy_consensus::{Transaction as TransactionTrait, TxEnvelope, TxType}; |
3 | 5 | use alloy_eips::{ |
4 | | - eip2718::{Decodable2718, Encodable2718}, |
| 6 | + eip2718::{Decodable2718, Eip2718Error, Encodable2718}, |
| 7 | + eip2930::AccessList, |
5 | 8 | eip7702::SignedAuthorization, |
6 | 9 | }; |
7 | 10 | use alloy_primitives::{Bytes, ChainId, B256, U256}; |
8 | | -use alloy_rpc_types_eth::{AccessList, TransactionRequest}; |
9 | | -use alloy_serde::WithOtherFields; |
10 | | - |
11 | | -/// Unsigned transaction type for a catch-all network. |
12 | | -#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)] |
13 | | -#[serde(untagged)] |
14 | | -#[doc(alias = "AnyTypedTx")] |
15 | | -pub enum AnyTypedTransaction { |
16 | | - /// An Ethereum transaction. |
17 | | - Ethereum(TypedTransaction), |
18 | | - /// A transaction with unknown type. |
19 | | - Unknown(UnknownTypedTransaction), |
20 | | -} |
21 | 11 |
|
22 | | -impl From<UnknownTypedTransaction> for AnyTypedTransaction { |
23 | | - fn from(value: UnknownTypedTransaction) -> Self { |
24 | | - Self::Unknown(value) |
25 | | - } |
26 | | -} |
| 12 | +/// Transaction type for a catch-all network. |
| 13 | +#[derive(Debug, Clone, Copy, PartialEq, Eq)] |
| 14 | +#[doc(alias = "AnyTransactionType")] |
| 15 | +pub struct AnyTxType(pub u8); |
27 | 16 |
|
28 | | -impl From<TypedTransaction> for AnyTypedTransaction { |
29 | | - fn from(value: TypedTransaction) -> Self { |
30 | | - Self::Ethereum(value) |
| 17 | +impl fmt::Display for AnyTxType { |
| 18 | + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
| 19 | + write!(f, "AnyTxType({})", self.0) |
31 | 20 | } |
32 | 21 | } |
33 | 22 |
|
34 | | -impl From<AnyTxEnvelope> for AnyTypedTransaction { |
35 | | - fn from(value: AnyTxEnvelope) -> Self { |
36 | | - match value { |
37 | | - AnyTxEnvelope::Ethereum(tx) => Self::Ethereum(tx.into()), |
38 | | - AnyTxEnvelope::Unknown(UnknownTxEnvelope { inner, .. }) => inner.into(), |
39 | | - } |
40 | | - } |
41 | | -} |
| 23 | +impl TryFrom<u8> for AnyTxType { |
| 24 | + type Error = Eip2718Error; |
42 | 25 |
|
43 | | -impl From<AnyTypedTransaction> for WithOtherFields<TransactionRequest> { |
44 | | - fn from(value: AnyTypedTransaction) -> Self { |
45 | | - match value { |
46 | | - AnyTypedTransaction::Ethereum(tx) => Self::new(tx.into()), |
47 | | - AnyTypedTransaction::Unknown(UnknownTypedTransaction { ty, mut fields, .. }) => { |
48 | | - fields.insert("type".to_string(), serde_json::Value::Number(ty.0.into())); |
49 | | - Self { inner: Default::default(), other: fields } |
50 | | - } |
51 | | - } |
| 26 | + fn try_from(value: u8) -> Result<Self, Self::Error> { |
| 27 | + Ok(Self(value)) |
52 | 28 | } |
53 | 29 | } |
54 | 30 |
|
55 | | -impl From<AnyTxEnvelope> for WithOtherFields<TransactionRequest> { |
56 | | - fn from(value: AnyTxEnvelope) -> Self { |
57 | | - AnyTypedTransaction::from(value).into() |
| 31 | +impl From<&AnyTxType> for u8 { |
| 32 | + fn from(value: &AnyTxType) -> Self { |
| 33 | + value.0 |
58 | 34 | } |
59 | 35 | } |
60 | 36 |
|
61 | | -impl TransactionTrait for AnyTypedTransaction { |
62 | | - #[inline] |
63 | | - fn chain_id(&self) -> Option<ChainId> { |
64 | | - match self { |
65 | | - Self::Ethereum(inner) => inner.chain_id(), |
66 | | - Self::Unknown(inner) => inner.chain_id(), |
67 | | - } |
68 | | - } |
69 | | - |
70 | | - #[inline] |
71 | | - fn nonce(&self) -> u64 { |
72 | | - match self { |
73 | | - Self::Ethereum(inner) => inner.nonce(), |
74 | | - Self::Unknown(inner) => inner.nonce(), |
75 | | - } |
76 | | - } |
77 | | - |
78 | | - #[inline] |
79 | | - fn gas_limit(&self) -> u64 { |
80 | | - match self { |
81 | | - Self::Ethereum(inner) => inner.gas_limit(), |
82 | | - Self::Unknown(inner) => inner.gas_limit(), |
83 | | - } |
84 | | - } |
85 | | - |
86 | | - #[inline] |
87 | | - fn gas_price(&self) -> Option<u128> { |
88 | | - match self { |
89 | | - Self::Ethereum(inner) => inner.gas_price(), |
90 | | - Self::Unknown(inner) => inner.gas_price(), |
91 | | - } |
92 | | - } |
93 | | - |
94 | | - #[inline] |
95 | | - fn max_fee_per_gas(&self) -> u128 { |
96 | | - match self { |
97 | | - Self::Ethereum(inner) => inner.max_fee_per_gas(), |
98 | | - Self::Unknown(inner) => inner.max_fee_per_gas(), |
99 | | - } |
100 | | - } |
101 | | - |
102 | | - #[inline] |
103 | | - fn max_priority_fee_per_gas(&self) -> Option<u128> { |
104 | | - match self { |
105 | | - Self::Ethereum(inner) => inner.max_priority_fee_per_gas(), |
106 | | - Self::Unknown(inner) => inner.max_priority_fee_per_gas(), |
107 | | - } |
108 | | - } |
109 | | - |
110 | | - #[inline] |
111 | | - fn max_fee_per_blob_gas(&self) -> Option<u128> { |
112 | | - match self { |
113 | | - Self::Ethereum(inner) => inner.max_fee_per_blob_gas(), |
114 | | - Self::Unknown(inner) => inner.max_fee_per_blob_gas(), |
115 | | - } |
116 | | - } |
117 | | - |
118 | | - #[inline] |
119 | | - fn priority_fee_or_price(&self) -> u128 { |
120 | | - self.max_priority_fee_per_gas().or_else(|| self.gas_price()).unwrap_or_default() |
121 | | - } |
122 | | - |
123 | | - fn effective_gas_price(&self, base_fee: Option<u64>) -> u128 { |
124 | | - match self { |
125 | | - Self::Ethereum(inner) => inner.effective_gas_price(base_fee), |
126 | | - Self::Unknown(inner) => inner.effective_gas_price(base_fee), |
127 | | - } |
128 | | - } |
129 | | - |
130 | | - #[inline] |
131 | | - fn is_dynamic_fee(&self) -> bool { |
132 | | - match self { |
133 | | - Self::Ethereum(inner) => inner.is_dynamic_fee(), |
134 | | - Self::Unknown(inner) => inner.is_dynamic_fee(), |
135 | | - } |
136 | | - } |
137 | | - |
138 | | - fn kind(&self) -> alloy_primitives::TxKind { |
139 | | - match self { |
140 | | - Self::Ethereum(inner) => inner.kind(), |
141 | | - Self::Unknown(inner) => inner.kind(), |
142 | | - } |
143 | | - } |
144 | | - |
145 | | - #[inline] |
146 | | - fn is_create(&self) -> bool { |
147 | | - match self { |
148 | | - Self::Ethereum(inner) => inner.is_create(), |
149 | | - Self::Unknown(inner) => inner.is_create(), |
150 | | - } |
| 37 | +impl From<AnyTxType> for u8 { |
| 38 | + fn from(value: AnyTxType) -> Self { |
| 39 | + value.0 |
151 | 40 | } |
| 41 | +} |
152 | 42 |
|
153 | | - #[inline] |
154 | | - fn value(&self) -> U256 { |
155 | | - match self { |
156 | | - Self::Ethereum(inner) => inner.value(), |
157 | | - Self::Unknown(inner) => inner.value(), |
158 | | - } |
159 | | - } |
160 | | - |
161 | | - #[inline] |
162 | | - fn input(&self) -> &Bytes { |
163 | | - match self { |
164 | | - Self::Ethereum(inner) => inner.input(), |
165 | | - Self::Unknown(inner) => inner.input(), |
166 | | - } |
| 43 | +#[cfg(feature = "serde")] |
| 44 | +impl serde::Serialize for AnyTxType { |
| 45 | + fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> |
| 46 | + where |
| 47 | + S: serde::Serializer, |
| 48 | + { |
| 49 | + use alloy_primitives::U8; |
| 50 | + U8::from(self.0).serialize(serializer) |
167 | 51 | } |
| 52 | +} |
168 | 53 |
|
169 | | - #[inline] |
170 | | - fn ty(&self) -> u8 { |
171 | | - match self { |
172 | | - Self::Ethereum(inner) => inner.ty(), |
173 | | - Self::Unknown(inner) => inner.ty(), |
174 | | - } |
| 54 | +#[cfg(feature = "serde")] |
| 55 | +impl<'de> serde::Deserialize<'de> for AnyTxType { |
| 56 | + fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> |
| 57 | + where |
| 58 | + D: serde::Deserializer<'de>, |
| 59 | + { |
| 60 | + use alloy_primitives::U8; |
| 61 | + U8::deserialize(deserializer).map(|t| Self(t.to::<u8>())) |
175 | 62 | } |
| 63 | +} |
176 | 64 |
|
177 | | - #[inline] |
178 | | - fn access_list(&self) -> Option<&AccessList> { |
179 | | - match self { |
180 | | - Self::Ethereum(inner) => inner.access_list(), |
181 | | - Self::Unknown(inner) => inner.access_list(), |
182 | | - } |
183 | | - } |
| 65 | +impl TryFrom<AnyTxType> for TxType { |
| 66 | + type Error = Eip2718Error; |
184 | 67 |
|
185 | | - #[inline] |
186 | | - fn blob_versioned_hashes(&self) -> Option<&[B256]> { |
187 | | - match self { |
188 | | - Self::Ethereum(inner) => inner.blob_versioned_hashes(), |
189 | | - Self::Unknown(inner) => inner.blob_versioned_hashes(), |
190 | | - } |
| 68 | + fn try_from(value: AnyTxType) -> Result<Self, Self::Error> { |
| 69 | + value.0.try_into() |
191 | 70 | } |
| 71 | +} |
192 | 72 |
|
193 | | - #[inline] |
194 | | - fn authorization_list(&self) -> Option<&[SignedAuthorization]> { |
195 | | - match self { |
196 | | - Self::Ethereum(inner) => inner.authorization_list(), |
197 | | - Self::Unknown(inner) => inner.authorization_list(), |
198 | | - } |
| 73 | +impl From<TxType> for AnyTxType { |
| 74 | + fn from(value: TxType) -> Self { |
| 75 | + Self(value as u8) |
199 | 76 | } |
200 | 77 | } |
201 | 78 |
|
202 | 79 | /// Transaction envelope for a catch-all network. |
203 | | -#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)] |
204 | | -#[serde(untagged)] |
| 80 | +#[derive(Debug, Clone, PartialEq, Eq)] |
| 81 | +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] |
| 82 | +#[cfg_attr(feature = "serde", serde(untagged))] |
205 | 83 | #[doc(alias = "AnyTransactionEnvelope")] |
206 | 84 | pub enum AnyTxEnvelope { |
207 | 85 | /// An Ethereum transaction. |
@@ -396,3 +274,14 @@ impl TransactionTrait for AnyTxEnvelope { |
396 | 274 | } |
397 | 275 | } |
398 | 276 | } |
| 277 | + |
| 278 | +#[cfg(test)] |
| 279 | +mod tests { |
| 280 | + use super::*; |
| 281 | + |
| 282 | + #[test] |
| 283 | + fn test_serde_anytype() { |
| 284 | + let ty = AnyTxType(126); |
| 285 | + assert_eq!(serde_json::to_string(&ty).unwrap(), "\"0x7e\""); |
| 286 | + } |
| 287 | +} |
0 commit comments