diff --git a/.env.example b/.env.example index 6a150be..f85f5ba 100644 --- a/.env.example +++ b/.env.example @@ -8,5 +8,7 @@ ARBITRUM_RPC_URL=https://arb1.arbitrum.io/rpc BASE_RPC_URL=https://mainnet.base.org BASE_SEPOLIA_RPC_URL=https://sepolia.base.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 diff --git a/.github/workflows/manual-sol-verify.yaml b/.github/workflows/manual-sol-verify.yaml index e39c5aa..0d66b1a 100644 --- a/.github/workflows/manual-sol-verify.yaml +++ b/.github/workflows/manual-sol-verify.yaml @@ -33,11 +33,13 @@ on: networks: type: string required: true - default: arbitrum base base-sepolia flare polygon + default: arbitrum base base-sepolia mainnet flare hyperliquid polygon description: | Which explorers to submit to. FOUNDRY's chain names, not the - `[rpc_endpoints]` aliases: `base-sepolia` is a chain name and - `base_sepolia` is rejected outright. The default is + `[rpc_endpoints]` aliases, and the two differ on three of the seven: + the aliases `base_sepolia`, `ethereum` and `hyperevm` are rejected + outright, and the chain names are `base-sepolia`, `mainnet` and + `hyperliquid`. 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: diff --git a/foundry.toml b/foundry.toml index d925ebd..42d0c86 100644 --- a/foundry.toml +++ b/foundry.toml @@ -77,7 +77,9 @@ recursive_deps = false arbitrum = "${ARBITRUM_RPC_URL}" base = "${BASE_RPC_URL}" base_sepolia = "${BASE_SEPOLIA_RPC_URL}" +ethereum = "${ETHEREUM_RPC_URL}" flare = "${FLARE_RPC_URL}" +hyperevm = "${HYPEREVM_RPC_URL}" polygon = "${POLYGON_RPC_URL}" # `rainix-manual-sol-artifacts` passes `--verify` by default and exports exactly @@ -88,9 +90,18 @@ polygon = "${POLYGON_RPC_URL}" # Both sections are checked against `LibRainDeploy.supportedNetworks()`, in both # directions, by `testSupportedNetworksAreFullyConfigured`. Adding a network is # an edit to all three or a red test, not a broadcast that discovers it. +# +# `chain` is stated on the entries whose alias foundry does not itself resolve +# to a chain. An entry with neither `chain` nor `url` under such an alias is not +# a missing key, it is a config error — "At least one of `url` or `chain` must +# be present for Etherscan config with unknown alias" — raised while resolving +# the section, so it takes down verification for every network in it and not +# only its own. [etherscan] 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}" } +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}" } diff --git a/script/Deploy.sol b/script/Deploy.sol index 0370711..9345fd7 100644 --- a/script/Deploy.sol +++ b/script/Deploy.sol @@ -24,8 +24,8 @@ import {RegistryDeploySuites} from "../src/abstract/RegistryDeploySuites.sol"; /// /// Deploying is idempotent by construction. `deployToNetworks` checks the /// recorded address against the creation code before it forks anything, then -/// skips any network that already has code there, so a partial run — three -/// chains of five, one RPC down — is fixed by running it again rather than by +/// skips any network that already has code there, so a partial run — five +/// chains of seven, one RPC down — is fixed by running it again rather than by /// unpicking anything. /// /// `RegistryDeployChainTest` is what says whether this has been run and worked diff --git a/src/abstract/RainDeployVerifyChain.sol b/src/abstract/RainDeployVerifyChain.sol index ce84877..7f1115f 100644 --- a/src/abstract/RainDeployVerifyChain.sol +++ b/src/abstract/RainDeployVerifyChain.sol @@ -36,8 +36,8 @@ error CodeHashMismatchOnNetwork( /// /// This is the only group that can catch a suite that never deployed to a /// network, or that is not there any more. Neither is a fact the repo can hold: -/// both can go false with nobody touching it — a release that reached four -/// chains of five, a chain added to `supportedNetworks()` after a release that +/// both can go false with nobody touching it — a release that reached six +/// chains of seven, a chain added to `supportedNetworks()` after a release that /// therefore never got it, a deploy that silently failed. /// /// ## Released only, for the same reason source anchors the candidate only @@ -108,7 +108,7 @@ abstract contract RainDeployVerifyChain is RainDeployVerifyBase { /// expectation. /// @param derived The derivation of every suite to check. function checkDeployedOnSupportedNetworks(DerivedDeploy[] memory derived) internal { - // Nothing to check is not a reason to touch five RPC endpoints. Forking + // Nothing to check is not a reason to touch seven RPC endpoints. Forking // to check nothing turns an outage into the failure of an assertion // that has no subject, which is the one failure this contract is // supposed to be legible against. diff --git a/src/lib/LibRainDeploy.sol b/src/lib/LibRainDeploy.sol index 4ce5e80..2c6dda5 100644 --- a/src/lib/LibRainDeploy.sol +++ b/src/lib/LibRainDeploy.sol @@ -85,9 +85,15 @@ library LibRainDeploy { /// Config name for Base Sepolia testnet. string constant BASE_SEPOLIA = "base_sepolia"; + /// Config name for Ethereum mainnet. + string constant ETHEREUM = "ethereum"; + /// Config name for Flare network. string constant FLARE = "flare"; + /// Config name for HyperEVM network. + string constant HYPEREVM = "hyperevm"; + /// Config name for Polygon network. string constant POLYGON = "polygon"; @@ -251,12 +257,14 @@ 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[](5); + string[] memory networks = new string[](7); networks[0] = ARBITRUM_ONE; networks[1] = BASE; networks[2] = BASE_SEPOLIA; - networks[3] = FLARE; - networks[4] = POLYGON; + networks[3] = ETHEREUM; + networks[4] = FLARE; + networks[5] = HYPEREVM; + networks[6] = POLYGON; return networks; } diff --git a/test/concrete/MissingDependencyDeploy.sol b/test/concrete/MissingDependencyDeploy.sol index c2d7ba8..b4592f1 100644 --- a/test/concrete/MissingDependencyDeploy.sol +++ b/test/concrete/MissingDependencyDeploy.sol @@ -22,7 +22,7 @@ address constant ABSENT_DEPENDENCY = address(0xdeadbee5); /// dependency check by design and would say nothing about the list. /// /// One network, so the refusal names a chain that is the whole target set -/// rather than the first of five. Keyed `second-address-candidate` for the +/// rather than the first of seven. Keyed `second-address-candidate` for the /// reason `StalePinDeploy` gives. contract MissingDependencyDeploy is RainDeployBroadcast { /// @inheritdoc RainDeployBroadcast diff --git a/test/src/abstract/RainDeployBroadcast.t.sol b/test/src/abstract/RainDeployBroadcast.t.sol index 0df6c91..6ec8c18 100644 --- a/test/src/abstract/RainDeployBroadcast.t.sol +++ b/test/src/abstract/RainDeployBroadcast.t.sol @@ -152,9 +152,9 @@ contract RainDeployBroadcastTest is Test { // // A fixture that names ONE network, so that where the broadcast went is // observable at all. `sDeploy` takes the default target set, and a suite - // deployed to all five chains and a suite deployed to the one chain the + // deployed to all seven chains and a suite deployed to the one chain the // repo asked for are indistinguishable from a fixture that asks for all - // five. + // seven. ExampleDeploySingleNetwork single = new ExampleDeploySingleNetwork(); // Derived here from the same source the declaration derives them from, @@ -198,7 +198,7 @@ contract RainDeployBroadcastTest is Test { // arbitrum for this fixture's single-element override, polygon for the // default. That is the whole of the difference an assertion can see, and // an override `run()` ignored is a repo that asked for one chain getting - // a suite on five — with a revert partway through leaving a dispatch + // a suite on seven — with a revert partway through leaving a dispatch // half done. // // `testDeployNetworksDefaultsToSupportedNetworks` asserts the default @@ -258,7 +258,7 @@ contract RainDeployBroadcastTest is Test { /// before the `CREATE2` goes out compares the recorded address against the /// recorded creation code, both of which come out of the same generated /// file. That catches a stale PIN and cannot catch a snapshot of the wrong - /// CONTRACT, so without this the bytes reaching five chains are whatever the + /// CONTRACT, so without this the bytes reaching seven chains are whatever the /// generated file happens to hold. `CREATE2` at a zero salt makes that /// permanent: the wrong bytes take the wrong bytes' own address, on every /// chain the dispatch reached, and the dispatch is `workflow_dispatch` on a diff --git a/test/src/abstract/RainDeployVerifyChain.t.sol b/test/src/abstract/RainDeployVerifyChain.t.sol index 8915def..f4f55ad 100644 --- a/test/src/abstract/RainDeployVerifyChain.t.sol +++ b/test/src/abstract/RainDeployVerifyChain.t.sol @@ -43,10 +43,10 @@ import { /// "nothing is deployed at this address" from something the fixture arranged /// into a claim about the world. That claim is false here: the exemplar's /// addresses come from `src/generated/candidate/`, which is exactly what -/// `Manual sol artifacts` broadcasts, and `AddressRegistry` is live on all five -/// supported networks. A negative case resting on it asserts nothing and -/// reports `next call did not revert as expected` — a fixture that only worked -/// while the repo had not yet done the thing it exists to do. +/// `Manual sol artifacts` broadcasts, and `AddressRegistry` is live on five of +/// the seven supported networks. A negative case resting on it asserts nothing +/// and reports `next call did not revert as expected` — a fixture that only +/// worked while the repo had not yet done the thing it exists to do. /// /// Pointing the fixture at a mock nobody deploys would move that dependency /// rather than remove it: the Zoltu factory is permissionless, so no address is @@ -94,7 +94,7 @@ contract RainDeployVerifyChainTest is ExampleDeploySuites, RainDeployVerifyChain /// A version that is not on a network MUST fail, naming the network, the /// version and the address. This is the whole reason the group exists: a - /// release that reached four chains of five, or a chain added after a + /// release that reached six chains of seven, or a chain added after a /// release that therefore never got it, is invisible to every other check. function testChainNotDeployedReverts() external { // Emptied, and left persistent, so every fork carries an empty account @@ -164,7 +164,7 @@ contract RainDeployVerifyChainTest is ExampleDeploySuites, RainDeployVerifyChain /// /// This contract is where it belongs because it already forks every /// supported network. Asserting it from the empty-set side would hand the - /// contract that exists to need no RPC endpoint the five-endpoint dependency + /// contract that exists to need no RPC endpoint the seven-endpoint dependency /// the early return removes from it. function testChainWithASingleSubjectDoesFork() external { (bool activeBefore,) = address(vm).call(abi.encodeWithSignature("activeFork()")); diff --git a/test/src/abstract/RegistryDeployChain.t.sol b/test/src/abstract/RegistryDeployChain.t.sol index de9b49d..e48943c 100644 --- a/test/src/abstract/RegistryDeployChain.t.sol +++ b/test/src/abstract/RegistryDeployChain.t.sol @@ -28,20 +28,20 @@ import {RegistryDeploySuites} from "../../../src/abstract/RegistryDeploySuites.s /// itself. contract RegistryDeployChainTest is RegistryDeploySuites, RainDeployVerifyChain { /// Nothing to check MUST NOT touch an RPC endpoint. This repo has released - /// nothing, so this is the branch every CI run takes: forking five networks + /// nothing, so this is the branch every CI run takes: forking seven networks /// to check nothing turns an outage into the failure of an assertion with /// no subject, which is the one failure this contract exists to stay /// legible against. /// /// The ABSENCE of a fork is what is asserted, because the pass is identical - /// either way — the matrix that forks all five and finds nothing to check on + /// either way — the matrix that forks all seven and finds nothing to check on /// each of them passes too, and is the only thing this contract would have /// done differently. `vm.activeFork()` reverts when nothing is selected, so /// the low-level call failing IS "no network was reached". /// /// It runs the whole inherited entry point rather than handing the matrix an /// empty array, so the derivation is inside what is asserted: a fork opened - /// while deriving would touch the same five endpoints for the same nothing. + /// while deriving would touch the same seven endpoints for the same nothing. /// /// The empty released set is asserted rather than assumed, because it is the /// premise and not the property. The first release gives this contract a diff --git a/test/src/lib/LibRainDeploy.t.sol b/test/src/lib/LibRainDeploy.t.sol index 5a3b0bd..06d9bd2 100644 --- a/test/src/lib/LibRainDeploy.t.sol +++ b/test/src/lib/LibRainDeploy.t.sol @@ -194,16 +194,18 @@ contract LibRainDeployTest is Test { ); } - /// `supportedNetworks` MUST return exactly 5 networks in the expected + /// `supportedNetworks` MUST return exactly 7 networks in the expected /// order matching the library constants. function testSupportedNetworks() external pure { string[] memory networks = LibRainDeploy.supportedNetworks(); - assertEq(networks.length, 5); + assertEq(networks.length, 7); assertEq(networks[0], LibRainDeploy.ARBITRUM_ONE); assertEq(networks[1], LibRainDeploy.BASE); assertEq(networks[2], LibRainDeploy.BASE_SEPOLIA); - assertEq(networks[3], LibRainDeploy.FLARE); - assertEq(networks[4], LibRainDeploy.POLYGON); + assertEq(networks[3], LibRainDeploy.ETHEREUM); + assertEq(networks[4], LibRainDeploy.FLARE); + assertEq(networks[5], LibRainDeploy.HYPEREVM); + assertEq(networks[6], LibRainDeploy.POLYGON); } /// PROPERTY: `[rpc_endpoints]` and `[etherscan]` in `foundry.toml` are @@ -1223,7 +1225,7 @@ contract LibRainDeployTest is Test { /// /// Two networks rather than `supportedNetworks()`. What is under test is /// that the loop visits every network it is given, which two prove as well - /// as five; the roster itself is `testSupportedNetworks`'s job. These are + /// as seven; the roster itself is `testSupportedNetworks`'s job. These are /// the two networks the rest of this suite forks, so the test does not /// depend on the reliability of RPC endpoints nothing else here touches. function testCheckResolvedAddressesOnNetworksEachNetwork() external { @@ -1273,7 +1275,7 @@ contract LibRainDeployTest is Test { /// just as happily — and the mismatch case above is one network, so it /// cannot tell them apart either. What separates them is a target that /// answers differently on a LATER network, which is exactly the deployment - /// this matrix exists for: one chain of five holding a value nobody looked + /// this matrix exists for: one chain of seven holding a value nobody looked /// at. /// /// The first network is the one the target agrees on, so nothing fails