Skip to content

Commit

Permalink
Fix inflated collateral (#354)
Browse files Browse the repository at this point in the history
* Inflated collateral issue.

* Fix inflated collateral.

* Bound current utilization ratio to 100% max.

* Add check for zero debt shares just in case.

* Add migration.

* Return error if utilization rate > 100%.

* Revert "Return error if utilization rate > 100%."

This reverts commit 835efaf.

* Update migration to v2.0.3.

* Update schema.
  • Loading branch information
piobab authored Feb 21, 2024
1 parent 28edbfb commit bb5da4a
Show file tree
Hide file tree
Showing 22 changed files with 305 additions and 34 deletions.
6 changes: 3 additions & 3 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion contracts/credit-manager/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "mars-credit-manager"
version = "2.0.2"
version = "2.0.3"
authors = { workspace = true }
license = { workspace = true }
edition = { workspace = true }
Expand Down
5 changes: 5 additions & 0 deletions contracts/credit-manager/src/borrow.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,11 @@ pub fn borrow(mut deps: DepsMut, account_id: &str, coin: Coin) -> ContractResult
.checked_multiply_ratio(coin.amount, total_debt_amount)?
};

// It shouldn't happen but just in case
if debt_shares_to_add.is_zero() {
return Err(ContractError::ZeroDebtShares);
}

TOTAL_DEBT_SHARES.update(deps.storage, &coin.denom, |shares| {
shares
.unwrap_or_else(Uint128::zero)
Expand Down
2 changes: 1 addition & 1 deletion contracts/credit-manager/src/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,6 @@ pub fn query(deps: Deps, env: Env, msg: QueryMsg) -> ContractResult<Binary> {
pub fn migrate(deps: DepsMut, env: Env, msg: MigrateMsg) -> ContractResult<Response> {
match msg {
MigrateMsg::V1_0_0ToV2_0_0(updates) => migrations::v2_0_0::migrate(deps, env, updates),
MigrateMsg::V2_0_1ToV2_0_2 {} => migrations::v2_0_2::migrate(deps),
MigrateMsg::V2_0_2ToV2_0_3 {} => migrations::v2_0_3::migrate(deps),
}
}
3 changes: 3 additions & 0 deletions contracts/credit-manager/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -186,4 +186,7 @@ pub enum ContractError {

#[error(transparent)]
Oracle(#[from] OracleError),

#[error("Debt cannot be represented by zero debt shares")]
ZeroDebtShares,
}
2 changes: 1 addition & 1 deletion contracts/credit-manager/src/migrations/mod.rs
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
pub mod v2_0_0;
pub mod v2_0_2;
pub mod v2_0_3;
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use crate::{
error::ContractError,
};

const FROM_VERSION: &str = "2.0.1";
const FROM_VERSION: &str = "2.0.2";

pub fn migrate(deps: DepsMut) -> Result<Response, ContractError> {
// make sure we're migrating the correct contract and from the correct version
Expand Down
10 changes: 5 additions & 5 deletions contracts/credit-manager/tests/tests/test_migration_v2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -151,24 +151,24 @@ fn successful_migration() {
}

#[test]
fn successful_migration_to_v2_0_2() {
fn successful_migration_to_v2_0_3() {
let mut deps = mock_dependencies();
cw2::set_contract_version(deps.as_mut().storage, "crates.io:mars-credit-manager", "2.0.1")
cw2::set_contract_version(deps.as_mut().storage, "crates.io:mars-credit-manager", "2.0.2")
.unwrap();

let res = migrate(deps.as_mut(), mock_env(), MigrateMsg::V2_0_1ToV2_0_2 {}).unwrap();
let res = migrate(deps.as_mut(), mock_env(), MigrateMsg::V2_0_2ToV2_0_3 {}).unwrap();

assert_eq!(res.messages, vec![]);
assert_eq!(res.events, vec![] as Vec<Event>);
assert!(res.data.is_none());
assert_eq!(
res.attributes,
vec![attr("action", "migrate"), attr("from_version", "2.0.1"), attr("to_version", "2.0.2")]
vec![attr("action", "migrate"), attr("from_version", "2.0.2"), attr("to_version", "2.0.3")]
);

let new_contract_version = ContractVersion {
contract: "crates.io:mars-credit-manager".to_string(),
version: "2.0.2".to_string(),
version: "2.0.3".to_string(),
};
assert_eq!(cw2::get_contract_version(deps.as_ref().storage).unwrap(), new_contract_version);
}
2 changes: 1 addition & 1 deletion contracts/red-bank/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[package]
name = "mars-red-bank"
description = "A smart contract that manages asset deposit, borrowing, and liquidations"
version = { workspace = true }
version = "2.0.1"
authors = { workspace = true }
edition = { workspace = true }
license = { workspace = true }
Expand Down
10 changes: 8 additions & 2 deletions contracts/red-bank/src/borrow.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,9 +58,15 @@ pub fn borrow(
&borrow_market,
env.block.time.seconds(),
)?;
let debt_balance_before = get_underlying_debt_amount(
borrow_market.debt_total_scaled,
&borrow_market,
env.block.time.seconds(),
)?;

// Cannot borrow zero amount or more than available collateral
if borrow_amount.is_zero() || borrow_amount > collateral_balance_before {
// Cannot borrow zero amount or more than available liquidity
let available_liquidity = collateral_balance_before.checked_sub(debt_balance_before)?;
if borrow_amount.is_zero() || borrow_amount > available_liquidity {
return Err(ContractError::InvalidBorrowAmount {
denom,
});
Expand Down
13 changes: 7 additions & 6 deletions contracts/red-bank/src/contract.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
use cosmwasm_std::{
entry_point, to_binary, Binary, Deps, DepsMut, Empty, Env, MessageInfo, Response,
};
use mars_types::red_bank::{ExecuteMsg, InstantiateMsg, QueryMsg};
use cosmwasm_std::{entry_point, to_binary, Binary, Deps, DepsMut, Env, MessageInfo, Response};
use mars_types::red_bank::{ExecuteMsg, InstantiateMsg, MigrateMsg, QueryMsg};

use crate::{
asset, borrow, collateral, config, deposit, error::ContractError, instantiate, liquidate,
Expand Down Expand Up @@ -260,6 +258,9 @@ pub fn query(deps: Deps, env: Env, msg: QueryMsg) -> Result<Binary, ContractErro
}

#[cfg_attr(not(feature = "library"), entry_point)]
pub fn migrate(deps: DepsMut, _env: Env, _msg: Empty) -> Result<Response, ContractError> {
migrations::v2_0_0::migrate(deps)
pub fn migrate(deps: DepsMut, _env: Env, msg: MigrateMsg) -> Result<Response, ContractError> {
match msg {
MigrateMsg::V1_0_0ToV2_0_0 {} => migrations::v2_0_0::migrate(deps),
MigrateMsg::V2_0_0ToV2_0_1 {} => migrations::v2_0_1::migrate(deps),
}
}
2 changes: 1 addition & 1 deletion contracts/red-bank/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ pub enum ContractError {
#[error("Cannot have 0 as liquidity index")]
InvalidLiquidityIndex {},

#[error("Borrow amount must be greater than 0 and less or equal available collateral (asset: {denom:?})")]
#[error("Borrow amount must be greater than 0 and less or equal available liquidity (asset: {denom:?})")]
InvalidBorrowAmount {
denom: String,
},
Expand Down
7 changes: 6 additions & 1 deletion contracts/red-bank/src/interest_rates.rs
Original file line number Diff line number Diff line change
Expand Up @@ -111,12 +111,17 @@ pub fn update_interest_rates(
let total_debt =
get_underlying_debt_amount(market.debt_total_scaled, market, current_timestamp)?;

let current_utilization_rate = if !total_collateral.is_zero() {
let mut current_utilization_rate = if !total_collateral.is_zero() {
Decimal::from_ratio(total_debt, total_collateral)
} else {
Decimal::zero()
};

// Limit utilization_rate to 100%.
// With the current code it should hopefully never happen that it gets calculated to more than 100%,
// but better be safe than sorry.
current_utilization_rate = current_utilization_rate.min(Decimal::one());

market.update_interest_rates(current_utilization_rate)?;

Ok(response.add_event(build_interests_updated_event(&market.denom, market)))
Expand Down
1 change: 1 addition & 0 deletions contracts/red-bank/src/migrations/mod.rs
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
pub mod v2_0_0;
pub mod v2_0_1;
21 changes: 21 additions & 0 deletions contracts/red-bank/src/migrations/v2_0_1.rs
Original file line number Diff line number Diff line change
@@ -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.0.0";

pub fn migrate(deps: DepsMut) -> Result<Response, ContractError> {
// 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))
}
2 changes: 1 addition & 1 deletion contracts/red-bank/tests/tests/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ mod test_borrow;
mod test_credit_accounts;
mod test_deposit;
mod test_health;
mod test_liquidate;
mod test_inflated_collateral;
mod test_migration_v2;
mod test_misc;
mod test_payment;
Expand Down
Loading

0 comments on commit bb5da4a

Please sign in to comment.