AntFleet

Disagreement · 1e8fb4a1-anthropic-9

UniswapV2Locker.claimFeesAndExit fee/principal computation can mis-allocate principal when only one side has fees

solo Opus
repo a7cc2ed7·PR #1·reviewed 1 week ago

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.

Other reviewer

The other reviewer flagged nothing in this file/line range.

Why this didn't post

This finding didn't meet AntFleet's unanimous agreement threshold. Both frontier models review every PR independently; only findings they both flag with the same severity and category are posted to the PR. This one fell through.

read the methodology →