hypersync_client/
decode.rs1use crate::simple_types::Log;
2use alloy_dyn_abi::{DecodedEvent, DynSolEvent, Specifier};
3use anyhow::{Context, Result};
4use hypersync_format::LogArgument;
5use std::collections::HashMap;
6
7#[derive(Debug, Hash, Eq, PartialEq, Clone)]
8struct EventKey {
9 topic0: Vec<u8>,
10 num_topics: usize,
11}
12
13type DecoderMap = HashMap<EventKey, DynSolEvent>;
14
15#[derive(Debug)]
17pub struct Decoder {
18 map: DecoderMap,
20}
21
22impl Decoder {
23 pub fn from_signatures<S: AsRef<str>>(signatures: &[S]) -> Result<Self> {
30 let map: DecoderMap = signatures
31 .iter()
32 .map(|sig| {
33 let event =
34 alloy_json_abi::Event::parse(sig.as_ref()).context("parse event signature")?;
35 let topic0 = event.selector().to_vec();
36 let num_topics = event.num_topics();
37 let event_key = EventKey { topic0, num_topics };
38 let event = event.resolve().context("resolve event")?;
39 Ok((event_key, event))
40 })
41 .collect::<Result<DecoderMap>>()
42 .context("construct event decoder map")?;
43
44 Ok(Self { map })
45 }
46
47 pub fn decode_log(&self, log: &Log) -> Result<Option<DecodedEvent>> {
51 let topic0 = log
52 .topics
53 .first()
54 .context("get topic0")?
55 .as_ref()
56 .context("get topic0")?;
57 let data = log.data.as_ref().context("get log.data")?;
58 self.decode(topic0.as_slice(), &log.topics, data)
59 }
60
61 pub fn decode(
63 &self,
64 topic0: &[u8],
65 topics: &[Option<LogArgument>],
66 data: &[u8],
67 ) -> Result<Option<DecodedEvent>> {
68 let event_key = EventKey {
69 topic0: topic0.into(),
70 num_topics: topics.iter().fold(
71 0,
72 |accum, topic| {
73 if topic.is_some() {
74 accum + 1
75 } else {
76 accum
77 }
78 },
79 ),
80 };
81
82 let event = match self.map.get(&event_key) {
83 Some(event) => event,
84 None => return Ok(None),
85 };
86
87 let topics = topics
88 .iter()
89 .take_while(|t| t.is_some())
90 .map(|t| t.as_ref().unwrap().into());
91
92 let decoded = event
93 .decode_log_parts(topics, data)
94 .context("decode log parts")?;
95
96 Ok(Some(decoded))
97 }
98}
99
100#[cfg(test)]
101mod tests {
102 use super::*;
103 use crate::simple_types::Log;
104 use alloy_dyn_abi::{DynSolType, DynSolValue};
105 use alloy_primitives::Signed;
106 use hypersync_format::{Data, FixedSizeData, Hex};
107
108 #[test]
109 fn test_decode_event_with_bytes() {
110 let signature = alloy_json_abi::Event::parse(
111 "CommitmentStored(bytes32 indexed commitmentIndex, address bidder, address commiter, \
112 uint256 bid, uint64 blockNumber, bytes32 bidHash, uint64 decayStartTimeStamp, uint64 \
113 decayEndTimeStamp, string txnHash, string revertingTxHashes, bytes32 commitmentHash, \
114 bytes bidSignature, bytes commitmentSignature, uint64 dispatchTimestamp, bytes \
115 sharedSecretKey)",
116 )
117 .unwrap();
118 let decoder = signature.resolve().unwrap();
119 let decoder = DynSolType::Tuple(decoder.body().to_vec());
120 let data = "0x0000000000000000000000006875d4607c6cb4dfce1300545ab91a4005e33fd00000000000000000000000008280f34750068c67acf5366a5c7caea554c36fb5000000000000000000000000000000000000000000000000001b432e3907129c00000000000000000000000000000000000000000000000000000000001e3cc984c827ef3f2d18d8adca7259b89fde393749d3c2286bcd3e22d7dc8b46166d0b00000000000000000000000000000000000000000000000000000190dc7b0a0b00000000000000000000000000000000000000000000000000000190dc7b488b00000000000000000000000000000000000000000000000000000000000001c00000000000000000000000000000000000000000000000000000000000000220a7bf0040bf8800e406be82addb4f1bdc926ab7b7e4634d6cba572c6a981624ee000000000000000000000000000000000000000000000000000000000000024000000000000000000000000000000000000000000000000000000000000002c000000000000000000000000000000000000000000000000000000190dc7b2ffd000000000000000000000000000000000000000000000000000000000000034000000000000000000000000000000000000000000000000000000000000000403433666339623636366532613764306462366235313763306364313439386665366361646261373135376331353038303764616332326633376136326165656300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000041dba6cf2d4520c006bbe215eba31cbb3c26f834e6f0eeae8d29c6b86613361930618bc734e88450af5d017aeca32b0ad007368fb6699802501f1260d6a92162611b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004179c7644cf559d284118d09b2f440f19ec6a9bc8138425553229493ac0c2fc12a796d10a77958f6c007ed50d73fa2cf526d0d50ad66c824965681b37a7b3b50b51c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000203a99c7cbb18c68ce8c92ff5ee2087c35b41cf6a0c8412eeef82f5e40c3fbbc90";
121 let data = Data::decode_hex(data).unwrap();
122 let res = decoder.abi_decode_sequence(&data).unwrap();
123 dbg!(res.as_tuple().unwrap().len());
124 for v in res.as_tuple().unwrap().iter() {
125 dbg!(v);
126 if let DynSolValue::Bytes(s) = v {
127 dbg!(Data::from(s.as_slice()).encode_hex());
128 }
129 }
130 }
131 #[test]
132 fn decodes_i24_event() {
133 let decoder = Decoder::from_signatures(&["event Mint(address sender, address indexed \
137 owner, int24 indexed tickLower, int24 indexed \
138 tickUpper, uint128 amount, uint256 amount0, \
139 uint256 amount1)"])
140 .unwrap();
141
142 let log = Log {
143 removed: None,
144 log_index: Some(176.into()),
145 transaction_index: Some(0.into()),
146 transaction_hash: Some(FixedSizeData::decode_hex("0x76aeccc2815612c23344557c07fff57aada63625f1977096d5e9c88f63c257a7").unwrap()),
147 block_hash: Some(FixedSizeData::decode_hex("0xd83042b6a32dc9b18d4c7c9819b914bc04470f77458e7276cb72f3d8fde5eb3d").unwrap()),
148 block_number: Some(13899663.into()),
149 address: Some(FixedSizeData::decode_hex("0x98c7A2338336d2d354663246F64676009c7bDa97").unwrap()),
150 data: Some(Data::decode_hex("0x000000000000000000000000827922686190790b37229fd06084350e74485b72000000000000000000000000000000000000000000000000000000000bebae76000000000000000000000000000000000000000000000000000000000000270f000000000000000000000000000000000000000000000000000000000000270f").unwrap()),
151 topics: vec![
152 "0x7a53080ba414158be7ec69b987b5fb7d07dee101fe85488f0853ae16239d0bde",
153 "0x000000000000000000000000827922686190790b37229fd06084350e74485b72",
154 "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
155 "0x0000000000000000000000000000000000000000000000000000000000000001"
156 ].into_iter().map(|s|FixedSizeData::decode_hex(s).ok()).collect(),
157 };
158
159 let decoded = decoder.decode_log(&log).unwrap().unwrap();
160
161 let owner = decoded.indexed[0].clone();
162 let tick_lower = decoded.indexed[1].clone();
163 let tick_upper = decoded.indexed[2].clone();
164 assert_eq!(
165 owner,
166 DynSolValue::Address(
167 "0x827922686190790b37229fd06084350E74485b72"
168 .parse()
169 .unwrap()
170 )
171 );
172 assert_eq!(tick_lower, DynSolValue::Int(Signed::MINUS_ONE, 24));
173 assert_eq!(tick_upper, DynSolValue::Int(Signed::ONE, 24));
174 }
175}