From ab6c827aca7b26b305640cf05be4e94b84bb215d Mon Sep 17 00:00:00 2001 From: 0xxgen1 <0xxgen@solend.fi> Date: Fri, 8 Nov 2024 17:26:25 +0000 Subject: [PATCH] (wip) - Deposit - Withdraw - Repay - Liquidate --- contracts/suilend/sources/lending_market.move | 4 +- contracts/suilend/sources/obligation.move | 127 +- contracts/suilend/tests/obligation_tests.move | 1259 ++++++++++++++--- 3 files changed, 1211 insertions(+), 179 deletions(-) diff --git a/contracts/suilend/sources/lending_market.move b/contracts/suilend/sources/lending_market.move index 10adaf8..bb74fe2 100644 --- a/contracts/suilend/sources/lending_market.move +++ b/contracts/suilend/sources/lending_market.move @@ -356,7 +356,9 @@ module suilend::lending_market { let (receive_balance, borrow_amount_with_fees) = reserve::borrow_liquidity(reserve, amount); let origination_fee_amount = borrow_amount_with_fees - balance::value(&receive_balance); - obligation::borrow

(obligation, reserve, clock, borrow_amount_with_fees); + obligation::borrow

(obligation, &mut lending_market.reserves, reserve_array_index, clock, borrow_amount_with_fees); + + let reserve = vector::borrow_mut(&mut lending_market.reserves, reserve_array_index); let borrow_value = reserve::market_value_upper_bound(reserve, decimal::from(borrow_amount_with_fees)); rate_limiter::process_qty( diff --git a/contracts/suilend/sources/obligation.move b/contracts/suilend/sources/obligation.move index 758fcd6..2dba176 100644 --- a/contracts/suilend/sources/obligation.move +++ b/contracts/suilend/sources/obligation.move @@ -399,6 +399,13 @@ module suilend::obligation { let borrow_index = find_borrow_index(obligation, reserve); assert!(borrow_index == vector::length(&obligation.borrows), ECannotDepositAndBorrowSameAsset); + let is_emode = is_emode(obligation); + let (open_ltv, close_ltv, _) = get_ltvs( + obligation, + reserve, + is_emode, + ); + let deposit = vector::borrow_mut(&mut obligation.deposits, deposit_index); deposit.deposited_ctoken_amount = deposit.deposited_ctoken_amount + ctoken_amount; @@ -414,14 +421,14 @@ module suilend::obligation { obligation.allowed_borrow_value_usd, mul( reserve::ctoken_market_value_lower_bound(reserve, ctoken_amount), - open_ltv(config(reserve)) + open_ltv, ) ); obligation.unhealthy_borrow_value_usd = add( obligation.unhealthy_borrow_value_usd, mul( deposit_value, - close_ltv(config(reserve)) + close_ltv, ) ); @@ -432,28 +439,46 @@ module suilend::obligation { deposit.deposited_ctoken_amount, clock ); + + if (is_emode) { + assert!(vector::length(&obligation.deposits) == 1, EIsolatedAssetViolation); + let target_reserve_index = emode_deposit_reserve_array_index(obligation); + let deposit = vector::borrow(&obligation.deposits, deposit_index); + assert!(deposit.reserve_array_index == target_reserve_index, EInvalidEModeDeposit); + }; + log_obligation_data(obligation); } /// Process a borrow action. Makes sure that the obligation is healthy after the borrow. public(package) fun borrow

( obligation: &mut Obligation

, - reserve: &mut Reserve

, + reserves: &mut vector>, + borrow_reserve_array_index: u64, clock: &Clock, amount: u64, ) { - let borrow_index = find_or_add_borrow(obligation, reserve, clock); + let borrow_reserve = vector::borrow_mut(reserves, borrow_reserve_array_index); + + let borrow_index = find_or_add_borrow(obligation, borrow_reserve, clock); assert!(vector::length(&obligation.borrows) <= MAX_BORROWS, ETooManyBorrows); - let deposit_index = find_deposit_index(obligation, reserve); + let is_emode = is_emode(obligation); + let borrow_weight = if (is_emode) { + decimal::from(1) + } else { + borrow_weight(config(borrow_reserve)) + }; + + let deposit_index = find_deposit_index(obligation, borrow_reserve); assert!(deposit_index == vector::length(&obligation.deposits), ECannotDepositAndBorrowSameAsset); let borrow = vector::borrow_mut(&mut obligation.borrows, borrow_index); borrow.borrowed_amount = add(borrow.borrowed_amount, decimal::from(amount)); // update health values - let borrow_market_value = reserve::market_value(reserve, decimal::from(amount)); - let borrow_market_value_upper_bound = reserve::market_value_upper_bound(reserve, decimal::from(amount)); + let borrow_market_value = reserve::market_value(borrow_reserve, decimal::from(amount)); + let borrow_market_value_upper_bound = reserve::market_value_upper_bound(borrow_reserve, decimal::from(amount)); borrow.market_value = add(borrow.market_value, borrow_market_value); obligation.unweighted_borrowed_value_usd = add( @@ -462,16 +487,16 @@ module suilend::obligation { ); obligation.weighted_borrowed_value_usd = add( obligation.weighted_borrowed_value_usd, - mul(borrow_market_value, borrow_weight(config(reserve))) + mul(borrow_market_value, borrow_weight) ); obligation.weighted_borrowed_value_upper_bound_usd = add( obligation.weighted_borrowed_value_upper_bound_usd, - mul(borrow_market_value_upper_bound, borrow_weight(config(reserve))) + mul(borrow_market_value_upper_bound, borrow_weight) ); let user_reward_manager = vector::borrow_mut(&mut obligation.user_reward_managers, borrow.user_reward_manager_index); liquidity_mining::change_user_reward_manager_share( - reserve::borrows_pool_reward_manager_mut(reserve), + reserve::borrows_pool_reward_manager_mut(borrow_reserve), user_reward_manager, liability_shares(borrow), clock @@ -479,9 +504,41 @@ module suilend::obligation { assert!(is_healthy(obligation), EObligationIsNotHealthy); - if (isolated(config(reserve)) || obligation.borrowing_isolated_asset) { + if (isolated(config(borrow_reserve)) || obligation.borrowing_isolated_asset) { assert!(vector::length(&obligation.borrows) == 1, EIsolatedAssetViolation); }; + + if (is_emode) { + // assert!(vector::length(&obligation.borrows) == 1, EIsolatedAssetViolation); + let target_reserve_index = emode_borrow_reserve_array_index(obligation); + let borrow = vector::borrow(&obligation.borrows, borrow_index); + assert!(borrow.reserve_array_index == target_reserve_index, EInvalidEModeBorrow); + + let emode_deposit_reserve_array_index = emode_deposit_reserve_array_index(obligation); + + let deposit_reserve = vector::borrow(reserves, emode_deposit_reserve_array_index); + + let (open_ltv, close_ltv, _) = get_ltvs( + obligation, + deposit_reserve, + is_emode, + ); + + let deposit_index = option::destroy_some(get_single_deposit_array_reserve_if_any(obligation)); + + let deposit = vector::borrow_mut(&mut obligation.deposits, deposit_index); + let deposit_value = reserve::ctoken_market_value(deposit_reserve, deposit.deposited_ctoken_amount); + + obligation.allowed_borrow_value_usd = mul( + reserve::ctoken_market_value_lower_bound(deposit_reserve, deposit.deposited_ctoken_amount), + open_ltv, + ); + obligation.unhealthy_borrow_value_usd = mul( + deposit_value, + close_ltv, + ); + }; + log_obligation_data(obligation); } @@ -492,6 +549,13 @@ module suilend::obligation { clock: &Clock, max_repay_amount: Decimal, ): Decimal { + // TODO: FIX BORROW WEIGHT + let borrow_weight = if (is_emode(obligation)) { + decimal::from(1) + } else { + borrow_weight(config(reserve)) + }; + let borrow_index = find_borrow_index(obligation, reserve); assert!(borrow_index < vector::length(&obligation.borrows), EBorrowNotFound); let borrow = vector::borrow_mut(&mut obligation.borrows, borrow_index); @@ -520,11 +584,11 @@ module suilend::obligation { ); obligation.weighted_borrowed_value_usd = saturating_sub( obligation.weighted_borrowed_value_usd, - mul(repay_value, borrow_weight(config(reserve))) + mul(repay_value, borrow_weight) ); obligation.weighted_borrowed_value_upper_bound_usd = saturating_sub( obligation.weighted_borrowed_value_upper_bound_usd, - mul(repay_value_upper_bound, borrow_weight(config(reserve))) + mul(repay_value_upper_bound, borrow_weight) ); } else { @@ -539,11 +603,11 @@ module suilend::obligation { ); obligation.weighted_borrowed_value_usd = add( obligation.weighted_borrowed_value_usd, - mul(additional_borrow_value, borrow_weight(config(reserve))) + mul(additional_borrow_value, borrow_weight) ); obligation.weighted_borrowed_value_upper_bound_usd = add( obligation.weighted_borrowed_value_upper_bound_usd, - mul(additional_borrow_value_upper_bound, borrow_weight(config(reserve))) + mul(additional_borrow_value_upper_bound, borrow_weight) ); }; @@ -1093,6 +1157,14 @@ module suilend::obligation { ) { let deposit_index = find_deposit_index(obligation, reserve); assert!(deposit_index < vector::length(&obligation.deposits), EDepositNotFound); + + let is_emode = is_emode(obligation); + let (open_ltv, close_ltv, _) = get_ltvs( + obligation, + reserve, + is_emode, + ); + let deposit = vector::borrow_mut(&mut obligation.deposits, deposit_index); let withdraw_market_value = reserve::ctoken_market_value(reserve, ctoken_amount); @@ -1107,14 +1179,14 @@ module suilend::obligation { mul( // need to use lower bound to keep calculation consistent reserve::ctoken_market_value_lower_bound(reserve, ctoken_amount), - open_ltv(config(reserve)) + open_ltv ) ); obligation.unhealthy_borrow_value_usd = sub( obligation.unhealthy_borrow_value_usd, mul( withdraw_market_value, - close_ltv(config(reserve)) + close_ltv ) ); @@ -1380,6 +1452,16 @@ module suilend::obligation { df::exists_(&obligation.id, EModeFlag {}) } + fun emode_borrow_reserve_array_index

(obligation: &Obligation

): u64 { + let borrow_reserve_index = get_single_borrow_array_reserve_if_any(obligation); + *option::borrow(&borrow_reserve_index) + } + + fun emode_deposit_reserve_array_index

(obligation: &Obligation

): u64 { + let deposit_reserve_index = get_single_deposit_array_reserve_if_any(obligation); + *option::borrow(&deposit_reserve_index) + } + fun get_single_borrow_array_reserve_if_any

( obligation: &Obligation

, ): Option { @@ -1390,4 +1472,15 @@ module suilend::obligation { option::some(vector::borrow(&obligation.borrows, 0).reserve_array_index) } } + + fun get_single_deposit_array_reserve_if_any

( + obligation: &Obligation

, + ): Option { + let len = vector::length(&obligation.deposits); + if (len != 1) { + option::none() + } else { + option::some(vector::borrow(&obligation.deposits, 0).reserve_array_index) + } + } } \ No newline at end of file diff --git a/contracts/suilend/tests/obligation_tests.move b/contracts/suilend/tests/obligation_tests.move index de0559c..c2724f7 100644 --- a/contracts/suilend/tests/obligation_tests.move +++ b/contracts/suilend/tests/obligation_tests.move @@ -327,17 +327,18 @@ module suilend::obligation_tests { let mut scenario = test_scenario::begin(owner); let clock = clock::create_for_testing(test_scenario::ctx(&mut scenario)); let lending_market_id = object::new(test_scenario::ctx(&mut scenario)); - let mut usdc_reserve = usdc_reserve(&mut scenario); - let mut sui_reserve = sui_reserve(&mut scenario); + let mut reserves = reserves(&mut scenario); let mut obligation = create_obligation(object::uid_to_inner(&lending_market_id), test_scenario::ctx(&mut scenario)); - deposit(&mut obligation, &mut sui_reserve, &clock, 100 * 1_000_000_000); - borrow(&mut obligation, &mut usdc_reserve, &clock, 200 * 1_000_000 + 1); + let sui_reserve = get_reserve_mut(&mut reserves); + + deposit(&mut obligation, sui_reserve, &clock, 100 * 1_000_000_000); + let borrow_idx = get_reserve_array_index(&reserves); + borrow(&mut obligation, &mut reserves, borrow_idx, &clock, 200 * 1_000_000 + 1); sui::test_utils::destroy(lending_market_id); - sui::test_utils::destroy(usdc_reserve); - sui::test_utils::destroy(sui_reserve); + sui::test_utils::destroy(reserves); sui::test_utils::destroy(obligation); sui::test_utils::destroy(clock); test_scenario::end(scenario); @@ -352,18 +353,21 @@ module suilend::obligation_tests { let mut scenario = test_scenario::begin(owner); let clock = clock::create_for_testing(test_scenario::ctx(&mut scenario)); let lending_market_id = object::new(test_scenario::ctx(&mut scenario)); - let mut usdc_reserve = usdc_reserve(&mut scenario); - let mut sui_reserve = sui_reserve(&mut scenario); + + let mut reserves = reserves(&mut scenario); let mut obligation = create_obligation(object::uid_to_inner(&lending_market_id), test_scenario::ctx(&mut scenario)); + let sui_reserve = get_reserve_mut(&mut reserves); - deposit(&mut obligation, &mut sui_reserve, &clock, 100 * 1_000_000_000); - borrow(&mut obligation, &mut usdc_reserve, &clock, 1); - deposit(&mut obligation, &mut usdc_reserve, &clock, 1); + deposit(&mut obligation, sui_reserve, &clock, 100 * 1_000_000_000); + + let borrow_idx = get_reserve_array_index(&reserves); + borrow(&mut obligation, &mut reserves, borrow_idx, &clock, 1); + let usdc_reserve = get_reserve_mut(&mut reserves); + deposit(&mut obligation, usdc_reserve, &clock, 1); sui::test_utils::destroy(lending_market_id); - sui::test_utils::destroy(usdc_reserve); - sui::test_utils::destroy(sui_reserve); + sui::test_utils::destroy(reserves); sui::test_utils::destroy(obligation); sui::test_utils::destroy(clock); test_scenario::end(scenario); @@ -378,15 +382,18 @@ module suilend::obligation_tests { let mut scenario = test_scenario::begin(owner); let clock = clock::create_for_testing(test_scenario::ctx(&mut scenario)); let lending_market_id = object::new(test_scenario::ctx(&mut scenario)); - let mut sui_reserve = sui_reserve(&mut scenario); - + + let mut reserves = reserves(&mut scenario); let mut obligation = create_obligation(object::uid_to_inner(&lending_market_id), test_scenario::ctx(&mut scenario)); - deposit(&mut obligation, &mut sui_reserve, &clock, 100 * 1_000_000_000); - borrow(&mut obligation, &mut sui_reserve, &clock, 1); + let sui_reserve = get_reserve_mut(&mut reserves); + + deposit(&mut obligation, sui_reserve, &clock, 100 * 1_000_000_000); + let borrow_idx = get_reserve_array_index(&reserves); + borrow(&mut obligation, &mut reserves, borrow_idx, &clock, 1); sui::test_utils::destroy(lending_market_id); - sui::test_utils::destroy(sui_reserve); + sui::test_utils::destroy(reserves); sui::test_utils::destroy(obligation); sui::test_utils::destroy(clock); test_scenario::end(scenario); @@ -427,9 +434,11 @@ module suilend::obligation_tests { config ); + let borrow_idx = get_reserve_array_index(&reserves); borrow( &mut obligation, - get_reserve_mut(&mut reserves), + &mut reserves, + borrow_idx, &clock, 1 ); @@ -439,7 +448,8 @@ module suilend::obligation_tests { // this fails borrow( &mut obligation, - get_reserve_mut(&mut reserves), + &mut reserves, + borrow_idx, &clock, 1 ); @@ -487,9 +497,11 @@ module suilend::obligation_tests { config ); + let borrow_idx = get_reserve_array_index(&reserves); borrow( &mut obligation, - get_reserve_mut(&mut reserves), + &mut reserves, + borrow_idx, &clock, 1 ); @@ -497,9 +509,11 @@ module suilend::obligation_tests { refresh(&mut obligation, &mut reserves, &clock); // this fails + let borrow_idx = get_reserve_array_index(&reserves); borrow( &mut obligation, - get_reserve_mut(&mut reserves), + &mut reserves, + borrow_idx, &clock, 1 ); @@ -547,20 +561,24 @@ module suilend::obligation_tests { config ); + let borrow_idx = get_reserve_array_index(&reserves); borrow( &mut obligation, - get_reserve_mut(&mut reserves), - &clock, + &mut reserves, + borrow_idx, + &clock, 1 ); refresh(&mut obligation, &mut reserves, &clock); // this fails + let borrow_idx = get_reserve_array_index(&reserves); borrow( &mut obligation, - get_reserve_mut(&mut reserves), - &clock, + &mut reserves, + borrow_idx, + &clock, 1 ); @@ -619,28 +637,30 @@ module suilend::obligation_tests { let mut scenario = test_scenario::begin(owner); let lending_market_id = object::new(test_scenario::ctx(&mut scenario)); - let mut usdc_reserve = usdc_reserve(&mut scenario); - let mut sui_reserve = sui_reserve(&mut scenario); + let mut reserves = reserves(&mut scenario); let mut obligation = create_obligation(object::uid_to_inner(&lending_market_id), test_scenario::ctx(&mut scenario)); let clock = clock::create_for_testing(test_scenario::ctx(&mut scenario)); + let usdc_reserve = get_reserve_mut(&mut reserves); reserve::update_price_for_testing( - &mut usdc_reserve, + usdc_reserve, &clock, decimal::from(1), decimal::from(2) ); + let sui_reserve = get_reserve_mut(&mut reserves); reserve::update_price_for_testing( - &mut sui_reserve, + sui_reserve, &clock, decimal::from(10), decimal::from(5) ); - deposit(&mut obligation, &mut sui_reserve, &clock, 100 * 1_000_000_000); - borrow(&mut obligation, &mut usdc_reserve, &clock, 12_500_000); - borrow(&mut obligation, &mut usdc_reserve, &clock, 12_500_000); + deposit(&mut obligation, sui_reserve, &clock, 100 * 1_000_000_000); + let borrow_idx = get_reserve_array_index(&reserves); + borrow(&mut obligation, &mut reserves, borrow_idx, &clock, 12_500_000); + borrow(&mut obligation, &mut reserves, borrow_idx, &clock, 12_500_000); assert!(obligation.deposits().length() == 1, 0); @@ -669,8 +689,7 @@ module suilend::obligation_tests { assert!(obligation.weighted_borrowed_value_upper_bound_usd() == decimal::from(100), 4); sui::test_utils::destroy(lending_market_id); - sui::test_utils::destroy(usdc_reserve); - sui::test_utils::destroy(sui_reserve); + sui::test_utils::destroy(reserves); sui::test_utils::destroy(obligation); clock::destroy_for_testing(clock); test_scenario::end(scenario); @@ -685,20 +704,21 @@ module suilend::obligation_tests { let mut scenario = test_scenario::begin(owner); let lending_market_id = object::new(test_scenario::ctx(&mut scenario)); - let mut usdc_reserve = usdc_reserve(&mut scenario); - let mut sui_reserve = sui_reserve(&mut scenario); + let mut reserves = reserves(&mut scenario); let clock = clock::create_for_testing(test_scenario::ctx(&mut scenario)); - let mut obligation = create_obligation(object::uid_to_inner(&lending_market_id), test_scenario::ctx(&mut scenario)); - deposit(&mut obligation, &mut sui_reserve, &clock, 100 * 1_000_000_000); - borrow(&mut obligation, &mut usdc_reserve, &clock, 50 * 1_000_000); + let sui_reserve = get_reserve_mut(&mut reserves); + deposit(&mut obligation, sui_reserve, &clock, 100 * 1_000_000_000); - withdraw(&mut obligation, &mut sui_reserve, &clock, 50 * 1_000_000_000 + 1); + let borrow_idx = get_reserve_array_index(&reserves); + borrow(&mut obligation, &mut reserves, borrow_idx, &clock, 50 * 1_000_000); + + let sui_reserve = get_reserve_mut(&mut reserves); + withdraw(&mut obligation, sui_reserve, &clock, 50 * 1_000_000_000 + 1); sui::test_utils::destroy(lending_market_id); - sui::test_utils::destroy(usdc_reserve); - sui::test_utils::destroy(sui_reserve); + sui::test_utils::destroy(reserves); sui::test_utils::destroy(obligation); sui::test_utils::destroy(clock); test_scenario::end(scenario); @@ -713,20 +733,22 @@ module suilend::obligation_tests { let mut scenario = test_scenario::begin(owner); let lending_market_id = object::new(test_scenario::ctx(&mut scenario)); - let mut usdc_reserve = usdc_reserve(&mut scenario); - let mut sui_reserve = sui_reserve(&mut scenario); + let mut reserves = reserves(&mut scenario); let clock = clock::create_for_testing(test_scenario::ctx(&mut scenario)); let mut obligation = create_obligation(object::uid_to_inner(&lending_market_id), test_scenario::ctx(&mut scenario)); - deposit(&mut obligation, &mut sui_reserve, &clock, 100 * 1_000_000_000); - borrow(&mut obligation, &mut usdc_reserve, &clock, 50 * 1_000_000); + let sui_reserve = get_reserve_mut(&mut reserves); + deposit(&mut obligation, sui_reserve, &clock, 100 * 1_000_000_000); + + let borrow_idx = get_reserve_array_index(&reserves); + borrow(&mut obligation, &mut reserves, borrow_idx, &clock, 50 * 1_000_000); - withdraw(&mut obligation, &mut usdc_reserve, &clock, 1); + let usdc_reserve = get_reserve_mut(&mut reserves); + withdraw(&mut obligation, usdc_reserve, &clock, 1); sui::test_utils::destroy(lending_market_id); - sui::test_utils::destroy(usdc_reserve); - sui::test_utils::destroy(sui_reserve); + sui::test_utils::destroy(reserves); sui::test_utils::destroy(obligation); sui::test_utils::destroy(clock); test_scenario::end(scenario); @@ -740,55 +762,52 @@ module suilend::obligation_tests { let mut scenario = test_scenario::begin(owner); let lending_market_id = object::new(test_scenario::ctx(&mut scenario)); - let mut usdc_reserve = usdc_reserve(&mut scenario); - let mut usdt_reserve = usdt_reserve(&mut scenario); - let mut sui_reserve = sui_reserve(&mut scenario); + let mut reserves = reserves(&mut scenario); let mut obligation = create_obligation(object::uid_to_inner(&lending_market_id), test_scenario::ctx(&mut scenario)); let clock = clock::create_for_testing(test_scenario::ctx(&mut scenario)); reserve::update_price_for_testing( - &mut usdc_reserve, + get_reserve_mut(&mut reserves), &clock, decimal::from(1), decimal::from(2) ); reserve::update_price_for_testing( - &mut usdt_reserve, + get_reserve_mut(&mut reserves), &clock, decimal::from(1), decimal::from(2) ); reserve::update_price_for_testing( - &mut sui_reserve, + get_reserve_mut(&mut reserves), &clock, decimal::from(10), decimal::from(5) ); - deposit(&mut obligation, &mut sui_reserve, &clock, 100 * 1_000_000_000); + deposit(&mut obligation, get_reserve_mut(&mut reserves), &clock, 100 * 1_000_000_000); - let amount = obligation.max_withdraw_amount(&sui_reserve); + let amount = obligation.max_withdraw_amount(get_reserve(&reserves)); assert!(amount == 100 * 1_000_000_000, 0); - borrow(&mut obligation, &mut usdc_reserve, &clock, 20 * 1_000_000); + let borrow_idx = get_reserve_array_index(&reserves); + borrow(&mut obligation, &mut reserves, borrow_idx, &clock, 20 * 1_000_000); // sui open ltv is 0.2 // allowed borrow value = 100 * 0.2 * 5 = 100 // weighted upper bound borrow value = 20 * 2 * 2 = 80 // => max withdraw amount should be 20 - let amount = obligation.max_withdraw_amount(&sui_reserve); + let amount = obligation.max_withdraw_amount(get_reserve(&mut reserves)); assert!(amount == 20 * 1_000_000_000, 0); - deposit(&mut obligation, &mut usdt_reserve, &clock, 100 * 1_000_000); + deposit(&mut obligation, get_reserve_mut(&mut reserves),&clock, 100 * 1_000_000); - let amount = obligation.max_withdraw_amount(&usdt_reserve); + let amount = obligation.max_withdraw_amount(get_reserve(&mut reserves)); assert!(amount == 100 * 1_000_000, 0); sui::test_utils::destroy(lending_market_id); - sui::test_utils::destroy(usdc_reserve); - sui::test_utils::destroy(usdt_reserve); - sui::test_utils::destroy(sui_reserve); + sui::test_utils::destroy(reserves); sui::test_utils::destroy(obligation); clock::destroy_for_testing(clock); test_scenario::end(scenario); @@ -803,28 +822,29 @@ module suilend::obligation_tests { let mut scenario = test_scenario::begin(owner); let lending_market_id = object::new(test_scenario::ctx(&mut scenario)); - let mut usdc_reserve = usdc_reserve(&mut scenario); - let mut sui_reserve = sui_reserve(&mut scenario); + let mut reserves = reserves(&mut scenario); let mut obligation = create_obligation(object::uid_to_inner(&lending_market_id), test_scenario::ctx(&mut scenario)); let clock = clock::create_for_testing(test_scenario::ctx(&mut scenario)); reserve::update_price_for_testing( - &mut usdc_reserve, - &clock, + get_reserve_mut(&mut reserves), + &clock, decimal::from(1), decimal::from(2) ); reserve::update_price_for_testing( - &mut sui_reserve, + get_reserve_mut(&mut reserves), &clock, decimal::from(10), decimal::from(5) ); - deposit(&mut obligation, &mut sui_reserve, &clock, 100 * 1_000_000_000); - borrow(&mut obligation, &mut usdc_reserve, &clock, 20 * 1_000_000); - withdraw(&mut obligation, &mut sui_reserve, &clock, 20 * 1_000_000_000); + deposit(&mut obligation, get_reserve_mut(&mut reserves), &clock, 100 * 1_000_000_000); + + let borrow_idx = get_reserve_array_index(&reserves); + borrow(&mut obligation, &mut reserves, borrow_idx, &clock, 20 * 1_000_000); + withdraw(&mut obligation, get_reserve_mut(&mut reserves), &clock, 20 * 1_000_000_000); assert!(obligation.deposits().length() == 1, 0); @@ -853,8 +873,7 @@ module suilend::obligation_tests { assert!(obligation.weighted_borrowed_value_upper_bound_usd() == decimal::from(80), 4); sui::test_utils::destroy(lending_market_id); - sui::test_utils::destroy(usdc_reserve); - sui::test_utils::destroy(sui_reserve); + sui::test_utils::destroy(reserves); clock::destroy_for_testing(clock); sui::test_utils::destroy(obligation); test_scenario::end(scenario); @@ -871,33 +890,34 @@ module suilend::obligation_tests { let mut clock = clock::create_for_testing(test_scenario::ctx(&mut scenario)); clock::set_for_testing(&mut clock, 0); - let mut usdc_reserve = usdc_reserve(&mut scenario); - let mut sui_reserve = sui_reserve(&mut scenario); + let mut reserves = reserves(&mut scenario); let mut obligation = create_obligation(object::uid_to_inner(&lending_market_id), test_scenario::ctx(&mut scenario)); reserve::update_price_for_testing( - &mut usdc_reserve, + get_reserve_mut(&mut reserves), &clock, decimal::from(1), decimal::from(2) ); reserve::update_price_for_testing( - &mut sui_reserve, + get_reserve_mut(&mut reserves), &clock, decimal::from(10), decimal::from(5) ); - deposit(&mut obligation, &mut sui_reserve, &clock, 100 * 1_000_000_000); - borrow(&mut obligation, &mut usdc_reserve, &clock, 25 * 1_000_000); + deposit(&mut obligation, get_reserve_mut(&mut reserves), &clock, 100 * 1_000_000_000); + + let borrow_idx = get_reserve_array_index(&reserves); + borrow(&mut obligation, &mut reserves, borrow_idx, &clock, 25 * 1_000_000); clock::set_for_testing(&mut clock, 1000); - reserve::compound_interest(&mut usdc_reserve, &clock); + reserve::compound_interest(get_reserve_mut(&mut reserves), &clock); let repay_amount = repay( &mut obligation, - &mut usdc_reserve, + get_reserve_mut(&mut reserves), &clock, decimal::from(25 * 1_000_000) ); @@ -932,8 +952,7 @@ module suilend::obligation_tests { assert!(obligation.weighted_borrowed_value_upper_bound_usd() == decimal::from(1), 4); sui::test_utils::destroy(lending_market_id); - sui::test_utils::destroy(usdc_reserve); - sui::test_utils::destroy(sui_reserve); + sui::test_utils::destroy(reserves); clock::destroy_for_testing(clock); sui::test_utils::destroy(obligation); test_scenario::end(scenario); @@ -950,18 +969,18 @@ module suilend::obligation_tests { let mut clock = clock::create_for_testing(test_scenario::ctx(&mut scenario)); clock::set_for_testing(&mut clock, 0); - let mut usdc_reserve = usdc_reserve(&mut scenario); - let mut sui_reserve = sui_reserve(&mut scenario); + let mut reserves = reserves(&mut scenario); let mut obligation = create_obligation(object::uid_to_inner(&lending_market_id), test_scenario::ctx(&mut scenario)); - deposit(&mut obligation, &mut sui_reserve, &clock, 100 * 1_000_000_000); - borrow(&mut obligation, &mut usdc_reserve, &clock, 100 * 1_000_000); + deposit(&mut obligation, get_reserve_mut(&mut reserves), &clock, 100 * 1_000_000_000); + let borrow_idx = get_reserve_array_index(&reserves); + borrow(&mut obligation, &mut reserves, borrow_idx, &clock, 100 * 1_000_000); clock::set_for_testing(&mut clock, 1000); - reserve::compound_interest(&mut usdc_reserve, &clock); + reserve::compound_interest(get_reserve_mut(&mut reserves), &clock); - let repay_amount = repay(&mut obligation, &mut usdc_reserve, &clock, decimal::from(500_000)); + let repay_amount = repay(&mut obligation, get_reserve_mut(&mut reserves), &clock, decimal::from(500_000)); assert!(repay_amount == decimal::from(500_000), 0); assert!(obligation.deposits().length() == 1, 0); @@ -992,8 +1011,7 @@ module suilend::obligation_tests { assert!(obligation.weighted_borrowed_value_usd() == decimal::from_percent_u64(20_100), 4); sui::test_utils::destroy(lending_market_id); - sui::test_utils::destroy(usdc_reserve); - sui::test_utils::destroy(sui_reserve); + sui::test_utils::destroy(reserves); clock::destroy_for_testing(clock); sui::test_utils::destroy(obligation); test_scenario::end(scenario); @@ -1010,33 +1028,33 @@ module suilend::obligation_tests { let mut clock = clock::create_for_testing(test_scenario::ctx(&mut scenario)); clock::set_for_testing(&mut clock, 0); - let mut usdc_reserve = usdc_reserve(&mut scenario); - let mut sui_reserve = sui_reserve(&mut scenario); + let mut reserves = reserves(&mut scenario); let mut obligation = create_obligation(object::uid_to_inner(&lending_market_id), test_scenario::ctx(&mut scenario)); - deposit(&mut obligation, &mut sui_reserve, &clock, 100 * 1_000_000_000); - borrow(&mut obligation, &mut usdc_reserve, &clock, 100 * 1_000_000); + deposit(&mut obligation, get_reserve_mut(&mut reserves), &clock, 100 * 1_000_000_000); + + let borrow_idx = get_reserve_array_index(&reserves); + borrow(&mut obligation, &mut reserves, borrow_idx, &clock, 100 * 1_000_000); clock::set_for_testing(&mut clock, 1000); reserve::update_price_for_testing( - &mut usdc_reserve, - &clock, + get_reserve_mut(&mut reserves), + &clock, decimal::from(10), decimal::from(10) ); - reserve::compound_interest(&mut usdc_reserve, &clock); + reserve::compound_interest(get_reserve_mut(&mut reserves), &clock); let repay_amount = repay( &mut obligation, - &mut usdc_reserve, + get_reserve_mut(&mut reserves), &clock, decimal::from(100 * 1_000_000) ); sui::test_utils::destroy(lending_market_id); - sui::test_utils::destroy(usdc_reserve); - sui::test_utils::destroy(sui_reserve); + sui::test_utils::destroy(reserves); clock::destroy_for_testing(clock); sui::test_utils::destroy(obligation); test_scenario::end(scenario); @@ -1053,18 +1071,18 @@ module suilend::obligation_tests { let mut clock = clock::create_for_testing(test_scenario::ctx(&mut scenario)); clock::set_for_testing(&mut clock, 0); - let mut usdc_reserve = usdc_reserve(&mut scenario); - let mut sui_reserve = sui_reserve(&mut scenario); + let mut reserves = reserves(&mut scenario); let mut obligation = create_obligation(object::uid_to_inner(&lending_market_id), test_scenario::ctx(&mut scenario)); - deposit(&mut obligation, &mut sui_reserve, &clock, 100 * 1_000_000_000); - borrow(&mut obligation, &mut usdc_reserve, &clock, 100 * 1_000_000); - + deposit(&mut obligation, get_reserve_mut(&mut reserves), &clock, 100 * 1_000_000_000); + + let borrow_idx = get_reserve_array_index(&reserves); + borrow(&mut obligation, &mut reserves, borrow_idx, &clock, 100 * 1_000_000); let repay_amount = repay( &mut obligation, - &mut usdc_reserve, + get_reserve_mut(&mut reserves), &clock, decimal::from(101 * 1_000_000) ); @@ -1079,7 +1097,7 @@ module suilend::obligation_tests { assert!(obligation.borrows().length() == 0, 0); let user_reward_manager_index = obligation.find_user_reward_manager_index( - reserve::borrows_pool_reward_manager_mut(&mut usdc_reserve) + reserve::borrows_pool_reward_manager_mut(get_reserve_mut(&mut reserves)) ); let user_reward_manager = &obligation.user_reward_managers()[user_reward_manager_index]; assert!(liquidity_mining::shares(user_reward_manager) == 0, 0); @@ -1091,8 +1109,7 @@ module suilend::obligation_tests { assert!(obligation.weighted_borrowed_value_usd() == decimal::from_percent_u64(0), 4); sui::test_utils::destroy(lending_market_id); - sui::test_utils::destroy(usdc_reserve); - sui::test_utils::destroy(sui_reserve); + sui::test_utils::destroy(reserves); clock::destroy_for_testing(clock); sui::test_utils::destroy(obligation); test_scenario::end(scenario); @@ -1103,6 +1120,7 @@ module suilend::obligation_tests { public fun test_refresh_fail_deposit_price_stale() { use sui::test_scenario::{Self}; use sui::clock::{Self}; + use sui::test_utils::{Self}; let owner = @0x26; let mut scenario = test_scenario::begin(owner); @@ -1128,7 +1146,7 @@ module suilend::obligation_tests { &clock ); - sui::test_utils::destroy(reserves); + test_utils::destroy(reserves); sui::test_utils::destroy(lending_market_id); clock::destroy_for_testing(clock); sui::test_utils::destroy(obligation); @@ -1157,9 +1175,11 @@ module suilend::obligation_tests { &clock, 100 * 1_000_000_000 ); + let borrow_idx = get_reserve_array_index(&reserves); borrow( &mut obligation, - get_reserve_mut(&mut reserves), + &mut reserves, + borrow_idx, &clock, 100 * 1_000_000 ); @@ -1185,6 +1205,7 @@ module suilend::obligation_tests { test_scenario::end(scenario); } + #[test] public fun test_refresh_happy() { use sui::test_scenario::{Self}; @@ -1212,9 +1233,11 @@ module suilend::obligation_tests { &clock, 100 * 1_000_000 ); + let borrow_idx = get_reserve_array_index(&reserves); borrow( &mut obligation, - get_reserve_mut(&mut reserves), + &mut reserves, + borrow_idx, &clock, 100 * 1_000_000 ); @@ -1298,9 +1321,11 @@ module suilend::obligation_tests { &clock, 100 * 1_000_000_000 ); + let borrow_idx = get_reserve_array_index(&reserves); borrow( &mut obligation, - get_reserve_mut(&mut reserves), + &mut reserves, + borrow_idx, &clock, 100 * 1_000_000 ); @@ -1347,15 +1372,19 @@ module suilend::obligation_tests { &clock, 100 * 1_000_000_000 ); + let borrow_idx = get_reserve_array_index(&reserves); borrow( &mut obligation, - get_reserve_mut(&mut reserves), + &mut reserves, + borrow_idx, &clock, 50 * 1_000_000 ); + let borrow_idx = get_reserve_array_index(&reserves); borrow( &mut obligation, - get_reserve_mut(&mut reserves), + &mut reserves, + borrow_idx, &clock, 50 * 1_000_000 ); @@ -1458,9 +1487,11 @@ module suilend::obligation_tests { &clock, 2 * 100_000_000 ); + let borrow_idx = get_reserve_array_index(&reserves); borrow( &mut obligation, - get_reserve_mut(&mut reserves), + &mut reserves, + borrow_idx, &clock, 100 * 1_000_000 ); @@ -1513,7 +1544,7 @@ module suilend::obligation_tests { assert!(obligation.deposits().length() == 1, 0); - let user_reward_manager_index = obligation.find_user_reward_manager_index( + let user_reward_manager_index = obligation.find_user_reward_manager_index( reserve::deposits_pool_reward_manager_mut(get_reserve_mut(&mut reserves)) ); let user_reward_manager = &obligation.user_reward_managers()[user_reward_manager_index]; @@ -1569,9 +1600,11 @@ module suilend::obligation_tests { &clock, 100 * 1_000_000_000 ); + let borrow_idx = get_reserve_array_index(&reserves); borrow( &mut obligation, - get_reserve_mut(&mut reserves), + &mut reserves, + borrow_idx, &clock, 1 * 1_000_000 ); @@ -1623,7 +1656,7 @@ module suilend::obligation_tests { let user_reward_manager_index = obligation.find_user_reward_manager_index( reserve::borrows_pool_reward_manager_mut(get_reserve_mut(&mut reserves)) ); - let user_reward_manager = &obligation.user_reward_managers()[user_reward_manager_index]; + let user_reward_manager = vector::borrow(obligation.user_reward_managers(), user_reward_manager_index); assert!(liquidity_mining::shares(user_reward_manager) == 0, 5); assert!(obligation.deposited_value_usd() == add(decimal::from(998), decimal::from_percent(90)), 0); @@ -1666,9 +1699,11 @@ module suilend::obligation_tests { &clock, 550_000 ); + let borrow_idx = get_reserve_array_index(&reserves); borrow( &mut obligation, - get_reserve_mut(&mut reserves), + &mut reserves, + borrow_idx, &clock, 10 * 1_000_000 ); @@ -1782,9 +1817,11 @@ module suilend::obligation_tests { &clock, 10 * 1_000_000_000 ); + let borrow_idx = get_reserve_array_index(&reserves); borrow( &mut obligation, - get_reserve_mut(&mut reserves), + &mut reserves, + borrow_idx, &clock, 1_000_000 ); @@ -1827,18 +1864,22 @@ module suilend::obligation_tests { &clock, 100 * 1_000_000 ); + let borrow_idx = get_reserve_array_index(&reserves); borrow( &mut obligation, - get_reserve_mut(&mut reserves), + &mut reserves, + borrow_idx, &clock, 1_000_000_000 ); assert!(!obligation.is_looped(), 0); + let borrow_idx = get_reserve_array_index(&reserves); borrow( &mut obligation, - get_reserve_mut(&mut reserves), + &mut reserves, + borrow_idx, &clock, 1_000_000 ); @@ -1899,27 +1940,33 @@ module suilend::obligation_tests { &clock, 100 * 1_000_000 ); + let borrow_idx = get_reserve_array_index(&reserves); borrow( &mut obligation, - get_reserve_mut(&mut reserves), + &mut reserves, + borrow_idx, &clock, 1_000_000_000 ); assert!(!obligation.is_looped(), 0); + let borrow_idx = get_reserve_array_index(&reserves); borrow( &mut obligation, - get_reserve_mut(&mut reserves), + &mut reserves, + borrow_idx, &clock, 1_000 ); assert!(!obligation.is_looped(), 0); + let borrow_idx = get_reserve_array_index(&reserves); borrow( &mut obligation, - get_reserve_mut(&mut reserves), + &mut reserves, + borrow_idx, &clock, 1_000_000 ); @@ -1935,9 +1982,11 @@ module suilend::obligation_tests { assert!(!obligation.is_looped(), 0); + let borrow_idx = get_reserve_array_index(&reserves); borrow( &mut obligation, - get_reserve_mut(&mut reserves), + &mut reserves, + borrow_idx, &clock, 1_000_000 ); @@ -1979,27 +2028,34 @@ module suilend::obligation_tests { &clock, 100 * 1_000_000 ); + + let borrow_idx = get_reserve_array_index(&reserves); borrow( &mut obligation, - get_reserve_mut(&mut reserves), + &mut reserves, + borrow_idx, &clock, 1_000_000_000 ); assert!(!obligation.is_looped(), 0); + let borrow_idx = get_reserve_array_index(&reserves); borrow( &mut obligation, - get_reserve_mut(&mut reserves), + &mut reserves, + borrow_idx, &clock, 1_000 ); assert!(!obligation.is_looped(), 0); + let borrow_idx = get_reserve_array_index(&reserves); borrow( &mut obligation, - get_reserve_mut(&mut reserves), + &mut reserves, + borrow_idx, &clock, 1_000_000 ); @@ -2015,9 +2071,11 @@ module suilend::obligation_tests { assert!(!obligation.is_looped(), 0); + let borrow_idx = get_reserve_array_index(&reserves); borrow( &mut obligation, - get_reserve_mut(&mut reserves), + &mut reserves, + borrow_idx, &clock, 1_000_000 ); @@ -2059,27 +2117,34 @@ module suilend::obligation_tests { &clock, 100 * 1_000_000 ); + + let borrow_idx = get_reserve_array_index(&reserves); borrow( &mut obligation, - get_reserve_mut(&mut reserves), + &mut reserves, + borrow_idx, &clock, 1_000_000_000 ); assert!(!obligation.is_looped(), 0); + let borrow_idx = get_reserve_array_index(&reserves); borrow( &mut obligation, - get_reserve_mut(&mut reserves), + &mut reserves, + borrow_idx, &clock, 1_000 ); assert!(!obligation.is_looped(), 0); + let borrow_idx = get_reserve_array_index(&reserves); borrow( &mut obligation, - get_reserve_mut(&mut reserves), + &mut reserves, + borrow_idx, &clock, 1_000_000 ); @@ -2095,9 +2160,11 @@ module suilend::obligation_tests { assert!(!obligation.is_looped(), 0); + let borrow_idx = get_reserve_array_index(&reserves); borrow( &mut obligation, - get_reserve_mut(&mut reserves), + &mut reserves, + borrow_idx, &clock, 1_000_000 ); @@ -2155,27 +2222,33 @@ module suilend::obligation_tests { sui::test_utils::destroy(vector::pop_back(obligation.borrows_mut())); + let borrow_idx = get_reserve_array_index(&reserves); borrow( &mut obligation, - get_reserve_mut(&mut reserves), + &mut reserves, + borrow_idx, &clock, 1_000 ); assert!(!obligation.is_looped(), 0); + let borrow_idx = get_reserve_array_index(&reserves); borrow( &mut obligation, - get_reserve_mut(&mut reserves), + &mut reserves, + borrow_idx, &clock, 1_000 ); assert!(!obligation.is_looped(), 0); + let borrow_idx = get_reserve_array_index(&reserves); borrow( &mut obligation, - get_reserve_mut(&mut reserves), + &mut reserves, + borrow_idx, &clock, 1_000 ); @@ -2224,9 +2297,11 @@ module suilend::obligation_tests { &clock, 100 * 1_000_000 ); + let borrow_idx = get_reserve_array_index(&reserves); borrow( &mut obligation, - get_reserve_mut(&mut reserves), + &mut reserves, + borrow_idx, &clock, 1_000_000_000 ); @@ -2242,9 +2317,11 @@ module suilend::obligation_tests { }; // actually loop + let borrow_idx = get_reserve_array_index(&reserves); borrow( &mut obligation, - get_reserve_mut(&mut reserves), + &mut reserves, + borrow_idx, &clock, 1_000_000 ); @@ -2266,7 +2343,7 @@ module suilend::obligation_tests { } #[test] - public fun test_set_emod_on_and_off() { + public fun test_emode_deposit_borrow() { use sui::test_scenario::{Self}; let owner = @0x26; @@ -2279,7 +2356,7 @@ module suilend::obligation_tests { let clock = clock::create_for_testing(test_scenario::ctx(&mut scenario)); reserve::update_price_for_testing( - get_reserve_mut(&mut reserves), + get_reserve_mut(&mut reserves), &clock, decimal::from(1), decimal::from(2) @@ -2291,14 +2368,14 @@ module suilend::obligation_tests { decimal::from(5) ); - let usdc_index = get_reserve_array_index(&reserves); + let borrow_idx = get_reserve_array_index(&reserves); let sui_reserve = get_reserve_mut(&mut reserves); let mut builder = sui_reserve.config().from(test_scenario::ctx(&mut scenario)); let emode_ltvs = reserve_config::create_emode_entry( - usdc_index, + borrow_idx, 40, - 60, + 80, ); builder.set_emode_ltv_for_borrow(emode_ltvs); @@ -2306,18 +2383,876 @@ module suilend::obligation_tests { sui_reserve.update_reserve_config(new_config); obligation.toggle_emode(&mut reserves, &clock); - assert!(obligation.is_emode(), 0); - obligation.toggle_emode(&mut reserves, &clock); - assert!(!obligation.is_emode(), 0); - sui::test_utils::destroy(lending_market_id); - sui::test_utils::destroy(reserves); - sui::test_utils::destroy(obligation); - clock::destroy_for_testing(clock); - test_scenario::end(scenario); - } - - #[test] + deposit(&mut obligation, get_reserve_mut(&mut reserves), &clock, 100 * 1_000_000_000); + let borrow_idx = get_reserve_array_index(&reserves); + borrow(&mut obligation, &mut reserves, borrow_idx, &clock, 12_500_000); + borrow(&mut obligation, &mut reserves, borrow_idx, &clock, 12_500_000); + + assert!(vector::length(obligation.deposits()) == 1, 0); + + let sui_deposit = vector::borrow(obligation.deposits(), 0); + assert!(sui_deposit.deposited_ctoken_amount() == 100 * 1_000_000_000, 3); + assert!(sui_deposit.market_value() == decimal::from(1000), 4); + + let user_reward_manager = vector::borrow(obligation.user_reward_managers(), sui_deposit.user_reward_manager_index()); + assert!(liquidity_mining::shares(user_reward_manager) == 100 * 1_000_000_000, 3); + + assert!(obligation.borrows().length() == 1, 0); + + let usdc_borrow = vector::borrow(obligation.borrows(), 0); + assert!(usdc_borrow.borrowed_amount() == decimal::from(25 * 1_000_000), 1); + assert!(usdc_borrow.cumulative_borrow_rate() == decimal::from(2), 2); + assert!(usdc_borrow.market_value() == decimal::from(25), 3); + + let user_reward_manager = vector::borrow(obligation.user_reward_managers(), usdc_borrow.user_reward_manager_index()); + assert!(liquidity_mining::shares(user_reward_manager) == 25 * 1_000_000 / 2, 4); + + // Values unchanged due to emode + assert!(obligation.deposited_value_usd() == decimal::from(1000), 0); + assert!(obligation.unweighted_borrowed_value_usd() == decimal::from(25), 3); + + // Values changed due to emode + assert!(obligation.allowed_borrow_value_usd() == decimal::from(200), 1); + assert!(obligation.unhealthy_borrow_value_usd() == decimal::from(800), 2); + assert!(obligation.weighted_borrowed_value_usd() == decimal::from(25), 4); + assert!(obligation.weighted_borrowed_value_upper_bound_usd() == decimal::from(50), 4); + + sui::test_utils::destroy(lending_market_id); + sui::test_utils::destroy(reserves); + sui::test_utils::destroy(obligation); + clock::destroy_for_testing(clock); + test_scenario::end(scenario); + } + + #[test] + public fun test_emode_multiple_borrows_happy() { + use sui::test_scenario::{Self}; + + let owner = @0x26; + let mut scenario = test_scenario::begin(owner); + let lending_market_id = object::new(test_scenario::ctx(&mut scenario)); + + let mut reserves = reserves(&mut scenario); + + let mut obligation = create_obligation(object::uid_to_inner(&lending_market_id), test_scenario::ctx(&mut scenario)); + let clock = clock::create_for_testing(test_scenario::ctx(&mut scenario)); + + reserve::update_price_for_testing( + get_reserve_mut(&mut reserves), + &clock, + decimal::from(1), + decimal::from(2) + ); + reserve::update_price_for_testing( + get_reserve_mut(&mut reserves), + &clock, + decimal::from(10), + decimal::from(5) + ); + + let borrow_idx = get_reserve_array_index(&reserves); + + let sui_reserve = get_reserve_mut(&mut reserves); + + let mut builder = sui_reserve.config().from(test_scenario::ctx(&mut scenario)); + let emode_ltvs = reserve_config::create_emode_entry( + borrow_idx, + 40, + 60, + ); + + builder.set_emode_ltv_for_borrow(emode_ltvs); + let new_config = builder.build(test_scenario::ctx(&mut scenario)); + sui_reserve.update_reserve_config(new_config); + + obligation.toggle_emode(&mut reserves, &clock); + + deposit(&mut obligation, get_reserve_mut(&mut reserves), &clock, 100 * 1_000_000_000); + borrow(&mut obligation, &mut reserves, borrow_idx, &clock, 12_500_000); + borrow(&mut obligation, &mut reserves, borrow_idx, &clock, 12_500_000); + + sui::test_utils::destroy(lending_market_id); + sui::test_utils::destroy(reserves); + sui::test_utils::destroy(obligation); + clock::destroy_for_testing(clock); + test_scenario::end(scenario); + } + + #[test] + #[expected_failure(abort_code = suilend::obligation::EIsolatedAssetViolation)] + public fun test_emode_multiple_borrows_fail() { + use sui::test_scenario::{Self}; + + let owner = @0x26; + let mut scenario = test_scenario::begin(owner); + let lending_market_id = object::new(test_scenario::ctx(&mut scenario)); + + let mut reserves = reserves(&mut scenario); + + let mut obligation = create_obligation(object::uid_to_inner(&lending_market_id), test_scenario::ctx(&mut scenario)); + let clock = clock::create_for_testing(test_scenario::ctx(&mut scenario)); + + reserve::update_price_for_testing( + get_reserve_mut(&mut reserves), + &clock, + decimal::from(1), + decimal::from(2) + ); + reserve::update_price_for_testing( + get_reserve_mut(&mut reserves), + &clock, + decimal::from(10), + decimal::from(5) + ); + reserve::update_price_for_testing( + get_reserve_mut(&mut reserves), + &clock, + decimal::from(1), + decimal::from(2) + ); + + let borrow_idx = get_reserve_array_index(&reserves); + let sui_reserve = get_reserve_mut(&mut reserves); + + let mut builder = sui_reserve.config().from(test_scenario::ctx(&mut scenario)); + let emode_ltvs = reserve_config::create_emode_entry( + borrow_idx, + 40, + 60, + ); + + builder.set_emode_ltv_for_borrow(emode_ltvs); + let new_config = builder.build(test_scenario::ctx(&mut scenario)); + sui_reserve.update_reserve_config(new_config); + + obligation.toggle_emode(&mut reserves, &clock); + + deposit(&mut obligation, get_reserve_mut(&mut reserves), &clock, 100 * 1_000_000_000); + borrow(&mut obligation, &mut reserves, borrow_idx, &clock, 12_500_000); + let borrow_idx = get_reserve_array_index(&reserves); + borrow(&mut obligation, &mut reserves, borrow_idx, &clock, 12_500_000); + + sui::test_utils::destroy(lending_market_id); + sui::test_utils::destroy(reserves); + sui::test_utils::destroy(obligation); + clock::destroy_for_testing(clock); + test_scenario::end(scenario); + } + + #[test] + #[expected_failure(abort_code = suilend::obligation::EIsolatedAssetViolation)] + public fun test_emode_multiple_deposits_fail() { + use sui::test_scenario::{Self}; + + let owner = @0x26; + let mut scenario = test_scenario::begin(owner); + let lending_market_id = object::new(test_scenario::ctx(&mut scenario)); + + let mut reserves = reserves(&mut scenario); + + let mut obligation = create_obligation(object::uid_to_inner(&lending_market_id), test_scenario::ctx(&mut scenario)); + let clock = clock::create_for_testing(test_scenario::ctx(&mut scenario)); + + reserve::update_price_for_testing( + get_reserve_mut(&mut reserves), + &clock, + decimal::from(1), + decimal::from(2) + ); + reserve::update_price_for_testing( + get_reserve_mut(&mut reserves), + &clock, + decimal::from(10), + decimal::from(5) + ); + + let usdc_index = get_reserve_array_index(&reserves); + let sui_reserve = get_reserve_mut(&mut reserves); + + let mut builder = sui_reserve.config().from(test_scenario::ctx(&mut scenario)); + let emode_ltvs = reserve_config::create_emode_entry( + usdc_index, + 40, + 60, + ); + + builder.set_emode_ltv_for_borrow(emode_ltvs); + let new_config = builder.build(test_scenario::ctx(&mut scenario)); + sui_reserve.update_reserve_config(new_config); + + let usdt_reserve = get_reserve_mut(&mut reserves); + let mut builder = usdt_reserve.config().from(test_scenario::ctx(&mut scenario)); + let emode_ltvs = reserve_config::create_emode_entry( + usdc_index, + 60, + 90, + ); + + builder.set_emode_ltv_for_borrow(emode_ltvs); + let new_config = builder.build(test_scenario::ctx(&mut scenario)); + usdt_reserve.update_reserve_config(new_config); + + obligation.toggle_emode(&mut reserves, &clock); + + deposit(&mut obligation, get_reserve_mut(&mut reserves), &clock, 100 * 1_000_000_000); + deposit(&mut obligation, get_reserve_mut(&mut reserves), &clock, 12_500_000); + + sui::test_utils::destroy(lending_market_id); + sui::test_utils::destroy(reserves); + sui::test_utils::destroy(obligation); + clock::destroy_for_testing(clock); + test_scenario::end(scenario); + } + + #[test] + #[expected_failure(abort_code = suilend::obligation::ENoEmodeConfigForGivenDepositReserve)] + public fun test_set_emode_invalid_deposit_reserve() { + use sui::test_scenario::{Self}; + + let owner = @0x26; + let mut scenario = test_scenario::begin(owner); + let lending_market_id = object::new(test_scenario::ctx(&mut scenario)); + + let mut reserves = reserves(&mut scenario); + + let mut obligation = create_obligation(object::uid_to_inner(&lending_market_id), test_scenario::ctx(&mut scenario)); + let clock = clock::create_for_testing(test_scenario::ctx(&mut scenario)); + + reserve::update_price_for_testing( + get_reserve_mut(&mut reserves), + &clock, + decimal::from(1), + decimal::from(2) + ); + reserve::update_price_for_testing( + get_reserve_mut(&mut reserves), + &clock, + decimal::from(10), + decimal::from(5) + ); + + deposit(&mut obligation, get_reserve_mut(&mut reserves), &clock, 12_500_000); + + obligation.toggle_emode(&mut reserves, &clock); + + sui::test_utils::destroy(lending_market_id); + sui::test_utils::destroy(reserves); + sui::test_utils::destroy(obligation); + clock::destroy_for_testing(clock); + test_scenario::end(scenario); + } + + #[test] + #[expected_failure(abort_code = suilend::obligation::EEModeNotValidWithCrossMargin)] + public fun test_emode_cross_borrow_fail() { + use sui::test_scenario::{Self}; + + let owner = @0x26; + let mut scenario = test_scenario::begin(owner); + let lending_market_id = object::new(test_scenario::ctx(&mut scenario)); + + let mut reserves = reserves(&mut scenario); + + let mut obligation = create_obligation(object::uid_to_inner(&lending_market_id), test_scenario::ctx(&mut scenario)); + let clock = clock::create_for_testing(test_scenario::ctx(&mut scenario)); + + reserve::update_price_for_testing( + get_reserve_mut(&mut reserves), + &clock, + decimal::from(1), + decimal::from(2) + ); + reserve::update_price_for_testing( + get_reserve_mut(&mut reserves), + &clock, + decimal::from(1), + decimal::from(2) + ); + reserve::update_price_for_testing( + get_reserve_mut(&mut reserves), + &clock, + decimal::from(10), + decimal::from(5) + ); + + deposit(&mut obligation, get_reserve_mut(&mut reserves), &clock, 100 * 1_000_000_000); + let borrow_idx = get_reserve_array_index(&reserves); + borrow(&mut obligation, &mut reserves, borrow_idx, &clock, 12_500_000); + let borrow_idx = get_reserve_array_index(&reserves); + borrow(&mut obligation, &mut reserves, borrow_idx, &clock, 12_500_000); + + obligation.toggle_emode(&mut reserves, &clock); + + sui::test_utils::destroy(lending_market_id); + sui::test_utils::destroy(reserves); + sui::test_utils::destroy(obligation); + clock::destroy_for_testing(clock); + test_scenario::end(scenario); + } + + #[test] + #[expected_failure(abort_code = suilend::obligation::EEModeNotValidWithCrossMargin)] + public fun test_emode_cross_deposit_fail() { + use sui::test_scenario::{Self}; + + let owner = @0x26; + let mut scenario = test_scenario::begin(owner); + let lending_market_id = object::new(test_scenario::ctx(&mut scenario)); + + let mut reserves = reserves(&mut scenario); + + let mut obligation = create_obligation(object::uid_to_inner(&lending_market_id), test_scenario::ctx(&mut scenario)); + let clock = clock::create_for_testing(test_scenario::ctx(&mut scenario)); + + reserve::update_price_for_testing( + get_reserve_mut(&mut reserves), + &clock, + decimal::from(1), + decimal::from(2) + ); + reserve::update_price_for_testing( + get_reserve_mut(&mut reserves), + &clock, + decimal::from(1), + decimal::from(2) + ); + reserve::update_price_for_testing( + get_reserve_mut(&mut reserves), + &clock, + decimal::from(10), + decimal::from(5) + ); + + deposit(&mut obligation, get_reserve_mut(&mut reserves), &clock, 100 * 1_000_000_000); + deposit(&mut obligation, get_reserve_mut(&mut reserves),&clock, 100 * 1_000_000_000); + let borrow_idx = get_reserve_array_index(&reserves); + borrow(&mut obligation, &mut reserves, borrow_idx, &clock, 12_500_000); + + obligation.toggle_emode(&mut reserves, &clock); + + sui::test_utils::destroy(lending_market_id); + sui::test_utils::destroy(reserves); + sui::test_utils::destroy(obligation); + clock::destroy_for_testing(clock); + test_scenario::end(scenario); + } + + #[test] + public fun test_emode_with_normal_borrow_reserve_ok_ltvs() { + use sui::test_scenario::{Self}; + + let owner = @0x26; + let mut scenario = test_scenario::begin(owner); + let lending_market_id = object::new(test_scenario::ctx(&mut scenario)); + + let mut reserves = reserves(&mut scenario); + + let mut obligation = create_obligation(object::uid_to_inner(&lending_market_id), test_scenario::ctx(&mut scenario)); + let clock = clock::create_for_testing(test_scenario::ctx(&mut scenario)); + + reserve::update_price_for_testing( + get_reserve_mut(&mut reserves), + &clock, + decimal::from(1), + decimal::from(2) + ); + reserve::update_price_for_testing( + get_reserve_mut(&mut reserves), + &clock, + decimal::from(1), + decimal::from(2) + ); + reserve::update_price_for_testing( + get_reserve_mut(&mut reserves), + &clock, + decimal::from(10), + decimal::from(5) + ); + + let borrow_idx = get_reserve_array_index(&reserves); + let sui_reserve = get_reserve_mut(&mut reserves); + + let mut builder = sui_reserve.config().from(test_scenario::ctx(&mut scenario)); + let emode_ltvs = reserve_config::create_emode_entry( + borrow_idx, + 40, + 60, + ); + + builder.set_emode_ltv_for_borrow(emode_ltvs); + let new_config = builder.build(test_scenario::ctx(&mut scenario)); + sui_reserve.update_reserve_config(new_config); + + deposit(&mut obligation, get_reserve_mut(&mut reserves), &clock, 100 * 1_000_000_000); + let borrow_idx = get_reserve_array_index(&reserves); + borrow(&mut obligation, &mut reserves, borrow_idx, &clock, 12_500_000); + + obligation.toggle_emode(&mut reserves, &clock); + + sui::test_utils::destroy(lending_market_id); + sui::test_utils::destroy(reserves); + sui::test_utils::destroy(obligation); + clock::destroy_for_testing(clock); + test_scenario::end(scenario); + } + + #[test] + #[expected_failure(abort_code = reserve_config::ENormalOpenLtvBetterThanEModeLtvs)] + public fun test_emode_with_normal_borrow_reserve_fail_open_ltv_1() { + use sui::test_scenario::{Self}; + + let owner = @0x26; + let mut scenario = test_scenario::begin(owner); + let lending_market_id = object::new(test_scenario::ctx(&mut scenario)); + + let mut reserves = reserves(&mut scenario); + + let mut obligation = create_obligation(object::uid_to_inner(&lending_market_id), test_scenario::ctx(&mut scenario)); + let clock = clock::create_for_testing(test_scenario::ctx(&mut scenario)); + + reserve::update_price_for_testing( + get_reserve_mut(&mut reserves), + &clock, + decimal::from(1), + decimal::from(2) + ); + reserve::update_price_for_testing( + get_reserve_mut(&mut reserves), + &clock, + decimal::from(1), + decimal::from(2) + ); + reserve::update_price_for_testing( + get_reserve_mut(&mut reserves), + &clock, + decimal::from(10), + decimal::from(5) + ); + + let borrow_idx = get_reserve_array_index(&reserves); + let sui_reserve = get_reserve_mut(&mut reserves); + + let mut builder = sui_reserve.config().from(test_scenario::ctx(&mut scenario)); + let emode_ltvs = reserve_config::create_emode_entry( + borrow_idx, + 20 - 1, + 80, + ); + + builder.set_emode_ltv_for_borrow(emode_ltvs); + let new_config = builder.build(test_scenario::ctx(&mut scenario)); + sui_reserve.update_reserve_config(new_config); + + obligation.toggle_emode(&mut reserves, &clock); + + sui::test_utils::destroy(lending_market_id); + sui::test_utils::destroy(reserves); + sui::test_utils::destroy(obligation); + clock::destroy_for_testing(clock); + test_scenario::end(scenario); + } + + #[test] + #[expected_failure(abort_code = reserve_config::ENormalCloseLtvBetterThanEModeLtvs)] + public fun test_emode_with_normal_borrow_reserve_fail_close_ltv_1() { + use sui::test_scenario::{Self}; + + let owner = @0x26; + let mut scenario = test_scenario::begin(owner); + let lending_market_id = object::new(test_scenario::ctx(&mut scenario)); + + let mut reserves = reserves(&mut scenario); + + let mut obligation = create_obligation(object::uid_to_inner(&lending_market_id), test_scenario::ctx(&mut scenario)); + let clock = clock::create_for_testing(test_scenario::ctx(&mut scenario)); + + reserve::update_price_for_testing( + get_reserve_mut(&mut reserves), + &clock, + decimal::from(1), + decimal::from(2) + ); + reserve::update_price_for_testing( + get_reserve_mut(&mut reserves), + &clock, + decimal::from(1), + decimal::from(2) + ); + reserve::update_price_for_testing( + get_reserve_mut(&mut reserves), + &clock, + decimal::from(10), + decimal::from(5) + ); + + let borrow_idx = get_reserve_array_index(&reserves); + let sui_reserve = get_reserve_mut(&mut reserves); + + let mut builder = sui_reserve.config().from(test_scenario::ctx(&mut scenario)); + let emode_ltvs = reserve_config::create_emode_entry( + borrow_idx, + 30, + 50 - 1, + ); + + builder.set_emode_ltv_for_borrow(emode_ltvs); + let new_config = builder.build(test_scenario::ctx(&mut scenario)); + sui_reserve.update_reserve_config(new_config); + + obligation.toggle_emode(&mut reserves, &clock); + deposit(&mut obligation, get_reserve_mut(&mut reserves), &clock, 100 * 1_000_000_000); + let borrow_idx = get_reserve_array_index(&reserves); + borrow(&mut obligation, &mut reserves, borrow_idx, &clock, 12_500_000); + + sui::test_utils::destroy(lending_market_id); + sui::test_utils::destroy(reserves); + sui::test_utils::destroy(obligation); + clock::destroy_for_testing(clock); + test_scenario::end(scenario); + } + + #[test] + #[expected_failure(abort_code = reserve_config::ENormalOpenLtvBetterThanEModeLtvs)] + public fun test_emode_with_normal_borrow_reserve_fail_open_ltv_2() { + use sui::test_scenario::{Self}; + + let owner = @0x26; + let mut scenario = test_scenario::begin(owner); + let lending_market_id = object::new(test_scenario::ctx(&mut scenario)); + + let mut reserves = reserves(&mut scenario); + + let mut obligation = create_obligation(object::uid_to_inner(&lending_market_id), test_scenario::ctx(&mut scenario)); + let clock = clock::create_for_testing(test_scenario::ctx(&mut scenario)); + + reserve::update_price_for_testing( + get_reserve_mut(&mut reserves), + &clock, + decimal::from(1), + decimal::from(2) + ); + reserve::update_price_for_testing( + get_reserve_mut(&mut reserves), + &clock, + decimal::from(1), + decimal::from(2) + ); + reserve::update_price_for_testing( + get_reserve_mut(&mut reserves), + &clock, + decimal::from(10), + decimal::from(5) + ); + + let borrow_idx = get_reserve_array_index(&reserves); + let sui_reserve = get_reserve_mut(&mut reserves); + + let mut builder = sui_reserve.config().from(test_scenario::ctx(&mut scenario)); + let emode_ltvs = reserve_config::create_emode_entry( + borrow_idx, + 20 - 1, + 80, + ); + + builder.set_emode_ltv_for_borrow(emode_ltvs); + let new_config = builder.build(test_scenario::ctx(&mut scenario)); + sui_reserve.update_reserve_config(new_config); + + deposit(&mut obligation, get_reserve_mut(&mut reserves), &clock, 100 * 1_000_000_000); + let borrow_idx = get_reserve_array_index(&reserves); + borrow(&mut obligation, &mut reserves, borrow_idx, &clock, 12_500_000); + obligation.toggle_emode(&mut reserves, &clock); + + sui::test_utils::destroy(lending_market_id); + sui::test_utils::destroy(reserves); + sui::test_utils::destroy(obligation); + clock::destroy_for_testing(clock); + test_scenario::end(scenario); + } + + #[test] + #[expected_failure(abort_code = reserve_config::ENormalCloseLtvBetterThanEModeLtvs)] + public fun test_emode_with_normal_borrow_reserve_fail_close_ltv_2() { + use sui::test_scenario::{Self}; + + let owner = @0x26; + let mut scenario = test_scenario::begin(owner); + let lending_market_id = object::new(test_scenario::ctx(&mut scenario)); + + let mut reserves = reserves(&mut scenario); + + let mut obligation = create_obligation(object::uid_to_inner(&lending_market_id), test_scenario::ctx(&mut scenario)); + let clock = clock::create_for_testing(test_scenario::ctx(&mut scenario)); + + reserve::update_price_for_testing( + get_reserve_mut(&mut reserves), + &clock, + decimal::from(1), + decimal::from(2) + ); + reserve::update_price_for_testing( + get_reserve_mut(&mut reserves), + &clock, + decimal::from(1), + decimal::from(2) + ); + reserve::update_price_for_testing( + get_reserve_mut(&mut reserves), + &clock, + decimal::from(10), + decimal::from(5) + ); + + let borrow_idx = get_reserve_array_index(&reserves); + let sui_reserve = get_reserve_mut(&mut reserves); + + let mut builder = sui_reserve.config().from(test_scenario::ctx(&mut scenario)); + let emode_ltvs = reserve_config::create_emode_entry( + borrow_idx, + 30, + 50 - 1, + ); + + builder.set_emode_ltv_for_borrow(emode_ltvs); + let new_config = builder.build(test_scenario::ctx(&mut scenario)); + sui_reserve.update_reserve_config(new_config); + + deposit(&mut obligation, get_reserve_mut(&mut reserves), &clock, 100 * 1_000_000_000); + let borrow_idx = get_reserve_array_index(&reserves); + borrow(&mut obligation, &mut reserves, borrow_idx, &clock, 12_500_000); + obligation.toggle_emode(&mut reserves, &clock); + + sui::test_utils::destroy(lending_market_id); + sui::test_utils::destroy(reserves); + sui::test_utils::destroy(obligation); + clock::destroy_for_testing(clock); + test_scenario::end(scenario); + } + + #[test] + public fun test_emode_refresh() { + use sui::test_scenario::{Self}; + + let owner = @0x26; + let mut scenario = test_scenario::begin(owner); + let lending_market_id = object::new(test_scenario::ctx(&mut scenario)); + + let mut reserves = reserves(&mut scenario); + + let mut obligation = create_obligation(object::uid_to_inner(&lending_market_id), test_scenario::ctx(&mut scenario)); + let clock = clock::create_for_testing(test_scenario::ctx(&mut scenario)); + + reserve::update_price_for_testing( + get_reserve_mut(&mut reserves), + &clock, + decimal::from(1), + decimal::from(2) + ); + reserve::update_price_for_testing( + get_reserve_mut(&mut reserves), + &clock, + decimal::from(10), + decimal::from(5) + ); + + let borrow_idx = get_reserve_array_index(&reserves); + let sui_reserve = get_reserve_mut(&mut reserves); + + let mut builder = sui_reserve.config().from(test_scenario::ctx(&mut scenario)); + let emode_ltvs = reserve_config::create_emode_entry( + borrow_idx, + 40, + 60, + ); + + builder.set_emode_ltv_for_borrow(emode_ltvs); + let new_config = builder.build(test_scenario::ctx(&mut scenario)); + sui_reserve.update_reserve_config(new_config); + + obligation.toggle_emode(&mut reserves, &clock); + + deposit(&mut obligation, get_reserve_mut(&mut reserves), &clock, 100 * 1_000_000_000); + borrow(&mut obligation, &mut reserves, borrow_idx, &clock, 12_500_000); + borrow(&mut obligation, &mut reserves, borrow_idx, &clock, 12_500_000); + + refresh(&mut obligation, &mut reserves, &clock); + + sui::test_utils::destroy(lending_market_id); + sui::test_utils::destroy(reserves); + sui::test_utils::destroy(obligation); + clock::destroy_for_testing(clock); + test_scenario::end(scenario); + } + + #[test] + public fun test_emode_withdraw() { + use sui::test_scenario::{Self}; + + let owner = @0x26; + let mut scenario = test_scenario::begin(owner); + let lending_market_id = object::new(test_scenario::ctx(&mut scenario)); + + let mut reserves = reserves(&mut scenario); + + let mut obligation = create_obligation(object::uid_to_inner(&lending_market_id), test_scenario::ctx(&mut scenario)); + let clock = clock::create_for_testing(test_scenario::ctx(&mut scenario)); + + reserve::update_price_for_testing( + get_reserve_mut(&mut reserves), + &clock, + decimal::from(1), + decimal::from(2) + ); + reserve::update_price_for_testing( + get_reserve_mut(&mut reserves), + &clock, + decimal::from(10), + decimal::from(5) + ); + + let borrow_idx = get_reserve_array_index(&reserves); + let sui_reserve = get_reserve_mut(&mut reserves); + + let mut builder = sui_reserve.config().from(test_scenario::ctx(&mut scenario)); + let emode_ltvs = reserve_config::create_emode_entry( + borrow_idx, + 40, + 60, + ); + + builder.set_emode_ltv_for_borrow(emode_ltvs); + let new_config = builder.build(test_scenario::ctx(&mut scenario)); + sui_reserve.update_reserve_config(new_config); + + obligation.toggle_emode(&mut reserves, &clock); + + deposit(&mut obligation, get_reserve_mut(&mut reserves), &clock, 100 * 1_000_000_000); + borrow(&mut obligation, &mut reserves, borrow_idx, &clock, 12_500_000); + borrow(&mut obligation, &mut reserves, borrow_idx, &clock, 12_500_000); + withdraw(&mut obligation, get_reserve_mut(&mut reserves), &clock, 20 * 1_000_000_000); + + sui::test_utils::destroy(lending_market_id); + sui::test_utils::destroy(reserves); + sui::test_utils::destroy(obligation); + clock::destroy_for_testing(clock); + test_scenario::end(scenario); + } + + #[test] + public fun test_emode_repay() { + use sui::test_scenario::{Self}; + + let owner = @0x26; + let mut scenario = test_scenario::begin(owner); + let lending_market_id = object::new(test_scenario::ctx(&mut scenario)); + + let mut reserves = reserves(&mut scenario); + + let mut obligation = create_obligation(object::uid_to_inner(&lending_market_id), test_scenario::ctx(&mut scenario)); + let clock = clock::create_for_testing(test_scenario::ctx(&mut scenario)); + + reserve::update_price_for_testing( + get_reserve_mut(&mut reserves), + &clock, + decimal::from(1), + decimal::from(2) + ); + reserve::update_price_for_testing( + get_reserve_mut(&mut reserves), + &clock, + decimal::from(10), + decimal::from(5) + ); + + let borrow_idx = get_reserve_array_index(&reserves); + let sui_reserve = get_reserve_mut(&mut reserves); + + let mut builder = sui_reserve.config().from(test_scenario::ctx(&mut scenario)); + let emode_ltvs = reserve_config::create_emode_entry( + borrow_idx, + 40, + 60, + ); + + builder.set_emode_ltv_for_borrow(emode_ltvs); + let new_config = builder.build(test_scenario::ctx(&mut scenario)); + sui_reserve.update_reserve_config(new_config); + + obligation.toggle_emode(&mut reserves, &clock); + + deposit(&mut obligation, get_reserve_mut(&mut reserves), &clock, 100 * 1_000_000_000); + borrow(&mut obligation, &mut reserves, borrow_idx, &clock, 12_500_000); + borrow(&mut obligation, &mut reserves, borrow_idx, &clock, 12_500_000); + repay( + &mut obligation, + get_reserve_mut(&mut reserves), + &clock, + decimal::from(12_500_000) + ); + + sui::test_utils::destroy(lending_market_id); + sui::test_utils::destroy(reserves); + sui::test_utils::destroy(obligation); + clock::destroy_for_testing(clock); + test_scenario::end(scenario); + } + + // TODO + + #[test] + public fun test_set_emode_on_and_off() { + use sui::test_scenario::{Self}; + + let owner = @0x26; + let mut scenario = test_scenario::begin(owner); + let lending_market_id = object::new(test_scenario::ctx(&mut scenario)); + + let mut reserves = reserves(&mut scenario); + + let mut obligation = create_obligation(object::uid_to_inner(&lending_market_id), test_scenario::ctx(&mut scenario)); + let clock = clock::create_for_testing(test_scenario::ctx(&mut scenario)); + + reserve::update_price_for_testing( + get_reserve_mut(&mut reserves), + &clock, + decimal::from(1), + decimal::from(2) + ); + reserve::update_price_for_testing( + get_reserve_mut(&mut reserves), + &clock, + decimal::from(10), + decimal::from(5) + ); + + let usdc_index = get_reserve_array_index(&reserves); + let sui_reserve = get_reserve_mut(&mut reserves); + + let mut builder = sui_reserve.config().from(test_scenario::ctx(&mut scenario)); + let emode_ltvs = reserve_config::create_emode_entry( + usdc_index, + 40, + 60, + ); + + builder.set_emode_ltv_for_borrow(emode_ltvs); + let new_config = builder.build(test_scenario::ctx(&mut scenario)); + sui_reserve.update_reserve_config(new_config); + + obligation.toggle_emode(&mut reserves, &clock); + assert!(obligation.is_emode(), 0); + obligation.toggle_emode(&mut reserves, &clock); + assert!(!obligation.is_emode(), 0); + + sui::test_utils::destroy(lending_market_id); + sui::test_utils::destroy(reserves); + sui::test_utils::destroy(obligation); + clock::destroy_for_testing(clock); + test_scenario::end(scenario); + } + + #[test] public fun test_emode_refresh_after_toggling_emode() { use sui::test_scenario::{Self}; @@ -2358,8 +3293,10 @@ module suilend::obligation_tests { sui_reserve.update_reserve_config(new_config); deposit(&mut obligation, get_reserve_mut(&mut reserves), &clock, 100 * 1_000_000_000); - borrow(&mut obligation, get_reserve_mut(&mut reserves), &clock, 12_500_000); - borrow(&mut obligation, get_reserve_mut(&mut reserves), &clock, 12_500_000); + + let borrow_idx = get_reserve_array_index(&reserves); + borrow(&mut obligation, &mut reserves, borrow_idx, &clock, 12_500_000); + borrow(&mut obligation, &mut reserves, borrow_idx, &clock, 12_500_000); obligation.toggle_emode(&mut reserves, &clock);