diff --git a/Cargo.lock b/Cargo.lock index e5ce9670..0e1f228c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3045,7 +3045,7 @@ dependencies = [ [[package]] name = "mars-red-bank" -version = "2.3.1" +version = "2.3.2" dependencies = [ "anyhow", "cosmwasm-schema 1.5.7", 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..d846b00c 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 {} => 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 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..3185ae45 --- /dev/null +++ b/contracts/red-bank/src/migrations/v2_3_2.rs @@ -0,0 +1,18 @@ +use cosmwasm_std::{DepsMut, Response}; +use cw2::{assert_contract_version, set_contract_version}; + +use crate::{contract::CONTRACT_NAME, error::ContractError}; + +pub const FROM_VERSION: &str = "2.3.1"; +pub const TO_VERSION: &str = "2.3.2"; + +pub fn migrate(deps: DepsMut) -> Result { + assert_contract_version(deps.storage, &format!("crates.io:{CONTRACT_NAME}"), FROM_VERSION)?; + + set_contract_version(deps.storage, format!("crates.io:{CONTRACT_NAME}"), TO_VERSION)?; + + Ok(Response::new() + .add_attribute("action", "migrate") + .add_attribute("from_version", FROM_VERSION) + .add_attribute("to_version", TO_VERSION)) +} diff --git a/contracts/red-bank/src/withdraw.rs b/contracts/red-bank/src/withdraw.rs index d405c7b2..faeb8b94 100644 --- a/contracts/red-bank/src/withdraw.rs +++ b/contracts/red-bank/src/withdraw.rs @@ -44,7 +44,7 @@ pub fn withdraw( // Query params to verify we can withdraw let asset_params = query_asset_params(&deps.querier, params_addr, &denom)?; - if !asset_params.red_bank.withdraw_enabled { + if !asset_params.red_bank.withdraw_enabled && !liquidation_related { return Err(ContractError::WithdrawNotEnabled { denom, }); diff --git a/contracts/red-bank/tests/tests/test_liquidate.rs b/contracts/red-bank/tests/tests/test_liquidate.rs index a7e3f818..b45f1aca 100644 --- a/contracts/red-bank/tests/tests/test_liquidate.rs +++ b/contracts/red-bank/tests/tests/test_liquidate.rs @@ -195,6 +195,45 @@ fn cannot_liquidate_healthy_position() { assert_err(error_res, ContractError::CannotLiquidateHealthyPosition {}); } +#[test] +fn can_liquidate_when_withdraw_disabled() { + let mut mock_env = MockEnvBuilder::new(None, Addr::unchecked("owner")) + .target_health_factor(Decimal::from_ratio(12u128, 10u128)) + .build(); + + let red_bank = mock_env.red_bank.clone(); + let oracle = mock_env.oracle.clone(); + let params = mock_env.params.clone(); + + let (_funded_amt, _provider, liquidatee, liquidator) = setup_env(&mut mock_env); + + // disable withdraw for the collateral asset + let mut osmo_params = params.query_params(&mut mock_env, "uosmo"); + osmo_params.red_bank.withdraw_enabled = false; + params.init_params(&mut mock_env, osmo_params); + + // withdrawing should be blocked + assert_err( + red_bank.withdraw(&mut mock_env, &liquidatee, "uosmo", Some(Uint128::new(1))), + ContractError::WithdrawNotEnabled { + denom: "uosmo".to_string(), + }, + ); + + // make position liquidatable + oracle.set_price_source_fixed(&mut mock_env, "uusdc", Decimal::from_ratio(68u128, 10u128)); + + let debt_before = red_bank.query_user_debt(&mut mock_env, &liquidatee, "uusdc").amount; + + red_bank + .liquidate(&mut mock_env, &liquidator, &liquidatee, "uosmo", &[coin(120, "uusdc")]) + .unwrap(); + + let debt_after = red_bank.query_user_debt(&mut mock_env, &liquidatee, "uusdc").amount; + + assert!(debt_after < debt_before); +} + #[test] fn max_debt_repayed() { let mut mock_env = MockEnvBuilder::new(None, Addr::unchecked("owner")) diff --git a/contracts/red-bank/tests/tests/test_migration_v2.rs b/contracts/red-bank/tests/tests/test_migration_v2.rs index 334926c8..decc40f7 100644 --- a/contracts/red-bank/tests/tests/test_migration_v2.rs +++ b/contracts/red-bank/tests/tests/test_migration_v2.rs @@ -111,3 +111,57 @@ fn v2_3_0_to_v2_3_1_successful_migration() { }; 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", "2.3.1").unwrap(); + + let err = migrate(deps.as_mut(), mock_env(), MigrateMsg::V2_3_1ToV2_3_2 {}).unwrap_err(); + + assert_eq!( + err, + ContractError::Version(VersionError::WrongContract { + expected: "crates.io:mars-red-bank".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, "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(); + + assert_eq!( + err, + ContractError::Version(VersionError::WrongVersion { + expected: "2.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, "crates.io:mars-red-bank", "2.3.1").unwrap(); + + let res = migrate(deps.as_mut(), mock_env(), MigrateMsg::V2_3_1ToV2_3_2 {}).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")] + ); + + let new_contract_version = ContractVersion { + contract: "crates.io:mars-red-bank".to_string(), + version: "2.3.2".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..7b97c5f4 100644 --- a/packages/types/src/red_bank/msg.rs +++ b/packages/types/src/red_bank/msg.rs @@ -260,4 +260,5 @@ pub enum QueryMsg { pub enum MigrateMsg { V2_2_0ToV2_3_0 {}, V2_3_0ToV2_3_1 {}, + V2_3_1ToV2_3_2 {}, } diff --git a/schemas/mars-red-bank/mars-red-bank.json b/schemas/mars-red-bank/mars-red-bank.json index 786226cd..80455e0f 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.2", "idl_version": "1.0.0", "instantiate": { "$schema": "http://json-schema.org/draft-07/schema#",