alloy_consensus/transaction/
typed.rs1use crate::{
2 transaction::{
3 eip4844::{TxEip4844, TxEip4844Variant, TxEip4844WithSidecar},
4 RlpEcdsaEncodableTx,
5 },
6 SignableTransaction, Transaction, TxEip1559, TxEip2930, TxEip7702, TxEnvelope, TxLegacy,
7 TxType,
8};
9use alloy_eips::{eip2930::AccessList, eip7702::SignedAuthorization, Typed2718};
10use alloy_primitives::{
11 bytes::BufMut, Bytes, ChainId, PrimitiveSignature as Signature, TxHash, TxKind, B256, U256,
12};
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 From<TxEnvelope> for TypedTransaction {
107 fn from(envelope: TxEnvelope) -> Self {
108 match envelope {
109 TxEnvelope::Legacy(tx) => Self::Legacy(tx.strip_signature()),
110 TxEnvelope::Eip2930(tx) => Self::Eip2930(tx.strip_signature()),
111 TxEnvelope::Eip1559(tx) => Self::Eip1559(tx.strip_signature()),
112 TxEnvelope::Eip4844(tx) => Self::Eip4844(tx.strip_signature()),
113 TxEnvelope::Eip7702(tx) => Self::Eip7702(tx.strip_signature()),
114 }
115 }
116}
117
118impl<Eip4844: RlpEcdsaEncodableTx> EthereumTypedTransaction<Eip4844> {
119 #[doc(alias = "transaction_type")]
121 pub const fn tx_type(&self) -> TxType {
122 match self {
123 Self::Legacy(_) => TxType::Legacy,
124 Self::Eip2930(_) => TxType::Eip2930,
125 Self::Eip1559(_) => TxType::Eip1559,
126 Self::Eip4844(_) => TxType::Eip4844,
127 Self::Eip7702(_) => TxType::Eip7702,
128 }
129 }
130
131 pub const fn legacy(&self) -> Option<&TxLegacy> {
133 match self {
134 Self::Legacy(tx) => Some(tx),
135 _ => None,
136 }
137 }
138
139 pub const fn eip2930(&self) -> Option<&TxEip2930> {
141 match self {
142 Self::Eip2930(tx) => Some(tx),
143 _ => None,
144 }
145 }
146
147 pub const fn eip1559(&self) -> Option<&TxEip1559> {
149 match self {
150 Self::Eip1559(tx) => Some(tx),
151 _ => None,
152 }
153 }
154
155 pub const fn eip7702(&self) -> Option<&TxEip7702> {
157 match self {
158 Self::Eip7702(tx) => Some(tx),
159 _ => None,
160 }
161 }
162
163 pub fn tx_hash(&self, signature: &Signature) -> TxHash {
165 match self {
166 Self::Legacy(tx) => tx.tx_hash(signature),
167 Self::Eip2930(tx) => tx.tx_hash(signature),
168 Self::Eip1559(tx) => tx.tx_hash(signature),
169 Self::Eip4844(tx) => tx.tx_hash(signature),
170 Self::Eip7702(tx) => tx.tx_hash(signature),
171 }
172 }
173}
174
175impl<Eip4844: Transaction> Transaction for EthereumTypedTransaction<Eip4844> {
176 #[inline]
177 fn chain_id(&self) -> Option<ChainId> {
178 match self {
179 Self::Legacy(tx) => tx.chain_id(),
180 Self::Eip2930(tx) => tx.chain_id(),
181 Self::Eip1559(tx) => tx.chain_id(),
182 Self::Eip4844(tx) => tx.chain_id(),
183 Self::Eip7702(tx) => tx.chain_id(),
184 }
185 }
186
187 #[inline]
188 fn nonce(&self) -> u64 {
189 match self {
190 Self::Legacy(tx) => tx.nonce(),
191 Self::Eip2930(tx) => tx.nonce(),
192 Self::Eip1559(tx) => tx.nonce(),
193 Self::Eip4844(tx) => tx.nonce(),
194 Self::Eip7702(tx) => tx.nonce(),
195 }
196 }
197
198 #[inline]
199 fn gas_limit(&self) -> u64 {
200 match self {
201 Self::Legacy(tx) => tx.gas_limit(),
202 Self::Eip2930(tx) => tx.gas_limit(),
203 Self::Eip1559(tx) => tx.gas_limit(),
204 Self::Eip4844(tx) => tx.gas_limit(),
205 Self::Eip7702(tx) => tx.gas_limit(),
206 }
207 }
208
209 #[inline]
210 fn gas_price(&self) -> Option<u128> {
211 match self {
212 Self::Legacy(tx) => tx.gas_price(),
213 Self::Eip2930(tx) => tx.gas_price(),
214 Self::Eip1559(tx) => tx.gas_price(),
215 Self::Eip4844(tx) => tx.gas_price(),
216 Self::Eip7702(tx) => tx.gas_price(),
217 }
218 }
219
220 #[inline]
221 fn max_fee_per_gas(&self) -> u128 {
222 match self {
223 Self::Legacy(tx) => tx.max_fee_per_gas(),
224 Self::Eip2930(tx) => tx.max_fee_per_gas(),
225 Self::Eip1559(tx) => tx.max_fee_per_gas(),
226 Self::Eip4844(tx) => tx.max_fee_per_gas(),
227 Self::Eip7702(tx) => tx.max_fee_per_gas(),
228 }
229 }
230
231 #[inline]
232 fn max_priority_fee_per_gas(&self) -> Option<u128> {
233 match self {
234 Self::Legacy(tx) => tx.max_priority_fee_per_gas(),
235 Self::Eip2930(tx) => tx.max_priority_fee_per_gas(),
236 Self::Eip1559(tx) => tx.max_priority_fee_per_gas(),
237 Self::Eip4844(tx) => tx.max_priority_fee_per_gas(),
238 Self::Eip7702(tx) => tx.max_priority_fee_per_gas(),
239 }
240 }
241
242 #[inline]
243 fn max_fee_per_blob_gas(&self) -> Option<u128> {
244 match self {
245 Self::Legacy(tx) => tx.max_fee_per_blob_gas(),
246 Self::Eip2930(tx) => tx.max_fee_per_blob_gas(),
247 Self::Eip1559(tx) => tx.max_fee_per_blob_gas(),
248 Self::Eip4844(tx) => tx.max_fee_per_blob_gas(),
249 Self::Eip7702(tx) => tx.max_fee_per_blob_gas(),
250 }
251 }
252
253 #[inline]
254 fn priority_fee_or_price(&self) -> u128 {
255 match self {
256 Self::Legacy(tx) => tx.priority_fee_or_price(),
257 Self::Eip2930(tx) => tx.priority_fee_or_price(),
258 Self::Eip1559(tx) => tx.priority_fee_or_price(),
259 Self::Eip4844(tx) => tx.priority_fee_or_price(),
260 Self::Eip7702(tx) => tx.priority_fee_or_price(),
261 }
262 }
263
264 fn effective_gas_price(&self, base_fee: Option<u64>) -> u128 {
265 match self {
266 Self::Legacy(tx) => tx.effective_gas_price(base_fee),
267 Self::Eip2930(tx) => tx.effective_gas_price(base_fee),
268 Self::Eip1559(tx) => tx.effective_gas_price(base_fee),
269 Self::Eip4844(tx) => tx.effective_gas_price(base_fee),
270 Self::Eip7702(tx) => tx.effective_gas_price(base_fee),
271 }
272 }
273
274 #[inline]
275 fn is_dynamic_fee(&self) -> bool {
276 match self {
277 Self::Legacy(tx) => tx.is_dynamic_fee(),
278 Self::Eip2930(tx) => tx.is_dynamic_fee(),
279 Self::Eip1559(tx) => tx.is_dynamic_fee(),
280 Self::Eip4844(tx) => tx.is_dynamic_fee(),
281 Self::Eip7702(tx) => tx.is_dynamic_fee(),
282 }
283 }
284
285 #[inline]
286 fn kind(&self) -> TxKind {
287 match self {
288 Self::Legacy(tx) => tx.kind(),
289 Self::Eip2930(tx) => tx.kind(),
290 Self::Eip1559(tx) => tx.kind(),
291 Self::Eip4844(tx) => tx.kind(),
292 Self::Eip7702(tx) => tx.kind(),
293 }
294 }
295
296 #[inline]
297 fn is_create(&self) -> bool {
298 match self {
299 Self::Legacy(tx) => tx.is_create(),
300 Self::Eip2930(tx) => tx.is_create(),
301 Self::Eip1559(tx) => tx.is_create(),
302 Self::Eip4844(tx) => tx.is_create(),
303 Self::Eip7702(tx) => tx.is_create(),
304 }
305 }
306
307 #[inline]
308 fn value(&self) -> U256 {
309 match self {
310 Self::Legacy(tx) => tx.value(),
311 Self::Eip2930(tx) => tx.value(),
312 Self::Eip1559(tx) => tx.value(),
313 Self::Eip4844(tx) => tx.value(),
314 Self::Eip7702(tx) => tx.value(),
315 }
316 }
317
318 #[inline]
319 fn input(&self) -> &Bytes {
320 match self {
321 Self::Legacy(tx) => tx.input(),
322 Self::Eip2930(tx) => tx.input(),
323 Self::Eip1559(tx) => tx.input(),
324 Self::Eip4844(tx) => tx.input(),
325 Self::Eip7702(tx) => tx.input(),
326 }
327 }
328
329 #[inline]
330 fn access_list(&self) -> Option<&AccessList> {
331 match self {
332 Self::Legacy(tx) => tx.access_list(),
333 Self::Eip2930(tx) => tx.access_list(),
334 Self::Eip1559(tx) => tx.access_list(),
335 Self::Eip4844(tx) => tx.access_list(),
336 Self::Eip7702(tx) => tx.access_list(),
337 }
338 }
339
340 #[inline]
341 fn blob_versioned_hashes(&self) -> Option<&[B256]> {
342 match self {
343 Self::Legacy(tx) => tx.blob_versioned_hashes(),
344 Self::Eip2930(tx) => tx.blob_versioned_hashes(),
345 Self::Eip1559(tx) => tx.blob_versioned_hashes(),
346 Self::Eip4844(tx) => tx.blob_versioned_hashes(),
347 Self::Eip7702(tx) => tx.blob_versioned_hashes(),
348 }
349 }
350
351 #[inline]
352 fn authorization_list(&self) -> Option<&[SignedAuthorization]> {
353 match self {
354 Self::Legacy(tx) => tx.authorization_list(),
355 Self::Eip2930(tx) => tx.authorization_list(),
356 Self::Eip1559(tx) => tx.authorization_list(),
357 Self::Eip4844(tx) => tx.authorization_list(),
358 Self::Eip7702(tx) => tx.authorization_list(),
359 }
360 }
361}
362
363impl<Eip4844: Typed2718> Typed2718 for EthereumTypedTransaction<Eip4844> {
364 fn ty(&self) -> u8 {
365 match self {
366 Self::Legacy(tx) => tx.ty(),
367 Self::Eip2930(tx) => tx.ty(),
368 Self::Eip1559(tx) => tx.ty(),
369 Self::Eip4844(tx) => tx.ty(),
370 Self::Eip7702(tx) => tx.ty(),
371 }
372 }
373}
374
375impl<Eip4844: RlpEcdsaEncodableTx + Typed2718> RlpEcdsaEncodableTx
376 for EthereumTypedTransaction<Eip4844>
377{
378 const DEFAULT_TX_TYPE: u8 = 0;
379
380 fn rlp_encoded_fields_length(&self) -> usize {
381 match self {
382 Self::Legacy(tx) => tx.rlp_encoded_fields_length(),
383 Self::Eip2930(tx) => tx.rlp_encoded_fields_length(),
384 Self::Eip1559(tx) => tx.rlp_encoded_fields_length(),
385 Self::Eip4844(tx) => tx.rlp_encoded_fields_length(),
386 Self::Eip7702(tx) => tx.rlp_encoded_fields_length(),
387 }
388 }
389
390 fn rlp_encode_fields(&self, out: &mut dyn alloy_rlp::BufMut) {
391 match self {
392 Self::Legacy(tx) => tx.rlp_encode_fields(out),
393 Self::Eip2930(tx) => tx.rlp_encode_fields(out),
394 Self::Eip1559(tx) => tx.rlp_encode_fields(out),
395 Self::Eip4844(tx) => tx.rlp_encode_fields(out),
396 Self::Eip7702(tx) => tx.rlp_encode_fields(out),
397 }
398 }
399
400 fn eip2718_encode_with_type(&self, signature: &Signature, _ty: u8, out: &mut dyn BufMut) {
401 match self {
402 Self::Legacy(tx) => tx.eip2718_encode_with_type(signature, tx.ty(), out),
403 Self::Eip2930(tx) => tx.eip2718_encode_with_type(signature, tx.ty(), out),
404 Self::Eip1559(tx) => tx.eip2718_encode_with_type(signature, tx.ty(), out),
405 Self::Eip4844(tx) => tx.eip2718_encode_with_type(signature, tx.ty(), out),
406 Self::Eip7702(tx) => tx.eip2718_encode_with_type(signature, tx.ty(), out),
407 }
408 }
409
410 fn eip2718_encode(&self, signature: &Signature, out: &mut dyn BufMut) {
411 match self {
412 Self::Legacy(tx) => tx.eip2718_encode(signature, out),
413 Self::Eip2930(tx) => tx.eip2718_encode(signature, out),
414 Self::Eip1559(tx) => tx.eip2718_encode(signature, out),
415 Self::Eip4844(tx) => tx.eip2718_encode(signature, out),
416 Self::Eip7702(tx) => tx.eip2718_encode(signature, out),
417 }
418 }
419
420 fn network_encode_with_type(&self, signature: &Signature, _ty: u8, out: &mut dyn BufMut) {
421 match self {
422 Self::Legacy(tx) => tx.network_encode_with_type(signature, tx.ty(), out),
423 Self::Eip2930(tx) => tx.network_encode_with_type(signature, tx.ty(), out),
424 Self::Eip1559(tx) => tx.network_encode_with_type(signature, tx.ty(), out),
425 Self::Eip4844(tx) => tx.network_encode_with_type(signature, tx.ty(), out),
426 Self::Eip7702(tx) => tx.network_encode_with_type(signature, tx.ty(), out),
427 }
428 }
429
430 fn network_encode(&self, signature: &Signature, out: &mut dyn BufMut) {
431 match self {
432 Self::Legacy(tx) => tx.network_encode(signature, out),
433 Self::Eip2930(tx) => tx.network_encode(signature, out),
434 Self::Eip1559(tx) => tx.network_encode(signature, out),
435 Self::Eip4844(tx) => tx.network_encode(signature, out),
436 Self::Eip7702(tx) => tx.network_encode(signature, out),
437 }
438 }
439
440 fn tx_hash_with_type(&self, signature: &Signature, _ty: u8) -> TxHash {
441 match self {
442 Self::Legacy(tx) => tx.tx_hash_with_type(signature, tx.ty()),
443 Self::Eip2930(tx) => tx.tx_hash_with_type(signature, tx.ty()),
444 Self::Eip1559(tx) => tx.tx_hash_with_type(signature, tx.ty()),
445 Self::Eip4844(tx) => tx.tx_hash_with_type(signature, tx.ty()),
446 Self::Eip7702(tx) => tx.tx_hash_with_type(signature, tx.ty()),
447 }
448 }
449
450 fn tx_hash(&self, signature: &Signature) -> TxHash {
451 match self {
452 Self::Legacy(tx) => tx.tx_hash(signature),
453 Self::Eip2930(tx) => tx.tx_hash(signature),
454 Self::Eip1559(tx) => tx.tx_hash(signature),
455 Self::Eip4844(tx) => tx.tx_hash(signature),
456 Self::Eip7702(tx) => tx.tx_hash(signature),
457 }
458 }
459}
460
461impl<Eip4844: SignableTransaction<Signature>> SignableTransaction<Signature>
462 for EthereumTypedTransaction<Eip4844>
463{
464 fn set_chain_id(&mut self, chain_id: ChainId) {
465 match self {
466 Self::Legacy(tx) => tx.set_chain_id(chain_id),
467 Self::Eip2930(tx) => tx.set_chain_id(chain_id),
468 Self::Eip1559(tx) => tx.set_chain_id(chain_id),
469 Self::Eip4844(tx) => tx.set_chain_id(chain_id),
470 Self::Eip7702(tx) => tx.set_chain_id(chain_id),
471 }
472 }
473
474 fn encode_for_signing(&self, out: &mut dyn BufMut) {
475 match self {
476 Self::Legacy(tx) => tx.encode_for_signing(out),
477 Self::Eip2930(tx) => tx.encode_for_signing(out),
478 Self::Eip1559(tx) => tx.encode_for_signing(out),
479 Self::Eip4844(tx) => tx.encode_for_signing(out),
480 Self::Eip7702(tx) => tx.encode_for_signing(out),
481 }
482 }
483
484 fn payload_len_for_signature(&self) -> usize {
485 match self {
486 Self::Legacy(tx) => tx.payload_len_for_signature(),
487 Self::Eip2930(tx) => tx.payload_len_for_signature(),
488 Self::Eip1559(tx) => tx.payload_len_for_signature(),
489 Self::Eip4844(tx) => tx.payload_len_for_signature(),
490 Self::Eip7702(tx) => tx.payload_len_for_signature(),
491 }
492 }
493}
494
495#[cfg(feature = "serde")]
496impl<Eip4844, T: From<EthereumTypedTransaction<Eip4844>>> From<EthereumTypedTransaction<Eip4844>>
497 for alloy_serde::WithOtherFields<T>
498{
499 fn from(value: EthereumTypedTransaction<Eip4844>) -> Self {
500 Self::new(value.into())
501 }
502}
503
504#[cfg(feature = "serde")]
505impl<T: From<TxEnvelope>> From<TxEnvelope> for alloy_serde::WithOtherFields<T> {
506 fn from(value: TxEnvelope) -> Self {
507 Self::new(value.into())
508 }
509}
510
511#[cfg(feature = "serde")]
512mod serde_from {
513 use crate::{EthereumTypedTransaction, TxEip1559, TxEip2930, TxEip7702, TxLegacy};
525
526 #[derive(Debug, serde::Deserialize)]
527 #[serde(untagged)]
528 pub(crate) enum MaybeTaggedTypedTransaction<Eip4844> {
529 Tagged(TaggedTypedTransaction<Eip4844>),
530 Untagged {
531 #[serde(default, rename = "type", deserialize_with = "alloy_serde::reject_if_some")]
532 _ty: Option<()>,
533 #[serde(flatten)]
534 tx: TxLegacy,
535 },
536 }
537
538 #[derive(Debug, serde::Serialize, serde::Deserialize)]
539 #[serde(tag = "type")]
540 pub(crate) enum TaggedTypedTransaction<Eip4844> {
541 #[serde(rename = "0x00", alias = "0x0")]
543 Legacy(TxLegacy),
544 #[serde(rename = "0x01", alias = "0x1")]
546 Eip2930(TxEip2930),
547 #[serde(rename = "0x02", alias = "0x2")]
549 Eip1559(TxEip1559),
550 #[serde(rename = "0x03", alias = "0x3")]
552 Eip4844(Eip4844),
553 #[serde(rename = "0x04", alias = "0x4")]
555 Eip7702(TxEip7702),
556 }
557
558 impl<Eip4844> From<MaybeTaggedTypedTransaction<Eip4844>> for EthereumTypedTransaction<Eip4844> {
559 fn from(value: MaybeTaggedTypedTransaction<Eip4844>) -> Self {
560 match value {
561 MaybeTaggedTypedTransaction::Tagged(tagged) => tagged.into(),
562 MaybeTaggedTypedTransaction::Untagged { tx, .. } => Self::Legacy(tx),
563 }
564 }
565 }
566
567 impl<Eip4844> From<TaggedTypedTransaction<Eip4844>> for EthereumTypedTransaction<Eip4844> {
568 fn from(value: TaggedTypedTransaction<Eip4844>) -> Self {
569 match value {
570 TaggedTypedTransaction::Legacy(signed) => Self::Legacy(signed),
571 TaggedTypedTransaction::Eip2930(signed) => Self::Eip2930(signed),
572 TaggedTypedTransaction::Eip1559(signed) => Self::Eip1559(signed),
573 TaggedTypedTransaction::Eip4844(signed) => Self::Eip4844(signed),
574 TaggedTypedTransaction::Eip7702(signed) => Self::Eip7702(signed),
575 }
576 }
577 }
578
579 impl<Eip4844> From<EthereumTypedTransaction<Eip4844>> for TaggedTypedTransaction<Eip4844> {
580 fn from(value: EthereumTypedTransaction<Eip4844>) -> Self {
581 match value {
582 EthereumTypedTransaction::Legacy(signed) => Self::Legacy(signed),
583 EthereumTypedTransaction::Eip2930(signed) => Self::Eip2930(signed),
584 EthereumTypedTransaction::Eip1559(signed) => Self::Eip1559(signed),
585 EthereumTypedTransaction::Eip4844(signed) => Self::Eip4844(signed),
586 EthereumTypedTransaction::Eip7702(signed) => Self::Eip7702(signed),
587 }
588 }
589 }
590}