Summary
 $STEAK Token Airdrop point system is vulnerable to gaming. A user can repeatedly stake and unstake countless of times, claiming multiple points for STEAK airdrop token, gaining a massive amount of points.
Vulnerability Details
Affected code here, this code only listens to the stake event, and records points for each stake
https://github.com/Cyfrin/2024-08-steaking/blob/87e81eb617f3bca3dfbd7c6d4c90bdf1c988ebb0/steaking-server/src/main.js#L12-L35
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();
    });
}
Impact
A user can repeatedly stake and unstake countless of times, claiming multiple points for STEAK airdrop token, gaining a massive amount of points which is unacceptable
Tools Used
Manual review
Recommendations
Add a listener for unstaking to the server and reduce points for each unstake, to avoid gamification of point system
const UNSTAKED = 
    steaking.on(UNSTAKED, async (by, amount, _) => {
        let steakPoints;
        steakPoints = await steakPointsModel.findOne({ walletAddress: by });
        if (!steakPoints) {
            return
        } else {
        
            steakPoints.points -= +ethers.formatEther(amount) * PRECISION;
        }
        await steakPoints.save();
    });