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.
In FeeCollector.sol
, the pending rewards are calculated using the following formula:
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.
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.
Manual code review
To prevent retroactive reward claims, implement tracking of distribution timestamps:
Introduce a lastDistributionTime
variable that updates whenever rewards are distributed.
Modify claimRewards()
to only consider rewards distributed after lastClaimTime[user]
.
Use getVotingPower(user, timestamp)
to calculate userVotingPower
at the time of the last distribution rather than at the current block timestamp.
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.
The contest is live. Earn rewards by submitting a finding.
This is your time to appeal against judgements on your submissions.
Appeals are being carefully reviewed by our judges.