Core Contracts

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

Rewards Will still accumulate in the BaseGuage contract, despite the protocol's intention for Emergency pause to stop all operations

Summary

The BaseGauge contract’s emergency pause stops users from staking, withdrawing, or claiming rewards, but lets the controller keep updating periods and adding rewards. The protocol doc says a pause should stop all operations, so this mismatch will keep piling up unexpected rewards even while paused.

Vulnerability Details

User actions like staking or claiming rewards stop because they use a rule called whenNotPaused. But controller actions, like updating periods or adding rewards don’t have this rule and can still happen.

function notifyRewardAmount(uint256 amount) external override onlyController updateReward(address(0)) {
if (amount > periodState.emission) revert RewardCapExceeded();
rewardRate = notifyReward(periodState, amount, periodState.emission, getPeriodDuration());
periodState.distributed += amount;
uint256 balance = rewardToken.balanceOf(address(this));
if (rewardRate * getPeriodDuration() > balance) revert InsufficientRewardBalance();
lastUpdateTime = block.timestamp;
emit RewardNotified(amount);
}
function updatePeriod() external override onlyController {
uint256 currentTime = block.timestamp;
uint256 periodEnd = periodState.periodStartTime + getPeriodDuration();
if (currentTime < periodEnd) revert PeriodNotElapsed();
uint256 periodDuration = getPeriodDuration();
uint256 avgWeight = periodState.votingPeriod.calculateAverage(periodEnd);
uint256 nextPeriodStart = ((currentTime / periodDuration) + 2) * periodDuration;
periodState.distributed = 0;
periodState.periodStartTime = nextPeriodStart;
TimeWeightedAverage.createPeriod(periodState.votingPeriod, nextPeriodStart, periodDuration, avgWeight, WEIGHT_PRECISION);
}

When not paused, the two functions can call notifyRewardAmount to add more rewards or updatePeriod to move time periods forward. When notifyRewardAmount runs, it updates the rewardRate and lastUpdateTime. This makes rewards build up here:

function getRewardPerToken() public view returns (uint256) {
if (totalSupply() == 0) return rewardPerTokenStored;
return rewardPerTokenStored + (
(lastTimeRewardApplicable() - lastUpdateTime) * rewardRate * 1e18 / totalSupply()
);
}

The protocol doc says an emergency pause “stops all operations,” meaning everything should freeze, user actions and controller actions. But right now, the controller can keep going.

Impact

  1. Rewards keep growing during a pause because the controller can add more. When unpaused, users might get a big payout they didn’t expect, or the reward pool could run out if too much builds up.

  2. If the controller adds rewards without enough tokens in the contract, claims will stop working when unpaused because of this check

if (reward > balance) revert InsufficientBalance();

Tools Used

Manual Review

Recommendations

Add whenNotPaused to controller functions so they stop during a pause. This makes the pause stop everything, no reward updates or period changes until unpaused.

Updates

Lead Judging Commences

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

BaseGauge::withdraw, stake, and checkpoint functions lack whenNotPaused modifier, allowing critical state changes even during emergency pause

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

BaseGauge::withdraw, stake, and checkpoint functions lack whenNotPaused modifier, allowing critical state changes even during emergency pause

Support

FAQs

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

Give us feedback!