Skip to content

Commit

Permalink
- Fixed assertion over emode ltv thresholds
Browse files Browse the repository at this point in the history
- 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
  • Loading branch information
0xLendlord committed Nov 8, 2024
1 parent 6dad8d5 commit d7cae35
Show file tree
Hide file tree
Showing 4 changed files with 116 additions and 141 deletions.
11 changes: 8 additions & 3 deletions contracts/suilend/sources/lending_market.move
Original file line number Diff line number Diff line change
Expand Up @@ -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<P>(
/// Set and unset emode for obligation - T is the deposit coin type
public fun toggle_emode<P>(
lending_market: &mut LendingMarket<P>,
obligation_owner_cap: &ObligationOwnerCap<P>,
turn_on: bool,
clock: &Clock
) {
assert!(lending_market.version == CURRENT_VERSION, EIncorrectVersion);
Expand All @@ -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,
Expand Down
217 changes: 93 additions & 124 deletions contracts/suilend/sources/obligation.move
Original file line number Diff line number Diff line change
Expand Up @@ -169,18 +169,22 @@ module suilend::obligation {
}
}

public(package) fun set_emode<P>(
public(package) fun toggle_emode<P>(
obligation: &mut Obligation<P>,
reserves: &mut vector<Reserve<P>>,
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<EModeFlag, bool>(&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
Expand All @@ -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);
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -1375,41 +1351,34 @@ module suilend::obligation {
obligation: &Obligation<P>,
deposit_reserve: &Reserve<P>,
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<P>(obligation: &Obligation<P>): bool {
public(package) fun is_emode<P>(obligation: &Obligation<P>): bool {
df::exists_(&obligation.id, EModeFlag {})
}

Expand Down
23 changes: 12 additions & 11 deletions contracts/suilend/sources/reserve_config.move
Original file line number Diff line number Diff line change
Expand Up @@ -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);
};
};
}
Expand Down Expand Up @@ -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<u64, EModeEntry> {
assert!(bag::contains(&reserve_config.additional_fields, EModeKey {}), ENoEModeConfigForDepositReserve);
bag::borrow(&reserve_config.additional_fields, EModeKey {})
let emode_config: &VecMap<u64, EModeEntry> = bag::borrow(&reserve_config.additional_fields, EModeKey {});
vec_map::contains(emode_config, reserve_array_index)
}

public(package) fun open_ltv_emode(
Expand All @@ -602,7 +599,11 @@ module suilend::reserve_config {
reserve_config: &ReserveConfig,
reserve_array_index: &u64,
): Option<EModeEntry> {
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) {
Expand Down
6 changes: 3 additions & 3 deletions contracts/suilend/tests/obligation_tests.move
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -2360,7 +2360,7 @@ module suilend::obligation_tests {
borrow<TEST_MARKET>(&mut obligation, get_reserve_mut<TEST_MARKET, TEST_USDC>(&mut reserves), &clock, 12_500_000);
borrow<TEST_MARKET>(&mut obligation, get_reserve_mut<TEST_MARKET, TEST_USDC>(&mut reserves), &clock, 12_500_000);

obligation.set_emode(&mut reserves, &clock);
obligation.toggle_emode(&mut reserves, &clock);

refresh<TEST_MARKET>(&mut obligation, &mut reserves, &clock);

Expand Down

0 comments on commit d7cae35

Please sign in to comment.