nautilus_blockchain/exchanges/parsing/uniswap_v2/
pool_created.rs1use alloy::primitives::Address;
17use nautilus_model::defi::{PoolIdentifier, rpc::RpcLog};
18use ustr::Ustr;
19
20use crate::{
21 events::pool_created::PoolCreatedEvent,
22 hypersync::{
23 HypersyncLog,
24 helpers::{
25 extract_address_from_topic, extract_block_number, validate_event_signature_hash,
26 },
27 },
28 rpc::helpers as rpc_helpers,
29};
30
31const PAIR_CREATED_EVENT_SIGNATURE_HASH: &str =
32 "0d3648bd0f6ba80134a33ba9275ac585d9d315f0ad8355cddefde31afa28d0e9";
33
34pub fn parse_pool_created_event_hypersync(log: HypersyncLog) -> anyhow::Result<PoolCreatedEvent> {
46 validate_event_signature_hash("PairCreatedEvent", PAIR_CREATED_EVENT_SIGNATURE_HASH, &log)?;
47
48 let block_number = extract_block_number(&log)?;
49 let token0 = extract_address_from_topic(&log, 1, "token0")?;
50 let token1 = extract_address_from_topic(&log, 2, "token1")?;
51
52 if let Some(data) = log.data {
53 let data_bytes = data.as_ref();
55
56 anyhow::ensure!(
57 data_bytes.len() >= 32,
58 "PairCreated event data too short: expected at least 32 bytes, was {}",
59 data_bytes.len()
60 );
61
62 let pair_address = Address::from_slice(&data_bytes[12..32]);
64 let pool_identifier = PoolIdentifier::Address(Ustr::from(&pair_address.to_string()));
65
66 Ok(PoolCreatedEvent::new(
67 block_number,
68 token0,
69 token1,
70 pair_address,
71 pool_identifier, None, None, ))
75 } else {
76 Err(anyhow::anyhow!("Missing data in pair created event log"))
77 }
78}
79
80pub fn parse_pool_created_event_rpc(log: &RpcLog) -> anyhow::Result<PoolCreatedEvent> {
86 rpc_helpers::validate_event_signature(
87 log,
88 PAIR_CREATED_EVENT_SIGNATURE_HASH,
89 "PairCreatedEvent",
90 )?;
91
92 let block_number = rpc_helpers::extract_block_number(log)?;
93 let token0 = rpc_helpers::extract_address_from_topic(log, 1, "token0")?;
94 let token1 = rpc_helpers::extract_address_from_topic(log, 2, "token1")?;
95
96 let data_bytes = rpc_helpers::extract_data_bytes(log)?;
98
99 anyhow::ensure!(
100 data_bytes.len() >= 32,
101 "PairCreated event data too short: expected at least 32 bytes, was {}",
102 data_bytes.len()
103 );
104
105 let pair_address = Address::from_slice(&data_bytes[12..32]);
107 let pool_identifier = PoolIdentifier::Address(Ustr::from(&pair_address.to_string()));
108
109 Ok(PoolCreatedEvent::new(
110 block_number,
111 token0,
112 token1,
113 pair_address,
114 pool_identifier, None, None, ))
118}
119
120#[cfg(test)]
121mod tests {
122 use rstest::{fixture, rstest};
123 use serde_json::json;
124
125 use super::*;
126
127 #[fixture]
133 fn hypersync_log_weth_usdt() -> HypersyncLog {
134 let log_json = json!({
135 "removed": null,
136 "log_index": "0x0",
137 "transaction_index": "0x1",
138 "transaction_hash": "0xe7b5c25477c6dd2425c4bc07547ffb2777e018a12eed1d348d7bf553913d97b7",
139 "block_hash": null,
140 "block_number": "0x8fcb296",
141 "address": "0xf1d7cc64fb4452f05c498126312ebe29f30fbcf9",
142 "data": "0x000000000000000000000000f64dfe17c8b87f012fcf50fbda1d62bfa148366a0000000000000000000000000000000000000000000000000000000000000001",
143 "topics": [
144 "0x0d3648bd0f6ba80134a33ba9275ac585d9d315f0ad8355cddefde31afa28d0e9",
145 "0x00000000000000000000000082af49447d8a07e3bd95bd0d56f35241523fbab1",
146 "0x000000000000000000000000af88d065e77c8cc2239327c5edb3a432268e5831"
147 ]
148 });
149 serde_json::from_value(log_json).expect("Failed to deserialize HyperSync log")
150 }
151
152 #[fixture]
153 fn rpc_log_weth_usdt() -> RpcLog {
154 let log_json = json!({
155 "removed": false,
156 "logIndex": "0x0",
157 "transactionIndex": "0x1",
158 "transactionHash": "0xe7b5c25477c6dd2425c4bc07547ffb2777e018a12eed1d348d7bf553913d97b7",
159 "blockHash": "0x5053fe02da5bb0c2fc690a467c1cc36e791047fc48c3ea4fe8bbeed069f3f7ba",
160 "blockNumber": "0x8fcb296",
161 "address": "0xf1d7cc64fb4452f05c498126312ebe29f30fbcf9",
162 "data": "0x000000000000000000000000f64dfe17c8b87f012fcf50fbda1d62bfa148366a0000000000000000000000000000000000000000000000000000000000000001",
163 "topics": [
164 "0x0d3648bd0f6ba80134a33ba9275ac585d9d315f0ad8355cddefde31afa28d0e9",
165 "0x00000000000000000000000082af49447d8a07e3bd95bd0d56f35241523fbab1",
166 "0x000000000000000000000000af88d065e77c8cc2239327c5edb3a432268e5831"
167 ]
168 });
169 serde_json::from_value(log_json).expect("Failed to deserialize RPC log")
170 }
171
172 #[rstest]
173 fn test_parse_pair_created_hypersync(hypersync_log_weth_usdt: HypersyncLog) {
174 let event =
175 parse_pool_created_event_hypersync(hypersync_log_weth_usdt).expect("Failed to parse");
176
177 assert_eq!(event.block_number, 150778518);
178 assert_eq!(
179 event.token0.to_string().to_lowercase(),
180 "0x82af49447d8a07e3bd95bd0d56f35241523fbab1"
181 );
182 assert_eq!(
183 event.token1.to_string().to_lowercase(),
184 "0xaf88d065e77c8cc2239327c5edb3a432268e5831"
185 );
186 assert_eq!(
187 event.pool_identifier.to_string(),
188 "0xF64Dfe17C8b87F012FCf50FbDA1D62bfA148366a",
189 );
190 assert_eq!(event.fee, None);
191 assert_eq!(event.tick_spacing, None);
192 }
193
194 #[rstest]
195 fn test_parse_pair_created_rpc(rpc_log_weth_usdt: RpcLog) {
196 let event = parse_pool_created_event_rpc(&rpc_log_weth_usdt).expect("Failed to parse");
197
198 assert_eq!(event.block_number, 150778518);
199 assert_eq!(
200 event.token0.to_string().to_lowercase(),
201 "0x82af49447d8a07e3bd95bd0d56f35241523fbab1"
202 );
203 assert_eq!(
204 event.token1.to_string().to_lowercase(),
205 "0xaf88d065e77c8cc2239327c5edb3a432268e5831"
206 );
207 assert_eq!(
208 event.pool_identifier.to_string(),
209 "0xF64Dfe17C8b87F012FCf50FbDA1D62bfA148366a"
210 );
211 assert_eq!(event.fee, None);
212 assert_eq!(event.tick_spacing, None);
213 }
214
215 #[rstest]
216 fn test_hypersync_rpc_match(hypersync_log_weth_usdt: HypersyncLog, rpc_log_weth_usdt: RpcLog) {
217 let hypersync_event =
218 parse_pool_created_event_hypersync(hypersync_log_weth_usdt).expect("HyperSync parse");
219 let rpc_event = parse_pool_created_event_rpc(&rpc_log_weth_usdt).expect("RPC parse");
220
221 assert_eq!(hypersync_event.block_number, rpc_event.block_number);
222 assert_eq!(hypersync_event.token0, rpc_event.token0);
223 assert_eq!(hypersync_event.token1, rpc_event.token1);
224 assert_eq!(hypersync_event.pool_identifier, rpc_event.pool_identifier);
225 assert_eq!(hypersync_event.fee, rpc_event.fee);
226 assert_eq!(hypersync_event.tick_spacing, rpc_event.tick_spacing);
227 }
228}