nautilus_model/defi/tick_map/
liquidity_math.rs1use crate::defi::{pool_analysis::error::LiquidityMathError, tick_map::tick::PoolTick};
17
18pub fn try_liquidity_math_add(x: u128, y: i128) -> Result<u128, LiquidityMathError> {
26 if y < 0 {
27 let delta = y.unsigned_abs();
28 let z = x.wrapping_sub(delta);
29 if z >= x {
30 return Err(LiquidityMathError::Underflow { current: x, delta });
31 }
32 Ok(z)
33 } else {
34 let delta = y as u128;
35 let z = x.wrapping_add(delta);
36 if z < x {
37 return Err(LiquidityMathError::Overflow { current: x, delta });
38 }
39 Ok(z)
40 }
41}
42
43#[must_use]
60pub fn liquidity_math_add(x: u128, y: i128) -> u128 {
61 match try_liquidity_math_add(x, y) {
62 Ok(value) => value,
63 Err(LiquidityMathError::Overflow { current, delta }) => {
64 panic!("Liquidity addition overflow: x={current}, y={y}, delta={delta}")
65 }
66 Err(LiquidityMathError::Underflow { current, delta }) => {
67 panic!("Liquidity subtraction underflow: x={current}, y={y}, delta={delta}")
68 }
69 }
70}
71
72#[must_use]
78pub fn tick_spacing_to_max_liquidity_per_tick(tick_spacing: i32) -> u128 {
79 assert!(tick_spacing != 0, "Tick spacing must be non-zero");
80
81 let min_tick = (PoolTick::MIN_TICK / tick_spacing) * tick_spacing;
83 let max_tick = (PoolTick::MAX_TICK / tick_spacing) * tick_spacing;
84
85 let num_ticks = ((i64::from(max_tick) - i64::from(min_tick)) / i64::from(tick_spacing)) + 1;
87
88 u128::MAX / num_ticks as u128
89}
90
91#[cfg(test)]
92mod tests {
93 use rstest::rstest;
94
95 use super::*;
96
97 #[rstest]
98 fn test_add() {
99 assert_eq!(liquidity_math_add(1, 0), 1);
100 assert_eq!(liquidity_math_add(1, 1), 2);
101 }
102
103 #[rstest]
104 fn test_subtract_one() {
105 assert_eq!(liquidity_math_add(1, -1), 0);
106 assert_eq!(liquidity_math_add(3, -2), 1);
107 }
108
109 #[rstest]
110 #[should_panic(expected = "Liquidity addition overflow")]
111 fn test_addition_overflow() {
112 let x = u128::MAX - 14; let _ = liquidity_math_add(x, 15);
114 }
115
116 #[rstest]
117 #[should_panic(expected = "Liquidity subtraction underflow")]
118 fn test_subtraction_underflow_zero() {
119 let _ = liquidity_math_add(0, -1);
120 }
121
122 #[rstest]
123 #[should_panic(expected = "Liquidity subtraction underflow")]
124 fn test_subtraction_underflow() {
125 let _ = liquidity_math_add(3, -4);
126 }
127
128 #[rstest]
129 fn test_try_add_returns_overflow_error() {
130 let x = u128::MAX - 14;
131 let err = try_liquidity_math_add(x, 15).unwrap_err();
132 assert_eq!(
133 err,
134 LiquidityMathError::Overflow {
135 current: x,
136 delta: 15
137 }
138 );
139 }
140
141 #[rstest]
142 fn test_try_add_returns_underflow_error() {
143 let err = try_liquidity_math_add(3, -4).unwrap_err();
144 assert_eq!(
145 err,
146 LiquidityMathError::Underflow {
147 current: 3,
148 delta: 4
149 }
150 );
151 }
152
153 #[rstest]
154 fn test_tick_spacing_to_max_liquidity() {
155 assert_eq!(
157 tick_spacing_to_max_liquidity_per_tick(1),
158 191_757_530_477_355_301_479_181_766_273_477
159 );
160 assert_eq!(
162 tick_spacing_to_max_liquidity_per_tick(10),
163 1_917_569_901_783_203_986_719_870_431_555_990
164 );
165 assert_eq!(
167 tick_spacing_to_max_liquidity_per_tick(60),
168 11_505_743_598_341_114_571_880_798_222_544_994
169 );
170 assert_eq!(
172 tick_spacing_to_max_liquidity_per_tick(200),
173 38_350_317_471_085_141_830_651_933_667_504_588
174 );
175 }
176}