Skip to content

espresso: deduplicate the generated contract bindings - #513

Open
philippecamacho wants to merge 2 commits into
espresso/batcherfrom
espresso/dedupe-bindings
Open

espresso: deduplicate the generated contract bindings#513
philippecamacho wants to merge 2 commits into
espresso/batcherfrom
espresso/dedupe-bindings

Conversation

@philippecamacho

@philippecamacho philippecamacho commented Aug 19, 2026

Copy link
Copy Markdown
Collaborator

Stacked on #459 (espresso/batcher). Closes #504.

Deletes espresso/bindings entirely: net −4,916 lines, no new logic.

batch_authenticator.go (2,277 lines) duplicated the binding already exported by the imported espresso-streamers module — and had drifted from it. e.

system_config.go (2,640 lines) existed for a single call, batcherHash(). The repo already generates that binding — op-e2e/bindings/systemconfig.go, via just gen-binding SystemConfig — so op-batcher uses it rather than keeping a third copy.

That import needs one line in ops/docker/op-stack-go/Dockerfile.dockerignore. The file excludes everything by default and re-includes an allowlist, and op-batcher is the first binary in a production image to depend on op-e2e/bindings — the other non-e2e importers aren't built by that Dockerfile. Without the line the image fails on a missing package even though a full-checkout go build ./... succeeds. Longer term that package belongs outside the e2e tree, which is too broad for this PR (35 importers).

Verified: go build ./..., go vet, gofmt, go test ./op-batcher/..., and docker build --target op-batcher-builder -f ops/docker/op-stack-go/Dockerfile . — all passing.

🤖 Generated with Claude Code


espresso/bindings shipped two large generated bindings that duplicated code
already available elsewhere in the build. The package is now gone entirely.

batch_authenticator.go (2277 lines) duplicated the binding already exported by
the imported espresso-streamers module, and the two ABIs had diverged: the
vendored copy predates the BatcherChangedThisBlock custom error, so it was a
contract revision behind. Comparing both against
packages/contracts-bedrock/snapshots/abi/BatchAuthenticator.json, the module's
copy matches the current contract exactly and the vendored one is missing
precisely that error. Nothing decodes custom errors from revert data today
(geth's bind v1 BoundContract.Call does not unpack them, and neither op-batcher
nor op-service/txmgr does so explicitly), so the staleness was latent rather
than an active decoding bug -- but a second copy of a binding is a copy that
drifts, and this one already had. Point op-batcher at
github.com/EspressoSystems/espresso-streamers/op/bindings, imported as
streamerBindings, as the canonical binding.

system_config.go (2640 lines) existed for a single call, batcherHash(), used
once to resolve the fallback batcher address -- and duplicated a binding the
repo already generates. op-e2e/bindings/systemconfig.go is produced by
`just gen-binding SystemConfig` (op-e2e/scripts/gen-binding.sh, abigen over the
forge artifact) and already exports SystemConfigCaller.BatcherHash, so use it
instead of vendoring or hand-rolling a third copy. op-e2e/bindings is a leaf
package with no dependencies beyond go-ethereum, and shipped binaries already
import it (op-chain-ops/cmd/check-fjord, op-chain-ops/cmd/check-jovian,
op-devstack/dsl).

Net: -4917 lines of generated code, no new code, and one canonical source for
each binding.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@philippecamacho
philippecamacho force-pushed the espresso/dedupe-bindings branch from ff41577 to acef413 Compare August 19, 2026 21:09

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: acef413273

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread op-batcher/batcher/espresso_active.go
op-batcher now imports op-e2e/bindings for the generated SystemConfig binding,
but Dockerfile.dockerignore excludes everything by default and re-included only
/op-e2e/e2eutils, so `just op-batcher` inside the image failed on a missing
package even though a full-checkout `go build ./...` succeeded.

Nothing had exercised this before: the other non-e2e importers of that package
(op-chain-ops/cmd/check-fjord, op-chain-ops/cmd/check-jovian, op-devstack/dsl)
are not built by this Dockerfile, and /op-devstack is not in the context at all.
op-batcher is the first binary in a production image to depend on it.

Verified by building a probe image against this dockerignore: before the change
/app/op-e2e contains only e2eutils; after it, bindings/systemconfig.go is
present.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@palango

palango commented Aug 20, 2026

Copy link
Copy Markdown

@philippecamacho Nice reduction in code.

Just a general question: How are the streamer bindings generated? I'd say this repo's contract should be the source of truth for bindings, so we need to make sure they match.

@piersy

piersy commented Aug 20, 2026

Copy link
Copy Markdown

Hi @philippecamacho. I'm in agreement with @palango, it's strange to house the contract in this repo, but have generated bindings in another repo that this repo then uses.

Instead I would suggest, writing a small interface in the espresso streamer that contains just the methods required by the streamer (currently just one) and then passing an instance of BatchAuthenticatorCaller to the streamer at construction. Then both the contract and bindings could live in this repo.

@palango

palango commented Aug 20, 2026

Copy link
Copy Markdown

Here's another comment from Claude:

Half the premise holds. The other half is backwards.

I diffed all four bindings against packages/contracts-bedrock/snapshots/abi/. BatchAuthenticator is as you say: 59/59 identical to the snapshot, and the vendored copy you deleted was missing BatcherChangedThisBlock. SystemConfig goes the other way. The espresso/bindings/system_config.go you deleted matched the snapshot exactly, 77/77. op-e2e/bindings/systemconfig.go has 70 entries and hasn't been regenerated since 2025-10-14 (3c82c3f761), while SystemConfig.sol moved as recently as 2026-04-25. Missing from it: setBatcherHash, isCustomGasToken, delayedWETH, lastUsedOPCM, lastUsedOPCMVersion, DELAYED_WETH_SLOT, OPCM_SLOT. batcherHash() is in both, so nothing breaks today. But we've just made the unmaintained copy the canonical one, and no CI job diffs it against the snapshot.

The import isn't free either. Building ./op-batcher/cmd on both branches: 49,033,074 bytes before, 49,892,434 after. That's +859 KB out of a PR that deletes 4,900 lines. go tool nm finds 215 op-e2e/bindings.* symbols in the new binary and none in the old: EntryPoint, EAS, Permit2, Safe, MIPS. strings says the payload is live rather than stripped, and it can't be stripped, because the package-level *bind.MetaData vars are data-section roots. So we ship 53 contract bindings to call batcherHash().

The rest:

  1. The dockerignore entry props up something the PR body itself calls wrong. op-e2e imports op-batcher in ten files; now op-batcher imports op-e2e/bindings, and a production image has to carry part of the e2e tree. You defer that as too broad at 35 importers, but moving systemconfig.go on its own touches none of the other 34 and takes the dockerignore line with it. If the line stays, comment it — every other non-obvious entry in that file has one, and deleting it breaks nothing except docker build --target op-batcher-builder.
  2. Neither changed function has a test. There's no espresso_active_test.go, and isBatcherActive, fallbackBatcherAddr and BatcherHash appear in no test in the package. fallbackBatcherAddr now straddles two binding packages, which is where an ABI mismatch would bite, and it fails quietly: shouldSkipPublishForActiveSeq (espresso_driver.go:312) logs a Warn and returns true, so the fallback batcher stops publishing while the process stays green. The one test you touched covers the half that was already provably identical.
  3. We don't end up with one canonical source. op-node/rollup/derive/batch_authenticator.go:33 still hardcodes BatchInfoAuthenticated(bytes32,address) as a string and keccaks it, and that's the copy gating derivation — get it wrong and authenticated batches vanish instead of a tx reverting. Three sources of BatchAuthenticator truth left, four binding homes. Fold op-node in too, or say in the body that this is op-batcher only.
  4. fallbackBatcherAddr (espresso_active.go:108) truncates a malformed batcherHash where op-node rejects it. common.BytesToAddress(batcherHash[:]) takes the low 20 bytes without looking at the leading 12; solabi.ReadAddress (op-service/solabi/util.go:53-63) checks the padding and errors. Pre-existing, but you're rewriting the signature and both call sites anyway.

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