diff --git a/pallets/loans/src/lib.rs b/pallets/loans/src/lib.rs index 43b888377..0ac3c344c 100644 --- a/pallets/loans/src/lib.rs +++ b/pallets/loans/src/lib.rs @@ -1136,6 +1136,51 @@ pub mod pallet { Self::deposit_event(Event::::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, + who: T::AccountId, + asset_id: AssetIdOf, + #[pallet::compact] redeem_amount: BalanceOf, + ) -> DispatchResultWithPostInfo { + T::UpdateOrigin::ensure_origin(origin)?; + ensure!(!redeem_amount.is_zero(), Error::::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, + who: T::AccountId, + asset_id: AssetIdOf, + ) -> 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::::get(asset_id, &who); + let redeem_amount = Self::do_redeem_voucher(&who, asset_id, deposits.voucher_balance)?; + Self::deposit_event(Event::::Redeemed(who, asset_id, redeem_amount)); + + Ok(().into()) + } } } @@ -1586,7 +1631,8 @@ impl Pallet { // 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)?; @@ -1665,7 +1711,7 @@ impl Pallet { // 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)? diff --git a/pallets/loans/src/tests.rs b/pallets/loans/src/tests.rs index f3951902b..9e9a61808 100644 --- a/pallets/loans/src/tests.rs +++ b/pallets/loans/src/tests.rs @@ -272,6 +272,27 @@ fn redeem_works() { }) } +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!(::Assets::balance(DOT, &ALICE), unit(920),); + }) +} + #[test] fn redeem_fails() { new_test_ext().execute_with(|| { @@ -368,6 +389,24 @@ fn redeem_all_works() { }) } +#[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!(::Assets::balance(DOT, &ALICE), unit(1000),); + assert!(!AccountDeposits::::contains_key(DOT, &ALICE)) + }) +} + #[test] fn borrow_allowed_works() { new_test_ext().execute_with(|| {