#42598 [SC-High] When claiming rewards from `StakeV2` left-over debt is sent to `StakeV2` instead of the user
Was this helpful?
Was this helpful?
Submitted on Mar 24th 2025 at 22:08:01 UTC by @kmm for
Report ID: #42598
Report Type: Smart Contract
Report severity: High
Target: https://github.com/immunefi-team/audit-comp-yeet/blob/main/src/contracts/Zapper.sol
Impacts:
Permanent freezing of funds
When claiming rewards from StakeV2
, vault tokens are zapped out via the zapper contract. However, if a user specifies a smaller inputAmount
for the token swap, any excess tokens are erroneously sent to StakeV2
and become permanently locked.
Users claim their rewards using the following functions:
claimRewardsInNative
claimRewardsInToken0
claimRewardsInToken1
claimRewardsInToken
The vulnerability applies to all of them, but we will focus on claimRewardsInToken1
:
The function validates the withdrawal amount and delegates to the zapper via zapOutToToken1
:
Here, _yeetOut
returns amounts in token0Debt
and token1Debt
. The user provides a swap amount (swapData.inputAmount
) for converting token0
to token1
. Any leftover token0Debt
(i.e., token0Debt - inputAmount
) is sent to _msgSender()
, which is the StakeV2
contract.
This leads to a scenario where if a user intends to convert only part of their token0Debt
(e.g., 50%), the remaining 50% is sent to StakeV2
and becomes inaccessible to the user. This results in a permanent partial loss.
Permanent, irrecoverable loss of user funds occurs when the user specifies a swapData.inputAmount
smaller than the full token0Debt
. The excess token0
is sent to StakeV2
and becomes trapped.
For simplicity assume that the rate of token0/token1 is always 1:1.
User has 5 compounding vault shares that equal (500 token0 and 500 token1).
User call claim claimRewardsInToken1
and passes swapData.inputAmount = 250
250 token0 is swapped for 250 token1 tokens
750 token1 tokens are sent out to the user, while 250 token0 tokens are sent to StakeV2
, causing a net loss of 250 token0 tokens for the user.