From 714a33b4c515c3a14a8e3208edda22189104159b Mon Sep 17 00:00:00 2001 From: JanKuczma Date: Wed, 26 Jun 2024 15:48:27 +0200 Subject: [PATCH 1/8] swap excess reserve method --- amm/contracts/stable_pool/lib.rs | 41 ++++++++++++++++++++++++++++++++ amm/traits/stable_pool.rs | 13 ++++++++++ 2 files changed, 54 insertions(+) diff --git a/amm/contracts/stable_pool/lib.rs b/amm/contracts/stable_pool/lib.rs index a18eb84..f110ea1 100644 --- a/amm/contracts/stable_pool/lib.rs +++ b/amm/contracts/stable_pool/lib.rs @@ -685,6 +685,47 @@ pub mod stable_pool { Ok((token_in_amount, swap_fee)) } + #[ink(message)] + fn swap( + &mut self, + token_in: AccountId, + token_out: AccountId, + min_token_out_amount: u128, + to: AccountId, + ) -> Result<(u128, u128), StablePoolError> { + //check token ids + let (token_in_id, token_out_id) = self.check_tokens(token_in, token_out)?; + + // Make sure rates are up to date before we attempt any calculations + self.update_rates(); + + let token_in_amount = self + .token_by_address(token_in) + .balance_of(self.env().account_id()); + + // calculate amount out, mint admin fee and update reserves + let (token_out_amount, swap_fee) = self._swap_to( + token_in_id, + token_out_id, + token_in_amount, + min_token_out_amount, + )?; + + // transfer token_out + self.token_by_address(token_out) + .transfer(to, token_out_amount, vec![])?; + + self.env().emit_event(Swap { + sender: self.env().caller(), + token_in, + amount_in: token_in_amount, + token_out, + amount_out: token_out_amount, + to, + }); + Ok((token_out_amount, swap_fee)) + } + #[ink(message)] fn set_owner(&mut self, new_owner: AccountId) -> Result<(), StablePoolError> { self.ensure_owner()?; diff --git a/amm/traits/stable_pool.rs b/amm/traits/stable_pool.rs index 546d1fa..7624178 100644 --- a/amm/traits/stable_pool.rs +++ b/amm/traits/stable_pool.rs @@ -152,6 +152,19 @@ pub trait StablePool { to: AccountId, ) -> Result<(u128, u128), StablePoolError>; + /// Swaps excess reserve balance of `token_in` to `token_out`. + /// + /// Swapped tokens are transferred to the `to` account. + /// Returns (amount_out, fees) + #[ink(message)] + fn swap( + &mut self, + token_in: AccountId, + token_out: AccountId, + min_token_out_amount: u128, + to: AccountId, + ) -> Result<(u128, u128), StablePoolError>; + #[ink(message)] fn set_owner(&mut self, new_owner: AccountId) -> Result<(), StablePoolError>; From 9a62df3b8a46d911d18930a8c6ffa25c74cd9419 Mon Sep 17 00:00:00 2001 From: JanKuczma Date: Wed, 26 Jun 2024 16:52:33 +0200 Subject: [PATCH 2/8] fix amount in --- amm/contracts/stable_pool/lib.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/amm/contracts/stable_pool/lib.rs b/amm/contracts/stable_pool/lib.rs index f110ea1..348618a 100644 --- a/amm/contracts/stable_pool/lib.rs +++ b/amm/contracts/stable_pool/lib.rs @@ -701,7 +701,9 @@ pub mod stable_pool { let token_in_amount = self .token_by_address(token_in) - .balance_of(self.env().account_id()); + .balance_of(self.env().account_id()) + .checked_sub(self.pool.reserves[token_in_id]) + .ok_or(StablePoolError::InsufficientInputAmount)?; // calculate amount out, mint admin fee and update reserves let (token_out_amount, swap_fee) = self._swap_to( From a7a116a2212cf6d31cdf49cf18e0cc6f48c38c86 Mon Sep 17 00:00:00 2001 From: JanKuczma Date: Fri, 28 Jun 2024 13:41:08 +0200 Subject: [PATCH 3/8] rename to swap_received --- amm/contracts/stable_pool/lib.rs | 2 +- amm/traits/stable_pool.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/amm/contracts/stable_pool/lib.rs b/amm/contracts/stable_pool/lib.rs index 348618a..f3adf96 100644 --- a/amm/contracts/stable_pool/lib.rs +++ b/amm/contracts/stable_pool/lib.rs @@ -686,7 +686,7 @@ pub mod stable_pool { } #[ink(message)] - fn swap( + fn swap_received( &mut self, token_in: AccountId, token_out: AccountId, diff --git a/amm/traits/stable_pool.rs b/amm/traits/stable_pool.rs index 7624178..f5ec24d 100644 --- a/amm/traits/stable_pool.rs +++ b/amm/traits/stable_pool.rs @@ -157,7 +157,7 @@ pub trait StablePool { /// Swapped tokens are transferred to the `to` account. /// Returns (amount_out, fees) #[ink(message)] - fn swap( + fn swap_received( &mut self, token_in: AccountId, token_out: AccountId, From 98842924746075dc3dd2d3c7b502ccc9d704d40a Mon Sep 17 00:00:00 2001 From: JanKuczma Date: Fri, 28 Jun 2024 17:31:48 +0200 Subject: [PATCH 4/8] emit Sync on swap_received --- amm/contracts/stable_pool/lib.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/amm/contracts/stable_pool/lib.rs b/amm/contracts/stable_pool/lib.rs index b4cf0db..59aac57 100644 --- a/amm/contracts/stable_pool/lib.rs +++ b/amm/contracts/stable_pool/lib.rs @@ -771,6 +771,9 @@ pub mod stable_pool { amount_out: token_out_amount, to, }); + self.env().emit_event(Sync { + reserves: self.reserves(), + }); Ok((token_out_amount, swap_fee)) } From a135b435e9742c9cecb10df8e1f6c6e450a435a7 Mon Sep 17 00:00:00 2001 From: JanKuczma Date: Mon, 1 Jul 2024 11:17:05 +0200 Subject: [PATCH 5/8] refactor --- amm/contracts/stable_pool/lib.rs | 212 ++++++++++++++----------------- 1 file changed, 98 insertions(+), 114 deletions(-) diff --git a/amm/contracts/stable_pool/lib.rs b/amm/contracts/stable_pool/lib.rs index 59aac57..55d64e4 100644 --- a/amm/contracts/stable_pool/lib.rs +++ b/amm/contracts/stable_pool/lib.rs @@ -266,6 +266,11 @@ pub mod stable_pool { address.into() } + #[inline] + fn token_by_id(&self, token_id: usize) -> contract_ref!(PSP22) { + self.pool.tokens[token_id].into() + } + fn update_rates(&mut self) { let current_time = self.env().block_timestamp(); let mut rate_changed = false; @@ -386,16 +391,27 @@ pub mod stable_pool { /// - updates reserves /// It assumes that rates have been updated. /// Returns (token_out_amount, swap_fee) - fn _swap_to( + fn _swap_exact_in( &mut self, - token_in_id: usize, - token_out_id: usize, - token_in_amount: u128, + token_in: AccountId, + token_out: AccountId, + token_in_amount: Option, min_token_out_amount: u128, + to: AccountId, ) -> Result<(u128, u128), StablePoolError> { + //check token ids + let (token_in_id, token_out_id) = self.check_tokens(token_in, token_out)?; + + // get transfered token_in amount + let token_in_amount = self._transfer_in(token_in_id, token_in_amount)?; + if token_in_amount == 0 { return Err(StablePoolError::InsufficientInputAmount); } + + // Make sure rates are up to date before we attempt any calculations + self.update_rates(); + let rates = self.get_scaled_rates()?; // calc amount_out and fees let (token_out_amount, fee) = math::rated_swap_to( @@ -418,6 +434,22 @@ pub mod stable_pool { // mint admin fee self.mint_admin_fee(fee, token_out_id)?; + + // transfer token_out + self.token_by_address(token_out) + .transfer(to, token_out_amount, vec![])?; + + self.env().emit_event(Swap { + sender: self.env().caller(), + token_in, + amount_in: token_in_amount, + token_out, + amount_out: token_out_amount, + to, + }); + self.env().emit_event(Sync { + reserves: self.reserves(), + }); Ok((token_out_amount, fee)) } @@ -428,17 +460,26 @@ pub mod stable_pool { /// - updates reserves /// It assumes that rates have been updated. /// Returns (token_in_amount, swap_fee) - fn _swap_from( + fn _swap_exact_out( &mut self, - token_in_id: usize, - token_out_id: usize, + token_in: AccountId, + token_out: AccountId, token_out_amount: u128, max_token_in_amount: u128, + to: AccountId, ) -> Result<(u128, u128), StablePoolError> { + //check token ids + let (token_in_id, token_out_id) = self.check_tokens(token_in, token_out)?; + if token_out_amount == 0 { return Err(StablePoolError::InsufficientOutputAmount); } + + // Make sure rates are up to date before we attempt any calculations + self.update_rates(); + let rates = self.get_scaled_rates()?; + // calc amount_out and fees let (token_in_amount, fee) = math::rated_swap_from( &rates, @@ -461,9 +502,49 @@ pub mod stable_pool { // mint admin fee self.mint_admin_fee(fee, token_out_id)?; - // note that fee is applied to token_out (same as in _swap_to) + // transfer token_in + _ = self._transfer_in(token_in_id, Some(token_in_amount))?; + + // transfer token_out + self.token_by_address(token_out) + .transfer(to, token_out_amount, vec![])?; + + self.env().emit_event(Swap { + sender: self.env().caller(), + token_in, + amount_in: token_in_amount, + token_out, + amount_out: token_out_amount, + to, + }); + self.env().emit_event(Sync { + reserves: self.reserves(), + }); + // note that fee is applied to token_out (same as in _swap_exact_in) Ok((token_in_amount, fee)) } + + fn _transfer_in( + &self, + token_id: usize, + amount: Option, + ) -> Result { + let mut token = self.token_by_id(token_id); + if let Some(token_amount) = amount { + token.transfer_from( + self.env().caller(), + self.env().account_id(), + token_amount, + vec![], + )?; + Ok(token_amount) + } else { + token + .balance_of(self.env().account_id()) + .checked_sub(self.pool.reserves[token_id]) + .ok_or(StablePoolError::InsufficientInputAmount) + } + } } impl StablePool for StablePoolContract { @@ -642,44 +723,13 @@ pub mod stable_pool { min_token_out_amount: u128, to: AccountId, ) -> Result<(u128, u128), StablePoolError> { - //check token ids - let (token_in_id, token_out_id) = self.check_tokens(token_in, token_out)?; - - // Make sure rates are up to date before we attempt any calculations - self.update_rates(); - - // transfer token_in - self.token_by_address(token_in).transfer_from( - self.env().caller(), - self.env().account_id(), - token_in_amount, - vec![], - )?; - - // calculate amount out, mint admin fee and update reserves - let (token_out_amount, swap_fee) = self._swap_to( - token_in_id, - token_out_id, - token_in_amount, - min_token_out_amount, - )?; - - // transfer token_out - self.token_by_address(token_out) - .transfer(to, token_out_amount, vec![])?; - - self.env().emit_event(Swap { - sender: self.env().caller(), + self._swap_exact_in( token_in, - amount_in: token_in_amount, token_out, - amount_out: token_out_amount, + Some(token_in_amount), + min_token_out_amount, to, - }); - self.env().emit_event(Sync { - reserves: self.reserves(), - }); - Ok((token_out_amount, swap_fee)) + ) } #[ink(message)] @@ -691,44 +741,13 @@ pub mod stable_pool { max_token_in_amount: u128, to: AccountId, ) -> Result<(u128, u128), StablePoolError> { - //check token ids - let (token_in_id, token_out_id) = self.check_tokens(token_in, token_out)?; - - // Make sure rates are up to date before we attempt any calculations - self.update_rates(); - - // calculate amount out, mint admin fee and update reserves - let (token_in_amount, swap_fee) = self._swap_from( - token_in_id, - token_out_id, - token_out_amount, - max_token_in_amount, - )?; - - // transfer token_in - self.token_by_address(token_in).transfer_from( - self.env().caller(), - self.env().account_id(), - token_in_amount, - vec![], - )?; - - // transfer token_out - self.token_by_address(token_out) - .transfer(to, token_out_amount, vec![])?; - - self.env().emit_event(Swap { - sender: self.env().caller(), + self._swap_exact_out( token_in, - amount_in: token_in_amount, token_out, - amount_out: token_out_amount, + token_out_amount, + max_token_in_amount, to, - }); - self.env().emit_event(Sync { - reserves: self.reserves(), - }); - Ok((token_in_amount, swap_fee)) + ) } #[ink(message)] @@ -739,42 +758,7 @@ pub mod stable_pool { min_token_out_amount: u128, to: AccountId, ) -> Result<(u128, u128), StablePoolError> { - //check token ids - let (token_in_id, token_out_id) = self.check_tokens(token_in, token_out)?; - - // Make sure rates are up to date before we attempt any calculations - self.update_rates(); - - let token_in_amount = self - .token_by_address(token_in) - .balance_of(self.env().account_id()) - .checked_sub(self.pool.reserves[token_in_id]) - .ok_or(StablePoolError::InsufficientInputAmount)?; - - // calculate amount out, mint admin fee and update reserves - let (token_out_amount, swap_fee) = self._swap_to( - token_in_id, - token_out_id, - token_in_amount, - min_token_out_amount, - )?; - - // transfer token_out - self.token_by_address(token_out) - .transfer(to, token_out_amount, vec![])?; - - self.env().emit_event(Swap { - sender: self.env().caller(), - token_in, - amount_in: token_in_amount, - token_out, - amount_out: token_out_amount, - to, - }); - self.env().emit_event(Sync { - reserves: self.reserves(), - }); - Ok((token_out_amount, swap_fee)) + self._swap_exact_in(token_in, token_out, None, min_token_out_amount, to) } #[ink(message)] From 35c01d93081db50449832b2e8612990aaea42e9a Mon Sep 17 00:00:00 2001 From: JanKuczma Date: Mon, 1 Jul 2024 15:00:20 +0200 Subject: [PATCH 6/8] test pr check --- amm/contracts/stable_pool/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/amm/contracts/stable_pool/lib.rs b/amm/contracts/stable_pool/lib.rs index 55d64e4..9d588a9 100644 --- a/amm/contracts/stable_pool/lib.rs +++ b/amm/contracts/stable_pool/lib.rs @@ -138,6 +138,7 @@ pub mod stable_pool { Ok(()) } + impl StablePoolContract { pub fn new_pool( tokens: Vec, From 537745d1947a5731f960b4ff49be366f8581a2e3 Mon Sep 17 00:00:00 2001 From: JanKuczma Date: Mon, 1 Jul 2024 15:02:29 +0200 Subject: [PATCH 7/8] Revert "test pr check" This reverts commit 35c01d93081db50449832b2e8612990aaea42e9a. --- amm/contracts/stable_pool/lib.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/amm/contracts/stable_pool/lib.rs b/amm/contracts/stable_pool/lib.rs index 9d588a9..55d64e4 100644 --- a/amm/contracts/stable_pool/lib.rs +++ b/amm/contracts/stable_pool/lib.rs @@ -138,7 +138,6 @@ pub mod stable_pool { Ok(()) } - impl StablePoolContract { pub fn new_pool( tokens: Vec, From 80dd6644bd8b64c2ea72613714360a81745a173f Mon Sep 17 00:00:00 2001 From: JanKuczma Date: Mon, 1 Jul 2024 16:26:06 +0200 Subject: [PATCH 8/8] use macro for simple checks --- amm/contracts/stable_pool/lib.rs | 81 +++++++++++++++----------------- 1 file changed, 39 insertions(+), 42 deletions(-) diff --git a/amm/contracts/stable_pool/lib.rs b/amm/contracts/stable_pool/lib.rs index 55d64e4..810b249 100644 --- a/amm/contracts/stable_pool/lib.rs +++ b/amm/contracts/stable_pool/lib.rs @@ -326,9 +326,7 @@ pub mod stable_pool { token_in: AccountId, token_out: AccountId, ) -> Result<(usize, usize), StablePoolError> { - if token_in == token_out { - return Err(StablePoolError::IdenticalTokenId); - } + ensure!(token_in != token_out, StablePoolError::IdenticalTokenId); //check token ids let token_in_id = self.token_id(token_in)?; let token_out_id = self.token_id(token_out)?; @@ -405,10 +403,6 @@ pub mod stable_pool { // get transfered token_in amount let token_in_amount = self._transfer_in(token_in_id, token_in_amount)?; - if token_in_amount == 0 { - return Err(StablePoolError::InsufficientInputAmount); - } - // Make sure rates are up to date before we attempt any calculations self.update_rates(); @@ -425,9 +419,10 @@ pub mod stable_pool { )?; // Check if swapped amount is not less than min_token_out_amount - if token_out_amount < min_token_out_amount { - return Err(StablePoolError::InsufficientOutputAmount); - }; + ensure!( + token_out_amount >= min_token_out_amount, + StablePoolError::InsufficientOutputAmount + ); // update reserves self.increase_reserve(token_in_id, token_in_amount)?; self.decrease_reserve(token_out_id, token_out_amount)?; @@ -471,9 +466,10 @@ pub mod stable_pool { //check token ids let (token_in_id, token_out_id) = self.check_tokens(token_in, token_out)?; - if token_out_amount == 0 { - return Err(StablePoolError::InsufficientOutputAmount); - } + ensure!( + token_out_amount > 0, + StablePoolError::InsufficientOutputAmount + ); // Make sure rates are up to date before we attempt any calculations self.update_rates(); @@ -492,9 +488,10 @@ pub mod stable_pool { )?; // Check if in token_in_amount is as constrained by the user - if token_in_amount > max_token_in_amount { - return Err(StablePoolError::TooLargeInputAmount); - }; + ensure!( + token_in_amount <= max_token_in_amount, + StablePoolError::TooLargeInputAmount + ); // update reserves self.increase_reserve(token_in_id, token_in_amount)?; self.decrease_reserve(token_out_id, token_out_amount)?; @@ -530,20 +527,22 @@ pub mod stable_pool { amount: Option, ) -> Result { let mut token = self.token_by_id(token_id); - if let Some(token_amount) = amount { + let amount = if let Some(token_amount) = amount { token.transfer_from( self.env().caller(), self.env().account_id(), token_amount, vec![], )?; - Ok(token_amount) + token_amount } else { token .balance_of(self.env().account_id()) .checked_sub(self.pool.reserves[token_id]) - .ok_or(StablePoolError::InsufficientInputAmount) - } + .ok_or(MathError::SubUnderflow(103))? + }; + ensure!(amount > 0, StablePoolError::InsufficientInputAmount); + Ok(amount) } } @@ -559,9 +558,10 @@ pub mod stable_pool { let (shares, fee_part) = self.get_mint_liquidity_for_amounts(amounts.clone())?; // Check min shares - if shares < min_share_amount { - return Err(StablePoolError::InsufficientLiquidityMinted); - } + ensure!( + shares >= min_share_amount, + StablePoolError::InsufficientLiquidityMinted + ); // transfer amounts for (id, &token) in self.pool.tokens.iter().enumerate() { @@ -665,9 +665,10 @@ pub mod stable_pool { self.get_burn_liquidity_for_amounts(amounts.clone())?; // check max shares - if shares_to_burn > max_share_amount { - return Err(StablePoolError::InsufficientLiquidityBurned); - } + ensure!( + shares_to_burn <= max_share_amount, + StablePoolError::InsufficientLiquidityBurned + ); // burn shares let events = self.psp22.burn(self.env().caller(), shares_to_burn)?; self.emit_events(events); @@ -859,9 +860,10 @@ pub mod stable_pool { &mut self, amounts: Vec, ) -> Result<(u128, u128), StablePoolError> { - if amounts.len() != self.pool.tokens.len() { - return Err(StablePoolError::IncorrectAmountsCount); - } + ensure!( + amounts.len() == self.pool.tokens.len(), + StablePoolError::IncorrectAmountsCount + ); self.update_rates(); let rates = self.get_scaled_rates()?; @@ -880,14 +882,11 @@ pub mod stable_pool { &mut self, liquidity: u128, ) -> Result, StablePoolError> { - match math::compute_amounts_given_lp( + Ok(math::compute_amounts_given_lp( liquidity, &self.reserves(), self.psp22.total_supply(), - ) { - Ok(amounts) => Ok(amounts), - Err(err) => Err(StablePoolError::MathError(err)), - } + )?) } #[ink(message)] @@ -896,9 +895,10 @@ pub mod stable_pool { amounts: Vec, ) -> Result<(u128, u128), StablePoolError> { self.update_rates(); - if amounts.len() != self.pool.tokens.len() { - return Err(StablePoolError::IncorrectAmountsCount); - } + ensure!( + amounts.len() == self.pool.tokens.len(), + StablePoolError::IncorrectAmountsCount + ); let rates = self.get_scaled_rates()?; math::rated_compute_lp_amount_for_withdraw( &rates, @@ -916,14 +916,11 @@ pub mod stable_pool { &mut self, liquidity: u128, ) -> Result, StablePoolError> { - match math::compute_amounts_given_lp( + Ok(math::compute_amounts_given_lp( liquidity, &self.reserves(), self.psp22.total_supply(), - ) { - Ok(amounts) => Ok(amounts), - Err(err) => Err(StablePoolError::MathError(err)), - } + )?) } }