Users are able to commit their Fjord Tokens in the FjordStaking contract in promise for rewards using the stake() function. This function simply takes an amount of tokens to stake as parameter and will be staked for the running currentEpoch. The rewards accrued will depend on the total amount of tokens staked for this particular epoch period (the period increases every week). This means users will get rewarded more or less depending on the epoch they staked their tokens.
Their commited tokens can be unstaked using the unstake() function but is only possible after a lockCycle period of 6 epochs have passed. So users have to stake their tokens for at least 7 weeks (7 epochs) before being able to retrieve them.
Since the protocol acts based on a 1 week cycle, users are not incentivized to stake their tokens early during an epoch since they are rewarded equally if they staked at the beginning of the epoch or at the very last second of the epoch.
Fjord accounts for the current epoch using the currentEpoch state variable. Every user's operation has the potential to increase it thanks to the checkEpochRollover modifier that calls _checkEpochRollover().
https://github.com/Cyfrin/2024-08-fjord/blob/main/src/FjordStaking.sol#L691-L696
Because the protocol uses a 1 week epoch as time referral for reward distribution, users will be more likely to stake as late as possible during an epoch. This way, they can keep their Fjord Tokens for longer before staking them and are restricted to the same limitations regarding reward claiming period and unstaking period as early stakers.
However, transactions on Ethereum can take a very long time to be included in a block, meaning users may find their stake() transaction to be executed in an epoch they did not intended to.
Take the ideal scenario where the protocol is at the beginning of epoch 5:
the user wants to stake its tokens to get rewards
rather than commiting them right away, he uses them in another protocol to generate yield since the rewards are the same whether user is an early staker or not
20 seconds before Fjord can transition to epoch 6, the user calls stake() and its tokens are staked for epoch 5
now the user has to wait for 6 weeks + 20 seconds before being able to call unstake()
Now take the worst scenario where the protocol is still at the beginning of epoch 5:
the user wants to stake its tokens to get rewards
rather than commiting them right away, he uses them in another protocol to generate yield since the rewards are the same whether user is an early staker or not
20 second before Fjord can transition to epoch 6, the user calls stake()
unfortunately, his transaction is delayed by 30 seconds
the user's transaction takes place during epoch 6 and the checkEpochRollover modifier increases currentEpoch to 6
the user's stake is commited for epoch 6, meaning it can't benefit from the rewards of epoch 5
now the user has to wait for 7 weeks - 30 seconds before being able to call unstake()
Not only this could delay to unstake period but it also affects the reward claim period.
Strategic investors may stake their tokens in an un-ideal epoch resulting in less profit from staking rewards.
Manual review
Add an additional parameter _epoch to the stake() and stakeVested() functions and verify the currentEpoch equals to it before executing the staking actions
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.