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