Stakers earn both Fjord Token and points when they stake on the FjordStaking
staking contract. A user can stake and unstake their Fjord token in the same epoch, this exposes a vulnerability in the FjordPoints.sol
contract, as the user can stake a few minutes before points are distributed and earn points as if they have staked for 1 whole week. The moment points are distributed they will claim their points and unstake immediately, this can be done within 5 minutes. A user who stakes for just 5 minutes will get the same rewards as someone who has been in the system for days and also remove their staked tokens, which is like receiving free money.
The stake function calls the points.onStaked(msg.sender, _amount)
function on the point contracts, which records the amount of the user's stake in the point contract, this amount is used to distribute rewards.
So users earn per staked amount.
https://github.com/Cyfrin/2024-08-fjord/blob/main/src/FjordStaking.sol#L388
Let's take a look at the onStaked
function, it updates the user staked amount and total Staked in the contract, it also runs the checkDistribution
and updatePendingPoints
modifier.
https://github.com/Cyfrin/2024-08-fjord/blob/main/src/FjordPoints.sol#L197
The checkDistribution
calls the distributePoints
function, let's take a look at the distribution function we can see that the distributePoints
function only updates the rewards when the last distribution time plus the EPOCH_DURATION
(1 week) is greater than the current block.timestamp
.
https://github.com/Cyfrin/2024-08-fjord/blob/main/src/FjordPoints.sol#L232
So if the user stakes 3 minutes to the end of the epoch the distributePoints
won't update the pointsPerToken i.e the block.timestamp
is 3 minutes less than lastDistribition
+ EPOCH_DURATION
, the user can wait for 3 minutes after staking to call the distributePoints
so they can earn points in just 5 minutes of staking.
After they have claimed their rewards on the point contract they can call the unstake
function on the staking contract and withdraw their tokens. This transaction will go through because the staking and unstaking are done on the same epoch.
Check the unstake function here, we can see that it allows users to unstake in the same epoch that they staked on.
https://github.com/Cyfrin/2024-08-fjord/blob/main/src/FjordStaking.sol#L473
Staker will earn rewards even for periods they didn't stake for.
People can earn points without locking their funds.
Manual Analysis.
Do not allow users to stake and unstake in the same epoch that they staked for. Do this for the unstake
and unstakeVested
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.