Core Contracts

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

Critical Scaling Discrepancy in RToken's Interest-Bearing Token Implementation

Summary

The RToken contract's mint function incorrectly uses unscaled amounts (amountToMint) instead of scaled amounts (amountScaled) when minting tokens, breaking the fundamental ray-based scaling mechanism required for proper interest accrual. This deviation from the intended design causes incorrect token distribution and compromises the entire interest-bearing token system.

Vulnerability Details

  • The token system is designed to operate with scaled balances. The amountScaled variable represents the normalized debt or deposit amount after applying the liquidity index.

  • By calling _mint(onBehalfOf, amountToMint.toUint128()) instead of using amountScaled, the contract effectively bypasses the scaling mechanism.

  • This misrepresentation will lead to incorrect interest accrual and improper accounting of user balances.

  • Over time, balances computed by balanceOf, which rely on the scaling system and this will not match the minted amounts, thereby breaking the protocol’s economic model.

/**
* @notice Mints RToken to a user
* @param caller The address initiating the mint
* @param onBehalfOf The recipient of the minted tokens
* @param amountToMint The amount of tokens to mint (in underlying asset units)
* @param index The liquidity index at the time of minting
* @return A tuple containing:
* - bool: True if this is the first mint for the recipient, false otherwise
* - uint256: The amount of scaled tokens minted
* - uint256: The new total supply after minting
* - uint256: The amount of underlying tokens minted. <----
*/
function mint(
address caller,
address onBehalfOf,
uint256 amountToMint,
uint256 index
) external override onlyReservePool returns (bool, uint256, uint256, uint256) {
uint256 amountScaled = amountToMint.rayDiv(index); // Correctly scales amount
// ...
_mint(onBehalfOf, amountToMint.toUint128()); // @audit Incorrectly uses unscaled amount
return (isFirstMint, amountToMint, totalSupply(), amountScaled);
}

https://github.com/Cyfrin/2025-02-raac/blob/89ccb062e2b175374d40d824263a4c0b601bcb7f/contracts/core/tokens/RToken.sol#L114-L141

Impact

  • Users will receive token amounts that do not properly reflect accrued interest. This discrepancy can lead to over- or under-representation of users' debt or deposit positions, impacting all economic operations such as lending, borrowing, and liquidation.

  • The interest accrual mechanism depends fundamentally on the correct use of scaled amounts; a failure here disrupts the balance calculations for all token holders and can cascade into broader protocol instability.

  • Investors could receive misleading token balances and interest rates, which could result in financial losses or misallocation of collateral.

Tools Used

Manual code review

Recommendations

Replace the use of amountToMint (the raw, unscaled amount) with amountScaled in the call to _mint. The corrected line should be:

function mint(
address caller,
address onBehalfOf,
uint256 amountToMint,
uint256 index
) external override onlyReservePool returns (bool, uint256, uint256, uint256) {
if (amountToMint == 0) {
return (false, 0, 0, 0);
}
uint256 amountScaled = amountToMint.rayDiv(index);
// ...
_mint(onBehalfOf, amountScaled.toUint128()); // Fixed: Use scaled amount
emit Mint(caller, onBehalfOf, amountToMint, index);
return (isFirstMint, amountScaled , totalSupply(), amountToMint);
}
Updates

Lead Judging Commences

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

RToken::mint should mint the amountScaled not the amountToMint

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

RToken::mint should mint the amountScaled not the amountToMint

Support

FAQs

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