Core Contracts

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

Resetting users rewards to the total amount distributed `Feecollector::claimRewards` will result to users not been able to claim future rewards

Summary

Resetting users rewards to totalDistributed in FeeCollector::claimRewards can prevent them from been able to claim thier future rewards, as their reward entitlement has been incorrectly adjusted, leading to lost or unclaimable rewards.

Details

function claimRewards(address user) external override nonReentrant whenNotPaused returns (uint256) {
// Reset user rewards before transfer
userRewards[user] = totalDistributed;

Upon claiming rewards, user's balance is been reset to the total amount distributed. This is a short term fix to prevent users from claiming same reward more than once. The problem with this is that userRewards[user] will still read as totalDistributed even in a situation where a user has accrued more reward, and this would lead to wrong calculation for users rewards as FeeCollector_calculatePendingRewardsreturn value is dependent on the value of userRewards[user]

POC

import {veRAACToken} from "../contracts/core/tokens/veRAACToken.sol";
import {FeeCollector} from "../contracts/core/collectors/FeeCollector.sol";
import {RAACToken} from "../contracts/core/tokens/RAACToken.sol";
import {Test, console} from "forge-std/Test.sol";
contract POC is Test {
RAACToken raacToken;
veRAACToken veRAAC;
FeeCollector feeCollector;
address owner = makeAddr("owner");
address user = makeAddr("user");
address user2 = makeAddr("user2");
address user3 = makeAddr("user3");
address treasury = makeAddr("treasury");
address repairFund = makeAddr("repairFund");
function setUp() public {
vm.startPrank(owner);
raacToken = new RAACToken( owner, 0, 0);
veRAAC = new veRAACToken(address(raacToken));
feeCollector = new FeeCollector(address(raacToken), address(veRAAC), treasury, repairFund, owner);
raacToken.setMinter(owner);
raacToken.mint(user, 20 ether);
raacToken.mint(user2, 20 ether);
raacToken.mint(address(feeCollector), 0.2 ether);
raacToken.mint(address(veRAAC), 0.2 ether);
vm.stopPrank();
}
function test_POC() public {
vm.startPrank(user);
raacToken.approve(address(feeCollector), 5 ether);
feeCollector.collectFee(1 ether, 2);
raacToken.approve(address(veRAAC), 2 ether);
veRAAC.lock(2 ether, 366 days);
vm.stopPrank();
vm.startPrank(user2);
raacToken.approve(address(feeCollector), 5 ether);
feeCollector.collectFee(1 ether, 2);
raacToken.approve(address(veRAAC), 2 ether);
veRAAC.lock(2 ether, 366 days);
vm.stopPrank();
vm.startPrank(owner);
feeCollector.distributeCollectedFees();
vm.stopPrank();
uint256 balanceBefore = (raacToken.balanceOf(user));
feeCollector.claimRewards(user);
uint256 balanceAfter = (raacToken.balanceOf(user));
assert(balanceAfter > balanceBefore);
feeCollector.claimRewards(user2);
vm.warp(block.timestamp + 368 days);
vm.prank(user);
veRAAC.withdraw();
vm.prank(user2);
veRAAC.withdraw();
vm.startPrank(user);
raacToken.approve(address(feeCollector), 5 ether);
feeCollector.collectFee(1 ether, 2);
raacToken.approve(address(veRAAC), 2 ether);
veRAAC.lock(2 ether, 366 days);
vm.stopPrank();
vm.startPrank(user2);
raacToken.approve(address(feeCollector), 5 ether);
feeCollector.collectFee(1 ether, 2);
raacToken.approve(address(veRAAC), 2 ether);
veRAAC.lock(2 ether, 366 days);
vm.stopPrank();
vm.expectRevert();
feeCollector.claimRewards(user);
}
}

test can be ran -vvvv so see the revert error.

Impact

Denial of service/wrong amount been claimed

Tool Used

Manual review

Updates

Lead Judging Commences

inallhonesty Lead Judge 3 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.