This report outlines a critical vulnerability identified in the reward distribution mechanism of FeeCollector
contract. The issue arises from incorrect state updates when users claim their rewards, which prevents users from claiming rewards in subsequent distributions. This vulnerability lead to a denial of service (DoS) for users attempting to claim their rightful rewards.
The vulnerability exists in the claimRewards
function, where the userRewards[user]
state is incorrectly updated to totalDistributed
after a user claims their rewards. This incorrect update causes the user's pending rewards to be calculated as zero in subsequent claims, even if they are eligible for additional rewards.
https://github.com/Cyfrin/2025-02-raac/blob/89ccb062e2b175374d40d824263a4c0b601bcb7f/contracts/core/collectors/FeeCollector.sol#L206
First Claim:
totalDistributed = 1,000,000
userVotingPower = 2,500
totalVotingPower = 50,000
userRewards[user] = 0
share = 2,500 * 1,000,000 / 50,000 = 50,000
(https://github.com/Cyfrin/2025-02-raac/blob/89ccb062e2b175374d40d824263a4c0b601bcb7f/contracts/core/collectors/FeeCollector.sol#L486)
pendingReward = 50,000 - 0 = 50,000
(https://github.com/Cyfrin/2025-02-raac/blob/89ccb062e2b175374d40d824263a4c0b601bcb7f/contracts/core/collectors/FeeCollector.sol#L487)
After the claim, userRewards[user]
is incorrectly set to totalDistributed = 1,000,000
.
Second Claim:
totalDistributed = 2,000,000
(increase by 1,000,000)
userVotingPower = 2,500
totalVotingPower = 50,000
userRewards[user] = 1,000,000
(from last state update)
share = 2,500 * 2,000,000 / 50,000 = 100,000
pendingReward = 0
(since share < userRewards[user]
) (https://github.com/Cyfrin/2025-02-raac/blob/89ccb062e2b175374d40d824263a4c0b601bcb7f/contracts/core/collectors/FeeCollector.sol#L487)
The transaction revert : if (pendingReward == 0) revert InsufficientBalance();
The root cause of the vulnerability is the incorrect update of userRewards[user]
to totalDistributed
instead of the user's current share (share
). This prevents users from claiming incremental rewards in future distributions.
Denial of Service (DoS): Users who have claimed rewards once will be unable to claim rewards in subsequent distributions, even if they are eligible.
Loss of Funds: Users will lose access to their rightful rewards, leading to financial losses and a breach of trust in the system.
run in FeeCollector.test.js
The vulnerability was identified through a thorough manual review
To fix the vulnerability, the userRewards[user]
state should be updated to the user's current share (share
) instead of totalDistributed
. This ensures that users can claim incremental rewards in subsequent distributions.
First Claim:
totalDistributed = 1,000,000
userVotingPower = 2,500
totalVotingPower = 50,000
userRewards[user] = 0
share = 2,500 * 1,000,000 / 50,000 = 50,000
(https://github.com/Cyfrin/2025-02-raac/blob/89ccb062e2b175374d40d824263a4c0b601bcb7f/contracts/core/collectors/FeeCollector.sol#L486)
pendingReward = 50,000 - 0 = 50,000
(https://github.com/Cyfrin/2025-02-raac/blob/89ccb062e2b175374d40d824263a4c0b601bcb7f/contracts/core/collectors/FeeCollector.sol#L487)
After the claim, userRewards[user]
is set to shares = 50,000
.
Second Claim:
totalDistributed = 2,000,000
(increase by 1,000,000)
userVotingPower = 2,500
totalVotingPower = 50,000
userRewards[user] = 50,000
(from last state update)
share = 2,500 * 2,000,000 / 50,000 = 100,000
pendingReward = 100,000 - 50,000 = 50,000
After the claim, userRewards[user]
is set to shares = 100,000
.
The contest is live. Earn rewards by submitting a finding.
This is your time to appeal against judgements on your submissions.
Appeals are being carefully reviewed by our judges.