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

Epoch misalignment allows users to gain points without locking tokens

Summary

A timing discrepancy between the FjordPoints and FjordStaking contracts can be exploited to earn points without effectively locking Fjord tokens, undermining the intended staking mechanism and potentially leading to disproportionate rewards for users who exploit this misalignment.

Vulnerability Details

The FjordPoints contract updates the pointsPerToken value after each EPOCH_DURATION (7 days) to distribute points to stakers proportionate to their staked tokens.

function distributePoints() public {
if (block.timestamp < lastDistribution + EPOCH_DURATION) {
return;
}
if (totalStaked == 0) {
return;
}
uint256 weeksPending = (block.timestamp - lastDistribution) / EPOCH_DURATION;
pointsPerToken =
pointsPerToken.add(weeksPending * (pointsPerEpoch.mul(PRECISION_18).div(totalStaked)));
totalPoints = totalPoints.add(pointsPerEpoch * weeksPending);
lastDistribution = lastDistribution + (weeksPending * 1 weeks);
emit PointsDistributed(pointsPerEpoch, pointsPerToken);
}

https://github.com/Cyfrin/2024-08-fjord/blob/14cab810598ddda6008d9523d0ed4a428b1b1153/src/FjordPoints.sol#L232-L248

In unstake function, users are allowed to unstake immediately without locking if the epoch is the same as the current epoch.

function unstake(uint16 _epoch, uint256 _amount)
external
checkEpochRollover
redeemPendingRewards
returns (uint256 total)
{
...
// _epoch is same as current epoch then user can unstake immediately
if (currentEpoch != _epoch) { // @audit allow to unstake immediately
// _epoch less than current epoch then user can unstake after at complete lockCycle
if (currentEpoch - _epoch <= lockCycle) revert UnstakeEarly();
}
//EFFECT
dr.staked -= _amount;
if (currentEpoch != _epoch) {
totalStaked -= _amount;
userData[msg.sender].totalStaked -= _amount;
} else {
// unstake immediately
newStaked -= _amount;
}
...
}

https://github.com/Cyfrin/2024-08-fjord/blob/14cab810598ddda6008d9523d0ed4a428b1b1153/src/FjordStaking.sol#L449-L494

From the deployment script we can see that the deployment transactions of FjordPoints and FjordStaking are two different transactions. If the epoch start times for the FjordPoints and FjordStaking contracts differ, a user can exploit this by staking just before the FjordPoints contract updates pointsPerToken and unstaking immediately afterward. This allows the user to accumulate points without locking their Fjord tokens for the full duration

Exploit Scenario:

  • FjordPoints contract starts its epoch at t1.

  • FjordStaking contract starts its epoch at t2 (where t2 > t1, e.g., 12 seconds later due to block timing).

  • FjordPoints updates pointsPerToken at t1 + 7 days.

  • FjordStaking ends its epoch at t2 + 7 days.

  • A user stakes at t1 + 7 days - 12 seconds and unstakes at t1 + 7 days (Although this is still within the same epoch in FjordStaking, it transitions to the next epoch in FjordPoints.).

t1 ---> t2 ----> t1 + 7 days ----> t2 + 7 days

|------------------|------------------|

Stake & Unstake window

By exploiting this window, the user gains points for the entire epoch without effectively locking their tokens.

Impact

This vulnerability allows users to accumulate points without adhering to the intended locking mechanism, leading to an unfair distribution of rewards. Given that the total points per epoch are fixed, a large-scale token holder could exploit this flaw to disproportionately accumulate points, effectively reducing the rewards available to honest participants.

Tools Used

Manual

Recommendations

To prevent this exploit, ensure that both the FjordPoints and FjordStaking contracts synchronize their epoch start times by passing the epoch start time as a parameter from one contract to the other, ensuring both operate on an identical schedule.

Updates

Lead Judging Commences

inallhonesty Lead Judge
about 1 year ago
inallhonesty Lead Judge about 1 year ago
Submission Judgement Published
Invalidated
Reason: Out of scope

Appeal created

ast3ros Submitter
about 1 year ago
inallhonesty Lead Judge
about 1 year ago
inallhonesty Lead Judge about 1 year ago
Submission Judgement Published
Validated
Assigned finding tags:

If epoch end times of FjordStaking and FjordPoints are desynchronized, users will be able to exploit the desynchronization to stake>claim>unstake instantly, getting points they shouldn't

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.

Support

FAQs

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