From d7cae351be6bd773d87c84806787b5fe31b03aea Mon Sep 17 00:00:00 2001
From: 0xxgen1 <0xxgen@solend.fi>
Date: Fri, 8 Nov 2024 12:03:27 +0000
Subject: [PATCH] - Fixed assertion over emode ltv thresholds - Refresh after
adding the emode flag - Ability to toggle emode on and off - Simplified
refresh emode - Fixed borrow weight when no emode pair - Do not fail when
getting ltvs - Simplied get_ltvs
---
contracts/suilend/sources/lending_market.move | 11 +-
contracts/suilend/sources/obligation.move | 217 ++++++++----------
contracts/suilend/sources/reserve_config.move | 23 +-
contracts/suilend/tests/obligation_tests.move | 6 +-
4 files changed, 116 insertions(+), 141 deletions(-)
diff --git a/contracts/suilend/sources/lending_market.move b/contracts/suilend/sources/lending_market.move
index a81cac2..f1f877a 100644
--- a/contracts/suilend/sources/lending_market.move
+++ b/contracts/suilend/sources/lending_market.move
@@ -378,10 +378,11 @@ module suilend::lending_market {
coin::from_balance(receive_balance, ctx)
}
- /// Set emode for obligation - T is the deposit coin type
- public fun set_emode
(
+ /// Set and unset emode for obligation - T is the deposit coin type
+ public fun toggle_emode
(
lending_market: &mut LendingMarket
,
obligation_owner_cap: &ObligationOwnerCap
,
+ turn_on: bool,
clock: &Clock
) {
assert!(lending_market.version == CURRENT_VERSION, EIncorrectVersion);
@@ -391,7 +392,11 @@ module suilend::lending_market {
obligation_owner_cap.obligation_id
);
- obligation::set_emode(
+ if (obligation.is_emode() == turn_on) {
+ return;
+ };
+
+ obligation::toggle_emode(
obligation,
&mut lending_market.reserves,
clock,
diff --git a/contracts/suilend/sources/obligation.move b/contracts/suilend/sources/obligation.move
index 7de0321..65f0f36 100644
--- a/contracts/suilend/sources/obligation.move
+++ b/contracts/suilend/sources/obligation.move
@@ -169,18 +169,22 @@ module suilend::obligation {
}
}
- public(package) fun set_emode
(
+ public(package) fun toggle_emode
(
obligation: &mut Obligation
,
reserves: &mut vector>,
clock: &Clock,
) {
assert!(!is_emode(obligation), EEModeAlreadySet);
- assert!(vector::length(&obligation.borrows) <= 1, EEModeNotValidWithCrossMargin);
- assert!(vector::length(&obligation.deposits) <= 1, EEModeNotValidWithCrossMargin);
- refresh(obligation, reserves, clock);
+ if (is_emode(obligation)) {
+ df::remove(&mut obligation.id, EModeFlag {});
+ } else {
+ assert!(vector::length(&obligation.borrows) <= 1, EEModeNotValidWithCrossMargin);
+ assert!(vector::length(&obligation.deposits) <= 1, EEModeNotValidWithCrossMargin);
+ df::add(&mut obligation.id, EModeFlag {}, true);
+ };
- df::add(&mut obligation.id, EModeFlag {}, true);
+ refresh(obligation, reserves, clock);
}
/// update the obligation's borrowed amounts and health values. this is
@@ -197,7 +201,6 @@ module suilend::obligation {
let is_emode = is_emode(obligation);
if (is_emode) {
obligation.refresh_emode(reserves, is_emode, clock)
-
} else {
while (i < vector::length(&obligation.deposits)) {
let deposit = vector::borrow_mut(&mut obligation.deposits, i);
@@ -299,117 +302,90 @@ module suilend::obligation {
is_emode: bool,
clock: &Clock
) {
- let mut i = 0;
- let mut deposited_value_usd = decimal::from(0);
- let mut allowed_borrow_value_usd = decimal::from(0);
- let mut unhealthy_borrow_value_usd = decimal::from(0);
+ // Deposit
- while (i < vector::length(&obligation.deposits)) {
- let deposit = vector::borrow_mut(&mut obligation.deposits, i);
+ if (obligation.deposits.length() == 0) {
+ return
+ };
- let deposit_reserve = vector::borrow_mut(reserves, deposit.reserve_array_index);
+ assert!(vector::length(&obligation.deposits) == 1, 0);
- reserve::compound_interest(deposit_reserve, clock);
- reserve::assert_price_is_fresh(deposit_reserve, clock);
+ let deposit = obligation.deposits.borrow_mut(0);
+ let deposit_reserve = vector::borrow_mut(reserves, deposit.reserve_array_index);
- let market_value = reserve::ctoken_market_value(
- deposit_reserve,
- deposit.deposited_ctoken_amount
- );
- let market_value_lower_bound = reserve::ctoken_market_value_lower_bound(
- deposit_reserve,
- deposit.deposited_ctoken_amount
- );
-
- deposit.market_value = market_value;
- deposited_value_usd = add(deposited_value_usd, market_value);
+ reserve::compound_interest(deposit_reserve, clock);
+ reserve::assert_price_is_fresh(deposit_reserve, clock);
- let (open_ltv, close_ltv) = get_ltvs(
- obligation,
- deposit_reserve,
- is_emode,
- );
+ let market_value = reserve::ctoken_market_value(
+ deposit_reserve,
+ deposit.deposited_ctoken_amount
+ );
+ let market_value_lower_bound = reserve::ctoken_market_value_lower_bound(
+ deposit_reserve,
+ deposit.deposited_ctoken_amount
+ );
- allowed_borrow_value_usd = add(
- allowed_borrow_value_usd,
- mul(
- market_value_lower_bound,
- open_ltv,
- ),
- );
+ deposit.market_value = market_value;
+ obligation.deposited_value_usd = market_value;
- unhealthy_borrow_value_usd = add(
- unhealthy_borrow_value_usd,
- mul(
- market_value,
- close_ltv,
- ),
- );
+ let (open_ltv, close_ltv, is_emode_ltvs) = get_ltvs(
+ obligation,
+ deposit_reserve,
+ is_emode,
+ );
- i = i + 1;
- };
+ obligation.allowed_borrow_value_usd = mul(
+ market_value_lower_bound,
+ open_ltv,
+ );
- obligation.deposited_value_usd = deposited_value_usd;
- obligation.allowed_borrow_value_usd = allowed_borrow_value_usd;
- obligation.unhealthy_borrow_value_usd = unhealthy_borrow_value_usd;
+ obligation.unhealthy_borrow_value_usd = mul(
+ market_value,
+ close_ltv,
+ );
- let mut i = 0;
- let mut unweighted_borrowed_value_usd = decimal::from(0);
- let mut weighted_borrowed_value_usd = decimal::from(0);
- let mut weighted_borrowed_value_upper_bound_usd = decimal::from(0);
- let mut borrowing_isolated_asset = false;
+ // Borrow
- while (i < vector::length(&obligation.borrows)) {
- let borrow = vector::borrow_mut(&mut obligation.borrows, i);
+ if (obligation.borrows.length() == 0) {
+ return
+ };
- let borrow_reserve = vector::borrow_mut(reserves, borrow.reserve_array_index);
- reserve::compound_interest(borrow_reserve, clock);
- reserve::assert_price_is_fresh(borrow_reserve, clock);
+ assert!(vector::length(&obligation.borrows) == 1, 0);
- compound_debt(borrow, borrow_reserve);
+ let borrow = obligation.borrows.borrow_mut(0);
- let market_value = reserve::market_value(borrow_reserve, borrow.borrowed_amount);
- let market_value_upper_bound = reserve::market_value_upper_bound(
- borrow_reserve,
- borrow.borrowed_amount
- );
+ let borrow_reserve = vector::borrow_mut(reserves, borrow.reserve_array_index);
+ reserve::compound_interest(borrow_reserve, clock);
+ reserve::assert_price_is_fresh(borrow_reserve, clock);
- borrow.market_value = market_value;
- unweighted_borrowed_value_usd = add(unweighted_borrowed_value_usd, market_value);
+ compound_debt(borrow, borrow_reserve);
- let borrow_weight = if (is_emode) {
- decimal::from(1)
- } else {
- borrow_weight(config(borrow_reserve))
- };
-
- weighted_borrowed_value_usd = add(
- weighted_borrowed_value_usd,
- mul(
- market_value,
- borrow_weight
- )
- );
- weighted_borrowed_value_upper_bound_usd = add(
- weighted_borrowed_value_upper_bound_usd,
- mul(
- market_value_upper_bound,
- borrow_weight
- )
- );
+ let market_value = reserve::market_value(borrow_reserve, borrow.borrowed_amount);
+ let market_value_upper_bound = reserve::market_value_upper_bound(
+ borrow_reserve,
+ borrow.borrowed_amount
+ );
- if (isolated(config(borrow_reserve))) {
- borrowing_isolated_asset = true;
- };
+ borrow.market_value = market_value;
+ obligation.unweighted_borrowed_value_usd = market_value;
- i = i + 1;
+ let borrow_weight = if (is_emode_ltvs) {
+ decimal::from(1)
+ } else {
+ borrow_weight(config(borrow_reserve))
};
- obligation.unweighted_borrowed_value_usd = unweighted_borrowed_value_usd;
- obligation.weighted_borrowed_value_usd = weighted_borrowed_value_usd;
- obligation.weighted_borrowed_value_upper_bound_usd = weighted_borrowed_value_upper_bound_usd;
+ obligation.weighted_borrowed_value_usd = mul(
+ market_value,
+ borrow_weight
+ );
- obligation.borrowing_isolated_asset = borrowing_isolated_asset;
+ obligation.weighted_borrowed_value_upper_bound_usd = mul(
+ market_value_upper_bound,
+ borrow_weight
+ );
+
+ obligation.borrowing_isolated_asset = isolated(config(borrow_reserve));
}
/// Process a deposit action
@@ -1375,41 +1351,34 @@ module suilend::obligation {
obligation: &Obligation,
deposit_reserve: &Reserve
,
is_emode: bool,
- ): (Decimal, Decimal) {
- if (is_emode) {
- // check_normal_ltv_against_emode_ltvs
- let borrow_reserve_index = get_single_borrow_array_reserve_if_any(obligation);
+ ): (Decimal, Decimal, bool) {
+ if (!is_emode) {
+ // When obligation normal mode
+ return (open_ltv(config(deposit_reserve)), close_ltv(config(deposit_reserve)), false)
+ };
- assert!(
- reserve_config::has_emode_config(config(deposit_reserve)),
- ENoEmodeConfigForGivenDepositReserve,
- );
+ let borrow_reserve_index = get_single_borrow_array_reserve_if_any(obligation);
+ if (borrow_reserve_index.is_none()) {
+ // When obligation is emode but there is no borrows
+ return (open_ltv(config(deposit_reserve)), close_ltv(config(deposit_reserve)), false)
+ };
- if (option::is_none(&borrow_reserve_index)) {
- // When obligation is emode but there is no borrows
- (open_ltv(config(deposit_reserve)), close_ltv(config(deposit_reserve)))
- } else {
- // When obligation is emode and there is a borrow
- let emode_entry = reserve_config::try_get_emode_entry(
- config(deposit_reserve),
- option::borrow(&borrow_reserve_index)
- );
+ // When obligation is emode and there is a borrow
+ let emode_entry = reserve_config::try_get_emode_entry(
+ config(deposit_reserve),
+ option::borrow(&borrow_reserve_index)
+ );
- if (option::is_some(&emode_entry)) {
- // When obligation is emode and there is a borrow AND there is a matching emode config
- (open_ltv_emode(option::borrow(&emode_entry)), close_ltv_emode(option::borrow(&emode_entry)))
- } else {
- // When obligation is emode and there is a borrow BUT there is no matching emode config
- (open_ltv(config(deposit_reserve)), close_ltv(config(deposit_reserve)))
- }
- }
+ if (option::is_some(&emode_entry)) {
+ // When obligation is emode and there is a borrow AND there is a matching emode config
+ (open_ltv_emode(option::borrow(&emode_entry)), close_ltv_emode(option::borrow(&emode_entry)), true)
} else {
- // When obligation normal mode
- (open_ltv(config(deposit_reserve)), close_ltv(config(deposit_reserve)))
+ // When obligation is emode and there is a borrow BUT there is no matching emode config
+ (open_ltv(config(deposit_reserve)), close_ltv(config(deposit_reserve)), false)
}
}
- fun is_emode
(obligation: &Obligation
): bool {
+ public(package) fun is_emode
(obligation: &Obligation
): bool {
df::exists_(&obligation.id, EModeFlag {})
}
diff --git a/contracts/suilend/sources/reserve_config.move b/contracts/suilend/sources/reserve_config.move
index 189e462..5cb39ff 100644
--- a/contracts/suilend/sources/reserve_config.move
+++ b/contracts/suilend/sources/reserve_config.move
@@ -247,8 +247,8 @@ module suilend::reserve_config {
while (vector::length(&keys) > 0) {
let emode = vec_map::get(emode_ltvs, &vector::pop_back(&mut keys));
- assert!(config.open_ltv_pct < emode.open_ltv_pct, ENormalOpenLtvBetterThanEModeLtvs);
- assert!(config.close_ltv_pct < emode.close_ltv_pct, ENormalCloseLtvBetterThanEModeLtvs);
+ assert!(config.open_ltv_pct <= emode.open_ltv_pct, ENormalOpenLtvBetterThanEModeLtvs);
+ assert!(config.close_ltv_pct <= emode.close_ltv_pct, ENormalCloseLtvBetterThanEModeLtvs);
};
};
}
@@ -575,15 +575,12 @@ module suilend::reserve_config {
reserve_config: &ReserveConfig,
reserve_array_index: &u64,
): bool {
- let emode_config = get_emode_config(reserve_config);
- vec_map::contains(emode_config, reserve_array_index)
- }
+ if (!bag::contains(&reserve_config.additional_fields, EModeKey {})) {
+ return false
+ };
- public(package) fun get_emode_config(
- reserve_config: &ReserveConfig,
- ): &VecMap {
- assert!(bag::contains(&reserve_config.additional_fields, EModeKey {}), ENoEModeConfigForDepositReserve);
- bag::borrow(&reserve_config.additional_fields, EModeKey {})
+ let emode_config: &VecMap = bag::borrow(&reserve_config.additional_fields, EModeKey {});
+ vec_map::contains(emode_config, reserve_array_index)
}
public(package) fun open_ltv_emode(
@@ -602,7 +599,11 @@ module suilend::reserve_config {
reserve_config: &ReserveConfig,
reserve_array_index: &u64,
): Option {
- let emode_config = get_emode_config(reserve_config);
+ if (!bag::contains(&reserve_config.additional_fields, EModeKey {})) {
+ return option::none()
+ };
+
+ let emode_config = bag::borrow(&reserve_config.additional_fields, EModeKey {});
let has_pair = vec_map::contains(emode_config, reserve_array_index);
if (has_pair) {
diff --git a/contracts/suilend/tests/obligation_tests.move b/contracts/suilend/tests/obligation_tests.move
index 9922d2a..8a2d5df 100644
--- a/contracts/suilend/tests/obligation_tests.move
+++ b/contracts/suilend/tests/obligation_tests.move
@@ -2306,8 +2306,8 @@ module suilend::obligation_tests {
let new_config = builder.build(test_scenario::ctx(&mut scenario));
sui_reserve.update_reserve_config(new_config);
- obligation.set_emode(&mut reserves, &clock);
- obligation.set_emode(&mut reserves, &clock);
+ obligation.toggle_emode(&mut reserves, &clock);
+ obligation.toggle_emode(&mut reserves, &clock);
sui::test_utils::destroy(lending_market_id);
sui::test_utils::destroy(reserves);
@@ -2360,7 +2360,7 @@ module suilend::obligation_tests {
borrow(&mut obligation, get_reserve_mut(&mut reserves), &clock, 12_500_000);
borrow(&mut obligation, get_reserve_mut(&mut reserves), &clock, 12_500_000);
- obligation.set_emode(&mut reserves, &clock);
+ obligation.toggle_emode(&mut reserves, &clock);
refresh(&mut obligation, &mut reserves, &clock);