Core Contracts

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

Incorrect Interest Compounding in `RToken.mint` Causes Permanent Loss of Accrued Yield

Summary

A critical error in the RToken.mint interest accrual logic allows permanent loss of user-earned interest. The vulnerability manifests when users make multiple deposits, as the implementation fails to compound existing balances' accrued interest into new mint operations. This breaks the fundamental promise of interest-bearing tokens by systematically undercounting balances, leading to irreversible fund loss proportional to deposit frequency and interest rate levels.

Vulnerability Details

The vulnerability exists in the interest accrual logic of the RToken.mint function at RToken.sol#L129-L136. When users make subsequent deposits after initial minting, the protocol fails to properly compound accrued interest from their existing balance into the new mint operation.

contract RToken is ERC20, ERC20Permit, IRToken, Ownable {
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();
@> _mint(onBehalfOf, amountToMint.toUint128());
emit Mint(caller, onBehalfOf, amountToMint, index);
return (isFirstMint, amountToMint, totalSupply(), amountScaled);
}
function _update(address from, address to, uint256 amount) internal override {
// Scale amount by normalized income for all operations (mint, burn, transfer)
uint256 scaledAmount = amount.rayDiv(ILendingPool(_reservePool).getNormalizedIncome());
super._update(from, to, scaledAmount);
}
}

The flawed implementation calculates a balanceIncrease representing accrued interest between index updates but neglects to add this amount to the newly minted tokens. This results in the interest being permanently excluded from the user's balance and the protocol's accounting system.

The core issue stems from:

  1. Interest calculation being performed but not incorporated into the minting amount

  2. Scaled balance updates not accounting for accrued interest between user interactions

  3. The _update function's scaling logic being applied to raw amounts without considering pending interest

This breaks the fundamental interest-bearing token mechanic where existing balances should automatically accrue interest that compounds with new deposits. The error causes systematic under-accounting of user balances during subsequent interactions with the lending pool.

Impact

This vulnerability directly causes permanent loss of user funds through failed interest compounding. For example:

  1. Initial Deposit: User deposits 100 RToken when liquidity index = 1.0

  2. Interest Accrual: Index rises to 1.1 (10% interest)

  3. Second Deposit: User deposits another 100 RToken

    • Expected Balance: (100×1.1) + 100 = 210 RToken

    • Actual Balance: 200 RToken (10 RToken lost)

  4. Subsequent Deposit: Index rises to 1.21, user deposits 100 RToken

    • Expected Balance: 210×1.1 + 100 = 331 RToken

    • Actual Balance: 300 RToken (31 RToken total loss)

This creates a compounding loss mechanism where each deposit permanently discards all previously accrued interest. Long-term users making regular deposits could lose >30% of expected returns. The protocol's core value proposition of interest-bearing tokens becomes fundamentally broken, leading to systemic underpayment of all depositors and potential protocol insolvency claims.

Tools Used

Manual Review

Recommendations

Implement the following changes to ensure proper interest compounding:

  1. Core Fix - Add accrued interest to mint amount:

uint256 balanceIncrease = 0;
if (_userState[onBehalfOf].index != 0 && _userState[onBehalfOf].index < index) {
balanceIncrease = scaledBalance.rayMul(index) - scaledBalance.rayMul(_userState[onBehalfOf].index);
+ amountToMint += balanceIncrease;
}
  1. Validation - Add boundary checks:

// Prevent overflow in extreme cases
if (amountToMint > type(uint128).max) revert ExcessiveMintAmount();

This ensures the interest-bearing token implementation aligns with industry standards like Aave's aToken model where interest automatically compounds to existing balances.

Updates

Lead Judging Commences

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

DebtToken::mint miscalculates debt by applying interest twice, inflating borrow amounts and risking premature liquidations

Support

FAQs

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