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    /// Add a layer to the stack being built. This is similar to
188    /// [`tower::ServiceBuilder::layer`].
189    ///
190    /// ## Note:
191    ///
192    /// Layers are added in outer-to-inner order, as in
193    /// [`tower::ServiceBuilder`]. The first layer added will be the first to
194    /// see the request.
195    ///
196    /// [`tower::ServiceBuilder::layer`]: https://docs.rs/tower/latest/tower/struct.ServiceBuilder.html#method.layer
197    /// [`tower::ServiceBuilder`]: https://docs.rs/tower/latest/tower/struct.ServiceBuilder.html
198    pub fn layer<Inner>(self, layer: Inner) -> ProviderBuilder<Stack<Inner, L>, F, N> {
199        ProviderBuilder {
200            layer: Stack::new(layer, self.layer),
201            filler: self.filler,
202            network: PhantomData,
203        }
204    }
205
206    /// Add a transaction filler to the stack being built. Transaction fillers
207    /// are used to fill in missing fields on transactions before they are sent,
208    /// and are all joined to form the outermost layer of the stack.
209    pub fn filler<F2>(self, filler: F2) -> ProviderBuilder<L, JoinFill<F, F2>, N> {
210        ProviderBuilder {
211            layer: self.layer,
212            filler: JoinFill::new(self.filler, filler),
213            network: PhantomData,
214        }
215    }
216
217    /// Change the network.
218    ///
219    /// By default, the network is `Ethereum`. This method must be called to configure a different
220    /// network.
221    ///
222    /// This replaces the filler stack with the target network's recommended fillers. Any custom
223    /// fillers should be added **after** calling `.network()`.
224    ///
225    /// ```ignore
226    /// builder.network::<Arbitrum>()
227    /// ```
228    pub fn network<Net: RecommendedFillers>(
229        self,
230    ) -> ProviderBuilder<L, JoinFill<Identity, Net::RecommendedFillers>, Net> {
231        ProviderBuilder {
232            layer: self.layer,
233            filler: JoinFill::new(Identity, Net::recommended_fillers()),
234            network: PhantomData,
235        }
236    }
237
238    /// Add a chain layer to the stack being built. The layer will set
239    /// the client's poll interval based on the average block time for this chain.
240    ///
241    /// Does nothing to the client with a local transport.
242    pub fn with_chain(self, chain: NamedChain) -> ProviderBuilder<Stack<ChainLayer, L>, F, N> {
243        self.layer(ChainLayer::new(chain))
244    }
245
246    // --- Fillers ---
247
248    /// Add blob gas estimation to the stack being built.
249    ///
250    /// See [`BlobGasFiller`] for more information.
251    pub fn with_blob_gas_estimation(self) -> ProviderBuilder<L, JoinFill<F, BlobGasFiller>, N> {
252        self.filler(BlobGasFiller::default())
253    }
254
255    /// Add blob gas estimation to the stack being built, using the provided estimator.
256    ///
257    /// See [`BlobGasFiller`] and [`BlobGasEstimator`] for more information.
258    pub fn with_blob_gas_estimator(
259        self,
260        estimator: BlobGasEstimator,
261    ) -> ProviderBuilder<L, JoinFill<F, BlobGasFiller>, N> {
262        self.filler(BlobGasFiller { estimator })
263    }
264
265    /// Add gas estimation to the stack being built.
266    ///
267    /// See [`GasFiller`] for more information.
268    pub fn with_gas_estimation(self) -> ProviderBuilder<L, JoinFill<F, GasFiller>, N> {
269        self.filler(GasFiller::default())
270    }
271
272    /// Add EIP-1559 gas estimation to the stack being built, using the provided estimator.
273    ///
274    /// See [`GasFiller`] and [`Eip1559Estimator`] for more information.
275    pub fn with_eip1559_estimator(
276        self,
277        estimator: Eip1559Estimator,
278    ) -> ProviderBuilder<L, JoinFill<F, GasFiller>, N> {
279        self.filler(GasFiller { estimator })
280    }
281
282    /// Add nonce management to the stack being built.
283    ///
284    /// See [`NonceFiller`] for more information.
285    pub fn with_nonce_management<M: NonceManager>(
286        self,
287        nonce_manager: M,
288    ) -> ProviderBuilder<L, JoinFill<F, NonceFiller<M>>, N> {
289        self.filler(NonceFiller::new(nonce_manager))
290    }
291
292    /// Add simple nonce management to the stack being built.
293    ///
294    /// See [`SimpleNonceManager`] for more information.
295    pub fn with_simple_nonce_management(
296        self,
297    ) -> ProviderBuilder<L, JoinFill<F, NonceFiller<SimpleNonceManager>>, N> {
298        self.with_nonce_management(SimpleNonceManager::default())
299    }
300
301    /// Add cached nonce management to the stack being built.
302    ///
303    /// See [`CachedNonceManager`] for more information.
304    pub fn with_cached_nonce_management(
305        self,
306    ) -> ProviderBuilder<L, JoinFill<F, NonceFiller<CachedNonceManager>>, N> {
307        self.with_nonce_management(CachedNonceManager::default())
308    }
309
310    /// Add a chain ID filler to the stack being built. The filler will attempt
311    /// to fetch the chain ID from the provider using
312    /// [`Provider::get_chain_id`]. the first time a transaction is prepared,
313    /// and will cache it for future transactions.
314    pub fn fetch_chain_id(self) -> ProviderBuilder<L, JoinFill<F, ChainIdFiller>, N> {
315        self.filler(ChainIdFiller::default())
316    }
317
318    /// Add a specific chain ID to the stack being built. The filler will
319    /// fill transactions with the provided chain ID, regardless of the chain ID
320    /// that the provider reports via [`Provider::get_chain_id`].
321    pub fn with_chain_id(
322        self,
323        chain_id: ChainId,
324    ) -> ProviderBuilder<L, JoinFill<F, ChainIdFiller>, N> {
325        self.filler(ChainIdFiller::new(Some(chain_id)))
326    }
327
328    /// Add a wallet layer to the stack being built.
329    ///
330    /// See [`WalletFiller`].
331    pub fn wallet<W: IntoWallet<N>>(
332        self,
333        wallet: W,
334    ) -> ProviderBuilder<L, JoinFill<F, WalletFiller<W::NetworkWallet>>, N>
335    where
336        N: Network,
337    {
338        self.filler(WalletFiller::new(wallet.into_wallet()))
339    }
340
341    // --- Layers ---
342
343    /// Aggregate multiple `eth_call` requests into a single batch request using Multicall3.
344    ///
345    /// See [`CallBatchLayer`] for more information.
346    pub fn with_call_batching(self) -> ProviderBuilder<Stack<CallBatchLayer, L>, F, N> {
347        self.layer(CallBatchLayer::new())
348    }
349
350    /// Aggregate multiple `eth_call` requests with block number queries done by calling Arbsym
351    /// precompile.
352    ///
353    /// See [`CallBatchLayer`] for more information.
354    pub fn with_arbitrum_call_batching(self) -> ProviderBuilder<Stack<CallBatchLayer, L>, F, N> {
355        self.layer(CallBatchLayer::new().arbitrum_compat())
356    }
357
358    /// Add response caching to the stack being built with the specified maximum cache size.
359    ///
360    /// See [`CacheLayer`](crate::layers::CacheLayer) for more information.
361    #[cfg(not(target_family = "wasm"))]
362    pub fn with_caching(
363        self,
364        max_items: u32,
365    ) -> ProviderBuilder<Stack<crate::layers::CacheLayer, L>, F, N> {
366        self.layer(crate::layers::CacheLayer::new(max_items))
367    }
368
369    /// Add response caching to the stack being built with a default cache size of 100 items.
370    ///
371    /// See [`CacheLayer`](crate::layers::CacheLayer) for more information.
372    #[cfg(not(target_family = "wasm"))]
373    pub fn with_default_caching(
374        self,
375    ) -> ProviderBuilder<Stack<crate::layers::CacheLayer, L>, F, N> {
376        self.with_caching(100)
377    }
378
379    /// Set a default [`BlockId`] for `eth_call` and `eth_estimateGas`.
380    ///
381    /// [`BlockId`]: alloy_eips::BlockId
382    pub fn with_default_block(
383        self,
384        block_id: alloy_eips::BlockId,
385    ) -> ProviderBuilder<Stack<BlockIdLayer, L>, F, N> {
386        self.layer(BlockIdLayer::new(block_id))
387    }
388
389    // --- Build to Provider ---
390
391    /// Finish the layer stack by providing a root [`Provider`], outputting
392    /// the final [`Provider`] type with all stack components.
393    pub fn connect_provider<P>(self, provider: P) -> F::Provider
394    where
395        L: ProviderLayer<P, N>,
396        F: TxFiller<N> + ProviderLayer<L::Provider, N>,
397        P: Provider<N>,
398        N: Network,
399    {
400        let Self { layer, filler, network: PhantomData } = self;
401        let stack = Stack::new(layer, filler);
402        stack.layer(provider)
403    }
404
405    /// Finish the layer stack by providing a root [`RpcClient`], outputting
406    /// the final [`Provider`] type with all stack components.
407    ///
408    /// This is a convenience function for
409    /// `ProviderBuilder::on_provider(RootProvider::new(client))`.
410    pub fn connect_client(self, client: RpcClient) -> F::Provider
411    where
412        L: ProviderLayer<RootProvider<N>, N>,
413        F: TxFiller<N> + ProviderLayer<L::Provider, N>,
414        N: Network,
415    {
416        self.connect_provider(RootProvider::new(client))
417    }
418
419    /// Finish the layer stack by providing a [`RpcClient`] that mocks responses, outputting
420    /// the final [`Provider`] type with all stack components.
421    ///
422    /// This is a convenience function for
423    /// `ProviderBuilder::on_client(RpcClient::mocked(asserter))`.
424    pub fn connect_mocked_client(self, asserter: alloy_transport::mock::Asserter) -> 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_client(RpcClient::mocked(asserter))
431    }
432
433    /// Finish the layer stack by providing a connection string for a built-in
434    /// transport type, outputting the final [`Provider`] type with all stack
435    /// components.
436    #[doc(alias = "on_builtin")]
437    pub async fn connect(self, s: &str) -> Result<F::Provider, TransportError>
438    where
439        L: ProviderLayer<RootProvider<N>, N>,
440        F: TxFiller<N> + ProviderLayer<L::Provider, N>,
441        N: Network,
442    {
443        let client = ClientBuilder::default().connect(s).await?;
444        Ok(self.connect_client(client))
445    }
446
447    /// Finish the layer stack by providing a connection string with custom configuration.
448    ///
449    /// This method allows for fine-grained control over connection settings
450    /// such as authentication, retry behavior, and transport-specific options.
451    /// The transport type is extracted from the connection string and configured
452    /// using the provided [`ConnectionConfig`].
453    ///
454    /// # Examples
455    ///
456    /// ```
457    /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
458    /// use alloy_provider::{ConnectionConfig, ProviderBuilder};
459    /// use alloy_transport::Authorization;
460    /// use std::time::Duration;
461    ///
462    /// let config = ConnectionConfig::new()
463    ///     .with_auth(Authorization::bearer("my-token"))
464    ///     .with_max_retries(3)
465    ///     .with_retry_interval(Duration::from_secs(2));
466    ///
467    /// let provider =
468    ///     ProviderBuilder::new().connect_with_config("ws://localhost:8545", config).await?;
469    /// # Ok(())
470    /// # }
471    /// ```
472    pub async fn connect_with_config(
473        self,
474        s: &str,
475        config: ConnectionConfig,
476    ) -> Result<F::Provider, TransportError>
477    where
478        L: ProviderLayer<RootProvider<N>, N>,
479        F: TxFiller<N> + ProviderLayer<L::Provider, N>,
480        N: Network,
481    {
482        let client = ClientBuilder::default().connect_with_config(s, config).await?;
483        Ok(self.connect_client(client))
484    }
485
486    /// Finish the layer stack by providing a [`TransportConnect`] instance.
487    pub async fn connect_with<C>(self, connect: &C) -> Result<F::Provider, TransportError>
488    where
489        L: ProviderLayer<RootProvider<N>, N>,
490        F: TxFiller<N> + ProviderLayer<L::Provider, N>,
491        N: Network,
492        C: TransportConnect,
493    {
494        connect
495            .get_transport()
496            .await
497            .map(|t| RpcClient::new(t, connect.is_local()))
498            .map(|client| self.connect_client(client))
499    }
500
501    /// Finish the layer stack by providing a [`PubSubConnect`] instance,
502    /// producing a [`Provider`] with pubsub capabilities.
503    ///
504    /// [`PubSubConnect`]: alloy_pubsub::PubSubConnect
505    #[cfg(feature = "pubsub")]
506    pub async fn connect_pubsub_with<C>(self, connect: C) -> Result<F::Provider, TransportError>
507    where
508        L: ProviderLayer<RootProvider<N>, N>,
509        F: TxFiller<N> + ProviderLayer<L::Provider, N>,
510        N: Network,
511        C: alloy_pubsub::PubSubConnect,
512    {
513        ClientBuilder::default().pubsub(connect).await.map(|client| self.connect_client(client))
514    }
515
516    /// Build this provider with a websocket connection.
517    #[cfg(feature = "ws")]
518    pub async fn connect_ws(
519        self,
520        connect: alloy_transport_ws::WsConnect,
521    ) -> Result<F::Provider, TransportError>
522    where
523        L: ProviderLayer<RootProvider<N>, N>,
524        F: TxFiller<N> + ProviderLayer<L::Provider, N>,
525        N: Network,
526    {
527        let client = ClientBuilder::default().ws(connect).await?;
528        Ok(self.connect_client(client))
529    }
530
531    /// Build this provider with an IPC connection.
532    #[cfg(feature = "ipc")]
533    pub async fn connect_ipc<T>(
534        self,
535        connect: alloy_transport_ipc::IpcConnect<T>,
536    ) -> Result<F::Provider, TransportError>
537    where
538        alloy_transport_ipc::IpcConnect<T>: alloy_pubsub::PubSubConnect,
539        L: ProviderLayer<RootProvider<N>, N>,
540        F: TxFiller<N> + ProviderLayer<L::Provider, N>,
541        N: Network,
542    {
543        let client = ClientBuilder::default().ipc(connect).await?;
544        Ok(self.connect_client(client))
545    }
546
547    /// Build this provider with an Reqwest HTTP transport.
548    #[cfg(any(test, feature = "reqwest"))]
549    pub fn connect_http(self, url: reqwest::Url) -> F::Provider
550    where
551        L: ProviderLayer<crate::RootProvider<N>, N>,
552        F: TxFiller<N> + ProviderLayer<L::Provider, N>,
553        N: Network,
554    {
555        let client = ClientBuilder::default().http(url);
556        self.connect_client(client)
557    }
558
559    /// Build this provider with a pre-built Reqwest client.
560    #[cfg(any(test, feature = "reqwest"))]
561    pub fn connect_reqwest<C>(self, client: C, url: reqwest::Url) -> F::Provider
562    where
563        L: ProviderLayer<crate::RootProvider<N>, N>,
564        F: TxFiller<N> + ProviderLayer<L::Provider, N>,
565        N: Network,
566        C: Into<reqwest::Client>,
567    {
568        let client = ClientBuilder::default().http_with_client(client.into(), url);
569        self.connect_client(client)
570    }
571
572    /// Build this provider with a provided Reqwest client builder.
573    #[cfg(any(test, feature = "reqwest"))]
574    pub fn with_reqwest<B>(self, url: reqwest::Url, builder: B) -> F::Provider
575    where
576        L: ProviderLayer<crate::RootProvider<N>, N>,
577        F: TxFiller<N> + ProviderLayer<L::Provider, N>,
578        N: Network,
579        B: FnOnce(reqwest::ClientBuilder) -> reqwest::Client,
580    {
581        self.connect_reqwest(builder(reqwest::ClientBuilder::default()), url)
582    }
583
584    /// Build this provider with an Hyper HTTP transport.
585    #[cfg(feature = "hyper")]
586    pub fn connect_hyper_http(self, url: url::Url) -> F::Provider
587    where
588        L: ProviderLayer<crate::RootProvider<N>, N>,
589        F: TxFiller<N> + ProviderLayer<L::Provider, N>,
590        N: Network,
591    {
592        let client = ClientBuilder::default().hyper_http(url);
593        self.connect_client(client)
594    }
595}
596
597#[cfg(any(test, feature = "anvil-node"))]
598type JoinedEthereumWalletFiller<F> = JoinFill<F, WalletFiller<alloy_network::EthereumWallet>>;
599
600#[cfg(any(test, feature = "anvil-node"))]
601type AnvilProviderResult<T> = Result<T, alloy_node_bindings::NodeError>;
602
603#[cfg(any(test, feature = "anvil-node"))]
604impl<L, F, N: Network> ProviderBuilder<L, F, N> {
605    /// Build this provider with anvil, using the BoxTransport.
606    ///
607    /// This method requires the `anvil-node` feature on `alloy-provider`.
608    /// When using the `alloy` meta-crate, enable `provider-anvil-node`, or
609    /// combine `providers` with `node-bindings`.
610    #[cfg_attr(docsrs, doc(cfg(feature = "anvil-node")))]
611    pub fn connect_anvil(self) -> F::Provider
612    where
613        F: TxFiller<N> + ProviderLayer<L::Provider, N>,
614        L: crate::builder::ProviderLayer<
615            crate::layers::AnvilProvider<crate::provider::RootProvider<N>, N>,
616            N,
617        >,
618    {
619        self.connect_anvil_with_config(std::convert::identity)
620    }
621
622    /// Build this provider with anvil, using the BoxTransport. This
623    /// function configures a wallet backed by anvil keys, and is intended for
624    /// use in tests.
625    ///
626    /// This method requires the `anvil-node` feature on `alloy-provider`.
627    /// When using the `alloy` meta-crate, enable `provider-anvil-node`, or
628    /// combine `providers` with `node-bindings`.
629    #[cfg_attr(docsrs, doc(cfg(feature = "anvil-node")))]
630    pub fn connect_anvil_with_wallet(
631        self,
632    ) -> <JoinedEthereumWalletFiller<F> as ProviderLayer<L::Provider, N>>::Provider
633    where
634        F: TxFiller<N> + ProviderLayer<L::Provider, N>,
635        L: crate::builder::ProviderLayer<
636            crate::layers::AnvilProvider<crate::provider::RootProvider<N>, N>,
637            N,
638        >,
639        alloy_network::EthereumWallet: alloy_network::NetworkWallet<N>,
640    {
641        self.connect_anvil_with_wallet_and_config(std::convert::identity)
642            .expect("failed to build provider")
643    }
644
645    /// Build this provider with anvil, using the BoxTransport. The
646    /// given function is used to configure the anvil instance.
647    ///
648    /// This method requires the `anvil-node` feature on `alloy-provider`.
649    /// When using the `alloy` meta-crate, enable `provider-anvil-node`, or
650    /// combine `providers` with `node-bindings`.
651    #[cfg_attr(docsrs, doc(cfg(feature = "anvil-node")))]
652    pub fn connect_anvil_with_config(
653        self,
654        f: impl FnOnce(alloy_node_bindings::Anvil) -> alloy_node_bindings::Anvil,
655    ) -> F::Provider
656    where
657        F: TxFiller<N> + ProviderLayer<L::Provider, N>,
658        L: crate::builder::ProviderLayer<
659            crate::layers::AnvilProvider<crate::provider::RootProvider<N>, N>,
660            N,
661        >,
662    {
663        let anvil_layer = crate::layers::AnvilLayer::from(f(Default::default()));
664        let url = anvil_layer.endpoint_url();
665
666        let rpc_client = ClientBuilder::default().http(url);
667
668        self.layer(anvil_layer).connect_client(rpc_client)
669    }
670
671    /// Build this provider with anvil, using the BoxTransport. The
672    /// given function is used to configure the anvil instance.
673    ///
674    /// This method requires the `anvil-node` feature on `alloy-provider`.
675    /// When using the `alloy` meta-crate, enable `provider-anvil-node`, or
676    /// combine `providers` with `node-bindings`.
677    #[cfg_attr(docsrs, doc(cfg(feature = "anvil-node")))]
678    #[deprecated(since = "0.12.6", note = "use `connect_anvil_with_config` instead")]
679    pub fn on_anvil_with_config(
680        self,
681        f: impl FnOnce(alloy_node_bindings::Anvil) -> alloy_node_bindings::Anvil,
682    ) -> F::Provider
683    where
684        L: ProviderLayer<crate::layers::AnvilProvider<RootProvider<N>, N>, N>,
685        F: TxFiller<N> + ProviderLayer<L::Provider, N>,
686    {
687        let anvil_layer = crate::layers::AnvilLayer::from(f(Default::default()));
688        let url = anvil_layer.endpoint_url();
689
690        let rpc_client = ClientBuilder::default().http(url);
691
692        self.layer(anvil_layer).connect_client(rpc_client)
693    }
694
695    /// Build this provider with anvil, using the BoxTransport.
696    /// This calls `try_on_anvil_with_wallet_and_config` and panics on error.
697    ///
698    /// This method requires the `anvil-node` feature on `alloy-provider`.
699    /// When using the `alloy` meta-crate, enable `provider-anvil-node`, or
700    /// combine `providers` with `node-bindings`.
701    #[cfg_attr(docsrs, doc(cfg(feature = "anvil-node")))]
702    pub fn connect_anvil_with_wallet_and_config(
703        self,
704        f: impl FnOnce(alloy_node_bindings::Anvil) -> alloy_node_bindings::Anvil,
705    ) -> AnvilProviderResult<
706        <JoinedEthereumWalletFiller<F> as ProviderLayer<L::Provider, N>>::Provider,
707    >
708    where
709        F: TxFiller<N> + ProviderLayer<L::Provider, N>,
710        L: crate::builder::ProviderLayer<
711            crate::layers::AnvilProvider<crate::provider::RootProvider<N>, N>,
712            N,
713        >,
714        alloy_network::EthereumWallet: alloy_network::NetworkWallet<N>,
715    {
716        let anvil_layer = crate::layers::AnvilLayer::from(f(Default::default()));
717        let url = anvil_layer.endpoint_url();
718
719        let wallet = anvil_layer
720            .instance()
721            .wallet()
722            .ok_or(alloy_node_bindings::NodeError::NoKeysAvailable)?;
723
724        let rpc_client = ClientBuilder::default().http(url);
725
726        Ok(self.wallet(wallet).layer(anvil_layer).connect_client(rpc_client))
727    }
728
729    /// Build this provider with anvil, using the BoxTransport.
730    /// This calls `try_on_anvil_with_wallet_and_config` and panics on error.
731    ///
732    /// This method requires the `anvil-node` feature on `alloy-provider`.
733    /// When using the `alloy` meta-crate, enable `provider-anvil-node`, or
734    /// combine `providers` with `node-bindings`.
735    #[cfg_attr(docsrs, doc(cfg(feature = "anvil-node")))]
736    #[deprecated(since = "0.12.6", note = "use `connect_anvil_with_wallet_and_config` instead")]
737    pub fn on_anvil_with_wallet_and_config(
738        self,
739        f: impl FnOnce(alloy_node_bindings::Anvil) -> alloy_node_bindings::Anvil,
740    ) -> AnvilProviderResult<
741        <JoinedEthereumWalletFiller<F> as ProviderLayer<L::Provider, N>>::Provider,
742    >
743    where
744        F: TxFiller<N> + ProviderLayer<L::Provider, N>,
745        L: crate::builder::ProviderLayer<
746            crate::layers::AnvilProvider<crate::provider::RootProvider<N>, N>,
747            N,
748        >,
749        alloy_network::EthereumWallet: alloy_network::NetworkWallet<N>,
750    {
751        let anvil_layer = crate::layers::AnvilLayer::from(f(Default::default()));
752        let url = anvil_layer.endpoint_url();
753
754        let wallet = anvil_layer
755            .instance()
756            .wallet()
757            .ok_or(alloy_node_bindings::NodeError::NoKeysAvailable)?;
758
759        let rpc_client = ClientBuilder::default().http(url);
760
761        Ok(self.wallet(wallet).layer(anvil_layer).connect_client(rpc_client))
762    }
763}
764
765#[cfg(test)]
766mod tests {
767    use super::*;
768    use crate::Provider;
769    use alloy_network::AnyNetwork;
770
771    #[tokio::test]
772    async fn basic() {
773        let provider = ProviderBuilder::new()
774            .with_cached_nonce_management()
775            .with_call_batching()
776            .connect_http("http://localhost:8545".parse().unwrap());
777        let _ = provider.get_account(Default::default());
778        let provider = provider.erased();
779        let _ = provider.get_account(Default::default());
780    }
781
782    #[tokio::test]
783    #[cfg(feature = "reqwest")]
784    async fn test_connect_reqwest() {
785        let provider = ProviderBuilder::new()
786            .with_cached_nonce_management()
787            .with_call_batching()
788            .connect_reqwest(
789                reqwest::Client::new(),
790                reqwest::Url::parse("http://localhost:8545").unwrap(),
791            );
792        let _ = provider.get_account(Default::default());
793        let provider = provider.erased();
794        let _ = provider.get_account(Default::default());
795    }
796
797    #[tokio::test]
798    #[cfg(feature = "reqwest")]
799    async fn test_with_reqwest() {
800        let provider = ProviderBuilder::new()
801            .with_cached_nonce_management()
802            .with_call_batching()
803            .with_reqwest(reqwest::Url::parse("http://localhost:8545").unwrap(), |builder| {
804                builder
805                    .user_agent("alloy/test")
806                    .timeout(std::time::Duration::from_secs(10))
807                    .build()
808                    .expect("failed to build reqwest client")
809            });
810        let _ = provider.get_account(Default::default());
811        let provider = provider.erased();
812        let _ = provider.get_account(Default::default());
813    }
814
815    #[tokio::test]
816    async fn compile_with_network() {
817        let p = ProviderBuilder::new_with_network::<AnyNetwork>().connect_anvil();
818        let num = p.get_block_number().await.unwrap();
819        assert_eq!(num, 0);
820    }
821
822    // Ensures `.network()` replaces fillers rather than keeping the old ones.
823    #[test]
824    fn network_replaces_fillers() {
825        // Add an extra filler before swapping, it should be dropped.
826        let builder = ProviderBuilder::new().filler(GasFiller::default()).network::<AnyNetwork>();
827
828        let _: ProviderBuilder<
829            Identity,
830            JoinFill<Identity, <AnyNetwork as RecommendedFillers>::RecommendedFillers>,
831            AnyNetwork,
832        > = builder;
833    }
834
835    #[tokio::test]
836    async fn network_swap_works_at_runtime() {
837        // Verify that `ProviderBuilder::new().network::<AnyNetwork>()` produces a working provider.
838        let p = ProviderBuilder::new().network::<AnyNetwork>().connect_anvil();
839        let num = p.get_block_number().await.unwrap();
840        assert_eq!(num, 0);
841    }
842}