Core Contracts

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

Incorrect Reward Calculation in FeeCollector Contract

Summary

The FeeCollector contract incorrectly distributes RAAC rewards to veRAAC holders by using totalDistributed, which includes all past distributions. This allows new veRAAC holders to claim rewards as if they had been holding from the beginning, leading to unfair distribution and potential exploitation.

Vulnerability Details

  1. Fees Collected

  • The FeeCollector contract collects RAAC tokens and distributes them across four categories:

    • veRAAC Holders (rewards)

    • Burn

    • Repair Fund

    • Treasury

2.Distributing Rewards to veRAAC Holders

  • During FeeCollector::distributeCollectedFees, a portion of fees (shares[0]) is allocated for veRAAC holders.

  • The total amount allocated so far is stored in totalDistributed.

FeeCollector::_processDistributions:

function _processDistributions(uint256 totalFees, uint256[4] memory shares) internal {
uint256 contractBalance = raacToken.balanceOf(address(this));
if (contractBalance < totalFees) revert InsufficientBalance();
if (shares[0] > 0) {
uint256 totalVeRAACSupply = veRAACToken.getTotalVotingPower();
if (totalVeRAACSupply > 0) {
TimeWeightedAverage.createPeriod(
distributionPeriod,
block.timestamp + 1,
7 days,
shares[0],
totalVeRAACSupply
);
@> totalDistributed += shares[0];
} else {
shares[3] += shares[0]; // Add to treasury if no veRAAC holders
}
}
if (shares[1] > 0) raacToken.burn(shares[1]);
.......

3.User Claims Rewards which Incorrectly calculate rewards

  • A user calls FeeCollector::claimRewards, which:

  • Calls FeeCollector::_calculatePendingRewards to determine how much RAAC they can claim.

  • FeeCollector::_calculatePendingRewards computes rewards as (totalDistributed * userVotingPower) / totalVotingPower .

  • Issue: totalDistributed includes all past distributions, even from before the user had voting power.

  • Updates userRewards[user] = totalDistributed; to mark rewards as claimed.

FeeCollector::claimRewards:

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::_calculatePendingRewards:

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;
}

4.Exploitation Scenario

  • Early users claim rewards normally.

  • A new user acquires veRAAC tokens later, but the calculation still includes all past distributions.

  • This user receives rewards from past distributions, even though they had no voting power at that time.

Impact

  1. Unfair distribution: New veRAAC holders receive rewards from past distributions they were never part of.

  2. Dilution of rewards: Early participants receive less, as the total rewards are unfairly shared.

Tools Used

Manual

Updates

Lead Judging Commences

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

Time-Weighted Average Logic is Not Applied to Reward Distribution in `FeeCollector`

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

Time-Weighted Average Logic is Not Applied to Reward Distribution in `FeeCollector`

Support

FAQs

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

Give us feedback!