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

Centralized Control and Staking Dilution leading to Reduced User Incentives and Engagement

Summary

The FjordPoints::setPointsPerEpoch function centralizes control of rewards distribution, allowing the contract owner to adjust rewards at will, which can lead to concerns about fairness. Additionally, the FjordPoints::distributePoints function causes reward dilution as more tokens are staked, especially during the 6-week lock period, resulting in reduced incentives for users to stake. These factors combined can lead to decreased user engagement and dissatisfaction.

Vulnerability Details

Centralization of Points Rewards

The function FjordPoints::setPointsPerEpoch, which allows the contract owner(as onlyOwner Modifier is used) to adjust the FjordPoints::pointsPerEpoch value, centralizes control over the rewards distribution. This centralized control means that the contract owner can unilaterally increase or decrease the points distributed per epoch, impacting the rewards that stakers receive. This lack of decentralization can lead to concerns about fairness and transparency among users, as their rewards are subject to the decisions of a single entity.

function setPointsPerEpoch(
uint256 _points
) external onlyOwner checkDistribution {
if (_points == 0) {
revert();
}
pointsPerEpoch = _points;
}

Dilution of Rewards

The FjordPoints::distributePoints function calculates the points distributed per token (pointsPerToken) by dividing the pointsPerEpoch by the totalStaked amount. As the totalStaked amount increases—especially due to the 6-week lock period enforced by the FjordStaking::lockCycle contract—each staker's share of the rewards decreases. This is because the pointsPerToken value is diluted when more tokens are staked. The inability of users to withdraw their tokens during the lock period exacerbates this dilution, leading to lower rewards and potential dissatisfaction among stakers.

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

Impact

The centralization of reward control and the dilution of rewards due to increased staking can lead to reduced user trust and lower staking incentives, ultimately decreasing overall user engagement and satisfaction.

Poc

Add the below test case in `points.t.sol` and you would see that the amount/factor by which the pointsPerToken is decreased as totalStaked increases.

function testPointsPerTokenDecreases() public {
fjordPoints.setPointsPerEpoch(10 ether);
address user = address(0x2);
uint256 amount = 1000 ether;
vm.startPrank(staking);
fjordPoints.onStaked(user, amount);
vm.stopPrank();

skip(1 weeks);
fjordPoints.distributePoints();
uint256 pointsIncreaseFirst = fjordPoints.pointsPerToken();
vm.startPrank(staking);
fjordPoints.onStaked(user, amount);
vm.stopPrank();
skip(1 weeks);
fjordPoints.distributePoints();
uint256 pointsIncreasesecond = fjordPoints.pointsPerToken();
uint256 DifferenceBetweenSecondAndFirstPointsPerToken = pointsIncreasesecond -
pointsIncreaseFirst;
vm.startPrank(staking);
fjordPoints.onStaked(user, amount);
vm.stopPrank();
skip(1 weeks);
fjordPoints.distributePoints();
uint256 pointsIncreasethird = fjordPoints.pointsPerToken();
uint256 DifferenceBetweenThirdAndSecondPointsPerToken = pointsIncreasethird -
pointsIncreasesecond;
console.log(DifferenceBetweenSecondAndFirstPointsPerToken);
console.log(DifferenceBetweenThirdAndSecondPointsPerToken);
assert(
DifferenceBetweenSecondAndFirstPointsPerToken >
DifferenceBetweenThirdAndSecondPointsPerToken
);
}

Tools Used

Manual

Recommendations

  1. Automated Adjustment of pointsPerEpoch: Implement an automated mechanism to adjust pointsPerEpoch based on predefined metrics such as the total staked amount, the number of active users, or the overall growth of the staking pool. This would remove the need for manual adjustments by a single entity, promoting fairness and transparency.

  2. Incentive Structures for Early Stakers: Consider adding bonus rewards or multipliers for early stakers or those who stake larger amounts. This would encourage users to stake even as the total staked amount grows, helping to counteract the effects of reward dilution.

Updates

Lead Judging Commences

inallhonesty Lead Judge 10 months ago
Submission Judgement Published
Invalidated
Reason: Non-acceptable severity

Support

FAQs

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