# Theft of holder fees when holderFeePercent was positive and is set to zero

Setting holderFeePercent to zero stops onBalanceChange tracking, letting new shareholders claim unclaimed fees owed to prior holders in FeeSplitter.

- Payout: Contest reward
- Severity: medium
- Program: Curves Protocol
- Credited: santipu\_
- Disclosed: 2024-07-19
- Write-up quality: 6/10
- Original: https://github.com/code-423n4/2024-01-curves-findings/issues/895
- Library page: https://bugbountylibrary.com/r/c4%3A2024-01-curves%3AM-02
- Tags: domain:web3 impact:funds-loss kind:contest platform:code4rena sev:medium tech:evm tech:solidity technique:regression technique:source-review vuln:access-control vuln:business-logic vuln:reward-distribution

**Target:** FeeSplitter / Curves _transferFees fee accounting
**Root cause:** _transferFees only calls feeRedistributor.onBalanceChange when holdersFeePercent > 0, so when the fee is set to zero, new shareholders' balances are not recorded and they can claim previously accrued fees.
**Steps:**
1. Deploy Curves and FeeSplitter; set curves on feeSplitter and manager.
2. Subject buys 1 share to enable trading.
3. Set maxFeePercent 0.05e18 and externalFeePercent(0,0,0.05e18).
4. Victim buys 10 shares, paying holder fee into FeeSplitter.
5. Set externalFeePercent(0,0,0) (holderFeePercent now zero).
6. Attacker buys 10 shares; onBalanceChange not called for attacker.
7. Attacker calls `feeSplitter.claimFees(subject)` and receives victim's accrued fees; victim's later claim reverts OutOfFund.
**How found:** Code reading of _transferFees conditional: noticed onBalanceChange is skipped when holdersFeePercent is zero, hypothesized stale accounting in FeeSplitter, confirmed with Foundry PoC.
**Exploitation:** Attacker waits until holderFeePercent is set to zero, buys shares (no onBalanceChange recorded), then calls claimFees to drain unclaimed holder fees that belong to prior shareholders; repeatable across addresses to drain FeeSplitter ETH.
**Escalation:** Repeat with multiple addresses to progressively drain FeeSplitter reserves.
**Insight:** When a fee/redistribution path is conditionally skipped based on a config value, check whether accrued state from when the config was active becomes claimable by parties who never contributed; test config transitions to zero.
