Core Contracts

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

The legitimate user loses rewards of raacToken in FeeCollector.

Summary

The legitimate user loses rewards of raacToken in FeeCollector.

Vulnerability Details

FeeCollector.sol#_calculatePendingRewards() function is as follows.

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;
}

As we can see above, totalDistributed is total distributed reward amount.
And we can see that userRewards[user] is variable to track claimed rewards.
And FeeCollector.sol#claim() function is as follows.

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;
}

In upper code, userRewards[user] is set by bigger value than normal.

Impact

This leads to user's loss.

Following scenario shows error.

  • Protocol distributes some funds : totalDistributed = 1e18.

  • And a user has 10% of voting power and claimes 0.1e18 of rewards as his voting power. At this time, userRewards[user] = 1e18(instead of 0.1e18).

  • And then, protocol distributes additional funds. totalDistributed = 2e18. (1e18 of additional distribution)

  • And then, he claims again. But he receives zero rewards because userRewards[user] = 1e18 > 0.2e18.

  • This is user's loss.

Tools Used

Manual review

Recommendations

Please modify FeeCollector.sol#claim() function as follows.

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;
}
Updates

Lead Judging Commences

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

Give us feedback!