Forge script: replay all 45 §5.1 EVM deploy steps with BSC address parity (dry run PASS/FAIL) #121

Closed
opened 2026-04-28 14:16:54 +00:00 by PlasticDigits · 5 comments
PlasticDigits commented 2026-04-28 14:16:54 +00:00 (Migrated from gitlab.com)

Summary

Implement a Foundry forge script that replays the canonical 45-transaction BSC deployer sequence (see docs/deployment-megaeth.md §5.1) on any new EVM chain so contract addresses match BSC / opBNB parity. The deliverable must include a dry-run path that prints predicted addresses and ends with an unambiguous PASS or FAIL versus the frozen BSC golden set.

Related: docs/deployment-megaeth.md §5.1–5.3, docs/export-transaction-list-1777384911253.csv, docs/reference/bsc-deployer-transaction-export-sample.csv, packages/contracts-evm/broadcast/Deploy.s.sol/56/run-latest.json, existing packages/contracts-evm/script/Deploy.s.sol.


Problem statement

Operators today must manually: recover per-tx nonces from BSC, align the target chain deployer nonce to N0, run multiple forge script / cast steps in order, and verify with cast compute-address / receipts. This is error-prone (wrong nonce, extra txs, wrong order) and cannot be rolled back without a new deployer. A single orchestrated script with simulation + PASS/FAIL reduces operational risk.


In-scope behavior

  • Exactly 45 outer transactions in the same order as §5.1 (deployer nonces 0–44 for a full run from step 1).
  • Same callees, CREATE ordering, and internal deployment patterns as BSC so final proxy and implementation addresses match the historical BSC table (not “match whatever BSC’s current pending nonce would deploy next”).
  • Configurable target chain: RPC, THIS_CHAIN_ID / CHAIN_IDENTIFIER, wrapped native / chain-specific env already used by Deploy.s.sol and deployment docs.
  • Dry run: no state change on chain (or explicit simulate / vm.startBroadcast skipped); output must include expected addresses after each material step and a final gate: all compared addresses equal BSC references → PASS; any mismatch → FAIL with step index, nonce, expected vs actual (or predicted vs golden).

Recommendations — script design and repo layout

Architecture

  • Single orchestrator script (e.g. script/EvmParityReplay.s.sol) with a small public API: runDryCheck(), runBroadcast() (or one run() + vm.envBool("DRY_RUN")) so CI and operators share one code path.
  • Frozen golden data in-repo (pick one, document the choice):
    • Option A: Solidity library BscParityGolden with address constants + step metadata (nonce, optional tx hash for docs only).
    • Option B: script/bsc-parity-replay.json consumed via vm.readFile + stdJson in Foundry (easier for 45 rows; keep schema versioned).
  • Step boundaries: map each of the 45 rows to an explicit function or enum-driven executeStep(uint8 i) so ordering is auditable in code review (not one 2000-line function).
  • Reuse existing deploy logic where possible: delegate to factories / init patterns from Deploy.s.sol (or shared internal libraries) instead of duplicating bytecode deployment details. If Deploy.s.sol cannot be called safely in sequence, extract shared internal deploy helpers into script/libraries/ or src/ as appropriate with minimal surface change.
  • CREATE2 / singleton / deploy helper (step 19, 39, 40): isolate in dedicated internal modules with comments pointing to §5.1 row numbers. Step 40 must reproduce internal creates (no top-level contractAddress); simulation must still assert child contract addresses (logs, eth_getTransactionReceipt parity in script via vm.recordLogs / known interface, or static prediction from salt + init code if deterministic).
  • Nonce model: script assumes vm.getNonce(deployer) == N0 at entry; first line of broadcast mode require this; dry run may use vm.setNonce in test only—document that production dry run uses fork + prank or pure prediction without mutating mainnet state.
  • Shell entrypoint (optional): scripts/evm/parity-replay.sh that exports env, runs forge script ... --sig runDryCheck vs runBroadcast, forwards --rpc-url, --slow, --legacy, etc.

Dry run implementation (pick a coherent approach)

  • Fork-based simulation: forge script against an empty or fresh account on a local fork with vm.setNonce(deployer, N0) only in test/fork context, then run all steps and scrape created addresses from receipts (if Foundry exposes them) or from return values / storage.
  • Alternative: off-chain prediction table: for pure EOA CREATE, cast compute-address-equivalent in Solidity (CREATE address from nonce); for CREATE2, vm.computeCreate2Address; for step 40, document required assertions against logs or a one-time golden map for internal addresses from BSC receipts.
  • Exit code: document that operators should rely on process exit non-zero on FAIL (may require a thin Solidity revert on mismatch or a bash wrapper grepping PASS/FAIL).

Safety

  • Broadcast mode: explicit --slow / confirmation flags per org policy; never silently skip steps.
  • Pre-flight require for WETH / chain id / THIS_CHAIN_ID vs block.chainid where applicable (mirror Deploy.s.sol).

Implementation checklist

  • Encode full 45-step order matching §5.1 (same nonce progression 0–44 for full replay).
  • Pin golden addresses (at minimum every EOA CREATE result from the table; plus any critical internal addresses from step 40 if required for downstream phases—list them explicitly in MR).
  • Implement dry run path: print tabular Step | Nonce | Kind | Expected | Predicted | Match (or equivalent).
  • Implement final summary line: PARITY_CHECK: PASS or PARITY_CHECK: FAIL (machine-readable).
  • Implement broadcast path calling the same step functions (no duplicated ordering).
  • Wire env vars consistent with Deploy.s.sol / deployment-guide.md (DEPLOYER, ADMIN_ADDRESS, THIS_CHAIN_ID, CHAIN_IDENTIFIER, WETH_ADDRESS, etc.).
  • Add Foundry test (e.g. test/BscParityReplayDryRun.t.sol) that runs dry check on a fork and asserts PASS (CI guard against regressions).
  • Document N0 semantics: full replay N0=0; partial replay e.g. N0=39 for step 40 only—script must document which entrypoints are supported.

Verification and QA checklist

  • Dry run on fork of BSC with deployer pranked and nonce aligned reproduces PASS (sanity: replays on BSC fork should match golden without broadcasting).
  • Dry run on fork of a second EVM chain (e.g. MegaETH or Sepolia) with vm.setNonce / fresh account: PASS if logic is chain-agnostic aside from env.
  • Manual spot-check: for steps 1, 19, 22, 23, 31, 34, 40, 45, compare script output to cast tx / cast receipt on BSC for the tx hashes in §5.1.
  • Confirm no extra transactions between steps in broadcast mode (nonce increases by 1 per outer tx for EOA sends; document exceptions if any batched).
  • Gas / RPC: document limits for chains with large block gas (e.g. MegaETH).

Documentation checklist (deliver with MR; may be short additions to existing runbooks)

  • How to run dry check (exact forge script command, required env, example output showing PASS).
  • How to run live broadcast after PASS.
  • Failure playbook: FAIL means do not broadcast; how to read which step failed; reminder that nonce cannot decrease.
  • Cross-link §5.1 table and CSV paths in MR description (not necessarily long new docs).

Acceptance criteria (must all be true to close)

  1. One documented forge script entrypoint replays the 45 transactions in §5.1 order on a configurable EVM RPC (subject to nonce N0 alignment documented in runbook/issue).
  2. Dry run prints expected contract addresses (per step or aggregated) derived from the same logic as broadcast.
  3. Dry run compares predictions to the BSC golden address set and prints PASS only if all checked addresses match; otherwise FAIL with enough context to fix (step + addresses).
  4. Broadcast path executes the same ordered operations as dry run (shared implementation), producing the same addresses as BSC when deployer nonce and chain-specific constants are correct.
  5. CI or test prevents accidental reordering / golden drift (minimum: one fork test that expects PASS).
  6. Code review can verify 1:1 mapping between §5.1 rows and code (comments, enum, or data file with 45 records).

Canonical references (BSC deployer sequence)

  • Deployer (historical table): 0xD699EbC6930F593f0725D2a7dC58ACC65b41a08e (from §5.1 verification snippet).
  • 45 rows: nonces 0–44, tx hashes and created addresses in docs/deployment-megaeth.md §5.1 table.
  • Key high-risk steps for parity: outer CREATE rows; Create2 singleton (step 19); Bridge register (23); Create2 factory (39); deploy helper (40) with internal creates; final proxies ending at step 45 → 0x12fedd29e71f66157e985aa1aaae434253e39a22.

Out of scope (unless explicitly expanded)

  • Terra / Solana registration scripts.
  • Changing production BSC contracts.
  • Automatic nonce burning on target chain (may remain manual cast send loop; script documents prerequisite cast nonce == N0).
## Summary Implement a **Foundry `forge script`** that replays the **canonical 45-transaction BSC deployer sequence** (see `docs/deployment-megaeth.md` §5.1) on **any new EVM chain** so contract addresses match **BSC / opBNB parity**. The deliverable must include a **dry-run path** that prints predicted addresses and ends with an unambiguous **PASS** or **FAIL** versus the frozen BSC golden set. **Related:** `docs/deployment-megaeth.md` §5.1–5.3, `docs/export-transaction-list-1777384911253.csv`, `docs/reference/bsc-deployer-transaction-export-sample.csv`, `packages/contracts-evm/broadcast/Deploy.s.sol/56/run-latest.json`, existing `packages/contracts-evm/script/Deploy.s.sol`. --- ## Problem statement Operators today must manually: recover per-tx nonces from BSC, align the target chain deployer nonce to `N0`, run multiple `forge script` / `cast` steps in order, and verify with `cast compute-address` / receipts. This is error-prone (wrong nonce, extra txs, wrong order) and **cannot be rolled back** without a new deployer. A single orchestrated script with **simulation + PASS/FAIL** reduces operational risk. --- ## In-scope behavior - **Exactly 45** outer transactions in the **same order** as §5.1 (deployer nonces **0–44** for a full run from step 1). - Same **callees**, **CREATE ordering**, and **internal deployment patterns** as BSC so **final proxy and implementation addresses** match the historical BSC table (not “match whatever BSC’s *current* pending nonce would deploy next”). - **Configurable target chain:** RPC, `THIS_CHAIN_ID` / `CHAIN_IDENTIFIER`, wrapped native / chain-specific env already used by `Deploy.s.sol` and deployment docs. - **Dry run:** no state change on chain (or explicit `simulate` / `vm.startBroadcast` skipped); output must include **expected addresses** after each material step and a **final gate**: all compared addresses equal BSC references → **PASS**; any mismatch → **FAIL** with step index, nonce, expected vs actual (or predicted vs golden). --- ## Recommendations — script design and repo layout ### Architecture - [ ] **Single orchestrator script** (e.g. `script/EvmParityReplay.s.sol`) with a small public API: `runDryCheck()`, `runBroadcast()` (or one `run()` + `vm.envBool("DRY_RUN")`) so CI and operators share one code path. - [ ] **Frozen golden data** in-repo (pick one, document the choice): - **Option A:** Solidity `library BscParityGolden` with `address` constants + step metadata (nonce, optional tx hash for docs only). - **Option B:** `script/bsc-parity-replay.json` consumed via `vm.readFile` + `stdJson` in Foundry (easier for 45 rows; keep schema versioned). - [ ] **Step boundaries:** map each of the **45** rows to an explicit function or enum-driven `executeStep(uint8 i)` so ordering is auditable in code review (not one 2000-line function). - [ ] **Reuse existing deploy logic** where possible: delegate to factories / init patterns from `Deploy.s.sol` (or shared internal libraries) instead of duplicating bytecode deployment details. If `Deploy.s.sol` cannot be called safely in sequence, extract **shared internal deploy helpers** into `script/libraries/` or `src/` as appropriate with minimal surface change. - [ ] **CREATE2 / singleton / deploy helper (step 19, 39, 40):** isolate in dedicated internal modules with comments pointing to §5.1 row numbers. Step 40 must reproduce **internal creates** (no top-level `contractAddress`); simulation must still assert **child contract addresses** (logs, `eth_getTransactionReceipt` parity in script via `vm.recordLogs` / known interface, or static prediction from salt + init code if deterministic). - [ ] **Nonce model:** script assumes `vm.getNonce(deployer) == N0` at entry; **first line** of broadcast mode `require` this; dry run may use `vm.setNonce` in test only—document that production dry run uses **fork + prank** or **pure prediction** without mutating mainnet state. - [ ] **Shell entrypoint (optional):** `scripts/evm/parity-replay.sh` that exports env, runs `forge script ... --sig runDryCheck` vs `runBroadcast`, forwards `--rpc-url`, `--slow`, `--legacy`, etc. ### Dry run implementation (pick a coherent approach) - [ ] **Fork-based simulation:** `forge script` against an **empty or fresh** account on a **local fork** with `vm.setNonce(deployer, N0)` only in **test/fork** context, then run all steps and scrape created addresses from receipts (if Foundry exposes them) or from return values / storage. - [ ] **Alternative:** off-chain prediction table: for pure EOA `CREATE`, `cast compute-address`-equivalent in Solidity (`CREATE` address from nonce); for CREATE2, `vm.computeCreate2Address`; for step 40, document required **assertions** against logs or a one-time golden map for internal addresses from BSC receipts. - [ ] Exit code: document that operators should rely on **process exit non-zero on FAIL** (may require a thin Solidity `revert` on mismatch or a bash wrapper grepping `PASS`/`FAIL`). ### Safety - [ ] Broadcast mode: explicit `--slow` / confirmation flags per org policy; never silently skip steps. - [ ] Pre-flight `require` for `WETH` / chain id / `THIS_CHAIN_ID` vs `block.chainid` where applicable (mirror `Deploy.s.sol`). --- ## Implementation checklist - [ ] Encode full **45-step order** matching §5.1 (same nonce progression 0–44 for full replay). - [ ] Pin **golden addresses** (at minimum every **EOA CREATE** result from the table; plus any **critical internal** addresses from step 40 if required for downstream phases—list them explicitly in MR). - [ ] Implement **dry run** path: print tabular **Step | Nonce | Kind | Expected | Predicted | Match** (or equivalent). - [ ] Implement **final summary line:** `PARITY_CHECK: PASS` or `PARITY_CHECK: FAIL` (machine-readable). - [ ] Implement **broadcast** path calling the same step functions (no duplicated ordering). - [ ] Wire env vars consistent with `Deploy.s.sol` / `deployment-guide.md` (`DEPLOYER`, `ADMIN_ADDRESS`, `THIS_CHAIN_ID`, `CHAIN_IDENTIFIER`, `WETH_ADDRESS`, etc.). - [ ] Add **Foundry test** (e.g. `test/BscParityReplayDryRun.t.sol`) that runs dry check on a fork and asserts PASS (CI guard against regressions). - [ ] Document **N0** semantics: full replay `N0=0`; partial replay e.g. `N0=39` for step 40 only—script must document which entrypoints are supported. --- ## Verification and QA checklist - [ ] Dry run on **fork** of BSC with deployer pranked and nonce aligned reproduces **PASS** (sanity: replays on BSC fork should match golden without broadcasting). - [ ] Dry run on **fork** of a second EVM chain (e.g. MegaETH or Sepolia) with `vm.setNonce` / fresh account: **PASS** if logic is chain-agnostic aside from env. - [ ] Manual spot-check: for steps **1, 19, 22, 23, 31, 34, 40, 45**, compare script output to `cast tx` / `cast receipt` on BSC for the tx hashes in §5.1. - [ ] Confirm **no extra transactions** between steps in broadcast mode (nonce increases by 1 per outer tx for EOA sends; document exceptions if any batched). - [ ] Gas / RPC: document limits for chains with large block gas (e.g. MegaETH). --- ## Documentation checklist (deliver with MR; may be short additions to existing runbooks) - [ ] How to run dry check (exact `forge script` command, required env, example output showing **PASS**). - [ ] How to run live broadcast after PASS. - [ ] Failure playbook: **FAIL** means do not broadcast; how to read which step failed; reminder that **nonce cannot decrease**. - [ ] Cross-link §5.1 table and CSV paths in MR description (not necessarily long new docs). --- ## Acceptance criteria (must all be true to close) 1. One documented **forge script entrypoint** replays the **45** transactions in **§5.1 order** on a configurable EVM RPC (subject to nonce `N0` alignment documented in runbook/issue). 2. **Dry run** prints **expected contract addresses** (per step or aggregated) derived from the same logic as broadcast. 3. Dry run compares predictions to the **BSC golden** address set and prints **PASS** only if **all** checked addresses match; otherwise **FAIL** with enough context to fix (step + addresses). 4. Broadcast path executes the **same ordered operations** as dry run (shared implementation), producing the **same addresses** as BSC when deployer nonce and chain-specific constants are correct. 5. **CI or test** prevents accidental reordering / golden drift (minimum: one fork test that expects PASS). 6. Code review can verify **1:1 mapping** between §5.1 rows and code (comments, enum, or data file with 45 records). --- ## Canonical references (BSC deployer sequence) - **Deployer (historical table):** `0xD699EbC6930F593f0725D2a7dC58ACC65b41a08e` (from §5.1 verification snippet). - **45 rows:** nonces **0–44**, tx hashes and created addresses in `docs/deployment-megaeth.md` §5.1 table. - **Key high-risk steps for parity:** outer **CREATE** rows; **Create2** singleton (step 19); **Bridge register** (23); **Create2 factory** (39); **deploy helper** (40) with internal creates; final proxies ending at step **45** → `0x12fedd29e71f66157e985aa1aaae434253e39a22`. --- ## Out of scope (unless explicitly expanded) - Terra / Solana registration scripts. - Changing production BSC contracts. - Automatic nonce burning on target chain (may remain manual `cast send` loop; script documents prerequisite `cast nonce == N0`).
PlasticDigits commented 2026-04-28 14:20:21 +00:00 (Migrated from gitlab.com)

changed the description

changed the description
PlasticDigits commented 2026-04-29 03:59:04 +00:00 (Migrated from gitlab.com)

mentioned in commit a9a99a38b8

mentioned in commit a9a99a38b8af7c6adf2a093cd9def6e719331e4c
PlasticDigits commented 2026-04-29 03:59:26 +00:00 (Migrated from gitlab.com)

GL-121 implemented (merged to main)

Summary

  • Golden file: packages/contracts-evm/script/bsc-parity-golden.json — 45 steps, EOA CREATE addresses from BSC receipts + tx hashes (source: docs/export-transaction-list-1777384911253.csv).
  • Dry run: EvmParityReplay.runDryCheck — prints per-step expected vs vm.computeCreateAddress; ends with PARITY_CHECK: PASS or reverts PARITY_CHECK: FAIL.
  • Broadcast (segmented): runBroadcastHead (nonces 0–17), runBroadcastFaucet19 (nonce 19), runBroadcastTail (from nonce 20) — reuses Deploy.deployAll + guard/Create3/factory pattern; outer step 18 remains manual Nick CREATE2 (documented in runbook).
  • CI: BscParityReplayDryRun.t.sol.
  • Docs: docs/deployment-megaeth.md §5.x; docs/deployment-guide.md §4.2a; skills/agent-evm-bsc-parity-replay.md; scripts/evm/parity-replay.sh.

Invariants (documented in golden JSON)

  • INV-PAR1: Each golden eoaCreatedContract must match vm.computeCreateAddress(historicalDeployer, nonce).
  • INV-PAR2: Nonce monotonicity — use ENTRY_NONCE / TAIL_ENTRY_NONCE for partial replay.
  • INV-PAR3: CREATE3-internal children not asserted in dry check (BSC factory vs FactoryTokenCl8yBridgedScript salt); verify on fork post-broadcast.

Checklist for @brouie

  1. cd packages/contracts-evm && forge test --match-contract BscParityReplayDryRun -vv → PASS.
  2. DEPLOYER_ADDRESS=0xD699EbC6930F593f0725D2a7dC58ACC65b41a08e forge script script/EvmParityReplay.s.sol:EvmParityReplay --sig runDryCheck -vvv → ends with PARITY_CHECK: PASS.
  3. Spot-check steps 1, 19, 22, 23, 31, 34, 40, 45 (bsc-parity-golden.json txHash vs BscScan).
  4. Read docs/deployment-megaeth.md §5.3 — confirm manual step 18 process is acceptable for your ops flow.
  5. (Optional fork) After a simulated runBroadcastTail, confirm guard/factory wiring; dry check intentionally skips CREATE3 factory equality to README row.

Issue left open per instructions.

## GL-121 implemented (merged to `main`) ### Summary - **Golden file:** `packages/contracts-evm/script/bsc-parity-golden.json` — 45 steps, EOA `CREATE` addresses from BSC receipts + tx hashes (source: `docs/export-transaction-list-1777384911253.csv`). - **Dry run:** `EvmParityReplay.runDryCheck` — prints per-step expected vs `vm.computeCreateAddress`; ends with `PARITY_CHECK: PASS` or reverts `PARITY_CHECK: FAIL`. - **Broadcast (segmented):** `runBroadcastHead` (nonces 0–17), `runBroadcastFaucet19` (nonce 19), `runBroadcastTail` (from nonce 20) — reuses `Deploy.deployAll` + guard/Create3/factory pattern; **outer step 18** remains manual Nick CREATE2 (documented in runbook). - **CI:** `BscParityReplayDryRun.t.sol`. - **Docs:** `docs/deployment-megaeth.md` §5.x; `docs/deployment-guide.md` §4.2a; `skills/agent-evm-bsc-parity-replay.md`; `scripts/evm/parity-replay.sh`. ### Invariants (documented in golden JSON) - **INV-PAR1:** Each golden `eoaCreatedContract` must match `vm.computeCreateAddress(historicalDeployer, nonce)`. - **INV-PAR2:** Nonce monotonicity — use `ENTRY_NONCE` / `TAIL_ENTRY_NONCE` for partial replay. - **INV-PAR3:** CREATE3-internal children not asserted in dry check (BSC factory vs `FactoryTokenCl8yBridgedScript` salt); verify on fork post-broadcast. ### Checklist for @brouie 1. `cd packages/contracts-evm && forge test --match-contract BscParityReplayDryRun -vv` → PASS. 2. `DEPLOYER_ADDRESS=0xD699EbC6930F593f0725D2a7dC58ACC65b41a08e forge script script/EvmParityReplay.s.sol:EvmParityReplay --sig runDryCheck -vvv` → ends with `PARITY_CHECK: PASS`. 3. Spot-check steps **1, 19, 22, 23, 31, 34, 40, 45** (`bsc-parity-golden.json` `txHash` vs BscScan). 4. Read `docs/deployment-megaeth.md` §5.3 — confirm manual step 18 process is acceptable for your ops flow. 5. (Optional fork) After a simulated `runBroadcastTail`, confirm guard/factory wiring; dry check intentionally skips CREATE3 factory equality to README row. Issue left **open** per instructions.
PlasticDigits commented 2026-04-29 06:43:03 +00:00 (Migrated from gitlab.com)

mentioned in issue #122

mentioned in issue #122
PlasticDigits commented 2026-04-29 06:43:04 +00:00 (Migrated from gitlab.com)

marked this issue as related to #122

marked this issue as related to #122
PlasticDigits (Migrated from gitlab.com) closed this issue 2026-05-01 03:10:52 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
code/cl8y-bridge-monorepo#121
No description provided.