# Creating rewardTokens without streaming depositTokens

Integer truncation in Locke.sol withdraw lets attacker stake then withdraw same block, streaming zero depositTokens while accruing rewardTokens, diluting honest stakers.

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

**Target:** Locke.sol staking/streaming reward accounting contract
**Root cause:** Withdraw computes streamed tokens as uint112(acctTimeDelta * ts.tokens / (endStream - ts.lastUpdate)); when the fraction is <1 it truncates to 0, so no depositTokens are streamed, yet cumulativeRewardPerToken/earned still credit rewards for the staked amount.
**Steps:**
1. Create/fund a stream and warp past start so lastUpdate fields are populated.
2. Call `doStake(stream, tokenB, stakeAmount)` then `doWithdraw(stream, stakeAmount)` to initialize lastUpdate without balance change.
3. Stake again: `doStake(stream, tokenB, stakeAmount)`.
4. Advance one block (`hevm.roll`/`hevm.warp`).
5. Withdraw: `doWithdraw(stream, stakeAmount)` — elapsed*stake/(endStream-lastUpdate) < 1 truncates to 0, so `ts.tokens -= 0` streams nothing.
6. Repeat per block from multiple EOAs to accumulate reward share.
7. After stream ends call `doClaimReward(stream)`; PoC shows alice gains 13227 rewardTokens with stakeAmount fully returned.
**How found:** Code reading of the withdraw path: noticed reward accrual (cumulativeRewardPerToken/earned) is independent of the streamed-token subtraction, and that the subtraction `uint112(acctTimeDelta * ts.tokens / (endStream - ts.lastUpdate))` truncates to zero for short elapsed windows, allowing stake/withdraw in the same block to earn rewards without streaming.
**Exploitation:** Attacker stakes depositTokens, waits a single block, then withdraws; the streamed-token subtraction truncates to zero so the deposit is never actually streamed, but earned() still accrues rewardTokens. Repeating each block (and across many EOAs) inflates the attacker's share of the reward pool, extracting rewardTokens at the expense of honest stakers.
**Escalation:** Repeat every block and from multiple EOA addresses to multiply the reward share captured; economic feasibility scales with staked amount vs total staked and rewardToken value vs gas.
**Insight:** In staking/reward contracts, check whether reward accrual and deposit-consumption are computed from independent state; any integer division that can truncate to 0 on a short time delta lets you stake-and-withdraw in one block to farm rewards without paying the streaming cost.
