If FjordPoints and FjordStaking are deployed in different blocks, resulting in different block timestamps, the epoch switch will occur at different times. This discrepancy creates an opportunity for Points farming without requiring token locking for the lockCycle, allowing users to frontrun the Points distribution.
Because FjordPoints and FjordStaking contracts are deployed independently, they might be deployed in different blocks with varying block timestamps.
Examining the deployment script:
Here, FjordPoints is deployed before FjordStaking. Although Foundry broadcasts these transactions sequentially, there is no guarantee that the FjordStaking deployment will occur in the same block as the FjordPoints deployment. In an edge case, FjordPoints could be included in one block, and due to a gas spike, FjordStaking might be deployed several blocks later.
If FjordPoints and FjordStaking are deployed in different blocks, their epoch start times will differ. This discrepancy can be exploited to farm Points without locking tokens for the lockCycle in FjordStaking.
This is possible because in the FjordStaking contract, users can stake and unstake FJO tokens immediately within the same epoch, as shown in this code:
Consider this scenario:
Alice notices that FjordPoints and FjordStaking are deployed in different blocks, causing their epoch switches to differ.
Alice stakes a large amount of FJO in FjordStaking just before the epoch switch in the FjordPoints contract.
Alice calls FjordPoints.distributePoints() in the first block that allows for new Points distribution (after the epoch switch in the FjordPoints contract) and then calls the unstake function in the same block before the epoch switch in the FjordStaking contract.
Alice is awarded Points from FJO that she didn't need to stake for the full lockCycle duration.
There is no frontrunning protection mechanism for Points distribution other than locking tokens for the lockCycle in the FjordStaking contract, which can be circumvented as shown.
In the FjordStaking contract, frontrunning protection is present in two forms: new rewards are only distributed to stakers from the previous epoch and only for tokens locked for the full lockCycle duration.
Frontrunning of the Points distribution in the FjordPoints contract.
https://github.com/Cyfrin/2024-08-fjord/blob/main/script/forge/DeployStaking.s.sol#L10-L25
https://github.com/Cyfrin/2024-08-fjord/blob/main/src/FjordStaking.sol#L462-L466
https://github.com/Cyfrin/2024-08-fjord/blob/main/src/FjordPoints.sol#L232-L248
Manual review.
The epoch switch times should be aligned for both FjordPoints and FjordStaking contracts.
This can be achieved by either deploying the FjordPoints contract within the FjordStaking contract's constructor:
Or by updating the lastDistribution with FjordStaking.startTime() when calling the FjordPoints.setStakingContract() function:
Impact: High - Users are getting an unreasonable amount of points through exploiting a vulnerability Likelihood: Low - Most of the times, when using the script, all deployment tx will get processed in the same block. But, there is a small chance for them to be processed in different blocks.
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.