Core Contracts

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

Reward claiming interval is not enforced sufficiently due to missing `lastClaimTime` update

Summary

The FeeCollector contract implements lastClaimTime mapping that maps user addresses to their last claim timestamp. However, this is not updated during claims as done in BaseGauge which is used to restrict the frequency with which users perform claims thereby allowing claims to be performed at any given time.

Vulnerability Details

In BaseGauge, rewards claiming frequency is enforced by the MIN_CLAIM_INTERVAL. This works in conjunction with the lastClaimTime mapping as shown in getReward():

function getReward() external virtual nonReentrant whenNotPaused updateReward(msg.sender) {
>> if (block.timestamp - lastClaimTime[msg.sender] < MIN_CLAIM_INTERVAL) {
revert ClaimTooFrequent();
}
lastClaimTime[msg.sender] = block.timestamp;
---SNIP---
}

This ensures a minimum interval between reward claims.

However, in FeeCollector contract, the same lastClaimTime mapping is implemented with a function to update it yet this is not used at all in claimRewards().

/**
* @notice User claim tracking
* @dev Maps user addresses to their last claim timestamp
*/
>> mapping(address => uint256) private lastClaimTime;
---SNIP---
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;
>> // @audit-issue lastClaimTime not updated
// Transfer rewards
raacToken.safeTransfer(user, pendingReward);
emit RewardClaimed(user, pendingReward);
return pendingReward;
}
---SNIP---
// @audit-issue Implemented but not used
function _updateLastClaimTime(address user) internal {
>> lastClaimTime[user] = block.timestamp;
}

Impact

If the same mechanism for claim used in BaseGauge is intended to be used in FeeCollector contract as well, then this is not achievable. As such, claims will be done at any time without any checked interval.

Tools Used

Manual Review

Recommendations

Enforce claim intervals to utilize this functionality:

+ /// @audit Provide a minimum interval between reward claims e.g,
+ uint256 public constant MIN_CLAIM_INTERVAL = 1 days;
---SNIP---
function claimRewards(address user) external override nonReentrant whenNotPaused returns (uint256) {
if (user == address(0)) revert InvalidAddress();
+ if (block.timestamp - lastClaimTime[user] < MIN_CLAIM_INTERVAL) {
+ revert ClaimTooFrequent();
+ }
uint256 pendingReward = _calculatePendingRewards(user);
if (pendingReward == 0) revert InsufficientBalance();
// Reset user rewards before transfer
userRewards[user] = totalDistributed;
+ // @audit Update lastClaimTime for the user
+ _updateLastClaimTime(user);
// Transfer rewards
raacToken.safeTransfer(user, pendingReward);
emit RewardClaimed(user, pendingReward);
return pendingReward;
}
Updates

Lead Judging Commences

inallhonesty Lead Judge 4 months ago
Submission Judgement Published
Validated
Assigned finding tags:

_updateLastClaimTime not properly used to track rewards claim time

Support

FAQs

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