Core Contracts

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

Incorrect Mint Amount in RToken Contract

Summary

The mint function mints an insufficient amount of tokens by not accounting for accrued interest (balanceIncrease) when creating new tokens. This violates the protocol's interest accrual mechanism and results in users receiving fewer tokens than entitled.

Vulnerability Details

In the current implementation, when new tokens are minted, the contract calculates two components:

  1. amountToMint: The new deposit amount

  2. balanceIncrease: Accrued interest based on the user's existing balance and index change
    However, the contract only mints amountToMint, ignoring the balanceIncrease:

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

// Current implementation
_mint(onBehalfOf, amountToMint.toUint128());

The correct implementation should be

// Should mint principal + accrued interest
_mint(onBehalfOf, (amountToMint + balanceIncrease).toUint128());

Impact

  • Users lose accrued interest on existing balances

  • Protocol accounting becomes inaccurate

  • Violates ERC-20 token standard expectations

Proof of concept

Scenario 1: Single User Interest Loss

// Initial setup
- Initial index: 1.0 RAY (1e27)
- User deposits: 1000 tokens
- New index: 1.1 RAY
// Calculations
scaledBalance = 1000
oldIndex = 1.0 RAY
newIndex = 1.1 RAY
balanceIncrease = 1000 * (1.1 - 1.0) = 100 tokens
// Current behavior
Minted amount = 1000 tokens
// Correct behavior
Should mint = 1000 + 100 = 1100 tokens
// Result
User loses 100 tokens of accrued interest

Scenario 2: Compound Interest Loss

// Step 1
- Initial deposit: 1000 tokens
- Index increases: 1.0 → 1.1
- Interest earned but not minted: 100 tokens
// Step 2
- Additional deposit: 500 tokens
- Index increases: 1.1 → 1.2
- New interest earned but not minted:
((1000 + 500) * (1.2 - 1.1)) = 150 tokens
// Total loss
- First interest loss: 100 tokens
- Second interest loss: 150 tokens
- Total: 250 tokens

Recommendations

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);
if (amountScaled == 0) revert InvalidAmount();
uint256 scaledBalance = balanceOf(onBehalfOf);
bool isFirstMint = scaledBalance == 0;
uint256 balanceIncrease = 0;
if (_userState[onBehalfOf].index != 0 && _userState[onBehalfOf].index < index) {
balanceIncrease = scaledBalance.rayMul(index) - scaledBalance.rayMul(_userState[onBehalfOf].index);
}
_userState[onBehalfOf].index = index.toUint128();
// FIXED: Include balanceIncrease in mint amount
uint256 totalMintAmount = amountToMint + balanceIncrease;
_mint(onBehalfOf, totalMintAmount.toUint128());
// Emit events with correct amounts
emit Mint(caller, onBehalfOf, totalMintAmount, index);
emit Transfer(address(0), onBehalfOf, totalMintAmount);
return (isFirstMint, totalMintAmount, totalSupply(), amountScaled);
}
Updates

Lead Judging Commences

inallhonesty Lead Judge 7 months ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement
Assigned finding tags:

RToken::mint calculates balanceIncrease (interest accrued since last interaction) but never mints it, causing users to lose earned interest between deposits

The balanceIncrease is the interest that has already accrued on the user's existing scaledBalance since their last interaction. It's not something you mint as new tokens in the _mint function.

inallhonesty Lead Judge 7 months ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement
Assigned finding tags:

RToken::mint calculates balanceIncrease (interest accrued since last interaction) but never mints it, causing users to lose earned interest between deposits

The balanceIncrease is the interest that has already accrued on the user's existing scaledBalance since their last interaction. It's not something you mint as new tokens in the _mint function.

Support

FAQs

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

Give us feedback!