Skip to main content

robust_provider/
lib.rs

1//! Robust, retrying wrapper around Alloy providers.
2//!
3//! This module exposes [`RobustProvider`], a small wrapper around Alloy's
4//! [`RootProvider`](alloy::providers::RootProvider) that adds:
5//! * bounded per-call timeouts
6//! * exponential backoff retries
7//! * transparent failover between a primary and one or more fallback providers
8//! * more robust WebSocket block subscriptions with automatic reconnection
9//!
10//! Use [`RobustProviderBuilder`] to construct a provider with sensible defaults
11//! and optional fallbacks, or implement the [`IntoRobustProvider`] and [`IntoRootProvider`]
12//! traits to support custom providers.
13//!
14//! # How it works
15//!
16//! All RPC calls performed through [`RobustProvider`] are wrapped in a total
17//! timeout and retried with exponential backoff up to `max_retries`. If the
18//! primary provider keeps failing, the call is retried against the configured
19//! fallback providers in the order they were added. For subscriptions,
20//! [`RobustSubscription`] also tracks lag, switches to fallbacks on repeated
21//! failure, and periodically attempts to reconnect to the primary provider.
22//!
23//! # Examples
24//!
25//! Creating a robust WebSocket provider with a fallback:
26//!
27//! ```rust,no_run
28//! use alloy::providers::{Provider, ProviderBuilder};
29//! use robust_provider::RobustProviderBuilder;
30//! use std::time::Duration;
31//! use tokio_stream::StreamExt;
32//!
33//! # async fn example() -> anyhow::Result<()> {
34//! let ws = ProviderBuilder::new().connect("ws://localhost:8545").await?;
35//! let ws_fallback = ProviderBuilder::new().connect("ws://localhost:8456").await?;
36//!
37//! let robust = RobustProviderBuilder::new(ws)
38//!     .fallback(ws_fallback)
39//!     .call_timeout(Duration::from_secs(30))
40//!     .subscription_timeout(Duration::from_secs(120))
41//!     .build()
42//!     .await?;
43//!
44//! // Make RPC calls with automatic retries and fallback
45//! let block_number = robust.get_block_number().await?;
46//! println!("Current block: {}", block_number);
47//!
48//! // Create subscriptions that automatically reconnect on failure
49//! let sub = robust.subscribe_blocks().await?;
50//! let mut stream = sub.into_stream();
51//! while let Some(response) = stream.next().await {
52//!     match response {
53//!         Ok(block) => println!("New block: {:?}", block),
54//!         Err(e) => println!("Got error: {:?}", e),
55//!     }
56//! }
57//! # Ok(()) }
58//! ```
59//!
60//! You can also convert existing providers using [`IntoRobustProvider`]
61
62#[macro_use]
63pub mod macros;
64
65mod robust_provider;
66
67pub use robust_provider::{
68    DEFAULT_CALL_TIMEOUT, DEFAULT_MAX_RETRIES, DEFAULT_MIN_DELAY, DEFAULT_RECONNECT_INTERVAL,
69    DEFAULT_SUBSCRIPTION_BUFFER_CAPACITY, DEFAULT_SUBSCRIPTION_TIMEOUT, Error, FailoverError,
70    IntoRobustProvider, IntoRootProvider, RobustProvider, RobustProviderBuilder,
71    RobustSubscription, RobustSubscriptionStream,
72};
73
74#[cfg(feature = "http-subscription")]
75pub use robust_provider::DEFAULT_POLL_INTERVAL;