#42214 [SC-High] Leftover `WBERA` and `YEET` sent to `StakeV2` instead of to user who is claiming rewards
Submitted on Mar 21st 2025 at 18:24:40 UTC by @Oxl33 for Audit Comp | Yeet
Report ID: #42214
Report Type: Smart Contract
Report severity: High
Target: https://github.com/immunefi-team/audit-comp-yeet/blob/main/src/contracts/Zapper.sol
Impacts:
Theft of unclaimed royalties
Description
Description:
When users claim rewards through one of the claim functions in StakeV2
, after removing liquidity from Kodiak
and swapping, commonly (if not always) there will be left unused WBERA
and YEET
tokens. The issue lies in the use of _msgSender()
as the receiver of the leftover tokens, because when StakeV2
calls Zapper
, msg.sender
is not the user who initiated the transaction, but StakeV2
contract. Due to this, one of the leftover tokens (or in the case of claimRewardsInToken
function - both WBERA
and YEET
) get sent to StakeV2
and later added to rewards that other stakers can claim, when in reality, these leftover tokens belong to the user who initiated the transaction, because they originated from Kodiak
LP tokens that the user had the right to claim as reward, based on their staked YEET
amount and the duration of their staking.
WBERA
and/or YEET
tokens can be left unused during the claiming process in these cases:
If
Kodiak
returns bigger than expected output amounts ofWBERA
andYEET
after burningKodiak
LP tokensDuring swaps from
WBERA
toYEET
and vice versa, if expected input amount is smaller than actual output fromKodiak
During swaps from
WBERA
/YEET
to another whitelisted token (e.g.KDK
oroBERO
), if expected input amount is smaller than actual output fromKodiak
Considering all these possible cases and the fact that claiming rewards is one of the main functionalities of this protocol, I believe this issue will happen commonly and will affect the majority of the users.
The issue occurs in these functions:
StakeV2::claimRewardsInNative
-> Zapper::zapOutNative
-> _swapToWBERA
- only YEET
sent to StakeV2
StakeV2::claimRewardsInToken0
-> Zapper::zapOutToToken0
- only YEET
OR only WBERA
sent to StakeV2
StakeV2::claimRewardsInToken1
-> Zapper::zapOutToToken1
- only YEET
OR only WBERA
sent to StakeV2
StakeV2::claimRewardsInToken
-> Zapper::zapOut
- both YEET
AND WBERA
sent to StakeV2
Impact:
I think the most fitting impact from all the in-scope impacts is Theft of unclaimed royalties
, because the affected users lose a part of the royalties that belongs to them and other stakers benefit from them, so it is similar to theft.
This impact is meant for high severity issues and I believe it fits the overall severity of this issue, because this issue will occur frequently (high likelihood) and the funds of users are mismanaged, which leads to undeserved losses for some users and gains for others (high impact).
Proof of Concept
Proof of Concept:
Alice stakes her YEET tokens using
StakeV2
A percentage of BERA from yeets of users gets sent to
StakeV2
Manager distributes the rewards with
executeRewardDistribution
andexecuteRewardDistributionYeet
functionsAlice wants to claim her share of the rewards and she wants to receive them in the form of e.g.
oBERO
tokens, so she callsStakeV2::claimRewardsInToken
and specifies output token asoBERO
(assuming it is whitelisted, because it is used incompound
)Zapper::zapOut
function gets called and there are leftover tokens ofWBERA
andYEET
, due to the reasons I mentioned inDescription
section_clearUserDebt
gets called withtoken0Debt
andtoken1Debt
being more than 0, and_msgSender()
beingStakeV2
contractLeftover tokens that rightfully belong to Alice get sent to
StakeV2
contract
Zapper::zapOut
function gets called here: https://github.com/immunefi-team/audit-comp-yeet/blob/da15231cdefd8f385fcdb85c27258b5f0d0cc270/src/StakeV2.sol#L391
_clearUserDebt
function gets called here (_msgSender()
is StakeV2
): https://github.com/immunefi-team/audit-comp-yeet/blob/da15231cdefd8f385fcdb85c27258b5f0d0cc270/src/contracts/Zapper.sol#L352
Was this helpful?