Core Contracts

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

`veRaac` rewards can't be properly distributed

Summary

Reward calculations for ve holders calculates the decayed voting power of the claimer against the total undecayed voting power of all holders, including expired ones.

Details

_calculatePendingRewards in FeeLocker distributes accrued rewards based on the % of one user's voting power against the total voting power.

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

getVotingPower checks the current power of the user by taking into consideration the decay via VotingPowerLib.getCurrentPower

if (timeDelta > 0) {
// Calculate decay per second and multiply by time delta
int128 decay = (point.slope * int128(int256(timeDelta))) / int128(int256(1));
adjustedBias = point.bias - decay;
}
// Return 0 if power has fully decayed
return adjustedBias > 0 ? uint256(uint128(adjustedBias)) : 0;

On the other side, getTotalVotingPower only checks the totalSupply of veRAACToken

function getTotalVotingPower() external view override returns (uint256) {
return totalSupply();
}

This creates 2 issues:

  1. Rewards can never be fully distributed since all users would have some decay. Assume that totalSupply = 100 and the sum of all decayed user balances is 80. Every user claims their rewards, yet only 80% of them will be distributed.

  2. Users with expired locks have nothing to claim since getVotingPower(user) will return 0, however their veRAAC is in circulation and contributes to the division by totalVotingPower, thus dilluting reward distribution.

Impact

Unfair reward calculation, unexpected/weird behaviour

Mitigation

Complete revamp of the function is needed.

Updates

Lead Judging Commences

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

veRAACToken::getTotalVotingPower returns non-decaying totalSupply while individual voting powers decay, causing impossible governance quorums and stuck rewards in FeeCollector

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

veRAACToken::getTotalVotingPower returns non-decaying totalSupply while individual voting powers decay, causing impossible governance quorums and stuck rewards in FeeCollector

Support

FAQs

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

Give us feedback!