Skip to content

Commit a294e5d

Browse files
authored
Merge pull request #1982 from parallel-finance/feat/force-redeem
Feat: force redeem from lending
2 parents d56e95b + 8238d52 commit a294e5d

File tree

2 files changed

+87
-2
lines changed

2 files changed

+87
-2
lines changed

pallets/loans/src/lib.rs

Lines changed: 48 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1137,6 +1137,51 @@ pub mod pallet {
11371137
Self::deposit_event(Event::<T>::LiquidationFreeCollateralsUpdated(collaterals));
11381138
Ok(().into())
11391139
}
1140+
1141+
/// Force redeems some of account internal supplies in exchange for the underlying asset.
1142+
///
1143+
/// - `asset_id`: the asset to be redeemed.
1144+
/// - `who`: the account to be redeemed.
1145+
/// - `redeem_amount`: the amount to be redeemed.
1146+
#[pallet::call_index(22)]
1147+
#[pallet::weight(T::WeightInfo::redeem())]
1148+
#[transactional]
1149+
pub fn force_redeem(
1150+
origin: OriginFor<T>,
1151+
who: T::AccountId,
1152+
asset_id: AssetIdOf<T>,
1153+
#[pallet::compact] redeem_amount: BalanceOf<T>,
1154+
) -> DispatchResultWithPostInfo {
1155+
T::UpdateOrigin::ensure_origin(origin)?;
1156+
ensure!(!redeem_amount.is_zero(), Error::<T>::InvalidAmount);
1157+
Self::do_redeem(&who, asset_id, redeem_amount)?;
1158+
1159+
Ok(().into())
1160+
}
1161+
1162+
/// Force redeems all of account internal supplies in exchange for the underlying asset.
1163+
///
1164+
/// - `asset_id`: the asset to be redeemed.
1165+
/// - `who`: the account to be redeemed.
1166+
#[pallet::call_index(23)]
1167+
#[pallet::weight(T::WeightInfo::redeem_all())]
1168+
#[transactional]
1169+
pub fn force_redeem_all(
1170+
origin: OriginFor<T>,
1171+
who: T::AccountId,
1172+
asset_id: AssetIdOf<T>,
1173+
) -> DispatchResultWithPostInfo {
1174+
T::UpdateOrigin::ensure_origin(origin)?;
1175+
Self::ensure_active_market(asset_id)?;
1176+
Self::accrue_interest(asset_id)?;
1177+
let exchange_rate = Self::exchange_rate_stored(asset_id)?;
1178+
Self::update_earned_stored(&who, asset_id, exchange_rate)?;
1179+
let deposits = AccountDeposits::<T>::get(asset_id, &who);
1180+
let redeem_amount = Self::do_redeem_voucher(&who, asset_id, deposits.voucher_balance)?;
1181+
Self::deposit_event(Event::<T>::Redeemed(who, asset_id, redeem_amount));
1182+
1183+
Ok(().into())
1184+
}
11401185
}
11411186
}
11421187

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

15931638
// The liquidator may not repay more than 50%(close_factor) of the borrower's borrow balance.
15941639
let account_borrows = Self::current_borrow_balance(borrower, liquidation_asset_id)?;
1595-
let account_borrows_value = Self::get_asset_value(liquidation_asset_id, account_borrows)?;
1640+
let account_borrows_value: FixedU128 =
1641+
Self::get_asset_value(liquidation_asset_id, account_borrows)?;
15961642
let repay_value = Self::get_asset_value(liquidation_asset_id, repay_amount)?;
15971643
let effects_borrows_value = if liquidation_asset_id == T::LiquidationFreeAssetId::get() {
15981644
let base_position = Self::get_lf_base_position(borrower)?;
@@ -1671,7 +1717,7 @@ impl<T: Config> Pallet<T> {
16711717
// if liquidate_value >= 340282366920938463463.374607431768211455,
16721718
// FixedU128::saturating_from_integer(liquidate_value) will overflow, so we use from_inner
16731719
// instead of saturating_from_integer, and after calculation use into_inner to get final value.
1674-
let collateral_token_price = Self::get_price(collateral_asset_id)?;
1720+
let collateral_token_price: FixedU128 = Self::get_price(collateral_asset_id)?;
16751721
let real_collateral_underlying_amount = liquidate_value
16761722
.checked_div(&collateral_token_price)
16771723
.ok_or(ArithmeticError::Underflow)?

pallets/loans/src/tests.rs

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -272,6 +272,27 @@ fn redeem_works() {
272272
})
273273
}
274274

275+
fn force_redeem_works() {
276+
new_test_ext().execute_with(|| {
277+
assert_ok!(Loans::mint(RuntimeOrigin::signed(ALICE), DOT, unit(100)));
278+
assert_ok!(Loans::force_redeem(
279+
RuntimeOrigin::root(),
280+
ALICE,
281+
DOT,
282+
unit(20)
283+
));
284+
285+
// DOT collateral: deposit - redeem = 100 - 20 = 80
286+
// DOT: cash - deposit + redeem = 1000 - 100 + 20 = 920
287+
assert_eq!(
288+
Loans::exchange_rate(DOT)
289+
.saturating_mul_int(Loans::account_deposits(DOT, ALICE).voucher_balance),
290+
unit(80)
291+
);
292+
assert_eq!(<Test as Config>::Assets::balance(DOT, &ALICE), unit(920),);
293+
})
294+
}
295+
275296
#[test]
276297
fn redeem_fails() {
277298
new_test_ext().execute_with(|| {
@@ -368,6 +389,24 @@ fn redeem_all_works() {
368389
})
369390
}
370391

392+
#[test]
393+
fn force_redeem_all_works() {
394+
new_test_ext().execute_with(|| {
395+
assert_ok!(Loans::mint(RuntimeOrigin::signed(ALICE), DOT, unit(100)));
396+
assert_ok!(Loans::force_redeem_all(RuntimeOrigin::root(), ALICE, DOT));
397+
398+
// DOT: cash - deposit + redeem = 1000 - 100 + 100 = 1000
399+
// DOT collateral: deposit - redeem = 100 - 100 = 0
400+
assert_eq!(
401+
Loans::exchange_rate(DOT)
402+
.saturating_mul_int(Loans::account_deposits(DOT, ALICE).voucher_balance),
403+
0,
404+
);
405+
assert_eq!(<Test as Config>::Assets::balance(DOT, &ALICE), unit(1000),);
406+
assert!(!AccountDeposits::<Test>::contains_key(DOT, &ALICE))
407+
})
408+
}
409+
371410
#[test]
372411
fn borrow_allowed_works() {
373412
new_test_ext().execute_with(|| {

0 commit comments

Comments
 (0)