feat(contracts): add contract upgrade version check#2112
Merged
mergify[bot] merged 8 commits intomainfrom Mar 16, 2026
Merged
Conversation
🧪 CI InsightsHere's what we observed from your CI run for 88bce8d. 🟢 All jobs passed!But CI Insights is watching 👀 |
Contributor
Author
Local validationRan the script locally against
Also confirmed: forge compilation errors are surfaced in CI output (not swallowed). |
Contributor
|
@claude /review |
Contributor
Author
Live testPushed
Will revert once CI confirms. |
868e7fb to
c08b695
Compare
1ffb7c2 to
55562df
Compare
Add `cbor_metadata = false` and `bytecode_hash = 'none'` to both host-contracts and gateway-contracts foundry.toml. This strips compiler metadata from deployed bytecode, making bytecode comparison across branches reliable for upgrade hygiene checks. Also add OZ remappings to gateway-contracts for Foundry compatibility.
New workflow + Bun/TypeScript script that compares deployed bytecode
between main and PR for every contract in upgrade-manifest.json.
When bytecode changes, the check enforces:
- REINITIALIZER_VERSION must be bumped
- A reinitializeV{N-1}() function must exist (not just commented out)
- At least one of MAJOR/MINOR/PATCH_VERSION must be bumped
When bytecode is unchanged, spurious REINITIALIZER_VERSION bumps are
flagged. Uses matrix strategy for host-contracts and gateway-contracts.
Addresses are generated via `make ensure-addresses` (same as upgrade tests).
Compare PR bytecode against UPGRADE_FROM_TAG (v0.11.0) instead of main. This avoids unnecessary reinitializer bumps when multiple PRs modify the same contract between deployments — only the first change after a release requires a bump. Keeps the tag in sync with *-upgrade-tests.yml workflows.
55562df to
9231fe5
Compare
When comparing against the v0.11.0 baseline, forge tries to compile all .sol files including ones that were deleted in the PR (e.g. MultichainACLChecks.sol). These reference removed address constants and break compilation of unrelated contracts like GatewayConfig, causing false positives. Strip deleted files from the baseline before compiling.
When forge can't compile the baseline (e.g. deleted contracts break unrelated imports), compare source files directly instead of blindly treating all contracts as changed. If the contract source is identical between baseline and PR, bytecode is unchanged — no version bump needed. Fixes false positive for GatewayConfig which is unchanged since v0.11.0 but couldn't be compiled because MultichainACLChecks.sol (deleted in PR) was still imported by other v0.11.0 files.
When comparing bytecode between the baseline tag and the PR, both sides must compile with identical address constants (they're embedded in bytecode). Previously we generated addresses only on the PR side and copied them to the baseline. This broke when contracts were removed between versions — the baseline still had source files importing the deleted constant, causing forge compilation to fail for the entire project, including unrelated unchanged contracts. Add ci/merge-address-constants.ts: generates addresses on both sides independently, then merges them. PR values win for shared constants, baseline-only constants (removed contracts) are preserved so the baseline compiles, and PR-only constants (new contracts) are preserved so the PR compiles. Reverts the source-comparison fallback in check-upgrade-hygiene.ts — both sides now compile successfully so we always compare real bytecode.
Rename workflow, script, job names, and all references from the ambiguous "upgrade hygiene" to the descriptive "upgrade version check".
zmalatrax
reviewed
Mar 16, 2026
Contributor
Author
|
@Mergifyio queue |
Merge Queue Status
This pull request spent 2 hours 45 minutes 29 seconds in the queue, including 1 hour 44 minutes 59 seconds running CI. Required conditions to merge
|
59 tasks
dartdart26
pushed a commit
that referenced
this pull request
Mar 16, 2026
* feat(contracts): enable deterministic bytecode output
Add `cbor_metadata = false` and `bytecode_hash = 'none'` to both
host-contracts and gateway-contracts foundry.toml. This strips
compiler metadata from deployed bytecode, making bytecode comparison
across branches reliable for upgrade hygiene checks.
Also add OZ remappings to gateway-contracts for Foundry compatibility.
* feat(contracts): add contract upgrade hygiene CI check
New workflow + Bun/TypeScript script that compares deployed bytecode
between main and PR for every contract in upgrade-manifest.json.
When bytecode changes, the check enforces:
- REINITIALIZER_VERSION must be bumped
- A reinitializeV{N-1}() function must exist (not just commented out)
- At least one of MAJOR/MINOR/PATCH_VERSION must be bumped
When bytecode is unchanged, spurious REINITIALIZER_VERSION bumps are
flagged. Uses matrix strategy for host-contracts and gateway-contracts.
Addresses are generated via `make ensure-addresses` (same as upgrade tests).
* feat(ci): compare bytecode against last deployed release tag
Compare PR bytecode against UPGRADE_FROM_TAG (v0.11.0) instead of main.
This avoids unnecessary reinitializer bumps when multiple PRs modify the
same contract between deployments — only the first change after a release
requires a bump. Keeps the tag in sync with *-upgrade-tests.yml workflows.
* fix(ci): remove deleted source files from baseline before compiling
When comparing against the v0.11.0 baseline, forge tries to compile all
.sol files including ones that were deleted in the PR (e.g.
MultichainACLChecks.sol). These reference removed address constants and
break compilation of unrelated contracts like GatewayConfig, causing
false positives. Strip deleted files from the baseline before compiling.
* fix(ci): fall back to source comparison when baseline compilation fails
When forge can't compile the baseline (e.g. deleted contracts break
unrelated imports), compare source files directly instead of blindly
treating all contracts as changed. If the contract source is identical
between baseline and PR, bytecode is unchanged — no version bump needed.
Fixes false positive for GatewayConfig which is unchanged since v0.11.0
but couldn't be compiled because MultichainACLChecks.sol (deleted in PR)
was still imported by other v0.11.0 files.
* fix(ci): merge address constants from both baseline and PR
When comparing bytecode between the baseline tag and the PR, both sides
must compile with identical address constants (they're embedded in
bytecode). Previously we generated addresses only on the PR side and
copied them to the baseline. This broke when contracts were removed
between versions — the baseline still had source files importing the
deleted constant, causing forge compilation to fail for the entire
project, including unrelated unchanged contracts.
Add ci/merge-address-constants.ts: generates addresses on both sides
independently, then merges them. PR values win for shared constants,
baseline-only constants (removed contracts) are preserved so the
baseline compiles, and PR-only constants (new contracts) are preserved
so the PR compiles.
Reverts the source-comparison fallback in check-upgrade-hygiene.ts —
both sides now compile successfully so we always compare real bytecode.
* refactor(ci): rename upgrade hygiene to upgrade version check
Rename workflow, script, job names, and all references from the
ambiguous "upgrade hygiene" to the descriptive "upgrade version check".
* chore: remove accidentally committed plan file
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
UPGRADE_FROM_TAG, currentlyv0.11.0) — notmain— so multiple PRs touching the same contract between deployments only require one reinitializer bumpreinitializeV{N-1}()function exists (not commented out), and MAJOR/MINOR/PATCH version bumphost-contractsandgateway-contractsChanges
host-contracts/foundry.toml,gateway-contracts/foundry.toml—cbor_metadata = false+bytecode_hash = 'none'for deterministic bytecode outputci/check-upgrade-versions.ts— Bun script: readsupgrade-manifest.json, extracts version constants from source, comparesforge inspect deployedBytecodebetween baseline tag and PRci/merge-address-constants.ts— Merges generated address constants from both baseline and PR so both sides compile. Needed because contracts may be added/removed between versions — generating addresses on only one side would leave the other unable to compile.github/workflows/contracts-upgrade-version-check.yml— Workflow withUPGRADE_FROM_TAGenv var (same as*-upgrade-tests.yml), paths-filter, matrix strategyTest plan