The StabilityPool's liquidateBorrower()
function fails to properly liquidate borrowers due to using stale state data when calculating the user's debt. The function retrieves the user's debt before updating the LendingPool's state, leading to an incorrect debt calculation and failed liquidation transaction.
Currently, the state update lendingPool.updateState()
is called after retrieving the user's debt and approving the token transfer, which means the debt calculation is based on outdated state information.
Pre-Conditions:
Some user borrows liquidity from the LendingPool using his NFT as collateral (assuming the LendingPool has enough crvUSD for borrowing)
The users collateral drops in price
Someone initiates the Liquidation process
Borrower fails to repay during the grace period
Borrower can be liquidated now by the StabilityPools liquidateBorrower
function
The issue occurs in the way the liquidateBorrower
function calculates the userDebt amount which is used for the approve :
If we look at the LendingPool finalizeLiquidation
function now we can see that it's using the updated reserve data which is using a higher amount than the previously approved amount by the StabilityPool causing the transaction to revert:
In order to run the test you need to:
Run foundryup
to get the latest version of Foundry
Install hardhat-foundry: npm install --save-dev @nomicfoundation/hardhat-foundry
Import it in your Hardhat config: require("@nomicfoundation/hardhat-foundry");
Make sure you've set the BASE_RPC_URL
in the .env
file or comment out the forking
option in the hardhat config.
Run npx hardhat init-foundry
There is one file in the test folder that will throw an error during compilation so rename the file in test/unit/libraries/ReserveLibraryMock.sol
to => ReserveLibraryMock.sol_broken
so it doesn't get compiled anymore (we don't need it anyways).
Create a new folder test/foundry
Paste the below code into a new test file i.e.: FoundryTest.t.sol
Run the test: forge test --mc FoundryTest -vvvv
Core protocol functionality (liquidations) is broken => borrowers with bad debt can't be liquidated => high risk of protocol insolvency
Manual review
Foundry
The liquidateBorrower()
function should update the LendingPool state before debt calculations, it should also remove the scaledUserDebt
logic because lendingPool.getUserDebt()
already returns the scaled amount:
The finalizeLiquidation()
function should be updated because the updateReserveState()
is done in the StabilityPool:
Additional:
Implement a function in the StabilityPool to manually approve crvUSD (onlyOwner should be allowed to call) for the LendingPool as a safety mechanism
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.