Core Contracts

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

`RToken::burn` misses to account interests and burns wrong amount

Summary

In the flow LendingPool::withdraw the function calls RToken::burn. This function contains two issues in handling withdraw of the reserve assets:

  • interest accruals are not calculated and are not properly added to the withdrawal amount

  • the function burns the non-scaled amount instead of the scaled amount

The users will withdraw the wrong reserve asset amount without the correct accrued interest.

Vulnerability Details

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);
@> @audit: missing interest accouting
_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);
}

Impact

The burn function omits to calculate the accrued interest and to add them toamount, causing accrued interest to be ignored. Additionally, it uses amount instead ofamountScaled causing incorrect token burning.
Users will withdraw the wrong reserve assets amount without the accrued interest.

Tools Used

Manual review.

Recommendations

Add interest accrual calculation and useScaled amount for burning.

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);
+ uint256 balanceIncrease = 0;
+ if (_userState[from].index != 0 && _userState[from].index < index) {
+ uint256 depositIndex = ILendingPool(_reservePool).getNormalizedIncome();
+ balanceIncrease = userBalance.rayMul(depositIndex) - userBalance.rayMul(_userState[from].index);
+ amount = amount + balanceIncrease;
+ }
_userState[from].index = index.toUint128();
if(amount > userBalance){
amount = userBalance;
}
uint256 amountScaled = amount.rayMul(index);
- _userState[from].index = index.toUint128();
- _burn(from, amount.toUint128());
+ _burn(from, amountScaled.toUint128());
if (receiverOfUnderlying != address(this)) {
IERC20(_assetAddress).safeTransfer(receiverOfUnderlying, amount);
}
emit Burn(from, receiverOfUnderlying, amount, index);
- return (amount, totalSupply(), amount);
+ return (amountScaled, totalSupply(), amount);
}
Updates

Lead Judging Commences

inallhonesty Lead Judge 3 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

RToken::burn incorrectly burns amount (asset units) instead of amountScaled (token units), breaking token economics and interest-accrual mechanism

inallhonesty Lead Judge 3 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

RToken::burn incorrectly burns amount (asset units) instead of amountScaled (token units), breaking token economics and interest-accrual mechanism

Support

FAQs

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