alloy_provider/provider/
trait.rs

1//! Ethereum JSON-RPC provider.
2
3#![allow(unknown_lints, mismatched_lifetime_syntaxes)]
4
5#[cfg(feature = "pubsub")]
6use super::get_block::SubFullBlocks;
7use super::{DynProvider, Empty, EthCallMany, MulticallBuilder, WatchBlocks};
8#[cfg(feature = "pubsub")]
9use crate::GetSubscription;
10use crate::{
11    heart::PendingTransactionError,
12    utils::{self, Eip1559Estimation, Eip1559Estimator},
13    EthCall, EthGetBlock, Identity, PendingTransaction, PendingTransactionBuilder,
14    PendingTransactionConfig, ProviderBuilder, ProviderCall, RootProvider, RpcWithBlock,
15    SendableTx,
16};
17use alloy_consensus::BlockHeader;
18use alloy_eips::eip2718::Encodable2718;
19use alloy_json_rpc::{RpcError, RpcRecv, RpcSend};
20use alloy_network::{Ethereum, Network};
21use alloy_network_primitives::{BlockResponse, ReceiptResponse};
22use alloy_primitives::{
23    hex, Address, BlockHash, BlockNumber, Bytes, StorageKey, StorageValue, TxHash, B256, U128,
24    U256, U64,
25};
26use alloy_rpc_client::{ClientRef, NoParams, PollerBuilder, WeakClient};
27#[cfg(feature = "pubsub")]
28use alloy_rpc_types_eth::pubsub::{Params, SubscriptionKind};
29use alloy_rpc_types_eth::{
30    erc4337::TransactionConditional,
31    simulate::{SimulatePayload, SimulatedBlock},
32    AccessListResult, BlockId, BlockNumberOrTag, Bundle, EIP1186AccountProofResponse,
33    EthCallResponse, FeeHistory, Filter, FilterChanges, Index, Log, SyncStatus,
34};
35use alloy_transport::TransportResult;
36use serde_json::value::RawValue;
37use std::borrow::Cow;
38
39/// A task that polls the provider with `eth_getFilterChanges`, returning a list of `R`.
40///
41/// See [`PollerBuilder`] for more details.
42pub type FilterPollerBuilder<R> = PollerBuilder<(U256,), Vec<R>>;
43
44/// Ethereum JSON-RPC interface.
45///
46/// # Subscriptions
47///
48/// The provider supports `pubsub` subscriptions to new block headers and
49/// pending transactions. This is only available on `pubsub` clients, such as
50/// Websockets or IPC.
51///
52/// For a polling alternatives available over HTTP, use the `watch_*` methods.
53/// However, be aware that polling increases RPC usage drastically.
54///
55/// ## Special treatment of EIP-1559
56///
57/// While many RPC features are encapsulated by extension traits,
58/// EIP-1559 fee estimation is generally assumed to be on by default. We
59/// generally assume that EIP-1559 is supported by the client and will
60/// proactively use it by default.
61///
62/// As a result, the provider supports EIP-1559 fee estimation the ethereum
63/// [`TransactionBuilder`] will use it by default. We acknowledge that this
64/// means EIP-1559 has a privileged status in comparison to other transaction
65/// types. Networks that DO NOT support EIP-1559 should create their own
66/// [`TransactionBuilder`] and Fillers to change this behavior.
67///
68/// [`TransactionBuilder`]: alloy_network::TransactionBuilder
69#[cfg_attr(target_family = "wasm", async_trait::async_trait(?Send))]
70#[cfg_attr(not(target_family = "wasm"), async_trait::async_trait)]
71#[auto_impl::auto_impl(&, &mut, Rc, Arc, Box)]
72pub trait Provider<N: Network = Ethereum>: Send + Sync {
73    /// Returns the root provider.
74    fn root(&self) -> &RootProvider<N>;
75
76    /// Returns the [`ProviderBuilder`] to build on.
77    fn builder() -> ProviderBuilder<Identity, Identity, N>
78    where
79        Self: Sized,
80    {
81        ProviderBuilder::default()
82    }
83
84    /// Returns the RPC client used to send requests.
85    ///
86    /// NOTE: this method should not be overridden.
87    #[inline]
88    fn client(&self) -> ClientRef<'_> {
89        self.root().client()
90    }
91
92    /// Returns a [`Weak`](std::sync::Weak) RPC client used to send requests.
93    ///
94    /// NOTE: this method should not be overridden.
95    #[inline]
96    fn weak_client(&self) -> WeakClient {
97        self.root().weak_client()
98    }
99
100    /// Returns a type erased provider wrapped in Arc. See [`DynProvider`].
101    ///
102    /// ```no_run
103    /// use alloy_provider::{DynProvider, Provider, ProviderBuilder};
104    ///
105    /// # async fn f() -> Result<(), Box<dyn std::error::Error>> {
106    /// let provider: DynProvider =
107    ///     ProviderBuilder::new().on_builtin("http://localhost:8080").await?.erased();
108    /// let block = provider.get_block_number().await?;
109    /// # Ok(())
110    /// # }
111    /// ```
112    #[auto_impl(keep_default_for(&, &mut, Rc, Arc, Box))]
113    #[doc(alias = "boxed")]
114    fn erased(self) -> DynProvider<N>
115    where
116        Self: Sized + 'static,
117    {
118        DynProvider::new(self)
119    }
120
121    /// Gets the accounts in the remote node. This is usually empty unless you're using a local
122    /// node.
123    fn get_accounts(&self) -> ProviderCall<NoParams, Vec<Address>> {
124        self.client().request_noparams("eth_accounts").into()
125    }
126
127    /// Returns the base fee per blob gas (blob gas price) in wei.
128    fn get_blob_base_fee(&self) -> ProviderCall<NoParams, U128, u128> {
129        self.client()
130            .request_noparams("eth_blobBaseFee")
131            .map_resp(utils::convert_u128 as fn(U128) -> u128)
132            .into()
133    }
134
135    /// Get the last block number available.
136    fn get_block_number(&self) -> ProviderCall<NoParams, U64, BlockNumber> {
137        self.client()
138            .request_noparams("eth_blockNumber")
139            .map_resp(utils::convert_u64 as fn(U64) -> u64)
140            .into()
141    }
142
143    /// Get the block number for a given block identifier.
144    ///
145    /// This is a convenience function that fetches the full block when the block identifier is not
146    /// a number.
147    async fn get_block_number_by_id(
148        &self,
149        block_id: BlockId,
150    ) -> TransportResult<Option<BlockNumber>> {
151        match block_id {
152            BlockId::Number(BlockNumberOrTag::Number(num)) => Ok(Some(num)),
153            BlockId::Number(BlockNumberOrTag::Latest) => self.get_block_number().await.map(Some),
154            _ => {
155                let block = self.get_block(block_id).await?;
156                Ok(block.map(|b| b.header().number()))
157            }
158        }
159    }
160
161    /// Execute a smart contract call with a transaction request and state
162    /// overrides, without publishing a transaction.
163    ///
164    /// This function returns [`EthCall`] which can be used to execute the
165    /// call, or to add [`StateOverride`] or a [`BlockId`]. If no overrides
166    /// or block ID is provided, the call will be executed on the pending block
167    /// with the current state.
168    ///
169    /// [`StateOverride`]: alloy_rpc_types_eth::state::StateOverride
170    ///
171    /// # Examples
172    ///
173    /// ```no_run
174    /// # use alloy_provider::Provider;
175    /// # use alloy_eips::BlockId;
176    /// # use alloy_rpc_types_eth::state::StateOverride;
177    /// # use alloy_transport::BoxTransport;
178    /// # async fn example<P: Provider>(
179    /// #    provider: P,
180    /// #    my_overrides: StateOverride
181    /// # ) -> Result<(), Box<dyn std::error::Error>> {
182    /// # let tx = alloy_rpc_types_eth::transaction::TransactionRequest::default();
183    /// // Execute a call on the latest block, with no state overrides
184    /// let output = provider.call(tx).await?;
185    /// # Ok(())
186    /// # }
187    /// ```
188    #[doc(alias = "eth_call")]
189    #[doc(alias = "call_with_overrides")]
190    fn call(&self, tx: N::TransactionRequest) -> EthCall<N, Bytes> {
191        EthCall::call(self.weak_client(), tx).block(BlockNumberOrTag::Pending.into())
192    }
193
194    /// Execute a list of [`Bundle`] against the provided [`StateContext`] and [`StateOverride`],
195    /// without publishing a transaction.
196    ///
197    /// This function returns [`EthCallMany`] builder which is used to execute the call, and also
198    /// set the [`StateContext`] and [`StateOverride`].
199    ///
200    /// [`StateContext`]: alloy_rpc_types_eth::StateContext
201    /// [`StateOverride`]: alloy_rpc_types_eth::state::StateOverride
202    #[doc(alias = "eth_callMany")]
203    fn call_many<'req>(
204        &self,
205        bundles: &'req [Bundle],
206    ) -> EthCallMany<'req, N, Vec<Vec<EthCallResponse>>> {
207        EthCallMany::new(self.weak_client(), bundles)
208    }
209
210    /// Execute a multicall by leveraging the [`MulticallBuilder`].
211    ///
212    /// Call [`MulticallBuilder::dynamic`] to add calls dynamically instead.
213    ///
214    /// See the [`MulticallBuilder`] documentation for more details.
215    #[auto_impl(keep_default_for(&, &mut, Rc, Arc, Box))]
216    fn multicall(&self) -> MulticallBuilder<Empty, &Self, N>
217    where
218        Self: Sized,
219    {
220        MulticallBuilder::new(self)
221    }
222
223    /// Executes an arbitrary number of transactions on top of the requested state.
224    ///
225    /// The transactions are packed into individual blocks. Overrides can be provided.
226    #[doc(alias = "eth_simulateV1")]
227    fn simulate<'req>(
228        &self,
229        payload: &'req SimulatePayload,
230    ) -> RpcWithBlock<&'req SimulatePayload, Vec<SimulatedBlock<N::BlockResponse>>> {
231        self.client().request("eth_simulateV1", payload).into()
232    }
233
234    /// Gets the chain ID.
235    fn get_chain_id(&self) -> ProviderCall<NoParams, U64, u64> {
236        self.client()
237            .request_noparams("eth_chainId")
238            .map_resp(utils::convert_u64 as fn(U64) -> u64)
239            .into()
240    }
241
242    /// Create an [EIP-2930] access list.
243    ///
244    /// [EIP-2930]: https://eips.ethereum.org/EIPS/eip-2930
245    fn create_access_list<'a>(
246        &self,
247        request: &'a N::TransactionRequest,
248    ) -> RpcWithBlock<&'a N::TransactionRequest, AccessListResult> {
249        self.client().request("eth_createAccessList", request).into()
250    }
251
252    /// Create an [`EthCall`] future to estimate the gas required for a
253    /// transaction.
254    ///
255    /// The future can be used to specify a [`StateOverride`] or [`BlockId`]
256    /// before dispatching the call. If no overrides or block ID is provided,
257    /// the gas estimate will be computed for the pending block with the
258    /// current state.
259    ///
260    /// [`StateOverride`]: alloy_rpc_types_eth::state::StateOverride
261    ///
262    /// # Note
263    ///
264    /// Not all client implementations support state overrides for eth_estimateGas.
265    fn estimate_gas(&self, tx: N::TransactionRequest) -> EthCall<N, U64, u64> {
266        EthCall::gas_estimate(self.weak_client(), tx)
267            .block(BlockNumberOrTag::Pending.into())
268            .map_resp(utils::convert_u64)
269    }
270
271    /// Estimates the EIP1559 `maxFeePerGas` and `maxPriorityFeePerGas` fields.
272    ///
273    /// Receives an [Eip1559Estimator] that can be used to modify
274    /// how to estimate these fees.
275    async fn estimate_eip1559_fees_with(
276        &self,
277        estimator: Eip1559Estimator,
278    ) -> TransportResult<Eip1559Estimation> {
279        let fee_history = self
280            .get_fee_history(
281                utils::EIP1559_FEE_ESTIMATION_PAST_BLOCKS,
282                BlockNumberOrTag::Latest,
283                &[utils::EIP1559_FEE_ESTIMATION_REWARD_PERCENTILE],
284            )
285            .await?;
286
287        // if the base fee of the Latest block is 0 then we need check if the latest block even has
288        // a base fee/supports EIP1559
289        let base_fee_per_gas = match fee_history.latest_block_base_fee() {
290            Some(base_fee) if base_fee != 0 => base_fee,
291            _ => {
292                // empty response, fetch basefee from latest block directly
293                self.get_block_by_number(BlockNumberOrTag::Latest)
294                    .await?
295                    .ok_or(RpcError::NullResp)?
296                    .header()
297                    .as_ref()
298                    .base_fee_per_gas()
299                    .ok_or(RpcError::UnsupportedFeature("eip1559"))?
300                    .into()
301            }
302        };
303
304        Ok(estimator.estimate(base_fee_per_gas, &fee_history.reward.unwrap_or_default()))
305    }
306
307    /// Estimates the EIP1559 `maxFeePerGas` and `maxPriorityFeePerGas` fields.
308    ///
309    /// Uses the builtin estimator [`utils::eip1559_default_estimator`] function.
310    async fn estimate_eip1559_fees(&self) -> TransportResult<Eip1559Estimation> {
311        self.estimate_eip1559_fees_with(Eip1559Estimator::default()).await
312    }
313
314    /// Returns a collection of historical gas information [FeeHistory] which
315    /// can be used to calculate the EIP1559 fields `maxFeePerGas` and `maxPriorityFeePerGas`.
316    /// `block_count` can range from 1 to 1024 blocks in a single request.
317    async fn get_fee_history(
318        &self,
319        block_count: u64,
320        last_block: BlockNumberOrTag,
321        reward_percentiles: &[f64],
322    ) -> TransportResult<FeeHistory> {
323        self.client()
324            .request("eth_feeHistory", (U64::from(block_count), last_block, reward_percentiles))
325            .await
326    }
327
328    /// Gets the current gas price in wei.
329    fn get_gas_price(&self) -> ProviderCall<NoParams, U128, u128> {
330        self.client()
331            .request_noparams("eth_gasPrice")
332            .map_resp(utils::convert_u128 as fn(U128) -> u128)
333            .into()
334    }
335
336    /// Retrieves account information ([Account](alloy_rpc_types_eth::Account)) for the given
337    /// [Address] at the particular [BlockId].
338    ///
339    /// Note: This is slightly different than `eth_getAccount` and not all clients support this
340    /// endpoint.
341    fn get_account_info(
342        &self,
343        address: Address,
344    ) -> RpcWithBlock<Address, alloy_rpc_types_eth::AccountInfo> {
345        self.client().request("eth_getAccountInfo", address).into()
346    }
347
348    /// Retrieves account information ([Account](alloy_consensus::Account)) for the given [Address]
349    /// at the particular [BlockId].
350    fn get_account(&self, address: Address) -> RpcWithBlock<Address, alloy_consensus::Account> {
351        self.client().request("eth_getAccount", address).into()
352    }
353
354    /// Gets the balance of the account.
355    ///
356    /// Defaults to the latest block. See also [`RpcWithBlock::block_id`].
357    fn get_balance(&self, address: Address) -> RpcWithBlock<Address, U256, U256> {
358        self.client().request("eth_getBalance", address).into()
359    }
360
361    /// Gets a block by either its hash, tag, or number
362    ///
363    /// By default this fetches the block with only the transaction hashes, and not full
364    /// transactions.
365    ///
366    /// To get full transactions one can do:
367    ///
368    /// ```ignore
369    /// let block = provider.get_block(BlockId::latest()).full().await.unwrap();
370    /// ```
371    fn get_block(&self, block: BlockId) -> EthGetBlock<N::BlockResponse> {
372        match block {
373            BlockId::Hash(hash) => EthGetBlock::by_hash(hash.block_hash, self.client()),
374            BlockId::Number(number) => EthGetBlock::by_number(number, self.client()),
375        }
376    }
377
378    /// Gets a block by its [BlockHash]
379    ///
380    /// By default this fetches the block with only the transaction hashes populated in the block,
381    /// and not the full transactions.
382    ///
383    /// # Examples
384    ///
385    /// ```no_run
386    /// # use alloy_provider::{Provider, ProviderBuilder};
387    /// # use alloy_primitives::b256;
388    ///
389    /// #[tokio::main]
390    /// async fn main() {
391    ///     let provider =
392    ///         ProviderBuilder::new().connect_http("https://eth.merkle.io".parse().unwrap());
393    ///     let block_hash = b256!("6032d03ee8e43e8999c2943152a4daebfc4b75b7f7a9647d2677299d215127da");
394    ///
395    ///     // Gets a block by its hash with only transactions hashes.
396    ///     let block = provider.get_block_by_hash(block_hash).await.unwrap();
397    ///
398    ///     // Gets a block by its hash with full transactions.
399    ///     let block = provider.get_block_by_hash(block_hash).full().await.unwrap();
400    /// }
401    /// ```
402    fn get_block_by_hash(&self, hash: BlockHash) -> EthGetBlock<N::BlockResponse> {
403        EthGetBlock::by_hash(hash, self.client())
404    }
405
406    /// Gets a block by its [BlockNumberOrTag]
407    ///
408    /// By default this fetches the block with only the transaction hashes populated in the block,
409    /// and not the full transactions.
410    ///
411    /// # Examples
412    ///
413    /// ```no_run
414    /// # use alloy_provider::{Provider, ProviderBuilder};
415    /// # use alloy_eips::BlockNumberOrTag;
416    ///
417    /// #[tokio::main]
418    /// async fn main() {
419    ///     let provider =
420    ///         ProviderBuilder::new().connect_http("https://eth.merkle.io".parse().unwrap());
421    ///     let num = BlockNumberOrTag::Number(0);
422    ///
423    ///     // Gets a block by its number with only transactions hashes.
424    ///     let block = provider.get_block_by_number(num).await.unwrap();
425    ///
426    ///     // Gets a block by its number with full transactions.
427    ///     let block = provider.get_block_by_number(num).full().await.unwrap();
428    /// }
429    /// ```
430    fn get_block_by_number(&self, number: BlockNumberOrTag) -> EthGetBlock<N::BlockResponse> {
431        EthGetBlock::by_number(number, self.client())
432    }
433
434    /// Returns the number of transactions in a block from a block matching the given block hash.
435    async fn get_block_transaction_count_by_hash(
436        &self,
437        hash: BlockHash,
438    ) -> TransportResult<Option<u64>> {
439        self.client()
440            .request("eth_getBlockTransactionCountByHash", (hash,))
441            .await
442            .map(|opt_count: Option<U64>| opt_count.map(|count| count.to::<u64>()))
443    }
444
445    /// Returns the number of transactions in a block matching the given block number.
446    async fn get_block_transaction_count_by_number(
447        &self,
448        block_number: BlockNumberOrTag,
449    ) -> TransportResult<Option<u64>> {
450        self.client()
451            .request("eth_getBlockTransactionCountByNumber", (block_number,))
452            .await
453            .map(|opt_count: Option<U64>| opt_count.map(|count| count.to::<u64>()))
454    }
455
456    /// Gets the selected block [BlockId] receipts.
457    fn get_block_receipts(
458        &self,
459        block: BlockId,
460    ) -> ProviderCall<(BlockId,), Option<Vec<N::ReceiptResponse>>> {
461        self.client().request("eth_getBlockReceipts", (block,)).into()
462    }
463
464    /// Gets the bytecode located at the corresponding [Address].
465    fn get_code_at(&self, address: Address) -> RpcWithBlock<Address, Bytes> {
466        self.client().request("eth_getCode", address).into()
467    }
468
469    /// Watch for new blocks by polling the provider with
470    /// [`eth_getFilterChanges`](Self::get_filter_changes).
471    ///
472    /// Returns a builder that is used to configure the poller. See [`PollerBuilder`] for more
473    /// details.
474    ///
475    /// # Examples
476    ///
477    /// Get the next 5 blocks:
478    ///
479    /// ```no_run
480    /// # async fn example(provider: impl alloy_provider::Provider) -> Result<(), Box<dyn std::error::Error>> {
481    /// use futures::StreamExt;
482    ///
483    /// let poller = provider.watch_blocks().await?;
484    /// let mut stream = poller.into_stream().flat_map(futures::stream::iter).take(5);
485    /// while let Some(block_hash) = stream.next().await {
486    ///    println!("new block: {block_hash}");
487    /// }
488    /// # Ok(())
489    /// # }
490    /// ```
491    async fn watch_blocks(&self) -> TransportResult<FilterPollerBuilder<B256>> {
492        let id = self.new_block_filter().await?;
493        Ok(PollerBuilder::new(self.weak_client(), "eth_getFilterChanges", (id,)))
494    }
495
496    /// Watch for new blocks by polling the provider with
497    /// [`eth_getFilterChanges`](Self::get_filter_changes) and transforming the returned block
498    /// hashes into full blocks bodies.
499    ///
500    /// Returns the [`WatchBlocks`] type which consumes the stream of block hashes from
501    /// [`PollerBuilder`] and returns a stream of [`BlockResponse`]'s.
502    ///
503    /// # Examples
504    ///
505    /// Get the next 5 full blocks:
506    ///
507    /// ```no_run
508    /// # async fn example(provider: impl alloy_provider::Provider) -> Result<(), Box<dyn std::error::Error>> {
509    /// use futures::StreamExt;
510    ///
511    /// let poller = provider.watch_full_blocks().await?.full();
512    /// let mut stream = poller.into_stream().flat_map(futures::stream::iter).take(5);
513    /// while let Some(block) = stream.next().await {
514    ///   println!("new block: {block:#?}");
515    /// }
516    /// # Ok(())
517    /// # }
518    /// ```
519    async fn watch_full_blocks(&self) -> TransportResult<WatchBlocks<N::BlockResponse>> {
520        let id = self.new_block_filter().await?;
521        let poller = PollerBuilder::new(self.weak_client(), "eth_getFilterChanges", (id,));
522
523        Ok(WatchBlocks::new(poller))
524    }
525
526    /// Watch for new pending transaction by polling the provider with
527    /// [`eth_getFilterChanges`](Self::get_filter_changes).
528    ///
529    /// Returns a builder that is used to configure the poller. See [`PollerBuilder`] for more
530    /// details.
531    ///
532    /// # Examples
533    ///
534    /// Get the next 5 pending transaction hashes:
535    ///
536    /// ```no_run
537    /// # async fn example(provider: impl alloy_provider::Provider) -> Result<(), Box<dyn std::error::Error>> {
538    /// use futures::StreamExt;
539    ///
540    /// let poller = provider.watch_pending_transactions().await?;
541    /// let mut stream = poller.into_stream().flat_map(futures::stream::iter).take(5);
542    /// while let Some(tx_hash) = stream.next().await {
543    ///    println!("new pending transaction hash: {tx_hash}");
544    /// }
545    /// # Ok(())
546    /// # }
547    /// ```
548    async fn watch_pending_transactions(&self) -> TransportResult<FilterPollerBuilder<B256>> {
549        let id = self.new_pending_transactions_filter(false).await?;
550        Ok(PollerBuilder::new(self.weak_client(), "eth_getFilterChanges", (id,)))
551    }
552
553    /// Watch for new logs using the given filter by polling the provider with
554    /// [`eth_getFilterChanges`](Self::get_filter_changes).
555    ///
556    /// Returns a builder that is used to configure the poller. See [`PollerBuilder`] for more
557    /// details.
558    ///
559    /// # Examples
560    ///
561    /// Get the next 5 USDC transfer logs:
562    ///
563    /// ```no_run
564    /// # async fn example(provider: impl alloy_provider::Provider) -> Result<(), Box<dyn std::error::Error>> {
565    /// use alloy_primitives::{address, b256};
566    /// use alloy_rpc_types_eth::Filter;
567    /// use futures::StreamExt;
568    ///
569    /// let address = address!("a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48");
570    /// let transfer_signature = b256!("ddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef");
571    /// let filter = Filter::new().address(address).event_signature(transfer_signature);
572    ///
573    /// let poller = provider.watch_logs(&filter).await?;
574    /// let mut stream = poller.into_stream().flat_map(futures::stream::iter).take(5);
575    /// while let Some(log) = stream.next().await {
576    ///    println!("new log: {log:#?}");
577    /// }
578    /// # Ok(())
579    /// # }
580    /// ```
581    async fn watch_logs(&self, filter: &Filter) -> TransportResult<FilterPollerBuilder<Log>> {
582        let id = self.new_filter(filter).await?;
583        Ok(PollerBuilder::new(self.weak_client(), "eth_getFilterChanges", (id,)))
584    }
585
586    /// Watch for new pending transaction bodies by polling the provider with
587    /// [`eth_getFilterChanges`](Self::get_filter_changes).
588    ///
589    /// Returns a builder that is used to configure the poller. See [`PollerBuilder`] for more
590    /// details.
591    ///
592    /// # Support
593    ///
594    /// This endpoint might not be supported by all clients.
595    ///
596    /// # Examples
597    ///
598    /// Get the next 5 pending transaction bodies:
599    ///
600    /// ```no_run
601    /// # async fn example(provider: impl alloy_provider::Provider) -> Result<(), Box<dyn std::error::Error>> {
602    /// use futures::StreamExt;
603    ///
604    /// let poller = provider.watch_full_pending_transactions().await?;
605    /// let mut stream = poller.into_stream().flat_map(futures::stream::iter).take(5);
606    /// while let Some(tx) = stream.next().await {
607    ///    println!("new pending transaction: {tx:#?}");
608    /// }
609    /// # Ok(())
610    /// # }
611    /// ```
612    async fn watch_full_pending_transactions(
613        &self,
614    ) -> TransportResult<FilterPollerBuilder<N::TransactionResponse>> {
615        let id = self.new_pending_transactions_filter(true).await?;
616        Ok(PollerBuilder::new(self.weak_client(), "eth_getFilterChanges", (id,)))
617    }
618
619    /// Get a list of values that have been added since the last poll.
620    ///
621    /// The return value depends on what stream `id` corresponds to.
622    /// See [`FilterChanges`] for all possible return values.
623    #[auto_impl(keep_default_for(&, &mut, Rc, Arc, Box))]
624    async fn get_filter_changes<R: RpcRecv>(&self, id: U256) -> TransportResult<Vec<R>>
625    where
626        Self: Sized,
627    {
628        self.client().request("eth_getFilterChanges", (id,)).await
629    }
630
631    /// Get a list of values that have been added since the last poll.
632    ///
633    /// This returns an enum over all possible return values. You probably want to use
634    /// [`get_filter_changes`](Self::get_filter_changes) instead.
635    async fn get_filter_changes_dyn(&self, id: U256) -> TransportResult<FilterChanges> {
636        self.client().request("eth_getFilterChanges", (id,)).await
637    }
638
639    /// Retrieves a [`Vec<Log>`] for the given filter ID.
640    async fn get_filter_logs(&self, id: U256) -> TransportResult<Vec<Log>> {
641        self.client().request("eth_getFilterLogs", (id,)).await
642    }
643
644    /// Request provider to uninstall the filter with the given ID.
645    async fn uninstall_filter(&self, id: U256) -> TransportResult<bool> {
646        self.client().request("eth_uninstallFilter", (id,)).await
647    }
648
649    /// Watch for the confirmation of a single pending transaction with the given configuration.
650    ///
651    /// Note that this is handled internally rather than calling any specific RPC method, and as
652    /// such should not be overridden.
653    #[inline]
654    async fn watch_pending_transaction(
655        &self,
656        config: PendingTransactionConfig,
657    ) -> Result<PendingTransaction, PendingTransactionError> {
658        self.root().watch_pending_transaction(config).await
659    }
660
661    /// Retrieves a [`Vec<Log>`] with the given [Filter].
662    async fn get_logs(&self, filter: &Filter) -> TransportResult<Vec<Log>> {
663        self.client().request("eth_getLogs", (filter,)).await
664    }
665
666    /// Get the account and storage values of the specified account including the merkle proofs.
667    ///
668    /// This call can be used to verify that the data has not been tampered with.
669    fn get_proof(
670        &self,
671        address: Address,
672        keys: Vec<StorageKey>,
673    ) -> RpcWithBlock<(Address, Vec<StorageKey>), EIP1186AccountProofResponse> {
674        self.client().request("eth_getProof", (address, keys)).into()
675    }
676
677    /// Gets the specified storage value from [Address].
678    fn get_storage_at(
679        &self,
680        address: Address,
681        key: U256,
682    ) -> RpcWithBlock<(Address, U256), StorageValue> {
683        self.client().request("eth_getStorageAt", (address, key)).into()
684    }
685
686    /// Gets a transaction by its sender and nonce.
687    ///
688    /// Note: not supported by all clients.
689    fn get_transaction_by_sender_nonce(
690        &self,
691        sender: Address,
692        nonce: u64,
693    ) -> ProviderCall<(Address, U64), Option<N::TransactionResponse>> {
694        self.client()
695            .request("eth_getTransactionBySenderAndNonce", (sender, U64::from(nonce)))
696            .into()
697    }
698
699    /// Gets a transaction by its [TxHash].
700    fn get_transaction_by_hash(
701        &self,
702        hash: TxHash,
703    ) -> ProviderCall<(TxHash,), Option<N::TransactionResponse>> {
704        self.client().request("eth_getTransactionByHash", (hash,)).into()
705    }
706
707    /// Gets a transaction by block hash and transaction index position.
708    fn get_transaction_by_block_hash_and_index(
709        &self,
710        block_hash: B256,
711        index: usize,
712    ) -> ProviderCall<(B256, Index), Option<N::TransactionResponse>> {
713        self.client()
714            .request("eth_getTransactionByBlockHashAndIndex", (block_hash, Index(index)))
715            .into()
716    }
717
718    /// Gets a raw transaction by block hash and transaction index position.
719    fn get_raw_transaction_by_block_hash_and_index(
720        &self,
721        block_hash: B256,
722        index: usize,
723    ) -> ProviderCall<(B256, Index), Option<Bytes>> {
724        self.client()
725            .request("eth_getRawTransactionByBlockHashAndIndex", (block_hash, Index(index)))
726            .into()
727    }
728
729    /// Gets a transaction by block number and transaction index position.
730    fn get_transaction_by_block_number_and_index(
731        &self,
732        block_number: BlockNumberOrTag,
733        index: usize,
734    ) -> ProviderCall<(BlockNumberOrTag, Index), Option<N::TransactionResponse>> {
735        self.client()
736            .request("eth_getTransactionByBlockNumberAndIndex", (block_number, Index(index)))
737            .into()
738    }
739
740    /// Gets a raw transaction by block number and transaction index position.
741    fn get_raw_transaction_by_block_number_and_index(
742        &self,
743        block_number: BlockNumberOrTag,
744        index: usize,
745    ) -> ProviderCall<(BlockNumberOrTag, Index), Option<Bytes>> {
746        self.client()
747            .request("eth_getRawTransactionByBlockNumberAndIndex", (block_number, Index(index)))
748            .into()
749    }
750
751    /// Returns the EIP-2718 encoded transaction if it exists, see also
752    /// [Decodable2718](alloy_eips::eip2718::Decodable2718).
753    ///
754    /// If the transaction is an EIP-4844 transaction that is still in the pool (pending) it will
755    /// include the sidecar, otherwise it will the consensus variant without the sidecar:
756    /// [TxEip4844](alloy_consensus::transaction::eip4844::TxEip4844).
757    ///
758    /// This can be decoded into [TxEnvelope](alloy_consensus::transaction::TxEnvelope).
759    fn get_raw_transaction_by_hash(&self, hash: TxHash) -> ProviderCall<(TxHash,), Option<Bytes>> {
760        self.client().request("eth_getRawTransactionByHash", (hash,)).into()
761    }
762
763    /// Gets the transaction count (AKA "nonce") of the corresponding address.
764    #[doc(alias = "get_nonce")]
765    #[doc(alias = "get_account_nonce")]
766    fn get_transaction_count(
767        &self,
768        address: Address,
769    ) -> RpcWithBlock<Address, U64, u64, fn(U64) -> u64> {
770        self.client()
771            .request("eth_getTransactionCount", address)
772            .map_resp(utils::convert_u64 as fn(U64) -> u64)
773            .into()
774    }
775
776    /// Gets a transaction receipt if it exists, by its [TxHash].
777    fn get_transaction_receipt(
778        &self,
779        hash: TxHash,
780    ) -> ProviderCall<(TxHash,), Option<N::ReceiptResponse>> {
781        self.client().request("eth_getTransactionReceipt", (hash,)).into()
782    }
783
784    /// Gets an uncle block through the tag [BlockId] and index [u64].
785    async fn get_uncle(&self, tag: BlockId, idx: u64) -> TransportResult<Option<N::BlockResponse>> {
786        let idx = U64::from(idx);
787        match tag {
788            BlockId::Hash(hash) => {
789                self.client()
790                    .request("eth_getUncleByBlockHashAndIndex", (hash.block_hash, idx))
791                    .await
792            }
793            BlockId::Number(number) => {
794                self.client().request("eth_getUncleByBlockNumberAndIndex", (number, idx)).await
795            }
796        }
797    }
798
799    /// Gets the number of uncles for the block specified by the tag [BlockId].
800    async fn get_uncle_count(&self, tag: BlockId) -> TransportResult<u64> {
801        match tag {
802            BlockId::Hash(hash) => self
803                .client()
804                .request("eth_getUncleCountByBlockHash", (hash.block_hash,))
805                .await
806                .map(|count: U64| count.to::<u64>()),
807            BlockId::Number(number) => self
808                .client()
809                .request("eth_getUncleCountByBlockNumber", (number,))
810                .await
811                .map(|count: U64| count.to::<u64>()),
812        }
813    }
814
815    /// Returns a suggestion for the current `maxPriorityFeePerGas` in wei.
816    fn get_max_priority_fee_per_gas(&self) -> ProviderCall<NoParams, U128, u128> {
817        self.client()
818            .request_noparams("eth_maxPriorityFeePerGas")
819            .map_resp(utils::convert_u128 as fn(U128) -> u128)
820            .into()
821    }
822
823    /// Notify the provider that we are interested in new blocks.
824    ///
825    /// Returns the ID to use with [`eth_getFilterChanges`](Self::get_filter_changes).
826    ///
827    /// See also [`watch_blocks`](Self::watch_blocks) to configure a poller.
828    async fn new_block_filter(&self) -> TransportResult<U256> {
829        self.client().request_noparams("eth_newBlockFilter").await
830    }
831
832    /// Notify the provider that we are interested in logs that match the given filter.
833    ///
834    /// Returns the ID to use with [`eth_getFilterChanges`](Self::get_filter_changes).
835    ///
836    /// See also [`watch_logs`](Self::watch_logs) to configure a poller.
837    async fn new_filter(&self, filter: &Filter) -> TransportResult<U256> {
838        self.client().request("eth_newFilter", (filter,)).await
839    }
840
841    /// Notify the provider that we are interested in new pending transactions.
842    ///
843    /// If `full` is `true`, the stream will consist of full transaction bodies instead of just the
844    /// hashes. This not supported by all clients.
845    ///
846    /// Returns the ID to use with [`eth_getFilterChanges`](Self::get_filter_changes).
847    ///
848    /// See also [`watch_pending_transactions`](Self::watch_pending_transactions) to configure a
849    /// poller.
850    async fn new_pending_transactions_filter(&self, full: bool) -> TransportResult<U256> {
851        // NOTE: We don't want to send `false` as the client might not support it.
852        let param = if full { &[true][..] } else { &[] };
853        self.client().request("eth_newPendingTransactionFilter", param).await
854    }
855
856    /// Broadcasts a raw transaction RLP bytes to the network.
857    ///
858    /// See [`send_transaction`](Self::send_transaction) for more details.
859    async fn send_raw_transaction(
860        &self,
861        encoded_tx: &[u8],
862    ) -> TransportResult<PendingTransactionBuilder<N>> {
863        let rlp_hex = hex::encode_prefixed(encoded_tx);
864        let tx_hash = self.client().request("eth_sendRawTransaction", (rlp_hex,)).await?;
865        Ok(PendingTransactionBuilder::new(self.root().clone(), tx_hash))
866    }
867
868    /// Broadcasts a raw transaction RLP bytes to the network and returns the transaction receipt
869    /// after it has been mined.
870    ///
871    /// Unlike send_raw_transaction which returns immediately with
872    /// a transaction hash, this method waits on the server side until the transaction is included
873    /// in a block and returns the receipt directly. This is an optimization that reduces the number
874    /// of RPC calls needed to confirm a transaction.
875    ///
876    /// This method implements the `eth_sendRawTransactionSync` RPC method as defined in
877    /// [EIP-7966](https://eips.ethereum.org/EIPS/eip-7966).
878    ///
879    /// Note: This is only available on certain clients that support the
880    /// `eth_sendRawTransactionSync` RPC method, such as Anvil.
881    async fn send_raw_transaction_sync(
882        &self,
883        encoded_tx: &[u8],
884    ) -> TransportResult<N::ReceiptResponse> {
885        let rlp_hex = hex::encode_prefixed(encoded_tx);
886        self.client().request("eth_sendRawTransactionSync", (rlp_hex,)).await
887    }
888
889    /// Broadcasts a raw transaction RLP bytes with a conditional [`TransactionConditional`] to the
890    /// network.
891    ///
892    /// TransactionConditional represents the preconditions that determine the inclusion of the
893    /// transaction, enforced out-of-protocol by the sequencer.
894    ///
895    /// Note: This endpoint is only available on certain networks, e.g. opstack chains, polygon,
896    /// bsc.
897    ///
898    /// See [`TransactionConditional`] for more details.
899    async fn send_raw_transaction_conditional(
900        &self,
901        encoded_tx: &[u8],
902        conditional: TransactionConditional,
903    ) -> TransportResult<PendingTransactionBuilder<N>> {
904        let rlp_hex = hex::encode_prefixed(encoded_tx);
905        let tx_hash = self
906            .client()
907            .request("eth_sendRawTransactionConditional", (rlp_hex, conditional))
908            .await?;
909        Ok(PendingTransactionBuilder::new(self.root().clone(), tx_hash))
910    }
911
912    /// Broadcasts a transaction to the network.
913    ///
914    /// Returns a [`PendingTransactionBuilder`] which can be used to configure
915    /// how and when to await the transaction's confirmation.
916    ///
917    /// # Examples
918    ///
919    /// See [`PendingTransactionBuilder`](crate::PendingTransactionBuilder) for more examples.
920    ///
921    /// ```no_run
922    /// # async fn example<N: alloy_network::Network>(provider: impl alloy_provider::Provider, tx: alloy_rpc_types_eth::transaction::TransactionRequest) -> Result<(), Box<dyn std::error::Error>> {
923    /// let tx_hash = provider.send_transaction(tx)
924    ///     .await?
925    ///     .with_required_confirmations(2)
926    ///     .with_timeout(Some(std::time::Duration::from_secs(60)))
927    ///     .watch()
928    ///     .await?;
929    /// # Ok(())
930    /// # }
931    /// ```
932    async fn send_transaction(
933        &self,
934        tx: N::TransactionRequest,
935    ) -> TransportResult<PendingTransactionBuilder<N>> {
936        self.send_transaction_internal(SendableTx::Builder(tx)).await
937    }
938
939    /// Broadcasts a transaction envelope to the network.
940    ///
941    /// Returns a [`PendingTransactionBuilder`] which can be used to configure
942    /// how and when to await the transaction's confirmation.
943    async fn send_tx_envelope(
944        &self,
945        tx: N::TxEnvelope,
946    ) -> TransportResult<PendingTransactionBuilder<N>> {
947        self.send_transaction_internal(SendableTx::Envelope(tx)).await
948    }
949
950    /// This method allows [`ProviderLayer`] and [`TxFiller`] to build the
951    /// transaction and send it to the network without changing user-facing
952    /// APIs. Generally implementers should NOT override this method.
953    ///
954    /// [`send_transaction`]: Self::send_transaction
955    /// [`ProviderLayer`]: crate::ProviderLayer
956    /// [`TxFiller`]: crate::fillers::TxFiller
957    #[doc(hidden)]
958    async fn send_transaction_internal(
959        &self,
960        tx: SendableTx<N>,
961    ) -> TransportResult<PendingTransactionBuilder<N>> {
962        // Make sure to initialize heartbeat before we submit transaction, so that
963        // we don't miss it if user will subscriber to it immediately after sending.
964        let _handle = self.root().get_heart();
965
966        match tx {
967            SendableTx::Builder(mut tx) => {
968                alloy_network::TransactionBuilder::prep_for_submission(&mut tx);
969                let tx_hash = self.client().request("eth_sendTransaction", (tx,)).await?;
970                Ok(PendingTransactionBuilder::new(self.root().clone(), tx_hash))
971            }
972            SendableTx::Envelope(tx) => {
973                let encoded_tx = tx.encoded_2718();
974                self.send_raw_transaction(&encoded_tx).await
975            }
976        }
977    }
978
979    /// Sends a transaction and waits for its receipt in a single call.
980    ///
981    /// This method combines transaction submission and receipt retrieval into a single
982    /// async operation, providing a simpler API compared to the two-step process of
983    /// [`send_transaction`](Self::send_transaction) followed by waiting for confirmation.
984    ///
985    /// Returns the transaction receipt directly after submission and confirmation.
986    ///
987    /// # Example
988    /// ```no_run
989    /// # use alloy_network_primitives::ReceiptResponse;
990    /// # async fn example<N: alloy_network::Network>(provider: impl alloy_provider::Provider<N>, tx: N::TransactionRequest) -> Result<(), Box<dyn std::error::Error>> {
991    /// let receipt = provider.send_transaction_sync(tx).await?;
992    /// println!("Transaction hash: {}", receipt.transaction_hash());
993    /// # Ok(())
994    /// # }
995    /// ```
996    async fn send_transaction_sync(
997        &self,
998        tx: N::TransactionRequest,
999    ) -> TransportResult<N::ReceiptResponse> {
1000        self.send_transaction_sync_internal(SendableTx::Builder(tx)).await
1001    }
1002
1003    /// This method allows [`ProviderLayer`] and [`TxFiller`] to build the
1004    /// transaction and send it to the network without changing user-facing
1005    /// APIs. Generally implementers should NOT override this method.
1006    ///
1007    /// If the input is a [`SendableTx::Builder`] then this utilizes `eth_sendTransactionSync` by
1008    /// default.
1009    ///
1010    /// [`send_transaction`]: Self::send_transaction
1011    /// [`ProviderLayer`]: crate::ProviderLayer
1012    /// [`TxFiller`]: crate::fillers::TxFiller
1013    #[doc(hidden)]
1014    async fn send_transaction_sync_internal(
1015        &self,
1016        tx: SendableTx<N>,
1017    ) -> TransportResult<N::ReceiptResponse> {
1018        // Make sure to initialize heartbeat before we submit transaction, so that
1019        // we don't miss it if user will subscriber to it immediately after sending.
1020        let _handle = self.root().get_heart();
1021
1022        match tx {
1023            SendableTx::Builder(mut tx) => {
1024                alloy_network::TransactionBuilder::prep_for_submission(&mut tx);
1025                let receipt = self.client().request("eth_sendTransactionSync", (tx,)).await?;
1026                Ok(receipt)
1027            }
1028            SendableTx::Envelope(tx) => {
1029                let encoded_tx = tx.encoded_2718();
1030                self.send_raw_transaction_sync(&encoded_tx).await
1031            }
1032        }
1033    }
1034
1035    /// Signs a transaction that can be submitted to the network later using
1036    /// [`Provider::send_raw_transaction`].
1037    ///
1038    /// The `"eth_signTransaction"` method is not supported by regular nodes.
1039    async fn sign_transaction(&self, tx: N::TransactionRequest) -> TransportResult<Bytes> {
1040        self.client().request("eth_signTransaction", (tx,)).await
1041    }
1042
1043    /// Subscribe to a stream of new block headers.
1044    ///
1045    /// # Errors
1046    ///
1047    /// This method is only available on `pubsub` clients, such as WebSockets or IPC, and will
1048    /// return a [`PubsubUnavailable`](alloy_transport::TransportErrorKind::PubsubUnavailable)
1049    /// transport error if the client does not support it.
1050    ///
1051    /// For a polling alternative available over HTTP, use [`Provider::watch_blocks`].
1052    /// However, be aware that polling increases RPC usage drastically.
1053    ///
1054    /// # Examples
1055    ///
1056    /// ```no_run
1057    /// # async fn example(provider: impl alloy_provider::Provider) -> Result<(), Box<dyn std::error::Error>> {
1058    /// use futures::StreamExt;
1059    ///
1060    /// let sub = provider.subscribe_blocks().await?;
1061    /// let mut stream = sub.into_stream().take(5);
1062    /// while let Some(block) = stream.next().await {
1063    ///    println!("new block: {block:#?}");
1064    /// }
1065    /// # Ok(())
1066    /// # }
1067    /// ```
1068    #[cfg(feature = "pubsub")]
1069    fn subscribe_blocks(&self) -> GetSubscription<(SubscriptionKind,), N::HeaderResponse> {
1070        let rpc_call = self.client().request("eth_subscribe", (SubscriptionKind::NewHeads,));
1071        GetSubscription::new(self.weak_client(), rpc_call)
1072    }
1073
1074    /// Subscribe to a stream of full block bodies.
1075    ///
1076    /// # Errors
1077    ///
1078    /// This method is only available on `pubsub` clients, such as WebSockets or IPC, and will
1079    /// return a [`PubsubUnavailable`](alloy_transport::TransportErrorKind::PubsubUnavailable)
1080    /// transport error if the client does not support it.
1081    ///
1082    /// # Examples
1083    ///
1084    /// ```no_run
1085    /// # async fn example(provider: impl alloy_provider::Provider) -> Result<(), Box<dyn std::error::Error>> {
1086    /// use futures::StreamExt;
1087    ///
1088    /// let sub = provider.subscribe_full_blocks().full().channel_size(10);
1089    /// let mut stream = sub.into_stream().await?.take(5);
1090    ///
1091    /// while let Some(block) = stream.next().await {
1092    ///   println!("{block:#?}");
1093    /// }
1094    /// # Ok(())
1095    /// # }
1096    /// ```
1097    #[cfg(feature = "pubsub")]
1098    fn subscribe_full_blocks(&self) -> SubFullBlocks<N> {
1099        SubFullBlocks::new(self.subscribe_blocks(), self.weak_client())
1100    }
1101
1102    /// Subscribe to a stream of pending transaction hashes.
1103    ///
1104    /// # Errors
1105    ///
1106    /// This method is only available on `pubsub` clients, such as WebSockets or IPC, and will
1107    /// return a [`PubsubUnavailable`](alloy_transport::TransportErrorKind::PubsubUnavailable)
1108    /// transport error if the client does not support it.
1109    ///
1110    /// For a polling alternative available over HTTP, use [`Provider::watch_pending_transactions`].
1111    /// However, be aware that polling increases RPC usage drastically.
1112    ///
1113    /// # Examples
1114    ///
1115    /// ```no_run
1116    /// # async fn example(provider: impl alloy_provider::Provider) -> Result<(), Box<dyn std::error::Error>> {
1117    /// use futures::StreamExt;
1118    ///
1119    /// let sub = provider.subscribe_pending_transactions().await?;
1120    /// let mut stream = sub.into_stream().take(5);
1121    /// while let Some(tx_hash) = stream.next().await {
1122    ///    println!("new pending transaction hash: {tx_hash}");
1123    /// }
1124    /// # Ok(())
1125    /// # }
1126    /// ```
1127    #[cfg(feature = "pubsub")]
1128    fn subscribe_pending_transactions(&self) -> GetSubscription<(SubscriptionKind,), B256> {
1129        let rpc_call =
1130            self.client().request("eth_subscribe", (SubscriptionKind::NewPendingTransactions,));
1131        GetSubscription::new(self.weak_client(), rpc_call)
1132    }
1133
1134    /// Subscribe to a stream of pending transaction bodies.
1135    ///
1136    /// # Support
1137    ///
1138    /// This endpoint is compatible only with Geth client version 1.11.0 or later.
1139    ///
1140    /// # Errors
1141    ///
1142    /// This method is only available on `pubsub` clients, such as WebSockets or IPC, and will
1143    /// return a [`PubsubUnavailable`](alloy_transport::TransportErrorKind::PubsubUnavailable)
1144    /// transport error if the client does not support it.
1145    ///
1146    /// For a polling alternative available over HTTP, use
1147    /// [`Provider::watch_full_pending_transactions`]. However, be aware that polling increases
1148    /// RPC usage drastically.
1149    ///
1150    /// # Examples
1151    ///
1152    /// ```no_run
1153    /// # async fn example(provider: impl alloy_provider::Provider) -> Result<(), Box<dyn std::error::Error>> {
1154    /// use futures::StreamExt;
1155    ///
1156    /// let sub = provider.subscribe_full_pending_transactions().await?;
1157    /// let mut stream = sub.into_stream().take(5);
1158    /// while let Some(tx) = stream.next().await {
1159    ///    println!("{tx:#?}");
1160    /// }
1161    /// # Ok(())
1162    /// # }
1163    /// ```
1164    #[cfg(feature = "pubsub")]
1165    fn subscribe_full_pending_transactions(
1166        &self,
1167    ) -> GetSubscription<(SubscriptionKind, Params), N::TransactionResponse> {
1168        let rpc_call = self.client().request(
1169            "eth_subscribe",
1170            (SubscriptionKind::NewPendingTransactions, Params::Bool(true)),
1171        );
1172        GetSubscription::new(self.weak_client(), rpc_call)
1173    }
1174
1175    /// Subscribe to a stream of logs matching given filter.
1176    ///
1177    /// # Errors
1178    ///
1179    /// This method is only available on `pubsub` clients, such as WebSockets or IPC, and will
1180    /// return a [`PubsubUnavailable`](alloy_transport::TransportErrorKind::PubsubUnavailable)
1181    /// transport error if the client does not support it.
1182    ///
1183    /// For a polling alternative available over HTTP, use
1184    /// [`Provider::watch_logs`]. However, be aware that polling increases
1185    /// RPC usage drastically.
1186    ///
1187    /// # Examples
1188    ///
1189    /// ```no_run
1190    /// # async fn example(provider: impl alloy_provider::Provider) -> Result<(), Box<dyn std::error::Error>> {
1191    /// use futures::StreamExt;
1192    /// use alloy_primitives::keccak256;
1193    /// use alloy_rpc_types_eth::Filter;
1194    ///
1195    /// let signature = keccak256("Transfer(address,address,uint256)".as_bytes());
1196    ///
1197    /// let sub = provider.subscribe_logs(&Filter::new().event_signature(signature)).await?;
1198    /// let mut stream = sub.into_stream().take(5);
1199    /// while let Some(tx) = stream.next().await {
1200    ///    println!("{tx:#?}");
1201    /// }
1202    /// # Ok(())
1203    /// # }
1204    /// ```
1205    #[cfg(feature = "pubsub")]
1206    fn subscribe_logs(&self, filter: &Filter) -> GetSubscription<(SubscriptionKind, Params), Log> {
1207        let rpc_call = self.client().request(
1208            "eth_subscribe",
1209            (SubscriptionKind::Logs, Params::Logs(Box::new(filter.clone()))),
1210        );
1211        GetSubscription::new(self.weak_client(), rpc_call)
1212    }
1213
1214    /// Subscribe to an RPC event.
1215    #[cfg(feature = "pubsub")]
1216    #[auto_impl(keep_default_for(&, &mut, Rc, Arc, Box))]
1217    fn subscribe<P, R>(&self, params: P) -> GetSubscription<P, R>
1218    where
1219        P: RpcSend,
1220        R: RpcRecv,
1221        Self: Sized,
1222    {
1223        let rpc_call = self.client().request("eth_subscribe", params);
1224        GetSubscription::new(self.weak_client(), rpc_call)
1225    }
1226
1227    /// Subscribe to a non-standard subscription method without parameters.
1228    ///
1229    /// This is a helper method for creating subscriptions to methods that are not
1230    /// "eth_subscribe" and don't require parameters. It automatically marks the
1231    /// request as a subscription.
1232    ///
1233    /// # Examples
1234    ///
1235    /// ```no_run
1236    /// # async fn example(provider: impl alloy_provider::Provider) -> Result<(), Box<dyn std::error::Error>> {
1237    /// use futures::StreamExt;
1238    ///
1239    /// let sub = provider.subscribe_to::<alloy_rpc_types_admin::PeerEvent>("admin_peerEvents").await?;
1240    /// let mut stream = sub.into_stream().take(5);
1241    /// while let Some(event) = stream.next().await {
1242    ///    println!("peer event: {event:#?}");
1243    /// }
1244    /// # Ok(())
1245    /// # }
1246    /// ```
1247    #[cfg(feature = "pubsub")]
1248    #[auto_impl(keep_default_for(&, &mut, Rc, Arc, Box))]
1249    fn subscribe_to<R>(&self, method: &'static str) -> GetSubscription<NoParams, R>
1250    where
1251        R: RpcRecv,
1252        Self: Sized,
1253    {
1254        let mut rpc_call = self.client().request_noparams(method);
1255        rpc_call.set_is_subscription();
1256        GetSubscription::new(self.weak_client(), rpc_call)
1257    }
1258
1259    /// Cancels a subscription given the subscription ID.
1260    #[cfg(feature = "pubsub")]
1261    async fn unsubscribe(&self, id: B256) -> TransportResult<()> {
1262        self.root().unsubscribe(id)
1263    }
1264
1265    /// Gets syncing info.
1266    fn syncing(&self) -> ProviderCall<NoParams, SyncStatus> {
1267        self.client().request_noparams("eth_syncing").into()
1268    }
1269
1270    /// Gets the client version.
1271    #[doc(alias = "web3_client_version")]
1272    fn get_client_version(&self) -> ProviderCall<NoParams, String> {
1273        self.client().request_noparams("web3_clientVersion").into()
1274    }
1275
1276    /// Gets the `Keccak-256` hash of the given data.
1277    #[doc(alias = "web3_sha3")]
1278    fn get_sha3(&self, data: &[u8]) -> ProviderCall<(String,), B256> {
1279        self.client().request("web3_sha3", (hex::encode_prefixed(data),)).into()
1280    }
1281
1282    /// Gets the network ID. Same as `eth_chainId`.
1283    fn get_net_version(&self) -> ProviderCall<NoParams, U64, u64> {
1284        self.client()
1285            .request_noparams("net_version")
1286            .map_resp(utils::convert_u64 as fn(U64) -> u64)
1287            .into()
1288    }
1289
1290    /* ---------------------------------------- raw calls --------------------------------------- */
1291
1292    /// Sends a raw JSON-RPC request.
1293    ///
1294    /// # Examples
1295    ///
1296    /// ```no_run
1297    /// # async fn example(provider: impl alloy_provider::Provider) -> Result<(), Box<dyn std::error::Error>> {
1298    /// use alloy_rpc_types_eth::BlockNumberOrTag;
1299    /// use alloy_rpc_client::NoParams;
1300    ///
1301    /// // No parameters: `()`
1302    /// let block_number = provider.raw_request("eth_blockNumber".into(), NoParams::default()).await?;
1303    ///
1304    /// // One parameter: `(param,)` or `[param]`
1305    /// let block = provider.raw_request("eth_getBlockByNumber".into(), (BlockNumberOrTag::Latest,)).await?;
1306    ///
1307    /// // Two or more parameters: `(param1, param2, ...)` or `[param1, param2, ...]`
1308    /// let full_block = provider.raw_request("eth_getBlockByNumber".into(), (BlockNumberOrTag::Latest, true)).await?;
1309    /// # Ok(())
1310    /// # }
1311    /// ```
1312    ///
1313    /// [`PubsubUnavailable`]: alloy_transport::TransportErrorKind::PubsubUnavailable
1314    async fn raw_request<P, R>(&self, method: Cow<'static, str>, params: P) -> TransportResult<R>
1315    where
1316        P: RpcSend,
1317        R: RpcRecv,
1318        Self: Sized,
1319    {
1320        self.client().request(method, &params).await
1321    }
1322
1323    /// Sends a raw JSON-RPC request with type-erased parameters and return.
1324    ///
1325    /// # Examples
1326    ///
1327    /// ```no_run
1328    /// # async fn example(provider: impl alloy_provider::Provider) -> Result<(), Box<dyn std::error::Error>> {
1329    /// use alloy_rpc_types_eth::BlockNumberOrTag;
1330    ///
1331    /// // No parameters: `()`
1332    /// let params = serde_json::value::to_raw_value(&())?;
1333    /// let block_number = provider.raw_request_dyn("eth_blockNumber".into(), &params).await?;
1334    ///
1335    /// // One parameter: `(param,)` or `[param]`
1336    /// let params = serde_json::value::to_raw_value(&(BlockNumberOrTag::Latest,))?;
1337    /// let block = provider.raw_request_dyn("eth_getBlockByNumber".into(), &params).await?;
1338    ///
1339    /// // Two or more parameters: `(param1, param2, ...)` or `[param1, param2, ...]`
1340    /// let params = serde_json::value::to_raw_value(&(BlockNumberOrTag::Latest, true))?;
1341    /// let full_block = provider.raw_request_dyn("eth_getBlockByNumber".into(), &params).await?;
1342    /// # Ok(())
1343    /// # }
1344    /// ```
1345    async fn raw_request_dyn(
1346        &self,
1347        method: Cow<'static, str>,
1348        params: &RawValue,
1349    ) -> TransportResult<Box<RawValue>> {
1350        self.client().request(method, params).await
1351    }
1352
1353    /// Creates a new [`TransactionRequest`](alloy_network::Network).
1354    #[inline]
1355    fn transaction_request(&self) -> N::TransactionRequest {
1356        Default::default()
1357    }
1358}
1359
1360#[cfg_attr(target_family = "wasm", async_trait::async_trait(?Send))]
1361#[cfg_attr(not(target_family = "wasm"), async_trait::async_trait)]
1362impl<N: Network> Provider<N> for RootProvider<N> {
1363    #[inline]
1364    fn root(&self) -> &Self {
1365        self
1366    }
1367
1368    #[inline]
1369    fn client(&self) -> ClientRef<'_> {
1370        self.inner.client_ref()
1371    }
1372
1373    #[inline]
1374    fn weak_client(&self) -> WeakClient {
1375        self.inner.weak_client()
1376    }
1377
1378    #[inline]
1379    async fn watch_pending_transaction(
1380        &self,
1381        config: PendingTransactionConfig,
1382    ) -> Result<PendingTransaction, PendingTransactionError> {
1383        let block_number =
1384            if let Some(receipt) = self.get_transaction_receipt(*config.tx_hash()).await? {
1385                // The transaction is already confirmed.
1386                if config.required_confirmations() <= 1 {
1387                    return Ok(PendingTransaction::ready(*config.tx_hash()));
1388                }
1389                // Transaction has custom confirmations, so let the heart know about its block
1390                // number and let it handle the situation.
1391                receipt.block_number()
1392            } else {
1393                None
1394            };
1395
1396        self.get_heart()
1397            .watch_tx(config, block_number)
1398            .await
1399            .map_err(|_| PendingTransactionError::FailedToRegister)
1400    }
1401}
1402
1403#[cfg(test)]
1404mod tests {
1405    use super::*;
1406    use crate::{builder, ext::test::async_ci_only, ProviderBuilder, WalletProvider};
1407    use alloy_consensus::{Transaction, TxEnvelope};
1408    use alloy_network::{AnyNetwork, EthereumWallet, TransactionBuilder};
1409    use alloy_node_bindings::{utils::run_with_tempdir, Anvil, Reth};
1410    use alloy_primitives::{address, b256, bytes, keccak256};
1411    use alloy_rlp::Decodable;
1412    use alloy_rpc_client::{BuiltInConnectionString, RpcClient};
1413    use alloy_rpc_types_eth::{request::TransactionRequest, Block};
1414    use alloy_signer_local::PrivateKeySigner;
1415    use alloy_transport::layers::{RetryBackoffLayer, RetryPolicy};
1416    use std::{io::Read, str::FromStr, time::Duration};
1417
1418    // For layer transport tests
1419    use alloy_consensus::transaction::SignerRecoverable;
1420    #[cfg(feature = "hyper")]
1421    use alloy_transport_http::{
1422        hyper,
1423        hyper::body::Bytes as HyperBytes,
1424        hyper_util::{
1425            client::legacy::{Client, Error},
1426            rt::TokioExecutor,
1427        },
1428        HyperResponse, HyperResponseFut,
1429    };
1430    #[cfg(feature = "hyper")]
1431    use http_body_util::Full;
1432    #[cfg(feature = "hyper")]
1433    use tower::{Layer, Service};
1434
1435    #[tokio::test]
1436    async fn test_provider_builder() {
1437        let provider =
1438            RootProvider::<Ethereum>::builder().with_recommended_fillers().connect_anvil();
1439        let num = provider.get_block_number().await.unwrap();
1440        assert_eq!(0, num);
1441    }
1442
1443    #[tokio::test]
1444    async fn test_builder_helper_fn() {
1445        let provider = builder::<Ethereum>().with_recommended_fillers().connect_anvil();
1446        let num = provider.get_block_number().await.unwrap();
1447        assert_eq!(0, num);
1448    }
1449
1450    #[cfg(feature = "hyper")]
1451    #[tokio::test]
1452    async fn test_default_hyper_transport() {
1453        let anvil = Anvil::new().spawn();
1454        let hyper_t = alloy_transport_http::HyperTransport::new_hyper(anvil.endpoint_url());
1455
1456        let rpc_client = alloy_rpc_client::RpcClient::new(hyper_t, true);
1457
1458        let provider = RootProvider::<Ethereum>::new(rpc_client);
1459        let num = provider.get_block_number().await.unwrap();
1460        assert_eq!(0, num);
1461    }
1462
1463    #[cfg(feature = "hyper")]
1464    #[tokio::test]
1465    async fn test_hyper_layer_transport() {
1466        struct LoggingLayer;
1467
1468        impl<S> Layer<S> for LoggingLayer {
1469            type Service = LoggingService<S>;
1470
1471            fn layer(&self, inner: S) -> Self::Service {
1472                LoggingService { inner }
1473            }
1474        }
1475
1476        #[derive(Clone)] // required
1477        struct LoggingService<S> {
1478            inner: S,
1479        }
1480
1481        impl<S, B> Service<hyper::Request<B>> for LoggingService<S>
1482        where
1483            S: Service<hyper::Request<B>, Response = HyperResponse, Error = Error>
1484                + Clone
1485                + Send
1486                + Sync
1487                + 'static,
1488            S::Future: Send,
1489            S::Error: std::error::Error + Send + Sync + 'static,
1490            B: From<Vec<u8>> + Send + 'static + Clone + Sync + std::fmt::Debug,
1491        {
1492            type Response = HyperResponse;
1493            type Error = Error;
1494            type Future = HyperResponseFut;
1495
1496            fn poll_ready(
1497                &mut self,
1498                cx: &mut std::task::Context<'_>,
1499            ) -> std::task::Poll<Result<(), Self::Error>> {
1500                self.inner.poll_ready(cx)
1501            }
1502
1503            fn call(&mut self, req: hyper::Request<B>) -> Self::Future {
1504                println!("Logging Layer - HyperRequest {req:?}");
1505
1506                let fut = self.inner.call(req);
1507
1508                Box::pin(fut)
1509            }
1510        }
1511        use http::header::{self, HeaderValue};
1512        use tower_http::{
1513            sensitive_headers::SetSensitiveRequestHeadersLayer, set_header::SetRequestHeaderLayer,
1514        };
1515        let anvil = Anvil::new().spawn();
1516        let hyper_client = Client::builder(TokioExecutor::new()).build_http::<Full<HyperBytes>>();
1517
1518        // Setup tower service with multiple layers modifying request headers
1519        let service = tower::ServiceBuilder::new()
1520            .layer(SetRequestHeaderLayer::if_not_present(
1521                header::USER_AGENT,
1522                HeaderValue::from_static("alloy app"),
1523            ))
1524            .layer(SetRequestHeaderLayer::overriding(
1525                header::AUTHORIZATION,
1526                HeaderValue::from_static("some-jwt-token"),
1527            ))
1528            .layer(SetRequestHeaderLayer::appending(
1529                header::SET_COOKIE,
1530                HeaderValue::from_static("cookie-value"),
1531            ))
1532            .layer(SetSensitiveRequestHeadersLayer::new([header::AUTHORIZATION])) // Hides the jwt token as sensitive.
1533            .layer(LoggingLayer)
1534            .service(hyper_client);
1535
1536        let layer_transport = alloy_transport_http::HyperClient::with_service(service);
1537
1538        let http_hyper =
1539            alloy_transport_http::Http::with_client(layer_transport, anvil.endpoint_url());
1540
1541        let rpc_client = alloy_rpc_client::RpcClient::new(http_hyper, true);
1542
1543        let provider = RootProvider::<Ethereum>::new(rpc_client);
1544        let num = provider.get_block_number().await.unwrap();
1545        assert_eq!(0, num);
1546
1547        // Test Cloning with service
1548        let cloned_t = provider.client().transport().clone();
1549
1550        let rpc_client = alloy_rpc_client::RpcClient::new(cloned_t, true);
1551
1552        let provider = RootProvider::<Ethereum>::new(rpc_client);
1553        let num = provider.get_block_number().await.unwrap();
1554        assert_eq!(0, num);
1555    }
1556
1557    #[cfg(feature = "hyper")]
1558    #[tokio::test]
1559    #[cfg_attr(windows, ignore = "no reth on windows")]
1560    async fn test_auth_layer_transport() {
1561        crate::ext::test::async_ci_only(|| async move {
1562            use alloy_node_bindings::Reth;
1563            use alloy_rpc_types_engine::JwtSecret;
1564            use alloy_transport_http::{AuthLayer, Http, HyperClient};
1565
1566            let secret = JwtSecret::random();
1567
1568            let reth =
1569                Reth::new().arg("--rpc.jwtsecret").arg(hex::encode(secret.as_bytes())).spawn();
1570
1571            let layer_transport = HyperClient::new().layer(AuthLayer::new(secret));
1572
1573            let http_hyper = Http::with_client(layer_transport, reth.endpoint_url());
1574
1575            let rpc_client = alloy_rpc_client::RpcClient::new(http_hyper, true);
1576
1577            let provider = RootProvider::<Ethereum>::new(rpc_client);
1578
1579            let num = provider.get_block_number().await.unwrap();
1580            assert_eq!(0, num);
1581        })
1582        .await;
1583    }
1584
1585    #[tokio::test]
1586    async fn test_builder_helper_fn_any_network() {
1587        let anvil = Anvil::new().spawn();
1588        let provider =
1589            builder::<AnyNetwork>().with_recommended_fillers().connect_http(anvil.endpoint_url());
1590        let num = provider.get_block_number().await.unwrap();
1591        assert_eq!(0, num);
1592    }
1593
1594    #[cfg(feature = "reqwest")]
1595    #[tokio::test]
1596    async fn object_safety() {
1597        let provider = ProviderBuilder::new().connect_anvil();
1598
1599        let refdyn = &provider as &dyn Provider<_>;
1600        let num = refdyn.get_block_number().await.unwrap();
1601        assert_eq!(0, num);
1602    }
1603
1604    #[cfg(feature = "ws")]
1605    #[tokio::test]
1606    async fn subscribe_blocks_http() {
1607        let provider = ProviderBuilder::new().connect_anvil_with_config(|a| a.block_time(1));
1608
1609        let err = provider.subscribe_blocks().await.unwrap_err();
1610        let alloy_json_rpc::RpcError::Transport(
1611            alloy_transport::TransportErrorKind::PubsubUnavailable,
1612        ) = err
1613        else {
1614            panic!("{err:?}");
1615        };
1616    }
1617
1618    // Ensures we can connect to a websocket using `wss`.
1619    #[cfg(feature = "ws")]
1620    #[tokio::test]
1621    async fn websocket_tls_setup() {
1622        for url in ["wss://mainnet.infura.io/ws/v3/b0f825787ba840af81e46c6a64d20754"] {
1623            let _ = ProviderBuilder::<_, _, Ethereum>::default().connect(url).await.unwrap();
1624        }
1625    }
1626
1627    #[cfg(feature = "ws")]
1628    #[tokio::test]
1629    async fn subscribe_blocks_ws() {
1630        use futures::stream::StreamExt;
1631
1632        let anvil = Anvil::new().block_time_f64(0.2).spawn();
1633        let ws = alloy_rpc_client::WsConnect::new(anvil.ws_endpoint());
1634        let client = alloy_rpc_client::RpcClient::connect_pubsub(ws).await.unwrap();
1635        let provider = RootProvider::<Ethereum>::new(client);
1636
1637        let sub = provider.subscribe_blocks().await.unwrap();
1638        let mut stream = sub.into_stream().take(5);
1639        let mut next = None;
1640        while let Some(header) = stream.next().await {
1641            if let Some(next) = &mut next {
1642                assert_eq!(header.number, *next);
1643                *next += 1;
1644            } else {
1645                next = Some(header.number + 1);
1646            }
1647        }
1648    }
1649
1650    #[cfg(feature = "ws")]
1651    #[tokio::test]
1652    async fn subscribe_full_blocks() {
1653        use futures::StreamExt;
1654
1655        let anvil = Anvil::new().block_time_f64(0.2).spawn();
1656        let ws = alloy_rpc_client::WsConnect::new(anvil.ws_endpoint());
1657        let client = alloy_rpc_client::RpcClient::connect_pubsub(ws).await.unwrap();
1658
1659        let provider = RootProvider::<Ethereum>::new(client);
1660
1661        let sub = provider.subscribe_full_blocks().hashes().channel_size(10);
1662
1663        let mut stream = sub.into_stream().await.unwrap().take(5);
1664
1665        let mut next = None;
1666        while let Some(Ok(block)) = stream.next().await {
1667            if let Some(next) = &mut next {
1668                assert_eq!(block.header().number, *next);
1669                *next += 1;
1670            } else {
1671                next = Some(block.header().number + 1);
1672            }
1673        }
1674    }
1675
1676    #[tokio::test]
1677    #[cfg(feature = "ws")]
1678    async fn subscribe_blocks_ws_remote() {
1679        use futures::stream::StreamExt;
1680
1681        let url = "wss://eth-mainnet.g.alchemy.com/v2/viFmeVzhg6bWKVMIWWS8MhmzREB-D4f7";
1682        let ws = alloy_rpc_client::WsConnect::new(url);
1683        let Ok(client) = alloy_rpc_client::RpcClient::connect_pubsub(ws).await else { return };
1684        let provider = RootProvider::<Ethereum>::new(client);
1685        let sub = provider.subscribe_blocks().await.unwrap();
1686        let mut stream = sub.into_stream().take(1);
1687        while let Some(header) = stream.next().await {
1688            println!("New block {header:?}");
1689            assert!(header.number > 0);
1690        }
1691    }
1692
1693    #[tokio::test]
1694    async fn test_custom_retry_policy() {
1695        #[derive(Debug, Clone)]
1696        struct CustomPolicy;
1697        impl RetryPolicy for CustomPolicy {
1698            fn should_retry(&self, _err: &alloy_transport::TransportError) -> bool {
1699                true
1700            }
1701
1702            fn backoff_hint(
1703                &self,
1704                _error: &alloy_transport::TransportError,
1705            ) -> Option<std::time::Duration> {
1706                None
1707            }
1708        }
1709
1710        let retry_layer = RetryBackoffLayer::new_with_policy(10, 100, 10000, CustomPolicy);
1711        let anvil = Anvil::new().spawn();
1712        let client = RpcClient::builder().layer(retry_layer).http(anvil.endpoint_url());
1713
1714        let provider = RootProvider::<Ethereum>::new(client);
1715        let num = provider.get_block_number().await.unwrap();
1716        assert_eq!(0, num);
1717    }
1718
1719    #[tokio::test]
1720    async fn test_send_tx() {
1721        let provider = ProviderBuilder::new().connect_anvil_with_wallet();
1722        let tx = TransactionRequest {
1723            value: Some(U256::from(100)),
1724            to: Some(address!("d8dA6BF26964aF9D7eEd9e03E53415D37aA96045").into()),
1725            gas_price: Some(20e9 as u128),
1726            gas: Some(21000),
1727            ..Default::default()
1728        };
1729
1730        let builder = provider.send_transaction(tx.clone()).await.expect("failed to send tx");
1731        let hash1 = *builder.tx_hash();
1732        let hash2 = builder.watch().await.expect("failed to await pending tx");
1733        assert_eq!(hash1, hash2);
1734
1735        let builder = provider.send_transaction(tx).await.expect("failed to send tx");
1736        let hash1 = *builder.tx_hash();
1737        let hash2 =
1738            builder.get_receipt().await.expect("failed to await pending tx").transaction_hash;
1739        assert_eq!(hash1, hash2);
1740    }
1741
1742    #[tokio::test]
1743    async fn test_send_tx_sync() {
1744        let provider = ProviderBuilder::new().connect_anvil_with_wallet();
1745        let tx = TransactionRequest {
1746            value: Some(U256::from(100)),
1747            to: Some(address!("d8dA6BF26964aF9D7eEd9e03E53415D37aA96045").into()),
1748            gas_price: Some(20e9 as u128),
1749            gas: Some(21000),
1750            ..Default::default()
1751        };
1752
1753        let _receipt =
1754            provider.send_transaction_sync(tx.clone()).await.expect("failed to send tx sync");
1755    }
1756
1757    #[tokio::test]
1758    async fn test_send_raw_transaction_sync() {
1759        let provider = ProviderBuilder::new().connect_anvil_with_wallet();
1760
1761        // Create a transaction
1762        let tx = TransactionRequest {
1763            nonce: Some(0),
1764            value: Some(U256::from(100)),
1765            to: Some(address!("d8dA6BF26964aF9D7eEd9e03E53415D37aA96045").into()),
1766            gas_price: Some(20e9 as u128),
1767            gas: Some(21000),
1768            ..Default::default()
1769        };
1770
1771        // Build and sign the transaction to get the envelope
1772        let tx_envelope = tx.build(&provider.wallet()).await.expect("failed to build tx");
1773
1774        // Encode the transaction
1775        let encoded = tx_envelope.encoded_2718();
1776
1777        // Send using the sync method - this directly returns the receipt
1778        let receipt =
1779            provider.send_raw_transaction_sync(&encoded).await.expect("failed to send raw tx sync");
1780
1781        // Verify receipt
1782        assert_eq!(receipt.to(), Some(address!("d8dA6BF26964aF9D7eEd9e03E53415D37aA96045")));
1783        // The main idea that returned receipt should be already mined
1784        assert!(receipt.block_number().is_some(), "transaction should be mined");
1785        assert!(receipt.transaction_hash() != B256::ZERO, "should have valid tx hash");
1786    }
1787
1788    #[tokio::test]
1789    async fn test_watch_confirmed_tx() {
1790        let provider = ProviderBuilder::new().connect_anvil_with_wallet();
1791        let tx = TransactionRequest {
1792            value: Some(U256::from(100)),
1793            to: Some(address!("d8dA6BF26964aF9D7eEd9e03E53415D37aA96045").into()),
1794            gas_price: Some(20e9 as u128),
1795            gas: Some(21000),
1796            ..Default::default()
1797        };
1798
1799        let builder = provider.send_transaction(tx.clone()).await.expect("failed to send tx");
1800        let hash1 = *builder.tx_hash();
1801
1802        // Wait until tx is confirmed.
1803        loop {
1804            if provider
1805                .get_transaction_receipt(hash1)
1806                .await
1807                .expect("failed to await pending tx")
1808                .is_some()
1809            {
1810                break;
1811            }
1812        }
1813
1814        // Submit another tx.
1815        let tx2 = TransactionRequest {
1816            value: Some(U256::from(100)),
1817            to: Some(address!("d8dA6BF26964aF9D7eEd9e03E53415D37aA96045").into()),
1818            gas_price: Some(20e9 as u128),
1819            gas: Some(21000),
1820            ..Default::default()
1821        };
1822        provider.send_transaction(tx2).await.expect("failed to send tx").watch().await.unwrap();
1823
1824        // Only subscribe for watching _after_ tx was confirmed and we submitted a new one.
1825        let watch = builder.watch();
1826        // Wrap watch future in timeout to prevent it from hanging.
1827        let watch_with_timeout = tokio::time::timeout(Duration::from_secs(1), watch);
1828        let hash2 = watch_with_timeout
1829            .await
1830            .expect("Watching tx timed out")
1831            .expect("failed to await pending tx");
1832        assert_eq!(hash1, hash2);
1833    }
1834
1835    #[tokio::test]
1836    async fn gets_block_number() {
1837        let provider = ProviderBuilder::new().connect_anvil();
1838        let num = provider.get_block_number().await.unwrap();
1839        assert_eq!(0, num)
1840    }
1841
1842    #[tokio::test]
1843    async fn gets_block_number_for_id() {
1844        let provider = ProviderBuilder::new().connect_anvil();
1845
1846        let block_num = provider
1847            .get_block_number_by_id(BlockId::Number(BlockNumberOrTag::Number(0)))
1848            .await
1849            .unwrap();
1850        assert_eq!(block_num, Some(0));
1851
1852        let block_num = provider
1853            .get_block_number_by_id(BlockId::Number(BlockNumberOrTag::Latest))
1854            .await
1855            .unwrap();
1856        assert_eq!(block_num, Some(0));
1857
1858        let block =
1859            provider.get_block_by_number(BlockNumberOrTag::Number(0)).await.unwrap().unwrap();
1860        let hash = block.header.hash;
1861        let block_num = provider.get_block_number_by_id(BlockId::Hash(hash.into())).await.unwrap();
1862        assert_eq!(block_num, Some(0));
1863    }
1864
1865    #[tokio::test]
1866    async fn gets_block_number_with_raw_req() {
1867        let provider = ProviderBuilder::new().connect_anvil();
1868        let num: U64 =
1869            provider.raw_request("eth_blockNumber".into(), NoParams::default()).await.unwrap();
1870        assert_eq!(0, num.to::<u64>())
1871    }
1872
1873    #[cfg(feature = "anvil-api")]
1874    #[tokio::test]
1875    async fn gets_transaction_count() {
1876        let provider = ProviderBuilder::new().connect_anvil();
1877        let accounts = provider.get_accounts().await.unwrap();
1878        let sender = accounts[0];
1879
1880        // Initial tx count should be 0
1881        let count = provider.get_transaction_count(sender).await.unwrap();
1882        assert_eq!(count, 0);
1883
1884        // Send Tx
1885        let tx = TransactionRequest {
1886            value: Some(U256::from(100)),
1887            from: Some(sender),
1888            to: Some(address!("d8dA6BF26964aF9D7eEd9e03E53415D37aA96045").into()),
1889            gas_price: Some(20e9 as u128),
1890            gas: Some(21000),
1891            ..Default::default()
1892        };
1893        let _ = provider.send_transaction(tx).await.unwrap().get_receipt().await;
1894
1895        // Tx count should be 1
1896        let count = provider.get_transaction_count(sender).await.unwrap();
1897        assert_eq!(count, 1);
1898
1899        // Tx count should be 0 at block 0
1900        let count = provider.get_transaction_count(sender).block_id(0.into()).await.unwrap();
1901        assert_eq!(count, 0);
1902    }
1903
1904    #[tokio::test]
1905    async fn gets_block_by_hash() {
1906        let provider = ProviderBuilder::new().connect_anvil();
1907        let num = 0;
1908        let tag: BlockNumberOrTag = num.into();
1909        let block = provider.get_block_by_number(tag).full().await.unwrap().unwrap();
1910        let hash = block.header.hash;
1911        let block = provider.get_block_by_hash(hash).full().await.unwrap().unwrap();
1912        assert_eq!(block.header.hash, hash);
1913    }
1914
1915    #[tokio::test]
1916    async fn gets_block_by_hash_with_raw_req() {
1917        let provider = ProviderBuilder::new().connect_anvil();
1918        let num = 0;
1919        let tag: BlockNumberOrTag = num.into();
1920        let block = provider.get_block_by_number(tag).full().await.unwrap().unwrap();
1921        let hash = block.header.hash;
1922        let block: Block = provider
1923            .raw_request::<(B256, bool), Block>("eth_getBlockByHash".into(), (hash, true))
1924            .await
1925            .unwrap();
1926        assert_eq!(block.header.hash, hash);
1927    }
1928
1929    #[tokio::test]
1930    async fn gets_block_by_number_full() {
1931        let provider = ProviderBuilder::new().connect_anvil();
1932        let num = 0;
1933        let tag: BlockNumberOrTag = num.into();
1934        let block = provider.get_block_by_number(tag).full().await.unwrap().unwrap();
1935        assert_eq!(block.header.number, num);
1936    }
1937
1938    #[tokio::test]
1939    async fn gets_block_by_number() {
1940        let provider = ProviderBuilder::new().connect_anvil();
1941        let num = 0;
1942        let tag: BlockNumberOrTag = num.into();
1943        let block = provider.get_block_by_number(tag).full().await.unwrap().unwrap();
1944        assert_eq!(block.header.number, num);
1945    }
1946
1947    #[tokio::test]
1948    async fn gets_client_version() {
1949        let provider = ProviderBuilder::new().connect_anvil();
1950        let version = provider.get_client_version().await.unwrap();
1951        assert!(version.contains("anvil"), "{version}");
1952    }
1953
1954    #[tokio::test]
1955    async fn gets_sha3() {
1956        let provider = ProviderBuilder::new().connect_anvil();
1957        let data = b"alloy";
1958        let hash = provider.get_sha3(data).await.unwrap();
1959        assert_eq!(hash, keccak256(data));
1960    }
1961
1962    #[tokio::test]
1963    async fn gets_chain_id() {
1964        let dev_chain_id: u64 = 13371337;
1965
1966        let provider =
1967            ProviderBuilder::new().connect_anvil_with_config(|a| a.chain_id(dev_chain_id));
1968
1969        let chain_id = provider.get_chain_id().await.unwrap();
1970        assert_eq!(chain_id, dev_chain_id);
1971    }
1972
1973    #[tokio::test]
1974    async fn gets_network_id() {
1975        let dev_chain_id: u64 = 13371337;
1976        let provider =
1977            ProviderBuilder::new().connect_anvil_with_config(|a| a.chain_id(dev_chain_id));
1978
1979        let chain_id = provider.get_net_version().await.unwrap();
1980        assert_eq!(chain_id, dev_chain_id);
1981    }
1982
1983    #[tokio::test]
1984    async fn gets_storage_at() {
1985        let provider = ProviderBuilder::new().connect_anvil();
1986        let addr = Address::with_last_byte(16);
1987        let storage = provider.get_storage_at(addr, U256::ZERO).await.unwrap();
1988        assert_eq!(storage, U256::ZERO);
1989    }
1990
1991    #[tokio::test]
1992    async fn gets_transaction_by_hash_not_found() {
1993        let provider = ProviderBuilder::new().connect_anvil();
1994        let tx_hash = b256!("5c03fab9114ceb98994b43892ade87ddfd9ae7e8f293935c3bd29d435dc9fd95");
1995        let tx = provider.get_transaction_by_hash(tx_hash).await.expect("failed to fetch tx");
1996
1997        assert!(tx.is_none());
1998    }
1999
2000    #[tokio::test]
2001    async fn gets_transaction_by_hash() {
2002        let provider = ProviderBuilder::new().connect_anvil_with_wallet();
2003
2004        let req = TransactionRequest::default()
2005            .from(provider.default_signer_address())
2006            .to(Address::repeat_byte(5))
2007            .value(U256::ZERO)
2008            .input(bytes!("deadbeef").into());
2009
2010        let tx_hash = *provider.send_transaction(req).await.expect("failed to send tx").tx_hash();
2011
2012        let tx = provider
2013            .get_transaction_by_hash(tx_hash)
2014            .await
2015            .expect("failed to fetch tx")
2016            .expect("tx not included");
2017        assert_eq!(tx.input(), &bytes!("deadbeef"));
2018    }
2019
2020    #[tokio::test]
2021    #[ignore]
2022    async fn gets_logs() {
2023        let provider = ProviderBuilder::new().connect_anvil();
2024        let filter = Filter::new()
2025            .at_block_hash(b256!(
2026                "b20e6f35d4b46b3c4cd72152faec7143da851a0dc281d390bdd50f58bfbdb5d3"
2027            ))
2028            .event_signature(b256!(
2029                "e1fffcc4923d04b559f4d29a8bfc6cda04eb5b0d3c460751c2402c5c5cc9109c"
2030            ));
2031        let logs = provider.get_logs(&filter).await.unwrap();
2032        assert_eq!(logs.len(), 1);
2033    }
2034
2035    #[tokio::test]
2036    #[ignore]
2037    async fn gets_tx_receipt() {
2038        let provider = ProviderBuilder::new().connect_anvil();
2039        let receipt = provider
2040            .get_transaction_receipt(b256!(
2041                "5c03fab9114ceb98994b43892ade87ddfd9ae7e8f293935c3bd29d435dc9fd95"
2042            ))
2043            .await
2044            .unwrap();
2045        assert!(receipt.is_some());
2046        let receipt = receipt.unwrap();
2047        assert_eq!(
2048            receipt.transaction_hash,
2049            b256!("5c03fab9114ceb98994b43892ade87ddfd9ae7e8f293935c3bd29d435dc9fd95")
2050        );
2051    }
2052
2053    #[tokio::test]
2054    async fn gets_max_priority_fee_per_gas() {
2055        let provider = ProviderBuilder::new().connect_anvil();
2056        let _fee = provider.get_max_priority_fee_per_gas().await.unwrap();
2057    }
2058
2059    #[tokio::test]
2060    async fn gets_fee_history() {
2061        let provider = ProviderBuilder::new().connect_anvil();
2062        let block_number = provider.get_block_number().await.unwrap();
2063        let fee_history = provider
2064            .get_fee_history(
2065                utils::EIP1559_FEE_ESTIMATION_PAST_BLOCKS,
2066                BlockNumberOrTag::Number(block_number),
2067                &[utils::EIP1559_FEE_ESTIMATION_REWARD_PERCENTILE],
2068            )
2069            .await
2070            .unwrap();
2071        assert_eq!(fee_history.oldest_block, 0_u64);
2072    }
2073
2074    #[tokio::test]
2075    async fn gets_block_transaction_count_by_hash() {
2076        let provider = ProviderBuilder::new().connect_anvil();
2077        let block = provider.get_block(BlockId::latest()).await.unwrap().unwrap();
2078        let hash = block.header.hash;
2079        let tx_count = provider.get_block_transaction_count_by_hash(hash).await.unwrap();
2080        assert!(tx_count.is_some());
2081    }
2082
2083    #[tokio::test]
2084    async fn gets_block_transaction_count_by_number() {
2085        let provider = ProviderBuilder::new().connect_anvil();
2086        let tx_count =
2087            provider.get_block_transaction_count_by_number(BlockNumberOrTag::Latest).await.unwrap();
2088        assert!(tx_count.is_some());
2089    }
2090
2091    #[tokio::test]
2092    async fn gets_block_receipts() {
2093        let provider = ProviderBuilder::new().connect_anvil();
2094        let receipts =
2095            provider.get_block_receipts(BlockId::Number(BlockNumberOrTag::Latest)).await.unwrap();
2096        assert!(receipts.is_some());
2097    }
2098
2099    #[tokio::test]
2100    async fn sends_raw_transaction() {
2101        let provider = ProviderBuilder::new().connect_anvil();
2102        let pending = provider
2103            .send_raw_transaction(
2104                // Transfer 1 ETH from default EOA address to the Genesis address.
2105                bytes!("f865808477359400825208940000000000000000000000000000000000000000018082f4f5a00505e227c1c636c76fac55795db1a40a4d24840d81b40d2fe0cc85767f6bd202a01e91b437099a8a90234ac5af3cb7ca4fb1432e133f75f9a91678eaf5f487c74b").as_ref()
2106            )
2107            .await.unwrap();
2108        assert_eq!(
2109            pending.tx_hash().to_string(),
2110            "0x9dae5cf33694a02e8a7d5de3fe31e9d05ca0ba6e9180efac4ab20a06c9e598a3"
2111        );
2112    }
2113
2114    #[tokio::test]
2115    async fn connect_boxed() {
2116        let anvil = Anvil::new().spawn();
2117
2118        let provider = RootProvider::<Ethereum>::connect(anvil.endpoint().as_str()).await;
2119
2120        match provider {
2121            Ok(provider) => {
2122                let num = provider.get_block_number().await.unwrap();
2123                assert_eq!(0, num);
2124            }
2125            Err(e) => {
2126                assert_eq!(
2127                    format!("{e}"),
2128                    "hyper not supported by BuiltinConnectionString. Please instantiate a hyper client manually"
2129                );
2130            }
2131        }
2132    }
2133
2134    #[tokio::test]
2135    async fn any_network_wallet_filler() {
2136        use alloy_serde::WithOtherFields;
2137        let anvil = Anvil::new().spawn();
2138        let signer: PrivateKeySigner =
2139            "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80".parse().unwrap();
2140        let wallet = EthereumWallet::from(signer);
2141
2142        let provider = ProviderBuilder::new()
2143            .network::<AnyNetwork>()
2144            .wallet(wallet)
2145            .connect_http(anvil.endpoint_url());
2146
2147        let tx = TransactionRequest::default()
2148            .with_to(address!("c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2"))
2149            .value(U256::from(325235));
2150
2151        let tx = WithOtherFields::new(tx);
2152
2153        let builder = provider.send_transaction(tx).await.unwrap().get_receipt().await.unwrap();
2154
2155        assert!(builder.status());
2156    }
2157
2158    #[tokio::test]
2159    async fn builtin_connect_boxed() {
2160        let anvil = Anvil::new().spawn();
2161
2162        let conn: BuiltInConnectionString = anvil.endpoint().parse().unwrap();
2163
2164        let transport = conn.connect_boxed().await.unwrap();
2165
2166        let client = alloy_rpc_client::RpcClient::new(transport, true);
2167
2168        let provider = RootProvider::<Ethereum>::new(client);
2169
2170        let num = provider.get_block_number().await.unwrap();
2171        assert_eq!(0, num);
2172    }
2173
2174    #[tokio::test]
2175    async fn test_uncle_count() {
2176        let provider = ProviderBuilder::new().connect_anvil();
2177
2178        let count = provider.get_uncle_count(0.into()).await.unwrap();
2179        assert_eq!(count, 0);
2180    }
2181
2182    #[tokio::test]
2183    #[cfg(any(
2184        feature = "reqwest-default-tls",
2185        feature = "reqwest-rustls-tls",
2186        feature = "reqwest-native-tls",
2187    ))]
2188    #[ignore = "ignore until <https://github.com/paradigmxyz/reth/pull/14727> is in"]
2189    async fn call_mainnet() {
2190        use alloy_network::TransactionBuilder;
2191        use alloy_sol_types::SolValue;
2192
2193        let url = "https://docs-demo.quiknode.pro/";
2194        let provider = ProviderBuilder::new().connect_http(url.parse().unwrap());
2195        let req = TransactionRequest::default()
2196            .with_to(address!("c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2")) // WETH
2197            .with_input(bytes!("06fdde03")); // `name()`
2198        let result = provider.call(req.clone()).await.unwrap();
2199        assert_eq!(String::abi_decode(&result).unwrap(), "Wrapped Ether");
2200
2201        let result = provider.call(req).block(0.into()).await.unwrap();
2202        assert_eq!(result.to_string(), "0x");
2203    }
2204
2205    #[tokio::test]
2206    async fn call_many_mainnet() {
2207        use alloy_rpc_types_eth::{BlockOverrides, StateContext};
2208
2209        let url = "https://docs-demo.quiknode.pro/";
2210        let provider = ProviderBuilder::new().connect_http(url.parse().unwrap());
2211        let tx1 = TransactionRequest::default()
2212            .with_to(address!("6b175474e89094c44da98b954eedeac495271d0f"))
2213            .with_gas_limit(1000000)
2214            .with_gas_price(2023155498)
2215            .with_input(hex!("a9059cbb000000000000000000000000bc0E63965946815d105E7591407704e6e1964E590000000000000000000000000000000000000000000000000000000005f5e100"));
2216        let tx2 = TransactionRequest::default()
2217            .with_to(address!("833589fcd6edb6e08f4c7c32d4f71b54bda02913"))
2218            .with_gas_price(2023155498)
2219            .with_input(hex!(
2220                "70a08231000000000000000000000000bc0E63965946815d105E7591407704e6e1964E59"
2221            ));
2222
2223        let transactions = vec![tx1.clone(), tx2.clone()];
2224
2225        let block_override =
2226            BlockOverrides { number: Some(U256::from(12279785)), ..Default::default() };
2227
2228        let bundles = vec![Bundle { transactions, block_override: Some(block_override.clone()) }];
2229
2230        let context = StateContext {
2231            block_number: Some(BlockId::number(12279785)),
2232            transaction_index: Some(1.into()),
2233        };
2234
2235        let results = provider.call_many(&bundles).context(&context).await.unwrap();
2236
2237        let tx1_res = EthCallResponse {
2238            value: Some(
2239                hex!("0000000000000000000000000000000000000000000000000000000000000001").into(),
2240            ),
2241            error: None,
2242        };
2243        let tx2_res = EthCallResponse { value: Some(Bytes::new()), error: None };
2244        let expected = vec![vec![tx1_res.clone(), tx2_res.clone()]];
2245
2246        assert_eq!(results, expected);
2247
2248        // Two bundles
2249        let bundles = vec![
2250            Bundle {
2251                transactions: vec![tx1.clone()],
2252                block_override: Some(block_override.clone()),
2253            },
2254            Bundle {
2255                transactions: vec![tx2.clone()],
2256                block_override: Some(block_override.clone()),
2257            },
2258        ];
2259
2260        let results = provider.call_many(&bundles).context(&context).await.unwrap();
2261        let expected = vec![vec![tx1_res.clone()], vec![tx2_res.clone()]];
2262        assert_eq!(results, expected);
2263
2264        // Two bundles by extending existing.
2265        let b1 =
2266            vec![Bundle { transactions: vec![tx1], block_override: Some(block_override.clone()) }];
2267        let b2 = vec![Bundle { transactions: vec![tx2], block_override: Some(block_override) }];
2268
2269        let results = provider.call_many(&b1).context(&context).extend_bundles(&b2).await.unwrap();
2270        assert_eq!(results, expected);
2271    }
2272
2273    #[tokio::test]
2274    #[cfg(feature = "hyper-tls")]
2275    async fn hyper_https() {
2276        let url = "https://reth-ethereum.ithaca.xyz/rpc";
2277
2278        // With the `hyper` feature enabled .connect builds the provider based on
2279        // `HyperTransport`.
2280        let provider = ProviderBuilder::new().connect(url).await.unwrap();
2281
2282        let _num = provider.get_block_number().await.unwrap();
2283    }
2284
2285    #[tokio::test]
2286    async fn test_empty_transactions() {
2287        let provider = ProviderBuilder::new().connect_anvil();
2288
2289        let block = provider.get_block_by_number(0.into()).await.unwrap().unwrap();
2290        assert!(block.transactions.is_hashes());
2291    }
2292
2293    #[tokio::test]
2294    async fn disable_test() {
2295        let provider = ProviderBuilder::new()
2296            .disable_recommended_fillers()
2297            .with_cached_nonce_management()
2298            .connect_anvil();
2299
2300        let tx = TransactionRequest::default()
2301            .with_kind(alloy_primitives::TxKind::Create)
2302            .value(U256::from(1235))
2303            .with_input(Bytes::from_str("ffffffffffffff").unwrap());
2304
2305        let err = provider.send_transaction(tx).await.unwrap_err().to_string();
2306        assert!(err.contains("missing properties: [(\"NonceManager\", [\"from\"])]"));
2307    }
2308
2309    #[tokio::test]
2310    async fn capture_anvil_logs() {
2311        let mut anvil = Anvil::new().keep_stdout().spawn();
2312
2313        let provider = ProviderBuilder::new().connect_http(anvil.endpoint_url());
2314
2315        let tx = TransactionRequest::default()
2316            .with_from(address!("f39Fd6e51aad88F6F4ce6aB8827279cffFb92266"))
2317            .with_to(address!("70997970C51812dc3A010C7d01b50e0d17dc79C8"))
2318            .value(U256::from(100));
2319
2320        let _ = provider.send_transaction(tx).await.unwrap().get_receipt().await.unwrap();
2321
2322        anvil.child_mut().kill().unwrap();
2323
2324        let mut output = String::new();
2325        anvil.child_mut().stdout.take().unwrap().read_to_string(&mut output).unwrap();
2326
2327        assert_eq!(anvil.chain_id(), 31337);
2328        assert_eq!(anvil.addresses().len(), 10);
2329        assert_eq!(anvil.keys().len(), 10);
2330
2331        assert!(output.contains("eth_sendTransaction"));
2332        assert!(output.contains("Block Number: 1"))
2333    }
2334
2335    #[tokio::test]
2336    async fn custom_estimator() {
2337        let provider = ProviderBuilder::new()
2338            .disable_recommended_fillers()
2339            .with_cached_nonce_management()
2340            .connect_anvil();
2341
2342        let _ = provider
2343            .estimate_eip1559_fees_with(Eip1559Estimator::new(|_fee, _rewards| Eip1559Estimation {
2344                max_fee_per_gas: 0,
2345                max_priority_fee_per_gas: 0,
2346            }))
2347            .await;
2348    }
2349
2350    #[tokio::test]
2351    #[cfg(not(windows))]
2352    async fn eth_sign_transaction() {
2353        async_ci_only(|| async {
2354            run_with_tempdir("reth-sign-tx", |dir| async {
2355                let reth = Reth::new().dev().disable_discovery().data_dir(dir).spawn();
2356                let provider = ProviderBuilder::new().connect_http(reth.endpoint_url());
2357
2358                let accounts = provider.get_accounts().await.unwrap();
2359                let from = accounts[0];
2360
2361                let tx = TransactionRequest::default()
2362                    .from(from)
2363                    .to(Address::random())
2364                    .value(U256::from(100))
2365                    .gas_limit(21000);
2366
2367                let signed_tx = provider.sign_transaction(tx).await.unwrap().to_vec();
2368
2369                let tx = TxEnvelope::decode(&mut signed_tx.as_slice()).unwrap();
2370
2371                let signer = tx.recover_signer().unwrap();
2372
2373                assert_eq!(signer, from);
2374            })
2375            .await
2376        })
2377        .await;
2378    }
2379
2380    #[cfg(feature = "throttle")]
2381    use alloy_transport::layers::ThrottleLayer;
2382
2383    #[cfg(feature = "throttle")]
2384    #[tokio::test]
2385    async fn test_throttled_provider() {
2386        let request_per_second = 10;
2387        let throttle_layer = ThrottleLayer::new(request_per_second);
2388
2389        let anvil = Anvil::new().spawn();
2390        let client = RpcClient::builder().layer(throttle_layer).http(anvil.endpoint_url());
2391        let provider = RootProvider::<Ethereum>::new(client);
2392
2393        let num_requests = 10;
2394        let start = std::time::Instant::now();
2395        for _ in 0..num_requests {
2396            provider.get_block_number().await.unwrap();
2397        }
2398
2399        let elapsed = start.elapsed();
2400        assert_eq!(elapsed.as_secs_f64().round() as u32, 1);
2401    }
2402
2403    #[tokio::test]
2404    #[cfg(feature = "hyper")]
2405    async fn test_connect_hyper_tls() {
2406        let p =
2407            ProviderBuilder::new().connect("https://reth-ethereum.ithaca.xyz/rpc").await.unwrap();
2408
2409        let _num = p.get_block_number().await.unwrap();
2410
2411        let anvil = Anvil::new().spawn();
2412        let p = ProviderBuilder::new().connect(&anvil.endpoint()).await.unwrap();
2413
2414        let _num = p.get_block_number().await.unwrap();
2415    }
2416
2417    #[tokio::test]
2418    async fn test_send_transaction_sync() {
2419        use alloy_network::TransactionBuilder;
2420        use alloy_primitives::{address, U256};
2421
2422        let anvil = Anvil::new().spawn();
2423        let provider = ProviderBuilder::new().connect_http(anvil.endpoint_url());
2424
2425        let tx = TransactionRequest::default()
2426            .with_from(address!("f39Fd6e51aad88F6F4ce6aB8827279cffFb92266"))
2427            .with_to(address!("70997970C51812dc3A010C7d01b50e0d17dc79C8"))
2428            .with_value(U256::from(100));
2429
2430        // Test the sync transaction sending
2431        let receipt = provider.send_transaction_sync(tx).await.unwrap();
2432
2433        // Verify we can access transaction metadata from the receipt
2434        let tx_hash = receipt.transaction_hash;
2435        assert!(!tx_hash.is_zero());
2436        assert_eq!(receipt.transaction_hash, tx_hash);
2437        assert!(receipt.status());
2438    }
2439
2440    #[tokio::test]
2441    async fn test_send_transaction_sync_with_fillers() {
2442        use alloy_network::TransactionBuilder;
2443        use alloy_primitives::{address, U256};
2444
2445        let provider = ProviderBuilder::new().connect_anvil_with_wallet();
2446
2447        // Create transaction without specifying gas or nonce - fillers should handle this
2448        let tx = TransactionRequest::default()
2449            .with_from(provider.default_signer_address())
2450            .with_to(address!("70997970C51812dc3A010C7d01b50e0d17dc79C8"))
2451            .with_value(U256::from(100));
2452        // Note: No gas limit, gas price, or nonce specified - fillers will provide these
2453
2454        // Test that sync transactions work with filler pipeline
2455        let receipt = provider.send_transaction_sync(tx).await.unwrap();
2456
2457        // Verify immediate access works
2458        let tx_hash = receipt.transaction_hash;
2459        assert!(!tx_hash.is_zero());
2460
2461        // Verify receipt shows fillers worked (gas was estimated and used)
2462        assert_eq!(receipt.transaction_hash, tx_hash);
2463        assert!(receipt.status());
2464        assert!(receipt.gas_used() > 0, "fillers should have estimated gas");
2465    }
2466}