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
94
95
96
97
98
99
100
101
102
103
104
105
use std::fmt::Debug;

use ::serde::ser::Serializer;
use ahash::{HashSet, HashSetExt};
use clickhouse::DbRow;
use redefined::Redefined;
use reth_primitives::B256;
use rkyv::{Archive, Deserialize as rDeserialize, Serialize as rSerialize};
use serde::{ser::SerializeStruct, Deserialize, Serialize};
use serde_with::serde_as;

use crate::{
    db::redefined_types::primitives::*,
    mev::{Mev, MevType},
    normalized_actions::*,
    Protocol,
};
#[allow(unused_imports)]
use crate::{display::utils::display_sandwich, normalized_actions::NormalizedTransfer, GasDetails};

#[serde_as]
#[derive(Debug, Deserialize, PartialEq, Clone, Default, Redefined)]
#[redefined_attr(derive(Debug, PartialEq, Clone, Serialize, rSerialize, rDeserialize, Archive))]
pub struct SearcherTx {
    pub tx_hash:      B256,
    pub block_number: u64,
    pub transfers:    Vec<NormalizedTransfer>,
    #[redefined(same_fields)]
    pub gas_details:  GasDetails,
}

impl Mev for SearcherTx {
    fn mev_type(&self) -> MevType {
        MevType::SearcherTx
    }

    fn mev_transaction_hashes(&self) -> Vec<B256> {
        vec![self.tx_hash]
    }

    fn total_gas_paid(&self) -> u128 {
        self.gas_details.gas_paid()
    }

    fn total_priority_fee_paid(&self, base_fee: u128) -> u128 {
        self.gas_details.priority_fee_paid(base_fee)
    }

    fn bribe(&self) -> u128 {
        self.gas_details.coinbase_transfer.unwrap_or(0)
    }

    fn protocols(&self) -> HashSet<Protocol> {
        HashSet::new()
    }
}

impl Serialize for SearcherTx {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        let mut ser_struct = serializer.serialize_struct("SearcherTx", 9)?;

        ser_struct.serialize_field("tx_hash", &format!("{:?}", self.tx_hash))?;
        ser_struct.serialize_field("block_number", &self.block_number)?;

        let victim_transfer: ClickhouseVecNormalizedTransfer = self
            .transfers
            .clone()
            .try_into()
            .map_err(serde::ser::Error::custom)?;
        ser_struct.serialize_field("transfers.trace_idx", &victim_transfer.trace_index)?;
        ser_struct.serialize_field("transfers.from", &victim_transfer.from)?;
        ser_struct.serialize_field("transfers.to", &victim_transfer.to)?;
        ser_struct.serialize_field("transfers.token", &victim_transfer.token)?;
        ser_struct.serialize_field("transfers.amount", &victim_transfer.amount)?;
        ser_struct.serialize_field("transfers.fee", &victim_transfer.fee)?;

        let gas_details = (
            self.gas_details.coinbase_transfer,
            self.gas_details.priority_fee,
            self.gas_details.gas_used,
            self.gas_details.effective_gas_price,
        );

        ser_struct.serialize_field("gas_details", &(gas_details))?;

        ser_struct.end()
    }
}

impl DbRow for SearcherTx {
    const COLUMN_NAMES: &'static [&'static str] = &[
        "tx_hash",
        "block_number",
        "transfers.trace_idx",
        "transfers.from",
        "transfers.to",
        "transfers.token",
        "transfers.amount",
        "transfers.fee",
        "gas_details",
    ];
}