From 21b0837c727060125de7f4c2dd91430b6db16df8 Mon Sep 17 00:00:00 2001 From: Josh Hardy Date: Wed, 9 Sep 2026 16:32:01 +0000 Subject: [PATCH 1/3] Add Robinhood Chain (4663) to the supported network set `LibRainDeploy.ROBINHOOD` joins `supportedNetworks()` (eighth entry, after `polygon`), so every consumer's default deploy ships its suite there too and `testZoltuFactoryCodehash` pins the factory on it. The Zoltu factory is live on 4663 with the canonical runtime (verified; the fork test passes against it). Robinhood Chain is an Arbitrum Orbit L2 settling to Ethereum, gas in ETH, EIP-1559 fees. `[rpc_endpoints]` reads `ROBINHOOD_RPC_URL`, which the rainix preflight exports once rainix#371 lands. `[etherscan]` gets an entry because `testSupportedNetworksAreFullyConfigured` requires one per network: Etherscan V2 does not index 4663, so it points at the chain's Blockscout, which speaks the Etherscan API. That explorer has rejected non-browser clients behind a browser challenge, so the entry documents the Sourcify fallback for a failed `--verify`. The key variable `CI_DEPLOY_ROBINHOOD_ETHERSCAN_API_KEY` is exported by the rainix reusables (rainix#371) so config resolution never sees it unset. Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_01HiqQdxokJ4edjAFyAkN9G3 --- foundry.toml | 9 +++++++++ src/lib/LibRainDeploy.sol | 9 ++++++++- test/src/lib/LibRainDeploy.t.sol | 5 +++-- 3 files changed, 20 insertions(+), 3 deletions(-) diff --git a/foundry.toml b/foundry.toml index 3c0583f..562619b 100644 --- a/foundry.toml +++ b/foundry.toml @@ -81,6 +81,7 @@ ethereum = "${ETHEREUM_RPC_URL}" flare = "${FLARE_RPC_URL}" hyperevm = "${HYPEREVM_RPC_URL}" polygon = "${POLYGON_RPC_URL}" +robinhood = "${ROBINHOOD_RPC_URL}" # `rainix-manual-sol-artifacts` passes `--verify` by default and exports exactly # these variable names, so a deploy without this section broadcasts and then @@ -105,3 +106,11 @@ ethereum = { key = "${CI_DEPLOY_ETHEREUM_ETHERSCAN_API_KEY}", chain = 1 } flare = { key = "${CI_DEPLOY_FLARE_ETHERSCAN_API_KEY}" } hyperevm = { key = "${CI_DEPLOY_HYPEREVM_ETHERSCAN_API_KEY}", chain = 999 } polygon = { key = "${CI_DEPLOY_POLYGON_ETHERSCAN_API_KEY}" } +# Robinhood Chain (4663) is not indexed by Etherscan V2. Its Blockscout +# explorer speaks the Etherscan API, so the entry points there; the key is +# whatever `CI_DEPLOY_ROBINHOOD_ETHERSCAN_API_KEY` carries (Blockscout ignores +# it). Blockscout sits behind a browser challenge that has rejected non-browser +# clients, so if `--verify` fails on this network after a broadcast, verify +# afterwards through Sourcify (which supports 4663 and which Blockscout +# imports): `forge verify-contract --verifier sourcify --chain 4663 ...`. +robinhood = { key = "${CI_DEPLOY_ROBINHOOD_ETHERSCAN_API_KEY}", chain = 4663, url = "https://robinhoodchain.blockscout.com/api" } diff --git a/src/lib/LibRainDeploy.sol b/src/lib/LibRainDeploy.sol index 1f0c3e3..82c480f 100644 --- a/src/lib/LibRainDeploy.sol +++ b/src/lib/LibRainDeploy.sol @@ -97,6 +97,12 @@ library LibRainDeploy { /// Config name for Polygon network. string constant POLYGON = "polygon"; + /// Config name for Robinhood Chain (chain id 4663), an Arbitrum Orbit L2 + /// settling to Ethereum. The Zoltu factory is deployed there with the + /// canonical runtime, so deterministic deploys land at the same addresses + /// as on every other supported network. + string constant ROBINHOOD = "robinhood"; + /// Checks whether a block is the first block where a contract with the /// expected code hash exists. True when the target has the expected code /// hash at `blockNumber` and does NOT have it at `blockNumber - 1`. At @@ -284,7 +290,7 @@ library LibRainDeploy { /// Returns the list of networks currently supported by Rain deployments. /// @return The list of supported network names. function supportedNetworks() internal pure returns (string[] memory) { - string[] memory networks = new string[](7); + string[] memory networks = new string[](8); networks[0] = ARBITRUM_ONE; networks[1] = BASE; networks[2] = BASE_SEPOLIA; @@ -292,6 +298,7 @@ library LibRainDeploy { networks[4] = FLARE; networks[5] = HYPEREVM; networks[6] = POLYGON; + networks[7] = ROBINHOOD; return networks; } diff --git a/test/src/lib/LibRainDeploy.t.sol b/test/src/lib/LibRainDeploy.t.sol index 9c5be2c..d33a842 100644 --- a/test/src/lib/LibRainDeploy.t.sol +++ b/test/src/lib/LibRainDeploy.t.sol @@ -193,11 +193,11 @@ contract LibRainDeployTest is Test { ); } - /// `supportedNetworks` MUST return exactly 7 networks in the expected + /// `supportedNetworks` MUST return exactly 8 networks in the expected /// order matching the library constants. function testSupportedNetworks() external pure { string[] memory networks = LibRainDeploy.supportedNetworks(); - assertEq(networks.length, 7); + assertEq(networks.length, 8); assertEq(networks[0], LibRainDeploy.ARBITRUM_ONE); assertEq(networks[1], LibRainDeploy.BASE); assertEq(networks[2], LibRainDeploy.BASE_SEPOLIA); @@ -205,6 +205,7 @@ contract LibRainDeployTest is Test { assertEq(networks[4], LibRainDeploy.FLARE); assertEq(networks[5], LibRainDeploy.HYPEREVM); assertEq(networks[6], LibRainDeploy.POLYGON); + assertEq(networks[7], LibRainDeploy.ROBINHOOD); } /// `ZOLTU_FACTORY_CODEHASH` MUST match the actual codehash of the Zoltu From 4b70cb4aeb7ee0ad4dc296bbbb106ad33004f190 Mon Sep 17 00:00:00 2001 From: Josh Hardy Date: Thu, 10 Sep 2026 07:18:51 +0000 Subject: [PATCH 2/3] Add BNB Smart Chain (56) to the supported network set `LibRainDeploy.BSC` joins `supportedNetworks()` (alphabetical, after `base_sepolia`). The Zoltu factory is live on 56 with the canonical runtime (the fork test pins it). Etherscan V2 covers 56, so the `[etherscan]` entry is the ordinary keyed form with `chain = 56`; the key variable is exported by rainix#371. Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_01HiqQdxokJ4edjAFyAkN9G3 --- foundry.toml | 2 ++ src/lib/LibRainDeploy.sol | 18 ++++++++++++------ test/src/lib/LibRainDeploy.t.sol | 15 ++++++++------- 3 files changed, 22 insertions(+), 13 deletions(-) diff --git a/foundry.toml b/foundry.toml index 562619b..4826c97 100644 --- a/foundry.toml +++ b/foundry.toml @@ -77,6 +77,7 @@ recursive_deps = false arbitrum = "${ARBITRUM_RPC_URL}" base = "${BASE_RPC_URL}" base_sepolia = "${BASE_SEPOLIA_RPC_URL}" +bsc = "${BSC_RPC_URL}" ethereum = "${ETHEREUM_RPC_URL}" flare = "${FLARE_RPC_URL}" hyperevm = "${HYPEREVM_RPC_URL}" @@ -102,6 +103,7 @@ robinhood = "${ROBINHOOD_RPC_URL}" arbitrum = { key = "${CI_DEPLOY_ARBITRUM_ETHERSCAN_API_KEY}" } base = { key = "${CI_DEPLOY_BASE_ETHERSCAN_API_KEY}" } base_sepolia = { key = "${CI_DEPLOY_BASE_SEPOLIA_ETHERSCAN_API_KEY}" } +bsc = { key = "${CI_DEPLOY_BSC_ETHERSCAN_API_KEY}", chain = 56 } ethereum = { key = "${CI_DEPLOY_ETHEREUM_ETHERSCAN_API_KEY}", chain = 1 } flare = { key = "${CI_DEPLOY_FLARE_ETHERSCAN_API_KEY}" } hyperevm = { key = "${CI_DEPLOY_HYPEREVM_ETHERSCAN_API_KEY}", chain = 999 } diff --git a/src/lib/LibRainDeploy.sol b/src/lib/LibRainDeploy.sol index 82c480f..d794da2 100644 --- a/src/lib/LibRainDeploy.sol +++ b/src/lib/LibRainDeploy.sol @@ -82,6 +82,11 @@ library LibRainDeploy { /// Config name for Base network. string constant BASE = "base"; + /// Config name for BNB Smart Chain (chain id 56). The Zoltu factory is + /// deployed there with the canonical runtime, so deterministic deploys + /// land at the same addresses as on every other supported network. + string constant BSC = "bsc"; + /// Config name for Base Sepolia testnet. string constant BASE_SEPOLIA = "base_sepolia"; @@ -290,15 +295,16 @@ library LibRainDeploy { /// Returns the list of networks currently supported by Rain deployments. /// @return The list of supported network names. function supportedNetworks() internal pure returns (string[] memory) { - string[] memory networks = new string[](8); + string[] memory networks = new string[](9); networks[0] = ARBITRUM_ONE; networks[1] = BASE; networks[2] = BASE_SEPOLIA; - networks[3] = ETHEREUM; - networks[4] = FLARE; - networks[5] = HYPEREVM; - networks[6] = POLYGON; - networks[7] = ROBINHOOD; + networks[3] = BSC; + networks[4] = ETHEREUM; + networks[5] = FLARE; + networks[6] = HYPEREVM; + networks[7] = POLYGON; + networks[8] = ROBINHOOD; return networks; } diff --git a/test/src/lib/LibRainDeploy.t.sol b/test/src/lib/LibRainDeploy.t.sol index d33a842..e051d40 100644 --- a/test/src/lib/LibRainDeploy.t.sol +++ b/test/src/lib/LibRainDeploy.t.sol @@ -193,19 +193,20 @@ contract LibRainDeployTest is Test { ); } - /// `supportedNetworks` MUST return exactly 8 networks in the expected + /// `supportedNetworks` MUST return exactly 9 networks in the expected /// order matching the library constants. function testSupportedNetworks() external pure { string[] memory networks = LibRainDeploy.supportedNetworks(); - assertEq(networks.length, 8); + assertEq(networks.length, 9); assertEq(networks[0], LibRainDeploy.ARBITRUM_ONE); assertEq(networks[1], LibRainDeploy.BASE); assertEq(networks[2], LibRainDeploy.BASE_SEPOLIA); - assertEq(networks[3], LibRainDeploy.ETHEREUM); - assertEq(networks[4], LibRainDeploy.FLARE); - assertEq(networks[5], LibRainDeploy.HYPEREVM); - assertEq(networks[6], LibRainDeploy.POLYGON); - assertEq(networks[7], LibRainDeploy.ROBINHOOD); + assertEq(networks[3], LibRainDeploy.BSC); + assertEq(networks[4], LibRainDeploy.ETHEREUM); + assertEq(networks[5], LibRainDeploy.FLARE); + assertEq(networks[6], LibRainDeploy.HYPEREVM); + assertEq(networks[7], LibRainDeploy.POLYGON); + assertEq(networks[8], LibRainDeploy.ROBINHOOD); } /// `ZOLTU_FACTORY_CODEHASH` MUST match the actual codehash of the Zoltu From f208af4f9d4375453ba83ec6fbc2fa92ee095ebc Mon Sep 17 00:00:00 2001 From: Josh Hardy Date: Thu, 10 Sep 2026 08:45:37 +0000 Subject: [PATCH 3/3] Mirror the new networks into .env.example and the verify dispatcher The same two files #142 touched for HyperEVM and Ethereum: local RPC placeholders for `bsc` and `robinhood`, and `bsc` in `manual-sol-verify`'s default explorer list. Robinhood Chain stays out of that default on purpose (Sourcify-only, see foundry.toml). Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_01HiqQdxokJ4edjAFyAkN9G3 --- .env.example | 2 ++ .github/workflows/manual-sol-verify.yaml | 9 ++++++--- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/.env.example b/.env.example index f85f5ba..f88a536 100644 --- a/.env.example +++ b/.env.example @@ -8,7 +8,9 @@ ARBITRUM_RPC_URL=https://arb1.arbitrum.io/rpc BASE_RPC_URL=https://mainnet.base.org BASE_SEPOLIA_RPC_URL=https://sepolia.base.org +BSC_RPC_URL=https://bsc-dataseed.binance.org ETHEREUM_RPC_URL=https://eth-pokt.nodies.app FLARE_RPC_URL=https://flare-api.flare.network/ext/C/rpc HYPEREVM_RPC_URL=https://rpc.hyperliquid.xyz/evm POLYGON_RPC_URL=https://polygon-bor-rpc.publicnode.com +ROBINHOOD_RPC_URL=https://rpc.mainnet.chain.robinhood.com diff --git a/.github/workflows/manual-sol-verify.yaml b/.github/workflows/manual-sol-verify.yaml index 0d66b1a..3b3a481 100644 --- a/.github/workflows/manual-sol-verify.yaml +++ b/.github/workflows/manual-sol-verify.yaml @@ -33,13 +33,16 @@ on: networks: type: string required: true - default: arbitrum base base-sepolia mainnet flare hyperliquid polygon + default: arbitrum base base-sepolia bsc mainnet flare hyperliquid polygon description: | Which explorers to submit to. FOUNDRY's chain names, not the - `[rpc_endpoints]` aliases, and the two differ on three of the seven: + `[rpc_endpoints]` aliases, and the two differ on three of the nine: the aliases `base_sepolia`, `ethereum` and `hyperevm` are rejected outright, and the chain names are `base-sepolia`, `mainnet` and - `hyperliquid`. The default is + `hyperliquid`. Robinhood Chain (4663) is deliberately absent from + the default: Etherscan does not index it, so its Blockscout is the + only explorer target and that has rejected non-browser clients — + verify it through Sourcify by hand. The default is `LibRainDeploy.supportedNetworks()` spelled that way, i.e. every network the deploy broadcasts to, so it has to move when that does. jobs: