reth_rpc/eth/
core.rs

1//! Implementation of the [`jsonrpsee`] generated [`EthApiServer`](crate::EthApi) trait
2//! Handles RPC requests for the `eth_` namespace.
3
4use std::sync::Arc;
5
6use crate::{eth::EthTxBuilder, EthApiBuilder};
7use alloy_consensus::BlockHeader;
8use alloy_eips::BlockNumberOrTag;
9use alloy_network::Ethereum;
10use alloy_primitives::{Bytes, U256};
11use derive_more::Deref;
12use reth_node_api::{FullNodeComponents, FullNodeTypes};
13use reth_rpc_eth_api::{
14    helpers::{EthSigner, SpawnBlocking},
15    node::RpcNodeCoreExt,
16    EthApiTypes, RpcNodeCore,
17};
18use reth_rpc_eth_types::{
19    EthApiError, EthStateCache, FeeHistoryCache, GasCap, GasPriceOracle, PendingBlock,
20};
21use reth_storage_api::{
22    BlockReader, BlockReaderIdExt, NodePrimitivesProvider, ProviderBlock, ProviderReceipt,
23};
24use reth_tasks::{
25    pool::{BlockingTaskGuard, BlockingTaskPool},
26    TaskSpawner, TokioTaskExecutor,
27};
28use tokio::sync::{broadcast, Mutex};
29
30const DEFAULT_BROADCAST_CAPACITY: usize = 2000;
31
32/// Helper type alias for [`EthApi`] with components from the given [`FullNodeComponents`].
33pub type EthApiFor<N> = EthApi<
34    <N as FullNodeTypes>::Provider,
35    <N as FullNodeComponents>::Pool,
36    <N as FullNodeComponents>::Network,
37    <N as FullNodeComponents>::Evm,
38>;
39
40/// Helper type alias for [`EthApi`] with components from the given [`FullNodeComponents`].
41pub type EthApiBuilderFor<N> = EthApiBuilder<
42    <N as FullNodeTypes>::Provider,
43    <N as FullNodeComponents>::Pool,
44    <N as FullNodeComponents>::Network,
45    <N as FullNodeComponents>::Evm,
46>;
47
48/// `Eth` API implementation.
49///
50/// This type provides the functionality for handling `eth_` related requests.
51/// These are implemented two-fold: Core functionality is implemented as
52/// [`EthApiSpec`](reth_rpc_eth_api::helpers::EthApiSpec) trait. Additionally, the required server
53/// implementations (e.g. [`EthApiServer`](reth_rpc_eth_api::EthApiServer)) are implemented
54/// separately in submodules. The rpc handler implementation can then delegate to the main impls.
55/// This way [`EthApi`] is not limited to [`jsonrpsee`] and can be used standalone or in other
56/// network handlers (for example ipc).
57///
58/// ## Trait requirements
59///
60/// While this type requires various unrestricted generic components, trait bounds are enforced when
61/// additional traits are implemented for this type.
62#[derive(Deref)]
63pub struct EthApi<Provider: BlockReader, Pool, Network, EvmConfig> {
64    /// All nested fields bundled together.
65    #[deref]
66    pub(super) inner: Arc<EthApiInner<Provider, Pool, Network, EvmConfig>>,
67    /// Transaction RPC response builder.
68    pub tx_resp_builder: EthTxBuilder,
69}
70
71impl<Provider, Pool, Network, EvmConfig> Clone for EthApi<Provider, Pool, Network, EvmConfig>
72where
73    Provider: BlockReader,
74{
75    fn clone(&self) -> Self {
76        Self { inner: self.inner.clone(), tx_resp_builder: EthTxBuilder }
77    }
78}
79
80impl<Provider, Pool, Network, EvmConfig> EthApi<Provider, Pool, Network, EvmConfig>
81where
82    Provider: BlockReaderIdExt,
83{
84    /// Convenience fn to obtain a new [`EthApiBuilder`] instance with mandatory components.
85    ///
86    /// Creating an [`EthApi`] requires a few mandatory components:
87    ///  - provider: The type responsible for fetching requested data from disk.
88    ///  - transaction pool: To interact with the pool, submitting new transactions (e.g.
89    ///    `eth_sendRawTransactions`).
90    ///  - network: required to handle requests related to network state (e.g. `eth_syncing`).
91    ///  - evm config: Knows how create a new EVM instance to transact,estimate,call,trace.
92    ///
93    /// # Create an instance with noop ethereum implementations
94    ///
95    /// ```no_run
96    /// use reth_evm_ethereum::EthEvmConfig;
97    /// use reth_network_api::noop::NoopNetwork;
98    /// use reth_provider::noop::NoopProvider;
99    /// use reth_rpc::EthApi;
100    /// use reth_transaction_pool::noop::NoopTransactionPool;
101    /// let eth_api = EthApi::builder(
102    ///     NoopProvider::default(),
103    ///     NoopTransactionPool::default(),
104    ///     NoopNetwork::default(),
105    ///     EthEvmConfig::mainnet(),
106    /// )
107    /// .build();
108    /// ```
109    pub fn builder(
110        provider: Provider,
111        pool: Pool,
112        network: Network,
113        evm_config: EvmConfig,
114    ) -> EthApiBuilder<Provider, Pool, Network, EvmConfig> {
115        EthApiBuilder::new(provider, pool, network, evm_config)
116    }
117
118    /// Creates a new, shareable instance using the default tokio task spawner.
119    #[expect(clippy::too_many_arguments)]
120    pub fn new(
121        provider: Provider,
122        pool: Pool,
123        network: Network,
124        eth_cache: EthStateCache<Provider::Block, Provider::Receipt>,
125        gas_oracle: GasPriceOracle<Provider>,
126        gas_cap: impl Into<GasCap>,
127        max_simulate_blocks: u64,
128        eth_proof_window: u64,
129        blocking_task_pool: BlockingTaskPool,
130        fee_history_cache: FeeHistoryCache,
131        evm_config: EvmConfig,
132        proof_permits: usize,
133    ) -> Self {
134        let inner = EthApiInner::new(
135            provider,
136            pool,
137            network,
138            eth_cache,
139            gas_oracle,
140            gas_cap,
141            max_simulate_blocks,
142            eth_proof_window,
143            blocking_task_pool,
144            fee_history_cache,
145            evm_config,
146            TokioTaskExecutor::default().boxed(),
147            proof_permits,
148        );
149
150        Self { inner: Arc::new(inner), tx_resp_builder: EthTxBuilder }
151    }
152}
153
154impl<Provider, Pool, Network, EvmConfig> EthApiTypes for EthApi<Provider, Pool, Network, EvmConfig>
155where
156    Self: Send + Sync,
157    Provider: BlockReader,
158{
159    type Error = EthApiError;
160    type NetworkTypes = Ethereum;
161    type TransactionCompat = EthTxBuilder;
162
163    fn tx_resp_builder(&self) -> &Self::TransactionCompat {
164        &self.tx_resp_builder
165    }
166}
167
168impl<Provider, Pool, Network, EvmConfig> RpcNodeCore for EthApi<Provider, Pool, Network, EvmConfig>
169where
170    Provider: BlockReader + NodePrimitivesProvider + Clone + Unpin,
171    Pool: Send + Sync + Clone + Unpin,
172    Network: Send + Sync + Clone,
173    EvmConfig: Send + Sync + Clone + Unpin,
174{
175    type Primitives = Provider::Primitives;
176    type Provider = Provider;
177    type Pool = Pool;
178    type Evm = EvmConfig;
179    type Network = Network;
180    type PayloadBuilder = ();
181
182    fn pool(&self) -> &Self::Pool {
183        self.inner.pool()
184    }
185
186    fn evm_config(&self) -> &Self::Evm {
187        self.inner.evm_config()
188    }
189
190    fn network(&self) -> &Self::Network {
191        self.inner.network()
192    }
193
194    fn payload_builder(&self) -> &Self::PayloadBuilder {
195        &()
196    }
197
198    fn provider(&self) -> &Self::Provider {
199        self.inner.provider()
200    }
201}
202
203impl<Provider, Pool, Network, EvmConfig> RpcNodeCoreExt
204    for EthApi<Provider, Pool, Network, EvmConfig>
205where
206    Provider: BlockReader + NodePrimitivesProvider + Clone + Unpin,
207    Pool: Send + Sync + Clone + Unpin,
208    Network: Send + Sync + Clone,
209    EvmConfig: Send + Sync + Clone + Unpin,
210{
211    #[inline]
212    fn cache(&self) -> &EthStateCache<ProviderBlock<Provider>, ProviderReceipt<Provider>> {
213        self.inner.cache()
214    }
215}
216
217impl<Provider, Pool, Network, EvmConfig> std::fmt::Debug
218    for EthApi<Provider, Pool, Network, EvmConfig>
219where
220    Provider: BlockReader,
221{
222    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
223        f.debug_struct("EthApi").finish_non_exhaustive()
224    }
225}
226
227impl<Provider, Pool, Network, EvmConfig> SpawnBlocking
228    for EthApi<Provider, Pool, Network, EvmConfig>
229where
230    Self: Clone + Send + Sync + 'static,
231    Provider: BlockReader,
232{
233    #[inline]
234    fn io_task_spawner(&self) -> impl TaskSpawner {
235        self.inner.task_spawner()
236    }
237
238    #[inline]
239    fn tracing_task_pool(&self) -> &BlockingTaskPool {
240        self.inner.blocking_task_pool()
241    }
242
243    #[inline]
244    fn tracing_task_guard(&self) -> &BlockingTaskGuard {
245        self.inner.blocking_task_guard()
246    }
247}
248
249/// Container type `EthApi`
250#[expect(missing_debug_implementations)]
251pub struct EthApiInner<Provider: BlockReader, Pool, Network, EvmConfig> {
252    /// The transaction pool.
253    pool: Pool,
254    /// The provider that can interact with the chain.
255    provider: Provider,
256    /// An interface to interact with the network
257    network: Network,
258    /// All configured Signers
259    signers: parking_lot::RwLock<Vec<Box<dyn EthSigner<Provider::Transaction>>>>,
260    /// The async cache frontend for eth related data
261    eth_cache: EthStateCache<Provider::Block, Provider::Receipt>,
262    /// The async gas oracle frontend for gas price suggestions
263    gas_oracle: GasPriceOracle<Provider>,
264    /// Maximum gas limit for `eth_call` and call tracing RPC methods.
265    gas_cap: u64,
266    /// Maximum number of blocks for `eth_simulateV1`.
267    max_simulate_blocks: u64,
268    /// The maximum number of blocks into the past for generating state proofs.
269    eth_proof_window: u64,
270    /// The block number at which the node started
271    starting_block: U256,
272    /// The type that can spawn tasks which would otherwise block.
273    task_spawner: Box<dyn TaskSpawner>,
274    /// Cached pending block if any
275    pending_block: Mutex<Option<PendingBlock<Provider::Block, Provider::Receipt>>>,
276    /// A pool dedicated to CPU heavy blocking tasks.
277    blocking_task_pool: BlockingTaskPool,
278    /// Cache for block fees history
279    fee_history_cache: FeeHistoryCache,
280    /// The type that defines how to configure the EVM
281    evm_config: EvmConfig,
282
283    /// Guard for getproof calls
284    blocking_task_guard: BlockingTaskGuard,
285
286    /// Transaction broadcast channel
287    raw_tx_sender: broadcast::Sender<Bytes>,
288}
289
290impl<Provider, Pool, Network, EvmConfig> EthApiInner<Provider, Pool, Network, EvmConfig>
291where
292    Provider: BlockReaderIdExt,
293{
294    /// Creates a new, shareable instance using the default tokio task spawner.
295    #[expect(clippy::too_many_arguments)]
296    pub fn new(
297        provider: Provider,
298        pool: Pool,
299        network: Network,
300        eth_cache: EthStateCache<Provider::Block, Provider::Receipt>,
301        gas_oracle: GasPriceOracle<Provider>,
302        gas_cap: impl Into<GasCap>,
303        max_simulate_blocks: u64,
304        eth_proof_window: u64,
305        blocking_task_pool: BlockingTaskPool,
306        fee_history_cache: FeeHistoryCache,
307        evm_config: EvmConfig,
308        task_spawner: Box<dyn TaskSpawner + 'static>,
309        proof_permits: usize,
310    ) -> Self {
311        let signers = parking_lot::RwLock::new(Default::default());
312        // get the block number of the latest block
313        let starting_block = U256::from(
314            provider
315                .header_by_number_or_tag(BlockNumberOrTag::Latest)
316                .ok()
317                .flatten()
318                .map(|header| header.number())
319                .unwrap_or_default(),
320        );
321
322        let (raw_tx_sender, _) = broadcast::channel(DEFAULT_BROADCAST_CAPACITY);
323
324        Self {
325            provider,
326            pool,
327            network,
328            signers,
329            eth_cache,
330            gas_oracle,
331            gas_cap: gas_cap.into().into(),
332            max_simulate_blocks,
333            eth_proof_window,
334            starting_block,
335            task_spawner,
336            pending_block: Default::default(),
337            blocking_task_pool,
338            fee_history_cache,
339            evm_config,
340            blocking_task_guard: BlockingTaskGuard::new(proof_permits),
341            raw_tx_sender,
342        }
343    }
344}
345
346impl<Provider, Pool, Network, EvmConfig> EthApiInner<Provider, Pool, Network, EvmConfig>
347where
348    Provider: BlockReader,
349{
350    /// Returns a handle to data on disk.
351    #[inline]
352    pub const fn provider(&self) -> &Provider {
353        &self.provider
354    }
355
356    /// Returns a handle to data in memory.
357    #[inline]
358    pub const fn cache(&self) -> &EthStateCache<Provider::Block, Provider::Receipt> {
359        &self.eth_cache
360    }
361
362    /// Returns a handle to the pending block.
363    #[inline]
364    pub const fn pending_block(
365        &self,
366    ) -> &Mutex<Option<PendingBlock<Provider::Block, Provider::Receipt>>> {
367        &self.pending_block
368    }
369
370    /// Returns a handle to the task spawner.
371    #[inline]
372    pub const fn task_spawner(&self) -> &dyn TaskSpawner {
373        &*self.task_spawner
374    }
375
376    /// Returns a handle to the blocking thread pool.
377    #[inline]
378    pub const fn blocking_task_pool(&self) -> &BlockingTaskPool {
379        &self.blocking_task_pool
380    }
381
382    /// Returns a handle to the EVM config.
383    #[inline]
384    pub const fn evm_config(&self) -> &EvmConfig {
385        &self.evm_config
386    }
387
388    /// Returns a handle to the transaction pool.
389    #[inline]
390    pub const fn pool(&self) -> &Pool {
391        &self.pool
392    }
393
394    /// Returns the gas cap.
395    #[inline]
396    pub const fn gas_cap(&self) -> u64 {
397        self.gas_cap
398    }
399
400    /// Returns the `max_simulate_blocks`.
401    #[inline]
402    pub const fn max_simulate_blocks(&self) -> u64 {
403        self.max_simulate_blocks
404    }
405
406    /// Returns a handle to the gas oracle.
407    #[inline]
408    pub const fn gas_oracle(&self) -> &GasPriceOracle<Provider> {
409        &self.gas_oracle
410    }
411
412    /// Returns a handle to the fee history cache.
413    #[inline]
414    pub const fn fee_history_cache(&self) -> &FeeHistoryCache {
415        &self.fee_history_cache
416    }
417
418    /// Returns a handle to the signers.
419    #[inline]
420    pub const fn signers(
421        &self,
422    ) -> &parking_lot::RwLock<Vec<Box<dyn EthSigner<Provider::Transaction>>>> {
423        &self.signers
424    }
425
426    /// Returns the starting block.
427    #[inline]
428    pub const fn starting_block(&self) -> U256 {
429        self.starting_block
430    }
431
432    /// Returns the inner `Network`
433    #[inline]
434    pub const fn network(&self) -> &Network {
435        &self.network
436    }
437
438    /// The maximum number of blocks into the past for generating state proofs.
439    #[inline]
440    pub const fn eth_proof_window(&self) -> u64 {
441        self.eth_proof_window
442    }
443
444    /// Returns reference to [`BlockingTaskGuard`].
445    #[inline]
446    pub const fn blocking_task_guard(&self) -> &BlockingTaskGuard {
447        &self.blocking_task_guard
448    }
449
450    /// Returns [`broadcast::Receiver`] of new raw transactions
451    #[inline]
452    pub fn subscribe_to_raw_transactions(&self) -> broadcast::Receiver<Bytes> {
453        self.raw_tx_sender.subscribe()
454    }
455
456    /// Broadcasts raw transaction if there are active subscribers.
457    #[inline]
458    pub fn broadcast_raw_transaction(&self, raw_tx: Bytes) {
459        let _ = self.raw_tx_sender.send(raw_tx);
460    }
461}
462
463#[cfg(test)]
464mod tests {
465    use crate::{EthApi, EthApiBuilder};
466    use alloy_consensus::{Block, BlockBody, Header};
467    use alloy_eips::BlockNumberOrTag;
468    use alloy_primitives::{Signature, B256, U64};
469    use alloy_rpc_types::FeeHistory;
470    use jsonrpsee_types::error::INVALID_PARAMS_CODE;
471    use rand::Rng;
472    use reth_chain_state::CanonStateSubscriptions;
473    use reth_chainspec::{BaseFeeParams, ChainSpec, ChainSpecProvider};
474    use reth_ethereum_primitives::TransactionSigned;
475    use reth_evm_ethereum::EthEvmConfig;
476    use reth_network_api::noop::NoopNetwork;
477    use reth_provider::test_utils::{MockEthProvider, NoopProvider};
478    use reth_rpc_eth_api::EthApiServer;
479    use reth_storage_api::{BlockReader, BlockReaderIdExt, StateProviderFactory};
480    use reth_testing_utils::generators;
481    use reth_transaction_pool::test_utils::{testing_pool, TestPool};
482
483    fn build_test_eth_api<
484        P: BlockReaderIdExt<
485                Block = reth_ethereum_primitives::Block,
486                Receipt = reth_ethereum_primitives::Receipt,
487                Header = alloy_consensus::Header,
488            > + BlockReader
489            + ChainSpecProvider<ChainSpec = ChainSpec>
490            + StateProviderFactory
491            + CanonStateSubscriptions<Primitives = reth_ethereum_primitives::EthPrimitives>
492            + Unpin
493            + Clone
494            + 'static,
495    >(
496        provider: P,
497    ) -> EthApi<P, TestPool, NoopNetwork, EthEvmConfig> {
498        EthApiBuilder::new(
499            provider.clone(),
500            testing_pool(),
501            NoopNetwork::default(),
502            EthEvmConfig::new(provider.chain_spec()),
503        )
504        .build()
505    }
506
507    // Function to prepare the EthApi with mock data
508    fn prepare_eth_api(
509        newest_block: u64,
510        mut oldest_block: Option<B256>,
511        block_count: u64,
512        mock_provider: MockEthProvider,
513    ) -> (EthApi<MockEthProvider, TestPool, NoopNetwork, EthEvmConfig>, Vec<u128>, Vec<f64>) {
514        let mut rng = generators::rng();
515
516        // Build mock data
517        let mut gas_used_ratios = Vec::with_capacity(block_count as usize);
518        let mut base_fees_per_gas = Vec::with_capacity(block_count as usize);
519        let mut last_header = None;
520        let mut parent_hash = B256::default();
521
522        for i in (0..block_count).rev() {
523            let hash = rng.random();
524            // Note: Generates saner values to avoid invalid overflows later
525            let gas_limit = rng.random::<u32>() as u64;
526            let base_fee_per_gas: Option<u64> =
527                rng.random::<bool>().then(|| rng.random::<u32>() as u64);
528            let gas_used = rng.random::<u32>() as u64;
529
530            let header = Header {
531                number: newest_block - i,
532                gas_limit,
533                gas_used,
534                base_fee_per_gas,
535                parent_hash,
536                ..Default::default()
537            };
538            last_header = Some(header.clone());
539            parent_hash = hash;
540
541            const TOTAL_TRANSACTIONS: usize = 100;
542            let mut transactions = Vec::with_capacity(TOTAL_TRANSACTIONS);
543            for _ in 0..TOTAL_TRANSACTIONS {
544                let random_fee: u128 = rng.random();
545
546                if let Some(base_fee_per_gas) = header.base_fee_per_gas {
547                    let transaction = TransactionSigned::new_unhashed(
548                        reth_ethereum_primitives::Transaction::Eip1559(
549                            alloy_consensus::TxEip1559 {
550                                max_priority_fee_per_gas: random_fee,
551                                max_fee_per_gas: random_fee + base_fee_per_gas as u128,
552                                ..Default::default()
553                            },
554                        ),
555                        Signature::test_signature(),
556                    );
557
558                    transactions.push(transaction);
559                } else {
560                    let transaction = TransactionSigned::new_unhashed(
561                        reth_ethereum_primitives::Transaction::Legacy(Default::default()),
562                        Signature::test_signature(),
563                    );
564
565                    transactions.push(transaction);
566                }
567            }
568
569            mock_provider.add_block(
570                hash,
571                Block {
572                    header: header.clone(),
573                    body: BlockBody { transactions, ..Default::default() },
574                },
575            );
576            mock_provider.add_header(hash, header);
577
578            oldest_block.get_or_insert(hash);
579            gas_used_ratios.push(gas_used as f64 / gas_limit as f64);
580            base_fees_per_gas.push(base_fee_per_gas.map(|fee| fee as u128).unwrap_or_default());
581        }
582
583        // Add final base fee (for the next block outside of the request)
584        let last_header = last_header.unwrap();
585        base_fees_per_gas.push(BaseFeeParams::ethereum().next_block_base_fee(
586            last_header.gas_used,
587            last_header.gas_limit,
588            last_header.base_fee_per_gas.unwrap_or_default(),
589        ) as u128);
590
591        let eth_api = build_test_eth_api(mock_provider);
592
593        (eth_api, base_fees_per_gas, gas_used_ratios)
594    }
595
596    /// Invalid block range
597    #[tokio::test]
598    async fn test_fee_history_empty() {
599        let response = <EthApi<_, _, _, _> as EthApiServer<_, _, _, _>>::fee_history(
600            &build_test_eth_api(NoopProvider::default()),
601            U64::from(1),
602            BlockNumberOrTag::Latest,
603            None,
604        )
605        .await;
606        assert!(response.is_err());
607        let error_object = response.unwrap_err();
608        assert_eq!(error_object.code(), INVALID_PARAMS_CODE);
609    }
610
611    #[tokio::test]
612    /// Invalid block range (request is before genesis)
613    async fn test_fee_history_invalid_block_range_before_genesis() {
614        let block_count = 10;
615        let newest_block = 1337;
616        let oldest_block = None;
617
618        let (eth_api, _, _) =
619            prepare_eth_api(newest_block, oldest_block, block_count, MockEthProvider::default());
620
621        let response = <EthApi<_, _, _, _> as EthApiServer<_, _, _, _>>::fee_history(
622            &eth_api,
623            U64::from(newest_block + 1),
624            newest_block.into(),
625            Some(vec![10.0]),
626        )
627        .await;
628
629        assert!(response.is_err());
630        let error_object = response.unwrap_err();
631        assert_eq!(error_object.code(), INVALID_PARAMS_CODE);
632    }
633
634    #[tokio::test]
635    /// Invalid block range (request is in the future)
636    async fn test_fee_history_invalid_block_range_in_future() {
637        let block_count = 10;
638        let newest_block = 1337;
639        let oldest_block = None;
640
641        let (eth_api, _, _) =
642            prepare_eth_api(newest_block, oldest_block, block_count, MockEthProvider::default());
643
644        let response = <EthApi<_, _, _, _> as EthApiServer<_, _, _, _>>::fee_history(
645            &eth_api,
646            U64::from(1),
647            (newest_block + 1000).into(),
648            Some(vec![10.0]),
649        )
650        .await;
651
652        assert!(response.is_err());
653        let error_object = response.unwrap_err();
654        assert_eq!(error_object.code(), INVALID_PARAMS_CODE);
655    }
656
657    #[tokio::test]
658    /// Requesting no block should result in a default response
659    async fn test_fee_history_no_block_requested() {
660        let block_count = 10;
661        let newest_block = 1337;
662        let oldest_block = None;
663
664        let (eth_api, _, _) =
665            prepare_eth_api(newest_block, oldest_block, block_count, MockEthProvider::default());
666
667        let response = <EthApi<_, _, _, _> as EthApiServer<_, _, _, _>>::fee_history(
668            &eth_api,
669            U64::from(0),
670            newest_block.into(),
671            None,
672        )
673        .await
674        .unwrap();
675        assert_eq!(
676            response,
677            FeeHistory::default(),
678            "none: requesting no block should yield a default response"
679        );
680    }
681
682    #[tokio::test]
683    /// Requesting a single block should return 1 block (+ base fee for the next block over)
684    async fn test_fee_history_single_block() {
685        let block_count = 10;
686        let newest_block = 1337;
687        let oldest_block = None;
688
689        let (eth_api, base_fees_per_gas, gas_used_ratios) =
690            prepare_eth_api(newest_block, oldest_block, block_count, MockEthProvider::default());
691
692        let fee_history =
693            eth_api.fee_history(U64::from(1), newest_block.into(), None).await.unwrap();
694        assert_eq!(
695            fee_history.base_fee_per_gas,
696            &base_fees_per_gas[base_fees_per_gas.len() - 2..],
697            "one: base fee per gas is incorrect"
698        );
699        assert_eq!(
700            fee_history.base_fee_per_gas.len(),
701            2,
702            "one: should return base fee of the next block as well"
703        );
704        assert_eq!(
705            &fee_history.gas_used_ratio,
706            &gas_used_ratios[gas_used_ratios.len() - 1..],
707            "one: gas used ratio is incorrect"
708        );
709        assert_eq!(fee_history.oldest_block, newest_block, "one: oldest block is incorrect");
710        assert!(
711            fee_history.reward.is_none(),
712            "one: no percentiles were requested, so there should be no rewards result"
713        );
714    }
715
716    /// Requesting all blocks should be ok
717    #[tokio::test]
718    async fn test_fee_history_all_blocks() {
719        let block_count = 10;
720        let newest_block = 1337;
721        let oldest_block = None;
722
723        let (eth_api, base_fees_per_gas, gas_used_ratios) =
724            prepare_eth_api(newest_block, oldest_block, block_count, MockEthProvider::default());
725
726        let fee_history =
727            eth_api.fee_history(U64::from(block_count), newest_block.into(), None).await.unwrap();
728
729        assert_eq!(
730            &fee_history.base_fee_per_gas, &base_fees_per_gas,
731            "all: base fee per gas is incorrect"
732        );
733        assert_eq!(
734            fee_history.base_fee_per_gas.len() as u64,
735            block_count + 1,
736            "all: should return base fee of the next block as well"
737        );
738        assert_eq!(
739            &fee_history.gas_used_ratio, &gas_used_ratios,
740            "all: gas used ratio is incorrect"
741        );
742        assert_eq!(
743            fee_history.oldest_block,
744            newest_block - block_count + 1,
745            "all: oldest block is incorrect"
746        );
747        assert!(
748            fee_history.reward.is_none(),
749            "all: no percentiles were requested, so there should be no rewards result"
750        );
751    }
752}