When a user interacts with the protocol (deposit, withdraw, borrow, repay) or he is getting liquidated the internal ReserveLibrary::updateInterestRatesAndLiquidity() is called to update the interest rates and the utilization rate:
However this function can cause borrows or repayments to revert.
Notice that reserve.totalLiquidity is updated everytime some tokens are deposited or withdrawn from the protocol. However this calculation doesn't account about the liquidity + earned yield in the Curve vault (which means can be withdrawn more than deposited) or when users are repaying their debt, because they will pay back the borrowed amount + any accrued interest on it. Thus liquidityTaken will be greater than reserve.totalLiquidity and the call will revert with InsufficientLiquidity error.
Consider the following scenario:
Case 1:
Lender deposits 1000 tokens, -> totalLiquidity += 1000 (80% will be allocated to the Curve vault) (20% to RToken)
The locked funds in the Curve vault earn yield over time (let's say 1 token)
Bob deposits collateral worth 2000 tokens
Bob wants to borrow some tokens, now if the selected amount is more than RToken's crvUSD reserves, the internal LendingPool::_ensureLiquidity() will be triggered to provide the required amount from the Curve vault
So let's say Bob wants to borrow exactly 1001 tokens (800 + 1 (yield) from vault + 200 in RToken)
The protocol should be able to lend this amount because it will be available
But totalLiquidity will be 1000 and it will try to take out 1001 and it will revert
Case 2:
Lender provides 1000 liquidity, -> totalLiquidity += 1000
Bob deposits collateral worth 2000 tokens
Bob borrows 1000 tokens
After some time Bob decides to return them, but he will repay more due to accrued interest
Let's say that his has 1 token interest accured on his debt, that means he must repay 1001
In updateInterestRatesAndLiquidity it will try to subtract totalLiquidity (1000) and liquidityTaken (1001) and will revert
Here is a coded POC, demonstrating case 1 (i'm using Foundry for tests)
Install Foundry
Run forge init --force in the terminal
Paste the following file in the test folder and run forge test --mt testX
Note: in order this test to be sucessful, there must be 3 fixes applied of issues reported in separate reports, one of them is explained and applied in the test. (that's why 800 tokens are transfered directly to the lending pool and that's why Bob borrows 1801 tokens, but not as described in the textual POC)
The other ones are shown below in LendingPool::_withdrawFromVault:
Two impacts:
DoS on borrows - Medium
Revert on repayments, which means borrowers will be liquidated and their collateral will be seized - High
Overall: High
Manual Review
It's hard to give straightforward recommendation, but definitelty the logic of tracking liquidity should be refactored.
The contest is live. Earn rewards by submitting a finding.
This is your time to appeal against judgements on your submissions.
Appeals are being carefully reviewed by our judges.