Core Contracts

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

Reward distribution exceeding distributionCap

Summary

When a user claims his reward by using getReward function defined in BaseGauge contract and used by RAACGauge and RWAGauge contracts, he gets rewards exceeding the distributionCap.

Vulnerability Details

If we look at the docs in 2025-02-raac/docs/core/governance/gauges/BaseGauge.md, we can see that:

Distribution cap limits reward amounts

This distributionCap is set using setDistributionCap function by FEE_ADMIN role

/**
* @notice Sets cap on reward distribution
* @param newCap New distribution cap value
*/
function setDistributionCap(uint256 newCap) external {
if (!hasRole(FEE_ADMIN, msg.sender)) revert UnauthorizedCaller();
@> distributionCap = newCap;
emit DistributionCapUpdated(newCap);
}

But in getReward function there is no check for distributionCap

/**
* @notice Claims accumulated rewards
* @dev Transfers earned rewards to caller
*/
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);
}
}

This leads to users getting rewards higher than the distributionCap.

Add this test to RAACGauge.test.js:

describe("Reward Cap", () => {
it("should distribute rewards exceeding distributionCap set by FEE_ADMIN", async () => {
// Grant FEE_ADMIN role to user2
await raacGauge.grantRole(await raacGauge.FEE_ADMIN(), user2.address);
// User1 approves the contract to spend their veRAAC tokens
await veRAACToken.connect(user1).approve(raacGauge.getAddress(), ethers.MaxUint256);
// User1 stakes 100 veRAAC tokens
await raacGauge.connect(user1).stake(ethers.parseEther("100"));
// Simulate half a week passing
await time.increase(WEEK / 2);
// Calculate the earned reward amount for user1
const rewardAmount = await raacGauge.earned(user1.address)
// Set a new distribution cap to half of the earned reward
const newDistributionCap = rewardAmount / BigInt(2);
// FEE_ADMIN (user2) updates the distribution cap
expect(await raacGauge.connect(user2).setDistributionCap(newDistributionCap)).to.emit(raacGauge, "DistributionCapUpdated");
// User1 claims their reward, which should exceed the new cap
expect(await raacGauge.connect(user1).getReward()).to.emit(raacGauge, "RewardPaid").withArgs(user1.address, rewardAmount);
})
});

Impact

Over distribution of reward tokens which can result in unexpected token inflation devaluing the reward token and potentially harming all stakeholders.

Tools Used

Manual review

Recommendations

Ensure that getReward function checks the distributionCap.

Updates

Lead Judging Commences

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

BaseGauge lacks enforcement of both distributionCap and MAX_REWARD_RATE limits

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

BaseGauge lacks enforcement of both distributionCap and MAX_REWARD_RATE limits

Support

FAQs

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