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