#41524 [SC-Critical] Incorrect Reward Calculation in accumulatedDeptRewardsYeet() Function Leads to Loss of User Funds During Vesting Period

Submitted on Mar 16th 2025 at 08:34:54 UTC by @InquisitorScythe for Audit Comp | Yeet

  • Report ID: #41524

  • Report Type: Smart Contract

  • Report severity: Critical

  • Target: https://github.com/immunefi-team/audit-comp-yeet/blob/main/src/StakeV2.sol

  • Impacts:

    • Direct theft of any user funds, whether at-rest or in-motion, other than unclaimed yield

Description

Brief/Intro

The accumulatedDeptRewardsYeet() function calculates rewards incorrectly by using a simple formula (stakingToken.balanceOf(address(this)) - totalSupply) that fails to account for tokens in vesting periods. This critical vulnerability allows the protocol to mistakenly identify users' vesting tokens as distributable rewards. When a manager calls executeRewardDistributionYeet(), these misidentified tokens can be permanently redirected to the vault, resulting in direct theft of users' funds that are in the unstaking process. If exploited in production, this could lead to substantial financial losses for users with active vestings, undermining protocol solvency and user trust.

Vulnerability Details

The vulnerability stems from a fundamental accounting error in the accumulatedDeptRewardsYeet() function, which calculates distributable rewards incorrectly:

function accumulatedDeptRewardsYeet() public view returns (uint256) {
    return stakingToken.balanceOf(address(this)) - totalSupply;
}

This function assumes that any balance of stakingToken held by the contract beyond the totalSupply represents distributable rewards. However, this fails to account for tokens that are in the vesting process after users have called startUnstake().

When a user calls startUnstake(), the following occurs:

  1. Their balance in balanceOf mapping is reduced

  2. The contract's totalSupply is reduced

  3. A new Vesting entry is created and tokens enter a vesting period (typically 10 days)

  4. Critically, the tokens remain in the contract until the vesting period ends

The vulnerability exists because tokens in vesting are no longer counted in totalSupply but still contribute to stakingToken.balanceOf(address(this)). This discrepancy causes accumulatedDeptRewardsYeet() to incorrectly include vesting tokens as distributable rewards.

The vulnerable flow is triggered when:

  1. Users stake tokens, increasing totalSupply

  2. Users call startUnstake(), which decreases totalSupply but keeps tokens in the contract

  3. accumulatedDeptRewardsYeet() incorrectly reports these vesting tokens as rewards

  4. A manager calls executeRewardDistributionYeet(), which:

  5. The function incorrectly approves zapper to spend tokens that actually belong to users in vesting

  6. The zapper converts these tokens to LP positions in the vault, permanently removing them from the contract

This vulnerability is particularly severe because:

  1. It allows direct theft of user funds that are in vesting

  2. It can be exploited by a malicious or misinformed manager

  3. Users have no means to prevent their vesting funds from being taken

  4. The larger the volume of unstaking, the larger the potential funds misdirected

The absence of proper accounting for vesting tokens represents a critical design flaw in the contract's reward calculation logic.

Impact Details

This vulnerability poses a severe risk to user assets and protocol integrity, with the following specific impacts:

  1. Direct Loss of User Funds:

    • When a manager calls executeRewardDistributionYeet(), tokens belonging to users in the vesting process can be permanently converted to vault shares

    • 100% of vesting tokens could be misappropriated if distributed

    • Example: If users collectively have 1,000,000 tokens in vesting and a manager distributes all "rewards," users would lose their entire vesting amount

  2. Protocol Insolvency:

    • As users complete their vesting period and attempt to claim tokens via the unstake() function, the contract will lack sufficient tokens to fulfill these claims

    • The contract would eventually become insolvent, unable to honor user withdrawals

    • This creates a "bank run" scenario where users who unstake early receive their tokens, while later users receive nothing

The vulnerability represents a Critical severity issue as it leads to direct theft of user funds, creates systemic insolvency, and undermines the core economic guarantees of the protocol. The impact is immediate and requires no complex conditions to exploit - simply normal operation of the contract by managers who believe they are distributing legitimate rewards.

References

none

Proof of Concept

Proof of Concept

add following test in test/StakeV2.test.sol

run the test with forge test ./test/StakeV2.test.sol --match-test test_executeRewardDistributionYeet_vulnerability -vv

output:

Was this helpful?