From efc93be88f74babff4d7831308adf36eb11e0416 Mon Sep 17 00:00:00 2001 From: 0xFable <0xfable@protonmail.com> Date: Wed, 10 Apr 2024 01:32:41 +0100 Subject: [PATCH 1/4] fix: Remove unused state functions for reading pairs, we use identifiers now not pair keys [pool-manager] there are a few functions in the state file that are not even used. Remove if not needed #317 --- .../pool-manager/src/manager/commands.rs | 4 +- .../liquidity_hub/pool-manager/src/state.rs | 62 +------------------ 2 files changed, 2 insertions(+), 64 deletions(-) diff --git a/contracts/liquidity_hub/pool-manager/src/manager/commands.rs b/contracts/liquidity_hub/pool-manager/src/manager/commands.rs index 762f051e..f93d160e 100644 --- a/contracts/liquidity_hub/pool-manager/src/manager/commands.rs +++ b/contracts/liquidity_hub/pool-manager/src/manager/commands.rs @@ -6,7 +6,7 @@ use white_whale_std::{ whale_lair::fill_rewards_msg, }; -use crate::state::{add_allow_native_token, get_pair_by_identifier, PAIR_COUNTER}; +use crate::state::{get_pair_by_identifier, PAIR_COUNTER}; use crate::{ state::{Config, MANAGER_CONFIG, PAIRS}, ContractError, @@ -230,8 +230,6 @@ pub fn add_native_token_decimals( return Err(ContractError::InvalidVerificationBalance {}); } - add_allow_native_token(deps.storage, denom.to_string(), decimals)?; - Ok(Response::new().add_attributes(vec![ ("action", "add_allow_native_token"), ("denom", &denom), diff --git a/contracts/liquidity_hub/pool-manager/src/state.rs b/contracts/liquidity_hub/pool-manager/src/state.rs index 29705728..c6e3c2c6 100644 --- a/contracts/liquidity_hub/pool-manager/src/state.rs +++ b/contracts/liquidity_hub/pool-manager/src/state.rs @@ -36,17 +36,6 @@ pub fn get_pair_by_identifier( .ok_or(ContractError::UnExistingPair {}) } -//todo not used, remove -// Used for PAIRS -pub fn pair_key(asset_infos: &[AssetInfoRaw]) -> Vec { - let mut asset_infos = asset_infos.to_vec(); - asset_infos.sort_by(|a, b| a.as_bytes().cmp(b.as_bytes())); - - asset_infos - .iter() - .flat_map(|info| info.as_bytes().to_vec()) - .collect() -} pub fn get_decimals(pair_info: &PairInfo) -> Vec { pair_info.asset_decimals.clone() } @@ -58,6 +47,7 @@ pub const SWAP_ROUTES: Map<(&str, &str), Vec> = Map::new("swap_ro // Dyanmic Maps for Fee and Pair info pub const COLLECTABLE_PROTOCOL_FEES: Map<&str, Vec> = Map::new("collected_protocol_fees"); + //todo remove pub const TOTAL_COLLECTED_PROTOCOL_FEES: Map<&str, Vec> = Map::new("total_collected_protocol_fees"); @@ -68,60 +58,10 @@ pub const ALL_TIME_BURNED_FEES: Map<&str, Vec> = Map::new("all_time_burne pub const MANAGER_CONFIG: Item = Item::new("manager_config"); pub const PAIR_COUNTER: Item = Item::new("vault_count"); -//todo remove -// key : asset info / value: decimals -pub const ALLOW_NATIVE_TOKENS: Map<&[u8], u8> = Map::new("allow_native_token"); -//todo remove -pub fn add_allow_native_token( - storage: &mut dyn Storage, - denom: String, - decimals: u8, -) -> StdResult<()> { - ALLOW_NATIVE_TOKENS.save(storage, denom.as_bytes(), &decimals) -} - // settings for pagination const MAX_LIMIT: u32 = 1000; const DEFAULT_LIMIT: u32 = 10; -//todo this is not even used, remove? -// start_after AssetInfoRaw??? There shouldn't be any AssetInfoRaw around -pub fn read_pairs( - storage: &dyn Storage, - _api: &dyn Api, - start_after: Option<[AssetInfoRaw; 2]>, - limit: Option, -) -> StdResult> { - // Note PairInfo may need to be refactored to handle the 2or3 design - let limit = limit.unwrap_or(DEFAULT_LIMIT).min(MAX_LIMIT) as usize; - let start = calc_range_start(start_after).map(Bound::ExclusiveRaw); - - PAIRS - .range(storage, start, None, Order::Ascending) - .take(limit) - .map(|item| { - let (_, v) = item?; - Ok(v) - }) - .collect::>>() -} - -// todo look at the cw_utils, there's calc_range_start, this could maybe be removed -// this will set the first key after the provided key, by appending a 1 byte -fn calc_range_start(start_after: Option<[AssetInfoRaw; 2]>) -> Option> { - start_after.map(|asset_infos| { - let mut asset_infos = asset_infos.to_vec(); - asset_infos.sort_by(|a, b| a.as_bytes().cmp(b.as_bytes())); - - let mut v = [asset_infos[0].as_bytes(), asset_infos[1].as_bytes()] - .concat() - .as_slice() - .to_vec(); - v.push(1); - v - }) -} - #[cw_serde] pub struct Config { pub whale_lair_addr: Addr, From fc847561e948c244c467e67ea4799e3386a0d96e Mon Sep 17 00:00:00 2001 From: 0xFable <0xfable@protonmail.com> Date: Wed, 10 Apr 2024 02:20:34 +0100 Subject: [PATCH 2/4] fix: [pool-manager] Remove COLLECTABLE_PROTOCOL_FEES, TOTAL_COLLECTED_PROTOCOL_FEES and ALL_TIME_BURNED_FEES #305 --- .../liquidity_hub/pool-manager/src/helpers.rs | 2 +- .../pool-manager/src/liquidity/commands.rs | 16 +-- .../pool-manager/src/manager/commands.rs | 12 +- .../liquidity_hub/pool-manager/src/queries.rs | 5 +- .../liquidity_hub/pool-manager/src/state.rs | 14 +- .../src/tests/integration_tests.rs | 25 +++- .../pool-manager/src/tests/suite.rs | 6 +- packages/white-whale-std/src/pool_manager.rs | 122 +++++++++++++++++- 8 files changed, 159 insertions(+), 43 deletions(-) diff --git a/contracts/liquidity_hub/pool-manager/src/helpers.rs b/contracts/liquidity_hub/pool-manager/src/helpers.rs index 260b94d7..38325890 100644 --- a/contracts/liquidity_hub/pool-manager/src/helpers.rs +++ b/contracts/liquidity_hub/pool-manager/src/helpers.rs @@ -4,8 +4,8 @@ use std::ops::Mul; use cosmwasm_schema::cw_serde; use cosmwasm_std::{Coin, Decimal, Decimal256, StdError, StdResult, Storage, Uint128, Uint256}; +use white_whale_std::pool_manager::PoolFee; use white_whale_std::pool_network::asset::{Asset, AssetInfo, PairType}; -use white_whale_std::pool_network::pair::PoolFee; use crate::error::ContractError; use crate::math::Decimal256Helper; diff --git a/contracts/liquidity_hub/pool-manager/src/liquidity/commands.rs b/contracts/liquidity_hub/pool-manager/src/liquidity/commands.rs index 1c73e98e..9bff7767 100644 --- a/contracts/liquidity_hub/pool-manager/src/liquidity/commands.rs +++ b/contracts/liquidity_hub/pool-manager/src/liquidity/commands.rs @@ -1,9 +1,9 @@ -use cosmwasm_std::{Addr, BankMsg, Coin, CosmosMsg, DepsMut, Env, MessageInfo, Response}; +use cosmwasm_std::{Addr, BankMsg, Coin, CosmosMsg, DepsMut, Env, MessageInfo, Response, Uint256}; use white_whale_std::pool_network::asset::PairType; use crate::{ helpers::{self}, - state::{get_pair_by_identifier, COLLECTABLE_PROTOCOL_FEES}, + state::get_pair_by_identifier, }; use crate::{ state::{MANAGER_CONFIG, PAIRS}, @@ -74,18 +74,6 @@ pub fn provide_liquidity( return Err(ContractError::InvalidZeroAmount {}); } - // TODO: remove - let collected_protocol_fees = COLLECTABLE_PROTOCOL_FEES - .load(deps.storage, &pair.lp_denom) - .unwrap_or_default(); - - // todo remove this, as collected_protocol_fees is not a thing - for pool in pool_assets.iter_mut() { - let protocol_fee = - get_protocol_fee_for_asset(collected_protocol_fees.clone(), pool.clone().denom); - pool.amount = pool.amount.checked_sub(protocol_fee).unwrap(); - } - let liquidity_token = pair.lp_denom.clone(); // Compute share and other logic based on the number of assets diff --git a/contracts/liquidity_hub/pool-manager/src/manager/commands.rs b/contracts/liquidity_hub/pool-manager/src/manager/commands.rs index f93d160e..674742a9 100644 --- a/contracts/liquidity_hub/pool-manager/src/manager/commands.rs +++ b/contracts/liquidity_hub/pool-manager/src/manager/commands.rs @@ -2,11 +2,12 @@ use cosmwasm_std::{ attr, Attribute, Coin, CosmosMsg, DepsMut, Env, MessageInfo, Response, Uint128, }; use white_whale_std::{ - pool_network::{asset::PairType, pair::PoolFee, querier::query_native_decimals}, + pool_manager::PoolFee, + pool_network::{asset::PairType, querier::query_native_decimals}, whale_lair::fill_rewards_msg, }; -use crate::state::{get_pair_by_identifier, PAIR_COUNTER}; +use crate::state::{get_pair_by_identifier, NATIVE_TOKEN_DECIMALS, PAIR_COUNTER}; use crate::{ state::{Config, MANAGER_CONFIG, PAIRS}, ContractError, @@ -26,7 +27,8 @@ pub const LP_SYMBOL: &str = "uLP"; /// /// ```rust /// # use cosmwasm_std::{DepsMut, Decimal, Env, MessageInfo, Response, CosmosMsg, WasmMsg, to_json_binary}; -/// # use white_whale_std::pool_network::{asset::{PairType}, pair::PoolFee}; +/// # use white_whale_std::pool_network::{asset::{PairType}}; +/// # use white_whale_std::pool_manager::PoolFee; /// # use white_whale_std::fee::Fee; /// # use pool_manager::error::ContractError; /// # use pool_manager::manager::commands::MAX_ASSETS_PER_POOL; @@ -49,6 +51,7 @@ pub const LP_SYMBOL: &str = "uLP"; /// burn_fee: Fee { /// share: Decimal::zero(), /// }, +/// extra_fees: vec![], /// }; /// /// #[cfg(feature = "osmosis")] @@ -65,6 +68,7 @@ pub const LP_SYMBOL: &str = "uLP"; /// osmosis_fee: Fee { /// share: Decimal::zero(), /// }, +/// extra_fees: vec![], /// }; /// let pair_type = PairType::ConstantProduct; /// let token_factory_lp = false; @@ -229,7 +233,7 @@ pub fn add_native_token_decimals( if balance.is_zero() { return Err(ContractError::InvalidVerificationBalance {}); } - + NATIVE_TOKEN_DECIMALS.save(deps.storage, denom.as_bytes(), &decimals)?; Ok(Response::new().add_attributes(vec![ ("action", "add_allow_native_token"), ("denom", &denom), diff --git a/contracts/liquidity_hub/pool-manager/src/queries.rs b/contracts/liquidity_hub/pool-manager/src/queries.rs index 5e221392..604acfa0 100644 --- a/contracts/liquidity_hub/pool-manager/src/queries.rs +++ b/contracts/liquidity_hub/pool-manager/src/queries.rs @@ -9,9 +9,10 @@ use white_whale_std::pool_network::{ // router::SimulateSwapOperationsResponse, }; +use crate::state::NATIVE_TOKEN_DECIMALS; use crate::{ helpers::{self, calculate_stableswap_y, StableSwapDirection}, - state::{get_decimals, get_pair_by_identifier, ALLOW_NATIVE_TOKENS}, + state::{get_decimals, get_pair_by_identifier}, ContractError, }; use crate::{math::Decimal256Helper, state::SWAP_ROUTES}; @@ -21,7 +22,7 @@ pub fn query_native_token_decimal( deps: Deps, denom: String, ) -> Result { - let decimals = ALLOW_NATIVE_TOKENS.load(deps.storage, denom.as_bytes())?; + let decimals = NATIVE_TOKEN_DECIMALS.load(deps.storage, denom.as_bytes())?; Ok(NativeTokenDecimalsResponse { decimals }) } diff --git a/contracts/liquidity_hub/pool-manager/src/state.rs b/contracts/liquidity_hub/pool-manager/src/state.rs index c6e3c2c6..efba906c 100644 --- a/contracts/liquidity_hub/pool-manager/src/state.rs +++ b/contracts/liquidity_hub/pool-manager/src/state.rs @@ -35,6 +35,8 @@ pub fn get_pair_by_identifier( .may_load(deps.storage, pair_identifier)? .ok_or(ContractError::UnExistingPair {}) } +// Remove after adding decimals to pair info +pub const NATIVE_TOKEN_DECIMALS: Map<&[u8], u8> = Map::new("allow_native_token"); pub fn get_decimals(pair_info: &PairInfo) -> Vec { pair_info.asset_decimals.clone() @@ -43,18 +45,6 @@ pub fn get_decimals(pair_info: &PairInfo) -> Vec { // Swap routes are used to establish defined routes for a given fee token to a desired fee token and is used for fee collection pub const SWAP_ROUTES: Map<(&str, &str), Vec> = Map::new("swap_routes"); -//todo remove -// Dyanmic Maps for Fee and Pair info -pub const COLLECTABLE_PROTOCOL_FEES: Map<&str, Vec> = Map::new("collected_protocol_fees"); - - -//todo remove -pub const TOTAL_COLLECTED_PROTOCOL_FEES: Map<&str, Vec> = - Map::new("total_collected_protocol_fees"); - -//todo remove -pub const ALL_TIME_BURNED_FEES: Map<&str, Vec> = Map::new("all_time_burned_fees"); - pub const MANAGER_CONFIG: Item = Item::new("manager_config"); pub const PAIR_COUNTER: Item = Item::new("vault_count"); diff --git a/contracts/liquidity_hub/pool-manager/src/tests/integration_tests.rs b/contracts/liquidity_hub/pool-manager/src/tests/integration_tests.rs index 08544d44..2f4f24bd 100644 --- a/contracts/liquidity_hub/pool-manager/src/tests/integration_tests.rs +++ b/contracts/liquidity_hub/pool-manager/src/tests/integration_tests.rs @@ -1,8 +1,8 @@ use crate::ContractError; use cosmwasm_std::{coin, Addr, Coin, Decimal, Uint128}; use white_whale_std::fee::Fee; +use white_whale_std::pool_manager::PoolFee; use white_whale_std::pool_network::asset::MINIMUM_LIQUIDITY_AMOUNT; -use white_whale_std::pool_network::pair::PoolFee; use super::suite::TestingSuite; @@ -105,6 +105,7 @@ fn deposit_and_withdraw_sanity_check() { burn_fee: Fee { share: Decimal::zero(), }, + extra_fees: vec![], }; #[cfg(feature = "osmosis")] @@ -121,6 +122,7 @@ fn deposit_and_withdraw_sanity_check() { osmosis_fee: Fee { share: Decimal::zero(), }, + extra_fees: vec![], }; // Create a pair @@ -259,6 +261,7 @@ mod pair_creation_failures { burn_fee: Fee { share: Decimal::zero(), }, + extra_fees: vec![], }; #[cfg(feature = "osmosis")] @@ -275,6 +278,7 @@ mod pair_creation_failures { osmosis_fee: Fee { share: Decimal::zero(), }, + extra_fees: vec![], }; // Create a pair suite @@ -324,6 +328,7 @@ mod pair_creation_failures { burn_fee: Fee { share: Decimal::zero(), }, + extra_fees: vec![], }; #[cfg(feature = "osmosis")] @@ -340,6 +345,7 @@ mod pair_creation_failures { osmosis_fee: Fee { share: Decimal::zero(), }, + extra_fees: vec![], }; // Create a pair @@ -405,6 +411,7 @@ mod router { burn_fee: Fee { share: Decimal::bps(50), // 0.5% }, + extra_fees: vec![], }; #[cfg(feature = "osmosis")] let pool_fees = PoolFee { @@ -420,6 +427,7 @@ mod router { osmosis_fee: Fee { share: Decimal::bps(50), }, + extra_fees: vec![], }; // Create a pair @@ -591,6 +599,7 @@ mod router { burn_fee: Fee { share: Decimal::zero(), }, + extra_fees: vec![], }; #[cfg(feature = "osmosis")] @@ -607,6 +616,7 @@ mod router { osmosis_fee: Fee { share: Decimal::from_ratio(1u128, 100_000u128), }, + extra_fees: vec![], }; // Create a pair @@ -727,6 +737,7 @@ mod router { burn_fee: Fee { share: Decimal::zero(), }, + extra_fees: vec![], }; #[cfg(feature = "osmosis")] @@ -743,6 +754,7 @@ mod router { osmosis_fee: Fee { share: Decimal::from_ratio(1u128, 100_000u128), }, + extra_fees: vec![], }; // Create a pair @@ -791,6 +803,7 @@ mod router { |result| { // ensure we got 999,000 in the response (1m - initial liquidity amount) let result = result.unwrap(); + println!("{:?}", result); assert!(result.has_event(&Event::new("wasm").add_attribute("share", "999000"))); }, ); @@ -879,6 +892,7 @@ mod router { burn_fee: Fee { share: Decimal::zero(), }, + extra_fees: vec![], }; #[cfg(feature = "osmosis")] @@ -895,6 +909,7 @@ mod router { osmosis_fee: Fee { share: Decimal::from_ratio(1u128, 100_000u128), }, + extra_fees: vec![], }; // Create a pair @@ -1102,6 +1117,7 @@ mod router { burn_fee: Fee { share: Decimal::bps(50), // 0.5% }, + extra_fees: vec![], }; #[cfg(feature = "osmosis")] let pool_fees = PoolFee { @@ -1117,6 +1133,7 @@ mod router { osmosis_fee: Fee { share: Decimal::bps(50), }, + extra_fees: vec![], }; // Create a pair @@ -1271,6 +1288,7 @@ mod swapping { burn_fee: Fee { share: Decimal::zero(), }, + extra_fees: vec![], }; #[cfg(feature = "osmosis")] @@ -1287,6 +1305,7 @@ mod swapping { osmosis_fee: Fee { share: Decimal::from_ratio(1u128, 100_000u128), }, + extra_fees: vec![], }; // Create a pair @@ -1483,6 +1502,7 @@ mod swapping { burn_fee: Fee { share: Decimal::zero(), }, + extra_fees: vec![], }; #[cfg(feature = "osmosis")] @@ -1499,6 +1519,7 @@ mod swapping { osmosis_fee: Fee { share: Decimal::from_ratio(1u128, 1000u128), }, + extra_fees: vec![], }; // Create a pair @@ -1686,6 +1707,7 @@ mod swapping { burn_fee: Fee { share: Decimal::zero(), }, + extra_fees: vec![], }; #[cfg(feature = "osmosis")] @@ -1702,6 +1724,7 @@ mod swapping { osmosis_fee: Fee { share: Decimal::zero(), }, + extra_fees: vec![], }; // Create a pair diff --git a/contracts/liquidity_hub/pool-manager/src/tests/suite.rs b/contracts/liquidity_hub/pool-manager/src/tests/suite.rs index 45c43e5e..53a6411a 100644 --- a/contracts/liquidity_hub/pool-manager/src/tests/suite.rs +++ b/contracts/liquidity_hub/pool-manager/src/tests/suite.rs @@ -7,11 +7,9 @@ use cw_multi_test::{ App, AppBuilder, AppResponse, BankKeeper, Contract, ContractWrapper, DistributionKeeper, Executor, FailingModule, GovFailingModule, IbcFailingModule, StakeKeeper, WasmKeeper, }; +use white_whale_std::pool_manager::PoolFee; +use white_whale_std::pool_network::asset::{AssetInfo, PairType}; use white_whale_std::pool_network::pair::{ReverseSimulationResponse, SimulationResponse}; -use white_whale_std::pool_network::{ - asset::{AssetInfo, PairType}, - pair::PoolFee, -}; use white_whale_testing::multi_test::stargate_mock::StargateMock; use cw_multi_test::addons::{MockAddressGenerator, MockApiBech32}; diff --git a/packages/white-whale-std/src/pool_manager.rs b/packages/white-whale-std/src/pool_manager.rs index dd7ee4b7..4247ab70 100644 --- a/packages/white-whale-std/src/pool_manager.rs +++ b/packages/white-whale-std/src/pool_manager.rs @@ -1,12 +1,12 @@ use std::fmt; -use crate::pool_network::{ +use crate::{fee::Fee, pool_network::{ asset::PairType, factory::NativeTokenDecimalsResponse, - pair::{PoolFee, ReverseSimulationResponse, SimulationResponse}, -}; + pair::{ReverseSimulationResponse, SimulationResponse}, +}}; use cosmwasm_schema::{cw_serde, QueryResponses}; -use cosmwasm_std::{Coin, Decimal, Uint128}; +use cosmwasm_std::{Coin, Decimal, StdError, StdResult, Uint128, Uint256}; use cw_ownable::{cw_ownable_execute, cw_ownable_query}; #[cw_serde] @@ -78,12 +78,124 @@ impl fmt::Display for SwapRoute { // Define a structure for Fees which names a number of defined fee collection types, maybe leaving room for a custom room a user can use to pass a fee with a defined custom name #[cw_serde] -pub enum Fee { +pub enum FeeTypes { Protocol, LiquidityProvider, FlashLoanFees, Custom(String), } + +/// Represents the fee structure for transactions within a pool. +/// +/// +/// # Fields +/// - `protocol_fee`: The fee percentage charged by the protocol on each transaction to support +/// operational and developmental needs. +/// - `swap_fee`: The fee percentage allocated to liquidity providers as a reward for supplying +/// liquidity to the pool, incentivizing participation and ensuring pool health. +/// - `burn_fee`: A fee percentage that is burned on each transaction, helping manage the token +/// economy by reducing supply over time, potentially increasing token value. +/// - `osmosis_fee` (optional): Specific to the Osmosis feature, this fee is charged on each +/// transaction when the Osmosis feature is enabled, supporting specific ecosystem requirements. +/// - `extra_fees`: A vector of custom fees allowing for extensible and adaptable fee structures +/// to meet diverse and evolving needs. Validation ensures that the total of all fees does not +/// exceed 100%, maintaining fairness and avoiding overcharging. +/// +/// # Features +/// - `osmosis`: Enables the `osmosis_fee` field, integrating specific fee requirements for the +/// Osmosis protocol within the pool's fee structure. +#[cw_serde] +pub struct PoolFee { + /// Fee percentage charged on each transaction for the protocol's benefit. + pub protocol_fee: Fee, + + /// Fee percentage allocated to liquidity providers on each swap. + pub swap_fee: Fee, + + /// Fee percentage that is burned on each transaction. Burning a portion of the transaction fee + /// helps in reducing the overall token supply. + pub burn_fee: Fee, + + /// Fee percentage charged on each transaction specifically for Osmosis integrations. This fee + /// is only applicable when the `osmosis` feature is enabled + #[cfg(feature = "osmosis")] + pub osmosis_fee: Fee, + + /// A list of custom, additional fees that can be defined for specific use cases or additional + /// functionalities. This vector enables the flexibility to introduce new fees without altering + /// the core fee structure. Total of all fees, including custom ones, is validated to not exceed + /// 100%, ensuring a balanced and fair fee distribution. + pub extra_fees: Vec, +} +impl PoolFee { + /// Validates the PoolFee structure to ensure no individual fee is zero or negative + /// and the sum of all fees does not exceed 20%. + pub fn is_valid(&self) -> StdResult<()> { + let mut total_share = Decimal::zero(); + + // Validate predefined fees and accumulate their shares + let predefined_fees = [ + &self.protocol_fee, + &self.swap_fee, + &self.burn_fee, + #[cfg(feature = "osmosis")] + &self.osmosis_fee, + ]; + + for fee in predefined_fees.iter().filter_map(|f| Some(*f)) { + fee.is_valid()?; // Validates the fee is not >= 100% + total_share = total_share + fee.share; + } + + // Validate extra fees and accumulate their shares + for fee in &self.extra_fees { + fee.is_valid()?; // Validates the fee is not >= 100% + total_share = total_share + fee.share; + } + + // Check if the total share exceeds 20% + if total_share > Decimal::percent(20) { + return Err(StdError::generic_err("Total fees cannot exceed 20%")); + } + + Ok(()) + } + + /// Computes and applies all defined fees to a given amount. + /// Returns the total amount of fees deducted. + pub fn compute_and_apply_fees(&self, amount: Uint256) -> StdResult { + let mut total_fee_amount = Uint256::zero(); + + // Compute protocol fee + let protocol_fee_amount = self.protocol_fee.compute(amount); + total_fee_amount += protocol_fee_amount; + + // Compute swap fee + let swap_fee_amount = self.swap_fee.compute(amount); + total_fee_amount += swap_fee_amount; + + // Compute burn fee + let burn_fee_amount = self.burn_fee.compute(amount); + total_fee_amount += burn_fee_amount; + + // Compute osmosis fee if applicable + #[cfg(feature = "osmosis")]{ + let osmosis_fee_amount = self.osmosis_fee.compute(amount); + + total_fee_amount += osmosis_fee_amount; + } + + // Compute extra fees + for extra_fee in &self.extra_fees { + let extra_fee_amount = extra_fee.compute(amount); + total_fee_amount += extra_fee_amount; + } + + // Convert the total fee amount to Uint128 (or handle potential conversion failure) + Uint128::try_from(total_fee_amount).map_err(|_| StdError::generic_err("Fee conversion error")) + } +} + #[cw_serde] pub struct StableSwapParams { From 02ac728719aa14ca571cc4580f317b410a05c0b8 Mon Sep 17 00:00:00 2001 From: 0xFable <0xfable@protonmail.com> Date: Wed, 10 Apr 2024 03:16:25 +0100 Subject: [PATCH 3/4] fix: Move PoolFee to fee.rs and add tests --- Cargo.lock | 1 + .../liquidity_hub/pool-manager/src/helpers.rs | 2 +- .../pool-manager/src/liquidity/commands.rs | 17 +- .../pool-manager/src/manager/commands.rs | 4 +- .../liquidity_hub/pool-manager/src/state.rs | 9 +- .../src/tests/integration_tests.rs | 6 +- .../pool-manager/src/tests/suite.rs | 6 +- packages/white-whale-std/Cargo.toml | 1 + packages/white-whale-std/src/fee.rs | 166 +++++++++++++++++- packages/white-whale-std/src/pool_manager.rs | 115 +----------- 10 files changed, 178 insertions(+), 149 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index de075a93..aab0810a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1750,6 +1750,7 @@ dependencies = [ "protobuf", "schemars", "serde", + "test-case", "thiserror", "uint", ] diff --git a/contracts/liquidity_hub/pool-manager/src/helpers.rs b/contracts/liquidity_hub/pool-manager/src/helpers.rs index 38325890..faa5161b 100644 --- a/contracts/liquidity_hub/pool-manager/src/helpers.rs +++ b/contracts/liquidity_hub/pool-manager/src/helpers.rs @@ -4,7 +4,7 @@ use std::ops::Mul; use cosmwasm_schema::cw_serde; use cosmwasm_std::{Coin, Decimal, Decimal256, StdError, StdResult, Storage, Uint128, Uint256}; -use white_whale_std::pool_manager::PoolFee; +use white_whale_std::fee::PoolFee; use white_whale_std::pool_network::asset::{Asset, AssetInfo, PairType}; use crate::error::ContractError; diff --git a/contracts/liquidity_hub/pool-manager/src/liquidity/commands.rs b/contracts/liquidity_hub/pool-manager/src/liquidity/commands.rs index 9bff7767..0bac12ea 100644 --- a/contracts/liquidity_hub/pool-manager/src/liquidity/commands.rs +++ b/contracts/liquidity_hub/pool-manager/src/liquidity/commands.rs @@ -1,4 +1,4 @@ -use cosmwasm_std::{Addr, BankMsg, Coin, CosmosMsg, DepsMut, Env, MessageInfo, Response, Uint256}; +use cosmwasm_std::{Addr, BankMsg, Coin, CosmosMsg, DepsMut, Env, MessageInfo, Response}; use white_whale_std::pool_network::asset::PairType; use crate::{ @@ -19,21 +19,6 @@ use white_whale_std::pool_network::{ pub const MAX_ASSETS_PER_POOL: usize = 4; pub const LP_SYMBOL: &str = "uLP"; -/// Gets the protocol fee amount for the given asset_id -pub fn get_protocol_fee_for_asset(collected_protocol_fees: Vec, asset_id: String) -> Uint128 { - let protocol_fee_asset = collected_protocol_fees - .iter() - .find(|&protocol_fee_asset| protocol_fee_asset.clone().denom == asset_id.clone()) - .cloned(); - - // get the protocol fee for the given pool_asset - if let Some(protocol_fee_asset) = protocol_fee_asset { - protocol_fee_asset.amount - } else { - Uint128::zero() - } -} - // todo allow providing liquidity with a single asset //todo allow passing an optional locking period for the LP once the liquidity is provided, so tokens diff --git a/contracts/liquidity_hub/pool-manager/src/manager/commands.rs b/contracts/liquidity_hub/pool-manager/src/manager/commands.rs index 674742a9..6781dd1e 100644 --- a/contracts/liquidity_hub/pool-manager/src/manager/commands.rs +++ b/contracts/liquidity_hub/pool-manager/src/manager/commands.rs @@ -2,7 +2,7 @@ use cosmwasm_std::{ attr, Attribute, Coin, CosmosMsg, DepsMut, Env, MessageInfo, Response, Uint128, }; use white_whale_std::{ - pool_manager::PoolFee, + fee::PoolFee, pool_network::{asset::PairType, querier::query_native_decimals}, whale_lair::fill_rewards_msg, }; @@ -28,7 +28,7 @@ pub const LP_SYMBOL: &str = "uLP"; /// ```rust /// # use cosmwasm_std::{DepsMut, Decimal, Env, MessageInfo, Response, CosmosMsg, WasmMsg, to_json_binary}; /// # use white_whale_std::pool_network::{asset::{PairType}}; -/// # use white_whale_std::pool_manager::PoolFee; +/// # use white_whale_std::fee::PoolFee; /// # use white_whale_std::fee::Fee; /// # use pool_manager::error::ContractError; /// # use pool_manager::manager::commands::MAX_ASSETS_PER_POOL; diff --git a/contracts/liquidity_hub/pool-manager/src/state.rs b/contracts/liquidity_hub/pool-manager/src/state.rs index efba906c..298c62cc 100644 --- a/contracts/liquidity_hub/pool-manager/src/state.rs +++ b/contracts/liquidity_hub/pool-manager/src/state.rs @@ -1,8 +1,7 @@ use cosmwasm_schema::cw_serde; -use cosmwasm_std::{Addr, Api, Coin, Deps, Order, StdResult, Storage}; -use cw_storage_plus::{Bound, Index, IndexList, IndexedMap, Item, Map, UniqueIndex}; +use cosmwasm_std::{Addr, Coin, Deps}; +use cw_storage_plus::{Index, IndexList, IndexedMap, Item, Map, UniqueIndex}; use white_whale_std::pool_manager::{PairInfo, SwapOperation}; -use white_whale_std::pool_network::asset::{Asset, AssetInfoRaw}; use white_whale_std::pool_network::pair::FeatureToggle; use crate::ContractError; @@ -48,10 +47,6 @@ pub const SWAP_ROUTES: Map<(&str, &str), Vec> = Map::new("swap_ro pub const MANAGER_CONFIG: Item = Item::new("manager_config"); pub const PAIR_COUNTER: Item = Item::new("vault_count"); -// settings for pagination -const MAX_LIMIT: u32 = 1000; -const DEFAULT_LIMIT: u32 = 10; - #[cw_serde] pub struct Config { pub whale_lair_addr: Addr, diff --git a/contracts/liquidity_hub/pool-manager/src/tests/integration_tests.rs b/contracts/liquidity_hub/pool-manager/src/tests/integration_tests.rs index 2f4f24bd..2ffe6794 100644 --- a/contracts/liquidity_hub/pool-manager/src/tests/integration_tests.rs +++ b/contracts/liquidity_hub/pool-manager/src/tests/integration_tests.rs @@ -1,7 +1,7 @@ use crate::ContractError; use cosmwasm_std::{coin, Addr, Coin, Decimal, Uint128}; use white_whale_std::fee::Fee; -use white_whale_std::pool_manager::PoolFee; +use white_whale_std::fee::PoolFee; use white_whale_std::pool_network::asset::MINIMUM_LIQUIDITY_AMOUNT; use super::suite::TestingSuite; @@ -221,11 +221,11 @@ fn deposit_and_withdraw_sanity_check() { .query_all_balances(creator.to_string(), |result| { let balances = result.unwrap(); assert!(balances.iter().any(|coin| { - coin.denom == "uwhale".to_string() + coin.denom == *"uwhale" && coin.amount == Uint128::from(1_000_000u128) - MINIMUM_LIQUIDITY_AMOUNT })); assert!(balances.iter().any(|coin| { - coin.denom == "uluna".to_string() + coin.denom == *"uluna" && coin.amount == Uint128::from(1_000_000u128) - MINIMUM_LIQUIDITY_AMOUNT })); }); diff --git a/contracts/liquidity_hub/pool-manager/src/tests/suite.rs b/contracts/liquidity_hub/pool-manager/src/tests/suite.rs index 53a6411a..0a43d000 100644 --- a/contracts/liquidity_hub/pool-manager/src/tests/suite.rs +++ b/contracts/liquidity_hub/pool-manager/src/tests/suite.rs @@ -7,7 +7,7 @@ use cw_multi_test::{ App, AppBuilder, AppResponse, BankKeeper, Contract, ContractWrapper, DistributionKeeper, Executor, FailingModule, GovFailingModule, IbcFailingModule, StakeKeeper, WasmKeeper, }; -use white_whale_std::pool_manager::PoolFee; +use white_whale_std::fee::PoolFee; use white_whale_std::pool_network::asset::{AssetInfo, PairType}; use white_whale_std::pool_network::pair::{ReverseSimulationResponse, SimulationResponse}; use white_whale_testing::multi_test::stargate_mock::StargateMock; @@ -412,9 +412,7 @@ impl TestingSuite { ) -> &Self { let pair_info_response: StdResult = self.app.wrap().query_wasm_smart( &self.pool_manager_addr, - &white_whale_std::pool_manager::QueryMsg::Pair { - pair_identifier: pair_identifier, - }, + &white_whale_std::pool_manager::QueryMsg::Pair { pair_identifier }, ); result(pair_info_response); diff --git a/packages/white-whale-std/Cargo.toml b/packages/white-whale-std/Cargo.toml index 60c6d994..aebb9eb0 100644 --- a/packages/white-whale-std/Cargo.toml +++ b/packages/white-whale-std/Cargo.toml @@ -45,3 +45,4 @@ thiserror.workspace = true [dev-dependencies] cw-multi-test.workspace = true +test-case.workspace = true \ No newline at end of file diff --git a/packages/white-whale-std/src/fee.rs b/packages/white-whale-std/src/fee.rs index 7f865b10..532685c5 100644 --- a/packages/white-whale-std/src/fee.rs +++ b/packages/white-whale-std/src/fee.rs @@ -1,5 +1,5 @@ use cosmwasm_schema::cw_serde; -use cosmwasm_std::{Decimal, Decimal256, StdError, StdResult, Uint256}; +use cosmwasm_std::{Decimal, Decimal256, StdError, StdResult, Uint128, Uint256}; use std::fmt::{Display, Formatter}; #[cw_serde] @@ -62,11 +62,125 @@ impl VaultFee { } } +/// Represents the fee structure for transactions within a pool. +/// +/// +/// # Fields +/// - `protocol_fee`: The fee percentage charged by the protocol on each transaction to support +/// operational and developmental needs. +/// - `swap_fee`: The fee percentage allocated to liquidity providers as a reward for supplying +/// liquidity to the pool, incentivizing participation and ensuring pool health. +/// - `burn_fee`: A fee percentage that is burned on each transaction, helping manage the token +/// economy by reducing supply over time, potentially increasing token value. +/// - `osmosis_fee` (optional): Specific to the Osmosis feature, this fee is charged on each +/// transaction when the Osmosis feature is enabled, supporting specific ecosystem requirements. +/// - `extra_fees`: A vector of custom fees allowing for extensible and adaptable fee structures +/// to meet diverse and evolving needs. Validation ensures that the total of all fees does not +/// exceed 100%, maintaining fairness and avoiding overcharging. +/// +/// # Features +/// - `osmosis`: Enables the `osmosis_fee` field, integrating specific fee requirements for the +/// Osmosis protocol within the pool's fee structure. +#[cw_serde] +pub struct PoolFee { + /// Fee percentage charged on each transaction for the protocol's benefit. + pub protocol_fee: Fee, + + /// Fee percentage allocated to liquidity providers on each swap. + pub swap_fee: Fee, + + /// Fee percentage that is burned on each transaction. Burning a portion of the transaction fee + /// helps in reducing the overall token supply. + pub burn_fee: Fee, + + /// Fee percentage charged on each transaction specifically for Osmosis integrations. This fee + /// is only applicable when the `osmosis` feature is enabled + #[cfg(feature = "osmosis")] + pub osmosis_fee: Fee, + + /// A list of custom, additional fees that can be defined for specific use cases or additional + /// functionalities. This vector enables the flexibility to introduce new fees without altering + /// the core fee structure. Total of all fees, including custom ones, is validated to not exceed + /// 100%, ensuring a balanced and fair fee distribution. + pub extra_fees: Vec, +} +impl PoolFee { + /// Validates the PoolFee structure to ensure no individual fee is zero or negative + /// and the sum of all fees does not exceed 20%. + pub fn is_valid(&self) -> StdResult<()> { + let mut total_share = Decimal::zero(); + + // Validate predefined fees and accumulate their shares + let predefined_fees = [ + &self.protocol_fee, + &self.swap_fee, + &self.burn_fee, + #[cfg(feature = "osmosis")] + &self.osmosis_fee, + ]; + + for fee in predefined_fees.iter().copied() { + fee.is_valid()?; // Validates the fee is not >= 100% + total_share += fee.share; + } + + // Validate extra fees and accumulate their shares + for fee in &self.extra_fees { + fee.is_valid()?; // Validates the fee is not >= 100% + total_share += fee.share; + } + + // Check if the total share exceeds 20% + if total_share > Decimal::percent(20) { + return Err(StdError::generic_err("Total fees cannot exceed 20%")); + } + + Ok(()) + } + + /// Computes and applies all defined fees to a given amount. + /// Returns the total amount of fees deducted. + pub fn compute_and_apply_fees(&self, amount: Uint256) -> StdResult { + let mut total_fee_amount = Uint256::zero(); + + // Compute protocol fee + let protocol_fee_amount = self.protocol_fee.compute(amount); + total_fee_amount += protocol_fee_amount; + + // Compute swap fee + let swap_fee_amount = self.swap_fee.compute(amount); + total_fee_amount += swap_fee_amount; + + // Compute burn fee + let burn_fee_amount = self.burn_fee.compute(amount); + total_fee_amount += burn_fee_amount; + + // Compute osmosis fee if applicable + #[cfg(feature = "osmosis")]{ + let osmosis_fee_amount = self.osmosis_fee.compute(amount); + + total_fee_amount += osmosis_fee_amount; + } + + // Compute extra fees + for extra_fee in &self.extra_fees { + let extra_fee_amount = extra_fee.compute(amount); + total_fee_amount += extra_fee_amount; + } + + // Convert the total fee amount to Uint128 (or handle potential conversion failure) + Uint128::try_from(total_fee_amount).map_err(|_| StdError::generic_err("Fee conversion error")) + } + +} + + #[cfg(test)] mod tests { - use cosmwasm_std::{Decimal, StdError, Uint128}; + use cosmwasm_std::{Decimal, StdError, Uint128, Uint256}; - use crate::fee::{Fee, VaultFee}; + use crate::fee::{Fee, PoolFee, VaultFee}; + use test_case::test_case; #[test] fn valid_fee() { @@ -206,4 +320,50 @@ mod tests { }; assert_eq!(vault_fee.is_valid(), Ok(())); } + + #[test_case(Decimal::permille(1), Decimal::permille(2), Decimal::permille(1), Uint256::from(1000u128), Uint128::from(4u128); "low fee scenario")] + #[test_case(Decimal::percent(1), Decimal::percent(2), Decimal::zero(), Uint256::from(1000u128), Uint128::from(30u128); "higher fee scenario")] + fn pool_fee_application( + protocol_fee_share: Decimal, + swap_fee_share: Decimal, + burn_fee_share: Decimal, + amount: Uint256, + expected_fee_deducted: Uint128, + ) { + let protocol_fee = Fee { share: protocol_fee_share }; + let swap_fee = Fee { share: swap_fee_share }; + let burn_fee = Fee { share: burn_fee_share }; + let extra_fees = vec![]; // Assuming no extra fees for simplicity + + let pool_fee = PoolFee { + protocol_fee, + swap_fee, + burn_fee, + #[cfg(feature = "osmosis")] + osmosis_fee: Fee { share: Decimal::zero() }, + extra_fees, + }; + + let total_fee_deducted = pool_fee.compute_and_apply_fees(amount).unwrap(); + assert_eq!(total_fee_deducted, expected_fee_deducted, "The total deducted fees did not match the expected value."); + } + + #[test] + fn pool_fee_exceeds_limit() { + let protocol_fee = Fee { share: Decimal::percent(10) }; + let swap_fee = Fee { share: Decimal::percent(5) }; + let burn_fee = Fee { share: Decimal::percent(5) }; + let extra_fees = vec![Fee { share: Decimal::percent(1) }]; // Sum is 21% + + let pool_fee = PoolFee { + protocol_fee, + swap_fee, + burn_fee, + #[cfg(feature = "osmosis")] + osmosis_fee: Fee { share: Decimal::zero() }, + extra_fees, + }; + + assert_eq!(pool_fee.is_valid(), Err(StdError::generic_err("Total fees cannot exceed 20%"))); + } } diff --git a/packages/white-whale-std/src/pool_manager.rs b/packages/white-whale-std/src/pool_manager.rs index 4247ab70..5178b4b6 100644 --- a/packages/white-whale-std/src/pool_manager.rs +++ b/packages/white-whale-std/src/pool_manager.rs @@ -1,12 +1,12 @@ use std::fmt; -use crate::{fee::Fee, pool_network::{ +use crate::{fee::PoolFee, pool_network::{ asset::PairType, factory::NativeTokenDecimalsResponse, pair::{ReverseSimulationResponse, SimulationResponse}, }}; use cosmwasm_schema::{cw_serde, QueryResponses}; -use cosmwasm_std::{Coin, Decimal, StdError, StdResult, Uint128, Uint256}; +use cosmwasm_std::{Coin, Decimal, Uint128}; use cw_ownable::{cw_ownable_execute, cw_ownable_query}; #[cw_serde] @@ -85,116 +85,6 @@ pub enum FeeTypes { Custom(String), } -/// Represents the fee structure for transactions within a pool. -/// -/// -/// # Fields -/// - `protocol_fee`: The fee percentage charged by the protocol on each transaction to support -/// operational and developmental needs. -/// - `swap_fee`: The fee percentage allocated to liquidity providers as a reward for supplying -/// liquidity to the pool, incentivizing participation and ensuring pool health. -/// - `burn_fee`: A fee percentage that is burned on each transaction, helping manage the token -/// economy by reducing supply over time, potentially increasing token value. -/// - `osmosis_fee` (optional): Specific to the Osmosis feature, this fee is charged on each -/// transaction when the Osmosis feature is enabled, supporting specific ecosystem requirements. -/// - `extra_fees`: A vector of custom fees allowing for extensible and adaptable fee structures -/// to meet diverse and evolving needs. Validation ensures that the total of all fees does not -/// exceed 100%, maintaining fairness and avoiding overcharging. -/// -/// # Features -/// - `osmosis`: Enables the `osmosis_fee` field, integrating specific fee requirements for the -/// Osmosis protocol within the pool's fee structure. -#[cw_serde] -pub struct PoolFee { - /// Fee percentage charged on each transaction for the protocol's benefit. - pub protocol_fee: Fee, - - /// Fee percentage allocated to liquidity providers on each swap. - pub swap_fee: Fee, - - /// Fee percentage that is burned on each transaction. Burning a portion of the transaction fee - /// helps in reducing the overall token supply. - pub burn_fee: Fee, - - /// Fee percentage charged on each transaction specifically for Osmosis integrations. This fee - /// is only applicable when the `osmosis` feature is enabled - #[cfg(feature = "osmosis")] - pub osmosis_fee: Fee, - - /// A list of custom, additional fees that can be defined for specific use cases or additional - /// functionalities. This vector enables the flexibility to introduce new fees without altering - /// the core fee structure. Total of all fees, including custom ones, is validated to not exceed - /// 100%, ensuring a balanced and fair fee distribution. - pub extra_fees: Vec, -} -impl PoolFee { - /// Validates the PoolFee structure to ensure no individual fee is zero or negative - /// and the sum of all fees does not exceed 20%. - pub fn is_valid(&self) -> StdResult<()> { - let mut total_share = Decimal::zero(); - - // Validate predefined fees and accumulate their shares - let predefined_fees = [ - &self.protocol_fee, - &self.swap_fee, - &self.burn_fee, - #[cfg(feature = "osmosis")] - &self.osmosis_fee, - ]; - - for fee in predefined_fees.iter().filter_map(|f| Some(*f)) { - fee.is_valid()?; // Validates the fee is not >= 100% - total_share = total_share + fee.share; - } - - // Validate extra fees and accumulate their shares - for fee in &self.extra_fees { - fee.is_valid()?; // Validates the fee is not >= 100% - total_share = total_share + fee.share; - } - - // Check if the total share exceeds 20% - if total_share > Decimal::percent(20) { - return Err(StdError::generic_err("Total fees cannot exceed 20%")); - } - - Ok(()) - } - - /// Computes and applies all defined fees to a given amount. - /// Returns the total amount of fees deducted. - pub fn compute_and_apply_fees(&self, amount: Uint256) -> StdResult { - let mut total_fee_amount = Uint256::zero(); - - // Compute protocol fee - let protocol_fee_amount = self.protocol_fee.compute(amount); - total_fee_amount += protocol_fee_amount; - - // Compute swap fee - let swap_fee_amount = self.swap_fee.compute(amount); - total_fee_amount += swap_fee_amount; - - // Compute burn fee - let burn_fee_amount = self.burn_fee.compute(amount); - total_fee_amount += burn_fee_amount; - - // Compute osmosis fee if applicable - #[cfg(feature = "osmosis")]{ - let osmosis_fee_amount = self.osmosis_fee.compute(amount); - - total_fee_amount += osmosis_fee_amount; - } - - // Compute extra fees - for extra_fee in &self.extra_fees { - let extra_fee_amount = extra_fee.compute(amount); - total_fee_amount += extra_fee_amount; - } - - // Convert the total fee amount to Uint128 (or handle potential conversion failure) - Uint128::try_from(total_fee_amount).map_err(|_| StdError::generic_err("Fee conversion error")) - } -} #[cw_serde] @@ -236,7 +126,6 @@ pub struct MigrateMsg {} pub enum ExecuteMsg { CreatePair { asset_denoms: Vec, - // TODO: Remap to NPoolFee maybe pool_fees: PoolFee, pair_type: PairType, pair_identifier: Option, From 99116600ab835a154f378ef0df004c129a23b850 Mon Sep 17 00:00:00 2001 From: 0xFable <0xfable@protonmail.com> Date: Wed, 10 Apr 2024 03:41:10 +0100 Subject: [PATCH 4/4] fix: remove get_decimals --- .../liquidity_hub/pool-manager/src/queries.rs | 6 +- .../liquidity_hub/pool-manager/src/state.rs | 4 -- packages/white-whale-std/src/fee.rs | 72 ++++++++++++------- packages/white-whale-std/src/pool_manager.rs | 14 ++-- 4 files changed, 59 insertions(+), 37 deletions(-) diff --git a/contracts/liquidity_hub/pool-manager/src/queries.rs b/contracts/liquidity_hub/pool-manager/src/queries.rs index 604acfa0..3090ed6a 100644 --- a/contracts/liquidity_hub/pool-manager/src/queries.rs +++ b/contracts/liquidity_hub/pool-manager/src/queries.rs @@ -12,7 +12,7 @@ use white_whale_std::pool_network::{ use crate::state::NATIVE_TOKEN_DECIMALS; use crate::{ helpers::{self, calculate_stableswap_y, StableSwapDirection}, - state::{get_decimals, get_pair_by_identifier}, + state::get_pair_by_identifier, ContractError, }; use crate::{math::Decimal256Helper, state::SWAP_ROUTES}; @@ -42,7 +42,7 @@ pub fn query_simulation( let ask_pool: Coin; let offer_decimal: u8; let ask_decimal: u8; - let decimals = get_decimals(&pair_info); + let decimals = pair_info.asset_decimals.clone(); // We now have the pools and pair info; we can now calculate the swap // Verify the pool if offer_asset.denom == pools[0].denom { @@ -108,7 +108,7 @@ pub fn query_reverse_simulation( let pair_info = get_pair_by_identifier(&deps, &pair_identifier)?; let pools = pair_info.assets.clone(); - let decimals = get_decimals(&pair_info); + let decimals = pair_info.asset_decimals.clone(); let offer_pool: Coin = pools[0].clone(); let offer_decimal = decimals[0]; let ask_pool: Coin = pools[1].clone(); diff --git a/contracts/liquidity_hub/pool-manager/src/state.rs b/contracts/liquidity_hub/pool-manager/src/state.rs index 298c62cc..8ec9a82a 100644 --- a/contracts/liquidity_hub/pool-manager/src/state.rs +++ b/contracts/liquidity_hub/pool-manager/src/state.rs @@ -37,10 +37,6 @@ pub fn get_pair_by_identifier( // Remove after adding decimals to pair info pub const NATIVE_TOKEN_DECIMALS: Map<&[u8], u8> = Map::new("allow_native_token"); -pub fn get_decimals(pair_info: &PairInfo) -> Vec { - pair_info.asset_decimals.clone() -} - // Swap routes are used to establish defined routes for a given fee token to a desired fee token and is used for fee collection pub const SWAP_ROUTES: Map<(&str, &str), Vec> = Map::new("swap_routes"); diff --git a/packages/white-whale-std/src/fee.rs b/packages/white-whale-std/src/fee.rs index 532685c5..203f4b69 100644 --- a/packages/white-whale-std/src/fee.rs +++ b/packages/white-whale-std/src/fee.rs @@ -63,8 +63,8 @@ impl VaultFee { } /// Represents the fee structure for transactions within a pool. -/// -/// +/// +/// /// # Fields /// - `protocol_fee`: The fee percentage charged by the protocol on each transaction to support /// operational and developmental needs. @@ -85,19 +85,19 @@ impl VaultFee { pub struct PoolFee { /// Fee percentage charged on each transaction for the protocol's benefit. pub protocol_fee: Fee, - - /// Fee percentage allocated to liquidity providers on each swap. + + /// Fee percentage allocated to liquidity providers on each swap. pub swap_fee: Fee, - + /// Fee percentage that is burned on each transaction. Burning a portion of the transaction fee /// helps in reducing the overall token supply. pub burn_fee: Fee, - + /// Fee percentage charged on each transaction specifically for Osmosis integrations. This fee /// is only applicable when the `osmosis` feature is enabled #[cfg(feature = "osmosis")] pub osmosis_fee: Fee, - + /// A list of custom, additional fees that can be defined for specific use cases or additional /// functionalities. This vector enables the flexibility to introduce new fees without altering /// the core fee structure. Total of all fees, including custom ones, is validated to not exceed @@ -156,10 +156,11 @@ impl PoolFee { total_fee_amount += burn_fee_amount; // Compute osmosis fee if applicable - #[cfg(feature = "osmosis")]{ - let osmosis_fee_amount = self.osmosis_fee.compute(amount); + #[cfg(feature = "osmosis")] + { + let osmosis_fee_amount = self.osmosis_fee.compute(amount); - total_fee_amount += osmosis_fee_amount; + total_fee_amount += osmosis_fee_amount; } // Compute extra fees @@ -169,12 +170,11 @@ impl PoolFee { } // Convert the total fee amount to Uint128 (or handle potential conversion failure) - Uint128::try_from(total_fee_amount).map_err(|_| StdError::generic_err("Fee conversion error")) + Uint128::try_from(total_fee_amount) + .map_err(|_| StdError::generic_err("Fee conversion error")) } - } - #[cfg(test)] mod tests { use cosmwasm_std::{Decimal, StdError, Uint128, Uint256}; @@ -330,9 +330,15 @@ mod tests { amount: Uint256, expected_fee_deducted: Uint128, ) { - let protocol_fee = Fee { share: protocol_fee_share }; - let swap_fee = Fee { share: swap_fee_share }; - let burn_fee = Fee { share: burn_fee_share }; + let protocol_fee = Fee { + share: protocol_fee_share, + }; + let swap_fee = Fee { + share: swap_fee_share, + }; + let burn_fee = Fee { + share: burn_fee_share, + }; let extra_fees = vec![]; // Assuming no extra fees for simplicity let pool_fee = PoolFee { @@ -340,30 +346,48 @@ mod tests { swap_fee, burn_fee, #[cfg(feature = "osmosis")] - osmosis_fee: Fee { share: Decimal::zero() }, + osmosis_fee: Fee { + share: Decimal::zero(), + }, extra_fees, }; let total_fee_deducted = pool_fee.compute_and_apply_fees(amount).unwrap(); - assert_eq!(total_fee_deducted, expected_fee_deducted, "The total deducted fees did not match the expected value."); + assert_eq!( + total_fee_deducted, expected_fee_deducted, + "The total deducted fees did not match the expected value." + ); } #[test] fn pool_fee_exceeds_limit() { - let protocol_fee = Fee { share: Decimal::percent(10) }; - let swap_fee = Fee { share: Decimal::percent(5) }; - let burn_fee = Fee { share: Decimal::percent(5) }; - let extra_fees = vec![Fee { share: Decimal::percent(1) }]; // Sum is 21% + let protocol_fee = Fee { + share: Decimal::percent(10), + }; + let swap_fee = Fee { + share: Decimal::percent(5), + }; + let burn_fee = Fee { + share: Decimal::percent(5), + }; + let extra_fees = vec![Fee { + share: Decimal::percent(1), + }]; // Sum is 21% let pool_fee = PoolFee { protocol_fee, swap_fee, burn_fee, #[cfg(feature = "osmosis")] - osmosis_fee: Fee { share: Decimal::zero() }, + osmosis_fee: Fee { + share: Decimal::zero(), + }, extra_fees, }; - assert_eq!(pool_fee.is_valid(), Err(StdError::generic_err("Total fees cannot exceed 20%"))); + assert_eq!( + pool_fee.is_valid(), + Err(StdError::generic_err("Total fees cannot exceed 20%")) + ); } } diff --git a/packages/white-whale-std/src/pool_manager.rs b/packages/white-whale-std/src/pool_manager.rs index 5178b4b6..6c12a4e7 100644 --- a/packages/white-whale-std/src/pool_manager.rs +++ b/packages/white-whale-std/src/pool_manager.rs @@ -1,10 +1,13 @@ use std::fmt; -use crate::{fee::PoolFee, pool_network::{ - asset::PairType, - factory::NativeTokenDecimalsResponse, - pair::{ReverseSimulationResponse, SimulationResponse}, -}}; +use crate::{ + fee::PoolFee, + pool_network::{ + asset::PairType, + factory::NativeTokenDecimalsResponse, + pair::{ReverseSimulationResponse, SimulationResponse}, + }, +}; use cosmwasm_schema::{cw_serde, QueryResponses}; use cosmwasm_std::{Coin, Decimal, Uint128}; use cw_ownable::{cw_ownable_execute, cw_ownable_query}; @@ -85,7 +88,6 @@ pub enum FeeTypes { Custom(String), } - #[cw_serde] pub struct StableSwapParams {