nautilus_model/defi/tick_map/
tick_bitmap.rs1use std::collections::HashMap;
17
18use alloy_primitives::U256;
19
20use crate::defi::tick_map::bit_math::{least_significant_bit, most_significant_bit};
21
22fn tick_position(tick: i32) -> (i16, u8) {
24 let word_pos = (tick >> 8) as i16;
25 let bit_pos = (tick & 0xFF) as u8;
26 (word_pos, bit_pos)
27}
28
29#[derive(Debug, Clone, Default)]
31pub struct TickBitmap {
32 words: HashMap<i16, U256>,
34 tick_spacing: i32,
36}
37
38impl TickBitmap {
39 pub fn new(tick_spacing: u32) -> Self {
41 Self {
42 words: HashMap::new(),
43 tick_spacing: tick_spacing as i32,
44 }
45 }
46
47 fn compress_tick(&self, tick: i32) -> i32 {
48 tick / self.tick_spacing
49 }
50
51 pub fn flip_tick(&mut self, tick: i32) {
57 let remainder = tick % self.tick_spacing;
58 if remainder != 0 {
59 panic!(
60 "Tick must be multiple of tick spacing: tick={}, tick_spacing={}, remainder={}",
61 tick, self.tick_spacing, remainder
62 );
63 }
64
65 let compressed_tick = self.compress_tick(tick);
66 let (word_position, bit_position) = tick_position(compressed_tick);
67
68 let word = self.words.entry(word_position).or_insert(U256::ZERO);
69
70 *word ^= U256::from(1u128) << bit_position;
72
73 if *word == U256::ZERO {
75 self.words.remove(&word_position);
76 }
77 }
78
79 pub fn is_initialized(&self, tick: i32) -> bool {
81 let compressed_tick = self.compress_tick(tick);
82 let (word_position, bit_position) = tick_position(compressed_tick);
83
84 if let Some(&word) = self.words.get(&word_position) {
85 (word & (U256::from(1u128) << bit_position)) != U256::ZERO
86 } else {
87 false
88 }
89 }
90
91 pub fn next_initialized_tick_within_one_word(
94 &self,
95 tick: i32,
96 less_than_or_equal: bool,
97 ) -> (i32, bool) {
98 let mut compressed_tick = self.compress_tick(tick);
99 if tick < 0 && tick % self.tick_spacing != 0 {
101 compressed_tick -= 1;
102 }
103
104 if less_than_or_equal {
105 let (word_pos, bit_pos) = tick_position(compressed_tick);
106 let mask =
108 (U256::from(1u128) << bit_pos) - U256::from(1u128) + (U256::from(1u128) << bit_pos);
109 let word = self.words.get(&word_pos).copied().unwrap_or(U256::ZERO);
110 let masked = word & mask;
111
112 let initialized = !masked.is_zero();
114 let next = if initialized {
116 (compressed_tick - (bit_pos as i32) + most_significant_bit(masked))
117 * self.tick_spacing
118 } else {
119 (compressed_tick - (bit_pos as i32)) * self.tick_spacing
120 };
121 (next, initialized)
122 } else {
123 let (word_pos, bit_pos) = tick_position(compressed_tick + 1);
125 let mask = !((U256::from(1u128) << bit_pos) - U256::from(1u128));
127 let word = self.words.get(&word_pos).copied().unwrap_or(U256::ZERO);
128 let masked = word & mask;
129
130 let initialized = !masked.is_zero();
132 let next = if initialized {
134 (compressed_tick + 1 + least_significant_bit(masked) - (bit_pos as i32))
135 * self.tick_spacing
136 } else {
137 (compressed_tick + 1 + (255i32) - (bit_pos as i32)) * self.tick_spacing };
139 (next, initialized)
140 }
141 }
142}
143
144#[cfg(test)]
149mod tests {
150 use rstest::{fixture, rstest};
151
152 use super::*;
153
154 #[fixture]
155 fn tick_bitmap() -> TickBitmap {
156 TickBitmap::new(1)
157 }
158
159 #[rstest]
160 fn test_tick_to_positions() {
161 assert_eq!(tick_position(256), (1, 0));
163
164 assert_eq!(tick_position(-256), (-1, 0));
166
167 assert_eq!(tick_position(100), (0, 100));
169 }
170
171 #[rstest]
172 fn test_flip_tick_toggle(mut tick_bitmap: TickBitmap) {
173 assert!(!tick_bitmap.is_initialized(100));
175
176 tick_bitmap.flip_tick(100);
178 assert!(tick_bitmap.is_initialized(100));
179
180 tick_bitmap.flip_tick(100);
182 assert!(!tick_bitmap.is_initialized(100));
183
184 assert!(!tick_bitmap.is_initialized(99));
186 assert!(!tick_bitmap.is_initialized(101));
187 }
188
189 #[rstest]
190 fn test_multiple_ticks_same_word(mut tick_bitmap: TickBitmap) {
191 tick_bitmap.flip_tick(50);
193 tick_bitmap.flip_tick(100);
194 tick_bitmap.flip_tick(200);
195
196 assert!(tick_bitmap.is_initialized(50));
197 assert!(tick_bitmap.is_initialized(100));
198 assert!(tick_bitmap.is_initialized(200));
199 assert!(!tick_bitmap.is_initialized(51));
200 }
201
202 #[rstest]
203 fn test_multiple_ticks_different_words(mut tick_bitmap: TickBitmap) {
204 tick_bitmap.flip_tick(100); tick_bitmap.flip_tick(300); tick_bitmap.flip_tick(-100); assert!(tick_bitmap.is_initialized(100));
210 assert!(tick_bitmap.is_initialized(300));
211 assert!(tick_bitmap.is_initialized(-100));
212 }
213
214 #[rstest]
215 fn test_next_initialized_tick_within_one_word_basic(mut tick_bitmap: TickBitmap) {
216 tick_bitmap.flip_tick(2); tick_bitmap.flip_tick(3); let (tick, initialized) = tick_bitmap.next_initialized_tick_within_one_word(1, false);
222 assert!(initialized);
223 assert_eq!(tick, 2); }
225
226 #[rstest]
227 fn test_next_initialized_tick_within_one_word_backward(mut tick_bitmap: TickBitmap) {
228 tick_bitmap.flip_tick(1); tick_bitmap.flip_tick(2); let (tick, initialized) = tick_bitmap.next_initialized_tick_within_one_word(3, true);
234 assert!(initialized);
235 assert_eq!(tick, 2); }
237
238 #[rstest]
239 fn test_next_initialized_tick_within_one_word_no_match(tick_bitmap: TickBitmap) {
240 let (_, initialized) = tick_bitmap.next_initialized_tick_within_one_word(60, false);
242 assert!(!initialized);
243
244 let (_, initialized) = tick_bitmap.next_initialized_tick_within_one_word(60, true);
245 assert!(!initialized);
246 }
247
248 #[rstest]
249 fn test_next_initialized_tick_with_negative_ticks(mut tick_bitmap: TickBitmap) {
250 tick_bitmap.flip_tick(-2); tick_bitmap.flip_tick(-1); let (tick, initialized) = tick_bitmap.next_initialized_tick_within_one_word(-3, false);
256 assert!(initialized);
257 assert_eq!(tick, -2); }
259
260 #[fixture]
261 fn tick_bitmap_uniswapv3_testing() -> TickBitmap {
262 let mut tick_bitmap = TickBitmap::new(1);
264 tick_bitmap.flip_tick(-200);
265 tick_bitmap.flip_tick(-55);
266 tick_bitmap.flip_tick(-4);
267 tick_bitmap.flip_tick(70);
268 tick_bitmap.flip_tick(78);
269 tick_bitmap.flip_tick(84);
270 tick_bitmap.flip_tick(139);
271 tick_bitmap.flip_tick(240);
272 tick_bitmap.flip_tick(535);
273
274 tick_bitmap
275 }
276
277 #[rstest]
278 fn test_uniswapv3_test_cases_lte_false(tick_bitmap_uniswapv3_testing: TickBitmap) {
279 let mut bitmap = tick_bitmap_uniswapv3_testing;
280
281 let (next, initialized) = bitmap.next_initialized_tick_within_one_word(78, false);
283 assert_eq!(next, 84);
284 assert!(initialized);
285 let (next, initialized) = bitmap.next_initialized_tick_within_one_word(-55, false);
286 assert_eq!(next, -4);
287 assert!(initialized);
288 let (next, initialized) = bitmap.next_initialized_tick_within_one_word(77, false);
290 assert_eq!(next, 78);
291 assert!(initialized);
292 let (next, initialized) = bitmap.next_initialized_tick_within_one_word(-56, false);
293 assert_eq!(next, -55);
294 assert!(initialized);
295 let (next, initialized) = bitmap.next_initialized_tick_within_one_word(255, false);
297 assert_eq!(next, 511); assert!(!initialized); let (next, initialized) = bitmap.next_initialized_tick_within_one_word(-257, false);
300 assert_eq!(next, -200);
301 assert!(initialized);
302 bitmap.flip_tick(340);
304 let (next, initialized) = bitmap.next_initialized_tick_within_one_word(328, false);
305 assert_eq!(next, 340);
306 assert!(initialized);
307 let (next, initialized) = bitmap.next_initialized_tick_within_one_word(508, false);
309 assert_eq!(next, 511);
310 assert!(!initialized);
311 let (next, initialized) = bitmap.next_initialized_tick_within_one_word(383, false);
313 assert_eq!(next, 511);
314 assert!(!initialized);
315 }
316
317 #[rstest]
318 fn test_uniswapv3_test_cases_lte_true(tick_bitmap_uniswapv3_testing: TickBitmap) {
319 let mut bitmap = tick_bitmap_uniswapv3_testing;
320
321 let (next, initialized) = bitmap.next_initialized_tick_within_one_word(78, true);
323 assert_eq!(next, 78);
324 assert!(initialized);
325 let (next, initialized) = bitmap.next_initialized_tick_within_one_word(79, true);
327 assert_eq!(next, 78);
328 assert!(initialized);
329 let (next, initialized) = bitmap.next_initialized_tick_within_one_word(258, true);
331 assert_eq!(next, 256);
332 assert!(!initialized);
333 let (next, initialized) = bitmap.next_initialized_tick_within_one_word(256, true);
335 assert_eq!(next, 256);
336 assert!(!initialized);
337 let (next, initialized) = bitmap.next_initialized_tick_within_one_word(255, true);
339 assert_eq!(next, 240);
340 assert!(initialized);
341 let (next, initialized) = bitmap.next_initialized_tick_within_one_word(72, true);
342 assert_eq!(next, 70);
343 assert!(initialized);
344 let (next, initialized) = bitmap.next_initialized_tick_within_one_word(-257, true);
346 assert_eq!(next, -512);
347 assert!(!initialized);
348 let (next, initialized) = bitmap.next_initialized_tick_within_one_word(1023, true);
350 assert_eq!(next, 768);
351 assert!(!initialized);
352 let (next, initialized) = bitmap.next_initialized_tick_within_one_word(900, true);
354 assert_eq!(next, 768);
355 assert!(!initialized);
356 bitmap.flip_tick(768);
358 let (next, initialized) = bitmap.next_initialized_tick_within_one_word(900, true);
359 assert_eq!(next, 768);
360 assert!(initialized);
361 }
362}