1#[cfg(all(not(target_arch = "wasm32"), any(test, feature = "reqwest", feature = "hyper")))]
2use crate::layers::{Asserter, MockLayer, MockProvider};
3use crate::{
4 fillers::{
5 CachedNonceManager, ChainIdFiller, FillerControlFlow, GasFiller, JoinFill, NonceFiller,
6 NonceManager, RecommendedFillers, SimpleNonceManager, TxFiller, WalletFiller,
7 },
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, RpcClient};
15use alloy_transport::{TransportError, TransportResult};
16use std::marker::PhantomData;
17
18pub trait ProviderLayer<P: Provider<N>, N: Network = Ethereum> {
22 type Provider: Provider<N>;
24
25 fn layer(&self, inner: P) -> Self::Provider;
27}
28
29#[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#[derive(Debug)]
76pub struct Stack<Inner, Outer> {
77 inner: Inner,
78 outer: Outer,
79}
80
81impl<Inner, Outer> Stack<Inner, Outer> {
82 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#[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, <alloy_network::Ethereum as RecommendedFillers>::RecommendedFillers>,
128 Ethereum,
129 >
130{
131 pub fn new() -> Self {
141 ProviderBuilder::default().with_recommended_fillers()
142 }
143
144 pub fn disable_recommended_fillers(self) -> ProviderBuilder<Identity, Identity, Ethereum> {
148 ProviderBuilder { layer: self.layer, filler: Identity, network: self.network }
149 }
150
151 #[cfg(all(not(target_arch = "wasm32"), any(test, feature = "reqwest", feature = "hyper")))]
155 pub fn mocked() -> MockProvider<RootProvider, Ethereum> {
156 Self::mocked_network()
157 }
158
159 #[cfg(all(not(target_arch = "wasm32"), any(test, feature = "reqwest", feature = "hyper")))]
163 pub fn mocked_network<Net: Network>() -> MockProvider<RootProvider<Net>, Net> {
164 let asserter = Asserter::new();
165 let layer = MockLayer::new(asserter);
166
167 let builder = ProviderBuilder::<_, _, Net>::default().layer(layer);
168
169 #[cfg(any(test, feature = "reqwest"))]
170 let mock_provider = builder.on_http("http://localhost:8545".parse().unwrap());
171
172 #[cfg(all(feature = "hyper", not(feature = "reqwest")))]
173 let mock_provider = builder.on_hyper_http("http://localhost:8545".parse().unwrap());
174
175 mock_provider
176 }
177}
178
179impl<N> Default for ProviderBuilder<Identity, Identity, N> {
180 fn default() -> Self {
181 Self { layer: Identity, filler: Identity, network: PhantomData }
182 }
183}
184
185impl<L, N: Network> ProviderBuilder<L, Identity, N> {
186 pub fn with_recommended_fillers(
189 self,
190 ) -> ProviderBuilder<L, JoinFill<Identity, N::RecommendedFillers>, N>
191 where
192 N: RecommendedFillers,
193 {
194 self.filler(N::recommended_fillers())
195 }
196
197 pub fn with_gas_estimation(self) -> ProviderBuilder<L, JoinFill<Identity, GasFiller>, N> {
201 self.filler(GasFiller)
202 }
203
204 pub fn with_nonce_management<M: NonceManager>(
208 self,
209 nonce_manager: M,
210 ) -> ProviderBuilder<L, JoinFill<Identity, NonceFiller<M>>, N> {
211 self.filler(NonceFiller::new(nonce_manager))
212 }
213
214 pub fn with_simple_nonce_management(
218 self,
219 ) -> ProviderBuilder<L, JoinFill<Identity, NonceFiller>, N> {
220 self.with_nonce_management(SimpleNonceManager::default())
221 }
222
223 pub fn with_cached_nonce_management(
227 self,
228 ) -> ProviderBuilder<L, JoinFill<Identity, NonceFiller<CachedNonceManager>>, N> {
229 self.with_nonce_management(CachedNonceManager::default())
230 }
231
232 pub fn fetch_chain_id(self) -> ProviderBuilder<L, JoinFill<Identity, ChainIdFiller>, N> {
237 self.filler(ChainIdFiller::default())
238 }
239
240 pub fn with_chain_id(
244 self,
245 chain_id: ChainId,
246 ) -> ProviderBuilder<L, JoinFill<Identity, ChainIdFiller>, N> {
247 self.filler(ChainIdFiller::new(Some(chain_id)))
248 }
249}
250
251impl<L, F, N> ProviderBuilder<L, F, N> {
252 pub fn layer<Inner>(self, layer: Inner) -> ProviderBuilder<Stack<Inner, L>, F, N> {
264 ProviderBuilder {
265 layer: Stack::new(layer, self.layer),
266 filler: self.filler,
267 network: PhantomData,
268 }
269 }
270
271 pub fn filler<F2>(self, filler: F2) -> ProviderBuilder<L, JoinFill<F, F2>, N> {
275 ProviderBuilder {
276 layer: self.layer,
277 filler: JoinFill::new(self.filler, filler),
278 network: PhantomData,
279 }
280 }
281
282 pub fn network<Net: Network>(self) -> ProviderBuilder<L, F, Net> {
291 ProviderBuilder { layer: self.layer, filler: self.filler, network: PhantomData }
292 }
293
294 pub fn with_chain(
299 self,
300 chain: NamedChain,
301 ) -> ProviderBuilder<Stack<crate::layers::ChainLayer, L>, F, N> {
302 let chain_layer = crate::layers::ChainLayer::from(chain);
303 self.layer(chain_layer)
304 }
305
306 pub fn on_provider<P>(self, provider: P) -> F::Provider
309 where
310 L: ProviderLayer<P, N>,
311 F: TxFiller<N> + ProviderLayer<L::Provider, N>,
312 P: Provider<N>,
313 N: Network,
314 {
315 let Self { layer, filler, network: PhantomData } = self;
316 let stack = Stack::new(layer, filler);
317 stack.layer(provider)
318 }
319
320 pub fn on_client(self, client: RpcClient) -> F::Provider
326 where
327 L: ProviderLayer<RootProvider<N>, N>,
328 F: TxFiller<N> + ProviderLayer<L::Provider, N>,
329 N: Network,
330 {
331 self.on_provider(RootProvider::new(client))
332 }
333
334 #[doc(alias = "on_builtin")]
338 pub async fn connect(self, s: &str) -> Result<F::Provider, TransportError>
339 where
340 L: ProviderLayer<RootProvider<N>, N>,
341 F: TxFiller<N> + ProviderLayer<L::Provider, N>,
342 N: Network,
343 {
344 let client = ClientBuilder::default().connect(s).await?;
345 Ok(self.on_client(client))
346 }
347
348 #[deprecated = "use `connect` instead"]
352 #[doc(hidden)]
353 pub async fn on_builtin(self, s: &str) -> Result<F::Provider, TransportError>
354 where
355 L: ProviderLayer<RootProvider<N>, N>,
356 F: TxFiller<N> + ProviderLayer<L::Provider, N>,
357 N: Network,
358 {
359 self.connect(s).await
360 }
361
362 #[cfg(feature = "ws")]
364 pub async fn on_ws(
365 self,
366 connect: alloy_transport_ws::WsConnect,
367 ) -> Result<F::Provider, TransportError>
368 where
369 L: ProviderLayer<RootProvider<N>, N>,
370 F: TxFiller<N> + ProviderLayer<L::Provider, N>,
371 N: Network,
372 {
373 let client = ClientBuilder::default().ws(connect).await?;
374 Ok(self.on_client(client))
375 }
376
377 #[cfg(feature = "ipc")]
379 pub async fn on_ipc<T>(
380 self,
381 connect: alloy_transport_ipc::IpcConnect<T>,
382 ) -> Result<F::Provider, TransportError>
383 where
384 alloy_transport_ipc::IpcConnect<T>: alloy_pubsub::PubSubConnect,
385 L: ProviderLayer<RootProvider<N>, N>,
386 F: TxFiller<N> + ProviderLayer<L::Provider, N>,
387 N: Network,
388 {
389 let client = ClientBuilder::default().ipc(connect).await?;
390 Ok(self.on_client(client))
391 }
392
393 #[cfg(any(test, feature = "reqwest"))]
395 pub fn on_http(self, url: reqwest::Url) -> F::Provider
396 where
397 L: ProviderLayer<crate::RootProvider<N>, N>,
398 F: TxFiller<N> + ProviderLayer<L::Provider, N>,
399 N: Network,
400 {
401 let client = ClientBuilder::default().http(url);
402 self.on_client(client)
403 }
404
405 #[cfg(feature = "hyper")]
407 pub fn on_hyper_http(self, url: url::Url) -> F::Provider
408 where
409 L: ProviderLayer<crate::RootProvider<N>, N>,
410 F: TxFiller<N> + ProviderLayer<L::Provider, N>,
411 N: Network,
412 {
413 let client = ClientBuilder::default().hyper_http(url);
414 self.on_client(client)
415 }
416}
417
418impl<L, F, N: Network> ProviderBuilder<L, F, N> {
419 #[allow(clippy::type_complexity)]
423 pub fn wallet<W: IntoWallet<N>>(
424 self,
425 wallet: W,
426 ) -> ProviderBuilder<L, JoinFill<F, WalletFiller<W::NetworkWallet>>, N> {
427 self.filler(WalletFiller::new(wallet.into_wallet()))
428 }
429}
430
431#[cfg(any(test, feature = "anvil-node"))]
432type JoinedEthereumWalletFiller<F> = JoinFill<F, WalletFiller<alloy_network::EthereumWallet>>;
433
434#[cfg(any(test, feature = "anvil-node"))]
435type AnvilProviderResult<T> = Result<T, alloy_node_bindings::NodeError>;
436
437#[cfg(any(test, feature = "anvil-node"))]
440impl<L, F> ProviderBuilder<L, F, Ethereum> {
441 pub fn on_anvil(self) -> F::Provider
443 where
444 F: TxFiller<Ethereum> + ProviderLayer<L::Provider, Ethereum>,
445 L: crate::builder::ProviderLayer<
446 crate::layers::AnvilProvider<crate::provider::RootProvider>,
447 >,
448 {
449 self.on_anvil_with_config(std::convert::identity)
450 }
451
452 pub fn on_anvil_with_wallet(
456 self,
457 ) -> <JoinedEthereumWalletFiller<F> as ProviderLayer<L::Provider>>::Provider
458 where
459 F: TxFiller<Ethereum> + ProviderLayer<L::Provider, Ethereum>,
460 L: crate::builder::ProviderLayer<
461 crate::layers::AnvilProvider<crate::provider::RootProvider>,
462 >,
463 {
464 self.on_anvil_with_wallet_and_config(std::convert::identity)
465 .expect("failed to build provider")
466 }
467
468 pub fn on_anvil_with_config(
471 self,
472 f: impl FnOnce(alloy_node_bindings::Anvil) -> alloy_node_bindings::Anvil,
473 ) -> F::Provider
474 where
475 F: TxFiller<Ethereum> + ProviderLayer<L::Provider, Ethereum>,
476 L: crate::builder::ProviderLayer<
477 crate::layers::AnvilProvider<crate::provider::RootProvider>,
478 >,
479 {
480 let anvil_layer = crate::layers::AnvilLayer::from(f(Default::default()));
481 let url = anvil_layer.endpoint_url();
482
483 let rpc_client = ClientBuilder::default().http(url);
484
485 self.layer(anvil_layer).on_client(rpc_client)
486 }
487
488 pub fn on_anvil_with_wallet_and_config(
491 self,
492 f: impl FnOnce(alloy_node_bindings::Anvil) -> alloy_node_bindings::Anvil,
493 ) -> AnvilProviderResult<<JoinedEthereumWalletFiller<F> as ProviderLayer<L::Provider>>::Provider>
494 where
495 F: TxFiller<Ethereum> + ProviderLayer<L::Provider, Ethereum>,
496 L: crate::builder::ProviderLayer<
497 crate::layers::AnvilProvider<crate::provider::RootProvider>,
498 >,
499 {
500 let anvil_layer = crate::layers::AnvilLayer::from(f(Default::default()));
501 let url = anvil_layer.endpoint_url();
502
503 let wallet = anvil_layer
504 .instance()
505 .wallet()
506 .ok_or(alloy_node_bindings::NodeError::NoKeysAvailable)?;
507
508 let rpc_client = ClientBuilder::default().http(url);
509
510 Ok(self.wallet(wallet).layer(anvil_layer).on_client(rpc_client))
511 }
512}
513
514