Core Contracts

Regnum Aurum Acquisition Corp
HardhatReal World AssetsNFT
77,280 USDC
View results
Submission Details
Severity: medium
Valid

Incorrect Scaling in withdraw flow

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, // from
recipient, // receiverOfUnderlying
amount, // amount
reserve.liquidityIndex // index
);
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(); //* duplicate
if (amount > userBalance) {
amount = userBalance;
}
@> uint256 amountScaled = amount.rayMul(index);
_userState[from].index = index.toUint128(); //* duplicate
_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:

// Burn RToken from the recipient - will send underlying asset to the recipient
(
uint256 burnedScaledAmount,
uint256 newTotalSupply,
uint256 amountUnderlying
) = IRToken(reserve.reserveRTokenAddress).burn(
recipient, // from
recipient, // receiverOfUnderlying
amount, // amount
reserve.liquidityIndex // index
);
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.

Updates

Lead Judging Commences

inallhonesty Lead Judge 4 months ago
Submission Judgement Published
Validated
Assigned finding tags:

RToken::burn returns incorrect underlying asset amount (amount instead of amountScaled), leading to wrong interest rate calculations

inallhonesty Lead Judge 4 months ago
Submission Judgement Published
Validated
Assigned finding tags:

RToken::burn returns incorrect underlying asset amount (amount instead of amountScaled), leading to wrong interest rate calculations

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.