1use alloy::consensus::{Signed, Typed2718};
2use alloy::network::eip2718::{Decodable2718, Encodable2718};
3use alloy::rlp::{Encodable, Header};
4use serde::{Deserialize, Serialize};
5
6use super::tx_type::TxType;
7use super::unsigned_tx::eip712::TxEip712;
8#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
11#[serde(
12 into = "serde_from::TaggedTxEnvelope",
13 from = "serde_from::MaybeTaggedTxEnvelope"
14)]
15pub enum TxEnvelope {
16 Native(alloy::consensus::TxEnvelope),
18 Eip712(Signed<TxEip712>),
20}
21
22impl TxEnvelope {
23 #[inline]
25 pub const fn is_legacy(&self) -> bool {
26 match self {
27 Self::Native(inner) => inner.is_legacy(),
28 Self::Eip712(_) => false,
29 }
30 }
31
32 #[inline]
34 pub const fn is_eip2930(&self) -> bool {
35 match self {
36 Self::Native(inner) => inner.is_eip2930(),
37 Self::Eip712(_) => false,
38 }
39 }
40
41 #[inline]
43 pub const fn is_eip1559(&self) -> bool {
44 match self {
45 Self::Native(inner) => inner.is_eip1559(),
46 Self::Eip712(_) => false,
47 }
48 }
49
50 #[inline]
52 pub const fn is_eip4844(&self) -> bool {
53 match self {
54 Self::Native(inner) => inner.is_eip4844(),
55 Self::Eip712(_) => false,
56 }
57 }
58
59 #[inline]
61 pub const fn is_eip7702(&self) -> bool {
62 match self {
63 Self::Native(inner) => inner.is_eip7702(),
64 Self::Eip712(_) => false,
65 }
66 }
67
68 #[inline]
70 pub const fn is_eip712(&self) -> bool {
71 matches!(self, Self::Eip712(_))
72 }
73
74 #[inline]
83 pub const fn is_replay_protected(&self) -> bool {
84 match self {
85 Self::Native(inner) => inner.is_replay_protected(),
86 Self::Eip712(_) => true,
87 }
88 }
89
90 pub const fn as_legacy(&self) -> Option<&Signed<alloy::consensus::TxLegacy>> {
92 match self {
93 Self::Native(inner) => inner.as_legacy(),
94 Self::Eip712(_) => None,
95 }
96 }
97
98 pub const fn as_eip2930(&self) -> Option<&Signed<alloy::consensus::TxEip2930>> {
100 match self {
101 Self::Native(inner) => inner.as_eip2930(),
102 Self::Eip712(_) => None,
103 }
104 }
105
106 pub const fn as_eip1559(&self) -> Option<&Signed<alloy::consensus::TxEip1559>> {
108 match self {
109 Self::Native(inner) => inner.as_eip1559(),
110 Self::Eip712(_) => None,
111 }
112 }
113
114 pub const fn as_eip4844(&self) -> Option<&Signed<alloy::consensus::TxEip4844Variant>> {
116 match self {
117 Self::Native(inner) => inner.as_eip4844(),
118 Self::Eip712(_) => None,
119 }
120 }
121
122 pub const fn as_eip7702(&self) -> Option<&Signed<alloy::consensus::TxEip7702>> {
124 match self {
125 Self::Native(inner) => inner.as_eip7702(),
126 Self::Eip712(_) => None,
127 }
128 }
129
130 pub const fn as_eip712(&self) -> Option<&Signed<TxEip712>> {
132 match self {
133 Self::Native(_) => None,
134 Self::Eip712(inner) => Some(inner),
135 }
136 }
137
138 pub fn signature_hash(&self) -> alloy::primitives::B256 {
140 match self {
141 Self::Native(inner) => inner.signature_hash(),
142 Self::Eip712(inner) => inner.signature_hash(),
143 }
144 }
145
146 pub const fn signature(&self) -> &alloy::primitives::Signature {
148 match self {
149 Self::Native(inner) => inner.signature(),
150 Self::Eip712(inner) => inner.signature(),
151 }
152 }
153
154 #[doc(alias = "transaction_hash")]
156 pub fn tx_hash(&self) -> &alloy::primitives::B256 {
157 match self {
158 Self::Native(inner) => inner.tx_hash(),
159 Self::Eip712(inner) => inner.hash(),
160 }
161 }
162
163 #[doc(alias = "transaction_type")]
165 pub const fn tx_type(&self) -> crate::network::tx_type::TxType {
166 match self {
167 Self::Native(inner) => match inner.tx_type() {
168 alloy::consensus::TxType::Legacy => crate::network::tx_type::TxType::Legacy,
169 alloy::consensus::TxType::Eip2930 => crate::network::tx_type::TxType::Eip2930,
170 alloy::consensus::TxType::Eip1559 => crate::network::tx_type::TxType::Eip1559,
171 alloy::consensus::TxType::Eip4844 => crate::network::tx_type::TxType::Eip4844,
172 alloy::consensus::TxType::Eip7702 => crate::network::tx_type::TxType::Eip7702,
173 },
174 Self::Eip712(_) => crate::network::tx_type::TxType::Eip712,
175 }
176 }
177
178 pub fn eip2718_encoded_length(&self) -> usize {
180 match self {
181 Self::Native(inner) => inner.eip2718_encoded_length(),
182 Self::Eip712(inner) => inner.tx().encoded_length(inner.signature()),
183 }
184 }
185}
186
187impl Typed2718 for TxEnvelope {
188 fn ty(&self) -> u8 {
189 match self {
190 Self::Native(inner) => inner.ty(),
191 Self::Eip712(inner) => inner.tx().tx_type() as u8,
192 }
193 }
194}
195
196impl Encodable2718 for TxEnvelope {
197 fn type_flag(&self) -> Option<u8> {
198 match self {
199 Self::Native(inner) => inner.type_flag(),
200 Self::Eip712(inner) => Some(inner.tx().tx_type() as u8),
201 }
202 }
203
204 fn encode_2718_len(&self) -> usize {
205 match self {
206 Self::Native(inner) => inner.encode_2718_len(),
207 Self::Eip712(inner) => {
208 let payload_length = inner.tx().fields_len()
209 + inner.signature().rlp_rs_len()
210 + inner.signature().v().length();
211 Header {
212 list: true,
213 payload_length,
214 }
215 .length()
216 + payload_length
217 }
218 }
219 }
220
221 fn encode_2718(&self, out: &mut dyn alloy::primitives::bytes::BufMut) {
222 match self {
223 Self::Native(inner) => inner.encode_2718(out),
224 Self::Eip712(tx) => {
225 tx.tx().encode_with_signature(tx.signature(), out);
226 }
227 }
228 }
229}
230
231impl Decodable2718 for TxEnvelope {
232 fn typed_decode(ty: u8, buf: &mut &[u8]) -> alloy::network::eip2718::Eip2718Result<Self> {
233 match ty {
234 _ if ty == (TxType::Eip712 as u8) => {
235 let tx = TxEip712::decode_signed_fields(buf)?;
236 Ok(Self::Eip712(tx))
237 }
238 _ => {
239 let inner = alloy::consensus::TxEnvelope::typed_decode(ty, buf)?;
240 Ok(Self::Native(inner))
241 }
242 }
243 }
244
245 fn fallback_decode(buf: &mut &[u8]) -> alloy::network::eip2718::Eip2718Result<Self> {
246 let inner = alloy::consensus::TxEnvelope::fallback_decode(buf)?;
247 Ok(Self::Native(inner))
248 }
249}
250
251impl AsRef<dyn alloy::consensus::Transaction> for TxEnvelope {
252 fn as_ref(&self) -> &dyn alloy::consensus::Transaction {
253 match self {
254 TxEnvelope::Native(inner) => inner,
255 TxEnvelope::Eip712(signed_inner) => signed_inner.tx(),
256 }
257 }
258}
259
260impl alloy::consensus::Transaction for TxEnvelope {
261 fn chain_id(&self) -> Option<alloy::primitives::ChainId> {
262 self.as_ref().chain_id()
263 }
264
265 fn nonce(&self) -> u64 {
266 self.as_ref().nonce()
267 }
268
269 fn gas_limit(&self) -> u64 {
270 self.as_ref().gas_limit()
271 }
272
273 fn gas_price(&self) -> Option<u128> {
274 self.as_ref().gas_price()
275 }
276
277 fn max_fee_per_gas(&self) -> u128 {
278 self.as_ref().max_fee_per_gas()
279 }
280
281 fn max_priority_fee_per_gas(&self) -> Option<u128> {
282 self.as_ref().max_priority_fee_per_gas()
283 }
284
285 fn max_fee_per_blob_gas(&self) -> Option<u128> {
286 self.as_ref().max_fee_per_blob_gas()
287 }
288
289 fn priority_fee_or_price(&self) -> u128 {
290 self.as_ref().priority_fee_or_price()
291 }
292
293 fn effective_gas_price(&self, base_fee: Option<u64>) -> u128 {
294 self.as_ref().effective_gas_price(base_fee)
295 }
296
297 fn is_dynamic_fee(&self) -> bool {
298 self.as_ref().is_dynamic_fee()
299 }
300
301 fn kind(&self) -> alloy::primitives::TxKind {
302 self.as_ref().kind()
303 }
304
305 fn is_create(&self) -> bool {
306 self.as_ref().is_create()
307 }
308
309 fn value(&self) -> alloy::primitives::U256 {
310 self.as_ref().value()
311 }
312
313 fn input(&self) -> &alloy::primitives::Bytes {
314 self.as_ref().input()
315 }
316
317 fn access_list(&self) -> Option<&alloy::rpc::types::AccessList> {
318 self.as_ref().access_list()
319 }
320
321 fn blob_versioned_hashes(&self) -> Option<&[alloy::primitives::B256]> {
322 self.as_ref().blob_versioned_hashes()
323 }
324
325 fn authorization_list(&self) -> Option<&[alloy::eips::eip7702::SignedAuthorization]> {
326 self.as_ref().authorization_list()
327 }
328}
329
330mod serde_from {
331 use crate::network::tx_envelope::TxEnvelope;
344 use crate::network::unsigned_tx::eip712::TxEip712;
345 use alloy::consensus::{Signed, TxEip1559, TxEip2930, TxEip4844Variant, TxEip7702, TxLegacy};
346
347 #[derive(Debug, serde::Deserialize)]
348 #[serde(untagged)]
349 pub(crate) enum MaybeTaggedTxEnvelope {
350 Tagged(TaggedTxEnvelope),
351 Untagged {
352 #[serde(
353 default,
354 rename = "type",
355 deserialize_with = "alloy::serde::reject_if_some"
356 )]
357 _ty: Option<()>,
358 #[serde(flatten, with = "alloy::consensus::transaction::signed_legacy_serde")]
359 tx: Signed<TxLegacy>,
360 },
361 }
362
363 #[derive(Debug, serde::Serialize, serde::Deserialize)]
364 #[serde(tag = "type")]
365 pub(crate) enum TaggedTxEnvelope {
366 #[serde(
368 rename = "0x0",
369 alias = "0x00",
370 with = "alloy::consensus::transaction::signed_legacy_serde"
371 )]
372 Legacy(Signed<TxLegacy>),
373 #[serde(rename = "0x1", alias = "0x01")]
374 Eip2930(Signed<TxEip2930>),
375 #[serde(rename = "0x2", alias = "0x02")]
376 Eip1559(Signed<TxEip1559>),
377 #[serde(rename = "0x3", alias = "0x03")]
378 Eip4844(Signed<TxEip4844Variant>),
379 #[serde(rename = "0x4", alias = "0x04")]
380 Eip7702(Signed<TxEip7702>),
381 #[serde(rename = "0x71")]
383 Eip712(Signed<TxEip712>),
384 }
385
386 impl From<MaybeTaggedTxEnvelope> for TxEnvelope {
387 fn from(value: MaybeTaggedTxEnvelope) -> Self {
388 match value {
389 MaybeTaggedTxEnvelope::Tagged(tagged) => tagged.into(),
390 MaybeTaggedTxEnvelope::Untagged { tx, .. } => {
391 Self::Native(alloy::consensus::TxEnvelope::Legacy(tx))
392 }
393 }
394 }
395 }
396
397 impl From<TaggedTxEnvelope> for TxEnvelope {
398 fn from(value: TaggedTxEnvelope) -> Self {
399 match value {
400 TaggedTxEnvelope::Legacy(signed) => {
401 Self::Native(alloy::consensus::TxEnvelope::Legacy(signed))
402 }
403 TaggedTxEnvelope::Eip2930(signed) => {
404 Self::Native(alloy::consensus::TxEnvelope::Eip2930(signed))
405 }
406 TaggedTxEnvelope::Eip1559(signed) => {
407 Self::Native(alloy::consensus::TxEnvelope::Eip1559(signed))
408 }
409 TaggedTxEnvelope::Eip4844(signed) => {
410 Self::Native(alloy::consensus::TxEnvelope::Eip4844(signed))
411 }
412 TaggedTxEnvelope::Eip7702(signed) => {
413 Self::Native(alloy::consensus::TxEnvelope::Eip7702(signed))
414 }
415 TaggedTxEnvelope::Eip712(signed) => Self::Eip712(signed),
416 }
417 }
418 }
419
420 impl From<TxEnvelope> for TaggedTxEnvelope {
421 fn from(value: TxEnvelope) -> Self {
422 match value {
423 TxEnvelope::Native(alloy::consensus::TxEnvelope::Legacy(signed)) => {
424 Self::Legacy(signed)
425 }
426 TxEnvelope::Native(alloy::consensus::TxEnvelope::Eip2930(signed)) => {
427 Self::Eip2930(signed)
428 }
429 TxEnvelope::Native(alloy::consensus::TxEnvelope::Eip1559(signed)) => {
430 Self::Eip1559(signed)
431 }
432 TxEnvelope::Native(alloy::consensus::TxEnvelope::Eip4844(signed)) => {
433 Self::Eip4844(signed)
434 }
435 TxEnvelope::Native(alloy::consensus::TxEnvelope::Eip7702(signed)) => {
436 Self::Eip7702(signed)
437 }
438 TxEnvelope::Eip712(signed) => Self::Eip712(signed),
439 }
440 }
441 }
442}