1use crate::{
2 fillers::{
3 CachedNonceManager, ChainIdFiller, FillerControlFlow, GasFiller, JoinFill, NonceFiller,
4 NonceManager, RecommendedFillers, SimpleNonceManager, TxFiller, WalletFiller,
5 },
6 layers::{CallBatchLayer, ChainLayer},
7 provider::SendableTx,
8 Provider, RootProvider,
9};
10use alloy_chains::NamedChain;
11use alloy_network::{Ethereum, IntoWallet, Network};
12use alloy_primitives::ChainId;
13use alloy_rpc_client::{ClientBuilder, RpcClient};
14use alloy_transport::{TransportError, TransportResult};
15use std::marker::PhantomData;
16
17pub trait ProviderLayer<P: Provider<N>, N: Network = Ethereum> {
21 type Provider: Provider<N>;
23
24 fn layer(&self, inner: P) -> Self::Provider;
26}
27
28#[derive(Clone, Copy, Debug)]
30pub struct Identity;
31
32impl<N> TxFiller<N> for Identity
33where
34 N: Network,
35{
36 type Fillable = ();
37
38 fn status(&self, _tx: &<N as Network>::TransactionRequest) -> FillerControlFlow {
39 FillerControlFlow::Finished
40 }
41
42 fn fill_sync(&self, _tx: &mut SendableTx<N>) {}
43
44 async fn prepare<P>(
45 &self,
46 _provider: &P,
47 _tx: &N::TransactionRequest,
48 ) -> TransportResult<Self::Fillable> {
49 Ok(())
50 }
51
52 async fn fill(
53 &self,
54 _to_fill: Self::Fillable,
55 tx: SendableTx<N>,
56 ) -> TransportResult<SendableTx<N>> {
57 Ok(tx)
58 }
59}
60
61impl<P, N> ProviderLayer<P, N> for Identity
62where
63 N: Network,
64 P: Provider<N>,
65{
66 type Provider = P;
67
68 fn layer(&self, inner: P) -> Self::Provider {
69 inner
70 }
71}
72
73#[derive(Debug)]
75pub struct Stack<Inner, Outer> {
76 inner: Inner,
77 outer: Outer,
78}
79
80impl<Inner, Outer> Stack<Inner, Outer> {
81 pub const fn new(inner: Inner, outer: Outer) -> Self {
83 Self { inner, outer }
84 }
85}
86
87impl<P, N, Inner, Outer> ProviderLayer<P, N> for Stack<Inner, Outer>
88where
89 N: Network,
90 P: Provider<N>,
91 Inner: ProviderLayer<P, N>,
92 Outer: ProviderLayer<Inner::Provider, N>,
93{
94 type Provider = Outer::Provider;
95
96 fn layer(&self, provider: P) -> Self::Provider {
97 let inner = self.inner.layer(provider);
98
99 self.outer.layer(inner)
100 }
101}
102
103#[derive(Debug)]
117pub struct ProviderBuilder<L, F, N = Ethereum> {
118 layer: L,
119 filler: F,
120 network: PhantomData<fn() -> N>,
121}
122
123impl
124 ProviderBuilder<
125 Identity,
126 JoinFill<Identity, <Ethereum as RecommendedFillers>::RecommendedFillers>,
127 Ethereum,
128 >
129{
130 pub fn new() -> Self {
140 ProviderBuilder::default().with_recommended_fillers()
141 }
142
143 pub fn disable_recommended_fillers(self) -> ProviderBuilder<Identity, Identity, Ethereum> {
148 ProviderBuilder { layer: self.layer, filler: Identity, network: self.network }
149 }
150}
151
152impl<N> Default for ProviderBuilder<Identity, Identity, N> {
153 fn default() -> Self {
154 Self { layer: Identity, filler: Identity, network: PhantomData }
155 }
156}
157
158impl ProviderBuilder<Identity, Identity, Ethereum> {
159 pub fn new_with_network<Net: RecommendedFillers>(
162 ) -> ProviderBuilder<Identity, JoinFill<Identity, Net::RecommendedFillers>, Net> {
163 ProviderBuilder {
164 layer: Identity,
165 filler: JoinFill::new(Identity, Net::recommended_fillers()),
166 network: PhantomData,
167 }
168 }
169}
170
171impl<L, N: Network> ProviderBuilder<L, Identity, N> {
172 pub fn with_recommended_fillers(
175 self,
176 ) -> ProviderBuilder<L, JoinFill<Identity, N::RecommendedFillers>, N>
177 where
178 N: RecommendedFillers,
179 {
180 self.filler(N::recommended_fillers())
181 }
182}
183
184impl<L, F, N> ProviderBuilder<L, F, N> {
185 pub fn layer<Inner>(self, layer: Inner) -> ProviderBuilder<Stack<Inner, L>, F, N> {
197 ProviderBuilder {
198 layer: Stack::new(layer, self.layer),
199 filler: self.filler,
200 network: PhantomData,
201 }
202 }
203
204 pub fn filler<F2>(self, filler: F2) -> ProviderBuilder<L, JoinFill<F, F2>, N> {
208 ProviderBuilder {
209 layer: self.layer,
210 filler: JoinFill::new(self.filler, filler),
211 network: PhantomData,
212 }
213 }
214
215 pub fn network<Net: Network>(self) -> ProviderBuilder<L, F, Net> {
224 ProviderBuilder { layer: self.layer, filler: self.filler, network: PhantomData }
225 }
226
227 pub fn with_chain(self, chain: NamedChain) -> ProviderBuilder<Stack<ChainLayer, L>, F, N> {
232 self.layer(ChainLayer::new(chain))
233 }
234
235 pub fn with_gas_estimation(self) -> ProviderBuilder<L, JoinFill<F, GasFiller>, N> {
241 self.filler(GasFiller)
242 }
243
244 pub fn with_nonce_management<M: NonceManager>(
248 self,
249 nonce_manager: M,
250 ) -> ProviderBuilder<L, JoinFill<F, NonceFiller<M>>, N> {
251 self.filler(NonceFiller::new(nonce_manager))
252 }
253
254 pub fn with_simple_nonce_management(
258 self,
259 ) -> ProviderBuilder<L, JoinFill<F, NonceFiller<SimpleNonceManager>>, N> {
260 self.with_nonce_management(SimpleNonceManager::default())
261 }
262
263 pub fn with_cached_nonce_management(
267 self,
268 ) -> ProviderBuilder<L, JoinFill<F, NonceFiller<CachedNonceManager>>, N> {
269 self.with_nonce_management(CachedNonceManager::default())
270 }
271
272 pub fn fetch_chain_id(self) -> ProviderBuilder<L, JoinFill<F, ChainIdFiller>, N> {
277 self.filler(ChainIdFiller::default())
278 }
279
280 pub fn with_chain_id(
284 self,
285 chain_id: ChainId,
286 ) -> ProviderBuilder<L, JoinFill<F, ChainIdFiller>, N> {
287 self.filler(ChainIdFiller::new(Some(chain_id)))
288 }
289
290 pub fn wallet<W: IntoWallet<N>>(
294 self,
295 wallet: W,
296 ) -> ProviderBuilder<L, JoinFill<F, WalletFiller<W::NetworkWallet>>, N>
297 where
298 N: Network,
299 {
300 self.filler(WalletFiller::new(wallet.into_wallet()))
301 }
302
303 pub fn with_call_batching(self) -> ProviderBuilder<Stack<CallBatchLayer, L>, F, N> {
309 self.layer(CallBatchLayer::new())
310 }
311
312 pub fn connect_provider<P>(self, provider: P) -> F::Provider
317 where
318 L: ProviderLayer<P, N>,
319 F: TxFiller<N> + ProviderLayer<L::Provider, N>,
320 P: Provider<N>,
321 N: Network,
322 {
323 let Self { layer, filler, network: PhantomData } = self;
324 let stack = Stack::new(layer, filler);
325 stack.layer(provider)
326 }
327
328 #[deprecated(since = "0.12.6", note = "use `connect_provider` instead")]
331 pub fn on_provider<P>(self, provider: P) -> F::Provider
332 where
333 L: ProviderLayer<P, N>,
334 F: TxFiller<N> + ProviderLayer<L::Provider, N>,
335 P: Provider<N>,
336 N: Network,
337 {
338 let Self { layer, filler, network: PhantomData } = self;
339 let stack = Stack::new(layer, filler);
340 stack.layer(provider)
341 }
342
343 pub fn connect_client(self, client: RpcClient) -> F::Provider
349 where
350 L: ProviderLayer<RootProvider<N>, N>,
351 F: TxFiller<N> + ProviderLayer<L::Provider, N>,
352 N: Network,
353 {
354 self.connect_provider(RootProvider::new(client))
355 }
356
357 #[deprecated(since = "0.12.6", note = "use `connect_client` instead")]
363 pub fn on_client(self, client: RpcClient) -> F::Provider
364 where
365 L: ProviderLayer<RootProvider<N>, N>,
366 F: TxFiller<N> + ProviderLayer<L::Provider, N>,
367 N: Network,
368 {
369 self.connect_provider(RootProvider::new(client))
370 }
371
372 pub fn connect_mocked_client(self, asserter: alloy_transport::mock::Asserter) -> F::Provider
378 where
379 L: ProviderLayer<RootProvider<N>, N>,
380 F: TxFiller<N> + ProviderLayer<L::Provider, N>,
381 N: Network,
382 {
383 self.connect_client(RpcClient::mocked(asserter))
384 }
385
386 #[deprecated(since = "0.12.6", note = "use `connect_mocked_client` instead")]
392 pub fn on_mocked_client(self, asserter: alloy_transport::mock::Asserter) -> F::Provider
393 where
394 L: ProviderLayer<RootProvider<N>, N>,
395 F: TxFiller<N> + ProviderLayer<L::Provider, N>,
396 N: Network,
397 {
398 self.connect_client(RpcClient::mocked(asserter))
399 }
400
401 #[doc(alias = "on_builtin")]
405 pub async fn connect(self, s: &str) -> Result<F::Provider, TransportError>
406 where
407 L: ProviderLayer<RootProvider<N>, N>,
408 F: TxFiller<N> + ProviderLayer<L::Provider, N>,
409 N: Network,
410 {
411 let client = ClientBuilder::default().connect(s).await?;
412 Ok(self.connect_client(client))
413 }
414
415 #[deprecated = "use `connect` instead"]
419 #[doc(hidden)]
420 pub async fn on_builtin(self, s: &str) -> Result<F::Provider, TransportError>
421 where
422 L: ProviderLayer<RootProvider<N>, N>,
423 F: TxFiller<N> + ProviderLayer<L::Provider, N>,
424 N: Network,
425 {
426 self.connect(s).await
427 }
428
429 #[cfg(feature = "ws")]
431 pub async fn connect_ws(
432 self,
433 connect: alloy_transport_ws::WsConnect,
434 ) -> Result<F::Provider, TransportError>
435 where
436 L: ProviderLayer<RootProvider<N>, N>,
437 F: TxFiller<N> + ProviderLayer<L::Provider, N>,
438 N: Network,
439 {
440 let client = ClientBuilder::default().ws(connect).await?;
441 Ok(self.connect_client(client))
442 }
443
444 #[cfg(feature = "ws")]
446 #[deprecated(since = "0.12.6", note = "use `connect_ws` instead")]
447 pub async fn on_ws(
448 self,
449 connect: alloy_transport_ws::WsConnect,
450 ) -> Result<F::Provider, TransportError>
451 where
452 L: ProviderLayer<RootProvider<N>, N>,
453 F: TxFiller<N> + ProviderLayer<L::Provider, N>,
454 N: Network,
455 {
456 let client = ClientBuilder::default().ws(connect).await?;
457 Ok(self.connect_client(client))
458 }
459
460 #[cfg(feature = "ipc")]
462 pub async fn connect_ipc<T>(
463 self,
464 connect: alloy_transport_ipc::IpcConnect<T>,
465 ) -> Result<F::Provider, TransportError>
466 where
467 alloy_transport_ipc::IpcConnect<T>: alloy_pubsub::PubSubConnect,
468 L: ProviderLayer<RootProvider<N>, N>,
469 F: TxFiller<N> + ProviderLayer<L::Provider, N>,
470 N: Network,
471 {
472 let client = ClientBuilder::default().ipc(connect).await?;
473 Ok(self.connect_client(client))
474 }
475
476 #[cfg(feature = "ipc")]
478 #[deprecated(since = "0.12.6", note = "use `connect_ipc` instead")]
479 pub async fn on_ipc<T>(
480 self,
481 connect: alloy_transport_ipc::IpcConnect<T>,
482 ) -> Result<F::Provider, TransportError>
483 where
484 alloy_transport_ipc::IpcConnect<T>: alloy_pubsub::PubSubConnect,
485 L: ProviderLayer<RootProvider<N>, N>,
486 F: TxFiller<N> + ProviderLayer<L::Provider, N>,
487 N: Network,
488 {
489 let client = ClientBuilder::default().ipc(connect).await?;
490 Ok(self.connect_client(client))
491 }
492
493 #[cfg(any(test, feature = "reqwest"))]
495 pub fn connect_http(self, url: reqwest::Url) -> F::Provider
496 where
497 L: ProviderLayer<crate::RootProvider<N>, N>,
498 F: TxFiller<N> + ProviderLayer<L::Provider, N>,
499 N: Network,
500 {
501 let client = ClientBuilder::default().http(url);
502 self.connect_client(client)
503 }
504
505 #[cfg(any(test, feature = "reqwest"))]
507 #[deprecated(since = "0.12.6", note = "use `connect_http` instead")]
508 pub fn on_http(self, url: reqwest::Url) -> F::Provider
509 where
510 L: ProviderLayer<RootProvider<N>, N>,
511 F: TxFiller<N> + ProviderLayer<L::Provider, N>,
512 N: Network,
513 {
514 let client = ClientBuilder::default().http(url);
515 self.connect_client(client)
516 }
517
518 #[cfg(feature = "hyper")]
520 pub fn connect_hyper_http(self, url: url::Url) -> F::Provider
521 where
522 L: ProviderLayer<crate::RootProvider<N>, N>,
523 F: TxFiller<N> + ProviderLayer<L::Provider, N>,
524 N: Network,
525 {
526 let client = ClientBuilder::default().hyper_http(url);
527 self.connect_client(client)
528 }
529
530 #[cfg(feature = "hyper")]
532 #[deprecated(since = "0.12.6", note = "use `connect_hyper_http` instead")]
533 pub fn on_hyper_http(self, url: url::Url) -> F::Provider
534 where
535 L: ProviderLayer<RootProvider<N>, N>,
536 F: TxFiller<N> + ProviderLayer<L::Provider, N>,
537 N: Network,
538 {
539 let client = ClientBuilder::default().hyper_http(url);
540 self.connect_client(client)
541 }
542}
543
544#[cfg(any(test, feature = "anvil-node"))]
545type JoinedEthereumWalletFiller<F> = JoinFill<F, WalletFiller<alloy_network::EthereumWallet>>;
546
547#[cfg(any(test, feature = "anvil-node"))]
548type AnvilProviderResult<T> = Result<T, alloy_node_bindings::NodeError>;
549
550#[cfg(any(test, feature = "anvil-node"))]
551impl<L, F, N: Network> ProviderBuilder<L, F, N> {
552 pub fn connect_anvil(self) -> F::Provider
554 where
555 F: TxFiller<N> + ProviderLayer<L::Provider, N>,
556 L: crate::builder::ProviderLayer<
557 crate::layers::AnvilProvider<crate::provider::RootProvider<N>, N>,
558 N,
559 >,
560 {
561 self.connect_anvil_with_config(std::convert::identity)
562 }
563
564 #[deprecated(since = "0.12.6", note = "use `connect_anvil` instead")]
566 pub fn on_anvil(self) -> F::Provider
567 where
568 L: ProviderLayer<crate::layers::AnvilProvider<RootProvider<N>, N>, N>,
569 F: TxFiller<N> + ProviderLayer<L::Provider, N>,
570 {
571 self.connect_anvil_with_config(std::convert::identity)
572 }
573
574 pub fn connect_anvil_with_wallet(
578 self,
579 ) -> <JoinedEthereumWalletFiller<F> as ProviderLayer<L::Provider, N>>::Provider
580 where
581 F: TxFiller<N> + ProviderLayer<L::Provider, N>,
582 L: crate::builder::ProviderLayer<
583 crate::layers::AnvilProvider<crate::provider::RootProvider<N>, N>,
584 N,
585 >,
586 alloy_network::EthereumWallet: alloy_network::NetworkWallet<N>,
587 {
588 self.connect_anvil_with_wallet_and_config(std::convert::identity)
589 .expect("failed to build provider")
590 }
591
592 #[deprecated(since = "0.12.6", note = "use `connect_anvil_with_wallet` instead")]
596 pub fn on_anvil_with_wallet(
597 self,
598 ) -> <JoinedEthereumWalletFiller<F> as ProviderLayer<L::Provider, N>>::Provider
599 where
600 F: TxFiller<N> + ProviderLayer<L::Provider, N>,
601 L: crate::builder::ProviderLayer<
602 crate::layers::AnvilProvider<crate::provider::RootProvider<N>, N>,
603 N,
604 >,
605 alloy_network::EthereumWallet: alloy_network::NetworkWallet<N>,
606 {
607 self.connect_anvil_with_wallet_and_config(std::convert::identity)
608 .expect("failed to build provider")
609 }
610
611 pub fn connect_anvil_with_config(
614 self,
615 f: impl FnOnce(alloy_node_bindings::Anvil) -> alloy_node_bindings::Anvil,
616 ) -> F::Provider
617 where
618 F: TxFiller<N> + ProviderLayer<L::Provider, N>,
619 L: crate::builder::ProviderLayer<
620 crate::layers::AnvilProvider<crate::provider::RootProvider<N>, N>,
621 N,
622 >,
623 {
624 let anvil_layer = crate::layers::AnvilLayer::from(f(Default::default()));
625 let url = anvil_layer.endpoint_url();
626
627 let rpc_client = ClientBuilder::default().http(url);
628
629 self.layer(anvil_layer).connect_client(rpc_client)
630 }
631
632 #[deprecated(since = "0.12.6", note = "use `connect_anvil_with_config` instead")]
635 pub fn on_anvil_with_config(
636 self,
637 f: impl FnOnce(alloy_node_bindings::Anvil) -> alloy_node_bindings::Anvil,
638 ) -> F::Provider
639 where
640 L: ProviderLayer<crate::layers::AnvilProvider<RootProvider<N>, N>, N>,
641 F: TxFiller<N> + ProviderLayer<L::Provider, N>,
642 {
643 let anvil_layer = crate::layers::AnvilLayer::from(f(Default::default()));
644 let url = anvil_layer.endpoint_url();
645
646 let rpc_client = ClientBuilder::default().http(url);
647
648 self.layer(anvil_layer).connect_client(rpc_client)
649 }
650
651 pub fn connect_anvil_with_wallet_and_config(
654 self,
655 f: impl FnOnce(alloy_node_bindings::Anvil) -> alloy_node_bindings::Anvil,
656 ) -> AnvilProviderResult<
657 <JoinedEthereumWalletFiller<F> as ProviderLayer<L::Provider, N>>::Provider,
658 >
659 where
660 F: TxFiller<N> + ProviderLayer<L::Provider, N>,
661 L: crate::builder::ProviderLayer<
662 crate::layers::AnvilProvider<crate::provider::RootProvider<N>, N>,
663 N,
664 >,
665 alloy_network::EthereumWallet: alloy_network::NetworkWallet<N>,
666 {
667 let anvil_layer = crate::layers::AnvilLayer::from(f(Default::default()));
668 let url = anvil_layer.endpoint_url();
669
670 let wallet = anvil_layer
671 .instance()
672 .wallet()
673 .ok_or(alloy_node_bindings::NodeError::NoKeysAvailable)?;
674
675 let rpc_client = ClientBuilder::default().http(url);
676
677 Ok(self.wallet(wallet).layer(anvil_layer).connect_client(rpc_client))
678 }
679
680 #[deprecated(since = "0.12.6", note = "use `connect_anvil_with_wallet_and_config` instead")]
683 pub fn on_anvil_with_wallet_and_config(
684 self,
685 f: impl FnOnce(alloy_node_bindings::Anvil) -> alloy_node_bindings::Anvil,
686 ) -> AnvilProviderResult<
687 <JoinedEthereumWalletFiller<F> as ProviderLayer<L::Provider, N>>::Provider,
688 >
689 where
690 F: TxFiller<N> + ProviderLayer<L::Provider, N>,
691 L: crate::builder::ProviderLayer<
692 crate::layers::AnvilProvider<crate::provider::RootProvider<N>, N>,
693 N,
694 >,
695 alloy_network::EthereumWallet: alloy_network::NetworkWallet<N>,
696 {
697 let anvil_layer = crate::layers::AnvilLayer::from(f(Default::default()));
698 let url = anvil_layer.endpoint_url();
699
700 let wallet = anvil_layer
701 .instance()
702 .wallet()
703 .ok_or(alloy_node_bindings::NodeError::NoKeysAvailable)?;
704
705 let rpc_client = ClientBuilder::default().http(url);
706
707 Ok(self.wallet(wallet).layer(anvil_layer).connect_client(rpc_client))
708 }
709}
710
711#[cfg(test)]
712mod tests {
713 use super::*;
714 use crate::Provider;
715 use alloy_network::AnyNetwork;
716
717 #[tokio::test]
718 async fn basic() {
719 let provider = ProviderBuilder::new()
720 .with_cached_nonce_management()
721 .with_call_batching()
722 .connect_http("http://localhost:8545".parse().unwrap());
723 let _ = provider.get_account(Default::default());
724 let provider = provider.erased();
725 let _ = provider.get_account(Default::default());
726 }
727
728 #[tokio::test]
729 async fn compile_with_network() {
730 let p = ProviderBuilder::new_with_network::<AnyNetwork>().connect_anvil();
731 let num = p.get_block_number().await.unwrap();
732 assert_eq!(num, 0);
733 }
734}