Core Contracts

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

Vulnerability Allowing Drain of RAAC Rewards from Stability Pool

Summary

The StabilityPool.withdraw() function allows users to withdraw rToken and receive RAAC rewards based on their total deposits, rather than the amount they are withdrawing. This creates a vulnerability where a malicious user can exploit this behavior to drain RAAC rewards from the pool by repeatedly invoking the withdraw() function with minimal withdrawal amounts.

Vulnerability Details

In the StabilityPool.withdraw() function, the calculation of RAAC rewards is performed using the calculateRaacRewards(msg.sender) function:

function withdraw(uint256 deCRVUSDAmount) external nonReentrant whenNotPaused validAmount(deCRVUSDAmount) {
_update();
if (deToken.balanceOf(msg.sender) < deCRVUSDAmount) revert InsufficientBalance();
uint256 rcrvUSDAmount = calculateRcrvUSDAmount(deCRVUSDAmount);
>> uint256 raacRewards = calculateRaacRewards(msg.sender); // Reward calculation
if (userDeposits[msg.sender] < rcrvUSDAmount) revert InsufficientBalance();
userDeposits[msg.sender] -= rcrvUSDAmount;
if (userDeposits[msg.sender] == 0) {
delete userDeposits[msg.sender];
}
deToken.burn(msg.sender, deCRVUSDAmount);
rToken.safeTransfer(msg.sender, rcrvUSDAmount);
if (raacRewards > 0) {
raacToken.safeTransfer(msg.sender, raacRewards); // Transfer rewards
}
emit Withdraw(msg.sender, rcrvUSDAmount, deCRVUSDAmount, raacRewards);
}

The calculateRaacRewards() function calculates the user's rewards based on their total deposits, not the amount being withdrawn:

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; // Reward based on total deposits
}

An attacker can exploit this by repeatedly calling the withdraw() function with a very small deCRVUSDAmount. Since the rewards are calculated based on the user's total deposits, the attacker can drain the RAAC rewards from the stability pool without proportionate withdrawals.

For example:

  1. The attacker deposits a certain amount of rToken to the stability pool.

  2. They then call withdraw() with a minimal amount multiple times.

  3. Each time, they receive rewards calculated on their total deposit, leading to excessive draining of RAAC tokens.

Impact

This vulnerability allows malicious users to drain RAAC rewards from the stability pool, which can significantly impact the reward distribution and integrity of the system. It undermines the intended reward mechanism, leading to potential financial loss for the protocol and its users.

Tools Used

  • Manual Review

Recommendations

To mitigate this vulnerability, it is recommended to modify the calculateRaacRewards() function to include the withdrawal amount in its calculations. This change will ensure that rewards are only distributed based on the amount being withdrawn, rather than the user's total deposits.

Updates

Lead Judging Commences

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

StabilityPool::withdraw can be called with partial amounts, but it always send the full rewards

Support

FAQs

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