Repository navigation
fix: add Prysm v7 config keys to the mainnet chain config - #26
Merged
Merged
Conversation
networks/mainnet/config.yml sets PRESET_BASE: mainnet, so every key it does not define is inherited from Ethereum mainnet. Prysm v7 added three settings that Vana's config never overrides, and each inherited default breaks a Vana node in a different way: - FULU/GLOAS fork versions inherit 0x06000000/0x07000000, which collide with Ethereum's own registered config, so the node refuses to start. - SLOT_DURATION_MS inherits 12000 and takes precedence over SECONDS_PER_SLOT, so the node does 12s/slot arithmetic on a 6s chain and dies with "invalid payload timestamp". - BLOB_SCHEDULE inherits Ethereum's BPO forks at epochs 412672/419072, which reuse the preceding fork's digest because Fulu is disabled. That halts Prysm's allTopics() fork-schedule walk, leaving Capella/Deneb out of the gossip allowlist so every gossip topic is refused. Verified on a live mainnet node running Prysm v7.1.8: sync distance 0, fork 0x20000093 (Deneb), no gossip rejections. genesis_validators_root and the Deneb fork digest are unchanged. Also verified backward compatible with Prysm v5.1.0, which logs the new keys as unknown fields and continues. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Mainnet moves to a new deposit contract, 0xB98aafa6684aef18AEf518772F01F5aE0DA5eA4C, at block 10,229,958. The new contract was deployed at block 10,099,568 already seeded with the retired contract's final state -- deposit count 32 and root 0x60dff8f6a92d68799e7653acdcefa253a1c2603b4dd071d8265491b465172401 -- so the deposit index and merkle tree continue across the boundary and no node rebuilds its deposit history. Both values were read from the two contracts on chain. networks/mainnet/config.yml gains RETIRED_DEPOSIT_CONTRACT_ADDRESS and DEPOSIT_CONTRACT_SWITCH_BLOCK, and DEPOSIT_CONTRACT_ADDRESS becomes the new contract. Below the switch block a node scans only the retired contract, at and above it only the current one, so a node syncing from genesis still reconstructs the same trie. The beacon node moves to Vana's build of Prysm, ghcr.io/vana-com/prysm-beacon-chain, because upstream Prysm does not know the two new keys and refuses to start once the configured deposit contract differs from the one recorded in its database. This is the one change here that is not backward compatible, and BEACON_IMAGE pins it separately from PRYSM_VERSION. Only the beacon node is forked. The validator and prysmctl stay upstream and move to v7.1.8, the release the Vana build is based on, so the two no longer sit four minor versions apart. The switch is beacon-node work and the upstream validator loads this config unchanged: both v5.1.0 and v7.1.8 were run against it and report the two new keys as unknown fields in the same non-fatal message they already emit for FULU_FORK_VERSION and BLOB_SCHEDULE, with every recognised field still applied. Every image tag the compose file now references was confirmed to exist. The beacon service passes --clear-deposit-contract so that an operator upgrading across the switch does not have to edit docker-compose.yml: it clears the recorded contract address and the recorded migration marker, then writes the address again from the config on the same start, keeping all other data. The cost is that the marker is cleared on every start, so the check that refuses a switch block different from the one already migrated for does not fire; the comment says to drop the flag once the fleet is past the switch. .env DEPOSIT_CONTRACT_ADDRESS moves to the new contract too -- it is where submit-deposits sends deposits, and the retired contract no longer accepts them. Verified against ghcr.io/vana-com/prysm-beacon-chain:v7.1.8-vega.1: the shipped config starts and reaches genesis loading, and each validation guard was confirmed to fire by breaking it (switch block 0, deployment block above the switch, retired equal to current, retired as the zero address). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
inf-1oop
added a commit
that referenced
this pull request
Sep 14, 2026
## Problem
`docker-compose.yml` passes
`--beacon-rest-api-provider=${EXTERNAL_IP}:${RPC_PORT}`, which renders
as `0.0.0.0:4000`. That value is wrong three ways:
| | |
|---|---|
| **No scheme** | A REST endpoint is a URL. The gRPC provider on the
line above is a dial target and must *not* have one — they look alike
but are parsed by different code. |
| **Wrong port** | `4000` is the gRPC port. The HTTP API is on `3500`
(`--grpc-gateway-port`, now an alias for `--http-port`). |
| **Silently inert on v5** | Which is why nobody noticed. |
## Why it is breaking now, and not before
On Prysm **v5.1.0** the REST API is enabled only by the explicit
boolean, so the provider flag was dead config and the validator used
gRPC:
```go
// v5.1.0 config/features/config.go:296
if ctx.Bool(EnableBeaconRESTApi.Name) {
```
**v7.1.8** also enables it *implicitly* whenever the provider flag is
set:
```go
// v7.1.8 config/features/config.go:372
if ctx.Bool(EnableBeaconRESTApi.Name) || ctx.IsSet(validatorflags.BeaconRESTApiProviderFlag.Name) {
```
So the value stops being ignored and starts deciding how the validator
reaches the beacon.
**#26 moved `PRYSM_VERSION` from `v5.1.0` to `v7.1.8` and is already
merged**, so this is live on `main` today. Any operator who pulls `main`
and restarts their validator loses attestations.
## Reproduction
Against `gcr.io/prysmaticlabs/prysm/validator:v7.1.8` with the value
`main` currently produces, repeating every 12 seconds:
```
WARN flags: Enabled feature flag enable-beacon-rest-api=...
INFO rest: Initialized REST connection provider count=1 endpoints=[[invalid endpoint]]
ERROR beacon-api: failed to get health of node error=failed to create request for
endpoint [invalid endpoint]: parse "0.0.0.0:4000/eth/v1/node/health":
first path segment in URL cannot contain colon url=0.0.0.0:4000
WARN fallback: No responsive beacon node found tried=[[invalid endpoint]]
```
The same run on `v5.1.0` initialises no REST provider at all. Note the
gRPC provider *is* still constructed (`endpoints=[beacon:4000]`) — the
validator does not discard it, but the health check runs over REST,
fails to build a request, and the fallback layer concludes no beacon is
responsive. Net effect: zero duties.
## Fix
```diff
- --beacon-rest-api-provider=${EXTERNAL_IP}:${RPC_PORT:-4000}
+ --beacon-rest-api-provider=http://beacon:${GRPC_GATEWAY_PORT:-3500}
```
Verified on `v7.1.8` with isolated datadirs per container, so no
bolt-lock contention between runs:
| Config | REST endpoint | REST feature | parse errors in 40s |
|---|---|---|---|
| `0.0.0.0:4000` (current `main`) | `[invalid endpoint]` | on | **4** |
| `http://beacon:3500` (this PR) | `http://beacon:3500` | on | **0** |
| flag dropped entirely | `http://127.0.0.1:3500` (default, unused) |
off | 0 |
## Note on the approach
Dropping the flag was the other option: it restores v5.1.0's behaviour
exactly, since the feature flag then stays off and gRPC carries the
duties. Repointing it instead puts the validator on the standardised
Beacon API, which is where Prysm is heading and which works against any
consensus client — but it is a **transport change, not only a syntax
fix**, and deserves a validator-in-the-loop test before the fleet moves.
What is verified here is that the endpoint parses and initialises
correctly. What is **not** verified is that duties actually flow over
REST end to end — that needs a live beacon and real keys.
## Scope
`docker-compose.yml` only. The Caddyfile is untouched; public endpoint
exposure was handled separately in #27.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
maciejwitowski
added a commit
that referenced
this pull request
Sep 14, 2026
`npx semantic-release@25.0.9` pinned only the top package. semantic-release loads its plugins from its own node_modules tree, and every dependency below it (release-notes-generator ^14.1.0, conventional-changelog-writer ^8.0.0, the GitHub plugin's Octokit stack) resolved fresh on each run with no lockfile. Any new npm release in that tree reached a job that holds a contents:write token, unreviewed. Move the toolchain into .github/release/package.json with a committed package-lock.json: semantic-release 25.0.9, commit-analyzer 13.0.1, release-notes-generator 14.1.1, github 12.0.9, conventionalcommits preset 9.3.1. The job installs it with `npm ci --ignore-scripts` (no package in the tree has an install script) and runs that binary from the repo root. It lives under .github/ so the operator checkout gets no root package.json. Node is pinned to 24, the version this was verified on. Verified on Node 24 against main at 47db176, running the two workflow steps as written with the full .releaserc and a GitHub token, `--dry-run --no-ci`, no root node_modules: next version 2.3.2, notes render Bug Fixes with #26, #27 and #28. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Dqz4KWjLzxNEJuea75Avb7
inf-1oop
added a commit
that referenced
this pull request
Sep 16, 2026
## `main` currently ships a validator that cannot start #26 set `PRYSM_VERSION=v7.1.8`, but the validator command still passes `--db-backup-output-dir`, which Prysm **removed** in v7: ``` Incorrect Usage: flag provided but not defined: -db-backup-output-dir ``` The container then crash-loops. This is not latent — it affects anyone who pulls `main` and starts a validator today. I hit it for real upgrading `contango-validator-1`, where it cost ~2.5 minutes of missed duties. Reproduced against `gcr.io/prysmaticlabs/prysm/validator:v7.1.8` with the exact flag set `main` renders: | flag set | result on v7.1.8 | |---|---| | `main` today | `Incorrect Usage: flag provided but not defined: -db-backup-output-dir` | | this PR | `INFO main: Prysm Validator started version=Prysm/v7.1.8/51b5a75...` | ## Three fixes, all in `docker-compose.yml` ### 1. Drop the two dead backup flags `--db-backup-output-dir` is gone in v7. `--enable-db-backup-webhook` goes with it — v7 keeps it only in `config/features/deprecated_flags.go` as a hidden no-op, so it logs `is deprecated and has no effect` on every start and does nothing. ### 2. `validator-backup` actually backs something up The service POSTs to the validator's `/db/backup` webhook — which is precisely what the deprecated flag used to enable. **The call returns success while backing up nothing.** That matters more than a normal dead feature: the README tells operators to run it before upgrading, and **slashing protection history lives in that database**. A backup step that silently no-ops is worse than no backup step, because it buys false confidence before the one operation where you need the file. It now runs `slashing-protection-history export`, which writes the standard **EIP-3076** file that any client can import. One constraint, spelled out in a comment in the file: it must run with the validator **stopped**, because `validator.db` is a bolt database with a single-writer lock. ```bash docker compose stop validator docker compose --profile backup run --rm validator-backup docker compose --profile node --profile validator --profile init up -d validator ``` ### 3. Restart policy for `geth`, `beacon`, `validator` Only `stats` and `caddy` used the `x-restart` anchor. Without it, an OOM kill or crash stops attesting until a human notices — an operator fleet has already hit this, with a geth OOM-killed during an archive sync sitting dead for two days while the node still reported "up". ## Verification - `main`'s rendered flag set fails immediately on v7.1.8 with the error above - this PR's flag set reaches `Prysm Validator started` - `validator slashing-protection-history export` exists in v7.1.8 and accepts `--slashing-protection-export-dir` - `docker compose config` clean across the `node`, `validator`, `init` and `backup` profiles - the running upgrade on `contango-validator-1` (canonical validator v7.1.8 + `v7.1.8-vega.1` beacon) has been performing all duties — attestations, aggregates, sync committee messages and contributions, and block proposals — for 4+ hours ## Deliberately not in this PR These came out of the same upgrade but need testing rather than review, so they belong to the validator rollout session: - **`--enable-doppelganger`** is absent from the repo. It is a real anti-double-signing control and operators are hand-rolling it in overrides. Shipping it costs ~2 epochs of missed attestations per start, which deserves a deliberate decision. - **The override trap.** Compose *replaces* `command:` rather than merging, so an operator override restating the validator command silently bypasses every future fix here — #28's REST fix was in the repo and inert on the node for exactly this reason. Fixes 3 (and doppelganger) reduce the *reason* to keep an override at all; the README should also state the rule. - **v5→v7 renamed log strings** (`Submitted new sync message` → `Submitted sync committee messages`). Any alerting that greps validator logs goes quiet and looks healthy. This fooled my own checks twice. - **No rollback path.** A v7→v5 validator downgrade is unsupported and untested. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
maciejwitowski
added a commit
that referenced
this pull request
Sep 24, 2026
## Problem The Release workflow fails at `generateNotes` on every push that should cut a release. No GitHub release has been published since v2.3.1 (2026-08-03), so #27, #26 and #28 are on `main` with no release. And v2.3.1 itself shipped with an empty body. | Run | Commit | Result | |---|---|---| | 2026-08-03 | ffd802d #21 | success, but v2.3.1 notes are the version header only | | 2026-08-12 → 08-19 | chore/ci commits | success (no release due, notes step never ran) | | 2026-09-04 | a4cb188 #27 | failure | | 2026-09-14 | a409872 #26 | failure | | 2026-09-14 | 47db176 #28 | failure | ``` Failed step "generateNotes" of plugin "@semantic-release/release-notes-generator" Error: Missing helper: "conventional-changelog-conventionalcommits requires conventional-changelog-writer@9 or newer ..." ``` ## Cause The workflow installed the release tooling unpinned, with no lockfile: - `npx semantic-release` resolves to 25.0.9. Its `@semantic-release/release-notes-generator` 14.1.1 (the latest) depends on `conventional-changelog-writer ^8`. - `npm install --no-save conventional-changelog-conventionalcommits` resolves to the latest preset. Preset **v10** targets writer 9: - 10.0.0–10.3.0 (from 2026-06-26): no error, but writer 8 renders only the version header. That is why the [v2.3.1 release](https://github.com/vana-com/vana/releases/tag/v2.3.1) has no notes, while v2.3.0 (January, preset 9.x) does. - 10.4.0 (2026-08-18): added an explicit guard, which turned the silent empty output into the hard failure above. Pinning only the top-level packages does not close this. semantic-release loads its plugins from its own `node_modules` tree, and every range below it (`release-notes-generator ^14.1.0`, `conventional-changelog-writer ^8.0.0`, the GitHub plugin's Octokit stack) resolves fresh on each run. The job holds a `contents: write` token, so any new npm release in that tree runs with it, unreviewed. ## Fix - New `.github/release/package.json` + committed `package-lock.json` for the toolchain: | Package | Version | |---|---| | `semantic-release` | 25.0.9 | | `@semantic-release/commit-analyzer` | 13.0.1 | | `@semantic-release/release-notes-generator` | 14.1.1 | | `@semantic-release/github` | 12.0.9 | | `conventional-changelog-conventionalcommits` | 9.3.1, the last major built for writer 8 | - The job installs it with `npm ci --ignore-scripts --prefix .github/release` (no package in the tree has an install script) and runs `.github/release/node_modules/.bin/semantic-release` from the repo root, so `.releaserc.yaml` is still the config. - It lives under `.github/` so the operator checkout gets no root `package.json`. `.gitignore` covers its `node_modules`. - Node pinned to `24`, the version this was verified on. - A comment in the workflow says when to bump the preset: once the notes generator ships writer 9. ## Verification Ran the two workflow steps as written, on Node 24 / npm 11, on a checkout of `main` at 47db176 with no root `node_modules`, the full `.releaserc.yaml` and a GitHub token, `--dry-run --no-ci`: - `npm ci` installs one deduped copy of each package; every plugin and the preset resolve from `.github/release/node_modules`. - GitHub `verifyConditions` passes, next version **2.3.2**, notes render `### Bug Fixes` with #26, #27, #28, same format as v2.3.0. Preset comparison (same command pair, earlier run): | Preset | Result | |---|---| | 9.3.1 | notes render Bug Fixes | | 10.3.0 | notes are the header only | | 10.4.0 | fails with the same "Missing helper" error as CI | Cosmetic: #26's commit body contains the word "closes" before "compatibility", so its line gets a bogus `closes #compatibility` link. Harmless; editing the release body after publish fixes it if wanted. ## Heads-up on merging Merging this triggers the workflow on `main`, which will **publish v2.3.2** as a public GitHub release containing #26, #27 and #28 (Prysm v7 config keys, the deposit contract switch config, the validator REST fix). The GitHub plugin also comments "included in version 2.3.2" on those PRs and labels them `released`, as it did on #21 for v2.3.1. Anyone watching this repo's releases gets notified, so merge when that announcement is fine to go out. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01Dqz4KWjLzxNEJuea75Avb7 --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
🎉 This PR is included in version 2.4.0 🎉 The release is available on GitHub release Your semantic-release bot 📦🚀 |
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.
This PR carries two changes to the mainnet chain config:
They are together because both touch
networks/mainnet/config.ymland both land in the same operator upgrade.Part 1 — Prysm v7 config keys
Problem
networks/mainnet/config.ymldeclaresPRESET_BASE: mainnet, so every key it does not define is inherited from Ethereum mainnet. Prysm v7 added three settings this config never overrides, and each inherited default breaks a Vana node in a different way — two of them silently.FULU_FORK_VERSION/GLOAS_FORK_VERSION0x06000000/0x07000000version 0x06000000 for fork fulu in config vana conflicts with existing config named=mainnetSLOT_DURATION_MS12000SECONDS_PER_SLOTfor all slot timing → 12s/slot arithmetic on a 6s chain → FATALinvalid payload timestampBLOB_SCHEDULEOn
BLOB_SCHEDULEspecificallyA BPO entry only receives a distinct fork digest when its epoch is at or after
FULU_FORK_EPOCH. Fulu is disabled here, so Ethereum's BPO entries instead reuse the preceding fork's digest — and since 412672/419072 fall between Bellatrix (epoch 2) and Capella/Deneb (503609), they carry Bellatrix's digest.Prysm's
allTopics()walks the fork schedule terminating onnext.ForkDigest != last.ForkDigest, so the repeated digest halts the walk before Capella/Deneb. The allowlist ends up with 221 topics and no78ba1733(the live Deneb digest), sopubsubrefuses everything withtopic is not allowed by the subscription filter.This one is the nastiest to diagnose: the node still starts and follows the chain by range sync, so it looks alive. It just participates in nothing — no gossip, and a sync distance that drifts instead of sitting at 0.
Verification
Tested on a live Vana mainnet node running Prysm v7.1.8:
invalid payload timestamp, then (afterSLOT_DURATION_MS) a running-but-gossip-dead node with sync distance drifting 2→17 and block arrival at 7–13s.Consensus-safety checks:
genesis_validators_rootunchanged:0x7a2ee1f3a4332b8a3229efd4cba0903049bc8b1e7cd1fe46052e320cf3f6d184current_version 0x20000093, head block versiondeneb0x78ba1733MaxBlobsPerBlockAtEpoch()at the current epoch is 6 both before and after — the inherited BPO entries were superseded by Deneb, so emptying the schedule changes no blob limitPart 2 — Deposit contract switch
Mainnet moves to a new deposit contract at block 10,229,958.
0xB98aafa6684aef18AEf518772F01F5aE0DA5eA4C0x17BbE91c315Bf14f38F6D35052a827cadfFe184e10229958Why this does not break the deposit trie
The new contract was deployed at block 10,099,568 already seeded with the retired contract's final state, so the merkle index continues across the boundary instead of restarting at 0. Read from both contracts on chain:
get_deposit_count()3232get_deposit_root()0x60dff8f6…1724010x60dff8f6…172401Below the switch block a node scans only the retired contract; at and above it, only the current one. A log batch that straddles the boundary is split into two queries, one per contract. A node syncing from genesis therefore reconstructs exactly the deposit history it always did, and no node resyncs.
This depends on a patch to Prysm (vana-com/prysm#1), released as
v7.1.8-vega.1, which adds the two config keys and the split scan.Compatibility
This is the part that is not backward compatible. The beacon node moves to Vana's build of Prysm,
ghcr.io/vana-com/prysm-beacon-chain:v7.1.8-vega.1, pinned by a newBEACON_IMAGEvariable. Upstream Prysm cannot run against this config: it does not know the two new keys, and it refuses to start at all onceDEPOSIT_CONTRACT_ADDRESSno longer matches the address recorded in its own database.Only the beacon node is forked. The validator and prysmctl stay on upstream Prysm, moving from
v5.1.0tov7.1.8— the release the Vana build is based on — so the two components no longer sit four minor versions apart. The switch is beacon-node work; the upstream validator reads this config file fine. Both v5.1.0 and v7.1.8 were run against it and report the two new keys as unknown fields in the same non-fatal message they already emit forFULU_FORK_VERSIONandBLOB_SCHEDULE, with every recognised field still applied —UnmarshalStrictpopulates what it recognises before collecting the strict errors, and the loader logs and continues.For part 1 alone, the earlier claim still holds: Prysm v5.1.0 logs the new keys as unknown fields and continues normally.
Operator impact
Upgrading is an image change:
beaconservice passes--clear-deposit-contract. A node's database records the deposit contract address it last ran with and refuses to start when the configured one differs (database contract is 0x17bbe91c... but tried to run with 0xb98aafa6...). The flag clears that record and the migration marker, then writes the address again from the config on the same start. All other data is kept — this is not a resync. It is on by default so operators don't have to editdocker-compose.yml; the cost is that the marker is cleared on every start, so the check that refuses a changed switch block does not fire. The comment in the file says to drop it once the fleet is past the switch.Rewinding deposit log scan.DEPOSIT_CONTRACT_ADDRESSin.envmoves to the new contract — it is wheresubmit-depositssends deposits, and the retired contract no longer accepts them.PRYSM_VERSIONcrosses two major versions for the validator, so operators should back up the validator database (slashing protection) first.Verification
Rehearsed end to end on a 4-stack shadowfork devnet, all 8 phases, zero failures:
v5.1.0→ patched, one stack at a time, while the other three kept producing. No keys moved, no node retired; 31 of 34 slots produced across the cutover, finality never stalled, and the deposit-log scan came back clean on all 4 stacks.incorrect merkle index: 0andCould not process deposit log: 0, confirming the switch does not disturb the later EIP-6110 transition.Against the published image with the config in this PR: it starts and reaches genesis loading, and each validation guard was confirmed to fire by deliberately breaking it (switch block 0, deployment block above the switch, retired equal to current, retired as the zero address). Every image tag the compose file now references was confirmed to exist.
Open question: the pre-switch window
The switch block is roughly 9 days out, but the new contract is already live and accepting deposits — its only gate is a pubkey allowlist (
DepositContract: publicKey not allowed), not a block height.That leaves a ~130,000-block window in which a deposit from an allowlisted key would land below the switch block, where no node scans the new contract. The result would be an index gap and nodes halting deposit processing. The devnet rehearsal avoided this by setting the switch block to the new contract's deployment block, which leaves no window.
Two ways to close it, for reviewers to decide:
DEPOSIT_CONTRACT_SWITCH_BLOCK: 10099568(the deployment block) — strictly safe, since the contract could not emit before it existed.10229958and hold the allowlist empty until then, as an operational guarantee.Separately worth noting: the retired contract now reverts on
get_deposit_count()andget_deposit_root()— it answered32at block 9,475,268 and reverts by block 10,000,000. Since the config still points at it today, that is the likely source of anyUnable to process past deposit contract logserrors currently seen on mainnet. The switch resolves it, and the Vana build additionally makes an unreadable deposit count non-fatal.Not included:
networks/moksha/config.ymlMoksha is missing all four v7 keys too and will hit the same class of problem. I have deliberately left it out because I could not test it, and it is not a copy-paste of this change:
ELECTRA_FORK_VERSION: 0x60000038, not the0x2000009xseries), so Fulu/Gloas versions need a deliberate choice.Moksha also has no deposit contract switch of its own.
Worth a follow-up from someone who can run a Moksha node.
🤖 Generated with Claude Code