Core Contracts

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

Incorrect Reward Calculation in `StabilityPool` due to Decimal Mismatch

Summary

The calculateRaacRewards function in StabilityPool.sol uses mismatched token decimals in its reward calculation formula. The function uses userDeposits (in RToken decimals) and totalDeposits (in deToken decimals) without normalizing their decimal places, leading to significantly incorrect reward calculations when the tokens have different decimal precisions.

Vulnerability Details

The vulnerability exists in the calculateRaacRewards function of the StabilityPool contract:

function calculateRaacRewards(address user) public view returns (uint256) {
uint256 userDeposit = userDeposits[user];
uint256 totalDeposits = deToken.totalSupply();
uint256 totalRewards = raacToken.balanceOf(address(this));
if (totalDeposits < 1e6) return 0;
>> return (totalRewards * userDeposit) / totalDeposits;
}

The core issue arises from using two values with potentially different decimal places in a division operation:

  1. userDeposits[user]: Tracks deposits in RToken decimals

  2. deToken.totalSupply(): Returns total deposits in deToken decimals

The contract explicitly declares support for different decimal places between tokens:

// Allow to make rToken / deToken decimals flexible
uint8 public rTokenDecimals;
uint8 public deTokenDecimals;

This is further evidenced by decimal normalization in other functions like calculateDeCRVUSDAmount:

function calculateDeCRVUSDAmount(uint256 rcrvUSDAmount) public view returns (uint256) {
uint256 scalingFactor = 10 ** (18 + deTokenDecimals - rTokenDecimals);
return (rcrvUSDAmount * scalingFactor) / getExchangeRate();
}

However, this critical decimal normalization is missing in the rewards calculation, despite the contract being explicitly designed to handle tokens with different decimals.

For example, if:

  • RToken has 18 decimals

  • deToken has 6 decimals

  • User deposits 100 RTokens (100e18)

  • Total deposits are 1000 deTokens (1000e6)

  • Available rewards are 200 RAAC (200e18)

The calculation becomes:

rewards = 200e18 * 100e18 / 1000e6 = 200e29

This result is drastically higher than intended because the division uses incompatible decimal scales. The user deposit should first be converted to deToken representation for correct calculation.

Impact

  • Severe miscalculation of user rewards leading to incorrect distributions or dos on withdrawals as the result of rewards will always be higher that the contact RAAC balance and thus a revert when try to withdraw since withdraw do an automatic rewards claim

  • Users could receive significantly more or fewer rewards than intended

Tools Used

  • Foundry

  • Manual Review

Recommendations

Use the deToken balance of the user instead of raw deposit amount to ensure decimal consistency:

function calculateRaacRewards(address user) public view returns (uint256) {
- uint256 userDeposit = userDeposits[user];
+ uint256 userDeposit = deToken.balanceOf(user);
uint256 totalDeposits = deToken.totalSupply();
uint256 totalRewards = raacToken.balanceOf(address(this));
if (totalDeposits < 1e6) return 0;
return (totalRewards * userDeposit) / totalDeposits;
}

This ensures both values in the division operation use the same decimal scale, resulting in correct reward calculations.

Updates

Lead Judging Commences

inallhonesty Lead Judge 7 months ago
Submission Judgement Published
Invalidated
Reason: Non-acceptable severity

Support

FAQs

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

Give us feedback!