Summary
ReserveLibrary.withdraw
assigns incorrect return values from RToken.burn
, leading to improper scaling of withdrawal amounts and potential miscalculations of user balances.
Details
ReserveLibrary.withdraw
says in the comments that it returns the scaled amount of RTokens burned
* @return amountWithdrawn The amount withdrawn.
* @return amountScaled The scaled amount of RTokens burned.
* @return amountUnderlying The amount of underlying asset transferred.
(
@> uint256 burnedScaledAmount,
uint256 newTotalSupply,
uint256 amountUnderlying
) = IRToken(reserve.reserveRTokenAddress).burn(
recipient,
recipient,
amount,
reserve.liquidityIndex
);
amountWithdrawn = burnedScaledAmount;
Let’s check RToken::burn
function:
* @notice Burns RToken from a user and transfers underlying asset
* @param from The address from which tokens are burned
* @param receiverOfUnderlying The address receiving the underlying asset
* @param amount The amount to burn (in underlying asset units)
* @param index The liquidity index at the time of burning
* @return A tuple containing:
* - uint256: The amount of scaled tokens burned
* - uint256: The new total supply after burning
* - uint256: The amount of underlying asset transferred
*/
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);
}
The problem is, uint256 amountScaled = amount.rayMul(index);
is not used. Instead, we see that in the return statement the amount
that’s passed in params is returned for both burnedScaledAmount
and amountUnderlying
values defined in ReserveLibrary.withdraw
. See ReserveLibrary.withdraw
function:
(
uint256 burnedScaledAmount,
uint256 newTotalSupply,
uint256 amountUnderlying
) = IRToken(reserve.reserveRTokenAddress).burn(
recipient,
recipient,
amount,
reserve.liquidityIndex
);
amountWithdrawn = burnedScaledAmount;
As it is seen, for both burnedScaledAmount
and amountUnderlying
vaues, the amount
from RToken::burn
is passed, which is wrong if we go back all the way to the comment @return amountScaled The scaled amount of RTokens burned.
on ReserveLibrary::withdraw
function
Impact
Incorrect scaling leads to miscalculations.