Relevant GitHub Links
https://github.com/Cyfrin/2025-02-raac/blob/89ccb062e2b175374d40d824263a4c0b601bcb7f/contracts/core/pools/StabilityPool/StabilityPool.sol#L453
Summary
In StabilityPool's liquidateBorrower(), the debt amount used for token transfer is in RAY precision (27 decimals) without conversion to the token's decimal precision, potentially causing failed transfers or incorrect amounts.
Vulnerability Details
The liquidateBorrower() function uses RAY precision values directly for token transfer:
function liquidateBorrower(address userAddress) external onlyManagerOrOwner nonReentrant whenNotPaused {
uint256 userDebt = lendingPool.getUserDebt(userAddress);
uint256 scaledUserDebt = WadRayMath.rayMul(userDebt, lendingPool.getNormalizedDebt());
uint256 crvUSDBalance = crvUSDToken.balanceOf(address(this));
if (crvUSDBalance < scaledUserDebt) revert InsufficientBalance();
bool approveSuccess = crvUSDToken.approve(address(lendingPool), scaledUserDebt);
}
The getUserDebt() returns value in RAY precision due to rayMul:
function getUserDebt(address userAddress) public view returns (uint256) {
return user.scaledDebtBalance.rayMul(reserve.usageIndex);
}
Impact
Liquidations may fail due to amount exceeding token balance (when RAY > token decimals)
If transfer succeeds, incorrect amounts will be transferred (up to factor of 10^9)
System becomes unable to liquidate positions correctly
Tools Used
Manual Review
Recommendations
Convert the debt amount to token decimals before transfer (pseudocode):
function liquidateBorrower(address userAddress) external onlyManagerOrOwner nonReentrant whenNotPaused {
uint256 userDebt = lendingPool.getUserDebt(userAddress);
uint256 scaledUserDebt = WadRayMath.rayMul(userDebt, lendingPool.getNormalizedDebt());
uint256 decimalDiff = 27 - crvUSDToken.decimals();
uint256 debtInTokenDecimals = scaledUserDebt / (10 ** decimalDiff);
uint256 crvUSDBalance = crvUSDToken.balanceOf(address(this));
if (crvUSDBalance < debtInTokenDecimals) revert InsufficientBalance();
bool approveSuccess = crvUSDToken.approve(address(lendingPool), debtInTokenDecimals);
}