1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
use alloy_primitives::{hex, Address};
use brontes_types::{
    normalized_actions::{
        Action, MultiCallFrameClassification, MultiFrameAction, MultiFrameRequest, NodeDataIndex,
    },
    Protocol, TreeSearchBuilder,
};

use crate::multi_frame_classification::MultiCallFrameClassifier;

pub struct OneInchAggregator;
pub struct OneInchFusion;

impl MultiCallFrameClassifier for OneInchAggregator {
    const KEY: [u8; 2] = [Protocol::OneInchV5 as u8, MultiFrameAction::Aggregator as u8];

    fn create_classifier(
        request: MultiFrameRequest,
    ) -> Option<MultiCallFrameClassification<Action>> {
        Some(MultiCallFrameClassification {
            trace_index:         request.trace_idx,
            tree_search_builder: TreeSearchBuilder::new().with_actions([
                Action::is_swap,
                Action::is_transfer,
                Action::is_eth_transfer,
            ]),
            parse_fn:            Box::new(|this_action, child_nodes| {
                parse_1inch(this_action, child_nodes, false)
            }),
        })
    }
}

const FUSION_ADDRESS: Address = Address::new(hex!("A88800CD213dA5Ae406ce248380802BD53b47647"));

impl MultiCallFrameClassifier for OneInchFusion {
    const KEY: [u8; 2] = [Protocol::OneInchFusion as u8, MultiFrameAction::Aggregator as u8];

    fn create_classifier(
        request: MultiFrameRequest,
    ) -> Option<MultiCallFrameClassification<Action>> {
        Some(MultiCallFrameClassification {
            trace_index:         request.trace_idx,
            tree_search_builder: TreeSearchBuilder::new().with_actions([
                Action::is_swap,
                Action::is_transfer,
                Action::is_eth_transfer,
            ]),
            parse_fn:            Box::new(|this_action, child_nodes| {
                parse_1inch(this_action, child_nodes, true)
            }),
        })
    }
}

fn parse_1inch(
    this_action: &mut Action,
    child_nodes: Vec<(NodeDataIndex, Action)>,
    is_fusion: bool,
) -> Vec<NodeDataIndex> {
    let this = this_action.try_aggregator_mut().unwrap();
    let mut prune_nodes = Vec::new();

    for (trace_index, action) in child_nodes {
        match action {
            Action::Swap(_) | Action::SwapWithFee(_) => {
                this.child_actions.push(action.clone());
                prune_nodes.push(trace_index);
            }
            Action::Transfer(_) | Action::EthTransfer(_) if !is_fusion => {
                this.child_actions.push(action.clone());
                prune_nodes.push(trace_index);
            }
            Action::Transfer(t) if is_fusion => {
                if t.from == FUSION_ADDRESS {
                    this.recipient = t.to;
                }
                this.child_actions.push(t.into());
                prune_nodes.push(trace_index);
            }
            Action::EthTransfer(e) if is_fusion => {
                if e.from == FUSION_ADDRESS {
                    this.recipient = e.to;
                }
                this.child_actions.push(e.into());
                prune_nodes.push(trace_index);
            }

            _ => {}
        }
    }
    prune_nodes
}