use alloy_primitives::{keccak256, Address, BlockNumber, U256};
use alloy_rpc_types::Filter;
#[derive(Debug, Clone, Default)]
pub struct TransferFilterBuilder {
from_block: Option<BlockNumber>,
to_block: Option<BlockNumber>,
token_address: Option<Address>,
from_address: Option<Address>,
to_address: Option<Address>,
}
impl TransferFilterBuilder {
pub fn new() -> Self {
Self::default()
}
pub fn with_token(mut self, token: Address) -> Self {
self.token_address = Some(token);
self
}
pub fn with_sender(mut self, sender: Address) -> Self {
self.from_address = Some(sender);
self
}
pub fn with_recipient(mut self, recipient: Address) -> Self {
self.to_address = Some(recipient);
self
}
pub fn build(self) -> Filter {
let mut filter =
Filter::new().event_signature(*keccak256(b"Transfer(address,address,uint256)"));
if let Some(from) = self.from_block {
filter = filter.from_block(from);
}
if let Some(to) = self.to_block {
filter = filter.to_block(to);
}
if let Some(token) = self.token_address {
filter = filter.address(token);
}
if let Some(from) = self.from_address {
filter = filter.topic1(from);
}
if let Some(to) = self.to_address {
filter = filter.topic2(U256::from_be_bytes(to.into_word().into()));
}
filter
}
}
#[allow(dead_code)] pub fn transfer_filter_to_recipient(recipient: Address) -> Filter {
TransferFilterBuilder::new()
.with_recipient(recipient)
.build()
}
#[allow(dead_code)] pub fn transfer_filter_from_to(token: Address, from: Address, to: Address) -> Filter {
TransferFilterBuilder::new()
.with_token(token)
.with_sender(from)
.with_recipient(to)
.build()
}
#[cfg(test)]
mod tests {
use super::*;
use alloy_primitives::address;
#[test]
fn test_builder_with_all_fields() {
let token = address!("A0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48");
let from = address!("1111111111111111111111111111111111111111");
let to = address!("2222222222222222222222222222222222222222");
let filter = TransferFilterBuilder::new()
.with_token(token)
.with_sender(from)
.with_recipient(to)
.build();
assert_eq!(filter.get_from_block(), None);
assert_eq!(filter.get_to_block(), None);
}
#[test]
fn test_builder_with_recipient_only() {
let recipient = address!("1111111111111111111111111111111111111111");
let filter = TransferFilterBuilder::new()
.with_recipient(recipient)
.build();
assert_eq!(filter.get_from_block(), None);
assert_eq!(filter.get_to_block(), None);
}
#[test]
fn test_transfer_filter_to_recipient_convenience() {
let router = address!("1111111111111111111111111111111111111111");
let filter = transfer_filter_to_recipient(router);
assert_eq!(filter.get_from_block(), None);
assert_eq!(filter.get_to_block(), None);
}
#[test]
fn test_transfer_filter_from_to_convenience() {
let token = address!("A0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48");
let from = address!("1111111111111111111111111111111111111111");
let to = address!("2222222222222222222222222222222222222222");
let filter = transfer_filter_from_to(token, from, to);
assert_eq!(filter.get_from_block(), None);
assert_eq!(filter.get_to_block(), None);
}
#[test]
fn test_default_builder() {
let filter = TransferFilterBuilder::new().build();
assert_eq!(filter.get_from_block(), None);
assert_eq!(filter.get_to_block(), None);
}
#[test]
fn test_builder_partial_fields() {
let token = address!("A0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48");
let filter = TransferFilterBuilder::new().with_token(token).build();
assert_eq!(filter.get_from_block(), None);
assert_eq!(filter.get_to_block(), None);
}
#[test]
fn test_builder_method_chaining() {
let filter = TransferFilterBuilder::new()
.with_token(Address::ZERO)
.with_sender(Address::ZERO)
.with_recipient(Address::ZERO)
.build();
assert_eq!(filter.get_from_block(), None);
assert_eq!(filter.get_to_block(), None);
}
mod integration {
use super::*;
#[test]
fn test_public_api_token_discovery_pattern() {
let router = address!("0x1234567890abcdef1234567890abcdef12345678");
let filter1 = TransferFilterBuilder::new().with_recipient(router).build();
let filter2 = transfer_filter_to_recipient(router);
assert_eq!(filter1.get_from_block(), filter2.get_from_block());
assert_eq!(filter1.get_to_block(), filter2.get_to_block());
}
#[test]
fn test_public_api_specific_transfer_pattern() {
let usdc = address!("A0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48");
let router = address!("1111111111111111111111111111111111111111");
let liquidator = address!("2222222222222222222222222222222222222222");
let filter1 = TransferFilterBuilder::new()
.with_token(usdc)
.with_sender(router)
.with_recipient(liquidator)
.build();
let filter2 = transfer_filter_from_to(usdc, router, liquidator);
assert_eq!(filter1.get_from_block(), filter2.get_from_block());
assert_eq!(filter1.get_to_block(), filter2.get_to_block());
}
#[test]
fn test_builder_idiomatic_naming() {
let token = address!("A0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48");
let from = address!("1111111111111111111111111111111111111111");
let to = address!("2222222222222222222222222222222222222222");
let _filter = TransferFilterBuilder::new()
.with_token(token)
.with_sender(from)
.with_recipient(to)
.build();
}
#[test]
fn test_partial_filter_construction() {
let router = address!("1111111111111111111111111111111111111111");
let _filter1 = TransferFilterBuilder::new().with_recipient(router).build();
let _filter2 = TransferFilterBuilder::new().with_sender(router).build();
let token = address!("A0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48");
let _filter3 = TransferFilterBuilder::new().with_token(token).build();
}
#[test]
fn test_convenience_functions_no_block_ranges() {
let router = address!("1111111111111111111111111111111111111111");
let token = address!("A0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48");
let filter1 = transfer_filter_to_recipient(router);
assert_eq!(filter1.get_from_block(), None);
assert_eq!(filter1.get_to_block(), None);
let filter2 = transfer_filter_from_to(token, router, router);
assert_eq!(filter2.get_from_block(), None);
assert_eq!(filter2.get_to_block(), None);
}
}
}