Skip to content

Commit 3656af7

Browse files
authored
Merge pull request #69 from mars-protocol/public/latest-master
Move latest public master changes
2 parents 165900d + 75c6007 commit 3656af7

File tree

60 files changed

+916
-528
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

60 files changed

+916
-528
lines changed

Cargo.lock

Lines changed: 10 additions & 10 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,8 +62,7 @@ anyhow = "1.0.86"
6262
apollo-cw-asset = { version = "0.1.2", features = ["astroport"] }
6363
apollo-utils = "0.1.2"
6464
astroport = "2.8.0"
65-
# TODO: update with official release once ready
66-
astroport-v5 = { package = "astroport", version = "=5.0.0-rc.1-tokenfactory" }
65+
astroport-v5 = { package = "astroport", version = "5.2.0" }
6766
bech32 = "0.11.0"
6867
cosmwasm-schema = "1.5.5"
6968
cosmwasm-std = "1.5.5"

contracts/credit-manager/src/deposit.rs

Lines changed: 75 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
use std::collections::BTreeSet;
1+
use std::collections::BTreeMap;
22

3-
use cosmwasm_std::{Coin, Coins, Deps, DepsMut, Response};
3+
use cosmwasm_std::{Coin, Coins, Deps, DepsMut, Response, Uint128};
44
use mars_types::params::TotalDepositResponse;
55

66
use crate::{
@@ -48,12 +48,15 @@ fn assert_sent_fund(expected: &Coin, received_coins: &Coins) -> ContractResult<(
4848
/// Given a list of denoms, assert that the total deposited amount of each denom
4949
/// across Red Bank and Rover does not exceed its deposit cap recorded in the
5050
/// params contract.
51-
pub fn assert_deposit_caps(deps: Deps, denoms: BTreeSet<String>) -> ContractResult<Response> {
51+
pub fn assert_deposit_caps(
52+
deps: Deps,
53+
denom_deposits: BTreeMap<String, Option<Uint128>>,
54+
) -> ContractResult<Response> {
5255
let params = PARAMS.load(deps.storage)?;
5356

5457
let mut response = Response::new().add_attribute("action", "callback/assert_deposit_caps");
5558

56-
for denom in denoms {
59+
for (denom, deposited_opt) in denom_deposits {
5760
// Asset is not found (not whitelisted) and it doesn't count towards the cap and Health Factor
5861
let params_opt = params.query_asset_params(&deps.querier, &denom)?;
5962
if params_opt.is_none() {
@@ -66,6 +69,16 @@ pub fn assert_deposit_caps(deps: Deps, denoms: BTreeSet<String>) -> ContractResu
6669
cap,
6770
} = params.query_total_deposit(&deps.querier, &denom)?;
6871

72+
// - If there is None in the map, it means that the deposit cap should be enforced. It is related to the Deposit action.
73+
// - If there is Some in the map, it means that the deposit amount should be compared (value before and after the TX).
74+
// It is related to the SwapExactIn and ProvideLiquidity actions.
75+
if let Some(deposited) = deposited_opt {
76+
// amount is lower than or equal to the previous deposit amount so it is fine
77+
if amount <= deposited {
78+
continue;
79+
}
80+
}
81+
6982
if amount > cap {
7083
return Err(ContractError::AboveAssetDepositCap {
7184
new_value: Coin {
@@ -84,3 +97,61 @@ pub fn assert_deposit_caps(deps: Deps, denoms: BTreeSet<String>) -> ContractResu
8497

8598
Ok(response)
8699
}
100+
101+
/// Update the total deposit amount for the asset in the denom_deposits map
102+
/// The function either resets the deposit amount to None for Deposit actions
103+
/// or updates the deposit amount based on the received coins and existing parameters.
104+
pub fn update_or_reset_denom_deposits(
105+
deps: Deps,
106+
denom_deposits: &mut BTreeMap<String, Option<Uint128>>,
107+
denom: &str,
108+
received_coins: &Coins,
109+
deposit_action: bool,
110+
) -> ContractResult<()> {
111+
// Overwrite the previous amount for Deposit action.
112+
// It means that we don't compare deposits before and after the TX.
113+
//
114+
// Strictly enforce the deposit cap to prevent any increase in total value.
115+
// Even if we use the funds for operations within the account (such as liquidation),
116+
// and withdraw them at the end (resulting in zero net inflow), temporary funds
117+
// could still be used for malicious actions.
118+
if deposit_action {
119+
denom_deposits.insert(denom.to_string(), None);
120+
121+
return Ok(());
122+
}
123+
124+
// Check if the denomination is already in the list.
125+
// This ensures that a Deposit action (which does not have an associated amount)
126+
// is not overwritten by a subsequent Swap or ProvideLiquidity action.
127+
// By confirming the existence of the denomination in the list, we maintain
128+
// the integrity of the original Deposit action.
129+
if denom_deposits.contains_key(denom) {
130+
return Ok(());
131+
}
132+
133+
// Load the params
134+
let params = PARAMS.load(deps.storage)?;
135+
136+
// Asset is not found (not whitelisted) and it doesn't count towards the cap and Health Factor
137+
let params_opt = params.query_asset_params(&deps.querier, denom)?;
138+
if params_opt.is_none() {
139+
return Ok(());
140+
}
141+
142+
// Check total deposit amount for the asset
143+
let total_deposit_amt = params.query_total_deposit(&deps.querier, denom)?.amount;
144+
145+
// Check if the asset was sent in the TX
146+
let received_amt = received_coins.amount_of(denom);
147+
148+
// Total deposit amount is the sum of all deposits for the asset across Red Bank and Rover.
149+
// If the asset was sent in the TX, the Credit Manager balance already includes the deposited amount
150+
// so we need to subtract it from the total deposit amount to see previous state.
151+
let new_total_deposit_amt = total_deposit_amt.checked_sub(received_amt).unwrap_or_default();
152+
153+
// Update the total deposit amount for the asset
154+
denom_deposits.insert(denom.to_string(), Some(new_total_deposit_amt));
155+
156+
Ok(())
157+
}

contracts/credit-manager/src/execute.rs

Lines changed: 36 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1-
use std::collections::BTreeSet;
1+
use std::collections::BTreeMap;
22

33
use cosmwasm_std::{
4-
to_json_binary, Addr, Coins, CosmosMsg, DepsMut, Env, MessageInfo, Response, StdResult, WasmMsg,
4+
to_json_binary, Addr, Coins, CosmosMsg, DepsMut, Env, MessageInfo, Response, StdResult,
5+
Uint128, WasmMsg,
56
};
67
use mars_types::{
78
account_nft::ExecuteMsg as NftExecuteMsg,
@@ -15,7 +16,7 @@ use crate::{
1516
borrow::borrow,
1617
claim_astro_lp_rewards::claim_lp_rewards,
1718
claim_rewards::claim_rewards,
18-
deposit::{assert_deposit_caps, deposit},
19+
deposit::{assert_deposit_caps, deposit, update_or_reset_denom_deposits},
1920
error::{ContractError, ContractResult},
2021
health::{assert_max_ltv, query_health_state},
2122
hls::assert_hls_rules,
@@ -151,13 +152,10 @@ pub fn dispatch_actions(
151152
None
152153
};
153154

154-
// We use a Set to record all denoms whose deposited amount may go up as the
155+
// We use a Map to record all denoms whose deposited amount may go up as the
155156
// result of any action. We invoke the AssertDepositCaps callback in the end
156157
// to make sure that none of the deposit cap is exceeded.
157158
//
158-
// Additionally, we use a BTreeSet (instead of a Vec or HashSet) to ensure
159-
// uniqueness and determininism.
160-
//
161159
// There are a few actions that may result in an asset's deposit amount
162160
// going up:
163161
// - Deposit: we check the deposited denom
@@ -172,14 +170,22 @@ pub fn dispatch_actions(
172170
// Note that Borrow/Lend/Reclaim does not impact total deposit amount,
173171
// because they simply move assets between Red Bank and Rover. We don't
174172
// check these actions.
175-
let mut denoms_for_cap_check = BTreeSet::new();
173+
// If there is None in the map, it means that the deposit cap should be enforced,
174+
// otherwise it should compare deposit amount before and after the TX.
175+
let mut denoms_for_cap_check: BTreeMap<String, Option<Uint128>> = BTreeMap::new();
176176

177177
for action in actions {
178178
match action {
179179
Action::Deposit(coin) => {
180180
response = deposit(&mut deps, response, account_id, &coin, &mut received_coins)?;
181-
// check the deposit cap of the deposited denom
182-
denoms_for_cap_check.insert(coin.denom);
181+
// add the denom to the map to check the deposit cap in the end of the TX
182+
update_or_reset_denom_deposits(
183+
deps.as_ref(),
184+
&mut denoms_for_cap_check,
185+
&coin.denom,
186+
&received_coins,
187+
true,
188+
)?;
183189
}
184190
Action::Withdraw(coin) => callbacks.push(CallbackMsg::Withdraw {
185191
account_id: account_id.to_string(),
@@ -275,18 +281,24 @@ pub fn dispatch_actions(
275281
Action::SwapExactIn {
276282
coin_in,
277283
denom_out,
278-
slippage,
284+
min_receive,
279285
route,
280286
} => {
281287
callbacks.push(CallbackMsg::SwapExactIn {
282288
account_id: account_id.to_string(),
283289
coin_in,
284290
denom_out: denom_out.clone(),
285-
slippage,
291+
min_receive,
286292
route,
287293
});
288-
// check the deposit cap of the swap output denom
289-
denoms_for_cap_check.insert(denom_out);
294+
// add the output denom to the map to check the deposit cap in the end of the TX
295+
update_or_reset_denom_deposits(
296+
deps.as_ref(),
297+
&mut denoms_for_cap_check,
298+
&denom_out,
299+
&received_coins,
300+
false,
301+
)?;
290302
}
291303
Action::ExitVault {
292304
vault,
@@ -324,8 +336,14 @@ pub fn dispatch_actions(
324336
slippage,
325337
});
326338

327-
// check the deposit cap of the LP output denom
328-
denoms_for_cap_check.insert(lp_token_out);
339+
// add the LP output denom to the map to check the deposit cap in the end of the TX
340+
update_or_reset_denom_deposits(
341+
deps.as_ref(),
342+
&mut denoms_for_cap_check,
343+
&lp_token_out,
344+
&received_coins,
345+
false,
346+
)?;
329347
}
330348
Action::WithdrawLiquidity {
331349
lp_token,
@@ -564,9 +582,9 @@ pub fn execute_callback(
564582
account_id,
565583
coin_in,
566584
denom_out,
567-
slippage,
585+
min_receive,
568586
route,
569-
} => swap_exact_in(deps, env, &account_id, &coin_in, &denom_out, slippage, route),
587+
} => swap_exact_in(deps, env, &account_id, &coin_in, &denom_out, min_receive, route),
570588
CallbackMsg::UpdateCoinBalance {
571589
account_id,
572590
previous_balance,

contracts/credit-manager/src/lend.rs

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use cosmwasm_std::{Coin, Deps, DepsMut, Response, Uint128};
22
use mars_types::credit_manager::ActionCoin;
33

44
use crate::{
5-
error::{ContractError, ContractResult},
5+
error::ContractResult,
66
state::{COIN_BALANCES, RED_BANK},
77
utils::{assert_coin_is_whitelisted, decrement_coin_balance},
88
};
@@ -15,29 +15,29 @@ pub fn lend(mut deps: DepsMut, account_id: &str, coin: &ActionCoin) -> ContractR
1515
amount: get_lend_amount(deps.as_ref(), account_id, coin)?,
1616
};
1717

18-
decrement_coin_balance(deps.storage, account_id, &amount_to_lend)?;
18+
// Don't error if there is zero amount to lend - just return an empty response
19+
let mut res = Response::new();
20+
if !amount_to_lend.amount.is_zero() {
21+
decrement_coin_balance(deps.storage, account_id, &amount_to_lend)?;
1922

20-
let red_bank_lend_msg = RED_BANK.load(deps.storage)?.lend_msg(&amount_to_lend, account_id)?;
23+
let red_bank_lend_msg =
24+
RED_BANK.load(deps.storage)?.lend_msg(&amount_to_lend, account_id)?;
2125

22-
Ok(Response::new()
23-
.add_message(red_bank_lend_msg)
26+
res = res.add_message(red_bank_lend_msg);
27+
}
28+
29+
Ok(res
2430
.add_attribute("action", "lend")
2531
.add_attribute("account_id", account_id)
2632
.add_attribute("coin_lent", &amount_to_lend.denom))
2733
}
2834

29-
/// Queries balance to ensure passing EXACT is not too high.
30-
/// Also asserts the amount is greater than zero.
35+
/// Queries balance to ensure passing EXACT is not too high
3136
fn get_lend_amount(deps: Deps, account_id: &str, coin: &ActionCoin) -> ContractResult<Uint128> {
3237
let amount_to_lend = if let Some(amount) = coin.amount.value() {
3338
amount
3439
} else {
3540
COIN_BALANCES.may_load(deps.storage, (account_id, &coin.denom))?.unwrap_or(Uint128::zero())
3641
};
37-
38-
if amount_to_lend.is_zero() {
39-
Err(ContractError::NoAmount)
40-
} else {
41-
Ok(amount_to_lend)
42-
}
42+
Ok(amount_to_lend)
4343
}

0 commit comments

Comments
 (0)