Core Contracts

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

Rewards Draining in `StabilityPool` Due to Flawed Reward Distribution Mechanism

Summary

The StabilityPool contract contains a critical flaw in its reward distribution mechanism that allows anyone to drain a all the rewards amount of RAAC . The issue stems from calculating and distributing rewards on each withdrawal without proper accumulation tracking, enabling attackers to repeatedly withdraw small amounts to claim an unfair share of the total rewards.

Vulnerability Details

The vulnerability exists in how rewards are calculated and distributed during withdrawals in the StabilityPool contract :

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);
// ... [withdrawal logic]
if (raacRewards > 0) {
raacToken.safeTransfer(msg.sender, raacRewards);
}
}
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 key issues that make this exploitable:

  1. Rewards are calculated and distributed on EACH withdrawal using the formula:

    rewards = (totalRewards * userDeposit) / totalDeposits
  • there is no tracking of already claimed rewards or reward rate over time

    • Each withdrawal recalculates rewards based on current total rewards and user deposit

    • Previous withdrawals don't reduce the user's claimable share

  1. The reward amount is proportional to the user's deposit but independent of the withdrawal amount

    • Even a 1 wei withdrawal triggers a full reward calculation

    • The user can withdraw minimal amounts repeatedly to claim rewards multiple times or repeatedly deposit and withdraw until they completely drain the pool from all the rewards

Impact

Attacker can drain all the rewards from the stability pool

Recommendations

Implement a reward accumulator pattern similar to MasterChef

Updates

Lead Judging Commences

inallhonesty Lead Judge 6 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.