Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 11 additions & 1 deletion contracts/red-bank/src/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,9 +59,19 @@ pub fn execute(
amount,
recipient,
account_id,
liquidation_related,
} => {
cw_utils::nonpayable(&info)?;
withdraw::withdraw(deps, env, info, denom, amount, recipient, account_id)
withdraw::withdraw(
deps,
env,
info,
denom,
amount,
recipient,
account_id,
liquidation_related.unwrap_or(false),
)
}
ExecuteMsg::Borrow {
denom,
Expand Down
12 changes: 10 additions & 2 deletions contracts/red-bank/src/health.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,17 @@ pub fn assert_below_liq_threshold_after_withdraw(
params_addr: &Addr,
denom: &str,
withdraw_amount: Uint128,
is_liquidation: bool,
) -> Result<bool, ContractError> {
let mut positions =
get_user_positions_map(deps, env, user_addr, account_id, oracle_addr, params_addr, false)?;
let mut positions = get_user_positions_map(
deps,
env,
user_addr,
account_id,
oracle_addr,
params_addr,
is_liquidation,
)?;
// Update position to compute health factor after withdraw
match positions.get_mut(denom) {
Some(p) => {
Expand Down
7 changes: 7 additions & 0 deletions contracts/red-bank/src/withdraw.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ pub fn withdraw(
amount: Option<Uint128>,
recipient: Option<String>,
account_id: Option<String>,
liquidation_related: bool,
) -> Result<Response, ContractError> {
let withdrawer = User(&info.sender);
let acc_id = account_id.clone().unwrap_or("".to_string());
Expand Down Expand Up @@ -63,12 +64,17 @@ pub fn withdraw(
MarsAddressType::Incentives,
MarsAddressType::RewardsCollector,
MarsAddressType::Params,
MarsAddressType::CreditManager,
],
)?;
let rewards_collector_addr = &addresses[&MarsAddressType::RewardsCollector];
let incentives_addr = &addresses[&MarsAddressType::Incentives];
let oracle_addr = &addresses[&MarsAddressType::Oracle];
let params_addr = &addresses[&MarsAddressType::Params];
let credit_manager_addr = &addresses[&MarsAddressType::CreditManager];

// if withdraw is part of the liquidation in credit manager we need to use correct pricing for the assets
let liquidation_related = info.sender == credit_manager_addr && liquidation_related;

// if asset is used as collateral and user is borrowing we need to validate health factor after withdraw,
// otherwise no reasons to block the withdraw
Expand All @@ -83,6 +89,7 @@ pub fn withdraw(
params_addr,
&denom,
withdraw_amount,
liquidation_related,
)?
{
return Err(ContractError::InvalidHealthFactorAfterWithdraw {});
Expand Down
65 changes: 62 additions & 3 deletions contracts/red-bank/tests/tests/helpers/mod.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#![allow(dead_code)]

use std::collections::HashMap;
use std::{collections::HashMap, fmt::Display, str::FromStr};

use anyhow::Result as AnyResult;
use cosmwasm_schema::serde;
Expand All @@ -21,8 +21,9 @@ use mars_red_bank::{
state::{COLLATERALS, DEBTS, MARKETS},
};
use mars_red_bank_types::red_bank::{
Collateral, CreateOrUpdateConfig, Debt, InstantiateMsg, Market, QueryMsg,
UserCollateralResponse, UserDebtResponse, UserHealthStatus, UserPositionResponse,
Collateral, CreateOrUpdateConfig, Debt, InitOrUpdateAssetParams, InstantiateMsg,
InterestRateModel, Market, QueryMsg, UserCollateralResponse, UserDebtResponse,
UserHealthStatus, UserPositionResponse,
};
use mars_testing::{mock_dependencies, mock_env, mock_info, MarsMockQuerier, MockEnvParams};

Expand Down Expand Up @@ -372,3 +373,61 @@ pub fn assert_err(res: AnyResult<AppResponse>, err: ContractError) {
}
}
}

pub fn assert_err_with_str(res: AnyResult<AppResponse>, expected: impl Display) {
match res {
Ok(_) => panic!("Result was not an error"),
Err(generic_err) => {
let contract_err: ContractError = generic_err.downcast().unwrap();
let msg = contract_err.to_string();
println!("error: {}", msg); // print error for debugging
assert!(msg.contains(&format!("{expected}")))
}
}
}

pub fn osmo_asset_params() -> (InitOrUpdateAssetParams, AssetParams) {
default_asset_params_with("uosmo", Decimal::percent(70), Decimal::percent(78))
}

pub fn usdc_asset_params() -> (InitOrUpdateAssetParams, AssetParams) {
default_asset_params_with("uusdc", Decimal::percent(90), Decimal::percent(96))
}

pub fn default_asset_params_with(
denom: &str,
max_loan_to_value: Decimal,
liquidation_threshold: Decimal,
) -> (InitOrUpdateAssetParams, AssetParams) {
let market_params = InitOrUpdateAssetParams {
reserve_factor: Some(Decimal::percent(20)),
interest_rate_model: Some(InterestRateModel {
optimal_utilization_rate: Decimal::percent(10),
base: Decimal::percent(30),
slope_1: Decimal::percent(25),
slope_2: Decimal::percent(30),
}),
};
let asset_params = AssetParams {
denom: denom.to_string(),
credit_manager: CmSettings {
whitelisted: false,
hls: None,
},
red_bank: RedBankSettings {
deposit_enabled: true,
borrow_enabled: true,
},
max_loan_to_value,
liquidation_threshold,
liquidation_bonus: LiquidationBonus {
starting_lb: Decimal::percent(1),
slope: Decimal::from_str("2.0").unwrap(),
min_lb: Decimal::percent(2),
max_lb: Decimal::percent(10),
},
protocol_liquidation_fee: Decimal::percent(2),
deposit_cap: Uint128::MAX,
};
(market_params, asset_params)
}
53 changes: 3 additions & 50 deletions contracts/red-bank/tests/tests/test_credit_accounts.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
use std::str::FromStr;

use cosmwasm_std::{coin, Addr, Decimal, Uint128};
use mars_params::types::asset::{AssetParams, CmSettings, LiquidationBonus, RedBankSettings};
use mars_red_bank::error::ContractError;
use mars_red_bank_types::red_bank::{InitOrUpdateAssetParams, InterestRateModel, UserHealthStatus};
use mars_red_bank_types::red_bank::UserHealthStatus;
use mars_testing::integration::mock_env::MockEnvBuilder;

use super::helpers::assert_err;
use crate::tests::helpers::{osmo_asset_params, usdc_asset_params};

#[test]
fn deposit_and_withdraw_for_credit_account_works() {
Expand Down Expand Up @@ -109,6 +107,7 @@ fn deposit_and_withdraw_for_credit_account_works() {
"uosmo",
None,
Some(account_id.clone()),
None,
)
.unwrap();

Expand All @@ -125,49 +124,3 @@ fn deposit_and_withdraw_for_credit_account_works() {
assert!(cm_position.total_collateralized_debt.is_zero());
assert_eq!(cm_position.health_status, UserHealthStatus::NotBorrowing);
}

fn osmo_asset_params() -> (InitOrUpdateAssetParams, AssetParams) {
default_asset_params_with("uosmo", Decimal::percent(70), Decimal::percent(78))
}

fn usdc_asset_params() -> (InitOrUpdateAssetParams, AssetParams) {
default_asset_params_with("uusdc", Decimal::percent(90), Decimal::percent(96))
}

fn default_asset_params_with(
denom: &str,
max_loan_to_value: Decimal,
liquidation_threshold: Decimal,
) -> (InitOrUpdateAssetParams, AssetParams) {
let market_params = InitOrUpdateAssetParams {
reserve_factor: Some(Decimal::percent(20)),
interest_rate_model: Some(InterestRateModel {
optimal_utilization_rate: Decimal::percent(10),
base: Decimal::percent(30),
slope_1: Decimal::percent(25),
slope_2: Decimal::percent(30),
}),
};
let asset_params = AssetParams {
denom: denom.to_string(),
credit_manager: CmSettings {
whitelisted: false,
hls: None,
},
red_bank: RedBankSettings {
deposit_enabled: true,
borrow_enabled: true,
},
max_loan_to_value,
liquidation_threshold,
liquidation_bonus: LiquidationBonus {
starting_lb: Decimal::percent(1),
slope: Decimal::from_str("2.0").unwrap(),
min_lb: Decimal::percent(2),
max_lb: Decimal::percent(10),
},
protocol_liquidation_fee: Decimal::percent(2),
deposit_cap: Uint128::MAX,
};
(market_params, asset_params)
}
1 change: 1 addition & 0 deletions contracts/red-bank/tests/tests/test_payment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ fn rejecting_unexpected_payments() {
amount: None,
recipient: None,
account_id: None,
liquidation_related: None,
},
)
.unwrap_err();
Expand Down
Loading