The liquidation process in StabilityPool::liquidateBorrower incorrectly assumes that the reserve asset will always be crvUSD. However, LendingPool::finalizeLiquidation transfers the reserve asset from the Stability Pool to the reserve's RToken contract, meaning the reserve asset could be any stablecoin, including crvUSD but with a different contract address. If the reserve asset is not recognized as the expected crvUSD contract, the Stability Pool will incorrectly check its crvUSD balance, potentially causing the liquidation to revert and preventing borrowers from being liquidated.
The issue arises in the following code snippet from StabilityPool::liquidateBorrower:
This check assumes that the reserve asset is always the crvUSD token contract that was initially deployed. However, LendingPool::finalizeLiquidation transfers the reserve asset using:
Here, reserve.reserveAssetAddress can be any stablecoin, including crvUSD with a different contract address. If the Stability Pool only checks for a specific crvUSD address rather than dynamically verifying the reserve asset, then any liquidation process involving a different crvUSD deployment or another stablecoin will fail.
If the reserve asset is not crvUSD, the liquidation will fail.The Stability Pool will check its crvUSD balance instead of the actual reserve asset balance, leading to an incorrect InsufficientBalance error. Even if the reserve asset is crvUSD, a different contract address could cause failure.
If the Lending Pool uses a different crvUSD contract instance than what the Stability Pool expects, the liquidation will not recognize the correct balance.
Project intends to reuse the same Lending Pool for other stablecoins
As confirmed by the developers in a private thread:
"We hope to be able to reuse the exact same contract for most other ERC20s (those with custom logic would draw a modified Lending Pool contract to do that, but all the 'classic' ERC20s should fit in the pool)."
"So for the pool, still a crvUSD at launch yes, but the same pool is supposed to be reused for other stables."
This confirms that the same Lending Pool may be used for different stablecoins, and the Stability Pool must support any reserve asset dynamically.
LendingPool constructor allows a flexible reserve asset
The LendingPool::constructor natspec contains:
This implies that the reserve asset can be another token, including a different crvUSD deployment.
If the same Stability Pool is reused, and it only checks crvUSD by a hardcoded contract reference, then any future stablecoin-based reserves—including a differently deployed crvUSD—will not be liquidatable, leading to stuck positions and bad debt accumulation.
Proof Of Code (POC)
This test was run in protocols-test.js in the "StabilityPool" describe block
Impact
Failed Liquidations: If the reserve asset is not crvUSD or a different crvUSD contract address is used, the Stability Pool will incorrectly check its balance, causing liquidations to revert.
Future Incompatibility: The project plans to support other stablecoins in the same pool, but this bug makes the liquidation mechanism unsuitable for multi-asset reserves.
Tools Used
Manual Review
Hardhat
Recommendations
Dynamic Reserve Asset Retrieval
Instead of assuming crvUSD, the Stability Pool should dynamically check the actual reserve asset from the Lending Pool.
Add a getter function in LendingPool:
Update StabilityPool::liquidateBorrower to check the correct reserve asset:
Replace the hardcoded crvUSD balance check with the correct asset balance:
This fix ensures that:
The liquidation process works correctly, regardless of the reserve asset.
Future stablecoins or different crvUSD deployments are properly supported.
The protocol remains resilient and adaptable to future asset changes.
By implementing this fix, the protocol can ensure seamless liquidation across multiple stablecoins, preventing bad debt accumulation and ensuring the long-term viability of the Lending Pool.
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.