From moonbeam-engineering
Maintains bridge infrastructure for Moonbeam including substrate-relay updates, zombienet chain spec generation, and bridge integration testing. Use when upgrading polkadot-sdk stable versions, updating the substrate-relay binary, regenerating relay chain specs, or debugging bridge relayer issues.
How this skill is triggered — by the user, by Claude, or both
Slash command
/moonbeam-engineering:bridge-maintenanceThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
- [Overview](#overview)
Moonbeam uses a GRANDPA-based bridge to connect Moonbeam (on Polkadot) with Moonriver (on Kusama). The bridge infrastructure consists of:
| Component | Repository | Purpose |
|---|---|---|
| Bridge pallets | moonbeam-foundation/polkadot-sdk | On-chain bridge logic (GRANDPA verifier, message lanes) |
| Substrate relay | moonbeam-foundation/parity-bridges-common | Off-chain relayer that syncs headers and messages |
| Zombienet configs | Moonbeam repo (zombienet/) | Local test network definitions |
| Chain specs | Moonbeam repo (zombienet/specs/) | Relay chain genesis specs for local testing |
When upgrading to a new polkadot-sdk stable version, all components must be
updated together to ensure bridge pallet version compatibility.
zombienet/
├── bin/ # Binaries (downloaded by Makefile)
│ ├── polkadot # Relay chain node
│ ├── polkadot-execute-worker
│ ├── polkadot-prepare-worker
│ ├── substrate-relay # Bridge relayer
│ ├── moonbeam -> ../../target/release/moonbeam # Symlink
│ └── zombienet # Zombienet CLI wrapper
├── configs/
│ ├── moonbeam-polkadot.toml # Moonbeam + Polkadot relay network
│ └── moonriver-kusama.toml # Moonriver + Kusama relay network
├── specs/
│ ├── polkadot-local.json # Polkadot relay chain spec
│ ├── kusama-local.json # Kusama relay chain spec
│ └── README.md # Generation instructions
└── integration-tests/bridges/
├── run-test.sh # Main test entry point
├── environments/moonbeam-moonriver/
│ ├── spawn.sh # Spawns both networks
│ ├── start_relayer.sh # Starts bridge relayers
│ └── bridge.sh # Bridge operations (init, relay, transfer)
└── tests/0001-moonbeam-moonriver-asset-transfer/
├── run.sh
├── glmr-reaches-moonriver.zndsl
└── movr-reaches-moonbeam.zndsl
Key version pins in the Makefile:
POLKADOT_VERSION := stable2512-2
BRIDGE_RELAY_VERSION := v1.8.19-moonbeam-stable2512
Since polkadot-sdk stable2512, the polkadot binary no longer ships
polkadot-local or kusama-local as built-in chain specs. They must be
generated from the polkadot-fellows/runtimes repo using its
chain-spec-generator binary.
Important: The zombienet configs must use chain_spec_path pointing to
committed JSON specs. Do NOT use chain = "rococo-local" as a substitute —
using the wrong relay chain type is dangerous for bridge testing.
Find the polkadot-fellows/runtimes commit that matches the SDK version.
Look for commits like "Update crates to SDK 2512-2 via psvm":
cd ../runtimes # or clone polkadot-fellows/runtimes
git log --oneline --grep="SDK 2512"
Build the chain-spec-generator (only relay runtimes needed):
git checkout <matching-commit>
cargo build --release -p chain-spec-generator \
--features polkadot,kusama,fast-runtime --no-default-features
Generate the specs:
./target/release/chain-spec-generator polkadot-local > zombienet/specs/polkadot-local.json
./target/release/chain-spec-generator kusama-local > zombienet/specs/kusama-local.json
Verify the specs look correct:
python3 -c "
import json
for f in ['polkadot-local', 'kusama-local']:
d = json.load(open(f'zombienet/specs/{f}.json'))
print(f'{f}: name={d[\"name\"]}, id={d[\"id\"]}, chainType={d[\"chainType\"]}')
"
Ensure the zombienet configs reference them via chain_spec_path:
# zombienet/configs/moonbeam-polkadot.toml
[relaychain]
chain_spec_path = "zombienet/specs/polkadot-local.json"
# zombienet/configs/moonriver-kusama.toml
[relaychain]
chain_spec_path = "zombienet/specs/kusama-local.json"
The substrate-relay binary lives in moonbeam-foundation/parity-bridges-common.
It must be built against the same polkadot-sdk version as the Moonbeam runtime
to ensure bridge pallet compatibility.
| Branch | Purpose |
|---|---|
master | Tracks upstream paritytech/parity-bridges-common master |
master-with-moonbeam | master + moonbeam/moonriver relay client code (4 commits) |
moonbeam-polkadot-stable2503 | Pinned to polkadot-sdk stable2503 |
moonbeam-polkadot-stable2512 | Pinned to polkadot-sdk stable2512 |
Start from the latest upstream master:
cd ../parity-bridges-common
git remote add upstream https://github.com/paritytech/parity-bridges-common.git
git fetch upstream master
git fetch origin master-with-moonbeam
git checkout -b moonbeam-polkadot-stable<NNNN> upstream/master
Cherry-pick the moonbeam-specific commits from master-with-moonbeam:
# List moonbeam commits (should be ~4 commits)
git log --oneline --reverse origin/master..origin/master-with-moonbeam
# Cherry-pick them all
git cherry-pick <commit1> <commit2> <commit3> <commit4>
These commits add:
chains/chain-moonbeam/, chains/chain-moonriver/, chains/chain-moonbase/relay-clients/client-moonbeam/, client-moonriver/, client-moonbase/substrate-relay/src/bridges/kusama_polkadot/polkadot-to-moonriver, kusama-to-moonbeam, etc..github/workflows/build-binaries.ymlPin polkadot-sdk to the stable branch:
# On Linux / GNU sed:
sed -i 's|git = "https://github.com/paritytech/polkadot-sdk", branch = "master"|git = "https://github.com/moonbeam-foundation/polkadot-sdk", branch = "moonbeam-polkadot-stable<NNNN>"|g' Cargo.toml
# On macOS / BSD sed:
sed -i '' 's|git = "https://github.com/paritytech/polkadot-sdk", branch = "master"|git = "https://github.com/moonbeam-foundation/polkadot-sdk", branch = "moonbeam-polkadot-stable<NNNN>"|g' Cargo.toml
Fix compilation issues. Common ones:
MultiSignature::Eth variant: Added in stable2512. Fix in
chains/chain-moonbeam/src/temporary.rs and chains/chain-moonbase/src/temporary.rs:
sp_runtime::MultiSignature::Eth(sig) => Self(ecdsa::Signature::from(sig.0)),
Resolve Cargo.lock. The ark-vrf crate is yanked from crates.io, so a
fresh cargo generate-lockfile will fail. Workaround: seed from the moonbeam
repo's lockfile:
cp ../moonbeam-stable2512/Cargo.lock Cargo.lock
cargo update --workspace
Build and verify:
cargo build --release -p substrate-relay
./target/release/substrate-relay --version
# Verify moonbeam bridges are registered:
./target/release/substrate-relay relay-headers --help 2>&1 | grep moon
Commit, push, and create a release:
git add -A
git commit -m "update pins to moonbeam-polkadot-stable<NNNN>"
git push origin moonbeam-polkadot-stable<NNNN>
# Trigger CI to build linux-x64 and macos-arm64 binaries
gh workflow run build-binaries.yml \
--ref moonbeam-polkadot-stable<NNNN> \
-R moonbeam-foundation/parity-bridges-common
# Wait for CI, download artifacts, create release
gh run watch <RUN_ID> -R moonbeam-foundation/parity-bridges-common
mkdir -p /tmp/relay-artifacts && cd /tmp/relay-artifacts
gh run download <RUN_ID> -R moonbeam-foundation/parity-bridges-common
mv substrate-relay-linux-x64/substrate-relay ./substrate-relay-linux-x64
mv substrate-relay-macos-arm64/substrate-relay ./substrate-relay-macos-arm64
gh release create v1.8.19-moonbeam-stable<NNNN> \
--repo moonbeam-foundation/parity-bridges-common \
--target moonbeam-polkadot-stable<NNNN> \
--title "v1.8.19-moonbeam-stable<NNNN>" \
--notes "substrate-relay for polkadot-sdk stable<NNNN>" \
substrate-relay-linux-x64 \
substrate-relay-macos-arm64
Update the moonbeam Makefile:
BRIDGE_RELAY_VERSION := v1.8.19-moonbeam-stable<NNNN>
polkadot, moonbeam, substrate-relay binaries (run make all to download/build)polkadot-js-api CLI: npm install -g @polkadot/api-cli@beta# Ensure PATH includes zombienet/bin
export PATH="${PWD}/zombienet/bin:$PATH"
# Download/build all binaries + run the test
make run-bridge-integration-tests
export PATH="${PWD}/zombienet/bin:$PATH"
export FRAMEWORK_REPO_PATH="$HOME/local_bridge_testing/downloads/polkadot-sdk"
./zombienet/integration-tests/bridges/run-test.sh 0001-moonbeam-moonriver-asset-transfer
init-bridge kusama-to-moonbeam, polkadot-to-moonriver)The finality relayer and the headers-and-messages relayer both submit GRANDPA
header proofs and can conflict. Check start_relayer.sh — it runs both
run-finality-relay and relay-headers-and-messages concurrently. The
headers-and-messages relayer already includes finality sync, so the standalone
finality relayer may be redundant.
If the relayer gets stuck or transactions fail silently, verify the bridge pallet versions match between the runtime and the relayer:
# Runtime versions (from Cargo.lock)
grep -A2 'name = "bp-header-chain"' Cargo.lock | head -3
# Relayer versions
grep -A2 'name = "bp-header-chain"' ../parity-bridges-common/Cargo.lock | head -3
They must use the same polkadot-sdk branch.
Logs are written to /tmp/bridge-integration-tests/run-*/logs/:
# Finality relayer
tail -f /tmp/bridge-integration-tests/run-*/logs/relayer_finality.log
# Parachains + messages relayer
tail -f /tmp/bridge-integration-tests/run-*/logs/relayer_parachains.log
The polkadot binary no longer has polkadot-local built-in. Regenerate chain
specs as described in Regenerating Relay Chain Specs.
pkill -9 -f 'bridge-integration-tests'
The bridge integration tests run in .github/workflows/build.yml under the
bridge-integration-tests job. It:
moonbeam binary from the build jobpolkadot-js-apimake run-bridge-integration-testsThe Makefile handles downloading all required binaries (polkadot,
substrate-relay, zombienet) from their respective GitHub releases using the
version pins at the top of the file.
When updating versions, ensure:
POLKADOT_VERSION matches the polkadot-sdk release tagBRIDGE_RELAY_VERSION points to a published release on
moonbeam-foundation/parity-bridges-common with the correct binary assetssubstrate-relay-{linux-x64,macos-arm64}npx claudepluginhub moonsong-labs/knowledge-work-plugins --plugin moonbeam-engineeringConfigures and spawns ephemeral Polkadot/Substrate networks for integration testing using zombienet-sdk TOML configs or Rust NetworkConfigBuilder; debugs spawn timeouts, port errors, and block production issues on native, Docker, or Kubernetes providers.
Builds USDC bridging with Circle Bridge Kit SDK and CCTP across EVM chains, Solana, and Circle Wallets. Sets up adapters (Viem, Ethers, Solana Kit), handles events, fees, speeds, and forwarding service.
Monitors cross-chain bridge TVL, volume, fees, and transaction status across protocols like Stargate, Wormhole. Compares routes and tracks transfers.