Skip to content

fix(L1): bump SystemConfig semver for the ETHLockbox removal - #440

Merged
jackchuma merged 1 commit into
mainfrom
bump-systemconfig-semver
Sep 15, 2026
Merged

jackchuma merged 1 commit into
mainfrom
bump-systemconfig-semver

Conversation

@jackchuma

Copy link
Copy Markdown
Collaborator

Summary

#439 bumped OptimismPortal2, DisputeGameFactory and AggregateVerifier on the grounds that each "[has] source changes relative to the versions currently deployed onchain, but still declare the same semver." SystemConfig meets that same description and was missed, so this bumps it 3.13.2 -> 3.14.0.

Why it qualifies

#378 (ETHLockbox removal) changed SystemConfig behaviour without touching the version:

-        address identifier = isFeatureEnabled[Features.ETH_LOCKBOX]
-            ? address(IOptimismPortal2(payable(optimismPortal())).ethLockbox())
-            : address(optimismPortal());
-        return superchainConfig.paused(address(0)) || superchainConfig.paused(identifier);
+        return superchainConfig.paused(address(0)) || superchainConfig.paused(optimismPortal());
  • paused() no longer selects the pause identifier based on whether the ETH_LOCKBOX feature is enabled.
  • _setFeature no longer guards ETH_LOCKBOX against being disabled while the portal still has a lockbox configured, or toggled while the system is paused.

snapshots/semver-lock.json already recorded the divergence. Comparing 1baa168c (the commit Base's Zeronet SystemConfig implementation was built from) against main:

1baa168c main
sourceCodeHash 0x1a24ffe1… 0x780ff372…
initCodeHash 0x3e8e52d9… 0xb91123c7…
@custom:semver 3.13.2 3.13.2

So the lock file and the declared version disagreed: the contract changed, the version did not.

Why minor, not major

No function, error or event declarations were added or removed, so the ABI is unchanged. That matches the DisputeGameFactory 1.4.0 -> 1.5.0 precedent in #439. OptimismPortal2 went major there only because it dropped ethLockbox() from its ABI.

Impact

Without this, a chain upgraded for Cobalt reports an unchanged SystemConfig version, so version() cannot distinguish the pre- and post-upgrade implementations and operators have to fall back to comparing implementation addresses. This surfaced while preparing the Zeronet Cobalt upgrade in contract-deployments#790, where the facilitator guide currently has to call out SystemConfig as a special case for exactly this reason.

Testing

  • just semver-lock regenerated; only the SystemConfig entry moves.
  • just snapshots produces no further changes, so no ABI or storage snapshot is affected.
  • just test: 1231 passed, 0 failed, 1 skipped.

A backport to releases/v8.3.0 follows separately.

Made with Cursor

#439 bumped OptimismPortal2, DisputeGameFactory and AggregateVerifier because
each had source changes relative to the versions deployed onchain while still
declaring the same semver. SystemConfig meets that description too and was
missed.

#378 changed two behaviours without touching the version:

- `paused()` no longer selects the pause identifier based on whether the
  ETH_LOCKBOX feature is enabled; it always uses `optimismPortal()`.
- `_setFeature` no longer guards ETH_LOCKBOX against being disabled while the
  portal still has a lockbox configured, or toggled while paused.

snapshots/semver-lock.json already records both a different sourceCodeHash and
initCodeHash for SystemConfig across that change, so the lock file and the
declared version disagreed.

Minor rather than major: no function, error or event declarations were added or
removed, so the ABI is unchanged. This matches the DisputeGameFactory 1.4.0 ->
1.5.0 precedent in #439; OptimismPortal2 went major only because it dropped
`ethLockbox()` from its ABI.

Co-authored-by: Cursor <cursoragent@cursor.com>
@cb-heimdall

cb-heimdall commented Sep 15, 2026

Copy link
Copy Markdown
Collaborator

✅ Heimdall Review Status

Requirement Status More Info
Reviews 1/1
Denominator calculation
Show calculation
1 if user is bot 0
1 if user is external 0
2 if repo is sensitive 0
From .codeflow.yml 1
Additional review requirements
Show calculation
Max 0
0
From CODEOWNERS 0
Global minimum 0
Max 1
1
1 if commit is unverified 0
Sum 1

@jackchuma
jackchuma merged commit 4951105 into main Sep 15, 2026
6 checks passed
@jackchuma
jackchuma deleted the bump-systemconfig-semver branch September 15, 2026 01:57
jackchuma added a commit that referenced this pull request Sep 17, 2026
main bumped OptimismPortal2 5.2.0 -> 6.0.0 in #438 for the ETHLockbox removal, and
this branch independently bumped 5.2.0 -> 6.0.0 for dropping `superchainConfig()`.
After merging main both ABIs declare 6.0.0 while differing by that getter, which is
the same divergence #438 and #440 set out to remove.

Major rather than minor: the removal is a breaking ABI change, matching the reason

Co-authored-by: Cursor <cursoragent@cursor.com>
#438 took the portal major in the first place.
jackchuma added a commit that referenced this pull request Sep 17, 2026
* refactor(L1): merge SuperchainConfig into SystemConfig

SuperchainConfig held the pause state and the Guardian/Incident Responder
roles for a single chain, while every pausable contract reached it by hopping
through SystemConfig. Collapsing the two removes a proxy, a shared ProxyAdmin,
and an indirection from every `paused()` read.

SystemConfig now owns:
- `GUARDIAN` / `INCIDENT_RESPONDER` immutables, set via the constructor.
- `pauseTimestamps` and the `pause`/`unpause`/`extend` API.
- `paused(address)`, `pausable`, `expiration`, and `pauseExpiry` views.

The former `superchainConfig` storage slot becomes `spacer_108_0_20` so
existing deployments keep their layout, and `pauseTimestamps` is appended.

Callers lose their `superchainConfig()` passthroughs (`config()` on
DelayedWETH) and read pause state straight off SystemConfig, so their ABIs
change and they take major version bumps. `initialize` also drops its
`_superchainConfig` argument.

Deploy tooling drops the whole superchain stage: no SuperchainConfig
implementation, proxy, or dedicated ProxyAdmin, and no separate upgrade step
for the shared proxy. The `superchainConfigGuardian` and
`superchainConfigIncidentResponder` deploy-config keys are renamed to
`guardian` and `incidentResponder` and now feed the SystemConfig constructor.

Pause coverage from SuperchainConfig.t.sol moves into SystemConfig.t.sol.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(L1): preserve pause state during upgrade

Co-authored-by: Codex <codex-noreply@coinbase.com>

* fix(L1): preserve legacy pause records during migration

Co-authored-by: Codex <codex-noreply@coinbase.com>

* chore(L1): refresh SystemConfig semver-lock hash

Co-authored-by: Cursor <cursoragent@cursor.com>

* docs(L1): document legacy pause migration risk

Co-authored-by: Codex <codex-noreply@coinbase.com>

* fix(L1): bump OptimismPortal2 semver for the superchainConfig removal

main bumped OptimismPortal2 5.2.0 -> 6.0.0 in #438 for the ETHLockbox removal, and
this branch independently bumped 5.2.0 -> 6.0.0 for dropping `superchainConfig()`.
After merging main both ABIs declare 6.0.0 while differing by that getter, which is
the same divergence #438 and #440 set out to remove.

Major rather than minor: the removal is a breaking ABI change, matching the reason

Co-authored-by: Cursor <cursoragent@cursor.com>
#438 took the portal major in the first place.

* test(L1): drop duplicated systemConfig assertions

Porting the `superchainConfig()` assertions onto `systemConfig()` left three getter
tests asserting the same address twice in a row.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(L1): validate supplied SystemConfig pause authorities

The Guardian and Incident Responder are now constructor immutables on the
SystemConfig implementation, so when `deploy` is handed a prebuilt implementation
set it never reads `implementationsInput.guardian` or `.incidentResponder` at all.
A stale implementation therefore installs its own pause authorities and the deploy
input silently has no effect.

Require a supplied implementation to carry the configured roles, and apply the
same non-zero Guardian check that freshly deployed implementations already get,
so a chain cannot deploy with nobody able to unpause it.

Co-authored-by: Cursor <cursoragent@cursor.com>

* docs(L1): name both pause roles in the migration warning

Legacy `SuperchainConfig.pause` is gated on `_assertOnlyGuardianOrIncidentResponder`,
so the Incident Responder can also open a pause in the window between the check and
the upgrade.

Co-authored-by: Cursor <cursoragent@cursor.com>

* chore(L1): restore semver-lock hashes built from source artifacts

A `just snapshots-check-no-build` run after the test-profile build rewrote the
ProtocolVersions and TEEProverRegistry initCodeHashes from those artifacts, and
they were committed alongside the OptimismPortal2 bump. Regenerated with
`just semver-lock`, which force-builds the source graph first.

Co-authored-by: Cursor <cursoragent@cursor.com>

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Codex <codex-noreply@coinbase.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.

3 participants