Skip to content

Migrate local forks to Anvil - #2999

Open
clement-ux wants to merge 1 commit into
chore/introduce-standalone-ops-clifrom
chore/migrate-local-node-to-anvil
Open

Migrate local forks to Anvil#2999
clement-ux wants to merge 1 commit into
chore/introduce-standalone-ops-clifrom
chore/migrate-local-node-to-anvil

Conversation

@clement-ux

@clement-ux clement-ux commented Sep 1, 2026

Copy link
Copy Markdown
Collaborator

Plain-English summary

After #2998, operational commands no longer need Hardhat. One active Hardhat job remains: starting a local blockchain or a fork of a live network for development and simulation.

This PR replaces that node with Anvil. The previous Hardhat node is kept temporarily as a comparison tool so we can generate the same Safe transaction with both implementations and compare the important fields before deleting Hardhat.

Before and after

Before: scripts/tests → Hardhat node → forked network
After:  scripts/tests → Anvil       → forked network

For Safe batches, both paths are run and compared on:

  • chainId
  • to
  • value
  • data

Gas estimates and metadata are intentionally ignored because they are node-specific and do not change the transaction's meaning.

What changes in this PR

  • make Anvil the canonical local/forked node for mainnet, Arbitrum, Base, Sonic, Plume, Hoodi, and HyperEVM;
  • connect Brownie to external Anvil networks;
  • migrate impersonation, balance, and code-mutation calls to anvil_* RPC methods;
  • preserve the previous node wrapper as hardhat-node.sh for temporary A/B comparison;
  • add the Safe batch comparator;
  • add Anvil, Brownie bridge, and Safe payload comparison tests.

What does not change yet

Stack and merge order

Base: #2998 (chore/introduce-standalone-ops-cli)

  1. Remove obsolete historical scripts and artifacts #2997 — remove obsolete historical scripts and artifacts
  2. Introduce standalone ops CLI #2998 — introduce standalone ops CLI
  3. Migrate local forks to Anvil #2999 — migrate local forks to Anvil (this PR)
  4. Remove the Hardhat runtime #3000 — remove the Hardhat runtime

Merge in cascade: #3000#2999#2998#2997master.

What to review

Please focus on node and simulation parity:

  • does each supported network use the correct chain ID and fork configuration?
  • are impersonation, balance changes, and code changes translated to the correct Anvil RPC methods?
  • does Brownie connect to the external node correctly?
  • does the Safe comparator check all fields that determine transaction meaning?

Validation

  • pnpm test:tasks — 38 passing
  • pnpm test:anvil — 3 passing
  • pnpm test:brownie — 4 passing
  • pnpm test:scripts — 66 passing
  • pnpm test:layouts — 11 passing
  • pnpm typecheck
  • pnpm lint:js, pnpm lint:ts, pnpm prettier:check
  • full GitHub CI passes

@clement-ux
clement-ux force-pushed the chore/migrate-local-node-to-anvil branch from 1f72c9a to 056a4ed Compare September 2, 2026 07:57

@sparrowDom sparrowDom 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.

Some comments inline



def main():
if web3.eth.chain_id != 1337:

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.

This guard means the smoke cannot run against either node this PR ships: anvil.sh passes the real chain id via --chain-id for every network, including ANVIL_NO_FORK=true, and node:legacy (./hardhat-node.sh fork) runs with FORK=true, so getHardhatNetworkChainId() returns 1 there too. The only 1337 node left is a bare ./hardhat-node.sh without the fork argument, which no script invokes, so the "generate the same Safe transaction with both implementations and compare" flow from the PR body raises this RuntimeError as shipped. The payload built below is also a synthetic one from a raw eth_sendTransaction; it never goes through to_gnosis_json (world_abstract.py:111), which is the batch-generation path the comparator exists to validate.

And once #3000 deletes hardhat-node.sh and node:legacy, this script, compare_safe_batches.py, tests/test_safe_payload_compare.py and the README paragraph ("compare a batch generated by the legacy node…", line 94) are left without a legacy side to compare against.

Two ways out: (a) make it real here: drop the 1337 guard (record the chain id, or assert against an EXPECTED_CHAIN_ID env), have the smoke wrap a real TemporaryForkForReallocations run so both engines emit a to_gnosis_json batch, and document how legacy-batch.json / anvil-batch.json are produced (today the batch only goes to stdout); or (b) treat the comparison as a one-off already done, and delete the three files plus the README paragraph in #3000 together with hardhat-node.sh.

@@ -1 +1 @@
dotenv: .env
# Environment variables are inherited from the shell. A local .env is optional.

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.

Unrelated to Anvil: this stops Brownie auto-loading brownie/.env. The README (line 139) still tells strategists to keep CMC_API_KEY in the environment, so anyone holding it in brownie/.env silently loses it. Revert, or say in the PR body why it goes.


# Hack to make weth.withdraw work
brownie.network.web3.provider.make_request('hardhat_setCode', [MULTICHAIN_STRATEGIST, '0x'])
brownie.network.web3.provider.make_request('anvil_setCode', [MULTICHAIN_STRATEGIST, '0x'])

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.

These eleven hardhat_setCodeanvil_setCode edits (here, 2025_05:539, 2025_06:47,250,411,700, 2025_07:625,655, 2026_01:61,148,170) are the only change to the runlogs. Anvil accepts the hardhat_* aliases unchanged (checked against anvil 1.7.1: hardhat_impersonateAccount and hardhat_setCode both succeed), so the edit is not needed for Anvil, and it makes these runlogs fail if replayed on the legacy node this PR keeps as the A/B oracle. Runlogs are records of what was executed; I would leave them untouched, and the same goes for the three files #3000 edits.

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.

2 participants