Skip to main content

alloy_provider/
builder.rs

1use crate::{
2    fillers::{
3        BlobGasEstimator, BlobGasFiller, CachedNonceManager, ChainIdFiller, FillerControlFlow,
4        GasFiller, JoinFill, NonceFiller, NonceManager, RecommendedFillers, SimpleNonceManager,
5        TxFiller, WalletFiller,
6    },
7    layers::{BlockIdLayer, CallBatchLayer, ChainLayer},
8    provider::SendableTx,
9    utils::Eip1559Estimator,
10    Provider, RootProvider,
11};
12use alloy_chains::NamedChain;
13use alloy_network::{Ethereum, IntoWallet, Network};
14use alloy_primitives::ChainId;
15use alloy_rpc_client::{ClientBuilder, ConnectionConfig, RpcClient};
16use alloy_transport::{TransportConnect, TransportError, TransportResult};
17use std::marker::PhantomData;
18
19/// A layering abstraction in the vein of [`tower::Layer`]
20///
21/// [`tower::Layer`]: https://docs.rs/tower/latest/tower/trait.Layer.html
22pub trait ProviderLayer<P: Provider<N>, N: Network = Ethereum> {
23    /// The provider constructed by this layer.
24    type Provider: Provider<N>;
25
26    /// Wrap the given provider in the layer's provider.
27    fn layer(&self, inner: P) -> Self::Provider;
28}
29
30/// An identity layer that does nothing.
31#[derive(Clone, Copy, Debug)]
32pub struct Identity;
33
34impl<N> TxFiller<N> for Identity
35where
36    N: Network,
37{
38    type Fillable = ();
39
40    fn status(&self, _tx: &<N as Network>::TransactionRequest) -> FillerControlFlow {
41        FillerControlFlow::Finished
42    }
43
44    fn fill_sync(&self, _tx: &mut SendableTx<N>) {}
45
46    async fn prepare<P>(
47        &self,
48        _provider: &P,
49        _tx: &N::TransactionRequest,
50    ) -> TransportResult<Self::Fillable> {
51        Ok(())
52    }
53
54    async fn fill(
55        &self,
56        _to_fill: Self::Fillable,
57        tx: SendableTx<N>,
58    ) -> TransportResult<SendableTx<N>> {
59        Ok(tx)
60    }
61}
62
63impl<P, N> ProviderLayer<P, N> for Identity
64where
65    N: Network,
66    P: Provider<N>,
67{
68    type Provider = P;
69
70    fn layer(&self, inner: P) -> Self::Provider {
71        inner
72    }
73}
74
75/// A stack of two providers.
76#[derive(Debug)]
77pub struct Stack<Inner, Outer> {
78    inner: Inner,
79    outer: Outer,
80}
81
82impl<Inner, Outer> Stack<Inner, Outer> {
83    /// Create a new `Stack`.
84    pub const fn new(inner: Inner, outer: Outer) -> Self {
85        Self { inner, outer }
86    }
87}
88
89impl<P, N, Inner, Outer> ProviderLayer<P, N> for Stack<Inner, Outer>
90where
91    N: Network,
92    P: Provider<N>,
93    Inner: ProviderLayer<P, N>,
94    Outer: ProviderLayer<Inner::Provider, N>,
95{
96    type Provider = Outer::Provider;
97
98    fn layer(&self, provider: P) -> Self::Provider {
99        let inner = self.inner.layer(provider);
100
101        self.outer.layer(inner)
102    }
103}
104
105/// A builder for constructing a [`Provider`] from various layers.
106///
107/// This type is similar to [`tower::ServiceBuilder`], with extra complication
108/// around maintaining the network and transport types.
109///
110/// The [`ProviderBuilder`] can be instantiated in two ways, using `ProviderBuilder::new()` or
111/// `ProviderBuilder::default()`.
112///
113/// `ProviderBuilder::new()` will create a new [`ProviderBuilder`] with the [`RecommendedFillers`]
114/// enabled, whereas `ProviderBuilder::default()` will instantiate it in its vanilla
115/// [`ProviderBuilder`] form i.e with no fillers enabled.
116///
117/// [`tower::ServiceBuilder`]: https://docs.rs/tower/latest/tower/struct.ServiceBuilder.html
118#[derive(Debug)]
119pub struct ProviderBuilder<L, F, N = Ethereum> {
120    layer: L,
121    filler: F,
122    network: PhantomData<fn() -> N>,
123}
124
125impl
126    ProviderBuilder<
127        Identity,
128        JoinFill<Identity, <Ethereum as RecommendedFillers>::RecommendedFillers>,
129        Ethereum,
130    >
131{
132    /// Create a new [`ProviderBuilder`] with the recommended filler enabled.
133    ///
134    /// Recommended fillers are preconfigured set of fillers that handle gas estimation, nonce
135    /// management, and chain-id fetching.
136    ///
137    /// Building a provider with this setting enabled will return a [`crate::fillers::FillProvider`]
138    /// with [`crate::utils::JoinedRecommendedFillers`].
139    ///
140    /// You can opt-out of using these fillers by using the `.disable_recommended_fillers()` method.
141    pub fn new() -> Self {
142        ProviderBuilder::default().with_recommended_fillers()
143    }
144
145    /// Opt-out of the recommended fillers by resetting the fillers stack in the
146    /// [`ProviderBuilder`].
147    ///
148    /// This is equivalent to creating the builder using `ProviderBuilder::default()`.
149    pub fn disable_recommended_fillers(self) -> ProviderBuilder<Identity, Identity, Ethereum> {
150        ProviderBuilder { layer: self.layer, filler: Identity, network: self.network }
151    }
152}
153
154impl<N> Default for ProviderBuilder<Identity, Identity, N> {
155    fn default() -> Self {
156        Self { layer: Identity, filler: Identity, network: PhantomData }
157    }
158}
159
160impl ProviderBuilder<Identity, Identity, Ethereum> {
161    /// Create a new [`ProviderBuilder`] with the [`RecommendedFillers`] for the provided
162    /// [`Network`].
163    pub fn new_with_network<Net: RecommendedFillers>(
164    ) -> ProviderBuilder<Identity, JoinFill<Identity, Net::RecommendedFillers>, Net> {
165        ProviderBuilder {
166            layer: Identity,
167            filler: JoinFill::new(Identity, Net::recommended_fillers()),
168            network: PhantomData,
169        }
170    }
171}
172
173impl<L, N: Network> ProviderBuilder<L, Identity, N> {
174    /// Add preconfigured set of layers handling gas estimation, nonce
175    /// management, and chain-id fetching.
176    pub fn with_recommended_fillers(
177        self,
178    ) -> ProviderBuilder<L, JoinFill<Identity, N::RecommendedFillers>, N>
179    where
180        N: RecommendedFillers,
181    {
182        self.filler(N::recommended_fillers())
183    }
184}
185
186impl<L, F, N> ProviderBuilder<L, F, N> {
187    /// Apply a function to this builder.
188    ///
189    /// This is useful for extracting reusable builder-style helper functions without manually
190    /// reconstructing the [`ProviderBuilder`].
191    pub fn apply<T>(self, f: impl FnOnce(Self) -> T) -> T {
192        f(self)
193    }
194
195    /// Map the layer stack to a new type.
196    ///
197    /// This is useful for customizing or replacing the accumulated layers in a reusable helper.
198    pub fn map_layer<L2>(self, f: impl FnOnce(L) -> L2) -> ProviderBuilder<L2, F, N> {
199        ProviderBuilder { layer: f(self.layer), filler: self.filler, network: PhantomData }
200    }
201
202    /// Map the filler stack to a new type.
203    ///
204    /// This is useful for customizing or replacing the accumulated fillers in a reusable helper.
205    pub fn map_filler<F2>(self, f: impl FnOnce(F) -> F2) -> ProviderBuilder<L, F2, N> {
206        ProviderBuilder { layer: self.layer, filler: f(self.filler), network: PhantomData }
207    }
208
209    /// Add a layer to the stack being built. This is similar to
210    /// [`tower::ServiceBuilder::layer`].
211    ///
212    /// ## Note:
213    ///
214    /// Layers are added in outer-to-inner order, as in
215    /// [`tower::ServiceBuilder`]. The first layer added will be the first to
216    /// see the request.
217    ///
218    /// [`tower::ServiceBuilder::layer`]: https://docs.rs/tower/latest/tower/struct.ServiceBuilder.html#method.layer
219    /// [`tower::ServiceBuilder`]: https://docs.rs/tower/latest/tower/struct.ServiceBuilder.html
220    pub fn layer<Inner>(self, layer: Inner) -> ProviderBuilder<Stack<Inner, L>, F, N> {
221        self.map_layer(|current| Stack::new(layer, current))
222    }
223
224    /// Add a transaction filler to the stack being built. Transaction fillers
225    /// are used to fill in missing fields on transactions before they are sent,
226    /// and are all joined to form the outermost layer of the stack.
227    pub fn filler<F2>(self, filler: F2) -> ProviderBuilder<L, JoinFill<F, F2>, N> {
228        self.map_filler(|current| JoinFill::new(current, filler))
229    }
230
231    /// Change the network.
232    ///
233    /// By default, the network is `Ethereum`. This method must be called to configure a different
234    /// network.
235    ///
236    /// This replaces the filler stack with the target network's recommended fillers. Any custom
237    /// fillers should be added **after** calling `.network()`.
238    ///
239    /// ```ignore
240    /// builder.network::<Arbitrum>()
241    /// ```
242    pub fn network<Net: RecommendedFillers>(
243        self,
244    ) -> ProviderBuilder<L, JoinFill<Identity, Net::RecommendedFillers>, Net> {
245        ProviderBuilder {
246            layer: self.layer,
247            filler: JoinFill::new(Identity, Net::recommended_fillers()),
248            network: PhantomData,
249        }
250    }
251
252    /// Add a chain layer to the stack being built. The layer will set
253    /// the client's poll interval based on the average block time for this chain.
254    ///
255    /// Does nothing to the client with a local transport.
256    pub fn with_chain(self, chain: NamedChain) -> ProviderBuilder<Stack<ChainLayer, L>, F, N> {
257        self.layer(ChainLayer::new(chain))
258    }
259
260    // --- Fillers ---
261
262    /// Add blob gas estimation to the stack being built.
263    ///
264    /// See [`BlobGasFiller`] for more information.
265    pub fn with_blob_gas_estimation(self) -> ProviderBuilder<L, JoinFill<F, BlobGasFiller>, N> {
266        self.filler(BlobGasFiller::default())
267    }
268
269    /// Add blob gas estimation to the stack being built, using the provided estimator.
270    ///
271    /// See [`BlobGasFiller`] and [`BlobGasEstimator`] for more information.
272    pub fn with_blob_gas_estimator(
273        self,
274        estimator: BlobGasEstimator,
275    ) -> ProviderBuilder<L, JoinFill<F, BlobGasFiller>, N> {
276        self.filler(BlobGasFiller { estimator })
277    }
278
279    /// Add gas estimation to the stack being built.
280    ///
281    /// See [`GasFiller`] for more information.
282    pub fn with_gas_estimation(self) -> ProviderBuilder<L, JoinFill<F, GasFiller>, N> {
283        self.filler(GasFiller::default())
284    }
285
286    /// Add EIP-1559 gas estimation to the stack being built, using the provided estimator.
287    ///
288    /// See [`GasFiller`] and [`Eip1559Estimator`] for more information.
289    pub fn with_eip1559_estimator(
290        self,
291        estimator: Eip1559Estimator,
292    ) -> ProviderBuilder<L, JoinFill<F, GasFiller>, N> {
293        self.filler(GasFiller { estimator })
294    }
295
296    /// Add nonce management to the stack being built.
297    ///
298    /// See [`NonceFiller`] for more information.
299    pub fn with_nonce_management<M: NonceManager>(
300        self,
301        nonce_manager: M,
302    ) -> ProviderBuilder<L, JoinFill<F, NonceFiller<M>>, N> {
303        self.filler(NonceFiller::new(nonce_manager))
304    }
305
306    /// Add simple nonce management to the stack being built.
307    ///
308    /// See [`SimpleNonceManager`] for more information.
309    pub fn with_simple_nonce_management(
310        self,
311    ) -> ProviderBuilder<L, JoinFill<F, NonceFiller<SimpleNonceManager>>, N> {
312        self.with_nonce_management(SimpleNonceManager::default())
313    }
314
315    /// Add cached nonce management to the stack being built.
316    ///
317    /// See [`CachedNonceManager`] for more information.
318    pub fn with_cached_nonce_management(
319        self,
320    ) -> ProviderBuilder<L, JoinFill<F, NonceFiller<CachedNonceManager>>, N> {
321        self.with_nonce_management(CachedNonceManager::default())
322    }
323
324    /// Add a chain ID filler to the stack being built. The filler will attempt
325    /// to fetch the chain ID from the provider using
326    /// [`Provider::get_chain_id`]. the first time a transaction is prepared,
327    /// and will cache it for future transactions.
328    pub fn fetch_chain_id(self) -> ProviderBuilder<L, JoinFill<F, ChainIdFiller>, N> {
329        self.filler(ChainIdFiller::default())
330    }
331
332    /// Add a specific chain ID to the stack being built. The filler will
333    /// fill transactions with the provided chain ID, regardless of the chain ID
334    /// that the provider reports via [`Provider::get_chain_id`].
335    pub fn with_chain_id(
336        self,
337        chain_id: ChainId,
338    ) -> ProviderBuilder<L, JoinFill<F, ChainIdFiller>, N> {
339        self.filler(ChainIdFiller::new(Some(chain_id)))
340    }
341
342    /// Add a wallet layer to the stack being built.
343    ///
344    /// See [`WalletFiller`].
345    pub fn wallet<W: IntoWallet<N>>(
346        self,
347        wallet: W,
348    ) -> ProviderBuilder<L, JoinFill<F, WalletFiller<W::NetworkWallet>>, N>
349    where
350        N: Network,
351    {
352        self.filler(WalletFiller::new(wallet.into_wallet()))
353    }
354
355    // --- Layers ---
356
357    /// Aggregate multiple `eth_call` requests into a single batch request using Multicall3.
358    ///
359    /// See [`CallBatchLayer`] for more information.
360    pub fn with_call_batching(self) -> ProviderBuilder<Stack<CallBatchLayer, L>, F, N> {
361        self.layer(CallBatchLayer::new())
362    }
363
364    /// Aggregate multiple `eth_call` requests with block number queries done by calling Arbsym
365    /// precompile.
366    ///
367    /// See [`CallBatchLayer`] for more information.
368    pub fn with_arbitrum_call_batching(self) -> ProviderBuilder<Stack<CallBatchLayer, L>, F, N> {
369        self.layer(CallBatchLayer::new().arbitrum_compat())
370    }
371
372    /// Add response caching to the stack being built with the specified maximum cache size.
373    ///
374    /// See [`CacheLayer`](crate::layers::CacheLayer) for more information.
375    #[cfg(not(target_family = "wasm"))]
376    pub fn with_caching(
377        self,
378        max_items: u32,
379    ) -> ProviderBuilder<Stack<crate::layers::CacheLayer, L>, F, N> {
380        self.layer(crate::layers::CacheLayer::new(max_items))
381    }
382
383    /// Add response caching to the stack being built with a default cache size of 100 items.
384    ///
385    /// See [`CacheLayer`](crate::layers::CacheLayer) for more information.
386    #[cfg(not(target_family = "wasm"))]
387    pub fn with_default_caching(
388        self,
389    ) -> ProviderBuilder<Stack<crate::layers::CacheLayer, L>, F, N> {
390        self.with_caching(100)
391    }
392
393    /// Set a default [`BlockId`] for `eth_call` and `eth_estimateGas`.
394    ///
395    /// [`BlockId`]: alloy_eips::BlockId
396    pub fn with_default_block(
397        self,
398        block_id: alloy_eips::BlockId,
399    ) -> ProviderBuilder<Stack<BlockIdLayer, L>, F, N> {
400        self.layer(BlockIdLayer::new(block_id))
401    }
402
403    // --- Build to Provider ---
404
405    /// Finish the layer stack by providing a root [`Provider`], outputting
406    /// the final [`Provider`] type with all stack components.
407    pub fn connect_provider<P>(self, provider: P) -> F::Provider
408    where
409        L: ProviderLayer<P, N>,
410        F: TxFiller<N> + ProviderLayer<L::Provider, N>,
411        P: Provider<N>,
412        N: Network,
413    {
414        let Self { layer, filler, network: PhantomData } = self;
415        let stack = Stack::new(layer, filler);
416        stack.layer(provider)
417    }
418
419    /// Finish the layer stack by providing a root [`RpcClient`], outputting
420    /// the final [`Provider`] type with all stack components.
421    ///
422    /// This is a convenience function for
423    /// `ProviderBuilder::on_provider(RootProvider::new(client))`.
424    pub fn connect_client(self, client: RpcClient) -> F::Provider
425    where
426        L: ProviderLayer<RootProvider<N>, N>,
427        F: TxFiller<N> + ProviderLayer<L::Provider, N>,
428        N: Network,
429    {
430        self.connect_provider(RootProvider::new(client))
431    }
432
433    /// Finish the layer stack by providing a [`RpcClient`] that mocks responses, outputting
434    /// the final [`Provider`] type with all stack components.
435    ///
436    /// This is a convenience function for
437    /// `ProviderBuilder::on_client(RpcClient::mocked(asserter))`.
438    pub fn connect_mocked_client(self, asserter: alloy_transport::mock::Asserter) -> F::Provider
439    where
440        L: ProviderLayer<RootProvider<N>, N>,
441        F: TxFiller<N> + ProviderLayer<L::Provider, N>,
442        N: Network,
443    {
444        self.connect_client(RpcClient::mocked(asserter))
445    }
446
447    /// Finish the layer stack by providing a connection string for a built-in
448    /// transport type, outputting the final [`Provider`] type with all stack
449    /// components.
450    #[doc(alias = "on_builtin")]
451    pub async fn connect(self, s: &str) -> Result<F::Provider, TransportError>
452    where
453        L: ProviderLayer<RootProvider<N>, N>,
454        F: TxFiller<N> + ProviderLayer<L::Provider, N>,
455        N: Network,
456    {
457        let client = ClientBuilder::default().connect(s).await?;
458        Ok(self.connect_client(client))
459    }
460
461    /// Finish the layer stack by providing a connection string with custom configuration.
462    ///
463    /// This method allows for fine-grained control over connection settings
464    /// such as authentication, retry behavior, and transport-specific options.
465    /// The transport type is extracted from the connection string and configured
466    /// using the provided [`ConnectionConfig`].
467    ///
468    /// # Examples
469    ///
470    /// ```
471    /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
472    /// use alloy_provider::{ConnectionConfig, ProviderBuilder};
473    /// use alloy_transport::Authorization;
474    /// use std::time::Duration;
475    ///
476    /// let config = ConnectionConfig::new()
477    ///     .with_auth(Authorization::bearer("my-token"))
478    ///     .with_max_retries(3)
479    ///     .with_retry_interval(Duration::from_secs(2));
480    ///
481    /// let provider =
482    ///     ProviderBuilder::new().connect_with_config("ws://localhost:8545", config).await?;
483    /// # Ok(())
484    /// # }
485    /// ```
486    pub async fn connect_with_config(
487        self,
488        s: &str,
489        config: ConnectionConfig,
490    ) -> Result<F::Provider, TransportError>
491    where
492        L: ProviderLayer<RootProvider<N>, N>,
493        F: TxFiller<N> + ProviderLayer<L::Provider, N>,
494        N: Network,
495    {
496        let client = ClientBuilder::default().connect_with_config(s, config).await?;
497        Ok(self.connect_client(client))
498    }
499
500    /// Finish the layer stack by providing a [`TransportConnect`] instance.
501    pub async fn connect_with<C>(self, connect: &C) -> Result<F::Provider, TransportError>
502    where
503        L: ProviderLayer<RootProvider<N>, N>,
504        F: TxFiller<N> + ProviderLayer<L::Provider, N>,
505        N: Network,
506        C: TransportConnect,
507    {
508        connect
509            .get_transport()
510            .await
511            .map(|t| RpcClient::new(t, connect.is_local()))
512            .map(|client| self.connect_client(client))
513    }
514
515    /// Finish the layer stack by providing a [`PubSubConnect`] instance,
516    /// producing a [`Provider`] with pubsub capabilities.
517    ///
518    /// [`PubSubConnect`]: alloy_pubsub::PubSubConnect
519    #[cfg(feature = "pubsub")]
520    pub async fn connect_pubsub_with<C>(self, connect: C) -> Result<F::Provider, TransportError>
521    where
522        L: ProviderLayer<RootProvider<N>, N>,
523        F: TxFiller<N> + ProviderLayer<L::Provider, N>,
524        N: Network,
525        C: alloy_pubsub::PubSubConnect,
526    {
527        ClientBuilder::default().pubsub(connect).await.map(|client| self.connect_client(client))
528    }
529
530    /// Build this provider with a websocket connection.
531    #[cfg(feature = "ws")]
532    pub async fn connect_ws(
533        self,
534        connect: alloy_transport_ws::WsConnect,
535    ) -> Result<F::Provider, TransportError>
536    where
537        L: ProviderLayer<RootProvider<N>, N>,
538        F: TxFiller<N> + ProviderLayer<L::Provider, N>,
539        N: Network,
540    {
541        let client = ClientBuilder::default().ws(connect).await?;
542        Ok(self.connect_client(client))
543    }
544
545    /// Build this provider with an IPC connection.
546    #[cfg(feature = "ipc")]
547    pub async fn connect_ipc<T>(
548        self,
549        connect: alloy_transport_ipc::IpcConnect<T>,
550    ) -> Result<F::Provider, TransportError>
551    where
552        alloy_transport_ipc::IpcConnect<T>: alloy_pubsub::PubSubConnect,
553        L: ProviderLayer<RootProvider<N>, N>,
554        F: TxFiller<N> + ProviderLayer<L::Provider, N>,
555        N: Network,
556    {
557        let client = ClientBuilder::default().ipc(connect).await?;
558        Ok(self.connect_client(client))
559    }
560
561    /// Build this provider with an Reqwest HTTP transport.
562    #[cfg(any(test, feature = "reqwest"))]
563    pub fn connect_http(self, url: reqwest::Url) -> F::Provider
564    where
565        L: ProviderLayer<crate::RootProvider<N>, N>,
566        F: TxFiller<N> + ProviderLayer<L::Provider, N>,
567        N: Network,
568    {
569        let client = ClientBuilder::default().http(url);
570        self.connect_client(client)
571    }
572
573    /// Build this provider with a pre-built Reqwest client.
574    #[cfg(any(test, feature = "reqwest"))]
575    pub fn connect_reqwest<C>(self, client: C, url: reqwest::Url) -> F::Provider
576    where
577        L: ProviderLayer<crate::RootProvider<N>, N>,
578        F: TxFiller<N> + ProviderLayer<L::Provider, N>,
579        N: Network,
580        C: Into<reqwest::Client>,
581    {
582        let client = ClientBuilder::default().http_with_client(client.into(), url);
583        self.connect_client(client)
584    }
585
586    /// Build this provider with a provided Reqwest client builder.
587    #[cfg(any(test, feature = "reqwest"))]
588    pub fn with_reqwest<B>(self, url: reqwest::Url, builder: B) -> F::Provider
589    where
590        L: ProviderLayer<crate::RootProvider<N>, N>,
591        F: TxFiller<N> + ProviderLayer<L::Provider, N>,
592        N: Network,
593        B: FnOnce(reqwest::ClientBuilder) -> reqwest::Client,
594    {
595        self.connect_reqwest(builder(reqwest::ClientBuilder::default()), url)
596    }
597
598    /// Build this provider with an Hyper HTTP transport.
599    #[cfg(feature = "hyper")]
600    pub fn connect_hyper_http(self, url: url::Url) -> F::Provider
601    where
602        L: ProviderLayer<crate::RootProvider<N>, N>,
603        F: TxFiller<N> + ProviderLayer<L::Provider, N>,
604        N: Network,
605    {
606        let client = ClientBuilder::default().hyper_http(url);
607        self.connect_client(client)
608    }
609}
610
611#[cfg(any(test, feature = "anvil-node"))]
612type JoinedEthereumWalletFiller<F> = JoinFill<F, WalletFiller<alloy_network::EthereumWallet>>;
613
614#[cfg(any(test, feature = "anvil-node"))]
615type AnvilProviderResult<T> = Result<T, alloy_node_bindings::NodeError>;
616
617#[cfg(any(test, feature = "anvil-node"))]
618impl<L, F, N: Network> ProviderBuilder<L, F, N> {
619    /// Build this provider with anvil, using the BoxTransport.
620    ///
621    /// This method requires the `anvil-node` feature on `alloy-provider`.
622    /// When using the `alloy` meta-crate, enable `provider-anvil-node`, or
623    /// combine `providers` with `node-bindings`.
624    #[cfg_attr(docsrs, doc(cfg(feature = "anvil-node")))]
625    pub fn connect_anvil(self) -> F::Provider
626    where
627        F: TxFiller<N> + ProviderLayer<L::Provider, N>,
628        L: crate::builder::ProviderLayer<
629            crate::layers::AnvilProvider<crate::provider::RootProvider<N>, N>,
630            N,
631        >,
632    {
633        self.connect_anvil_with_config(std::convert::identity)
634    }
635
636    /// Build this provider with anvil, using the BoxTransport. This
637    /// function configures a wallet backed by anvil keys, and is intended for
638    /// use in tests.
639    ///
640    /// This method requires the `anvil-node` feature on `alloy-provider`.
641    /// When using the `alloy` meta-crate, enable `provider-anvil-node`, or
642    /// combine `providers` with `node-bindings`.
643    #[cfg_attr(docsrs, doc(cfg(feature = "anvil-node")))]
644    pub fn connect_anvil_with_wallet(
645        self,
646    ) -> <JoinedEthereumWalletFiller<F> as ProviderLayer<L::Provider, N>>::Provider
647    where
648        F: TxFiller<N> + ProviderLayer<L::Provider, N>,
649        L: crate::builder::ProviderLayer<
650            crate::layers::AnvilProvider<crate::provider::RootProvider<N>, N>,
651            N,
652        >,
653        alloy_network::EthereumWallet: alloy_network::NetworkWallet<N>,
654    {
655        self.connect_anvil_with_wallet_and_config(std::convert::identity)
656            .expect("failed to build provider")
657    }
658
659    /// Build this provider with anvil, using the BoxTransport. The
660    /// given function is used to configure the anvil instance.
661    ///
662    /// This method requires the `anvil-node` feature on `alloy-provider`.
663    /// When using the `alloy` meta-crate, enable `provider-anvil-node`, or
664    /// combine `providers` with `node-bindings`.
665    #[cfg_attr(docsrs, doc(cfg(feature = "anvil-node")))]
666    pub fn connect_anvil_with_config(
667        self,
668        f: impl FnOnce(alloy_node_bindings::Anvil) -> alloy_node_bindings::Anvil,
669    ) -> F::Provider
670    where
671        F: TxFiller<N> + ProviderLayer<L::Provider, N>,
672        L: crate::builder::ProviderLayer<
673            crate::layers::AnvilProvider<crate::provider::RootProvider<N>, N>,
674            N,
675        >,
676    {
677        let anvil_layer = crate::layers::AnvilLayer::from(f(Default::default()));
678        let url = anvil_layer.endpoint_url();
679
680        let rpc_client = ClientBuilder::default().http(url);
681
682        self.layer(anvil_layer).connect_client(rpc_client)
683    }
684
685    /// Build this provider with anvil, using the BoxTransport. The
686    /// given function is used to configure the anvil instance.
687    ///
688    /// This method requires the `anvil-node` feature on `alloy-provider`.
689    /// When using the `alloy` meta-crate, enable `provider-anvil-node`, or
690    /// combine `providers` with `node-bindings`.
691    #[cfg_attr(docsrs, doc(cfg(feature = "anvil-node")))]
692    #[deprecated(since = "0.12.6", note = "use `connect_anvil_with_config` instead")]
693    pub fn on_anvil_with_config(
694        self,
695        f: impl FnOnce(alloy_node_bindings::Anvil) -> alloy_node_bindings::Anvil,
696    ) -> F::Provider
697    where
698        L: ProviderLayer<crate::layers::AnvilProvider<RootProvider<N>, N>, N>,
699        F: TxFiller<N> + ProviderLayer<L::Provider, N>,
700    {
701        let anvil_layer = crate::layers::AnvilLayer::from(f(Default::default()));
702        let url = anvil_layer.endpoint_url();
703
704        let rpc_client = ClientBuilder::default().http(url);
705
706        self.layer(anvil_layer).connect_client(rpc_client)
707    }
708
709    /// Build this provider with anvil, using the BoxTransport.
710    /// This calls `try_on_anvil_with_wallet_and_config` and panics on error.
711    ///
712    /// This method requires the `anvil-node` feature on `alloy-provider`.
713    /// When using the `alloy` meta-crate, enable `provider-anvil-node`, or
714    /// combine `providers` with `node-bindings`.
715    #[cfg_attr(docsrs, doc(cfg(feature = "anvil-node")))]
716    pub fn connect_anvil_with_wallet_and_config(
717        self,
718        f: impl FnOnce(alloy_node_bindings::Anvil) -> alloy_node_bindings::Anvil,
719    ) -> AnvilProviderResult<
720        <JoinedEthereumWalletFiller<F> as ProviderLayer<L::Provider, N>>::Provider,
721    >
722    where
723        F: TxFiller<N> + ProviderLayer<L::Provider, N>,
724        L: crate::builder::ProviderLayer<
725            crate::layers::AnvilProvider<crate::provider::RootProvider<N>, N>,
726            N,
727        >,
728        alloy_network::EthereumWallet: alloy_network::NetworkWallet<N>,
729    {
730        let anvil_layer = crate::layers::AnvilLayer::from(f(Default::default()));
731        let url = anvil_layer.endpoint_url();
732
733        let wallet = anvil_layer
734            .instance()
735            .wallet()
736            .ok_or(alloy_node_bindings::NodeError::NoKeysAvailable)?;
737
738        let rpc_client = ClientBuilder::default().http(url);
739
740        Ok(self.wallet(wallet).layer(anvil_layer).connect_client(rpc_client))
741    }
742
743    /// Build this provider with anvil, using the BoxTransport.
744    /// This calls `try_on_anvil_with_wallet_and_config` and panics on error.
745    ///
746    /// This method requires the `anvil-node` feature on `alloy-provider`.
747    /// When using the `alloy` meta-crate, enable `provider-anvil-node`, or
748    /// combine `providers` with `node-bindings`.
749    #[cfg_attr(docsrs, doc(cfg(feature = "anvil-node")))]
750    #[deprecated(since = "0.12.6", note = "use `connect_anvil_with_wallet_and_config` instead")]
751    pub fn on_anvil_with_wallet_and_config(
752        self,
753        f: impl FnOnce(alloy_node_bindings::Anvil) -> alloy_node_bindings::Anvil,
754    ) -> AnvilProviderResult<
755        <JoinedEthereumWalletFiller<F> as ProviderLayer<L::Provider, N>>::Provider,
756    >
757    where
758        F: TxFiller<N> + ProviderLayer<L::Provider, N>,
759        L: crate::builder::ProviderLayer<
760            crate::layers::AnvilProvider<crate::provider::RootProvider<N>, N>,
761            N,
762        >,
763        alloy_network::EthereumWallet: alloy_network::NetworkWallet<N>,
764    {
765        let anvil_layer = crate::layers::AnvilLayer::from(f(Default::default()));
766        let url = anvil_layer.endpoint_url();
767
768        let wallet = anvil_layer
769            .instance()
770            .wallet()
771            .ok_or(alloy_node_bindings::NodeError::NoKeysAvailable)?;
772
773        let rpc_client = ClientBuilder::default().http(url);
774
775        Ok(self.wallet(wallet).layer(anvil_layer).connect_client(rpc_client))
776    }
777}
778
779#[cfg(test)]
780mod tests {
781    use super::*;
782    use crate::Provider;
783    use alloy_network::AnyNetwork;
784
785    #[tokio::test]
786    async fn basic() {
787        let provider = ProviderBuilder::new()
788            .with_cached_nonce_management()
789            .with_call_batching()
790            .connect_http("http://localhost:8545".parse().unwrap());
791        let _ = provider.get_account(Default::default());
792        let provider = provider.erased();
793        let _ = provider.get_account(Default::default());
794    }
795
796    #[tokio::test]
797    #[cfg(feature = "reqwest")]
798    async fn test_connect_reqwest() {
799        let provider = ProviderBuilder::new()
800            .with_cached_nonce_management()
801            .with_call_batching()
802            .connect_reqwest(
803                reqwest::Client::new(),
804                reqwest::Url::parse("http://localhost:8545").unwrap(),
805            );
806        let _ = provider.get_account(Default::default());
807        let provider = provider.erased();
808        let _ = provider.get_account(Default::default());
809    }
810
811    #[tokio::test]
812    #[cfg(feature = "reqwest")]
813    async fn test_with_reqwest() {
814        let provider = ProviderBuilder::new()
815            .with_cached_nonce_management()
816            .with_call_batching()
817            .with_reqwest(reqwest::Url::parse("http://localhost:8545").unwrap(), |builder| {
818                builder
819                    .user_agent("alloy/test")
820                    .timeout(std::time::Duration::from_secs(10))
821                    .build()
822                    .expect("failed to build reqwest client")
823            });
824        let _ = provider.get_account(Default::default());
825        let provider = provider.erased();
826        let _ = provider.get_account(Default::default());
827    }
828
829    #[tokio::test]
830    async fn compile_with_network() {
831        let p = ProviderBuilder::new_with_network::<AnyNetwork>().connect_anvil();
832        let num = p.get_block_number().await.unwrap();
833        assert_eq!(num, 0);
834    }
835
836    // Ensures `.network()` replaces fillers rather than keeping the old ones.
837    #[test]
838    fn network_replaces_fillers() {
839        // Add an extra filler before swapping, it should be dropped.
840        let builder = ProviderBuilder::new().filler(GasFiller::default()).network::<AnyNetwork>();
841
842        let _: ProviderBuilder<
843            Identity,
844            JoinFill<Identity, <AnyNetwork as RecommendedFillers>::RecommendedFillers>,
845            AnyNetwork,
846        > = builder;
847    }
848
849    #[test]
850    fn apply_transforms_builder() {
851        let builder = ProviderBuilder::new()
852            .apply(|builder| builder.disable_recommended_fillers().with_gas_estimation());
853
854        let _: ProviderBuilder<Identity, JoinFill<Identity, GasFiller>, Ethereum> = builder;
855    }
856
857    #[test]
858    fn map_filler_replaces_fillers() {
859        let builder = ProviderBuilder::new().map_filler(|_| GasFiller::default());
860
861        let _: ProviderBuilder<Identity, GasFiller, Ethereum> = builder;
862    }
863
864    #[test]
865    fn map_layer_replaces_layers() {
866        let builder = ProviderBuilder::<Identity, Identity>::default()
867            .map_layer(|_| ChainLayer::new(NamedChain::Mainnet));
868
869        let _: ProviderBuilder<ChainLayer, Identity, Ethereum> = builder;
870    }
871
872    #[tokio::test]
873    async fn network_swap_works_at_runtime() {
874        // Verify that `ProviderBuilder::new().network::<AnyNetwork>()` produces a working provider.
875        let p = ProviderBuilder::new().network::<AnyNetwork>().connect_anvil();
876        let num = p.get_block_number().await.unwrap();
877        assert_eq!(num, 0);
878    }
879}