diff --git a/src/contracts/collections/mod.rs b/src/contracts/collections/mod.rs index 106ddcc6..c1beb435 100644 --- a/src/contracts/collections/mod.rs +++ b/src/contracts/collections/mod.rs @@ -1,9 +1,11 @@ pub mod fee_tiers; +pub mod pool_keys; pub mod pools; pub mod positions; pub mod ticks; pub use fee_tiers::*; +pub use pool_keys::*; pub use pools::*; pub use positions::*; pub use ticks::*; diff --git a/src/contracts/collections/pool_keys.rs b/src/contracts/collections/pool_keys.rs new file mode 100644 index 00000000..810c40d0 --- /dev/null +++ b/src/contracts/collections/pool_keys.rs @@ -0,0 +1,105 @@ +use crate::{contracts::PoolKey, InvariantError}; +use alloc::vec::Vec; + +#[ink::storage_item] +#[derive(Debug, Default)] +pub struct PoolKeys { + pool_keys: Vec, +} + +impl PoolKeys { + pub fn add(&mut self, pool_key: PoolKey) -> Result<(), InvariantError> { + if self.contains(pool_key) { + return Err(InvariantError::PoolKeyAlreadyExist); + } + + self.pool_keys.push(pool_key); + Ok(()) + } + + pub fn remove(&mut self, pool_key: PoolKey) -> Result<(), InvariantError> { + let index = self + .pool_keys + .iter() + .position(|vec_pool_key| *vec_pool_key == pool_key) + .ok_or(InvariantError::PoolKeyNotFound)?; + + self.pool_keys.remove(index); + + Ok(()) + } + + pub fn contains(&self, fee_tier_key: PoolKey) -> bool { + self.pool_keys.contains(&fee_tier_key) + } + + pub fn get_all(&self) -> Vec { + self.pool_keys.clone() + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::{contracts::FeeTier, math::percentage::Percentage}; + use decimal::*; + use ink::primitives::AccountId; + + #[ink::test] + fn test_add() { + let pool_keys = &mut PoolKeys::default(); + let pool_key = PoolKey::default(); + let token_x = AccountId::from([1; 32]); + let token_y = AccountId::from([2; 32]); + let fee_tier = FeeTier { + fee: Percentage::new(0), + tick_spacing: 1, + }; + let new_pool_key = PoolKey::new(token_x, token_y, fee_tier).unwrap(); + + pool_keys.add(pool_key).unwrap(); + assert_eq!(pool_keys.contains(pool_key), true); + assert_eq!(pool_keys.contains(new_pool_key), false); + + let result = pool_keys.add(pool_key); + assert_eq!(result, Err(InvariantError::PoolKeyAlreadyExist)); + } + + #[ink::test] + fn test_remove() { + let pool_keys = &mut PoolKeys::default(); + let pool_key = PoolKey::default(); + + pool_keys.add(pool_key).unwrap(); + + pool_keys.remove(pool_key).unwrap(); + assert_eq!(pool_keys.contains(pool_key), false); + + let result = pool_keys.remove(pool_key); + assert_eq!(result, Err(InvariantError::PoolKeyNotFound)); + } + + #[ink::test] + fn test_get_all() { + let pool_keys = &mut PoolKeys::default(); + let pool_key = PoolKey::default(); + let token_x = AccountId::from([1; 32]); + let token_y = AccountId::from([2; 32]); + let fee_tier = FeeTier { + fee: Percentage::new(0), + tick_spacing: 1, + }; + let new_pool_key = PoolKey::new(token_x, token_y, fee_tier).unwrap(); + + let result = pool_keys.get_all(); + assert_eq!(result, vec![]); + assert_eq!(result.len(), 0); + + pool_keys.add(pool_key).unwrap(); + pool_keys.add(new_pool_key).unwrap(); + + let result = pool_keys.get_all(); + assert_eq!(result, vec![pool_key, new_pool_key]); + assert_eq!(result.len(), 2); + } +} diff --git a/src/contracts/entrypoints.rs b/src/contracts/entrypoints.rs index 9219df1e..a56a739f 100644 --- a/src/contracts/entrypoints.rs +++ b/src/contracts/entrypoints.rs @@ -124,6 +124,9 @@ pub trait Invariant { fee_tier: FeeTier, ) -> Result; + #[ink(message)] + fn get_pools(&self) -> Vec; + #[ink(message)] fn get_tick(&self, key: PoolKey, index: i32) -> Result; diff --git a/src/contracts/storage/fee_tier.rs b/src/contracts/storage/fee_tier.rs index 1babbdfd..709b0019 100644 --- a/src/contracts/storage/fee_tier.rs +++ b/src/contracts/storage/fee_tier.rs @@ -1,4 +1,5 @@ -use crate::math::types::percentage::Percentage; +use crate::{math::types::percentage::Percentage, InvariantError}; +use decimal::*; #[derive(scale::Decode, scale::Encode, Debug, Copy, Clone, PartialEq)] #[cfg_attr( feature = "std", @@ -10,7 +11,15 @@ pub struct FeeTier { } impl FeeTier { - pub fn new(fee: Percentage, tick_spacing: u16) -> FeeTier { - FeeTier { fee, tick_spacing } + pub fn new(fee: Percentage, tick_spacing: u16) -> Result { + if tick_spacing == 0 || tick_spacing > 100 { + return Err(InvariantError::InvalidTickSpacing); + } + + if fee > Percentage::from_integer(1) { + return Err(InvariantError::InvalidFee); + } + + Ok(Self { fee, tick_spacing }) } } diff --git a/src/lib.rs b/src/lib.rs index 9596102c..78f917d8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -15,17 +15,20 @@ pub enum InvariantError { PositionNotFound, TickNotFound, FeeTierNotFound, + PoolKeyNotFound, AmountIsZero, WrongLimit, PriceLimitReached, NoGainSwap, InvalidTickSpacing, FeeTierAlreadyExist, + PoolKeyAlreadyExist, UnauthorizedFeeReceiver, ZeroLiquidity, TransferError, TokensAreTheSame, AmountUnderMinimumAmountOut, + InvalidFee, } #[ink::contract] pub mod contract { @@ -35,6 +38,7 @@ pub mod contract { use crate::contracts::FeeTierKey; use crate::contracts::Invariant; use crate::contracts::Pool; + use crate::contracts::PoolKeys; use crate::contracts::Tick; use crate::contracts::Tickmap; use crate::contracts::{FeeTier, FeeTiers, PoolKey, Pools, Position, Positions, Ticks}; // @@ -142,7 +146,7 @@ pub mod contract { tickmap: Tickmap, ticks: Ticks, fee_tier_keys: Vec, - pool_keys: Vec, + pool_keys: PoolKeys, state: State, } @@ -921,8 +925,7 @@ pub mod contract { let pool_key = PoolKey::new(token_0, token_1, fee_tier)?; let pool = Pool::create(init_tick, current_timestamp, self.state.admin); self.pools.add(pool_key, &pool)?; - - self.pool_keys.push(pool_key); + self.pool_keys.add(pool_key)?; Ok(()) } @@ -940,16 +943,6 @@ pub mod contract { Ok(pool) } - fn remove_pool(&mut self, key: PoolKey) { - self.pools.remove(key); - self.pool_keys.retain(|&x| x != key); - } - - // Ticks - fn add_tick(&mut self, key: PoolKey, index: i32, tick: Tick) { - self.ticks.add(key, index, &tick); - } - #[ink(message)] fn get_tick(&self, key: PoolKey, index: i32) -> Result { self.ticks.get(key, index) @@ -959,117 +952,10 @@ pub mod contract { fn get_tickmap_bit(&self, key: PoolKey, index: i32) -> bool { self.tickmap.get(index, key.fee_tier.tick_spacing, key) } - fn remove_tick(&mut self, key: PoolKey, index: i32) { - self.ticks.remove(key, index); - } - fn emit_swap_event( - &self, - address: AccountId, - pool: PoolKey, - amount_in: TokenAmount, - amount_out: TokenAmount, - fee: TokenAmount, - start_sqrt_price: SqrtPrice, - target_sqrt_price: SqrtPrice, - x_to_y: bool, - ) { - let timestamp = self.get_timestamp(); - ink::codegen::EmitEvent::::emit_event( - self.env(), - SwapEvent { - timestamp, - address, - pool, - amount_in, - amount_out, - fee, - start_sqrt_price, - target_sqrt_price, - x_to_y, - }, - ); - } - fn emit_create_position_event( - &self, - address: AccountId, - pool: PoolKey, - liquidity: Liquidity, - lower_tick: i32, - upper_tick: i32, - current_sqrt_price: SqrtPrice, - ) { - let timestamp = self.get_timestamp(); - ink::codegen::EmitEvent::::emit_event( - self.env(), - CreatePositionEvent { - timestamp, - address, - pool, - liquidity, - lower_tick, - upper_tick, - current_sqrt_price, - }, - ); - } - fn emit_remove_position_event( - &self, - address: AccountId, - pool: PoolKey, - liquidity: Liquidity, - lower_tick: i32, - upper_tick: i32, - current_sqrt_price: SqrtPrice, - ) { - let timestamp = self.get_timestamp(); - ink::codegen::EmitEvent::::emit_event( - self.env(), - RemovePositionEvent { - timestamp, - address, - pool, - liquidity, - lower_tick, - upper_tick, - current_sqrt_price, - }, - ); - } - fn emit_cross_tick_event(&self, address: AccountId, pool: PoolKey, index: i32) { - let timestamp = self.get_timestamp(); - ink::codegen::EmitEvent::::emit_event( - self.env(), - CrossTickEvent { - timestamp, - address, - pool, - index, - }, - ); - } - - fn get_timestamp(&self) -> u64 { - self.env().block_timestamp() - } - - fn _order_tokens( - &self, - token_0: AccountId, - token_1: AccountId, - balance_0: Balance, - balance_1: Balance, - ) -> OrderPair { - match token_0.lt(&token_1) { - true => OrderPair { - x: (token_0, balance_0), - y: (token_1, balance_1), - }, - false => OrderPair { - x: (token_1, balance_1), - y: (token_0, balance_0), - }, - } + #[ink(message)] + fn get_pools(&self) -> Vec { + self.pool_keys.get_all() } } @@ -1233,7 +1119,7 @@ pub mod contract { let alice = ink_e2e::alice(); - let fee_tier = FeeTier::new(Percentage::from_scale(6, 3), 10); + let fee_tier = FeeTier::new(Percentage::from_scale(6, 3), 10).unwrap(); create_fee_tier!(client, ContractRef, dex, fee_tier, alice); @@ -1379,7 +1265,7 @@ pub mod contract { let alice = ink_e2e::alice(); - let fee_tier = FeeTier::new(Percentage::from_scale(6, 3), 10); + let fee_tier = FeeTier::new(Percentage::from_scale(6, 3), 10).unwrap(); create_fee_tier!(client, ContractRef, dex, fee_tier, alice); @@ -1532,7 +1418,7 @@ pub mod contract { mint_with_aprove_for_bob!(client, TokenRef, token_x, dex, amount); approve!(client, TokenRef, token_y, dex, u64::MAX as u128, bob); - let fee_tier = FeeTier::new(Percentage::from_scale(6, 3), 1); + let fee_tier = FeeTier::new(Percentage::from_scale(6, 3), 1).unwrap(); create_fee_tier!(client, ContractRef, dex, fee_tier, alice); @@ -1669,7 +1555,7 @@ pub mod contract { approve!(client, TokenRef, token_x, dex, u128::MAX, alice); approve!(client, TokenRef, token_y, dex, u128::MAX, alice); - let fee_tier = FeeTier::new(Percentage::from_scale(6, 3), 1); + let fee_tier = FeeTier::new(Percentage::from_scale(6, 3), 1).unwrap(); create_fee_tier!(client, ContractRef, dex, fee_tier, alice); let init_tick = get_max_tick(1); @@ -1724,7 +1610,7 @@ pub mod contract { approve!(client, TokenRef, token_x, dex, u128::MAX, alice); approve!(client, TokenRef, token_y, dex, u128::MAX, alice); - let fee_tier = FeeTier::new(Percentage::from_scale(6, 3), 1); + let fee_tier = FeeTier::new(Percentage::from_scale(6, 3), 1).unwrap(); create_fee_tier!(client, ContractRef, dex, fee_tier, alice); let init_tick = get_max_tick(1); @@ -1784,7 +1670,7 @@ pub mod contract { approve!(client, TokenRef, token_x, dex, u128::MAX, alice); approve!(client, TokenRef, token_y, dex, u128::MAX, alice); - let fee_tier = FeeTier::new(Percentage::from_scale(6, 3), 1); + let fee_tier = FeeTier::new(Percentage::from_scale(6, 3), 1).unwrap(); create_fee_tier!(client, ContractRef, dex, fee_tier, alice); let init_tick = 0; @@ -1914,7 +1800,7 @@ pub mod contract { approve!(client, TokenRef, token_x, dex, u128::MAX, alice); approve!(client, TokenRef, token_y, dex, u128::MAX, alice); - let fee_tier = FeeTier::new(Percentage::from_scale(6, 3), 1); + let fee_tier = FeeTier::new(Percentage::from_scale(6, 3), 1).unwrap(); create_fee_tier!(client, ContractRef, dex, fee_tier, alice); @@ -1990,7 +1876,7 @@ pub mod contract { let liquidity = Liquidity::from_integer(10000000); - let fee_tier = FeeTier::new(Percentage::from_scale(6, 3), 10); + let fee_tier = FeeTier::new(Percentage::from_scale(6, 3), 10).unwrap(); let pool_key = PoolKey::new(token_x, token_y, fee_tier).unwrap(); @@ -2103,7 +1989,7 @@ pub mod contract { init_basic_pool!(client, ContractRef, TokenRef, dex, token_x, token_y); init_basic_position!(client, ContractRef, TokenRef, dex, token_x, token_y); - let fee_tier = FeeTier::new(Percentage::from_scale(6, 3), 10); + let fee_tier = FeeTier::new(Percentage::from_scale(6, 3), 10).unwrap(); let pool_key = PoolKey::new(token_x, token_y, fee_tier).unwrap(); @@ -2136,7 +2022,7 @@ pub mod contract { init_basic_position!(client, ContractRef, TokenRef, dex, token_x, token_y); init_basic_swap!(client, ContractRef, TokenRef, dex, token_x, token_y); - let fee_tier = FeeTier::new(Percentage::from_scale(6, 3), 10); + let fee_tier = FeeTier::new(Percentage::from_scale(6, 3), 10).unwrap(); let pool_key = PoolKey::new(token_x, token_y, fee_tier).unwrap(); let alice = ink_e2e::alice(); let pool = get_pool!(client, ContractRef, dex, token_x, token_y, fee_tier).unwrap(); @@ -2248,7 +2134,7 @@ pub mod contract { init_cross_position!(client, ContractRef, TokenRef, dex, token_x, token_y); init_cross_swap!(client, ContractRef, TokenRef, dex, token_x, token_y); - let fee_tier = FeeTier::new(Percentage::from_scale(6, 3), 10); + let fee_tier = FeeTier::new(Percentage::from_scale(6, 3), 10).unwrap(); let pool_key = PoolKey::new(token_x, token_y, fee_tier).unwrap(); let alice = ink_e2e::alice(); @@ -2302,7 +2188,7 @@ pub mod contract { init_basic_position!(client, ContractRef, TokenRef, dex, token_x, token_y); init_basic_swap!(client, ContractRef, TokenRef, dex, token_x, token_y); - let fee_tier = FeeTier::new(Percentage::from_scale(6, 3), 10); + let fee_tier = FeeTier::new(Percentage::from_scale(6, 3), 10).unwrap(); let pool_key = PoolKey::new(token_x, token_y, fee_tier).unwrap(); let alice = ink_e2e::alice(); withdraw_protocol_fee!(client, ContractRef, dex, pool_key, alice); @@ -2432,7 +2318,7 @@ pub mod contract { let alice = ink_e2e::alice(); - let fee_tier = FeeTier::new(Percentage::new(0), 1); + let fee_tier = FeeTier::new(Percentage::new(0), 1).unwrap(); create_fee_tier!(client, ContractRef, dex, fee_tier, alice); @@ -2462,7 +2348,7 @@ pub mod contract { #[ink_e2e::test] async fn create_fee_tier_test(mut client: ink_e2e::Client) -> E2EResult<()> { let dex = create_dex!(client, ContractRef, Percentage::new(0)); - let fee_tier = FeeTier::new(Percentage::new(0), 10u16); + let fee_tier = FeeTier::new(Percentage::new(0), 10u16).unwrap(); let alice = ink_e2e::alice(); create_fee_tier!(client, ContractRef, dex, fee_tier, alice); let fee_tier = get_fee_tier!(client, ContractRef, dex, Percentage::new(0), 10u16); @@ -2490,7 +2376,7 @@ pub mod contract { let dex = create_dex!(client, ContractRef, Percentage::new(0)); let (token_x, token_y) = create_tokens!(client, TokenRef, TokenRef, 500, 500); - let fee_tier = FeeTier::new(Percentage::from_scale(5, 1), 100); + let fee_tier = FeeTier::new(Percentage::from_scale(5, 1), 100).unwrap(); let init_tick = 0; let alice = ink_e2e::alice(); @@ -2516,7 +2402,7 @@ pub mod contract { async fn fee_tier_test(mut client: ink_e2e::Client) -> E2EResult<()> { let dex = create_dex!(client, ContractRef, Percentage::new(0)); let admin = ink_e2e::alice(); - let fee_tier = FeeTier::new(Percentage::from_scale(5, 1), 100); + let fee_tier = FeeTier::new(Percentage::from_scale(5, 1), 100).unwrap(); let result = create_fee_tier!(client, ContractRef, dex, fee_tier, admin); assert!(result.is_ok()); Ok(()) @@ -2527,7 +2413,7 @@ pub mod contract { let dex = create_dex!(client, ContractRef, Percentage::new(0)); let admin = ink_e2e::alice(); // 0 tick spacing | should fail - let fee_tier = FeeTier::new(Percentage::from_scale(5, 1), 0); + let fee_tier = FeeTier::new(Percentage::from_scale(5, 1), 0).unwrap(); let result = create_fee_tier!(client, ContractRef, dex, fee_tier, admin); } @@ -2537,7 +2423,7 @@ pub mod contract { let dex = create_dex!(client, ContractRef, Percentage::new(0)); let user = ink_e2e::bob(); // not-admin - let fee_tier = FeeTier::new(Percentage::from_scale(5, 1), 10); + let fee_tier = FeeTier::new(Percentage::from_scale(5, 1), 10).unwrap(); let result = create_fee_tier!(client, ContractRef, dex, fee_tier, user); } @@ -2554,7 +2440,7 @@ pub mod contract { let (token_x, token_y) = create_tokens!(client, TokenRef, TokenRef, initial_balance, initial_balance); - let fee_tier = FeeTier::new(Percentage::from_scale(2, 4), 4); + let fee_tier = FeeTier::new(Percentage::from_scale(2, 4), 4).unwrap(); create_fee_tier!(client, ContractRef, dex, fee_tier, alice); @@ -2658,7 +2544,7 @@ pub mod contract { let (token_x, token_y) = create_tokens!(client, TokenRef, TokenRef, initial_balance, initial_balance); - let fee_tier = FeeTier::new(Percentage::from_scale(2, 4), 10); + let fee_tier = FeeTier::new(Percentage::from_scale(2, 4), 10).unwrap(); create_fee_tier!(client, ContractRef, dex, fee_tier, alice); @@ -2936,7 +2822,7 @@ pub mod contract { let (token_x, token_y) = create_tokens!(client, TokenRef, TokenRef, initial_balance, initial_balance); - let fee_tier = FeeTier::new(Percentage::from_scale(2, 4), 4); + let fee_tier = FeeTier::new(Percentage::from_scale(2, 4), 4).unwrap(); create_fee_tier!(client, ContractRef, dex, fee_tier, alice); @@ -3040,7 +2926,7 @@ pub mod contract { let (token_x, token_y) = create_tokens!(client, TokenRef, TokenRef, initial_balance, initial_balance); - let fee_tier = FeeTier::new(Percentage::from_scale(2, 4), 4); + let fee_tier = FeeTier::new(Percentage::from_scale(2, 4), 4).unwrap(); create_fee_tier!(client, ContractRef, dex, fee_tier, alice); @@ -3137,7 +3023,7 @@ pub mod contract { let dex = create_dex!(client, ContractRef, Percentage::new(0)); let (token_x, token_y) = create_tokens!(client, TokenRef, TokenRef, 500, 500); - let fee_tier = FeeTier::new(Percentage::from_scale(5, 1), 1); + let fee_tier = FeeTier::new(Percentage::from_scale(5, 1), 1).unwrap(); let init_tick = 0; let alice = ink_e2e::alice(); @@ -3171,7 +3057,7 @@ pub mod contract { let dex = create_dex!(client, ContractRef, Percentage::new(0)); let (token_x, token_y) = create_tokens!(client, TokenRef, TokenRef, 500, 500); - let fee_tier = FeeTier::new(Percentage::from_scale(5, 1), 100); + let fee_tier = FeeTier::new(Percentage::from_scale(5, 1), 100).unwrap(); let init_tick = 0; let admin = ink_e2e::alice(); @@ -3197,7 +3083,7 @@ pub mod contract { #[ink_e2e::test] async fn remove_position_test(mut client: ink_e2e::Client) -> E2EResult<()> { - let fee_tier = FeeTier::new(Percentage::from_scale(6, 3), 10); + let fee_tier = FeeTier::new(Percentage::from_scale(6, 3), 10).unwrap(); let alice = ink_e2e::alice(); let bob = ink_e2e::bob(); let init_tick = 0; @@ -3531,7 +3417,7 @@ pub mod contract { #[ink_e2e::test] #[should_panic] async fn no_liquidity_swap(mut client: ink_e2e::Client) -> () { - let fee_tier = FeeTier::new(Percentage::from_scale(6, 3), 10); + let fee_tier = FeeTier::new(Percentage::from_scale(6, 3), 10).unwrap(); let alice = ink_e2e::alice(); let bob = ink_e2e::bob(); let init_tick = 0; @@ -3683,7 +3569,7 @@ pub mod contract { #[ink_e2e::test] async fn liquidity_gap_test(mut client: ink_e2e::Client) -> E2EResult<()> { - let fee_tier = FeeTier::new(Percentage::from_scale(6, 3), 10); + let fee_tier = FeeTier::new(Percentage::from_scale(6, 3), 10).unwrap(); let alice = ink_e2e::alice(); let bob = ink_e2e::bob(); let init_tick = 0; @@ -3877,7 +3763,7 @@ pub mod contract { #[ink_e2e::test] async fn cross_both_side_test(mut client: ink_e2e::Client) -> E2EResult<()> { - let fee_tier = FeeTier::new(Percentage::from_scale(6, 3), 10); + let fee_tier = FeeTier::new(Percentage::from_scale(6, 3), 10).unwrap(); let alice = ink_e2e::alice(); let bob = ink_e2e::bob(); let init_tick = 0; @@ -4107,7 +3993,7 @@ pub mod contract { #[ink_e2e::test] #[should_panic] async fn cross_both_side_not_cross_case_test(mut client: ink_e2e::Client) -> () { - let fee_tier = FeeTier::new(Percentage::from_scale(6, 3), 10); + let fee_tier = FeeTier::new(Percentage::from_scale(6, 3), 10).unwrap(); let alice = ink_e2e::alice(); let bob = ink_e2e::bob(); let init_tick = 0;