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

Wrong return value in FjordStaking::claimReward

Summary

FjordStaking::claimReward allows user to claim reward in 2 different ways depending on _isClaimEarly param. If set to true user will be able to claim rewards immediately but will incur penalty (50%). Otherwise, user will be able to claim full amount after cooldown period of 3 epochs. Furthermore, function is supposed to return rewardAmount and penaltyAmount. However, when _isClaimEarly param is set to false, it does not return the accurate value for rewardAmount.

Vulnerability Details

FjordStaking.sol#L641

In the case when user sets the _isClaimEarly param to false when calling claimReward function , the following function will return (0, 0), even tough the ud.unclaimedRewards is ≠ 0.

function claimReward(bool _isClaimEarly)
external
checkEpochRollover
redeemPendingRewards
returns (uint256 rewardAmount, uint256 penaltyAmount)
{
(...)
if (!_isClaimEarly) {
claimReceipts[msg.sender] =
ClaimReceipt({ requestEpoch: currentEpoch, amount: ud.unclaimedRewards });
emit ClaimReceiptCreated(msg.sender, currentEpoch);
@> return (0, 0);
}
(...)
}

PoC

Add following test in the test/unit/unstake.t.sol:

function testClaimRewardReturnsWrongValue() public {
address user=makeAddr("user");
deal(address(token), user, 2 ether);
//user stakes in the first epoch
vm.startPrank(user);
token.approve(address(fjordStaking), 1 ether);
fjordStaking.stake(1 ether);// user stakes 1 ether
vm.stopPrank();
vm.startPrank(minter);
for (uint256 i = 0; i < 7; i++) {
vm.warp(vm.getBlockTimestamp() + fjordStaking.epochDuration());
fjordStaking.addReward(1 ether);
}
vm.stopPrank();
vm.startPrank(user);
(uint256 rewardAmount, uint256 penaltyAmount)=fjordStaking.claimReward(false);
vm.stopPrank();
(uint256 totalStaked, uint256 unclaimedRewards, uint16 unredeemedEpoch, uint16 lastClaimedEpoch) = fjordStaking.userData(user);
console.log("-----------------------------------------");
console.log("totalStaked", totalStaked);
console.log("unclaimedRewards", unclaimedRewards);
console.log("unredeemedEpoch", unredeemedEpoch);
console.log("lastClaimedEpoch", lastClaimedEpoch);
console.log("-----------------------------------------");
assert(0 == rewardAmount);
assert(0 == penaltyAmount);
}

Impact

The incorrect return values from the claimReward function can mislead users about the amount of rewards they are entitled to. Additionally, this issue poses a greater risk if the function is used in other contracts or integrated systems that depend on its return values. For instance, if an external protocol or automated system relies on the correct rewardAmount value to execute further logic or distribute assets, the incorrect data could impact their operation and as result their users.

Tools Used

Manual code review / Foundry tests

Recommendations

Consider making following changes:

function claimReward(bool _isClaimEarly)
external
checkEpochRollover
redeemPendingRewards
returns (uint256 rewardAmount, uint256 penaltyAmount)
{
(...)
if (ud.unclaimedRewards == 0) revert NothingToClaim();
if (!_isClaimEarly) {
claimReceipts[msg.sender] =
ClaimReceipt({ requestEpoch: currentEpoch, amount: ud.unclaimedRewards });
emit ClaimReceiptCreated(msg.sender, currentEpoch);
- return (0, 0);
+ return (ud.unclaimedRewards, 0);
}
(...)
}
Updates

Lead Judging Commences

inallhonesty Lead Judge
10 months ago
inallhonesty Lead Judge 10 months ago
Submission Judgement Published
Invalidated
Reason: Non-acceptable severity

Support

FAQs

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