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