Skip to content

contracts-bedrock: remove SystemConfig batchInbox and setGasConfig - #21641

Merged
mds1 merged 12 commits into
developfrom
mds/systemconfig-remove-unused-getters
Jul 31, 2026
Merged

contracts-bedrock: remove SystemConfig batchInbox and setGasConfig#21641
mds1 merged 12 commits into
developfrom
mds/systemconfig-remove-unused-getters

Conversation

@mds1

@mds1 mds1 commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

During U19, OPCMv2's reinit-everything path recomputed the SystemConfig batch inbox address via _chainIdToBatchInboxAddress (the new-chain scheme) instead of preserving the existing value, rotating the onchain batch inbox for all pre-OPCM chains (OP, Unichain, Mode, Metal, Zora). Nothing in the OP Stack reads it — derivation takes the batch inbox from the rollup config — so it was harmless, but it showed the onchain copy is a redundant source of truth that can silently drift. This PR removes the copy rather than fixing the reinit path. Closes #21614.

Two removals, both eliminating a duplicate write path instead of patching its symptom:

  1. batchInbox() and the _batchInbox initializer param are gone, and initialize now clears the legacy slot so the stale value is zeroed for every chain at the next upgrade. OPContractsManagerV2._makeSystemConfigInitArgs no longer computes a batch inbox (the exact line that caused the U19 rotation), OPContractsManagerMigrator no longer reads and re-passes one, and the now-unused _chainIdToBatchInboxAddress helper is removed from OPContractsManagerUtilsCaller. OPContractsManagerUtils.chainIdToBatchInboxAddress stays — op-deployer still derives new-chain batch inboxes with the same convention.

  2. setGasConfig and _setGasConfig are gone. setGasConfig could only write version-0 scalars (the require blocks a nonzero top byte) and did not update the basefeeScalar/blobbasefeeScalar mirrors. OPCMv2's _loadFullConfig reads those mirrors, not scalar(), so calling setGasConfig desynchronized them and the next upgrade silently reverted the operator's change — the same recompute-instead-of-preserve bug class as the batch inbox. op-node accepts version-0 scalars after Ecotone (CheckEcotoneL1SystemConfigScalar allows L1ScalarBedrock), so this was live rather than theoretical. Because _setGasConfigEcotone writes basefeeScalar, blobbasefeeScalar, and scalar atomically, removing the only other writer makes the mirrors agree with scalar by construction — no OPCM change needed.

overhead stays, now marked @custom:deprecated. setGasConfig was its only writer, so it is no longer writable, and _setGasConfigEcotone hardcodes uint256(0) in its place instead of re-emitting the stored value: op-node ignores the overhead after Ecotone, and the FEE_SCALARS payload has to stay 64 bytes, so the word cannot simply be dropped. Keeping the getter also means superchain-registry's L1 staging report, which calls overhead() on every chain, needs no change. Removing the variable outright is deliberately deferred to a future tech-debt pass rather than scope-creeping this one.

op-chain-ops/cmd/ecotone-scalar is deleted. It existed to compute the scalar argument for setGasConfig; the just ecotone-scalar recipe and the op-chain-ops README entry go with it. An old monorepo commit can be checked out if it is ever needed for historical purposes.

Semver 3.14.2 → 4.0.0, init version 3 → 4. OPCMv2 gets the dev-work patch bump to 7.2.4.

Test coverage

GPOParamsChange in op-e2e/actions/derivation/system_config_test.go had two halves, and only one of them used setGasConfig. Both survive, split in two.

The half that never touched the setter is now GPODefaultParams. It builds a Delta-only chain, sends one L2 transaction, and checks the pre-Ecotone L1 data fee end to end against the genesis gas config: L1GasPrice matches the L1 origin basefee, L1Fee == basefee * L1GasUsed * FeeScalar, L1Fee matches types.L1Cost(L1GasUsed - 2100, basefee, 2100, 1_000_000), and FeeScalar is 1. That exercises op-node deriving the fee parameters, the sequencer applying them through the L1 attributes transaction, and the values surfacing on the receipt.

The half that changed the gas config mid-chain is rebuilt on Ecotone as TestGPOParamsChangeEcotone, using setGasConfigEcotone instead of setGasConfig. It activates Ecotone as the latest fork, moves the basefee scalar 1_000_000 → 2_300_000 and the blobbasefee scalar 0 → 800_000, and checks the same three points in time the old test did: the derived scalars are unchanged in the last L2 block before the L1 origin with the update is adopted, they change in the exact block that adopts it, and they persist across a later L1 origin. At each point the receipt's L1 data fee is recomputed independently with opfees.L1CostEcotone from the fee parameters the receipt itself reports, and the receipt's L1BaseFeeScalar/L1BlobBaseFeeScalar are checked against the values written on L1. It is not parameterized over batch type, because Ecotone implies Delta and the singular-batch case is unreachable.

One assertion could not be carried over as-is: the old test compared the derived config against Genesis.SystemConfig wholesale. On an Ecotone chain that never holds, because initialize calls _setGasConfigEcotone, so the derived config is already on the versioned scalar encoding with a zeroed overhead while the genesis config still carries the pre-Ecotone encoding from the deploy config. The new test compares the scalars instead.

testFuzz_setGasConfigEcotone_succeeds now seeds the overhead slot with 2100 before calling the setter, so the event assertion proves the emitted zero is hardcoded rather than read from storage, and asserts overhead() still returns 2100 afterwards.

Why removal is safe

I audited the monorepo (Go, Rust, Solidity), superchain-registry, superchain-ops, monitorism, security-tools, op-txverify, ecosystem, specs, and docs, plus a GitHub code search across the org. batchInbox() has zero contract-level readers: op-node/cmd/batch_decoder uses rollupCfg.BatchInboxAddress, superchain-registry's staging.go uses the deploy config, and op-geth's superchain/types.go is a config struct field. No monitor reads startBlock() either — monitorism's withdrawals-v2 StartBlock is a --start.block CLI flag for backfill scanning.

No superchain-ops change needed

OPCMUpgradeV800 and OPCMUpgradeV700 both call sysCfg.batchInbox() while etching code at EOA-valued slots, but they do it from _templateSetup, which runs before the upgrade executes. The SystemConfig is still on the pre-V800 version at that point, so the getter is there, and neither template reads it afterwards. ethereum-optimism/superchain-ops#1492 was opened on the assumption that a version-tolerant read was needed and has been closed as unnecessary; its target file had also since been removed from main in ethereum-optimism/superchain-ops#1501.

Note for third parties

Reading batchInbox() or calling setGasConfig stops working at op-contracts v8.0.0. The batch inbox address remains in the superchain registry and in each chain's rollup config, which are the values the OP Stack actually uses. overhead() keeps working, but FEE_SCALARS ConfigUpdate events now carry a zero in its slot.

Companion PR: ethereum-optimism/specs#922.

@mintlify

mintlify Bot commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

Preview deployment for your docs. Learn more about Mintlify Previews.

Project Status Preview Updated (UTC)
Optimism-Docs 🟢 Ready View Preview Jul 8, 2026, 1:19 AM

Comment thread packages/contracts-bedrock/src/L1/SystemConfig.sol Outdated
@opgitgovernance opgitgovernance added the S-stale Status: Will be closed unless there is activity label Jul 22, 2026
@opgitgovernance

Copy link
Copy Markdown
Contributor

This pr has been automatically marked as stale and will be closed in 5 days if no updates

@mds1
mds1 force-pushed the mds/systemconfig-remove-unused-getters branch from cb71d80 to 4a40aa3 Compare July 27, 2026 15:31
mds1 added a commit to ethereum-optimism/superchain-ops that referenced this pull request Jul 27, 2026
…deV800

op-contracts/v8.0.0 removes `batchInbox()` from the SystemConfig and clears the legacy slot during
reinitialization, so a direct `sysCfg.batchInbox()` call reverts once a chain is on SystemConfig
4.0.0. Read it with a staticcall that works against both 3.14.x and 4.0.0 instead, resolving to
`address(0)` when the selector is missing. A missing selector reverts with empty return data, so we
still surface any revert that carries data rather than treating it as the post-removal case.

Same change in the SuperRootMigrate integration test, which duplicates the EOA-etching loop.

See ethereum-optimism/optimism#21641 and ethereum-optimism/optimism#21614. This carries forward the
fix from the now-obsolete #1492, which targeted the V800 templates before they were removed from
main in #1501.
@mds1 mds1 changed the title contracts-bedrock: remove batchInbox and startBlock from SystemConfig contracts-bedrock: remove SystemConfig batchInbox and setGasConfig Jul 27, 2026
mds1 added 3 commits July 27, 2026 12:24
During U19, OPCMv2's reinit-everything path recomputed the batch inbox address via
`_chainIdToBatchInboxAddress` (the new-chain scheme) instead of preserving the existing value,
rotating it for every pre-OPCM chain. Nothing in the OP Stack reads it, since derivation takes the
batch inbox from the rollup config, so it was harmless, but it showed the onchain copy is a
redundant source of truth that can silently drift. Remove the copy rather than fix the reinit path.
Closes #21614.

`batchInbox()` and the `_batchInbox` initializer param are gone, and `initialize` clears the legacy
slot. OPCMv2 no longer computes a batch inbox, the migrator no longer reads and re-passes one, and
the now-unused `_chainIdToBatchInboxAddress` helper is removed.
`OPContractsManagerUtils.chainIdToBatchInboxAddress` stays because op-deployer still derives
new-chain batch inboxes with the same convention.

`setGasConfig` goes for the same reason. It could only write version-0 scalars and did not update
the `basefeeScalar`/`blobbasefeeScalar` mirrors that OPCMv2's `_loadFullConfig` reads, so calling it
desynchronized `scalar` from those mirrors and the next upgrade silently reverted the change.
Because `_setGasConfigEcotone` writes all three atomically, removing the only other writer makes
them agree by construction, so OPCM needs no change. `overhead` is therefore immutable, which is
fine because op-node zeroes it after Ecotone.

`startBlock` is deliberately kept: `_setStartBlock()` only writes when the slot is 0, so
reinitialization preserves it, unlike the batch inbox which was overwritten unconditionally. Two
new tests cover the difference. Keeping it also means `op-node genesis l2` needs no change, and
keeping `overhead` means no storage layout change.

Removes the `GPOParamsChange` action test, which drove a pre-Ecotone GPO change through
`setGasConfig`. Action tests deploy contracts from local source, so there is no longer a function to
call. The derivation path it covered keeps unit coverage in the `GasConfig` case of
op-node/rollup/derive/system_config_test.go.

Semver 3.14.2 -> 4.0.0, init version 3 -> 4, OPCMv2 7.2.2 -> 7.2.3.
Deleting all of GPOParamsChange threw away coverage that did not depend on `setGasConfig`. Its first
half checks the pre-Ecotone L1 data fee end to end against the genesis gas config: op-node derives
the fee parameters, the sequencer applies them through the L1 attributes transaction, and they
surface on the receipt. Restore that as GPODefaultParams.

Only the second half needed the removed setter, and that scenario is unreachable now — no live chain
is pre-Ecotone, and nothing can write a version-0 scalar once `setGasConfig` is gone.

Drops the L2Batcher setup along with it, since only the removed half submitted batches.
Comment thread docs/public-docs/chain-operators/guides/features/custom-gas-token-guide.mdx Outdated
Comment thread op-chain-ops/cmd/ecotone-scalar/README.md Outdated
develop independently reached 7.2.3, so this PR's OPCMv2 changes need a
new dev-work patch bump to satisfy the semver-diff check.

Co-Authored-By: Claude
@sebastianst sebastianst self-assigned this Jul 28, 2026
@sebastianst sebastianst removed the S-stale Status: Will be closed unless there is activity label Jul 28, 2026
Comment thread docs/public-docs/app-developers/tools/data/data-glossary.mdx Outdated
Comment thread op-e2e/actions/derivation/system_config_test.go
Comment thread packages/contracts-bedrock/src/L1/SystemConfig.sol
`_setGasConfigEcotone` now encodes `uint256(0)` instead of the legacy
`overhead` value. op-node zeroes the overhead after Ecotone, so the emitted
value was already ignored, and the word cannot be dropped because the event
data is expected to be 64 bytes long.

The `overhead` variable and its getter stay, so the ABI is unchanged and
offchain readers (superchain-registry's L1 staging report, op-analytics) keep
working. `setGasConfig` was its only writer, so it is now immutable.
@OptimismBot
OptimismBot force-pushed the mds/systemconfig-remove-unused-getters branch from e2c7104 to 14e2268 Compare July 28, 2026 17:29
Mark `overhead` with `@custom:deprecated`, trim the comments this PR added, and
put a TODO on the legacy batch inbox slot clear so it and the constant come out
after U20 (maurelian).

Per seb: remove `op-chain-ops/cmd/ecotone-scalar` outright rather than reword its
README, source the batch inbox from the rollup config in the data glossary, and
rebuild the deleted half of `GPOParamsChange` on Ecotone as
`TestGPOParamsChangeEcotone` with `setGasConfigEcotone` instead of dropping its
assertions.

The rebuilt test cannot compare the derived config against `Genesis.SystemConfig`
wholesale the way the old one did: `initialize` calls `_setGasConfigEcotone`, so
on an Ecotone chain the derived config is already on the versioned scalar
encoding with a zeroed overhead while the genesis config still carries the
pre-Ecotone encoding. It compares the scalars instead.

Also point the custom gas token guide's fee bullets at `setMinBaseFee` and
`setOperatorFeeScalars`, the actual setters for those parameters.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Stale sourceCodeHash came from comment-only edits made after the last regen.

Co-Authored-By: Claude
Comment thread packages/contracts-bedrock/src/L1/SystemConfig.sol Outdated
Comment thread packages/contracts-bedrock/src/L1/SystemConfig.sol
Comment thread packages/contracts-bedrock/src/L1/SystemConfig.sol Outdated
Comment thread packages/contracts-bedrock/test/L1/SystemConfig.t.sol

@ajsutton ajsutton left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM.

Comment thread packages/contracts-bedrock/src/L1/SystemConfig.sol Outdated
Comment thread op-e2e/actions/derivation/system_config_test.go

@sebastianst sebastianst left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 GPT-5.6 says: Approved. The inline comments are non-blocking.

Comment thread op-e2e/actions/derivation/system_config_test.go
Comment thread op-e2e/actions/derivation/system_config_test.go Outdated
Comment thread op-e2e/actions/derivation/system_config_test.go Outdated
@mds1
mds1 added this pull request to the merge queue Jul 31, 2026
Merged via the queue into develop with commit d01c3e1 Jul 31, 2026
207 checks passed
@mds1
mds1 deleted the mds/systemconfig-remove-unused-getters branch July 31, 2026 00:54
claude Bot pushed a commit that referenced this pull request Jul 31, 2026
Resolves conflicts with #21641 (SystemConfig batchInbox/setGasConfig
removal): SystemConfig keeps upstream's 4.0.0 major version, which
subsumes the minor bump from this branch, and semver-lock.json is
regenerated from the merged sources.

Co-Authored-By: Claude
Co-authored-by: John Mardlin <john@oplabs.co>
claude Bot pushed a commit that referenced this pull request Jul 31, 2026
The merge from develop brought in #21641, which had already taken 4.0.0,
so this branch's SystemConfig changes (initVersion removal) need their
own version bump for the semver-diff check to pass.

Co-Authored-By: Claude
Co-authored-by: John Mardlin <john@oplabs.co>
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.

L1 contracts upgrade to op-contracts/v7.0.0-rc.4 overrides SystemConfig.batchInbox value.

8 participants