Skip to content

feat: HypNative routing via HYP_ERC20_COLLATERAL sentinel#4

Open
yorhodes wants to merge 4 commits intohyperlane-xyz:mainfrom
yorhodes:yorke/hyp-native-overload-tests
Open

feat: HypNative routing via HYP_ERC20_COLLATERAL sentinel#4
yorhodes wants to merge 4 commits intohyperlane-xyz:mainfrom
yorhodes:yorke/hyp-native-overload-tests

Conversation

@yorhodes
Copy link
Copy Markdown
Member

@yorhodes yorhodes commented Apr 22, 2026

Summary

Adds native-ETH (HypNative) warp route support to the UniversalRouter BridgeRouter using the existing HYP_ERC20_COLLATERAL branch with token == address(0) as the native sentinel — matching how the rest of the router (Payments.pay/sweep/wrapETH, Dispatcher CONTRACT_BALANCE resolution) already treats address(0) as the native marker.

Client logic is identical to the ERC20 collateral path:

  • amount = CONTRACT_BALANCE
  • maxTokenFee as a cap (router recomputes actual fee from quoteTransferRemote)
  • No caller-side exact-output math

Origin-side composition mirrors the ERC20 pattern too — e.g. TRANSFER_FROM(WETH) + UNWRAP_WETH + BRIDGE_TOKEN(CONTRACT_BALANCE).

Changes

  • BridgeRouter: on the HYP_ERC20_COLLATERAL branch, when token == address(0) skip the ERC20 pull/approve and forward the full native amount to transferRemote (covers bridgeAmount + all fees from msg.value).
  • Dispatcher: resolve CONTRACT_BALANCE from address(this).balance when token == Constants.ETH (was unconditional ERC20.balanceOf, which reverts on address(0)).
  • Tests: integrated alongside the HypERC20Collateral tests in bridgeToken.t.sol, with bidirectional HypNative deployed on root + leaf and cross-chain delivery verified on leaf.

Why the sentinel vs. a new HYP_NATIVE type

  • HypNative.token() literally returns address(0), so the sentinel is semantically accurate.
  • quoteExactInputBridgeAmount already handles native via quotes[0].token == token.
  • Removing the dual-branch dispatch saves bytecode (23,801 bytes runtime, 775 under EIP-170 — 45 bytes less than a separate-branch implementation).
  • No new calldata format for clients to learn.

Test coverage (bridgeToken.t.sol)

Every case uses plain amount = CONTRACT_BALANCE client logic — no caller-side fee inflation. Every happy path verifies cross-chain delivery on the leaf fork via leafMailbox.processNextInboundMessage():

Test What it covers
test_HypNative_WhenNoFees Baseline, 1:1 credit + leaf delivery
test_HypNative_WithHookFee Native TestPostDispatchHook fees on required + default hooks; router auto-deducts from balance
test_HypNative_WithLinearWarpFee Native LinearFee at 1% rate; bridgeAmount = amount² / (amount + f(amount)) = 1 ETH exactly
test_HypNative_WithCombinedFees Native hook + native linear together; bridge credits exact bridgeAmount, both fee recipients get their cut
test_HypNative_WhenTokenFeeExceedsMax TokenFeeExceedsMax revert when cap is tight
test_HypNative_WethToNative_ThenBridge Symmetric ERC20→native→bridge composition via TRANSFER_FROM + UNWRAP_WETH + BRIDGE_TOKEN(CONTRACT_BALANCE)
testGas_HypNativeBridge Direct native (msg.value) gas snapshot
testGas_HypNativeBridgeRouterBalance Router-already-holds-native gas snapshot (mirror of testGas_HypXERC20BridgeRouterBalance)

Parity with HypERC20Collateral tests

Collateral test HypNative equivalent
Happy path + 5 bps linear fee + cross-chain delivery WithLinearWarpFee (+ WithHookFee, WithCombinedFees)
Permit2 vs direct approval split N/A — no approvals on native
Token-mismatch revert N/A — token=address(0) is the sentinel, no mismatch concept
No-approval revert N/A — no approvals
Gas snapshots testGas_HypNativeBridge + testGas_HypNativeBridgeRouterBalance

Extras beyond the Collateral suite: no-fees baseline, combined fees, cap-exceeded revert, WETH→native composition.

Test plan

  • forge test --mt HypNative -vv (6/6 happy paths + revert passing, cross-chain delivery verified)
  • Existing bridgeToken.t.sol suite unchanged (same 4 pre-existing fork-persistence failures as main)
  • Bytecode under EIP-170 (23,801 / 24,576 — 775 headroom)

Notes

The earlier exploratory overload test file (bridgeTokenHypNativeOverload.t.sol) from commit 274375a is superseded by the integrated tests; its coverage is preserved in the new cases.

🤖 Generated with Claude Code

yorhodes and others added 2 commits April 22, 2026 12:33
Explores whether HypNative (native-ETH) warp routes can be bridged through
UniversalRouter without adding a dedicated HYP_NATIVE bridge type, by passing
token=address(0) and payer=router into the existing HYP_ERC20_COLLATERAL
branch. Tests cover:

- no-fee baseline (passes)
- mailbox hook fee (exact-output via caller-side amount inflation)
- linear warp fee
- combined hook + linear fee
- origin-chain swap composition (CONTRACT_BALANCE sentinel can't be used
  with token=address(0); concrete inflated amount + SWEEP is required)

Findings are intended as input for deciding between a HypNativeWethWrapper
vs. adding a HYP_NATIVE branch to BridgeRouter.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds support for HypNative (native-ETH) warp routes on the UniversalRouter
BridgeRouter without a new bridge type. The sentinel is `token == address(0)`
inside the existing HYP_ERC20_COLLATERAL branch, matching how the rest of
the router (Payments.pay/sweep/wrapETH, Dispatcher CONTRACT_BALANCE)
already treats address(0) as the native marker.

Client logic is identical to the ERC20 collateral flow:
 * pass amount = CONTRACT_BALANCE
 * set maxTokenFee as the fee cap
 * let quoteExactInputBridgeAmount compute the destination-credited amount

No caller-side exact-output math — the router drains its native balance and
the bridge's own quote resolves fees. WETH sources compose the same way as
any ERC20: TRANSFER_FROM + UNWRAP_WETH + BRIDGE_TOKEN(CONTRACT_BALANCE).

Changes:
 * BridgeRouter: on the HYP_ERC20_COLLATERAL branch, when token==0 skip the
   ERC20 pull/approve and forward the full native amount to transferRemote.
 * Dispatcher: resolve CONTRACT_BALANCE from address(this).balance when
   token is ETH (was ERC20.balanceOf unconditionally, which reverts on
   address(0)).
 * Tests: replace the exploratory overload file with integrated HypNative
   cases in bridgeToken.t.sol alongside the HypERC20Collateral tests.

Runtime bytecode: 23,759 → 23,801 (+42, still under EIP-170 by 775).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@yorhodes yorhodes changed the title test: HypNative routing via HYP_ERC20_COLLATERAL overload feat: HypNative routing via HYP_ERC20_COLLATERAL sentinel Apr 23, 2026
@yorhodes yorhodes marked this pull request as ready for review April 23, 2026 21:19
yorhodes and others added 2 commits April 23, 2026 17:20
Adds the combined (native hook + native linear warp fee) case for parity with
the original PR #4 exploration, and a router-balance gas snapshot to mirror
testGas_HypXERC20BridgeRouterBalance.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Deploys a HypNative on the leaf fork, enrolls bidirectionally between root
and leaf, and funds it with native liquidity. Each happy-path HypNative test
now calls `_assertHypNativeDelivery` after `router.execute`, which processes
the inbound mailbox message on the leaf fork and asserts the recipient's
native-balance delta matches the expected bridgeAmount.

This closes the last parity gap with HypERC20Collateral's tests, which verify
cross-chain delivery on leaf via `leafMailbox.processNextInboundMessage()`.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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