Summary
The lastClaimTime
for the user will be updated even if the user has no reward, so he has to wait 24hours for the next try.
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;
UserState storage state = userStates[msg.sender];
uint256 reward = state.rewards;
if (reward > 0) {
state.rewards = 0;
uint256 balance = rewardToken.balanceOf(address(this));
if (reward > balance) {
revert InsufficientBalance();
}
rewardToken.safeTransfer(msg.sender, reward);
emit RewardPaid(msg.sender, reward);
}
}
Vulnerability Details
If a user calls getReward()
when they have no pending reward, their lastClaimTime
is updated anyway.
If they get a reward just seconds later, they must wait 24 hours.
This is not good approach since the users can get frustrated.
Impact
Users are unfairly penalized. They must wait for the 24 hours to pass even if they didn't get any reward.
Tools Used
Manual
Recommendations
Consider updating the lastClaimTime
for the user only if they have rewards to claim.
function getReward() external virtual nonReentrant whenNotPaused updateReward(msg.sender) {
if (block.timestamp - lastClaimTime[msg.sender] < MIN_CLAIM_INTERVAL) {
revert ClaimTooFrequent();
}
// @audit lastClaimTime should be updated only if you have reward.
// otherwise 1second later i could have a reward, but i must wait until tomorrow.
- lastClaimTime[msg.sender] = block.timestamp;
UserState storage state = userStates[msg.sender];
uint256 reward = state.rewards;
if (reward > 0) {
state.rewards = 0;
+ lastClaimTime[msg.sender] = block.timestamp;
uint256 balance = rewardToken.balanceOf(address(this));
if (reward > balance) {
revert InsufficientBalance();
}
rewardToken.safeTransfer(msg.sender, reward);
emit RewardPaid(msg.sender, reward);
}
}