Skip to main content

reth_transaction_pool/
traits.rs

1//! Transaction Pool Traits and Types
2//!
3//! This module defines the core abstractions for transaction pool implementations,
4//! handling the complexity of different transaction representations across the
5//! network, mempool, and the chain itself.
6//!
7//! ## Key Concepts
8//!
9//! ### Transaction Representations
10//!
11//! Transactions exist in different formats throughout their lifecycle:
12//!
13//! 1. **Consensus Format** ([`PoolTransaction::Consensus`])
14//!    - The canonical format stored in blocks
15//!    - Minimal size for efficient storage
16//!    - Example: EIP-4844 transactions store only blob hashes: ([`TransactionSigned::Eip4844`])
17//!
18//! 2. **Pooled Format** ([`PoolTransaction::Pooled`])
19//!    - Extended format for network propagation
20//!    - Includes additional validation data
21//!    - Example: EIP-4844 transactions include full blob sidecars: ([`PooledTransactionVariant`])
22//!
23//! ### Type Relationships
24//!
25//! ```text
26//! NodePrimitives::SignedTx  ←──   NetworkPrimitives::BroadcastedTransaction
27//!        │                              │
28//!        │ (consensus format)           │ (announced to peers)
29//!        │                              │
30//!        └──────────┐  ┌────────────────┘
31//!                   ▼  ▼
32//!            PoolTransaction::Consensus
33//!                   │ ▲
34//!                   │ │ from pooled (always succeeds)
35//!                   │ │
36//!                   ▼ │ try_from consensus (may fail)
37//!            PoolTransaction::Pooled  ←──→  NetworkPrimitives::PooledTransaction
38//!                                             (sent on request)
39//! ```
40//!
41//! ### Special Cases
42//!
43//! #### EIP-4844 Blob Transactions
44//! - Consensus format: Only blob hashes (32 bytes each)
45//! - Pooled format: Full blobs + commitments + proofs (large data per blob)
46//! - Network behavior: Not broadcast automatically, only sent on explicit request
47//!
48//! #### Optimism Deposit Transactions
49//! - Only exist in consensus format
50//! - Never enter the mempool (system transactions)
51//! - Conversion from consensus to pooled always fails
52
53use crate::{
54    blobstore::BlobStoreError,
55    error::{InvalidPoolTransactionError, PoolError, PoolResult},
56    pool::{
57        state::SubPool, BestTransactionFilter, NewTransactionEvent, TransactionEvents,
58        TransactionListenerKind,
59    },
60    validate::ValidPoolTransaction,
61    AddedTransactionOutcome, AllTransactionsEvents,
62};
63use alloy_consensus::{error::ValueError, transaction::TxHashRef, BlockHeader, Signed, Typed2718};
64use alloy_eips::{
65    eip2718::{Encodable2718, WithEncoded},
66    eip2930::AccessList,
67    eip4844::{
68        env_settings::KzgSettings, BlobAndProofV1, BlobAndProofV2, BlobTransactionValidationError,
69    },
70    eip7594::BlobTransactionSidecarVariant,
71    eip7702::SignedAuthorization,
72};
73use alloy_primitives::{map::AddressSet, Address, Bytes, TxHash, TxKind, B256, U256};
74use futures_util::{ready, Stream};
75use reth_eth_wire_types::HandleMempoolData;
76use reth_ethereum_primitives::{PooledTransactionVariant, TransactionSigned};
77use reth_execution_types::ChangedAccount;
78use reth_primitives_traits::{Block, InMemorySize, Recovered, SealedBlock, SignedTransaction};
79use serde::{Deserialize, Serialize};
80use std::{
81    collections::HashMap,
82    fmt,
83    fmt::Debug,
84    future::Future,
85    pin::Pin,
86    sync::Arc,
87    task::{Context, Poll},
88};
89use tokio::sync::mpsc::Receiver;
90
91/// The `PeerId` type.
92pub type PeerId = alloy_primitives::B512;
93
94/// Helper type alias to access [`PoolTransaction`] for a given [`TransactionPool`].
95pub type PoolTx<P> = <P as TransactionPool>::Transaction;
96/// Helper type alias to access [`PoolTransaction::Consensus`] for a given [`TransactionPool`].
97pub type PoolConsensusTx<P> = <<P as TransactionPool>::Transaction as PoolTransaction>::Consensus;
98
99/// Helper type alias to access [`PoolTransaction::Pooled`] for a given [`TransactionPool`].
100pub type PoolPooledTx<P> = <<P as TransactionPool>::Transaction as PoolTransaction>::Pooled;
101
102/// General purpose abstraction of a transaction-pool.
103///
104/// This is intended to be used by API-consumers such as RPC that need inject new incoming,
105/// unverified transactions. And by block production that needs to get transactions to execute in a
106/// new block.
107///
108/// Note: This requires `Clone` for convenience, since it is assumed that this will be implemented
109/// for a wrapped `Arc` type, see also [`Pool`](crate::Pool).
110#[auto_impl::auto_impl(&, Arc)]
111pub trait TransactionPool: Clone + Debug + Send + Sync {
112    /// The transaction type of the pool
113    type Transaction: EthPoolTransaction;
114
115    /// Returns stats about the pool and all sub-pools.
116    fn pool_size(&self) -> PoolSize;
117
118    /// Returns the block the pool is currently tracking.
119    ///
120    /// This tracks the block that the pool has last seen.
121    fn block_info(&self) -> BlockInfo;
122
123    /// Imports an _external_ transaction.
124    ///
125    /// This is intended to be used by the network to insert incoming transactions received over the
126    /// p2p network.
127    ///
128    /// Consumer: P2P
129    fn add_external_transaction(
130        &self,
131        transaction: Self::Transaction,
132    ) -> impl Future<Output = PoolResult<AddedTransactionOutcome>> + Send {
133        self.add_transaction(TransactionOrigin::External, transaction)
134    }
135
136    /// Imports all _external_ transactions
137    ///
138    /// Consumer: Utility
139    fn add_external_transactions(
140        &self,
141        transactions: Vec<Self::Transaction>,
142    ) -> impl Future<Output = Vec<PoolResult<AddedTransactionOutcome>>> + Send {
143        self.add_transactions(TransactionOrigin::External, transactions)
144    }
145
146    /// Adds an _unvalidated_ transaction into the pool and subscribe to state changes.
147    ///
148    /// This is the same as [`TransactionPool::add_transaction`] but returns an event stream for the
149    /// given transaction.
150    ///
151    /// Consumer: Custom
152    fn add_transaction_and_subscribe(
153        &self,
154        origin: TransactionOrigin,
155        transaction: Self::Transaction,
156    ) -> impl Future<Output = PoolResult<TransactionEvents>> + Send;
157
158    /// Adds an _unvalidated_ transaction into the pool.
159    ///
160    /// Consumer: RPC
161    fn add_transaction(
162        &self,
163        origin: TransactionOrigin,
164        transaction: Self::Transaction,
165    ) -> impl Future<Output = PoolResult<AddedTransactionOutcome>> + Send;
166
167    /// Adds the given _unvalidated_ transactions into the pool.
168    ///
169    /// All transactions will use the same `origin`.
170    ///
171    /// Returns a list of results.
172    ///
173    /// Consumer: RPC
174    fn add_transactions(
175        &self,
176        origin: TransactionOrigin,
177        transactions: Vec<Self::Transaction>,
178    ) -> impl Future<Output = Vec<PoolResult<AddedTransactionOutcome>>> + Send {
179        self.add_transactions_with_origins(transactions.into_iter().map(move |tx| (origin, tx)))
180    }
181
182    /// Adds the given _unvalidated_ transactions into the pool.
183    ///
184    /// Each transaction is paired with its own [`TransactionOrigin`].
185    ///
186    /// Returns a list of results.
187    ///
188    /// Consumer: RPC
189    fn add_transactions_with_origins(
190        &self,
191        transactions: impl IntoIterator<Item = (TransactionOrigin, Self::Transaction)> + Send,
192    ) -> impl Future<Output = Vec<PoolResult<AddedTransactionOutcome>>> + Send;
193
194    /// Submit a consensus transaction directly to the pool
195    fn add_consensus_transaction(
196        &self,
197        tx: Recovered<<Self::Transaction as PoolTransaction>::Consensus>,
198        origin: TransactionOrigin,
199    ) -> impl Future<Output = PoolResult<AddedTransactionOutcome>> + Send {
200        async move {
201            let tx_hash = *tx.tx_hash();
202
203            let pool_transaction = match Self::Transaction::try_from_consensus(tx) {
204                Ok(tx) => tx,
205                Err(e) => return Err(PoolError::other(tx_hash, e.to_string())),
206            };
207
208            self.add_transaction(origin, pool_transaction).await
209        }
210    }
211
212    /// Submit a consensus transaction and subscribe to event stream
213    fn add_consensus_transaction_and_subscribe(
214        &self,
215        tx: Recovered<<Self::Transaction as PoolTransaction>::Consensus>,
216        origin: TransactionOrigin,
217    ) -> impl Future<Output = PoolResult<TransactionEvents>> + Send {
218        async move {
219            let tx_hash = *tx.tx_hash();
220
221            let pool_transaction = match Self::Transaction::try_from_consensus(tx) {
222                Ok(tx) => tx,
223                Err(e) => return Err(PoolError::other(tx_hash, e.to_string())),
224            };
225
226            self.add_transaction_and_subscribe(origin, pool_transaction).await
227        }
228    }
229
230    /// Returns a new transaction change event stream for the given transaction.
231    ///
232    /// Returns `None` if the transaction is not in the pool.
233    fn transaction_event_listener(&self, tx_hash: TxHash) -> Option<TransactionEvents>;
234
235    /// Returns a new transaction change event stream for _all_ transactions in the pool.
236    fn all_transactions_event_listener(&self) -> AllTransactionsEvents<Self::Transaction>;
237
238    /// Returns a new Stream that yields transactions hashes for new __pending__ transactions
239    /// inserted into the pool that are allowed to be propagated.
240    ///
241    /// Note: This is intended for networking and will __only__ yield transactions that are allowed
242    /// to be propagated over the network, see also [`TransactionListenerKind`].
243    ///
244    /// Consumer: RPC/P2P
245    fn pending_transactions_listener(&self) -> Receiver<TxHash> {
246        self.pending_transactions_listener_for(TransactionListenerKind::PropagateOnly)
247    }
248
249    /// Returns a new [Receiver] that yields transactions hashes for new __pending__ transactions
250    /// inserted into the pending pool depending on the given [`TransactionListenerKind`] argument.
251    fn pending_transactions_listener_for(&self, kind: TransactionListenerKind) -> Receiver<TxHash>;
252
253    /// Returns a new stream that yields new valid transactions added to the pool.
254    fn new_transactions_listener(&self) -> Receiver<NewTransactionEvent<Self::Transaction>> {
255        self.new_transactions_listener_for(TransactionListenerKind::PropagateOnly)
256    }
257
258    /// Returns a new [Receiver] that yields blob "sidecars" (blobs w/ assoc. kzg
259    /// commitments/proofs) for eip-4844 transactions inserted into the pool
260    fn blob_transaction_sidecars_listener(&self) -> Receiver<NewBlobSidecar>;
261
262    /// Returns a new stream that yields new valid transactions added to the pool
263    /// depending on the given [`TransactionListenerKind`] argument.
264    fn new_transactions_listener_for(
265        &self,
266        kind: TransactionListenerKind,
267    ) -> Receiver<NewTransactionEvent<Self::Transaction>>;
268
269    /// Returns a new Stream that yields new transactions added to the pending sub-pool.
270    ///
271    /// This is a convenience wrapper around [`Self::new_transactions_listener`] that filters for
272    /// [`SubPool::Pending`](crate::SubPool).
273    fn new_pending_pool_transactions_listener(
274        &self,
275    ) -> NewSubpoolTransactionStream<Self::Transaction> {
276        NewSubpoolTransactionStream::new(
277            self.new_transactions_listener_for(TransactionListenerKind::PropagateOnly),
278            SubPool::Pending,
279        )
280    }
281
282    /// Returns a new Stream that yields new transactions added to the basefee sub-pool.
283    ///
284    /// This is a convenience wrapper around [`Self::new_transactions_listener`] that filters for
285    /// [`SubPool::BaseFee`](crate::SubPool).
286    fn new_basefee_pool_transactions_listener(
287        &self,
288    ) -> NewSubpoolTransactionStream<Self::Transaction> {
289        NewSubpoolTransactionStream::new(self.new_transactions_listener(), SubPool::BaseFee)
290    }
291
292    /// Returns a new Stream that yields new transactions added to the queued-pool.
293    ///
294    /// This is a convenience wrapper around [`Self::new_transactions_listener`] that filters for
295    /// [`SubPool::Queued`](crate::SubPool).
296    fn new_queued_transactions_listener(&self) -> NewSubpoolTransactionStream<Self::Transaction> {
297        NewSubpoolTransactionStream::new(self.new_transactions_listener(), SubPool::Queued)
298    }
299
300    /// Returns a new Stream that yields new transactions added to the blob sub-pool.
301    ///
302    /// This is a convenience wrapper around [`Self::new_transactions_listener`] that filters for
303    /// [`SubPool::Blob`](crate::SubPool).
304    fn new_blob_pool_transactions_listener(
305        &self,
306    ) -> NewSubpoolTransactionStream<Self::Transaction> {
307        NewSubpoolTransactionStream::new(self.new_transactions_listener(), SubPool::Blob)
308    }
309
310    /// Returns the _hashes_ of all transactions in the pool that are allowed to be propagated.
311    ///
312    /// This excludes hashes that aren't allowed to be propagated.
313    ///
314    /// Note: This returns a `Vec` but should guarantee that all hashes are unique.
315    ///
316    /// Consumer: P2P
317    fn pooled_transaction_hashes(&self) -> Vec<TxHash>;
318
319    /// Returns only the first `max` hashes of transactions in the pool.
320    ///
321    /// Consumer: P2P
322    fn pooled_transaction_hashes_max(&self, max: usize) -> Vec<TxHash>;
323
324    /// Returns the _full_ transaction objects all transactions in the pool that are allowed to be
325    /// propagated.
326    ///
327    /// This is intended to be used by the network for the initial exchange of pooled transaction
328    /// _hashes_
329    ///
330    /// Note: This returns a `Vec` but should guarantee that all transactions are unique.
331    ///
332    /// Caution: In case of blob transactions, this does not include the sidecar.
333    ///
334    /// Consumer: P2P
335    fn pooled_transactions(&self) -> Vec<Arc<ValidPoolTransaction<Self::Transaction>>>;
336
337    /// Returns only the first `max` transactions in the pool.
338    ///
339    /// Consumer: P2P
340    fn pooled_transactions_max(
341        &self,
342        max: usize,
343    ) -> Vec<Arc<ValidPoolTransaction<Self::Transaction>>>;
344
345    /// Returns converted [`PooledTransactionVariant`] for the given transaction hashes that are
346    /// allowed to be propagated.
347    ///
348    /// This adheres to the expected behavior of
349    /// [`GetPooledTransactions`](https://github.com/ethereum/devp2p/blob/master/caps/eth.md#getpooledtransactions-0x09):
350    ///
351    /// The transactions must be in same order as in the request, but it is OK to skip transactions
352    /// which are not available.
353    ///
354    /// If the transaction is a blob transaction, the sidecar will be included.
355    ///
356    /// Consumer: P2P
357    fn get_pooled_transaction_elements(
358        &self,
359        tx_hashes: Vec<TxHash>,
360        limit: GetPooledTransactionLimit,
361    ) -> Vec<<Self::Transaction as PoolTransaction>::Pooled>;
362
363    /// Extends the given vector with pooled transactions for the given hashes that are allowed to
364    /// be propagated.
365    ///
366    /// This adheres to the expected behavior of [`Self::get_pooled_transaction_elements`].
367    ///
368    /// Consumer: P2P
369    fn append_pooled_transaction_elements(
370        &self,
371        tx_hashes: &[TxHash],
372        limit: GetPooledTransactionLimit,
373        out: &mut Vec<<Self::Transaction as PoolTransaction>::Pooled>,
374    ) {
375        out.extend(self.get_pooled_transaction_elements(tx_hashes.to_vec(), limit));
376    }
377
378    /// Returns the pooled transaction variant for the given transaction hash.
379    ///
380    /// This adheres to the expected behavior of
381    /// [`GetPooledTransactions`](https://github.com/ethereum/devp2p/blob/master/caps/eth.md#getpooledtransactions-0x09):
382    ///
383    /// If the transaction is a blob transaction, the sidecar will be included.
384    ///
385    /// It is expected that this variant represents the valid p2p format for full transactions.
386    /// E.g. for EIP-4844 transactions this is the consensus transaction format with the blob
387    /// sidecar.
388    ///
389    /// Consumer: P2P
390    fn get_pooled_transaction_element(
391        &self,
392        tx_hash: TxHash,
393    ) -> Option<Recovered<<Self::Transaction as PoolTransaction>::Pooled>>;
394
395    /// Returns an iterator that yields transactions that are ready for block production.
396    ///
397    /// Consumer: Block production
398    fn best_transactions(
399        &self,
400    ) -> Box<dyn BestTransactions<Item = Arc<ValidPoolTransaction<Self::Transaction>>>>;
401
402    /// Returns an iterator that yields transactions that are ready for block production with the
403    /// given base fee and optional blob fee attributes.
404    ///
405    /// Consumer: Block production
406    fn best_transactions_with_attributes(
407        &self,
408        best_transactions_attributes: BestTransactionsAttributes,
409    ) -> Box<dyn BestTransactions<Item = Arc<ValidPoolTransaction<Self::Transaction>>>>;
410
411    /// Returns all transactions that can be included in the next block.
412    ///
413    /// This is primarily used for the `txpool_` RPC namespace:
414    /// <https://geth.ethereum.org/docs/interacting-with-geth/rpc/ns-txpool> which distinguishes
415    /// between `pending` and `queued` transactions, where `pending` are transactions ready for
416    /// inclusion in the next block and `queued` are transactions that are ready for inclusion in
417    /// future blocks.
418    ///
419    /// Consumer: RPC
420    fn pending_transactions(&self) -> Vec<Arc<ValidPoolTransaction<Self::Transaction>>>;
421
422    /// Returns a pending transaction if it exists and is ready for immediate execution
423    /// (i.e., has the lowest nonce among the sender's pending transactions).
424    fn get_pending_transaction_by_sender_and_nonce(
425        &self,
426        sender: Address,
427        nonce: u64,
428    ) -> Option<Arc<ValidPoolTransaction<Self::Transaction>>> {
429        self.best_transactions().find(|tx| tx.sender() == sender && tx.nonce() == nonce)
430    }
431
432    /// Returns first `max` transactions that can be included in the next block.
433    /// See <https://github.com/paradigmxyz/reth/issues/12767#issuecomment-2493223579>
434    ///
435    /// Consumer: Block production
436    fn pending_transactions_max(
437        &self,
438        max: usize,
439    ) -> Vec<Arc<ValidPoolTransaction<Self::Transaction>>>;
440
441    /// Returns all transactions that can be included in _future_ blocks.
442    ///
443    /// This and [`Self::pending_transactions`] are mutually exclusive.
444    ///
445    /// Consumer: RPC
446    fn queued_transactions(&self) -> Vec<Arc<ValidPoolTransaction<Self::Transaction>>>;
447
448    /// Returns the number of transactions that are ready for inclusion in the next block and the
449    /// number of transactions that are ready for inclusion in future blocks: `(pending, queued)`.
450    fn pending_and_queued_txn_count(&self) -> (usize, usize);
451
452    /// Returns all transactions that are currently in the pool grouped by whether they are ready
453    /// for inclusion in the next block or not.
454    ///
455    /// This is primarily used for the `txpool_` namespace: <https://geth.ethereum.org/docs/interacting-with-geth/rpc/ns-txpool>
456    ///
457    /// Consumer: RPC
458    fn all_transactions(&self) -> AllPoolTransactions<Self::Transaction>;
459
460    /// Returns the _hashes_ of all transactions regardless of whether they can be propagated or
461    /// not.
462    ///
463    /// Unlike [`Self::pooled_transaction_hashes`] this doesn't consider whether the transaction can
464    /// be propagated or not.
465    ///
466    /// Note: This returns a `Vec` but should guarantee that all hashes are unique.
467    ///
468    /// Consumer: Utility
469    fn all_transaction_hashes(&self) -> Vec<TxHash>;
470
471    /// Removes a single transaction corresponding to the given hash.
472    ///
473    /// Note: This removes the transaction as if it got discarded (_not_ mined).
474    ///
475    /// Returns the removed transaction if it was found in the pool.
476    ///
477    /// Consumer: Utility
478    fn remove_transaction(
479        &self,
480        hash: TxHash,
481    ) -> Option<Arc<ValidPoolTransaction<Self::Transaction>>> {
482        self.remove_transactions(vec![hash]).pop()
483    }
484
485    /// Removes all transactions corresponding to the given hashes.
486    ///
487    /// Note: This removes the transactions as if they got discarded (_not_ mined).
488    ///
489    /// Consumer: Utility
490    fn remove_transactions(
491        &self,
492        hashes: Vec<TxHash>,
493    ) -> Vec<Arc<ValidPoolTransaction<Self::Transaction>>>;
494
495    /// Removes all transactions corresponding to the given hashes.
496    ///
497    /// Also removes all _dependent_ transactions.
498    ///
499    /// Consumer: Utility
500    fn remove_transactions_and_descendants(
501        &self,
502        hashes: Vec<TxHash>,
503    ) -> Vec<Arc<ValidPoolTransaction<Self::Transaction>>>;
504
505    /// Removes all transactions from the given sender
506    ///
507    /// Consumer: Utility
508    fn remove_transactions_by_sender(
509        &self,
510        sender: Address,
511    ) -> Vec<Arc<ValidPoolTransaction<Self::Transaction>>>;
512
513    /// Prunes a single transaction from the pool.
514    ///
515    /// This is similar to [`Self::remove_transaction`] but treats the transaction as _mined_
516    /// rather than discarded. The key difference is that pruning does **not** park descendant
517    /// transactions: their nonce requirements are considered satisfied, so they remain in whatever
518    /// sub-pool they currently occupy and can be included in the next block.
519    ///
520    /// In contrast, [`Self::remove_transaction`] treats the removal as a discard, which
521    /// introduces a nonce gap and moves all descendant transactions to the queued (parked)
522    /// sub-pool.
523    ///
524    /// Returns the pruned transaction if it existed in the pool.
525    ///
526    /// Consumer: Utility
527    fn prune_transaction(
528        &self,
529        hash: TxHash,
530    ) -> Option<Arc<ValidPoolTransaction<Self::Transaction>>> {
531        self.prune_transactions(vec![hash]).pop()
532    }
533
534    /// Prunes all transactions corresponding to the given hashes from the pool.
535    ///
536    /// This behaves like [`Self::prune_transaction`] but for multiple transactions at once.
537    /// Each transaction is removed as if it was mined: descendant transactions are **not** parked
538    /// and their nonce requirements are considered satisfied.
539    ///
540    /// This is useful for scenarios like Flashblocks where transactions are committed across
541    /// multiple partial blocks without a canonical state update: previously committed transactions
542    /// can be pruned so that the best-transactions iterator yields their descendants in the
543    /// correct priority order.
544    ///
545    /// Consumer: Utility
546    fn prune_transactions(
547        &self,
548        hashes: Vec<TxHash>,
549    ) -> Vec<Arc<ValidPoolTransaction<Self::Transaction>>>;
550
551    /// Retains only those hashes that are unknown to the pool.
552    /// In other words, removes all transactions from the given set that are currently present in
553    /// the pool. Returns hashes already known to the pool.
554    ///
555    /// Consumer: P2P
556    fn retain_unknown<A>(&self, announcement: &mut A)
557    where
558        A: HandleMempoolData;
559
560    /// Returns if the transaction for the given hash is already included in this pool.
561    fn contains(&self, tx_hash: &TxHash) -> bool {
562        self.get(tx_hash).is_some()
563    }
564
565    /// Returns the transaction for the given hash.
566    fn get(&self, tx_hash: &TxHash) -> Option<Arc<ValidPoolTransaction<Self::Transaction>>>;
567
568    /// Returns all transactions objects for the given hashes.
569    ///
570    /// Caution: This in case of blob transactions, this does not include the sidecar.
571    fn get_all(&self, txs: Vec<TxHash>) -> Vec<Arc<ValidPoolTransaction<Self::Transaction>>>;
572
573    /// Notify the pool about transactions that are propagated to peers.
574    ///
575    /// Consumer: P2P
576    fn on_propagated(&self, txs: PropagatedTransactions);
577
578    /// Returns all transactions sent by a given user
579    fn get_transactions_by_sender(
580        &self,
581        sender: Address,
582    ) -> Vec<Arc<ValidPoolTransaction<Self::Transaction>>>;
583
584    /// Returns all pending transactions filtered by predicate
585    fn get_pending_transactions_with_predicate(
586        &self,
587        predicate: impl FnMut(&ValidPoolTransaction<Self::Transaction>) -> bool,
588    ) -> Vec<Arc<ValidPoolTransaction<Self::Transaction>>>;
589
590    /// Returns all pending transactions sent by a given user
591    fn get_pending_transactions_by_sender(
592        &self,
593        sender: Address,
594    ) -> Vec<Arc<ValidPoolTransaction<Self::Transaction>>>;
595
596    /// Returns all queued transactions sent by a given user
597    fn get_queued_transactions_by_sender(
598        &self,
599        sender: Address,
600    ) -> Vec<Arc<ValidPoolTransaction<Self::Transaction>>>;
601
602    /// Returns the highest transaction sent by a given user
603    fn get_highest_transaction_by_sender(
604        &self,
605        sender: Address,
606    ) -> Option<Arc<ValidPoolTransaction<Self::Transaction>>>;
607
608    /// Returns the transaction with the highest nonce that is executable given the on chain nonce.
609    /// In other words the highest non nonce gapped transaction.
610    ///
611    /// Note: The next pending pooled transaction must have the on chain nonce.
612    ///
613    /// For example, for a given on chain nonce of `5`, the next transaction must have that nonce.
614    /// If the pool contains txs `[5,6,7]` this returns tx `7`.
615    /// If the pool contains txs `[6,7]` this returns `None` because the next valid nonce (5) is
616    /// missing, which means txs `[6,7]` are nonce gapped.
617    fn get_highest_consecutive_transaction_by_sender(
618        &self,
619        sender: Address,
620        on_chain_nonce: u64,
621    ) -> Option<Arc<ValidPoolTransaction<Self::Transaction>>>;
622
623    /// Returns a transaction sent by a given user and a nonce
624    fn get_transaction_by_sender_and_nonce(
625        &self,
626        sender: Address,
627        nonce: u64,
628    ) -> Option<Arc<ValidPoolTransaction<Self::Transaction>>>;
629
630    /// Returns all transactions that where submitted with the given [`TransactionOrigin`]
631    fn get_transactions_by_origin(
632        &self,
633        origin: TransactionOrigin,
634    ) -> Vec<Arc<ValidPoolTransaction<Self::Transaction>>>;
635
636    /// Returns all pending transactions filtered by [`TransactionOrigin`]
637    fn get_pending_transactions_by_origin(
638        &self,
639        origin: TransactionOrigin,
640    ) -> Vec<Arc<ValidPoolTransaction<Self::Transaction>>>;
641
642    /// Returns all transactions that where submitted as [`TransactionOrigin::Local`]
643    fn get_local_transactions(&self) -> Vec<Arc<ValidPoolTransaction<Self::Transaction>>> {
644        self.get_transactions_by_origin(TransactionOrigin::Local)
645    }
646
647    /// Returns all transactions that where submitted as [`TransactionOrigin::Private`]
648    fn get_private_transactions(&self) -> Vec<Arc<ValidPoolTransaction<Self::Transaction>>> {
649        self.get_transactions_by_origin(TransactionOrigin::Private)
650    }
651
652    /// Returns all transactions that where submitted as [`TransactionOrigin::External`]
653    fn get_external_transactions(&self) -> Vec<Arc<ValidPoolTransaction<Self::Transaction>>> {
654        self.get_transactions_by_origin(TransactionOrigin::External)
655    }
656
657    /// Returns all pending transactions that where submitted as [`TransactionOrigin::Local`]
658    fn get_local_pending_transactions(&self) -> Vec<Arc<ValidPoolTransaction<Self::Transaction>>> {
659        self.get_pending_transactions_by_origin(TransactionOrigin::Local)
660    }
661
662    /// Returns all pending transactions that where submitted as [`TransactionOrigin::Private`]
663    fn get_private_pending_transactions(
664        &self,
665    ) -> Vec<Arc<ValidPoolTransaction<Self::Transaction>>> {
666        self.get_pending_transactions_by_origin(TransactionOrigin::Private)
667    }
668
669    /// Returns all pending transactions that where submitted as [`TransactionOrigin::External`]
670    fn get_external_pending_transactions(
671        &self,
672    ) -> Vec<Arc<ValidPoolTransaction<Self::Transaction>>> {
673        self.get_pending_transactions_by_origin(TransactionOrigin::External)
674    }
675
676    /// Returns a set of all senders of transactions in the pool
677    fn unique_senders(&self) -> AddressSet;
678
679    /// Returns the [`BlobTransactionSidecarVariant`] for the given transaction hash if it exists in
680    /// the blob store.
681    fn get_blob(
682        &self,
683        tx_hash: TxHash,
684    ) -> Result<Option<Arc<BlobTransactionSidecarVariant>>, BlobStoreError>;
685
686    /// Returns all [`BlobTransactionSidecarVariant`] for the given transaction hashes if they
687    /// exists in the blob store.
688    ///
689    /// This only returns the blobs that were found in the store.
690    /// If there's no blob it will not be returned.
691    fn get_all_blobs(
692        &self,
693        tx_hashes: Vec<TxHash>,
694    ) -> Result<Vec<(TxHash, Arc<BlobTransactionSidecarVariant>)>, BlobStoreError>;
695
696    /// Returns the exact [`BlobTransactionSidecarVariant`] for the given transaction hashes in the
697    /// order they were requested.
698    ///
699    /// Returns an error if any of the blobs are not found in the blob store.
700    fn get_all_blobs_exact(
701        &self,
702        tx_hashes: Vec<TxHash>,
703    ) -> Result<Vec<Arc<BlobTransactionSidecarVariant>>, BlobStoreError>;
704
705    /// Return the [`BlobAndProofV1`]s for a list of blob versioned hashes.
706    fn get_blobs_for_versioned_hashes_v1(
707        &self,
708        versioned_hashes: &[B256],
709    ) -> Result<Vec<Option<BlobAndProofV1>>, BlobStoreError>;
710
711    /// Return the [`BlobAndProofV2`]s for a list of blob versioned hashes.
712    /// Blobs and proofs are returned only if they are present for _all_ of the requested versioned
713    /// hashes.
714    fn get_blobs_for_versioned_hashes_v2(
715        &self,
716        versioned_hashes: &[B256],
717    ) -> Result<Option<Vec<BlobAndProofV2>>, BlobStoreError>;
718
719    /// Return the [`BlobAndProofV2`]s for a list of blob versioned hashes.
720    ///
721    /// The response is always the same length as the request. Missing or older-version blobs are
722    /// returned as `None` elements.
723    fn get_blobs_for_versioned_hashes_v3(
724        &self,
725        versioned_hashes: &[B256],
726    ) -> Result<Vec<Option<BlobAndProofV2>>, BlobStoreError>;
727}
728
729/// Extension for [`TransactionPool`] trait that allows to set the current block info.
730#[auto_impl::auto_impl(&, Arc)]
731pub trait TransactionPoolExt: TransactionPool {
732    /// The block type used for chain tip updates.
733    type Block: Block;
734
735    /// Sets the current block info for the pool.
736    fn set_block_info(&self, info: BlockInfo);
737
738    /// Event listener for when the pool needs to be updated.
739    ///
740    /// Implementers need to update the pool accordingly:
741    ///
742    /// ## Fee changes
743    ///
744    /// The [`CanonicalStateUpdate`] includes the base and blob fee of the pending block, which
745    /// affects the dynamic fee requirement of pending transactions in the pool.
746    ///
747    /// ## EIP-4844 Blob transactions
748    ///
749    /// Mined blob transactions need to be removed from the pool, but from the pool only. The blob
750    /// sidecar must not be removed from the blob store. Only after a blob transaction is
751    /// finalized, its sidecar is removed from the blob store. This ensures that in case of a reorg,
752    /// the sidecar is still available.
753    fn on_canonical_state_change(&self, update: CanonicalStateUpdate<'_, Self::Block>);
754
755    /// Updates the accounts in the pool
756    fn update_accounts(&self, accounts: Vec<ChangedAccount>);
757
758    /// Deletes the blob sidecar for the given transaction from the blob store
759    fn delete_blob(&self, tx: B256);
760
761    /// Deletes multiple blob sidecars from the blob store
762    fn delete_blobs(&self, txs: Vec<B256>);
763
764    /// Maintenance function to cleanup blobs that are no longer needed.
765    fn cleanup_blobs(&self);
766}
767
768/// A Helper type that bundles all transactions in the pool.
769#[derive(Debug, Clone)]
770pub struct AllPoolTransactions<T: PoolTransaction> {
771    /// Transactions that are ready for inclusion in the next block.
772    pub pending: Vec<Arc<ValidPoolTransaction<T>>>,
773    /// Transactions that are ready for inclusion in _future_ blocks, but are currently parked,
774    /// because they depend on other transactions that are not yet included in the pool (nonce gap)
775    /// or otherwise blocked.
776    pub queued: Vec<Arc<ValidPoolTransaction<T>>>,
777}
778
779// === impl AllPoolTransactions ===
780
781impl<T: PoolTransaction> AllPoolTransactions<T> {
782    /// Returns the combined number of all transactions.
783    pub const fn count(&self) -> usize {
784        self.pending.len() + self.queued.len()
785    }
786
787    /// Returns an iterator over all pending [`Recovered`] transactions.
788    pub fn pending_recovered(&self) -> impl Iterator<Item = Recovered<T::Consensus>> + '_ {
789        self.pending.iter().map(|tx| tx.transaction.clone_into_consensus())
790    }
791
792    /// Returns an iterator over all queued [`Recovered`] transactions.
793    pub fn queued_recovered(&self) -> impl Iterator<Item = Recovered<T::Consensus>> + '_ {
794        self.queued.iter().map(|tx| tx.transaction.clone_into_consensus())
795    }
796
797    /// Returns an iterator over all transactions, both pending and queued.
798    pub fn all(&self) -> impl Iterator<Item = Recovered<T::Consensus>> + '_ {
799        self.pending
800            .iter()
801            .chain(self.queued.iter())
802            .map(|tx| tx.transaction.clone_into_consensus())
803    }
804}
805
806impl<T: PoolTransaction> Default for AllPoolTransactions<T> {
807    fn default() -> Self {
808        Self { pending: Default::default(), queued: Default::default() }
809    }
810}
811
812impl<T: PoolTransaction> IntoIterator for AllPoolTransactions<T> {
813    type Item = Arc<ValidPoolTransaction<T>>;
814    type IntoIter = std::iter::Chain<
815        std::vec::IntoIter<Arc<ValidPoolTransaction<T>>>,
816        std::vec::IntoIter<Arc<ValidPoolTransaction<T>>>,
817    >;
818
819    fn into_iter(self) -> Self::IntoIter {
820        self.pending.into_iter().chain(self.queued)
821    }
822}
823
824/// Represents transactions that were propagated over the network.
825#[derive(Debug, Clone, Eq, PartialEq, Default)]
826pub struct PropagatedTransactions(pub HashMap<TxHash, Vec<PropagateKind>>);
827
828/// Represents how a transaction was propagated over the network.
829#[derive(Debug, Copy, Clone, Eq, PartialEq)]
830#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
831pub enum PropagateKind {
832    /// The full transaction object was sent to the peer.
833    ///
834    /// This is equivalent to the `Transaction` message
835    Full(PeerId),
836    /// Only the Hash was propagated to the peer.
837    Hash(PeerId),
838}
839
840// === impl PropagateKind ===
841
842impl PropagateKind {
843    /// Returns the peer the transaction was sent to
844    pub const fn peer(&self) -> &PeerId {
845        match self {
846            Self::Full(peer) | Self::Hash(peer) => peer,
847        }
848    }
849
850    /// Returns true if the transaction was sent as a full transaction
851    pub const fn is_full(&self) -> bool {
852        matches!(self, Self::Full(_))
853    }
854
855    /// Returns true if the transaction was sent as a hash
856    pub const fn is_hash(&self) -> bool {
857        matches!(self, Self::Hash(_))
858    }
859}
860
861impl From<PropagateKind> for PeerId {
862    fn from(value: PropagateKind) -> Self {
863        match value {
864            PropagateKind::Full(peer) | PropagateKind::Hash(peer) => peer,
865        }
866    }
867}
868
869/// This type represents a new blob sidecar that has been stored in the transaction pool's
870/// blobstore; it includes the `TransactionHash` of the blob transaction along with the assoc.
871/// sidecar (blobs, commitments, proofs)
872#[derive(Debug, Clone)]
873pub struct NewBlobSidecar {
874    /// hash of the EIP-4844 transaction.
875    pub tx_hash: TxHash,
876    /// the blob transaction sidecar.
877    pub sidecar: Arc<BlobTransactionSidecarVariant>,
878}
879
880/// Where the transaction originates from.
881///
882/// Depending on where the transaction was picked up, it affects how the transaction is handled
883/// internally, e.g. limits for simultaneous transaction of one sender.
884#[derive(Debug, Copy, Clone, PartialEq, Eq, Default, Deserialize, Serialize)]
885pub enum TransactionOrigin {
886    /// Transaction is coming from a local source.
887    #[default]
888    Local,
889    /// Transaction has been received externally.
890    ///
891    /// This is usually considered an "untrusted" source, for example received from another in the
892    /// network.
893    External,
894    /// Transaction is originated locally and is intended to remain private.
895    ///
896    /// This type of transaction should not be propagated to the network. It's meant for
897    /// private usage within the local node only.
898    Private,
899}
900
901// === impl TransactionOrigin ===
902
903impl TransactionOrigin {
904    /// Whether the transaction originates from a local source.
905    pub const fn is_local(&self) -> bool {
906        matches!(self, Self::Local)
907    }
908
909    /// Whether the transaction originates from an external source.
910    pub const fn is_external(&self) -> bool {
911        matches!(self, Self::External)
912    }
913    /// Whether the transaction originates from a private source.
914    pub const fn is_private(&self) -> bool {
915        matches!(self, Self::Private)
916    }
917}
918
919/// Represents the kind of update to the canonical state.
920#[derive(Debug, Clone, Copy, PartialEq, Eq)]
921pub enum PoolUpdateKind {
922    /// The update was due to a block commit.
923    Commit,
924    /// The update was due to a reorganization.
925    Reorg,
926}
927
928/// Represents changes after a new canonical block or range of canonical blocks was added to the
929/// chain.
930///
931/// It is expected that this is only used if the added blocks are canonical to the pool's last known
932/// block hash. In other words, the first added block of the range must be the child of the last
933/// known block hash.
934///
935/// This is used to update the pool state accordingly.
936#[derive(Clone, Debug)]
937pub struct CanonicalStateUpdate<'a, B: Block> {
938    /// Hash of the tip block.
939    pub new_tip: &'a SealedBlock<B>,
940    /// EIP-1559 Base fee of the _next_ (pending) block
941    ///
942    /// The base fee of a block depends on the utilization of the last block and its base fee.
943    pub pending_block_base_fee: u64,
944    /// EIP-4844 blob fee of the _next_ (pending) block
945    ///
946    /// Only after Cancun
947    pub pending_block_blob_fee: Option<u128>,
948    /// A set of changed accounts across a range of blocks.
949    pub changed_accounts: Vec<ChangedAccount>,
950    /// All mined transactions in the block range.
951    pub mined_transactions: Vec<B256>,
952    /// The kind of update to the canonical state.
953    pub update_kind: PoolUpdateKind,
954}
955
956impl<B> CanonicalStateUpdate<'_, B>
957where
958    B: Block,
959{
960    /// Returns the number of the tip block.
961    pub fn number(&self) -> u64 {
962        self.new_tip.number()
963    }
964
965    /// Returns the hash of the tip block.
966    pub fn hash(&self) -> B256 {
967        self.new_tip.hash()
968    }
969
970    /// Timestamp of the latest chain update
971    pub fn timestamp(&self) -> u64 {
972        self.new_tip.timestamp()
973    }
974
975    /// Returns the block info for the tip block.
976    pub fn block_info(&self) -> BlockInfo {
977        BlockInfo {
978            block_gas_limit: self.new_tip.gas_limit(),
979            last_seen_block_hash: self.hash(),
980            last_seen_block_number: self.number(),
981            pending_basefee: self.pending_block_base_fee,
982            pending_blob_fee: self.pending_block_blob_fee,
983        }
984    }
985}
986
987impl<B> fmt::Display for CanonicalStateUpdate<'_, B>
988where
989    B: Block,
990{
991    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
992        f.debug_struct("CanonicalStateUpdate")
993            .field("hash", &self.hash())
994            .field("number", &self.number())
995            .field("pending_block_base_fee", &self.pending_block_base_fee)
996            .field("pending_block_blob_fee", &self.pending_block_blob_fee)
997            .field("changed_accounts", &self.changed_accounts.len())
998            .field("mined_transactions", &self.mined_transactions.len())
999            .finish()
1000    }
1001}
1002
1003/// Alias to restrict the [`BestTransactions`] items to the pool's transaction type.
1004pub type BestTransactionsFor<Pool> = Box<
1005    dyn BestTransactions<Item = Arc<ValidPoolTransaction<<Pool as TransactionPool>::Transaction>>>,
1006>;
1007
1008/// An `Iterator` that only returns transactions that are ready to be executed.
1009///
1010/// This makes no assumptions about the order of the transactions, but expects that _all_
1011/// transactions are valid (no nonce gaps.) for the tracked state of the pool.
1012///
1013/// Note: this iterator will always return the best transaction that it currently knows.
1014/// There is no guarantee transactions will be returned sequentially in decreasing
1015/// priority order.
1016pub trait BestTransactions: Iterator + Send {
1017    /// Mark the transaction as invalid.
1018    ///
1019    /// Implementers must ensure all subsequent transaction _don't_ depend on this transaction.
1020    /// In other words, this must remove the given transaction _and_ drain all transaction that
1021    /// depend on it.
1022    fn mark_invalid(&mut self, transaction: &Self::Item, kind: &InvalidPoolTransactionError);
1023
1024    /// An iterator may be able to receive additional pending transactions that weren't present it
1025    /// the pool when it was created.
1026    ///
1027    /// This ensures that iterator will return the best transaction that it currently knows and not
1028    /// listen to pool updates.
1029    fn no_updates(&mut self);
1030
1031    /// Convenience function for [`Self::no_updates`] that returns the iterator again.
1032    fn without_updates(mut self) -> Self
1033    where
1034        Self: Sized,
1035    {
1036        self.no_updates();
1037        self
1038    }
1039
1040    /// Skip all blob transactions.
1041    ///
1042    /// There's only limited blob space available in a block, once exhausted, EIP-4844 transactions
1043    /// can no longer be included.
1044    ///
1045    /// If called then the iterator will no longer yield blob transactions.
1046    ///
1047    /// Note: this will also exclude any transactions that depend on blob transactions.
1048    fn skip_blobs(&mut self) {
1049        self.set_skip_blobs(true);
1050    }
1051
1052    /// Controls whether the iterator skips blob transactions or not.
1053    ///
1054    /// If set to true, no blob transactions will be returned.
1055    fn set_skip_blobs(&mut self, skip_blobs: bool);
1056
1057    /// Convenience function for [`Self::skip_blobs`] that returns the iterator again.
1058    fn without_blobs(mut self) -> Self
1059    where
1060        Self: Sized,
1061    {
1062        self.skip_blobs();
1063        self
1064    }
1065
1066    /// Creates an iterator which uses a closure to determine whether a transaction should be
1067    /// returned by the iterator.
1068    ///
1069    /// All items the closure returns false for are marked as invalid via [`Self::mark_invalid`] and
1070    /// descendant transactions will be skipped.
1071    fn filter_transactions<P>(self, predicate: P) -> BestTransactionFilter<Self, P>
1072    where
1073        P: FnMut(&Self::Item) -> bool,
1074        Self: Sized,
1075    {
1076        BestTransactionFilter::new(self, predicate)
1077    }
1078}
1079
1080impl<T> BestTransactions for Box<T>
1081where
1082    T: BestTransactions + ?Sized,
1083{
1084    fn mark_invalid(&mut self, transaction: &Self::Item, kind: &InvalidPoolTransactionError) {
1085        (**self).mark_invalid(transaction, kind)
1086    }
1087
1088    fn no_updates(&mut self) {
1089        (**self).no_updates();
1090    }
1091
1092    fn skip_blobs(&mut self) {
1093        (**self).skip_blobs();
1094    }
1095
1096    fn set_skip_blobs(&mut self, skip_blobs: bool) {
1097        (**self).set_skip_blobs(skip_blobs);
1098    }
1099}
1100
1101/// A no-op implementation that yields no transactions.
1102impl<T> BestTransactions for std::iter::Empty<T> {
1103    fn mark_invalid(&mut self, _tx: &T, _kind: &InvalidPoolTransactionError) {}
1104
1105    fn no_updates(&mut self) {}
1106
1107    fn skip_blobs(&mut self) {}
1108
1109    fn set_skip_blobs(&mut self, _skip_blobs: bool) {}
1110}
1111
1112/// A filter that allows to check if a transaction satisfies a set of conditions
1113pub trait TransactionFilter {
1114    /// The type of the transaction to check.
1115    type Transaction;
1116
1117    /// Returns true if the transaction satisfies the conditions.
1118    fn is_valid(&self, transaction: &Self::Transaction) -> bool;
1119}
1120
1121/// A no-op implementation of [`TransactionFilter`] which
1122/// marks all transactions as valid.
1123#[derive(Debug, Clone)]
1124pub struct NoopTransactionFilter<T>(std::marker::PhantomData<T>);
1125
1126// We can't derive Default because this forces T to be
1127// Default as well, which isn't necessary.
1128impl<T> Default for NoopTransactionFilter<T> {
1129    fn default() -> Self {
1130        Self(std::marker::PhantomData)
1131    }
1132}
1133
1134impl<T> TransactionFilter for NoopTransactionFilter<T> {
1135    type Transaction = T;
1136
1137    fn is_valid(&self, _transaction: &Self::Transaction) -> bool {
1138        true
1139    }
1140}
1141
1142/// A Helper type that bundles the best transactions attributes together.
1143#[derive(Debug, Copy, Clone, PartialEq, Eq)]
1144pub struct BestTransactionsAttributes {
1145    /// The base fee attribute for best transactions.
1146    pub basefee: u64,
1147    /// The blob fee attribute for best transactions.
1148    pub blob_fee: Option<u64>,
1149}
1150
1151// === impl BestTransactionsAttributes ===
1152
1153impl BestTransactionsAttributes {
1154    /// Creates a new `BestTransactionsAttributes` with the given basefee and blob fee.
1155    pub const fn new(basefee: u64, blob_fee: Option<u64>) -> Self {
1156        Self { basefee, blob_fee }
1157    }
1158
1159    /// Creates a new `BestTransactionsAttributes` with the given basefee.
1160    pub const fn base_fee(basefee: u64) -> Self {
1161        Self::new(basefee, None)
1162    }
1163
1164    /// Sets the given blob fee.
1165    pub const fn with_blob_fee(mut self, blob_fee: u64) -> Self {
1166        self.blob_fee = Some(blob_fee);
1167        self
1168    }
1169}
1170
1171/// Trait for transaction types stored in the transaction pool.
1172///
1173/// This trait represents the actual transaction object stored in the mempool, which includes not
1174/// only the transaction data itself but also additional metadata needed for efficient pool
1175/// operations. Implementations typically cache values that are frequently accessed during
1176/// transaction ordering, validation, and eviction.
1177///
1178/// ## Key Responsibilities
1179///
1180/// 1. **Metadata Caching**: Store computed values like address, cost and encoded size
1181/// 2. **Representation Conversion**: Handle conversions between consensus and pooled
1182///    representations
1183/// 3. **Validation Support**: Provide methods for pool-specific validation rules
1184///
1185/// ## Cached Metadata
1186///
1187/// Implementations should cache frequently accessed values to avoid recomputation:
1188/// - **Address**: Recovered sender address of the transaction
1189/// - **Cost**: Max amount spendable (gas × price + value + blob costs)
1190/// - **Size**: RLP encoded length for mempool size limits
1191///
1192/// See [`EthPooledTransaction`] for a reference implementation.
1193///
1194/// ## Transaction Representations
1195///
1196/// This trait abstracts over the different representations a transaction can have:
1197///
1198/// 1. **Consensus representation** (`Consensus` associated type): The canonical form included in
1199///    blocks
1200///    - Compact representation without networking metadata
1201///    - For EIP-4844: includes only blob hashes, not the actual blobs
1202///    - Used for block execution and state transitions
1203///
1204/// 2. **Pooled representation** (`Pooled` associated type): The form used for network propagation
1205///    - May include additional data for validation
1206///    - For EIP-4844: includes full blob sidecars (blobs, commitments, proofs)
1207///    - Used for mempool validation and p2p gossiping
1208///
1209/// ## Why Two Representations?
1210///
1211/// This distinction is necessary because:
1212///
1213/// - **EIP-4844 blob transactions**: Require large blob sidecars for validation that would bloat
1214///   blocks if included. Only blob hashes are stored on-chain.
1215///
1216/// - **Network efficiency**: Blob transactions are not broadcast to all peers automatically but
1217///   must be explicitly requested to reduce bandwidth usage.
1218///
1219/// - **Special transactions**: Some transactions (like OP deposit transactions) exist only in
1220///   consensus format and are never in the mempool.
1221///
1222/// ## Conversion Rules
1223///
1224/// - `Consensus` → `Pooled`: May fail for transactions that cannot be pooled (e.g., OP deposit
1225///   transactions, blob transactions without sidecars)
1226/// - `Pooled` → `Consensus`: Always succeeds (pooled is a superset)
1227pub trait PoolTransaction:
1228    alloy_consensus::Transaction + InMemorySize + Debug + Send + Sync + Clone
1229{
1230    /// Associated error type for the `try_from_consensus` method.
1231    type TryFromConsensusError: fmt::Display;
1232
1233    /// Associated type representing the raw consensus variant of the transaction.
1234    type Consensus: SignedTransaction + From<Self::Pooled>;
1235
1236    /// Associated type representing the recovered pooled variant of the transaction.
1237    type Pooled: TryFrom<Self::Consensus, Error = Self::TryFromConsensusError> + SignedTransaction;
1238
1239    /// Define a method to convert from the `Consensus` type to `Self`
1240    ///
1241    /// This conversion may fail for transactions that are valid for inclusion in blocks
1242    /// but cannot exist in the transaction pool. Examples include:
1243    ///
1244    /// - **OP Deposit transactions**: These are special system transactions that are directly
1245    ///   included in blocks by the sequencer/validator and never enter the mempool
1246    /// - **Blob transactions without sidecars**: After being included in a block, the sidecar data
1247    ///   is pruned, making the consensus transaction unpoolable
1248    fn try_from_consensus(
1249        tx: Recovered<Self::Consensus>,
1250    ) -> Result<Self, Self::TryFromConsensusError> {
1251        let (tx, signer) = tx.into_parts();
1252        Ok(Self::from_pooled(Recovered::new_unchecked(tx.try_into()?, signer)))
1253    }
1254
1255    /// Clone the transaction into a consensus variant.
1256    ///
1257    /// This method is preferred when the [`PoolTransaction`] already wraps the consensus variant.
1258    fn clone_into_consensus(&self) -> Recovered<Self::Consensus> {
1259        self.clone().into_consensus()
1260    }
1261
1262    /// Define a method to convert from the `Self` type to `Consensus`
1263    fn into_consensus(self) -> Recovered<Self::Consensus>;
1264
1265    /// Converts the transaction into consensus format while preserving the EIP-2718 encoded bytes.
1266    /// This is used to optimize transaction execution by reusing cached encoded bytes instead of
1267    /// re-encoding the transaction. The cached bytes are particularly useful in payload building
1268    /// where the same transaction may be executed multiple times.
1269    fn into_consensus_with2718(self) -> WithEncoded<Recovered<Self::Consensus>> {
1270        self.into_consensus().into_encoded()
1271    }
1272
1273    /// Define a method to convert from the `Pooled` type to `Self`
1274    fn from_pooled(pooled: Recovered<Self::Pooled>) -> Self;
1275
1276    /// Tries to convert the `Consensus` type into the `Pooled` type.
1277    fn try_into_pooled(self) -> Result<Recovered<Self::Pooled>, Self::TryFromConsensusError> {
1278        let consensus = self.into_consensus();
1279        let (tx, signer) = consensus.into_parts();
1280        Ok(Recovered::new_unchecked(tx.try_into()?, signer))
1281    }
1282
1283    /// Clones the consensus transactions and tries to convert the `Consensus` type into the
1284    /// `Pooled` type.
1285    fn clone_into_pooled(&self) -> Result<Recovered<Self::Pooled>, Self::TryFromConsensusError> {
1286        let consensus = self.clone_into_consensus();
1287        let (tx, signer) = consensus.into_parts();
1288        Ok(Recovered::new_unchecked(tx.try_into()?, signer))
1289    }
1290
1291    /// Converts the `Pooled` type into the `Consensus` type.
1292    fn pooled_into_consensus(tx: Self::Pooled) -> Self::Consensus {
1293        tx.into()
1294    }
1295
1296    /// Hash of the transaction.
1297    fn hash(&self) -> &TxHash;
1298
1299    /// The Sender of the transaction.
1300    fn sender(&self) -> Address;
1301
1302    /// Reference to the Sender of the transaction.
1303    fn sender_ref(&self) -> &Address;
1304
1305    /// Returns the cost that this transaction is allowed to consume:
1306    ///
1307    /// For EIP-1559 transactions: `max_fee_per_gas * gas_limit + tx_value`.
1308    /// For legacy transactions: `gas_price * gas_limit + tx_value`.
1309    /// For EIP-4844 blob transactions: `max_fee_per_gas * gas_limit + tx_value +
1310    /// max_blob_fee_per_gas * blob_gas_used`.
1311    fn cost(&self) -> &U256;
1312
1313    /// Returns the length of the rlp encoded transaction object
1314    ///
1315    /// Note: Implementations should cache this value.
1316    fn encoded_length(&self) -> usize;
1317
1318    /// Ensures that the transaction's code size does not exceed the provided `max_init_code_size`.
1319    ///
1320    /// This is specifically relevant for contract creation transactions ([`TxKind::Create`]),
1321    /// where the input data contains the initialization code. If the input code size exceeds
1322    /// the configured limit, an [`InvalidPoolTransactionError::ExceedsMaxInitCodeSize`] error is
1323    /// returned.
1324    fn ensure_max_init_code_size(
1325        &self,
1326        max_init_code_size: usize,
1327    ) -> Result<(), InvalidPoolTransactionError> {
1328        let input_len = self.input().len();
1329        if self.is_create() && input_len > max_init_code_size {
1330            Err(InvalidPoolTransactionError::ExceedsMaxInitCodeSize(input_len, max_init_code_size))
1331        } else {
1332            Ok(())
1333        }
1334    }
1335
1336    /// Allows to communicate to the pool that the transaction doesn't require a nonce check.
1337    fn requires_nonce_check(&self) -> bool {
1338        true
1339    }
1340}
1341
1342/// Super trait for transactions that can be converted to and from Eth transactions intended for the
1343/// ethereum style pool.
1344///
1345/// This extends the [`PoolTransaction`] trait with additional methods that are specific to the
1346/// Ethereum pool.
1347pub trait EthPoolTransaction: PoolTransaction {
1348    /// Extracts the blob sidecar from the transaction.
1349    fn take_blob(&mut self) -> EthBlobTransactionSidecar;
1350
1351    /// A specialization for the EIP-4844 transaction type.
1352    /// Tries to reattach the blob sidecar to the transaction.
1353    ///
1354    /// This returns an option, but callers should ensure that the transaction is an EIP-4844
1355    /// transaction: [`Typed2718::is_eip4844`].
1356    fn try_into_pooled_eip4844(
1357        self,
1358        sidecar: Arc<BlobTransactionSidecarVariant>,
1359    ) -> Option<Recovered<Self::Pooled>>;
1360
1361    /// Tries to convert the `Consensus` type with a blob sidecar into the `Pooled` type.
1362    ///
1363    /// Returns `None` if passed transaction is not a blob transaction.
1364    fn try_from_eip4844(
1365        tx: Recovered<Self::Consensus>,
1366        sidecar: BlobTransactionSidecarVariant,
1367    ) -> Option<Self>;
1368
1369    /// Validates the blob sidecar of the transaction with the given settings.
1370    fn validate_blob(
1371        &self,
1372        blob: &BlobTransactionSidecarVariant,
1373        settings: &KzgSettings,
1374    ) -> Result<(), BlobTransactionValidationError>;
1375}
1376
1377/// The default [`PoolTransaction`] for the [Pool](crate::Pool) for Ethereum.
1378///
1379/// This type wraps a consensus transaction with additional cached data that's
1380/// frequently accessed by the pool for transaction ordering and validation:
1381///
1382/// - `cost`: Pre-calculated max cost (gas * price + value + blob costs)
1383/// - `encoded_length`: Cached RLP encoding length for size limits
1384/// - `blob_sidecar`: Blob data state (None/Missing/Present)
1385///
1386/// This avoids recalculating these values repeatedly during pool operations.
1387#[derive(Debug, Clone, PartialEq, Eq)]
1388pub struct EthPooledTransaction<T = TransactionSigned> {
1389    /// `EcRecovered` transaction, the consensus format.
1390    pub transaction: Recovered<T>,
1391
1392    /// For EIP-1559 transactions: `max_fee_per_gas * gas_limit + tx_value`.
1393    /// For legacy transactions: `gas_price * gas_limit + tx_value`.
1394    /// For EIP-4844 blob transactions: `max_fee_per_gas * gas_limit + tx_value +
1395    /// max_blob_fee_per_gas * blob_gas_used`.
1396    pub cost: U256,
1397
1398    /// This is the RLP length of the transaction, computed when the transaction is added to the
1399    /// pool.
1400    pub encoded_length: usize,
1401
1402    /// The blob side car for this transaction
1403    pub blob_sidecar: EthBlobTransactionSidecar,
1404}
1405
1406impl<T: SignedTransaction> EthPooledTransaction<T> {
1407    /// Create new instance of [Self].
1408    ///
1409    /// Caution: In case of blob transactions, this does marks the blob sidecar as
1410    /// [`EthBlobTransactionSidecar::Missing`]
1411    pub fn new(transaction: Recovered<T>, encoded_length: usize) -> Self {
1412        let mut blob_sidecar = EthBlobTransactionSidecar::None;
1413
1414        let gas_cost = U256::from(transaction.max_fee_per_gas())
1415            .saturating_mul(U256::from(transaction.gas_limit()));
1416
1417        let mut cost = gas_cost.saturating_add(transaction.value());
1418
1419        if let (Some(blob_gas_used), Some(max_fee_per_blob_gas)) =
1420            (transaction.blob_gas_used(), transaction.max_fee_per_blob_gas())
1421        {
1422            // Add max blob cost using saturating math to avoid overflow
1423            cost = cost.saturating_add(U256::from(
1424                max_fee_per_blob_gas.saturating_mul(blob_gas_used as u128),
1425            ));
1426
1427            // because the blob sidecar is not included in this transaction variant, mark it as
1428            // missing
1429            blob_sidecar = EthBlobTransactionSidecar::Missing;
1430        }
1431
1432        Self { transaction, cost, encoded_length, blob_sidecar }
1433    }
1434
1435    /// Return the reference to the underlying transaction.
1436    pub const fn transaction(&self) -> &Recovered<T> {
1437        &self.transaction
1438    }
1439}
1440
1441impl PoolTransaction for EthPooledTransaction {
1442    type TryFromConsensusError = ValueError<TransactionSigned>;
1443
1444    type Consensus = TransactionSigned;
1445
1446    type Pooled = PooledTransactionVariant;
1447
1448    fn clone_into_consensus(&self) -> Recovered<Self::Consensus> {
1449        self.transaction().clone()
1450    }
1451
1452    fn into_consensus(self) -> Recovered<Self::Consensus> {
1453        self.transaction
1454    }
1455
1456    fn from_pooled(tx: Recovered<Self::Pooled>) -> Self {
1457        let encoded_length = tx.encode_2718_len();
1458        let (tx, signer) = tx.into_parts();
1459        match tx {
1460            PooledTransactionVariant::Eip4844(tx) => {
1461                // include the blob sidecar
1462                let (tx, sig, hash) = tx.into_parts();
1463                let (tx, blob) = tx.into_parts();
1464                let tx = Signed::new_unchecked(tx, sig, hash);
1465                let tx = TransactionSigned::from(tx);
1466                let tx = Recovered::new_unchecked(tx, signer);
1467                let mut pooled = Self::new(tx, encoded_length);
1468                pooled.blob_sidecar = EthBlobTransactionSidecar::Present(blob);
1469                pooled
1470            }
1471            tx => {
1472                // no blob sidecar
1473                let tx = Recovered::new_unchecked(tx.into(), signer);
1474                Self::new(tx, encoded_length)
1475            }
1476        }
1477    }
1478
1479    /// Returns hash of the transaction.
1480    fn hash(&self) -> &TxHash {
1481        self.transaction.tx_hash()
1482    }
1483
1484    /// Returns the Sender of the transaction.
1485    fn sender(&self) -> Address {
1486        self.transaction.signer()
1487    }
1488
1489    /// Returns a reference to the Sender of the transaction.
1490    fn sender_ref(&self) -> &Address {
1491        self.transaction.signer_ref()
1492    }
1493
1494    /// Returns the cost that this transaction is allowed to consume:
1495    ///
1496    /// For EIP-1559 transactions: `max_fee_per_gas * gas_limit + tx_value`.
1497    /// For legacy transactions: `gas_price * gas_limit + tx_value`.
1498    /// For EIP-4844 blob transactions: `max_fee_per_gas * gas_limit + tx_value +
1499    /// max_blob_fee_per_gas * blob_gas_used`.
1500    fn cost(&self) -> &U256 {
1501        &self.cost
1502    }
1503
1504    /// Returns the length of the rlp encoded object
1505    fn encoded_length(&self) -> usize {
1506        self.encoded_length
1507    }
1508}
1509
1510impl<T: Typed2718> Typed2718 for EthPooledTransaction<T> {
1511    fn ty(&self) -> u8 {
1512        self.transaction.ty()
1513    }
1514}
1515
1516impl<T: InMemorySize> InMemorySize for EthPooledTransaction<T> {
1517    fn size(&self) -> usize {
1518        self.transaction.size()
1519    }
1520}
1521
1522impl<T: alloy_consensus::Transaction> alloy_consensus::Transaction for EthPooledTransaction<T> {
1523    fn chain_id(&self) -> Option<alloy_primitives::ChainId> {
1524        self.transaction.chain_id()
1525    }
1526
1527    fn nonce(&self) -> u64 {
1528        self.transaction.nonce()
1529    }
1530
1531    fn gas_limit(&self) -> u64 {
1532        self.transaction.gas_limit()
1533    }
1534
1535    fn gas_price(&self) -> Option<u128> {
1536        self.transaction.gas_price()
1537    }
1538
1539    fn max_fee_per_gas(&self) -> u128 {
1540        self.transaction.max_fee_per_gas()
1541    }
1542
1543    fn max_priority_fee_per_gas(&self) -> Option<u128> {
1544        self.transaction.max_priority_fee_per_gas()
1545    }
1546
1547    fn max_fee_per_blob_gas(&self) -> Option<u128> {
1548        self.transaction.max_fee_per_blob_gas()
1549    }
1550
1551    fn priority_fee_or_price(&self) -> u128 {
1552        self.transaction.priority_fee_or_price()
1553    }
1554
1555    fn effective_gas_price(&self, base_fee: Option<u64>) -> u128 {
1556        self.transaction.effective_gas_price(base_fee)
1557    }
1558
1559    fn is_dynamic_fee(&self) -> bool {
1560        self.transaction.is_dynamic_fee()
1561    }
1562
1563    fn kind(&self) -> TxKind {
1564        self.transaction.kind()
1565    }
1566
1567    fn is_create(&self) -> bool {
1568        self.transaction.is_create()
1569    }
1570
1571    fn value(&self) -> U256 {
1572        self.transaction.value()
1573    }
1574
1575    fn input(&self) -> &Bytes {
1576        self.transaction.input()
1577    }
1578
1579    fn access_list(&self) -> Option<&AccessList> {
1580        self.transaction.access_list()
1581    }
1582
1583    fn blob_versioned_hashes(&self) -> Option<&[B256]> {
1584        self.transaction.blob_versioned_hashes()
1585    }
1586
1587    fn authorization_list(&self) -> Option<&[SignedAuthorization]> {
1588        self.transaction.authorization_list()
1589    }
1590}
1591
1592impl EthPoolTransaction for EthPooledTransaction {
1593    fn take_blob(&mut self) -> EthBlobTransactionSidecar {
1594        if self.is_eip4844() {
1595            std::mem::replace(&mut self.blob_sidecar, EthBlobTransactionSidecar::Missing)
1596        } else {
1597            EthBlobTransactionSidecar::None
1598        }
1599    }
1600
1601    fn try_into_pooled_eip4844(
1602        self,
1603        sidecar: Arc<BlobTransactionSidecarVariant>,
1604    ) -> Option<Recovered<Self::Pooled>> {
1605        let (signed_transaction, signer) = self.into_consensus().into_parts();
1606        let pooled_transaction =
1607            signed_transaction.try_into_pooled_eip4844(Arc::unwrap_or_clone(sidecar)).ok()?;
1608
1609        Some(Recovered::new_unchecked(pooled_transaction, signer))
1610    }
1611
1612    fn try_from_eip4844(
1613        tx: Recovered<Self::Consensus>,
1614        sidecar: BlobTransactionSidecarVariant,
1615    ) -> Option<Self> {
1616        let (tx, signer) = tx.into_parts();
1617        tx.try_into_pooled_eip4844(sidecar)
1618            .ok()
1619            .map(|tx| tx.with_signer(signer))
1620            .map(Self::from_pooled)
1621    }
1622
1623    fn validate_blob(
1624        &self,
1625        sidecar: &BlobTransactionSidecarVariant,
1626        settings: &KzgSettings,
1627    ) -> Result<(), BlobTransactionValidationError> {
1628        match self.transaction.inner().as_eip4844() {
1629            Some(tx) => tx.tx().validate_blob(sidecar, settings),
1630            _ => Err(BlobTransactionValidationError::NotBlobTransaction(self.ty())),
1631        }
1632    }
1633}
1634
1635/// Represents the blob sidecar of the [`EthPooledTransaction`].
1636///
1637/// EIP-4844 blob transactions require additional data (blobs, commitments, proofs)
1638/// for validation that is not included in the consensus format. This enum tracks
1639/// the sidecar state throughout the transaction's lifecycle in the pool.
1640#[derive(Debug, Clone, PartialEq, Eq)]
1641pub enum EthBlobTransactionSidecar {
1642    /// This transaction does not have a blob sidecar
1643    /// (applies to all non-EIP-4844 transaction types)
1644    None,
1645    /// This transaction has a blob sidecar (EIP-4844) but it is missing.
1646    ///
1647    /// This can happen when:
1648    /// - The sidecar was extracted after the transaction was added to the pool
1649    /// - The transaction was re-injected after a reorg without its sidecar
1650    /// - The transaction was recovered from the consensus format (e.g., from a block)
1651    Missing,
1652    /// The EIP-4844 transaction was received from the network with its complete sidecar.
1653    ///
1654    /// This sidecar contains:
1655    /// - The actual blob data (large data per blob)
1656    /// - KZG commitments for each blob
1657    /// - KZG proofs for validation
1658    ///
1659    /// The sidecar is required for validating the transaction but is not included
1660    /// in blocks (only the blob hashes are included in the consensus format).
1661    Present(BlobTransactionSidecarVariant),
1662}
1663
1664impl EthBlobTransactionSidecar {
1665    /// Returns the blob sidecar if it is present
1666    pub const fn maybe_sidecar(&self) -> Option<&BlobTransactionSidecarVariant> {
1667        match self {
1668            Self::Present(sidecar) => Some(sidecar),
1669            _ => None,
1670        }
1671    }
1672}
1673
1674/// Represents the current status of the pool.
1675#[derive(Debug, Clone, Copy, Default)]
1676pub struct PoolSize {
1677    /// Number of transactions in the _pending_ sub-pool.
1678    pub pending: usize,
1679    /// Reported size of transactions in the _pending_ sub-pool.
1680    pub pending_size: usize,
1681    /// Number of transactions in the _blob_ pool.
1682    pub blob: usize,
1683    /// Reported size of transactions in the _blob_ pool.
1684    pub blob_size: usize,
1685    /// Number of transactions in the _basefee_ pool.
1686    pub basefee: usize,
1687    /// Reported size of transactions in the _basefee_ sub-pool.
1688    pub basefee_size: usize,
1689    /// Number of transactions in the _queued_ sub-pool.
1690    pub queued: usize,
1691    /// Reported size of transactions in the _queued_ sub-pool.
1692    pub queued_size: usize,
1693    /// Number of all transactions of all sub-pools
1694    ///
1695    /// Note: this is the sum of ```pending + basefee + queued + blob```
1696    pub total: usize,
1697}
1698
1699// === impl PoolSize ===
1700
1701impl PoolSize {
1702    /// Asserts that the invariants of the pool size are met.
1703    #[cfg(test)]
1704    pub(crate) fn assert_invariants(&self) {
1705        assert_eq!(self.total, self.pending + self.basefee + self.queued + self.blob);
1706    }
1707}
1708
1709/// Represents the current status of the pool.
1710#[derive(Default, Debug, Clone, Copy, Eq, PartialEq)]
1711pub struct BlockInfo {
1712    /// Hash for the currently tracked block.
1713    pub last_seen_block_hash: B256,
1714    /// Currently tracked block.
1715    pub last_seen_block_number: u64,
1716    /// Current block gas limit for the latest block.
1717    pub block_gas_limit: u64,
1718    /// Currently enforced base fee: the threshold for the basefee sub-pool.
1719    ///
1720    /// Note: this is the derived base fee of the _next_ block that builds on the block the pool is
1721    /// currently tracking.
1722    pub pending_basefee: u64,
1723    /// Currently enforced blob fee: the threshold for eip-4844 blob transactions.
1724    ///
1725    /// Note: this is the derived blob fee of the _next_ block that builds on the block the pool is
1726    /// currently tracking
1727    pub pending_blob_fee: Option<u128>,
1728}
1729
1730/// The limit to enforce for [`TransactionPool::get_pooled_transaction_elements`].
1731#[derive(Debug, Clone, Copy, Eq, PartialEq)]
1732pub enum GetPooledTransactionLimit {
1733    /// No limit, return all transactions.
1734    None,
1735    /// Enforce a size limit on the returned transactions, for example 2MB
1736    ResponseSizeSoftLimit(usize),
1737}
1738
1739impl GetPooledTransactionLimit {
1740    /// Returns true if the given size exceeds the limit.
1741    #[inline]
1742    pub const fn exceeds(&self, size: usize) -> bool {
1743        match self {
1744            Self::None => false,
1745            Self::ResponseSizeSoftLimit(limit) => size > *limit,
1746        }
1747    }
1748}
1749
1750/// A Stream that yields full transactions the subpool
1751#[must_use = "streams do nothing unless polled"]
1752#[derive(Debug)]
1753pub struct NewSubpoolTransactionStream<Tx: PoolTransaction> {
1754    st: Receiver<NewTransactionEvent<Tx>>,
1755    subpool: SubPool,
1756}
1757
1758// === impl NewSubpoolTransactionStream ===
1759
1760impl<Tx: PoolTransaction> NewSubpoolTransactionStream<Tx> {
1761    /// Create a new stream that yields full transactions from the subpool
1762    pub const fn new(st: Receiver<NewTransactionEvent<Tx>>, subpool: SubPool) -> Self {
1763        Self { st, subpool }
1764    }
1765
1766    /// Tries to receive the next value for this stream.
1767    pub fn try_recv(
1768        &mut self,
1769    ) -> Result<NewTransactionEvent<Tx>, tokio::sync::mpsc::error::TryRecvError> {
1770        loop {
1771            match self.st.try_recv() {
1772                Ok(event) => {
1773                    if event.subpool == self.subpool {
1774                        return Ok(event)
1775                    }
1776                }
1777                Err(e) => return Err(e),
1778            }
1779        }
1780    }
1781}
1782
1783impl<Tx: PoolTransaction> Stream for NewSubpoolTransactionStream<Tx> {
1784    type Item = NewTransactionEvent<Tx>;
1785
1786    fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
1787        loop {
1788            match ready!(self.st.poll_recv(cx)) {
1789                Some(event) => {
1790                    if event.subpool == self.subpool {
1791                        return Poll::Ready(Some(event))
1792                    }
1793                }
1794                None => return Poll::Ready(None),
1795            }
1796        }
1797    }
1798}
1799
1800#[cfg(test)]
1801mod tests {
1802    use super::*;
1803    use alloy_consensus::{
1804        EthereumTxEnvelope, SignableTransaction, TxEip1559, TxEip2930, TxEip4844, TxEip7702,
1805        TxEnvelope, TxLegacy,
1806    };
1807    use alloy_eips::eip4844::DATA_GAS_PER_BLOB;
1808    use alloy_primitives::Signature;
1809
1810    #[test]
1811    fn test_pool_size_invariants() {
1812        let pool_size = PoolSize {
1813            pending: 10,
1814            pending_size: 1000,
1815            blob: 5,
1816            blob_size: 500,
1817            basefee: 8,
1818            basefee_size: 800,
1819            queued: 7,
1820            queued_size: 700,
1821            total: 10 + 5 + 8 + 7, // Correct total
1822        };
1823
1824        // Call the assert_invariants method to check if the invariants are correct
1825        pool_size.assert_invariants();
1826    }
1827
1828    #[test]
1829    #[should_panic]
1830    fn test_pool_size_invariants_fail() {
1831        let pool_size = PoolSize {
1832            pending: 10,
1833            pending_size: 1000,
1834            blob: 5,
1835            blob_size: 500,
1836            basefee: 8,
1837            basefee_size: 800,
1838            queued: 7,
1839            queued_size: 700,
1840            total: 10 + 5 + 8, // Incorrect total
1841        };
1842
1843        // Call the assert_invariants method, which should panic
1844        pool_size.assert_invariants();
1845    }
1846
1847    #[test]
1848    fn test_eth_pooled_transaction_new_legacy() {
1849        // Create a legacy transaction with specific parameters
1850        let tx = TxEnvelope::Legacy(
1851            TxLegacy {
1852                gas_price: 10,
1853                gas_limit: 1000,
1854                value: U256::from(100),
1855                ..Default::default()
1856            }
1857            .into_signed(Signature::test_signature()),
1858        );
1859        let transaction = Recovered::new_unchecked(tx, Default::default());
1860        let pooled_tx = EthPooledTransaction::new(transaction.clone(), 200);
1861
1862        // Check that the pooled transaction is created correctly
1863        assert_eq!(pooled_tx.transaction, transaction);
1864        assert_eq!(pooled_tx.encoded_length, 200);
1865        assert_eq!(pooled_tx.blob_sidecar, EthBlobTransactionSidecar::None);
1866        assert_eq!(pooled_tx.cost, U256::from(100) + U256::from(10 * 1000));
1867    }
1868
1869    #[test]
1870    fn test_eth_pooled_transaction_new_eip2930() {
1871        // Create an EIP-2930 transaction with specific parameters
1872        let tx = TxEnvelope::Eip2930(
1873            TxEip2930 {
1874                gas_price: 10,
1875                gas_limit: 1000,
1876                value: U256::from(100),
1877                ..Default::default()
1878            }
1879            .into_signed(Signature::test_signature()),
1880        );
1881        let transaction = Recovered::new_unchecked(tx, Default::default());
1882        let pooled_tx = EthPooledTransaction::new(transaction.clone(), 200);
1883        let expected_cost = U256::from(100) + (U256::from(10 * 1000));
1884
1885        assert_eq!(pooled_tx.transaction, transaction);
1886        assert_eq!(pooled_tx.encoded_length, 200);
1887        assert_eq!(pooled_tx.blob_sidecar, EthBlobTransactionSidecar::None);
1888        assert_eq!(pooled_tx.cost, expected_cost);
1889    }
1890
1891    #[test]
1892    fn test_eth_pooled_transaction_new_eip1559() {
1893        // Create an EIP-1559 transaction with specific parameters
1894        let tx = TxEnvelope::Eip1559(
1895            TxEip1559 {
1896                max_fee_per_gas: 10,
1897                gas_limit: 1000,
1898                value: U256::from(100),
1899                ..Default::default()
1900            }
1901            .into_signed(Signature::test_signature()),
1902        );
1903        let transaction = Recovered::new_unchecked(tx, Default::default());
1904        let pooled_tx = EthPooledTransaction::new(transaction.clone(), 200);
1905
1906        // Check that the pooled transaction is created correctly
1907        assert_eq!(pooled_tx.transaction, transaction);
1908        assert_eq!(pooled_tx.encoded_length, 200);
1909        assert_eq!(pooled_tx.blob_sidecar, EthBlobTransactionSidecar::None);
1910        assert_eq!(pooled_tx.cost, U256::from(100) + U256::from(10 * 1000));
1911    }
1912
1913    #[test]
1914    fn test_eth_pooled_transaction_new_eip4844() {
1915        // Create an EIP-4844 transaction with specific parameters
1916        let tx = EthereumTxEnvelope::Eip4844(
1917            TxEip4844 {
1918                max_fee_per_gas: 10,
1919                gas_limit: 1000,
1920                value: U256::from(100),
1921                max_fee_per_blob_gas: 5,
1922                blob_versioned_hashes: vec![B256::default()],
1923                ..Default::default()
1924            }
1925            .into_signed(Signature::test_signature()),
1926        );
1927        let transaction = Recovered::new_unchecked(tx, Default::default());
1928        let pooled_tx = EthPooledTransaction::new(transaction.clone(), 300);
1929
1930        // Check that the pooled transaction is created correctly
1931        assert_eq!(pooled_tx.transaction, transaction);
1932        assert_eq!(pooled_tx.encoded_length, 300);
1933        assert_eq!(pooled_tx.blob_sidecar, EthBlobTransactionSidecar::Missing);
1934        let expected_cost =
1935            U256::from(100) + U256::from(10 * 1000) + U256::from(5 * DATA_GAS_PER_BLOB);
1936        assert_eq!(pooled_tx.cost, expected_cost);
1937    }
1938
1939    #[test]
1940    fn test_eth_pooled_transaction_new_eip7702() {
1941        // Init an EIP-7702 transaction with specific parameters
1942        let tx = EthereumTxEnvelope::<TxEip4844>::Eip7702(
1943            TxEip7702 {
1944                max_fee_per_gas: 10,
1945                gas_limit: 1000,
1946                value: U256::from(100),
1947                ..Default::default()
1948            }
1949            .into_signed(Signature::test_signature()),
1950        );
1951        let transaction = Recovered::new_unchecked(tx, Default::default());
1952        let pooled_tx = EthPooledTransaction::new(transaction.clone(), 200);
1953
1954        // Check that the pooled transaction is created correctly
1955        assert_eq!(pooled_tx.transaction, transaction);
1956        assert_eq!(pooled_tx.encoded_length, 200);
1957        assert_eq!(pooled_tx.blob_sidecar, EthBlobTransactionSidecar::None);
1958        assert_eq!(pooled_tx.cost, U256::from(100) + U256::from(10 * 1000));
1959    }
1960
1961    #[test]
1962    fn test_pooled_transaction_limit() {
1963        // No limit should never exceed
1964        let limit_none = GetPooledTransactionLimit::None;
1965        // Any size should return false
1966        assert!(!limit_none.exceeds(1000));
1967
1968        // Size limit of 2MB (2 * 1024 * 1024 bytes)
1969        let size_limit_2mb = GetPooledTransactionLimit::ResponseSizeSoftLimit(2 * 1024 * 1024);
1970
1971        // Test with size below the limit
1972        // 1MB is below 2MB, should return false
1973        assert!(!size_limit_2mb.exceeds(1024 * 1024));
1974
1975        // Test with size exactly at the limit
1976        // 2MB equals the limit, should return false
1977        assert!(!size_limit_2mb.exceeds(2 * 1024 * 1024));
1978
1979        // Test with size exceeding the limit
1980        // 3MB is above the 2MB limit, should return true
1981        assert!(size_limit_2mb.exceeds(3 * 1024 * 1024));
1982    }
1983}