Core Contracts

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

Anyone Can Claim Rewards On-Behalf of Another User, Undermining the Expected User Experience

Summary

The FeeCollector contract’s claimRewards function is designed to calculate and transfer accumulated rewards to users based on their voting power. However, it allows any address to trigger a reward claim on behalf of any user without strict access control. While this does not directly misdirect funds, the open nature of the function can lead to unintended or premature claims that disrupt the expected reward accumulation process for users.

Vulnerability Details

In the current implementation, the claimRewards function is defined as follows:

https://github.com/Cyfrin/2025-02-raac/blob/89ccb062e2b175374d40d824263a4c0b601bcb7f/contracts/core/collectors/FeeCollector.sol#L199-L213

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();
// Reset user rewards before transfer
userRewards[user] = totalDistributed;
// Transfer rewards
raacToken.safeTransfer(user, pendingReward);
emit RewardClaimed(user, pendingReward);
return pendingReward;
}

A key observation is that this function accepts an arbitrary user address and sends rewards to that address. There is no requirement that msg.sender equals user. In practice, this means that any actor can trigger a reward claim on behalf of any user.

The vulnerability lies in the lack of access control on the claim trigger, allowing third parties to force the reward claiming process for users without their explicit initiation.

Impact

While the funds are always transferred to the intended recipient and cannot be stolen directly, the open claim functionality undermines the expected user experience. Users might be surprised by the timing of their reward resets if third parties trigger claims on their behalf.

Tools Used

  • Manual code review

Recommendations

Modify the claimRewards function to ensure that only the user for whom the rewards are being claimed can trigger the claim. For example, require that msg.sender == user.

function claimRewards(address user) external override nonReentrant whenNotPaused returns (uint256) {
if (user == address(0)) revert InvalidAddress();
+ if (user != msg.sender) revert InvalidCaller();
uint256 pendingReward = _calculatePendingRewards(user);
if (pendingReward == 0) revert InsufficientBalance();
// Reset user rewards before transfer
userRewards[user] = totalDistributed;
// Transfer rewards
raacToken.safeTransfer(user, pendingReward);
emit RewardClaimed(user, pendingReward);
return pendingReward;
}
Updates

Lead Judging Commences

inallhonesty Lead Judge 6 months ago
Submission Judgement Published
Invalidated
Reason: Non-acceptable severity

Support

FAQs

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