diff --git a/Cargo.lock b/Cargo.lock index 0e1f228c..5f137a40 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3045,7 +3045,7 @@ dependencies = [ [[package]] name = "mars-red-bank" -version = "2.3.2" +version = "2.3.3" dependencies = [ "anyhow", "cosmwasm-schema 1.5.7", diff --git a/contracts/red-bank/Cargo.toml b/contracts/red-bank/Cargo.toml index 094affe5..25016988 100644 --- a/contracts/red-bank/Cargo.toml +++ b/contracts/red-bank/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "mars-red-bank" description = "A smart contract that manages asset deposit, borrowing, and liquidations" -version = "2.3.2" +version = "2.3.3" authors = { workspace = true } edition = { workspace = true } license = { workspace = true } diff --git a/contracts/red-bank/src/contract.rs b/contracts/red-bank/src/contract.rs index d846b00c..ebb5ab49 100644 --- a/contracts/red-bank/src/contract.rs +++ b/contracts/red-bank/src/contract.rs @@ -237,6 +237,10 @@ pub fn migrate(deps: DepsMut, _env: Env, msg: MigrateMsg) -> Result migrations::v2_3_0::migrate(deps), MigrateMsg::V2_3_0ToV2_3_1 {} => migrations::v2_3_1::migrate(deps), + MigrateMsg::V2_3_2ToV2_3_3 { + haircut, + market, + } => migrations::v2_3_3::migrate(deps, haircut, &market), MigrateMsg::V2_3_1ToV2_3_2 {} => migrations::v2_3_2::migrate(deps), } } diff --git a/contracts/red-bank/src/migrations/mod.rs b/contracts/red-bank/src/migrations/mod.rs index 9c182a09..11393a6e 100644 --- a/contracts/red-bank/src/migrations/mod.rs +++ b/contracts/red-bank/src/migrations/mod.rs @@ -1,3 +1,4 @@ pub mod v2_3_0; pub mod v2_3_1; pub mod v2_3_2; +pub mod v2_3_3; diff --git a/contracts/red-bank/src/migrations/v2_3_3.rs b/contracts/red-bank/src/migrations/v2_3_3.rs new file mode 100644 index 00000000..e1e1489c --- /dev/null +++ b/contracts/red-bank/src/migrations/v2_3_3.rs @@ -0,0 +1,47 @@ +use cosmwasm_std::{Addr, Decimal, DepsMut, Response}; +use cw2::{assert_contract_version, set_contract_version}; +use mars_types::keys::{UserId, UserIdKey}; + +use crate::{ + contract::{CONTRACT_NAME, CONTRACT_VERSION}, + error::ContractError, + state::{COLLATERALS, MARKETS}, +}; + +const FROM_VERSION: &str = "2.3.2"; + +pub fn migrate(deps: DepsMut, haircut: Decimal, denom: &str) -> 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)?; + // Load affected market + let mut market = MARKETS.load(deps.storage, denom)?; + // Apply haircut + let new_index = market.liquidity_index.checked_mul(Decimal::one().checked_sub(haircut)?)?; + market.liquidity_index = new_index; + // Save new state + MARKETS.save(deps.storage, denom, &market)?; + + // Remove MPF collateral + let mpf_account_id = "4954"; + let acc_id = mpf_account_id.to_string(); + + let user_id = UserId::credit_manager( + Addr::unchecked( + "neutron1qdzn3l4kn7gsjna2tfpg3g3mwd6kunx4p50lfya59k02846xas6qslgs3r".to_string(), + ), + acc_id, + ); + let user_id_key: UserIdKey = user_id.try_into()?; + + COLLATERALS.remove(deps.storage, (&user_id_key, denom)); + + 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) + .add_attribute("to_version", CONTRACT_VERSION) + .add_attribute("haircut_percent", haircut.to_string()) + .add_attribute("haircut_market", denom)) +} diff --git a/contracts/red-bank/tests/tests/test_migration_v2.rs b/contracts/red-bank/tests/tests/test_migration_v2.rs index decc40f7..f1b7ef67 100644 --- a/contracts/red-bank/tests/tests/test_migration_v2.rs +++ b/contracts/red-bank/tests/tests/test_migration_v2.rs @@ -1,8 +1,19 @@ -use cosmwasm_std::{attr, testing::mock_env, Event}; +use cosmwasm_std::{attr, testing::mock_env, Addr, Decimal, Event, Uint128}; use cw2::{ContractVersion, VersionError}; -use mars_red_bank::{contract::migrate, error::ContractError}; +use mars_red_bank::{ + contract::{migrate, CONTRACT_VERSION}, + error::ContractError, + state::{COLLATERALS, MARKETS}, +}; use mars_testing::mock_dependencies; -use mars_types::red_bank::MigrateMsg; +use mars_types::{ + keys::{UserId, UserIdKey}, + red_bank::{Collateral, Market, MigrateMsg}, +}; + +const CONTRACT_NAME: &str = "crates.io:mars-red-bank"; + +const FROM_VERSION_V2_3_2: &str = "2.3.2"; #[test] fn v2_2_0_to_v2_3_0_wrong_contract_name() { @@ -23,7 +34,7 @@ fn v2_2_0_to_v2_3_0_wrong_contract_name() { #[test] fn v2_2_0_to_v2_3_0_wrong_contract_version() { let mut deps = mock_dependencies(&[]); - cw2::set_contract_version(deps.as_mut().storage, "crates.io:mars-red-bank", "4.1.0").unwrap(); + cw2::set_contract_version(deps.as_mut().storage, CONTRACT_NAME, "4.1.0").unwrap(); let err = migrate(deps.as_mut(), mock_env(), MigrateMsg::V2_2_0ToV2_3_0 {}).unwrap_err(); @@ -39,7 +50,7 @@ fn v2_2_0_to_v2_3_0_wrong_contract_version() { #[test] fn v2_2_0_to_v2_3_0_successful_migration() { let mut deps = mock_dependencies(&[]); - cw2::set_contract_version(deps.as_mut().storage, "crates.io:mars-red-bank", "2.2.0").unwrap(); + cw2::set_contract_version(deps.as_mut().storage, CONTRACT_NAME, "2.2.0").unwrap(); let res = migrate(deps.as_mut(), mock_env(), MigrateMsg::V2_2_0ToV2_3_0 {}).unwrap(); @@ -52,7 +63,7 @@ fn v2_2_0_to_v2_3_0_successful_migration() { ); let new_contract_version = ContractVersion { - contract: "crates.io:mars-red-bank".to_string(), + contract: CONTRACT_NAME.to_string(), version: "2.3.0".to_string(), }; assert_eq!(cw2::get_contract_version(deps.as_ref().storage).unwrap(), new_contract_version); @@ -77,7 +88,7 @@ fn v2_3_0_to_v2_3_1_wrong_contract_name() { #[test] fn v2_3_0_to_v2_3_1_wrong_contract_version() { let mut deps = mock_dependencies(&[]); - cw2::set_contract_version(deps.as_mut().storage, "crates.io:mars-red-bank", "2.2.0").unwrap(); + cw2::set_contract_version(deps.as_mut().storage, CONTRACT_NAME, "2.2.0").unwrap(); let err = migrate(deps.as_mut(), mock_env(), MigrateMsg::V2_3_0ToV2_3_1 {}).unwrap_err(); @@ -93,7 +104,7 @@ fn v2_3_0_to_v2_3_1_wrong_contract_version() { #[test] fn v2_3_0_to_v2_3_1_successful_migration() { let mut deps = mock_dependencies(&[]); - cw2::set_contract_version(deps.as_mut().storage, "crates.io:mars-red-bank", "2.3.0").unwrap(); + cw2::set_contract_version(deps.as_mut().storage, CONTRACT_NAME, "2.3.0").unwrap(); let res = migrate(deps.as_mut(), mock_env(), MigrateMsg::V2_3_0ToV2_3_1 {}).unwrap(); @@ -106,62 +117,117 @@ fn v2_3_0_to_v2_3_1_successful_migration() { ); let new_contract_version = ContractVersion { - contract: "crates.io:mars-red-bank".to_string(), + contract: CONTRACT_NAME.to_string(), version: "2.3.1".to_string(), }; assert_eq!(cw2::get_contract_version(deps.as_ref().storage).unwrap(), new_contract_version); } #[test] -fn v2_3_1_to_v2_3_2_wrong_contract_name() { +fn v2_3_2_to_v2_3_3_wrong_contract_name() { let mut deps = mock_dependencies(&[]); - cw2::set_contract_version(deps.as_mut().storage, "contract_xyz", "2.3.1").unwrap(); - - let err = migrate(deps.as_mut(), mock_env(), MigrateMsg::V2_3_1ToV2_3_2 {}).unwrap_err(); + cw2::set_contract_version(deps.as_mut().storage, "contract_xyz", FROM_VERSION_V2_3_2).unwrap(); + + let err = migrate( + deps.as_mut(), + mock_env(), + MigrateMsg::V2_3_2ToV2_3_3 { + haircut: Decimal::percent(10), + market: "umars".to_string(), + }, + ) + .unwrap_err(); assert_eq!( err, ContractError::Version(VersionError::WrongContract { - expected: "crates.io:mars-red-bank".to_string(), + expected: CONTRACT_NAME.to_string(), found: "contract_xyz".to_string() }) ); } #[test] -fn v2_3_1_to_v2_3_2_wrong_contract_version() { +fn v2_3_2_to_v2_3_3_wrong_contract_version() { let mut deps = mock_dependencies(&[]); - cw2::set_contract_version(deps.as_mut().storage, "crates.io:mars-red-bank", "2.3.0").unwrap(); - - let err = migrate(deps.as_mut(), mock_env(), MigrateMsg::V2_3_1ToV2_3_2 {}).unwrap_err(); + cw2::set_contract_version(deps.as_mut().storage, CONTRACT_NAME, "2.3.0").unwrap(); + + let err = migrate( + deps.as_mut(), + mock_env(), + MigrateMsg::V2_3_2ToV2_3_3 { + haircut: Decimal::percent(10), + market: "umars".to_string(), + }, + ) + .unwrap_err(); assert_eq!( err, ContractError::Version(VersionError::WrongVersion { - expected: "2.3.1".to_string(), + expected: FROM_VERSION_V2_3_2.to_string(), found: "2.3.0".to_string() }) ); } #[test] -fn v2_3_1_to_v2_3_2_successful_migration() { +fn v2_3_2_to_v2_3_3_successful_migration() { let mut deps = mock_dependencies(&[]); - cw2::set_contract_version(deps.as_mut().storage, "crates.io:mars-red-bank", "2.3.1").unwrap(); + cw2::set_contract_version(deps.as_mut().storage, CONTRACT_NAME, FROM_VERSION_V2_3_2).unwrap(); - let res = migrate(deps.as_mut(), mock_env(), MigrateMsg::V2_3_1ToV2_3_2 {}).unwrap(); + let denom = "umars"; + let market = Market { + denom: denom.to_string(), + liquidity_index: Decimal::percent(200), + ..Market::default() + }; + MARKETS.save(deps.as_mut().storage, denom, &market).unwrap(); + + let user_addr = + Addr::unchecked("neutron1qdzn3l4kn7gsjna2tfpg3g3mwd6kunx4p50lfya59k02846xas6qslgs3r"); + let user_id = UserId::credit_manager(user_addr, "4954".to_string()); + let user_id_key: UserIdKey = user_id.try_into().unwrap(); + let collateral = Collateral { + amount_scaled: Uint128::new(1234), + enabled: true, + }; + COLLATERALS.save(deps.as_mut().storage, (&user_id_key, denom), &collateral).unwrap(); + + let haircut = Decimal::percent(10); + let res = migrate( + deps.as_mut(), + mock_env(), + MigrateMsg::V2_3_2ToV2_3_3 { + haircut, + market: denom.to_string(), + }, + ) + .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.3.1"), attr("to_version", "2.3.2")] + vec![ + attr("action", "migrate"), + attr("from_version", FROM_VERSION_V2_3_2), + attr("to_version", CONTRACT_VERSION), + attr("to_version", CONTRACT_VERSION), + attr("haircut_percent", haircut.to_string()), + attr("haircut_market", denom), + ] ); + let new_market = MARKETS.load(deps.as_ref().storage, denom).unwrap(); + assert_eq!(new_market.liquidity_index, Decimal::percent(180)); + + assert!(COLLATERALS.may_load(deps.as_ref().storage, (&user_id_key, denom)).unwrap().is_none()); + let new_contract_version = ContractVersion { - contract: "crates.io:mars-red-bank".to_string(), - version: "2.3.2".to_string(), + contract: CONTRACT_NAME.to_string(), + version: CONTRACT_VERSION.to_string(), }; assert_eq!(cw2::get_contract_version(deps.as_ref().storage).unwrap(), new_contract_version); } diff --git a/packages/types/src/red_bank/msg.rs b/packages/types/src/red_bank/msg.rs index 7b97c5f4..59a6e012 100644 --- a/packages/types/src/red_bank/msg.rs +++ b/packages/types/src/red_bank/msg.rs @@ -261,4 +261,8 @@ pub enum MigrateMsg { V2_2_0ToV2_3_0 {}, V2_3_0ToV2_3_1 {}, V2_3_1ToV2_3_2 {}, + V2_3_2ToV2_3_3 { + haircut: Decimal, + market: String, + }, } diff --git a/schemas/mars-red-bank/mars-red-bank.json b/schemas/mars-red-bank/mars-red-bank.json index 80455e0f..a7121401 100644 --- a/schemas/mars-red-bank/mars-red-bank.json +++ b/schemas/mars-red-bank/mars-red-bank.json @@ -1,6 +1,6 @@ { "contract_name": "mars-red-bank", - "contract_version": "2.3.2", + "contract_version": "2.3.3", "idl_version": "1.0.0", "instantiate": { "$schema": "http://json-schema.org/draft-07/schema#",