Summary
The withdraw function() contains a vulnerability due to incorrect return parameters from the burn function in the RToken contract. This issue causes inconsistencies in accounting and event emissions, potentially leading to miscalculations in liquidity.
Vulnerability Details
https://github.com/Cyfrin/2025-02-raac/blob/89ccb062e2b175374d40d824263a4c0b601bcb7f/contracts/core/pools/LendingPool/LendingPool.sol#L252
In the Withdraw() of lendingPool contract, it calls the ReserveLibrary.withdraw
(uint256 amountWithdrawn, uint256 amountScaled, uint256 amountUnderlying) = ReserveLibrary.withdraw(
reserve,
rateData,
amount,
msg.sender
);
The ReserveLibrary.withdraw() calls RToken.burn()
https://github.com/Cyfrin/2025-02-raac/blob/89ccb062e2b175374d40d824263a4c0b601bcb7f/contracts/libraries/pools/ReserveLibrary.sol#L365
function withdraw(
ReserveData storage reserve,
ReserveRateData storage rateData,
uint256 amount,
address recipient
) internal returns (uint256 amountWithdrawn, uint256 amountScaled, uint256 amountUnderlying) {
if (amount < 1) revert InvalidAmount();
updateReserveInterests(reserve, rateData);
(uint256 burnedScaledAmount, uint256 newTotalSupply, uint256 amountUnderlying) = IRToken(reserve.reserveRTokenAddress).burn(
recipient,
recipient,
amount,
reserve.liquidityIndex
);
amountWithdrawn = burnedScaledAmount;
updateInterestRatesAndLiquidity(reserve, rateData, 0, amountUnderlying);
emit Withdraw(recipient, amountUnderlying, burnedScaledAmount);
return (amountUnderlying, burnedScaledAmount, amountUnderlying);
}
Here is the burn function in the RToken contract
https://github.com/Cyfrin/2025-02-raac/blob/89ccb062e2b175374d40d824263a4c0b601bcb7f/contracts/core/tokens/RToken.sol#L154C4-L185C6
function burn(
address from,
address receiverOfUnderlying,
uint256 amount,
uint256 index
) external override onlyReservePool returns (uint256, uint256, uint256) {
if (amount == 0) {
return (0, totalSupply(), 0);
}
uint256 userBalance = balanceOf(from);
_userState[from].index = index.toUint128();
if(amount > userBalance){
amount = userBalance;
}
uint256 amountScaled = amount.rayMul(index);
_userState[from].index = index.toUint128();
_burn(from, amount.toUint128());
if (receiverOfUnderlying != address(this)) {
IERC20(_assetAddress).safeTransfer(receiverOfUnderlying, amount);
}
emit Burn(from, receiverOfUnderlying, amount, index);
return (amount, totalSupply(), amount);
}
In the burn(), it incorrectly returns "return (amount, totalSupply(), amount)" instead of "return (amountScaled, totalSupply(), amount)"
This will in turn affect the return values from ReserveLibrary.withdraw() and in turn return values of Withdraw() from lendingpool contract which will lead to inconsistencies
Impact
Tools Used
Manual review
Recommendations
Fix the ordering of the parameters from burn() in RToken contract