# TokenDistributor: ERC777 tokensToSend hook can be exploited to drain contract

ERC777 tokensToSend hook reenters TokenDistributor.createERC20Distribution before balance update, inflating distribution supply and letting attacker steal another party's tokens.

- Payout: Contest reward
- Severity: high
- Program: PartyDAO
- Credited: Lambda
- Disclosed: 2022-11-17
- Write-up quality: 7/10
- Original: https://github.com/code-423n4/2022-09-party-findings/issues/120
- Library page: https://bugbountylibrary.com/r/c4%3A2022-09-party%3AH-04
- Tags: domain:web3 impact:funds-loss kind:contest platform:code4rena sev:high tech:evm tech:solidity technique:source-review vuln:accounting-error vuln:reentrancy

**Target:** TokenDistributor.createERC20Distribution / _transfer
**Root cause:** createERC20Distribution reads token.balanceOf(address(this)) while _storedBalances[balanceID] was already decremented in _transfer, and the ERC777 tokensToSend hook fires before the balance update, allowing reentrancy that inflates the new distribution's supply.
**Steps:**
1. Party A and Party B each have 1,000,000 ERC777 tokens stored in TokenDistributor.
2. User A calls `claim`, which invokes `_transfer`; `_storedBalances[balanceId]` is decreased by 1,000,000 (now 1,000,000) before the token transfer.
3. In the ERC777 `tokensToSend` hook, User A calls `PartyGovernance.distribute` -> `TokenDistributor.createERC20Distribution` (transfers 0 tokens).
4. `createERC20Distribution` passes `token.balanceOf(address(this))` = 2,000,000 (stale) to `_createDistribution`.
5. Supply computed as `2,000,000 - 1,000,000 = 1,000,000`, creating a distribution backed by Party B's tokens.
6. After the hook returns and the first transfer completes, User A claims the second distribution to steal Party B's 1,000,000 tokens.
**How found:** Code reading of TokenDistributor._transfer and createERC20Distribution against the ERC777 spec: noticed tokensToSend fires before balance updates, so balanceOf is stale while _storedBalances is already decremented, making the supply formula overcount.
**Exploitation:** Attacker-controlled contract holding a party balance calls claim on an ERC777 token; inside the tokensToSend hook it triggers a new distribution via PartyGovernance.distribute, exploiting the stale balanceOf to mint a distribution whose supply equals the victim party's stored tokens, then claims it after the hook unwinds.
**Insight:** When a contract computes share/supply from token.balanceOf minus internal accounting, check ERC777/ERC677/ERC1363 transfer hooks: any external call between the accounting decrement and the actual token movement lets an attacker re-enter and capture the stale balance as a new distribution.
