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