diff --git a/script/Build.sol b/script/Build.sol index 2181640..70a5132 100644 --- a/script/Build.sol +++ b/script/Build.sol @@ -40,9 +40,9 @@ struct GeneratedContract { /// lib writers and the freeze. contract Build is Script, RegistryDeploySuites { /// Every contract this repo generates deploy pins for. - /// @return contracts The generated contracts. - function generatedContracts() internal pure returns (GeneratedContract[] memory contracts) { - contracts = new GeneratedContract[](2); + /// @return The generated contracts. + function generatedContracts() internal pure returns (GeneratedContract[] memory) { + GeneratedContract[] memory contracts = new GeneratedContract[](2); contracts[0] = GeneratedContract({ contractName: "AddressRegistry", constantPrefix: "ADDRESS_REGISTRY", candidate: addressRegistryCandidate() }); @@ -51,17 +51,19 @@ contract Build is Script, RegistryDeploySuites { constantPrefix: "MIGRATION_REGISTRY", candidate: migrationRegistryCandidate() }); + return contracts; } /// Every generated contract's name, in declaration order — the order the /// aggregate emits its entries in. Read by the freeze and the aggregate. - /// @return names The contract names. - function generatedContractNames() internal pure returns (string[] memory names) { + /// @return The contract names. + function generatedContractNames() internal pure returns (string[] memory) { GeneratedContract[] memory contracts = generatedContracts(); - names = new string[](contracts.length); + string[] memory names = new string[](contracts.length); for (uint256 i = 0; i < contracts.length; i++) { names[i] = contracts[i].contractName; } + return names; } /// @notice Regenerate the rolling snapshots, their alias libs, the diff --git a/src/abstract/RainDeploySuitesBase.sol b/src/abstract/RainDeploySuitesBase.sol index 54e831d..6308fad 100644 --- a/src/abstract/RainDeploySuitesBase.sol +++ b/src/abstract/RainDeploySuitesBase.sol @@ -168,12 +168,13 @@ abstract contract RainDeploySuitesBase { /// Guarding each reader separately would be two spellings of one rule, and /// the reader that got the second spelling wrong is the one that silently /// stops asserting. - /// @return candidates The candidates. - function checkedCandidateSuites() internal pure returns (DeployCandidate[] memory candidates) { - candidates = candidateSuites(); + /// @return The candidates. + function checkedCandidateSuites() internal pure returns (DeployCandidate[] memory) { + DeployCandidate[] memory candidates = candidateSuites(); if (candidates.length == 0) { revert NoDeployCandidates(); } + return candidates; } /// EVERY candidate MUST record the creation code this repo compiles. @@ -232,12 +233,12 @@ abstract contract RainDeploySuitesBase { /// One pairwise pass over the whole set, so a candidate colliding with /// another candidate is caught by the same code that catches a candidate /// colliding with a release — there is no second rule to keep in step. - /// @return suites Every declared suite. - function allSuites() internal pure returns (DeploySuite[] memory suites) { + /// @return Every declared suite. + function allSuites() internal pure returns (DeploySuite[] memory) { DeploySuite[] memory released = releasedSuites(); DeployCandidate[] memory candidates = checkedCandidateSuites(); - suites = new DeploySuite[](released.length + candidates.length); + DeploySuite[] memory suites = new DeploySuite[](released.length + candidates.length); for (uint256 i = 0; i < released.length; i++) { suites[i] = released[i]; } @@ -252,15 +253,19 @@ abstract contract RainDeploySuitesBase { } } } + + return suites; } /// Every declared key, comma separated, for the unknown-suite error. - /// @return names The declared keys. - function suiteNames() internal pure returns (string memory names) { + /// @return The declared keys. + function suiteNames() internal pure returns (string memory) { DeploySuite[] memory suites = allSuites(); + string memory names; for (uint256 i = 0; i < suites.length; i++) { names = i == 0 ? suites[i].suite : string.concat(names, ", ", suites[i].suite); } + return names; } /// The suite a key selects. diff --git a/src/abstract/RainDeployVerifyBase.sol b/src/abstract/RainDeployVerifyBase.sol index 81b57b4..1c87ab5 100644 --- a/src/abstract/RainDeployVerifyBase.sol +++ b/src/abstract/RainDeployVerifyBase.sol @@ -89,8 +89,8 @@ abstract contract RainDeployVerifyBase is RainDeploySuitesBase, Test { /// against itself, and every chain would pass whether or not anything is /// deployed there. /// @param suite The suite to derive from. - /// @return derived The address and code hash the creation code produces. - function deriveDeployment(DeploySuite memory suite) internal returns (DerivedDeploy memory derived) { + /// @return The address and code hash the creation code produces. + function deriveDeployment(DeploySuite memory suite) internal returns (DerivedDeploy memory) { address formulaAddress = LibRainDeploy.zoltuAddress(suite.creationCode); uint256 snapshotId = vm.snapshotState(); @@ -107,7 +107,7 @@ abstract contract RainDeployVerifyBase is RainDeploySuitesBase, Test { revert ZoltuDerivationMismatch(suite.suite, formulaAddress, factoryAddress); } - derived = + DerivedDeploy memory derived = DerivedDeploy({suite: suite.suite, deployedAddress: formulaAddress, bytecodeHash: factoryAddress.codehash}); // A failed revert is unrecoverable, not a warning to silence. The etch @@ -117,6 +117,8 @@ abstract contract RainDeployVerifyBase is RainDeploySuitesBase, Test { if (!vm.revertToState(snapshotId)) { revert DerivationSnapshotRevertFailed(suite.suite, snapshotId); } + + return derived; } /// Derives every suite once, before anything forks. Callers that compare @@ -124,11 +126,12 @@ abstract contract RainDeployVerifyBase is RainDeploySuitesBase, Test { /// EVM, because on a fork the derived address is exactly the address the /// deployment under test occupies. /// @param suites The suites to derive. - /// @return derived The derivation of each, positionally paired. - function deriveDeployments(DeploySuite[] memory suites) internal returns (DerivedDeploy[] memory derived) { - derived = new DerivedDeploy[](suites.length); + /// @return The derivation of each, positionally paired. + function deriveDeployments(DeploySuite[] memory suites) internal returns (DerivedDeploy[] memory) { + DerivedDeploy[] memory derived = new DerivedDeploy[](suites.length); for (uint256 i = 0; i < suites.length; i++) { derived[i] = deriveDeployment(suites[i]); } + return derived; } } diff --git a/src/abstract/RegistryDeploySuites.sol b/src/abstract/RegistryDeploySuites.sol index 81a929f..56f679b 100644 --- a/src/abstract/RegistryDeploySuites.sol +++ b/src/abstract/RegistryDeploySuites.sol @@ -82,10 +82,11 @@ abstract contract RegistryDeploySuites is RainDeploySuitesBase { /// fourth place to edit: `script/Build.sol` generates the per-contract /// released lib AND the aggregate `releasedSuites()` reads from that one /// list. - function candidateSuites() internal pure override returns (DeployCandidate[] memory candidates) { - candidates = new DeployCandidate[](2); + function candidateSuites() internal pure override returns (DeployCandidate[] memory) { + DeployCandidate[] memory candidates = new DeployCandidate[](2); candidates[0] = addressRegistryCandidate(); candidates[1] = migrationRegistryCandidate(); + return candidates; } /// This repo's rolling `AddressRegistry` candidate. diff --git a/src/concrete/AddressRegistry.sol b/src/concrete/AddressRegistry.sol index 33a44a7..1017fe2 100644 --- a/src/concrete/AddressRegistry.sol +++ b/src/concrete/AddressRegistry.sol @@ -69,10 +69,11 @@ contract AddressRegistry is IAddressRegistryV1 { /// @dev Returns whatever root has bound most recently. A caller that needs /// an answer that cannot move reads once and stores it, which is what a /// consumer resolving a name in its constructor does. - function get(bytes32 name) external view returns (address account) { - account = sAddresses[name]; + function get(bytes32 name) external view returns (address) { + address account = sAddresses[name]; if (account == address(0)) { revert NameNotRegistered(name); } + return account; } } diff --git a/src/generated/candidate/AddressRegistry.sol b/src/generated/candidate/AddressRegistry.sol index 20eb338..a23e8bc 100644 --- a/src/generated/candidate/AddressRegistry.sol +++ b/src/generated/candidate/AddressRegistry.sol @@ -5,19 +5,19 @@ pragma solidity ^0.8.25; // THIS FILE IS AUTOGENERATED BY THE BUILD SCRIPT. DO NOT EDIT BY HAND. /// @dev Hash of the known bytecode. -bytes32 constant BYTECODE_HASH = bytes32(0xef835570415a69bdf98ea5cacd8c4d2caba4730d06c2218bf102cb4473f4ea73); +bytes32 constant BYTECODE_HASH = bytes32(0x6c37a6ad35101c07818fd5ea57c8d7ed4ed54f4a9ba7b0fce86e27cfc6510097); /// @dev The deterministic deploy address of the contract when deployed via /// the Zoltu factory. -address constant DEPLOYED_ADDRESS = address(0x25aC2b82915f191dbE64e65BAeDDD68b97b68fe1); +address constant DEPLOYED_ADDRESS = address(0x8cACfbD5d78b6D87080cE0839708ac2dA5461F78); /// @dev The creation bytecode of the contract. bytes constant CREATION_CODE = - hex"6080604052348015600e575f80fd5b506102558061001c5f395ff3fe608060405234801561000f575f80fd5b5060043610610034575f3560e01c80638eaa6ac014610038578063d22057a914610074575b5f80fd5b61004b6100463660046101f8565b610089565b60405173ffffffffffffffffffffffffffffffffffffffff909116815260200160405180910390f35b61008761008236600461020f565b6100f1565b005b5f8181526020819052604090205473ffffffffffffffffffffffffffffffffffffffff16806100ec576040517fe9b7924f000000000000000000000000000000000000000000000000000000008152600481018390526024015b60405180910390fd5b919050565b331561012b576040517f8c7257830000000000000000000000000000000000000000000000000000000081523360048201526024016100e3565b73ffffffffffffffffffffffffffffffffffffffff811661017b576040517f657fb0ff000000000000000000000000000000000000000000000000000000008152600481018390526024016100e3565b5f8281526020819052604080822080547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff85169081179091559051909184917f1082cda15f9606da555bb7e9bf4eeee2f8e34abe85d3924bf9bacb716f8feca69190a35050565b5f60208284031215610208575f80fd5b5035919050565b5f8060408385031215610220575f80fd5b82359150602083013573ffffffffffffffffffffffffffffffffffffffff8116811461024a575f80fd5b80915050925092905056"; + hex"6080604052348015600e575f80fd5b506102568061001c5f395ff3fe608060405234801561000f575f80fd5b5060043610610034575f3560e01c80638eaa6ac014610038578063d22057a914610074575b5f80fd5b61004b6100463660046101f9565b610089565b60405173ffffffffffffffffffffffffffffffffffffffff909116815260200160405180910390f35b610087610082366004610210565b6100f2565b005b5f8181526020819052604081205473ffffffffffffffffffffffffffffffffffffffff16806100ec576040517fe9b7924f000000000000000000000000000000000000000000000000000000008152600481018490526024015b60405180910390fd5b92915050565b331561012c576040517f8c7257830000000000000000000000000000000000000000000000000000000081523360048201526024016100e3565b73ffffffffffffffffffffffffffffffffffffffff811661017c576040517f657fb0ff000000000000000000000000000000000000000000000000000000008152600481018390526024016100e3565b5f8281526020819052604080822080547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff85169081179091559051909184917f1082cda15f9606da555bb7e9bf4eeee2f8e34abe85d3924bf9bacb716f8feca69190a35050565b5f60208284031215610209575f80fd5b5035919050565b5f8060408385031215610221575f80fd5b82359150602083013573ffffffffffffffffffffffffffffffffffffffff8116811461024b575f80fd5b80915050925092905056"; /// @dev The runtime bytecode of the contract. bytes constant RUNTIME_CODE = - hex"608060405234801561000f575f80fd5b5060043610610034575f3560e01c80638eaa6ac014610038578063d22057a914610074575b5f80fd5b61004b6100463660046101f8565b610089565b60405173ffffffffffffffffffffffffffffffffffffffff909116815260200160405180910390f35b61008761008236600461020f565b6100f1565b005b5f8181526020819052604090205473ffffffffffffffffffffffffffffffffffffffff16806100ec576040517fe9b7924f000000000000000000000000000000000000000000000000000000008152600481018390526024015b60405180910390fd5b919050565b331561012b576040517f8c7257830000000000000000000000000000000000000000000000000000000081523360048201526024016100e3565b73ffffffffffffffffffffffffffffffffffffffff811661017b576040517f657fb0ff000000000000000000000000000000000000000000000000000000008152600481018390526024016100e3565b5f8281526020819052604080822080547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff85169081179091559051909184917f1082cda15f9606da555bb7e9bf4eeee2f8e34abe85d3924bf9bacb716f8feca69190a35050565b5f60208284031215610208575f80fd5b5035919050565b5f8060408385031215610220575f80fd5b82359150602083013573ffffffffffffffffffffffffffffffffffffffff8116811461024a575f80fd5b80915050925092905056"; + hex"608060405234801561000f575f80fd5b5060043610610034575f3560e01c80638eaa6ac014610038578063d22057a914610074575b5f80fd5b61004b6100463660046101f9565b610089565b60405173ffffffffffffffffffffffffffffffffffffffff909116815260200160405180910390f35b610087610082366004610210565b6100f2565b005b5f8181526020819052604081205473ffffffffffffffffffffffffffffffffffffffff16806100ec576040517fe9b7924f000000000000000000000000000000000000000000000000000000008152600481018490526024015b60405180910390fd5b92915050565b331561012c576040517f8c7257830000000000000000000000000000000000000000000000000000000081523360048201526024016100e3565b73ffffffffffffffffffffffffffffffffffffffff811661017c576040517f657fb0ff000000000000000000000000000000000000000000000000000000008152600481018390526024016100e3565b5f8281526020819052604080822080547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff85169081179091559051909184917f1082cda15f9606da555bb7e9bf4eeee2f8e34abe85d3924bf9bacb716f8feca69190a35050565b5f60208284031215610209575f80fd5b5035919050565b5f8060408385031215610221575f80fd5b82359150602083013573ffffffffffffffffffffffffffffffffffffffff8116811461024b575f80fd5b80915050925092905056"; /// @dev The addresses that MUST already have code on a network before /// this release can be broadcast there, `abi.encode`d as an `address[]` diff --git a/src/interface/IAddressRegistryV1.sol b/src/interface/IAddressRegistryV1.sol index 84c925a..63f0bdb 100644 --- a/src/interface/IAddressRegistryV1.sol +++ b/src/interface/IAddressRegistryV1.sol @@ -82,6 +82,6 @@ interface IAddressRegistryV1 { /// does. Reading at the point of use instead means reading whatever root /// has bound most recently. /// @param name The name to read. - /// @return account The address bound to `name`. Never the zero address. - function get(bytes32 name) external view returns (address account); + /// @return The address bound to `name`. Never the zero address. + function get(bytes32 name) external view returns (address); } diff --git a/src/lib/LibAddressRegistryReleased.sol b/src/lib/LibAddressRegistryReleased.sol index 32cd212..a1c8cff 100644 --- a/src/lib/LibAddressRegistryReleased.sol +++ b/src/lib/LibAddressRegistryReleased.sol @@ -24,8 +24,9 @@ import {DeploySuite} from "../abstract/RainDeploySuitesBase.sol"; /// back in to preserve what it last said. library LibAddressRegistryReleased { /// Every frozen release, in tag order. - /// @return suites The released suites. - function releasedSuites() internal pure returns (DeploySuite[] memory suites) { - suites = new DeploySuite[](0); + /// @return The released suites. + function releasedSuites() internal pure returns (DeploySuite[] memory) { + DeploySuite[] memory suites = new DeploySuite[](0); + return suites; } } diff --git a/src/lib/LibMigrationRegistryReleased.sol b/src/lib/LibMigrationRegistryReleased.sol index 626130d..2a9324a 100644 --- a/src/lib/LibMigrationRegistryReleased.sol +++ b/src/lib/LibMigrationRegistryReleased.sol @@ -24,8 +24,9 @@ import {DeploySuite} from "../abstract/RainDeploySuitesBase.sol"; /// back in to preserve what it last said. library LibMigrationRegistryReleased { /// Every frozen release, in tag order. - /// @return suites The released suites. - function releasedSuites() internal pure returns (DeploySuite[] memory suites) { - suites = new DeploySuite[](0); + /// @return The released suites. + function releasedSuites() internal pure returns (DeploySuite[] memory) { + DeploySuite[] memory suites = new DeploySuite[](0); + return suites; } } diff --git a/src/lib/LibRainDeploy.sol b/src/lib/LibRainDeploy.sol index e4d2686..4ce5e80 100644 --- a/src/lib/LibRainDeploy.sol +++ b/src/lib/LibRainDeploy.sol @@ -109,19 +109,20 @@ library LibRainDeploy { /// @param target The contract address to check. /// @param expectedCodeHash The code hash to look for. /// @param blockNumber The block number to check. - /// @return isStart True if the contract first appears at this block. + /// @return True if the contract first appears at this block. function isStartBlock(Vm vm, address target, bytes32 expectedCodeHash, uint256 blockNumber) internal - returns (bool isStart) + returns (bool) { uint256 originalBlock = block.number; vm.rollFork(blockNumber); - isStart = target.codehash == expectedCodeHash; + bool isStart = target.codehash == expectedCodeHash; if (isStart && blockNumber > 0) { vm.rollFork(blockNumber - 1); isStart = target.codehash != expectedCodeHash; } vm.rollFork(originalBlock); + return isStart; } /// Finds the block number at which a contract was first deployed by binary @@ -154,11 +155,11 @@ library LibRainDeploy { /// @param expectedCodeHash The expected code hash of the target contract. /// @param startBlock The earliest block to search from. The target MUST /// NOT have the expected code hash at this block. - /// @return deployBlock The first block number where `target` has the - /// expected code hash. + /// @return The first block number where `target` has the expected code + /// hash. function findDeployBlock(Vm vm, address target, bytes32 expectedCodeHash, uint256 startBlock) internal - returns (uint256 deployBlock) + returns (uint256) { if (target.code.length == 0) { revert NotDeployed(target); @@ -191,8 +192,8 @@ library LibRainDeploy { } } - deployBlock = low; vm.rollFork(originalBlock); + return low; } /// Etches the Zoltu factory bytecode into the factory address. Useful for @@ -219,9 +220,10 @@ library LibRainDeploy { /// Deploys the given creation code via the Zoltu factory. /// Handles the return data and errors appropriately. /// @param creationCode The creation code to deploy. - /// @return deployedAddress The address of the deployed contract. - function deployZoltu(bytes memory creationCode) internal returns (address deployedAddress) { + /// @return The address of the deployed contract. + function deployZoltu(bytes memory creationCode) internal returns (address) { address zoltuFactory = ZOLTU_FACTORY; + address deployedAddress; bool success; assembly ("memory-safe") { // Zero scratch space so mload(0) reads a clean 32-byte word. @@ -243,6 +245,7 @@ library LibRainDeploy { console2.logBytes32(deployedAddress.codehash); revert DeployFailed(success, deployedAddress); } + return deployedAddress; } /// Returns the list of networks currently supported by Rain deployments. @@ -415,7 +418,7 @@ library LibRainDeploy { /// the address the Zoltu factory derives for `creationCode`. /// @param expectedCodeHash The expected code hash of the deployed contract. /// @param dependencies The addresses that must have code on each network. - /// @return deployedAddress The deployed contract address. + /// @return The deployed contract address. function deployToNetworks( Vm vm, string[] memory networks, @@ -425,7 +428,7 @@ library LibRainDeploy { address expectedAddress, bytes32 expectedCodeHash, address[] memory dependencies - ) internal returns (address deployedAddress) { + ) internal returns (address) { if (networks.length == 0) { revert NoNetworks(); } @@ -466,7 +469,7 @@ library LibRainDeploy { console2.log(" - Deploying via Zoltu"); vm.startBroadcast(deployer); - deployedAddress = deployZoltu(creationCode); + address deployedAddress = deployZoltu(creationCode); vm.stopBroadcast(); if (deployedAddress != expectedAddress) { revert UnexpectedDeployedAddress(expectedAddress, deployedAddress); @@ -478,21 +481,22 @@ library LibRainDeploy { // its dependencies present to remain deployed, which keeps a // rerun a clean no-op here. console2.log(" - Code already exists at expected address, skipping deployment"); - deployedAddress = expectedAddress; } - console2.log(" - Final Address:", deployedAddress); + console2.log(" - Final Address:", expectedAddress); console2.log(" - Verifying code hash"); - if (expectedCodeHash != deployedAddress.codehash) { - revert UnexpectedDeployedCodeHash(expectedCodeHash, deployedAddress.codehash); + if (expectedCodeHash != expectedAddress.codehash) { + revert UnexpectedDeployedCodeHash(expectedCodeHash, expectedAddress.codehash); } console2.log("manual verification command:"); console2.log( string.concat( - "forge verify-contract --chain ", networks[i], " ", vm.toString(deployedAddress), " ", contractPath + "forge verify-contract --chain ", networks[i], " ", vm.toString(expectedAddress), " ", contractPath ) ); } + + return expectedAddress; } /// Deploys the given creation code via the Zoltu factory to the given @@ -507,7 +511,7 @@ library LibRainDeploy { /// the address the Zoltu factory derives for `creationCode`. /// @param expectedCodeHash The expected code hash of the deployed contract. /// @param dependencies The dependency addresses to check. - /// @return deployedAddress The address of the deployed contract. + /// @return The address of the deployed contract. function deployAndBroadcast( Vm vm, string[] memory networks, @@ -517,7 +521,7 @@ library LibRainDeploy { address expectedAddress, bytes32 expectedCodeHash, address[] memory dependencies - ) internal returns (address deployedAddress) { + ) internal returns (address) { if (networks.length == 0) { revert NoNetworks(); } @@ -525,7 +529,7 @@ library LibRainDeploy { console2.log("Deploying from address:", deployer); - deployedAddress = deployToNetworks( + return deployToNetworks( vm, networks, deployer, creationCode, contractPath, expectedAddress, expectedCodeHash, dependencies ); } diff --git a/src/lib/LibRainDeploySnapshot.sol b/src/lib/LibRainDeploySnapshot.sol index 855be7b..5076108 100644 --- a/src/lib/LibRainDeploySnapshot.sol +++ b/src/lib/LibRainDeploySnapshot.sol @@ -331,8 +331,8 @@ library LibRainDeploySnapshot { /// filter on, because nothing else has any business being in there. /// @param vm The Vm instance for file operations. /// @param root The record root — `LIB_FS_ROOT` for a repo's real record. - /// @return paths Every frozen record file. - function frozenSnapshotPaths(Vm vm, string memory root) internal view returns (string[] memory paths) { + /// @return Every frozen record file. + function frozenSnapshotPaths(Vm vm, string memory root) internal view returns (string[] memory) { // A repo with no generated directory at all has released nothing. That // is a real state — it is this repo's own, before its first release — // rather than a missing file to fail on. @@ -362,10 +362,11 @@ library LibRainDeploySnapshot { count++; } - paths = new string[](count); + string[] memory paths = new string[](count); for (uint256 i = 0; i < count; i++) { paths[i] = found[i]; } + return paths; } /// The constants a snapshot declares below the `BYTECODE_HASH` that @@ -728,9 +729,9 @@ library LibRainDeploySnapshot { /// nothing sorts a list that short faster than it takes to say so. /// @param vm The Vm instance for string operations. /// @param paths The record's files, in any order. - /// @return sorted The same files, in release order. - function sortedRecordPaths(Vm vm, string[] memory paths) internal pure returns (string[] memory sorted) { - sorted = new string[](paths.length); + /// @return The same files, in release order. + function sortedRecordPaths(Vm vm, string[] memory paths) internal pure returns (string[] memory) { + string[] memory sorted = new string[](paths.length); for (uint256 i = 0; i < paths.length; i++) { uint256 j = i; while (j > 0 && recordPrecedes(vm, paths[i], sorted[j - 1])) { @@ -739,6 +740,7 @@ library LibRainDeploySnapshot { } sorted[j] = paths[i]; } + return sorted; } /// One contract's releases out of a record, in tag order. @@ -833,12 +835,13 @@ library LibRainDeploySnapshot { /// written anywhere but into the immutable record. /// @param vm The Vm instance for string operations. /// @param paths The record's files, in the order they are emitted. - /// @return imports The import block. - function releasedImportBlock(Vm vm, string[] memory paths) internal pure returns (string memory imports) { - imports = "import {DeploySuite} from \"../abstract/RainDeploySuitesBase.sol\";\n\n"; + /// @return The import block. + function releasedImportBlock(Vm vm, string[] memory paths) internal pure returns (string memory) { + string memory imports = "import {DeploySuite} from \"../abstract/RainDeploySuitesBase.sol\";\n\n"; for (uint256 i = 0; i < paths.length; i++) { imports = string.concat(imports, releasedImport(vm, paths[i])); } + return imports; } /// The library block of a generated released-suites lib. @@ -921,13 +924,13 @@ library LibRainDeploySnapshot { "/// path, which is intended: the alternative is parsing this generated file\n" "/// back in to preserve what it last said.\nlibrary ", libraryName, - " {\n /// Every frozen release, in tag order.\n" " /// @return suites The released suites.\n" - " function releasedSuites() internal pure returns (DeploySuite[] memory suites) {\n" - " suites = new DeploySuite[](", + " {\n /// Every frozen release, in tag order.\n" " /// @return The released suites.\n" + " function releasedSuites() internal pure returns (DeploySuite[] memory) {\n" + " DeploySuite[] memory suites = new DeploySuite[](", vm.toString(paths.length), ");\n", entries, - " }\n}\n" + " return suites;\n }\n}\n" ); } @@ -1026,13 +1029,14 @@ library LibRainDeploySnapshot { /// file also compiles only from a directory whose parent holds /// `abstract/RainDeploySuitesBase.sol`. /// @param contractNames The contracts whose released libs to aggregate. - /// @return imports The import block. - function aggregateImportBlock(string[] memory contractNames) internal pure returns (string memory imports) { - imports = "import {DeploySuite} from \"../abstract/RainDeploySuitesBase.sol\";\n\n"; + /// @return The import block. + function aggregateImportBlock(string[] memory contractNames) internal pure returns (string memory) { + string memory imports = "import {DeploySuite} from \"../abstract/RainDeploySuitesBase.sol\";\n\n"; for (uint256 i = 0; i < contractNames.length; i++) { string memory libraryName = releasedLibraryName(contractNames[i]); imports = string.concat(imports, "import {", libraryName, "} from \"./", libraryName, ".sol\";\n\n"); } + return imports; } /// The library block of the generated aggregate lib: every per-contract @@ -1092,10 +1096,10 @@ library LibRainDeploySnapshot { "/// release every check quietly stops asking about.\nlibrary ", RELEASED_SUITES_LIBRARY, " {\n /// Every released suite, in declaration order.\n", - " /// @return suites The released suites.\n", - " function releasedSuites() internal pure returns (DeploySuite[] memory suites) {\n", + " /// @return The released suites.\n", + " function releasedSuites() internal pure returns (DeploySuite[] memory) {\n", contractNames.length == 0 - ? " suites = new DeploySuite[](0);\n" + ? " return new DeploySuite[](0);\n" : string.concat( " DeploySuite[][] memory released = new DeploySuite[][](", vm.toString(contractNames.length), @@ -1105,14 +1109,15 @@ library LibRainDeploySnapshot { " for (uint256 i = 0; i < released.length; i++) {\n", " total += released[i].length;\n", " }\n\n", - " suites = new DeploySuite[](total);\n\n", + " DeploySuite[] memory suites = new DeploySuite[](total);\n\n", " uint256 offset = 0;\n", " for (uint256 i = 0; i < released.length; i++) {\n", " for (uint256 j = 0; j < released[i].length; j++) {\n", " suites[offset + j] = released[i][j];\n", " }\n", " offset += released[i].length;\n", - " }\n" + " }\n\n", + " return suites;\n" ), " }\n}\n" ); @@ -1210,15 +1215,21 @@ library LibRainDeploySnapshot { /// @param vm The Vm instance for file operations. /// @param recordRoot The record root — `LIB_FS_ROOT` for a repo's real /// record. - /// @return newest The newest frozen tag, or `""` if nothing is frozen. - function newestFrozenTag(Vm vm, string memory recordRoot) internal view returns (string memory newest) { + /// @return The newest frozen tag, or `""` if nothing is frozen. + function newestFrozenTag(Vm vm, string memory recordRoot) internal view returns (string memory) { string[] memory paths = frozenSnapshotPaths(vm, recordRoot); - for (uint256 i = 0; i < paths.length; i++) { + if (paths.length == 0) { + return ""; + } + + string memory newest = tagForRecordPath(vm, paths[0]); + for (uint256 i = 1; i < paths.length; i++) { string memory tag = tagForRecordPath(vm, paths[i]); - if (bytes(newest).length == 0 || tagPrecedes(vm, newest, tag)) { + if (tagPrecedes(vm, newest, tag)) { newest = tag; } } + return newest; } /// Refuse a release tag that does not strictly follow every tag already in diff --git a/src/lib/LibReleasedSuites.sol b/src/lib/LibReleasedSuites.sol index dc665ed..9bb7305 100644 --- a/src/lib/LibReleasedSuites.sol +++ b/src/lib/LibReleasedSuites.sol @@ -25,8 +25,8 @@ import {LibMigrationRegistryReleased} from "./LibMigrationRegistryReleased.sol"; /// release every check quietly stops asking about. library LibReleasedSuites { /// Every released suite, in declaration order. - /// @return suites The released suites. - function releasedSuites() internal pure returns (DeploySuite[] memory suites) { + /// @return The released suites. + function releasedSuites() internal pure returns (DeploySuite[] memory) { DeploySuite[][] memory released = new DeploySuite[][](2); released[0] = LibAddressRegistryReleased.releasedSuites(); released[1] = LibMigrationRegistryReleased.releasedSuites(); @@ -36,7 +36,7 @@ library LibReleasedSuites { total += released[i].length; } - suites = new DeploySuite[](total); + DeploySuite[] memory suites = new DeploySuite[](total); uint256 offset = 0; for (uint256 i = 0; i < released.length; i++) { @@ -45,5 +45,7 @@ library LibReleasedSuites { } offset += released[i].length; } + + return suites; } } diff --git a/test/concrete/BuildHarness.sol b/test/concrete/BuildHarness.sol index 2dda367..f80d38a 100644 --- a/test/concrete/BuildHarness.sol +++ b/test/concrete/BuildHarness.sol @@ -20,6 +20,12 @@ contract BuildHarness is Build { return generatedContracts(); } + /// The name list the freeze and the aggregate are emitted from. + /// @return The generated contract names. + function externalGeneratedContractNames() external pure returns (string[] memory) { + return generatedContractNames(); + } + /// The deploy declaration's list, through the same guarded reader every /// other consumer of the declaration uses. /// @return The declared candidates. diff --git a/test/script/Build.t.sol b/test/script/Build.t.sol index 63de863..3e6f1b1 100644 --- a/test/script/Build.t.sol +++ b/test/script/Build.t.sol @@ -184,4 +184,25 @@ contract BuildTest is Test { ); } } + + /// PROPERTY: `generatedContractNames()` is every `generatedContracts()` + /// entry's `contractName`, positionally. + /// + /// It is the list `cutRelease` freezes and the list the aggregate is + /// emitted from, and both reach it only through `forge script`. A name + /// list shorter than the declaration freezes one contract fewer and emits + /// an aggregate that declares that contract's releases as nothing at all, + /// and every other assertion here is still green: the tests above read + /// `generatedContracts()` and the committed file, neither of which this + /// list passes through. + function testGeneratedContractNamesAreTheDeclarationInOrder() external view { + GeneratedContract[] memory generated = sBuild.externalGeneratedContracts(); + string[] memory names = sBuild.externalGeneratedContractNames(); + + assertEq(names.length, generated.length, "a different number of names than generated contracts"); + + for (uint256 i = 0; i < generated.length; i++) { + assertEq(names[i], generated[i].contractName, "the names are not the declaration in order"); + } + } } diff --git a/test/src/lib/LibRainDeploySnapshot.t.sol b/test/src/lib/LibRainDeploySnapshot.t.sol index 6bbd34b..c5cc543 100644 --- a/test/src/lib/LibRainDeploySnapshot.t.sol +++ b/test/src/lib/LibRainDeploySnapshot.t.sol @@ -698,8 +698,8 @@ contract LibRainDeploySnapshotTest is Test { "/// record. A moved source path retroactively updates every entry's artifact\n" "/// path, which is intended: the alternative is parsing this generated file\n" "/// back in to preserve what it last said.\n" "library LibAddressRegistryReleased {\n" - " /// Every frozen release, in tag order.\n" " /// @return suites The released suites.\n" - " function releasedSuites() internal pure returns (DeploySuite[] memory suites) {\n"; + " /// Every frozen release, in tag order.\n" " /// @return The released suites.\n" + " function releasedSuites() internal pure returns (DeploySuite[] memory) {\n"; /// The entry one release contributes. /// @param index The entry's index. @@ -769,7 +769,11 @@ contract LibRainDeploySnapshotTest is Test { LibRainDeploySnapshot.releasedLibraryBlock( vm, EMITTED_LIBRARY, EMITTED_CONTRACT, recordOf(0), emitterTemplate() ), - string.concat(EXPECTED_LIBRARY_HEADER, " suites = new DeploySuite[](0);\n", " }\n}\n") + string.concat( + EXPECTED_LIBRARY_HEADER, + " DeploySuite[] memory suites = new DeploySuite[](0);\n", + " return suites;\n }\n}\n" + ) ); assertEq( @@ -778,9 +782,9 @@ contract LibRainDeploySnapshotTest is Test { ), string.concat( EXPECTED_LIBRARY_HEADER, - " suites = new DeploySuite[](1);\n", + " DeploySuite[] memory suites = new DeploySuite[](1);\n", expectedEntry("0", "0_0_1"), - " }\n}\n" + " return suites;\n }\n}\n" ) ); @@ -790,10 +794,10 @@ contract LibRainDeploySnapshotTest is Test { ), string.concat( EXPECTED_LIBRARY_HEADER, - " suites = new DeploySuite[](2);\n", + " DeploySuite[] memory suites = new DeploySuite[](2);\n", expectedEntry("0", "0_0_1"), expectedEntry("1", "0_0_2"), - " }\n}\n" + " return suites;\n }\n}\n" ) ); } @@ -1226,8 +1230,8 @@ contract LibRainDeploySnapshotTest is Test { "/// so a contract that is generated, aliased and frozen cannot be missing\n" "/// from the declaration, and a release missing from the declaration is a\n" "/// release every check quietly stops asking about.\n" "library LibReleasedSuites {\n" - " /// Every released suite, in declaration order.\n" " /// @return suites The released suites.\n" - " function releasedSuites() internal pure returns (DeploySuite[] memory suites) {\n"; + " /// Every released suite, in declaration order.\n" " /// @return The released suites.\n" + " function releasedSuites() internal pure returns (DeploySuite[] memory) {\n"; /// The aggregate's text from below the released libs it reads to the end of /// `releasedSuites`: the sum of their lengths, the array that sum @@ -1236,11 +1240,11 @@ contract LibRainDeploySnapshotTest is Test { /// every line the same width however many contracts there are. string constant EXPECTED_AGGREGATE_CONCATENATION = "\n uint256 total = 0;\n" " for (uint256 i = 0; i < released.length; i++) {\n" " total += released[i].length;\n" - " }\n\n" " suites = new DeploySuite[](total);\n\n" " uint256 offset = 0;\n" - " for (uint256 i = 0; i < released.length; i++) {\n" + " }\n\n" " DeploySuite[] memory suites = new DeploySuite[](total);\n\n" + " uint256 offset = 0;\n" " for (uint256 i = 0; i < released.length; i++) {\n" " for (uint256 j = 0; j < released[i].length; j++) {\n" " suites[offset + j] = released[i][j];\n" " }\n" - " offset += released[i].length;\n" " }\n"; + " offset += released[i].length;\n" " }\n\n" " return suites;\n"; /// The aggregate MUST import the released lib of EVERY contract it is /// handed, by the sibling path the released writer wrote it to, and nothing @@ -1283,7 +1287,7 @@ contract LibRainDeploySnapshotTest is Test { function testAggregateLibraryBlockDeclaresNothingForNoContracts() external pure { assertEq( LibRainDeploySnapshot.aggregateLibraryBlock(vm, aggregateNames(0)), - string.concat(EXPECTED_AGGREGATE_HEADER, " suites = new DeploySuite[](0);\n", " }\n}\n") + string.concat(EXPECTED_AGGREGATE_HEADER, " return new DeploySuite[](0);\n", " }\n}\n") ); }