# The distribution logic will be broken after calling rageQuit()

PartyDAO's TokenDistributor computes claims from live totalVotingPower, so a member can rageQuit one NFT then claim a larger share of an already-funded distribution, stealing others' funds.

- Payout: Contest reward
- Severity: high
- Program: PartyDAO
- Credited: hansfriese
- Disclosed: 2023-06-23
- Write-up quality: 7/10
- Original: https://github.com/code-423n4/2023-05-party-findings/issues/6
- Library page: https://bugbountylibrary.com/r/c4%3A2023-05-party%3AH-01
- Tags: domain:web3 impact:funds-loss kind:contest platform:code4rena sev:high tech:solidity technique:source-review vuln:accounting-error vuln:business-logic vuln:governance

**Target:** PartyGovernanceNFT / TokenDistributor claim accounting
**Root cause:** getDistributionShareOf() divides votingPowerByTokenId by the current _governanceValues.totalVotingPower, which decreases when NFTs are burned via rageQuit()/burn(), so remaining NFTs' shares of an already-created distribution inflate.
**Steps:**
1. Create party with totalVotingPower=300, three NFTs of 100 voting power (Alice 2, Bob 1).
2. Execute a distribution proposal funding TokenDistributor with 3 ETH.
3. Alice calls `TokenDistributor.claim()` with NFT #1, receiving 1 ETH (100/300).
4. Alice calls `party.rageQuit([tokenId1], [ETH], alice)`, burning NFT #1 and reducing totalVotingPower to 200.
5. Alice calls `claim()` with NFT #2; `getDistributionShareOf()` now returns 100/200 = 50%, so she receives 1.5 ETH.
6. Bob's claim is capped by remaining memberSupply, receiving only 0.5 ETH; Alice nets 2.5 ETH instead of 2.
**How found:** Code reading of getDistributionShareOf() and getClaimAmount() revealed the share is computed against live totalVotingPower; noticing the newly added rageQuit()/burn() paths mutate that denominator led to the hypothesis that post-distribution burns inflate remaining shares, confirmed with a Foundry PoC.
**Exploitation:** A member of a party with a pending/executed distribution claims their normal share, then burns one NFT via rageQuit() (or burn() if remaining-fund share is small), lowering totalVotingPower, then claims again with remaining NFTs at an inflated share until memberSupply is drained, leaving later claimants underpaid.
**Escalation:** Can be repeated across multiple NFTs/distributions and works even with rageQuit disabled by using burn() directly when the burned NFT's remaining-fund share is less than the stolen amount.
**Insight:** When a payout ratio is computed from a mutable global denominator (total supply/power), check whether any burn/withdraw path can shrink that denominator after the payout pool is funded; snapshot the denominator per distribution instead.
