> For the complete documentation index, see [llms.txt](https://reports.immunefi.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://reports.immunefi.com/yeet/41521-sc-critical-unstaked-tokens-incorrectly-counted-as-rewards-during-vesting-period.md).

# #41521 \[SC-Critical] Unstaked tokens incorrectly counted as rewards during vesting period

**Submitted on Mar 16th 2025 at 07:55:16 UTC by @merlinboii for** [**Audit Comp | Yeet**](https://immunefi.com/audit-competition/audit-comp-yeet)

* **Report ID:** #41521
* **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

**An accounting error in `StakeV2` causes unstaked tokens in vesting to be incorrectly counted as rewards**. This could lead to **unintended loss of funds for stakers** and **potential insolvency for the protocol**.

## Vulnerability Details

The `accumulatedDeptRewardsYeet()` function **does not account for unstaked tokens during the vesting period**:

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

Since `totalSupply` is reduced immediately for the `unstakeAmount` at `startUnstake()`, while the unstaked amount remains in the contract during vesting, **`accumulatedDeptRewardsYeet()` mistakenly includes this unstaked amount as part of the rewards**. Consequently, **when `executeRewardDistributionYeet()` is called, these mistakenly counted rewards are distributed to stakers, leading to an unintended loss of funds**.

```solidity
function startUnstake(uint256 unStakeAmount) external {
    --- SNIPPED ---

    balanceOf[msg.sender] -= unStakeAmount;
@>  totalSupply -= unStakeAmount;

    uint256 start = block.timestamp;
    uint256 end = start + VESTING_PERIOD;
    vestings[msg.sender].push(Vesting(unStakeAmount, start, end));
    stakedTimes[msg.sender]++;  
    emit VestingStarted(msg.sender, unStakeAmount, vestings[msg.sender].length - 1);
}
```

## Impact Details

Consider the following scenario:\
0\. Users A and B each stake 100e18 YEET\
\- Contract State:\
\- `YEET.balance(StakeV2)`: 200e18\
\- `totalSupply`: 200e18\
\- Real rewards: 0

1. B starts unstaking 100e18 YEET
   * Contract State:
     * `YEET.balance(StakeV2)`: 200e18 (unchanged)
     * `totalSupply`: 100e18 (reduced by B's unstake)
     * Tokens in vesting: 100e18 (B's unstaking amount)
2. Manager Distributes "Rewards":
   * `accumulatedDeptRewardsYeet()` returns: 200e18 - 100e18 = 100e18
   * These rewards (actually B's vesting tokens) are distributed
   * Contract State:
     * `YEET.balance(StakeV2)`: 100e18
     * `totalSupply`: 100e18
     * **Tokens distributed as "rewards": 100e18**
3. B finalizes unstake at vesting period ends and withdraws their 100e18 YEET
   * Final Contract State:
     * `YEET.balance(StakeV2)`: 0
     * `totalSupply`: 100e18 (A's stake)
     * **Result: A's stake becomes unbacked by tokens**

This **scenario demonstrates the following impacts**:

1. **User A suffers a complete loss of their 100e18 YEET stake** as they cannot unstake due to insufficient contract balance
2. **The protocol becomes technically insolvent** as it owes User A 100e18 YEET but has 0 balance
3. **The lack of vesting token tracking allows the manager to unknowingly distribute vesting tokens as rewards through `accumulatedDeptRewardsYeet()`, creating a HIGH likelihood**.

### The severity assessment

This issue qualifies as `Direct theft of any user funds` because:

1. Stakers lose their entire principal investment
2. The loss occurs while funds are at-rest in the staking contract
3. The vulnerability allows one user (B) to profit at the expense of another (A)

## References

<https://github.com/immunefi-team/audit-comp-yeet/blob/main/src/StakeV2.sol#L247-L262\\>
<https://github.com/immunefi-team/audit-comp-yeet/blob/main/src/StakeV2.sol#L148-L150>

## Proof of Concept

## Proof of Concept

1. Users A and B each stake 100e18 YEET
   * Contract State:
     * `YEET.balance(StakeV2)`: 200e18
     * `totalSupply`: 200e18
     * Real rewards: 0
2. B starts unstaking 100e18 YEET
   * Contract State:
     * `YEET.balance(StakeV2)`: 200e18 (unchanged)
     * `totalSupply`: 100e18 (reduced by B's unstake)
     * Tokens in vesting: 100e18 (B's unstaking amount)
3. Manager Distributes "Rewards":
   * `accumulatedDeptRewardsYeet()` returns: 200e18 - 100e18 = 100e18
   * These rewards (actually B's vesting tokens) are distributed
   * Contract State:
     * `YEET.balance(StakeV2)`: 100e18
     * `totalSupply`: 100e18
     * **Tokens distributed as "rewards": 100e18**
4. B finalizes unstake at vesting period ends and withdraws their 100e18 YEET
   * Final Contract State:
     * `YEET.balance(StakeV2)`: 0
     * `totalSupply`: 100e18 (A's stake)
     * **Result: A's stake becomes unbacked by tokens**


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://reports.immunefi.com/yeet/41521-sc-critical-unstaked-tokens-incorrectly-counted-as-rewards-during-vesting-period.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
