use super::big_num::U128;
use super::fixed_point_32;
use super::full_math::MulDiv;
use super::unsafe_math::UnsafeMathTrait;
pub fn get_next_sqrt_price_from_amount_0_rounding_up(
sqrt_p_x32: u64,
liquidity: u64,
amount: u64,
add: bool,
) -> u64 {
if amount == 0 {
return sqrt_p_x32;
};
let numerator_1 = (U128::from(liquidity)) << fixed_point_32::RESOLUTION;
if add {
if let Some(product) = amount.checked_mul(sqrt_p_x32) {
let denominator = numerator_1 + U128::from(product);
if denominator >= numerator_1 {
return numerator_1
.mul_div_ceil(U128::from(sqrt_p_x32), denominator)
.unwrap()
.as_u64();
};
}
U128::div_rounding_up(
numerator_1,
(numerator_1 / U128::from(sqrt_p_x32))
.checked_add(U128::from(amount))
.unwrap(),
)
.as_u64()
} else {
let product = U128::from(amount.checked_mul(sqrt_p_x32).unwrap());
assert!(numerator_1 > product);
let denominator = numerator_1 - product;
numerator_1
.mul_div_ceil(U128::from(sqrt_p_x32), denominator)
.unwrap()
.as_u64()
}
}
pub fn get_next_sqrt_price_from_amount_1_rounding_down(
sqrt_p_x32: u64,
liquidity: u64,
amount: u64,
add: bool,
) -> u64 {
if add {
let quotient = if amount <= (u32::MAX as u64) {
(amount << fixed_point_32::RESOLUTION) / liquidity
} else {
amount
.mul_div_floor(fixed_point_32::Q32, liquidity as u64)
.unwrap()
};
sqrt_p_x32.checked_add(quotient).unwrap()
} else {
let quotient = if amount <= (u32::MAX as u64) {
u64::div_rounding_up(amount << fixed_point_32::RESOLUTION, liquidity)
} else {
amount.mul_div_ceil(fixed_point_32::Q32, liquidity).unwrap()
};
assert!(sqrt_p_x32 > quotient);
sqrt_p_x32 - quotient
}
}
pub fn get_next_sqrt_price_from_input(
sqrt_p_x32: u64,
liquidity: u64,
amount_in: u64,
zero_for_one: bool,
) -> u64 {
assert!(sqrt_p_x32 > 0);
assert!(liquidity > 0);
if zero_for_one {
get_next_sqrt_price_from_amount_0_rounding_up(sqrt_p_x32, liquidity, amount_in, true)
} else {
get_next_sqrt_price_from_amount_1_rounding_down(sqrt_p_x32, liquidity, amount_in, true)
}
}
pub fn get_next_sqrt_price_from_output(
sqrt_p_x32: u64,
liquidity: u64,
amount_out: u64,
zero_for_one: bool,
) -> u64 {
assert!(sqrt_p_x32 > 0);
assert!(liquidity > 0);
if zero_for_one {
get_next_sqrt_price_from_amount_1_rounding_down(sqrt_p_x32, liquidity, amount_out, false)
} else {
get_next_sqrt_price_from_amount_0_rounding_up(sqrt_p_x32, liquidity, amount_out, false)
}
}
pub fn get_amount_0_delta_unsigned(
mut sqrt_ratio_a_x32: u64,
mut sqrt_ratio_b_x32: u64,
liquidity: u64,
round_up: bool,
) -> u64 {
if sqrt_ratio_a_x32 > sqrt_ratio_b_x32 {
std::mem::swap(&mut sqrt_ratio_a_x32, &mut sqrt_ratio_b_x32);
};
let numerator_1 = U128::from(liquidity) << fixed_point_32::RESOLUTION;
let numerator_2 = U128::from(sqrt_ratio_b_x32 - sqrt_ratio_a_x32);
assert!(sqrt_ratio_a_x32 > 0);
if round_up {
U128::div_rounding_up(
numerator_1
.mul_div_ceil(numerator_2, U128::from(sqrt_ratio_b_x32))
.unwrap(),
U128::from(sqrt_ratio_a_x32),
)
.as_u64()
} else {
(numerator_1
.mul_div_floor(numerator_2, U128::from(sqrt_ratio_b_x32))
.unwrap()
/ U128::from(sqrt_ratio_a_x32))
.as_u64()
}
}
pub fn get_amount_1_delta_unsigned(
mut sqrt_ratio_a_x32: u64,
mut sqrt_ratio_b_x32: u64,
liquidity: u64,
round_up: bool,
) -> u64 {
if sqrt_ratio_a_x32 > sqrt_ratio_b_x32 {
std::mem::swap(&mut sqrt_ratio_a_x32, &mut sqrt_ratio_b_x32);
};
if round_up {
liquidity.mul_div_ceil(sqrt_ratio_b_x32 - sqrt_ratio_a_x32, fixed_point_32::Q32)
} else {
liquidity.mul_div_floor(sqrt_ratio_b_x32 - sqrt_ratio_a_x32, fixed_point_32::Q32)
}
.unwrap()
}
pub fn get_amount_0_delta_signed(
sqrt_ratio_a_x32: u64,
sqrt_ratio_b_x32: u64,
liquidity: i64,
) -> i64 {
if liquidity < 0 {
-(get_amount_0_delta_unsigned(sqrt_ratio_a_x32, sqrt_ratio_b_x32, -liquidity as u64, false)
as i64)
} else {
get_amount_0_delta_unsigned(sqrt_ratio_a_x32, sqrt_ratio_b_x32, liquidity as u64, true)
as i64
}
}
pub fn get_amount_1_delta_signed(
sqrt_ratio_a_x32: u64,
sqrt_ratio_b_x32: u64,
liquidity: i64,
) -> i64 {
if liquidity < 0 {
-(get_amount_1_delta_unsigned(sqrt_ratio_a_x32, sqrt_ratio_b_x32, -liquidity as u64, false)
as i64)
} else {
get_amount_1_delta_unsigned(sqrt_ratio_a_x32, sqrt_ratio_b_x32, liquidity as u64, true)
as i64
}
}
#[cfg(test)]
mod sqrt_math {
use super::*;
use crate::libraries::test_utils::*;
mod get_next_sqrt_price_from_input {
use super::*;
#[test]
#[should_panic]
fn fails_if_price_is_zero() {
get_next_sqrt_price_from_input(0, 0, u64::pow(10, 17), false);
}
#[test]
#[should_panic]
fn fails_if_liquidity_is_zero() {
get_next_sqrt_price_from_input(1, 0, u64::pow(10, 8), true);
}
#[test]
#[should_panic]
fn fails_if_input_amount_overflows_the_price() {
let sqrt_p_x32 = u64::MAX;
let liquidity: u64 = 1024;
let amount_in: u64 = 1024;
get_next_sqrt_price_from_input(sqrt_p_x32, liquidity, amount_in, false);
}
#[test]
fn any_input_amount_cannot_underflow_the_price() {
let sqrt_p_x32 = 1;
let liquidity = 1;
let amount_in = u64::pow(2, 63);
assert_eq!(
get_next_sqrt_price_from_input(sqrt_p_x32, liquidity, amount_in, true),
1
);
}
#[test]
fn returns_input_price_if_amount_in_is_zero_and_zero_for_one_is_true() {
let sqrt_p_x32 = 1 * fixed_point_32::Q32;
assert_eq!(
get_next_sqrt_price_from_input(sqrt_p_x32, u64::pow(10, 8), 0, true),
sqrt_p_x32
);
}
#[test]
fn returns_input_price_if_amount_in_is_zero_and_zero_for_one_is_false() {
let sqrt_p_x32 = 1 * fixed_point_32::Q32;
assert_eq!(
get_next_sqrt_price_from_input(sqrt_p_x32, u64::pow(10, 8), 0, false),
sqrt_p_x32
);
}
#[test]
fn returns_the_minimum_price_for_max_inputs() {
let sqrt_p_x32 = u64::MAX - 1;
let liquidity = u32::MAX as u64;
let max_amount_no_overflow =
u64::MAX - ((liquidity << fixed_point_32::RESOLUTION) / sqrt_p_x32);
assert_eq!(
get_next_sqrt_price_from_input(sqrt_p_x32, liquidity, max_amount_no_overflow, true),
1
);
}
#[test]
fn input_amount_of_01_token_1() {
let sqrt_p_x32 = 1 * fixed_point_32::Q32;
let liquidity = u64::pow(10, 8);
let amount_0_in = u64::pow(10, 7); assert_eq!(
get_next_sqrt_price_from_input(sqrt_p_x32, liquidity, amount_0_in, false),
4724464025 );
}
#[test]
fn input_amount_of_01_token_0() {
let sqrt_p_x32 = 1 * fixed_point_32::Q32;
let liquidity = u64::pow(10, 8);
let amount_0_in = u64::pow(10, 7); assert_eq!(
get_next_sqrt_price_from_input(sqrt_p_x32, liquidity, amount_0_in, true),
3904515724 );
}
#[test]
fn amount_in_is_greater_than_u32_max_and_zero_for_one_is_true() {
let sqrt_p_x32 = 1 * fixed_point_32::Q32;
let liquidity = u64::pow(10, 8);
let amount_0_in = u64::pow(10, 12); assert_eq!(
get_next_sqrt_price_from_input(sqrt_p_x32, liquidity, amount_0_in, true),
429454 );
}
#[test]
fn can_return_1_with_enough_amount_in_and_zero_for_one_is_true() {
assert_eq!(
get_next_sqrt_price_from_input(encode_price_sqrt_x32(1, 1), 1, u64::MAX / 2, true),
1 );
}
}
mod get_next_sqrt_price_from_output {
use super::*;
#[test]
#[should_panic]
fn fails_if_price_is_zero() {
get_next_sqrt_price_from_output(0, 0, u64::pow(10, 17), false);
}
#[test]
#[should_panic]
fn fails_if_liquidity_is_zero() {
get_next_sqrt_price_from_output(1, 0, u64::pow(10, 17), true);
}
#[test]
#[should_panic]
fn fails_if_output_amount_is_exactly_the_virtual_reserves_of_token_0() {
let reserve_0: u64 = 4;
let reserve_1 = 262144;
let sqrt_p_x32 = encode_price_sqrt_x32(reserve_1, reserve_0); let liquidity = encode_liquidity(reserve_1, reserve_0); get_next_sqrt_price_from_output(sqrt_p_x32, liquidity, reserve_0, false);
}
#[test]
#[should_panic]
fn fails_if_output_amount_is_greater_than_virtual_reserves_of_token_0() {
let reserve_0: u64 = 4;
let reserve_1 = 262144;
let sqrt_p_x32 = encode_price_sqrt_x32(reserve_1, reserve_0); let liquidity = encode_liquidity(reserve_1, reserve_0); get_next_sqrt_price_from_output(sqrt_p_x32, liquidity, reserve_0 + 1, false);
}
#[test]
#[should_panic]
fn fails_if_output_amount_is_greater_than_virtual_reserves_of_token_1() {
let reserve_0: u64 = 4;
let reserve_1 = 262144;
let sqrt_p_x32 = encode_price_sqrt_x32(reserve_1, reserve_0); let liquidity = encode_liquidity(reserve_1, reserve_0); get_next_sqrt_price_from_output(sqrt_p_x32, liquidity, reserve_1 + 1, true);
}
#[test]
#[should_panic]
fn fails_if_output_amount_is_exactly_the_virtual_reserves_of_token_1() {
let reserve_0: u64 = 4;
let reserve_1 = 262144;
let sqrt_p_x32 = encode_price_sqrt_x32(reserve_1, reserve_0); let liquidity = encode_liquidity(reserve_1, reserve_0); get_next_sqrt_price_from_output(sqrt_p_x32, liquidity, reserve_1, true);
}
#[test]
fn succeeds_if_output_amount_is_less_than_virtual_reserves_of_token_1() {
let reserve_0: u64 = 4;
let reserve_1 = 262144;
let sqrt_p_x32 = encode_price_sqrt_x32(reserve_1, reserve_0); println!("Sqrt p {}", sqrt_p_x32);
let liquidity = encode_liquidity(reserve_1, reserve_0);
assert_eq!(
get_next_sqrt_price_from_output(sqrt_p_x32, liquidity, reserve_1 - 1, true),
4194304 );
}
#[test]
fn returns_input_price_if_amount_in_is_zero_and_zero_for_one_is_true() {
let sqrt_p_x32 = encode_price_sqrt_x32(1, 1); assert_eq!(
get_next_sqrt_price_from_output(sqrt_p_x32, u64::pow(10, 8), 0, true),
sqrt_p_x32
);
}
#[test]
fn returns_input_price_if_amount_in_is_zero_and_zero_for_one_is_false() {
let sqrt_p_x32 = encode_price_sqrt_x32(1, 1); assert_eq!(
get_next_sqrt_price_from_output(sqrt_p_x32, u64::pow(10, 8), 0, false),
sqrt_p_x32
);
}
#[test]
fn output_amount_of_01_token_1_when_zero_for_one_is_false() {
let reserve_0 = u64::pow(10, 8);
let reserve_1 = u64::pow(10, 8);
let sqrt_p_x32 = encode_price_sqrt_x32(reserve_1, reserve_0); let liquidity = encode_liquidity(reserve_1, reserve_0);
let amount_0_out = reserve_1 / 10;
assert_eq!(
get_next_sqrt_price_from_output(sqrt_p_x32, liquidity, amount_0_out, false),
4772185885 );
}
#[test]
fn output_amount_of_01_token_1_when_zero_for_one_is_true() {
let reserve_0 = u64::pow(10, 8);
let reserve_1 = u64::pow(10, 8);
let sqrt_p_x32 = encode_price_sqrt_x32(reserve_1, reserve_0); let liquidity = encode_liquidity(reserve_1, reserve_0);
let amount_1_out = reserve_1 / 10;
assert_eq!(
get_next_sqrt_price_from_output(sqrt_p_x32, liquidity, amount_1_out, true),
3865470566 );
}
#[test]
#[should_panic]
fn reverts_if_amount_out_is_impossible_in_zero_for_one_direction() {
let sqrt_p_x32 = encode_price_sqrt_x32(1, 1);
let liquidity = encode_liquidity(1, 1);
get_next_sqrt_price_from_output(sqrt_p_x32, liquidity, u64::MAX, true);
}
#[test]
#[should_panic]
fn reverts_if_amount_out_is_impossible_in_one_for_zero_direction() {
let sqrt_p_x32 = encode_price_sqrt_x32(1, 1);
let liquidity = encode_liquidity(1, 1);
get_next_sqrt_price_from_output(sqrt_p_x32, liquidity, u64::MAX, false);
}
}
mod get_amount_0_delta {
use super::*;
#[test]
fn returns_0_if_liquidity_is_0() {
assert_eq!(
get_amount_0_delta_unsigned(
encode_price_sqrt_x32(1, 1),
encode_price_sqrt_x32(2, 1),
0,
true
),
0
)
}
#[test]
fn returns_0_if_prices_are_equal() {
assert_eq!(
get_amount_0_delta_unsigned(
encode_price_sqrt_x32(100, 100),
encode_price_sqrt_x32(100, 100),
encode_liquidity(100, 100),
true
),
0
)
}
#[test]
fn returns_one_eleventh_of_amount_0_for_price_change_from_1_to_1_point_21() {
let amount_0 = get_amount_0_delta_unsigned(
encode_price_sqrt_x32(100, 100), encode_price_sqrt_x32(121, 100), u64::pow(10, 8),
true,
);
assert_eq!(amount_0, 9090910);
let amount_0_rounded_down = get_amount_0_delta_unsigned(
encode_price_sqrt_x32(1, 1),
encode_price_sqrt_x32(121, 100),
u64::pow(10, 8),
false,
);
assert_eq!(amount_0_rounded_down, amount_0 - 1); }
#[test]
fn works_for_prices_that_overflow() {
let amount_0_up = get_amount_0_delta_unsigned(
encode_price_sqrt_x32(u64::pow(2, 50), 1), encode_price_sqrt_x32(u64::pow(2, 48), 1), u64::pow(10, 8),
true,
);
assert_eq!(amount_0_up, 3);
let amount_0_down = get_amount_0_delta_unsigned(
encode_price_sqrt_x32(u64::pow(2, 50), 1), encode_price_sqrt_x32(u64::pow(2, 48), 1), u64::pow(10, 8),
false,
);
assert_eq!(amount_0_down, 2); }
}
mod get_amount_1_delta {
use super::*;
#[test]
fn returns_0_if_liquidity_is_0() {
assert_eq!(
get_amount_1_delta_unsigned(
encode_price_sqrt_x32(1, 1),
encode_price_sqrt_x32(2, 1),
0,
true
),
0
)
}
#[test]
fn returns_0_if_prices_are_equal() {
assert_eq!(
get_amount_1_delta_unsigned(
encode_price_sqrt_x32(100, 100),
encode_price_sqrt_x32(100, 100),
encode_liquidity(100, 100),
true
),
0
)
}
#[test]
fn returns_one_tenth_of_amount_1_for_price_change_from_1_to_1_point_21() {
let amount_1 = get_amount_1_delta_unsigned(
encode_price_sqrt_x32(100, 100), encode_price_sqrt_x32(121, 100), u64::pow(10, 8),
true,
);
assert_eq!(amount_1, u64::pow(10, 7) + 1);
let amount_1_rounded_down = get_amount_1_delta_unsigned(
encode_price_sqrt_x32(1, 1),
encode_price_sqrt_x32(121, 100),
u64::pow(10, 8),
false,
);
assert_eq!(amount_1_rounded_down, u64::pow(10, 7)); }
}
mod swap_computation {
use super::*;
#[test]
fn sqrt_p_by_sqrt_q_overflows() {
let amount_0_up =
get_amount_0_delta_unsigned(u64::MAX, u64::MAX - 1, u64::pow(10, 8), true);
assert_eq!(amount_0_up, 1);
let amount_0_down =
get_amount_0_delta_unsigned(u64::MAX, u64::MAX - 1, u64::pow(10, 8), false);
assert_eq!(amount_0_down, 0); }
}
}