mod config;
mod factory;
mod pool;
pub use config::ProviderConfig;
#[cfg(feature = "ws")]
pub use factory::create_ws_provider;
pub use factory::{
create_http_provider, create_typed_http_provider, rate_limited_http_provider,
simple_http_provider,
};
pub use pool::{ChainEndpoint, PooledProvider, ProviderPool, ProviderPoolBuilder};
use alloy_chains::NamedChain;
use alloy_network::{AnyNetwork, Ethereum};
use op_alloy_network::Optimism;
use std::sync::Arc;
pub type AnyHttpProvider = alloy_provider::RootProvider<AnyNetwork>;
pub type EthereumHttpProvider = alloy_provider::RootProvider<Ethereum>;
pub type OptimismHttpProvider = alloy_provider::RootProvider<Optimism>;
#[must_use]
pub fn network_type_for_chain(chain: NamedChain) -> NetworkType {
match chain {
NamedChain::Mainnet
| NamedChain::Sepolia
| NamedChain::Holesky
| NamedChain::Goerli
| NamedChain::Polygon
| NamedChain::PolygonAmoy
| NamedChain::Arbitrum
| NamedChain::ArbitrumSepolia
| NamedChain::ArbitrumGoerli
| NamedChain::ArbitrumNova => NetworkType::Ethereum,
NamedChain::Optimism
| NamedChain::OptimismSepolia
| NamedChain::OptimismGoerli
| NamedChain::Base
| NamedChain::BaseSepolia
| NamedChain::BaseGoerli
| NamedChain::Mode
| NamedChain::ModeSepolia
| NamedChain::Fraxtal
| NamedChain::FraxtalTestnet
| NamedChain::Zora
| NamedChain::ZoraSepolia => NetworkType::Optimism,
_ => NetworkType::Ethereum,
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum NetworkType {
Ethereum,
Optimism,
}
impl NetworkType {
#[must_use]
pub fn has_l1_data_fees(&self) -> bool {
matches!(self, Self::Optimism)
}
#[must_use]
pub fn name(&self) -> &'static str {
match self {
Self::Ethereum => "Ethereum",
Self::Optimism => "Optimism",
}
}
}
#[derive(Clone)]
pub struct ChainAwareProvider<P> {
provider: P,
chain: NamedChain,
network_type: NetworkType,
}
impl<P> ChainAwareProvider<P> {
pub fn new(provider: P, chain: NamedChain) -> Self {
Self {
provider,
network_type: network_type_for_chain(chain),
chain,
}
}
#[must_use]
pub fn chain(&self) -> NamedChain {
self.chain
}
#[must_use]
pub fn network_type(&self) -> NetworkType {
self.network_type
}
#[must_use]
pub fn provider(&self) -> &P {
&self.provider
}
pub fn into_inner(self) -> P {
self.provider
}
#[must_use]
pub fn has_l1_data_fees(&self) -> bool {
self.network_type.has_l1_data_fees()
}
}
impl<P> std::ops::Deref for ChainAwareProvider<P> {
type Target = P;
fn deref(&self) -> &Self::Target {
&self.provider
}
}
pub struct DynProviderBuilder {
rate_limit_per_second: Option<u32>,
timeout_ms: Option<u64>,
}
impl Default for DynProviderBuilder {
fn default() -> Self {
Self::new()
}
}
impl DynProviderBuilder {
#[must_use]
pub fn new() -> Self {
Self {
rate_limit_per_second: None,
timeout_ms: None,
}
}
#[must_use]
pub fn with_rate_limit(mut self, requests_per_second: u32) -> Self {
self.rate_limit_per_second = Some(requests_per_second);
self
}
#[must_use]
pub fn with_timeout_ms(mut self, timeout_ms: u64) -> Self {
self.timeout_ms = Some(timeout_ms);
self
}
pub fn build_http_for_chain(
self,
url: &str,
chain: NamedChain,
) -> Result<ChainAwareProvider<AnyHttpProvider>, crate::errors::RpcError> {
let config = ProviderConfig::new(url).with_rate_limit_opt(self.rate_limit_per_second);
let provider = create_http_provider(config)?;
Ok(ChainAwareProvider::new(provider, chain))
}
pub fn build_http(self, url: &str) -> Result<AnyHttpProvider, crate::errors::RpcError> {
let config = ProviderConfig::new(url).with_rate_limit_opt(self.rate_limit_per_second);
create_http_provider(config)
}
}
pub type SharedProvider<P> = Arc<P>;
pub fn share_provider<P>(provider: P) -> SharedProvider<P> {
Arc::new(provider)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_network_type_for_chain_ethereum() {
assert_eq!(
network_type_for_chain(NamedChain::Mainnet),
NetworkType::Ethereum
);
assert_eq!(
network_type_for_chain(NamedChain::Sepolia),
NetworkType::Ethereum
);
assert_eq!(
network_type_for_chain(NamedChain::Arbitrum),
NetworkType::Ethereum
);
assert_eq!(
network_type_for_chain(NamedChain::Polygon),
NetworkType::Ethereum
);
}
#[test]
fn test_network_type_for_chain_optimism() {
assert_eq!(
network_type_for_chain(NamedChain::Optimism),
NetworkType::Optimism
);
assert_eq!(
network_type_for_chain(NamedChain::Base),
NetworkType::Optimism
);
assert_eq!(
network_type_for_chain(NamedChain::Mode),
NetworkType::Optimism
);
assert_eq!(
network_type_for_chain(NamedChain::Zora),
NetworkType::Optimism
);
}
#[test]
fn test_network_type_l1_fees() {
assert!(!NetworkType::Ethereum.has_l1_data_fees());
assert!(NetworkType::Optimism.has_l1_data_fees());
}
#[test]
fn test_network_type_name() {
assert_eq!(NetworkType::Ethereum.name(), "Ethereum");
assert_eq!(NetworkType::Optimism.name(), "Optimism");
}
#[test]
fn test_dyn_provider_builder_defaults() {
let builder = DynProviderBuilder::new();
assert!(builder.rate_limit_per_second.is_none());
assert!(builder.timeout_ms.is_none());
}
#[test]
fn test_dyn_provider_builder_config() {
let builder = DynProviderBuilder::new()
.with_rate_limit(10)
.with_timeout_ms(5000);
assert_eq!(builder.rate_limit_per_second, Some(10));
assert_eq!(builder.timeout_ms, Some(5000));
}
}