Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Force close obligations #18

Open
wants to merge 3 commits into
base: devel
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
188 changes: 186 additions & 2 deletions contracts/sources/lending_market.move
Original file line number Diff line number Diff line change
Expand Up @@ -437,7 +437,7 @@ module suilend::lending_market {
);
obligation::refresh<P>(obligation, &mut lending_market.reserves, clock);

let (withdraw_ctoken_amount, required_repay_amount) = obligation::liquidate<P>(
let liquidate_info = obligation::liquidate<P>(
obligation,
&mut lending_market.reserves,
repay_reserve_array_index,
Expand All @@ -446,6 +446,11 @@ module suilend::lending_market {
coin::value(repay_coins)
);

let withdraw_ctoken_amount = obligation::final_withdraw_amount(&liquidate_info);
let required_repay_amount = obligation::final_settle_amount(&liquidate_info);
let protocol_fee_amount = obligation::protocol_fee_amount(&liquidate_info);
let liquidator_bonus_amount = obligation::liquidator_bonus_amount(&liquidate_info);

assert!(gt(required_repay_amount, decimal::from(0)), ETooSmall);

let required_repay_coins = coin::split(repay_coins, ceil(required_repay_amount), ctx);
Expand All @@ -459,8 +464,9 @@ module suilend::lending_market {

let withdraw_reserve = vector::borrow_mut(&mut lending_market.reserves, withdraw_reserve_array_index);
assert!(reserve::coin_type(withdraw_reserve) == type_name::get<Withdraw>(), EWrongType);

let ctokens = reserve::withdraw_ctokens<P, Withdraw>(withdraw_reserve, withdraw_ctoken_amount);
let (protocol_fee_amount, liquidator_bonus_amount) = reserve::deduct_liquidation_fee<P, Withdraw>(withdraw_reserve, &mut ctokens);
reserve::add_ctoken_fee<P, Withdraw>(withdraw_reserve, balance::split(&mut ctokens, protocol_fee_amount));

let repay_reserve = vector::borrow(&lending_market.reserves, repay_reserve_array_index);
let withdraw_reserve = vector::borrow(&lending_market.reserves, withdraw_reserve_array_index);
Expand Down Expand Up @@ -812,6 +818,18 @@ module suilend::lending_market {
reserve::update_reserve_config<P>(reserve, config);
}

public fun set_obligation_closability_status<P>(
_: &LendingMarketOwnerCap<P>,
lending_market: &mut LendingMarket<P>,
obligation_id: ID,
closable: bool
) {
assert!(lending_market.version == CURRENT_VERSION, EIncorrectVersion);

let obligation = object_table::borrow_mut(&mut lending_market.obligations, obligation_id);
obligation::set_closability_status<P>(obligation, closable);
}

public fun add_pool_reward<P, RewardType>(
_: &LendingMarketOwnerCap<P>,
lending_market: &mut LendingMarket<P>,
Expand Down Expand Up @@ -1856,6 +1874,172 @@ module suilend::lending_market {
test_scenario::end(scenario);
}

#[test]
public fun test_liquidate_closeable_obligation() {
use sui::test_utils::{Self};
use suilend::test_usdc::{TEST_USDC};
use suilend::test_sui::{TEST_SUI};
use suilend::mock_pyth::{Self};
use suilend::reserve_config::{Self, default_reserve_config};
use suilend::decimal::{sub};

use std::type_name::{Self};

let owner = @0x26;
let scenario = test_scenario::begin(owner);
let State { clock, owner_cap, lending_market, prices, type_to_index } = setup({
let bag = bag::new(test_scenario::ctx(&mut scenario));
bag::add(
&mut bag,
type_name::get<TEST_USDC>(),
ReserveArgs {
config: {
let config = default_reserve_config();
let builder = reserve_config::from(&config, test_scenario::ctx(&mut scenario));
reserve_config::set_open_ltv_pct(&mut builder, 50);
reserve_config::set_close_ltv_pct(&mut builder, 50);
reserve_config::set_max_close_ltv_pct(&mut builder, 50);
sui::test_utils::destroy(config);

reserve_config::build(builder, test_scenario::ctx(&mut scenario))
},
initial_deposit: 100 * 1_000_000
}
);
bag::add(
&mut bag,
type_name::get<TEST_SUI>(),
ReserveArgs {
config: reserve_config::default_reserve_config(),
initial_deposit: 100 * 1_000_000_000
}
);

bag
}, &mut scenario);

clock::set_for_testing(&mut clock, 1 * 1000);

// set reserve parameters and prices
mock_pyth::update_price<TEST_USDC>(&mut prices, 1, 0, &clock); // $1
mock_pyth::update_price<TEST_SUI>(&mut prices, 1, 1, &clock); // $10

refresh_reserve_price<LENDING_MARKET>(
&mut lending_market,
*bag::borrow(&type_to_index, type_name::get<TEST_USDC>()),
&clock,
mock_pyth::get_price_obj<TEST_USDC>(&prices)
);
refresh_reserve_price<LENDING_MARKET>(
&mut lending_market,
*bag::borrow(&type_to_index, type_name::get<TEST_SUI>()),
&clock,
mock_pyth::get_price_obj<TEST_SUI>(&prices)
);

// create obligation
let obligation_owner_cap = create_obligation(
&mut lending_market,
test_scenario::ctx(&mut scenario)
);

let coins = coin::mint_for_testing<TEST_USDC>(100 * 1_000_000, test_scenario::ctx(&mut scenario));
let ctokens = deposit_liquidity_and_mint_ctokens<LENDING_MARKET, TEST_USDC>(
&mut lending_market,
*bag::borrow(&type_to_index, type_name::get<TEST_USDC>()),
&clock,
coins,
test_scenario::ctx(&mut scenario)
);
deposit_ctokens_into_obligation<LENDING_MARKET, TEST_USDC>(
&mut lending_market,
*bag::borrow(&type_to_index, type_name::get<TEST_USDC>()),
&obligation_owner_cap,
&clock,
ctokens,
test_scenario::ctx(&mut scenario)
);

let sui = borrow<LENDING_MARKET, TEST_SUI>(
&mut lending_market,
*bag::borrow(&type_to_index, type_name::get<TEST_SUI>()),
&obligation_owner_cap,
&clock,
5 * 1_000_000_000,
test_scenario::ctx(&mut scenario)
);
test_utils::destroy(sui);

// mark obligation as closable
set_obligation_closability_status<LENDING_MARKET>(
&owner_cap,
&mut lending_market,
obligation_id(&obligation_owner_cap),
true
);


let obligation = obligation(&lending_market, obligation_id(&obligation_owner_cap));
assert!(obligation::is_closable(obligation), 0);

let sui_reserve = reserve<LENDING_MARKET, TEST_SUI>(&lending_market);
let old_reserve_borrowed_amount = reserve::borrowed_amount<LENDING_MARKET>(sui_reserve);

let old_deposited_amount = obligation::deposited_ctoken_amount<LENDING_MARKET, TEST_USDC>(obligation);
let old_borrowed_amount = obligation::borrowed_amount<LENDING_MARKET, TEST_SUI>(obligation);

// liquidate the obligation
let sui = coin::mint_for_testing<TEST_SUI>(5 * 1_000_000_000, test_scenario::ctx(&mut scenario));
let (usdc, exemption) = liquidate<LENDING_MARKET, TEST_SUI, TEST_USDC>(
&mut lending_market,
obligation_id(&obligation_owner_cap),
*bag::borrow(&type_to_index, type_name::get<TEST_SUI>()),
*bag::borrow(&type_to_index, type_name::get<TEST_USDC>()),
&clock,
&mut sui,
test_scenario::ctx(&mut scenario)
);

// no bonus was taken
assert!(coin::value(&sui) == 4 * 1_000_000_000, 0);
assert!(coin::value(&usdc) == 10 * 1_000_000, 0);
assert!(exemption.amount == 10 * 1_000_000, 0);

let obligation = obligation(&lending_market, obligation_id(&obligation_owner_cap));

let sui_reserve = reserve<LENDING_MARKET, TEST_SUI>(&lending_market);
let reserve_borrowed_amount = reserve::borrowed_amount<LENDING_MARKET>(sui_reserve);

let deposited_amount = obligation::deposited_ctoken_amount<LENDING_MARKET, TEST_USDC>(obligation);
let borrowed_amount = obligation::borrowed_amount<LENDING_MARKET, TEST_SUI>(obligation);

assert!(reserve_borrowed_amount == sub(old_reserve_borrowed_amount, decimal::from(1_000_000_000)), 0);
assert!(borrowed_amount == sub(old_borrowed_amount, decimal::from(1_000_000_000)), 0);
assert!(deposited_amount == old_deposited_amount - 10 * 1_000_000, 0);

// unclose obligation
set_obligation_closability_status<LENDING_MARKET>(
&owner_cap,
&mut lending_market,
obligation_id(&obligation_owner_cap),
false
);


let obligation = obligation(&lending_market, obligation_id(&obligation_owner_cap));
assert!(!obligation::is_closable(obligation), 0);

test_utils::destroy(sui);
test_utils::destroy(usdc);
test_utils::destroy(obligation_owner_cap);
test_utils::destroy(owner_cap);
test_utils::destroy(lending_market);
test_utils::destroy(clock);
test_utils::destroy(prices);
test_utils::destroy(type_to_index);
test_scenario::end(scenario);
}

#[test_only]
const MILLISECONDS_IN_DAY: u64 = 86_400_000;

Expand Down
Loading
Loading