# Tokens can be stolen when depositToken == rewardToken

When depositToken equals rewardToken, recoverTokens double-counts the shared balance, letting the stream creator drain both deposit and reward pools.

- Payout: Audit contest
- Severity: high
- Program: Streaming Protocol
- Found by: cmichel
- Disclosed: 2022-02-11
- Write-up quality: 7/10
- Original: https://github.com/code-423n4/2021-11-streaming-findings/issues/215
- Library page: https://bugbountylibrary.com/r/c4%3A2021-11-streaming%3AH-02
- Tags: area:web3 domain:web3 impact:funds-loss kind:contest platform:code4rena sev:high tech:solidity technique:source-review vuln:accounting-error vuln:business-logic

**Target:** Streaming contract recoverTokens()
**Root cause:** recoverTokens computes excess separately for depositToken and rewardToken branches using balanceOf, but when both are the same token the shared balance is counted twice, so the second branch transfers funds already owed to the other pool.
**Steps:**
1. Deploy stream with depositToken == rewardToken (e.g. xSushi-style).
2. Users stake deposits (depositTokenAmount - redeemedDepositTokens = 1000) and creator funds rewards (rewardTokenAmount + rewardTokenFeeAmount = 500).
3. After endDepositLock/endRewardLock, creator calls `recoverTokens(token, creator)`.
4. First branch (token == depositToken) computes excess = balanceOf - (depositTokenAmount - redeemedDepositTokens) = 1500 - 1000 = 500 and transfers it.
5. Second branch (token == rewardToken) recomputes excess = newBalance - (rewardTokenAmount + rewardTokenFeeAmount) = 1000 - 500 = 500 and transfers it.
6. Creator nets 1000 while depositors/reward claimants are left short.
**How found:** Code review of recoverTokens noticed the two independent if-branches each subtract only their own internal accounting from balanceOf; realized that when depositToken == rewardToken the shared balance is subtracted twice, yielding a double payout.
**Exploitation:** Stream creator waits past both locks, calls recoverTokens once; the function falls through both if-branches sequentially, each transferring an 'excess' computed against the same shared balance, draining both the reward pool and part of the deposit pool.
**Insight:** When a contract tracks two logical pools but reads a single balanceOf, check every branch that computes 'excess' — if the same token can occupy both roles, each branch independently over-credits the shared balance.
