1use crate::{
2 transaction::{
3 eip4844::{TxEip4844, TxEip4844Variant, TxEip4844WithSidecar},
4 RlpEcdsaEncodableTx,
5 },
6 EthereumTxEnvelope, SignableTransaction, Transaction, TxEip1559, TxEip2930, TxEip7702,
7 TxLegacy, TxType,
8};
9use alloy_eips::{
10 eip2718::IsTyped2718, eip2930::AccessList, eip7702::SignedAuthorization, Typed2718,
11};
12use alloy_primitives::{bytes::BufMut, Bytes, ChainId, Signature, TxHash, TxKind, B256, U256};
13
14pub type TypedTransaction = EthereumTypedTransaction<TxEip4844Variant>;
16
17#[derive(Clone, Debug, PartialEq, Eq, Hash)]
33#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
34#[cfg_attr(
35 feature = "serde",
36 serde(
37 from = "serde_from::MaybeTaggedTypedTransaction<Eip4844>",
38 into = "serde_from::TaggedTypedTransaction<Eip4844>",
39 bound = "Eip4844: Clone + serde::Serialize + serde::de::DeserializeOwned"
40 )
41)]
42#[cfg_attr(all(any(test, feature = "arbitrary"), feature = "k256"), derive(arbitrary::Arbitrary))]
43#[doc(alias = "TypedTx", alias = "TxTyped", alias = "TransactionTyped")]
44pub enum EthereumTypedTransaction<Eip4844> {
45 #[cfg_attr(feature = "serde", serde(rename = "0x00", alias = "0x0"))]
47 Legacy(TxLegacy),
48 #[cfg_attr(feature = "serde", serde(rename = "0x01", alias = "0x1"))]
50 Eip2930(TxEip2930),
51 #[cfg_attr(feature = "serde", serde(rename = "0x02", alias = "0x2"))]
53 Eip1559(TxEip1559),
54 #[cfg_attr(feature = "serde", serde(rename = "0x03", alias = "0x3"))]
56 Eip4844(Eip4844),
57 #[cfg_attr(feature = "serde", serde(rename = "0x04", alias = "0x4"))]
59 Eip7702(TxEip7702),
60}
61
62impl<Eip4844> From<TxLegacy> for EthereumTypedTransaction<Eip4844> {
63 fn from(tx: TxLegacy) -> Self {
64 Self::Legacy(tx)
65 }
66}
67
68impl<Eip4844> From<TxEip2930> for EthereumTypedTransaction<Eip4844> {
69 fn from(tx: TxEip2930) -> Self {
70 Self::Eip2930(tx)
71 }
72}
73
74impl<Eip4844> From<TxEip1559> for EthereumTypedTransaction<Eip4844> {
75 fn from(tx: TxEip1559) -> Self {
76 Self::Eip1559(tx)
77 }
78}
79
80impl<Eip4844: From<TxEip4844>> From<TxEip4844> for EthereumTypedTransaction<Eip4844> {
81 fn from(tx: TxEip4844) -> Self {
82 Self::Eip4844(tx.into())
83 }
84}
85
86impl<T, Eip4844: From<TxEip4844WithSidecar<T>>> From<TxEip4844WithSidecar<T>>
87 for EthereumTypedTransaction<Eip4844>
88{
89 fn from(tx: TxEip4844WithSidecar<T>) -> Self {
90 Self::Eip4844(tx.into())
91 }
92}
93
94impl<Sidecar, Eip4844: From<TxEip4844Variant<Sidecar>>> From<TxEip4844Variant<Sidecar>>
95 for EthereumTypedTransaction<Eip4844>
96{
97 fn from(tx: TxEip4844Variant<Sidecar>) -> Self {
98 Self::Eip4844(tx.into())
99 }
100}
101
102impl<Eip4844> From<TxEip7702> for EthereumTypedTransaction<Eip4844> {
103 fn from(tx: TxEip7702) -> Self {
104 Self::Eip7702(tx)
105 }
106}
107
108impl<Eip4844> From<EthereumTxEnvelope<Eip4844>> for EthereumTypedTransaction<Eip4844> {
109 fn from(envelope: EthereumTxEnvelope<Eip4844>) -> Self {
110 match envelope {
111 EthereumTxEnvelope::Legacy(tx) => Self::Legacy(tx.strip_signature()),
112 EthereumTxEnvelope::Eip2930(tx) => Self::Eip2930(tx.strip_signature()),
113 EthereumTxEnvelope::Eip1559(tx) => Self::Eip1559(tx.strip_signature()),
114 EthereumTxEnvelope::Eip4844(tx) => Self::Eip4844(tx.strip_signature()),
115 EthereumTxEnvelope::Eip7702(tx) => Self::Eip7702(tx.strip_signature()),
116 }
117 }
118}
119
120impl<T> From<EthereumTypedTransaction<TxEip4844WithSidecar<T>>>
121 for EthereumTypedTransaction<TxEip4844>
122{
123 fn from(value: EthereumTypedTransaction<TxEip4844WithSidecar<T>>) -> Self {
124 value.map_eip4844(|eip4844| eip4844.into())
125 }
126}
127
128impl<T> From<EthereumTypedTransaction<TxEip4844Variant<T>>>
129 for EthereumTypedTransaction<TxEip4844>
130{
131 fn from(value: EthereumTypedTransaction<TxEip4844Variant<T>>) -> Self {
132 value.map_eip4844(|eip4844| eip4844.into())
133 }
134}
135
136impl<T> From<EthereumTypedTransaction<TxEip4844>>
137 for EthereumTypedTransaction<TxEip4844Variant<T>>
138{
139 fn from(value: EthereumTypedTransaction<TxEip4844>) -> Self {
140 value.map_eip4844(|eip4844| eip4844.into())
141 }
142}
143
144impl<Eip4844> EthereumTypedTransaction<Eip4844> {
145 pub fn map_eip4844<U>(self, mut f: impl FnMut(Eip4844) -> U) -> EthereumTypedTransaction<U> {
150 match self {
151 Self::Legacy(tx) => EthereumTypedTransaction::Legacy(tx),
152 Self::Eip2930(tx) => EthereumTypedTransaction::Eip2930(tx),
153 Self::Eip1559(tx) => EthereumTypedTransaction::Eip1559(tx),
154 Self::Eip4844(tx) => EthereumTypedTransaction::Eip4844(f(tx)),
155 Self::Eip7702(tx) => EthereumTypedTransaction::Eip7702(tx),
156 }
157 }
158}
159
160impl<Eip4844: RlpEcdsaEncodableTx> EthereumTypedTransaction<Eip4844> {
161 #[doc(alias = "transaction_type")]
163 pub const fn tx_type(&self) -> TxType {
164 match self {
165 Self::Legacy(_) => TxType::Legacy,
166 Self::Eip2930(_) => TxType::Eip2930,
167 Self::Eip1559(_) => TxType::Eip1559,
168 Self::Eip4844(_) => TxType::Eip4844,
169 Self::Eip7702(_) => TxType::Eip7702,
170 }
171 }
172
173 pub const fn legacy(&self) -> Option<&TxLegacy> {
175 match self {
176 Self::Legacy(tx) => Some(tx),
177 _ => None,
178 }
179 }
180
181 pub const fn eip2930(&self) -> Option<&TxEip2930> {
183 match self {
184 Self::Eip2930(tx) => Some(tx),
185 _ => None,
186 }
187 }
188
189 pub const fn eip1559(&self) -> Option<&TxEip1559> {
191 match self {
192 Self::Eip1559(tx) => Some(tx),
193 _ => None,
194 }
195 }
196
197 pub const fn eip7702(&self) -> Option<&TxEip7702> {
199 match self {
200 Self::Eip7702(tx) => Some(tx),
201 _ => None,
202 }
203 }
204
205 pub fn tx_hash(&self, signature: &Signature) -> TxHash {
207 match self {
208 Self::Legacy(tx) => tx.tx_hash(signature),
209 Self::Eip2930(tx) => tx.tx_hash(signature),
210 Self::Eip1559(tx) => tx.tx_hash(signature),
211 Self::Eip4844(tx) => tx.tx_hash(signature),
212 Self::Eip7702(tx) => tx.tx_hash(signature),
213 }
214 }
215}
216
217impl<Eip4844: Transaction> Transaction for EthereumTypedTransaction<Eip4844> {
218 #[inline]
219 fn chain_id(&self) -> Option<ChainId> {
220 match self {
221 Self::Legacy(tx) => tx.chain_id(),
222 Self::Eip2930(tx) => tx.chain_id(),
223 Self::Eip1559(tx) => tx.chain_id(),
224 Self::Eip4844(tx) => tx.chain_id(),
225 Self::Eip7702(tx) => tx.chain_id(),
226 }
227 }
228
229 #[inline]
230 fn nonce(&self) -> u64 {
231 match self {
232 Self::Legacy(tx) => tx.nonce(),
233 Self::Eip2930(tx) => tx.nonce(),
234 Self::Eip1559(tx) => tx.nonce(),
235 Self::Eip4844(tx) => tx.nonce(),
236 Self::Eip7702(tx) => tx.nonce(),
237 }
238 }
239
240 #[inline]
241 fn gas_limit(&self) -> u64 {
242 match self {
243 Self::Legacy(tx) => tx.gas_limit(),
244 Self::Eip2930(tx) => tx.gas_limit(),
245 Self::Eip1559(tx) => tx.gas_limit(),
246 Self::Eip4844(tx) => tx.gas_limit(),
247 Self::Eip7702(tx) => tx.gas_limit(),
248 }
249 }
250
251 #[inline]
252 fn gas_price(&self) -> Option<u128> {
253 match self {
254 Self::Legacy(tx) => tx.gas_price(),
255 Self::Eip2930(tx) => tx.gas_price(),
256 Self::Eip1559(tx) => tx.gas_price(),
257 Self::Eip4844(tx) => tx.gas_price(),
258 Self::Eip7702(tx) => tx.gas_price(),
259 }
260 }
261
262 #[inline]
263 fn max_fee_per_gas(&self) -> u128 {
264 match self {
265 Self::Legacy(tx) => tx.max_fee_per_gas(),
266 Self::Eip2930(tx) => tx.max_fee_per_gas(),
267 Self::Eip1559(tx) => tx.max_fee_per_gas(),
268 Self::Eip4844(tx) => tx.max_fee_per_gas(),
269 Self::Eip7702(tx) => tx.max_fee_per_gas(),
270 }
271 }
272
273 #[inline]
274 fn max_priority_fee_per_gas(&self) -> Option<u128> {
275 match self {
276 Self::Legacy(tx) => tx.max_priority_fee_per_gas(),
277 Self::Eip2930(tx) => tx.max_priority_fee_per_gas(),
278 Self::Eip1559(tx) => tx.max_priority_fee_per_gas(),
279 Self::Eip4844(tx) => tx.max_priority_fee_per_gas(),
280 Self::Eip7702(tx) => tx.max_priority_fee_per_gas(),
281 }
282 }
283
284 #[inline]
285 fn max_fee_per_blob_gas(&self) -> Option<u128> {
286 match self {
287 Self::Legacy(tx) => tx.max_fee_per_blob_gas(),
288 Self::Eip2930(tx) => tx.max_fee_per_blob_gas(),
289 Self::Eip1559(tx) => tx.max_fee_per_blob_gas(),
290 Self::Eip4844(tx) => tx.max_fee_per_blob_gas(),
291 Self::Eip7702(tx) => tx.max_fee_per_blob_gas(),
292 }
293 }
294
295 #[inline]
296 fn priority_fee_or_price(&self) -> u128 {
297 match self {
298 Self::Legacy(tx) => tx.priority_fee_or_price(),
299 Self::Eip2930(tx) => tx.priority_fee_or_price(),
300 Self::Eip1559(tx) => tx.priority_fee_or_price(),
301 Self::Eip4844(tx) => tx.priority_fee_or_price(),
302 Self::Eip7702(tx) => tx.priority_fee_or_price(),
303 }
304 }
305
306 fn effective_gas_price(&self, base_fee: Option<u64>) -> u128 {
307 match self {
308 Self::Legacy(tx) => tx.effective_gas_price(base_fee),
309 Self::Eip2930(tx) => tx.effective_gas_price(base_fee),
310 Self::Eip1559(tx) => tx.effective_gas_price(base_fee),
311 Self::Eip4844(tx) => tx.effective_gas_price(base_fee),
312 Self::Eip7702(tx) => tx.effective_gas_price(base_fee),
313 }
314 }
315
316 #[inline]
317 fn is_dynamic_fee(&self) -> bool {
318 match self {
319 Self::Legacy(tx) => tx.is_dynamic_fee(),
320 Self::Eip2930(tx) => tx.is_dynamic_fee(),
321 Self::Eip1559(tx) => tx.is_dynamic_fee(),
322 Self::Eip4844(tx) => tx.is_dynamic_fee(),
323 Self::Eip7702(tx) => tx.is_dynamic_fee(),
324 }
325 }
326
327 #[inline]
328 fn kind(&self) -> TxKind {
329 match self {
330 Self::Legacy(tx) => tx.kind(),
331 Self::Eip2930(tx) => tx.kind(),
332 Self::Eip1559(tx) => tx.kind(),
333 Self::Eip4844(tx) => tx.kind(),
334 Self::Eip7702(tx) => tx.kind(),
335 }
336 }
337
338 #[inline]
339 fn is_create(&self) -> bool {
340 match self {
341 Self::Legacy(tx) => tx.is_create(),
342 Self::Eip2930(tx) => tx.is_create(),
343 Self::Eip1559(tx) => tx.is_create(),
344 Self::Eip4844(tx) => tx.is_create(),
345 Self::Eip7702(tx) => tx.is_create(),
346 }
347 }
348
349 #[inline]
350 fn value(&self) -> U256 {
351 match self {
352 Self::Legacy(tx) => tx.value(),
353 Self::Eip2930(tx) => tx.value(),
354 Self::Eip1559(tx) => tx.value(),
355 Self::Eip4844(tx) => tx.value(),
356 Self::Eip7702(tx) => tx.value(),
357 }
358 }
359
360 #[inline]
361 fn input(&self) -> &Bytes {
362 match self {
363 Self::Legacy(tx) => tx.input(),
364 Self::Eip2930(tx) => tx.input(),
365 Self::Eip1559(tx) => tx.input(),
366 Self::Eip4844(tx) => tx.input(),
367 Self::Eip7702(tx) => tx.input(),
368 }
369 }
370
371 #[inline]
372 fn access_list(&self) -> Option<&AccessList> {
373 match self {
374 Self::Legacy(tx) => tx.access_list(),
375 Self::Eip2930(tx) => tx.access_list(),
376 Self::Eip1559(tx) => tx.access_list(),
377 Self::Eip4844(tx) => tx.access_list(),
378 Self::Eip7702(tx) => tx.access_list(),
379 }
380 }
381
382 #[inline]
383 fn blob_versioned_hashes(&self) -> Option<&[B256]> {
384 match self {
385 Self::Legacy(tx) => tx.blob_versioned_hashes(),
386 Self::Eip2930(tx) => tx.blob_versioned_hashes(),
387 Self::Eip1559(tx) => tx.blob_versioned_hashes(),
388 Self::Eip4844(tx) => tx.blob_versioned_hashes(),
389 Self::Eip7702(tx) => tx.blob_versioned_hashes(),
390 }
391 }
392
393 #[inline]
394 fn authorization_list(&self) -> Option<&[SignedAuthorization]> {
395 match self {
396 Self::Legacy(tx) => tx.authorization_list(),
397 Self::Eip2930(tx) => tx.authorization_list(),
398 Self::Eip1559(tx) => tx.authorization_list(),
399 Self::Eip4844(tx) => tx.authorization_list(),
400 Self::Eip7702(tx) => tx.authorization_list(),
401 }
402 }
403}
404
405impl<Eip4844: Typed2718> Typed2718 for EthereumTypedTransaction<Eip4844> {
406 fn ty(&self) -> u8 {
407 match self {
408 Self::Legacy(tx) => tx.ty(),
409 Self::Eip2930(tx) => tx.ty(),
410 Self::Eip1559(tx) => tx.ty(),
411 Self::Eip4844(tx) => tx.ty(),
412 Self::Eip7702(tx) => tx.ty(),
413 }
414 }
415}
416
417impl<T> IsTyped2718 for EthereumTypedTransaction<T> {
418 fn is_type(type_id: u8) -> bool {
419 <TxType as IsTyped2718>::is_type(type_id)
420 }
421}
422
423impl<Eip4844: RlpEcdsaEncodableTx + Typed2718> RlpEcdsaEncodableTx
424 for EthereumTypedTransaction<Eip4844>
425{
426 fn rlp_encoded_fields_length(&self) -> usize {
427 match self {
428 Self::Legacy(tx) => tx.rlp_encoded_fields_length(),
429 Self::Eip2930(tx) => tx.rlp_encoded_fields_length(),
430 Self::Eip1559(tx) => tx.rlp_encoded_fields_length(),
431 Self::Eip4844(tx) => tx.rlp_encoded_fields_length(),
432 Self::Eip7702(tx) => tx.rlp_encoded_fields_length(),
433 }
434 }
435
436 fn rlp_encode_fields(&self, out: &mut dyn alloy_rlp::BufMut) {
437 match self {
438 Self::Legacy(tx) => tx.rlp_encode_fields(out),
439 Self::Eip2930(tx) => tx.rlp_encode_fields(out),
440 Self::Eip1559(tx) => tx.rlp_encode_fields(out),
441 Self::Eip4844(tx) => tx.rlp_encode_fields(out),
442 Self::Eip7702(tx) => tx.rlp_encode_fields(out),
443 }
444 }
445
446 fn eip2718_encode_with_type(&self, signature: &Signature, _ty: u8, out: &mut dyn BufMut) {
447 match self {
448 Self::Legacy(tx) => tx.eip2718_encode_with_type(signature, tx.ty(), out),
449 Self::Eip2930(tx) => tx.eip2718_encode_with_type(signature, tx.ty(), out),
450 Self::Eip1559(tx) => tx.eip2718_encode_with_type(signature, tx.ty(), out),
451 Self::Eip4844(tx) => tx.eip2718_encode_with_type(signature, tx.ty(), out),
452 Self::Eip7702(tx) => tx.eip2718_encode_with_type(signature, tx.ty(), out),
453 }
454 }
455
456 fn eip2718_encode(&self, signature: &Signature, out: &mut dyn BufMut) {
457 match self {
458 Self::Legacy(tx) => tx.eip2718_encode(signature, out),
459 Self::Eip2930(tx) => tx.eip2718_encode(signature, out),
460 Self::Eip1559(tx) => tx.eip2718_encode(signature, out),
461 Self::Eip4844(tx) => tx.eip2718_encode(signature, out),
462 Self::Eip7702(tx) => tx.eip2718_encode(signature, out),
463 }
464 }
465
466 fn network_encode_with_type(&self, signature: &Signature, _ty: u8, out: &mut dyn BufMut) {
467 match self {
468 Self::Legacy(tx) => tx.network_encode_with_type(signature, tx.ty(), out),
469 Self::Eip2930(tx) => tx.network_encode_with_type(signature, tx.ty(), out),
470 Self::Eip1559(tx) => tx.network_encode_with_type(signature, tx.ty(), out),
471 Self::Eip4844(tx) => tx.network_encode_with_type(signature, tx.ty(), out),
472 Self::Eip7702(tx) => tx.network_encode_with_type(signature, tx.ty(), out),
473 }
474 }
475
476 fn network_encode(&self, signature: &Signature, out: &mut dyn BufMut) {
477 match self {
478 Self::Legacy(tx) => tx.network_encode(signature, out),
479 Self::Eip2930(tx) => tx.network_encode(signature, out),
480 Self::Eip1559(tx) => tx.network_encode(signature, out),
481 Self::Eip4844(tx) => tx.network_encode(signature, out),
482 Self::Eip7702(tx) => tx.network_encode(signature, out),
483 }
484 }
485
486 fn tx_hash_with_type(&self, signature: &Signature, _ty: u8) -> TxHash {
487 match self {
488 Self::Legacy(tx) => tx.tx_hash_with_type(signature, tx.ty()),
489 Self::Eip2930(tx) => tx.tx_hash_with_type(signature, tx.ty()),
490 Self::Eip1559(tx) => tx.tx_hash_with_type(signature, tx.ty()),
491 Self::Eip4844(tx) => tx.tx_hash_with_type(signature, tx.ty()),
492 Self::Eip7702(tx) => tx.tx_hash_with_type(signature, tx.ty()),
493 }
494 }
495
496 fn tx_hash(&self, signature: &Signature) -> TxHash {
497 match self {
498 Self::Legacy(tx) => tx.tx_hash(signature),
499 Self::Eip2930(tx) => tx.tx_hash(signature),
500 Self::Eip1559(tx) => tx.tx_hash(signature),
501 Self::Eip4844(tx) => tx.tx_hash(signature),
502 Self::Eip7702(tx) => tx.tx_hash(signature),
503 }
504 }
505}
506
507impl<Eip4844: SignableTransaction<Signature>> SignableTransaction<Signature>
508 for EthereumTypedTransaction<Eip4844>
509{
510 fn set_chain_id(&mut self, chain_id: ChainId) {
511 match self {
512 Self::Legacy(tx) => tx.set_chain_id(chain_id),
513 Self::Eip2930(tx) => tx.set_chain_id(chain_id),
514 Self::Eip1559(tx) => tx.set_chain_id(chain_id),
515 Self::Eip4844(tx) => tx.set_chain_id(chain_id),
516 Self::Eip7702(tx) => tx.set_chain_id(chain_id),
517 }
518 }
519
520 fn encode_for_signing(&self, out: &mut dyn BufMut) {
521 match self {
522 Self::Legacy(tx) => tx.encode_for_signing(out),
523 Self::Eip2930(tx) => tx.encode_for_signing(out),
524 Self::Eip1559(tx) => tx.encode_for_signing(out),
525 Self::Eip4844(tx) => tx.encode_for_signing(out),
526 Self::Eip7702(tx) => tx.encode_for_signing(out),
527 }
528 }
529
530 fn payload_len_for_signature(&self) -> usize {
531 match self {
532 Self::Legacy(tx) => tx.payload_len_for_signature(),
533 Self::Eip2930(tx) => tx.payload_len_for_signature(),
534 Self::Eip1559(tx) => tx.payload_len_for_signature(),
535 Self::Eip4844(tx) => tx.payload_len_for_signature(),
536 Self::Eip7702(tx) => tx.payload_len_for_signature(),
537 }
538 }
539}
540
541#[cfg(feature = "serde")]
542impl<Eip4844, T: From<EthereumTypedTransaction<Eip4844>>> From<EthereumTypedTransaction<Eip4844>>
543 for alloy_serde::WithOtherFields<T>
544{
545 fn from(value: EthereumTypedTransaction<Eip4844>) -> Self {
546 Self::new(value.into())
547 }
548}
549
550#[cfg(feature = "serde")]
551impl<Eip4844, T> From<EthereumTxEnvelope<Eip4844>> for alloy_serde::WithOtherFields<T>
552where
553 T: From<EthereumTxEnvelope<Eip4844>>,
554{
555 fn from(value: EthereumTxEnvelope<Eip4844>) -> Self {
556 Self::new(value.into())
557 }
558}
559
560#[cfg(feature = "serde")]
561mod serde_from {
562 use crate::{EthereumTypedTransaction, TxEip1559, TxEip2930, TxEip7702, TxLegacy};
574
575 #[derive(Debug, serde::Deserialize)]
576 #[serde(untagged)]
577 pub(crate) enum MaybeTaggedTypedTransaction<Eip4844> {
578 Tagged(TaggedTypedTransaction<Eip4844>),
579 Untagged {
580 #[serde(default, rename = "type", deserialize_with = "alloy_serde::reject_if_some")]
581 _ty: Option<()>,
582 #[serde(flatten)]
583 tx: TxLegacy,
584 },
585 }
586
587 #[derive(Debug, serde::Serialize, serde::Deserialize)]
588 #[serde(tag = "type")]
589 pub(crate) enum TaggedTypedTransaction<Eip4844> {
590 #[serde(rename = "0x00", alias = "0x0")]
592 Legacy(TxLegacy),
593 #[serde(rename = "0x01", alias = "0x1")]
595 Eip2930(TxEip2930),
596 #[serde(rename = "0x02", alias = "0x2")]
598 Eip1559(TxEip1559),
599 #[serde(rename = "0x03", alias = "0x3")]
601 Eip4844(Eip4844),
602 #[serde(rename = "0x04", alias = "0x4")]
604 Eip7702(TxEip7702),
605 }
606
607 impl<Eip4844> From<MaybeTaggedTypedTransaction<Eip4844>> for EthereumTypedTransaction<Eip4844> {
608 fn from(value: MaybeTaggedTypedTransaction<Eip4844>) -> Self {
609 match value {
610 MaybeTaggedTypedTransaction::Tagged(tagged) => tagged.into(),
611 MaybeTaggedTypedTransaction::Untagged { tx, .. } => Self::Legacy(tx),
612 }
613 }
614 }
615
616 impl<Eip4844> From<TaggedTypedTransaction<Eip4844>> for EthereumTypedTransaction<Eip4844> {
617 fn from(value: TaggedTypedTransaction<Eip4844>) -> Self {
618 match value {
619 TaggedTypedTransaction::Legacy(signed) => Self::Legacy(signed),
620 TaggedTypedTransaction::Eip2930(signed) => Self::Eip2930(signed),
621 TaggedTypedTransaction::Eip1559(signed) => Self::Eip1559(signed),
622 TaggedTypedTransaction::Eip4844(signed) => Self::Eip4844(signed),
623 TaggedTypedTransaction::Eip7702(signed) => Self::Eip7702(signed),
624 }
625 }
626 }
627
628 impl<Eip4844> From<EthereumTypedTransaction<Eip4844>> for TaggedTypedTransaction<Eip4844> {
629 fn from(value: EthereumTypedTransaction<Eip4844>) -> Self {
630 match value {
631 EthereumTypedTransaction::Legacy(signed) => Self::Legacy(signed),
632 EthereumTypedTransaction::Eip2930(signed) => Self::Eip2930(signed),
633 EthereumTypedTransaction::Eip1559(signed) => Self::Eip1559(signed),
634 EthereumTypedTransaction::Eip4844(signed) => Self::Eip4844(signed),
635 EthereumTypedTransaction::Eip7702(signed) => Self::Eip7702(signed),
636 }
637 }
638 }
639}
640
641#[cfg(all(feature = "serde", feature = "serde-bincode-compat"))]
643pub(crate) mod serde_bincode_compat {
644 use alloc::borrow::Cow;
645 use serde::{Deserialize, Deserializer, Serialize, Serializer};
646 use serde_with::{DeserializeAs, SerializeAs};
647
648 #[derive(Debug, Serialize, Deserialize)]
664 pub enum EthereumTypedTransaction<'a, Eip4844: Clone = crate::transaction::TxEip4844> {
665 Legacy(crate::serde_bincode_compat::transaction::TxLegacy<'a>),
667 Eip2930(crate::serde_bincode_compat::transaction::TxEip2930<'a>),
669 Eip1559(crate::serde_bincode_compat::transaction::TxEip1559<'a>),
671 Eip4844(Cow<'a, Eip4844>),
675 Eip7702(crate::serde_bincode_compat::transaction::TxEip7702<'a>),
677 }
678
679 impl<'a, T: Clone> From<&'a super::EthereumTypedTransaction<T>>
680 for EthereumTypedTransaction<'a, T>
681 {
682 fn from(value: &'a super::EthereumTypedTransaction<T>) -> Self {
683 match value {
684 super::EthereumTypedTransaction::Legacy(tx) => Self::Legacy(tx.into()),
685 super::EthereumTypedTransaction::Eip2930(tx) => Self::Eip2930(tx.into()),
686 super::EthereumTypedTransaction::Eip1559(tx) => Self::Eip1559(tx.into()),
687 super::EthereumTypedTransaction::Eip4844(tx) => Self::Eip4844(Cow::Borrowed(tx)),
688 super::EthereumTypedTransaction::Eip7702(tx) => Self::Eip7702(tx.into()),
689 }
690 }
691 }
692
693 impl<'a, T: Clone> From<EthereumTypedTransaction<'a, T>> for super::EthereumTypedTransaction<T> {
694 fn from(value: EthereumTypedTransaction<'a, T>) -> Self {
695 match value {
696 EthereumTypedTransaction::Legacy(tx) => Self::Legacy(tx.into()),
697 EthereumTypedTransaction::Eip2930(tx) => Self::Eip2930(tx.into()),
698 EthereumTypedTransaction::Eip1559(tx) => Self::Eip1559(tx.into()),
699 EthereumTypedTransaction::Eip4844(tx) => Self::Eip4844(tx.into_owned()),
700 EthereumTypedTransaction::Eip7702(tx) => Self::Eip7702(tx.into()),
701 }
702 }
703 }
704
705 impl<T: Serialize + Clone> SerializeAs<super::EthereumTypedTransaction<T>>
706 for EthereumTypedTransaction<'_, T>
707 {
708 fn serialize_as<S>(
709 source: &super::EthereumTypedTransaction<T>,
710 serializer: S,
711 ) -> Result<S::Ok, S::Error>
712 where
713 S: Serializer,
714 {
715 EthereumTypedTransaction::<'_, T>::from(source).serialize(serializer)
716 }
717 }
718
719 impl<'de, T: Deserialize<'de> + Clone> DeserializeAs<'de, super::EthereumTypedTransaction<T>>
720 for EthereumTypedTransaction<'de, T>
721 {
722 fn deserialize_as<D>(
723 deserializer: D,
724 ) -> Result<super::EthereumTypedTransaction<T>, D::Error>
725 where
726 D: Deserializer<'de>,
727 {
728 EthereumTypedTransaction::<'_, T>::deserialize(deserializer).map(Into::into)
729 }
730 }
731
732 #[cfg(test)]
733 mod tests {
734 use super::super::{serde_bincode_compat, EthereumTypedTransaction};
735 use crate::TxEip4844;
736 use arbitrary::Arbitrary;
737 use bincode::config;
738 use rand::Rng;
739 use serde::{Deserialize, Serialize};
740 use serde_with::serde_as;
741
742 #[test]
743 fn test_typed_tx_bincode_roundtrip() {
744 #[serde_as]
745 #[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
746 struct Data {
747 #[serde_as(as = "serde_bincode_compat::EthereumTypedTransaction<'_>")]
748 transaction: EthereumTypedTransaction<TxEip4844>,
749 }
750
751 let mut bytes = [0u8; 1024];
752 rand::thread_rng().fill(bytes.as_mut_slice());
753 let data = Data {
754 transaction: EthereumTypedTransaction::arbitrary(
755 &mut arbitrary::Unstructured::new(&bytes),
756 )
757 .unwrap(),
758 };
759
760 let encoded = bincode::serde::encode_to_vec(&data, config::legacy()).unwrap();
761 let (decoded, _) =
762 bincode::serde::decode_from_slice::<Data, _>(&encoded, config::legacy()).unwrap();
763 assert_eq!(decoded, data);
764 }
765 }
766}