diff --git a/crates/consensus/src/transaction/eip1559.rs b/crates/consensus/src/transaction/eip1559.rs index 9016095e4e2..1777e661ab3 100644 --- a/crates/consensus/src/transaction/eip1559.rs +++ b/crates/consensus/src/transaction/eip1559.rs @@ -1,6 +1,6 @@ use crate::{EncodableSignature, SignableTransaction, Signed, Transaction, TxType}; use alloy_eips::eip2930::AccessList; -use alloy_primitives::{keccak256, Bytes, ChainId, Signature, TxKind, U256}; +use alloy_primitives::{keccak256, Bytes, ChainId, Signature, TxKind, B256, U256}; use alloy_rlp::{BufMut, Decodable, Encodable, Header}; use core::mem; @@ -278,6 +278,18 @@ impl Transaction for TxEip1559 { None } + fn max_fee_per_gas(&self) -> u128 { + self.max_fee_per_gas + } + + fn max_priority_fee_per_gas(&self) -> Option { + Some(self.max_priority_fee_per_gas) + } + + fn priority_fee_or_price(&self) -> u128 { + self.max_priority_fee_per_gas + } + fn to(&self) -> TxKind { self.to } @@ -289,6 +301,18 @@ impl Transaction for TxEip1559 { fn input(&self) -> &[u8] { &self.input } + + fn ty(&self) -> u8 { + TxType::Eip2930 as u8 + } + + fn access_list(&self) -> Option<&AccessList> { + Some(&self.access_list) + } + + fn blob_versioned_hashes(&self) -> Option<&[B256]> { + None + } } impl SignableTransaction for TxEip1559 { diff --git a/crates/consensus/src/transaction/eip2930.rs b/crates/consensus/src/transaction/eip2930.rs index bffebce22f2..4cfbeb615dd 100644 --- a/crates/consensus/src/transaction/eip2930.rs +++ b/crates/consensus/src/transaction/eip2930.rs @@ -1,6 +1,6 @@ use crate::{EncodableSignature, SignableTransaction, Signed, Transaction, TxType}; use alloy_eips::eip2930::AccessList; -use alloy_primitives::{keccak256, Bytes, ChainId, Signature, TxKind, U256}; +use alloy_primitives::{keccak256, Bytes, ChainId, Signature, TxKind, B256, U256}; use alloy_rlp::{length_of_length, BufMut, Decodable, Encodable, Header}; use core::mem; @@ -242,6 +242,18 @@ impl Transaction for TxEip2930 { Some(self.gas_price) } + fn max_fee_per_gas(&self) -> u128 { + self.gas_price + } + + fn max_priority_fee_per_gas(&self) -> Option { + None + } + + fn priority_fee_or_price(&self) -> u128 { + self.gas_price + } + fn to(&self) -> TxKind { self.to } @@ -253,6 +265,18 @@ impl Transaction for TxEip2930 { fn input(&self) -> &[u8] { &self.input } + + fn ty(&self) -> u8 { + TxType::Eip2930 as u8 + } + + fn access_list(&self) -> Option<&AccessList> { + Some(&self.access_list) + } + + fn blob_versioned_hashes(&self) -> Option<&[B256]> { + None + } } impl SignableTransaction for TxEip2930 { diff --git a/crates/consensus/src/transaction/eip4844.rs b/crates/consensus/src/transaction/eip4844.rs index 95f92c5a291..c239a12ea59 100644 --- a/crates/consensus/src/transaction/eip4844.rs +++ b/crates/consensus/src/transaction/eip4844.rs @@ -216,6 +216,27 @@ impl Transaction for TxEip4844Variant { None } + fn max_fee_per_gas(&self) -> u128 { + match self { + Self::TxEip4844(tx) => tx.max_fee_per_gas(), + Self::TxEip4844WithSidecar(tx) => tx.max_fee_per_gas(), + } + } + + fn max_priority_fee_per_gas(&self) -> Option { + match self { + Self::TxEip4844(tx) => tx.max_priority_fee_per_gas(), + Self::TxEip4844WithSidecar(tx) => tx.max_priority_fee_per_gas(), + } + } + + fn priority_fee_or_price(&self) -> u128 { + match self { + Self::TxEip4844(tx) => tx.priority_fee_or_price(), + Self::TxEip4844WithSidecar(tx) => tx.priority_fee_or_price(), + } + } + fn to(&self) -> TxKind { match self { Self::TxEip4844(tx) => tx.to, @@ -237,6 +258,24 @@ impl Transaction for TxEip4844Variant { Self::TxEip4844WithSidecar(tx) => tx.tx().input.as_ref(), } } + + fn ty(&self) -> u8 { + TxType::Eip4844 as u8 + } + + fn access_list(&self) -> Option<&AccessList> { + match self { + Self::TxEip4844(tx) => tx.access_list(), + Self::TxEip4844WithSidecar(tx) => tx.access_list(), + } + } + + fn blob_versioned_hashes(&self) -> Option<&[B256]> { + match self { + Self::TxEip4844(tx) => tx.blob_versioned_hashes(), + Self::TxEip4844WithSidecar(tx) => tx.blob_versioned_hashes(), + } + } } impl SignableTransaction for TxEip4844Variant { @@ -649,6 +688,18 @@ impl Transaction for TxEip4844 { None } + fn max_fee_per_gas(&self) -> u128 { + self.max_fee_per_gas + } + + fn max_priority_fee_per_gas(&self) -> Option { + Some(self.max_priority_fee_per_gas) + } + + fn priority_fee_or_price(&self) -> u128 { + self.max_priority_fee_per_gas + } + fn to(&self) -> TxKind { self.to.into() } @@ -660,6 +711,18 @@ impl Transaction for TxEip4844 { fn input(&self) -> &[u8] { &self.input } + + fn ty(&self) -> u8 { + TxType::Eip4844 as u8 + } + + fn access_list(&self) -> Option<&AccessList> { + Some(&self.access_list) + } + + fn blob_versioned_hashes(&self) -> Option<&[B256]> { + Some(&self.blob_versioned_hashes) + } } impl Encodable for TxEip4844 { @@ -890,6 +953,18 @@ impl Transaction for TxEip4844WithSidecar { self.tx.gas_price() } + fn max_fee_per_gas(&self) -> u128 { + self.tx.max_fee_per_gas() + } + + fn max_priority_fee_per_gas(&self) -> Option { + self.tx.max_priority_fee_per_gas() + } + + fn priority_fee_or_price(&self) -> u128 { + self.tx.priority_fee_or_price() + } + fn to(&self) -> TxKind { self.tx.to() } @@ -901,6 +976,18 @@ impl Transaction for TxEip4844WithSidecar { fn input(&self) -> &[u8] { self.tx.input() } + + fn ty(&self) -> u8 { + TxType::Eip4844 as u8 + } + + fn access_list(&self) -> Option<&AccessList> { + Some(&self.tx.access_list) + } + + fn blob_versioned_hashes(&self) -> Option<&[B256]> { + self.tx.blob_versioned_hashes() + } } #[cfg(test)] diff --git a/crates/consensus/src/transaction/eip7702.rs b/crates/consensus/src/transaction/eip7702.rs index a062aefd28e..44bd709b373 100644 --- a/crates/consensus/src/transaction/eip7702.rs +++ b/crates/consensus/src/transaction/eip7702.rs @@ -1,6 +1,6 @@ use crate::{EncodableSignature, SignableTransaction, Signed, Transaction, TxType}; use alloy_eips::eip2930::AccessList; -use alloy_primitives::{keccak256, Bytes, ChainId, Signature, TxKind, U256}; +use alloy_primitives::{keccak256, Bytes, ChainId, Signature, TxKind, B256, U256}; use alloy_rlp::{length_of_length, BufMut, Decodable, Encodable, Header}; use core::mem; @@ -301,6 +301,18 @@ impl Transaction for TxEip7702 { None } + fn max_fee_per_gas(&self) -> u128 { + self.max_fee_per_gas + } + + fn max_priority_fee_per_gas(&self) -> Option { + Some(self.max_priority_fee_per_gas) + } + + fn priority_fee_or_price(&self) -> u128 { + self.max_priority_fee_per_gas + } + fn to(&self) -> TxKind { self.to } @@ -312,6 +324,18 @@ impl Transaction for TxEip7702 { fn input(&self) -> &[u8] { &self.input } + + fn ty(&self) -> u8 { + TxType::Eip7702 as u8 + } + + fn access_list(&self) -> Option<&AccessList> { + Some(&self.access_list) + } + + fn blob_versioned_hashes(&self) -> Option<&[B256]> { + None + } } impl SignableTransaction for TxEip7702 { diff --git a/crates/consensus/src/transaction/envelope.rs b/crates/consensus/src/transaction/envelope.rs index 90ee3693838..7c00564265e 100644 --- a/crates/consensus/src/transaction/envelope.rs +++ b/crates/consensus/src/transaction/envelope.rs @@ -1,7 +1,12 @@ +#[cfg(not(feature = "std"))] +use alloc::vec::Vec; use core::fmt; use crate::{Signed, Transaction, TxEip1559, TxEip2930, TxEip7702, TxLegacy}; -use alloy_eips::eip2718::{Decodable2718, Eip2718Error, Eip2718Result, Encodable2718}; +use alloy_eips::{ + eip2718::{Decodable2718, Eip2718Error, Eip2718Result, Encodable2718}, + eip2930::AccessList, +}; use alloy_primitives::{TxKind, B256}; use alloy_rlp::{Decodable, Encodable, Header}; @@ -432,6 +437,36 @@ impl Transaction for TxEnvelope { } } + fn max_fee_per_gas(&self) -> u128 { + match self { + Self::Legacy(tx) => tx.tx().max_fee_per_gas(), + Self::Eip2930(tx) => tx.tx().max_fee_per_gas(), + Self::Eip1559(tx) => tx.tx().max_fee_per_gas(), + Self::Eip4844(tx) => tx.tx().max_fee_per_gas(), + Self::Eip7702(tx) => tx.tx().max_fee_per_gas(), + } + } + + fn max_priority_fee_per_gas(&self) -> Option { + match self { + Self::Legacy(tx) => tx.tx().max_priority_fee_per_gas(), + Self::Eip2930(tx) => tx.tx().max_priority_fee_per_gas(), + Self::Eip1559(tx) => tx.tx().max_priority_fee_per_gas(), + Self::Eip4844(tx) => tx.tx().max_priority_fee_per_gas(), + Self::Eip7702(tx) => tx.tx().max_priority_fee_per_gas(), + } + } + + fn priority_fee_or_price(&self) -> u128 { + match self { + Self::Legacy(tx) => tx.tx().priority_fee_or_price(), + Self::Eip2930(tx) => tx.tx().priority_fee_or_price(), + Self::Eip1559(tx) => tx.tx().priority_fee_or_price(), + Self::Eip4844(tx) => tx.tx().priority_fee_or_price(), + Self::Eip7702(tx) => tx.tx().priority_fee_or_price(), + } + } + fn input(&self) -> &[u8] { match self { Self::Legacy(tx) => tx.tx().input(), @@ -471,6 +506,36 @@ impl Transaction for TxEnvelope { Self::Eip7702(tx) => tx.tx().value(), } } + + fn ty(&self) -> u8 { + match self { + Self::Legacy(tx) => tx.tx().ty(), + Self::Eip2930(tx) => tx.tx().ty(), + Self::Eip1559(tx) => tx.tx().ty(), + Self::Eip4844(tx) => tx.tx().ty(), + Self::Eip7702(tx) => tx.tx().ty(), + } + } + + fn access_list(&self) -> Option<&AccessList> { + match self { + Self::Legacy(tx) => tx.tx().access_list(), + Self::Eip2930(tx) => tx.tx().access_list(), + Self::Eip1559(tx) => tx.tx().access_list(), + Self::Eip4844(tx) => tx.tx().access_list(), + Self::Eip7702(tx) => tx.tx().access_list(), + } + } + + fn blob_versioned_hashes(&self) -> Option<&[B256]> { + match self { + Self::Legacy(tx) => tx.tx().blob_versioned_hashes(), + Self::Eip2930(tx) => tx.tx().blob_versioned_hashes(), + Self::Eip1559(tx) => tx.tx().blob_versioned_hashes(), + Self::Eip4844(tx) => tx.tx().blob_versioned_hashes(), + Self::Eip7702(tx) => tx.tx().blob_versioned_hashes(), + } + } } #[cfg(test)] diff --git a/crates/consensus/src/transaction/legacy.rs b/crates/consensus/src/transaction/legacy.rs index 04733e405cc..e45ac32ebd0 100644 --- a/crates/consensus/src/transaction/legacy.rs +++ b/crates/consensus/src/transaction/legacy.rs @@ -1,8 +1,11 @@ -use crate::{EncodableSignature, SignableTransaction, Signed, Transaction}; -use alloy_primitives::{keccak256, Bytes, ChainId, Signature, TxKind, U256}; -use alloy_rlp::{length_of_length, BufMut, Decodable, Encodable, Header, Result}; use core::mem; +use alloy_eips::eip2930::AccessList; +use alloy_primitives::{keccak256, Bytes, ChainId, Signature, TxKind, B256, U256}; +use alloy_rlp::{length_of_length, BufMut, Decodable, Encodable, Header, Result}; + +use crate::{EncodableSignature, SignableTransaction, Signed, Transaction, TxType}; + #[cfg(not(feature = "std"))] use alloc::vec::Vec; @@ -217,6 +220,18 @@ impl Transaction for TxLegacy { Some(self.gas_price) } + fn max_fee_per_gas(&self) -> u128 { + self.gas_price + } + + fn max_priority_fee_per_gas(&self) -> Option { + None + } + + fn priority_fee_or_price(&self) -> u128 { + self.gas_price + } + fn to(&self) -> TxKind { self.to } @@ -228,6 +243,18 @@ impl Transaction for TxLegacy { fn input(&self) -> &[u8] { &self.input } + + fn ty(&self) -> u8 { + TxType::Legacy as u8 + } + + fn access_list(&self) -> Option<&AccessList> { + None + } + + fn blob_versioned_hashes(&self) -> Option<&[B256]> { + None + } } impl SignableTransaction for TxLegacy { diff --git a/crates/consensus/src/transaction/mod.rs b/crates/consensus/src/transaction/mod.rs index 0b2307c4122..c77fa28da0d 100644 --- a/crates/consensus/src/transaction/mod.rs +++ b/crates/consensus/src/transaction/mod.rs @@ -1,6 +1,7 @@ //! Transaction types. use crate::Signed; +use alloy_eips::eip2930::AccessList; use alloy_primitives::{keccak256, ChainId, TxKind, B256, U256}; use core::any; @@ -51,6 +52,49 @@ pub trait Transaction: any::Any + Send + Sync + 'static { /// Get `gas_price`. fn gas_price(&self) -> Option; + /// Returns the EIP-1559 the maximum fee per gas the caller is willing to pay. + /// + /// For legacy transactions this is `gas_price`. + /// + /// This is also commonly referred to as the "Gas Fee Cap" (`GasFeeCap`). + fn max_fee_per_gas(&self) -> u128; + + /// Returns the EIP-1559 Priority fee the caller is paying to the block author. + /// + /// This will return `None` for non-EIP1559 transactions + fn max_priority_fee_per_gas(&self) -> Option; + + /// Return the max priority fee per gas if the transaction is an EIP-1559 transaction, and + /// otherwise return the gas price. + /// + /// # Warning + /// + /// This is different than the `max_priority_fee_per_gas` method, which returns `None` for + /// non-EIP-1559 transactions. + fn priority_fee_or_price(&self) -> u128; + + /// Returns the effective tip for this transaction. + /// + /// For EIP-1559 transactions: `min(max_fee_per_gas - base_fee, max_priority_fee_per_gas)`. + /// For legacy transactions: `gas_price - base_fee`. + fn effective_tip_per_gas(&self, base_fee: u64) -> Option { + let base_fee = base_fee as u128; + + let max_fee_per_gas = self.max_fee_per_gas(); + + // Check if max_fee_per_gas is less than base_fee + if max_fee_per_gas < base_fee { + return None; + } + + // Calculate the difference between max_fee_per_gas and base_fee + let fee = max_fee_per_gas - base_fee; + + // Compare the fee with max_priority_fee_per_gas (or gas price for non-EIP1559 transactions) + self.max_priority_fee_per_gas() + .map_or(Some(fee), |priority_fee| Some(fee.min(priority_fee))) + } + /// Get `to`. fn to(&self) -> TxKind; @@ -59,6 +103,17 @@ pub trait Transaction: any::Any + Send + Sync + 'static { /// Get `data`. fn input(&self) -> &[u8]; + + /// Returns the transaction type + fn ty(&self) -> u8; + + /// Returns the EIP2930 `access_list` for the particular transaction type. Returns `None` for + /// older transaction types. + fn access_list(&self) -> Option<&AccessList>; + + /// Blob versioned hashes for eip4844 transaction. For previous transaction types this is + /// `None`. + fn blob_versioned_hashes(&self) -> Option<&[B256]>; } /// A signable transaction. diff --git a/crates/consensus/src/transaction/typed.rs b/crates/consensus/src/transaction/typed.rs index 44062ea4a53..361b9049d31 100644 --- a/crates/consensus/src/transaction/typed.rs +++ b/crates/consensus/src/transaction/typed.rs @@ -1,8 +1,13 @@ +#[cfg(not(feature = "std"))] +use alloc::vec::Vec; + +use alloy_eips::eip2930::AccessList; +use alloy_primitives::{ChainId, TxKind, B256}; + use crate::{ transaction::eip4844::{TxEip4844, TxEip4844Variant, TxEip4844WithSidecar}, Transaction, TxEip1559, TxEip2930, TxEip7702, TxEnvelope, TxLegacy, TxType, }; -use alloy_primitives::{ChainId, TxKind}; /// The TypedTransaction enum represents all Ethereum transaction request types. /// @@ -174,6 +179,36 @@ impl Transaction for TypedTransaction { } } + fn max_fee_per_gas(&self) -> u128 { + match self { + Self::Legacy(tx) => tx.max_fee_per_gas(), + Self::Eip2930(tx) => tx.max_fee_per_gas(), + Self::Eip1559(tx) => tx.max_fee_per_gas(), + Self::Eip4844(tx) => tx.max_fee_per_gas(), + Self::Eip7702(tx) => tx.max_fee_per_gas(), + } + } + + fn max_priority_fee_per_gas(&self) -> Option { + match self { + Self::Legacy(tx) => tx.max_priority_fee_per_gas(), + Self::Eip2930(tx) => tx.max_priority_fee_per_gas(), + Self::Eip1559(tx) => tx.max_priority_fee_per_gas(), + Self::Eip4844(tx) => tx.max_priority_fee_per_gas(), + Self::Eip7702(tx) => tx.max_priority_fee_per_gas(), + } + } + + fn priority_fee_or_price(&self) -> u128 { + match self { + Self::Legacy(tx) => tx.priority_fee_or_price(), + Self::Eip2930(tx) => tx.priority_fee_or_price(), + Self::Eip1559(tx) => tx.priority_fee_or_price(), + Self::Eip4844(tx) => tx.priority_fee_or_price(), + Self::Eip7702(tx) => tx.priority_fee_or_price(), + } + } + fn to(&self) -> TxKind { match self { Self::Legacy(tx) => tx.to(), @@ -203,6 +238,36 @@ impl Transaction for TypedTransaction { Self::Eip7702(tx) => tx.input(), } } + + fn ty(&self) -> u8 { + match self { + Self::Legacy(tx) => tx.ty(), + Self::Eip2930(tx) => tx.ty(), + Self::Eip1559(tx) => tx.ty(), + Self::Eip4844(tx) => tx.ty(), + Self::Eip7702(tx) => tx.ty(), + } + } + + fn access_list(&self) -> Option<&AccessList> { + match self { + Self::Legacy(tx) => tx.access_list(), + Self::Eip2930(tx) => tx.access_list(), + Self::Eip1559(tx) => tx.access_list(), + Self::Eip4844(tx) => tx.access_list(), + Self::Eip7702(tx) => tx.access_list(), + } + } + + fn blob_versioned_hashes(&self) -> Option<&[B256]> { + match self { + Self::Legacy(tx) => tx.blob_versioned_hashes(), + Self::Eip2930(tx) => tx.blob_versioned_hashes(), + Self::Eip1559(tx) => tx.blob_versioned_hashes(), + Self::Eip4844(tx) => tx.blob_versioned_hashes(), + Self::Eip7702(tx) => tx.blob_versioned_hashes(), + } + } } #[cfg(feature = "serde")]