Beginner FriendlyFoundryDeFi
100 EXP
View results
Submission Details
Severity: high
Valid

[H-02] Users can stake and unstake to farm points

Summary

The code contains an issue where users can exploit the staking system by repeatedly staking and unstaking to farm points. The current implementation does not account for points being deducted when a user unstakes, leading to a potential abuse of the system.

Vulnerability Details

The smart contract includes an unstake function that allows users to remove their staked tokens. However, the current event listener in the JavaScript code only listens to the STAKED event and increments the user's points when they stake tokens. There is no corresponding listener for the UNSTAKED event to reduce the points when a user unstakes. This loophole allows users to stake, earn points, and then unstake without losing the points, thereby enabling them to repeatedly farm points without maintaining a stake.

Impact

This vulnerability allows users to artificially inflate their points by repeatedly staking and unstaking, which could lead to an unfair distribution of rewards, compromise the integrity of the staking system, and potentially affect the overall tokenomics of the project.

Tools Used

  • Vyper smart contract

  • ethers library

  • JavaScript

Recommendations

To mitigate this issue, the code should be updated to listen for the UNSTAKED event and appropriately deduct points from the user's account when they unstake tokens. Below is an example of how the code can be modified to include this logic:

steaking.on(STAKED, async (_, amount, onBehalfOf) => {
let steakPoints = await steakPointsModel.findOne({ walletAddress: onBehalfOf });
const pointsToAdd = parseFloat(ethers.formatEther(amount)) * PRECISION;
if (!steakPoints) {
steakPoints = new steakPointsModel({
walletAddress: onBehalfOf,
points: pointsToAdd,
});
} else {
steakPoints.points += pointsToAdd;
}
await steakPoints.save();
});
steaking.on(UNSTAKED, async (_, amount, onBehalfOf) => {
let steakPoints = await steakPointsModel.findOne({ walletAddress: onBehalfOf });
const pointsToDeduct = parseFloat(ethers.formatEther(amount)) * PRECISION;
if (steakPoints) {
steakPoints.points = Math.max(0, steakPoints.points - pointsToDeduct);
await steakPoints.save();
}
});

This ensures that points are accurately adjusted based on both staking and unstaking actions, preventing users from exploiting the system to farm points.

Updates

Lead Judging Commences

inallhonesty Lead Judge 10 months ago
Submission Judgement Published
Validated
Assigned finding tags:

Steaking server is not taking unstakes into account

Support

FAQs

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