diff --git a/.cargo/audit.toml b/.cargo/audit.toml index d971b765..4c505fae 100644 --- a/.cargo/audit.toml +++ b/.cargo/audit.toml @@ -4,4 +4,4 @@ # Ignore the following advisory IDs. # Reported vulnerabilities relate to test-tube which is only used for testing. # RUSTSEC-2024-0344 - no newer dependency fixing the issue in cosmwasm -ignore = ["RUSTSEC-2024-0003", "RUSTSEC-2024-0006", "RUSTSEC-2024-0019", "RUSTSEC-2024-0332", "RUSTSEC-2024-0336", "RUSTSEC-2024-0344"] \ No newline at end of file +ignore = ["RUSTSEC-2024-0003", "RUSTSEC-2024-0006", "RUSTSEC-2024-0019", "RUSTSEC-2024-0332", "RUSTSEC-2024-0336", "RUSTSEC-2024-0344", "RUSTSEC-2024-0421"] \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index d8a9ccfb..26970090 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2136,6 +2136,7 @@ dependencies = [ "mars-mock-credit-manager", "mars-mock-rover-health", "mars-owner", + "mars-testing", "mars-types", "serde_json", "thiserror", @@ -2590,6 +2591,7 @@ dependencies = [ "mars-owner", "mars-params", "mars-rover-health-computer", + "mars-testing", "mars-types", ] @@ -2735,6 +2737,7 @@ dependencies = [ "cw-storage-plus 1.2.0", "cw-utils 1.0.3", "cw-vault-standard", + "cw2 1.1.2", "cw721 0.18.0", "cw721-base 0.18.0", "mars-owner", diff --git a/contracts/account-nft/Cargo.toml b/contracts/account-nft/Cargo.toml index 703a90d1..f687c410 100644 --- a/contracts/account-nft/Cargo.toml +++ b/contracts/account-nft/Cargo.toml @@ -35,4 +35,5 @@ cw-multi-test = { workspace = true } mars-mock-credit-manager = { workspace = true } mars-mock-rover-health = { workspace = true } mars-owner = { workspace = true } +mars-testing = { workspace = true } serde_json = { workspace = true } diff --git a/contracts/account-nft/src/contract.rs b/contracts/account-nft/src/contract.rs index cc256337..7be32936 100644 --- a/contracts/account-nft/src/contract.rs +++ b/contracts/account-nft/src/contract.rs @@ -10,6 +10,7 @@ use mars_types::account_nft::{ExecuteMsg, InstantiateMsg, NftConfig, QueryMsg}; use crate::{ error::ContractError, execute::{burn, mint, update_config}, + migrations, query::{query_config, query_next_id}, state::{CONFIG, NEXT_ID}, }; @@ -79,3 +80,8 @@ pub fn query(deps: Deps, env: Env, msg: QueryMsg) -> StdResult { _ => Parent::default().query(deps, env, msg.try_into()?), } } + +#[cfg_attr(not(feature = "library"), entry_point)] +pub fn migrate(deps: DepsMut, _env: Env, _msg: Empty) -> Result { + migrations::v2_2_0::migrate(deps) +} diff --git a/contracts/account-nft/src/lib.rs b/contracts/account-nft/src/lib.rs index 512e6c21..0c2c364c 100644 --- a/contracts/account-nft/src/lib.rs +++ b/contracts/account-nft/src/lib.rs @@ -1,5 +1,6 @@ pub mod contract; pub mod error; pub mod execute; +pub mod migrations; pub mod query; pub mod state; diff --git a/contracts/account-nft/src/migrations/mod.rs b/contracts/account-nft/src/migrations/mod.rs new file mode 100644 index 00000000..8fd44388 --- /dev/null +++ b/contracts/account-nft/src/migrations/mod.rs @@ -0,0 +1 @@ +pub mod v2_2_0; diff --git a/contracts/address-provider/src/migrations/v2_0_0.rs b/contracts/account-nft/src/migrations/v2_2_0.rs similarity index 95% rename from contracts/address-provider/src/migrations/v2_0_0.rs rename to contracts/account-nft/src/migrations/v2_2_0.rs index 84fa9745..adef3abf 100644 --- a/contracts/address-provider/src/migrations/v2_0_0.rs +++ b/contracts/account-nft/src/migrations/v2_2_0.rs @@ -6,7 +6,7 @@ use crate::{ error::ContractError, }; -const FROM_VERSION: &str = "1.2.0"; +const FROM_VERSION: &str = "2.1.0"; pub fn migrate(deps: DepsMut) -> Result { // make sure we're migrating the correct contract and from the correct version diff --git a/contracts/account-nft/tests/tests/mod.rs b/contracts/account-nft/tests/tests/mod.rs index 56df371c..ade75fbc 100644 --- a/contracts/account-nft/tests/tests/mod.rs +++ b/contracts/account-nft/tests/tests/mod.rs @@ -2,6 +2,7 @@ mod helpers; mod test_burn_allowance; mod test_instantiate; +mod test_migration_v2; mod test_mint; mod test_proposed_minter; mod test_update_config; diff --git a/contracts/account-nft/tests/tests/test_migration_v2.rs b/contracts/account-nft/tests/tests/test_migration_v2.rs new file mode 100644 index 00000000..4fb78276 --- /dev/null +++ b/contracts/account-nft/tests/tests/test_migration_v2.rs @@ -0,0 +1,60 @@ +use cosmwasm_std::{attr, testing::mock_env, Empty, Event}; +use cw2::{ContractVersion, VersionError}; +use mars_account_nft::{contract::migrate, error::ContractError}; +use mars_testing::mock_dependencies; + +#[test] +fn wrong_contract_name() { + let mut deps = mock_dependencies(&[]); + cw2::set_contract_version(deps.as_mut().storage, "contract_xyz", "2.1.0").unwrap(); + + let err = migrate(deps.as_mut(), mock_env(), Empty {}).unwrap_err(); + + assert_eq!( + err, + ContractError::Version(VersionError::WrongContract { + expected: "crates.io:mars-account-nft".to_string(), + found: "contract_xyz".to_string() + }) + ); +} + +#[test] +fn wrong_contract_version() { + let mut deps = mock_dependencies(&[]); + cw2::set_contract_version(deps.as_mut().storage, "crates.io:mars-account-nft", "4.1.0") + .unwrap(); + + let err = migrate(deps.as_mut(), mock_env(), Empty {}).unwrap_err(); + + assert_eq!( + err, + ContractError::Version(VersionError::WrongVersion { + expected: "2.1.0".to_string(), + found: "4.1.0".to_string() + }) + ); +} + +#[test] +fn successful_migration() { + let mut deps = mock_dependencies(&[]); + cw2::set_contract_version(deps.as_mut().storage, "crates.io:mars-account-nft", "2.1.0") + .unwrap(); + + let res = migrate(deps.as_mut(), mock_env(), Empty {}).unwrap(); + + assert_eq!(res.messages, vec![]); + assert_eq!(res.events, vec![] as Vec); + assert!(res.data.is_none()); + assert_eq!( + res.attributes, + vec![attr("action", "migrate"), attr("from_version", "2.1.0"), attr("to_version", "2.2.0")] + ); + + let new_contract_version = ContractVersion { + contract: "crates.io:mars-account-nft".to_string(), + version: "2.2.0".to_string(), + }; + assert_eq!(cw2::get_contract_version(deps.as_ref().storage).unwrap(), new_contract_version); +} diff --git a/contracts/address-provider/src/contract.rs b/contracts/address-provider/src/contract.rs index 60166ebb..e64cd492 100644 --- a/contracts/address-provider/src/contract.rs +++ b/contracts/address-provider/src/contract.rs @@ -173,5 +173,5 @@ fn query_all_addresses( #[cfg_attr(not(feature = "library"), entry_point)] pub fn migrate(deps: DepsMut, _env: Env, _msg: Empty) -> Result { - migrations::v2_0_0::migrate(deps) + migrations::v2_2_0::migrate(deps) } diff --git a/contracts/address-provider/src/migrations/mod.rs b/contracts/address-provider/src/migrations/mod.rs index 7592b6f1..8fd44388 100644 --- a/contracts/address-provider/src/migrations/mod.rs +++ b/contracts/address-provider/src/migrations/mod.rs @@ -1 +1 @@ -pub mod v2_0_0; +pub mod v2_2_0; diff --git a/contracts/address-provider/src/migrations/v2_2_0.rs b/contracts/address-provider/src/migrations/v2_2_0.rs new file mode 100644 index 00000000..adef3abf --- /dev/null +++ b/contracts/address-provider/src/migrations/v2_2_0.rs @@ -0,0 +1,21 @@ +use cosmwasm_std::{DepsMut, Response}; +use cw2::{assert_contract_version, set_contract_version}; + +use crate::{ + contract::{CONTRACT_NAME, CONTRACT_VERSION}, + error::ContractError, +}; + +const FROM_VERSION: &str = "2.1.0"; + +pub fn migrate(deps: DepsMut) -> Result { + // make sure we're migrating the correct contract and from the correct version + assert_contract_version(deps.storage, &format!("crates.io:{CONTRACT_NAME}"), FROM_VERSION)?; + + set_contract_version(deps.storage, format!("crates.io:{CONTRACT_NAME}"), CONTRACT_VERSION)?; + + Ok(Response::new() + .add_attribute("action", "migrate") + .add_attribute("from_version", FROM_VERSION) + .add_attribute("to_version", CONTRACT_VERSION)) +} diff --git a/contracts/address-provider/tests/tests/test_migration_v2.rs b/contracts/address-provider/tests/tests/test_migration_v2.rs index 3809bc85..667f93ed 100644 --- a/contracts/address-provider/tests/tests/test_migration_v2.rs +++ b/contracts/address-provider/tests/tests/test_migration_v2.rs @@ -6,7 +6,7 @@ use mars_testing::mock_dependencies; #[test] fn wrong_contract_name() { let mut deps = mock_dependencies(&[]); - cw2::set_contract_version(deps.as_mut().storage, "contract_xyz", "1.2.0").unwrap(); + cw2::set_contract_version(deps.as_mut().storage, "contract_xyz", "2.1.0").unwrap(); let err = migrate(deps.as_mut(), mock_env(), Empty {}).unwrap_err(); @@ -30,7 +30,7 @@ fn wrong_contract_version() { assert_eq!( err, ContractError::Version(VersionError::WrongVersion { - expected: "1.2.0".to_string(), + expected: "2.1.0".to_string(), found: "4.1.0".to_string() }) ); @@ -39,7 +39,7 @@ fn wrong_contract_version() { #[test] fn successful_migration() { let mut deps = mock_dependencies(&[]); - cw2::set_contract_version(deps.as_mut().storage, "crates.io:mars-address-provider", "1.2.0") + cw2::set_contract_version(deps.as_mut().storage, "crates.io:mars-address-provider", "2.1.0") .unwrap(); let res = migrate(deps.as_mut(), mock_env(), Empty {}).unwrap(); @@ -49,7 +49,7 @@ fn successful_migration() { assert!(res.data.is_none()); assert_eq!( res.attributes, - vec![attr("action", "migrate"), attr("from_version", "1.2.0"), attr("to_version", "2.2.0")] + vec![attr("action", "migrate"), attr("from_version", "2.1.0"), attr("to_version", "2.2.0")] ); let new_contract_version = ContractVersion { diff --git a/contracts/credit-manager/src/contract.rs b/contracts/credit-manager/src/contract.rs index 7cbc1e31..0a04fff3 100644 --- a/contracts/credit-manager/src/contract.rs +++ b/contracts/credit-manager/src/contract.rs @@ -1,5 +1,5 @@ use cosmwasm_std::{ - entry_point, to_json_binary, Binary, Deps, DepsMut, Env, MessageInfo, Reply, Response, + entry_point, to_json_binary, Binary, Deps, DepsMut, Empty, Env, MessageInfo, Reply, Response, }; use cw2::set_contract_version; use mars_types::{ @@ -12,6 +12,7 @@ use crate::{ error::{ContractError, ContractResult}, execute::{create_credit_account, dispatch_actions, execute_callback}, instantiate::store_config, + migrations, perp::update_balance_after_deleverage, query::{ query_accounts, query_all_coin_balances, query_all_debt_shares, @@ -174,3 +175,8 @@ pub fn query(deps: Deps, env: Env, msg: QueryMsg) -> ContractResult { }; res.map_err(Into::into) } + +#[cfg_attr(not(feature = "library"), entry_point)] +pub fn migrate(deps: DepsMut, _env: Env, _msg: Empty) -> Result { + migrations::v2_2_0::migrate(deps) +} diff --git a/contracts/credit-manager/src/lib.rs b/contracts/credit-manager/src/lib.rs index b84585c2..ca5634de 100644 --- a/contracts/credit-manager/src/lib.rs +++ b/contracts/credit-manager/src/lib.rs @@ -13,6 +13,7 @@ pub mod liquidate; pub mod liquidate_astro_lp; pub mod liquidate_deposit; pub mod liquidate_lend; +pub mod migrations; pub mod perp; pub mod perp_vault; pub mod query; diff --git a/contracts/credit-manager/src/migrations/mod.rs b/contracts/credit-manager/src/migrations/mod.rs new file mode 100644 index 00000000..8fd44388 --- /dev/null +++ b/contracts/credit-manager/src/migrations/mod.rs @@ -0,0 +1 @@ +pub mod v2_2_0; diff --git a/contracts/credit-manager/src/migrations/v2_2_0.rs b/contracts/credit-manager/src/migrations/v2_2_0.rs new file mode 100644 index 00000000..c93c5ff7 --- /dev/null +++ b/contracts/credit-manager/src/migrations/v2_2_0.rs @@ -0,0 +1,24 @@ +use cosmwasm_std::{DepsMut, Response}; +use cw2::{assert_contract_version, set_contract_version}; + +use crate::{ + contract::{CONTRACT_NAME, CONTRACT_VERSION}, + error::ContractError, + state::NEXT_TRIGGER_ID, +}; + +const FROM_VERSION: &str = "2.1.0"; + +pub fn migrate(deps: DepsMut) -> Result { + // make sure we're migrating the correct contract and from the correct version + assert_contract_version(deps.storage, &format!("crates.io:{CONTRACT_NAME}"), FROM_VERSION)?; + + NEXT_TRIGGER_ID.save(deps.storage, &1)?; + + set_contract_version(deps.storage, format!("crates.io:{CONTRACT_NAME}"), CONTRACT_VERSION)?; + + Ok(Response::new() + .add_attribute("action", "migrate") + .add_attribute("from_version", FROM_VERSION) + .add_attribute("to_version", CONTRACT_VERSION)) +} diff --git a/contracts/credit-manager/tests/tests/mod.rs b/contracts/credit-manager/tests/tests/mod.rs index 8124af10..e6f7ae5b 100644 --- a/contracts/credit-manager/tests/tests/mod.rs +++ b/contracts/credit-manager/tests/tests/mod.rs @@ -25,6 +25,7 @@ mod test_liquidate_lend; mod test_liquidate_staked_astro_lp; mod test_liquidate_vault; mod test_liquidation_pricing; +mod test_migration_v2; mod test_no_health_check; mod test_perp; mod test_perp_vault; diff --git a/contracts/credit-manager/tests/tests/test_migration_v2.rs b/contracts/credit-manager/tests/tests/test_migration_v2.rs new file mode 100644 index 00000000..46e9c45b --- /dev/null +++ b/contracts/credit-manager/tests/tests/test_migration_v2.rs @@ -0,0 +1,63 @@ +use cosmwasm_std::{attr, testing::mock_env, Empty, Event}; +use cw2::{ContractVersion, VersionError}; +use mars_credit_manager::{contract::migrate, error::ContractError, state::NEXT_TRIGGER_ID}; +use mars_testing::mock_dependencies; + +#[test] +fn wrong_contract_name() { + let mut deps = mock_dependencies(&[]); + cw2::set_contract_version(deps.as_mut().storage, "contract_xyz", "2.1.0").unwrap(); + + let err = migrate(deps.as_mut(), mock_env(), Empty {}).unwrap_err(); + + assert_eq!( + err, + ContractError::Version(VersionError::WrongContract { + expected: "crates.io:mars-credit-manager".to_string(), + found: "contract_xyz".to_string() + }) + ); +} + +#[test] +fn wrong_contract_version() { + let mut deps = mock_dependencies(&[]); + cw2::set_contract_version(deps.as_mut().storage, "crates.io:mars-credit-manager", "4.1.0") + .unwrap(); + + let err = migrate(deps.as_mut(), mock_env(), Empty {}).unwrap_err(); + + assert_eq!( + err, + ContractError::Version(VersionError::WrongVersion { + expected: "2.1.0".to_string(), + found: "4.1.0".to_string() + }) + ); +} + +#[test] +fn successful_migration() { + let mut deps = mock_dependencies(&[]); + cw2::set_contract_version(deps.as_mut().storage, "crates.io:mars-credit-manager", "2.1.0") + .unwrap(); + + let res = migrate(deps.as_mut(), mock_env(), Empty {}).unwrap(); + + let order_id = NEXT_TRIGGER_ID.load(deps.as_ref().storage).unwrap(); + assert_eq!(order_id, 1); + + assert_eq!(res.messages, vec![]); + assert_eq!(res.events, vec![] as Vec); + assert!(res.data.is_none()); + assert_eq!( + res.attributes, + vec![attr("action", "migrate"), attr("from_version", "2.1.0"), attr("to_version", "2.2.0")] + ); + + let new_contract_version = ContractVersion { + contract: "crates.io:mars-credit-manager".to_string(), + version: "2.2.0".to_string(), + }; + assert_eq!(cw2::get_contract_version(deps.as_ref().storage).unwrap(), new_contract_version); +} diff --git a/contracts/health/Cargo.toml b/contracts/health/Cargo.toml index 9b9579d8..84e459d7 100644 --- a/contracts/health/Cargo.toml +++ b/contracts/health/Cargo.toml @@ -34,5 +34,6 @@ cw-utils = { workspace = true } cw-vault-standard = { workspace = true } mars-mock-credit-manager = { workspace = true } mars-mock-oracle = { workspace = true } -mars-params = { workspace = true } mars-mock-vault = { workspace = true } +mars-params = { workspace = true } +mars-testing = { workspace = true } diff --git a/contracts/health/src/contract.rs b/contracts/health/src/contract.rs index ac50009d..840c62c5 100644 --- a/contracts/health/src/contract.rs +++ b/contracts/health/src/contract.rs @@ -1,18 +1,19 @@ #[cfg(not(feature = "library"))] use cosmwasm_std::entry_point; -use cosmwasm_std::{to_json_binary, Binary, Deps, DepsMut, Env, MessageInfo, Response}; +use cosmwasm_std::{to_json_binary, Binary, Deps, DepsMut, Empty, Env, MessageInfo, Response}; use cw2::set_contract_version; use mars_owner::OwnerInit::SetInitialOwner; use mars_types::health::{ConfigResponse, ExecuteMsg, HealthResult, InstantiateMsg, QueryMsg}; use crate::{ compute::{health_state, health_values}, + migrations, state::{CREDIT_MANAGER, OWNER}, update_config::update_config, }; -const CONTRACT_NAME: &str = env!("CARGO_PKG_NAME"); -const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION"); +pub const CONTRACT_NAME: &str = env!("CARGO_PKG_NAME"); +pub const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION"); #[cfg_attr(not(feature = "library"), entry_point)] pub fn instantiate( @@ -79,3 +80,8 @@ pub fn query_config(deps: Deps) -> HealthResult { owner_response, }) } + +#[cfg_attr(not(feature = "library"), entry_point)] +pub fn migrate(deps: DepsMut, _env: Env, _msg: Empty) -> HealthResult { + migrations::v2_2_0::migrate(deps) +} diff --git a/contracts/health/src/lib.rs b/contracts/health/src/lib.rs index b202a2fa..57c6a07e 100644 --- a/contracts/health/src/lib.rs +++ b/contracts/health/src/lib.rs @@ -1,5 +1,6 @@ pub mod compute; pub mod contract; +pub mod migrations; pub mod querier; pub mod state; pub mod update_config; diff --git a/contracts/health/src/migrations/mod.rs b/contracts/health/src/migrations/mod.rs new file mode 100644 index 00000000..8fd44388 --- /dev/null +++ b/contracts/health/src/migrations/mod.rs @@ -0,0 +1 @@ +pub mod v2_2_0; diff --git a/contracts/health/src/migrations/v2_2_0.rs b/contracts/health/src/migrations/v2_2_0.rs new file mode 100644 index 00000000..a76a4f90 --- /dev/null +++ b/contracts/health/src/migrations/v2_2_0.rs @@ -0,0 +1,19 @@ +use cosmwasm_std::{DepsMut, Response}; +use cw2::{assert_contract_version, set_contract_version}; +use mars_types::health::HealthResult; + +use crate::contract::{CONTRACT_NAME, CONTRACT_VERSION}; + +const FROM_VERSION: &str = "2.1.0"; + +pub fn migrate(deps: DepsMut) -> HealthResult { + // make sure we're migrating the correct contract and from the correct version + assert_contract_version(deps.storage, &format!("crates.io:{CONTRACT_NAME}"), FROM_VERSION)?; + + set_contract_version(deps.storage, format!("crates.io:{CONTRACT_NAME}"), CONTRACT_VERSION)?; + + Ok(Response::new() + .add_attribute("action", "migrate") + .add_attribute("from_version", FROM_VERSION) + .add_attribute("to_version", CONTRACT_VERSION)) +} diff --git a/contracts/health/tests/tests/mod.rs b/contracts/health/tests/tests/mod.rs index 76b4263b..7055c2c6 100644 --- a/contracts/health/tests/tests/mod.rs +++ b/contracts/health/tests/tests/mod.rs @@ -5,4 +5,5 @@ mod test_health_values; mod test_hls; mod test_instantiate; mod test_liquidation_pricing; +mod test_migration_v2; mod test_update_config; diff --git a/contracts/health/tests/tests/test_migration_v2.rs b/contracts/health/tests/tests/test_migration_v2.rs new file mode 100644 index 00000000..c8461611 --- /dev/null +++ b/contracts/health/tests/tests/test_migration_v2.rs @@ -0,0 +1,61 @@ +use cosmwasm_std::{attr, testing::mock_env, Empty, Event}; +use cw2::{ContractVersion, VersionError}; +use mars_rover_health::contract::migrate; +use mars_testing::mock_dependencies; +use mars_types::health::HealthError; + +#[test] +fn wrong_contract_name() { + let mut deps = mock_dependencies(&[]); + cw2::set_contract_version(deps.as_mut().storage, "contract_xyz", "2.1.0").unwrap(); + + let err = migrate(deps.as_mut(), mock_env(), Empty {}).unwrap_err(); + + assert_eq!( + err, + HealthError::Version(VersionError::WrongContract { + expected: "crates.io:mars-rover-health".to_string(), + found: "contract_xyz".to_string() + }) + ); +} + +#[test] +fn wrong_contract_version() { + let mut deps = mock_dependencies(&[]); + cw2::set_contract_version(deps.as_mut().storage, "crates.io:mars-rover-health", "4.1.0") + .unwrap(); + + let err = migrate(deps.as_mut(), mock_env(), Empty {}).unwrap_err(); + + assert_eq!( + err, + HealthError::Version(VersionError::WrongVersion { + expected: "2.1.0".to_string(), + found: "4.1.0".to_string() + }) + ); +} + +#[test] +fn successful_migration() { + let mut deps = mock_dependencies(&[]); + cw2::set_contract_version(deps.as_mut().storage, "crates.io:mars-rover-health", "2.1.0") + .unwrap(); + + let res = migrate(deps.as_mut(), mock_env(), Empty {}).unwrap(); + + assert_eq!(res.messages, vec![]); + assert_eq!(res.events, vec![] as Vec); + assert!(res.data.is_none()); + assert_eq!( + res.attributes, + vec![attr("action", "migrate"), attr("from_version", "2.1.0"), attr("to_version", "2.2.0")] + ); + + let new_contract_version = ContractVersion { + contract: "crates.io:mars-rover-health".to_string(), + version: "2.2.0".to_string(), + }; + assert_eq!(cw2::get_contract_version(deps.as_ref().storage).unwrap(), new_contract_version); +} diff --git a/contracts/incentives/src/contract.rs b/contracts/incentives/src/contract.rs index 87ae6a03..db1e029d 100644 --- a/contracts/incentives/src/contract.rs +++ b/contracts/incentives/src/contract.rs @@ -158,7 +158,7 @@ pub fn execute( max_whitelisted_denoms, )?), ExecuteMsg::UpdateOwner(update) => config::update_owner(deps, info, update), - ExecuteMsg::Migrate(msg) => migrations::v2_0_1::execute_migration(deps, info, msg), + ExecuteMsg::Migrate(msg) => migrations::v2_2_0::execute_migration(deps, info, msg), } } @@ -266,5 +266,5 @@ pub fn query(deps: Deps, env: Env, msg: QueryMsg) -> StdResult { /// MIGRATION #[cfg_attr(not(feature = "library"), entry_point)] pub fn migrate(deps: DepsMut, env: Env, msg: Empty) -> Result { - migrations::v2_0_1::migrate(deps, env, msg) + migrations::v2_2_0::migrate(deps, env, msg) } diff --git a/contracts/incentives/src/migrations/mod.rs b/contracts/incentives/src/migrations/mod.rs index 04e6bfcb..8fd44388 100644 --- a/contracts/incentives/src/migrations/mod.rs +++ b/contracts/incentives/src/migrations/mod.rs @@ -1 +1 @@ -pub mod v2_0_1; +pub mod v2_2_0; diff --git a/contracts/incentives/src/migrations/v2_0_1.rs b/contracts/incentives/src/migrations/v2_2_0.rs similarity index 67% rename from contracts/incentives/src/migrations/v2_0_1.rs rename to contracts/incentives/src/migrations/v2_2_0.rs index 84750093..d5cb3e67 100644 --- a/contracts/incentives/src/migrations/v2_0_1.rs +++ b/contracts/incentives/src/migrations/v2_2_0.rs @@ -2,7 +2,7 @@ use cosmwasm_std::{DepsMut, Empty, Env, MessageInfo, Order, Response, StdResult} use cw2::{assert_contract_version, set_contract_version}; use cw_storage_plus::Bound; use mars_types::{ - incentives::{IncentiveKind, MigrateV2ToV2_0_1}, + incentives::{IncentiveKind, MigrateV2_1_0ToV2_2_0}, keys::{IncentiveId, IncentiveIdKey, IncentiveKindKey, UserId, UserIdKey}, }; @@ -10,12 +10,31 @@ use crate::{ contract::{CONTRACT_NAME, CONTRACT_VERSION}, error::ContractError, state::{ - EMISSIONS, INCENTIVE_STATES, MIGRATION_GUARD, OWNER, USER_ASSET_INDICES, - USER_UNCLAIMED_REWARDS, + ASTRO_USER_LP_DEPOSITS, EMISSIONS, INCENTIVE_STATES, MIGRATION_GUARD, OWNER, + USER_ASSET_INDICES, USER_ASTRO_INCENTIVE_STATES, USER_UNCLAIMED_REWARDS, }, }; -const FROM_VERSION: &str = "2.0.0"; +const FROM_VERSION: &str = "2.1.0"; + +pub mod v1_state { + use cosmwasm_std::{Addr, Decimal, DepsMut, Uint128}; + use cw_storage_plus::Map; + + /// Don't care about the actual types, just use some dummy types to clear the storage + pub const ASSET_INCENTIVES: Map<&str, String> = Map::new("incentives"); + pub const USER_ASSET_INDICES: Map<(&Addr, &str), Decimal> = Map::new("indices"); + pub const USER_UNCLAIMED_REWARDS: Map<&Addr, Uint128> = Map::new("unclaimed_rewards"); + pub const USER_UNCLAIMED_REWARDS_BACKUP: Map<&Addr, Uint128> = Map::new("ur_backup"); + + /// Clear old state so we can re-use the keys + pub fn clear_state(deps: &mut DepsMut) { + ASSET_INCENTIVES.clear(deps.storage); + USER_ASSET_INDICES.clear(deps.storage); + USER_UNCLAIMED_REWARDS.clear(deps.storage); + USER_UNCLAIMED_REWARDS_BACKUP.clear(deps.storage); + } +} pub mod v2_state { use cosmwasm_std::{Decimal, Uint128}; @@ -27,6 +46,15 @@ pub mod v2_state { pub const USER_ASSET_INDICES: Map<(&UserIdKey, &str, &str), Decimal> = Map::new("indices_v2"); pub const USER_UNCLAIMED_REWARDS: Map<(&UserIdKey, &str, &str), Uint128> = Map::new("unclaimed_rewards_v2"); + + // Map of User Lp deposits. Key is (user_id, lp_denom) + pub const ASTRO_USER_LP_DEPOSITS: Map<(&str, &str), Uint128> = Map::new("lp_deposits"); + + /// A map containing the individual incentive index for each unique user + /// Note - this may contain many denoms for one user + /// The key is (account_id, lp_token_denom, reward_denom) + pub const USER_ASTRO_INCENTIVE_STATES: Map<(&str, &str, &str), Decimal> = + Map::new("user_astroport_incentive_states"); } pub fn migrate(mut deps: DepsMut, _env: Env, _msg: Empty) -> Result { @@ -34,6 +62,9 @@ pub fn migrate(mut deps: DepsMut, _env: Env, _msg: Empty) -> Result Result Result<(), ContractError> { pub fn execute_migration( deps: DepsMut, info: MessageInfo, - msg: MigrateV2ToV2_0_1, + msg: MigrateV2_1_0ToV2_2_0, ) -> Result { match msg { - MigrateV2ToV2_0_1::UserUnclaimedRewards { + MigrateV2_1_0ToV2_2_0::UserUnclaimedRewards { limit, } => migrate_user_unclaimed_rewards(deps, limit as usize), - MigrateV2ToV2_0_1::UserAssetIndices { + MigrateV2_1_0ToV2_2_0::UserAssetIndices { limit, } => migrate_user_asset_indices(deps, limit as usize), - MigrateV2ToV2_0_1::ClearV2State {} => { + MigrateV2_1_0ToV2_2_0::ClearV2State {} => { OWNER.assert_owner(deps.storage, &info.sender)?; clear_v2_state(deps) } @@ -151,6 +184,44 @@ fn migrate_user_unclaimed_rewards(deps: DepsMut, limit: usize) -> Result Result<(), ContractError> { + // Collect all LP positions that are zero + let zero_balance_items = ASTRO_USER_LP_DEPOSITS + .range(deps.storage, None, None, Order::Ascending) + .filter_map(|item| match item { + Ok(((user_id, account), value)) if value.is_zero() => { + Some(Ok(((user_id.to_string(), account.to_string()), value))) + } + Ok(_) => None, + Err(e) => Some(Err(e)), + }) + .collect::>>()?; + + // Iterate all LP positions that are zero, and delete the associated incentive indexes + for ((account_id, denom), _) in zero_balance_items.iter() { + ASTRO_USER_LP_DEPOSITS.remove(deps.storage, (account_id, denom)); + + // Get all incentives for (user, lp_token_denom) key + let prefix = USER_ASTRO_INCENTIVE_STATES.prefix((account_id, denom)); + + // Iterate over all reward_denom keys + let keys_to_remove = prefix + .keys(deps.storage, None, None, Order::Ascending) + .collect::>>()?; + + // Delete each matching (account_id, lp_token_denom, reward_denom) incentive index. + for incentive_denom in keys_to_remove { + USER_ASTRO_INCENTIVE_STATES + .remove(deps.storage, (account_id, denom.as_str(), &incentive_denom)); + } + } + + Ok(()) +} + fn migrate_user_asset_indices(deps: DepsMut, limit: usize) -> Result { // Only allow to migrate users asset indices if guard is locked via `migrate` entrypoint MIGRATION_GUARD.assert_locked(deps.storage)?; @@ -240,12 +311,15 @@ fn clear_v2_state(deps: DepsMut) -> Result { #[cfg(test)] pub mod tests { + use std::str::FromStr; + use cosmwasm_std::{attr, testing::mock_dependencies, Addr, Decimal, Uint128}; use mars_types::incentives::IncentiveState; use mars_utils::error::GuardError; use super::*; use crate::error::ContractError; + #[test] fn cannot_migrate_without_lock() { let mut deps = mock_dependencies(); @@ -295,6 +369,231 @@ pub mod tests { ); } + #[test] + fn clear_zero_amounts_in_staked_astro_lps() { + let mut deps = mock_dependencies(); + + MIGRATION_GUARD.try_lock(deps.as_mut().storage).unwrap(); + + let lp_denom_1 = "factory/neutronasdfkldshfkldsjfklfdsaaaaassss111/astroport/share"; + let lp_denom_2 = "factory/neutronasdfkldshfkldsjfklfdsfdsfdsfd2222/astroport/share"; + let reward_denom_1 = "untrn"; + let reward_denom_2 = "ibc/D189335C6E4A68B513C10AB227BF1C1D38C746766278BA3EEB4FB14124F1D858"; + + // + // Instantiate Deposits + // + + // User 1 + // Lp_denom 1 has balance > 0 + // LP_denom 2 balance of 0 + v2_state::ASTRO_USER_LP_DEPOSITS + .save(deps.as_mut().storage, ("1", lp_denom_1), &Uint128::new(100000000)) + .unwrap(); + v2_state::ASTRO_USER_LP_DEPOSITS + .save(deps.as_mut().storage, ("1", lp_denom_2), &Uint128::new(0)) + .unwrap(); + + // User 2 + // Lp_denom 1 has balance of 0 + // LP_denom 2 balance > 0 + v2_state::ASTRO_USER_LP_DEPOSITS + .save(deps.as_mut().storage, ("2", lp_denom_1), &Uint128::new(0)) + .unwrap(); + + v2_state::ASTRO_USER_LP_DEPOSITS + .save(deps.as_mut().storage, ("2", lp_denom_2), &Uint128::new(100000000)) + .unwrap(); + + // User 3 + // Lp_denom 1 has balance > 0 + v2_state::ASTRO_USER_LP_DEPOSITS + .save(deps.as_mut().storage, ("3", lp_denom_1), &Uint128::new(100000000)) + .unwrap(); + + // User 4 + // Lp_denom 1 has balance of 0 + v2_state::ASTRO_USER_LP_DEPOSITS + .save(deps.as_mut().storage, ("4", lp_denom_1), &Uint128::new(0)) + .unwrap(); + + // User 5 + // Lp_denom 1 has balance > 0 + // Lp_denom 2 has balance > 0 + v2_state::ASTRO_USER_LP_DEPOSITS + .save(deps.as_mut().storage, ("5", lp_denom_1), &Uint128::new(100000000)) + .unwrap(); + v2_state::ASTRO_USER_LP_DEPOSITS + .save(deps.as_mut().storage, ("5", lp_denom_2), &Uint128::new(100000000)) + .unwrap(); + + // + // Instantiate user reward states + // + + // User 1 + v2_state::USER_ASTRO_INCENTIVE_STATES + .save( + deps.as_mut().storage, + ("1", lp_denom_1, reward_denom_2), + &Decimal::from_str("1.0001456").unwrap(), + ) + .unwrap(); + + v2_state::USER_ASTRO_INCENTIVE_STATES + .save( + deps.as_mut().storage, + ("1", lp_denom_1, reward_denom_1), + &Decimal::from_str("1.0001456").unwrap(), + ) + .unwrap(); + + v2_state::USER_ASTRO_INCENTIVE_STATES + .save( + deps.as_mut().storage, + ("1", lp_denom_2, reward_denom_1), + &Decimal::from_str("1.21456").unwrap(), + ) + .unwrap(); + v2_state::USER_ASTRO_INCENTIVE_STATES + .save( + deps.as_mut().storage, + ("1", lp_denom_2, reward_denom_2), + &Decimal::from_str("1.21456").unwrap(), + ) + .unwrap(); + + // User 2 + v2_state::USER_ASTRO_INCENTIVE_STATES + .save( + deps.as_mut().storage, + ("2", lp_denom_1, reward_denom_2), + &Decimal::from_str("1.0001456").unwrap(), + ) + .unwrap(); + + v2_state::USER_ASTRO_INCENTIVE_STATES + .save( + deps.as_mut().storage, + ("2", lp_denom_2, reward_denom_1), + &Decimal::from_str("1.0001456").unwrap(), + ) + .unwrap(); + + // User 3 + v2_state::USER_ASTRO_INCENTIVE_STATES + .save( + deps.as_mut().storage, + ("3", lp_denom_1, reward_denom_2), + &Decimal::from_str("1.0001456").unwrap(), + ) + .unwrap(); + v2_state::USER_ASTRO_INCENTIVE_STATES + .save( + deps.as_mut().storage, + ("3", lp_denom_1, reward_denom_1), + &Decimal::from_str("1.0001456").unwrap(), + ) + .unwrap(); + + // User 4 no incentive states + + // User 5 - only 1 reward asset + v2_state::USER_ASTRO_INCENTIVE_STATES + .save( + deps.as_mut().storage, + ("5", lp_denom_1, reward_denom_2), + &Decimal::from_str("1.0001456").unwrap(), + ) + .unwrap(); + v2_state::USER_ASTRO_INCENTIVE_STATES + .save( + deps.as_mut().storage, + ("5", lp_denom_2, reward_denom_2), + &Decimal::from_str("1.0001456").unwrap(), + ) + .unwrap(); + + // Assert user positions before + let user_deposits_before = v2_state::ASTRO_USER_LP_DEPOSITS + .range(deps.as_ref().storage, None, None, Order::Ascending) + .collect::>>() + .unwrap(); + + assert_eq!(user_deposits_before.len(), 8); + assert_eq!(user_deposits_before[0].0, ("1".to_string(), lp_denom_1.to_string())); + assert_eq!(user_deposits_before[1].0, ("1".to_string(), lp_denom_2.to_string())); + assert_eq!(user_deposits_before[2].0, ("2".to_string(), lp_denom_1.to_string())); + assert_eq!(user_deposits_before[3].0, ("2".to_string(), lp_denom_2.to_string())); + assert_eq!(user_deposits_before[4].0, ("3".to_string(), lp_denom_1.to_string())); + assert_eq!(user_deposits_before[5].0, ("4".to_string(), lp_denom_1.to_string())); + assert_eq!(user_deposits_before[6].0, ("5".to_string(), lp_denom_1.to_string())); + assert_eq!(user_deposits_before[7].0, ("5".to_string(), lp_denom_2.to_string())); + + let incentive_states_before = v2_state::USER_ASTRO_INCENTIVE_STATES + .range(deps.as_ref().storage, None, None, Order::Ascending) + .collect::>>() + .unwrap(); + + // Assert all incentives are there + assert_eq!(incentive_states_before.len(), 10); + + // Clear balances + clear_zero_amounts_in_staked_astro_lp(&mut deps.as_mut()).unwrap(); + + let user_deposits_after = v2_state::ASTRO_USER_LP_DEPOSITS + .range(deps.as_ref().storage, None, None, Order::Ascending) + .collect::>>() + .unwrap(); + + assert_eq!(user_deposits_after[0].0, ("1".to_string(), lp_denom_1.to_string())); + assert_eq!(user_deposits_after[1].0, ("2".to_string(), lp_denom_2.to_string())); + assert_eq!(user_deposits_after[2].0, ("3".to_string(), lp_denom_1.to_string())); + assert_eq!(user_deposits_after[3].0, ("5".to_string(), lp_denom_1.to_string())); + assert_eq!(user_deposits_after[4].0, ("5".to_string(), lp_denom_2.to_string())); + + // Incentive records that should be cleared + // (user_1,lp_denom_2) + // - both incentives (2 records deleted) + + // (User 2, lp_denom_1) + // - one incentive + let incentive_states_after = v2_state::USER_ASTRO_INCENTIVE_STATES + .range(deps.as_ref().storage, None, None, Order::Ascending) + .collect::>>() + .unwrap(); + + assert_eq!(incentive_states_after.len(), 7); // because we deleted 3 records + assert_eq!( + incentive_states_after[0].0, + ("1".to_string(), lp_denom_1.to_string(), reward_denom_2.to_string()) + ); + assert_eq!( + incentive_states_after[1].0, + ("1".to_string(), lp_denom_1.to_string(), reward_denom_1.to_string()) + ); + assert_eq!( + incentive_states_after[2].0, + ("2".to_string(), lp_denom_2.to_string(), reward_denom_1.to_string()) + ); + assert_eq!( + incentive_states_after[3].0, + ("3".to_string(), lp_denom_1.to_string(), reward_denom_2.to_string()) + ); + assert_eq!( + incentive_states_after[4].0, + ("3".to_string(), lp_denom_1.to_string(), reward_denom_1.to_string()) + ); + assert_eq!( + incentive_states_after[5].0, + ("5".to_string(), lp_denom_1.to_string(), reward_denom_2.to_string()) + ); + assert_eq!( + incentive_states_after[6].0, + ("5".to_string(), lp_denom_2.to_string(), reward_denom_2.to_string()) + ); + } + #[test] fn migrate_v2_user_unclaimed_rewards() { let mut deps = mock_dependencies(); diff --git a/contracts/incentives/src/state.rs b/contracts/incentives/src/state.rs index 007fcba0..03d79107 100644 --- a/contracts/incentives/src/state.rs +++ b/contracts/incentives/src/state.rs @@ -30,7 +30,7 @@ pub const WHITELIST_COUNT: Item = Item::new("whitelist_count"); /// A map containing the incentive index and last updated time for a given collateral and incentive /// denom. The key is (incentive kind, collateral denom, incentive denom). pub const INCENTIVE_STATES: Map<(&IncentiveKindKey, &str, &str), IncentiveState> = - Map::new("incentive_states_v2_0_1"); + Map::new("incentive_states_v2_2_0"); /// A map containing the global incentive index for a given lp token /// The key is (lp token denom, incentive denom). @@ -45,17 +45,17 @@ pub const USER_ASTRO_INCENTIVE_STATES: Map<(&str, &str, &str), Decimal> = /// A map containing emission speeds (incentive tokens per second) for a given collateral and /// incentive denom. The key is (incentive id (kind + col denom), incentive denom, schedule start time). -pub const EMISSIONS: Map<(&IncentiveIdKey, &str, u64), Uint128> = Map::new("emissions_v2_0_1"); +pub const EMISSIONS: Map<(&IncentiveIdKey, &str, u64), Uint128> = Map::new("emissions_v2_2_0"); /// A map containing the incentive index for a given user, collateral denom and incentive denom. /// The key is (user address with optional account id, incentive id (kind + col denom), incentive denom). pub const USER_ASSET_INDICES: Map<(&UserIdKey, &IncentiveIdKey, &str), Decimal> = - Map::new("indices_v2_0_1"); + Map::new("indices_v2_2_0"); /// A map containing the amount of unclaimed incentives for a given user and incentive denom. /// The key is (user address with optional account id, incentive id (kind + col denom), incentive denom). pub const USER_UNCLAIMED_REWARDS: Map<(&UserIdKey, &IncentiveIdKey, &str), Uint128> = - Map::new("unclaimed_rewards_v2_0_1"); + Map::new("unclaimed_rewards_v2_2_0"); /// Used to mark the contract as locked during migrations pub const MIGRATION_GUARD: Guard = Guard::new("guard"); diff --git a/contracts/incentives/tests/tests/mod.rs b/contracts/incentives/tests/tests/mod.rs index 808f186e..a3f66538 100644 --- a/contracts/incentives/tests/tests/mod.rs +++ b/contracts/incentives/tests/tests/mod.rs @@ -5,6 +5,7 @@ mod test_balance_change; mod test_claim_astro_lp_rewards; mod test_claim_rewards; mod test_indices_usage; +mod test_migration_v2; mod test_quering; mod test_set_asset_incentive; mod test_update_owner; diff --git a/contracts/incentives/tests/tests/test_migration_v2.rs b/contracts/incentives/tests/tests/test_migration_v2.rs index 171184be..a463d605 100644 --- a/contracts/incentives/tests/tests/test_migration_v2.rs +++ b/contracts/incentives/tests/tests/test_migration_v2.rs @@ -1,28 +1,12 @@ -use std::{collections::HashMap, str::FromStr}; - -use cosmwasm_std::{ - attr, - testing::{mock_env, mock_info}, - Addr, Decimal, Empty, Event, Order, StdResult, Uint128, -}; -use cw2::{ContractVersion, VersionError}; -use mars_incentives::{ - contract::{execute, migrate}, - migrations::v2_0_0::v1_state, - state::{MIGRATION_GUARD, OWNER, USER_ASSET_INDICES, USER_UNCLAIMED_REWARDS}, - ContractError, -}; +use cosmwasm_std::{testing::mock_env, Empty}; +use cw2::VersionError; +use mars_incentives::{contract::migrate, ContractError}; use mars_testing::mock_dependencies; -use mars_types::{ - incentives::{ExecuteMsg, MigrateV1ToV2}, - keys::{UserId, UserIdKey}, -}; -use mars_utils::error::GuardError; #[test] fn wrong_contract_name() { let mut deps = mock_dependencies(&[]); - cw2::set_contract_version(deps.as_mut().storage, "contract_xyz", "1.2.0").unwrap(); + cw2::set_contract_version(deps.as_mut().storage, "contract_xyz", "2.1.0").unwrap(); let err = migrate(deps.as_mut(), mock_env(), Empty {}).unwrap_err(); @@ -45,339 +29,8 @@ fn wrong_contract_version() { assert_eq!( err, ContractError::Version(VersionError::WrongVersion { - expected: "1.2.0".to_string(), + expected: "2.1.0".to_string(), found: "4.1.0".to_string() }) ); } - -#[test] -fn full_migration() { - let mut deps = mock_dependencies(&[]); - cw2::set_contract_version(deps.as_mut().storage, "crates.io:mars-incentives", "1.2.0").unwrap(); - - let old_owner = "spiderman_246"; - let deps_muted = deps.as_mut(); - OWNER - .initialize( - deps_muted.storage, - deps_muted.api, - mars_owner::OwnerInit::SetInitialOwner { - owner: old_owner.to_string(), - }, - ) - .unwrap(); - - let atom_denom = "uatom"; - let usdc_denom = "uusdc"; - let osmo_denom = "uosmo"; - - let mars_incentive_denom = "umars"; - let astro_incentive_denom = "uastro"; - - // Set user asset indices for all incentive assets - let user_1 = Addr::unchecked("user_1"); - let user_1_atom_idx_old = Decimal::one(); - v1_state::USER_ASSET_INDICES - .save( - deps.as_mut().storage, - (&user_1, atom_denom, mars_incentive_denom), - &user_1_atom_idx_old, - ) - .unwrap(); - let user_1_usdc_idx_old = Decimal::from_str("2.2356").unwrap(); - v1_state::USER_ASSET_INDICES - .save( - deps.as_mut().storage, - (&user_1, usdc_denom, mars_incentive_denom), - &user_1_usdc_idx_old, - ) - .unwrap(); - let user_1_osmo_idx_old = Decimal::from_str("33.25").unwrap(); - v1_state::USER_ASSET_INDICES - .save( - deps.as_mut().storage, - (&user_1, osmo_denom, astro_incentive_denom), - &user_1_osmo_idx_old, - ) - .unwrap(); - - // Set user asset indices only for osmo. Index is up to date with asset incentive index. No rewards accured. - let user_2 = Addr::unchecked("user_2"); - let user_2_osmo_idx_old = Decimal::from_str("1.2356").unwrap(); - v1_state::USER_ASSET_INDICES - .save( - deps.as_mut().storage, - (&user_2, osmo_denom, astro_incentive_denom), - &user_2_osmo_idx_old, - ) - .unwrap(); - - // Set user asset indices only for atom - let user_3 = Addr::unchecked("user_3"); - let user_3_atom_idx_old = Decimal::one(); - v1_state::USER_ASSET_INDICES - .save( - deps.as_mut().storage, - (&user_3, atom_denom, mars_incentive_denom), - &user_3_atom_idx_old, - ) - .unwrap(); - - // Set unclaimed rewards for user_2 - let user_2_usdc_mars_unclaimed_rewards = Uint128::new(500); - v1_state::USER_UNCLAIMED_REWARDS - .save( - deps.as_mut().storage, - (&user_2, usdc_denom, mars_incentive_denom), - &user_2_usdc_mars_unclaimed_rewards, - ) - .unwrap(); - let user_2_atom_mars_unclaimed_rewards = Uint128::new(12345); - v1_state::USER_UNCLAIMED_REWARDS - .save( - deps.as_mut().storage, - (&user_2, atom_denom, mars_incentive_denom), - &user_2_atom_mars_unclaimed_rewards, - ) - .unwrap(); - - // Set unclaimed rewards for user_1 - let user_1_osmo_astro_unclaimed_rewards = Uint128::new(1000); - v1_state::USER_UNCLAIMED_REWARDS - .save( - deps.as_mut().storage, - (&user_1, osmo_denom, astro_incentive_denom), - &user_1_osmo_astro_unclaimed_rewards, - ) - .unwrap(); - - // can't migrate users indexes if guard is inactive - let err = execute( - deps.as_mut(), - mock_env(), - mock_info(old_owner, &[]), - ExecuteMsg::Migrate(MigrateV1ToV2::UsersIndexesAndRewards { - limit: 2, - }), - ) - .unwrap_err(); - assert_eq!(err, ContractError::Guard(GuardError::Inactive {})); - - let res = migrate(deps.as_mut(), mock_env(), Empty {}).unwrap(); - - assert_eq!(res.messages, vec![]); - assert_eq!(res.events, vec![] as Vec); - assert!(res.data.is_none()); - assert_eq!( - res.attributes, - vec![attr("action", "migrate"), attr("from_version", "1.2.0"), attr("to_version", "2.2.0")] - ); - - let new_contract_version = ContractVersion { - contract: "crates.io:mars-incentives".to_string(), - version: "2.2.0".to_string(), - }; - assert_eq!(cw2::get_contract_version(deps.as_ref().storage).unwrap(), new_contract_version); - - // Check if user unclaimed rewards are migrated correctly - let user_unclaimed_rewards = USER_UNCLAIMED_REWARDS - .range(deps.as_ref().storage, None, None, Order::Ascending) - .collect::>>() - .unwrap(); - assert_eq!(user_unclaimed_rewards.len(), 3); - - let user_id = UserId::credit_manager(user_1, "".to_string()); - let user_1_id_key: UserIdKey = user_id.try_into().unwrap(); - let user_1_osmo_astro_rewards_migrated = *user_unclaimed_rewards - .get(&(user_1_id_key.clone(), osmo_denom.to_string(), astro_incentive_denom.to_string())) - .unwrap(); - assert_eq!(user_1_osmo_astro_rewards_migrated, user_1_osmo_astro_unclaimed_rewards); - - let user_id = UserId::credit_manager(user_2, "".to_string()); - let user_2_id_key: UserIdKey = user_id.try_into().unwrap(); - let user_2_atom_mars_rewards_migrated = *user_unclaimed_rewards - .get(&(user_2_id_key.clone(), atom_denom.to_string(), mars_incentive_denom.to_string())) - .unwrap(); - assert_eq!(user_2_atom_mars_rewards_migrated, user_2_atom_mars_unclaimed_rewards); - let user_2_usdc_mars_rewards_migrated = *user_unclaimed_rewards - .get(&(user_2_id_key.clone(), usdc_denom.to_string(), mars_incentive_denom.to_string())) - .unwrap(); - assert_eq!(user_2_usdc_mars_rewards_migrated, user_2_usdc_mars_unclaimed_rewards); - - // check if guard is active for user actions - let err = execute( - deps.as_mut(), - mock_env(), - mock_info("red-bank", &[]), - ExecuteMsg::BalanceChange { - user_addr: Addr::unchecked("depositor"), - account_id: None, - denom: "uosmo".to_string(), - user_amount_scaled_before: Uint128::one(), - total_amount_scaled_before: Uint128::one(), - }, - ) - .unwrap_err(); - assert_eq!(err, ContractError::Guard(GuardError::Active {})); - - let err = execute( - deps.as_mut(), - mock_env(), - mock_info("depositor", &[]), - ExecuteMsg::ClaimRewards { - account_id: None, - start_after_collateral_denom: None, - start_after_incentive_denom: None, - limit: None, - }, - ) - .unwrap_err(); - assert_eq!(err, ContractError::Guard(GuardError::Active {})); - - // non-owner is unauthorized to clear state - let err = execute( - deps.as_mut(), - mock_env(), - mock_info("random_user", &[]), - ExecuteMsg::Migrate(MigrateV1ToV2::ClearV1State {}), - ) - .unwrap_err(); - assert_eq!(err, ContractError::Owner(mars_owner::OwnerError::NotOwner {})); - - // can't clear old V1 state if migration in progress - guard is active - let err = execute( - deps.as_mut(), - mock_env(), - mock_info(old_owner, &[]), - ExecuteMsg::Migrate(MigrateV1ToV2::ClearV1State {}), - ) - .unwrap_err(); - assert_eq!(err, ContractError::Guard(GuardError::Active {})); - - let res = execute( - deps.as_mut(), - mock_env(), - mock_info(old_owner, &[]), - ExecuteMsg::Migrate(MigrateV1ToV2::UsersIndexesAndRewards { - limit: 2, - }), - ) - .unwrap(); - assert_eq!( - res.attributes, - vec![ - attr("action", "migrate_users_indexes_and_rewards"), - attr("result", "in_progress"), - attr("start_after", "none"), - attr("limit", "2"), - attr("has_more", "true"), - ] - ); - - let res = execute( - deps.as_mut(), - mock_env(), - mock_info("random_user_1", &[]), - ExecuteMsg::Migrate(MigrateV1ToV2::UsersIndexesAndRewards { - limit: 2, - }), - ) - .unwrap(); - assert_eq!( - res.attributes, - vec![ - attr("action", "migrate_users_indexes_and_rewards"), - attr("result", "in_progress"), - attr("start_after", "user_1-uosmo-uastro"), - attr("limit", "2"), - attr("has_more", "true"), - ] - ); - - let res = execute( - deps.as_mut(), - mock_env(), - mock_info("random_user_2", &[]), - ExecuteMsg::Migrate(MigrateV1ToV2::UsersIndexesAndRewards { - limit: 2, - }), - ) - .unwrap(); - assert_eq!( - res.attributes, - vec![ - attr("action", "migrate_users_indexes_and_rewards"), - attr("result", "done"), - attr("start_after", "user_2-uosmo-uastro"), - attr("limit", "2"), - attr("has_more", "false"), - ] - ); - - // check v1 state after full migration - assert!(!v1_state::USER_ASSET_INDICES.is_empty(&deps.storage)); - assert!(!v1_state::USER_UNCLAIMED_REWARDS.is_empty(&deps.storage)); - - // Check if user asset indices are updated correctly - let user_asset_indices = USER_ASSET_INDICES - .range(deps.as_ref().storage, None, None, Order::Ascending) - .collect::>>() - .unwrap(); - assert_eq!(user_asset_indices.len(), 5); - - assert_eq!( - user_asset_indices - .get(&(user_1_id_key.clone(), atom_denom.to_string(), mars_incentive_denom.to_string())) - .unwrap(), - user_1_atom_idx_old - ); - assert_eq!( - user_asset_indices - .get(&(user_1_id_key.clone(), usdc_denom.to_string(), mars_incentive_denom.to_string())) - .unwrap(), - user_1_usdc_idx_old - ); - assert_eq!( - user_asset_indices - .get(&( - user_1_id_key.clone(), - osmo_denom.to_string(), - astro_incentive_denom.to_string() - )) - .unwrap(), - user_1_osmo_idx_old - ); - - assert_eq!( - user_asset_indices - .get(&(user_2_id_key, osmo_denom.to_string(), astro_incentive_denom.to_string())) - .unwrap(), - user_2_osmo_idx_old - ); - - let user_id = UserId::credit_manager(user_3, "".to_string()); - let user_3_id_key: UserIdKey = user_id.try_into().unwrap(); - assert_eq!( - user_asset_indices - .get(&(user_3_id_key.clone(), atom_denom.to_string(), mars_incentive_denom.to_string())) - .unwrap(), - user_3_atom_idx_old - ); - - // Clear old V1 state - execute( - deps.as_mut(), - mock_env(), - mock_info(old_owner, &[]), - ExecuteMsg::Migrate(MigrateV1ToV2::ClearV1State {}), - ) - .unwrap(); - - // check v1 state after clearing - assert!(v1_state::USER_ASSET_INDICES.is_empty(&deps.storage)); - assert!(v1_state::USER_UNCLAIMED_REWARDS.is_empty(&deps.storage)); - - // guard should be unlocked after migration - assert!(MIGRATION_GUARD.assert_unlocked(&deps.storage).is_ok()); -} diff --git a/contracts/oracle/wasm/src/contract.rs b/contracts/oracle/wasm/src/contract.rs index 9033a09b..c5db8897 100644 --- a/contracts/oracle/wasm/src/contract.rs +++ b/contracts/oracle/wasm/src/contract.rs @@ -82,6 +82,6 @@ pub mod entry { #[entry_point] pub fn migrate(deps: DepsMut, _env: Env, _msg: Empty) -> ContractResult { - migrations::v2_0_0::migrate(deps) + migrations::v2_2_0::migrate(deps) } } diff --git a/contracts/oracle/wasm/src/migrations/mod.rs b/contracts/oracle/wasm/src/migrations/mod.rs index 7592b6f1..8fd44388 100644 --- a/contracts/oracle/wasm/src/migrations/mod.rs +++ b/contracts/oracle/wasm/src/migrations/mod.rs @@ -1 +1 @@ -pub mod v2_0_0; +pub mod v2_2_0; diff --git a/contracts/oracle/wasm/src/migrations/v2_0_0.rs b/contracts/oracle/wasm/src/migrations/v2_2_0.rs similarity index 95% rename from contracts/oracle/wasm/src/migrations/v2_0_0.rs rename to contracts/oracle/wasm/src/migrations/v2_2_0.rs index 74387cf6..6f8cdb94 100644 --- a/contracts/oracle/wasm/src/migrations/v2_0_0.rs +++ b/contracts/oracle/wasm/src/migrations/v2_2_0.rs @@ -4,7 +4,7 @@ use mars_oracle_base::ContractError; use crate::contract::{CONTRACT_NAME, CONTRACT_VERSION}; -const FROM_VERSION: &str = "1.3.0"; +const FROM_VERSION: &str = "2.1.0"; pub fn migrate(deps: DepsMut) -> Result { // make sure we're migrating the correct contract and from the correct version diff --git a/contracts/oracle/wasm/tests/tests/test_migration_v2.rs b/contracts/oracle/wasm/tests/tests/test_migration_v2.rs index e02c0e2a..25171fc4 100644 --- a/contracts/oracle/wasm/tests/tests/test_migration_v2.rs +++ b/contracts/oracle/wasm/tests/tests/test_migration_v2.rs @@ -7,7 +7,7 @@ use mars_testing::mock_dependencies; #[test] fn wrong_contract_name() { let mut deps = mock_dependencies(&[]); - cw2::set_contract_version(deps.as_mut().storage, "contract_xyz", "1.3.0").unwrap(); + cw2::set_contract_version(deps.as_mut().storage, "contract_xyz", "2.1.0").unwrap(); let err = migrate(deps.as_mut(), mock_env(), Empty {}).unwrap_err(); @@ -31,7 +31,7 @@ fn wrong_contract_version() { assert_eq!( err, ContractError::Version(VersionError::WrongVersion { - expected: "1.3.0".to_string(), + expected: "2.1.0".to_string(), found: "4.1.0".to_string() }) ); @@ -40,7 +40,7 @@ fn wrong_contract_version() { #[test] fn successful_migration() { let mut deps = mock_dependencies(&[]); - cw2::set_contract_version(deps.as_mut().storage, "crates.io:mars-oracle-wasm", "1.3.0") + cw2::set_contract_version(deps.as_mut().storage, "crates.io:mars-oracle-wasm", "2.1.0") .unwrap(); let res = migrate(deps.as_mut(), mock_env(), Empty {}).unwrap(); @@ -50,7 +50,7 @@ fn successful_migration() { assert!(res.data.is_none()); assert_eq!( res.attributes, - vec![attr("action", "migrate"), attr("from_version", "1.3.0"), attr("to_version", "2.2.0")] + vec![attr("action", "migrate"), attr("from_version", "2.1.0"), attr("to_version", "2.2.0")] ); let new_contract_version = ContractVersion { diff --git a/contracts/params/src/contract.rs b/contracts/params/src/contract.rs index 3f54df40..126ce827 100644 --- a/contracts/params/src/contract.rs +++ b/contracts/params/src/contract.rs @@ -1,11 +1,11 @@ #[cfg(not(feature = "library"))] use cosmwasm_std::entry_point; -use cosmwasm_std::{to_json_binary, Binary, Deps, DepsMut, Empty, Env, MessageInfo, Response}; +use cosmwasm_std::{to_json_binary, Binary, Deps, DepsMut, Env, MessageInfo, Response}; use cw2::set_contract_version; use mars_owner::OwnerInit::SetInitialOwner; use mars_types::params::{ - CmEmergencyUpdate, EmergencyUpdate, ExecuteMsg, InstantiateMsg, PerpsEmergencyUpdate, QueryMsg, - RedBankEmergencyUpdate, + CmEmergencyUpdate, EmergencyUpdate, ExecuteMsg, InstantiateMsg, MigrateMsg, + PerpsEmergencyUpdate, QueryMsg, RedBankEmergencyUpdate, }; use crate::{ @@ -165,6 +165,6 @@ pub fn query(deps: Deps, env: Env, msg: QueryMsg) -> ContractResult { } #[cfg_attr(not(feature = "library"), entry_point)] -pub fn migrate(deps: DepsMut, _env: Env, _msg: Empty) -> Result { - migrations::v2_2_0::migrate(deps) +pub fn migrate(deps: DepsMut, _env: Env, msg: MigrateMsg) -> Result { + migrations::v2_2_0::migrate(deps, msg) } diff --git a/contracts/params/src/migrations/v2_2_0.rs b/contracts/params/src/migrations/v2_2_0.rs index 0c0eaf98..798632b9 100644 --- a/contracts/params/src/migrations/v2_2_0.rs +++ b/contracts/params/src/migrations/v2_2_0.rs @@ -1,16 +1,83 @@ -use cosmwasm_std::{DepsMut, Response}; +use cosmwasm_std::{Addr, Decimal, DepsMut, Order, Response, StdResult}; use cw2::{assert_contract_version, set_contract_version}; use mars_owner::OwnerInit::SetInitialOwner; +use mars_types::params::{ + AssetParams, CmSettings, HlsAssetType, HlsParams, LiquidationBonus, MigrateMsg, RedBankSettings, +}; use crate::{ contract::{CONTRACT_NAME, CONTRACT_VERSION}, error::ContractError, - state::{OWNER, RISK_MANAGER}, + state::{ASSET_PARAMS, OWNER, RISK_MANAGER}, }; const FROM_VERSION: &str = "2.1.0"; -pub fn migrate(deps: DepsMut) -> Result { +/// Copy paste of the state structs from the v2.1.0 of the contract (https://github.com/mars-protocol/contracts/tree/v2.1.0). +pub mod v2_1_0_state { + use cosmwasm_schema::cw_serde; + use cosmwasm_std::{Addr, Decimal, Uint128}; + use cw_storage_plus::Map; + + #[cw_serde] + pub enum HlsAssetType { + Coin { + denom: String, + }, + Vault { + addr: T, + }, + } + + #[cw_serde] + pub struct HlsParamsBase { + pub max_loan_to_value: Decimal, + pub liquidation_threshold: Decimal, + pub correlations: Vec>, + } + + pub type HlsParams = HlsParamsBase; + pub type HlsParamsUnchecked = HlsParamsBase; + + #[cw_serde] + pub struct CmSettings { + pub whitelisted: bool, + pub hls: Option>, + } + + #[cw_serde] + pub struct RedBankSettings { + pub deposit_enabled: bool, + pub borrow_enabled: bool, + } + + #[cw_serde] + pub struct LiquidationBonus { + pub starting_lb: Decimal, + pub slope: Decimal, + pub min_lb: Decimal, + pub max_lb: Decimal, + } + + #[cw_serde] + pub struct AssetParamsBase { + pub denom: String, + pub credit_manager: CmSettings, + pub red_bank: RedBankSettings, + pub max_loan_to_value: Decimal, + pub liquidation_threshold: Decimal, + pub liquidation_bonus: LiquidationBonus, + pub protocol_liquidation_fee: Decimal, + pub deposit_cap: Uint128, + } + + pub type AssetParams = AssetParamsBase; + pub type AssetParamsUnchecked = AssetParamsBase; + + pub const ASSET_PARAMS: Map<&str, AssetParams> = Map::new("asset_params"); +} + +pub fn migrate(deps: DepsMut, msg: MigrateMsg) -> Result { // Make sure we're migrating the correct contract and from the correct version. assert_contract_version(deps.storage, &format!("crates.io:{CONTRACT_NAME}"), FROM_VERSION)?; @@ -27,8 +94,78 @@ pub fn migrate(deps: DepsMut) -> Result { }, )?; + // Migrate assets + let asset_params = v2_1_0_state::ASSET_PARAMS + .range(deps.storage, None, None, Order::Ascending) + .collect::>>()?; + v2_1_0_state::ASSET_PARAMS.clear(deps.storage); + for (denom, asset_param) in asset_params.into_iter() { + ASSET_PARAMS.save( + deps.storage, + &denom, + &from_v2_1_0_to_v2_2_0_asset_param(asset_param, msg.close_factor), + )?; + } + Ok(Response::new() .add_attribute("action", "migrate") .add_attribute("from_version", FROM_VERSION) .add_attribute("to_version", CONTRACT_VERSION)) } + +fn from_v2_1_0_to_v2_2_0_asset_param( + value: v2_1_0_state::AssetParams, + close_factor: Decimal, +) -> AssetParams { + AssetParams { + denom: value.denom, + credit_manager: CmSettings { + whitelisted: value.credit_manager.whitelisted, + hls: value.credit_manager.hls.map(Into::into), + withdraw_enabled: true, // New field + }, + red_bank: RedBankSettings { + deposit_enabled: value.red_bank.deposit_enabled, + borrow_enabled: value.red_bank.borrow_enabled, + withdraw_enabled: value.red_bank.deposit_enabled, // New field, make it dependent on deposit_enabled + }, + max_loan_to_value: value.max_loan_to_value, + liquidation_threshold: value.liquidation_threshold, + liquidation_bonus: LiquidationBonus { + starting_lb: value.liquidation_bonus.starting_lb, + slope: value.liquidation_bonus.slope, + min_lb: value.liquidation_bonus.min_lb, + max_lb: value.liquidation_bonus.max_lb, + }, + protocol_liquidation_fee: value.protocol_liquidation_fee, + deposit_cap: value.deposit_cap, + close_factor, // New field + } +} + +impl From> for HlsAssetType { + fn from(value: v2_1_0_state::HlsAssetType) -> Self { + match value { + v2_1_0_state::HlsAssetType::Coin { + denom, + } => HlsAssetType::Coin { + denom, + }, + v2_1_0_state::HlsAssetType::Vault { + addr, + } => HlsAssetType::Vault { + addr, + }, + } + } +} + +impl From for HlsParams { + fn from(value: v2_1_0_state::HlsParams) -> Self { + Self { + max_loan_to_value: value.max_loan_to_value, + liquidation_threshold: value.liquidation_threshold, + correlations: value.correlations.into_iter().map(Into::into).collect(), + } + } +} diff --git a/contracts/params/tests/tests/test_migration_v2.rs b/contracts/params/tests/tests/test_migration_v2.rs index eca548ac..fa6d3938 100644 --- a/contracts/params/tests/tests/test_migration_v2.rs +++ b/contracts/params/tests/tests/test_migration_v2.rs @@ -1,18 +1,31 @@ -use cosmwasm_std::{attr, testing::mock_env, Addr, Empty, Event}; +use std::{collections::HashMap, str::FromStr}; + +use cosmwasm_std::{attr, testing::mock_env, Addr, Decimal, Event, Order, StdResult, Uint128}; use cw2::{ContractVersion, VersionError}; use mars_params::{ contract::migrate, error::ContractError, - state::{OWNER, RISK_MANAGER}, + migrations::v2_2_0::v2_1_0_state, + state::{ASSET_PARAMS, OWNER, RISK_MANAGER}, }; use mars_testing::mock_dependencies; +use mars_types::params::{ + AssetParams, CmSettings, HlsAssetType, HlsParams, LiquidationBonus, MigrateMsg, RedBankSettings, +}; #[test] fn wrong_contract_name() { let mut deps = mock_dependencies(&[]); cw2::set_contract_version(deps.as_mut().storage, "contract_xyz", "2.1.0").unwrap(); - let err = migrate(deps.as_mut(), mock_env(), Empty {}).unwrap_err(); + let err = migrate( + deps.as_mut(), + mock_env(), + MigrateMsg { + close_factor: Decimal::one(), + }, + ) + .unwrap_err(); assert_eq!( err, @@ -28,7 +41,14 @@ fn wrong_contract_version() { let mut deps = mock_dependencies(&[]); cw2::set_contract_version(deps.as_mut().storage, "crates.io:mars-params", "2.0.0").unwrap(); - let err = migrate(deps.as_mut(), mock_env(), Empty {}).unwrap_err(); + let err = migrate( + deps.as_mut(), + mock_env(), + MigrateMsg { + close_factor: Decimal::one(), + }, + ) + .unwrap_err(); assert_eq!( err, @@ -57,7 +77,18 @@ fn successful_migration() { ) .unwrap(); - let res = migrate(deps.as_mut(), mock_env(), Empty {}).unwrap(); + // Initialize the ASSET_PARAMS storage items with the old state + v2_1_0_state::ASSET_PARAMS.save(deps.as_mut().storage, "asset_2", &asset_2()).unwrap(); + v2_1_0_state::ASSET_PARAMS.save(deps.as_mut().storage, "asset_1", &asset_1()).unwrap(); + + let res = migrate( + deps.as_mut(), + mock_env(), + MigrateMsg { + close_factor: Decimal::from_str("0.9").unwrap(), + }, + ) + .unwrap(); assert_eq!(res.messages, vec![]); assert_eq!(res.events, vec![] as Vec); @@ -76,4 +107,143 @@ fn successful_migration() { assert!(OWNER.is_owner(deps.as_ref().storage, &owner).unwrap()); // Check that the risk manager has been set to the owner as default assert!(RISK_MANAGER.is_owner(deps.as_ref().storage, &owner).unwrap()); + + // Check that the ASSET_PARAMS storage items have been migrated correctly + let asset_params = ASSET_PARAMS + .range(deps.as_ref().storage, None, None, Order::Ascending) + .collect::>>() + .unwrap(); + assert_eq!(asset_params.len(), 2); + assert_eq!(asset_params.get("asset_1").unwrap(), &expected_asset_1()); + assert_eq!(asset_params.get("asset_2").unwrap(), &expected_asset_2()); +} + +fn asset_1() -> v2_1_0_state::AssetParams { + v2_1_0_state::AssetParams { + denom: "asset_1".to_string(), + credit_manager: v2_1_0_state::CmSettings { + whitelisted: false, + hls: None, + }, + red_bank: v2_1_0_state::RedBankSettings { + deposit_enabled: false, + borrow_enabled: false, + }, + max_loan_to_value: Decimal::from_str("0.6").unwrap(), + liquidation_threshold: Decimal::from_str("0.65").unwrap(), + liquidation_bonus: v2_1_0_state::LiquidationBonus { + starting_lb: Decimal::from_str("0.1").unwrap(), + slope: Decimal::from_str("0.2").unwrap(), + min_lb: Decimal::from_str("0.3").unwrap(), + max_lb: Decimal::from_str("0.4").unwrap(), + }, + protocol_liquidation_fee: Decimal::from_str("0.05").unwrap(), + deposit_cap: Uint128::from(1230000u128), + } +} + +fn expected_asset_1() -> AssetParams { + AssetParams { + denom: "asset_1".to_string(), + credit_manager: CmSettings { + whitelisted: false, + hls: None, + withdraw_enabled: true, + }, + red_bank: RedBankSettings { + deposit_enabled: false, + borrow_enabled: false, + withdraw_enabled: false, + }, + max_loan_to_value: Decimal::from_str("0.6").unwrap(), + liquidation_threshold: Decimal::from_str("0.65").unwrap(), + liquidation_bonus: LiquidationBonus { + starting_lb: Decimal::from_str("0.1").unwrap(), + slope: Decimal::from_str("0.2").unwrap(), + min_lb: Decimal::from_str("0.3").unwrap(), + max_lb: Decimal::from_str("0.4").unwrap(), + }, + protocol_liquidation_fee: Decimal::from_str("0.05").unwrap(), + deposit_cap: Uint128::from(1230000u128), + close_factor: Decimal::from_str("0.9").unwrap(), + } +} + +fn asset_2() -> v2_1_0_state::AssetParams { + v2_1_0_state::AssetParams { + denom: "asset_2".to_string(), + credit_manager: v2_1_0_state::CmSettings { + whitelisted: true, + hls: Some(v2_1_0_state::HlsParams { + max_loan_to_value: Decimal::from_str("0.2").unwrap(), + liquidation_threshold: Decimal::from_str("0.22").unwrap(), + correlations: vec![ + v2_1_0_state::HlsAssetType::Coin { + denom: "denom_1".to_string(), + }, + v2_1_0_state::HlsAssetType::Vault { + addr: Addr::unchecked("vault_addr_123"), + }, + v2_1_0_state::HlsAssetType::Coin { + denom: "denom_2".to_string(), + }, + ], + }), + }, + red_bank: v2_1_0_state::RedBankSettings { + deposit_enabled: true, + borrow_enabled: true, + }, + max_loan_to_value: Decimal::from_str("0.89").unwrap(), + liquidation_threshold: Decimal::from_str("0.67").unwrap(), + liquidation_bonus: v2_1_0_state::LiquidationBonus { + starting_lb: Decimal::from_str("0.2").unwrap(), + slope: Decimal::from_str("0.1").unwrap(), + min_lb: Decimal::from_str("0.4").unwrap(), + max_lb: Decimal::from_str("0.3").unwrap(), + }, + protocol_liquidation_fee: Decimal::from_str("0.15").unwrap(), + deposit_cap: Uint128::from(123u128), + } +} + +fn expected_asset_2() -> AssetParams { + AssetParams { + denom: "asset_2".to_string(), + credit_manager: CmSettings { + whitelisted: true, + hls: Some(HlsParams { + max_loan_to_value: Decimal::from_str("0.2").unwrap(), + liquidation_threshold: Decimal::from_str("0.22").unwrap(), + correlations: vec![ + HlsAssetType::Coin { + denom: "denom_1".to_string(), + }, + HlsAssetType::Vault { + addr: Addr::unchecked("vault_addr_123"), + }, + HlsAssetType::Coin { + denom: "denom_2".to_string(), + }, + ], + }), + withdraw_enabled: true, + }, + red_bank: RedBankSettings { + deposit_enabled: true, + borrow_enabled: true, + withdraw_enabled: true, + }, + max_loan_to_value: Decimal::from_str("0.89").unwrap(), + liquidation_threshold: Decimal::from_str("0.67").unwrap(), + liquidation_bonus: LiquidationBonus { + starting_lb: Decimal::from_str("0.2").unwrap(), + slope: Decimal::from_str("0.1").unwrap(), + min_lb: Decimal::from_str("0.4").unwrap(), + max_lb: Decimal::from_str("0.3").unwrap(), + }, + protocol_liquidation_fee: Decimal::from_str("0.15").unwrap(), + deposit_cap: Uint128::from(123u128), + close_factor: Decimal::from_str("0.9").unwrap(), + } } diff --git a/contracts/red-bank/src/contract.rs b/contracts/red-bank/src/contract.rs index 8d89886c..7491b3dd 100644 --- a/contracts/red-bank/src/contract.rs +++ b/contracts/red-bank/src/contract.rs @@ -5,7 +5,7 @@ use mars_types::red_bank::{ExecuteMsg, InstantiateMsg, QueryMsg}; use crate::{ asset, borrow, collateral, config, deposit, error::ContractError, instantiate, liquidate, - migrations, query, repay, state::MIGRATION_GUARD, withdraw, + migrations, query, repay, withdraw, }; pub const CONTRACT_NAME: &str = env!("CARGO_PKG_NAME"); @@ -45,7 +45,6 @@ pub fn execute( account_id, on_behalf_of, } => { - MIGRATION_GUARD.assert_unlocked(deps.storage)?; let sent_coin = cw_utils::one_coin(&info)?; deposit::deposit( deps, @@ -64,7 +63,6 @@ pub fn execute( account_id, liquidation_related, } => { - MIGRATION_GUARD.assert_unlocked(deps.storage)?; cw_utils::nonpayable(&info)?; withdraw::withdraw( deps, @@ -82,14 +80,12 @@ pub fn execute( amount, recipient, } => { - MIGRATION_GUARD.assert_unlocked(deps.storage)?; cw_utils::nonpayable(&info)?; borrow::borrow(deps, env, info, denom, amount, recipient) } ExecuteMsg::Repay { on_behalf_of, } => { - MIGRATION_GUARD.assert_unlocked(deps.storage)?; let sent_coin = cw_utils::one_coin(&info)?; repay::repay(deps, env, info, on_behalf_of, sent_coin.denom, sent_coin.amount) } @@ -98,7 +94,6 @@ pub fn execute( collateral_denom, recipient, } => { - MIGRATION_GUARD.assert_unlocked(deps.storage)?; let user_addr = deps.api.addr_validate(&user)?; let sent_coin = cw_utils::one_coin(&info)?; liquidate::liquidate( @@ -116,14 +111,9 @@ pub fn execute( denom, enable, } => { - MIGRATION_GUARD.assert_unlocked(deps.storage)?; cw_utils::nonpayable(&info)?; collateral::update_asset_collateral_status(deps, env, info, denom, enable) } - ExecuteMsg::Migrate(msg) => { - cw_utils::nonpayable(&info)?; - migrations::v2_0_0::execute_migration(deps, info, msg) - } } } @@ -249,5 +239,5 @@ pub fn query(deps: Deps, env: Env, msg: QueryMsg) -> Result Result { - migrations::v2_0_0::migrate(deps) + migrations::v2_2_0::migrate(deps) } diff --git a/contracts/red-bank/src/migrations/mod.rs b/contracts/red-bank/src/migrations/mod.rs index 7592b6f1..8fd44388 100644 --- a/contracts/red-bank/src/migrations/mod.rs +++ b/contracts/red-bank/src/migrations/mod.rs @@ -1 +1 @@ -pub mod v2_0_0; +pub mod v2_2_0; diff --git a/contracts/red-bank/src/migrations/v2_0_0.rs b/contracts/red-bank/src/migrations/v2_0_0.rs deleted file mode 100644 index a839f2fb..00000000 --- a/contracts/red-bank/src/migrations/v2_0_0.rs +++ /dev/null @@ -1,383 +0,0 @@ -use cosmwasm_std::{Addr, DepsMut, MessageInfo, Order, Response, StdResult}; -use cw2::{assert_contract_version, set_contract_version}; -use cw_storage_plus::Bound; -use mars_types::{ - keys::{UserId, UserIdKey}, - red_bank::{Config, Market, MigrateV1ToV2}, -}; - -use crate::{ - contract::{CONTRACT_NAME, CONTRACT_VERSION}, - error::ContractError, - state::{COLLATERALS, CONFIG, MARKETS, MIGRATION_GUARD, OWNER}, -}; - -const FROM_VERSION: &str = "1.2.1"; - -pub mod v1_state { - use cosmwasm_schema::cw_serde; - use cosmwasm_std::{Addr, Decimal, Uint128}; - use cw_storage_plus::{Item, Map}; - use mars_types::red_bank::{Collateral, InterestRateModel}; - - pub const CONFIG: Item = Item::new("config"); - pub const MARKETS: Map<&str, Market> = Map::new("markets"); - pub const COLLATERALS: Map<(&Addr, &str), Collateral> = Map::new("collaterals"); - - #[cw_serde] - pub struct Config { - pub address_provider: Addr, - pub close_factor: Decimal, - } - - #[cw_serde] - pub struct Market { - pub denom: String, - pub max_loan_to_value: Decimal, - pub liquidation_threshold: Decimal, - pub liquidation_bonus: Decimal, - pub reserve_factor: Decimal, - pub interest_rate_model: InterestRateModel, - pub borrow_index: Decimal, - pub liquidity_index: Decimal, - pub borrow_rate: Decimal, - pub liquidity_rate: Decimal, - pub indexes_last_updated: u64, - pub collateral_total_scaled: Uint128, - pub debt_total_scaled: Uint128, - pub deposit_enabled: bool, - pub borrow_enabled: bool, - pub deposit_cap: Uint128, - } -} - -pub fn migrate(deps: DepsMut) -> Result { - // Lock red-bank to prevent any operations during migration. - // Unlock is executed after full migration in `migrate_collaterals`. - MIGRATION_GUARD.try_lock(deps.storage)?; - - // make sure we're migrating the correct contract and from the correct version - assert_contract_version(deps.storage, &format!("crates.io:{CONTRACT_NAME}"), FROM_VERSION)?; - - // Config package updated, re-initializing - let old_config = v1_state::CONFIG.load(deps.storage)?; - v1_state::CONFIG.remove(deps.storage); - CONFIG.save( - deps.storage, - &Config { - address_provider: old_config.address_provider, - }, - )?; - - // Migrate markets. - // Remove LP tokens because they are not supported in red-bank. Params for LP tokens exist in `params`contract. - let markets = v1_state::MARKETS - .range(deps.storage, None, None, Order::Ascending) - .collect::>>()?; - v1_state::MARKETS.clear(deps.storage); - for (denom, market) in markets.into_iter() { - if denom.starts_with("gamm/pool") { - continue; - } - MARKETS.save(deps.storage, &denom, &market.into())?; - } - - set_contract_version(deps.storage, format!("crates.io:{CONTRACT_NAME}"), CONTRACT_VERSION)?; - - Ok(Response::new() - .add_attribute("action", "migrate") - .add_attribute("from_version", FROM_VERSION) - .add_attribute("to_version", CONTRACT_VERSION)) -} - -/// Few of the fields in old `Market` struct are moved to `params` contract -impl From for Market { - fn from(value: v1_state::Market) -> Self { - Self { - denom: value.denom, - interest_rate_model: value.interest_rate_model, - borrow_index: value.borrow_index, - liquidity_index: value.liquidity_index, - borrow_rate: value.borrow_rate, - liquidity_rate: value.liquidity_rate, - indexes_last_updated: value.indexes_last_updated, - collateral_total_scaled: value.collateral_total_scaled, - debt_total_scaled: value.debt_total_scaled, - reserve_factor: value.reserve_factor, - } - } -} - -pub fn execute_migration( - deps: DepsMut, - info: MessageInfo, - msg: MigrateV1ToV2, -) -> Result { - match msg { - MigrateV1ToV2::Collaterals { - limit, - } => migrate_collaterals(deps, limit as usize), - MigrateV1ToV2::ClearV1State {} => { - OWNER.assert_owner(deps.storage, &info.sender)?; - clear_v1_state(deps) - } - } -} - -fn migrate_collaterals(deps: DepsMut, limit: usize) -> Result { - // Only allow to migrate collaterals if guard is locked via `migrate` entrypoint - MIGRATION_GUARD.assert_locked(deps.storage)?; - - // convert last key from v2 to v1 - let colls_last_key = COLLATERALS.last(deps.storage)?.map(|kv| kv.0); - let colls_last_key = if let Some((user_id_key, denom)) = colls_last_key { - let user_id: UserId = user_id_key.try_into()?; - Some((user_id.addr, denom)) - } else { - None - }; - - // last key from new collaterals is first key (excluded) for v1 collaterals during pagination - let start_after = - colls_last_key.as_ref().map(|(addr, denom)| Bound::exclusive((addr, denom.as_str()))); - let mut v1_colls = v1_state::COLLATERALS - .range(deps.storage, start_after, None, Order::Ascending) - .take(limit + 1) - .collect::>>()?; - - let has_more = v1_colls.len() > limit; - if has_more { - v1_colls.pop(); // Remove the extra item used for checking if there are more items - } - - // save collaterals - for ((user_addr, denom), collateral) in v1_colls.into_iter() { - let user_id = UserId::credit_manager(user_addr, "".to_string()); - let user_id_key: UserIdKey = user_id.try_into()?; - COLLATERALS.save(deps.storage, (&user_id_key, &denom), &collateral)?; - } - - if !has_more { - // red-bank locked via `migrate` entrypoint. Unlock red-bank after full migration - MIGRATION_GUARD.try_unlock(deps.storage)?; - } - - Ok(Response::new() - .add_attribute("action", "migrate_collaterals") - .add_attribute( - "result", - if has_more { - "in_progress" - } else { - "done" - }, - ) - .add_attribute("start_after", key_to_str(colls_last_key)) - .add_attribute("limit", limit.to_string()) - .add_attribute("has_more", has_more.to_string())) -} - -fn key_to_str(key: Option<(Addr, String)>) -> String { - key.map(|(addr, denom)| format!("{}-{}", addr, denom)).unwrap_or("none".to_string()) -} - -fn clear_v1_state(deps: DepsMut) -> Result { - // It is safe to clear v1 state only after full migration (guard is unlocked) - MIGRATION_GUARD.assert_unlocked(deps.storage)?; - v1_state::COLLATERALS.clear(deps.storage); - Ok(Response::new().add_attribute("action", "clear_v1_state")) -} - -#[cfg(test)] -mod tests { - use std::collections::HashMap; - - use cosmwasm_std::{attr, testing::mock_dependencies, Addr, Uint128}; - use mars_types::red_bank::Collateral; - use mars_utils::error::GuardError; - - use super::*; - - #[test] - fn cannot_migrate_v1_collaterals_without_lock() { - let mut deps = mock_dependencies(); - - let res_error = migrate_collaterals(deps.as_mut(), 10).unwrap_err(); - assert_eq!(res_error, ContractError::Guard(GuardError::Inactive {})); - } - - #[test] - fn empty_v1_collaterals() { - let mut deps = mock_dependencies(); - - MIGRATION_GUARD.try_lock(deps.as_mut().storage).unwrap(); - - let res = migrate_collaterals(deps.as_mut(), 10).unwrap(); - assert_eq!( - res.attributes, - vec![ - attr("action", "migrate_collaterals"), - attr("result", "done"), - attr("start_after", "none"), - attr("limit", "10"), - attr("has_more", "false"), - ] - ); - } - - #[test] - fn migrate_v1_collaterals() { - let mut deps = mock_dependencies(); - - MIGRATION_GUARD.try_lock(deps.as_mut().storage).unwrap(); - - // prepare v1 collaterals - let user_1_osmo_collateral = Collateral { - amount_scaled: Uint128::new(12345), - enabled: true, - }; - v1_state::COLLATERALS - .save( - deps.as_mut().storage, - (&Addr::unchecked("user_1"), "uosmo"), - &user_1_osmo_collateral, - ) - .unwrap(); - let user_2_atom_collateral = Collateral { - amount_scaled: Uint128::new(1), - enabled: true, - }; - v1_state::COLLATERALS - .save( - deps.as_mut().storage, - (&Addr::unchecked("user_2"), "uatom"), - &user_2_atom_collateral, - ) - .unwrap(); - let user_2_jake_collateral = Collateral { - amount_scaled: Uint128::new(1023), - enabled: true, - }; - v1_state::COLLATERALS - .save( - deps.as_mut().storage, - (&Addr::unchecked("user_2"), "ujake"), - &user_2_jake_collateral, - ) - .unwrap(); - let user_3_jake_collateral = Collateral { - amount_scaled: Uint128::new(1111111), - enabled: false, - }; - v1_state::COLLATERALS - .save( - deps.as_mut().storage, - (&Addr::unchecked("user_3"), "ujake"), - &user_3_jake_collateral, - ) - .unwrap(); - let user_1_axl_collateral = Collateral { - amount_scaled: Uint128::new(123456789), - enabled: true, - }; - v1_state::COLLATERALS - .save( - deps.as_mut().storage, - (&Addr::unchecked("user_1"), "uaxl"), - &user_1_axl_collateral, - ) - .unwrap(); - - // migrate first 2 collaterals - let res = migrate_collaterals(deps.as_mut(), 2).unwrap(); - assert_eq!( - res.attributes, - vec![ - attr("action", "migrate_collaterals"), - attr("result", "in_progress"), - attr("start_after", "none"), - attr("limit", "2"), - attr("has_more", "true"), - ] - ); - - // in new collaterals we should have 2 collaterals - let collaterals = COLLATERALS - .range(deps.as_ref().storage, None, None, Order::Ascending) - .collect::>>() - .unwrap(); - assert_eq!(collaterals.len(), 2); - - // migrate next 2 collaterals - let res = migrate_collaterals(deps.as_mut(), 2).unwrap(); - assert_eq!( - res.attributes, - vec![ - attr("action", "migrate_collaterals"), - attr("result", "in_progress"), - attr("start_after", "user_1-uosmo"), - attr("limit", "2"), - attr("has_more", "true"), - ] - ); - - // in new collaterals we should have more 2 collaterals, 4 in total - let collaterals = COLLATERALS - .range(deps.as_ref().storage, None, None, Order::Ascending) - .collect::>>() - .unwrap(); - assert_eq!(collaterals.len(), 4); - - // migrate next 2 collaterals, we have only 1 left - let res = migrate_collaterals(deps.as_mut(), 2).unwrap(); - assert_eq!( - res.attributes, - vec![ - attr("action", "migrate_collaterals"), - attr("result", "done"), - attr("start_after", "user_2-ujake"), - attr("limit", "2"), - attr("has_more", "false"), - ] - ); - - // in new collaterals we should have more 1 collaterals, 5 in total - let collaterals = COLLATERALS - .range(deps.as_ref().storage, None, None, Order::Ascending) - .collect::>>() - .unwrap(); - assert_eq!(collaterals.len(), 5); - - // compare values - let user_id = UserId::credit_manager(Addr::unchecked("user_1"), "".to_string()); - let user_1_id_key: UserIdKey = user_id.try_into().unwrap(); - assert_eq!( - collaterals.get(&(user_1_id_key.clone(), "uosmo".to_string())).unwrap(), - &user_1_osmo_collateral - ); - assert_eq!( - collaterals.get(&(user_1_id_key, "uaxl".to_string())).unwrap(), - &user_1_axl_collateral - ); - let user_id = UserId::credit_manager(Addr::unchecked("user_2"), "".to_string()); - let user_2_id_key: UserIdKey = user_id.try_into().unwrap(); - assert_eq!( - collaterals.get(&(user_2_id_key.clone(), "uatom".to_string())).unwrap(), - &user_2_atom_collateral - ); - assert_eq!( - collaterals.get(&(user_2_id_key, "ujake".to_string())).unwrap(), - &user_2_jake_collateral - ); - let user_id = UserId::credit_manager(Addr::unchecked("user_3"), "".to_string()); - let user_3_id_key: UserIdKey = user_id.try_into().unwrap(); - assert_eq!( - collaterals.get(&(user_3_id_key, "ujake".to_string())).unwrap(), - &user_3_jake_collateral - ); - - // try to migrate one more time, guard is unlocked - let res_err = migrate_collaterals(deps.as_mut(), 2).unwrap_err(); - assert_eq!(res_err, ContractError::Guard(GuardError::Inactive {})); - } -} diff --git a/contracts/red-bank/src/migrations/v2_2_0.rs b/contracts/red-bank/src/migrations/v2_2_0.rs new file mode 100644 index 00000000..adef3abf --- /dev/null +++ b/contracts/red-bank/src/migrations/v2_2_0.rs @@ -0,0 +1,21 @@ +use cosmwasm_std::{DepsMut, Response}; +use cw2::{assert_contract_version, set_contract_version}; + +use crate::{ + contract::{CONTRACT_NAME, CONTRACT_VERSION}, + error::ContractError, +}; + +const FROM_VERSION: &str = "2.1.0"; + +pub fn migrate(deps: DepsMut) -> Result { + // make sure we're migrating the correct contract and from the correct version + assert_contract_version(deps.storage, &format!("crates.io:{CONTRACT_NAME}"), FROM_VERSION)?; + + set_contract_version(deps.storage, format!("crates.io:{CONTRACT_NAME}"), CONTRACT_VERSION)?; + + Ok(Response::new() + .add_attribute("action", "migrate") + .add_attribute("from_version", FROM_VERSION) + .add_attribute("to_version", CONTRACT_VERSION)) +} diff --git a/contracts/red-bank/src/state.rs b/contracts/red-bank/src/state.rs index 21a48f4a..5230b455 100644 --- a/contracts/red-bank/src/state.rs +++ b/contracts/red-bank/src/state.rs @@ -5,13 +5,9 @@ use mars_types::{ keys::UserIdKey, red_bank::{Collateral, Config, Debt, Market}, }; -use mars_utils::guard::Guard; pub const OWNER: Owner = Owner::new("owner"); pub const CONFIG: Item> = Item::new("config"); pub const MARKETS: Map<&str, Market> = Map::new("markets"); pub const COLLATERALS: Map<(&UserIdKey, &str), Collateral> = Map::new("colls"); pub const DEBTS: Map<(&Addr, &str), Debt> = Map::new("debts"); - -/// Used to mark the contract as locked during migrations -pub const MIGRATION_GUARD: Guard = Guard::new("guard"); diff --git a/contracts/red-bank/tests/tests/test_migration_v2.rs b/contracts/red-bank/tests/tests/test_migration_v2.rs index f389c8b8..e2667571 100644 --- a/contracts/red-bank/tests/tests/test_migration_v2.rs +++ b/contracts/red-bank/tests/tests/test_migration_v2.rs @@ -1,28 +1,12 @@ -use std::{collections::HashMap, str::FromStr}; - -use cosmwasm_std::{ - attr, - testing::{mock_env, mock_info}, - Addr, Decimal, Empty, Event, Order, StdResult, Uint128, -}; +use cosmwasm_std::{attr, testing::mock_env, Empty, Event}; use cw2::{ContractVersion, VersionError}; -use mars_red_bank::{ - contract::{execute, migrate}, - error::ContractError, - migrations::v2_0_0::v1_state, - state::{COLLATERALS, CONFIG, MARKETS, MIGRATION_GUARD, OWNER}, -}; +use mars_red_bank::{contract::migrate, error::ContractError}; use mars_testing::mock_dependencies; -use mars_types::{ - keys::{UserId, UserIdKey}, - red_bank::{Collateral, ExecuteMsg, InterestRateModel, Market, MigrateV1ToV2}, -}; -use mars_utils::error::GuardError; #[test] fn wrong_contract_name() { let mut deps = mock_dependencies(&[]); - cw2::set_contract_version(deps.as_mut().storage, "contract_xyz", "1.2.1").unwrap(); + cw2::set_contract_version(deps.as_mut().storage, "contract_xyz", "2.1.0").unwrap(); let err = migrate(deps.as_mut(), mock_env(), Empty {}).unwrap_err(); @@ -45,63 +29,16 @@ fn wrong_contract_version() { assert_eq!( err, ContractError::Version(VersionError::WrongVersion { - expected: "1.2.1".to_string(), + expected: "2.1.0".to_string(), found: "4.1.0".to_string() }) ); } #[test] -fn full_migration() { +fn successful_migration() { let mut deps = mock_dependencies(&[]); - cw2::set_contract_version(deps.as_mut().storage, "crates.io:mars-red-bank", "1.2.1").unwrap(); - - let old_owner = "spiderman_246"; - let deps_muted = deps.as_mut(); - OWNER - .initialize( - deps_muted.storage, - deps_muted.api, - mars_owner::OwnerInit::SetInitialOwner { - owner: old_owner.to_string(), - }, - ) - .unwrap(); - - let v1_config = v1_state::Config { - address_provider: Addr::unchecked("address_provider"), - close_factor: Decimal::percent(50), - }; - v1_state::CONFIG.save(deps.as_mut().storage, &v1_config).unwrap(); - - let atom_market = atom_market(); - v1_state::MARKETS.save(deps.as_mut().storage, &atom_market.denom, &atom_market).unwrap(); - let lp_market = lp_market(); - v1_state::MARKETS.save(deps.as_mut().storage, &lp_market.denom, &lp_market).unwrap(); - let osmo_market = osmo_market(); - v1_state::MARKETS.save(deps.as_mut().storage, &osmo_market.denom, &osmo_market).unwrap(); - - let user_1_atom_collateral = Collateral { - amount_scaled: Uint128::new(12345), - enabled: true, - }; - v1_state::COLLATERALS - .save(deps.as_mut().storage, (&Addr::unchecked("user_1"), "uatom"), &user_1_atom_collateral) - .unwrap(); - let user_1_osmo_collateral = Collateral { - amount_scaled: Uint128::new(345678903), - enabled: false, - }; - v1_state::COLLATERALS - .save(deps.as_mut().storage, (&Addr::unchecked("user_1"), "uosmo"), &user_1_osmo_collateral) - .unwrap(); - let user_2_atom_collateral = Collateral { - amount_scaled: Uint128::new(1), - enabled: true, - }; - v1_state::COLLATERALS - .save(deps.as_mut().storage, (&Addr::unchecked("user_2"), "uatom"), &user_2_atom_collateral) - .unwrap(); + cw2::set_contract_version(deps.as_mut().storage, "crates.io:mars-red-bank", "2.1.0").unwrap(); let res = migrate(deps.as_mut(), mock_env(), Empty {}).unwrap(); @@ -110,7 +47,7 @@ fn full_migration() { assert!(res.data.is_none()); assert_eq!( res.attributes, - vec![attr("action", "migrate"), attr("from_version", "1.2.1"), attr("to_version", "2.2.0")] + vec![attr("action", "migrate"), attr("from_version", "2.1.0"), attr("to_version", "2.2.0")] ); let new_contract_version = ContractVersion { @@ -118,261 +55,4 @@ fn full_migration() { version: "2.2.0".to_string(), }; assert_eq!(cw2::get_contract_version(deps.as_ref().storage).unwrap(), new_contract_version); - - let config = CONFIG.load(&deps.storage).unwrap(); - assert_eq!(v1_config.address_provider, config.address_provider); - - // check markets data, lp tokens should be filtered out - let markets = MARKETS - .range(deps.as_ref().storage, None, None, Order::Ascending) - .collect::>>() - .unwrap(); - assert_eq!(markets.len(), 2); - assert!(compare_markers(&atom_market, markets.get(&atom_market.denom).unwrap())); - assert!(compare_markers(&osmo_market, markets.get(&osmo_market.denom).unwrap())); - - // check if guard is active for user actions - let err = execute( - deps.as_mut(), - mock_env(), - mock_info("depositor", &[]), - ExecuteMsg::Deposit { - account_id: None, - on_behalf_of: None, - }, - ) - .unwrap_err(); - assert_eq!(err, ContractError::Guard(GuardError::Active {})); - - let err = execute( - deps.as_mut(), - mock_env(), - mock_info("withdrawer", &[]), - ExecuteMsg::Withdraw { - denom: "uosmo".to_string(), - amount: None, - recipient: None, - account_id: None, - liquidation_related: None, - }, - ) - .unwrap_err(); - assert_eq!(err, ContractError::Guard(GuardError::Active {})); - - let err = execute( - deps.as_mut(), - mock_env(), - mock_info("borrower", &[]), - ExecuteMsg::Borrow { - denom: "uosmo".to_string(), - amount: Uint128::one(), - recipient: None, - }, - ) - .unwrap_err(); - assert_eq!(err, ContractError::Guard(GuardError::Active {})); - - let err = execute( - deps.as_mut(), - mock_env(), - mock_info("borrower", &[]), - ExecuteMsg::Repay { - on_behalf_of: None, - }, - ) - .unwrap_err(); - assert_eq!(err, ContractError::Guard(GuardError::Active {})); - - let err = execute( - deps.as_mut(), - mock_env(), - mock_info("liquidator", &[]), - ExecuteMsg::Liquidate { - user: "liquidatee".to_string(), - collateral_denom: "uosmo".to_string(), - recipient: None, - }, - ) - .unwrap_err(); - assert_eq!(err, ContractError::Guard(GuardError::Active {})); - - let err = execute( - deps.as_mut(), - mock_env(), - mock_info("liquidator", &[]), - ExecuteMsg::UpdateAssetCollateralStatus { - denom: "uosmo".to_string(), - enable: false, - }, - ) - .unwrap_err(); - assert_eq!(err, ContractError::Guard(GuardError::Active {})); - - // non-owner is unauthorized to clear state - let err = execute( - deps.as_mut(), - mock_env(), - mock_info("random_user", &[]), - ExecuteMsg::Migrate(MigrateV1ToV2::ClearV1State {}), - ) - .unwrap_err(); - assert_eq!(err, ContractError::Owner(mars_owner::OwnerError::NotOwner {})); - - // can't clear old V1 collaterals state if migration in progress - guard is active - let err = execute( - deps.as_mut(), - mock_env(), - mock_info(old_owner, &[]), - ExecuteMsg::Migrate(MigrateV1ToV2::ClearV1State {}), - ) - .unwrap_err(); - assert_eq!(err, ContractError::Guard(GuardError::Active {})); - - // check users collaterals after using `migrate` entrypoint - assert!(!v1_state::COLLATERALS.is_empty(&deps.storage)); - assert!(COLLATERALS.is_empty(&deps.storage)); - - // migrate collaterals - execute( - deps.as_mut(), - mock_env(), - mock_info("ranom_user", &[]), - ExecuteMsg::Migrate(MigrateV1ToV2::Collaterals { - limit: 100, - }), - ) - .unwrap(); - - // check v1 users collaterals after full migration - assert!(!v1_state::COLLATERALS.is_empty(&deps.storage)); - - // check users collaterals with new user key (addr + account id) - let collaterals = COLLATERALS - .range(deps.as_ref().storage, None, None, Order::Ascending) - .collect::>>() - .unwrap(); - assert_eq!(collaterals.len(), 3); - let user_id = UserId::credit_manager(Addr::unchecked("user_1"), "".to_string()); - let user_1_id_key: UserIdKey = user_id.try_into().unwrap(); - assert_eq!( - collaterals.get(&(user_1_id_key.clone(), "uatom".to_string())).unwrap(), - &user_1_atom_collateral - ); - assert_eq!( - collaterals.get(&(user_1_id_key, "uosmo".to_string())).unwrap(), - &user_1_osmo_collateral - ); - let user_id = UserId::credit_manager(Addr::unchecked("user_2"), "".to_string()); - let user_2_id_key: UserIdKey = user_id.try_into().unwrap(); - assert_eq!( - collaterals.get(&(user_2_id_key, "uatom".to_string())).unwrap(), - &user_2_atom_collateral - ); - - // Clear old V1 collaterals state - execute( - deps.as_mut(), - mock_env(), - mock_info(old_owner, &[]), - ExecuteMsg::Migrate(MigrateV1ToV2::ClearV1State {}), - ) - .unwrap(); - - // check users collaterals after clearing - assert!(v1_state::COLLATERALS.is_empty(&deps.storage)); - assert!(!COLLATERALS.is_empty(&deps.storage)); - - // guard should be unlocked after migration - assert!(MIGRATION_GUARD.assert_unlocked(&deps.storage).is_ok()); -} - -fn atom_market() -> v1_state::Market { - v1_state::Market { - denom: "uatom".to_string(), - reserve_factor: Decimal::percent(10), - interest_rate_model: InterestRateModel { - optimal_utilization_rate: Decimal::from_str("0.6").unwrap(), - base: Decimal::zero(), - slope_1: Decimal::from_str("0.15").unwrap(), - slope_2: Decimal::from_str("3").unwrap(), - }, - borrow_index: Decimal::from_str("1.095285439526354046").unwrap(), - liquidity_index: Decimal::from_str("1.044176487308390288").unwrap(), - borrow_rate: Decimal::from_str("0.196215745883701305").unwrap(), - liquidity_rate: Decimal::from_str("0.121276949637996324").unwrap(), - indexes_last_updated: 1695042123, - collateral_total_scaled: Uint128::new(107605849836144570), - debt_total_scaled: Uint128::new(70450559958286857), - max_loan_to_value: Decimal::percent(60), - liquidation_threshold: Decimal::percent(50), - liquidation_bonus: Decimal::percent(5), - deposit_enabled: true, - borrow_enabled: true, - deposit_cap: Uint128::MAX, - } -} - -fn lp_market() -> v1_state::Market { - v1_state::Market { - denom: "gamm/pool/1".to_string(), - reserve_factor: Decimal::percent(10), - interest_rate_model: InterestRateModel { - optimal_utilization_rate: Decimal::from_str("0.6").unwrap(), - base: Decimal::zero(), - slope_1: Decimal::from_str("0.15").unwrap(), - slope_2: Decimal::from_str("3").unwrap(), - }, - borrow_index: Decimal::one(), - liquidity_index: Decimal::one(), - borrow_rate: Decimal::zero(), - liquidity_rate: Decimal::zero(), - indexes_last_updated: 1695042123, - collateral_total_scaled: Uint128::zero(), - debt_total_scaled: Uint128::zero(), - max_loan_to_value: Decimal::percent(60), - liquidation_threshold: Decimal::percent(50), - liquidation_bonus: Decimal::percent(5), - deposit_enabled: false, - borrow_enabled: false, - deposit_cap: Uint128::MAX, - } -} - -fn osmo_market() -> v1_state::Market { - v1_state::Market { - denom: "uosmo".to_string(), - reserve_factor: Decimal::percent(10), - interest_rate_model: InterestRateModel { - optimal_utilization_rate: Decimal::from_str("0.6").unwrap(), - base: Decimal::zero(), - slope_1: Decimal::from_str("0.15").unwrap(), - slope_2: Decimal::from_str("3").unwrap(), - }, - borrow_index: Decimal::from_str("1.048833892520260924").unwrap(), - liquidity_index: Decimal::from_str("1.012932497073055883").unwrap(), - borrow_rate: Decimal::from_str("0.080575412566632138").unwrap(), - liquidity_rate: Decimal::from_str("0.023372629597018728").unwrap(), - indexes_last_updated: 1695042123, - collateral_total_scaled: Uint128::new(3475219753161696357), - debt_total_scaled: Uint128::new(1081729307695065417), - max_loan_to_value: Decimal::percent(60), - liquidation_threshold: Decimal::percent(50), - liquidation_bonus: Decimal::percent(5), - deposit_enabled: true, - borrow_enabled: true, - deposit_cap: Uint128::MAX, - } -} - -fn compare_markers(old_market: &v1_state::Market, market: &Market) -> bool { - old_market.denom == market.denom - && old_market.reserve_factor == market.reserve_factor - && old_market.interest_rate_model == market.interest_rate_model - && old_market.borrow_index == market.borrow_index - && old_market.liquidity_index == market.liquidity_index - && old_market.borrow_rate == market.borrow_rate - && old_market.liquidity_rate == market.liquidity_rate - && old_market.indexes_last_updated == market.indexes_last_updated - && old_market.collateral_total_scaled == market.collateral_total_scaled - && old_market.debt_total_scaled == market.debt_total_scaled } diff --git a/contracts/rewards-collector/neutron/src/lib.rs b/contracts/rewards-collector/neutron/src/lib.rs index dfc921c4..ec1a1eee 100644 --- a/contracts/rewards-collector/neutron/src/lib.rs +++ b/contracts/rewards-collector/neutron/src/lib.rs @@ -93,6 +93,6 @@ pub mod entry { #[entry_point] pub fn migrate(deps: DepsMut, _env: Env, _msg: Empty) -> ContractResult { - migrations::v2_0_0::migrate(deps) + migrations::v2_2_0::migrate(deps) } } diff --git a/contracts/rewards-collector/neutron/src/migrations/mod.rs b/contracts/rewards-collector/neutron/src/migrations/mod.rs index 7592b6f1..8fd44388 100644 --- a/contracts/rewards-collector/neutron/src/migrations/mod.rs +++ b/contracts/rewards-collector/neutron/src/migrations/mod.rs @@ -1 +1 @@ -pub mod v2_0_0; +pub mod v2_2_0; diff --git a/contracts/rewards-collector/neutron/src/migrations/v2_0_0.rs b/contracts/rewards-collector/neutron/src/migrations/v2_2_0.rs similarity index 95% rename from contracts/rewards-collector/neutron/src/migrations/v2_0_0.rs rename to contracts/rewards-collector/neutron/src/migrations/v2_2_0.rs index 4a46ded6..e226e8df 100644 --- a/contracts/rewards-collector/neutron/src/migrations/v2_0_0.rs +++ b/contracts/rewards-collector/neutron/src/migrations/v2_2_0.rs @@ -4,7 +4,7 @@ use mars_rewards_collector_base::ContractError; use crate::entry::{CONTRACT_NAME, CONTRACT_VERSION}; -const FROM_VERSION: &str = "1.2.0"; +const FROM_VERSION: &str = "2.1.0"; pub fn migrate(deps: DepsMut) -> Result { // make sure we're migrating the correct contract and from the correct version diff --git a/contracts/rewards-collector/neutron/tests/tests/test_migration_v2.rs b/contracts/rewards-collector/neutron/tests/tests/test_migration_v2.rs index 26432f95..eebb7b27 100644 --- a/contracts/rewards-collector/neutron/tests/tests/test_migration_v2.rs +++ b/contracts/rewards-collector/neutron/tests/tests/test_migration_v2.rs @@ -7,7 +7,7 @@ use mars_testing::mock_dependencies; #[test] fn wrong_contract_name() { let mut deps = mock_dependencies(&[]); - cw2::set_contract_version(deps.as_mut().storage, "contract_xyz", "1.2.0").unwrap(); + cw2::set_contract_version(deps.as_mut().storage, "contract_xyz", "2.1.0").unwrap(); let err = migrate(deps.as_mut(), mock_env(), Empty {}).unwrap_err(); @@ -35,7 +35,7 @@ fn wrong_contract_version() { assert_eq!( err, ContractError::Version(VersionError::WrongVersion { - expected: "1.2.0".to_string(), + expected: "2.1.0".to_string(), found: "4.1.0".to_string() }) ); @@ -47,7 +47,7 @@ fn successful_migration() { cw2::set_contract_version( deps.as_mut().storage, "crates.io:mars-rewards-collector-neutron", - "1.2.0", + "2.1.0", ) .unwrap(); @@ -58,7 +58,7 @@ fn successful_migration() { assert!(res.data.is_none()); assert_eq!( res.attributes, - vec![attr("action", "migrate"), attr("from_version", "1.2.0"), attr("to_version", "2.2.0")] + vec![attr("action", "migrate"), attr("from_version", "2.1.0"), attr("to_version", "2.2.0")] ); let new_contract_version = ContractVersion { diff --git a/contracts/swapper/astroport/src/contract.rs b/contracts/swapper/astroport/src/contract.rs index b9ab70fc..3759cd23 100644 --- a/contracts/swapper/astroport/src/contract.rs +++ b/contracts/swapper/astroport/src/contract.rs @@ -39,5 +39,5 @@ pub fn query(deps: Deps, env: Env, msg: QueryMsg) -> ContractResult { #[cfg_attr(not(feature = "library"), entry_point)] pub fn migrate(deps: DepsMut, _env: Env, _msg: Empty) -> Result { - migrations::v2_0_0::migrate(deps) + migrations::v2_2_0::migrate(deps) } diff --git a/contracts/swapper/astroport/src/migrations/mod.rs b/contracts/swapper/astroport/src/migrations/mod.rs index 7592b6f1..8fd44388 100644 --- a/contracts/swapper/astroport/src/migrations/mod.rs +++ b/contracts/swapper/astroport/src/migrations/mod.rs @@ -1 +1 @@ -pub mod v2_0_0; +pub mod v2_2_0; diff --git a/contracts/swapper/astroport/src/migrations/v2_0_0.rs b/contracts/swapper/astroport/src/migrations/v2_2_0.rs similarity index 95% rename from contracts/swapper/astroport/src/migrations/v2_0_0.rs rename to contracts/swapper/astroport/src/migrations/v2_2_0.rs index 3dbff64c..49b6fd75 100644 --- a/contracts/swapper/astroport/src/migrations/v2_0_0.rs +++ b/contracts/swapper/astroport/src/migrations/v2_2_0.rs @@ -4,7 +4,7 @@ use mars_swapper_base::ContractError; use crate::contract::{CONTRACT_NAME, CONTRACT_VERSION}; -const FROM_VERSION: &str = "1.2.0"; +const FROM_VERSION: &str = "2.1.0"; pub fn migrate(deps: DepsMut) -> Result { // make sure we're migrating the correct contract and from the correct version diff --git a/contracts/swapper/astroport/tests/tests/test_migration_v2.rs b/contracts/swapper/astroport/tests/tests/test_migration_v2.rs index 8d5e5178..addd8382 100644 --- a/contracts/swapper/astroport/tests/tests/test_migration_v2.rs +++ b/contracts/swapper/astroport/tests/tests/test_migration_v2.rs @@ -7,7 +7,7 @@ use mars_testing::mock_dependencies; #[test] fn wrong_contract_name() { let mut deps = mock_dependencies(&[]); - cw2::set_contract_version(deps.as_mut().storage, "contract_xyz", "1.2.0").unwrap(); + cw2::set_contract_version(deps.as_mut().storage, "contract_xyz", "2.1.0").unwrap(); let err = migrate(deps.as_mut(), mock_env(), Empty {}).unwrap_err(); @@ -31,7 +31,7 @@ fn wrong_contract_version() { assert_eq!( err, ContractError::Version(VersionError::WrongVersion { - expected: "1.2.0".to_string(), + expected: "2.1.0".to_string(), found: "4.1.0".to_string() }) ); @@ -40,7 +40,7 @@ fn wrong_contract_version() { #[test] fn successful_migration() { let mut deps = mock_dependencies(&[]); - cw2::set_contract_version(deps.as_mut().storage, "crates.io:mars-swapper-astroport", "1.2.0") + cw2::set_contract_version(deps.as_mut().storage, "crates.io:mars-swapper-astroport", "2.1.0") .unwrap(); let res = migrate(deps.as_mut(), mock_env(), Empty {}).unwrap(); @@ -50,7 +50,7 @@ fn successful_migration() { assert!(res.data.is_none()); assert_eq!( res.attributes, - vec![attr("action", "migrate"), attr("from_version", "1.2.0"), attr("to_version", "2.2.0")] + vec![attr("action", "migrate"), attr("from_version", "2.1.0"), attr("to_version", "2.2.0")] ); let new_contract_version = ContractVersion { diff --git a/contracts/v2-zapper/astroport/src/contract.rs b/contracts/v2-zapper/astroport/src/contract.rs index b65da870..a8127cb2 100644 --- a/contracts/v2-zapper/astroport/src/contract.rs +++ b/contracts/v2-zapper/astroport/src/contract.rs @@ -1,9 +1,11 @@ -use cosmwasm_std::{entry_point, Binary, Deps, DepsMut, Env, MessageInfo, Response, StdResult}; +use cosmwasm_std::{ + entry_point, Binary, Deps, DepsMut, Empty, Env, MessageInfo, Response, StdResult, +}; use cw2::set_contract_version; use mars_types::zapper::{ExecuteMsg, InstantiateMsg, QueryMsg}; use mars_zapper_base::{ContractError, ZapperBase}; -use crate::lp_pool::AstroportLpPool; +use crate::{lp_pool::AstroportLpPool, migrations}; /// The Astroport zapper contract inherits logic from the base zapper contract pub type AstroportZapper = ZapperBase; @@ -36,3 +38,8 @@ pub fn execute( pub fn query(deps: Deps, env: Env, msg: QueryMsg) -> StdResult { AstroportZapper::default().query(deps, env, msg) } + +#[cfg_attr(not(feature = "library"), entry_point)] +pub fn migrate(deps: DepsMut, _env: Env, _msg: Empty) -> Result { + migrations::v2_2_0::migrate(deps) +} diff --git a/contracts/v2-zapper/astroport/src/lib.rs b/contracts/v2-zapper/astroport/src/lib.rs index d2b15554..e6ab3aff 100644 --- a/contracts/v2-zapper/astroport/src/lib.rs +++ b/contracts/v2-zapper/astroport/src/lib.rs @@ -1,2 +1,3 @@ pub mod contract; pub mod lp_pool; +pub mod migrations; diff --git a/contracts/v2-zapper/astroport/src/migrations/mod.rs b/contracts/v2-zapper/astroport/src/migrations/mod.rs new file mode 100644 index 00000000..8fd44388 --- /dev/null +++ b/contracts/v2-zapper/astroport/src/migrations/mod.rs @@ -0,0 +1 @@ +pub mod v2_2_0; diff --git a/contracts/v2-zapper/astroport/src/migrations/v2_2_0.rs b/contracts/v2-zapper/astroport/src/migrations/v2_2_0.rs new file mode 100644 index 00000000..88eddb05 --- /dev/null +++ b/contracts/v2-zapper/astroport/src/migrations/v2_2_0.rs @@ -0,0 +1,19 @@ +use cosmwasm_std::{DepsMut, Response}; +use cw2::{assert_contract_version, set_contract_version}; +use mars_zapper_base::ContractError; + +use crate::contract::{CONTRACT_NAME, CONTRACT_VERSION}; + +const FROM_VERSION: &str = "2.1.0"; + +pub fn migrate(deps: DepsMut) -> Result { + // make sure we're migrating the correct contract and from the correct version + assert_contract_version(deps.storage, &format!("crates.io:{CONTRACT_NAME}"), FROM_VERSION)?; + + set_contract_version(deps.storage, format!("crates.io:{CONTRACT_NAME}"), CONTRACT_VERSION)?; + + Ok(Response::new() + .add_attribute("action", "migrate") + .add_attribute("from_version", FROM_VERSION) + .add_attribute("to_version", CONTRACT_VERSION)) +} diff --git a/contracts/v2-zapper/astroport/tests/all_tests.rs b/contracts/v2-zapper/astroport/tests/all_tests.rs new file mode 100644 index 00000000..14f00389 --- /dev/null +++ b/contracts/v2-zapper/astroport/tests/all_tests.rs @@ -0,0 +1 @@ +mod tests; diff --git a/contracts/v2-zapper/astroport/tests/tests/mod.rs b/contracts/v2-zapper/astroport/tests/tests/mod.rs new file mode 100644 index 00000000..c6a4f2bc --- /dev/null +++ b/contracts/v2-zapper/astroport/tests/tests/mod.rs @@ -0,0 +1 @@ +mod test_migration_v2; diff --git a/contracts/v2-zapper/astroport/tests/tests/test_migration_v2.rs b/contracts/v2-zapper/astroport/tests/tests/test_migration_v2.rs new file mode 100644 index 00000000..596df025 --- /dev/null +++ b/contracts/v2-zapper/astroport/tests/tests/test_migration_v2.rs @@ -0,0 +1,61 @@ +use cosmwasm_std::{attr, testing::mock_env, Empty, Event}; +use cw2::{ContractVersion, VersionError}; +use mars_testing::mock_dependencies; +use mars_zapper_astroport::contract::migrate; +use mars_zapper_base::ContractError; + +#[test] +fn wrong_contract_name() { + let mut deps = mock_dependencies(&[]); + cw2::set_contract_version(deps.as_mut().storage, "contract_xyz", "2.1.0").unwrap(); + + let err = migrate(deps.as_mut(), mock_env(), Empty {}).unwrap_err(); + + assert_eq!( + err, + ContractError::Version(VersionError::WrongContract { + expected: "crates.io:mars-zapper-astroport".to_string(), + found: "contract_xyz".to_string() + }) + ); +} + +#[test] +fn wrong_contract_version() { + let mut deps = mock_dependencies(&[]); + cw2::set_contract_version(deps.as_mut().storage, "crates.io:mars-zapper-astroport", "4.1.0") + .unwrap(); + + let err = migrate(deps.as_mut(), mock_env(), Empty {}).unwrap_err(); + + assert_eq!( + err, + ContractError::Version(VersionError::WrongVersion { + expected: "2.1.0".to_string(), + found: "4.1.0".to_string() + }) + ); +} + +#[test] +fn successful_migration() { + let mut deps = mock_dependencies(&[]); + cw2::set_contract_version(deps.as_mut().storage, "crates.io:mars-zapper-astroport", "2.1.0") + .unwrap(); + + let res = migrate(deps.as_mut(), mock_env(), Empty {}).unwrap(); + + assert_eq!(res.messages, vec![]); + assert_eq!(res.events, vec![] as Vec); + assert!(res.data.is_none()); + assert_eq!( + res.attributes, + vec![attr("action", "migrate"), attr("from_version", "2.1.0"), attr("to_version", "2.2.0")] + ); + + let new_contract_version = ContractVersion { + contract: "crates.io:mars-zapper-astroport".to_string(), + version: "2.2.0".to_string(), + }; + assert_eq!(cw2::get_contract_version(deps.as_ref().storage).unwrap(), new_contract_version); +} diff --git a/packages/types/Cargo.toml b/packages/types/Cargo.toml index 224f583f..410f1dfa 100644 --- a/packages/types/Cargo.toml +++ b/packages/types/Cargo.toml @@ -22,6 +22,7 @@ javascript = ["tsify", "wasm-bindgen"] [dependencies] cosmwasm-schema = { workspace = true } cosmwasm-std = { workspace = true } +cw2 = { workspace = true } cw721 = { workspace = true } cw721-base = { workspace = true } cw-paginate = { workspace = true } diff --git a/packages/types/src/health/error.rs b/packages/types/src/health/error.rs index 31c5db74..f14e30d8 100644 --- a/packages/types/src/health/error.rs +++ b/packages/types/src/health/error.rs @@ -3,6 +3,7 @@ use cosmwasm_std::{ ConversionOverflowError, DecimalRangeExceeded, DivideByZeroError, OverflowError, SignedDecimalRangeExceeded, StdError, }; +use cw2::VersionError; use mars_owner::OwnerError; use thiserror::Error; @@ -79,4 +80,7 @@ pub enum HealthError { #[error("{0}")] CheckedMultiplyRatio(#[from] CheckedMultiplyRatioError), + + #[error("{0}")] + Version(#[from] VersionError), } diff --git a/packages/types/src/incentives.rs b/packages/types/src/incentives.rs index 699b7fe0..308abf9c 100644 --- a/packages/types/src/incentives.rs +++ b/packages/types/src/incentives.rs @@ -203,12 +203,12 @@ pub enum ExecuteMsg { UpdateOwner(OwnerUpdate), // Manages migration. It is used to handle migration in batches to avoid out of gas errors. - Migrate(MigrateV2ToV2_0_1), + Migrate(MigrateV2_1_0ToV2_2_0), } -/// Migrate from V2 to V2_0_1, only owner can call +/// Migrate from V2_1_0 to V2_2_0, only owner can call #[cw_serde] -pub enum MigrateV2ToV2_0_1 { +pub enum MigrateV2_1_0ToV2_2_0 { /// Migrate users unclaimed rewards UserUnclaimedRewards { limit: u32, diff --git a/packages/types/src/params/msg.rs b/packages/types/src/params/msg.rs index d323859c..f62fbcb7 100644 --- a/packages/types/src/params/msg.rs +++ b/packages/types/src/params/msg.rs @@ -1,5 +1,5 @@ use cosmwasm_schema::{cw_serde, QueryResponses}; -use cosmwasm_std::Uint128; +use cosmwasm_std::{Decimal, Uint128}; use mars_owner::OwnerUpdate; use super::{asset::AssetParamsUnchecked, vault::VaultConfigUnchecked, PerpParams}; @@ -174,3 +174,8 @@ pub enum EmergencyUpdate { RedBank(RedBankEmergencyUpdate), Perps(PerpsEmergencyUpdate), } + +#[cw_serde] +pub struct MigrateMsg { + pub close_factor: Decimal, +} diff --git a/packages/types/src/red_bank/msg.rs b/packages/types/src/red_bank/msg.rs index b33c970f..fb219edb 100644 --- a/packages/types/src/red_bank/msg.rs +++ b/packages/types/src/red_bank/msg.rs @@ -102,8 +102,6 @@ pub enum ExecuteMsg { /// Option to enable (true) / disable (false) asset as collateral enable: bool, }, - // Manages migration. It is used to handle migration in batches to avoid out of gas errors. - Migrate(MigrateV1ToV2), } #[cw_serde] diff --git a/schemas/mars-incentives/mars-incentives.json b/schemas/mars-incentives/mars-incentives.json index b4109bc7..7cd594fb 100644 --- a/schemas/mars-incentives/mars-incentives.json +++ b/schemas/mars-incentives/mars-incentives.json @@ -398,7 +398,7 @@ ], "properties": { "migrate": { - "$ref": "#/definitions/MigrateV2ToV2_0_1" + "$ref": "#/definitions/MigrateV2_1_0ToV2_2_0" } }, "additionalProperties": false @@ -469,8 +469,8 @@ "perp_vault" ] }, - "MigrateV2ToV2_0_1": { - "description": "Migrate from V2 to V2_0_1, only owner can call", + "MigrateV2_1_0ToV2_2_0": { + "description": "Migrate from V2_1_0 to V2_2_0, only owner can call", "oneOf": [ { "description": "Migrate users unclaimed rewards", diff --git a/schemas/mars-red-bank/mars-red-bank.json b/schemas/mars-red-bank/mars-red-bank.json index 0d5f5c2b..8893bc0b 100644 --- a/schemas/mars-red-bank/mars-red-bank.json +++ b/schemas/mars-red-bank/mars-red-bank.json @@ -351,18 +351,6 @@ } }, "additionalProperties": false - }, - { - "type": "object", - "required": [ - "migrate" - ], - "properties": { - "migrate": { - "$ref": "#/definitions/MigrateV1ToV2" - } - }, - "additionalProperties": false } ], "definitions": { @@ -454,49 +442,6 @@ }, "additionalProperties": false }, - "MigrateV1ToV2": { - "description": "Migrate from V1 to V2, only owner can call", - "oneOf": [ - { - "description": "Migrate collaterals in batches", - "type": "object", - "required": [ - "collaterals" - ], - "properties": { - "collaterals": { - "type": "object", - "required": [ - "limit" - ], - "properties": { - "limit": { - "type": "integer", - "format": "uint32", - "minimum": 0.0 - } - }, - "additionalProperties": false - } - }, - "additionalProperties": false - }, - { - "description": "Clears old V1 state once all batches are migrated or after a certain time", - "type": "object", - "required": [ - "clear_v1_state" - ], - "properties": { - "clear_v1_state": { - "type": "object", - "additionalProperties": false - } - }, - "additionalProperties": false - } - ] - }, "OwnerUpdate": { "oneOf": [ { diff --git a/scripts/types/generated/mars-incentives/MarsIncentives.client.ts b/scripts/types/generated/mars-incentives/MarsIncentives.client.ts index 08979420..f0e42fa4 100644 --- a/scripts/types/generated/mars-incentives/MarsIncentives.client.ts +++ b/scripts/types/generated/mars-incentives/MarsIncentives.client.ts @@ -15,7 +15,7 @@ import { Addr, ActionAmount, OwnerUpdate, - MigrateV2ToV2_0_1, + MigrateV2_1_0ToV2_2_0, WhitelistEntry, Coin, ActionCoin, @@ -451,7 +451,7 @@ export interface MarsIncentivesInterface extends MarsIncentivesReadOnlyInterface _funds?: Coin[], ) => Promise migrate: ( - migrateV2ToV201: MigrateV2ToV2_0_1, + migrateV210ToV220: MigrateV2_1_0ToV2_2_0, fee?: number | StdFee | 'auto', memo?: string, _funds?: Coin[], @@ -739,7 +739,7 @@ export class MarsIncentivesClient ) } migrate = async ( - migrateV2ToV201: MigrateV2ToV2_0_1, + migrateV210ToV220: MigrateV2_1_0ToV2_2_0, fee: number | StdFee | 'auto' = 'auto', memo?: string, _funds?: Coin[], @@ -748,7 +748,7 @@ export class MarsIncentivesClient this.sender, this.contractAddress, { - migrate: migrateV2ToV201, + migrate: migrateV210ToV220, }, fee, memo, diff --git a/scripts/types/generated/mars-incentives/MarsIncentives.react-query.ts b/scripts/types/generated/mars-incentives/MarsIncentives.react-query.ts index 350700c4..a835a862 100644 --- a/scripts/types/generated/mars-incentives/MarsIncentives.react-query.ts +++ b/scripts/types/generated/mars-incentives/MarsIncentives.react-query.ts @@ -16,7 +16,7 @@ import { Addr, ActionAmount, OwnerUpdate, - MigrateV2ToV2_0_1, + MigrateV2_1_0ToV2_2_0, WhitelistEntry, Coin, ActionCoin, @@ -445,7 +445,7 @@ export function useMarsIncentivesStakedAstroLpRewardsQuery< } export interface MarsIncentivesMigrateMutation { client: MarsIncentivesClient - msg: MigrateV2ToV2_0_1 + msg: MigrateV2_1_0ToV2_2_0 args?: { fee?: number | StdFee | 'auto' memo?: string diff --git a/scripts/types/generated/mars-incentives/MarsIncentives.types.ts b/scripts/types/generated/mars-incentives/MarsIncentives.types.ts index eccfb1e2..9052491f 100644 --- a/scripts/types/generated/mars-incentives/MarsIncentives.types.ts +++ b/scripts/types/generated/mars-incentives/MarsIncentives.types.ts @@ -75,7 +75,7 @@ export type ExecuteMsg = update_owner: OwnerUpdate } | { - migrate: MigrateV2ToV2_0_1 + migrate: MigrateV2_1_0ToV2_2_0 } export type Uint128 = string export type IncentiveKind = 'red_bank' | 'perp_vault' @@ -100,7 +100,7 @@ export type OwnerUpdate = } } | 'clear_emergency_owner' -export type MigrateV2ToV2_0_1 = +export type MigrateV2_1_0ToV2_2_0 = | { user_unclaimed_rewards: { limit: number diff --git a/scripts/types/generated/mars-red-bank/MarsRedBank.client.ts b/scripts/types/generated/mars-red-bank/MarsRedBank.client.ts index 8aa61f4b..5b4adef1 100644 --- a/scripts/types/generated/mars-red-bank/MarsRedBank.client.ts +++ b/scripts/types/generated/mars-red-bank/MarsRedBank.client.ts @@ -14,7 +14,6 @@ import { OwnerUpdate, Decimal, Uint128, - MigrateV1ToV2, InitOrUpdateAssetParams, InterestRateModel, QueryMsg, @@ -489,12 +488,6 @@ export interface MarsRedBankInterface extends MarsRedBankReadOnlyInterface { memo?: string, _funds?: Coin[], ) => Promise - migrate: ( - migrateV1ToV2: MigrateV1ToV2, - fee?: number | StdFee | 'auto', - memo?: string, - _funds?: Coin[], - ) => Promise } export class MarsRedBankClient extends MarsRedBankQueryClient implements MarsRedBankInterface { client: SigningCosmWasmClient @@ -515,7 +508,6 @@ export class MarsRedBankClient extends MarsRedBankQueryClient implements MarsRed this.repay = this.repay.bind(this) this.liquidate = this.liquidate.bind(this) this.updateAssetCollateralStatus = this.updateAssetCollateralStatus.bind(this) - this.migrate = this.migrate.bind(this) } updateOwner = async ( ownerUpdate: OwnerUpdate, @@ -777,21 +769,4 @@ export class MarsRedBankClient extends MarsRedBankQueryClient implements MarsRed _funds, ) } - migrate = async ( - migrateV1ToV2: MigrateV1ToV2, - fee: number | StdFee | 'auto' = 'auto', - memo?: string, - _funds?: Coin[], - ): Promise => { - return await this.client.execute( - this.sender, - this.contractAddress, - { - migrate: migrateV1ToV2, - }, - fee, - memo, - _funds, - ) - } } diff --git a/scripts/types/generated/mars-red-bank/MarsRedBank.react-query.ts b/scripts/types/generated/mars-red-bank/MarsRedBank.react-query.ts index 7f848782..f1d3381c 100644 --- a/scripts/types/generated/mars-red-bank/MarsRedBank.react-query.ts +++ b/scripts/types/generated/mars-red-bank/MarsRedBank.react-query.ts @@ -15,7 +15,6 @@ import { OwnerUpdate, Decimal, Uint128, - MigrateV1ToV2, InitOrUpdateAssetParams, InterestRateModel, QueryMsg, @@ -616,26 +615,6 @@ export function useMarsRedBankConfigQuery({ }, ) } -export interface MarsRedBankMigrateMutation { - client: MarsRedBankClient - msg: MigrateV1ToV2 - args?: { - fee?: number | StdFee | 'auto' - memo?: string - funds?: Coin[] - } -} -export function useMarsRedBankMigrateMutation( - options?: Omit< - UseMutationOptions, - 'mutationFn' - >, -) { - return useMutation( - ({ client, msg, args: { fee, memo, funds } = {} }) => client.migrate(msg, fee, memo, funds), - options, - ) -} export interface MarsRedBankUpdateAssetCollateralStatusMutation { client: MarsRedBankClient msg: { diff --git a/scripts/types/generated/mars-red-bank/MarsRedBank.types.ts b/scripts/types/generated/mars-red-bank/MarsRedBank.types.ts index bd54d453..10a38ba0 100644 --- a/scripts/types/generated/mars-red-bank/MarsRedBank.types.ts +++ b/scripts/types/generated/mars-red-bank/MarsRedBank.types.ts @@ -73,9 +73,6 @@ export type ExecuteMsg = enable: boolean } } - | { - migrate: MigrateV1ToV2 - } export type OwnerUpdate = | { propose_new_owner: { @@ -93,15 +90,6 @@ export type OwnerUpdate = | 'clear_emergency_owner' export type Decimal = string export type Uint128 = string -export type MigrateV1ToV2 = - | { - collaterals: { - limit: number - } - } - | { - clear_v1_state: {} - } export interface InitOrUpdateAssetParams { interest_rate_model?: InterestRateModel | null reserve_factor?: Decimal | null