From 90f8c4c6c438564e7dd2f6c125dce8e468631c7f Mon Sep 17 00:00:00 2001 From: mark Date: Wed, 24 Dec 2025 11:38:19 +1300 Subject: [PATCH 1/3] add 2.3.2 migration --- contracts/red-bank/Cargo.toml | 2 +- contracts/red-bank/src/contract.rs | 1 + contracts/red-bank/src/migrations/mod.rs | 1 + contracts/red-bank/src/migrations/v2_3_2.rs | 47 ++++++ .../red-bank/tests/tests/test_migration_v2.rs | 143 ++++++++++++++++-- packages/types/src/red_bank/msg.rs | 4 + 6 files changed, 188 insertions(+), 10 deletions(-) create mode 100644 contracts/red-bank/src/migrations/v2_3_2.rs diff --git a/contracts/red-bank/Cargo.toml b/contracts/red-bank/Cargo.toml index f0b40eda..094affe5 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.1" +version = "2.3.2" 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 93a222f6..51d44a45 100644 --- a/contracts/red-bank/src/contract.rs +++ b/contracts/red-bank/src/contract.rs @@ -237,5 +237,6 @@ 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_1ToV2_3_2 { haircut, market } => migrations::v2_3_2::migrate(deps, haircut, &market) } } diff --git a/contracts/red-bank/src/migrations/mod.rs b/contracts/red-bank/src/migrations/mod.rs index c83ce919..9c182a09 100644 --- a/contracts/red-bank/src/migrations/mod.rs +++ b/contracts/red-bank/src/migrations/mod.rs @@ -1,2 +1,3 @@ pub mod v2_3_0; pub mod v2_3_1; +pub mod v2_3_2; diff --git a/contracts/red-bank/src/migrations/v2_3_2.rs b/contracts/red-bank/src/migrations/v2_3_2.rs new file mode 100644 index 00000000..a3f3b62f --- /dev/null +++ b/contracts/red-bank/src/migrations/v2_3_2.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.1"; + +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 334926c8..8b72ea84 100644 --- a/contracts/red-bank/tests/tests/test_migration_v2.rs +++ b/contracts/red-bank/tests/tests/test_migration_v2.rs @@ -1,8 +1,18 @@ -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_1: &str = "2.3.1"; #[test] fn v2_2_0_to_v2_3_0_wrong_contract_name() { @@ -23,7 +33,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 +49,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 +62,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 +87,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 +103,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,8 +116,123 @@ 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() { + let mut deps = mock_dependencies(&[]); + cw2::set_contract_version(deps.as_mut().storage, "contract_xyz", FROM_VERSION_V2_3_1).unwrap(); + + let err = migrate( + deps.as_mut(), + mock_env(), + MigrateMsg::V2_3_1ToV2_3_2 { + haircut: Decimal::percent(10), + market: "umars".to_string(), + }, + ) + .unwrap_err(); + + assert_eq!( + err, + ContractError::Version(VersionError::WrongContract { + expected: CONTRACT_NAME.to_string(), + found: "contract_xyz".to_string() + }) + ); +} + +#[test] +fn v2_3_1_to_v2_3_2_wrong_contract_version() { + let mut deps = mock_dependencies(&[]); + 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_1ToV2_3_2 { + haircut: Decimal::percent(10), + market: "umars".to_string(), + }, + ) + .unwrap_err(); + + assert_eq!( + err, + ContractError::Version(VersionError::WrongVersion { + expected: FROM_VERSION_V2_3_1.to_string(), + found: "2.3.0".to_string() + }) + ); +} + +#[test] +fn v2_3_1_to_v2_3_2_successful_migration() { + let mut deps = mock_dependencies(&[]); + cw2::set_contract_version(deps.as_mut().storage, CONTRACT_NAME, FROM_VERSION_V2_3_1).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_1ToV2_3_2 { + 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", FROM_VERSION_V2_3_1), + 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: 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 907b3ec5..03742111 100644 --- a/packages/types/src/red_bank/msg.rs +++ b/packages/types/src/red_bank/msg.rs @@ -260,4 +260,8 @@ pub enum QueryMsg { pub enum MigrateMsg { V2_2_0ToV2_3_0 {}, V2_3_0ToV2_3_1 {}, + V2_3_1ToV2_3_2 { + haircut: Decimal, + market: String + }, } From 51f9ac32b6a27b69f8efc215aca2aac5b5085259 Mon Sep 17 00:00:00 2001 From: mark Date: Wed, 24 Dec 2025 16:24:34 +1300 Subject: [PATCH 2/3] bump version --- Cargo.lock | 2 +- contracts/red-bank/Cargo.toml | 2 +- contracts/red-bank/src/contract.rs | 5 +++- contracts/red-bank/src/migrations/mod.rs | 2 +- .../src/migrations/{v2_3_2.rs => v2_3_3.rs} | 18 ++++++------- .../red-bank/tests/tests/test_migration_v2.rs | 26 +++++++------------ packages/types/src/red_bank/msg.rs | 4 +-- schemas/mars-red-bank/mars-red-bank.json | 2 +- 8 files changed, 29 insertions(+), 32 deletions(-) rename contracts/red-bank/src/migrations/{v2_3_2.rs => v2_3_3.rs} (76%) diff --git a/Cargo.lock b/Cargo.lock index e5ce9670..5f137a40 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3045,7 +3045,7 @@ dependencies = [ [[package]] name = "mars-red-bank" -version = "2.3.1" +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 51d44a45..0b9c4f9b 100644 --- a/contracts/red-bank/src/contract.rs +++ b/contracts/red-bank/src/contract.rs @@ -237,6 +237,9 @@ 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_1ToV2_3_2 { haircut, market } => migrations::v2_3_2::migrate(deps, haircut, &market) + MigrateMsg::V2_3_1ToV2_3_3 { + haircut, + market, + } => migrations::v2_3_3::migrate(deps, haircut, &market), } } diff --git a/contracts/red-bank/src/migrations/mod.rs b/contracts/red-bank/src/migrations/mod.rs index 9c182a09..1ad6c0d4 100644 --- a/contracts/red-bank/src/migrations/mod.rs +++ b/contracts/red-bank/src/migrations/mod.rs @@ -1,3 +1,3 @@ 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_2.rs b/contracts/red-bank/src/migrations/v2_3_3.rs similarity index 76% rename from contracts/red-bank/src/migrations/v2_3_2.rs rename to contracts/red-bank/src/migrations/v2_3_3.rs index a3f3b62f..26cb018a 100644 --- a/contracts/red-bank/src/migrations/v2_3_2.rs +++ b/contracts/red-bank/src/migrations/v2_3_3.rs @@ -8,20 +8,15 @@ use crate::{ state::{COLLATERALS, MARKETS}, }; -const FROM_VERSION: &str = "2.3.1"; +const FROM_VERSION: &str = "2.3.2"; -pub fn migrate( - deps: DepsMut, - haircut: Decimal, - denom: &str, -) -> Result { +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)?)?; + 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)?; @@ -30,7 +25,12 @@ pub fn migrate( 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 = 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)); diff --git a/contracts/red-bank/tests/tests/test_migration_v2.rs b/contracts/red-bank/tests/tests/test_migration_v2.rs index 8b72ea84..0736763d 100644 --- a/contracts/red-bank/tests/tests/test_migration_v2.rs +++ b/contracts/red-bank/tests/tests/test_migration_v2.rs @@ -123,14 +123,14 @@ fn v2_3_0_to_v2_3_1_successful_migration() { } #[test] -fn v2_3_1_to_v2_3_2_wrong_contract_name() { +fn v2_3_1_to_v2_3_3_wrong_contract_name() { let mut deps = mock_dependencies(&[]); cw2::set_contract_version(deps.as_mut().storage, "contract_xyz", FROM_VERSION_V2_3_1).unwrap(); let err = migrate( deps.as_mut(), mock_env(), - MigrateMsg::V2_3_1ToV2_3_2 { + MigrateMsg::V2_3_1ToV2_3_3 { haircut: Decimal::percent(10), market: "umars".to_string(), }, @@ -147,14 +147,14 @@ fn v2_3_1_to_v2_3_2_wrong_contract_name() { } #[test] -fn v2_3_1_to_v2_3_2_wrong_contract_version() { +fn v2_3_1_to_v2_3_3_wrong_contract_version() { let mut deps = mock_dependencies(&[]); 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_1ToV2_3_2 { + MigrateMsg::V2_3_1ToV2_3_3 { haircut: Decimal::percent(10), market: "umars".to_string(), }, @@ -171,7 +171,7 @@ fn v2_3_1_to_v2_3_2_wrong_contract_version() { } #[test] -fn v2_3_1_to_v2_3_2_successful_migration() { +fn v2_3_1_to_v2_3_3_successful_migration() { let mut deps = mock_dependencies(&[]); cw2::set_contract_version(deps.as_mut().storage, CONTRACT_NAME, FROM_VERSION_V2_3_1).unwrap(); @@ -183,24 +183,21 @@ fn v2_3_1_to_v2_3_2_successful_migration() { }; MARKETS.save(deps.as_mut().storage, denom, &market).unwrap(); - let user_addr = Addr::unchecked( - "neutron1qdzn3l4kn7gsjna2tfpg3g3mwd6kunx4p50lfya59k02846xas6qslgs3r", - ); + 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(); + 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_1ToV2_3_2 { + MigrateMsg::V2_3_1ToV2_3_3 { haircut, market: denom.to_string(), }, @@ -225,10 +222,7 @@ fn v2_3_1_to_v2_3_2_successful_migration() { 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()); + assert!(COLLATERALS.may_load(deps.as_ref().storage, (&user_id_key, denom)).unwrap().is_none()); let new_contract_version = ContractVersion { contract: CONTRACT_NAME.to_string(), diff --git a/packages/types/src/red_bank/msg.rs b/packages/types/src/red_bank/msg.rs index 03742111..e025b9a6 100644 --- a/packages/types/src/red_bank/msg.rs +++ b/packages/types/src/red_bank/msg.rs @@ -260,8 +260,8 @@ pub enum QueryMsg { pub enum MigrateMsg { V2_2_0ToV2_3_0 {}, V2_3_0ToV2_3_1 {}, - V2_3_1ToV2_3_2 { + V2_3_1ToV2_3_3 { haircut: Decimal, - market: String + market: String, }, } diff --git a/schemas/mars-red-bank/mars-red-bank.json b/schemas/mars-red-bank/mars-red-bank.json index 786226cd..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.1", + "contract_version": "2.3.3", "idl_version": "1.0.0", "instantiate": { "$schema": "http://json-schema.org/draft-07/schema#", From af576da6c2fb3d013b9b2487580f20a592e0bd6d Mon Sep 17 00:00:00 2001 From: mark Date: Wed, 24 Dec 2025 16:27:54 +1300 Subject: [PATCH 3/3] clippy --- contracts/red-bank/src/migrations/v2_3_3.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/contracts/red-bank/src/migrations/v2_3_3.rs b/contracts/red-bank/src/migrations/v2_3_3.rs index 26cb018a..e1e1489c 100644 --- a/contracts/red-bank/src/migrations/v2_3_3.rs +++ b/contracts/red-bank/src/migrations/v2_3_3.rs @@ -14,12 +14,12 @@ pub fn migrate(deps: DepsMut, haircut: Decimal, denom: &str) -> Result