From 91bea1c6fb83a00e05b74572156eb5fb97571c08 Mon Sep 17 00:00:00 2001 From: Sniezka Date: Mon, 11 Dec 2023 10:20:03 +0100 Subject: [PATCH 01/10] Added sqrt price checks --- src/contracts/entrypoints.rs | 2 ++ src/e2e/change_fee_receiver.rs | 5 +++++ src/e2e/claim.rs | 7 +++++-- src/e2e/create_pool.rs | 15 ++++++++++++--- src/e2e/cross.rs | 7 +++++-- src/e2e/cross_both_side.rs | 6 ++++-- src/e2e/limits.rs | 8 ++++++++ src/e2e/liquidity_gap.rs | 3 ++- src/e2e/max_tick_cross.rs | 4 +++- src/e2e/multiple_swap.rs | 4 +++- src/e2e/position.rs | 21 +++++++++++++++++---- src/e2e/position_list.rs | 21 ++++++++++++++++----- src/e2e/position_slippage.rs | 6 +++++- src/e2e/protocol_fee.rs | 7 +++++-- src/e2e/slippage.rs | 4 +++- src/e2e/swap.rs | 12 +++++++++--- src/e2e/swap_route.rs | 9 ++++++++- src/lib.rs | 28 ++++++++++++++++++++++++++-- src/test_helpers/entrypoints.rs | 12 +++++++----- src/test_helpers/snippets.rs | 32 ++++++++++++++++++++++++++++++-- 20 files changed, 175 insertions(+), 38 deletions(-) diff --git a/src/contracts/entrypoints.rs b/src/contracts/entrypoints.rs index 36ae49fd..cb99b35c 100644 --- a/src/contracts/entrypoints.rs +++ b/src/contracts/entrypoints.rs @@ -282,6 +282,7 @@ pub trait Invariant { /// - `token_0`: The address of the first token. /// - `token_1`: The address of the second token. /// - `fee_tier`: A struct identifying the pool fee and tick spacing. + /// - `init_sqrt_price`: The square root of the price for the initial pool related to `init_tick`. /// - `init_tick`: The initial tick at which the pool will be created. /// /// # Errors @@ -294,6 +295,7 @@ pub trait Invariant { token_0: AccountId, token_1: AccountId, fee_tier: FeeTier, + init_sqrt_price: SqrtPrice, init_tick: i32, ) -> Result<(), InvariantError>; diff --git a/src/e2e/change_fee_receiver.rs b/src/e2e/change_fee_receiver.rs index d3194dd7..2d7027fc 100644 --- a/src/e2e/change_fee_receiver.rs +++ b/src/e2e/change_fee_receiver.rs @@ -5,6 +5,7 @@ pub mod e2e_tests { contract::ContractRef, contracts::{entrypoints::Invariant, FeeTier, PoolKey}, math::types::percentage::Percentage, + math::types::sqrt_price::calculate_sqrt_price, }; use decimal::*; use ink_e2e::build_message; @@ -23,6 +24,7 @@ pub mod e2e_tests { let fee_tier = FeeTier::new(Percentage::from_scale(5, 1), 1).unwrap(); let init_tick = 0; + let init_sqrt_price = calculate_sqrt_price(init_tick).unwrap(); let alice = ink_e2e::alice(); @@ -35,6 +37,7 @@ pub mod e2e_tests { token_x, token_y, fee_tier, + init_sqrt_price, init_tick, alice ); @@ -59,6 +62,7 @@ pub mod e2e_tests { let fee_tier = FeeTier::new(Percentage::from_scale(5, 1), 100).unwrap(); let init_tick = 0; + let init_sqrt_price = calculate_sqrt_price(init_tick).unwrap(); let admin = ink_e2e::alice(); @@ -71,6 +75,7 @@ pub mod e2e_tests { token_x, token_y, fee_tier, + init_sqrt_price, init_tick, admin ); diff --git a/src/e2e/claim.rs b/src/e2e/claim.rs index 2f8b78a7..e2fc408b 100644 --- a/src/e2e/claim.rs +++ b/src/e2e/claim.rs @@ -5,8 +5,11 @@ pub mod e2e_tests { contracts::{entrypoints::Invariant, FeeTier, PoolKey}, math::{ types::{ - fee_growth::FeeGrowth, liquidity::Liquidity, percentage::Percentage, - sqrt_price::SqrtPrice, token_amount::TokenAmount, + fee_growth::FeeGrowth, + liquidity::Liquidity, + percentage::Percentage, + sqrt_price::{calculate_sqrt_price, SqrtPrice}, + token_amount::TokenAmount, }, MIN_SQRT_PRICE, }, diff --git a/src/e2e/create_pool.rs b/src/e2e/create_pool.rs index 43c8a555..4a2b1b72 100644 --- a/src/e2e/create_pool.rs +++ b/src/e2e/create_pool.rs @@ -4,6 +4,7 @@ pub mod e2e_tests { contract::ContractRef, contracts::{entrypoints::Invariant, FeeTier}, math::types::percentage::Percentage, + math::types::sqrt_price::calculate_sqrt_price, InvariantError, }; use decimal::*; @@ -20,6 +21,7 @@ pub mod e2e_tests { let fee_tier = FeeTier::new(Percentage::from_scale(5, 1), 100).unwrap(); let init_tick = 0; + let init_sqrt_price = calculate_sqrt_price(init_tick).unwrap(); let alice = ink_e2e::alice(); @@ -32,6 +34,7 @@ pub mod e2e_tests { token_x, token_y, fee_tier, + init_sqrt_price, init_tick, alice ); @@ -50,7 +53,7 @@ pub mod e2e_tests { let fee_tier = FeeTier::new(Percentage::from_scale(5, 1), 100).unwrap(); let init_tick = 0; - + let init_sqrt_price = calculate_sqrt_price(init_tick).unwrap(); let alice = ink_e2e::alice(); add_fee_tier!(client, ContractRef, dex, fee_tier, alice).unwrap(); @@ -62,6 +65,7 @@ pub mod e2e_tests { token_x, token_y, fee_tier, + init_sqrt_price, init_tick, alice ) @@ -76,6 +80,7 @@ pub mod e2e_tests { token_y, token_x, fee_tier, + init_sqrt_price, init_tick, alice ); @@ -91,7 +96,7 @@ pub mod e2e_tests { let fee_tier = FeeTier::new(Percentage::from_scale(5, 1), 100).unwrap(); let init_tick = 0; - + let init_sqrt_price = calculate_sqrt_price(init_tick).unwrap(); let alice = ink_e2e::alice(); add_fee_tier!(client, ContractRef, dex, fee_tier, alice).unwrap(); @@ -103,6 +108,7 @@ pub mod e2e_tests { token_x, token_x, fee_tier, + init_sqrt_price, init_tick, alice ); @@ -121,6 +127,7 @@ pub mod e2e_tests { let fee_tier = FeeTier::new(Percentage::from_scale(5, 1), 100).unwrap(); let init_tick = 0; + let init_sqrt_price = calculate_sqrt_price(init_tick).unwrap(); let alice = ink_e2e::alice(); @@ -131,6 +138,7 @@ pub mod e2e_tests { token_x, token_y, fee_tier, + init_sqrt_price, init_tick, alice ); @@ -150,7 +158,7 @@ pub mod e2e_tests { let fee_tier = FeeTier::new(Percentage::from_scale(5, 1), 3).unwrap(); let init_tick = 2; - + let init_sqrt_price = calculate_sqrt_price(init_tick).unwrap(); add_fee_tier!(client, ContractRef, dex, fee_tier, alice).unwrap(); let result = create_pool!( @@ -160,6 +168,7 @@ pub mod e2e_tests { token_x, token_y, fee_tier, + init_sqrt_price, init_tick, alice ); diff --git a/src/e2e/cross.rs b/src/e2e/cross.rs index 92ac0794..b66d43eb 100644 --- a/src/e2e/cross.rs +++ b/src/e2e/cross.rs @@ -5,8 +5,11 @@ pub mod e2e_tests { contracts::{entrypoints::Invariant, FeeTier, PoolKey}, math::{ types::{ - fee_growth::FeeGrowth, liquidity::Liquidity, percentage::Percentage, - sqrt_price::SqrtPrice, token_amount::TokenAmount, + fee_growth::FeeGrowth, + liquidity::Liquidity, + percentage::Percentage, + sqrt_price::{calculate_sqrt_price, SqrtPrice}, + token_amount::TokenAmount, }, MIN_SQRT_PRICE, }, diff --git a/src/e2e/cross_both_side.rs b/src/e2e/cross_both_side.rs index 452ba8bd..6be91d2d 100644 --- a/src/e2e/cross_both_side.rs +++ b/src/e2e/cross_both_side.rs @@ -30,7 +30,7 @@ pub mod e2e_tests { let fee_tier = FeeTier::new(Percentage::from_scale(6, 3), 10).unwrap(); let alice = ink_e2e::alice(); let init_tick = 0; - + let init_sqrt_price = calculate_sqrt_price(init_tick).unwrap(); let initial_mint = 10u128.pow(10); let dex = create_dex!(client, ContractRef, Percentage::from_scale(1, 2)); @@ -47,6 +47,7 @@ pub mod e2e_tests { token_x, token_y, fee_tier, + init_sqrt_price, init_tick, alice ) @@ -312,7 +313,7 @@ pub mod e2e_tests { let fee_tier = FeeTier::new(Percentage::from_scale(6, 3), 10).unwrap(); let alice = ink_e2e::alice(); let init_tick = 0; - + let init_sqrt_price = calculate_sqrt_price(init_tick).unwrap(); let initial_mint = 10u128.pow(10); let dex = create_dex!(client, ContractRef, Percentage::from_scale(1, 2)); @@ -329,6 +330,7 @@ pub mod e2e_tests { token_x, token_y, fee_tier, + init_sqrt_price, init_tick, alice ) diff --git a/src/e2e/limits.rs b/src/e2e/limits.rs index dc798197..9766cf91 100644 --- a/src/e2e/limits.rs +++ b/src/e2e/limits.rs @@ -62,6 +62,7 @@ pub mod e2e_tests { add_fee_tier!(client, ContractRef, dex, fee_tier, alice).unwrap(); let init_tick = 0; + let init_sqrt_price = calculate_sqrt_price(init_tick).unwrap(); create_pool!( client, ContractRef, @@ -69,6 +70,7 @@ pub mod e2e_tests { token_x, token_y, fee_tier, + init_sqrt_price, init_tick, alice ) @@ -140,6 +142,7 @@ pub mod e2e_tests { add_fee_tier!(client, ContractRef, dex, fee_tier, alice).unwrap(); let init_tick = get_max_tick(1); + let init_sqrt_price = calculate_sqrt_price(init_tick).unwrap(); create_pool!( client, ContractRef, @@ -147,6 +150,7 @@ pub mod e2e_tests { token_x, token_y, fee_tier, + init_sqrt_price, init_tick, alice ) @@ -202,6 +206,7 @@ pub mod e2e_tests { add_fee_tier!(client, ContractRef, dex, fee_tier, alice).unwrap(); let init_tick = 0; + let init_sqrt_price = calculate_sqrt_price(init_tick).unwrap(); create_pool!( client, ContractRef, @@ -209,6 +214,7 @@ pub mod e2e_tests { token_x, token_y, fee_tier, + init_sqrt_price, init_tick, alice ) @@ -305,6 +311,7 @@ pub mod e2e_tests { add_fee_tier!(client, ContractRef, dex, fee_tier, alice).unwrap(); let init_tick = get_max_tick(1); + let init_sqrt_price = calculate_sqrt_price(init_tick).unwrap(); create_pool!( client, ContractRef, @@ -312,6 +319,7 @@ pub mod e2e_tests { token_x, token_y, fee_tier, + init_sqrt_price, init_tick, alice ) diff --git a/src/e2e/liquidity_gap.rs b/src/e2e/liquidity_gap.rs index 2be7b9b1..47a59f83 100644 --- a/src/e2e/liquidity_gap.rs +++ b/src/e2e/liquidity_gap.rs @@ -31,7 +31,7 @@ pub mod e2e_tests { let alice = ink_e2e::alice(); let bob = ink_e2e::bob(); let init_tick = 0; - + let init_sqrt_price = calculate_sqrt_price(init_tick).unwrap(); let initial_mint = 10u128.pow(10); let dex = create_dex!(client, ContractRef, Percentage::from_scale(1, 2)); @@ -48,6 +48,7 @@ pub mod e2e_tests { token_x, token_y, fee_tier, + init_sqrt_price, init_tick, alice ) diff --git a/src/e2e/max_tick_cross.rs b/src/e2e/max_tick_cross.rs index 5c79ded1..d91ab868 100644 --- a/src/e2e/max_tick_cross.rs +++ b/src/e2e/max_tick_cross.rs @@ -6,7 +6,9 @@ pub mod e2e_tests { math::{ log::get_tick_at_sqrt_price, types::{ - liquidity::Liquidity, percentage::Percentage, sqrt_price::SqrtPrice, + liquidity::Liquidity, + percentage::Percentage, + sqrt_price::{calculate_sqrt_price, SqrtPrice}, token_amount::TokenAmount, }, MIN_SQRT_PRICE, diff --git a/src/e2e/multiple_swap.rs b/src/e2e/multiple_swap.rs index 2e71d1fe..0b20cdf2 100644 --- a/src/e2e/multiple_swap.rs +++ b/src/e2e/multiple_swap.rs @@ -5,7 +5,9 @@ pub mod e2e_tests { contracts::{entrypoints::Invariant, get_liquidity, FeeTier, PoolKey}, math::{ types::{ - fee_growth::FeeGrowth, percentage::Percentage, sqrt_price::SqrtPrice, + fee_growth::FeeGrowth, + percentage::Percentage, + sqrt_price::{calculate_sqrt_price, SqrtPrice}, token_amount::TokenAmount, }, MAX_SQRT_PRICE, MIN_SQRT_PRICE, diff --git a/src/e2e/position.rs b/src/e2e/position.rs index 70521778..508326ad 100644 --- a/src/e2e/position.rs +++ b/src/e2e/position.rs @@ -5,8 +5,11 @@ pub mod e2e_tests { contracts::{entrypoints::Invariant, FeeTier, PoolKey}, math::{ types::{ - fee_growth::FeeGrowth, liquidity::Liquidity, percentage::Percentage, - sqrt_price::SqrtPrice, token_amount::TokenAmount, + fee_growth::FeeGrowth, + liquidity::Liquidity, + percentage::Percentage, + sqrt_price::{calculate_sqrt_price, SqrtPrice}, + token_amount::TokenAmount, }, MIN_SQRT_PRICE, }, @@ -34,6 +37,8 @@ pub mod e2e_tests { add_fee_tier!(client, ContractRef, dex, fee_tier, alice).unwrap(); + let init_tick = 10; + let init_sqrt_price = calculate_sqrt_price(init_tick).unwrap(); create_pool!( client, ContractRef, @@ -41,7 +46,8 @@ pub mod e2e_tests { token_x, token_y, fee_tier, - 10, + init_sqrt_price, + init_tick, alice ) .unwrap(); @@ -74,6 +80,7 @@ pub mod e2e_tests { let alice = ink_e2e::alice(); let bob = ink_e2e::bob(); let init_tick = 0; + let init_sqrt_price = calculate_sqrt_price(init_tick).unwrap(); let remove_position_index = 0; let initial_mint = 10u128.pow(10); @@ -92,6 +99,7 @@ pub mod e2e_tests { token_x, token_y, fee_tier, + init_sqrt_price, init_tick, alice ) @@ -247,6 +255,7 @@ pub mod e2e_tests { let min_tick_test = -max_tick_test; let alice = ink_e2e::alice(); let init_tick = -23028; + let init_sqrt_price = calculate_sqrt_price(init_tick).unwrap(); let dex = create_dex!(client, ContractRef, Percentage::new(0)); let initial_balance = 100_000_000; @@ -264,6 +273,7 @@ pub mod e2e_tests { token_x, token_y, fee_tier, + init_sqrt_price, init_tick, alice ) @@ -343,7 +353,7 @@ pub mod e2e_tests { async fn test_position_below_current_tick(mut client: ink_e2e::Client) -> E2EResult<()> { let alice = ink_e2e::alice(); let init_tick = -23028; - + let init_sqrt_price = calculate_sqrt_price(init_tick).unwrap(); let dex = create_dex!(client, ContractRef, Percentage::new(0)); let initial_balance = 10_000_000_000; @@ -360,6 +370,7 @@ pub mod e2e_tests { token_x, token_y, fee_tier, + init_sqrt_price, init_tick, alice ) @@ -441,6 +452,7 @@ pub mod e2e_tests { async fn test_position_above_current_tick(mut client: ink_e2e::Client) -> E2EResult<()> { let alice = ink_e2e::alice(); let init_tick = -23028; + let init_sqrt_price = calculate_sqrt_price(init_tick).unwrap(); let dex = create_dex!(client, ContractRef, Percentage::new(0)); let initial_balance = 10_000_000_000; @@ -458,6 +470,7 @@ pub mod e2e_tests { token_x, token_y, fee_tier, + init_sqrt_price, init_tick, alice ) diff --git a/src/e2e/position_list.rs b/src/e2e/position_list.rs index 8cf2bcb1..43ee6788 100644 --- a/src/e2e/position_list.rs +++ b/src/e2e/position_list.rs @@ -4,8 +4,10 @@ pub mod e2e_tests { contract::ContractRef, contracts::{entrypoints::Invariant, FeeTier, PoolKey}, math::types::{ - fee_growth::FeeGrowth, liquidity::Liquidity, percentage::Percentage, - sqrt_price::SqrtPrice, + fee_growth::FeeGrowth, + liquidity::Liquidity, + percentage::Percentage, + sqrt_price::{calculate_sqrt_price, SqrtPrice}, }, InvariantError, }; @@ -35,7 +37,7 @@ pub mod e2e_tests { add_fee_tier!(client, ContractRef, dex, fee_tier, alice).unwrap(); let init_tick = -23028; - + let init_sqrt_price = calculate_sqrt_price(init_tick).unwrap(); create_pool!( client, ContractRef, @@ -43,6 +45,7 @@ pub mod e2e_tests { token_x, token_y, fee_tier, + init_sqrt_price, init_tick, alice ) @@ -57,7 +60,7 @@ pub mod e2e_tests { async fn test_add_multiple_positions(mut client: ink_e2e::Client) -> E2EResult<()> { let alice = ink_e2e::alice(); let init_tick = -23028; - + let init_sqrt_price = calculate_sqrt_price(init_tick).unwrap(); let dex = create_dex!(client, ContractRef, Percentage::new(0)); let initial_balance = 10u128.pow(10); @@ -74,6 +77,7 @@ pub mod e2e_tests { token_x, token_y, fee_tier, + init_sqrt_price, init_tick, alice ) @@ -259,6 +263,7 @@ pub mod e2e_tests { ) -> E2EResult<()> { let alice = ink_e2e::alice(); let init_tick = -23028; + let init_sqrt_price = calculate_sqrt_price(init_tick).unwrap(); let dex = create_dex!(client, ContractRef, Percentage::new(0)); let initial_balance = 10u128.pow(10); @@ -276,6 +281,7 @@ pub mod e2e_tests { token_x, token_y, fee_tier, + init_sqrt_price, init_tick, alice ) @@ -424,6 +430,7 @@ pub mod e2e_tests { async fn test_transfer_position_ownership(mut client: ink_e2e::Client) -> E2EResult<()> { let alice = ink_e2e::alice(); let init_tick = -23028; + let init_sqrt_price = calculate_sqrt_price(init_tick).unwrap(); let dex = create_dex!(client, ContractRef, Percentage::new(0)); let initial_balance = 10u128.pow(10); @@ -441,6 +448,7 @@ pub mod e2e_tests { token_x, token_y, fee_tier, + init_sqrt_price, init_tick, alice ) @@ -681,6 +689,7 @@ pub mod e2e_tests { ) -> E2EResult<()> { let alice = ink_e2e::alice(); let init_tick = -23028; + let init_sqrt_price = calculate_sqrt_price(init_tick).unwrap(); let dex = create_dex!(client, ContractRef, Percentage::new(0)); let initial_balance = 10u128.pow(10); @@ -698,6 +707,7 @@ pub mod e2e_tests { token_x, token_y, fee_tier, + init_sqrt_price, init_tick, alice ) @@ -795,7 +805,7 @@ pub mod e2e_tests { ) -> E2EResult<()> { let alice = ink_e2e::alice(); let init_tick = 0; - + let init_sqrt_price = calculate_sqrt_price(init_tick).unwrap(); let dex = create_dex!(client, ContractRef, Percentage::new(0)); let initial_balance = 100_000_000; @@ -812,6 +822,7 @@ pub mod e2e_tests { token_x, token_y, fee_tier, + init_sqrt_price, init_tick, alice ) diff --git a/src/e2e/position_slippage.rs b/src/e2e/position_slippage.rs index 53a660c8..527b2663 100644 --- a/src/e2e/position_slippage.rs +++ b/src/e2e/position_slippage.rs @@ -3,7 +3,11 @@ pub mod e2e_tests { use crate::{ contract::ContractRef, contracts::{entrypoints::Invariant, FeeTier, PoolKey}, - math::types::{liquidity::Liquidity, percentage::Percentage, sqrt_price::SqrtPrice}, + math::types::{ + liquidity::Liquidity, + percentage::Percentage, + sqrt_price::{calculate_sqrt_price, SqrtPrice}, + }, InvariantError, }; use decimal::*; diff --git a/src/e2e/protocol_fee.rs b/src/e2e/protocol_fee.rs index 7365a3ea..68880db0 100644 --- a/src/e2e/protocol_fee.rs +++ b/src/e2e/protocol_fee.rs @@ -6,8 +6,11 @@ pub mod e2e_tests { contracts::{entrypoints::Invariant, FeeTier, PoolKey}, math::{ types::{ - fee_growth::FeeGrowth, liquidity::Liquidity, percentage::Percentage, - sqrt_price::SqrtPrice, token_amount::TokenAmount, + fee_growth::FeeGrowth, + liquidity::Liquidity, + percentage::Percentage, + sqrt_price::{calculate_sqrt_price, SqrtPrice}, + token_amount::TokenAmount, }, MIN_SQRT_PRICE, }, diff --git a/src/e2e/slippage.rs b/src/e2e/slippage.rs index 7a145e9f..233072fd 100644 --- a/src/e2e/slippage.rs +++ b/src/e2e/slippage.rs @@ -5,7 +5,9 @@ pub mod e2e_tests { contracts::{entrypoints::Invariant, FeeTier, PoolKey}, math::{ types::{ - liquidity::Liquidity, percentage::Percentage, sqrt_price::SqrtPrice, + liquidity::Liquidity, + percentage::Percentage, + sqrt_price::{calculate_sqrt_price, SqrtPrice}, token_amount::TokenAmount, }, MAX_SQRT_PRICE, MIN_SQRT_PRICE, diff --git a/src/e2e/swap.rs b/src/e2e/swap.rs index 38b8138a..a2aafba0 100644 --- a/src/e2e/swap.rs +++ b/src/e2e/swap.rs @@ -6,7 +6,7 @@ pub mod e2e_tests { math::{ types::{ fee_growth::FeeGrowth, liquidity::Liquidity, percentage::Percentage, - sqrt_price::SqrtPrice, token_amount::TokenAmount, + sqrt_price::calculate_sqrt_price, sqrt_price::SqrtPrice, token_amount::TokenAmount, }, MAX_SQRT_PRICE, MIN_SQRT_PRICE, }, @@ -43,6 +43,8 @@ pub mod e2e_tests { add_fee_tier!(client, ContractRef, dex, fee_tier, alice).unwrap(); + let init_tick = 0; + let init_sqrt_price = calculate_sqrt_price(init_tick).unwrap(); create_pool!( client, ContractRef, @@ -50,7 +52,8 @@ pub mod e2e_tests { token_x, token_y, fee_tier, - 0, + init_sqrt_price, + init_tick, alice ) .unwrap(); @@ -198,6 +201,8 @@ pub mod e2e_tests { add_fee_tier!(client, ContractRef, dex, fee_tier, alice).unwrap(); + let init_tick = 0; + let init_sqrt_price = calculate_sqrt_price(init_tick).unwrap(); create_pool!( client, ContractRef, @@ -205,7 +210,8 @@ pub mod e2e_tests { token_x, token_y, fee_tier, - 0, + init_sqrt_price, + init_tick, alice ) .unwrap(); diff --git a/src/e2e/swap_route.rs b/src/e2e/swap_route.rs index abaabe02..9a8eb42f 100644 --- a/src/e2e/swap_route.rs +++ b/src/e2e/swap_route.rs @@ -3,7 +3,10 @@ pub mod e2e_tests { use crate::{ contract::{ContractRef, Hop}, contracts::{entrypoints::Invariant, FeeTier, PoolKey}, - math::types::{liquidity::Liquidity, percentage::Percentage, token_amount::TokenAmount}, + math::types::{ + liquidity::Liquidity, percentage::Percentage, sqrt_price::calculate_sqrt_price, + token_amount::TokenAmount, + }, }; use decimal::*; use ink_e2e::build_message; @@ -37,6 +40,7 @@ pub mod e2e_tests { add_fee_tier!(client, ContractRef, dex, fee_tier, alice).unwrap(); let init_tick = 0; + let init_sqrt_price = calculate_sqrt_price(init_tick).unwrap(); create_pool!( client, ContractRef, @@ -44,12 +48,14 @@ pub mod e2e_tests { token_x, token_y, fee_tier, + init_sqrt_price, init_tick, alice ) .unwrap(); let init_tick = 0; + let init_sqrt_price = calculate_sqrt_price(init_tick).unwrap(); create_pool!( client, ContractRef, @@ -57,6 +63,7 @@ pub mod e2e_tests { token_y, token_z, fee_tier, + init_sqrt_price, init_tick, alice ) diff --git a/src/lib.rs b/src/lib.rs index c6f9d011..98943dda 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -33,6 +33,7 @@ pub enum InvariantError { InvalidFee, NotEmptyTickDeinitialization, InvalidInitTick, + InvalidInitSqrtPrice, } #[ink::contract] pub mod contract { @@ -888,6 +889,7 @@ pub mod contract { token_0: AccountId, token_1: AccountId, fee_tier: FeeTier, + init_sqrt_price: SqrtPrice, init_tick: i32, ) -> Result<(), InvariantError> { let current_timestamp = self.env().block_timestamp(); @@ -899,6 +901,15 @@ pub mod contract { check_tick(init_tick, fee_tier.tick_spacing) .map_err(|_| InvariantError::InvalidInitTick)?; + let upper_bound = unwrap!(SqrtPrice::from_tick(init_tick)); + let lower_bound = unwrap!(SqrtPrice::from_tick( + init_tick - fee_tier.tick_spacing as i32 + )); + + if init_sqrt_price > upper_bound || init_sqrt_price <= lower_bound { + return Err(InvariantError::InvalidInitSqrtPrice); + } + let pool_key = PoolKey::new(token_0, token_1, fee_tier)?; if self.pools.get(pool_key).is_ok() { return Err(InvariantError::PoolAlreadyExist); @@ -951,6 +962,7 @@ pub mod contract { use crate::math::consts::MAX_TICK; use crate::math::percentage::Percentage; + use crate::math::sqrt_price::calculate_sqrt_price; #[ink::test] fn initialize_works() { @@ -967,6 +979,8 @@ pub mod contract { tick_spacing: 1, }; + let init_sqrt_price = calculate_sqrt_price(0).unwrap(); + contract.add_fee_tier(fee_tier).unwrap(); let result = contract.create_pool( @@ -976,6 +990,7 @@ pub mod contract { fee: Percentage::new(1), tick_spacing: 1, }, + init_sqrt_price, 0, ); assert!(result.is_ok()); @@ -986,6 +1001,7 @@ pub mod contract { fee: Percentage::new(1), tick_spacing: 1, }, + init_sqrt_price, 0, ); assert_eq!(result, Err(InvariantError::PoolAlreadyExist)); @@ -996,6 +1012,7 @@ pub mod contract { let mut contract = Contract::new(Percentage::new(0)); let token_0 = AccountId::from([0x01; 32]); let token_1 = AccountId::from([0x02; 32]); + let init_sqrt_price = calculate_sqrt_price(0).unwrap(); let result = contract.get_pool( token_1, token_0, @@ -1013,7 +1030,7 @@ pub mod contract { contract.add_fee_tier(fee_tier).unwrap(); - let result = contract.create_pool(token_0, token_1, fee_tier, 0); + let result = contract.create_pool(token_0, token_1, fee_tier, init_sqrt_price, 0); assert!(result.is_ok()); let result = contract.get_pool( token_1, @@ -1029,6 +1046,7 @@ pub mod contract { #[ink::test] fn create_tick() { let mut contract = Contract::new(Percentage::new(0)); + let init_sqrt_price = calculate_sqrt_price(0).unwrap(); let token_0 = AccountId::from([0x01; 32]); let token_1 = AccountId::from([0x02; 32]); let fee_tier = FeeTier { @@ -1044,7 +1062,13 @@ pub mod contract { assert_eq!(result, Err(InvariantError::PoolNotFound)); contract.add_fee_tier(fee_tier).unwrap(); - let _ = contract.create_pool(pool_key.token_x, pool_key.token_y, pool_key.fee_tier, 0); + let _ = contract.create_pool( + pool_key.token_x, + pool_key.token_y, + pool_key.fee_tier, + init_sqrt_price, + 0, + ); let result = contract.create_tick(pool_key, 0); assert!(result.is_ok()); } diff --git a/src/test_helpers/entrypoints.rs b/src/test_helpers/entrypoints.rs index 96950f98..fe8b0285 100644 --- a/src/test_helpers/entrypoints.rs +++ b/src/test_helpers/entrypoints.rs @@ -399,17 +399,19 @@ macro_rules! remove_fee_tier { #[macro_export] macro_rules! create_pool { - ($client:ident, $dex:ty, $dex_address:expr, $token_0:expr, $token_1:expr, $fee_tier:expr, $init_tick:expr, $caller:ident) => {{ - let message = build_message::<$dex>($dex_address.clone()) - .call(|contract| contract.create_pool($token_0, $token_1, $fee_tier, $init_tick)); + ($client:ident, $dex:ty, $dex_address:expr, $token_0:expr, $token_1:expr, $fee_tier:expr, $init_sqrt_price:expr, $init_tick:expr, $caller:ident) => {{ + let message = build_message::<$dex>($dex_address.clone()).call(|contract| { + contract.create_pool($token_0, $token_1, $fee_tier, $init_sqrt_price, $init_tick) + }); let result = $client .call_dry_run(&$caller, &message, 0, None) .await .return_value(); if result.is_ok() { - let message = build_message::<$dex>($dex_address.clone()) - .call(|contract| contract.create_pool($token_0, $token_1, $fee_tier, $init_tick)); + let message = build_message::<$dex>($dex_address.clone()).call(|contract| { + contract.create_pool($token_0, $token_1, $fee_tier, $init_sqrt_price, $init_tick) + }); $client .call(&$caller, message, 0, None) .await diff --git a/src/test_helpers/snippets.rs b/src/test_helpers/snippets.rs index 958f5283..9fc52107 100644 --- a/src/test_helpers/snippets.rs +++ b/src/test_helpers/snippets.rs @@ -197,6 +197,7 @@ macro_rules! init_basic_pool { add_fee_tier!($client, $dex, $dex_address, fee_tier, alice).unwrap(); let init_tick = 0; + let init_sqrt_price = calculate_sqrt_price(init_tick).unwrap(); create_pool!( $client, $dex, @@ -204,6 +205,7 @@ macro_rules! init_basic_pool { $token_x_address, $token_y_address, fee_tier, + init_sqrt_price, init_tick, alice ) @@ -222,6 +224,7 @@ macro_rules! init_slippage_pool_with_liquidity { add_fee_tier!($client, $dex, $dex_address, fee_tier, alice).unwrap(); let init_tick = 0; + let init_sqrt_price = calculate_sqrt_price(init_tick).unwrap(); create_pool!( $client, $dex, @@ -229,6 +232,7 @@ macro_rules! init_slippage_pool_with_liquidity { $token_x_address, $token_y_address, fee_tier, + init_sqrt_price, init_tick, alice ) @@ -683,7 +687,19 @@ macro_rules! big_deposit_and_swap { add_fee_tier!($client, $dex, dex, fee_tier, alice).unwrap(); let init_tick = 0; - create_pool!($client, $dex, dex, token_x, token_y, fee_tier, init_tick, alice).unwrap(); + let init_sqrt_price = calculate_sqrt_price(init_tick).unwrap(); + create_pool!( + $client, + $dex, + dex, + token_x, + token_y, + fee_tier, + init_sqrt_price, + init_tick, + alice + ) + .unwrap(); let lower_tick = if $x_to_y { -(fee_tier.tick_spacing as i32) @@ -790,7 +806,19 @@ macro_rules! multiple_swap { add_fee_tier!($client, $dex, dex, fee_tier, alice).unwrap(); let init_tick = 0; - create_pool!($client, $dex, dex, token_x, token_y, fee_tier, init_tick, alice).unwrap(); + let init_sqrt_price = calculate_sqrt_price(init_tick).unwrap(); + create_pool!( + $client, + $dex, + dex, + token_x, + token_y, + fee_tier, + init_sqrt_price, + init_tick, + alice + ) + .unwrap(); let mint_amount = 10u128.pow(10); approve!($client, $token, token_x, dex, mint_amount, alice).unwrap(); From 46d52c7761ee114f66301a076ae98516ed418eb7 Mon Sep 17 00:00:00 2001 From: Sniezka Date: Mon, 11 Dec 2023 10:29:04 +0100 Subject: [PATCH 02/10] Configured creating pool and tests --- src/contracts/storage/pool.rs | 20 ++++++++++++-------- src/lib.rs | 7 ++++++- 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/src/contracts/storage/pool.rs b/src/contracts/storage/pool.rs index 5e1a4979..4f7eed47 100644 --- a/src/contracts/storage/pool.rs +++ b/src/contracts/storage/pool.rs @@ -5,11 +5,8 @@ use crate::{ clamm::*, log::get_tick_at_sqrt_price, types::{ - fee_growth::FeeGrowth, - liquidity::Liquidity, - percentage::Percentage, - sqrt_price::{calculate_sqrt_price, SqrtPrice}, - token_amount::TokenAmount, + fee_growth::FeeGrowth, liquidity::Liquidity, percentage::Percentage, + sqrt_price::SqrtPrice, token_amount::TokenAmount, }, }, }; @@ -58,9 +55,14 @@ impl Default for Pool { } impl Pool { - pub fn create(init_tick: i32, current_timestamp: u64, fee_receiver: AccountId) -> Self { + pub fn create( + init_sqrt_price: SqrtPrice, + init_tick: i32, + current_timestamp: u64, + fee_receiver: AccountId, + ) -> Self { Self { - sqrt_price: unwrap!(calculate_sqrt_price(init_tick)), + sqrt_price: init_sqrt_price, current_tick_index: init_tick, start_timestamp: current_timestamp, last_timestamp: current_timestamp, @@ -195,6 +197,7 @@ impl Pool { #[cfg(test)] mod tests { + use crate::math::types::sqrt_price::calculate_sqrt_price; use decimal::Factories; use super::*; @@ -204,8 +207,9 @@ mod tests { let init_tick = 100; let current_timestamp = 100; let fee_receiver = AccountId::from([1; 32]); + let init_sqrt_price = calculate_sqrt_price(init_tick).unwrap(); - let pool = Pool::create(init_tick, current_timestamp, fee_receiver); + let pool = Pool::create(init_sqrt_price, init_tick, current_timestamp, fee_receiver); assert_eq!(pool.sqrt_price, calculate_sqrt_price(init_tick).unwrap()); assert_eq!(pool.current_tick_index, init_tick); diff --git a/src/lib.rs b/src/lib.rs index 98943dda..c78494fb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -914,7 +914,12 @@ pub mod contract { if self.pools.get(pool_key).is_ok() { return Err(InvariantError::PoolAlreadyExist); }; - let pool = Pool::create(init_tick, current_timestamp, self.state.admin); + let pool = Pool::create( + init_sqrt_price, + init_tick, + current_timestamp, + self.state.admin, + ); self.pools.add(pool_key, &pool)?; self.pool_keys.add(pool_key)?; From 8989b0f25950c20d4fa3bd149756ca29eb379bda Mon Sep 17 00:00:00 2001 From: Sniezka Date: Mon, 11 Dec 2023 10:45:03 +0100 Subject: [PATCH 03/10] Updated error in entrypoints.rs --- src/contracts/entrypoints.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/contracts/entrypoints.rs b/src/contracts/entrypoints.rs index cb99b35c..fcf159ea 100644 --- a/src/contracts/entrypoints.rs +++ b/src/contracts/entrypoints.rs @@ -289,6 +289,9 @@ pub trait Invariant { /// - Fails if the specified fee tier cannot be found. /// - Fails if the user attempts to create a pool for the same tokens. /// - Fails if Pool with same tokens and fee tier already exist. + /// - Fails if the init tick is not divisible by the tick spacing. + /// - Fails if the init sqrt price is not related to the init tick. + /// #[ink(message)] fn create_pool( &mut self, From 0846a84a4ff4b3a38be4fb003b7f0e0fcaa32b08 Mon Sep 17 00:00:00 2001 From: Sniezka Date: Mon, 11 Dec 2023 11:10:37 +0100 Subject: [PATCH 04/10] Moved validation to storage layer --- src/contracts/storage/pool.rs | 23 +++++++++++++++++++---- src/lib.rs | 12 ++---------- 2 files changed, 21 insertions(+), 14 deletions(-) diff --git a/src/contracts/storage/pool.rs b/src/contracts/storage/pool.rs index 4f7eed47..188d2311 100644 --- a/src/contracts/storage/pool.rs +++ b/src/contracts/storage/pool.rs @@ -9,6 +9,7 @@ use crate::{ sqrt_price::SqrtPrice, token_amount::TokenAmount, }, }, + InvariantError, }; use decimal::*; @@ -59,16 +60,23 @@ impl Pool { init_sqrt_price: SqrtPrice, init_tick: i32, current_timestamp: u64, + tick_spacing: u16, fee_receiver: AccountId, - ) -> Self { - Self { + ) -> Result { + let upper_bound = unwrap!(SqrtPrice::from_tick(init_tick)); + let lower_bound = unwrap!(SqrtPrice::from_tick(init_tick - tick_spacing as i32)); + + if init_sqrt_price > upper_bound || init_sqrt_price <= lower_bound { + return Err(InvariantError::InvalidInitSqrtPrice); + } + Ok(Self { sqrt_price: init_sqrt_price, current_tick_index: init_tick, start_timestamp: current_timestamp, last_timestamp: current_timestamp, fee_receiver, ..Self::default() - } + }) } pub fn add_fee( @@ -209,7 +217,14 @@ mod tests { let fee_receiver = AccountId::from([1; 32]); let init_sqrt_price = calculate_sqrt_price(init_tick).unwrap(); - let pool = Pool::create(init_sqrt_price, init_tick, current_timestamp, fee_receiver); + let pool = Pool::create( + init_sqrt_price, + init_tick, + current_timestamp, + 1, + fee_receiver, + ) + .unwrap(); assert_eq!(pool.sqrt_price, calculate_sqrt_price(init_tick).unwrap()); assert_eq!(pool.current_tick_index, init_tick); diff --git a/src/lib.rs b/src/lib.rs index c78494fb..c55183e7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -901,15 +901,6 @@ pub mod contract { check_tick(init_tick, fee_tier.tick_spacing) .map_err(|_| InvariantError::InvalidInitTick)?; - let upper_bound = unwrap!(SqrtPrice::from_tick(init_tick)); - let lower_bound = unwrap!(SqrtPrice::from_tick( - init_tick - fee_tier.tick_spacing as i32 - )); - - if init_sqrt_price > upper_bound || init_sqrt_price <= lower_bound { - return Err(InvariantError::InvalidInitSqrtPrice); - } - let pool_key = PoolKey::new(token_0, token_1, fee_tier)?; if self.pools.get(pool_key).is_ok() { return Err(InvariantError::PoolAlreadyExist); @@ -918,8 +909,9 @@ pub mod contract { init_sqrt_price, init_tick, current_timestamp, + fee_tier.tick_spacing, self.state.admin, - ); + )?; self.pools.add(pool_key, &pool)?; self.pool_keys.add(pool_key)?; From a9abce2c83baeb9fb663ce10d6539e1cbcd25c9a Mon Sep 17 00:00:00 2001 From: Sniezka Date: Mon, 11 Dec 2023 11:36:07 +0100 Subject: [PATCH 05/10] Added tests to validate tick and price relations --- src/e2e/create_pool.rs | 96 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 95 insertions(+), 1 deletion(-) diff --git a/src/e2e/create_pool.rs b/src/e2e/create_pool.rs index 4a2b1b72..0f866908 100644 --- a/src/e2e/create_pool.rs +++ b/src/e2e/create_pool.rs @@ -4,7 +4,7 @@ pub mod e2e_tests { contract::ContractRef, contracts::{entrypoints::Invariant, FeeTier}, math::types::percentage::Percentage, - math::types::sqrt_price::calculate_sqrt_price, + math::types::sqrt_price::{calculate_sqrt_price, SqrtPrice}, InvariantError, }; use decimal::*; @@ -177,4 +177,98 @@ pub mod e2e_tests { Ok(()) } + + #[ink_e2e::test] + async fn test_create_pool_init_sqrt_price_minimal_difference_from_tick( + mut client: ink_e2e::Client, + ) -> E2EResult<()> { + let dex = create_dex!(client, ContractRef, Percentage::new(0)); + let (token_x, token_y) = create_tokens!(client, TokenRef, 500, 500); + let alice = ink_e2e::alice(); + + let fee_tier = FeeTier::new(Percentage::from_scale(5, 1), 3).unwrap(); + let init_tick = 0; + let init_sqrt_price = calculate_sqrt_price(init_tick).unwrap() - SqrtPrice::new(1); + add_fee_tier!(client, ContractRef, dex, fee_tier, alice).unwrap(); + + create_pool!( + client, + ContractRef, + dex, + token_x, + token_y, + fee_tier, + init_sqrt_price, + init_tick, + alice + ) + .unwrap(); + + Ok(()) + } + + #[ink_e2e::test] + async fn test_create_pool_init_sqrt_price_has_closer_init_tick( + mut client: ink_e2e::Client, + ) -> E2EResult<()> { + let dex = create_dex!(client, ContractRef, Percentage::new(0)); + let (token_x, token_y) = create_tokens!(client, TokenRef, 500, 500); + let alice = ink_e2e::alice(); + + let fee_tier = FeeTier::new(Percentage::from_scale(5, 1), 1).unwrap(); + let init_tick = 2; + // tick = 3 -> 1.000150003749000000000000 + // between -> 1.000175003749000000000000 + // tick = 4 -> 1.000200010000000000000000 + let init_sqrt_price = SqrtPrice::new(1000175003749000000000000); + add_fee_tier!(client, ContractRef, dex, fee_tier, alice).unwrap(); + + let result = create_pool!( + client, + ContractRef, + dex, + token_x, + token_y, + fee_tier, + init_sqrt_price, + init_tick, + alice + ); + assert_eq!(result, Err(InvariantError::InvalidInitSqrtPrice)); + + Ok(()) + } + + #[ink_e2e::test] + async fn test_create_pool_init_sqrt_price_has_closer_init_tick_spacing_over_one( + mut client: ink_e2e::Client, + ) -> E2EResult<()> { + let dex = create_dex!(client, ContractRef, Percentage::new(0)); + let (token_x, token_y) = create_tokens!(client, TokenRef, 500, 500); + let alice = ink_e2e::alice(); + + let fee_tier = FeeTier::new(Percentage::from_scale(5, 1), 3).unwrap(); + let init_tick = 3; + // tick = 3 -> 1.000150003749000000000000 + // between -> 1.000225003749000000000000 + // tick = 6 -> 1.000300030001000000000000 + let init_sqrt_price = SqrtPrice::new(1000225003749000000000000); + println!("init_sqrt_price: {}", init_sqrt_price); + add_fee_tier!(client, ContractRef, dex, fee_tier, alice).unwrap(); + + let result = create_pool!( + client, + ContractRef, + dex, + token_x, + token_y, + fee_tier, + init_sqrt_price, + init_tick, + alice + ); + assert_eq!(result, Err(InvariantError::InvalidInitSqrtPrice)); + + Ok(()) + } } From aba7aac39ea442ed980d4dabbb8e79b60556b717 Mon Sep 17 00:00:00 2001 From: Sniezka Date: Mon, 11 Dec 2023 11:36:31 +0100 Subject: [PATCH 06/10] Cleanup --- src/e2e/create_pool.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/e2e/create_pool.rs b/src/e2e/create_pool.rs index 0f866908..994a5798 100644 --- a/src/e2e/create_pool.rs +++ b/src/e2e/create_pool.rs @@ -253,7 +253,6 @@ pub mod e2e_tests { // between -> 1.000225003749000000000000 // tick = 6 -> 1.000300030001000000000000 let init_sqrt_price = SqrtPrice::new(1000225003749000000000000); - println!("init_sqrt_price: {}", init_sqrt_price); add_fee_tier!(client, ContractRef, dex, fee_tier, alice).unwrap(); let result = create_pool!( From df5d5423ab6a68120b0f00882c125f2b76f362b1 Mon Sep 17 00:00:00 2001 From: Sniezka Date: Mon, 11 Dec 2023 14:00:20 +0100 Subject: [PATCH 07/10] Extended tests --- src/contracts/storage/pool.rs | 75 ++++++++++++++++++++++++++++++++++- src/e2e/create_pool.rs | 45 +++++++++++++++++++++ 2 files changed, 118 insertions(+), 2 deletions(-) diff --git a/src/contracts/storage/pool.rs b/src/contracts/storage/pool.rs index 188d2311..12719942 100644 --- a/src/contracts/storage/pool.rs +++ b/src/contracts/storage/pool.rs @@ -58,7 +58,7 @@ impl Default for Pool { impl Pool { pub fn create( init_sqrt_price: SqrtPrice, - init_tick: i32, + mut init_tick: i32, current_timestamp: u64, tick_spacing: u16, fee_receiver: AccountId, @@ -66,9 +66,13 @@ impl Pool { let upper_bound = unwrap!(SqrtPrice::from_tick(init_tick)); let lower_bound = unwrap!(SqrtPrice::from_tick(init_tick - tick_spacing as i32)); - if init_sqrt_price > upper_bound || init_sqrt_price <= lower_bound { + if init_sqrt_price > upper_bound || init_sqrt_price < lower_bound { return Err(InvariantError::InvalidInitSqrtPrice); } + if init_sqrt_price < upper_bound && init_sqrt_price >= lower_bound { + init_tick -= tick_spacing as i32; + } + Ok(Self { sqrt_price: init_sqrt_price, current_tick_index: init_tick, @@ -231,6 +235,73 @@ mod tests { assert_eq!(pool.start_timestamp, current_timestamp); assert_eq!(pool.last_timestamp, current_timestamp); assert_eq!(pool.fee_receiver, fee_receiver); + + { + let init_tick = 0; + let init_sqrt_price = calculate_sqrt_price(init_tick).unwrap() - SqrtPrice::new(1); + let tick_spacing = 3; + let pool = Pool::create( + init_sqrt_price, + init_tick, + current_timestamp, + tick_spacing, + fee_receiver, + ) + .unwrap(); + assert_eq!(pool.current_tick_index, -3); + } + { + let init_tick = 2; + let init_sqrt_price = SqrtPrice::new(1000175003749000000000000); + let tick_spacing = 1; + let pool = Pool::create( + init_sqrt_price, + init_tick, + current_timestamp, + tick_spacing, + fee_receiver, + ); + assert_eq!(pool, Err(InvariantError::InvalidInitSqrtPrice)); + let correct_init_tick = 4; + let pool = Pool::create( + init_sqrt_price, + correct_init_tick, + current_timestamp, + tick_spacing, + fee_receiver, + ) + .unwrap(); + assert_eq!( + pool.current_tick_index, + correct_init_tick - tick_spacing as i32 + ); + } + { + let init_tick = 3; + let init_sqrt_price = SqrtPrice::new(1000225003749000000000000); + let tick_spacing = 3; + let pool = Pool::create( + init_sqrt_price, + init_tick, + current_timestamp, + tick_spacing, + fee_receiver, + ); + assert_eq!(pool, Err(InvariantError::InvalidInitSqrtPrice)); + let correct_init_tick = 6; + let pool = Pool::create( + init_sqrt_price, + correct_init_tick, + current_timestamp, + tick_spacing, + fee_receiver, + ) + .unwrap(); + assert_eq!( + pool.current_tick_index, + correct_init_tick - tick_spacing as i32 + ); + } } #[test] diff --git a/src/e2e/create_pool.rs b/src/e2e/create_pool.rs index 994a5798..5f2d1648 100644 --- a/src/e2e/create_pool.rs +++ b/src/e2e/create_pool.rs @@ -204,6 +204,12 @@ pub mod e2e_tests { ) .unwrap(); + let pool = get_pool!(client, ContractRef, dex, token_x, token_y, fee_tier).unwrap(); + assert_eq!( + pool.current_tick_index, + init_tick - fee_tier.tick_spacing as i32 + ); + Ok(()) } @@ -235,6 +241,25 @@ pub mod e2e_tests { alice ); assert_eq!(result, Err(InvariantError::InvalidInitSqrtPrice)); + let correct_init_tick = 4; + create_pool!( + client, + ContractRef, + dex, + token_x, + token_y, + fee_tier, + init_sqrt_price, + correct_init_tick, + alice + ) + .unwrap(); + + let pool = get_pool!(client, ContractRef, dex, token_x, token_y, fee_tier).unwrap(); + assert_eq!( + pool.current_tick_index, + correct_init_tick - fee_tier.tick_spacing as i32 + ); Ok(()) } @@ -268,6 +293,26 @@ pub mod e2e_tests { ); assert_eq!(result, Err(InvariantError::InvalidInitSqrtPrice)); + let correct_init_tick = 6; + create_pool!( + client, + ContractRef, + dex, + token_x, + token_y, + fee_tier, + init_sqrt_price, + correct_init_tick, + alice + ) + .unwrap(); + + let pool = get_pool!(client, ContractRef, dex, token_x, token_y, fee_tier).unwrap(); + assert_eq!( + pool.current_tick_index, + correct_init_tick - fee_tier.tick_spacing as i32 + ); + Ok(()) } } From e9d9f283480608cbbfa9a1bdbe14a638e5f643c8 Mon Sep 17 00:00:00 2001 From: Sniezka Date: Tue, 12 Dec 2023 10:13:44 +0100 Subject: [PATCH 08/10] Fixed create_pool validation --- src/contracts/storage/pool.rs | 33 +++++++++++++----------------- src/e2e/create_pool.rs | 23 +++++++-------------- src/e2e/mod.rs | 38 +++++++++++++++++------------------ 3 files changed, 40 insertions(+), 54 deletions(-) diff --git a/src/contracts/storage/pool.rs b/src/contracts/storage/pool.rs index 12719942..af557270 100644 --- a/src/contracts/storage/pool.rs +++ b/src/contracts/storage/pool.rs @@ -58,20 +58,21 @@ impl Default for Pool { impl Pool { pub fn create( init_sqrt_price: SqrtPrice, - mut init_tick: i32, + init_tick: i32, current_timestamp: u64, tick_spacing: u16, fee_receiver: AccountId, ) -> Result { - let upper_bound = unwrap!(SqrtPrice::from_tick(init_tick)); - let lower_bound = unwrap!(SqrtPrice::from_tick(init_tick - tick_spacing as i32)); + let lower_bound = unwrap!(SqrtPrice::from_tick(init_tick)); + let upper_bound = unwrap!(SqrtPrice::from_tick(init_tick + tick_spacing as i32)); if init_sqrt_price > upper_bound || init_sqrt_price < lower_bound { return Err(InvariantError::InvalidInitSqrtPrice); } - if init_sqrt_price < upper_bound && init_sqrt_price >= lower_bound { - init_tick -= tick_spacing as i32; - } + + // if init_sqrt_price < upper_bound && init_sqrt_price >= lower_bound { + // init_tick -= tick_spacing as i32; + // } Ok(Self { sqrt_price: init_sqrt_price, @@ -238,7 +239,7 @@ mod tests { { let init_tick = 0; - let init_sqrt_price = calculate_sqrt_price(init_tick).unwrap() - SqrtPrice::new(1); + let init_sqrt_price = calculate_sqrt_price(init_tick).unwrap() + SqrtPrice::new(1); let tick_spacing = 3; let pool = Pool::create( init_sqrt_price, @@ -248,7 +249,7 @@ mod tests { fee_receiver, ) .unwrap(); - assert_eq!(pool.current_tick_index, -3); + assert_eq!(pool.current_tick_index, init_tick); } { let init_tick = 2; @@ -262,7 +263,7 @@ mod tests { fee_receiver, ); assert_eq!(pool, Err(InvariantError::InvalidInitSqrtPrice)); - let correct_init_tick = 4; + let correct_init_tick = 3; let pool = Pool::create( init_sqrt_price, correct_init_tick, @@ -271,13 +272,10 @@ mod tests { fee_receiver, ) .unwrap(); - assert_eq!( - pool.current_tick_index, - correct_init_tick - tick_spacing as i32 - ); + assert_eq!(pool.current_tick_index, correct_init_tick); } { - let init_tick = 3; + let init_tick = 0; let init_sqrt_price = SqrtPrice::new(1000225003749000000000000); let tick_spacing = 3; let pool = Pool::create( @@ -288,7 +286,7 @@ mod tests { fee_receiver, ); assert_eq!(pool, Err(InvariantError::InvalidInitSqrtPrice)); - let correct_init_tick = 6; + let correct_init_tick = 3; let pool = Pool::create( init_sqrt_price, correct_init_tick, @@ -297,10 +295,7 @@ mod tests { fee_receiver, ) .unwrap(); - assert_eq!( - pool.current_tick_index, - correct_init_tick - tick_spacing as i32 - ); + assert_eq!(pool.current_tick_index, correct_init_tick); } } diff --git a/src/e2e/create_pool.rs b/src/e2e/create_pool.rs index 5f2d1648..72b8f1ca 100644 --- a/src/e2e/create_pool.rs +++ b/src/e2e/create_pool.rs @@ -188,7 +188,7 @@ pub mod e2e_tests { let fee_tier = FeeTier::new(Percentage::from_scale(5, 1), 3).unwrap(); let init_tick = 0; - let init_sqrt_price = calculate_sqrt_price(init_tick).unwrap() - SqrtPrice::new(1); + let init_sqrt_price = calculate_sqrt_price(init_tick).unwrap() + SqrtPrice::new(1); add_fee_tier!(client, ContractRef, dex, fee_tier, alice).unwrap(); create_pool!( @@ -205,10 +205,7 @@ pub mod e2e_tests { .unwrap(); let pool = get_pool!(client, ContractRef, dex, token_x, token_y, fee_tier).unwrap(); - assert_eq!( - pool.current_tick_index, - init_tick - fee_tier.tick_spacing as i32 - ); + assert_eq!(pool.current_tick_index, init_tick); Ok(()) } @@ -241,7 +238,7 @@ pub mod e2e_tests { alice ); assert_eq!(result, Err(InvariantError::InvalidInitSqrtPrice)); - let correct_init_tick = 4; + let correct_init_tick = 3; create_pool!( client, ContractRef, @@ -256,10 +253,7 @@ pub mod e2e_tests { .unwrap(); let pool = get_pool!(client, ContractRef, dex, token_x, token_y, fee_tier).unwrap(); - assert_eq!( - pool.current_tick_index, - correct_init_tick - fee_tier.tick_spacing as i32 - ); + assert_eq!(pool.current_tick_index, correct_init_tick); Ok(()) } @@ -273,7 +267,7 @@ pub mod e2e_tests { let alice = ink_e2e::alice(); let fee_tier = FeeTier::new(Percentage::from_scale(5, 1), 3).unwrap(); - let init_tick = 3; + let init_tick = 0; // tick = 3 -> 1.000150003749000000000000 // between -> 1.000225003749000000000000 // tick = 6 -> 1.000300030001000000000000 @@ -293,7 +287,7 @@ pub mod e2e_tests { ); assert_eq!(result, Err(InvariantError::InvalidInitSqrtPrice)); - let correct_init_tick = 6; + let correct_init_tick = 3; create_pool!( client, ContractRef, @@ -308,10 +302,7 @@ pub mod e2e_tests { .unwrap(); let pool = get_pool!(client, ContractRef, dex, token_x, token_y, fee_tier).unwrap(); - assert_eq!( - pool.current_tick_index, - correct_init_tick - fee_tier.tick_spacing as i32 - ); + assert_eq!(pool.current_tick_index, correct_init_tick); Ok(()) } diff --git a/src/e2e/mod.rs b/src/e2e/mod.rs index 1ede7b9f..ce77a88e 100644 --- a/src/e2e/mod.rs +++ b/src/e2e/mod.rs @@ -1,20 +1,20 @@ -pub mod add_fee_tier; -pub mod change_fee_receiver; -pub mod change_protocol_fee; -pub mod claim; -pub mod constructor; +// pub mod add_fee_tier; +// pub mod change_fee_receiver; +// pub mod change_protocol_fee; +// pub mod claim; +// pub mod constructor; pub mod create_pool; -pub mod cross; -pub mod cross_both_side; -pub mod limits; -pub mod liquidity_gap; -pub mod max_tick_cross; -pub mod multiple_swap; -pub mod position; -pub mod position_list; -pub mod position_slippage; -pub mod protocol_fee; -pub mod remove_fee_tier; -pub mod slippage; -pub mod swap; -pub mod swap_route; +// pub mod cross; +// pub mod cross_both_side; +// pub mod limits; +// pub mod liquidity_gap; +// pub mod max_tick_cross; +// pub mod multiple_swap; +// pub mod position; +// pub mod position_list; +// pub mod position_slippage; +// pub mod protocol_fee; +// pub mod remove_fee_tier; +// pub mod slippage; +// pub mod swap; +// pub mod swap_route; From 85f1d7f44fe840847b98a0f486060ec720be30c7 Mon Sep 17 00:00:00 2001 From: Sniezka Date: Tue, 12 Dec 2023 11:14:54 +0100 Subject: [PATCH 09/10] Added max tick validation case --- src/contracts/storage/pool.rs | 21 +++++++++++-------- src/e2e/mod.rs | 38 +++++++++++++++++------------------ 2 files changed, 32 insertions(+), 27 deletions(-) diff --git a/src/contracts/storage/pool.rs b/src/contracts/storage/pool.rs index af557270..c4626dc3 100644 --- a/src/contracts/storage/pool.rs +++ b/src/contracts/storage/pool.rs @@ -4,10 +4,12 @@ use crate::{ math::{ clamm::*, log::get_tick_at_sqrt_price, + sqrt_price::get_max_tick, types::{ fee_growth::FeeGrowth, liquidity::Liquidity, percentage::Percentage, sqrt_price::SqrtPrice, token_amount::TokenAmount, }, + MAX_TICK, }, InvariantError, }; @@ -63,17 +65,20 @@ impl Pool { tick_spacing: u16, fee_receiver: AccountId, ) -> Result { - let lower_bound = unwrap!(SqrtPrice::from_tick(init_tick)); - let upper_bound = unwrap!(SqrtPrice::from_tick(init_tick + tick_spacing as i32)); + if init_tick + tick_spacing as i32 > MAX_TICK { + let max_tick = get_max_tick(tick_spacing); + if init_sqrt_price > unwrap!(SqrtPrice::from_tick(max_tick)) { + return Err(InvariantError::InvalidInitSqrtPrice); + } + } else { + let lower_bound = unwrap!(SqrtPrice::from_tick(init_tick)); + let upper_bound = unwrap!(SqrtPrice::from_tick(init_tick + tick_spacing as i32)); - if init_sqrt_price > upper_bound || init_sqrt_price < lower_bound { - return Err(InvariantError::InvalidInitSqrtPrice); + if init_sqrt_price >= upper_bound || init_sqrt_price < lower_bound { + return Err(InvariantError::InvalidInitSqrtPrice); + } } - // if init_sqrt_price < upper_bound && init_sqrt_price >= lower_bound { - // init_tick -= tick_spacing as i32; - // } - Ok(Self { sqrt_price: init_sqrt_price, current_tick_index: init_tick, diff --git a/src/e2e/mod.rs b/src/e2e/mod.rs index ce77a88e..1ede7b9f 100644 --- a/src/e2e/mod.rs +++ b/src/e2e/mod.rs @@ -1,20 +1,20 @@ -// pub mod add_fee_tier; -// pub mod change_fee_receiver; -// pub mod change_protocol_fee; -// pub mod claim; -// pub mod constructor; +pub mod add_fee_tier; +pub mod change_fee_receiver; +pub mod change_protocol_fee; +pub mod claim; +pub mod constructor; pub mod create_pool; -// pub mod cross; -// pub mod cross_both_side; -// pub mod limits; -// pub mod liquidity_gap; -// pub mod max_tick_cross; -// pub mod multiple_swap; -// pub mod position; -// pub mod position_list; -// pub mod position_slippage; -// pub mod protocol_fee; -// pub mod remove_fee_tier; -// pub mod slippage; -// pub mod swap; -// pub mod swap_route; +pub mod cross; +pub mod cross_both_side; +pub mod limits; +pub mod liquidity_gap; +pub mod max_tick_cross; +pub mod multiple_swap; +pub mod position; +pub mod position_list; +pub mod position_slippage; +pub mod protocol_fee; +pub mod remove_fee_tier; +pub mod slippage; +pub mod swap; +pub mod swap_route; From 6808c1b04ab6156b19de03880fa0535038f282a8 Mon Sep 17 00:00:00 2001 From: Sniezka Date: Tue, 12 Dec 2023 13:25:40 +0100 Subject: [PATCH 10/10] Added pool::Create tests --- src/contracts/storage/pool.rs | 56 ++++++++++++++++++++++++++++++++++- 1 file changed, 55 insertions(+), 1 deletion(-) diff --git a/src/contracts/storage/pool.rs b/src/contracts/storage/pool.rs index c4626dc3..507f478c 100644 --- a/src/contracts/storage/pool.rs +++ b/src/contracts/storage/pool.rs @@ -67,7 +67,8 @@ impl Pool { ) -> Result { if init_tick + tick_spacing as i32 > MAX_TICK { let max_tick = get_max_tick(tick_spacing); - if init_sqrt_price > unwrap!(SqrtPrice::from_tick(max_tick)) { + let max_sqrt_price = unwrap!(SqrtPrice::from_tick(max_tick)); + if init_sqrt_price != max_sqrt_price { return Err(InvariantError::InvalidInitSqrtPrice); } } else { @@ -302,6 +303,59 @@ mod tests { .unwrap(); assert_eq!(pool.current_tick_index, correct_init_tick); } + { + let init_tick = MAX_TICK; + let init_sqrt_price = calculate_sqrt_price(init_tick).unwrap(); + let tick_spacing = 1; + Pool::create( + init_sqrt_price, + init_tick, + current_timestamp, + tick_spacing, + fee_receiver, + ) + .unwrap(); + } + { + let init_tick = MAX_TICK; + let init_sqrt_price = calculate_sqrt_price(init_tick).unwrap() - SqrtPrice::new(1); + let tick_spacing = 1; + Pool::create( + init_sqrt_price, + init_tick, + current_timestamp, + tick_spacing, + fee_receiver, + ) + .unwrap_err(); + } + { + let init_tick = MAX_TICK; + let init_sqrt_price = SqrtPrice::from_integer(1); + let tick_spacing = 1; + Pool::create( + init_sqrt_price, + init_tick, + current_timestamp, + tick_spacing, + fee_receiver, + ) + .unwrap_err(); + } + { + let init_tick = MAX_TICK - 1; + let init_sqrt_price = calculate_sqrt_price(init_tick).unwrap(); + let tick_spacing = 1; + let pool = Pool::create( + init_sqrt_price, + init_tick, + current_timestamp, + tick_spacing, + fee_receiver, + ) + .unwrap(); + assert_eq!(pool.current_tick_index, init_tick); + } } #[test]