alloy_consensus/transaction/
mod.rs1use crate::Signed;
4use alloc::vec::Vec;
5use alloy_eips::{eip2930::AccessList, eip7702::SignedAuthorization};
6use alloy_primitives::{keccak256, Address, Bytes, ChainId, Selector, TxKind, B256, U256};
7use core::{any, fmt};
8
9mod eip1559;
10pub use eip1559::TxEip1559;
11
12mod eip2930;
13pub use eip2930::TxEip2930;
14
15mod eip7702;
16pub use eip7702::TxEip7702;
17
18pub mod eip4844;
20pub mod pooled;
21pub use pooled::PooledTransaction;
22
23use alloy_eips::eip4844::DATA_GAS_PER_BLOB;
24pub use alloy_eips::eip4844::{
25 builder::{SidecarBuilder, SidecarCoder, SimpleCoder},
26 utils as eip4844_utils, Blob, BlobTransactionSidecar, Bytes48,
27};
28#[cfg(feature = "kzg")]
29pub use eip4844::BlobTransactionValidationError;
30pub use eip4844::{TxEip4844, TxEip4844Variant, TxEip4844WithSidecar};
31
32pub use either::Either;
34
35mod envelope;
36pub use envelope::{EthereumTxEnvelope, TxEnvelope, TxType};
37
38mod legacy;
39pub use legacy::{from_eip155_value, to_eip155_value, TxLegacy};
40
41mod rlp;
42#[doc(hidden)]
43pub use rlp::{RlpEcdsaDecodableTx, RlpEcdsaEncodableTx, RlpEcdsaTx};
44
45mod typed;
46pub use typed::{EthereumTypedTransaction, TypedTransaction};
47
48mod meta;
49pub use meta::{TransactionInfo, TransactionMeta};
50
51mod recovered;
52pub use recovered::{Recovered, SignerRecoverable};
53
54#[cfg(feature = "serde")]
55pub use legacy::signed_legacy_serde;
56
57#[cfg(all(feature = "serde", feature = "serde-bincode-compat"))]
59pub mod serde_bincode_compat {
60 pub use super::{
61 eip1559::serde_bincode_compat::*, eip2930::serde_bincode_compat::*,
62 eip7702::serde_bincode_compat::*, legacy::serde_bincode_compat::*,
63 };
64}
65
66use alloy_eips::Typed2718;
67
68#[doc(alias = "Tx")]
73#[auto_impl::auto_impl(&, Arc)]
74pub trait Transaction: Typed2718 + fmt::Debug + any::Any + Send + Sync + 'static {
75 fn chain_id(&self) -> Option<ChainId>;
77
78 fn nonce(&self) -> u64;
80
81 fn gas_limit(&self) -> u64;
83
84 fn gas_price(&self) -> Option<u128>;
86
87 fn max_fee_per_gas(&self) -> u128;
93
94 fn max_priority_fee_per_gas(&self) -> Option<u128>;
99
100 fn max_fee_per_blob_gas(&self) -> Option<u128>;
106
107 fn priority_fee_or_price(&self) -> u128;
115
116 fn effective_gas_price(&self, base_fee: Option<u64>) -> u128;
120
121 fn effective_tip_per_gas(&self, base_fee: u64) -> Option<u128> {
126 let base_fee = base_fee as u128;
127
128 let max_fee_per_gas = self.max_fee_per_gas();
129
130 if max_fee_per_gas < base_fee {
132 return None;
133 }
134
135 let fee = max_fee_per_gas - base_fee;
137
138 self.max_priority_fee_per_gas()
140 .map_or(Some(fee), |priority_fee| Some(fee.min(priority_fee)))
141 }
142
143 fn is_dynamic_fee(&self) -> bool;
145
146 fn kind(&self) -> TxKind;
148
149 fn is_create(&self) -> bool;
153
154 fn to(&self) -> Option<Address> {
159 self.kind().to().copied()
160 }
161
162 fn value(&self) -> U256;
164
165 fn input(&self) -> &Bytes;
167
168 fn function_selector(&self) -> Option<&Selector> {
172 if self.kind().is_call() {
173 self.input().get(..4).and_then(|s| TryFrom::try_from(s).ok())
174 } else {
175 None
176 }
177 }
178
179 fn access_list(&self) -> Option<&AccessList>;
182
183 fn blob_versioned_hashes(&self) -> Option<&[B256]>;
186
187 fn blob_count(&self) -> Option<u64> {
193 self.blob_versioned_hashes().map(|h| h.len() as u64)
194 }
195
196 #[inline]
200 fn blob_gas_used(&self) -> Option<u64> {
201 self.blob_count().map(|blobs| blobs * DATA_GAS_PER_BLOB)
203 }
204
205 fn authorization_list(&self) -> Option<&[SignedAuthorization]>;
209
210 fn authorization_count(&self) -> Option<u64> {
216 self.authorization_list().map(|auths| auths.len() as u64)
217 }
218}
219
220#[doc(alias = "SignableTx", alias = "TxSignable")]
227pub trait SignableTransaction<Signature>: Transaction {
228 fn set_chain_id(&mut self, chain_id: ChainId);
232
233 fn set_chain_id_checked(&mut self, chain_id: ChainId) -> bool {
236 match self.chain_id() {
237 Some(tx_chain_id) => {
238 if tx_chain_id != chain_id {
239 return false;
240 }
241 self.set_chain_id(chain_id);
242 }
243 None => {
244 self.set_chain_id(chain_id);
245 }
246 }
247 true
248 }
249
250 fn encode_for_signing(&self, out: &mut dyn alloy_rlp::BufMut);
252
253 fn payload_len_for_signature(&self) -> usize;
255
256 fn encoded_for_signing(&self) -> Vec<u8> {
260 let mut buf = Vec::with_capacity(self.payload_len_for_signature());
261 self.encode_for_signing(&mut buf);
262 buf
263 }
264
265 fn signature_hash(&self) -> B256 {
267 keccak256(self.encoded_for_signing())
268 }
269
270 fn into_signed(self, signature: Signature) -> Signed<Self, Signature>
272 where
273 Self: Sized,
274 {
275 Signed::new_unhashed(self, signature)
276 }
277}
278
279#[doc(hidden)]
281impl<S: 'static> dyn SignableTransaction<S> {
282 pub fn __downcast_ref<T: any::Any>(&self) -> Option<&T> {
283 if any::Any::type_id(self) == any::TypeId::of::<T>() {
284 unsafe { Some(&*(self as *const _ as *const T)) }
285 } else {
286 None
287 }
288 }
289}
290
291#[cfg(feature = "serde")]
292impl<T: Transaction> Transaction for alloy_serde::WithOtherFields<T> {
293 #[inline]
294 fn chain_id(&self) -> Option<ChainId> {
295 self.inner.chain_id()
296 }
297
298 #[inline]
299 fn nonce(&self) -> u64 {
300 self.inner.nonce()
301 }
302
303 #[inline]
304 fn gas_limit(&self) -> u64 {
305 self.inner.gas_limit()
306 }
307
308 #[inline]
309 fn gas_price(&self) -> Option<u128> {
310 self.inner.gas_price()
311 }
312
313 #[inline]
314 fn max_fee_per_gas(&self) -> u128 {
315 self.inner.max_fee_per_gas()
316 }
317
318 #[inline]
319 fn max_priority_fee_per_gas(&self) -> Option<u128> {
320 self.inner.max_priority_fee_per_gas()
321 }
322
323 #[inline]
324 fn max_fee_per_blob_gas(&self) -> Option<u128> {
325 self.inner.max_fee_per_blob_gas()
326 }
327
328 #[inline]
329 fn priority_fee_or_price(&self) -> u128 {
330 self.inner.priority_fee_or_price()
331 }
332
333 fn effective_gas_price(&self, base_fee: Option<u64>) -> u128 {
334 self.inner.effective_gas_price(base_fee)
335 }
336
337 #[inline]
338 fn is_dynamic_fee(&self) -> bool {
339 self.inner.is_dynamic_fee()
340 }
341
342 #[inline]
343 fn kind(&self) -> TxKind {
344 self.inner.kind()
345 }
346
347 #[inline]
348 fn is_create(&self) -> bool {
349 self.inner.is_create()
350 }
351
352 #[inline]
353 fn value(&self) -> U256 {
354 self.inner.value()
355 }
356
357 #[inline]
358 fn input(&self) -> &Bytes {
359 self.inner.input()
360 }
361
362 #[inline]
363 fn access_list(&self) -> Option<&AccessList> {
364 self.inner.access_list()
365 }
366
367 #[inline]
368 fn blob_versioned_hashes(&self) -> Option<&[B256]> {
369 self.inner.blob_versioned_hashes()
370 }
371
372 #[inline]
373 fn authorization_list(&self) -> Option<&[SignedAuthorization]> {
374 self.inner.authorization_list()
375 }
376}
377
378impl<L, R> Transaction for either::Either<L, R>
379where
380 L: Transaction,
381 R: Transaction,
382{
383 fn chain_id(&self) -> Option<ChainId> {
384 match self {
385 Self::Left(tx) => tx.chain_id(),
386 Self::Right(tx) => tx.chain_id(),
387 }
388 }
389
390 fn nonce(&self) -> u64 {
391 match self {
392 Self::Left(tx) => tx.nonce(),
393 Self::Right(tx) => tx.nonce(),
394 }
395 }
396
397 fn gas_limit(&self) -> u64 {
398 match self {
399 Self::Left(tx) => tx.gas_limit(),
400 Self::Right(tx) => tx.gas_limit(),
401 }
402 }
403
404 fn gas_price(&self) -> Option<u128> {
405 match self {
406 Self::Left(tx) => tx.gas_price(),
407 Self::Right(tx) => tx.gas_price(),
408 }
409 }
410
411 fn max_fee_per_gas(&self) -> u128 {
412 match self {
413 Self::Left(tx) => tx.max_fee_per_gas(),
414 Self::Right(tx) => tx.max_fee_per_gas(),
415 }
416 }
417
418 fn max_priority_fee_per_gas(&self) -> Option<u128> {
419 match self {
420 Self::Left(tx) => tx.max_priority_fee_per_gas(),
421 Self::Right(tx) => tx.max_priority_fee_per_gas(),
422 }
423 }
424
425 fn max_fee_per_blob_gas(&self) -> Option<u128> {
426 match self {
427 Self::Left(tx) => tx.max_fee_per_blob_gas(),
428 Self::Right(tx) => tx.max_fee_per_blob_gas(),
429 }
430 }
431
432 fn priority_fee_or_price(&self) -> u128 {
433 match self {
434 Self::Left(tx) => tx.priority_fee_or_price(),
435 Self::Right(tx) => tx.priority_fee_or_price(),
436 }
437 }
438
439 fn effective_gas_price(&self, base_fee: Option<u64>) -> u128 {
440 match self {
441 Self::Left(tx) => tx.effective_gas_price(base_fee),
442 Self::Right(tx) => tx.effective_gas_price(base_fee),
443 }
444 }
445
446 fn effective_tip_per_gas(&self, base_fee: u64) -> Option<u128> {
447 match self {
448 Self::Left(tx) => tx.effective_tip_per_gas(base_fee),
449 Self::Right(tx) => tx.effective_tip_per_gas(base_fee),
450 }
451 }
452
453 fn is_dynamic_fee(&self) -> bool {
454 match self {
455 Self::Left(tx) => tx.is_dynamic_fee(),
456 Self::Right(tx) => tx.is_dynamic_fee(),
457 }
458 }
459
460 fn kind(&self) -> TxKind {
461 match self {
462 Self::Left(tx) => tx.kind(),
463 Self::Right(tx) => tx.kind(),
464 }
465 }
466
467 fn is_create(&self) -> bool {
468 match self {
469 Self::Left(tx) => tx.is_create(),
470 Self::Right(tx) => tx.is_create(),
471 }
472 }
473
474 fn to(&self) -> Option<Address> {
475 match self {
476 Self::Left(tx) => tx.to(),
477 Self::Right(tx) => tx.to(),
478 }
479 }
480
481 fn value(&self) -> U256 {
482 match self {
483 Self::Left(tx) => tx.value(),
484 Self::Right(tx) => tx.value(),
485 }
486 }
487
488 fn input(&self) -> &Bytes {
489 match self {
490 Self::Left(tx) => tx.input(),
491 Self::Right(tx) => tx.input(),
492 }
493 }
494
495 fn function_selector(&self) -> Option<&Selector> {
496 match self {
497 Self::Left(tx) => tx.function_selector(),
498 Self::Right(tx) => tx.function_selector(),
499 }
500 }
501
502 fn access_list(&self) -> Option<&AccessList> {
503 match self {
504 Self::Left(tx) => tx.access_list(),
505 Self::Right(tx) => tx.access_list(),
506 }
507 }
508
509 fn blob_versioned_hashes(&self) -> Option<&[B256]> {
510 match self {
511 Self::Left(tx) => tx.blob_versioned_hashes(),
512 Self::Right(tx) => tx.blob_versioned_hashes(),
513 }
514 }
515
516 fn blob_count(&self) -> Option<u64> {
517 match self {
518 Self::Left(tx) => tx.blob_count(),
519 Self::Right(tx) => tx.blob_count(),
520 }
521 }
522
523 fn blob_gas_used(&self) -> Option<u64> {
524 match self {
525 Self::Left(tx) => tx.blob_gas_used(),
526 Self::Right(tx) => tx.blob_gas_used(),
527 }
528 }
529
530 fn authorization_list(&self) -> Option<&[SignedAuthorization]> {
531 match self {
532 Self::Left(tx) => tx.authorization_list(),
533 Self::Right(tx) => tx.authorization_list(),
534 }
535 }
536
537 fn authorization_count(&self) -> Option<u64> {
538 match self {
539 Self::Left(tx) => tx.authorization_count(),
540 Self::Right(tx) => tx.authorization_count(),
541 }
542 }
543}