Summary
The StabilityPool contract's liquidateBorrower
function lacks caps on liquidation sizes, allowing managers to liquidate arbitrarily large positions which could destabilize the protocol and drain liquidity.
Vulnerability Details
The liquidateBorrower
function has no maximum limits on liquidation sizes:
function liquidateBorrower(
address userAddress
) external onlyManagerOrOwner nonReentrant whenNotPaused {
_update();
uint256 userDebt = lendingPool.getUserDebt(userAddress);
uint256 scaledUserDebt = WadRayMath.rayMul(
userDebt,
lendingPool.getNormalizedDebt()
);
if (crvUSDBalance < scaledUserDebt) revert InsufficientBalance();
lendingPool.finalizeLiquidation(userAddress);
}
Impact
This could result in:
-
Protocol Destabilization:
Draining of stability pool reserves
Market panic from large liquidations
Systemic risk to the protocol
-
Financial Impact:
Tools Used
Manual code review
Recommendations
Implement liquidation caps and limits
contract StabilityPool {
+ uint256 public constant MAX_LIQUIDATION_SIZE = 1_000_000e18;
+ uint256 public constant DAILY_LIQUIDATION_LIMIT = 5_000_000e18;
+ mapping(uint256 => uint256) public dailyLiquidationVolume;
function liquidateBorrower(address userAddress) external onlyManagerOrOwner {
uint256 scaledUserDebt = WadRayMath.rayMul(
userDebt,
lendingPool.getNormalizedDebt()
);
+ require(scaledUserDebt <= MAX_LIQUIDATION_SIZE, "Exceeds max liquidation");
+ uint256 today = block.timestamp / 1 days;
+ uint256 newDailyTotal = dailyLiquidationVolume[today] + scaledUserDebt;
+ require(newDailyTotal <= DAILY_LIQUIDATION_LIMIT, "Exceeds daily limit");
+ dailyLiquidationVolume[today] = newDailyTotal;
// ... rest of liquidation logic
}
}