# The Main Invariant "Fees paid to a given user should not exceed the amount of fees earned by the liquidity owned by that user." can be broken due to slight difference when computing collected fee

Panoptic computes collected fees as (currFeeGrowth*liquidity/Q128)-(prevFeeGrowth*liquidity/Q128) instead of Uniswap's (curr-prev)*liquidity/Q128, letting users collect more fees than earned.

- Payout: Contest reward
- Severity: medium
- Program: Panoptic
- Credited: 0xDING99YA
- Disclosed: 2024-06-24
- Write-up quality: 6/10
- Original: https://github.com/code-423n4/2023-11-panoptic-findings/issues/437
- Library page: https://bugbountylibrary.com/r/c4%3A2023-11-panoptic%3AM-03
- Tags: domain:web3 impact:funds-loss kind:contest platform:code4rena sev:medium tech:evm tech:solidity technique:regression technique:source-review vuln:business-logic vuln:precision-loss vuln:reward-distribution

**Target:** SemiFungiblePositionManager fee accounting (Uniswap V3 pools)
**Root cause:** Panoptic splits the fee-growth multiplication into two separate mulDiv128 calls and subtracts, whereas Uniswap V3 subtracts fee growth first then multiplies; floor rounding on each term makes the difference exceed the true collected fee.
**Steps:**
1. Fork mainnet, use Gemini USD/USDC Uniswap V3 pool `0x5aA1356999821b533EC5d9f79c23B8cB7c295C61`.
2. Swap to obtain Gemini USD, mint position at ticks 92100-92200 with liquidity 10000000000.
3. Record `prevFeeGrowthInside0LastX128` from `positions()`.
4. Perform two swaps (4800 and 5000) then burn to update feeGrowth.
5. Compare `(curr-prev)*liq/2^128` (Uniswap) vs `curr*liq/2^128 - prev*liq/2^128` (Panoptic); assert Panoptic value is larger.
**How found:** Code-reading comparison of Panoptic's `_getFeesBase`/fee-collection math against Uniswap V3's `(currFeeGrowth - prevFeeGrowth) * liquidity / Q128`, spotting that `[A]-[B] <= [A-B]` is not guaranteed under integer division; confirmed with a Foundry mainnet-fork PoC.
**Exploitation:** A position holder collects fees via Panoptic's split computation, receiving 1 wei more than the Uniswap-correct amount; with multiple users on the same option position, earlier collectors drain fees owed to later ones.
**Escalation:** Accumulates over many swaps and across multiple users sharing one option position; amplified for low-decimal high-value tokens (e.g. token pegged to ETH/BTC).
**Insight:** When reimplementing a protocol's accounting math, check whether splitting a multiply-then-subtract into two floored multiplications can violate the original inequality; test with feeGrowth values near Q128 boundaries and low-decimal tokens.
