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