kabu_defi_uniswap_v3_math/
liquidity_math.rs

1use crate::error::UniswapV3MathError;
2use crate::full_math::mul_div;
3use crate::sqrt_price_math::Q96;
4use alloy::primitives::{U128, U256};
5use eyre::eyre;
6
7// returns (uint128 z)
8pub fn add_delta(x: u128, y: i128) -> Result<u128, UniswapV3MathError> {
9    if y < 0 {
10        let z = x.overflowing_sub(-y as u128);
11
12        if z.1 {
13            Err(UniswapV3MathError::LiquiditySub)
14        } else {
15            Ok(z.0)
16        }
17    } else {
18        let z = x.overflowing_add(y as u128);
19        if z.0 < x {
20            Err(UniswapV3MathError::LiquidityAdd)
21        } else {
22            Ok(z.0)
23        }
24    }
25}
26
27pub fn get_liquidity_for_amount0(sqrt_ratio_a_x_96: U256, sqrt_ratio_b_x_96: U256, amount0: U256) -> eyre::Result<u128> {
28    let (sqrt_ratio_a_x_96, sqrt_ratio_b_x_96) =
29        if sqrt_ratio_a_x_96 > sqrt_ratio_b_x_96 { (sqrt_ratio_b_x_96, sqrt_ratio_a_x_96) } else { (sqrt_ratio_a_x_96, sqrt_ratio_b_x_96) };
30
31    //let mut denominator = Q96;
32    let intermediate = mul_div(sqrt_ratio_a_x_96, sqrt_ratio_b_x_96, Q96)?;
33    let ret = mul_div(amount0, intermediate, sqrt_ratio_b_x_96 - sqrt_ratio_a_x_96)?;
34    if ret > U256::from(U128::MAX) {
35        Err(eyre!("LIQUIDITY_OVERFLOWN"))
36    } else {
37        Ok(ret.to())
38    }
39}
40
41pub fn get_liquidity_for_amount1(sqrt_ratio_a_x_96: U256, sqrt_ratio_b_x_96: U256, amount1: U256) -> eyre::Result<u128> {
42    let (sqrt_ratio_a_x_96, sqrt_ratio_b_x_96) =
43        if sqrt_ratio_a_x_96 > sqrt_ratio_b_x_96 { (sqrt_ratio_b_x_96, sqrt_ratio_a_x_96) } else { (sqrt_ratio_a_x_96, sqrt_ratio_b_x_96) };
44    let ret = mul_div(amount1, Q96, sqrt_ratio_b_x_96 - sqrt_ratio_a_x_96)?;
45    if ret > U256::from(U128::MAX) {
46        Err(eyre!("LIQUIDITY_OVERFLOWN"))
47    } else {
48        Ok(ret.to())
49    }
50}
51
52pub fn get_liquidity_for_amounts(
53    sqrt_ratio_x_96: U256,
54    sqrt_ratio_a_x_96: U256,
55    sqrt_ratio_b_x_96: U256,
56    amount0: U256,
57    amount1: U256,
58) -> eyre::Result<u128> {
59    let (sqrt_ratio_a_x_96, sqrt_ratio_b_x_96) =
60        if sqrt_ratio_a_x_96 > sqrt_ratio_b_x_96 { (sqrt_ratio_b_x_96, sqrt_ratio_a_x_96) } else { (sqrt_ratio_a_x_96, sqrt_ratio_b_x_96) };
61    let liquidity = if sqrt_ratio_x_96 <= sqrt_ratio_a_x_96 {
62        get_liquidity_for_amount0(sqrt_ratio_a_x_96, sqrt_ratio_b_x_96, amount0)?
63    } else if sqrt_ratio_x_96 < sqrt_ratio_b_x_96 {
64        let liquidity0 = get_liquidity_for_amount0(sqrt_ratio_x_96, sqrt_ratio_b_x_96, amount0)?;
65        let liquidity1 = get_liquidity_for_amount1(sqrt_ratio_a_x_96, sqrt_ratio_x_96, amount1)?;
66        if liquidity0 < liquidity1 {
67            liquidity0
68        } else {
69            liquidity1
70        }
71    } else {
72        get_liquidity_for_amount1(sqrt_ratio_a_x_96, sqrt_ratio_b_x_96, amount1)?
73    };
74    Ok(liquidity)
75}
76
77#[cfg(test)]
78mod test {
79
80    use crate::liquidity_math::add_delta;
81
82    #[test]
83    fn test_add_delta() {
84        // 1 + 0
85        let result = add_delta(1, 0);
86        assert_eq!(result.unwrap(), 1);
87
88        // 1 + -1
89        let result = add_delta(1, -1);
90        assert_eq!(result.unwrap(), 0);
91
92        // 1 + 1
93        let result = add_delta(1, 1);
94        assert_eq!(result.unwrap(), 2);
95
96        // 2**128-15 + 15 overflows
97        let result = add_delta(340282366920938463463374607431768211441, 15);
98        assert_eq!(result.err().unwrap().to_string(), "Liquidity Add");
99
100        // 0 + -1 underflows
101        let result = add_delta(0, -1);
102        assert_eq!(result.err().unwrap().to_string(), "Liquidity Sub");
103
104        // 3 + -4 underflows
105        let result = add_delta(3, -4);
106        assert_eq!(result.err().unwrap().to_string(), "Liquidity Sub");
107    }
108}