Skip to content

Commit f99f6c0

Browse files
authored
Repay debt with available balance (#348)
* Repay debt with available balance. * Fix scripts job. * Bound to available benefactor balance when repaying debt. * Migrate CM. * Fix broken test result.
1 parent 4db3253 commit f99f6c0

File tree

13 files changed

+183
-10
lines changed

13 files changed

+183
-10
lines changed

.github/workflows/scripts.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ jobs:
2626
uses: davidB/rust-cargo-make@v1
2727

2828
- name: Install stable Rust
29-
run: cd ../ && cargo make install-stable && cd scripts
29+
run: cd ../ && cargo make install-stable-for-scripts && cd scripts
3030

3131
# selecting a toolchain should happen before the plugin, as the cache uses the current rustc version as its cache key
3232
- name: Cache dependencies

Cargo.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Makefile.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,10 @@ rustup component add clippy --toolchain ${RUST_VERSION}
2424
rustup component add llvm-tools-preview --toolchain ${RUST_VERSION}
2525
'''
2626

27+
[tasks.install-stable-for-scripts]
28+
env = { RUST_VERSION = "1.72.0" }
29+
run_task = "install-stable"
30+
2731
[tasks.install-nightly]
2832
script = '''
2933
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --profile minimal --default-toolchain nightly

contracts/credit-manager/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "mars-credit-manager"
3-
version = { workspace = true }
3+
version = "2.0.1"
44
authors = { workspace = true }
55
license = { workspace = true }
66
edition = { workspace = true }

contracts/credit-manager/src/contract.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,5 +128,6 @@ pub fn query(deps: Deps, env: Env, msg: QueryMsg) -> ContractResult<Binary> {
128128
pub fn migrate(deps: DepsMut, env: Env, msg: MigrateMsg) -> ContractResult<Response> {
129129
match msg {
130130
MigrateMsg::V1_0_0ToV2_0_0(updates) => migrations::v2_0_0::migrate(deps, env, updates),
131+
MigrateMsg::V2_0_0ToV2_0_1 {} => migrations::v2_0_1::migrate(deps),
131132
}
132133
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
pub mod v2_0_0;
2+
pub mod v2_0_1;
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
use cosmwasm_std::{DepsMut, Response};
2+
use cw2::{assert_contract_version, set_contract_version};
3+
4+
use crate::{
5+
contract::{CONTRACT_NAME, CONTRACT_VERSION},
6+
error::ContractError,
7+
};
8+
9+
const FROM_VERSION: &str = "2.0.0";
10+
11+
pub fn migrate(deps: DepsMut) -> Result<Response, ContractError> {
12+
// make sure we're migrating the correct contract and from the correct version
13+
assert_contract_version(deps.storage, &format!("crates.io:{CONTRACT_NAME}"), FROM_VERSION)?;
14+
15+
set_contract_version(deps.storage, format!("crates.io:{CONTRACT_NAME}"), CONTRACT_VERSION)?;
16+
17+
Ok(Response::new()
18+
.add_attribute("action", "migrate")
19+
.add_attribute("from_version", FROM_VERSION)
20+
.add_attribute("to_version", CONTRACT_VERSION))
21+
}

contracts/credit-manager/src/repay.rs

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,17 @@ use mars_types::credit_manager::{ActionCoin, CallbackMsg::Repay, ExecuteMsg};
99

1010
use crate::{
1111
error::{ContractError, ContractResult},
12-
state::{DEBT_SHARES, RED_BANK, TOTAL_DEBT_SHARES},
12+
state::{COIN_BALANCES, DEBT_SHARES, RED_BANK, TOTAL_DEBT_SHARES},
1313
utils::{debt_shares_to_amount, decrement_coin_balance, increment_coin_balance},
1414
};
1515

1616
pub fn repay(deps: DepsMut, account_id: &str, coin: &ActionCoin) -> ContractResult<Response> {
1717
// Ensure repayment does not exceed max debt on account
1818
let (debt_amount, debt_shares) =
1919
current_debt_for_denom(deps.as_ref(), account_id, &coin.denom)?;
20-
let amount_to_repay = min(debt_amount, coin.amount.value().unwrap_or(Uint128::MAX));
20+
let coin_balance =
21+
COIN_BALANCES.may_load(deps.storage, (account_id, &coin.denom))?.unwrap_or_default();
22+
let amount_to_repay = min(debt_amount, coin.amount.value().unwrap_or(coin_balance));
2123
let coin_to_repay = Coin {
2224
denom: coin.denom.to_string(),
2325
amount: amount_to_repay,
@@ -86,7 +88,10 @@ pub fn repay_for_recipient(
8688
) -> ContractResult<Response> {
8789
let (debt_amount, _) =
8890
current_debt_for_denom(deps.as_ref(), recipient_account_id, &coin.denom)?;
89-
let amount_to_repay = min(debt_amount, coin.amount.value().unwrap_or(Uint128::MAX));
91+
let coin_balance = COIN_BALANCES
92+
.may_load(deps.storage, (benefactor_account_id, &coin.denom))?
93+
.unwrap_or_default();
94+
let amount_to_repay = min(debt_amount, coin.amount.value().unwrap_or(coin_balance));
9095
let coin_to_repay = &Coin {
9196
denom: coin.denom,
9297
amount: amount_to_repay,

contracts/credit-manager/tests/tests/test_migration_v2.rs

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
use cosmwasm_std::{
2+
attr,
23
testing::{mock_dependencies, mock_env},
3-
Addr, Decimal,
4+
Addr, Decimal, Event,
45
};
5-
use cw2::VersionError;
6+
use cw2::{ContractVersion, VersionError};
67
use mars_credit_manager::{
78
contract::migrate,
89
error::ContractError,
@@ -148,3 +149,26 @@ fn successful_migration() {
148149
let set_red_bank = RED_BANK.load(deps.as_ref().storage).unwrap();
149150
assert_eq!(old_red_bank, set_red_bank.addr.as_str());
150151
}
152+
153+
#[test]
154+
fn successful_migration_to_v2_0_1() {
155+
let mut deps = mock_dependencies();
156+
cw2::set_contract_version(deps.as_mut().storage, "crates.io:mars-credit-manager", "2.0.0")
157+
.unwrap();
158+
159+
let res = migrate(deps.as_mut(), mock_env(), MigrateMsg::V2_0_0ToV2_0_1 {}).unwrap();
160+
161+
assert_eq!(res.messages, vec![]);
162+
assert_eq!(res.events, vec![] as Vec<Event>);
163+
assert!(res.data.is_none());
164+
assert_eq!(
165+
res.attributes,
166+
vec![attr("action", "migrate"), attr("from_version", "2.0.0"), attr("to_version", "2.0.1")]
167+
);
168+
169+
let new_contract_version = ContractVersion {
170+
contract: "crates.io:mars-credit-manager".to_string(),
171+
version: "2.0.1".to_string(),
172+
};
173+
assert_eq!(cw2::get_contract_version(deps.as_ref().storage).unwrap(), new_contract_version);
174+
}

contracts/credit-manager/tests/tests/test_repay.rs

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ use mars_types::{
1010
use super::helpers::{
1111
assert_err, uosmo_info, AccountToFund, CoinInfo, MockEnv, DEFAULT_RED_BANK_COIN_BALANCE,
1212
};
13+
use crate::tests::helpers::{get_coin, get_debt, uatom_info};
1314

1415
#[test]
1516
fn only_token_owner_can_repay() {
@@ -389,3 +390,55 @@ fn amount_none_repays_total_debt() {
389390
let coin = mock.query_balance(&Addr::unchecked(config.red_bank), &coin_info.denom);
390391
assert_eq!(coin.amount, DEFAULT_RED_BANK_COIN_BALANCE.add(Uint128::new(1)));
391392
}
393+
394+
#[test]
395+
fn amount_none_repays_no_more_than_available_asset() {
396+
let uosmo_info = uosmo_info();
397+
let uatom_info = uatom_info();
398+
399+
let user = Addr::unchecked("user");
400+
401+
let mut mock = MockEnv::new()
402+
.set_params(&[uosmo_info.clone(), uatom_info.clone()])
403+
.fund_account(AccountToFund {
404+
addr: user.clone(),
405+
funds: coins(300, uatom_info.denom.clone()),
406+
})
407+
.build()
408+
.unwrap();
409+
410+
let account_id = mock.create_credit_account(&user).unwrap();
411+
412+
mock.update_credit_account(
413+
&account_id,
414+
&user,
415+
vec![
416+
Deposit(uatom_info.to_coin(300)),
417+
Borrow(uosmo_info.to_coin(50)),
418+
Withdraw(uosmo_info.to_action_coin(10)),
419+
Repay {
420+
recipient_account_id: None,
421+
coin: uosmo_info.to_action_coin_full_balance(),
422+
},
423+
],
424+
&[uatom_info.to_coin(300)],
425+
)
426+
.unwrap();
427+
428+
let position = mock.query_positions(&account_id);
429+
assert_eq!(position.debts.len(), 1);
430+
let uosmo_debt = get_debt(&uosmo_info.denom, &position.debts);
431+
// debt: 50 uosmo,
432+
// account balance: 40 uosmo (50 borrowed - 10 withdrawn)
433+
// repaying full balance should repay 40 uosmo
434+
assert_eq!(uosmo_debt.amount, Uint128::new(11)); // 10 + 1 interest
435+
436+
assert_eq!(position.deposits.len(), 1);
437+
assert_eq!(get_coin(&uatom_info.denom, &position.deposits), uatom_info.to_coin(300));
438+
439+
let coin = mock.query_balance(&mock.rover, &uatom_info.denom);
440+
assert_eq!(coin.amount, Uint128::new(300));
441+
442+
let coin = mock.query_balance(&mock.rover, &uosmo_info.denom);
443+
assert_eq!(coin.amount, Uint128::zero());
444+
}

0 commit comments

Comments
 (0)