From 4d0b026a4505b806113b83f7253cf67b187ba292 Mon Sep 17 00:00:00 2001 From: Carlos Date: Fri, 1 Apr 2022 12:55:01 +0900 Subject: [PATCH] Re-release v2.2.x (#93) * new mirror_admin_manager contract * integrate admin_manager with governance, multiple poll types with different parameters * add the hability to change the mir-ust pair on collector * add collector migration logic * feat: ability to deprecate a staking token * add migration loogic for staking pool_infos * feat: integrate collateral_oracle with new tefi_oracle * fix test and typos, closes #77 * feat: integrate mint contract with new oracle and preIPO mech * feat: update mAsset whitelisting process to use new oracle and preIPO mech * chore: update admin_manager schema * chore: fmt and clippy * chore: fmt, clippy and generate schemas * fix(col_oracle): change is_revoked migration to consider rewly revoked assets * fix(col_oracle): add missing migrate_config funtion to migration entry point * fix(gov): minor fixes * make gov update_config an admin action * chore: fmt & clippy fixes * fix typos * gov: add poll_gas_limit parameter to prevent ghost failed polls * gov: add unit tests for new poll configs * migrate MIR-UST pair on contract migration (collector, staking) + add friendlier staking error for deprecated token * fix: modify bond migration check to have new addresses marked as migrated * remove unnecessary optional storage parameter + minor optimization * add admin_action to PollResponse * prevent converting address 2 times * update versions to v2.2.0 * update tefi_oracle to latest version, improved whitelisting process * col_oracle: migrate anchor oracle source to mirror tefi oracle * remove previous staking migration logic * change oracle address on factory/mint contracts migration * fix pagination on descending positions query * import tefi-oracle package from crates.io * update CHANGELOG and optimizer version --- .github/workflows/release-artifacts.yml | 2 +- CHANGELOG.md | 7 +- contracts/mirror_admin_manager/Cargo.toml | 4 +- contracts/mirror_collateral_oracle/Cargo.toml | 5 +- .../schema/config_response.json | 12 - .../schema/execute_msg.json | 90 +++--- .../schema/instantiate_msg.json | 12 - .../schema/migrate_msg.json | 15 +- .../schema/query_msg.json | 4 +- .../mirror_collateral_oracle/src/contract.rs | 84 +---- .../mirror_collateral_oracle/src/migration.rs | 154 ++++++++- .../mirror_collateral_oracle/src/querier.rs | 113 ++----- .../mirror_collateral_oracle/src/state.rs | 5 +- .../src/testing/mock_querier.rs | 53 +--- .../src/testing/tests.rs | 137 ++------ contracts/mirror_collector/Cargo.toml | 4 +- contracts/mirror_community/Cargo.toml | 4 +- contracts/mirror_factory/Cargo.toml | 5 +- .../mirror_factory/schema/execute_msg.json | 35 +- .../mirror_factory/schema/query_msg.json | 2 +- contracts/mirror_factory/src/contract.rs | 298 ++++++++++-------- contracts/mirror_factory/src/querier.rs | 32 +- contracts/mirror_factory/src/state.rs | 29 +- .../src/testing/mock_querier.rs | 96 +----- contracts/mirror_factory/src/testing/tests.rs | 228 +++++++------- contracts/mirror_gov/Cargo.toml | 4 +- .../mirror_gov/schema/poll_response.json | 207 ++++++++++++ .../mirror_gov/schema/polls_response.json | 207 ++++++++++++ contracts/mirror_gov/src/contract.rs | 35 +- contracts/mirror_gov/src/state.rs | 2 +- contracts/mirror_gov/src/testing/tests.rs | 87 +++++ contracts/mirror_limit_order/Cargo.toml | 4 +- contracts/mirror_lock/Cargo.toml | 4 +- contracts/mirror_mint/Cargo.toml | 5 +- .../schema/asset_config_response.json | 6 +- contracts/mirror_mint/schema/execute_msg.json | 6 +- contracts/mirror_mint/src/contract.rs | 48 +-- contracts/mirror_mint/src/lib.rs | 1 + contracts/mirror_mint/src/migration.rs | 104 ++++++ contracts/mirror_mint/src/positions.rs | 64 +--- contracts/mirror_mint/src/querier.rs | 125 ++++---- contracts/mirror_mint/src/state.rs | 13 +- .../mirror_mint/src/testing/contract_test.rs | 8 +- .../mirror_mint/src/testing/mock_querier.rs | 76 +---- .../mirror_mint/src/testing/pre_ipo_test.rs | 13 +- contracts/mirror_oracle/Cargo.toml | 4 +- contracts/mirror_short_reward/Cargo.toml | 4 +- contracts/mirror_staking/Cargo.toml | 5 +- contracts/mirror_staking/src/contract.rs | 21 +- contracts/mirror_staking/src/lib.rs | 1 - contracts/mirror_staking/src/migration.rs | 142 --------- contracts/mirror_staking/src/querier.rs | 20 +- .../src/testing/mock_querier.rs | 13 +- packages/mirror_protocol/Cargo.toml | 2 +- .../mirror_protocol/src/collateral_oracle.rs | 31 +- packages/mirror_protocol/src/factory.rs | 20 +- packages/mirror_protocol/src/gov.rs | 1 + packages/mirror_protocol/src/lib.rs | 2 +- packages/mirror_protocol/src/mint.rs | 6 + packages/mirror_protocol/src/staking.rs | 3 +- packages/tefi_oracle/Cargo.toml | 15 - packages/tefi_oracle/src/de.rs | 47 --- packages/tefi_oracle/src/errors.rs | 29 -- packages/tefi_oracle/src/hub.rs | 99 ------ packages/tefi_oracle/src/lib.rs | 5 - packages/tefi_oracle/src/proxy.rs | 15 - packages/tefi_oracle/src/querier.rs | 43 --- 67 files changed, 1466 insertions(+), 1511 deletions(-) create mode 100644 contracts/mirror_mint/src/migration.rs delete mode 100644 contracts/mirror_staking/src/migration.rs delete mode 100644 packages/tefi_oracle/Cargo.toml delete mode 100644 packages/tefi_oracle/src/de.rs delete mode 100644 packages/tefi_oracle/src/errors.rs delete mode 100644 packages/tefi_oracle/src/hub.rs delete mode 100644 packages/tefi_oracle/src/lib.rs delete mode 100644 packages/tefi_oracle/src/proxy.rs delete mode 100644 packages/tefi_oracle/src/querier.rs diff --git a/.github/workflows/release-artifacts.yml b/.github/workflows/release-artifacts.yml index d8362481..cd22998d 100644 --- a/.github/workflows/release-artifacts.yml +++ b/.github/workflows/release-artifacts.yml @@ -26,7 +26,7 @@ jobs: docker run --rm -v "$(pwd)":/code \ --mount type=volume,source="$(basename "$(pwd)")_cache",target=/code/target \ --mount type=volume,source=registry_cache,target=/usr/local/cargo/registry \ - cosmwasm/workspace-optimizer:0.11.5 + cosmwasm/workspace-optimizer:0.12.5 tar -zcvf cosmwasm-artifacts.tar.gz artifacts - name: Create Release uses: softprops/action-gh-release@v1 diff --git a/CHANGELOG.md b/CHANGELOG.md index 5282c4e8..3ca67bf7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ -# 2.1.4 +# 2.2.0 - Re-release -* Auction discount fix for mint contract #92 \ No newline at end of file +* Restore the updates for v2.2.0 that were postponed (#93) +* First round of preparations for Astroport migration (#80) +* Allow governance voters to execute migrations with a new admin_manager contract (#81) +* Integrate with the new oracle contracts (#81) \ No newline at end of file diff --git a/contracts/mirror_admin_manager/Cargo.toml b/contracts/mirror_admin_manager/Cargo.toml index cd8f6999..0ba460a3 100644 --- a/contracts/mirror_admin_manager/Cargo.toml +++ b/contracts/mirror_admin_manager/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "mirror-admin-manager" -version = "2.1.1" +version = "2.2.0" authors = ["Terraform Labs, PTE."] edition = "2018" description = "Admin manager contract for Mirror Protocol - holds and manages admin privileges for Mirror contracts" @@ -37,7 +37,7 @@ backtraces = ["cosmwasm-std/backtraces"] cw20 = { version = "0.8.0" } cosmwasm-std = { version = "0.16.0", features = ["iterator"] } cosmwasm-storage = { version = "0.16.0", features = ["iterator"] } -mirror-protocol = { version = "2.1.1", path = "../../packages/mirror_protocol" } +mirror-protocol = { version = "2.2.0", path = "../../packages/mirror_protocol" } schemars = "0.8.1" serde = { version = "1.0.103", default-features = false, features = ["derive"] } cw-storage-plus = { version = "0.8.0", features = ["iterator"]} diff --git a/contracts/mirror_collateral_oracle/Cargo.toml b/contracts/mirror_collateral_oracle/Cargo.toml index 1c254f94..befe6b7b 100644 --- a/contracts/mirror_collateral_oracle/Cargo.toml +++ b/contracts/mirror_collateral_oracle/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "mirror-collateral-oracle" -version = "2.1.2" +version = "2.2.0" authors = ["Terraform Labs, PTE."] edition = "2018" description = "A proxy oracle for collaterals in Mirror Protocol" @@ -36,7 +36,8 @@ backtraces = ["cosmwasm-std/backtraces"] [dependencies] cosmwasm-std = { version = "0.16.0", features = ["iterator"] } cosmwasm-storage = { version = "0.16.0", features = ["iterator"] } -mirror-protocol = { version = "2.1.1", path = "../../packages/mirror_protocol" } +mirror-protocol = { version = "2.2.0", path = "../../packages/mirror_protocol" } +tefi-oracle = "0.2.0" schemars = "0.8.1" serde = { version = "1.0.103", default-features = false, features = ["derive"] } terraswap = "2.4.0" diff --git a/contracts/mirror_collateral_oracle/schema/config_response.json b/contracts/mirror_collateral_oracle/schema/config_response.json index f53de6b4..95445530 100644 --- a/contracts/mirror_collateral_oracle/schema/config_response.json +++ b/contracts/mirror_collateral_oracle/schema/config_response.json @@ -3,29 +3,17 @@ "title": "ConfigResponse", "type": "object", "required": [ - "anchor_oracle", - "band_oracle", "base_denom", "mint_contract", - "mirror_oracle", "owner" ], "properties": { - "anchor_oracle": { - "type": "string" - }, - "band_oracle": { - "type": "string" - }, "base_denom": { "type": "string" }, "mint_contract": { "type": "string" }, - "mirror_oracle": { - "type": "string" - }, "owner": { "type": "string" } diff --git a/contracts/mirror_collateral_oracle/schema/execute_msg.json b/contracts/mirror_collateral_oracle/schema/execute_msg.json index adfe5d82..108b4e30 100644 --- a/contracts/mirror_collateral_oracle/schema/execute_msg.json +++ b/contracts/mirror_collateral_oracle/schema/execute_msg.json @@ -1,7 +1,7 @@ { "$schema": "http://json-schema.org/draft-07/schema#", "title": "ExecuteMsg", - "anyOf": [ + "oneOf": [ { "type": "object", "required": [ @@ -11,18 +11,6 @@ "update_config": { "type": "object", "properties": { - "anchor_oracle": { - "type": [ - "string", - "null" - ] - }, - "band_oracle": { - "type": [ - "string", - "null" - ] - }, "base_denom": { "type": [ "string", @@ -35,12 +23,6 @@ "null" ] }, - "mirror_oracle": { - "type": [ - "string", - "null" - ] - }, "owner": { "type": [ "string", @@ -152,7 +134,7 @@ "definitions": { "AssetInfo": { "description": "AssetInfo contract_addr is usually passed from the cw20 hook so we can trust the contract_addr is properly validated.", - "anyOf": [ + "oneOf": [ { "type": "object", "required": [ @@ -200,39 +182,23 @@ "type": "string" }, "SourceType": { - "anyOf": [ - { - "type": "object", - "required": [ - "mirror_oracle" - ], - "properties": { - "mirror_oracle": { - "type": "object" - } - }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "anchor_oracle" - ], - "properties": { - "anchor_oracle": { - "type": "object" - } - }, - "additionalProperties": false - }, + "oneOf": [ { "type": "object", "required": [ - "band_oracle" + "tefi_oracle" ], "properties": { - "band_oracle": { - "type": "object" + "tefi_oracle": { + "type": "object", + "required": [ + "oracle_addr" + ], + "properties": { + "oracle_addr": { + "type": "string" + } + } } }, "additionalProperties": false @@ -260,13 +226,13 @@ { "type": "object", "required": [ - "terraswap" + "amm_pair" ], "properties": { - "terraswap": { + "amm_pair": { "type": "object", "required": [ - "terraswap_pair_addr" + "pair_addr" ], "properties": { "intermediate_denom": { @@ -275,7 +241,7 @@ "null" ] }, - "terraswap_pair_addr": { + "pair_addr": { "type": "string" } } @@ -322,6 +288,26 @@ } }, "additionalProperties": false + }, + { + "type": "object", + "required": [ + "lunax" + ], + "properties": { + "lunax": { + "type": "object", + "required": [ + "staking_contract_addr" + ], + "properties": { + "staking_contract_addr": { + "type": "string" + } + } + } + }, + "additionalProperties": false } ] } diff --git a/contracts/mirror_collateral_oracle/schema/instantiate_msg.json b/contracts/mirror_collateral_oracle/schema/instantiate_msg.json index 0f716972..9cc8800c 100644 --- a/contracts/mirror_collateral_oracle/schema/instantiate_msg.json +++ b/contracts/mirror_collateral_oracle/schema/instantiate_msg.json @@ -3,29 +3,17 @@ "title": "InstantiateMsg", "type": "object", "required": [ - "anchor_oracle", - "band_oracle", "base_denom", "mint_contract", - "mirror_oracle", "owner" ], "properties": { - "anchor_oracle": { - "type": "string" - }, - "band_oracle": { - "type": "string" - }, "base_denom": { "type": "string" }, "mint_contract": { "type": "string" }, - "mirror_oracle": { - "type": "string" - }, "owner": { "type": "string" } diff --git a/contracts/mirror_collateral_oracle/schema/migrate_msg.json b/contracts/mirror_collateral_oracle/schema/migrate_msg.json index 666fb7e9..19beaed0 100644 --- a/contracts/mirror_collateral_oracle/schema/migrate_msg.json +++ b/contracts/mirror_collateral_oracle/schema/migrate_msg.json @@ -1,6 +1,17 @@ { "$schema": "http://json-schema.org/draft-07/schema#", "title": "MigrateMsg", - "description": "We currently take no arguments for migrations", - "type": "object" + "type": "object", + "required": [ + "anchor_tefi_oracle_addr", + "mirror_tefi_oracle_addr" + ], + "properties": { + "anchor_tefi_oracle_addr": { + "type": "string" + }, + "mirror_tefi_oracle_addr": { + "type": "string" + } + } } diff --git a/contracts/mirror_collateral_oracle/schema/query_msg.json b/contracts/mirror_collateral_oracle/schema/query_msg.json index 3ad23f4e..a3013588 100644 --- a/contracts/mirror_collateral_oracle/schema/query_msg.json +++ b/contracts/mirror_collateral_oracle/schema/query_msg.json @@ -1,7 +1,7 @@ { "$schema": "http://json-schema.org/draft-07/schema#", "title": "QueryMsg", - "anyOf": [ + "oneOf": [ { "type": "object", "required": [ @@ -29,7 +29,7 @@ "asset": { "type": "string" }, - "block_height": { + "timeframe": { "type": [ "integer", "null" diff --git a/contracts/mirror_collateral_oracle/src/contract.rs b/contracts/mirror_collateral_oracle/src/contract.rs index efe5303d..b25f9ec6 100644 --- a/contracts/mirror_collateral_oracle/src/contract.rs +++ b/contracts/mirror_collateral_oracle/src/contract.rs @@ -1,4 +1,4 @@ -use crate::migration::migrate_collateral_infos; +use crate::migration::{migrate_collateral_infos, migrate_config}; use crate::querier::query_price; use crate::state::{ read_collateral_info, read_collateral_infos, read_config, store_collateral_info, store_config, @@ -29,9 +29,6 @@ pub fn instantiate( owner: deps.api.addr_canonicalize(&msg.owner)?, mint_contract: deps.api.addr_canonicalize(&msg.mint_contract)?, base_denom: msg.base_denom, - mirror_oracle: deps.api.addr_canonicalize(&msg.mirror_oracle)?, - anchor_oracle: deps.api.addr_canonicalize(&msg.anchor_oracle)?, - band_oracle: deps.api.addr_canonicalize(&msg.band_oracle)?, }, )?; @@ -50,19 +47,7 @@ pub fn execute( owner, mint_contract, base_denom, - mirror_oracle, - anchor_oracle, - band_oracle, - } => update_config( - deps, - info, - owner, - mint_contract, - base_denom, - mirror_oracle, - anchor_oracle, - band_oracle, - ), + } => update_config(deps, info, owner, mint_contract, base_denom), ExecuteMsg::RegisterCollateralAsset { asset, price_source, @@ -86,9 +71,6 @@ pub fn update_config( owner: Option, mint_contract: Option, base_denom: Option, - mirror_oracle: Option, - anchor_oracle: Option, - band_oracle: Option, ) -> StdResult { let mut config: Config = read_config(deps.storage)?; if deps.api.addr_canonicalize(info.sender.as_str())? != config.owner { @@ -107,18 +89,6 @@ pub fn update_config( config.base_denom = base_denom; } - if let Some(mirror_oracle) = mirror_oracle { - config.mirror_oracle = deps.api.addr_canonicalize(&mirror_oracle)?; - } - - if let Some(anchor_oracle) = anchor_oracle { - config.anchor_oracle = deps.api.addr_canonicalize(&anchor_oracle)?; - } - - if let Some(band_oracle) = band_oracle { - config.band_oracle = deps.api.addr_canonicalize(&band_oracle)?; - } - store_config(deps.storage, &config)?; Ok(Response::default()) } @@ -242,13 +212,12 @@ pub fn update_collateral_multiplier( } #[cfg_attr(not(feature = "library"), entry_point)] -pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> StdResult { +pub fn query(deps: Deps, env: Env, msg: QueryMsg) -> StdResult { match msg { QueryMsg::Config {} => to_binary(&query_config(deps)?), - QueryMsg::CollateralPrice { - asset, - block_height, - } => to_binary(&query_collateral_price(deps, asset, block_height)?), + QueryMsg::CollateralPrice { asset, timeframe } => { + to_binary(&query_collateral_price(deps, env, asset, timeframe)?) + } QueryMsg::CollateralAssetInfo { asset } => to_binary(&query_collateral_info(deps, asset)?), QueryMsg::CollateralAssetInfos {} => to_binary(&query_collateral_infos(deps)?), } @@ -260,9 +229,6 @@ pub fn query_config(deps: Deps) -> StdResult { owner: deps.api.addr_humanize(&config.owner)?.to_string(), mint_contract: deps.api.addr_humanize(&config.mint_contract)?.to_string(), base_denom: config.base_denom, - mirror_oracle: deps.api.addr_humanize(&config.mirror_oracle)?.to_string(), - anchor_oracle: deps.api.addr_humanize(&config.anchor_oracle)?.to_string(), - band_oracle: deps.api.addr_humanize(&config.band_oracle)?.to_string(), }; Ok(resp) @@ -270,8 +236,9 @@ pub fn query_config(deps: Deps) -> StdResult { pub fn query_collateral_price( deps: Deps, + env: Env, quote_asset: String, - block_height: Option, + timeframe: Option, ) -> StdResult { let config: Config = read_config(deps.storage)?; @@ -284,9 +251,10 @@ pub fn query_collateral_price( let (price, last_updated): (Decimal, u64) = query_price( deps, + env, &config, "e_asset, - block_height, + timeframe, &collateral.price_source, )?; @@ -323,36 +291,10 @@ pub fn query_collateral_infos(deps: Deps) -> StdResult #[cfg_attr(not(feature = "library"), entry_point)] pub fn migrate(deps: DepsMut, _env: Env, msg: MigrateMsg) -> StdResult { - // migrate collateral infos to inclue new source type - migrate_collateral_infos(deps.storage)?; + migrate_config(deps.storage)?; - // validate input - deps.api.addr_validate(&msg.lunax_staking_contract)?; - deps.api.addr_validate(&msg.lunax_token_addr)?; - if msg.multiplier.is_zero() { - return Err(StdError::generic_err("Multiplier must be bigger than 0")); - } - - // register lunax - let lunax_asset_info = AssetInfo::Token { - contract_addr: msg.lunax_token_addr, - }; - let lunax_source = SourceType::Lunax { - staking_contract_addr: msg.lunax_staking_contract, - }; - - let config: Config = read_config(deps.storage)?; - let self_info = MessageInfo { - sender: deps.api.addr_humanize(&config.owner)?, - funds: vec![], - }; - register_collateral( - deps, - self_info, - lunax_asset_info, - lunax_source, - msg.multiplier, - )?; + deps.api.addr_validate(&msg.mirror_tefi_oracle_addr)?; + migrate_collateral_infos(deps.storage, msg.mirror_tefi_oracle_addr)?; Ok(Response::default()) } diff --git a/contracts/mirror_collateral_oracle/src/migration.rs b/contracts/mirror_collateral_oracle/src/migration.rs index 048be659..0962251f 100644 --- a/contracts/mirror_collateral_oracle/src/migration.rs +++ b/contracts/mirror_collateral_oracle/src/migration.rs @@ -1,11 +1,21 @@ -use cosmwasm_storage::Bucket; +use cosmwasm_storage::{singleton, singleton_read, Bucket, ReadonlySingleton, Singleton}; use mirror_protocol::collateral_oracle::SourceType; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; -use cosmwasm_std::{Decimal, Order, StdResult, Storage}; +use cosmwasm_std::{CanonicalAddr, Decimal, Order, StdError, StdResult, Storage}; -use crate::state::{CollateralAssetInfo, PREFIX_COLLATERAL_ASSET_INFO}; +use crate::state::{CollateralAssetInfo, Config, KEY_CONFIG, PREFIX_COLLATERAL_ASSET_INFO}; + +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] +pub struct LegacyConfig { + pub owner: CanonicalAddr, + pub mint_contract: CanonicalAddr, + pub base_denom: String, + pub mirror_oracle: CanonicalAddr, + pub anchor_oracle: CanonicalAddr, + pub band_oracle: CanonicalAddr, +} #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] pub struct LegacyCollateralAssetInfo { @@ -34,9 +44,28 @@ pub enum LegacySourceType { Native { native_denom: String, }, + Lunax { + staking_contract_addr: String, + }, } -pub fn migrate_collateral_infos(storage: &mut dyn Storage) -> StdResult<()> { +pub fn migrate_config(storage: &mut dyn Storage) -> StdResult<()> { + let legacty_store: ReadonlySingleton = singleton_read(storage, KEY_CONFIG); + let legacy_config: LegacyConfig = legacty_store.load()?; + let config = Config { + owner: legacy_config.owner, + mint_contract: legacy_config.mint_contract, + base_denom: legacy_config.base_denom, + }; + let mut store: Singleton = singleton(storage, KEY_CONFIG); + store.save(&config)?; + Ok(()) +} + +pub fn migrate_collateral_infos( + storage: &mut dyn Storage, + mirror_tefi_oracle_addr: String, +) -> StdResult<()> { let mut legacy_collateral_infos_bucket: Bucket = Bucket::new(storage, PREFIX_COLLATERAL_ASSET_INFO); @@ -55,9 +84,15 @@ pub fn migrate_collateral_infos(storage: &mut dyn Storage) -> StdResult<()> { for (_, legacy_collateral_info) in collateral_infos.into_iter() { let new_price_source: SourceType = match legacy_collateral_info.price_source { - LegacySourceType::BandOracle {} => SourceType::BandOracle {}, - LegacySourceType::AnchorOracle {} => SourceType::AnchorOracle {}, - LegacySourceType::MirrorOracle {} => SourceType::MirrorOracle {}, + LegacySourceType::BandOracle { .. } => { + return Err(StdError::generic_err("not supported")) + } // currently there are no assets with this config + LegacySourceType::AnchorOracle { .. } => SourceType::TefiOracle { + oracle_addr: mirror_tefi_oracle_addr.clone(), + }, + LegacySourceType::MirrorOracle { .. } => SourceType::TefiOracle { + oracle_addr: mirror_tefi_oracle_addr.clone(), + }, LegacySourceType::AnchorMarket { anchor_market_addr } => { SourceType::AnchorMarket { anchor_market_addr } } @@ -66,10 +101,15 @@ pub fn migrate_collateral_infos(storage: &mut dyn Storage) -> StdResult<()> { LegacySourceType::Terraswap { terraswap_pair_addr, intermediate_denom, - } => SourceType::Terraswap { - terraswap_pair_addr, + } => SourceType::AmmPair { + pair_addr: terraswap_pair_addr, intermediate_denom, }, + LegacySourceType::Lunax { + staking_contract_addr, + } => SourceType::Lunax { + staking_contract_addr, + }, }; let new_collateral_info = &CollateralAssetInfo { @@ -83,3 +123,99 @@ pub fn migrate_collateral_infos(storage: &mut dyn Storage) -> StdResult<()> { Ok(()) } + +#[cfg(test)] +mod migrate_tests { + use crate::state::read_collateral_info; + + use super::*; + use cosmwasm_std::testing::mock_dependencies; + + pub fn collateral_infos_old_store( + storage: &mut dyn Storage, + ) -> Bucket { + Bucket::new(storage, PREFIX_COLLATERAL_ASSET_INFO) + } + + #[test] + fn test_collateral_infos_migration() { + let mut deps = mock_dependencies(&[]); + let mut legacy_store = collateral_infos_old_store(&mut deps.storage); + + let col_info_1 = LegacyCollateralAssetInfo { + asset: "mAPPL0000".to_string(), + multiplier: Decimal::one(), + price_source: LegacySourceType::MirrorOracle {}, + is_revoked: false, + }; + let col_info_2 = LegacyCollateralAssetInfo { + asset: "anc0000".to_string(), + multiplier: Decimal::one(), + price_source: LegacySourceType::Terraswap { + terraswap_pair_addr: "pair0000".to_string(), + intermediate_denom: None, + }, + is_revoked: false, + }; + let col_info_3 = LegacyCollateralAssetInfo { + asset: "bluna0000".to_string(), + multiplier: Decimal::one(), + price_source: LegacySourceType::AnchorOracle {}, + is_revoked: false, + }; + + legacy_store + .save(col_info_1.asset.as_bytes(), &col_info_1) + .unwrap(); + legacy_store + .save(col_info_2.asset.as_bytes(), &col_info_2) + .unwrap(); + legacy_store + .save(col_info_3.asset.as_bytes(), &col_info_3) + .unwrap(); + + migrate_collateral_infos(deps.as_mut().storage, "mirrortefi0000".to_string()).unwrap(); + + let new_col_info_1: CollateralAssetInfo = + read_collateral_info(deps.as_mut().storage, &col_info_1.asset).unwrap(); + let new_col_info_2: CollateralAssetInfo = + read_collateral_info(deps.as_mut().storage, &col_info_2.asset).unwrap(); + let new_col_info_3: CollateralAssetInfo = + read_collateral_info(deps.as_mut().storage, &col_info_3.asset).unwrap(); + + assert_eq!( + new_col_info_1, + CollateralAssetInfo { + asset: "mAPPL0000".to_string(), + multiplier: Decimal::one(), + price_source: SourceType::TefiOracle { + oracle_addr: "mirrortefi0000".to_string(), + }, + is_revoked: false, + } + ); + assert_eq!( + new_col_info_2, + CollateralAssetInfo { + asset: "anc0000".to_string(), + multiplier: Decimal::one(), + price_source: SourceType::AmmPair { + pair_addr: "pair0000".to_string(), + intermediate_denom: None, + }, + is_revoked: false, + } + ); + assert_eq!( + new_col_info_3, + CollateralAssetInfo { + asset: "bluna0000".to_string(), + multiplier: Decimal::one(), + price_source: SourceType::TefiOracle { + oracle_addr: "mirrortefi0000".to_string(), + }, + is_revoked: false, + } + ) + } +} diff --git a/contracts/mirror_collateral_oracle/src/querier.rs b/contracts/mirror_collateral_oracle/src/querier.rs index 6f326858..4da3bf3f 100644 --- a/contracts/mirror_collateral_oracle/src/querier.rs +++ b/contracts/mirror_collateral_oracle/src/querier.rs @@ -2,28 +2,24 @@ use crate::math::decimal_multiplication; use crate::state::Config; use cosmwasm_bignumber::{Decimal256, Uint256}; use cosmwasm_std::{ - to_binary, Addr, Decimal, Deps, QuerierWrapper, QueryRequest, StdError, StdResult, Timestamp, - Uint128, WasmQuery, + to_binary, Addr, Decimal, Deps, Env, QuerierWrapper, QueryRequest, StdError, StdResult, + Timestamp, Uint128, WasmQuery, }; use mirror_protocol::collateral_oracle::SourceType; use serde::{Deserialize, Serialize}; -use std::str::FromStr; +use tefi_oracle::hub::{ + HubQueryMsg as TeFiOracleQueryMsg, PriceResponse as TeFiOraclePriceResponse, +}; use terra_cosmwasm::{ExchangeRatesResponse, TerraQuerier}; use terraswap::asset::{Asset, AssetInfo}; -use terraswap::pair::QueryMsg as TerraswapPairQueryMsg; +use terraswap::pair::QueryMsg as AMMPairQueryMsg; #[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] #[serde(rename_all = "snake_case")] pub enum SourceQueryMsg { - Price { - base_asset: String, - quote_asset: String, - }, + // Query message for terraswap pool Pool {}, - GetReferenceData { - base_symbol: String, - quote_symbol: String, - }, + // Query message for anchor market EpochState { block_height: Option, distributed_interest: Option, @@ -33,23 +29,11 @@ pub enum SourceQueryMsg { } #[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] -pub struct TerraOracleResponse { - // oracle queries returns rate - pub rate: Decimal, - pub last_updated_base: u64, -} -#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] -pub struct TerraswapResponse { - // terraswap queries return pool assets +pub struct AMMPairResponse { + // queries return pool assets pub assets: [Asset; 2], } #[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] -pub struct BandOracleResponse { - // band oracle queries returns rate (uint128) - pub rate: Uint128, - pub last_updated_base: u64, -} -#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] pub struct AnchorMarketResponse { // anchor market queries return exchange rate in Decimal256 pub exchange_rate: Decimal256, @@ -76,61 +60,35 @@ pub struct LunaxStateResponse { #[allow(clippy::ptr_arg)] pub fn query_price( deps: Deps, + env: Env, config: &Config, asset: &String, - block_height: Option, + timeframe: Option, price_source: &SourceType, ) -> StdResult<(Decimal, u64)> { match price_source { - SourceType::BandOracle {} => { - let res: BandOracleResponse = - deps.querier.query(&QueryRequest::Wasm(WasmQuery::Smart { - contract_addr: deps.api.addr_humanize(&config.band_oracle)?.to_string(), - msg: to_binary(&SourceQueryMsg::GetReferenceData { - base_symbol: asset.to_string(), - quote_symbol: config.base_denom.clone(), - }) - .unwrap(), - }))?; - let rate: Decimal = parse_band_rate(res.rate)?; - - Ok((rate, res.last_updated_base)) - } SourceType::FixedPrice { price } => Ok((*price, u64::MAX)), - SourceType::MirrorOracle {} => { - let res: TerraOracleResponse = + SourceType::TefiOracle { oracle_addr } => { + let res: TeFiOraclePriceResponse = deps.querier.query(&QueryRequest::Wasm(WasmQuery::Smart { - contract_addr: deps.api.addr_humanize(&config.mirror_oracle)?.to_string(), - msg: to_binary(&SourceQueryMsg::Price { - base_asset: asset.to_string(), - quote_asset: config.base_denom.clone(), + contract_addr: oracle_addr.to_string(), + msg: to_binary(&TeFiOracleQueryMsg::Price { + asset_token: asset.to_string(), + timeframe, }) .unwrap(), }))?; - Ok((res.rate, res.last_updated_base)) + Ok((res.rate, res.last_updated)) } - SourceType::AnchorOracle {} => { - let res: TerraOracleResponse = - deps.querier.query(&QueryRequest::Wasm(WasmQuery::Smart { - contract_addr: deps.api.addr_humanize(&config.anchor_oracle)?.to_string(), - msg: to_binary(&SourceQueryMsg::Price { - base_asset: asset.to_string(), - quote_asset: config.base_denom.clone(), - }) - .unwrap(), - }))?; - - Ok((res.rate, res.last_updated_base)) - } - SourceType::Terraswap { - terraswap_pair_addr, + SourceType::AmmPair { + pair_addr, intermediate_denom, } => { - let res: TerraswapResponse = + let res: AMMPairResponse = deps.querier.query(&QueryRequest::Wasm(WasmQuery::Smart { - contract_addr: terraswap_pair_addr.to_string(), - msg: to_binary(&TerraswapPairQueryMsg::Pool {}).unwrap(), + contract_addr: pair_addr.to_string(), + msg: to_binary(&AMMPairQueryMsg::Pool {}).unwrap(), }))?; let assets: [Asset; 2] = res.assets; @@ -168,7 +126,7 @@ pub fn query_price( deps.querier.query(&QueryRequest::Wasm(WasmQuery::Smart { contract_addr: anchor_market_addr.to_string(), msg: to_binary(&SourceQueryMsg::EpochState { - block_height, + block_height: Some(env.block.height), distributed_interest: None, }) .unwrap(), @@ -207,27 +165,6 @@ pub fn query_price( } } -/// Parses a uint that contains the price multiplied by 1e18 -fn parse_band_rate(uint_rate: Uint128) -> StdResult { - // manipulate the uint as a string to prevent overflow - let mut rate_uint_string: String = uint_rate.to_string(); - - let uint_len = rate_uint_string.len(); - if uint_len > 18 { - let dec_point = rate_uint_string.len() - 18; - rate_uint_string.insert(dec_point, '.'); - } else { - let mut prefix: String = "0.".to_owned(); - let dec_zeros = 18 - uint_len; - for _ in 0..dec_zeros { - prefix.push('0'); - } - rate_uint_string = prefix + rate_uint_string.as_str(); - } - - Decimal::from_str(rate_uint_string.as_str()) -} - fn query_native_rate( querier: &QuerierWrapper, base_denom: String, diff --git a/contracts/mirror_collateral_oracle/src/state.rs b/contracts/mirror_collateral_oracle/src/state.rs index fb8bf64d..36dfecd7 100644 --- a/contracts/mirror_collateral_oracle/src/state.rs +++ b/contracts/mirror_collateral_oracle/src/state.rs @@ -5,16 +5,13 @@ use schemars::JsonSchema; use serde::{Deserialize, Serialize}; pub static PREFIX_COLLATERAL_ASSET_INFO: &[u8] = b"collateral_asset_info"; -static KEY_CONFIG: &[u8] = b"config"; +pub static KEY_CONFIG: &[u8] = b"config"; #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] pub struct Config { pub owner: CanonicalAddr, pub mint_contract: CanonicalAddr, pub base_denom: String, - pub mirror_oracle: CanonicalAddr, - pub anchor_oracle: CanonicalAddr, - pub band_oracle: CanonicalAddr, } pub fn store_config(storage: &mut dyn Storage, config: &Config) -> StdResult<()> { diff --git a/contracts/mirror_collateral_oracle/src/testing/mock_querier.rs b/contracts/mirror_collateral_oracle/src/testing/mock_querier.rs index bbd049cc..213b2cb4 100644 --- a/contracts/mirror_collateral_oracle/src/testing/mock_querier.rs +++ b/contracts/mirror_collateral_oracle/src/testing/mock_querier.rs @@ -1,15 +1,14 @@ -use crate::math::decimal_division; use cosmwasm_bignumber::{Decimal256, Uint256}; use cosmwasm_std::testing::{MockApi, MockQuerier, MockStorage, MOCK_CONTRACT_ADDR}; use cosmwasm_std::{ from_binary, from_slice, to_binary, Addr, Coin, ContractResult, Decimal, OwnedDeps, Querier, QuerierResult, QueryRequest, SystemError, SystemResult, Timestamp, Uint128, WasmQuery, }; -use mirror_protocol::oracle::PriceResponse; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; use std::collections::HashMap; use std::str::FromStr; +use tefi_oracle::hub::PriceResponse as TeFiOraclePriceResponse; use terra_cosmwasm::{ ExchangeRateItem, ExchangeRatesResponse, TerraQuery, TerraQueryWrapper, TerraRoute, }; @@ -106,14 +105,6 @@ impl Querier for WasmMockQuerier { } } -#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] -#[serde(rename_all = "snake_case")] -pub struct ReferenceData { - rate: Uint128, - last_updated_base: u64, - last_updated_quote: u64, -} - #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] #[serde(rename_all = "snake_case")] pub struct EpochStateResponse { @@ -143,14 +134,10 @@ pub struct LunaxStateResponse { #[serde(rename_all = "snake_case")] pub enum QueryMsg { Price { - base_asset: String, - quote_asset: String, + asset_token: String, + timeframe: Option, }, Pool {}, - GetReferenceData { - base_symbol: String, - quote_symbol: String, - }, EpochState { block_heigth: Option, distributed_interest: Option, @@ -187,24 +174,15 @@ impl WasmMockQuerier { .unwrap() { QueryMsg::Price { - base_asset, - quote_asset, - } => match self.oracle_price_querier.oracle_price.get(&base_asset) { - Some(base_price) => { - match self.oracle_price_querier.oracle_price.get("e_asset) { - Some(quote_price) => { - SystemResult::Ok(ContractResult::from(to_binary(&PriceResponse { - rate: decimal_division(*base_price, *quote_price), - last_updated_base: 1000u64, - last_updated_quote: 1000u64, - }))) - } - None => SystemResult::Err(SystemError::InvalidRequest { - error: "No oracle price exists".to_string(), - request: msg.as_slice().into(), - }), - } - } + asset_token, + timeframe: _, + } => match self.oracle_price_querier.oracle_price.get(&asset_token) { + Some(base_price) => SystemResult::Ok(ContractResult::from(to_binary( + &TeFiOraclePriceResponse { + rate: *base_price, + last_updated: 1000u64, + }, + ))), None => SystemResult::Err(SystemError::InvalidRequest { error: "No oracle price exists".to_string(), request: msg.as_slice().into(), @@ -231,13 +209,6 @@ impl WasmMockQuerier { request: msg.as_slice().into(), }), }, - QueryMsg::GetReferenceData { .. } => { - SystemResult::Ok(ContractResult::from(to_binary(&ReferenceData { - rate: Uint128::from(3465211050000000000000u128), - last_updated_base: 100u64, - last_updated_quote: 100u64, - }))) - } QueryMsg::EpochState { .. } => { SystemResult::Ok(ContractResult::from(to_binary(&EpochStateResponse { exchange_rate: Decimal256::from_ratio(10, 3), diff --git a/contracts/mirror_collateral_oracle/src/testing/tests.rs b/contracts/mirror_collateral_oracle/src/testing/tests.rs index 7ddb3cf7..adb9b9c7 100644 --- a/contracts/mirror_collateral_oracle/src/testing/tests.rs +++ b/contracts/mirror_collateral_oracle/src/testing/tests.rs @@ -7,7 +7,6 @@ use cosmwasm_std::{Decimal, StdError, Uint128}; use mirror_protocol::collateral_oracle::{ CollateralInfoResponse, CollateralPriceResponse, ExecuteMsg, InstantiateMsg, SourceType, }; -use std::str::FromStr; use terraswap::asset::AssetInfo; #[test] @@ -18,9 +17,6 @@ fn proper_initialization() { owner: "owner0000".to_string(), mint_contract: "mint0000".to_string(), base_denom: "uusd".to_string(), - mirror_oracle: "mirrororacle0000".to_string(), - anchor_oracle: "anchororacle0000".to_string(), - band_oracle: "bandoracle0000".to_string(), }; let info = mock_info("addr0000", &[]); @@ -44,9 +40,6 @@ fn update_config() { owner: "owner0000".to_string(), mint_contract: "mint0000".to_string(), base_denom: "uusd".to_string(), - mirror_oracle: "mirrororacle0000".to_string(), - anchor_oracle: "anchororacle0000".to_string(), - band_oracle: "bandoracle0000".to_string(), }; let info = mock_info("addr0000", &[]); @@ -58,9 +51,6 @@ fn update_config() { owner: Some("owner0001".to_string()), mint_contract: Some("mint0001".to_string()), base_denom: Some("uluna".to_string()), - mirror_oracle: Some("mirrororacle0001".to_string()), - anchor_oracle: Some("anchororacle0001".to_string()), - band_oracle: Some("bandoracle0001".to_string()), }; let res = execute(deps.as_mut(), mock_env(), info, msg).unwrap(); @@ -71,9 +61,6 @@ fn update_config() { assert_eq!("owner0001", value.owner.as_str()); assert_eq!("mint0001", value.mint_contract.as_str()); assert_eq!("uluna", value.base_denom.as_str()); - assert_eq!("mirrororacle0001", value.mirror_oracle.as_str()); - assert_eq!("anchororacle0001", value.anchor_oracle.as_str()); - assert_eq!("bandoracle0001", value.band_oracle.as_str()); // Unauthorized err let info = mock_info("owner0000", &[]); @@ -81,9 +68,6 @@ fn update_config() { owner: None, mint_contract: None, base_denom: None, - mirror_oracle: None, - anchor_oracle: None, - band_oracle: None, }; let res = execute(deps.as_mut(), mock_env(), info, msg); @@ -105,9 +89,6 @@ fn register_collateral() { owner: "owner0000".to_string(), mint_contract: "mint0000".to_string(), base_denom: "uusd".to_string(), - mirror_oracle: "mirrororacle0000".to_string(), - anchor_oracle: "anchororacle0000".to_string(), - band_oracle: "bandoracle0000".to_string(), }; let info = mock_info("addr0000", &[]); @@ -118,7 +99,9 @@ fn register_collateral() { contract_addr: "mTSLA".to_string(), }, multiplier: Decimal::percent(100), - price_source: SourceType::MirrorOracle {}, + price_source: SourceType::TefiOracle { + oracle_addr: "mirrorOracle0000".to_string(), + }, }; // unauthorized attempt @@ -137,7 +120,7 @@ fn register_collateral() { query_res, CollateralInfoResponse { asset: "mTSLA".to_string(), - source_type: "mirror_oracle".to_string(), + source_type: "tefi_oracle".to_string(), multiplier: Decimal::percent(100), is_revoked: false, } @@ -156,9 +139,6 @@ fn update_collateral() { owner: "owner0000".to_string(), mint_contract: "mint0000".to_string(), base_denom: "uusd".to_string(), - mirror_oracle: "mirrororacle0000".to_string(), - anchor_oracle: "anchororacle0000".to_string(), - band_oracle: "bandoracle0000".to_string(), }; let info = mock_info("addr0000", &[]); @@ -169,7 +149,9 @@ fn update_collateral() { contract_addr: "mTSLA".to_string(), }, multiplier: Decimal::percent(100), - price_source: SourceType::MirrorOracle {}, + price_source: SourceType::TefiOracle { + oracle_addr: "mirrorOracle0000".to_string(), + }, }; // successfull attempt @@ -183,7 +165,7 @@ fn update_collateral() { query_res, CollateralInfoResponse { asset: "mTSLA".to_string(), - source_type: "mirror_oracle".to_string(), + source_type: "tefi_oracle".to_string(), multiplier: Decimal::percent(100), is_revoked: false, } @@ -280,9 +262,6 @@ fn get_oracle_price() { owner: "owner0000".to_string(), mint_contract: "mint0000".to_string(), base_denom: "uusd".to_string(), - mirror_oracle: "mirrororacle0000".to_string(), - anchor_oracle: "anchororacle0000".to_string(), - band_oracle: "bandoracle0000".to_string(), }; let info = mock_info("addr0000", &[]); @@ -293,13 +272,16 @@ fn get_oracle_price() { contract_addr: "mTSLA".to_string(), }, multiplier: Decimal::percent(100), - price_source: SourceType::MirrorOracle {}, + price_source: SourceType::TefiOracle { + oracle_addr: "mirrorOracle0000".to_string(), + }, }; let info = mock_info("owner0000", &[]); let _res = execute(deps.as_mut(), mock_env(), info, msg).unwrap(); // attempt to query price - let query_res = query_collateral_price(deps.as_ref(), "mTSLA".to_string(), None).unwrap(); + let query_res = + query_collateral_price(deps.as_ref(), mock_env(), "mTSLA".to_string(), None).unwrap(); assert_eq!( query_res, CollateralPriceResponse { @@ -313,7 +295,7 @@ fn get_oracle_price() { } #[test] -fn get_terraswap_price() { +fn get_amm_pair_price() { let mut deps = mock_dependencies(&[]); deps.querier.with_terraswap_pools(&[ ( @@ -340,9 +322,6 @@ fn get_terraswap_price() { owner: "owner0000".to_string(), mint_contract: "mint0000".to_string(), base_denom: "uusd".to_string(), - mirror_oracle: "mirrororacle0000".to_string(), - anchor_oracle: "anchororacle0000".to_string(), - band_oracle: "bandoracle0000".to_string(), }; let info = mock_info("addr0000", &[]); @@ -353,8 +332,8 @@ fn get_terraswap_price() { contract_addr: "anc0000".to_string(), }, multiplier: Decimal::percent(100), - price_source: SourceType::Terraswap { - terraswap_pair_addr: "ustancpair0000".to_string(), + price_source: SourceType::AmmPair { + pair_addr: "ustancpair0000".to_string(), intermediate_denom: None, }, }; @@ -363,7 +342,8 @@ fn get_terraswap_price() { let _res = execute(deps.as_mut(), mock_env(), info, msg).unwrap(); // attempt to query price - let query_res = query_collateral_price(deps.as_ref(), "anc0000".to_string(), None).unwrap(); + let query_res = + query_collateral_price(deps.as_ref(), mock_env(), "anc0000".to_string(), None).unwrap(); assert_eq!( query_res, CollateralPriceResponse { @@ -381,8 +361,8 @@ fn get_terraswap_price() { contract_addr: "bluna0000".to_string(), }, multiplier: Decimal::percent(100), - price_source: SourceType::Terraswap { - terraswap_pair_addr: "lunablunapair0000".to_string(), + price_source: SourceType::AmmPair { + pair_addr: "lunablunapair0000".to_string(), intermediate_denom: Some("uluna".to_string()), }, }; @@ -391,7 +371,8 @@ fn get_terraswap_price() { let _res = execute(deps.as_mut(), mock_env(), info, msg).unwrap(); // attempt to query price - let query_res = query_collateral_price(deps.as_ref(), "bluna0000".to_string(), None).unwrap(); + let query_res = + query_collateral_price(deps.as_ref(), mock_env(), "bluna0000".to_string(), None).unwrap(); assert_eq!( query_res, CollateralPriceResponse { @@ -412,9 +393,6 @@ fn get_fixed_price() { owner: "owner0000".to_string(), mint_contract: "mint0000".to_string(), base_denom: "uusd".to_string(), - mirror_oracle: "mirrororacle0000".to_string(), - anchor_oracle: "anchororacle0000".to_string(), - band_oracle: "bandoracle0000".to_string(), }; let info = mock_info("addr0000", &[]); @@ -434,7 +412,8 @@ fn get_fixed_price() { let _res = execute(deps.as_mut(), mock_env(), info, msg).unwrap(); // attempt to query price - let query_res = query_collateral_price(deps.as_ref(), "aUST".to_string(), None).unwrap(); + let query_res = + query_collateral_price(deps.as_ref(), mock_env(), "aUST".to_string(), None).unwrap(); assert_eq!( query_res, CollateralPriceResponse { @@ -447,47 +426,6 @@ fn get_fixed_price() { ); } -#[test] -fn get_band_oracle_price() { - let mut deps = mock_dependencies(&[]); - - let msg = InstantiateMsg { - owner: "owner0000".to_string(), - mint_contract: "mint0000".to_string(), - base_denom: "uusd".to_string(), - mirror_oracle: "mirrororacle0000".to_string(), - anchor_oracle: "anchororacle0000".to_string(), - band_oracle: "bandoracle0000".to_string(), - }; - - let info = mock_info("addr0000", &[]); - let _res = instantiate(deps.as_mut(), mock_env(), info, msg).unwrap(); - - let msg = ExecuteMsg::RegisterCollateralAsset { - asset: AssetInfo::NativeToken { - denom: "uluna".to_string(), - }, - multiplier: Decimal::percent(100), - price_source: SourceType::BandOracle {}, - }; - - let info = mock_info("owner0000", &[]); - let _res = execute(deps.as_mut(), mock_env(), info, msg).unwrap(); - - // attempt to query price - let query_res = query_collateral_price(deps.as_ref(), "uluna".to_string(), None).unwrap(); - assert_eq!( - query_res, - CollateralPriceResponse { - asset: "uluna".to_string(), - rate: Decimal::from_str("3465.211050000000000000").unwrap(), - last_updated: 100u64, - multiplier: Decimal::percent(100), - is_revoked: false, - } - ); -} - #[test] fn get_anchor_market_price() { let mut deps = mock_dependencies(&[]); @@ -496,9 +434,6 @@ fn get_anchor_market_price() { owner: "owner0000".to_string(), mint_contract: "mint0000".to_string(), base_denom: "uusd".to_string(), - mirror_oracle: "mirrororacle0000".to_string(), - anchor_oracle: "anchororacle0000".to_string(), - band_oracle: "bandoracle0000".to_string(), }; let info = mock_info("addr0000", &[]); @@ -518,7 +453,8 @@ fn get_anchor_market_price() { let _res = execute(deps.as_mut(), mock_env(), info, msg).unwrap(); // attempt to query price - let query_res = query_collateral_price(deps.as_ref(), "aust0000".to_string(), None).unwrap(); + let query_res = + query_collateral_price(deps.as_ref(), mock_env(), "aust0000".to_string(), None).unwrap(); assert_eq!( query_res, CollateralPriceResponse { @@ -539,9 +475,6 @@ fn get_lunax_price() { owner: "owner0000".to_string(), mint_contract: "mint0000".to_string(), base_denom: "uusd".to_string(), - mirror_oracle: "mirrororacle0000".to_string(), - anchor_oracle: "anchororacle0000".to_string(), - band_oracle: "bandoracle0000".to_string(), }; let info = mock_info("addr0000", &[]); @@ -561,7 +494,8 @@ fn get_lunax_price() { let _res = execute(deps.as_mut(), mock_env(), info, msg).unwrap(); // attempt to query price - let query_res = query_collateral_price(deps.as_ref(), "lunax0000".to_string(), None).unwrap(); + let query_res = + query_collateral_price(deps.as_ref(), mock_env(), "lunax0000".to_string(), None).unwrap(); assert_eq!( query_res, CollateralPriceResponse { @@ -582,9 +516,6 @@ fn get_native_price() { owner: "owner0000".to_string(), mint_contract: "mint0000".to_string(), base_denom: "uusd".to_string(), - mirror_oracle: "mirrororacle0000".to_string(), - anchor_oracle: "anchororacle0000".to_string(), - band_oracle: "bandoracle0000".to_string(), }; let info = mock_info("addr0000", &[]); @@ -604,7 +535,8 @@ fn get_native_price() { let _res = execute(deps.as_mut(), mock_env(), info, msg).unwrap(); // attempt to query price - let query_res = query_collateral_price(deps.as_ref(), "uluna".to_string(), None).unwrap(); + let query_res = + query_collateral_price(deps.as_ref(), mock_env(), "uluna".to_string(), None).unwrap(); assert_eq!( query_res, CollateralPriceResponse { @@ -625,9 +557,6 @@ fn revoke_collateral() { owner: "owner0000".to_string(), mint_contract: "mint0000".to_string(), base_denom: "uusd".to_string(), - mirror_oracle: "mirrororacle0000".to_string(), - anchor_oracle: "anchororacle0000".to_string(), - band_oracle: "bandoracle0000".to_string(), }; let info = mock_info("addr0000", &[]); @@ -647,7 +576,8 @@ fn revoke_collateral() { let _res = execute(deps.as_mut(), mock_env(), info, msg).unwrap(); // attempt to query price - let query_res = query_collateral_price(deps.as_ref(), "aUST".to_string(), None).unwrap(); + let query_res = + query_collateral_price(deps.as_ref(), mock_env(), "aUST".to_string(), None).unwrap(); assert_eq!( query_res, CollateralPriceResponse { @@ -689,7 +619,8 @@ fn revoke_collateral() { ); // attempt to query price of revoked asset - let query_res = query_collateral_price(deps.as_ref(), "aUST".to_string(), None).unwrap(); + let query_res = + query_collateral_price(deps.as_ref(), mock_env(), "aUST".to_string(), None).unwrap(); assert_eq!( query_res, CollateralPriceResponse { diff --git a/contracts/mirror_collector/Cargo.toml b/contracts/mirror_collector/Cargo.toml index 1f8fb159..27bb62bf 100644 --- a/contracts/mirror_collector/Cargo.toml +++ b/contracts/mirror_collector/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "mirror-collector" -version = "2.1.3" +version = "2.2.0" authors = ["Terraform Labs, PTE."] edition = "2018" description = "A Collector contract for Mirror Protocol - collect all swap rewards and send it to staking contract" @@ -37,7 +37,7 @@ backtraces = ["cosmwasm-std/backtraces"] cw20 = { version = "0.8.0" } cosmwasm-std = { version = "0.16.0" } cosmwasm-storage = { version = "0.16.0" } -mirror-protocol = { version = "2.1.1", path = "../../packages/mirror_protocol" } +mirror-protocol = { version = "2.2.0", path = "../../packages/mirror_protocol" } schemars = "0.8.1" serde = { version = "1.0.103", default-features = false, features = ["derive"] } terra-cosmwasm = "2.2.0" diff --git a/contracts/mirror_community/Cargo.toml b/contracts/mirror_community/Cargo.toml index a4983ec2..a8520821 100644 --- a/contracts/mirror_community/Cargo.toml +++ b/contracts/mirror_community/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "mirror-community" -version = "2.1.1" +version = "2.2.0" authors = ["Terraform Labs, PTE."] edition = "2018" description = "A Community contract for Mirror Protocol - Keeps premined MIR token which can be used through Gov proposal" @@ -37,7 +37,7 @@ backtraces = ["cosmwasm-std/backtraces"] cw20 = { version = "0.8.0" } cosmwasm-std = { version = "0.16.0" } cosmwasm-storage = { version = "0.16.0" } -mirror-protocol = { version = "2.1.1", path = "../../packages/mirror_protocol" } +mirror-protocol = { version = "2.2.0", path = "../../packages/mirror_protocol" } schemars = "0.8.1" serde = { version = "1.0.103", default-features = false, features = ["derive"] } diff --git a/contracts/mirror_factory/Cargo.toml b/contracts/mirror_factory/Cargo.toml index 957e989a..b9c12456 100644 --- a/contracts/mirror_factory/Cargo.toml +++ b/contracts/mirror_factory/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "mirror-factory" -version = "2.1.1" +version = "2.2.0" authors = ["Terraform Labs, PTE."] edition = "2018" description = "A Factory contract for Mirror Protocol - mint mirror token and distribute it to registered staking contracts" @@ -37,7 +37,8 @@ backtraces = ["cosmwasm-std/backtraces"] cw20 = { version = "0.8.0" } cosmwasm-std = { version = "0.16.0", features = ["iterator"] } cosmwasm-storage = { version = "0.16.0", features = ["iterator"] } -mirror-protocol = { version = "2.1.1", path = "../../packages/mirror_protocol" } +mirror-protocol = { version = "2.2.0", path = "../../packages/mirror_protocol" } +tefi-oracle = "0.2.0" protobuf = { version = "2", features = ["with-bytes"] } terraswap = "2.4.0" schemars = "0.8.1" diff --git a/contracts/mirror_factory/schema/execute_msg.json b/contracts/mirror_factory/schema/execute_msg.json index e9c732c3..dfb93b94 100644 --- a/contracts/mirror_factory/schema/execute_msg.json +++ b/contracts/mirror_factory/schema/execute_msg.json @@ -1,7 +1,7 @@ { "$schema": "http://json-schema.org/draft-07/schema#", "title": "ExecuteMsg", - "anyOf": [ + "oneOf": [ { "description": "Owner Operations", "type": "object", @@ -137,7 +137,7 @@ "type": "object", "required": [ "name", - "oracle_feeder", + "oracle_proxy", "params", "symbol" ], @@ -146,8 +146,8 @@ "description": "asset name used to create token contract", "type": "string" }, - "oracle_feeder": { - "description": "authorized asset oracle feeder", + "oracle_proxy": { + "description": "oracle proxy that will provide prices for this asset", "type": "string" }, "params": { @@ -192,7 +192,7 @@ "additionalProperties": false }, { - "description": "Feeder Operations ////////////////// Revoke asset from MIR rewards pool and register end_price to mint contract Only feeder can set end_price", + "description": "Revoke asset from MIR rewards pool and register end_price to mint contract", "type": "object", "required": [ "revoke_asset" @@ -206,16 +206,6 @@ "properties": { "asset_token": { "type": "string" - }, - "end_price": { - "anyOf": [ - { - "$ref": "#/definitions/Decimal" - }, - { - "type": "null" - } - ] } } } @@ -232,21 +222,21 @@ "migrate_asset": { "type": "object", "required": [ - "end_price", "from_token", "name", + "oracle_proxy", "symbol" ], "properties": { - "end_price": { - "$ref": "#/definitions/Decimal" - }, "from_token": { "type": "string" }, "name": { "type": "string" }, + "oracle_proxy": { + "type": "string" + }, "symbol": { "type": "string" } @@ -293,6 +283,13 @@ } ] }, + "ipo_trigger_addr": { + "description": "For pre-IPO assets, address authorized to trigger the ipo event", + "type": [ + "string", + "null" + ] + }, "min_collateral_ratio": { "description": "Minium collateral ratio applied to asset mint", "allOf": [ diff --git a/contracts/mirror_factory/schema/query_msg.json b/contracts/mirror_factory/schema/query_msg.json index c0e92ad6..2fefe307 100644 --- a/contracts/mirror_factory/schema/query_msg.json +++ b/contracts/mirror_factory/schema/query_msg.json @@ -1,7 +1,7 @@ { "$schema": "http://json-schema.org/draft-07/schema#", "title": "QueryMsg", - "anyOf": [ + "oneOf": [ { "type": "object", "required": [ diff --git a/contracts/mirror_factory/src/contract.rs b/contracts/mirror_factory/src/contract.rs index 750701ee..f5fefb99 100644 --- a/contracts/mirror_factory/src/contract.rs +++ b/contracts/mirror_factory/src/contract.rs @@ -6,13 +6,14 @@ use cosmwasm_std::{ Env, MessageInfo, Reply, ReplyOn, Response, StdError, StdResult, SubMsg, Uint128, WasmMsg, }; -use crate::querier::{load_mint_asset_config, load_oracle_feeder, query_last_price}; +use crate::querier::{load_mint_asset_config, query_last_price}; use crate::response::MsgInstantiateContractResponse; use crate::state::{ decrease_total_weight, increase_total_weight, read_all_weight, read_config, - read_last_distributed, read_params, read_tmp_asset, read_tmp_oracle, read_total_weight, - read_weight, remove_params, remove_weight, store_config, store_last_distributed, store_params, - store_tmp_asset, store_tmp_oracle, store_total_weight, store_weight, Config, + read_last_distributed, read_tmp_asset, read_tmp_whitelist_info, read_total_weight, read_weight, + remove_tmp_whitelist_info, remove_weight, store_config, store_last_distributed, + store_tmp_asset, store_tmp_whitelist_info, store_total_weight, store_weight, Config, + WhitelistTmpInfo, }; use mirror_protocol::factory::{ @@ -20,9 +21,9 @@ use mirror_protocol::factory::{ QueryMsg, }; use mirror_protocol::mint::{ExecuteMsg as MintExecuteMsg, IPOParams}; -use mirror_protocol::oracle::ExecuteMsg as OracleExecuteMsg; use mirror_protocol::staking::Cw20HookMsg as StakingCw20HookMsg; use mirror_protocol::staking::ExecuteMsg as StakingExecuteMsg; +use tefi_oracle::hub::HubExecuteMsg as TeFiOracleExecuteMsg; use protobuf::Message; @@ -100,23 +101,20 @@ pub fn execute(deps: DepsMut, env: Env, info: MessageInfo, msg: ExecuteMsg) -> S ExecuteMsg::Whitelist { name, symbol, - oracle_feeder, + oracle_proxy, params, - } => whitelist(deps, info, name, symbol, oracle_feeder, params), + } => whitelist(deps, info, name, symbol, oracle_proxy, params), ExecuteMsg::Distribute {} => distribute(deps, env), ExecuteMsg::PassCommand { contract_addr, msg } => { pass_command(deps, info, contract_addr, msg) } - ExecuteMsg::RevokeAsset { - asset_token, - end_price, - } => revoke_asset(deps, info, asset_token, end_price), + ExecuteMsg::RevokeAsset { asset_token } => revoke_asset(deps, info, asset_token), ExecuteMsg::MigrateAsset { name, symbol, from_token, - end_price, - } => migrate_asset(deps, info, name, symbol, from_token, end_price), + oracle_proxy, + } => migrate_asset(deps, info, name, symbol, from_token, oracle_proxy), } } @@ -237,7 +235,7 @@ pub fn pass_command( /// 2. Call `TokenCreationHook` /// 2-1. Initialize distribution info /// 2-2. Register asset to mint contract -/// 2-3. Register asset and oracle feeder to oracle contract +/// 2-3. Register asset and oracle proxy to oracle contract /// 2-4. Create terraswap pair through terraswap factory /// 3. Call `TerraswapCreationHook` /// 3-1. Register asset to staking contract @@ -246,7 +244,7 @@ pub fn whitelist( info: MessageInfo, name: String, symbol: String, - oracle_feeder: String, + oracle_proxy: String, params: Params, ) -> StdResult { let config: Config = read_config(deps.storage)?; @@ -254,15 +252,23 @@ pub fn whitelist( return Err(StdError::generic_err("unauthorized")); } - if read_params(deps.storage).is_ok() { + if read_tmp_whitelist_info(deps.storage).is_ok() { + // this error should never happen return Err(StdError::generic_err("A whitelist process is in progress")); } - store_params(deps.storage, ¶ms)?; + // checks format and returns uppercase + let symbol = format_symbol(&symbol)?; + let cw20_symbol = format!("m{}", symbol); - // store oracle in temp storage to use in reply callback - let oracle = deps.api.addr_validate(&oracle_feeder)?; - store_tmp_oracle(deps.storage, &oracle)?; + store_tmp_whitelist_info( + deps.storage, + &WhitelistTmpInfo { + params, + oracle_proxy: deps.api.addr_canonicalize(&oracle_proxy)?, // validates and converts + symbol: symbol.to_string(), + }, + )?; Ok(Response::new() .add_submessage(SubMsg { @@ -274,7 +280,7 @@ pub fn whitelist( label: "".to_string(), msg: to_binary(&TokenInstantiateMsg { name: name.clone(), - symbol: symbol.to_string(), + symbol: cw20_symbol.to_string(), decimals: 6u8, initial_balances: vec![], mint: Some(MinterResponse { @@ -291,6 +297,7 @@ pub fn whitelist( .add_attributes(vec![ attr("action", "whitelist"), attr("symbol", symbol), + attr("cw20_symbol", cw20_symbol), attr("name", name), ])) } @@ -299,8 +306,8 @@ pub fn whitelist( pub fn reply(deps: DepsMut, env: Env, msg: Reply) -> StdResult { match msg.id { 1 => { - // fetch saved oracle_feeder from temp state - let oracle_feeder = read_tmp_oracle(deps.storage)?; + // fetch tmp whitelist info + let whitelist_info = read_tmp_whitelist_info(deps.storage)?; // get new token's contract address let res: MsgInstantiateContractResponse = Message::parse_from_bytes( @@ -311,7 +318,7 @@ pub fn reply(deps: DepsMut, env: Env, msg: Reply) -> StdResult { })?; let asset_token = Addr::unchecked(res.get_contract_address()); - token_creation_hook(deps, env, asset_token, oracle_feeder) + token_creation_hook(deps, env, asset_token, whitelist_info) } 2 => { // fetch saved asset_token from temp state @@ -326,23 +333,17 @@ pub fn reply(deps: DepsMut, env: Env, msg: Reply) -> StdResult { /// TokenCreationHook /// 1. Initialize distribution info /// 2. Register asset to mint contract -/// 3. Register asset and oracle feeder to oracle contract +/// 3. Register asset and oracle proxy to oracle hub contract /// 4. Create terraswap pair through terraswap factory with `TerraswapCreationHook` pub fn token_creation_hook( deps: DepsMut, env: Env, asset_token: Addr, - oracle_feeder: Addr, + whitelist_info: WhitelistTmpInfo, ) -> StdResult { let config: Config = read_config(deps.storage)?; - - // If the param is not exists, it means there is no whitelist process in progress - let params: Params = match read_params(deps.storage) { - Ok(v) => v, - Err(_) => return Err(StdError::generic_err("No whitelist process in progress")), - }; - let asset_token_raw = deps.api.addr_canonicalize(asset_token.as_str())?; + let params = whitelist_info.params; // If weight is given as params, we use that or just use default let weight = if let Some(weight) = params.weight { @@ -355,43 +356,51 @@ pub fn token_creation_hook( store_weight(deps.storage, &asset_token_raw, weight)?; increase_total_weight(deps.storage, weight)?; - // Remove params == clear flag - remove_params(deps.storage); + // Remove tmp info + remove_tmp_whitelist_info(deps.storage); let mut attributes: Vec = vec![]; // Check if all IPO params exist - let ipo_params: Option = - if let (Some(mint_period), Some(min_collateral_ratio_after_ipo), Some(pre_ipo_price)) = ( - params.mint_period, - params.min_collateral_ratio_after_ipo, - params.pre_ipo_price, - ) { - let mint_end: u64 = env.block.time.plus_seconds(mint_period).seconds(); - attributes = vec![ - attr("is_pre_ipo", "true"), - attr("mint_end", mint_end.to_string()), - attr( - "min_collateral_ratio_after_ipo", - min_collateral_ratio_after_ipo.to_string(), - ), - attr("pre_ipo_price", pre_ipo_price.to_string()), - ]; - Some(IPOParams { - mint_end, - pre_ipo_price, - min_collateral_ratio_after_ipo, - }) - } else { - attributes.push(attr("is_pre_ipo", "false")); - None - }; + let ipo_params: Option = if let ( + Some(mint_period), + Some(min_collateral_ratio_after_ipo), + Some(pre_ipo_price), + Some(trigger_addr), + ) = ( + params.mint_period, + params.min_collateral_ratio_after_ipo, + params.pre_ipo_price, + params.ipo_trigger_addr, + ) { + let mint_end: u64 = env.block.time.plus_seconds(mint_period).seconds(); + attributes = vec![ + attr("is_pre_ipo", "true"), + attr("mint_end", mint_end.to_string()), + attr( + "min_collateral_ratio_after_ipo", + min_collateral_ratio_after_ipo.to_string(), + ), + attr("pre_ipo_price", pre_ipo_price.to_string()), + attr("ipo_trigger_addr", trigger_addr.to_string()), + ]; + Some(IPOParams { + mint_end, + pre_ipo_price, + min_collateral_ratio_after_ipo, + trigger_addr, + }) + } else { + attributes.push(attr("is_pre_ipo", "false")); + None + }; // store asset_token in temp storage to use in reply callback store_tmp_asset(deps.storage, &asset_token)?; // Register asset to mint contract - // Register asset to oracle contract + // Register price source to oracle contract + // Register asset mapping to oracle contract // Create terraswap pair Ok(Response::new() .add_messages(vec![ @@ -408,9 +417,22 @@ pub fn token_creation_hook( CosmosMsg::Wasm(WasmMsg::Execute { contract_addr: deps.api.addr_humanize(&config.oracle_contract)?.to_string(), funds: vec![], - msg: to_binary(&OracleExecuteMsg::RegisterAsset { - asset_token: asset_token.to_string(), - feeder: oracle_feeder.to_string(), + msg: to_binary(&TeFiOracleExecuteMsg::RegisterSource { + // if the source already exists, will skip and return gracefully + symbol: whitelist_info.symbol.to_string(), + proxy_addr: deps + .api + .addr_humanize(&whitelist_info.oracle_proxy)? + .to_string(), + priority: None, // default priority + })?, + }), + CosmosMsg::Wasm(WasmMsg::Execute { + contract_addr: deps.api.addr_humanize(&config.oracle_contract)?.to_string(), + funds: vec![], + msg: to_binary(&TeFiOracleExecuteMsg::InsertAssetSymbolMap { + // map asset_token to symbol on oracle + map: vec![(asset_token.to_string(), whitelist_info.symbol)], })?, }), ]) @@ -543,7 +565,7 @@ pub fn distribute(deps: DepsMut, env: Env) -> StdResult { // store last distributed store_last_distributed(deps.storage, env.block.time.seconds())?; - // mint token to self and try send minted tokens to staking contract + // send token rewards to staking contract const SPLIT_UNIT: usize = 10; Ok(Response::new() .add_messages( @@ -570,52 +592,25 @@ pub fn distribute(deps: DepsMut, env: Env) -> StdResult { ])) } -pub fn revoke_asset( - deps: DepsMut, - info: MessageInfo, - asset_token: String, - end_price: Option, -) -> StdResult { +pub fn revoke_asset(deps: DepsMut, info: MessageInfo, asset_token: String) -> StdResult { let config: Config = read_config(deps.storage)?; let asset_token_raw: CanonicalAddr = deps.api.addr_canonicalize(&asset_token)?; let sender_raw: CanonicalAddr = deps.api.addr_canonicalize(info.sender.as_str())?; let mint: Addr = deps.api.addr_humanize(&config.mint_contract)?; let oracle: Addr = deps.api.addr_humanize(&config.oracle_contract)?; - let end_price: Decimal = match end_price { - Some(value) => { - // only feeder can revoke_asset with end_price - let oracle_feeder: Addr = deps.api.addr_humanize(&load_oracle_feeder( - &deps.querier, - deps.api.addr_humanize(&config.oracle_contract)?, - &asset_token_raw, - )?)?; - if oracle_feeder != info.sender { - return Err(StdError::generic_err("unauthorized")); - } + // only owner can revoke asset + if config.owner != sender_raw { + return Err(StdError::generic_err("unauthorized")); + } - value - } - None => { - // revoke asset without end_price can be called by owner - if config.owner != sender_raw { - return Err(StdError::generic_err("unauthorized")); - } + // check if the asset has a preIPO price + let (_, _, pre_ipo_price) = load_mint_asset_config(&deps.querier, mint, &asset_token_raw)?; - // check if the asset has a preIPO price - let (_, _, pre_ipo_price) = - load_mint_asset_config(&deps.querier, mint, &asset_token_raw)?; - pre_ipo_price.unwrap_or( - // if there is no pre_ipo_price, fetch last reported price from oracle - query_last_price( - &deps.querier, - oracle, - asset_token.to_string(), - config.base_denom, - )?, - ) - } - }; + let end_price: Decimal = pre_ipo_price.unwrap_or( + // if there is no pre_ipo_price, fetch last reported price from oracle + query_last_price(&deps.querier, oracle, asset_token.to_string())?, + ); let weight = read_weight(deps.storage, &asset_token_raw)?; remove_weight(deps.storage, &asset_token_raw); @@ -643,46 +638,57 @@ pub fn migrate_asset( name: String, symbol: String, asset_token: String, - end_price: Decimal, + oracle_proxy: String, ) -> StdResult { let config: Config = read_config(deps.storage)?; let asset_token_raw: CanonicalAddr = deps.api.addr_canonicalize(&asset_token)?; - let oracle_feeder: Addr = deps.api.addr_humanize(&load_oracle_feeder( - &deps.querier, - deps.api.addr_humanize(&config.oracle_contract)?, - &asset_token_raw, - )?)?; + let sender_raw: CanonicalAddr = deps.api.addr_canonicalize(info.sender.as_str())?; + let oracle_proxy_raw: CanonicalAddr = deps.api.addr_canonicalize(&oracle_proxy)?; + let mint: Addr = deps.api.addr_humanize(&config.mint_contract)?; + let oracle: Addr = deps.api.addr_humanize(&config.oracle_contract)?; - if oracle_feeder != info.sender { + if sender_raw != config.owner { return Err(StdError::generic_err("unauthorized")); } + // check if the asset has a preIPO price + let (auction_discount, min_collateral_ratio, pre_ipo_price) = + load_mint_asset_config(&deps.querier, mint.clone(), &asset_token_raw)?; + + if pre_ipo_price.is_some() { + return Err(StdError::generic_err("Can not migrate a preIPO asset")); + } + + let end_price = query_last_price(&deps.querier, oracle, asset_token.to_string())?; + let weight = read_weight(deps.storage, &asset_token_raw)?; remove_weight(deps.storage, &asset_token_raw); decrease_total_weight(deps.storage, weight)?; - let mint_contract = deps.api.addr_humanize(&config.mint_contract)?; - let mint_config: (Decimal, Decimal, _) = - load_mint_asset_config(&deps.querier, mint_contract.clone(), &asset_token_raw)?; + // checks format and returns uppercase + let symbol = format_symbol(&symbol)?; + let cw20_symbol = format!("m{}", symbol); - store_params( + store_tmp_whitelist_info( deps.storage, - &Params { - auction_discount: mint_config.0, - min_collateral_ratio: mint_config.1, - weight: Some(weight), - mint_period: None, - min_collateral_ratio_after_ipo: None, - pre_ipo_price: None, + &WhitelistTmpInfo { + params: Params { + auction_discount, + min_collateral_ratio, + weight: Some(weight), + mint_period: None, + min_collateral_ratio_after_ipo: None, + pre_ipo_price: None, + ipo_trigger_addr: None, + }, + symbol, + oracle_proxy: oracle_proxy_raw, }, )?; - // store oracle in temp storage to use in reply callback - store_tmp_oracle(deps.storage, &oracle_feeder)?; - Ok(Response::new() .add_message(CosmosMsg::Wasm(WasmMsg::Execute { - contract_addr: mint_contract.to_string(), + contract_addr: mint.to_string(), funds: vec![], msg: to_binary(&MintExecuteMsg::RegisterMigration { asset_token: asset_token.clone(), @@ -698,11 +704,11 @@ pub fn migrate_asset( label: "".to_string(), msg: to_binary(&TokenInstantiateMsg { name, - symbol, + symbol: cw20_symbol, decimals: 6u8, initial_balances: vec![], mint: Some(MinterResponse { - minter: deps.api.addr_humanize(&config.mint_contract)?.to_string(), + minter: mint.to_string(), cap: None, }), })?, @@ -767,6 +773,42 @@ pub fn query_distribution_info(deps: Deps) -> StdResult StdResult { +pub fn migrate(deps: DepsMut, _env: Env, msg: MigrateMsg) -> StdResult { + // change oracle address to point to new tefi hub + let mut config: Config = read_config(deps.storage)?; + config.oracle_contract = deps.api.addr_canonicalize(&msg.tefi_oracle_contract)?; + store_config(deps.storage, &config)?; + Ok(Response::default()) } + +fn format_symbol(symbol: &str) -> StdResult { + let first_char = symbol + .chars() + .next() + .ok_or_else(|| StdError::generic_err("invalid symbol format"))?; + if first_char == 'm' { + return Err(StdError::generic_err("symbol should not start with 'm'")); + } + + Ok(symbol.to_uppercase()) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_format_masset_symbol() { + format_symbol("mAAPL").unwrap_err(); + format_symbol("mTSLA").unwrap_err(); + + assert_eq!(format_symbol("aAPL").unwrap(), "AAPL".to_string(),); + assert_eq!( + format_symbol("MSFT").unwrap(), // starts with 'M' not 'm' + "MSFT".to_string(), + ); + assert_eq!(format_symbol("tsla").unwrap(), "TSLA".to_string(),); + assert_eq!(format_symbol("ANC").unwrap(), "ANC".to_string(),) + } +} diff --git a/contracts/mirror_factory/src/querier.rs b/contracts/mirror_factory/src/querier.rs index 99f7f98c..f840ff23 100644 --- a/contracts/mirror_factory/src/querier.rs +++ b/contracts/mirror_factory/src/querier.rs @@ -5,44 +5,20 @@ use cosmwasm_std::{ use cosmwasm_storage::to_length_prefixed; use mirror_protocol::mint::IPOParams; -use mirror_protocol::oracle::{PriceResponse, QueryMsg as OracleQueryMsg}; use serde::{Deserialize, Serialize}; - -pub fn load_oracle_feeder( - querier: &QuerierWrapper, - contract_addr: Addr, - asset_token: &CanonicalAddr, -) -> StdResult { - let res: StdResult = querier.query(&QueryRequest::Wasm(WasmQuery::Raw { - contract_addr: contract_addr.to_string(), - key: Binary::from(concat( - &to_length_prefixed(b"feeder"), - asset_token.as_slice(), - )), - })); - - let feeder: CanonicalAddr = match res { - Ok(v) => v, - Err(_) => { - return Err(StdError::generic_err("Failed to fetch the oracle feeder")); - } - }; - - Ok(feeder) -} +use tefi_oracle::hub::{HubQueryMsg as OracleQueryMsg, PriceResponse}; /// Query asset price igonoring price age pub fn query_last_price( querier: &QuerierWrapper, oracle: Addr, - base_asset: String, - quote_asset: String, + asset: String, ) -> StdResult { let res: PriceResponse = querier.query(&QueryRequest::Wasm(WasmQuery::Smart { contract_addr: oracle.to_string(), msg: to_binary(&OracleQueryMsg::Price { - base_asset, - quote_asset, + asset_token: asset, + timeframe: None, })?, }))?; diff --git a/contracts/mirror_factory/src/state.rs b/contracts/mirror_factory/src/state.rs index 891bc901..3cd09614 100644 --- a/contracts/mirror_factory/src/state.rs +++ b/contracts/mirror_factory/src/state.rs @@ -7,10 +7,9 @@ use cosmwasm_storage::{singleton, singleton_read, Bucket, ReadonlyBucket, Single use mirror_protocol::factory::Params; static KEY_CONFIG: &[u8] = b"config"; -static KEY_PARAMS: &[u8] = b"params"; static KEY_TOTAL_WEIGHT: &[u8] = b"total_weight"; static KEY_LAST_DISTRIBUTED: &[u8] = b"last_distributed"; -static KEY_TMP_ORACLE: &[u8] = b"tmp_oracle_feeder"; +static KEY_WHITELIST_TMP_INFO: &[u8] = b"tmp_whitelist_info"; static KEY_TMP_ASSET: &[u8] = b"tmp_asset_token"; static PREFIX_WEIGHT: &[u8] = b"weight"; @@ -30,12 +29,11 @@ pub struct Config { pub distribution_schedule: Vec<(u64, u64, Uint128)>, // [[start_time, end_time, distribution_amount], [], ...] } -pub fn store_tmp_oracle(storage: &mut dyn Storage, tmp_oracle: &Addr) -> StdResult<()> { - singleton(storage, KEY_TMP_ORACLE).save(tmp_oracle) -} - -pub fn read_tmp_oracle(storage: &dyn Storage) -> StdResult { - singleton_read(storage, KEY_TMP_ORACLE).load() +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] +pub struct WhitelistTmpInfo { + pub params: Params, + pub oracle_proxy: CanonicalAddr, + pub symbol: String, } pub fn store_tmp_asset(storage: &mut dyn Storage, tmp_asset: &Addr) -> StdResult<()> { @@ -54,17 +52,20 @@ pub fn read_config(storage: &dyn Storage) -> StdResult { singleton_read(storage, KEY_CONFIG).load() } -pub fn store_params(storage: &mut dyn Storage, init_data: &Params) -> StdResult<()> { - singleton(storage, KEY_PARAMS).save(init_data) +pub fn store_tmp_whitelist_info( + storage: &mut dyn Storage, + info: &WhitelistTmpInfo, +) -> StdResult<()> { + singleton(storage, KEY_WHITELIST_TMP_INFO).save(info) } -pub fn remove_params(storage: &mut dyn Storage) { - let mut store: Singleton = singleton(storage, KEY_PARAMS); +pub fn remove_tmp_whitelist_info(storage: &mut dyn Storage) { + let mut store: Singleton = singleton(storage, KEY_WHITELIST_TMP_INFO); store.remove() } -pub fn read_params(storage: &dyn Storage) -> StdResult { - singleton_read(storage, KEY_PARAMS).load() +pub fn read_tmp_whitelist_info(storage: &dyn Storage) -> StdResult { + singleton_read(storage, KEY_WHITELIST_TMP_INFO).load() } pub fn store_total_weight(storage: &mut dyn Storage, total_weight: u32) -> StdResult<()> { diff --git a/contracts/mirror_factory/src/testing/mock_querier.rs b/contracts/mirror_factory/src/testing/mock_querier.rs index dcd39123..61a99db4 100644 --- a/contracts/mirror_factory/src/testing/mock_querier.rs +++ b/contracts/mirror_factory/src/testing/mock_querier.rs @@ -8,10 +8,9 @@ use cosmwasm_std::{ }; use cosmwasm_storage::to_length_prefixed; -use crate::math::decimal_division; use crate::querier::MintAssetConfig; -use mirror_protocol::oracle::PriceResponse; use std::collections::HashMap; +use tefi_oracle::hub::PriceResponse; use terraswap::asset::{AssetInfo, PairInfo}; /// mock_dependencies is a drop-in replacement for cosmwasm_std::testing::mock_dependencies @@ -32,7 +31,6 @@ pub fn mock_dependencies( pub struct WasmMockQuerier { base: MockQuerier, terraswap_factory_querier: TerraswapFactoryQuerier, - oracle_querier: OracleQuerier, oracle_price_querier: OraclePriceQuerier, mint_querier: MintQuerier, } @@ -83,27 +81,6 @@ pub(crate) fn oracle_price_to_map( oracle_price_map } -#[derive(Clone, Default)] -pub struct OracleQuerier { - feeders: HashMap, -} - -impl OracleQuerier { - pub fn new(feeders: &[(&String, &String)]) -> Self { - OracleQuerier { - feeders: address_pair_to_map(feeders), - } - } -} - -pub(crate) fn address_pair_to_map(address_pair: &[(&String, &String)]) -> HashMap { - let mut address_pair_map: HashMap = HashMap::new(); - for (addr1, addr2) in address_pair.iter() { - address_pair_map.insert(addr1.to_string(), addr2.to_string()); - } - address_pair_map -} - #[derive(Clone, Default)] pub struct MintQuerier { configs: HashMap, @@ -150,8 +127,8 @@ pub enum QueryMsg { asset_infos: [AssetInfo; 2], }, Price { - base_asset: String, - quote_asset: String, + asset_token: String, + timeframe: Option, }, } @@ -184,23 +161,14 @@ impl WasmMockQuerier { } } QueryMsg::Price { - base_asset, - quote_asset, - } => match self.oracle_price_querier.oracle_price.get(&base_asset) { + asset_token, + timeframe: _, + } => match self.oracle_price_querier.oracle_price.get(&asset_token) { Some(base_price) => { - match self.oracle_price_querier.oracle_price.get("e_asset) { - Some(quote_price) => { - SystemResult::Ok(ContractResult::from(to_binary(&PriceResponse { - rate: decimal_division(*base_price, *quote_price), - last_updated_base: 1000u64, - last_updated_quote: 1000u64, - }))) - } - None => SystemResult::Err(SystemError::InvalidRequest { - error: "No oracle price exists".to_string(), - request: msg.as_slice().into(), - }), - } + SystemResult::Ok(ContractResult::from(to_binary(&PriceResponse { + rate: *base_price, + last_updated: 1000u64, + }))) } None => SystemResult::Err(SystemError::InvalidRequest { error: "No oracle price exists".to_string(), @@ -208,44 +176,15 @@ impl WasmMockQuerier { }), }, }, - QueryRequest::Wasm(WasmQuery::Raw { contract_addr, key }) => { + QueryRequest::Wasm(WasmQuery::Raw { + contract_addr: _, + key, + }) => { let key: &[u8] = key.as_slice(); let prefix_asset_config = to_length_prefixed(b"asset_config").to_vec(); - let prefix_feeder = to_length_prefixed(b"feeder").to_vec(); let api: MockApi = MockApi::default(); - if key.len() > prefix_feeder.len() - && key[..prefix_feeder.len()].to_vec() == prefix_feeder - { - let api: MockApi = MockApi::default(); - let rest_key: &[u8] = &key[prefix_feeder.len()..]; - - if contract_addr == "oracle0000" { - let asset_token: String = api - .addr_humanize(&(CanonicalAddr::from(rest_key.to_vec()))) - .unwrap() - .to_string(); - - let feeder = match self.oracle_querier.feeders.get(&asset_token) { - Some(v) => v, - None => { - return SystemResult::Err(SystemError::InvalidRequest { - error: format!( - "Oracle Feeder is not found for {}", - asset_token - ), - request: key.into(), - }) - } - }; - - SystemResult::Ok(ContractResult::from(to_binary( - &api.addr_canonicalize(feeder).unwrap(), - ))) - } else { - panic!("DO NOT ENTER HERE") - } - } else if key.len() > prefix_asset_config.len() + if key.len() > prefix_asset_config.len() && key[..prefix_asset_config.len()].to_vec() == prefix_asset_config { let rest_key: &[u8] = &key[prefix_asset_config.len()..]; @@ -285,7 +224,6 @@ impl WasmMockQuerier { base, terraswap_factory_querier: TerraswapFactoryQuerier::default(), mint_querier: MintQuerier::default(), - oracle_querier: OracleQuerier::default(), oracle_price_querier: OraclePriceQuerier::default(), } } @@ -295,10 +233,6 @@ impl WasmMockQuerier { self.terraswap_factory_querier = TerraswapFactoryQuerier::new(pairs); } - pub fn with_oracle_feeders(&mut self, feeders: &[(&String, &String)]) { - self.oracle_querier = OracleQuerier::new(feeders); - } - pub fn with_mint_configs(&mut self, configs: &[(&String, &(Decimal, Decimal))]) { self.mint_querier = MintQuerier::new(configs); } diff --git a/contracts/mirror_factory/src/testing/tests.rs b/contracts/mirror_factory/src/testing/tests.rs index ca83be79..9a91de0e 100644 --- a/contracts/mirror_factory/src/testing/tests.rs +++ b/contracts/mirror_factory/src/testing/tests.rs @@ -3,8 +3,8 @@ use crate::response::MsgInstantiateContractResponse; use crate::testing::mock_querier::{mock_dependencies, WasmMockQuerier}; use crate::state::{ - read_params, read_tmp_asset, read_tmp_oracle, read_total_weight, read_weight, - store_total_weight, store_weight, + read_tmp_asset, read_tmp_whitelist_info, read_total_weight, read_weight, store_total_weight, + store_weight, WhitelistTmpInfo, }; use cosmwasm_std::testing::{mock_env, mock_info, MockApi, MockStorage}; use cosmwasm_std::{ @@ -18,9 +18,9 @@ use mirror_protocol::factory::{ ConfigResponse, DistributionInfoResponse, ExecuteMsg, InstantiateMsg, Params, QueryMsg, }; use mirror_protocol::mint::{ExecuteMsg as MintExecuteMsg, IPOParams}; -use mirror_protocol::oracle::ExecuteMsg as OracleExecuteMsg; use mirror_protocol::staking::Cw20HookMsg as StakingCw20HookMsg; use mirror_protocol::staking::ExecuteMsg as StakingExecuteMsg; +use tefi_oracle::hub::HubExecuteMsg as TeFiOracleExecuteMsg; use protobuf::Message; @@ -293,8 +293,8 @@ fn test_whitelist() { let msg = ExecuteMsg::Whitelist { name: "apple derivative".to_string(), - symbol: "mAPPL".to_string(), - oracle_feeder: "feeder0000".to_string(), + symbol: "APPL".to_string(), + oracle_proxy: "oracleproxy0000".to_string(), params: Params { auction_discount: Decimal::percent(5), min_collateral_ratio: Decimal::percent(150), @@ -302,6 +302,7 @@ fn test_whitelist() { mint_period: None, min_collateral_ratio_after_ipo: None, pre_ipo_price: None, + ipo_trigger_addr: None, }, }; let info = mock_info("owner0000", &[]); @@ -311,7 +312,8 @@ fn test_whitelist() { res.attributes, vec![ attr("action", "whitelist"), - attr("symbol", "mAPPL"), + attr("symbol", "APPL"), + attr("cw20_symbol", "mAPPL"), attr("name", "apple derivative") ] ); @@ -344,16 +346,21 @@ fn test_whitelist() { }] ); - let params: Params = read_params(&deps.storage).unwrap(); + let wl_info: WhitelistTmpInfo = read_tmp_whitelist_info(&deps.storage).unwrap(); assert_eq!( - params, - Params { - auction_discount: Decimal::percent(5), - min_collateral_ratio: Decimal::percent(150), - weight: Some(100u32), - mint_period: None, - min_collateral_ratio_after_ipo: None, - pre_ipo_price: None, + wl_info, + WhitelistTmpInfo { + params: Params { + auction_discount: Decimal::percent(5), + min_collateral_ratio: Decimal::percent(150), + weight: Some(100u32), + mint_period: None, + min_collateral_ratio_after_ipo: None, + pre_ipo_price: None, + ipo_trigger_addr: None, + }, + oracle_proxy: deps.api.addr_canonicalize("oracleproxy0000").unwrap(), + symbol: "APPL".to_string(), } ); @@ -369,10 +376,6 @@ fn test_whitelist() { StdError::GenericErr { msg, .. } => assert_eq!(msg, "unauthorized"), _ => panic!("DO NOT ENTER HERE"), } - - //ensure temp oracle was stored - let tmp_oracle = read_tmp_oracle(&deps.storage).unwrap(); - assert_eq!(tmp_oracle.to_string(), "feeder0000"); } #[test] @@ -403,8 +406,8 @@ fn test_token_creation_hook() { let msg = ExecuteMsg::Whitelist { name: "apple derivative".to_string(), - symbol: "mAPPL".to_string(), - oracle_feeder: "feeder0000".to_string(), + symbol: "APPL".to_string(), + oracle_proxy: "oracleproxy0000".to_string(), params: Params { auction_discount: Decimal::percent(5), min_collateral_ratio: Decimal::percent(150), @@ -412,15 +415,12 @@ fn test_token_creation_hook() { mint_period: None, min_collateral_ratio_after_ipo: None, pre_ipo_price: None, + ipo_trigger_addr: None, }, }; let info = mock_info("owner0000", &[]); let _res = execute(deps.as_mut(), mock_env(), info, msg).unwrap(); - //ensure temp oracle was stored - let tmp_oracle = read_tmp_oracle(&deps.storage).unwrap(); - assert_eq!(tmp_oracle.to_string(), "feeder0000"); - let mut token_inst_res = MsgInstantiateContractResponse::new(); token_inst_res.set_contract_address("asset0000".to_string()); @@ -455,9 +455,18 @@ fn test_token_creation_hook() { SubMsg::new(CosmosMsg::Wasm(WasmMsg::Execute { contract_addr: "oracle0000".to_string(), funds: vec![], - msg: to_binary(&OracleExecuteMsg::RegisterAsset { - asset_token: "asset0000".to_string(), - feeder: "feeder0000".to_string(), + msg: to_binary(&TeFiOracleExecuteMsg::RegisterSource { + symbol: "APPL".to_string(), + proxy_addr: "oracleproxy0000".to_string(), + priority: None, + }) + .unwrap(), + })), + SubMsg::new(CosmosMsg::Wasm(WasmMsg::Execute { + contract_addr: "oracle0000".to_string(), + funds: vec![], + msg: to_binary(&TeFiOracleExecuteMsg::InsertAssetSymbolMap { + map: vec![("asset0000".to_string(), "APPL".to_string())] }) .unwrap(), })), @@ -535,8 +544,8 @@ fn test_token_creation_hook_without_weight() { let msg = ExecuteMsg::Whitelist { name: "apple derivative".to_string(), - symbol: "mAPPL".to_string(), - oracle_feeder: "feeder0000".to_string(), + symbol: "APPL".to_string(), + oracle_proxy: "oracleproxy0000".to_string(), params: Params { auction_discount: Decimal::percent(5), min_collateral_ratio: Decimal::percent(150), @@ -544,15 +553,12 @@ fn test_token_creation_hook_without_weight() { mint_period: None, min_collateral_ratio_after_ipo: None, pre_ipo_price: None, + ipo_trigger_addr: None, }, }; let info = mock_info("owner0000", &[]); let _res = execute(deps.as_mut(), mock_env(), info, msg).unwrap(); - //ensure temp oracle was stored - let tmp_oracle = read_tmp_oracle(&deps.storage).unwrap(); - assert_eq!(tmp_oracle.to_string(), "feeder0000"); - let mut token_inst_res = MsgInstantiateContractResponse::new(); token_inst_res.set_contract_address("asset0000".to_string()); @@ -587,9 +593,18 @@ fn test_token_creation_hook_without_weight() { SubMsg::new(CosmosMsg::Wasm(WasmMsg::Execute { contract_addr: "oracle0000".to_string(), funds: vec![], - msg: to_binary(&OracleExecuteMsg::RegisterAsset { - asset_token: "asset0000".to_string(), - feeder: "feeder0000".to_string(), + msg: to_binary(&TeFiOracleExecuteMsg::RegisterSource { + symbol: "APPL".to_string(), + proxy_addr: "oracleproxy0000".to_string(), + priority: None, + }) + .unwrap(), + })), + SubMsg::new(CosmosMsg::Wasm(WasmMsg::Execute { + contract_addr: "oracle0000".to_string(), + funds: vec![], + msg: to_binary(&TeFiOracleExecuteMsg::InsertAssetSymbolMap { + map: vec![("asset0000".to_string(), "APPL".to_string())] }) .unwrap(), })), @@ -661,8 +676,8 @@ fn test_terraswap_creation_hook() { let msg = ExecuteMsg::Whitelist { name: "apple derivative".to_string(), - symbol: "mAPPL".to_string(), - oracle_feeder: "feeder0000".to_string(), + symbol: "APPL".to_string(), + oracle_proxy: "oracleproxy0000".to_string(), params: Params { auction_discount: Decimal::percent(5), min_collateral_ratio: Decimal::percent(150), @@ -670,15 +685,12 @@ fn test_terraswap_creation_hook() { mint_period: None, min_collateral_ratio_after_ipo: None, pre_ipo_price: None, + ipo_trigger_addr: None, }, }; let info = mock_info("owner0000", &[]); let _res = execute(deps.as_mut(), mock_env(), info, msg).unwrap(); - //ensure temp oracle was stored - let tmp_oracle = read_tmp_oracle(&deps.storage).unwrap(); - assert_eq!(tmp_oracle.to_string(), "feeder0000"); - let mut token_inst_res = MsgInstantiateContractResponse::new(); token_inst_res.set_contract_address("asset0000".to_string()); @@ -755,8 +767,8 @@ fn test_distribute() { // whitelist first item with weight 1.5 let msg = ExecuteMsg::Whitelist { name: "apple derivative".to_string(), - symbol: "mAPPL".to_string(), - oracle_feeder: "feeder0000".to_string(), + symbol: "APPL".to_string(), + oracle_proxy: "oracleproxy0000".to_string(), params: Params { auction_discount: Decimal::percent(5), min_collateral_ratio: Decimal::percent(150), @@ -764,15 +776,12 @@ fn test_distribute() { mint_period: None, min_collateral_ratio_after_ipo: None, pre_ipo_price: None, + ipo_trigger_addr: None, }, }; let info = mock_info("owner0000", &[]); let _res = execute(deps.as_mut(), mock_env(), info, msg).unwrap(); - //ensure temp oracle was stored - let tmp_oracle = read_tmp_oracle(&deps.storage).unwrap(); - assert_eq!(tmp_oracle.to_string(), "feeder0000"); - let mut token_inst_res = MsgInstantiateContractResponse::new(); token_inst_res.set_contract_address("asset0000".to_string()); @@ -803,8 +812,8 @@ fn test_distribute() { // Whitelist second item with weight 1 let msg = ExecuteMsg::Whitelist { name: "google derivative".to_string(), - symbol: "mGOGL".to_string(), - oracle_feeder: "feeder0000".to_string(), + symbol: "GOGL".to_string(), + oracle_proxy: "oracleproxy0000".to_string(), params: Params { auction_discount: Decimal::percent(5), min_collateral_ratio: Decimal::percent(150), @@ -812,6 +821,7 @@ fn test_distribute() { mint_period: None, min_collateral_ratio_after_ipo: None, pre_ipo_price: None, + ipo_trigger_addr: None, }, }; let info = mock_info("owner0000", &[]); @@ -919,7 +929,7 @@ fn whitelist_token( let msg = ExecuteMsg::Whitelist { name: name.to_string(), symbol: symbol.to_string(), - oracle_feeder: "feeder0000".to_string(), + oracle_proxy: "oracleproxy0000".to_string(), params: Params { auction_discount: Decimal::percent(5), min_collateral_ratio: Decimal::percent(150), @@ -927,15 +937,12 @@ fn whitelist_token( mint_period: None, min_collateral_ratio_after_ipo: None, pre_ipo_price: None, + ipo_trigger_addr: None, }, }; let info = mock_info("owner0000", &[]); let _res = execute(deps.as_mut(), mock_env(), info, msg).unwrap(); - //ensure temp oracle was stored - let tmp_oracle = read_tmp_oracle(&deps.storage).unwrap(); - assert_eq!(tmp_oracle.to_string(), "feeder0000"); - // callback 1 let mut token_inst_res = MsgInstantiateContractResponse::new(); token_inst_res.set_contract_address(asset_token.to_string()); @@ -1205,13 +1212,19 @@ fn test_revocation() { (&"uusdmirror0000".to_string(), &"MIRLP000".to_string()), ]); deps.querier.with_oracle_price(&[ - (&"uusd".to_string(), &Decimal::one()), + (&"asset0000".to_string(), &Decimal::percent(100)), (&"asset0001".to_string(), &Decimal::percent(200)), ]); - deps.querier.with_mint_configs(&[( - &"asset0001".to_string(), - &(Decimal::percent(1), Decimal::percent(1)), - )]); + deps.querier.with_mint_configs(&[ + ( + &"asset0000".to_string(), + &(Decimal::percent(1), Decimal::percent(1)), + ), + ( + &"asset0001".to_string(), + &(Decimal::percent(1), Decimal::percent(1)), + ), + ]); let msg = InstantiateMsg { base_denom: BASE_DENOM.to_string(), @@ -1233,19 +1246,12 @@ fn test_revocation() { }; let _res = execute(deps.as_mut(), mock_env(), info, msg).unwrap(); - whitelist_token(&mut deps, "tesla derivative", "mTSLA", "asset0000", 100u32); - whitelist_token(&mut deps, "apple derivative", "mAPPL", "asset0001", 100u32); - - // register queriers - deps.querier.with_oracle_feeders(&[ - (&"asset0000".to_string(), &"feeder0000".to_string()), - (&"asset0001".to_string(), &"feeder0000".to_string()), - ]); + whitelist_token(&mut deps, "tesla derivative", "TSLA", "asset0000", 100u32); + whitelist_token(&mut deps, "apple derivative", "APPL", "asset0001", 100u32); // unauthorized revoke attempt let msg = ExecuteMsg::RevokeAsset { asset_token: "asset0000".to_string(), - end_price: Some(Decimal::from_ratio(2u128, 3u128)), }; let info = mock_info("address0000", &[]); let err = execute(deps.as_mut(), mock_env(), info, msg.clone()).unwrap_err(); @@ -1254,16 +1260,8 @@ fn test_revocation() { _ => panic!("DO NOT ENTER HERE"), } - // unatuthorized attemt 2, only owner can fix set price - let info = mock_info("addr0000", &[]); - let err = execute(deps.as_mut(), mock_env(), info, msg.clone()).unwrap_err(); - match err { - StdError::GenericErr { msg, .. } => assert_eq!(msg, "unauthorized"), - _ => panic!("DO NOT ENTER HERE"), - } - - // SUCCESS - the feeder revokes item 1 - let info = mock_info("feeder0000", &[]); + // SUCCESS - revoke item 1 + let info = mock_info("owner0000", &[]); let res = execute(deps.as_mut(), mock_env(), info, msg).unwrap(); assert_eq!( res.messages, @@ -1272,7 +1270,7 @@ fn test_revocation() { funds: vec![], msg: to_binary(&MintExecuteMsg::RegisterMigration { asset_token: "asset0000".to_string(), - end_price: Decimal::from_ratio(2u128, 3u128), + end_price: Decimal::percent(100), // last price feed }) .unwrap(), }))] @@ -1280,9 +1278,8 @@ fn test_revocation() { let msg = ExecuteMsg::RevokeAsset { asset_token: "asset0001".to_string(), - end_price: None, // owner can revoke without price feed }; - // SUCCESS - the owner revokes item 2 + // SUCCESS - revoke item 2 let info = mock_info("owner0000", &[]); let res = execute(deps.as_mut(), mock_env(), info, msg).unwrap(); assert_eq!( @@ -1327,7 +1324,7 @@ fn test_migration() { }; let _res = execute(deps.as_mut(), mock_env(), info, msg).unwrap(); - whitelist_token(&mut deps, "apple derivative", "mAPPL", "asset0000", 100u32); + whitelist_token(&mut deps, "apple derivative", "APPL", "asset0000", 100u32); // register queriers deps.querier.with_mint_configs(&[( @@ -1335,16 +1332,16 @@ fn test_migration() { &(Decimal::percent(1), Decimal::percent(1)), )]); deps.querier - .with_oracle_feeders(&[(&"asset0000".to_string(), &"feeder0000".to_string())]); + .with_oracle_price(&[(&"asset0000".to_string(), &Decimal::percent(300))]); // unauthorized migrate attempt let msg = ExecuteMsg::MigrateAsset { name: "apple migration".to_string(), - symbol: "mAPPL2".to_string(), + symbol: "APPL2".to_string(), from_token: "asset0000".to_string(), - end_price: Decimal::from_ratio(2u128, 1u128), + oracle_proxy: "oracleproxy0000".to_string(), }; - let info = mock_info("owner0000", &[]); + let info = mock_info("addr0000", &[]); let res = execute(deps.as_mut(), mock_env(), info, msg.clone()).unwrap_err(); match res { @@ -1352,7 +1349,7 @@ fn test_migration() { _ => panic!("DO NOT ENTER HERE"), } - let info = mock_info("feeder0000", &[]); + let info = mock_info("owner0000", &[]); let res = execute(deps.as_mut(), mock_env(), info, msg).unwrap(); assert_eq!( res.messages, @@ -1362,7 +1359,7 @@ fn test_migration() { funds: vec![], msg: to_binary(&MintExecuteMsg::RegisterMigration { asset_token: "asset0000".to_string(), - end_price: Decimal::from_ratio(2u128, 1u128), + end_price: Decimal::percent(300), }) .unwrap(), })), @@ -1421,8 +1418,8 @@ fn test_whitelist_pre_ipo_asset() { let msg = ExecuteMsg::Whitelist { name: "pre-IPO asset".to_string(), - symbol: "mPreIPO".to_string(), - oracle_feeder: "feeder0000".to_string(), + symbol: "PreIPO".to_string(), + oracle_proxy: "oracleproxy0000".to_string(), params: Params { auction_discount: Decimal::percent(5), min_collateral_ratio: Decimal::percent(1000), @@ -1430,6 +1427,7 @@ fn test_whitelist_pre_ipo_asset() { mint_period: Some(10000u64), min_collateral_ratio_after_ipo: Some(Decimal::percent(150)), pre_ipo_price: Some(Decimal::percent(1)), + ipo_trigger_addr: Some("trigger0000".to_string()), }, }; let info = mock_info("owner0000", &[]); @@ -1446,7 +1444,7 @@ fn test_whitelist_pre_ipo_asset() { label: "".to_string(), msg: to_binary(&TokenInstantiateMsg { name: "pre-IPO asset".to_string(), - symbol: "mPreIPO".to_string(), + symbol: "mPREIPO".to_string(), decimals: 6u8, initial_balances: vec![], mint: Some(MinterResponse { @@ -1463,23 +1461,24 @@ fn test_whitelist_pre_ipo_asset() { }] ); - let params: Params = read_params(&deps.storage).unwrap(); + let wl_info: WhitelistTmpInfo = read_tmp_whitelist_info(&deps.storage).unwrap(); assert_eq!( - params, - Params { - auction_discount: Decimal::percent(5), - min_collateral_ratio: Decimal::percent(1000), - weight: Some(100u32), - mint_period: Some(10000u64), - min_collateral_ratio_after_ipo: Some(Decimal::percent(150)), - pre_ipo_price: Some(Decimal::percent(1)), + wl_info, + WhitelistTmpInfo { + params: Params { + auction_discount: Decimal::percent(5), + min_collateral_ratio: Decimal::percent(1000), + weight: Some(100u32), + mint_period: Some(10000u64), + min_collateral_ratio_after_ipo: Some(Decimal::percent(150)), + pre_ipo_price: Some(Decimal::percent(1)), + ipo_trigger_addr: Some("trigger0000".to_string()) + }, + oracle_proxy: deps.api.addr_canonicalize("oracleproxy0000").unwrap(), + symbol: "PREIPO".to_string(), } ); - //ensure temp oracle was stored - let tmp_oracle = read_tmp_oracle(&deps.storage).unwrap(); - assert_eq!(tmp_oracle.to_string(), "feeder0000"); - // callback 1 let mut token_inst_res = MsgInstantiateContractResponse::new(); token_inst_res.set_contract_address("asset0000".to_string()); @@ -1513,6 +1512,7 @@ fn test_whitelist_pre_ipo_asset() { / 1_000_000_000, min_collateral_ratio_after_ipo: Decimal::percent(150), pre_ipo_price: Decimal::percent(1), + trigger_addr: "trigger0000".to_string() }), }) .unwrap(), @@ -1520,9 +1520,18 @@ fn test_whitelist_pre_ipo_asset() { SubMsg::new(CosmosMsg::Wasm(WasmMsg::Execute { contract_addr: "oracle0000".to_string(), funds: vec![], - msg: to_binary(&OracleExecuteMsg::RegisterAsset { - asset_token: "asset0000".to_string(), - feeder: "feeder0000".to_string(), + msg: to_binary(&TeFiOracleExecuteMsg::RegisterSource { + symbol: "PREIPO".to_string(), + proxy_addr: "oracleproxy0000".to_string(), + priority: None, + }) + .unwrap(), + })), + SubMsg::new(CosmosMsg::Wasm(WasmMsg::Execute { + contract_addr: "oracle0000".to_string(), + funds: vec![], + msg: to_binary(&TeFiOracleExecuteMsg::InsertAssetSymbolMap { + map: vec![("asset0000".to_string(), "PREIPO".to_string())] }) .unwrap(), })), @@ -1561,6 +1570,7 @@ fn test_whitelist_pre_ipo_asset() { ), attr("min_collateral_ratio_after_ipo", "1.5"), attr("pre_ipo_price", "0.01"), + attr("ipo_trigger_addr", "trigger0000"), ] ); } diff --git a/contracts/mirror_gov/Cargo.toml b/contracts/mirror_gov/Cargo.toml index e03137bb..a9ab2dc6 100644 --- a/contracts/mirror_gov/Cargo.toml +++ b/contracts/mirror_gov/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "mirror-gov" -version = "2.1.1" +version = "2.2.0" authors = ["Terraform Labs, PTE."] edition = "2018" license = "Apache-2.0" @@ -38,7 +38,7 @@ backtraces = ["cosmwasm-std/backtraces"] cw20 = { version = "0.8.0" } cosmwasm-std = { version = "0.16.0" } cosmwasm-storage = { version = "0.16.0" } -mirror-protocol = { version = "2.1.1", path = "../../packages/mirror_protocol" } +mirror-protocol = { version = "2.2.0", path = "../../packages/mirror_protocol" } schemars = "0.8.1" serde = { version = "1.0.103", default-features = false, features = ["derive"] } hex = "0.4" diff --git a/contracts/mirror_gov/schema/poll_response.json b/contracts/mirror_gov/schema/poll_response.json index 2736b2a5..1949f326 100644 --- a/contracts/mirror_gov/schema/poll_response.json +++ b/contracts/mirror_gov/schema/poll_response.json @@ -19,6 +19,16 @@ "abstain_votes": { "$ref": "#/definitions/Uint128" }, + "admin_action": { + "anyOf": [ + { + "$ref": "#/definitions/PollAdminAction" + }, + { + "type": "null" + } + ] + }, "creator": { "type": "string" }, @@ -95,6 +105,203 @@ "description": "Binary is a wrapper around Vec to add base64 de/serialization with serde. It also adds some helper methods to help encode inline.\n\nThis is only needed as serde-json-{core,wasm} has a horrible encoding for Vec", "type": "string" }, + "Decimal": { + "description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)", + "type": "string" + }, + "PollAdminAction": { + "oneOf": [ + { + "description": "Updates migration manager owner", + "type": "object", + "required": [ + "update_owner" + ], + "properties": { + "update_owner": { + "type": "object", + "required": [ + "owner" + ], + "properties": { + "owner": { + "type": "string" + } + } + } + }, + "additionalProperties": false + }, + { + "description": "Executes a set of migrations. The poll can be executes as soon as it reaches the quorum and threshold", + "type": "object", + "required": [ + "execute_migrations" + ], + "properties": { + "execute_migrations": { + "type": "object", + "required": [ + "migrations" + ], + "properties": { + "migrations": { + "type": "array", + "items": { + "type": "array", + "items": [ + { + "type": "string" + }, + { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + { + "$ref": "#/definitions/Binary" + } + ], + "maxItems": 3, + "minItems": 3 + } + } + } + } + }, + "additionalProperties": false + }, + { + "description": "Transfer admin privileges over Mirror contracts to the authorized_addr", + "type": "object", + "required": [ + "authorize_claim" + ], + "properties": { + "authorize_claim": { + "type": "object", + "required": [ + "authorized_addr" + ], + "properties": { + "authorized_addr": { + "type": "string" + } + } + } + }, + "additionalProperties": false + }, + { + "description": "Updates Governace contract configuration", + "type": "object", + "required": [ + "update_config" + ], + "properties": { + "update_config": { + "type": "object", + "properties": { + "admin_manager": { + "type": [ + "string", + "null" + ] + }, + "auth_admin_poll_config": { + "anyOf": [ + { + "$ref": "#/definitions/PollConfig" + }, + { + "type": "null" + } + ] + }, + "default_poll_config": { + "anyOf": [ + { + "$ref": "#/definitions/PollConfig" + }, + { + "type": "null" + } + ] + }, + "effective_delay": { + "type": [ + "integer", + "null" + ], + "format": "uint64", + "minimum": 0.0 + }, + "migration_poll_config": { + "anyOf": [ + { + "$ref": "#/definitions/PollConfig" + }, + { + "type": "null" + } + ] + }, + "owner": { + "type": [ + "string", + "null" + ] + }, + "snapshot_period": { + "type": [ + "integer", + "null" + ], + "format": "uint64", + "minimum": 0.0 + }, + "voter_weight": { + "anyOf": [ + { + "$ref": "#/definitions/Decimal" + }, + { + "type": "null" + } + ] + } + } + } + }, + "additionalProperties": false + } + ] + }, + "PollConfig": { + "type": "object", + "required": [ + "proposal_deposit", + "quorum", + "threshold", + "voting_period" + ], + "properties": { + "proposal_deposit": { + "$ref": "#/definitions/Uint128" + }, + "quorum": { + "$ref": "#/definitions/Decimal" + }, + "threshold": { + "$ref": "#/definitions/Decimal" + }, + "voting_period": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + } + }, "PollExecuteMsg": { "type": "object", "required": [ diff --git a/contracts/mirror_gov/schema/polls_response.json b/contracts/mirror_gov/schema/polls_response.json index c2ce3bf5..0acc4ee0 100644 --- a/contracts/mirror_gov/schema/polls_response.json +++ b/contracts/mirror_gov/schema/polls_response.json @@ -18,6 +18,203 @@ "description": "Binary is a wrapper around Vec to add base64 de/serialization with serde. It also adds some helper methods to help encode inline.\n\nThis is only needed as serde-json-{core,wasm} has a horrible encoding for Vec", "type": "string" }, + "Decimal": { + "description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)", + "type": "string" + }, + "PollAdminAction": { + "oneOf": [ + { + "description": "Updates migration manager owner", + "type": "object", + "required": [ + "update_owner" + ], + "properties": { + "update_owner": { + "type": "object", + "required": [ + "owner" + ], + "properties": { + "owner": { + "type": "string" + } + } + } + }, + "additionalProperties": false + }, + { + "description": "Executes a set of migrations. The poll can be executes as soon as it reaches the quorum and threshold", + "type": "object", + "required": [ + "execute_migrations" + ], + "properties": { + "execute_migrations": { + "type": "object", + "required": [ + "migrations" + ], + "properties": { + "migrations": { + "type": "array", + "items": { + "type": "array", + "items": [ + { + "type": "string" + }, + { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + { + "$ref": "#/definitions/Binary" + } + ], + "maxItems": 3, + "minItems": 3 + } + } + } + } + }, + "additionalProperties": false + }, + { + "description": "Transfer admin privileges over Mirror contracts to the authorized_addr", + "type": "object", + "required": [ + "authorize_claim" + ], + "properties": { + "authorize_claim": { + "type": "object", + "required": [ + "authorized_addr" + ], + "properties": { + "authorized_addr": { + "type": "string" + } + } + } + }, + "additionalProperties": false + }, + { + "description": "Updates Governace contract configuration", + "type": "object", + "required": [ + "update_config" + ], + "properties": { + "update_config": { + "type": "object", + "properties": { + "admin_manager": { + "type": [ + "string", + "null" + ] + }, + "auth_admin_poll_config": { + "anyOf": [ + { + "$ref": "#/definitions/PollConfig" + }, + { + "type": "null" + } + ] + }, + "default_poll_config": { + "anyOf": [ + { + "$ref": "#/definitions/PollConfig" + }, + { + "type": "null" + } + ] + }, + "effective_delay": { + "type": [ + "integer", + "null" + ], + "format": "uint64", + "minimum": 0.0 + }, + "migration_poll_config": { + "anyOf": [ + { + "$ref": "#/definitions/PollConfig" + }, + { + "type": "null" + } + ] + }, + "owner": { + "type": [ + "string", + "null" + ] + }, + "snapshot_period": { + "type": [ + "integer", + "null" + ], + "format": "uint64", + "minimum": 0.0 + }, + "voter_weight": { + "anyOf": [ + { + "$ref": "#/definitions/Decimal" + }, + { + "type": "null" + } + ] + } + } + } + }, + "additionalProperties": false + } + ] + }, + "PollConfig": { + "type": "object", + "required": [ + "proposal_deposit", + "quorum", + "threshold", + "voting_period" + ], + "properties": { + "proposal_deposit": { + "$ref": "#/definitions/Uint128" + }, + "quorum": { + "$ref": "#/definitions/Decimal" + }, + "threshold": { + "$ref": "#/definitions/Decimal" + }, + "voting_period": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + } + }, "PollExecuteMsg": { "type": "object", "required": [ @@ -52,6 +249,16 @@ "abstain_votes": { "$ref": "#/definitions/Uint128" }, + "admin_action": { + "anyOf": [ + { + "$ref": "#/definitions/PollAdminAction" + }, + { + "type": "null" + } + ] + }, "creator": { "type": "string" }, diff --git a/contracts/mirror_gov/src/contract.rs b/contracts/mirror_gov/src/contract.rs index 3516ec00..18d3ee7d 100644 --- a/contracts/mirror_gov/src/contract.rs +++ b/contracts/mirror_gov/src/contract.rs @@ -349,6 +349,7 @@ pub fn create_poll( MAX_POLLS_IN_PROGRESS + 10usize, // increase maximum to prevent mailcious users from authorizing migrations ) } + // all other admin actions have the most restrictive parameters _ => ( config.auth_admin_poll_config.proposal_deposit, current_seconds + config.auth_admin_poll_config.voting_period, @@ -397,7 +398,7 @@ pub fn create_poll( )); } Some(ExecuteData { - contract: deps.api.addr_canonicalize(&poll_execute_msg.contract)?, + contract: target_contract, msg: poll_execute_msg.msg, }) } else { @@ -427,7 +428,7 @@ pub fn create_poll( poll_indexer_store(deps.storage, &PollStatus::InProgress) .save(&poll_id.to_be_bytes(), &true)?; - if poll_admin_action.is_some() { + if let Some(poll_admin_action) = poll_admin_action { poll_additional_params_store(deps.storage).save( &poll_id.to_be_bytes(), &PollAdditionalParams { @@ -459,16 +460,11 @@ pub fn end_poll(deps: DepsMut, env: Env, poll_id: u64) -> StdResult { let (target_quorum, target_threshold, is_fast_track) = match poll_additional_params_read(deps.storage).load(&poll_id.to_be_bytes()) { Ok(params) => match params.admin_action { - Some(PollAdminAction::ExecuteMigrations { .. }) => ( + PollAdminAction::ExecuteMigrations { .. } => ( config.migration_poll_config.quorum, config.migration_poll_config.threshold, true, ), - None => ( - config.default_poll_config.quorum, - config.default_poll_config.threshold, - false, - ), _ => ( config.auth_admin_poll_config.quorum, config.auth_admin_poll_config.threshold, @@ -591,11 +587,8 @@ pub fn execute_poll(deps: DepsMut, env: Env, poll_id: u64) -> StdResult match params.admin_action { - Some(action) => match action { - PollAdminAction::ExecuteMigrations { .. } => (true, Some(to_binary(&action)?)), - _ => (false, Some(to_binary(&action)?)), - }, - _ => (false, None), + PollAdminAction::ExecuteMigrations { .. } => (true, Some(params.admin_action)), + _ => (false, Some(params.admin_action)), }, _ => (false, None), }; @@ -623,15 +616,15 @@ pub fn execute_poll(deps: DepsMut, env: Env, poll_id: u64) -> StdResult CosmosMsg::Wasm(WasmMsg::Execute { contract_addr: env.contract.address.to_string(), - msg: admin_msg, + msg: to_binary(&admin_msg)?, funds: vec![], }), _ => CosmosMsg::Wasm(WasmMsg::Execute { contract_addr: deps.api.addr_humanize(&config.admin_manager)?.to_string(), - msg: admin_msg, + msg: to_binary(&admin_msg)?, funds: vec![], }), } @@ -867,6 +860,10 @@ fn query_poll(deps: Deps, poll_id: u64) -> StdResult { Some(poll) => poll, None => return Err(StdError::generic_err("Poll does not exist")), }; + let admin_action = poll_additional_params_read(deps.storage) + .load(&poll_id.to_be_bytes()) + .map(|params| Some(params.admin_action)) + .unwrap_or_default(); Ok(PollResponse { id: poll.id, @@ -891,6 +888,7 @@ fn query_poll(deps: Deps, poll_id: u64) -> StdResult { total_balance_at_end_poll: poll.total_balance_at_end_poll, voters_reward: poll.voters_reward, staked_amount: poll.staked_amount, + admin_action, }) } @@ -905,6 +903,10 @@ fn query_polls( let poll_responses: StdResult> = polls .iter() .map(|poll| { + let admin_action = poll_additional_params_read(deps.storage) + .load(&poll.id.to_be_bytes()) + .map(|params| Some(params.admin_action)) + .unwrap_or_default(); Ok(PollResponse { id: poll.id, creator: deps.api.addr_humanize(&poll.creator).unwrap().to_string(), @@ -928,6 +930,7 @@ fn query_polls( total_balance_at_end_poll: poll.total_balance_at_end_poll, voters_reward: poll.voters_reward, staked_amount: poll.staked_amount, + admin_action, }) }) .collect(); diff --git a/contracts/mirror_gov/src/state.rs b/contracts/mirror_gov/src/state.rs index ba42b18b..bfdd07b8 100644 --- a/contracts/mirror_gov/src/state.rs +++ b/contracts/mirror_gov/src/state.rs @@ -74,7 +74,7 @@ pub struct Poll { #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] pub struct PollAdditionalParams { - pub admin_action: Option, + pub admin_action: PollAdminAction, } #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] diff --git a/contracts/mirror_gov/src/testing/tests.rs b/contracts/mirror_gov/src/testing/tests.rs index 4806f8ac..31a27ef7 100644 --- a/contracts/mirror_gov/src/testing/tests.rs +++ b/contracts/mirror_gov/src/testing/tests.rs @@ -443,6 +443,7 @@ fn query_polls() { voters_reward: Uint128::zero(), abstain_votes: Uint128::zero(), staked_amount: None, + admin_action: None, }, PollResponse { id: 2u64, @@ -460,6 +461,7 @@ fn query_polls() { voters_reward: Uint128::zero(), abstain_votes: Uint128::zero(), staked_amount: None, + admin_action: None, }, ] ); @@ -494,6 +496,7 @@ fn query_polls() { voters_reward: Uint128::zero(), abstain_votes: Uint128::zero(), staked_amount: None, + admin_action: None, },] ); @@ -527,6 +530,7 @@ fn query_polls() { voters_reward: Uint128::zero(), abstain_votes: Uint128::zero(), staked_amount: None, + admin_action: None, }] ); @@ -560,6 +564,7 @@ fn query_polls() { voters_reward: Uint128::zero(), abstain_votes: Uint128::zero(), staked_amount: None, + admin_action: None, },] ); @@ -3165,6 +3170,7 @@ fn distribute_voting_rewards_only_to_polls_in_progress() { total_balance_at_end_poll: None, voters_reward: Uint128::from(1000000000u128), staked_amount: None, + admin_action: None, }, PollResponse { id: 2u64, @@ -3182,6 +3188,7 @@ fn distribute_voting_rewards_only_to_polls_in_progress() { total_balance_at_end_poll: None, voters_reward: Uint128::zero(), staked_amount: None, + admin_action: None, }, ] ); @@ -4747,6 +4754,46 @@ fn create_auth_admin_poll_config() { Uint128::new(DEFAULT_AUTH_ADMIN_PROPOSAL_DEPOSIT), ); + let res = query( + deps.as_ref(), + mock_env(), + QueryMsg::Polls { + filter: None, + start_after: None, + limit: None, + order_by: Some(OrderBy::Asc), + }, + ) + .unwrap(); + let response: PollsResponse = from_binary(&res).unwrap(); + assert_eq!( + response.polls, + vec![PollResponse { + id: 1u64, + creator: TEST_CREATOR.to_string(), + status: PollStatus::InProgress, + end_time: creator_env + .block + .time + .plus_seconds(DEFAULT_AUTH_ADMIN_VOTING_PERIOD) + .seconds(), + title: "test".to_string(), + description: "test".to_string(), + link: None, + deposit_amount: Uint128::new(DEFAULT_AUTH_ADMIN_PROPOSAL_DEPOSIT), + execute_data: None, + yes_votes: Uint128::zero(), + no_votes: Uint128::zero(), + total_balance_at_end_poll: None, + voters_reward: Uint128::zero(), + abstain_votes: Uint128::zero(), + staked_amount: None, + admin_action: Some(PollAdminAction::AuthorizeClaim { + authorized_addr: "someaddrr0000".to_string(), + }), + }] + ); + deps.querier.with_token_balances(&[( &VOTING_TOKEN.to_string(), &[( @@ -4922,6 +4969,46 @@ fn create_migration_poll_config() { Uint128::new(DEFAULT_MIGRATION_PROPOSAL_DEPOSIT), ); + let res = query( + deps.as_ref(), + mock_env(), + QueryMsg::Polls { + filter: None, + start_after: None, + limit: None, + order_by: Some(OrderBy::Asc), + }, + ) + .unwrap(); + let response: PollsResponse = from_binary(&res).unwrap(); + assert_eq!( + response.polls, + vec![PollResponse { + id: 1u64, + creator: TEST_CREATOR.to_string(), + status: PollStatus::InProgress, + end_time: creator_env + .block + .time + .plus_seconds(DEFAULT_MIGRATION_VOTING_PERIOD) + .seconds(), + title: "test".to_string(), + description: "test".to_string(), + link: None, + deposit_amount: Uint128::new(DEFAULT_MIGRATION_PROPOSAL_DEPOSIT), + execute_data: None, + yes_votes: Uint128::zero(), + no_votes: Uint128::zero(), + total_balance_at_end_poll: None, + voters_reward: Uint128::zero(), + abstain_votes: Uint128::zero(), + staked_amount: None, + admin_action: Some(PollAdminAction::ExecuteMigrations { + migrations: vec![("contract0000".to_string(), 0, migration_msg.clone())], + }), + }] + ); + deps.querier.with_token_balances(&[( &VOTING_TOKEN.to_string(), &[( diff --git a/contracts/mirror_limit_order/Cargo.toml b/contracts/mirror_limit_order/Cargo.toml index ddd83265..f98a4f42 100644 --- a/contracts/mirror_limit_order/Cargo.toml +++ b/contracts/mirror_limit_order/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "mirror-limit-order" -version = "2.1.1" +version = "2.2.0" authors = ["Terraform Labs, PTE."] edition = "2018" description = "A limit order contract for Mirror Protocol - provides limit order features" @@ -38,7 +38,7 @@ cw20 = { version = "0.8.0" } cosmwasm-std = { version = "0.16.0" } cosmwasm-storage = { version = "0.16.0" } integer-sqrt = "0.1.5" -mirror-protocol = { version = "2.1.1", path = "../../packages/mirror_protocol" } +mirror-protocol = { version = "2.2.0", path = "../../packages/mirror_protocol" } terraswap = "2.4.0" terra-cosmwasm = "2.2.0" schemars = "0.8.1" diff --git a/contracts/mirror_lock/Cargo.toml b/contracts/mirror_lock/Cargo.toml index 92c81efd..97e4bb6e 100644 --- a/contracts/mirror_lock/Cargo.toml +++ b/contracts/mirror_lock/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "mirror-lock" -version = "2.1.1" +version = "2.2.0" authors = ["Terraform Labs, PTE."] edition = "2018" description = "A Lock contract for Mirror Protocol - mint contract to lock funds for shorting positions" @@ -37,7 +37,7 @@ backtraces = ["cosmwasm-std/backtraces"] cw20 = { version = "0.8.0" } cosmwasm-std = { version = "0.16.0", features = ["iterator"] } cosmwasm-storage = { version = "0.16.0", features = ["iterator"] } -mirror-protocol = { version = "2.1.1", path = "../../packages/mirror_protocol" } +mirror-protocol = { version = "2.2.0", path = "../../packages/mirror_protocol" } terraswap = "2.4.0" schemars = "0.8.1" serde = { version = "1.0.103", default-features = false, features = ["derive"] } diff --git a/contracts/mirror_mint/Cargo.toml b/contracts/mirror_mint/Cargo.toml index 98aeab99..be3e98b3 100644 --- a/contracts/mirror_mint/Cargo.toml +++ b/contracts/mirror_mint/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "mirror-mint" -version = "2.1.4" +version = "2.2.0" authors = ["Terraform Labs, PTE."] edition = "2018" description = "A Mint contract for Mirror Protocol - allows you to register and mint asset token" @@ -37,7 +37,8 @@ backtraces = ["cosmwasm-std/backtraces"] cw20 = { version = "0.8.0" } cosmwasm-std = { version = "0.16.0", features = ["iterator"] } cosmwasm-storage = { version = "0.16.0", features = ["iterator"] } -mirror-protocol = { version = "2.1.1", path = "../../packages/mirror_protocol" } +mirror-protocol = { version = "2.2.0", path = "../../packages/mirror_protocol" } +tefi-oracle = "0.2.0" terraswap = "2.4.0" schemars = "0.8.1" serde = { version = "1.0.103", default-features = false, features = ["derive"] } diff --git a/contracts/mirror_mint/schema/asset_config_response.json b/contracts/mirror_mint/schema/asset_config_response.json index d43eae93..fec94987 100644 --- a/contracts/mirror_mint/schema/asset_config_response.json +++ b/contracts/mirror_mint/schema/asset_config_response.json @@ -48,7 +48,8 @@ "required": [ "min_collateral_ratio_after_ipo", "mint_end", - "pre_ipo_price" + "pre_ipo_price", + "trigger_addr" ], "properties": { "min_collateral_ratio_after_ipo": { @@ -61,6 +62,9 @@ }, "pre_ipo_price": { "$ref": "#/definitions/Decimal" + }, + "trigger_addr": { + "type": "string" } } } diff --git a/contracts/mirror_mint/schema/execute_msg.json b/contracts/mirror_mint/schema/execute_msg.json index 278ba4d5..0361f2b4 100644 --- a/contracts/mirror_mint/schema/execute_msg.json +++ b/contracts/mirror_mint/schema/execute_msg.json @@ -449,7 +449,8 @@ "required": [ "min_collateral_ratio_after_ipo", "mint_end", - "pre_ipo_price" + "pre_ipo_price", + "trigger_addr" ], "properties": { "min_collateral_ratio_after_ipo": { @@ -462,6 +463,9 @@ }, "pre_ipo_price": { "$ref": "#/definitions/Decimal" + }, + "trigger_addr": { + "type": "string" } } }, diff --git a/contracts/mirror_mint/src/contract.rs b/contracts/mirror_mint/src/contract.rs index 279442bc..4c24f5a6 100644 --- a/contracts/mirror_mint/src/contract.rs +++ b/contracts/mirror_mint/src/contract.rs @@ -1,10 +1,10 @@ use crate::{ asserts::{assert_auction_discount, assert_min_collateral_ratio, assert_protocol_fee}, + migration::migrate_asset_configs, positions::{ auction, burn, deposit, mint, open_position, query_next_position_idx, query_position, query_positions, withdraw, }, - querier::load_oracle_feeder, state::{ read_asset_config, read_config, store_asset_config, store_config, store_position_idx, AssetConfig, Config, @@ -14,14 +14,17 @@ use crate::{ use cosmwasm_std::entry_point; use cosmwasm_std::{ attr, from_binary, to_binary, Addr, Binary, CanonicalAddr, CosmosMsg, Decimal, Deps, DepsMut, - Empty, Env, MessageInfo, Response, StdError, StdResult, Uint128, WasmMsg, + Env, MessageInfo, Response, StdError, StdResult, Uint128, WasmMsg, }; use cw20::Cw20ReceiveMsg; -use mirror_protocol::collateral_oracle::{ExecuteMsg as CollateralOracleExecuteMsg, SourceType}; use mirror_protocol::mint::{ AssetConfigResponse, ConfigResponse, Cw20HookMsg, ExecuteMsg, IPOParams, InstantiateMsg, QueryMsg, }; +use mirror_protocol::{ + collateral_oracle::{ExecuteMsg as CollateralOracleExecuteMsg, SourceType}, + mint::MigrateMsg, +}; use terraswap::asset::{Asset, AssetInfo}; pub const MIN_CR_ALLOWED: &str = "1.1"; @@ -162,7 +165,7 @@ pub fn execute(deps: DepsMut, env: Env, info: MessageInfo, msg: ExecuteMsg) -> S ExecuteMsg::Withdraw { position_idx, collateral, - } => withdraw(deps, env, info.sender, position_idx, collateral), + } => withdraw(deps, info.sender, position_idx, collateral), ExecuteMsg::Mint { position_idx, asset, @@ -211,7 +214,7 @@ pub fn receive_cw20( } Ok(Cw20HookMsg::Auction { position_idx }) => { let cw20_sender = deps.api.addr_validate(cw20_msg.sender.as_str())?; - auction(deps, env, cw20_sender, position_idx, passed_asset) + auction(deps, cw20_sender, position_idx, passed_asset) } Err(_) => Err(StdError::generic_err("invalid cw20 hook message")), } @@ -354,7 +357,9 @@ pub fn register_asset( contract_addr: asset_token.to_string(), }, multiplier: Decimal::one(), // default collateral multiplier for new mAssets - price_source: SourceType::MirrorOracle {}, + price_source: SourceType::TefiOracle { + oracle_addr: deps.api.addr_humanize(&config.oracle)?.to_string(), + }, })?, })); } @@ -430,23 +435,18 @@ pub fn register_migration( pub fn trigger_ipo(deps: DepsMut, info: MessageInfo, asset_token: Addr) -> StdResult { let config = read_config(deps.storage)?; let asset_token_raw: CanonicalAddr = deps.api.addr_canonicalize(asset_token.as_str())?; - let oracle_feeder: Addr = load_oracle_feeder( - deps.as_ref(), - deps.api.addr_humanize(&config.oracle)?, - asset_token.clone(), - )?; - - // only asset feeder can trigger ipo - if oracle_feeder != info.sender { - return Err(StdError::generic_err("unauthorized")); - } - let mut asset_config: AssetConfig = read_asset_config(deps.storage, &asset_token_raw)?; let ipo_params: IPOParams = match asset_config.ipo_params { Some(v) => v, None => return Err(StdError::generic_err("Asset does not have IPO params")), }; + let trigger_addr = deps.api.addr_validate(&ipo_params.trigger_addr)?; + + // only trigger addr can trigger ipo + if trigger_addr != info.sender { + return Err(StdError::generic_err("unauthorized")); + } asset_config.min_collateral_ratio = ipo_params.min_collateral_ratio_after_ipo; asset_config.ipo_params = None; @@ -466,7 +466,9 @@ pub fn trigger_ipo(deps: DepsMut, info: MessageInfo, asset_token: Addr) -> StdRe contract_addr: asset_token.to_string(), }, multiplier: Decimal::one(), // default collateral multiplier for new mAssets - price_source: SourceType::MirrorOracle {}, + price_source: SourceType::TefiOracle { + oracle_addr: deps.api.addr_humanize(&config.oracle)?.to_string(), + }, })?, })]) .add_attributes(vec![ @@ -545,6 +547,14 @@ pub fn query_asset_config(deps: Deps, asset_token: String) -> StdResult StdResult { +pub fn migrate(deps: DepsMut, _env: Env, msg: MigrateMsg) -> StdResult { + // change oracle address to point to new tefi hub + let mut config: Config = read_config(deps.storage)?; + config.oracle = deps.api.addr_canonicalize(&msg.tefi_oracle_contract)?; + store_config(deps.storage, &config)?; + + // just to check that there are no ipo assets so that the ipo params type can be changed + migrate_asset_configs(deps.storage)?; + Ok(Response::default()) } diff --git a/contracts/mirror_mint/src/lib.rs b/contracts/mirror_mint/src/lib.rs index e8ebe04e..d10c5a00 100644 --- a/contracts/mirror_mint/src/lib.rs +++ b/contracts/mirror_mint/src/lib.rs @@ -1,6 +1,7 @@ mod asserts; pub mod contract; mod math; +mod migration; mod positions; mod querier; mod state; diff --git a/contracts/mirror_mint/src/migration.rs b/contracts/mirror_mint/src/migration.rs new file mode 100644 index 00000000..354ee33a --- /dev/null +++ b/contracts/mirror_mint/src/migration.rs @@ -0,0 +1,104 @@ +use cosmwasm_storage::Bucket; +use schemars::JsonSchema; +use serde::{Deserialize, Serialize}; + +use cosmwasm_std::{CanonicalAddr, Decimal, Order, StdError, StdResult, Storage}; + +use crate::state::{AssetConfig, PREFIX_ASSET_CONFIG}; +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] +pub struct LegacyAssetConfig { + pub token: CanonicalAddr, + pub auction_discount: Decimal, + pub min_collateral_ratio: Decimal, + pub end_price: Option, + pub ipo_params: Option, +} + +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] +pub struct LegacyIPOParams { + pub mint_end: u64, + pub pre_ipo_price: Decimal, + pub min_collateral_ratio_after_ipo: Decimal, +} + +pub fn migrate_asset_configs(storage: &mut dyn Storage) -> StdResult<()> { + let mut legacy_asset_configs_bucket: Bucket = + Bucket::new(storage, PREFIX_ASSET_CONFIG); + + let mut asset_configs: Vec<(CanonicalAddr, LegacyAssetConfig)> = vec![]; + for item in legacy_asset_configs_bucket.range(None, None, Order::Ascending) { + let (k, p) = item?; + asset_configs.push((CanonicalAddr::from(k), p)); + } + + for (asset, _) in asset_configs.clone().into_iter() { + legacy_asset_configs_bucket.remove(asset.as_slice()); + } + + let mut new_asset_configs_bucket: Bucket = + Bucket::new(storage, PREFIX_ASSET_CONFIG); + + for (asset, asset_config) in asset_configs.into_iter() { + if asset_config.ipo_params.is_some() { + return Err(StdError::generic_err( + "can not exeucte migration while there is an ipo event", + )); + } + let new_asset_config = &AssetConfig { + token: asset_config.token, + auction_discount: asset_config.auction_discount, + min_collateral_ratio: asset_config.min_collateral_ratio, + end_price: asset_config.end_price, + ipo_params: None, + }; + new_asset_configs_bucket.save(asset.as_slice(), new_asset_config)?; + } + + Ok(()) +} + +#[cfg(test)] +mod migrate_tests { + use crate::state::read_asset_config; + + use super::*; + use cosmwasm_std::{testing::mock_dependencies, Api}; + + pub fn asset_configs_old_store(storage: &mut dyn Storage) -> Bucket { + Bucket::new(storage, PREFIX_ASSET_CONFIG) + } + + #[test] + fn test_asset_configs_migration() { + let mut deps = mock_dependencies(&[]); + let mut legacy_store = asset_configs_old_store(&mut deps.storage); + + let asset_config = LegacyAssetConfig { + token: deps.api.addr_canonicalize("mAPPL").unwrap(), + auction_discount: Decimal::percent(20), + min_collateral_ratio: Decimal::percent(150), + end_price: None, + ipo_params: None, + }; + + legacy_store + .save(asset_config.token.as_slice(), &asset_config) + .unwrap(); + + migrate_asset_configs(deps.as_mut().storage).unwrap(); + + let new_asset_config: AssetConfig = + read_asset_config(deps.as_mut().storage, &asset_config.token).unwrap(); + + assert_eq!( + new_asset_config, + AssetConfig { + token: asset_config.token, + auction_discount: asset_config.auction_discount, + min_collateral_ratio: asset_config.min_collateral_ratio, + end_price: asset_config.end_price, + ipo_params: None, + } + ); + } +} diff --git a/contracts/mirror_mint/src/positions.rs b/contracts/mirror_mint/src/positions.rs index 06c20b65..67a6cebf 100644 --- a/contracts/mirror_mint/src/positions.rs +++ b/contracts/mirror_mint/src/positions.rs @@ -50,14 +50,9 @@ pub fn open_position( // assert the collateral is listed and has not been migrated/revoked let collateral_info_raw: AssetInfoRaw = collateral.info.to_raw(deps.api)?; let collateral_oracle: Addr = deps.api.addr_humanize(&config.collateral_oracle)?; - let (collateral_price, collateral_multiplier) = - assert_revoked_collateral(load_collateral_info( - deps.as_ref(), - collateral_oracle, - &collateral_info_raw, - Some(env.block.time.seconds()), - Some(env.block.height), - )?)?; + let (collateral_price, collateral_multiplier) = assert_revoked_collateral( + load_collateral_info(deps.as_ref(), collateral_oracle, &collateral_info_raw, true)?, + )?; // assert asset migrated let asset_info_raw: AssetInfoRaw = asset_info.to_raw(deps.api)?; @@ -82,12 +77,7 @@ pub fn open_position( } let oracle: Addr = deps.api.addr_humanize(&config.oracle)?; - let asset_price: Decimal = load_asset_price( - deps.as_ref(), - oracle, - &asset_info_raw, - Some(env.block.time.seconds()), - )?; + let asset_price: Decimal = load_asset_price(deps.as_ref(), oracle, &asset_info_raw, true)?; let asset_price_in_collateral_asset = decimal_division(collateral_price, asset_price); @@ -236,8 +226,7 @@ pub fn deposit( deps.as_ref(), collateral_oracle, &position.collateral.info, - None, - None, + false, )?)?; // assert asset migrated @@ -261,7 +250,6 @@ pub fn deposit( pub fn withdraw( deps: DepsMut, - env: Env, sender: Addr, position_idx: Uint128, collateral: Option, @@ -298,12 +286,7 @@ pub fn withdraw( let asset_config: AssetConfig = read_asset_config(deps.storage, &asset_token_raw)?; let oracle: Addr = deps.api.addr_humanize(&config.oracle)?; - let asset_price: Decimal = load_asset_price( - deps.as_ref(), - oracle, - &position.asset.info, - Some(env.block.time.seconds()), - )?; + let asset_price: Decimal = load_asset_price(deps.as_ref(), oracle, &position.asset.info, true)?; // Fetch collateral info from collateral oracle let collateral_oracle: Addr = deps.api.addr_humanize(&config.collateral_oracle)?; @@ -312,8 +295,7 @@ pub fn withdraw( deps.as_ref(), collateral_oracle, &position.collateral.info, - Some(env.block.time.seconds()), - Some(env.block.height), + true, )?; // ignore multiplier for delisted assets @@ -411,20 +393,14 @@ pub fn mint( deps.as_ref(), collateral_oracle, &position.collateral.info, - Some(env.block.time.seconds()), - Some(env.block.height), + true, )?)?; // for assets with limited minting period (preIPO assets), assert minting phase assert_mint_period(&env, &asset_config)?; let oracle: Addr = deps.api.addr_humanize(&config.oracle)?; - let asset_price: Decimal = load_asset_price( - deps.as_ref(), - oracle, - &position.asset.info, - Some(env.block.time.seconds()), - )?; + let asset_price: Decimal = load_asset_price(deps.as_ref(), oracle, &position.asset.info, true)?; // Compute new asset amount let asset_amount: Uint128 = mint_amount + position.asset.amount; @@ -585,8 +561,7 @@ pub fn burn( deps.as_ref(), collateral_oracle, &position.collateral.info, - Some(env.block.time.seconds()), - Some(env.block.height), + true, )?; // If the collateral is default denom asset and the asset is deprecated, @@ -657,12 +632,8 @@ pub fn burn( return Err(StdError::generic_err("unauthorized")); } let oracle = deps.api.addr_humanize(&config.oracle)?; - let asset_price: Decimal = load_asset_price( - deps.as_ref(), - oracle, - &asset.info.to_raw(deps.api)?, - Some(env.block.time.seconds()), - )?; + let asset_price: Decimal = + load_asset_price(deps.as_ref(), oracle, &asset.info.to_raw(deps.api)?, true)?; let collateral_price_in_asset: Decimal = decimal_division(asset_price, collateral_price); // Subtract the protocol fee from the position's collateral @@ -740,7 +711,6 @@ pub fn burn( pub fn auction( deps: DepsMut, - env: Env, sender: Addr, position_idx: Uint128, asset: Asset, @@ -768,12 +738,7 @@ pub fn auction( } let oracle: Addr = deps.api.addr_humanize(&config.oracle)?; - let asset_price: Decimal = load_asset_price( - deps.as_ref(), - oracle, - &position.asset.info, - Some(env.block.time.seconds()), - )?; + let asset_price: Decimal = load_asset_price(deps.as_ref(), oracle, &position.asset.info, true)?; // fetch collateral info from collateral oracle let collateral_oracle: Addr = deps.api.addr_humanize(&config.collateral_oracle)?; @@ -781,8 +746,7 @@ pub fn auction( deps.as_ref(), collateral_oracle, &position.collateral.info, - Some(env.block.time.seconds()), - Some(env.block.height), + true, )?; // Compute collateral price in asset unit diff --git a/contracts/mirror_mint/src/querier.rs b/contracts/mirror_mint/src/querier.rs index e39776af..98223881 100644 --- a/contracts/mirror_mint/src/querier.rs +++ b/contracts/mirror_mint/src/querier.rs @@ -1,40 +1,24 @@ use cosmwasm_std::{ - to_binary, Addr, Decimal, Deps, QuerierWrapper, QueryRequest, StdError, StdResult, WasmQuery, + to_binary, Addr, Decimal, Deps, QuerierWrapper, QueryRequest, StdResult, WasmQuery, }; -use crate::state::{read_config, read_fixed_price, Config}; +use crate::{ + math::decimal_division, + state::{read_config, read_fixed_price, Config}, +}; use mirror_protocol::collateral_oracle::{ - CollateralPriceResponse, QueryMsg as CollateralOracleQueryMsg, + CollateralInfoResponse, CollateralPriceResponse, QueryMsg as CollateralOracleQueryMsg, }; -use mirror_protocol::oracle::{FeederResponse, PriceResponse, QueryMsg as OracleQueryMsg}; +use tefi_oracle::hub::{HubQueryMsg as OracleQueryMsg, PriceResponse}; use terraswap::asset::AssetInfoRaw; const PRICE_EXPIRE_TIME: u64 = 60; -pub fn load_oracle_feeder(deps: Deps, contract_addr: Addr, asset_token: Addr) -> StdResult { - let res: StdResult = - deps.querier.query(&QueryRequest::Wasm(WasmQuery::Smart { - contract_addr: contract_addr.to_string(), - msg: to_binary(&OracleQueryMsg::Feeder { - asset_token: asset_token.to_string(), - })?, - })); - - let feeder: Addr = match res { - Ok(v) => deps.api.addr_validate(v.feeder.as_str())?, - Err(_) => { - return Err(StdError::generic_err("Failed to fetch the oracle feeder")); - } - }; - - Ok(feeder) -} - pub fn load_asset_price( deps: Deps, oracle: Addr, asset: &AssetInfoRaw, - block_time: Option, + check_expire: bool, ) -> StdResult { let config: Config = read_config(deps.storage)?; @@ -49,13 +33,7 @@ pub fn load_asset_price( Decimal::one() } else { // fetch price from oracle - query_price( - &deps.querier, - oracle, - asset_denom, - config.base_denom, - block_time, - )? + query_price(&deps.querier, oracle, asset_denom, None, check_expire)? } }; @@ -66,8 +44,7 @@ pub fn load_collateral_info( deps: Deps, collateral_oracle: Addr, collateral: &AssetInfoRaw, - block_time: Option, - block_height: Option, + check_expire: bool, ) -> StdResult<(Decimal, Decimal, bool)> { let config: Config = read_config(deps.storage)?; let collateral_denom: String = (collateral.to_normal(deps.api)?).to_string(); @@ -84,13 +61,8 @@ pub fn load_collateral_info( if let Some(end_price) = end_price { // load collateral_multiplier from collateral oracle // if asset is revoked, no need to check for old price - let (_, collateral_multiplier, _) = query_collateral( - &deps.querier, - collateral_oracle, - collateral_denom, - None, - block_height, - )?; + let (collateral_multiplier, _) = + query_collateral_info(&deps.querier, collateral_oracle, collateral_denom)?; Ok((end_price, collateral_multiplier, true)) } else { @@ -99,8 +71,7 @@ pub fn load_collateral_info( &deps.querier, collateral_oracle, collateral_denom, - block_time, - block_height, + check_expire, )?; Ok((collateral_oracle_price, collateral_multiplier, is_revoked)) @@ -111,26 +82,37 @@ pub fn query_price( querier: &QuerierWrapper, oracle: Addr, base_asset: String, - quote_asset: String, - block_time: Option, + quote_asset: Option, + check_expire: bool, ) -> StdResult { - let res: PriceResponse = querier.query(&QueryRequest::Wasm(WasmQuery::Smart { + let timeframe: Option = if check_expire { + Some(PRICE_EXPIRE_TIME) + } else { + None + }; + let base_res: PriceResponse = querier.query(&QueryRequest::Wasm(WasmQuery::Smart { contract_addr: oracle.to_string(), msg: to_binary(&OracleQueryMsg::Price { - base_asset, - quote_asset, + asset_token: base_asset, + timeframe, })?, }))?; - if let Some(block_time) = block_time { - if res.last_updated_base < (block_time - PRICE_EXPIRE_TIME) - || res.last_updated_quote < (block_time - PRICE_EXPIRE_TIME) - { - return Err(StdError::generic_err("Price is too old")); - } - } + let rate = if let Some(quote_asset) = quote_asset { + let quote_res: PriceResponse = querier.query(&QueryRequest::Wasm(WasmQuery::Smart { + contract_addr: oracle.to_string(), + msg: to_binary(&OracleQueryMsg::Price { + asset_token: quote_asset, + timeframe, + })?, + }))?; - Ok(res.rate) + decimal_division(base_res.rate, quote_res.rate) + } else { + base_res.rate + }; + + Ok(rate) } // queries the collateral oracle to get the asset rate and multiplier @@ -138,22 +120,31 @@ pub fn query_collateral( querier: &QuerierWrapper, collateral_oracle: Addr, asset: String, - block_time: Option, - block_height: Option, + check_expire: bool, ) -> StdResult<(Decimal, Decimal, bool)> { + let timeframe: Option = if check_expire { + Some(PRICE_EXPIRE_TIME) + } else { + None + }; let res: CollateralPriceResponse = querier.query(&QueryRequest::Wasm(WasmQuery::Smart { contract_addr: collateral_oracle.to_string(), - msg: to_binary(&CollateralOracleQueryMsg::CollateralPrice { - asset, - block_height, - })?, + msg: to_binary(&CollateralOracleQueryMsg::CollateralPrice { asset, timeframe })?, }))?; - if let Some(block_time) = block_time { - if res.last_updated < (block_time - PRICE_EXPIRE_TIME) { - return Err(StdError::generic_err("Collateral price is too old")); - } - } - Ok((res.rate, res.multiplier, res.is_revoked)) } + +// queries only collateral information (multiplier and is_revoked), without price +pub fn query_collateral_info( + querier: &QuerierWrapper, + collateral_oracle: Addr, + asset: String, +) -> StdResult<(Decimal, bool)> { + let res: CollateralInfoResponse = querier.query(&QueryRequest::Wasm(WasmQuery::Smart { + contract_addr: collateral_oracle.to_string(), + msg: to_binary(&CollateralOracleQueryMsg::CollateralAssetInfo { asset })?, + }))?; + + Ok((res.multiplier, res.is_revoked)) +} diff --git a/contracts/mirror_mint/src/state.rs b/contracts/mirror_mint/src/state.rs index bcb4094f..9894be61 100644 --- a/contracts/mirror_mint/src/state.rs +++ b/contracts/mirror_mint/src/state.rs @@ -9,7 +9,7 @@ use mirror_protocol::mint::IPOParams; use std::convert::TryInto; use terraswap::asset::{AssetInfoRaw, AssetRaw}; -static PREFIX_ASSET_CONFIG: &[u8] = b"asset_config"; +pub static PREFIX_ASSET_CONFIG: &[u8] = b"asset_config"; static PREFIX_POSITION: &[u8] = b"position"; static PREFIX_INDEX_BY_USER: &[u8] = b"by_user"; static PREFIX_INDEX_BY_ASSET: &[u8] = b"by_asset"; @@ -201,14 +201,13 @@ pub fn read_positions( let position_bucket: ReadonlyBucket = ReadonlyBucket::new(storage, PREFIX_POSITION); let limit = limit.unwrap_or(DEFAULT_LIMIT).min(MAX_LIMIT) as usize; - let start = calc_range_start(start_after); + let (start, end, order_by) = match order_by { + Some(OrderBy::Asc) => (calc_range_start(start_after), None, OrderBy::Asc), + _ => (None, calc_range_end(start_after), OrderBy::Desc), + }; position_bucket - .range( - start.as_deref(), - None, - order_by.unwrap_or(OrderBy::Desc).into(), - ) + .range(start.as_deref(), end.as_deref(), order_by.into()) .take(limit) .map(|item| { let (_, v) = item?; diff --git a/contracts/mirror_mint/src/testing/contract_test.rs b/contracts/mirror_mint/src/testing/contract_test.rs index e68ff457..71837cf6 100644 --- a/contracts/mirror_mint/src/testing/contract_test.rs +++ b/contracts/mirror_mint/src/testing/contract_test.rs @@ -133,7 +133,9 @@ fn register_asset() { contract_addr: "asset0000".to_string(), }, multiplier: Decimal::one(), - price_source: SourceType::MirrorOracle {}, + price_source: SourceType::TefiOracle { + oracle_addr: "oracle0000".to_string(), + }, }) .unwrap(), }))] @@ -249,6 +251,7 @@ fn update_asset() { min_collateral_ratio_after_ipo: Decimal::percent(150), mint_end: 10000u64, pre_ipo_price: Decimal::percent(1), + trigger_addr: "ipotrigger0000".to_string(), }), }; let info = mock_info("owner0000", &[]); @@ -272,7 +275,8 @@ fn update_asset() { ipo_params: Some(IPOParams { min_collateral_ratio_after_ipo: Decimal::percent(150), mint_end: 10000u64, - pre_ipo_price: Decimal::percent(1) + pre_ipo_price: Decimal::percent(1), + trigger_addr: "ipotrigger0000".to_string(), }), } ); diff --git a/contracts/mirror_mint/src/testing/mock_querier.rs b/contracts/mirror_mint/src/testing/mock_querier.rs index 4cdc1ba1..807155d6 100644 --- a/contracts/mirror_mint/src/testing/mock_querier.rs +++ b/contracts/mirror_mint/src/testing/mock_querier.rs @@ -7,9 +7,8 @@ use schemars::JsonSchema; use serde::{Deserialize, Serialize}; use std::collections::HashMap; -use crate::math::decimal_division; use mirror_protocol::collateral_oracle::CollateralPriceResponse; -use mirror_protocol::oracle::{FeederResponse, PriceResponse}; +use tefi_oracle::hub::PriceResponse; use terra_cosmwasm::{TaxCapResponse, TaxRateResponse, TerraQuery, TerraQueryWrapper, TerraRoute}; use terraswap::{asset::AssetInfo, asset::PairInfo}; @@ -34,7 +33,6 @@ pub struct WasmMockQuerier { oracle_price_querier: OraclePriceQuerier, collateral_oracle_querier: CollateralOracleQuerier, terraswap_pair_querier: TerraswapPairQuerier, - oracle_querier: OracleQuerier, } #[derive(Clone, Default)] @@ -138,27 +136,6 @@ pub(crate) fn paris_to_map(pairs: &[(&String, &String, &String)]) -> HashMap, -} - -impl OracleQuerier { - pub fn new(feeders: &[(&String, &String)]) -> Self { - OracleQuerier { - feeders: address_pair_to_map(feeders), - } - } -} - -pub(crate) fn address_pair_to_map(address_pair: &[(&String, &String)]) -> HashMap { - let mut address_pair_map: HashMap = HashMap::new(); - for (addr1, addr2) in address_pair.iter() { - address_pair_map.insert(addr1.to_string(), addr2.to_string()); - } - address_pair_map -} - impl Querier for WasmMockQuerier { fn raw_query(&self, bin_request: &[u8]) -> QuerierResult { // MockQuerier doesn't support Custom, so we ignore it completely here @@ -179,8 +156,8 @@ impl Querier for WasmMockQuerier { #[serde(rename_all = "snake_case")] pub enum MockQueryMsg { Price { - base_asset: String, - quote_asset: String, + asset_token: String, + timeframe: Option, }, CollateralPrice { asset: String, @@ -188,9 +165,6 @@ pub enum MockQueryMsg { Pair { asset_infos: [AssetInfo; 2], }, - Feeder { - asset_token: String, - }, } impl WasmMockQuerier { @@ -226,23 +200,14 @@ impl WasmMockQuerier { msg, }) => match from_binary(msg).unwrap() { MockQueryMsg::Price { - base_asset, - quote_asset, - } => match self.oracle_price_querier.oracle_price.get(&base_asset) { + asset_token, + timeframe: _, + } => match self.oracle_price_querier.oracle_price.get(&asset_token) { Some(base_price) => { - match self.oracle_price_querier.oracle_price.get("e_asset) { - Some(quote_price) => { - SystemResult::Ok(ContractResult::from(to_binary(&PriceResponse { - rate: decimal_division(*base_price, *quote_price), - last_updated_base: 1000u64, - last_updated_quote: 1000u64, - }))) - } - None => SystemResult::Err(SystemError::InvalidRequest { - error: "No oracle price exists".to_string(), - request: msg.as_slice().into(), - }), - } + SystemResult::Ok(ContractResult::from(to_binary(&PriceResponse { + rate: *base_price, + last_updated: 1000u64, + }))) } None => SystemResult::Err(SystemError::InvalidRequest { error: "No oracle price exists".to_string(), @@ -285,22 +250,6 @@ impl WasmMockQuerier { }), } } - MockQueryMsg::Feeder { asset_token } => { - match self.oracle_querier.feeders.get(&asset_token) { - Some(v) => { - SystemResult::Ok(ContractResult::from(to_binary(&FeederResponse { - asset_token, - feeder: v.to_string(), - }))) - } - None => { - return SystemResult::Err(SystemError::InvalidRequest { - error: format!("Oracle Feeder is not found for {}", asset_token), - request: msg.as_slice().into(), - }) - } - } - } }, _ => self.base.handle_query(request), } @@ -315,7 +264,6 @@ impl WasmMockQuerier { oracle_price_querier: OraclePriceQuerier::default(), collateral_oracle_querier: CollateralOracleQuerier::default(), terraswap_pair_querier: TerraswapPairQuerier::default(), - oracle_querier: OracleQuerier::default(), } } @@ -341,8 +289,4 @@ impl WasmMockQuerier { pub fn with_terraswap_pair(&mut self, pairs: &[(&String, &String, &String)]) { self.terraswap_pair_querier = TerraswapPairQuerier::new(pairs); } - - pub fn with_oracle_feeders(&mut self, feeders: &[(&String, &String)]) { - self.oracle_querier = OracleQuerier::new(feeders); - } } diff --git a/contracts/mirror_mint/src/testing/pre_ipo_test.rs b/contracts/mirror_mint/src/testing/pre_ipo_test.rs index 3d85c1dc..1b7e322d 100644 --- a/contracts/mirror_mint/src/testing/pre_ipo_test.rs +++ b/contracts/mirror_mint/src/testing/pre_ipo_test.rs @@ -36,8 +36,6 @@ fn pre_ipo_assets() { &Decimal::from_ratio(10u128, 1u128), ), ]); - deps.querier - .with_oracle_feeders(&[(&"preIPOAsset0000".to_string(), &"feeder0000".to_string())]); let base_denom = "uusd".to_string(); @@ -67,6 +65,7 @@ fn pre_ipo_assets() { mint_end, min_collateral_ratio_after_ipo: Decimal::percent(150), pre_ipo_price: Decimal::percent(100), + trigger_addr: "ipotrigger0000".to_string(), }), }; @@ -226,7 +225,7 @@ fn pre_ipo_assets() { /////////////////// current_time = creator_env.block.time.plus_seconds(20).seconds(); - // register migration initiated by the feeder + // register migration initiated by the trigger address let msg = ExecuteMsg::TriggerIPO { asset_token: "preIPOAsset0000".to_string(), }; @@ -238,7 +237,7 @@ fn pre_ipo_assets() { // succesfull attempt let env = mock_env_with_block_time(current_time); - let info = mock_info("feeder0000", &[]); + let info = mock_info("ipotrigger0000", &[]); let res = execute(deps.as_mut(), env, info, msg).unwrap(); assert_eq!( @@ -258,7 +257,9 @@ fn pre_ipo_assets() { contract_addr: "preIPOAsset0000".to_string(), }, multiplier: Decimal::one(), - price_source: SourceType::MirrorOracle {}, + price_source: SourceType::TefiOracle { + oracle_addr: "oracle0000".to_string(), + }, }) .unwrap(), }))] @@ -273,7 +274,7 @@ fn pre_ipo_assets() { ) .unwrap(); let asset_config_res: AssetConfigResponse = from_binary(&res).unwrap(); - // traditional asset configuration, feeder feeds real price + // traditional asset configuration, price is obtained from the oracle assert_eq!( asset_config_res, AssetConfigResponse { diff --git a/contracts/mirror_oracle/Cargo.toml b/contracts/mirror_oracle/Cargo.toml index 7a56ed94..ca534a27 100644 --- a/contracts/mirror_oracle/Cargo.toml +++ b/contracts/mirror_oracle/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "mirror-oracle" -version = "2.1.1" +version = "2.2.0" authors = ["Terraform Labs, PTE."] edition = "2018" description = "A Oracle contract for Mirror Protocol - allows you to feed asset oracle price with owner key" @@ -36,7 +36,7 @@ backtraces = ["cosmwasm-std/backtraces"] [dependencies] cosmwasm-std = { version = "0.16.0", features = ["iterator"] } cosmwasm-storage = { version = "0.16.0", features = ["iterator"] } -mirror-protocol = { version = "2.1.1", path = "../../packages/mirror_protocol" } +mirror-protocol = { version = "2.2.0", path = "../../packages/mirror_protocol" } schemars = "0.8.1" serde = { version = "1.0.103", default-features = false, features = ["derive"] } diff --git a/contracts/mirror_short_reward/Cargo.toml b/contracts/mirror_short_reward/Cargo.toml index a23a3330..2a48c772 100644 --- a/contracts/mirror_short_reward/Cargo.toml +++ b/contracts/mirror_short_reward/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "mirror-short-reward" -version = "2.1.1" +version = "2.2.0" authors = ["Terraform Labs, PTE."] edition = "2018" description = "Short Reward contract for Mirror Protocol - implements the short reward weight calculation" @@ -35,7 +35,7 @@ backtraces = ["cosmwasm-std/backtraces"] [dependencies] cosmwasm-std = { version = "0.16.0" } -mirror-protocol = { version = "2.1.1", path = "../../packages/mirror_protocol" } +mirror-protocol = { version = "2.2.0", path = "../../packages/mirror_protocol" } schemars = "0.8.1" serde = { version = "1.0.103", default-features = false, features = ["derive"] } diff --git a/contracts/mirror_staking/Cargo.toml b/contracts/mirror_staking/Cargo.toml index 2aab189b..e7908269 100644 --- a/contracts/mirror_staking/Cargo.toml +++ b/contracts/mirror_staking/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "mirror-staking" -version = "2.1.1" +version = "2.2.0" authors = ["Terraform Labs, PTE."] edition = "2018" description = "A Staking contract for Mirror Protocol - distribute rewards to stakers" @@ -38,9 +38,10 @@ cw20 = { version = "0.8.0" } terraswap = "2.4.0" cosmwasm-std = { version = "0.16.0" } cosmwasm-storage = { version = "0.16.0" } -mirror-protocol = { version = "2.1.1", path = "../../packages/mirror_protocol" } +mirror-protocol = { version = "2.2.0", path = "../../packages/mirror_protocol" } schemars = "0.8.1" serde = { version = "1.0.103", default-features = false, features = ["derive"] } +tefi-oracle = "0.2.0" [dev-dependencies] terra-cosmwasm = { version = "2.2.0" } diff --git a/contracts/mirror_staking/src/contract.rs b/contracts/mirror_staking/src/contract.rs index 48b2a5c2..42d8677b 100644 --- a/contracts/mirror_staking/src/contract.rs +++ b/contracts/mirror_staking/src/contract.rs @@ -1,4 +1,3 @@ -use crate::migration::migrate_pool_infos; use crate::rewards::{adjust_premium, deposit_reward, query_reward_info, withdraw_reward}; use crate::staking::{ auto_stake, auto_stake_hook, bond, decrease_short_token, increase_short_token, unbond, @@ -401,22 +400,10 @@ pub fn query_pool_info(deps: Deps, asset_token: String) -> StdResult StdResult { - migrate_pool_infos(deps.storage)?; - - // when the migration is executed, deprecate directly the MIR pool - let config = read_config(deps.storage)?; - let self_info = MessageInfo { - sender: deps.api.addr_humanize(&config.owner)?, - funds: vec![], - }; - let asset_token_to_deprecate_addr = deps.api.addr_validate(&msg.asset_token_to_deprecate)?; - let new_staking_token_addr = deps.api.addr_validate(&msg.new_staking_token)?; - deprecate_staking_token( - deps, - self_info, - asset_token_to_deprecate_addr, - new_staking_token_addr, - )?; + // change the oracle address for the tefi oracle address + let mut config: Config = read_config(deps.storage)?; + config.oracle_contract = deps.api.addr_canonicalize(&msg.tefi_oracle_contract)?; + store_config(deps.storage, &config)?; Ok(Response::default()) } diff --git a/contracts/mirror_staking/src/lib.rs b/contracts/mirror_staking/src/lib.rs index 2ff3e9f8..b9c06410 100644 --- a/contracts/mirror_staking/src/lib.rs +++ b/contracts/mirror_staking/src/lib.rs @@ -1,6 +1,5 @@ pub mod contract; mod math; -mod migration; mod querier; mod rewards; mod staking; diff --git a/contracts/mirror_staking/src/migration.rs b/contracts/mirror_staking/src/migration.rs deleted file mode 100644 index 9f17e111..00000000 --- a/contracts/mirror_staking/src/migration.rs +++ /dev/null @@ -1,142 +0,0 @@ -use cosmwasm_std::{CanonicalAddr, Decimal, Order, StdResult, Storage, Uint128}; -use cosmwasm_storage::Bucket; -use schemars::JsonSchema; -use serde::{Deserialize, Serialize}; - -use crate::state::{PoolInfo, PREFIX_POOL_INFO}; - -#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] -pub struct LegacyPoolInfo { - pub staking_token: CanonicalAddr, - pub pending_reward: Uint128, - pub short_pending_reward: Uint128, - pub total_bond_amount: Uint128, - pub total_short_amount: Uint128, - pub reward_index: Decimal, - pub short_reward_index: Decimal, - pub premium_rate: Decimal, - pub short_reward_weight: Decimal, - pub premium_updated_time: u64, -} - -pub fn migrate_pool_infos(storage: &mut dyn Storage) -> StdResult<()> { - let mut legacy_pool_infos_bucket: Bucket = - Bucket::new(storage, PREFIX_POOL_INFO); - - let mut pools: Vec<(CanonicalAddr, LegacyPoolInfo)> = vec![]; - for item in legacy_pool_infos_bucket.range(None, None, Order::Ascending) { - let (k, p) = item?; - pools.push((CanonicalAddr::from(k), p)); - } - - for (asset, _) in pools.clone().into_iter() { - legacy_pool_infos_bucket.remove(asset.as_slice()); - } - - let mut new_pool_infos_bucket: Bucket = Bucket::new(storage, PREFIX_POOL_INFO); - - for (asset, legacy_pool_info) in pools.into_iter() { - let new_pool_info = &PoolInfo { - staking_token: legacy_pool_info.staking_token, - total_bond_amount: legacy_pool_info.total_bond_amount, - total_short_amount: legacy_pool_info.total_short_amount, - reward_index: legacy_pool_info.reward_index, - short_reward_index: legacy_pool_info.short_reward_index, - pending_reward: legacy_pool_info.pending_reward, - short_pending_reward: legacy_pool_info.short_pending_reward, - premium_rate: legacy_pool_info.premium_rate, - short_reward_weight: legacy_pool_info.short_reward_weight, - premium_updated_time: legacy_pool_info.premium_updated_time, - migration_params: None, - }; - new_pool_infos_bucket.save(asset.as_slice(), new_pool_info)?; - } - - Ok(()) -} - -#[cfg(test)] -mod migrate_tests { - use crate::state::read_pool_info; - - use super::*; - use cosmwasm_std::{testing::mock_dependencies, Api}; - - pub fn pool_infos_old_store(storage: &mut dyn Storage) -> Bucket { - Bucket::new(storage, PREFIX_POOL_INFO) - } - - #[test] - fn test_pool_infos_migration() { - let mut deps = mock_dependencies(&[]); - let mut legacy_store = pool_infos_old_store(&mut deps.storage); - - let asset_1 = deps.api.addr_canonicalize("asset1").unwrap(); - let pool_info_1 = LegacyPoolInfo { - staking_token: deps.api.addr_canonicalize("staking1").unwrap(), - total_bond_amount: Uint128::from(1u128), - total_short_amount: Uint128::from(1u128), - reward_index: Decimal::percent(1), - short_reward_index: Decimal::percent(1), - pending_reward: Uint128::from(1u128), - short_pending_reward: Uint128::from(1u128), - premium_rate: Decimal::percent(1), - short_reward_weight: Decimal::percent(1), - premium_updated_time: 1, - }; - let asset_2 = deps.api.addr_canonicalize("asset2").unwrap(); - let pool_info_2 = LegacyPoolInfo { - staking_token: deps.api.addr_canonicalize("staking2").unwrap(), - total_bond_amount: Uint128::from(2u128), - total_short_amount: Uint128::from(2u128), - reward_index: Decimal::percent(2), - short_reward_index: Decimal::percent(2), - pending_reward: Uint128::from(2u128), - short_pending_reward: Uint128::from(2u128), - premium_rate: Decimal::percent(2), - short_reward_weight: Decimal::percent(2), - premium_updated_time: 2, - }; - - legacy_store.save(asset_1.as_slice(), &pool_info_1).unwrap(); - legacy_store.save(asset_2.as_slice(), &pool_info_2).unwrap(); - - migrate_pool_infos(deps.as_mut().storage).unwrap(); - - let new_pool_info_1: PoolInfo = read_pool_info(deps.as_mut().storage, &asset_1).unwrap(); - let new_pool_info_2: PoolInfo = read_pool_info(deps.as_mut().storage, &asset_2).unwrap(); - - assert_eq!( - new_pool_info_1, - PoolInfo { - staking_token: deps.api.addr_canonicalize("staking1").unwrap(), - total_bond_amount: Uint128::from(1u128), - total_short_amount: Uint128::from(1u128), - reward_index: Decimal::percent(1), - short_reward_index: Decimal::percent(1), - pending_reward: Uint128::from(1u128), - short_pending_reward: Uint128::from(1u128), - premium_rate: Decimal::percent(1), - short_reward_weight: Decimal::percent(1), - premium_updated_time: 1, - migration_params: None, - } - ); - assert_eq!( - new_pool_info_2, - PoolInfo { - staking_token: deps.api.addr_canonicalize("staking2").unwrap(), - total_bond_amount: Uint128::from(2u128), - total_short_amount: Uint128::from(2u128), - reward_index: Decimal::percent(2), - short_reward_index: Decimal::percent(2), - pending_reward: Uint128::from(2u128), - short_pending_reward: Uint128::from(2u128), - premium_rate: Decimal::percent(2), - short_reward_weight: Decimal::percent(2), - premium_updated_time: 2, - migration_params: None, - } - ) - } -} diff --git a/contracts/mirror_staking/src/querier.rs b/contracts/mirror_staking/src/querier.rs index 55829b7d..9f754040 100644 --- a/contracts/mirror_staking/src/querier.rs +++ b/contracts/mirror_staking/src/querier.rs @@ -2,8 +2,8 @@ use crate::math::{decimal_division, decimal_subtraction}; use cosmwasm_std::{ to_binary, Addr, Decimal, Deps, QuerierWrapper, QueryRequest, StdResult, Uint128, WasmQuery, }; -use mirror_protocol::oracle::{PriceResponse, QueryMsg as OracleQueryMsg}; use mirror_protocol::short_reward::{QueryMsg as ShortRewardQueryMsg, ShortRewardWeightResponse}; +use tefi_oracle::hub::{HubQueryMsg as OracleQueryMsg, PriceResponse}; use terraswap::{ asset::AssetInfo, asset::PairInfo, pair::PoolResponse, pair::QueryMsg as PairQueryMsg, querier::query_pair_info, @@ -20,9 +20,7 @@ pub fn compute_premium_rate( &deps.querier, factory_contract, &[ - AssetInfo::NativeToken { - denom: base_denom.to_string(), - }, + AssetInfo::NativeToken { denom: base_denom }, AssetInfo::Token { contract_addr: asset_token.to_string(), }, @@ -46,8 +44,7 @@ pub fn compute_premium_rate( Decimal::from_ratio(pool.assets[1].amount, pool.assets[0].amount) }; - let oracle_price: Decimal = - query_price(deps, oracle_contract, asset_token.to_string(), base_denom)?; + let oracle_price: Decimal = query_price(deps, oracle_contract, asset_token.to_string())?; if oracle_price.is_zero() { Ok((Decimal::zero(), true)) @@ -77,17 +74,12 @@ pub fn compute_short_reward_weight( Ok(res.short_reward_weight) } -pub fn query_price( - deps: Deps, - oracle: Addr, - base_asset: String, - quote_asset: String, -) -> StdResult { +pub fn query_price(deps: Deps, oracle: Addr, quote_asset: String) -> StdResult { let res: PriceResponse = deps.querier.query(&QueryRequest::Wasm(WasmQuery::Smart { contract_addr: oracle.to_string(), msg: to_binary(&OracleQueryMsg::Price { - base_asset, - quote_asset, + asset_token: quote_asset, + timeframe: None, })?, }))?; diff --git a/contracts/mirror_staking/src/testing/mock_querier.rs b/contracts/mirror_staking/src/testing/mock_querier.rs index 60dd4e50..4a155355 100644 --- a/contracts/mirror_staking/src/testing/mock_querier.rs +++ b/contracts/mirror_staking/src/testing/mock_querier.rs @@ -4,9 +4,9 @@ use cosmwasm_std::{ QuerierResult, QueryRequest, SystemError, SystemResult, Uint128, WasmQuery, }; use cosmwasm_storage::to_length_prefixed; -use mirror_protocol::oracle::PriceResponse; use mirror_protocol::short_reward::ShortRewardWeightResponse; use serde::Deserialize; +use tefi_oracle::hub::PriceResponse; use terra_cosmwasm::{TaxCapResponse, TaxRateResponse, TerraQuery, TerraQueryWrapper, TerraRoute}; use terraswap::{asset::Asset, asset::AssetInfo, asset::PairInfo, pair::PoolResponse}; @@ -56,8 +56,8 @@ pub enum MockQueryMsg { asset_infos: [AssetInfo; 2], }, Price { - base_asset: String, - quote_asset: String, + asset_token: String, + timeframe: Option, }, Pool {}, ShortRewardWeight { @@ -111,12 +111,11 @@ impl WasmMockQuerier { }))) } MockQueryMsg::Price { - base_asset: _, - quote_asset: _, + asset_token: _, + timeframe: _, } => SystemResult::Ok(ContractResult::from(to_binary(&PriceResponse { rate: self.oracle_price, - last_updated_base: 100, - last_updated_quote: 100, + last_updated: 100, }))), MockQueryMsg::Balance { address: _ } => { SystemResult::Ok(ContractResult::from(to_binary(&cw20::BalanceResponse { diff --git a/packages/mirror_protocol/Cargo.toml b/packages/mirror_protocol/Cargo.toml index 9384301e..b90d534d 100644 --- a/packages/mirror_protocol/Cargo.toml +++ b/packages/mirror_protocol/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "mirror-protocol" -version = "2.1.1" +version = "2.2.0" authors = ["Terraform Labs, PTE."] edition = "2018" description = "Common helpers for mirror-protocol" diff --git a/packages/mirror_protocol/src/collateral_oracle.rs b/packages/mirror_protocol/src/collateral_oracle.rs index d33bd043..9f3ef467 100644 --- a/packages/mirror_protocol/src/collateral_oracle.rs +++ b/packages/mirror_protocol/src/collateral_oracle.rs @@ -9,9 +9,6 @@ pub struct InstantiateMsg { pub owner: String, pub mint_contract: String, pub base_denom: String, - pub mirror_oracle: String, - pub anchor_oracle: String, - pub band_oracle: String, } #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] @@ -21,9 +18,6 @@ pub enum ExecuteMsg { owner: Option, mint_contract: Option, base_denom: Option, - mirror_oracle: Option, - anchor_oracle: Option, - band_oracle: Option, }, RegisterCollateralAsset { asset: AssetInfo, @@ -49,7 +43,7 @@ pub enum QueryMsg { Config {}, CollateralPrice { asset: String, - block_height: Option, + timeframe: Option, }, CollateralAssetInfo { asset: String, @@ -62,9 +56,6 @@ pub struct ConfigResponse { pub owner: String, pub mint_contract: String, pub base_denom: String, - pub mirror_oracle: String, - pub anchor_oracle: String, - pub band_oracle: String, } #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] @@ -91,22 +82,20 @@ pub struct CollateralInfosResponse { #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] pub struct MigrateMsg { - pub lunax_token_addr: String, - pub lunax_staking_contract: String, - pub multiplier: Decimal, + pub mirror_tefi_oracle_addr: String, } #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] #[serde(rename_all = "snake_case")] pub enum SourceType { - MirrorOracle {}, - AnchorOracle {}, - BandOracle {}, + TefiOracle { + oracle_addr: String, + }, FixedPrice { price: Decimal, }, - Terraswap { - terraswap_pair_addr: String, + AmmPair { + pair_addr: String, intermediate_denom: Option, }, AnchorMarket { @@ -123,11 +112,9 @@ pub enum SourceType { impl fmt::Display for SourceType { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match *self { - SourceType::MirrorOracle { .. } => write!(f, "mirror_oracle"), - SourceType::AnchorOracle { .. } => write!(f, "anchor_oracle"), - SourceType::BandOracle { .. } => write!(f, "band_oracle"), + SourceType::TefiOracle { .. } => write!(f, "tefi_oracle"), SourceType::FixedPrice { .. } => write!(f, "fixed_price"), - SourceType::Terraswap { .. } => write!(f, "terraswap"), + SourceType::AmmPair { .. } => write!(f, "amm_pair"), SourceType::AnchorMarket { .. } => write!(f, "anchor_market"), SourceType::Native { .. } => write!(f, "native"), SourceType::Lunax { .. } => write!(f, "lunax"), diff --git a/packages/mirror_protocol/src/factory.rs b/packages/mirror_protocol/src/factory.rs index ccf0a217..8041b380 100644 --- a/packages/mirror_protocol/src/factory.rs +++ b/packages/mirror_protocol/src/factory.rs @@ -39,8 +39,8 @@ pub enum ExecuteMsg { name: String, /// asset symbol used to create token contract symbol: String, - /// authorized asset oracle feeder - oracle_feeder: String, + /// oracle proxy that will provide prices for this asset + oracle_proxy: String, /// used to create all necessary contract or register asset params: Params, }, @@ -48,17 +48,10 @@ pub enum ExecuteMsg { contract_addr: String, msg: Binary, }, - - ////////////////////// - /// Feeder Operations - /// ////////////////// - /// Revoke asset from MIR rewards pool /// and register end_price to mint contract - /// Only feeder can set end_price RevokeAsset { asset_token: String, - end_price: Option, }, /// Migrate asset to new asset by registering /// end_price to mint contract and add @@ -66,8 +59,8 @@ pub enum ExecuteMsg { MigrateAsset { name: String, symbol: String, + oracle_proxy: String, from_token: String, - end_price: Decimal, }, /////////////////// @@ -106,9 +99,10 @@ pub struct DistributionInfoResponse { pub last_distributed: u64, } -/// We currently take no arguments for migrations #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] -pub struct MigrateMsg {} +pub struct MigrateMsg { + pub tefi_oracle_contract: String, +} #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] #[serde(rename_all = "snake_case")] @@ -125,4 +119,6 @@ pub struct Params { pub min_collateral_ratio_after_ipo: Option, /// For pre-IPO assets, fixed price during minting period pub pre_ipo_price: Option, + /// For pre-IPO assets, address authorized to trigger the ipo event + pub ipo_trigger_addr: Option, } diff --git a/packages/mirror_protocol/src/gov.rs b/packages/mirror_protocol/src/gov.rs index 72dc0609..ad54abf3 100644 --- a/packages/mirror_protocol/src/gov.rs +++ b/packages/mirror_protocol/src/gov.rs @@ -193,6 +193,7 @@ pub struct PollResponse { pub total_balance_at_end_poll: Option, pub voters_reward: Uint128, pub staked_amount: Option, + pub admin_action: Option, } #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema)] diff --git a/packages/mirror_protocol/src/lib.rs b/packages/mirror_protocol/src/lib.rs index 7519714b..d5cd0205 100644 --- a/packages/mirror_protocol/src/lib.rs +++ b/packages/mirror_protocol/src/lib.rs @@ -8,6 +8,6 @@ pub mod gov; pub mod limit_order; pub mod lock; pub mod mint; -pub mod oracle; +pub mod oracle; // deprecated pub mod short_reward; pub mod staking; diff --git a/packages/mirror_protocol/src/mint.rs b/packages/mirror_protocol/src/mint.rs index 2342b991..dc5da5da 100644 --- a/packages/mirror_protocol/src/mint.rs +++ b/packages/mirror_protocol/src/mint.rs @@ -104,6 +104,7 @@ pub struct IPOParams { pub mint_end: u64, pub pre_ipo_price: Decimal, pub min_collateral_ratio_after_ipo: Decimal, + pub trigger_addr: String, } #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] @@ -186,3 +187,8 @@ pub struct PositionsResponse { pub struct NextPositionIdxResponse { pub next_position_idx: Uint128, } + +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] +pub struct MigrateMsg { + pub tefi_oracle_contract: String, +} diff --git a/packages/mirror_protocol/src/staking.rs b/packages/mirror_protocol/src/staking.rs index 3d9b564f..9c880ff0 100644 --- a/packages/mirror_protocol/src/staking.rs +++ b/packages/mirror_protocol/src/staking.rs @@ -96,8 +96,7 @@ pub enum Cw20HookMsg { /// We currently take no arguments for migrations #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] pub struct MigrateMsg { - pub asset_token_to_deprecate: String, - pub new_staking_token: String, + pub tefi_oracle_contract: String, } #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] diff --git a/packages/tefi_oracle/Cargo.toml b/packages/tefi_oracle/Cargo.toml deleted file mode 100644 index 59af59b5..00000000 --- a/packages/tefi_oracle/Cargo.toml +++ /dev/null @@ -1,15 +0,0 @@ -[package] -name = "tefi-oracle" -version = "0.1.0" -authors = ["Terraform Labs, PTE."] -edition = "2018" -description = "Common TeFi oracle types" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -cosmwasm-std = { version = "0.16.2" } -schemars = "0.8.1" -serde = { version = "1.0.103", default-features = false, features = ["derive"] } -thiserror = { version = "1.0.23" } -cosmwasm-bignumber = "2.2.0" diff --git a/packages/tefi_oracle/src/de.rs b/packages/tefi_oracle/src/de.rs deleted file mode 100644 index b16a6e80..00000000 --- a/packages/tefi_oracle/src/de.rs +++ /dev/null @@ -1,47 +0,0 @@ -use cosmwasm_std::{Addr, StdError, StdResult}; -use std::array::TryFromSliceError; -use std::convert::TryInto; - -/// This code is mostly just a copy of the necessary functions from storage-plus -/// but not introduced until cw-storage-plus 0.10.0. Can remove this -/// file entirely once we upgrade cw-storage-plus and use the prefix_de/range_de -/// methods instead. - -pub fn deserialize_key(key: Vec) -> StdResult { - K::from_vec(key) -} - -pub trait KeyDeserialize { - type Output: Sized; - - fn from_vec(value: Vec) -> StdResult; -} - -impl KeyDeserialize for u64 { - type Output = u64; - - #[inline(always)] - fn from_vec(value: Vec) -> StdResult { - Ok(u64::from_be_bytes(value.as_slice().try_into().map_err( - |err: TryFromSliceError| StdError::generic_err(err.to_string()), - )?)) - } -} - -impl KeyDeserialize for String { - type Output = String; - - #[inline(always)] - fn from_vec(value: Vec) -> StdResult { - String::from_utf8(value).map_err(StdError::invalid_utf8) - } -} - -impl KeyDeserialize for Addr { - type Output = Addr; - - #[inline(always)] - fn from_vec(value: Vec) -> StdResult { - Ok(Addr::unchecked(String::from_vec(value)?)) - } -} diff --git a/packages/tefi_oracle/src/errors.rs b/packages/tefi_oracle/src/errors.rs deleted file mode 100644 index 2e7e400b..00000000 --- a/packages/tefi_oracle/src/errors.rs +++ /dev/null @@ -1,29 +0,0 @@ -use cosmwasm_std::{OverflowError, StdError}; -use thiserror::Error; - -#[derive(Error, Debug, PartialEq)] -pub enum ContractError { - #[error("{0}")] - Std(#[from] StdError), - - #[error("{0}")] - OverflowError(#[from] OverflowError), - - #[error("Sender is not authorized to execute this operation")] - Unauthorized {}, - - #[error("The proxy address is not registered")] - ProxyNotRegistered {}, - - #[error("The asset token is not registered")] - AssetNotRegistered {}, - - #[error("Quote asset not supported")] - InvalidQuote {}, - - #[error("There is no price available with the requested constrains")] - PriceNotAvailable {}, - - #[error("Proxy error: {reason}")] - ProxyError { reason: String }, -} diff --git a/packages/tefi_oracle/src/hub.rs b/packages/tefi_oracle/src/hub.rs deleted file mode 100644 index 6443f19c..00000000 --- a/packages/tefi_oracle/src/hub.rs +++ /dev/null @@ -1,99 +0,0 @@ -use cosmwasm_bignumber::Decimal256; -use schemars::JsonSchema; -use serde::{Deserialize, Serialize}; - -use cosmwasm_std::Decimal; - -#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] -pub struct InstantiateMsg { - pub owner: String, - pub base_denom: String, -} - -#[derive(Serialize, Deserialize, Clone, PartialEq, JsonSchema, Debug)] -#[serde(rename_all = "snake_case")] -pub enum HubExecuteMsg { - /// Owner operation to update the owner parameter - UpdateOwner { owner: String }, - /// Registers a new proxy contract for an asset_token - RegisterProxy { - asset_token: String, - proxy_addr: String, - priority: Option, - }, - /// Updates the priority paramter of an existing proxy - UpdatePriority { - asset_token: String, - proxy_addr: String, - priority: u8, - }, - /// Remves an already whitelisted proxy - RemoveProxy { - asset_token: String, - proxy_addr: String, - }, -} - -#[derive(Serialize, Deserialize, Clone, PartialEq, JsonSchema, Debug)] -#[serde(rename_all = "snake_case")] -pub enum HubQueryMsg { - /// Queries contract configuration - Config {}, - /// Queries the information of all registered proxies for the provided asset_token - ProxyList { asset_token: String }, - /// Queries the highes priority available price within the timeframe - /// If timeframe is not provided, it will ignore the price age - Price { - asset_token: String, - timeframe: Option, - }, - /// Queries all registered proxy prices for the provied asset_token - PriceList { asset_token: String }, - /// Anchor legacy query interface for oracle prices - LegacyPrice { base: String, quote: String }, -} - -#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] -pub struct ConfigResponse { - pub owner: String, - pub base_denom: String, -} - -#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] -pub struct PriceResponse { - pub rate: Decimal, - pub last_updated: u64, -} - -#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] -pub enum PriceQueryResult { - Success(PriceResponse), - Fail, -} - -#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] -pub struct PriceListResponse { - pub price_list: Vec<(u8, PriceQueryResult)>, // (priority, result) -} - -#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] -pub struct ProxyListResponse { - pub asset_token: String, - pub proxies: Vec<(u8, String)>, -} - -impl From for PriceResponse { - fn from(proxy_res: crate::proxy::ProxyPriceResponse) -> Self { - PriceResponse { - rate: proxy_res.rate, - last_updated: proxy_res.last_updated, - } - } -} - -#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] -pub struct LegacyPriceResponse { - pub rate: Decimal256, - pub last_updated_base: u64, - pub last_updated_quote: u64, -} diff --git a/packages/tefi_oracle/src/lib.rs b/packages/tefi_oracle/src/lib.rs deleted file mode 100644 index 24a9008d..00000000 --- a/packages/tefi_oracle/src/lib.rs +++ /dev/null @@ -1,5 +0,0 @@ -pub mod de; -pub mod errors; -pub mod hub; -pub mod proxy; -pub mod querier; diff --git a/packages/tefi_oracle/src/proxy.rs b/packages/tefi_oracle/src/proxy.rs deleted file mode 100644 index 00fec96f..00000000 --- a/packages/tefi_oracle/src/proxy.rs +++ /dev/null @@ -1,15 +0,0 @@ -use cosmwasm_std::Decimal; -use schemars::JsonSchema; -use serde::{Deserialize, Serialize}; - -#[derive(Serialize, Deserialize, Clone, PartialEq, JsonSchema, Debug)] -#[serde(rename_all = "snake_case")] -pub enum ProxyQueryMsg { - Price { asset_token: String }, -} - -#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema, Default)] -pub struct ProxyPriceResponse { - pub rate: Decimal, // rate denominated in base_denom - pub last_updated: u64, // timestamp in seconds -} diff --git a/packages/tefi_oracle/src/querier.rs b/packages/tefi_oracle/src/querier.rs deleted file mode 100644 index d355a622..00000000 --- a/packages/tefi_oracle/src/querier.rs +++ /dev/null @@ -1,43 +0,0 @@ -use cosmwasm_std::{to_binary, Addr, QuerierWrapper, QueryRequest, StdResult, WasmQuery}; - -use crate::hub::{HubQueryMsg, PriceResponse}; -use crate::proxy::{ProxyPriceResponse, ProxyQueryMsg}; - -/// @dev Queries an asset token price from the orcle proxy contract, price is given in the base denomination -/// @param proxy_addr : Proxy contract address -/// @param asset_token : Asset token address. Native assets are not supported -pub fn query_proxy_asset_price( - querier: &QuerierWrapper, - proxy_addr: &Addr, - asset_token: &Addr, -) -> StdResult { - let res: ProxyPriceResponse = querier.query(&QueryRequest::Wasm(WasmQuery::Smart { - contract_addr: String::from(proxy_addr), - msg: to_binary(&ProxyQueryMsg::Price { - asset_token: String::from(asset_token), - })?, - }))?; - - Ok(res) -} - -/// @dev Queries an asstet token price from hub. Hub contract will redirect the query to the corresponding price source. -/// @param oracle_hub_addr : Oracle hub contract address -/// @param asset_token : Asset token address. Native assets are not supported -/// @param timeframe : (optional) Valid price timeframe in seconds -pub fn query_asset_price( - querier: &QuerierWrapper, - oracle_hub_addr: &Addr, - asset_token: &Addr, - timeframe: Option, -) -> StdResult { - let res: PriceResponse = querier.query(&QueryRequest::Wasm(WasmQuery::Smart { - contract_addr: String::from(oracle_hub_addr), - msg: to_binary(&HubQueryMsg::Price { - asset_token: String::from(asset_token), - timeframe, - })?, - }))?; - - Ok(res) -}