Skip to content

fix: OZ 2.3.0 audit part 2 (L-03, L-04, N-04, N-09, N-10, N-11) - #524

Merged
dianakocsis merged 10 commits into
dev-2.3.0from
fix/oz-2.3.0-audit-part2-findings
Oct 8, 2026
Merged

dianakocsis merged 10 commits into
dev-2.3.0from
fix/oz-2.3.0-audit-part2-findings

Conversation

@dianakocsis

@dianakocsis dianakocsis commented Oct 6, 2026 •

Copy link
Copy Markdown
Contributor
AI-Generated Description

What

Seven commits addressing the remaining findings from the OpenZeppelin 2.3.0 audit, continuing from #523 (L-01, N-02 through N-07):

Finding Change Commit
N-09 Fix stale "Maximum supported command at this moment is 0x3f" comment in Commands.sol — command types are 7 bits (COMMAND_TYPE_MASK), and 0x40–0x5f is the highest assigned range. Also fix duplicated-word typo ("the the" → "the") in MaxInputAmount.sol 5705424
N-11 Call protocolFeeController() on poolManager directly instead of casting through IProtocolFees, removing the now-unused import 91034da
N-10 Make TransientSlots the single source of truth for slot constants — convert library constants to file-level constants so consumers import them directly instead of duplicating the literal and asserting equality in tests 80d69fe
L-03 Map PERMIT2_TRANSFER_FROM and each PERMIT2_TRANSFER_FROM_BATCH detail amount through the resolved-amount register, so a route can pull exactly the amount a prior RESOLVE produced instead of an offchain upper bound. Also wire WRAP_ETH and UNWRAP_WETH_EXACT through ResolvedAmount.map 53ef266
L-04 Document that sub-plans share the resolved-amount register and that a reverted sub-plan undoes its writes daf65a1
— Accept the resolved amount in every exact amount the router moves: wire SETTLE, TAKE, and ACROSS_V4_DEPOSIT_V3 through ResolvedAmount.map; expand README and Constants.sol with the full sentinel-aware and sentinel-excluded field lists 131d396
N-04 Note that share-rounding tokens (e.g. stETH delivering 1-2 wei less) revert V2 exact-output swaps, directing integrators to V2_SWAP_EXACT_IN instead 543fcaa

Changes

  • contracts/base/Dispatcher.sol: Drop the IProtocolFees import and call poolManager.protocolFeeController() directly (N-11); map PERMIT2_TRANSFER_FROM amount through ResolvedAmount.map and SafeCast160 (L-03); map WRAP_ETH and UNWRAP_WETH_EXACT amounts through ResolvedAmount.map; add sub-plan revert note to _resolve NatSpec (L-04)
  • contracts/modules/Permit2Payments.sol: Rewrite permit2TransferFrom(batch) to transfer one detail at a time so each amount passes through ResolvedAmount.map, replacing the single PERMIT2.transferFrom(batchDetails) call (L-03)
  • contracts/modules/uniswap/v4/V4SwapRouter.sol: Map SETTLE and TAKE amounts through ResolvedAmount.map by overriding _mapSettleAmount and _mapTakeAmount
  • contracts/modules/ChainedActions.sol: Map ACROSS_V4_DEPOSIT_V3 inputAmount through ResolvedAmount.map
  • contracts/libraries/TransientSlots.sol: Convert from a library with internal constant members to file-level constant declarations so inline assembly in consumer libraries can reference them directly without duplication (N-10)
  • contracts/libraries/Locker.sol, NestedUnlock.sol, ResolvedAmount.sol, MaxInputAmount.sol: Remove duplicated slot constants, import from TransientSlots (N-10)
  • contracts/base/RouteSigner.sol: Remove three duplicated slot constants, import from TransientSlots (N-10)
  • contracts/libraries/Commands.sol: Update the command-type range comment to reflect the actual 7-bit layout (N-09)
  • contracts/libraries/MaxInputAmount.sol: Remove duplicate "the" in the slot documentation comment (N-09)
  • contracts/libraries/Constants.sol: Expand the USE_RESOLVED_AMOUNT doc comment to list which amount fields consume the sentinel and which do not (L-03)
  • contracts/modules/uniswap/v2/V2SwapRouter.sol: Expand delivery-check comment to mention share-based rounding (N-04)
  • README.md: Update RESOLVE section with the full list of sentinel-aware and sentinel-excluded amount fields; document sub-plan register sharing (L-04); add V2_SWAP_EXACT_OUT rounding-token note (N-04)
  • test/foundry-tests/Resolve.t.sol: Add ResolvePermit2TransferTest — exercises resolved-amount mapping for PERMIT2_TRANSFER_FROM and PERMIT2_TRANSFER_FROM_BATCH, plus robustness tests (truncated details, overstated length, dirty fields, foreign owner)
  • test/foundry-tests/ResolveV4.t.sol: Tests for resolved-amount mapping through SETTLE and TAKE v4 actions
  • test/foundry-tests/TransientSlots.t.sol: Remove test_librariesUseTableSlots (redundant now that there is only one copy of each constant); update imports to file-level constants
  • test/foundry-tests/Locker.t.sol, MaxInputAmount.t.sol, ResolvedAmount.t.sol: Remove slot-equality assertions that are no longer needed

Bytecode

Runtime Free under 24,576
dev-2.3.0 before 24,228 348
This PR 24,121 455
The batch-to-single-detail refactor in Permit2Payments and the IProtocolFees import removal together save bytes despite adding the ResolvedAmount.map and SafeCast160 calls.

Notes

  • The PERMIT2_TRANSFER_FROM_BATCH refactor issues one permit2.transferFrom per detail instead of one batch call; Permit2's batch implementation performs the same per-detail transfers, so the onchain behaviour is identical
  • The TransientSlots refactor eliminates the class of bug where a consumer's duplicated literal drifts from the table — there is now exactly one definition per slot
  • Follows up on fix: OZ 2.3.0 audit fixes (L-01, N-02, N-03, N-04, N-05, N-06, N-07) #523 which addressed L-01, N-02 through N-07
  • Includes the OpenZeppelin UR 2.3.0 and v4-periphery diff audit report PDF

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@dianakocsis
dianakocsis requested a review from a team as a code owner October 6, 2026 16:43
@github-actions github-actions Bot changed the title 2.3.0 audit part 2 fixes docs: fix typo and stale max-command comment (OZ N-09) Oct 6, 2026
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@github-actions github-actions Bot changed the title docs: fix typo and stale max-command comment (OZ N-09) refactor: OZ 2.3.0 audit part 2 (N-09, N-11) Oct 6, 2026
…Z N-10)

Declare the transient slots as file-level constants, which inline assembly
can reference when imported, and drop the per-library literal copies and
the tests that pinned them to the table. Bytecode is unchanged.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@github-actions github-actions Bot changed the title refactor: OZ 2.3.0 audit part 2 (N-09, N-11) refactor: OZ 2.3.0 audit part 2 (N-09, N-10, N-11) Oct 6, 2026
…r (OZ L-03)

PERMIT2_TRANSFER_FROM and each PERMIT2_TRANSFER_FROM_BATCH detail now accept
the USE_RESOLVED_AMOUNT sentinel, so a route can pull exactly an amount a prior
RESOLVE produced. The batch transfers one detail at a time through Permit2's
single transferFrom, which runs the same per-detail transfer as its batch call
and avoids copying the details to memory. Document exactly which amount fields
read the sentinel.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@github-actions github-actions Bot changed the title refactor: OZ 2.3.0 audit part 2 (N-09, N-10, N-11) fix: OZ 2.3.0 audit part 2 (L-03, N-09, N-10, N-11) Oct 7, 2026
dianakocsis and others added 2 commits October 7, 2026 20:23
…L-04)

The register is intentionally shared by a plan and its sub-plans: a RESOLVE in
a sub-plan that succeeds replaces the parent's value, and a sub-plan that
reverts has its writes undone, its RESOLVE included. Document this and correct
the _resolve comment, which implied a cleared value can never come back.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Map the USE_RESOLVED_AMOUNT sentinel in WRAP_ETH, UNWRAP_WETH_EXACT, the
ACROSS_V4_DEPOSIT_V3 inputAmount, and the v4 SETTLE and TAKE amounts, so the
RESOLVE register covers every exact amount alongside the swap, TRANSFER and
Permit2 transfer amounts. Minimums, caps, thresholds, portions and signed permit
amounts keep their literal meaning.

The TAKE override needs _mapTakeAmount to be virtual, so point v4-periphery at
fix/oz-ur-2.3.0-audit-part2-findings (a4558a6) and update foundry.lock to match.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@dianakocsis dianakocsis changed the title fix: OZ 2.3.0 audit part 2 (L-03, N-09, N-10, N-11) fix: OZ 2.3.0 audit part 2 (L-03, L-04, N-09, N-10, N-11) Oct 8, 2026
…s (OZ N-04)

V2 exact-output measures delivery at the recipient, so a token that hands over
less than the amount transferred anywhere on the path reverts the route. Add
share-based rounding such as stETH, which typically delivers 1-2 wei less, to
the documented causes, and point such tokens to exact-input swaps.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@github-actions github-actions Bot changed the title fix: OZ 2.3.0 audit part 2 (L-03, L-04, N-09, N-10, N-11) fix: OZ 2.3.0 audit part 2 (L-03, L-04, N-04, N-09, N-10, N-11) Oct 8, 2026
dianakocsis and others added 3 commits October 8, 2026 18:57
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
#612 was squash-merged into dev-ur-2.3.0 and its branch deleted, which left the
previous pin (a4558a6) on no branch. 7ed8439 is the squash; its tree equals
#612's tip. The only difference from a4558a6 is the V4Quoter empty-path guard,
which is not part of the router, so the router bytecode is unchanged.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@dianakocsis
dianakocsis merged commit 26f89e6 into dev-2.3.0 Oct 8, 2026
7 checks passed
@dianakocsis
dianakocsis deleted the fix/oz-2.3.0-audit-part2-findings branch October 8, 2026 23:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant