Summary
User can only claim rewards once due to incorrect comparison.
Vulnerability Details
Users can call the claimRewards to claim the accumulated rewards.
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();
userRewards[user] = totalDistributed;
raacToken.safeTransfer(user, pendingReward);
emit RewardClaimed(user, pendingReward);
return pendingReward;
}
An issue with the current implementation is that it sets the userRewards[user] to totalDistributed
during the first claim, this setting will prevent user from claiming rewards again when they accumulate, this is because the _calculatePendingRewards returns zero when the calculated share of user is less than the totalDistributed.
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;
}
So there is no way for a single user share to be more than the useuserReward
after the first claim.[Unless he increased his voting power in large amount]
This will prevent user from claiming rewards again.
Impact
User cannot claim rewards again
Tools Used
Manual review
Recommendations
Refactor the implementation and only add what the user able to claim , not totalDistributed
.
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();
userRewards[user] = pendingReward;
raacToken.safeTransfer(user, pendingReward);
emit RewardClaimed(user, pendingReward);
return pendingReward;
}