Core Contracts

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

`FeeCollector::claimRewards` Function Miscalculates Rewards, Leading to Incorrect Distribution

Summary

The claimRewards function incorrectly updates userRewards[user] by directly assigning it the global accumulated value totalDistributed, leading to miscalculations in rewards distribution.

Vulnerability Details

userRewards[user] should track the total rewards a user has claimed. However, in the current implementation, it is incorrectly set to the protocol-wide accumulated value totalDistributed.

/**
* @notice Claims accumulated rewards for a user
* @param user Address of the user claiming rewards
* @return amount Amount of rewards claimed
*/
function claimRewards(
address user
) external override nonReentrant whenNotPaused returns (uint256) {
if (user == address(0)) revert InvalidAddress();
uint256 pendingReward = _calculatePendingRewards(user);
if (pendingReward == 0) revert InsufficientBalance();
// Reset user rewards before transfer
@> userRewards[user] = totalDistributed;
// Transfer rewards
raacToken.safeTransfer(user, pendingReward);
emit RewardClaimed(user, pendingReward);
return pendingReward;
}

As a result, when a user attempts to claim rewards again, the calculation in the following line:

function _calculatePendingRewards(
address user
) internal view returns (uint256) {
uint256 userVotingPower = veRAACToken.getVotingPower(user);
if (userVotingPower == 0) return 0;
uint256 totalVotingPower = veRAACToken.getTotalVotingPower();
if (totalVotingPower == 0) return 0;
uint256 share = (totalDistributed * userVotingPower) / totalVotingPower;
@> return share > userRewards[user] ? share - userRewards[user] : 0;
}

may erroneously return 0, preventing the user from claiming their rightful rewards.

Steps to Reproduce

Initial State:

  • totalDistributed = 1000

  • User A has a voting power share of 10%

First Claim:

`pending = 1000 * 10% - 0 = 100 RAAC userRewards[A] = 1000 (incorrect assignment) `

Second Distribution:

  • The protocol distributes additional rewards, increasing totalDistributed to 2000.

Second Claim Attempt:

`share = 2000 * 10% = 200 < userRewards[A] (1000)

Result:

  • pendingReward is calculated as 0, preventing the user from claiming their new rewards.

Impact

Users may be unable to claim subsequent rewards, leading to an unfair distribution of incentives.

Tools Used

Manual

Recommendations

/**
* @notice Claims accumulated rewards for a user
* @param user Address of the user claiming rewards
* @return amount Amount of rewards claimed
*/
function claimRewards(
address user
) external override nonReentrant whenNotPaused returns (uint256) {
if (user == address(0)) revert InvalidAddress();
uint256 pendingReward = _calculatePendingRewards(user);
if (pendingReward == 0) revert InsufficientBalance();
// Reset user rewards before transfer
- userRewards[user] = totalDistributed;
+ userRewards[user] += pendingReward;
// Transfer rewards
raacToken.safeTransfer(user, pendingReward);
emit RewardClaimed(user, pendingReward);
return pendingReward;
}

This fix ensures that userRewards[user] correctly tracks only the total claimed rewards instead of being incorrectly reset to totalDistributed.

Updates

Lead Judging Commences

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

FeeCollector::claimRewards sets `userRewards[user]` to `totalDistributed` seriously grieving users from rewards

Support

FAQs

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