1- use std:: { marker :: PhantomData , time:: Duration } ;
1+ use std:: { pin :: Pin , time:: Duration } ;
22
3- use alloy:: { network:: Network , providers:: Provider } ;
3+ use alloy:: { network:: Network , providers:: RootProvider } ;
44
55use crate :: robust_provider:: {
6- Error , IntoProvider , RobustProvider , subscription:: DEFAULT_RECONNECT_INTERVAL ,
6+ Error , IntoRootProvider , RobustProvider , subscription:: DEFAULT_RECONNECT_INTERVAL ,
77} ;
88
9+ type BoxedProviderFuture < N > = Pin < Box < dyn Future < Output = Result < RootProvider < N > , Error > > + Send > > ;
10+
911// RPC retry and timeout settings
1012/// Default timeout used by `RobustProvider`
1113pub const DEFAULT_CALL_TIMEOUT : Duration = Duration :: from_secs ( 60 ) ;
@@ -16,22 +18,21 @@ pub const DEFAULT_MAX_RETRIES: usize = 3;
1618/// Default base delay between retries.
1719pub const DEFAULT_MIN_DELAY : Duration = Duration :: from_secs ( 1 ) ;
1820
19- #[ derive( Clone ) ]
20- pub struct RobustProviderBuilder < N : Network , P : IntoProvider < N > > {
21+ pub struct RobustProviderBuilder < N : Network , P : IntoRootProvider < N > > {
2122 primary_provider : P ,
22- fallback_providers : Vec < P > ,
23+ fallback_providers : Vec < BoxedProviderFuture < N > > ,
2324 call_timeout : Duration ,
2425 subscription_timeout : Duration ,
2526 max_retries : usize ,
2627 min_delay : Duration ,
2728 reconnect_interval : Duration ,
28- _network : PhantomData < N > ,
2929}
3030
31- impl < N : Network , P : IntoProvider < N > > RobustProviderBuilder < N , P > {
31+ impl < N : Network , P : IntoRootProvider < N > > RobustProviderBuilder < N , P > {
3232 /// Create a new [`RobustProvider`] with default settings.
3333 ///
3434 /// The provided provider is treated as the primary provider.
35+ /// Any type implementing [`IntoRootProvider`] can be used.
3536 #[ must_use]
3637 pub fn new ( provider : P ) -> Self {
3738 Self {
@@ -42,7 +43,6 @@ impl<N: Network, P: IntoProvider<N>> RobustProviderBuilder<N, P> {
4243 max_retries : DEFAULT_MAX_RETRIES ,
4344 min_delay : DEFAULT_MIN_DELAY ,
4445 reconnect_interval : DEFAULT_RECONNECT_INTERVAL ,
45- _network : PhantomData ,
4646 }
4747 }
4848
@@ -58,8 +58,8 @@ impl<N: Network, P: IntoProvider<N>> RobustProviderBuilder<N, P> {
5858 ///
5959 /// Fallback providers are used when the primary provider times out or fails.
6060 #[ must_use]
61- pub fn fallback ( mut self , provider : P ) -> Self {
62- self . fallback_providers . push ( provider) ;
61+ pub fn fallback < F : IntoRootProvider < N > + Send + ' static > ( mut self , provider : F ) -> Self {
62+ self . fallback_providers . push ( Box :: pin ( provider. into_root_provider ( ) ) ) ;
6363 self
6464 }
6565
@@ -113,11 +113,11 @@ impl<N: Network, P: IntoProvider<N>> RobustProviderBuilder<N, P> {
113113 ///
114114 /// Returns an error if any of the providers fail to connect.
115115 pub async fn build ( self ) -> Result < RobustProvider < N > , Error > {
116- let primary_provider = self . primary_provider . into_provider ( ) . await ?. root ( ) . to_owned ( ) ;
116+ let primary_provider = self . primary_provider . into_root_provider ( ) . await ?;
117117
118- let mut fallback_providers = vec ! [ ] ;
119- for p in self . fallback_providers {
120- fallback_providers. push ( p . into_provider ( ) . await ?. root ( ) . to_owned ( ) ) ;
118+ let mut fallback_providers = Vec :: with_capacity ( self . fallback_providers . len ( ) ) ;
119+ for fallback in self . fallback_providers {
120+ fallback_providers. push ( fallback . await ?) ;
121121 }
122122
123123 Ok ( RobustProvider {
@@ -131,3 +131,55 @@ impl<N: Network, P: IntoProvider<N>> RobustProviderBuilder<N, P> {
131131 } )
132132 }
133133}
134+
135+ #[ cfg( test) ]
136+ mod tests {
137+ use super :: * ;
138+ use alloy:: providers:: { ProviderBuilder , WsConnect } ;
139+ use alloy_node_bindings:: Anvil ;
140+
141+ #[ tokio:: test]
142+ async fn test_builder_primary_type_different_to_fallback ( ) -> anyhow:: Result < ( ) > {
143+ let anvil = Anvil :: new ( ) . try_spawn ( ) ?;
144+
145+ let fill_provider = ProviderBuilder :: new ( )
146+ . connect_ws ( WsConnect :: new ( anvil. ws_endpoint_url ( ) . as_str ( ) ) )
147+ . await ?;
148+
149+ let root_provider = RootProvider :: new_http ( anvil. endpoint_url ( ) ) ;
150+
151+ let robust = RobustProviderBuilder :: new ( fill_provider)
152+ . fallback ( root_provider)
153+ . call_timeout ( Duration :: from_secs ( 5 ) )
154+ . build ( )
155+ . await ?;
156+
157+ assert_eq ! ( robust. fallback_providers. len( ) , 1 ) ;
158+
159+ Ok ( ( ) )
160+ }
161+
162+ #[ tokio:: test]
163+ async fn test_builder_with_multiple_fallback_types ( ) -> anyhow:: Result < ( ) > {
164+ let anvil = Anvil :: new ( ) . try_spawn ( ) ?;
165+
166+ let fill_provider = ProviderBuilder :: new ( )
167+ . connect_ws ( WsConnect :: new ( anvil. ws_endpoint_url ( ) . as_str ( ) ) )
168+ . await ?;
169+
170+ let root_provider = RootProvider :: new_http ( anvil. endpoint_url ( ) ) ;
171+
172+ let url_provider = anvil. endpoint_url ( ) ;
173+
174+ let robust = RobustProviderBuilder :: new ( fill_provider)
175+ . fallback ( root_provider)
176+ . fallback ( url_provider. clone ( ) )
177+ . fallback ( url_provider)
178+ . build ( )
179+ . await ?;
180+
181+ assert_eq ! ( robust. fallback_providers. len( ) , 3 ) ;
182+
183+ Ok ( ( ) )
184+ }
185+ }
0 commit comments