diff --git a/packages/contracts-bedrock/scripts/Deployer.sol b/packages/contracts-bedrock/scripts/Deployer.sol index 3cbe2e771b82d942a4e38e0a53d9d2b51c48a3dd..5b5b136d414e7d783dbccd652bfd8734e8e869b7 100644 --- a/packages/contracts-bedrock/scripts/Deployer.sol +++ b/packages/contracts-bedrock/scripts/Deployer.sol @@ -4,8 +4,9 @@ pragma solidity ^0.8.0; import { Script } from "forge-std/Script.sol"; import { stdJson } from "forge-std/StdJson.sol"; import { console2 as console } from "forge-std/console2.sol"; -import { Executables } from "./Executables.sol"; -import { Chains } from "./Chains.sol"; +import { Executables } from "scripts/Executables.sol"; +import { Chains } from "scripts/Chains.sol"; +import { Predeploys } from "src/libraries/Predeploys.sol"; /// @notice store the new deployment to be saved struct Deployment { @@ -197,7 +198,7 @@ abstract contract Deployer is Script { return _getExistingDeploymentAddress(_name) != address(0); } - /// @notice Returns the address of a deployment. + /// @notice Returns the address of a deployment. Also handles the predeploys. /// @param _name The name of the deployment. /// @return The address of the deployment. May be `address(0)` if the deployment does not /// exist. @@ -209,7 +210,54 @@ abstract contract Deployer is Script { } return existing.addr; } - return _getExistingDeploymentAddress(_name); + address addr = _getExistingDeploymentAddress(_name); + if (addr != address(0)) return payable(addr); + + bytes32 digest = keccak256(bytes(_name)); + if (digest == keccak256(bytes("L2CrossDomainMessenger"))) { + return payable(Predeploys.L2_CROSS_DOMAIN_MESSENGER); + } else if (digest == keccak256(bytes("L2ToL1MessagePasser"))) { + return payable(Predeploys.L2_TO_L1_MESSAGE_PASSER); + } else if (digest == keccak256(bytes("L2StandardBridge"))) { + return payable(Predeploys.L2_STANDARD_BRIDGE); + } else if (digest == keccak256(bytes("L2ERC721Bridge"))) { + return payable(Predeploys.L2_ERC721_BRIDGE); + } else if (digest == keccak256(bytes("SequencerFeeWallet"))) { + return payable(Predeploys.SEQUENCER_FEE_WALLET); + } else if (digest == keccak256(bytes("OptimismMintableERC20Factory"))) { + return payable(Predeploys.OPTIMISM_MINTABLE_ERC20_FACTORY); + } else if (digest == keccak256(bytes("OptimismMintableERC721Factory"))) { + return payable(Predeploys.OPTIMISM_MINTABLE_ERC721_FACTORY); + } else if (digest == keccak256(bytes("L1Block"))) { + return payable(Predeploys.L1_BLOCK_ATTRIBUTES); + } else if (digest == keccak256(bytes("GasPriceOracle"))) { + return payable(Predeploys.GAS_PRICE_ORACLE); + } else if (digest == keccak256(bytes("L1MessageSender"))) { + return payable(Predeploys.L1_MESSAGE_SENDER); + } else if (digest == keccak256(bytes("DeployerWhitelist"))) { + return payable(Predeploys.DEPLOYER_WHITELIST); + } else if (digest == keccak256(bytes("WETH9"))) { + return payable(Predeploys.WETH9); + } else if (digest == keccak256(bytes("LegacyERC20ETH"))) { + return payable(Predeploys.LEGACY_ERC20_ETH); + } else if (digest == keccak256(bytes("L1BlockNumber"))) { + return payable(Predeploys.L1_BLOCK_NUMBER); + } else if (digest == keccak256(bytes("LegacyMessagePasser"))) { + return payable(Predeploys.LEGACY_MESSAGE_PASSER); + } else if (digest == keccak256(bytes("ProxyAdmin"))) { + return payable(Predeploys.PROXY_ADMIN); + } else if (digest == keccak256(bytes("BaseFeeVault"))) { + return payable(Predeploys.BASE_FEE_VAULT); + } else if (digest == keccak256(bytes("L1FeeVault"))) { + return payable(Predeploys.L1_FEE_VAULT); + } else if (digest == keccak256(bytes("GovernanceToken"))) { + return payable(Predeploys.GOVERNANCE_TOKEN); + } else if (digest == keccak256(bytes("SchemaRegistry"))) { + return payable(Predeploys.SCHEMA_REGISTRY); + } else if (digest == keccak256(bytes("EAS"))) { + return payable(Predeploys.EAS); + } + return payable(address(0)); } /// @notice Returns the address of a deployment and reverts if the deployment @@ -348,25 +396,43 @@ abstract contract Deployer is Script { return string.concat(sanitized, ".sol:", _name); } - /// @notice Returns the filesystem path to the artifact path. Assumes that the name of the - /// file matches the name of the contract. - function _getForgeArtifactPath(string memory _name) internal returns (string memory) { + function _getForgeArtifactDirectory(string memory _name) internal returns (string memory) { string[] memory cmd = new string[](3); cmd[0] = Executables.bash; cmd[1] = "-c"; cmd[2] = string.concat(Executables.forge, " config --json | ", Executables.jq, " -r .out"); bytes memory res = vm.ffi(cmd); string memory contractName = _stripSemver(_name); - string memory forgeArtifactPath = - string.concat(vm.projectRoot(), "/", string(res), "/", contractName, ".sol/", _name, ".json"); - return forgeArtifactPath; + return string.concat(vm.projectRoot(), "/", string(res), "/", contractName, ".sol"); + } + + /// @notice Returns the filesystem path to the artifact path. If the contract was compiled + /// with multiple solidity versions then return the first one based on the result of `ls`. + function _getForgeArtifactPath(string memory _name) internal returns (string memory) { + string memory directory = _getForgeArtifactDirectory(_name); + string memory path = string.concat(directory, "/", _name, ".json"); + if (vm.exists(path)) return path; + + string[] memory cmd = new string[](3); + cmd[0] = Executables.bash; + cmd[1] = "-c"; + cmd[2] = string.concat( + Executables.ls, + " -1 --color=never ", + directory, + " | ", + Executables.jq, + " -R -s -c 'split(\"\n\") | map(select(length > 0))'" + ); + bytes memory res = vm.ffi(cmd); + string[] memory files = stdJson.readStringArray(string(res), ""); + return string.concat(directory, "/", files[0]); } /// @notice Returns the forge artifact given a contract name. function _getForgeArtifact(string memory _name) internal returns (string memory) { string memory forgeArtifactPath = _getForgeArtifactPath(_name); - string memory forgeArtifact = vm.readFile(forgeArtifactPath); - return forgeArtifact; + return vm.readFile(forgeArtifactPath); } /// @notice Returns the receipt of a deployment transaction. @@ -413,6 +479,9 @@ abstract contract Deployer is Script { string[] memory cmd = new string[](3); cmd[0] = Executables.bash; cmd[1] = "-c"; + + cmd[2] = ""; + cmd[2] = string.concat(Executables.jq, " -r '.abi' < ", _getForgeArtifactPath(_name)); bytes memory res = vm.ffi(cmd); abi_ = string(res); @@ -464,7 +533,9 @@ abstract contract Deployer is Script { if (_isProxy) { _contractName = string.concat(_contractName, "Proxy"); } - bytes32 slotVal = vm.load(mustGetAddress(_contractName), bytes32(vm.parseUint(slot.slot))); + address addr = getAddress(_contractName); + + bytes32 slotVal = vm.load(addr, bytes32(vm.parseUint(slot.slot))); initialized_ = uint8((uint256(slotVal) >> (slot.offset * 8)) & 0xFF); } diff --git a/packages/contracts-bedrock/scripts/Executables.sol b/packages/contracts-bedrock/scripts/Executables.sol index 12c4dfbedfce8e167f5a63781ce5a687aeb874c9..8eab92078b9b3c4d5c4bfb389cd81190e3c0f028 100644 --- a/packages/contracts-bedrock/scripts/Executables.sol +++ b/packages/contracts-bedrock/scripts/Executables.sol @@ -11,4 +11,5 @@ library Executables { string internal constant echo = "echo"; string internal constant sed = "sed"; string internal constant find = "find"; + string internal constant ls = "ls"; } diff --git a/packages/contracts-bedrock/test/vendor/Initializable.t.sol b/packages/contracts-bedrock/test/vendor/Initializable.t.sol index 3636adf1f8d619ad2f53b096dba8424d46ca6f5e..5f87c78cce7b70eb7deb35476b782563255551dc 100644 --- a/packages/contracts-bedrock/test/vendor/Initializable.t.sol +++ b/packages/contracts-bedrock/test/vendor/Initializable.t.sol @@ -103,14 +103,22 @@ contract Initializer_Test is Bridge_Initializer { initializedSlotVal: deploy.loadInitializedSlot("ProtocolVersions", true) }) ); + // L2CrossDomainMessenger + contracts.push( + InitializeableContract({ + target: address(l2CrossDomainMessenger), + initCalldata: abi.encodeCall(l2CrossDomainMessenger.initialize, ()), + initializedSlotVal: deploy.loadInitializedSlot("L2CrossDomainMessenger", false) + }) + ); } /// @notice Tests that: - /// 1. All `Initializable` contracts in `src/L1` are accounted for in the `contracts` array. + /// 1. All `Initializable` contracts in `src/L1` and `src/L2` are accounted for in the `contracts` array. /// 2. The `_initialized` flag of each contract is properly set to `1`, signifying that the /// contracts are initialized. /// 3. The `initialize()` function of each contract cannot be called more than once. - function test_cannotReinitializeL1_succeeds() public { + function test_cannotReinitialize_succeeds() public { // Ensure that all L1 `Initializable` contracts are accounted for. assertEq(_getNumL1Initializable(), contracts.length); @@ -135,7 +143,7 @@ contract Initializer_Test is Bridge_Initializer { command[1] = "-c"; command[2] = string.concat( Executables.find, - " src/L1 -type f -exec basename {} \\;", + " src/L1 src/L2 -type f -exec basename {} \\;", " | ", Executables.sed, " 's/\\.[^.]*$//'",