Skip to content

feat(bridge): let the upgrade script set implementation immutables - #265

Open
poszu wants to merge 1 commit into
mainfrom
bartosz/bridge-upgrade-immutables
Open

feat(bridge): let the upgrade script set implementation immutables#265
poszu wants to merge 1 commit into
mainfrom
bartosz/bridge-upgrade-immutables

Conversation

@poszu

@poszu poszu commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

UpgradeBridge.s.sol cannot change an implementation immutable. BRIDGE_CONTRACT and CHAIN_ID live in implementation bytecode rather than storage, and run(address) reads them off the current implementation before redeploying. That is correct for an ordinary code upgrade, but it means a value that needs to change cannot be — the upgrade faithfully reproduces whatever is already there.

Change

  • New run(address,address,uint256) overload taking the pod bridge address and chain id explicitly.
  • run(address) unchanged — it still carries the current implementation's values over, so upgrade_bridge.sh and existing flows keep working. Both overloads delegate to a shared _upgrade.
  • Both paths now assert, after the swap, that the immutables took effect and that the stored domainSeparator still matches CHAIN_ID and version.

That last assertion is the non-obvious one. domainSeparator is storage, computed at the last _updateVersion. Changing CHAIN_ID therefore strands it until the next updateValidatorConfig, and claims in the interim would verify against a separator that no longer corresponds to the chain id — a silent failure rather than a loud one. Failing the script is the better outcome.

Verification

forge build is clean and both overloads appear in the ABI (run(address), run(address,address,uint256)). Exercised against a live proxy on Arbitrum Sepolia — dry run first, then broadcast — with the immutables applied and the domain-separator assertion passing.

The change is narrow by construction: masking the four immutable spans, forge build's deployedBytecode is byte-identical to already-deployed implementations, so the immutables are the only thing an upgrade through this path alters.

No test references this script, and the change is confined to a deployment script, so the suite is unaffected.

🤖 Generated with Claude Code

`BRIDGE_CONTRACT` and `CHAIN_ID` are immutables, so they live in
implementation bytecode rather than storage. `run(address)` reads them off
the current implementation and redeploys the same values -- correct for an
ordinary code upgrade, but it means a value that needs to change cannot be:
the upgrade faithfully reproduces whatever is already there.

Adds a `run(address,address,uint256)` overload taking the pod bridge address
and chain id explicitly. `run(address)` is unchanged and still carries the
current values over, so upgrade_bridge.sh keeps working. Both delegate to a
shared `_upgrade`.

Both paths now assert, after the swap, that the immutables took effect and
that the stored `domainSeparator` still matches CHAIN_ID and version.
`domainSeparator` is storage, computed at the last version update, so
changing CHAIN_ID strands it until the next `updateValidatorConfig`, and
claims in the interim would verify against a separator that no longer
corresponds to the chain id. Better to fail the script than leave that live.

Co-Authored-By: Claude Opus 5 (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