diff --git a/.github/workflows/git-clean.yaml b/.github/workflows/git-clean.yaml new file mode 100644 index 0000000..2324c62 --- /dev/null +++ b/.github/workflows/git-clean.yaml @@ -0,0 +1,6 @@ +name: Git is clean +on: [push] +jobs: + copy-artifacts: + uses: rainlanguage/rainix/.github/workflows/rainix-copy-artifacts.yaml@main + secrets: inherit diff --git a/.github/workflows/package-release.yaml b/.github/workflows/package-release.yaml index 4718aa4..bf2a944 100644 --- a/.github/workflows/package-release.yaml +++ b/.github/workflows/package-release.yaml @@ -9,5 +9,5 @@ jobs: uses: rainlanguage/rainix/.github/workflows/rainix-tag-release.yaml@main with: soldeer-package: rain-factory-deploy - snapshot-generate-cmd: forge script ./script/BuildPointers.sol && forge fmt + snapshot-generate-cmd: forge script ./script/Build.sol --sig "cutRelease()" && forge fmt secrets: inherit diff --git a/CLAUDE.md b/CLAUDE.md index 13e53ff..63791c3 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -26,14 +26,17 @@ live in `rain.factory` and arrive as the `rain-factory` Soldeer dependency ## Deploy-pin invariants (the hazards) -- `src/generated//` snapshots are **frozen**: a release ADDS a new tag dir, - never edits or deletes an existing one. CI enforces append-only. -- `[external.package].version` is the **last released** version — it names the - current `src/generated//` snapshot. A normal PR does not bump it; only a - release moves it, in lockstep with the snapshot. -- `LibCloneFactoryDeploy.sol` aliases the current tag's snapshot; - `script/BuildPointers.sol` regenerates both. Generated files — do not - hand-edit. +- `src/generated/candidate/` is the **rolling** snapshot, rewritten from what + source compiles to by `script/Build.sol` and currency-checked by CI. + `LibCloneFactoryDeploy.sol` aliases it. +- `src/generated//` snapshots are **frozen**: `cutRelease()` freezes the + candidate into a new tag dir; a release only ADDS one, never edits or deletes + an existing one. CI enforces append-only. +- `[external.package].version` is the **last released** version. A normal PR + does not bump it; only a release moves it, in lockstep with a new frozen + `/`. +- Generated files (`src/generated/`, `src/lib/LibCloneFactoryDeploy.sol`) — do + not hand-edit; `script/Build.sol` regenerates them. ## Release / deploy shape diff --git a/README.md b/README.md index 837efa3..d08b411 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,9 @@ # rain.factory.deploy The **deployment** half of `rain.factory`: the concrete `CloneFactory` contract, -its deployed address + codehash pins (`LibCloneFactoryDeploy`), the frozen -per-tag deploy-pin snapshots under `src/generated//`, and the deploy -script. +its deployed address + codehash pins (`LibCloneFactoryDeploy`), the rolling +`src/generated/candidate/` snapshot those pins alias, the frozen per-release +snapshots under `src/generated//`, and the deploy script. The **library** half — the `ICloneable*` interfaces — lives in [`rain.factory`](https://github.com/rainlanguage/rain.factory) and is imported @@ -20,7 +20,8 @@ tagging: the `Manual sol artifacts` workflow runs `script/Deploy.sol` for the `clone-factory` suite. Tagging then runs `rainix-tag-release`, which never broadcasts a deploy itself; its mechanics live in rainix. -Nothing publishes on merge, so `[external.package].version` and the frozen -`src/generated//` snapshot it names only ever move together. +Nothing publishes on merge: a release bumps `[external.package].version` and +freezes the current `src/generated/candidate/` snapshot into a new +`src/generated//` in lockstep. See rainlanguage/rain.factory#46 for the split rationale. diff --git a/foundry.toml b/foundry.toml index 76f86b1..b6e73fa 100644 --- a/foundry.toml +++ b/foundry.toml @@ -5,7 +5,7 @@ # into `[profile.*]` by the fixer). `version` is the last-published Soldeer # release — a deploy repo moves it only via a manual `sol-v*` tag, in lockstep # with the frozen `src/generated//` snapshot it names — and is read here by -# `script/BuildPointers.sol` (`deployTag()`) to locate that snapshot. +# `script/Build.sol`'s `cutRelease()` to locate the snapshot it freezes. [external.package] name = "rain-factory-deploy" version = "0.1.5" @@ -26,9 +26,9 @@ evm_version = "cancun" bytecode_hash = "none" cbor_metadata = false -# BuildPointers reads the version from foundry.toml and writes the generated -# per-tag snapshots + the current-pin lib under src/. Nothing else in this repo -# touches the filesystem. +# `script/Build.sol` writes the generated candidate snapshot and the alias lib +# under `src/`, and reads the release version from this file when `cutRelease()` +# runs. Nothing else in this repo touches the filesystem. fs_permissions = [ { access = "read", path = "./foundry.toml" }, { access = "read-write", path = "./src" }, @@ -39,11 +39,11 @@ libs = ["dependencies"] runs = 2048 [dependencies] -forge-std = "1.16.1" +forge-std = "1.16.2" "@openzeppelin-contracts" = "5.6.1" "rain-extrospection" = "0.1.1" -"rain-deploy" = "0.1.3" -"rain-sol-codegen" = "0.1.3" +"rain-deploy" = "0.1.7" +"rain-sol-codegen" = "0.1.36" "rain-factory" = "0.1.5" [soldeer] diff --git a/script/Build.sol b/script/Build.sol new file mode 100644 index 0000000..949fbf0 --- /dev/null +++ b/script/Build.sol @@ -0,0 +1,93 @@ +// SPDX-License-Identifier: LicenseRef-DCL-1.0 +// SPDX-FileCopyrightText: Copyright (c) 2020 Rain Open Source Software Ltd +pragma solidity =0.8.25; + +import {BuildScript} from "rain-deploy-0.1.7/src/abstract/BuildScript.sol"; +import {DeployCandidate} from "../src/abstract/RainDeploySuitesBase.sol"; +import {CloneFactoryDeploySuites} from "../src/abstract/CloneFactoryDeploySuites.sol"; +import {LibRainDeploySnapshot} from "rain-deploy-0.1.7/src/lib/LibRainDeploySnapshot.sol"; + +/// One contract's generated files: the rolling snapshot, the alias lib that +/// re-exports its pins and the released-suites lib emitted from its record. +struct GeneratedContract { + /// Places the snapshot inside `src/generated//` and names both + /// generated libs. + string contractName; + /// Prefix for the constants the alias lib exports, e.g. `CLONE_FACTORY`. + string constantPrefix; + /// Snapshots are written from its `sourceCreationCode` and + /// `snapshot.dependencies`; the released lib takes its suite key and + /// artifact path from its `snapshot`. + DeployCandidate candidate; +} + +/// @title Build +/// @notice Generates the deploy pins for every contract this repo deploys. +/// `generatedContracts()` is the only list, read by every hook below. +/// +/// `run()` (what CI regenerates against) rewrites the rolling +/// `src/generated/candidate/` snapshot, the alias lib and the released-suites +/// libs. `cutRelease()` freezes the candidate into `src/generated//` +/// first. The frozen `0_1_3`/`0_1_4`/`0_1_5` snapshots are append-only +/// historical records, never regenerated here. +contract Build is BuildScript, CloneFactoryDeploySuites { + /// Every contract this repo generates deploy pins for. + /// @return The generated contracts. + function generatedContracts() internal pure returns (GeneratedContract[] memory) { + GeneratedContract[] memory contracts = new GeneratedContract[](1); + contracts[0] = GeneratedContract({ + contractName: "CloneFactory", constantPrefix: "CLONE_FACTORY", candidate: cloneFactoryCandidate() + }); + return contracts; + } + + /// @inheritdoc BuildScript + /// @dev In declaration order — the order the aggregate emits its entries + /// in. + function snapshotContractNames() internal pure override returns (string[] memory) { + GeneratedContract[] memory contracts = generatedContracts(); + string[] memory names = new string[](contracts.length); + for (uint256 i = 0; i < contracts.length; i++) { + names[i] = contracts[i].contractName; + } + return names; + } + + /// @inheritdoc BuildScript + /// @dev Every alias lib, every released-suites lib and the aggregate over + /// them. + function regenerateLibs() internal override { + GeneratedContract[] memory contracts = generatedContracts(); + for (uint256 i = 0; i < contracts.length; i++) { + LibRainDeploySnapshot.writeAliasLib( + vm, + LibRainDeploySnapshot.LIB_DIR, + contracts[i].contractName, + contracts[i].constantPrefix, + LibRainDeploySnapshot.CANDIDATE + ); + LibRainDeploySnapshot.writeReleasedSuitesLib( + vm, + LibRainDeploySnapshot.LIB_DIR, + recordRoot(), + contracts[i].contractName, + contracts[i].candidate.snapshot + ); + } + LibRainDeploySnapshot.writeReleasedSuitesAggregate(vm, LibRainDeploySnapshot.LIB_DIR, snapshotContractNames()); + } + + /// @inheritdoc BuildScript + function regenerateSnapshots() internal override { + GeneratedContract[] memory contracts = generatedContracts(); + for (uint256 i = 0; i < contracts.length; i++) { + LibRainDeploySnapshot.writeSnapshot( + vm, + LibRainDeploySnapshot.CANDIDATE, + contracts[i].contractName, + contracts[i].candidate.sourceCreationCode, + contracts[i].candidate.snapshot.dependencies + ); + } + } +} diff --git a/script/BuildPointers.sol b/script/BuildPointers.sol deleted file mode 100644 index a839511..0000000 --- a/script/BuildPointers.sol +++ /dev/null @@ -1,112 +0,0 @@ -// SPDX-License-Identifier: LicenseRef-DCL-1.0 -// SPDX-FileCopyrightText: Copyright (c) 2020 Rain Open Source Software Ltd -pragma solidity =0.8.25; - -import {Script} from "forge-std-1.16.1/src/Script.sol"; -import {LibCodeGen} from "rain-sol-codegen-0.1.3/src/lib/LibCodeGen.sol"; -import {LibFs} from "rain-sol-codegen-0.1.3/src/lib/LibFs.sol"; -import {LibRainDeploy} from "rain-deploy-0.1.3/src/lib/LibRainDeploy.sol"; -import {CloneFactory} from "../src/concrete/CloneFactory.sol"; - -/// @title BuildPointers -/// @notice Generates the deterministic-deploy pins for `CloneFactory`: -/// 1. A frozen per-release snapshot `src/generated//CloneFactory.pointers.sol` -/// (`BYTECODE_HASH`, `DEPLOYED_ADDRESS`, `CREATION_CODE`, `RUNTIME_CODE`) for -/// the current `deployTag()`. Historical tags are never regenerated; a -/// release bump writes a new `/` snapshot beside them. -/// 2. `src/lib/LibCloneFactoryDeploy.sol` — the current-release address and -/// codehash, aliased from the current `deployTag()` snapshot so that -/// snapshot stays the single source of truth (never a duplicated literal). -/// Kept in `src/lib` so consumers' import path is stable across releases. -/// -/// Run as `forge script script/BuildPointers.sol`. Wired into the autopublish -/// `soldeer-generate-cmd` so the pins regenerate born-green on each release. -contract BuildPointers is Script { - string constant GEN_LIB_PATH = "src/lib/LibCloneFactoryDeploy.sol"; - - // REUSE-IgnoreStart (the two SPDX lines below are the header EMITTED into the - // generated lib, not this script's own license — hide from reuse lint) - string constant GEN_SPDX_LICENSE = "// SPDX-License-Identifier: LicenseRef-DCL-1.0"; - string constant GEN_SPDX_COPYRIGHT = "// SPDX-FileCopyrightText: Copyright (c) 2020 Rain Open Source Software Ltd"; - - // REUSE-IgnoreEnd - - /// @notice The canonical release tag. Read from `foundry.toml` - /// `[external.package].version` — the single source of truth — with dots - /// converted to underscores for the Solidity dir form (`0.1.3` -> `0_1_3`). - function deployTag() internal view returns (string memory) { - string memory version = vm.parseTomlString(vm.readFile("foundry.toml"), ".external.package.version"); - bytes memory b = bytes(version); - bytes memory out = new bytes(b.length); - for (uint256 i = 0; i < b.length; i++) { - out[i] = b[i] == "." ? bytes1("_") : b[i]; - } - return string(out); - } - - function run() external { - LibRainDeploy.etchZoltuFactory(vm); - - // A fresh next-version slot has no `/` dir yet, and `vm.writeFile` - // won't create one. - vm.createDir(string.concat("src/generated/", deployTag()), true); - - bytes memory creationCode = type(CloneFactory).creationCode; - address deployed = LibRainDeploy.deployZoltu(creationCode); - - // Frozen per-tag snapshot. - LibFs.buildFileForContract( - vm, - deployed, - string.concat(deployTag(), "/CloneFactory"), - string.concat( - LibCodeGen.addressConstantString( - vm, - "/// @dev The deterministic deploy address of the contract when deployed via\n/// the Zoltu factory.", - "DEPLOYED_ADDRESS", - deployed - ), - LibCodeGen.bytesConstantString( - vm, "/// @dev The creation bytecode of the contract.", "CREATION_CODE", creationCode - ), - LibCodeGen.bytesConstantString( - vm, "/// @dev The runtime bytecode of the contract.", "RUNTIME_CODE", deployed.code - ) - ) - ); - - // Current-release pin lib. - genLibCloneFactoryDeploy(); - } - - /// @notice (Re)generate `src/lib/LibCloneFactoryDeploy.sol`, aliasing the - /// current `deployTag()` snapshot's `DEPLOYED_ADDRESS` + `BYTECODE_HASH` as - /// the current-release constants — the snapshot stays the single source of - /// truth (never a duplicated literal). Emitted line-by-line to match the - /// generated-file convention. - function genLibCloneFactoryDeploy() internal { - string memory importPath = string.concat("../generated/", deployTag(), "/CloneFactory.pointers.sol"); - vm.writeFile(GEN_LIB_PATH, ""); - vm.writeLine(GEN_LIB_PATH, GEN_SPDX_LICENSE); - vm.writeLine(GEN_LIB_PATH, GEN_SPDX_COPYRIGHT); - vm.writeLine(GEN_LIB_PATH, "pragma solidity ^0.8.25;"); - vm.writeLine(GEN_LIB_PATH, ""); - vm.writeLine(GEN_LIB_PATH, "// THIS FILE IS AUTOGENERATED BY ./script/BuildPointers.sol"); - vm.writeLine(GEN_LIB_PATH, ""); - vm.writeLine(GEN_LIB_PATH, "import {"); - vm.writeLine(GEN_LIB_PATH, " DEPLOYED_ADDRESS as CLONE_FACTORY_ADDR,"); - vm.writeLine(GEN_LIB_PATH, " BYTECODE_HASH as CLONE_FACTORY_HASH"); - vm.writeLine(GEN_LIB_PATH, string.concat("} from \"", importPath, "\";")); - vm.writeLine(GEN_LIB_PATH, ""); - vm.writeLine(GEN_LIB_PATH, "/// @title LibCloneFactoryDeploy"); - vm.writeLine(GEN_LIB_PATH, "/// @notice The deterministic Zoltu deploy address and code hash of the current"); - vm.writeLine(GEN_LIB_PATH, "/// `CloneFactory` release, aliased from the frozen per-release snapshot in"); - vm.writeLine(GEN_LIB_PATH, "/// `src/generated//CloneFactory.pointers.sol` so that snapshot stays the"); - vm.writeLine(GEN_LIB_PATH, "/// single source of truth. Lets consumers verify/deploy against a precommitted"); - vm.writeLine(GEN_LIB_PATH, "/// address + hash rather than a registry."); - vm.writeLine(GEN_LIB_PATH, "library LibCloneFactoryDeploy {"); - vm.writeLine(GEN_LIB_PATH, " address constant CLONE_FACTORY_DEPLOYED_ADDRESS = CLONE_FACTORY_ADDR;"); - vm.writeLine(GEN_LIB_PATH, " bytes32 constant CLONE_FACTORY_DEPLOYED_CODEHASH = CLONE_FACTORY_HASH;"); - vm.writeLine(GEN_LIB_PATH, "}"); - } -} diff --git a/script/Deploy.sol b/script/Deploy.sol index eb7a3d4..393b0d8 100644 --- a/script/Deploy.sol +++ b/script/Deploy.sol @@ -2,39 +2,10 @@ // SPDX-FileCopyrightText: Copyright (c) 2020 Rain Open Source Software Ltd pragma solidity =0.8.25; -import {Script} from "forge-std-1.16.1/src/Script.sol"; -import {CloneFactory} from "../src/concrete/CloneFactory.sol"; -import {LibRainDeploy} from "rain-deploy-0.1.3/src/lib/LibRainDeploy.sol"; -import {LibCloneFactoryDeploy} from "../src/lib/LibCloneFactoryDeploy.sol"; - -/// @dev Hash of the "clone-factory" deployment suite string. -bytes32 constant DEPLOYMENT_SUITE_CLONE_FACTORY = keccak256("clone-factory"); +import {RainDeployBroadcast} from "rain-deploy-0.1.7/src/abstract/RainDeployBroadcast.sol"; +import {CloneFactoryDeploySuites} from "../src/abstract/CloneFactoryDeploySuites.sol"; /// @title Deploy -/// @notice A script that deploys a CloneFactory. -contract Deploy is Script { - mapping(string => mapping(address => bytes32)) internal sDepCodeHashes; - - function run() external { - uint256 deployerPrivateKey = vm.envUint("DEPLOYMENT_KEY"); - - bytes32 suite = keccak256(bytes(vm.envOr("DEPLOYMENT_SUITE", string("clone-factory")))); - if (suite == DEPLOYMENT_SUITE_CLONE_FACTORY) { - LibRainDeploy.deployAndBroadcast( - vm, - LibRainDeploy.supportedNetworks(), - deployerPrivateKey, - type(CloneFactory).creationCode, - "src/concrete/CloneFactory.sol:CloneFactory", - LibCloneFactoryDeploy.CLONE_FACTORY_DEPLOYED_ADDRESS, - LibCloneFactoryDeploy.CLONE_FACTORY_DEPLOYED_CODEHASH, - new address[](0), - sDepCodeHashes - ); - } else { - revert( - "Invalid deployment suite specified. Please set the DEPLOYMENT_SUITE environment variable to 'clone-factory'." - ); - } - } -} +/// @notice The declaration plus `RainDeployBroadcast`; the `Manual sol +/// artifacts` workflow dispatches the `clone-factory` suite. +contract Deploy is CloneFactoryDeploySuites, RainDeployBroadcast {} diff --git a/slither.config.json b/slither.config.json index 1e19395..947bc8d 100644 --- a/slither.config.json +++ b/slither.config.json @@ -1,4 +1,4 @@ { "detectors_to_exclude": "assembly-usage,solc-version,pragma,unindexed-event-address", - "filter_paths": "dependencies/forge-std-1.16.1,dependencies/@openzeppelin-contracts-5.6.1" + "filter_paths": "dependencies,src/abstract/(CloneFactoryDeploySuites|RainDeploySuitesBase)\\.sol" } diff --git a/soldeer.lock b/soldeer.lock index 300e1e9..2779c8c 100644 --- a/soldeer.lock +++ b/soldeer.lock @@ -7,17 +7,17 @@ integrity = "bce03af7ada1eee21a7fff393f238bcd7cd75a022a4db55ffb6b0dbb32433d35" [[dependencies]] name = "forge-std" -version = "1.16.1" -url = "https://soldeer-revisions.s3.amazonaws.com/forge-std/1_16_1_08-05-2026_08:51:16_forge-std-1.16.zip" -checksum = "839b61832925c7152c7b6dffbfa4998d9e606211179bd8f604733124e8a7cb57" -integrity = "60e55d10150354ca4a1e2985c5456c834b92b82ef85ab0e1d92a7786cddbd219" +version = "1.16.2" +url = "https://soldeer-revisions.s3.amazonaws.com/forge-std/1_16_2_03-07-2026_07:37:45_forge-std-1.16.zip" +checksum = "405dccc9d60d753f6abc412b4adf0359f4390f65dbadbbb277a12b4946ed8969" +integrity = "5fb4325b60d7d4194bd0481e2f94398cf1d9c6561dd12a66fb2748ef4a68205f" [[dependencies]] name = "rain-deploy" -version = "0.1.3" -url = "https://soldeer-revisions.s3.amazonaws.com/rain-deploy/0_1_3_20-05-2026_13:02:35_rain.zip" -checksum = "6e9a7a1562ee2766a1cc3a4a077b853ec628a3070b282e6e9102a252f2a6a5b5" -integrity = "10bff708d9e5d8b77655b8a8fc0c755cef8e3fc876cc3ff100425d27b08294a0" +version = "0.1.7" +url = "https://soldeer-revisions.s3.amazonaws.com/rain-deploy/0_1_7_19-08-2026_05:57:12_rain.zip" +checksum = "d0e868184ad16fc6abb704bb1dddab57ebf2beb042529690e030568f5c81a9b3" +integrity = "e51da6f2b3a0f427a0aac5d7714bed2027f5cf0f39b31f0692e9623ceeaa47f2" [[dependencies]] name = "rain-extrospection" @@ -35,7 +35,7 @@ integrity = "6e6eb9dc5174068e39d6ac80981210723088dde56c9cb0925ffe657ba3154cbc" [[dependencies]] name = "rain-sol-codegen" -version = "0.1.3" -url = "https://soldeer-revisions.s3.amazonaws.com/rain-sol-codegen/0_1_3_15-07-2026_08:55:10_rain.sol.zip" -checksum = "e9b8fa2e32ad2c5e2ea6c4f32a909c4d8969b207a486fdbe9d542f14b4125143" -integrity = "f78a6eee689ce93859736cb7d3b149802168e400688512b8865cf625e0bf3534" +version = "0.1.36" +url = "https://soldeer-revisions.s3.amazonaws.com/rain-sol-codegen/0_1_36_18-08-2026_09:20:33_rain.sol.zip" +checksum = "b5d101d46941a95fee441224e101004139814ae9afa2793138874bd7e93991ff" +integrity = "164e2655ac8b66bcf52c799421706d3dfaab987addaaeadd74574e70fd2eb6ed" diff --git a/src/abstract/CloneFactoryDeploySuites.sol b/src/abstract/CloneFactoryDeploySuites.sol new file mode 100644 index 0000000..7098258 --- /dev/null +++ b/src/abstract/CloneFactoryDeploySuites.sol @@ -0,0 +1,59 @@ +// SPDX-License-Identifier: LicenseRef-DCL-1.0 +// SPDX-FileCopyrightText: Copyright (c) 2020 Rain Open Source Software Ltd +pragma solidity ^0.8.25; + +import {DeployCandidate, DeploySuite, RainDeploySuitesBase} from "./RainDeploySuitesBase.sol"; +import {CloneFactory} from "../concrete/CloneFactory.sol"; +import { + CREATION_CODE as CLONE_FACTORY_CREATION_CODE_CANDIDATE, + RUNTIME_CODE as CLONE_FACTORY_RUNTIME_CODE_CANDIDATE +} from "../generated/candidate/CloneFactory.sol"; +import {LibCloneFactoryDeploy} from "../lib/LibCloneFactoryDeploy.sol"; +import {LibReleasedSuites} from "../lib/LibReleasedSuites.sol"; + +/// @title CloneFactoryDeploySuites +/// @notice Everything this repo deploys, declared ONCE: the hand-written +/// `clone-factory` candidate below, and the released side read from the +/// generated `LibReleasedSuites`, which `script/Build.sol` emits from the +/// frozen record. +/// +/// It lives in `src/` rather than `test/` because `.soldeerignore` excludes +/// `test/` from the published package, and in a deploy repo the deployment +/// process is the product. +abstract contract CloneFactoryDeploySuites is RainDeploySuitesBase { + /// @inheritdoc RainDeploySuitesBase + function releasedSuites() internal pure override returns (DeploySuite[] memory) { + return LibReleasedSuites.releasedSuites(); + } + + /// @inheritdoc RainDeploySuitesBase + function candidateSuites() internal pure override returns (DeployCandidate[] memory) { + DeployCandidate[] memory candidates = new DeployCandidate[](1); + candidates[0] = cloneFactoryCandidate(); + return candidates; + } + + /// This repo's rolling `CloneFactory` candidate. Named rather than reached + /// by index into `candidateSuites`, because `script/Build.sol` emits the + /// released-suites lib from THIS candidate specifically, and naming it + /// keeps the suite key, the artifact path and the dependency list spelled + /// once. + /// + /// `CloneFactory` reads nothing and calls nothing at construction, so it + /// has no dependency that must already be on chain. + /// @return The candidate. + function cloneFactoryCandidate() internal pure returns (DeployCandidate memory) { + return DeployCandidate({ + snapshot: DeploySuite({ + suite: "clone-factory", + creationCode: CLONE_FACTORY_CREATION_CODE_CANDIDATE, + storedDeployedAddress: LibCloneFactoryDeploy.CLONE_FACTORY_DEPLOYED_ADDRESS, + storedBytecodeHash: LibCloneFactoryDeploy.CLONE_FACTORY_DEPLOYED_CODEHASH, + storedRuntimeCode: CLONE_FACTORY_RUNTIME_CODE_CANDIDATE, + artifactPath: "src/concrete/CloneFactory.sol:CloneFactory", + dependencies: new address[](0) + }), + sourceCreationCode: type(CloneFactory).creationCode + }); + } +} diff --git a/src/abstract/RainDeploySuitesBase.sol b/src/abstract/RainDeploySuitesBase.sol new file mode 100644 index 0000000..d1dc438 --- /dev/null +++ b/src/abstract/RainDeploySuitesBase.sol @@ -0,0 +1,15 @@ +// SPDX-License-Identifier: LicenseRef-DCL-1.0 +// SPDX-FileCopyrightText: Copyright (c) 2020 Rain Open Source Software Ltd +pragma solidity ^0.8.25; + +// Re-exports the deploy-suite declaration from the pinned `rain-deploy` +// package under this repo's own `src/abstract/` path: the generated libs +// under `src/lib/` import `../abstract/RainDeploySuitesBase.sol` — the +// writers emit that relative path, and relative imports never go through +// remappings — so this file makes the path resolve, and holds the one local +// spelling of the package path. +import { + DeployCandidate, + DeploySuite, + RainDeploySuitesBase +} from "rain-deploy-0.1.7/src/abstract/RainDeploySuitesBase.sol"; diff --git a/src/generated/0_1_3/CloneFactory.pointers.sol b/src/generated/0_1_3/CloneFactory.pointers.sol deleted file mode 100644 index 8f125c5..0000000 --- a/src/generated/0_1_3/CloneFactory.pointers.sol +++ /dev/null @@ -1,25 +0,0 @@ -// SPDX-License-Identifier: LicenseRef-DCL-1.0 -// SPDX-FileCopyrightText: Copyright (c) 2020 Rain Open Source Software Ltd -pragma solidity ^0.8.25; - -// THIS FILE IS AUTOGENERATED BY ./script/BuildPointers.sol - -// This file is committed to the repository because there is a circular -// dependency between the contract and its pointers file. The contract -// needs the pointers file to exist so that it can compile, and the pointers -// file needs the contract to exist so that it can be compiled. - -/// @dev Hash of the known bytecode. -bytes32 constant BYTECODE_HASH = bytes32(0xf21b813c7075a1621285df3a8369d0652c31ea80cb807be1aaadafeecd134475); - -/// @dev The deterministic deploy address of the contract when deployed via -/// the Zoltu factory. -address constant DEPLOYED_ADDRESS = address(0x444acC29d63fa643E8adCC35FD9aa6DE111dCb39); - -/// @dev The creation bytecode of the contract. -bytes constant CREATION_CODE = - hex"6080604052348015600e575f80fd5b506103f48061001c5f395ff3fe608060405234801561000f575f80fd5b5060043610610029575f3560e01c80630fbe133c1461002d575b5f80fd5b61004061003b3660046102fb565b610069565b60405173ffffffffffffffffffffffffffffffffffffffff909116815260200160405180910390f35b5f8373ffffffffffffffffffffffffffffffffffffffff163b5f036100ba576040517ff432283200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f6100c485610213565b6040805133815273ffffffffffffffffffffffffffffffffffffffff888116602083015283168183015290519192507f274b5f356634f32a865af65bdc3d8205939d9413d75e1f367652e4f3b24d0c3a919081900360600190a16040517f439fab910000000000000000000000000000000000000000000000000000000081527fe0e57eda3f08f2a93bbe980d3df7f9c315eac41181f58b865a13d917fe769fc39073ffffffffffffffffffffffffffffffffffffffff83169063439fab91906101949088908890600401610391565b6020604051808303815f875af11580156101b0573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906101d491906103dd565b1461020b576040517f19b991a800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b949350505050565b5f61021e825f610224565b92915050565b5f8147101561026c576040517fcf4791810000000000000000000000000000000000000000000000000000000081524760048201526024810183905260440160405180910390fd5b763d602d80600a3d3981f3363d3d373d3d3d363d730000008360601b60e81c175f526e5af43d82803e903d91602b57fd5bf38360781b176020526037600983f0905073ffffffffffffffffffffffffffffffffffffffff811661021e576040517fb06ebf3d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f805f6040848603121561030d575f80fd5b833573ffffffffffffffffffffffffffffffffffffffff81168114610330575f80fd5b9250602084013567ffffffffffffffff8082111561034c575f80fd5b818601915086601f83011261035f575f80fd5b81358181111561036d575f80fd5b87602082850101111561037e575f80fd5b6020830194508093505050509250925092565b60208152816020820152818360408301375f818301604090810191909152601f9092017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0160101919050565b5f602082840312156103ed575f80fd5b505191905056"; - -/// @dev The runtime bytecode of the contract. -bytes constant RUNTIME_CODE = - hex"608060405234801561000f575f80fd5b5060043610610029575f3560e01c80630fbe133c1461002d575b5f80fd5b61004061003b3660046102fb565b610069565b60405173ffffffffffffffffffffffffffffffffffffffff909116815260200160405180910390f35b5f8373ffffffffffffffffffffffffffffffffffffffff163b5f036100ba576040517ff432283200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f6100c485610213565b6040805133815273ffffffffffffffffffffffffffffffffffffffff888116602083015283168183015290519192507f274b5f356634f32a865af65bdc3d8205939d9413d75e1f367652e4f3b24d0c3a919081900360600190a16040517f439fab910000000000000000000000000000000000000000000000000000000081527fe0e57eda3f08f2a93bbe980d3df7f9c315eac41181f58b865a13d917fe769fc39073ffffffffffffffffffffffffffffffffffffffff83169063439fab91906101949088908890600401610391565b6020604051808303815f875af11580156101b0573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906101d491906103dd565b1461020b576040517f19b991a800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b949350505050565b5f61021e825f610224565b92915050565b5f8147101561026c576040517fcf4791810000000000000000000000000000000000000000000000000000000081524760048201526024810183905260440160405180910390fd5b763d602d80600a3d3981f3363d3d373d3d3d363d730000008360601b60e81c175f526e5af43d82803e903d91602b57fd5bf38360781b176020526037600983f0905073ffffffffffffffffffffffffffffffffffffffff811661021e576040517fb06ebf3d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f805f6040848603121561030d575f80fd5b833573ffffffffffffffffffffffffffffffffffffffff81168114610330575f80fd5b9250602084013567ffffffffffffffff8082111561034c575f80fd5b818601915086601f83011261035f575f80fd5b81358181111561036d575f80fd5b87602082850101111561037e575f80fd5b6020830194508093505050509250925092565b60208152816020820152818360408301375f818301604090810191909152601f9092017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0160101919050565b5f602082840312156103ed575f80fd5b505191905056"; diff --git a/src/generated/0_1_4/CloneFactory.pointers.sol b/src/generated/0_1_4/CloneFactory.pointers.sol deleted file mode 100644 index 8f125c5..0000000 --- a/src/generated/0_1_4/CloneFactory.pointers.sol +++ /dev/null @@ -1,25 +0,0 @@ -// SPDX-License-Identifier: LicenseRef-DCL-1.0 -// SPDX-FileCopyrightText: Copyright (c) 2020 Rain Open Source Software Ltd -pragma solidity ^0.8.25; - -// THIS FILE IS AUTOGENERATED BY ./script/BuildPointers.sol - -// This file is committed to the repository because there is a circular -// dependency between the contract and its pointers file. The contract -// needs the pointers file to exist so that it can compile, and the pointers -// file needs the contract to exist so that it can be compiled. - -/// @dev Hash of the known bytecode. -bytes32 constant BYTECODE_HASH = bytes32(0xf21b813c7075a1621285df3a8369d0652c31ea80cb807be1aaadafeecd134475); - -/// @dev The deterministic deploy address of the contract when deployed via -/// the Zoltu factory. -address constant DEPLOYED_ADDRESS = address(0x444acC29d63fa643E8adCC35FD9aa6DE111dCb39); - -/// @dev The creation bytecode of the contract. -bytes constant CREATION_CODE = - hex"6080604052348015600e575f80fd5b506103f48061001c5f395ff3fe608060405234801561000f575f80fd5b5060043610610029575f3560e01c80630fbe133c1461002d575b5f80fd5b61004061003b3660046102fb565b610069565b60405173ffffffffffffffffffffffffffffffffffffffff909116815260200160405180910390f35b5f8373ffffffffffffffffffffffffffffffffffffffff163b5f036100ba576040517ff432283200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f6100c485610213565b6040805133815273ffffffffffffffffffffffffffffffffffffffff888116602083015283168183015290519192507f274b5f356634f32a865af65bdc3d8205939d9413d75e1f367652e4f3b24d0c3a919081900360600190a16040517f439fab910000000000000000000000000000000000000000000000000000000081527fe0e57eda3f08f2a93bbe980d3df7f9c315eac41181f58b865a13d917fe769fc39073ffffffffffffffffffffffffffffffffffffffff83169063439fab91906101949088908890600401610391565b6020604051808303815f875af11580156101b0573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906101d491906103dd565b1461020b576040517f19b991a800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b949350505050565b5f61021e825f610224565b92915050565b5f8147101561026c576040517fcf4791810000000000000000000000000000000000000000000000000000000081524760048201526024810183905260440160405180910390fd5b763d602d80600a3d3981f3363d3d373d3d3d363d730000008360601b60e81c175f526e5af43d82803e903d91602b57fd5bf38360781b176020526037600983f0905073ffffffffffffffffffffffffffffffffffffffff811661021e576040517fb06ebf3d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f805f6040848603121561030d575f80fd5b833573ffffffffffffffffffffffffffffffffffffffff81168114610330575f80fd5b9250602084013567ffffffffffffffff8082111561034c575f80fd5b818601915086601f83011261035f575f80fd5b81358181111561036d575f80fd5b87602082850101111561037e575f80fd5b6020830194508093505050509250925092565b60208152816020820152818360408301375f818301604090810191909152601f9092017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0160101919050565b5f602082840312156103ed575f80fd5b505191905056"; - -/// @dev The runtime bytecode of the contract. -bytes constant RUNTIME_CODE = - hex"608060405234801561000f575f80fd5b5060043610610029575f3560e01c80630fbe133c1461002d575b5f80fd5b61004061003b3660046102fb565b610069565b60405173ffffffffffffffffffffffffffffffffffffffff909116815260200160405180910390f35b5f8373ffffffffffffffffffffffffffffffffffffffff163b5f036100ba576040517ff432283200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f6100c485610213565b6040805133815273ffffffffffffffffffffffffffffffffffffffff888116602083015283168183015290519192507f274b5f356634f32a865af65bdc3d8205939d9413d75e1f367652e4f3b24d0c3a919081900360600190a16040517f439fab910000000000000000000000000000000000000000000000000000000081527fe0e57eda3f08f2a93bbe980d3df7f9c315eac41181f58b865a13d917fe769fc39073ffffffffffffffffffffffffffffffffffffffff83169063439fab91906101949088908890600401610391565b6020604051808303815f875af11580156101b0573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906101d491906103dd565b1461020b576040517f19b991a800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b949350505050565b5f61021e825f610224565b92915050565b5f8147101561026c576040517fcf4791810000000000000000000000000000000000000000000000000000000081524760048201526024810183905260440160405180910390fd5b763d602d80600a3d3981f3363d3d373d3d3d363d730000008360601b60e81c175f526e5af43d82803e903d91602b57fd5bf38360781b176020526037600983f0905073ffffffffffffffffffffffffffffffffffffffff811661021e576040517fb06ebf3d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f805f6040848603121561030d575f80fd5b833573ffffffffffffffffffffffffffffffffffffffff81168114610330575f80fd5b9250602084013567ffffffffffffffff8082111561034c575f80fd5b818601915086601f83011261035f575f80fd5b81358181111561036d575f80fd5b87602082850101111561037e575f80fd5b6020830194508093505050509250925092565b60208152816020820152818360408301375f818301604090810191909152601f9092017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0160101919050565b5f602082840312156103ed575f80fd5b505191905056"; diff --git a/src/generated/0_1_5/CloneFactory.pointers.sol b/src/generated/candidate/CloneFactory.sol similarity index 93% rename from src/generated/0_1_5/CloneFactory.pointers.sol rename to src/generated/candidate/CloneFactory.sol index f0b9c14..363974b 100644 --- a/src/generated/0_1_5/CloneFactory.pointers.sol +++ b/src/generated/candidate/CloneFactory.sol @@ -2,12 +2,7 @@ // SPDX-FileCopyrightText: Copyright (c) 2020 Rain Open Source Software Ltd pragma solidity ^0.8.25; -// THIS FILE IS AUTOGENERATED BY ./script/BuildPointers.sol - -// This file is committed to the repository because there is a circular -// dependency between the contract and its pointers file. The contract -// needs the pointers file to exist so that it can compile, and the pointers -// file needs the contract to exist so that it can be compiled. +// THIS FILE IS AUTOGENERATED BY THE BUILD SCRIPT. DO NOT EDIT BY HAND. /// @dev Hash of the known bytecode. bytes32 constant BYTECODE_HASH = bytes32(0x3ec02e2f27d73f603f5b37a7dc228956004e9154c6d1f9c5335bd60036379d00); @@ -23,3 +18,9 @@ bytes constant CREATION_CODE = /// @dev The runtime bytecode of the contract. bytes constant RUNTIME_CODE = hex"608060405234801561000f575f80fd5b5060043610610034575f3560e01c806340419eec1461003857806393a7e71114610074575b5f80fd5b61004b6100463660046103f1565b610087565b60405173ffffffffffffffffffffffffffffffffffffffff909116815260200160405180910390f35b61004b610082366004610473565b6100c7565b5f61009185610159565b5f6100ae866100a933865f9182526020526040902090565b6101ac565b90506100bd86828787876101b8565b9695505050505050565b5f61014f846100df84865f9182526020526040902090565b6040513060388201526f5af43d82803e903d91602b57fd5bf3ff60248201526014810192909252733d602d80600a3d3981f3363d3d373d3d3d363d73825260588201526037600c8201206078820152605560439091012073ffffffffffffffffffffffffffffffffffffffff1690565b90505b9392505050565b8073ffffffffffffffffffffffffffffffffffffffff163b5f036101a9576040517ff432283200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b50565b5f61015283835f6102f1565b5f7f3b3e5b48cfaf4a4b3b2b36425ddec21c2c507fd502d2aea55eb7ffa36b0ca2053387878588886040516101f2969594939291906104f3565b60405180910390a16040517f439fab910000000000000000000000000000000000000000000000000000000081527fe0e57eda3f08f2a93bbe980d3df7f9c315eac41181f58b865a13d917fe769fc39073ffffffffffffffffffffffffffffffffffffffff87169063439fab91906102709088908890600401610546565b6020604051808303815f875af115801561028c573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906102b09190610559565b146102e7576040517f19b991a800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5092949350505050565b5f81471015610339576040517fcf4791810000000000000000000000000000000000000000000000000000000081524760048201526024810183905260440160405180910390fd5b763d602d80600a3d3981f3363d3d373d3d3d363d730000008460601b60e81c175f526e5af43d82803e903d91602b57fd5bf38460781b17602052826037600984f5905073ffffffffffffffffffffffffffffffffffffffff8116610152576040517fb06ebf3d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b803573ffffffffffffffffffffffffffffffffffffffff811681146103ec575f80fd5b919050565b5f805f8060608587031215610404575f80fd5b61040d856103c9565b9350602085013567ffffffffffffffff80821115610429575f80fd5b818701915087601f83011261043c575f80fd5b81358181111561044a575f80fd5b88602082850101111561045b575f80fd5b95986020929092019750949560400135945092505050565b5f805f60608486031215610485575f80fd5b61048e846103c9565b9250602084013591506104a3604085016103c9565b90509250925092565b81835281816020850137505f602082840101525f60207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f840116840101905092915050565b5f73ffffffffffffffffffffffffffffffffffffffff8089168352808816602084015280871660408401525084606083015260a0608083015261053a60a0830184866104ac565b98975050505050505050565b602081525f61014f6020830184866104ac565b5f60208284031215610569575f80fd5b505191905056"; + +/// @dev The addresses that MUST already have code on a network before +/// this release can be broadcast there, `abi.encode`d as an `address[]` +/// because Solidity has no file-scope constant of dynamic array type. +bytes constant DEPENDENCIES = + hex"00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000"; diff --git a/src/lib/LibCloneFactoryDeploy.sol b/src/lib/LibCloneFactoryDeploy.sol index d63d7ef..5c4a3a9 100644 --- a/src/lib/LibCloneFactoryDeploy.sol +++ b/src/lib/LibCloneFactoryDeploy.sol @@ -2,19 +2,18 @@ // SPDX-FileCopyrightText: Copyright (c) 2020 Rain Open Source Software Ltd pragma solidity ^0.8.25; -// THIS FILE IS AUTOGENERATED BY ./script/BuildPointers.sol +// THIS FILE IS AUTOGENERATED BY THE BUILD SCRIPT. DO NOT EDIT BY HAND. import { DEPLOYED_ADDRESS as CLONE_FACTORY_ADDR, BYTECODE_HASH as CLONE_FACTORY_HASH -} from "../generated/0_1_5/CloneFactory.pointers.sol"; +} from "../generated/candidate/CloneFactory.sol"; /// @title LibCloneFactoryDeploy -/// @notice The deterministic Zoltu deploy address and code hash of the current -/// `CloneFactory` release, aliased from the frozen per-release snapshot in -/// `src/generated//CloneFactory.pointers.sol` so that snapshot stays the -/// single source of truth. Lets consumers verify/deploy against a precommitted -/// address + hash rather than a registry. +/// @notice The deterministic Zoltu deploy address and code hash of +/// `CloneFactory`, aliased from its generated snapshot so that snapshot stays the +/// single source of truth. The import path never moves, so consumers are +/// unaffected by which snapshot it names. library LibCloneFactoryDeploy { address constant CLONE_FACTORY_DEPLOYED_ADDRESS = CLONE_FACTORY_ADDR; bytes32 constant CLONE_FACTORY_DEPLOYED_CODEHASH = CLONE_FACTORY_HASH; diff --git a/src/lib/LibCloneFactoryReleased.sol b/src/lib/LibCloneFactoryReleased.sol new file mode 100644 index 0000000..a196f50 --- /dev/null +++ b/src/lib/LibCloneFactoryReleased.sol @@ -0,0 +1,32 @@ +// SPDX-License-Identifier: LicenseRef-DCL-1.0 +// SPDX-FileCopyrightText: Copyright (c) 2020 Rain Open Source Software Ltd +pragma solidity ^0.8.25; + +// THIS FILE IS AUTOGENERATED BY THE BUILD SCRIPT. DO NOT EDIT BY HAND. + +import {DeploySuite} from "../abstract/RainDeploySuitesBase.sol"; + +/// @title LibCloneFactoryReleased +/// @notice Every frozen release of `CloneFactory`: one entry per file in +/// the append-only `src/generated//` record, in tag order. +/// +/// The deploy address, code hash, creation code, runtime code and dependency +/// list of each entry are aliased from that release's own frozen snapshot, so +/// what a release deployed, and what it required to already be on chain, are +/// read from the immutable file and from nowhere else. A dependency dropped +/// from current source stays required by the releases cut with it, and one +/// added is not imposed on releases cut without it. +/// +/// The key and the artifact path are explorer and ordering metadata +/// regenerated from the CURRENT declaration, and are not part of that +/// record. A moved source path retroactively updates every entry's artifact +/// path, which is intended: the alternative is parsing this generated file +/// back in to preserve what it last said. +library LibCloneFactoryReleased { + /// Every frozen release, in tag order. + /// @return The released suites. + function releasedSuites() internal pure returns (DeploySuite[] memory) { + DeploySuite[] memory suites = new DeploySuite[](0); + return suites; + } +} diff --git a/src/lib/LibReleasedSuites.sol b/src/lib/LibReleasedSuites.sol new file mode 100644 index 0000000..328db0c --- /dev/null +++ b/src/lib/LibReleasedSuites.sol @@ -0,0 +1,48 @@ +// SPDX-License-Identifier: LicenseRef-DCL-1.0 +// SPDX-FileCopyrightText: Copyright (c) 2020 Rain Open Source Software Ltd +pragma solidity ^0.8.25; + +// THIS FILE IS AUTOGENERATED BY THE BUILD SCRIPT. DO NOT EDIT BY HAND. + +import {DeploySuite} from "../abstract/RainDeploySuitesBase.sol"; + +import {LibCloneFactoryReleased} from "./LibCloneFactoryReleased.sol"; + +/// @title LibReleasedSuites +/// @notice Every frozen release this repo has cut, of every contract it +/// deploys: the per-contract released libs concatenated, in declaration +/// order. +/// +/// There is one released lib per deployed contract because a release freezes +/// every contract it names into a single tag directory, so a lib that took +/// the record whole would give another contract's snapshot this contract's +/// suite key and collide with its own entry for that tag. This is where +/// those libs meet, and it is emitted from the same list that wrote them -- +/// so a contract that is generated, aliased and frozen cannot be missing +/// from the declaration, and a release missing from the declaration is a +/// release every check quietly stops asking about. +library LibReleasedSuites { + /// Every released suite, in declaration order. + /// @return The released suites. + function releasedSuites() internal pure returns (DeploySuite[] memory) { + DeploySuite[][] memory released = new DeploySuite[][](1); + released[0] = LibCloneFactoryReleased.releasedSuites(); + + uint256 total = 0; + for (uint256 i = 0; i < released.length; i++) { + total += released[i].length; + } + + DeploySuite[] memory suites = new DeploySuite[](total); + + uint256 offset = 0; + for (uint256 i = 0; i < released.length; i++) { + for (uint256 j = 0; j < released[i].length; j++) { + suites[offset + j] = released[i][j]; + } + offset += released[i].length; + } + + return suites; + } +} diff --git a/test/src/abstract/CloneFactoryDeployChain.t.sol b/test/src/abstract/CloneFactoryDeployChain.t.sol new file mode 100644 index 0000000..8e5d9f3 --- /dev/null +++ b/test/src/abstract/CloneFactoryDeployChain.t.sol @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: LicenseRef-DCL-1.0 +// SPDX-FileCopyrightText: Copyright (c) 2020 Rain Open Source Software Ltd +pragma solidity =0.8.25; + +import {RainDeployVerifyChain} from "rain-deploy-0.1.7/src/abstract/RainDeployVerifyChain.sol"; +import {CloneFactoryDeploySuites} from "src/abstract/CloneFactoryDeploySuites.sol"; + +/// @title CloneFactoryDeployChainTest +/// @notice Binds this repo's declaration to `RainDeployVerifyChain`: every +/// `CloneFactory` release is live, with the code it froze, on every supported +/// network. +contract CloneFactoryDeployChainTest is CloneFactoryDeploySuites, RainDeployVerifyChain {} diff --git a/test/src/abstract/CloneFactoryDeploySnapshot.t.sol b/test/src/abstract/CloneFactoryDeploySnapshot.t.sol new file mode 100644 index 0000000..fa3fb72 --- /dev/null +++ b/test/src/abstract/CloneFactoryDeploySnapshot.t.sol @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: LicenseRef-DCL-1.0 +// SPDX-FileCopyrightText: Copyright (c) 2020 Rain Open Source Software Ltd +pragma solidity =0.8.25; + +import {RainDeployVerifySnapshot} from "rain-deploy-0.1.7/src/abstract/RainDeployVerifySnapshot.sol"; +import {CloneFactoryDeploySuites} from "src/abstract/CloneFactoryDeploySuites.sol"; + +/// @title CloneFactoryDeploySnapshotTest +/// @notice Binds this repo's declaration to `RainDeployVerifySnapshot`: every +/// deploy-pin assertion over the `CloneFactory` suites that needs no network. +contract CloneFactoryDeploySnapshotTest is CloneFactoryDeploySuites, RainDeployVerifySnapshot {} diff --git a/test/src/concrete/CloneFactoryCloneDeterministic.t.sol b/test/src/concrete/CloneFactoryCloneDeterministic.t.sol index bf8572e..f6db10c 100644 --- a/test/src/concrete/CloneFactoryCloneDeterministic.t.sol +++ b/test/src/concrete/CloneFactoryCloneDeterministic.t.sol @@ -2,7 +2,7 @@ // SPDX-FileCopyrightText: Copyright (c) 2020 Rain Open Source Software Ltd pragma solidity =0.8.25; -import {Test, Vm} from "forge-std-1.16.1/src/Test.sol"; +import {Test, Vm} from "forge-std-1.16.2/src/Test.sol"; import {Clones} from "@openzeppelin-contracts-5.6.1/proxy/Clones.sol"; import {LibExtrospectERC1167Proxy} from "rain-extrospection-0.1.1/src/lib/LibExtrospectERC1167Proxy.sol"; diff --git a/test/src/lib/LibCloneFactoryDeploy.t.sol b/test/src/lib/LibCloneFactoryDeploy.t.sol index c319fec..f44b37d 100644 --- a/test/src/lib/LibCloneFactoryDeploy.t.sol +++ b/test/src/lib/LibCloneFactoryDeploy.t.sol @@ -2,8 +2,8 @@ // SPDX-FileCopyrightText: Copyright (c) 2020 Rain Open Source Software Ltd pragma solidity =0.8.25; -import {Test} from "forge-std-1.16.1/src/Test.sol"; -import {LibRainDeploy} from "rain-deploy-0.1.3/src/lib/LibRainDeploy.sol"; +import {Test} from "forge-std-1.16.2/src/Test.sol"; +import {LibRainDeploy} from "rain-deploy-0.1.7/src/lib/LibRainDeploy.sol"; import {LibCloneFactoryDeploy} from "../../../src/lib/LibCloneFactoryDeploy.sol"; import {CloneFactory} from "../../../src/concrete/CloneFactory.sol"; diff --git a/test/src/lib/LibCloneFactoryDeployCandidate.t.sol b/test/src/lib/LibCloneFactoryDeployCandidate.t.sol new file mode 100644 index 0000000..3a26683 --- /dev/null +++ b/test/src/lib/LibCloneFactoryDeployCandidate.t.sol @@ -0,0 +1,67 @@ +// SPDX-License-Identifier: LicenseRef-DCL-1.0 +// SPDX-FileCopyrightText: Copyright (c) 2020 Rain Open Source Software Ltd +pragma solidity =0.8.25; + +import {Test} from "forge-std-1.16.2/src/Test.sol"; +import {LibRainDeploy} from "rain-deploy-0.1.7/src/lib/LibRainDeploy.sol"; +import { + BYTECODE_HASH, + DEPLOYED_ADDRESS, + CREATION_CODE, + RUNTIME_CODE, + DEPENDENCIES +} from "../../../src/generated/candidate/CloneFactory.sol"; +import {LibCloneFactoryDeploy} from "../../../src/lib/LibCloneFactoryDeploy.sol"; +import {CloneFactory} from "../../../src/concrete/CloneFactory.sol"; + +/// @title LibCloneFactoryDeployCandidateTest +/// @notice The rolling `src/generated/candidate/CloneFactory.sol` snapshot and +/// the `LibCloneFactoryDeploy` alias must stay consistent end to end: +/// SOURCE (`type(CloneFactory).creationCode`) -> CANDIDATE pins -> ALIAS. CI +/// currency-checks that `forge script ./script/Build.sol` regenerates the +/// candidate and the alias byte-identically; these assertions pin what that +/// regeneration must hold true. +contract LibCloneFactoryDeployCandidateTest is Test { + /// SOURCE -> CANDIDATE: the candidate records THIS repo's current + /// `CloneFactory` creation code, so the pins describe what source compiles + /// to rather than a stale snapshot. + function testCandidateCreationCodeMatchesSource() external pure { + assertEq(CREATION_CODE, type(CloneFactory).creationCode, "candidate creation code is not current source"); + } + + /// CANDIDATE self-consistency: Zoltu-deploying the recorded `CREATION_CODE` + /// lands at the recorded `DEPLOYED_ADDRESS` with the recorded runtime code + /// and code hash, and `keccak256(RUNTIME_CODE)` is that hash — the snapshot + /// reproduces its own deployment. + function testCandidateReproducesItsDeployment() external { + LibRainDeploy.etchZoltuFactory(vm); + address deployed = LibRainDeploy.deployZoltu(CREATION_CODE); + assertEq(deployed, DEPLOYED_ADDRESS, "creation code deploys to a different address"); + assertEq(deployed.code, RUNTIME_CODE, "deployed runtime code is not the recorded runtime code"); + assertEq(deployed.codehash, BYTECODE_HASH, "deployed code hash is not the recorded hash"); + assertEq(keccak256(RUNTIME_CODE), BYTECODE_HASH, "recorded runtime code does not hash to the recorded hash"); + } + + /// CANDIDATE -> ALIAS: the shipped `LibCloneFactoryDeploy` re-exports the + /// candidate's address and code hash unchanged, so consumers pin the + /// candidate through a stable import path. + function testAliasReExportsCandidate() external pure { + assertEq( + LibCloneFactoryDeploy.CLONE_FACTORY_DEPLOYED_ADDRESS, + DEPLOYED_ADDRESS, + "alias address is not the candidate address" + ); + assertEq( + LibCloneFactoryDeploy.CLONE_FACTORY_DEPLOYED_CODEHASH, + BYTECODE_HASH, + "alias code hash is not the candidate hash" + ); + } + + /// `CloneFactory` has no contracts that must already have code on a network + /// before it can be broadcast, so the recorded dependency list is empty. + function testCandidateHasNoDependencies() external pure { + address[] memory dependencies = abi.decode(DEPENDENCIES, (address[])); + assertEq(dependencies.length, 0, "candidate records unexpected deploy dependencies"); + } +} diff --git a/test/src/lib/LibCloneFactoryDeployProd.t.sol b/test/src/lib/LibCloneFactoryDeployProd.t.sol index ac4c5ba..af4fe6b 100644 --- a/test/src/lib/LibCloneFactoryDeployProd.t.sol +++ b/test/src/lib/LibCloneFactoryDeployProd.t.sol @@ -2,8 +2,8 @@ // SPDX-FileCopyrightText: Copyright (c) 2020 Rain Open Source Software Ltd pragma solidity =0.8.25; -import {Test} from "forge-std-1.16.1/src/Test.sol"; -import {LibRainDeploy} from "rain-deploy-0.1.3/src/lib/LibRainDeploy.sol"; +import {Test} from "forge-std-1.16.2/src/Test.sol"; +import {LibRainDeploy} from "rain-deploy-0.1.7/src/lib/LibRainDeploy.sol"; import {LibCloneFactoryDeploy} from "../../../src/lib/LibCloneFactoryDeploy.sol"; /// @title LibCloneFactoryDeployProdTest diff --git a/test/src/lib/LibCloneFactoryDeployTaggedConstants.t.sol b/test/src/lib/LibCloneFactoryDeployTaggedConstants.t.sol deleted file mode 100644 index a13cb31..0000000 --- a/test/src/lib/LibCloneFactoryDeployTaggedConstants.t.sol +++ /dev/null @@ -1,83 +0,0 @@ -// SPDX-License-Identifier: LicenseRef-DCL-1.0 -// SPDX-FileCopyrightText: Copyright (c) 2020 Rain Open Source Software Ltd -pragma solidity =0.8.25; - -import {Test} from "forge-std-1.16.1/src/Test.sol"; -import {LibRainDeploy} from "rain-deploy-0.1.3/src/lib/LibRainDeploy.sol"; -import { - BYTECODE_HASH as CLONE_FACTORY_BYTECODE_HASH_0_1_3, - DEPLOYED_ADDRESS as CLONE_FACTORY_DEPLOYED_ADDRESS_0_1_3, - CREATION_CODE as CLONE_FACTORY_CREATION_CODE_0_1_3, - RUNTIME_CODE as CLONE_FACTORY_RUNTIME_CODE_0_1_3 -} from "../../../src/generated/0_1_3/CloneFactory.pointers.sol"; -import { - BYTECODE_HASH as CLONE_FACTORY_BYTECODE_HASH_0_1_4, - DEPLOYED_ADDRESS as CLONE_FACTORY_DEPLOYED_ADDRESS_0_1_4, - CREATION_CODE as CLONE_FACTORY_CREATION_CODE_0_1_4, - RUNTIME_CODE as CLONE_FACTORY_RUNTIME_CODE_0_1_4 -} from "../../../src/generated/0_1_4/CloneFactory.pointers.sol"; -import { - BYTECODE_HASH as CLONE_FACTORY_BYTECODE_HASH_0_1_5, - DEPLOYED_ADDRESS as CLONE_FACTORY_DEPLOYED_ADDRESS_0_1_5, - CREATION_CODE as CLONE_FACTORY_CREATION_CODE_0_1_5, - RUNTIME_CODE as CLONE_FACTORY_RUNTIME_CODE_0_1_5 -} from "../../../src/generated/0_1_5/CloneFactory.pointers.sol"; - -/// @title LibCloneFactoryDeployTaggedConstantsTest -/// @notice Each frozen per-tag `CloneFactory` snapshot must be self-consistent -/// and reproducible: its recorded `BYTECODE_HASH` is the keccak of its recorded -/// `RUNTIME_CODE`, and Zoltu-deploying its recorded `CREATION_CODE` lands at the -/// recorded `DEPLOYED_ADDRESS` with the recorded codehash. A new release adds a -/// tag import + a test pair here. -contract LibCloneFactoryDeployTaggedConstantsTest is Test { - /// `keccak256(RUNTIME_CODE) == BYTECODE_HASH` for the tag — the pin is - /// internally consistent. - function testCloneFactory_0_1_3_RuntimeHashesToBytecodeHash() external pure { - assertEq(keccak256(CLONE_FACTORY_RUNTIME_CODE_0_1_3), CLONE_FACTORY_BYTECODE_HASH_0_1_3); - } - - /// Deploying the frozen `CREATION_CODE` via the Zoltu factory lands at the - /// recorded `DEPLOYED_ADDRESS` with the recorded codehash — the snapshot - /// reproduces its own deployment. - function testCloneFactory_0_1_3_CreationDeploysToPinnedAddress() external { - LibRainDeploy.etchZoltuFactory(vm); - address deployed = LibRainDeploy.deployZoltu(CLONE_FACTORY_CREATION_CODE_0_1_3); - assertEq(deployed, CLONE_FACTORY_DEPLOYED_ADDRESS_0_1_3); - assertEq(deployed.codehash, CLONE_FACTORY_BYTECODE_HASH_0_1_3); - assertEq(keccak256(deployed.code), CLONE_FACTORY_BYTECODE_HASH_0_1_3); - } - - /// `keccak256(RUNTIME_CODE) == BYTECODE_HASH` for the tag — the pin is - /// internally consistent. - function testCloneFactory_0_1_4_RuntimeHashesToBytecodeHash() external pure { - assertEq(keccak256(CLONE_FACTORY_RUNTIME_CODE_0_1_4), CLONE_FACTORY_BYTECODE_HASH_0_1_4); - } - - /// Deploying the frozen `CREATION_CODE` via the Zoltu factory lands at the - /// recorded `DEPLOYED_ADDRESS` with the recorded codehash — the snapshot - /// reproduces its own deployment. - function testCloneFactory_0_1_4_CreationDeploysToPinnedAddress() external { - LibRainDeploy.etchZoltuFactory(vm); - address deployed = LibRainDeploy.deployZoltu(CLONE_FACTORY_CREATION_CODE_0_1_4); - assertEq(deployed, CLONE_FACTORY_DEPLOYED_ADDRESS_0_1_4); - assertEq(deployed.codehash, CLONE_FACTORY_BYTECODE_HASH_0_1_4); - assertEq(keccak256(deployed.code), CLONE_FACTORY_BYTECODE_HASH_0_1_4); - } - - /// `keccak256(RUNTIME_CODE) == BYTECODE_HASH` for the tag — the pin is - /// internally consistent. - function testCloneFactory_0_1_5_RuntimeHashesToBytecodeHash() external pure { - assertEq(keccak256(CLONE_FACTORY_RUNTIME_CODE_0_1_5), CLONE_FACTORY_BYTECODE_HASH_0_1_5); - } - - /// Deploying the frozen `CREATION_CODE` via the Zoltu factory lands at the - /// recorded `DEPLOYED_ADDRESS` with the recorded codehash — the snapshot - /// reproduces its own deployment. - function testCloneFactory_0_1_5_CreationDeploysToPinnedAddress() external { - LibRainDeploy.etchZoltuFactory(vm); - address deployed = LibRainDeploy.deployZoltu(CLONE_FACTORY_CREATION_CODE_0_1_5); - assertEq(deployed, CLONE_FACTORY_DEPLOYED_ADDRESS_0_1_5); - assertEq(deployed.codehash, CLONE_FACTORY_BYTECODE_HASH_0_1_5); - assertEq(keccak256(deployed.code), CLONE_FACTORY_BYTECODE_HASH_0_1_5); - } -}