use alloy_network::Network;
use alloy_primitives::{Address, B256, I256, U256};
use alloy_provider::Provider;
use alloy_rpc_types::{BlockNumberOrTag, Filter, Log};
use alloy_sol_types::SolEvent;
#[cfg(feature = "v2")]
use crate::v2_router::OdosV2Router;
#[cfg(feature = "v3")]
use crate::v3_router::OdosV3Router;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct SwapEvent {
pub sender: Address,
pub input_token: Address,
pub input_amount: U256,
pub output_token: Address,
pub amount_out: U256,
pub slippage: I256,
pub referral_code: u64,
pub block_number: Option<u64>,
pub transaction_hash: Option<B256>,
pub log_index: Option<u64>,
}
#[derive(Debug, Clone)]
pub struct SwapEventFilter {
router_address: Address,
from_block: Option<BlockNumberOrTag>,
to_block: Option<BlockNumberOrTag>,
sender: Option<Address>,
}
impl SwapEventFilter {
pub fn new(router_address: Address) -> Self {
Self {
router_address,
from_block: None,
to_block: None,
sender: None,
}
}
pub fn from_block(mut self, block: u64) -> Self {
self.from_block = Some(BlockNumberOrTag::Number(block));
self
}
pub fn from_latest(mut self) -> Self {
self.from_block = Some(BlockNumberOrTag::Latest);
self
}
pub fn to_block(mut self, block: u64) -> Self {
self.to_block = Some(BlockNumberOrTag::Number(block));
self
}
pub fn to_latest(mut self) -> Self {
self.to_block = Some(BlockNumberOrTag::Latest);
self
}
pub fn sender(mut self, sender: Address) -> Self {
self.sender = Some(sender);
self
}
#[cfg(feature = "v2")]
pub fn build_v2_filter(&self) -> Filter {
let mut filter = Filter::new()
.address(self.router_address)
.event_signature(OdosV2Router::Swap::SIGNATURE_HASH);
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(sender) = self.sender {
filter = filter.topic1(sender.into_word());
}
filter
}
#[cfg(feature = "v3")]
pub fn build_v3_filter(&self) -> Filter {
let mut filter = Filter::new()
.address(self.router_address)
.event_signature(OdosV3Router::Swap::SIGNATURE_HASH);
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(sender) = self.sender {
filter = filter.topic1(sender.into_word());
}
filter
}
#[cfg(feature = "v2")]
pub async fn get_v2_events<N, P>(
&self,
provider: &P,
) -> Result<Vec<SwapEvent>, alloy_transport::TransportError>
where
N: Network,
P: Provider<N>,
{
let filter = self.build_v2_filter();
let logs = provider.get_logs(&filter).await?;
Ok(logs
.into_iter()
.filter_map(|log| Self::decode_v2_swap_log(&log))
.collect())
}
#[cfg(feature = "v3")]
pub async fn get_v3_events<N, P>(
&self,
provider: &P,
) -> Result<Vec<SwapEvent>, alloy_transport::TransportError>
where
N: Network,
P: Provider<N>,
{
let filter = self.build_v3_filter();
let logs = provider.get_logs(&filter).await?;
Ok(logs
.into_iter()
.filter_map(|log| Self::decode_v3_swap_log(&log))
.collect())
}
#[cfg(feature = "v2")]
fn decode_v2_swap_log(log: &Log) -> Option<SwapEvent> {
let decoded = OdosV2Router::Swap::decode_log(&log.inner).ok()?;
Some(SwapEvent {
sender: decoded.data.sender,
input_token: decoded.data.inputToken,
input_amount: decoded.data.inputAmount,
output_token: decoded.data.outputToken,
amount_out: decoded.data.amountOut,
slippage: decoded.data.slippage,
referral_code: u64::from(decoded.data.referralCode),
block_number: log.block_number,
transaction_hash: log.transaction_hash,
log_index: log.log_index,
})
}
#[cfg(feature = "v3")]
fn decode_v3_swap_log(log: &Log) -> Option<SwapEvent> {
let decoded = OdosV3Router::Swap::decode_log(&log.inner).ok()?;
Some(SwapEvent {
sender: decoded.data.sender,
input_token: decoded.data.inputToken,
input_amount: decoded.data.inputAmount,
output_token: decoded.data.outputToken,
amount_out: decoded.data.amountOut,
slippage: decoded.data.slippage,
referral_code: decoded.data.referralCode,
block_number: log.block_number,
transaction_hash: log.transaction_hash,
log_index: log.log_index,
})
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct SwapMultiEvent {
pub sender: Address,
pub tokens_in: Vec<Address>,
pub amounts_in: Vec<U256>,
pub tokens_out: Vec<Address>,
pub amounts_out: Vec<U256>,
pub block_number: Option<u64>,
pub transaction_hash: Option<B256>,
pub log_index: Option<u64>,
}
#[cfg(test)]
mod tests {
use super::*;
use alloy_primitives::address;
#[test]
fn test_swap_event_filter_builder() {
let router = address!("cf5540fffcdc3d510b18bfca6d2b9987b0772559");
let sender = address!("742d35Cc6634C0532925a3b8D35f3e7a5edD29c0");
let filter = SwapEventFilter::new(router)
.from_block(18_000_000)
.to_block(18_100_000)
.sender(sender);
assert_eq!(filter.router_address, router);
assert_eq!(
filter.from_block,
Some(BlockNumberOrTag::Number(18_000_000))
);
assert_eq!(filter.to_block, Some(BlockNumberOrTag::Number(18_100_000)));
assert_eq!(filter.sender, Some(sender));
}
#[test]
fn test_swap_event_filter_latest_blocks() {
let router = address!("cf5540fffcdc3d510b18bfca6d2b9987b0772559");
let filter = SwapEventFilter::new(router).from_latest().to_latest();
assert_eq!(filter.from_block, Some(BlockNumberOrTag::Latest));
assert_eq!(filter.to_block, Some(BlockNumberOrTag::Latest));
}
#[cfg(feature = "v3")]
#[test]
fn test_build_v3_filter() {
let router = address!("cf5540fffcdc3d510b18bfca6d2b9987b0772559");
let filter = SwapEventFilter::new(router)
.from_block(18_000_000)
.build_v3_filter();
let _ = filter;
}
}