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

Wei Deposits Fail to Receive Points and causes the pointsPerEpoch to be less in every epoch for other users

Summary

When a user stakes with 1 wei, it causes the total points per epoch for other users to decrease. However, the user staking 1 wei does not receive any points themselves due to the calculation method, which results in their claimable points always being zero. This leads to a consistent reduction in claimable points for all other users in the epochs and pointsPerEpoch not being accurate among users.

Vulnerability Details

The vulnerability arises when users stake extremely small amounts of tokens, such as 1 wei, in the staking process. In such cases, the protocol calculates points based on the proportion of tokens staked relative to the total staked amount. However, due to the very small stake amount, the calculation effectively rounds down to zero, resulting in no points being awarded to the user with the 1 wei stake.

Despite not receiving any points themselves, the 1 wei stake impacts the overall distribution of points across all stakers. The presence of these minimal stakes causes the pointsPerEpoch—total points distributed per epoch—to be slightly reduced for all users. This happens because the protocol allocates a portion of points to the 1 wei stake in the Points program, but due to rounding errors or the precision limitations in the calculation, these points are effectively lost.

PoC

  • Copy the Poc and paste it inside the test/unit/points.t.sol

  • The PoC is checking rewards for 5 epochs

function testClaimPointsOfwei() public {
address users = address(0x2);
address weiUser = address(0x3);
uint256 usersStakeAmount = 1000 ether;
// changing this to 100 wei is when the weiUser can be able to mint the points accordingly,still the amount received by users will still be the same as when weiUser stakes 1wei/10wei/100wei
uint256 weiUserStake = 1 wei;
vm.startPrank(staking);
fjordPoints.onStaked(users, usersStakeAmount);
fjordPoints.onStaked(weiUser, weiUserStake);
vm.stopPrank();
skip(1 weeks); // 1st epoch passed
fjordPoints.distributePoints();
vm.prank(users);
fjordPoints.claimPoints();
vm.prank(weiUser);
fjordPoints.claimPoints();
console.log("Users first Epoch Rewards: ", fjordPoints.balanceOf(users));
console.log("weiUser first Epoch Rewards: ", fjordPoints.balanceOf(weiUser));
skip(1 weeks); // 2rd epoch passed
fjordPoints.distributePoints();
vm.prank(users);
fjordPoints.claimPoints();
vm.prank(weiUser);
fjordPoints.claimPoints();
console.log("Users second Epoch Rewards: ", fjordPoints.balanceOf(users));
console.log("weiUser second Epoch Rewards: ", fjordPoints.balanceOf(weiUser));
skip(1 weeks); // 3rd epoch passed
fjordPoints.distributePoints();
vm.prank(users);
fjordPoints.claimPoints();
vm.prank(weiUser);
fjordPoints.claimPoints();
console.log("Users third Epoch Rewards: ", fjordPoints.balanceOf(users));
console.log("weiUser third Epoch Rewards: ", fjordPoints.balanceOf(weiUser));
skip(1 weeks); // 4th epoch passed
fjordPoints.distributePoints();
vm.prank(users);
fjordPoints.claimPoints();
vm.prank(weiUser);
fjordPoints.claimPoints();
console.log("Users fourth Epoch Rewards: ", fjordPoints.balanceOf(users));
console.log("weiUser fourth Epoch Rewards: ", fjordPoints.balanceOf(weiUser));
skip(1 weeks); // 5th epoch passed
fjordPoints.distributePoints();
vm.prank(users);
fjordPoints.claimPoints();
vm.prank(weiUser);
fjordPoints.claimPoints();
console.log("Users fifth Epoch Rewards: ", fjordPoints.balanceOf(users));
console.log("weiUser fifth Epoch Rewards: ", fjordPoints.balanceOf(weiUser));
console.log("Contract Points Balance: ", fjordPoints.balanceOf(address(this)));
}
  • Output

[PASS] testClaimPointsOfwei() (gas: 340469)
Logs:
Users first Epoch Rewards: 99999999999999999000
weiUser first Epoch Rewards: 0
Users second Epoch Rewards: 199999999999999998000
weiUser second Epoch Rewards: 0
Users third Epoch Rewards: 299999999999999997000
weiUser third Epoch Rewards: 0
Users fourth Epoch Rewards: 399999999999999996000
weiUser fourth Epoch Rewards: 0
Users fifth Epoch Rewards: 499999999999999995000
weiUser fifth Epoch Rewards: 0
Contract Points Balance: 0

Impact

The primary impact of this vulnerability is that it reduces the rewards for users who stake significant amounts of tokens. When users stake extremely small amounts, such as 1 wei, they decrease the total pointsPerEpoch distributed to all stakers

Users who stake amounts as small as 1 wei do not receive any points because their stake is too insignificant to contribute meaningfully to the pool. The calculation effectively rounds down to zero, meaning these small stakers earn no points which means not all the points from pointsPerEpoch gets distributed.

Tools Used

Manual Review

Recommendations

Restrict users that won't be able to earn points due to their small wei stake by not including them into the points process (onStake), they can keep staking until they have a totalstake that can earn points and those points that would not be sent on small wei stakers will be put to use, if not it will disrupt the experience of users with larger amounts.

Updates

Lead Judging Commences

inallhonesty Lead Judge about 1 year ago
Submission Judgement Published
Invalidated
Reason: Non-acceptable severity

Support

FAQs

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