diff --git a/packages/contracts-bedrock/scripts/deploy/VerifyOPCM.s.sol b/packages/contracts-bedrock/scripts/deploy/VerifyOPCM.s.sol index 3da017d5b70..1186b465bdd 100644 --- a/packages/contracts-bedrock/scripts/deploy/VerifyOPCM.s.sol +++ b/packages/contracts-bedrock/scripts/deploy/VerifyOPCM.s.sol @@ -472,20 +472,8 @@ contract VerifyOPCM is Script { // If requested and this is not a blueprint, we also need to check the creation code. if (!_target.blueprint && !_skipConstructorVerification) { - // Use the Etherscan API to get the creation code. - bytes memory actualCreationCode = bytes( - Process.bash( - string.concat( - "curl -s 'https://api.etherscan.io/v2/api?chainid=", - vm.toString(block.chainid), - "&module=contract&action=getcontractcreation&contractaddresses=", - vm.toString(_target.addr), - "&apikey=", - Config.etherscanApiKey(), - "' | jq -r '.result[0].creationBytecode'" - ) - ) - ); + // Get the creation code from the selected block explorer. + bytes memory actualCreationCode = _getCreationCode(_target.addr); // Verify that the artifact bytecode is a prefix of the actual creation code and // extract any remaining bytes so we can verify the constructor arguments. @@ -523,6 +511,40 @@ contract VerifyOPCM is Script { return success; } + /// @notice Gets the creation code for a given contract address from the configured block explorer. + /// @param _addr The address of the contract to get the creation code for. + /// @return The creation code of the contract. + function _getCreationCode(address _addr) internal returns (bytes memory) { + // Prepare the command to execute. + string memory cmd; + + // Check which block explorer to use. + if (LibString.eq(Config.blockExplorer(), "blockscout")) { + console.log(" Fetching creation code from Blockscout..."); + cmd = string.concat( + "curl -s '", + Config.blockscoutApiUrl(), + "/api?module=contract&action=getcontractcreation&contractaddresses=", + vm.toString(_addr), + "' | jq -r '.result[0].creationBytecode'" + ); + } else { + console.log(" Fetching creation code from Etherscan..."); + cmd = string.concat( + "curl -s 'https://api.etherscan.io/v2/api?chainid=", + vm.toString(block.chainid), + "&module=contract&action=getcontractcreation&contractaddresses=", + vm.toString(_addr), + "&apikey=", + Config.etherscanApiKey(), + "' | jq -r '.result[0].creationBytecode'" + ); + } + + // Execute the command. + return bytes(Process.bash(cmd)); + } + /// @notice Checks if V2 dispute games feature is enabled in the dev feature bitmap. /// @param _opcm The OPContractsManager to check. /// @return True if V2 dispute games are enabled. diff --git a/packages/contracts-bedrock/scripts/libraries/Config.sol b/packages/contracts-bedrock/scripts/libraries/Config.sol index 739fe88e6fb..56f1ed9d1e3 100644 --- a/packages/contracts-bedrock/scripts/libraries/Config.sol +++ b/packages/contracts-bedrock/scripts/libraries/Config.sol @@ -129,6 +129,41 @@ library Config { env_ = vm.envString("ETHERSCAN_API_KEY"); } + /// @notice Returns the block explorer to use for fetching creation code. + function blockExplorer() internal view returns (string memory env_) { + env_ = vm.envOr("BLOCK_EXPLORER", string("blockscout")); + } + + /// @notice Returns the base URL for the Blockscout API. + function blockscoutApiUrl() internal view returns (string memory) { + string memory envUrl = vm.envOr("BLOCKSCOUT_API_URL", string("")); + if (bytes(envUrl).length > 0) { + return envUrl; + } + + if (block.chainid == 1) { + // Ethereum + return "https://eth.blockscout.com"; + } else if (block.chainid == 10) { + // OP Mainnet + return "https://explorer.optimism.io"; + } else if (block.chainid == 11155111) { + // Sepolia + return "https://eth-sepolia.blockscout.com"; + } else if (block.chainid == 8453) { + // Base + return "https://base.blockscout.com"; + } else if (block.chainid == 84532) { + // Base Sepolia + return "https://base-sepolia.blockscout.com"; + } else if (block.chainid == 11155420) { + // OP Sepolia + return "https://optimism-sepolia.blockscout.com"; + } else { + return ""; + } + } + /// @notice Returns the OutputMode for genesis allocs generation. /// It reads the mode from the environment variable OUTPUT_MODE. /// If it is unset, OutputMode.ALL is returned.