skar_client/
decode.rs

1use std::collections::HashMap as StdHashMap;
2
3use alloy_dyn_abi::{DecodedEvent, DynSolEvent, DynSolType, Specifier};
4use alloy_json_abi::JsonAbi;
5use anyhow::{anyhow, Context, Result};
6use polars_arrow::array::BinaryViewArray;
7use skar_format::Hex;
8use xxhash_rust::xxh3::Xxh3Builder;
9
10use crate::ArrowBatch;
11
12pub type FastMap<K, V> = StdHashMap<K, V, Xxh3Builder>;
13
14pub struct Decoder {
15    contracts: FastMap<Vec<u8>, FastMap<Vec<u8>, DynSolEvent>>,
16}
17
18impl Decoder {
19    pub fn new(json_abis: &[(skar_format::Address, JsonAbi)]) -> Result<Self> {
20        let mut contracts = FastMap::default();
21
22        for (addr, abi) in json_abis.iter() {
23            let mut event_map = FastMap::default();
24
25            for (_, events) in abi.events.iter() {
26                for event in events {
27                    event_map.insert(
28                        event.selector().to_vec(),
29                        event.resolve().context("resolve event")?,
30                    );
31                }
32            }
33
34            if contracts.insert(addr.to_vec(), event_map).is_some() {
35                return Err(anyhow!("duplicate contract address {}", addr.encode_hex()));
36            }
37        }
38
39        Ok(Self { contracts })
40    }
41
42    #[inline]
43    pub fn decode(
44        &self,
45        address: &[u8],
46        topic0: &[u8],
47        topics: &[Option<&[u8]>],
48        data: &[u8],
49    ) -> Result<Option<DecodedEvent>> {
50        let contract = match self.contracts.get(address) {
51            Some(contract) => contract,
52            None => return Ok(None),
53        };
54        let event = match contract.get(topic0) {
55            Some(event) => event,
56            None => return Ok(None),
57        };
58
59        let topics = topics
60            .iter()
61            .filter_map(|&t| t.map(|t| t.try_into().unwrap()));
62
63        // Check if we are decoding a single u256 and the body is empty
64        //
65        // This case can happen when decoding zero value erc20 transfers
66        let decoded = if data.is_empty() && event.body() == [DynSolType::Uint(256)] {
67            event
68                .decode_log_parts(topics, [0; 32].as_slice(), false)
69                .context("decode log parts")?
70        } else {
71            event
72                .decode_log_parts(topics, data, false)
73                .context("decode log parts")?
74        };
75
76        Ok(Some(decoded))
77    }
78
79    pub fn decode_logs(&self, logs: &[ArrowBatch]) -> Result<Option<Vec<Option<DecodedEvent>>>> {
80        let mut events = Vec::new();
81
82        for batch in logs {
83            let address = match batch.column::<BinaryViewArray>("address") {
84                Ok(address) => address,
85                Err(_) => return Ok(None),
86            };
87            let data = match batch.column::<BinaryViewArray>("data") {
88                Ok(data) => data,
89                Err(_) => return Ok(None),
90            };
91            let topic0 = match batch.column::<BinaryViewArray>("topic0") {
92                Ok(topic0) => topic0,
93                Err(_) => return Ok(None),
94            };
95            let topic1 = match batch.column::<BinaryViewArray>("topic1") {
96                Ok(topic1) => topic1,
97                Err(_) => return Ok(None),
98            };
99            let topic2 = match batch.column::<BinaryViewArray>("topic2") {
100                Ok(topic2) => topic2,
101                Err(_) => return Ok(None),
102            };
103            let topic3 = match batch.column::<BinaryViewArray>("topic3") {
104                Ok(topic3) => topic3,
105                Err(_) => return Ok(None),
106            };
107
108            for (((((address, data), topic0), topic1), topic2), topic3) in address
109                .values_iter()
110                .zip(data.values_iter())
111                .zip(topic0.iter())
112                .zip(topic1.iter())
113                .zip(topic2.iter())
114                .zip(topic3.iter())
115            {
116                let topic0 = match topic0 {
117                    Some(topic0) => topic0,
118                    None => {
119                        events.push(None);
120                        continue;
121                    }
122                };
123
124                let decoded = self
125                    .decode(
126                        address,
127                        topic0,
128                        &[Some(topic0), topic1, topic2, topic3],
129                        data,
130                    )
131                    .context("decode event")?;
132
133                events.push(decoded);
134            }
135        }
136
137        Ok(Some(events))
138    }
139}