nautilus_model/defi/tick_map/
tick_bitmap.rs1use ahash::AHashMap;
17use alloy_primitives::U256;
18
19use crate::defi::tick_map::bit_math::{least_significant_bit, most_significant_bit};
20
21fn tick_position(tick: i32) -> (i16, u8) {
23 let word_pos = (tick >> 8) as i16;
24 let bit_pos = (tick & 0xFF) as u8;
25 (word_pos, bit_pos)
26}
27
28#[derive(Debug, Clone, Default)]
30pub struct TickBitmap {
31 words: AHashMap<i16, U256>,
33 tick_spacing: i32,
35}
36
37impl TickBitmap {
38 pub fn new(tick_spacing: u32) -> Self {
40 Self {
41 words: AHashMap::new(),
42 tick_spacing: tick_spacing as i32,
43 }
44 }
45
46 fn compress_tick(&self, tick: i32) -> i32 {
47 tick / self.tick_spacing
48 }
49
50 pub fn flip_tick(&mut self, tick: i32) {
56 let remainder = tick % self.tick_spacing;
57 if remainder != 0 {
58 panic!(
59 "Tick must be multiple of tick spacing: tick={}, tick_spacing={}, remainder={}",
60 tick, self.tick_spacing, remainder
61 );
62 }
63
64 let compressed_tick = self.compress_tick(tick);
65 let (word_position, bit_position) = tick_position(compressed_tick);
66
67 let word = self.words.entry(word_position).or_insert(U256::ZERO);
68
69 *word ^= U256::from(1u128) << bit_position;
71
72 if *word == U256::ZERO {
74 self.words.remove(&word_position);
75 }
76 }
77
78 pub fn is_initialized(&self, tick: i32) -> bool {
80 let compressed_tick = self.compress_tick(tick);
81 let (word_position, bit_position) = tick_position(compressed_tick);
82
83 if let Some(&word) = self.words.get(&word_position) {
84 (word & (U256::from(1u128) << bit_position)) != U256::ZERO
85 } else {
86 false
87 }
88 }
89
90 pub fn next_initialized_tick_within_one_word(
93 &self,
94 tick: i32,
95 less_than_or_equal: bool,
96 ) -> (i32, bool) {
97 let mut compressed_tick = self.compress_tick(tick);
98 if tick < 0 && tick % self.tick_spacing != 0 {
100 compressed_tick -= 1;
101 }
102
103 if less_than_or_equal {
104 let (word_pos, bit_pos) = tick_position(compressed_tick);
105 let mask =
107 (U256::from(1u128) << bit_pos) - U256::from(1u128) + (U256::from(1u128) << bit_pos);
108 let word = self.words.get(&word_pos).copied().unwrap_or(U256::ZERO);
109 let masked = word & mask;
110
111 let initialized = !masked.is_zero();
113 let next = if initialized {
115 (compressed_tick - (bit_pos as i32) + most_significant_bit(masked))
116 * self.tick_spacing
117 } else {
118 (compressed_tick - (bit_pos as i32)) * self.tick_spacing
119 };
120 (next, initialized)
121 } else {
122 let (word_pos, bit_pos) = tick_position(compressed_tick + 1);
124 let mask = !((U256::from(1u128) << bit_pos) - U256::from(1u128));
126 let word = self.words.get(&word_pos).copied().unwrap_or(U256::ZERO);
127 let masked = word & mask;
128
129 let initialized = !masked.is_zero();
131 let next = if initialized {
133 (compressed_tick + 1 + least_significant_bit(masked) - (bit_pos as i32))
134 * self.tick_spacing
135 } else {
136 (compressed_tick + 1 + (255i32) - (bit_pos as i32)) * self.tick_spacing };
138 (next, initialized)
139 }
140 }
141}
142
143#[cfg(test)]
144mod tests {
145 use rstest::{fixture, rstest};
146
147 use super::*;
148
149 #[fixture]
150 fn tick_bitmap() -> TickBitmap {
151 TickBitmap::new(1)
152 }
153
154 #[rstest]
155 fn test_tick_to_positions() {
156 assert_eq!(tick_position(256), (1, 0));
158
159 assert_eq!(tick_position(-256), (-1, 0));
161
162 assert_eq!(tick_position(100), (0, 100));
164 }
165
166 #[rstest]
167 fn test_flip_tick_toggle(mut tick_bitmap: TickBitmap) {
168 assert!(!tick_bitmap.is_initialized(100));
170
171 tick_bitmap.flip_tick(100);
173 assert!(tick_bitmap.is_initialized(100));
174
175 tick_bitmap.flip_tick(100);
177 assert!(!tick_bitmap.is_initialized(100));
178
179 assert!(!tick_bitmap.is_initialized(99));
181 assert!(!tick_bitmap.is_initialized(101));
182 }
183
184 #[rstest]
185 fn test_multiple_ticks_same_word(mut tick_bitmap: TickBitmap) {
186 tick_bitmap.flip_tick(50);
188 tick_bitmap.flip_tick(100);
189 tick_bitmap.flip_tick(200);
190
191 assert!(tick_bitmap.is_initialized(50));
192 assert!(tick_bitmap.is_initialized(100));
193 assert!(tick_bitmap.is_initialized(200));
194 assert!(!tick_bitmap.is_initialized(51));
195 }
196
197 #[rstest]
198 fn test_multiple_ticks_different_words(mut tick_bitmap: TickBitmap) {
199 tick_bitmap.flip_tick(100); tick_bitmap.flip_tick(300); tick_bitmap.flip_tick(-100); assert!(tick_bitmap.is_initialized(100));
205 assert!(tick_bitmap.is_initialized(300));
206 assert!(tick_bitmap.is_initialized(-100));
207 }
208
209 #[rstest]
210 fn test_next_initialized_tick_within_one_word_basic(mut tick_bitmap: TickBitmap) {
211 tick_bitmap.flip_tick(2); tick_bitmap.flip_tick(3); let (tick, initialized) = tick_bitmap.next_initialized_tick_within_one_word(1, false);
217 assert!(initialized);
218 assert_eq!(tick, 2); }
220
221 #[rstest]
222 fn test_next_initialized_tick_within_one_word_backward(mut tick_bitmap: TickBitmap) {
223 tick_bitmap.flip_tick(1); tick_bitmap.flip_tick(2); let (tick, initialized) = tick_bitmap.next_initialized_tick_within_one_word(3, true);
229 assert!(initialized);
230 assert_eq!(tick, 2); }
232
233 #[rstest]
234 fn test_next_initialized_tick_within_one_word_no_match(tick_bitmap: TickBitmap) {
235 let (_, initialized) = tick_bitmap.next_initialized_tick_within_one_word(60, false);
237 assert!(!initialized);
238
239 let (_, initialized) = tick_bitmap.next_initialized_tick_within_one_word(60, true);
240 assert!(!initialized);
241 }
242
243 #[rstest]
244 fn test_next_initialized_tick_with_negative_ticks(mut tick_bitmap: TickBitmap) {
245 tick_bitmap.flip_tick(-2); tick_bitmap.flip_tick(-1); let (tick, initialized) = tick_bitmap.next_initialized_tick_within_one_word(-3, false);
251 assert!(initialized);
252 assert_eq!(tick, -2); }
254
255 #[fixture]
256 fn tick_bitmap_uniswapv3_testing() -> TickBitmap {
257 let mut tick_bitmap = TickBitmap::new(1);
259 tick_bitmap.flip_tick(-200);
260 tick_bitmap.flip_tick(-55);
261 tick_bitmap.flip_tick(-4);
262 tick_bitmap.flip_tick(70);
263 tick_bitmap.flip_tick(78);
264 tick_bitmap.flip_tick(84);
265 tick_bitmap.flip_tick(139);
266 tick_bitmap.flip_tick(240);
267 tick_bitmap.flip_tick(535);
268
269 tick_bitmap
270 }
271
272 #[rstest]
273 fn test_uniswapv3_test_cases_lte_false(tick_bitmap_uniswapv3_testing: TickBitmap) {
274 let mut bitmap = tick_bitmap_uniswapv3_testing;
275
276 let (next, initialized) = bitmap.next_initialized_tick_within_one_word(78, false);
278 assert_eq!(next, 84);
279 assert!(initialized);
280 let (next, initialized) = bitmap.next_initialized_tick_within_one_word(-55, false);
281 assert_eq!(next, -4);
282 assert!(initialized);
283 let (next, initialized) = bitmap.next_initialized_tick_within_one_word(77, false);
285 assert_eq!(next, 78);
286 assert!(initialized);
287 let (next, initialized) = bitmap.next_initialized_tick_within_one_word(-56, false);
288 assert_eq!(next, -55);
289 assert!(initialized);
290 let (next, initialized) = bitmap.next_initialized_tick_within_one_word(255, false);
292 assert_eq!(next, 511); assert!(!initialized); let (next, initialized) = bitmap.next_initialized_tick_within_one_word(-257, false);
295 assert_eq!(next, -200);
296 assert!(initialized);
297 bitmap.flip_tick(340);
299 let (next, initialized) = bitmap.next_initialized_tick_within_one_word(328, false);
300 assert_eq!(next, 340);
301 assert!(initialized);
302 let (next, initialized) = bitmap.next_initialized_tick_within_one_word(508, false);
304 assert_eq!(next, 511);
305 assert!(!initialized);
306 let (next, initialized) = bitmap.next_initialized_tick_within_one_word(383, false);
308 assert_eq!(next, 511);
309 assert!(!initialized);
310 }
311
312 #[rstest]
313 fn test_uniswapv3_test_cases_lte_true(tick_bitmap_uniswapv3_testing: TickBitmap) {
314 let mut bitmap = tick_bitmap_uniswapv3_testing;
315
316 let (next, initialized) = bitmap.next_initialized_tick_within_one_word(78, true);
318 assert_eq!(next, 78);
319 assert!(initialized);
320 let (next, initialized) = bitmap.next_initialized_tick_within_one_word(79, true);
322 assert_eq!(next, 78);
323 assert!(initialized);
324 let (next, initialized) = bitmap.next_initialized_tick_within_one_word(258, true);
326 assert_eq!(next, 256);
327 assert!(!initialized);
328 let (next, initialized) = bitmap.next_initialized_tick_within_one_word(256, true);
330 assert_eq!(next, 256);
331 assert!(!initialized);
332 let (next, initialized) = bitmap.next_initialized_tick_within_one_word(255, true);
334 assert_eq!(next, 240);
335 assert!(initialized);
336 let (next, initialized) = bitmap.next_initialized_tick_within_one_word(72, true);
337 assert_eq!(next, 70);
338 assert!(initialized);
339 let (next, initialized) = bitmap.next_initialized_tick_within_one_word(-257, true);
341 assert_eq!(next, -512);
342 assert!(!initialized);
343 let (next, initialized) = bitmap.next_initialized_tick_within_one_word(1023, true);
345 assert_eq!(next, 768);
346 assert!(!initialized);
347 let (next, initialized) = bitmap.next_initialized_tick_within_one_word(900, true);
349 assert_eq!(next, 768);
350 assert!(!initialized);
351 bitmap.flip_tick(768);
353 let (next, initialized) = bitmap.next_initialized_tick_within_one_word(900, true);
354 assert_eq!(next, 768);
355 assert!(initialized);
356 }
357}