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

Total rewards of epoch 1 are not visible and claimable in epoch 2

Vulnerability Details

Total rewards of epoch 1 are not displayed in epoch 2, and so people will not be able to claim epoch 1 rewards in epoch 2!

This happens because there is a bug in the function 'FjordStaking::_checkEpochRollover'.

En fact, once a new epoch occurs, this function correctly updates the new epoch but when there is a change between epoch 1 and epoch 2, the variable 'totalStaked' at the start of the function '_checkEpochRollover' will remain 0 since this one is udated at the end of the function.

When this happens, the lines of codes 699-709 in 'FjordStaking' will not be executed.

Impact

When this happens, the 'totalRewards' variable in epoch 2 will result 0, also if there have been some stakes and the epoch rolloved.

Also 'rewardPerToken' will be 0 in epoch 2, and so people will not be able to claim their rewards since 'UserData.unclaimedRewards' will return 0.

POC

(Using foundry)

function test_totalRewardsOfEpoch1AreNotDisplayedInEpoch2(uint256 amount) public {
vm.startPrank(minter);
//Give 2000 Fjord to minter
deal(address(fjordToken), minter, 2000e18);
ERC20(fjordToken).approve(address(fjordStaking), 2000e18);
fjordStaking.addReward(2000e18);
vm.stopPrank();
address gianni = address(123);
vm.startPrank(gianni);
deal(address(fjordToken), gianni, 1_000e18); //assigns 1.000 FJORD to Gianni
uint256 startingGianniBalance = ERC20(fjordToken).balanceOf(address(gianni));
//Approve the staking contract to spend FJORD tokens
ERC20(fjordToken).approve(address(fjordStaking), type(uint256).max);
amount = bound(amount, 1, 1_000); // 1 - 1.000 FJORD
//stake in EPOCH 1
fjordStaking.stake(amount);
assertEq(fjordStaking.totalRewards(), 0);
assertEq(fjordStaking.rewardPerToken(1), 0);
//stake in EPOCH 2
skip(7 days);
fjordStaking.stake(amount);
assertEq(fjordStaking.totalRewards(), 0); //Total rewards continue to be 0!
(uint256 totalStaked,uint256 unclaimedRewards,uint16 unredeemedEpoch,uint16 lastClaimedEpoch) = fjordStaking.userData(address(gianni));
assertEq(unclaimedRewards, 0); //Also user unclaimed rewards will be 0, and so user will be not able to claim epoch 1 rewards in epoch 2!
vm.expectRevert();
fjordStaking.claimReward(true); //This call will fail since UserData.unclaimedRewards is 0.
}

Tools Used

Manual Review, Foundry

Recommendations

Consider changing the logic of '_checkEpochRollover' causing 'totalStaked' to update sooner.

Updates

Lead Judging Commences

inallhonesty Lead Judge
about 1 year ago
inallhonesty Lead Judge about 1 year ago
Submission Judgement Published
Invalidated
Reason: Design choice

Support

FAQs

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