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

Fjord Points Unfairly Distributed Due to Epoch-Based Calculation

Summary

The FjordPoints contract's method for distributing points is vulnerable to exploitation. The current implementation allows users to stake just before an epoch flip and still receive the same amount of points as users who have staked throughout the entire epoch. This behavior is detrimental to loyal stakers, as it enables opportunistic participants to benefit from rewards without contributing fairly. The issue arises because the pointsPerToken calculation is not updated granularly based on changes in totalStaked, but rather at the end of each epoch.

Vulnerability Details

The root cause of the vulnerability is that the pointsPerToken is updated only at the end of an epoch, regardless of changes in the totalStaked amount during that epoch. This allows participants to stake just before an epoch flip and collect the same points. Let us walk through the issue with the following scenario:

  1. Alice stakes her tokens at the beginning of an epoch and keeps them staked until the end.

  2. Bob notices the epoch is about to flip and stakes his tokens right before the end.

  3. As a result, both Alice and Bob receive the same amount of pointsPerToken, even though Alice's tokens were staked for the entire epoch and Bob's were staked only briefly.

Snippets

  • Found in src/FjordPoints.sol at Line 151

    @>: constant pointsPerToken within the same epoch allows late participants to enjoy the same share of pointsPerToken even as late as epoch end.

    146: modifier updatePendingPoints(address user) {
    ...
    150: userInfo.pendingPoints = userInfo.pendingPoints.add(owed);
    151:@> userInfo.lastPointsPerToken = pointsPerToken;
    152: _;
    153: }
  • Found in src/FjordPoints.sol at Line 243

    @>: it's possible to snip the points at the end of an epoch without staking the entire time since pointsPerToken is not accrued granularity every time totalStaked changes but waits until the epoch flip.

    232: function distributePoints() public {
    ...
    242: pointsPerToken =
    243:@> pointsPerToken.add(weeksPending * (pointsPerEpoch.mul(PRECISION_18).div(totalStaked)));
    244: totalPoints = totalPoints.add(pointsPerEpoch * weeksPending);
    ...
    248: }

Impact

This vulnerability is severe as it undermines the incentive for long-term staking, leading to unfair rewards distribution. Users who stake their tokens for the entire epoch receive the same rewards as those who stake briefly just before the epoch flip. This behavior can cause financial loss for loyal stakers, discourage participation, and damage the protocol’s reputation.

Tools Used

Manual Review

Recommendations

The distributePoints function should be modified to accrue pointsPerToken every time totalStaked changes. This would ensure that points are distributed fairly based on the actual duration of staking, preventing late stakers from unfairly benefiting from the system.

Updates

Lead Judging Commences

inallhonesty Lead Judge 10 months ago
Submission Judgement Published
Invalidated
Reason: Design choice

Support

FAQs

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