https://github.com/kinetic-market/public-money-market-contracts
accrueInterest
is expected to revert when the rate is higher than the maximum allowed rate, which is possible since the utilization can be more than 1Borrow rate calculation can cause accrueInterest() to revert
accrueInterest
is expected to revert when the rate is higher than the maximum allowed rate, which is possible since the utilization can be more than 1
accrueInterest
is an essential function to keep updated of the global cToken
interest payment from the borrower to the depositor. The function is called anytime there is a deposit/liquidate/borrow/repay/withdraw.
The function is set to revert when the borrowRateMantissa
is bigger than the borrowRateMaxMantissa
.
require(borrowRateMantissa <= borrowRateMaxMantissa, "borrow rate is absurdly high");
.
With proper configuration, this check should be fine since the real borrowRate
should be calibrated to be within the maxRate
. However, since the utilization could actually be bigger than 1 (when reserve is bigger than cash, the actual rate could be bigger than the expected unreachable max rate:
Example: base: 1% multiplierPerTimestamp: 5% APR jumpMultiplier: 50% APR klink: 50% We could reasonably assume the max APR is 1% + 5% * 0.5 + 50% * (1-0.5) = 28.5%
However when reserve is more than cash it would cause the utilization be bigger than 1, let'say utilization is 101%: such that the actual rate would be:
1% + 5% * 0.5 + 50% * (1.01 - 0.5) = 29%
This would cause the accurueInterest
to revert.
All deposit/withdraw/borrow/repay function would fail and severe brick the operation of the protocol and lock user funds. Interest accrual cannot work which impact both borrower for timely exit as well as deposit to collect their interest.
function accrueInterest() public returns (uint) {
/* Remember the initial block timestamp */
uint currentBlockTimestamp = getBlockTimestamp();
uint accrualBlockTimestampPrior = accrualBlockTimestamp;
/* Short-circuit accumulating 0 interest */
if (accrualBlockTimestampPrior == currentBlockTimestamp) {
return uint(Error.NO_ERROR);
}
/* Read the previous values out of storage */
uint cashPrior = getCashPrior();
uint borrowsPrior = totalBorrows;
uint reservesPrior = totalReserves;
uint borrowIndexPrior = borrowIndex;
/* Calculate the current borrow interest rate */
uint borrowRateMantissa = interestRateModel.getBorrowRate(cashPrior, borrowsPrior, reservesPrior);
require(borrowRateMantissa <= borrowRateMaxMantissa, "borrow rate is absurdly high");
/* Calculate the number of blocks elapsed since the last accrual */
(MathError mathErr, uint blockDelta) = subUInt(currentBlockTimestamp, accrualBlockTimestampPrior);
require(mathErr == MathError.NO_ERROR, "could not calculate block delta");
/*
* Calculate the interest accumulated into borrows and reserves and the new index:
* simpleInterestFactor = borrowRate * blockDelta
* interestAccumulated = simpleInterestFactor * totalBorrows
* totalBorrowsNew = interestAccumulated + totalBorrows
* totalReservesNew = interestAccumulated * reserveFactor + totalReserves
* borrowIndexNew = simpleInterestFactor * borrowIndex + borrowIndex
*/
Exp memory simpleInterestFactor;
uint interestAccumulated;
uint totalBorrowsNew;
uint totalReservesNew;
uint borrowIndexNew;
(mathErr, simpleInterestFactor) = mulScalar(Exp({mantissa: borrowRateMantissa}), blockDelta);
if (mathErr != MathError.NO_ERROR) {
return failOpaque(Error.MATH_ERROR, FailureInfo.ACCRUE_INTEREST_SIMPLE_INTEREST_FACTOR_CALCULATION_FAILED, uint(mathErr));
}
(mathErr, interestAccumulated) = mulScalarTruncate(simpleInterestFactor, borrowsPrior);
if (mathErr != MathError.NO_ERROR) {
return failOpaque(Error.MATH_ERROR, FailureInfo.ACCRUE_INTEREST_ACCUMULATED_INTEREST_CALCULATION_FAILED, uint(mathErr));
}
(mathErr, totalBorrowsNew) = addUInt(interestAccumulated, borrowsPrior);
if (mathErr != MathError.NO_ERROR) {
return failOpaque(Error.MATH_ERROR, FailureInfo.ACCRUE_INTEREST_NEW_TOTAL_BORROWS_CALCULATION_FAILED, uint(mathErr));
}
(mathErr, totalReservesNew) = mulScalarTruncateAddUInt(Exp({mantissa: reserveFactorMantissa}), interestAccumulated, reservesPrior);
if (mathErr != MathError.NO_ERROR) {
return failOpaque(Error.MATH_ERROR, FailureInfo.ACCRUE_INTEREST_NEW_TOTAL_RESERVES_CALCULATION_FAILED, uint(mathErr));
}
(mathErr, borrowIndexNew) = mulScalarTruncateAddUInt(simpleInterestFactor, borrowIndexPrior, borrowIndexPrior);
if (mathErr != MathError.NO_ERROR) {
return failOpaque(Error.MATH_ERROR, FailureInfo.ACCRUE_INTEREST_NEW_BORROW_INDEX_CALCULATION_FAILED, uint(mathErr));
}
/////////////////////////
// EFFECTS & INTERACTIONS
// (No safe failures beyond this point)
/* We write the previously calculated values into storage */
accrualBlockTimestamp = currentBlockTimestamp;
borrowIndex = borrowIndexNew;
totalBorrows = totalBorrowsNew;
totalReserves = totalReservesNew;
/* We emit an AccrueInterest event */
emit AccrueInterest(cashPrior, interestAccumulated, borrowIndexNew, totalBorrowsNew);
return uint(Error.NO_ERROR);
}