1use ahash::AHashMap;
17use alloy_primitives::U256;
18
19use crate::defi::tick_map::{
20 liquidity_math::tick_spacing_to_max_liquidity_per_tick, tick::PoolTick, tick_bitmap::TickBitmap,
21};
22
23pub mod bit_math;
24pub mod full_math;
25pub mod liquidity_math;
26pub mod sqrt_price_math;
27pub mod tick;
28pub mod tick_bitmap;
29pub mod tick_math;
30
31#[derive(Debug, Clone)]
37pub struct TickMap {
38 ticks: AHashMap<i32, PoolTick>,
40 tick_bitmap: TickBitmap,
42 pub liquidity: u128,
44 pub max_liquidity_per_tick: u128,
46}
47
48impl Default for TickMap {
49 fn default() -> Self {
50 Self::new(1)
51 }
52}
53
54impl TickMap {
55 #[must_use]
61 pub fn new(tick_spacing: u32) -> Self {
62 assert!(tick_spacing > 0, "Tick spacing must be greater than zero");
63 Self {
64 ticks: AHashMap::new(),
65 tick_bitmap: TickBitmap::new(tick_spacing),
66 liquidity: 0,
67 max_liquidity_per_tick: tick_spacing_to_max_liquidity_per_tick(tick_spacing as i32),
68 }
69 }
70
71 #[must_use]
73 pub fn get_tick(&self, tick: i32) -> Option<&PoolTick> {
74 self.ticks.get(&tick)
75 }
76
77 pub fn get_tick_or_init(&mut self, tick: i32) -> &mut PoolTick {
79 self.ticks
80 .entry(tick)
81 .or_insert_with(|| PoolTick::from_tick(tick))
82 }
83
84 pub fn get_fee_growth_inside(
86 &mut self,
87 lower_tick: i32,
88 upper_tick: i32,
89 current_tick: i32,
90 fee_growth_global_0: U256,
91 fee_growth_global_1: U256,
92 ) -> (U256, U256) {
93 self.ticks
95 .entry(lower_tick)
96 .or_insert_with(|| PoolTick::from_tick(lower_tick));
97 self.ticks
98 .entry(upper_tick)
99 .or_insert_with(|| PoolTick::from_tick(upper_tick));
100
101 let lower_tick = &self.ticks[&lower_tick];
103 let upper_tick = &self.ticks[&upper_tick];
104
105 let fee_growth_below_0 = if current_tick >= lower_tick.value {
107 lower_tick.fee_growth_outside_0
108 } else {
109 fee_growth_global_0 - lower_tick.fee_growth_outside_0
110 };
111 let fee_growth_below_1 = if current_tick >= lower_tick.value {
112 lower_tick.fee_growth_outside_1
113 } else {
114 fee_growth_global_1 - lower_tick.fee_growth_outside_1
115 };
116
117 let fee_growth_above_0 = if current_tick < upper_tick.value {
119 upper_tick.fee_growth_outside_0
120 } else {
121 fee_growth_global_0 - upper_tick.fee_growth_outside_0
122 };
123 let fee_growth_above_1 = if current_tick < upper_tick.value {
124 upper_tick.fee_growth_outside_1
125 } else {
126 fee_growth_global_1 - upper_tick.fee_growth_outside_1
127 };
128
129 let fee_growth_inside_0 = fee_growth_global_0 - fee_growth_below_0 - fee_growth_above_0;
131 let fee_growth_inside_1 = fee_growth_global_1 - fee_growth_below_1 - fee_growth_above_1;
132
133 (fee_growth_inside_0, fee_growth_inside_1)
134 }
135
136 fn update_tick_data(
138 &mut self,
139 tick: i32,
140 tick_current: i32,
141 liquidity_delta: i128,
142 upper: bool,
143 fee_growth_global_0: U256,
144 fee_growth_global_1: U256,
145 ) -> bool {
146 let max_liquidity_per_tick = self.max_liquidity_per_tick;
147 let tick = self.get_tick_or_init(tick);
148
149 let liquidity_gross_before = tick.update_liquidity(liquidity_delta, upper);
150 let liquidity_gross_after = tick.liquidity_gross;
151 assert!(
152 liquidity_gross_after <= max_liquidity_per_tick,
153 "Liquidity exceeds maximum per tick"
154 );
155
156 if liquidity_gross_before == 0 {
157 if tick.value <= tick_current {
159 tick.fee_growth_outside_0 = fee_growth_global_0;
160 tick.fee_growth_outside_1 = fee_growth_global_1;
161 }
162 tick.initialized = true;
163 }
164
165 (liquidity_gross_after == 0) != (liquidity_gross_before == 0)
167 }
168
169 pub fn update(
171 &mut self,
172 tick: i32,
173 tick_current: i32,
174 liquidity_delta: i128,
175 upper: bool,
176 fee_growth_global_0: U256,
177 fee_growth_global_1: U256,
178 ) -> bool {
179 let flipped = self.update_tick_data(
180 tick,
181 tick_current,
182 liquidity_delta,
183 upper,
184 fee_growth_global_0,
185 fee_growth_global_1,
186 );
187
188 if flipped {
190 self.tick_bitmap.flip_tick(tick);
191 }
192
193 flipped
194 }
195
196 pub fn cross_tick(
198 &mut self,
199 tick: i32,
200 fee_growth_global_0: U256,
201 fee_growth_global_1: U256,
202 ) -> i128 {
203 let tick = self.get_tick_or_init(tick);
204 tick.update_fee_growth(fee_growth_global_0, fee_growth_global_1);
205
206 tick.liquidity_net
207 }
208
209 #[must_use]
211 pub fn active_tick_count(&self) -> usize {
212 self.ticks
213 .iter()
214 .filter(|(_, tick)| self.is_tick_initialized(tick.value))
215 .count()
216 }
217
218 #[must_use]
220 pub fn total_tick_count(&self) -> usize {
221 self.ticks.len()
222 }
223
224 #[must_use]
226 pub fn get_all_ticks(&self) -> &AHashMap<i32, PoolTick> {
227 &self.ticks
228 }
229
230 pub fn set_tick(&mut self, tick_data: PoolTick) {
232 let tick = tick_data.value;
233 self.ticks.insert(tick, tick_data);
234 }
235
236 pub fn restore_tick(&mut self, tick_data: PoolTick) {
241 let is_initialized = tick_data.initialized;
242 let tick_value = tick_data.value;
243
244 self.set_tick(tick_data);
245
246 if is_initialized {
248 self.tick_bitmap.flip_tick(tick_value);
249 }
250 }
251
252 pub fn clear(&mut self, tick: i32) {
254 self.ticks.remove(&tick);
255 }
256
257 #[must_use]
259 pub fn next_initialized_tick(&self, tick: i32, lte: bool) -> (i32, bool) {
260 self.tick_bitmap
261 .next_initialized_tick_within_one_word(tick, lte)
262 }
263
264 #[must_use]
266 pub fn is_tick_initialized(&self, tick: i32) -> bool {
267 self.tick_bitmap.is_initialized(tick)
268 }
269}
270
271#[cfg(test)]
272mod tests {
273 use std::str::FromStr;
274
275 use rstest::{fixture, rstest};
276
277 use super::*;
278
279 #[fixture]
280 fn tick_map() -> TickMap {
281 TickMap::new(1)
282 }
283
284 #[rstest]
285 fn test_new_tick_maps(tick_map: TickMap) {
286 assert_eq!(tick_map.active_tick_count(), 0);
287 assert_eq!(tick_map.liquidity, 0);
288 }
289
290 #[rstest]
291 fn test_get_fee_growth_inside_uninitialized_ticks(mut tick_map: TickMap) {
292 let fee_growth_global_0 = U256::from(15);
293 let fee_growth_global_1 = U256::from(15);
294
295 let (fee_growth_inside_0, fee_growth_inside_1) =
297 tick_map.get_fee_growth_inside(-2, 2, 0, fee_growth_global_0, fee_growth_global_1);
298 assert_eq!(fee_growth_inside_0, U256::from_str("15").unwrap());
299 assert_eq!(fee_growth_inside_1, U256::from_str("15").unwrap());
300
301 let (fee_growth_inside_0, fee_growth_inside_1) =
303 tick_map.get_fee_growth_inside(-2, 2, 4, fee_growth_global_0, fee_growth_global_1);
304 assert_eq!(fee_growth_inside_0, U256::ZERO);
305 assert_eq!(fee_growth_inside_1, U256::ZERO);
306
307 let (fee_growth_inside_0, fee_growth_inside_1) =
309 tick_map.get_fee_growth_inside(-2, 2, -4, fee_growth_global_0, fee_growth_global_1);
310 assert_eq!(fee_growth_inside_0, U256::ZERO);
311 assert_eq!(fee_growth_inside_1, U256::ZERO);
312 }
313
314 #[rstest]
315 fn test_get_fee_growth_inside_if_upper_tick_is_below(mut tick_map: TickMap) {
316 tick_map.set_tick(PoolTick::new(
317 2, 0,
319 0,
320 U256::from(2),
321 U256::from(3),
322 true,
323 0,
324 ));
325 let fee_growth_global_0 = U256::from(15);
326 let fee_growth_global_1 = U256::from(15);
327 let (fee_growth_inside_0, fee_growth_inside_1) =
328 tick_map.get_fee_growth_inside(-2, 2, 0, fee_growth_global_0, fee_growth_global_1);
329 assert_eq!(fee_growth_inside_0, U256::from(13));
330 assert_eq!(fee_growth_inside_1, U256::from(12));
331 }
332
333 #[rstest]
334 fn test_get_fee_growth_inside_if_lower_tick_is_above(mut tick_map: TickMap) {
335 tick_map.set_tick(PoolTick::new(
336 -2, 0,
338 0,
339 U256::from(2),
340 U256::from(3),
341 true,
342 0,
343 ));
344 let fee_growth_global_0 = U256::from(15);
345 let fee_growth_global_1 = U256::from(15);
346 let (fee_growth_inside_0, fee_growth_inside_1) =
347 tick_map.get_fee_growth_inside(-2, 2, 0, fee_growth_global_0, fee_growth_global_1);
348 assert_eq!(fee_growth_inside_0, U256::from(13));
349 assert_eq!(fee_growth_inside_1, U256::from(12));
350 }
351
352 #[rstest]
353 fn test_get_fee_growth_inside_if_upper_and_lower_tick_are_initialized(mut tick_map: TickMap) {
354 tick_map.set_tick(PoolTick::new(
355 -2, 0,
357 0,
358 U256::from(2),
359 U256::from(3),
360 true,
361 0,
362 ));
363 tick_map.set_tick(PoolTick::new(
364 2, 0,
366 0,
367 U256::from(4),
368 U256::from(1),
369 true,
370 0,
371 ));
372 let fee_growth_global_0 = U256::from(15);
373 let fee_growth_global_1 = U256::from(15);
374 let (fee_growth_inside_0, fee_growth_inside_1) =
375 tick_map.get_fee_growth_inside(-2, 2, 0, fee_growth_global_0, fee_growth_global_1);
376 assert_eq!(fee_growth_inside_0, U256::from(9));
377 assert_eq!(fee_growth_inside_1, U256::from(11));
378 }
379
380 #[rstest]
381 #[case::current_below_range(-4, (10, 12), (4, 1), (6, 11))]
382 #[case::current_at_upper_tick(2, (2, 3), (10, 12), (8, 9))]
383 fn test_get_fee_growth_inside_outside_range(
384 mut tick_map: TickMap,
385 #[case] current_tick: i32,
386 #[case] lower_outside: (u32, u32),
387 #[case] upper_outside: (u32, u32),
388 #[case] expected_inside: (u32, u32),
389 ) {
390 tick_map.set_tick(PoolTick::new(
391 -2,
392 0,
393 0,
394 U256::from(lower_outside.0),
395 U256::from(lower_outside.1),
396 true,
397 0,
398 ));
399 tick_map.set_tick(PoolTick::new(
400 2,
401 0,
402 0,
403 U256::from(upper_outside.0),
404 U256::from(upper_outside.1),
405 true,
406 0,
407 ));
408
409 let fee_growth_inside =
410 tick_map.get_fee_growth_inside(-2, 2, current_tick, U256::from(15), U256::from(15));
411
412 assert_eq!(
413 fee_growth_inside,
414 (U256::from(expected_inside.0), U256::from(expected_inside.1))
415 );
416 }
417
418 #[rstest]
419 fn test_get_fee_growth_inside_with_overflow(mut tick_map: TickMap) {
420 tick_map.set_tick(PoolTick::new(
421 -2,
422 0,
423 0,
424 U256::MAX - U256::from(3u32), U256::MAX - U256::from(2u32), true,
427 0,
428 ));
429 tick_map.set_tick(PoolTick::new(
430 2,
431 0,
432 0,
433 U256::from(3u32),
434 U256::from(5u32),
435 true,
436 0,
437 ));
438 let fee_growth_global_0 = U256::from(15);
439 let fee_growth_global_1 = U256::from(15);
440 let (fee_growth_inside_0, fee_growth_inside_1) =
441 tick_map.get_fee_growth_inside(-2, 2, 0, fee_growth_global_0, fee_growth_global_1);
442 assert_eq!(fee_growth_inside_0, U256::from(16u32));
443 assert_eq!(fee_growth_inside_1, U256::from(13u32));
444 }
445
446 #[rstest]
447 fn test_update_flips_from_zero_to_nonzero(mut tick_map: TickMap) {
448 assert!(!tick_map.is_tick_initialized(0));
450
451 let flipped = tick_map.update(0, 0, 1, false, U256::ZERO, U256::ZERO);
452 assert!(flipped);
453
454 assert!(tick_map.is_tick_initialized(0));
456 }
457
458 #[rstest]
459 fn test_update_does_not_flip_from_nonzero_to_greater_nonzero(mut tick_map: TickMap) {
460 tick_map.update(0, 0, 1, false, U256::ZERO, U256::ZERO);
462 assert!(tick_map.is_tick_initialized(0));
463
464 let flipped = tick_map.update(0, 0, 1, false, U256::ZERO, U256::ZERO);
466 assert!(!flipped);
467
468 assert!(tick_map.is_tick_initialized(0));
470 }
471
472 #[rstest]
473 fn test_update_flips_from_nonzero_to_zero(mut tick_map: TickMap) {
474 let flipped_first = tick_map.update(0, 0, 1, false, U256::ZERO, U256::ZERO);
476 assert!(flipped_first);
477 assert!(tick_map.is_tick_initialized(0));
478
479 let flipped_second = tick_map.update(0, 0, -1, false, U256::ZERO, U256::ZERO);
481 assert!(flipped_second);
482
483 assert!(!tick_map.is_tick_initialized(0));
485 }
486
487 #[rstest]
488 fn test_update_does_not_flip_from_nonzero_to_lesser_nonzero(mut tick_map: TickMap) {
489 tick_map.update(0, 0, 2, false, U256::ZERO, U256::ZERO);
491 assert!(tick_map.is_tick_initialized(0));
492
493 let flipped = tick_map.update(0, 0, -1, false, U256::ZERO, U256::ZERO);
495 assert!(!flipped);
496
497 assert!(tick_map.is_tick_initialized(0));
499 }
500
501 #[rstest]
502 #[should_panic(expected = "Liquidity exceeds maximum per tick")]
503 fn test_update_reverts_if_total_liquidity_gross_exceeds_max() {
504 let mut tick_map = TickMap::new(200); let max_liquidity = tick_map.max_liquidity_per_tick;
508 tick_map.update(
509 0,
510 0,
511 (max_liquidity / 2) as i128,
512 false,
513 U256::ZERO,
514 U256::ZERO,
515 );
516 tick_map.update(
517 0,
518 0,
519 (max_liquidity / 2) as i128,
520 true,
521 U256::ZERO,
522 U256::ZERO,
523 );
524
525 tick_map.update(0, 0, 1, false, U256::ZERO, U256::ZERO);
527 }
528
529 #[rstest]
530 fn test_update_nets_liquidity_based_on_upper_flag(mut tick_map: TickMap) {
531 tick_map.update(0, 0, 2, false, U256::ZERO, U256::ZERO);
533 tick_map.update(0, 0, 1, true, U256::ZERO, U256::ZERO);
535 tick_map.update(0, 0, 3, true, U256::ZERO, U256::ZERO);
537 tick_map.update(0, 0, 1, false, U256::ZERO, U256::ZERO);
539
540 let tick = tick_map.get_tick(0).unwrap();
541
542 assert_eq!(tick.liquidity_gross, 7);
544
545 assert_eq!(tick.liquidity_net, -1);
547 }
548
549 #[rstest]
550 fn test_update_assumes_all_growth_happens_below_ticks_lte_current_tick() {
551 let mut tick_map = TickMap::new(1);
552 let fee_growth_global_0 = U256::from(15);
553 let fee_growth_global_1 = U256::from(2);
554 tick_map.update(1, 1, 1, false, fee_growth_global_0, fee_growth_global_1);
556
557 let tick = tick_map.get_tick(1).unwrap();
558
559 assert_eq!(tick.fee_growth_outside_0, U256::from(15u32));
561 assert_eq!(tick.fee_growth_outside_1, U256::from(2u32));
562 assert!(tick.initialized);
563 assert_eq!(tick.liquidity_gross, 1);
564 assert_eq!(tick.liquidity_net, 1);
565 }
566
567 #[rstest]
568 fn test_update_does_not_set_growth_fields_if_tick_already_initialized() {
569 let mut tick_map = TickMap::new(1);
570 let fee_growth_0_initial = U256::from(1);
571 let fee_growth_1_initial = U256::from(2);
572 tick_map.update(1, 1, 1, false, fee_growth_0_initial, fee_growth_1_initial);
574
575 let fee_growth_0_second = U256::from(6);
577 let fee_growth_1_second = U256::from(7);
578 tick_map.update(1, 1, 1, false, fee_growth_0_second, fee_growth_1_second);
579
580 let tick = tick_map.get_tick(1).unwrap();
581
582 assert_eq!(tick.fee_growth_outside_0, U256::from(1u32)); assert_eq!(tick.fee_growth_outside_1, U256::from(2u32)); assert!(tick.initialized);
586 assert_eq!(tick.liquidity_gross, 2); assert_eq!(tick.liquidity_net, 2); }
589
590 #[rstest]
591 fn test_update_does_not_set_growth_fields_for_ticks_gt_current_tick() {
592 let mut tick_map = TickMap::new(1);
593 let fee_growth_global_0 = U256::from(1);
594 let fee_growth_global_1 = U256::from(2u32);
595 tick_map.update(2, 1, 1, false, fee_growth_global_0, fee_growth_global_1);
597
598 let tick = tick_map.get_tick(2).unwrap();
599
600 assert_eq!(tick.fee_growth_outside_0, U256::ZERO); assert_eq!(tick.fee_growth_outside_1, U256::ZERO); assert!(tick.initialized);
604 assert_eq!(tick.liquidity_gross, 1);
605 assert_eq!(tick.liquidity_net, 1);
606 }
607
608 #[rstest]
609 fn test_clear_deletes_all_data_in_tick(mut tick_map: TickMap) {
610 tick_map.set_tick(PoolTick::new(
612 2,
613 3,
614 4,
615 U256::from(1u32),
616 U256::from(2u32),
617 true,
618 0,
619 ));
620
621 let tick_before = tick_map.get_tick(2).unwrap();
623 assert_eq!(tick_before.fee_growth_outside_0, U256::from(1u32));
624 assert_eq!(tick_before.fee_growth_outside_1, U256::from(2u32));
625 assert_eq!(tick_before.liquidity_gross, 3);
626 assert_eq!(tick_before.liquidity_net, 4);
627 assert!(tick_before.initialized);
628
629 tick_map.clear(2);
631
632 assert_eq!(tick_map.get_tick(2), None);
634 }
635
636 #[rstest]
637 fn test_cross_tick_flips_growth_variables(mut tick_map: TickMap) {
638 tick_map.set_tick(PoolTick::new(
640 2,
641 3,
642 4,
643 U256::from(1u32),
644 U256::from(2u32),
645 true,
646 7,
647 ));
648
649 let liquidity_net = tick_map.cross_tick(
654 2,
655 U256::from(7u32), U256::from(9u32), );
658
659 let tick = tick_map.get_tick(2).unwrap();
660
661 assert_eq!(tick.fee_growth_outside_0, U256::from(6u32)); assert_eq!(tick.fee_growth_outside_1, U256::from(7u32)); assert_eq!(liquidity_net, 4);
667
668 assert_eq!(tick.liquidity_gross, 3);
670 assert_eq!(tick.liquidity_net, 4);
671 assert!(tick.initialized);
672 }
673}