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

Feat: force redeem from lending #1982

Merged
merged 2 commits into from
Sep 23, 2024
Merged
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
50 changes: 48 additions & 2 deletions pallets/loans/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1137,6 +1137,51 @@ pub mod pallet {
Self::deposit_event(Event::<T>::LiquidationFreeCollateralsUpdated(collaterals));
Ok(().into())
}

/// Force redeems some of account internal supplies in exchange for the underlying asset.
///
/// - `asset_id`: the asset to be redeemed.
/// - `who`: the account to be redeemed.
/// - `redeem_amount`: the amount to be redeemed.
#[pallet::call_index(22)]
#[pallet::weight(T::WeightInfo::redeem())]
#[transactional]
pub fn force_redeem(
origin: OriginFor<T>,
who: T::AccountId,
asset_id: AssetIdOf<T>,
#[pallet::compact] redeem_amount: BalanceOf<T>,
) -> DispatchResultWithPostInfo {
T::UpdateOrigin::ensure_origin(origin)?;
ensure!(!redeem_amount.is_zero(), Error::<T>::InvalidAmount);
Self::do_redeem(&who, asset_id, redeem_amount)?;

Ok(().into())
}

/// Force redeems all of account internal supplies in exchange for the underlying asset.
///
/// - `asset_id`: the asset to be redeemed.
/// - `who`: the account to be redeemed.
#[pallet::call_index(23)]
#[pallet::weight(T::WeightInfo::redeem_all())]
#[transactional]
pub fn force_redeem_all(
origin: OriginFor<T>,
who: T::AccountId,
asset_id: AssetIdOf<T>,
) -> DispatchResultWithPostInfo {
T::UpdateOrigin::ensure_origin(origin)?;
Self::ensure_active_market(asset_id)?;
Self::accrue_interest(asset_id)?;
let exchange_rate = Self::exchange_rate_stored(asset_id)?;
Self::update_earned_stored(&who, asset_id, exchange_rate)?;
let deposits = AccountDeposits::<T>::get(asset_id, &who);
let redeem_amount = Self::do_redeem_voucher(&who, asset_id, deposits.voucher_balance)?;
Self::deposit_event(Event::<T>::Redeemed(who, asset_id, redeem_amount));

Ok(().into())
}
}
}

Expand Down Expand Up @@ -1592,7 +1637,8 @@ impl<T: Config> Pallet<T> {

// The liquidator may not repay more than 50%(close_factor) of the borrower's borrow balance.
let account_borrows = Self::current_borrow_balance(borrower, liquidation_asset_id)?;
let account_borrows_value = Self::get_asset_value(liquidation_asset_id, account_borrows)?;
let account_borrows_value: FixedU128 =
Self::get_asset_value(liquidation_asset_id, account_borrows)?;
let repay_value = Self::get_asset_value(liquidation_asset_id, repay_amount)?;
let effects_borrows_value = if liquidation_asset_id == T::LiquidationFreeAssetId::get() {
let base_position = Self::get_lf_base_position(borrower)?;
Expand Down Expand Up @@ -1671,7 +1717,7 @@ impl<T: Config> Pallet<T> {
// if liquidate_value >= 340282366920938463463.374607431768211455,
// FixedU128::saturating_from_integer(liquidate_value) will overflow, so we use from_inner
// instead of saturating_from_integer, and after calculation use into_inner to get final value.
let collateral_token_price = Self::get_price(collateral_asset_id)?;
let collateral_token_price: FixedU128 = Self::get_price(collateral_asset_id)?;
let real_collateral_underlying_amount = liquidate_value
.checked_div(&collateral_token_price)
.ok_or(ArithmeticError::Underflow)?
Expand Down
39 changes: 39 additions & 0 deletions pallets/loans/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,27 @@
})
}

fn force_redeem_works() {
new_test_ext().execute_with(|| {
assert_ok!(Loans::mint(RuntimeOrigin::signed(ALICE), DOT, unit(100)));
assert_ok!(Loans::force_redeem(
RuntimeOrigin::root(),
ALICE,
DOT,
unit(20)
));

// DOT collateral: deposit - redeem = 100 - 20 = 80
// DOT: cash - deposit + redeem = 1000 - 100 + 20 = 920
assert_eq!(
Loans::exchange_rate(DOT)
.saturating_mul_int(Loans::account_deposits(DOT, ALICE).voucher_balance),
unit(80)
);
assert_eq!(<Test as Config>::Assets::balance(DOT, &ALICE), unit(920),);
})
}

#[test]
fn redeem_fails() {
new_test_ext().execute_with(|| {
Expand Down Expand Up @@ -368,6 +389,24 @@
})
}

#[test]
fn force_redeem_all_works() {
new_test_ext().execute_with(|| {
assert_ok!(Loans::mint(RuntimeOrigin::signed(ALICE), DOT, unit(100)));
assert_ok!(Loans::force_redeem_all(RuntimeOrigin::root(), ALICE, DOT));

// DOT: cash - deposit + redeem = 1000 - 100 + 100 = 1000
// DOT collateral: deposit - redeem = 100 - 100 = 0
assert_eq!(
Loans::exchange_rate(DOT)
.saturating_mul_int(Loans::account_deposits(DOT, ALICE).voucher_balance),
0,
);
assert_eq!(<Test as Config>::Assets::balance(DOT, &ALICE), unit(1000),);
assert!(!AccountDeposits::<Test>::contains_key(DOT, &ALICE))
})
}

#[test]
fn borrow_allowed_works() {
new_test_ext().execute_with(|| {
Expand Down Expand Up @@ -1052,7 +1091,7 @@
}

#[test]
fn reward_calculation_one_palyer_in_multi_markets_works() {

Check warning on line 1094 in pallets/loans/src/tests.rs

View workflow job for this annotation

GitHub Actions / Spell Check with Typos

"palyer" should be "player".
new_test_ext().execute_with(|| {
assert_ok!(Loans::mint(RuntimeOrigin::signed(ALICE), DOT, unit(100)));
assert_ok!(Loans::mint(RuntimeOrigin::signed(ALICE), KSM, unit(100)));
Expand Down
Loading