Core Contracts

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

Loss of Reward to users when the scaling factor is greater than or less than 1e18

Summary

The code allows for rtokens and token flexibility but this creates a bug when both decimals are not the same causing users to lose funds also if the scale is reduced on the other hand will allow one user to take more rewards than they are entitled to and other users will not be able to withdraw their funds.

Vulnerability Details

According to the code

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

Allowing for a flexible decimal creates a situation that causes the Reward calculation to fail to accurately track rewards

When a user calls to withdraw

/**
* @notice Allows a user to withdraw their rToken and RAAC rewards.
* @param deCRVUSDAmount Amount of deToken to redeem.
*/
function withdraw(uint256 deCRVUSDAmount) external nonReentrant whenNotPaused validAmount(deCRVUSDAmount) {
_update();
if (deToken.balanceOf(msg.sender) < deCRVUSDAmount) revert InsufficientBalance();
uint256 rcrvUSDAmount = calculateRcrvUSDAmount(deCRVUSDAmount);
@audit>> uint256 raacRewards = calculateRaacRewards(msg.sender); //bug calculate based on rtoken or dtoken scaling
if (userDeposits[msg.sender] < rcrvUSDAmount) revert InsufficientBalance();
userDeposits[msg.sender] -= rcrvUSDAmount;

Based on the reward calculation we take the Rtoken balance and divide by the CRVUSD total supply but this is wrong as rtoken and detoken are allowed to have flexible decimals hence the value returned when both tokens are not both the same will be wrong

/**
* @notice Calculates the pending RAAC rewards for a user.
* @param user Address of the user.
* @return Amount of RAAC rewards.
*/
function calculateRaacRewards(address user) public view returns (uint256) {
@audit>> rtoken >> uint256 userDeposit = userDeposits[user];
@audit>> scaled rtoken to crvusd >> uint256 totalDeposits = deToken.totalSupply();
@audit>> uint256 totalRewards = raacToken.balanceOf(address(this));
if (totalDeposits < 1e6) return 0; // this must be enforced in withdraw or else if i withdraw and leave tokens in the
@audit>> return (totalRewards * userDeposit) / totalDeposits; // bug from line 229 use detokens straight or scale balance to CRVUSD first . loss of reward de is bigger than rtoken value // medium i need the 2 tokens to have different decimals
}

Rtokens are always scaled to get CRVUSD

/**
* @notice Calculates the amount of deToken to mint for a given rToken deposit.
* @param rcrvUSDAmount Amount of rToken deposited.
* @return Amount of deToken to mint.
*/
function calculateDeCRVUSDAmount(uint256 rcrvUSDAmount) public view returns (uint256) {
@audit>> uint256 scalingFactor = 10**(18 + deTokenDecimals - rTokenDecimals);
@audit>> return (rcrvUSDAmount * scalingFactor) / getExchangeRate(); // line 229 expect if detoken and rtoken have thes same decimals find out if so
}
/**
* @notice Calculates the amount of rToken to return for a given deToken redemption.
* @param deCRVUSDAmount Amount of deToken to redeem.
* @return Amount of rToken to return.
*/
function calculateRcrvUSDAmount(uint256 deCRVUSDAmount) public view returns (uint256) {
@audit>> uint256 scalingFactor = 10**(18 + rTokenDecimals - deTokenDecimals);
@audit>> return (deCRVUSDAmount * getExchangeRate()) / scalingFactor;
}

If the scaling factor is larger or smaller we either send lower rewards or just try and send higher which reverts withdrawals.

deToken.burn(msg.sender, deCRVUSDAmount);
rToken.safeTransfer(msg.sender, rcrvUSDAmount);
@audit>> if (raacRewards > 0) {
raacToken.safeTransfer(msg.sender, raacRewards); //sending less reward or 0 rewards when user earned for providing liquidity
}
emit Withdraw(msg.sender, rcrvUSDAmount, deCRVUSDAmount, raacRewards);
}

Impact

The possibility of occurring is low as both tokens need to be flexible and have different decimals but when it occur the impact is high as users will either lose their share of the reward or won't be able to make any withdrawals.

Tools Used

Manual review

Recommendations

Scale the userdeposit in the raac reward calculation

uint256 userDeposit = userDeposits[user];
uint256 totalDeposits = deToken.totalSupply();
++ uint256 deCRVUSDAmount = calculateDeCRVUSDAmount(userDeposit);
---------------------------------------------------
++ return (totalRewards * deCRVUSDAmount) / totalDeposits;
Updates

Lead Judging Commences

inallhonesty Lead Judge 4 months ago
Submission Judgement Published
Invalidated
Reason: Non-acceptable severity
Assigned finding tags:

Incorrect scaling factor formula in StabilityPool::calculateRcrvUSDAmount function

Both tokens have 18 decimals. Info

Support

FAQs

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