diff --git a/README.md b/README.md index 63d9085..c33348d 100644 --- a/README.md +++ b/README.md @@ -38,6 +38,13 @@ export ETHERSCAN_EXPLORER_TOKEN= export OPTISCAN_EXPLORER_TOKEN= ``` +Set your Etherscan API endpoint URL to fetch verified source code, + +```bash +export L1_EXPLORER_API_HOSTNAME= +export L2_EXPLORER_API_HOSTNAME= +``` + Set your Github token to query API without strict rate limiting, ```bash diff --git a/config_samples/optimism/testnet/optimism_testnet_config_L1.json b/config_samples/optimism/testnet/optimism_testnet_config_L1.json index d70fb19..0cbfc53 100644 --- a/config_samples/optimism/testnet/optimism_testnet_config_L1.json +++ b/config_samples/optimism/testnet/optimism_testnet_config_L1.json @@ -1,22 +1,52 @@ { - "contracts": { - "0x4Abf633d9c0F4aEebB4C2E3213c7aa1b8505D332": "OssifiableProxy", - "0x8375029773953d91CaCfa452b7D24556b9F318AA": "L1LidoTokensBridge", - "0x10cA9008D7dcea1Bed4d5394F8c58F3113A2814D": "TokenRateNotifier", - "0x4067B05a6B2f6801Bfb8d4fF417eD32e71c216d9": "OpStackTokenRatePusher" - }, - "explorer_hostname": "api-sepolia.etherscan.io", - "explorer_token_env_var": "ETHERSCAN_EXPLORER_TOKEN", - "github_repo": { - "url": "https://github.com/lidofinance/lido-l2-with-steth", - "commit": "8f19e1101a211c8f3d42af7ffcb87ab0ebcf750c", - "relative_root": "" - }, - "dependencies": { - "@openzeppelin/contracts": { - "url": "https://github.com/OpenZeppelin/openzeppelin-contracts", - "commit": "d4fb3a89f9d0a39c7ee6f2601d33ffbf30085322", - "relative_root": "contracts" - } + "contracts": { + "0x4Abf633d9c0F4aEebB4C2E3213c7aa1b8505D332": "OssifiableProxy", + "0x8375029773953d91CaCfa452b7D24556b9F318AA": "L1LidoTokensBridge", + "0x10cA9008D7dcea1Bed4d5394F8c58F3113A2814D": "TokenRateNotifier", + "0x4067B05a6B2f6801Bfb8d4fF417eD32e71c216d9": "OpStackTokenRatePusher" + }, + "explorer_hostname": "api-sepolia.etherscan.io", + "explorer_token_env_var": "ETHERSCAN_EXPLORER_TOKEN", + "github_repo": { + "url": "https://github.com/lidofinance/lido-l2-with-steth", + "commit": "8f19e1101a211c8f3d42af7ffcb87ab0ebcf750c", + "relative_root": "" + }, + "dependencies": { + "@openzeppelin/contracts": { + "url": "https://github.com/OpenZeppelin/openzeppelin-contracts", + "commit": "d4fb3a89f9d0a39c7ee6f2601d33ffbf30085322", + "relative_root": "contracts" } + }, + "fail_on_comparison_error": true, + "bytecode_comparison": { + "constructor_args": { + "0x4Abf633d9c0F4aEebB4C2E3213c7aa1b8505D332": [ + "0xd497Be005638efCf09F6BFC8DAFBBB0BB72cD991", + "0xd497Be005638efCf09F6BFC8DAFBBB0BB72cD991", + "0x" + ], + "0x8375029773953d91CaCfa452b7D24556b9F318AA": [ + "0x58Cc85b8D04EA49cC6DBd3CbFFd00B4B8D6cb3ef", + "0xdBA2760246f315203F8B716b3a7590F0FFdc704a", + "0xB82381A3fBD3FaFA77B3a7bE693342618240067b", + "0x3e3FE7dBc6B4C189E7128855dD526361c49b40Af", + "0x24B47cd3A74f1799b32B2de11073764Cb1bb318B", + "0xf49D208B5C7b10415C7BeAFe9e656F2DF9eDfe3B", + "0xd497Be005638efCf09F6BFC8DAFBBB0BB72cD991" + ], + "0x10cA9008D7dcea1Bed4d5394F8c58F3113A2814D": [ + "0x8375029773953d91CaCfa452b7D24556b9F318AA", + "0x3e3FE7dBc6B4C189E7128855dD526361c49b40Af" + ], + "0x4067B05a6B2f6801Bfb8d4fF417eD32e71c216d9": [ + "0x58Cc85b8D04EA49cC6DBd3CbFFd00B4B8D6cb3ef", + "0xB82381A3fBD3FaFA77B3a7bE693342618240067b", + "0xd497Be005638efCf09F6BFC8DAFBBB0BB72cD991", + "0xB34F2747BCd9BCC4107A0ccEb43D5dcdd7Fabf89", + 300000 + ] + } + } } diff --git a/config_samples/optimism/testnet/optimism_testnet_config_L2.json b/config_samples/optimism/testnet/optimism_testnet_config_L2.json index 8577011..3e97195 100644 --- a/config_samples/optimism/testnet/optimism_testnet_config_L2.json +++ b/config_samples/optimism/testnet/optimism_testnet_config_L2.json @@ -1,36 +1,85 @@ { "contracts": { - "0xB34F2747BCd9BCC4107A0ccEb43D5dcdd7Fabf89": "OssifiableProxy", - "0xa989A4B3A26e28DC9d106F163B2B1f35153E0517": "TokenRateOracle", - "0x24B47cd3A74f1799b32B2de11073764Cb1bb318B": "OssifiableProxy", - "0x298953B9426eba4F35a137a4754278a16d97A063": "ERC20BridgedPermit", - "0xf49D208B5C7b10415C7BeAFe9e656F2DF9eDfe3B": "OssifiableProxy", - "0xFd21C82c99ddFa56EB0B9B2D1d0709b7E26D1B2C": "ERC20RebasableBridgedPermit", - "0xdBA2760246f315203F8B716b3a7590F0FFdc704a": "OssifiableProxy", - "0xD48c69358193a34aC035ea7dfB70daDea1600112": "L2ERC20ExtendedTokensBridge" + "0xB34F2747BCd9BCC4107A0ccEb43D5dcdd7Fabf89": "OssifiableProxy", + "0xa989A4B3A26e28DC9d106F163B2B1f35153E0517": "TokenRateOracle", + "0x24B47cd3A74f1799b32B2de11073764Cb1bb318B": "OssifiableProxy", + "0x298953B9426eba4F35a137a4754278a16d97A063": "ERC20BridgedPermit", + "0xf49D208B5C7b10415C7BeAFe9e656F2DF9eDfe3B": "OssifiableProxy", + "0xFd21C82c99ddFa56EB0B9B2D1d0709b7E26D1B2C": "ERC20RebasableBridgedPermit", + "0xdBA2760246f315203F8B716b3a7590F0FFdc704a": "OssifiableProxy", + "0xD48c69358193a34aC035ea7dfB70daDea1600112": "L2ERC20ExtendedTokensBridge" }, "explorer_hostname": "api-sepolia-optimistic.etherscan.io", "explorer_token_env_var": "OPTISCAN_EXPLORER_TOKEN", "github_repo": { - "url": "https://github.com/lidofinance/lido-l2", - "commit": "384be204288d04ea8557242cee57d4dc8e521aa4", - "relative_root": "" + "url": "https://github.com/lidofinance/lido-l2-with-steth", + "commit": "8f19e1101a211c8f3d42af7ffcb87ab0ebcf750c", + "relative_root": "" }, "dependencies": { - "@openzeppelin/contracts": { - "url": "https://github.com/OpenZeppelin/openzeppelin-contracts", - "commit": "d4fb3a89f9d0a39c7ee6f2601d33ffbf30085322", - "relative_root": "contracts" - } + "@openzeppelin/contracts": { + "url": "https://github.com/OpenZeppelin/openzeppelin-contracts", + "commit": "d4fb3a89f9d0a39c7ee6f2601d33ffbf30085322", + "relative_root": "contracts" + } }, "fail_on_comparison_error": true, "bytecode_comparison": { - "constructor_args": { - "0xf49D208B5C7b10415C7BeAFe9e656F2DF9eDfe3B": [ - "0xFd21C82c99ddFa56EB0B9B2D1d0709b7E26D1B2C", - "0xf695357C66bA514150Da95b189acb37b46DDe602", - "0xa6487c53000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000174c6971756964207374616b656420457468657220322e300000000000000000000000000000000000000000000000000000000000000000000000000000000005737445544800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000013100000000000000000000000000000000000000000000000000000000000000" - ] - } + "constructor_args": { + "0xB34F2747BCd9BCC4107A0ccEb43D5dcdd7Fabf89": [ + "0xB34F2747BCd9BCC4107A0ccEb43D5dcdd7Fabf89", + "0xB34F2747BCd9BCC4107A0ccEb43D5dcdd7Fabf89", + "0x" + ], + "0xa989A4B3A26e28DC9d106F163B2B1f35153E0517": [ + "0x4200000000000000000000000000000000000007", + "0xdBA2760246f315203F8B716b3a7590F0FFdc704a", + "0x4067B05a6B2f6801Bfb8d4fF417eD32e71c216d9", + 86400, + 172800, + 500, + 86400, + 3600 + ], + "0x24B47cd3A74f1799b32B2de11073764Cb1bb318B": [ + "0x24B47cd3A74f1799b32B2de11073764Cb1bb318B", + "0x24B47cd3A74f1799b32B2de11073764Cb1bb318B", + "0x" + ], + "0x298953B9426eba4F35a137a4754278a16d97A063": [ + "Wrapped liquid staked Ether 2.0", + "wstETH", + "2", + 18, + "0xdBA2760246f315203F8B716b3a7590F0FFdc704a" + ], + "0xf49D208B5C7b10415C7BeAFe9e656F2DF9eDfe3B": [ + "0xf49D208B5C7b10415C7BeAFe9e656F2DF9eDfe3B", + "0xf49D208B5C7b10415C7BeAFe9e656F2DF9eDfe3B", + "0x" + ], + "0xFd21C82c99ddFa56EB0B9B2D1d0709b7E26D1B2C": [ + "Liquid staked Ether 2.0", + "stETH", + "1", + 18, + "0x24B47cd3A74f1799b32B2de11073764Cb1bb318B", + "0xB34F2747BCd9BCC4107A0ccEb43D5dcdd7Fabf89", + "0xdBA2760246f315203F8B716b3a7590F0FFdc704a" + ], + "0xdBA2760246f315203F8B716b3a7590F0FFdc704a": [ + "0xdBA2760246f315203F8B716b3a7590F0FFdc704a", + "0xdBA2760246f315203F8B716b3a7590F0FFdc704a", + "0x" + ], + "0xD48c69358193a34aC035ea7dfB70daDea1600112": [ + "0x4200000000000000000000000000000000000007", + "0x4Abf633d9c0F4aEebB4C2E3213c7aa1b8505D332", + "0xB82381A3fBD3FaFA77B3a7bE693342618240067b", + "0x3e3FE7dBc6B4C189E7128855dD526361c49b40Af", + "0x24B47cd3A74f1799b32B2de11073764Cb1bb318B", + "0xf49D208B5C7b10415C7BeAFe9e656F2DF9eDfe3B" + ] + } } -} \ No newline at end of file +} diff --git a/config_samples/optimism/testnet/optimism_testnet_config_L2_gov.json b/config_samples/optimism/testnet/optimism_testnet_config_L2_gov.json index b6d7599..1fa6aed 100644 --- a/config_samples/optimism/testnet/optimism_testnet_config_L2_gov.json +++ b/config_samples/optimism/testnet/optimism_testnet_config_L2_gov.json @@ -1,20 +1,34 @@ { - "contracts": { - "0xf695357C66bA514150Da95b189acb37b46DDe602": "OptimismBridgeExecutor" - }, - "explorer_hostname": "api-sepolia-optimistic.etherscan.io", - "explorer_token_env_var": "OPTISCAN_EXPLORER_TOKEN", - "github_repo": { - "url": "https://github.com/lidofinance/governance-crosschain-bridges", - "commit": "8fa25b0080dd3dcc2390313631aea6796a12c9d8", - "relative_root": "" - }, - "dependencies": { - "@openzeppelin/contracts": { - "url": "https://github.com/OpenZeppelin/openzeppelin-contracts", - "commit": "d4fb3a89f9d0a39c7ee6f2601d33ffbf30085322", - "relative_root": "contracts", - "//": "OZ 4.6.0" - } + "contracts": { + "0xf695357C66bA514150Da95b189acb37b46DDe602": "OptimismBridgeExecutor" + }, + "explorer_hostname": "api-sepolia-optimistic.etherscan.io", + "explorer_token_env_var": "OPTISCAN_EXPLORER_TOKEN", + "github_repo": { + "url": "https://github.com/lidofinance/governance-crosschain-bridges", + "commit": "8fa25b0080dd3dcc2390313631aea6796a12c9d8", + "relative_root": "" + }, + "dependencies": { + "@openzeppelin/contracts": { + "url": "https://github.com/OpenZeppelin/openzeppelin-contracts", + "commit": "d4fb3a89f9d0a39c7ee6f2601d33ffbf30085322", + "relative_root": "contracts", + "//": "OZ 4.6.0" } + }, + "fail_on_comparison_error": true, + "bytecode_comparison": { + "constructor_args": { + "0xf695357C66bA514150Da95b189acb37b46DDe602": [ + "0x4200000000000000000000000000000000000007", + "0x32A0E5828B62AAb932362a4816ae03b860b65e83", + 0, + 86400, + 0, + 1, + "0x0000000000000000000000000000000000000000" + ] + } + } } diff --git a/config_samples/swell/swell_mainnet_config_L1.json b/config_samples/swell/swell_mainnet_config_L1.json index ee89a2a..a58b383 100644 --- a/config_samples/swell/swell_mainnet_config_L1.json +++ b/config_samples/swell/swell_mainnet_config_L1.json @@ -1,19 +1,19 @@ { - "contracts": { - "0xecf3376512EDAcA4FBB63d2c67d12a0397d24121": "OssifiableProxy", - "0x7e97935FbDF2a27EA35c4fdDdaCf5ACd685e65A2": "L1ERC20TokenBridge" - }, - "explorer_hostname": "api.etherscan.io", - "github_repo": { - "url": "https://github.com/lidofinance/lido-l2", - "commit": "082e7eb59de63bd376b30886568813408d04f00b", - "relative_root": "" - }, - "dependencies": { - "@openzeppelin/contracts": { - "url": "https://github.com/OpenZeppelin/openzeppelin-contracts", - "commit": "d4fb3a89f9d0a39c7ee6f2601d33ffbf30085322", - "relative_root": "contracts" - } + "contracts": { + "0xecf3376512EDAcA4FBB63d2c67d12a0397d24121": "OssifiableProxy", + "0x7e97935FbDF2a27EA35c4fdDdaCf5ACd685e65A2": "L1ERC20TokenBridge" + }, + "explorer_hostname": "api.etherscan.io", + "github_repo": { + "url": "https://github.com/lidofinance/lido-l2", + "commit": "082e7eb59de63bd376b30886568813408d04f00b", + "relative_root": "" + }, + "dependencies": { + "@openzeppelin/contracts": { + "url": "https://github.com/OpenZeppelin/openzeppelin-contracts", + "commit": "d4fb3a89f9d0a39c7ee6f2601d33ffbf30085322", + "relative_root": "contracts" } + } } diff --git a/config_samples/swell/swell_mainnet_config_L2.json b/config_samples/swell/swell_mainnet_config_L2.json index 693f928..85f4dfc 100644 --- a/config_samples/swell/swell_mainnet_config_L2.json +++ b/config_samples/swell/swell_mainnet_config_L2.json @@ -1,21 +1,21 @@ { - "contracts": { - "0x8311496799B8C2C7f13bC32c123ac4Eea068e6F0": "OssifiableProxy", - "0x66ca84bC3C2dB33b6bd7B8994C033444C72b8ADE": "L2ERC20TokenBridge", - "0x7c98E0779EB5924b3ba8cE3B17648539ed5b0Ecc": "OssifiableProxy", - "0xa1A3257813eD45d91e9c45E03C66FcDD54B4e7c1": "ERC20Bridged" - }, - "explorer_hostname": "explorer.swellnetwork.io", - "github_repo": { - "url": "https://github.com/lidofinance/lido-l2", - "commit": "082e7eb59de63bd376b30886568813408d04f00b", - "relative_root": "" - }, - "dependencies": { - "@openzeppelin/contracts": { - "url": "https://github.com/OpenZeppelin/openzeppelin-contracts", - "commit": "d4fb3a89f9d0a39c7ee6f2601d33ffbf30085322", - "relative_root": "contracts" - } + "contracts": { + "0x8311496799B8C2C7f13bC32c123ac4Eea068e6F0": "OssifiableProxy", + "0x66ca84bC3C2dB33b6bd7B8994C033444C72b8ADE": "L2ERC20TokenBridge", + "0x7c98E0779EB5924b3ba8cE3B17648539ed5b0Ecc": "OssifiableProxy", + "0xa1A3257813eD45d91e9c45E03C66FcDD54B4e7c1": "ERC20Bridged" + }, + "explorer_hostname": "explorer.swellnetwork.io", + "github_repo": { + "url": "https://github.com/lidofinance/lido-l2", + "commit": "082e7eb59de63bd376b30886568813408d04f00b", + "relative_root": "" + }, + "dependencies": { + "@openzeppelin/contracts": { + "url": "https://github.com/OpenZeppelin/openzeppelin-contracts", + "commit": "d4fb3a89f9d0a39c7ee6f2601d33ffbf30085322", + "relative_root": "contracts" } + } } diff --git a/config_samples/swell/swell_mainnet_config_L2_gov.json b/config_samples/swell/swell_mainnet_config_L2_gov.json index 58156c4..720460c 100644 --- a/config_samples/swell/swell_mainnet_config_L2_gov.json +++ b/config_samples/swell/swell_mainnet_config_L2_gov.json @@ -1,19 +1,19 @@ { - "contracts": { - "0xFF22ea467301010F1364fc154c13e0c86Fcfb077": "OptimismBridgeExecutor" - }, - "explorer_hostname": "explorer.swellnetwork.io", - "github_repo": { - "url": "https://github.com/lidofinance/governance-crosschain-bridges", - "commit": "8fa25b0080dd3dcc2390313631aea6796a12c9d8", - "relative_root": "" - }, - "dependencies": { - "@openzeppelin/contracts": { - "url": "https://github.com/OpenZeppelin/openzeppelin-contracts", - "commit": "d4fb3a89f9d0a39c7ee6f2601d33ffbf30085322", - "relative_root": "contracts", - "//": "OZ 4.6.0" - } + "contracts": { + "0xFF22ea467301010F1364fc154c13e0c86Fcfb077": "OptimismBridgeExecutor" + }, + "explorer_hostname": "explorer.swellnetwork.io", + "github_repo": { + "url": "https://github.com/lidofinance/governance-crosschain-bridges", + "commit": "8fa25b0080dd3dcc2390313631aea6796a12c9d8", + "relative_root": "" + }, + "dependencies": { + "@openzeppelin/contracts": { + "url": "https://github.com/OpenZeppelin/openzeppelin-contracts", + "commit": "d4fb3a89f9d0a39c7ee6f2601d33ffbf30085322", + "relative_root": "contracts", + "//": "OZ 4.6.0" } + } } diff --git a/diffyscan/diffyscan.py b/diffyscan/diffyscan.py index a7d6259..f731ac5 100644 --- a/diffyscan/diffyscan.py +++ b/diffyscan/diffyscan.py @@ -16,6 +16,7 @@ get_contract_from_explorer, compile_contract_from_explorer, parse_compiled_contract, + get_explorer_hostname, ) from .utils.github import ( get_file_from_github, @@ -103,9 +104,10 @@ def run_source_diff( recursive_parsing=False, prettify=False, ): + explorer_hostname = get_explorer_hostname(config) logger.divider() logger.okay("Contract", contract_address_from_config) - logger.okay("Blockchain explorer Hostname", config["explorer_hostname"]) + logger.okay("Blockchain explorer Hostname", explorer_hostname) logger.okay("Repo", config["github_repo"]["url"]) logger.okay("Repo commit", config["github_repo"]["commit"]) logger.okay("Repo relative root", config["github_repo"]["relative_root"]) @@ -113,7 +115,7 @@ def run_source_diff( logger.divider() logger.info( - f"Fetching source code from blockchain explorer {config['explorer_hostname']} ..." + f"Fetching source code from blockchain explorer {explorer_hostname} ..." ) source_files = ( @@ -264,7 +266,7 @@ def process_config( try: contract_code = get_contract_from_explorer( explorer_token, - config["explorer_hostname"], + get_explorer_hostname(config), contract_address, contract_name, ) @@ -306,7 +308,9 @@ def parse_arguments(): "path", nargs="?", default=None, help="Path to config or directory with configs" ) parser.add_argument( - "--hardhat-path", default=DEFAULT_HARDHAT_CONFIG_PATH, help="Path to Hardhat config" + "--hardhat-path", + default=DEFAULT_HARDHAT_CONFIG_PATH, + help="Path to Hardhat config", ) parser.add_argument( "--yes", diff --git a/diffyscan/utils/compiler.py b/diffyscan/utils/compiler.py index 11d1500..4f0da07 100644 --- a/diffyscan/utils/compiler.py +++ b/diffyscan/utils/compiler.py @@ -25,7 +25,7 @@ def get_solc_native_platform_from_os(): def get_compiler_info(required_platform, required_compiler_version): - compilers_list_url = f"https://raw.githubusercontent.com/ethereum/solc-bin/gh-pages/{required_platform}/list.json" + compilers_list_url = f"https://raw.githubusercontent.com/ethereum/solc-bin/refs/heads/gh-pages/{required_platform}/list.json" available_compilers_list = fetch(compilers_list_url).json() required_build_info = next( ( diff --git a/diffyscan/utils/encoder.py b/diffyscan/utils/encoder.py index a60c237..8b3f393 100644 --- a/diffyscan/utils/encoder.py +++ b/diffyscan/utils/encoder.py @@ -85,7 +85,12 @@ def encode_constructor_arguments(constructor_abi: list, constructor_config_args: arg_value = constructor_config_args[argument_index] if arg_type == "address": constructor_calldata += encode_address(arg_value) - elif arg_type == "uint256" or arg_type == "bool" or arg_type == "uint8": + elif ( + arg_type == "uint256" + or arg_type == "bool" + or arg_type == "uint8" + or arg_type == "uint32" + ): constructor_calldata += to_hex_with_alignment(arg_value) elif arg_type == "bytes32": constructor_calldata += encode_bytes32(arg_value) diff --git a/diffyscan/utils/explorer.py b/diffyscan/utils/explorer.py index b84a467..385e7a4 100644 --- a/diffyscan/utils/explorer.py +++ b/diffyscan/utils/explorer.py @@ -2,7 +2,7 @@ import sys import os -from .common import fetch +from .common import fetch, load_env from .logger import logger from .compiler import ( get_solc_native_platform_from_os, @@ -14,6 +14,7 @@ from .constants import SOLC_DIR from .custom_exceptions import ExplorerError + def _errorNoSourceCodeAndExit(address): logger.error("source code is not verified or an EOA address", address) sys.exit(1) @@ -35,36 +36,37 @@ def _get_contract_from_etherscan(token, etherscan_hostname, contract): solc_input = result["SourceCode"] contract = { - "name": result["ContractName"], - "compiler": result["CompilerVersion"], - } + "name": result["ContractName"], + "compiler": result["CompilerVersion"], + } if solc_input.startswith("{{"): contract["solcInput"] = json.loads(solc_input[1:-1]) else: contract["solcInput":] = { - "language": "Solidity", - "sources": {result["ContractName"]: {"content": solc_input}}, - "settings": { - "optimizer": { - "enabled": result["OptimizationUsed"] == "1", - "runs": int(result["Runs"]), - }, - "outputSelection": { - "*": { - "*": [ - "abi", - "evm.bytecode", - "evm.deployedBytecode", - "evm.methodIdentifiers", - "metadata", - ], - "": ["ast"], - } - }, + "language": "Solidity", + "sources": {result["ContractName"]: {"content": solc_input}}, + "settings": { + "optimizer": { + "enabled": result["OptimizationUsed"] == "1", + "runs": int(result["Runs"]), + }, + "outputSelection": { + "*": { + "*": [ + "abi", + "evm.bytecode", + "evm.deployedBytecode", + "evm.methodIdentifiers", + "metadata", + ], + "": ["ast"], + } }, - } + }, + } return contract + def _get_contract_from_zksync(zksync_explorer_hostname, contract): zksync_explorer_link = ( f"https://{zksync_explorer_hostname}/contract_verification/info/{contract}" @@ -110,9 +112,7 @@ def _get_contract_from_mantle(mantle_explorer_hostname, contract): def _get_contract_from_blockscout(explorer_hostname, contract): - explorer_link = ( - f"https://{explorer_hostname}/api/v2/smart-contracts/{contract}" - ) + explorer_link = f"https://{explorer_hostname}/api/v2/smart-contracts/{contract}" response = fetch(explorer_link).json() if "name" not in response: @@ -125,7 +125,28 @@ def _get_contract_from_blockscout(explorer_hostname, contract): contract = { "name": response["name"], - "solcInput": {"language": "Solidity", "sources": source_files}, + "solcInput": { + "language": "Solidity", + "sources": source_files, + "settings": { + "optimizer": { + "enabled": response["optimization_enabled"] == True, + "runs": int(response["optimization_runs"]), + }, + "outputSelection": { + "*": { + "*": [ + "abi", + "evm.bytecode", + "evm.deployedBytecode", + "evm.methodIdentifiers", + "metadata", + ], + "": ["ast"], + } + }, + }, + }, "compiler": response["compiler_version"], } return contract @@ -141,9 +162,11 @@ def get_contract_from_explorer( result = _get_contract_from_mantle(explorer_hostname, contract_address) elif explorer_hostname.endswith("lineascan.build"): result = _get_contract_from_etherscan(None, explorer_hostname, contract_address) - elif explorer_hostname.endswith("mode.network"): - result = _get_contract_from_blockscout(explorer_hostname, contract_address) - elif explorer_hostname.endswith("swellnetwork.io"): + elif ( + explorer_hostname.endswith("mode.network") + or explorer_hostname.endswith("blockscout.com") + or explorer_hostname.endswith("swellnetwork.io") + ): result = _get_contract_from_blockscout(explorer_hostname, contract_address) else: result = _get_contract_from_etherscan( @@ -159,31 +182,69 @@ def get_contract_from_explorer( return result + def compile_contract_from_explorer(contract_code): required_platform = get_solc_native_platform_from_os() build_name = contract_code["compiler"][1:] build_info = get_compiler_info(required_platform, build_name) - compiler_path = os.path.join(SOLC_DIR, build_info['path']) + compiler_path = os.path.join(SOLC_DIR, build_info["path"]) is_compiler_already_prepared = os.path.isfile(compiler_path) if not is_compiler_already_prepared: - prepare_compiler(required_platform, build_info, compiler_path) + prepare_compiler(required_platform, build_info, compiler_path) input_settings = json.dumps(contract_code["solcInput"]) - compiled_contracts = compile_contracts(compiler_path, input_settings)['contracts'].values() + compiled_contracts = compile_contracts(compiler_path, input_settings)[ + "contracts" + ].values() + + is_compiler_already_prepared = os.path.isfile(compiler_path) - target_contract_name = contract_code['name'] + if not is_compiler_already_prepared: + prepare_compiler(required_platform, build_info, compiler_path) + + input_settings = json.dumps(contract_code["solcInput"]) + compiled_contracts = compile_contracts(compiler_path, input_settings)[ + "contracts" + ].values() + + target_contract_name = contract_code["name"] return get_target_compiled_contract(compiled_contracts, target_contract_name) + def parse_compiled_contract(target_compiled_contract): - contract_creation_code_without_calldata = '0x' + target_compiled_contract['evm']['bytecode']['object'] - deployed_bytecode = '0x' + target_compiled_contract['evm']['deployedBytecode']['object'] + contract_creation_code_without_calldata = ( + "0x" + target_compiled_contract["evm"]["bytecode"]["object"] + ) + deployed_bytecode = ( + "0x" + target_compiled_contract["evm"]["deployedBytecode"]["object"] + ) immutables = {} - if ('immutableReferences' in target_compiled_contract['evm']['deployedBytecode']): - immutable_references = target_compiled_contract['evm']['deployedBytecode']['immutableReferences'] + if "immutableReferences" in target_compiled_contract["evm"]["deployedBytecode"]: + immutable_references = target_compiled_contract["evm"]["deployedBytecode"][ + "immutableReferences" + ] for refs in immutable_references.values(): for ref in refs: - immutables[ref['start']] = ref['length'] + immutables[ref["start"]] = ref["length"] return contract_creation_code_without_calldata, deployed_bytecode, immutables + + +def get_explorer_hostname(config): + explorer_hostname = None + if "explorer_hostname_env_var" in config: + explorer_hostname = load_env( + config["explorer_hostname_env_var"], masked=True, required=False + ) + if explorer_hostname is None: + logger.warn( + f'Failed to find an explorer hostname env in the config ("explorer_hostname_env_var")' + ) + explorer_hostname = config["explorer_hostname"] + if explorer_hostname is None: + logger.warn( + f'Failed to find explorer hostname in the config ("explorer_hostname")' + ) + return explorer_hostname diff --git a/diffyscan/utils/hardhat.py b/diffyscan/utils/hardhat.py index 0ec742f..adc5ba5 100644 --- a/diffyscan/utils/hardhat.py +++ b/diffyscan/utils/hardhat.py @@ -12,7 +12,8 @@ class Hardhat: sub_process = None - TIMEOUT_FOR_CONNECT_SEC = 10 + HARDHAT_START_TIMEOUT_SEC = 60 + HARDHAT_STOP_TIMEOUT_SEC = 60 ATTEMPTS_FOR_CONNECT = 5 def start( @@ -30,18 +31,23 @@ def start( f"Failed to find Hardhat config by path '{hardhat_config_path}'" ) - local_node_command_prefix = ( - f"npx hardhat node --hostname {parsed_url.hostname} " - f"--port {parsed_url.port} " - f"--config {hardhat_config_path} " + hardhat_cmd = [ + "npx", + "hardhat", + "node", + "--hostname", + parsed_url.hostname, + "--port", + str(parsed_url.port), + "--config", + hardhat_config_path, + ] + hardhat_cmd_line_masked = " ".join( + hardhat_cmd + ["--fork", mask_text(remote_rpc_url)] ) + hardhat_cmd.extend(["--fork", remote_rpc_url]) - local_node_command = local_node_command_prefix + f"--fork '{remote_rpc_url}'" - local_node_command_masked = ( - local_node_command_prefix + f"--fork '{mask_text(remote_rpc_url)}'" - ) - - logger.info(f'Trying to start Hardhat: "{local_node_command_masked}"') + logger.info(f'Trying to start Hardhat: "{hardhat_cmd_line_masked}"') is_port_used = self._is_port_in_use_(parsed_url) if is_port_used: answer = input( @@ -56,22 +62,43 @@ def start( if is_port_used: raise HardhatError(f"{parsed_url.netloc} is busy") self.sub_process = subprocess.Popen( - "exec " + local_node_command, - shell=True, - stdout=subprocess.DEVNULL, + hardhat_cmd, + stdout=subprocess.PIPE, stderr=subprocess.PIPE, + preexec_fn=os.setsid, ) - try: - _, errs = self.sub_process.communicate(timeout=self.TIMEOUT_FOR_CONNECT_SEC) - if errs: - raise HardhatError(f"{errs.decode()}") - except subprocess.TimeoutExpired: - self._handle_timeout(parsed_url) + + start_time = time.time() + while time.time() - start_time < self.HARDHAT_START_TIMEOUT_SEC: + output = self.sub_process.stdout.readline().decode().capitalize() + if "WILL BE LOST" in output: + logger.info("Hardhat node is ready") + break + else: + raise HardhatError( + f"Hardhat node seems to have failed to start in {self.HARDHAT_START_TIMEOUT_SEC}" + ) + time.sleep(5) def stop(self): if self.sub_process is not None and self.sub_process.poll() is None: - os.kill(self.sub_process.pid, signal.SIGTERM) - logger.info(f"Hardhat stopped, PID {self.sub_process.pid}") + os.killpg(os.getpgid(self.sub_process.pid), signal.SIGTERM) + try: + self.sub_process.communicate(timeout=self.HARDHAT_STOP_TIMEOUT_SEC) + time.sleep(1) + logger.info(f"Hardhat stopped, PID {self.sub_process.pid}") + except subprocess.TimeoutExpired: + os.killpg(os.getpgid(self.sub_process.pid), signal.SIGKILL) + logger.info( + f"Hardhat process failed to terminate in {self.HARDHAT_STOP_TIMEOUT_SEC} seconds, sent KILL command to PID {self.sub_process.pid}" + ) + try: + self.sub_process.communicate(timeout=self.HARDHAT_STOP_TIMEOUT_SEC) + logger.info(f"Hardhat got KILLed, PID {self.sub_process.pid}") + except subprocess.TimeoutExpired: + logger.error( + f"Hardhat process failed to got KILLed in {self.HARDHAT_STOP_TIMEOUT_SEC} seconds, PID {self.sub_process.pid}" + ) def _is_port_in_use_(self, parsed_url) -> bool: with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: diff --git a/hardhat_configs/automaton_hardhat_config.js b/hardhat_configs/automaton_hardhat_config.js new file mode 100644 index 0000000..22f4f07 --- /dev/null +++ b/hardhat_configs/automaton_hardhat_config.js @@ -0,0 +1,10 @@ +module.exports = { + solidity: "0.8.9", + networks: { + hardhat: { + chainId: Number(process.env.CHAIN_ID), + blockGasLimit: 92000000, + hardfork: "cancun", + } + }, +}; diff --git a/package-lock.json b/package-lock.json index 160243d..ba97953 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5,7 +5,7 @@ "packages": { "": { "dependencies": { - "hardhat": "^2.22.13", + "hardhat": "^2.22.15", "kill-port": "^2.0.1" }, "devDependencies": { @@ -773,74 +773,82 @@ ] }, "node_modules/@nomicfoundation/edr": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/@nomicfoundation/edr/-/edr-0.6.3.tgz", - "integrity": "sha512-hThe5ORR75WFYTXKL0K2AyLDxkTMrG+VQ1yL9BhQYsuh3OIH+3yNDxMz2LjfvrpOrMmJ4kk5NKdFewpqDojjXQ==", + "version": "0.6.4", + "resolved": "https://registry.npmjs.org/@nomicfoundation/edr/-/edr-0.6.4.tgz", + "integrity": "sha512-YgrSuT3yo5ZQkbvBGqQ7hG+RDvz3YygSkddg4tb1Z0Y6pLXFzwrcEwWaJCFAVeeZxdxGfCgGMUYgRVneK+WXkw==", + "license": "MIT", "dependencies": { - "@nomicfoundation/edr-darwin-arm64": "0.6.3", - "@nomicfoundation/edr-darwin-x64": "0.6.3", - "@nomicfoundation/edr-linux-arm64-gnu": "0.6.3", - "@nomicfoundation/edr-linux-arm64-musl": "0.6.3", - "@nomicfoundation/edr-linux-x64-gnu": "0.6.3", - "@nomicfoundation/edr-linux-x64-musl": "0.6.3", - "@nomicfoundation/edr-win32-x64-msvc": "0.6.3" + "@nomicfoundation/edr-darwin-arm64": "0.6.4", + "@nomicfoundation/edr-darwin-x64": "0.6.4", + "@nomicfoundation/edr-linux-arm64-gnu": "0.6.4", + "@nomicfoundation/edr-linux-arm64-musl": "0.6.4", + "@nomicfoundation/edr-linux-x64-gnu": "0.6.4", + "@nomicfoundation/edr-linux-x64-musl": "0.6.4", + "@nomicfoundation/edr-win32-x64-msvc": "0.6.4" }, "engines": { "node": ">= 18" } }, "node_modules/@nomicfoundation/edr-darwin-arm64": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-darwin-arm64/-/edr-darwin-arm64-0.6.3.tgz", - "integrity": "sha512-hqtI7tYDqKG5PDmZ//Z65EH5cgH8VL/SAAu50rpHP7WAVfJWkOCcYbecywwF6nhHdonJbRTDGAeG1/+VOy6zew==", + "version": "0.6.4", + "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-darwin-arm64/-/edr-darwin-arm64-0.6.4.tgz", + "integrity": "sha512-QNQErISLgssV9+qia8sIjRANqtbW8snSDvjspixT/kSQ5ZSGxxctTg7x72wPSrcu8+EBEveIe5uqENIp5GH8HQ==", + "license": "MIT", "engines": { "node": ">= 18" } }, "node_modules/@nomicfoundation/edr-darwin-x64": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-darwin-x64/-/edr-darwin-x64-0.6.3.tgz", - "integrity": "sha512-4fGi79/lyOlRUORhCYsYb3sWqRHuHT7qqzyZfZuNOn8llaxmT1k36xNmvpyg37R8SzjnhT/DzoukSJrs23Ip9Q==", + "version": "0.6.4", + "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-darwin-x64/-/edr-darwin-x64-0.6.4.tgz", + "integrity": "sha512-cjVmREiwByyc9+oGfvAh49IAw+oVJHF9WWYRD+Tm/ZlSpnEVWxrGNBak2bd/JSYjn+mZE7gmWS4SMRi4nKaLUg==", + "license": "MIT", "engines": { "node": ">= 18" } }, "node_modules/@nomicfoundation/edr-linux-arm64-gnu": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-linux-arm64-gnu/-/edr-linux-arm64-gnu-0.6.3.tgz", - "integrity": "sha512-yFFTvGFMhfAvQ1Z2itUh1jpoUA+mVROyVELcaxjIq8fyg602lQmbS+NXkhQ+oaeDgJ+06mSENrHBg4fcfRf9cw==", + "version": "0.6.4", + "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-linux-arm64-gnu/-/edr-linux-arm64-gnu-0.6.4.tgz", + "integrity": "sha512-96o9kRIVD6W5VkgKvUOGpWyUGInVQ5BRlME2Fa36YoNsRQMaKtmYJEU0ACosYES6ZTpYC8U5sjMulvPtVoEfOA==", + "license": "MIT", "engines": { "node": ">= 18" } }, "node_modules/@nomicfoundation/edr-linux-arm64-musl": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-linux-arm64-musl/-/edr-linux-arm64-musl-0.6.3.tgz", - "integrity": "sha512-pOKmd0Fa3a6BHg5qbjbl/jMRELVi9oazbfiuU7Bvgn/dpTK+ID3jwT0SXiuC2zxjmPByWgXL6G9XRf5BPAM2rQ==", + "version": "0.6.4", + "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-linux-arm64-musl/-/edr-linux-arm64-musl-0.6.4.tgz", + "integrity": "sha512-+JVEW9e5plHrUfQlSgkEj/UONrIU6rADTEk+Yp9pbe+mzNkJdfJYhs5JYiLQRP4OjxH4QOrXI97bKU6FcEbt5Q==", + "license": "MIT", "engines": { "node": ">= 18" } }, "node_modules/@nomicfoundation/edr-linux-x64-gnu": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-linux-x64-gnu/-/edr-linux-x64-gnu-0.6.3.tgz", - "integrity": "sha512-3AUferhkLIXtLV63w5GjpHttzdxZ36i656XMy+pkBZbbiqnzIVeKWg6DJv1A94fQY16gB4gqj9CLq4CWvbNN6w==", + "version": "0.6.4", + "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-linux-x64-gnu/-/edr-linux-x64-gnu-0.6.4.tgz", + "integrity": "sha512-nzYWW+fO3EZItOeP4CrdMgDXfaGBIBkKg0Y/7ySpUxLqzut40O4Mb0/+quqLAFkacUSWMlFp8nsmypJfOH5zoA==", + "license": "MIT", "engines": { "node": ">= 18" } }, "node_modules/@nomicfoundation/edr-linux-x64-musl": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-linux-x64-musl/-/edr-linux-x64-musl-0.6.3.tgz", - "integrity": "sha512-fr6bD872WIBXe9YnTDi0CzYepMcYRgSnkVqn0yK4wRnIvKrloWhxXNVY45GVIl51aNZguBnvoA4WEt6HIazs3A==", + "version": "0.6.4", + "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-linux-x64-musl/-/edr-linux-x64-musl-0.6.4.tgz", + "integrity": "sha512-QFRoE9qSQ2boRrVeQ1HdzU+XN7NUgwZ1SIy5DQt4d7jCP+5qTNsq8LBNcqhRBOATgO63nsweNUhxX/Suj5r1Sw==", + "license": "MIT", "engines": { "node": ">= 18" } }, "node_modules/@nomicfoundation/edr-win32-x64-msvc": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-win32-x64-msvc/-/edr-win32-x64-msvc-0.6.3.tgz", - "integrity": "sha512-sn34MvN1ajw2Oq1+Drpxej78Z0HfIzI4p4WlolupAV9dOZKzp2JAIQeLVfZpjIFbF3zuyxLPP4dUBrQoFPEqhA==", + "version": "0.6.4", + "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-win32-x64-msvc/-/edr-win32-x64-msvc-0.6.4.tgz", + "integrity": "sha512-2yopjelNkkCvIjUgBGhrn153IBPLwnsDeNiq6oA0WkeM8tGmQi4td+PGi9jAriUDAkc59Yoi2q9hYA6efiY7Zw==", + "license": "MIT", "engines": { "node": ">= 18" } @@ -2376,13 +2384,14 @@ "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==" }, "node_modules/hardhat": { - "version": "2.22.13", - "resolved": "https://registry.npmjs.org/hardhat/-/hardhat-2.22.13.tgz", - "integrity": "sha512-psVJX4FSXDpSXwsU8OcKTJN04pQEj9cFBMX5OPko+OFwbIoiOpvRmafa954/UaA1934npTj8sV3gaTSdx9bPbA==", + "version": "2.22.15", + "resolved": "https://registry.npmjs.org/hardhat/-/hardhat-2.22.15.tgz", + "integrity": "sha512-BpTGa9PE/sKAaHi4s/S1e9WGv63DR1m7Lzfd60C8gSEchDPfAJssVRSq0MZ2v2k76ig9m0kHAwVLf5teYwu/Mw==", + "license": "MIT", "dependencies": { "@ethersproject/abi": "^5.1.2", "@metamask/eth-sig-util": "^4.0.0", - "@nomicfoundation/edr": "^0.6.3", + "@nomicfoundation/edr": "^0.6.4", "@nomicfoundation/ethereumjs-common": "4.0.4", "@nomicfoundation/ethereumjs-tx": "5.0.4", "@nomicfoundation/ethereumjs-util": "9.0.4", diff --git a/package.json b/package.json index 56c4683..0d06a53 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,7 @@ "@commitlint/config-conventional": "^19.5.0" }, "dependencies": { - "hardhat": "^2.22.13", + "hardhat": "^2.22.17", "kill-port": "^2.0.1" }, "packageManager": "npm@10.8.2",