pool_sync_mantle/pools/pool_structures/
v3_structure.rs1use alloy::dyn_abi::DynSolValue;
2use alloy::primitives::{Address, U256};
3use alloy::rpc::types::Log;
4use alloy::sol_types::SolEvent;
5use serde::{Deserialize, Serialize};
6use std::collections::HashMap;
7
8use crate::events::DataEvents;
9use crate::pools::PoolType;
10
11#[derive(Debug, Clone, Serialize, Deserialize, Default)]
12pub struct UniswapV3Pool {
13 pub address: Address,
14 pub token0: Address,
15 pub token1: Address,
16 pub token0_name: String,
17 pub token1_name: String,
18 pub token0_decimals: u8,
19 pub token1_decimals: u8,
20 pub liquidity: u128,
21 pub sqrt_price: U256,
22 pub fee: u32,
23 pub tick: i32,
24 pub tick_spacing: i32,
25 pub tick_bitmap: HashMap<i16, U256>,
26 pub ticks: HashMap<i32, TickInfo>,
27}
28
29#[derive(Debug, Clone, Serialize, Deserialize, Default)]
30pub struct TickInfo {
31 pub liquidity_net: i128,
32 pub initialized: bool,
33 pub liquidity_gross: u128,
34}
35
36pub fn process_tick_data(
37 pool: &mut UniswapV3Pool,
38 log: Log,
39 _pool_type: PoolType,
40 is_initial_sync: bool,
41) {
42 let event_sig = log.topic0().unwrap();
43
44 if *event_sig == DataEvents::Burn::SIGNATURE_HASH {
45 process_burn(pool, log, is_initial_sync);
46 } else if *event_sig == DataEvents::Mint::SIGNATURE_HASH {
47 process_mint(pool, log, is_initial_sync);
48 } else if *event_sig == DataEvents::Swap::SIGNATURE_HASH {
49 process_swap(pool, log);
50 }
51}
52
53fn process_burn(pool: &mut UniswapV3Pool, log: Log, is_initial_sync: bool) {
54 let burn_event = DataEvents::Burn::decode_log(log.as_ref(), true).unwrap();
55 modify_position(
56 pool,
57 burn_event.tickLower.unchecked_into(),
58 burn_event.tickUpper.unchecked_into(),
59 -(burn_event.amount as i128),
60 is_initial_sync,
61 );
62}
63
64fn process_mint(pool: &mut UniswapV3Pool, log: Log, is_initial_sync: bool) {
65 let mint_event = DataEvents::Mint::decode_log(log.as_ref(), true).unwrap();
66 modify_position(
67 pool,
68 mint_event.tickLower.unchecked_into(),
69 mint_event.tickUpper.unchecked_into(),
70 mint_event.amount as i128,
71 is_initial_sync,
72 );
73}
74
75fn process_swap(pool: &mut UniswapV3Pool, log: Log) {
76 let swap_event = DataEvents::Swap::decode_log(log.as_ref(), true).unwrap();
77 pool.tick = swap_event.tick.as_i32();
78 pool.sqrt_price = U256::from(swap_event.sqrtPriceX96);
79 pool.liquidity = swap_event.liquidity;
80}
81
82pub fn modify_position(
84 pool: &mut UniswapV3Pool,
85 tick_lower: i32,
86 tick_upper: i32,
87 liquidity_delta: i128,
88 is_initial_sync: bool,
89) {
90 update_position(pool, tick_lower, tick_upper, liquidity_delta);
93
94 if liquidity_delta != 0 && !is_initial_sync {
96 if pool.tick >= tick_lower && pool.tick < tick_upper {
98 pool.liquidity = if liquidity_delta < 0 {
99 pool.liquidity - ((-liquidity_delta) as u128)
100 } else {
101 pool.liquidity + (liquidity_delta as u128)
102 }
103 }
104 }
105}
106
107pub fn update_position(
108 pool: &mut UniswapV3Pool,
109 tick_lower: i32,
110 tick_upper: i32,
111 liquidity_delta: i128,
112) {
113 let mut flipped_lower = false;
114 let mut flipped_upper = false;
115
116 if liquidity_delta != 0 {
117 flipped_lower = update_tick(pool, tick_lower, liquidity_delta, false);
118 flipped_upper = update_tick(pool, tick_upper, liquidity_delta, true);
119 if flipped_lower {
120 flip_tick(pool, tick_lower, pool.tick_spacing);
121 }
122 if flipped_upper {
123 flip_tick(pool, tick_upper, pool.tick_spacing);
124 }
125 }
126
127 if liquidity_delta < 0 {
128 if flipped_lower {
129 pool.ticks.remove(&tick_lower);
130 }
131
132 if flipped_upper {
133 pool.ticks.remove(&tick_upper);
134 }
135 }
136}
137
138pub fn update_tick(
139 pool: &mut UniswapV3Pool,
140 tick: i32,
141 liquidity_delta: i128,
142 upper: bool,
143) -> bool {
144 let info = match pool.ticks.get_mut(&tick) {
145 Some(info) => info,
146 None => {
147 pool.ticks.insert(tick, TickInfo::default());
148 pool.ticks
149 .get_mut(&tick)
150 .expect("Tick does not exist in ticks")
151 }
152 };
153
154 let liquidity_gross_before = info.liquidity_gross;
155
156 let liquidity_gross_after = if liquidity_delta < 0 {
157 liquidity_gross_before - ((-liquidity_delta) as u128)
158 } else {
159 liquidity_gross_before + (liquidity_delta as u128)
160 };
161
162 let flipped = (liquidity_gross_after == 0) != (liquidity_gross_before == 0);
165
166 if liquidity_gross_before == 0 {
167 info.initialized = true;
168 }
169
170 info.liquidity_gross = liquidity_gross_after;
171
172 info.liquidity_net = if upper {
173 info.liquidity_net - liquidity_delta
174 } else {
175 info.liquidity_net + liquidity_delta
176 };
177
178 flipped
179}
180
181pub fn flip_tick(pool: &mut UniswapV3Pool, tick: i32, tick_spacing: i32) {
182 let (word_pos, bit_pos) = uniswap_v3_math::tick_bitmap::position(tick / tick_spacing);
183 let mask = U256::from(1) << bit_pos;
184
185 if let Some(word) = pool.tick_bitmap.get_mut(&word_pos) {
186 *word ^= mask;
187 } else {
188 pool.tick_bitmap.insert(word_pos, mask);
189 }
190}
191
192impl From<&[DynSolValue]> for UniswapV3Pool {
193 fn from(data: &[DynSolValue]) -> Self {
194 let safe_u8_conversion = |value: &DynSolValue| -> u8 {
196 let uint_val = value.as_uint().unwrap().0;
197 if uint_val > U256::from(255u32) {
198 18 } else {
200 uint_val.try_into().unwrap_or(18)
201 }
202 };
203
204 let safe_u32_conversion = |value: &DynSolValue| -> u32 {
206 let uint_val = value.as_uint().unwrap().0;
207 if uint_val > U256::from(u32::MAX) {
208 3000 } else {
210 uint_val.try_into().unwrap_or(3000)
211 }
212 };
213
214 let safe_i32_conversion = |value: &DynSolValue| -> i32 {
216 match value.as_int() {
217 Some(signed_val) => {
218 signed_val.0.try_into().unwrap_or(0)
220 }
221 None => 0, }
223 };
224
225 Self {
226 address: data[0].as_address().unwrap(),
227 token0: data[1].as_address().unwrap(),
228 token0_decimals: safe_u8_conversion(&data[2]),
229 token1: data[3].as_address().unwrap(),
230 token1_decimals: safe_u8_conversion(&data[4]),
231 liquidity: data[5].as_uint().unwrap().0.try_into().unwrap_or(0),
232 sqrt_price: data[6].as_uint().unwrap().0,
233 tick: safe_i32_conversion(&data[7]),
234 tick_spacing: safe_i32_conversion(&data[8]),
235 fee: safe_u32_conversion(&data[9]),
236 ..Default::default()
237 }
238 }
239}