use alloy_chains::NamedChain;
use alloy_primitives::BlockNumber;
use alloy_provider::Provider;
use alloy_rpc_types::{Filter, Log};
use tracing::{debug, error};
use crate::config::SemioscanConfig;
use crate::errors::EventProcessingError;
use crate::scan::LogScanner;
pub struct EventScanner<P> {
inner: LogScanner<P>,
}
impl<P: Provider> EventScanner<P> {
pub fn new(provider: P, config: SemioscanConfig) -> Self {
Self {
inner: LogScanner::new(provider, config),
}
}
pub async fn scan(
&self,
chain: NamedChain,
filter_template: Filter,
start_block: BlockNumber,
end_block: BlockNumber,
) -> Result<Vec<Log>, EventProcessingError> {
self.inner
.scan::<EventProcessingError, _>(
chain,
filter_template,
start_block,
end_block,
|_, _, _| None,
)
.await
}
#[allow(dead_code)]
pub async fn scan_with_handler<F, Fut>(
&self,
chain: NamedChain,
filter_template: Filter,
start_block: BlockNumber,
end_block: BlockNumber,
mut handler: F,
) -> Result<(), EventProcessingError>
where
F: FnMut(Vec<Log>) -> Fut,
Fut: std::future::Future<Output = Result<(), EventProcessingError>>,
{
debug!(
chain = %chain,
start_block,
end_block,
"Starting event scan with handler"
);
let logs = self
.inner
.scan::<EventProcessingError, _>(
chain,
filter_template,
start_block,
end_block,
|_, _, _| None,
)
.await?;
match handler(logs).await {
Ok(()) => {}
Err(e) => {
error!(?e, %chain, "Handler returned error during event scan");
return Err(e);
}
}
debug!(chain = %chain, "Finished event scan with handler");
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::SemioscanConfigBuilder;
use alloy_chains::NamedChain;
use std::time::Duration;
#[test]
fn test_config_provides_correct_defaults() {
let config = SemioscanConfig::default();
assert_eq!(
config.get_rate_limit_delay(NamedChain::Base),
Some(Duration::from_millis(250))
);
assert_eq!(
config.get_rate_limit_delay(NamedChain::Sonic),
Some(Duration::from_millis(250))
);
assert_eq!(config.get_rate_limit_delay(NamedChain::Arbitrum), None);
}
#[test]
fn test_custom_config_overrides() {
let config = SemioscanConfigBuilder::with_defaults()
.chain_rate_limit(NamedChain::Arbitrum, Duration::from_millis(100))
.build();
assert_eq!(
config.get_rate_limit_delay(NamedChain::Arbitrum),
Some(Duration::from_millis(100))
);
}
#[test]
fn test_minimal_config_has_no_delays() {
let config = SemioscanConfig::minimal();
assert_eq!(config.get_rate_limit_delay(NamedChain::Base), None);
assert_eq!(config.get_rate_limit_delay(NamedChain::Sonic), None);
assert_eq!(config.get_rate_limit_delay(NamedChain::Arbitrum), None);
}
}