Core Contracts

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

Potential DoS on Low Voting Power Users Due to TotalDistributed Reset

Summary

The reward claiming mechanism in the FeeCollector contract creates an issue where users with low voting power may have to wait excessively long before they can claim new rewards.

Vulnerability Details

  1. Users Claim Rewards

  • When a user claims rewards, the function updates userRewards[user] = totalDistributed.
    FeeCollector::claimRewards:

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

2.New Rewards Become Available

  • FeeCollector::_calculatePendingRewards determines the user’s share of totalDistributed based on their voting power.

  • A user can only claim new rewards if their share exceeds userRewards[user].
    FeeCollector::_calculatePendingRewards:

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

3.Issue for Low Voting Power Users

  • If a user has low voting power, their reward share grows slowly.

  • Since userRewards[user] is set to totalDistributed, the gap to claim new rewards again becomes large.

  • They must wait a long time before their share increases enough to surpass userRewards[user].

Example Scenario

1.Initial Setup

  • totalDistributed = 10,000 RAAC

  • User A has 5% voting power

  • User A's share: (10,000 * 5%) = 500 RAAC

  • User A claims 500 RAAC → userRewards[A] = 10,000

2.New Rewards Distributed

  • totalDistributed increases by 1,000 RAAC, now totalDistributed = 11,000 RAAC

  • User A’s new share: (11,000 * 5%) = 550 RAAC

  • Pending rewards: 550 - 10,000 = 0 (User cannot claim rewards!)

3.User A Cannot Claim Rewards Until TotalDistributed Increases Significantly

  • For User A to receive just 50 RAAC, totalDistributed must increase to at least 20,000 RAAC

  • This means users with lower voting power experience long delays before they can claim again.

  • However, each time they claim, their userRewards[user] is reset to totalDistributed, forcing them to wait until totalDistributed increases significantly before they can claim again.

  • If distributions happen in small increments (e.g., 50 RAAC at a time), it takes an extremely long time for the user’s share to exceed their last recorded userRewards[user], effectively delaying or denying their ability to claim rewards efficiently—creating a potential denial-of-service (DoS) for users with low voting power.

Impact

  1. Delayed Access to Rewards – Users with low voting power must wait a long time before they can claim rewards again, as totalDistributed needs to increase significantly before their share exceeds userRewards[user].

  2. Denial-of-Service (DoS) Effect – In extreme cases, if distributions remain low for extended periods, affected users may be indefinitely prevented from claiming their rewards.

Tools Used

Manual

Recommendations

  1. Implement a separate tracking mechanism to account for new rewards per user rather than using totalDistributed directly.

Updates

Lead Judging Commences

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