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

In the Steaking server, there's no logic to account for unstaking

Summary

In the steaking server, only staking is accounted for. When stakers unstake their WETH from the protocol, there is no logic to deduct appropriate points from stakers record in the database

Vulnerability Details

Since there's no logic for deducting points from the database, a staker can stake, get the staking points, then decide to unstake her ETH without waiting, and she will still be eligible for the airdrop since her points in the database is not affected.

PoC

function testStakeGetPointsAndunstakeInstantly() public {
uint256 totalAmountStakedBefore = steaking.totalAmountStaked();
uint256 dealAmount = 1 ether;
_stake(user1, dealAmount, user1);
_unstake(user1, dealAmount, user1);
uint256 totalAmountStakedAfter = steaking.totalAmountStaked();
assert(totalAmountStakedAfter == 0 && totalAmountStakedBefore == 0);
}

Impact

  • loss of funds for protocol in terms of $stake token due to possible manipulations from stakers

  • little/No liquidity for the protocol

Tools Used

  • manual review

  • foundry test

Recommendations

in the main.js include the following code

import dotenv from "dotenv";
import { ethers } from "ethers";
import connectToMongodb from "./utils/connectToMongodb.js";
import getConfig from "./utils/getConfig.js";
import steakingAbi from "./utils/steakingAbi.js";
import steakPointsModel from "./models/steakPoints.js";
import { STAKED, PRECISION } from "./utils/constants.js";
dotenv.config();
async function main() {
await connectToMongodb();
const { rpcUrl, steakingAddress } = getConfig();
const provider = new ethers.JsonRpcProvider(rpcUrl);
const steaking = new ethers.Contract(steakingAddress, steakingAbi, provider);
steaking.on(STAKED, async (_, amount, onBehalfOf) => {
let steakPoints;
steakPoints = await steakPointsModel.findOne({ walletAddress: onBehalfOf });
if (!steakPoints) {
steakPoints = new steakPointsModel({
walletAddress: onBehalfOf,
points: +ethers.formatEther(amount) * PRECISION,
});
} else {
steakPoints.points += +ethers.formatEther(amount) * PRECISION;
}
await steakPoints.save();
});
+ steaking.on("Unstaked", async (_, amount, onBehalfOf) => {
+ let staker = await steakPointsModel.findOne({ walletAddress: onBehalfOf });
+ let steakPoints = {
+ points: staker && staker.points > 0 ? staker.points - +ethers.formatEther(amount) * PRECISION : 0
+ }
+ await steakPointsModel.updateOne({ walletAddress: onBehalfOf },
+ steakPoints
+ );
+
+ });
}
main().catch((error) => {
console.log(error.message);
process.exit(1);
});
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.