From f57bf048da735bd1428a58c5e0d4f27f7bdf5c2f Mon Sep 17 00:00:00 2001 From: Supanat Potiwarakorn Date: Thu, 16 Feb 2023 17:33:27 +0700 Subject: [PATCH 1/4] add shares when supply --- contracts/transmuter/src/contract.rs | 37 +++++++++++++++++++-- contracts/transmuter/src/multitest/suite.rs | 16 ++++++++- 2 files changed, 49 insertions(+), 4 deletions(-) diff --git a/contracts/transmuter/src/contract.rs b/contracts/transmuter/src/contract.rs index bd29986..46e4193 100644 --- a/contracts/transmuter/src/contract.rs +++ b/contracts/transmuter/src/contract.rs @@ -1,7 +1,9 @@ use cosmwasm_schema::cw_serde; -use cosmwasm_std::{ensure_eq, BankMsg, Coin, Deps, DepsMut, Env, MessageInfo, Response, StdError}; +use cosmwasm_std::{ + ensure_eq, Addr, BankMsg, Coin, Deps, DepsMut, Env, MessageInfo, Response, StdError, Uint128, +}; use cw_controllers::{Admin, AdminResponse}; -use cw_storage_plus::Item; +use cw_storage_plus::{Item, Map}; use sylvia::contract; use crate::{error::ContractError, transmuter_pool::TransmuterPool}; @@ -12,6 +14,7 @@ const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION"); pub struct Transmuter<'a> { pub(crate) pool: Item<'a, TransmuterPool>, + pub(crate) shares: Map<'a, &'a Addr, Uint128>, pub(crate) admin: Admin<'a>, } @@ -22,6 +25,7 @@ impl Transmuter<'_> { Self { pool: Item::new("pool"), admin: Admin::new("admin"), + shares: Map::new("shares"), } } @@ -66,10 +70,21 @@ impl Transmuter<'_> { // ensure funds length == 1 ensure_eq!(info.funds.len(), 1, ContractError::SingleCoinExpected {}); + let supplying_coin = info.funds[0].clone(); + + // update shares + self.shares.update( + deps.storage, + &info.sender, + |shares| -> Result { + Ok(shares.unwrap_or_default() + supplying_coin.amount) + }, + )?; + // update pool self.pool .update(deps.storage, |mut pool| -> Result<_, ContractError> { - pool.supply(&info.funds[0])?; + pool.supply(&supplying_coin)?; Ok(pool) })?; @@ -166,6 +181,22 @@ impl Transmuter<'_> { pool: self.pool.load(deps.storage)?, }) } + + #[msg(query)] + fn shares(&self, ctx: (Deps, Env), address: String) -> Result { + let (deps, _env) = ctx; + Ok(SharesResponse { + shares: self + .shares + .may_load(deps.storage, &deps.api.addr_validate(&address)?)? + .unwrap_or_default(), + }) + } +} + +#[cw_serde] +pub struct SharesResponse { + pub shares: Uint128, } #[cw_serde] diff --git a/contracts/transmuter/src/multitest/suite.rs b/contracts/transmuter/src/multitest/suite.rs index 11e0ded..5c9b2a9 100644 --- a/contracts/transmuter/src/multitest/suite.rs +++ b/contracts/transmuter/src/multitest/suite.rs @@ -1,6 +1,6 @@ use super::test_env::*; use crate::{ - contract::{ExecMsg, InstantiateMsg, PoolResponse, QueryMsg}, + contract::{ExecMsg, InstantiateMsg, PoolResponse, QueryMsg, SharesResponse}, transmuter_pool::TransmuterPool, ContractError, }; @@ -107,6 +107,20 @@ fn test_supply() { out_coin_reserve: supplied_amount[0].clone() } ); + + // check shares + let SharesResponse { shares } = t + .app + .wrap() + .query_wasm_smart( + t.contract.clone(), + &QueryMsg::Shares { + address: t.accounts["provider"].to_string(), + }, + ) + .unwrap(); + + assert_eq!(shares, supplied_amount[0].amount); } #[test] From 17c619447bfc453e4dda9bdf2fc4885b6344cee8 Mon Sep 17 00:00:00 2001 From: Supanat Potiwarakorn Date: Thu, 16 Feb 2023 17:57:35 +0700 Subject: [PATCH 2/4] calc withdrawing coins --- contracts/transmuter/src/error.rs | 8 +- .../src/transmuter_pool/withdraw.rs | 81 ++++++++++++++++++- 2 files changed, 87 insertions(+), 2 deletions(-) diff --git a/contracts/transmuter/src/error.rs b/contracts/transmuter/src/error.rs index 3ef0da3..9ffcc96 100644 --- a/contracts/transmuter/src/error.rs +++ b/contracts/transmuter/src/error.rs @@ -1,4 +1,4 @@ -use cosmwasm_std::{Coin, StdError}; +use cosmwasm_std::{Coin, StdError, Uint128}; use thiserror::Error; #[derive(Error, Debug, PartialEq)] @@ -32,4 +32,10 @@ pub enum ContractError { #[error("Insufficient fund: required: {required}, available: {available}")] InsufficientFund { required: Coin, available: Coin }, + + #[error("Insufficient shares: required: {required}, available: {available}")] + InsufficientShares { + required: Uint128, + available: Uint128, + }, } diff --git a/contracts/transmuter/src/transmuter_pool/withdraw.rs b/contracts/transmuter/src/transmuter_pool/withdraw.rs index ea055da..d98f96e 100644 --- a/contracts/transmuter/src/transmuter_pool/withdraw.rs +++ b/contracts/transmuter/src/transmuter_pool/withdraw.rs @@ -1,4 +1,4 @@ -use cosmwasm_std::Coin; +use cosmwasm_std::{Coin, Uint128}; use crate::ContractError; @@ -34,6 +34,33 @@ impl TransmuterPool { Ok(()) } + + fn calc_withdrawing_coins(&self, shares: Uint128) -> Result, ContractError> { + // withdraw nothing if shares is zero + if shares.is_zero() { + Ok(vec![]) + + // withdraw from in_coin first + } else if shares <= self.in_coin.amount { + Ok(vec![Coin::new(shares.u128(), self.in_coin.denom.clone())]) + + // withdraw from out_coin_reserve when in_coin is drained + } else if self.in_coin.amount.is_zero() { + Ok(vec![Coin::new( + shares.u128(), + self.out_coin_reserve.denom.clone(), + )]) + + // withdraw from both in_coin and out_coin_reserve if shares is larger than in_coin + } else { + let in_coin = Coin::new(self.in_coin.amount.u128(), self.in_coin.denom.clone()); + let out_coin_reserve = Coin::new( + (shares - self.in_coin.amount).u128(), + self.out_coin_reserve.denom.clone(), + ); + Ok(vec![in_coin, out_coin_reserve]) + } + } } #[cfg(test)] @@ -157,4 +184,56 @@ mod tests { } ); } + + #[test] + fn test_calc_withdrawing_coins() { + // withdraw 0 + let pool = TransmuterPool { + in_coin: Coin::new(100_000, ETH_USDC), + out_coin_reserve: Coin::new(100_000, COSMOS_USDC), + }; + let coins = pool.calc_withdrawing_coins(Uint128::zero()).unwrap(); + assert_eq!(coins, vec![]); + + // withdraw in_coin + let pool = TransmuterPool { + in_coin: Coin::new(100_000, ETH_USDC), + out_coin_reserve: Coin::new(100_000, COSMOS_USDC), + }; + let coins = pool.calc_withdrawing_coins(Uint128::new(10_000)).unwrap(); + assert_eq!(coins, vec![Coin::new(10_000, ETH_USDC)]); + + // withdraw out_coin + let pool = TransmuterPool { + in_coin: Coin::new(0, ETH_USDC), + out_coin_reserve: Coin::new(100_000, COSMOS_USDC), + }; + let coins = pool.calc_withdrawing_coins(Uint128::new(10_000)).unwrap(); + assert_eq!(coins, vec![Coin::new(10_000, COSMOS_USDC)]); + + // withdraw both + let pool = TransmuterPool { + in_coin: Coin::new(100_000, ETH_USDC), + out_coin_reserve: Coin::new(100_000, COSMOS_USDC), + }; + let coins = pool.calc_withdrawing_coins(Uint128::new(100_001)).unwrap(); + assert_eq!( + coins, + vec![Coin::new(100_000, ETH_USDC), Coin::new(1, COSMOS_USDC)] + ); + + // withdraw all + let pool = TransmuterPool { + in_coin: Coin::new(100_000, ETH_USDC), + out_coin_reserve: Coin::new(100_000, COSMOS_USDC), + }; + let coins = pool.calc_withdrawing_coins(Uint128::new(200_000)).unwrap(); + assert_eq!( + coins, + vec![ + Coin::new(100_000, ETH_USDC), + Coin::new(100_000, COSMOS_USDC) + ] + ); + } } From 5db82f0ff7e04bc989cc345cecb072490ed883c7 Mon Sep 17 00:00:00 2001 From: Supanat Potiwarakorn Date: Thu, 16 Feb 2023 19:12:19 +0700 Subject: [PATCH 3/4] calculate withdrawing coins from shares --- contracts/transmuter/src/contract.rs | 35 ++++-- contracts/transmuter/src/multitest/suite.rs | 109 +++++++++++++----- .../src/transmuter_pool/withdraw.rs | 3 +- 3 files changed, 112 insertions(+), 35 deletions(-) diff --git a/contracts/transmuter/src/contract.rs b/contracts/transmuter/src/contract.rs index 46e4193..741cf64 100644 --- a/contracts/transmuter/src/contract.rs +++ b/contracts/transmuter/src/contract.rs @@ -1,6 +1,6 @@ use cosmwasm_schema::cw_serde; use cosmwasm_std::{ - ensure_eq, Addr, BankMsg, Coin, Deps, DepsMut, Env, MessageInfo, Response, StdError, Uint128, + ensure, ensure_eq, Addr, BankMsg, Deps, DepsMut, Env, MessageInfo, Response, StdError, Uint128, }; use cw_controllers::{Admin, AdminResponse}; use cw_storage_plus::{Item, Map}; @@ -140,17 +140,38 @@ impl Transmuter<'_> { fn withdraw( &self, ctx: (DepsMut, Env, MessageInfo), - coins: Vec, + shares: Uint128, ) -> Result { let (deps, _env, info) = ctx; - // allow only admin to withdraw - self.admin - .assert_admin(deps.as_ref(), &info.sender) - .map_err(|_| ContractError::Unauthorized {})?; - // withdraw let mut pool = self.pool.load(deps.storage)?; + + // check if sender's shares is enough + let sender_shares = self + .shares + .may_load(deps.storage, &info.sender)? + .unwrap_or_default(); + + ensure!( + sender_shares >= shares, + ContractError::InsufficientShares { + required: shares, + available: sender_shares + } + ); + + // update shares + self.shares.update( + deps.storage, + &info.sender, + |sender_shares| -> Result { + Ok(sender_shares.unwrap_or_default() - shares) + }, + )?; + + // withdraw + let coins = pool.calc_withdrawing_coins(shares)?; pool.withdraw(&coins)?; // save pool diff --git a/contracts/transmuter/src/multitest/suite.rs b/contracts/transmuter/src/multitest/suite.rs index 5c9b2a9..6f2d2fa 100644 --- a/contracts/transmuter/src/multitest/suite.rs +++ b/contracts/transmuter/src/multitest/suite.rs @@ -4,7 +4,7 @@ use crate::{ transmuter_pool::TransmuterPool, ContractError, }; -use cosmwasm_std::{Addr, Coin}; +use cosmwasm_std::{Addr, Coin, Uint128}; use cw_controllers::AdminResponse; use cw_multi_test::Executor; @@ -356,7 +356,8 @@ fn test_admin() { fn test_withdraw() { let mut t = TestEnvBuilder::new() .with_account("user", vec![Coin::new(1_500, ETH_USDC)]) - .with_account("provider", vec![Coin::new(200_000, COSMOS_USDC)]) + .with_account("provider_1", vec![Coin::new(100_000, COSMOS_USDC)]) + .with_account("provider_2", vec![Coin::new(100_000, COSMOS_USDC)]) .with_instantiate_msg(InstantiateMsg { in_denom: ETH_USDC.to_string(), out_denom: COSMOS_USDC.to_string(), @@ -367,7 +368,16 @@ fn test_withdraw() { // supply t.app .execute_contract( - t.accounts["provider"].clone(), + t.accounts["provider_1"].clone(), + t.contract.clone(), + &ExecMsg::Supply {}, + &[Coin::new(100_000, COSMOS_USDC)], + ) + .unwrap(); + + t.app + .execute_contract( + t.accounts["provider_2"].clone(), t.contract.clone(), &ExecMsg::Supply {}, &[Coin::new(100_000, COSMOS_USDC)], @@ -384,42 +394,57 @@ fn test_withdraw() { ) .unwrap(); - // non-admin cannot withdraw + // non-provider cannot withdraw let err = t .app .execute_contract( t.accounts["user"].clone(), t.contract.clone(), &ExecMsg::Withdraw { - coins: vec![Coin::new(1_000, COSMOS_USDC)], + shares: 100u128.into(), }, &[], ) .unwrap_err(); + assert_eq!( err.downcast_ref::().unwrap(), - &ContractError::Unauthorized {} + &ContractError::InsufficientShares { + required: 100u128.into(), + available: Uint128::zero() + } ); - // admin can withdraw + // provider can withdraw t.app .execute_contract( - Addr::unchecked("admin"), + t.accounts["provider_1"].clone(), t.contract.clone(), &ExecMsg::Withdraw { - coins: vec![Coin::new(1_000, COSMOS_USDC), Coin::new(1_000, ETH_USDC)], + shares: 99_999u128.into(), }, &[], ) .unwrap(); + // check shares + let SharesResponse { shares } = t + .app + .wrap() + .query_wasm_smart( + t.contract.clone(), + &QueryMsg::Shares { + address: t.accounts["provider_1"].to_string(), + }, + ) + .unwrap(); + + assert_eq!(shares, Uint128::one()); + // check balances assert_eq!( t.app.wrap().query_all_balances(&t.contract).unwrap(), - vec![ - Coin::new(500, ETH_USDC), - Coin::new(100_000 - 1500 - 1000, COSMOS_USDC) - ] + vec![Coin::new(200_000 - 1500 - (99_999 - 1_500), COSMOS_USDC)] ); let PoolResponse { pool } = t @@ -431,37 +456,67 @@ fn test_withdraw() { assert_eq!( pool, TransmuterPool { - in_coin: Coin::new(500, ETH_USDC), - out_coin_reserve: Coin::new(100_000 - 1500 - 1000, COSMOS_USDC) + in_coin: Coin::new(0, ETH_USDC), + out_coin_reserve: Coin::new(200_000 - 1500 - (99_999 - 1_500), COSMOS_USDC) } ); - // check admin balance + // check pool balances assert_eq!( - t.app - .wrap() - .query_all_balances(&Addr::unchecked("admin")) - .unwrap(), - vec![Coin::new(1_000, ETH_USDC), Coin::new(1_000, COSMOS_USDC)] + t.app.wrap().query_all_balances(&t.contract).unwrap(), + vec![Coin::new(100_001, COSMOS_USDC)] ); - // withdrawing excess coins fails + // withdrawing excess shares fails let err = t .app .execute_contract( - Addr::unchecked("admin"), + Addr::unchecked("provider_1"), t.contract.clone(), &ExecMsg::Withdraw { - coins: vec![Coin::new(100_000, COSMOS_USDC)], + shares: 2u128.into(), }, &[], ) .unwrap_err(); + assert_eq!( err.downcast_ref::().unwrap(), - &ContractError::InsufficientFund { - required: Coin::new(100_000, COSMOS_USDC), - available: Coin::new(100_000 - 1500 - 1000, COSMOS_USDC) + &ContractError::InsufficientShares { + required: 2u128.into(), + available: Uint128::one() } ); + + // provider_2 withdrawing all shares succeeds + t.app + .execute_contract( + Addr::unchecked("provider_2"), + t.contract.clone(), + &ExecMsg::Withdraw { + shares: 100_000u128.into(), + }, + &[], + ) + .unwrap(); + + // check shares + let SharesResponse { shares } = t + .app + .wrap() + .query_wasm_smart( + t.contract.clone(), + &QueryMsg::Shares { + address: t.accounts["provider_2"].to_string(), + }, + ) + .unwrap(); + + assert_eq!(shares, Uint128::zero()); + + // check pool balances + assert_eq!( + t.app.wrap().query_all_balances(&t.contract).unwrap(), + vec![Coin::new(1, COSMOS_USDC)] + ); } diff --git a/contracts/transmuter/src/transmuter_pool/withdraw.rs b/contracts/transmuter/src/transmuter_pool/withdraw.rs index d98f96e..8f5e2f5 100644 --- a/contracts/transmuter/src/transmuter_pool/withdraw.rs +++ b/contracts/transmuter/src/transmuter_pool/withdraw.rs @@ -35,7 +35,8 @@ impl TransmuterPool { Ok(()) } - fn calc_withdrawing_coins(&self, shares: Uint128) -> Result, ContractError> { + /// Calculate the amount of coins to withdraw from the pool based on the number of shares. + pub fn calc_withdrawing_coins(&self, shares: Uint128) -> Result, ContractError> { // withdraw nothing if shares is zero if shares.is_zero() { Ok(vec![]) From 0b3378b1d5968ae6f0d4ba5387b52e04b8a91da5 Mon Sep 17 00:00:00 2001 From: Supanat Potiwarakorn Date: Fri, 17 Feb 2023 11:42:44 +0700 Subject: [PATCH 4/4] simplify withdraw --- contracts/transmuter/src/contract.rs | 28 ++--- contracts/transmuter/src/multitest/suite.rs | 102 ++++++++++++------ .../src/transmuter_pool/withdraw.rs | 82 +------------- 3 files changed, 83 insertions(+), 129 deletions(-) diff --git a/contracts/transmuter/src/contract.rs b/contracts/transmuter/src/contract.rs index 741cf64..4d34c78 100644 --- a/contracts/transmuter/src/contract.rs +++ b/contracts/transmuter/src/contract.rs @@ -1,6 +1,7 @@ use cosmwasm_schema::cw_serde; use cosmwasm_std::{ - ensure, ensure_eq, Addr, BankMsg, Deps, DepsMut, Env, MessageInfo, Response, StdError, Uint128, + ensure, ensure_eq, Addr, BankMsg, Coin, Deps, DepsMut, Env, MessageInfo, Response, StdError, + Uint128, }; use cw_controllers::{Admin, AdminResponse}; use cw_storage_plus::{Item, Map}; @@ -140,23 +141,24 @@ impl Transmuter<'_> { fn withdraw( &self, ctx: (DepsMut, Env, MessageInfo), - shares: Uint128, + coins: Vec, ) -> Result { let (deps, _env, info) = ctx; - // withdraw - let mut pool = self.pool.load(deps.storage)?; - // check if sender's shares is enough let sender_shares = self .shares .may_load(deps.storage, &info.sender)? .unwrap_or_default(); + let required_shares = coins + .iter() + .fold(Uint128::zero(), |acc, curr| acc + curr.amount); + ensure!( - sender_shares >= shares, + sender_shares >= required_shares, ContractError::InsufficientShares { - required: shares, + required: required_shares, available: sender_shares } ); @@ -166,16 +168,16 @@ impl Transmuter<'_> { deps.storage, &info.sender, |sender_shares| -> Result { - Ok(sender_shares.unwrap_or_default() - shares) + Ok(sender_shares.unwrap_or_default() - required_shares) }, )?; // withdraw - let coins = pool.calc_withdrawing_coins(shares)?; - pool.withdraw(&coins)?; - - // save pool - self.pool.save(deps.storage, &pool)?; + self.pool + .update(deps.storage, |mut pool| -> Result<_, ContractError> { + pool.withdraw(&coins)?; + Ok(pool) + })?; let bank_send_msg = BankMsg::Send { to_address: info.sender.to_string(), diff --git a/contracts/transmuter/src/multitest/suite.rs b/contracts/transmuter/src/multitest/suite.rs index 6f2d2fa..797f0cb 100644 --- a/contracts/transmuter/src/multitest/suite.rs +++ b/contracts/transmuter/src/multitest/suite.rs @@ -401,7 +401,7 @@ fn test_withdraw() { t.accounts["user"].clone(), t.contract.clone(), &ExecMsg::Withdraw { - shares: 100u128.into(), + coins: vec![Coin::new(1_500, ETH_USDC)], }, &[], ) @@ -410,7 +410,7 @@ fn test_withdraw() { assert_eq!( err.downcast_ref::().unwrap(), &ContractError::InsufficientShares { - required: 100u128.into(), + required: 1500u128.into(), available: Uint128::zero() } ); @@ -421,7 +421,7 @@ fn test_withdraw() { t.accounts["provider_1"].clone(), t.contract.clone(), &ExecMsg::Withdraw { - shares: 99_999u128.into(), + coins: vec![Coin::new(500, ETH_USDC)], }, &[], ) @@ -439,12 +439,15 @@ fn test_withdraw() { ) .unwrap(); - assert_eq!(shares, Uint128::one()); + assert_eq!(shares, Uint128::new(100_000 - 500)); // check balances assert_eq!( t.app.wrap().query_all_balances(&t.contract).unwrap(), - vec![Coin::new(200_000 - 1500 - (99_999 - 1_500), COSMOS_USDC)] + vec![ + Coin::new(1500 - 500, ETH_USDC), + Coin::new(200_000 - 1500, COSMOS_USDC) + ] ); let PoolResponse { pool } = t @@ -456,25 +459,65 @@ fn test_withdraw() { assert_eq!( pool, TransmuterPool { - in_coin: Coin::new(0, ETH_USDC), - out_coin_reserve: Coin::new(200_000 - 1500 - (99_999 - 1_500), COSMOS_USDC) + in_coin: Coin::new(1500 - 500, ETH_USDC), + out_coin_reserve: Coin::new(200_000 - 1500, COSMOS_USDC) } ); - // check pool balances + // provider can withdraw both sides + t.app + .execute_contract( + t.accounts["provider_2"].clone(), + t.contract.clone(), + &ExecMsg::Withdraw { + coins: vec![Coin::new(1_000, ETH_USDC), Coin::new(99_000, COSMOS_USDC)], + }, + &[], + ) + .unwrap(); + + // check shares + let SharesResponse { shares } = t + .app + .wrap() + .query_wasm_smart( + t.contract.clone(), + &QueryMsg::Shares { + address: t.accounts["provider_2"].to_string(), + }, + ) + .unwrap(); + + assert_eq!(shares, Uint128::new(0)); + + // check balances assert_eq!( t.app.wrap().query_all_balances(&t.contract).unwrap(), - vec![Coin::new(100_001, COSMOS_USDC)] + vec![Coin::new(200_000 - 1500 - 99_000, COSMOS_USDC)] + ); + + let PoolResponse { pool } = t + .app + .wrap() + .query_wasm_smart(t.contract.clone(), &QueryMsg::Pool {}) + .unwrap(); + + assert_eq!( + pool, + TransmuterPool { + in_coin: Coin::new(0, ETH_USDC), + out_coin_reserve: Coin::new(200_000 - 1500 - 99_000, COSMOS_USDC) + } ); // withdrawing excess shares fails let err = t .app .execute_contract( - Addr::unchecked("provider_1"), + Addr::unchecked("provider_2"), t.contract.clone(), &ExecMsg::Withdraw { - shares: 2u128.into(), + coins: vec![Coin::new(1, ETH_USDC)], }, &[], ) @@ -483,40 +526,29 @@ fn test_withdraw() { assert_eq!( err.downcast_ref::().unwrap(), &ContractError::InsufficientShares { - required: 2u128.into(), - available: Uint128::one() + required: Uint128::one(), + available: Uint128::zero() } ); - // provider_2 withdrawing all shares succeeds - t.app + // has remaining shares but no coins on the requested side + let err = t + .app .execute_contract( - Addr::unchecked("provider_2"), + Addr::unchecked("provider_1"), t.contract.clone(), &ExecMsg::Withdraw { - shares: 100_000u128.into(), + coins: vec![Coin::new(1, ETH_USDC), Coin::new(1, COSMOS_USDC)], }, &[], ) - .unwrap(); - - // check shares - let SharesResponse { shares } = t - .app - .wrap() - .query_wasm_smart( - t.contract.clone(), - &QueryMsg::Shares { - address: t.accounts["provider_2"].to_string(), - }, - ) - .unwrap(); - - assert_eq!(shares, Uint128::zero()); + .unwrap_err(); - // check pool balances assert_eq!( - t.app.wrap().query_all_balances(&t.contract).unwrap(), - vec![Coin::new(1, COSMOS_USDC)] + err.downcast_ref::().unwrap(), + &ContractError::InsufficientFund { + required: Coin::new(1, ETH_USDC), + available: Coin::new(0, ETH_USDC) + } ); } diff --git a/contracts/transmuter/src/transmuter_pool/withdraw.rs b/contracts/transmuter/src/transmuter_pool/withdraw.rs index 8f5e2f5..ea055da 100644 --- a/contracts/transmuter/src/transmuter_pool/withdraw.rs +++ b/contracts/transmuter/src/transmuter_pool/withdraw.rs @@ -1,4 +1,4 @@ -use cosmwasm_std::{Coin, Uint128}; +use cosmwasm_std::Coin; use crate::ContractError; @@ -34,34 +34,6 @@ impl TransmuterPool { Ok(()) } - - /// Calculate the amount of coins to withdraw from the pool based on the number of shares. - pub fn calc_withdrawing_coins(&self, shares: Uint128) -> Result, ContractError> { - // withdraw nothing if shares is zero - if shares.is_zero() { - Ok(vec![]) - - // withdraw from in_coin first - } else if shares <= self.in_coin.amount { - Ok(vec![Coin::new(shares.u128(), self.in_coin.denom.clone())]) - - // withdraw from out_coin_reserve when in_coin is drained - } else if self.in_coin.amount.is_zero() { - Ok(vec![Coin::new( - shares.u128(), - self.out_coin_reserve.denom.clone(), - )]) - - // withdraw from both in_coin and out_coin_reserve if shares is larger than in_coin - } else { - let in_coin = Coin::new(self.in_coin.amount.u128(), self.in_coin.denom.clone()); - let out_coin_reserve = Coin::new( - (shares - self.in_coin.amount).u128(), - self.out_coin_reserve.denom.clone(), - ); - Ok(vec![in_coin, out_coin_reserve]) - } - } } #[cfg(test)] @@ -185,56 +157,4 @@ mod tests { } ); } - - #[test] - fn test_calc_withdrawing_coins() { - // withdraw 0 - let pool = TransmuterPool { - in_coin: Coin::new(100_000, ETH_USDC), - out_coin_reserve: Coin::new(100_000, COSMOS_USDC), - }; - let coins = pool.calc_withdrawing_coins(Uint128::zero()).unwrap(); - assert_eq!(coins, vec![]); - - // withdraw in_coin - let pool = TransmuterPool { - in_coin: Coin::new(100_000, ETH_USDC), - out_coin_reserve: Coin::new(100_000, COSMOS_USDC), - }; - let coins = pool.calc_withdrawing_coins(Uint128::new(10_000)).unwrap(); - assert_eq!(coins, vec![Coin::new(10_000, ETH_USDC)]); - - // withdraw out_coin - let pool = TransmuterPool { - in_coin: Coin::new(0, ETH_USDC), - out_coin_reserve: Coin::new(100_000, COSMOS_USDC), - }; - let coins = pool.calc_withdrawing_coins(Uint128::new(10_000)).unwrap(); - assert_eq!(coins, vec![Coin::new(10_000, COSMOS_USDC)]); - - // withdraw both - let pool = TransmuterPool { - in_coin: Coin::new(100_000, ETH_USDC), - out_coin_reserve: Coin::new(100_000, COSMOS_USDC), - }; - let coins = pool.calc_withdrawing_coins(Uint128::new(100_001)).unwrap(); - assert_eq!( - coins, - vec![Coin::new(100_000, ETH_USDC), Coin::new(1, COSMOS_USDC)] - ); - - // withdraw all - let pool = TransmuterPool { - in_coin: Coin::new(100_000, ETH_USDC), - out_coin_reserve: Coin::new(100_000, COSMOS_USDC), - }; - let coins = pool.calc_withdrawing_coins(Uint128::new(200_000)).unwrap(); - assert_eq!( - coins, - vec![ - Coin::new(100_000, ETH_USDC), - Coin::new(100_000, COSMOS_USDC) - ] - ); - } }