Core Contracts

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

User are unable to claim further rewards after the first claim

Target

contracts/core/collectors/FeeCollector.sol

Vulnerability Details

The feeCollector contract is responsible for managing reward periods and user claims. The claimRewards function within the contract allows a user to claim accumulated rewards, after the user’s pending rewards are calculated, the state variable mapping that stores users claimed rewards (userRewards[user]).

function claimRewards(address user) external override nonReentrant whenNotPaused returns (uint256) {
if (user == address(0)) revert InvalidAddress();
uint256 pendingReward = _calculatePendingRewards(user);
if (pendingReward == 0) revert InsufficientBalance();
// Reset user rewards before transfer
userRewards[user] = totalDistributed;
// Transfer rewards
raacToken.safeTransfer(user, pendingReward);
emit RewardClaimed(user, pendingReward);
return pendingReward;
}

FeeCollector.claimRewards

However, when a user claims their rewards the total user’s rewards is updated incorrectly to the total distributed in that round rather than the amount claimed, this leads to a situation where whenever the user comes back to for another reward claim, they are considered ineligible due to accounting logic that calculate user’s claims thereby blocking user’s from subsequent reward claims

function _calculatePendingRewards(address user) internal view returns (uint256) {
uint256 userVotingPower = veRAACToken.getVotingPower(user);
if (userVotingPower == 0) return 0;
uint256 totalVotingPower = veRAACToken.getTotalVotingPower();
if (totalVotingPower == 0) return 0;
uint256 share = (totalDistributed * userVotingPower) / totalVotingPower;
return share > userRewards[user] ? share - userRewards[user] : 0;
}

FeeCollector._calculatePendingRewards

Impact

Users would be unable to claim rewards after first claim, as afterwards, pending rewards will return zero

Tools Used

Manual Review

Recommendations

Users already claimed rewards should to accounted for correctly so subsequent claim requests will not incorrectly revert

POC

add the test script below to test/unit/core/collectors/FeeCollector.test.js

describe("POC #5", function () {
it("sets user's total claimed to total distributed", async function () {
await feeCollector.connect(owner).distributeCollectedFees();
await time.increase(WEEK);
const initialBalance = await raacToken.balanceOf(user1.address);
console.log("--- Initial balance -------");
console.log(initialBalance);
await feeCollector.connect(user1).claimRewards(user1.address);
console.log(await raacToken.balanceOf(user1.address));
console.log(await feeCollector.totalDistributed());
console.log(await feeCollector.userRewards(user1.address));
});
});
Updates

Lead Judging Commences

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

FeeCollector::claimRewards sets `userRewards[user]` to `totalDistributed` seriously grieving users from rewards

Support

FAQs

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

Give us feedback!