nautilus_model/defi/tick_map/
liquidity_math.rs

1// -------------------------------------------------------------------------------------------------
2//  Copyright (C) 2015-2025 Nautech Systems Pty Ltd. All rights reserved.
3//  https://nautechsystems.io
4//
5//  Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
6//  You may not use this file except in compliance with the License.
7//  You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html
8//
9//  Unless required by applicable law or agreed to in writing, software
10//  distributed under the License is distributed on an "AS IS" BASIS,
11//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12//  See the License for the specific language governing permissions and
13//  limitations under the License.
14// -------------------------------------------------------------------------------------------------
15
16use crate::defi::tick_map::tick::PoolTick;
17
18/// Add a signed liquidity delta to liquidity and panic if it overflows or underflows.
19///
20/// # Returns
21///
22/// The resulting liquidity after applying the delta.
23///
24/// # Panics
25///
26/// This function panics if:
27/// - Adding positive delta causes overflow.
28/// - Subtracting causes underflow.
29pub fn liquidity_math_add(x: u128, y: i128) -> u128 {
30    if y < 0 {
31        let delta = (-y) as u128;
32        let z = x.wrapping_sub(delta);
33        assert!(
34            z < x,
35            "Liquidity subtraction underflow: x={x}, y={y}, delta={delta}, result={z}"
36        );
37        z
38    } else {
39        let delta = y as u128;
40        let z = x.wrapping_add(delta);
41        assert!(
42            z >= x,
43            "Liquidity addition overflow: x={x}, y={y}, delta={delta}, result={z}"
44        );
45        z
46    }
47}
48
49/// Derives max liquidity per tick from a given tick spacing
50pub fn tick_spacing_to_max_liquidity_per_tick(tick_spacing: i32) -> u128 {
51    // Calculate min and max tick aligned to tick spacing
52    let min_tick = (PoolTick::MIN_TICK / tick_spacing) * tick_spacing;
53    let max_tick = (PoolTick::MAX_TICK / tick_spacing) * tick_spacing;
54
55    // Calculate total number of ticks, cast to i64 to avoid potential overflow in subtraction
56    let num_ticks = ((max_tick as i64 - min_tick as i64) / tick_spacing as i64) + 1;
57
58    u128::MAX / num_ticks as u128
59}
60
61#[cfg(test)]
62mod tests {
63    use rstest::rstest;
64
65    use super::*;
66
67    #[rstest]
68    fn test_add() {
69        assert_eq!(liquidity_math_add(1, 0), 1);
70        assert_eq!(liquidity_math_add(1, 1), 2);
71    }
72
73    #[rstest]
74    fn test_subtract_one() {
75        assert_eq!(liquidity_math_add(1, -1), 0);
76        assert_eq!(liquidity_math_add(3, -2), 1);
77    }
78
79    #[rstest]
80    #[should_panic(expected = "Liquidity addition overflow")]
81    fn test_addition_overflow() {
82        let x = u128::MAX - 14; // Close to max so adding 15 will overflow
83        liquidity_math_add(x, 15);
84    }
85
86    #[rstest]
87    #[should_panic(expected = "Liquidity subtraction underflow")]
88    fn test_subtraction_underflow_zero() {
89        liquidity_math_add(0, -1);
90    }
91
92    #[rstest]
93    #[should_panic(expected = "Liquidity subtraction underflow")]
94    fn test_subtraction_underflow() {
95        liquidity_math_add(3, -4);
96    }
97
98    #[rstest]
99    fn test_tick_spacing_to_max_liquidity() {
100        // 0.01 tier ot 1 tick spacing
101        assert_eq!(
102            tick_spacing_to_max_liquidity_per_tick(1),
103            191757530477355301479181766273477
104        );
105        // 0.05 % tier or 10 tick spacing
106        assert_eq!(
107            tick_spacing_to_max_liquidity_per_tick(10),
108            1917569901783203986719870431555990
109        );
110        // 0.3 % tier or 60 tick spacing
111        assert_eq!(
112            tick_spacing_to_max_liquidity_per_tick(60),
113            11505743598341114571880798222544994
114        );
115        // 1.00% tier or 200 tick spacing
116        assert_eq!(
117            tick_spacing_to_max_liquidity_per_tick(200),
118            38350317471085141830651933667504588
119        );
120    }
121}