Opus finding
UniswapV2Locker.claimFeesAndExit fee/principal computation can mis-allocate principal when only one side has fees
mediumbughigh
- src/UniswapV2Locker.sol:80-97
`reserve0` and `reserve1` are read from `getReserves()` BEFORE the LP transfer-to-pool and burn. After `IUniswapV2Pair.burn(address(this))`, the pool's reserves change, so position0/position1 are computed from pre-burn reserves but compared to post-burn amounts received. This is the canonical pattern for measuring fee-vs-principal in a constant-product pool (k-method), so it is intentional. But note: `kLast = state.amount0 * state.amount1` uses the ORIGINAL locked amounts (uint112 truncated). If reserves shifted dramatically and original amounts were truncated by the uint112 cast (`uint112((balance * reserve0) / supply)`), the kLast invariant breaks and fee computation can be inaccurate. More importantly, when computing `position0 = sqrt(kLast * reserve0 / reserve1)` with current reserves, the result can exceed `amount0` due to price movement, in which case `fees0 = 0` (correct). The bigger issue is the `uint112` truncation in `receiveAndLock`: `amount0 = uint112((balance * reserve0) / supply)`. For high-supply pools this can silently truncate, leading to under-counted kLast and inflated fee detection.
Recommendation
Store `amount0/amount1` as `uint256` instead of `uint112` to avoid silent truncation, since `kLast` is computed in 256-bit arithmetic anyway.