| Crates.io | robust-provider |
| lib.rs | robust-provider |
| version | 0.2.0 |
| created_at | 2026-01-07 18:10:57.197101+00 |
| updated_at | 2026-01-15 11:01:37.496223+00 |
| description | Robust Provider is a library for creating resilient RPC providers for EVM-based blockchains with automatic retries and failover support. |
| homepage | |
| repository | https://github.com/OpenZeppelin/Robust-Provider |
| max_upload_size | |
| id | 2028778 |
| size | 217,525 |
Robust, retrying wrapper around Alloy providers.
⚠️ WARNING: ACTIVE DEVELOPMENT ⚠️
This project is under active development and likely contains bugs. APIs and behaviour may change without notice. Use at your own risk.
Robust Provider is a Rust library that wraps Alloy providers with production-ready resilience features. It adds automatic retries, timeouts, and transparent failover between multiple RPC endpoints - making it ideal for applications that need reliable blockchain connectivity.
Add robust-provider to your Cargo.toml:
[dependencies]
robust-provider = "0.2.0"
Create a robust provider with automatic retries and fallback:
use alloy::providers::{Provider, ProviderBuilder};
use robust_provider::RobustProviderBuilder;
use std::time::Duration;
use tokio_stream::StreamExt;
async fn run() -> anyhow::Result<()> {
let ws = ProviderBuilder::new().connect("ws://localhost:8545").await?;
let ws_fallback = ProviderBuilder::new().connect("ws://localhost:8546").await?;
let robust = RobustProviderBuilder::new(ws)
.fallback(ws_fallback)
.call_timeout(Duration::from_secs(30))
.subscription_timeout(Duration::from_secs(120))
.build()
.await?;
// Make RPC calls with automatic retries and fallback
let block_number = robust.get_block_number().await?;
println!("Current block: {}", block_number);
// Create subscriptions that automatically reconnect on failure
let sub = robust.subscribe_blocks().await?;
let mut stream = sub.into_stream();
while let Some(response) = stream.next().await {
match response {
Ok(block) => println!("New block: {:?}", block),
Err(e) => println!("Got error: {:?}", e),
}
}
Ok(())
}
RobustProviderBuilder provides a fluent API for constructing a RobustProvider with custom settings:
use alloy::providers::ProviderBuilder;
use robust_provider::RobustProviderBuilder;
use std::time::Duration;
// Standard configuration with retries
let provider = ProviderBuilder::new().connect("ws://localhost:8545").await?;
let robust = RobustProviderBuilder::new(provider)
.call_timeout(Duration::from_secs(30))
.max_retries(3)
.build()
.await?;
// With multiple fallback providers
let primary = ProviderBuilder::new().connect("ws://primary:8545").await?;
let fallback_1 = ProviderBuilder::new().connect("ws://fallback1:8545").await?;
let fallback_2 = ProviderBuilder::new().connect_http("http://fallback2:8545".parse()?);
let robust = RobustProviderBuilder::new(primary)
.fallback(fallback_1)
.fallback(fallback_2)
.build()
.await?;
| Option | Default | Description |
|---|---|---|
call_timeout |
60s | Maximum time for RPC operations before timing out |
subscription_timeout |
120s | Maximum time to wait for subscription messages |
max_retries |
3 | Number of retry attempts before failing over |
min_delay |
1s | Base delay for exponential backoff between retries |
reconnect_interval |
30s | Interval between primary provider reconnection attempts (for subscription) |
subscription_buffer_capacity |
128 | Buffer size for subscription streams |
RobustSubscription wraps Alloy's block subscriptions with automatic failover and reconnection:
let robust = RobustProviderBuilder::new(provider)
.fallback(fallback)
.subscription_timeout(Duration::from_secs(120))
.reconnect_interval(Duration::from_secs(30))
.build()
.await?;
let subscription = robust.subscribe_blocks().await?;
let mut stream = subscription.into_stream();
while let Some(result) = stream.next().await {
match result {
Ok(block) => {
println!("Block {}: {}", block.number, block.hash);
}
Err(e) => {
// Errors are propagated but the stream continues
// (except for Closed errors which terminate the stream)
eprintln!("Subscription error: {:?}", e);
}
}
}
Subscription behaviour:
subscription_timeout, the provider automatically fails over to fallbacks.reconnect_interval).Lagged error indicates the consumer is not keeping pace with incoming blocks.The library provides two conversion traits for flexible provider handling:
IntoRootProviderConverts various types into an Alloy RootProvider. Implementations are provided for:
RobustProviderRootProvider&str (connection URL)UrlFillProviderCacheProviderDynProviderCallBatchProviderIntoRobustProviderConverts any IntoRootProvider type directly into a RobustProvider with default settings:
use robust_provider::IntoRobustProvider;
// Convert a URL directly to a RobustProvider
let robust: RobustProvider<Ethereum> = "ws://localhost:8545".into().await?;
// Or convert an existing provider
let provider = ProviderBuilder::new().connect("ws://localhost:8545").await?;
let robust: RobustProvider<Ethereum> = provider.into().await?;
Run the test suite:
cargo nextest run
The tests use local Anvil instances to verify retry logic, failover behaviour, and subscription resilience.
⚠️ Work In Progress ⚠️
This library is under active development and many RPC endpoints have not been implemented yet. If you encounter a missing endpoint, see the Extensibility section below for how to make raw RPC calls.
The library exposes try_operation_with_failover, allowing you to wrap any RPC call with the full retry and failover logic, even for endpoints that haven't been explicitly implemented:
use alloy::providers::Provider;
// Use try_operation_with_failover to call any RPC method with full resilience
let block = robust
.try_operation_with_failover(
|provider| async move { provider.get_block_by_number(0.into()).await },
false,
)
.await?;
This gives you the same automatic retries, timeouts, and failover behaviour for any RPC method supported by your node.