use std::time::Duration;
use alloy_chains::NamedChain;
use crate::types::config::MaxBlockRange;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct ScanConfig {
pub max_block_range: MaxBlockRange,
pub rate_limit_delay: Option<Duration>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct LookupConfig {
pub serial_lookup_fallback_attempts: usize,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub struct RpcConfig {
pub rpc_timeout: Duration,
pub rate_limit_delay: Option<Duration>,
}
impl RpcConfig {
#[must_use]
pub fn new(rpc_timeout: Duration) -> Self {
Self {
rpc_timeout,
rate_limit_delay: None,
}
}
#[must_use]
#[track_caller]
pub fn with_rate_limit_delay(mut self, delay: Duration) -> Self {
super::assert_nonzero_rate_limit_delay(delay);
self.rate_limit_delay = Some(delay);
self
}
}
pub trait ScanPolicy {
fn scan_config(&self, chain: NamedChain) -> ScanConfig;
}
pub trait LookupPolicy {
fn lookup_config(&self, chain: NamedChain) -> LookupConfig;
}
pub trait RpcPolicy {
fn rpc_config(&self, chain: NamedChain) -> RpcConfig;
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{SemioscanConfig, SemioscanConfigBuilder};
#[test]
fn semioscan_config_scan_view_matches_chain_lookups() {
let config = SemioscanConfigBuilder::with_defaults()
.chain_max_blocks(NamedChain::Arbitrum, 1234)
.chain_rate_limit(NamedChain::Arbitrum, Duration::from_millis(77))
.build();
let scan = config.scan_config(NamedChain::Arbitrum);
assert_eq!(scan.max_block_range, MaxBlockRange::new(1234));
assert_eq!(scan.rate_limit_delay, Some(Duration::from_millis(77)));
}
#[test]
fn semioscan_config_scan_view_falls_back_to_global_defaults() {
let config = SemioscanConfig::default();
let scan = config.scan_config(NamedChain::Optimism);
assert_eq!(scan.max_block_range, MaxBlockRange::new(500));
assert_eq!(scan.rate_limit_delay, None);
let base = config.scan_config(NamedChain::Base);
assert_eq!(base.rate_limit_delay, Some(Duration::from_millis(250)));
}
#[test]
fn semioscan_config_lookup_view_matches_chain_lookups() {
let config = SemioscanConfigBuilder::with_defaults()
.serial_lookup_fallback_attempts(3)
.chain_serial_lookup_fallback_attempts(NamedChain::ZkSync, 0)
.build();
assert_eq!(
config
.lookup_config(NamedChain::Mainnet)
.serial_lookup_fallback_attempts,
3
);
assert_eq!(
config
.lookup_config(NamedChain::ZkSync)
.serial_lookup_fallback_attempts,
0
);
}
#[test]
fn minimal_config_lookup_view_uses_default_attempts() {
let config = SemioscanConfig::minimal();
assert_eq!(
config
.lookup_config(NamedChain::Mainnet)
.serial_lookup_fallback_attempts,
1
);
}
#[test]
fn semioscan_config_rpc_view_returns_global_timeout_by_default() {
let config = SemioscanConfig::default();
assert_eq!(
config.rpc_config(NamedChain::Mainnet).rpc_timeout,
Duration::from_secs(30)
);
assert_eq!(
config.rpc_config(NamedChain::Arbitrum).rpc_timeout,
Duration::from_secs(30)
);
}
#[test]
fn semioscan_config_rpc_view_honors_chain_override() {
let config = SemioscanConfigBuilder::with_defaults()
.rpc_timeout(Duration::from_secs(45))
.chain_timeout(NamedChain::Polygon, Duration::from_secs(90))
.build();
assert_eq!(
config.rpc_config(NamedChain::Mainnet).rpc_timeout,
Duration::from_secs(45)
);
assert_eq!(
config.rpc_config(NamedChain::Polygon).rpc_timeout,
Duration::from_secs(90)
);
}
}