Core Contracts

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

`RToken::burn` function has incorrect scaling logic and mismatched return values

Summary

The burn function executes but contains two key issues:

  1. Incorrect amount scaling when burning tokens

  2. Return values don't match parameter order

Vulnerability Details

[](https://github.com/Cyfrin/2025-02-raac/blob/89ccb062e2b175374d40d824263a4c0b601bcb7f/contracts/core/tokens/RToken.sol#L164C9-L184C48)

RToken::burn burns tokens based on the liquidityIndex to account for accrued interest. As interest accrues (liquidityIndex > 1), fewer rTokens should be burned to receive the same underlying amount, reflecting their increased value.

The implementation has three issues:

  1. Incorrect scaling:

uint256 amountScaled = amount.rayMul(index); // wrong

Using rayMul instead of rayDiv inflates the burn amount.

Example:

  • Initial: 50 underlying = 50 rTokens (index = 1.0)

  • After interest: 50 underlying should = 45.45 rTokens (index = 1.1)

  • Current code burns 55 rTokens (50 * 1.1)

The current implementation:

  1. Uses rayMul instead of rayDiv, scaling up instead of down

  2. Burns raw amount instead of scaled amount

  3. Returns wrong scaled value

  4. Burns unscaled amount:

_burn(from, amount.toUint128()); // Burns raw amount instead of scaled
  1. Returns amount in first parameter when it should be amountScaled

return (amount, totalSupply(), amount);

The order is wrong, we're returning the unscaled amount for the first parameter which is supposed to be the scaled tokens burned per natspec.

* @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
*/

Impact

  • Incorrect burn amounts lead to value loss for users when interest has accrued

  • Inaccurate return values break protocols integrating with burn function

Tools Used

Foundry

Recommendations

function burn(
......
- uint256 amountScaled = amount.rayMul(index);
+ uint256 amountScaled = amount.rayDiv(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 about 1 month 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 calculates amountScaled using rayMul instead of rayDiv, causing incorrect token burn amounts and breaking the interest accrual mechanism

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

inallhonesty Lead Judge about 1 month 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 calculates amountScaled using rayMul instead of rayDiv, causing incorrect token burn amounts and breaking the interest accrual mechanism

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.