diff --git a/README.md b/README.md index b721ecff..72aabf33 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,6 @@ [![first-timers-only](https://img.shields.io/badge/first--timers--only-friendly-blue.svg?style=flat-square)](https://www.firsttimersonly.com/) [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square)](https://makeapullrequest.com) - [Discord invite]: https://discord.com/invite/tSxyyCWgYX [Discord badge]: https://img.shields.io/discord/908044702794801233 [Twitter handle]: https://img.shields.io/twitter/follow/WhiteWhaleDefi.svg?style=social&label=Follow @@ -21,27 +20,81 @@ ## Getting started -To get started with `migaloo-core`, please go through the [contributing guide](./docs/CONTRIBUTING.md) to see the +To get started with `white-whale-core`, please go through the [contributing guide](./docs/CONTRIBUTING.md) to see the different ways to contribute to the project. ## Resources 1. [Website](https://whitewhale.money/) 2. [LitePaper](https://whitewhale.money/LitepaperV2.pdf) -3. [Docs](https://ww0-1.gitbook.io/migaloo-docs/) +3. [Docs](https://docs.whitewhale.money/white-whale) 4. [Discord](https://discord.com/invite/tSxyyCWgYX) 5. [Twitter](https://twitter.com/WhiteWhaleDefi) 6. [Telegram](https://t.me/whitewhaleofficial) +## V2 Architecture + +White Whale V2 is the next iteration of the White Whale protocol V1. Besides minor yet impactful tweaks, functionality wise V2 +hasn't changed much from V1. The main difference is the architecture, which makes it easier to integrate with to other +protocols. In V1, the protocol used multiple contracts which makes it complex, factories spawning other contracts and so on. +In V2, the protocol has been simplified significantly and built around singleton contracts. So to get an idea, V1 had 14+ contracts, +while V2 has only 5 contracts. + +The following is the V2 architecture, and a general description of each contract: + +```mermaid +--- +title: White Whale V2 +--- +flowchart LR +P[Pool Manager] <--> I[Incentive Manager] +P[Pool Manager] --> B[Bonding Manager] +V[Vault Manager] --> B +B --> E[Epoch Manager] +I --> E + + click P "https://github.com/White-Whale-Defi-Platform/white-whale-core/tree/release/v2_contracts/contracts/liquidity_hub/pool-manager" "Pool Manager" + click V "https://github.com/White-Whale-Defi-Platform/white-whale-core/tree/release/v2_contracts/contracts/liquidity_hub/vault-manager" "Vault Manager" + click I "https://github.com/White-Whale-Defi-Platform/white-whale-core/tree/release/v2_contracts/contracts/liquidity_hub/incentive-manager" "Incentive Manager" + click B "https://github.com/White-Whale-Defi-Platform/white-whale-core/tree/release/v2_contracts/contracts/liquidity_hub/bonding-manager" "Bonding Manager" + click E "https://github.com/White-Whale-Defi-Platform/white-whale-core/tree/release/v2_contracts/contracts/liquidity_hub/epoch-manager" "Epoch Manager" +``` +The direction of the arrows represents the dependencies between the contracts. + +### Pool Manager +The Pool Manager is the contract that manages the pools in the protocol. It is responsible for creating pool and handling +swaps. Pool creation is permisionless, meaning anyone can create a pool if the fee is paid. The Pool Manager depends on +both the Incentive and Bonding Managers. + +### Vault Manager +The Vault Manager is the contract that manages the vaults in the protocol. It is responsible for creating vaults and performing +flashloan operations. Vault creation is permissionless, meaning anyone can create a vault if the fee is paid. The Vault Manager +depends on the Bonding Manager, as that's where the flashloan fees are sent for distribution. + +### Incentive Manager +The Incentive Manager is the contract that manages the incentives in the protocol. It is responsible for creating and +distributing incentives on pools. Incentive creation is permissionless, meaning anyone can create an incentive if the fee is paid. +The Incentive Manager depends on the Epoch Manager, as incentives are distributed based on epochs. + +### Bonding Manager +The Bonding Manager is the contract that manages the bonding in the protocol. It is responsible for bonding eligible tokens +and distributing the fees generated by the pools and vaults among the users that bond tokens. The Bonding Manager depends +on the Epoch Manager, as the fee distribution is done based on epochs. + +### Epoch Manager +The Epoch Manager is the contract that manages the epochs in the protocol. Its single responsibility is to create the epochs, +which are used by the Incentive and Bonding Managers for distributing incentives and fees. + +--- ## Deployed contracts -White Whale Migaloo is a protocol that exists across multiple chains. You can find contract addresses for different chain deployments -in the [documentation](https://ww0-1.gitbook.io/migaloo-docs/smart-contracts/deployments). +White Whale is a protocol that exists across multiple chains. You can find contract addresses for different chain deployments +in the [documentation](https://docs.whitewhale.money/white-whale/smart-contracts/liquidity-hub-deployments). -## Building and Deploying Migaloo +## Building and Deploying White Whale -To build and deploy Migaloo's smart contracts, there are a series of deployment scripts under `scripts/`. You need at -least Rust v1.65.0 to compile the contracts. +To build and deploy White Whale's smart contracts, there are a series of deployment scripts under `scripts/`. Alternatively, +there are a few `just` recipes you can take advantage of. You need at least Rust v1.65.0 to compile the contracts. ### Build scripts @@ -51,72 +104,24 @@ least Rust v1.65.0 to compile the contracts. it is customizable by passing the number of kB to the script. For example `check_artifacts_size.sh 400` verifies the artifacts are under 400 kB. -### Deployment scripts - -The deployment scripts are found under `scripts/deployment/`. The following is the structure found on under this folder: - -```bash -. -├── deploy_env -│   ├── base_env.sh -│   ├── chain_env.sh -│   ├── mainnets -│   │   ├── chihuahua.env -│   │   ├── juno.env -│   │   └── terra.env -│   ├── mnemonics -│   │   ├── deployer_mnemonic_testnet.txt -│   │   └── deployer_mnemonic.txt -│   └── testnets -│   ├── archway.env -│   ├── injective.env -│   ├── juno.env -│   ├── local.env -│   └── terra.env -├── deploy_liquidity_hub.sh -├── deploy_pool.sh -├── deploy_vault.sh -├── input -│   ├── pool.json -│   └── vault.json -├── output -│   ├── uni-5_liquidity_hub_contracts.json -│   ├── uni-5_pools.json -│   └── uni-5_vaults.json -└── wallet_importer.sh -``` - -There are three main scripts: `deploy_liquidity_hub.sh`, `deploy_pool.sh` and `deploy_vault.sh`. The rest of the scripts -in there are used as auxiliary scripts by the main three listed before. +### Just recipes -The `deploy_env/` folder contains env files defining the parameters for the blockchain where the deployment is going to occur, -whether it is a mainnet or testnet deployment. +All recipes are found in the `justfile`. To see all available recipes, run `just` or `just --list`. Here are some of them: -The `input/` folder is used for adding json files containing the config parameters when deploying pools or vaults. -The `output/` folder is where the scripts will write the data regarding the deployment, in json format. The name of the file -follows the following nomenclature: `"chain_id"_liquidity_hub_contracts`, `"chain_id"_pools`, `"chain_id"_vaults`. +- `build FEATURE=''` # Builds the whole project with the a feature flag if provided. +- `fmt` # Formats the rust, toml and sh files in the project. +- `get-pools CHAIN` # Extracts the pools from the given chain. +- `schemas` # Generates the schemas for the contracts. -- `deploy_liquidity_hub.sh`: deploys the liquidity hubs. It can deploy the entire LH or parts of it. To learn how to use it, -run the script with the `-h` flag. -- `deploy_pool.sh`: deploys a pool based on the configuration specified at `input/pool.json`. To learn how to use it, -run the script with the `-h` flag. -- `deploy_vault.sh`: deploys a vault based on the configuration specified at `input/vault.json`. To learn how to use it, -run the script with the `-h` flag. - -Notice that to deploy a pool or vault you need to have deployed the pool or vault factory respectively. - -Here are some examples: +### Deployment scripts -```bash -scripts/deployment/deploy_liquidity_hub.sh -c juno -d all -scripts/deployment/deploy_liquidity_hub.sh -c juno -d vault-network -scripts/deployment/deploy_pool-sh -c juno -p scripts/deployment/input/pool.json -scripts/deployment/deploy_vault-sh -c juno -v scripts/deployment/input/vault.json -``` +TODO update deployment scripts for V2. -## Testing Migaloo +## Testing White Whale -To run the tests, run `cargo test`. You can also run `cargo tarpaulin -v` to get test code coverage. +To run the tests, run `cargo test`. You can also run `cargo tarpaulin -v` to get test code coverage. Note that the White Whale +project contains a few feature flags that can be used to run the tests. If you want to run the tests for a particular feature, +run `cargo test --features "feature_name"`. ## Disclaimer @@ -124,7 +129,7 @@ To run the tests, run `cargo test`. You can also run `cargo tarpaulin -v` to get ## Audit -Migaloo core contracts have been audited by [SCV-Security](https://www.scv.services/). The report can be found [here](https://github.com/SCV-Security/PublicReports/blob/main/CW/WhiteWhale/White%20Whale%20-%20Migaloo%20Audit%20Report%20v1.0.pdf). +The White Whale V2 contract's audit is currently in progress. ## Contributing diff --git a/contracts/liquidity_hub/epoch-manager/tests/common.rs b/contracts/liquidity_hub/epoch-manager/tests/common.rs index 7ac9721a..b6370cfd 100644 --- a/contracts/liquidity_hub/epoch-manager/tests/common.rs +++ b/contracts/liquidity_hub/epoch-manager/tests/common.rs @@ -8,6 +8,7 @@ use white_whale_std::epoch_manager::epoch_manager::{ }; /// Mocks contract instantiation. +#[allow(dead_code)] pub(crate) fn mock_instantiation( deps: DepsMut, info: MessageInfo, @@ -28,6 +29,7 @@ pub(crate) fn mock_instantiation( } /// Mocks hook addition. +#[allow(dead_code)] pub(crate) fn mock_add_hook(deps: DepsMut, info: MessageInfo) -> Result { let msg = ExecuteMsg::AddHook { contract_addr: "hook_contract_1".to_string(), diff --git a/contracts/liquidity_hub/incentive-manager/tests/common/suite.rs b/contracts/liquidity_hub/incentive-manager/tests/common/suite.rs index 9b0bc2b4..1b319a41 100644 --- a/contracts/liquidity_hub/incentive-manager/tests/common/suite.rs +++ b/contracts/liquidity_hub/incentive-manager/tests/common/suite.rs @@ -166,6 +166,7 @@ impl TestingSuite { .unwrap(); } + #[allow(clippy::inconsistent_digit_grouping)] fn create_epoch_manager(&mut self) { let epoch_manager_contract = self.app.store_code(epoch_manager_contract()); @@ -197,6 +198,7 @@ impl TestingSuite { } #[track_caller] + #[allow(clippy::too_many_arguments)] pub(crate) fn instantiate( &mut self, whale_lair_addr: String, @@ -239,6 +241,7 @@ impl TestingSuite { } #[track_caller] + #[allow(clippy::too_many_arguments)] pub(crate) fn instantiate_err( &mut self, whale_lair_addr: String, @@ -300,6 +303,7 @@ impl TestingSuite { } #[track_caller] + #[allow(clippy::too_many_arguments)] pub(crate) fn update_config( &mut self, sender: Addr, @@ -555,7 +559,7 @@ impl TestingSuite { result( self.app - .execute_contract(sender, self.epoch_manager_addr.clone(), &msg, &vec![]), + .execute_contract(sender, self.epoch_manager_addr.clone(), &msg, &[]), ); self @@ -584,7 +588,7 @@ impl TestingSuite { #[track_caller] pub(crate) fn query_current_epoch( &mut self, - result: impl Fn(StdResult), + mut result: impl FnMut(StdResult), ) -> &mut Self { let current_epoch_response: StdResult = self.app.wrap().query_wasm_smart( &self.epoch_manager_addr, diff --git a/contracts/liquidity_hub/incentive-manager/tests/integration.rs b/contracts/liquidity_hub/incentive-manager/tests/integration.rs index 65980c55..d3204a69 100644 --- a/contracts/liquidity_hub/incentive-manager/tests/integration.rs +++ b/contracts/liquidity_hub/incentive-manager/tests/integration.rs @@ -1,13 +1,11 @@ extern crate core; -use std::cell::RefCell; - use cosmwasm_std::{coin, Addr, Coin, Decimal, Uint128}; use incentive_manager::ContractError; use white_whale_std::incentive_manager::{ - Config, Curve, EpochId, Incentive, IncentiveAction, IncentiveParams, IncentivesBy, - LpWeightResponse, Position, PositionAction, RewardsResponse, + Config, Curve, Incentive, IncentiveAction, IncentiveParams, IncentivesBy, LpWeightResponse, + Position, PositionAction, RewardsResponse, }; use crate::common::suite::TestingSuite; @@ -707,6 +705,7 @@ fn expand_incentives() { } #[test] +#[allow(clippy::inconsistent_digit_grouping)] fn close_incentives() { let lp_denom = "factory/pool/uLP".to_string(); @@ -790,7 +789,7 @@ fn close_incentives() { }, ) .query_balance("ulab".to_string(), other.clone(), |balance| { - assert_eq!(balance, Uint128::new(99_999_6000)); + assert_eq!(balance, Uint128::new(999_996_000)); }) .manage_incentive( other.clone(), @@ -803,7 +802,7 @@ fn close_incentives() { }, ) .query_balance("ulab".to_string(), other.clone(), |balance| { - assert_eq!(balance, Uint128::new(100_000_0000)); + assert_eq!(balance, Uint128::new(1000_000_000)); }); suite @@ -829,7 +828,7 @@ fn close_incentives() { }, ) .query_balance("ulab".to_string(), other.clone(), |balance| { - assert_eq!(balance, Uint128::new(99_999_6000)); + assert_eq!(balance, Uint128::new(999_996_000)); }) // the owner of the contract can also close incentives .manage_incentive( @@ -843,7 +842,7 @@ fn close_incentives() { }, ) .query_balance("ulab".to_string(), other.clone(), |balance| { - assert_eq!(balance, Uint128::new(100_000_0000)); + assert_eq!(balance, Uint128::new(1000_000_000)); }); } @@ -1115,15 +1114,16 @@ pub fn update_config() { } #[test] +#[allow(clippy::inconsistent_digit_grouping)] pub fn test_manage_position() { let lp_denom = "factory/pool/uLP".to_string(); let mut suite = TestingSuite::default_with_balances(vec![ - coin(1_000_000_000u128, "uwhale".to_string()), - coin(1_000_000_000u128, "ulab".to_string()), - coin(1_000_000_000u128, "uosmo".to_string()), + coin(1_000_000_000u128, "uwhale"), + coin(1_000_000_000u128, "ulab"), + coin(1_000_000_000u128, "uosmo"), coin(1_000_000_000u128, lp_denom.clone()), - coin(1_000_000_000u128, "invalid_lp".clone()), + coin(1_000_000_000u128, "invalid_lp"), ]); let creator = suite.creator(); @@ -1990,11 +1990,11 @@ fn claim_expired_incentive_returns_nothing() { let lp_denom = "factory/pool/uLP".to_string(); let mut suite = TestingSuite::default_with_balances(vec![ - coin(1_000_000_000u128, "uwhale".to_string()), - coin(1_000_000_000u128, "ulab".to_string()), - coin(1_000_000_000u128, "uosmo".to_string()), + coin(1_000_000_000u128, "uwhale"), + coin(1_000_000_000u128, "ulab"), + coin(1_000_000_000u128, "uosmo"), coin(1_000_000_000u128, lp_denom.clone()), - coin(1_000_000_000u128, "invalid_lp".clone()), + coin(1_000_000_000u128, "invalid_lp"), ]); let creator = suite.creator(); @@ -2136,11 +2136,11 @@ fn test_close_expired_incentives() { let lp_denom = "factory/pool/uLP".to_string(); let mut suite = TestingSuite::default_with_balances(vec![ - coin(1_000_000_000u128, "uwhale".to_string()), - coin(1_000_000_000u128, "ulab".to_string()), - coin(1_000_000_000u128, "uosmo".to_string()), + coin(1_000_000_000u128, "uwhale"), + coin(1_000_000_000u128, "ulab"), + coin(1_000_000_000u128, "uosmo"), coin(1_000_000_000u128, lp_denom.clone()), - coin(1_000_000_000u128, "invalid_lp".clone()), + coin(1_000_000_000u128, "invalid_lp"), ]); let creator = suite.creator(); @@ -2182,18 +2182,18 @@ fn test_close_expired_incentives() { }); } - let current_id: RefCell = RefCell::new(0u64); + let mut current_id = 0; // try opening another incentive for the same lp denom, the expired incentive should get closed suite .query_current_epoch(|result| { let epoch_response = result.unwrap(); - *current_id.borrow_mut() = epoch_response.epoch.id; + current_id = epoch_response.epoch.id; }) .query_incentives(None, None, None, |result| { let incentives_response = result.unwrap(); assert_eq!(incentives_response.incentives.len(), 1); - assert!(incentives_response.incentives[0].is_expired(current_id.borrow().clone())); + assert!(incentives_response.incentives[0].is_expired(current_id)); }) .manage_incentive( other.clone(), diff --git a/contracts/liquidity_hub/pool-manager/schema/pool-manager.json b/contracts/liquidity_hub/pool-manager/schema/pool-manager.json index 5ee19507..6e4527c7 100644 --- a/contracts/liquidity_hub/pool-manager/schema/pool-manager.json +++ b/contracts/liquidity_hub/pool-manager/schema/pool-manager.json @@ -8,16 +8,12 @@ "type": "object", "required": [ "fee_collector_addr", - "owner", "pool_creation_fee" ], "properties": { "fee_collector_addr": { "type": "string" }, - "owner": { - "type": "string" - }, "pool_creation_fee": { "$ref": "#/definitions/Coin" } @@ -58,11 +54,20 @@ "create_pair": { "type": "object", "required": [ + "asset_decimals", "asset_denoms", "pair_type", "pool_fees" ], "properties": { + "asset_decimals": { + "type": "array", + "items": { + "type": "integer", + "format": "uint8", + "minimum": 0.0 + } + }, "asset_denoms": { "type": "array", "items": { @@ -182,6 +187,7 @@ "additionalProperties": false }, { + "description": "Withdraws liquidity from the pool.", "type": "object", "required": [ "withdraw_liquidity" @@ -202,34 +208,6 @@ }, "additionalProperties": false }, - { - "description": "Adds native token info to the contract so it can instantiate pair contracts that include it", - "type": "object", - "required": [ - "add_native_token_decimals" - ], - "properties": { - "add_native_token_decimals": { - "type": "object", - "required": [ - "decimals", - "denom" - ], - "properties": { - "decimals": { - "type": "integer", - "format": "uint8", - "minimum": 0.0 - }, - "denom": { - "type": "string" - } - }, - "additionalProperties": false - } - }, - "additionalProperties": false - }, { "description": "Execute multiple [`SwapOperations`] to allow for multi-hop swaps.", "type": "object", @@ -310,6 +288,51 @@ }, "additionalProperties": false }, + { + "description": "Updates the configuration of the contract. If a field is not specified (i.e., set to `None`), it will not be modified.", + "type": "object", + "required": [ + "update_config" + ], + "properties": { + "update_config": { + "type": "object", + "properties": { + "feature_toggle": { + "description": "The new feature toggles of the contract, allowing fine-tuned control over which operations are allowed.", + "anyOf": [ + { + "$ref": "#/definitions/FeatureToggle" + }, + { + "type": "null" + } + ] + }, + "pool_creation_fee": { + "description": "The new fee that must be paid when a pool is created.", + "anyOf": [ + { + "$ref": "#/definitions/Coin" + }, + { + "type": "null" + } + ] + }, + "whale_lair_addr": { + "description": "The new whale-lair contract address.", + "type": [ + "string", + "null" + ] + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, { "description": "Update the contract's ownership. The `action` to be provided can be either to propose transferring ownership to an account, accept a pending ownership transfer, or renounce the ownership permanently.", "type": "object", @@ -442,6 +465,27 @@ } ] }, + "FeatureToggle": { + "description": "Pool feature toggle", + "type": "object", + "required": [ + "deposits_enabled", + "swaps_enabled", + "withdrawals_enabled" + ], + "properties": { + "deposits_enabled": { + "type": "boolean" + }, + "swaps_enabled": { + "type": "boolean" + }, + "withdrawals_enabled": { + "type": "boolean" + } + }, + "additionalProperties": false + }, "Fee": { "type": "object", "required": [ @@ -611,20 +655,38 @@ "title": "QueryMsg", "oneOf": [ { - "description": "Retrieves the decimals for the given native or ibc denom.", + "description": "Retrieves the contract's config.", + "type": "object", + "required": [ + "config" + ], + "properties": { + "config": { + "type": "object", + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Retrieves the decimals for the given asset.", "type": "object", "required": [ - "native_token_decimals" + "asset_decimals" ], "properties": { - "native_token_decimals": { + "asset_decimals": { "type": "object", "required": [ - "denom" + "denom", + "pair_identifier" ], "properties": { "denom": { "type": "string" + }, + "pair_identifier": { + "type": "string" } }, "additionalProperties": false @@ -799,22 +861,114 @@ }, "sudo": null, "responses": { - "native_token_decimals": { + "asset_decimals": { "$schema": "http://json-schema.org/draft-07/schema#", - "title": "NativeTokenDecimalsResponse", + "title": "AssetDecimalsResponse", + "description": "The response for the `AssetDecimals` query.", "type": "object", "required": [ - "decimals" + "decimals", + "denom", + "pair_identifier" ], "properties": { "decimals": { + "description": "The decimals for the requested denom.", "type": "integer", "format": "uint8", "minimum": 0.0 + }, + "denom": { + "description": "The queried denom in the given pair_identifier.", + "type": "string" + }, + "pair_identifier": { + "description": "The pair identifier to do the query for.", + "type": "string" } }, "additionalProperties": false }, + "config": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "ConfigResponse", + "type": "object", + "required": [ + "config" + ], + "properties": { + "config": { + "$ref": "#/definitions/Config" + } + }, + "additionalProperties": false, + "definitions": { + "Addr": { + "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", + "type": "string" + }, + "Coin": { + "type": "object", + "required": [ + "amount", + "denom" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Uint128" + }, + "denom": { + "type": "string" + } + } + }, + "Config": { + "type": "object", + "required": [ + "feature_toggle", + "pool_creation_fee", + "whale_lair_addr" + ], + "properties": { + "feature_toggle": { + "$ref": "#/definitions/FeatureToggle" + }, + "pool_creation_fee": { + "$ref": "#/definitions/Coin" + }, + "whale_lair_addr": { + "$ref": "#/definitions/Addr" + } + }, + "additionalProperties": false + }, + "FeatureToggle": { + "description": "Pool feature toggle", + "type": "object", + "required": [ + "deposits_enabled", + "swaps_enabled", + "withdrawals_enabled" + ], + "properties": { + "deposits_enabled": { + "type": "boolean" + }, + "swaps_enabled": { + "type": "boolean" + }, + "withdrawals_enabled": { + "type": "boolean" + } + }, + "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); ```", + "type": "string" + } + } + }, "ownership": { "$schema": "http://json-schema.org/draft-07/schema#", "title": "Ownership_for_String", @@ -912,45 +1066,14 @@ }, "pair": { "$schema": "http://json-schema.org/draft-07/schema#", - "title": "PairInfo", + "title": "PairInfoResponse", "type": "object", "required": [ - "asset_decimals", - "asset_denoms", - "assets", - "lp_denom", - "pair_type", - "pool_fees" + "pair_info" ], "properties": { - "asset_decimals": { - "type": "array", - "items": { - "type": "integer", - "format": "uint8", - "minimum": 0.0 - } - }, - "asset_denoms": { - "type": "array", - "items": { - "type": "string" - } - }, - "assets": { - "type": "array", - "items": { - "$ref": "#/definitions/Coin" - } - }, - "lp_denom": { - "type": "string" - }, - "pair_type": { - "$ref": "#/definitions/PairType" - }, - "pool_fees": { - "$ref": "#/definitions/PoolFee" + "pair_info": { + "$ref": "#/definitions/PairInfo" } }, "additionalProperties": false, @@ -986,6 +1109,49 @@ }, "additionalProperties": false }, + "PairInfo": { + "type": "object", + "required": [ + "asset_decimals", + "asset_denoms", + "assets", + "lp_denom", + "pair_type", + "pool_fees" + ], + "properties": { + "asset_decimals": { + "type": "array", + "items": { + "type": "integer", + "format": "uint8", + "minimum": 0.0 + } + }, + "asset_denoms": { + "type": "array", + "items": { + "type": "string" + } + }, + "assets": { + "type": "array", + "items": { + "$ref": "#/definitions/Coin" + } + }, + "lp_denom": { + "type": "string" + }, + "pair_type": { + "$ref": "#/definitions/PairType" + }, + "pool_fees": { + "$ref": "#/definitions/PoolFee" + } + }, + "additionalProperties": false + }, "PairType": { "oneOf": [ { @@ -1146,11 +1312,17 @@ }, "swap_route": { "$schema": "http://json-schema.org/draft-07/schema#", - "title": "Array_of_SwapOperation", - "type": "array", - "items": { - "$ref": "#/definitions/SwapOperation" + "title": "SwapRouteResponse", + "type": "object", + "required": [ + "swap_route" + ], + "properties": { + "swap_route": { + "$ref": "#/definitions/SwapRoute" + } }, + "additionalProperties": false, "definitions": { "SwapOperation": { "oneOf": [ @@ -1184,16 +1356,48 @@ "additionalProperties": false } ] + }, + "SwapRoute": { + "type": "object", + "required": [ + "ask_asset_denom", + "offer_asset_denom", + "swap_operations" + ], + "properties": { + "ask_asset_denom": { + "type": "string" + }, + "offer_asset_denom": { + "type": "string" + }, + "swap_operations": { + "type": "array", + "items": { + "$ref": "#/definitions/SwapOperation" + } + } + }, + "additionalProperties": false } } }, "swap_routes": { "$schema": "http://json-schema.org/draft-07/schema#", - "title": "Array_of_SwapRouteResponse", - "type": "array", - "items": { - "$ref": "#/definitions/SwapRouteResponse" + "title": "SwapRoutesResponse", + "type": "object", + "required": [ + "swap_routes" + ], + "properties": { + "swap_routes": { + "type": "array", + "items": { + "$ref": "#/definitions/SwapRoute" + } + } }, + "additionalProperties": false, "definitions": { "SwapOperation": { "oneOf": [ @@ -1228,12 +1432,12 @@ } ] }, - "SwapRouteResponse": { + "SwapRoute": { "type": "object", "required": [ "ask_asset_denom", "offer_asset_denom", - "swap_route" + "swap_operations" ], "properties": { "ask_asset_denom": { @@ -1242,7 +1446,7 @@ "offer_asset_denom": { "type": "string" }, - "swap_route": { + "swap_operations": { "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 2f526569..a268cfc0 100644 --- a/contracts/liquidity_hub/pool-manager/schema/raw/execute.json +++ b/contracts/liquidity_hub/pool-manager/schema/raw/execute.json @@ -11,11 +11,20 @@ "create_pair": { "type": "object", "required": [ + "asset_decimals", "asset_denoms", "pair_type", "pool_fees" ], "properties": { + "asset_decimals": { + "type": "array", + "items": { + "type": "integer", + "format": "uint8", + "minimum": 0.0 + } + }, "asset_denoms": { "type": "array", "items": { @@ -135,6 +144,7 @@ "additionalProperties": false }, { + "description": "Withdraws liquidity from the pool.", "type": "object", "required": [ "withdraw_liquidity" @@ -155,34 +165,6 @@ }, "additionalProperties": false }, - { - "description": "Adds native token info to the contract so it can instantiate pair contracts that include it", - "type": "object", - "required": [ - "add_native_token_decimals" - ], - "properties": { - "add_native_token_decimals": { - "type": "object", - "required": [ - "decimals", - "denom" - ], - "properties": { - "decimals": { - "type": "integer", - "format": "uint8", - "minimum": 0.0 - }, - "denom": { - "type": "string" - } - }, - "additionalProperties": false - } - }, - "additionalProperties": false - }, { "description": "Execute multiple [`SwapOperations`] to allow for multi-hop swaps.", "type": "object", @@ -263,6 +245,51 @@ }, "additionalProperties": false }, + { + "description": "Updates the configuration of the contract. If a field is not specified (i.e., set to `None`), it will not be modified.", + "type": "object", + "required": [ + "update_config" + ], + "properties": { + "update_config": { + "type": "object", + "properties": { + "feature_toggle": { + "description": "The new feature toggles of the contract, allowing fine-tuned control over which operations are allowed.", + "anyOf": [ + { + "$ref": "#/definitions/FeatureToggle" + }, + { + "type": "null" + } + ] + }, + "pool_creation_fee": { + "description": "The new fee that must be paid when a pool is created.", + "anyOf": [ + { + "$ref": "#/definitions/Coin" + }, + { + "type": "null" + } + ] + }, + "whale_lair_addr": { + "description": "The new whale-lair contract address.", + "type": [ + "string", + "null" + ] + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, { "description": "Update the contract's ownership. The `action` to be provided can be either to propose transferring ownership to an account, accept a pending ownership transfer, or renounce the ownership permanently.", "type": "object", @@ -395,6 +422,27 @@ } ] }, + "FeatureToggle": { + "description": "Pool feature toggle", + "type": "object", + "required": [ + "deposits_enabled", + "swaps_enabled", + "withdrawals_enabled" + ], + "properties": { + "deposits_enabled": { + "type": "boolean" + }, + "swaps_enabled": { + "type": "boolean" + }, + "withdrawals_enabled": { + "type": "boolean" + } + }, + "additionalProperties": false + }, "Fee": { "type": "object", "required": [ diff --git a/contracts/liquidity_hub/pool-manager/schema/raw/instantiate.json b/contracts/liquidity_hub/pool-manager/schema/raw/instantiate.json index ba6a54e3..e87e515e 100644 --- a/contracts/liquidity_hub/pool-manager/schema/raw/instantiate.json +++ b/contracts/liquidity_hub/pool-manager/schema/raw/instantiate.json @@ -4,16 +4,12 @@ "type": "object", "required": [ "fee_collector_addr", - "owner", "pool_creation_fee" ], "properties": { "fee_collector_addr": { "type": "string" }, - "owner": { - "type": "string" - }, "pool_creation_fee": { "$ref": "#/definitions/Coin" } diff --git a/contracts/liquidity_hub/pool-manager/schema/raw/query.json b/contracts/liquidity_hub/pool-manager/schema/raw/query.json index e43c3d5e..e25110ff 100644 --- a/contracts/liquidity_hub/pool-manager/schema/raw/query.json +++ b/contracts/liquidity_hub/pool-manager/schema/raw/query.json @@ -3,20 +3,38 @@ "title": "QueryMsg", "oneOf": [ { - "description": "Retrieves the decimals for the given native or ibc denom.", + "description": "Retrieves the contract's config.", "type": "object", "required": [ - "native_token_decimals" + "config" ], "properties": { - "native_token_decimals": { + "config": { + "type": "object", + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Retrieves the decimals for the given asset.", + "type": "object", + "required": [ + "asset_decimals" + ], + "properties": { + "asset_decimals": { "type": "object", "required": [ - "denom" + "denom", + "pair_identifier" ], "properties": { "denom": { "type": "string" + }, + "pair_identifier": { + "type": "string" } }, "additionalProperties": false 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 new file mode 100644 index 00000000..a64999bd --- /dev/null +++ b/contracts/liquidity_hub/pool-manager/schema/raw/response_to_asset_decimals.json @@ -0,0 +1,28 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "AssetDecimalsResponse", + "description": "The response for the `AssetDecimals` query.", + "type": "object", + "required": [ + "decimals", + "denom", + "pair_identifier" + ], + "properties": { + "decimals": { + "description": "The decimals for the requested denom.", + "type": "integer", + "format": "uint8", + "minimum": 0.0 + }, + "denom": { + "description": "The queried denom in the given pair_identifier.", + "type": "string" + }, + "pair_identifier": { + "description": "The pair identifier to do the query for.", + "type": "string" + } + }, + "additionalProperties": false +} 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 new file mode 100644 index 00000000..7284764a --- /dev/null +++ b/contracts/liquidity_hub/pool-manager/schema/raw/response_to_config.json @@ -0,0 +1,80 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "ConfigResponse", + "type": "object", + "required": [ + "config" + ], + "properties": { + "config": { + "$ref": "#/definitions/Config" + } + }, + "additionalProperties": false, + "definitions": { + "Addr": { + "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", + "type": "string" + }, + "Coin": { + "type": "object", + "required": [ + "amount", + "denom" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Uint128" + }, + "denom": { + "type": "string" + } + } + }, + "Config": { + "type": "object", + "required": [ + "feature_toggle", + "pool_creation_fee", + "whale_lair_addr" + ], + "properties": { + "feature_toggle": { + "$ref": "#/definitions/FeatureToggle" + }, + "pool_creation_fee": { + "$ref": "#/definitions/Coin" + }, + "whale_lair_addr": { + "$ref": "#/definitions/Addr" + } + }, + "additionalProperties": false + }, + "FeatureToggle": { + "description": "Pool feature toggle", + "type": "object", + "required": [ + "deposits_enabled", + "swaps_enabled", + "withdrawals_enabled" + ], + "properties": { + "deposits_enabled": { + "type": "boolean" + }, + "swaps_enabled": { + "type": "boolean" + }, + "withdrawals_enabled": { + "type": "boolean" + } + }, + "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); ```", + "type": "string" + } + } +} diff --git a/contracts/liquidity_hub/pool-manager/schema/raw/response_to_native_token_decimals.json b/contracts/liquidity_hub/pool-manager/schema/raw/response_to_native_token_decimals.json index 70700bba..c60a6606 100644 --- a/contracts/liquidity_hub/pool-manager/schema/raw/response_to_native_token_decimals.json +++ b/contracts/liquidity_hub/pool-manager/schema/raw/response_to_native_token_decimals.json @@ -1,15 +1,27 @@ { "$schema": "http://json-schema.org/draft-07/schema#", "title": "NativeTokenDecimalsResponse", + "description": "The response for the `NativeTokenDecimals` query.", "type": "object", "required": [ - "decimals" + "decimals", + "denom", + "pair_identifier" ], "properties": { "decimals": { + "description": "The decimals for the requested denom.", "type": "integer", "format": "uint8", "minimum": 0.0 + }, + "denom": { + "description": "The denom to get the decimals in the given pair_identifier for.", + "type": "string" + }, + "pair_identifier": { + "description": "The pair identifier to do the query for.", + "type": "string" } }, "additionalProperties": false diff --git a/contracts/liquidity_hub/pool-manager/schema/raw/response_to_pair.json b/contracts/liquidity_hub/pool-manager/schema/raw/response_to_pair.json index fd23cc1a..ff5cb97b 100644 --- a/contracts/liquidity_hub/pool-manager/schema/raw/response_to_pair.json +++ b/contracts/liquidity_hub/pool-manager/schema/raw/response_to_pair.json @@ -1,44 +1,13 @@ { "$schema": "http://json-schema.org/draft-07/schema#", - "title": "PairInfo", + "title": "PairInfoResponse", "type": "object", "required": [ - "asset_decimals", - "asset_denoms", - "assets", - "lp_denom", - "pair_type", - "pool_fees" + "pair_info" ], "properties": { - "asset_decimals": { - "type": "array", - "items": { - "type": "integer", - "format": "uint8", - "minimum": 0.0 - } - }, - "asset_denoms": { - "type": "array", - "items": { - "type": "string" - } - }, - "assets": { - "type": "array", - "items": { - "$ref": "#/definitions/Coin" - } - }, - "lp_denom": { - "type": "string" - }, - "pair_type": { - "$ref": "#/definitions/PairType" - }, - "pool_fees": { - "$ref": "#/definitions/PoolFee" + "pair_info": { + "$ref": "#/definitions/PairInfo" } }, "additionalProperties": false, @@ -74,6 +43,49 @@ }, "additionalProperties": false }, + "PairInfo": { + "type": "object", + "required": [ + "asset_decimals", + "asset_denoms", + "assets", + "lp_denom", + "pair_type", + "pool_fees" + ], + "properties": { + "asset_decimals": { + "type": "array", + "items": { + "type": "integer", + "format": "uint8", + "minimum": 0.0 + } + }, + "asset_denoms": { + "type": "array", + "items": { + "type": "string" + } + }, + "assets": { + "type": "array", + "items": { + "$ref": "#/definitions/Coin" + } + }, + "lp_denom": { + "type": "string" + }, + "pair_type": { + "$ref": "#/definitions/PairType" + }, + "pool_fees": { + "$ref": "#/definitions/PoolFee" + } + }, + "additionalProperties": false + }, "PairType": { "oneOf": [ { 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 e12f76f7..82801ed3 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,10 +1,16 @@ { "$schema": "http://json-schema.org/draft-07/schema#", - "title": "Array_of_SwapOperation", - "type": "array", - "items": { - "$ref": "#/definitions/SwapOperation" + "title": "SwapRouteResponse", + "type": "object", + "required": [ + "swap_route" + ], + "properties": { + "swap_route": { + "$ref": "#/definitions/SwapRoute" + } }, + "additionalProperties": false, "definitions": { "SwapOperation": { "oneOf": [ @@ -38,6 +44,29 @@ "additionalProperties": false } ] + }, + "SwapRoute": { + "type": "object", + "required": [ + "ask_asset_denom", + "offer_asset_denom", + "swap_operations" + ], + "properties": { + "ask_asset_denom": { + "type": "string" + }, + "offer_asset_denom": { + "type": "string" + }, + "swap_operations": { + "type": "array", + "items": { + "$ref": "#/definitions/SwapOperation" + } + } + }, + "additionalProperties": false } } } 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 28f3c58d..c1f6e47c 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,10 +1,19 @@ { "$schema": "http://json-schema.org/draft-07/schema#", - "title": "Array_of_SwapRouteResponse", - "type": "array", - "items": { - "$ref": "#/definitions/SwapRouteResponse" + "title": "SwapRoutesResponse", + "type": "object", + "required": [ + "swap_routes" + ], + "properties": { + "swap_routes": { + "type": "array", + "items": { + "$ref": "#/definitions/SwapRoute" + } + } }, + "additionalProperties": false, "definitions": { "SwapOperation": { "oneOf": [ @@ -39,12 +48,12 @@ } ] }, - "SwapRouteResponse": { + "SwapRoute": { "type": "object", "required": [ "ask_asset_denom", "offer_asset_denom", - "swap_route" + "swap_operations" ], "properties": { "ask_asset_denom": { @@ -53,7 +62,7 @@ "offer_asset_denom": { "type": "string" }, - "swap_route": { + "swap_operations": { "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 bf096dd2..44ec8b86 100644 --- a/contracts/liquidity_hub/pool-manager/src/contract.rs +++ b/contracts/liquidity_hub/pool-manager/src/contract.rs @@ -8,8 +8,9 @@ use cosmwasm_std::{ }; use cw2::set_contract_version; use semver::Version; -use white_whale_std::pool_manager::{ExecuteMsg, InstantiateMsg, MigrateMsg, QueryMsg}; -use white_whale_std::pool_network::pair::FeatureToggle; +use white_whale_std::pool_manager::{ + ExecuteMsg, FeatureToggle, InstantiateMsg, MigrateMsg, PairInfoResponse, QueryMsg, +}; // version info for migration info const CONTRACT_NAME: &str = "crates.io:ww-pool-manager"; @@ -19,13 +20,12 @@ const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION"); pub fn instantiate( deps: DepsMut, _env: Env, - _info: MessageInfo, + info: MessageInfo, msg: InstantiateMsg, ) -> Result { set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?; let config: Config = Config { whale_lair_addr: deps.api.addr_validate(&msg.fee_collector_addr)?, - owner: deps.api.addr_validate(&msg.owner)?, // We must set a creation fee on instantiation to prevent spamming of pools pool_creation_fee: msg.pool_creation_fee, feature_toggle: FeatureToggle { @@ -37,7 +37,7 @@ pub fn instantiate( MANAGER_CONFIG.save(deps.storage, &config)?; // initialize vault counter PAIR_COUNTER.save(deps.storage, &0u64)?; - cw_ownable::initialize_owner(deps.storage, deps.api, Some(msg.owner.as_str()))?; + cw_ownable::initialize_owner(deps.storage, deps.api, Some(info.sender.as_str()))?; Ok(Response::default()) } @@ -52,6 +52,7 @@ pub fn execute( match msg { ExecuteMsg::CreatePair { asset_denoms, + asset_decimals, pool_fees, pair_type, pair_identifier, @@ -60,6 +61,7 @@ pub fn execute( env, info, asset_denoms, + asset_decimals, pool_fees, pair_type, pair_identifier, @@ -84,11 +86,7 @@ pub fn execute( to, pair_identifier, } => { - let to_addr = if let Some(to_addr) = to { - Some(deps.api.addr_validate(&to_addr)?) - } else { - None - }; + let to_addr = to.map(|addr| deps.api.addr_validate(&addr)).transpose()?; swap::commands::swap( deps, @@ -106,9 +104,6 @@ pub fn execute( ExecuteMsg::WithdrawLiquidity { pair_identifier } => { liquidity::commands::withdraw_liquidity(deps, env, info, pair_identifier) } - ExecuteMsg::AddNativeTokenDecimals { denom, decimals } => { - manager::commands::add_native_token_decimals(deps, env, denom, decimals) - } ExecuteMsg::UpdateOwnership(action) => { Ok( cw_ownable::update_ownership(deps, &env.block, &info.sender, action).map( @@ -152,6 +147,17 @@ pub fn execute( // ) // } ExecuteMsg::AddSwapRoutes { swap_routes: _ } => Ok(Response::new()), + ExecuteMsg::UpdateConfig { + whale_lair_addr, + pool_creation_fee, + feature_toggle, + } => manager::update_config( + deps, + info, + whale_lair_addr, + pool_creation_fee, + feature_toggle, + ), } } @@ -174,9 +180,15 @@ fn optional_addr_validate( #[cfg_attr(not(feature = "library"), entry_point)] pub fn query(deps: Deps, env: Env, msg: QueryMsg) -> Result { match msg { - QueryMsg::NativeTokenDecimals { denom } => Ok(to_json_binary( - &queries::query_native_token_decimal(deps, denom)?, - )?), + QueryMsg::Config {} => Ok(to_json_binary(&queries::query_config(deps)?)?), + QueryMsg::AssetDecimals { + pair_identifier, + denom, + } => Ok(to_json_binary(&queries::query_asset_decimals( + deps, + pair_identifier, + denom, + )?)?), QueryMsg::Simulation { offer_asset, ask_asset, @@ -224,9 +236,9 @@ pub fn query(deps: Deps, env: Env, msg: QueryMsg) -> Result Ok(to_json_binary(&get_swap_routes(deps)?)?), QueryMsg::Ownership {} => Ok(to_json_binary(&cw_ownable::get_ownership(deps.storage)?)?), - QueryMsg::Pair { pair_identifier } => Ok(to_json_binary( - &PAIRS.load(deps.storage, &pair_identifier)?, - )?), + QueryMsg::Pair { pair_identifier } => Ok(to_json_binary(&PairInfoResponse { + pair_info: PAIRS.load(deps.storage, &pair_identifier)?, + })?), } } diff --git a/contracts/liquidity_hub/pool-manager/src/manager/commands.rs b/contracts/liquidity_hub/pool-manager/src/manager/commands.rs index 6fa15ca2..1beda63f 100644 --- a/contracts/liquidity_hub/pool-manager/src/manager/commands.rs +++ b/contracts/liquidity_hub/pool-manager/src/manager/commands.rs @@ -1,13 +1,9 @@ use cosmwasm_std::{ attr, Attribute, Coin, CosmosMsg, DepsMut, Env, MessageInfo, Response, Uint128, }; -use white_whale_std::{ - fee::PoolFee, - pool_network::{asset::PairType, querier::query_native_decimals}, - whale_lair::fill_rewards_msg_coin, -}; +use white_whale_std::{fee::PoolFee, pool_network::asset::PairType, whale_lair::fill_rewards_msg}; -use crate::state::{get_pair_by_identifier, NATIVE_TOKEN_DECIMALS, PAIR_COUNTER}; +use crate::state::{get_pair_by_identifier, PAIR_COUNTER}; use crate::{ state::{Config, MANAGER_CONFIG, PAIRS}, ContractError, @@ -15,7 +11,6 @@ use crate::{ use white_whale_std::lp_common::LP_SYMBOL; use white_whale_std::pool_manager::PairInfo; -use white_whale_std::pool_network::querier::query_balance; pub const MAX_ASSETS_PER_POOL: usize = 4; @@ -40,6 +35,7 @@ pub const MAX_ASSETS_PER_POOL: usize = 4; /// "uatom".into(), /// "uscrt".into(), /// ]; +/// let asset_decimals = vec![6, 6]; /// #[cfg(not(feature = "osmosis"))] /// let pool_fees = PoolFee { /// protocol_fee: Fee { @@ -73,7 +69,7 @@ pub const MAX_ASSETS_PER_POOL: usize = 4; /// let pair_type = PairType::ConstantProduct; /// let token_factory_lp = false; /// -/// let response = create_pair(deps, env, info, asset_infos, pool_fees, pair_type, None)?; +/// let response = create_pair(deps, env, info, asset_infos, asset_decimals, pool_fees, pair_type, None)?; /// # Ok(response) /// # } /// ``` @@ -83,7 +79,8 @@ pub fn create_pair( deps: DepsMut, env: Env, info: MessageInfo, - asset_denoms: Vec, //Review just a vec + asset_denoms: Vec, + asset_decimals: Vec, pool_fees: PoolFee, pair_type: PairType, pair_identifier: Option, @@ -115,22 +112,6 @@ pub fn create_pair( creation_fee, )?); - let asset_decimals_vec = asset_denoms - .iter() - .map(|asset| { - //todo pass the asset_decimals in the create_pair msg. Let the user creating the pool - // defining the decimals, they are incentivized to do it right as they are paying a fee - - let _ = query_native_decimals( - &deps.querier, - env.contract.address.clone(), - asset.to_string(), - ); - - 0u8 - }) - .collect::>(); - // Check if the asset infos are the same if asset_denoms .iter() @@ -184,7 +165,7 @@ pub fn create_pair( asset_denoms, pair_type: pair_type.clone(), lp_denom: lp_asset.clone(), - asset_decimals: asset_decimals_vec, + asset_decimals, pool_fees, assets, }, @@ -214,22 +195,3 @@ pub fn create_pair( .add_attributes(attributes) .add_messages(messages)) } - -/// Adds native/ibc token with decimals to the factory's whitelist so it can create pairs with that asset -pub fn add_native_token_decimals( - deps: DepsMut, - env: Env, - denom: String, - decimals: u8, -) -> Result { - let balance = query_balance(&deps.querier, env.contract.address, denom.to_string())?; - if balance.is_zero() { - return Err(ContractError::InvalidVerificationBalance {}); - } - NATIVE_TOKEN_DECIMALS.save(deps.storage, denom.as_bytes(), &decimals)?; - Ok(Response::new().add_attributes(vec![ - ("action", "add_allow_native_token"), - ("denom", &denom), - ("decimals", &decimals.to_string()), - ])) -} diff --git a/contracts/liquidity_hub/pool-manager/src/manager/mod.rs b/contracts/liquidity_hub/pool-manager/src/manager/mod.rs index 82b6da3c..6a13468f 100644 --- a/contracts/liquidity_hub/pool-manager/src/manager/mod.rs +++ b/contracts/liquidity_hub/pool-manager/src/manager/mod.rs @@ -1 +1,4 @@ pub mod commands; + +mod update_config; +pub use update_config::update_config; diff --git a/contracts/liquidity_hub/pool-manager/src/manager/update_config.rs b/contracts/liquidity_hub/pool-manager/src/manager/update_config.rs new file mode 100644 index 00000000..5f4d2fbf --- /dev/null +++ b/contracts/liquidity_hub/pool-manager/src/manager/update_config.rs @@ -0,0 +1,33 @@ +use cosmwasm_std::{Coin, DepsMut, MessageInfo, Response}; +use white_whale_std::pool_manager::{Config, FeatureToggle}; + +use crate::{state::MANAGER_CONFIG, ContractError}; + +pub fn update_config( + deps: DepsMut, + info: MessageInfo, + whale_lair_addr: Option, + pool_creation_fee: Option, + feature_toggle: Option, +) -> Result { + // permission check + cw_ownable::assert_owner(deps.storage, &info.sender)?; + + MANAGER_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.whale_lair_addr = whale_lair_addr; + } + + if let Some(pool_creation_fee) = pool_creation_fee { + config.pool_creation_fee = pool_creation_fee; + } + + if let Some(feature_toggle) = feature_toggle { + config.feature_toggle = feature_toggle; + } + Ok::(config) + })?; + + Ok(Response::default().add_attribute("action", "update_config")) +} diff --git a/contracts/liquidity_hub/pool-manager/src/queries.rs b/contracts/liquidity_hub/pool-manager/src/queries.rs index 3090ed6a..36498988 100644 --- a/contracts/liquidity_hub/pool-manager/src/queries.rs +++ b/contracts/liquidity_hub/pool-manager/src/queries.rs @@ -1,15 +1,17 @@ use std::cmp::Ordering; use cosmwasm_std::{Coin, Decimal256, Deps, Env, Fraction, Order, StdResult, Uint128}; -use white_whale_std::pool_manager::{SwapOperation, SwapRouteResponse}; + +use white_whale_std::pool_manager::{ + AssetDecimalsResponse, Config, SwapRoute, SwapRouteResponse, SwapRoutesResponse, +}; use white_whale_std::pool_network::{ asset::PairType, - factory::NativeTokenDecimalsResponse, pair::{ReverseSimulationResponse, SimulationResponse}, // router::SimulateSwapOperationsResponse, }; -use crate::state::NATIVE_TOKEN_DECIMALS; +use crate::state::MANAGER_CONFIG; use crate::{ helpers::{self, calculate_stableswap_y, StableSwapDirection}, state::get_pair_by_identifier, @@ -17,14 +19,29 @@ use crate::{ }; use crate::{math::Decimal256Helper, state::SWAP_ROUTES}; -/// Query the native token decimals -pub fn query_native_token_decimal( +/// Query the config of the contract. +pub fn query_config(deps: Deps) -> Result { + Ok(MANAGER_CONFIG.load(deps.storage)?) +} + +/// Query the native asset decimals +pub fn query_asset_decimals( deps: Deps, + pair_identifier: String, denom: String, -) -> Result { - let decimals = NATIVE_TOKEN_DECIMALS.load(deps.storage, denom.as_bytes())?; - - Ok(NativeTokenDecimalsResponse { decimals }) +) -> Result { + let pair_info = get_pair_by_identifier(&deps, &pair_identifier)?; + let decimal_index = pair_info + .asset_denoms + .iter() + .position(|d| d.clone() == denom) + .ok_or(ContractError::AssetMismatch {})?; + + Ok(AssetDecimalsResponse { + pair_identifier, + denom, + decimals: pair_info.asset_decimals[decimal_index], + }) } // Simulate a swap with the provided asset to determine the amount of the other asset that would be received @@ -228,40 +245,48 @@ 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 -pub fn get_swap_routes(deps: Deps) -> Result, ContractError> { - let swap_routes: Vec = SWAP_ROUTES +pub fn get_swap_routes(deps: Deps) -> Result { + let swap_routes: Vec = SWAP_ROUTES .range(deps.storage, None, None, Order::Ascending) .map(|item| { let swap_info = item?; // Destructure key into (offer_asset, ask_asset) let (offer_asset_denom, ask_asset_denom) = swap_info.0; // Destructure value into vec of SwapOperation - let swap_route = swap_info.1; + let swap_operations = swap_info.1; - Ok(SwapRouteResponse { + Ok(SwapRoute { offer_asset_denom, ask_asset_denom, - swap_route, + swap_operations, }) }) - .collect::>>()?; + .collect::>>()?; - Ok(swap_routes) + Ok(SwapRoutesResponse { swap_routes }) } pub fn get_swap_route( deps: Deps, offer_asset_denom: String, ask_asset_denom: String, -) -> Result, ContractError> { +) -> Result { let swap_route_key = SWAP_ROUTES.key((&offer_asset_denom, &ask_asset_denom)); - swap_route_key - .load(deps.storage) - .map_err(|_| ContractError::NoSwapRouteForAssets { - offer_asset: offer_asset_denom, - ask_asset: ask_asset_denom, - }) + let swap_operations = + swap_route_key + .load(deps.storage) + .map_err(|_| ContractError::NoSwapRouteForAssets { + offer_asset: offer_asset_denom.clone(), + ask_asset: ask_asset_denom.clone(), + })?; + Ok(SwapRouteResponse { + swap_route: SwapRoute { + offer_asset_denom, + ask_asset_denom, + swap_operations, + }, + }) } // TODO: May need to remove this for a new implementation, router swap operation queries diff --git a/contracts/liquidity_hub/pool-manager/src/state.rs b/contracts/liquidity_hub/pool-manager/src/state.rs index 896bf509..d88423aa 100644 --- a/contracts/liquidity_hub/pool-manager/src/state.rs +++ b/contracts/liquidity_hub/pool-manager/src/state.rs @@ -1,8 +1,6 @@ -use cosmwasm_schema::cw_serde; -use cosmwasm_std::{Addr, Coin, Deps}; +use cosmwasm_std::Deps; use cw_storage_plus::{Index, IndexList, IndexedMap, Item, Map, UniqueIndex}; use white_whale_std::pool_manager::{PairInfo, SwapOperation}; -use white_whale_std::pool_network::pair::FeatureToggle; use crate::ContractError; @@ -34,21 +32,10 @@ pub fn get_pair_by_identifier( .may_load(deps.storage, pair_identifier)? .ok_or(ContractError::UnExistingPair {}) } -// Remove after adding decimals to pair info -pub const NATIVE_TOKEN_DECIMALS: Map<&[u8], u8> = Map::new("allow_native_token"); // Swap routes are used to establish defined routes for a given fee token to a desired fee token and is used for fee collection pub const SWAP_ROUTES: Map<(&str, &str), Vec> = Map::new("swap_routes"); +pub use white_whale_std::pool_manager::Config; pub const MANAGER_CONFIG: Item = Item::new("manager_config"); pub const PAIR_COUNTER: Item = Item::new("vault_count"); - -#[cw_serde] -pub struct Config { - pub whale_lair_addr: Addr, - pub owner: Addr, - // We must set a creation fee on instantiation to prevent spamming of pools - pub pool_creation_fee: Coin, - // Whether or not swaps, deposits, and withdrawals are enabled - pub feature_toggle: FeatureToggle, -} 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 9a443605..c928aa7c 100644 --- a/contracts/liquidity_hub/pool-manager/src/swap/perform_swap.rs +++ b/contracts/liquidity_hub/pool-manager/src/swap/perform_swap.rs @@ -129,7 +129,6 @@ pub fn perform_swap( swap_fee_asset, burn_fee_asset, protocol_fee_asset, - pair_info, spread_amount: swap_computation.spread_amount, }) 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 2ffe6794..ccadf96f 100644 --- a/contracts/liquidity_hub/pool-manager/src/tests/integration_tests.rs +++ b/contracts/liquidity_hub/pool-manager/src/tests/integration_tests.rs @@ -16,74 +16,12 @@ fn instantiate_normal() { suite.instantiate(suite.senders[0].to_string()); } -#[test] -fn verify_ownership() { - let mut suite = TestingSuite::default_with_balances(vec![]); - let creator = suite.creator(); - let other = suite.senders[1].clone(); - let unauthorized = suite.senders[2].clone(); - - suite - .instantiate_default() - .query_ownership(|result| { - let ownership = result.unwrap(); - assert_eq!(Addr::unchecked(ownership.owner.unwrap()), creator); - }) - .update_ownership( - unauthorized, - cw_ownable::Action::TransferOwnership { - new_owner: other.to_string(), - expiry: None, - }, - |result| { - let err = result.unwrap_err().downcast::().unwrap(); - - match err { - ContractError::OwnershipError { .. } => {} - _ => panic!("Wrong error type, should return ContractError::OwnershipError"), - } - }, - ) - .update_ownership( - creator, - cw_ownable::Action::TransferOwnership { - new_owner: other.to_string(), - expiry: None, - }, - |result| { - result.unwrap(); - }, - ) - .update_ownership( - other.clone(), - cw_ownable::Action::AcceptOwnership, - |result| { - result.unwrap(); - }, - ) - .query_ownership(|result| { - let ownership = result.unwrap(); - assert_eq!(Addr::unchecked(ownership.owner.unwrap()), other); - }) - .update_ownership( - other.clone(), - cw_ownable::Action::RenounceOwnership, - |result| { - result.unwrap(); - }, - ) - .query_ownership(|result| { - let ownership = result.unwrap(); - assert!(ownership.owner.is_none()); - }); -} - // add features `token_factory` so tests are compiled using the correct flag #[test] fn deposit_and_withdraw_sanity_check() { let mut suite = TestingSuite::default_with_balances(vec![ - coin(1_000_001u128, "uwhale".to_string()), - coin(1_000_001u128, "uluna".to_string()), + coin(1_000_000u128, "uwhale".to_string()), + coin(1_000_000u128, "uluna".to_string()), coin(1_000u128, "uusd".to_string()), ]); let creator = suite.creator(); @@ -126,21 +64,18 @@ fn deposit_and_withdraw_sanity_check() { }; // Create a pair - suite - .instantiate_default() - .add_native_token_decimals(creator.clone(), "uwhale".to_string(), 6) - .add_native_token_decimals(creator.clone(), "uluna".to_string(), 6) - .create_pair( - creator.clone(), - asset_denoms, - pool_fees, - white_whale_std::pool_network::asset::PairType::ConstantProduct, - Some("whale-uluna".to_string()), - vec![coin(1000, "uusd")], - |result| { - result.unwrap(); - }, - ); + suite.instantiate_default().create_pair( + creator.clone(), + asset_denoms, + vec![6u8, 6u8], + pool_fees, + white_whale_std::pool_network::asset::PairType::ConstantProduct, + Some("whale-uluna".to_string()), + vec![coin(1000, "uusd")], + |result| { + result.unwrap(); + }, + ); let contract_addr = suite.pool_manager_addr.clone(); let lp_denom = suite.get_lp_denom("whale-uluna".to_string()); @@ -281,25 +216,23 @@ mod pair_creation_failures { extra_fees: vec![], }; // Create a pair - suite - .instantiate_default() - .add_native_token_decimals(creator.clone(), "uwhale".to_string(), 6) - .create_pair( - creator.clone(), - asset_infos, - pool_fees, - white_whale_std::pool_network::asset::PairType::ConstantProduct, - None, - vec![coin(90, "uusd")], - |result| { - let err = result.unwrap_err().downcast::().unwrap(); + suite.instantiate_default().create_pair( + creator.clone(), + asset_infos, + vec![6u8, 6u8], + pool_fees, + white_whale_std::pool_network::asset::PairType::ConstantProduct, + None, + vec![coin(90, "uusd")], + |result| { + let err = result.unwrap_err().downcast::().unwrap(); - match err { - ContractError::InvalidPairCreationFee { .. } => {} - _ => panic!("Wrong error type, should return ContractError::Unauthorized"), - } - }, - ); + match err { + ContractError::InvalidPairCreationFee { .. } => {} + _ => panic!("Wrong error type, should return ContractError::Unauthorized"), + } + }, + ); } #[test] @@ -351,10 +284,10 @@ mod pair_creation_failures { // Create a pair suite .instantiate_default() - .add_native_token_decimals(creator.clone(), "uwhale".to_string(), 6) .create_pair( creator.clone(), asset_infos.clone(), + vec![6u8, 6u8], pool_fees.clone(), white_whale_std::pool_network::asset::PairType::ConstantProduct, Some("mycoolpair".to_string()), @@ -366,6 +299,7 @@ mod pair_creation_failures { .create_pair( creator.clone(), asset_infos, + vec![6u8, 6u8], pool_fees, white_whale_std::pool_network::asset::PairType::ConstantProduct, Some("mycoolpair".to_string()), @@ -388,9 +322,9 @@ mod router { #[test] fn basic_swap_operations_test() { let mut suite = TestingSuite::default_with_balances(vec![ - coin(1_000_000_001u128, "uwhale".to_string()), + coin(1_000_000_000u128, "uwhale".to_string()), coin(1_000_000_000u128, "uluna".to_string()), - coin(1_000_000_001u128, "uusd".to_string()), + coin(1_000_000_000u128, "uusd".to_string()), ]); let creator = suite.creator(); let _other = suite.senders[1].clone(); @@ -433,12 +367,10 @@ mod router { // Create a pair suite .instantiate_default() - .add_native_token_decimals(creator.clone(), "uwhale".to_string(), 6) - .add_native_token_decimals(creator.clone(), "uluna".to_string(), 6) - .add_native_token_decimals(creator.clone(), "uusd".to_string(), 6) .create_pair( creator.clone(), first_pair, + vec![6u8, 6u8], pool_fees.clone(), white_whale_std::pool_network::asset::PairType::ConstantProduct, Some("whale-uluna".to_string()), @@ -450,6 +382,7 @@ mod router { .create_pair( creator.clone(), second_pair, + vec![6u8, 6u8], pool_fees, white_whale_std::pool_network::asset::PairType::ConstantProduct, Some("uluna-uusd".to_string()), @@ -517,10 +450,9 @@ mod router { }, ]; - // before swap uusd balance = 1_000_000_001 + // before swap uusd balance = 1_000_000_000 // - 2*1_000 pair creation fee // - 1_000_000 liquidity provision - // - 1 for native token creation (for decimal precisions) // = 998_998_000 let pre_swap_amount = 998_998_000; suite.query_balance(creator.to_string(), "uusd".to_string(), |amt| { @@ -622,12 +554,10 @@ mod router { // Create a pair suite .instantiate_default() - .add_native_token_decimals(creator.clone(), "uwhale".to_string(), 6) - .add_native_token_decimals(creator.clone(), "uluna".to_string(), 6) - .add_native_token_decimals(creator.clone(), "uusd".to_string(), 6) .create_pair( creator.clone(), first_pair, + vec![6u8, 6u8], pool_fees.clone(), white_whale_std::pool_network::asset::PairType::ConstantProduct, Some("whale-uluna".to_string()), @@ -639,6 +569,7 @@ mod router { .create_pair( creator.clone(), second_pair, + vec![6u8, 6u8], pool_fees, white_whale_std::pool_network::asset::PairType::ConstantProduct, Some("uluna-uusd".to_string()), @@ -760,12 +691,10 @@ mod router { // Create a pair suite .instantiate_default() - .add_native_token_decimals(creator.clone(), "uwhale".to_string(), 6) - .add_native_token_decimals(creator.clone(), "uluna".to_string(), 6) - .add_native_token_decimals(creator.clone(), "uusd".to_string(), 6) .create_pair( creator.clone(), first_pair, + vec![6u8, 6u8], pool_fees.clone(), white_whale_std::pool_network::asset::PairType::ConstantProduct, Some("whale-uluna".to_string()), @@ -777,6 +706,7 @@ mod router { .create_pair( creator.clone(), second_pair, + vec![6u8, 6u8], pool_fees, white_whale_std::pool_network::asset::PairType::ConstantProduct, Some("uluna-uusd".to_string()), @@ -867,9 +797,9 @@ mod router { #[test] fn sends_to_correct_receiver() { let mut suite = TestingSuite::default_with_balances(vec![ - coin(1_000_000_001u128, "uwhale".to_string()), + coin(1_000_000_000u128, "uwhale".to_string()), coin(1_000_000_000u128, "uluna".to_string()), - coin(1_000_000_001u128, "uusd".to_string()), + coin(1_000_000_000u128, "uusd".to_string()), ]); let creator = suite.creator(); let other = suite.senders[1].clone(); @@ -915,12 +845,10 @@ mod router { // Create a pair suite .instantiate_default() - .add_native_token_decimals(creator.clone(), "uwhale".to_string(), 6) - .add_native_token_decimals(creator.clone(), "uluna".to_string(), 6) - .add_native_token_decimals(creator.clone(), "uusd".to_string(), 6) .create_pair( creator.clone(), first_pair, + vec![6u8, 6u8], pool_fees.clone(), white_whale_std::pool_network::asset::PairType::ConstantProduct, Some("whale-uluna".to_string()), @@ -932,6 +860,7 @@ mod router { .create_pair( creator.clone(), second_pair, + vec![6u8, 6u8], pool_fees, white_whale_std::pool_network::asset::PairType::ConstantProduct, Some("uluna-uusd".to_string()), @@ -1000,10 +929,10 @@ mod router { }, ]; - // before swap uusd balance = 1_000_000_001 - // before swap uwhale balance = 1_000_000_001 - // before swap uluna balance = 1_000_000_001 - let pre_swap_amount = 1_000_000_001; + // before swap uusd balance = 1_000_000_000 + // before swap uwhale balance = 1_000_000_000 + // before swap uluna balance = 1_000_000_000 + let pre_swap_amount = 1_000_000_000; suite.query_balance(other.to_string(), "uusd".to_string(), |amt| { assert_eq!(amt.unwrap().amount.u128(), pre_swap_amount); }); @@ -1011,7 +940,7 @@ mod router { assert_eq!(amt.unwrap().amount.u128(), pre_swap_amount); }); suite.query_balance(other.to_string(), "uluna".to_string(), |amt| { - assert_eq!(amt.unwrap().amount.u128(), pre_swap_amount - 1); + assert_eq!(amt.unwrap().amount.u128(), pre_swap_amount); }); // also check the same for unauthorized receiver suite.query_balance(other.to_string(), "uusd".to_string(), |amt| { @@ -1021,30 +950,28 @@ mod router { assert_eq!(amt.unwrap().amount.u128(), pre_swap_amount); }); suite.query_balance(other.to_string(), "uluna".to_string(), |amt| { - assert_eq!(amt.unwrap().amount.u128(), pre_swap_amount - 1); + assert_eq!(amt.unwrap().amount.u128(), pre_swap_amount); }); // also check for contract - // when we add tokens to the contract, we must send a fee of 1_u128 so the contract - // can register the native token suite.query_balance( suite.pool_manager_addr.to_string(), "uusd".to_string(), |amt| { - assert_eq!(amt.unwrap().amount.u128(), liquidity_amount + 1); + assert_eq!(amt.unwrap().amount.u128(), liquidity_amount); }, ); suite.query_balance( suite.pool_manager_addr.to_string(), "uwhale".to_string(), |amt| { - assert_eq!(amt.unwrap().amount.u128(), liquidity_amount + 1); + assert_eq!(amt.unwrap().amount.u128(), liquidity_amount); }, ); suite.query_balance( suite.pool_manager_addr.to_string(), "uluna".to_string(), |amt| { - assert_eq!(amt.unwrap().amount.u128(), 2 * liquidity_amount + 1); + assert_eq!(amt.unwrap().amount.u128(), 2 * liquidity_amount); }, ); @@ -1072,21 +999,21 @@ mod router { suite.pool_manager_addr.to_string(), "uusd".to_string(), |amt| { - assert_eq!(amt.unwrap().amount.u128(), liquidity_amount - 998 + 1); + assert_eq!(amt.unwrap().amount.u128(), liquidity_amount - 998); }, ); suite.query_balance( suite.pool_manager_addr.to_string(), "uwhale".to_string(), |amt| { - assert_eq!(amt.unwrap().amount.u128(), liquidity_amount + 1000 + 1); + assert_eq!(amt.unwrap().amount.u128(), liquidity_amount + 1000); }, ); suite.query_balance( suite.pool_manager_addr.to_string(), "uluna".to_string(), |amt| { - assert_eq!(amt.unwrap().amount.u128(), 2 * liquidity_amount + 1); + assert_eq!(amt.unwrap().amount.u128(), 2 * liquidity_amount); }, ); } @@ -1094,9 +1021,9 @@ mod router { #[test] fn checks_minimum_receive() { let mut suite = TestingSuite::default_with_balances(vec![ - coin(1_000_000_001u128, "uwhale".to_string()), + coin(1_000_000_000u128, "uwhale".to_string()), coin(1_000_000_000u128, "uluna".to_string()), - coin(1_000_000_001u128, "uusd".to_string()), + coin(1_000_000_000u128, "uusd".to_string()), ]); let creator = suite.creator(); let _other = suite.senders[1].clone(); @@ -1139,12 +1066,10 @@ mod router { // Create a pair suite .instantiate_default() - .add_native_token_decimals(creator.clone(), "uwhale".to_string(), 6) - .add_native_token_decimals(creator.clone(), "uluna".to_string(), 6) - .add_native_token_decimals(creator.clone(), "uusd".to_string(), 6) .create_pair( creator.clone(), first_pair, + vec![6u8, 6u8], pool_fees.clone(), white_whale_std::pool_network::asset::PairType::ConstantProduct, Some("whale-uluna".to_string()), @@ -1156,6 +1081,7 @@ mod router { .create_pair( creator.clone(), second_pair, + vec![6u8, 6u8], pool_fees, white_whale_std::pool_network::asset::PairType::ConstantProduct, Some("uluna-uusd".to_string()), @@ -1223,10 +1149,9 @@ mod router { }, ]; - // before swap uusd balance = 1_000_000_001 + // before swap uusd balance = 1_000_000_000 // - 2*1_000 pair creation fee // - 1_000_000 liquidity provision - // - 1 for native token creation (for decimal precisions) // = 998_998_000 let pre_swap_amount = 998_998_000; suite.query_balance(creator.to_string(), "uusd".to_string(), |amt| { @@ -1309,21 +1234,23 @@ mod swapping { }; // Create a pair - suite - .instantiate_default() - .add_native_token_decimals(creator.clone(), "uwhale".to_string(), 6) - .add_native_token_decimals(creator.clone(), "uluna".to_string(), 6) - .create_pair( - creator.clone(), - asset_infos, - pool_fees, - white_whale_std::pool_network::asset::PairType::ConstantProduct, - Some("whale-uluna".to_string()), - vec![coin(1000, "uusd")], - |result| { - result.unwrap(); - }, - ); + suite.instantiate_default().create_pair( + creator.clone(), + asset_infos, + vec![6u8, 6u8], + pool_fees, + white_whale_std::pool_network::asset::PairType::ConstantProduct, + Some("whale-uluna".to_string()), + vec![coin(1000, "uusd")], + |result| { + result.unwrap(); + }, + ); + + // 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]); + }); // Lets try to add liquidity suite.provide_liquidity( @@ -1351,6 +1278,7 @@ mod swapping { })); }, ); + let simulated_return_amount = RefCell::new(Uint128::zero()); suite.query_simulation( "whale-uluna".to_string(), @@ -1523,21 +1451,18 @@ mod swapping { }; // Create a pair - suite - .instantiate_default() - .add_native_token_decimals(creator.clone(), "uwhale".to_string(), 6) - .add_native_token_decimals(creator.clone(), "uluna".to_string(), 6) - .create_pair( - creator.clone(), - asset_infos, - pool_fees, - white_whale_std::pool_network::asset::PairType::StableSwap { amp: 100 }, - Some("whale-uluna".to_string()), - vec![coin(1000, "uusd")], - |result| { - result.unwrap(); - }, - ); + suite.instantiate_default().create_pair( + creator.clone(), + asset_infos, + vec![6u8, 6u8], + pool_fees, + white_whale_std::pool_network::asset::PairType::StableSwap { amp: 100 }, + Some("whale-uluna".to_string()), + vec![coin(1000, "uusd")], + |result| { + result.unwrap(); + }, + ); // Lets try to add liquidity suite.provide_liquidity( @@ -1728,21 +1653,18 @@ mod swapping { }; // Create a pair - suite - .instantiate_default() - .add_native_token_decimals(creator.clone(), "uwhale".to_string(), 6) - .add_native_token_decimals(creator.clone(), "uluna".to_string(), 6) - .create_pair( - creator.clone(), - asset_infos, - pool_fees, - white_whale_std::pool_network::asset::PairType::ConstantProduct, - Some("whale-uluna".to_string()), - vec![coin(1000, "uusd")], - |result| { - result.unwrap(); - }, - ); + suite.instantiate_default().create_pair( + creator.clone(), + asset_infos, + vec![6u8, 6u8], + pool_fees, + white_whale_std::pool_network::asset::PairType::ConstantProduct, + Some("whale-uluna".to_string()), + vec![coin(1000, "uusd")], + |result| { + result.unwrap(); + }, + ); // Lets try to add liquidity, 1000 of each token. suite.provide_liquidity( @@ -1832,3 +1754,133 @@ mod swapping { ); } } + +mod ownership { + use white_whale_std::pool_manager::FeatureToggle; + + use super::*; + + #[test] + fn verify_ownership() { + let mut suite = TestingSuite::default_with_balances(vec![]); + let creator = suite.creator(); + let other = suite.senders[1].clone(); + let unauthorized = suite.senders[2].clone(); + + suite + .instantiate_default() + .query_ownership(|result| { + let ownership = result.unwrap(); + assert_eq!(Addr::unchecked(ownership.owner.unwrap()), creator); + }) + .update_ownership( + unauthorized, + cw_ownable::Action::TransferOwnership { + new_owner: other.to_string(), + expiry: None, + }, + |result| { + let err = result.unwrap_err().downcast::().unwrap(); + + match err { + ContractError::OwnershipError { .. } => {} + _ => { + panic!("Wrong error type, should return ContractError::OwnershipError") + } + } + }, + ) + .update_ownership( + creator, + cw_ownable::Action::TransferOwnership { + new_owner: other.to_string(), + expiry: None, + }, + |result| { + result.unwrap(); + }, + ) + .update_ownership( + other.clone(), + cw_ownable::Action::AcceptOwnership, + |result| { + result.unwrap(); + }, + ) + .query_ownership(|result| { + let ownership = result.unwrap(); + assert_eq!(Addr::unchecked(ownership.owner.unwrap()), other); + }) + .update_ownership( + other.clone(), + cw_ownable::Action::RenounceOwnership, + |result| { + result.unwrap(); + }, + ) + .query_ownership(|result| { + let ownership = result.unwrap(); + assert!(ownership.owner.is_none()); + }); + } + + #[test] + fn checks_ownership_when_updating_config() { + let mut suite = TestingSuite::default_with_balances(vec![]); + let unauthorized = suite.senders[2].clone(); + + suite.instantiate_default().update_config( + unauthorized.clone(), + None, + None, + None, + |result| { + let err = result.unwrap_err().downcast::().unwrap(); + + match err { + ContractError::OwnershipError { .. } => {} + _ => { + panic!("Wrong error type, should return ContractError::OwnershipError") + } + } + }, + ); + } + + #[test] + fn updates_config_fields() { + let mut suite = TestingSuite::default_with_balances(vec![]); + let creator = suite.creator(); + let other = suite.senders[1].clone(); + + suite.instantiate_default(); + let current_pool_creation_fee = suite.query_config().pool_creation_fee; + let initial_config = suite.query_config(); + + suite.update_config( + creator, + Some(other), + Some(coin( + current_pool_creation_fee + .amount + .checked_add(Uint128::from(1u32)) + .unwrap() + .u128(), + current_pool_creation_fee.denom, + )), + Some(FeatureToggle { + deposits_enabled: false, + swaps_enabled: false, + withdrawals_enabled: false, + }), + |res| { + res.unwrap(); + }, + ); + + let config = suite.query_config(); + assert_ne!(config.whale_lair_addr, initial_config.whale_lair_addr); + assert_ne!(config.pool_creation_fee, initial_config.pool_creation_fee); + assert_ne!(config.feature_toggle, initial_config.feature_toggle); + } +} diff --git a/contracts/liquidity_hub/pool-manager/src/tests/mock_querier.rs b/contracts/liquidity_hub/pool-manager/src/tests/mock_querier.rs index fe5eff24..2db5b940 100644 --- a/contracts/liquidity_hub/pool-manager/src/tests/mock_querier.rs +++ b/contracts/liquidity_hub/pool-manager/src/tests/mock_querier.rs @@ -137,20 +137,6 @@ impl WasmMockQuerier { }), } } - Ok(white_whale_std::pool_manager::QueryMsg::NativeTokenDecimals { denom }) => { - match self.pool_factory_querier.native_token_decimals.get(&denom) { - Some(decimals) => SystemResult::Ok(ContractResult::Ok( - to_binary(&NativeTokenDecimalsResponse { - decimals: *decimals, - }) - .unwrap(), - )), - None => SystemResult::Err(SystemError::InvalidRequest { - error: "No decimal info exist".to_string(), - request: msg.as_slice().into(), - }), - } - } _ => match from_binary(msg) { Ok(white_whale_std::pool_manager::QueryMsg::Simulation { offer_asset, @@ -176,73 +162,6 @@ impl WasmMockQuerier { burn_fee_amount: Uint128::zero(), }, ))), - _ => { - match from_binary(msg).unwrap() { - Cw20QueryMsg::TokenInfo {} => { - let balances: &HashMap = - match self.token_querier.balances.get(contract_addr) { - Some(balances) => balances, - None => { - return SystemResult::Err(SystemError::InvalidRequest { - error: format!( - "No balance info exists for the contract {contract_addr}" - ), - request: msg.as_slice().into(), - }) - } - }; - println!("balances: {:?}", self.token_querier.balances); - - let mut total_supply = Uint128::zero(); - - for balance in balances { - total_supply += *balance.1; - } - - SystemResult::Ok(ContractResult::Ok( - to_binary(&TokenInfoResponse { - name: "mAAPL".to_string(), - symbol: "mAAPL".to_string(), - decimals: 8, - total_supply, - }) - .unwrap(), - )) - } - Cw20QueryMsg::Balance { address } => { - let balances: &HashMap = - match self.token_querier.balances.get(contract_addr) { - Some(balances) => balances, - None => { - return SystemResult::Err(SystemError::InvalidRequest { - error: format!( - "No balance info exists for the contract {contract_addr}" - ), - request: msg.as_slice().into(), - }) - } - }; - - let balance = match balances.get(&address) { - Some(v) => *v, - None => { - return SystemResult::Ok(ContractResult::Ok( - to_binary(&Cw20BalanceResponse { - balance: Uint128::zero(), - }) - .unwrap(), - )); - } - }; - - SystemResult::Ok(ContractResult::Ok( - to_binary(&Cw20BalanceResponse { balance }).unwrap(), - )) - } - - _ => panic!("DO NOT ENTER HERE"), - } - } }, }, QueryRequest::Wasm(WasmQuery::ContractInfo { .. }) => { diff --git a/contracts/liquidity_hub/pool-manager/src/tests/suite.rs b/contracts/liquidity_hub/pool-manager/src/tests/suite.rs index d74de6e5..e0c22a32 100644 --- a/contracts/liquidity_hub/pool-manager/src/tests/suite.rs +++ b/contracts/liquidity_hub/pool-manager/src/tests/suite.rs @@ -1,8 +1,10 @@ use cosmwasm_std::testing::MockStorage; -use white_whale_std::pool_manager::SwapOperation; +use white_whale_std::pool_manager::{ + Config, FeatureToggle, PairInfoResponse, SwapOperation, SwapRouteResponse, SwapRoutesResponse, +}; use white_whale_std::pool_manager::{InstantiateMsg, PairInfo}; -use cosmwasm_std::{Addr, Coin, Decimal, Empty, StdResult, Timestamp, Uint128, Uint64}; +use cosmwasm_std::{coin, Addr, Coin, Decimal, Empty, StdResult, Timestamp, Uint128, Uint64}; use cw_multi_test::{ App, AppBuilder, AppResponse, BankKeeper, Contract, ContractWrapper, DistributionKeeper, Executor, FailingModule, GovFailingModule, IbcFailingModule, StakeKeeper, WasmKeeper, @@ -120,11 +122,7 @@ impl TestingSuite { pub(crate) fn instantiate(&mut self, whale_lair_addr: String) -> &mut Self { let msg = InstantiateMsg { fee_collector_addr: whale_lair_addr, - owner: self.creator().to_string(), - pool_creation_fee: Coin { - amount: Uint128::from(1_000u128), - denom: "uusd".to_string(), - }, + pool_creation_fee: coin(1_000, "uusd"), }; let pool_manager_id = self.app.store_code(contract_pool_manager()); @@ -188,35 +186,6 @@ impl TestingSuite { ) .unwrap(); } - - #[track_caller] - pub fn add_native_token_decimals( - &mut self, - sender: Addr, - native_token_denom: String, - decimals: u8, - ) -> &mut Self { - let msg = white_whale_std::pool_manager::ExecuteMsg::AddNativeTokenDecimals { - denom: native_token_denom.clone(), - decimals, - }; - - let _creator = self.creator().clone(); - - self.app - .execute_contract( - sender, - self.pool_manager_addr.clone(), - &msg, - &[Coin { - denom: native_token_denom.to_string(), - amount: Uint128::from(1u128), - }], - ) - .unwrap(); - - self - } } /// execute messages @@ -261,6 +230,7 @@ impl TestingSuite { } #[track_caller] + #[allow(clippy::too_many_arguments)] pub(crate) fn swap( &mut self, sender: Addr, @@ -291,6 +261,7 @@ impl TestingSuite { } #[track_caller] + #[allow(clippy::too_many_arguments)] pub(crate) fn execute_swap_operations( &mut self, sender: Addr, @@ -317,10 +288,12 @@ impl TestingSuite { } #[track_caller] + #[allow(clippy::too_many_arguments)] pub(crate) fn create_pair( &mut self, sender: Addr, asset_denoms: Vec, + asset_decimals: Vec, pool_fees: PoolFee, pair_type: PairType, pair_identifier: Option, @@ -329,6 +302,7 @@ impl TestingSuite { ) -> &mut Self { let msg = white_whale_std::pool_manager::ExecuteMsg::CreatePair { asset_denoms, + asset_decimals, pool_fees, pair_type, pair_identifier, @@ -361,6 +335,33 @@ impl TestingSuite { self } + + /// Updates the configuration of the contract. + /// + /// Any parameters which are set to `None` when passed will not update + /// the current configuration. + #[track_caller] + pub(crate) fn update_config( + &mut self, + sender: Addr, + new_whale_lair_addr: Option, + new_pool_creation_fee: Option, + new_feature_toggle: Option, + result: impl Fn(Result), + ) -> &mut Self { + result(self.app.execute_contract( + sender, + self.pool_manager_addr.clone(), + &white_whale_std::pool_manager::ExecuteMsg::UpdateConfig { + whale_lair_addr: new_whale_lair_addr.map(|addr| addr.to_string()), + pool_creation_fee: new_pool_creation_fee, + feature_toggle: new_feature_toggle, + }, + &[], + )); + + self + } } /// queries @@ -405,12 +406,12 @@ impl TestingSuite { self } - pub(crate) fn _query_pair_info( + pub(crate) fn query_pair_info( &self, pair_identifier: String, - result: impl Fn(StdResult), + result: impl Fn(StdResult), ) -> &Self { - let pair_info_response: StdResult = self.app.wrap().query_wasm_smart( + let pair_info_response: StdResult = self.app.wrap().query_wasm_smart( &self.pool_manager_addr, &white_whale_std::pool_manager::QueryMsg::Pair { pair_identifier }, ); @@ -476,7 +477,7 @@ impl TestingSuite { result: impl Fn(StdResult), ) -> &mut Self { // Get the LP token from Config - let lp_token_response: PairInfo = self + let lp_token_response: PairInfoResponse = self .app .wrap() .query_wasm_smart( @@ -492,7 +493,7 @@ impl TestingSuite { let balance: Uint128 = self .app .wrap() - .query_balance(sender, lp_token_response.lp_denom) + .query_balance(sender, lp_token_response.pair_info.lp_denom) .unwrap() .amount; @@ -516,4 +517,50 @@ impl TestingSuite { // Get balance of LP token, if native we can just query balance otherwise we need to go to cw20 lp_token_response.lp_denom } + + /// Retrieves the current configuration of the pool manager contract. + pub(crate) fn query_config(&mut self) -> Config { + self.app + .wrap() + .query_wasm_smart( + &self.pool_manager_addr, + &white_whale_std::pool_manager::QueryMsg::Config {}, + ) + .unwrap() + } + + /// Retrieves a swap route for a given pair of assets. + pub(crate) fn _query_swap_route( + &mut self, + offer_asset_denom: String, + ask_asset_denom: String, + result: impl Fn(StdResult), + ) -> &mut Self { + let swap_route_response: StdResult = self.app.wrap().query_wasm_smart( + &self.pool_manager_addr, + &white_whale_std::pool_manager::QueryMsg::SwapRoute { + offer_asset_denom, + ask_asset_denom, + }, + ); + + result(swap_route_response); + + self + } + + /// Retrieves the swap routes for a given pair 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 {}, + ); + + result(swap_routes_response); + + self + } } diff --git a/contracts/liquidity_hub/pool-network/incentive/src/tests/integration.rs b/contracts/liquidity_hub/pool-network/incentive/src/tests/integration.rs index 9bc87c2b..23d12a1b 100644 --- a/contracts/liquidity_hub/pool-network/incentive/src/tests/integration.rs +++ b/contracts/liquidity_hub/pool-network/incentive/src/tests/integration.rs @@ -288,7 +288,7 @@ fn try_open_more_flows_than_allowed() { amount: Uint128::new(i * 2_000u128), }, None, - &vec![coin(i * 2_000u128, "uwhale".to_string())], + &[coin(i * 2_000u128, "uwhale".to_string())], |result| { if i > 7 { // this should fail as only 7 incentives can be opened as specified in `instantiate_default` @@ -410,7 +410,7 @@ fn try_open_flows_with_wrong_epochs() { amount: Uint128::new(2_000u128), }, None, - &vec![coin(2_000u128, "uwhale".to_string())], + &[coin(2_000u128, "uwhale".to_string())], |result| { let err = result.unwrap_err().downcast::().unwrap(); @@ -435,7 +435,7 @@ fn try_open_flows_with_wrong_epochs() { amount: Uint128::new(2_000u128), }, None, - &vec![coin(2_000u128, "uwhale".to_string())], + &[coin(2_000u128, "uwhale".to_string())], |result| { let err = result.unwrap_err().downcast::().unwrap(); @@ -462,7 +462,7 @@ fn try_open_flows_with_wrong_epochs() { amount: Uint128::new(2_000u128), }, None, - &vec![coin(2_000u128, "uwhale".to_string())], + &[coin(2_000u128, "uwhale".to_string())], |result| { let err = result.unwrap_err().downcast::().unwrap(); @@ -485,7 +485,7 @@ fn try_open_flows_with_wrong_epochs() { amount: Uint128::new(2_000u128), }, None, - &vec![coin(2_000u128, "uwhale".to_string())], + &[coin(2_000u128, "uwhale".to_string())], |result| { result.unwrap(); }, @@ -541,7 +541,7 @@ fn open_flow_with_fee_native_token_and_flow_same_native_token() { amount: Uint128::new(0u128), }, None, - &vec![coin(1_000u128, "uwhale".to_string())], + &[coin(1_000u128, "uwhale".to_string())], |result| { // this should fail as not enough funds were sent let err = result.unwrap_err().downcast::().unwrap(); @@ -565,7 +565,7 @@ fn open_flow_with_fee_native_token_and_flow_same_native_token() { amount: Uint128::new(1_000u128), }, None, - &vec![coin(1_000u128, "uwhale".to_string())], + &[coin(1_000u128, "uwhale".to_string())], |result| { // this should fail as not enough funds were sent to cover for fee + MIN_FLOW_AMOUNT let err = result.unwrap_err().downcast::().unwrap(); @@ -588,7 +588,7 @@ fn open_flow_with_fee_native_token_and_flow_same_native_token() { amount: Uint128::new(1_000u128), }, None, - &vec![coin(100u128, "uwhale".to_string())], + &[coin(100u128, "uwhale".to_string())], |result| { // this should fail as not enough funds were sent to cover for fee + MIN_FLOW_AMOUNT let err = result.unwrap_err().downcast::().unwrap(); @@ -611,7 +611,7 @@ fn open_flow_with_fee_native_token_and_flow_same_native_token() { amount: Uint128::new(2_000u128), }, None, - &vec![coin(500u128, "uwhale".to_string())], + &[coin(500u128, "uwhale".to_string())], |result| { // this should fail as we didn't send enough funds to cover for the fee let err = result.unwrap_err().downcast::().unwrap(); @@ -634,7 +634,7 @@ fn open_flow_with_fee_native_token_and_flow_same_native_token() { amount: Uint128::new(2_000u128), }, None, - &vec![coin(2_000u128, "uwhale".to_string())], + &[coin(2_000u128, "uwhale".to_string())], |result| { // this should succeed as we sent enough funds to cover for fee + MIN_FLOW_AMOUNT result.unwrap(); @@ -752,7 +752,7 @@ fn open_flow_with_fee_native_token_and_flow_different_native_token() { amount: Uint128::new(500u128), }, None, - &vec![coin(1_000u128, "uwhale".to_string())], + &[coin(1_000u128, "uwhale".to_string())], |result| { // this should fail as MIN_FLOW_AMOUNT is not met let err = result.unwrap_err().downcast::().unwrap(); @@ -776,7 +776,7 @@ fn open_flow_with_fee_native_token_and_flow_different_native_token() { amount: Uint128::new(1_000u128), }, None, - &vec![coin(1_000u128, "uwhale".to_string())], + &[coin(1_000u128, "uwhale".to_string())], |result| { // this should fail as the flow asset was not sent let err = result.unwrap_err().downcast::().unwrap(); @@ -799,7 +799,7 @@ fn open_flow_with_fee_native_token_and_flow_different_native_token() { amount: Uint128::new(1_000u128), }, None, - &vec![ + &[ coin(1_000u128, "uwhale".to_string()), coin(500u128, "ampWHALE".to_string()), ], @@ -825,7 +825,7 @@ fn open_flow_with_fee_native_token_and_flow_different_native_token() { amount: Uint128::new(1_000u128), }, None, - &vec![ + &[ coin(100u128, "uwhale".to_string()), coin(1_00u128, "ampWHALE".to_string()), ], @@ -851,7 +851,7 @@ fn open_flow_with_fee_native_token_and_flow_different_native_token() { amount: Uint128::new(1_000u128), }, None, - &vec![ + &[ coin(1_000u128, "uwhale".to_string()), coin(1_000u128, "ampWHALE".to_string()), ], @@ -963,7 +963,7 @@ fn open_flow_with_fee_native_token_and_flow_different_native_token() { amount: Uint128::new(1_000u128), }, None, - &vec![ + &[ coin(50_000u128, "uwhale".to_string()), coin(1_000u128, "ampWHALE".to_string()), ], @@ -1040,7 +1040,7 @@ fn open_flow_with_fee_native_token_and_flow_cw20_token() { amount: Uint128::new(500u128), }, None, - &vec![coin(1_000u128, "uwhale".to_string())], + &[coin(1_000u128, "uwhale".to_string())], |result| { // this should fail as MIN_FLOW_AMOUNT is not met let err = result.unwrap_err().downcast::().unwrap(); @@ -1062,7 +1062,7 @@ fn open_flow_with_fee_native_token_and_flow_cw20_token() { amount: Uint128::new(1_000u128), }, None, - &vec![coin(1_000u128, "uwhale".to_string())], + &[coin(1_000u128, "uwhale".to_string())], |result| { // this should fail as the flow asset was not sent, i.e. Allowance was not increased let err = result.unwrap_err().downcast::().unwrap(); @@ -1090,7 +1090,7 @@ fn open_flow_with_fee_native_token_and_flow_cw20_token() { amount: Uint128::new(1_000u128), }, None, - &vec![coin(1_000u128, "uwhale".to_string())], + &[coin(1_000u128, "uwhale".to_string())], |result| { // this should succeed as the allowance was increased result.unwrap(); @@ -1215,7 +1215,7 @@ fn open_flow_with_fee_cw20_token_and_flow_same_cw20_token() { amount: Uint128::new(500u128), }, None, - &vec![], + &[], |result| { // this should fail as not enough funds were sent let err = result.unwrap_err().downcast::().unwrap(); @@ -1237,7 +1237,7 @@ fn open_flow_with_fee_cw20_token_and_flow_same_cw20_token() { amount: Uint128::new(1_000u128), }, None, - &vec![], + &[], |result| { // this should fail as not enough funds were sent to cover for fee let err = result.unwrap_err().downcast::().unwrap(); @@ -1266,7 +1266,7 @@ fn open_flow_with_fee_cw20_token_and_flow_same_cw20_token() { amount: Uint128::new(1_500u128), }, None, - &vec![], + &[], |result| { // this should fail as not enough funds were sent to cover for fee and MIN_FLOW_AMOUNT let err = result.unwrap_err().downcast::().unwrap(); @@ -1294,7 +1294,7 @@ fn open_flow_with_fee_cw20_token_and_flow_same_cw20_token() { amount: Uint128::new(2_000u128), }, None, - &vec![], + &[], |result| { // this should succeed as enough funds were sent to cover for fee and MIN_FLOW_AMOUNT result.unwrap(); @@ -1401,7 +1401,7 @@ fn open_flow_with_fee_cw20_token_and_flow_different_cw20_token() { amount: Uint128::new(500u128), }, None, - &vec![], + &[], |result| { // this should fail as not enough funds were sent let err = result.unwrap_err().downcast::().unwrap(); @@ -1423,7 +1423,7 @@ fn open_flow_with_fee_cw20_token_and_flow_different_cw20_token() { amount: Uint128::new(1_000u128), }, None, - &vec![], + &[], |result| { // this should fail as the asset to pay for the fee was not transferred let err = result.unwrap_err().downcast::().unwrap(); @@ -1452,7 +1452,7 @@ fn open_flow_with_fee_cw20_token_and_flow_different_cw20_token() { amount: Uint128::new(1_000u128), }, None, - &vec![], + &[], |result| { // this should fail as not enough funds were sent to cover for fee let err = result.unwrap_err().downcast::().unwrap(); @@ -1481,7 +1481,7 @@ fn open_flow_with_fee_cw20_token_and_flow_different_cw20_token() { amount: Uint128::new(1_000u128), }, None, - &vec![], + &[], |result| { // this should fail as not enough funds were sent to cover the flow asset let err = result.unwrap_err().downcast::().unwrap(); @@ -1510,7 +1510,7 @@ fn open_flow_with_fee_cw20_token_and_flow_different_cw20_token() { amount: Uint128::new(1_000u128), }, None, - &vec![], + &[], |result| { // this should succeed as both the fee was paid in full and the flow asset amount // matches the one sent to the contract @@ -1638,7 +1638,7 @@ fn open_flow_with_fee_cw20_token_and_flow_native_token() { amount: Uint128::new(500u128), }, None, - &vec![], + &[], |result| { // this should fail as not enough funds were sent let err = result.unwrap_err().downcast::().unwrap(); @@ -1662,7 +1662,7 @@ fn open_flow_with_fee_cw20_token_and_flow_native_token() { amount: Uint128::new(1_000u128), }, None, - &vec![], + &[], |result| { // this should fail as the asset to pay for the fee was not transferred let err = result.unwrap_err().downcast::().unwrap(); @@ -1693,7 +1693,7 @@ fn open_flow_with_fee_cw20_token_and_flow_native_token() { amount: Uint128::new(1_000u128), }, None, - &vec![], + &[], |result| { // this should fail as not enough funds were sent to cover for fee let err = result.unwrap_err().downcast::().unwrap(); @@ -1724,7 +1724,7 @@ fn open_flow_with_fee_cw20_token_and_flow_native_token() { amount: Uint128::new(1_000u128), }, None, - &vec![], + &[], |result| { // this should fail as the flow asset was not sent to the contract let err = result.unwrap_err().downcast::().unwrap(); @@ -1748,7 +1748,7 @@ fn open_flow_with_fee_cw20_token_and_flow_native_token() { amount: Uint128::new(1_000u128), }, None, - &vec![coin(900u128, "usdc".to_string())], + &[coin(900u128, "usdc".to_string())], |result| { // this should fail as the flow asset was not sent to the contract let err = result.unwrap_err().downcast::().unwrap(); @@ -1772,7 +1772,7 @@ fn open_flow_with_fee_cw20_token_and_flow_native_token() { amount: Uint128::new(1_000u128), }, None, - &vec![coin(1_000u128, "usdc".to_string())], + &[coin(1_000u128, "usdc".to_string())], |result| { // this should succeed as the flow asset was sent to the contract result.unwrap(); @@ -1905,7 +1905,7 @@ fn close_native_token_flows() { amount: Uint128::new(2_000u128), }, None, - &vec![coin(2_000u128, "uwhale".to_string())], + &[coin(2_000u128, "uwhale".to_string())], |result| { result.unwrap(); }, @@ -1923,7 +1923,7 @@ fn close_native_token_flows() { amount: Uint128::new(11_000u128), }, None, - &vec![coin(11_000u128, "uwhale".to_string())], + &[coin(11_000u128, "uwhale".to_string())], |result| { result.unwrap(); }, @@ -2127,7 +2127,7 @@ fn close_native_token_flows() { amount: Uint128::new(5_000u128), }, None, - &vec![coin(5_000u128, "uwhale".to_string())], + &[coin(5_000u128, "uwhale".to_string())], |result| { result.unwrap(); }, @@ -2217,7 +2217,7 @@ fn close_cw20_token_flows() { amount: Uint128::new(1_000u128), }, None, - &vec![coin(1_000u128, "uwhale".to_string())], + &[coin(1_000u128, "uwhale".to_string())], |result| { result.unwrap(); }, @@ -2239,7 +2239,7 @@ fn close_cw20_token_flows() { amount: Uint128::new(10_000u128), }, None, - &vec![coin(1_000u128, "uwhale".to_string())], + &[coin(1_000u128, "uwhale".to_string())], |result| { result.unwrap(); }, @@ -2417,7 +2417,7 @@ fn close_cw20_token_flows() { amount: Uint128::new(5_000u128), }, None, - &vec![coin(1_000u128, "uwhale".to_string())], + &[coin(1_000u128, "uwhale".to_string())], |result| { result.unwrap(); }, @@ -2694,7 +2694,7 @@ fn open_flow_positions_and_claim_native_token_incentive() { amount: Uint128::new(1_000_000_000u128), }, None, - &vec![coin(1_000_000_000u128, "usdc"), coin(1_000u128, "uwhale")], + &[coin(1_000_000_000u128, "usdc"), coin(1_000u128, "uwhale")], |result| { result.unwrap(); }, @@ -3103,7 +3103,7 @@ fn open_flow_positions_claim_cw20_token_incentive() { amount: Uint128::new(1_000_000_000u128), }, None, - &vec![coin(1_000u128, "uwhale")], + &[coin(1_000u128, "uwhale")], |result| { result.unwrap(); }, @@ -3325,7 +3325,7 @@ fn open_expand_close_flows_positions_and_claim_native_token_incentive() { amount: Uint128::new(1_000_000_000u128), }, None, - &vec![ + &[ coin(1_000_000_000u128, "ampWHALE"), coin(1_000u128, "uwhale"), ], @@ -3344,7 +3344,7 @@ fn open_expand_close_flows_positions_and_claim_native_token_incentive() { amount: Uint128::new(10_000_000_000u128), }, None, - &vec![coin(10_000_000_000u128, "usdc"), coin(1_000u128, "uwhale")], + &[coin(10_000_000_000u128, "usdc"), coin(1_000u128, "uwhale")], |result| { result.unwrap(); }, @@ -4391,7 +4391,7 @@ fn open_expand_position_with_optional_receiver() { amount: Uint128::new(1_000_000_000u128), }, None, - &vec![ + &[ coin(1_000_000_000u128, "ampWHALE"), coin(1_000u128, "uwhale"), ], @@ -4534,7 +4534,7 @@ fn close_position_if_empty_rewards() { amount: Uint128::new(1_000u128), }, None, - &vec![coin(1_000u128, "ampWHALE"), coin(1_000u128, "uwhale")], + &[coin(1_000u128, "ampWHALE"), coin(1_000u128, "uwhale")], |result| { result.unwrap(); }, @@ -4884,7 +4884,7 @@ fn open_expand_flow_with_native_token() { amount: Uint128::new(2_000u128), }, None, - &vec![coin(2_000u128, "uwhale".to_string())], + &[coin(2_000u128, "uwhale".to_string())], |result| { // this should succeed as we sent enough funds to cover for fee + MIN_FLOW_AMOUNT result.unwrap(); @@ -5187,7 +5187,7 @@ fn open_expand_flow_with_cw20_token() { amount: Uint128::new(2_000u128), }, None, - &vec![coin(1_000u128, "uwhale".to_string())], + &[coin(1_000u128, "uwhale".to_string())], |result| { // this should succeed as we sent enough funds to cover for fee + MIN_FLOW_AMOUNT result.unwrap(); @@ -5455,7 +5455,7 @@ fn fail_expand_ended_flow() { amount: Uint128::new(2_000u128), }, None, - &vec![coin(1_000u128, "uwhale".to_string())], + &[coin(1_000u128, "uwhale".to_string())], |result| { // this should succeed as we sent enough funds to cover for fee + MIN_FLOW_AMOUNT result.unwrap(); @@ -5598,7 +5598,7 @@ fn open_expand_flow_with_default_values() { amount: Uint128::new(1_000_000_000u128), }, Some("alias".to_string()), - &vec![coin(1_000_000_000u128, "usdc"), coin(1_000u128, "uwhale")], + &[coin(1_000_000_000u128, "usdc"), coin(1_000u128, "uwhale")], |result| { result.unwrap(); }, @@ -5961,7 +5961,7 @@ fn open_expand_flow_verify_rewards() { amount: Uint128::new(10_000u128), }, Some("alias".to_string()), - &vec![coin(10_000u128, "usdc"), coin(1_000u128, "uwhale")], + &[coin(10_000u128, "usdc"), coin(1_000u128, "uwhale")], |result| { result.unwrap(); }, @@ -6466,7 +6466,7 @@ fn open_expand_flow_over_expand_limit() { amount: Uint128::new(10_000u128), }, Some("alias".to_string()), - &vec![coin(10_000u128, "usdc"), coin(1_000u128, "uwhale")], + &[coin(10_000u128, "usdc"), coin(1_000u128, "uwhale")], |result| { result.unwrap(); }, diff --git a/contracts/liquidity_hub/pool-network/incentive/src/tests/suite.rs b/contracts/liquidity_hub/pool-network/incentive/src/tests/suite.rs index 4cf0e681..25fcc47d 100644 --- a/contracts/liquidity_hub/pool-network/incentive/src/tests/suite.rs +++ b/contracts/liquidity_hub/pool-network/incentive/src/tests/suite.rs @@ -229,6 +229,7 @@ impl TestingSuite { } #[track_caller] + #[allow(clippy::too_many_arguments)] pub(crate) fn instantiate( &mut self, fee_collector_addr: String, @@ -261,6 +262,7 @@ impl TestingSuite { } #[track_caller] + #[allow(clippy::too_many_arguments)] pub(crate) fn instantiate_err( &mut self, fee_collector_addr: String, @@ -355,6 +357,7 @@ impl TestingSuite { self } + #[allow(clippy::too_many_arguments)] pub(crate) fn open_incentive_flow( &mut self, sender: Addr, @@ -364,7 +367,7 @@ impl TestingSuite { curve: Option, flow_asset: Asset, flow_label: Option, - funds: &Vec, + funds: &[Coin], result: impl Fn(Result), ) -> &mut Self { let msg = white_whale_std::pool_network::incentive::ExecuteMsg::OpenFlow { @@ -398,6 +401,7 @@ impl TestingSuite { self } + #[allow(clippy::too_many_arguments)] pub(crate) fn open_incentive_position( &mut self, sender: Addr, @@ -438,6 +442,7 @@ impl TestingSuite { self } + #[allow(clippy::too_many_arguments)] pub(crate) fn expand_incentive_position( &mut self, sender: Addr, @@ -553,6 +558,7 @@ impl TestingSuite { self } + #[allow(clippy::too_many_arguments)] pub(crate) fn expand_flow( &mut self, sender: Addr, diff --git a/contracts/liquidity_hub/pool-network/stableswap_3pool/src/stableswap_math/curve.rs b/contracts/liquidity_hub/pool-network/stableswap_3pool/src/stableswap_math/curve.rs index 281f8f2c..3aec253e 100644 --- a/contracts/liquidity_hub/pool-network/stableswap_3pool/src/stableswap_math/curve.rs +++ b/contracts/liquidity_hub/pool-network/stableswap_3pool/src/stableswap_math/curve.rs @@ -773,14 +773,6 @@ mod tests { swap_token_c_amount in 0..MAX_TOKENS_IN.u128(), pool_token_supply in 0..MAX_TOKENS_IN.u128(), ) { - let deposit_amount_a = deposit_amount_a; - let deposit_amount_b = deposit_amount_b; - let deposit_amount_c = deposit_amount_c; - let swap_token_a_amount = swap_token_a_amount; - let swap_token_b_amount = swap_token_b_amount; - let swap_token_c_amount = swap_token_c_amount; - let pool_token_supply = pool_token_supply; - let start_ramp_ts = cmp::max(0, current_ts - MIN_RAMP_DURATION); let stop_ramp_ts = cmp::min(u64::MAX, current_ts + MIN_RAMP_DURATION); let invariant = StableSwap::new(amp_factor, amp_factor, current_ts, start_ramp_ts, stop_ramp_ts); diff --git a/contracts/liquidity_hub/pool-network/stableswap_3pool/src/stableswap_math/mod.rs b/contracts/liquidity_hub/pool-network/stableswap_3pool/src/stableswap_math/mod.rs index bc714fff..115bc095 100644 --- a/contracts/liquidity_hub/pool-network/stableswap_3pool/src/stableswap_math/mod.rs +++ b/contracts/liquidity_hub/pool-network/stableswap_3pool/src/stableswap_math/mod.rs @@ -1,6 +1,5 @@ //! Math utilities for stable-swap. #![deny(rustdoc::all)] -#![allow(rustdoc::missing_doc_code_examples)] #![deny(missing_docs)] #![deny(clippy::unwrap_used)] #![deny(clippy::arithmetic_side_effects)] diff --git a/contracts/liquidity_hub/pool-network/terraswap_pair/src/tests/swap.rs b/contracts/liquidity_hub/pool-network/terraswap_pair/src/tests/swap.rs index 2122c3b7..d0a4fc53 100644 --- a/contracts/liquidity_hub/pool-network/terraswap_pair/src/tests/swap.rs +++ b/contracts/liquidity_hub/pool-network/terraswap_pair/src/tests/swap.rs @@ -1,4 +1,3 @@ -use anybuf::Anybuf; use cosmwasm_std::testing::{mock_env, mock_info, MOCK_CONTRACT_ADDR}; use cosmwasm_std::{ attr, coins, from_json, to_json_binary, BankMsg, Coin, CosmosMsg, Decimal, Reply, ReplyOn, diff --git a/contracts/liquidity_hub/pool-network/terraswap_router/src/testing/tests.rs b/contracts/liquidity_hub/pool-network/terraswap_router/src/testing/tests.rs index 81f2bb9d..638076c8 100644 --- a/contracts/liquidity_hub/pool-network/terraswap_router/src/testing/tests.rs +++ b/contracts/liquidity_hub/pool-network/terraswap_router/src/testing/tests.rs @@ -1685,7 +1685,7 @@ fn remove_swap_route() { ask_asset_info: swap_route_1.ask_asset_info.clone(), }; let res: Vec = - from_json(&query(deps.as_ref(), mock_env(), msg).unwrap()).unwrap(); + from_json(query(deps.as_ref(), mock_env(), msg).unwrap()).unwrap(); assert_eq!(res, swap_route_1.swap_operations); // remove swap route @@ -1716,7 +1716,7 @@ fn remove_swap_route() { ask_asset_info: swap_route_2.ask_asset_info.clone(), }; let res: Vec = - from_json(&query(deps.as_ref(), mock_env(), msg).unwrap()).unwrap(); + from_json(query(deps.as_ref(), mock_env(), msg).unwrap()).unwrap(); assert_eq!(res, swap_route_2.swap_operations); // remove swap route 1 again should fail @@ -1889,7 +1889,7 @@ fn remove_swap_routes() { ask_asset_info: swap_route_1.ask_asset_info.clone(), }; let res: Vec = - from_json(&query(deps.as_ref(), mock_env(), msg).unwrap()).unwrap(); + from_json(query(deps.as_ref(), mock_env(), msg).unwrap()).unwrap(); assert_eq!(res, swap_route_1.swap_operations); // remove swap routes diff --git a/contracts/liquidity_hub/vault-manager/tests/common/suite.rs b/contracts/liquidity_hub/vault-manager/tests/common/suite.rs index c221cee6..f0219c00 100644 --- a/contracts/liquidity_hub/vault-manager/tests/common/suite.rs +++ b/contracts/liquidity_hub/vault-manager/tests/common/suite.rs @@ -360,6 +360,7 @@ impl TestingSuite { self } #[track_caller] + #[allow(clippy::too_many_arguments)] pub(crate) fn update_config( &mut self, sender: Addr, diff --git a/contracts/liquidity_hub/vault-manager/tests/integration.rs b/contracts/liquidity_hub/vault-manager/tests/integration.rs index b711cd90..ac505626 100644 --- a/contracts/liquidity_hub/vault-manager/tests/integration.rs +++ b/contracts/liquidity_hub/vault-manager/tests/integration.rs @@ -326,7 +326,7 @@ fn deposit_withdraw() { }, ).query_vault(FilterVaultBy::Identifier("0".to_string()), |result| { let vault_response = result.unwrap(); - let vault = vault_response.vaults.get(0).unwrap(); + let vault = vault_response.vaults.first().unwrap(); *vault_lp_denom.borrow_mut() = vault.lp_denom.clone(); assert_eq!( @@ -343,7 +343,7 @@ fn deposit_withdraw() { limit: None, }), |result| { let vault_response = result.unwrap(); - let vault = vault_response.vaults.get(0).unwrap(); + let vault = vault_response.vaults.first().unwrap(); *vault_lp_denom.borrow_mut() = vault.lp_denom.clone(); assert_eq!( @@ -357,7 +357,7 @@ fn deposit_withdraw() { }) .query_vault(FilterVaultBy::LpAsset(vault_lp_denom.clone().into_inner()), |result| { let vault_response = result.unwrap(); - let vault = vault_response.vaults.get(0).unwrap(); + let vault = vault_response.vaults.first().unwrap(); *vault_lp_denom.borrow_mut() = vault.lp_denom.clone(); assert_eq!( @@ -708,7 +708,7 @@ pub fn successful_flashloan() { }, ], balanced_pool.clone(), - &vec![ + &[ coin(1_000_000u128, "uluna".to_string()), coin(1_000_000u128, "uwhale".to_string()), ], @@ -733,7 +733,7 @@ pub fn successful_flashloan() { }, ], skewed_pool.clone(), - &vec![ + &[ coin(2_000_000u128, "uluna".to_string()), coin(1_000_000u128, "uwhale".to_string()), ], @@ -882,7 +882,6 @@ pub fn unsuccessful_flashloan() { suite.instantiate_default().create_cw20_token(); - let cw20_token = suite.cw20_tokens.first().unwrap().clone(); let vault_manager = suite.vault_manager_addr.clone(); // create some vaults @@ -1035,7 +1034,7 @@ pub fn unsuccessful_flashloan() { }, ], balanced_pool.clone(), - &vec![ + &[ coin(1_000_000u128, "uluna".to_string()), coin(1_000_000u128, "uwhale".to_string()), ], @@ -1060,7 +1059,7 @@ pub fn unsuccessful_flashloan() { }, ], skewed_pool.clone(), - &vec![ + &[ coin(2_000_000u128, "uluna".to_string()), coin(1_000_000u128, "uwhale".to_string()), ], diff --git a/packages/white-whale-std/Cargo.toml b/packages/white-whale-std/Cargo.toml index 7ad29f12..bbbcf61a 100644 --- a/packages/white-whale-std/Cargo.toml +++ b/packages/white-whale-std/Cargo.toml @@ -46,4 +46,4 @@ cw20-base.workspace = true [dev-dependencies] cw-multi-test.workspace = true -test-case.workspace = true \ No newline at end of file +test-case.workspace = true diff --git a/packages/white-whale-std/src/pool_manager.rs b/packages/white-whale-std/src/pool_manager.rs index 6c12a4e7..9ddd957c 100644 --- a/packages/white-whale-std/src/pool_manager.rs +++ b/packages/white-whale-std/src/pool_manager.rs @@ -1,15 +1,8 @@ use std::fmt; -use crate::{ - fee::PoolFee, - pool_network::{ - asset::PairType, - factory::NativeTokenDecimalsResponse, - pair::{ReverseSimulationResponse, SimulationResponse}, - }, -}; +use crate::{fee::PoolFee, pool_network::asset::PairType}; use cosmwasm_schema::{cw_serde, QueryResponses}; -use cosmwasm_std::{Coin, Decimal, Uint128}; +use cosmwasm_std::{Addr, Coin, Decimal, Uint128}; use cw_ownable::{cw_ownable_execute, cw_ownable_query}; #[cw_serde] @@ -64,9 +57,7 @@ pub struct SwapRoute { // Used for all swap routes #[cw_serde] pub struct SwapRouteResponse { - pub offer_asset_denom: String, - pub ask_asset_denom: String, - pub swap_route: Vec, + pub swap_route: SwapRoute, } impl fmt::Display for SwapRoute { @@ -112,10 +103,18 @@ pub struct PairInfo { } impl PairInfo {} +#[cw_serde] +pub struct Config { + pub whale_lair_addr: Addr, + // We must set a creation fee on instantiation to prevent spamming of pools + pub pool_creation_fee: Coin, + // Whether or not swaps, deposits, and withdrawals are enabled + pub feature_toggle: FeatureToggle, +} + #[cw_serde] pub struct InstantiateMsg { pub fee_collector_addr: String, - pub owner: String, pub pool_creation_fee: Coin, } @@ -128,6 +127,7 @@ pub struct MigrateMsg {} pub enum ExecuteMsg { CreatePair { asset_denoms: Vec, + asset_decimals: Vec, pool_fees: PoolFee, pair_type: PairType, pair_identifier: Option, @@ -147,16 +147,8 @@ pub enum ExecuteMsg { to: Option, pair_identifier: String, }, - // /// Withdraws liquidity from the pool. - WithdrawLiquidity { - pair_identifier: String, - }, - /// Adds native token info to the contract so it can instantiate pair contracts that include it - AddNativeTokenDecimals { - denom: String, - decimals: u8, - }, - + /// Withdraws liquidity from the pool. + WithdrawLiquidity { pair_identifier: String }, /// Execute multiple [`SwapOperations`] to allow for multi-hop swaps. ExecuteSwapOperations { /// The operations that should be performed in sequence. @@ -191,8 +183,17 @@ pub enum ExecuteMsg { // receiver: String, // }, /// Adds swap routes to the router. - AddSwapRoutes { - swap_routes: Vec, + AddSwapRoutes { swap_routes: Vec }, + /// Updates the configuration of the contract. + /// If a field is not specified (i.e., set to `None`), it will not be modified. + UpdateConfig { + /// The new whale-lair contract address. + whale_lair_addr: Option, + /// The new fee that must be paid when a pool is created. + pool_creation_fee: Option, + /// The new feature toggles of the contract, allowing fine-tuned + /// control over which operations are allowed. + feature_toggle: Option, }, } @@ -200,9 +201,16 @@ pub enum ExecuteMsg { #[cw_serde] #[derive(QueryResponses)] pub enum QueryMsg { - /// Retrieves the decimals for the given native or ibc denom. - #[returns(NativeTokenDecimalsResponse)] - NativeTokenDecimals { denom: String }, + /// Retrieves the contract's config. + #[returns(ConfigResponse)] + Config {}, + + /// Retrieves the decimals for the given asset. + #[returns(AssetDecimalsResponse)] + AssetDecimals { + pair_identifier: String, + denom: String, + }, /// Simulates a swap. #[returns(SimulationResponse)] @@ -221,13 +229,13 @@ pub enum QueryMsg { }, /// Gets the swap route for the given offer and ask assets. - #[returns(Vec)] + #[returns(SwapRouteResponse)] SwapRoute { offer_asset_denom: String, ask_asset_denom: String, }, /// Gets all swap routes registered - #[returns(Vec)] + #[returns(SwapRoutesResponse)] SwapRoutes {}, // /// Simulates swap operations. @@ -243,6 +251,64 @@ pub enum QueryMsg { // ask_amount: Uint128, // operations: Vec, // }, - #[returns(PairInfo)] + #[returns(PairInfoResponse)] Pair { pair_identifier: String }, } + +#[cw_serde] +pub struct ConfigResponse { + pub config: Config, +} + +#[cw_serde] +pub struct SwapRoutesResponse { + pub swap_routes: Vec, +} + +#[cw_serde] +pub struct PairInfoResponse { + pub pair_info: PairInfo, +} + +/// 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. + pub denom: String, + /// The decimals for the requested denom. + pub decimals: u8, +} + +/// SimulationResponse returns swap simulation response +#[cw_serde] +pub struct SimulationResponse { + pub return_amount: Uint128, + pub spread_amount: Uint128, + pub swap_fee_amount: Uint128, + pub protocol_fee_amount: Uint128, + pub burn_fee_amount: Uint128, + #[cfg(feature = "osmosis")] + pub osmosis_fee_amount: Uint128, +} + +/// ReverseSimulationResponse returns reverse swap simulation response +#[cw_serde] +pub struct ReverseSimulationResponse { + pub offer_amount: Uint128, + pub spread_amount: Uint128, + pub swap_fee_amount: Uint128, + pub protocol_fee_amount: Uint128, + pub burn_fee_amount: Uint128, + #[cfg(feature = "osmosis")] + pub osmosis_fee_amount: Uint128, +} + +/// Pool feature toggle +#[cw_serde] +pub struct FeatureToggle { + pub withdrawals_enabled: bool, + pub deposits_enabled: bool, + pub swaps_enabled: bool, +}