# The fees are incorrectly updated in the `_deposit` and `_withdraw` functions, which allows the attacker to break the protocol fees

Multipool's _deposit/_withdraw compute protocol fee from tokensOwed delta, so pre-accrued Uniswap V3 position fees are collected but never counted, letting anyone zero out protocol fees.

- Payout: Contest reward
- Severity: medium
- Program: Real Wagmi
- Credited: duc
- Disclosed: 2023-06-14
- Write-up quality: 6/10
- Original: https://github.com/sherlock-audit/2023-06-real-wagmi-judging/issues/52
- Library page: https://bugbountylibrary.com/r/sherlock%3A2023-06-real-wagmi%3AM-2
- Tags: domain:web3 impact:funds-loss kind:contest platform:sherlock sev:medium tech:evm tech:solidity technique:source-review vuln:accounting-error vuln:fee-bypass

**Target:** Multipool.sol _deposit/_withdraw fee accounting over Uniswap V3 positions
**Root cause:** fee0/fee1 are derived from tokensOwedAfter - tokensOwedBefore (and minus burn amounts), which excludes fees already accrued in the position before the call, while collect() sweeps the full owed amount.
**Steps:**
1. Attacker mints a minuscule amount into each Multipool Uniswap V3 position (anyone can mint to any position), forcing fee accrual into position.tokensOwed.
2. Attacker calls `earn()` (or triggers _deposit/_withdraw) so tokensOwed0Before ≈ tokensOwed0After.
3. fee0/fee1 computed as delta ≈ 0, so `_upFeesGrowth` records near-zero protocol fee.
4. collect() still sweeps full owed fees to the contract, but protocol fee share is lost to reserves.
**How found:** Code reading of _deposit/_withdraw: noticed fee0/fee1 use tokensOwed delta rather than the collected amount, and that Uniswap V3 accrues fees into position data on any mint/burn, so a third party can pre-accrue them.
**Exploitation:** Pre-accrue position fees via dust mint so the before/after tokensOwed delta is ~0; subsequent deposit/withdraw collects all fees but credits ~0 to protocol feeGrowth, diverting the protocol's fee share.
**Insight:** When a contract derives its fee share from a before/after snapshot of Uniswap V3 position.tokensOwed, remember that anyone can poke a position with a dust mint to force accrual; the snapshot then misses all pre-existing owed fees. Compare against the actual collect() return values instead.
