# Malicious Users Can Duplicate Protocol Earned Yield By Transferring wCVX Tokens To Another Account

wCVX wrapper omitted _beforeTokenTransfer checkpointing, letting a user transfer tokens and re-checkpoint each new account to duplicate deposited balance and drain rewards.

- Payout: Audit contest
- Severity: high
- Program: Yield
- Found by: leastwood
- Disclosed: 2022-03-04
- Write-up quality: 7/10
- Original: https://github.com/code-423n4/2022-01-yield-findings/issues/86
- Library page: https://bugbountylibrary.com/r/c4%3A2022-01-yield%3AH-01
- Tags: domain:web3 impact:funds-loss kind:contest platform:code4rena sev:high tech:evm tech:solidity technique:incomplete-fix technique:source-review vuln:accounting-error vuln:business-logic vuln:reward-distribution

**Target:** ConvexYieldWrapper.sol wCVX reward accounting
**Root cause:** Custom wrapper removed the reference ConvexStakingWrapper's _beforeTokenTransfer checkpoint hook, so token transfers do not update the sender's checkpointed deposited balance while the recipient can be checkpointed independently.
**Steps:**
1. Alice wraps convex tokens, receives 100 wCVX; _getDepositedBalance() returns 100 and a checkpoint records it.
2. Alice transfers 100 wCVX to Bob; no checkpoint fires on transfer.
3. Bob calls `user_checkpoint()` to record his own 100 balance.
4. Protocol now counts both Alice and Bob at 100 wCVX.
5. Either ensures the 100 wCVX are in their account when calling `getReward()`, then transfers tokens out; repeat across accounts.
**How found:** Code review comparing Yield's custom ConvexYieldWrapper against the reference ConvexStakingWrapper, noticing the _beforeTokenTransfer checkpoint function was removed; reasoned that transfers would leave stale checkpoints.
**Exploitation:** Attacker wraps tokens once, then cycles the same wCVX balance through many accounts, checkpointing each, so each account claims a full share of protocol rewards while only one deposit exists; tokens are held only during getReward() calls.
**Escalation:** Replicated across arbitrary numbers of accounts (sybil) to drain the reward pool, also diluting honest users' expected rewards.
**Insight:** When a protocol forks a rewards wrapper, diff it against the upstream reference for removed accounting hooks like _beforeTokenTransfer/_checkpoint; any balance-changing path (transfer, mint, burn) that skips accrual lets the same balance be counted in multiple accounts.
