From 4aba16c88ff88158f2e5d28cb43e3e094775137a Mon Sep 17 00:00:00 2001 From: rplusq Date: Fri, 10 Jul 2026 19:11:44 +0100 Subject: [PATCH 1/3] fix(deploy): require explicit sender on all production chains BaseScript's production guard only covered mainnet + optimism, so a Base/Arbitrum (or any other non-testnet) broadcast could silently fall back to the public TEST_MNEMONIC key. Invert to an allow-list of local/testnet chains; every other chain must pass ETH_FROM. Co-Authored-By: Claude Opus 4.8 (1M context) --- evm/script/Base.s.sol | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/evm/script/Base.s.sol b/evm/script/Base.s.sol index f7ac882..e497906 100644 --- a/evm/script/Base.s.sol +++ b/evm/script/Base.s.sol @@ -66,12 +66,19 @@ abstract contract BaseScript is Script, StdCheats { require(vm.envUint("CHAIN_ID") == block.chainid, "Chain ID mismatch"); // Get the specified sender address specifiedSender = vm.envOr({ name: "ETH_FROM", defaultValue: address(0) }); - bool isMainnetOrOptimism = - block.chainid == getChain("mainnet").chainId || block.chainid == getChain("optimism").chainId; - // We exit early if the chain is mainnet or optimism and no sender is specified - if (isMainnetOrOptimism && specifiedSender == address(0)) { - revert("You must specify a sender for a production deployment"); + // Allow-list the local + testnet chains where deriving the broadcaster from a mnemonic is acceptable. + // EVERY other chain (mainnet, optimism, arbitrum, base, …) is a production deployment and MUST pass an + // explicit ETH_FROM — otherwise the deployment would silently fall back to the public TEST_MNEMONIC key. + bool isLocalOrTestnet = block.chainid == 31_337 // anvil + || block.chainid == 1337 // hardhat / localhost + || block.chainid == 11_155_111 // sepolia + || block.chainid == 11_155_420 // optimism sepolia + || block.chainid == 84_532 // base sepolia + || block.chainid == 421_614; // arbitrum sepolia + + if (!isLocalOrTestnet && specifiedSender == address(0)) { + revert("You must specify a sender (ETH_FROM) for a production deployment"); } // Select the broadcaster, either the specified sender or the first derived address from the mnemonic From c7dd523be3d37ec804820a67b03f39c44ee3e2d3 Mon Sep 17 00:00:00 2001 From: rplusq Date: Tue, 14 Jul 2026 20:42:53 +0100 Subject: [PATCH 2/3] fix(deploy): guard prod chains explicitly (mainnet/op/arbitrum/base) Add arbitrum (42161) and base (8453) to the existing production-chain sender guard rather than inverting to a testnet allow-list. Keeps the diff minimal and matches the existing style; covers the concrete set of production chains we deploy to. Co-Authored-By: Claude Opus 4.8 (1M context) --- evm/script/Base.s.sol | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/evm/script/Base.s.sol b/evm/script/Base.s.sol index e497906..25a178f 100644 --- a/evm/script/Base.s.sol +++ b/evm/script/Base.s.sol @@ -67,17 +67,14 @@ abstract contract BaseScript is Script, StdCheats { // Get the specified sender address specifiedSender = vm.envOr({ name: "ETH_FROM", defaultValue: address(0) }); - // Allow-list the local + testnet chains where deriving the broadcaster from a mnemonic is acceptable. - // EVERY other chain (mainnet, optimism, arbitrum, base, …) is a production deployment and MUST pass an - // explicit ETH_FROM — otherwise the deployment would silently fall back to the public TEST_MNEMONIC key. - bool isLocalOrTestnet = block.chainid == 31_337 // anvil - || block.chainid == 1337 // hardhat / localhost - || block.chainid == 11_155_111 // sepolia - || block.chainid == 11_155_420 // optimism sepolia - || block.chainid == 84_532 // base sepolia - || block.chainid == 421_614; // arbitrum sepolia - - if (!isLocalOrTestnet && specifiedSender == address(0)) { + // Production chains where deriving the broadcaster from a mnemonic is unacceptable: an unset ETH_FROM + // would silently fall back to the public TEST_MNEMONIC key. Each such chain MUST pass an explicit ETH_FROM. + bool isProduction = block.chainid == getChain("mainnet").chainId // ethereum + || block.chainid == getChain("optimism").chainId // optimism + || block.chainid == 42_161 // arbitrum one + || block.chainid == 8453; // base + + if (isProduction && specifiedSender == address(0)) { revert("You must specify a sender (ETH_FROM) for a production deployment"); } From 50796c50699f768437950c17ddfc33e1d7e0d988 Mon Sep 17 00:00:00 2001 From: rplusq Date: Tue, 14 Jul 2026 20:44:59 +0100 Subject: [PATCH 3/3] fix(deploy): use getChain aliases for arbitrum/base guard Consistent with the mainnet/optimism entries. Note forge-std's alias is arbitrum_one (not arbitrum). Co-Authored-By: Claude Opus 4.8 (1M context) --- evm/script/Base.s.sol | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/evm/script/Base.s.sol b/evm/script/Base.s.sol index 25a178f..a4cf666 100644 --- a/evm/script/Base.s.sol +++ b/evm/script/Base.s.sol @@ -71,8 +71,8 @@ abstract contract BaseScript is Script, StdCheats { // would silently fall back to the public TEST_MNEMONIC key. Each such chain MUST pass an explicit ETH_FROM. bool isProduction = block.chainid == getChain("mainnet").chainId // ethereum || block.chainid == getChain("optimism").chainId // optimism - || block.chainid == 42_161 // arbitrum one - || block.chainid == 8453; // base + || block.chainid == getChain("arbitrum_one").chainId // arbitrum + || block.chainid == getChain("base").chainId; // base if (isProduction && specifiedSender == address(0)) { revert("You must specify a sender (ETH_FROM) for a production deployment");