Skip to content

Commit

Permalink
WIP: allow redeems to use reserve cash
Browse files Browse the repository at this point in the history
  • Loading branch information
daniel-savu committed Jan 20, 2023
1 parent 3c08fd4 commit 1f5ad9d
Show file tree
Hide file tree
Showing 3 changed files with 24 additions and 20 deletions.
8 changes: 6 additions & 2 deletions crates/loans/src/interest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -129,8 +129,9 @@ impl<T: Config> Pallet<T> {
}

/// Calculate the borrowing utilization ratio of the specified market
///
/// utilizationRatio = totalBorrows / (totalCash + totalBorrows − totalReserves)
/// `utilization_ratio = borrows / (cash + borrows - reserve)`
/// Since the market can reach a state where `cash == 0 && borrows == reserve`,
/// this rate can be infinitely large.
pub(crate) fn calc_utilization_ratio(
cash: BalanceOf<T>,
borrows: BalanceOf<T>,
Expand All @@ -145,6 +146,9 @@ impl<T: Config> Pallet<T> {
.and_then(|r| r.checked_sub(reserves))
.ok_or(ArithmeticError::Overflow)?;

if total.is_zero() {
return Err(DispatchError::Arithmetic(ArithmeticError::DivisionByZero));
}
Ok(Ratio::from_rational(borrows, total))
}

Expand Down
17 changes: 9 additions & 8 deletions crates/loans/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1195,13 +1195,12 @@ pub mod pallet {
Ok(().into())
}

/// Add reserves by transferring from payer.
/// TODO: This extrinsic currently does nothing useful. See the TODO comment
/// of the `ensure_enough_cash` function for more details. Based on that
/// TODO, decide whether this extrinsic should be kept.
/// Add reserves by transferring from payer. This is useful because the reserve balance can be used as
/// cash for redeeming. In case the market reaches 100% utilization, the protocol can deposit
/// more cash to the reserve, to allow users to exit (redeem) their lend token positions.
/// The reserve cash cannot be used to borrowing.
///
/// May only be called from `T::ReserveOrigin`.
///
/// - `payer`: the payer account.
/// - `asset_id`: the assets to be added.
/// - `add_amount`: the amount to be added.
Expand Down Expand Up @@ -1454,7 +1453,9 @@ impl<T: Config> Pallet<T> {
// Ensure there is enough cash in the market
let exchange_rate = Self::exchange_rate_stored(asset_id)?;
let redeem_amount = Self::calc_underlying_amount(voucher_amount, exchange_rate)?;
Self::ensure_enough_cash(asset_id, redeem_amount)?;
if Self::get_total_cash(asset_id) < redeem_amount {
return Err(Error::<T>::InsufficientCash.into());
}

// TODO: Only free lend tokens are redeemable. Replace logic below with this:
// if voucher_amount > Self::free_lend_tokens(asset_id, redeemer)?.amount() {
Expand Down Expand Up @@ -1511,7 +1512,7 @@ impl<T: Config> Pallet<T> {
/// Borrower shouldn't borrow more than their total collateral value allows
fn borrow_allowed(asset_id: AssetIdOf<T>, borrower: &T::AccountId, borrow_amount: BalanceOf<T>) -> DispatchResult {
Self::ensure_under_borrow_cap(asset_id, borrow_amount)?;
Self::ensure_enough_cash(asset_id, borrow_amount)?;
Self::ensure_borrow_cash(asset_id, borrow_amount)?;
let borrow_value = Self::get_asset_value(asset_id, borrow_amount)?;
Self::ensure_liquidity(borrower, borrow_value)?;

Expand Down Expand Up @@ -1833,7 +1834,7 @@ impl<T: Config> Pallet<T> {
/// https://github.com/compound-finance/compound-protocol/blob/a3214f67b73310d547e00fc578e8355911c9d376/contracts/CToken.sol#L518
/// - but getCashPrior is the entire balance of the contract:
/// https://github.com/compound-finance/compound-protocol/blob/a3214f67b73310d547e00fc578e8355911c9d376/contracts/CToken.sol#L1125
fn ensure_enough_cash(asset_id: AssetIdOf<T>, amount: BalanceOf<T>) -> DispatchResult {
fn ensure_borrow_cash(asset_id: AssetIdOf<T>, amount: BalanceOf<T>) -> DispatchResult {
let reducible_cash = Self::get_total_cash(asset_id)
.checked_sub(Self::total_reserves(asset_id))
.ok_or(ArithmeticError::Underflow)?;
Expand Down
19 changes: 9 additions & 10 deletions crates/loans/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -362,7 +362,7 @@ fn redeem_fails_when_insufficient_liquidity() {
}

#[test]
fn redeem_fails_when_would_use_reserved_balanace() {
fn redeem_succeds_when_would_use_reserved_balanace() {
new_test_ext().execute_with(|| {
// Prepare: Bob Deposit 200 DOT
assert_ok!(Loans::mint(RuntimeOrigin::signed(BOB), DOT, 200));
Expand All @@ -375,10 +375,9 @@ fn redeem_fails_when_would_use_reserved_balanace() {
assert_ok!(Loans::borrow(RuntimeOrigin::signed(ALICE), DOT, 50));
assert_ok!(Loans::add_reserves(RuntimeOrigin::root(), ALICE, DOT, 50));

assert_noop!(
Loans::redeem(RuntimeOrigin::signed(BOB), DOT, 151),
Error::<Test>::InsufficientCash
);
assert_ok!(Loans::redeem(RuntimeOrigin::signed(BOB), DOT, 151));
let utilization_ratio = Loans::utilization_ratio(DOT);
println!("utilization: {:?}", utilization_ratio);
})
}

Expand Down Expand Up @@ -956,7 +955,7 @@ fn calc_collateral_amount_works() {
}

#[test]
fn ensure_enough_cash_works() {
fn ensure_borrow_cash_works() {
new_test_ext().execute_with(|| {
assert_ok!(Tokens::set_balance(
RuntimeOrigin::root(),
Expand All @@ -965,17 +964,17 @@ fn ensure_enough_cash_works() {
unit(1000),
0
));
assert_ok!(Loans::ensure_enough_cash(KSM, unit(1000)));
assert_ok!(Loans::ensure_borrow_cash(KSM, unit(1000)));
TotalReserves::<Test>::insert(KSM, unit(10));
assert_noop!(
Loans::ensure_enough_cash(KSM, unit(1000)),
Loans::ensure_borrow_cash(KSM, unit(1000)),
Error::<Test>::InsufficientCash,
);
assert_ok!(Loans::ensure_enough_cash(KSM, unit(990)));
assert_ok!(Loans::ensure_borrow_cash(KSM, unit(990)));
// Borrows don't count as cash
TotalBorrows::<Test>::insert(KSM, unit(20));
assert_noop!(
Loans::ensure_enough_cash(KSM, unit(1000)),
Loans::ensure_borrow_cash(KSM, unit(1000)),
Error::<Test>::InsufficientCash
);
})
Expand Down

0 comments on commit 1f5ad9d

Please sign in to comment.