setAnnualInterestBips() can be abused to keep a market's reserve ratio at 90%
Lines of code
https://github.com/code-423n4/2023-10-wildcat/blob/main/src/WildcatMarketController.sol#L472-L485
Vulnerability details
Bug Description
If a borrower calls setAnnualInterestBips() to reduce a market's annual interest rate, its reserve ratio will be set to 90% for 2 weeks:
WildcatMarketController.sol#L472-L485
solidity// If borrower is reducing the interest rate, increase the reserve // ratio for the next two weeks. if (annualInterestBips < WildcatMarket(market).annualInterestBips()) { TemporaryReserveRatio storage tmp = temporaryExcessReserveRatio[market]; if (tmp.expiry == 0) { tmp.reserveRatioBips = uint128(WildcatMarket(market).reserveRatioBips()); // Require 90% liquidity coverage for the next 2 weeks WildcatMarket(market).setReserveRatioBips(9000); } tmp.expiry = uint128(block.timestamp + 2 weeks); }
This is meant to give lenders the option to withdraw from the market should they disagree with the decrease in annual interest rate.
However, such an implementation can be abused by a borrower in a market where the reserve ratio above 90%:
- Borrower deploys a market with its reserve ratio at 95%.
- A lender, who agrees to a 95% reserve ratio, deposits into the market.
- Borrower calls
setAnnualInterestBips()to reduceannualInterestBipsby 1.- This causes the market's reserve ratio to be set to 90% for 2 weeks.
- After 2 weeks, the borrower calls
setAnnualInterestBips()and decreasesannualInterestBipsby 1 again. - By repeating steps 3 and 4, the borrower can effectively keep the market's reserve ratio at 90% forever.
In the scenario above, the 5% reduction in reserve ratio works in favor of the borrower since he does not have to keep as much assets in the market. The amount of assets that all lenders can withdraw at any given time will also be 5% less than what it should be.
Note that it is possible for a market to be deployed with a reserve ratio above 90% if the protocol's MaximumReserveRatioBips permits. For example, MaximumReserveRatioBips is set to 100% in current tests:
solidityuint16 constant MaximumReserveRatioBips = 10_000;
Impact
In a market where the reserve ratio is above 90%, a borrower repeatedly call setAnnualInterestBips() every two weeks to keep the reserve ratio at 90%.
This is problematic as a market's reserve ratio is not meant to be adjustable post-deployment, since the borrower and his lenders must agree to a fixed reserve ratio beforehand.
Recommended Mitigation
In setAnnualInterestBips(), consider setting the market's reserve ratio to 90% only if it is lower:
WildcatMarketController.sol#L472-L485
diff// If borrower is reducing the interest rate, increase the reserve // ratio for the next two weeks. - if (annualInterestBips < WildcatMarket(market).annualInterestBips()) { + if (annualInterestBips < WildcatMarket(market).annualInterestBips() && WildcatMarket(market).reserveRatioBips() < 9000) { TemporaryReserveRatio storage tmp = temporaryExcessReserveRatio[market]; if (tmp.expiry == 0) { tmp.reserveRatioBips = uint128(WildcatMarket(market).reserveRatioBips()); // Require 90% liquidity coverage for the next 2 weeks WildcatMarket(market).setReserveRatioBips(9000); } tmp.expiry = uint128(block.timestamp + 2 weeks); }
Assessed type
Other
