1use crate::{
2 error::ValueError,
3 transaction::{
4 eip4844::{TxEip4844, TxEip4844Variant, TxEip4844WithSidecar},
5 RlpEcdsaEncodableTx,
6 },
7 EthereumTxEnvelope, SignableTransaction, Signed, 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 pub fn into_envelope(self, signature: Signature) -> EthereumTxEnvelope<Eip4844> {
162 match self {
163 Self::Legacy(tx) => EthereumTxEnvelope::Legacy(tx.into_signed(signature)),
164 Self::Eip2930(tx) => EthereumTxEnvelope::Eip2930(tx.into_signed(signature)),
165 Self::Eip1559(tx) => EthereumTxEnvelope::Eip1559(tx.into_signed(signature)),
166 Self::Eip4844(tx) => EthereumTxEnvelope::Eip4844(Signed::new_unhashed(tx, signature)),
167 Self::Eip7702(tx) => EthereumTxEnvelope::Eip7702(tx.into_signed(signature)),
168 }
169 }
170}
171
172impl<Eip4844: RlpEcdsaEncodableTx> EthereumTypedTransaction<Eip4844> {
173 #[doc(alias = "transaction_type")]
175 pub const fn tx_type(&self) -> TxType {
176 match self {
177 Self::Legacy(_) => TxType::Legacy,
178 Self::Eip2930(_) => TxType::Eip2930,
179 Self::Eip1559(_) => TxType::Eip1559,
180 Self::Eip4844(_) => TxType::Eip4844,
181 Self::Eip7702(_) => TxType::Eip7702,
182 }
183 }
184
185 pub const fn legacy(&self) -> Option<&TxLegacy> {
187 match self {
188 Self::Legacy(tx) => Some(tx),
189 _ => None,
190 }
191 }
192
193 pub const fn eip2930(&self) -> Option<&TxEip2930> {
195 match self {
196 Self::Eip2930(tx) => Some(tx),
197 _ => None,
198 }
199 }
200
201 pub const fn eip1559(&self) -> Option<&TxEip1559> {
203 match self {
204 Self::Eip1559(tx) => Some(tx),
205 _ => None,
206 }
207 }
208
209 pub const fn eip7702(&self) -> Option<&TxEip7702> {
211 match self {
212 Self::Eip7702(tx) => Some(tx),
213 _ => None,
214 }
215 }
216
217 pub fn try_into_legacy(self) -> Result<TxLegacy, ValueError<Self>> {
219 match self {
220 Self::Legacy(tx) => Ok(tx),
221 _ => Err(ValueError::new(self, "Expected legacy transaction")),
222 }
223 }
224
225 pub fn try_into_eip2930(self) -> Result<TxEip2930, ValueError<Self>> {
227 match self {
228 Self::Eip2930(tx) => Ok(tx),
229 _ => Err(ValueError::new(self, "Expected EIP-2930 transaction")),
230 }
231 }
232
233 pub fn try_into_eip4844(self) -> Result<Eip4844, ValueError<Self>> {
235 match self {
236 Self::Eip4844(tx) => Ok(tx),
237 _ => Err(ValueError::new(self, "Expected EIP-4844 transaction")),
238 }
239 }
240
241 pub fn try_into_eip7702(self) -> Result<TxEip7702, ValueError<Self>> {
243 match self {
244 Self::Eip7702(tx) => Ok(tx),
245 _ => Err(ValueError::new(self, "Expected EIP-7702 transaction")),
246 }
247 }
248
249 pub fn tx_hash(&self, signature: &Signature) -> TxHash {
251 match self {
252 Self::Legacy(tx) => tx.tx_hash(signature),
253 Self::Eip2930(tx) => tx.tx_hash(signature),
254 Self::Eip1559(tx) => tx.tx_hash(signature),
255 Self::Eip4844(tx) => tx.tx_hash(signature),
256 Self::Eip7702(tx) => tx.tx_hash(signature),
257 }
258 }
259}
260
261impl<Eip4844: Transaction> Transaction for EthereumTypedTransaction<Eip4844> {
262 #[inline]
263 fn chain_id(&self) -> Option<ChainId> {
264 match self {
265 Self::Legacy(tx) => tx.chain_id(),
266 Self::Eip2930(tx) => tx.chain_id(),
267 Self::Eip1559(tx) => tx.chain_id(),
268 Self::Eip4844(tx) => tx.chain_id(),
269 Self::Eip7702(tx) => tx.chain_id(),
270 }
271 }
272
273 #[inline]
274 fn nonce(&self) -> u64 {
275 match self {
276 Self::Legacy(tx) => tx.nonce(),
277 Self::Eip2930(tx) => tx.nonce(),
278 Self::Eip1559(tx) => tx.nonce(),
279 Self::Eip4844(tx) => tx.nonce(),
280 Self::Eip7702(tx) => tx.nonce(),
281 }
282 }
283
284 #[inline]
285 fn gas_limit(&self) -> u64 {
286 match self {
287 Self::Legacy(tx) => tx.gas_limit(),
288 Self::Eip2930(tx) => tx.gas_limit(),
289 Self::Eip1559(tx) => tx.gas_limit(),
290 Self::Eip4844(tx) => tx.gas_limit(),
291 Self::Eip7702(tx) => tx.gas_limit(),
292 }
293 }
294
295 #[inline]
296 fn gas_price(&self) -> Option<u128> {
297 match self {
298 Self::Legacy(tx) => tx.gas_price(),
299 Self::Eip2930(tx) => tx.gas_price(),
300 Self::Eip1559(tx) => tx.gas_price(),
301 Self::Eip4844(tx) => tx.gas_price(),
302 Self::Eip7702(tx) => tx.gas_price(),
303 }
304 }
305
306 #[inline]
307 fn max_fee_per_gas(&self) -> u128 {
308 match self {
309 Self::Legacy(tx) => tx.max_fee_per_gas(),
310 Self::Eip2930(tx) => tx.max_fee_per_gas(),
311 Self::Eip1559(tx) => tx.max_fee_per_gas(),
312 Self::Eip4844(tx) => tx.max_fee_per_gas(),
313 Self::Eip7702(tx) => tx.max_fee_per_gas(),
314 }
315 }
316
317 #[inline]
318 fn max_priority_fee_per_gas(&self) -> Option<u128> {
319 match self {
320 Self::Legacy(tx) => tx.max_priority_fee_per_gas(),
321 Self::Eip2930(tx) => tx.max_priority_fee_per_gas(),
322 Self::Eip1559(tx) => tx.max_priority_fee_per_gas(),
323 Self::Eip4844(tx) => tx.max_priority_fee_per_gas(),
324 Self::Eip7702(tx) => tx.max_priority_fee_per_gas(),
325 }
326 }
327
328 #[inline]
329 fn max_fee_per_blob_gas(&self) -> Option<u128> {
330 match self {
331 Self::Legacy(tx) => tx.max_fee_per_blob_gas(),
332 Self::Eip2930(tx) => tx.max_fee_per_blob_gas(),
333 Self::Eip1559(tx) => tx.max_fee_per_blob_gas(),
334 Self::Eip4844(tx) => tx.max_fee_per_blob_gas(),
335 Self::Eip7702(tx) => tx.max_fee_per_blob_gas(),
336 }
337 }
338
339 #[inline]
340 fn priority_fee_or_price(&self) -> u128 {
341 match self {
342 Self::Legacy(tx) => tx.priority_fee_or_price(),
343 Self::Eip2930(tx) => tx.priority_fee_or_price(),
344 Self::Eip1559(tx) => tx.priority_fee_or_price(),
345 Self::Eip4844(tx) => tx.priority_fee_or_price(),
346 Self::Eip7702(tx) => tx.priority_fee_or_price(),
347 }
348 }
349
350 fn effective_gas_price(&self, base_fee: Option<u64>) -> u128 {
351 match self {
352 Self::Legacy(tx) => tx.effective_gas_price(base_fee),
353 Self::Eip2930(tx) => tx.effective_gas_price(base_fee),
354 Self::Eip1559(tx) => tx.effective_gas_price(base_fee),
355 Self::Eip4844(tx) => tx.effective_gas_price(base_fee),
356 Self::Eip7702(tx) => tx.effective_gas_price(base_fee),
357 }
358 }
359
360 #[inline]
361 fn is_dynamic_fee(&self) -> bool {
362 match self {
363 Self::Legacy(tx) => tx.is_dynamic_fee(),
364 Self::Eip2930(tx) => tx.is_dynamic_fee(),
365 Self::Eip1559(tx) => tx.is_dynamic_fee(),
366 Self::Eip4844(tx) => tx.is_dynamic_fee(),
367 Self::Eip7702(tx) => tx.is_dynamic_fee(),
368 }
369 }
370
371 #[inline]
372 fn kind(&self) -> TxKind {
373 match self {
374 Self::Legacy(tx) => tx.kind(),
375 Self::Eip2930(tx) => tx.kind(),
376 Self::Eip1559(tx) => tx.kind(),
377 Self::Eip4844(tx) => tx.kind(),
378 Self::Eip7702(tx) => tx.kind(),
379 }
380 }
381
382 #[inline]
383 fn is_create(&self) -> bool {
384 match self {
385 Self::Legacy(tx) => tx.is_create(),
386 Self::Eip2930(tx) => tx.is_create(),
387 Self::Eip1559(tx) => tx.is_create(),
388 Self::Eip4844(tx) => tx.is_create(),
389 Self::Eip7702(tx) => tx.is_create(),
390 }
391 }
392
393 #[inline]
394 fn value(&self) -> U256 {
395 match self {
396 Self::Legacy(tx) => tx.value(),
397 Self::Eip2930(tx) => tx.value(),
398 Self::Eip1559(tx) => tx.value(),
399 Self::Eip4844(tx) => tx.value(),
400 Self::Eip7702(tx) => tx.value(),
401 }
402 }
403
404 #[inline]
405 fn input(&self) -> &Bytes {
406 match self {
407 Self::Legacy(tx) => tx.input(),
408 Self::Eip2930(tx) => tx.input(),
409 Self::Eip1559(tx) => tx.input(),
410 Self::Eip4844(tx) => tx.input(),
411 Self::Eip7702(tx) => tx.input(),
412 }
413 }
414
415 #[inline]
416 fn access_list(&self) -> Option<&AccessList> {
417 match self {
418 Self::Legacy(tx) => tx.access_list(),
419 Self::Eip2930(tx) => tx.access_list(),
420 Self::Eip1559(tx) => tx.access_list(),
421 Self::Eip4844(tx) => tx.access_list(),
422 Self::Eip7702(tx) => tx.access_list(),
423 }
424 }
425
426 #[inline]
427 fn blob_versioned_hashes(&self) -> Option<&[B256]> {
428 match self {
429 Self::Legacy(tx) => tx.blob_versioned_hashes(),
430 Self::Eip2930(tx) => tx.blob_versioned_hashes(),
431 Self::Eip1559(tx) => tx.blob_versioned_hashes(),
432 Self::Eip4844(tx) => tx.blob_versioned_hashes(),
433 Self::Eip7702(tx) => tx.blob_versioned_hashes(),
434 }
435 }
436
437 #[inline]
438 fn authorization_list(&self) -> Option<&[SignedAuthorization]> {
439 match self {
440 Self::Legacy(tx) => tx.authorization_list(),
441 Self::Eip2930(tx) => tx.authorization_list(),
442 Self::Eip1559(tx) => tx.authorization_list(),
443 Self::Eip4844(tx) => tx.authorization_list(),
444 Self::Eip7702(tx) => tx.authorization_list(),
445 }
446 }
447}
448
449impl<Eip4844: Typed2718> Typed2718 for EthereumTypedTransaction<Eip4844> {
450 fn ty(&self) -> u8 {
451 match self {
452 Self::Legacy(tx) => tx.ty(),
453 Self::Eip2930(tx) => tx.ty(),
454 Self::Eip1559(tx) => tx.ty(),
455 Self::Eip4844(tx) => tx.ty(),
456 Self::Eip7702(tx) => tx.ty(),
457 }
458 }
459}
460
461impl<T> IsTyped2718 for EthereumTypedTransaction<T> {
462 fn is_type(type_id: u8) -> bool {
463 <TxType as IsTyped2718>::is_type(type_id)
464 }
465}
466
467impl<Eip4844: RlpEcdsaEncodableTx + Typed2718> RlpEcdsaEncodableTx
468 for EthereumTypedTransaction<Eip4844>
469{
470 fn rlp_encoded_fields_length(&self) -> usize {
471 match self {
472 Self::Legacy(tx) => tx.rlp_encoded_fields_length(),
473 Self::Eip2930(tx) => tx.rlp_encoded_fields_length(),
474 Self::Eip1559(tx) => tx.rlp_encoded_fields_length(),
475 Self::Eip4844(tx) => tx.rlp_encoded_fields_length(),
476 Self::Eip7702(tx) => tx.rlp_encoded_fields_length(),
477 }
478 }
479
480 fn rlp_encode_fields(&self, out: &mut dyn alloy_rlp::BufMut) {
481 match self {
482 Self::Legacy(tx) => tx.rlp_encode_fields(out),
483 Self::Eip2930(tx) => tx.rlp_encode_fields(out),
484 Self::Eip1559(tx) => tx.rlp_encode_fields(out),
485 Self::Eip4844(tx) => tx.rlp_encode_fields(out),
486 Self::Eip7702(tx) => tx.rlp_encode_fields(out),
487 }
488 }
489
490 fn eip2718_encode_with_type(&self, signature: &Signature, _ty: u8, out: &mut dyn BufMut) {
491 match self {
492 Self::Legacy(tx) => tx.eip2718_encode_with_type(signature, tx.ty(), out),
493 Self::Eip2930(tx) => tx.eip2718_encode_with_type(signature, tx.ty(), out),
494 Self::Eip1559(tx) => tx.eip2718_encode_with_type(signature, tx.ty(), out),
495 Self::Eip4844(tx) => tx.eip2718_encode_with_type(signature, tx.ty(), out),
496 Self::Eip7702(tx) => tx.eip2718_encode_with_type(signature, tx.ty(), out),
497 }
498 }
499
500 fn eip2718_encode(&self, signature: &Signature, out: &mut dyn BufMut) {
501 match self {
502 Self::Legacy(tx) => tx.eip2718_encode(signature, out),
503 Self::Eip2930(tx) => tx.eip2718_encode(signature, out),
504 Self::Eip1559(tx) => tx.eip2718_encode(signature, out),
505 Self::Eip4844(tx) => tx.eip2718_encode(signature, out),
506 Self::Eip7702(tx) => tx.eip2718_encode(signature, out),
507 }
508 }
509
510 fn network_encode_with_type(&self, signature: &Signature, _ty: u8, out: &mut dyn BufMut) {
511 match self {
512 Self::Legacy(tx) => tx.network_encode_with_type(signature, tx.ty(), out),
513 Self::Eip2930(tx) => tx.network_encode_with_type(signature, tx.ty(), out),
514 Self::Eip1559(tx) => tx.network_encode_with_type(signature, tx.ty(), out),
515 Self::Eip4844(tx) => tx.network_encode_with_type(signature, tx.ty(), out),
516 Self::Eip7702(tx) => tx.network_encode_with_type(signature, tx.ty(), out),
517 }
518 }
519
520 fn network_encode(&self, signature: &Signature, out: &mut dyn BufMut) {
521 match self {
522 Self::Legacy(tx) => tx.network_encode(signature, out),
523 Self::Eip2930(tx) => tx.network_encode(signature, out),
524 Self::Eip1559(tx) => tx.network_encode(signature, out),
525 Self::Eip4844(tx) => tx.network_encode(signature, out),
526 Self::Eip7702(tx) => tx.network_encode(signature, out),
527 }
528 }
529
530 fn tx_hash_with_type(&self, signature: &Signature, _ty: u8) -> TxHash {
531 match self {
532 Self::Legacy(tx) => tx.tx_hash_with_type(signature, tx.ty()),
533 Self::Eip2930(tx) => tx.tx_hash_with_type(signature, tx.ty()),
534 Self::Eip1559(tx) => tx.tx_hash_with_type(signature, tx.ty()),
535 Self::Eip4844(tx) => tx.tx_hash_with_type(signature, tx.ty()),
536 Self::Eip7702(tx) => tx.tx_hash_with_type(signature, tx.ty()),
537 }
538 }
539
540 fn tx_hash(&self, signature: &Signature) -> TxHash {
541 match self {
542 Self::Legacy(tx) => tx.tx_hash(signature),
543 Self::Eip2930(tx) => tx.tx_hash(signature),
544 Self::Eip1559(tx) => tx.tx_hash(signature),
545 Self::Eip4844(tx) => tx.tx_hash(signature),
546 Self::Eip7702(tx) => tx.tx_hash(signature),
547 }
548 }
549}
550
551impl<Eip4844: SignableTransaction<Signature>> SignableTransaction<Signature>
552 for EthereumTypedTransaction<Eip4844>
553{
554 fn set_chain_id(&mut self, chain_id: ChainId) {
555 match self {
556 Self::Legacy(tx) => tx.set_chain_id(chain_id),
557 Self::Eip2930(tx) => tx.set_chain_id(chain_id),
558 Self::Eip1559(tx) => tx.set_chain_id(chain_id),
559 Self::Eip4844(tx) => tx.set_chain_id(chain_id),
560 Self::Eip7702(tx) => tx.set_chain_id(chain_id),
561 }
562 }
563
564 fn encode_for_signing(&self, out: &mut dyn BufMut) {
565 match self {
566 Self::Legacy(tx) => tx.encode_for_signing(out),
567 Self::Eip2930(tx) => tx.encode_for_signing(out),
568 Self::Eip1559(tx) => tx.encode_for_signing(out),
569 Self::Eip4844(tx) => tx.encode_for_signing(out),
570 Self::Eip7702(tx) => tx.encode_for_signing(out),
571 }
572 }
573
574 fn payload_len_for_signature(&self) -> usize {
575 match self {
576 Self::Legacy(tx) => tx.payload_len_for_signature(),
577 Self::Eip2930(tx) => tx.payload_len_for_signature(),
578 Self::Eip1559(tx) => tx.payload_len_for_signature(),
579 Self::Eip4844(tx) => tx.payload_len_for_signature(),
580 Self::Eip7702(tx) => tx.payload_len_for_signature(),
581 }
582 }
583}
584
585#[cfg(feature = "serde")]
586impl<Eip4844, T: From<EthereumTypedTransaction<Eip4844>>> From<EthereumTypedTransaction<Eip4844>>
587 for alloy_serde::WithOtherFields<T>
588{
589 fn from(value: EthereumTypedTransaction<Eip4844>) -> Self {
590 Self::new(value.into())
591 }
592}
593
594#[cfg(feature = "serde")]
595impl<Eip4844, T> From<EthereumTxEnvelope<Eip4844>> for alloy_serde::WithOtherFields<T>
596where
597 T: From<EthereumTxEnvelope<Eip4844>>,
598{
599 fn from(value: EthereumTxEnvelope<Eip4844>) -> Self {
600 Self::new(value.into())
601 }
602}
603
604#[cfg(feature = "serde")]
605mod serde_from {
606 use crate::{EthereumTypedTransaction, TxEip1559, TxEip2930, TxEip7702, TxLegacy};
618
619 #[derive(Debug, serde::Deserialize)]
620 #[serde(untagged)]
621 pub(crate) enum MaybeTaggedTypedTransaction<Eip4844> {
622 Tagged(TaggedTypedTransaction<Eip4844>),
623 Untagged {
624 #[serde(default, rename = "type", deserialize_with = "alloy_serde::reject_if_some")]
625 _ty: Option<()>,
626 #[serde(flatten)]
627 tx: TxLegacy,
628 },
629 }
630
631 #[derive(Debug, serde::Serialize, serde::Deserialize)]
632 #[serde(tag = "type")]
633 pub(crate) enum TaggedTypedTransaction<Eip4844> {
634 #[serde(rename = "0x00", alias = "0x0")]
636 Legacy(TxLegacy),
637 #[serde(rename = "0x01", alias = "0x1")]
639 Eip2930(TxEip2930),
640 #[serde(rename = "0x02", alias = "0x2")]
642 Eip1559(TxEip1559),
643 #[serde(rename = "0x03", alias = "0x3")]
645 Eip4844(Eip4844),
646 #[serde(rename = "0x04", alias = "0x4")]
648 Eip7702(TxEip7702),
649 }
650
651 impl<Eip4844> From<MaybeTaggedTypedTransaction<Eip4844>> for EthereumTypedTransaction<Eip4844> {
652 fn from(value: MaybeTaggedTypedTransaction<Eip4844>) -> Self {
653 match value {
654 MaybeTaggedTypedTransaction::Tagged(tagged) => tagged.into(),
655 MaybeTaggedTypedTransaction::Untagged { tx, .. } => Self::Legacy(tx),
656 }
657 }
658 }
659
660 impl<Eip4844> From<TaggedTypedTransaction<Eip4844>> for EthereumTypedTransaction<Eip4844> {
661 fn from(value: TaggedTypedTransaction<Eip4844>) -> Self {
662 match value {
663 TaggedTypedTransaction::Legacy(signed) => Self::Legacy(signed),
664 TaggedTypedTransaction::Eip2930(signed) => Self::Eip2930(signed),
665 TaggedTypedTransaction::Eip1559(signed) => Self::Eip1559(signed),
666 TaggedTypedTransaction::Eip4844(signed) => Self::Eip4844(signed),
667 TaggedTypedTransaction::Eip7702(signed) => Self::Eip7702(signed),
668 }
669 }
670 }
671
672 impl<Eip4844> From<EthereumTypedTransaction<Eip4844>> for TaggedTypedTransaction<Eip4844> {
673 fn from(value: EthereumTypedTransaction<Eip4844>) -> Self {
674 match value {
675 EthereumTypedTransaction::Legacy(signed) => Self::Legacy(signed),
676 EthereumTypedTransaction::Eip2930(signed) => Self::Eip2930(signed),
677 EthereumTypedTransaction::Eip1559(signed) => Self::Eip1559(signed),
678 EthereumTypedTransaction::Eip4844(signed) => Self::Eip4844(signed),
679 EthereumTypedTransaction::Eip7702(signed) => Self::Eip7702(signed),
680 }
681 }
682 }
683}
684
685#[cfg(all(feature = "serde", feature = "serde-bincode-compat"))]
687pub(crate) mod serde_bincode_compat {
688 use alloc::borrow::Cow;
689 use serde::{Deserialize, Deserializer, Serialize, Serializer};
690 use serde_with::{DeserializeAs, SerializeAs};
691
692 #[derive(Debug, Serialize, Deserialize)]
708 pub enum EthereumTypedTransaction<'a, Eip4844: Clone = crate::transaction::TxEip4844> {
709 Legacy(crate::serde_bincode_compat::transaction::TxLegacy<'a>),
711 Eip2930(crate::serde_bincode_compat::transaction::TxEip2930<'a>),
713 Eip1559(crate::serde_bincode_compat::transaction::TxEip1559<'a>),
715 Eip4844(Cow<'a, Eip4844>),
719 Eip7702(crate::serde_bincode_compat::transaction::TxEip7702<'a>),
721 }
722
723 impl<'a, T: Clone> From<&'a super::EthereumTypedTransaction<T>>
724 for EthereumTypedTransaction<'a, T>
725 {
726 fn from(value: &'a super::EthereumTypedTransaction<T>) -> Self {
727 match value {
728 super::EthereumTypedTransaction::Legacy(tx) => Self::Legacy(tx.into()),
729 super::EthereumTypedTransaction::Eip2930(tx) => Self::Eip2930(tx.into()),
730 super::EthereumTypedTransaction::Eip1559(tx) => Self::Eip1559(tx.into()),
731 super::EthereumTypedTransaction::Eip4844(tx) => Self::Eip4844(Cow::Borrowed(tx)),
732 super::EthereumTypedTransaction::Eip7702(tx) => Self::Eip7702(tx.into()),
733 }
734 }
735 }
736
737 impl<'a, T: Clone> From<EthereumTypedTransaction<'a, T>> for super::EthereumTypedTransaction<T> {
738 fn from(value: EthereumTypedTransaction<'a, T>) -> Self {
739 match value {
740 EthereumTypedTransaction::Legacy(tx) => Self::Legacy(tx.into()),
741 EthereumTypedTransaction::Eip2930(tx) => Self::Eip2930(tx.into()),
742 EthereumTypedTransaction::Eip1559(tx) => Self::Eip1559(tx.into()),
743 EthereumTypedTransaction::Eip4844(tx) => Self::Eip4844(tx.into_owned()),
744 EthereumTypedTransaction::Eip7702(tx) => Self::Eip7702(tx.into()),
745 }
746 }
747 }
748
749 impl<T: Serialize + Clone> SerializeAs<super::EthereumTypedTransaction<T>>
750 for EthereumTypedTransaction<'_, T>
751 {
752 fn serialize_as<S>(
753 source: &super::EthereumTypedTransaction<T>,
754 serializer: S,
755 ) -> Result<S::Ok, S::Error>
756 where
757 S: Serializer,
758 {
759 EthereumTypedTransaction::<'_, T>::from(source).serialize(serializer)
760 }
761 }
762
763 impl<'de, T: Deserialize<'de> + Clone> DeserializeAs<'de, super::EthereumTypedTransaction<T>>
764 for EthereumTypedTransaction<'de, T>
765 {
766 fn deserialize_as<D>(
767 deserializer: D,
768 ) -> Result<super::EthereumTypedTransaction<T>, D::Error>
769 where
770 D: Deserializer<'de>,
771 {
772 EthereumTypedTransaction::<'_, T>::deserialize(deserializer).map(Into::into)
773 }
774 }
775
776 #[cfg(test)]
777 mod tests {
778 use super::super::{serde_bincode_compat, EthereumTypedTransaction};
779 use crate::TxEip4844;
780 use arbitrary::Arbitrary;
781 use bincode::config;
782 use rand::Rng;
783 use serde::{Deserialize, Serialize};
784 use serde_with::serde_as;
785
786 #[test]
787 fn test_typed_tx_bincode_roundtrip() {
788 #[serde_as]
789 #[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
790 struct Data {
791 #[serde_as(as = "serde_bincode_compat::EthereumTypedTransaction<'_>")]
792 transaction: EthereumTypedTransaction<TxEip4844>,
793 }
794
795 let mut bytes = [0u8; 1024];
796 rand::thread_rng().fill(bytes.as_mut_slice());
797 let data = Data {
798 transaction: EthereumTypedTransaction::arbitrary(
799 &mut arbitrary::Unstructured::new(&bytes),
800 )
801 .unwrap(),
802 };
803
804 let encoded = bincode::serde::encode_to_vec(&data, config::legacy()).unwrap();
805 let (decoded, _) =
806 bincode::serde::decode_from_slice::<Data, _>(&encoded, config::legacy()).unwrap();
807 assert_eq!(decoded, data);
808 }
809 }
810}