The userRewards[user]
storage variable in FeeCollector
is intended to act as a checkpoint for tracking distributed rewards. However, since it is only updated when a user claims rewards, it allows users to retroactively claim past distributed rewards they were not eligible for at the time, leading to reward over-allocations and potential exploitation.
When claimRewards(...)
is called, users with voting power are entitled to a share of the totalDistributed
RAAC tokens. This total is updated when FeeCollector
processes collected fees.
The userRewards[user]
checkpoint is only updated during the claim process:
The _calculatePendingRewards(...)
function determines the user's reward share:
Assume FeeCollector
distributes 1000 RAAC at time X and another 2000 RAAC at time Y.
Users claims their rewards before time Z, correctly receiving their share of the first two distributions.
3 A new user B gains voting power before time Z. At time Z, FeeCollector
distributes 3000 RAAC,
Since the checkpoint is not updated for past distributions, the new user wrongfully claims 6000 RAAC, receiving rewards they were never eligible for.
Voting power changes over time, but the current implementation does not update reward eligibility when tokens are locked or unlocked. As a result, users may miss rewards they are entitled to.
Users can claim past distributed rewards, leading to significant overpayment.
Reward calculations become inaccurate, creating a system-wide accounting issue.
Legitimate users may lose rewards, as new claimants can unfairly take a larger share.
N/A
The voting contract should update the user's checkpoint upon every locking and unlocking, also storage variable for eligible/unclaimed rewards should be involved. In general, a Synthetix staking contract should be used as a reference for the rewarding model. (ref)
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.