Core Contracts

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

User reward calculation breaks after the first claim

Summary

userRewards mapping in FeeCollector is updated with incorrect values after the first reward claim, diminishing future claims and causing a soft DoS

Details

After a user claims their rewards, there is a mapping userRewards which tracks accumulated rewards for each user.

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();
@> userRewards[user] = totalDistributed;
raacToken.safeTransfer(user, pendingReward);
return pendingReward;
}

This mapping is used in future _calculatePendingRewards calculations in order to account for already claimed yield.

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

However the calculation highlighted above is incorrect as it compares the user's current share of all historical distributions against the total historical distributions on their previous claim. To put that simply, let's observe an example:

  1. totalDistributed = 100, there are only 2 users with 50% voting power each, each is assigned 50 tokens as rewards

  2. Alice calls claimRewards, gets her 50 tokens, userRewards[Alice] = 100

  3. Some time passes, protocol earns fees and totalDistributed goes up to 150, each user should be eligible for half of the extra 50 tokens added

  4. Alice calls claimRewards

  5. share = (150 * 0.5) = 75, 75 < 100 => the function returns 0

  6. Alice does not get any rewards despite new rewards having been added

Even if the added rewards are greater than the initial ones, the function will greatly undermine Alice's rewards.

Impact

Loss of rewards, broken core functionality

Mitigation

Complete rehaul of this method is needed

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!