Skip to content

fix(bridge): migrate L1 deposits and quotes from deprecated Mailbox to Bridgehub#121

Open
DrVelvetFog wants to merge 2 commits into
NodleCode:mainfrom
DrVelvetFog:fix/104-bridgehub-migration
Open

fix(bridge): migrate L1 deposits and quotes from deprecated Mailbox to Bridgehub#121
DrVelvetFog wants to merge 2 commits into
NodleCode:mainfrom
DrVelvetFog:fix/104-bridgehub-migration

Conversation

@DrVelvetFog

@DrVelvetFog DrVelvetFog commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

Follows up on my scoping comment in #104 — with the deprecation cutoff roughly ten weeks out I figured a concrete PR beats a plan, same as #120. Happy to rework any of it to your preferences.

What this does

Routes the two deprecated Mailbox surfaces through the Bridgehub, exactly as scoped in #104:

  • deposit() now calls Bridgehub.requestL2TransactionDirect with an L2TransactionRequestDirect (chainId = L2_CHAIN_ID, mintValue = msg.value — the Bridgehub requires them equal for ETH-based chains, verified against the pinned era-contracts submodule; refund-recipient aliasing is handled inside the Bridgehub via actualRefundRecipient, matching the old Mailbox behavior).
  • quoteL2BaseCost / quoteL2BaseCostAtGasPrice now source from Bridgehub.l2TransactionBaseCost, scoped by chain id.
  • The proof paths stay on the Mailbox/Diamond (proveL1ToL2TransactionStatus, proveL2MessageInclusion) — those aren't deprecated, so claimFailedDeposit and finalizeWithdrawal are untouched.

External function signatures are unchanged; existing integrators of deposit/quote calls see the same ABI.

Deployment / cutover notes (the part that needs your eyes most)

L1_MAILBOX was an immutable on a non-proxy contract, so this ships as a new deployment, not an upgrade:

  • Constructor gains _bridgehub and _l2ChainId (zero-checked). DeployL1Bridge.s.sol and ops/deploy_L1L2_bridge.sh now expect BRIDGEHUB and L2_CHAIN_ID env vars.
  • The new bridge needs MINTER_ROLE on L1 NODL (the deploy script already grants it).
  • The old bridge keeps working for in-flight items: its depositAmount / isWithdrawalFinalized state stays valid, and since its proof paths aren't deprecated it can continue serving claimFailedDeposit and finalizeWithdrawal for anything initiated before the cutover. Only new deposits need to move to the new address.
  • Sequencing that cutover (frontend/app pointing, whether to pause the old bridge's deposit) is your ops call — the contract change deliberately doesn't assume an answer.

Testing

  • New MockBridgehub in the test suite (same "not inheriting the interface on purpose" pattern as the existing MockMailbox, including mirroring the real Bridgehub's msg.value == mintValue revert); MockMailbox trimmed to the proof paths it still serves.
  • New tests: full request-field mapping into L2TransactionRequestDirect (chain id, mintValue, target, gas params, canonical hash return), and constructor guards for zero Bridgehub / zero chain id.
  • forge test: bridge suite 26/26, full repo suite 1080/1080 across 45 suites. yarn lint (solhint): no errors, no new warnings on touched files. yarn spellcheck: clean (added Bridgehub casings to .cspell.json, same as docs(readme): add manual on-chain grant claim guide #120 did for zkclient).

One judgment call to flag: I left quoteL2BaseCost's tx.gasprice convenience semantics exactly as they were rather than "improving" anything while migrating — this PR only swaps the transport. If you'd rather the quote helpers change shape while the constructor is breaking anyway, glad to fold that in.

Closes #104

CI note: the second commit (ci: skip coverage PR comment for fork PRs) fixes the Coverage job failing on fork PRs — the report-lcov comment step can never post with the read-only fork GITHUB_TOKEN (Resource not accessible by integration); same-repo PRs like #118/#113 are unaffected and keep the sticky comment. It is deliberately a separate commit so you can drop it if you would rather handle CI differently.

DrVelvetFog and others added 2 commits July 2, 2026 08:38
ZKsync has deprecated Mailbox.requestL2Transaction (and its
l2TransactionBaseCost quoting) in favor of the Bridgehub, with the
legacy entry points scheduled to stop working around mid-September.
This routes L1Bridge deposits through Bridgehub.requestL2TransactionDirect
and sources base-cost quotes from Bridgehub.l2TransactionBaseCost,
scoped by the new L2_CHAIN_ID immutable.

The Mailbox (Diamond proxy) stays for the L2->L1 proof paths
(proveL1ToL2TransactionStatus / proveL2MessageInclusion), which are
not deprecated. External function signatures are unchanged; the
constructor gains _bridgehub and _l2ChainId, so this ships as a new
deployment rather than an upgrade (cutover notes in the PR).

- src/bridge/L1Bridge.sol: Bridgehub routing for deposit + quotes,
  mintValue = msg.value per the Bridgehub's ETH-chain invariant
- test: new MockBridgehub (mirrors the MockMailbox not-inheriting
  pattern); MockMailbox trimmed to the proof paths; new coverage for
  request-field mapping and zero-bridgehub / zero-chain-id constructor
  guards (26 bridge tests, full suite 1080 green)
- script/DeployL1Bridge.s.sol + ops/deploy_L1L2_bridge.sh: BRIDGEHUB
  and L2_CHAIN_ID env vars

Closes NodleCode#104

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The Coverage job's 'Report coverage to PR' step fails on pull requests
from forks with 'Resource not accessible by integration': GitHub caps
GITHUB_TOKEN at read-only for fork-triggered pull_request runs, so the
sticky comment can never be posted (same-repo PRs are unaffected and
keep the comment). Gate the step on the head repo being this repo; the
coverage run, artifact upload, and threshold check still execute for
fork PRs.

Co-Authored-By: Claude Fable 5 <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.

ZKsync Mailbox functions deprecation

1 participant