@@ -4,10 +4,58 @@ use url::Url;
44use crate :: error:: { DefiError , Result } ;
55use crate :: registry:: ChainConfig ;
66
7+ /// Build a provider using the effective RPC URL (env var override supported).
78pub fn build_provider ( chain : & ChainConfig ) -> Result < impl alloy:: providers:: Provider > {
8- let url : Url = chain
9- . rpc_url
9+ let rpc = chain. effective_rpc_url ( ) ;
10+ let url : Url = rpc
1011 . parse ( )
1112 . map_err ( |e| DefiError :: RpcError ( format ! ( "Invalid RPC URL: {e}" ) ) ) ?;
1213 Ok ( ProviderBuilder :: new ( ) . connect_http ( url) )
1314}
15+
16+ /// Build a provider from a raw URL string.
17+ pub fn build_provider_from_url ( rpc_url : & str ) -> Result < impl alloy:: providers:: Provider > {
18+ let url: Url = rpc_url
19+ . parse ( )
20+ . map_err ( |e| DefiError :: RpcError ( format ! ( "Invalid RPC URL: {e}" ) ) ) ?;
21+ Ok ( ProviderBuilder :: new ( ) . connect_http ( url) )
22+ }
23+
24+ /// Retry an async RPC operation with exponential backoff on rate limit errors.
25+ /// Retries up to `max_retries` times with initial delay `initial_delay_ms`.
26+ pub async fn with_retry < F , Fut , T > (
27+ max_retries : u32 ,
28+ initial_delay_ms : u64 ,
29+ mut operation : F ,
30+ ) -> Result < T >
31+ where
32+ F : FnMut ( ) -> Fut ,
33+ Fut : std:: future:: Future < Output = Result < T > > ,
34+ {
35+ let mut delay = initial_delay_ms;
36+ for attempt in 0 ..=max_retries {
37+ match operation ( ) . await {
38+ Ok ( val) => return Ok ( val) ,
39+ Err ( e) => {
40+ let err_str = e. to_string ( ) ;
41+ let is_rate_limit = err_str. contains ( "rate limit" )
42+ || err_str. contains ( "-32005" )
43+ || err_str. contains ( "429" ) ;
44+
45+ if !is_rate_limit || attempt == max_retries {
46+ return Err ( e) ;
47+ }
48+
49+ eprintln ! (
50+ "Rate limited (attempt {}/{}), retrying in {}ms..." ,
51+ attempt + 1 ,
52+ max_retries,
53+ delay
54+ ) ;
55+ tokio:: time:: sleep ( std:: time:: Duration :: from_millis ( delay) ) . await ;
56+ delay = ( delay * 2 ) . min ( 30_000 ) ; // exponential backoff, max 30s
57+ }
58+ }
59+ }
60+ unreachable ! ( )
61+ }
0 commit comments