Skip to content

[#470] Fix USDC zap mint: restore _encodeAsStruct for V4 multi-hop#65

Merged
realproject7 merged 1 commit intomainfrom
task/470-usdc-zap-final-fix
Mar 23, 2026
Merged

[#470] Fix USDC zap mint: restore _encodeAsStruct for V4 multi-hop#65
realproject7 merged 1 commit intomainfrom
task/470-usdc-zap-final-fix

Conversation

@realproject7
Copy link
Copy Markdown
Owner

Summary

  • Root cause: V4 Router's CalldataDecoder.decodeSwapExactInParams/decodeSwapExactOutParams reads the first word of action params as an offset pointer (swapParams := add(params.offset, calldataload(params.offset))). Our abi.encode(field1, field2, ...) produced a flat tuple (first word = address), but the decoder expects abi.encode(struct) format (first word = 0x20 offset).
  • Previous fix (PR #469) correctly added _encodeAsStruct but also included noPriceChecks (uint256[]) matching the v4-periphery lib's new minHopPriceX36 field. The deployed Universal Router (Jan 2025) predates that field (added Nov 2025), corrupting the ABI layout.
  • Fix: Restore _encodeAsStruct (adds 0x20 prefix) WITHOUT noPriceChecks
  • Deployed new ZapPlotLinkV2: 0xAe50C9444DA2Ac80B209dC8B416d1B4A7D3939B0
  • Successful mainnet USDC mint tx: 0x417378c8...

Test plan

  • Fork test: USDC mintReverse (exact input) — PASS
  • Fork test: USDC mint (exact output) — PASS
  • Fork test: ETH mintReverse — PASS (no regression)
  • Real mainnet tx: USDC mint 1000 tokens — SUCCESS
  • Verify ETH and HUNT zap still work on new deployment
  • Update plotlink app constants (separate PR)

Fixes realproject7/plotlink#470

🤖 Generated with Claude Code

Root cause: V4 Router's CalldataDecoder.decodeSwapExactInParams/Out reads
the first word of action params as an offset pointer (assembly:
swapParams := add(params.offset, calldataload(params.offset))).
abi.encode(field1, field2, ...) produces a flat tuple starting with
the first field value, but the decoder expects a struct-style encoding
with an outer 0x20 offset word.

Previous fix attempts (PR #469) correctly identified the need for
_encodeAsStruct but also added a noPriceChecks (uint256[]) field
matching the v4-periphery lib's new minHopPriceX36 struct field.
The deployed Universal Router (Jan 2025) predates that field (added
Nov 2025), so the extra array corrupted the ABI layout.

Fix: restore _encodeAsStruct (adds 0x20 prefix) without noPriceChecks.

Verified: successful USDC mint tx on Base mainnet:
0x417378c881237fbbd9bbfd59225c643ebc26851d9151af58b2a1047d5cc70ac5

New ZapPlotLinkV2: 0xAe50C9444DA2Ac80B209dC8B416d1B4A7D3939B0

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown
Collaborator

@project7-interns project7-interns left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Verdict: APPROVE

Summary

The contract change is narrowly scoped to the V4 multi-hop ABI layout regression and the deployment/docs updates are consistent with the new address. I did not find correctness, security, or scope issues in this diff.

Findings

  • None.

Decision

Approve. The fix restores the struct-style encoding expected by the deployed Universal Router without reintroducing the incompatible extra field from PR #469.

Copy link
Copy Markdown
Collaborator

@project7-interns project7-interns left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

T2b APPROVE

Fix is minimal, correct, and validated on mainnet.

  • _encodeAsStruct correctly prepends 0x20 offset word for V4 Router's CalldataDecoder
  • noPriceChecks field properly removed — incompatible with deployed Jan 2025 Universal Router
  • Both swap directions (exact-in and exact-out) are fixed
  • Helper is private pure, no security concerns
  • No regression to ETH single-hop paths

Note (non-blocking): exact-in path uses uint128(0) as amountOutMinimum — slippage protection relies on downstream bonding curve check. Pre-existing, not introduced here.

@realproject7 realproject7 merged commit 9ee5ee6 into main Mar 23, 2026
2 checks passed
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.

2 participants