diff --git a/contracts/liquidity_hub/bonding-manager/src/helpers.rs b/contracts/liquidity_hub/bonding-manager/src/helpers.rs index 40ead8f35..543560168 100644 --- a/contracts/liquidity_hub/bonding-manager/src/helpers.rs +++ b/contracts/liquidity_hub/bonding-manager/src/helpers.rs @@ -7,7 +7,7 @@ use white_whale_std::bonding_manager::{ClaimableEpochsResponse, EpochResponse}; use white_whale_std::constants::LP_SYMBOL; use white_whale_std::epoch_manager::epoch_manager::EpochConfig; use white_whale_std::pool_manager::{ - PairInfoResponse, SimulateSwapOperationsResponse, SwapRouteResponse, + PoolInfoResponse, SimulateSwapOperationsResponse, SwapRouteResponse, }; use crate::contract::LP_WITHDRAWAL_REPLY_ID; @@ -128,13 +128,13 @@ pub fn handle_lp_tokens( .collect(); for lp_token in lp_tokens { // LP tokens have the format "{pair_label}.pool.{identifier}.{LP_SYMBOL}", get the identifier and not the LP SYMBOL - let pair_identifier = lp_token.denom.split(".pool.").collect::>()[1] + let pool_identifier = lp_token.denom.split(".pool.").collect::>()[1] .split('.') .collect::>()[0]; // if LP Tokens ,verify and withdraw then swap to whale let lp_withdrawal_msg = white_whale_std::pool_manager::ExecuteMsg::WithdrawLiquidity { - pair_identifier: pair_identifier.to_string(), + pool_identifier: pool_identifier.to_string(), }; // Add a submessage to withdraw the LP tokens let lp_msg: SubMsg = SubMsg { @@ -190,8 +190,8 @@ pub fn swap_coins_to_main_token( ); // check if the pool has any assets, if not skip the swap // Note we are only checking the first operation here. Might be better to another loop to check all operations - let pool_query = white_whale_std::pool_manager::QueryMsg::Pair { - pair_identifier: swap_routes + let pool_query = white_whale_std::pool_manager::QueryMsg::Pool { + pool_identifier: swap_routes .swap_route .swap_operations .first() @@ -200,11 +200,11 @@ pub fn swap_coins_to_main_token( }; let mut skip_swap = false; // Query for the pool to check if it has any assets - let resp: PairInfoResponse = deps + let resp: PoolInfoResponse = deps .querier .query_wasm_smart(config.pool_manager_addr.to_string(), &pool_query)?; // Check pair 'assets' and if either one has 0 amount then don't do swaps - resp.pair_info.assets.iter().for_each(|asset| { + resp.pool_info.assets.iter().for_each(|asset| { if asset.amount.is_zero() { skip_swap = true; } @@ -226,7 +226,7 @@ pub fn swap_coins_to_main_token( let msg = white_whale_std::pool_manager::ExecuteMsg::ExecuteSwapOperations { operations: swap_routes.swap_route.swap_operations.clone(), minimum_receive: Some(simulate.amount), - to: None, + receiver: None, max_spread: Some(Decimal::percent(5)), }; let binary_msg = to_json_binary(&msg)?; diff --git a/contracts/liquidity_hub/bonding-manager/src/tests/claim.rs b/contracts/liquidity_hub/bonding-manager/src/tests/claim.rs index 34f659a7b..e41955596 100644 --- a/contracts/liquidity_hub/bonding-manager/src/tests/claim.rs +++ b/contracts/liquidity_hub/bonding-manager/src/tests/claim.rs @@ -182,7 +182,7 @@ fn test_claim_successfully() { sender.clone(), asset_infos.clone(), pool_fees.clone(), - white_whale_std::pool_network::asset::PairType::ConstantProduct, + white_whale_std::pool_manager::PoolType::ConstantProduct, Some("whale-uusdc".to_string()), vec![coin(1000, "uwhale")], |result| { diff --git a/contracts/liquidity_hub/bonding-manager/src/tests/rewards.rs b/contracts/liquidity_hub/bonding-manager/src/tests/rewards.rs index a5c3768d4..b05b9f915 100644 --- a/contracts/liquidity_hub/bonding-manager/src/tests/rewards.rs +++ b/contracts/liquidity_hub/bonding-manager/src/tests/rewards.rs @@ -39,7 +39,7 @@ fn test_fill_rewards_from_pool_manager() { creator.clone(), asset_infos.clone(), pool_fees.clone(), - white_whale_std::pool_network::asset::PairType::ConstantProduct, + white_whale_std::pool_manager::PoolType::ConstantProduct, Some("whale-uusdc".to_string()), vec![coin(1000, "uwhale")], |result| { @@ -123,7 +123,7 @@ fn test_fill_rewards_from_pool_manager() { creator.clone(), asset_infos.clone(), pool_fees.clone(), - white_whale_std::pool_network::asset::PairType::ConstantProduct, + white_whale_std::pool_manager::PoolType::ConstantProduct, Some("whale-uusdc-second".to_string()), vec![coin(1000, "uwhale")], |result| { @@ -145,7 +145,7 @@ fn test_fill_rewards_from_pool_manager() { creator.clone(), asset_infos, pool_fees, - white_whale_std::pool_network::asset::PairType::ConstantProduct, + white_whale_std::pool_manager::PoolType::ConstantProduct, Some("whale-uusdc-third".to_string()), vec![coin(1000, "uwhale")], |result| { diff --git a/contracts/liquidity_hub/bonding-manager/src/tests/robot.rs b/contracts/liquidity_hub/bonding-manager/src/tests/robot.rs index 987880007..3b79f5b63 100644 --- a/contracts/liquidity_hub/bonding-manager/src/tests/robot.rs +++ b/contracts/liquidity_hub/bonding-manager/src/tests/robot.rs @@ -20,6 +20,7 @@ use white_whale_std::bonding_manager::{ }; use white_whale_std::bonding_manager::{ClaimableEpochsResponse, Epoch}; use white_whale_std::epoch_manager::epoch_manager::{Epoch as EpochV2, EpochConfig}; +use white_whale_std::pool_manager::PoolType; use white_whale_std::pool_network::asset::PairType; pub fn bonding_manager_contract() -> Box> { @@ -619,12 +620,12 @@ impl TestingRobot { pub(crate) fn provide_liquidity( &mut self, sender: Addr, - pair_identifier: String, + pool_identifier: String, funds: Vec, result: impl Fn(Result), ) -> &mut Self { let msg = white_whale_std::pool_manager::ExecuteMsg::ProvideLiquidity { - pair_identifier, + pool_identifier, slippage_tolerance: None, receiver: None, lock_position_identifier: None, @@ -644,22 +645,21 @@ impl TestingRobot { pub(crate) fn swap( &mut self, sender: Addr, - offer_asset: Coin, + _offer_asset: Coin, ask_asset_denom: String, belief_price: Option, max_spread: Option, - to: Option, - pair_identifier: String, + receiver: Option, + pool_identifier: String, funds: Vec, result: impl Fn(Result), ) -> &mut Self { let msg = white_whale_std::pool_manager::ExecuteMsg::Swap { - offer_asset, ask_asset_denom, belief_price, max_spread, - to, - pair_identifier, + receiver, + pool_identifier, }; result( @@ -710,16 +710,16 @@ impl TestingRobot { sender: Addr, asset_denoms: Vec, pool_fees: PoolFee, - pair_type: PairType, - pair_identifier: Option, + pool_type: PoolType, + pool_identifier: Option, pair_creation_fee_funds: Vec, result: impl Fn(Result), ) -> &mut Self { - let msg = white_whale_std::pool_manager::ExecuteMsg::CreatePair { + let msg = white_whale_std::pool_manager::ExecuteMsg::CreatePool { asset_denoms, pool_fees, - pair_type, - pair_identifier, + pool_type, + pool_identifier, asset_decimals: vec![6, 6], }; diff --git a/contracts/liquidity_hub/pool-manager/schema/pool-manager.json b/contracts/liquidity_hub/pool-manager/schema/pool-manager.json index 382096178..b6932daf0 100644 --- a/contracts/liquidity_hub/pool-manager/schema/pool-manager.json +++ b/contracts/liquidity_hub/pool-manager/schema/pool-manager.json @@ -13,13 +13,20 @@ ], "properties": { "bonding_manager_addr": { + "description": "The address of the bonding manager contract.", "type": "string" }, "incentive_manager_addr": { + "description": "The address of the incentive manager contract.", "type": "string" }, "pool_creation_fee": { - "$ref": "#/definitions/Coin" + "description": "How much it costs to create a pool. It helps prevent spamming of new pools.", + "allOf": [ + { + "$ref": "#/definitions/Coin" + } + ] } }, "additionalProperties": false, @@ -50,21 +57,23 @@ "title": "ExecuteMsg", "oneOf": [ { + "description": "Creates a new pool.", "type": "object", "required": [ - "create_pair" + "create_pool" ], "properties": { - "create_pair": { + "create_pool": { "type": "object", "required": [ "asset_decimals", "asset_denoms", - "pair_type", - "pool_fees" + "pool_fees", + "pool_type" ], "properties": { "asset_decimals": { + "description": "The decimals for the given asset denoms, provided in the same order as `asset_denoms`.", "type": "array", "items": { "type": "integer", @@ -73,22 +82,34 @@ } }, "asset_denoms": { + "description": "The asset denoms for the pool.", "type": "array", "items": { "type": "string" } }, - "pair_identifier": { + "pool_fees": { + "description": "The fees for the pool.", + "allOf": [ + { + "$ref": "#/definitions/PoolFee" + } + ] + }, + "pool_identifier": { + "description": "The identifier for the pool.", "type": [ "string", "null" ] }, - "pair_type": { - "$ref": "#/definitions/PairType" - }, - "pool_fees": { - "$ref": "#/definitions/PoolFee" + "pool_type": { + "description": "The type of pool to create.", + "allOf": [ + { + "$ref": "#/definitions/PoolType" + } + ] } }, "additionalProperties": false @@ -106,7 +127,7 @@ "provide_liquidity": { "type": "object", "required": [ - "pair_identifier" + "pool_identifier" ], "properties": { "lock_position_identifier": { @@ -117,6 +138,7 @@ ] }, "max_spread": { + "description": "The maximum allowable spread between the bid and ask prices for the pool. When provided, if the spread exceeds this value, the liquidity provision will not be executed.", "anyOf": [ { "$ref": "#/definitions/Decimal" @@ -126,16 +148,19 @@ } ] }, - "pair_identifier": { + "pool_identifier": { + "description": "The identifier for the pool to provide liquidity for.", "type": "string" }, "receiver": { + "description": "The receiver of the LP", "type": [ "string", "null" ] }, "slippage_tolerance": { + "description": "A percentage value representing the acceptable slippage for the operation. When provided, if the slippage exceeds this value, the liquidity provision will not be executed.", "anyOf": [ { "$ref": "#/definitions/Decimal" @@ -171,14 +196,15 @@ "type": "object", "required": [ "ask_asset_denom", - "offer_asset", - "pair_identifier" + "pool_identifier" ], "properties": { "ask_asset_denom": { + "description": "The return asset of the swap.", "type": "string" }, "belief_price": { + "description": "The belief price of the swap.", "anyOf": [ { "$ref": "#/definitions/Decimal" @@ -189,6 +215,7 @@ ] }, "max_spread": { + "description": "The maximum spread to incur when performing the swap. If the spread exceeds this value, the swap will not be executed.", "anyOf": [ { "$ref": "#/definitions/Decimal" @@ -198,13 +225,12 @@ } ] }, - "offer_asset": { - "$ref": "#/definitions/Coin" - }, - "pair_identifier": { + "pool_identifier": { + "description": "The identifier for the pool to swap in.", "type": "string" }, - "to": { + "receiver": { + "description": "The recipient of the output tokens. If not provided, the tokens will be sent to the sender of the message.", "type": [ "string", "null" @@ -226,10 +252,10 @@ "withdraw_liquidity": { "type": "object", "required": [ - "pair_identifier" + "pool_identifier" ], "properties": { - "pair_identifier": { + "pool_identifier": { "type": "string" } }, @@ -280,7 +306,7 @@ "$ref": "#/definitions/SwapOperation" } }, - "to": { + "receiver": { "description": "The (optional) recipient of the output tokens.\n\nIf left unspecified, tokens will be sent to the sender of the message.", "type": [ "string", @@ -521,7 +547,7 @@ ] }, "FeatureToggle": { - "description": "Pool feature toggle", + "description": "Pool feature toggle, can control whether swaps, deposits, and withdrawals are enabled.", "type": "object", "required": [ "deposits_enabled", @@ -530,12 +556,15 @@ ], "properties": { "deposits_enabled": { + "description": "Whether or not deposits are enabled", "type": "boolean" }, "swaps_enabled": { + "description": "Whether or not swaps are enabled", "type": "boolean" }, "withdrawals_enabled": { + "description": "Whether or not swaps are enabled", "type": "boolean" } }, @@ -553,40 +582,6 @@ }, "additionalProperties": false }, - "PairType": { - "oneOf": [ - { - "type": "string", - "enum": [ - "constant_product" - ] - }, - { - "type": "object", - "required": [ - "stable_swap" - ], - "properties": { - "stable_swap": { - "type": "object", - "required": [ - "amp" - ], - "properties": { - "amp": { - "description": "The amount of amplification to perform on the constant product part of the swap formula.", - "type": "integer", - "format": "uint64", - "minimum": 0.0 - } - }, - "additionalProperties": false - } - }, - "additionalProperties": false - } - ] - }, "PoolFee": { "description": "Represents the fee structure for transactions within a pool.\n\n# 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.\n\n# Features - `osmosis`: Enables the `osmosis_fee` field, integrating specific fee requirements for the Osmosis protocol within the pool's fee structure.", "type": "object", @@ -631,9 +626,48 @@ }, "additionalProperties": false }, + "PoolType": { + "description": "Possible pool types, it can be either a constant product (xyk) pool or a stable swap pool.", + "oneOf": [ + { + "description": "A stable swap pool.", + "type": "object", + "required": [ + "stable_swap" + ], + "properties": { + "stable_swap": { + "type": "object", + "required": [ + "amp" + ], + "properties": { + "amp": { + "description": "The amount of amplification to perform on the constant product part of the swap formula.", + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "xyk pool", + "type": "string", + "enum": [ + "constant_product" + ] + } + ] + }, "SwapOperation": { + "description": "The type of swap operation to perform.", "oneOf": [ { + "description": "A swap operation that uses the WhaleSwap router.", "type": "object", "required": [ "whale_swap" @@ -648,12 +682,15 @@ ], "properties": { "pool_identifier": { + "description": "The identifier of the pool to use for the swap.", "type": "string" }, "token_in_denom": { + "description": "The token denom to swap in.", "type": "string" }, "token_out_denom": { + "description": "The token denom returning from the swap.", "type": "string" } }, @@ -665,6 +702,7 @@ ] }, "SwapRoute": { + "description": "The swap route structure", "type": "object", "required": [ "ask_asset_denom", @@ -673,12 +711,15 @@ ], "properties": { "ask_asset_denom": { + "description": "The ask asset denom, i.e. the asset that is being received.", "type": "string" }, "offer_asset_denom": { + "description": "The offer asset denom, i.e. the asset that is being swapped.", "type": "string" }, "swap_operations": { + "description": "The swap operations to perform to get from offer asset to ask asset.", "type": "array", "items": { "$ref": "#/definitions/SwapOperation" @@ -711,17 +752,10 @@ "oneOf": [ { "description": "Retrieves the contract's config.", - "type": "object", - "required": [ + "type": "string", + "enum": [ "config" - ], - "properties": { - "config": { - "type": "object", - "additionalProperties": false - } - }, - "additionalProperties": false + ] }, { "description": "Retrieves the decimals for the given asset.", @@ -734,13 +768,15 @@ "type": "object", "required": [ "denom", - "pair_identifier" + "pool_identifier" ], "properties": { "denom": { + "description": "The queried denom in the given pool_identifier.", "type": "string" }, - "pair_identifier": { + "pool_identifier": { + "description": "The pool identifier to do the query for.", "type": "string" } }, @@ -759,14 +795,25 @@ "simulation": { "type": "object", "required": [ + "ask_asset_denom", "offer_asset", - "pair_identifier" + "pool_identifier" ], "properties": { + "ask_asset_denom": { + "description": "The ask asset denom to get.", + "type": "string" + }, "offer_asset": { - "$ref": "#/definitions/Coin" + "description": "The offer asset to swap.", + "allOf": [ + { + "$ref": "#/definitions/Coin" + } + ] }, - "pair_identifier": { + "pool_identifier": { + "description": "The pool identifier to swap in.", "type": "string" } }, @@ -786,17 +833,24 @@ "type": "object", "required": [ "ask_asset", - "offer_asset", - "pair_identifier" + "offer_asset_denom", + "pool_identifier" ], "properties": { "ask_asset": { - "$ref": "#/definitions/Coin" + "description": "The ask asset to get after the swap.", + "allOf": [ + { + "$ref": "#/definitions/Coin" + } + ] }, - "offer_asset": { - "$ref": "#/definitions/Coin" + "offer_asset_denom": { + "description": "The offer asset denom to input.", + "type": "string" }, - "pair_identifier": { + "pool_identifier": { + "description": "The pool identifier to swap in.", "type": "string" } }, @@ -820,9 +874,11 @@ ], "properties": { "ask_asset_denom": { + "description": "The ask asset denom, i.e. the asset that is being received.", "type": "string" }, "offer_asset_denom": { + "description": "The offer asset denom, i.e. the asset that is being swapped.", "type": "string" } }, @@ -833,17 +889,10 @@ }, { "description": "Gets all swap routes registered", - "type": "object", - "required": [ + "type": "string", + "enum": [ "swap_routes" - ], - "properties": { - "swap_routes": { - "type": "object", - "additionalProperties": false - } - }, - "additionalProperties": false + ] }, { "description": "Simulates swap operations.", @@ -860,9 +909,15 @@ ], "properties": { "offer_amount": { - "$ref": "#/definitions/Uint128" + "description": "The amount to swap.", + "allOf": [ + { + "$ref": "#/definitions/Uint128" + } + ] }, "operations": { + "description": "The operations to perform.", "type": "array", "items": { "$ref": "#/definitions/SwapOperation" @@ -889,9 +944,15 @@ ], "properties": { "ask_amount": { - "$ref": "#/definitions/Uint128" + "description": "The amount to get after the swap.", + "allOf": [ + { + "$ref": "#/definitions/Uint128" + } + ] }, "operations": { + "description": "The operations to perform.", "type": "array", "items": { "$ref": "#/definitions/SwapOperation" @@ -904,18 +965,19 @@ "additionalProperties": false }, { + "description": "Retrieves the pool information for the given pool identifier.", "type": "object", "required": [ - "pair" + "pool" ], "properties": { - "pair": { + "pool": { "type": "object", "required": [ - "pair_identifier" + "pool_identifier" ], "properties": { - "pair_identifier": { + "pool_identifier": { "type": "string" } }, @@ -925,7 +987,7 @@ "additionalProperties": false }, { - "description": "Retrieves the creator of the swap routes that can then remove them.", + "description": "Retrieves the creator of the swap route to get from offer to ask asset. The creator of the swap route can remove it.", "type": "object", "required": [ "swap_route_creator" @@ -939,9 +1001,11 @@ ], "properties": { "ask_asset_denom": { + "description": "The ask asset denom, i.e. the asset that is being received.", "type": "string" }, "offer_asset_denom": { + "description": "The offer asset denom, i.e. the asset that is being swapped.", "type": "string" } }, @@ -982,8 +1046,10 @@ } }, "SwapOperation": { + "description": "The type of swap operation to perform.", "oneOf": [ { + "description": "A swap operation that uses the WhaleSwap router.", "type": "object", "required": [ "whale_swap" @@ -998,12 +1064,15 @@ ], "properties": { "pool_identifier": { + "description": "The identifier of the pool to use for the swap.", "type": "string" }, "token_in_denom": { + "description": "The token denom to swap in.", "type": "string" }, "token_out_denom": { + "description": "The token denom returning from the swap.", "type": "string" } }, @@ -1023,7 +1092,6 @@ "migrate": { "$schema": "http://json-schema.org/draft-07/schema#", "title": "MigrateMsg", - "description": "The migrate message", "type": "object", "additionalProperties": false }, @@ -1037,7 +1105,7 @@ "required": [ "decimals", "denom", - "pair_identifier" + "pool_identifier" ], "properties": { "decimals": { @@ -1047,11 +1115,11 @@ "minimum": 0.0 }, "denom": { - "description": "The queried denom in the given pair_identifier.", + "description": "The queried denom in the given pool_identifier.", "type": "string" }, - "pair_identifier": { - "description": "The pair identifier to do the query for.", + "pool_identifier": { + "description": "The pool identifier to do the query for.", "type": "string" } }, @@ -1060,13 +1128,19 @@ "config": { "$schema": "http://json-schema.org/draft-07/schema#", "title": "ConfigResponse", + "description": "The response for the `Config` query.", "type": "object", "required": [ "config" ], "properties": { "config": { - "$ref": "#/definitions/Config" + "description": "The contract configuration.", + "allOf": [ + { + "$ref": "#/definitions/Config" + } + ] } }, "additionalProperties": false, @@ -1091,6 +1165,7 @@ } }, "Config": { + "description": "The contract configuration.", "type": "object", "required": [ "bonding_manager_addr", @@ -1119,13 +1194,18 @@ ] }, "pool_creation_fee": { - "$ref": "#/definitions/Coin" + "description": "How much it costs to create a pool. It helps prevent spamming of new pools.", + "allOf": [ + { + "$ref": "#/definitions/Coin" + } + ] } }, "additionalProperties": false }, "FeatureToggle": { - "description": "Pool feature toggle", + "description": "Pool feature toggle, can control whether swaps, deposits, and withdrawals are enabled.", "type": "object", "required": [ "deposits_enabled", @@ -1134,12 +1214,15 @@ ], "properties": { "deposits_enabled": { + "description": "Whether or not deposits are enabled", "type": "boolean" }, "swaps_enabled": { + "description": "Whether or not swaps are enabled", "type": "boolean" }, "withdrawals_enabled": { + "description": "Whether or not swaps are enabled", "type": "boolean" } }, @@ -1246,20 +1329,31 @@ } } }, - "pair": { + "pool": { "$schema": "http://json-schema.org/draft-07/schema#", - "title": "PairInfoResponse", + "title": "PoolInfoResponse", + "description": "The response for the `Pool` query.", "type": "object", "required": [ - "pair_info", + "pool_info", "total_share" ], "properties": { - "pair_info": { - "$ref": "#/definitions/PairInfo" + "pool_info": { + "description": "The pool information for the given pool identifier.", + "allOf": [ + { + "$ref": "#/definitions/PoolInfo" + } + ] }, "total_share": { - "$ref": "#/definitions/Coin" + "description": "The total LP tokens in the pool.", + "allOf": [ + { + "$ref": "#/definitions/Coin" + } + ] } }, "additionalProperties": false, @@ -1295,18 +1389,64 @@ }, "additionalProperties": false }, - "PairInfo": { + "PoolFee": { + "description": "Represents the fee structure for transactions within a pool.\n\n# 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.\n\n# Features - `osmosis`: Enables the `osmosis_fee` field, integrating specific fee requirements for the Osmosis protocol within the pool's fee structure.", + "type": "object", + "required": [ + "burn_fee", + "extra_fees", + "protocol_fee", + "swap_fee" + ], + "properties": { + "burn_fee": { + "description": "Fee percentage that is burned on each transaction. Burning a portion of the transaction fee helps in reducing the overall token supply.", + "allOf": [ + { + "$ref": "#/definitions/Fee" + } + ] + }, + "extra_fees": { + "description": "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.", + "type": "array", + "items": { + "$ref": "#/definitions/Fee" + } + }, + "protocol_fee": { + "description": "Fee percentage charged on each transaction for the protocol's benefit.", + "allOf": [ + { + "$ref": "#/definitions/Fee" + } + ] + }, + "swap_fee": { + "description": "Fee percentage allocated to liquidity providers on each swap.", + "allOf": [ + { + "$ref": "#/definitions/Fee" + } + ] + } + }, + "additionalProperties": false + }, + "PoolInfo": { + "description": "Contains the pool information", "type": "object", "required": [ "asset_decimals", "asset_denoms", "assets", "lp_denom", - "pair_type", - "pool_fees" + "pool_fees", + "pool_type" ], "properties": { "asset_decimals": { + "description": "The decimals for the given asset denoms, provided in the same order as asset_denoms.", "type": "array", "items": { "type": "integer", @@ -1315,38 +1455,47 @@ } }, "asset_denoms": { + "description": "The asset denoms for the pool.", "type": "array", "items": { "type": "string" } }, "assets": { + "description": "The total amount of assets in the pool.", "type": "array", "items": { "$ref": "#/definitions/Coin" } }, "lp_denom": { + "description": "The LP denom of the pool.", "type": "string" }, - "pair_type": { - "$ref": "#/definitions/PairType" - }, "pool_fees": { - "$ref": "#/definitions/PoolFee" + "description": "The fees for the pool.", + "allOf": [ + { + "$ref": "#/definitions/PoolFee" + } + ] + }, + "pool_type": { + "description": "The type of pool to create.", + "allOf": [ + { + "$ref": "#/definitions/PoolType" + } + ] } }, "additionalProperties": false }, - "PairType": { + "PoolType": { + "description": "Possible pool types, it can be either a constant product (xyk) pool or a stable swap pool.", "oneOf": [ { - "type": "string", - "enum": [ - "constant_product" - ] - }, - { + "description": "A stable swap pool.", "type": "object", "required": [ "stable_swap" @@ -1369,52 +1518,15 @@ } }, "additionalProperties": false - } - ] - }, - "PoolFee": { - "description": "Represents the fee structure for transactions within a pool.\n\n# 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.\n\n# Features - `osmosis`: Enables the `osmosis_fee` field, integrating specific fee requirements for the Osmosis protocol within the pool's fee structure.", - "type": "object", - "required": [ - "burn_fee", - "extra_fees", - "protocol_fee", - "swap_fee" - ], - "properties": { - "burn_fee": { - "description": "Fee percentage that is burned on each transaction. Burning a portion of the transaction fee helps in reducing the overall token supply.", - "allOf": [ - { - "$ref": "#/definitions/Fee" - } - ] - }, - "extra_fees": { - "description": "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.", - "type": "array", - "items": { - "$ref": "#/definitions/Fee" - } - }, - "protocol_fee": { - "description": "Fee percentage charged on each transaction for the protocol's benefit.", - "allOf": [ - { - "$ref": "#/definitions/Fee" - } - ] }, - "swap_fee": { - "description": "Fee percentage allocated to liquidity providers on each swap.", - "allOf": [ - { - "$ref": "#/definitions/Fee" - } + { + "description": "xyk pool", + "type": "string", + "enum": [ + "constant_product" ] } - }, - "additionalProperties": false + ] }, "Uint128": { "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", @@ -1425,13 +1537,19 @@ "reverse_simulate_swap_operations": { "$schema": "http://json-schema.org/draft-07/schema#", "title": "ReverseSimulateSwapOperationsResponse", + "description": "The response for the `ReverseSimulateSwapOperations` query.", "type": "object", "required": [ "amount" ], "properties": { "amount": { - "$ref": "#/definitions/Uint128" + "description": "The amount of the initial token needed to get the final token after the swap operations.", + "allOf": [ + { + "$ref": "#/definitions/Uint128" + } + ] } }, "additionalProperties": false, @@ -1456,19 +1574,44 @@ ], "properties": { "burn_fee_amount": { - "$ref": "#/definitions/Uint128" + "description": "The burn fee amount of the swap.", + "allOf": [ + { + "$ref": "#/definitions/Uint128" + } + ] }, "offer_amount": { - "$ref": "#/definitions/Uint128" + "description": "The amount of the offer asset needed to get the ask amount.", + "allOf": [ + { + "$ref": "#/definitions/Uint128" + } + ] }, "protocol_fee_amount": { - "$ref": "#/definitions/Uint128" + "description": "The protocol fee amount of the swap.", + "allOf": [ + { + "$ref": "#/definitions/Uint128" + } + ] }, "spread_amount": { - "$ref": "#/definitions/Uint128" + "description": "The spread amount of the swap.", + "allOf": [ + { + "$ref": "#/definitions/Uint128" + } + ] }, "swap_fee_amount": { - "$ref": "#/definitions/Uint128" + "description": "The swap fee amount of the swap.", + "allOf": [ + { + "$ref": "#/definitions/Uint128" + } + ] } }, "additionalProperties": false, @@ -1482,13 +1625,19 @@ "simulate_swap_operations": { "$schema": "http://json-schema.org/draft-07/schema#", "title": "SimulateSwapOperationsResponse", + "description": "The response for the `SimulateSwapOperations` query.", "type": "object", "required": [ "amount" ], "properties": { "amount": { - "$ref": "#/definitions/Uint128" + "description": "The amount of the final token after the swap operations.", + "allOf": [ + { + "$ref": "#/definitions/Uint128" + } + ] } }, "additionalProperties": false, @@ -1506,6 +1655,7 @@ "type": "object", "required": [ "burn_fee_amount", + "extra_fees_amount", "protocol_fee_amount", "return_amount", "spread_amount", @@ -1513,19 +1663,52 @@ ], "properties": { "burn_fee_amount": { - "$ref": "#/definitions/Uint128" + "description": "The burn fee amount of the swap.", + "allOf": [ + { + "$ref": "#/definitions/Uint128" + } + ] + }, + "extra_fees_amount": { + "description": "The extra fees amount of the swap.", + "allOf": [ + { + "$ref": "#/definitions/Uint128" + } + ] }, "protocol_fee_amount": { - "$ref": "#/definitions/Uint128" + "description": "The protocol fee amount of the swap.", + "allOf": [ + { + "$ref": "#/definitions/Uint128" + } + ] }, "return_amount": { - "$ref": "#/definitions/Uint128" + "description": "The return amount of the ask asset given the offer amount.", + "allOf": [ + { + "$ref": "#/definitions/Uint128" + } + ] }, "spread_amount": { - "$ref": "#/definitions/Uint128" + "description": "The spread amount of the swap.", + "allOf": [ + { + "$ref": "#/definitions/Uint128" + } + ] }, "swap_fee_amount": { - "$ref": "#/definitions/Uint128" + "description": "The swap fee amount of the swap.", + "allOf": [ + { + "$ref": "#/definitions/Uint128" + } + ] } }, "additionalProperties": false, @@ -1539,20 +1722,28 @@ "swap_route": { "$schema": "http://json-schema.org/draft-07/schema#", "title": "SwapRouteResponse", + "description": "The response for the `SwapRoute` query.", "type": "object", "required": [ "swap_route" ], "properties": { "swap_route": { - "$ref": "#/definitions/SwapRoute" + "description": "The swap route taken for the queried swap.", + "allOf": [ + { + "$ref": "#/definitions/SwapRoute" + } + ] } }, "additionalProperties": false, "definitions": { "SwapOperation": { + "description": "The type of swap operation to perform.", "oneOf": [ { + "description": "A swap operation that uses the WhaleSwap router.", "type": "object", "required": [ "whale_swap" @@ -1567,12 +1758,15 @@ ], "properties": { "pool_identifier": { + "description": "The identifier of the pool to use for the swap.", "type": "string" }, "token_in_denom": { + "description": "The token denom to swap in.", "type": "string" }, "token_out_denom": { + "description": "The token denom returning from the swap.", "type": "string" } }, @@ -1584,6 +1778,7 @@ ] }, "SwapRoute": { + "description": "The swap route structure", "type": "object", "required": [ "ask_asset_denom", @@ -1592,12 +1787,15 @@ ], "properties": { "ask_asset_denom": { + "description": "The ask asset denom, i.e. the asset that is being received.", "type": "string" }, "offer_asset_denom": { + "description": "The offer asset denom, i.e. the asset that is being swapped.", "type": "string" }, "swap_operations": { + "description": "The swap operations to perform to get from offer asset to ask asset.", "type": "array", "items": { "$ref": "#/definitions/SwapOperation" @@ -1611,12 +1809,14 @@ "swap_route_creator": { "$schema": "http://json-schema.org/draft-07/schema#", "title": "SwapRouteCreatorResponse", + "description": "The response for the `SwapRouteCreator` query.", "type": "object", "required": [ "creator" ], "properties": { "creator": { + "description": "The creator of the swap route.", "type": "string" } }, @@ -1625,12 +1825,14 @@ "swap_routes": { "$schema": "http://json-schema.org/draft-07/schema#", "title": "SwapRoutesResponse", + "description": "The response for the `SwapRoutes` query.", "type": "object", "required": [ "swap_routes" ], "properties": { "swap_routes": { + "description": "The swap routes registered in the contract.", "type": "array", "items": { "$ref": "#/definitions/SwapRoute" @@ -1640,8 +1842,10 @@ "additionalProperties": false, "definitions": { "SwapOperation": { + "description": "The type of swap operation to perform.", "oneOf": [ { + "description": "A swap operation that uses the WhaleSwap router.", "type": "object", "required": [ "whale_swap" @@ -1656,12 +1860,15 @@ ], "properties": { "pool_identifier": { + "description": "The identifier of the pool to use for the swap.", "type": "string" }, "token_in_denom": { + "description": "The token denom to swap in.", "type": "string" }, "token_out_denom": { + "description": "The token denom returning from the swap.", "type": "string" } }, @@ -1673,6 +1880,7 @@ ] }, "SwapRoute": { + "description": "The swap route structure", "type": "object", "required": [ "ask_asset_denom", @@ -1681,12 +1889,15 @@ ], "properties": { "ask_asset_denom": { + "description": "The ask asset denom, i.e. the asset that is being received.", "type": "string" }, "offer_asset_denom": { + "description": "The offer asset denom, i.e. the asset that is being swapped.", "type": "string" }, "swap_operations": { + "description": "The swap operations to perform to get from offer asset to ask asset.", "type": "array", "items": { "$ref": "#/definitions/SwapOperation" diff --git a/contracts/liquidity_hub/pool-manager/schema/raw/execute.json b/contracts/liquidity_hub/pool-manager/schema/raw/execute.json index e595c48ee..e40859b6b 100644 --- a/contracts/liquidity_hub/pool-manager/schema/raw/execute.json +++ b/contracts/liquidity_hub/pool-manager/schema/raw/execute.json @@ -3,21 +3,23 @@ "title": "ExecuteMsg", "oneOf": [ { + "description": "Creates a new pool.", "type": "object", "required": [ - "create_pair" + "create_pool" ], "properties": { - "create_pair": { + "create_pool": { "type": "object", "required": [ "asset_decimals", "asset_denoms", - "pair_type", - "pool_fees" + "pool_fees", + "pool_type" ], "properties": { "asset_decimals": { + "description": "The decimals for the given asset denoms, provided in the same order as `asset_denoms`.", "type": "array", "items": { "type": "integer", @@ -26,22 +28,34 @@ } }, "asset_denoms": { + "description": "The asset denoms for the pool.", "type": "array", "items": { "type": "string" } }, - "pair_identifier": { + "pool_fees": { + "description": "The fees for the pool.", + "allOf": [ + { + "$ref": "#/definitions/PoolFee" + } + ] + }, + "pool_identifier": { + "description": "The identifier for the pool.", "type": [ "string", "null" ] }, - "pair_type": { - "$ref": "#/definitions/PairType" - }, - "pool_fees": { - "$ref": "#/definitions/PoolFee" + "pool_type": { + "description": "The type of pool to create.", + "allOf": [ + { + "$ref": "#/definitions/PoolType" + } + ] } }, "additionalProperties": false @@ -59,7 +73,7 @@ "provide_liquidity": { "type": "object", "required": [ - "pair_identifier" + "pool_identifier" ], "properties": { "lock_position_identifier": { @@ -70,6 +84,7 @@ ] }, "max_spread": { + "description": "The maximum allowable spread between the bid and ask prices for the pool. When provided, if the spread exceeds this value, the liquidity provision will not be executed.", "anyOf": [ { "$ref": "#/definitions/Decimal" @@ -79,16 +94,19 @@ } ] }, - "pair_identifier": { + "pool_identifier": { + "description": "The identifier for the pool to provide liquidity for.", "type": "string" }, "receiver": { + "description": "The receiver of the LP", "type": [ "string", "null" ] }, "slippage_tolerance": { + "description": "A percentage value representing the acceptable slippage for the operation. When provided, if the slippage exceeds this value, the liquidity provision will not be executed.", "anyOf": [ { "$ref": "#/definitions/Decimal" @@ -124,14 +142,15 @@ "type": "object", "required": [ "ask_asset_denom", - "offer_asset", - "pair_identifier" + "pool_identifier" ], "properties": { "ask_asset_denom": { + "description": "The return asset of the swap.", "type": "string" }, "belief_price": { + "description": "The belief price of the swap.", "anyOf": [ { "$ref": "#/definitions/Decimal" @@ -142,6 +161,7 @@ ] }, "max_spread": { + "description": "The maximum spread to incur when performing the swap. If the spread exceeds this value, the swap will not be executed.", "anyOf": [ { "$ref": "#/definitions/Decimal" @@ -151,13 +171,12 @@ } ] }, - "offer_asset": { - "$ref": "#/definitions/Coin" - }, - "pair_identifier": { + "pool_identifier": { + "description": "The identifier for the pool to swap in.", "type": "string" }, - "to": { + "receiver": { + "description": "The recipient of the output tokens. If not provided, the tokens will be sent to the sender of the message.", "type": [ "string", "null" @@ -179,10 +198,10 @@ "withdraw_liquidity": { "type": "object", "required": [ - "pair_identifier" + "pool_identifier" ], "properties": { - "pair_identifier": { + "pool_identifier": { "type": "string" } }, @@ -233,7 +252,7 @@ "$ref": "#/definitions/SwapOperation" } }, - "to": { + "receiver": { "description": "The (optional) recipient of the output tokens.\n\nIf left unspecified, tokens will be sent to the sender of the message.", "type": [ "string", @@ -474,7 +493,7 @@ ] }, "FeatureToggle": { - "description": "Pool feature toggle", + "description": "Pool feature toggle, can control whether swaps, deposits, and withdrawals are enabled.", "type": "object", "required": [ "deposits_enabled", @@ -483,12 +502,15 @@ ], "properties": { "deposits_enabled": { + "description": "Whether or not deposits are enabled", "type": "boolean" }, "swaps_enabled": { + "description": "Whether or not swaps are enabled", "type": "boolean" }, "withdrawals_enabled": { + "description": "Whether or not swaps are enabled", "type": "boolean" } }, @@ -506,40 +528,6 @@ }, "additionalProperties": false }, - "PairType": { - "oneOf": [ - { - "type": "string", - "enum": [ - "constant_product" - ] - }, - { - "type": "object", - "required": [ - "stable_swap" - ], - "properties": { - "stable_swap": { - "type": "object", - "required": [ - "amp" - ], - "properties": { - "amp": { - "description": "The amount of amplification to perform on the constant product part of the swap formula.", - "type": "integer", - "format": "uint64", - "minimum": 0.0 - } - }, - "additionalProperties": false - } - }, - "additionalProperties": false - } - ] - }, "PoolFee": { "description": "Represents the fee structure for transactions within a pool.\n\n# 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.\n\n# Features - `osmosis`: Enables the `osmosis_fee` field, integrating specific fee requirements for the Osmosis protocol within the pool's fee structure.", "type": "object", @@ -584,9 +572,48 @@ }, "additionalProperties": false }, + "PoolType": { + "description": "Possible pool types, it can be either a constant product (xyk) pool or a stable swap pool.", + "oneOf": [ + { + "description": "A stable swap pool.", + "type": "object", + "required": [ + "stable_swap" + ], + "properties": { + "stable_swap": { + "type": "object", + "required": [ + "amp" + ], + "properties": { + "amp": { + "description": "The amount of amplification to perform on the constant product part of the swap formula.", + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "xyk pool", + "type": "string", + "enum": [ + "constant_product" + ] + } + ] + }, "SwapOperation": { + "description": "The type of swap operation to perform.", "oneOf": [ { + "description": "A swap operation that uses the WhaleSwap router.", "type": "object", "required": [ "whale_swap" @@ -601,12 +628,15 @@ ], "properties": { "pool_identifier": { + "description": "The identifier of the pool to use for the swap.", "type": "string" }, "token_in_denom": { + "description": "The token denom to swap in.", "type": "string" }, "token_out_denom": { + "description": "The token denom returning from the swap.", "type": "string" } }, @@ -618,6 +648,7 @@ ] }, "SwapRoute": { + "description": "The swap route structure", "type": "object", "required": [ "ask_asset_denom", @@ -626,12 +657,15 @@ ], "properties": { "ask_asset_denom": { + "description": "The ask asset denom, i.e. the asset that is being received.", "type": "string" }, "offer_asset_denom": { + "description": "The offer asset denom, i.e. the asset that is being swapped.", "type": "string" }, "swap_operations": { + "description": "The swap operations to perform to get from offer asset to ask asset.", "type": "array", "items": { "$ref": "#/definitions/SwapOperation" diff --git a/contracts/liquidity_hub/pool-manager/schema/raw/instantiate.json b/contracts/liquidity_hub/pool-manager/schema/raw/instantiate.json index 8fdc47956..f73769af3 100644 --- a/contracts/liquidity_hub/pool-manager/schema/raw/instantiate.json +++ b/contracts/liquidity_hub/pool-manager/schema/raw/instantiate.json @@ -9,13 +9,20 @@ ], "properties": { "bonding_manager_addr": { + "description": "The address of the bonding manager contract.", "type": "string" }, "incentive_manager_addr": { + "description": "The address of the incentive manager contract.", "type": "string" }, "pool_creation_fee": { - "$ref": "#/definitions/Coin" + "description": "How much it costs to create a pool. It helps prevent spamming of new pools.", + "allOf": [ + { + "$ref": "#/definitions/Coin" + } + ] } }, "additionalProperties": false, diff --git a/contracts/liquidity_hub/pool-manager/schema/raw/migrate.json b/contracts/liquidity_hub/pool-manager/schema/raw/migrate.json index be217b7d3..7fbe8c570 100644 --- a/contracts/liquidity_hub/pool-manager/schema/raw/migrate.json +++ b/contracts/liquidity_hub/pool-manager/schema/raw/migrate.json @@ -1,7 +1,6 @@ { "$schema": "http://json-schema.org/draft-07/schema#", "title": "MigrateMsg", - "description": "The migrate message", "type": "object", "additionalProperties": false } diff --git a/contracts/liquidity_hub/pool-manager/schema/raw/query.json b/contracts/liquidity_hub/pool-manager/schema/raw/query.json index a74ad26dc..ec6ac2fe0 100644 --- a/contracts/liquidity_hub/pool-manager/schema/raw/query.json +++ b/contracts/liquidity_hub/pool-manager/schema/raw/query.json @@ -4,17 +4,10 @@ "oneOf": [ { "description": "Retrieves the contract's config.", - "type": "object", - "required": [ + "type": "string", + "enum": [ "config" - ], - "properties": { - "config": { - "type": "object", - "additionalProperties": false - } - }, - "additionalProperties": false + ] }, { "description": "Retrieves the decimals for the given asset.", @@ -27,13 +20,15 @@ "type": "object", "required": [ "denom", - "pair_identifier" + "pool_identifier" ], "properties": { "denom": { + "description": "The queried denom in the given pool_identifier.", "type": "string" }, - "pair_identifier": { + "pool_identifier": { + "description": "The pool identifier to do the query for.", "type": "string" } }, @@ -52,14 +47,25 @@ "simulation": { "type": "object", "required": [ + "ask_asset_denom", "offer_asset", - "pair_identifier" + "pool_identifier" ], "properties": { + "ask_asset_denom": { + "description": "The ask asset denom to get.", + "type": "string" + }, "offer_asset": { - "$ref": "#/definitions/Coin" + "description": "The offer asset to swap.", + "allOf": [ + { + "$ref": "#/definitions/Coin" + } + ] }, - "pair_identifier": { + "pool_identifier": { + "description": "The pool identifier to swap in.", "type": "string" } }, @@ -79,17 +85,24 @@ "type": "object", "required": [ "ask_asset", - "offer_asset", - "pair_identifier" + "offer_asset_denom", + "pool_identifier" ], "properties": { "ask_asset": { - "$ref": "#/definitions/Coin" + "description": "The ask asset to get after the swap.", + "allOf": [ + { + "$ref": "#/definitions/Coin" + } + ] }, - "offer_asset": { - "$ref": "#/definitions/Coin" + "offer_asset_denom": { + "description": "The offer asset denom to input.", + "type": "string" }, - "pair_identifier": { + "pool_identifier": { + "description": "The pool identifier to swap in.", "type": "string" } }, @@ -113,9 +126,11 @@ ], "properties": { "ask_asset_denom": { + "description": "The ask asset denom, i.e. the asset that is being received.", "type": "string" }, "offer_asset_denom": { + "description": "The offer asset denom, i.e. the asset that is being swapped.", "type": "string" } }, @@ -126,17 +141,10 @@ }, { "description": "Gets all swap routes registered", - "type": "object", - "required": [ + "type": "string", + "enum": [ "swap_routes" - ], - "properties": { - "swap_routes": { - "type": "object", - "additionalProperties": false - } - }, - "additionalProperties": false + ] }, { "description": "Simulates swap operations.", @@ -153,9 +161,15 @@ ], "properties": { "offer_amount": { - "$ref": "#/definitions/Uint128" + "description": "The amount to swap.", + "allOf": [ + { + "$ref": "#/definitions/Uint128" + } + ] }, "operations": { + "description": "The operations to perform.", "type": "array", "items": { "$ref": "#/definitions/SwapOperation" @@ -182,9 +196,15 @@ ], "properties": { "ask_amount": { - "$ref": "#/definitions/Uint128" + "description": "The amount to get after the swap.", + "allOf": [ + { + "$ref": "#/definitions/Uint128" + } + ] }, "operations": { + "description": "The operations to perform.", "type": "array", "items": { "$ref": "#/definitions/SwapOperation" @@ -197,18 +217,19 @@ "additionalProperties": false }, { + "description": "Retrieves the pool information for the given pool identifier.", "type": "object", "required": [ - "pair" + "pool" ], "properties": { - "pair": { + "pool": { "type": "object", "required": [ - "pair_identifier" + "pool_identifier" ], "properties": { - "pair_identifier": { + "pool_identifier": { "type": "string" } }, @@ -218,7 +239,7 @@ "additionalProperties": false }, { - "description": "Retrieves the creator of the swap routes that can then remove them.", + "description": "Retrieves the creator of the swap route to get from offer to ask asset. The creator of the swap route can remove it.", "type": "object", "required": [ "swap_route_creator" @@ -232,9 +253,11 @@ ], "properties": { "ask_asset_denom": { + "description": "The ask asset denom, i.e. the asset that is being received.", "type": "string" }, "offer_asset_denom": { + "description": "The offer asset denom, i.e. the asset that is being swapped.", "type": "string" } }, @@ -275,8 +298,10 @@ } }, "SwapOperation": { + "description": "The type of swap operation to perform.", "oneOf": [ { + "description": "A swap operation that uses the WhaleSwap router.", "type": "object", "required": [ "whale_swap" @@ -291,12 +316,15 @@ ], "properties": { "pool_identifier": { + "description": "The identifier of the pool to use for the swap.", "type": "string" }, "token_in_denom": { + "description": "The token denom to swap in.", "type": "string" }, "token_out_denom": { + "description": "The token denom returning from the swap.", "type": "string" } }, diff --git a/contracts/liquidity_hub/pool-manager/schema/raw/response_to_asset_decimals.json b/contracts/liquidity_hub/pool-manager/schema/raw/response_to_asset_decimals.json index a64999bd1..4cb4ceab1 100644 --- a/contracts/liquidity_hub/pool-manager/schema/raw/response_to_asset_decimals.json +++ b/contracts/liquidity_hub/pool-manager/schema/raw/response_to_asset_decimals.json @@ -6,7 +6,7 @@ "required": [ "decimals", "denom", - "pair_identifier" + "pool_identifier" ], "properties": { "decimals": { @@ -16,11 +16,11 @@ "minimum": 0.0 }, "denom": { - "description": "The queried denom in the given pair_identifier.", + "description": "The queried denom in the given pool_identifier.", "type": "string" }, - "pair_identifier": { - "description": "The pair identifier to do the query for.", + "pool_identifier": { + "description": "The pool identifier to do the query for.", "type": "string" } }, diff --git a/contracts/liquidity_hub/pool-manager/schema/raw/response_to_config.json b/contracts/liquidity_hub/pool-manager/schema/raw/response_to_config.json index b1b907f1e..b9a36357c 100644 --- a/contracts/liquidity_hub/pool-manager/schema/raw/response_to_config.json +++ b/contracts/liquidity_hub/pool-manager/schema/raw/response_to_config.json @@ -1,13 +1,19 @@ { "$schema": "http://json-schema.org/draft-07/schema#", "title": "ConfigResponse", + "description": "The response for the `Config` query.", "type": "object", "required": [ "config" ], "properties": { "config": { - "$ref": "#/definitions/Config" + "description": "The contract configuration.", + "allOf": [ + { + "$ref": "#/definitions/Config" + } + ] } }, "additionalProperties": false, @@ -32,6 +38,7 @@ } }, "Config": { + "description": "The contract configuration.", "type": "object", "required": [ "bonding_manager_addr", @@ -60,13 +67,18 @@ ] }, "pool_creation_fee": { - "$ref": "#/definitions/Coin" + "description": "How much it costs to create a pool. It helps prevent spamming of new pools.", + "allOf": [ + { + "$ref": "#/definitions/Coin" + } + ] } }, "additionalProperties": false }, "FeatureToggle": { - "description": "Pool feature toggle", + "description": "Pool feature toggle, can control whether swaps, deposits, and withdrawals are enabled.", "type": "object", "required": [ "deposits_enabled", @@ -75,12 +87,15 @@ ], "properties": { "deposits_enabled": { + "description": "Whether or not deposits are enabled", "type": "boolean" }, "swaps_enabled": { + "description": "Whether or not swaps are enabled", "type": "boolean" }, "withdrawals_enabled": { + "description": "Whether or not swaps are enabled", "type": "boolean" } }, diff --git a/contracts/liquidity_hub/pool-manager/schema/raw/response_to_pool.json b/contracts/liquidity_hub/pool-manager/schema/raw/response_to_pool.json new file mode 100644 index 000000000..ac073d626 --- /dev/null +++ b/contracts/liquidity_hub/pool-manager/schema/raw/response_to_pool.json @@ -0,0 +1,205 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "PoolInfoResponse", + "description": "The response for the `Pool` query.", + "type": "object", + "required": [ + "pool_info", + "total_share" + ], + "properties": { + "pool_info": { + "description": "The pool information for the given pool identifier.", + "allOf": [ + { + "$ref": "#/definitions/PoolInfo" + } + ] + }, + "total_share": { + "description": "The total LP tokens in the pool.", + "allOf": [ + { + "$ref": "#/definitions/Coin" + } + ] + } + }, + "additionalProperties": false, + "definitions": { + "Coin": { + "type": "object", + "required": [ + "amount", + "denom" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Uint128" + }, + "denom": { + "type": "string" + } + } + }, + "Decimal": { + "description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)", + "type": "string" + }, + "Fee": { + "type": "object", + "required": [ + "share" + ], + "properties": { + "share": { + "$ref": "#/definitions/Decimal" + } + }, + "additionalProperties": false + }, + "PoolFee": { + "description": "Represents the fee structure for transactions within a pool.\n\n# 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.\n\n# Features - `osmosis`: Enables the `osmosis_fee` field, integrating specific fee requirements for the Osmosis protocol within the pool's fee structure.", + "type": "object", + "required": [ + "burn_fee", + "extra_fees", + "protocol_fee", + "swap_fee" + ], + "properties": { + "burn_fee": { + "description": "Fee percentage that is burned on each transaction. Burning a portion of the transaction fee helps in reducing the overall token supply.", + "allOf": [ + { + "$ref": "#/definitions/Fee" + } + ] + }, + "extra_fees": { + "description": "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.", + "type": "array", + "items": { + "$ref": "#/definitions/Fee" + } + }, + "protocol_fee": { + "description": "Fee percentage charged on each transaction for the protocol's benefit.", + "allOf": [ + { + "$ref": "#/definitions/Fee" + } + ] + }, + "swap_fee": { + "description": "Fee percentage allocated to liquidity providers on each swap.", + "allOf": [ + { + "$ref": "#/definitions/Fee" + } + ] + } + }, + "additionalProperties": false + }, + "PoolInfo": { + "description": "Contains the pool information", + "type": "object", + "required": [ + "asset_decimals", + "asset_denoms", + "assets", + "lp_denom", + "pool_fees", + "pool_type" + ], + "properties": { + "asset_decimals": { + "description": "The decimals for the given asset denoms, provided in the same order as asset_denoms.", + "type": "array", + "items": { + "type": "integer", + "format": "uint8", + "minimum": 0.0 + } + }, + "asset_denoms": { + "description": "The asset denoms for the pool.", + "type": "array", + "items": { + "type": "string" + } + }, + "assets": { + "description": "The total amount of assets in the pool.", + "type": "array", + "items": { + "$ref": "#/definitions/Coin" + } + }, + "lp_denom": { + "description": "The LP denom of the pool.", + "type": "string" + }, + "pool_fees": { + "description": "The fees for the pool.", + "allOf": [ + { + "$ref": "#/definitions/PoolFee" + } + ] + }, + "pool_type": { + "description": "The type of pool to create.", + "allOf": [ + { + "$ref": "#/definitions/PoolType" + } + ] + } + }, + "additionalProperties": false + }, + "PoolType": { + "description": "Possible pool types, it can be either a constant product (xyk) pool or a stable swap pool.", + "oneOf": [ + { + "description": "A stable swap pool.", + "type": "object", + "required": [ + "stable_swap" + ], + "properties": { + "stable_swap": { + "type": "object", + "required": [ + "amp" + ], + "properties": { + "amp": { + "description": "The amount of amplification to perform on the constant product part of the swap formula.", + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "xyk pool", + "type": "string", + "enum": [ + "constant_product" + ] + } + ] + }, + "Uint128": { + "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", + "type": "string" + } + } +} diff --git a/contracts/liquidity_hub/pool-manager/schema/raw/response_to_reverse_simulate_swap_operations.json b/contracts/liquidity_hub/pool-manager/schema/raw/response_to_reverse_simulate_swap_operations.json index 6e6216b7e..9c364c10a 100644 --- a/contracts/liquidity_hub/pool-manager/schema/raw/response_to_reverse_simulate_swap_operations.json +++ b/contracts/liquidity_hub/pool-manager/schema/raw/response_to_reverse_simulate_swap_operations.json @@ -1,13 +1,19 @@ { "$schema": "http://json-schema.org/draft-07/schema#", "title": "ReverseSimulateSwapOperationsResponse", + "description": "The response for the `ReverseSimulateSwapOperations` query.", "type": "object", "required": [ "amount" ], "properties": { "amount": { - "$ref": "#/definitions/Uint128" + "description": "The amount of the initial token needed to get the final token after the swap operations.", + "allOf": [ + { + "$ref": "#/definitions/Uint128" + } + ] } }, "additionalProperties": false, diff --git a/contracts/liquidity_hub/pool-manager/schema/raw/response_to_reverse_simulation.json b/contracts/liquidity_hub/pool-manager/schema/raw/response_to_reverse_simulation.json index 93cb52989..f901b023c 100644 --- a/contracts/liquidity_hub/pool-manager/schema/raw/response_to_reverse_simulation.json +++ b/contracts/liquidity_hub/pool-manager/schema/raw/response_to_reverse_simulation.json @@ -12,19 +12,44 @@ ], "properties": { "burn_fee_amount": { - "$ref": "#/definitions/Uint128" + "description": "The burn fee amount of the swap.", + "allOf": [ + { + "$ref": "#/definitions/Uint128" + } + ] }, "offer_amount": { - "$ref": "#/definitions/Uint128" + "description": "The amount of the offer asset needed to get the ask amount.", + "allOf": [ + { + "$ref": "#/definitions/Uint128" + } + ] }, "protocol_fee_amount": { - "$ref": "#/definitions/Uint128" + "description": "The protocol fee amount of the swap.", + "allOf": [ + { + "$ref": "#/definitions/Uint128" + } + ] }, "spread_amount": { - "$ref": "#/definitions/Uint128" + "description": "The spread amount of the swap.", + "allOf": [ + { + "$ref": "#/definitions/Uint128" + } + ] }, "swap_fee_amount": { - "$ref": "#/definitions/Uint128" + "description": "The swap fee amount of the swap.", + "allOf": [ + { + "$ref": "#/definitions/Uint128" + } + ] } }, "additionalProperties": false, diff --git a/contracts/liquidity_hub/pool-manager/schema/raw/response_to_simulate_swap_operations.json b/contracts/liquidity_hub/pool-manager/schema/raw/response_to_simulate_swap_operations.json index b15e18ee0..80f0fc325 100644 --- a/contracts/liquidity_hub/pool-manager/schema/raw/response_to_simulate_swap_operations.json +++ b/contracts/liquidity_hub/pool-manager/schema/raw/response_to_simulate_swap_operations.json @@ -1,13 +1,19 @@ { "$schema": "http://json-schema.org/draft-07/schema#", "title": "SimulateSwapOperationsResponse", + "description": "The response for the `SimulateSwapOperations` query.", "type": "object", "required": [ "amount" ], "properties": { "amount": { - "$ref": "#/definitions/Uint128" + "description": "The amount of the final token after the swap operations.", + "allOf": [ + { + "$ref": "#/definitions/Uint128" + } + ] } }, "additionalProperties": false, diff --git a/contracts/liquidity_hub/pool-manager/schema/raw/response_to_simulation.json b/contracts/liquidity_hub/pool-manager/schema/raw/response_to_simulation.json index d364c2b5a..d4d0906a5 100644 --- a/contracts/liquidity_hub/pool-manager/schema/raw/response_to_simulation.json +++ b/contracts/liquidity_hub/pool-manager/schema/raw/response_to_simulation.json @@ -5,6 +5,7 @@ "type": "object", "required": [ "burn_fee_amount", + "extra_fees_amount", "protocol_fee_amount", "return_amount", "spread_amount", @@ -12,19 +13,52 @@ ], "properties": { "burn_fee_amount": { - "$ref": "#/definitions/Uint128" + "description": "The burn fee amount of the swap.", + "allOf": [ + { + "$ref": "#/definitions/Uint128" + } + ] + }, + "extra_fees_amount": { + "description": "The extra fees amount of the swap.", + "allOf": [ + { + "$ref": "#/definitions/Uint128" + } + ] }, "protocol_fee_amount": { - "$ref": "#/definitions/Uint128" + "description": "The protocol fee amount of the swap.", + "allOf": [ + { + "$ref": "#/definitions/Uint128" + } + ] }, "return_amount": { - "$ref": "#/definitions/Uint128" + "description": "The return amount of the ask asset given the offer amount.", + "allOf": [ + { + "$ref": "#/definitions/Uint128" + } + ] }, "spread_amount": { - "$ref": "#/definitions/Uint128" + "description": "The spread amount of the swap.", + "allOf": [ + { + "$ref": "#/definitions/Uint128" + } + ] }, "swap_fee_amount": { - "$ref": "#/definitions/Uint128" + "description": "The swap fee amount of the swap.", + "allOf": [ + { + "$ref": "#/definitions/Uint128" + } + ] } }, "additionalProperties": false, diff --git a/contracts/liquidity_hub/pool-manager/schema/raw/response_to_swap_route.json b/contracts/liquidity_hub/pool-manager/schema/raw/response_to_swap_route.json index 82801ed31..c988b63ec 100644 --- a/contracts/liquidity_hub/pool-manager/schema/raw/response_to_swap_route.json +++ b/contracts/liquidity_hub/pool-manager/schema/raw/response_to_swap_route.json @@ -1,20 +1,28 @@ { "$schema": "http://json-schema.org/draft-07/schema#", "title": "SwapRouteResponse", + "description": "The response for the `SwapRoute` query.", "type": "object", "required": [ "swap_route" ], "properties": { "swap_route": { - "$ref": "#/definitions/SwapRoute" + "description": "The swap route taken for the queried swap.", + "allOf": [ + { + "$ref": "#/definitions/SwapRoute" + } + ] } }, "additionalProperties": false, "definitions": { "SwapOperation": { + "description": "The type of swap operation to perform.", "oneOf": [ { + "description": "A swap operation that uses the WhaleSwap router.", "type": "object", "required": [ "whale_swap" @@ -29,12 +37,15 @@ ], "properties": { "pool_identifier": { + "description": "The identifier of the pool to use for the swap.", "type": "string" }, "token_in_denom": { + "description": "The token denom to swap in.", "type": "string" }, "token_out_denom": { + "description": "The token denom returning from the swap.", "type": "string" } }, @@ -46,6 +57,7 @@ ] }, "SwapRoute": { + "description": "The swap route structure", "type": "object", "required": [ "ask_asset_denom", @@ -54,12 +66,15 @@ ], "properties": { "ask_asset_denom": { + "description": "The ask asset denom, i.e. the asset that is being received.", "type": "string" }, "offer_asset_denom": { + "description": "The offer asset denom, i.e. the asset that is being swapped.", "type": "string" }, "swap_operations": { + "description": "The swap operations to perform to get from offer asset to ask asset.", "type": "array", "items": { "$ref": "#/definitions/SwapOperation" diff --git a/contracts/liquidity_hub/pool-manager/schema/raw/response_to_swap_route_creator.json b/contracts/liquidity_hub/pool-manager/schema/raw/response_to_swap_route_creator.json index 825c4ef49..0c80f1348 100644 --- a/contracts/liquidity_hub/pool-manager/schema/raw/response_to_swap_route_creator.json +++ b/contracts/liquidity_hub/pool-manager/schema/raw/response_to_swap_route_creator.json @@ -1,12 +1,14 @@ { "$schema": "http://json-schema.org/draft-07/schema#", "title": "SwapRouteCreatorResponse", + "description": "The response for the `SwapRouteCreator` query.", "type": "object", "required": [ "creator" ], "properties": { "creator": { + "description": "The creator of the swap route.", "type": "string" } }, diff --git a/contracts/liquidity_hub/pool-manager/schema/raw/response_to_swap_routes.json b/contracts/liquidity_hub/pool-manager/schema/raw/response_to_swap_routes.json index c1f6e47c1..4096e3684 100644 --- a/contracts/liquidity_hub/pool-manager/schema/raw/response_to_swap_routes.json +++ b/contracts/liquidity_hub/pool-manager/schema/raw/response_to_swap_routes.json @@ -1,12 +1,14 @@ { "$schema": "http://json-schema.org/draft-07/schema#", "title": "SwapRoutesResponse", + "description": "The response for the `SwapRoutes` query.", "type": "object", "required": [ "swap_routes" ], "properties": { "swap_routes": { + "description": "The swap routes registered in the contract.", "type": "array", "items": { "$ref": "#/definitions/SwapRoute" @@ -16,8 +18,10 @@ "additionalProperties": false, "definitions": { "SwapOperation": { + "description": "The type of swap operation to perform.", "oneOf": [ { + "description": "A swap operation that uses the WhaleSwap router.", "type": "object", "required": [ "whale_swap" @@ -32,12 +36,15 @@ ], "properties": { "pool_identifier": { + "description": "The identifier of the pool to use for the swap.", "type": "string" }, "token_in_denom": { + "description": "The token denom to swap in.", "type": "string" }, "token_out_denom": { + "description": "The token denom returning from the swap.", "type": "string" } }, @@ -49,6 +56,7 @@ ] }, "SwapRoute": { + "description": "The swap route structure", "type": "object", "required": [ "ask_asset_denom", @@ -57,12 +65,15 @@ ], "properties": { "ask_asset_denom": { + "description": "The ask asset denom, i.e. the asset that is being received.", "type": "string" }, "offer_asset_denom": { + "description": "The offer asset denom, i.e. the asset that is being swapped.", "type": "string" }, "swap_operations": { + "description": "The swap operations to perform to get from offer asset to ask asset.", "type": "array", "items": { "$ref": "#/definitions/SwapOperation" diff --git a/contracts/liquidity_hub/pool-manager/src/contract.rs b/contracts/liquidity_hub/pool-manager/src/contract.rs index e27b12ec4..0e8e4d03a 100644 --- a/contracts/liquidity_hub/pool-manager/src/contract.rs +++ b/contracts/liquidity_hub/pool-manager/src/contract.rs @@ -1,17 +1,13 @@ use crate::error::ContractError; -use crate::helpers::{ - reverse_simulate_swap_operations, simulate_swap_operations, validate_asset_balance, -}; -use crate::queries::{get_pair, get_swap_route, get_swap_route_creator, get_swap_routes}; -use crate::router::commands::{add_swap_routes, remove_swap_routes}; +use crate::helpers::validate_asset_balance; use crate::state::{ - Config, SingleSideLiquidityProvisionBuffer, MANAGER_CONFIG, PAIR_COUNTER, - TMP_SINGLE_SIDE_LIQUIDITY_PROVISION, + Config, SingleSideLiquidityProvisionBuffer, CONFIG, POOL_COUNTER, + SINGLE_SIDE_LIQUIDITY_PROVISION_BUFFER, }; use crate::{liquidity, manager, queries, router, swap}; #[cfg(not(feature = "library"))] use cosmwasm_std::{ - entry_point, to_json_binary, Addr, Api, Binary, Deps, DepsMut, Env, MessageInfo, Response, + entry_point, to_json_binary, Binary, Deps, DepsMut, Env, MessageInfo, Response, }; use cosmwasm_std::{wasm_execute, Reply, StdError}; use cw2::set_contract_version; @@ -44,9 +40,9 @@ pub fn instantiate( swaps_enabled: true, }, }; - MANAGER_CONFIG.save(deps.storage, &config)?; + CONFIG.save(deps.storage, &config)?; // initialize vault counter - PAIR_COUNTER.save(deps.storage, &0u64)?; + POOL_COUNTER.save(deps.storage, &0u64)?; cw_ownable::initialize_owner(deps.storage, deps.api, Some(info.sender.as_str()))?; Ok(Response::default()) @@ -63,12 +59,12 @@ pub fn reply(deps: DepsMut, env: Env, msg: Reply) -> Result Result Result { match msg { - ExecuteMsg::CreatePair { + ExecuteMsg::CreatePool { asset_denoms, asset_decimals, pool_fees, - pair_type, - pair_identifier, - } => manager::commands::create_pair( + pool_type, + pool_identifier, + } => manager::commands::create_pool( deps, env, info, asset_denoms, asset_decimals, pool_fees, - pair_type, - pair_identifier, + pool_type, + pool_identifier, ), ExecuteMsg::ProvideLiquidity { max_spread, slippage_tolerance, receiver, - pair_identifier, + pool_identifier, unlocking_duration, lock_position_identifier, } => liquidity::commands::provide_liquidity( @@ -125,35 +121,28 @@ pub fn execute( slippage_tolerance, max_spread, receiver, - pair_identifier, + pool_identifier, unlocking_duration, lock_position_identifier, ), ExecuteMsg::Swap { - offer_asset, ask_asset_denom, belief_price, max_spread, - to, - pair_identifier, - } => { - let to_addr = to.map(|addr| deps.api.addr_validate(&addr)).transpose()?; - - swap::commands::swap( - deps, - env, - info.clone(), - info.sender, - offer_asset, - ask_asset_denom, - belief_price, - max_spread, - to_addr, - pair_identifier, - ) - } - ExecuteMsg::WithdrawLiquidity { pair_identifier } => { - liquidity::commands::withdraw_liquidity(deps, env, info, pair_identifier) + receiver, + pool_identifier, + } => swap::commands::swap( + deps, + info.clone(), + info.sender, + ask_asset_denom, + belief_price, + max_spread, + receiver, + pool_identifier, + ), + ExecuteMsg::WithdrawLiquidity { pool_identifier } => { + liquidity::commands::withdraw_liquidity(deps, env, info, pool_identifier) } ExecuteMsg::UpdateOwnership(action) => { Ok( @@ -169,39 +158,21 @@ pub fn execute( ExecuteMsg::ExecuteSwapOperations { operations, minimum_receive, - to, + receiver, max_spread, - } => { - let api = deps.api; - router::commands::execute_swap_operations( - deps, - info, - operations, - minimum_receive, - optional_addr_validate(api, to)?, - max_spread, - ) - } - // ExecuteMsg::ExecuteSwapOperation { - // operation, - // to, - // max_spread, - // } => { - // let api = deps.api; - // router::commands::execute_swap_operation( - // deps, - // env, - // info, - // operation, - // optional_addr_validate(api, to)?.map(|v| v.to_string()), - // max_spread, - // ) - // } + } => router::commands::execute_swap_operations( + deps, + info, + operations, + minimum_receive, + receiver, + max_spread, + ), ExecuteMsg::AddSwapRoutes { swap_routes } => { - add_swap_routes(deps, info.sender, swap_routes) + router::commands::add_swap_routes(deps, info.sender, swap_routes) } ExecuteMsg::RemoveSwapRoutes { swap_routes } => { - remove_swap_routes(deps, info.sender, swap_routes) + router::commands::remove_swap_routes(deps, info.sender, swap_routes) } ExecuteMsg::UpdateConfig { whale_lair_addr, @@ -217,57 +188,42 @@ pub fn execute( } } -//todo remove. solution: just embed the content of the function where it's used -// Came from router can probably go -#[allow(dead_code)] -fn optional_addr_validate( - api: &dyn Api, - addr: Option, -) -> Result, ContractError> { - let addr = if let Some(addr) = addr { - Some(api.addr_validate(&addr)?) - } else { - None - }; - - Ok(addr) -} - #[cfg_attr(not(feature = "library"), entry_point)] -pub fn query(deps: Deps, env: Env, msg: QueryMsg) -> Result { +pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> Result { match msg { - QueryMsg::Config {} => Ok(to_json_binary(&queries::query_config(deps)?)?), + QueryMsg::Config => Ok(to_json_binary(&queries::query_config(deps)?)?), QueryMsg::AssetDecimals { - pair_identifier, + pool_identifier, denom, } => Ok(to_json_binary(&queries::query_asset_decimals( deps, - pair_identifier, + pool_identifier, denom, )?)?), QueryMsg::Simulation { offer_asset, - pair_identifier, + ask_asset_denom, + pool_identifier, } => Ok(to_json_binary(&queries::query_simulation( deps, offer_asset, - pair_identifier, + ask_asset_denom, + pool_identifier, )?)?), QueryMsg::ReverseSimulation { ask_asset, - offer_asset, - pair_identifier, + offer_asset_denom, + pool_identifier, } => Ok(to_json_binary(&queries::query_reverse_simulation( deps, - env, ask_asset, - offer_asset, - pair_identifier, + offer_asset_denom, + pool_identifier, )?)?), QueryMsg::SimulateSwapOperations { offer_amount, operations, - } => Ok(to_json_binary(&simulate_swap_operations( + } => Ok(to_json_binary(&queries::simulate_swap_operations( deps, offer_amount, operations, @@ -275,26 +231,26 @@ pub fn query(deps: Deps, env: Env, msg: QueryMsg) -> Result Ok(to_json_binary(&reverse_simulate_swap_operations( + } => Ok(to_json_binary(&queries::reverse_simulate_swap_operations( deps, ask_amount, operations, )?)?), QueryMsg::SwapRoute { offer_asset_denom, ask_asset_denom, - } => Ok(to_json_binary(&get_swap_route( + } => Ok(to_json_binary(&queries::get_swap_route( deps, offer_asset_denom, ask_asset_denom, )?)?), - QueryMsg::SwapRoutes {} => Ok(to_json_binary(&get_swap_routes(deps)?)?), + QueryMsg::SwapRoutes => Ok(to_json_binary(&queries::get_swap_routes(deps)?)?), QueryMsg::Ownership {} => Ok(to_json_binary(&cw_ownable::get_ownership(deps.storage)?)?), - QueryMsg::Pair { pair_identifier } => { - Ok(to_json_binary(&get_pair(deps, pair_identifier)?)?) + QueryMsg::Pool { pool_identifier } => { + Ok(to_json_binary(&queries::get_pool(deps, pool_identifier)?)?) } QueryMsg::SwapRouteCreator { offer_asset_denom, ask_asset_denom, - } => Ok(to_json_binary(&get_swap_route_creator( + } => Ok(to_json_binary(&queries::get_swap_route_creator( deps, offer_asset_denom, ask_asset_denom, @@ -323,6 +279,3 @@ pub fn migrate(deps: DepsMut, _env: Env, _msg: MigrateMsg) -> Result `ask_pool`. @@ -132,26 +128,23 @@ pub fn calculate_stableswap_y( if y >= previous_y { if y.checked_sub(previous_y)? <= Uint256::one() { - return y - .try_into() - .map_err(|_| ContractError::SwapOverflowError {}); + return y.try_into().map_err(|_| ContractError::SwapOverflowError); } } else if y < previous_y && previous_y.checked_sub(y)? <= Uint256::one() { - return y - .try_into() - .map_err(|_| ContractError::SwapOverflowError {}); + return y.try_into().map_err(|_| ContractError::SwapOverflowError); } } - Err(ContractError::ConvergeError {}) + Err(ContractError::ConvergeError) } +/// computes a swap pub fn compute_swap( offer_pool: Uint128, ask_pool: Uint128, offer_amount: Uint128, pool_fees: PoolFee, - swap_type: &PairType, + swap_type: &PoolType, offer_precision: u8, ask_precision: u8, ) -> Result { @@ -160,7 +153,7 @@ pub fn compute_swap( let offer_amount: Uint256 = offer_amount.into(); match swap_type { - PairType::ConstantProduct => { + PoolType::ConstantProduct => { // offer => ask // ask_amount = (ask_pool * offer_amount / (offer_pool + offer_amount)) - swap_fee - protocol_fee - burn_fee let return_amount: Uint256 = Uint256::one() @@ -169,73 +162,16 @@ pub fn compute_swap( // calculate spread, swap and protocol fees let exchange_rate = Decimal256::from_ratio(ask_pool, offer_pool); let spread_amount: Uint256 = (offer_amount * exchange_rate) - return_amount; - let swap_fee_amount: Uint256 = pool_fees.swap_fee.compute(return_amount); - let protocol_fee_amount: Uint256 = pool_fees.protocol_fee.compute(return_amount); - let burn_fee_amount: Uint256 = pool_fees.burn_fee.compute(return_amount); - //todo compute the extra fees - //let extra_fees_amount: Uint256 = pool_fees.extra_fees.compute(return_amount); + let fees_computation = compute_fees(pool_fees, return_amount)?; - // swap and protocol fee will be absorbed by the pool. Burn fee amount will be burned on a subsequent msg. - #[cfg(not(feature = "osmosis"))] - { - let return_amount: Uint256 = return_amount - .checked_sub(swap_fee_amount)? - .checked_sub(protocol_fee_amount)? - .checked_sub(burn_fee_amount)?; - - Ok(SwapComputation { - return_amount: return_amount - .try_into() - .map_err(|_| ContractError::SwapOverflowError {})?, - spread_amount: spread_amount - .try_into() - .map_err(|_| ContractError::SwapOverflowError {})?, - swap_fee_amount: swap_fee_amount - .try_into() - .map_err(|_| ContractError::SwapOverflowError {})?, - protocol_fee_amount: protocol_fee_amount - .try_into() - .map_err(|_| ContractError::SwapOverflowError {})?, - burn_fee_amount: burn_fee_amount - .try_into() - .map_err(|_| ContractError::SwapOverflowError {})?, - }) - } - - #[cfg(feature = "osmosis")] - { - let osmosis_fee_amount: Uint256 = pool_fees.osmosis_fee.compute(return_amount); - - let return_amount: Uint256 = return_amount - .checked_sub(swap_fee_amount)? - .checked_sub(protocol_fee_amount)? - .checked_sub(burn_fee_amount)? - .checked_sub(osmosis_fee_amount)?; - - Ok(SwapComputation { - return_amount: return_amount - .try_into() - .map_err(|_| ContractError::SwapOverflowError {})?, - spread_amount: spread_amount - .try_into() - .map_err(|_| ContractError::SwapOverflowError {})?, - swap_fee_amount: swap_fee_amount - .try_into() - .map_err(|_| ContractError::SwapOverflowError {})?, - protocol_fee_amount: protocol_fee_amount - .try_into() - .map_err(|_| ContractError::SwapOverflowError {})?, - burn_fee_amount: burn_fee_amount - .try_into() - .map_err(|_| ContractError::SwapOverflowError {})?, - osmosis_fee_amount: osmosis_fee_amount - .try_into() - .map_err(|_| ContractError::SwapOverflowError {})?, - }) - } + Ok(get_swap_computation( + return_amount, + spread_amount, + fees_computation, + )?) } - PairType::StableSwap { amp } => { + PoolType::StableSwap { amp } => { let offer_pool = Decimal256::decimal_with_precision(offer_pool, offer_precision)?; let ask_pool = Decimal256::decimal_with_precision(ask_pool, ask_precision)?; let offer_amount = Decimal256::decimal_with_precision(offer_amount, offer_precision)?; @@ -259,72 +195,150 @@ pub fn compute_swap( .to_uint256_with_precision(u32::from(ask_precision))? .saturating_sub(return_amount); - // subtract fees from return_amount - let swap_fee_amount: Uint256 = pool_fees.swap_fee.compute(return_amount); - let protocol_fee_amount: Uint256 = pool_fees.protocol_fee.compute(return_amount); - let burn_fee_amount: Uint256 = pool_fees.burn_fee.compute(return_amount); + let fees_computation = compute_fees(pool_fees, return_amount)?; - #[cfg(not(feature = "osmosis"))] - { - let return_amount = return_amount - .checked_sub(swap_fee_amount)? - .checked_sub(protocol_fee_amount)? - .checked_sub(burn_fee_amount)?; - - Ok(SwapComputation { - return_amount: return_amount - .try_into() - .map_err(|_| ContractError::SwapOverflowError {})?, - spread_amount: spread_amount - .try_into() - .map_err(|_| ContractError::SwapOverflowError {})?, - swap_fee_amount: swap_fee_amount - .try_into() - .map_err(|_| ContractError::SwapOverflowError {})?, - protocol_fee_amount: protocol_fee_amount - .try_into() - .map_err(|_| ContractError::SwapOverflowError {})?, - burn_fee_amount: burn_fee_amount - .try_into() - .map_err(|_| ContractError::SwapOverflowError {})?, - }) - } + Ok(get_swap_computation( + return_amount, + spread_amount, + fees_computation, + )?) + } + } +} - #[cfg(feature = "osmosis")] - { - let osmosis_fee_amount: Uint256 = pool_fees.osmosis_fee.compute(return_amount); - - let return_amount = return_amount - .checked_sub(swap_fee_amount)? - .checked_sub(protocol_fee_amount)? - .checked_sub(burn_fee_amount)? - .checked_sub(osmosis_fee_amount)?; - - Ok(SwapComputation { - return_amount: return_amount - .try_into() - .map_err(|_| ContractError::SwapOverflowError {})?, - spread_amount: spread_amount - .try_into() - .map_err(|_| ContractError::SwapOverflowError {})?, - swap_fee_amount: swap_fee_amount - .try_into() - .map_err(|_| ContractError::SwapOverflowError {})?, - protocol_fee_amount: protocol_fee_amount - .try_into() - .map_err(|_| ContractError::SwapOverflowError {})?, - burn_fee_amount: burn_fee_amount - .try_into() - .map_err(|_| ContractError::SwapOverflowError {})?, - osmosis_fee_amount: osmosis_fee_amount - .try_into() - .map_err(|_| ContractError::SwapOverflowError {})?, - }) - } +/// Computes the pool fees for a given (return) amount +fn compute_fees(pool_fees: PoolFee, amount: Uint256) -> Result { + let swap_fee_amount: Uint256 = pool_fees.swap_fee.compute(amount); + let protocol_fee_amount: Uint256 = pool_fees.protocol_fee.compute(amount); + let burn_fee_amount: Uint256 = pool_fees.burn_fee.compute(amount); + + let extra_fees_amount: Uint256 = if !pool_fees.extra_fees.is_empty() { + let mut extra_fees_amount: Uint256 = Uint256::zero(); + + for extra_fee in pool_fees.extra_fees { + extra_fees_amount = extra_fees_amount.checked_add(extra_fee.compute(amount))?; } + + extra_fees_amount + } else { + Uint256::zero() + }; + + #[cfg(not(feature = "osmosis"))] + { + Ok(FeesComputation { + swap_fee_amount, + protocol_fee_amount, + burn_fee_amount, + extra_fees_amount, + }) + } + + #[cfg(feature = "osmosis")] + { + let osmosis_fee_amount: Uint256 = pool_fees.osmosis_fee.compute(amount); + + Ok(FeesComputation { + swap_fee_amount, + protocol_fee_amount, + burn_fee_amount, + extra_fees_amount, + osmosis_fee_amount, + }) + } +} + +/// Builds the swap computation struct, subtracting the fees from the return amount. +fn get_swap_computation( + return_amount: Uint256, + spread_amount: Uint256, + fees_computation: FeesComputation, +) -> Result { + #[cfg(not(feature = "osmosis"))] + { + let return_amount = return_amount + .checked_sub(fees_computation.swap_fee_amount)? + .checked_sub(fees_computation.protocol_fee_amount)? + .checked_sub(fees_computation.burn_fee_amount)? + .checked_sub(fees_computation.extra_fees_amount)?; + + Ok(SwapComputation { + return_amount: return_amount + .try_into() + .map_err(|_| ContractError::SwapOverflowError)?, + spread_amount: spread_amount + .try_into() + .map_err(|_| ContractError::SwapOverflowError)?, + swap_fee_amount: fees_computation + .swap_fee_amount + .try_into() + .map_err(|_| ContractError::SwapOverflowError)?, + protocol_fee_amount: fees_computation + .protocol_fee_amount + .try_into() + .map_err(|_| ContractError::SwapOverflowError)?, + burn_fee_amount: fees_computation + .burn_fee_amount + .try_into() + .map_err(|_| ContractError::SwapOverflowError)?, + extra_fees_amount: fees_computation + .extra_fees_amount + .try_into() + .map_err(|_| ContractError::SwapOverflowError)?, + }) + } + + #[cfg(feature = "osmosis")] + { + let return_amount = return_amount + .checked_sub(fees_computation.swap_fee_amount)? + .checked_sub(fees_computation.protocol_fee_amount)? + .checked_sub(fees_computation.burn_fee_amount)? + .checked_sub(fees_computation.extra_fees_amount)? + .checked_sub(fees_computation.osmosis_fee_amount)?; + + Ok(SwapComputation { + return_amount: return_amount + .try_into() + .map_err(|_| ContractError::SwapOverflowError)?, + spread_amount: spread_amount + .try_into() + .map_err(|_| ContractError::SwapOverflowError)?, + swap_fee_amount: fees_computation + .swap_fee_amount + .try_into() + .map_err(|_| ContractError::SwapOverflowError)?, + protocol_fee_amount: fees_computation + .protocol_fee_amount + .try_into() + .map_err(|_| ContractError::SwapOverflowError)?, + burn_fee_amount: fees_computation + .burn_fee_amount + .try_into() + .map_err(|_| ContractError::SwapOverflowError)?, + extra_fees_amount: fees_computation + .extra_fees_amount + .try_into() + .map_err(|_| ContractError::SwapOverflowError)?, + osmosis_fee_amount: fees_computation + .osmosis_fee_amount + .try_into() + .map_err(|_| ContractError::SwapOverflowError)?, + }) } } +/// Represents the swap computation values +#[cw_serde] +pub struct FeesComputation { + pub swap_fee_amount: Uint256, + pub protocol_fee_amount: Uint256, + pub burn_fee_amount: Uint256, + pub extra_fees_amount: Uint256, + #[cfg(feature = "osmosis")] + pub osmosis_fee_amount: Uint256, +} + /// Represents the swap computation values #[cw_serde] pub struct SwapComputation { @@ -333,18 +347,49 @@ pub struct SwapComputation { pub swap_fee_amount: Uint128, pub protocol_fee_amount: Uint128, pub burn_fee_amount: Uint128, + pub extra_fees_amount: Uint128, #[cfg(feature = "osmosis")] pub osmosis_fee_amount: Uint128, } +impl SwapComputation { + /// Converts the SwapComputation struct to a SimulationResponse struct + pub fn to_simulation_response(&self) -> SimulationResponse { + #[cfg(not(feature = "osmosis"))] + { + SimulationResponse { + return_amount: self.return_amount, + spread_amount: self.spread_amount, + swap_fee_amount: self.swap_fee_amount, + protocol_fee_amount: self.protocol_fee_amount, + burn_fee_amount: self.burn_fee_amount, + extra_fees_amount: self.extra_fees_amount, + } + } + + #[cfg(feature = "osmosis")] + { + SimulationResponse { + return_amount: self.return_amount, + spread_amount: self.spread_amount, + swap_fee_amount: self.swap_fee_amount, + protocol_fee_amount: self.protocol_fee_amount, + burn_fee_amount: self.burn_fee_amount, + osmosis_fee_amount: self.osmosis_fee_amount, + extra_fees_amount: self.extra_fees_amount, + } + } + } +} + pub fn compute_offer_amount( - offer_pool: Uint128, - ask_pool: Uint128, + offer_asset_in_pool: Uint128, + ask_asset_in_pool: Uint128, ask_amount: Uint128, pool_fees: PoolFee, ) -> StdResult { - let offer_pool: Uint256 = offer_pool.into(); - let ask_pool: Uint256 = ask_pool.into(); + let offer_asset_in_pool: Uint256 = offer_asset_in_pool.into(); + let ask_asset_in_pool: Uint256 = ask_asset_in_pool.into(); let ask_amount: Uint256 = ask_amount.into(); // ask => offer @@ -370,17 +415,17 @@ pub fn compute_offer_amount( let one_minus_commission = Decimal256::one() - fees; let inv_one_minus_commission = Decimal256::one() / one_minus_commission; - let cp: Uint256 = offer_pool * ask_pool; + let cp: Uint256 = offer_asset_in_pool * ask_asset_in_pool; let offer_amount: Uint256 = Uint256::one() .multiply_ratio( cp, - ask_pool.checked_sub(ask_amount * inv_one_minus_commission)?, + ask_asset_in_pool.checked_sub(ask_amount * inv_one_minus_commission)?, ) - .checked_sub(offer_pool)?; + .checked_sub(offer_asset_in_pool)?; let before_commission_deduction: Uint256 = ask_amount * inv_one_minus_commission; let before_spread_deduction: Uint256 = - offer_amount * Decimal256::from_ratio(ask_pool, offer_pool); + offer_amount * Decimal256::from_ratio(ask_asset_in_pool, offer_asset_in_pool); let spread_amount = before_spread_deduction.saturating_sub(before_commission_deduction); @@ -431,7 +476,7 @@ pub fn assert_slippage_tolerance( slippage_tolerance: &Option, deposits: &[Uint128; 2], pools: &[Coin; 2], - pair_type: PairType, + pool_type: PoolType, amount: Uint128, pool_token_supply: Uint128, ) -> Result<(), ContractError> { @@ -446,8 +491,8 @@ pub fn assert_slippage_tolerance( let pools: [Uint256; 2] = [pools[0].amount.into(), pools[1].amount.into()]; // Ensure each prices are not dropped as much as slippage tolerance rate - match pair_type { - PairType::StableSwap { .. } => { + match pool_type { + PoolType::StableSwap { .. } => { let pools_total = pools[0].checked_add(pools[1])?; let deposits_total = deposits[0].checked_add(deposits[1])?; @@ -458,17 +503,17 @@ pub fn assert_slippage_tolerance( // slippage when adding liquidity. Due to the math behind the stableswap, the amp factor // needs to be in as well, much like when swaps are done if pool_ratio * one_minus_slippage_tolerance > deposit_ratio { - return Err(ContractError::MaxSlippageAssertion {}); + return Err(ContractError::MaxSlippageAssertion); } } - PairType::ConstantProduct => { + PoolType::ConstantProduct => { if Decimal256::from_ratio(deposits[0], deposits[1]) * one_minus_slippage_tolerance > Decimal256::from_ratio(pools[0], pools[1]) || Decimal256::from_ratio(deposits[1], deposits[0]) * one_minus_slippage_tolerance > Decimal256::from_ratio(pools[1], pools[0]) { - return Err(ContractError::MaxSlippageAssertion {}); + return Err(ContractError::MaxSlippageAssertion); } } } @@ -500,12 +545,12 @@ pub fn instantiate_fees( storage: &mut dyn Storage, asset_info_0: AssetInfo, asset_info_1: AssetInfo, - pair_key: &Vec, - fee_storage_item: cw_storage_plus::Map<'static, &'static [u8], std::vec::Vec>, + pool_key: &Vec, + fee_storage_item: cw_storage_plus::Map<'static, &'static [u8], Vec>, ) -> StdResult<()> { fee_storage_item.save( storage, - pair_key, + pool_key, &vec![ Asset { info: asset_info_0, @@ -528,74 +573,12 @@ pub fn assert_admin(deps: Deps, env: &Env, sender: &Addr) -> Result<(), Contract .query_wasm_contract_info(env.contract.address.clone())?; if let Some(admin) = contract_info.admin { if sender != deps.api.addr_validate(admin.as_str())? { - return Err(ContractError::Unauthorized {}); + return Err(ContractError::Unauthorized); } } Ok(()) } -/// This function iterates over the swap operations, simulates each swap -/// to get the final amount after all the swaps. -pub fn simulate_swap_operations( - deps: Deps, - offer_amount: Uint128, - operations: Vec, -) -> Result { - let operations_len = operations.len(); - if operations_len == 0 { - return Err(ContractError::NoSwapOperationsProvided {}); - } - - let mut amount = offer_amount; - - for operation in operations.into_iter() { - match operation { - SwapOperation::WhaleSwap { - token_in_denom, - token_out_denom: _, - pool_identifier, - } => { - let res = - query_simulation(deps, coin(amount.u128(), token_in_denom), pool_identifier)?; - amount = res.return_amount; - } - } - } - - Ok(SimulateSwapOperationsResponse { amount }) -} - -/// This function iterates over the swap operations in the reverse order, -/// simulates each swap to get the final amount after all the swaps. -pub fn reverse_simulate_swap_operations( - deps: Deps, - ask_amount: Uint128, - operations: Vec, -) -> Result { - let operations_len = operations.len(); - if operations_len == 0 { - return Err(ContractError::NoSwapOperationsProvided {}); - } - - let mut amount = ask_amount; - - for operation in operations.into_iter().rev() { - match operation { - SwapOperation::WhaleSwap { - token_in_denom: _, - token_out_denom, - pool_identifier, - } => { - let res = - query_simulation(deps, coin(amount.u128(), token_out_denom), pool_identifier)?; - amount = res.return_amount; - } - } - } - - Ok(SimulateSwapOperationsResponse { amount }) -} - /// Validates the amounts after a single side liquidity provision swap are correct. pub fn validate_asset_balance( deps: &DepsMut, @@ -641,3 +624,41 @@ pub fn aggregate_outgoing_fees( Ok(fees) } + +/// Gets the offer and ask asset indexes in a pool, together with their decimals. +pub fn get_asset_indexes_in_pool( + pool_info: &PoolInfo, + offer_asset_denom: String, + ask_asset_denom: String, +) -> Result<(Coin, Coin, usize, usize, u8, u8), ContractError> { + // Find the index of the offer and ask asset in the pools + let offer_index = pool_info + .assets + .iter() + .position(|pool| offer_asset_denom == pool.denom) + .ok_or(ContractError::AssetMismatch)?; + let ask_index = pool_info + .assets + .iter() + .position(|pool| ask_asset_denom == pool.denom) + .ok_or(ContractError::AssetMismatch)?; + + // make sure it's not the same asset + ensure!(offer_index != ask_index, ContractError::AssetMismatch); + + let decimals = &pool_info.asset_decimals; + + let offer_asset_in_pool = pool_info.assets[offer_index].clone(); + let ask_asset_in_pool = pool_info.assets[ask_index].clone(); + let offer_decimal = decimals[offer_index]; + let ask_decimal = decimals[ask_index]; + + Ok(( + offer_asset_in_pool, + ask_asset_in_pool, + offer_index, + ask_index, + offer_decimal, + ask_decimal, + )) +} diff --git a/contracts/liquidity_hub/pool-manager/src/liquidity/commands.rs b/contracts/liquidity_hub/pool-manager/src/liquidity/commands.rs index d33071f34..8d163a13b 100644 --- a/contracts/liquidity_hub/pool-manager/src/liquidity/commands.rs +++ b/contracts/liquidity_hub/pool-manager/src/liquidity/commands.rs @@ -5,8 +5,8 @@ use cosmwasm_std::{ use cosmwasm_std::{Decimal, OverflowError, Uint128}; use white_whale_std::coin::aggregate_coins; -use white_whale_std::pool_manager::ExecuteMsg; -use white_whale_std::pool_network::asset::PairType; +use white_whale_std::common::validate_addr_or_default; +use white_whale_std::pool_manager::{ExecuteMsg, PoolType}; use white_whale_std::pool_network::{ asset::{get_total_share, MINIMUM_LIQUIDITY_AMOUNT}, U256, @@ -14,19 +14,20 @@ use white_whale_std::pool_network::{ use crate::{ helpers::{self}, - state::get_pair_by_identifier, + state::get_pool_by_identifier, }; use crate::{ - state::{MANAGER_CONFIG, PAIRS}, + state::{CONFIG, POOLS}, ContractError, }; -// After writing create_pair I see this can get quite verbose so attempting to +// After writing create_pool I see this can get quite verbose so attempting to // break it down into smaller modules which house some things like swap, liquidity etc use crate::contract::SINGLE_SIDE_LIQUIDITY_PROVISION_REPLY_ID; use crate::helpers::aggregate_outgoing_fees; use crate::queries::query_simulation; use crate::state::{ - LiquidityProvisionData, SingleSideLiquidityProvisionBuffer, TMP_SINGLE_SIDE_LIQUIDITY_PROVISION, + LiquidityProvisionData, SingleSideLiquidityProvisionBuffer, + SINGLE_SIDE_LIQUIDITY_PROVISION_BUFFER, }; pub const MAX_ASSETS_PER_POOL: usize = 4; @@ -39,21 +40,21 @@ pub fn provide_liquidity( slippage_tolerance: Option, max_spread: Option, receiver: Option, - pair_identifier: String, + pool_identifier: String, unlocking_duration: Option, lock_position_identifier: Option, ) -> Result { - let config = MANAGER_CONFIG.load(deps.storage)?; + let config = CONFIG.load(deps.storage)?; // check if the deposit feature is enabled ensure!( config.feature_toggle.deposits_enabled, ContractError::OperationDisabled("provide_liquidity".to_string()) ); - // Get the pair by the pair_identifier - let mut pair = get_pair_by_identifier(&deps.as_ref(), &pair_identifier)?; + // Get the pool by the pool_identifier + let mut pool = get_pool_by_identifier(&deps.as_ref(), &pool_identifier)?; - let mut pool_assets = pair.assets.clone(); + let mut pool_assets = pool.assets.clone(); let deposits = aggregate_coins(info.funds.clone())?; ensure!(!deposits.is_empty(), ContractError::EmptyAssets); @@ -63,10 +64,13 @@ pub fn provide_liquidity( deposits.iter().all(|asset| pool_assets .iter() .any(|pool_asset| pool_asset.denom == asset.denom)), - ContractError::AssetMismatch {} + ContractError::AssetMismatch ); - let receiver = receiver.unwrap_or_else(|| info.sender.to_string()); + let receiver = + validate_addr_or_default(&deps.as_ref(), receiver, info.sender.clone()).to_string(); + + //let receiver = receiver.unwrap_or_else(|| info.sender.to_string()); // check if the user is providing liquidity with a single asset let is_single_asset_provision = deposits.len() == 1usize; @@ -76,21 +80,38 @@ pub fn provide_liquidity( ContractError::EmptyPoolForSingleSideLiquidityProvision ); + // can't provide single side liquidity on a pool with more than 2 assets + ensure!( + pool_assets.len() == 2, + ContractError::InvalidPoolAssetsForSingleSideLiquidityProvision + ); + let deposit = deposits[0].clone(); + let ask_asset_denom = pool_assets + .iter() + .find(|pool_asset| pool_asset.denom != deposit.denom) + .ok_or(ContractError::AssetMismatch)? + .denom + .clone(); + // swap half of the deposit asset for the other asset in the pool let swap_half = Coin { denom: deposit.denom.clone(), amount: deposit.amount.checked_div_floor((2u64, 1u64))?, }; - let swap_simulation_response = - query_simulation(deps.as_ref(), swap_half.clone(), pair_identifier.clone())?; + let swap_simulation_response = query_simulation( + deps.as_ref(), + swap_half.clone(), + ask_asset_denom, + pool_identifier.clone(), + )?; let ask_denom = pool_assets .iter() .find(|pool_asset| pool_asset.denom != deposit.denom) - .ok_or(ContractError::AssetMismatch {})? + .ok_or(ContractError::AssetMismatch)? .denom .clone(); @@ -118,10 +139,10 @@ pub fn provide_liquidity( // subtracting the fees. ensure!( !expected_ask_asset_balance_in_contract.amount.is_zero(), - StdError::generic_err("Spread limit exceeded") + ContractError::MaxSpreadAssertion ); - TMP_SINGLE_SIDE_LIQUIDITY_PROVISION.save( + SINGLE_SIDE_LIQUIDITY_PROVISION_BUFFER.save( deps.storage, &SingleSideLiquidityProvisionBuffer { receiver, @@ -135,7 +156,7 @@ pub fn provide_liquidity( liquidity_provision_data: LiquidityProvisionData { max_spread, slippage_tolerance, - pair_identifier: pair_identifier.clone(), + pool_identifier: pool_identifier.clone(), unlocking_duration, lock_position_identifier, }, @@ -147,12 +168,11 @@ pub fn provide_liquidity( wasm_execute( env.contract.address.into_string(), &ExecuteMsg::Swap { - offer_asset: swap_half.clone(), ask_asset_denom: ask_denom, belief_price: None, max_spread, - to: None, - pair_identifier, + receiver: None, + pool_identifier, }, vec![swap_half], )?, @@ -166,7 +186,7 @@ pub fn provide_liquidity( let pool_asset_index = pool_assets .iter() .position(|pool_asset| &pool_asset.denom == asset_denom) - .ok_or(ContractError::AssetMismatch {})?; + .ok_or(ContractError::AssetMismatch)?; // Increment the pool asset amount by the amount sent pool_assets[pool_asset_index].amount = pool_assets[pool_asset_index] @@ -177,25 +197,27 @@ pub fn provide_liquidity( // After totting up the pool assets we need to check if any of them are zero. // The very first deposit cannot be done with a single asset if pool_assets.iter().any(|deposit| deposit.amount.is_zero()) { - return Err(ContractError::InvalidZeroAmount {}); + return Err(ContractError::InvalidZeroAmount); } let mut messages: Vec = vec![]; - let liquidity_token = pair.lp_denom.clone(); + let liquidity_token = pool.lp_denom.clone(); // Compute share and other logic based on the number of assets let total_share = get_total_share(&deps.as_ref(), liquidity_token.clone())?; - let share = match &pair.pair_type { - PairType::ConstantProduct => { + let share = match &pool.pool_type { + PoolType::ConstantProduct => { if total_share == Uint128::zero() { // Make sure at least MINIMUM_LIQUIDITY_AMOUNT is deposited to mitigate the risk of the first // depositor preventing small liquidity providers from joining the pool let share = Uint128::new( (U256::from(pool_assets[0].amount.u128()) .checked_mul(U256::from(pool_assets[1].amount.u128())) - .ok_or::(ContractError::LiquidityShareComputation {}))? + .ok_or::( + ContractError::LiquidityShareComputationFailed, + ))? .integer_sqrt() .as_u128(), ) @@ -245,7 +267,7 @@ pub fn provide_liquidity( &slippage_tolerance, &deposits_as, &pools_as, - pair.pair_type.clone(), + pool.pool_type.clone(), amount, total_share, )?; @@ -253,7 +275,7 @@ pub fn provide_liquidity( amount } } - PairType::StableSwap { amp: _ } => { + PoolType::StableSwap { amp: _ } => { // TODO: Handle stableswap Uint128::one() @@ -295,9 +317,9 @@ pub fn provide_liquidity( )?); } - pair.assets = pool_assets.clone(); + pool.assets = pool_assets.clone(); - PAIRS.save(deps.storage, &pair_identifier, &pair)?; + POOLS.save(deps.storage, &pool_identifier, &pool)?; Ok(Response::new().add_messages(messages).add_attributes(vec![ ("action", "provide_liquidity"), @@ -322,9 +344,9 @@ pub fn withdraw_liquidity( deps: DepsMut, env: Env, info: MessageInfo, - pair_identifier: String, + pool_identifier: String, ) -> Result { - let config = MANAGER_CONFIG.load(deps.storage)?; + let config = CONFIG.load(deps.storage)?; // check if the withdraw feature is enabled if !config.feature_toggle.withdrawals_enabled { return Err(ContractError::OperationDisabled( @@ -332,9 +354,9 @@ pub fn withdraw_liquidity( )); } - // Get the pair by the pair_identifier - let mut pair = get_pair_by_identifier(&deps.as_ref(), &pair_identifier)?; - let liquidity_token = pair.lp_denom.clone(); + // Get the pool by the pool_identifier + let mut pool = get_pool_by_identifier(&deps.as_ref(), &pool_identifier)?; + let liquidity_token = pool.lp_denom.clone(); // Verify that the LP token was sent let amount = cw_utils::must_pay(&info, &liquidity_token)?; @@ -348,7 +370,7 @@ pub fn withdraw_liquidity( ensure!(share_ratio <= Decimal::one(), ContractError::InvalidLpShare); // Use the ratio to calculate the amount of each pool asset to refund - let refund_assets: Vec = pair + let refund_assets: Vec = pool .assets .iter() .map(|pool_asset| { @@ -367,12 +389,12 @@ pub fn withdraw_liquidity( amount: refund_assets.clone(), })); - // Deduct balances on pair_info by the amount of each refund asset - for (i, pool_asset) in pair.assets.iter_mut().enumerate() { + // Deduct balances on pool_info by the amount of each refund asset + for (i, pool_asset) in pool.assets.iter_mut().enumerate() { pool_asset.amount = pool_asset.amount.checked_sub(refund_assets[i].amount)?; } - PAIRS.save(deps.storage, &pair_identifier, &pair)?; + POOLS.save(deps.storage, &pool_identifier, &pool)?; // Burn the LP tokens messages.push(white_whale_std::lp_common::burn_lp_asset_msg( diff --git a/contracts/liquidity_hub/pool-manager/src/manager/commands.rs b/contracts/liquidity_hub/pool-manager/src/manager/commands.rs index c70b5a6de..b476a744f 100644 --- a/contracts/liquidity_hub/pool-manager/src/manager/commands.rs +++ b/contracts/liquidity_hub/pool-manager/src/manager/commands.rs @@ -1,36 +1,36 @@ use cosmwasm_std::{ attr, Attribute, Coin, CosmosMsg, DepsMut, Env, MessageInfo, Response, Uint128, }; -use white_whale_std::{ - fee::PoolFee, pool_network::asset::PairType, whale_lair::fill_rewards_msg_coin, -}; -use crate::state::{get_pair_by_identifier, PAIR_COUNTER}; +use white_whale_std::fee::PoolFee; + +use crate::state::{get_pool_by_identifier, POOL_COUNTER}; use crate::{ - state::{Config, MANAGER_CONFIG, PAIRS}, + state::{Config, CONFIG, POOLS}, ContractError, }; use white_whale_std::lp_common::LP_SYMBOL; -use white_whale_std::pool_manager::PairInfo; +use white_whale_std::pool_manager::{PoolInfo, PoolType}; +use white_whale_std::whale_lair::fill_rewards_msg_coin; pub const MAX_ASSETS_PER_POOL: usize = 4; -/// Creates a liquidity pool pair with 2, 3, or N assets. The function dynamically handles different numbers of assets, -/// allowing for the creation of pairs with varying configurations. The maximum number of assets per pool is defined by +/// Creates a pool with 2, 3, or N assets. The function dynamically handles different numbers of assets, +/// allowing for the creation of pools with varying configurations. The maximum number of assets per pool is defined by /// the constant `MAX_ASSETS_PER_POOL`. /// /// # Example /// /// ```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::fee::PoolFee; /// # use white_whale_std::fee::Fee; /// # use pool_manager::error::ContractError; /// # use pool_manager::manager::commands::MAX_ASSETS_PER_POOL; -/// # use pool_manager::manager::commands::create_pair; +/// # use pool_manager::manager::commands::create_pool; /// # use std::convert::TryInto; +/// # use white_whale_std::pool_manager::PoolType; /// # /// # fn example(deps: DepsMut, env: Env, info: MessageInfo) -> Result { /// let asset_infos = vec![ @@ -68,47 +68,47 @@ pub const MAX_ASSETS_PER_POOL: usize = 4; /// }, /// extra_fees: vec![], /// }; -/// let pair_type = PairType::ConstantProduct; +/// let pool_type = PoolType::ConstantProduct; /// let token_factory_lp = false; /// -/// let response = create_pair(deps, env, info, asset_infos, asset_decimals, pool_fees, pair_type, None)?; +/// let response = create_pool(deps, env, info, asset_infos, asset_decimals, pool_fees, pool_type, None)?; /// # Ok(response) /// # } /// ``` #[allow(unreachable_code)] #[allow(clippy::too_many_arguments)] -pub fn create_pair( +pub fn create_pool( deps: DepsMut, env: Env, info: MessageInfo, asset_denoms: Vec, asset_decimals: Vec, pool_fees: PoolFee, - pair_type: PairType, - pair_identifier: Option, + pool_type: PoolType, + pool_identifier: Option, ) -> Result { // Load config for pool creation fee - let config: Config = MANAGER_CONFIG.load(deps.storage)?; + let config: Config = CONFIG.load(deps.storage)?; - // Check if fee was provided and is sufficientd + // Check if fee was provided and is sufficient if !config.pool_creation_fee.amount.is_zero() { // verify fee payment let amount = cw_utils::must_pay(&info, &config.pool_creation_fee.denom)?; if amount != config.pool_creation_fee.amount { - return Err(ContractError::InvalidPairCreationFee { + return Err(ContractError::InvalidPoolCreationFee { amount, expected: config.pool_creation_fee.amount, }); } } - // Prepare the sending of pair creation fee + // Prepare the sending of pool creation fee let mut messages: Vec = vec![]; // send pool creation fee to whale lair let creation_fee = vec![config.pool_creation_fee]; - // send pair creation fee to whale lair i.e the new fee_collector + // send pool creation fee to whale lair i.e the new fee_collector messages.push(fill_rewards_msg_coin( config.bonding_manager_addr.into_string(), creation_fee, @@ -119,20 +119,20 @@ pub fn create_pair( .iter() .any(|asset| asset_denoms.iter().filter(|&a| a == asset).count() > 1) { - return Err(ContractError::SameAsset {}); + return Err(ContractError::SameAsset); } // Verify pool fees pool_fees.is_valid()?; - let pair_id = PAIR_COUNTER.load(deps.storage)?; + let pool_id = POOL_COUNTER.load(deps.storage)?; // if no identifier is provided, use the pool counter (id) as identifier - let identifier = pair_identifier.unwrap_or(pair_id.to_string()); + let identifier = pool_identifier.unwrap_or(pool_id.to_string()); // check if there is an existing pool with the given identifier - let pair = get_pair_by_identifier(&deps.as_ref(), &identifier); - if pair.is_ok() { - return Err(ContractError::PairExists { + let pool = get_pool_by_identifier(&deps.as_ref(), &identifier); + if pool.is_ok() { + return Err(ContractError::PoolExists { asset_infos: asset_denoms .iter() .map(|i| i.to_string()) @@ -142,8 +142,8 @@ pub fn create_pair( }); } - // prepare labels for creating the pair token with a meaningful name - let pair_label = asset_denoms.join("-"); + // prepare labels for creating the pool token with a meaningful name + let pool_label = asset_denoms.join("-"); let mut attributes = Vec::::new(); @@ -156,16 +156,16 @@ pub fn create_pair( }) .collect::>(); - let lp_symbol = format!("{pair_label}.pool.{identifier}.{LP_SYMBOL}"); + let lp_symbol = format!("{pool_label}.pool.{identifier}.{LP_SYMBOL}"); let lp_asset = format!("{}/{}/{}", "factory", env.contract.address, lp_symbol); #[allow(clippy::redundant_clone)] - PAIRS.save( + POOLS.save( deps.storage, &identifier, - &PairInfo { + &PoolInfo { asset_denoms, - pair_type: pair_type.clone(), + pool_type: pool_type.clone(), lp_denom: lp_asset.clone(), asset_decimals, pool_fees, @@ -175,31 +175,31 @@ pub fn create_pair( attributes.push(attr("lp_asset", lp_asset)); - // #[cfg(all( - // not(feature = "token_factory"), - // not(feature = "osmosis_token_factory"), - // not(feature = "injective") - // ))] - // { - // return Err(ContractError::TokenFactoryNotEnabled {}); - // } + #[cfg(all( + not(feature = "token_factory"), + not(feature = "osmosis_token_factory"), + not(feature = "injective") + ))] + { + return Err(ContractError::TokenFactoryNotEnabled); + } messages.push(white_whale_std::tokenfactory::create_denom::create_denom( env.contract.address, lp_symbol, )); - // increase pair counter - PAIR_COUNTER.update(deps.storage, |mut counter| -> Result<_, ContractError> { + // increase pool counter + POOL_COUNTER.update(deps.storage, |mut counter| -> Result<_, ContractError> { counter += 1; Ok(counter) })?; - attributes.push(attr("action", "create_pair")); - attributes.push(attr("pair", &pair_label)); - attributes.push(attr("pair_label", pair_label.as_str())); - attributes.push(attr("pair_type", pair_type.get_label())); - attributes.push(attr("pair_identifier", identifier.as_str())); + attributes.push(attr("action", "create_pool")); + attributes.push(attr("pool", &pool_label)); + attributes.push(attr("pool_label", pool_label.as_str())); + attributes.push(attr("pool_type", pool_type.get_label())); + attributes.push(attr("pool_identifier", identifier.as_str())); Ok(Response::new() .add_attributes(attributes) diff --git a/contracts/liquidity_hub/pool-manager/src/manager/update_config.rs b/contracts/liquidity_hub/pool-manager/src/manager/update_config.rs index 5bc62acaa..c0d906962 100644 --- a/contracts/liquidity_hub/pool-manager/src/manager/update_config.rs +++ b/contracts/liquidity_hub/pool-manager/src/manager/update_config.rs @@ -1,7 +1,7 @@ use cosmwasm_std::{Coin, DepsMut, MessageInfo, Response}; use white_whale_std::pool_manager::{Config, FeatureToggle}; -use crate::{state::MANAGER_CONFIG, ContractError}; +use crate::{state::CONFIG, ContractError}; pub fn update_config( deps: DepsMut, @@ -13,7 +13,7 @@ pub fn update_config( // permission check cw_ownable::assert_owner(deps.storage, &info.sender)?; - MANAGER_CONFIG.update(deps.storage, |mut config| { + CONFIG.update(deps.storage, |mut config| { if let Some(whale_lair_addr) = whale_lair_addr { let whale_lair_addr = deps.api.addr_validate(&whale_lair_addr)?; config.bonding_manager_addr = whale_lair_addr; diff --git a/contracts/liquidity_hub/pool-manager/src/math.rs b/contracts/liquidity_hub/pool-manager/src/math.rs index 3c01f4acb..2f512bc76 100644 --- a/contracts/liquidity_hub/pool-manager/src/math.rs +++ b/contracts/liquidity_hub/pool-manager/src/math.rs @@ -23,7 +23,7 @@ impl Decimal256Helper for Decimal256 { precision: u8, ) -> Result { Decimal256::from_atomics(value, u32::from(precision)) - .map_err(|_| ContractError::DecimalOverflow {}) + .map_err(|_| ContractError::DecimalOverflow) } fn checked_multiply_ratio( diff --git a/contracts/liquidity_hub/pool-manager/src/queries.rs b/contracts/liquidity_hub/pool-manager/src/queries.rs index 183d3dcdc..5a557d099 100644 --- a/contracts/liquidity_hub/pool-manager/src/queries.rs +++ b/contracts/liquidity_hub/pool-manager/src/queries.rs @@ -1,43 +1,44 @@ use std::cmp::Ordering; -use cosmwasm_std::{Coin, Decimal256, Deps, Env, Fraction, Order, StdResult, Uint128}; +use cosmwasm_std::{coin, Coin, Decimal256, Deps, Fraction, Order, StdResult, Uint128}; use white_whale_std::pool_manager::{ - AssetDecimalsResponse, Config, PairInfoResponse, ReverseSimulationResponse, SimulationResponse, - SwapRoute, SwapRouteCreatorResponse, SwapRouteResponse, SwapRoutesResponse, + AssetDecimalsResponse, Config, PoolInfoResponse, PoolType, ReverseSimulationResponse, + SimulateSwapOperationsResponse, SimulationResponse, SwapOperation, SwapRoute, + SwapRouteCreatorResponse, SwapRouteResponse, SwapRoutesResponse, }; -use white_whale_std::pool_network::asset::PairType; -use crate::state::{MANAGER_CONFIG, PAIRS}; +use crate::helpers::get_asset_indexes_in_pool; +use crate::state::{CONFIG, POOLS}; use crate::{ helpers::{self, calculate_stableswap_y, StableSwapDirection}, - state::get_pair_by_identifier, + state::get_pool_by_identifier, ContractError, }; use crate::{math::Decimal256Helper, state::SWAP_ROUTES}; /// Query the config of the contract. pub fn query_config(deps: Deps) -> Result { - Ok(MANAGER_CONFIG.load(deps.storage)?) + Ok(CONFIG.load(deps.storage)?) } /// Query the native asset decimals pub fn query_asset_decimals( deps: Deps, - pair_identifier: String, + pool_identifier: String, denom: String, ) -> Result { - let pair_info = get_pair_by_identifier(&deps, &pair_identifier)?; - let decimal_index = pair_info + let pool_info = get_pool_by_identifier(&deps, &pool_identifier)?; + let decimal_index = pool_info .asset_denoms .iter() .position(|d| d.clone() == denom) - .ok_or(ContractError::AssetMismatch {})?; + .ok_or(ContractError::AssetMismatch)?; Ok(AssetDecimalsResponse { - pair_identifier, + pool_identifier, denom, - decimals: pair_info.asset_decimals[decimal_index], + decimals: pool_info.asset_decimals[decimal_index], }) } @@ -45,42 +46,20 @@ pub fn query_asset_decimals( pub fn query_simulation( deps: Deps, offer_asset: Coin, - pair_identifier: String, + ask_asset_denom: String, + pool_identifier: String, ) -> Result { - let pair_info = get_pair_by_identifier(&deps, &pair_identifier)?; - let pools = pair_info.assets.clone(); - - // determine what's the offer and ask pool based on the offer_asset - let offer_pool: Coin; - let ask_pool: Coin; - let offer_decimal: u8; - let ask_decimal: u8; - 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 { - offer_pool = pools[0].clone(); - ask_pool = pools[1].clone(); - offer_decimal = decimals[0]; - ask_decimal = decimals[1]; - } else if offer_asset.denom == pools[1].denom { - offer_pool = pools[1].clone(); - ask_pool = pools[0].clone(); - - offer_decimal = decimals[1]; - ask_decimal = decimals[0]; - } else { - return Err(ContractError::AssetMismatch {}); - } + let pool_info = get_pool_by_identifier(&deps, &pool_identifier)?; - let pool_fees = pair_info.pool_fees; + let (offer_asset_in_pool, ask_asset_in_pool, _, _, offer_decimal, ask_decimal) = + get_asset_indexes_in_pool(&pool_info, offer_asset.denom, ask_asset_denom)?; let swap_computation = helpers::compute_swap( - offer_pool.amount, - ask_pool.amount, + offer_asset_in_pool.amount, + ask_asset_in_pool.amount, offer_asset.amount, - pool_fees, - &pair_info.pair_type, + pool_info.pool_fees, + &pool_info.pool_type, offer_decimal, ask_decimal, )?; @@ -93,6 +72,7 @@ pub fn query_simulation( swap_fee_amount: swap_computation.swap_fee_amount, protocol_fee_amount: swap_computation.protocol_fee_amount, burn_fee_amount: swap_computation.burn_fee_amount, + extra_fees_amount: swap_computation.extra_fees_amount, }) } @@ -104,6 +84,7 @@ pub fn query_simulation( swap_fee_amount: swap_computation.swap_fee_amount, protocol_fee_amount: swap_computation.protocol_fee_amount, burn_fee_amount: swap_computation.burn_fee_amount, + extra_fees_amount: swap_computation.extra_fees_amount, osmosis_fee_amount: swap_computation.osmosis_fee_amount, }) } @@ -113,26 +94,23 @@ pub fn query_simulation( /// the number of target tokens. pub fn query_reverse_simulation( deps: Deps, - _env: Env, ask_asset: Coin, - _offer_asset: Coin, - pair_identifier: String, + offer_asset_denom: String, + pool_identifier: String, ) -> Result { - let pair_info = get_pair_by_identifier(&deps, &pair_identifier)?; - let pools = pair_info.assets.clone(); - - 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(); - let ask_decimal = decimals[1]; - let pool_fees = pair_info.pool_fees; - - match pair_info.pair_type { - PairType::ConstantProduct => { + let pool_info = get_pool_by_identifier(&deps, &pool_identifier)?; + + let (offer_asset_in_pool, ask_asset_in_pool, _, _, offer_decimal, ask_decimal) = + get_asset_indexes_in_pool(&pool_info, offer_asset_denom, ask_asset.denom)?; + + let pool_fees = pool_info.pool_fees; + + //todo clean this up + match pool_info.pool_type { + PoolType::ConstantProduct => { let offer_amount_computation = helpers::compute_offer_amount( - offer_pool.amount, - ask_pool.amount, + offer_asset_in_pool.amount, + ask_asset_in_pool.amount, ask_asset.amount, pool_fees, )?; @@ -160,9 +138,11 @@ pub fn query_reverse_simulation( }) } } - PairType::StableSwap { amp } => { - let offer_pool = Decimal256::decimal_with_precision(offer_pool.amount, offer_decimal)?; - let ask_pool = Decimal256::decimal_with_precision(ask_pool.amount, ask_decimal)?; + PoolType::StableSwap { amp } => { + let offer_pool = + Decimal256::decimal_with_precision(offer_asset_in_pool.amount, offer_decimal)?; + let ask_pool = + Decimal256::decimal_with_precision(ask_asset_in_pool.amount, ask_decimal)?; let before_fees = (Decimal256::one() .checked_sub(pool_fees.protocol_fee.to_decimal_256())? @@ -240,7 +220,7 @@ pub fn query_reverse_simulation( // Router related queries, swap routes and SwapOperations // get_swap_routes which only takes deps: Deps as input -// the function will read from SWAP_ROUTES and return all swpa routes in a vec +// the function will read from SWAP_ROUTES and return all swap routes in a vec pub fn get_swap_routes(deps: Deps) -> Result { let swap_routes: Vec = SWAP_ROUTES .range(deps.storage, None, None, Order::Ascending) @@ -304,116 +284,83 @@ pub fn get_swap_route_creator( }) } -/// Gets the pair info for a given pair identifier. Returns a [PairInfoResponse]. -pub fn get_pair(deps: Deps, pair_identifier: String) -> Result { - let pair = PAIRS.load(deps.storage, &pair_identifier)?; - let total_share = deps.querier.query_supply(pair.lp_denom)?; +/// Gets the pool info for a given pool identifier. Returns a [PoolInfoResponse]. +pub fn get_pool(deps: Deps, pool_identifier: String) -> Result { + let pool = POOLS.load(deps.storage, &pool_identifier)?; + let total_share = deps.querier.query_supply(pool.lp_denom)?; - Ok(PairInfoResponse { - pair_info: PAIRS.load(deps.storage, &pair_identifier)?, + Ok(PoolInfoResponse { + pool_info: POOLS.load(deps.storage, &pool_identifier)?, total_share, }) } -// TODO: May need to remove this for a new implementation, router swap operation queries -// pub fn simulate_swap_operations( -// deps: Deps, -// env: Env, -// offer_amount: Uint128, -// operations: Vec, -// ) -> Result { -// let operations_len = operations.len(); -// if operations_len == 0 { -// return Err(ContractError::NoSwapOperationsProvided {}); -// } - -// let mut offer_amount = offer_amount; -// for operation in operations.into_iter() { -// match operation { -// SwapOperation::WhaleSwap { -// token_in_info, -// token_out_info, -// pool_identifier, -// } => { -// let res: SimulationResponse = query_simulation( -// deps, -// env.clone(), -// Asset { -// info: token_in_info, -// amount: offer_amount, -// }, -// Asset { -// info: token_out_info, -// amount: Uint128::zero(), -// }, -// pool_identifier, -// )?; - -// offer_amount = res.return_amount; -// } -// } -// } - -// Ok(SimulateSwapOperationsResponse { -// amount: offer_amount, -// }) -// } - -// pub fn reverse_simulate_swap_operations( -// deps: Deps, -// env: Env, -// ask_amount: Uint128, -// operations: Vec, -// ) -> Result { -// let operations_len = operations.len(); -// if operations_len == 0 { -// return Err(ContractError::NoSwapOperationsProvided {}); -// } - -// let mut ask_amount = ask_amount; -// for operation in operations.into_iter().rev() { -// ask_amount = match operation { -// SwapOperation::WhaleSwap { -// token_in_info: offer_asset_info, -// token_out_info: ask_asset_info, -// pool_identifier, -// } => reverse_simulate_return_amount( -// deps, -// env.clone(), -// ask_amount, -// offer_asset_info, -// ask_asset_info, -// pool_identifier, -// )?, -// } -// } - -// Ok(SimulateSwapOperationsResponse { amount: ask_amount }) -// } - -// pub fn reverse_simulate_return_amount( -// deps: Deps, -// env: Env, -// _ask_amount: Uint128, -// offer_asset_info: AssetInfo, -// ask_asset_info: AssetInfo, -// pool_identifier: String, -// ) -> Result { -// let _pair_info = get_pair_by_identifier(&deps, pool_identifier.clone())?; - -// let res: ReverseSimulationResponse = query_reverse_simulation( -// deps, -// env, -// Asset { -// info: ask_asset_info, -// amount: Uint128::zero(), -// }, -// Asset { -// info: offer_asset_info, -// amount: Uint128::zero(), -// }, -// pool_identifier, -// )?; - -// Ok(res.offer_amount) -// } +/// This function iterates over the swap operations, simulates each swap +/// to get the final amount after all the swaps. +pub fn simulate_swap_operations( + deps: Deps, + offer_amount: Uint128, + operations: Vec, +) -> Result { + let operations_len = operations.len(); + if operations_len == 0 { + return Err(ContractError::NoSwapOperationsProvided); + } + + let mut amount = offer_amount; + + for operation in operations.into_iter() { + match operation { + SwapOperation::WhaleSwap { + token_in_denom, + token_out_denom, + pool_identifier, + } => { + let res = query_simulation( + deps, + coin(amount.u128(), token_in_denom), + token_out_denom, + pool_identifier, + )?; + amount = res.return_amount; + } + } + } + + Ok(SimulateSwapOperationsResponse { amount }) +} + +/// This function iterates over the swap operations in the reverse order, +/// simulates each swap to get the final amount after all the swaps. +pub fn reverse_simulate_swap_operations( + deps: Deps, + ask_amount: Uint128, + operations: Vec, +) -> Result { + let operations_len = operations.len(); + if operations_len == 0 { + return Err(ContractError::NoSwapOperationsProvided); + } + + let mut amount = ask_amount; + + for operation in operations.into_iter().rev() { + match operation { + SwapOperation::WhaleSwap { + token_in_denom, + token_out_denom, + pool_identifier, + } => { + let res = query_simulation( + deps, + coin(amount.u128(), token_out_denom), + token_in_denom, + pool_identifier, + )?; + amount = res.return_amount; + } + } + } + + Ok(SimulateSwapOperationsResponse { amount }) +} diff --git a/contracts/liquidity_hub/pool-manager/src/router/commands.rs b/contracts/liquidity_hub/pool-manager/src/router/commands.rs index 231f7c30d..abf9b180c 100644 --- a/contracts/liquidity_hub/pool-manager/src/router/commands.rs +++ b/contracts/liquidity_hub/pool-manager/src/router/commands.rs @@ -1,13 +1,14 @@ use cosmwasm_std::{ - attr, coin, to_json_binary, Addr, BankMsg, Coin, CosmosMsg, Decimal, DepsMut, MessageInfo, - Response, Uint128, WasmMsg, + attr, coin, ensure, wasm_execute, Addr, BankMsg, Coin, CosmosMsg, Decimal, DepsMut, + MessageInfo, Response, Uint128, }; +use white_whale_std::common::validate_addr_or_default; use white_whale_std::pool_manager::{SwapOperation, SwapRoute}; use white_whale_std::whale_lair; +use crate::queries::simulate_swap_operations; use crate::{ - helpers::simulate_swap_operations, - state::{SwapOperations, MANAGER_CONFIG, SWAP_ROUTES}, + state::{SwapOperations, CONFIG, SWAP_ROUTES}, swap::perform_swap::perform_swap, ContractError, }; @@ -17,7 +18,7 @@ fn assert_operations(operations: Vec) -> Result<(), ContractError // check that the output of each swap is the input of the next swap let mut previous_output_info = operations .first() - .ok_or(ContractError::NoSwapOperationsProvided {})? + .ok_or(ContractError::NoSwapOperationsProvided)? .get_input_asset_info() .clone(); @@ -40,25 +41,26 @@ pub fn execute_swap_operations( info: MessageInfo, operations: Vec, minimum_receive: Option, - to: Option, + receiver: Option, max_spread: Option, ) -> Result { - let config = MANAGER_CONFIG.load(deps.storage)?; + let config = CONFIG.load(deps.storage)?; // check if the swap feature is enabled - if !config.feature_toggle.swaps_enabled { - return Err(ContractError::OperationDisabled("swap".to_string())); - } + ensure!( + config.feature_toggle.swaps_enabled, + ContractError::OperationDisabled("swap".to_string()) + ); // ensure that there was at least one operation // and retrieve the output token info let target_asset_denom = operations .last() - .ok_or(ContractError::NoSwapOperationsProvided {})? + .ok_or(ContractError::NoSwapOperationsProvided)? .get_target_asset_info(); let offer_asset_denom = operations .first() - .ok_or(ContractError::NoSwapOperationsProvided {})? + .ok_or(ContractError::NoSwapOperationsProvided)? .get_input_asset_info(); let offer_asset = Coin { @@ -69,7 +71,8 @@ pub fn execute_swap_operations( assert_operations(operations.clone())?; // we return the output to the sender if no alternative recipient was specified. - let to = to.unwrap_or(info.sender.clone()); + let receiver = + validate_addr_or_default(&deps.as_ref(), receiver, info.sender.clone()).to_string(); // perform each swap operation // we start off with the initial funds @@ -83,7 +86,9 @@ pub fn execute_swap_operations( for operation in operations { match operation { SwapOperation::WhaleSwap { - pool_identifier, .. + token_out_denom, + pool_identifier, + .. } => { // inside assert_operations() we have already checked that // the output of each swap is the input of the next swap. @@ -91,6 +96,7 @@ pub fn execute_swap_operations( let swap_result = perform_swap( deps.branch(), previous_swap_output.clone(), + token_out_denom, pool_identifier, None, max_spread, @@ -117,19 +123,14 @@ pub fn execute_swap_operations( })); } if !swap_result.protocol_fee_asset.amount.is_zero() { - fee_messages.push(CosmosMsg::Wasm(WasmMsg::Execute { - contract_addr: config.bonding_manager_addr.to_string(), - msg: to_json_binary(&whale_lair::ExecuteMsg::FillRewardsCoin {})?, - funds: vec![swap_result.protocol_fee_asset.clone()], - })); - } - - // todo remove, the swap_fee_asset stays in the pool - if !swap_result.swap_fee_asset.amount.is_zero() { - fee_messages.push(CosmosMsg::Bank(BankMsg::Send { - to_address: config.bonding_manager_addr.to_string(), - amount: vec![swap_result.swap_fee_asset], - })); + fee_messages.push( + wasm_execute( + config.bonding_manager_addr.to_string(), + &whale_lair::ExecuteMsg::FillRewardsCoin {}, + vec![swap_result.protocol_fee_asset.clone()], + )? + .into(), + ); } } } @@ -149,17 +150,17 @@ pub fn execute_swap_operations( // send output to recipient Ok(Response::new() .add_message(BankMsg::Send { - to_address: to.to_string(), + to_address: receiver.clone(), amount: vec![coin(receiver_balance.u128(), target_asset_denom.clone())], }) .add_messages(fee_messages) .add_attributes(vec![ - attr("action", "execute_swap_operations"), - attr("sender", info.sender.as_str()), - attr("receiver", to.as_str()), + attr("action", "execute_swap_operations".to_string()), + attr("sender", info.sender.to_string()), + attr("receiver", receiver), attr("offer_info", offer_asset.denom), - attr("offer_amount", offer_asset.amount), - attr("return_denom", &target_asset_denom), + attr("offer_amount", offer_asset.amount.to_string()), + attr("return_denom", target_asset_denom), attr("return_amount", receiver_balance.to_string()), ]) .add_attributes(swap_attributes)) @@ -222,7 +223,7 @@ pub fn remove_swap_routes( // only contract owner or route creator can remove the swap route let creator = swap_route_key.load(deps.storage)?.creator; if !cw_ownable::is_owner(deps.storage, &sender)? && sender != creator { - return Err(ContractError::Unauthorized {}); + return Err(ContractError::Unauthorized); } swap_route_key.remove(deps.storage); attributes.push(attr("swap_route", swap_route.clone().to_string())); diff --git a/contracts/liquidity_hub/pool-manager/src/state.rs b/contracts/liquidity_hub/pool-manager/src/state.rs index 32a2ebcaa..7422f803d 100644 --- a/contracts/liquidity_hub/pool-manager/src/state.rs +++ b/contracts/liquidity_hub/pool-manager/src/state.rs @@ -3,7 +3,7 @@ use cosmwasm_std::{Coin, Decimal, Deps}; use cw_storage_plus::{Index, IndexList, IndexedMap, Item, Map, UniqueIndex}; pub use white_whale_std::pool_manager::Config; -use white_whale_std::pool_manager::{PairInfo, SwapOperation}; +use white_whale_std::pool_manager::{PoolInfo, SwapOperation}; use crate::ContractError; @@ -11,11 +11,21 @@ use crate::ContractError; /// provision is completed #[cw_serde] pub struct SingleSideLiquidityProvisionBuffer { + /// The receiver of the LP pub receiver: String, + /// The expected offer asset balance in the contract after the single side liquidity provision + /// is done. Used for validations. pub expected_offer_asset_balance_in_contract: Coin, + /// The expected ask asset balance in the contract after the single side liquidity provision + /// is done. Used for validations. pub expected_ask_asset_balance_in_contract: Coin, + /// Half of the offer asset, i.e. the amount of the offer asset that is going to be swapped + /// for the ask asset so the LP is provided in balanced proportions. pub offer_asset_half: Coin, + /// The expected ask asset after half of the offer asset is swapped for the ask asset. This is + /// computed via a swap simulation. pub expected_ask_asset: Coin, + /// The remaining data for the liquidity provision. pub liquidity_provision_data: LiquidityProvisionData, } @@ -23,55 +33,65 @@ pub struct SingleSideLiquidityProvisionBuffer { /// single asset. #[cw_serde] pub struct LiquidityProvisionData { + /// The maximum allowable spread between the bid and ask prices for the pool. + /// When provided, if the spread exceeds this value, the liquidity provision will not be + /// executed. pub max_spread: Option, + /// A percentage value representing the acceptable slippage for the operation. + /// When provided, if the slippage exceeds this value, the liquidity provision will not be + /// executed. pub slippage_tolerance: Option, - pub pair_identifier: String, + /// The identifier for the pool to provide liquidity for. + pub pool_identifier: String, + /// The amount of time in seconds to unlock tokens if taking part on the incentives. If not passed, + /// the tokens will not be locked and the LP tokens will be returned to the user. pub unlocking_duration: Option, + /// The identifier of the position to lock the LP tokens in the incentive manager, if any. pub lock_position_identifier: Option, } -pub const TMP_SINGLE_SIDE_LIQUIDITY_PROVISION: Item = - Item::new("tmp_single_side_liquidity_provision"); +pub const SINGLE_SIDE_LIQUIDITY_PROVISION_BUFFER: Item = + Item::new("single_side_liquidity_provision_buffer"); -pub const PAIRS: IndexedMap<&str, PairInfo, PairIndexes> = IndexedMap::new( - "pairs", - PairIndexes { - lp_asset: UniqueIndex::new(|v| v.lp_denom.to_string(), "pairs__lp_asset"), +pub const POOLS: IndexedMap<&str, PoolInfo, PoolIndexes> = IndexedMap::new( + "pools", + PoolIndexes { + lp_asset: UniqueIndex::new(|v| v.lp_denom.to_string(), "pools__lp_asset"), }, ); -pub struct PairIndexes<'a> { - pub lp_asset: UniqueIndex<'a, String, PairInfo, String>, - // pub asset_info: MultiIndex<'a, String, NPairInfo, String>, +pub struct PoolIndexes<'a> { + pub lp_asset: UniqueIndex<'a, String, PoolInfo, String>, } -impl<'a> IndexList for PairIndexes<'a> { - fn get_indexes(&'_ self) -> Box> + '_> { - let v: Vec<&dyn Index> = vec![&self.lp_asset]; +impl<'a> IndexList for PoolIndexes<'a> { + fn get_indexes(&'_ self) -> Box> + '_> { + let v: Vec<&dyn Index> = vec![&self.lp_asset]; Box::new(v.into_iter()) } } -/// Gets the pair given its identifier -pub fn get_pair_by_identifier( +/// Gets the pool given its identifier +pub fn get_pool_by_identifier( deps: &Deps, - pair_identifier: &str, -) -> Result { - PAIRS - .may_load(deps.storage, pair_identifier)? - .ok_or(ContractError::UnExistingPair {}) + pool_identifier: &str, +) -> Result { + POOLS + .may_load(deps.storage, pool_identifier)? + .ok_or(ContractError::UnExistingPool) } -// Swap routes are used to establish defined routes for a given fee -// token to a desired fee token and is used for fee collection +/// Swap routes are used to establish defined routes for a given fee +/// token to a desired fee token and is used for fee collection #[cw_serde] pub struct SwapOperations { - // creator of the swap route, can remove it later + /// creator of the swap route, can remove it later pub creator: String, + /// The operations to be executed for a given swap. pub swap_operations: Vec, } pub const SWAP_ROUTES: Map<(&str, &str), SwapOperations> = Map::new("swap_routes"); -pub const MANAGER_CONFIG: Item = Item::new("manager_config"); -pub const PAIR_COUNTER: Item = Item::new("vault_count"); +pub const CONFIG: Item = Item::new("config"); +pub const POOL_COUNTER: Item = Item::new("pool_count"); diff --git a/contracts/liquidity_hub/pool-manager/src/swap/commands.rs b/contracts/liquidity_hub/pool-manager/src/swap/commands.rs index a4dc082b9..cd101024b 100644 --- a/contracts/liquidity_hub/pool-manager/src/swap/commands.rs +++ b/contracts/liquidity_hub/pool-manager/src/swap/commands.rs @@ -1,128 +1,119 @@ -use crate::{state::MANAGER_CONFIG, ContractError}; +use crate::{state::CONFIG, ContractError}; use cosmwasm_std::{ - ensure, to_json_binary, Addr, BankMsg, Coin, CosmosMsg, DepsMut, Env, MessageInfo, Response, - WasmMsg, + ensure, wasm_execute, Addr, BankMsg, CosmosMsg, DepsMut, MessageInfo, Response, }; pub const MAX_ASSETS_PER_POOL: usize = 4; -use crate::state::get_pair_by_identifier; +use crate::state::get_pool_by_identifier; use cosmwasm_std::Decimal; +use white_whale_std::common::validate_addr_or_default; use white_whale_std::whale_lair; use super::perform_swap::perform_swap; #[allow(clippy::too_many_arguments)] pub fn swap( - deps: DepsMut, - _env: Env, + mut deps: DepsMut, info: MessageInfo, sender: Addr, - offer_asset: Coin, ask_asset_denom: String, belief_price: Option, max_spread: Option, - to: Option, - pair_identifier: String, + receiver: Option, + pool_identifier: String, ) -> Result { - let config = MANAGER_CONFIG.load(deps.storage)?; + let config = CONFIG.load(deps.storage)?; // check if the swap feature is enabled ensure!( config.feature_toggle.swaps_enabled, ContractError::OperationDisabled("swap".to_string()) ); - // todo remove this, not needed. You can just swap whatever it is sent in info.funds, just worth - // veritying the asset is the same as the one in the pool - if cw_utils::one_coin(&info)? != offer_asset { - return Err(ContractError::AssetMismatch {}); - } + let offer_asset = cw_utils::one_coin(&info)?; // verify that the assets sent match the ones from the pool - let pair = get_pair_by_identifier(&deps.as_ref(), &pair_identifier)?; + let pool = get_pool_by_identifier(&deps.as_ref(), &pool_identifier)?; ensure!( - [ask_asset_denom, offer_asset.denom.clone()] + vec![ask_asset_denom.clone(), offer_asset.denom.clone()] .iter() - .all(|asset| pair + .all(|asset| pool .assets .iter() .any(|pool_asset| pool_asset.denom == *asset)), - ContractError::AssetMismatch {} + ContractError::AssetMismatch ); // perform the swap let swap_result = perform_swap( - deps, + deps.branch(), offer_asset.clone(), - pair_identifier, + ask_asset_denom, + pool_identifier, belief_price, max_spread, )?; // add messages let mut messages: Vec = vec![]; - let receiver = to.unwrap_or_else(|| sender.clone()); - // first we add the swap result + let receiver = validate_addr_or_default(&deps.as_ref(), receiver, info.sender); + if !swap_result.return_asset.amount.is_zero() { messages.push(CosmosMsg::Bank(BankMsg::Send { - to_address: receiver.to_string(), + to_address: receiver.clone().into_string(), amount: vec![swap_result.return_asset.clone()], })); } - // then we add the fees if !swap_result.burn_fee_asset.amount.is_zero() { messages.push(CosmosMsg::Bank(BankMsg::Burn { amount: vec![swap_result.burn_fee_asset.clone()], })); } + if !swap_result.protocol_fee_asset.amount.is_zero() { - messages.push(CosmosMsg::Wasm(WasmMsg::Execute { - contract_addr: config.bonding_manager_addr.to_string(), - msg: to_json_binary(&whale_lair::ExecuteMsg::FillRewardsCoin {})?, - funds: vec![swap_result.protocol_fee_asset.clone()], - })); + messages.push( + wasm_execute( + config.bonding_manager_addr.to_string(), + &whale_lair::ExecuteMsg::FillRewardsCoin {}, + vec![swap_result.protocol_fee_asset.clone()], + )? + .into(), + ); } - //todo remove, this stays within the pool. Verify this with a test with multiple (duplicated) - // pools, see how the swap fees behave - // if !swap_result.swap_fee_asset.amount.is_zero() { - // messages.push(CosmosMsg::Bank(BankMsg::Send { - // to_address: config.bonding_manager_addr.to_string(), - // amount: vec![swap_result.swap_fee_asset.clone()], - // })); - // } + println!("messages: {:?}", messages); Ok(Response::new().add_messages(messages).add_attributes(vec![ - ("action", "swap"), - ("sender", sender.as_str()), - ("receiver", receiver.as_str()), - ("offer_denom", &offer_asset.denom), - ("ask_denom", &swap_result.return_asset.denom), - ("offer_amount", &offer_asset.amount.to_string()), - ( - "return_amount", - &swap_result.return_asset.amount.to_string(), - ), - ("spread_amount", &swap_result.spread_amount.to_string()), + ("action", "swap".to_string()), + ("sender", sender.into_string()), + ("receiver", receiver.into_string()), + ("offer_denom", offer_asset.denom), + ("ask_denom", swap_result.return_asset.denom), + ("offer_amount", offer_asset.amount.to_string()), + ("return_amount", swap_result.return_asset.amount.to_string()), + ("spread_amount", swap_result.spread_amount.to_string()), ( "swap_fee_amount", - &swap_result.swap_fee_asset.amount.to_string(), + swap_result.swap_fee_asset.amount.to_string(), ), ( "protocol_fee_amount", - &swap_result.protocol_fee_asset.amount.to_string(), + swap_result.protocol_fee_asset.amount.to_string(), ), ( "burn_fee_amount", - &swap_result.burn_fee_asset.amount.to_string(), + swap_result.burn_fee_asset.amount.to_string(), ), #[cfg(feature = "osmosis")] ( "osmosis_fee_amount", - &swap_result.osmosis_fee_amount.to_string(), + swap_result.osmosis_fee_amount.to_string(), + ), + ( + "swap_type", + swap_result.pool_info.pool_type.get_label().to_string(), ), - ("swap_type", swap_result.pair_info.pair_type.get_label()), ])) } diff --git a/contracts/liquidity_hub/pool-manager/src/swap/perform_swap.rs b/contracts/liquidity_hub/pool-manager/src/swap/perform_swap.rs index ffda14a05..01ba30198 100644 --- a/contracts/liquidity_hub/pool-manager/src/swap/perform_swap.rs +++ b/contracts/liquidity_hub/pool-manager/src/swap/perform_swap.rs @@ -1,11 +1,12 @@ use cosmwasm_std::{Coin, Decimal, DepsMut, Uint128}; -use white_whale_std::pool_manager::PairInfo; +use white_whale_std::pool_manager::PoolInfo; use white_whale_std::pool_network::swap::assert_max_spread; +use crate::helpers::{aggregate_outgoing_fees, get_asset_indexes_in_pool}; use crate::{ helpers, - state::{get_pair_by_identifier, PAIRS}, + state::{get_pool_by_identifier, POOLS}, ContractError, }; @@ -22,14 +23,14 @@ pub struct SwapResult { /// The osmosis fee of `return_asset` associated with this swap transaction. #[cfg(feature = "osmosis")] pub osmosis_fee_asset: Coin, - /// The pair that was traded. - pub pair_info: PairInfo, + /// The pool that was traded. + pub pool_info: PoolInfo, /// The amount of spread that occurred during the swap from the original exchange rate. pub spread_amount: Uint128, } /// Attempts to perform a swap from `offer_asset` to the relevant opposing -/// asset in the pair identified by `pair_identifier`. +/// asset in the pool identified by `pool_identifier`. /// /// Assumes that `offer_asset` is a **native token**. /// @@ -39,52 +40,35 @@ pub struct SwapResult { pub fn perform_swap( deps: DepsMut, offer_asset: Coin, - pair_identifier: String, + ask_asset_denom: String, + pool_identifier: String, belief_price: Option, max_spread: Option, ) -> Result { - let mut pair_info = get_pair_by_identifier(&deps.as_ref(), &pair_identifier)?; - let pools = &pair_info.assets; - - // compute the offer and ask pool - let offer_pool: Coin; - let ask_pool: Coin; - let offer_decimal: u8; - let ask_decimal: u8; - let decimals = &pair_info.asset_decimals; - - // calculate the swap - // first, set relevant variables - if offer_asset.denom == pools[0].denom { - offer_pool = pools[0].clone(); - ask_pool = pools[1].clone(); - offer_decimal = decimals[0]; - ask_decimal = decimals[1]; - } else if offer_asset.denom == pools[1].denom { - offer_pool = pools[1].clone(); - ask_pool = pools[0].clone(); - - offer_decimal = decimals[1]; - ask_decimal = decimals[0]; - } else { - return Err(ContractError::AssetMismatch {}); - } + let mut pool_info = get_pool_by_identifier(&deps.as_ref(), &pool_identifier)?; - let offer_amount = offer_asset.amount; - let pool_fees = pair_info.pool_fees.clone(); + let ( + offer_asset_in_pool, + ask_asset_in_pool, + offer_index, + ask_index, + offer_decimal, + ask_decimal, + ) = get_asset_indexes_in_pool(&pool_info, offer_asset.denom, ask_asset_denom)?; + // compute the swap let swap_computation = helpers::compute_swap( - offer_pool.amount, - ask_pool.amount, - offer_amount, - pool_fees, - &pair_info.pair_type, + offer_asset_in_pool.amount, + ask_asset_in_pool.amount, + offer_asset.amount, + pool_info.pool_fees.clone(), + &pool_info.pool_type, offer_decimal, ask_decimal, )?; let return_asset = Coin { - denom: ask_pool.denom.clone(), + denom: ask_asset_in_pool.denom.clone(), amount: swap_computation.return_amount, }; @@ -97,35 +81,37 @@ pub fn perform_swap( return_asset.amount, swap_computation.spread_amount, )?; - // State changes to the pairs balances - // Deduct the return amount from the pool and add the offer amount to the pool - if offer_asset.denom == pools[0].denom { - pair_info.assets[0].amount += offer_amount; - pair_info.assets[1].amount -= swap_computation.return_amount; - pair_info.assets[1].amount -= swap_computation.protocol_fee_amount; - pair_info.assets[1].amount -= swap_computation.burn_fee_amount; - } else { - pair_info.assets[1].amount += offer_amount; - pair_info.assets[0].amount -= swap_computation.return_amount; - pair_info.assets[0].amount -= swap_computation.protocol_fee_amount; - pair_info.assets[0].amount -= swap_computation.burn_fee_amount; - } - PAIRS.save(deps.storage, &pair_identifier, &pair_info)?; + // State changes to the pools balances + { + // add the offer amount to the pool + pool_info.assets[offer_index].amount = pool_info.assets[offer_index] + .amount + .checked_add(offer_asset.amount)?; + + // Deduct the return amount and fees from the pool + let outgoing_fees = aggregate_outgoing_fees(&swap_computation.to_simulation_response())?; + + pool_info.assets[ask_index].amount = pool_info.assets[ask_index] + .amount + .checked_sub(return_asset.amount)? + .checked_sub(outgoing_fees)?; + + POOLS.save(deps.storage, &pool_identifier, &pool_info)?; + } - // TODO: Might be handy to make the below fees into a helper method let burn_fee_asset = Coin { - denom: ask_pool.denom.clone(), + denom: ask_asset_in_pool.denom.clone(), amount: swap_computation.burn_fee_amount, }; let protocol_fee_asset = Coin { - denom: ask_pool.denom.clone(), + denom: ask_asset_in_pool.denom.clone(), amount: swap_computation.protocol_fee_amount, }; #[allow(clippy::redundant_clone)] let swap_fee_asset = Coin { - denom: ask_pool.denom.clone(), + denom: ask_asset_in_pool.denom.clone(), amount: swap_computation.swap_fee_amount, }; @@ -136,7 +122,7 @@ pub fn perform_swap( swap_fee_asset, burn_fee_asset, protocol_fee_asset, - pair_info, + pool_info, spread_amount: swap_computation.spread_amount, }) } @@ -144,7 +130,7 @@ pub fn perform_swap( #[cfg(feature = "osmosis")] { let osmosis_fee_asset = Coin { - denom: ask_pool.denom, + denom: ask_asset_in_pool.denom, amount: swap_computation.swap_fee_amount, }; @@ -154,7 +140,7 @@ pub fn perform_swap( burn_fee_asset, protocol_fee_asset, osmosis_fee_asset, - pair_info, + pool_info, spread_amount: swap_computation.spread_amount, }) } diff --git a/contracts/liquidity_hub/pool-manager/src/tests/gas/mod.rs b/contracts/liquidity_hub/pool-manager/src/tests/gas/mod.rs deleted file mode 100644 index 8b1378917..000000000 --- a/contracts/liquidity_hub/pool-manager/src/tests/gas/mod.rs +++ /dev/null @@ -1 +0,0 @@ - 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 bd75c667f..80c1660d6 100644 --- a/contracts/liquidity_hub/pool-manager/src/tests/integration_tests.rs +++ b/contracts/liquidity_hub/pool-manager/src/tests/integration_tests.rs @@ -2,15 +2,13 @@ use cosmwasm_std::{coin, Addr, Coin, Decimal, Uint128}; use white_whale_std::fee::Fee; use white_whale_std::fee::PoolFee; +use white_whale_std::pool_manager::PoolType; use white_whale_std::pool_network::asset::MINIMUM_LIQUIDITY_AMOUNT; use crate::ContractError; use super::suite::TestingSuite; -// Using our suite lets test create pair -// and add liquidity to it - #[test] fn instantiate_normal() { let mut suite = TestingSuite::default_with_balances(vec![]); @@ -33,7 +31,6 @@ fn deposit_and_withdraw_sanity_check() { // Asset denoms with uwhale and uluna let asset_denoms = vec!["uwhale".to_string(), "uluna".to_string()]; - // Default Pool fees white_whale_std::pool_network::pair::PoolFee #[cfg(not(feature = "osmosis"))] let pool_fees = PoolFee { protocol_fee: Fee { @@ -65,13 +62,13 @@ fn deposit_and_withdraw_sanity_check() { extra_fees: vec![], }; - // Create a pair - suite.instantiate_default().create_pair( + // Create a pool + suite.instantiate_default().create_pool( creator.clone(), asset_denoms, vec![6u8, 6u8], pool_fees, - white_whale_std::pool_network::asset::PairType::ConstantProduct, + PoolType::ConstantProduct, Some("whale-uluna".to_string()), vec![coin(1000, "uusd")], |result| { @@ -90,6 +87,7 @@ fn deposit_and_withdraw_sanity_check() { None, None, None, + None, vec![ Coin { denom: "uwhale".to_string(), @@ -123,7 +121,7 @@ fn deposit_and_withdraw_sanity_check() { // contract should have 1_000 LP shares (MINIMUM_LIQUIDITY_AMOUNT) .query_all_balances(contract_addr.to_string(), |result| { let balances = result.unwrap(); - // check that balances has 999_000 factory/migaloo1wug8sewp6cedgkmrmvhl3lf3tulagm9hnvy8p0rppz9yjw0g4wtqvk723g/uwhale-uluna.vault.whale-uluna.uLP + // check that balances has 999_000 factory/migaloo1wug8sewp6cedgkmrmvhl3lf3tulagm9hnvy8p0rppz9yjw0g4wtqvk723g/uwhale-uluna.pool.whale-uluna.uLP assert!(balances.iter().any(|coin| { coin.denom == lp_denom.clone() && coin.amount == MINIMUM_LIQUIDITY_AMOUNT })); @@ -171,12 +169,12 @@ fn deposit_and_withdraw_sanity_check() { }); } -mod pair_creation_failures { +mod pool_creation_failures { use super::*; - // Insufficient fee to create pair; 90 instead of 100 + // Insufficient fee to create pool; 90 instead of 100 #[test] - fn insufficient_pair_creation_fee() { + fn insufficient_pool_creation_fee() { let mut suite = TestingSuite::default_with_balances(vec![ coin(1_000_000_001u128, "uwhale".to_string()), coin(1_000_000_000u128, "uluna".to_string()), @@ -189,7 +187,6 @@ mod pair_creation_failures { let asset_infos = vec!["uwhale".to_string()]; - // Default Pool fees white_whale_std::pool_network::pair::PoolFee #[cfg(not(feature = "osmosis"))] let pool_fees = PoolFee { protocol_fee: Fee { @@ -220,20 +217,20 @@ mod pair_creation_failures { }, extra_fees: vec![], }; - // Create a pair - suite.instantiate_default().create_pair( + // Create a poo + suite.instantiate_default().create_pool( creator.clone(), asset_infos, vec![6u8, 6u8], pool_fees, - white_whale_std::pool_network::asset::PairType::ConstantProduct, + PoolType::ConstantProduct, None, vec![coin(90, "uusd")], |result| { let err = result.unwrap_err().downcast::().unwrap(); match err { - ContractError::InvalidPairCreationFee { .. } => {} + ContractError::InvalidPoolCreationFee { .. } => {} _ => panic!("Wrong error type, should return ContractError::Unauthorized"), } }, @@ -241,7 +238,7 @@ mod pair_creation_failures { } #[test] - fn cant_recreate_existing_pair() { + fn cant_recreate_existing_poo() { let mut suite = TestingSuite::default_with_balances(vec![ coin(1_000_000_001u128, "uwhale".to_string()), coin(1_000_000_000u128, "uluna".to_string()), @@ -254,7 +251,6 @@ mod pair_creation_failures { let asset_infos = vec!["uwhale".to_string()]; - // Default Pool fees white_whale_std::pool_network::pair::PoolFee #[cfg(not(feature = "osmosis"))] let pool_fees = PoolFee { protocol_fee: Fee { @@ -286,34 +282,34 @@ mod pair_creation_failures { extra_fees: vec![], }; - // Create a pair + // Create a poo suite .instantiate_default() - .create_pair( + .create_pool( creator.clone(), asset_infos.clone(), vec![6u8, 6u8], pool_fees.clone(), - white_whale_std::pool_network::asset::PairType::ConstantProduct, - Some("mycoolpair".to_string()), + PoolType::ConstantProduct, + Some("mycoolpoo".to_string()), vec![coin(1000, "uusd")], |result| { result.unwrap(); }, ) - .create_pair( + .create_pool( creator.clone(), asset_infos, vec![6u8, 6u8], pool_fees, - white_whale_std::pool_network::asset::PairType::ConstantProduct, - Some("mycoolpair".to_string()), + PoolType::ConstantProduct, + Some("mycoolpoo".to_string()), vec![coin(1000, "uusd")], |result| { let err = result.unwrap_err().downcast::().unwrap(); match err { - ContractError::PairExists { .. } => {} - _ => panic!("Wrong error type, should return ContractError::PairExists"), + ContractError::PoolExists { .. } => {} + _ => panic!("Wrong error type, should return ContractError::PoolExists"), } }, ); @@ -339,8 +335,8 @@ mod router { let _unauthorized = suite.senders[2].clone(); // Asset infos with uwhale and uluna - let first_pair = vec!["uwhale".to_string(), "uluna".to_string()]; - let second_pair = vec!["uluna".to_string(), "uusd".to_string()]; + let first_pool = vec!["uwhale".to_string(), "uluna".to_string()]; + let second_pool = vec!["uluna".to_string(), "uusd".to_string()]; #[cfg(not(feature = "osmosis"))] let pool_fees = PoolFee { @@ -372,27 +368,27 @@ mod router { extra_fees: vec![], }; - // Create a pair + // Create a pool suite .instantiate_default() - .create_pair( + .create_pool( creator.clone(), - first_pair, + first_pool, vec![6u8, 6u8], pool_fees.clone(), - white_whale_std::pool_network::asset::PairType::ConstantProduct, + PoolType::ConstantProduct, Some("whale-uluna".to_string()), vec![coin(1000, "uusd")], |result| { result.unwrap(); }, ) - .create_pair( + .create_pool( creator.clone(), - second_pair, + second_pool, vec![6u8, 6u8], pool_fees, - white_whale_std::pool_network::asset::PairType::ConstantProduct, + PoolType::ConstantProduct, Some("uluna-uusd".to_string()), vec![coin(1000, "uusd")], |result| { @@ -407,6 +403,7 @@ mod router { None, None, None, + None, vec![ Coin { denom: "uwhale".to_string(), @@ -431,6 +428,7 @@ mod router { None, None, None, + None, vec![ Coin { denom: "uluna".to_string(), @@ -449,7 +447,7 @@ mod router { ); // Prepare the swap operations, we want to go from WHALE -> UUSD - // We will use the uluna-uusd pair as the intermediary pool + // We will use the uluna-uusd pool as the intermediary pool let swap_operations = vec![ white_whale_std::pool_manager::SwapOperation::WhaleSwap { @@ -465,7 +463,7 @@ mod router { ]; // before swap uusd balance = 1_000_000_000 - // - 2*1_000 pair creation fee + // - 2*1_000 pool creation fee // - 1_000_000 liquidity provision // = 998_998_000 let pre_swap_amount = 998_998_000; @@ -498,7 +496,7 @@ mod router { suite.bonding_manager_addr.to_string(), "uusd".to_string(), |amt| { - assert_eq!(amt.unwrap().amount.u128(), 2000 + 4 * 2); + assert_eq!(amt.unwrap().amount.u128(), 2000 + 4); }, ); suite.query_balance( @@ -512,7 +510,7 @@ mod router { suite.bonding_manager_addr.to_string(), "uluna".to_string(), |amt| { - assert_eq!(amt.unwrap().amount.u128(), 4 * 2); + assert_eq!(amt.unwrap().amount.u128(), 4); }, ); } @@ -529,10 +527,9 @@ mod router { let _unauthorized = suite.senders[2].clone(); // Asset infos with uwhale and uluna - let first_pair = vec!["uwhale".to_string(), "uluna".to_string()]; - let second_pair = vec!["uluna".to_string(), "uusd".to_string()]; + let first_pool = vec!["uwhale".to_string(), "uluna".to_string()]; + let second_pool = vec!["uluna".to_string(), "uusd".to_string()]; - // Default Pool fees white_whale_std::pool_network::pair::PoolFee // Protocol fee is 0.01% and swap fee is 0.02% and burn fee is 0% #[cfg(not(feature = "osmosis"))] let pool_fees = PoolFee { @@ -565,27 +562,27 @@ mod router { extra_fees: vec![], }; - // Create a pair + // Create a pool suite .instantiate_default() - .create_pair( + .create_pool( creator.clone(), - first_pair, + first_pool, vec![6u8, 6u8], pool_fees.clone(), - white_whale_std::pool_network::asset::PairType::ConstantProduct, + PoolType::ConstantProduct, Some("whale-uluna".to_string()), vec![coin(1000, "uusd")], |result| { result.unwrap(); }, ) - .create_pair( + .create_pool( creator.clone(), - second_pair, + second_pool, vec![6u8, 6u8], pool_fees, - white_whale_std::pool_network::asset::PairType::ConstantProduct, + PoolType::ConstantProduct, Some("uluna-uusd".to_string()), vec![coin(1000, "uusd")], |result| { @@ -600,6 +597,7 @@ mod router { None, None, None, + None, vec![ Coin { denom: "uwhale".to_string(), @@ -624,6 +622,7 @@ mod router { None, None, None, + None, vec![ Coin { denom: "uluna".to_string(), @@ -654,7 +653,7 @@ mod router { |result| { assert_eq!( result.unwrap_err().downcast_ref::(), - Some(&ContractError::NoSwapOperationsProvided {}) + Some(&ContractError::NoSwapOperationsProvided) ) }, ); @@ -672,10 +671,9 @@ mod router { let _unauthorized = suite.senders[2].clone(); // Asset infos with uwhale and uluna - let first_pair = vec!["uwhale".to_string(), "uluna".to_string()]; - let second_pair = vec!["uluna".to_string(), "uusd".to_string()]; + let first_pool = vec!["uwhale".to_string(), "uluna".to_string()]; + let second_pool = vec!["uluna".to_string(), "uusd".to_string()]; - // Default Pool fees white_whale_std::pool_network::pair::PoolFee // Protocol fee is 0.01% and swap fee is 0.02% and burn fee is 0% #[cfg(not(feature = "osmosis"))] let pool_fees = PoolFee { @@ -708,27 +706,27 @@ mod router { extra_fees: vec![], }; - // Create a pair + // Create a pool suite .instantiate_default() - .create_pair( + .create_pool( creator.clone(), - first_pair, + first_pool, vec![6u8, 6u8], pool_fees.clone(), - white_whale_std::pool_network::asset::PairType::ConstantProduct, + PoolType::ConstantProduct, Some("whale-uluna".to_string()), vec![coin(1000, "uusd")], |result| { result.unwrap(); }, ) - .create_pair( + .create_pool( creator.clone(), - second_pair, + second_pool, vec![6u8, 6u8], pool_fees, - white_whale_std::pool_network::asset::PairType::ConstantProduct, + PoolType::ConstantProduct, Some("uluna-uusd".to_string()), vec![coin(1000, "uusd")], |result| { @@ -743,6 +741,7 @@ mod router { None, None, None, + None, vec![ Coin { denom: "uwhale".to_string(), @@ -767,6 +766,7 @@ mod router { None, None, None, + None, vec![ Coin { denom: "uluna".to_string(), @@ -785,7 +785,7 @@ mod router { ); // Prepare the swap operations, we want to go from WHALE -> UUSD - // We will use the uluna-uusd pair as the intermediary pool + // We will use the uluna-uusd pool as the intermediary pool let swap_operations = vec![ white_whale_std::pool_manager::SwapOperation::WhaleSwap { @@ -831,10 +831,9 @@ mod router { let unauthorized = suite.senders[2].clone(); // Asset infos with uwhale and uluna - let first_pair = vec!["uwhale".to_string(), "uluna".to_string()]; - let second_pair = vec!["uluna".to_string(), "uusd".to_string()]; + let first_pool = vec!["uwhale".to_string(), "uluna".to_string()]; + let second_pool = vec!["uluna".to_string(), "uusd".to_string()]; - // Default Pool fees white_whale_std::pool_network::pair::PoolFee // Protocol fee is 0.01% and swap fee is 0.02% and burn fee is 0% #[cfg(not(feature = "osmosis"))] let pool_fees = PoolFee { @@ -867,27 +866,27 @@ mod router { extra_fees: vec![], }; - // Create a pair + // Create a pool suite .instantiate_default() - .create_pair( + .create_pool( creator.clone(), - first_pair, + first_pool, vec![6u8, 6u8], pool_fees.clone(), - white_whale_std::pool_network::asset::PairType::ConstantProduct, + PoolType::ConstantProduct, Some("whale-uluna".to_string()), vec![coin(1000, "uusd")], |result| { result.unwrap(); }, ) - .create_pair( + .create_pool( creator.clone(), - second_pair, + second_pool, vec![6u8, 6u8], pool_fees, - white_whale_std::pool_network::asset::PairType::ConstantProduct, + PoolType::ConstantProduct, Some("uluna-uusd".to_string()), vec![coin(1000, "uusd")], |result| { @@ -903,6 +902,7 @@ mod router { None, None, None, + None, vec![ Coin { denom: "uwhale".to_string(), @@ -927,6 +927,7 @@ mod router { None, None, None, + None, vec![ Coin { denom: "uluna".to_string(), @@ -945,7 +946,7 @@ mod router { ); // Prepare the swap operations, we want to go from WHALE -> UUSD - // We will use the uluna-uusd pair as the intermediary pool + // We will use the uluna-uusd pool as the intermediary pool let swap_operations = vec![ white_whale_std::pool_manager::SwapOperation::WhaleSwap { @@ -1061,8 +1062,8 @@ mod router { let _unauthorized = suite.senders[2].clone(); // Asset infos with uwhale and uluna - let first_pair = vec!["uwhale".to_string(), "uluna".to_string()]; - let second_pair = vec!["uluna".to_string(), "uusd".to_string()]; + let first_pool = vec!["uwhale".to_string(), "uluna".to_string()]; + let second_pool = vec!["uluna".to_string(), "uusd".to_string()]; #[cfg(not(feature = "osmosis"))] let pool_fees = PoolFee { @@ -1094,27 +1095,27 @@ mod router { extra_fees: vec![], }; - // Create a pair + // Create a pool suite .instantiate_default() - .create_pair( + .create_pool( creator.clone(), - first_pair, + first_pool, vec![6u8, 6u8], pool_fees.clone(), - white_whale_std::pool_network::asset::PairType::ConstantProduct, + PoolType::ConstantProduct, Some("whale-uluna".to_string()), vec![coin(1000, "uusd")], |result| { result.unwrap(); }, ) - .create_pair( + .create_pool( creator.clone(), - second_pair, + second_pool, vec![6u8, 6u8], pool_fees, - white_whale_std::pool_network::asset::PairType::ConstantProduct, + PoolType::ConstantProduct, Some("uluna-uusd".to_string()), vec![coin(1000, "uusd")], |result| { @@ -1129,6 +1130,7 @@ mod router { None, None, None, + None, vec![ Coin { denom: "uwhale".to_string(), @@ -1153,6 +1155,7 @@ mod router { None, None, None, + None, vec![ Coin { denom: "uluna".to_string(), @@ -1171,7 +1174,7 @@ mod router { ); // Prepare the swap operations, we want to go from WHALE -> UUSD - // We will use the uluna-uusd pair as the intermediary pool + // We will use the uluna-uusd pool as the intermediary pool let swap_operations = vec![ white_whale_std::pool_manager::SwapOperation::WhaleSwap { @@ -1187,7 +1190,7 @@ mod router { ]; // before swap uusd balance = 1_000_000_000 - // - 2*1_000 pair creation fee + // - 2*1_000 pool creation fee // - 1_000_000 liquidity provision // = 998_998_000 let pre_swap_amount = 998_998_000; @@ -1226,8 +1229,8 @@ mod router { let _other = suite.senders[1].clone(); let _unauthorized = suite.senders[2].clone(); - let first_pair = vec!["uwhale".to_string(), "uluna".to_string()]; - let second_pair = vec!["uluna".to_string(), "uusd".to_string()]; + let first_pool = vec!["uwhale".to_string(), "uluna".to_string()]; + let second_pool = vec!["uluna".to_string(), "uusd".to_string()]; #[cfg(not(feature = "osmosis"))] let pool_fees = PoolFee { @@ -1259,27 +1262,27 @@ mod router { extra_fees: vec![], }; - // Create a pair + // Create a pool suite .instantiate_default() - .create_pair( + .create_pool( creator.clone(), - first_pair, + first_pool, vec![6u8, 6u8], pool_fees.clone(), - white_whale_std::pool_network::asset::PairType::ConstantProduct, + PoolType::ConstantProduct, Some("whale-uluna".to_string()), vec![coin(1000, "uusd")], |result| { result.unwrap(); }, ) - .create_pair( + .create_pool( creator.clone(), - second_pair, + second_pool, vec![6u8, 6u8], pool_fees, - white_whale_std::pool_network::asset::PairType::ConstantProduct, + PoolType::ConstantProduct, Some("uluna-uusd".to_string()), vec![coin(1000, "uusd")], |result| { @@ -1292,6 +1295,7 @@ mod router { None, None, None, + None, vec![ Coin { denom: "uwhale".to_string(), @@ -1312,6 +1316,7 @@ mod router { None, None, None, + None, vec![ Coin { denom: "uluna".to_string(), @@ -1390,8 +1395,8 @@ mod router { let other = suite.senders[1].clone(); let unauthorized = suite.senders[2].clone(); - let first_pair = vec!["uwhale".to_string(), "uluna".to_string()]; - let second_pair = vec!["uluna".to_string(), "uusd".to_string()]; + let first_pool = vec!["uwhale".to_string(), "uluna".to_string()]; + let second_pool = vec!["uluna".to_string(), "uusd".to_string()]; #[cfg(not(feature = "osmosis"))] let pool_fees = PoolFee { @@ -1423,27 +1428,27 @@ mod router { extra_fees: vec![], }; - // Create a pair + // Create a pool suite .instantiate_default() - .create_pair( + .create_pool( creator.clone(), - first_pair, + first_pool, vec![6u8, 6u8], pool_fees.clone(), - white_whale_std::pool_network::asset::PairType::ConstantProduct, + PoolType::ConstantProduct, Some("whale-uluna".to_string()), vec![coin(1000, "uusd")], |result| { result.unwrap(); }, ) - .create_pair( + .create_pool( creator.clone(), - second_pair, + second_pool, vec![6u8, 6u8], pool_fees, - white_whale_std::pool_network::asset::PairType::ConstantProduct, + PoolType::ConstantProduct, Some("uluna-uusd".to_string()), vec![coin(1000, "uusd")], |result| { @@ -1456,6 +1461,7 @@ mod router { None, None, None, + None, vec![ Coin { denom: "uwhale".to_string(), @@ -1476,6 +1482,7 @@ mod router { None, None, None, + None, vec![ Coin { denom: "uluna".to_string(), @@ -1586,8 +1593,8 @@ mod router { let _unauthorized = suite.senders[2].clone(); // Asset infos with uwhale and uluna - let first_pair = vec!["uwhale".to_string(), "uluna".to_string()]; - let second_pair = vec!["uluna".to_string(), "uusd".to_string()]; + let first_pool = vec!["uwhale".to_string(), "uluna".to_string()]; + let second_pool = vec!["uluna".to_string(), "uusd".to_string()]; #[cfg(not(feature = "osmosis"))] let pool_fees = PoolFee { @@ -1619,27 +1626,27 @@ mod router { extra_fees: vec![], }; - // Create a pair + // Create a pool suite .instantiate_default() - .create_pair( + .create_pool( creator.clone(), - first_pair, + first_pool, vec![6u8, 6u8], pool_fees.clone(), - white_whale_std::pool_network::asset::PairType::ConstantProduct, + PoolType::ConstantProduct, Some("whale-uluna".to_string()), vec![coin(1_000, "uusd")], |result| { result.unwrap(); }, ) - .create_pair( + .create_pool( creator.clone(), - second_pair, + second_pool, vec![6u8, 6u8], pool_fees, - white_whale_std::pool_network::asset::PairType::ConstantProduct, + PoolType::ConstantProduct, Some("uluna-uusd".to_string()), vec![coin(1_000, "uusd")], |result| { @@ -1654,6 +1661,7 @@ mod router { None, None, None, + None, vec![ Coin { denom: "uwhale".to_string(), @@ -1678,6 +1686,7 @@ mod router { None, None, None, + None, vec![ Coin { denom: "uluna".to_string(), @@ -1696,7 +1705,7 @@ mod router { ); // Prepare the swap operations, we want to go from WHALE -> UUSD - // We will use the uluna-uusd pair as the intermediary pool + // We will use the uluna-uusd pool as the intermediary pool let swap_operations = vec![ white_whale_std::pool_manager::SwapOperation::WhaleSwap { @@ -1789,6 +1798,8 @@ mod swapping { use cosmwasm_std::assert_approx_eq; + use white_whale_std::pool_manager::PoolType; + use super::*; #[test] @@ -1805,7 +1816,6 @@ mod swapping { let asset_infos = vec!["uwhale".to_string(), "uluna".to_string()]; - // Default Pool fees white_whale_std::pool_network::pair::PoolFee // Protocol fee is 0.01% and swap fee is 0.02% and burn fee is 0% #[cfg(not(feature = "osmosis"))] let pool_fees = PoolFee { @@ -1838,13 +1848,13 @@ mod swapping { extra_fees: vec![], }; - // Create a pair - suite.instantiate_default().create_pair( + // Create a pool + suite.instantiate_default().create_pool( creator.clone(), asset_infos, vec![6u8, 6u8], pool_fees, - white_whale_std::pool_network::asset::PairType::ConstantProduct, + PoolType::ConstantProduct, Some("whale-uluna".to_string()), vec![coin(1000, "uusd")], |result| { @@ -1852,9 +1862,9 @@ mod swapping { }, ); - // Query pair info to ensure the query is working fine - suite.query_pair_info("whale-uluna".to_string(), |result| { - assert_eq!(result.unwrap().pair_info.asset_decimals, vec![6u8, 6u8]); + // Query pool info to ensure the query is working fine + suite.query_pool_info("whale-uluna".to_string(), |result| { + assert_eq!(result.unwrap().pool_info.asset_decimals, vec![6u8, 6u8]); }); // Lets try to add liquidity @@ -1865,6 +1875,7 @@ mod swapping { None, None, None, + None, vec![ Coin { denom: "uwhale".to_string(), @@ -1887,13 +1898,13 @@ mod swapping { })); }, ) - .query_pair_info("whale-uluna".to_string(), |result| { + .query_pool_info("whale-uluna".to_string(), |result| { let response = result.unwrap(); assert_eq!( response.total_share, Coin { - denom: response.pair_info.lp_denom, - amount: Uint128::from(1_000_000u128) + denom: response.pool_info.lp_denom, + amount: Uint128::from(1_000_000u128), } ); }); @@ -1905,6 +1916,7 @@ mod swapping { denom: "uwhale".to_string(), amount: Uint128::from(1000u128), }, + "uluna".to_string(), |result| { // Ensure that the return amount is 1_000 minus spread assert_eq!( @@ -1918,10 +1930,6 @@ mod swapping { // Now lets try a swap suite.swap( creator.clone(), - Coin { - denom: "uwhale".to_string(), - amount: Uint128::from(1000u128), - }, "uluna".to_string(), None, None, @@ -1965,11 +1973,11 @@ mod swapping { let simulated_offer_amount = RefCell::new(Uint128::zero()); suite.query_reverse_simulation( "whale-uluna".to_string(), - "uluna".to_string(), Coin { denom: "uwhale".to_string(), amount: Uint128::from(1000u128), }, + "uluna".to_string(), |result| { *simulated_offer_amount.borrow_mut() = result.unwrap().offer_amount; }, @@ -1978,10 +1986,6 @@ mod swapping { // Now lets try a swap suite.swap( creator.clone(), - Coin { - denom: "uluna".to_string(), - amount: Uint128::from(simulated_offer_amount.borrow().u128()), - }, "uwhale".to_string(), None, None, @@ -2033,7 +2037,6 @@ mod swapping { let asset_infos = vec!["uwhale".to_string(), "uluna".to_string()]; - // Default Pool fees white_whale_std::pool_network::pair::PoolFee // Protocol fee is 0.01% and swap fee is 0.02% and burn fee is 0% #[cfg(not(feature = "osmosis"))] let pool_fees = PoolFee { @@ -2066,13 +2069,13 @@ mod swapping { extra_fees: vec![], }; - // Create a pair - suite.instantiate_default().create_pair( + // Create a pool + suite.instantiate_default().create_pool( creator.clone(), asset_infos, vec![6u8, 6u8], pool_fees, - white_whale_std::pool_network::asset::PairType::StableSwap { amp: 100 }, + PoolType::StableSwap { amp: 100 }, Some("whale-uluna".to_string()), vec![coin(1000, "uusd")], |result| { @@ -2087,6 +2090,7 @@ mod swapping { None, None, None, + None, vec![ Coin { denom: "uwhale".to_string(), @@ -2111,6 +2115,7 @@ mod swapping { denom: "uwhale".to_string(), amount: Uint128::from(1000u128), }, + "uluna".to_string(), |result| { *simulated_return_amount.borrow_mut() = result.unwrap().return_amount; }, @@ -2119,10 +2124,6 @@ mod swapping { // Now lets try a swap suite.swap( creator.clone(), - Coin { - denom: "uwhale".to_string(), - amount: Uint128::from(1000u128), - }, "uluna".to_string(), None, None, @@ -2166,11 +2167,11 @@ mod swapping { let simulated_offer_amount = RefCell::new(Uint128::zero()); suite.query_reverse_simulation( "whale-uluna".to_string(), - "uluna".to_string(), Coin { denom: "uwhale".to_string(), amount: Uint128::from(1000u128), }, + "uluna".to_string(), |result| { *simulated_offer_amount.borrow_mut() = result.unwrap().offer_amount; }, @@ -2179,10 +2180,6 @@ mod swapping { // Now lets try a swap suite.swap( creator.clone(), - Coin { - denom: "uluna".to_string(), - amount: Uint128::from(simulated_offer_amount.borrow().u128()), - }, "uwhale".to_string(), None, None, @@ -2234,7 +2231,6 @@ mod swapping { let asset_infos = vec!["uwhale".to_string(), "uluna".to_string()]; - // Default Pool fees white_whale_std::pool_network::pair::PoolFee // Protocol fee is 0.001% and swap fee is 0.002% and burn fee is 0% #[cfg(not(feature = "osmosis"))] let pool_fees = PoolFee { @@ -2267,13 +2263,13 @@ mod swapping { extra_fees: vec![], }; - // Create a pair - suite.instantiate_default().create_pair( + // Create a pool + suite.instantiate_default().create_pool( creator.clone(), asset_infos, vec![6u8, 6u8], pool_fees, - white_whale_std::pool_network::asset::PairType::ConstantProduct, + PoolType::ConstantProduct, Some("whale-uluna".to_string()), vec![coin(1000, "uusd")], |result| { @@ -2288,6 +2284,7 @@ mod swapping { None, None, None, + None, vec![ Coin { denom: "uwhale".to_string(), @@ -2321,10 +2318,6 @@ mod swapping { // Total -> 9,900,693 (Returned Amount) + 99,010 (Spread)(0.009x%) + 198 (Swap Fee) + 99 (Protocol Fee) = 10,000,000 uLUNA suite.swap( creator.clone(), - Coin { - denom: "uwhale".to_string(), - amount: Uint128::from(10000000u128), - }, "uluna".to_string(), None, Some(Decimal::percent(1)), @@ -2510,6 +2503,7 @@ mod locking_lp { use white_whale_std::fee::{Fee, PoolFee}; use white_whale_std::incentive_manager::Position; + use white_whale_std::pool_manager::PoolType; use white_whale_std::pool_network::asset::MINIMUM_LIQUIDITY_AMOUNT; use crate::tests::suite::TestingSuite; @@ -2528,7 +2522,6 @@ mod locking_lp { // Asset denoms with uwhale and uluna let asset_denoms = vec!["uwhale".to_string(), "uluna".to_string()]; - // Default Pool fees white_whale_std::pool_network::pair::PoolFee #[cfg(not(feature = "osmosis"))] let pool_fees = PoolFee { protocol_fee: Fee { @@ -2560,13 +2553,13 @@ mod locking_lp { extra_fees: vec![], }; - // Create a pair - suite.instantiate_default().create_pair( + // Create a pool + suite.instantiate_default().create_pool( creator.clone(), asset_denoms, vec![6u8, 6u8], pool_fees, - white_whale_std::pool_network::asset::PairType::ConstantProduct, + PoolType::ConstantProduct, Some("whale-uluna".to_string()), vec![coin(1000, "uusd")], |result| { @@ -2586,6 +2579,7 @@ mod locking_lp { Some(86_400u64), None, None, + None, vec![ Coin { denom: "uwhale".to_string(), @@ -2652,6 +2646,7 @@ mod locking_lp { Some(200_000u64), None, None, + None, vec![ Coin { denom: "uwhale".to_string(), @@ -2717,7 +2712,6 @@ mod locking_lp { // Asset denoms with uwhale and uluna let asset_denoms = vec!["uwhale".to_string(), "uluna".to_string()]; - // Default Pool fees white_whale_std::pool_network::pair::PoolFee #[cfg(not(feature = "osmosis"))] let pool_fees = PoolFee { protocol_fee: Fee { @@ -2749,13 +2743,13 @@ mod locking_lp { extra_fees: vec![], }; - // Create a pair - suite.instantiate_default().create_pair( + // Create a pool + suite.instantiate_default().create_pool( creator.clone(), asset_denoms, vec![6u8, 6u8], pool_fees, - white_whale_std::pool_network::asset::PairType::ConstantProduct, + PoolType::ConstantProduct, Some("whale-uluna".to_string()), vec![coin(1000, "uusd")], |result| { @@ -2775,6 +2769,7 @@ mod locking_lp { Some(86_400u64), Some("incentive_identifier".to_string()), None, + None, vec![ Coin { denom: "uwhale".to_string(), @@ -2841,6 +2836,7 @@ mod locking_lp { Some(200_000u64), Some("incentive_identifier".to_string()), None, + None, vec![ Coin { denom: "uwhale".to_string(), @@ -2890,6 +2886,7 @@ mod provide_liquidity { use cosmwasm_std::{coin, Coin, Decimal, StdError, Uint128}; use white_whale_std::fee::{Fee, PoolFee}; + use white_whale_std::pool_manager::PoolType; use white_whale_std::pool_network::asset::MINIMUM_LIQUIDITY_AMOUNT; use crate::tests::suite::TestingSuite; @@ -2910,7 +2907,6 @@ mod provide_liquidity { // Asset denoms with uwhale and uluna let asset_denoms = vec!["uwhale".to_string(), "uluna".to_string()]; - // Default Pool fees white_whale_std::pool_network::pair::PoolFee #[cfg(not(feature = "osmosis"))] let pool_fees = PoolFee { protocol_fee: Fee { @@ -2942,13 +2938,13 @@ mod provide_liquidity { extra_fees: vec![], }; - // Create a pair - suite.instantiate_default().create_pair( + // Create a pool + suite.instantiate_default().create_pool( creator.clone(), asset_denoms, vec![6u8, 6u8], pool_fees, - white_whale_std::pool_network::asset::PairType::ConstantProduct, + PoolType::ConstantProduct, Some("whale-uluna".to_string()), vec![coin(1000, "uusd")], |result| { @@ -2965,7 +2961,7 @@ mod provide_liquidity { creator.clone(), "whale-uluna".to_string(), None, - None, + None, None, None, vec![], |result| { @@ -2981,7 +2977,7 @@ mod provide_liquidity { creator.clone(), "whale-uluna".to_string(), None, - None, + None, None, None, vec![Coin { denom: "uosmo".to_string(), @@ -2991,7 +2987,7 @@ mod provide_liquidity { let err = result.unwrap_err().downcast::().unwrap(); match err { - ContractError::AssetMismatch { .. } => {} + ContractError::AssetMismatch => {} _ => panic!("Wrong error type, should return ContractError::AssetMismatch"), } }, @@ -3000,7 +2996,7 @@ mod provide_liquidity { creator.clone(), "whale-uluna".to_string(), None, - None, + None, None, None, vec![Coin { denom: "uwhale".to_string(), @@ -3026,6 +3022,7 @@ mod provide_liquidity { None, None, None, + None, vec![ Coin { denom: "uosmo".to_string(), @@ -3040,7 +3037,7 @@ mod provide_liquidity { let err = result.unwrap_err().downcast::().unwrap(); match err { - ContractError::AssetMismatch {} => {} + ContractError::AssetMismatch => {} _ => panic!("Wrong error type, should return ContractError::AssetMismatch"), } }, @@ -3051,6 +3048,7 @@ mod provide_liquidity { None, None, None, + None, vec![ Coin { denom: "uwhale".to_string(), @@ -3089,6 +3087,7 @@ mod provide_liquidity { None, None, None, + None, vec![ Coin { denom: "uwhale".to_string(), @@ -3114,6 +3113,7 @@ mod provide_liquidity { None, None, Some(Decimal::percent(50)), + None, vec![ Coin { denom: "uwhale".to_string(), @@ -3148,17 +3148,17 @@ mod provide_liquidity { // 1_000 to the contract, and 1_000_000 to the second, single-side LP assert_eq!(res.unwrap().amount, Uint128::from(2_000_000u128)); }) - .query_pair_info("whale-uluna".to_string(), |res| { + .query_pool_info("whale-uluna".to_string(), |res| { let response = res.unwrap(); let whale = response - .pair_info + .pool_info .assets .iter() .find(|coin| coin.denom == "uwhale".to_string()) .unwrap(); let luna = response - .pair_info + .pool_info .assets .iter() .find(|coin| coin.denom == "uluna".to_string()) @@ -3356,7 +3356,6 @@ mod provide_liquidity { // Asset denoms with uwhale and uluna let asset_denoms = vec!["uwhale".to_string(), "uluna".to_string()]; - // Default Pool fees white_whale_std::pool_network::pair::PoolFee #[cfg(not(feature = "osmosis"))] let pool_fees = PoolFee { protocol_fee: Fee { @@ -3388,13 +3387,13 @@ mod provide_liquidity { extra_fees: vec![], }; - // Create a pair - suite.instantiate_default().create_pair( + // Create a pool + suite.instantiate_default().create_pool( creator.clone(), asset_denoms, vec![6u8, 6u8], pool_fees, - white_whale_std::pool_network::asset::PairType::ConstantProduct, + PoolType::ConstantProduct, Some("whale-uluna".to_string()), vec![coin(1000, "uusd")], |result| { @@ -3412,6 +3411,7 @@ mod provide_liquidity { None, None, None, + None, vec![ Coin { denom: "uwhale".to_string(), @@ -3439,6 +3439,7 @@ mod provide_liquidity { None, None, Some(Decimal::percent(50)), + None, vec![Coin { denom: "uwhale".to_string(), amount: Uint128::from(1_760u128), @@ -3457,6 +3458,7 @@ mod provide_liquidity { None, None, Some(Decimal::percent(50)), + None, vec![Coin { denom: "uwhale".to_string(), amount: Uint128::from(10_000u128), @@ -3475,6 +3477,7 @@ mod provide_liquidity { None, None, Some(Decimal::percent(50)), + None, vec![Coin { denom: "uwhale".to_string(), amount: Uint128::from(1_000u128), @@ -3485,3 +3488,673 @@ mod provide_liquidity { ); } } + +mod multiple_pools { + use cosmwasm_std::{coin, Coin, Decimal, Uint128}; + + use white_whale_std::fee::{Fee, PoolFee}; + use white_whale_std::pool_manager::{PoolInfo, PoolType}; + + use crate::tests::suite::TestingSuite; + use crate::ContractError; + + #[cfg(not(feature = "osmosis"))] + #[test] + fn provide_liquidity_to_multiple_pools_check_fees() { + let mut suite = TestingSuite::default_with_balances(vec![ + coin(1_000_000_000u128, "uwhale".to_string()), + coin(1_000_000_000u128, "uluna".to_string()), + coin(1_000_000_000u128, "uosmo".to_string()), + coin(1_000_000_000u128, "uusd".to_string()), + ]); + let creator = suite.creator(); + let other = suite.senders[1].clone(); + + // Asset denoms with uwhale and uluna + let asset_denoms_1 = vec!["uwhale".to_string(), "uluna".to_string()]; + let asset_denoms_2 = vec!["uluna".to_string(), "uusd".to_string()]; + + #[cfg(not(feature = "osmosis"))] + let pool_fees_1 = PoolFee { + protocol_fee: Fee { + share: Decimal::percent(10), + }, + swap_fee: Fee { + share: Decimal::percent(7), + }, + burn_fee: Fee { + share: Decimal::percent(3), + }, + extra_fees: vec![], + }; + + #[cfg(not(feature = "osmosis"))] + let pool_fees_2 = PoolFee { + protocol_fee: Fee { + share: Decimal::zero(), + }, + swap_fee: Fee { + share: Decimal::percent(15), + }, + burn_fee: Fee { + share: Decimal::percent(5), + }, + extra_fees: vec![], + }; + + // Create pools + suite + .instantiate_default() + .create_pool( + creator.clone(), + asset_denoms_1.clone(), + vec![6u8, 6u8], + pool_fees_1.clone(), + PoolType::ConstantProduct, + Some("whale-uluna-pool-1".to_string()), + vec![coin(1000, "uusd")], + |result| { + result.unwrap(); + }, + ) + .create_pool( + creator.clone(), + asset_denoms_1, + vec![6u8, 6u8], + pool_fees_2.clone(), + PoolType::ConstantProduct, + Some("whale-uluna-pool-2".to_string()), + vec![coin(1000, "uusd")], + |result| { + result.unwrap(); + }, + ) + .create_pool( + creator.clone(), + asset_denoms_2, + vec![6u8, 6u8], + pool_fees_1.clone(), + PoolType::ConstantProduct, + Some("uluna-uusd-pool-1".to_string()), + vec![coin(1000, "uusd")], + |result| { + result.unwrap(); + }, + ); + + let pool_manager_addr = suite.pool_manager_addr.clone(); + let bonding_manager_addr = suite.bonding_manager_addr.clone(); + + // let's provide liquidity with two assets + suite + .provide_liquidity( + creator.clone(), + "whale-uluna".to_string(), + None, + None, + None, + None, + vec![ + Coin { + denom: "uwhale".to_string(), + amount: Uint128::from(1_000_000u128), + }, + Coin { + denom: "uluna".to_string(), + amount: Uint128::from(1_000_000u128), + }, + ], + |result| { + let err = result.unwrap_err().downcast::().unwrap(); + match err { + ContractError::UnExistingPool => {} + _ => panic!("Wrong error type, should return ContractError::UnExistingPool"), + } + }, + ) + .provide_liquidity( + creator.clone(), + "whale-uluna-pool-1".to_string(), + None, + None, + None, + None, + vec![ + Coin { + denom: "uwhale".to_string(), + amount: Uint128::from(1_000_000u128), + }, + Coin { + denom: "uluna".to_string(), + amount: Uint128::from(1_000_000u128), + }, + ], + |result| { + result.unwrap(); + }, + ) + .provide_liquidity( + other.clone(), + "whale-uluna-pool-2".to_string(), + None, + None, + None, + None, + vec![ + Coin { + denom: "uwhale".to_string(), + amount: Uint128::from(1_000_000u128), + }, + Coin { + denom: "uluna".to_string(), + amount: Uint128::from(1_000_000u128), + }, + ], + |result| { + result.unwrap(); + }, + ) + .provide_liquidity( + other.clone(), + "uluna-uusd-pool-1".to_string(), + None, + None, + None, + None, + vec![ + Coin { + denom: "uluna".to_string(), + amount: Uint128::from(1_000_000u128), + }, + Coin { + denom: "uusd".to_string(), + amount: Uint128::from(1_000_000u128), + }, + ], + |result| { + result.unwrap(); + }, + ) + .query_all_balances(pool_manager_addr.to_string(), |result| { + let balances = result.unwrap(); + assert_eq!( + balances, + vec![ + Coin { + denom: "factory/migaloo1zwv6feuzhy6a9wekh96cd57lsarmqlwxdypdsplw6zhfncqw6ftqqhavvl/uluna-uusd.pool.uluna-uusd-pool-1.uLP".to_string(), + amount: Uint128::from(1_000u128), + }, + Coin { + denom: "factory/migaloo1zwv6feuzhy6a9wekh96cd57lsarmqlwxdypdsplw6zhfncqw6ftqqhavvl/uwhale-uluna.pool.whale-uluna-pool-1.uLP".to_string(), + amount: Uint128::from(1_000u128), + }, + Coin { + denom: "factory/migaloo1zwv6feuzhy6a9wekh96cd57lsarmqlwxdypdsplw6zhfncqw6ftqqhavvl/uwhale-uluna.pool.whale-uluna-pool-2.uLP".to_string(), + amount: Uint128::from(1_000u128), + }, + Coin { + denom: "uluna".to_string(), + amount: Uint128::from(3_000_000u128), + }, + Coin { + denom: "uusd".to_string(), + amount: Uint128::from(1_000_000u128), + }, + Coin { + denom: "uwhale".to_string(), + amount: Uint128::from(2_000_000u128), + }, + ] + ); + }); + + // let's do swaps in whale-uluna-pool-1 and verify the fees are channeled correctly + suite + .swap( + creator.clone(), + "uluna".to_string(), + None, + None, + None, + "whale-uluna-pool-1".to_string(), + vec![coin(1000u128, "uwhale".to_string())], + |result| { + result.unwrap(); + }, + ) + .query_pool_info("whale-uluna-pool-1".to_string(), |result| { + let response = result.unwrap(); + let pool_info = response.pool_info; + + // swapped 1000 uwhale + // fees: + // swap -> 69 (~7%) + // protocol -> 99 (~10%) + // burn -> 29 (~3%) + // total_fees = 197, of which 69 stay in the pool (for LPs). + // Going out of the pool is 99 (bonding manager) + 29 (burned) + + assert_eq!(pool_info, PoolInfo { + asset_denoms: vec!["uwhale".to_string(), "uluna".to_string()], + lp_denom: "factory/migaloo1zwv6feuzhy6a9wekh96cd57lsarmqlwxdypdsplw6zhfncqw6ftqqhavvl/uwhale-uluna.pool.whale-uluna-pool-1.uLP".to_string(), + asset_decimals: vec![6u8, 6u8], + assets: vec![coin(1001000, "uwhale"), coin(999070, "uluna")], + pool_type: PoolType::ConstantProduct, + pool_fees: pool_fees_1.clone(), + }); + }) + ; + + // verify the fees went to the bonding manager + suite.query_balance( + bonding_manager_addr.clone().to_string(), + "uluna", + |result| { + assert_eq!(result.unwrap(), coin(99, "uluna")); + }, + ) + .swap( + creator.clone(), + "uwhale".to_string(), + None, + None, + None, + "whale-uluna-pool-1".to_string(), + vec![coin(2_000u128, "uluna".to_string())], + |result| { + result.unwrap(); + }, + ) + .query_pool_info("whale-uluna-pool-1".to_string(), |result| { + let response = result.unwrap(); + let pool_info = response.pool_info; + + // swapped 2000 uluna + // fees: + // swap -> 139 (~7%) + // protocol -> 199 (~10%) + // burn -> 59 (~3%) + // total_fees = 397, of which 139 stay in the pool (for LPs). + // Going out of the pool is 199 (bonding manager) + 59 (burned) + + assert_eq!(pool_info, PoolInfo { + asset_denoms: vec!["uwhale".to_string(), "uluna".to_string()], + lp_denom: "factory/migaloo1zwv6feuzhy6a9wekh96cd57lsarmqlwxdypdsplw6zhfncqw6ftqqhavvl/uwhale-uluna.pool.whale-uluna-pool-1.uLP".to_string(), + asset_decimals: vec![6u8, 6u8], + assets: vec![coin(999_140, "uwhale"), coin(1_001_070, "uluna")], + pool_type: PoolType::ConstantProduct, + pool_fees: pool_fees_1.clone(), + }); + }) + ; + + suite + .query_balance( + bonding_manager_addr.clone().to_string(), + "uwhale", + |result| { + assert_eq!(result.unwrap(), coin(199, "uwhale")); + }, + ) + .query_balance( + bonding_manager_addr.clone().to_string(), + "uluna", + |result| { + assert_eq!(result.unwrap(), coin(99, "uluna")); + }, + ); + + // let's do swaps in whale-uluna-pool-2 and verify the fees are channeled correctly + suite + .swap( + creator.clone(), + "uluna".to_string(), + None, + None, + None, + "whale-uluna-pool-2".to_string(), + vec![coin(1000u128, "uwhale".to_string())], + |result| { + result.unwrap(); + }, + ) + .query_pool_info("whale-uluna-pool-2".to_string(), |result| { + let response = result.unwrap(); + let pool_info = response.pool_info; + + // swapped 1000 uwhale + // fees: + // swap -> 149 (~15%) + // protocol -> 0 (0%) + // burn -> 49 (~5%) + // total_fees = 198, of which 149 stay in the pool (for LPs). + // Going out of the pool is 49 (burned) + + assert_eq!(pool_info, PoolInfo { + asset_denoms: vec!["uwhale".to_string(), "uluna".to_string()], + lp_denom: "factory/migaloo1zwv6feuzhy6a9wekh96cd57lsarmqlwxdypdsplw6zhfncqw6ftqqhavvl/uwhale-uluna.pool.whale-uluna-pool-2.uLP".to_string(), + asset_decimals: vec![6u8, 6u8], + assets: vec![coin(1001000, "uwhale"), coin(999_150, "uluna")], + pool_type: PoolType::ConstantProduct, + pool_fees: pool_fees_2.clone(), + }); + }) + ; + + suite + .swap( + creator.clone(), + "uwhale".to_string(), + None, + None, + None, + "whale-uluna-pool-2".to_string(), + vec![coin(2_000u128, "uluna".to_string())], + |result| { + result.unwrap(); + }, + ) + .query_pool_info("whale-uluna-pool-2".to_string(), |result| { + let response = result.unwrap(); + let pool_info = response.pool_info; + + // swapped 2000 uluna + // fees: + // swap -> 299 (~15%) + // protocol -> 0 (0%) + // burn -> 99 (~5%) + // total_fees = 398, of which 299 stay in the pool (for LPs). + // Going out of the pool is 99 (burned) + + assert_eq!(pool_info, PoolInfo { + asset_denoms: vec!["uwhale".to_string(), "uluna".to_string()], + lp_denom: "factory/migaloo1zwv6feuzhy6a9wekh96cd57lsarmqlwxdypdsplw6zhfncqw6ftqqhavvl/uwhale-uluna.pool.whale-uluna-pool-2.uLP".to_string(), + asset_decimals: vec![6u8, 6u8], + assets: vec![coin(999_300, "uwhale"), coin(1_001_150, "uluna")], + pool_type: PoolType::ConstantProduct, + pool_fees: pool_fees_2.clone(), + }); + }); + + suite + .query_balance( + bonding_manager_addr.clone().to_string(), + "uwhale", + |result| { + // no additional funds were sent to the bonding manager + assert_eq!(result.unwrap(), coin(199, "uwhale")); + }, + ) + .query_balance( + bonding_manager_addr.clone().to_string(), + "uluna", + |result| { + // no additional funds were sent to the bonding manager + assert_eq!(result.unwrap(), coin(99, "uluna")); + }, + ); + + // let's do swaps in uluna-uusd-pool-1 and verify the fees are channeled correctly + suite + .swap( + creator.clone(), + "uusd".to_string(), + None, + None, + None, + "uluna-uusd-pool-1".to_string(), + vec![coin(3000u128, "uluna".to_string())], + |result| { + result.unwrap(); + }, + ) + .query_pool_info("uluna-uusd-pool-1".to_string(), |result| { + let response = result.unwrap(); + let pool_info = response.pool_info; + + // swapped 3000 uluna + // fees: + // swap -> 209 (~7%) + // protocol -> 299 (~10%) + // burn -> 89 (~3%) + // total_fees = 597, of which 209 stay in the pool (for LPs). + // Going out of the pool is 299 (bonding manager) + 89 (burned) + + assert_eq!(pool_info, PoolInfo { + asset_denoms: vec!["uluna".to_string(), "uusd".to_string()], + lp_denom: "factory/migaloo1zwv6feuzhy6a9wekh96cd57lsarmqlwxdypdsplw6zhfncqw6ftqqhavvl/uluna-uusd.pool.uluna-uusd-pool-1.uLP".to_string(), + asset_decimals: vec![6u8, 6u8], + assets: vec![coin(1003000, "uluna"), coin(997_218, "uusd")], + pool_type: PoolType::ConstantProduct, + pool_fees: pool_fees_1.clone(), + }); + }) + ; + + suite.query_balance(bonding_manager_addr.clone().to_string(), "uusd", |result| { + // 3000 of pool creation fees + 299 from the previous swap + assert_eq!(result.unwrap(), coin(3299, "uusd")); + }); + + suite + .swap( + creator.clone(), + "uluna".to_string(), + None, + None, + None, + "uluna-uusd-pool-1".to_string(), + vec![coin(1_500u128, "uusd".to_string())], + |result| { + result.unwrap(); + }, + ) + .query_pool_info("uluna-uusd-pool-1".to_string(), |result| { + let response = result.unwrap(); + let pool_info = response.pool_info; + + // swapped 1500 uusd + // fees: + // swap -> 105 (~7%) + // protocol -> 150 (~10%) + // burn -> 45 (~3%) + // total_fees = 300, of which 105 stay in the pool (for LPs). + // Going out of the pool is 150 (bonding manager) + 45 (burned) + + assert_eq!(pool_info, PoolInfo { + asset_denoms: vec!["uluna".to_string(), "uusd".to_string()], + lp_denom: "factory/migaloo1zwv6feuzhy6a9wekh96cd57lsarmqlwxdypdsplw6zhfncqw6ftqqhavvl/uluna-uusd.pool.uluna-uusd-pool-1.uLP".to_string(), + asset_decimals: vec![6u8, 6u8], + assets: vec![coin(1_001_599, "uluna"), coin(998_718, "uusd")], + pool_type: PoolType::ConstantProduct, + pool_fees: pool_fees_1.clone(), + }); + }) + ; + + suite + .query_balance( + bonding_manager_addr.clone().to_string(), + "uwhale", + |result| { + // no additional funds were sent to the bonding manager + assert_eq!(result.unwrap(), coin(199, "uwhale")); + }, + ) + .query_balance( + bonding_manager_addr.clone().to_string(), + "uluna", + |result| { + // 99 + 150 + assert_eq!(result.unwrap(), coin(249, "uluna")); + }, + ).query_balance( + bonding_manager_addr.clone().to_string(), + "uusd", + |result| { + // 99 + 150 + assert_eq!(result.unwrap(), coin(3299, "uusd")); + }, + ) + .query_all_balances( + pool_manager_addr.clone().to_string(), + |result| { + let balances = result.unwrap(); + assert_eq!(balances, vec![ + Coin { + denom: "factory/migaloo1zwv6feuzhy6a9wekh96cd57lsarmqlwxdypdsplw6zhfncqw6ftqqhavvl/uluna-uusd.pool.uluna-uusd-pool-1.uLP".to_string(), + amount: Uint128::from(1_000u128), + }, + Coin { + denom: "factory/migaloo1zwv6feuzhy6a9wekh96cd57lsarmqlwxdypdsplw6zhfncqw6ftqqhavvl/uwhale-uluna.pool.whale-uluna-pool-1.uLP".to_string(), + amount: Uint128::from(1_000u128), + }, + Coin { + denom: "factory/migaloo1zwv6feuzhy6a9wekh96cd57lsarmqlwxdypdsplw6zhfncqw6ftqqhavvl/uwhale-uluna.pool.whale-uluna-pool-2.uLP".to_string(), + amount: Uint128::from(1_000u128), + }, + Coin { + denom: "uluna".to_string(), + amount: Uint128::from(3_003_819u128), + }, + Coin { + denom: "uusd".to_string(), + amount: Uint128::from(998_718u128), + }, + Coin { + denom: "uwhale".to_string(), + amount: Uint128::from(1_998_440u128), + }, + ]); + }, + ); + + // swap via the router now + let swap_operations = vec![ + white_whale_std::pool_manager::SwapOperation::WhaleSwap { + token_in_denom: "uwhale".to_string(), + token_out_denom: "uluna".to_string(), + pool_identifier: "whale-uluna-pool-2".to_string(), + }, + white_whale_std::pool_manager::SwapOperation::WhaleSwap { + token_in_denom: "uluna".to_string(), + token_out_denom: "uusd".to_string(), + pool_identifier: "uluna-uusd-pool-1".to_string(), + }, + ]; + + suite.execute_swap_operations( + creator.clone(), + swap_operations, + None, + None, + None, + vec![coin(5_000u128, "uwhale".to_string())], + |result| { + result.unwrap(); + }, + ).query_pool_info("whale-uluna-pool-1".to_string(), |result| { + let response = result.unwrap(); + let pool_info = response.pool_info; + + // this should have not changed since last time, since we didn't touch this pool + assert_eq!(pool_info, PoolInfo { + asset_denoms: vec!["uwhale".to_string(), "uluna".to_string()], + lp_denom: "factory/migaloo1zwv6feuzhy6a9wekh96cd57lsarmqlwxdypdsplw6zhfncqw6ftqqhavvl/uwhale-uluna.pool.whale-uluna-pool-1.uLP".to_string(), + asset_decimals: vec![6u8, 6u8], + assets: vec![coin(999_140, "uwhale"), coin(1_001_070, "uluna")], + pool_type: PoolType::ConstantProduct, + pool_fees: pool_fees_1.clone(), + }); + }) + .query_pool_info("whale-uluna-pool-2".to_string(), |result| { + let response = result.unwrap(); + let pool_info = response.pool_info; + + // the swap above was: + // SwapComputation { return_amount: Uint128(3988), + // spread_amount: Uint128(25), swap_fee_amount: Uint128(747), + // protocol_fee_amount: Uint128(0), burn_fee_amount: Uint128(249) } + + assert_eq!(pool_info, PoolInfo { + asset_denoms: vec!["uwhale".to_string(), "uluna".to_string()], + lp_denom: "factory/migaloo1zwv6feuzhy6a9wekh96cd57lsarmqlwxdypdsplw6zhfncqw6ftqqhavvl/uwhale-uluna.pool.whale-uluna-pool-2.uLP".to_string(), + asset_decimals: vec![6u8, 6u8], + assets: vec![coin(1_004_300, "uwhale"), coin(996_913, "uluna")], + pool_type: PoolType::ConstantProduct, + pool_fees: pool_fees_2.clone(), + }); + }).query_pool_info("uluna-uusd-pool-1".to_string(), |result| { + let response = result.unwrap(); + let pool_info = response.pool_info; + + // the swap above was: + // SwapComputation { return_amount: Uint128(3169), + // spread_amount: Uint128(16), swap_fee_amount: Uint128(277), + // protocol_fee_amount: Uint128(396), burn_fee_amount: Uint128(118) } + + assert_eq!(pool_info, PoolInfo { + asset_denoms: vec!["uluna".to_string(), "uusd".to_string()], + lp_denom: "factory/migaloo1zwv6feuzhy6a9wekh96cd57lsarmqlwxdypdsplw6zhfncqw6ftqqhavvl/uluna-uusd.pool.uluna-uusd-pool-1.uLP".to_string(), + asset_decimals: vec![6u8, 6u8], + assets: vec![coin(1_005_587, "uluna"), coin(995_035, "uusd")], + pool_type: PoolType::ConstantProduct, + pool_fees: pool_fees_1.clone(), + }); + }); + + suite.query_all_balances( + bonding_manager_addr.clone().to_string(), + |result| { + let balances = result.unwrap(); + assert_eq!(balances, vec![ + // the whale-uluna-pool-2 doesn't have protocol fees, hence no luna was accrued + // in the last swap + Coin { + denom: "uluna".to_string(), + amount: Uint128::from(249u128), + }, + Coin { + denom: "uusd".to_string(), + amount: Uint128::from(3_695u128), + }, + Coin { + denom: "uwhale".to_string(), + amount: Uint128::from(199u128), + }, + ]); + }, + ).query_all_balances( + pool_manager_addr.clone().to_string(), + |result| { + let balances = result.unwrap(); + assert_eq!(balances, vec![ + Coin { + denom: "factory/migaloo1zwv6feuzhy6a9wekh96cd57lsarmqlwxdypdsplw6zhfncqw6ftqqhavvl/uluna-uusd.pool.uluna-uusd-pool-1.uLP".to_string(), + amount: Uint128::from(1_000u128), + }, + Coin { + denom: "factory/migaloo1zwv6feuzhy6a9wekh96cd57lsarmqlwxdypdsplw6zhfncqw6ftqqhavvl/uwhale-uluna.pool.whale-uluna-pool-1.uLP".to_string(), + amount: Uint128::from(1_000u128), + }, + Coin { + denom: "factory/migaloo1zwv6feuzhy6a9wekh96cd57lsarmqlwxdypdsplw6zhfncqw6ftqqhavvl/uwhale-uluna.pool.whale-uluna-pool-2.uLP".to_string(), + amount: Uint128::from(1_000u128), + }, + Coin { + denom: "uluna".to_string(), + amount: Uint128::from(3_003_570u128), + }, + Coin { + denom: "uusd".to_string(), + amount: Uint128::from(995_035u128), + }, + Coin { + denom: "uwhale".to_string(), + amount: Uint128::from(2_003_440u128), + }, + ]); + }, + ); + } +} diff --git a/contracts/liquidity_hub/pool-manager/src/tests/mock_querier.rs b/contracts/liquidity_hub/pool-manager/src/tests/mock_querier.rs deleted file mode 100644 index 2db5b9408..000000000 --- a/contracts/liquidity_hub/pool-manager/src/tests/mock_querier.rs +++ /dev/null @@ -1,324 +0,0 @@ -use std::collections::HashMap; -use std::iter::FromIterator; -use std::marker::PhantomData; -use std::panic; - -use cosmwasm_std::testing::{MockApi, MockQuerier, MockStorage, MOCK_CONTRACT_ADDR}; -use cosmwasm_std::{ - from_binary, from_slice, to_binary, Addr, CodeInfoResponse, Coin, ContractInfoResponse, - ContractResult, Empty, HexBinary, OwnedDeps, Querier, QuerierResult, QueryRequest, SystemError, - SystemResult, Uint128, WasmQuery, -}; -use cw20::{BalanceResponse as Cw20BalanceResponse, Cw20QueryMsg, TokenInfoResponse}; -use cw_multi_test::addons::{MockAddressGenerator, MockApiBech32}; -use white_whale_std::pool_network::asset::{Asset, AssetInfo, PairInfo, PairType, TrioInfo}; -use white_whale_std::pool_network::factory::{ - NativeTokenDecimalsResponse, QueryMsg as FactoryQueryMsg, -}; -use white_whale_std::pool_network::pair::{ - self, PoolResponse as PairPoolResponse, QueryMsg as PairQueryMsg, -}; -use white_whale_std::pool_network::pair::{ReverseSimulationResponse, SimulationResponse}; -use white_whale_std::pool_network::temp_mock_api::MockSimpleApi; -use white_whale_std::pool_network::trio; -use white_whale_std::pool_network::trio::{ - PoolResponse as TrioPoolResponse, QueryMsg as TrioQueryMsg, -}; -/// mock_dependencies is a drop-in replacement for cosmwasm_std::testing::mock_dependencies -/// this uses our CustomQuerier. -pub fn mock_dependencies( - contract_balance: &[Coin], -) -> OwnedDeps { - let custom_querier: WasmMockQuerier = - WasmMockQuerier::new(MockQuerier::new(&[(MOCK_CONTRACT_ADDR, contract_balance)])); - - OwnedDeps { - storage: MockStorage::default(), - api: MockSimpleApi::default(), - querier: custom_querier, - custom_query_type: PhantomData, - } -} - -pub struct WasmMockQuerier { - base: MockQuerier, - token_querier: TokenQuerier, - pool_factory_querier: PoolFactoryQuerier, -} - -#[derive(Clone, Default)] -pub struct TokenQuerier { - // this lets us iterate over all pairs that match the first string - balances: HashMap>, -} - -impl TokenQuerier { - pub fn new(balances: &[(&String, &[(&String, &Uint128)])]) -> Self { - TokenQuerier { - balances: balances_to_map(balances), - } - } -} - -pub fn balances_to_map( - balances: &[(&String, &[(&String, &Uint128)])], -) -> HashMap> { - let mut balances_map: HashMap> = HashMap::new(); - for (contract_addr, balances) in balances.iter() { - let mut contract_balances_map: HashMap = HashMap::new(); - for (addr, balance) in balances.iter() { - contract_balances_map.insert(addr.to_string(), **balance); - } - - balances_map.insert(contract_addr.to_string(), contract_balances_map); - } - balances_map -} - -#[derive(Clone, Default)] -pub struct PoolFactoryQuerier { - pairs: HashMap, - native_token_decimals: HashMap, -} - -impl PoolFactoryQuerier { - pub fn new(pairs: &[(&String, &PairInfo)], native_token_decimals: &[(String, u8)]) -> Self { - PoolFactoryQuerier { - pairs: pairs_to_map(pairs), - native_token_decimals: native_token_decimals_to_map(native_token_decimals), - } - } -} - -pub fn pairs_to_map(pairs: &[(&String, &PairInfo)]) -> HashMap { - let mut pairs_map: HashMap = HashMap::new(); - // Key is the pool_identifer - for (key, pair) in pairs.iter() { - pairs_map.insert(key.to_string(), (**pair).clone()); - } - pairs_map -} - -pub fn native_token_decimals_to_map(native_token_decimals: &[(String, u8)]) -> HashMap { - let mut native_token_decimals_map: HashMap = HashMap::new(); - - for (denom, decimals) in native_token_decimals.iter() { - native_token_decimals_map.insert(denom.to_string(), *decimals); - } - native_token_decimals_map -} - -impl Querier for WasmMockQuerier { - fn raw_query(&self, bin_request: &[u8]) -> QuerierResult { - // MockQuerier doesn't support Custom, so we ignore it completely here - let request: QueryRequest = match from_slice(bin_request) { - Ok(v) => v, - Err(e) => { - return SystemResult::Err(SystemError::InvalidRequest { - error: format!("Parsing query request: {e}"), - request: bin_request.into(), - }); - } - }; - self.handle_query(&request) - } -} - -impl WasmMockQuerier { - pub fn handle_query(&self, request: &QueryRequest) -> QuerierResult { - match &request { - QueryRequest::Wasm(WasmQuery::Smart { contract_addr, msg }) => match from_binary(msg) { - Ok(white_whale_std::pool_manager::QueryMsg::Pair { pair_identifier }) => { - match self.pool_factory_querier.pairs.get(&pair_identifier) { - Some(v) => SystemResult::Ok(ContractResult::Ok(to_binary(v).unwrap())), - None => SystemResult::Err(SystemError::InvalidRequest { - error: "No pair info exists".to_string(), - request: msg.as_slice().into(), - }), - } - } - _ => match from_binary(msg) { - Ok(white_whale_std::pool_manager::QueryMsg::Simulation { - offer_asset, - ask_asset, - pair_identifier, - }) => SystemResult::Ok(ContractResult::from(to_binary(&SimulationResponse { - return_amount: offer_asset.amount, - swap_fee_amount: Uint128::zero(), - spread_amount: Uint128::zero(), - protocol_fee_amount: Uint128::zero(), - burn_fee_amount: Uint128::zero(), - }))), - Ok(white_whale_std::pool_manager::QueryMsg::ReverseSimulation { - ask_asset, - offer_asset, - pair_identifier, - }) => SystemResult::Ok(ContractResult::from(to_binary( - &ReverseSimulationResponse { - offer_amount: ask_asset.amount, - swap_fee_amount: Uint128::zero(), - spread_amount: Uint128::zero(), - protocol_fee_amount: Uint128::zero(), - burn_fee_amount: Uint128::zero(), - }, - ))), - }, - }, - QueryRequest::Wasm(WasmQuery::ContractInfo { .. }) => { - let mut contract_info_response = ContractInfoResponse::default(); - contract_info_response.code_id = 0u64; - contract_info_response.creator = "creator".to_string(); - contract_info_response.admin = Some("creator".to_string()); - - SystemResult::Ok(ContractResult::Ok( - to_binary(&contract_info_response).unwrap(), - )) - } - QueryRequest::Wasm(WasmQuery::CodeInfo { code_id }) => { - let mut default = CodeInfoResponse::default(); - - match code_id { - 11 => { - default.code_id = 67; - default.creator = Addr::unchecked("creator").to_string(); - default.checksum = HexBinary::from_hex(&sha256::digest(format!( - "code_checksum_{}", - code_id - ))) - .unwrap(); - SystemResult::Ok(to_binary(&default).into()) - } - _ => { - return SystemResult::Err(SystemError::Unknown {}); - } - } - } - _ => self.base.handle_query(request), - } - } -} - -impl WasmMockQuerier { - pub fn new(base: MockQuerier) -> Self { - WasmMockQuerier { - base, - token_querier: TokenQuerier::default(), - pool_factory_querier: PoolFactoryQuerier::default(), - } - } - - // configure the mint whitelist mock querier - pub fn with_token_balances(&mut self, balances: &[(&String, &[(&String, &Uint128)])]) { - self.token_querier = TokenQuerier::new(balances); - } - - // configure the pair - pub fn with_pool_factory( - &mut self, - pairs: &[(&String, &PairInfo)], - native_token_decimals: &[(String, u8)], - ) { - self.pool_factory_querier = PoolFactoryQuerier::new(pairs, native_token_decimals); - } - - pub fn with_balance(&mut self, balances: &[(&String, Vec)]) { - for (addr, balance) in balances { - self.base.update_balance(addr.to_string(), balance.clone()); - } - } -} - -#[cfg(test)] -mod mock_exception { - use cosmwasm_std::Binary; - - use super::*; - - #[test] - fn raw_query_err() { - let deps = mock_dependencies(&[]); - assert_eq!( - deps.querier.raw_query(&[]), - SystemResult::Err(SystemError::InvalidRequest { - error: "Parsing query request: Error parsing into type cosmwasm_std::query::QueryRequest: EOF while parsing a JSON value.".to_string(), - request: Binary(vec![]), - }) - ); - } - - #[test] - fn none_factory_pair_will_err() { - let deps = mock_dependencies(&[]); - - let msg = to_binary(&white_whale_std::pool_manager::QueryMsg::Pair { - pair_identifier: "pair0000".to_string(), - }) - .unwrap(); - assert_eq!( - deps.querier - .handle_query(&QueryRequest::Wasm(WasmQuery::Smart { - contract_addr: "contract0000".to_string(), - msg: msg.clone(), - })), - SystemResult::Err(SystemError::InvalidRequest { - error: "No pair info exists".to_string(), - request: msg, - }) - ) - } - - #[test] - fn none_tokens_info_will_err() { - let deps = mock_dependencies(&[]); - - let msg = to_binary(&Cw20QueryMsg::TokenInfo {}).unwrap(); - - assert_eq!( - deps.querier - .handle_query(&QueryRequest::Wasm(WasmQuery::Smart { - contract_addr: "token0000".to_string(), - msg: msg.clone(), - })), - SystemResult::Err(SystemError::InvalidRequest { - error: "No balance info exists for the contract token0000".to_string(), - request: msg, - }) - ) - } - - #[test] - fn none_tokens_balance_will_err() { - let deps = mock_dependencies(&[]); - - let msg = to_binary(&Cw20QueryMsg::Balance { - address: "address0000".to_string(), - }) - .unwrap(); - - assert_eq!( - deps.querier - .handle_query(&QueryRequest::Wasm(WasmQuery::Smart { - contract_addr: "token0000".to_string(), - msg: msg.clone(), - })), - SystemResult::Err(SystemError::InvalidRequest { - error: "No balance info exists for the contract token0000".to_string(), - request: msg, - }) - ) - } - - #[test] - #[should_panic] - fn none_tokens_minter_will_panic() { - let deps = mock_dependencies(&[]); - - let msg = to_binary(&Cw20QueryMsg::Minter {}).unwrap(); - - deps.querier - .handle_query(&QueryRequest::Wasm(WasmQuery::Smart { - contract_addr: "token0000".to_string(), - msg, - })); - } -} diff --git a/contracts/liquidity_hub/pool-manager/src/tests/mod.rs b/contracts/liquidity_hub/pool-manager/src/tests/mod.rs index ba1e59453..8a80c1006 100644 --- a/contracts/liquidity_hub/pool-manager/src/tests/mod.rs +++ b/contracts/liquidity_hub/pool-manager/src/tests/mod.rs @@ -1,6 +1,2 @@ pub mod integration_tests; pub mod suite; - -// TODO: Uncomment once unit_tests are working or removed -// pub mod mock_querier; -// pub mod unit_tests; diff --git a/contracts/liquidity_hub/pool-manager/src/tests/suite.rs b/contracts/liquidity_hub/pool-manager/src/tests/suite.rs index 39a5da6fb..122a05856 100644 --- a/contracts/liquidity_hub/pool-manager/src/tests/suite.rs +++ b/contracts/liquidity_hub/pool-manager/src/tests/suite.rs @@ -1,11 +1,11 @@ use cosmwasm_std::testing::MockStorage; use std::cell::RefCell; -use white_whale_std::pool_manager::InstantiateMsg; use white_whale_std::pool_manager::{ - Config, FeatureToggle, PairInfoResponse, ReverseSimulateSwapOperationsResponse, + Config, FeatureToggle, PoolInfoResponse, ReverseSimulateSwapOperationsResponse, ReverseSimulationResponse, SimulateSwapOperationsResponse, SimulationResponse, SwapOperation, SwapRouteCreatorResponse, SwapRouteResponse, SwapRoutesResponse, }; +use white_whale_std::pool_manager::{InstantiateMsg, PoolType}; use cosmwasm_std::{coin, Addr, Coin, Decimal, Empty, StdResult, Timestamp, Uint128, Uint64}; use cw_multi_test::addons::{MockAddressGenerator, MockApiBech32}; @@ -18,7 +18,7 @@ use white_whale_std::epoch_manager::epoch_manager::{Epoch, EpochConfig}; use white_whale_std::fee::PoolFee; use white_whale_std::incentive_manager::PositionsResponse; use white_whale_std::lp_common::LP_SYMBOL; -use white_whale_std::pool_network::asset::{AssetInfo, PairType}; +use white_whale_std::pool_network::asset::AssetInfo; use white_whale_testing::multi_test::stargate_mock::StargateMock; fn contract_pool_manager() -> Box> { @@ -105,11 +105,10 @@ impl TestingSuite { self } - pub(crate) fn get_lp_denom(&self, pair_id: String) -> String { - // TODO: this should have + pub(crate) fn get_lp_denom(&self, pool_identifier: String) -> String { format!( "factory/{}/u{}.pool.{}.{}", - self.pool_manager_addr, pair_id, pair_id, LP_SYMBOL + self.pool_manager_addr, pool_identifier, pool_identifier, LP_SYMBOL ) } } @@ -317,18 +316,19 @@ impl TestingSuite { pub(crate) fn provide_liquidity( &mut self, sender: Addr, - pair_identifier: String, + pool_identifier: String, unlocking_duration: Option, lock_position_identifier: Option, max_spread: Option, + receiver: Option, funds: Vec, result: impl Fn(Result), ) -> &mut Self { let msg = white_whale_std::pool_manager::ExecuteMsg::ProvideLiquidity { - pair_identifier, + pool_identifier, slippage_tolerance: None, max_spread, - receiver: None, + receiver, unlocking_duration, lock_position_identifier, }; @@ -346,22 +346,20 @@ impl TestingSuite { pub(crate) fn swap( &mut self, sender: Addr, - offer_asset: Coin, ask_asset_denom: String, belief_price: Option, max_spread: Option, - to: Option, - pair_identifier: String, + receiver: Option, + pool_identifier: String, funds: Vec, result: impl Fn(Result), ) -> &mut Self { let msg = white_whale_std::pool_manager::ExecuteMsg::Swap { - offer_asset, ask_asset_denom, belief_price, max_spread, - to, - pair_identifier, + receiver, + pool_identifier, }; result( @@ -379,7 +377,7 @@ impl TestingSuite { sender: Addr, operations: Vec, minimum_receive: Option, - to: Option, + receiver: Option, max_spread: Option, funds: Vec, result: impl Fn(Result), @@ -387,7 +385,7 @@ impl TestingSuite { let msg = white_whale_std::pool_manager::ExecuteMsg::ExecuteSwapOperations { operations, minimum_receive, - to, + receiver, max_spread, }; @@ -401,30 +399,30 @@ impl TestingSuite { #[track_caller] #[allow(clippy::too_many_arguments)] - pub(crate) fn create_pair( + pub(crate) fn create_pool( &mut self, sender: Addr, asset_denoms: Vec, asset_decimals: Vec, pool_fees: PoolFee, - pair_type: PairType, - pair_identifier: Option, - pair_creation_fee_funds: Vec, + pool_type: PoolType, + pool_identifier: Option, + pool_creation_fee_funds: Vec, result: impl Fn(Result), ) -> &mut Self { - let msg = white_whale_std::pool_manager::ExecuteMsg::CreatePair { + let msg = white_whale_std::pool_manager::ExecuteMsg::CreatePool { asset_denoms, asset_decimals, pool_fees, - pair_type, - pair_identifier, + pool_type, + pool_identifier, }; result(self.app.execute_contract( sender, self.pool_manager_addr.clone(), &msg, - &pair_creation_fee_funds, + &pool_creation_fee_funds, )); self @@ -434,11 +432,11 @@ impl TestingSuite { pub(crate) fn withdraw_liquidity( &mut self, sender: Addr, - pair_identifier: String, + pool_identifier: String, funds: Vec, result: impl Fn(Result), ) -> &mut Self { - let msg = white_whale_std::pool_manager::ExecuteMsg::WithdrawLiquidity { pair_identifier }; + let msg = white_whale_std::pool_manager::ExecuteMsg::WithdrawLiquidity { pool_identifier }; result( self.app @@ -554,61 +552,60 @@ impl TestingSuite { self } - pub(crate) fn query_pair_info( + pub(crate) fn query_pool_info( &self, - pair_identifier: String, - result: impl Fn(StdResult), + pool_identifier: String, + result: impl Fn(StdResult), ) -> &Self { - let pair_info_response: StdResult = self.app.wrap().query_wasm_smart( + let pool_info_response: StdResult = self.app.wrap().query_wasm_smart( &self.pool_manager_addr, - &white_whale_std::pool_manager::QueryMsg::Pair { pair_identifier }, + &white_whale_std::pool_manager::QueryMsg::Pool { pool_identifier }, ); - result(pair_info_response); + result(pool_info_response); self } pub(crate) fn query_simulation( &mut self, - pair_identifier: String, + pool_identifier: String, offer_asset: Coin, + ask_asset_denom: String, result: impl Fn(StdResult), ) -> &mut Self { - let pair_info_response: StdResult = self.app.wrap().query_wasm_smart( + let pool_info_response: StdResult = self.app.wrap().query_wasm_smart( &self.pool_manager_addr, &white_whale_std::pool_manager::QueryMsg::Simulation { offer_asset, - pair_identifier, + ask_asset_denom, + pool_identifier, }, ); - result(pair_info_response); + result(pool_info_response); self } pub(crate) fn query_reverse_simulation( &mut self, - pair_identifier: String, - offer_asset: String, + pool_identifier: String, ask_asset: Coin, + offer_asset_denom: String, result: impl Fn(StdResult), ) -> &mut Self { - let pair_info_response: StdResult = + let pool_info_response: StdResult = self.app.wrap().query_wasm_smart( &self.pool_manager_addr, &white_whale_std::pool_manager::QueryMsg::ReverseSimulation { - offer_asset: Coin { - amount: Uint128::zero(), - denom: offer_asset, - }, ask_asset, - pair_identifier, + offer_asset_denom, + pool_identifier, }, ); - result(pair_info_response); + result(pool_info_response); self } @@ -619,7 +616,7 @@ impl TestingSuite { operations: Vec, result: impl Fn(StdResult), ) -> &mut Self { - let pair_info_response: StdResult = + let pool_info_response: StdResult = self.app.wrap().query_wasm_smart( &self.pool_manager_addr, &white_whale_std::pool_manager::QueryMsg::SimulateSwapOperations { @@ -628,7 +625,7 @@ impl TestingSuite { }, ); - result(pair_info_response); + result(pool_info_response); self } @@ -639,7 +636,7 @@ impl TestingSuite { operations: Vec, result: impl Fn(StdResult), ) -> &mut Self { - let pair_info_response: StdResult = + let pool_info_response: StdResult = self.app.wrap().query_wasm_smart( &self.pool_manager_addr, &white_whale_std::pool_manager::QueryMsg::ReverseSimulateSwapOperations { @@ -648,7 +645,7 @@ impl TestingSuite { }, ); - result(pair_info_response); + result(pool_info_response); self } @@ -660,13 +657,13 @@ impl TestingSuite { result: impl Fn(StdResult), ) -> &mut Self { // Get the LP token from Config - let lp_token_response: PairInfoResponse = self + let lp_token_response: PoolInfoResponse = self .app .wrap() .query_wasm_smart( &self.pool_manager_addr, - &white_whale_std::pool_manager::QueryMsg::Pair { - pair_identifier: identifier, + &white_whale_std::pool_manager::QueryMsg::Pool { + pool_identifier: identifier, }, ) .unwrap(); @@ -676,7 +673,7 @@ impl TestingSuite { let balance: Uint128 = self .app .wrap() - .query_balance(sender, lp_token_response.pair_info.lp_denom) + .query_balance(sender, lp_token_response.pool_info.lp_denom) .unwrap() .amount; @@ -690,12 +687,12 @@ impl TestingSuite { .wrap() .query_wasm_smart( &self.pool_manager_addr, - &white_whale_std::pool_manager::QueryMsg::Config {}, + &white_whale_std::pool_manager::QueryMsg::Config, ) .unwrap() } - /// Retrieves a swap route for a given pair of assets. + /// Retrieves a swap route for a given pool of assets. pub(crate) fn query_swap_route( &mut self, offer_asset_denom: String, @@ -715,14 +712,14 @@ impl TestingSuite { self } - /// Retrieves the swap routes for a given pair of assets. + /// Retrieves the swap routes for a given poolr of assets. pub(crate) fn query_swap_routes( &mut self, result: impl Fn(StdResult), ) -> &mut Self { let swap_routes_response: StdResult = self.app.wrap().query_wasm_smart( &self.pool_manager_addr, - &white_whale_std::pool_manager::QueryMsg::SwapRoutes {}, + &white_whale_std::pool_manager::QueryMsg::SwapRoutes, ); result(swap_routes_response); @@ -730,7 +727,7 @@ impl TestingSuite { self } - /// Retrieves the swap route creator for a given pair of assets. + /// Retrieves the swap route creator for a given pool of assets. pub(crate) fn query_swap_route_creator( &mut self, offer_asset_denom: String, @@ -779,9 +776,9 @@ impl TestingSuite { ) -> &mut Self { let lp_denom = RefCell::new("".to_string()); - self.query_pair_info(identifier.clone(), |res| { + self.query_pool_info(identifier.clone(), |res| { let response = res.unwrap(); - *lp_denom.borrow_mut() = response.pair_info.lp_denom.clone(); + *lp_denom.borrow_mut() = response.pool_info.lp_denom.clone(); }); let supply_response = self.app.wrap().query_supply(lp_denom.into_inner()); diff --git a/contracts/liquidity_hub/pool-manager/src/tests/temp_mock_api.rs b/contracts/liquidity_hub/pool-manager/src/tests/temp_mock_api.rs deleted file mode 100644 index fb5a7b1ea..000000000 --- a/contracts/liquidity_hub/pool-manager/src/tests/temp_mock_api.rs +++ /dev/null @@ -1,87 +0,0 @@ -use cosmwasm_std::{ - Addr, Api, CanonicalAddr, RecoverPubkeyError, StdError, StdResult, VerificationError, -}; -// Reworked mock api to work with instantiate2 in mock_querier, can eventually be removed -#[derive(Copy, Clone, Default)] -pub struct MockSimpleApi {} - -impl Api for MockSimpleApi { - fn addr_validate(&self, input: &str) -> StdResult { - let canonical = self.addr_canonicalize(input)?; - let normalized = self.addr_humanize(&canonical)?; - if input != normalized && normalized != "contract1" { - return Err(StdError::generic_err( - "Invalid input: address not normalized", - )); - } - - Ok(Addr::unchecked(input)) - } - - fn addr_canonicalize(&self, input: &str) -> StdResult { - // Very straigthfoward canonicalization, we simply serialize the address to bytes - Ok(input.chars().map(|c| c as u8).collect::>().into()) - } - - fn addr_humanize(&self, canonical: &CanonicalAddr) -> StdResult { - let mut address: String = canonical.0 .0.iter().map(|&c| c as char).collect(); - if address - == "\u{82}³r\u{13}Ø\r¯ËÌB\u{85}Ó^-b¸\u{19}\u{89}Z\rBðf0ç\u{9d}µís+æ\u{16}".to_string() - { - address = "contract1".to_string(); - } - Ok(Addr::unchecked(address)) - } - - fn secp256k1_verify( - &self, - message_hash: &[u8], - signature: &[u8], - public_key: &[u8], - ) -> Result { - cosmwasm_std::testing::MockApi::default().secp256k1_verify( - message_hash, - signature, - public_key, - ) - } - - fn secp256k1_recover_pubkey( - &self, - message_hash: &[u8], - signature: &[u8], - recovery_param: u8, - ) -> Result, RecoverPubkeyError> { - cosmwasm_std::testing::MockApi::default().secp256k1_recover_pubkey( - message_hash, - signature, - recovery_param, - ) - } - - fn ed25519_verify( - &self, - message: &[u8], - signature: &[u8], - public_key: &[u8], - ) -> Result { - cosmwasm_std::testing::MockApi::default().ed25519_verify(message, signature, public_key) - } - - fn ed25519_batch_verify( - &self, - messages: &[&[u8]], - signatures: &[&[u8]], - public_keys: &[&[u8]], - ) -> Result { - cosmwasm_std::testing::MockApi::default().ed25519_batch_verify( - messages, - signatures, - public_keys, - ) - } - - fn debug(&self, message: &str) { - println!("{}", message); - } -} diff --git a/contracts/liquidity_hub/pool-manager/src/tests/unit_tests/mod.rs b/contracts/liquidity_hub/pool-manager/src/tests/unit_tests/mod.rs deleted file mode 100644 index c79f956f8..000000000 --- a/contracts/liquidity_hub/pool-manager/src/tests/unit_tests/mod.rs +++ /dev/null @@ -1,3 +0,0 @@ -pub mod pairs; -pub mod provide_liquidity; -pub mod swap; diff --git a/contracts/liquidity_hub/pool-manager/src/tests/unit_tests/pairs.rs b/contracts/liquidity_hub/pool-manager/src/tests/unit_tests/pairs.rs deleted file mode 100644 index 03ca151ab..000000000 --- a/contracts/liquidity_hub/pool-manager/src/tests/unit_tests/pairs.rs +++ /dev/null @@ -1,620 +0,0 @@ -use cosmwasm_std::testing::{ - mock_dependencies_with_balance, mock_env, mock_info, MockApi, MockStorage, MOCK_CONTRACT_ADDR, -}; -use cosmwasm_std::{ - attr, coin, from_binary, to_binary, Api, CanonicalAddr, Coin, CosmosMsg, Decimal, OwnedDeps, - Reply, ReplyOn, Response, SubMsg, SubMsgResponse, SubMsgResult, Uint128, WasmMsg, -}; - -use white_whale_std::fee::Fee; -use white_whale_std::pool_network; -use white_whale_std::pool_network::asset::{ - AssetInfo, AssetInfoRaw, PairInfo, PairInfoRaw, PairType, -}; -use white_whale_std::pool_network::factory::{ - ConfigResponse, ExecuteMsg, InstantiateMsg, MigrateMsg, NativeTokenDecimalsResponse, QueryMsg, -}; -use white_whale_std::pool_network::mock_querier::{ - mock_dependencies, mock_dependencies_trio, WasmMockQuerier, WasmMockTrioQuerier, -}; -use white_whale_std::pool_network::pair::{ - InstantiateMsg as PairInstantiateMsg, MigrateMsg as PairMigrateMsg, PoolFee, -}; -use white_whale_std::pool_network::trio::{ - InstantiateMsg as TrioInstantiateMsg, MigrateMsg as TrioMigrateMsg, PoolFee as TrioPoolFee, -}; - -use crate::contract::{execute, instantiate, query}; -use crate::error::ContractError; -use crate::state::{pair_key, PAIRS}; -use test_case::test_case; -use white_whale_std::pool_manager::InstantiateMsg as SingleSwapInstantiateMsg; -#[cfg(test)] -mod pair_creation_tests { - use super::*; - use crate::tests::mock_querier::mock_dependencies; - use cosmwasm_std::testing::{mock_env, mock_info}; - use cosmwasm_std::{coin, coins, Binary, Decimal, DepsMut, Uint128}; - use cw20::MinterResponse; - use white_whale_std::pool_network::asset::Asset; - - // use crate::msg::{AssetInfo, ExecuteMsg, Fee, PairType, PoolFee}; - use crate::state::add_allow_native_token; - use crate::token::InstantiateMsg as TokenInstantiateMsg; - use cosmwasm_std::attr; - use cosmwasm_std::SubMsg; - use cosmwasm_std::WasmMsg; - use test_case::test_case; - use white_whale_std::pool_manager::ExecuteMsg; - use white_whale_std::pool_network::pair; - - // Constants for testing - const MOCK_CONTRACT_ADDR: &str = "contract_addr"; - - #[test] - fn create_pair() { - let mut deps = mock_dependencies(&[coin(10u128, "uusd".to_string())]); - // Instantiate contract - let msg = SingleSwapInstantiateMsg { - fee_collector_addr: "fee_collector_addr".to_string(), - owner: "owner".to_string(), - pair_code_id: 10u64, - token_code_id: 11u64, - pool_creation_fee: Asset { - amount: Uint128::new(1000000u128), - info: AssetInfo::NativeToken { - denom: "uusd".to_string(), - }, - }, - }; - let env = mock_env(); - let info = mock_info("owner", &[]); - let res = instantiate(deps.as_mut(), env, info, msg).unwrap(); - assert_eq!(0, res.messages.len()); - deps.querier - .with_pool_factory(&[], &[("uusd".to_string(), 6u8)]); - deps.querier.with_token_balances(&[( - &"asset0001".to_string(), - &[(&"addr0000".to_string(), &Uint128::new(1000000u128))], - )]); - let asset_infos = [ - AssetInfo::NativeToken { - denom: "uusd".to_string(), - }, - AssetInfo::Token { - contract_addr: "asset0001".to_string(), - }, - ]; - - let msg = ExecuteMsg::CreatePair { - asset_infos: asset_infos.to_vec(), - pool_fees: PoolFee { - protocol_fee: Fee { - share: Decimal::percent(1u64), - }, - swap_fee: Fee { - share: Decimal::percent(1u64), - }, - burn_fee: Fee { - share: Decimal::zero(), - }, - }, - pair_type: PairType::ConstantProduct, - token_factory_lp: false, - pair_identifier: Some("uusd-mAAPL".to_string()), - }; - - let env = mock_env(); - let info = mock_info( - "addr0000", - &[Coin { - denom: "uusd".to_string(), - amount: Uint128::new(1000000u128), - }], - ); - let res = execute(deps.as_mut(), env.clone(), info.clone(), msg).unwrap(); - assert_eq!( - res.attributes, - vec![ - attr("action", "create_pair"), - attr("pair", "uusd-mAAPL"), - attr("pair_label", "uusd-mAAPL"), - attr("pair_type", "ConstantProduct"), - ] - ); - let seed = format!( - "{}{}{}", - "uusd-mAAPL".to_string(), - info.sender.into_string(), - env.block.height - ); - let salt = Binary::from(seed.as_bytes()); - - assert_eq!( - res.messages, - vec![SubMsg { - id: 0, - gas_limit: None, - reply_on: ReplyOn::Never, - msg: WasmMsg::Instantiate2 { - msg: to_binary(&TokenInstantiateMsg { - name: "uusd-mAAPL-LP".to_string(), - symbol: "uLP".to_string(), - decimals: 6, - initial_balances: vec![], - mint: Some(MinterResponse { - minter: env.contract.address.to_string(), - cap: None, - }), - }) - .unwrap(), - code_id: 11u64, - funds: vec![], - label: "uusd-mAAPL-LP".to_string(), - admin: None, - salt - } - .into(), - },] - ); - } - - #[test] - fn create_stableswap_pair() { - let mut deps = mock_dependencies(&[coin(10u128, "uusd".to_string())]); - // deps.api = Box::new(MockApi::default()); - // Instantiate contract - let msg = SingleSwapInstantiateMsg { - fee_collector_addr: "fee_collector_addr".to_string(), - owner: "owner".to_string(), - pair_code_id: 10u64, - token_code_id: 11u64, - pool_creation_fee: Asset { - amount: Uint128::new(1000000u128), - info: AssetInfo::NativeToken { - denom: "uusd".to_string(), - }, - }, - }; - let env = mock_env(); - let info = mock_info("owner", &[]); - let res = instantiate(deps.as_mut(), env, info, msg).unwrap(); - assert_eq!(0, res.messages.len()); - - deps.querier - .with_pool_factory(&[], &[("uusd".to_string(), 6u8)]); - deps.querier.with_token_balances(&[( - &"asset0001".to_string(), - &[(&"addr0000".to_string(), &Uint128::new(1000000u128))], - )]); - - let asset_infos = [ - AssetInfo::NativeToken { - denom: "uusd".to_string(), - }, - AssetInfo::Token { - contract_addr: "asset0001".to_string(), - }, - ]; - - let msg = ExecuteMsg::CreatePair { - asset_infos: asset_infos.to_vec(), - pool_fees: PoolFee { - protocol_fee: Fee { - share: Decimal::percent(1u64), - }, - swap_fee: Fee { - share: Decimal::percent(1u64), - }, - burn_fee: Fee { - share: Decimal::zero(), - }, - }, - pair_type: PairType::StableSwap { amp: 100 }, - token_factory_lp: false, - pair_identifier: Some("uusd-mAAPL".to_string()), - }; - - let env = mock_env(); - let info = mock_info( - "addr0000", - &[Coin { - denom: "uusd".to_string(), - amount: Uint128::new(1000000u128), - }], - ); - let res = execute(deps.as_mut(), env.clone(), info.clone(), msg).unwrap(); - let seed = format!( - "{}{}{}", - "uusd-mAAPL".to_string(), - info.sender.into_string(), - env.block.height - ); - let salt = Binary::from(seed.as_bytes()); - assert_eq!( - res.attributes, - vec![ - attr("action", "create_pair"), - attr("pair", "uusd-mAAPL"), - attr("pair_label", "uusd-mAAPL"), - attr("pair_type", "StableSwap"), - ] - ); - - assert_eq!( - res.messages, - vec![SubMsg { - id: 0, - gas_limit: None, - reply_on: ReplyOn::Never, - msg: WasmMsg::Instantiate2 { - msg: to_binary(&TokenInstantiateMsg { - name: "uusd-mAAPL-LP".to_string(), - symbol: "uLP".to_string(), - decimals: 6, - initial_balances: vec![], - mint: Some(MinterResponse { - minter: env.contract.address.to_string(), - cap: None, - }), - }) - .unwrap(), - code_id: 11u64, - funds: vec![], - label: "uusd-mAAPL-LP".to_string(), - admin: None, - salt - } - .into(), - },] - ); - } - - #[test] - fn create_pair_native_token_and_ibc_token() { - let mut deps = mock_dependencies(&[ - coin(10u128, "uusd".to_string()), - coin( - 10u128, - "ibc/27394FB092D2ECCD56123C74F36E4C1F926001CEADA9CA97EA622B25F41E5EB2".to_string(), - ), - ]); - - // Instantiate contract - let msg = SingleSwapInstantiateMsg { - fee_collector_addr: "fee_collector_addr".to_string(), - owner: "owner".to_string(), - pair_code_id: 10u64, - token_code_id: 11u64, - pool_creation_fee: Asset { - amount: Uint128::new(1000000u128), - info: AssetInfo::NativeToken { - denom: "uusd".to_string(), - }, - }, - }; - let env = mock_env(); - let info = mock_info("owner", &[]); - let res = instantiate(deps.as_mut(), env, info, msg).unwrap(); - assert_eq!(0, res.messages.len()); - - deps.querier.with_pool_factory( - &[], - &[ - ("uusd".to_string(), 6u8), - ( - "ibc/27394FB092D2ECCD56123C74F36E4C1F926001CEADA9CA97EA622B25F41E5EB2" - .to_string(), - 6u8, - ), - ], - ); - deps.querier.with_token_balances(&[( - &"asset0001".to_string(), - &[(&"addr0000".to_string(), &Uint128::new(1000000u128))], - )]); - - // deps = init(deps); - // deps.querier.with_pool_factory( - // &[], - // &[ - // ("uusd".to_string(), 6u8), - // ( - // "ibc/27394FB092D2ECCD56123C74F36E4C1F926001CEADA9CA97EA622B25F41E5EB2".to_string(), - // 6u8, - // ), - // ], - // ); - - let asset_infos = [ - AssetInfo::NativeToken { - denom: "uusd".to_string(), - }, - AssetInfo::NativeToken { - denom: "ibc/27394FB092D2ECCD56123C74F36E4C1F926001CEADA9CA97EA622B25F41E5EB2" - .to_string(), - }, - ]; - - let msg = ExecuteMsg::CreatePair { - asset_infos: asset_infos.to_vec(), - pool_fees: PoolFee { - protocol_fee: Fee { - share: Decimal::percent(1u64), - }, - swap_fee: Fee { - share: Decimal::percent(1u64), - }, - burn_fee: Fee { - share: Decimal::zero(), - }, - }, - pair_type: PairType::ConstantProduct, - token_factory_lp: false, - pair_identifier: None, - }; - - let env = mock_env(); - let info = mock_info( - "addr0000", - &[Coin { - denom: "uusd".to_string(), - amount: Uint128::new(1000000u128), - }], - ); - let res = execute(deps.as_mut(), env.clone(), info.clone(), msg).unwrap(); - assert_eq!( - res.attributes, - vec![ - attr("action", "create_pair"), - attr("pair", "uusd-ibc/2739...5EB2"), - attr("pair_label", "uusd-ibc/2739...5EB2"), - attr("pair_type", "ConstantProduct"), - ] - ); - let seed = format!( - "{}{}{}", - "uusd-ibc/2739...5EB2".to_string(), - info.sender.into_string(), - env.block.height - ); - let salt = Binary::from(seed.as_bytes()); - assert_eq!( - res.messages, - vec![SubMsg { - id: 0, - gas_limit: None, - reply_on: ReplyOn::Never, - msg: WasmMsg::Instantiate2 { - msg: to_binary(&TokenInstantiateMsg { - name: "uusd-ibc/2739...5EB2-LP".to_string(), - symbol: "uLP".to_string(), - decimals: 6, - initial_balances: vec![], - mint: Some(MinterResponse { - minter: env.contract.address.to_string(), - cap: None, - }), - }) - .unwrap(), - code_id: 11u64, - funds: vec![], - label: "uusd-ibc/2739...5EB2-LP".to_string(), - admin: None, - salt - } - .into(), - },] - ); - } - - #[test] - fn create_ibc_tokens_pair() { - let mut deps = mock_dependencies(&[ - coin( - 10u128, - "ibc/4CD525F166D32B0132C095F353F4C6F033B0FF5C49141470D1EFDA1D63303D04".to_string(), - ), - coin( - 10u128, - "ibc/27394FB092D2ECCD56123C74F36E4C1F926001CEADA9CA97EA622B25F41E5EB2".to_string(), - ), - ]); - let pair_creation_fee = Asset { - amount: Uint128::new(1000000u128), - info: AssetInfo::NativeToken { - denom: "uusd".to_string(), - }, - }; - - // Instantiate contract - let msg = SingleSwapInstantiateMsg { - fee_collector_addr: "fee_collector_addr".to_string(), - owner: "owner".to_string(), - pair_code_id: 10u64, - token_code_id: 11u64, - pool_creation_fee: pair_creation_fee.clone(), - }; - let env = mock_env(); - let info = mock_info("owner", &[]); - let res = instantiate(deps.as_mut(), env, info, msg).unwrap(); - assert_eq!(0, res.messages.len()); - - // deps = init(deps); - deps.querier.with_pool_factory( - &[], - &[ - ( - "ibc/4CD525F166D32B0132C095F353F4C6F033B0FF5C49141470D1EFDA1D63303D04" - .to_string(), - 6u8, - ), - ( - "ibc/27394FB092D2ECCD56123C74F36E4C1F926001CEADA9CA97EA622B25F41E5EB2" - .to_string(), - 6u8, - ), - ], - ); - - let asset_infos = [ - AssetInfo::NativeToken { - denom: "ibc/4CD525F166D32B0132C095F353F4C6F033B0FF5C49141470D1EFDA1D63303D04" - .to_string(), - }, - AssetInfo::NativeToken { - denom: "ibc/27394FB092D2ECCD56123C74F36E4C1F926001CEADA9CA97EA622B25F41E5EB2" - .to_string(), - }, - ]; - - let msg = ExecuteMsg::CreatePair { - asset_infos: asset_infos.to_vec(), - pool_fees: PoolFee { - protocol_fee: Fee { - share: Decimal::percent(1u64), - }, - swap_fee: Fee { - share: Decimal::percent(1u64), - }, - burn_fee: Fee { - share: Decimal::zero(), - }, - }, - pair_type: PairType::ConstantProduct, - token_factory_lp: false, - pair_identifier: None, - }; - - let env = mock_env(); - let info = mock_info("addr0000", &[coin(1000000u128, "uusd".to_string())]); - let res = execute(deps.as_mut(), env.clone(), info.clone(), msg).unwrap(); - assert_eq!( - res.attributes, - vec![ - attr("action", "create_pair"), - attr("pair", "ibc/4CD5...3D04-ibc/2739...5EB2"), - attr("pair_label", "ibc/4CD5...3D04-ibc/2739...5EB2"), - attr("pair_type", "ConstantProduct"), - ] - ); - let seed = format!( - "{}{}{}", - "ibc/4CD5...3D04-ibc/2739...5EB2".to_string(), - info.sender.into_string(), - env.block.height - ); - let salt = Binary::from(seed.as_bytes()); - assert_eq!( - res.messages, - vec![SubMsg { - id: 0, - gas_limit: None, - reply_on: ReplyOn::Never, - msg: WasmMsg::Instantiate2 { - msg: to_binary(&TokenInstantiateMsg { - name: "ibc/4CD5...3D04-ibc/2739...5EB2-LP".to_string(), - symbol: "uLP".to_string(), - decimals: 6, - initial_balances: vec![], - mint: Some(MinterResponse { - minter: env.contract.address.to_string(), - cap: None, - }), - }) - .unwrap(), - code_id: 11u64, - funds: vec![], - label: "ibc/4CD5...3D04-ibc/2739...5EB2-LP".to_string(), - admin: None, - salt - } - .into(), - },] - ); - } - - #[test_case( - AssetInfo::NativeToken { denom: "uusd".to_string() }, - AssetInfo::NativeToken { denom: "uusd".to_string() }, - ContractError::SameAsset { } ; - "fail_to_create_same_pair" - )] - #[test_case( - AssetInfo::NativeToken { denom: "uusd".to_string() }, - AssetInfo::Token { contract_addr: "asset0001".to_string() }, - ContractError::ExistingPair { } ; - "fail_to_create_existing_pair" - )] - #[test_case( - AssetInfo::NativeToken { denom: "uusd".to_string() }, - AssetInfo::NativeToken { denom: "uxxx".to_string() }, - ContractError::Std(cosmwasm_std::StdError::generic_err("Querier system error: Cannot parse request: No decimal info exist in: {\"native_token_decimals\":{\"denom\":\"uxxx\"}}".to_string())) ; - "fail_to_create_pair_with_inactive_denoms" - )] - fn test_failures(asset1: AssetInfo, asset2: AssetInfo, expected_error: ContractError) { - let mut deps = mock_dependencies(&[coin(10000000u128, "uusd".to_string())]); - // deps = init(deps); - // Instantiate contract - let msg = SingleSwapInstantiateMsg { - fee_collector_addr: "fee_collector_addr".to_string(), - owner: "owner".to_string(), - pair_code_id: 10u64, - token_code_id: 11u64, - pool_creation_fee: Asset { - amount: Uint128::new(1000000u128), - info: AssetInfo::NativeToken { - denom: "uusd".to_string(), - }, - }, - }; - let env = mock_env(); - let info = mock_info("owner", &[]); - let res = instantiate(deps.as_mut(), env, info, msg).unwrap(); - assert_eq!(0, res.messages.len()); - - deps.querier - .with_pool_factory(&[], &[("uusd".to_string(), 6u8)]); - deps.querier.with_token_balances(&[( - &"asset0001".to_string(), - &[(&"addr0000".to_string(), &Uint128::new(1000000u128))], - )]); - let msg = white_whale_std::pool_manager::ExecuteMsg::CreatePair { - asset_infos: [asset1, asset2].to_vec(), - pool_fees: PoolFee { - protocol_fee: Fee { - share: Decimal::percent(1u64), - }, - swap_fee: Fee { - share: Decimal::percent(1u64), - }, - burn_fee: Fee { - share: Decimal::zero(), - }, - }, - pair_type: PairType::ConstantProduct, - token_factory_lp: false, - pair_identifier: None, - }; - - let env = mock_env(); - let info = mock_info( - "addr0000", - &[Coin { - denom: "uusd".to_string(), - amount: Uint128::new(1000000u128), - }], - ); - - if let ContractError::ExistingPair { .. } = expected_error { - // Create the pair so when we try again below we get ExistingPair provided the error checking is behaving properly - let res = execute(deps.as_mut(), env.clone(), info.clone(), msg.clone()); - } - - let res = execute(deps.as_mut(), env, info, msg); - match res { - Ok(_) => panic!("Should return expected error"), - Err(err) => assert_eq!(err, expected_error), - } - } -} diff --git a/contracts/liquidity_hub/pool-manager/src/tests/unit_tests/provide_liquidity.rs b/contracts/liquidity_hub/pool-manager/src/tests/unit_tests/provide_liquidity.rs deleted file mode 100644 index a740287d4..000000000 --- a/contracts/liquidity_hub/pool-manager/src/tests/unit_tests/provide_liquidity.rs +++ /dev/null @@ -1,941 +0,0 @@ -use cosmwasm_std::testing::{mock_env, mock_info, MOCK_CONTRACT_ADDR}; -use cosmwasm_std::{ - attr, to_binary, Coin, CosmosMsg, Decimal, Reply, Response, StdError, SubMsg, SubMsgResponse, - SubMsgResult, Uint128, WasmMsg, -}; -#[cfg(feature = "token_factory")] -use cosmwasm_std::{coin, BankMsg}; -use cw20::Cw20ExecuteMsg; -use white_whale_std::fee::Fee; -#[cfg(feature = "token_factory")] -use white_whale_std::lp_common::LP_SYMBOL; - -use crate::tests::mock_querier::mock_dependencies; -#[cfg(feature = "token_factory")] -use white_whale_std::pool_network; -use white_whale_std::pool_network::asset::{Asset, AssetInfo, PairType, MINIMUM_LIQUIDITY_AMOUNT}; -#[cfg(feature = "token_factory")] -use white_whale_std::pool_network::denom::MsgMint; -use white_whale_std::pool_network::pair::PoolFee; -// use white_whale_std::pool_network::pair::{ExecuteMsg, InstantiateMsg, PoolFee}; -use crate::contract::{execute, instantiate}; -use crate::error::ContractError; -use white_whale_std::pool_manager::ExecuteMsg; -use white_whale_std::pool_manager::InstantiateMsg as SingleSwapInstantiateMsg; -#[test] -fn provide_liquidity_cw20_lp() { - let mut deps = mock_dependencies(&[Coin { - denom: "uusd".to_string(), - amount: Uint128::from(2_000u128), - }]); - - // Note: In order to support and write tests sooner I override the MockAPI in mock_querier with a SimpleMockAPI - // The effect is I can continue testing with instantiate2 but now an addr like liquidity0000 becomes TEMP_CONTRACT_ADDR - // This likely breaks tests elsewhere - let TEMP_CONTRACT_ADDR: String = - "4j®Q¶õ\u{1c}¼\u{f}º\u{8d}N\u{7}Sȶö\u{7}©³8\u{7f}j¼Þp\u{9c}\u{1b}æù\u{a0}î".to_string(); - - deps.querier.with_token_balances(&[ - ( - &TEMP_CONTRACT_ADDR.clone(), - &[(&MOCK_CONTRACT_ADDR.to_string(), &Uint128::zero())], - ), - ( - &"asset0000".to_string(), - &[(&"addr0000".to_string(), &Uint128::from(100u128))], - ), - ]); - deps.querier - .with_pool_factory(&[], &[("uusd".to_string(), 6u8)]); - - // Instantiate contract - let msg = SingleSwapInstantiateMsg { - fee_collector_addr: "fee_collector_addr".to_string(), - owner: "owner".to_string(), - pair_code_id: 10u64, - token_code_id: 11u64, - pool_creation_fee: Asset { - amount: Uint128::new(1000000u128), - info: AssetInfo::NativeToken { - denom: "uusd".to_string(), - }, - }, - }; - let env = mock_env(); - let info = mock_info("owner", &[]); - let res = instantiate(deps.as_mut(), env, info, msg).unwrap(); - assert_eq!(0, res.messages.len()); - - // Create the Pair - let asset_infos = [ - AssetInfo::Token { - contract_addr: "asset0000".to_string(), - }, - AssetInfo::NativeToken { - denom: "uusd".to_string(), - }, - ]; - - let msg = ExecuteMsg::CreatePair { - asset_infos: asset_infos.to_vec(), - pool_fees: PoolFee { - protocol_fee: Fee { - share: Decimal::percent(1u64), - }, - swap_fee: Fee { - share: Decimal::percent(1u64), - }, - burn_fee: Fee { - share: Decimal::zero(), - }, - }, - pair_type: PairType::ConstantProduct, - token_factory_lp: false, - pair_identifier: None, - }; - - let env = mock_env(); - - let info = mock_info( - "addr0000", - &[Coin { - denom: "uusd".to_string(), - amount: Uint128::new(1000000u128), - }], - ); - - let res = execute(deps.as_mut(), env.clone(), info.clone(), msg).unwrap(); - - // unsuccessfully providing liquidity since share becomes zero, MINIMUM_LIQUIDITY_AMOUNT provided - let msg = ExecuteMsg::ProvideLiquidity { - assets: [ - Asset { - info: AssetInfo::Token { - contract_addr: "asset0000".to_string(), - }, - amount: MINIMUM_LIQUIDITY_AMOUNT, - }, - Asset { - info: AssetInfo::NativeToken { - denom: "uusd".to_string(), - }, - amount: MINIMUM_LIQUIDITY_AMOUNT, - }, - ] - .to_vec(), - slippage_tolerance: None, - receiver: None, - pair_identifier: "0".to_string(), - }; - - let env = mock_env(); - let info = mock_info( - "addr0000", - &[Coin { - denom: "uusd".to_string(), - amount: MINIMUM_LIQUIDITY_AMOUNT, - }], - ); - let res = execute(deps.as_mut(), env, info, msg).unwrap_err(); - match res { - ContractError::InvalidInitialLiquidityAmount { .. } => {} - _ => { - println!("{:?}", res); - panic!("should return ContractError::InvalidInitialLiquidityAmount") - } - } - - // successfully provide liquidity for the exist pool - let msg = ExecuteMsg::ProvideLiquidity { - assets: [ - Asset { - info: AssetInfo::Token { - contract_addr: "asset0000".to_string(), - }, - amount: Uint128::from(2_000u128), - }, - Asset { - info: AssetInfo::NativeToken { - denom: "uusd".to_string(), - }, - amount: Uint128::from(2_000u128), - }, - ] - .to_vec(), - slippage_tolerance: None, - receiver: None, - pair_identifier: "0".to_string(), - }; - - let env = mock_env(); - let info = mock_info( - "addr0000", - &[Coin { - denom: "uusd".to_string(), - amount: Uint128::from(2_000u128), - }], - ); - let res = execute(deps.as_mut(), env, info, msg).unwrap(); - - assert_eq!(res.messages.len(), 2usize); - - let transfer_from_msg = res.messages.get(0).expect("no message"); - let mint_initial_lp_msg = res.messages.get(1).expect("no message"); - let mint_msg = res.messages.get(2).expect("no message"); - assert_eq!( - transfer_from_msg, - &SubMsg::new(CosmosMsg::Wasm(WasmMsg::Execute { - contract_addr: "asset0000".to_string(), - msg: to_binary(&Cw20ExecuteMsg::TransferFrom { - owner: "addr0000".to_string(), - recipient: MOCK_CONTRACT_ADDR.to_string(), - amount: Uint128::from(2_000u128), - }) - .unwrap(), - funds: vec![], - })) - ); - assert_eq!( - mint_initial_lp_msg, - &SubMsg::new(CosmosMsg::Wasm(WasmMsg::Execute { - contract_addr: TEMP_CONTRACT_ADDR.clone(), - msg: to_binary(&Cw20ExecuteMsg::Mint { - recipient: "cosmos2contract".to_string(), - amount: MINIMUM_LIQUIDITY_AMOUNT, - }) - .unwrap(), - funds: vec![], - })) - ); - assert_eq!( - mint_msg, - &SubMsg::new(CosmosMsg::Wasm(WasmMsg::Execute { - contract_addr: TEMP_CONTRACT_ADDR.clone(), - msg: to_binary(&Cw20ExecuteMsg::Transfer { - recipient: "addr0000".to_string(), - amount: MINIMUM_LIQUIDITY_AMOUNT, - }) - .unwrap(), - funds: vec![], - })) - ); - - // provide more liquidity 1:2, which is not proportional to 1:1, - // then it must accept 1:1 and treat left amount as donation - deps.querier.with_balance(&[( - &MOCK_CONTRACT_ADDR.to_string(), - vec![Coin { - denom: "uusd".to_string(), - amount: Uint128::from( - 200u128 + 200u128, /* user deposit must be pre-applied */ - ), - }], - )]); - - deps.querier.with_token_balances(&[ - ( - &TEMP_CONTRACT_ADDR.to_string(), - &[(&MOCK_CONTRACT_ADDR.to_string(), &Uint128::from(100u128))], - ), - ( - &"asset0000".to_string(), - &[(&MOCK_CONTRACT_ADDR.to_string(), &Uint128::from(200u128))], - ), - ]); - - let msg = ExecuteMsg::ProvideLiquidity { - assets: [ - Asset { - info: AssetInfo::Token { - contract_addr: "asset0000".to_string(), - }, - amount: Uint128::from(100u128), - }, - Asset { - info: AssetInfo::NativeToken { - denom: "uusd".to_string(), - }, - amount: Uint128::from(200u128), - }, - ] - .to_vec(), - slippage_tolerance: None, - receiver: Some("staking0000".to_string()), // try changing receiver - pair_identifier: 1.to_string(), - }; - - let env = mock_env(); - let info = mock_info( - "addr0000", - &[Coin { - denom: "uusd".to_string(), - amount: Uint128::from(200u128), - }], - ); - - // only accept 100, then 50 share will be generated with 100 * (100 / 200) - let res: Response = execute(deps.as_mut(), env, info, msg).unwrap(); - assert_eq!(res.messages.len(), 3usize); - - let transfer_from_msg = res.messages.get(0).expect("no message"); - let mint_msg = res.messages.get(1).expect("no message"); - assert_eq!( - transfer_from_msg, - &SubMsg::new(CosmosMsg::Wasm(WasmMsg::Execute { - contract_addr: "asset0000".to_string(), - msg: to_binary(&Cw20ExecuteMsg::TransferFrom { - owner: "addr0000".to_string(), - recipient: MOCK_CONTRACT_ADDR.to_string(), - amount: Uint128::from(200u128), - }) - .unwrap(), - funds: vec![], - })) - ); - assert_eq!( - mint_msg, - &SubMsg::new(CosmosMsg::Wasm(WasmMsg::Execute { - contract_addr: TEMP_CONTRACT_ADDR.to_string(), - msg: to_binary(&Cw20ExecuteMsg::Mint { - recipient: "cosmos2contract".to_string(), // LP tokens sent to specified receiver - amount: Uint128::from(33u128), - }) - .unwrap(), - funds: vec![], - })) - ); - - // check wrong argument - let msg = ExecuteMsg::ProvideLiquidity { - assets: [ - Asset { - info: AssetInfo::Token { - contract_addr: "asset0000".to_string(), - }, - amount: Uint128::from(100u128), - }, - Asset { - info: AssetInfo::NativeToken { - denom: "uusd".to_string(), - }, - amount: Uint128::from(50u128), - }, - ] - .to_vec(), - slippage_tolerance: None, - receiver: None, - pair_identifier: 1.to_string(), - }; - - let env = mock_env(); - let info = mock_info( - "addr0000", - &[Coin { - denom: "uusd".to_string(), - amount: Uint128::from(100u128), - }], - ); - let res = execute(deps.as_mut(), env, info, msg).unwrap_err(); - match res { - ContractError::Std(StdError::GenericErr { msg, .. }) => assert_eq!( - msg, - "Native token balance mismatch between the argument and the transferred".to_string() - ), - _ => { - panic!("Must return generic error"); - } - } - - // initialize token balance to 1:1 - deps.querier.with_balance(&[( - &MOCK_CONTRACT_ADDR.to_string(), - vec![Coin { - denom: "uusd".to_string(), - amount: Uint128::from( - 100u128 + 100u128, /* user deposit must be pre-applied */ - ), - }], - )]); - - deps.querier.with_token_balances(&[ - ( - &"liquidity0000".to_string(), - &[(&MOCK_CONTRACT_ADDR.to_string(), &Uint128::from(100u128))], - ), - ( - &"asset0000".to_string(), - &[(&MOCK_CONTRACT_ADDR.to_string(), &Uint128::from(200u128))], - ), - ]); - - // failed because the price is under slippage_tolerance - let msg = ExecuteMsg::ProvideLiquidity { - assets: [ - Asset { - info: AssetInfo::Token { - contract_addr: "asset0000".to_string(), - }, - amount: Uint128::from(98u128), - }, - Asset { - info: AssetInfo::NativeToken { - denom: "uusd".to_string(), - }, - amount: Uint128::from(100u128), - }, - ] - .to_vec(), - slippage_tolerance: Some(Decimal::percent(1)), - receiver: None, - pair_identifier: 1.to_string(), - }; - - let env = mock_env(); - let info = mock_info( - "addr0001", - &[Coin { - denom: "uusd".to_string(), - amount: Uint128::from(100u128), - }], - ); - - deps.querier.with_token_balances(&[ - ( - &TEMP_CONTRACT_ADDR.to_string(), - &[(&MOCK_CONTRACT_ADDR.to_string(), &Uint128::from(100u128))], - ), - ( - &"asset0000".to_string(), - &[(&MOCK_CONTRACT_ADDR.to_string(), &Uint128::from(100u128))], - ), - ]); - let res = execute(deps.as_mut(), env, info, msg).unwrap_err(); - match res { - ContractError::MaxSlippageAssertion {} => {} - _ => panic!("DO NOT ENTER HERE"), - } - - // initialize token balance to 1:1 - deps.querier.with_balance(&[( - &MOCK_CONTRACT_ADDR.to_string(), - vec![Coin { - denom: "uusd".to_string(), - amount: Uint128::from(100u128 + 98u128 /* user deposit must be pre-applied */), - }], - )]); - - // failed because the price is under slippage_tolerance - let msg = ExecuteMsg::ProvideLiquidity { - assets: [ - Asset { - info: AssetInfo::Token { - contract_addr: "asset0000".to_string(), - }, - amount: Uint128::from(100u128), - }, - Asset { - info: AssetInfo::NativeToken { - denom: "uusd".to_string(), - }, - amount: Uint128::from(98u128), - }, - ] - .to_vec(), - slippage_tolerance: Some(Decimal::percent(1)), - receiver: None, - pair_identifier: 1.to_string(), - }; - - let env = mock_env(); - let info = mock_info( - "addr0001", - &[Coin { - denom: "uusd".to_string(), - amount: Uint128::from(98u128), - }], - ); - let res = execute(deps.as_mut(), env, info, msg).unwrap_err(); - match res { - ContractError::MaxSlippageAssertion {} => {} - _ => panic!("DO NOT ENTER HERE"), - } - - // initialize token balance to 1:1 - deps.querier.with_balance(&[( - &MOCK_CONTRACT_ADDR.to_string(), - vec![Coin { - denom: "uusd".to_string(), - amount: Uint128::from( - 100u128 + 100u128, /* user deposit must be pre-applied */ - ), - }], - )]); - - deps.querier.with_token_balances(&[ - ( - &TEMP_CONTRACT_ADDR.clone(), - &[(&MOCK_CONTRACT_ADDR.to_string(), &Uint128::from(100u128))], - ), - ( - &"asset0000".to_string(), - &[ - (&"addr0001".to_string(), &Uint128::from(100u128)), - (&MOCK_CONTRACT_ADDR.to_string(), &Uint128::from(100u128)), - ], - ), - ]); - - // successfully provides - let msg = ExecuteMsg::ProvideLiquidity { - assets: [ - Asset { - info: AssetInfo::Token { - contract_addr: "asset0000".to_string(), - }, - amount: Uint128::from(99u128), - }, - Asset { - info: AssetInfo::NativeToken { - denom: "uusd".to_string(), - }, - amount: Uint128::from(100u128), - }, - ] - .to_vec(), - slippage_tolerance: Some(Decimal::percent(2)), - receiver: None, - pair_identifier: 1.to_string(), - }; - - let env = mock_env(); - let info = mock_info( - "addr0001", - &[Coin { - denom: "uusd".to_string(), - amount: Uint128::from(100u128), - }], - ); - let _res = execute(deps.as_mut(), env, info, msg).unwrap(); - - // initialize token balance to 1:1 - deps.querier.with_balance(&[( - &MOCK_CONTRACT_ADDR.to_string(), - vec![Coin { - denom: "uusd".to_string(), - amount: Uint128::from(100u128 + 99u128 /* user deposit must be pre-applied */), - }], - )]); - - // successfully provides - let msg = ExecuteMsg::ProvideLiquidity { - assets: [ - Asset { - info: AssetInfo::Token { - contract_addr: "asset0000".to_string(), - }, - amount: Uint128::from(100u128), - }, - Asset { - info: AssetInfo::NativeToken { - denom: "uusd".to_string(), - }, - amount: Uint128::from(99u128), - }, - ] - .to_vec(), - slippage_tolerance: Some(Decimal::percent(2)), - receiver: None, - pair_identifier: 1.to_string(), - }; - - let env = mock_env(); - let info = mock_info( - "addr0001", - &[Coin { - denom: "uusd".to_string(), - amount: Uint128::from(99u128), - }], - ); - let _res = execute(deps.as_mut(), env, info, msg).unwrap(); -} - -#[test] -fn provide_liquidity_zero_amount() { - let mut deps = mock_dependencies(&[Coin { - denom: "uusd".to_string(), - amount: Uint128::from(2_000u128), - }]); - - // Note: In order to support and write tests sooner I override the MockAPI in mock_querier with a SimpleMockAPI - // The effect is I can continue testing with instantiate2 but now an addr like liquidity0000 becomes TEMP_CONTRACT_ADDR - // This likely breaks tests elsewhere - let TEMP_CONTRACT_ADDR: String = - "contract757573642d6d4141504c61646472303030303132333435".to_string(); - - deps.querier.with_token_balances(&[ - ( - &TEMP_CONTRACT_ADDR.clone(), - &[(&MOCK_CONTRACT_ADDR.to_string(), &Uint128::zero())], - ), - (&"asset0000".to_string(), &[]), - ]); - - deps.querier - .with_pool_factory(&[], &[("uusd".to_string(), 6u8)]); - - // Instantiate contract - let msg = SingleSwapInstantiateMsg { - fee_collector_addr: "fee_collector_addr".to_string(), - owner: "owner".to_string(), - pair_code_id: 10u64, - token_code_id: 11u64, - pool_creation_fee: Asset { - amount: Uint128::new(1000000u128), - info: AssetInfo::NativeToken { - denom: "uusd".to_string(), - }, - }, - }; - let env = mock_env(); - let info = mock_info("owner", &[]); - let res = instantiate(deps.as_mut(), env, info, msg).unwrap(); - assert_eq!(0, res.messages.len()); - - // Create the Pair - let asset_infos = [ - AssetInfo::Token { - contract_addr: "asset0000".to_string(), - }, - AssetInfo::NativeToken { - denom: "uusd".to_string(), - }, - ]; - - let msg = ExecuteMsg::CreatePair { - asset_infos: asset_infos.to_vec(), - pool_fees: PoolFee { - protocol_fee: Fee { - share: Decimal::percent(1u64), - }, - swap_fee: Fee { - share: Decimal::percent(1u64), - }, - burn_fee: Fee { - share: Decimal::zero(), - }, - }, - pair_type: PairType::ConstantProduct, - token_factory_lp: false, - pair_identifier: None, - }; - - let env = mock_env(); - - let info = mock_info( - "addr0000", - &[Coin { - denom: "uusd".to_string(), - amount: Uint128::new(1000000u128), - }], - ); - - let res = execute(deps.as_mut(), env.clone(), info.clone(), msg).unwrap(); - - // Instantiate contract - let msg = SingleSwapInstantiateMsg { - fee_collector_addr: "fee_collector_addr".to_string(), - owner: "owner".to_string(), - pair_code_id: 10u64, - token_code_id: 11u64, - pool_creation_fee: Asset { - amount: Uint128::new(1000000u128), - info: AssetInfo::NativeToken { - denom: "uusd".to_string(), - }, - }, - }; - let env = mock_env(); - let info = mock_info("owner", &[]); - let res = instantiate(deps.as_mut(), env, info, msg).unwrap(); - assert_eq!(0, res.messages.len()); - - // provide invalid (zero) liquidity - let msg = ExecuteMsg::ProvideLiquidity { - assets: [ - Asset { - info: AssetInfo::Token { - contract_addr: "asset0000".to_string(), - }, - amount: Uint128::zero(), - }, - Asset { - info: AssetInfo::NativeToken { - denom: "uusd".to_string(), - }, - amount: Uint128::from(100u128), - }, - ] - .to_vec(), - slippage_tolerance: None, - receiver: None, - pair_identifier: "0".to_string(), - }; - - let env = mock_env(); - let info = mock_info( - "addr0000", - &[Coin { - denom: "uusd".to_string(), - amount: Uint128::from(100u128), - }], - ); - let res = execute(deps.as_mut(), env, info, msg); - - match res { - Ok(_) => panic!("should return ContractError::InvalidZeroAmount"), - Err(ContractError::InvalidZeroAmount {}) => {} - _ => { - panic!("should return ContractError::InvalidZeroAmount") - } - } -} - -#[test] -fn provide_liquidity_invalid_minimum_lp_amount() { - let mut deps = mock_dependencies(&[Coin { - denom: "uusd".to_string(), - amount: Uint128::from(999u128), - }]); - - // Note: In order to support and write tests sooner I override the MockAPI in mock_querier with a SimpleMockAPI - // The effect is I can continue testing with instantiate2 but now an addr like liquidity0000 becomes TEMP_CONTRACT_ADDR - // This likely breaks tests elsewhere - let TEMP_CONTRACT_ADDR: String = - "contract6d4141504c2d7575736461646472303030303132333435".to_string(); - - deps.querier.with_token_balances(&[ - ( - &TEMP_CONTRACT_ADDR.to_string(), - &[(&MOCK_CONTRACT_ADDR.to_string(), &Uint128::zero())], - ), - (&"asset0001".to_string(), &[]), - ]); - deps.querier - .with_pool_factory(&[], &[("uusd".to_string(), 6u8)]); - - // Instantiate contract - let msg = SingleSwapInstantiateMsg { - fee_collector_addr: "fee_collector_addr".to_string(), - owner: "owner".to_string(), - pair_code_id: 10u64, - token_code_id: 11u64, - pool_creation_fee: Asset { - amount: Uint128::new(1000000u128), - info: AssetInfo::NativeToken { - denom: "uusd".to_string(), - }, - }, - }; - let env = mock_env(); - let info = mock_info("owner", &[]); - let res = instantiate(deps.as_mut(), env, info, msg).unwrap(); - assert_eq!(0, res.messages.len()); - - let asset_infos = [ - AssetInfo::Token { - contract_addr: "asset0001".to_string(), - }, - AssetInfo::NativeToken { - denom: "uusd".to_string(), - }, - ]; - - let msg = ExecuteMsg::CreatePair { - asset_infos: asset_infos.to_vec(), - pool_fees: PoolFee { - protocol_fee: Fee { - share: Decimal::percent(1u64), - }, - swap_fee: Fee { - share: Decimal::percent(1u64), - }, - burn_fee: Fee { - share: Decimal::zero(), - }, - }, - pair_type: PairType::ConstantProduct, - token_factory_lp: false, - pair_identifier: None, - }; - - let env = mock_env(); - let info = mock_info( - "addr0000", - &[Coin { - denom: "uusd".to_string(), - amount: Uint128::new(1000000u128), - }], - ); - let res = execute(deps.as_mut(), env.clone(), info.clone(), msg).unwrap(); - assert_eq!( - res.attributes, - vec![ - attr("action", "create_pair"), - attr("pair", "mAAPL-uusd"), - attr("pair_label", "mAAPL-uusd"), - attr("pair_type", "ConstantProduct"), - ] - ); - - // successfully provide liquidity for the exist pool - let msg = ExecuteMsg::ProvideLiquidity { - assets: [ - Asset { - info: AssetInfo::Token { - contract_addr: "asset0001".to_string(), - }, - amount: (MINIMUM_LIQUIDITY_AMOUNT - Uint128::one()), - }, - Asset { - info: AssetInfo::NativeToken { - denom: "uusd".to_string(), - }, - amount: Uint128::from(100u128), - }, - ] - .to_vec(), - slippage_tolerance: None, - receiver: None, - pair_identifier: 1.to_string(), - }; - - let env = mock_env(); - let info = mock_info( - "addr0000", - &[Coin { - denom: "uusd".to_string(), - amount: Uint128::from(100u128), - }], - ); - let res = execute(deps.as_mut(), env, info, msg); - - match res { - Ok(_) => panic!("should return ContractError::InvalidInitialLiquidityAmount"), - Err(ContractError::InvalidInitialLiquidityAmount { .. }) => {} - e => { - println!("{:?}", e.unwrap()); - panic!("should return ContractError::InvalidInitialLiquidityAmount"); - } - } -} - -#[cfg(feature = "token_factory")] -#[test] -fn provide_liquidity_tokenfactory_lp() { - let lp_denom = format!("{}/{MOCK_CONTRACT_ADDR}/{LP_SYMBOL}", "factory"); - - let mut deps = mock_dependencies(&[ - Coin { - denom: "uusd".to_string(), - amount: Uint128::from(2_000u128), - }, - Coin { - denom: "uwhale".to_string(), - amount: Uint128::from(2_000u128), - }, - Coin { - denom: lp_denom.clone(), - amount: Uint128::zero(), - }, - ]); - - deps.querier - .with_token_balances(&[(&"asset0000".to_string(), &[])]); - - // Instantiate contract - let msg = SingleSwapInstantiateMsg { - fee_collector_addr: "fee_collector_addr".to_string(), - owner: "owner".to_string(), - pair_code_id: 10u64, - token_code_id: 11u64, - pool_creation_fee: vec![Asset { - amount: Uint128::new(1000000u128), - info: AssetInfo::NativeToken { - denom: "uusd".to_string(), - }, - }], - }; - let env = mock_env(); - let info = mock_info("owner", &[]); - let res = instantiate(deps.as_mut(), env, info, msg).unwrap(); - assert_eq!(0, res.messages.len()); - // successfully provide liquidity for the exist pool - let msg = ExecuteMsg::ProvideLiquidity { - assets: [ - Asset { - info: AssetInfo::NativeToken { - denom: "uwhale".to_string(), - }, - amount: Uint128::from(2_000u128), - }, - Asset { - info: AssetInfo::NativeToken { - denom: "uusd".to_string(), - }, - amount: Uint128::from(2_000u128), - }, - ] - .to_vec(), - slippage_tolerance: None, - receiver: None, - }; - - let env = mock_env(); - let info = mock_info( - "addr0000", - &[ - Coin { - denom: "uusd".to_string(), - amount: Uint128::from(2_000u128), - }, - Coin { - denom: "uwhale".to_string(), - amount: Uint128::from(2_000u128), - }, - ], - ); - let res = execute(deps.as_mut(), env, info, msg).unwrap(); - - assert_eq!(res.messages.len(), 3usize); - - let mint_initial_lp_msg = res.messages.get(0).expect("no message").clone().msg; - let mint_msg = res.messages.get(1).expect("no message").clone().msg; - let bank_send_msg = res.messages.get(2).expect("no message").clone().msg; - - let mint_initial_lp_msg_expected = >::into(MsgMint { - sender: MOCK_CONTRACT_ADDR.to_string(), - amount: Some(pool_network::denom::Coin { - denom: lp_denom.clone(), - amount: MINIMUM_LIQUIDITY_AMOUNT.to_string(), - }), - }); - - let mint_msg_expected = >::into(MsgMint { - sender: MOCK_CONTRACT_ADDR.to_string(), - amount: Some(pool_network::denom::Coin { - denom: lp_denom.clone(), - amount: "1000".to_string(), - }), - }); - - let bank_send_msg_expected = CosmosMsg::Bank(BankMsg::Send { - to_address: "addr0000".to_string(), - amount: vec![coin(1000u128, lp_denom.clone())], - }); - - assert_eq!(mint_initial_lp_msg, mint_initial_lp_msg_expected); - - assert_eq!(mint_msg, mint_msg_expected); - - assert_eq!(bank_send_msg, bank_send_msg_expected); -} diff --git a/contracts/liquidity_hub/pool-manager/src/tests/unit_tests/swap.rs b/contracts/liquidity_hub/pool-manager/src/tests/unit_tests/swap.rs deleted file mode 100644 index 478516e53..000000000 --- a/contracts/liquidity_hub/pool-manager/src/tests/unit_tests/swap.rs +++ /dev/null @@ -1,395 +0,0 @@ -use cosmwasm_std::testing::{mock_env, mock_info, MOCK_CONTRACT_ADDR}; -use cosmwasm_std::{ - attr, to_binary, Coin, CosmosMsg, Decimal, Reply, ReplyOn, Response, StdError, SubMsg, - SubMsgResponse, SubMsgResult, Uint128, WasmMsg, -}; -#[cfg(feature = "token_factory")] -use cosmwasm_std::{coin, BankMsg}; -use cw20::Cw20ExecuteMsg; -use white_whale_std::fee::Fee; -#[cfg(feature = "token_factory")] -use white_whale_std::lp_common::LP_SYMBOL; - -use crate::tests::mock_querier::mock_dependencies; -#[cfg(feature = "token_factory")] -use white_whale_std::pool_network; -use white_whale_std::pool_network::asset::{Asset, AssetInfo, PairType, MINIMUM_LIQUIDITY_AMOUNT}; -#[cfg(feature = "token_factory")] -use white_whale_std::pool_network::denom::MsgMint; -use white_whale_std::pool_network::pair::PoolFee; -// use white_whale_std::pool_network::pair::{ExecuteMsg, InstantiateMsg, PoolFee}; -use crate::contract::{execute, instantiate}; -use crate::error::ContractError; -use white_whale_std::pool_manager::ExecuteMsg; -use white_whale_std::pool_manager::InstantiateMsg as SingleSwapInstantiateMsg; - -#[test] -fn try_native_to_token() { - let total_share = Uint128::from(30000000000u128); - let asset_pool_amount = Uint128::from(20000000000u128); - let collateral_pool_amount = Uint128::from(30000000000u128); - let exchange_rate: Decimal = Decimal::from_ratio(asset_pool_amount, collateral_pool_amount); - let offer_amount = Uint128::from(1500000000u128); - - let mut deps = mock_dependencies(&[Coin { - denom: "uusd".to_string(), - amount: collateral_pool_amount + offer_amount, - /* user deposit must be pre-applied */ - }]); - - // Note: In order to support and write tests sooner I override the MockAPI in mock_querier with a SimpleMockAPI - // The effect is I can continue testing with instantiate2 but now an addr like liquidity0000 becomes TEMP_CONTRACT_ADDR - // This likely breaks tests elsewhere - let TEMP_CONTRACT_ADDR: String = - "contract757573642d6d4141504c61646472303030303132333435".to_string(); - - deps.querier.with_token_balances(&[ - ( - &TEMP_CONTRACT_ADDR.clone(), - &[(&MOCK_CONTRACT_ADDR.to_string(), &total_share)], - ), - ( - &"asset0000".to_string(), - &[(&MOCK_CONTRACT_ADDR.to_string(), &asset_pool_amount)], - ), - ]); - deps.querier - .with_pool_factory(&[], &[("uusd".to_string(), 6u8)]); - - // Instantiate contract - let msg = SingleSwapInstantiateMsg { - fee_collector_addr: "fee_collector_addr".to_string(), - owner: "owner".to_string(), - pair_code_id: 10u64, - token_code_id: 11u64, - pool_creation_fee: Asset { - amount: Uint128::new(1000000u128), - info: AssetInfo::NativeToken { - denom: "uusd".to_string(), - }, - }, - }; - let env = mock_env(); - let info = mock_info("owner", &[]); - let res = instantiate(deps.as_mut(), env, info, msg).unwrap(); - assert_eq!(0, res.messages.len()); - - // Create the Pair - let asset_infos = [ - AssetInfo::NativeToken { - denom: "uusd".to_string(), - }, - AssetInfo::Token { - contract_addr: "asset0000".to_string(), - }, - ]; - - let msg = ExecuteMsg::CreatePair { - asset_infos: asset_infos.to_vec(), - pool_fees: PoolFee { - protocol_fee: Fee { - share: Decimal::from_ratio(1u128, 1000u128), - }, - swap_fee: Fee { - share: Decimal::from_ratio(3u128, 1000u128), - }, - burn_fee: Fee { - share: Decimal::from_ratio(1u128, 1000u128), - }, - }, - pair_type: PairType::ConstantProduct, - token_factory_lp: false, - pair_identifier: None, - }; - - let env = mock_env(); - - let info = mock_info( - "addr0000", - &[Coin { - denom: "uusd".to_string(), - amount: Uint128::new(1000000u128), - }], - ); - - let res = execute(deps.as_mut(), env.clone(), info.clone(), msg).unwrap(); - - // normal swap - let msg = ExecuteMsg::Swap { - offer_asset: Asset { - info: AssetInfo::NativeToken { - denom: "uusd".to_string(), - }, - amount: offer_amount, - }, - ask_asset: AssetInfo::Token { - contract_addr: "asset0000".to_string(), - }, - belief_price: None, - max_spread: Some(Decimal::percent(5)), - to: None, - pair_identifier: "0".to_string(), - }; - let env = mock_env(); - let info = mock_info( - "addr0000", - &[Coin { - denom: "uusd".to_string(), - amount: offer_amount, - }], - ); - let res = execute(deps.as_mut(), env, info, msg).unwrap(); - assert_eq!(res.messages.len(), 1); - - let msg_transfer = res.messages.get(0).expect("no message"); - - // current price is 1.5, so expected return without spread is 1000 - // ask_amount = ((ask_pool - accrued protocol fees) * offer_amount / (offer_pool - accrued protocol fees + offer_amount)) - // 952.380952 = (20000 - 0) * 1500 / (30000 - 0 + 1500) - swap_fee - protocol_fee - burn_fee - // TODO: Returned amount is 904545457 and spread is up to 5% -- 43290043. Investigate this - let expected_ret_amount = Uint128::from(952_380_952u128); - let expected_spread_amount = (offer_amount * exchange_rate) - .checked_sub(expected_ret_amount) - .unwrap(); - let expected_swap_fee_amount = expected_ret_amount.multiply_ratio(3u128, 1000u128); // 0.3% - let expected_protocol_fee_amount = expected_ret_amount.multiply_ratio(1u128, 1000u128); // 0.1% - let expected_burn_fee_amount = expected_ret_amount.multiply_ratio(1u128, 1000u128); // 0.1% - let expected_return_amount = expected_ret_amount - .checked_sub(expected_swap_fee_amount) - .unwrap() - .checked_sub(expected_protocol_fee_amount) - .unwrap() - .checked_sub(expected_burn_fee_amount) - .unwrap(); - - // since there is a burn_fee on the PoolFee, check burn message - // since we swapped to a cw20 token, the burn message should be a Cw20ExecuteMsg::Burn - let expected_burn_msg = SubMsg { - id: 0, - msg: CosmosMsg::Wasm(WasmMsg::Execute { - contract_addr: "asset0000".to_string(), - msg: to_binary(&Cw20ExecuteMsg::Burn { - amount: expected_burn_fee_amount, - }) - .unwrap(), - funds: vec![], - }), - gas_limit: None, - reply_on: ReplyOn::Never, - }; - println!("{:?}", exchange_rate); - println!("{:?}", offer_amount * exchange_rate); - assert_eq!(res.messages.last().unwrap().clone(), expected_burn_msg); - - // // as we swapped native to token, we accumulate the protocol fees in token - // let protocol_fees_for_token = query_fees( - // deps.as_ref(), - // Some("asset0000".to_string()), - // None, - // COLLECTED_PROTOCOL_FEES, - // Some(ALL_TIME_COLLECTED_PROTOCOL_FEES), - // ) - // .unwrap() - // .fees; - // assert_eq!( - // protocol_fees_for_token.first().unwrap().amount, - // expected_protocol_fee_amount - // ); - // let burned_fees_for_token = query_fees( - // deps.as_ref(), - // Some("asset0000".to_string()), - // None, - // ALL_TIME_BURNED_FEES, - // None, - // ) - // .unwrap() - // .fees; - // assert_eq!( - // burned_fees_for_token.first().unwrap().amount, - // expected_burn_fee_amount - // ); - // let protocol_fees_for_native = query_fees( - // deps.as_ref(), - // Some("uusd".to_string()), - // None, - // COLLECTED_PROTOCOL_FEES, - // Some(ALL_TIME_COLLECTED_PROTOCOL_FEES), - // ) - // .unwrap() - // .fees; - // assert_eq!( - // protocol_fees_for_native.first().unwrap().amount, - // Uint128::zero() - // ); - // let burned_fees_for_native = query_fees( - // deps.as_ref(), - // Some("uusd".to_string()), - // None, - // ALL_TIME_BURNED_FEES, - // None, - // ) - // .unwrap() - // .fees; - // assert_eq!( - // burned_fees_for_native.first().unwrap().amount, - // Uint128::zero() - // ); - - // // check simulation res, reset values pre-swap to check simulation - // deps.querier.with_balance(&[( - // &MOCK_CONTRACT_ADDR.to_string(), - // vec![Coin { - // denom: "uusd".to_string(), - // amount: collateral_pool_amount, - // /* user deposit must be pre-applied */ - // }], - // )]); - - // // reset protocol fees so the simulation returns same values as the actual swap - // COLLECTED_PROTOCOL_FEES - // .save( - // &mut deps.storage, - // &vec![ - // Asset { - // info: AssetInfo::NativeToken { - // denom: "uusd".to_string(), - // }, - // amount: Uint128::zero(), - // }, - // Asset { - // info: AssetInfo::Token { - // contract_addr: "asset0000".to_string(), - // }, - // amount: Uint128::zero(), - // }, - // ], - // ) - // .unwrap(); - - // let simulation_res: SimulationResponse = from_binary( - // &query( - // deps.as_ref(), - // mock_env(), - // QueryMsg::Simulation { - // offer_asset: Asset { - // info: AssetInfo::NativeToken { - // denom: "uusd".to_string(), - // }, - // amount: offer_amount, - // }, - // }, - // ) - // .unwrap(), - // ) - // .unwrap(); - - // assert_eq!(expected_return_amount, simulation_res.return_amount); - // assert_eq!(expected_swap_fee_amount, simulation_res.swap_fee_amount); - // assert_eq!(expected_burn_fee_amount, simulation_res.burn_fee_amount); - // assert_eq!(expected_spread_amount, simulation_res.spread_amount); - // assert_eq!( - // expected_protocol_fee_amount, - // simulation_res.protocol_fee_amount - // ); - - // // reset protocol fees so the simulation returns same values as the actual swap - // COLLECTED_PROTOCOL_FEES - // .save( - // &mut deps.storage, - // &vec![ - // Asset { - // info: AssetInfo::NativeToken { - // denom: "uusd".to_string(), - // }, - // amount: Uint128::zero(), - // }, - // Asset { - // info: AssetInfo::Token { - // contract_addr: "asset0000".to_string(), - // }, - // amount: Uint128::zero(), - // }, - // ], - // ) - // .unwrap(); - - // let reverse_simulation_res: ReverseSimulationResponse = from_binary( - // &query( - // deps.as_ref(), - // mock_env(), - // QueryMsg::ReverseSimulation { - // ask_asset: Asset { - // info: AssetInfo::Token { - // contract_addr: "asset0000".to_string(), - // }, - // amount: expected_return_amount, - // }, - // }, - // ) - // .unwrap(), - // ) - // .unwrap(); - - // assert!( - // (offer_amount.u128() as i128 - reverse_simulation_res.offer_amount.u128() as i128).abs() - // < 3i128 - // ); - // assert!( - // (expected_swap_fee_amount.u128() as i128 - // - reverse_simulation_res.swap_fee_amount.u128() as i128) - // .abs() - // < 3i128 - // ); - // assert!( - // (expected_spread_amount.u128() as i128 - // - reverse_simulation_res.spread_amount.u128() as i128) - // .abs() - // < 3i128 - // ); - // assert!( - // (expected_protocol_fee_amount.u128() as i128 - // - reverse_simulation_res.protocol_fee_amount.u128() as i128) - // .abs() - // < 3i128 - // ); - // assert!( - // (expected_burn_fee_amount.u128() as i128 - // - reverse_simulation_res.burn_fee_amount.u128() as i128) - // .abs() - // < 3i128 - // ); - - // assert_eq!( - // res.attributes, - // vec![ - // attr("action", "swap"), - // attr("sender", "addr0000"), - // attr("receiver", "addr0000"), - // attr("offer_asset", "uusd"), - // attr("ask_asset", "asset0000"), - // attr("offer_amount", offer_amount.to_string()), - // attr("return_amount", expected_return_amount.to_string()), - // attr("spread_amount", expected_spread_amount.to_string()), - // attr("swap_fee_amount", expected_swap_fee_amount.to_string()), - // attr( - // "protocol_fee_amount", - // expected_protocol_fee_amount.to_string(), - // ), - // attr("burn_fee_amount", expected_burn_fee_amount.to_string()), - // attr("swap_type", "ConstantProduct"), - // ] - // ); - - // assert_eq!( - // &SubMsg::new(CosmosMsg::Wasm(WasmMsg::Execute { - // contract_addr: "asset0000".to_string(), - // msg: to_binary(&Cw20ExecuteMsg::Transfer { - // recipient: "addr0000".to_string(), - // amount: expected_return_amount, - // }) - // .unwrap(), - // funds: vec![], - // })), - // msg_transfer, - // ); -} diff --git a/contracts/liquidity_hub/pool-manager/src/tests/unit_tests/withdrawals.rs b/contracts/liquidity_hub/pool-manager/src/tests/unit_tests/withdrawals.rs deleted file mode 100644 index 681b9e68d..000000000 --- a/contracts/liquidity_hub/pool-manager/src/tests/unit_tests/withdrawals.rs +++ /dev/null @@ -1,654 +0,0 @@ -use cosmwasm_std::testing::{mock_env, mock_info, MOCK_CONTRACT_ADDR}; -use cosmwasm_std::{ - attr, to_binary, BankMsg, Coin, CosmosMsg, Decimal, Reply, Response, SubMsg, SubMsgResponse, - SubMsgResult, Uint128, WasmMsg, -}; -use cw20::{Cw20ExecuteMsg, Cw20ReceiveMsg}; - -#[cfg(feature = "token_factory")] -use cosmwasm_std::coin; -use white_whale_std::fee::Fee; -#[cfg(feature = "token_factory")] -use white_whale_std::lp_common::LP_SYMBOL; -#[cfg(feature = "token_factory")] -use white_whale_std::pool_network; -use white_whale_std::pool_network::asset::{AssetInfo, PairType}; -#[cfg(feature = "token_factory")] -use white_whale_std::pool_network::denom::MsgBurn; -use white_whale_std::pool_network::mock_querier::mock_dependencies; -use white_whale_std::pool_network::pair::{Cw20HookMsg, ExecuteMsg, InstantiateMsg, PoolFee}; - -use crate::contract::{execute, instantiate, reply}; -use crate::error::ContractError; -use crate::state::{get_fees_for_asset, store_fee, COLLECTED_PROTOCOL_FEES}; - -#[test] -fn withdraw_xyk_liquidity_cw20_lp() { - let mut deps = mock_dependencies(&[Coin { - denom: "uusd".to_string(), - amount: Uint128::from(100u128), - }]); - - deps.querier.with_token_balances(&[ - ( - &"liquidity0000".to_string(), - &[(&"addr0000".to_string(), &Uint128::from(100u128))], - ), - ( - &"asset0000".to_string(), - &[(&MOCK_CONTRACT_ADDR.to_string(), &Uint128::from(100u128))], - ), - ]); - - let msg = InstantiateMsg { - asset_infos: [ - AssetInfo::NativeToken { - denom: "uusd".to_string(), - }, - AssetInfo::Token { - contract_addr: "asset0000".to_string(), - }, - ], - token_code_id: 10u64, - asset_decimals: [6u8, 8u8], - pool_fees: PoolFee { - protocol_fee: Fee { - share: Decimal::percent(1u64), - }, - swap_fee: Fee { - share: Decimal::percent(1u64), - }, - burn_fee: Fee { - share: Decimal::zero(), - }, - }, - fee_collector_addr: "collector".to_string(), - pair_type: PairType::ConstantProduct, - token_factory_lp: false, - }; - - let env = mock_env(); - let info = mock_info("addr0000", &[]); - // we can just call .unwrap() to assert this was a success - let _res = instantiate(deps.as_mut(), env, info, msg).unwrap(); - - // store liquidity token - let reply_msg = Reply { - id: 1, - result: SubMsgResult::Ok(SubMsgResponse { - events: vec![], - data: Some( - vec![ - 10, 13, 108, 105, 113, 117, 105, 100, 105, 116, 121, 48, 48, 48, 48, - ] - .into(), - ), - }), - }; - - let _res = reply(deps.as_mut(), mock_env(), reply_msg).unwrap(); - - // store some protocol fees in both native and token - store_fee( - deps.as_mut().storage, - Uint128::from(10u8), - "uusd".to_string(), - COLLECTED_PROTOCOL_FEES, - ) - .unwrap(); - store_fee( - deps.as_mut().storage, - Uint128::from(20u8), - "asset0000".to_string(), - COLLECTED_PROTOCOL_FEES, - ) - .unwrap(); - - // withdraw liquidity - let msg = ExecuteMsg::Receive(Cw20ReceiveMsg { - sender: "addr0000".to_string(), - msg: to_binary(&Cw20HookMsg::WithdrawLiquidity {}).unwrap(), - amount: Uint128::from(100u128), - }); - - let env = mock_env(); - let info = mock_info("liquidity0000", &[]); - let res = execute(deps.as_mut(), env, info, msg).unwrap(); - let log_withdrawn_share = res.attributes.get(2).expect("no log"); - let log_refund_assets = res.attributes.get(3).expect("no log"); - let msg_refund_0 = res.messages.get(0).expect("no message"); - let msg_refund_1 = res.messages.get(1).expect("no message"); - let msg_burn_liquidity = res.messages.get(2).expect("no message"); - - let protocol_fee_native = get_fees_for_asset( - deps.as_mut().storage, - "uusd".to_string(), - COLLECTED_PROTOCOL_FEES, - ) - .unwrap(); - let expected_native_refund_amount: Uint128 = Uint128::from(100u128) - .checked_sub(protocol_fee_native.amount) - .unwrap(); - - let protocol_fee_token = get_fees_for_asset( - deps.as_mut().storage, - "asset0000".to_string(), - COLLECTED_PROTOCOL_FEES, - ) - .unwrap(); - let expected_token_refund_amount: Uint128 = Uint128::from(100u128) - .checked_sub(protocol_fee_token.amount) - .unwrap(); - - assert_eq!( - msg_refund_0, - &SubMsg::new(CosmosMsg::Bank(BankMsg::Send { - to_address: "addr0000".to_string(), - amount: vec![Coin { - denom: "uusd".to_string(), - amount: expected_native_refund_amount, - }], - })) - ); - assert_eq!( - msg_refund_1, - &SubMsg::new(CosmosMsg::Wasm(WasmMsg::Execute { - contract_addr: "asset0000".to_string(), - msg: to_binary(&Cw20ExecuteMsg::Transfer { - recipient: "addr0000".to_string(), - amount: expected_token_refund_amount, - }) - .unwrap(), - funds: vec![], - })) - ); - assert_eq!( - msg_burn_liquidity, - &SubMsg::new(CosmosMsg::Wasm(WasmMsg::Execute { - contract_addr: "liquidity0000".to_string(), - msg: to_binary(&Cw20ExecuteMsg::Burn { - amount: Uint128::from(100u128), - }) - .unwrap(), - funds: vec![], - })) - ); - - assert_eq!( - log_withdrawn_share, - &attr("withdrawn_share", 100u128.to_string()) - ); - assert_eq!( - log_refund_assets, - &attr("refund_assets", "90uusd, 80asset0000") - ); -} - -#[test] -fn withdraw_stableswap_liquidity() { - let mut deps = mock_dependencies(&[Coin { - denom: "uusd".to_string(), - amount: Uint128::from(100u128), - }]); - - deps.querier.with_token_balances(&[ - ( - &"liquidity0000".to_string(), - &[(&"addr0000".to_string(), &Uint128::from(100u128))], - ), - ( - &"asset0000".to_string(), - &[(&MOCK_CONTRACT_ADDR.to_string(), &Uint128::from(100u128))], - ), - ]); - - let msg = InstantiateMsg { - asset_infos: [ - AssetInfo::NativeToken { - denom: "uusd".to_string(), - }, - AssetInfo::Token { - contract_addr: "asset0000".to_string(), - }, - ], - token_code_id: 10u64, - asset_decimals: [6u8, 8u8], - pool_fees: PoolFee { - protocol_fee: Fee { - share: Decimal::percent(1u64), - }, - swap_fee: Fee { - share: Decimal::percent(1u64), - }, - burn_fee: Fee { - share: Decimal::zero(), - }, - }, - fee_collector_addr: "collector".to_string(), - pair_type: PairType::StableSwap { amp: 100 }, - token_factory_lp: false, - }; - - let env = mock_env(); - let info = mock_info("addr0000", &[]); - instantiate(deps.as_mut(), env, info, msg).unwrap(); - - // store liquidity token - let reply_msg = Reply { - id: 1, - result: SubMsgResult::Ok(SubMsgResponse { - events: vec![], - data: Some( - vec![ - 10, 13, 108, 105, 113, 117, 105, 100, 105, 116, 121, 48, 48, 48, 48, - ] - .into(), - ), - }), - }; - reply(deps.as_mut(), mock_env(), reply_msg).unwrap(); - - // store some protocol fees in both native and token - store_fee( - deps.as_mut().storage, - Uint128::from(10u8), - "uusd".to_string(), - COLLECTED_PROTOCOL_FEES, - ) - .unwrap(); - store_fee( - deps.as_mut().storage, - Uint128::from(20u8), - "asset0000".to_string(), - COLLECTED_PROTOCOL_FEES, - ) - .unwrap(); - - // withdraw liquidity - let msg = ExecuteMsg::Receive(Cw20ReceiveMsg { - sender: "addr0000".to_string(), - msg: to_binary(&Cw20HookMsg::WithdrawLiquidity {}).unwrap(), - amount: Uint128::from(100u128), - }); - - let env = mock_env(); - let info = mock_info("liquidity0000", &[]); - let res = execute(deps.as_mut(), env, info, msg).unwrap(); - - let protocol_fee_native = get_fees_for_asset( - deps.as_mut().storage, - "uusd".to_string(), - COLLECTED_PROTOCOL_FEES, - ) - .unwrap(); - let expected_native_refund_amount: Uint128 = Uint128::from(100u128) - .checked_sub(protocol_fee_native.amount) - .unwrap(); - - let protocol_fee_token = get_fees_for_asset( - deps.as_mut().storage, - "asset0000".to_string(), - COLLECTED_PROTOCOL_FEES, - ) - .unwrap(); - let expected_token_refund_amount: Uint128 = Uint128::from(100u128) - .checked_sub(protocol_fee_token.amount) - .unwrap(); - - assert_eq!( - res, - Response::new() - .add_messages(vec![ - CosmosMsg::Bank(BankMsg::Send { - to_address: "addr0000".to_string(), - amount: vec![Coin { - denom: "uusd".to_string(), - amount: expected_native_refund_amount, - }], - }), - CosmosMsg::Wasm(WasmMsg::Execute { - contract_addr: "asset0000".to_string(), - msg: to_binary(&Cw20ExecuteMsg::Transfer { - recipient: "addr0000".to_string(), - amount: expected_token_refund_amount, - }) - .unwrap(), - funds: vec![], - }), - CosmosMsg::Wasm(WasmMsg::Execute { - contract_addr: "liquidity0000".to_string(), - msg: to_binary(&Cw20ExecuteMsg::Burn { - amount: Uint128::from(100u128), - }) - .unwrap(), - funds: vec![], - }), - ]) - .add_attributes(vec![ - ("action", "withdraw_liquidity"), - ("sender", "addr0000"), - ("withdrawn_share", "100"), - ("refund_assets", ("90uusd, 80asset0000")), - ]) - ); -} - -#[test] -fn test_withdrawal_unauthorized() { - let mut deps = mock_dependencies(&[Coin { - denom: "uusd".to_string(), - amount: Uint128::from(100u128), - }]); - - deps.querier.with_token_balances(&[ - ( - &"liquidity0000".to_string(), - &[(&"addr0000".to_string(), &Uint128::from(100u128))], - ), - ( - &"asset0000".to_string(), - &[(&MOCK_CONTRACT_ADDR.to_string(), &Uint128::from(100u128))], - ), - ]); - - let msg = InstantiateMsg { - asset_infos: [ - AssetInfo::NativeToken { - denom: "uusd".to_string(), - }, - AssetInfo::Token { - contract_addr: "asset0000".to_string(), - }, - ], - token_code_id: 10u64, - asset_decimals: [6u8, 8u8], - pool_fees: PoolFee { - protocol_fee: Fee { - share: Decimal::percent(1u64), - }, - swap_fee: Fee { - share: Decimal::percent(1u64), - }, - burn_fee: Fee { - share: Decimal::zero(), - }, - }, - fee_collector_addr: "collector".to_string(), - pair_type: PairType::ConstantProduct, - token_factory_lp: false, - }; - - let env = mock_env(); - let info = mock_info("addr0000", &[]); - instantiate(deps.as_mut(), env, info, msg).unwrap(); - - // withdraw liquidity should fail - let msg = ExecuteMsg::Receive(Cw20ReceiveMsg { - sender: "addr0000".to_string(), - msg: to_binary(&Cw20HookMsg::WithdrawLiquidity {}).unwrap(), - amount: Uint128::from(100u128), - }); - - let env = mock_env(); - let info = mock_info("not_cw20", &[]); - let res = execute(deps.as_mut(), env, info, msg); - match res { - Ok(_) => panic!("should return ContractError::Unauthorized"), - Err(ContractError::Unauthorized { .. }) => (), - _ => panic!("should return ContractError::Unauthorized"), - } -} - -#[test] -fn test_withdrawal_wrong_message() { - let mut deps = mock_dependencies(&[Coin { - denom: "uusd".to_string(), - amount: Uint128::from(100u128), - }]); - - deps.querier.with_token_balances(&[ - ( - &"liquidity0000".to_string(), - &[(&"addr0000".to_string(), &Uint128::from(100u128))], - ), - ( - &"asset0000".to_string(), - &[(&MOCK_CONTRACT_ADDR.to_string(), &Uint128::from(100u128))], - ), - ]); - - let msg = InstantiateMsg { - asset_infos: [ - AssetInfo::NativeToken { - denom: "uusd".to_string(), - }, - AssetInfo::Token { - contract_addr: "asset0000".to_string(), - }, - ], - token_code_id: 10u64, - asset_decimals: [6u8, 8u8], - pool_fees: PoolFee { - protocol_fee: Fee { - share: Decimal::percent(1u64), - }, - swap_fee: Fee { - share: Decimal::percent(1u64), - }, - burn_fee: Fee { - share: Decimal::zero(), - }, - }, - fee_collector_addr: "collector".to_string(), - pair_type: PairType::ConstantProduct, - token_factory_lp: false, - }; - - let env = mock_env(); - let info = mock_info("addr0000", &[]); - instantiate(deps.as_mut(), env, info, msg).unwrap(); - - // withdraw liquidity should fail - let msg = ExecuteMsg::Receive(Cw20ReceiveMsg { - sender: "addr0000".to_string(), - msg: to_binary(&"invalid_message").unwrap(), - amount: Uint128::from(100u128), - }); - - let env = mock_env(); - let info = mock_info("liquidity0000", &[]); - let res = execute(deps.as_mut(), env, info, msg); - match res { - Ok(_) => panic!("should return ContractError::Std"), - Err(ContractError::Std { .. }) => (), - _ => panic!("should return ContractError::Std"), - } -} - -#[cfg(feature = "token_factory")] -#[test] -fn withdraw_xyk_liquidity_token_factory_lp() { - let lp_denom = format!("{}/{MOCK_CONTRACT_ADDR}/{LP_SYMBOL}", "factory"); - - let mut deps = mock_dependencies(&[ - Coin { - denom: "uusd".to_string(), - amount: Uint128::from(2000u128), - }, - Coin { - denom: "uwhale".to_string(), - amount: Uint128::from(2000u128), - }, - Coin { - denom: lp_denom.clone(), - amount: Uint128::from(1000u128), - }, - ]); - - deps.querier.with_balance(&[( - &"addr0000".to_string(), - vec![Coin { - denom: lp_denom.clone(), - amount: Uint128::from(1000u128 /* user deposit must be pre-applied */), - }], - )]); - - let msg = InstantiateMsg { - asset_infos: [ - AssetInfo::NativeToken { - denom: "uusd".to_string(), - }, - AssetInfo::NativeToken { - denom: "uwhale".to_string(), - }, - ], - token_code_id: 10u64, - asset_decimals: [6u8, 8u8], - pool_fees: PoolFee { - protocol_fee: Fee { - share: Decimal::percent(1u64), - }, - swap_fee: Fee { - share: Decimal::percent(1u64), - }, - burn_fee: Fee { - share: Decimal::zero(), - }, - }, - fee_collector_addr: "collector".to_string(), - pair_type: PairType::ConstantProduct, - token_factory_lp: true, - }; - - let env = mock_env(); - let info = mock_info("addr0000", &[]); - // we can just call .unwrap() to assert this was a success - let _res = instantiate(deps.as_mut(), env, info, msg).unwrap(); - - // withdraw liquidity - let msg = ExecuteMsg::WithdrawLiquidity {}; - - let env = mock_env(); - let info = mock_info( - "addr0000", - &[Coin { - denom: lp_denom.clone(), - amount: Uint128::from(1000u128), - }], - ); - let res = execute(deps.as_mut(), env, info, msg).unwrap(); - - let log_withdrawn_share = res.attributes.get(2).expect("no log"); - let log_refund_assets = res.attributes.get(3).expect("no log"); - let msg_refund_0 = res.messages.get(0).expect("no message").clone().msg; - let msg_refund_1 = res.messages.get(1).expect("no message").clone().msg; - let msg_burn_liquidity = res.messages.get(2).expect("no message").clone().msg; - - let expected_asset_0_refund_amount: Uint128 = Uint128::from(1000u128); - let expected_asset_1_refund_amount: Uint128 = Uint128::from(1000u128); - - let msg_refund_0_expected = CosmosMsg::Bank(BankMsg::Send { - to_address: "addr0000".to_string(), - amount: vec![coin(expected_asset_0_refund_amount.u128(), "uusd")], - }); - let msg_refund_1_expected = CosmosMsg::Bank(BankMsg::Send { - to_address: "addr0000".to_string(), - amount: vec![coin(expected_asset_1_refund_amount.u128(), "uwhale")], - }); - let msg_burn_liquidity_expected = >::into(MsgBurn { - sender: MOCK_CONTRACT_ADDR.to_string(), - amount: Some(pool_network::denom::Coin { - denom: lp_denom.clone(), - amount: "1000".to_string(), - }), - }); - - assert_eq!(msg_refund_0, msg_refund_0_expected); - assert_eq!(msg_refund_1, msg_refund_1_expected); - assert_eq!(msg_burn_liquidity, msg_burn_liquidity_expected); - - assert_eq!( - log_withdrawn_share, - &attr("withdrawn_share", 1000u128.to_string()) - ); - assert_eq!( - log_refund_assets, - &attr("refund_assets", "1000uusd, 1000uwhale") - ); -} - -#[cfg(feature = "token_factory")] -#[test] -fn withdraw_xyk_liquidity_token_factory_lp_wrong_asset() { - let lp_denom = format!("{}/{MOCK_CONTRACT_ADDR}/{LP_SYMBOL}", "factory"); - - let mut deps = mock_dependencies(&[ - Coin { - denom: "uusd".to_string(), - amount: Uint128::from(2000u128), - }, - Coin { - denom: "uwhale".to_string(), - amount: Uint128::from(2000u128), - }, - Coin { - denom: lp_denom.clone(), - amount: Uint128::from(1000u128), - }, - ]); - - deps.querier.with_balance(&[( - &"addr0000".to_string(), - vec![Coin { - denom: lp_denom.clone(), - amount: Uint128::from(1000u128 /* user deposit must be pre-applied */), - }], - )]); - - let msg = InstantiateMsg { - asset_infos: [ - AssetInfo::NativeToken { - denom: "uusd".to_string(), - }, - AssetInfo::NativeToken { - denom: "uwhale".to_string(), - }, - ], - token_code_id: 10u64, - asset_decimals: [6u8, 8u8], - pool_fees: PoolFee { - protocol_fee: Fee { - share: Decimal::percent(1u64), - }, - swap_fee: Fee { - share: Decimal::percent(1u64), - }, - burn_fee: Fee { - share: Decimal::zero(), - }, - }, - fee_collector_addr: "collector".to_string(), - pair_type: PairType::ConstantProduct, - token_factory_lp: true, - }; - - let env = mock_env(); - let info = mock_info("addr0000", &[]); - // we can just call .unwrap() to assert this was a success - let _res = instantiate(deps.as_mut(), env, info, msg).unwrap(); - - // withdraw liquidity - let msg = ExecuteMsg::WithdrawLiquidity {}; - - let env = mock_env(); - let info = mock_info( - "addr0000", - &[Coin { - denom: "not_lp_denom".to_string(), - amount: Uint128::from(1000u128), - }], - ); - let err = execute(deps.as_mut(), env, info, msg).unwrap_err(); - - assert_eq!(err, ContractError::AssetMismatch {}); -} diff --git a/packages/white-whale-std/src/common.rs b/packages/white-whale-std/src/common.rs index 9e971497a..6f8ddd569 100644 --- a/packages/white-whale-std/src/common.rs +++ b/packages/white-whale-std/src/common.rs @@ -1,4 +1,4 @@ -use cosmwasm_std::{Addr, DepsMut, Env, MessageInfo, Response, StdError, StdResult, Storage}; +use cosmwasm_std::{Addr, Deps, DepsMut, Env, MessageInfo, Response, StdError, StdResult, Storage}; use cw_ownable::{Action, OwnershipError}; use cw_storage_plus::Item; @@ -31,3 +31,16 @@ pub fn update_ownership( .add_attributes(ownership.into_attributes()) }) } + +/// Validates a [String] address or returns the default address if the validation fails. +pub fn validate_addr_or_default(deps: &Deps, unvalidated: Option, default: Addr) -> Addr { + unvalidated + .map_or_else( + || Some(default.clone()), + |recv| match deps.api.addr_validate(&recv) { + Ok(validated) => Some(validated), + Err(_) => None, + }, + ) + .unwrap_or(default) +} diff --git a/packages/white-whale-std/src/pool_manager.rs b/packages/white-whale-std/src/pool_manager.rs index a2a5e5c34..19011b2cd 100644 --- a/packages/white-whale-std/src/pool_manager.rs +++ b/packages/white-whale-std/src/pool_manager.rs @@ -1,15 +1,20 @@ use std::fmt; -use crate::{fee::PoolFee, pool_network::asset::PairType}; +use crate::fee::PoolFee; use cosmwasm_schema::{cw_serde, QueryResponses}; use cosmwasm_std::{Addr, Coin, Decimal, Uint128}; use cw_ownable::{cw_ownable_execute, cw_ownable_query}; +/// The type of swap operation to perform. #[cw_serde] pub enum SwapOperation { + /// A swap operation that uses the WhaleSwap router. WhaleSwap { + /// The token denom to swap in. token_in_denom: String, + /// The token denom returning from the swap. token_out_denom: String, + /// The identifier of the pool to use for the swap. pool_identifier: String, }, } @@ -55,16 +60,21 @@ impl fmt::Display for SwapOperation { } } +/// The swap route structure #[cw_serde] pub struct SwapRoute { + /// The offer asset denom, i.e. the asset that is being swapped. pub offer_asset_denom: String, + /// The ask asset denom, i.e. the asset that is being received. pub ask_asset_denom: String, + /// The swap operations to perform to get from offer asset to ask asset. pub swap_operations: Vec, } -// Used for all swap routes +/// The response for the `SwapRoute` query. #[cw_serde] pub struct SwapRouteResponse { + /// The swap route taken for the queried swap. pub swap_route: SwapRoute, } @@ -78,7 +88,8 @@ 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 +// Defines a structure for Fees which names a number of defined fee collection types, leaving +// room for a custom fee a user can use to pass a fee with a defined custom name #[cw_serde] pub enum FeeTypes { Protocol, @@ -87,37 +98,69 @@ pub enum FeeTypes { Custom(String), } +/// Params for the stable swap, used when changing the amplification factor #[cw_serde] - pub struct StableSwapParams { + /// Initial amplification factor pub initial_amp: String, + /// Future amplification factor, i.e. target pub future_amp: String, + /// Block height when the initial amplification factor kicks in. It goes gradually from the + /// initial to the future amplification factor between the given blocks. pub initial_amp_block: String, + /// Block height when the future amplification factor will be set. It goes gradually from the + /// initial to the future amplification factor between the given blocks. pub future_amp_block: String, } -// Store PairInfo to N -// We define a custom struct for which allows for dynamic but defined pairs +/// Contains the pool information #[cw_serde] -pub struct PairInfo { +pub struct PoolInfo { + /// The asset denoms for the pool. pub asset_denoms: Vec, + /// The LP denom of the pool. pub lp_denom: String, + /// The decimals for the given asset denoms, provided in the same order as asset_denoms. pub asset_decimals: Vec, + /// The total amount of assets in the pool. pub assets: Vec, - pub pair_type: PairType, + /// The type of pool to create. + pub pool_type: PoolType, + /// The fees for the pool. pub pool_fees: PoolFee, - // TODO: Add stable swap params - // pub stable_swap_params: Option + //pub stable_swap_params: Option +} + +/// Possible pool types, it can be either a constant product (xyk) pool or a stable swap pool. +#[cw_serde] +pub enum PoolType { + /// A stable swap pool. + StableSwap { + /// The amount of amplification to perform on the constant product part of the swap formula. + amp: u64, + }, + /// xyk pool + ConstantProduct, +} + +impl PoolType { + /// Gets a string representation of the pair type + pub fn get_label(&self) -> &str { + match self { + PoolType::ConstantProduct => "ConstantProduct", + PoolType::StableSwap { .. } => "StableSwap", + } + } } -impl PairInfo {} +/// The contract configuration. #[cw_serde] pub struct Config { /// The address of the bonding manager contract. pub bonding_manager_addr: Addr, /// The address of the incentive manager contract. pub incentive_manager_addr: Addr, - // We must set a creation fee on instantiation to prevent spamming of pools + /// How much it costs to create a pool. It helps prevent spamming of new pools. pub pool_creation_fee: Coin, // Whether or not swaps, deposits, and withdrawals are enabled pub feature_toggle: FeatureToggle, @@ -125,31 +168,47 @@ pub struct Config { #[cw_serde] pub struct InstantiateMsg { + /// The address of the bonding manager contract. pub bonding_manager_addr: String, + /// The address of the incentive manager contract. pub incentive_manager_addr: String, + /// How much it costs to create a pool. It helps prevent spamming of new pools. pub pool_creation_fee: Coin, } -/// The migrate message #[cw_serde] pub struct MigrateMsg {} #[cw_ownable_execute] #[cw_serde] pub enum ExecuteMsg { - CreatePair { + /// Creates a new pool. + CreatePool { + /// The asset denoms for the pool. asset_denoms: Vec, + /// The decimals for the given asset denoms, provided in the same order as `asset_denoms`. asset_decimals: Vec, + /// The fees for the pool. pool_fees: PoolFee, - pair_type: PairType, - pair_identifier: Option, + /// The type of pool to create. + pool_type: PoolType, + /// The identifier for the pool. + pool_identifier: Option, }, /// Provides liquidity to the pool ProvideLiquidity { + /// A percentage value representing the acceptable slippage for the operation. + /// When provided, if the slippage exceeds this value, the liquidity provision will not be + /// executed. slippage_tolerance: Option, + /// The maximum allowable spread between the bid and ask prices for the pool. + /// When provided, if the spread exceeds this value, the liquidity provision will not be + /// executed. max_spread: Option, + /// The receiver of the LP receiver: Option, - pair_identifier: String, + /// The identifier for the pool to provide liquidity for. + pool_identifier: String, /// The amount of time in seconds to unlock tokens if taking part on the incentives. If not passed, /// the tokens will not be locked and the LP tokens will be returned to the user. unlocking_duration: Option, @@ -158,16 +217,21 @@ pub enum ExecuteMsg { }, /// Swap an offer asset to the other Swap { - //todo remove offer_asset, take it from info.funds - offer_asset: Coin, + /// The return asset of the swap. ask_asset_denom: String, + /// The belief price of the swap. belief_price: Option, + /// The maximum spread to incur when performing the swap. If the spread exceeds this value, + /// the swap will not be executed. max_spread: Option, - to: Option, - pair_identifier: String, + /// The recipient of the output tokens. If not provided, the tokens will be sent to the sender + /// of the message. + receiver: Option, + /// The identifier for the pool to swap in. + pool_identifier: String, }, /// Withdraws liquidity from the pool. - WithdrawLiquidity { pair_identifier: String }, + WithdrawLiquidity { pool_identifier: String }, /// Execute multiple [`SwapOperations`] to allow for multi-hop swaps. ExecuteSwapOperations { /// The operations that should be performed in sequence. @@ -181,26 +245,12 @@ pub enum ExecuteMsg { /// The (optional) recipient of the output tokens. /// /// If left unspecified, tokens will be sent to the sender of the message. - to: Option, + receiver: Option, /// The (optional) maximum spread to incur when performing any swap. /// /// If left unspecified, there is no limit to what spread the transaction can incur. max_spread: Option, }, - // /// Swap the offer to ask token. This message can only be called internally by the router contract. - // ExecuteSwapOperation { - // operation: SwapOperation, - // to: Option, - // max_spread: Option, - // }, - // /// Checks if the swap amount exceeds the minimum_receive. This message can only be called - // /// internally by the router contract. - // AssertMinimumReceive { - // asset_info: AssetInfo, - // prev_balance: Uint128, - // minimum_receive: Uint128, - // receiver: String, - // }, /// Adds swap routes to the router. AddSwapRoutes { swap_routes: Vec }, /// Removes swap routes from the router. @@ -224,86 +274,107 @@ pub enum ExecuteMsg { pub enum QueryMsg { /// Retrieves the contract's config. #[returns(ConfigResponse)] - Config {}, - + Config, /// Retrieves the decimals for the given asset. #[returns(AssetDecimalsResponse)] AssetDecimals { - pair_identifier: String, + /// The pool identifier to do the query for. + pool_identifier: String, + /// The queried denom in the given pool_identifier. denom: String, }, - /// Simulates a swap. #[returns(SimulationResponse)] Simulation { + /// The offer asset to swap. offer_asset: Coin, - pair_identifier: String, + /// The ask asset denom to get. + ask_asset_denom: String, + /// The pool identifier to swap in. + pool_identifier: String, }, - /// Simulates a reverse swap, i.e. given the ask asset, how much of the offer asset is needed to - /// perform the swap. + /// Simulates a reverse swap, i.e. given the ask asset, how much of the offer asset is needed + /// to perform the swap. #[returns(ReverseSimulationResponse)] ReverseSimulation { + /// The ask asset to get after the swap. ask_asset: Coin, - offer_asset: Coin, - pair_identifier: String, + /// The offer asset denom to input. + offer_asset_denom: String, + /// The pool identifier to swap in. + pool_identifier: String, }, - /// Gets the swap route for the given offer and ask assets. #[returns(SwapRouteResponse)] SwapRoute { + /// The offer asset denom, i.e. the asset that is being swapped. offer_asset_denom: String, + /// The ask asset denom, i.e. the asset that is being received. ask_asset_denom: String, }, /// Gets all swap routes registered #[returns(SwapRoutesResponse)] - SwapRoutes {}, - + SwapRoutes, /// Simulates swap operations. #[returns(SimulateSwapOperationsResponse)] SimulateSwapOperations { + /// The amount to swap. offer_amount: Uint128, + /// The operations to perform. operations: Vec, }, /// Simulates a reverse swap operations, i.e. given the ask asset, how much of the offer asset /// is needed to perform the swap. #[returns(ReverseSimulateSwapOperationsResponse)] ReverseSimulateSwapOperations { + /// The amount to get after the swap. ask_amount: Uint128, + /// The operations to perform. operations: Vec, }, - - #[returns(PairInfoResponse)] - Pair { pair_identifier: String }, - /// Retrieves the creator of the swap routes that can then remove them. + /// Retrieves the pool information for the given pool identifier. + #[returns(PoolInfoResponse)] + Pool { pool_identifier: String }, + /// Retrieves the creator of the swap route to get from offer to ask asset. The creator of + /// the swap route can remove it. #[returns(SwapRouteCreatorResponse)] SwapRouteCreator { + /// The offer asset denom, i.e. the asset that is being swapped. offer_asset_denom: String, + /// The ask asset denom, i.e. the asset that is being received. ask_asset_denom: String, }, } +/// The response for the `Config` query. #[cw_serde] pub struct ConfigResponse { + /// The contract configuration. pub config: Config, } +/// The response for the `SwapRoutes` query. #[cw_serde] pub struct SwapRoutesResponse { + /// The swap routes registered in the contract. pub swap_routes: Vec, } +/// The response for the `Pool` query. #[cw_serde] -pub struct PairInfoResponse { - pub pair_info: PairInfo, +pub struct PoolInfoResponse { + /// The pool information for the given pool identifier. + pub pool_info: PoolInfo, + /// The total LP tokens in the pool. pub total_share: Coin, } /// The response for the `AssetDecimals` query. #[cw_serde] pub struct AssetDecimalsResponse { - /// The pair identifier to do the query for. - pub pair_identifier: String, - /// The queried denom in the given pair_identifier. + /// The pool identifier to do the query for. + pub pool_identifier: String, + /// The queried denom in the given pool_identifier. pub denom: String, /// The decimals for the requested denom. pub decimals: u8, @@ -312,11 +383,19 @@ pub struct AssetDecimalsResponse { /// SimulationResponse returns swap simulation response #[cw_serde] pub struct SimulationResponse { + /// The return amount of the ask asset given the offer amount. pub return_amount: Uint128, + /// The spread amount of the swap. pub spread_amount: Uint128, + /// The swap fee amount of the swap. pub swap_fee_amount: Uint128, + /// The protocol fee amount of the swap. pub protocol_fee_amount: Uint128, + /// The burn fee amount of the swap. pub burn_fee_amount: Uint128, + /// The extra fees amount of the swap. + pub extra_fees_amount: Uint128, + /// The fee amount of the swap going to the osmosis community pool. #[cfg(feature = "osmosis")] pub osmosis_fee_amount: Uint128, } @@ -324,35 +403,49 @@ pub struct SimulationResponse { /// ReverseSimulationResponse returns reverse swap simulation response #[cw_serde] pub struct ReverseSimulationResponse { + /// The amount of the offer asset needed to get the ask amount. pub offer_amount: Uint128, + /// The spread amount of the swap. pub spread_amount: Uint128, + /// The swap fee amount of the swap. pub swap_fee_amount: Uint128, + /// The protocol fee amount of the swap. pub protocol_fee_amount: Uint128, + /// The burn fee amount of the swap. pub burn_fee_amount: Uint128, + /// The fee amount of the swap going to the osmosis community pool. #[cfg(feature = "osmosis")] pub osmosis_fee_amount: Uint128, } -/// Pool feature toggle +/// Pool feature toggle, can control whether swaps, deposits, and withdrawals are enabled. #[cw_serde] pub struct FeatureToggle { + /// Whether or not swaps are enabled pub withdrawals_enabled: bool, + /// Whether or not deposits are enabled pub deposits_enabled: bool, + /// Whether or not swaps are enabled pub swaps_enabled: bool, } -// We define a custom struct for each query response +/// The response for the `SimulateSwapOperations` query. #[cw_serde] pub struct SimulateSwapOperationsResponse { + /// The amount of the final token after the swap operations. pub amount: Uint128, } +/// The response for the `ReverseSimulateSwapOperations` query. #[cw_serde] pub struct ReverseSimulateSwapOperationsResponse { + /// The amount of the initial token needed to get the final token after the swap operations. pub amount: Uint128, } +/// The response for the `SwapRouteCreator` query. #[cw_serde] pub struct SwapRouteCreatorResponse { + /// The creator of the swap route. pub creator: String, } diff --git a/packages/white-whale-testing/Cargo.toml b/packages/white-whale-testing/Cargo.toml index 6b9c1e13a..c4894e0d2 100644 --- a/packages/white-whale-testing/Cargo.toml +++ b/packages/white-whale-testing/Cargo.toml @@ -34,4 +34,4 @@ white-whale-std.workspace = true whale-lair.workspace = true fee_collector.workspace = true fee_distributor.workspace = true -epoch-manager.workspace = true \ No newline at end of file +epoch-manager.workspace = true