DeFiFoundry
20,000 USDC
View results
Submission Details
Severity: medium
Invalid

Incorrect Reward and Penalty Deduction Logic Leading to Potential Depletion of Rewards Pool

Summary

In FjordStaking.sol line https://github.com/Cyfrin/2024-08-fjord/blob/0312fa9dca29fa7ed9fc432fdcd05545b736575d/src/FjordStaking.sol#L650

function claimReward(
bool _isClaimEarly
)
external
checkEpochRollover
redeemPendingRewards
returns (uint256 rewardAmount, uint256 penaltyAmount)
{
//CHECK
UserData storage ud = userData[msg.sender];
// do not allow to claimReward while user have pending claimReceipt
// or user have claimed from the last epoch
if (
claimReceipts[msg.sender].requestEpoch > 0 ||
claimReceipts[msg.sender].requestEpoch >= currentEpoch - 1
) revert ClaimTooEarly();
if (ud.unclaimedRewards == 0) revert NothingToClaim();
//EFFECT
if (!_isClaimEarly) {
claimReceipts[msg.sender] = ClaimReceipt({
requestEpoch: currentEpoch,
amount: ud.unclaimedRewards
});
emit ClaimReceiptCreated(msg.sender, currentEpoch);
return (0, 0);
}
`rewardAmount = ud.unclaimedRewards;
penaltyAmount = rewardAmount / 2;
rewardAmount -= penaltyAmount;
if (rewardAmount == 0) return (0, 0);
totalRewards -= (rewardAmount + penaltyAmount);
userData[msg.sender].unclaimedRewards -= (rewardAmount + penaltyAmount);
`
//INTERACT
fjordToken.safeTransfer(msg.sender, rewardAmount);
//@audit rewardAmountu gönderiyor ancak penalty varsa hatalı olmaz mı?
emit EarlyRewardClaimed(msg.sender, rewardAmount, penaltyAmount);
}

After the penalty is deducted, the remaining amount rewardAmount is transferred to the user. However, in the previous step, totalRewards and unclaimedRewards are reduced, including the penalty. In this scenario, even though the penalty amount is deducted from the total reward pool of tokens, there is a situation where the payment is not made to the user.

Vulnerability Details

In the claimReward function, there is a logical inconsistency that can lead to an incorrect reduction of the total rewards pool. Specifically, when a user claims their reward early and incurs a penalty, both the reward amount (after penalty deduction) and the penalty amount are subtracted from the totalRewards and unclaimedRewards variables. However, only the reward amount is transferred to the user, while the penalty amount is not. This results in an unintended reduction of the totalRewards by the penalty amount, even though the penalty is not actually paid out.

The issue arises because the totalRewards is reduced by both the reward amount and the penalty amount, even though the penalty amount is not transferred to the user. This leads to a discrepancy where the penalty tokens are effectively "lost" from the rewards pool, reducing the total available rewards more than intended.

Impact

This vulnerability can lead to an inaccurate reduction of the totalRewards, which could deplete the rewards pool faster than anticipated. Over time, this could result in fewer rewards being available for other users, ultimately affecting the fairness and integrity of the reward distribution system.

Tools Used

Manual review

Recommendations

To resolve this issue, the code should be revised so that only the reward amount is subtracted from the totalRewards, while the penalty amount is subtracted solely from the user's claimable rewards. This ensures that the penalty amount remains in the rewards pool and is not prematurely deducted.

function claimReward(
bool _isClaimEarly
)
external
checkEpochRollover
redeemPendingRewards
returns (uint256 rewardAmount, uint256 penaltyAmount)
{
//CHECK
UserData storage ud = userData[msg.sender];
// do not allow to claimReward while user have pending claimReceipt
// or user have claimed from the last epoch
if (
claimReceipts[msg.sender].requestEpoch > 0 ||
claimReceipts[msg.sender].requestEpoch >= currentEpoch - 1
) revert ClaimTooEarly();
if (ud.unclaimedRewards == 0) revert NothingToClaim();
//EFFECT
if (!_isClaimEarly) {
claimReceipts[msg.sender] = ClaimReceipt({
requestEpoch: currentEpoch,
amount: ud.unclaimedRewards
});
emit ClaimReceiptCreated(msg.sender, currentEpoch);
return (0, 0);
}
rewardAmount = ud.unclaimedRewards;
penaltyAmount = rewardAmount / 2;
rewardAmount -= penaltyAmount;
if (rewardAmount == 0) return (0, 0);
- totalRewards -= (rewardAmount + penaltyAmount);
+ totalRewards -= rewardAmount; // Only deduct the transferred reward amount from the total rewards
userData[msg.sender].unclaimedRewards -= (rewardAmount + penaltyAmount);
//INTERACT
fjordToken.safeTransfer(msg.sender, rewardAmount);
//@audit rewardAmountu gönderiyor ancak penalty varsa hatalı olmaz mı?
emit EarlyRewardClaimed(msg.sender, rewardAmount, penaltyAmount);
}

With this adjustment, the totalRewards will only decrease by the amount actually paid out to the user, preserving the integrity of the rewards pool. The penalty amount will remain within the pool, ensuring that it is not mistakenly deducted without being transferred.

Updates

Lead Judging Commences

inallhonesty Lead Judge
about 1 year ago
inallhonesty Lead Judge about 1 year ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement

Support

FAQs

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