https://github.com/kinetic-market/public-money-market-contracts
The accrueInterest
function in the Public Money Market relies on a constant value borrowRateMaxMantissa
to prevent excessively high borrow rates. This value, however, is not adaptable to different blockchains and can lead to potential protocol instability on faster chains like Flare Network.
uint internal constant borrowRateMaxMantissa = 0.0005e16;
In the Public Money Market (and Compound v2), the accrueInterest()
is the common function which calculates interest accrued from the last checkpointed block up to the current block and writes new checkpoint to storage.
There are lines which fetch the interest rate (borrowRateMantissa
) as shown below:
/* Calculate the current borrow interest rate */
uint borrowRateMantissa = interestRateModel.getBorrowRate(cashPrior, borrowsPrior, reservesPrior);
require(borrowRateMantissa <= borrowRateMaxMantissa, "borrow rate is absurdly high");
This borrowRateMantissa
is fetched from interestRateModel.getBorrowRate
, but the issue here is the check of borrowRateMaxMantissa
:
require(borrowRateMantissa <= borrowRateMaxMantissa, "borrow rate is absurdly high");
This check is to make sure the borrow rate fall under the configured borrowRateMaxMantissa
uint internal constant borrowRateMaxMantissa = 0.0005e16;
Most of Public Money Market's configuration is mimicking Compound v2; for example, the above borrowRateMaxMantissa uses the same constant value as in Compound.
The purpose of borrowRateMaxMantissa
is to put the protocol in failure mode when absurd utilization causes the borrow rate to become unreasonably high. It is defined as a constant but should really be changed according to the average block time of the chain the protocol is deployed on.
Assuming block time on L1 where Compound v2 is deployed is 12 seconds, we can safely assume, the current borrowRateMaxMantissa
value should be 12 times lower.
However, on faster chains like Flare Network, which have block times of around 3 seconds, the same rate will result in significantly higher annualized rates, potentially leading to an absurdly high borrow rate that could destabilize the protocol.
Another Compound fork on Optimism, Hundred Finance, uses the correct value 0.00004e16(https://optimistic.etherscan.io/address/0x0145BE461a112c60c12c34d5Bc538d10670E99Ab#code#F2#L32).
Example for Ethereum Chain (Block Time = 12 Seconds)
uint256 internal constant borrowRateMaxMantissa = 0.0005e16;
For Ethereum, which has an average block time of 12 seconds, the maximum borrow rate per year can be calculated as follows:
Annualized Borrow Rate for Ethereum = 0.0005 * (365 * 24 * 3600) / 12
= 0.0005% × 31,536,000 seconds per year/12 seconds per block
= 1314% annualized borrow rate
Example for the Flare Network (Block Time ~3 Seconds)
Let’s calculate the annualized borrow rate for the Flare Network with an average block time of 3 seconds:
Annualized Borrow Rate for the Flare Network = 0.0005 * (365 * 24 * 3600) / 3
= 0.0005% × 31,536,000 seconds per year/3 seconds per block
= 5256% annualized borrow rate
This 5256% is obviously much too high. which potentially trigger the "absurdly high" borrow rate check more frequently, causing the protocol to enter a failure state.
Mitigation Decide on a maximum borrow rate that the protocol is comfortable with and adjust the borrowRateMaxMantissa according to the block time of the chain( Flare Network).