The current reward distribution mechanism in FeeCollector.sol results in unclaimable rewards accumulating over time due to the decaying nature of userVotingPower. While rewards are allocated based on totalDistributed, users’ voting power decreases over time, preventing them from fully claiming their share. The issue stems from using dynamically changing userVotingPower instead of a checkpointed voting power at the time of distribution. This results in a portion of rewards being permanently locked in the contract, requiring governance intervention through emergencyWithdraw().
Incorrect Reward Calculation Logic:
Problem: userVotingPower decays over time, while totalVotingPower (based on totalSupply()) only updates during lock(), increase(), extend() and withdraw() in veRAACToken.sol.
Issue: Users cannot claim their full share if they wait too long, as their voting power is lower at claim time compared to distribution time.
Unclaimable Rewards Accumulate Over Time:
Even if all users claim their rewards, a portion will always remain unclaimable due to the mismatch between decaying voting power and totalSupply updates.
The longer a user delays claiming, the more rewards become inaccessible.
The only way to recover these lost rewards is through emergencyWithdraw(), which grants governance control over excess rewards, centralizing their redistribution.
Users lose a portion of their allocated rewards if they do not claim early.
The system does not ensure full reward distribution, favoring early claimers.
Unclaimed rewards accumulate, leading to centralized decision-making on redistribution.
Users may lose confidence in the reward system due to inaccessible rewards.
Manual
Implement Voting Power Checkpointing at Distribution
Store userVotingPower at the time of reward distribution instead of dynamically using the decaying power.
Use PowerCheckpoint.sol to fetch historical voting power when rewards were assigned.
Modify _calculatePendingRewards() to Use Checkpoints
Replace userVotingPower with the stored voting power from the time of distribution.
Ensure totalVotingPower also references the checkpointed value instead of totalSupply().
Implement Periodic Redistribution of Unclaimed Rewards
If rewards remain unclaimed for an extended period, redistribute them among active veRAAC holders instead of relying on emergencyWithdraw().
Require Automatic Reward Claims Before Withdrawals
Before allowing users to call withdraw(), enforce an automatic reward claim to prevent loss.
This ensures users do not lose their accumulated rewards due to voting power decay.
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.