AntFleet

Anatomy · f1aedd24-1

Token0/token1 ordering hardcoded; will silently produce wrong results if WETH > DIEM in address ordering

highbug
repo 53606958·PR #6·reviewed 1 week ago

The vulnerable code

scripts/reposition.ts:233-235

Code snippet unavailable.

The reasoning

Opus

Build-mode allocation comment contradicts code: ‘below/above range’ swap direction is inverted relative to stated token0/token1 mapping

criticalbughigh
  • scripts/reposition.ts:233-244
  • scripts/reposition.ts:305-322
In Uniswap v3, when the current price (tick) is below the position's tickLower, the position is composed entirely of token1 (the higher-priced asset in the range, which Uniswap stores by sorted address). The code asserts token0=WETH and token1=DIEM, but on Base the canonical WETH (0x4200…0006) and DIEM token addresses determine sorting — the comment hardcodes an assumption that may be wrong, and even if token0=WETH/token1=DIEM, Uniswap v3 mechanics say: below-range position holds all token0 (not token1), and above-range holds all token1. The code/comment has this inverted. If currentTick < tickLower, the position is 100% token0 = WETH (not DIEM), so trying to swap diemBal/2 → WETH will fail or do nothing because there is no DIEM, and the mint will be single-sided WETH. The same inversion applies above range. This will cause mints to be near single-sided (poor capital efficiency) or revert on slippage, and on a live run will misallocate funds.

Recommendation

Correct the Uniswap v3 invariant: below range (tick < tickLower) → position is 100% token0 → if token0=WETH, swap 50% WETH→DIEM. Above range → 100% token1 → if token1=DIEM, swap 50% DIEM→WETH. Also verify token0/token1 ordering dynamically from the NFPM positions() result (already fetched in `pos`) rather than hardcoding.

GPT-5

Below/above range asset-side is reversed; swap direction likely wrong

mediumbughigh
  • scripts/reposition.ts:6-8
  • scripts/reposition.ts:270-275
  • scripts/reposition.ts:365-381
For Uniswap v3, with token0=WETH and token1=DIEM, positions are all token0 when price is below range and all token1 when above range. The header comment reflects this correctly, but the in-code comments and swap logic invert it (treating below range as all DIEM and above range as all WETH). This leads to deciding the wrong swap direction and often skipping the balancing swap due to zero balance on the assumed side, degrading to single-sided mint and suboptimal liquidity.

Recommendation

Fix the below/above range mapping: below range (currentTick < tickLower) → all token0 (WETH); above range (currentTick > tickUpper) → all token1 (DIEM). Alternatively, infer swap direction from actual post-collect balances (compare wethBal and diemBal) to be robust regardless of mapping assumptions.

The agreement

Both frontier models flagged this within the same line range. AntFleet's unanimous gate fired — the finding posted on the PR.

Closure

Tweet thread template

tweet 1 of 8184 / 280

Two frontier models reviewed PR #6 on 53606958. Both found this bug: high bug: Token0/token1 ordering hardcoded; will silently produce wrong results if WETH > DIEM in address ordering

tweet 2 of 8115 / 280

The vulnerable code (scripts/reposition.ts:233-235): (full snippet at https://www.antfleet.dev/anatomy/f1aedd24-1)

tweet 3 of 8280 / 280

What Opus saw: "In Uniswap v3, when the current price (tick) is below the position's tickLower, the position is composed entirely of token1 (the higher-priced asset in the range, which Uniswap stores by sorted address). The code asserts token0=WETH and token1=DIEM, but on Base …

tweet 4 of 8280 / 280

What GPT-5 saw: "For Uniswap v3, with token0=WETH and token1=DIEM, positions are all token0 when price is below range and all token1 when above range. The header comment reflects this correctly, but the in-code comments and swap logic invert it (treating below range as all DIEM…

tweet 5 of 897 / 280

Both flagged the same line range. AntFleet's unanimous gate fired — the finding posted on the PR.

tweet 6 of 893 / 280

The fix landed in commit pending: (view diff at https://www.antfleet.dev/anatomy/f1aedd24-1)

tweet 7 of 881 / 280

AntFleet reviews every PR with two frontier models. Only unanimous findings post.

tweet 8 of 877 / 280

Full anatomy + reasoning + diffs: https://www.antfleet.dev/anatomy/f1aedd24-1

Paste into X composer one tweet at a time. X has no multi-tweet intent API.