use alloy_network::AnyNetwork;
use alloy_provider::RootProvider;
use alloy_rpc_client::ClientBuilder;
use crate::errors::RpcError;
use crate::transport::RateLimitLayer;
use super::config::ProviderConfig;
use super::AnyHttpProvider;
pub fn create_http_provider(config: ProviderConfig) -> Result<AnyHttpProvider, RpcError> {
let url: url::Url = config
.url
.parse()
.map_err(|e| RpcError::ProviderUrlInvalid(format!("{e}")))?;
match (config.rate_limit_per_second, config.min_delay) {
(Some(rps), None) => {
let client = ClientBuilder::default()
.layer(RateLimitLayer::per_second(rps))
.http(url);
Ok(RootProvider::<AnyNetwork>::new(client))
}
(None, Some(delay)) => {
let client = ClientBuilder::default()
.layer(RateLimitLayer::with_min_delay(delay))
.http(url);
Ok(RootProvider::<AnyNetwork>::new(client))
}
(None, None) => Ok(RootProvider::<AnyNetwork>::new_http(url)),
(Some(rps), Some(_)) => {
tracing::warn!(
"Both rate_limit_per_second and min_delay specified, using rate_limit_per_second"
);
let config = ProviderConfig {
rate_limit_per_second: Some(rps),
min_delay: None,
..config
};
create_http_provider(config)
}
}
}
#[cfg(feature = "ws")]
pub async fn create_ws_provider(
config: ProviderConfig,
) -> Result<alloy_provider::RootProvider<AnyNetwork>, RpcError> {
use alloy_provider::WsConnect;
let ws = WsConnect::new(&config.url);
let client = match config.rate_limit_per_second {
Some(rps) => ClientBuilder::default()
.layer(RateLimitLayer::per_second(rps))
.ws(ws)
.await
.map_err(|e| RpcError::ProviderConnectionFailed(e.to_string()))?,
None => ClientBuilder::default()
.ws(ws)
.await
.map_err(|e| RpcError::ProviderConnectionFailed(e.to_string()))?,
};
Ok(RootProvider::<AnyNetwork>::new(client))
}
pub fn create_typed_http_provider<N>(
config: ProviderConfig,
) -> Result<alloy_provider::RootProvider<N>, RpcError>
where
N: alloy_network::Network,
{
let url: url::Url = config
.url
.parse()
.map_err(|e| RpcError::ProviderUrlInvalid(format!("{e}")))?;
match config.rate_limit_per_second {
Some(rps) => {
let client = ClientBuilder::default()
.layer(RateLimitLayer::per_second(rps))
.http(url);
Ok(RootProvider::<N>::new(client))
}
None => Ok(RootProvider::<N>::new_http(url)),
}
}
pub fn simple_http_provider(url: &str) -> Result<AnyHttpProvider, RpcError> {
create_http_provider(ProviderConfig::new(url))
}
pub fn rate_limited_http_provider(
url: &str,
requests_per_second: u32,
) -> Result<AnyHttpProvider, RpcError> {
create_http_provider(ProviderConfig::new(url).with_rate_limit(requests_per_second))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_create_http_provider_invalid_url() {
let result = create_http_provider(ProviderConfig::new("not-a-valid-url"));
assert!(result.is_err());
}
#[test]
fn test_create_http_provider_valid_url() {
let result = create_http_provider(ProviderConfig::new("http://localhost:8545"));
assert!(result.is_ok());
}
#[test]
fn test_create_http_provider_with_rate_limit() {
let result =
create_http_provider(ProviderConfig::new("http://localhost:8545").with_rate_limit(10));
assert!(result.is_ok());
}
#[test]
fn test_simple_http_provider() {
let result = simple_http_provider("http://localhost:8545");
assert!(result.is_ok());
}
#[test]
fn test_rate_limited_http_provider() {
let result = rate_limited_http_provider("http://localhost:8545", 10);
assert!(result.is_ok());
}
#[test]
fn test_create_typed_http_provider() {
use alloy_network::Ethereum;
let result =
create_typed_http_provider::<Ethereum>(ProviderConfig::new("http://localhost:8545"));
assert!(result.is_ok());
}
}