Core Contracts

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

Incorrect Utilization Rate Calculation in RAACMinter leads to incorrect emission rate.

Summary

In the RAACMinter contract, getUtilizationRate() incorrectly uses the lending pool's interest rate index (reserve.usageIndex) instead of actual borrowed amount (reserve.totalUsage) to calculate utilization, leading to incorrect emission rate adjustments.

Vulnerability Details

The issue lies in the implementation of getUtilizationRate() in RAACMinter:

function getUtilizationRate() internal view returns (uint256) {
// @audit totalBorrowed is set to reserve.usageIndex
uint256 totalBorrowed = lendingPool.getNormalizedDebt();
uint256 totalDeposits = stabilityPool.getTotalDeposits();
if (totalDeposits == 0) return 0;
return (totalBorrowed * 100) / totalDeposits; // @audit this will give incorrect rate.
}

The function calls lendingPool.getNormalizedDebt() which returns reserve.usageIndex;

function getNormalizedDebt() external view returns (uint256) {
@> return reserve.usageIndex;
}

On the other hand, getTotalDeposits() in the StabilityPool does correctly return the balance of actual rTokens.

function getTotalDeposits() external view returns (uint256) {
return rToken.balanceOf(address(this)); // @audit this is actual amounts (not an index)
}

This is fundamentally incorrect because usageIndex is an interest rate accumulator in RAY units (27 decimals) that tracks compound interest for borrowers. It starts at 1e27 and increases over time to reflect accrued interest. For the actual borrowed amount is tracked in reserve.totalUsage. So this means this operation return (totalBorrowed * 100) / totalDeposits; is WRONG.

This incorrect calculation flows through the system:

  1. getUtilizationRate() is called by calculateNewEmissionRate()

  2. calculateNewEmissionRate() determines whether to increase/decrease emissions based on this incorrect utilization

  3. updateEmissionRate() uses this calculation to adjust the protocol's emission schedule

  4. tick() uses the emission rate to determine how many RAAC tokens to mint

  5. The tick function is called from StabilityPool when rewards are minted.

The difference is significant:

  • usageIndex: A number around 1e27 that grows with interest (e.g., 1.05e27 after 5% interest)

  • totalUsage: Actual borrowed amount (e.g., 1000e18 tokens)

For example, if there are:

  • 1000 tokens deposited

  • 500 tokens borrowed

  • usageIndex of 1.05e27

Current calculation: (1.05e27 * 100) / 1000e18 = massive number > 100%
Correct calculation should be: (500e18 * 100) / 1000e18 = 50%

PoC

  1. Pool starts with usageIndex = 1e27 (RAY)

  2. User deposits 1000 tokens into StabilityPool

  3. User borrows 100 tokens (10% utilization)

  4. After some time, usageIndex grows to 1.05e27 due to interest

  5. getUtilizationRate() returns massive number due to using 1.05e27 instead of actual borrowed amount

  6. This causes emission rate to increase incorrectly

Impact

  • Incorrect emission rate adjustments due to vastly inflated utilization calculations

  • Protocol emits too many RAAC tokens when utilization appears higher than reality

Tools Used

Manual Review

Recommendations

Modify getUtilizationRate() to use the actual borrowed amount:

function getUtilizationRate() internal view returns (uint256) {
uint256 totalBorrowed = lendingPool.reserve.totalUsage; // Get actual borrowed amount
uint256 totalDeposits = stabilityPool.getTotalDeposits();
if (totalDeposits == 0) return 0;
return (totalBorrowed * 100) / totalDeposits;
}
Updates

Lead Judging Commences

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

RAACMinter::getUtilizationRate incorrectly mixes stability pool deposits with lending pool debt index instead of using proper lending pool metrics

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

RAACMinter::getUtilizationRate incorrectly mixes stability pool deposits with lending pool debt index instead of using proper lending pool metrics

Support

FAQs

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

Give us feedback!