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

Unfair point distribution in `FjordPoints` due to snapshot-based calculation

Summary

FjordPoints contract's point distribution mechanism is based on a snapshot of staked tokens at the time of distribution, rather than considering the duration of staking within each epoch. This creates an avenue for users to game the protocol by staking large amounts just before distribution and unstaking immediately after.

Vulnerability Details

The distributePoints() function updates global variables like pointsPerToken and totalPoints based on the current totalStaked amount. However, it doesn't account for when users staked or unstaked within an epoch. Points are only calculated and assigned to users when they interact with the contract (stake, unstake, or claim points) using the updatePendingPoints modifier.

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);
}

This causses 2 main problems;

  1. Users who stake just before distributePoints() is called can receive points for the entire epoch, even if they only staked for a brief period.

  2. Users who unstake just before distributePoints() is called may not receive points for the period they had tokens staked.

For example:

  • Alice stakes 100 tokens for a full week (epoch).

  • Bob stakes 1000 tokens just before distribution.

  • After distribution, Bob would receive 10 times more points than Alice, despite only staking for a very short time which shouldnt be so.

Impact

Unfair distribution of points, not accurately reflecting users' staking commitment over time. This opens up honeypot oppportunities for malicious users who time their stakes to maximize point earnings without long-term commitment which isn't what the protocol would want and if thats the case, then there's no incentive for long-time commitment.

Tools Used

Manual

Recommendations

consider implementing a more granular point distribution system that accounts for the duration of staking within each epoch. Something like a checkpoint system that records changes in user stakes and global total stakes at each interaction and modify the point calculation logic to consider the time-weighted average of a user's staked amount over the entire epoch, rather than just the final amount at distribution time.

Another point I could think of is to distribute points more frequently, such as every block or every few blocks, rather than once per epoch. This would minimize the impact of users staking or unstaking just before the distribution. Downside of this approach is that it'll increase gas costs due to more frequent point calculations and updates.

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.