use crate::{normalized_actions::Action, tree::BlockTree, TreeSearchBuilder};
pub fn remove_swap_transfers(tree: &mut BlockTree<Action>) {
tree.remove_duplicate_data(
TreeSearchBuilder::default().with_action(Action::is_swap),
TreeSearchBuilder::default().with_action(Action::is_transfer),
|data| (data.node.index, data.data.clone()),
|other_nodes, node, data| {
let Some(swap_data) = data.get_ref(node.data).and_then(|node| node.first()) else {
return vec![];
};
let swap_data = swap_data.force_swap_ref();
other_nodes
.iter()
.filter_map(|(index, data)| {
let Action::Transfer(transfer) = data else {
return None;
};
if (transfer.amount == swap_data.amount_in
|| (&transfer.amount + &transfer.fee) == swap_data.amount_out)
&& (transfer.to == swap_data.pool || transfer.from == swap_data.pool)
{
return Some(*index)
}
None
})
.collect::<Vec<_>>()
},
);
}
pub fn remove_mint_transfers(tree: &mut BlockTree<Action>) {
tree.remove_duplicate_data(
TreeSearchBuilder::default().with_action(Action::is_mint),
TreeSearchBuilder::default().with_action(Action::is_transfer),
|data| (data.node.index, data.data.clone()),
|other_nodes, node, node_data| {
let Some(Action::Mint(mint_data)) =
node_data.get_ref(node.data).and_then(|node| node.first())
else {
unreachable!("value not mint")
};
other_nodes
.iter()
.filter_map(|(index, data)| {
let Action::Transfer(transfer) = data else {
return None;
};
for (amount, token) in mint_data.amount.iter().zip(&mint_data.token) {
if transfer.amount.eq(amount) && transfer.token.eq(token) {
return Some(*index)
}
}
None
})
.collect::<Vec<_>>()
},
);
}
pub fn remove_burn_transfers(tree: &mut BlockTree<Action>) {
tree.remove_duplicate_data(
TreeSearchBuilder::default().with_action(Action::is_burn),
TreeSearchBuilder::default().with_action(Action::is_transfer),
|data| (data.node.index, data.data.clone()),
|other_nodes, node, node_data| {
let Some(Action::Burn(burn_data)) =
node_data.get_ref(node.data).and_then(|node| node.first())
else {
unreachable!("value not burn")
};
other_nodes
.iter()
.filter_map(|(index, data)| {
let Action::Transfer(transfer) = data else {
return None;
};
for (amount, token) in burn_data.amount.iter().zip(&burn_data.token) {
if transfer.amount.eq(amount) && transfer.token.eq(token) {
return Some(*index)
}
}
None
})
.collect::<Vec<_>>()
},
);
}
pub fn remove_collect_transfers(tree: &mut BlockTree<Action>) {
tree.remove_duplicate_data(
TreeSearchBuilder::default().with_action(Action::is_collect),
TreeSearchBuilder::default().with_action(Action::is_transfer),
|data| (data.node.index, data.data.clone()),
|other_nodes, node, node_info| {
let Some(Action::Collect(collect_data)) =
node_info.get_ref(node.data).and_then(|node| node.first())
else {
unreachable!("value not collect")
};
other_nodes
.iter()
.filter_map(|(index, data)| {
let Action::Transfer(transfer) = data else {
return None;
};
for (amount, token) in collect_data.amount.iter().zip(&collect_data.token) {
if transfer.amount.eq(amount) && transfer.token.eq(token) {
return Some(*index)
}
}
None
})
.collect::<Vec<_>>()
},
);
}
#[cfg(test)]
pub mod test {
use std::sync::Arc;
use alloy_primitives::hex;
use brontes_classifier::test_utils::ClassifierTestUtils;
use brontes_types::{
normalized_actions::Action,
tree::{remove_swap_transfers, BlockTree},
TreeSearchBuilder,
};
#[brontes_macros::test]
pub async fn test_transfer_de_duplication() {
let tx_hash =
hex!("07a0580a713928d78caad0d09b20d23ae6fba47753b8007e6313f911fc9084be").into();
let utils = ClassifierTestUtils::new().await;
let mut tree: BlockTree<Action> = utils.build_tree_tx(tx_hash).await.unwrap();
let search_args = TreeSearchBuilder::default().with_action(Action::is_transfer);
let transfers: Vec<Action> = Arc::new(tree.clone())
.collect(&tx_hash, search_args.clone())
.collect::<Vec<_>>();
assert_eq!(transfers.len(), 4);
remove_swap_transfers(&mut tree);
let transfers: Vec<Action> = Arc::new(tree.clone())
.collect(&tx_hash, search_args)
.collect::<Vec<_>>();
assert_eq!(transfers.len(), 0);
}
}