diff --git a/packages/contracts-bedrock/scripts/deploy/AddGameType.s.sol b/packages/contracts-bedrock/scripts/deploy/AddGameType.s.sol index 07579ed32166e..42bcf189fbf59 100644 --- a/packages/contracts-bedrock/scripts/deploy/AddGameType.s.sol +++ b/packages/contracts-bedrock/scripts/deploy/AddGameType.s.sol @@ -6,6 +6,7 @@ import { Script } from "forge-std/Script.sol"; // Scripts import { DeployUtils } from "scripts/libraries/DeployUtils.sol"; +import { DummyCaller } from "scripts/libraries/DummyCaller.sol"; // Interfaces import { IOPContractsManager } from "interfaces/L1/IOPContractsManager.sol"; @@ -56,10 +57,12 @@ contract AddGameType is Script { } function run(Input memory _agi) public returns (Output memory) { - // Etch DummyCaller contract + // Etch DummyCaller contract. This contract is used to mimic the contract that is used + // as the source of the delegatecall to the OPCM. In practice this will be the governance + // 2/2 or similar. address prank = _agi.prank; - bytes memory code = vm.getDeployedCode("AddGameType.s.sol:DummyCaller"); + bytes memory code = type(DummyCaller).runtimeCode; vm.etch(prank, code); vm.store(prank, bytes32(0), bytes32(uint256(uint160(address(_agi.opcmImpl))))); vm.label(prank, "DummyCaller"); @@ -81,14 +84,12 @@ contract AddGameType is Script { permissioned: _agi.permissioned }); - // Call into the DummyCaller to perform the delegatecall + // Call into the DummyCaller to perform the delegatecall. + // The DummyCaller uses a fallback that reverts on failure, so no need to check success. vm.broadcast(msg.sender); - - (bool success, bytes memory result) = DummyCaller(prank).addGameType(gameConfigs); - require(success, "AddGameType: addGameType failed"); + IOPContractsManager.AddGameOutput[] memory outputs = IOPContractsManager(prank).addGameType(gameConfigs); // Decode the result and set it in the output - IOPContractsManager.AddGameOutput[] memory outputs = abi.decode(result, (IOPContractsManager.AddGameOutput[])); require(outputs.length == 1, "AddGameType: unexpected number of outputs"); return Output({ delayedWETHProxy: outputs[0].delayedWETH, faultDisputeGameProxy: outputs[0].faultDisputeGame }); } @@ -98,20 +99,3 @@ contract AddGameType is Script { DeployUtils.assertValidContractAddress(address(_ago.faultDisputeGameProxy)); } } - -/// @title DummyCaller -/// @notice This contract is used to mimic the contract that is used as the source of the delegatecall to the OPCM. -/// @dev This contract is used for OPCM versions 4.1.0 and above. - -contract DummyCaller { - address internal _opcmAddr; - - function addGameType(IOPContractsManager.AddGameInput[] memory _gameConfigs) - external - returns (bool, bytes memory) - { - bytes memory data = abi.encodeCall(DummyCaller.addGameType, _gameConfigs); - (bool success, bytes memory result) = _opcmAddr.delegatecall(data); - return (success, result); - } -} diff --git a/packages/contracts-bedrock/scripts/deploy/InteropMigration.s.sol b/packages/contracts-bedrock/scripts/deploy/InteropMigration.s.sol index 8e0517df54b66..47f9a0e37cfc5 100644 --- a/packages/contracts-bedrock/scripts/deploy/InteropMigration.s.sol +++ b/packages/contracts-bedrock/scripts/deploy/InteropMigration.s.sol @@ -6,6 +6,7 @@ import { BaseDeployIO } from "scripts/deploy/BaseDeployIO.sol"; import { IOPContractsManagerInteropMigrator, IOPContractsManager } from "interfaces/L1/IOPContractsManager.sol"; import { Duration, Proposal, Hash } from "src/dispute/lib/Types.sol"; import { DeployUtils } from "scripts/libraries/DeployUtils.sol"; +import { DummyCaller } from "scripts/libraries/DummyCaller.sol"; import { IDisputeGameFactory } from "interfaces/dispute/IDisputeGameFactory.sol"; import { IOptimismPortal2 as IOptimismPortal } from "interfaces/L1/IOptimismPortal2.sol"; @@ -194,16 +195,15 @@ contract InteropMigration is Script { // as the source of the delegatecall to the OPCM. In practice this will be the governance // 2/2 or similar. address prank = _imi.prank(); - bytes memory code = vm.getDeployedCode("InteropMigration.s.sol:DummyCaller"); + bytes memory code = type(DummyCaller).runtimeCode; vm.etch(prank, code); vm.store(prank, bytes32(0), bytes32(uint256(uint160(address(opcm))))); vm.label(prank, "DummyCaller"); - // Call into the DummyCaller. This will perform the delegatecall under the hood and - // return the result. + // Call into the DummyCaller. This will perform the delegatecall under the hood. + // The DummyCaller uses a fallback that reverts on failure, so no need to check success. vm.broadcast(msg.sender); - (bool success,) = DummyCaller(prank).migrate(inputs); - require(success, "InteropMigration: migrate failed"); + IOPContractsManagerInteropMigrator(prank).migrate(inputs); // After migration all portals will have the same DGF IOptimismPortal portal = IOptimismPortal(payable(opChainConfigs[0].systemConfigProxy.optimismPortal())); @@ -225,16 +225,3 @@ contract InteropMigration is Script { } } } - -contract DummyCaller { - address internal _opcmAddr; - - function migrate(IOPContractsManagerInteropMigrator.MigrateInput memory _migrateInput) - external - returns (bool, bytes memory) - { - bytes memory data = abi.encodeCall(DummyCaller.migrate, _migrateInput); - (bool success, bytes memory result) = _opcmAddr.delegatecall(data); - return (success, result); - } -} diff --git a/packages/contracts-bedrock/scripts/deploy/UpgradeOPChain.s.sol b/packages/contracts-bedrock/scripts/deploy/UpgradeOPChain.s.sol index d180f80ffdc23..df0b2799f5f7f 100644 --- a/packages/contracts-bedrock/scripts/deploy/UpgradeOPChain.s.sol +++ b/packages/contracts-bedrock/scripts/deploy/UpgradeOPChain.s.sol @@ -6,6 +6,7 @@ import { OPContractsManager } from "src/L1/OPContractsManager.sol"; import { OPContractsManagerV2 } from "src/L1/opcm/OPContractsManagerV2.sol"; import { BaseDeployIO } from "scripts/deploy/BaseDeployIO.sol"; import { DevFeatures } from "src/libraries/DevFeatures.sol"; +import { DummyCaller } from "scripts/libraries/DummyCaller.sol"; contract UpgradeOPChainInput is BaseDeployIO { address internal _prank; @@ -80,23 +81,24 @@ contract UpgradeOPChain is Script { // as the source of the delegatecall to the OPCM. In practice this will be the governance // 2/2 or similar. address prank = _uoci.prank(); - bytes memory code = _getDummyCallerCode(useOPCMv2); + bytes memory code = _getDummyCallerCode(); vm.etch(prank, code); vm.store(prank, bytes32(0), bytes32(uint256(uint160(address(opcm))))); vm.label(prank, "DummyCaller"); - // Call into the DummyCaller. This will perform the delegatecall under the hood and - // return the result. - (bool success,) = _upgrade(prank, useOPCMv2, _uoci.upgradeInput()); - require(success, "UpgradeChain: upgrade failed"); + // Get the upgrade input before broadcasting + bytes memory upgradeInput = _uoci.upgradeInput(); + + // Call into the DummyCaller. This will perform the delegatecall under the hood. + // The DummyCaller uses a fallback that reverts on failure, so no need to check success. + vm.broadcast(msg.sender); + _upgrade(prank, useOPCMv2, upgradeInput); } - /// @notice Helper function to get the proper dummy caller code based on the OPCM version. - /// @param _useOPCMv2 Whether to use OPCM v2. + /// @notice Helper function to get the dummy caller code. /// @return code The code of the dummy caller. - function _getDummyCallerCode(bool _useOPCMv2) internal view returns (bytes memory) { - if (_useOPCMv2) return vm.getDeployedCode("UpgradeOPChain.s.sol:DummyCallerV2"); - else return vm.getDeployedCode("UpgradeOPChain.s.sol:DummyCallerV1"); + function _getDummyCallerCode() internal pure returns (bytes memory) { + return type(DummyCaller).runtimeCode; } /// @notice Helper function to upgrade the OPCM based on the OPCM version. Performs the decoding of the upgrade @@ -104,46 +106,18 @@ contract UpgradeOPChain is Script { /// @param _prank The address of the dummy caller contract. /// @param _useOPCMv2 Whether to use OPCM v2. /// @param _upgradeInput The upgrade input. - /// @return success Whether the upgrade succeeded. - /// @return result The result of the upgrade (bool, bytes memory). - function _upgrade( - address _prank, - bool _useOPCMv2, - bytes memory _upgradeInput - ) - internal - returns (bool, bytes memory) - { - vm.broadcast(msg.sender); + function _upgrade(address _prank, bool _useOPCMv2, bytes memory _upgradeInput) internal { + bytes memory data; if (_useOPCMv2) { - return DummyCallerV2(_prank).upgrade(abi.decode(_upgradeInput, (OPContractsManagerV2.UpgradeInput))); + data = abi.encodeCall( + OPContractsManagerV2.upgrade, abi.decode(_upgradeInput, (OPContractsManagerV2.UpgradeInput)) + ); } else { - return DummyCallerV1(_prank).upgrade(abi.decode(_upgradeInput, (OPContractsManager.OpChainConfig[]))); + data = abi.encodeCall( + OPContractsManager.upgrade, abi.decode(_upgradeInput, (OPContractsManager.OpChainConfig[])) + ); } - } -} -/// @title DummyCallerV2 -/// @notice This contract is used to mimic the contract that is used as the source of the delegatecall to the OPCM v2. -/// Uses OPContractsManagerV2.UpgradeInput type for the upgrade input. - -contract DummyCallerV2 { - address internal _opcmAddr; - - function upgrade(OPContractsManagerV2.UpgradeInput memory _upgradeInput) external returns (bool, bytes memory) { - bytes memory data = abi.encodeCall(OPContractsManagerV2.upgrade, _upgradeInput); - (bool success, bytes memory result) = _opcmAddr.delegatecall(data); - return (success, result); - } -} -/// @notice This contract is used to mimic the contract that is used as the source of the delegatecall to the OPCM v1. -/// Uses OPContractsManager.OpChainConfig[] type for the upgrade input. - -contract DummyCallerV1 { - address internal _opcmAddr; - - function upgrade(OPContractsManager.OpChainConfig[] memory _opChainConfigs) external returns (bool, bytes memory) { - bytes memory data = abi.encodeCall(OPContractsManager.upgrade, _opChainConfigs); - (bool success, bytes memory result) = _opcmAddr.delegatecall(data); - return (success, result); + (bool success,) = _prank.call(data); + require(success, "UpgradeOPChain: upgrade failed"); } } diff --git a/packages/contracts-bedrock/scripts/deploy/UpgradeSuperchainConfig.s.sol b/packages/contracts-bedrock/scripts/deploy/UpgradeSuperchainConfig.s.sol index 8cc0cb13123d2..b8bdb483a957e 100644 --- a/packages/contracts-bedrock/scripts/deploy/UpgradeSuperchainConfig.s.sol +++ b/packages/contracts-bedrock/scripts/deploy/UpgradeSuperchainConfig.s.sol @@ -7,6 +7,7 @@ import { IOPContractsManagerV2 } from "interfaces/L1/opcm/IOPContractsManagerV2. import { IOPContractsManagerUtils } from "interfaces/L1/opcm/IOPContractsManagerUtils.sol"; import { ISuperchainConfig } from "interfaces/L1/ISuperchainConfig.sol"; import { DevFeatures } from "src/libraries/DevFeatures.sol"; +import { DummyCaller } from "scripts/libraries/DummyCaller.sol"; contract UpgradeSuperchainConfig is Script { struct Input { @@ -31,14 +32,16 @@ contract UpgradeSuperchainConfig is Script { // 2/2 or similar. address prank = _input.prank; - bytes memory code = _getDummyCallerCode(useOPCMv2); + bytes memory code = _getDummyCallerCode(); vm.etch(prank, code); vm.store(prank, bytes32(0), bytes32(uint256(uint160(opcm)))); vm.label(prank, "DummyCaller"); - (bool success,) = _upgrade(prank, useOPCMv2, _input); - require(success, "UpgradeSuperchainConfig: upgradeSuperchainConfig failed"); + // Call into the DummyCaller. This will perform the delegatecall under the hood. + // The DummyCaller uses a fallback that reverts on failure, so no need to check success. + vm.broadcast(msg.sender); + _upgrade(prank, useOPCMv2, _input); } /// @notice Asserts that the input is valid. @@ -50,12 +53,10 @@ contract UpgradeSuperchainConfig is Script { require(address(_input.superchainConfig) != address(0), "UpgradeSuperchainConfig: superchainConfig not set"); } - /// @notice Helper function to get the proper dummy caller code based on the OPCM version. - /// @param _useOPCMv2 Whether to use OPCM v2. + /// @notice Helper function to get the dummy caller code. /// @return code The code of the dummy caller. - function _getDummyCallerCode(bool _useOPCMv2) internal view returns (bytes memory) { - if (_useOPCMv2) return vm.getDeployedCode("UpgradeSuperchainConfig.s.sol:DummyCallerV2"); - else return vm.getDeployedCode("UpgradeSuperchainConfig.s.sol:DummyCaller"); + function _getDummyCallerCode() internal pure returns (bytes memory) { + return type(DummyCaller).runtimeCode; } /// @notice Helper function to upgrade the OPCM based on the OPCM version. Performs the decoding of the upgrade @@ -63,48 +64,20 @@ contract UpgradeSuperchainConfig is Script { /// @param _prank The address of the dummy caller contract. /// @param _useOPCMv2 Whether to use OPCM v2. /// @param _input The input. - /// @return success Whether the upgrade succeeded. - /// @return result The result of the upgrade (bool, bytes memory). - function _upgrade(address _prank, bool _useOPCMv2, Input memory _input) internal returns (bool, bytes memory) { - // Call into the DummyCaller to perform the delegatecall - vm.broadcast(msg.sender); + function _upgrade(address _prank, bool _useOPCMv2, Input memory _input) internal { + bytes memory data; if (_useOPCMv2) { - return DummyCallerV2(_prank).upgradeSuperchain( + data = abi.encodeCall( + IOPContractsManagerV2.upgradeSuperchain, IOPContractsManagerV2.SuperchainUpgradeInput({ superchainConfig: _input.superchainConfig, extraInstructions: _input.extraInstructions }) ); } else { - return DummyCaller(_prank).upgradeSuperchainConfig(_input.superchainConfig); + data = abi.encodeCall(IOPContractsManager.upgradeSuperchainConfig, _input.superchainConfig); } - } -} - -/// @title DummyCaller -/// @notice This contract is used to mimic the contract that is used as the source of the delegatecall to the OPCM. -contract DummyCaller { - address internal _opcmAddr; - - function upgradeSuperchainConfig(ISuperchainConfig _superchainConfig) external returns (bool, bytes memory) { - bytes memory data = abi.encodeCall(IOPContractsManager.upgradeSuperchainConfig, (_superchainConfig)); - (bool success, bytes memory result) = _opcmAddr.delegatecall(data); - return (success, result); - } -} - -/// @title DummyCallerV2 -/// @notice This contract is used to mimic the contract that is used as the source of the delegatecall to the OPCM v2. -/// Uses IOPContractsManagerV2.SuperchainUpgradeInput type for the upgrade input. -contract DummyCallerV2 { - address internal _opcmAddr; - - function upgradeSuperchain(IOPContractsManagerV2.SuperchainUpgradeInput memory _superchainUpgradeInput) - external - returns (bool, bytes memory) - { - bytes memory data = abi.encodeCall(IOPContractsManagerV2.upgradeSuperchain, (_superchainUpgradeInput)); - (bool success, bytes memory result) = _opcmAddr.delegatecall(data); - return (success, result); + (bool success,) = _prank.call(data); + require(success, "UpgradeSuperchainConfig: upgrade failed"); } } diff --git a/packages/contracts-bedrock/scripts/libraries/DummyCaller.sol b/packages/contracts-bedrock/scripts/libraries/DummyCaller.sol new file mode 100644 index 0000000000000..cc98d050b728a --- /dev/null +++ b/packages/contracts-bedrock/scripts/libraries/DummyCaller.sol @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +/// @title DummyCaller +/// @notice Generic delegatecall forwarder. Reads target from storage slot 0, +/// forwards calldata via delegatecall, reverts on failure, returns on success. +/// @dev This contract is used to mimic the contract that is used as the source of the +/// delegatecall to the OPCM. In practice this will be the governance 2/2 or similar. +/// The target address must be stored in storage slot 0 before calling any function. +contract DummyCaller { + address internal _opcmAddr; + + fallback() external { + address target = _opcmAddr; + assembly { + calldatacopy(0, 0, calldatasize()) + let result := delegatecall(gas(), target, 0, calldatasize(), 0, 0) + returndatacopy(0, 0, returndatasize()) + switch result + case 0 { revert(0, returndatasize()) } + default { return(0, returndatasize()) } + } + } +}