Core Contracts

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

Failure to Reset `rewardRate` in `updatePeriod()`

Summary

The updatePeriod() function fails to reset rewardRate to zero when a new period starts. This allows users to claim rewards even when no new rewards have been notified, leading to an over-distribution of tokens.


Vulnerability Details

The rewardRate determines the rate at which rewards are distributed to users. When a new period starts, the rewardRate should be reset to zero to ensure that no rewards are distributed until a new reward amount is notified. However, the current implementation does not reset rewardRate, causing the following issue:

  • If rewardRate is not reset, users can continue to claim rewards at the previous period's rate, even if no new rewards have been notified.

  • This results in an over-distribution of tokens, as users can claim rewards for which no corresponding funds have been allocated.


Impact

  • Over-Distribution of Tokens: Users can claim rewards without new rewards being notified, leading to an unfair distribution of tokens.

  • Financial Loss: The protocol will lose funds due to excessive reward payouts.

  • Incorrect Accounting: The reward distribution mechanism becomes unreliable, undermining trust in the protocol.


Poc

run in BaseGauge.test.js

https://github.com/Cyfrin/2025-02-raac/blob/89ccb062e2b175374d40d824263a4c0b601bcb7f/contracts/core/governance/gauges/BaseGauge.sol#L452

it(" should distribute reward in second period even when no reward are notify", async () => {
let nextPeriodStart = await baseGauge.getCurrentPeriodStart();
let t = await time.latest();
await time.increase(nextPeriodStart- BigInt(t));
await baseGauge.setEmission(ethers.parseEther("1000"));
// Setup rewards and voting
await gaugeController.connect(user1).vote(await baseGauge.getAddress(), 5000);
// Stake some tokens to gauge to be eligible for rewards
await rewardToken.mint(user1.address, ethers.parseEther("1000"));
await rewardToken.connect(user1).approve(await baseGauge.getAddress(), ethers.parseEther("1000"));
await baseGauge.connect(user1).stake(ethers.parseEther("1000"));
await baseGauge.notifyRewardAmount(ethers.parseEther("1000"));
// Wait for rewards to accrue for 7 Days
await time.increase(DAY * 7);
let before = await rewardToken.balanceOf(user1.address);
// user claim reward for the whole period
await baseGauge.connect(user1).getReward();
let after = await rewardToken.balanceOf(user1.address);
const FirstPeriodReward = after-before;
console.log('Reward get in the first period : ',FirstPeriodReward);
// start a new period
await baseGauge.connect(owner).updatePeriod();
nextPeriodStart = await baseGauge.getCurrentPeriodStart();
t = await time.latest();
await time.increase(nextPeriodStart- BigInt(t));
await baseGauge.setEmission(ethers.parseEther("1000"));
// no reward will be notify in second period
//await baseGauge.notifyRewardAmount(ethers.parseEther("1000"));
// Wait for rewards to accrue
await time.increase(DAY * 7);
before = await rewardToken.balanceOf(user1.address);
await baseGauge.connect(user1).getReward();
after = await rewardToken.balanceOf(user1.address);
// in the two periods 1000 total reward is distributed but in the second period user get less reward.
const secondPeriodReward = after-before;
// The second period will still distribute more reward than first period
expect(secondPeriodReward).to.be.greaterThan((FirstPeriodReward * BigInt(2)) );
console.log('Reward get in the second period : ',secondPeriodReward);
});

Tools Used

  • Manual code review.

  • Test output analysis.


Recommendations

Reset rewardRate to zero when a new period starts in the updatePeriod() function. Specifically, add the following line after resetting the period state.

Updates

Lead Judging Commences

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

BaseGauge::updatePeriod fails to reset rewardRate when a new period starts, allowing users to continue claiming rewards at the previous period's rate even without new rewards being notified

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

BaseGauge::updatePeriod fails to reset rewardRate when a new period starts, allowing users to continue claiming rewards at the previous period's rate even without new rewards being notified

Support

FAQs

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

Give us feedback!