Skip to content

Commit 0cdf8a5

Browse files
committed
Merge branch 'main' into event-channeling
2 parents 3961e75 + 2a74b92 commit 0cdf8a5

File tree

5 files changed

+288
-13
lines changed

5 files changed

+288
-13
lines changed

.github/workflows/check.yml

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -69,11 +69,7 @@ jobs:
6969
components: clippy
7070

7171
- name: Clippy Check
72-
uses: giraffate/clippy-action@v1
73-
with:
74-
reporter: "github-pr-check"
75-
github_token: ${{ secrets.GITHUB_TOKEN }}
76-
clippy_flags: --all-targets --all-features -- -D warnings -D clippy::pedantic
72+
run: cargo clippy --all-targets --all-features -- -D warnings -D clippy::pedantic
7773

7874
typos-cli:
7975
name: typos

.github/workflows/test.yml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,9 @@ jobs:
2525
- name: Install stable toolchain
2626
uses: actions-rust-lang/setup-rust-toolchain@v1
2727

28+
- name: Install Foundry
29+
uses: foundry-rs/foundry-toolchain@v1
30+
2831
- name: Cache cargo-nextest binary
2932
id: cache-cargo-nextest
3033
uses: actions/cache@v4
@@ -39,7 +42,7 @@ jobs:
3942
tool: cargo-nextest
4043

4144
- name: Cargo test
42-
run: cargo nextest run --locked --all-targets --all-features --no-tests=pass
45+
run: cargo nextest run --locked --all-targets --all-features --no-tests=pass --no-fail-fast
4346

4447
# https://github.com/rust-lang/cargo/issues/6669
4548
- name: Run doc tests

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
/target
22
/examples/**/target
3+
.DS_Store

src/builder.rs

Lines changed: 212 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,36 +24,47 @@ impl ScannerBuilder {
2424
}
2525
}
2626

27+
#[must_use]
2728
pub fn start_block(mut self, start_block: u64) -> Self {
2829
self.start_block = Some(start_block);
2930
self
3031
}
3132

33+
#[must_use]
3234
pub fn end_block(mut self, end_block: u64) -> Self {
3335
self.end_block = Some(end_block);
3436
self
3537
}
3638

39+
#[must_use]
3740
pub fn max_blocks_per_filter(mut self, max_blocks: u64) -> Self {
3841
self.max_blocks_per_filter = max_blocks;
3942
self
4043
}
4144

45+
#[must_use]
4246
pub fn add_event_filter(mut self, filter: EventFilter) -> Self {
4347
self.tracked_events.push(filter);
4448
self
4549
}
4650

51+
#[must_use]
4752
pub fn add_event_filters(mut self, filters: Vec<EventFilter>) -> Self {
4853
self.tracked_events.extend(filters);
4954
self
5055
}
5156

57+
#[must_use]
5258
pub fn callback_config(mut self, cfg: CallbackConfig) -> Self {
5359
self.callback_config = cfg;
5460
self
5561
}
5662

63+
/// Builds the scanner
64+
///
65+
/// # Errors
66+
///
67+
/// Returns an error if the scanner fails to build
5768
pub async fn build(self) -> anyhow::Result<Scanner> {
5869
Scanner::new(
5970
self.rpc_url,
@@ -66,3 +77,204 @@ impl ScannerBuilder {
6677
.await
6778
}
6879
}
80+
81+
#[cfg(test)]
82+
mod tests {
83+
use super::*;
84+
use crate::callback::EventCallback;
85+
use alloy::{primitives::address, rpc::types::Log};
86+
use async_trait::async_trait;
87+
use std::sync::Arc;
88+
89+
struct MockCallback;
90+
91+
#[async_trait]
92+
impl EventCallback for MockCallback {
93+
async fn on_event(&self, _log: &Log) -> anyhow::Result<()> {
94+
Ok(())
95+
}
96+
}
97+
98+
const WS_URL: &str = "ws://localhost:8545";
99+
100+
#[test]
101+
fn test_builder_new_defaults() {
102+
let builder = ScannerBuilder::new(WS_URL);
103+
assert_eq!(builder.rpc_url, WS_URL);
104+
assert_eq!(builder.start_block, None);
105+
assert_eq!(builder.end_block, None);
106+
assert_eq!(builder.max_blocks_per_filter, 1000);
107+
assert!(builder.tracked_events.is_empty());
108+
}
109+
110+
#[test]
111+
fn test_builder_start_block() {
112+
let start_block = 100;
113+
let builder = ScannerBuilder::new(WS_URL).start_block(start_block);
114+
assert_eq!(builder.start_block, Some(start_block));
115+
}
116+
117+
#[test]
118+
fn test_builder_end_block() {
119+
let end_block = 500;
120+
let builder = ScannerBuilder::new(WS_URL).end_block(end_block);
121+
assert_eq!(builder.end_block, Some(end_block));
122+
}
123+
124+
#[test]
125+
fn test_builder_block_range() {
126+
let start_block = 100;
127+
let end_block = 500;
128+
let builder = ScannerBuilder::new(WS_URL).start_block(start_block).end_block(end_block);
129+
assert_eq!(builder.start_block, Some(start_block));
130+
assert_eq!(builder.end_block, Some(end_block));
131+
}
132+
133+
#[test]
134+
fn test_builder_max_blocks_per_filter() {
135+
let max_blocks = 5000;
136+
let builder = ScannerBuilder::new(WS_URL).max_blocks_per_filter(max_blocks);
137+
assert_eq!(builder.max_blocks_per_filter, max_blocks);
138+
}
139+
140+
#[test]
141+
fn test_builder_callback_config() {
142+
let max_attempts = 5;
143+
let delay_ms = 500;
144+
let config = CallbackConfig { max_attempts, delay_ms };
145+
146+
let builder = ScannerBuilder::new(WS_URL).callback_config(config.clone());
147+
148+
assert_eq!(builder.callback_config.max_attempts, max_attempts);
149+
assert_eq!(builder.callback_config.delay_ms, delay_ms);
150+
}
151+
152+
#[test]
153+
fn test_builder_default_callback_config() {
154+
let builder = ScannerBuilder::new(WS_URL);
155+
156+
assert_eq!(builder.callback_config.max_attempts, 3);
157+
assert_eq!(builder.callback_config.delay_ms, 200);
158+
}
159+
160+
#[test]
161+
fn test_builder_add_event_filter() {
162+
let addr = address!("f39Fd6e51aad88F6F4ce6aB8827279cffFb92266");
163+
let event = "Transfer(address,address,uint256)".to_string();
164+
let filter = EventFilter {
165+
contract_address: addr,
166+
event: event.clone(),
167+
callback: Arc::new(MockCallback),
168+
};
169+
let builder = ScannerBuilder::new(WS_URL).add_event_filter(filter.clone());
170+
171+
assert_eq!(builder.tracked_events.len(), 1);
172+
assert_eq!(builder.tracked_events[0].contract_address, addr);
173+
assert_eq!(builder.tracked_events[0].event, event);
174+
}
175+
176+
#[test]
177+
fn test_builder_add_multiple_event_filters() {
178+
let addr1 = address!("f39Fd6e51aad88F6F4ce6aB8827279cffFb92266");
179+
let event1 = "Transfer(address,address,uint256)".to_string();
180+
let addr2 = address!("70997970C51812dc3A010C7d01b50e0d17dc79C8");
181+
let event2 = "Approval(address,address,uint256)".to_string();
182+
183+
let filter1 = EventFilter {
184+
contract_address: addr1,
185+
event: event1.clone(),
186+
callback: Arc::new(MockCallback),
187+
};
188+
let filter2 = EventFilter {
189+
contract_address: addr2,
190+
event: event2.clone(),
191+
callback: Arc::new(MockCallback),
192+
};
193+
194+
let builder = ScannerBuilder::new(WS_URL)
195+
.add_event_filter(filter1.clone())
196+
.add_event_filter(filter2.clone());
197+
198+
assert_eq!(builder.tracked_events.len(), 2);
199+
for (i, expected_filter) in builder.tracked_events.iter().enumerate() {
200+
assert_eq!(
201+
builder.tracked_events[i].contract_address,
202+
expected_filter.contract_address
203+
);
204+
assert_eq!(builder.tracked_events[i].event, expected_filter.event);
205+
}
206+
}
207+
208+
#[test]
209+
fn test_builder_add_event_filters_batch() {
210+
let addr1 = address!("f39Fd6e51aad88F6F4ce6aB8827279cffFb92266");
211+
let event1 = "Transfer(address,address,uint256)".to_string();
212+
let addr2 = address!("70997970C51812dc3A010C7d01b50e0d17dc79C8");
213+
let event2 = "Approval(address,address,uint256)".to_string();
214+
let addr3 = address!("3C44CdDdB6a900fa2b585dd299e03d12FA4293BC");
215+
let event3 = "Mint(address,uint256)".to_string();
216+
217+
let filter_1 = EventFilter {
218+
contract_address: addr1,
219+
event: event1.clone(),
220+
callback: Arc::new(MockCallback),
221+
};
222+
let filter_2 = EventFilter {
223+
contract_address: addr2,
224+
event: event2.clone(),
225+
callback: Arc::new(MockCallback),
226+
};
227+
let filter_3 = EventFilter {
228+
contract_address: addr3,
229+
event: event3.clone(),
230+
callback: Arc::new(MockCallback),
231+
};
232+
233+
let filters = vec![filter_1.clone(), filter_2.clone(), filter_3.clone()];
234+
let builder = ScannerBuilder::new(WS_URL).add_event_filters(filters.clone());
235+
236+
assert_eq!(builder.tracked_events.len(), filters.len());
237+
238+
for (i, expected_filter) in filters.iter().enumerate() {
239+
assert_eq!(
240+
builder.tracked_events[i].contract_address,
241+
expected_filter.contract_address
242+
);
243+
assert_eq!(builder.tracked_events[i].event, expected_filter.event);
244+
}
245+
}
246+
247+
#[test]
248+
fn test_builder_chain_all_methods() {
249+
let start_block = 100;
250+
let end_block = 500;
251+
252+
let addr = address!("f39Fd6e51aad88F6F4ce6aB8827279cffFb92266");
253+
let event = "Transfer(address,address,uint256)".to_string();
254+
255+
let filter = EventFilter {
256+
contract_address: addr,
257+
event: event.clone(),
258+
callback: Arc::new(MockCallback),
259+
};
260+
261+
let max_attempts = 5;
262+
let delay_ms = 500;
263+
let config = CallbackConfig { max_attempts, delay_ms };
264+
265+
let max_blocks_per_filter = 2000;
266+
let builder = ScannerBuilder::new(WS_URL)
267+
.start_block(start_block)
268+
.end_block(end_block)
269+
.max_blocks_per_filter(max_blocks_per_filter)
270+
.add_event_filter(filter.clone())
271+
.callback_config(config.clone());
272+
273+
assert_eq!(builder.start_block, Some(start_block));
274+
assert_eq!(builder.end_block, Some(end_block));
275+
assert_eq!(builder.max_blocks_per_filter, max_blocks_per_filter);
276+
assert_eq!(builder.tracked_events.len(), 1);
277+
assert_eq!(builder.callback_config.max_attempts, max_attempts);
278+
assert_eq!(builder.callback_config.delay_ms, delay_ms);
279+
}
280+
}

0 commit comments

Comments
 (0)