1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
//! Transaction trait [`Transaction`] and associated types.
mod alloy_types;
pub mod eip2930;
pub mod eip7702;
mod either;
pub mod transaction_type;
pub use alloy_types::{
AccessList, AccessListItem, Authorization, RecoveredAuthority, RecoveredAuthorization,
SignedAuthorization,
};
pub use eip2930::AccessListItemTr;
pub use eip7702::AuthorizationTr;
pub use transaction_type::TransactionType;
use crate::result::InvalidTransaction;
use auto_impl::auto_impl;
use core::{cmp::min, fmt::Debug};
use primitives::{eip4844::GAS_PER_BLOB, Address, Bytes, TxKind, B256, U256};
use std::boxed::Box;
/// Transaction validity error types.
pub trait TransactionError: Debug + core::error::Error {}
/// Main Transaction trait that abstracts and specifies all transaction currently supported by Ethereum
///
/// Access to any associated type is gated behind [`tx_type`][Transaction::tx_type] function.
///
/// It can be extended to support new transaction types and only transaction types can be
/// deprecated by not returning tx_type.
#[auto_impl(&, Box, Arc, Rc)]
pub trait Transaction {
/// EIP-2930 Access list item type.
type AccessListItem<'a>: AccessListItemTr
where
Self: 'a;
/// EIP-7702 Authorization type.
type Authorization<'a>: AuthorizationTr
where
Self: 'a;
/// Returns the transaction type.
///
/// Depending on this field other functions should be called.
fn tx_type(&self) -> u8;
/// Caller aka Author aka transaction signer.
///
/// Note : Common field for all transactions.
fn caller(&self) -> Address;
/// The maximum amount of gas the transaction can use.
///
/// Note : Common field for all transactions.
fn gas_limit(&self) -> u64;
/// The value sent to the receiver of [`TxKind::Call`].
///
/// Note : Common field for all transactions.
fn value(&self) -> U256;
/// Returns the input data of the transaction.
///
/// Note : Common field for all transactions.
fn input(&self) -> &Bytes;
/// The nonce of the transaction.
///
/// Note : Common field for all transactions.
fn nonce(&self) -> u64;
/// Transaction kind. It can be Call or Create.
///
/// Kind is applicable for: Legacy, EIP-2930, EIP-1559
/// And is Call for EIP-4844 and EIP-7702 transactions.
fn kind(&self) -> TxKind;
/// Chain Id is optional for legacy transactions.
///
/// As it was introduced in EIP-155.
fn chain_id(&self) -> Option<u64>;
/// Gas price for the transaction.
/// It is only applicable for Legacy and EIP-2930 transactions.
/// For Eip1559 it is max_fee_per_gas.
fn gas_price(&self) -> u128;
/// Access list for the transaction.
///
/// Introduced in EIP-2930.
fn access_list(&self) -> Option<impl Iterator<Item = Self::AccessListItem<'_>>>;
/// Returns vector of fixed size hash(32 bytes)
///
/// Note : EIP-4844 transaction field.
fn blob_versioned_hashes(&self) -> &[B256];
/// Max fee per data gas
///
/// Note : EIP-4844 transaction field.
fn max_fee_per_blob_gas(&self) -> u128;
/// Total gas for all blobs. Max number of blocks is already checked
/// so we dont need to check for overflow.
fn total_blob_gas(&self) -> u64 {
GAS_PER_BLOB * self.blob_versioned_hashes().len() as u64
}
/// Calculates the maximum [EIP-4844] `data_fee` of the transaction.
///
/// This is used for ensuring that the user has at least enough funds to pay the
/// `max_fee_per_blob_gas * total_blob_gas`, on top of regular gas costs.
///
/// See EIP-4844:
/// <https://github.com/ethereum/EIPs/blob/master/EIPS/eip-4844.md#execution-layer-validation>
fn calc_max_data_fee(&self) -> U256 {
U256::from((self.total_blob_gas() as u128).saturating_mul(self.max_fee_per_blob_gas()))
}
/// Returns length of the authorization list.
///
/// # Note
///
/// Transaction is considered invalid if list is empty.
fn authorization_list_len(&self) -> usize;
/// List of authorizations, that contains the signature that authorizes this
/// caller to place the code to signer account.
///
/// Set EOA account code for one transaction
///
/// [EIP-Set EOA account code for one transaction](https://eips.ethereum.org/EIPS/eip-7702)
fn authorization_list(&self) -> impl Iterator<Item = Self::Authorization<'_>>;
/// Returns maximum fee that can be paid for the transaction.
fn max_fee_per_gas(&self) -> u128 {
self.gas_price()
}
/// Maximum priority fee per gas.
fn max_priority_fee_per_gas(&self) -> Option<u128>;
/// Returns effective gas price is gas price field for Legacy and Eip2930 transaction.
///
/// While for transactions after Eip1559 it is minimum of max_fee and `base + max_priority_fee`.
fn effective_gas_price(&self, base_fee: u128) -> u128 {
if self.tx_type() == TransactionType::Legacy as u8
|| self.tx_type() == TransactionType::Eip2930 as u8
{
return self.gas_price();
}
// for EIP-1559 tx and onwards gas_price represents maximum price.
let max_price = self.gas_price();
let Some(max_priority_fee) = self.max_priority_fee_per_gas() else {
return max_price;
};
min(max_price, base_fee.saturating_add(max_priority_fee))
}
/// Returns the maximum balance that can be spent by the transaction.
///
/// Return U256 or error if all values overflow U256 number.
fn max_balance_spending(&self) -> Result<U256, InvalidTransaction> {
// gas_limit * max_fee + value + additional_gas_cost
let mut max_balance_spending = (self.gas_limit() as u128)
.checked_mul(self.max_fee_per_gas())
.and_then(|gas_cost| U256::from(gas_cost).checked_add(self.value()))
.ok_or(InvalidTransaction::OverflowPaymentInTransaction)?;
// add blob fee
if self.tx_type() == TransactionType::Eip4844 {
let data_fee = self.calc_max_data_fee();
max_balance_spending = max_balance_spending
.checked_add(data_fee)
.ok_or(InvalidTransaction::OverflowPaymentInTransaction)?;
}
Ok(max_balance_spending)
}
/// Checks if the caller has enough balance to cover the maximum balance spending of this transaction.
///
/// Internally calls [`Self::max_balance_spending`] and checks if the balance is enough.
#[inline]
fn ensure_enough_balance(&self, balance: U256) -> Result<(), InvalidTransaction> {
let max_balance_spending = self.max_balance_spending()?;
if max_balance_spending > balance {
return Err(InvalidTransaction::LackOfFundForMaxFee {
fee: Box::new(max_balance_spending),
balance: Box::new(balance),
});
}
Ok(())
}
/// Returns the effective balance that is going to be spent that depends on base_fee
/// Multiplication for gas are done in u128 type (saturated) and value is added as U256 type.
///
/// It is calculated as `tx.effective_gas_price * tx.gas_limit + tx.value`. Additionally adding
/// `blob_price * tx.total_blob_gas` blob fee if transaction is EIP-4844.
///
/// # Reason
///
/// This is done for performance reasons and it is known to be safe as there is no more that u128::MAX value of eth in existence.
///
/// This is always strictly less than [`Self::max_balance_spending`].
///
/// Return U256 or error if all values overflow U256 number.
#[inline]
fn effective_balance_spending(
&self,
base_fee: u128,
blob_price: u128,
) -> Result<U256, InvalidTransaction> {
// gas_limit * max_fee + value + additional_gas_cost
let mut effective_balance_spending = (self.gas_limit() as u128)
.checked_mul(self.effective_gas_price(base_fee))
.and_then(|gas_cost| U256::from(gas_cost).checked_add(self.value()))
.ok_or(InvalidTransaction::OverflowPaymentInTransaction)?;
// add blob fee
if self.tx_type() == TransactionType::Eip4844 {
let blob_gas = self.total_blob_gas() as u128;
effective_balance_spending = effective_balance_spending
.checked_add(U256::from(blob_price.saturating_mul(blob_gas)))
.ok_or(InvalidTransaction::OverflowPaymentInTransaction)?;
}
Ok(effective_balance_spending)
}
/// Returns the effective balance calculated with [`Self::effective_balance_spending`] but without the value.
///
/// Effective balance is always strictly less than [`Self::max_balance_spending`].
///
/// This functions returns `tx.effective_gas_price * tx.gas_limit + blob_price * tx.total_blob_gas`.
#[inline]
fn gas_balance_spending(
&self,
base_fee: u128,
blob_price: u128,
) -> Result<U256, InvalidTransaction> {
Ok(self.effective_balance_spending(base_fee, blob_price)? - self.value())
}
}