Core Contracts

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

Fee Collector Reward Calculation

Summary

The FeeCollector contract has a flawed reward distribution mechanism that allows new users to claim a disproportionately large share of rewards when they claim for the first time. This occurs because the contract does not track when rewards are distributed, allowing new users to retroactively claim rewards for periods when they had no voting power.

Vulnerability Details

In FeeCollector.sol, the pending rewards are calculated using the following formula:

uint256 share = (totalDistributed * userVotingPower) / totalVotingPower;

where:

  • totalVotingPower is the total voting power of veRAACToken.

  • userVotingPower is the user's voting power of veRAACToken.

  • totalDistributed represents the total rewards distributed up to the current time.

The issue arises when a new user claims rewards by calling claimRewards(). Since their userRewards is initially 0, they receive a share based on totalDistributed rather than the rewards distributed after they acquired voting power. This allows them to claim rewards they were not entitled to.

The veRAACToken contract includes checkpoints that can be accessed through getVotingPower(address user, uint256 timestamp), which tracks historical voting power. However, the FeeCollector contract does not track when rewards were distributed, which is necessary to properly allocate rewards only to eligible users.

Impact

  • Users who have never claimed before can receive an incorrect share of previously distributed rewards, leading to unfair reward allocation.

  • This could result in excessive depletion of the reward pool, reducing incentives for long-term stakers.

  • Potential economic attack vector where an attacker could continuously create new accounts, acquire voting power, and claim past rewards unfairly.

Tools Used

Manual code review

Recommended Mitigation

To prevent retroactive reward claims, implement tracking of distribution timestamps:

  1. Introduce a lastDistributionTime variable that updates whenever rewards are distributed.

  2. Modify claimRewards() to only consider rewards distributed after lastClaimTime[user].

  3. Use getVotingPower(user, timestamp) to calculate userVotingPower at the time of the last distribution rather than at the current block timestamp.

  4. Explicitly store userRewards[user] at the time of claim to prevent claiming past rewards again.

By implementing these mitigations, only users who held voting power during a given distribution period will be eligible for that period's rewards.

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

_updateLastClaimTime not properly used to track rewards claim time

Time-Weighted Average Logic is Not Applied to Reward Distribution in `FeeCollector`

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

_updateLastClaimTime not properly used to track rewards claim time

Time-Weighted Average Logic is Not Applied to Reward Distribution in `FeeCollector`

Support

FAQs

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