Core Contracts

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

Reward Misalignment: Using block.timestamp Instead of lastTimeRewardApplicable() in _updateReward Causes Extra Reward Accrual Post-Period

Summary

The BaseGauge contract updates the global timestamp (lastUpdateTime) using lastTimeRewardApplicable(), which caps the time at periodFinish() if the reward period has ended. However, when updating an individual user’s reward state in _updateReward, the contract sets the user’s lastUpdateTime directly to block.timestamp. Under active reward periods this is equivalent, but after the period ends (when block.timestamp > periodFinish()), this inconsistency can lead to a slight over-calculation of rewards for that user.

Vulnerability Details

  • What Happens:

    • The function getRewardPerToken() calculates accrued rewards based on the difference between lastTimeRewardApplicable() and lastUpdateTime.

    • In _updateReward, the global lastUpdateTime is updated to lastTimeRewardApplicable(), ensuring that reward accrual does not count time beyond the reward period.

    • However, for per-user updates, state.lastUpdateTime is set to block.timestamp rather than the effective value from lastTimeRewardApplicable().

  • Why This Is Problematic:

    • Under normal conditions (when block.timestamp < periodFinish()), both block.timestamp and lastTimeRewardApplicable() are equal.

    • Once the reward period ends (block.timestamp > periodFinish()), lastTimeRewardApplicable() returns periodFinish(), while block.timestamp remains higher.

    • This causes the user’s recorded lastUpdateTime to be higher than the global lastUpdateTime, leading to a small discrepancy when calculating the rewards earned since the last update.


    Given that the reward accrual formulas depend on a consistent timestamp for both global and per-user state, this inconsistency may result in either an over- or under-calculation of rewards for users immediately after the reward period ends.

Impact

Users may receive slightly more or less reward than intended if their lastUpdateTime is based on block.timestamp instead of the capped periodFinish() value once the reward period has ended.

Tools Used

  • Manual review

Recommendations

Modify the _updateReward function so that the user’s state.lastUpdateTime is set to lastTimeRewardApplicable() rather than block.timestamp.

Updates

Lead Judging Commences

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

BaseGauge sets user's lastUpdateTime to uncapped block.timestamp while global lastUpdateTime uses capped lastTimeRewardApplicable(), generating reward calc inconsistencies after period ends

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

BaseGauge sets user's lastUpdateTime to uncapped block.timestamp while global lastUpdateTime uses capped lastTimeRewardApplicable(), generating reward calc inconsistencies after period ends

Support

FAQs

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