Core Contracts

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

Wrong return value in LendingPool :: withdraw

[L-1] Wrong return value in LendingPool :: withdraw

Description: According to withdraw function netspec in lending pool below

@return amountWithdrawn The amount withdrawn.
* @return amountScaled The scaled amount of RTokens burned.
* @return amountUnderlying The amount of underlying asset transferred

The return value where ment to be different especially amountScaled which was supposed to return the scaled amount

But these return values ended up returning same values , which was not the initial plan of the protocol according to the netspec

in the burn function in Rtoken amount was scaled but not used, and this was supposed to be one of the returning values according to netspec

uint256 amountScaled = amount.rayMul(index);

Again in the burn function in Rtoken provided below this is the return value, it returns the amount twice and even where it was supposed to return scaled amount it returned the amount provided in the parameter

return (amount, totalSupply(), amount);

Then in the withdraw internal function in ReserveLibrary the protocol went ahead to assume that the burn function will return the amount scaled which they did not instead they return only the amount

(uint256 burnedScaledAmount, uint256 newTotalSupply, uint256 amountUnderlying) = IRToken(reserve.reserveRTokenAddress).burn(
recipient, // from
recipient, // receiverOfUnderlying
amount, // amount
reserve.liquidityIndex // index

In conclusion this is the final return value of the withdraw

return (amountUnderlying, burnedScaledAmount, amountUnderlying);

Meaning that if the user amount parameter was to be 100 then the return value will be

return (100, 100, 100);

which was not the intention according to netspec

Impact: This Wrong assumption by the protocol leads to incorrect return values

Proof of Concept:

/**
* @notice Handles withdrawal operation from the reserve.
* @dev Burns RTokens from the user and transfers the underlying asset.
* @param reserve The reserve data.
* @param rateData The reserve rate parameters.
* @param amount The amount to withdraw.
* @param recipient The address receiving the underlying asset.
* @return amountWithdrawn The amount withdrawn.
* @return amountScaled The scaled amount of RTokens burned.
* @return amountUnderlying The amount of underlying asset transferred. */
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();
// Update the reserve interests
updateReserveInterests(reserve, rateData);
// @check mint and burn uses scaled amount, check if user will get less than he deposited
// 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;
// Update the total liquidity and interest rates
updateInterestRatesAndLiquidity(reserve, rateData, 0, amountUnderlying);
emit Withdraw(recipient, amountUnderlying, burnedScaledAmount);
return (amountUnderlying, burnedScaledAmount, amountUnderlying);
}

function burn from Rtoken Contract

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);
}

Recommended Mitigation:
Kindly use the amount that was already scaled in the burn function

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.