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> {
147 ProviderBuilder { layer: self.layer, filler: Identity, network: self.network }
148 }
149}
150
151impl<N> Default for ProviderBuilder<Identity, Identity, N> {
152 fn default() -> Self {
153 Self { layer: Identity, filler: Identity, network: PhantomData }
154 }
155}
156
157impl<L, N: Network> ProviderBuilder<L, Identity, N> {
158 pub fn with_recommended_fillers(
161 self,
162 ) -> ProviderBuilder<L, JoinFill<Identity, N::RecommendedFillers>, N>
163 where
164 N: RecommendedFillers,
165 {
166 self.filler(N::recommended_fillers())
167 }
168
169 pub fn with_gas_estimation(self) -> ProviderBuilder<L, JoinFill<Identity, GasFiller>, N> {
173 self.filler(GasFiller)
174 }
175
176 pub fn with_nonce_management<M: NonceManager>(
180 self,
181 nonce_manager: M,
182 ) -> ProviderBuilder<L, JoinFill<Identity, NonceFiller<M>>, N> {
183 self.filler(NonceFiller::new(nonce_manager))
184 }
185
186 pub fn with_simple_nonce_management(
190 self,
191 ) -> ProviderBuilder<L, JoinFill<Identity, NonceFiller>, N> {
192 self.with_nonce_management(SimpleNonceManager::default())
193 }
194
195 pub fn with_cached_nonce_management(
199 self,
200 ) -> ProviderBuilder<L, JoinFill<Identity, NonceFiller<CachedNonceManager>>, N> {
201 self.with_nonce_management(CachedNonceManager::default())
202 }
203
204 pub fn fetch_chain_id(self) -> ProviderBuilder<L, JoinFill<Identity, ChainIdFiller>, N> {
209 self.filler(ChainIdFiller::default())
210 }
211
212 pub fn with_chain_id(
216 self,
217 chain_id: ChainId,
218 ) -> ProviderBuilder<L, JoinFill<Identity, ChainIdFiller>, N> {
219 self.filler(ChainIdFiller::new(Some(chain_id)))
220 }
221}
222
223impl<L, F, N> ProviderBuilder<L, F, N> {
224 pub fn layer<Inner>(self, layer: Inner) -> ProviderBuilder<Stack<Inner, L>, F, N> {
236 ProviderBuilder {
237 layer: Stack::new(layer, self.layer),
238 filler: self.filler,
239 network: PhantomData,
240 }
241 }
242
243 pub fn filler<F2>(self, filler: F2) -> ProviderBuilder<L, JoinFill<F, F2>, N> {
247 ProviderBuilder {
248 layer: self.layer,
249 filler: JoinFill::new(self.filler, filler),
250 network: PhantomData,
251 }
252 }
253
254 pub fn network<Net: Network>(self) -> ProviderBuilder<L, F, Net> {
263 ProviderBuilder { layer: self.layer, filler: self.filler, network: PhantomData }
264 }
265
266 pub fn with_chain(self, chain: NamedChain) -> ProviderBuilder<Stack<ChainLayer, L>, F, N> {
271 self.layer(ChainLayer::new(chain))
272 }
273
274 pub fn on_provider<P>(self, provider: P) -> F::Provider
277 where
278 L: ProviderLayer<P, N>,
279 F: TxFiller<N> + ProviderLayer<L::Provider, N>,
280 P: Provider<N>,
281 N: Network,
282 {
283 let Self { layer, filler, network: PhantomData } = self;
284 let stack = Stack::new(layer, filler);
285 stack.layer(provider)
286 }
287
288 pub fn on_client(self, client: RpcClient) -> F::Provider
294 where
295 L: ProviderLayer<RootProvider<N>, N>,
296 F: TxFiller<N> + ProviderLayer<L::Provider, N>,
297 N: Network,
298 {
299 self.on_provider(RootProvider::new(client))
300 }
301
302 pub fn on_mocked_client(self, asserter: alloy_transport::mock::Asserter) -> F::Provider
308 where
309 L: ProviderLayer<RootProvider<N>, N>,
310 F: TxFiller<N> + ProviderLayer<L::Provider, N>,
311 N: Network,
312 {
313 self.on_client(RpcClient::mocked(asserter))
314 }
315
316 #[doc(alias = "on_builtin")]
320 pub async fn connect(self, s: &str) -> Result<F::Provider, TransportError>
321 where
322 L: ProviderLayer<RootProvider<N>, N>,
323 F: TxFiller<N> + ProviderLayer<L::Provider, N>,
324 N: Network,
325 {
326 let client = ClientBuilder::default().connect(s).await?;
327 Ok(self.on_client(client))
328 }
329
330 #[deprecated = "use `connect` instead"]
334 #[doc(hidden)]
335 pub async fn on_builtin(self, s: &str) -> Result<F::Provider, TransportError>
336 where
337 L: ProviderLayer<RootProvider<N>, N>,
338 F: TxFiller<N> + ProviderLayer<L::Provider, N>,
339 N: Network,
340 {
341 self.connect(s).await
342 }
343
344 #[cfg(feature = "ws")]
346 pub async fn on_ws(
347 self,
348 connect: alloy_transport_ws::WsConnect,
349 ) -> Result<F::Provider, TransportError>
350 where
351 L: ProviderLayer<RootProvider<N>, N>,
352 F: TxFiller<N> + ProviderLayer<L::Provider, N>,
353 N: Network,
354 {
355 let client = ClientBuilder::default().ws(connect).await?;
356 Ok(self.on_client(client))
357 }
358
359 #[cfg(feature = "ipc")]
361 pub async fn on_ipc<T>(
362 self,
363 connect: alloy_transport_ipc::IpcConnect<T>,
364 ) -> Result<F::Provider, TransportError>
365 where
366 alloy_transport_ipc::IpcConnect<T>: alloy_pubsub::PubSubConnect,
367 L: ProviderLayer<RootProvider<N>, N>,
368 F: TxFiller<N> + ProviderLayer<L::Provider, N>,
369 N: Network,
370 {
371 let client = ClientBuilder::default().ipc(connect).await?;
372 Ok(self.on_client(client))
373 }
374
375 #[cfg(any(test, feature = "reqwest"))]
377 pub fn on_http(self, url: reqwest::Url) -> F::Provider
378 where
379 L: ProviderLayer<crate::RootProvider<N>, N>,
380 F: TxFiller<N> + ProviderLayer<L::Provider, N>,
381 N: Network,
382 {
383 let client = ClientBuilder::default().http(url);
384 self.on_client(client)
385 }
386
387 #[cfg(feature = "hyper")]
389 pub fn on_hyper_http(self, url: url::Url) -> F::Provider
390 where
391 L: ProviderLayer<crate::RootProvider<N>, N>,
392 F: TxFiller<N> + ProviderLayer<L::Provider, N>,
393 N: Network,
394 {
395 let client = ClientBuilder::default().hyper_http(url);
396 self.on_client(client)
397 }
398
399 pub fn with_call_batching(self) -> ProviderBuilder<Stack<CallBatchLayer, L>, F, N> {
403 self.layer(CallBatchLayer::new())
404 }
405}
406
407impl<L, F, N: Network> ProviderBuilder<L, F, N> {
408 #[allow(clippy::type_complexity)]
412 pub fn wallet<W: IntoWallet<N>>(
413 self,
414 wallet: W,
415 ) -> ProviderBuilder<L, JoinFill<F, WalletFiller<W::NetworkWallet>>, N> {
416 self.filler(WalletFiller::new(wallet.into_wallet()))
417 }
418}
419
420#[cfg(any(test, feature = "anvil-node"))]
421type JoinedEthereumWalletFiller<F> = JoinFill<F, WalletFiller<alloy_network::EthereumWallet>>;
422
423#[cfg(any(test, feature = "anvil-node"))]
424type AnvilProviderResult<T> = Result<T, alloy_node_bindings::NodeError>;
425
426#[cfg(any(test, feature = "anvil-node"))]
429impl<L, F> ProviderBuilder<L, F, Ethereum> {
430 pub fn on_anvil(self) -> F::Provider
432 where
433 F: TxFiller<Ethereum> + ProviderLayer<L::Provider, Ethereum>,
434 L: crate::builder::ProviderLayer<
435 crate::layers::AnvilProvider<crate::provider::RootProvider>,
436 >,
437 {
438 self.on_anvil_with_config(std::convert::identity)
439 }
440
441 pub fn on_anvil_with_wallet(
445 self,
446 ) -> <JoinedEthereumWalletFiller<F> as ProviderLayer<L::Provider>>::Provider
447 where
448 F: TxFiller<Ethereum> + ProviderLayer<L::Provider, Ethereum>,
449 L: crate::builder::ProviderLayer<
450 crate::layers::AnvilProvider<crate::provider::RootProvider>,
451 >,
452 {
453 self.on_anvil_with_wallet_and_config(std::convert::identity)
454 .expect("failed to build provider")
455 }
456
457 pub fn on_anvil_with_config(
460 self,
461 f: impl FnOnce(alloy_node_bindings::Anvil) -> alloy_node_bindings::Anvil,
462 ) -> F::Provider
463 where
464 F: TxFiller<Ethereum> + ProviderLayer<L::Provider, Ethereum>,
465 L: crate::builder::ProviderLayer<
466 crate::layers::AnvilProvider<crate::provider::RootProvider>,
467 >,
468 {
469 let anvil_layer = crate::layers::AnvilLayer::from(f(Default::default()));
470 let url = anvil_layer.endpoint_url();
471
472 let rpc_client = ClientBuilder::default().http(url);
473
474 self.layer(anvil_layer).on_client(rpc_client)
475 }
476
477 pub fn on_anvil_with_wallet_and_config(
480 self,
481 f: impl FnOnce(alloy_node_bindings::Anvil) -> alloy_node_bindings::Anvil,
482 ) -> AnvilProviderResult<<JoinedEthereumWalletFiller<F> as ProviderLayer<L::Provider>>::Provider>
483 where
484 F: TxFiller<Ethereum> + ProviderLayer<L::Provider, Ethereum>,
485 L: crate::builder::ProviderLayer<
486 crate::layers::AnvilProvider<crate::provider::RootProvider>,
487 >,
488 {
489 let anvil_layer = crate::layers::AnvilLayer::from(f(Default::default()));
490 let url = anvil_layer.endpoint_url();
491
492 let wallet = anvil_layer
493 .instance()
494 .wallet()
495 .ok_or(alloy_node_bindings::NodeError::NoKeysAvailable)?;
496
497 let rpc_client = ClientBuilder::default().http(url);
498
499 Ok(self.wallet(wallet).layer(anvil_layer).on_client(rpc_client))
500 }
501}
502
503