From a5430d488a76e056466c41e292122dc1184e378e Mon Sep 17 00:00:00 2001 From: 0xxgen1 <0xxgen@solend.fi> Date: Wed, 2 Oct 2024 18:32:25 +0100 Subject: [PATCH] Remove emode cross-margin --- contracts/sources/obligation.move | 327 ++++++-------------------- contracts/sources/reserve_config.move | 34 +-- 2 files changed, 91 insertions(+), 270 deletions(-) diff --git a/contracts/sources/obligation.move b/contracts/sources/obligation.move index c8e1e3c..20548c0 100644 --- a/contracts/sources/obligation.move +++ b/contracts/sources/obligation.move @@ -1,22 +1,22 @@ module suilend::obligation { // === Imports === use std::type_name::{TypeName, Self}; - use std::option::{Self, Option, some, none, is_some, is_none}; use sui::object::{Self, UID, ID}; use sui::balance::{Balance}; use std::vector::{Self}; - use sui::vec_map::{Self}; + use sui::dynamic_field::{Self as df}; use sui::event::{Self}; use sui::tx_context::{TxContext}; use suilend::reserve::{Self, Reserve, config}; use suilend::reserve_config::{ - ReserveConfig, open_ltv, close_ltv, borrow_weight, liquidation_bonus, protocol_liquidation_fee, isolated, + open_ltv_emode, + close_ltv_emode, }; use sui::clock::{Clock}; use suilend::decimal::{Self, Decimal, mul, add, sub, div, gt, lt, min, floor, le, eq, saturating_sub}; @@ -47,6 +47,8 @@ module suilend::obligation { const MAX_DEPOSITS: u64 = 5; const MAX_BORROWS: u64 = 5; + struct EModeFlag has store, copy, drop {} + // === Structs === struct Obligation has key, store { id: UID, @@ -169,13 +171,10 @@ module suilend::obligation { } } - struct BorrowData has copy, drop { - reserve_array_index: u64, - borrow_weighted_value_usd: Decimal, - borrow_weighted_value_upper_bound_usd: Decimal, + fun is_emode

(obligation: &Obligation

): bool { + df::exists_(&obligation.id, EModeFlag {}) } - /// update the obligation's borrowed amounts and health values. this is /// called by the lending market prior to any borrow, withdraw, or liquidate operation. public(friend) fun refresh

( @@ -183,14 +182,76 @@ module suilend::obligation { reserves: &mut vector>, clock: &Clock ) { + let i = 0; + let deposited_value_usd = decimal::from(0); + let allowed_borrow_value_usd = decimal::from(0); + let unhealthy_borrow_value_usd = decimal::from(0); + let is_emode = is_emode(obligation); + + while (i < vector::length(&obligation.deposits)) { + let deposit = vector::borrow_mut(&mut obligation.deposits, i); + + let deposit_reserve = vector::borrow_mut(reserves, deposit.reserve_array_index); + + reserve::compound_interest(deposit_reserve, clock); + reserve::assert_price_is_fresh(deposit_reserve, clock); + + 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); + + let (open_ltv, close_ltv) = if (is_emode) { + // There is only one borrow in such circunstance, hence the indexing to 0 + let borrow_reserve_array_index = vector::borrow(&obligation.borrows, 0).reserve_array_index; + + let emode_data = reserve_config::get_emode_ltvs( + config(deposit_reserve), + &borrow_reserve_array_index + ); + + (open_ltv_emode(emode_data), close_ltv_emode(emode_data)) + } else { + (open_ltv(config(deposit_reserve)), close_ltv(config(deposit_reserve))) + }; + + allowed_borrow_value_usd = add( + allowed_borrow_value_usd, + mul( + market_value_lower_bound, + open_ltv, + ), + ); + + unhealthy_borrow_value_usd = add( + unhealthy_borrow_value_usd, + mul( + market_value, + close_ltv, + ), + ); + + i = i + 1; + }; + + 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; + + let i = 0; let unweighted_borrowed_value_usd = decimal::from(0); let weighted_borrowed_value_usd = decimal::from(0); let weighted_borrowed_value_upper_bound_usd = decimal::from(0); let borrowing_isolated_asset = false; - let borrow_data: vector = vector::empty(); - while (i < vector::length(&obligation.borrows)) { let borrow = vector::borrow_mut(&mut obligation.borrows, i); @@ -229,12 +290,6 @@ module suilend::obligation { borrow_weighted_value_upper_bound_usd, ); - vector::push_back(&mut borrow_data, BorrowData { - reserve_array_index: borrow.reserve_array_index, - borrow_weighted_value_usd, - borrow_weighted_value_upper_bound_usd, - }); - if (isolated(config(borrow_reserve))) { borrowing_isolated_asset = true; }; @@ -247,246 +302,6 @@ module suilend::obligation { obligation.weighted_borrowed_value_upper_bound_usd = weighted_borrowed_value_upper_bound_usd; obligation.borrowing_isolated_asset = borrowing_isolated_asset; - - let i = 0; - let deposited_value_usd = decimal::from(0); - let allowed_borrow_value_usd = decimal::from(0); - let unhealthy_borrow_value_usd = decimal::from(0); - - while (i < vector::length(&obligation.deposits)) { - let deposit = vector::borrow_mut(&mut obligation.deposits, i); - - let deposit_reserve = vector::borrow_mut(reserves, deposit.reserve_array_index); - - reserve::compound_interest(deposit_reserve, clock); - reserve::assert_price_is_fresh(deposit_reserve, clock); - - 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); - - let ( - market_value_lower_bound_in_emode, - open_ltv_emode, - market_value_in_emode, - close_ltv_emode, - ) = compute_emode_deposits_and_ltvs( - market_value, - market_value_lower_bound, - &mut borrow_data, - config(deposit_reserve), - ); - - let allowed_borrow_value_usd_i = compute_borrow_value_with_emode( - market_value_lower_bound, - open_ltv(config(deposit_reserve)), - market_value_lower_bound_in_emode, // borrow_weighted_value_upper_bound_in_emode - open_ltv_emode, - ); - - allowed_borrow_value_usd = add( - allowed_borrow_value_usd, - allowed_borrow_value_usd_i, - ); - - let unhealthy_borrow_value_usd_i = compute_borrow_value_with_emode( - market_value, - close_ltv(config(deposit_reserve)), - market_value_in_emode, // borrow_weighted_value_in_emode - close_ltv_emode, - ); - - unhealthy_borrow_value_usd = add( - unhealthy_borrow_value_usd, - unhealthy_borrow_value_usd_i, - ); - - i = i + 1; - }; - - 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; - } - - fun compute_emode_deposits_and_ltvs( - deposit_value: Decimal, - deposit_value_lower_bound: Decimal, - borrow_data: &mut vector, - config: &ReserveConfig, - ): ( - Option, // deposit_value_lower_bound_in_emode - Option, // open_ltv - Option, // deposit_value_in_emode - Option, // close_ltv - ) - { - if (!reserve_config::has_emode_config(config)) { - return (none(), none(), none(), none()) - }; - - let emode_config = reserve_config::get_emode_config(config); - - let len = vector::length(borrow_data); - - let deposit_value_lower_bound_in_emode = decimal::from(0); - let deposit_value_in_emode = decimal::from(0); - - let open_ltv = decimal::from(0); - let close_ltv = decimal::from(0); - - let residual_deposit_value = deposit_value; - let residual_deposit_value_lower_bound = deposit_value_lower_bound; - - while (len > 0) { - let borrow = vector::borrow_mut(borrow_data, len - 1); - - let (open_ltv_i, close_ltv_i) = reserve_config::get_emode_ltvs( - emode_config, - borrow.reserve_array_index - ); - - // open_ltv and close_ltv options are either both some or none - // so we only need to check one of them - if (is_none(&open_ltv_i)) { - continue - } else { - // === Collect values for emode_allowed - let open_ltv_i = option::destroy_some(open_ltv_i); - let deposit_value_lower_bound_in_emode_before = deposit_value_lower_bound_in_emode; - - update_deposits_in_emode( - &mut deposit_value_lower_bound_in_emode, - &mut residual_deposit_value_lower_bound, - &mut borrow.borrow_weighted_value_upper_bound_usd, - ); - - // Delta - let deposit_value_lower_bound_in_emode_i = sub( - deposit_value_lower_bound_in_emode, - deposit_value_lower_bound_in_emode_before - ); - - open_ltv = add( - open_ltv, - mul(deposit_value_lower_bound_in_emode_i, open_ltv_i) - ); - - - // === Collect values for emode_unhealthy - let close_ltv_i = option::destroy_some(close_ltv_i); - let deposit_value_in_emode_before = deposit_value_in_emode; - - update_deposits_in_emode( - &mut deposit_value_in_emode, - &mut residual_deposit_value, - &mut borrow.borrow_weighted_value_usd, - ); - - // Delta - let deposit_value_in_emode_i = sub(deposit_value_in_emode, deposit_value_in_emode_before); - close_ltv = add( - close_ltv, - mul(deposit_value_in_emode_i, close_ltv_i) - ); - - // Pop element from basket of unweighted borrows if values - // are fully collected - if ( - eq(borrow.borrow_weighted_value_upper_bound_usd, decimal::from(0)) - && eq(borrow.borrow_weighted_value_usd, decimal::from(0)) - ) { - vector::pop_back(borrow_data); - }; - }; - - len = len - 1; - }; - - open_ltv = div( - open_ltv, - deposit_value_lower_bound_in_emode, - ); - - close_ltv = div( - close_ltv, - deposit_value_in_emode, - ); - - ( - some(deposit_value_lower_bound_in_emode), - some(open_ltv), - some(deposit_value_in_emode), - some(close_ltv), - ) - } - - fun update_deposits_in_emode( - emode_deposit_value: &mut Decimal, - residual_deposit_value: &mut Decimal, - borrow_weighted_value: &mut Decimal, - ) { - *emode_deposit_value = add( - *emode_deposit_value, - min(*borrow_weighted_value, *residual_deposit_value) - ); - - let previous_residual_deposit_value_lower_bound = *residual_deposit_value; - - *residual_deposit_value = saturating_sub( - *residual_deposit_value, - *borrow_weighted_value, - ); - - *borrow_weighted_value = saturating_sub( - *borrow_weighted_value, - previous_residual_deposit_value_lower_bound - ); - - } - - fun compute_borrow_value_with_emode( - deposit_value_usd: Decimal, - ltv: Decimal, - deposit_value_usd_in_emode: Option, - ltv_emode: Option, - ): Decimal { - let net_deposit_value_usd = deposit_value_usd; - - let emode_value = if (is_some(&deposit_value_usd_in_emode)) { - let deposit_value_usd_in_emode = option::destroy_some(deposit_value_usd_in_emode); - let ltv_emode = option::destroy_some(ltv_emode); - - net_deposit_value_usd = saturating_sub( - net_deposit_value_usd, deposit_value_usd_in_emode - ); - - mul( - deposit_value_usd_in_emode, - ltv_emode, - ) - - } else { - decimal::from(0) - }; - - let normal_value = mul( - net_deposit_value_usd, - ltv - ); - - add( - emode_value, - normal_value, - ) } /// Process a deposit action diff --git a/contracts/sources/reserve_config.move b/contracts/sources/reserve_config.move index 79e8eca..3d85ee1 100644 --- a/contracts/sources/reserve_config.move +++ b/contracts/sources/reserve_config.move @@ -1,7 +1,6 @@ /// parameters for a Reserve. module suilend::reserve_config { use std::vector::{Self}; - use std::option::{Option, some, none}; use suilend::decimal::{Decimal, Self, add, sub, mul, div, ge, le}; use sui::tx_context::{TxContext}; use sui::bag::{Self, Bag}; @@ -483,22 +482,29 @@ module suilend::reserve_config { ): bool { bag::contains(&reserve_config.additional_fields, EModeKey {}) } + + public(friend) fun open_ltv_emode( + emode_data: &EModeData, + ): Decimal { + decimal::from_percent(emode_data.open_ltv_pct) + } + + public(friend) fun close_ltv_emode( + emode_data: &EModeData, + ): Decimal { + decimal::from_percent(emode_data.open_ltv_pct) + } public(friend) fun get_emode_ltvs( - emode_config: &VecMap, - reserve_array_index: u64, - ): (Option, Option) { - let has_pair = vec_map::contains(emode_config, &reserve_array_index); + reserve_config: &ReserveConfig, + reserve_array_index: &u64, + ): &EModeData { + let emode_config = get_emode_config(reserve_config); + let has_pair = vec_map::contains(emode_config, reserve_array_index); - if (!has_pair) { - (none(), none()) - } else { - let emode_data = vec_map::get(emode_config, &reserve_array_index); - ( - some(decimal::from_percent(emode_data.open_ltv_pct)), - some(decimal::from_percent(emode_data.close_ltv_pct)) - ) - } + assert!(has_pair, 0); + + vec_map::get(emode_config, reserve_array_index) } // === Tests ==