Core Contracts

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

EmissionRate will always be wrong, leading to unintended behavior

Summary

The RAACMintercontract is repsonsible for managing the minting and distribution of RAAC reward tokens.

The contract uses a utilizationRateto determine the emissionRate -> which is then used to determine amount of tokens that are to be minted. The amount of tokens that do get minted completely rely on the emissionRateto determine the amount that gets minted.

The issue is that utilizationRateis miscalculated and will always lead to the wrong emissionRatevalue , and unintended amounts of minted tokens.

Vulnerability Details

Before we dive into the issue causing the miscalculation of utilizationRate-> lets understand where it is used, and how it is depended on for the minting of RAAC Tokens:

  • stabilityPoolcalls the RAACMintercontract to update and mint new RAAC tokens and then uses the total balance of the minted RAAC tokens whenever its needed to determine rewards for users:

    function _update() internal {
    _mintRAACRewards();
    }
    /* tick is a function that will mint new RAAC Tokens in the RAACMinter contract */
    function _mintRAACRewards() internal {
    if (address(raacMinter) != address(0)) {
    raacMinter.tick();
    }
    }
  • RaacMinter::tick updates and calculates the emissionRate: TheemissionRateis calculated using the new utilizationRate.

    /**
    * @dev Triggers the minting process and updates the emission rate if the interval has passed
    */
    function tick() external nonReentrant whenNotPaused {
    if (emissionUpdateInterval == 0 || block.timestamp >= lastEmissionUpdateTimestamp + emissionUpdateInterval) {
    updateEmissionRate();
    }
    /* 2
    /* calculates new emission rate BASED off the value of utilizationRate */
    function calculateNewEmissionRate() internal view returns (uint256) {
    uint256 utilizationRate = getUtilizationRate();
    uint256 adjustment = (emissionRate * adjustmentFactor) / 100;
    -> if (utilizationRate > utilizationTarget) {
    uint256 increasedRate = emissionRate + adjustment;
    uint256 maxRate = increasedRate > benchmarkRate ? increasedRate : benchmarkRate;
    return maxRate < maxEmissionRate ? maxRate : maxEmissionRate;
    -> } else if (utilizationRate < utilizationTarget) {
    uint256 decreasedRate = emissionRate > adjustment ? emissionRate - adjustment : 0;
    uint256 minRate = decreasedRate < benchmarkRate ? decreasedRate : benchmarkRate;
    return minRate > minEmissionRate ? minRate : minEmissionRate;
    }
    return emissionRate;
  • RAACMinter::tickthen uses the emission rate to calculate amoountToMintand mints that amount of new RAAC Tokens

/* Function tick */
-> uint256 amountToMint = emissionRate * blocksSinceLastUpdate;
if (amountToMint > 0) {
excessTokens += amountToMint;
lastUpdateBlock = currentBlock;
-> raacToken.mint(address(stabilityPool), amountToMint);
emit RAACMinted(amountToMint);
}
}
}
  • The new tokens are minted to the StabilityPooland the total balance of RAACTokens are used to determine how many reward tokens a user should receive and then, StabilityPooltransfers the rewards to the user.

**** utilizationRatemiscalculation. :

  • The utilizationRateis calculated by comparing the totalBorrowedamount (total rTokens issued by LendingPool) and the totalDeposits(total amount of rTokens deposited into the StabilityPool).

  • totalBorrowed-> is going to be a greater amount than totalDepositsbecause not all users that receive rTOkens from the lendingPool will go and deposit them into the StabilityPool.

  • This will always lead to an inflated amount that will not be the intended value for utilizationRatethat the protocol expects.

    /**
    * @dev Calculates the current system utilization rate
    -> * @return The utilization rate as a percentage (0-100)
    */
    function getUtilizationRate() internal view returns (uint256) {
    uint256 totalBorrowed = lendingPool.getNormalizedDebt();
    uint256 totalDeposits = stabilityPool.getTotalDeposits();
    if (totalDeposits == 0) return 0;
    -> return (totalBorrowed * 100) / totalDeposits;
  • The protocol is expecting a calculated value between 0-100, representing a utilizationRate percentage.

But this will never be achieved, for example:

  • totalBorrowed-> will most likely always be larger than totalDeposits

  • totalBorrowed. =. 300

    • totalDeposits. = 150

  • The utilizationRateshould be 50 , representing a 50% utilizationRate

  • But the return value will be 200

  • 300 * 100 / 150 = 200

The wrong value for utilzation rate is calculated

Impact

Becasue of the wrong utilizationRate-> only 1 scenario will be used for calculating the emissionRate

function calculateNewEmissionRate() internal view returns (uint256) {
uint256 utilizationRate = getUtilizationRate();
uint256 adjustment = (emissionRate * adjustmentFactor) / 100;
-> if (utilizationRate > utilizationTarget) {
uint256 increasedRate = emissionRate + adjustment;
uint256 maxRate = increasedRate > benchmarkRate ? increasedRate : benchmarkRate;
return maxRate < maxEmissionRate ? maxRate : maxEmissionRate;
} else if (utilizationRate < utilizationTarget) {
uint256 decreasedRate = emissionRate > adjustment ? emissionRate - adjustment : 0;
uint256 minRate = decreasedRate < benchmarkRate ? decreasedRate : benchmarkRate;
return minRate > minEmissionRate ? minRate : minEmissionRate;
}
return emissionRate;

The function has 2 seperate blocks of logic to handle times when the utilzationRateis below 70 (70%) and when its above 70 (70%).

But becasue of the wrong calculation, as seen above, the value of utilzationRatewill most likely always be greater than utilizationTarget(70).

As a result, only 1 block of logic will be executed, which increases the rate until the maxRate is reached. And then, it will always stay at maxRate.

This will lead to the unintended emission amounts and the unintended minting amount of new RAACTokens. This is then used to transfer RAAC token rewards to users, which can be more rewards than it should be. Leading to users getting more rewards or unintended amounts of rewards.

Tools Used

Manual Review

Recommendations

Change the utilizationRatecalculation, taking into consideration that totalBorrowedwill be greater than totalDeposits:

In the scenario above, with this change, the utilizationRatewill be the expected and correct 50 (50%)

150 * 100 / 300 = 50

(totalDeposits * 100) / totalBorrowed
Updates

Lead Judging Commences

inallhonesty Lead Judge about 1 month ago
Submission Judgement Published
Validated
Assigned finding tags:

RAACMinter::getUtilizationRate allows values over 100% despite function comment specifying 0-100 range, causing emission rate to permanently reach maximum when borrows exceed deposits

inallhonesty Lead Judge about 1 month ago
Submission Judgement Published
Validated
Assigned finding tags:

RAACMinter::getUtilizationRate allows values over 100% despite function comment specifying 0-100 range, causing emission rate to permanently reach maximum when borrows exceed deposits

Support

FAQs

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