From 4304dfe211f964576a555fb63dfed1e2dc035f2b Mon Sep 17 00:00:00 2001 From: alexvozhak Date: Sun, 7 Jul 2024 16:24:37 +0300 Subject: [PATCH 001/118] Added binary verificator --- diffyscan/diffyscan.py | 76 ++++---- diffyscan/utils/binary_verifier.py | 277 +++++++++++++++++++++++++++++ diffyscan/utils/common.py | 29 ++- diffyscan/utils/explorer.py | 115 ++++++++---- diffyscan/utils/github.py | 8 +- 5 files changed, 420 insertions(+), 85 deletions(-) create mode 100644 diffyscan/utils/binary_verifier.py diff --git a/diffyscan/diffyscan.py b/diffyscan/diffyscan.py index f53d9cf..104d177 100644 --- a/diffyscan/diffyscan.py +++ b/diffyscan/diffyscan.py @@ -5,14 +5,15 @@ import os import subprocess import tempfile - -from .utils.common import load_config, load_env -from .utils.constants import DIFFS_DIR, START_TIME, DEFAULT_CONFIG_PATH -from .utils.explorer import get_contract_from_explorer -from .utils.github import get_file_from_github, get_file_from_github_recursive, resolve_dep -from .utils.helpers import create_dirs -from .utils.logger import logger - +import shutil + +from utils.common import load_config, load_env +from utils.constants import DIFFS_DIR, START_TIME, DEFAULT_CONFIG_PATH +from utils.explorer import get_contract_from_explorer +from utils.github import get_file_from_github, get_file_from_github_recursive, resolve_dep +from utils.helpers import create_dirs +from utils.logger import logger +from utils.binary_verifier import * __version__ = "0.0.0" @@ -32,10 +33,17 @@ def prettify_solidity(solidity_contract_content: str): with open(github_file_name, "r") as fp: return fp.read() +def run_binary_diff(contract_address, code): + logger.info(f'Started binary checking') + bytecode_from_etherscan, immutables = get_bytecode_from_etherscan(code) + + bytecode_from_blockchain = get_bytecode_from_blockchain(contract_address) + + match(bytecode_from_blockchain, bytecode_from_etherscan, immutables) -def run_diff(config, name, address, explorer_api_token, github_api_token, recursive_parsing=False, prettify=False): +def run_source_diff(contract_address_from_config, contract_code, config, github_api_token, recursive_parsing=False, prettify=False): logger.divider() - logger.okay("Contract", address) + logger.okay("Contract", contract_address_from_config) logger.okay("Blockchain explorer Hostname", config["explorer_hostname"]) logger.okay("Repo", config["github_repo"]["url"]) logger.okay("Repo commit", config["github_repo"]["commit"]) @@ -46,21 +54,10 @@ def run_diff(config, name, address, explorer_api_token, github_api_token, recurs logger.info( f"Fetching source code from blockchain explorer {config['explorer_hostname']} ..." ) - contract_name, source_files = get_contract_from_explorer( - token=explorer_api_token, - explorer_hostname=config["explorer_hostname"], - contract=address, - ) - - if contract_name != name: - logger.error( - "Contract name in config does not match with Blockchain explorer", - f"{address}: {name} != {contract_name}", - ) - sys.exit(1) + source_files = contract_code["solcInput"].items() if not "sources" in contract_code["solcInput"] else contract_code["solcInput"]["sources"].items() files_count = len(source_files) - logger.okay("Contract", contract_name) + logger.okay("Contract", contract_code["name"]) logger.okay("Files", files_count) if not g_skip_user_input: @@ -114,7 +111,7 @@ def run_diff(config, name, address, explorer_api_token, github_api_token, recurs explorer_lines = explorer_content.splitlines() diff_html = difflib.HtmlDiff().make_file(github_lines, explorer_lines) - diff_report_filename = f"{DIFFS_DIR}/{address}/{filename}.html" + diff_report_filename = f"{DIFFS_DIR}/{contract_address_from_config}/{filename}.html" create_dirs(diff_report_filename) with open(diff_report_filename, "w") as f: @@ -144,8 +141,7 @@ def run_diff(config, name, address, explorer_api_token, github_api_token, recurs logger.report_table(report) - -def process_config(path: str, recursive_parsing: bool, unify_formatting: bool): +def process_config(path: str, recursive_parsing: bool, unify_formatting: bool, binary_check: bool, autoclean: bool): logger.info(f"Loading config {path}...") config = load_config(path) @@ -155,10 +151,16 @@ def process_config(path: str, recursive_parsing: bool, unify_formatting: bool): explorer_token = load_env(config["explorer_token_env_var"], masked=True, required=False) contracts = config["contracts"] - logger.info(f"Running diff for contracts from config {contracts}...") - for address, name in config["contracts"].items(): - run_diff(config, name, address, explorer_token, github_api_token, recursive_parsing, unify_formatting) - + for contract_address, contract_name in contracts.items(): + contract_code = get_contract_from_explorer(explorer_token, config["explorer_hostname"], contract_address, contract_name) + run_source_diff(contract_address, contract_code, config, github_api_token, recursive_parsing, unify_formatting) + if (binary_check): + run_binary_diff(contract_address, contract_code) + + if (autoclean): + builds_dir_path = os.getenv('SOLC_DIR', 'solc') + shutil.rmtree(builds_dir_path) + logger.okay(f'{builds_dir_path} deleted') def parse_arguments(): parser = argparse.ArgumentParser() @@ -171,31 +173,31 @@ def parse_arguments(): action=argparse.BooleanOptionalAction, ) parser.add_argument("--prettify", "-p", help="Unify formatting by prettier before comparing", action="store_true") + parser.add_argument("--binary-check", "-binary", help="Match contracts by binaries such as verify-bytecode.ts", default=True) + parser.add_argument("--autoclean", "-clean", help="Autoclean build dir after work", default=True) return parser.parse_args() - - + + def main(): global g_skip_user_input args = parse_arguments() g_skip_user_input = args.yes - if args.version: print(f"Diffyscan {__version__}") return - logger.info("Welcome to Diffyscan!") logger.divider() if args.path is None: - process_config(DEFAULT_CONFIG_PATH, args.support_brownie, args.prettify) + process_config(DEFAULT_CONFIG_PATH, args.support_brownie, args.prettify, args.binary_check, args.autoclean) elif os.path.isfile(args.path): - process_config(args.path, args.support_brownie, args.prettify) + process_config(args.path, args.support_brownie, args.prettify, args.binary_check, args.autoclean) elif os.path.isdir(args.path): for filename in os.listdir(args.path): config_path = os.path.join(args.path, filename) if os.path.isfile(config_path): - process_config(config_path, args.support_brownie, args.prettify) + process_config(config_path, args.support_brownie, args.prettify, args.binary_check, args.autoclean) else: logger.error(f"Specified config path {args.path} not found") sys.exit(1) diff --git a/diffyscan/utils/binary_verifier.py b/diffyscan/utils/binary_verifier.py new file mode 100644 index 0000000..0992606 --- /dev/null +++ b/diffyscan/utils/binary_verifier.py @@ -0,0 +1,277 @@ +import hashlib +import subprocess +import json +import os +import stat + + +from utils.common import fetch, pull, get_solc_native_platform_from_os +from utils.helpers import create_dirs +from utils.logger import logger + +RPC_URL = os.getenv('RPC_URL', '') +if not RPC_URL: + raise ValueError('RPC_URL variable is not set') + +def get_compiler_info(platform, required_compiler_version): + compilers_list_url = ( + f'https://raw.githubusercontent.com/ethereum/solc-bin/gh-pages/{platform}/list.json' + ) + available_compilers_list = fetch(compilers_list_url).json() + required_build_info = next((compiler for compiler in available_compilers_list['builds'] if compiler['longVersion'] == required_compiler_version), None) + + if not required_build_info: + raise ValueError(f'Required compiler version for "{platform}" is not found') + + return required_build_info + +def download_compiler(platform, build_info, destination_path): + compiler_url = ( + f'https://binaries.soliditylang.org/{platform}/{build_info["path"]}' + ) + download_compiler_response = fetch(compiler_url) + + with open(destination_path, 'wb') as compiler_file: + compiler_file.write(download_compiler_response.content) + return download_compiler_response.content + +def check_compiler_checksum(compiler, valid_checksum): + compiler_checksum = hashlib.sha256(compiler).hexdigest() + if compiler_checksum != valid_checksum: + raise ValueError(f'Bad checksum') + +def set_compiler_executable(compiler_path): + compiler_file_rights = os.stat(compiler_path) + os.chmod(compiler_path, compiler_file_rights.st_mode | stat.S_IEXEC) + +def compile_contract(compiler_path, input_settings): + process = subprocess.run([compiler_path,'--standard-json'], input=input_settings.encode(), capture_output=True, check=True, timeout=30) + return json.loads(process.stdout) + +def prepare_compiler(platform, build_info, compiler_path): + create_dirs(compiler_path) + compiler_binary = download_compiler(platform, build_info, compiler_path) + valid_checksum = build_info['sha256'][2:]; + check_compiler_checksum(compiler_binary, valid_checksum) + set_compiler_executable(compiler_path) + +def get_target_contract(compilation_result, target_contract_name): + contracts = compilation_result['contracts'].values(); + contracts_to_check = [] + for contracts in compilation_result['contracts'].values(): + for name, contract in contracts.items(): + if name == target_contract_name: + contracts_to_check.append(contract) + + if len(contracts_to_check) != 1: + raise ValueError('multiple contracts with the same name') + + logger.info('Contracts were successfully compiled. The target contract is ready for matching') + + return contracts_to_check[0] + +def get_bytecode_from_etherscan(code): + platform = get_solc_native_platform_from_os() + build_name = code["compiler"][1:] + build_info = get_compiler_info(platform, build_name) + compilers_dir_path = os.getenv('SOLC_DIR', 'solc') + compiler_path = os.path.join(compilers_dir_path, build_info['path']) + + is_compiler_already_prepared = os.path.isfile(compiler_path) + + if not is_compiler_already_prepared: + prepare_compiler(platform, build_info, compiler_path) + + input_settings = json.dumps(code["solcInput"]) + contract_name = code['name'] + + compilation_result = compile_contract(compiler_path, input_settings) + + target_contract = get_target_contract(compilation_result, contract_name) + + expected_bytecode = target_contract['evm']['deployedBytecode']['object'] + + immutables = {} + if ('immutableReferences' in target_contract['evm']['deployedBytecode']): + immutable_references = target_contract['evm']['deployedBytecode']['immutableReferences'] + for refs in immutable_references.values(): + for ref in refs: + immutables[ref['start']] = ref['length'] + + return expected_bytecode, immutables + +def get_bytecode_from_blockchain(contract_address): + logger.info(f'Retrieving the bytecode by contract address "{contract_address}" from the blockchain...') + + payload = json.dumps({'id': 1, 'jsonrpc': '2.0', 'method': 'eth_getCode', 'params': [contract_address, 'latest']}) + sources_url_response = pull(RPC_URL, payload) + + sources_url_response_in_json = sources_url_response.json() + if 'result' in sources_url_response_in_json: + bytecode = sources_url_response_in_json['result'] + else: + raise ValueError(f'Failed to find section "result" in response') + + logger.okay(f'Bytecode was successfully fetched') + + return bytecode + + + +OPCODES = { + 0x00: 'STOP', 0x01: 'ADD', 0x02: 'MUL', 0x03: 'SUB', 0x04: 'DIV', 0x05: 'SDIV', + 0x06: 'MOD', 0x07: 'SMOD', 0x08: 'ADDMOD', 0x09: 'MULMOD', 0x0A: 'EXP', 0x0B: 'SIGNEXTEND', + 0x10: 'LT', 0x11: 'GT', 0x12: 'SLT', 0x13: 'SGT', 0x14: 'EQ', 0x15: 'ISZERO', 0x16: 'AND', + 0x17: 'OR', 0x18: 'XOR', 0x19: 'NOT', 0x1A: 'BYTE', 0x1B: 'SHL', 0x1C: 'SHR', 0x1D: 'SAR', + 0x20: 'SHA3', + 0x30: 'ADDRESS', 0x31: 'BALANCE', 0x32: 'ORIGIN', 0x33: 'CALLER', + 0x34: 'CALLVALUE', 0x35: 'CALLDATALOAD', 0x36: 'CALLDATASIZE', 0x37: 'CALLDATACOPY', + 0x38: 'CODESIZE', 0x39: 'CODECOPY', 0x3A: 'GASPRICE', 0x3B: 'EXTCODESIZE', + 0x3C: 'EXTCODECOPY', 0x3D: 'RETURNDATASIZE', 0x3E: 'RETURNDATACOPY', 0x3F: 'EXTCODEHASH', + 0x40: 'BLOCKHASH', 0x41: 'COINBASE', 0x42: 'TIMESTAMP', 0x43: 'NUMBER', + 0x44: 'PREVRANDAO', 0x45: 'GASLIMIT', 0x46: 'CHAINID', 0x47: 'SELFBALANCE', 0x48: 'BASEFEE', + 0x50: 'POP', 0x51: 'MLOAD', 0x52: 'MSTORE', 0x53: 'MSTORE8', + 0x54: 'SLOAD', 0x55: 'SSTORE', 0x56: 'JUMP', 0x57: 'JUMPI', + 0x58: 'PC', 0x59: 'MSIZE', 0x5A: 'GAS', 0x5B: 'JUMPDEST', + 0x5F: 'PUSH0', 0x60: 'PUSH1', 0x61: 'PUSH2', 0x62: 'PUSH3', 0x63: 'PUSH4', 0x64: 'PUSH5', + 0x65: 'PUSH6', 0x66: 'PUSH7', 0x67: 'PUSH8', 0x68: 'PUSH9', 0x69: 'PUSH10', 0x6A: 'PUSH11', + 0x6B: 'PUSH12', 0x6C: 'PUSH13', 0x6D: 'PUSH14', 0x6E: 'PUSH15', 0x6F: 'PUSH16', 0x70: 'PUSH17', + 0x71: 'PUSH18', 0x72: 'PUSH19', 0x73: 'PUSH20', 0x74: 'PUSH21', 0x75: 'PUSH22', 0x76: 'PUSH23', + 0x77: 'PUSH24', 0x78: 'PUSH25', 0x79: 'PUSH26', 0x7A: 'PUSH27', 0x7B: 'PUSH28', 0x7C: 'PUSH29', + 0x7D: 'PUSH30', 0x7E: 'PUSH31', 0x7F: 'PUSH32', + 0x80: 'DUP1', 0x81: 'DUP2', 0x82: 'DUP3', 0x83: 'DUP4', + 0x84: 'DUP5', 0x85: 'DUP6', 0x86: 'DUP7', 0x87: 'DUP8', + 0x88: 'DUP9', 0x89: 'DUP10', 0x8A: 'DUP11', 0x8B: 'DUP12', + 0x8C: 'DUP13', 0x8D: 'DUP14', 0x8E: 'DUP15', 0x8F: 'DUP16', + 0x90: 'SWAP1', 0x91: 'SWAP2', 0x92: 'SWAP3', 0x93: 'SWAP4', + 0x94: 'SWAP5', 0x95: 'SWAP6', 0x96: 'SWAP7', 0x97: 'SWAP8', + 0x98: 'SWAP9', 0x99: 'SWAP10', 0x9A: 'SWAP11', 0x9B: 'SWAP12', + 0x9C: 'SWAP13', 0x9D: 'SWAP14', 0x9E: 'SWAP15', 0x9F: 'SWAP16', + 0xA0: 'LOG0', 0xA1: 'LOG1', 0xA2: 'LOG2', 0xA3: 'LOG3', 0xA4: 'LOG4', + 0xF0: 'CREATE', 0xF1: 'CALL', 0xF2: 'CALLCODE', 0xF3: 'RETURN', 0xF4: 'DELEGATECALL', + 0xF5: 'CREATE2', 0xFA: 'STATICCALL', 0xFD: 'REVERT', 0xFE: 'INVALID', 0xFF: 'SELFDESTRUCT', +} + +def parse(bytecode): + instructions = [] + buffer = bytearray.fromhex(bytecode[2:] if bytecode.startswith('0x') else bytecode) + i = 0 + while i < len(buffer): + opcode = buffer[i] + length = 1 + (opcode >= 0x5f and opcode <= 0x7f) * (opcode - 0x5f) + instructions.append({ + 'start': i, + 'length': length, + 'op': {'name': OPCODES.get(opcode, 'INVALID'), 'code': opcode}, + 'bytecode': buffer[i:i + length].hex() + }) + i += length + return instructions + +def match(actualBytecode, expectedBytecode, immutables): + logger.info('Comparing actual code with the expected one:') + + actualInstructions = parse(actualBytecode) + expectedInstructions = parse(expectedBytecode) + maxInstructionsCount = max(len(actualInstructions), len(expectedInstructions)) + + differences = [] + for i in range(maxInstructionsCount): + actual = actualInstructions[i] if i < len(actualInstructions) else None + expected = expectedInstructions[i] if i < len(expectedInstructions) else None + if not actual and not expected: + raise ValueError('Invalid instructions data') + elif actual.get('bytecode') != expected.get('bytecode'): + differences.append(i) + + if not differences: + logger.okay(f'Bytecodes are fully matched') + return + logger.warn(f'Bytecodes have differences') + + nearLinesCount = 3 + checkpoints = {0, *differences} + + if actualInstructions: + checkpoints.add(len(actualInstructions) - 1) + + if expectedInstructions: + checkpoints.add(len(expectedInstructions) - 1) + + for ind in list(checkpoints): + startIndex = max(0, ind - nearLinesCount) + lastIndex = min(ind + nearLinesCount, maxInstructionsCount - 1) + for i in range(startIndex, lastIndex + 1): + checkpoints.add(i) + + checkpointsArray = sorted(list(checkpoints)) + + def hex(index, padStart=2): + return f'{index:0{padStart}X}' + + def red(text): + return f'\u001b[31m{text}\x1b[0m' + + def bgRed(text): + return f'\u001b[37;41m{text}\x1b[0m' + + def green(text): + return f'\u001b[32m{text}\x1b[0m' + + def bgGreen(text): + return f'\u001b[37;42m{text}\x1b[0m' + + def bgYellow(text): + return f'\u001b[37;43m{text}\x1b[0m' + + logger.divider() + logger.info(f'0000 00 STOP - both expected and actual bytecode instructions match') + logger.info(f'{bgRed("0x0002")} - the actual bytecode differs') + logger.info(f'{bgYellow("0x0001")} - the actual bytecode differs on the immutable reference position') + logger.info(f'{bgGreen("0x0003")} - the expected bytecode value when it doesn\'t match the actual one') + logger.info(f'{red("0000 00 STOP")} - the actual bytecode instruction doesn\'t exist, but expected is present') + logger.info(f'{green("0000 00 STOP")} - the actual bytecode instruction exists when the expected doesn\'t') + logger.divider() + for i in range(len(checkpointsArray)): + currInd = checkpointsArray[i] + prevInd = checkpointsArray[i - 1] if i > 0 else None + if prevInd and prevInd != currInd - 1: + print('...') + + actual = actualInstructions[currInd] if currInd < len(actualInstructions) else None + expected = expectedInstructions[currInd] if currInd < len(expectedInstructions) else None + + if not actual and expected: + params = '0x' + expected['bytecode'][2:] + print(red(f'{hex(currInd, 4)} {hex(expected["op"]["code"])} {expected["op"]["name"]} {params}')) + elif actual and not expected: + params = '0x' + actual['bytecode'][2:] + print(green(f'{hex(currInd, 4)} {hex(actual["op"]["code"])} {actual["op"]["name"]} {params}')) + elif actual and expected: + opcode = hex(actual["op"]["code"]) if actual["op"]["code"] == expected["op"]["code"] else bgRed(hex(actual["op"]["code"])) + ' ' + bgGreen(hex(expected["op"]["code"])) + opname = actual["op"]["name"] if actual["op"]["name"] == expected["op"]["name"] else bgRed(actual["op"]["name"]) + ' ' + bgGreen(expected["op"]["name"]) + actualParams = '0x' + actual['bytecode'][2:] if len(actual['bytecode']) > 2 else '' + expectedParams = '0x' + expected['bytecode'][2:] if len(expected['bytecode']) > 2 else '' + + paramsLength = len(expected['bytecode']) // 2 - 1 + isImmutable = immutables.get(expected['start'] + 1) == paramsLength + params = actualParams if actualParams == expectedParams else (bgYellow(actualParams) + ' ' + bgGreen(expectedParams) if isImmutable else bgRed(actualParams) + ' ' + bgGreen(expectedParams)) + print(f'{hex(currInd, 4)} {opcode} {opname} {params}') + else: + raise ValueError('Invalid bytecode difference data') + +def parse(bytecode): + buffer = bytes.fromhex(bytecode[2:] if bytecode.startswith('0x') else bytecode) + instructions = [] + i = 0 + while i < len(buffer): + opcode = buffer[i] + length = 1 + (opcode - 0x5f if 0x5f <= opcode <= 0x7f else 0) + instructions.append({ + 'start': i, + 'length': length, + 'op': {'name': OPCODES.get(opcode, 'INVALID'), 'code': opcode}, + 'bytecode': buffer[i:i+length].hex() + }) + i += length + return instructions \ No newline at end of file diff --git a/diffyscan/utils/common.py b/diffyscan/utils/common.py index afae8fe..00e9f6f 100644 --- a/diffyscan/utils/common.py +++ b/diffyscan/utils/common.py @@ -8,7 +8,6 @@ from .logger import logger from .types import Config - def load_env(variable_name, required=True, masked=False): value = os.getenv(variable_name, default=None) @@ -30,11 +29,7 @@ def load_config(path: str) -> Config: with open(path, mode="r") as config_file: return json.load(config_file) - -def fetch(url, headers={}): - logger.log(f"Fetch: {url}") - response = requests.get(url, headers=headers) - +def handle_response(response, url): if response.status_code == 404: return None @@ -43,9 +38,20 @@ def fetch(url, headers={}): logger.error("Status", response.status_code) logger.error("Response", response.text) - return response.json() + return response +def fetch(url, headers={}): + logger.log(f"Fetch: {url}") + response = requests.get(url, headers=headers) + + return handle_response(response, url) +def pull(url, payload={}): + logger.log(f"Pull: {url}") + response = requests.post(url, data=payload) + + return handle_response(response, url) + def mask_text(text, mask_start=3, mask_end=3): text_length = len(text) mask = "*" * (text_length - mask_start - mask_end) @@ -57,3 +63,12 @@ def parse_repo_link(repo_link): repo_location = [item.strip("/") for item in parse_result[2].split("tree")] user_slash_repo = repo_location[0] return user_slash_repo + +def get_solc_native_platform_from_os(): + platform_name = sys.platform + if platform_name == 'linux': + return 'linux-amd64' + elif platform_name == 'darwin': + return 'macosx-amd64' + else: + raise ValueError(f'Unsupported platform {platform_name}') \ No newline at end of file diff --git a/diffyscan/utils/explorer.py b/diffyscan/utils/explorer.py index a3fa6f6..9a8e686 100644 --- a/diffyscan/utils/explorer.py +++ b/diffyscan/utils/explorer.py @@ -11,40 +11,67 @@ def _errorNoSourceCodeAndExit(address): def _get_contract_from_etherscan(token, etherscan_hostname, contract): - etherscan_link = f"https://{etherscan_hostname}/api?module=contract&action=getsourcecode&address={contract}" + etherscan_link = ( + f"https://{etherscan_hostname}/api?module=contract&action=getsourcecode&address={contract}" + ) if token is not None: etherscan_link = f"{etherscan_link}&apikey={token}" - response = fetch(etherscan_link) + response = fetch(etherscan_link).json() if response["message"] == "NOTOK": - logger.error("Failed", response["result"]) - logger.error("Status", response.status_code) - logger.error("Response", response.text) - sys.exit(1) + raise ValueError(response["result"]) - data = response["result"][0] - if "ContractName" not in data: + result = response["result"][0] + if "ContractName" not in result: _errorNoSourceCodeAndExit(contract) - contract_name = data["ContractName"] - - json_escaped = data["SourceCode"].startswith("{{") - source_files = ( - json.loads(data["SourceCode"][1:-1])["sources"].items() - if json_escaped - else json.loads(data["SourceCode"]).items() - ) - - return (contract_name, source_files) - + solc_input = result['SourceCode'] + + if solc_input.startswith('{{'): + return { + 'name': result['ContractName'], + 'solcInput': json.loads(solc_input[1:-1]), + 'compiler': result['CompilerVersion'] + } + else: + return { + 'name': result['ContractName'], + 'compiler': result['CompilerVersion'], + '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'] + } + } + } + } + } def _get_contract_from_zksync(zksync_explorer_hostname, contract): zksync_explorer_link = ( f"https://{zksync_explorer_hostname}/contract_verification/info/{contract}" ) - response = fetch(zksync_explorer_link) + response = fetch(zksync_explorer_link).json if not response.get("verifiedAt"): logger.error("Status", response.status_code) @@ -55,16 +82,17 @@ def _get_contract_from_zksync(zksync_explorer_hostname, contract): if "contractName" not in data: _errorNoSourceCodeAndExit(contract) - contract_name = data["contractName"].split(":")[-1] - source_files = data["sourceCode"]["sources"].items() - - return (contract_name, source_files) - + return { + 'name': data['ContractName'], + 'sources': json.loads(data["sourceCode"]["sources"]), + 'compiler': data["CompilerVersion"] + } def _get_contract_from_mantle(mantle_explorer_hostname, contract): - etherscan_link = f"https://{mantle_explorer_hostname}/api?module=contract&action=getsourcecode&address={contract}" - - response = fetch(etherscan_link) + etherscan_link = ( + f"https://{mantle_explorer_hostname}/api?module=contract&action=getsourcecode&address={contract}" + ) + response = fetch(etherscan_link).json data = response["result"][0] if "ContractName" not in data: @@ -74,14 +102,27 @@ def _get_contract_from_mantle(mantle_explorer_hostname, contract): for entry in data.get("AdditionalSources", []): source_files.append((entry["Filename"], {"content": entry["SourceCode"]})) - return (data["ContractName"], source_files) - + return { + 'name': data['ContractName'], + 'sources': json.loads(data["sourceCode"]["sources"]), + 'compiler': data["CompilerVersion"] + } -def get_contract_from_explorer(token, explorer_hostname, contract): +def get_contract_from_explorer(token, explorer_hostname, contract_address, contract_name_from_config): + result = {} if explorer_hostname.startswith("zksync"): - return _get_contract_from_zksync(explorer_hostname, contract) - if explorer_hostname.endswith("mantle.xyz"): - return _get_contract_from_mantle(explorer_hostname, contract) - if explorer_hostname.endswith("lineascan.build"): - return _get_contract_from_etherscan(None, explorer_hostname, contract) - return _get_contract_from_etherscan(token, explorer_hostname, contract) + result = _get_contract_from_zksync(explorer_hostname, contract_address) + elif explorer_hostname.endswith("mantle.xyz"): + 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) + else: + result =_get_contract_from_etherscan(token, explorer_hostname, contract_address) + + contract_name_from_etherscan = result['name'] + if contract_name_from_etherscan != contract_name_from_config: + raise ValueError( + f"Contract name in config does not match with Blockchain explorer {contract_address}: {contract_name_from_config} != {contract_name_from_etherscan}", + ) + + return result diff --git a/diffyscan/utils/github.py b/diffyscan/utils/github.py index 97d8128..b202da3 100644 --- a/diffyscan/utils/github.py +++ b/diffyscan/utils/github.py @@ -18,7 +18,7 @@ def get_file_from_github(github_api_token, dependency_repo, path_to_file, dep_na github_data = fetch( github_api_url, headers={"Authorization": f"token {github_api_token}"} - ) + ).json() if not github_data: logger.error("No github data for", github_api_url) @@ -66,7 +66,7 @@ def _get_direct_file( ) response = fetch( github_api_url, headers={"Authorization": f"token {github_api_token}"} - ) + ).json() if response is None: return None @@ -98,7 +98,7 @@ def _recursive_search( ) github_data = fetch( github_api_url, headers={"Authorization": f"token {github_api_token}"} - ) + ).json() if github_data and isinstance(github_data, dict) and "content" in github_data: return base64.b64decode(github_data["content"]).decode() @@ -106,7 +106,7 @@ def _recursive_search( github_api_url = get_github_api_url(user_slash_repo, relative_path, None, commit) github_data = fetch( github_api_url, headers={"Authorization": f"token {github_api_token}"} - ) + ).json() if github_data and isinstance(github_data, list): directories = [item["path"] for item in github_data if item["type"] == "dir"] From aa6733ab5f49bc1007f2bfe499542f8703c51787 Mon Sep 17 00:00:00 2001 From: alexvozhak Date: Mon, 29 Jul 2024 18:03:58 +0300 Subject: [PATCH 002/118] Added binary verification through Ganache --- config_samples/lido_dao_holesky_config.json | 29 ++++ config_samples/lido_dao_sepolia_config.json | 78 +++++++++- diffyscan/diffyscan.py | 59 +++++-- diffyscan/utils/binary_verifier.py | 162 +++++++++++++------- diffyscan/utils/constants.py | 13 ++ diffyscan/utils/encoder.py | 96 ++++++++++++ diffyscan/utils/ganache.py | 34 ++++ 7 files changed, 402 insertions(+), 69 deletions(-) create mode 100644 diffyscan/utils/encoder.py create mode 100644 diffyscan/utils/ganache.py diff --git a/config_samples/lido_dao_holesky_config.json b/config_samples/lido_dao_holesky_config.json index 1b94274..64c9dac 100644 --- a/config_samples/lido_dao_holesky_config.json +++ b/config_samples/lido_dao_holesky_config.json @@ -111,5 +111,34 @@ "commit": "6bd6b76d1156e20e45d1016f355d154141c7e5b9", "relative_root": "contracts" } + }, + "ConstructorArgs": { + "OracleReportSanityChecker" : [ + "0x28FAB2059C713A7F9D8c86Db49f9bb0e96Af1ef8", + "0x22896Bfc68814BFD855b1a167255eE497006e730", + [ + 1500, + 500, + 1000, + 250, + 2000, + 100, + 100, + 128, + 5000000 + ], + [ + [], + [], + [], + [], + [], + [], + [], + [], + [], + [] + ] + ] } } diff --git a/config_samples/lido_dao_sepolia_config.json b/config_samples/lido_dao_sepolia_config.json index f03da40..50a4f06 100644 --- a/config_samples/lido_dao_sepolia_config.json +++ b/config_samples/lido_dao_sepolia_config.json @@ -97,5 +97,81 @@ "commit": "6bd6b76d1156e20e45d1016f355d154141c7e5b9", "relative_root": "contracts" } - } + }, + "ConstructorArgs": { + "0x8f6254332f69557A72b0DA2D5F0Bc07d4CA991E7" : ["0xf30a674935479cc6f2254ba65d7534eab8bd6ba2","0x6885E36BFcb68CB383DfE90023a462C03BCB2AE5",""], + "0x9726CA9AEFF4BC8FB8C084BdAbdB71608248E3f8" : [ + "0x3e3FE7dBc6B4C189E7128855dD526361c49b40Af" + ], + "0x604dc1776eEbe7ddCf4cf5429226Ad20a5a294eE" : [ ["0xd497be005638efcf09f6bfc8dafbbb0bb72cd991", + "0x6885e36bfcb68cb383dfe90023a462c03bcb2ae5", + "0x94b1b8e2680882f8652882e7f196169de3d9a3b2", + "0x3483c140ef7f2716460198ff831a8e53f05f1606", + "0x3e3fe7dbc6b4c189e7128855dd526361c49b40af", + "0xbac2a471443f18ac5c31078b96c5797a78fcc680", + "0x3483c140ef7f2716460198ff831a8e53f05f1606", + "0x61bb0ef69262d5ef1cc2873cf61766751d99b699", + "0x4f36aaeb18ab56a4e380241bea6ebf215b9cb12c", + "0x32a0e5828b62aab932362a4816ae03b860b65e83", + "0x7637d44c9f2e9ca584a8b5d2ea493012a5cdaeb6", + "0x1583c7b3f4c3b008720e6bce5726336b0ab25fdd", + "0xde7318afa67ead6d6bbc8224dfce5ed6e4b86d76", + "0x7bc76076b0f3879b4a750450c0ccf02c6ca11220"] + ], + "0xB82381A3fBD3FaFA77B3a7bE693342618240067b" : ["0x3e3FE7dBc6B4C189E7128855dD526361c49b40Af"], + "0x46cF57508B0565decC0419B833C2dAFa50B132e0" : ["0x80b5DC88C98E528bF9cb4B7F0f076aC41da24651"], + "0x94B1B8e2680882f8652882e7F196169dE3d9a3B2" : ["0x3e3FE7dBc6B4C189E7128855dD526361c49b40Af", "0x32A0E5828B62AAb932362a4816ae03b860b65e83"], + "0x0220A1cF6C3a548BE75aEabCdA509CaB08CDe063" : [ "0xB82381A3fBD3FaFA77B3a7bE693342618240067b", "Lido: stETH Withdrawal NFT", "unstETH" + ], + "0xee386d787Db24AbEe4dcc591F35405E323b70Dad" : ["0x3e3FE7dBc6B4C189E7128855dD526361c49b40Af","0x32A0E5828B62AAb932362a4816ae03b860b65e83"], + "0x61Bb0Ef69262d5EF1cc2873cf61766751D99B699" : ["0x6885E36BFcb68CB383DfE90023a462C03BCB2AE5", "0x32A0E5828B62AAb932362a4816ae03b860b65e83", "0x3e3FE7dBc6B4C189E7128855dD526361c49b40Af", 0, 0], + "0x082d16150BF75BB8F2197eEC1d293DbA96c93638" : ["0x8f6254332f69557A72b0DA2D5F0Bc07d4CA991E7","0x3e3FE7dBc6B4C189E7128855dD526361c49b40Af", "0x3483c140EF7F2716460198Ff831a8e53F05F1606", 12, 1655733600], + "0x1c2807B207f140a1DE0b39E5546eDEf67Af2568c" : [1], + "0xd06dF83b8ad6D89C86a187fba4Eae918d497BdCB" : ["0x9D381f44d1fbdf8190fA0EcdC028e2Af24DdD3FE","0x0000000000000000000000000000000000000000",0, "TEST Lido DAO Token", 18, "TLDO", 1], + "0x758D8c3CE794b3Dfe3b3A3482B7eD33de2109D95" : [32, 12, 1655733600, 12, 10, "0x6885E36BFcb68CB383DfE90023a462C03BCB2AE5", "0xd497Be005638efCf09F6BFC8DAFBBB0BB72cD991"], + "0xC40801b88C835a58e54eEE6679D301ba31a4C72b" : [12, 1655733600, "0x8f6254332f69557A72b0DA2D5F0Bc07d4CA991E7"], + "0xbac2A471443F18aC5C31078b96C5797A78fCc680" : [ + "0x28FAB2059C713A7F9D8c86Db49f9bb0e96Af1ef8", + "0x22896Bfc68814BFD855b1a167255eE497006e730", + [ + 1500, + 500, + 1000, + 250, + 2000, + 100, + 100, + 128, + 5000000 + ], + [ + [], + [], + [], + [], + [], + [], + [], + [], + [], + [] + ] + ], + "0x7bC76076b0f3879b4A750450C0Ccf02c6Ca11220" : ["0x6885E36BFcb68CB383DfE90023a462C03BCB2AE5",""], + "0x6155bD199ECcc79Ff4e8B392f6cBD9c9874E8916" : ["0x1c2807B207f140a1DE0b39E5546eDEf67Af2568c"], + "0xC73cd4B2A7c1CBC5BF046eB4A7019365558ABF66" : ["0x6155bD199ECcc79Ff4e8B392f6cBD9c9874E8916", "0xcd567bdf93dd0f6acc3bc7f2155f83244d56a65abbfbefb763e015420102c67b", ""], + "0x52AD3004Bc993d63931142Dd4f3DD647414048a1" : ["0x6155bD199ECcc79Ff4e8B392f6cBD9c9874E8916","0x5c9918c99c4081ca9459c178381be71d9da40e49e151687da55099c49a4237f1", ""], + "0x098a952BD200005382aEb3229e38ae39A7616F56" : [32,12,1655733600, 75, 10, "0x7FAcEF1c7248ed171FDd9ea3B25B4550b38e6133", "0x7637d44c9f2e9cA584a8B5D2EA493012A5cdaEB6"], + "0x1583C7b3f4C3B008720E6BcE5726336b0aB25fdd" : ["0x0220A1cF6C3a548BE75aEabCdA509CaB08CDe063", "0x32A0E5828B62AAb932362a4816ae03b860b65e83","0x0"], + "0x32A0E5828B62AAb932362a4816ae03b860b65e83" : ["0x6155bD199ECcc79Ff4e8B392f6cBD9c9874E8916","0x701a4fd1f5174d12a0f1d9ad2c88d0ad11ab6aad0ac72b7d9ce621815f8016a9","0"], + "0x33d6E15047E8644F8DDf5CD05d202dfE587DA6E3" : ["0x6155bD199ECcc79Ff4e8B392f6cBD9c9874E8916","0x7071f283424072341f856ac9e947e7ec0eb68719f757a7e785979b6b8717579d","0"], + "0x3483c140EF7F2716460198Ff831a8e53F05F1606" : ["0x6155bD199ECcc79Ff4e8B392f6cBD9c9874E8916","0x8b47ba2a8454ec799cd91646e7ec47168e91fd139b23f017455f3e5898aaba93", "0"], + "0x39A0EbdEE54cB319f4F42141daaBDb6ba25D341A" : ["0x6155bD199ECcc79Ff4e8B392f6cBD9c9874E8916","0x0abcd104777321a82b010357f20887d61247493d89d2e987ff57bcecbde00e1e", "0"], + "0x3e3FE7dBc6B4C189E7128855dD526361c49b40Af" : ["0x6155bD199ECcc79Ff4e8B392f6cBD9c9874E8916","0x3ca7c3e38968823ccb4c78ea688df41356f182ae1d159e4ee608d30d68cef320", "0"], + "0x4F36aAEb18Ab56A4e380241bea6ebF215b9cb12c" : ["0x46cF57508B0565decC0419B833C2dAFa50B132e0", "0x32A0E5828B62AAb932362a4816ae03b860b65e83", "0"], + "0x7637d44c9f2e9cA584a8B5D2EA493012A5cdaEB6" : ["0xC40801b88C835a58e54eEE6679D301ba31a4C72b", "0x32A0E5828B62AAb932362a4816ae03b860b65e83", "0"], + "0x8A1AA86d35b2EE8C9369618E7D7b40000cCD3295" : ["0x6155bD199ECcc79Ff4e8B392f6cBD9c9874E8916", "0xe3262375f45a6e2026b7e7b18c2b807434f2508fe1a2a3dfb493c7df8f4aad6a", "0"], + "0xd497Be005638efCf09F6BFC8DAFBBB0BB72cD991" : ["0x082d16150BF75BB8F2197eEC1d293DbA96c93638", "0x32A0E5828B62AAb932362a4816ae03b860b65e83", "0"] + } } + diff --git a/diffyscan/diffyscan.py b/diffyscan/diffyscan.py index 104d177..9f06a74 100644 --- a/diffyscan/diffyscan.py +++ b/diffyscan/diffyscan.py @@ -6,14 +6,15 @@ import subprocess import tempfile import shutil - + from utils.common import load_config, load_env -from utils.constants import DIFFS_DIR, START_TIME, DEFAULT_CONFIG_PATH +from utils.constants import DIFFS_DIR, START_TIME, DEFAULT_CONFIG_PATH, LOCAL_RPC_URL, REMOTE_RPC_URL from utils.explorer import get_contract_from_explorer from utils.github import get_file_from_github, get_file_from_github_recursive, resolve_dep from utils.helpers import create_dirs from utils.logger import logger from utils.binary_verifier import * +from utils.ganache import ganache __version__ = "0.0.0" @@ -33,13 +34,38 @@ def prettify_solidity(solidity_contract_content: str): with open(github_file_name, "r") as fp: return fp.read() -def run_binary_diff(contract_address, code): - logger.info(f'Started binary checking') - bytecode_from_etherscan, immutables = get_bytecode_from_etherscan(code) +def run_binary_diff(remote_contract_address, contract_source_code, config): + logger.info(f'Started binary checking for {remote_contract_address}') + + contract_creation_code, immutables, is_valid_constructor = get_contract_creation_code_from_etherscan(contract_source_code, config, remote_contract_address) + + if not is_valid_constructor: + logger.error(f'Failed to find constructorArgs, binary diff skipped') + return + + deployer_account = get_account(LOCAL_RPC_URL) + + if (deployer_account is None): + logger.error(f'Failed to receive the account, binary diff skipped') + return + + local_contract_address = deploy_contract(LOCAL_RPC_URL, deployer_account, contract_creation_code) + + if (local_contract_address is None): + logger.error(f'Failed to deploy bytecode to {LOCAL_RPC_URL}, binary diff skipped') + return - bytecode_from_blockchain = get_bytecode_from_blockchain(contract_address) + local_deployed_bytecode = get_bytecode(local_contract_address, LOCAL_RPC_URL) + if (local_deployed_bytecode is None): + logger.error(f'Failed to receive bytecode from {LOCAL_RPC_URL}') + return - match(bytecode_from_blockchain, bytecode_from_etherscan, immutables) + remote_deployed_bytecode = get_bytecode(remote_contract_address, REMOTE_RPC_URL) + if remote_deployed_bytecode is None: + logger.error(f'Failed to receive bytecode from {REMOTE_RPC_URL}') + return + + to_match(local_deployed_bytecode, remote_deployed_bytecode, immutables, remote_contract_address) def run_source_diff(contract_address_from_config, contract_code, config, github_api_token, recursive_parsing=False, prettify=False): logger.divider() @@ -149,13 +175,22 @@ def process_config(path: str, recursive_parsing: bool, unify_formatting: bool, b explorer_token = None if "explorer_token_env_var" in config: explorer_token = load_env(config["explorer_token_env_var"], masked=True, required=False) - contracts = config["contracts"] - for contract_address, contract_name in contracts.items(): - contract_code = get_contract_from_explorer(explorer_token, config["explorer_hostname"], contract_address, contract_name) - run_source_diff(contract_address, contract_code, config, github_api_token, recursive_parsing, unify_formatting) + + try: if (binary_check): - run_binary_diff(contract_address, contract_code) + ganache.start() + + for contract_address, contract_name in contracts.items(): + contract_code = get_contract_from_explorer(explorer_token, config["explorer_hostname"], contract_address, contract_name) + run_source_diff(contract_address, contract_code, config, github_api_token, recursive_parsing, unify_formatting) + if (binary_check): + run_binary_diff(contract_address, contract_code, config) + except KeyboardInterrupt: + logger.info(f'Keyboard interrupt by user') + finally: + ganache.stop() + if (autoclean): builds_dir_path = os.getenv('SOLC_DIR', 'solc') diff --git a/diffyscan/utils/binary_verifier.py b/diffyscan/utils/binary_verifier.py index 0992606..b84ffab 100644 --- a/diffyscan/utils/binary_verifier.py +++ b/diffyscan/utils/binary_verifier.py @@ -4,15 +4,11 @@ import os import stat - from utils.common import fetch, pull, get_solc_native_platform_from_os from utils.helpers import create_dirs from utils.logger import logger - -RPC_URL = os.getenv('RPC_URL', '') -if not RPC_URL: - raise ValueError('RPC_URL variable is not set') - +from utils.encoder import encode_constructor_arguments + def get_compiler_info(platform, required_compiler_version): compilers_list_url = ( f'https://raw.githubusercontent.com/ethereum/solc-bin/gh-pages/{platform}/list.json' @@ -44,7 +40,7 @@ def set_compiler_executable(compiler_path): compiler_file_rights = os.stat(compiler_path) os.chmod(compiler_path, compiler_file_rights.st_mode | stat.S_IEXEC) -def compile_contract(compiler_path, input_settings): +def compile_contracts(compiler_path, input_settings): process = subprocess.run([compiler_path,'--standard-json'], input=input_settings.encode(), capture_output=True, check=True, timeout=30) return json.loads(process.stdout) @@ -55,10 +51,9 @@ def prepare_compiler(platform, build_info, compiler_path): check_compiler_checksum(compiler_binary, valid_checksum) set_compiler_executable(compiler_path) -def get_target_contract(compilation_result, target_contract_name): - contracts = compilation_result['contracts'].values(); +def get_target_compiled_contract(compiled_contracts, target_contract_name): contracts_to_check = [] - for contracts in compilation_result['contracts'].values(): + for contracts in compiled_contracts: for name, contract in contracts.items(): if name == target_contract_name: contracts_to_check.append(contract) @@ -66,13 +61,13 @@ def get_target_contract(compilation_result, target_contract_name): if len(contracts_to_check) != 1: raise ValueError('multiple contracts with the same name') - logger.info('Contracts were successfully compiled. The target contract is ready for matching') + logger.okay('Contracts were successfully compiled. The target contract is ready for matching') return contracts_to_check[0] -def get_bytecode_from_etherscan(code): +def get_contract_creation_code_from_etherscan(contract_code, config, remote_contract_address): platform = get_solc_native_platform_from_os() - build_name = code["compiler"][1:] + build_name = contract_code["compiler"][1:] build_info = get_compiler_info(platform, build_name) compilers_dir_path = os.getenv('SOLC_DIR', 'solc') compiler_path = os.path.join(compilers_dir_path, build_info['path']) @@ -82,41 +77,112 @@ def get_bytecode_from_etherscan(code): if not is_compiler_already_prepared: prepare_compiler(platform, build_info, compiler_path) - input_settings = json.dumps(code["solcInput"]) - contract_name = code['name'] + input_settings = json.dumps(contract_code["solcInput"]) + compiled_contracts = compile_contracts(compiler_path, input_settings)['contracts'].values() - compilation_result = compile_contract(compiler_path, input_settings) + target_contract_name = contract_code['name'] + target_compiled_contract = get_target_compiled_contract(compiled_contracts, target_contract_name) - target_contract = get_target_contract(compilation_result, contract_name) - - expected_bytecode = target_contract['evm']['deployedBytecode']['object'] - + contract_creation_code = f'0x{target_compiled_contract['evm']['bytecode']['object']}' immutables = {} - if ('immutableReferences' in target_contract['evm']['deployedBytecode']): - immutable_references = target_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'] - - return expected_bytecode, immutables + constructor_abi = None + try: + constructor_abi = [entry["inputs"] for entry in target_compiled_contract['abi'] if entry["type"] == "constructor"][0] + except IndexError: + logger.info(f'Constructor in ABI not found') + return contract_creation_code, immutables, True + + constructor_calldata = None + + if (remote_contract_address in config["ConstructorArgs"]): + constructor_args = config["ConstructorArgs"][remote_contract_address] + if constructor_args: + constructor_calldata = encode_constructor_arguments(constructor_abi, constructor_args) + return contract_creation_code+constructor_calldata, immutables, True -def get_bytecode_from_blockchain(contract_address): - logger.info(f'Retrieving the bytecode by contract address "{contract_address}" from the blockchain...') + logger.warn(f'Constructor in ABI found, but config not found (contract {target_contract_name})') + return contract_creation_code, immutables, True + +def get_bytecode(contract_address, rpc_url): + logger.info(f'Receiving the bytecode from "{rpc_url}" ...') payload = json.dumps({'id': 1, 'jsonrpc': '2.0', 'method': 'eth_getCode', 'params': [contract_address, 'latest']}) - sources_url_response = pull(RPC_URL, payload) - sources_url_response_in_json = sources_url_response.json() - if 'result' in sources_url_response_in_json: - bytecode = sources_url_response_in_json['result'] - else: - raise ValueError(f'Failed to find section "result" in response') + sources_url_response_in_json = pull(rpc_url, payload).json() + if 'result' not in sources_url_response_in_json or sources_url_response_in_json['result'] == '0x': + return None + + logger.okay(f'Bytecode was successfully received') + return sources_url_response_in_json['result'] + +def get_chain_id(rpc_url): + payload = json.dumps({'id': 1, 'jsonrpc': '2.0', 'method': 'eth_chainId', 'params': []}) + + response = pull(rpc_url, payload).json() + if 'error' in response: + logger.error(f'Failed to received chainId: {response['error']['message']}') + return 1 + return int (response['result'],16) + +def get_account(rpc_url): + logger.info(f'Receiving the account from "{rpc_url}" ...') + + payload = json.dumps({'id': 42, 'jsonrpc': '2.0', 'method': 'eth_accounts', 'params': []}) - logger.okay(f'Bytecode was successfully fetched') + account_address_response = pull(rpc_url, payload).json() - return bytecode + if 'result' not in account_address_response: + return None + + logger.okay(f'The account was successfully received') + return account_address_response['result'][0] + +def deploy_contract(rpc_url, deployer, data): + logger.info(f'Trying to deploy transaction to "{rpc_url}" ...') + + payload_sendTransaction = json.dumps({ + "jsonrpc": "2.0", + "method": "eth_sendTransaction", + "params": [{ + "from": deployer, + "gas": 9000000, + "input": data + }], + "id": 1 + }) + response_sendTransaction = pull(rpc_url, payload_sendTransaction).json() + + if 'error' in response_sendTransaction: + logger.warn(f'Failed to deploy transaction: {response_sendTransaction['error']['message']}') + return None + + logger.okay(f'Transaction was successfully deployed') + tx_hash = response_sendTransaction['result'] + + payload_getTransactionReceipt = json.dumps({'id': 1, 'jsonrpc': '2.0', 'method': 'eth_getTransactionReceipt', 'params':[tx_hash]}) + response_getTransactionReceipt = pull(rpc_url, payload_getTransactionReceipt).json() + + if 'result' not in response_getTransactionReceipt or \ + 'contractAddress' not in response_getTransactionReceipt['result'] or \ + 'status' not in response_getTransactionReceipt['result'] : + logger.error(f'Failed to received transaction receipt') + return None + + if response_getTransactionReceipt['result']['status'] != '0x1': + logger.error(f'Failed to received transaction receipt. Transaction has been reverted (status 0x0). Input missmatch?') + return None + + contract_address = response_getTransactionReceipt['result']['contractAddress'] + + return contract_address + OPCODES = { 0x00: 'STOP', 0x01: 'ADD', 0x02: 'MUL', 0x03: 'SUB', 0x04: 'DIV', 0x05: 'SDIV', @@ -151,25 +217,9 @@ def get_bytecode_from_blockchain(contract_address): 0xF0: 'CREATE', 0xF1: 'CALL', 0xF2: 'CALLCODE', 0xF3: 'RETURN', 0xF4: 'DELEGATECALL', 0xF5: 'CREATE2', 0xFA: 'STATICCALL', 0xFD: 'REVERT', 0xFE: 'INVALID', 0xFF: 'SELFDESTRUCT', } - -def parse(bytecode): - instructions = [] - buffer = bytearray.fromhex(bytecode[2:] if bytecode.startswith('0x') else bytecode) - i = 0 - while i < len(buffer): - opcode = buffer[i] - length = 1 + (opcode >= 0x5f and opcode <= 0x7f) * (opcode - 0x5f) - instructions.append({ - 'start': i, - 'length': length, - 'op': {'name': OPCODES.get(opcode, 'INVALID'), 'code': opcode}, - 'bytecode': buffer[i:i + length].hex() - }) - i += length - return instructions - -def match(actualBytecode, expectedBytecode, immutables): - logger.info('Comparing actual code with the expected one:') + +def to_match(actualBytecode, expectedBytecode, immutables, remote_contract_address): + logger.info('Comparing actual code with the expected one...') actualInstructions = parse(actualBytecode) expectedInstructions = parse(expectedBytecode) @@ -181,13 +231,13 @@ def match(actualBytecode, expectedBytecode, immutables): expected = expectedInstructions[i] if i < len(expectedInstructions) else None if not actual and not expected: raise ValueError('Invalid instructions data') - elif actual.get('bytecode') != expected.get('bytecode'): + elif (actual is not None) and (actual.get('bytecode') != expected.get('bytecode')): differences.append(i) if not differences: - logger.okay(f'Bytecodes are fully matched') + logger.okay(f'Bytecodes are fully matched (contract {remote_contract_address})') return - logger.warn(f'Bytecodes have differences') + logger.warn(f'Bytecodes have differences contract {remote_contract_address})') nearLinesCount = 3 checkpoints = {0, *differences} diff --git a/diffyscan/utils/constants.py b/diffyscan/utils/constants.py index 757e5a2..10918b6 100644 --- a/diffyscan/utils/constants.py +++ b/diffyscan/utils/constants.py @@ -1,4 +1,5 @@ import time +import os DIGEST_DIR = "digest" START_TIME = time.time() @@ -6,3 +7,15 @@ DIFFS_DIR = f"{DIGEST_DIR}/{START_TIME_INT}/diffs" LOGS_PATH = f"{DIGEST_DIR}/{START_TIME_INT}/logs.txt" DEFAULT_CONFIG_PATH = "config.json" + +REMOTE_RPC_URL = os.getenv('REMOTE_RPC_URL', '') +if not REMOTE_RPC_URL: + raise ValueError('REMOTE_RPC_URL variable is not set') + +REMOTE_EXPLORER_NAME = os.getenv('REMOTE_EXPLORER_NAME', '') +if REMOTE_EXPLORER_NAME == '': + raise ValueError('REMOTE_EXPLORER_NAME variable is not set') + +LOCAL_RPC_URL = os.getenv('LOCAL_RPC_URL', '') +if not LOCAL_RPC_URL: + raise ValueError('LOCAL_RPC_URL variable is not set') diff --git a/diffyscan/utils/encoder.py b/diffyscan/utils/encoder.py new file mode 100644 index 0000000..1b3076b --- /dev/null +++ b/diffyscan/utils/encoder.py @@ -0,0 +1,96 @@ +from utils.logger import logger + +def encode_address(address): + number = int(address, 16) + formatted_address = f"{number:064x}" + return formatted_address + +def encode_uint256(number): + return format(number, "064x") + +def encode_bytes32(data): + return data.replace("0x", "") + +def encode_bytes(data): + bytes_str = data.lstrip("0x") + data_length = len(bytes_str) // 2 + encoded_length = 0 + if data_length > 0: + encoded_length = ((len(bytes_str) - 1) // 64 + 1) * 64 + bytes_str += "0" * (encoded_length - len(bytes_str)) + return format(data_length, "064x") + bytes_str + +def encode_tuple(types, args): + args_length = len(types) + encoded_offsets = "" + encoded_data = "" + for arg_index in range(args_length): + arg_type = types[arg_index] + arg_value = args[arg_index] + if arg_type == "address": + encoded_offsets += encode_address(arg_value) + elif arg_type == "uint256" or arg_type == "bool" or arg_type == 'uint8': + encoded_offsets += encode_uint256(arg_value) + elif arg_type == "bytes32": + encoded_offsets += encode_bytes32(arg_value) + elif arg_type == "address[]" and not arg_value: + encoded_data += '0' * 64 + offset = format((arg_index + args_length) * 32, "064x") + encoded_offsets += offset + else: + logger.warn(f"Unknown constructor argument type '{arg_type}', use --constructor-calldata instead") + return encoded_offsets+encoded_data + +def to_hex_with_alignment(value): + return format(value, "064x") + +def encode_constructor_arguments(constructor_abi, constructor_config_args): + arg_length = len(constructor_abi) + + logger.info(f"Constructor args types: {[arg['type'] for arg in constructor_abi]}") + + constructor_calldata = "" + compl_data = [] + if arg_length > 0: + for argument_index in range(arg_length): + arg_type = constructor_abi[argument_index]["type"] + 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': + constructor_calldata += encode_uint256(arg_value) + elif arg_type == "bytes32": + constructor_calldata += encode_bytes32(arg_value) + elif arg_type == "bytes": + data = format((argument_index+1)*32, "064x") + constructor_calldata+=data + data2 = encode_bytes(arg_value) + compl_data.append(data2) + elif arg_type == "string": + offset_arg = to_hex_with_alignment((arg_length + len(compl_data))*32) + constructor_calldata += offset_arg + length_arg = to_hex_with_alignment(len(arg_value.encode('utf-8'))) + hex_text = arg_value.encode('utf-8').hex().ljust(64, '0') + compl_data.append(length_arg) + compl_data.append(hex_text) + elif arg_type == "tuple": + args_tuple_types = [component["type"] for component in constructor_abi[argument_index]["components"]] + if len(args_tuple_types) and args_tuple_types[0] == "address[]": + dynamic_type_length = format((len(constructor_calldata)//64 + 1) *32, "064x") + constructor_calldata += dynamic_type_length + logger.info(f'dynamic_type_length {dynamic_type_length}') + compl_data.append(encode_tuple(args_tuple_types, arg_value)) + else: + constructor_calldata += encode_tuple(args_tuple_types, arg_value) + elif arg_type.endswith("[]"): + data = format((argument_index+1)*32, "064x") + constructor_calldata+=data + data2 = encode_bytes(arg_value) + compl_data.append(data2) + else: + raise ValueError(f"Unknown constructor argument type: {arg_type}") + for data in compl_data: + constructor_calldata += data + + return constructor_calldata + diff --git a/diffyscan/utils/ganache.py b/diffyscan/utils/ganache.py new file mode 100644 index 0000000..0e8b317 --- /dev/null +++ b/diffyscan/utils/ganache.py @@ -0,0 +1,34 @@ +import os +import subprocess +import signal + +from urllib.parse import urlparse +from utils.logger import logger +from utils.constants import LOCAL_RPC_URL, REMOTE_RPC_URL +from utils.binary_verifier import get_chain_id +class Ganache: + sub_process = None + + def __init__(self): + pass + + def start(self): + + local_node_command = ( + f'ganache --host {urlparse(LOCAL_RPC_URL).hostname} ' \ + f'--port {urlparse(LOCAL_RPC_URL).port} ' \ + f'--chain.chainId {get_chain_id (REMOTE_RPC_URL)} ' \ + f'--fork.url {REMOTE_RPC_URL} ' \ + f'-l 92000000 --hardfork istanbul -d ' + ) + + self.sub_process = subprocess.Popen("exec "+ local_node_command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + + logger.info(f'Ganache successfully started: "{local_node_command}", PID {self.sub_process.pid}') + + 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'Ganache stopped, PID {self.sub_process.pid}') + +ganache = Ganache() \ No newline at end of file From 4229785476a230b71cb9bb5cf7ce33f0da955351 Mon Sep 17 00:00:00 2001 From: alexvozhak Date: Tue, 30 Jul 2024 03:16:20 +0300 Subject: [PATCH 003/118] Made work with env uniform --- diffyscan/diffyscan.py | 17 ++++++----------- diffyscan/utils/binary_verifier.py | 4 ++-- diffyscan/utils/common.py | 17 ----------------- diffyscan/utils/constants.py | 14 +++++++++++--- 4 files changed, 19 insertions(+), 33 deletions(-) diff --git a/diffyscan/diffyscan.py b/diffyscan/diffyscan.py index 9f06a74..c178a5f 100644 --- a/diffyscan/diffyscan.py +++ b/diffyscan/diffyscan.py @@ -7,8 +7,8 @@ import tempfile import shutil -from utils.common import load_config, load_env -from utils.constants import DIFFS_DIR, START_TIME, DEFAULT_CONFIG_PATH, LOCAL_RPC_URL, REMOTE_RPC_URL +from utils.common import load_config +from utils.constants import * from utils.explorer import get_contract_from_explorer from utils.github import get_file_from_github, get_file_from_github_recursive, resolve_dep from utils.helpers import create_dirs @@ -171,10 +171,6 @@ def process_config(path: str, recursive_parsing: bool, unify_formatting: bool, b logger.info(f"Loading config {path}...") config = load_config(path) - github_api_token = load_env("GITHUB_API_TOKEN", masked=True) - explorer_token = None - if "explorer_token_env_var" in config: - explorer_token = load_env(config["explorer_token_env_var"], masked=True, required=False) contracts = config["contracts"] try: @@ -182,8 +178,8 @@ def process_config(path: str, recursive_parsing: bool, unify_formatting: bool, b ganache.start() for contract_address, contract_name in contracts.items(): - contract_code = get_contract_from_explorer(explorer_token, config["explorer_hostname"], contract_address, contract_name) - run_source_diff(contract_address, contract_code, config, github_api_token, recursive_parsing, unify_formatting) + contract_code = get_contract_from_explorer(ETHERSCAN_TOKEN, config["explorer_hostname"], contract_address, contract_name) + run_source_diff(contract_address, contract_code, config, GITHUB_API_TOKEN, recursive_parsing, unify_formatting) if (binary_check): run_binary_diff(contract_address, contract_code, config) except KeyboardInterrupt: @@ -193,9 +189,8 @@ def process_config(path: str, recursive_parsing: bool, unify_formatting: bool, b if (autoclean): - builds_dir_path = os.getenv('SOLC_DIR', 'solc') - shutil.rmtree(builds_dir_path) - logger.okay(f'{builds_dir_path} deleted') + shutil.rmtree(SOLC_DIR) + logger.okay(f'{SOLC_DIR} deleted') def parse_arguments(): parser = argparse.ArgumentParser() diff --git a/diffyscan/utils/binary_verifier.py b/diffyscan/utils/binary_verifier.py index b84ffab..0220562 100644 --- a/diffyscan/utils/binary_verifier.py +++ b/diffyscan/utils/binary_verifier.py @@ -8,6 +8,7 @@ from utils.helpers import create_dirs from utils.logger import logger from utils.encoder import encode_constructor_arguments +from utils.constants import SOLC_DIR def get_compiler_info(platform, required_compiler_version): compilers_list_url = ( @@ -69,8 +70,7 @@ def get_contract_creation_code_from_etherscan(contract_code, config, remote_cont platform = get_solc_native_platform_from_os() build_name = contract_code["compiler"][1:] build_info = get_compiler_info(platform, build_name) - compilers_dir_path = os.getenv('SOLC_DIR', 'solc') - compiler_path = os.path.join(compilers_dir_path, build_info['path']) + compiler_path = os.path.join(SOLC_DIR, build_info['path']) is_compiler_already_prepared = os.path.isfile(compiler_path) diff --git a/diffyscan/utils/common.py b/diffyscan/utils/common.py index 00e9f6f..7afd069 100644 --- a/diffyscan/utils/common.py +++ b/diffyscan/utils/common.py @@ -8,23 +8,6 @@ from .logger import logger from .types import Config -def load_env(variable_name, required=True, masked=False): - value = os.getenv(variable_name, default=None) - - if required and not value: - logger.error("Env not found", variable_name) - sys.exit(1) - - printable_value = mask_text(value) if masked and value is not None else str(value) - - if printable_value: - logger.okay(f"{variable_name}", printable_value) - else: - logger.info(f"{variable_name} var is not set") - - return value - - def load_config(path: str) -> Config: with open(path, mode="r") as config_file: return json.load(config_file) diff --git a/diffyscan/utils/constants.py b/diffyscan/utils/constants.py index 10918b6..ea71162 100644 --- a/diffyscan/utils/constants.py +++ b/diffyscan/utils/constants.py @@ -12,10 +12,18 @@ if not REMOTE_RPC_URL: raise ValueError('REMOTE_RPC_URL variable is not set') -REMOTE_EXPLORER_NAME = os.getenv('REMOTE_EXPLORER_NAME', '') -if REMOTE_EXPLORER_NAME == '': - raise ValueError('REMOTE_EXPLORER_NAME variable is not set') +SOLC_DIR = os.getenv('SOLC_DIR', '') +if SOLC_DIR == '': + raise ValueError('SOLC_DIR variable is not set') LOCAL_RPC_URL = os.getenv('LOCAL_RPC_URL', '') if not LOCAL_RPC_URL: raise ValueError('LOCAL_RPC_URL variable is not set') + +ETHERSCAN_TOKEN = os.getenv('ETHERSCAN_TOKEN', '') +if not ETHERSCAN_TOKEN: + raise ValueError('ETHERSCAN_TOKEN variable is not set') + +GITHUB_API_TOKEN = os.getenv('GITHUB_API_TOKEN', '') +if not GITHUB_API_TOKEN: + raise ValueError('GITHUB_API_TOKEN variable is not set') From cf4cad11005301da5a35b2f9e0b9478c64de155e Mon Sep 17 00:00:00 2001 From: alexvozhak Date: Tue, 30 Jul 2024 03:17:17 +0300 Subject: [PATCH 004/118] Corrected the alignment in lido_dao_sepolia_config.json --- config_samples/lido_dao_sepolia_config.json | 69 +++++++-------------- 1 file changed, 23 insertions(+), 46 deletions(-) diff --git a/config_samples/lido_dao_sepolia_config.json b/config_samples/lido_dao_sepolia_config.json index 50a4f06..ccb5dbc 100644 --- a/config_samples/lido_dao_sepolia_config.json +++ b/config_samples/lido_dao_sepolia_config.json @@ -100,29 +100,27 @@ }, "ConstructorArgs": { "0x8f6254332f69557A72b0DA2D5F0Bc07d4CA991E7" : ["0xf30a674935479cc6f2254ba65d7534eab8bd6ba2","0x6885E36BFcb68CB383DfE90023a462C03BCB2AE5",""], - "0x9726CA9AEFF4BC8FB8C084BdAbdB71608248E3f8" : [ - "0x3e3FE7dBc6B4C189E7128855dD526361c49b40Af" - ], - "0x604dc1776eEbe7ddCf4cf5429226Ad20a5a294eE" : [ ["0xd497be005638efcf09f6bfc8dafbbb0bb72cd991", - "0x6885e36bfcb68cb383dfe90023a462c03bcb2ae5", - "0x94b1b8e2680882f8652882e7f196169de3d9a3b2", - "0x3483c140ef7f2716460198ff831a8e53f05f1606", - "0x3e3fe7dbc6b4c189e7128855dd526361c49b40af", - "0xbac2a471443f18ac5c31078b96c5797a78fcc680", - "0x3483c140ef7f2716460198ff831a8e53f05f1606", - "0x61bb0ef69262d5ef1cc2873cf61766751d99b699", - "0x4f36aaeb18ab56a4e380241bea6ebf215b9cb12c", - "0x32a0e5828b62aab932362a4816ae03b860b65e83", - "0x7637d44c9f2e9ca584a8b5d2ea493012a5cdaeb6", - "0x1583c7b3f4c3b008720e6bce5726336b0ab25fdd", - "0xde7318afa67ead6d6bbc8224dfce5ed6e4b86d76", - "0x7bc76076b0f3879b4a750450c0ccf02c6ca11220"] - ], + "0x9726CA9AEFF4BC8FB8C084BdAbdB71608248E3f8" : ["0x3e3FE7dBc6B4C189E7128855dD526361c49b40Af"], + "0x604dc1776eEbe7ddCf4cf5429226Ad20a5a294eE" : [ + ["0xd497be005638efcf09f6bfc8dafbbb0bb72cd991", + "0x6885e36bfcb68cb383dfe90023a462c03bcb2ae5", + "0x94b1b8e2680882f8652882e7f196169de3d9a3b2", + "0x3483c140ef7f2716460198ff831a8e53f05f1606", + "0x3e3fe7dbc6b4c189e7128855dd526361c49b40af", + "0xbac2a471443f18ac5c31078b96c5797a78fcc680", + "0x3483c140ef7f2716460198ff831a8e53f05f1606", + "0x61bb0ef69262d5ef1cc2873cf61766751d99b699", + "0x4f36aaeb18ab56a4e380241bea6ebf215b9cb12c", + "0x32a0e5828b62aab932362a4816ae03b860b65e83", + "0x7637d44c9f2e9ca584a8b5d2ea493012a5cdaeb6", + "0x1583c7b3f4c3b008720e6bce5726336b0ab25fdd", + "0xde7318afa67ead6d6bbc8224dfce5ed6e4b86d76", + "0x7bc76076b0f3879b4a750450c0ccf02c6ca11220"] + ], "0xB82381A3fBD3FaFA77B3a7bE693342618240067b" : ["0x3e3FE7dBc6B4C189E7128855dD526361c49b40Af"], "0x46cF57508B0565decC0419B833C2dAFa50B132e0" : ["0x80b5DC88C98E528bF9cb4B7F0f076aC41da24651"], "0x94B1B8e2680882f8652882e7F196169dE3d9a3B2" : ["0x3e3FE7dBc6B4C189E7128855dD526361c49b40Af", "0x32A0E5828B62AAb932362a4816ae03b860b65e83"], - "0x0220A1cF6C3a548BE75aEabCdA509CaB08CDe063" : [ "0xB82381A3fBD3FaFA77B3a7bE693342618240067b", "Lido: stETH Withdrawal NFT", "unstETH" - ], + "0x0220A1cF6C3a548BE75aEabCdA509CaB08CDe063" : [ "0xB82381A3fBD3FaFA77B3a7bE693342618240067b", "Lido: stETH Withdrawal NFT", "unstETH"], "0xee386d787Db24AbEe4dcc591F35405E323b70Dad" : ["0x3e3FE7dBc6B4C189E7128855dD526361c49b40Af","0x32A0E5828B62AAb932362a4816ae03b860b65e83"], "0x61Bb0Ef69262d5EF1cc2873cf61766751D99B699" : ["0x6885E36BFcb68CB383DfE90023a462C03BCB2AE5", "0x32A0E5828B62AAb932362a4816ae03b860b65e83", "0x3e3FE7dBc6B4C189E7128855dD526361c49b40Af", 0, 0], "0x082d16150BF75BB8F2197eEC1d293DbA96c93638" : ["0x8f6254332f69557A72b0DA2D5F0Bc07d4CA991E7","0x3e3FE7dBc6B4C189E7128855dD526361c49b40Af", "0x3483c140EF7F2716460198Ff831a8e53F05F1606", 12, 1655733600], @@ -131,32 +129,11 @@ "0x758D8c3CE794b3Dfe3b3A3482B7eD33de2109D95" : [32, 12, 1655733600, 12, 10, "0x6885E36BFcb68CB383DfE90023a462C03BCB2AE5", "0xd497Be005638efCf09F6BFC8DAFBBB0BB72cD991"], "0xC40801b88C835a58e54eEE6679D301ba31a4C72b" : [12, 1655733600, "0x8f6254332f69557A72b0DA2D5F0Bc07d4CA991E7"], "0xbac2A471443F18aC5C31078b96C5797A78fCc680" : [ - "0x28FAB2059C713A7F9D8c86Db49f9bb0e96Af1ef8", - "0x22896Bfc68814BFD855b1a167255eE497006e730", - [ - 1500, - 500, - 1000, - 250, - 2000, - 100, - 100, - 128, - 5000000 - ], - [ - [], - [], - [], - [], - [], - [], - [], - [], - [], - [] - ] - ], + "0x28FAB2059C713A7F9D8c86Db49f9bb0e96Af1ef8", + "0x22896Bfc68814BFD855b1a167255eE497006e730", + [1500,500,1000,250,2000,100,100,128,5000000], + [[],[],[],[],[],[],[],[],[],[]] + ], "0x7bC76076b0f3879b4A750450C0Ccf02c6Ca11220" : ["0x6885E36BFcb68CB383DfE90023a462C03BCB2AE5",""], "0x6155bD199ECcc79Ff4e8B392f6cBD9c9874E8916" : ["0x1c2807B207f140a1DE0b39E5546eDEf67Af2568c"], "0xC73cd4B2A7c1CBC5BF046eB4A7019365558ABF66" : ["0x6155bD199ECcc79Ff4e8B392f6cBD9c9874E8916", "0xcd567bdf93dd0f6acc3bc7f2155f83244d56a65abbfbefb763e015420102c67b", ""], From 34c6851dae7520cacdc712e0cac0cf2ceeb5627d Mon Sep 17 00:00:00 2001 From: alexvozhak Date: Tue, 30 Jul 2024 03:17:34 +0300 Subject: [PATCH 005/118] Updated readme --- README.md | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 084387f..30e0da3 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ ![license MIT](https://img.shields.io/badge/license-MIT-brightgreen) [![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black) -Diff your Ethereum smart contracts code from GitHub against Blockchain explorer verified source code. +Diff your Ethereum smart contracts code from GitHub against Blockchain explorer verified source code, deployed bytecode from local node (Ganache) against remote Supports reformatting solidity code by means of prettifier solidity plugin before comparing the sources (option `--prettify`). @@ -34,6 +34,17 @@ Set your Github token to query API without strict rate limiting, ```bash export GITHUB_API_TOKEN= ``` +Set local and remote RPC URLs for matching bytecode from local node againts remote + +```bash +export REMOTE_RPC_URL= //https://ethereum-sepolia-rpc.publicnode.com +export LOCAL_RPC_URL= //http://127.0.0.1:7545 +``` +Set absolute or relative path for downloading solc compilers + +```bash +export SOLC_DIR= +``` Start script with one of the examples provided (or entire folder of configs) From c53391b579258098d36c349e3b1a19c3207522ca Mon Sep 17 00:00:00 2001 From: alexvozhak Date: Tue, 30 Jul 2024 13:37:01 +0300 Subject: [PATCH 006/118] Reverted taking etherscan env var from config --- diffyscan/diffyscan.py | 12 ++++++++++-- diffyscan/utils/common.py | 17 +++++++++++++++++ diffyscan/utils/constants.py | 4 ---- 3 files changed, 27 insertions(+), 6 deletions(-) diff --git a/diffyscan/diffyscan.py b/diffyscan/diffyscan.py index c178a5f..a12c9d0 100644 --- a/diffyscan/diffyscan.py +++ b/diffyscan/diffyscan.py @@ -7,7 +7,7 @@ import tempfile import shutil -from utils.common import load_config +from utils.common import load_config, load_env from utils.constants import * from utils.explorer import get_contract_from_explorer from utils.github import get_file_from_github, get_file_from_github_recursive, resolve_dep @@ -171,6 +171,14 @@ def process_config(path: str, recursive_parsing: bool, unify_formatting: bool, b logger.info(f"Loading config {path}...") config = load_config(path) + explorer_token = None + if "explorer_token_env_var" in config: + explorer_token = load_env(config["explorer_token_env_var"], masked=True, required=False) + if (explorer_token is None): + explorer_token = os.getenv('ETHERSCAN_EXPLORER_TOKEN', default=None) + if (explorer_token is None): + raise ValueError(f'Failed to find "ETHERSCAN_EXPLORER_TOKEN" in env') + contracts = config["contracts"] try: @@ -178,7 +186,7 @@ def process_config(path: str, recursive_parsing: bool, unify_formatting: bool, b ganache.start() for contract_address, contract_name in contracts.items(): - contract_code = get_contract_from_explorer(ETHERSCAN_TOKEN, config["explorer_hostname"], contract_address, contract_name) + contract_code = get_contract_from_explorer(explorer_token, config["explorer_hostname"], contract_address, contract_name) run_source_diff(contract_address, contract_code, config, GITHUB_API_TOKEN, recursive_parsing, unify_formatting) if (binary_check): run_binary_diff(contract_address, contract_code, config) diff --git a/diffyscan/utils/common.py b/diffyscan/utils/common.py index 7afd069..00e9f6f 100644 --- a/diffyscan/utils/common.py +++ b/diffyscan/utils/common.py @@ -8,6 +8,23 @@ from .logger import logger from .types import Config +def load_env(variable_name, required=True, masked=False): + value = os.getenv(variable_name, default=None) + + if required and not value: + logger.error("Env not found", variable_name) + sys.exit(1) + + printable_value = mask_text(value) if masked and value is not None else str(value) + + if printable_value: + logger.okay(f"{variable_name}", printable_value) + else: + logger.info(f"{variable_name} var is not set") + + return value + + def load_config(path: str) -> Config: with open(path, mode="r") as config_file: return json.load(config_file) diff --git a/diffyscan/utils/constants.py b/diffyscan/utils/constants.py index ea71162..e0f1e8a 100644 --- a/diffyscan/utils/constants.py +++ b/diffyscan/utils/constants.py @@ -20,10 +20,6 @@ if not LOCAL_RPC_URL: raise ValueError('LOCAL_RPC_URL variable is not set') -ETHERSCAN_TOKEN = os.getenv('ETHERSCAN_TOKEN', '') -if not ETHERSCAN_TOKEN: - raise ValueError('ETHERSCAN_TOKEN variable is not set') - GITHUB_API_TOKEN = os.getenv('GITHUB_API_TOKEN', '') if not GITHUB_API_TOKEN: raise ValueError('GITHUB_API_TOKEN variable is not set') From f77bf502ce2b1dc85c20ca33a422619fbec914e2 Mon Sep 17 00:00:00 2001 From: alexvozhak Date: Tue, 30 Jul 2024 17:47:52 +0300 Subject: [PATCH 007/118] Fixed relative paths --- diffyscan/diffyscan.py | 16 ++++++++-------- diffyscan/utils/binary_verifier.py | 10 +++++----- diffyscan/utils/encoder.py | 2 +- diffyscan/utils/ganache.py | 6 +++--- 4 files changed, 17 insertions(+), 17 deletions(-) mode change 100644 => 100755 diffyscan/diffyscan.py diff --git a/diffyscan/diffyscan.py b/diffyscan/diffyscan.py old mode 100644 new mode 100755 index a12c9d0..18cd7d7 --- a/diffyscan/diffyscan.py +++ b/diffyscan/diffyscan.py @@ -7,14 +7,14 @@ import tempfile import shutil -from utils.common import load_config, load_env -from utils.constants import * -from utils.explorer import get_contract_from_explorer -from utils.github import get_file_from_github, get_file_from_github_recursive, resolve_dep -from utils.helpers import create_dirs -from utils.logger import logger -from utils.binary_verifier import * -from utils.ganache import ganache +from .utils.common import load_config, load_env +from .utils.constants import * +from .utils.explorer import get_contract_from_explorer +from .utils.github import get_file_from_github, get_file_from_github_recursive, resolve_dep +from .utils.helpers import create_dirs +from .utils.logger import logger +from .utils.binary_verifier import * +from .utils.ganache import ganache __version__ = "0.0.0" diff --git a/diffyscan/utils/binary_verifier.py b/diffyscan/utils/binary_verifier.py index 0220562..e2cf68f 100644 --- a/diffyscan/utils/binary_verifier.py +++ b/diffyscan/utils/binary_verifier.py @@ -4,11 +4,11 @@ import os import stat -from utils.common import fetch, pull, get_solc_native_platform_from_os -from utils.helpers import create_dirs -from utils.logger import logger -from utils.encoder import encode_constructor_arguments -from utils.constants import SOLC_DIR +from .common import fetch, pull, get_solc_native_platform_from_os +from .helpers import create_dirs +from .logger import logger +from .encoder import encode_constructor_arguments +from .constants import SOLC_DIR def get_compiler_info(platform, required_compiler_version): compilers_list_url = ( diff --git a/diffyscan/utils/encoder.py b/diffyscan/utils/encoder.py index 1b3076b..0069cf8 100644 --- a/diffyscan/utils/encoder.py +++ b/diffyscan/utils/encoder.py @@ -1,4 +1,4 @@ -from utils.logger import logger +from .logger import logger def encode_address(address): number = int(address, 16) diff --git a/diffyscan/utils/ganache.py b/diffyscan/utils/ganache.py index 0e8b317..d85cd62 100644 --- a/diffyscan/utils/ganache.py +++ b/diffyscan/utils/ganache.py @@ -3,9 +3,9 @@ import signal from urllib.parse import urlparse -from utils.logger import logger -from utils.constants import LOCAL_RPC_URL, REMOTE_RPC_URL -from utils.binary_verifier import get_chain_id +from .logger import logger +from .constants import LOCAL_RPC_URL, REMOTE_RPC_URL +from .binary_verifier import get_chain_id class Ganache: sub_process = None From 43336d86dc2c546ee5d7a9171761741ef268abe8 Mon Sep 17 00:00:00 2001 From: alexvozhak Date: Wed, 31 Jul 2024 16:51:49 +0300 Subject: [PATCH 008/118] Added Ganache checks --- README.md | 3 ++- diffyscan/utils/binary_verifier.py | 4 +-- diffyscan/utils/ganache.py | 43 +++++++++++++++++++++++------- package.json | 4 ++- 4 files changed, 41 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index 30e0da3..7306738 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,7 @@ Diff your Ethereum smart contracts code from GitHub against Blockchain explorer verified source code, deployed bytecode from local node (Ganache) against remote Supports reformatting solidity code by means of prettifier solidity plugin before comparing the sources (option `--prettify`). +Supports binary matching deployed bytecode from local node (Ganache) against remote (option `--binary-check`) ## Install @@ -15,7 +16,7 @@ Supports reformatting solidity code by means of prettifier solidity plugin befor pipx install git+https://github.com/lidofinance/diffyscan ``` -If need `--prettify` option +If need `--prettify`, '--binary-check' options ```shell npm install diff --git a/diffyscan/utils/binary_verifier.py b/diffyscan/utils/binary_verifier.py index e2cf68f..ec43f23 100644 --- a/diffyscan/utils/binary_verifier.py +++ b/diffyscan/utils/binary_verifier.py @@ -99,13 +99,13 @@ def get_contract_creation_code_from_etherscan(contract_code, config, remote_cont constructor_calldata = None - if (remote_contract_address in config["ConstructorArgs"]): + if "ConstructorArgs" in config and remote_contract_address in config["ConstructorArgs"]: constructor_args = config["ConstructorArgs"][remote_contract_address] if constructor_args: constructor_calldata = encode_constructor_arguments(constructor_abi, constructor_args) return contract_creation_code+constructor_calldata, immutables, True - logger.warn(f'Constructor in ABI found, but config not found (contract {target_contract_name})') + logger.warn(f"Constructor in ABI found, but config section 'ConstructorArgs' not found (contract {target_contract_name})") return contract_creation_code, immutables, True def get_bytecode(contract_address, rpc_url): diff --git a/diffyscan/utils/ganache.py b/diffyscan/utils/ganache.py index d85cd62..4c1694f 100644 --- a/diffyscan/utils/ganache.py +++ b/diffyscan/utils/ganache.py @@ -8,24 +8,49 @@ from .binary_verifier import get_chain_id class Ganache: sub_process = None + TIMEOUT_FOR_INIT_SEC = 5 def __init__(self): pass - - def start(self): + def is_port_in_use(self, parsed_url) -> bool: + import socket + with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: + return s.connect_ex((parsed_url.hostname, parsed_url.port)) == 0 + + def start(self): + parsed_url = urlparse(LOCAL_RPC_URL) + local_node_command = ( - f'ganache --host {urlparse(LOCAL_RPC_URL).hostname} ' \ - f'--port {urlparse(LOCAL_RPC_URL).port} ' \ + f'npx ganache --host {parsed_url.hostname} ' \ + f'--port {parsed_url.port} ' \ f'--chain.chainId {get_chain_id (REMOTE_RPC_URL)} ' \ f'--fork.url {REMOTE_RPC_URL} ' \ - f'-l 92000000 --hardfork istanbul -d ' + f'-l 92000000 --hardfork istanbul -d' ) - - self.sub_process = subprocess.Popen("exec "+ local_node_command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) - logger.info(f'Ganache successfully started: "{local_node_command}", PID {self.sub_process.pid}') - + logger.info(f'Trying to start Ganache: "{local_node_command}"') + is_port_used = self.is_port_in_use(parsed_url) + if (is_port_used): + answer = input(f'Port {parsed_url.port} is busy. Fix it? write "yes": ') + if (answer.lower() == 'yes'): + return_code = subprocess.call(f'exec npx kill-port {parsed_url.port}', shell=True) + if (return_code == 0): + is_port_used = self.is_port_in_use(parsed_url) + if (is_port_used): + raise ValueError(f'Failed to start Ganache: {parsed_url.netloc} is busy') + self.sub_process = subprocess.Popen("exec "+ local_node_command, shell=True, stdout=subprocess.DEVNULL, stderr=subprocess.PIPE) + try: + _, errs = self.sub_process.communicate(timeout=self.TIMEOUT_FOR_INIT_SEC) + if (errs): + raise ValueError(f'Failed to start Ganache: {errs.decode()}') + except subprocess.TimeoutExpired: + is_port_used = self.is_port_in_use(parsed_url) + if (is_port_used): + logger.okay(f'Ganache successfully started, PID {self.sub_process.pid}') + else: + raise ValueError(f'Failed to start Ganache: something is wrong') + 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) diff --git a/package.json b/package.json index 99b33ec..b23f505 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,8 @@ { "devDependencies": { "prettier": "^3.3.0", - "prettier-plugin-solidity": "^1.3.1" + "prettier-plugin-solidity": "^1.3.1", + "ganache": "^7.9.2", + "kill-port": "^2.0.1" } } From d445eb3e05a7f30097a22dfef99786f5ab77dd9d Mon Sep 17 00:00:00 2001 From: alexvozhak Date: Thu, 1 Aug 2024 18:53:25 +0300 Subject: [PATCH 009/118] Formatted the code in Black style --- diffyscan/diffyscan.py | 222 +++++++++++++++++++++-------- diffyscan/utils/binary_verifier.py | 2 +- diffyscan/utils/common.py | 18 ++- diffyscan/utils/constants.py | 22 +-- diffyscan/utils/encoder.py | 99 +++++++------ diffyscan/utils/explorer.py | 99 +++++++------ diffyscan/utils/ganache.py | 109 +++++++------- 7 files changed, 353 insertions(+), 218 deletions(-) diff --git a/diffyscan/diffyscan.py b/diffyscan/diffyscan.py index 18cd7d7..9c4d083 100755 --- a/diffyscan/diffyscan.py +++ b/diffyscan/diffyscan.py @@ -10,7 +10,11 @@ from .utils.common import load_config, load_env from .utils.constants import * from .utils.explorer import get_contract_from_explorer -from .utils.github import get_file_from_github, get_file_from_github_recursive, resolve_dep +from .utils.github import ( + get_file_from_github, + get_file_from_github_recursive, + resolve_dep, +) from .utils.helpers import create_dirs from .utils.logger import logger from .utils.binary_verifier import * @@ -21,53 +25,85 @@ g_skip_user_input: bool = False + def prettify_solidity(solidity_contract_content: str): - github_file_name = os.path.join(tempfile.gettempdir(), "9B91E897-EA51-4FCC-8DAF-FCFF135A6963.sol") + github_file_name = os.path.join( + tempfile.gettempdir(), "9B91E897-EA51-4FCC-8DAF-FCFF135A6963.sol" + ) with open(github_file_name, "w") as fp: fp.write(solidity_contract_content) prettier_return_code = subprocess.call( - ["npx", "prettier", "--plugin=prettier-plugin-solidity", "--write", github_file_name], - stdout=subprocess.DEVNULL) + [ + "npx", + "prettier", + "--plugin=prettier-plugin-solidity", + "--write", + github_file_name, + ], + stdout=subprocess.DEVNULL, + ) if prettier_return_code != 0: logger.error("Prettier/npx subprocess failed (see the error above)") sys.exit() with open(github_file_name, "r") as fp: return fp.read() + def run_binary_diff(remote_contract_address, contract_source_code, config): - logger.info(f'Started binary checking for {remote_contract_address}') - - contract_creation_code, immutables, is_valid_constructor = get_contract_creation_code_from_etherscan(contract_source_code, config, remote_contract_address) - + logger.info(f"Started binary checking for {remote_contract_address}") + + contract_creation_code, immutables, is_valid_constructor = ( + get_contract_creation_code_from_etherscan( + contract_source_code, config, remote_contract_address + ) + ) + if not is_valid_constructor: - logger.error(f'Failed to find constructorArgs, binary diff skipped') + logger.error(f"Failed to find constructorArgs, binary diff skipped") return - + deployer_account = get_account(LOCAL_RPC_URL) - - if (deployer_account is None): - logger.error(f'Failed to receive the account, binary diff skipped') + + if deployer_account is None: + logger.error(f"Failed to receive the account, binary diff skipped") return - - local_contract_address = deploy_contract(LOCAL_RPC_URL, deployer_account, contract_creation_code) - - if (local_contract_address is None): - logger.error(f'Failed to deploy bytecode to {LOCAL_RPC_URL}, binary diff skipped') + + local_contract_address = deploy_contract( + LOCAL_RPC_URL, deployer_account, contract_creation_code + ) + + if local_contract_address is None: + logger.error( + f"Failed to deploy bytecode to {LOCAL_RPC_URL}, binary diff skipped" + ) return local_deployed_bytecode = get_bytecode(local_contract_address, LOCAL_RPC_URL) - if (local_deployed_bytecode is None): - logger.error(f'Failed to receive bytecode from {LOCAL_RPC_URL}') + if local_deployed_bytecode is None: + logger.error(f"Failed to receive bytecode from {LOCAL_RPC_URL}") return - + remote_deployed_bytecode = get_bytecode(remote_contract_address, REMOTE_RPC_URL) if remote_deployed_bytecode is None: - logger.error(f'Failed to receive bytecode from {REMOTE_RPC_URL}') + logger.error(f"Failed to receive bytecode from {REMOTE_RPC_URL}") return - - to_match(local_deployed_bytecode, remote_deployed_bytecode, immutables, remote_contract_address) -def run_source_diff(contract_address_from_config, contract_code, config, github_api_token, recursive_parsing=False, prettify=False): + to_match( + local_deployed_bytecode, + remote_deployed_bytecode, + immutables, + remote_contract_address, + ) + + +def run_source_diff( + contract_address_from_config, + contract_code, + config, + github_api_token, + recursive_parsing=False, + prettify=False, +): logger.divider() logger.okay("Contract", contract_address_from_config) logger.okay("Blockchain explorer Hostname", config["explorer_hostname"]) @@ -81,7 +117,11 @@ def run_source_diff(contract_address_from_config, contract_code, config, github_ f"Fetching source code from blockchain explorer {config['explorer_hostname']} ..." ) - source_files = contract_code["solcInput"].items() if not "sources" in contract_code["solcInput"] else contract_code["solcInput"]["sources"].items() + source_files = ( + contract_code["solcInput"].items() + if not "sources" in contract_code["solcInput"] + else contract_code["solcInput"]["sources"].items() + ) files_count = len(source_files) logger.okay("Contract", contract_code["name"]) logger.okay("Files", files_count) @@ -119,9 +159,13 @@ def run_source_diff(contract_address_from_config, contract_code, config, github_ file_found = bool(repo) if recursive_parsing: - github_file = get_file_from_github_recursive(github_api_token, repo, path_to_file, dep_name) + github_file = get_file_from_github_recursive( + github_api_token, repo, path_to_file, dep_name + ) else: - github_file = get_file_from_github(github_api_token, repo, path_to_file, dep_name) + github_file = get_file_from_github( + github_api_token, repo, path_to_file, dep_name + ) if not github_file: github_file = "" @@ -137,7 +181,9 @@ def run_source_diff(contract_address_from_config, contract_code, config, github_ explorer_lines = explorer_content.splitlines() diff_html = difflib.HtmlDiff().make_file(github_lines, explorer_lines) - diff_report_filename = f"{DIFFS_DIR}/{contract_address_from_config}/{filename}.html" + diff_report_filename = ( + f"{DIFFS_DIR}/{contract_address_from_config}/{filename}.html" + ) create_dirs(diff_report_filename) with open(diff_report_filename, "w") as f: @@ -167,55 +213,97 @@ def run_source_diff(contract_address_from_config, contract_code, config, github_ logger.report_table(report) -def process_config(path: str, recursive_parsing: bool, unify_formatting: bool, binary_check: bool, autoclean: bool): + +def process_config( + path: str, + recursive_parsing: bool, + unify_formatting: bool, + binary_check: bool, + autoclean: bool, +): logger.info(f"Loading config {path}...") config = load_config(path) explorer_token = None if "explorer_token_env_var" in config: - explorer_token = load_env(config["explorer_token_env_var"], masked=True, required=False) - if (explorer_token is None): - explorer_token = os.getenv('ETHERSCAN_EXPLORER_TOKEN', default=None) - if (explorer_token is None): - raise ValueError(f'Failed to find "ETHERSCAN_EXPLORER_TOKEN" in env') - + explorer_token = load_env( + config["explorer_token_env_var"], masked=True, required=False + ) + if explorer_token is None: + explorer_token = os.getenv("ETHERSCAN_EXPLORER_TOKEN", default=None) + if explorer_token is None: + raise ValueError(f'Failed to find "ETHERSCAN_EXPLORER_TOKEN" in env') + contracts = config["contracts"] - + try: - if (binary_check): - ganache.start() - + if binary_check: + ganache.start() + for contract_address, contract_name in contracts.items(): - contract_code = get_contract_from_explorer(explorer_token, config["explorer_hostname"], contract_address, contract_name) - run_source_diff(contract_address, contract_code, config, GITHUB_API_TOKEN, recursive_parsing, unify_formatting) - if (binary_check): + contract_code = get_contract_from_explorer( + explorer_token, + config["explorer_hostname"], + contract_address, + contract_name, + ) + run_source_diff( + contract_address, + contract_code, + config, + GITHUB_API_TOKEN, + recursive_parsing, + unify_formatting, + ) + if binary_check: run_binary_diff(contract_address, contract_code, config) except KeyboardInterrupt: - logger.info(f'Keyboard interrupt by user') + logger.info(f"Keyboard interrupt by user") finally: ganache.stop() - - if (autoclean): + if autoclean: shutil.rmtree(SOLC_DIR) - logger.okay(f'{SOLC_DIR} deleted') + logger.okay(f"{SOLC_DIR} deleted") + def parse_arguments(): parser = argparse.ArgumentParser() - parser.add_argument("--version", "-V", action="store_true", help="Display version information") - parser.add_argument("path", nargs="?", default=None, help="Path to config or directory with configs") - parser.add_argument("--yes", "-y", help="If set don't ask for input before validating each contract", action="store_true") + parser.add_argument( + "--version", "-V", action="store_true", help="Display version information" + ) + parser.add_argument( + "path", nargs="?", default=None, help="Path to config or directory with configs" + ) + parser.add_argument( + "--yes", + "-y", + help="If set don't ask for input before validating each contract", + action="store_true", + ) parser.add_argument( "--support-brownie", help="Support recursive retrieving for contracts. It may be useful for contracts whose sources have been verified by the brownie tooling, which automatically replaces relative paths to contracts in imports with plain contract names.", action=argparse.BooleanOptionalAction, ) - parser.add_argument("--prettify", "-p", help="Unify formatting by prettier before comparing", action="store_true") - parser.add_argument("--binary-check", "-binary", help="Match contracts by binaries such as verify-bytecode.ts", default=True) - parser.add_argument("--autoclean", "-clean", help="Autoclean build dir after work", default=True) + parser.add_argument( + "--prettify", + "-p", + help="Unify formatting by prettier before comparing", + action="store_true", + ) + parser.add_argument( + "--binary-check", + "-binary", + help="Match contracts by binaries such as verify-bytecode.ts", + default=True, + ) + parser.add_argument( + "--autoclean", "-clean", help="Autoclean build dir after work", default=True + ) return parser.parse_args() - - + + def main(): global g_skip_user_input @@ -228,14 +316,32 @@ def main(): logger.divider() if args.path is None: - process_config(DEFAULT_CONFIG_PATH, args.support_brownie, args.prettify, args.binary_check, args.autoclean) + process_config( + DEFAULT_CONFIG_PATH, + args.support_brownie, + args.prettify, + args.binary_check, + args.autoclean, + ) elif os.path.isfile(args.path): - process_config(args.path, args.support_brownie, args.prettify, args.binary_check, args.autoclean) + process_config( + args.path, + args.support_brownie, + args.prettify, + args.binary_check, + args.autoclean, + ) elif os.path.isdir(args.path): for filename in os.listdir(args.path): config_path = os.path.join(args.path, filename) if os.path.isfile(config_path): - process_config(config_path, args.support_brownie, args.prettify, args.binary_check, args.autoclean) + process_config( + config_path, + args.support_brownie, + args.prettify, + args.binary_check, + args.autoclean, + ) else: logger.error(f"Specified config path {args.path} not found") sys.exit(1) diff --git a/diffyscan/utils/binary_verifier.py b/diffyscan/utils/binary_verifier.py index ec43f23..8388b0f 100644 --- a/diffyscan/utils/binary_verifier.py +++ b/diffyscan/utils/binary_verifier.py @@ -324,4 +324,4 @@ def parse(bytecode): 'bytecode': buffer[i:i+length].hex() }) i += length - return instructions \ No newline at end of file + return instructions diff --git a/diffyscan/utils/common.py b/diffyscan/utils/common.py index 00e9f6f..34b2507 100644 --- a/diffyscan/utils/common.py +++ b/diffyscan/utils/common.py @@ -8,6 +8,7 @@ from .logger import logger from .types import Config + def load_env(variable_name, required=True, masked=False): value = os.getenv(variable_name, default=None) @@ -29,6 +30,7 @@ def load_config(path: str) -> Config: with open(path, mode="r") as config_file: return json.load(config_file) + def handle_response(response, url): if response.status_code == 404: return None @@ -40,18 +42,21 @@ def handle_response(response, url): return response + def fetch(url, headers={}): logger.log(f"Fetch: {url}") response = requests.get(url, headers=headers) return handle_response(response, url) + def pull(url, payload={}): logger.log(f"Pull: {url}") response = requests.post(url, data=payload) return handle_response(response, url) - + + def mask_text(text, mask_start=3, mask_end=3): text_length = len(text) mask = "*" * (text_length - mask_start - mask_end) @@ -64,11 +69,12 @@ def parse_repo_link(repo_link): user_slash_repo = repo_location[0] return user_slash_repo + def get_solc_native_platform_from_os(): platform_name = sys.platform - if platform_name == 'linux': - return 'linux-amd64' - elif platform_name == 'darwin': - return 'macosx-amd64' + if platform_name == "linux": + return "linux-amd64" + elif platform_name == "darwin": + return "macosx-amd64" else: - raise ValueError(f'Unsupported platform {platform_name}') \ No newline at end of file + raise ValueError(f"Unsupported platform {platform_name}") diff --git a/diffyscan/utils/constants.py b/diffyscan/utils/constants.py index e0f1e8a..81b4ac4 100644 --- a/diffyscan/utils/constants.py +++ b/diffyscan/utils/constants.py @@ -8,18 +8,18 @@ LOGS_PATH = f"{DIGEST_DIR}/{START_TIME_INT}/logs.txt" DEFAULT_CONFIG_PATH = "config.json" -REMOTE_RPC_URL = os.getenv('REMOTE_RPC_URL', '') +REMOTE_RPC_URL = os.getenv("REMOTE_RPC_URL", "") if not REMOTE_RPC_URL: - raise ValueError('REMOTE_RPC_URL variable is not set') + raise ValueError("REMOTE_RPC_URL variable is not set") -SOLC_DIR = os.getenv('SOLC_DIR', '') -if SOLC_DIR == '': - raise ValueError('SOLC_DIR variable is not set') - -LOCAL_RPC_URL = os.getenv('LOCAL_RPC_URL', '') +SOLC_DIR = os.getenv("SOLC_DIR", "") +if SOLC_DIR == "": + raise ValueError("SOLC_DIR variable is not set") + +LOCAL_RPC_URL = os.getenv("LOCAL_RPC_URL", "") if not LOCAL_RPC_URL: - raise ValueError('LOCAL_RPC_URL variable is not set') - -GITHUB_API_TOKEN = os.getenv('GITHUB_API_TOKEN', '') + raise ValueError("LOCAL_RPC_URL variable is not set") + +GITHUB_API_TOKEN = os.getenv("GITHUB_API_TOKEN", "") if not GITHUB_API_TOKEN: - raise ValueError('GITHUB_API_TOKEN variable is not set') + raise ValueError("GITHUB_API_TOKEN variable is not set") diff --git a/diffyscan/utils/encoder.py b/diffyscan/utils/encoder.py index 0069cf8..b5e41b2 100644 --- a/diffyscan/utils/encoder.py +++ b/diffyscan/utils/encoder.py @@ -1,16 +1,20 @@ from .logger import logger + def encode_address(address): number = int(address, 16) formatted_address = f"{number:064x}" return formatted_address + def encode_uint256(number): return format(number, "064x") + def encode_bytes32(data): return data.replace("0x", "") + def encode_bytes(data): bytes_str = data.lstrip("0x") data_length = len(bytes_str) // 2 @@ -20,6 +24,7 @@ def encode_bytes(data): bytes_str += "0" * (encoded_length - len(bytes_str)) return format(data_length, "064x") + bytes_str + def encode_tuple(types, args): args_length = len(types) encoded_offsets = "" @@ -29,21 +34,25 @@ def encode_tuple(types, args): arg_value = args[arg_index] if arg_type == "address": encoded_offsets += 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": encoded_offsets += encode_uint256(arg_value) elif arg_type == "bytes32": encoded_offsets += encode_bytes32(arg_value) elif arg_type == "address[]" and not arg_value: - encoded_data += '0' * 64 + encoded_data += "0" * 64 offset = format((arg_index + args_length) * 32, "064x") encoded_offsets += offset else: - logger.warn(f"Unknown constructor argument type '{arg_type}', use --constructor-calldata instead") - return encoded_offsets+encoded_data + logger.warn( + f"Unknown constructor argument type '{arg_type}', use --constructor-calldata instead" + ) + return encoded_offsets + encoded_data + def to_hex_with_alignment(value): return format(value, "064x") + def encode_constructor_arguments(constructor_abi, constructor_config_args): arg_length = len(constructor_abi) @@ -52,45 +61,49 @@ def encode_constructor_arguments(constructor_abi, constructor_config_args): constructor_calldata = "" compl_data = [] if arg_length > 0: - for argument_index in range(arg_length): - arg_type = constructor_abi[argument_index]["type"] - 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': - constructor_calldata += encode_uint256(arg_value) - elif arg_type == "bytes32": - constructor_calldata += encode_bytes32(arg_value) - elif arg_type == "bytes": - data = format((argument_index+1)*32, "064x") - constructor_calldata+=data - data2 = encode_bytes(arg_value) - compl_data.append(data2) - elif arg_type == "string": - offset_arg = to_hex_with_alignment((arg_length + len(compl_data))*32) - constructor_calldata += offset_arg - length_arg = to_hex_with_alignment(len(arg_value.encode('utf-8'))) - hex_text = arg_value.encode('utf-8').hex().ljust(64, '0') - compl_data.append(length_arg) - compl_data.append(hex_text) - elif arg_type == "tuple": - args_tuple_types = [component["type"] for component in constructor_abi[argument_index]["components"]] - if len(args_tuple_types) and args_tuple_types[0] == "address[]": - dynamic_type_length = format((len(constructor_calldata)//64 + 1) *32, "064x") - constructor_calldata += dynamic_type_length - logger.info(f'dynamic_type_length {dynamic_type_length}') - compl_data.append(encode_tuple(args_tuple_types, arg_value)) - else: - constructor_calldata += encode_tuple(args_tuple_types, arg_value) - elif arg_type.endswith("[]"): - data = format((argument_index+1)*32, "064x") - constructor_calldata+=data - data2 = encode_bytes(arg_value) - compl_data.append(data2) - else: - raise ValueError(f"Unknown constructor argument type: {arg_type}") - for data in compl_data: + for argument_index in range(arg_length): + arg_type = constructor_abi[argument_index]["type"] + 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": + constructor_calldata += encode_uint256(arg_value) + elif arg_type == "bytes32": + constructor_calldata += encode_bytes32(arg_value) + elif arg_type == "bytes": + data = format((argument_index + 1) * 32, "064x") + constructor_calldata += data + data2 = encode_bytes(arg_value) + compl_data.append(data2) + elif arg_type == "string": + offset_arg = to_hex_with_alignment((arg_length + len(compl_data)) * 32) + constructor_calldata += offset_arg + length_arg = to_hex_with_alignment(len(arg_value.encode("utf-8"))) + hex_text = arg_value.encode("utf-8").hex().ljust(64, "0") + compl_data.append(length_arg) + compl_data.append(hex_text) + elif arg_type == "tuple": + args_tuple_types = [ + component["type"] + for component in constructor_abi[argument_index]["components"] + ] + if len(args_tuple_types) and args_tuple_types[0] == "address[]": + dynamic_type_length = format( + (len(constructor_calldata) // 64 + 1) * 32, "064x" + ) + constructor_calldata += dynamic_type_length + logger.info(f"dynamic_type_length {dynamic_type_length}") + compl_data.append(encode_tuple(args_tuple_types, arg_value)) + else: + constructor_calldata += encode_tuple(args_tuple_types, arg_value) + elif arg_type.endswith("[]"): + data = format((argument_index + 1) * 32, "064x") + constructor_calldata += data + data2 = encode_bytes(arg_value) + compl_data.append(data2) + else: + raise ValueError(f"Unknown constructor argument type: {arg_type}") + for data in compl_data: constructor_calldata += data return constructor_calldata - diff --git a/diffyscan/utils/explorer.py b/diffyscan/utils/explorer.py index 9a8e686..e482cee 100644 --- a/diffyscan/utils/explorer.py +++ b/diffyscan/utils/explorer.py @@ -11,9 +11,7 @@ def _errorNoSourceCodeAndExit(address): def _get_contract_from_etherscan(token, etherscan_hostname, contract): - etherscan_link = ( - f"https://{etherscan_hostname}/api?module=contract&action=getsourcecode&address={contract}" - ) + etherscan_link = f"https://{etherscan_hostname}/api?module=contract&action=getsourcecode&address={contract}" if token is not None: etherscan_link = f"{etherscan_link}&apikey={token}" @@ -26,46 +24,43 @@ def _get_contract_from_etherscan(token, etherscan_hostname, contract): if "ContractName" not in result: _errorNoSourceCodeAndExit(contract) - solc_input = result['SourceCode'] + solc_input = result["SourceCode"] - if solc_input.startswith('{{'): + if solc_input.startswith("{{"): return { - 'name': result['ContractName'], - 'solcInput': json.loads(solc_input[1:-1]), - 'compiler': result['CompilerVersion'] + "name": result["ContractName"], + "solcInput": json.loads(solc_input[1:-1]), + "compiler": result["CompilerVersion"], } else: return { - 'name': result['ContractName'], - 'compiler': result['CompilerVersion'], - 'solcInput': { - 'language': 'Solidity', - 'sources': { - result['ContractName']: { - 'content': solc_input - } - }, - 'settings': { - 'optimizer': { - 'enabled': result['OptimizationUsed'] == '1', - 'runs': int(result['Runs']) + "name": result["ContractName"], + "compiler": result["CompilerVersion"], + "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' + "outputSelection": { + "*": { + "*": [ + "abi", + "evm.bytecode", + "evm.deployedBytecode", + "evm.methodIdentifiers", + "metadata", ], - '': ['ast'] + "": ["ast"], } - } - } - } + }, + }, + }, } + def _get_contract_from_zksync(zksync_explorer_hostname, contract): zksync_explorer_link = ( f"https://{zksync_explorer_hostname}/contract_verification/info/{contract}" @@ -83,15 +78,14 @@ def _get_contract_from_zksync(zksync_explorer_hostname, contract): _errorNoSourceCodeAndExit(contract) return { - 'name': data['ContractName'], - 'sources': json.loads(data["sourceCode"]["sources"]), - 'compiler': data["CompilerVersion"] + "name": data["ContractName"], + "sources": json.loads(data["sourceCode"]["sources"]), + "compiler": data["CompilerVersion"], } + def _get_contract_from_mantle(mantle_explorer_hostname, contract): - etherscan_link = ( - f"https://{mantle_explorer_hostname}/api?module=contract&action=getsourcecode&address={contract}" - ) + etherscan_link = f"https://{mantle_explorer_hostname}/api?module=contract&action=getsourcecode&address={contract}" response = fetch(etherscan_link).json data = response["result"][0] @@ -103,12 +97,15 @@ def _get_contract_from_mantle(mantle_explorer_hostname, contract): source_files.append((entry["Filename"], {"content": entry["SourceCode"]})) return { - 'name': data['ContractName'], - 'sources': json.loads(data["sourceCode"]["sources"]), - 'compiler': data["CompilerVersion"] + "name": data["ContractName"], + "sources": json.loads(data["sourceCode"]["sources"]), + "compiler": data["CompilerVersion"], } -def get_contract_from_explorer(token, explorer_hostname, contract_address, contract_name_from_config): + +def get_contract_from_explorer( + token, explorer_hostname, contract_address, contract_name_from_config +): result = {} if explorer_hostname.startswith("zksync"): result = _get_contract_from_zksync(explorer_hostname, contract_address) @@ -117,12 +114,14 @@ def get_contract_from_explorer(token, explorer_hostname, contract_address, contr elif explorer_hostname.endswith("lineascan.build"): result = _get_contract_from_etherscan(None, explorer_hostname, contract_address) else: - result =_get_contract_from_etherscan(token, explorer_hostname, contract_address) - - contract_name_from_etherscan = result['name'] + result = _get_contract_from_etherscan( + token, explorer_hostname, contract_address + ) + + contract_name_from_etherscan = result["name"] if contract_name_from_etherscan != contract_name_from_config: - raise ValueError( - f"Contract name in config does not match with Blockchain explorer {contract_address}: {contract_name_from_config} != {contract_name_from_etherscan}", - ) - + raise ValueError( + f"Contract name in config does not match with Blockchain explorer {contract_address}: {contract_name_from_config} != {contract_name_from_etherscan}", + ) + return result diff --git a/diffyscan/utils/ganache.py b/diffyscan/utils/ganache.py index 4c1694f..130c015 100644 --- a/diffyscan/utils/ganache.py +++ b/diffyscan/utils/ganache.py @@ -6,54 +6,65 @@ from .logger import logger from .constants import LOCAL_RPC_URL, REMOTE_RPC_URL from .binary_verifier import get_chain_id + + class Ganache: - sub_process = None - TIMEOUT_FOR_INIT_SEC = 5 - - def __init__(self): - pass - - def is_port_in_use(self, parsed_url) -> bool: - import socket - with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: - return s.connect_ex((parsed_url.hostname, parsed_url.port)) == 0 - - def start(self): - parsed_url = urlparse(LOCAL_RPC_URL) - - local_node_command = ( - f'npx ganache --host {parsed_url.hostname} ' \ - f'--port {parsed_url.port} ' \ - f'--chain.chainId {get_chain_id (REMOTE_RPC_URL)} ' \ - f'--fork.url {REMOTE_RPC_URL} ' \ - f'-l 92000000 --hardfork istanbul -d' - ) - - logger.info(f'Trying to start Ganache: "{local_node_command}"') - is_port_used = self.is_port_in_use(parsed_url) - if (is_port_used): - answer = input(f'Port {parsed_url.port} is busy. Fix it? write "yes": ') - if (answer.lower() == 'yes'): - return_code = subprocess.call(f'exec npx kill-port {parsed_url.port}', shell=True) - if (return_code == 0): - is_port_used = self.is_port_in_use(parsed_url) - if (is_port_used): - raise ValueError(f'Failed to start Ganache: {parsed_url.netloc} is busy') - self.sub_process = subprocess.Popen("exec "+ local_node_command, shell=True, stdout=subprocess.DEVNULL, stderr=subprocess.PIPE) - try: - _, errs = self.sub_process.communicate(timeout=self.TIMEOUT_FOR_INIT_SEC) - if (errs): - raise ValueError(f'Failed to start Ganache: {errs.decode()}') - except subprocess.TimeoutExpired: + sub_process = None + TIMEOUT_FOR_INIT_SEC = 5 + + def __init__(self): + pass + + def is_port_in_use(self, parsed_url) -> bool: + import socket + + with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: + return s.connect_ex((parsed_url.hostname, parsed_url.port)) == 0 + + def start(self): + parsed_url = urlparse(LOCAL_RPC_URL) + + local_node_command = ( + f"npx ganache --host {parsed_url.hostname} " + f"--port {parsed_url.port} " + f"--chain.chainId {get_chain_id (REMOTE_RPC_URL)} " + f"--fork.url {REMOTE_RPC_URL} " + f"-l 92000000 --hardfork istanbul -d" + ) + + logger.info(f'Trying to start Ganache: "{local_node_command}"') is_port_used = self.is_port_in_use(parsed_url) - if (is_port_used): - logger.okay(f'Ganache successfully started, PID {self.sub_process.pid}') - else: - raise ValueError(f'Failed to start Ganache: something is wrong') - - 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'Ganache stopped, PID {self.sub_process.pid}') - -ganache = Ganache() \ No newline at end of file + if is_port_used: + answer = input(f'Port {parsed_url.port} is busy. Fix it? write "yes": ') + if answer.lower() == "yes": + return_code = subprocess.call( + f"exec npx kill-port {parsed_url.port}", shell=True + ) + if return_code == 0: + is_port_used = self.is_port_in_use(parsed_url) + if is_port_used: + raise ValueError(f"Failed to start Ganache: {parsed_url.netloc} is busy") + self.sub_process = subprocess.Popen( + "exec " + local_node_command, + shell=True, + stdout=subprocess.DEVNULL, + stderr=subprocess.PIPE, + ) + try: + _, errs = self.sub_process.communicate(timeout=self.TIMEOUT_FOR_INIT_SEC) + if errs: + raise ValueError(f"Failed to start Ganache: {errs.decode()}") + except subprocess.TimeoutExpired: + is_port_used = self.is_port_in_use(parsed_url) + if is_port_used: + logger.okay(f"Ganache successfully started, PID {self.sub_process.pid}") + else: + raise ValueError(f"Failed to start Ganache: something is wrong") + + 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"Ganache stopped, PID {self.sub_process.pid}") + + +ganache = Ganache() From 3e093de448a6c75840805ef028b72fa66001b745 Mon Sep 17 00:00:00 2001 From: alexvozhak Date: Sat, 3 Aug 2024 18:44:16 +0300 Subject: [PATCH 010/118] fixed binary-check arg, removed autoclean arg --- diffyscan/diffyscan.py | 40 +++++++----------------------- diffyscan/utils/binary_verifier.py | 5 ++++ 2 files changed, 14 insertions(+), 31 deletions(-) diff --git a/diffyscan/diffyscan.py b/diffyscan/diffyscan.py index 9c4d083..03b42ec 100755 --- a/diffyscan/diffyscan.py +++ b/diffyscan/diffyscan.py @@ -5,7 +5,6 @@ import os import subprocess import tempfile -import shutil from .utils.common import load_config, load_env from .utils.constants import * @@ -215,11 +214,7 @@ def run_source_diff( def process_config( - path: str, - recursive_parsing: bool, - unify_formatting: bool, - binary_check: bool, - autoclean: bool, + path: str, recursive_parsing: bool, unify_formatting: bool, binary_check: bool ): logger.info(f"Loading config {path}...") config = load_config(path) @@ -260,11 +255,9 @@ def process_config( except KeyboardInterrupt: logger.info(f"Keyboard interrupt by user") finally: - ganache.stop() - - if autoclean: - shutil.rmtree(SOLC_DIR) - logger.okay(f"{SOLC_DIR} deleted") + if binary_check: + ganache.stop() + delete_compilers() def parse_arguments(): @@ -294,12 +287,9 @@ def parse_arguments(): ) parser.add_argument( "--binary-check", - "-binary", + "-b", help="Match contracts by binaries such as verify-bytecode.ts", - default=True, - ) - parser.add_argument( - "--autoclean", "-clean", help="Autoclean build dir after work", default=True + action="store_true", ) return parser.parse_args() @@ -317,30 +307,18 @@ def main(): if args.path is None: process_config( - DEFAULT_CONFIG_PATH, - args.support_brownie, - args.prettify, - args.binary_check, - args.autoclean, + DEFAULT_CONFIG_PATH, args.support_brownie, args.prettify, args.binary_check ) elif os.path.isfile(args.path): process_config( - args.path, - args.support_brownie, - args.prettify, - args.binary_check, - args.autoclean, + args.path, args.support_brownie, args.prettify, args.binary_check ) elif os.path.isdir(args.path): for filename in os.listdir(args.path): config_path = os.path.join(args.path, filename) if os.path.isfile(config_path): process_config( - config_path, - args.support_brownie, - args.prettify, - args.binary_check, - args.autoclean, + config_path, args.support_brownie, args.prettify, args.binary_check ) else: logger.error(f"Specified config path {args.path} not found") diff --git a/diffyscan/utils/binary_verifier.py b/diffyscan/utils/binary_verifier.py index 8388b0f..634f676 100644 --- a/diffyscan/utils/binary_verifier.py +++ b/diffyscan/utils/binary_verifier.py @@ -3,6 +3,7 @@ import json import os import stat +import shutil from .common import fetch, pull, get_solc_native_platform_from_os from .helpers import create_dirs @@ -22,6 +23,10 @@ def get_compiler_info(platform, required_compiler_version): return required_build_info +def delete_compilers(): + shutil.rmtree(SOLC_DIR) + logger.okay(f"{SOLC_DIR} deleted") + def download_compiler(platform, build_info, destination_path): compiler_url = ( f'https://binaries.soliditylang.org/{platform}/{build_info["path"]}' From 930c0d8e7cee20f3cba1f4859eb832e8bf9f1576 Mon Sep 17 00:00:00 2001 From: alexvozhak Date: Sun, 4 Aug 2024 02:28:04 +0300 Subject: [PATCH 011/118] Moved from Ganache to Hardhat --- README.md | 4 ++-- config_samples/lido_dao_sepolia_config.json | 1 + diffyscan/diffyscan.py | 19 +++++++++++-------- diffyscan/utils/binary_verifier.py | 2 +- diffyscan/utils/{ganache.py => hardhat.py} | 12 +++++------- hardhat_configs/sepolia_hardhat.config.js | 10 ++++++++++ package.json | 2 +- 7 files changed, 31 insertions(+), 19 deletions(-) rename diffyscan/utils/{ganache.py => hardhat.py} (89%) create mode 100644 hardhat_configs/sepolia_hardhat.config.js diff --git a/README.md b/README.md index 7306738..0c03beb 100644 --- a/README.md +++ b/README.md @@ -5,10 +5,10 @@ ![license MIT](https://img.shields.io/badge/license-MIT-brightgreen) [![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black) -Diff your Ethereum smart contracts code from GitHub against Blockchain explorer verified source code, deployed bytecode from local node (Ganache) against remote +Diff your Ethereum smart contracts code from GitHub against Blockchain explorer verified source code, deployed bytecode from local node (Hardhat) against remote Supports reformatting solidity code by means of prettifier solidity plugin before comparing the sources (option `--prettify`). -Supports binary matching deployed bytecode from local node (Ganache) against remote (option `--binary-check`) +Supports binary matching deployed bytecode from local node (Hardhat) against remote (option `--binary-check`) ## Install diff --git a/config_samples/lido_dao_sepolia_config.json b/config_samples/lido_dao_sepolia_config.json index ccb5dbc..0957ac9 100644 --- a/config_samples/lido_dao_sepolia_config.json +++ b/config_samples/lido_dao_sepolia_config.json @@ -40,6 +40,7 @@ "0x2dfF8E2c55959f719Faa6dCC4a3B0937bcA71F73": "ACL" }, "explorer_token_env_var": "ETHERSCAN_TOKEN", + "hardhat_config_path": "../hardhat_configs/sepolia_hardhat.config.js", "explorer_hostname": "api-sepolia.etherscan.io", "github_repo": { "url": "https://github.com/lidofinance/lido-dao", diff --git a/diffyscan/diffyscan.py b/diffyscan/diffyscan.py index 03b42ec..55274ed 100755 --- a/diffyscan/diffyscan.py +++ b/diffyscan/diffyscan.py @@ -17,7 +17,7 @@ from .utils.helpers import create_dirs from .utils.logger import logger from .utils.binary_verifier import * -from .utils.ganache import ganache +from .utils.hardhat import hardhat __version__ = "0.0.0" @@ -48,7 +48,9 @@ def prettify_solidity(solidity_contract_content: str): return fp.read() -def run_binary_diff(remote_contract_address, contract_source_code, config): +def run_binary_diff( + remote_contract_address, contract_source_code, config, deployer_account +): logger.info(f"Started binary checking for {remote_contract_address}") contract_creation_code, immutables, is_valid_constructor = ( @@ -61,8 +63,6 @@ def run_binary_diff(remote_contract_address, contract_source_code, config): logger.error(f"Failed to find constructorArgs, binary diff skipped") return - deployer_account = get_account(LOCAL_RPC_URL) - if deployer_account is None: logger.error(f"Failed to receive the account, binary diff skipped") return @@ -73,7 +73,7 @@ def run_binary_diff(remote_contract_address, contract_source_code, config): if local_contract_address is None: logger.error( - f"Failed to deploy bytecode to {LOCAL_RPC_URL}, binary diff skipped" + f"Failed to deploy bytecode to {LOCAL_RPC_URL}, binary diff skipped ({remote_contract_address})" ) return @@ -233,7 +233,7 @@ def process_config( try: if binary_check: - ganache.start() + hardhat.start(config["hardhat_config_path"]) for contract_address, contract_name in contracts.items(): contract_code = get_contract_from_explorer( @@ -251,12 +251,15 @@ def process_config( unify_formatting, ) if binary_check: - run_binary_diff(contract_address, contract_code, config) + deployer_account = get_account(LOCAL_RPC_URL) + run_binary_diff( + contract_address, contract_code, config, deployer_account + ) except KeyboardInterrupt: logger.info(f"Keyboard interrupt by user") finally: if binary_check: - ganache.stop() + hardhat.stop() delete_compilers() diff --git a/diffyscan/utils/binary_verifier.py b/diffyscan/utils/binary_verifier.py index 634f676..61bd01e 100644 --- a/diffyscan/utils/binary_verifier.py +++ b/diffyscan/utils/binary_verifier.py @@ -164,7 +164,7 @@ def deploy_contract(rpc_url, deployer, data): response_sendTransaction = pull(rpc_url, payload_sendTransaction).json() if 'error' in response_sendTransaction: - logger.warn(f'Failed to deploy transaction: {response_sendTransaction['error']['message']}') + logger.error(f'Failed to deploy transaction: {response_sendTransaction['error']['message']}') return None logger.okay(f'Transaction was successfully deployed') diff --git a/diffyscan/utils/ganache.py b/diffyscan/utils/hardhat.py similarity index 89% rename from diffyscan/utils/ganache.py rename to diffyscan/utils/hardhat.py index 130c015..d2f6290 100644 --- a/diffyscan/utils/ganache.py +++ b/diffyscan/utils/hardhat.py @@ -8,7 +8,7 @@ from .binary_verifier import get_chain_id -class Ganache: +class Hardhat: sub_process = None TIMEOUT_FOR_INIT_SEC = 5 @@ -21,15 +21,13 @@ def is_port_in_use(self, parsed_url) -> bool: with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: return s.connect_ex((parsed_url.hostname, parsed_url.port)) == 0 - def start(self): + def start(self, hardhat_path): parsed_url = urlparse(LOCAL_RPC_URL) local_node_command = ( - f"npx ganache --host {parsed_url.hostname} " + f"npx hardhat node --hostname {parsed_url.hostname} " f"--port {parsed_url.port} " - f"--chain.chainId {get_chain_id (REMOTE_RPC_URL)} " - f"--fork.url {REMOTE_RPC_URL} " - f"-l 92000000 --hardfork istanbul -d" + f"--config {hardhat_path} " ) logger.info(f'Trying to start Ganache: "{local_node_command}"') @@ -67,4 +65,4 @@ def stop(self): logger.info(f"Ganache stopped, PID {self.sub_process.pid}") -ganache = Ganache() +hardhat = Hardhat() diff --git a/hardhat_configs/sepolia_hardhat.config.js b/hardhat_configs/sepolia_hardhat.config.js new file mode 100644 index 0000000..8b1932e --- /dev/null +++ b/hardhat_configs/sepolia_hardhat.config.js @@ -0,0 +1,10 @@ +module.exports = { + solidity: "0.8.9", + networks: { + hardhat: { + chainId: 11155111, + blockGasLimit: 92000000, + hardfork: "istanbul", + } + }, +}; diff --git a/package.json b/package.json index b23f505..75e31d4 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "devDependencies": { "prettier": "^3.3.0", "prettier-plugin-solidity": "^1.3.1", - "ganache": "^7.9.2", + "hardhat": "^2.22.7", "kill-port": "^2.0.1" } } From 1b38ff6e131aaa90d6bcf85b2399c7c3813fd9a7 Mon Sep 17 00:00:00 2001 From: alexvozhak Date: Sun, 4 Aug 2024 03:03:56 +0300 Subject: [PATCH 012/118] Renamed Ganache to Hardhat in logs --- diffyscan/utils/hardhat.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/diffyscan/utils/hardhat.py b/diffyscan/utils/hardhat.py index d2f6290..619b0c4 100644 --- a/diffyscan/utils/hardhat.py +++ b/diffyscan/utils/hardhat.py @@ -30,7 +30,7 @@ def start(self, hardhat_path): f"--config {hardhat_path} " ) - logger.info(f'Trying to start Ganache: "{local_node_command}"') + logger.info(f'Trying to start Hardhat: "{local_node_command}"') is_port_used = self.is_port_in_use(parsed_url) if is_port_used: answer = input(f'Port {parsed_url.port} is busy. Fix it? write "yes": ') @@ -41,7 +41,7 @@ def start(self, hardhat_path): if return_code == 0: is_port_used = self.is_port_in_use(parsed_url) if is_port_used: - raise ValueError(f"Failed to start Ganache: {parsed_url.netloc} is busy") + raise ValueError(f"Failed to start Hardhat: {parsed_url.netloc} is busy") self.sub_process = subprocess.Popen( "exec " + local_node_command, shell=True, @@ -51,18 +51,18 @@ def start(self, hardhat_path): try: _, errs = self.sub_process.communicate(timeout=self.TIMEOUT_FOR_INIT_SEC) if errs: - raise ValueError(f"Failed to start Ganache: {errs.decode()}") + raise ValueError(f"Failed to start Hardhat: {errs.decode()}") except subprocess.TimeoutExpired: is_port_used = self.is_port_in_use(parsed_url) if is_port_used: - logger.okay(f"Ganache successfully started, PID {self.sub_process.pid}") + logger.okay(f"Hardhat successfully started, PID {self.sub_process.pid}") else: - raise ValueError(f"Failed to start Ganache: something is wrong") + raise ValueError(f"Failed to start Hardhat: something is wrong") 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"Ganache stopped, PID {self.sub_process.pid}") + logger.info(f"Hardhat stopped, PID {self.sub_process.pid}") hardhat = Hardhat() From 72faada923de7293a0e469c1e18cd42d6e98eca5 Mon Sep 17 00:00:00 2001 From: alexvozhak Date: Sun, 4 Aug 2024 03:31:23 +0300 Subject: [PATCH 013/118] Added new arg for Hardhat config path --- README.md | 2 +- config_samples/lido_dao_sepolia_config.json | 1 - diffyscan/diffyscan.py | 33 +++++++++++++++++---- 3 files changed, 29 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 0c03beb..563db9c 100644 --- a/README.md +++ b/README.md @@ -50,7 +50,7 @@ export SOLC_DIR= Start script with one of the examples provided (or entire folder of configs) ```bash -diffyscan config_samples/lido_dao_sepolia_config.json +diffyscan config_samples/lido_dao_sepolia_config.json hardhat_configs/sepolia_hardhat.config.js ``` Alternatively, create a new config file named `config.json`, diff --git a/config_samples/lido_dao_sepolia_config.json b/config_samples/lido_dao_sepolia_config.json index 0957ac9..ccb5dbc 100644 --- a/config_samples/lido_dao_sepolia_config.json +++ b/config_samples/lido_dao_sepolia_config.json @@ -40,7 +40,6 @@ "0x2dfF8E2c55959f719Faa6dCC4a3B0937bcA71F73": "ACL" }, "explorer_token_env_var": "ETHERSCAN_TOKEN", - "hardhat_config_path": "../hardhat_configs/sepolia_hardhat.config.js", "explorer_hostname": "api-sepolia.etherscan.io", "github_repo": { "url": "https://github.com/lidofinance/lido-dao", diff --git a/diffyscan/diffyscan.py b/diffyscan/diffyscan.py index 55274ed..6d5a153 100755 --- a/diffyscan/diffyscan.py +++ b/diffyscan/diffyscan.py @@ -214,7 +214,11 @@ def run_source_diff( def process_config( - path: str, recursive_parsing: bool, unify_formatting: bool, binary_check: bool + path: str, + hardhat_path: str, + recursive_parsing: bool, + unify_formatting: bool, + binary_check: bool, ): logger.info(f"Loading config {path}...") config = load_config(path) @@ -233,7 +237,7 @@ def process_config( try: if binary_check: - hardhat.start(config["hardhat_config_path"]) + hardhat.start(hardhat_path) for contract_address, contract_name in contracts.items(): contract_code = get_contract_from_explorer( @@ -271,6 +275,9 @@ def parse_arguments(): parser.add_argument( "path", nargs="?", default=None, help="Path to config or directory with configs" ) + parser.add_argument( + "hardhat_path", nargs="?", default=None, help="Path to Hardhat config" + ) parser.add_argument( "--yes", "-y", @@ -307,21 +314,37 @@ def main(): return logger.info("Welcome to Diffyscan!") logger.divider() + if args.hardhat_path is None or not os.path.isfile( + os.path.abspath(args.hardhat_path) + ): + raise ValueError(f"Path to hardhat config not found") if args.path is None: process_config( - DEFAULT_CONFIG_PATH, args.support_brownie, args.prettify, args.binary_check + DEFAULT_CONFIG_PATH, + os.path.abspath(args.hardhat_path), + args.support_brownie, + args.prettify, + args.binary_check, ) elif os.path.isfile(args.path): process_config( - args.path, args.support_brownie, args.prettify, args.binary_check + args.path, + args.hardhat_path, + args.support_brownie, + args.prettify, + args.binary_check, ) elif os.path.isdir(args.path): for filename in os.listdir(args.path): config_path = os.path.join(args.path, filename) if os.path.isfile(config_path): process_config( - config_path, args.support_brownie, args.prettify, args.binary_check + config_path, + args.hardhat_path, + args.support_brownie, + args.prettify, + args.binary_check, ) else: logger.error(f"Specified config path {args.path} not found") From 2c5c83178c6b6434cb486ff2ef2eedaf6c94b059 Mon Sep 17 00:00:00 2001 From: alexvozhak Date: Sun, 4 Aug 2024 03:32:02 +0300 Subject: [PATCH 014/118] Added small check before deleting compilers directory --- diffyscan/utils/binary_verifier.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/diffyscan/utils/binary_verifier.py b/diffyscan/utils/binary_verifier.py index 61bd01e..862f6b0 100644 --- a/diffyscan/utils/binary_verifier.py +++ b/diffyscan/utils/binary_verifier.py @@ -24,8 +24,9 @@ def get_compiler_info(platform, required_compiler_version): return required_build_info def delete_compilers(): - shutil.rmtree(SOLC_DIR) - logger.okay(f"{SOLC_DIR} deleted") + if os.path.exists(SOLC_DIR): + shutil.rmtree(SOLC_DIR) + logger.okay(f"{SOLC_DIR} deleted") def download_compiler(platform, build_info, destination_path): compiler_url = ( From 3c40dd888a6573eea77fde0c3128fa52c766894e Mon Sep 17 00:00:00 2001 From: alexvozhak Date: Sun, 4 Aug 2024 15:34:39 +0300 Subject: [PATCH 015/118] Now eth_accounts is sent at once --- diffyscan/diffyscan.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/diffyscan/diffyscan.py b/diffyscan/diffyscan.py index 6d5a153..2f15350 100755 --- a/diffyscan/diffyscan.py +++ b/diffyscan/diffyscan.py @@ -238,6 +238,7 @@ def process_config( try: if binary_check: hardhat.start(hardhat_path) + deployer_account = get_account(LOCAL_RPC_URL) for contract_address, contract_name in contracts.items(): contract_code = get_contract_from_explorer( @@ -255,7 +256,6 @@ def process_config( unify_formatting, ) if binary_check: - deployer_account = get_account(LOCAL_RPC_URL) run_binary_diff( contract_address, contract_code, config, deployer_account ) From 234543cbad281710bc7b5d9ffe2af6e5690007e4 Mon Sep 17 00:00:00 2001 From: alexvozhak Date: Tue, 6 Aug 2024 01:27:10 +0300 Subject: [PATCH 016/118] Fixed review comments --- README.md | 14 +- config_samples/lido_dao_sepolia_config.json | 7 + diffyscan/diffyscan.py | 157 ++++++++------------ diffyscan/utils/binary_verifier.py | 131 +++++----------- diffyscan/utils/common.py | 76 +++++++--- diffyscan/utils/constants.py | 49 ++++-- diffyscan/utils/hardhat.py | 21 ++- diffyscan/utils/logger.py | 24 +++ 8 files changed, 241 insertions(+), 238 deletions(-) diff --git a/README.md b/README.md index 563db9c..560c848 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ Diff your Ethereum smart contracts code from GitHub against Blockchain explorer verified source code, deployed bytecode from local node (Hardhat) against remote Supports reformatting solidity code by means of prettifier solidity plugin before comparing the sources (option `--prettify`). -Supports binary matching deployed bytecode from local node (Hardhat) against remote (option `--binary-check`) +Supports binary matching deployed bytecode from local node (Hardhat) against remote (see section 'binary_checking' in lido_dao_sepolia_config.json as example) ## Install @@ -16,7 +16,7 @@ Supports binary matching deployed bytecode from local node (Hardhat) against rem pipx install git+https://github.com/lidofinance/diffyscan ``` -If need `--prettify`, '--binary-check' options +If need `--prettify` options ```shell npm install @@ -37,16 +37,6 @@ export GITHUB_API_TOKEN= ``` Set local and remote RPC URLs for matching bytecode from local node againts remote -```bash -export REMOTE_RPC_URL= //https://ethereum-sepolia-rpc.publicnode.com -export LOCAL_RPC_URL= //http://127.0.0.1:7545 -``` -Set absolute or relative path for downloading solc compilers - -```bash -export SOLC_DIR= -``` - Start script with one of the examples provided (or entire folder of configs) ```bash diff --git a/config_samples/lido_dao_sepolia_config.json b/config_samples/lido_dao_sepolia_config.json index ccb5dbc..271c62f 100644 --- a/config_samples/lido_dao_sepolia_config.json +++ b/config_samples/lido_dao_sepolia_config.json @@ -41,6 +41,13 @@ }, "explorer_token_env_var": "ETHERSCAN_TOKEN", "explorer_hostname": "api-sepolia.etherscan.io", + "binary_checking": { + "use": 1, + "skip_deploy_error" : 1, + "hardhat_config_name": "sepolia_hardhat.config.js", + "local_RPC_URL": "http://127.0.0.1:7545", + "remote_RPC_URL":"https://ethereum-sepolia-rpc.publicnode.com" + }, "github_repo": { "url": "https://github.com/lidofinance/lido-dao", "commit": "cadffa46a2b8ed6cfa1127fca2468bae1a82d6bf", diff --git a/diffyscan/diffyscan.py b/diffyscan/diffyscan.py index 2f15350..a1b1618 100755 --- a/diffyscan/diffyscan.py +++ b/diffyscan/diffyscan.py @@ -3,10 +3,8 @@ import time import argparse import os -import subprocess -import tempfile -from .utils.common import load_config, load_env +from .utils.common import load_config, load_env, prettify_solidity from .utils.constants import * from .utils.explorer import get_contract_from_explorer from .utils.github import ( @@ -25,76 +23,75 @@ g_skip_user_input: bool = False -def prettify_solidity(solidity_contract_content: str): - github_file_name = os.path.join( - tempfile.gettempdir(), "9B91E897-EA51-4FCC-8DAF-FCFF135A6963.sol" - ) - with open(github_file_name, "w") as fp: - fp.write(solidity_contract_content) - prettier_return_code = subprocess.call( - [ - "npx", - "prettier", - "--plugin=prettier-plugin-solidity", - "--write", - github_file_name, - ], - stdout=subprocess.DEVNULL, - ) - if prettier_return_code != 0: - logger.error("Prettier/npx subprocess failed (see the error above)") - sys.exit() - with open(github_file_name, "r") as fp: - return fp.read() - - def run_binary_diff( - remote_contract_address, contract_source_code, config, deployer_account + contract_address_from_config, + contract_name_from_config, + contract_source_code, + config, + deployer_account, ): - logger.info(f"Started binary checking for {remote_contract_address}") - - contract_creation_code, immutables, is_valid_constructor = ( - get_contract_creation_code_from_etherscan( - contract_source_code, config, remote_contract_address - ) - ) - - if not is_valid_constructor: - logger.error(f"Failed to find constructorArgs, binary diff skipped") - return + address_name = f"({contract_address_from_config} : {contract_name_from_config})" + logger.divider() + logger.info(f"Started binary checking for {address_name}") if deployer_account is None: - logger.error(f"Failed to receive the account, binary diff skipped") - return + raise ValueError(f"Failed to receive the account {address_name})") + if "ConstructorArgs" not in config: + raise ValueError(f"Failed to find constructorArgs {address_name})") + + contract_creation_code, immutables = get_contract_creation_code_from_etherscan( + contract_source_code, + config["ConstructorArgs"], + contract_address_from_config, + ) - local_contract_address = deploy_contract( - LOCAL_RPC_URL, deployer_account, contract_creation_code + local_RPC_URL = config["binary_checking"]["local_RPC_URL"] + local_contract_address, text_reason = deploy_contract( + local_RPC_URL, deployer_account, contract_creation_code ) + skip_deploy_error = config["binary_checking"]["skip_deploy_error"] if local_contract_address is None: - logger.error( - f"Failed to deploy bytecode to {LOCAL_RPC_URL}, binary diff skipped ({remote_contract_address})" + skip_or_raise( + skip_deploy_error, + f"Failed to deploy bytecode to {local_RPC_URL} {address_name} {text_reason}", ) return - local_deployed_bytecode = get_bytecode(local_contract_address, LOCAL_RPC_URL) + local_deployed_bytecode = get_bytecode(local_contract_address, local_RPC_URL) if local_deployed_bytecode is None: - logger.error(f"Failed to receive bytecode from {LOCAL_RPC_URL}") + skip_or_raise( + skip_deploy_error, + text_error=f"Failed to receive bytecode from {local_RPC_URL} {address_name})", + ) return - remote_deployed_bytecode = get_bytecode(remote_contract_address, REMOTE_RPC_URL) + remote_RPC_URL = config["binary_checking"]["remote_RPC_URL"] + remote_deployed_bytecode = get_bytecode( + contract_address_from_config, remote_RPC_URL + ) if remote_deployed_bytecode is None: - logger.error(f"Failed to receive bytecode from {REMOTE_RPC_URL}") + skip_or_raise( + skip_deploy_error, + f"Failed to receive bytecode from {remote_RPC_URL} {address_name})", + ) return to_match( local_deployed_bytecode, remote_deployed_bytecode, immutables, - remote_contract_address, + contract_address_from_config, ) +def skip_or_raise(skip_deploy_error, text_error): + if skip_deploy_error: + logger.error(text_error) + else: + raise ValueError(text_error) + + def run_source_diff( contract_address_from_config, contract_code, @@ -213,13 +210,7 @@ def run_source_diff( logger.report_table(report) -def process_config( - path: str, - hardhat_path: str, - recursive_parsing: bool, - unify_formatting: bool, - binary_check: bool, -): +def process_config(path: str, recursive_parsing: bool, unify_formatting: bool): logger.info(f"Loading config {path}...") config = load_config(path) @@ -234,11 +225,16 @@ def process_config( raise ValueError(f'Failed to find "ETHERSCAN_EXPLORER_TOKEN" in env') contracts = config["contracts"] + binary_check = ( + "binary_checking" in config + and "use" in config["binary_checking"] + and config["binary_checking"]["use"] + ) try: if binary_check: - hardhat.start(hardhat_path) - deployer_account = get_account(LOCAL_RPC_URL) + hardhat.start(path, config["binary_checking"]) + deployer_account = get_account(config["binary_checking"]["local_RPC_URL"]) for contract_address, contract_name in contracts.items(): contract_code = get_contract_from_explorer( @@ -257,14 +253,17 @@ def process_config( ) if binary_check: run_binary_diff( - contract_address, contract_code, config, deployer_account + contract_address, + contract_name, + contract_code, + config, + deployer_account, ) except KeyboardInterrupt: logger.info(f"Keyboard interrupt by user") finally: if binary_check: hardhat.stop() - delete_compilers() def parse_arguments(): @@ -275,9 +274,6 @@ def parse_arguments(): parser.add_argument( "path", nargs="?", default=None, help="Path to config or directory with configs" ) - parser.add_argument( - "hardhat_path", nargs="?", default=None, help="Path to Hardhat config" - ) parser.add_argument( "--yes", "-y", @@ -295,12 +291,6 @@ def parse_arguments(): help="Unify formatting by prettier before comparing", action="store_true", ) - parser.add_argument( - "--binary-check", - "-b", - help="Match contracts by binaries such as verify-bytecode.ts", - action="store_true", - ) return parser.parse_args() @@ -314,38 +304,15 @@ def main(): return logger.info("Welcome to Diffyscan!") logger.divider() - if args.hardhat_path is None or not os.path.isfile( - os.path.abspath(args.hardhat_path) - ): - raise ValueError(f"Path to hardhat config not found") - if args.path is None: - process_config( - DEFAULT_CONFIG_PATH, - os.path.abspath(args.hardhat_path), - args.support_brownie, - args.prettify, - args.binary_check, - ) + process_config(DEFAULT_CONFIG_PATH, args.support_brownie, args.prettify) elif os.path.isfile(args.path): - process_config( - args.path, - args.hardhat_path, - args.support_brownie, - args.prettify, - args.binary_check, - ) + process_config(args.path, args.support_brownie, args.prettify) elif os.path.isdir(args.path): for filename in os.listdir(args.path): config_path = os.path.join(args.path, filename) if os.path.isfile(config_path): - process_config( - config_path, - args.hardhat_path, - args.support_brownie, - args.prettify, - args.binary_check, - ) + process_config(config_path, args.support_brownie, args.prettify) else: logger.error(f"Specified config path {args.path} not found") sys.exit(1) diff --git a/diffyscan/utils/binary_verifier.py b/diffyscan/utils/binary_verifier.py index 862f6b0..c7e2bc6 100644 --- a/diffyscan/utils/binary_verifier.py +++ b/diffyscan/utils/binary_verifier.py @@ -3,14 +3,13 @@ import json import os import stat -import shutil from .common import fetch, pull, get_solc_native_platform_from_os from .helpers import create_dirs -from .logger import logger +from .logger import * from .encoder import encode_constructor_arguments -from .constants import SOLC_DIR - +from .constants import SOLC_DIR, OPCODES + def get_compiler_info(platform, required_compiler_version): compilers_list_url = ( f'https://raw.githubusercontent.com/ethereum/solc-bin/gh-pages/{platform}/list.json' @@ -23,19 +22,19 @@ def get_compiler_info(platform, required_compiler_version): return required_build_info -def delete_compilers(): - if os.path.exists(SOLC_DIR): - shutil.rmtree(SOLC_DIR) - logger.okay(f"{SOLC_DIR} deleted") - def download_compiler(platform, build_info, destination_path): compiler_url = ( f'https://binaries.soliditylang.org/{platform}/{build_info["path"]}' ) download_compiler_response = fetch(compiler_url) - with open(destination_path, 'wb') as compiler_file: - compiler_file.write(download_compiler_response.content) + try: + with open(destination_path, 'wb') as compiler_file: + compiler_file.write(download_compiler_response.content) + except IOError as e: + raise ValueError(f"Error writing to file: {e}") + except Exception as e: + raise ValueError(f"An error occurred: {e}") return download_compiler_response.content def check_compiler_checksum(compiler, valid_checksum): @@ -48,7 +47,14 @@ def set_compiler_executable(compiler_path): os.chmod(compiler_path, compiler_file_rights.st_mode | stat.S_IEXEC) def compile_contracts(compiler_path, input_settings): - process = subprocess.run([compiler_path,'--standard-json'], input=input_settings.encode(), capture_output=True, check=True, timeout=30) + try: + process = subprocess.run([compiler_path,'--standard-json'], input=input_settings.encode(), capture_output=True, check=True, timeout=30) + except subprocess.CalledProcessError as e: + raise ValueError(f"Error during subprocess execution: {e}") + except subprocess.TimeoutExpired as e: + raise ValueError(f"Process timed out: {e}") + except Exception as e: + raise ValueError(f"An unexpected error occurred: {e}") return json.loads(process.stdout) def prepare_compiler(platform, build_info, compiler_path): @@ -72,7 +78,7 @@ def get_target_compiled_contract(compiled_contracts, target_contract_name): return contracts_to_check[0] -def get_contract_creation_code_from_etherscan(contract_code, config, remote_contract_address): +def get_contract_creation_code_from_etherscan(contract_code, constructor_args, contract_address_from_config): platform = get_solc_native_platform_from_os() build_name = contract_code["compiler"][1:] build_info = get_compiler_info(platform, build_name) @@ -101,18 +107,20 @@ def get_contract_creation_code_from_etherscan(contract_code, config, remote_cont constructor_abi = [entry["inputs"] for entry in target_compiled_contract['abi'] if entry["type"] == "constructor"][0] except IndexError: logger.info(f'Constructor in ABI not found') - return contract_creation_code, immutables, True + return contract_creation_code, immutables + + if contract_address_from_config not in constructor_args: + raise ValueError(f"Failed to find constructorArgs") constructor_calldata = None - if "ConstructorArgs" in config and remote_contract_address in config["ConstructorArgs"]: - constructor_args = config["ConstructorArgs"][remote_contract_address] + if constructor_args is not None and contract_address_from_config in constructor_args: + constructor_args = constructor_args [contract_address_from_config] if constructor_args: constructor_calldata = encode_constructor_arguments(constructor_abi, constructor_args) - return contract_creation_code+constructor_calldata, immutables, True + return contract_creation_code+constructor_calldata, immutables - logger.warn(f"Constructor in ABI found, but config section 'ConstructorArgs' not found (contract {target_contract_name})") - return contract_creation_code, immutables, True + return contract_creation_code, immutables, False def get_bytecode(contract_address, rpc_url): logger.info(f'Receiving the bytecode from "{rpc_url}" ...') @@ -126,15 +134,6 @@ def get_bytecode(contract_address, rpc_url): logger.okay(f'Bytecode was successfully received') return sources_url_response_in_json['result'] -def get_chain_id(rpc_url): - payload = json.dumps({'id': 1, 'jsonrpc': '2.0', 'method': 'eth_chainId', 'params': []}) - - response = pull(rpc_url, payload).json() - if 'error' in response: - logger.error(f'Failed to received chainId: {response['error']['message']}') - return 1 - return int (response['result'],16) - def get_account(rpc_url): logger.info(f'Receiving the account from "{rpc_url}" ...') @@ -165,8 +164,7 @@ def deploy_contract(rpc_url, deployer, data): response_sendTransaction = pull(rpc_url, payload_sendTransaction).json() if 'error' in response_sendTransaction: - logger.error(f'Failed to deploy transaction: {response_sendTransaction['error']['message']}') - return None + return None, response_sendTransaction['error']['message'] logger.okay(f'Transaction was successfully deployed') @@ -178,52 +176,16 @@ def deploy_contract(rpc_url, deployer, data): if 'result' not in response_getTransactionReceipt or \ 'contractAddress' not in response_getTransactionReceipt['result'] or \ 'status' not in response_getTransactionReceipt['result'] : - logger.error(f'Failed to received transaction receipt') - return None + return None, f'Failed to received transaction receipt' if response_getTransactionReceipt['result']['status'] != '0x1': - logger.error(f'Failed to received transaction receipt. Transaction has been reverted (status 0x0). Input missmatch?') - return None + return None, f'Failed to received transaction receipt. \ + Transaction has been reverted (status 0x0). Input missmatch?' contract_address = response_getTransactionReceipt['result']['contractAddress'] - return contract_address - - -OPCODES = { - 0x00: 'STOP', 0x01: 'ADD', 0x02: 'MUL', 0x03: 'SUB', 0x04: 'DIV', 0x05: 'SDIV', - 0x06: 'MOD', 0x07: 'SMOD', 0x08: 'ADDMOD', 0x09: 'MULMOD', 0x0A: 'EXP', 0x0B: 'SIGNEXTEND', - 0x10: 'LT', 0x11: 'GT', 0x12: 'SLT', 0x13: 'SGT', 0x14: 'EQ', 0x15: 'ISZERO', 0x16: 'AND', - 0x17: 'OR', 0x18: 'XOR', 0x19: 'NOT', 0x1A: 'BYTE', 0x1B: 'SHL', 0x1C: 'SHR', 0x1D: 'SAR', - 0x20: 'SHA3', - 0x30: 'ADDRESS', 0x31: 'BALANCE', 0x32: 'ORIGIN', 0x33: 'CALLER', - 0x34: 'CALLVALUE', 0x35: 'CALLDATALOAD', 0x36: 'CALLDATASIZE', 0x37: 'CALLDATACOPY', - 0x38: 'CODESIZE', 0x39: 'CODECOPY', 0x3A: 'GASPRICE', 0x3B: 'EXTCODESIZE', - 0x3C: 'EXTCODECOPY', 0x3D: 'RETURNDATASIZE', 0x3E: 'RETURNDATACOPY', 0x3F: 'EXTCODEHASH', - 0x40: 'BLOCKHASH', 0x41: 'COINBASE', 0x42: 'TIMESTAMP', 0x43: 'NUMBER', - 0x44: 'PREVRANDAO', 0x45: 'GASLIMIT', 0x46: 'CHAINID', 0x47: 'SELFBALANCE', 0x48: 'BASEFEE', - 0x50: 'POP', 0x51: 'MLOAD', 0x52: 'MSTORE', 0x53: 'MSTORE8', - 0x54: 'SLOAD', 0x55: 'SSTORE', 0x56: 'JUMP', 0x57: 'JUMPI', - 0x58: 'PC', 0x59: 'MSIZE', 0x5A: 'GAS', 0x5B: 'JUMPDEST', - 0x5F: 'PUSH0', 0x60: 'PUSH1', 0x61: 'PUSH2', 0x62: 'PUSH3', 0x63: 'PUSH4', 0x64: 'PUSH5', - 0x65: 'PUSH6', 0x66: 'PUSH7', 0x67: 'PUSH8', 0x68: 'PUSH9', 0x69: 'PUSH10', 0x6A: 'PUSH11', - 0x6B: 'PUSH12', 0x6C: 'PUSH13', 0x6D: 'PUSH14', 0x6E: 'PUSH15', 0x6F: 'PUSH16', 0x70: 'PUSH17', - 0x71: 'PUSH18', 0x72: 'PUSH19', 0x73: 'PUSH20', 0x74: 'PUSH21', 0x75: 'PUSH22', 0x76: 'PUSH23', - 0x77: 'PUSH24', 0x78: 'PUSH25', 0x79: 'PUSH26', 0x7A: 'PUSH27', 0x7B: 'PUSH28', 0x7C: 'PUSH29', - 0x7D: 'PUSH30', 0x7E: 'PUSH31', 0x7F: 'PUSH32', - 0x80: 'DUP1', 0x81: 'DUP2', 0x82: 'DUP3', 0x83: 'DUP4', - 0x84: 'DUP5', 0x85: 'DUP6', 0x86: 'DUP7', 0x87: 'DUP8', - 0x88: 'DUP9', 0x89: 'DUP10', 0x8A: 'DUP11', 0x8B: 'DUP12', - 0x8C: 'DUP13', 0x8D: 'DUP14', 0x8E: 'DUP15', 0x8F: 'DUP16', - 0x90: 'SWAP1', 0x91: 'SWAP2', 0x92: 'SWAP3', 0x93: 'SWAP4', - 0x94: 'SWAP5', 0x95: 'SWAP6', 0x96: 'SWAP7', 0x97: 'SWAP8', - 0x98: 'SWAP9', 0x99: 'SWAP10', 0x9A: 'SWAP11', 0x9B: 'SWAP12', - 0x9C: 'SWAP13', 0x9D: 'SWAP14', 0x9E: 'SWAP15', 0x9F: 'SWAP16', - 0xA0: 'LOG0', 0xA1: 'LOG1', 0xA2: 'LOG2', 0xA3: 'LOG3', 0xA4: 'LOG4', - 0xF0: 'CREATE', 0xF1: 'CALL', 0xF2: 'CALLCODE', 0xF3: 'RETURN', 0xF4: 'DELEGATECALL', - 0xF5: 'CREATE2', 0xFA: 'STATICCALL', 0xFD: 'REVERT', 0xFE: 'INVALID', 0xFF: 'SELFDESTRUCT', -} - + return contract_address, '' + def to_match(actualBytecode, expectedBytecode, immutables, remote_contract_address): logger.info('Comparing actual code with the expected one...') @@ -262,24 +224,7 @@ def to_match(actualBytecode, expectedBytecode, immutables, remote_contract_addre checkpointsArray = sorted(list(checkpoints)) - def hex(index, padStart=2): - return f'{index:0{padStart}X}' - - def red(text): - return f'\u001b[31m{text}\x1b[0m' - - def bgRed(text): - return f'\u001b[37;41m{text}\x1b[0m' - - def green(text): - return f'\u001b[32m{text}\x1b[0m' - - def bgGreen(text): - return f'\u001b[37;42m{text}\x1b[0m' - - def bgYellow(text): - return f'\u001b[37;43m{text}\x1b[0m' - + logger.divider() logger.info(f'0000 00 STOP - both expected and actual bytecode instructions match') logger.info(f'{bgRed("0x0002")} - the actual bytecode differs') @@ -299,12 +244,12 @@ def bgYellow(text): if not actual and expected: params = '0x' + expected['bytecode'][2:] - print(red(f'{hex(currInd, 4)} {hex(expected["op"]["code"])} {expected["op"]["name"]} {params}')) + print(logger.red(f'{to_hex(currInd, 4)} {to_hex(expected["op"]["code"])} {expected["op"]["name"]} {params}')) elif actual and not expected: params = '0x' + actual['bytecode'][2:] - print(green(f'{hex(currInd, 4)} {hex(actual["op"]["code"])} {actual["op"]["name"]} {params}')) + print(logger.green(f'{to_hex(currInd, 4)} {to_hex(actual["op"]["code"])} {actual["op"]["name"]} {params}')) elif actual and expected: - opcode = hex(actual["op"]["code"]) if actual["op"]["code"] == expected["op"]["code"] else bgRed(hex(actual["op"]["code"])) + ' ' + bgGreen(hex(expected["op"]["code"])) + opcode = to_hex(actual["op"]["code"]) if actual["op"]["code"] == expected["op"]["code"] else bgRed(to_hex(actual["op"]["code"])) + ' ' + bgGreen(to_hex(expected["op"]["code"])) opname = actual["op"]["name"] if actual["op"]["name"] == expected["op"]["name"] else bgRed(actual["op"]["name"]) + ' ' + bgGreen(expected["op"]["name"]) actualParams = '0x' + actual['bytecode'][2:] if len(actual['bytecode']) > 2 else '' expectedParams = '0x' + expected['bytecode'][2:] if len(expected['bytecode']) > 2 else '' @@ -312,7 +257,7 @@ def bgYellow(text): paramsLength = len(expected['bytecode']) // 2 - 1 isImmutable = immutables.get(expected['start'] + 1) == paramsLength params = actualParams if actualParams == expectedParams else (bgYellow(actualParams) + ' ' + bgGreen(expectedParams) if isImmutable else bgRed(actualParams) + ' ' + bgGreen(expectedParams)) - print(f'{hex(currInd, 4)} {opcode} {opname} {params}') + print(f'{to_hex(currInd, 4)} {opcode} {opname} {params}') else: raise ValueError('Invalid bytecode difference data') diff --git a/diffyscan/utils/common.py b/diffyscan/utils/common.py index 34b2507..3f1f0fb 100644 --- a/diffyscan/utils/common.py +++ b/diffyscan/utils/common.py @@ -1,9 +1,12 @@ import json import os import sys -from urllib.parse import urlparse - +import subprocess +import tempfile import requests +import platform + +from urllib.parse import urlparse from .logger import logger from .types import Config @@ -31,30 +34,38 @@ def load_config(path: str) -> Config: return json.load(config_file) -def handle_response(response, url): - if response.status_code == 404: - return None - - if not response.ok and response.status_code != 200: - logger.error("Request failed", url) - logger.error("Status", response.status_code) - logger.error("Response", response.text) - - return response - - -def fetch(url, headers={}): +def fetch(url, headers=None): logger.log(f"Fetch: {url}") - response = requests.get(url, headers=headers) + try: + response = requests.get(url, headers=headers) + response.raise_for_status() + except requests.exceptions.HTTPError as http_err: + raise ValueError(f"HTTP error occurred: {http_err}") + except requests.exceptions.ConnectionError as conn_err: + raise ValueError(f"Connection error occurred: {conn_err}") + except requests.exceptions.Timeout as timeout_err: + raise ValueError(f"Timeout error occurred: {timeout_err}") + except requests.exceptions.RequestException as req_err: + raise ValueError(f"An error occurred: {req_err}") - return handle_response(response, url) + return response -def pull(url, payload={}): +def pull(url, payload=None, headers=None): logger.log(f"Pull: {url}") - response = requests.post(url, data=payload) + try: + response = requests.post(url, data=payload, headers=headers) + response.raise_for_status() + except requests.exceptions.HTTPError as http_err: + raise ValueError(f"HTTP error occurred: {http_err}") + except requests.exceptions.ConnectionError as conn_err: + raise ValueError(f"Connection error occurred: {conn_err}") + except requests.exceptions.Timeout as timeout_err: + raise ValueError(f"Timeout error occurred: {timeout_err}") + except requests.exceptions.RequestException as req_err: + raise ValueError(f"An error occurred: {req_err}") - return handle_response(response, url) + return response def mask_text(text, mask_start=3, mask_end=3): @@ -75,6 +86,29 @@ def get_solc_native_platform_from_os(): if platform_name == "linux": return "linux-amd64" elif platform_name == "darwin": - return "macosx-amd64" + return "macosx-amd64" if platform.machine() == "x86_64" else "macosx-arm64" else: raise ValueError(f"Unsupported platform {platform_name}") + + +def prettify_solidity(solidity_contract_content: str): + github_file_name = os.path.join( + tempfile.gettempdir(), "9B91E897-EA51-4FCC-8DAF-FCFF135A6963.sol" + ) + with open(github_file_name, "w") as fp: + fp.write(solidity_contract_content) + prettier_return_code = subprocess.call( + [ + "npx", + "prettier", + "--plugin=prettier-plugin-solidity", + "--write", + github_file_name, + ], + stdout=subprocess.DEVNULL, + ) + if prettier_return_code != 0: + logger.error("Prettier/npx subprocess failed (see the error above)") + sys.exit() + with open(github_file_name, "r") as fp: + return fp.read() diff --git a/diffyscan/utils/constants.py b/diffyscan/utils/constants.py index 81b4ac4..524b96a 100644 --- a/diffyscan/utils/constants.py +++ b/diffyscan/utils/constants.py @@ -1,5 +1,6 @@ import time import os +import tempfile DIGEST_DIR = "digest" START_TIME = time.time() @@ -8,18 +9,44 @@ LOGS_PATH = f"{DIGEST_DIR}/{START_TIME_INT}/logs.txt" DEFAULT_CONFIG_PATH = "config.json" -REMOTE_RPC_URL = os.getenv("REMOTE_RPC_URL", "") -if not REMOTE_RPC_URL: - raise ValueError("REMOTE_RPC_URL variable is not set") - -SOLC_DIR = os.getenv("SOLC_DIR", "") -if SOLC_DIR == "": - raise ValueError("SOLC_DIR variable is not set") - -LOCAL_RPC_URL = os.getenv("LOCAL_RPC_URL", "") -if not LOCAL_RPC_URL: - raise ValueError("LOCAL_RPC_URL variable is not set") +SOLC_DIR = os.path.join(tempfile.gettempdir(), "solc_builds") GITHUB_API_TOKEN = os.getenv("GITHUB_API_TOKEN", "") if not GITHUB_API_TOKEN: raise ValueError("GITHUB_API_TOKEN variable is not set") + +# fmt: off +OPCODES = { + 0x00: 'STOP', 0x01: 'ADD', 0x02: 'MUL', 0x03: 'SUB', 0x04: 'DIV', 0x05: 'SDIV', + 0x06: 'MOD', 0x07: 'SMOD', 0x08: 'ADDMOD', 0x09: 'MULMOD', 0x0A: 'EXP', 0x0B: 'SIGNEXTEND', + 0x10: 'LT', 0x11: 'GT', 0x12: 'SLT', 0x13: 'SGT', 0x14: 'EQ', 0x15: 'ISZERO', 0x16: 'AND', + 0x17: 'OR', 0x18: 'XOR', 0x19: 'NOT', 0x1A: 'BYTE', 0x1B: 'SHL', 0x1C: 'SHR', 0x1D: 'SAR', + 0x20: 'SHA3', + 0x30: 'ADDRESS', 0x31: 'BALANCE', 0x32: 'ORIGIN', 0x33: 'CALLER', + 0x34: 'CALLVALUE', 0x35: 'CALLDATALOAD', 0x36: 'CALLDATASIZE', 0x37: 'CALLDATACOPY', + 0x38: 'CODESIZE', 0x39: 'CODECOPY', 0x3A: 'GASPRICE', 0x3B: 'EXTCODESIZE', + 0x3C: 'EXTCODECOPY', 0x3D: 'RETURNDATASIZE', 0x3E: 'RETURNDATACOPY', 0x3F: 'EXTCODEHASH', + 0x40: 'BLOCKHASH', 0x41: 'COINBASE', 0x42: 'TIMESTAMP', 0x43: 'NUMBER', + 0x44: 'PREVRANDAO', 0x45: 'GASLIMIT', 0x46: 'CHAINID', 0x47: 'SELFBALANCE', 0x48: 'BASEFEE', + 0x50: 'POP', 0x51: 'MLOAD', 0x52: 'MSTORE', 0x53: 'MSTORE8', + 0x54: 'SLOAD', 0x55: 'SSTORE', 0x56: 'JUMP', 0x57: 'JUMPI', + 0x58: 'PC', 0x59: 'MSIZE', 0x5A: 'GAS', 0x5B: 'JUMPDEST', + 0x5F: 'PUSH0', 0x60: 'PUSH1', 0x61: 'PUSH2', 0x62: 'PUSH3', 0x63: 'PUSH4', 0x64: 'PUSH5', + 0x65: 'PUSH6', 0x66: 'PUSH7', 0x67: 'PUSH8', 0x68: 'PUSH9', 0x69: 'PUSH10', 0x6A: 'PUSH11', + 0x6B: 'PUSH12', 0x6C: 'PUSH13', 0x6D: 'PUSH14', 0x6E: 'PUSH15', 0x6F: 'PUSH16', 0x70: 'PUSH17', + 0x71: 'PUSH18', 0x72: 'PUSH19', 0x73: 'PUSH20', 0x74: 'PUSH21', 0x75: 'PUSH22', 0x76: 'PUSH23', + 0x77: 'PUSH24', 0x78: 'PUSH25', 0x79: 'PUSH26', 0x7A: 'PUSH27', 0x7B: 'PUSH28', 0x7C: 'PUSH29', + 0x7D: 'PUSH30', 0x7E: 'PUSH31', 0x7F: 'PUSH32', + 0x80: 'DUP1', 0x81: 'DUP2', 0x82: 'DUP3', 0x83: 'DUP4', + 0x84: 'DUP5', 0x85: 'DUP6', 0x86: 'DUP7', 0x87: 'DUP8', + 0x88: 'DUP9', 0x89: 'DUP10', 0x8A: 'DUP11', 0x8B: 'DUP12', + 0x8C: 'DUP13', 0x8D: 'DUP14', 0x8E: 'DUP15', 0x8F: 'DUP16', + 0x90: 'SWAP1', 0x91: 'SWAP2', 0x92: 'SWAP3', 0x93: 'SWAP4', + 0x94: 'SWAP5', 0x95: 'SWAP6', 0x96: 'SWAP7', 0x97: 'SWAP8', + 0x98: 'SWAP9', 0x99: 'SWAP10', 0x9A: 'SWAP11', 0x9B: 'SWAP12', + 0x9C: 'SWAP13', 0x9D: 'SWAP14', 0x9E: 'SWAP15', 0x9F: 'SWAP16', + 0xA0: 'LOG0', 0xA1: 'LOG1', 0xA2: 'LOG2', 0xA3: 'LOG3', 0xA4: 'LOG4', + 0xF0: 'CREATE', 0xF1: 'CALL', 0xF2: 'CALLCODE', 0xF3: 'RETURN', 0xF4: 'DELEGATECALL', + 0xF5: 'CREATE2', 0xFA: 'STATICCALL', 0xFD: 'REVERT', 0xFE: 'INVALID', 0xFF: 'SELFDESTRUCT', +} +# fmt: on diff --git a/diffyscan/utils/hardhat.py b/diffyscan/utils/hardhat.py index 619b0c4..075dd28 100644 --- a/diffyscan/utils/hardhat.py +++ b/diffyscan/utils/hardhat.py @@ -4,8 +4,6 @@ from urllib.parse import urlparse from .logger import logger -from .constants import LOCAL_RPC_URL, REMOTE_RPC_URL -from .binary_verifier import get_chain_id class Hardhat: @@ -21,13 +19,18 @@ def is_port_in_use(self, parsed_url) -> bool: with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: return s.connect_ex((parsed_url.hostname, parsed_url.port)) == 0 - def start(self, hardhat_path): - parsed_url = urlparse(LOCAL_RPC_URL) - + def start(self, main_config_relative_path: str, binary_config): + parsed_url = urlparse(binary_config["local_RPC_URL"]) + hardhat_config_relative_path = Hardhat.get_config_path( + os.path.dirname(main_config_relative_path), + "hardhat_configs", + binary_config["hardhat_config_name"], + ) local_node_command = ( f"npx hardhat node --hostname {parsed_url.hostname} " f"--port {parsed_url.port} " - f"--config {hardhat_path} " + f"--config {hardhat_config_relative_path} " + f"--fork {binary_config["remote_RPC_URL"]}" ) logger.info(f'Trying to start Hardhat: "{local_node_command}"') @@ -64,5 +67,11 @@ def stop(self): os.kill(self.sub_process.pid, signal.SIGTERM) logger.info(f"Hardhat stopped, PID {self.sub_process.pid}") + def get_config_path(from_path: str, to_path: str, filename: str) -> str: + parent_directory = os.path.join(from_path, os.pardir) + new_directory = os.path.join(parent_directory, to_path) + path_to_file = os.path.join(new_directory, filename) + return os.path.normpath(path_to_file) + hardhat = Hardhat() diff --git a/diffyscan/utils/logger.py b/diffyscan/utils/logger.py index 256ef90..ca55aae 100644 --- a/diffyscan/utils/logger.py +++ b/diffyscan/utils/logger.py @@ -138,3 +138,27 @@ def divider(self): logger = Logger(LOGS_PATH) + + +def to_hex(index, padStart=2): + return f"{index:0{padStart}X}" + + +def red(text): + return f"\u001b[31m{text}\x1b[0m" + + +def bgRed(text): + return f"\u001b[37;41m{text}\x1b[0m" + + +def green(text): + return f"\u001b[32m{text}\x1b[0m" + + +def bgGreen(text): + return f"\u001b[37;42m{text}\x1b[0m" + + +def bgYellow(text): + return f"\u001b[37;43m{text}\x1b[0m" From 5c7dcc9e5fe8f0fc39319e5299989f6f9278fd15 Mon Sep 17 00:00:00 2001 From: alexvozhak Date: Tue, 6 Aug 2024 02:02:26 +0300 Subject: [PATCH 017/118] Refactored binary_verifier.py --- diffyscan/diffyscan.py | 11 +- diffyscan/utils/binary_verifier.py | 318 +++++++++-------------------- diffyscan/utils/common.py | 11 - diffyscan/utils/compiler.py | 109 ++++++++++ diffyscan/utils/explorer.py | 48 ++++- diffyscan/utils/node_handler.py | 93 +++++++++ 6 files changed, 347 insertions(+), 243 deletions(-) create mode 100644 diffyscan/utils/compiler.py create mode 100644 diffyscan/utils/node_handler.py diff --git a/diffyscan/diffyscan.py b/diffyscan/diffyscan.py index a1b1618..88d5cf0 100755 --- a/diffyscan/diffyscan.py +++ b/diffyscan/diffyscan.py @@ -6,7 +6,7 @@ from .utils.common import load_config, load_env, prettify_solidity from .utils.constants import * -from .utils.explorer import get_contract_from_explorer +from .utils.explorer import get_contract_from_explorer, get_code_from_explorer from .utils.github import ( get_file_from_github, get_file_from_github_recursive, @@ -16,6 +16,7 @@ from .utils.logger import logger from .utils.binary_verifier import * from .utils.hardhat import hardhat +from .utils.node_handler import * __version__ = "0.0.0" @@ -39,7 +40,7 @@ def run_binary_diff( if "ConstructorArgs" not in config: raise ValueError(f"Failed to find constructorArgs {address_name})") - contract_creation_code, immutables = get_contract_creation_code_from_etherscan( + contract_creation_code, immutables = get_code_from_explorer( contract_source_code, config["ConstructorArgs"], contract_address_from_config, @@ -58,7 +59,9 @@ def run_binary_diff( ) return - local_deployed_bytecode = get_bytecode(local_contract_address, local_RPC_URL) + local_deployed_bytecode = get_bytecode_from_node( + local_contract_address, local_RPC_URL + ) if local_deployed_bytecode is None: skip_or_raise( skip_deploy_error, @@ -67,7 +70,7 @@ def run_binary_diff( return remote_RPC_URL = config["binary_checking"]["remote_RPC_URL"] - remote_deployed_bytecode = get_bytecode( + remote_deployed_bytecode = get_bytecode_from_node( contract_address_from_config, remote_RPC_URL ) if remote_deployed_bytecode is None: diff --git a/diffyscan/utils/binary_verifier.py b/diffyscan/utils/binary_verifier.py index c7e2bc6..967bbea 100644 --- a/diffyscan/utils/binary_verifier.py +++ b/diffyscan/utils/binary_verifier.py @@ -1,193 +1,9 @@ -import hashlib -import subprocess -import json -import os -import stat - -from .common import fetch, pull, get_solc_native_platform_from_os -from .helpers import create_dirs from .logger import * -from .encoder import encode_constructor_arguments -from .constants import SOLC_DIR, OPCODES - -def get_compiler_info(platform, required_compiler_version): - compilers_list_url = ( - f'https://raw.githubusercontent.com/ethereum/solc-bin/gh-pages/{platform}/list.json' - ) - available_compilers_list = fetch(compilers_list_url).json() - required_build_info = next((compiler for compiler in available_compilers_list['builds'] if compiler['longVersion'] == required_compiler_version), None) - - if not required_build_info: - raise ValueError(f'Required compiler version for "{platform}" is not found') - - return required_build_info - -def download_compiler(platform, build_info, destination_path): - compiler_url = ( - f'https://binaries.soliditylang.org/{platform}/{build_info["path"]}' - ) - download_compiler_response = fetch(compiler_url) - - try: - with open(destination_path, 'wb') as compiler_file: - compiler_file.write(download_compiler_response.content) - except IOError as e: - raise ValueError(f"Error writing to file: {e}") - except Exception as e: - raise ValueError(f"An error occurred: {e}") - return download_compiler_response.content - -def check_compiler_checksum(compiler, valid_checksum): - compiler_checksum = hashlib.sha256(compiler).hexdigest() - if compiler_checksum != valid_checksum: - raise ValueError(f'Bad checksum') - -def set_compiler_executable(compiler_path): - compiler_file_rights = os.stat(compiler_path) - os.chmod(compiler_path, compiler_file_rights.st_mode | stat.S_IEXEC) - -def compile_contracts(compiler_path, input_settings): - try: - process = subprocess.run([compiler_path,'--standard-json'], input=input_settings.encode(), capture_output=True, check=True, timeout=30) - except subprocess.CalledProcessError as e: - raise ValueError(f"Error during subprocess execution: {e}") - except subprocess.TimeoutExpired as e: - raise ValueError(f"Process timed out: {e}") - except Exception as e: - raise ValueError(f"An unexpected error occurred: {e}") - return json.loads(process.stdout) - -def prepare_compiler(platform, build_info, compiler_path): - create_dirs(compiler_path) - compiler_binary = download_compiler(platform, build_info, compiler_path) - valid_checksum = build_info['sha256'][2:]; - check_compiler_checksum(compiler_binary, valid_checksum) - set_compiler_executable(compiler_path) - -def get_target_compiled_contract(compiled_contracts, target_contract_name): - contracts_to_check = [] - for contracts in compiled_contracts: - for name, contract in contracts.items(): - if name == target_contract_name: - contracts_to_check.append(contract) - - if len(contracts_to_check) != 1: - raise ValueError('multiple contracts with the same name') - - logger.okay('Contracts were successfully compiled. The target contract is ready for matching') - - return contracts_to_check[0] - -def get_contract_creation_code_from_etherscan(contract_code, constructor_args, contract_address_from_config): - platform = get_solc_native_platform_from_os() - build_name = contract_code["compiler"][1:] - build_info = get_compiler_info(platform, build_name) - 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(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'] - target_compiled_contract = get_target_compiled_contract(compiled_contracts, target_contract_name) - - contract_creation_code = f'0x{target_compiled_contract['evm']['bytecode']['object']}' - immutables = {} - 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'] - constructor_abi = None - try: - constructor_abi = [entry["inputs"] for entry in target_compiled_contract['abi'] if entry["type"] == "constructor"][0] - except IndexError: - logger.info(f'Constructor in ABI not found') - return contract_creation_code, immutables - - if contract_address_from_config not in constructor_args: - raise ValueError(f"Failed to find constructorArgs") - - constructor_calldata = None +from .constants import OPCODES - if constructor_args is not None and contract_address_from_config in constructor_args: - constructor_args = constructor_args [contract_address_from_config] - if constructor_args: - constructor_calldata = encode_constructor_arguments(constructor_abi, constructor_args) - return contract_creation_code+constructor_calldata, immutables - return contract_creation_code, immutables, False - -def get_bytecode(contract_address, rpc_url): - logger.info(f'Receiving the bytecode from "{rpc_url}" ...') - - payload = json.dumps({'id': 1, 'jsonrpc': '2.0', 'method': 'eth_getCode', 'params': [contract_address, 'latest']}) - - sources_url_response_in_json = pull(rpc_url, payload).json() - if 'result' not in sources_url_response_in_json or sources_url_response_in_json['result'] == '0x': - return None - - logger.okay(f'Bytecode was successfully received') - return sources_url_response_in_json['result'] - -def get_account(rpc_url): - logger.info(f'Receiving the account from "{rpc_url}" ...') - - payload = json.dumps({'id': 42, 'jsonrpc': '2.0', 'method': 'eth_accounts', 'params': []}) - - account_address_response = pull(rpc_url, payload).json() - - if 'result' not in account_address_response: - return None - - logger.okay(f'The account was successfully received') - - return account_address_response['result'][0] - -def deploy_contract(rpc_url, deployer, data): - logger.info(f'Trying to deploy transaction to "{rpc_url}" ...') - - payload_sendTransaction = json.dumps({ - "jsonrpc": "2.0", - "method": "eth_sendTransaction", - "params": [{ - "from": deployer, - "gas": 9000000, - "input": data - }], - "id": 1 - }) - response_sendTransaction = pull(rpc_url, payload_sendTransaction).json() - - if 'error' in response_sendTransaction: - return None, response_sendTransaction['error']['message'] - - logger.okay(f'Transaction was successfully deployed') - - tx_hash = response_sendTransaction['result'] - - payload_getTransactionReceipt = json.dumps({'id': 1, 'jsonrpc': '2.0', 'method': 'eth_getTransactionReceipt', 'params':[tx_hash]}) - response_getTransactionReceipt = pull(rpc_url, payload_getTransactionReceipt).json() - - if 'result' not in response_getTransactionReceipt or \ - 'contractAddress' not in response_getTransactionReceipt['result'] or \ - 'status' not in response_getTransactionReceipt['result'] : - return None, f'Failed to received transaction receipt' - - if response_getTransactionReceipt['result']['status'] != '0x1': - return None, f'Failed to received transaction receipt. \ - Transaction has been reverted (status 0x0). Input missmatch?' - - contract_address = response_getTransactionReceipt['result']['contractAddress'] - - return contract_address, '' - def to_match(actualBytecode, expectedBytecode, immutables, remote_contract_address): - logger.info('Comparing actual code with the expected one...') + logger.info("Comparing actual code with the expected one...") actualInstructions = parse(actualBytecode) expectedInstructions = parse(expectedBytecode) @@ -198,14 +14,16 @@ def to_match(actualBytecode, expectedBytecode, immutables, remote_contract_addre actual = actualInstructions[i] if i < len(actualInstructions) else None expected = expectedInstructions[i] if i < len(expectedInstructions) else None if not actual and not expected: - raise ValueError('Invalid instructions data') - elif (actual is not None) and (actual.get('bytecode') != expected.get('bytecode')): + raise ValueError("Invalid instructions data") + elif (actual is not None) and ( + actual.get("bytecode") != expected.get("bytecode") + ): differences.append(i) if not differences: - logger.okay(f'Bytecodes are fully matched (contract {remote_contract_address})') + logger.okay(f"Bytecodes are fully matched (contract {remote_contract_address})") return - logger.warn(f'Bytecodes have differences contract {remote_contract_address})') + logger.warn(f"Bytecodes have differences contract {remote_contract_address})") nearLinesCount = 3 checkpoints = {0, *differences} @@ -224,55 +42,101 @@ def to_match(actualBytecode, expectedBytecode, immutables, remote_contract_addre checkpointsArray = sorted(list(checkpoints)) - logger.divider() - logger.info(f'0000 00 STOP - both expected and actual bytecode instructions match') + logger.info(f"0000 00 STOP - both expected and actual bytecode instructions match") logger.info(f'{bgRed("0x0002")} - the actual bytecode differs') - logger.info(f'{bgYellow("0x0001")} - the actual bytecode differs on the immutable reference position') - logger.info(f'{bgGreen("0x0003")} - the expected bytecode value when it doesn\'t match the actual one') - logger.info(f'{red("0000 00 STOP")} - the actual bytecode instruction doesn\'t exist, but expected is present') - logger.info(f'{green("0000 00 STOP")} - the actual bytecode instruction exists when the expected doesn\'t') + logger.info( + f'{bgYellow("0x0001")} - the actual bytecode differs on the immutable reference position' + ) + logger.info( + f'{bgGreen("0x0003")} - the expected bytecode value when it doesn\'t match the actual one' + ) + logger.info( + f'{red("0000 00 STOP")} - the actual bytecode instruction doesn\'t exist, but expected is present' + ) + logger.info( + f'{green("0000 00 STOP")} - the actual bytecode instruction exists when the expected doesn\'t' + ) logger.divider() for i in range(len(checkpointsArray)): currInd = checkpointsArray[i] prevInd = checkpointsArray[i - 1] if i > 0 else None if prevInd and prevInd != currInd - 1: - print('...') + print("...") - actual = actualInstructions[currInd] if currInd < len(actualInstructions) else None - expected = expectedInstructions[currInd] if currInd < len(expectedInstructions) else None + actual = ( + actualInstructions[currInd] if currInd < len(actualInstructions) else None + ) + expected = ( + expectedInstructions[currInd] + if currInd < len(expectedInstructions) + else None + ) if not actual and expected: - params = '0x' + expected['bytecode'][2:] - print(logger.red(f'{to_hex(currInd, 4)} {to_hex(expected["op"]["code"])} {expected["op"]["name"]} {params}')) + params = "0x" + expected["bytecode"][2:] + print( + logger.red( + f'{to_hex(currInd, 4)} {to_hex(expected["op"]["code"])} {expected["op"]["name"]} {params}' + ) + ) elif actual and not expected: - params = '0x' + actual['bytecode'][2:] - print(logger.green(f'{to_hex(currInd, 4)} {to_hex(actual["op"]["code"])} {actual["op"]["name"]} {params}')) + params = "0x" + actual["bytecode"][2:] + print( + logger.green( + f'{to_hex(currInd, 4)} {to_hex(actual["op"]["code"])} {actual["op"]["name"]} {params}' + ) + ) elif actual and expected: - opcode = to_hex(actual["op"]["code"]) if actual["op"]["code"] == expected["op"]["code"] else bgRed(to_hex(actual["op"]["code"])) + ' ' + bgGreen(to_hex(expected["op"]["code"])) - opname = actual["op"]["name"] if actual["op"]["name"] == expected["op"]["name"] else bgRed(actual["op"]["name"]) + ' ' + bgGreen(expected["op"]["name"]) - actualParams = '0x' + actual['bytecode'][2:] if len(actual['bytecode']) > 2 else '' - expectedParams = '0x' + expected['bytecode'][2:] if len(expected['bytecode']) > 2 else '' - - paramsLength = len(expected['bytecode']) // 2 - 1 - isImmutable = immutables.get(expected['start'] + 1) == paramsLength - params = actualParams if actualParams == expectedParams else (bgYellow(actualParams) + ' ' + bgGreen(expectedParams) if isImmutable else bgRed(actualParams) + ' ' + bgGreen(expectedParams)) - print(f'{to_hex(currInd, 4)} {opcode} {opname} {params}') + opcode = ( + to_hex(actual["op"]["code"]) + if actual["op"]["code"] == expected["op"]["code"] + else bgRed(to_hex(actual["op"]["code"])) + + " " + + bgGreen(to_hex(expected["op"]["code"])) + ) + opname = ( + actual["op"]["name"] + if actual["op"]["name"] == expected["op"]["name"] + else bgRed(actual["op"]["name"]) + " " + bgGreen(expected["op"]["name"]) + ) + actualParams = ( + "0x" + actual["bytecode"][2:] if len(actual["bytecode"]) > 2 else "" + ) + expectedParams = ( + "0x" + expected["bytecode"][2:] if len(expected["bytecode"]) > 2 else "" + ) + + paramsLength = len(expected["bytecode"]) // 2 - 1 + isImmutable = immutables.get(expected["start"] + 1) == paramsLength + params = ( + actualParams + if actualParams == expectedParams + else ( + bgYellow(actualParams) + " " + bgGreen(expectedParams) + if isImmutable + else bgRed(actualParams) + " " + bgGreen(expectedParams) + ) + ) + print(f"{to_hex(currInd, 4)} {opcode} {opname} {params}") else: - raise ValueError('Invalid bytecode difference data') + raise ValueError("Invalid bytecode difference data") + def parse(bytecode): - buffer = bytes.fromhex(bytecode[2:] if bytecode.startswith('0x') else bytecode) - instructions = [] - i = 0 - while i < len(buffer): - opcode = buffer[i] - length = 1 + (opcode - 0x5f if 0x5f <= opcode <= 0x7f else 0) - instructions.append({ - 'start': i, - 'length': length, - 'op': {'name': OPCODES.get(opcode, 'INVALID'), 'code': opcode}, - 'bytecode': buffer[i:i+length].hex() - }) - i += length - return instructions + buffer = bytes.fromhex(bytecode[2:] if bytecode.startswith("0x") else bytecode) + instructions = [] + i = 0 + while i < len(buffer): + opcode = buffer[i] + length = 1 + (opcode - 0x5F if 0x5F <= opcode <= 0x7F else 0) + instructions.append( + { + "start": i, + "length": length, + "op": {"name": OPCODES.get(opcode, "INVALID"), "code": opcode}, + "bytecode": buffer[i : i + length].hex(), + } + ) + i += length + return instructions diff --git a/diffyscan/utils/common.py b/diffyscan/utils/common.py index 3f1f0fb..2c30532 100644 --- a/diffyscan/utils/common.py +++ b/diffyscan/utils/common.py @@ -4,7 +4,6 @@ import subprocess import tempfile import requests -import platform from urllib.parse import urlparse @@ -81,16 +80,6 @@ def parse_repo_link(repo_link): return user_slash_repo -def get_solc_native_platform_from_os(): - platform_name = sys.platform - if platform_name == "linux": - return "linux-amd64" - elif platform_name == "darwin": - return "macosx-amd64" if platform.machine() == "x86_64" else "macosx-arm64" - else: - raise ValueError(f"Unsupported platform {platform_name}") - - def prettify_solidity(solidity_contract_content: str): github_file_name = os.path.join( tempfile.gettempdir(), "9B91E897-EA51-4FCC-8DAF-FCFF135A6963.sol" diff --git a/diffyscan/utils/compiler.py b/diffyscan/utils/compiler.py new file mode 100644 index 0000000..03ae4ba --- /dev/null +++ b/diffyscan/utils/compiler.py @@ -0,0 +1,109 @@ +import platform +import hashlib +import subprocess +import json +import os +import stat +import sys + +from .common import fetch +from .helpers import create_dirs +from .logger import logger + + +def get_solc_native_platform_from_os(): + platform_name = sys.platform + if platform_name == "linux": + return "linux-amd64" + elif platform_name == "darwin": + return "macosx-amd64" if platform.machine() == "x86_64" else "macosx-arm64" + else: + raise ValueError(f"Unsupported platform {platform_name}") + + +def get_compiler_info(platform, required_compiler_version): + compilers_list_url = f"https://raw.githubusercontent.com/ethereum/solc-bin/gh-pages/{platform}/list.json" + available_compilers_list = fetch(compilers_list_url).json() + required_build_info = next( + ( + compiler + for compiler in available_compilers_list["builds"] + if compiler["longVersion"] == required_compiler_version + ), + None, + ) + + if not required_build_info: + raise ValueError(f'Required compiler version for "{platform}" is not found') + + return required_build_info + + +def download_compiler(platform, build_info, destination_path): + compiler_url = f'https://binaries.soliditylang.org/{platform}/{build_info["path"]}' + download_compiler_response = fetch(compiler_url) + + try: + with open(destination_path, "wb") as compiler_file: + compiler_file.write(download_compiler_response.content) + except IOError as e: + raise ValueError(f"Error writing to file: {e}") + except Exception as e: + raise ValueError(f"An error occurred: {e}") + return download_compiler_response.content + + +def check_compiler_checksum(compiler, valid_checksum): + compiler_checksum = hashlib.sha256(compiler).hexdigest() + if compiler_checksum != valid_checksum: + raise ValueError( + f"Compiler checksum mismatch. Expected: {valid_checksum}, Got: {compiler_checksum}" + ) + + +def set_compiler_executable(compiler_path): + compiler_file_rights = os.stat(compiler_path) + os.chmod(compiler_path, compiler_file_rights.st_mode | stat.S_IEXEC) + + +def prepare_compiler(platform, build_info, compiler_path): + create_dirs(compiler_path) + compiler_binary = download_compiler(platform, build_info, compiler_path) + valid_checksum = build_info["sha256"][2:] + check_compiler_checksum(compiler_binary, valid_checksum) + set_compiler_executable(compiler_path) + + +def compile_contracts(compiler_path, input_settings): + try: + process = subprocess.run( + [compiler_path, "--standard-json"], + input=input_settings.encode(), + capture_output=True, + check=True, + timeout=30, + ) + except subprocess.CalledProcessError as e: + raise ValueError(f"Error during subprocess execution: {e}") + except subprocess.TimeoutExpired as e: + raise ValueError(f"Process timed out: {e}") + except Exception as e: + raise ValueError(f"An unexpected error occurred: {e}") + return json.loads(process.stdout) + + +def get_target_compiled_contract(compiled_contracts, target_contract_name): + contracts_to_check = [] + for contracts in compiled_contracts: + for name, contract in contracts.items(): + if name == target_contract_name: + contracts_to_check.append(contract) + + if len(contracts_to_check) != 1: + raise ValueError("multiple contracts with the same name") + + logger.okay( + "Contracts were successfully compiled. The target contract is ready for matching" + ) + + return contracts_to_check[0] diff --git a/diffyscan/utils/explorer.py b/diffyscan/utils/explorer.py index 7435fa1..a5fa2c3 100644 --- a/diffyscan/utils/explorer.py +++ b/diffyscan/utils/explorer.py @@ -3,7 +3,9 @@ from .common import fetch from .logger import logger - +from .compiler import * +from .constants import SOLC_DIR +from .encoder import encode_constructor_arguments def _errorNoSourceCodeAndExit(address): logger.error("source code is not verified or an EOA address", address) @@ -148,3 +150,47 @@ def get_contract_from_explorer( ) return result + +def get_code_from_explorer(contract_code, constructor_args, contract_address_from_config): + platform = get_solc_native_platform_from_os() + build_name = contract_code["compiler"][1:] + build_info = get_compiler_info(platform, build_name) + 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(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'] + target_compiled_contract = get_target_compiled_contract(compiled_contracts, target_contract_name) + + contract_creation_code = f'0x{target_compiled_contract['evm']['bytecode']['object']}' + immutables = {} + 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'] + constructor_abi = None + try: + constructor_abi = [entry["inputs"] for entry in target_compiled_contract['abi'] if entry["type"] == "constructor"][0] + except IndexError: + logger.info(f'Constructor in ABI not found') + return contract_creation_code, immutables + + if contract_address_from_config not in constructor_args: + raise ValueError(f"Failed to find constructorArgs") + + constructor_calldata = None + + if constructor_args is not None and contract_address_from_config in constructor_args: + constructor_args = constructor_args [contract_address_from_config] + if constructor_args: + constructor_calldata = encode_constructor_arguments(constructor_abi, constructor_args) + return contract_creation_code+constructor_calldata, immutables + + return contract_creation_code, immutables, False diff --git a/diffyscan/utils/node_handler.py b/diffyscan/utils/node_handler.py new file mode 100644 index 0000000..02c0d4d --- /dev/null +++ b/diffyscan/utils/node_handler.py @@ -0,0 +1,93 @@ +import json + +from .common import pull +from .logger import logger + + +def get_bytecode_from_node(contract_address, rpc_url): + logger.info(f'Receiving the bytecode from "{rpc_url}" ...') + + payload = json.dumps( + { + "id": 1, + "jsonrpc": "2.0", + "method": "eth_getCode", + "params": [contract_address, "latest"], + } + ) + + sources_url_response_in_json = pull(rpc_url, payload).json() + if ( + "result" not in sources_url_response_in_json + or sources_url_response_in_json["result"] == "0x" + ): + return None + + logger.okay(f"Bytecode was successfully received") + return sources_url_response_in_json["result"] + + +def get_account(rpc_url): + logger.info(f'Receiving the account from "{rpc_url}" ...') + + payload = json.dumps( + {"id": 42, "jsonrpc": "2.0", "method": "eth_accounts", "params": []} + ) + + account_address_response = pull(rpc_url, payload).json() + + if "result" not in account_address_response: + return None + + logger.okay(f"The account was successfully received") + + return account_address_response["result"][0] + + +def deploy_contract(rpc_url, deployer, data): + logger.info(f'Trying to deploy transaction to "{rpc_url}" ...') + + payload_sendTransaction = json.dumps( + { + "jsonrpc": "2.0", + "method": "eth_sendTransaction", + "params": [{"from": deployer, "gas": 9000000, "input": data}], + "id": 1, + } + ) + response_sendTransaction = pull(rpc_url, payload_sendTransaction).json() + + if "error" in response_sendTransaction: + return None, response_sendTransaction["error"]["message"] + + logger.okay(f"Transaction was successfully deployed") + + tx_hash = response_sendTransaction["result"] + + payload_getTransactionReceipt = json.dumps( + { + "id": 1, + "jsonrpc": "2.0", + "method": "eth_getTransactionReceipt", + "params": [tx_hash], + } + ) + response_getTransactionReceipt = pull(rpc_url, payload_getTransactionReceipt).json() + + if ( + "result" not in response_getTransactionReceipt + or "contractAddress" not in response_getTransactionReceipt["result"] + or "status" not in response_getTransactionReceipt["result"] + ): + return None, f"Failed to received transaction receipt" + + if response_getTransactionReceipt["result"]["status"] != "0x1": + return ( + None, + f"Failed to received transaction receipt. \ + Transaction has been reverted (status 0x0). Input missmatch?", + ) + + contract_address = response_getTransactionReceipt["result"]["contractAddress"] + + return contract_address, "" From d33d54627532b825a63f5167e9c19a370c0b7bad Mon Sep 17 00:00:00 2001 From: alexvozhak Date: Tue, 6 Aug 2024 02:35:29 +0300 Subject: [PATCH 018/118] Trying to fix Hardhat --- package.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index 75e31d4..f69e035 100644 --- a/package.json +++ b/package.json @@ -1,8 +1,8 @@ { - "devDependencies": { - "prettier": "^3.3.0", - "prettier-plugin-solidity": "^1.3.1", + "dependencies": { "hardhat": "^2.22.7", - "kill-port": "^2.0.1" + "kill-port": "^2.0.1", + "prettier": "^3.3.0", + "prettier-plugin-solidity": "^1.3.1" } } From c5c136c065dc11c29d59def639cf0c4e0756a07f Mon Sep 17 00:00:00 2001 From: alexvozhak Date: Tue, 6 Aug 2024 02:47:59 +0300 Subject: [PATCH 019/118] Revert "Trying to fix Hardhat" This reverts commit d33d54627532b825a63f5167e9c19a370c0b7bad. --- package.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index f69e035..75e31d4 100644 --- a/package.json +++ b/package.json @@ -1,8 +1,8 @@ { - "dependencies": { - "hardhat": "^2.22.7", - "kill-port": "^2.0.1", + "devDependencies": { "prettier": "^3.3.0", - "prettier-plugin-solidity": "^1.3.1" + "prettier-plugin-solidity": "^1.3.1", + "hardhat": "^2.22.7", + "kill-port": "^2.0.1" } } From 0719e621a561f646d4233c0377eff337d442ad6f Mon Sep 17 00:00:00 2001 From: alexvozhak Date: Tue, 6 Aug 2024 14:28:05 +0300 Subject: [PATCH 020/118] Trying to fix Hardhat autoinstall #2 --- package-lock.json | 3003 ++++++++++++++++++++++++++++++++++++++++++++- package.json | 4 +- 2 files changed, 2994 insertions(+), 13 deletions(-) diff --git a/package-lock.json b/package-lock.json index 2a945af..6098460 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4,21 +4,2440 @@ "requires": true, "packages": { "": { + "dependencies": { + "hardhat": "^2.22.7", + "kill-port": "^2.0.1" + }, "devDependencies": { "prettier": "^3.3.0", "prettier-plugin-solidity": "^1.3.1" } }, - "node_modules/@solidity-parser/parser": { - "version": "0.17.0", - "resolved": "https://registry.npmjs.org/@solidity-parser/parser/-/parser-0.17.0.tgz", - "integrity": "sha512-Nko8R0/kUo391jsEHHxrGM07QFdnPGvlmox4rmH0kNiNAashItAilhy4Mv4pK5gQmW5f4sXAF58fwJbmlkGcVw==", - "dev": true + "node_modules/@ethersproject/abi": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/abi/-/abi-5.7.0.tgz", + "integrity": "sha512-351ktp42TiRcYB3H1OP8yajPeAQstMW/yCFokj/AthP9bLHzQFPlOrxOcwYEDkUAICmOHljvN4K39OMTMUa9RA==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/address": "^5.7.0", + "@ethersproject/bignumber": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/constants": "^5.7.0", + "@ethersproject/hash": "^5.7.0", + "@ethersproject/keccak256": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/properties": "^5.7.0", + "@ethersproject/strings": "^5.7.0" + } + }, + "node_modules/@ethersproject/abstract-provider": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/abstract-provider/-/abstract-provider-5.7.0.tgz", + "integrity": "sha512-R41c9UkchKCpAqStMYUpdunjo3pkEvZC3FAwZn5S5MGbXoMQOHIdHItezTETxAO5bevtMApSyEhn9+CHcDsWBw==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/bignumber": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/networks": "^5.7.0", + "@ethersproject/properties": "^5.7.0", + "@ethersproject/transactions": "^5.7.0", + "@ethersproject/web": "^5.7.0" + } + }, + "node_modules/@ethersproject/abstract-signer": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/abstract-signer/-/abstract-signer-5.7.0.tgz", + "integrity": "sha512-a16V8bq1/Cz+TGCkE2OPMTOUDLS3grCpdjoJCYNnVBbdYEMSgKrU0+B90s8b6H+ByYTBZN7a3g76jdIJi7UfKQ==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/abstract-provider": "^5.7.0", + "@ethersproject/bignumber": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/properties": "^5.7.0" + } + }, + "node_modules/@ethersproject/address": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/address/-/address-5.7.0.tgz", + "integrity": "sha512-9wYhYt7aghVGo758POM5nqcOMaE168Q6aRLJZwUmiqSrAungkG74gSSeKEIR7ukixesdRZGPgVqme6vmxs1fkA==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/bignumber": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/keccak256": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/rlp": "^5.7.0" + } + }, + "node_modules/@ethersproject/base64": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/base64/-/base64-5.7.0.tgz", + "integrity": "sha512-Dr8tcHt2mEbsZr/mwTPIQAf3Ai0Bks/7gTw9dSqk1mQvhW3XvRlmDJr/4n+wg1JmCl16NZue17CDh8xb/vZ0sQ==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/bytes": "^5.7.0" + } + }, + "node_modules/@ethersproject/bignumber": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/bignumber/-/bignumber-5.7.0.tgz", + "integrity": "sha512-n1CAdIHRWjSucQO3MC1zPSVgV/6dy/fjL9pMrPP9peL+QxEg9wOsVqwD4+818B6LUEtaXzVHQiuivzRoxPxUGw==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "bn.js": "^5.2.1" + } + }, + "node_modules/@ethersproject/bytes": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/bytes/-/bytes-5.7.0.tgz", + "integrity": "sha512-nsbxwgFXWh9NyYWo+U8atvmMsSdKJprTcICAkvbBffT75qDocbuggBU0SJiVK2MuTrp0q+xvLkTnGMPK1+uA9A==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/logger": "^5.7.0" + } + }, + "node_modules/@ethersproject/constants": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/constants/-/constants-5.7.0.tgz", + "integrity": "sha512-DHI+y5dBNvkpYUMiRQyxRBYBefZkJfo70VUkUAsRjcPs47muV9evftfZ0PJVCXYbAiCgght0DtcF9srFQmIgWA==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/bignumber": "^5.7.0" + } + }, + "node_modules/@ethersproject/hash": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/hash/-/hash-5.7.0.tgz", + "integrity": "sha512-qX5WrQfnah1EFnO5zJv1v46a8HW0+E5xuBBDTwMFZLuVTx0tbU2kkx15NqdjxecrLGatQN9FGQKpb1FKdHCt+g==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/abstract-signer": "^5.7.0", + "@ethersproject/address": "^5.7.0", + "@ethersproject/base64": "^5.7.0", + "@ethersproject/bignumber": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/keccak256": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/properties": "^5.7.0", + "@ethersproject/strings": "^5.7.0" + } + }, + "node_modules/@ethersproject/keccak256": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/keccak256/-/keccak256-5.7.0.tgz", + "integrity": "sha512-2UcPboeL/iW+pSg6vZ6ydF8tCnv3Iu/8tUmLLzWWGzxWKFFqOBQFLo6uLUv6BDrLgCDfN28RJ/wtByx+jZ4KBg==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/bytes": "^5.7.0", + "js-sha3": "0.8.0" + } + }, + "node_modules/@ethersproject/logger": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/logger/-/logger-5.7.0.tgz", + "integrity": "sha512-0odtFdXu/XHtjQXJYA3u9G0G8btm0ND5Cu8M7i5vhEcE8/HmF4Lbdqanwyv4uQTr2tx6b7fQRmgLrsnpQlmnig==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ] + }, + "node_modules/@ethersproject/networks": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/@ethersproject/networks/-/networks-5.7.1.tgz", + "integrity": "sha512-n/MufjFYv3yFcUyfhnXotyDlNdFb7onmkSy8aQERi2PjNcnWQ66xXxa3XlS8nCcA8aJKJjIIMNJTC7tu80GwpQ==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/logger": "^5.7.0" + } + }, + "node_modules/@ethersproject/properties": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/properties/-/properties-5.7.0.tgz", + "integrity": "sha512-J87jy8suntrAkIZtecpxEPxY//szqr1mlBaYlQ0r4RCaiD2hjheqF9s1LVE8vVuJCXisjIP+JgtK/Do54ej4Sw==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/logger": "^5.7.0" + } + }, + "node_modules/@ethersproject/rlp": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/rlp/-/rlp-5.7.0.tgz", + "integrity": "sha512-rBxzX2vK8mVF7b0Tol44t5Tb8gomOHkj5guL+HhzQ1yBh/ydjGnpw6at+X6Iw0Kp3OzzzkcKp8N9r0W4kYSs9w==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/logger": "^5.7.0" + } + }, + "node_modules/@ethersproject/signing-key": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/signing-key/-/signing-key-5.7.0.tgz", + "integrity": "sha512-MZdy2nL3wO0u7gkB4nA/pEf8lu1TlFswPNmy8AiYkfKTdO6eXBJyUdmHO/ehm/htHw9K/qF8ujnTyUAD+Ry54Q==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/properties": "^5.7.0", + "bn.js": "^5.2.1", + "elliptic": "6.5.4", + "hash.js": "1.1.7" + } + }, + "node_modules/@ethersproject/strings": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/strings/-/strings-5.7.0.tgz", + "integrity": "sha512-/9nu+lj0YswRNSH0NXYqrh8775XNyEdUQAuf3f+SmOrnVewcJ5SBNAjF7lpgehKi4abvNNXyf+HX86czCdJ8Mg==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/constants": "^5.7.0", + "@ethersproject/logger": "^5.7.0" + } + }, + "node_modules/@ethersproject/transactions": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/transactions/-/transactions-5.7.0.tgz", + "integrity": "sha512-kmcNicCp1lp8qanMTC3RIikGgoJ80ztTyvtsFvCYpSCfkjhD0jZ2LOrnbcuxuToLIUYYf+4XwD1rP+B/erDIhQ==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/address": "^5.7.0", + "@ethersproject/bignumber": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/constants": "^5.7.0", + "@ethersproject/keccak256": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/properties": "^5.7.0", + "@ethersproject/rlp": "^5.7.0", + "@ethersproject/signing-key": "^5.7.0" + } + }, + "node_modules/@ethersproject/web": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/@ethersproject/web/-/web-5.7.1.tgz", + "integrity": "sha512-Gueu8lSvyjBWL4cYsWsjh6MtMwM0+H4HvqFPZfB6dV8ctbP9zFAO73VG1cMWae0FLPCtz0peKPpZY8/ugJJX2w==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/base64": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/properties": "^5.7.0", + "@ethersproject/strings": "^5.7.0" + } + }, + "node_modules/@fastify/busboy": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-2.1.1.tgz", + "integrity": "sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA==", + "engines": { + "node": ">=14" + } + }, + "node_modules/@metamask/eth-sig-util": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@metamask/eth-sig-util/-/eth-sig-util-4.0.1.tgz", + "integrity": "sha512-tghyZKLHZjcdlDqCA3gNZmLeR0XvOE9U1qoQO9ohyAZT6Pya+H9vkBPcsyXytmYLNgVoin7CKCmweo/R43V+tQ==", + "dependencies": { + "ethereumjs-abi": "^0.6.8", + "ethereumjs-util": "^6.2.1", + "ethjs-util": "^0.1.6", + "tweetnacl": "^1.0.3", + "tweetnacl-util": "^0.15.1" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/@noble/hashes": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.2.0.tgz", + "integrity": "sha512-FZfhjEDbT5GRswV3C6uvLPHMiVD6lQBmpoX5+eSiPaMTXte/IKqI5dykDxzZB/WBeK/CDuQRBWarPdi3FNY2zQ==", + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ] + }, + "node_modules/@noble/secp256k1": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/@noble/secp256k1/-/secp256k1-1.7.1.tgz", + "integrity": "sha512-hOUk6AyBFmqVrv7k5WAw/LpszxVbj9gGN4JRkIX52fdFAj1UA61KXmZDvqVEm+pOyec3+fIeZB02LYa/pWOArw==", + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ] + }, + "node_modules/@nomicfoundation/edr": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/@nomicfoundation/edr/-/edr-0.5.2.tgz", + "integrity": "sha512-hW/iLvUQZNTVjFyX/I40rtKvvDOqUEyIi96T28YaLfmPL+3LW2lxmYLUXEJ6MI14HzqxDqrLyhf6IbjAa2r3Dw==", + "dependencies": { + "@nomicfoundation/edr-darwin-arm64": "0.5.2", + "@nomicfoundation/edr-darwin-x64": "0.5.2", + "@nomicfoundation/edr-linux-arm64-gnu": "0.5.2", + "@nomicfoundation/edr-linux-arm64-musl": "0.5.2", + "@nomicfoundation/edr-linux-x64-gnu": "0.5.2", + "@nomicfoundation/edr-linux-x64-musl": "0.5.2", + "@nomicfoundation/edr-win32-x64-msvc": "0.5.2" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@nomicfoundation/edr-darwin-arm64": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-darwin-arm64/-/edr-darwin-arm64-0.5.2.tgz", + "integrity": "sha512-Gm4wOPKhbDjGTIRyFA2QUAPfCXA1AHxYOKt3yLSGJkQkdy9a5WW+qtqKeEKHc/+4wpJSLtsGQfpzyIzggFfo/A==", + "engines": { + "node": ">= 18" + } + }, + "node_modules/@nomicfoundation/edr-darwin-x64": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-darwin-x64/-/edr-darwin-x64-0.5.2.tgz", + "integrity": "sha512-ClyABq2dFCsrYEED3/UIO0c7p4H1/4vvlswFlqUyBpOkJccr75qIYvahOSJRM62WgUFRhbSS0OJXFRwc/PwmVg==", + "engines": { + "node": ">= 18" + } + }, + "node_modules/@nomicfoundation/edr-linux-arm64-gnu": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-linux-arm64-gnu/-/edr-linux-arm64-gnu-0.5.2.tgz", + "integrity": "sha512-HWMTVk1iOabfvU2RvrKLDgtFjJZTC42CpHiw2h6rfpsgRqMahvIlx2jdjWYzFNy1jZKPTN1AStQ/91MRrg5KnA==", + "engines": { + "node": ">= 18" + } + }, + "node_modules/@nomicfoundation/edr-linux-arm64-musl": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-linux-arm64-musl/-/edr-linux-arm64-musl-0.5.2.tgz", + "integrity": "sha512-CwsQ10xFx/QAD5y3/g5alm9+jFVuhc7uYMhrZAu9UVF+KtVjeCvafj0PaVsZ8qyijjqVuVsJ8hD1x5ob7SMcGg==", + "engines": { + "node": ">= 18" + } + }, + "node_modules/@nomicfoundation/edr-linux-x64-gnu": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-linux-x64-gnu/-/edr-linux-x64-gnu-0.5.2.tgz", + "integrity": "sha512-CWVCEdhWJ3fmUpzWHCRnC0/VLBDbqtqTGTR6yyY1Ep3S3BOrHEAvt7h5gx85r2vLcztisu2vlDq51auie4IU1A==", + "engines": { + "node": ">= 18" + } + }, + "node_modules/@nomicfoundation/edr-linux-x64-musl": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-linux-x64-musl/-/edr-linux-x64-musl-0.5.2.tgz", + "integrity": "sha512-+aJDfwhkddy2pP5u1ISg3IZVAm0dO836tRlDTFWtvvSMQ5hRGqPcWwlsbobhDQsIxhPJyT7phL0orCg5W3WMeA==", + "engines": { + "node": ">= 18" + } + }, + "node_modules/@nomicfoundation/edr-win32-x64-msvc": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-win32-x64-msvc/-/edr-win32-x64-msvc-0.5.2.tgz", + "integrity": "sha512-CcvvuA3sAv7liFNPsIR/68YlH6rrybKzYttLlMr80d4GKJjwJ5OKb3YgE6FdZZnOfP19HEHhsLcE0DPLtY3r0w==", + "engines": { + "node": ">= 18" + } + }, + "node_modules/@nomicfoundation/ethereumjs-common": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@nomicfoundation/ethereumjs-common/-/ethereumjs-common-4.0.4.tgz", + "integrity": "sha512-9Rgb658lcWsjiicr5GzNCjI1llow/7r0k50dLL95OJ+6iZJcVbi15r3Y0xh2cIO+zgX0WIHcbzIu6FeQf9KPrg==", + "dependencies": { + "@nomicfoundation/ethereumjs-util": "9.0.4" + } + }, + "node_modules/@nomicfoundation/ethereumjs-rlp": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/@nomicfoundation/ethereumjs-rlp/-/ethereumjs-rlp-5.0.4.tgz", + "integrity": "sha512-8H1S3s8F6QueOc/X92SdrA4RDenpiAEqMg5vJH99kcQaCy/a3Q6fgseo75mgWlbanGJXSlAPtnCeG9jvfTYXlw==", + "bin": { + "rlp": "bin/rlp.cjs" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@nomicfoundation/ethereumjs-tx": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/@nomicfoundation/ethereumjs-tx/-/ethereumjs-tx-5.0.4.tgz", + "integrity": "sha512-Xjv8wAKJGMrP1f0n2PeyfFCCojHd7iS3s/Ab7qzF1S64kxZ8Z22LCMynArYsVqiFx6rzYy548HNVEyI+AYN/kw==", + "dependencies": { + "@nomicfoundation/ethereumjs-common": "4.0.4", + "@nomicfoundation/ethereumjs-rlp": "5.0.4", + "@nomicfoundation/ethereumjs-util": "9.0.4", + "ethereum-cryptography": "0.1.3" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "c-kzg": "^2.1.2" + }, + "peerDependenciesMeta": { + "c-kzg": { + "optional": true + } + } + }, + "node_modules/@nomicfoundation/ethereumjs-tx/node_modules/ethereum-cryptography": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-0.1.3.tgz", + "integrity": "sha512-w8/4x1SGGzc+tO97TASLja6SLd3fRIK2tLVcV2Gx4IB21hE19atll5Cq9o3d0ZmAYC/8aw0ipieTSiekAea4SQ==", + "dependencies": { + "@types/pbkdf2": "^3.0.0", + "@types/secp256k1": "^4.0.1", + "blakejs": "^1.1.0", + "browserify-aes": "^1.2.0", + "bs58check": "^2.1.2", + "create-hash": "^1.2.0", + "create-hmac": "^1.1.7", + "hash.js": "^1.1.7", + "keccak": "^3.0.0", + "pbkdf2": "^3.0.17", + "randombytes": "^2.1.0", + "safe-buffer": "^5.1.2", + "scrypt-js": "^3.0.0", + "secp256k1": "^4.0.1", + "setimmediate": "^1.0.5" + } + }, + "node_modules/@nomicfoundation/ethereumjs-util": { + "version": "9.0.4", + "resolved": "https://registry.npmjs.org/@nomicfoundation/ethereumjs-util/-/ethereumjs-util-9.0.4.tgz", + "integrity": "sha512-sLOzjnSrlx9Bb9EFNtHzK/FJFsfg2re6bsGqinFinH1gCqVfz9YYlXiMWwDM4C/L4ywuHFCYwfKTVr/QHQcU0Q==", + "dependencies": { + "@nomicfoundation/ethereumjs-rlp": "5.0.4", + "ethereum-cryptography": "0.1.3" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "c-kzg": "^2.1.2" + }, + "peerDependenciesMeta": { + "c-kzg": { + "optional": true + } + } + }, + "node_modules/@nomicfoundation/ethereumjs-util/node_modules/ethereum-cryptography": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-0.1.3.tgz", + "integrity": "sha512-w8/4x1SGGzc+tO97TASLja6SLd3fRIK2tLVcV2Gx4IB21hE19atll5Cq9o3d0ZmAYC/8aw0ipieTSiekAea4SQ==", + "dependencies": { + "@types/pbkdf2": "^3.0.0", + "@types/secp256k1": "^4.0.1", + "blakejs": "^1.1.0", + "browserify-aes": "^1.2.0", + "bs58check": "^2.1.2", + "create-hash": "^1.2.0", + "create-hmac": "^1.1.7", + "hash.js": "^1.1.7", + "keccak": "^3.0.0", + "pbkdf2": "^3.0.17", + "randombytes": "^2.1.0", + "safe-buffer": "^5.1.2", + "scrypt-js": "^3.0.0", + "secp256k1": "^4.0.1", + "setimmediate": "^1.0.5" + } + }, + "node_modules/@nomicfoundation/solidity-analyzer": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@nomicfoundation/solidity-analyzer/-/solidity-analyzer-0.1.2.tgz", + "integrity": "sha512-q4n32/FNKIhQ3zQGGw5CvPF6GTvDCpYwIf7bEY/dZTZbgfDsHyjJwURxUJf3VQuuJj+fDIFl4+KkBVbw4Ef6jA==", + "engines": { + "node": ">= 12" + }, + "optionalDependencies": { + "@nomicfoundation/solidity-analyzer-darwin-arm64": "0.1.2", + "@nomicfoundation/solidity-analyzer-darwin-x64": "0.1.2", + "@nomicfoundation/solidity-analyzer-linux-arm64-gnu": "0.1.2", + "@nomicfoundation/solidity-analyzer-linux-arm64-musl": "0.1.2", + "@nomicfoundation/solidity-analyzer-linux-x64-gnu": "0.1.2", + "@nomicfoundation/solidity-analyzer-linux-x64-musl": "0.1.2", + "@nomicfoundation/solidity-analyzer-win32-x64-msvc": "0.1.2" + } + }, + "node_modules/@nomicfoundation/solidity-analyzer-darwin-arm64": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@nomicfoundation/solidity-analyzer-darwin-arm64/-/solidity-analyzer-darwin-arm64-0.1.2.tgz", + "integrity": "sha512-JaqcWPDZENCvm++lFFGjrDd8mxtf+CtLd2MiXvMNTBD33dContTZ9TWETwNFwg7JTJT5Q9HEecH7FA+HTSsIUw==", + "optional": true, + "engines": { + "node": ">= 12" + } + }, + "node_modules/@nomicfoundation/solidity-analyzer-darwin-x64": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@nomicfoundation/solidity-analyzer-darwin-x64/-/solidity-analyzer-darwin-x64-0.1.2.tgz", + "integrity": "sha512-fZNmVztrSXC03e9RONBT+CiksSeYcxI1wlzqyr0L7hsQlK1fzV+f04g2JtQ1c/Fe74ZwdV6aQBdd6Uwl1052sw==", + "optional": true, + "engines": { + "node": ">= 12" + } + }, + "node_modules/@nomicfoundation/solidity-analyzer-linux-arm64-gnu": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@nomicfoundation/solidity-analyzer-linux-arm64-gnu/-/solidity-analyzer-linux-arm64-gnu-0.1.2.tgz", + "integrity": "sha512-3d54oc+9ZVBuB6nbp8wHylk4xh0N0Gc+bk+/uJae+rUgbOBwQSfuGIbAZt1wBXs5REkSmynEGcqx6DutoK0tPA==", + "optional": true, + "engines": { + "node": ">= 12" + } + }, + "node_modules/@nomicfoundation/solidity-analyzer-linux-arm64-musl": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@nomicfoundation/solidity-analyzer-linux-arm64-musl/-/solidity-analyzer-linux-arm64-musl-0.1.2.tgz", + "integrity": "sha512-iDJfR2qf55vgsg7BtJa7iPiFAsYf2d0Tv/0B+vhtnI16+wfQeTbP7teookbGvAo0eJo7aLLm0xfS/GTkvHIucA==", + "optional": true, + "engines": { + "node": ">= 12" + } + }, + "node_modules/@nomicfoundation/solidity-analyzer-linux-x64-gnu": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@nomicfoundation/solidity-analyzer-linux-x64-gnu/-/solidity-analyzer-linux-x64-gnu-0.1.2.tgz", + "integrity": "sha512-9dlHMAt5/2cpWyuJ9fQNOUXFB/vgSFORg1jpjX1Mh9hJ/MfZXlDdHQ+DpFCs32Zk5pxRBb07yGvSHk9/fezL+g==", + "optional": true, + "engines": { + "node": ">= 12" + } + }, + "node_modules/@nomicfoundation/solidity-analyzer-linux-x64-musl": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@nomicfoundation/solidity-analyzer-linux-x64-musl/-/solidity-analyzer-linux-x64-musl-0.1.2.tgz", + "integrity": "sha512-GzzVeeJob3lfrSlDKQw2bRJ8rBf6mEYaWY+gW0JnTDHINA0s2gPR4km5RLIj1xeZZOYz4zRw+AEeYgLRqB2NXg==", + "optional": true, + "engines": { + "node": ">= 12" + } + }, + "node_modules/@nomicfoundation/solidity-analyzer-win32-x64-msvc": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@nomicfoundation/solidity-analyzer-win32-x64-msvc/-/solidity-analyzer-win32-x64-msvc-0.1.2.tgz", + "integrity": "sha512-Fdjli4DCcFHb4Zgsz0uEJXZ2K7VEO+w5KVv7HmT7WO10iODdU9csC2az4jrhEsRtiR9Gfd74FlG0NYlw1BMdyA==", + "optional": true, + "engines": { + "node": ">= 12" + } + }, + "node_modules/@scure/base": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.1.7.tgz", + "integrity": "sha512-PPNYBslrLNNUQ/Yad37MHYsNQtK67EhWb6WtSvNLLPo7SdVZgkUjD6Dg+5On7zNwmskf8OX7I7Nx5oN+MIWE0g==", + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@scure/bip32": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.1.5.tgz", + "integrity": "sha512-XyNh1rB0SkEqd3tXcXMi+Xe1fvg+kUIcoRIEujP1Jgv7DqW2r9lg3Ah0NkFaCs9sTkQAQA8kw7xiRXzENi9Rtw==", + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], + "dependencies": { + "@noble/hashes": "~1.2.0", + "@noble/secp256k1": "~1.7.0", + "@scure/base": "~1.1.0" + } + }, + "node_modules/@scure/bip39": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.1.1.tgz", + "integrity": "sha512-t+wDck2rVkh65Hmv280fYdVdY25J9YeEUIgn2LG1WM6gxFkGzcksoDiUkWVpVp3Oex9xGC68JU2dSbUfwZ2jPg==", + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], + "dependencies": { + "@noble/hashes": "~1.2.0", + "@scure/base": "~1.1.0" + } + }, + "node_modules/@sentry/core": { + "version": "5.30.0", + "resolved": "https://registry.npmjs.org/@sentry/core/-/core-5.30.0.tgz", + "integrity": "sha512-TmfrII8w1PQZSZgPpUESqjB+jC6MvZJZdLtE/0hZ+SrnKhW3x5WlYLvTXZpcWePYBku7rl2wn1RZu6uT0qCTeg==", + "dependencies": { + "@sentry/hub": "5.30.0", + "@sentry/minimal": "5.30.0", + "@sentry/types": "5.30.0", + "@sentry/utils": "5.30.0", + "tslib": "^1.9.3" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@sentry/hub": { + "version": "5.30.0", + "resolved": "https://registry.npmjs.org/@sentry/hub/-/hub-5.30.0.tgz", + "integrity": "sha512-2tYrGnzb1gKz2EkMDQcfLrDTvmGcQPuWxLnJKXJvYTQDGLlEvi2tWz1VIHjunmOvJrB5aIQLhm+dcMRwFZDCqQ==", + "dependencies": { + "@sentry/types": "5.30.0", + "@sentry/utils": "5.30.0", + "tslib": "^1.9.3" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@sentry/minimal": { + "version": "5.30.0", + "resolved": "https://registry.npmjs.org/@sentry/minimal/-/minimal-5.30.0.tgz", + "integrity": "sha512-BwWb/owZKtkDX+Sc4zCSTNcvZUq7YcH3uAVlmh/gtR9rmUvbzAA3ewLuB3myi4wWRAMEtny6+J/FN/x+2wn9Xw==", + "dependencies": { + "@sentry/hub": "5.30.0", + "@sentry/types": "5.30.0", + "tslib": "^1.9.3" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@sentry/node": { + "version": "5.30.0", + "resolved": "https://registry.npmjs.org/@sentry/node/-/node-5.30.0.tgz", + "integrity": "sha512-Br5oyVBF0fZo6ZS9bxbJZG4ApAjRqAnqFFurMVJJdunNb80brh7a5Qva2kjhm+U6r9NJAB5OmDyPkA1Qnt+QVg==", + "dependencies": { + "@sentry/core": "5.30.0", + "@sentry/hub": "5.30.0", + "@sentry/tracing": "5.30.0", + "@sentry/types": "5.30.0", + "@sentry/utils": "5.30.0", + "cookie": "^0.4.1", + "https-proxy-agent": "^5.0.0", + "lru_map": "^0.3.3", + "tslib": "^1.9.3" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@sentry/tracing": { + "version": "5.30.0", + "resolved": "https://registry.npmjs.org/@sentry/tracing/-/tracing-5.30.0.tgz", + "integrity": "sha512-dUFowCr0AIMwiLD7Fs314Mdzcug+gBVo/+NCMyDw8tFxJkwWAKl7Qa2OZxLQ0ZHjakcj1hNKfCQJ9rhyfOl4Aw==", + "dependencies": { + "@sentry/hub": "5.30.0", + "@sentry/minimal": "5.30.0", + "@sentry/types": "5.30.0", + "@sentry/utils": "5.30.0", + "tslib": "^1.9.3" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@sentry/types": { + "version": "5.30.0", + "resolved": "https://registry.npmjs.org/@sentry/types/-/types-5.30.0.tgz", + "integrity": "sha512-R8xOqlSTZ+htqrfteCWU5Nk0CDN5ApUTvrlvBuiH1DyP6czDZ4ktbZB0hAgBlVcK0U+qpD3ag3Tqqpa5Q67rPw==", + "engines": { + "node": ">=6" + } + }, + "node_modules/@sentry/utils": { + "version": "5.30.0", + "resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-5.30.0.tgz", + "integrity": "sha512-zaYmoH0NWWtvnJjC9/CBseXMtKHm/tm40sz3YfJRxeQjyzRqNQPgivpd9R/oDJCYj999mzdW382p/qi2ypjLww==", + "dependencies": { + "@sentry/types": "5.30.0", + "tslib": "^1.9.3" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@solidity-parser/parser": { + "version": "0.17.0", + "resolved": "https://registry.npmjs.org/@solidity-parser/parser/-/parser-0.17.0.tgz", + "integrity": "sha512-Nko8R0/kUo391jsEHHxrGM07QFdnPGvlmox4rmH0kNiNAashItAilhy4Mv4pK5gQmW5f4sXAF58fwJbmlkGcVw==", + "dev": true + }, + "node_modules/@types/bn.js": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-5.1.5.tgz", + "integrity": "sha512-V46N0zwKRF5Q00AZ6hWtN0T8gGmDUaUzLWQvHFo5yThtVwK/VCenFY3wXVbOvNfajEpsTfQM4IN9k/d6gUVX3A==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/@types/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-ssE3Vlrys7sdIzs5LOxCzTVMsU7i9oa/IaW92wF32JFb3CVczqOkru2xspuKczHEbG3nvmPY7IFqVmGGHdNbYw==" + }, + "node_modules/@types/node": { + "version": "22.1.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.1.0.tgz", + "integrity": "sha512-AOmuRF0R2/5j1knA3c6G3HOk523Ga+l+ZXltX8SF1+5oqcXijjfTd8fY3XRZqSihEu9XhtQnKYLmkFaoxgsJHw==", + "dependencies": { + "undici-types": "~6.13.0" + } + }, + "node_modules/@types/pbkdf2": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@types/pbkdf2/-/pbkdf2-3.1.2.tgz", + "integrity": "sha512-uRwJqmiXmh9++aSu1VNEn3iIxWOhd8AHXNSdlaLfdAAdSTY9jYVeGWnzejM3dvrkbqE3/hyQkQQ29IFATEGlew==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/secp256k1": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/@types/secp256k1/-/secp256k1-4.0.6.tgz", + "integrity": "sha512-hHxJU6PAEUn0TP4S/ZOzuTUvJWuZ6eIKeNKb5RBpODvSl6hp1Wrw4s7ATY50rklRCScUDpHzVA/DQdSjJ3UoYQ==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/adm-zip": { + "version": "0.4.16", + "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.4.16.tgz", + "integrity": "sha512-TFi4HBKSGfIKsK5YCkKaaFG2m4PEDyViZmEwof3MTIgzimHLto6muaHVpbrljdIvIrFZzEq/p4nafOeLcYegrg==", + "engines": { + "node": ">=0.3.0" + } + }, + "node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/aggregate-error": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", + "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", + "dependencies": { + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-align": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.1.tgz", + "integrity": "sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==", + "dependencies": { + "string-width": "^4.1.0" + } + }, + "node_modules/ansi-colors": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", + "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", + "engines": { + "node": ">=6" + } + }, + "node_modules/ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "dependencies": { + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" + }, + "node_modules/base-x": { + "version": "3.0.10", + "resolved": "https://registry.npmjs.org/base-x/-/base-x-3.0.10.tgz", + "integrity": "sha512-7d0s06rR9rYaIWHkpfLIFICM/tkSVdoPC9qYAQRpxn9DdKNWNsKC0uk++akckyLq16Tx2WIinnZ6WRriAt6njQ==", + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/blakejs": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/blakejs/-/blakejs-1.2.1.tgz", + "integrity": "sha512-QXUSXI3QVc/gJME0dBpXrag1kbzOqCjCX8/b54ntNyW6sjtoqxqRk3LTmXzaJoh71zMsDCjM+47jS7XiwN/+fQ==" + }, + "node_modules/bn.js": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.1.tgz", + "integrity": "sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==" + }, + "node_modules/boxen": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/boxen/-/boxen-5.1.2.tgz", + "integrity": "sha512-9gYgQKXx+1nP8mP7CzFyaUARhg7D3n1dF/FnErWmu9l6JvGpNUN278h0aSb+QjoiKSWG+iZ3uHrcqk0qrY9RQQ==", + "dependencies": { + "ansi-align": "^3.0.0", + "camelcase": "^6.2.0", + "chalk": "^4.1.0", + "cli-boxes": "^2.2.1", + "string-width": "^4.2.2", + "type-fest": "^0.20.2", + "widest-line": "^3.1.0", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/boxen/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/boxen/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/boxen/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/boxen/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "node_modules/boxen/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/boxen/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/boxen/node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/brorand": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", + "integrity": "sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w==" + }, + "node_modules/browser-stdout": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", + "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==" + }, + "node_modules/browserify-aes": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", + "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==", + "dependencies": { + "buffer-xor": "^1.0.3", + "cipher-base": "^1.0.0", + "create-hash": "^1.1.0", + "evp_bytestokey": "^1.0.3", + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/bs58": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/bs58/-/bs58-4.0.1.tgz", + "integrity": "sha512-Ok3Wdf5vOIlBrgCvTq96gBkJw+JUEzdBgyaza5HLtPm7yTHkjRy8+JzNyHF7BHa0bNWOQIp3m5YF0nnFcOIKLw==", + "dependencies": { + "base-x": "^3.0.2" + } + }, + "node_modules/bs58check": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/bs58check/-/bs58check-2.1.2.tgz", + "integrity": "sha512-0TS1jicxdU09dwJMNZtVAfzPi6Q6QeN0pM1Fkzrjn+XYHvzMKPU3pHVpva+769iNVSfIYWf7LJ6WR+BuuMf8cA==", + "dependencies": { + "bs58": "^4.0.0", + "create-hash": "^1.1.0", + "safe-buffer": "^5.1.2" + } + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==" + }, + "node_modules/buffer-xor": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", + "integrity": "sha512-571s0T7nZWK6vB67HI5dyUF7wXiNcfaPPPTl6zYCNApANjIvYJTg7hlud/+cJpdAhS7dVzqMLmfhfHR3rAcOjQ==" + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/ci-info": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", + "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==" + }, + "node_modules/cipher-base": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", + "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==", + "dependencies": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/clean-stack": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", + "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", + "engines": { + "node": ">=6" + } + }, + "node_modules/cli-boxes": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-2.2.1.tgz", + "integrity": "sha512-y4coMcylgSCdVinjiDBuR8PCC2bLjyGTwEmPb9NHR/QaNU6EUOXcTY/s6VjGMD6ENSEaeQYHCY0GNGS5jfMwPw==", + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" + }, + "node_modules/command-exists": { + "version": "1.2.9", + "resolved": "https://registry.npmjs.org/command-exists/-/command-exists-1.2.9.tgz", + "integrity": "sha512-LTQ/SGc+s0Xc0Fu5WaKnR0YiygZkm9eKFvyS+fRsU7/ZWFF8ykFM6Pc9aCVf1+xasOOZpO3BAVgVrKvsqKHV7w==" + }, + "node_modules/commander": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", + "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==", + "engines": { + "node": ">= 12" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" + }, + "node_modules/cookie": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz", + "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/create-hash": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", + "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", + "dependencies": { + "cipher-base": "^1.0.1", + "inherits": "^2.0.1", + "md5.js": "^1.3.4", + "ripemd160": "^2.0.1", + "sha.js": "^2.4.0" + } + }, + "node_modules/create-hmac": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", + "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", + "dependencies": { + "cipher-base": "^1.0.3", + "create-hash": "^1.1.0", + "inherits": "^2.0.1", + "ripemd160": "^2.0.0", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + } + }, + "node_modules/debug": { + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.6.tgz", + "integrity": "sha512-O/09Bd4Z1fBrU4VzkhFqVgpPzaGbw6Sm9FEkBT1A/YBXQFGuuSxa1dN2nxgxS34JmKXqYx8CZAwEVoJFImUXIg==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decamelize": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", + "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/diff": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.2.0.tgz", + "integrity": "sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/elliptic": { + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz", + "integrity": "sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==", + "dependencies": { + "bn.js": "^4.11.9", + "brorand": "^1.1.0", + "hash.js": "^1.0.0", + "hmac-drbg": "^1.0.1", + "inherits": "^2.0.4", + "minimalistic-assert": "^1.0.1", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "node_modules/elliptic/node_modules/bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + }, + "node_modules/enquirer": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.4.1.tgz", + "integrity": "sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ==", + "dependencies": { + "ansi-colors": "^4.1.1", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/env-paths": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", + "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", + "engines": { + "node": ">=6" + } + }, + "node_modules/escalade": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz", + "integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/ethereum-cryptography": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-1.2.0.tgz", + "integrity": "sha512-6yFQC9b5ug6/17CQpCyE3k9eKBMdhyVjzUy1WkiuY/E4vj/SXDBbCw8QEIaXqf0Mf2SnY6RmpDcwlUmBSS0EJw==", + "dependencies": { + "@noble/hashes": "1.2.0", + "@noble/secp256k1": "1.7.1", + "@scure/bip32": "1.1.5", + "@scure/bip39": "1.1.1" + } + }, + "node_modules/ethereumjs-abi": { + "version": "0.6.8", + "resolved": "https://registry.npmjs.org/ethereumjs-abi/-/ethereumjs-abi-0.6.8.tgz", + "integrity": "sha512-Tx0r/iXI6r+lRsdvkFDlut0N08jWMnKRZ6Gkq+Nmw75lZe4e6o3EkSnkaBP5NF6+m5PTGAr9JP43N3LyeoglsA==", + "dependencies": { + "bn.js": "^4.11.8", + "ethereumjs-util": "^6.0.0" + } + }, + "node_modules/ethereumjs-abi/node_modules/bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" + }, + "node_modules/ethereumjs-util": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-6.2.1.tgz", + "integrity": "sha512-W2Ktez4L01Vexijrm5EB6w7dg4n/TgpoYU4avuT5T3Vmnw/eCRtiBrJfQYS/DCSvDIOLn2k57GcHdeBcgVxAqw==", + "dependencies": { + "@types/bn.js": "^4.11.3", + "bn.js": "^4.11.0", + "create-hash": "^1.1.2", + "elliptic": "^6.5.2", + "ethereum-cryptography": "^0.1.3", + "ethjs-util": "0.1.6", + "rlp": "^2.2.3" + } + }, + "node_modules/ethereumjs-util/node_modules/@types/bn.js": { + "version": "4.11.6", + "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-4.11.6.tgz", + "integrity": "sha512-pqr857jrp2kPuO9uRjZ3PwnJTjoQy+fcdxvBTvHm6dkmEL9q+hDD/2j/0ELOBPtPnS8LjCX0gI9nbl8lVkadpg==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/ethereumjs-util/node_modules/bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" + }, + "node_modules/ethereumjs-util/node_modules/ethereum-cryptography": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-0.1.3.tgz", + "integrity": "sha512-w8/4x1SGGzc+tO97TASLja6SLd3fRIK2tLVcV2Gx4IB21hE19atll5Cq9o3d0ZmAYC/8aw0ipieTSiekAea4SQ==", + "dependencies": { + "@types/pbkdf2": "^3.0.0", + "@types/secp256k1": "^4.0.1", + "blakejs": "^1.1.0", + "browserify-aes": "^1.2.0", + "bs58check": "^2.1.2", + "create-hash": "^1.2.0", + "create-hmac": "^1.1.7", + "hash.js": "^1.1.7", + "keccak": "^3.0.0", + "pbkdf2": "^3.0.17", + "randombytes": "^2.1.0", + "safe-buffer": "^5.1.2", + "scrypt-js": "^3.0.0", + "secp256k1": "^4.0.1", + "setimmediate": "^1.0.5" + } + }, + "node_modules/ethjs-util": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/ethjs-util/-/ethjs-util-0.1.6.tgz", + "integrity": "sha512-CUnVOQq7gSpDHZVVrQW8ExxUETWrnrvXYvYz55wOU8Uj4VCgw56XC2B/fVqQN+f7gmrnRHSLVnFAwsCuNwji8w==", + "dependencies": { + "is-hex-prefixed": "1.0.0", + "strip-hex-prefix": "1.0.0" + }, + "engines": { + "node": ">=6.5.0", + "npm": ">=3" + } + }, + "node_modules/evp_bytestokey": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", + "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==", + "dependencies": { + "md5.js": "^1.3.4", + "safe-buffer": "^5.1.1" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha512-NWzkk0jSJtTt08+FBFMvXoeZnOJD+jTtsRmBYbAIzJdX6l7dLgR7CTubCM5/eDdPUBvLCeVasP1brfVR/9/EZQ==", + "dependencies": { + "locate-path": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/flat": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", + "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", + "bin": { + "flat": "cli.js" + } + }, + "node_modules/follow-redirects": { + "version": "1.15.6", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz", + "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/fp-ts": { + "version": "1.19.3", + "resolved": "https://registry.npmjs.org/fp-ts/-/fp-ts-1.19.3.tgz", + "integrity": "sha512-H5KQDspykdHuztLTg+ajGN0Z2qUjcEf3Ybxc6hLt0k7/zPkn29XnKnxlBPyW2XIddWrGaJBzBl4VLYOtk39yZg==" + }, + "node_modules/fs-extra": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz", + "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==", + "dependencies": { + "graceful-fs": "^4.1.2", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + }, + "engines": { + "node": ">=6 <7 || >=8" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-them-args": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/get-them-args/-/get-them-args-1.3.2.tgz", + "integrity": "sha512-LRn8Jlk+DwZE4GTlDbT3Hikd1wSHgLMme/+7ddlqKd7ldwR6LjJgTVWzBnR01wnYGe4KgrXjg287RaI22UHmAw==" + }, + "node_modules/glob": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==" + }, + "node_modules/hardhat": { + "version": "2.22.7", + "resolved": "https://registry.npmjs.org/hardhat/-/hardhat-2.22.7.tgz", + "integrity": "sha512-nrXQAl+qUr75TsCLDo8P41YXLc+5U7qQMMCIrbbmy1/uQaVPncdjDrD5BR0CENvHRj7EBqO+JkofpozXoIfJKg==", + "dependencies": { + "@ethersproject/abi": "^5.1.2", + "@metamask/eth-sig-util": "^4.0.0", + "@nomicfoundation/edr": "^0.5.0", + "@nomicfoundation/ethereumjs-common": "4.0.4", + "@nomicfoundation/ethereumjs-tx": "5.0.4", + "@nomicfoundation/ethereumjs-util": "9.0.4", + "@nomicfoundation/solidity-analyzer": "^0.1.0", + "@sentry/node": "^5.18.1", + "@types/bn.js": "^5.1.0", + "@types/lru-cache": "^5.1.0", + "adm-zip": "^0.4.16", + "aggregate-error": "^3.0.0", + "ansi-escapes": "^4.3.0", + "boxen": "^5.1.2", + "chalk": "^2.4.2", + "chokidar": "^3.4.0", + "ci-info": "^2.0.0", + "debug": "^4.1.1", + "enquirer": "^2.3.0", + "env-paths": "^2.2.0", + "ethereum-cryptography": "^1.0.3", + "ethereumjs-abi": "^0.6.8", + "find-up": "^2.1.0", + "fp-ts": "1.19.3", + "fs-extra": "^7.0.1", + "glob": "7.2.0", + "immutable": "^4.0.0-rc.12", + "io-ts": "1.10.4", + "keccak": "^3.0.2", + "lodash": "^4.17.11", + "mnemonist": "^0.38.0", + "mocha": "^10.0.0", + "p-map": "^4.0.0", + "raw-body": "^2.4.1", + "resolve": "1.17.0", + "semver": "^6.3.0", + "solc": "0.8.26", + "source-map-support": "^0.5.13", + "stacktrace-parser": "^0.1.10", + "tsort": "0.0.1", + "undici": "^5.14.0", + "uuid": "^8.3.2", + "ws": "^7.4.6" + }, + "bin": { + "hardhat": "internal/cli/bootstrap.js" + }, + "peerDependencies": { + "ts-node": "*", + "typescript": "*" + }, + "peerDependenciesMeta": { + "ts-node": { + "optional": true + }, + "typescript": { + "optional": true + } + } + }, + "node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "engines": { + "node": ">=4" + } + }, + "node_modules/hash-base": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.1.0.tgz", + "integrity": "sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA==", + "dependencies": { + "inherits": "^2.0.4", + "readable-stream": "^3.6.0", + "safe-buffer": "^5.2.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/hash.js": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", + "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", + "dependencies": { + "inherits": "^2.0.3", + "minimalistic-assert": "^1.0.1" + } + }, + "node_modules/he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "bin": { + "he": "bin/he" + } + }, + "node_modules/hmac-drbg": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", + "integrity": "sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg==", + "dependencies": { + "hash.js": "^1.0.3", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "node_modules/http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "dependencies": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/immutable": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.7.tgz", + "integrity": "sha512-1hqclzwYwjRDFLjcFxOM5AYkkG0rpFPpr1RLPMEuGczoS7YA8gLhy8SWXYRAA/XwfEHpfo3cw5JGioS32fnMRw==" + }, + "node_modules/indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "engines": { + "node": ">=8" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "node_modules/io-ts": { + "version": "1.10.4", + "resolved": "https://registry.npmjs.org/io-ts/-/io-ts-1.10.4.tgz", + "integrity": "sha512-b23PteSnYXSONJ6JQXRAlvJhuw8KOtkqa87W4wDtvMrud/DTJd5X+NpOOI+O/zZwVq6v0VLAaJ+1EDViKEuN9g==", + "dependencies": { + "fp-ts": "^1.0.0" + } + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-hex-prefixed": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-hex-prefixed/-/is-hex-prefixed-1.0.0.tgz", + "integrity": "sha512-WvtOiug1VFrE9v1Cydwm+FnXd3+w9GaeVUss5W4v/SLy3UW00vP+6iNF2SdnfiBoLy4bTqVdkftNGTUeOFVsbA==", + "engines": { + "node": ">=6.5.0", + "npm": ">=3" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-plain-obj": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", + "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/js-sha3": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.8.0.tgz", + "integrity": "sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q==" + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/keccak": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/keccak/-/keccak-3.0.4.tgz", + "integrity": "sha512-3vKuW0jV8J3XNTzvfyicFR5qvxrSAGl7KIhvgOu5cmWwM7tZRj3fMbj/pfIf4be7aznbc+prBWGjywox/g2Y6Q==", + "hasInstallScript": true, + "dependencies": { + "node-addon-api": "^2.0.0", + "node-gyp-build": "^4.2.0", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/kill-port": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/kill-port/-/kill-port-2.0.1.tgz", + "integrity": "sha512-e0SVOV5jFo0mx8r7bS29maVWp17qGqLBZ5ricNSajON6//kmb7qqqNnml4twNE8Dtj97UQD+gNFOaipS/q1zzQ==", + "dependencies": { + "get-them-args": "1.3.2", + "shell-exec": "1.0.2" + }, + "bin": { + "kill-port": "cli.js" + } + }, + "node_modules/locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha512-NCI2kiDkyR7VeEKm27Kda/iQHyKJe1Bu0FlTbYp3CqJu+9IFe9bLyAjMxf5ZDDbEg+iMPzB5zYyUTSm8wVTKmA==", + "dependencies": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + }, + "node_modules/log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "dependencies": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-symbols/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/log-symbols/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/log-symbols/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/log-symbols/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "node_modules/log-symbols/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/log-symbols/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/lru_map": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/lru_map/-/lru_map-0.3.3.tgz", + "integrity": "sha512-Pn9cox5CsMYngeDbmChANltQl+5pi6XmTrraMSzhPmMBbmgcxmqWry0U3PGapCU1yB4/LqCcom7qhHZiF/jGfQ==" + }, + "node_modules/md5.js": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", + "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==", + "dependencies": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "node_modules/memorystream": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/memorystream/-/memorystream-0.3.1.tgz", + "integrity": "sha512-S3UwM3yj5mtUSEfP41UZmt/0SCoVYUcU1rkXv+BQ5Ig8ndL4sPoJNBUJERafdPb5jjHJGuMgytgKvKIf58XNBw==", + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==" + }, + "node_modules/minimalistic-crypto-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", + "integrity": "sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg==" + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/mnemonist": { + "version": "0.38.5", + "resolved": "https://registry.npmjs.org/mnemonist/-/mnemonist-0.38.5.tgz", + "integrity": "sha512-bZTFT5rrPKtPJxj8KSV0WkPyNxl72vQepqqVUAW2ARUpUSF2qXMB6jZj7hW5/k7C1rtpzqbD/IIbJwLXUjCHeg==", + "dependencies": { + "obliterator": "^2.0.0" + } + }, + "node_modules/mocha": { + "version": "10.7.0", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.7.0.tgz", + "integrity": "sha512-v8/rBWr2VO5YkspYINnvu81inSz2y3ODJrhO175/Exzor1RcEZZkizgE2A+w/CAXXoESS8Kys5E62dOHGHzULA==", + "dependencies": { + "ansi-colors": "^4.1.3", + "browser-stdout": "^1.3.1", + "chokidar": "^3.5.3", + "debug": "^4.3.5", + "diff": "^5.2.0", + "escape-string-regexp": "^4.0.0", + "find-up": "^5.0.0", + "glob": "^8.1.0", + "he": "^1.2.0", + "js-yaml": "^4.1.0", + "log-symbols": "^4.1.0", + "minimatch": "^5.1.6", + "ms": "^2.1.3", + "serialize-javascript": "^6.0.2", + "strip-json-comments": "^3.1.1", + "supports-color": "^8.1.1", + "workerpool": "^6.5.1", + "yargs": "^16.2.0", + "yargs-parser": "^20.2.9", + "yargs-unparser": "^2.0.0" + }, + "bin": { + "_mocha": "bin/_mocha", + "mocha": "bin/mocha.js" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/mocha/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/mocha/node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mocha/node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mocha/node_modules/glob": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", + "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^5.0.1", + "once": "^1.3.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/mocha/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/mocha/node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mocha/node_modules/minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/mocha/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + }, + "node_modules/mocha/node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mocha/node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mocha/node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "engines": { + "node": ">=8" + } + }, + "node_modules/mocha/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "node_modules/node-addon-api": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-2.0.2.tgz", + "integrity": "sha512-Ntyt4AIXyaLIuMHF6IOoTakB3K+RWxwtsHNRxllEoA6vPwP9o4866g6YWDLUdnucilZhmkxiHwHr11gAENw+QA==" + }, + "node_modules/node-gyp-build": { + "version": "4.8.1", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.1.tgz", + "integrity": "sha512-OSs33Z9yWr148JZcbZd5WiAXhh/n9z8TxQcdMhIOlpN9AhWpLfvVFO73+m77bBABQMaY9XSvIa+qk0jlI7Gcaw==", + "bin": { + "node-gyp-build": "bin.js", + "node-gyp-build-optional": "optional.js", + "node-gyp-build-test": "build-test.js" + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/obliterator": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/obliterator/-/obliterator-2.0.4.tgz", + "integrity": "sha512-lgHwxlxV1qIg1Eap7LgIeoBWIMFibOjbrYPIPJZcI1mmGAI2m3lNYpK12Y+GBdPQ0U1hRwSord7GIaawz962qQ==" + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "dependencies": { + "p-try": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha512-nQja7m7gSKuewoVRen45CtVfODR3crN3goVQ0DDZ9N3yHxgpkuBhZqsaiotSQRrADUrne346peY7kT3TSACykg==", + "dependencies": { + "p-limit": "^1.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/p-map": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", + "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", + "dependencies": { + "aggregate-error": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-try": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha512-U1etNYuMJoIz3ZXSrrySFjsXQTWOx2/jdi86L+2pRvph/qMKL6sbcCYdH23fqsbm8TH2Gn0OybpT4eSFlCVHww==", + "engines": { + "node": ">=4" + } + }, + "node_modules/path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", + "engines": { + "node": ">=4" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" + }, + "node_modules/pbkdf2": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.2.tgz", + "integrity": "sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA==", + "dependencies": { + "create-hash": "^1.1.2", + "create-hmac": "^1.1.4", + "ripemd160": "^2.0.1", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + }, + "engines": { + "node": ">=0.12" + } + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } }, "node_modules/prettier": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.3.0.tgz", - "integrity": "sha512-J9odKxERhCQ10OC2yb93583f6UnYutOeiV5i0zEDS7UGTdUt0u+y8erxl3lBKvwo/JHyyoEdXjwp4dke9oyZ/g==", + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.3.3.tgz", + "integrity": "sha512-i2tDNA0O5IrMO757lfrdQZCc2jPNDVntV0m/+4whiDfWaTKfMNgR7Qz0NAeGz/nRqF4m5/6CLzbP4/liHt12Ew==", "dev": true, "bin": { "prettier": "bin/prettier.cjs" @@ -47,10 +2466,10 @@ "prettier": ">=2.3.0" } }, - "node_modules/semver": { - "version": "7.6.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz", - "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==", + "node_modules/prettier-plugin-solidity/node_modules/semver": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", "dev": true, "bin": { "semver": "bin/semver.js" @@ -59,11 +2478,571 @@ "node": ">=10" } }, + "node_modules/randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dependencies": { + "safe-buffer": "^5.1.0" + } + }, + "node_modules/raw-body": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", + "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", + "dependencies": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve": { + "version": "1.17.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz", + "integrity": "sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==", + "dependencies": { + "path-parse": "^1.0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/ripemd160": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", + "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", + "dependencies": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1" + } + }, + "node_modules/rlp": { + "version": "2.2.7", + "resolved": "https://registry.npmjs.org/rlp/-/rlp-2.2.7.tgz", + "integrity": "sha512-d5gdPmgQ0Z+AklL2NVXr/IoSjNZFfTVvQWzL/AM2AOcSzYP2xjlb0AC8YyCLc41MSNf6P6QVtjgPdmVtzb+4lQ==", + "dependencies": { + "bn.js": "^5.2.0" + }, + "bin": { + "rlp": "bin/rlp" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "node_modules/scrypt-js": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/scrypt-js/-/scrypt-js-3.0.1.tgz", + "integrity": "sha512-cdwTTnqPu0Hyvf5in5asVdZocVDTNRmR7XEcJuIzMjJeSHybHl7vpB66AzwTaIg6CLSbtjcxc8fqcySfnTkccA==" + }, + "node_modules/secp256k1": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/secp256k1/-/secp256k1-4.0.3.tgz", + "integrity": "sha512-NLZVf+ROMxwtEj3Xa562qgv2BK5e2WNmXPiOdVIPLgs6lyTzMvBq0aWTYMI5XCP9jZMVKOcqZLw/Wc4vDkuxhA==", + "hasInstallScript": true, + "dependencies": { + "elliptic": "^6.5.4", + "node-addon-api": "^2.0.0", + "node-gyp-build": "^4.2.0" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/serialize-javascript": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", + "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", + "dependencies": { + "randombytes": "^2.1.0" + } + }, + "node_modules/setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==" + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" + }, + "node_modules/sha.js": { + "version": "2.4.11", + "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", + "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", + "dependencies": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + }, + "bin": { + "sha.js": "bin.js" + } + }, + "node_modules/shell-exec": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/shell-exec/-/shell-exec-1.0.2.tgz", + "integrity": "sha512-jyVd+kU2X+mWKMmGhx4fpWbPsjvD53k9ivqetutVW/BQ+WIZoDoP4d8vUMGezV6saZsiNoW2f9GIhg9Dondohg==" + }, + "node_modules/solc": { + "version": "0.8.26", + "resolved": "https://registry.npmjs.org/solc/-/solc-0.8.26.tgz", + "integrity": "sha512-yiPQNVf5rBFHwN6SIf3TUUvVAFKcQqmSUFeq+fb6pNRCo0ZCgpYOZDi3BVoezCPIAcKrVYd/qXlBLUP9wVrZ9g==", + "dependencies": { + "command-exists": "^1.2.8", + "commander": "^8.1.0", + "follow-redirects": "^1.12.1", + "js-sha3": "0.8.0", + "memorystream": "^0.3.1", + "semver": "^5.5.0", + "tmp": "0.0.33" + }, + "bin": { + "solcjs": "solc.js" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/solc/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "bin": { + "semver": "bin/semver" + } + }, "node_modules/solidity-comments-extractor": { "version": "0.0.8", "resolved": "https://registry.npmjs.org/solidity-comments-extractor/-/solidity-comments-extractor-0.0.8.tgz", "integrity": "sha512-htM7Vn6LhHreR+EglVMd2s+sZhcXAirB1Zlyrv5zBuTxieCvjfnRpd7iZk75m/u6NOlEyQ94C6TWbBn2cY7w8g==", "dev": true + }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/stacktrace-parser": { + "version": "0.1.10", + "resolved": "https://registry.npmjs.org/stacktrace-parser/-/stacktrace-parser-0.1.10.tgz", + "integrity": "sha512-KJP1OCML99+8fhOHxwwzyWrlUuVX5GQ0ZpJTd1DFXhdkrvg1szxfHhawXUZ3g9TkXORQd4/WG68jMlQZ2p8wlg==", + "dependencies": { + "type-fest": "^0.7.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/stacktrace-parser/node_modules/type-fest": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.7.1.tgz", + "integrity": "sha512-Ne2YiiGN8bmrmJJEuTWTLJR32nh/JdL1+PSicowtNb0WFpn59GK8/lfD61bVtzguz7b3PBt74nxpv/Pw5po5Rg==", + "engines": { + "node": ">=8" + } + }, + "node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-hex-prefix": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-hex-prefix/-/strip-hex-prefix-1.0.0.tgz", + "integrity": "sha512-q8d4ue7JGEiVcypji1bALTos+0pWtyGlivAWyPuTkHzuTCJqrK9sWxYQZUq6Nq3cuyv3bm734IhHvHtGGURU6A==", + "dependencies": { + "is-hex-prefixed": "1.0.0" + }, + "engines": { + "node": ">=6.5.0", + "npm": ">=3" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/tmp": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "dependencies": { + "os-tmpdir": "~1.0.2" + }, + "engines": { + "node": ">=0.6.0" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + }, + "node_modules/tsort": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/tsort/-/tsort-0.0.1.tgz", + "integrity": "sha512-Tyrf5mxF8Ofs1tNoxA13lFeZ2Zrbd6cKbuH3V+MQ5sb6DtBj5FjrXVsRWT8YvNAQTqNoz66dz1WsbigI22aEnw==" + }, + "node_modules/tweetnacl": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.3.tgz", + "integrity": "sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==" + }, + "node_modules/tweetnacl-util": { + "version": "0.15.1", + "resolved": "https://registry.npmjs.org/tweetnacl-util/-/tweetnacl-util-0.15.1.tgz", + "integrity": "sha512-RKJBIj8lySrShN4w6i/BonWp2Z/uxwC3h4y7xsRrpP59ZboCd0GpEVsOnMDYLMmKBpYhb5TgHzZXy7wTfYFBRw==" + }, + "node_modules/type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/undici": { + "version": "5.28.4", + "resolved": "https://registry.npmjs.org/undici/-/undici-5.28.4.tgz", + "integrity": "sha512-72RFADWFqKmUb2hmmvNODKL3p9hcB6Gt2DOQMis1SEBaV6a4MH8soBvzg+95CYhCKPFedut2JY9bMfrDl9D23g==", + "dependencies": { + "@fastify/busboy": "^2.0.0" + }, + "engines": { + "node": ">=14.0" + } + }, + "node_modules/undici-types": { + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.13.0.tgz", + "integrity": "sha512-xtFJHudx8S2DSoujjMd1WeWvn7KKWFRESZTMeL1RptAYERu29D6jphMjjY+vn96jvN3kVPDNxU/E13VTaXj6jg==" + }, + "node_modules/universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" + }, + "node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/widest-line": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-3.1.0.tgz", + "integrity": "sha512-NsmoXalsWVDMGupxZ5R08ka9flZjjiLvHVAWYOKtiKM8ujtZWr9cRffak+uSE48+Ob8ObalXpwyeUiyDD6QFgg==", + "dependencies": { + "string-width": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/workerpool": { + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.5.1.tgz", + "integrity": "sha512-Fs4dNYcsdpYSAfVxhnl1L5zTksjvOJxtC5hzMNl+1t9B8hTJTdKDyZ5ju7ztgPy+ft9tBFXoOlDNiOT9WUXZlA==" + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/wrap-ansi/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" + }, + "node_modules/ws": { + "version": "7.5.10", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz", + "integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==", + "engines": { + "node": ">=8.3.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs-parser": { + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs-unparser": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", + "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", + "dependencies": { + "camelcase": "^6.0.0", + "decamelize": "^4.0.0", + "flat": "^5.0.2", + "is-plain-obj": "^2.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } } } } diff --git a/package.json b/package.json index 75e31d4..5e09e53 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,9 @@ { "devDependencies": { "prettier": "^3.3.0", - "prettier-plugin-solidity": "^1.3.1", + "prettier-plugin-solidity": "^1.3.1" + }, + "dependencies": { "hardhat": "^2.22.7", "kill-port": "^2.0.1" } From 7d47afe0b1899e4116217d75225e89c52907fac2 Mon Sep 17 00:00:00 2001 From: alexvozhak Date: Tue, 6 Aug 2024 14:56:52 +0300 Subject: [PATCH 021/118] Updated readme --- README.md | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 560c848..3306271 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ Diff your Ethereum smart contracts code from GitHub against Blockchain explorer verified source code, deployed bytecode from local node (Hardhat) against remote Supports reformatting solidity code by means of prettifier solidity plugin before comparing the sources (option `--prettify`). -Supports binary matching deployed bytecode from local node (Hardhat) against remote (see section 'binary_checking' in lido_dao_sepolia_config.json as example) +Supports binary checking deployed bytecode from local node (Hardhat) against remote (see section 'binary_checking' in lido_dao_sepolia_config.json as example) ## Install @@ -16,7 +16,7 @@ Supports binary matching deployed bytecode from local node (Hardhat) against rem pipx install git+https://github.com/lidofinance/diffyscan ``` -If need `--prettify` options +If need `--prettify` or binary checking options support ```shell npm install @@ -35,12 +35,11 @@ Set your Github token to query API without strict rate limiting, ```bash export GITHUB_API_TOKEN= ``` -Set local and remote RPC URLs for matching bytecode from local node againts remote Start script with one of the examples provided (or entire folder of configs) ```bash -diffyscan config_samples/lido_dao_sepolia_config.json hardhat_configs/sepolia_hardhat.config.js +diffyscan config_samples/lido_dao_sepolia_config.json ``` Alternatively, create a new config file named `config.json`, @@ -92,7 +91,7 @@ This project was developed using these dependencies with their exact versions li - Python 3.12 - Poetry 1.8 -- if need `--prettify` option support: +- if need `--prettify` or binary checking options support - npm Other versions may work as well but were not tested at all. @@ -130,7 +129,7 @@ poetry shell poetry install ``` -5. If need `--prettify` option +5. If need `--prettify` or binary checking options support ```shell npm install From 300eb778b125caadadda792adcb51b0fbdb895f8 Mon Sep 17 00:00:00 2001 From: alexvozhak Date: Tue, 6 Aug 2024 15:01:26 +0300 Subject: [PATCH 022/118] Updated RPC URL for sepolia --- config_samples/lido_dao_sepolia_config.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config_samples/lido_dao_sepolia_config.json b/config_samples/lido_dao_sepolia_config.json index 271c62f..7ffeb6e 100644 --- a/config_samples/lido_dao_sepolia_config.json +++ b/config_samples/lido_dao_sepolia_config.json @@ -46,7 +46,7 @@ "skip_deploy_error" : 1, "hardhat_config_name": "sepolia_hardhat.config.js", "local_RPC_URL": "http://127.0.0.1:7545", - "remote_RPC_URL":"https://ethereum-sepolia-rpc.publicnode.com" + "remote_RPC_URL":"https://sepolia.drpc.org" }, "github_repo": { "url": "https://github.com/lidofinance/lido-dao", From ee579a2aac8c192dbac5bd3ef1550c96b7ac732f Mon Sep 17 00:00:00 2001 From: alexvozhak Date: Tue, 6 Aug 2024 16:32:18 +0300 Subject: [PATCH 023/118] Moved 'constructor_args' into 'binary_checking' section. Also small fixes --- config_samples/lido_dao_sepolia_config.json | 118 ++++++++++---------- diffyscan/diffyscan.py | 21 ++-- diffyscan/utils/explorer.py | 2 +- 3 files changed, 74 insertions(+), 67 deletions(-) diff --git a/config_samples/lido_dao_sepolia_config.json b/config_samples/lido_dao_sepolia_config.json index 7ffeb6e..b12e2e1 100644 --- a/config_samples/lido_dao_sepolia_config.json +++ b/config_samples/lido_dao_sepolia_config.json @@ -41,13 +41,6 @@ }, "explorer_token_env_var": "ETHERSCAN_TOKEN", "explorer_hostname": "api-sepolia.etherscan.io", - "binary_checking": { - "use": 1, - "skip_deploy_error" : 1, - "hardhat_config_name": "sepolia_hardhat.config.js", - "local_RPC_URL": "http://127.0.0.1:7545", - "remote_RPC_URL":"https://sepolia.drpc.org" - }, "github_repo": { "url": "https://github.com/lidofinance/lido-dao", "commit": "cadffa46a2b8ed6cfa1127fca2468bae1a82d6bf", @@ -105,57 +98,64 @@ "relative_root": "contracts" } }, - "ConstructorArgs": { - "0x8f6254332f69557A72b0DA2D5F0Bc07d4CA991E7" : ["0xf30a674935479cc6f2254ba65d7534eab8bd6ba2","0x6885E36BFcb68CB383DfE90023a462C03BCB2AE5",""], - "0x9726CA9AEFF4BC8FB8C084BdAbdB71608248E3f8" : ["0x3e3FE7dBc6B4C189E7128855dD526361c49b40Af"], - "0x604dc1776eEbe7ddCf4cf5429226Ad20a5a294eE" : [ - ["0xd497be005638efcf09f6bfc8dafbbb0bb72cd991", - "0x6885e36bfcb68cb383dfe90023a462c03bcb2ae5", - "0x94b1b8e2680882f8652882e7f196169de3d9a3b2", - "0x3483c140ef7f2716460198ff831a8e53f05f1606", - "0x3e3fe7dbc6b4c189e7128855dd526361c49b40af", - "0xbac2a471443f18ac5c31078b96c5797a78fcc680", - "0x3483c140ef7f2716460198ff831a8e53f05f1606", - "0x61bb0ef69262d5ef1cc2873cf61766751d99b699", - "0x4f36aaeb18ab56a4e380241bea6ebf215b9cb12c", - "0x32a0e5828b62aab932362a4816ae03b860b65e83", - "0x7637d44c9f2e9ca584a8b5d2ea493012a5cdaeb6", - "0x1583c7b3f4c3b008720e6bce5726336b0ab25fdd", - "0xde7318afa67ead6d6bbc8224dfce5ed6e4b86d76", - "0x7bc76076b0f3879b4a750450c0ccf02c6ca11220"] - ], - "0xB82381A3fBD3FaFA77B3a7bE693342618240067b" : ["0x3e3FE7dBc6B4C189E7128855dD526361c49b40Af"], - "0x46cF57508B0565decC0419B833C2dAFa50B132e0" : ["0x80b5DC88C98E528bF9cb4B7F0f076aC41da24651"], - "0x94B1B8e2680882f8652882e7F196169dE3d9a3B2" : ["0x3e3FE7dBc6B4C189E7128855dD526361c49b40Af", "0x32A0E5828B62AAb932362a4816ae03b860b65e83"], - "0x0220A1cF6C3a548BE75aEabCdA509CaB08CDe063" : [ "0xB82381A3fBD3FaFA77B3a7bE693342618240067b", "Lido: stETH Withdrawal NFT", "unstETH"], - "0xee386d787Db24AbEe4dcc591F35405E323b70Dad" : ["0x3e3FE7dBc6B4C189E7128855dD526361c49b40Af","0x32A0E5828B62AAb932362a4816ae03b860b65e83"], - "0x61Bb0Ef69262d5EF1cc2873cf61766751D99B699" : ["0x6885E36BFcb68CB383DfE90023a462C03BCB2AE5", "0x32A0E5828B62AAb932362a4816ae03b860b65e83", "0x3e3FE7dBc6B4C189E7128855dD526361c49b40Af", 0, 0], - "0x082d16150BF75BB8F2197eEC1d293DbA96c93638" : ["0x8f6254332f69557A72b0DA2D5F0Bc07d4CA991E7","0x3e3FE7dBc6B4C189E7128855dD526361c49b40Af", "0x3483c140EF7F2716460198Ff831a8e53F05F1606", 12, 1655733600], - "0x1c2807B207f140a1DE0b39E5546eDEf67Af2568c" : [1], - "0xd06dF83b8ad6D89C86a187fba4Eae918d497BdCB" : ["0x9D381f44d1fbdf8190fA0EcdC028e2Af24DdD3FE","0x0000000000000000000000000000000000000000",0, "TEST Lido DAO Token", 18, "TLDO", 1], - "0x758D8c3CE794b3Dfe3b3A3482B7eD33de2109D95" : [32, 12, 1655733600, 12, 10, "0x6885E36BFcb68CB383DfE90023a462C03BCB2AE5", "0xd497Be005638efCf09F6BFC8DAFBBB0BB72cD991"], - "0xC40801b88C835a58e54eEE6679D301ba31a4C72b" : [12, 1655733600, "0x8f6254332f69557A72b0DA2D5F0Bc07d4CA991E7"], - "0xbac2A471443F18aC5C31078b96C5797A78fCc680" : [ - "0x28FAB2059C713A7F9D8c86Db49f9bb0e96Af1ef8", - "0x22896Bfc68814BFD855b1a167255eE497006e730", - [1500,500,1000,250,2000,100,100,128,5000000], - [[],[],[],[],[],[],[],[],[],[]] - ], - "0x7bC76076b0f3879b4A750450C0Ccf02c6Ca11220" : ["0x6885E36BFcb68CB383DfE90023a462C03BCB2AE5",""], - "0x6155bD199ECcc79Ff4e8B392f6cBD9c9874E8916" : ["0x1c2807B207f140a1DE0b39E5546eDEf67Af2568c"], - "0xC73cd4B2A7c1CBC5BF046eB4A7019365558ABF66" : ["0x6155bD199ECcc79Ff4e8B392f6cBD9c9874E8916", "0xcd567bdf93dd0f6acc3bc7f2155f83244d56a65abbfbefb763e015420102c67b", ""], - "0x52AD3004Bc993d63931142Dd4f3DD647414048a1" : ["0x6155bD199ECcc79Ff4e8B392f6cBD9c9874E8916","0x5c9918c99c4081ca9459c178381be71d9da40e49e151687da55099c49a4237f1", ""], - "0x098a952BD200005382aEb3229e38ae39A7616F56" : [32,12,1655733600, 75, 10, "0x7FAcEF1c7248ed171FDd9ea3B25B4550b38e6133", "0x7637d44c9f2e9cA584a8B5D2EA493012A5cdaEB6"], - "0x1583C7b3f4C3B008720E6BcE5726336b0aB25fdd" : ["0x0220A1cF6C3a548BE75aEabCdA509CaB08CDe063", "0x32A0E5828B62AAb932362a4816ae03b860b65e83","0x0"], - "0x32A0E5828B62AAb932362a4816ae03b860b65e83" : ["0x6155bD199ECcc79Ff4e8B392f6cBD9c9874E8916","0x701a4fd1f5174d12a0f1d9ad2c88d0ad11ab6aad0ac72b7d9ce621815f8016a9","0"], - "0x33d6E15047E8644F8DDf5CD05d202dfE587DA6E3" : ["0x6155bD199ECcc79Ff4e8B392f6cBD9c9874E8916","0x7071f283424072341f856ac9e947e7ec0eb68719f757a7e785979b6b8717579d","0"], - "0x3483c140EF7F2716460198Ff831a8e53F05F1606" : ["0x6155bD199ECcc79Ff4e8B392f6cBD9c9874E8916","0x8b47ba2a8454ec799cd91646e7ec47168e91fd139b23f017455f3e5898aaba93", "0"], - "0x39A0EbdEE54cB319f4F42141daaBDb6ba25D341A" : ["0x6155bD199ECcc79Ff4e8B392f6cBD9c9874E8916","0x0abcd104777321a82b010357f20887d61247493d89d2e987ff57bcecbde00e1e", "0"], - "0x3e3FE7dBc6B4C189E7128855dD526361c49b40Af" : ["0x6155bD199ECcc79Ff4e8B392f6cBD9c9874E8916","0x3ca7c3e38968823ccb4c78ea688df41356f182ae1d159e4ee608d30d68cef320", "0"], - "0x4F36aAEb18Ab56A4e380241bea6ebF215b9cb12c" : ["0x46cF57508B0565decC0419B833C2dAFa50B132e0", "0x32A0E5828B62AAb932362a4816ae03b860b65e83", "0"], - "0x7637d44c9f2e9cA584a8B5D2EA493012A5cdaEB6" : ["0xC40801b88C835a58e54eEE6679D301ba31a4C72b", "0x32A0E5828B62AAb932362a4816ae03b860b65e83", "0"], - "0x8A1AA86d35b2EE8C9369618E7D7b40000cCD3295" : ["0x6155bD199ECcc79Ff4e8B392f6cBD9c9874E8916", "0xe3262375f45a6e2026b7e7b18c2b807434f2508fe1a2a3dfb493c7df8f4aad6a", "0"], - "0xd497Be005638efCf09F6BFC8DAFBBB0BB72cD991" : ["0x082d16150BF75BB8F2197eEC1d293DbA96c93638", "0x32A0E5828B62AAb932362a4816ae03b860b65e83", "0"] - } + "binary_checking": { + "use": 1, + "skip_deploy_error" : 1, + "hardhat_config_name": "sepolia_hardhat.config.js", + "local_RPC_URL": "http://127.0.0.1:7545", + "remote_RPC_URL":"https://sepolia.drpc.org", + "constructor_args": { + "0x8f6254332f69557A72b0DA2D5F0Bc07d4CA991E7" : ["0xf30a674935479cc6f2254ba65d7534eab8bd6ba2","0x6885E36BFcb68CB383DfE90023a462C03BCB2AE5",""], + "0x9726CA9AEFF4BC8FB8C084BdAbdB71608248E3f8" : ["0x3e3FE7dBc6B4C189E7128855dD526361c49b40Af"], + "0x604dc1776eEbe7ddCf4cf5429226Ad20a5a294eE" : [ + ["0xd497be005638efcf09f6bfc8dafbbb0bb72cd991", + "0x6885e36bfcb68cb383dfe90023a462c03bcb2ae5", + "0x94b1b8e2680882f8652882e7f196169de3d9a3b2", + "0x3483c140ef7f2716460198ff831a8e53f05f1606", + "0x3e3fe7dbc6b4c189e7128855dd526361c49b40af", + "0xbac2a471443f18ac5c31078b96c5797a78fcc680", + "0x3483c140ef7f2716460198ff831a8e53f05f1606", + "0x61bb0ef69262d5ef1cc2873cf61766751d99b699", + "0x4f36aaeb18ab56a4e380241bea6ebf215b9cb12c", + "0x32a0e5828b62aab932362a4816ae03b860b65e83", + "0x7637d44c9f2e9ca584a8b5d2ea493012a5cdaeb6", + "0x1583c7b3f4c3b008720e6bce5726336b0ab25fdd", + "0xde7318afa67ead6d6bbc8224dfce5ed6e4b86d76", + "0x7bc76076b0f3879b4a750450c0ccf02c6ca11220"] + ], + "0xB82381A3fBD3FaFA77B3a7bE693342618240067b" : ["0x3e3FE7dBc6B4C189E7128855dD526361c49b40Af"], + "0x46cF57508B0565decC0419B833C2dAFa50B132e0" : ["0x80b5DC88C98E528bF9cb4B7F0f076aC41da24651"], + "0x94B1B8e2680882f8652882e7F196169dE3d9a3B2" : ["0x3e3FE7dBc6B4C189E7128855dD526361c49b40Af", "0x32A0E5828B62AAb932362a4816ae03b860b65e83"], + "0x0220A1cF6C3a548BE75aEabCdA509CaB08CDe063" : [ "0xB82381A3fBD3FaFA77B3a7bE693342618240067b", "Lido: stETH Withdrawal NFT", "unstETH"], + "0xee386d787Db24AbEe4dcc591F35405E323b70Dad" : ["0x3e3FE7dBc6B4C189E7128855dD526361c49b40Af","0x32A0E5828B62AAb932362a4816ae03b860b65e83"], + "0x61Bb0Ef69262d5EF1cc2873cf61766751D99B699" : ["0x6885E36BFcb68CB383DfE90023a462C03BCB2AE5", "0x32A0E5828B62AAb932362a4816ae03b860b65e83", "0x3e3FE7dBc6B4C189E7128855dD526361c49b40Af", 0, 0], + "0x082d16150BF75BB8F2197eEC1d293DbA96c93638" : ["0x8f6254332f69557A72b0DA2D5F0Bc07d4CA991E7","0x3e3FE7dBc6B4C189E7128855dD526361c49b40Af", "0x3483c140EF7F2716460198Ff831a8e53F05F1606", 12, 1655733600], + "0x1c2807B207f140a1DE0b39E5546eDEf67Af2568c" : [1], + "0xd06dF83b8ad6D89C86a187fba4Eae918d497BdCB" : ["0x9D381f44d1fbdf8190fA0EcdC028e2Af24DdD3FE","0x0000000000000000000000000000000000000000",0, "TEST Lido DAO Token", 18, "TLDO", 1], + "0x758D8c3CE794b3Dfe3b3A3482B7eD33de2109D95" : [32, 12, 1655733600, 12, 10, "0x6885E36BFcb68CB383DfE90023a462C03BCB2AE5", "0xd497Be005638efCf09F6BFC8DAFBBB0BB72cD991"], + "0xC40801b88C835a58e54eEE6679D301ba31a4C72b" : [12, 1655733600, "0x8f6254332f69557A72b0DA2D5F0Bc07d4CA991E7"], + "0xbac2A471443F18aC5C31078b96C5797A78fCc680" : [ + "0x28FAB2059C713A7F9D8c86Db49f9bb0e96Af1ef8", + "0x22896Bfc68814BFD855b1a167255eE497006e730", + [1500,500,1000,250,2000,100,100,128,5000000], + [[],[],[],[],[],[],[],[],[],[]] + ], + "0x7bC76076b0f3879b4A750450C0Ccf02c6Ca11220" : ["0x6885E36BFcb68CB383DfE90023a462C03BCB2AE5",""], + "0x6155bD199ECcc79Ff4e8B392f6cBD9c9874E8916" : ["0x1c2807B207f140a1DE0b39E5546eDEf67Af2568c"], + "0xC73cd4B2A7c1CBC5BF046eB4A7019365558ABF66" : ["0x6155bD199ECcc79Ff4e8B392f6cBD9c9874E8916", "0xcd567bdf93dd0f6acc3bc7f2155f83244d56a65abbfbefb763e015420102c67b", ""], + "0x52AD3004Bc993d63931142Dd4f3DD647414048a1" : ["0x6155bD199ECcc79Ff4e8B392f6cBD9c9874E8916","0x5c9918c99c4081ca9459c178381be71d9da40e49e151687da55099c49a4237f1", ""], + "0x098a952BD200005382aEb3229e38ae39A7616F56" : [32,12,1655733600, 75, 10, "0x7FAcEF1c7248ed171FDd9ea3B25B4550b38e6133", "0x7637d44c9f2e9cA584a8B5D2EA493012A5cdaEB6"], + "0x1583C7b3f4C3B008720E6BcE5726336b0aB25fdd" : ["0x0220A1cF6C3a548BE75aEabCdA509CaB08CDe063", "0x32A0E5828B62AAb932362a4816ae03b860b65e83","0x0"], + "0x32A0E5828B62AAb932362a4816ae03b860b65e83" : ["0x6155bD199ECcc79Ff4e8B392f6cBD9c9874E8916","0x701a4fd1f5174d12a0f1d9ad2c88d0ad11ab6aad0ac72b7d9ce621815f8016a9","0"], + "0x33d6E15047E8644F8DDf5CD05d202dfE587DA6E3" : ["0x6155bD199ECcc79Ff4e8B392f6cBD9c9874E8916","0x7071f283424072341f856ac9e947e7ec0eb68719f757a7e785979b6b8717579d","0"], + "0x3483c140EF7F2716460198Ff831a8e53F05F1606" : ["0x6155bD199ECcc79Ff4e8B392f6cBD9c9874E8916","0x8b47ba2a8454ec799cd91646e7ec47168e91fd139b23f017455f3e5898aaba93", "0"], + "0x39A0EbdEE54cB319f4F42141daaBDb6ba25D341A" : ["0x6155bD199ECcc79Ff4e8B392f6cBD9c9874E8916","0x0abcd104777321a82b010357f20887d61247493d89d2e987ff57bcecbde00e1e", "0"], + "0x3e3FE7dBc6B4C189E7128855dD526361c49b40Af" : ["0x6155bD199ECcc79Ff4e8B392f6cBD9c9874E8916","0x3ca7c3e38968823ccb4c78ea688df41356f182ae1d159e4ee608d30d68cef320", "0"], + "0x4F36aAEb18Ab56A4e380241bea6ebF215b9cb12c" : ["0x46cF57508B0565decC0419B833C2dAFa50B132e0", "0x32A0E5828B62AAb932362a4816ae03b860b65e83", "0"], + "0x7637d44c9f2e9cA584a8B5D2EA493012A5cdaEB6" : ["0xC40801b88C835a58e54eEE6679D301ba31a4C72b", "0x32A0E5828B62AAb932362a4816ae03b860b65e83", "0"], + "0x8A1AA86d35b2EE8C9369618E7D7b40000cCD3295" : ["0x6155bD199ECcc79Ff4e8B392f6cBD9c9874E8916", "0xe3262375f45a6e2026b7e7b18c2b807434f2508fe1a2a3dfb493c7df8f4aad6a", "0"], + "0xd497Be005638efCf09F6BFC8DAFBBB0BB72cD991" : ["0x082d16150BF75BB8F2197eEC1d293DbA96c93638", "0x32A0E5828B62AAb932362a4816ae03b860b65e83", "0"] + } + }, } diff --git a/diffyscan/diffyscan.py b/diffyscan/diffyscan.py index 88d5cf0..03139f8 100755 --- a/diffyscan/diffyscan.py +++ b/diffyscan/diffyscan.py @@ -37,12 +37,12 @@ def run_binary_diff( if deployer_account is None: raise ValueError(f"Failed to receive the account {address_name})") - if "ConstructorArgs" not in config: - raise ValueError(f"Failed to find constructorArgs {address_name})") + if "constructor_args" not in config["binary_checking"]: + raise ValueError(f"Failed to find 'constructor_args' section {address_name})") contract_creation_code, immutables = get_code_from_explorer( contract_source_code, - config["ConstructorArgs"], + config["binary_checking"]["constructor_args"], contract_address_from_config, ) @@ -222,10 +222,15 @@ def process_config(path: str, recursive_parsing: bool, unify_formatting: bool): explorer_token = load_env( config["explorer_token_env_var"], masked=True, required=False ) - if explorer_token is None: - explorer_token = os.getenv("ETHERSCAN_EXPLORER_TOKEN", default=None) - if explorer_token is None: - raise ValueError(f'Failed to find "ETHERSCAN_EXPLORER_TOKEN" in env') + if explorer_token is None: + logger.warn( + f'Failed to find explorer token in config ("explorer_token_env_var")' + ) + explorer_token = os.getenv("ETHERSCAN_EXPLORER_TOKEN", default=None) + if explorer_token is None: + raise ValueError( + f'Failed to find explorer token in env ("ETHERSCAN_EXPLORER_TOKEN")' + ) contracts = config["contracts"] binary_check = ( @@ -238,6 +243,8 @@ def process_config(path: str, recursive_parsing: bool, unify_formatting: bool): if binary_check: hardhat.start(path, config["binary_checking"]) deployer_account = get_account(config["binary_checking"]["local_RPC_URL"]) + else: + logger.warn("Binary checking not activated") for contract_address, contract_name in contracts.items(): contract_code = get_contract_from_explorer( diff --git a/diffyscan/utils/explorer.py b/diffyscan/utils/explorer.py index a5fa2c3..11f9355 100644 --- a/diffyscan/utils/explorer.py +++ b/diffyscan/utils/explorer.py @@ -183,7 +183,7 @@ def get_code_from_explorer(contract_code, constructor_args, contract_address_fro return contract_creation_code, immutables if contract_address_from_config not in constructor_args: - raise ValueError(f"Failed to find constructorArgs") + raise ValueError(f"Failed to find constructor args values {contract_address_from_config}") constructor_calldata = None From 835338a47e68b58f0bdf681f9d73d4bf42abaddc Mon Sep 17 00:00:00 2001 From: alexvozhak Date: Tue, 6 Aug 2024 18:22:25 +0300 Subject: [PATCH 024/118] Filling holesky config (3/53) --- config_samples/lido_dao_holesky_config.json | 61 +++++++++++---------- hardhat_configs/holesky_hardhat.config.js | 10 ++++ 2 files changed, 43 insertions(+), 28 deletions(-) create mode 100644 hardhat_configs/holesky_hardhat.config.js diff --git a/config_samples/lido_dao_holesky_config.json b/config_samples/lido_dao_holesky_config.json index 64c9dac..b7525e0 100644 --- a/config_samples/lido_dao_holesky_config.json +++ b/config_samples/lido_dao_holesky_config.json @@ -55,6 +55,7 @@ "0x2997EA0D07D79038D83Cb04b3BB9A2Bc512E3fDA": "AppProxyUpgradeable" }, "explorer_hostname": "api-holesky.etherscan.io", + "explorer_token_env_var": "ETHERSCAN_TOKEN", "github_repo": { "url": "https://github.com/lidofinance/lido-dao", "commit": "cadffa46a2b8ed6cfa1127fca2468bae1a82d6bf", @@ -112,33 +113,37 @@ "relative_root": "contracts" } }, - "ConstructorArgs": { - "OracleReportSanityChecker" : [ - "0x28FAB2059C713A7F9D8c86Db49f9bb0e96Af1ef8", - "0x22896Bfc68814BFD855b1a167255eE497006e730", - [ - 1500, - 500, - 1000, - 250, - 2000, - 100, - 100, - 128, - 5000000 - ], - [ - [], - [], - [], - [], - [], - [], - [], - [], - [], - [] - ] - ] + "binary_checking": { + "use": 1, + "skip_deploy_error" : 1, + "hardhat_config_name": "holesky_hardhat.config.js", + "local_RPC_URL": "http://127.0.0.1:7545", + "remote_RPC_URL":"https://ethereum-holesky.blockpi.network/v1/rpc/public", + "constructor_args": { + "0x28FAB2059C713A7F9D8c86Db49f9bb0e96Af1ef8" : ["0xab89ED3D8f31bcF8BB7De53F02084d1e6F043D34", "0xE92329EC7ddB11D25e25b3c21eeBf11f15eB325d",""], + "0xF0d576c7d934bBeCc68FE15F1c5DAF98ea2B78bb" : [ + "0x28FAB2059C713A7F9D8c86Db49f9bb0e96Af1ef8", + "0x22896Bfc68814BFD855b1a167255eE497006e730", + [1500,500,1000,250,2000,100,100,128,5000000], + [[],[],[],[],[],[],[],[],[],[]] + ], + "0xDba5Ad530425bb1b14EECD76F1b4a517780de537" : [ + ["0x4E97A3972ce8511D87F334dA17a2C332542a5246", + "0x045dd46212A178428c088573A7d102B9d89a022A", + "0xE73a3602b99f1f913e72F8bdcBC235e206794Ac8", + "0x072f72BE3AcFE2c52715829F2CD9061A6C8fF019", + "0x3F1c547b21f65e10480dE3ad8E19fAAC46C95034", + "0xF0d576c7d934bBeCc68FE15F1c5DAF98ea2B78bb", + "0x072f72BE3AcFE2c52715829F2CD9061A6C8fF019", + "0x4E46BD7147ccf666E1d73A3A456fC7a68de82eCA", + "0xd6EbF043D30A7fe46D1Db32BA90a0A51207FE229", + "0xE92329EC7ddB11D25e25b3c21eeBf11f15eB325d", + "0xffDDF7025410412deaa05E3E1cE68FE53208afcb", + "0xc7cc160b58F8Bb0baC94b80847E2CF2800565C50", + "0xF0179dEC45a37423EAD4FaD5fCb136197872EAd9", + "0xC01fC1F2787687Bc656EAc0356ba9Db6e6b7afb7" + ] + ] } } +} diff --git a/hardhat_configs/holesky_hardhat.config.js b/hardhat_configs/holesky_hardhat.config.js new file mode 100644 index 0000000..b972e6b --- /dev/null +++ b/hardhat_configs/holesky_hardhat.config.js @@ -0,0 +1,10 @@ +module.exports = { + solidity: "0.8.9", + networks: { + hardhat: { + chainId: 17000, + blockGasLimit: 92000000, + hardfork: "istanbul", + } + }, +}; From 536200932e6fe9f619696d1e72fa7f2234f57f34 Mon Sep 17 00:00:00 2001 From: Alexvozhak <137793272+Alexvozhak@users.noreply.github.com> Date: Wed, 7 Aug 2024 17:28:47 +0300 Subject: [PATCH 025/118] Update README.md Co-authored-by: Yuri Tkachenko --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 3306271..f8df5ed 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ Diff your Ethereum smart contracts code from GitHub against Blockchain explorer verified source code, deployed bytecode from local node (Hardhat) against remote Supports reformatting solidity code by means of prettifier solidity plugin before comparing the sources (option `--prettify`). -Supports binary checking deployed bytecode from local node (Hardhat) against remote (see section 'binary_checking' in lido_dao_sepolia_config.json as example) +Supports binary checking deployed bytecode from the local node (Hardhat) against remote (see section 'binary_checking' in `./config_samples/lido_dao_sepolia_config.json` as an example) ## Install From 1210abcdac7671b863819b3d1f768a01dc1ace75 Mon Sep 17 00:00:00 2001 From: Alexvozhak <137793272+Alexvozhak@users.noreply.github.com> Date: Wed, 7 Aug 2024 17:29:06 +0300 Subject: [PATCH 026/118] Update README.md Co-authored-by: Yuri Tkachenko --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f8df5ed..d86988f 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ Supports binary checking deployed bytecode from the local node (Hardhat) against pipx install git+https://github.com/lidofinance/diffyscan ``` -If need `--prettify` or binary checking options support +If need `--prettify` or binary checking options support: ```shell npm install From 43d6debe17af7bf024a776e1748773c3e5bf0b10 Mon Sep 17 00:00:00 2001 From: Alexvozhak <137793272+Alexvozhak@users.noreply.github.com> Date: Wed, 7 Aug 2024 17:29:15 +0300 Subject: [PATCH 027/118] Update README.md Co-authored-by: Yuri Tkachenko --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index d86988f..27b4350 100644 --- a/README.md +++ b/README.md @@ -91,7 +91,7 @@ This project was developed using these dependencies with their exact versions li - Python 3.12 - Poetry 1.8 -- if need `--prettify` or binary checking options support +- if need `--prettify` or binary checking options support: - npm Other versions may work as well but were not tested at all. From c6803c5e76e761dc19bfb89f63eef0c38f41646a Mon Sep 17 00:00:00 2001 From: Alexvozhak <137793272+Alexvozhak@users.noreply.github.com> Date: Wed, 7 Aug 2024 17:29:25 +0300 Subject: [PATCH 028/118] Update README.md Co-authored-by: Yuri Tkachenko --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 27b4350..3394456 100644 --- a/README.md +++ b/README.md @@ -129,7 +129,7 @@ poetry shell poetry install ``` -5. If need `--prettify` or binary checking options support +5. If need `--prettify` or binary checking options support: ```shell npm install From 138fdd4b9b922ea00db370285e93f027d8e2a146 Mon Sep 17 00:00:00 2001 From: Alexvozhak <137793272+Alexvozhak@users.noreply.github.com> Date: Wed, 7 Aug 2024 17:30:00 +0300 Subject: [PATCH 029/118] Update diffyscan/utils/explorer.py Co-authored-by: Yuri Tkachenko --- diffyscan/utils/explorer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/diffyscan/utils/explorer.py b/diffyscan/utils/explorer.py index 11f9355..f684cd2 100644 --- a/diffyscan/utils/explorer.py +++ b/diffyscan/utils/explorer.py @@ -88,7 +88,7 @@ def _get_contract_from_zksync(zksync_explorer_hostname, contract): def _get_contract_from_mantle(mantle_explorer_hostname, contract): etherscan_link = f"https://{mantle_explorer_hostname}/api?module=contract&action=getsourcecode&address={contract}" - response = fetch(etherscan_link).json + response = fetch(etherscan_link).json() data = response["result"][0] if "ContractName" not in data: From 13a7a19ad5f443a81fe51cb8bef6a868475b98e9 Mon Sep 17 00:00:00 2001 From: Alexvozhak <137793272+Alexvozhak@users.noreply.github.com> Date: Wed, 7 Aug 2024 17:30:11 +0300 Subject: [PATCH 030/118] Update diffyscan/utils/explorer.py Co-authored-by: Yuri Tkachenko --- diffyscan/utils/explorer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/diffyscan/utils/explorer.py b/diffyscan/utils/explorer.py index f684cd2..a5d2c6f 100644 --- a/diffyscan/utils/explorer.py +++ b/diffyscan/utils/explorer.py @@ -68,7 +68,7 @@ def _get_contract_from_zksync(zksync_explorer_hostname, contract): f"https://{zksync_explorer_hostname}/contract_verification/info/{contract}" ) - response = fetch(zksync_explorer_link).json + response = fetch(zksync_explorer_link).json() if not response.get("verifiedAt"): logger.error("Status", response.status_code) From a296e522fafdfad288fa9f4e55cd4b8a06bc776a Mon Sep 17 00:00:00 2001 From: alexvozhak Date: Wed, 7 Aug 2024 19:32:39 +0300 Subject: [PATCH 031/118] Fixed review comments, small refactoring in diffyscan.py --- README.md | 2 +- config_samples/lido_dao_holesky_config.json | 4 +- config_samples/lido_dao_sepolia_config.json | 4 +- diffyscan/diffyscan.py | 49 ++++++++++++----- diffyscan/utils/compiler.py | 22 ++++---- diffyscan/utils/encoder.py | 2 - diffyscan/utils/explorer.py | 60 +++++++++++---------- diffyscan/utils/hardhat.py | 1 + 8 files changed, 85 insertions(+), 59 deletions(-) diff --git a/README.md b/README.md index 3394456..58cadd1 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ ![license MIT](https://img.shields.io/badge/license-MIT-brightgreen) [![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black) -Diff your Ethereum smart contracts code from GitHub against Blockchain explorer verified source code, deployed bytecode from local node (Hardhat) against remote +Diff your Ethereum smart contracts code from GitHub against Blockchain explorer verified source code, deployed bytecode from local node (Hardhat) against remote. Supports reformatting solidity code by means of prettifier solidity plugin before comparing the sources (option `--prettify`). Supports binary checking deployed bytecode from the local node (Hardhat) against remote (see section 'binary_checking' in `./config_samples/lido_dao_sepolia_config.json` as an example) diff --git a/config_samples/lido_dao_holesky_config.json b/config_samples/lido_dao_holesky_config.json index b7525e0..f4ee3f7 100644 --- a/config_samples/lido_dao_holesky_config.json +++ b/config_samples/lido_dao_holesky_config.json @@ -114,8 +114,8 @@ } }, "binary_checking": { - "use": 1, - "skip_deploy_error" : 1, + "enable": true, + "skip_deploy_error" : false, "hardhat_config_name": "holesky_hardhat.config.js", "local_RPC_URL": "http://127.0.0.1:7545", "remote_RPC_URL":"https://ethereum-holesky.blockpi.network/v1/rpc/public", diff --git a/config_samples/lido_dao_sepolia_config.json b/config_samples/lido_dao_sepolia_config.json index b12e2e1..6687fb4 100644 --- a/config_samples/lido_dao_sepolia_config.json +++ b/config_samples/lido_dao_sepolia_config.json @@ -99,8 +99,8 @@ } }, "binary_checking": { - "use": 1, - "skip_deploy_error" : 1, + "enable": true, + "skip_deploy_error" : false, "hardhat_config_name": "sepolia_hardhat.config.js", "local_RPC_URL": "http://127.0.0.1:7545", "remote_RPC_URL":"https://sepolia.drpc.org", diff --git a/diffyscan/diffyscan.py b/diffyscan/diffyscan.py index 03139f8..7a4b63d 100755 --- a/diffyscan/diffyscan.py +++ b/diffyscan/diffyscan.py @@ -6,7 +6,13 @@ from .utils.common import load_config, load_env, prettify_solidity from .utils.constants import * -from .utils.explorer import get_contract_from_explorer, get_code_from_explorer +from .utils.explorer import ( + get_contract_from_explorer, + compile_contract_from_explorer, + get_constructor_calldata, + get_contract_creation_code, + get_constructor_args_from_abi, +) from .utils.github import ( get_file_from_github, get_file_from_github_recursive, @@ -31,31 +37,46 @@ def run_binary_diff( config, deployer_account, ): - address_name = f"({contract_address_from_config} : {contract_name_from_config})" + address_name = f"{contract_address_from_config} : {contract_name_from_config}" logger.divider() logger.info(f"Started binary checking for {address_name}") if deployer_account is None: - raise ValueError(f"Failed to receive the account {address_name})") + raise ValueError(f"Failed to receive the account)") if "constructor_args" not in config["binary_checking"]: - raise ValueError(f"Failed to find 'constructor_args' section {address_name})") + raise ValueError(f"Failed to find 'constructor_args' section in config)") - contract_creation_code, immutables = get_code_from_explorer( - contract_source_code, - config["binary_checking"]["constructor_args"], - contract_address_from_config, + target_compiled_contract = compile_contract_from_explorer(contract_source_code) + + contract_creation_code, immutables = get_contract_creation_code( + target_compiled_contract ) + constructor_abi, text_info = get_constructor_args_from_abi(target_compiled_contract) + if constructor_abi is None: + logger.info(text_info) + else: + calldata, text_error = get_constructor_calldata( + contract_address_from_config, + config["binary_checking"]["constructor_args"], + constructor_abi, + ) + + skip_deploy_error = config["binary_checking"]["skip_deploy_error"] + if calldata is None: + skip_or_raise(skip_deploy_error, text_error) + return + contract_creation_code += calldata + local_RPC_URL = config["binary_checking"]["local_RPC_URL"] local_contract_address, text_reason = deploy_contract( local_RPC_URL, deployer_account, contract_creation_code ) - skip_deploy_error = config["binary_checking"]["skip_deploy_error"] if local_contract_address is None: skip_or_raise( skip_deploy_error, - f"Failed to deploy bytecode to {local_RPC_URL} {address_name} {text_reason}", + f"Failed to deploy bytecode to {local_RPC_URL} {text_reason}", ) return @@ -65,7 +86,7 @@ def run_binary_diff( if local_deployed_bytecode is None: skip_or_raise( skip_deploy_error, - text_error=f"Failed to receive bytecode from {local_RPC_URL} {address_name})", + text_error=f"Failed to receive bytecode from {local_RPC_URL})", ) return @@ -76,7 +97,7 @@ def run_binary_diff( if remote_deployed_bytecode is None: skip_or_raise( skip_deploy_error, - f"Failed to receive bytecode from {remote_RPC_URL} {address_name})", + f"Failed to receive bytecode from {remote_RPC_URL})", ) return @@ -235,8 +256,8 @@ def process_config(path: str, recursive_parsing: bool, unify_formatting: bool): contracts = config["contracts"] binary_check = ( "binary_checking" in config - and "use" in config["binary_checking"] - and config["binary_checking"]["use"] + and "enable" in config["binary_checking"] + and config["binary_checking"]["enable"] ) try: diff --git a/diffyscan/utils/compiler.py b/diffyscan/utils/compiler.py index 03ae4ba..b51d07a 100644 --- a/diffyscan/utils/compiler.py +++ b/diffyscan/utils/compiler.py @@ -21,8 +21,8 @@ def get_solc_native_platform_from_os(): raise ValueError(f"Unsupported platform {platform_name}") -def get_compiler_info(platform, required_compiler_version): - compilers_list_url = f"https://raw.githubusercontent.com/ethereum/solc-bin/gh-pages/{platform}/list.json" +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" available_compilers_list = fetch(compilers_list_url).json() required_build_info = next( ( @@ -34,13 +34,17 @@ def get_compiler_info(platform, required_compiler_version): ) if not required_build_info: - raise ValueError(f'Required compiler version for "{platform}" is not found') + raise ValueError( + f'Required compiler version for "{required_platform}" is not found' + ) return required_build_info -def download_compiler(platform, build_info, destination_path): - compiler_url = f'https://binaries.soliditylang.org/{platform}/{build_info["path"]}' +def download_compiler(required_platform, build_info, destination_path): + compiler_url = ( + f'https://binaries.soliditylang.org/{required_platform}/{build_info["path"]}' + ) download_compiler_response = fetch(compiler_url) try: @@ -66,9 +70,9 @@ def set_compiler_executable(compiler_path): os.chmod(compiler_path, compiler_file_rights.st_mode | stat.S_IEXEC) -def prepare_compiler(platform, build_info, compiler_path): +def prepare_compiler(required_platform, build_info, compiler_path): create_dirs(compiler_path) - compiler_binary = download_compiler(platform, build_info, compiler_path) + compiler_binary = download_compiler(required_platform, build_info, compiler_path) valid_checksum = build_info["sha256"][2:] check_compiler_checksum(compiler_binary, valid_checksum) set_compiler_executable(compiler_path) @@ -102,8 +106,6 @@ def get_target_compiled_contract(compiled_contracts, target_contract_name): if len(contracts_to_check) != 1: raise ValueError("multiple contracts with the same name") - logger.okay( - "Contracts were successfully compiled. The target contract is ready for matching" - ) + logger.okay(f"Contracts were successfully compiled") return contracts_to_check[0] diff --git a/diffyscan/utils/encoder.py b/diffyscan/utils/encoder.py index b5e41b2..cdc933c 100644 --- a/diffyscan/utils/encoder.py +++ b/diffyscan/utils/encoder.py @@ -56,8 +56,6 @@ def to_hex_with_alignment(value): def encode_constructor_arguments(constructor_abi, constructor_config_args): arg_length = len(constructor_abi) - logger.info(f"Constructor args types: {[arg['type'] for arg in constructor_abi]}") - constructor_calldata = "" compl_data = [] if arg_length > 0: diff --git a/diffyscan/utils/explorer.py b/diffyscan/utils/explorer.py index a5d2c6f..84ad733 100644 --- a/diffyscan/utils/explorer.py +++ b/diffyscan/utils/explorer.py @@ -27,18 +27,14 @@ def _get_contract_from_etherscan(token, etherscan_hostname, contract): _errorNoSourceCodeAndExit(contract) solc_input = result["SourceCode"] - - if solc_input.startswith("{{"): - return { + contract = { "name": result["ContractName"], - "solcInput": json.loads(solc_input[1:-1]), "compiler": result["CompilerVersion"], } + if solc_input.startswith("{{"): + contract["solcInput"] = json.loads(solc_input[1:-1]) else: - return { - "name": result["ContractName"], - "compiler": result["CompilerVersion"], - "solcInput": { + contract["solcInput":] = { "language": "Solidity", "sources": {result["ContractName"]: {"content": solc_input}}, "settings": { @@ -59,10 +55,9 @@ def _get_contract_from_etherscan(token, etherscan_hostname, contract): } }, }, - }, - } - - + } + return contract + def _get_contract_from_zksync(zksync_explorer_hostname, contract): zksync_explorer_link = ( f"https://{zksync_explorer_hostname}/contract_verification/info/{contract}" @@ -79,11 +74,12 @@ def _get_contract_from_zksync(zksync_explorer_hostname, contract): if "contractName" not in data: _errorNoSourceCodeAndExit(contract) - return { + contract = { "name": data["ContractName"], "sources": json.loads(data["sourceCode"]["sources"]), "compiler": data["CompilerVersion"], } + return contract def _get_contract_from_mantle(mantle_explorer_hostname, contract): @@ -98,11 +94,12 @@ def _get_contract_from_mantle(mantle_explorer_hostname, contract): for entry in data.get("AdditionalSources", []): source_files.append((entry["Filename"], {"content": entry["SourceCode"]})) - return { + contract = { "name": data["ContractName"], "sources": json.loads(data["sourceCode"]["sources"]), "compiler": data["CompilerVersion"], } + return contract def _get_contract_from_mode(mode_explorer_hostname, contract): @@ -119,11 +116,12 @@ def _get_contract_from_mode(mode_explorer_hostname, contract): for entry in response.get("additional_sources", []): source_files[entry["file_path"]] = {"content": entry["source_code"]} - return { + contract = { "name": response["name"], "solcInput": {"language": "Solidity", "sources": source_files}, "compiler": response["compiler_version"], } + return contract def get_contract_from_explorer( @@ -151,39 +149,47 @@ def get_contract_from_explorer( return result -def get_code_from_explorer(contract_code, constructor_args, contract_address_from_config): - platform = get_solc_native_platform_from_os() +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(platform, build_name) + build_info = get_compiler_info(required_platform, build_name) 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(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() target_contract_name = contract_code['name'] - target_compiled_contract = get_target_compiled_contract(compiled_contracts, target_contract_name) + return get_target_compiled_contract(compiled_contracts, target_contract_name) - contract_creation_code = f'0x{target_compiled_contract['evm']['bytecode']['object']}' +def get_contract_creation_code(target_compiled_contract): + contract_creation_code_without_calldata = f'0x{target_compiled_contract['evm']['bytecode']['object']}' immutables = {} 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'] + + return contract_creation_code_without_calldata, immutables + +def get_constructor_args_from_abi(target_compiled_contract): constructor_abi = None try: constructor_abi = [entry["inputs"] for entry in target_compiled_contract['abi'] if entry["type"] == "constructor"][0] except IndexError: - logger.info(f'Constructor in ABI not found') - return contract_creation_code, immutables - + return None, f'Constructor in ABI not found, calldata calculation skipped' + + logger.okay(f'Constructor in ABI successfully found: {[arg['type'] for arg in constructor_abi]}') + return constructor_abi, '' + +def get_constructor_calldata(contract_address_from_config, constructor_args, constructor_abi): if contract_address_from_config not in constructor_args: - raise ValueError(f"Failed to find constructor args values {contract_address_from_config}") + return None, f"Failed to find constructor's values in config" constructor_calldata = None @@ -191,6 +197,4 @@ def get_code_from_explorer(contract_code, constructor_args, contract_address_fro constructor_args = constructor_args [contract_address_from_config] if constructor_args: constructor_calldata = encode_constructor_arguments(constructor_abi, constructor_args) - return contract_creation_code+constructor_calldata, immutables - - return contract_creation_code, immutables, False + return constructor_calldata, '' diff --git a/diffyscan/utils/hardhat.py b/diffyscan/utils/hardhat.py index 075dd28..5b7bd14 100644 --- a/diffyscan/utils/hardhat.py +++ b/diffyscan/utils/hardhat.py @@ -67,6 +67,7 @@ def stop(self): os.kill(self.sub_process.pid, signal.SIGTERM) logger.info(f"Hardhat stopped, PID {self.sub_process.pid}") + @staticmethod def get_config_path(from_path: str, to_path: str, filename: str) -> str: parent_directory = os.path.join(from_path, os.pardir) new_directory = os.path.join(parent_directory, to_path) From 623f79a1bae5602374ba3aec958ae56108e1d86b Mon Sep 17 00:00:00 2001 From: alexvozhak Date: Thu, 8 Aug 2024 00:29:54 +0300 Subject: [PATCH 032/118] Added handling prepared calldata from config --- config_samples/lido_dao_holesky_config.json | 9 +++-- config_samples/lido_dao_sepolia_config.json | 7 +++- diffyscan/diffyscan.py | 30 +++++--------- diffyscan/utils/binary_verifier.py | 6 +-- diffyscan/utils/calldata.py | 45 +++++++++++++++++++++ diffyscan/utils/encoder.py | 4 +- diffyscan/utils/explorer.py | 23 ----------- 7 files changed, 71 insertions(+), 53 deletions(-) create mode 100644 diffyscan/utils/calldata.py diff --git a/config_samples/lido_dao_holesky_config.json b/config_samples/lido_dao_holesky_config.json index f4ee3f7..2eb9f9c 100644 --- a/config_samples/lido_dao_holesky_config.json +++ b/config_samples/lido_dao_holesky_config.json @@ -118,10 +118,13 @@ "skip_deploy_error" : false, "hardhat_config_name": "holesky_hardhat.config.js", "local_RPC_URL": "http://127.0.0.1:7545", - "remote_RPC_URL":"https://ethereum-holesky.blockpi.network/v1/rpc/public", + "remote_RPC_URL":"https://ethereum-holesky.blockpi.network/v1/rpc/public", + "constructor_calldata": { + "0x28FAB2059C713A7F9D8c86Db49f9bb0e96Af1ef8" : "000000000000000000000000ab89ed3d8f31bcf8bb7de53f02084d1e6f043d34000000000000000000000000e92329ec7ddb11d25e25b3c21eebf11f15eb325d00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" + }, "constructor_args": { - "0x28FAB2059C713A7F9D8c86Db49f9bb0e96Af1ef8" : ["0xab89ED3D8f31bcF8BB7De53F02084d1e6F043D34", "0xE92329EC7ddB11D25e25b3c21eeBf11f15eB325d",""], - "0xF0d576c7d934bBeCc68FE15F1c5DAF98ea2B78bb" : [ + "0x28FAB2059C713A7F9D8c86Db49f9bb0e96Af1ef8" : ["0xab89ED3D8f31bcF8BB7De53F02084d1e6F043D34", "0xE92329EC7ddB11D25e25b3c21eeBf11f15eB325d",""], + "0xF0d576c7d934bBeCc68FE15F1c5DAF98ea2B78bb" : [ "0x28FAB2059C713A7F9D8c86Db49f9bb0e96Af1ef8", "0x22896Bfc68814BFD855b1a167255eE497006e730", [1500,500,1000,250,2000,100,100,128,5000000], diff --git a/config_samples/lido_dao_sepolia_config.json b/config_samples/lido_dao_sepolia_config.json index 6687fb4..b51159f 100644 --- a/config_samples/lido_dao_sepolia_config.json +++ b/config_samples/lido_dao_sepolia_config.json @@ -103,7 +103,10 @@ "skip_deploy_error" : false, "hardhat_config_name": "sepolia_hardhat.config.js", "local_RPC_URL": "http://127.0.0.1:7545", - "remote_RPC_URL":"https://sepolia.drpc.org", + "remote_RPC_URL":"https://sepolia.drpc.org", + "constructor_calldata": { + "0x8f6254332f69557A72b0DA2D5F0Bc07d4CA991E7" : "000000000000000000000000f30a674935479cc6f2254ba65d7534eab8bd6ba20000000000000000000000006885e36bfcb68cb383dfe90023a462c03bcb2ae500000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" + }, "constructor_args": { "0x8f6254332f69557A72b0DA2D5F0Bc07d4CA991E7" : ["0xf30a674935479cc6f2254ba65d7534eab8bd6ba2","0x6885E36BFcb68CB383DfE90023a462C03BCB2AE5",""], "0x9726CA9AEFF4BC8FB8C084BdAbdB71608248E3f8" : ["0x3e3FE7dBc6B4C189E7128855dD526361c49b40Af"], @@ -156,6 +159,6 @@ "0x8A1AA86d35b2EE8C9369618E7D7b40000cCD3295" : ["0x6155bD199ECcc79Ff4e8B392f6cBD9c9874E8916", "0xe3262375f45a6e2026b7e7b18c2b807434f2508fe1a2a3dfb493c7df8f4aad6a", "0"], "0xd497Be005638efCf09F6BFC8DAFBBB0BB72cD991" : ["0x082d16150BF75BB8F2197eEC1d293DbA96c93638", "0x32A0E5828B62AAb932362a4816ae03b860b65e83", "0"] } - }, + } } diff --git a/diffyscan/diffyscan.py b/diffyscan/diffyscan.py index 7a4b63d..6a6eba6 100755 --- a/diffyscan/diffyscan.py +++ b/diffyscan/diffyscan.py @@ -9,9 +9,7 @@ from .utils.explorer import ( get_contract_from_explorer, compile_contract_from_explorer, - get_constructor_calldata, get_contract_creation_code, - get_constructor_args_from_abi, ) from .utils.github import ( get_file_from_github, @@ -23,6 +21,7 @@ from .utils.binary_verifier import * from .utils.hardhat import hardhat from .utils.node_handler import * +from .utils.calldata import get_calldata __version__ = "0.0.0" @@ -43,30 +42,24 @@ def run_binary_diff( if deployer_account is None: raise ValueError(f"Failed to receive the account)") - if "constructor_args" not in config["binary_checking"]: - raise ValueError(f"Failed to find 'constructor_args' section in config)") target_compiled_contract = compile_contract_from_explorer(contract_source_code) contract_creation_code, immutables = get_contract_creation_code( target_compiled_contract ) + skip_deploy_error = config["binary_checking"]["skip_deploy_error"] - constructor_abi, text_info = get_constructor_args_from_abi(target_compiled_contract) - if constructor_abi is None: - logger.info(text_info) - else: - calldata, text_error = get_constructor_calldata( - contract_address_from_config, - config["binary_checking"]["constructor_args"], - constructor_abi, - ) - - skip_deploy_error = config["binary_checking"]["skip_deploy_error"] - if calldata is None: - skip_or_raise(skip_deploy_error, text_error) - return + calldata, text_error = get_calldata( + contract_address_from_config, + target_compiled_contract, + config["binary_checking"], + ) + if calldata is not None: contract_creation_code += calldata + elif text_error is not None: + skip_or_raise(skip_deploy_error, text_error) + return local_RPC_URL = config["binary_checking"]["local_RPC_URL"] local_contract_address, text_reason = deploy_contract( @@ -105,7 +98,6 @@ def run_binary_diff( local_deployed_bytecode, remote_deployed_bytecode, immutables, - contract_address_from_config, ) diff --git a/diffyscan/utils/binary_verifier.py b/diffyscan/utils/binary_verifier.py index 967bbea..5cc3fb0 100644 --- a/diffyscan/utils/binary_verifier.py +++ b/diffyscan/utils/binary_verifier.py @@ -2,7 +2,7 @@ from .constants import OPCODES -def to_match(actualBytecode, expectedBytecode, immutables, remote_contract_address): +def to_match(actualBytecode, expectedBytecode, immutables): logger.info("Comparing actual code with the expected one...") actualInstructions = parse(actualBytecode) @@ -21,9 +21,9 @@ def to_match(actualBytecode, expectedBytecode, immutables, remote_contract_addre differences.append(i) if not differences: - logger.okay(f"Bytecodes are fully matched (contract {remote_contract_address})") + logger.okay(f"Bytecodes are fully matched") return - logger.warn(f"Bytecodes have differences contract {remote_contract_address})") + logger.warn(f"Bytecodes have differences") nearLinesCount = 3 checkpoints = {0, *differences} diff --git a/diffyscan/utils/calldata.py b/diffyscan/utils/calldata.py new file mode 100644 index 0000000..951595e --- /dev/null +++ b/diffyscan/utils/calldata.py @@ -0,0 +1,45 @@ +from .logger import logger +from .encoder import encode_constructor_arguments + +def get_calldata(contract_address_from_config, target_compiled_contract, binary_config): + calldata = get_prepared_calldata_from_config(contract_address_from_config, binary_config) + if calldata is not None: + return calldata, '' + + calldata, text_error = parse_calldata_from_config( + contract_address_from_config, + binary_config["constructor_args"], + target_compiled_contract, + ) + return calldata, text_error + +def get_constructor_abi(target_compiled_contract): + constructor_abi = None + try: + constructor_abi = [entry["inputs"] for entry in target_compiled_contract['abi'] if entry["type"] == "constructor"][0] + except IndexError: + logger.okay(f'Constructor in ABI not found, calldata calculation skipped') + return None + + logger.okay(f'Constructor in ABI successfully found: {[arg['type'] for arg in constructor_abi]}') + return constructor_abi + +def get_prepared_calldata_from_config(contract_address_from_config, binary_config): + if "constructor_calldata" not in binary_config or contract_address_from_config not in binary_config["constructor_calldata"]: + return None + calldata_field = binary_config["constructor_calldata"] + logger.info(f"Trying to use prepared calldata from config") + prepared_calldata_from_config = calldata_field[contract_address_from_config] + return prepared_calldata_from_config + +def parse_calldata_from_config(contract_address_from_config, constructor_args, target_compiled_contract): + logger.info(f"Trying to parse calldata from config") + constructor_abi = get_constructor_abi(target_compiled_contract) + if constructor_abi is None: + return None, None + + if constructor_args is None or contract_address_from_config not in constructor_args: + return None, f"Failed to find constructor's values or calldata in config" + + calldata = encode_constructor_arguments(constructor_abi, constructor_args [contract_address_from_config]) + return calldata, '' diff --git a/diffyscan/utils/encoder.py b/diffyscan/utils/encoder.py index cdc933c..146b834 100644 --- a/diffyscan/utils/encoder.py +++ b/diffyscan/utils/encoder.py @@ -43,9 +43,7 @@ def encode_tuple(types, args): offset = format((arg_index + args_length) * 32, "064x") encoded_offsets += offset else: - logger.warn( - f"Unknown constructor argument type '{arg_type}', use --constructor-calldata instead" - ) + logger.warn(f"Unknown constructor argument type '{arg_type}'") return encoded_offsets + encoded_data diff --git a/diffyscan/utils/explorer.py b/diffyscan/utils/explorer.py index 84ad733..b488f8c 100644 --- a/diffyscan/utils/explorer.py +++ b/diffyscan/utils/explorer.py @@ -5,7 +5,6 @@ from .logger import logger from .compiler import * from .constants import SOLC_DIR -from .encoder import encode_constructor_arguments def _errorNoSourceCodeAndExit(address): logger.error("source code is not verified or an EOA address", address) @@ -176,25 +175,3 @@ def get_contract_creation_code(target_compiled_contract): immutables[ref['start']] = ref['length'] return contract_creation_code_without_calldata, immutables - -def get_constructor_args_from_abi(target_compiled_contract): - constructor_abi = None - try: - constructor_abi = [entry["inputs"] for entry in target_compiled_contract['abi'] if entry["type"] == "constructor"][0] - except IndexError: - return None, f'Constructor in ABI not found, calldata calculation skipped' - - logger.okay(f'Constructor in ABI successfully found: {[arg['type'] for arg in constructor_abi]}') - return constructor_abi, '' - -def get_constructor_calldata(contract_address_from_config, constructor_args, constructor_abi): - if contract_address_from_config not in constructor_args: - return None, f"Failed to find constructor's values in config" - - constructor_calldata = None - - if constructor_args is not None and contract_address_from_config in constructor_args: - constructor_args = constructor_args [contract_address_from_config] - if constructor_args: - constructor_calldata = encode_constructor_arguments(constructor_abi, constructor_args) - return constructor_calldata, '' From 0521f02376d39137c3eb2c2c76684fc72d8f67f4 Mon Sep 17 00:00:00 2001 From: Alexvozhak <137793272+Alexvozhak@users.noreply.github.com> Date: Wed, 14 Aug 2024 23:52:17 +0300 Subject: [PATCH 033/118] Update README.md Co-authored-by: Eugene Mamin --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 58cadd1..806fca1 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ ![license MIT](https://img.shields.io/badge/license-MIT-brightgreen) [![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black) -Diff your Ethereum smart contracts code from GitHub against Blockchain explorer verified source code, deployed bytecode from local node (Hardhat) against remote. +Diff deployed EVM-compatible smart contract sourcecode and bytecode against the specified GitHub repo commit. Supports reformatting solidity code by means of prettifier solidity plugin before comparing the sources (option `--prettify`). Supports binary checking deployed bytecode from the local node (Hardhat) against remote (see section 'binary_checking' in `./config_samples/lido_dao_sepolia_config.json` as an example) From 6059460c83fb38e9a988448c0c01792735d57e1f Mon Sep 17 00:00:00 2001 From: Alexvozhak <137793272+Alexvozhak@users.noreply.github.com> Date: Wed, 14 Aug 2024 23:52:57 +0300 Subject: [PATCH 034/118] Update README.md Co-authored-by: Eugene Mamin --- README.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 806fca1..38131ee 100644 --- a/README.md +++ b/README.md @@ -7,8 +7,11 @@ Diff deployed EVM-compatible smart contract sourcecode and bytecode against the specified GitHub repo commit. -Supports reformatting solidity code by means of prettifier solidity plugin before comparing the sources (option `--prettify`). -Supports binary checking deployed bytecode from the local node (Hardhat) against remote (see section 'binary_checking' in `./config_samples/lido_dao_sepolia_config.json` as an example) +Key features: +- retrieve and diff sources from the GitHub repo against the queried ones from a blockscan service (e.g. Etherscan) +- compare the bytecode compiled and deployed on the forked network locally against remote (see section 'binary_checking' in `./config_samples/lido_dao_sepolia_config.json` as an example) +- preprocess solidity sourcecode by means of prettifier solidity plugin before comparing the sources (option `--prettify`) if needed. +- preprocess imports to flat paths for Brownie compatibility (option `--support-brownie`) ## Install From a6b93eae32cc633251278ff25647f77e9d7dd4eb Mon Sep 17 00:00:00 2001 From: Alexvozhak <137793272+Alexvozhak@users.noreply.github.com> Date: Wed, 14 Aug 2024 23:53:10 +0300 Subject: [PATCH 035/118] Update README.md Co-authored-by: Eugene Mamin --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 38131ee..2c18684 100644 --- a/README.md +++ b/README.md @@ -94,7 +94,7 @@ This project was developed using these dependencies with their exact versions li - Python 3.12 - Poetry 1.8 -- if need `--prettify` or binary checking options support: +- if deployed bytecode binary comparison or pretifier sources preprocessing are needed: - npm Other versions may work as well but were not tested at all. From 95d1e6a742461998bedc4f75e802205a5ff7b63d Mon Sep 17 00:00:00 2001 From: Alexvozhak <137793272+Alexvozhak@users.noreply.github.com> Date: Wed, 14 Aug 2024 23:53:19 +0300 Subject: [PATCH 036/118] Update README.md Co-authored-by: Eugene Mamin --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 2c18684..1fa6523 100644 --- a/README.md +++ b/README.md @@ -132,7 +132,7 @@ poetry shell poetry install ``` -5. If need `--prettify` or binary checking options support: +5. If deployed bytecode binary comparison or pretifier sources preprocessing are needed: ```shell npm install From e882a538b1ea958756e6e3cbe29d117fb3d7bb75 Mon Sep 17 00:00:00 2001 From: Alexvozhak <137793272+Alexvozhak@users.noreply.github.com> Date: Wed, 14 Aug 2024 23:53:53 +0300 Subject: [PATCH 037/118] Update package.json Co-authored-by: Eugene Mamin --- package.json | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/package.json b/package.json index 5e09e53..4871712 100644 --- a/package.json +++ b/package.json @@ -6,5 +6,9 @@ "dependencies": { "hardhat": "^2.22.7", "kill-port": "^2.0.1" + }, + "packageManager": "npm@10.8.2", + "engines": { + "node": ">=20.0" } } From 099c0adef4288d579eff463ade0c280d7a841b9b Mon Sep 17 00:00:00 2001 From: Alexvozhak <137793272+Alexvozhak@users.noreply.github.com> Date: Wed, 14 Aug 2024 23:55:25 +0300 Subject: [PATCH 038/118] Update diffyscan/utils/hardhat.py Co-authored-by: Eugene Mamin --- diffyscan/utils/hardhat.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/diffyscan/utils/hardhat.py b/diffyscan/utils/hardhat.py index 5b7bd14..8dc7921 100644 --- a/diffyscan/utils/hardhat.py +++ b/diffyscan/utils/hardhat.py @@ -36,7 +36,7 @@ def start(self, main_config_relative_path: str, binary_config): logger.info(f'Trying to start Hardhat: "{local_node_command}"') is_port_used = self.is_port_in_use(parsed_url) if is_port_used: - answer = input(f'Port {parsed_url.port} is busy. Fix it? write "yes": ') + answer = input(f'Port {parsed_url.port} is busy. Kill the app instance occupying the port? write "yes": ') if answer.lower() == "yes": return_code = subprocess.call( f"exec npx kill-port {parsed_url.port}", shell=True From f11a53fb45db0191f028b2a4597ef4ffe527e7ca Mon Sep 17 00:00:00 2001 From: Alexvozhak <137793272+Alexvozhak@users.noreply.github.com> Date: Wed, 14 Aug 2024 23:56:43 +0300 Subject: [PATCH 039/118] Update README.md Co-authored-by: Eugene Mamin --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 1fa6523..9c7ea9a 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,7 @@ ![python >=3.10,<4](https://img.shields.io/badge/python-≥3.10,<4-blue) ![poetry ^1.8](https://img.shields.io/badge/poetry-^1.8-blue) +![NodeJs >=20](https://img.shields.io/badge/NodeJS-≥20-yellow) ![license MIT](https://img.shields.io/badge/license-MIT-brightgreen) [![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black) From 086e36fd85c16edd0b87b9ed2bc9ff963a1c0e99 Mon Sep 17 00:00:00 2001 From: Alexvozhak <137793272+Alexvozhak@users.noreply.github.com> Date: Wed, 14 Aug 2024 23:59:04 +0300 Subject: [PATCH 040/118] Update README.md Co-authored-by: Eugene Mamin --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 9c7ea9a..ee0c5c6 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ Key features: pipx install git+https://github.com/lidofinance/diffyscan ``` -If need `--prettify` or binary checking options support: +If deployed bytecode binary comparison or pretifier sources preprocessing are needed: ```shell npm install From 78fc2f6a69ffd2ad34ee78cac6052212e9c28f62 Mon Sep 17 00:00:00 2001 From: Alexvozhak <137793272+Alexvozhak@users.noreply.github.com> Date: Thu, 15 Aug 2024 00:03:09 +0300 Subject: [PATCH 041/118] Update diffyscan/diffyscan.py Co-authored-by: Eugene Mamin --- diffyscan/diffyscan.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/diffyscan/diffyscan.py b/diffyscan/diffyscan.py index 6a6eba6..22eb0e6 100755 --- a/diffyscan/diffyscan.py +++ b/diffyscan/diffyscan.py @@ -29,7 +29,7 @@ g_skip_user_input: bool = False -def run_binary_diff( +def run_bytecode_diff( contract_address_from_config, contract_name_from_config, contract_source_code, From 29271ad56d2c4f737055b01f7ffcbb7611319625 Mon Sep 17 00:00:00 2001 From: Alexvozhak <137793272+Alexvozhak@users.noreply.github.com> Date: Thu, 15 Aug 2024 00:03:55 +0300 Subject: [PATCH 042/118] Update diffyscan/diffyscan.py Co-authored-by: Eugene Mamin --- diffyscan/diffyscan.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/diffyscan/diffyscan.py b/diffyscan/diffyscan.py index 22eb0e6..a682869 100755 --- a/diffyscan/diffyscan.py +++ b/diffyscan/diffyscan.py @@ -38,7 +38,7 @@ def run_bytecode_diff( ): address_name = f"{contract_address_from_config} : {contract_name_from_config}" logger.divider() - logger.info(f"Started binary checking for {address_name}") + logger.info(f"Binary bytecode comparion started for {address_name}") if deployer_account is None: raise ValueError(f"Failed to receive the account)") From 0f7e83884a75b7a1761f6aec9b19fc2375d29dde Mon Sep 17 00:00:00 2001 From: Alexvozhak <137793272+Alexvozhak@users.noreply.github.com> Date: Thu, 15 Aug 2024 00:04:13 +0300 Subject: [PATCH 043/118] Update config_samples/lido_dao_holesky_config.json Co-authored-by: Eugene Mamin --- config_samples/lido_dao_holesky_config.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config_samples/lido_dao_holesky_config.json b/config_samples/lido_dao_holesky_config.json index 2eb9f9c..bfb4cc3 100644 --- a/config_samples/lido_dao_holesky_config.json +++ b/config_samples/lido_dao_holesky_config.json @@ -113,7 +113,7 @@ "relative_root": "contracts" } }, - "binary_checking": { + "bytecode_comparison": { "enable": true, "skip_deploy_error" : false, "hardhat_config_name": "holesky_hardhat.config.js", From e7b0529b93fecf744230a101cdced1e22815eb58 Mon Sep 17 00:00:00 2001 From: Alexvozhak <137793272+Alexvozhak@users.noreply.github.com> Date: Thu, 15 Aug 2024 00:04:55 +0300 Subject: [PATCH 044/118] Update diffyscan/diffyscan.py Co-authored-by: Eugene Mamin --- diffyscan/diffyscan.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/diffyscan/diffyscan.py b/diffyscan/diffyscan.py index a682869..4a38b0f 100755 --- a/diffyscan/diffyscan.py +++ b/diffyscan/diffyscan.py @@ -41,7 +41,7 @@ def run_bytecode_diff( logger.info(f"Binary bytecode comparion started for {address_name}") if deployer_account is None: - raise ValueError(f"Failed to receive the account)") + raise ValueError(f"The deployer account isn't set)") target_compiled_contract = compile_contract_from_explorer(contract_source_code) From 5f574542acc9d814596ff39db3ed03531c4911ab Mon Sep 17 00:00:00 2001 From: Alexvozhak <137793272+Alexvozhak@users.noreply.github.com> Date: Thu, 15 Aug 2024 00:06:07 +0300 Subject: [PATCH 045/118] Update diffyscan/diffyscan.py Co-authored-by: Eugene Mamin --- diffyscan/diffyscan.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/diffyscan/diffyscan.py b/diffyscan/diffyscan.py index 4a38b0f..2ba9bf9 100755 --- a/diffyscan/diffyscan.py +++ b/diffyscan/diffyscan.py @@ -101,7 +101,7 @@ def run_bytecode_diff( ) -def skip_or_raise(skip_deploy_error, text_error): +def raise_error_or_log(message: str, raise_exception: bool = True): if skip_deploy_error: logger.error(text_error) else: From e13367fd574910db789772375b24a5bdafa00188 Mon Sep 17 00:00:00 2001 From: Alexvozhak <137793272+Alexvozhak@users.noreply.github.com> Date: Thu, 15 Aug 2024 00:07:25 +0300 Subject: [PATCH 046/118] Update diffyscan/diffyscan.py Co-authored-by: Eugene Mamin --- diffyscan/diffyscan.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/diffyscan/diffyscan.py b/diffyscan/diffyscan.py index 2ba9bf9..0047dc6 100755 --- a/diffyscan/diffyscan.py +++ b/diffyscan/diffyscan.py @@ -237,7 +237,7 @@ def process_config(path: str, recursive_parsing: bool, unify_formatting: bool): ) if explorer_token is None: logger.warn( - f'Failed to find explorer token in config ("explorer_token_env_var")' + f'Failed to find an explorer token in the config ("explorer_token_env_var")' ) explorer_token = os.getenv("ETHERSCAN_EXPLORER_TOKEN", default=None) if explorer_token is None: From dd3e26181c8729bd52096b580a372f23ca1a712b Mon Sep 17 00:00:00 2001 From: Alexvozhak <137793272+Alexvozhak@users.noreply.github.com> Date: Thu, 15 Aug 2024 00:09:09 +0300 Subject: [PATCH 047/118] Update diffyscan/diffyscan.py Co-authored-by: Eugene Mamin --- diffyscan/diffyscan.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/diffyscan/diffyscan.py b/diffyscan/diffyscan.py index 0047dc6..84c3391 100755 --- a/diffyscan/diffyscan.py +++ b/diffyscan/diffyscan.py @@ -257,7 +257,7 @@ def process_config(path: str, recursive_parsing: bool, unify_formatting: bool): hardhat.start(path, config["binary_checking"]) deployer_account = get_account(config["binary_checking"]["local_RPC_URL"]) else: - logger.warn("Binary checking not activated") + logger.warn("Binary bytecode comparison is not activated") for contract_address, contract_name in contracts.items(): contract_code = get_contract_from_explorer( From de09c77721e929896cf232198133a771d7383b64 Mon Sep 17 00:00:00 2001 From: Alexvozhak <137793272+Alexvozhak@users.noreply.github.com> Date: Thu, 15 Aug 2024 00:10:29 +0300 Subject: [PATCH 048/118] Update diffyscan/diffyscan.py Co-authored-by: Eugene Mamin --- diffyscan/diffyscan.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/diffyscan/diffyscan.py b/diffyscan/diffyscan.py index 84c3391..d5ac76d 100755 --- a/diffyscan/diffyscan.py +++ b/diffyscan/diffyscan.py @@ -299,7 +299,7 @@ def parse_arguments(): ) parser.add_argument( "--yes", - "-y", + "-Y", help="If set don't ask for input before validating each contract", action="store_true", ) From e821bd5968e0e4b0b966ade301a9c51919bbef46 Mon Sep 17 00:00:00 2001 From: Alexvozhak <137793272+Alexvozhak@users.noreply.github.com> Date: Thu, 15 Aug 2024 00:11:11 +0300 Subject: [PATCH 049/118] Update diffyscan/diffyscan.py Co-authored-by: Eugene Mamin --- diffyscan/diffyscan.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/diffyscan/diffyscan.py b/diffyscan/diffyscan.py index d5ac76d..15138b4 100755 --- a/diffyscan/diffyscan.py +++ b/diffyscan/diffyscan.py @@ -310,7 +310,7 @@ def parse_arguments(): ) parser.add_argument( "--prettify", - "-p", + "-P", help="Unify formatting by prettier before comparing", action="store_true", ) From 9f2da4b0986226b4c614690e37b1018f15ead19d Mon Sep 17 00:00:00 2001 From: Alexvozhak <137793272+Alexvozhak@users.noreply.github.com> Date: Thu, 15 Aug 2024 00:11:59 +0300 Subject: [PATCH 050/118] Update diffyscan/utils/binary_verifier.py Co-authored-by: Eugene Mamin --- diffyscan/utils/binary_verifier.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/diffyscan/utils/binary_verifier.py b/diffyscan/utils/binary_verifier.py index 5cc3fb0..f7fb34a 100644 --- a/diffyscan/utils/binary_verifier.py +++ b/diffyscan/utils/binary_verifier.py @@ -2,7 +2,7 @@ from .constants import OPCODES -def to_match(actualBytecode, expectedBytecode, immutables): +def match_bytecode(actualBytecode, expectedBytecode, immutables): logger.info("Comparing actual code with the expected one...") actualInstructions = parse(actualBytecode) From 0cc99290ced6b16b7cd2323e47a25270705a833a Mon Sep 17 00:00:00 2001 From: Alexvozhak <137793272+Alexvozhak@users.noreply.github.com> Date: Thu, 15 Aug 2024 00:16:01 +0300 Subject: [PATCH 051/118] Update diffyscan/utils/calldata.py Co-authored-by: Eugene Mamin --- diffyscan/utils/calldata.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/diffyscan/utils/calldata.py b/diffyscan/utils/calldata.py index 951595e..76518fe 100644 --- a/diffyscan/utils/calldata.py +++ b/diffyscan/utils/calldata.py @@ -4,7 +4,7 @@ def get_calldata(contract_address_from_config, target_compiled_contract, binary_config): calldata = get_prepared_calldata_from_config(contract_address_from_config, binary_config) if calldata is not None: - return calldata, '' + return calldata, None calldata, text_error = parse_calldata_from_config( contract_address_from_config, From 7d60b5308e12e364796bd3cbfb25b37ad9e8297c Mon Sep 17 00:00:00 2001 From: Alexvozhak <137793272+Alexvozhak@users.noreply.github.com> Date: Thu, 15 Aug 2024 00:19:29 +0300 Subject: [PATCH 052/118] Update diffyscan/utils/calldata.py Co-authored-by: Eugene Mamin --- diffyscan/utils/calldata.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/diffyscan/utils/calldata.py b/diffyscan/utils/calldata.py index 76518fe..1c8fcb6 100644 --- a/diffyscan/utils/calldata.py +++ b/diffyscan/utils/calldata.py @@ -18,7 +18,7 @@ def get_constructor_abi(target_compiled_contract): try: constructor_abi = [entry["inputs"] for entry in target_compiled_contract['abi'] if entry["type"] == "constructor"][0] except IndexError: - logger.okay(f'Constructor in ABI not found, calldata calculation skipped') + logger.okay(f'Contract's ABI doesn't have a constructor, calldata calculation skipped') return None logger.okay(f'Constructor in ABI successfully found: {[arg['type'] for arg in constructor_abi]}') From 9967cdeb248d0c0a987888ca9321691fed57652f Mon Sep 17 00:00:00 2001 From: Alexvozhak <137793272+Alexvozhak@users.noreply.github.com> Date: Thu, 15 Aug 2024 00:20:34 +0300 Subject: [PATCH 053/118] Update diffyscan/utils/calldata.py Co-authored-by: Eugene Mamin --- diffyscan/utils/calldata.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/diffyscan/utils/calldata.py b/diffyscan/utils/calldata.py index 1c8fcb6..4e92cec 100644 --- a/diffyscan/utils/calldata.py +++ b/diffyscan/utils/calldata.py @@ -24,7 +24,7 @@ def get_constructor_abi(target_compiled_contract): logger.okay(f'Constructor in ABI successfully found: {[arg['type'] for arg in constructor_abi]}') return constructor_abi -def get_prepared_calldata_from_config(contract_address_from_config, binary_config): +def get_raw_calldata_from_config(contract_address_from_config, binary_config): if "constructor_calldata" not in binary_config or contract_address_from_config not in binary_config["constructor_calldata"]: return None calldata_field = binary_config["constructor_calldata"] From c53e939da6df1bf64faf51b8750069dcce05fe8f Mon Sep 17 00:00:00 2001 From: Alexvozhak <137793272+Alexvozhak@users.noreply.github.com> Date: Thu, 15 Aug 2024 00:26:28 +0300 Subject: [PATCH 054/118] Update diffyscan/utils/common.py Co-authored-by: Eugene Mamin --- diffyscan/utils/common.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/diffyscan/utils/common.py b/diffyscan/utils/common.py index 2c30532..4658f0c 100644 --- a/diffyscan/utils/common.py +++ b/diffyscan/utils/common.py @@ -45,7 +45,7 @@ def fetch(url, headers=None): except requests.exceptions.Timeout as timeout_err: raise ValueError(f"Timeout error occurred: {timeout_err}") except requests.exceptions.RequestException as req_err: - raise ValueError(f"An error occurred: {req_err}") + raise ValueError(f"Request exception occurred: {req_err}") return response From d9f246a9c70200b8359ea38abfac7d20ae07bcb5 Mon Sep 17 00:00:00 2001 From: Alexvozhak <137793272+Alexvozhak@users.noreply.github.com> Date: Thu, 15 Aug 2024 00:26:57 +0300 Subject: [PATCH 055/118] Update diffyscan/utils/common.py Co-authored-by: Eugene Mamin --- diffyscan/utils/common.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/diffyscan/utils/common.py b/diffyscan/utils/common.py index 4658f0c..6b77fee 100644 --- a/diffyscan/utils/common.py +++ b/diffyscan/utils/common.py @@ -62,7 +62,7 @@ def pull(url, payload=None, headers=None): except requests.exceptions.Timeout as timeout_err: raise ValueError(f"Timeout error occurred: {timeout_err}") except requests.exceptions.RequestException as req_err: - raise ValueError(f"An error occurred: {req_err}") + raise ValueError(f"Request exception occurred: {req_err}") return response From 37ebc52398ba30cfbc9b2134546decfad094776c Mon Sep 17 00:00:00 2001 From: Alexvozhak <137793272+Alexvozhak@users.noreply.github.com> Date: Thu, 15 Aug 2024 00:27:29 +0300 Subject: [PATCH 056/118] Update diffyscan/utils/common.py Co-authored-by: Eugene Mamin --- diffyscan/utils/common.py | 1 + 1 file changed, 1 insertion(+) diff --git a/diffyscan/utils/common.py b/diffyscan/utils/common.py index 6b77fee..0fe131c 100644 --- a/diffyscan/utils/common.py +++ b/diffyscan/utils/common.py @@ -86,6 +86,7 @@ def prettify_solidity(solidity_contract_content: str): ) with open(github_file_name, "w") as fp: fp.write(solidity_contract_content) + prettier_return_code = subprocess.call( [ "npx", From e518f77ca1101d16288f2bcd42317584153b5fb8 Mon Sep 17 00:00:00 2001 From: Alexvozhak <137793272+Alexvozhak@users.noreply.github.com> Date: Thu, 15 Aug 2024 00:28:04 +0300 Subject: [PATCH 057/118] Update diffyscan/utils/compiler.py Co-authored-by: Eugene Mamin --- diffyscan/utils/compiler.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/diffyscan/utils/compiler.py b/diffyscan/utils/compiler.py index b51d07a..8f02904 100644 --- a/diffyscan/utils/compiler.py +++ b/diffyscan/utils/compiler.py @@ -17,6 +17,8 @@ def get_solc_native_platform_from_os(): return "linux-amd64" elif platform_name == "darwin": return "macosx-amd64" if platform.machine() == "x86_64" else "macosx-arm64" + elif plaform_name == "win32": + return "windows-amd64" else: raise ValueError(f"Unsupported platform {platform_name}") From 3ac8afeed603ab1695ac9dfbedcbc8801938f3b9 Mon Sep 17 00:00:00 2001 From: Alexvozhak <137793272+Alexvozhak@users.noreply.github.com> Date: Thu, 15 Aug 2024 00:28:47 +0300 Subject: [PATCH 058/118] Update diffyscan/utils/compiler.py Co-authored-by: Eugene Mamin --- diffyscan/utils/compiler.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/diffyscan/utils/compiler.py b/diffyscan/utils/compiler.py index 8f02904..0045c6d 100644 --- a/diffyscan/utils/compiler.py +++ b/diffyscan/utils/compiler.py @@ -37,7 +37,7 @@ def get_compiler_info(required_platform, required_compiler_version): if not required_build_info: raise ValueError( - f'Required compiler version for "{required_platform}" is not found' + f'Required compiler version "{required_compiler_version}" for "{required_platform}" is not found' ) return required_build_info From f83a99a1b1a4ca19211cdc92439c4f15608ee9f2 Mon Sep 17 00:00:00 2001 From: Alexvozhak <137793272+Alexvozhak@users.noreply.github.com> Date: Thu, 15 Aug 2024 00:33:04 +0300 Subject: [PATCH 059/118] Update diffyscan/utils/compiler.py Co-authored-by: Eugene Mamin --- diffyscan/utils/compiler.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/diffyscan/utils/compiler.py b/diffyscan/utils/compiler.py index 0045c6d..5c9ee87 100644 --- a/diffyscan/utils/compiler.py +++ b/diffyscan/utils/compiler.py @@ -90,7 +90,7 @@ def compile_contracts(compiler_path, input_settings): timeout=30, ) except subprocess.CalledProcessError as e: - raise ValueError(f"Error during subprocess execution: {e}") + raise ValueError(f"Error during compiler subprocess execution: {e}") except subprocess.TimeoutExpired as e: raise ValueError(f"Process timed out: {e}") except Exception as e: From 2419fd28c8722eec83ab0b0dc3ee68b0380b5974 Mon Sep 17 00:00:00 2001 From: Alexvozhak <137793272+Alexvozhak@users.noreply.github.com> Date: Thu, 15 Aug 2024 00:33:40 +0300 Subject: [PATCH 060/118] Update diffyscan/utils/compiler.py Co-authored-by: Eugene Mamin --- diffyscan/utils/compiler.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/diffyscan/utils/compiler.py b/diffyscan/utils/compiler.py index 5c9ee87..041f0af 100644 --- a/diffyscan/utils/compiler.py +++ b/diffyscan/utils/compiler.py @@ -92,7 +92,7 @@ def compile_contracts(compiler_path, input_settings): except subprocess.CalledProcessError as e: raise ValueError(f"Error during compiler subprocess execution: {e}") except subprocess.TimeoutExpired as e: - raise ValueError(f"Process timed out: {e}") + raise ValueError(f"Compiler process timed out: {e}") except Exception as e: raise ValueError(f"An unexpected error occurred: {e}") return json.loads(process.stdout) From 090e41a55ce7ad9dd8922e6cc82d98b212e68d64 Mon Sep 17 00:00:00 2001 From: alexvozhak Date: Thu, 5 Sep 2024 18:40:19 +0300 Subject: [PATCH 061/118] revived branch --- diffyscan/diffyscan.py | 19 +++++++++---------- diffyscan/utils/calldata.py | 6 +++--- diffyscan/utils/compiler.py | 2 +- diffyscan/utils/hardhat.py | 5 +---- 4 files changed, 14 insertions(+), 18 deletions(-) diff --git a/diffyscan/diffyscan.py b/diffyscan/diffyscan.py index 15138b4..6d826cc 100755 --- a/diffyscan/diffyscan.py +++ b/diffyscan/diffyscan.py @@ -58,7 +58,7 @@ def run_bytecode_diff( if calldata is not None: contract_creation_code += calldata elif text_error is not None: - skip_or_raise(skip_deploy_error, text_error) + raise_error_or_log(skip_deploy_error, text_error) return local_RPC_URL = config["binary_checking"]["local_RPC_URL"] @@ -67,7 +67,7 @@ def run_bytecode_diff( ) if local_contract_address is None: - skip_or_raise( + raise_error_or_log( skip_deploy_error, f"Failed to deploy bytecode to {local_RPC_URL} {text_reason}", ) @@ -77,7 +77,7 @@ def run_bytecode_diff( local_contract_address, local_RPC_URL ) if local_deployed_bytecode is None: - skip_or_raise( + raise_error_or_log( skip_deploy_error, text_error=f"Failed to receive bytecode from {local_RPC_URL})", ) @@ -88,13 +88,13 @@ def run_bytecode_diff( contract_address_from_config, remote_RPC_URL ) if remote_deployed_bytecode is None: - skip_or_raise( + raise_error_or_log( skip_deploy_error, f"Failed to receive bytecode from {remote_RPC_URL})", ) return - to_match( + match_bytecode( local_deployed_bytecode, remote_deployed_bytecode, immutables, @@ -102,10 +102,9 @@ def run_bytecode_diff( def raise_error_or_log(message: str, raise_exception: bool = True): - if skip_deploy_error: - logger.error(text_error) - else: - raise ValueError(text_error) + if raise_exception: + raise ValueError(message) + logger.error(message) def run_source_diff( @@ -275,7 +274,7 @@ def process_config(path: str, recursive_parsing: bool, unify_formatting: bool): unify_formatting, ) if binary_check: - run_binary_diff( + run_bytecode_diff( contract_address, contract_name, contract_code, diff --git a/diffyscan/utils/calldata.py b/diffyscan/utils/calldata.py index 4e92cec..d1d9186 100644 --- a/diffyscan/utils/calldata.py +++ b/diffyscan/utils/calldata.py @@ -2,7 +2,7 @@ from .encoder import encode_constructor_arguments def get_calldata(contract_address_from_config, target_compiled_contract, binary_config): - calldata = get_prepared_calldata_from_config(contract_address_from_config, binary_config) + calldata = get_raw_calldata_from_config(contract_address_from_config, binary_config) if calldata is not None: return calldata, None @@ -18,7 +18,7 @@ def get_constructor_abi(target_compiled_contract): try: constructor_abi = [entry["inputs"] for entry in target_compiled_contract['abi'] if entry["type"] == "constructor"][0] except IndexError: - logger.okay(f'Contract's ABI doesn't have a constructor, calldata calculation skipped') + logger.okay(f"Contract's ABI doesn't have a constructor, calldata calculation skipped") return None logger.okay(f'Constructor in ABI successfully found: {[arg['type'] for arg in constructor_abi]}') @@ -42,4 +42,4 @@ def parse_calldata_from_config(contract_address_from_config, constructor_args, t return None, f"Failed to find constructor's values or calldata in config" calldata = encode_constructor_arguments(constructor_abi, constructor_args [contract_address_from_config]) - return calldata, '' + return calldata, None diff --git a/diffyscan/utils/compiler.py b/diffyscan/utils/compiler.py index 041f0af..0ece359 100644 --- a/diffyscan/utils/compiler.py +++ b/diffyscan/utils/compiler.py @@ -17,7 +17,7 @@ def get_solc_native_platform_from_os(): return "linux-amd64" elif platform_name == "darwin": return "macosx-amd64" if platform.machine() == "x86_64" else "macosx-arm64" - elif plaform_name == "win32": + elif platform_name == "win32": return "windows-amd64" else: raise ValueError(f"Unsupported platform {platform_name}") diff --git a/diffyscan/utils/hardhat.py b/diffyscan/utils/hardhat.py index 8dc7921..66936b5 100644 --- a/diffyscan/utils/hardhat.py +++ b/diffyscan/utils/hardhat.py @@ -69,10 +69,7 @@ def stop(self): @staticmethod def get_config_path(from_path: str, to_path: str, filename: str) -> str: - parent_directory = os.path.join(from_path, os.pardir) - new_directory = os.path.join(parent_directory, to_path) - path_to_file = os.path.join(new_directory, filename) - return os.path.normpath(path_to_file) + return os.path.normpath(os.path.join(from_path, os.pardir, to_path, filename)) hardhat = Hardhat() From 16529847653e665efc8cf990a2d84b1b4618b849 Mon Sep 17 00:00:00 2001 From: alexvozhak Date: Sat, 7 Sep 2024 14:21:12 +0300 Subject: [PATCH 062/118] removed "enable" field --- config_samples/lido_dao_sepolia_config.json | 3 +- diffyscan/diffyscan.py | 49 +++++++++------------ 2 files changed, 22 insertions(+), 30 deletions(-) diff --git a/config_samples/lido_dao_sepolia_config.json b/config_samples/lido_dao_sepolia_config.json index b51159f..23be4b8 100644 --- a/config_samples/lido_dao_sepolia_config.json +++ b/config_samples/lido_dao_sepolia_config.json @@ -98,8 +98,7 @@ "relative_root": "contracts" } }, - "binary_checking": { - "enable": true, + "bytecode_comparison": { "skip_deploy_error" : false, "hardhat_config_name": "sepolia_hardhat.config.js", "local_RPC_URL": "http://127.0.0.1:7545", diff --git a/diffyscan/diffyscan.py b/diffyscan/diffyscan.py index 6d826cc..f2a47a9 100755 --- a/diffyscan/diffyscan.py +++ b/diffyscan/diffyscan.py @@ -33,7 +33,7 @@ def run_bytecode_diff( contract_address_from_config, contract_name_from_config, contract_source_code, - config, + binary_config, deployer_account, ): address_name = f"{contract_address_from_config} : {contract_name_from_config}" @@ -48,12 +48,12 @@ def run_bytecode_diff( contract_creation_code, immutables = get_contract_creation_code( target_compiled_contract ) - skip_deploy_error = config["binary_checking"]["skip_deploy_error"] + skip_deploy_error = binary_config["skip_deploy_error"] calldata, text_error = get_calldata( contract_address_from_config, target_compiled_contract, - config["binary_checking"], + binary_config, ) if calldata is not None: contract_creation_code += calldata @@ -61,7 +61,7 @@ def run_bytecode_diff( raise_error_or_log(skip_deploy_error, text_error) return - local_RPC_URL = config["binary_checking"]["local_RPC_URL"] + local_RPC_URL = binary_config["local_RPC_URL"] local_contract_address, text_reason = deploy_contract( local_RPC_URL, deployer_account, contract_creation_code ) @@ -83,7 +83,7 @@ def run_bytecode_diff( ) return - remote_RPC_URL = config["binary_checking"]["remote_RPC_URL"] + remote_RPC_URL = binary_config["remote_RPC_URL"] remote_deployed_bytecode = get_bytecode_from_node( contract_address_from_config, remote_RPC_URL ) @@ -243,22 +243,17 @@ def process_config(path: str, recursive_parsing: bool, unify_formatting: bool): raise ValueError( f'Failed to find explorer token in env ("ETHERSCAN_EXPLORER_TOKEN")' ) - - contracts = config["contracts"] - binary_check = ( - "binary_checking" in config - and "enable" in config["binary_checking"] - and config["binary_checking"]["enable"] - ) + bytecode_comparison_key = "bytecode_comparison" + if bytecode_comparison_key not in config: + raise ValueError( + f'Failed to find "{bytecode_comparison_key}" section in config' + ) try: - if binary_check: - hardhat.start(path, config["binary_checking"]) - deployer_account = get_account(config["binary_checking"]["local_RPC_URL"]) - else: - logger.warn("Binary bytecode comparison is not activated") + hardhat.start(path, config[bytecode_comparison_key]) + deployer_account = get_account(config[bytecode_comparison_key]["local_RPC_URL"]) - for contract_address, contract_name in contracts.items(): + for contract_address, contract_name in config["contracts"].items(): contract_code = get_contract_from_explorer( explorer_token, config["explorer_hostname"], @@ -273,19 +268,17 @@ def process_config(path: str, recursive_parsing: bool, unify_formatting: bool): recursive_parsing, unify_formatting, ) - if binary_check: - run_bytecode_diff( - contract_address, - contract_name, - contract_code, - config, - deployer_account, - ) + run_bytecode_diff( + contract_address, + contract_name, + contract_code, + config[bytecode_comparison_key], + deployer_account, + ) except KeyboardInterrupt: logger.info(f"Keyboard interrupt by user") finally: - if binary_check: - hardhat.stop() + hardhat.stop() def parse_arguments(): From dc53f7412e8cca1bc1e2d5d48540b563e5a18f8a Mon Sep 17 00:00:00 2001 From: alexvozhak Date: Sat, 7 Sep 2024 16:11:05 +0300 Subject: [PATCH 063/118] jq applied --- config_samples/lido_dao_holesky_config.json | 316 +++++++------- config_samples/lido_dao_sepolia_config.json | 451 +++++++++++++------- 2 files changed, 465 insertions(+), 302 deletions(-) diff --git a/config_samples/lido_dao_holesky_config.json b/config_samples/lido_dao_holesky_config.json index bfb4cc3..5717c24 100644 --- a/config_samples/lido_dao_holesky_config.json +++ b/config_samples/lido_dao_holesky_config.json @@ -1,152 +1,178 @@ { - "contracts": { - "0x28FAB2059C713A7F9D8c86Db49f9bb0e96Af1ef8": "OssifiableProxy", - "0xDba5Ad530425bb1b14EECD76F1b4a517780de537": "LidoLocator", - "0x3F1c547b21f65e10480dE3ad8E19fAAC46C95034": "AppProxyUpgradeable", - "0x59034815464d18134A55EED3702b535D8A32c52b": "Lido", - "0x8d09a4502Cc8Cf1547aD300E066060D043f6982D": "WstETH", - "0xE154732c5Eab277fd88a9fF6Bdff7805eD97BCB1": "EIP712StETH", - "0xd6EbF043D30A7fe46D1Db32BA90a0A51207FE229": "OssifiableProxy", - "0x32f236423928c2c138F46351D9E5FD26331B1aa4": "StakingRouter", - "0x595F64Ddc3856a3b5Ff4f4CC1d1fb4B46cFd2bAC": "AppProxyUpgradeable", - "0xE0270CF2564d81E02284e16539F59C1B5a4718fE": "NodeOperatorsRegistry", - "0x045dd46212A178428c088573A7d102B9d89a022A": "DepositSecurityModule", - "0xE73a3602b99f1f913e72F8bdcBC235e206794Ac8": "LidoExecutionLayerRewardsVault", - "0xc7cc160b58F8Bb0baC94b80847E2CF2800565C50": "OssifiableProxy", - "0xFF72B5cdc701E9eE677966B2702c766c38F412a4": "WithdrawalQueueERC721", - "0xd517d9d04DA9B47dA23df91261bd3bF435BE964A": "WithdrawalVault", - "0x4E46BD7147ccf666E1d73A3A456fC7a68de82eCA": "Burner", - "0x4E97A3972ce8511D87F334dA17a2C332542a5246": "OssifiableProxy", - "0x6AcA050709469F1f98d8f40f68b1C83B533cd2b2": "AccountingOracle", - "0xa067FC95c22D51c3bC35fd4BE37414Ee8cc890d2": "HashConsensus", - "0xffDDF7025410412deaa05E3E1cE68FE53208afcb": "OssifiableProxy", - "0x210f60EC8A4D020b3e22f15fee2d2364e9b22357": "ValidatorsExitBusOracle", - "0xe77Cf1A027d7C10Ee6bb7Ede5E922a181FF40E8f": "HashConsensus", - "0xF0d576c7d934bBeCc68FE15F1c5DAF98ea2B78bb": "OracleReportSanityChecker", - "0x072f72BE3AcFE2c52715829F2CD9061A6C8fF019": "AppProxyUpgradeable", - "0xcE4B3D5bd6259F5dD73253c51b17e5a87bb9Ee64": "LegacyOracle", - "0x14ae7daeecdf57034f3E9db8564e46Dba8D97344": "MiniMeToken", - "0xdA7d2573Df555002503F29aA4003e398d28cc00f": "AppProxyUpgradeable", - "0x994c92228803e8b2D0fb8a610AbCB47412EeF8eF": "Voting", - "0xFaa1692c6eea8eeF534e7819749aD93a1420379A": "AppProxyUpgradeable", - "0x6f0b994E6827faC1fDb58AF66f365676247bAD71": "TokenManager", - "0xf0F281E5d7FBc54EAFcE0dA225CDbde04173AB16": "AppProxyUpgradeable", - "0x1a76ED38B14C768e02b96A879d89Db18AC83EC53": "Finance", - "0xE92329EC7ddB11D25e25b3c21eeBf11f15eB325d": "AppProxyUpgradeable", - "0xF4aDA7Ff34c508B9Af2dE4160B6078D2b58FD46B": "Agent", - "0xfd1E42595CeC3E83239bf8dFc535250e7F48E0bC": "AppProxyUpgradeable", - "0xF1A087E055EA1C11ec3B540795Bd1A544e6dcbe9": "ACL", - "0xC01fC1F2787687Bc656EAc0356ba9Db6e6b7afb7": "OracleDaemonConfig", - "0xA37fb4C41e7D30af5172618a863BBB0f9042c604": "AppProxyUpgradeable", - "0x8959360c48D601a6817BAf2449E5D00cC543FA3A": "Repo", - "0xB3d74c319C0C792522705fFD3097f873eEc71764": "AppProxyUpgradeable", - "0x4E8970d148CB38460bE9b6ddaab20aE2A74879AF": "AppProxyUpgradeable", - "0x3b03f75Ec541Ca11a223bB58621A3146246E1644": "KernelProxy", - "0x34c0cbf9836FD945423bD3d2d72880da9d068E5F": "Kernel", - "0xAa8B4F258a4817bfb0058b861447878168ddf7B0": "CallsScript", - "0xE1200ae048163B67D69Bc0492bF5FddC3a2899C0": "AppProxyPinned", - "0x923B9Cab88E4a1d3de7EE921dEFBF9e2AC6e0791": "EVMScriptRegistry", - "0xB576A85c310CC7Af5C106ab26d2942fA3a5ea94A": "AppProxyUpgradeable", - "0x3EcF7190312F50043DB0494bA0389135Fc3833F3": "APMRegistry", - "0x54eF0022cc769344D0cBCeF12e051281cCBb9fad": "APMRegistryFactory", - "0xe7b4567913AaF2bD54A26E742cec22727D8109eA": "AppProxyUpgradeable", - "0x0df65b7c78Dc42a872010d031D3601C284D8fE71": "AppProxyUpgradeable", - "0xD327b4Fb87fa01599DaD491Aa63B333c44C74472": "AppProxyUpgradeable", - "0x2997EA0D07D79038D83Cb04b3BB9A2Bc512E3fDA": "AppProxyUpgradeable" + "contracts": { + "0x28FAB2059C713A7F9D8c86Db49f9bb0e96Af1ef8": "OssifiableProxy", + "0xDba5Ad530425bb1b14EECD76F1b4a517780de537": "LidoLocator", + "0x3F1c547b21f65e10480dE3ad8E19fAAC46C95034": "AppProxyUpgradeable", + "0x59034815464d18134A55EED3702b535D8A32c52b": "Lido", + "0x8d09a4502Cc8Cf1547aD300E066060D043f6982D": "WstETH", + "0xE154732c5Eab277fd88a9fF6Bdff7805eD97BCB1": "EIP712StETH", + "0xd6EbF043D30A7fe46D1Db32BA90a0A51207FE229": "OssifiableProxy", + "0x32f236423928c2c138F46351D9E5FD26331B1aa4": "StakingRouter", + "0x595F64Ddc3856a3b5Ff4f4CC1d1fb4B46cFd2bAC": "AppProxyUpgradeable", + "0xE0270CF2564d81E02284e16539F59C1B5a4718fE": "NodeOperatorsRegistry", + "0x045dd46212A178428c088573A7d102B9d89a022A": "DepositSecurityModule", + "0xE73a3602b99f1f913e72F8bdcBC235e206794Ac8": "LidoExecutionLayerRewardsVault", + "0xc7cc160b58F8Bb0baC94b80847E2CF2800565C50": "OssifiableProxy", + "0xFF72B5cdc701E9eE677966B2702c766c38F412a4": "WithdrawalQueueERC721", + "0xd517d9d04DA9B47dA23df91261bd3bF435BE964A": "WithdrawalVault", + "0x4E46BD7147ccf666E1d73A3A456fC7a68de82eCA": "Burner", + "0x4E97A3972ce8511D87F334dA17a2C332542a5246": "OssifiableProxy", + "0x6AcA050709469F1f98d8f40f68b1C83B533cd2b2": "AccountingOracle", + "0xa067FC95c22D51c3bC35fd4BE37414Ee8cc890d2": "HashConsensus", + "0xffDDF7025410412deaa05E3E1cE68FE53208afcb": "OssifiableProxy", + "0x210f60EC8A4D020b3e22f15fee2d2364e9b22357": "ValidatorsExitBusOracle", + "0xe77Cf1A027d7C10Ee6bb7Ede5E922a181FF40E8f": "HashConsensus", + "0xF0d576c7d934bBeCc68FE15F1c5DAF98ea2B78bb": "OracleReportSanityChecker", + "0x072f72BE3AcFE2c52715829F2CD9061A6C8fF019": "AppProxyUpgradeable", + "0xcE4B3D5bd6259F5dD73253c51b17e5a87bb9Ee64": "LegacyOracle", + "0x14ae7daeecdf57034f3E9db8564e46Dba8D97344": "MiniMeToken", + "0xdA7d2573Df555002503F29aA4003e398d28cc00f": "AppProxyUpgradeable", + "0x994c92228803e8b2D0fb8a610AbCB47412EeF8eF": "Voting", + "0xFaa1692c6eea8eeF534e7819749aD93a1420379A": "AppProxyUpgradeable", + "0x6f0b994E6827faC1fDb58AF66f365676247bAD71": "TokenManager", + "0xf0F281E5d7FBc54EAFcE0dA225CDbde04173AB16": "AppProxyUpgradeable", + "0x1a76ED38B14C768e02b96A879d89Db18AC83EC53": "Finance", + "0xE92329EC7ddB11D25e25b3c21eeBf11f15eB325d": "AppProxyUpgradeable", + "0xF4aDA7Ff34c508B9Af2dE4160B6078D2b58FD46B": "Agent", + "0xfd1E42595CeC3E83239bf8dFc535250e7F48E0bC": "AppProxyUpgradeable", + "0xF1A087E055EA1C11ec3B540795Bd1A544e6dcbe9": "ACL", + "0xC01fC1F2787687Bc656EAc0356ba9Db6e6b7afb7": "OracleDaemonConfig", + "0xA37fb4C41e7D30af5172618a863BBB0f9042c604": "AppProxyUpgradeable", + "0x8959360c48D601a6817BAf2449E5D00cC543FA3A": "Repo", + "0xB3d74c319C0C792522705fFD3097f873eEc71764": "AppProxyUpgradeable", + "0x4E8970d148CB38460bE9b6ddaab20aE2A74879AF": "AppProxyUpgradeable", + "0x3b03f75Ec541Ca11a223bB58621A3146246E1644": "KernelProxy", + "0x34c0cbf9836FD945423bD3d2d72880da9d068E5F": "Kernel", + "0xAa8B4F258a4817bfb0058b861447878168ddf7B0": "CallsScript", + "0xE1200ae048163B67D69Bc0492bF5FddC3a2899C0": "AppProxyPinned", + "0x923B9Cab88E4a1d3de7EE921dEFBF9e2AC6e0791": "EVMScriptRegistry", + "0xB576A85c310CC7Af5C106ab26d2942fA3a5ea94A": "AppProxyUpgradeable", + "0x3EcF7190312F50043DB0494bA0389135Fc3833F3": "APMRegistry", + "0x54eF0022cc769344D0cBCeF12e051281cCBb9fad": "APMRegistryFactory", + "0xe7b4567913AaF2bD54A26E742cec22727D8109eA": "AppProxyUpgradeable", + "0x0df65b7c78Dc42a872010d031D3601C284D8fE71": "AppProxyUpgradeable", + "0xD327b4Fb87fa01599DaD491Aa63B333c44C74472": "AppProxyUpgradeable", + "0x2997EA0D07D79038D83Cb04b3BB9A2Bc512E3fDA": "AppProxyUpgradeable" + }, + "explorer_hostname": "api-holesky.etherscan.io", + "explorer_token_env_var": "ETHERSCAN_TOKEN", + "github_repo": { + "url": "https://github.com/lidofinance/lido-dao", + "commit": "cadffa46a2b8ed6cfa1127fca2468bae1a82d6bf", + "relative_root": "" + }, + "dependencies": { + "@aragon/os": { + "url": "https://github.com/aragon/aragonOS", + "commit": "f3ae59b00f73984e562df00129c925339cd069ff", + "relative_root": "" + }, + "@aragon/minime": { + "url": "https://github.com/aragon/minime", + "commit": "1d5251fc88eee5024ff318d95bc9f4c5de130430", + "relative_root": "" + }, + "@aragon/apps-lido": { + "url": "https://github.com/lidofinance/aragon-apps/", + "commit": "b09834d29c0db211ddd50f50905cbeff257fc8e0", + "relative_root": "" + }, + "@aragon/apps-finance": { + "url": "https://github.com/lidofinance/aragon-apps/", + "commit": "b09834d29c0db211ddd50f50905cbeff257fc8e0", + "relative_root": "apps/finance" + }, + "@aragon/apps-vault": { + "url": "https://github.com/lidofinance/aragon-apps/", + "commit": "b09834d29c0db211ddd50f50905cbeff257fc8e0", + "relative_root": "apps/vault" }, - "explorer_hostname": "api-holesky.etherscan.io", - "explorer_token_env_var": "ETHERSCAN_TOKEN", - "github_repo": { - "url": "https://github.com/lidofinance/lido-dao", - "commit": "cadffa46a2b8ed6cfa1127fca2468bae1a82d6bf", - "relative_root": "" + "@aragon/apps-agent": { + "url": "https://github.com/lidofinance/aragon-apps/", + "commit": "b09834d29c0db211ddd50f50905cbeff257fc8e0", + "relative_root": "apps/agent" }, - "dependencies": { - "@aragon/os": { - "url": "https://github.com/aragon/aragonOS", - "commit": "f3ae59b00f73984e562df00129c925339cd069ff", - "relative_root": "" - }, - "@aragon/minime": { - "url": "https://github.com/aragon/minime", - "commit": "1d5251fc88eee5024ff318d95bc9f4c5de130430", - "relative_root": "" - }, - "@aragon/apps-lido": { - "url": "https://github.com/lidofinance/aragon-apps/", - "commit": "b09834d29c0db211ddd50f50905cbeff257fc8e0", - "relative_root": "" - }, - "@aragon/apps-finance": { - "url": "https://github.com/lidofinance/aragon-apps/", - "commit": "b09834d29c0db211ddd50f50905cbeff257fc8e0", - "relative_root": "apps/finance" - }, - "@aragon/apps-vault": { - "url": "https://github.com/lidofinance/aragon-apps/", - "commit": "b09834d29c0db211ddd50f50905cbeff257fc8e0", - "relative_root": "apps/vault" - }, - "@aragon/apps-agent": { - "url": "https://github.com/lidofinance/aragon-apps/", - "commit": "b09834d29c0db211ddd50f50905cbeff257fc8e0", - "relative_root": "apps/agent" - }, - "openzeppelin-solidity": { - "url": "https://github.com/OpenZeppelin/openzeppelin-contracts", - "commit": "06e265b38d3e9daeaa7b33f9035c700d6bc0c6a0", - "relative_root": "" - }, - "solidity-bytes-utils": { - "url": "https://github.com/GNSPS/solidity-bytes-utils", - "commit": "9776282d181839fbb4b18f2cf218e316d6df871c", - "relative_root": "" - }, - "@openzeppelin/contracts": { - "url": "https://github.com/OpenZeppelin/openzeppelin-contracts", - "commit": "fa64a1ced0b70ab89073d5d0b6e01b0778f7e7d6", - "relative_root": "contracts" - }, - "@openzeppelin/contracts-v4.4": { - "url": "https://github.com/OpenZeppelin/openzeppelin-contracts", - "commit": "6bd6b76d1156e20e45d1016f355d154141c7e5b9", - "relative_root": "contracts" - } + "openzeppelin-solidity": { + "url": "https://github.com/OpenZeppelin/openzeppelin-contracts", + "commit": "06e265b38d3e9daeaa7b33f9035c700d6bc0c6a0", + "relative_root": "" }, - "bytecode_comparison": { - "enable": true, - "skip_deploy_error" : false, - "hardhat_config_name": "holesky_hardhat.config.js", - "local_RPC_URL": "http://127.0.0.1:7545", - "remote_RPC_URL":"https://ethereum-holesky.blockpi.network/v1/rpc/public", - "constructor_calldata": { - "0x28FAB2059C713A7F9D8c86Db49f9bb0e96Af1ef8" : "000000000000000000000000ab89ed3d8f31bcf8bb7de53f02084d1e6f043d34000000000000000000000000e92329ec7ddb11d25e25b3c21eebf11f15eb325d00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" + "solidity-bytes-utils": { + "url": "https://github.com/GNSPS/solidity-bytes-utils", + "commit": "9776282d181839fbb4b18f2cf218e316d6df871c", + "relative_root": "" + }, + "@openzeppelin/contracts": { + "url": "https://github.com/OpenZeppelin/openzeppelin-contracts", + "commit": "fa64a1ced0b70ab89073d5d0b6e01b0778f7e7d6", + "relative_root": "contracts" + }, + "@openzeppelin/contracts-v4.4": { + "url": "https://github.com/OpenZeppelin/openzeppelin-contracts", + "commit": "6bd6b76d1156e20e45d1016f355d154141c7e5b9", + "relative_root": "contracts" + } }, - "constructor_args": { - "0x28FAB2059C713A7F9D8c86Db49f9bb0e96Af1ef8" : ["0xab89ED3D8f31bcF8BB7De53F02084d1e6F043D34", "0xE92329EC7ddB11D25e25b3c21eeBf11f15eB325d",""], - "0xF0d576c7d934bBeCc68FE15F1c5DAF98ea2B78bb" : [ - "0x28FAB2059C713A7F9D8c86Db49f9bb0e96Af1ef8", - "0x22896Bfc68814BFD855b1a167255eE497006e730", - [1500,500,1000,250,2000,100,100,128,5000000], - [[],[],[],[],[],[],[],[],[],[]] - ], - "0xDba5Ad530425bb1b14EECD76F1b4a517780de537" : [ - ["0x4E97A3972ce8511D87F334dA17a2C332542a5246", - "0x045dd46212A178428c088573A7d102B9d89a022A", - "0xE73a3602b99f1f913e72F8bdcBC235e206794Ac8", - "0x072f72BE3AcFE2c52715829F2CD9061A6C8fF019", - "0x3F1c547b21f65e10480dE3ad8E19fAAC46C95034", - "0xF0d576c7d934bBeCc68FE15F1c5DAF98ea2B78bb", - "0x072f72BE3AcFE2c52715829F2CD9061A6C8fF019", - "0x4E46BD7147ccf666E1d73A3A456fC7a68de82eCA", - "0xd6EbF043D30A7fe46D1Db32BA90a0A51207FE229", - "0xE92329EC7ddB11D25e25b3c21eeBf11f15eB325d", - "0xffDDF7025410412deaa05E3E1cE68FE53208afcb", - "0xc7cc160b58F8Bb0baC94b80847E2CF2800565C50", - "0xF0179dEC45a37423EAD4FaD5fCb136197872EAd9", - "0xC01fC1F2787687Bc656EAc0356ba9Db6e6b7afb7" - ] - ] + "bytecode_comparison": { + "enable": true, + "skip_deploy_error": false, + "hardhat_config_name": "holesky_hardhat.config.js", + "local_RPC_URL": "http://127.0.0.1:7545", + "remote_RPC_URL": "https://ethereum-holesky.blockpi.network/v1/rpc/public", + "constructor_calldata": { + "0x28FAB2059C713A7F9D8c86Db49f9bb0e96Af1ef8": "000000000000000000000000ab89ed3d8f31bcf8bb7de53f02084d1e6f043d34000000000000000000000000e92329ec7ddb11d25e25b3c21eebf11f15eb325d00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" + }, + "constructor_args": { + "0x28FAB2059C713A7F9D8c86Db49f9bb0e96Af1ef8": [ + "0xab89ED3D8f31bcF8BB7De53F02084d1e6F043D34", + "0xE92329EC7ddB11D25e25b3c21eeBf11f15eB325d", + "" + ], + "0xF0d576c7d934bBeCc68FE15F1c5DAF98ea2B78bb": [ + "0x28FAB2059C713A7F9D8c86Db49f9bb0e96Af1ef8", + "0x22896Bfc68814BFD855b1a167255eE497006e730", + [ + 1500, + 500, + 1000, + 250, + 2000, + 100, + 100, + 128, + 5000000 + ], + [ + [], + [], + [], + [], + [], + [], + [], + [], + [], + [] + ] + ], + "0xDba5Ad530425bb1b14EECD76F1b4a517780de537": [ + [ + "0x4E97A3972ce8511D87F334dA17a2C332542a5246", + "0x045dd46212A178428c088573A7d102B9d89a022A", + "0xE73a3602b99f1f913e72F8bdcBC235e206794Ac8", + "0x072f72BE3AcFE2c52715829F2CD9061A6C8fF019", + "0x3F1c547b21f65e10480dE3ad8E19fAAC46C95034", + "0xF0d576c7d934bBeCc68FE15F1c5DAF98ea2B78bb", + "0x072f72BE3AcFE2c52715829F2CD9061A6C8fF019", + "0x4E46BD7147ccf666E1d73A3A456fC7a68de82eCA", + "0xd6EbF043D30A7fe46D1Db32BA90a0A51207FE229", + "0xE92329EC7ddB11D25e25b3c21eeBf11f15eB325d", + "0xffDDF7025410412deaa05E3E1cE68FE53208afcb", + "0xc7cc160b58F8Bb0baC94b80847E2CF2800565C50", + "0xF0179dEC45a37423EAD4FaD5fCb136197872EAd9", + "0xC01fC1F2787687Bc656EAc0356ba9Db6e6b7afb7" + ] + ] } -} + } } diff --git a/config_samples/lido_dao_sepolia_config.json b/config_samples/lido_dao_sepolia_config.json index 23be4b8..672c895 100644 --- a/config_samples/lido_dao_sepolia_config.json +++ b/config_samples/lido_dao_sepolia_config.json @@ -1,163 +1,300 @@ { - "contracts": { - "0x8f6254332f69557A72b0DA2D5F0Bc07d4CA991E7": "OssifiableProxy", - "0x604dc1776eEbe7ddCf4cf5429226Ad20a5a294eE": "LidoLocator", - "0x3e3FE7dBc6B4C189E7128855dD526361c49b40Af": "AppProxyUpgradeable", - "0x3e7e93bA66d26608c2Ffe1630F445D8D29aC6C92": "Lido", - "0xB82381A3fBD3FaFA77B3a7bE693342618240067b": "WstETH", - "0x9726CA9AEFF4BC8FB8C084BdAbdB71608248E3f8": "EIP712StETH", - "0x4F36aAEb18Ab56A4e380241bea6ebF215b9cb12c": "OssifiableProxy", - "0x46cF57508B0565decC0419B833C2dAFa50B132e0": "StakingRouter", - "0x33d6E15047E8644F8DDf5CD05d202dfE587DA6E3": "AppProxyUpgradeable", - "0xC3704E12c34D929e082257B793c8F114b7Bc7546": "NodeOperatorsRegistry", - "0x94B1B8e2680882f8652882e7F196169dE3d9a3B2": "LidoExecutionLayerRewardsVault", - "0x1583C7b3f4C3B008720E6BcE5726336b0aB25fdd": "OssifiableProxy", - "0x0220A1cF6C3a548BE75aEabCdA509CaB08CDe063": "WithdrawalQueueERC721", - "0xee386d787Db24AbEe4dcc591F35405E323b70Dad": "WithdrawalVault", - "0x61Bb0Ef69262d5EF1cc2873cf61766751D99B699": "Burner", - "0xd497Be005638efCf09F6BFC8DAFBBB0BB72cD991": "OssifiableProxy", - "0x082d16150BF75BB8F2197eEC1d293DbA96c93638": "AccountingOracle", - "0x758D8c3CE794b3Dfe3b3A3482B7eD33de2109D95": "HashConsensus", - "0x7637d44c9f2e9cA584a8B5D2EA493012A5cdaEB6": "OssifiableProxy", - "0xC40801b88C835a58e54eEE6679D301ba31a4C72b": "ValidatorsExitBusOracle", - "0x098a952BD200005382aEb3229e38ae39A7616F56": "HashConsensus", - "0xbac2A471443F18aC5C31078b96C5797A78fCc680": "OracleReportSanityChecker", - "0x7bC76076b0f3879b4A750450C0Ccf02c6Ca11220": "OracleDaemonConfig", - "0x3483c140EF7F2716460198Ff831a8e53F05F1606": "AppProxyUpgradeable", - "0x3F3609d02779Ef8C38AA93c4f5E1b9a44169a6Da": "LegacyOracle", - "0x6155bD199ECcc79Ff4e8B392f6cBD9c9874E8916": "KernelProxy", - "0x1c2807B207f140a1DE0b39E5546eDEf67Af2568c": "Kernel", - "0xd06dF83b8ad6D89C86a187fba4Eae918d497BdCB": "MiniMeToken", - "0x39A0EbdEE54cB319f4F42141daaBDb6ba25D341A": "AppProxyUpgradeable", - "0x9a4384837a49F1CAbfC1daBC4Df268E111864d77": "Voting", - "0xC73cd4B2A7c1CBC5BF046eB4A7019365558ABF66": "AppProxyUpgradeable", - "0x05766b9Ea781343721261BC35E037223438033c2": "TokenManager", - "0x52AD3004Bc993d63931142Dd4f3DD647414048a1": "AppProxyUpgradeable", - "0x6a2ae4361D305c2A87F932555A067612578CE499": "Finance", - "0x32A0E5828B62AAb932362a4816ae03b860b65e83": "AppProxyUpgradeable", - "0xdd4F0f62878022Bc61d5D7FDCFEe66aceadBc234": "Agent", - "0x8A1AA86d35b2EE8C9369618E7D7b40000cCD3295": "AppProxyUpgradeable", - "0x2dfF8E2c55959f719Faa6dCC4a3B0937bcA71F73": "ACL" + "contracts": { + "0x8f6254332f69557A72b0DA2D5F0Bc07d4CA991E7": "OssifiableProxy", + "0x604dc1776eEbe7ddCf4cf5429226Ad20a5a294eE": "LidoLocator", + "0x3e3FE7dBc6B4C189E7128855dD526361c49b40Af": "AppProxyUpgradeable", + "0x3e7e93bA66d26608c2Ffe1630F445D8D29aC6C92": "Lido", + "0xB82381A3fBD3FaFA77B3a7bE693342618240067b": "WstETH", + "0x9726CA9AEFF4BC8FB8C084BdAbdB71608248E3f8": "EIP712StETH", + "0x4F36aAEb18Ab56A4e380241bea6ebF215b9cb12c": "OssifiableProxy", + "0x46cF57508B0565decC0419B833C2dAFa50B132e0": "StakingRouter", + "0x33d6E15047E8644F8DDf5CD05d202dfE587DA6E3": "AppProxyUpgradeable", + "0xC3704E12c34D929e082257B793c8F114b7Bc7546": "NodeOperatorsRegistry", + "0x94B1B8e2680882f8652882e7F196169dE3d9a3B2": "LidoExecutionLayerRewardsVault", + "0x1583C7b3f4C3B008720E6BcE5726336b0aB25fdd": "OssifiableProxy", + "0x0220A1cF6C3a548BE75aEabCdA509CaB08CDe063": "WithdrawalQueueERC721", + "0xee386d787Db24AbEe4dcc591F35405E323b70Dad": "WithdrawalVault", + "0x61Bb0Ef69262d5EF1cc2873cf61766751D99B699": "Burner", + "0xd497Be005638efCf09F6BFC8DAFBBB0BB72cD991": "OssifiableProxy", + "0x082d16150BF75BB8F2197eEC1d293DbA96c93638": "AccountingOracle", + "0x758D8c3CE794b3Dfe3b3A3482B7eD33de2109D95": "HashConsensus", + "0x7637d44c9f2e9cA584a8B5D2EA493012A5cdaEB6": "OssifiableProxy", + "0xC40801b88C835a58e54eEE6679D301ba31a4C72b": "ValidatorsExitBusOracle", + "0x098a952BD200005382aEb3229e38ae39A7616F56": "HashConsensus", + "0xbac2A471443F18aC5C31078b96C5797A78fCc680": "OracleReportSanityChecker", + "0x7bC76076b0f3879b4A750450C0Ccf02c6Ca11220": "OracleDaemonConfig", + "0x3483c140EF7F2716460198Ff831a8e53F05F1606": "AppProxyUpgradeable", + "0x3F3609d02779Ef8C38AA93c4f5E1b9a44169a6Da": "LegacyOracle", + "0x6155bD199ECcc79Ff4e8B392f6cBD9c9874E8916": "KernelProxy", + "0x1c2807B207f140a1DE0b39E5546eDEf67Af2568c": "Kernel", + "0xd06dF83b8ad6D89C86a187fba4Eae918d497BdCB": "MiniMeToken", + "0x39A0EbdEE54cB319f4F42141daaBDb6ba25D341A": "AppProxyUpgradeable", + "0x9a4384837a49F1CAbfC1daBC4Df268E111864d77": "Voting", + "0xC73cd4B2A7c1CBC5BF046eB4A7019365558ABF66": "AppProxyUpgradeable", + "0x05766b9Ea781343721261BC35E037223438033c2": "TokenManager", + "0x52AD3004Bc993d63931142Dd4f3DD647414048a1": "AppProxyUpgradeable", + "0x6a2ae4361D305c2A87F932555A067612578CE499": "Finance", + "0x32A0E5828B62AAb932362a4816ae03b860b65e83": "AppProxyUpgradeable", + "0xdd4F0f62878022Bc61d5D7FDCFEe66aceadBc234": "Agent", + "0x8A1AA86d35b2EE8C9369618E7D7b40000cCD3295": "AppProxyUpgradeable", + "0x2dfF8E2c55959f719Faa6dCC4a3B0937bcA71F73": "ACL" + }, + "explorer_token_env_var": "ETHERSCAN_TOKEN", + "explorer_hostname": "api-sepolia.etherscan.io", + "github_repo": { + "url": "https://github.com/lidofinance/lido-dao", + "commit": "cadffa46a2b8ed6cfa1127fca2468bae1a82d6bf", + "relative_root": "" + }, + "dependencies": { + "@aragon/os": { + "url": "https://github.com/aragon/aragonOS", + "commit": "f3ae59b00f73984e562df00129c925339cd069ff", + "relative_root": "" + }, + "@aragon/minime": { + "url": "https://github.com/aragon/minime", + "commit": "1d5251fc88eee5024ff318d95bc9f4c5de130430", + "relative_root": "" + }, + "@aragon/apps-lido": { + "url": "https://github.com/lidofinance/aragon-apps/", + "commit": "b09834d29c0db211ddd50f50905cbeff257fc8e0", + "relative_root": "" + }, + "@aragon/apps-finance": { + "url": "https://github.com/lidofinance/aragon-apps/", + "commit": "b09834d29c0db211ddd50f50905cbeff257fc8e0", + "relative_root": "apps/finance" }, - "explorer_token_env_var": "ETHERSCAN_TOKEN", - "explorer_hostname": "api-sepolia.etherscan.io", - "github_repo": { - "url": "https://github.com/lidofinance/lido-dao", - "commit": "cadffa46a2b8ed6cfa1127fca2468bae1a82d6bf", - "relative_root": "" + "@aragon/apps-vault": { + "url": "https://github.com/lidofinance/aragon-apps/", + "commit": "b09834d29c0db211ddd50f50905cbeff257fc8e0", + "relative_root": "apps/vault" }, - "dependencies": { - "@aragon/os": { - "url": "https://github.com/aragon/aragonOS", - "commit": "f3ae59b00f73984e562df00129c925339cd069ff", - "relative_root": "" - }, - "@aragon/minime": { - "url": "https://github.com/aragon/minime", - "commit": "1d5251fc88eee5024ff318d95bc9f4c5de130430", - "relative_root": "" - }, - "@aragon/apps-lido": { - "url": "https://github.com/lidofinance/aragon-apps/", - "commit": "b09834d29c0db211ddd50f50905cbeff257fc8e0", - "relative_root": "" - }, - "@aragon/apps-finance": { - "url": "https://github.com/lidofinance/aragon-apps/", - "commit": "b09834d29c0db211ddd50f50905cbeff257fc8e0", - "relative_root": "apps/finance" - }, - "@aragon/apps-vault": { - "url": "https://github.com/lidofinance/aragon-apps/", - "commit": "b09834d29c0db211ddd50f50905cbeff257fc8e0", - "relative_root": "apps/vault" - }, - "@aragon/apps-agent": { - "url": "https://github.com/lidofinance/aragon-apps/", - "commit": "b09834d29c0db211ddd50f50905cbeff257fc8e0", - "relative_root": "apps/agent" - }, - "openzeppelin-solidity": { - "url": "https://github.com/OpenZeppelin/openzeppelin-contracts", - "commit": "06e265b38d3e9daeaa7b33f9035c700d6bc0c6a0", - "relative_root": "" - }, - "solidity-bytes-utils": { - "url": "https://github.com/GNSPS/solidity-bytes-utils", - "commit": "9776282d181839fbb4b18f2cf218e316d6df871c", - "relative_root": "" - }, - "@openzeppelin/contracts": { - "url": "https://github.com/OpenZeppelin/openzeppelin-contracts", - "commit": "fa64a1ced0b70ab89073d5d0b6e01b0778f7e7d6", - "relative_root": "contracts" - }, - "@openzeppelin/contracts-v4.4": { - "url": "https://github.com/OpenZeppelin/openzeppelin-contracts", - "commit": "6bd6b76d1156e20e45d1016f355d154141c7e5b9", - "relative_root": "contracts" - } + "@aragon/apps-agent": { + "url": "https://github.com/lidofinance/aragon-apps/", + "commit": "b09834d29c0db211ddd50f50905cbeff257fc8e0", + "relative_root": "apps/agent" }, - "bytecode_comparison": { - "skip_deploy_error" : false, - "hardhat_config_name": "sepolia_hardhat.config.js", - "local_RPC_URL": "http://127.0.0.1:7545", - "remote_RPC_URL":"https://sepolia.drpc.org", - "constructor_calldata": { - "0x8f6254332f69557A72b0DA2D5F0Bc07d4CA991E7" : "000000000000000000000000f30a674935479cc6f2254ba65d7534eab8bd6ba20000000000000000000000006885e36bfcb68cb383dfe90023a462c03bcb2ae500000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" + "openzeppelin-solidity": { + "url": "https://github.com/OpenZeppelin/openzeppelin-contracts", + "commit": "06e265b38d3e9daeaa7b33f9035c700d6bc0c6a0", + "relative_root": "" + }, + "solidity-bytes-utils": { + "url": "https://github.com/GNSPS/solidity-bytes-utils", + "commit": "9776282d181839fbb4b18f2cf218e316d6df871c", + "relative_root": "" + }, + "@openzeppelin/contracts": { + "url": "https://github.com/OpenZeppelin/openzeppelin-contracts", + "commit": "fa64a1ced0b70ab89073d5d0b6e01b0778f7e7d6", + "relative_root": "contracts" + }, + "@openzeppelin/contracts-v4.4": { + "url": "https://github.com/OpenZeppelin/openzeppelin-contracts", + "commit": "6bd6b76d1156e20e45d1016f355d154141c7e5b9", + "relative_root": "contracts" + } }, - "constructor_args": { - "0x8f6254332f69557A72b0DA2D5F0Bc07d4CA991E7" : ["0xf30a674935479cc6f2254ba65d7534eab8bd6ba2","0x6885E36BFcb68CB383DfE90023a462C03BCB2AE5",""], - "0x9726CA9AEFF4BC8FB8C084BdAbdB71608248E3f8" : ["0x3e3FE7dBc6B4C189E7128855dD526361c49b40Af"], - "0x604dc1776eEbe7ddCf4cf5429226Ad20a5a294eE" : [ - ["0xd497be005638efcf09f6bfc8dafbbb0bb72cd991", - "0x6885e36bfcb68cb383dfe90023a462c03bcb2ae5", - "0x94b1b8e2680882f8652882e7f196169de3d9a3b2", - "0x3483c140ef7f2716460198ff831a8e53f05f1606", - "0x3e3fe7dbc6b4c189e7128855dd526361c49b40af", - "0xbac2a471443f18ac5c31078b96c5797a78fcc680", - "0x3483c140ef7f2716460198ff831a8e53f05f1606", - "0x61bb0ef69262d5ef1cc2873cf61766751d99b699", - "0x4f36aaeb18ab56a4e380241bea6ebf215b9cb12c", - "0x32a0e5828b62aab932362a4816ae03b860b65e83", - "0x7637d44c9f2e9ca584a8b5d2ea493012a5cdaeb6", - "0x1583c7b3f4c3b008720e6bce5726336b0ab25fdd", - "0xde7318afa67ead6d6bbc8224dfce5ed6e4b86d76", - "0x7bc76076b0f3879b4a750450c0ccf02c6ca11220"] - ], - "0xB82381A3fBD3FaFA77B3a7bE693342618240067b" : ["0x3e3FE7dBc6B4C189E7128855dD526361c49b40Af"], - "0x46cF57508B0565decC0419B833C2dAFa50B132e0" : ["0x80b5DC88C98E528bF9cb4B7F0f076aC41da24651"], - "0x94B1B8e2680882f8652882e7F196169dE3d9a3B2" : ["0x3e3FE7dBc6B4C189E7128855dD526361c49b40Af", "0x32A0E5828B62AAb932362a4816ae03b860b65e83"], - "0x0220A1cF6C3a548BE75aEabCdA509CaB08CDe063" : [ "0xB82381A3fBD3FaFA77B3a7bE693342618240067b", "Lido: stETH Withdrawal NFT", "unstETH"], - "0xee386d787Db24AbEe4dcc591F35405E323b70Dad" : ["0x3e3FE7dBc6B4C189E7128855dD526361c49b40Af","0x32A0E5828B62AAb932362a4816ae03b860b65e83"], - "0x61Bb0Ef69262d5EF1cc2873cf61766751D99B699" : ["0x6885E36BFcb68CB383DfE90023a462C03BCB2AE5", "0x32A0E5828B62AAb932362a4816ae03b860b65e83", "0x3e3FE7dBc6B4C189E7128855dD526361c49b40Af", 0, 0], - "0x082d16150BF75BB8F2197eEC1d293DbA96c93638" : ["0x8f6254332f69557A72b0DA2D5F0Bc07d4CA991E7","0x3e3FE7dBc6B4C189E7128855dD526361c49b40Af", "0x3483c140EF7F2716460198Ff831a8e53F05F1606", 12, 1655733600], - "0x1c2807B207f140a1DE0b39E5546eDEf67Af2568c" : [1], - "0xd06dF83b8ad6D89C86a187fba4Eae918d497BdCB" : ["0x9D381f44d1fbdf8190fA0EcdC028e2Af24DdD3FE","0x0000000000000000000000000000000000000000",0, "TEST Lido DAO Token", 18, "TLDO", 1], - "0x758D8c3CE794b3Dfe3b3A3482B7eD33de2109D95" : [32, 12, 1655733600, 12, 10, "0x6885E36BFcb68CB383DfE90023a462C03BCB2AE5", "0xd497Be005638efCf09F6BFC8DAFBBB0BB72cD991"], - "0xC40801b88C835a58e54eEE6679D301ba31a4C72b" : [12, 1655733600, "0x8f6254332f69557A72b0DA2D5F0Bc07d4CA991E7"], - "0xbac2A471443F18aC5C31078b96C5797A78fCc680" : [ - "0x28FAB2059C713A7F9D8c86Db49f9bb0e96Af1ef8", - "0x22896Bfc68814BFD855b1a167255eE497006e730", - [1500,500,1000,250,2000,100,100,128,5000000], - [[],[],[],[],[],[],[],[],[],[]] - ], - "0x7bC76076b0f3879b4A750450C0Ccf02c6Ca11220" : ["0x6885E36BFcb68CB383DfE90023a462C03BCB2AE5",""], - "0x6155bD199ECcc79Ff4e8B392f6cBD9c9874E8916" : ["0x1c2807B207f140a1DE0b39E5546eDEf67Af2568c"], - "0xC73cd4B2A7c1CBC5BF046eB4A7019365558ABF66" : ["0x6155bD199ECcc79Ff4e8B392f6cBD9c9874E8916", "0xcd567bdf93dd0f6acc3bc7f2155f83244d56a65abbfbefb763e015420102c67b", ""], - "0x52AD3004Bc993d63931142Dd4f3DD647414048a1" : ["0x6155bD199ECcc79Ff4e8B392f6cBD9c9874E8916","0x5c9918c99c4081ca9459c178381be71d9da40e49e151687da55099c49a4237f1", ""], - "0x098a952BD200005382aEb3229e38ae39A7616F56" : [32,12,1655733600, 75, 10, "0x7FAcEF1c7248ed171FDd9ea3B25B4550b38e6133", "0x7637d44c9f2e9cA584a8B5D2EA493012A5cdaEB6"], - "0x1583C7b3f4C3B008720E6BcE5726336b0aB25fdd" : ["0x0220A1cF6C3a548BE75aEabCdA509CaB08CDe063", "0x32A0E5828B62AAb932362a4816ae03b860b65e83","0x0"], - "0x32A0E5828B62AAb932362a4816ae03b860b65e83" : ["0x6155bD199ECcc79Ff4e8B392f6cBD9c9874E8916","0x701a4fd1f5174d12a0f1d9ad2c88d0ad11ab6aad0ac72b7d9ce621815f8016a9","0"], - "0x33d6E15047E8644F8DDf5CD05d202dfE587DA6E3" : ["0x6155bD199ECcc79Ff4e8B392f6cBD9c9874E8916","0x7071f283424072341f856ac9e947e7ec0eb68719f757a7e785979b6b8717579d","0"], - "0x3483c140EF7F2716460198Ff831a8e53F05F1606" : ["0x6155bD199ECcc79Ff4e8B392f6cBD9c9874E8916","0x8b47ba2a8454ec799cd91646e7ec47168e91fd139b23f017455f3e5898aaba93", "0"], - "0x39A0EbdEE54cB319f4F42141daaBDb6ba25D341A" : ["0x6155bD199ECcc79Ff4e8B392f6cBD9c9874E8916","0x0abcd104777321a82b010357f20887d61247493d89d2e987ff57bcecbde00e1e", "0"], - "0x3e3FE7dBc6B4C189E7128855dD526361c49b40Af" : ["0x6155bD199ECcc79Ff4e8B392f6cBD9c9874E8916","0x3ca7c3e38968823ccb4c78ea688df41356f182ae1d159e4ee608d30d68cef320", "0"], - "0x4F36aAEb18Ab56A4e380241bea6ebF215b9cb12c" : ["0x46cF57508B0565decC0419B833C2dAFa50B132e0", "0x32A0E5828B62AAb932362a4816ae03b860b65e83", "0"], - "0x7637d44c9f2e9cA584a8B5D2EA493012A5cdaEB6" : ["0xC40801b88C835a58e54eEE6679D301ba31a4C72b", "0x32A0E5828B62AAb932362a4816ae03b860b65e83", "0"], - "0x8A1AA86d35b2EE8C9369618E7D7b40000cCD3295" : ["0x6155bD199ECcc79Ff4e8B392f6cBD9c9874E8916", "0xe3262375f45a6e2026b7e7b18c2b807434f2508fe1a2a3dfb493c7df8f4aad6a", "0"], - "0xd497Be005638efCf09F6BFC8DAFBBB0BB72cD991" : ["0x082d16150BF75BB8F2197eEC1d293DbA96c93638", "0x32A0E5828B62AAb932362a4816ae03b860b65e83", "0"] - } - } + "bytecode_comparison": { + "skip_deploy_error": false, + "hardhat_config_name": "sepolia_hardhat.config.js", + "local_RPC_URL": "http://127.0.0.1:7545", + "remote_RPC_URL": "https://sepolia.drpc.org", + "constructor_calldata": { + "0x8f6254332f69557A72b0DA2D5F0Bc07d4CA991E7": "000000000000000000000000f30a674935479cc6f2254ba65d7534eab8bd6ba20000000000000000000000006885e36bfcb68cb383dfe90023a462c03bcb2ae500000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" + }, + "constructor_args": { + "0x8f6254332f69557A72b0DA2D5F0Bc07d4CA991E7": [ + "0xf30a674935479cc6f2254ba65d7534eab8bd6ba2", + "0x6885E36BFcb68CB383DfE90023a462C03BCB2AE5", + "" + ], + "0x9726CA9AEFF4BC8FB8C084BdAbdB71608248E3f8": [ + "0x3e3FE7dBc6B4C189E7128855dD526361c49b40Af" + ], + "0x604dc1776eEbe7ddCf4cf5429226Ad20a5a294eE": [ + [ + "0xd497be005638efcf09f6bfc8dafbbb0bb72cd991", + "0x6885e36bfcb68cb383dfe90023a462c03bcb2ae5", + "0x94b1b8e2680882f8652882e7f196169de3d9a3b2", + "0x3483c140ef7f2716460198ff831a8e53f05f1606", + "0x3e3fe7dbc6b4c189e7128855dd526361c49b40af", + "0xbac2a471443f18ac5c31078b96c5797a78fcc680", + "0x3483c140ef7f2716460198ff831a8e53f05f1606", + "0x61bb0ef69262d5ef1cc2873cf61766751d99b699", + "0x4f36aaeb18ab56a4e380241bea6ebf215b9cb12c", + "0x32a0e5828b62aab932362a4816ae03b860b65e83", + "0x7637d44c9f2e9ca584a8b5d2ea493012a5cdaeb6", + "0x1583c7b3f4c3b008720e6bce5726336b0ab25fdd", + "0xde7318afa67ead6d6bbc8224dfce5ed6e4b86d76", + "0x7bc76076b0f3879b4a750450c0ccf02c6ca11220" + ] + ], + "0xB82381A3fBD3FaFA77B3a7bE693342618240067b": [ + "0x3e3FE7dBc6B4C189E7128855dD526361c49b40Af" + ], + "0x46cF57508B0565decC0419B833C2dAFa50B132e0": [ + "0x80b5DC88C98E528bF9cb4B7F0f076aC41da24651" + ], + "0x94B1B8e2680882f8652882e7F196169dE3d9a3B2": [ + "0x3e3FE7dBc6B4C189E7128855dD526361c49b40Af", + "0x32A0E5828B62AAb932362a4816ae03b860b65e83" + ], + "0x0220A1cF6C3a548BE75aEabCdA509CaB08CDe063": [ + "0xB82381A3fBD3FaFA77B3a7bE693342618240067b", + "Lido: stETH Withdrawal NFT", + "unstETH" + ], + "0xee386d787Db24AbEe4dcc591F35405E323b70Dad": [ + "0x3e3FE7dBc6B4C189E7128855dD526361c49b40Af", + "0x32A0E5828B62AAb932362a4816ae03b860b65e83" + ], + "0x61Bb0Ef69262d5EF1cc2873cf61766751D99B699": [ + "0x6885E36BFcb68CB383DfE90023a462C03BCB2AE5", + "0x32A0E5828B62AAb932362a4816ae03b860b65e83", + "0x3e3FE7dBc6B4C189E7128855dD526361c49b40Af", + 0, + 0 + ], + "0x082d16150BF75BB8F2197eEC1d293DbA96c93638": [ + "0x8f6254332f69557A72b0DA2D5F0Bc07d4CA991E7", + "0x3e3FE7dBc6B4C189E7128855dD526361c49b40Af", + "0x3483c140EF7F2716460198Ff831a8e53F05F1606", + 12, + 1655733600 + ], + "0x1c2807B207f140a1DE0b39E5546eDEf67Af2568c": [ + 1 + ], + "0xd06dF83b8ad6D89C86a187fba4Eae918d497BdCB": [ + "0x9D381f44d1fbdf8190fA0EcdC028e2Af24DdD3FE", + "0x0000000000000000000000000000000000000000", + 0, + "TEST Lido DAO Token", + 18, + "TLDO", + 1 + ], + "0x758D8c3CE794b3Dfe3b3A3482B7eD33de2109D95": [ + 32, + 12, + 1655733600, + 12, + 10, + "0x6885E36BFcb68CB383DfE90023a462C03BCB2AE5", + "0xd497Be005638efCf09F6BFC8DAFBBB0BB72cD991" + ], + "0xC40801b88C835a58e54eEE6679D301ba31a4C72b": [ + 12, + 1655733600, + "0x8f6254332f69557A72b0DA2D5F0Bc07d4CA991E7" + ], + "0xbac2A471443F18aC5C31078b96C5797A78fCc680": [ + "0x28FAB2059C713A7F9D8c86Db49f9bb0e96Af1ef8", + "0x22896Bfc68814BFD855b1a167255eE497006e730", + [ + 1500, + 500, + 1000, + 250, + 2000, + 100, + 100, + 128, + 5000000 + ], + [ + [], + [], + [], + [], + [], + [], + [], + [], + [], + [] + ] + ], + "0x7bC76076b0f3879b4A750450C0Ccf02c6Ca11220": [ + "0x6885E36BFcb68CB383DfE90023a462C03BCB2AE5", + "" + ], + "0x6155bD199ECcc79Ff4e8B392f6cBD9c9874E8916": [ + "0x1c2807B207f140a1DE0b39E5546eDEf67Af2568c" + ], + "0xC73cd4B2A7c1CBC5BF046eB4A7019365558ABF66": [ + "0x6155bD199ECcc79Ff4e8B392f6cBD9c9874E8916", + "0xcd567bdf93dd0f6acc3bc7f2155f83244d56a65abbfbefb763e015420102c67b", + "" + ], + "0x52AD3004Bc993d63931142Dd4f3DD647414048a1": [ + "0x6155bD199ECcc79Ff4e8B392f6cBD9c9874E8916", + "0x5c9918c99c4081ca9459c178381be71d9da40e49e151687da55099c49a4237f1", + "" + ], + "0x098a952BD200005382aEb3229e38ae39A7616F56": [ + 32, + 12, + 1655733600, + 75, + 10, + "0x7FAcEF1c7248ed171FDd9ea3B25B4550b38e6133", + "0x7637d44c9f2e9cA584a8B5D2EA493012A5cdaEB6" + ], + "0x1583C7b3f4C3B008720E6BcE5726336b0aB25fdd": [ + "0x0220A1cF6C3a548BE75aEabCdA509CaB08CDe063", + "0x32A0E5828B62AAb932362a4816ae03b860b65e83", + "0x0" + ], + "0x32A0E5828B62AAb932362a4816ae03b860b65e83": [ + "0x6155bD199ECcc79Ff4e8B392f6cBD9c9874E8916", + "0x701a4fd1f5174d12a0f1d9ad2c88d0ad11ab6aad0ac72b7d9ce621815f8016a9", + "0" + ], + "0x33d6E15047E8644F8DDf5CD05d202dfE587DA6E3": [ + "0x6155bD199ECcc79Ff4e8B392f6cBD9c9874E8916", + "0x7071f283424072341f856ac9e947e7ec0eb68719f757a7e785979b6b8717579d", + "0" + ], + "0x3483c140EF7F2716460198Ff831a8e53F05F1606": [ + "0x6155bD199ECcc79Ff4e8B392f6cBD9c9874E8916", + "0x8b47ba2a8454ec799cd91646e7ec47168e91fd139b23f017455f3e5898aaba93", + "0" + ], + "0x39A0EbdEE54cB319f4F42141daaBDb6ba25D341A": [ + "0x6155bD199ECcc79Ff4e8B392f6cBD9c9874E8916", + "0x0abcd104777321a82b010357f20887d61247493d89d2e987ff57bcecbde00e1e", + "0" + ], + "0x3e3FE7dBc6B4C189E7128855dD526361c49b40Af": [ + "0x6155bD199ECcc79Ff4e8B392f6cBD9c9874E8916", + "0x3ca7c3e38968823ccb4c78ea688df41356f182ae1d159e4ee608d30d68cef320", + "0" + ], + "0x4F36aAEb18Ab56A4e380241bea6ebF215b9cb12c": [ + "0x46cF57508B0565decC0419B833C2dAFa50B132e0", + "0x32A0E5828B62AAb932362a4816ae03b860b65e83", + "0" + ], + "0x7637d44c9f2e9cA584a8B5D2EA493012A5cdaEB6": [ + "0xC40801b88C835a58e54eEE6679D301ba31a4C72b", + "0x32A0E5828B62AAb932362a4816ae03b860b65e83", + "0" + ], + "0x8A1AA86d35b2EE8C9369618E7D7b40000cCD3295": [ + "0x6155bD199ECcc79Ff4e8B392f6cBD9c9874E8916", + "0xe3262375f45a6e2026b7e7b18c2b807434f2508fe1a2a3dfb493c7df8f4aad6a", + "0" + ], + "0xd497Be005638efCf09F6BFC8DAFBBB0BB72cD991": [ + "0x082d16150BF75BB8F2197eEC1d293DbA96c93638", + "0x32A0E5828B62AAb932362a4816ae03b860b65e83", + "0" + ] + } + } } - From f3b94bb6b36605892e6901ee311b23426412f742 Mon Sep 17 00:00:00 2001 From: alexvozhak Date: Sat, 7 Sep 2024 18:49:14 +0300 Subject: [PATCH 064/118] optimized binary comparison logic --- diffyscan/diffyscan.py | 34 ++++++++++++++++++------------ diffyscan/utils/binary_verifier.py | 12 +++++++++-- diffyscan/utils/explorer.py | 15 +++++++------ 3 files changed, 39 insertions(+), 22 deletions(-) diff --git a/diffyscan/diffyscan.py b/diffyscan/diffyscan.py index f2a47a9..686eda1 100755 --- a/diffyscan/diffyscan.py +++ b/diffyscan/diffyscan.py @@ -9,7 +9,7 @@ from .utils.explorer import ( get_contract_from_explorer, compile_contract_from_explorer, - get_contract_creation_code, + parse_compiled_contract, ) from .utils.github import ( get_file_from_github, @@ -45,9 +45,28 @@ def run_bytecode_diff( target_compiled_contract = compile_contract_from_explorer(contract_source_code) - contract_creation_code, immutables = get_contract_creation_code( + contract_creation_code, deployed_bytecode, immutables = parse_compiled_contract( target_compiled_contract ) + + remote_RPC_URL = binary_config["remote_RPC_URL"] + remote_deployed_bytecode = get_bytecode_from_node( + contract_address_from_config, remote_RPC_URL + ) + if remote_deployed_bytecode is None: + raise_error_or_log( + skip_deploy_error, + f"Failed to receive bytecode from {remote_RPC_URL})", + ) + return + + if match_bytecode(deployed_bytecode, remote_deployed_bytecode, immutables): + return + + logger.info( + f"Trying to check bytecode via using calldata and deploying into local node" + ) + skip_deploy_error = binary_config["skip_deploy_error"] calldata, text_error = get_calldata( @@ -83,17 +102,6 @@ def run_bytecode_diff( ) return - remote_RPC_URL = binary_config["remote_RPC_URL"] - remote_deployed_bytecode = get_bytecode_from_node( - contract_address_from_config, remote_RPC_URL - ) - if remote_deployed_bytecode is None: - raise_error_or_log( - skip_deploy_error, - f"Failed to receive bytecode from {remote_RPC_URL})", - ) - return - match_bytecode( local_deployed_bytecode, remote_deployed_bytecode, diff --git a/diffyscan/utils/binary_verifier.py b/diffyscan/utils/binary_verifier.py index f7fb34a..1914cea 100644 --- a/diffyscan/utils/binary_verifier.py +++ b/diffyscan/utils/binary_verifier.py @@ -22,8 +22,7 @@ def match_bytecode(actualBytecode, expectedBytecode, immutables): if not differences: logger.okay(f"Bytecodes are fully matched") - return - logger.warn(f"Bytecodes have differences") + return True nearLinesCount = 3 checkpoints = {0, *differences} @@ -58,6 +57,8 @@ def match_bytecode(actualBytecode, expectedBytecode, immutables): f'{green("0000 00 STOP")} - the actual bytecode instruction exists when the expected doesn\'t' ) logger.divider() + + is_matched = True for i in range(len(checkpointsArray)): currInd = checkpointsArray[i] prevInd = checkpointsArray[i - 1] if i > 0 else None @@ -109,6 +110,8 @@ def match_bytecode(actualBytecode, expectedBytecode, immutables): paramsLength = len(expected["bytecode"]) // 2 - 1 isImmutable = immutables.get(expected["start"] + 1) == paramsLength + if actualParams != expectedParams and not isImmutable: + is_matched = False params = ( actualParams if actualParams == expectedParams @@ -121,6 +124,11 @@ def match_bytecode(actualBytecode, expectedBytecode, immutables): print(f"{to_hex(currInd, 4)} {opcode} {opname} {params}") else: raise ValueError("Invalid bytecode difference data") + if is_matched: + logger.warn(f"Bytecodes have differences only in the immutables") + else: + logger.error(f"Bytecodes have differences not in the immutables") + return is_matched def parse(bytecode): diff --git a/diffyscan/utils/explorer.py b/diffyscan/utils/explorer.py index b488f8c..dad0013 100644 --- a/diffyscan/utils/explorer.py +++ b/diffyscan/utils/explorer.py @@ -165,13 +165,14 @@ def compile_contract_from_explorer(contract_code): target_contract_name = contract_code['name'] return get_target_compiled_contract(compiled_contracts, target_contract_name) -def get_contract_creation_code(target_compiled_contract): +def parse_compiled_contract(target_compiled_contract): contract_creation_code_without_calldata = f'0x{target_compiled_contract['evm']['bytecode']['object']}' + deployed_bytecode = f'0x{target_compiled_contract['evm']['deployedBytecode']['object']}' immutables = {} 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'] - - return contract_creation_code_without_calldata, immutables + immutable_references = target_compiled_contract['evm']['deployedBytecode']['immutableReferences'] + for refs in immutable_references.values(): + for ref in refs: + immutables[ref['start']] = ref['length'] + + return contract_creation_code_without_calldata, deployed_bytecode, immutables From e9622dd9581ca7125289b5eb71f8643f6510480d Mon Sep 17 00:00:00 2001 From: alexvozhak Date: Sat, 7 Sep 2024 20:56:42 +0300 Subject: [PATCH 065/118] made warning instead of exception when explorer token isn't set --- diffyscan/diffyscan.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/diffyscan/diffyscan.py b/diffyscan/diffyscan.py index 686eda1..0f24381 100755 --- a/diffyscan/diffyscan.py +++ b/diffyscan/diffyscan.py @@ -248,7 +248,7 @@ def process_config(path: str, recursive_parsing: bool, unify_formatting: bool): ) explorer_token = os.getenv("ETHERSCAN_EXPLORER_TOKEN", default=None) if explorer_token is None: - raise ValueError( + logger.warn( f'Failed to find explorer token in env ("ETHERSCAN_EXPLORER_TOKEN")' ) bytecode_comparison_key = "bytecode_comparison" From eda4d738026a466a48d1b3416e77d43575440817 Mon Sep 17 00:00:00 2001 From: alexvozhak Date: Sat, 7 Sep 2024 20:56:49 +0300 Subject: [PATCH 066/118] removed field "enable" from lido_dao_holesky_config.json --- config_samples/lido_dao_holesky_config.json | 1 - 1 file changed, 1 deletion(-) diff --git a/config_samples/lido_dao_holesky_config.json b/config_samples/lido_dao_holesky_config.json index 5717c24..fa71e0d 100644 --- a/config_samples/lido_dao_holesky_config.json +++ b/config_samples/lido_dao_holesky_config.json @@ -114,7 +114,6 @@ } }, "bytecode_comparison": { - "enable": true, "skip_deploy_error": false, "hardhat_config_name": "holesky_hardhat.config.js", "local_RPC_URL": "http://127.0.0.1:7545", From bbdd40ab7a3161edb3bd1207267ca6024627d009 Mon Sep 17 00:00:00 2001 From: alexvozhak Date: Sat, 7 Sep 2024 20:57:27 +0300 Subject: [PATCH 067/118] added skip_binary_comparison argument --- diffyscan/diffyscan.py | 69 ++++++++++++++++++++++++++++++------------ 1 file changed, 49 insertions(+), 20 deletions(-) diff --git a/diffyscan/diffyscan.py b/diffyscan/diffyscan.py index 0f24381..fb37271 100755 --- a/diffyscan/diffyscan.py +++ b/diffyscan/diffyscan.py @@ -233,7 +233,12 @@ def run_source_diff( logger.report_table(report) -def process_config(path: str, recursive_parsing: bool, unify_formatting: bool): +def process_config( + path: str, + recursive_parsing: bool, + unify_formatting: bool, + skip_binary_comparison: bool, +): logger.info(f"Loading config {path}...") config = load_config(path) @@ -251,15 +256,19 @@ def process_config(path: str, recursive_parsing: bool, unify_formatting: bool): logger.warn( f'Failed to find explorer token in env ("ETHERSCAN_EXPLORER_TOKEN")' ) - bytecode_comparison_key = "bytecode_comparison" - if bytecode_comparison_key not in config: - raise ValueError( - f'Failed to find "{bytecode_comparison_key}" section in config' - ) + if not skip_binary_comparison: + bytecode_comparison_key = "bytecode_comparison" + if bytecode_comparison_key not in config: + raise ValueError( + f'Failed to find "{bytecode_comparison_key}" section in config' + ) try: - hardhat.start(path, config[bytecode_comparison_key]) - deployer_account = get_account(config[bytecode_comparison_key]["local_RPC_URL"]) + if not skip_binary_comparison: + hardhat.start(path, config[bytecode_comparison_key]) + deployer_account = get_account( + config[bytecode_comparison_key]["local_RPC_URL"] + ) for contract_address, contract_name in config["contracts"].items(): contract_code = get_contract_from_explorer( @@ -276,17 +285,19 @@ def process_config(path: str, recursive_parsing: bool, unify_formatting: bool): recursive_parsing, unify_formatting, ) - run_bytecode_diff( - contract_address, - contract_name, - contract_code, - config[bytecode_comparison_key], - deployer_account, - ) + if not skip_binary_comparison: + run_bytecode_diff( + contract_address, + contract_name, + contract_code, + config[bytecode_comparison_key], + deployer_account, + ) except KeyboardInterrupt: logger.info(f"Keyboard interrupt by user") finally: - hardhat.stop() + if not skip_binary_comparison: + hardhat.stop() def parse_arguments(): @@ -314,6 +325,12 @@ def parse_arguments(): help="Unify formatting by prettier before comparing", action="store_true", ) + parser.add_argument( + "--skip-binary-comparison", + "-B", + help="Skip binary bytecode comparison", + action="store_true", + ) return parser.parse_args() @@ -328,14 +345,26 @@ def main(): logger.info("Welcome to Diffyscan!") logger.divider() if args.path is None: - process_config(DEFAULT_CONFIG_PATH, args.support_brownie, args.prettify) + process_config( + DEFAULT_CONFIG_PATH, + args.support_brownie, + args.prettify, + args.skip_binary_comparison, + ) elif os.path.isfile(args.path): - process_config(args.path, args.support_brownie, args.prettify) + process_config( + args.path, args.support_brownie, args.prettify, args.skip_binary_comparison + ) elif os.path.isdir(args.path): for filename in os.listdir(args.path): - config_path = os.path.join(args.path, filename) + config_path = os.path.join(args.path, filename, args.skip_binary_comparison) if os.path.isfile(config_path): - process_config(config_path, args.support_brownie, args.prettify) + process_config( + config_path, + args.support_brownie, + args.prettify, + args.skip_binary_comparison, + ) else: logger.error(f"Specified config path {args.path} not found") sys.exit(1) From 71453d9a8fecc6217edc98442a8b1095be8bba26 Mon Sep 17 00:00:00 2001 From: alexvozhak Date: Sun, 8 Sep 2024 01:33:58 +0300 Subject: [PATCH 068/118] added custom exceptions --- config_samples/lido_dao_holesky_config.json | 2 +- config_samples/lido_dao_sepolia_config.json | 2 +- diffyscan/diffyscan.py | 129 +++++++++++--------- diffyscan/utils/binary_verifier.py | 13 +- diffyscan/utils/calldata.py | 25 ++-- diffyscan/utils/common.py | 17 +-- diffyscan/utils/compiler.py | 19 +-- diffyscan/utils/custom_exceptions.py | 26 ++++ diffyscan/utils/encoder.py | 3 +- diffyscan/utils/explorer.py | 5 +- diffyscan/utils/hardhat.py | 7 +- diffyscan/utils/node_handler.py | 14 +-- 12 files changed, 160 insertions(+), 102 deletions(-) create mode 100644 diffyscan/utils/custom_exceptions.py diff --git a/config_samples/lido_dao_holesky_config.json b/config_samples/lido_dao_holesky_config.json index fa71e0d..6888e8b 100644 --- a/config_samples/lido_dao_holesky_config.json +++ b/config_samples/lido_dao_holesky_config.json @@ -114,7 +114,7 @@ } }, "bytecode_comparison": { - "skip_deploy_error": false, + "raise_exception": true, "hardhat_config_name": "holesky_hardhat.config.js", "local_RPC_URL": "http://127.0.0.1:7545", "remote_RPC_URL": "https://ethereum-holesky.blockpi.network/v1/rpc/public", diff --git a/config_samples/lido_dao_sepolia_config.json b/config_samples/lido_dao_sepolia_config.json index 672c895..1ef8cf1 100644 --- a/config_samples/lido_dao_sepolia_config.json +++ b/config_samples/lido_dao_sepolia_config.json @@ -99,7 +99,7 @@ } }, "bytecode_comparison": { - "skip_deploy_error": false, + "raise_exception": true, "hardhat_config_name": "sepolia_hardhat.config.js", "local_RPC_URL": "http://127.0.0.1:7545", "remote_RPC_URL": "https://sepolia.drpc.org", diff --git a/diffyscan/diffyscan.py b/diffyscan/diffyscan.py index fb37271..0beb799 100755 --- a/diffyscan/diffyscan.py +++ b/diffyscan/diffyscan.py @@ -22,6 +22,7 @@ from .utils.hardhat import hardhat from .utils.node_handler import * from .utils.calldata import get_calldata +import utils.custom_exceptions as custom_exc __version__ = "0.0.0" @@ -39,80 +40,92 @@ def run_bytecode_diff( address_name = f"{contract_address_from_config} : {contract_name_from_config}" logger.divider() logger.info(f"Binary bytecode comparion started for {address_name}") + is_need_raise_exception = binary_config["raise_exception"] + try: + target_compiled_contract = compile_contract_from_explorer(contract_source_code) - if deployer_account is None: - raise ValueError(f"The deployer account isn't set)") - - target_compiled_contract = compile_contract_from_explorer(contract_source_code) - - contract_creation_code, deployed_bytecode, immutables = parse_compiled_contract( - target_compiled_contract - ) + contract_creation_code, deployed_bytecode, immutables = parse_compiled_contract( + target_compiled_contract + ) - remote_RPC_URL = binary_config["remote_RPC_URL"] - remote_deployed_bytecode = get_bytecode_from_node( - contract_address_from_config, remote_RPC_URL - ) - if remote_deployed_bytecode is None: - raise_error_or_log( - skip_deploy_error, - f"Failed to receive bytecode from {remote_RPC_URL})", + remote_RPC_URL = binary_config["remote_RPC_URL"] + remote_deployed_bytecode = get_bytecode_from_node( + contract_address_from_config, remote_RPC_URL ) - return - if match_bytecode(deployed_bytecode, remote_deployed_bytecode, immutables): - return + if match_bytecode( + deployed_bytecode, + remote_deployed_bytecode, + immutables, + ): + return - logger.info( - f"Trying to check bytecode via using calldata and deploying into local node" - ) + logger.info( + f"Trying to check bytecode via using calldata and deploying into local node" + ) - skip_deploy_error = binary_config["skip_deploy_error"] + calldata = get_calldata( + contract_address_from_config, + target_compiled_contract, + binary_config, + ) - calldata, text_error = get_calldata( - contract_address_from_config, - target_compiled_contract, - binary_config, - ) - if calldata is not None: contract_creation_code += calldata - elif text_error is not None: - raise_error_or_log(skip_deploy_error, text_error) - return - local_RPC_URL = binary_config["local_RPC_URL"] - local_contract_address, text_reason = deploy_contract( - local_RPC_URL, deployer_account, contract_creation_code - ) - - if local_contract_address is None: - raise_error_or_log( - skip_deploy_error, - f"Failed to deploy bytecode to {local_RPC_URL} {text_reason}", + local_contract_address = deploy_contract( + binary_config["local_RPC_URL"], deployer_account, contract_creation_code ) - return - local_deployed_bytecode = get_bytecode_from_node( - local_contract_address, local_RPC_URL - ) - if local_deployed_bytecode is None: - raise_error_or_log( - skip_deploy_error, - text_error=f"Failed to receive bytecode from {local_RPC_URL})", + local_deployed_bytecode = get_bytecode_from_node( + local_contract_address, binary_config["local_RPC_URL"] ) - return - match_bytecode( - local_deployed_bytecode, - remote_deployed_bytecode, - immutables, - ) + match_bytecode( + local_deployed_bytecode, + remote_deployed_bytecode, + immutables, + ) + except custom_exc.CompileError as compiler_exc: + raise_exception_or_log( + f"Failed to compile contract. {compiler_exc}", + is_need_raise_exception, + ) + except custom_exc.NodeError as node_exc: + raise_exception_or_log( + f"Failed to receive bytecode from {remote_RPC_URL}. {node_exc}", + is_need_raise_exception, + ) + except custom_exc.EncoderError as encoder_exc: + raise_exception_or_log( + f"Failed to encode calldata arguments. {encoder_exc}", + is_need_raise_exception, + ) + except custom_exc.CalldataError as calldata_exc: + raise_exception_or_log( + f"Failed to get calldata. {calldata_exc}", + is_need_raise_exception, + ) + except custom_exc.HardhatError as hardhat_exc: + raise_exception_or_log( + f"Failed to start Hardhat: {hardhat_exc}", + is_need_raise_exception, + ) + except custom_exc.ExplorerError as explorer_exc: + raise_exception_or_log( + f"Failed to communicate with Blockchain explorer: {explorer_exc}", + is_need_raise_exception, + ) + except custom_exc.BinVerifierError as bin_verifier_exc: + raise_exception_or_log( + f"Failed in binary comparison: {bin_verifier_exc}", + is_need_raise_exception, + ) -def raise_error_or_log(message: str, raise_exception: bool = True): +def raise_exception_or_log(custom_exception: Exception, raise_exception: bool = True): if raise_exception: - raise ValueError(message) - logger.error(message) + raise custom_exception() + logger.error(str(custom_exception)) def run_source_diff( diff --git a/diffyscan/utils/binary_verifier.py b/diffyscan/utils/binary_verifier.py index 1914cea..c0dd0b2 100644 --- a/diffyscan/utils/binary_verifier.py +++ b/diffyscan/utils/binary_verifier.py @@ -1,5 +1,6 @@ from .logger import * from .constants import OPCODES +from .custom_exceptions import BinVerifierError def match_bytecode(actualBytecode, expectedBytecode, immutables): @@ -14,7 +15,7 @@ def match_bytecode(actualBytecode, expectedBytecode, immutables): actual = actualInstructions[i] if i < len(actualInstructions) else None expected = expectedInstructions[i] if i < len(expectedInstructions) else None if not actual and not expected: - raise ValueError("Invalid instructions data") + raise BinVerifierError("Invalid instructions data") elif (actual is not None) and ( actual.get("bytecode") != expected.get("bytecode") ): @@ -123,11 +124,15 @@ def match_bytecode(actualBytecode, expectedBytecode, immutables): ) print(f"{to_hex(currInd, 4)} {opcode} {opname} {params}") else: - raise ValueError("Invalid bytecode difference data") + raise BinVerifierError("Invalid bytecode difference data") if is_matched: - logger.warn(f"Bytecodes have differences only in the immutables") + logger.okay( + f"Bytecodes have differences only on the immutable reference position" + ) else: - logger.error(f"Bytecodes have differences not in the immutables") + logger.error( + f"Bytecodes have differences not on the immutable reference position" + ) return is_matched diff --git a/diffyscan/utils/calldata.py b/diffyscan/utils/calldata.py index d1d9186..8cb13c9 100644 --- a/diffyscan/utils/calldata.py +++ b/diffyscan/utils/calldata.py @@ -1,17 +1,19 @@ from .logger import logger from .encoder import encode_constructor_arguments +from .custom_exceptions import CalldataError def get_calldata(contract_address_from_config, target_compiled_contract, binary_config): calldata = get_raw_calldata_from_config(contract_address_from_config, binary_config) if calldata is not None: - return calldata, None - - calldata, text_error = parse_calldata_from_config( + return calldata + + calldata = parse_calldata_from_config( contract_address_from_config, binary_config["constructor_args"], target_compiled_contract, ) - return calldata, text_error + + return calldata def get_constructor_abi(target_compiled_contract): constructor_abi = None @@ -36,10 +38,17 @@ def parse_calldata_from_config(contract_address_from_config, constructor_args, t logger.info(f"Trying to parse calldata from config") constructor_abi = get_constructor_abi(target_compiled_contract) if constructor_abi is None: - return None, None + raise CalldataError("Failed to find ABI constructor in compiled contract") - if constructor_args is None or contract_address_from_config not in constructor_args: - return None, f"Failed to find constructor's values or calldata in config" + if constructor_args is None: + raise CalldataError("Failed to find constructor's args in config") + + if contract_address_from_config not in constructor_args: + raise CalldataError("Failed to find contract calldata in config") calldata = encode_constructor_arguments(constructor_abi, constructor_args [contract_address_from_config]) - return calldata, None + + if not calldata: + raise CalldataError("Contract calldata is empty") + + return calldata diff --git a/diffyscan/utils/common.py b/diffyscan/utils/common.py index 0fe131c..babd823 100644 --- a/diffyscan/utils/common.py +++ b/diffyscan/utils/common.py @@ -9,6 +9,7 @@ from .logger import logger from .types import Config +from .custom_exceptions import NodeError def load_env(variable_name, required=True, masked=False): @@ -39,13 +40,13 @@ def fetch(url, headers=None): response = requests.get(url, headers=headers) response.raise_for_status() except requests.exceptions.HTTPError as http_err: - raise ValueError(f"HTTP error occurred: {http_err}") + raise NodeError(f"HTTP error occurred: {http_err}") except requests.exceptions.ConnectionError as conn_err: - raise ValueError(f"Connection error occurred: {conn_err}") + raise NodeError(f"Connection error occurred: {conn_err}") except requests.exceptions.Timeout as timeout_err: - raise ValueError(f"Timeout error occurred: {timeout_err}") + raise NodeError(f"Timeout error occurred: {timeout_err}") except requests.exceptions.RequestException as req_err: - raise ValueError(f"Request exception occurred: {req_err}") + raise NodeError(f"Request exception occurred: {req_err}") return response @@ -56,13 +57,13 @@ def pull(url, payload=None, headers=None): response = requests.post(url, data=payload, headers=headers) response.raise_for_status() except requests.exceptions.HTTPError as http_err: - raise ValueError(f"HTTP error occurred: {http_err}") + raise NodeError(f"HTTP error occurred: {http_err}") except requests.exceptions.ConnectionError as conn_err: - raise ValueError(f"Connection error occurred: {conn_err}") + raise NodeError(f"Connection error occurred: {conn_err}") except requests.exceptions.Timeout as timeout_err: - raise ValueError(f"Timeout error occurred: {timeout_err}") + raise NodeError(f"Timeout error occurred: {timeout_err}") except requests.exceptions.RequestException as req_err: - raise ValueError(f"Request exception occurred: {req_err}") + raise NodeError(f"Request exception occurred: {req_err}") return response diff --git a/diffyscan/utils/compiler.py b/diffyscan/utils/compiler.py index 0ece359..11d1500 100644 --- a/diffyscan/utils/compiler.py +++ b/diffyscan/utils/compiler.py @@ -9,6 +9,7 @@ from .common import fetch from .helpers import create_dirs from .logger import logger +from .custom_exceptions import CompileError def get_solc_native_platform_from_os(): @@ -20,7 +21,7 @@ def get_solc_native_platform_from_os(): elif platform_name == "win32": return "windows-amd64" else: - raise ValueError(f"Unsupported platform {platform_name}") + raise CompileError(f"Unsupported platform {platform_name}") def get_compiler_info(required_platform, required_compiler_version): @@ -36,7 +37,7 @@ def get_compiler_info(required_platform, required_compiler_version): ) if not required_build_info: - raise ValueError( + raise CompileError( f'Required compiler version "{required_compiler_version}" for "{required_platform}" is not found' ) @@ -53,16 +54,16 @@ def download_compiler(required_platform, build_info, destination_path): with open(destination_path, "wb") as compiler_file: compiler_file.write(download_compiler_response.content) except IOError as e: - raise ValueError(f"Error writing to file: {e}") + raise CompileError(f"Error writing to file: {e}") except Exception as e: - raise ValueError(f"An error occurred: {e}") + raise CompileError(f"An error occurred: {e}") return download_compiler_response.content def check_compiler_checksum(compiler, valid_checksum): compiler_checksum = hashlib.sha256(compiler).hexdigest() if compiler_checksum != valid_checksum: - raise ValueError( + raise CompileError( f"Compiler checksum mismatch. Expected: {valid_checksum}, Got: {compiler_checksum}" ) @@ -90,11 +91,11 @@ def compile_contracts(compiler_path, input_settings): timeout=30, ) except subprocess.CalledProcessError as e: - raise ValueError(f"Error during compiler subprocess execution: {e}") + raise CompileError(f"Error during compiler subprocess execution: {e}") except subprocess.TimeoutExpired as e: - raise ValueError(f"Compiler process timed out: {e}") + raise CompileError(f"Compiler process timed out: {e}") except Exception as e: - raise ValueError(f"An unexpected error occurred: {e}") + raise CompileError(f"An unexpected error occurred: {e}") return json.loads(process.stdout) @@ -106,7 +107,7 @@ def get_target_compiled_contract(compiled_contracts, target_contract_name): contracts_to_check.append(contract) if len(contracts_to_check) != 1: - raise ValueError("multiple contracts with the same name") + raise CompileError("multiple contracts with the same name") logger.okay(f"Contracts were successfully compiled") diff --git a/diffyscan/utils/custom_exceptions.py b/diffyscan/utils/custom_exceptions.py new file mode 100644 index 0000000..9cf9b2a --- /dev/null +++ b/diffyscan/utils/custom_exceptions.py @@ -0,0 +1,26 @@ +class CompileError(Exception): + pass + + +class NodeError(Exception): + pass + + +class CalldataError(Exception): + pass + + +class EncoderError(Exception): + pass + + +class HardhatError(Exception): + pass + + +class ExplorerError(Exception): + pass + + +class BinVerifierError(Exception): + pass diff --git a/diffyscan/utils/encoder.py b/diffyscan/utils/encoder.py index 146b834..5d110c7 100644 --- a/diffyscan/utils/encoder.py +++ b/diffyscan/utils/encoder.py @@ -1,4 +1,5 @@ from .logger import logger +from .custom_exceptions import EncoderError def encode_address(address): @@ -98,7 +99,7 @@ def encode_constructor_arguments(constructor_abi, constructor_config_args): data2 = encode_bytes(arg_value) compl_data.append(data2) else: - raise ValueError(f"Unknown constructor argument type: {arg_type}") + raise EncoderError(f"Unknown constructor argument type: {arg_type}") for data in compl_data: constructor_calldata += data diff --git a/diffyscan/utils/explorer.py b/diffyscan/utils/explorer.py index dad0013..1189b9e 100644 --- a/diffyscan/utils/explorer.py +++ b/diffyscan/utils/explorer.py @@ -5,6 +5,7 @@ from .logger import logger from .compiler import * 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) @@ -19,7 +20,7 @@ def _get_contract_from_etherscan(token, etherscan_hostname, contract): response = fetch(etherscan_link).json() if response["message"] == "NOTOK": - raise ValueError(response["result"]) + raise ExplorerError(f'Received bad response: {response["result"]}') result = response["result"][0] if "ContractName" not in result: @@ -142,7 +143,7 @@ def get_contract_from_explorer( contract_name_from_etherscan = result["name"] if contract_name_from_etherscan != contract_name_from_config: - raise ValueError( + raise ExplorerError( f"Contract name in config does not match with Blockchain explorer {contract_address}: {contract_name_from_config} != {contract_name_from_etherscan}", ) diff --git a/diffyscan/utils/hardhat.py b/diffyscan/utils/hardhat.py index 66936b5..9f2ee0f 100644 --- a/diffyscan/utils/hardhat.py +++ b/diffyscan/utils/hardhat.py @@ -4,6 +4,7 @@ from urllib.parse import urlparse from .logger import logger +from .custom_exceptions import HardhatError class Hardhat: @@ -44,7 +45,7 @@ def start(self, main_config_relative_path: str, binary_config): if return_code == 0: is_port_used = self.is_port_in_use(parsed_url) if is_port_used: - raise ValueError(f"Failed to start Hardhat: {parsed_url.netloc} is busy") + raise HardhatError(f"{parsed_url.netloc} is busy") self.sub_process = subprocess.Popen( "exec " + local_node_command, shell=True, @@ -54,13 +55,13 @@ def start(self, main_config_relative_path: str, binary_config): try: _, errs = self.sub_process.communicate(timeout=self.TIMEOUT_FOR_INIT_SEC) if errs: - raise ValueError(f"Failed to start Hardhat: {errs.decode()}") + raise HardhatError(f"{errs.decode()}") except subprocess.TimeoutExpired: is_port_used = self.is_port_in_use(parsed_url) if is_port_used: logger.okay(f"Hardhat successfully started, PID {self.sub_process.pid}") else: - raise ValueError(f"Failed to start Hardhat: something is wrong") + raise HardhatError(f"something is wrong") def stop(self): if self.sub_process is not None and self.sub_process.poll() is None: diff --git a/diffyscan/utils/node_handler.py b/diffyscan/utils/node_handler.py index 02c0d4d..de5b28d 100644 --- a/diffyscan/utils/node_handler.py +++ b/diffyscan/utils/node_handler.py @@ -2,6 +2,7 @@ from .common import pull from .logger import logger +from .custom_exceptions import NodeError def get_bytecode_from_node(contract_address, rpc_url): @@ -21,7 +22,7 @@ def get_bytecode_from_node(contract_address, rpc_url): "result" not in sources_url_response_in_json or sources_url_response_in_json["result"] == "0x" ): - return None + raise NodeError(f"Failed to receive bytecode from {rpc_url})") logger.okay(f"Bytecode was successfully received") return sources_url_response_in_json["result"] @@ -37,7 +38,7 @@ def get_account(rpc_url): account_address_response = pull(rpc_url, payload).json() if "result" not in account_address_response: - return None + raise NodeError(f"The deployer account isn't set") logger.okay(f"The account was successfully received") @@ -58,7 +59,7 @@ def deploy_contract(rpc_url, deployer, data): response_sendTransaction = pull(rpc_url, payload_sendTransaction).json() if "error" in response_sendTransaction: - return None, response_sendTransaction["error"]["message"] + raise NodeError(response_sendTransaction["error"]["message"]) logger.okay(f"Transaction was successfully deployed") @@ -79,15 +80,14 @@ def deploy_contract(rpc_url, deployer, data): or "contractAddress" not in response_getTransactionReceipt["result"] or "status" not in response_getTransactionReceipt["result"] ): - return None, f"Failed to received transaction receipt" + raise NodeError(f"Failed to received transaction receipt") if response_getTransactionReceipt["result"]["status"] != "0x1": - return ( - None, + raise NodeError( f"Failed to received transaction receipt. \ Transaction has been reverted (status 0x0). Input missmatch?", ) contract_address = response_getTransactionReceipt["result"]["contractAddress"] - return contract_address, "" + return contract_address From 6b00c89431415c6c528530a602f2bd81c24153a1 Mon Sep 17 00:00:00 2001 From: alexvozhak Date: Sun, 8 Sep 2024 20:53:28 +0300 Subject: [PATCH 069/118] added exception handler --- diffyscan/diffyscan.py | 48 +++-------------------- diffyscan/utils/custom_exceptions.py | 58 +++++++++++++++++++++------- diffyscan/utils/node_handler.py | 2 +- 3 files changed, 51 insertions(+), 57 deletions(-) diff --git a/diffyscan/diffyscan.py b/diffyscan/diffyscan.py index 0beb799..d7b3ceb 100755 --- a/diffyscan/diffyscan.py +++ b/diffyscan/diffyscan.py @@ -22,7 +22,7 @@ from .utils.hardhat import hardhat from .utils.node_handler import * from .utils.calldata import get_calldata -import utils.custom_exceptions as custom_exc +import utils.custom_exceptions as CustomExceptions __version__ = "0.0.0" @@ -85,47 +85,8 @@ def run_bytecode_diff( remote_deployed_bytecode, immutables, ) - except custom_exc.CompileError as compiler_exc: - raise_exception_or_log( - f"Failed to compile contract. {compiler_exc}", - is_need_raise_exception, - ) - except custom_exc.NodeError as node_exc: - raise_exception_or_log( - f"Failed to receive bytecode from {remote_RPC_URL}. {node_exc}", - is_need_raise_exception, - ) - except custom_exc.EncoderError as encoder_exc: - raise_exception_or_log( - f"Failed to encode calldata arguments. {encoder_exc}", - is_need_raise_exception, - ) - except custom_exc.CalldataError as calldata_exc: - raise_exception_or_log( - f"Failed to get calldata. {calldata_exc}", - is_need_raise_exception, - ) - except custom_exc.HardhatError as hardhat_exc: - raise_exception_or_log( - f"Failed to start Hardhat: {hardhat_exc}", - is_need_raise_exception, - ) - except custom_exc.ExplorerError as explorer_exc: - raise_exception_or_log( - f"Failed to communicate with Blockchain explorer: {explorer_exc}", - is_need_raise_exception, - ) - except custom_exc.BinVerifierError as bin_verifier_exc: - raise_exception_or_log( - f"Failed in binary comparison: {bin_verifier_exc}", - is_need_raise_exception, - ) - - -def raise_exception_or_log(custom_exception: Exception, raise_exception: bool = True): - if raise_exception: - raise custom_exception() - logger.error(str(custom_exception)) + except CustomExceptions.BaseCustomException as custom_exc: + CustomExceptions.ExceptionHandler.raise_exception_or_log(custom_exc) def run_source_diff( @@ -282,6 +243,9 @@ def process_config( deployer_account = get_account( config[bytecode_comparison_key]["local_RPC_URL"] ) + CustomExceptions.ExceptionHandler.initialize( + config[bytecode_comparison_key]["raise_exception"] + ) for contract_address, contract_name in config["contracts"].items(): contract_code = get_contract_from_explorer( diff --git a/diffyscan/utils/custom_exceptions.py b/diffyscan/utils/custom_exceptions.py index 9cf9b2a..5b1c7bc 100644 --- a/diffyscan/utils/custom_exceptions.py +++ b/diffyscan/utils/custom_exceptions.py @@ -1,26 +1,56 @@ -class CompileError(Exception): - pass +from .logger import logger -class NodeError(Exception): - pass +class BaseCustomException(Exception): + def __init__(self, message: str): + super().__init__(message) + self.message = message -class CalldataError(Exception): - pass +class CompileError(BaseCustomException): + def __init__(self, reason: str): + super().__init__(f"Failed to compile contract: {reason}") -class EncoderError(Exception): - pass +class NodeError(BaseCustomException): + def __init__(self, reason: str): + super().__init__(f"Failed to receive bytecode from node: {reason}") -class HardhatError(Exception): - pass +class CalldataError(BaseCustomException): + def __init__(self, reason: str): + super().__init__(f"Failed to get calldata: {reason}") -class ExplorerError(Exception): - pass +class EncoderError(BaseCustomException): + def __init__(self, reason: str): + super().__init__(f"Failed to encode calldata arguments: {reason}") -class BinVerifierError(Exception): - pass +class HardhatError(BaseCustomException): + def __init__(self, reason: str): + super().__init__(f"Failed to start Hardhat: {reason}") + + +class ExplorerError(BaseCustomException): + def __init__(self, reason: str): + super().__init__(f"Failed to communicate with Blockchain explorer: {reason}") + + +class BinVerifierError(BaseCustomException): + def __init__(self, reason: str): + super().__init__(f"Failed in binary comparison: {reason}") + + +class ExceptionHandler: + raise_exception = True + + @staticmethod + def initialize(raise_exception: bool) -> None: + ExceptionHandler.raise_exception = raise_exception + + @staticmethod + def raise_exception_or_log(custom_exception: BaseCustomException) -> None: + if ExceptionHandler.raise_exception: + raise custom_exception + logger.error(str(custom_exception)) diff --git a/diffyscan/utils/node_handler.py b/diffyscan/utils/node_handler.py index de5b28d..8aafb3a 100644 --- a/diffyscan/utils/node_handler.py +++ b/diffyscan/utils/node_handler.py @@ -22,7 +22,7 @@ def get_bytecode_from_node(contract_address, rpc_url): "result" not in sources_url_response_in_json or sources_url_response_in_json["result"] == "0x" ): - raise NodeError(f"Failed to receive bytecode from {rpc_url})") + raise NodeError(f"Received bad response") logger.okay(f"Bytecode was successfully received") return sources_url_response_in_json["result"] From 003c44ed7edda7674795f7d179c2ebf13a66703a Mon Sep 17 00:00:00 2001 From: alexvozhak Date: Sun, 8 Sep 2024 23:14:48 +0300 Subject: [PATCH 070/118] added BinartConfig type, integrated mypy --- diffyscan/diffyscan.py | 29 +++++++++++++++-------------- diffyscan/utils/types.py | 12 ++++++++++++ 2 files changed, 27 insertions(+), 14 deletions(-) diff --git a/diffyscan/diffyscan.py b/diffyscan/diffyscan.py index d7b3ceb..552c609 100755 --- a/diffyscan/diffyscan.py +++ b/diffyscan/diffyscan.py @@ -5,7 +5,13 @@ import os from .utils.common import load_config, load_env, prettify_solidity -from .utils.constants import * + +from .utils.constants import ( + DIFFS_DIR, + GITHUB_API_TOKEN, + DEFAULT_CONFIG_PATH, + START_TIME, +) from .utils.explorer import ( get_contract_from_explorer, compile_contract_from_explorer, @@ -18,9 +24,9 @@ ) from .utils.helpers import create_dirs from .utils.logger import logger -from .utils.binary_verifier import * +from .utils.binary_verifier import match_bytecode from .utils.hardhat import hardhat -from .utils.node_handler import * +from .utils.node_handler import get_bytecode_from_node, get_account, deploy_contract from .utils.calldata import get_calldata import utils.custom_exceptions as CustomExceptions @@ -41,6 +47,7 @@ def run_bytecode_diff( logger.divider() logger.info(f"Binary bytecode comparion started for {address_name}") is_need_raise_exception = binary_config["raise_exception"] + CustomExceptions.ExceptionHandler.initialize(is_need_raise_exception) try: target_compiled_contract = compile_contract_from_explorer(contract_source_code) @@ -231,20 +238,14 @@ def process_config( f'Failed to find explorer token in env ("ETHERSCAN_EXPLORER_TOKEN")' ) if not skip_binary_comparison: - bytecode_comparison_key = "bytecode_comparison" - if bytecode_comparison_key not in config: - raise ValueError( - f'Failed to find "{bytecode_comparison_key}" section in config' - ) + if "bytecode_comparison" not in config: + raise ValueError(f'Failed to find "bytecode_comparison" section in config') try: if not skip_binary_comparison: - hardhat.start(path, config[bytecode_comparison_key]) + hardhat.start(path, config["bytecode_comparison"]) deployer_account = get_account( - config[bytecode_comparison_key]["local_RPC_URL"] - ) - CustomExceptions.ExceptionHandler.initialize( - config[bytecode_comparison_key]["raise_exception"] + config["bytecode_comparison"]["local_RPC_URL"] ) for contract_address, contract_name in config["contracts"].items(): @@ -267,7 +268,7 @@ def process_config( contract_address, contract_name, contract_code, - config[bytecode_comparison_key], + config["bytecode_comparison"], deployer_account, ) except KeyboardInterrupt: diff --git a/diffyscan/utils/types.py b/diffyscan/utils/types.py index 7a3d570..e9ff4ee 100644 --- a/diffyscan/utils/types.py +++ b/diffyscan/utils/types.py @@ -1,8 +1,20 @@ from typing import TypedDict +class BinartConfig(TypedDict): + raise_exception: bool + hardhat_config_name: str + local_RPC_URL: str + remote_RPC_URL: str + constructor_calldata: set + constructor_args: set + + class Config(TypedDict): contracts: dict[str, str] network: str github_repo: str dependencies: dict[str, str] + explorer_hostname: str + explorer_token_env_var: str + bytecode_comparison: BinartConfig From 067ad46750a1b2a0708ecd88e158c165741b8c61 Mon Sep 17 00:00:00 2001 From: alexvozhak Date: Mon, 9 Sep 2024 00:01:49 +0300 Subject: [PATCH 071/118] fixed slicing: renamed types.py to custom_types.py --- diffyscan/utils/common.py | 2 +- diffyscan/utils/{types.py => custom_types.py} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename diffyscan/utils/{types.py => custom_types.py} (100%) diff --git a/diffyscan/utils/common.py b/diffyscan/utils/common.py index babd823..b3a860c 100644 --- a/diffyscan/utils/common.py +++ b/diffyscan/utils/common.py @@ -8,7 +8,7 @@ from urllib.parse import urlparse from .logger import logger -from .types import Config +from .custom_types import Config from .custom_exceptions import NodeError diff --git a/diffyscan/utils/types.py b/diffyscan/utils/custom_types.py similarity index 100% rename from diffyscan/utils/types.py rename to diffyscan/utils/custom_types.py From b9cc0375ca4bd08e56584c07529b6212a820b9a8 Mon Sep 17 00:00:00 2001 From: alexvozhak Date: Mon, 9 Sep 2024 00:03:05 +0300 Subject: [PATCH 072/118] applied black for calldata.py --- diffyscan/utils/calldata.py | 53 ++++++++++++++++++++++++------------- 1 file changed, 35 insertions(+), 18 deletions(-) diff --git a/diffyscan/utils/calldata.py b/diffyscan/utils/calldata.py index 8cb13c9..a95528d 100644 --- a/diffyscan/utils/calldata.py +++ b/diffyscan/utils/calldata.py @@ -2,11 +2,12 @@ from .encoder import encode_constructor_arguments from .custom_exceptions import CalldataError + def get_calldata(contract_address_from_config, target_compiled_contract, binary_config): calldata = get_raw_calldata_from_config(contract_address_from_config, binary_config) if calldata is not None: - return calldata - + return calldata + calldata = parse_calldata_from_config( contract_address_from_config, binary_config["constructor_args"], @@ -14,41 +15,57 @@ def get_calldata(contract_address_from_config, target_compiled_contract, binary_ ) return calldata - + + def get_constructor_abi(target_compiled_contract): constructor_abi = None try: - constructor_abi = [entry["inputs"] for entry in target_compiled_contract['abi'] if entry["type"] == "constructor"][0] + constructor_abi = [ + entry["inputs"] + for entry in target_compiled_contract["abi"] + if entry["type"] == "constructor" + ][0] except IndexError: - logger.okay(f"Contract's ABI doesn't have a constructor, calldata calculation skipped") + logger.okay( + f"Contract's ABI doesn't have a constructor, calldata calculation skipped" + ) return None - logger.okay(f'Constructor in ABI successfully found: {[arg['type'] for arg in constructor_abi]}') + logger.okay(f"Constructor in ABI successfully found") return constructor_abi + def get_raw_calldata_from_config(contract_address_from_config, binary_config): - if "constructor_calldata" not in binary_config or contract_address_from_config not in binary_config["constructor_calldata"]: + if ( + "constructor_calldata" not in binary_config + or contract_address_from_config not in binary_config["constructor_calldata"] + ): return None calldata_field = binary_config["constructor_calldata"] logger.info(f"Trying to use prepared calldata from config") prepared_calldata_from_config = calldata_field[contract_address_from_config] return prepared_calldata_from_config - -def parse_calldata_from_config(contract_address_from_config, constructor_args, target_compiled_contract): + + +def parse_calldata_from_config( + contract_address_from_config, constructor_args, target_compiled_contract +): logger.info(f"Trying to parse calldata from config") constructor_abi = get_constructor_abi(target_compiled_contract) if constructor_abi is None: - raise CalldataError("Failed to find ABI constructor in compiled contract") - + raise CalldataError("Failed to find ABI constructor in compiled contract") + if constructor_args is None: - raise CalldataError("Failed to find constructor's args in config") - + raise CalldataError("Failed to find constructor's args in config") + if contract_address_from_config not in constructor_args: - raise CalldataError("Failed to find contract calldata in config") + raise CalldataError("Failed to find contract calldata in config") + + calldata = encode_constructor_arguments( + constructor_abi, constructor_args[contract_address_from_config] + ) - calldata = encode_constructor_arguments(constructor_abi, constructor_args [contract_address_from_config]) - if not calldata: - raise CalldataError("Contract calldata is empty") - + raise CalldataError("Contract calldata is empty") + return calldata From e38ddb0eed1b6b78b1c406af152d86d89cddd88c Mon Sep 17 00:00:00 2001 From: alexvozhak Date: Mon, 9 Sep 2024 00:23:32 +0300 Subject: [PATCH 073/118] removed GITHUB_API_TOKEN from constant.py --- diffyscan/diffyscan.py | 7 +++++-- diffyscan/utils/constants.py | 4 ---- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/diffyscan/diffyscan.py b/diffyscan/diffyscan.py index 552c609..10c7b0d 100755 --- a/diffyscan/diffyscan.py +++ b/diffyscan/diffyscan.py @@ -8,7 +8,6 @@ from .utils.constants import ( DIFFS_DIR, - GITHUB_API_TOKEN, DEFAULT_CONFIG_PATH, START_TIME, ) @@ -241,6 +240,10 @@ def process_config( if "bytecode_comparison" not in config: raise ValueError(f'Failed to find "bytecode_comparison" section in config') + github_api_token = os.getenv("GITHUB_API_TOKEN", "") + if not github_api_token: + raise ValueError("GITHUB_API_TOKEN variable is not set") + try: if not skip_binary_comparison: hardhat.start(path, config["bytecode_comparison"]) @@ -259,7 +262,7 @@ def process_config( contract_address, contract_code, config, - GITHUB_API_TOKEN, + github_api_token, recursive_parsing, unify_formatting, ) diff --git a/diffyscan/utils/constants.py b/diffyscan/utils/constants.py index 524b96a..96feb56 100644 --- a/diffyscan/utils/constants.py +++ b/diffyscan/utils/constants.py @@ -11,10 +11,6 @@ SOLC_DIR = os.path.join(tempfile.gettempdir(), "solc_builds") -GITHUB_API_TOKEN = os.getenv("GITHUB_API_TOKEN", "") -if not GITHUB_API_TOKEN: - raise ValueError("GITHUB_API_TOKEN variable is not set") - # fmt: off OPCODES = { 0x00: 'STOP', 0x01: 'ADD', 0x02: 'MUL', 0x03: 'SUB', 0x04: 'DIV', 0x05: 'SDIV', From 38af17e46604bc049ceb3a805e4448a41a76d602 Mon Sep 17 00:00:00 2001 From: alexvozhak Date: Mon, 9 Sep 2024 00:25:49 +0300 Subject: [PATCH 074/118] removed redundant log message --- diffyscan/utils/encoder.py | 1 - 1 file changed, 1 deletion(-) diff --git a/diffyscan/utils/encoder.py b/diffyscan/utils/encoder.py index 5d110c7..6190738 100644 --- a/diffyscan/utils/encoder.py +++ b/diffyscan/utils/encoder.py @@ -89,7 +89,6 @@ def encode_constructor_arguments(constructor_abi, constructor_config_args): (len(constructor_calldata) // 64 + 1) * 32, "064x" ) constructor_calldata += dynamic_type_length - logger.info(f"dynamic_type_length {dynamic_type_length}") compl_data.append(encode_tuple(args_tuple_types, arg_value)) else: constructor_calldata += encode_tuple(args_tuple_types, arg_value) From d62048bf552f65336dfde5f9e4a308b06223d027 Mon Sep 17 00:00:00 2001 From: alexvozhak Date: Mon, 9 Sep 2024 00:27:23 +0300 Subject: [PATCH 075/118] added mypy and types-requests into package.json --- package.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 4871712..aef162d 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,9 @@ { "devDependencies": { "prettier": "^3.3.0", - "prettier-plugin-solidity": "^1.3.1" + "prettier-plugin-solidity": "^1.3.1", + "mypy": "^1.11.2", + "types-requests": "^2.2.2" }, "dependencies": { "hardhat": "^2.22.7", From a282c07f7969bec7ca9854a158c73c3a031c981e Mon Sep 17 00:00:00 2001 From: alexvozhak Date: Mon, 9 Sep 2024 00:34:10 +0300 Subject: [PATCH 076/118] fixed alignment --- diffyscan/utils/explorer.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/diffyscan/utils/explorer.py b/diffyscan/utils/explorer.py index 1189b9e..d5eb0b5 100644 --- a/diffyscan/utils/explorer.py +++ b/diffyscan/utils/explorer.py @@ -144,7 +144,8 @@ def get_contract_from_explorer( contract_name_from_etherscan = result["name"] if contract_name_from_etherscan != contract_name_from_config: raise ExplorerError( - f"Contract name in config does not match with Blockchain explorer {contract_address}: {contract_name_from_config} != {contract_name_from_etherscan}", + f"Contract name in config does not match with Blockchain explorer {contract_address}: \ + {contract_name_from_config} != {contract_name_from_etherscan}", ) return result From 954ce599dc2c8ae359b098e2a840e39cbb625895 Mon Sep 17 00:00:00 2001 From: alexvozhak Date: Mon, 9 Sep 2024 00:51:59 +0300 Subject: [PATCH 077/118] moved finding urls from config to env --- config_samples/lido_dao_holesky_config.json | 2 -- config_samples/lido_dao_sepolia_config.json | 2 -- diffyscan/diffyscan.py | 34 +++++++++++++++------ diffyscan/utils/custom_types.py | 2 -- diffyscan/utils/hardhat.py | 20 ++++++++---- 5 files changed, 38 insertions(+), 22 deletions(-) diff --git a/config_samples/lido_dao_holesky_config.json b/config_samples/lido_dao_holesky_config.json index 6888e8b..538a088 100644 --- a/config_samples/lido_dao_holesky_config.json +++ b/config_samples/lido_dao_holesky_config.json @@ -116,8 +116,6 @@ "bytecode_comparison": { "raise_exception": true, "hardhat_config_name": "holesky_hardhat.config.js", - "local_RPC_URL": "http://127.0.0.1:7545", - "remote_RPC_URL": "https://ethereum-holesky.blockpi.network/v1/rpc/public", "constructor_calldata": { "0x28FAB2059C713A7F9D8c86Db49f9bb0e96Af1ef8": "000000000000000000000000ab89ed3d8f31bcf8bb7de53f02084d1e6f043d34000000000000000000000000e92329ec7ddb11d25e25b3c21eebf11f15eb325d00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" }, diff --git a/config_samples/lido_dao_sepolia_config.json b/config_samples/lido_dao_sepolia_config.json index 1ef8cf1..b8fe72f 100644 --- a/config_samples/lido_dao_sepolia_config.json +++ b/config_samples/lido_dao_sepolia_config.json @@ -101,8 +101,6 @@ "bytecode_comparison": { "raise_exception": true, "hardhat_config_name": "sepolia_hardhat.config.js", - "local_RPC_URL": "http://127.0.0.1:7545", - "remote_RPC_URL": "https://sepolia.drpc.org", "constructor_calldata": { "0x8f6254332f69557A72b0DA2D5F0Bc07d4CA991E7": "000000000000000000000000f30a674935479cc6f2254ba65d7534eab8bd6ba20000000000000000000000006885e36bfcb68cb383dfe90023a462c03bcb2ae500000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" }, diff --git a/diffyscan/diffyscan.py b/diffyscan/diffyscan.py index 10c7b0d..ce5098a 100755 --- a/diffyscan/diffyscan.py +++ b/diffyscan/diffyscan.py @@ -39,13 +39,14 @@ def run_bytecode_diff( contract_address_from_config, contract_name_from_config, contract_source_code, - binary_config, + is_need_raise_exception, deployer_account, + local_rpc_url, + remote_rpc_url, ): address_name = f"{contract_address_from_config} : {contract_name_from_config}" logger.divider() logger.info(f"Binary bytecode comparion started for {address_name}") - is_need_raise_exception = binary_config["raise_exception"] CustomExceptions.ExceptionHandler.initialize(is_need_raise_exception) try: target_compiled_contract = compile_contract_from_explorer(contract_source_code) @@ -54,9 +55,8 @@ def run_bytecode_diff( target_compiled_contract ) - remote_RPC_URL = binary_config["remote_RPC_URL"] remote_deployed_bytecode = get_bytecode_from_node( - contract_address_from_config, remote_RPC_URL + contract_address_from_config, remote_rpc_url ) if match_bytecode( @@ -79,11 +79,11 @@ def run_bytecode_diff( contract_creation_code += calldata local_contract_address = deploy_contract( - binary_config["local_RPC_URL"], deployer_account, contract_creation_code + local_rpc_url, deployer_account, contract_creation_code ) local_deployed_bytecode = get_bytecode_from_node( - local_contract_address, binary_config["local_RPC_URL"] + local_contract_address, local_rpc_url ) match_bytecode( @@ -236,6 +236,7 @@ def process_config( logger.warn( f'Failed to find explorer token in env ("ETHERSCAN_EXPLORER_TOKEN")' ) + if not skip_binary_comparison: if "bytecode_comparison" not in config: raise ValueError(f'Failed to find "bytecode_comparison" section in config') @@ -244,12 +245,23 @@ def process_config( if not github_api_token: raise ValueError("GITHUB_API_TOKEN variable is not set") + local_rpc_url = os.getenv("LOCAL_RPC_URL", "") + if not local_rpc_url: + raise ValueError("LOCAL_RPC_URL variable is not set") + + remote_rpc_url = os.getenv("REMOTE_RPC_URL", "") + if not remote_rpc_url: + raise ValueError("REMOTE_RPC_URL variable is not set") + try: if not skip_binary_comparison: - hardhat.start(path, config["bytecode_comparison"]) - deployer_account = get_account( - config["bytecode_comparison"]["local_RPC_URL"] + hardhat.start( + path, + config["bytecode_comparison"]["hardhat_config_name"], + local_rpc_url, + remote_rpc_url, ) + deployer_account = get_account(local_rpc_url) for contract_address, contract_name in config["contracts"].items(): contract_code = get_contract_from_explorer( @@ -271,8 +283,10 @@ def process_config( contract_address, contract_name, contract_code, - config["bytecode_comparison"], + config["bytecode_comparison"]["raise_exception"], deployer_account, + local_rpc_url, + remote_rpc_url, ) except KeyboardInterrupt: logger.info(f"Keyboard interrupt by user") diff --git a/diffyscan/utils/custom_types.py b/diffyscan/utils/custom_types.py index e9ff4ee..bcce14a 100644 --- a/diffyscan/utils/custom_types.py +++ b/diffyscan/utils/custom_types.py @@ -4,8 +4,6 @@ class BinartConfig(TypedDict): raise_exception: bool hardhat_config_name: str - local_RPC_URL: str - remote_RPC_URL: str constructor_calldata: set constructor_args: set diff --git a/diffyscan/utils/hardhat.py b/diffyscan/utils/hardhat.py index 9f2ee0f..929b5cd 100644 --- a/diffyscan/utils/hardhat.py +++ b/diffyscan/utils/hardhat.py @@ -20,24 +20,32 @@ def is_port_in_use(self, parsed_url) -> bool: with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: return s.connect_ex((parsed_url.hostname, parsed_url.port)) == 0 - def start(self, main_config_relative_path: str, binary_config): - parsed_url = urlparse(binary_config["local_RPC_URL"]) + def start( + self, + main_config_relative_path: str, + hardhat_config_name: str, + local_rpc_url: str, + remote_rpc_url: str, + ): + parsed_url = urlparse(local_rpc_url) hardhat_config_relative_path = Hardhat.get_config_path( os.path.dirname(main_config_relative_path), "hardhat_configs", - binary_config["hardhat_config_name"], + hardhat_config_name, ) local_node_command = ( f"npx hardhat node --hostname {parsed_url.hostname} " f"--port {parsed_url.port} " f"--config {hardhat_config_relative_path} " - f"--fork {binary_config["remote_RPC_URL"]}" + f"--fork {remote_rpc_url}" ) logger.info(f'Trying to start Hardhat: "{local_node_command}"') is_port_used = self.is_port_in_use(parsed_url) if is_port_used: - answer = input(f'Port {parsed_url.port} is busy. Kill the app instance occupying the port? write "yes": ') + answer = input( + f'Port {parsed_url.port} is busy. Kill the app instance occupying the port? write "yes": ' + ) if answer.lower() == "yes": return_code = subprocess.call( f"exec npx kill-port {parsed_url.port}", shell=True @@ -68,7 +76,7 @@ def stop(self): os.kill(self.sub_process.pid, signal.SIGTERM) logger.info(f"Hardhat stopped, PID {self.sub_process.pid}") - @staticmethod + @staticmethod def get_config_path(from_path: str, to_path: str, filename: str) -> str: return os.path.normpath(os.path.join(from_path, os.pardir, to_path, filename)) From 0da6b397aee12d95c251b89d45a5c5b07f111d67 Mon Sep 17 00:00:00 2001 From: alexvozhak Date: Mon, 9 Sep 2024 01:02:06 +0300 Subject: [PATCH 078/118] fix after refactor --- diffyscan/diffyscan.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/diffyscan/diffyscan.py b/diffyscan/diffyscan.py index ce5098a..ba5d5ec 100755 --- a/diffyscan/diffyscan.py +++ b/diffyscan/diffyscan.py @@ -39,7 +39,7 @@ def run_bytecode_diff( contract_address_from_config, contract_name_from_config, contract_source_code, - is_need_raise_exception, + config, deployer_account, local_rpc_url, remote_rpc_url, @@ -47,7 +47,9 @@ def run_bytecode_diff( address_name = f"{contract_address_from_config} : {contract_name_from_config}" logger.divider() logger.info(f"Binary bytecode comparion started for {address_name}") - CustomExceptions.ExceptionHandler.initialize(is_need_raise_exception) + CustomExceptions.ExceptionHandler.initialize( + config["bytecode_comparison"]["raise_exception"] + ) try: target_compiled_contract = compile_contract_from_explorer(contract_source_code) @@ -73,7 +75,7 @@ def run_bytecode_diff( calldata = get_calldata( contract_address_from_config, target_compiled_contract, - binary_config, + config["bytecode_comparison"], ) contract_creation_code += calldata @@ -283,7 +285,7 @@ def process_config( contract_address, contract_name, contract_code, - config["bytecode_comparison"]["raise_exception"], + config, deployer_account, local_rpc_url, remote_rpc_url, From 03210d6ffb73e67ad255bf65f137cd8ba87bc474 Mon Sep 17 00:00:00 2001 From: alexvozhak Date: Mon, 9 Sep 2024 01:21:23 +0300 Subject: [PATCH 079/118] added ExceptionHandler for source diff also --- config_samples/lido_dao_holesky_config.json | 2 +- config_samples/lido_dao_sepolia_config.json | 2 +- diffyscan/diffyscan.py | 116 ++++++++++---------- diffyscan/utils/common.py | 10 +- diffyscan/utils/custom_types.py | 2 +- 5 files changed, 66 insertions(+), 66 deletions(-) diff --git a/config_samples/lido_dao_holesky_config.json b/config_samples/lido_dao_holesky_config.json index 538a088..a160ead 100644 --- a/config_samples/lido_dao_holesky_config.json +++ b/config_samples/lido_dao_holesky_config.json @@ -113,8 +113,8 @@ "relative_root": "contracts" } }, + "raise_exception": true, "bytecode_comparison": { - "raise_exception": true, "hardhat_config_name": "holesky_hardhat.config.js", "constructor_calldata": { "0x28FAB2059C713A7F9D8c86Db49f9bb0e96Af1ef8": "000000000000000000000000ab89ed3d8f31bcf8bb7de53f02084d1e6f043d34000000000000000000000000e92329ec7ddb11d25e25b3c21eebf11f15eb325d00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" diff --git a/config_samples/lido_dao_sepolia_config.json b/config_samples/lido_dao_sepolia_config.json index b8fe72f..25c3f64 100644 --- a/config_samples/lido_dao_sepolia_config.json +++ b/config_samples/lido_dao_sepolia_config.json @@ -98,8 +98,8 @@ "relative_root": "contracts" } }, + "raise_exception": true, "bytecode_comparison": { - "raise_exception": true, "hardhat_config_name": "sepolia_hardhat.config.js", "constructor_calldata": { "0x8f6254332f69557A72b0DA2D5F0Bc07d4CA991E7": "000000000000000000000000f30a674935479cc6f2254ba65d7534eab8bd6ba20000000000000000000000006885e36bfcb68cb383dfe90023a462c03bcb2ae500000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" diff --git a/diffyscan/diffyscan.py b/diffyscan/diffyscan.py index ba5d5ec..a3444fa 100755 --- a/diffyscan/diffyscan.py +++ b/diffyscan/diffyscan.py @@ -47,54 +47,48 @@ def run_bytecode_diff( address_name = f"{contract_address_from_config} : {contract_name_from_config}" logger.divider() logger.info(f"Binary bytecode comparion started for {address_name}") - CustomExceptions.ExceptionHandler.initialize( - config["bytecode_comparison"]["raise_exception"] - ) - try: - target_compiled_contract = compile_contract_from_explorer(contract_source_code) + target_compiled_contract = compile_contract_from_explorer(contract_source_code) - contract_creation_code, deployed_bytecode, immutables = parse_compiled_contract( - target_compiled_contract - ) + contract_creation_code, deployed_bytecode, immutables = parse_compiled_contract( + target_compiled_contract + ) - remote_deployed_bytecode = get_bytecode_from_node( - contract_address_from_config, remote_rpc_url - ) + remote_deployed_bytecode = get_bytecode_from_node( + contract_address_from_config, remote_rpc_url + ) - if match_bytecode( - deployed_bytecode, - remote_deployed_bytecode, - immutables, - ): - return + if match_bytecode( + deployed_bytecode, + remote_deployed_bytecode, + immutables, + ): + return - logger.info( - f"Trying to check bytecode via using calldata and deploying into local node" - ) + logger.info( + f"Trying to check bytecode via using calldata and deploying into local node" + ) - calldata = get_calldata( - contract_address_from_config, - target_compiled_contract, - config["bytecode_comparison"], - ) + calldata = get_calldata( + contract_address_from_config, + target_compiled_contract, + config["bytecode_comparison"], + ) - contract_creation_code += calldata + contract_creation_code += calldata - local_contract_address = deploy_contract( - local_rpc_url, deployer_account, contract_creation_code - ) + local_contract_address = deploy_contract( + local_rpc_url, deployer_account, contract_creation_code + ) - local_deployed_bytecode = get_bytecode_from_node( - local_contract_address, local_rpc_url - ) + local_deployed_bytecode = get_bytecode_from_node( + local_contract_address, local_rpc_url + ) - match_bytecode( - local_deployed_bytecode, - remote_deployed_bytecode, - immutables, - ) - except CustomExceptions.BaseCustomException as custom_exc: - CustomExceptions.ExceptionHandler.raise_exception_or_log(custom_exc) + match_bytecode( + local_deployed_bytecode, + remote_deployed_bytecode, + immutables, + ) def run_source_diff( @@ -255,6 +249,8 @@ def process_config( if not remote_rpc_url: raise ValueError("REMOTE_RPC_URL variable is not set") + CustomExceptions.ExceptionHandler.initialize(config["raise_exception"]) + try: if not skip_binary_comparison: hardhat.start( @@ -266,32 +262,36 @@ def process_config( deployer_account = get_account(local_rpc_url) for contract_address, contract_name in config["contracts"].items(): - contract_code = get_contract_from_explorer( - explorer_token, - config["explorer_hostname"], - contract_address, - contract_name, - ) - run_source_diff( - contract_address, - contract_code, - config, - github_api_token, - recursive_parsing, - unify_formatting, - ) - if not skip_binary_comparison: - run_bytecode_diff( + try: + contract_code = get_contract_from_explorer( + explorer_token, + config["explorer_hostname"], contract_address, contract_name, + ) + run_source_diff( + contract_address, contract_code, config, - deployer_account, - local_rpc_url, - remote_rpc_url, + github_api_token, + recursive_parsing, + unify_formatting, ) + if not skip_binary_comparison: + run_bytecode_diff( + contract_address, + contract_name, + contract_code, + config, + deployer_account, + local_rpc_url, + remote_rpc_url, + ) + except CustomExceptions.BaseCustomException as custom_exc: + CustomExceptions.ExceptionHandler.raise_exception_or_log(custom_exc) except KeyboardInterrupt: logger.info(f"Keyboard interrupt by user") + finally: if not skip_binary_comparison: hardhat.stop() diff --git a/diffyscan/utils/common.py b/diffyscan/utils/common.py index b3a860c..429d348 100644 --- a/diffyscan/utils/common.py +++ b/diffyscan/utils/common.py @@ -9,7 +9,7 @@ from .logger import logger from .custom_types import Config -from .custom_exceptions import NodeError +from .custom_exceptions import NodeError, ExplorerError def load_env(variable_name, required=True, masked=False): @@ -40,13 +40,13 @@ def fetch(url, headers=None): response = requests.get(url, headers=headers) response.raise_for_status() except requests.exceptions.HTTPError as http_err: - raise NodeError(f"HTTP error occurred: {http_err}") + raise ExplorerError(f"HTTP error occurred: {http_err}") except requests.exceptions.ConnectionError as conn_err: - raise NodeError(f"Connection error occurred: {conn_err}") + raise ExplorerError(f"Connection error occurred: {conn_err}") except requests.exceptions.Timeout as timeout_err: - raise NodeError(f"Timeout error occurred: {timeout_err}") + raise ExplorerError(f"Timeout error occurred: {timeout_err}") except requests.exceptions.RequestException as req_err: - raise NodeError(f"Request exception occurred: {req_err}") + raise ExplorerError(f"Request exception occurred: {req_err}") return response diff --git a/diffyscan/utils/custom_types.py b/diffyscan/utils/custom_types.py index bcce14a..bc721ef 100644 --- a/diffyscan/utils/custom_types.py +++ b/diffyscan/utils/custom_types.py @@ -2,7 +2,6 @@ class BinartConfig(TypedDict): - raise_exception: bool hardhat_config_name: str constructor_calldata: set constructor_args: set @@ -16,3 +15,4 @@ class Config(TypedDict): explorer_hostname: str explorer_token_env_var: str bytecode_comparison: BinartConfig + raise_exception: bool From e85e0662db2616cb187c0231da65e330ef38156d Mon Sep 17 00:00:00 2001 From: alexvozhak Date: Mon, 9 Sep 2024 01:30:32 +0300 Subject: [PATCH 080/118] fixed imports --- diffyscan/utils/binary_verifier.py | 2 +- diffyscan/utils/explorer.py | 9 ++++++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/diffyscan/utils/binary_verifier.py b/diffyscan/utils/binary_verifier.py index c0dd0b2..20a30c2 100644 --- a/diffyscan/utils/binary_verifier.py +++ b/diffyscan/utils/binary_verifier.py @@ -1,4 +1,4 @@ -from .logger import * +from .logger import logger, bgYellow, bgRed, bgGreen, red, green, to_hex from .constants import OPCODES from .custom_exceptions import BinVerifierError diff --git a/diffyscan/utils/explorer.py b/diffyscan/utils/explorer.py index d5eb0b5..d0e8669 100644 --- a/diffyscan/utils/explorer.py +++ b/diffyscan/utils/explorer.py @@ -1,9 +1,16 @@ import json import sys +import os from .common import fetch from .logger import logger -from .compiler import * +from .compiler import ( + get_solc_native_platform_from_os, + get_compiler_info, + prepare_compiler, + compile_contracts, + get_target_compiled_contract, +) from .constants import SOLC_DIR from .custom_exceptions import ExplorerError From d02490df26d72da90875b5ba4c5cbe07172bab75 Mon Sep 17 00:00:00 2001 From: alexvozhak Date: Mon, 9 Sep 2024 01:46:36 +0300 Subject: [PATCH 081/118] fixed custom_exceptions import --- diffyscan/diffyscan.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/diffyscan/diffyscan.py b/diffyscan/diffyscan.py index a3444fa..876d5a3 100755 --- a/diffyscan/diffyscan.py +++ b/diffyscan/diffyscan.py @@ -27,7 +27,7 @@ from .utils.hardhat import hardhat from .utils.node_handler import get_bytecode_from_node, get_account, deploy_contract from .utils.calldata import get_calldata -import utils.custom_exceptions as CustomExceptions +from .utils.custom_exceptions import ExceptionHandler, BaseCustomException __version__ = "0.0.0" @@ -249,7 +249,7 @@ def process_config( if not remote_rpc_url: raise ValueError("REMOTE_RPC_URL variable is not set") - CustomExceptions.ExceptionHandler.initialize(config["raise_exception"]) + ExceptionHandler.initialize(config["raise_exception"]) try: if not skip_binary_comparison: @@ -287,8 +287,8 @@ def process_config( local_rpc_url, remote_rpc_url, ) - except CustomExceptions.BaseCustomException as custom_exc: - CustomExceptions.ExceptionHandler.raise_exception_or_log(custom_exc) + except BaseCustomException as custom_exc: + ExceptionHandler.raise_exception_or_log(custom_exc) except KeyboardInterrupt: logger.info(f"Keyboard interrupt by user") From a7e135495514b99245163a0093ae3d6a10dc98d4 Mon Sep 17 00:00:00 2001 From: alexvozhak Date: Tue, 10 Sep 2024 01:49:04 +0300 Subject: [PATCH 082/118] fixed remarks in binary_verifier --- diffyscan/utils/binary_verifier.py | 102 ++++++++++++++--------------- diffyscan/utils/constants.py | 13 ++++ 2 files changed, 62 insertions(+), 53 deletions(-) diff --git a/diffyscan/utils/binary_verifier.py b/diffyscan/utils/binary_verifier.py index 20a30c2..e19f6d4 100644 --- a/diffyscan/utils/binary_verifier.py +++ b/diffyscan/utils/binary_verifier.py @@ -1,46 +1,43 @@ from .logger import logger, bgYellow, bgRed, bgGreen, red, green, to_hex -from .constants import OPCODES +from .constants import OPCODES, PUSH0, PUSH32 from .custom_exceptions import BinVerifierError -def match_bytecode(actualBytecode, expectedBytecode, immutables): +def format_bytecode(bytecode): + return "0x" + bytecode[2:] if len(bytecode) > 2 else "" + + +def match_bytecode(actual_bytecode, expected_bytecode, immutables): logger.info("Comparing actual code with the expected one...") - actualInstructions = parse(actualBytecode) - expectedInstructions = parse(expectedBytecode) - maxInstructionsCount = max(len(actualInstructions), len(expectedInstructions)) - - differences = [] - for i in range(maxInstructionsCount): - actual = actualInstructions[i] if i < len(actualInstructions) else None - expected = expectedInstructions[i] if i < len(expectedInstructions) else None - if not actual and not expected: - raise BinVerifierError("Invalid instructions data") - elif (actual is not None) and ( - actual.get("bytecode") != expected.get("bytecode") - ): - differences.append(i) - - if not differences: + actual_instructions = parse(actual_bytecode) + expected_instructions = parse(expected_bytecode) + zipped_instructions = list(zip(actual_instructions, expected_instructions)) + is_mismatch = lambda pair: pair[0].get("bytecode") != pair[1].get("bytecode") + mismatches = [ + index for index, pair in enumerate(zipped_instructions) if is_mismatch(pair) + ] + + if not mismatches: logger.okay(f"Bytecodes are fully matched") return True nearLinesCount = 3 - checkpoints = {0, *differences} + checkpoints = {0, *mismatches} - if actualInstructions: - checkpoints.add(len(actualInstructions) - 1) + if actual_instructions: + checkpoints.add(len(actual_instructions) - 1) - if expectedInstructions: - checkpoints.add(len(expectedInstructions) - 1) + if expected_instructions: + checkpoints.add(len(expected_instructions) - 1) for ind in list(checkpoints): - startIndex = max(0, ind - nearLinesCount) - lastIndex = min(ind + nearLinesCount, maxInstructionsCount - 1) - for i in range(startIndex, lastIndex + 1): - checkpoints.add(i) + start_index = max(0, ind - nearLinesCount) + end_index = min(ind + nearLinesCount, len(zipped_instructions) - 1) + + checkpoints.update(range(start_index, end_index + 1)) - checkpointsArray = sorted(list(checkpoints)) + checkpoints = sorted(checkpoints) logger.divider() logger.info(f"0000 00 STOP - both expected and actual bytecode instructions match") @@ -60,18 +57,18 @@ def match_bytecode(actualBytecode, expectedBytecode, immutables): logger.divider() is_matched = True - for i in range(len(checkpointsArray)): - currInd = checkpointsArray[i] - prevInd = checkpointsArray[i - 1] if i > 0 else None - if prevInd and prevInd != currInd - 1: + for previous_index, current_index in zip(checkpoints, checkpoints[1:]): + if previous_index != current_index - 1: print("...") actual = ( - actualInstructions[currInd] if currInd < len(actualInstructions) else None + actual_instructions[current_index] + if current_index < len(actual_instructions) + else None ) expected = ( - expectedInstructions[currInd] - if currInd < len(expectedInstructions) + expected_instructions[current_index] + if current_index < len(expected_instructions) else None ) @@ -79,14 +76,14 @@ def match_bytecode(actualBytecode, expectedBytecode, immutables): params = "0x" + expected["bytecode"][2:] print( logger.red( - f'{to_hex(currInd, 4)} {to_hex(expected["op"]["code"])} {expected["op"]["name"]} {params}' + f'{to_hex(current_index, 4)} {to_hex(expected["op"]["code"])} {expected["op"]["name"]} {params}' ) ) elif actual and not expected: params = "0x" + actual["bytecode"][2:] print( logger.green( - f'{to_hex(currInd, 4)} {to_hex(actual["op"]["code"])} {actual["op"]["name"]} {params}' + f'{to_hex(current_index, 4)} {to_hex(actual["op"]["code"])} {actual["op"]["name"]} {params}' ) ) elif actual and expected: @@ -102,27 +99,24 @@ def match_bytecode(actualBytecode, expectedBytecode, immutables): if actual["op"]["name"] == expected["op"]["name"] else bgRed(actual["op"]["name"]) + " " + bgGreen(expected["op"]["name"]) ) - actualParams = ( - "0x" + actual["bytecode"][2:] if len(actual["bytecode"]) > 2 else "" - ) - expectedParams = ( - "0x" + expected["bytecode"][2:] if len(expected["bytecode"]) > 2 else "" - ) - paramsLength = len(expected["bytecode"]) // 2 - 1 - isImmutable = immutables.get(expected["start"] + 1) == paramsLength - if actualParams != expectedParams and not isImmutable: + actual_params = format_bytecode(actual["bytecode"]) + expected_params = format_bytecode(expected["bytecode"]) + + params_length = len(expected["bytecode"]) // 2 - 1 + is_immutable = immutables.get(expected["start"] + 1) == params_length + if actual_params != expected_params and not is_immutable: is_matched = False params = ( - actualParams - if actualParams == expectedParams + actual_params + if actual_params == expected_params else ( - bgYellow(actualParams) + " " + bgGreen(expectedParams) - if isImmutable - else bgRed(actualParams) + " " + bgGreen(expectedParams) + bgYellow(actual_params) + " " + bgGreen(expected_params) + if is_immutable + else bgRed(actual_params) + " " + bgGreen(expected_params) ) ) - print(f"{to_hex(currInd, 4)} {opcode} {opname} {params}") + print(f"{to_hex(current_index, 4)} {opcode} {opname} {params}") else: raise BinVerifierError("Invalid bytecode difference data") if is_matched: @@ -142,7 +136,9 @@ def parse(bytecode): i = 0 while i < len(buffer): opcode = buffer[i] - length = 1 + (opcode - 0x5F if 0x5F <= opcode <= 0x7F else 0) + # if opcode not in OPCODES: + # raise BinVerifierError(f"Unknown opcode {hex(opcode)}") + length = 1 + (opcode - PUSH0 if PUSH0 <= opcode <= PUSH32 else 0) instructions.append( { "start": i, diff --git a/diffyscan/utils/constants.py b/diffyscan/utils/constants.py index 96feb56..8e29898 100644 --- a/diffyscan/utils/constants.py +++ b/diffyscan/utils/constants.py @@ -46,3 +46,16 @@ 0xF5: 'CREATE2', 0xFA: 'STATICCALL', 0xFD: 'REVERT', 0xFE: 'INVALID', 0xFF: 'SELFDESTRUCT', } # fmt: on + + +def get_key_from_value(dictinary: dict, value: str): + keys = [ + dict_key for dict_key, dict_value in dictinary.items() if dict_value == value + ] + if keys: + return keys[0] + return None + + +PUSH0 = get_key_from_value(OPCODES, "PUSH0") +PUSH32 = get_key_from_value(OPCODES, "PUSH32") From eef60b616527846e8a3b3e646e639c919d8f4193 Mon Sep 17 00:00:00 2001 From: alexvozhak Date: Tue, 10 Sep 2024 20:16:24 +0300 Subject: [PATCH 083/118] encoder has become more readable --- diffyscan/utils/encoder.py | 142 +++++++++++++++++++++---------------- 1 file changed, 79 insertions(+), 63 deletions(-) diff --git a/diffyscan/utils/encoder.py b/diffyscan/utils/encoder.py index 6190738..1a64155 100644 --- a/diffyscan/utils/encoder.py +++ b/diffyscan/utils/encoder.py @@ -1,32 +1,35 @@ -from .logger import logger from .custom_exceptions import EncoderError -def encode_address(address): - number = int(address, 16) - formatted_address = f"{number:064x}" - return formatted_address +def to_hex_with_alignment(value: int) -> str: + return format(value, "064x") -def encode_uint256(number): - return format(number, "064x") +def encode_address(address: str) -> str: + number = int(address, 16) + return to_hex_with_alignment(number) -def encode_bytes32(data): +def encode_bytes32(data: str) -> str: return data.replace("0x", "") -def encode_bytes(data): +def encode_bytes(data: str) -> str: bytes_str = data.lstrip("0x") - data_length = len(bytes_str) // 2 + if not bytes_str: + return to_hex_with_alignment(0) + + # untested, taken from here: + # https://github.com/lidofinance/lido-dao/blob/a9a9ea50b5be7dba33e06d70cc068252ffd01b52/bytecode-verificator/bytecode_verificator.sh#L239 + count_of_bytes_from_hex = len(bytes_str) // 2 encoded_length = 0 - if data_length > 0: + if count_of_bytes_from_hex > 0: encoded_length = ((len(bytes_str) - 1) // 64 + 1) * 64 bytes_str += "0" * (encoded_length - len(bytes_str)) - return format(data_length, "064x") + bytes_str + return to_hex_with_alignment(count_of_bytes_from_hex) + bytes_str -def encode_tuple(types, args): +def encode_tuple(types: list, args: list): args_length = len(types) encoded_offsets = "" encoded_data = "" @@ -36,70 +39,83 @@ def encode_tuple(types, args): if arg_type == "address": encoded_offsets += encode_address(arg_value) elif arg_type == "uint256" or arg_type == "bool" or arg_type == "uint8": - encoded_offsets += encode_uint256(arg_value) + encoded_offsets += to_hex_with_alignment(arg_value) elif arg_type == "bytes32": encoded_offsets += encode_bytes32(arg_value) elif arg_type == "address[]" and not arg_value: - encoded_data += "0" * 64 - offset = format((arg_index + args_length) * 32, "064x") + encoded_data += to_hex_with_alignment(0) + offset = to_hex_with_alignment((arg_index + args_length) * 32) encoded_offsets += offset else: - logger.warn(f"Unknown constructor argument type '{arg_type}'") + raise EncoderError( + f"Unknown constructor argument type '{arg_type}' in tuple" + ) return encoded_offsets + encoded_data -def to_hex_with_alignment(value): - return format(value, "064x") +def encode_dynamic_type(arg_value: str, argument_index: int): + offset_to_start_of_data_part = to_hex_with_alignment((argument_index + 1) * 32) + encoded_value = encode_bytes(arg_value) + return offset_to_start_of_data_part, encoded_value + +def encode_string(arg_length: int, compl_data: list, arg_value: str): + argument_index = arg_length + len(compl_data) + encoded_value = arg_value.encode("utf-8") + offset_to_start_of_data_part = to_hex_with_alignment(argument_index * 32) + encoded_value_length = to_hex_with_alignment(len(encoded_value)) + return ( + offset_to_start_of_data_part, + encoded_value_length, + encoded_value.hex().ljust(64, "0"), + ) -def encode_constructor_arguments(constructor_abi, constructor_config_args): + +def encode_constructor_arguments(constructor_abi: list, constructor_config_args: list): + # see https://docs.soliditylang.org/en/develop/abi-spec.html#contract-abi-specification arg_length = len(constructor_abi) constructor_calldata = "" compl_data = [] - if arg_length > 0: - for argument_index in range(arg_length): - arg_type = constructor_abi[argument_index]["type"] - 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": - constructor_calldata += encode_uint256(arg_value) - elif arg_type == "bytes32": - constructor_calldata += encode_bytes32(arg_value) - elif arg_type == "bytes": - data = format((argument_index + 1) * 32, "064x") - constructor_calldata += data - data2 = encode_bytes(arg_value) - compl_data.append(data2) - elif arg_type == "string": - offset_arg = to_hex_with_alignment((arg_length + len(compl_data)) * 32) - constructor_calldata += offset_arg - length_arg = to_hex_with_alignment(len(arg_value.encode("utf-8"))) - hex_text = arg_value.encode("utf-8").hex().ljust(64, "0") - compl_data.append(length_arg) - compl_data.append(hex_text) - elif arg_type == "tuple": - args_tuple_types = [ - component["type"] - for component in constructor_abi[argument_index]["components"] - ] - if len(args_tuple_types) and args_tuple_types[0] == "address[]": - dynamic_type_length = format( - (len(constructor_calldata) // 64 + 1) * 32, "064x" - ) - constructor_calldata += dynamic_type_length - compl_data.append(encode_tuple(args_tuple_types, arg_value)) - else: - constructor_calldata += encode_tuple(args_tuple_types, arg_value) - elif arg_type.endswith("[]"): - data = format((argument_index + 1) * 32, "064x") - constructor_calldata += data - data2 = encode_bytes(arg_value) - compl_data.append(data2) + for argument_index in range(arg_length): + arg_type = constructor_abi[argument_index]["type"] + 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": + constructor_calldata += to_hex_with_alignment(arg_value) + elif arg_type == "bytes32": + constructor_calldata += encode_bytes32(arg_value) + elif arg_type == "bytes" or arg_type.endswith("[]"): + offset_to_start_of_data_part, encoded_value = encode_dynamic_type( + arg_value, argument_index + ) + constructor_calldata += offset_to_start_of_data_part + compl_data.append(encoded_value) + elif arg_type == "string": + offset_to_start_of_data_part, encoded_value_length, encoded_value = ( + encode_string(arg_length, compl_data, arg_value) + ) + constructor_calldata += offset_to_start_of_data_part + compl_data.append(encoded_value_length) + compl_data.append(encoded_value) + elif arg_type == "tuple": + args_tuple_types = [ + component["type"] + for component in constructor_abi[argument_index]["components"] + ] + if all(arg == "address[]" for arg in args_tuple_types): + argument_index = len(constructor_calldata) // 64 + offset_to_start_of_data_part = to_hex_with_alignment( + (argument_index + 1) * 32 + ) + constructor_calldata += offset_to_start_of_data_part + compl_data.append(encode_tuple(args_tuple_types, arg_value)) else: - raise EncoderError(f"Unknown constructor argument type: {arg_type}") - for data in compl_data: - constructor_calldata += data + constructor_calldata += encode_tuple(args_tuple_types, arg_value) + else: + raise EncoderError(f"Unknown constructor argument type: {arg_type}") + for offset_to_start_of_data_part in compl_data: + constructor_calldata += offset_to_start_of_data_part return constructor_calldata From 12da7c693c21bc2f8f3d9337f145116443e9b6e4 Mon Sep 17 00:00:00 2001 From: alexvozhak Date: Wed, 11 Sep 2024 00:20:19 +0300 Subject: [PATCH 084/118] added invalid local rpc url handling --- diffyscan/diffyscan.py | 2 ++ diffyscan/utils/hardhat.py | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/diffyscan/diffyscan.py b/diffyscan/diffyscan.py index 876d5a3..a87afce 100755 --- a/diffyscan/diffyscan.py +++ b/diffyscan/diffyscan.py @@ -244,6 +244,8 @@ def process_config( local_rpc_url = os.getenv("LOCAL_RPC_URL", "") if not local_rpc_url: raise ValueError("LOCAL_RPC_URL variable is not set") + if not local_rpc_url.startswith("http://"): + local_rpc_url = "http://" + local_rpc_url remote_rpc_url = os.getenv("REMOTE_RPC_URL", "") if not remote_rpc_url: diff --git a/diffyscan/utils/hardhat.py b/diffyscan/utils/hardhat.py index 929b5cd..bd9c87e 100644 --- a/diffyscan/utils/hardhat.py +++ b/diffyscan/utils/hardhat.py @@ -28,6 +28,10 @@ def start( remote_rpc_url: str, ): parsed_url = urlparse(local_rpc_url) + if parsed_url.port == None: + raise HardhatError( + f"Invalid LOCAL_RPC_URL (TCP port not specified): '{local_rpc_url}'" + ) hardhat_config_relative_path = Hardhat.get_config_path( os.path.dirname(main_config_relative_path), "hardhat_configs", From 665a7dc7debd03bfe09c30aba573747dfab756fd Mon Sep 17 00:00:00 2001 From: alexvozhak Date: Wed, 11 Sep 2024 01:26:32 +0300 Subject: [PATCH 085/118] updated readme, added supporting custom hardhat_config.js --- README.md | 57 +++++++++++++++++++ config_samples/lido_dao_holesky_config.json | 2 +- config_samples/lido_dao_sepolia_config.json | 2 +- diffyscan/utils/hardhat.py | 19 +++++-- ...at.config.js => holesky_hardhat_config.js} | 0 ...at.config.js => sepolia_hardhat_config.js} | 0 6 files changed, 73 insertions(+), 7 deletions(-) rename hardhat_configs/{holesky_hardhat.config.js => holesky_hardhat_config.js} (100%) rename hardhat_configs/{sepolia_hardhat.config.js => sepolia_hardhat_config.js} (100%) diff --git a/README.md b/README.md index ee0c5c6..661f608 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,7 @@ Key features: - compare the bytecode compiled and deployed on the forked network locally against remote (see section 'binary_checking' in `./config_samples/lido_dao_sepolia_config.json` as an example) - preprocess solidity sourcecode by means of prettifier solidity plugin before comparing the sources (option `--prettify`) if needed. - preprocess imports to flat paths for Brownie compatibility (option `--support-brownie`) +- exclude binary comparison (option `--skip-binary-comparison`) ## Install @@ -40,6 +41,17 @@ Set your Github token to query API without strict rate limiting, export GITHUB_API_TOKEN= ``` +Set remote RPC URL to validate contract bytecode at remote rpc node, + +```bash +export REMOTE_RPC_URL = +``` + +Set local RPC URL to validate contract bytecode at local rpc node (Hardhat), + +```bash +export LOCAL_RPC_URL = (example `http://127.0.0.1:7545`) + Start script with one of the examples provided (or entire folder of configs) ```bash @@ -67,9 +79,54 @@ Alternatively, create a new config file named `config.json`, "commit": "6bd6b76d1156e20e45d1016f355d154141c7e5b9", "relative_root": "contracts" } + }, + "raise_exception": true, + "bytecode_comparison": { + "hardhat_config_name": "holesky_hardhat.config.js", + "constructor_calldata": { + "0x28FAB2059C713A7F9D8c86Db49f9bb0e96Af1ef8": "000000000000000000000000ab89ed3d8f31bcf8bb7de53f02084d1e6f043d34000000000000000000000000e92329ec7ddb11d25e25b3c21eebf11f15eb325d00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" + }, + "constructor_args": { + "0x28FAB2059C713A7F9D8c86Db49f9bb0e96Af1ef8": [ + "0xab89ED3D8f31bcF8BB7De53F02084d1e6F043D34", + "0xE92329EC7ddB11D25e25b3c21eeBf11f15eB325d", + "" + ], + "0xDba5Ad530425bb1b14EECD76F1b4a517780de537": [ + [ + "0x4E97A3972ce8511D87F334dA17a2C332542a5246", + "0x045dd46212A178428c088573A7d102B9d89a022A", + "0xE73a3602b99f1f913e72F8bdcBC235e206794Ac8", + "0x072f72BE3AcFE2c52715829F2CD9061A6C8fF019", + "0x3F1c547b21f65e10480dE3ad8E19fAAC46C95034", + "0xF0d576c7d934bBeCc68FE15F1c5DAF98ea2B78bb", + "0x072f72BE3AcFE2c52715829F2CD9061A6C8fF019", + "0x4E46BD7147ccf666E1d73A3A456fC7a68de82eCA", + "0xd6EbF043D30A7fe46D1Db32BA90a0A51207FE229", + "0xE92329EC7ddB11D25e25b3c21eeBf11f15eB325d", + "0xffDDF7025410412deaa05E3E1cE68FE53208afcb", + "0xc7cc160b58F8Bb0baC94b80847E2CF2800565C50", + "0xF0179dEC45a37423EAD4FaD5fCb136197872EAd9", + "0xC01fC1F2787687Bc656EAc0356ba9Db6e6b7afb7" + ] + ] + } } } ``` +use `holesky_hardhat.config.js` from `config.json` or create near the diffyscan.py own Hardhat config `hardhat_config.js`: +```json +module.exports = { + solidity: "0.8.9", + networks: { + hardhat: { + chainId: 17000, + blockGasLimit: 92000000, + hardfork: "istanbul", + } + }, +}; +``` Start the script diff --git a/config_samples/lido_dao_holesky_config.json b/config_samples/lido_dao_holesky_config.json index a160ead..a82bfe1 100644 --- a/config_samples/lido_dao_holesky_config.json +++ b/config_samples/lido_dao_holesky_config.json @@ -115,7 +115,7 @@ }, "raise_exception": true, "bytecode_comparison": { - "hardhat_config_name": "holesky_hardhat.config.js", + "hardhat_config_name": "holesky_hardhat_config.js", "constructor_calldata": { "0x28FAB2059C713A7F9D8c86Db49f9bb0e96Af1ef8": "000000000000000000000000ab89ed3d8f31bcf8bb7de53f02084d1e6f043d34000000000000000000000000e92329ec7ddb11d25e25b3c21eebf11f15eb325d00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" }, diff --git a/config_samples/lido_dao_sepolia_config.json b/config_samples/lido_dao_sepolia_config.json index 25c3f64..62acaaa 100644 --- a/config_samples/lido_dao_sepolia_config.json +++ b/config_samples/lido_dao_sepolia_config.json @@ -100,7 +100,7 @@ }, "raise_exception": true, "bytecode_comparison": { - "hardhat_config_name": "sepolia_hardhat.config.js", + "hardhat_config_name": "sepolia_hardhat_config.js", "constructor_calldata": { "0x8f6254332f69557A72b0DA2D5F0Bc07d4CA991E7": "000000000000000000000000f30a674935479cc6f2254ba65d7534eab8bd6ba20000000000000000000000006885e36bfcb68cb383dfe90023a462c03bcb2ae500000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" }, diff --git a/diffyscan/utils/hardhat.py b/diffyscan/utils/hardhat.py index bd9c87e..d0b11dc 100644 --- a/diffyscan/utils/hardhat.py +++ b/diffyscan/utils/hardhat.py @@ -32,15 +32,24 @@ def start( raise HardhatError( f"Invalid LOCAL_RPC_URL (TCP port not specified): '{local_rpc_url}'" ) - hardhat_config_relative_path = Hardhat.get_config_path( - os.path.dirname(main_config_relative_path), - "hardhat_configs", - hardhat_config_name, + + hardhat_config_path = os.path.join( + os.path.dirname(main_config_relative_path), "hardhat_config.js" ) + + if not os.path.isfile(hardhat_config_path): + hardhat_config_path = Hardhat.get_config_path( + os.path.dirname(main_config_relative_path), + "hardhat_configs", + hardhat_config_name, + ) + if not os.path.isfile(hardhat_config_path): + raise HardhatError(f"Failed to find any Hardhat config") + local_node_command = ( f"npx hardhat node --hostname {parsed_url.hostname} " f"--port {parsed_url.port} " - f"--config {hardhat_config_relative_path} " + f"--config {hardhat_config_path} " f"--fork {remote_rpc_url}" ) diff --git a/hardhat_configs/holesky_hardhat.config.js b/hardhat_configs/holesky_hardhat_config.js similarity index 100% rename from hardhat_configs/holesky_hardhat.config.js rename to hardhat_configs/holesky_hardhat_config.js diff --git a/hardhat_configs/sepolia_hardhat.config.js b/hardhat_configs/sepolia_hardhat_config.js similarity index 100% rename from hardhat_configs/sepolia_hardhat.config.js rename to hardhat_configs/sepolia_hardhat_config.js From ab17d3c0d0d895c20280ea06182e0b8180169ab9 Mon Sep 17 00:00:00 2001 From: alexvozhak Date: Wed, 11 Sep 2024 23:49:22 +0300 Subject: [PATCH 086/118] fixed binary comparison logic --- diffyscan/diffyscan.py | 8 +++++--- diffyscan/utils/binary_verifier.py | 16 ++++++++-------- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/diffyscan/diffyscan.py b/diffyscan/diffyscan.py index a87afce..52292a8 100755 --- a/diffyscan/diffyscan.py +++ b/diffyscan/diffyscan.py @@ -46,7 +46,7 @@ def run_bytecode_diff( ): address_name = f"{contract_address_from_config} : {contract_name_from_config}" logger.divider() - logger.info(f"Binary bytecode comparion started for {address_name}") + logger.info(f"Binary bytecode comparison started for {address_name}") target_compiled_contract = compile_contract_from_explorer(contract_source_code) contract_creation_code, deployed_bytecode, immutables = parse_compiled_contract( @@ -57,11 +57,13 @@ def run_bytecode_diff( contract_address_from_config, remote_rpc_url ) - if match_bytecode( + is_fully_matched = match_bytecode( deployed_bytecode, remote_deployed_bytecode, immutables, - ): + ) + + if is_fully_matched: return logger.info( diff --git a/diffyscan/utils/binary_verifier.py b/diffyscan/utils/binary_verifier.py index e19f6d4..b75cfcb 100644 --- a/diffyscan/utils/binary_verifier.py +++ b/diffyscan/utils/binary_verifier.py @@ -56,7 +56,7 @@ def match_bytecode(actual_bytecode, expected_bytecode, immutables): ) logger.divider() - is_matched = True + is_matched_with_excluded_immutables = True for previous_index, current_index in zip(checkpoints, checkpoints[1:]): if previous_index != current_index - 1: print("...") @@ -106,7 +106,7 @@ def match_bytecode(actual_bytecode, expected_bytecode, immutables): params_length = len(expected["bytecode"]) // 2 - 1 is_immutable = immutables.get(expected["start"] + 1) == params_length if actual_params != expected_params and not is_immutable: - is_matched = False + is_matched_with_excluded_immutables = False params = ( actual_params if actual_params == expected_params @@ -119,15 +119,15 @@ def match_bytecode(actual_bytecode, expected_bytecode, immutables): print(f"{to_hex(current_index, 4)} {opcode} {opname} {params}") else: raise BinVerifierError("Invalid bytecode difference data") - if is_matched: + if is_matched_with_excluded_immutables: logger.okay( f"Bytecodes have differences only on the immutable reference position" ) - else: - logger.error( - f"Bytecodes have differences not on the immutable reference position" - ) - return is_matched + return False + + raise BinVerifierError( + f"Bytecodes have differences not on the immutable reference position" + ) def parse(bytecode): From c6af324bd36f7db138f53e6fce2aa6fb7c6db486 Mon Sep 17 00:00:00 2001 From: alexvozhak Date: Thu, 12 Sep 2024 02:29:10 +0300 Subject: [PATCH 087/118] reworked pointing to hardhat config path --- README.md | 16 ++-------------- config_samples/lido_dao_holesky_config.json | 2 +- config_samples/lido_dao_sepolia_config.json | 2 +- diffyscan/diffyscan.py | 3 +-- diffyscan/utils/custom_types.py | 2 +- diffyscan/utils/hardhat.py | 19 ++----------------- 6 files changed, 8 insertions(+), 36 deletions(-) diff --git a/README.md b/README.md index 661f608..e707ed7 100644 --- a/README.md +++ b/README.md @@ -82,7 +82,7 @@ Alternatively, create a new config file named `config.json`, }, "raise_exception": true, "bytecode_comparison": { - "hardhat_config_name": "holesky_hardhat.config.js", + "hardhat_config_path": "../hardhat_configs/holesky_hardhat.config.js", "constructor_calldata": { "0x28FAB2059C713A7F9D8c86Db49f9bb0e96Af1ef8": "000000000000000000000000ab89ed3d8f31bcf8bb7de53f02084d1e6f043d34000000000000000000000000e92329ec7ddb11d25e25b3c21eebf11f15eb325d00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" }, @@ -114,19 +114,7 @@ Alternatively, create a new config file named `config.json`, } } ``` -use `holesky_hardhat.config.js` from `config.json` or create near the diffyscan.py own Hardhat config `hardhat_config.js`: -```json -module.exports = { - solidity: "0.8.9", - networks: { - hardhat: { - chainId: 17000, - blockGasLimit: 92000000, - hardfork: "istanbul", - } - }, -}; -``` +, then insert a correct path to Hardhat config into `hardhat_config_path` config section. Start the script diff --git a/config_samples/lido_dao_holesky_config.json b/config_samples/lido_dao_holesky_config.json index a82bfe1..d76d552 100644 --- a/config_samples/lido_dao_holesky_config.json +++ b/config_samples/lido_dao_holesky_config.json @@ -115,7 +115,7 @@ }, "raise_exception": true, "bytecode_comparison": { - "hardhat_config_name": "holesky_hardhat_config.js", + "hardhat_config_path": "../hardhat_configs/holesky_hardhat_config.js", "constructor_calldata": { "0x28FAB2059C713A7F9D8c86Db49f9bb0e96Af1ef8": "000000000000000000000000ab89ed3d8f31bcf8bb7de53f02084d1e6f043d34000000000000000000000000e92329ec7ddb11d25e25b3c21eebf11f15eb325d00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" }, diff --git a/config_samples/lido_dao_sepolia_config.json b/config_samples/lido_dao_sepolia_config.json index 62acaaa..d584a8f 100644 --- a/config_samples/lido_dao_sepolia_config.json +++ b/config_samples/lido_dao_sepolia_config.json @@ -100,7 +100,7 @@ }, "raise_exception": true, "bytecode_comparison": { - "hardhat_config_name": "sepolia_hardhat_config.js", + "hardhat_config_path": "../hardhat_configs/sepolia_hardhat_config.js", "constructor_calldata": { "0x8f6254332f69557A72b0DA2D5F0Bc07d4CA991E7": "000000000000000000000000f30a674935479cc6f2254ba65d7534eab8bd6ba20000000000000000000000006885e36bfcb68cb383dfe90023a462c03bcb2ae500000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" }, diff --git a/diffyscan/diffyscan.py b/diffyscan/diffyscan.py index 52292a8..30abf1d 100755 --- a/diffyscan/diffyscan.py +++ b/diffyscan/diffyscan.py @@ -258,8 +258,7 @@ def process_config( try: if not skip_binary_comparison: hardhat.start( - path, - config["bytecode_comparison"]["hardhat_config_name"], + config["bytecode_comparison"]["hardhat_config_path"], local_rpc_url, remote_rpc_url, ) diff --git a/diffyscan/utils/custom_types.py b/diffyscan/utils/custom_types.py index bc721ef..fca7728 100644 --- a/diffyscan/utils/custom_types.py +++ b/diffyscan/utils/custom_types.py @@ -2,7 +2,7 @@ class BinartConfig(TypedDict): - hardhat_config_name: str + hardhat_config_path: str constructor_calldata: set constructor_args: set diff --git a/diffyscan/utils/hardhat.py b/diffyscan/utils/hardhat.py index d0b11dc..c5ab9ef 100644 --- a/diffyscan/utils/hardhat.py +++ b/diffyscan/utils/hardhat.py @@ -22,8 +22,7 @@ def is_port_in_use(self, parsed_url) -> bool: def start( self, - main_config_relative_path: str, - hardhat_config_name: str, + hardhat_config_path: str, local_rpc_url: str, remote_rpc_url: str, ): @@ -33,18 +32,8 @@ def start( f"Invalid LOCAL_RPC_URL (TCP port not specified): '{local_rpc_url}'" ) - hardhat_config_path = os.path.join( - os.path.dirname(main_config_relative_path), "hardhat_config.js" - ) - if not os.path.isfile(hardhat_config_path): - hardhat_config_path = Hardhat.get_config_path( - os.path.dirname(main_config_relative_path), - "hardhat_configs", - hardhat_config_name, - ) - if not os.path.isfile(hardhat_config_path): - raise HardhatError(f"Failed to find any Hardhat config") + raise HardhatError(f"Failed to find Hardhat config: {hardhat_config_path}") local_node_command = ( f"npx hardhat node --hostname {parsed_url.hostname} " @@ -89,9 +78,5 @@ def stop(self): os.kill(self.sub_process.pid, signal.SIGTERM) logger.info(f"Hardhat stopped, PID {self.sub_process.pid}") - @staticmethod - def get_config_path(from_path: str, to_path: str, filename: str) -> str: - return os.path.normpath(os.path.join(from_path, os.pardir, to_path, filename)) - hardhat = Hardhat() From 697d90a6de16346f51e82876e1eb4a242e963ca1 Mon Sep 17 00:00:00 2001 From: alexvozhak Date: Thu, 12 Sep 2024 02:35:20 +0300 Subject: [PATCH 088/118] Revert "reworked pointing to hardhat config path" This reverts commit c6af324bd36f7db138f53e6fce2aa6fb7c6db486. --- README.md | 16 ++++++++++++++-- config_samples/lido_dao_holesky_config.json | 2 +- config_samples/lido_dao_sepolia_config.json | 2 +- diffyscan/diffyscan.py | 3 ++- diffyscan/utils/custom_types.py | 2 +- diffyscan/utils/hardhat.py | 19 +++++++++++++++++-- 6 files changed, 36 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index e707ed7..661f608 100644 --- a/README.md +++ b/README.md @@ -82,7 +82,7 @@ Alternatively, create a new config file named `config.json`, }, "raise_exception": true, "bytecode_comparison": { - "hardhat_config_path": "../hardhat_configs/holesky_hardhat.config.js", + "hardhat_config_name": "holesky_hardhat.config.js", "constructor_calldata": { "0x28FAB2059C713A7F9D8c86Db49f9bb0e96Af1ef8": "000000000000000000000000ab89ed3d8f31bcf8bb7de53f02084d1e6f043d34000000000000000000000000e92329ec7ddb11d25e25b3c21eebf11f15eb325d00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" }, @@ -114,7 +114,19 @@ Alternatively, create a new config file named `config.json`, } } ``` -, then insert a correct path to Hardhat config into `hardhat_config_path` config section. +use `holesky_hardhat.config.js` from `config.json` or create near the diffyscan.py own Hardhat config `hardhat_config.js`: +```json +module.exports = { + solidity: "0.8.9", + networks: { + hardhat: { + chainId: 17000, + blockGasLimit: 92000000, + hardfork: "istanbul", + } + }, +}; +``` Start the script diff --git a/config_samples/lido_dao_holesky_config.json b/config_samples/lido_dao_holesky_config.json index d76d552..a82bfe1 100644 --- a/config_samples/lido_dao_holesky_config.json +++ b/config_samples/lido_dao_holesky_config.json @@ -115,7 +115,7 @@ }, "raise_exception": true, "bytecode_comparison": { - "hardhat_config_path": "../hardhat_configs/holesky_hardhat_config.js", + "hardhat_config_name": "holesky_hardhat_config.js", "constructor_calldata": { "0x28FAB2059C713A7F9D8c86Db49f9bb0e96Af1ef8": "000000000000000000000000ab89ed3d8f31bcf8bb7de53f02084d1e6f043d34000000000000000000000000e92329ec7ddb11d25e25b3c21eebf11f15eb325d00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" }, diff --git a/config_samples/lido_dao_sepolia_config.json b/config_samples/lido_dao_sepolia_config.json index d584a8f..62acaaa 100644 --- a/config_samples/lido_dao_sepolia_config.json +++ b/config_samples/lido_dao_sepolia_config.json @@ -100,7 +100,7 @@ }, "raise_exception": true, "bytecode_comparison": { - "hardhat_config_path": "../hardhat_configs/sepolia_hardhat_config.js", + "hardhat_config_name": "sepolia_hardhat_config.js", "constructor_calldata": { "0x8f6254332f69557A72b0DA2D5F0Bc07d4CA991E7": "000000000000000000000000f30a674935479cc6f2254ba65d7534eab8bd6ba20000000000000000000000006885e36bfcb68cb383dfe90023a462c03bcb2ae500000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" }, diff --git a/diffyscan/diffyscan.py b/diffyscan/diffyscan.py index 30abf1d..52292a8 100755 --- a/diffyscan/diffyscan.py +++ b/diffyscan/diffyscan.py @@ -258,7 +258,8 @@ def process_config( try: if not skip_binary_comparison: hardhat.start( - config["bytecode_comparison"]["hardhat_config_path"], + path, + config["bytecode_comparison"]["hardhat_config_name"], local_rpc_url, remote_rpc_url, ) diff --git a/diffyscan/utils/custom_types.py b/diffyscan/utils/custom_types.py index fca7728..bc721ef 100644 --- a/diffyscan/utils/custom_types.py +++ b/diffyscan/utils/custom_types.py @@ -2,7 +2,7 @@ class BinartConfig(TypedDict): - hardhat_config_path: str + hardhat_config_name: str constructor_calldata: set constructor_args: set diff --git a/diffyscan/utils/hardhat.py b/diffyscan/utils/hardhat.py index c5ab9ef..d0b11dc 100644 --- a/diffyscan/utils/hardhat.py +++ b/diffyscan/utils/hardhat.py @@ -22,7 +22,8 @@ def is_port_in_use(self, parsed_url) -> bool: def start( self, - hardhat_config_path: str, + main_config_relative_path: str, + hardhat_config_name: str, local_rpc_url: str, remote_rpc_url: str, ): @@ -32,8 +33,18 @@ def start( f"Invalid LOCAL_RPC_URL (TCP port not specified): '{local_rpc_url}'" ) + hardhat_config_path = os.path.join( + os.path.dirname(main_config_relative_path), "hardhat_config.js" + ) + if not os.path.isfile(hardhat_config_path): - raise HardhatError(f"Failed to find Hardhat config: {hardhat_config_path}") + hardhat_config_path = Hardhat.get_config_path( + os.path.dirname(main_config_relative_path), + "hardhat_configs", + hardhat_config_name, + ) + if not os.path.isfile(hardhat_config_path): + raise HardhatError(f"Failed to find any Hardhat config") local_node_command = ( f"npx hardhat node --hostname {parsed_url.hostname} " @@ -78,5 +89,9 @@ def stop(self): os.kill(self.sub_process.pid, signal.SIGTERM) logger.info(f"Hardhat stopped, PID {self.sub_process.pid}") + @staticmethod + def get_config_path(from_path: str, to_path: str, filename: str) -> str: + return os.path.normpath(os.path.join(from_path, os.pardir, to_path, filename)) + hardhat = Hardhat() From 310963106d27b114f3896fd355c377b352d28b56 Mon Sep 17 00:00:00 2001 From: alexvozhak Date: Thu, 12 Sep 2024 21:57:15 +0300 Subject: [PATCH 089/118] removed redundant contract's calldata --- config_samples/lido_dao_holesky_config.json | 5 ----- config_samples/lido_dao_sepolia_config.json | 5 ----- 2 files changed, 10 deletions(-) diff --git a/config_samples/lido_dao_holesky_config.json b/config_samples/lido_dao_holesky_config.json index a82bfe1..d00d59e 100644 --- a/config_samples/lido_dao_holesky_config.json +++ b/config_samples/lido_dao_holesky_config.json @@ -120,11 +120,6 @@ "0x28FAB2059C713A7F9D8c86Db49f9bb0e96Af1ef8": "000000000000000000000000ab89ed3d8f31bcf8bb7de53f02084d1e6f043d34000000000000000000000000e92329ec7ddb11d25e25b3c21eebf11f15eb325d00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" }, "constructor_args": { - "0x28FAB2059C713A7F9D8c86Db49f9bb0e96Af1ef8": [ - "0xab89ED3D8f31bcF8BB7De53F02084d1e6F043D34", - "0xE92329EC7ddB11D25e25b3c21eeBf11f15eB325d", - "" - ], "0xF0d576c7d934bBeCc68FE15F1c5DAF98ea2B78bb": [ "0x28FAB2059C713A7F9D8c86Db49f9bb0e96Af1ef8", "0x22896Bfc68814BFD855b1a167255eE497006e730", diff --git a/config_samples/lido_dao_sepolia_config.json b/config_samples/lido_dao_sepolia_config.json index 62acaaa..2373a90 100644 --- a/config_samples/lido_dao_sepolia_config.json +++ b/config_samples/lido_dao_sepolia_config.json @@ -105,11 +105,6 @@ "0x8f6254332f69557A72b0DA2D5F0Bc07d4CA991E7": "000000000000000000000000f30a674935479cc6f2254ba65d7534eab8bd6ba20000000000000000000000006885e36bfcb68cb383dfe90023a462c03bcb2ae500000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" }, "constructor_args": { - "0x8f6254332f69557A72b0DA2D5F0Bc07d4CA991E7": [ - "0xf30a674935479cc6f2254ba65d7534eab8bd6ba2", - "0x6885E36BFcb68CB383DfE90023a462C03BCB2AE5", - "" - ], "0x9726CA9AEFF4BC8FB8C084BdAbdB71608248E3f8": [ "0x3e3FE7dBc6B4C189E7128855dD526361c49b40Af" ], From 85afaa73d6e6870324c42a05d1ae851b70d4444e Mon Sep 17 00:00:00 2001 From: alexvozhak Date: Thu, 12 Sep 2024 21:58:44 +0300 Subject: [PATCH 090/118] renamed config field raise_exception to raise_exceptions_on_comparison --- config_samples/lido_dao_holesky_config.json | 2 +- config_samples/lido_dao_sepolia_config.json | 2 +- diffyscan/diffyscan.py | 2 +- diffyscan/utils/custom_types.py | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/config_samples/lido_dao_holesky_config.json b/config_samples/lido_dao_holesky_config.json index d00d59e..4d8185b 100644 --- a/config_samples/lido_dao_holesky_config.json +++ b/config_samples/lido_dao_holesky_config.json @@ -113,7 +113,7 @@ "relative_root": "contracts" } }, - "raise_exception": true, + "raise_exceptions_on_comparison": true, "bytecode_comparison": { "hardhat_config_name": "holesky_hardhat_config.js", "constructor_calldata": { diff --git a/config_samples/lido_dao_sepolia_config.json b/config_samples/lido_dao_sepolia_config.json index 2373a90..e402e45 100644 --- a/config_samples/lido_dao_sepolia_config.json +++ b/config_samples/lido_dao_sepolia_config.json @@ -98,7 +98,7 @@ "relative_root": "contracts" } }, - "raise_exception": true, + "raise_exceptions_on_comparison": true, "bytecode_comparison": { "hardhat_config_name": "sepolia_hardhat_config.js", "constructor_calldata": { diff --git a/diffyscan/diffyscan.py b/diffyscan/diffyscan.py index 52292a8..996ff95 100755 --- a/diffyscan/diffyscan.py +++ b/diffyscan/diffyscan.py @@ -253,7 +253,7 @@ def process_config( if not remote_rpc_url: raise ValueError("REMOTE_RPC_URL variable is not set") - ExceptionHandler.initialize(config["raise_exception"]) + ExceptionHandler.initialize(config["raise_exceptions_on_comparison"]) try: if not skip_binary_comparison: diff --git a/diffyscan/utils/custom_types.py b/diffyscan/utils/custom_types.py index bc721ef..7d6ab9b 100644 --- a/diffyscan/utils/custom_types.py +++ b/diffyscan/utils/custom_types.py @@ -15,4 +15,4 @@ class Config(TypedDict): explorer_hostname: str explorer_token_env_var: str bytecode_comparison: BinartConfig - raise_exception: bool + raise_exceptions_on_comparison: bool From b0e7fe2b6022233564e6521609d88b8288fd7a9e Mon Sep 17 00:00:00 2001 From: alexvozhak Date: Thu, 12 Sep 2024 23:24:11 +0300 Subject: [PATCH 091/118] added default hardhat config support --- README.md | 7 ++++--- config_samples/lido_dao_holesky_config.json | 1 - config_samples/lido_dao_sepolia_config.json | 1 - diffyscan/diffyscan.py | 19 ++++++++++++++++--- diffyscan/utils/constants.py | 1 + diffyscan/utils/hardhat.py | 19 +++---------------- 6 files changed, 24 insertions(+), 24 deletions(-) diff --git a/README.md b/README.md index 661f608..3ae5f9e 100644 --- a/README.md +++ b/README.md @@ -10,10 +10,11 @@ Diff deployed EVM-compatible smart contract sourcecode and bytecode against the Key features: - retrieve and diff sources from the GitHub repo against the queried ones from a blockscan service (e.g. Etherscan) -- compare the bytecode compiled and deployed on the forked network locally against remote (see section 'binary_checking' in `./config_samples/lido_dao_sepolia_config.json` as an example) +- compare the bytecode compiled and deployed on the forked network locally against remote (see section 'bytecode_comparison' in `./config_samples/lido_dao_sepolia_config.json` as an example) - preprocess solidity sourcecode by means of prettifier solidity plugin before comparing the sources (option `--prettify`) if needed. - preprocess imports to flat paths for Brownie compatibility (option `--support-brownie`) - exclude binary comparison (option `--skip-binary-comparison`) +- provide own Hardhat config as optional argument ## Install @@ -58,7 +59,7 @@ Start script with one of the examples provided (or entire folder of configs) diffyscan config_samples/lido_dao_sepolia_config.json ``` -Alternatively, create a new config file named `config.json`, +Alternatively, create a new config file named `config.json` near the diffyscan.py, ```json { @@ -114,7 +115,7 @@ Alternatively, create a new config file named `config.json`, } } ``` -use `holesky_hardhat.config.js` from `config.json` or create near the diffyscan.py own Hardhat config `hardhat_config.js`: +then create a new Hardhat config file named `hardhat_config.js` near the diffyscan.py ```json module.exports = { solidity: "0.8.9", diff --git a/config_samples/lido_dao_holesky_config.json b/config_samples/lido_dao_holesky_config.json index 4d8185b..b5a842d 100644 --- a/config_samples/lido_dao_holesky_config.json +++ b/config_samples/lido_dao_holesky_config.json @@ -115,7 +115,6 @@ }, "raise_exceptions_on_comparison": true, "bytecode_comparison": { - "hardhat_config_name": "holesky_hardhat_config.js", "constructor_calldata": { "0x28FAB2059C713A7F9D8c86Db49f9bb0e96Af1ef8": "000000000000000000000000ab89ed3d8f31bcf8bb7de53f02084d1e6f043d34000000000000000000000000e92329ec7ddb11d25e25b3c21eebf11f15eb325d00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" }, diff --git a/config_samples/lido_dao_sepolia_config.json b/config_samples/lido_dao_sepolia_config.json index e402e45..4f83205 100644 --- a/config_samples/lido_dao_sepolia_config.json +++ b/config_samples/lido_dao_sepolia_config.json @@ -100,7 +100,6 @@ }, "raise_exceptions_on_comparison": true, "bytecode_comparison": { - "hardhat_config_name": "sepolia_hardhat_config.js", "constructor_calldata": { "0x8f6254332f69557A72b0DA2D5F0Bc07d4CA991E7": "000000000000000000000000f30a674935479cc6f2254ba65d7534eab8bd6ba20000000000000000000000006885e36bfcb68cb383dfe90023a462c03bcb2ae500000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" }, diff --git a/diffyscan/diffyscan.py b/diffyscan/diffyscan.py index 996ff95..8ce31fd 100755 --- a/diffyscan/diffyscan.py +++ b/diffyscan/diffyscan.py @@ -9,6 +9,7 @@ from .utils.constants import ( DIFFS_DIR, DEFAULT_CONFIG_PATH, + DEFAULT_HARDHAT_CONFIG_PATH, START_TIME, ) from .utils.explorer import ( @@ -213,6 +214,7 @@ def run_source_diff( def process_config( path: str, + hardhat_config_path: str, recursive_parsing: bool, unify_formatting: bool, skip_binary_comparison: bool, @@ -258,8 +260,7 @@ def process_config( try: if not skip_binary_comparison: hardhat.start( - path, - config["bytecode_comparison"]["hardhat_config_name"], + hardhat_config_path, local_rpc_url, remote_rpc_url, ) @@ -309,6 +310,9 @@ def parse_arguments(): parser.add_argument( "path", nargs="?", default=None, help="Path to config or directory with configs" ) + parser.add_argument( + "hardhat_path", nargs="?", default=None, help="Path to Hardhat config" + ) parser.add_argument( "--yes", "-Y", @@ -345,16 +349,24 @@ def main(): return logger.info("Welcome to Diffyscan!") logger.divider() + hardhat_config_path = ( + DEFAULT_HARDHAT_CONFIG_PATH if args.hardhat_path is None else args.hardhat_path + ) if args.path is None: process_config( DEFAULT_CONFIG_PATH, + hardhat_config_path, args.support_brownie, args.prettify, args.skip_binary_comparison, ) elif os.path.isfile(args.path): process_config( - args.path, args.support_brownie, args.prettify, args.skip_binary_comparison + args.path, + hardhat_config_path, + args.support_brownie, + args.prettify, + args.skip_binary_comparison, ) elif os.path.isdir(args.path): for filename in os.listdir(args.path): @@ -362,6 +374,7 @@ def main(): if os.path.isfile(config_path): process_config( config_path, + hardhat_config_path, args.support_brownie, args.prettify, args.skip_binary_comparison, diff --git a/diffyscan/utils/constants.py b/diffyscan/utils/constants.py index 8e29898..5c640c6 100644 --- a/diffyscan/utils/constants.py +++ b/diffyscan/utils/constants.py @@ -8,6 +8,7 @@ DIFFS_DIR = f"{DIGEST_DIR}/{START_TIME_INT}/diffs" LOGS_PATH = f"{DIGEST_DIR}/{START_TIME_INT}/logs.txt" DEFAULT_CONFIG_PATH = "config.json" +DEFAULT_HARDHAT_CONFIG_PATH = "hardhat_config.js" SOLC_DIR = os.path.join(tempfile.gettempdir(), "solc_builds") diff --git a/diffyscan/utils/hardhat.py b/diffyscan/utils/hardhat.py index d0b11dc..35c2091 100644 --- a/diffyscan/utils/hardhat.py +++ b/diffyscan/utils/hardhat.py @@ -22,8 +22,7 @@ def is_port_in_use(self, parsed_url) -> bool: def start( self, - main_config_relative_path: str, - hardhat_config_name: str, + hardhat_config_path: str, local_rpc_url: str, remote_rpc_url: str, ): @@ -33,18 +32,10 @@ def start( f"Invalid LOCAL_RPC_URL (TCP port not specified): '{local_rpc_url}'" ) - hardhat_config_path = os.path.join( - os.path.dirname(main_config_relative_path), "hardhat_config.js" - ) - if not os.path.isfile(hardhat_config_path): - hardhat_config_path = Hardhat.get_config_path( - os.path.dirname(main_config_relative_path), - "hardhat_configs", - hardhat_config_name, + raise HardhatError( + f"Failed to find Hardhat config by path '{hardhat_config_path}'" ) - if not os.path.isfile(hardhat_config_path): - raise HardhatError(f"Failed to find any Hardhat config") local_node_command = ( f"npx hardhat node --hostname {parsed_url.hostname} " @@ -89,9 +80,5 @@ def stop(self): os.kill(self.sub_process.pid, signal.SIGTERM) logger.info(f"Hardhat stopped, PID {self.sub_process.pid}") - @staticmethod - def get_config_path(from_path: str, to_path: str, filename: str) -> str: - return os.path.normpath(os.path.join(from_path, os.pardir, to_path, filename)) - hardhat = Hardhat() From b616c9670fb755e502281dd68152a0f2884dd354 Mon Sep 17 00:00:00 2001 From: alexvozhak Date: Thu, 12 Sep 2024 23:45:14 +0300 Subject: [PATCH 092/118] extended response exception message --- diffyscan/utils/node_handler.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/diffyscan/utils/node_handler.py b/diffyscan/utils/node_handler.py index 8aafb3a..d1d0f05 100644 --- a/diffyscan/utils/node_handler.py +++ b/diffyscan/utils/node_handler.py @@ -22,7 +22,7 @@ def get_bytecode_from_node(contract_address, rpc_url): "result" not in sources_url_response_in_json or sources_url_response_in_json["result"] == "0x" ): - raise NodeError(f"Received bad response") + raise NodeError(f"Received bad response: {sources_url_response_in_json}") logger.okay(f"Bytecode was successfully received") return sources_url_response_in_json["result"] From 02527e51e030ab647732cf82ebbefe1ee9548346 Mon Sep 17 00:00:00 2001 From: alexvozhak Date: Thu, 12 Sep 2024 23:46:55 +0300 Subject: [PATCH 093/118] removed adding http prefix from local rpc url --- diffyscan/diffyscan.py | 2 -- diffyscan/utils/hardhat.py | 6 ++---- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/diffyscan/diffyscan.py b/diffyscan/diffyscan.py index 8ce31fd..7cf0f8d 100755 --- a/diffyscan/diffyscan.py +++ b/diffyscan/diffyscan.py @@ -248,8 +248,6 @@ def process_config( local_rpc_url = os.getenv("LOCAL_RPC_URL", "") if not local_rpc_url: raise ValueError("LOCAL_RPC_URL variable is not set") - if not local_rpc_url.startswith("http://"): - local_rpc_url = "http://" + local_rpc_url remote_rpc_url = os.getenv("REMOTE_RPC_URL", "") if not remote_rpc_url: diff --git a/diffyscan/utils/hardhat.py b/diffyscan/utils/hardhat.py index 35c2091..e71cb48 100644 --- a/diffyscan/utils/hardhat.py +++ b/diffyscan/utils/hardhat.py @@ -27,10 +27,8 @@ def start( remote_rpc_url: str, ): parsed_url = urlparse(local_rpc_url) - if parsed_url.port == None: - raise HardhatError( - f"Invalid LOCAL_RPC_URL (TCP port not specified): '{local_rpc_url}'" - ) + if not parsed_url.port or not parsed_url.hostname: + raise HardhatError(f"Invalid LOCAL_RPC_URL: '{local_rpc_url}'") if not os.path.isfile(hardhat_config_path): raise HardhatError( From 9c9be8829fb0bac9821d95f01bfb597e9ad73546 Mon Sep 17 00:00:00 2001 From: alexvozhak Date: Fri, 13 Sep 2024 00:10:25 +0300 Subject: [PATCH 094/118] added warning for unknown opcode --- diffyscan/utils/binary_verifier.py | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/diffyscan/utils/binary_verifier.py b/diffyscan/utils/binary_verifier.py index b75cfcb..512365c 100644 --- a/diffyscan/utils/binary_verifier.py +++ b/diffyscan/utils/binary_verifier.py @@ -10,8 +10,15 @@ def format_bytecode(bytecode): def match_bytecode(actual_bytecode, expected_bytecode, immutables): logger.info("Comparing actual code with the expected one...") - actual_instructions = parse(actual_bytecode) - expected_instructions = parse(expected_bytecode) + actual_instructions, unknown_opcodes_first_half = parse(actual_bytecode) + expected_instructions, unknown_opcodes_second_half = parse(expected_bytecode) + + unknown_opcodes = ( + unknown_opcodes_first_half or set() | unknown_opcodes_second_half or set() + ) + if unknown_opcodes: + logger.warn(f"Detected unknown opcodes: {unknown_opcodes}") + zipped_instructions = list(zip(actual_instructions, expected_instructions)) is_mismatch = lambda pair: pair[0].get("bytecode") != pair[1].get("bytecode") mismatches = [ @@ -134,10 +141,11 @@ def parse(bytecode): buffer = bytes.fromhex(bytecode[2:] if bytecode.startswith("0x") else bytecode) instructions = [] i = 0 + unknown_opcodes = set() while i < len(buffer): opcode = buffer[i] - # if opcode not in OPCODES: - # raise BinVerifierError(f"Unknown opcode {hex(opcode)}") + if opcode not in OPCODES: + unknown_opcodes.add(hex(opcode)) length = 1 + (opcode - PUSH0 if PUSH0 <= opcode <= PUSH32 else 0) instructions.append( { @@ -148,4 +156,4 @@ def parse(bytecode): } ) i += length - return instructions + return instructions, unknown_opcodes From 1fec4cb19741da37eea6a198c1222441d4ccbf56 Mon Sep 17 00:00:00 2001 From: alexvozhak Date: Fri, 13 Sep 2024 01:29:04 +0300 Subject: [PATCH 095/118] changed log message by remark --- diffyscan/diffyscan.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/diffyscan/diffyscan.py b/diffyscan/diffyscan.py index 7cf0f8d..5911e30 100755 --- a/diffyscan/diffyscan.py +++ b/diffyscan/diffyscan.py @@ -67,9 +67,7 @@ def run_bytecode_diff( if is_fully_matched: return - logger.info( - f"Trying to check bytecode via using calldata and deploying into local node" - ) + logger.info(f"Automated match hasn't worked out") calldata = get_calldata( contract_address_from_config, From dc318d99d655c64814aa794713eb3baaae3d61f0 Mon Sep 17 00:00:00 2001 From: alexvozhak Date: Fri, 13 Sep 2024 01:36:56 +0300 Subject: [PATCH 096/118] added random guid for temp .sol files --- diffyscan/utils/common.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/diffyscan/utils/common.py b/diffyscan/utils/common.py index 429d348..f41e23a 100644 --- a/diffyscan/utils/common.py +++ b/diffyscan/utils/common.py @@ -4,6 +4,7 @@ import subprocess import tempfile import requests +import uuid from urllib.parse import urlparse @@ -82,9 +83,7 @@ def parse_repo_link(repo_link): def prettify_solidity(solidity_contract_content: str): - github_file_name = os.path.join( - tempfile.gettempdir(), "9B91E897-EA51-4FCC-8DAF-FCFF135A6963.sol" - ) + github_file_name = os.path.join(tempfile.gettempdir(), f"{uuid.uuid4()}.sol") with open(github_file_name, "w") as fp: fp.write(solidity_contract_content) From 42fcee7e5787e3f860a0c34ebbb0e0dd5296b9db Mon Sep 17 00:00:00 2001 From: alexvozhak Date: Fri, 13 Sep 2024 01:41:25 +0300 Subject: [PATCH 097/118] fixed readme by remark --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 3ae5f9e..57ef5fc 100644 --- a/README.md +++ b/README.md @@ -48,7 +48,7 @@ Set remote RPC URL to validate contract bytecode at remote rpc node, export REMOTE_RPC_URL = ``` -Set local RPC URL to validate contract bytecode at local rpc node (Hardhat), +Set local RPC URL to check immutables against the local deployment and provided constructor arguments, ```bash export LOCAL_RPC_URL = (example `http://127.0.0.1:7545`) From 62881b9cf9d0da7fe5dd687cbe9bfc7c83a74d03 Mon Sep 17 00:00:00 2001 From: alexvozhak Date: Fri, 13 Sep 2024 01:48:16 +0300 Subject: [PATCH 098/118] added extra comment for encode_constructor_arguments() --- diffyscan/utils/encoder.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/diffyscan/utils/encoder.py b/diffyscan/utils/encoder.py index 1a64155..1399196 100644 --- a/diffyscan/utils/encoder.py +++ b/diffyscan/utils/encoder.py @@ -73,6 +73,8 @@ def encode_string(arg_length: int, compl_data: list, arg_value: str): def encode_constructor_arguments(constructor_abi: list, constructor_config_args: list): # see https://docs.soliditylang.org/en/develop/abi-spec.html#contract-abi-specification + # transferred from here: + # https://github.com/lidofinance/lido-dao/blob/master/bytecode-verificator/bytecode_verificator.sh#L369-L405 arg_length = len(constructor_abi) constructor_calldata = "" From 102c72b0e52f6799304a70ead29c3be8df0c23e2 Mon Sep 17 00:00:00 2001 From: alexvozhak Date: Sun, 22 Sep 2024 20:37:18 +0300 Subject: [PATCH 099/118] renamed raise_exceptions_on_comparison to fail_on_comparison_error --- README.md | 2 +- config_samples/lido_dao_holesky_config.json | 2 +- config_samples/lido_dao_sepolia_config.json | 2 +- diffyscan/diffyscan.py | 2 +- diffyscan/utils/custom_types.py | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 57ef5fc..b83b416 100644 --- a/README.md +++ b/README.md @@ -81,7 +81,7 @@ Alternatively, create a new config file named `config.json` near the diffyscan.p "relative_root": "contracts" } }, - "raise_exception": true, + "fail_on_comparison_error": true, "bytecode_comparison": { "hardhat_config_name": "holesky_hardhat.config.js", "constructor_calldata": { diff --git a/config_samples/lido_dao_holesky_config.json b/config_samples/lido_dao_holesky_config.json index b5a842d..5ef782a 100644 --- a/config_samples/lido_dao_holesky_config.json +++ b/config_samples/lido_dao_holesky_config.json @@ -113,7 +113,7 @@ "relative_root": "contracts" } }, - "raise_exceptions_on_comparison": true, + "fail_on_comparison_error": true, "bytecode_comparison": { "constructor_calldata": { "0x28FAB2059C713A7F9D8c86Db49f9bb0e96Af1ef8": "000000000000000000000000ab89ed3d8f31bcf8bb7de53f02084d1e6f043d34000000000000000000000000e92329ec7ddb11d25e25b3c21eebf11f15eb325d00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" diff --git a/config_samples/lido_dao_sepolia_config.json b/config_samples/lido_dao_sepolia_config.json index 4f83205..fff0ee7 100644 --- a/config_samples/lido_dao_sepolia_config.json +++ b/config_samples/lido_dao_sepolia_config.json @@ -98,7 +98,7 @@ "relative_root": "contracts" } }, - "raise_exceptions_on_comparison": true, + "fail_on_comparison_error": true, "bytecode_comparison": { "constructor_calldata": { "0x8f6254332f69557A72b0DA2D5F0Bc07d4CA991E7": "000000000000000000000000f30a674935479cc6f2254ba65d7534eab8bd6ba20000000000000000000000006885e36bfcb68cb383dfe90023a462c03bcb2ae500000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" diff --git a/diffyscan/diffyscan.py b/diffyscan/diffyscan.py index 5911e30..10d2655 100755 --- a/diffyscan/diffyscan.py +++ b/diffyscan/diffyscan.py @@ -251,7 +251,7 @@ def process_config( if not remote_rpc_url: raise ValueError("REMOTE_RPC_URL variable is not set") - ExceptionHandler.initialize(config["raise_exceptions_on_comparison"]) + ExceptionHandler.initialize(config["fail_on_comparison_error"]) try: if not skip_binary_comparison: diff --git a/diffyscan/utils/custom_types.py b/diffyscan/utils/custom_types.py index 7d6ab9b..9e79acd 100644 --- a/diffyscan/utils/custom_types.py +++ b/diffyscan/utils/custom_types.py @@ -15,4 +15,4 @@ class Config(TypedDict): explorer_hostname: str explorer_token_env_var: str bytecode_comparison: BinartConfig - raise_exceptions_on_comparison: bool + fail_on_comparison_error: bool From ac4419edb42f67adb6b2112fa8acd0bb91b80df2 Mon Sep 17 00:00:00 2001 From: alexvozhak Date: Sun, 22 Sep 2024 20:43:13 +0300 Subject: [PATCH 100/118] uniqueness in readme example has been enforced --- README.md | 5 ----- 1 file changed, 5 deletions(-) diff --git a/README.md b/README.md index b83b416..933d671 100644 --- a/README.md +++ b/README.md @@ -88,11 +88,6 @@ Alternatively, create a new config file named `config.json` near the diffyscan.p "0x28FAB2059C713A7F9D8c86Db49f9bb0e96Af1ef8": "000000000000000000000000ab89ed3d8f31bcf8bb7de53f02084d1e6f043d34000000000000000000000000e92329ec7ddb11d25e25b3c21eebf11f15eb325d00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" }, "constructor_args": { - "0x28FAB2059C713A7F9D8c86Db49f9bb0e96Af1ef8": [ - "0xab89ED3D8f31bcF8BB7De53F02084d1e6F043D34", - "0xE92329EC7ddB11D25e25b3c21eeBf11f15eB325d", - "" - ], "0xDba5Ad530425bb1b14EECD76F1b4a517780de537": [ [ "0x4E97A3972ce8511D87F334dA17a2C332542a5246", From 601ccbea8c34939ab3d22cf9590491b8b732e339 Mon Sep 17 00:00:00 2001 From: alexvozhak Date: Sun, 22 Sep 2024 20:43:57 +0300 Subject: [PATCH 101/118] fixed diffyscan example call for pipx usage --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 933d671..9f43101 100644 --- a/README.md +++ b/README.md @@ -127,7 +127,7 @@ module.exports = { Start the script ```bash -dyffyscan +dyffyscan /path/to/config.json /path/to/hardhat_config.js ``` > Note: Brownie verification tooling might rewrite the imports in the source submission. It transforms relative paths to imported contracts into flat paths ('./folder/contract.sol' -> 'contract.sol'), which makes Diffyscan unable to find a contract for verification. @@ -135,7 +135,7 @@ dyffyscan For contracts whose sources were verified by brownie tooling: ```bash -diffyscan --support-brownie +diffyscan /path/to/config.json /path/to/hardhat_config.js --support-brownie ``` ℹ️ See more config examples inside the [config_samples](./config_samples/) dir. From 498be467ce0160bb887b2020f1469cf17eedcbc3 Mon Sep 17 00:00:00 2001 From: alexvozhak Date: Sun, 22 Sep 2024 23:41:42 +0300 Subject: [PATCH 102/118] Added handling different codes length --- diffyscan/utils/binary_verifier.py | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/diffyscan/utils/binary_verifier.py b/diffyscan/utils/binary_verifier.py index 512365c..9548652 100644 --- a/diffyscan/utils/binary_verifier.py +++ b/diffyscan/utils/binary_verifier.py @@ -1,3 +1,5 @@ +import itertools + from .logger import logger, bgYellow, bgRed, bgGreen, red, green, to_hex from .constants import OPCODES, PUSH0, PUSH32 from .custom_exceptions import BinVerifierError @@ -19,8 +21,18 @@ def match_bytecode(actual_bytecode, expected_bytecode, immutables): if unknown_opcodes: logger.warn(f"Detected unknown opcodes: {unknown_opcodes}") - zipped_instructions = list(zip(actual_instructions, expected_instructions)) - is_mismatch = lambda pair: pair[0].get("bytecode") != pair[1].get("bytecode") + if len(actual_instructions) != len(expected_instructions): + logger.warn(f"Codes have a different length") + + zipped_instructions = list( + itertools.zip_longest(actual_instructions, expected_instructions) + ) + + is_mismatch = ( + lambda pair: pair[0] is None + or pair[1] is None + or pair[0].get("bytecode") != pair[1].get("bytecode") + ) mismatches = [ index for index, pair in enumerate(zipped_instructions) if is_mismatch(pair) ] @@ -82,14 +94,14 @@ def match_bytecode(actual_bytecode, expected_bytecode, immutables): if not actual and expected: params = "0x" + expected["bytecode"][2:] print( - logger.red( + red( f'{to_hex(current_index, 4)} {to_hex(expected["op"]["code"])} {expected["op"]["name"]} {params}' ) ) elif actual and not expected: params = "0x" + actual["bytecode"][2:] print( - logger.green( + green( f'{to_hex(current_index, 4)} {to_hex(actual["op"]["code"])} {actual["op"]["name"]} {params}' ) ) From 9dc240fc0a7585df887818bd3c066960c9314870 Mon Sep 17 00:00:00 2001 From: alexvozhak Date: Mon, 23 Sep 2024 00:18:52 +0300 Subject: [PATCH 103/118] Fixed logic for calldata in config --- diffyscan/utils/calldata.py | 39 +++++++++++++++++++++++-------------- 1 file changed, 24 insertions(+), 15 deletions(-) diff --git a/diffyscan/utils/calldata.py b/diffyscan/utils/calldata.py index a95528d..f4620d1 100644 --- a/diffyscan/utils/calldata.py +++ b/diffyscan/utils/calldata.py @@ -4,18 +4,34 @@ def get_calldata(contract_address_from_config, target_compiled_contract, binary_config): - calldata = get_raw_calldata_from_config(contract_address_from_config, binary_config) - if calldata is not None: - return calldata - calldata = parse_calldata_from_config( + raw_calldata_exist = ( + "constructor_calldata" in binary_config + and contract_address_from_config in binary_config["constructor_calldata"] + ) + + calldata_args_exist = ( + "constructor_args" in binary_config + and contract_address_from_config in binary_config["constructor_args"] + ) + + if raw_calldata_exist and calldata_args_exist: + raise CalldataError( + f"Contract address found in 'constructor_args' and in 'constructor_calldata' in config" + ) + if not raw_calldata_exist and not calldata_args_exist: + raise CalldataError( + "Contract address not found in 'constructor_args' and in 'constructor_calldata' in config" + ) + if raw_calldata_exist: + return get_raw_calldata_from_config(contract_address_from_config, binary_config) + + return parse_calldata_from_config( contract_address_from_config, binary_config["constructor_args"], target_compiled_contract, ) - return calldata - def get_constructor_abi(target_compiled_contract): constructor_abi = None @@ -36,13 +52,9 @@ def get_constructor_abi(target_compiled_contract): def get_raw_calldata_from_config(contract_address_from_config, binary_config): - if ( - "constructor_calldata" not in binary_config - or contract_address_from_config not in binary_config["constructor_calldata"] - ): - return None - calldata_field = binary_config["constructor_calldata"] logger.info(f"Trying to use prepared calldata from config") + + calldata_field = binary_config["constructor_calldata"] prepared_calldata_from_config = calldata_field[contract_address_from_config] return prepared_calldata_from_config @@ -58,9 +70,6 @@ def parse_calldata_from_config( if constructor_args is None: raise CalldataError("Failed to find constructor's args in config") - if contract_address_from_config not in constructor_args: - raise CalldataError("Failed to find contract calldata in config") - calldata = encode_constructor_arguments( constructor_abi, constructor_args[contract_address_from_config] ) From d71d9f26644ba8ed68cc55104390ec677704c2c1 Mon Sep 17 00:00:00 2001 From: alexvozhak Date: Mon, 23 Sep 2024 00:26:07 +0300 Subject: [PATCH 104/118] added comment for near_lines_count constant --- diffyscan/utils/binary_verifier.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/diffyscan/utils/binary_verifier.py b/diffyscan/utils/binary_verifier.py index 9548652..9658682 100644 --- a/diffyscan/utils/binary_verifier.py +++ b/diffyscan/utils/binary_verifier.py @@ -41,7 +41,8 @@ def match_bytecode(actual_bytecode, expected_bytecode, immutables): logger.okay(f"Bytecodes are fully matched") return True - nearLinesCount = 3 + near_lines_count = 3 # context depth, i.e., the number of lines above and \below to be displayed for each diff + checkpoints = {0, *mismatches} if actual_instructions: @@ -51,8 +52,8 @@ def match_bytecode(actual_bytecode, expected_bytecode, immutables): checkpoints.add(len(expected_instructions) - 1) for ind in list(checkpoints): - start_index = max(0, ind - nearLinesCount) - end_index = min(ind + nearLinesCount, len(zipped_instructions) - 1) + start_index = max(0, ind - near_lines_count) + end_index = min(ind + near_lines_count, len(zipped_instructions) - 1) checkpoints.update(range(start_index, end_index + 1)) From 84934374ca18e301938d6a155afb494f35ec370c Mon Sep 17 00:00:00 2001 From: alexvozhak Date: Mon, 23 Sep 2024 00:32:13 +0300 Subject: [PATCH 105/118] Changed hardfork from istanbul to cancun --- README.md | 2 +- hardhat_configs/holesky_hardhat_config.js | 2 +- hardhat_configs/sepolia_hardhat_config.js | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 9f43101..3c6cbf6 100644 --- a/README.md +++ b/README.md @@ -118,7 +118,7 @@ module.exports = { hardhat: { chainId: 17000, blockGasLimit: 92000000, - hardfork: "istanbul", + hardfork: "cancun", } }, }; diff --git a/hardhat_configs/holesky_hardhat_config.js b/hardhat_configs/holesky_hardhat_config.js index b972e6b..41523f5 100644 --- a/hardhat_configs/holesky_hardhat_config.js +++ b/hardhat_configs/holesky_hardhat_config.js @@ -4,7 +4,7 @@ module.exports = { hardhat: { chainId: 17000, blockGasLimit: 92000000, - hardfork: "istanbul", + hardfork: "cancun", } }, }; diff --git a/hardhat_configs/sepolia_hardhat_config.js b/hardhat_configs/sepolia_hardhat_config.js index 8b1932e..435644f 100644 --- a/hardhat_configs/sepolia_hardhat_config.js +++ b/hardhat_configs/sepolia_hardhat_config.js @@ -4,7 +4,7 @@ module.exports = { hardhat: { chainId: 11155111, blockGasLimit: 92000000, - hardfork: "istanbul", + hardfork: "cancun", } }, }; From 37617f7231188aa73d730b43db6e99f96244013e Mon Sep 17 00:00:00 2001 From: alexvozhak Date: Tue, 24 Sep 2024 01:15:20 +0300 Subject: [PATCH 106/118] Husky integrated with precommit hook --- .husky/pre-commit | 9 +++++++++ package.json | 4 ++++ 2 files changed, 13 insertions(+) create mode 100644 .husky/pre-commit diff --git a/.husky/pre-commit b/.husky/pre-commit new file mode 100644 index 0000000..b6f9048 --- /dev/null +++ b/.husky/pre-commit @@ -0,0 +1,9 @@ +#!/bin/bash + +json_files=$(git diff --cached --name-only | grep '\.json$') +if [ -n "$json_files" ]; then +for file in $json_files; do + jq . "$file" > tmp.$$.json && mv tmp.$$.json "$file" + git add "$file" +done +fi diff --git a/package.json b/package.json index aef162d..16ac488 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,6 @@ { "devDependencies": { + "husky": "^9.1.6", "prettier": "^3.3.0", "prettier-plugin-solidity": "^1.3.1", "mypy": "^1.11.2", @@ -12,5 +13,8 @@ "packageManager": "npm@10.8.2", "engines": { "node": ">=20.0" + }, + "scripts": { + "prepare": "husky" } } From 2516736c3e0109b66c51fd2b1cded0a25876b3fa Mon Sep 17 00:00:00 2001 From: alexvozhak Date: Tue, 24 Sep 2024 23:51:42 +0300 Subject: [PATCH 107/118] tested encoding bytes on optimizm contract --- .../testnet/optimism_testnet_config_L2.json | 49 ++++++++++++------- diffyscan/utils/encoder.py | 4 +- .../sepolia_optimizm_hardhat_config.js | 10 ++++ 3 files changed, 42 insertions(+), 21 deletions(-) create mode 100644 hardhat_configs/sepolia_optimizm_hardhat_config.js diff --git a/config_samples/optimism/testnet/optimism_testnet_config_L2.json b/config_samples/optimism/testnet/optimism_testnet_config_L2.json index b1e603a..1b69f17 100644 --- a/config_samples/optimism/testnet/optimism_testnet_config_L2.json +++ b/config_samples/optimism/testnet/optimism_testnet_config_L2.json @@ -1,22 +1,33 @@ { - "contracts": { - "0xdBA2760246f315203F8B716b3a7590F0FFdc704a": "OssifiableProxy", - "0x2B4a7968C173ea52745C3740B13da9609D83Bd82": "L2ERC20TokenBridge", - "0x24B47cd3A74f1799b32B2de11073764Cb1bb318B": "OssifiableProxy", - "0xaB0c6F1015b644c252064155759Cdc90a6CBd50d": "ERC20Bridged" - }, - "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": "" - }, - "dependencies": { - "@openzeppelin/contracts": { - "url": "https://github.com/OpenZeppelin/openzeppelin-contracts", - "commit": "d4fb3a89f9d0a39c7ee6f2601d33ffbf30085322", - "relative_root": "contracts" - } + "contracts": { + "0xf49D208B5C7b10415C7BeAFe9e656F2DF9eDfe3B": "OssifiableProxy", + "0xdBA2760246f315203F8B716b3a7590F0FFdc704a": "OssifiableProxy", + "0x2B4a7968C173ea52745C3740B13da9609D83Bd82": "L2ERC20TokenBridge", + "0x24B47cd3A74f1799b32B2de11073764Cb1bb318B": "OssifiableProxy", + "0xaB0c6F1015b644c252064155759Cdc90a6CBd50d": "ERC20Bridged" + }, + "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": "" + }, + "dependencies": { + "@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" + ] + } + } } diff --git a/diffyscan/utils/encoder.py b/diffyscan/utils/encoder.py index 1399196..c9c024c 100644 --- a/diffyscan/utils/encoder.py +++ b/diffyscan/utils/encoder.py @@ -19,8 +19,8 @@ def encode_bytes(data: str) -> str: if not bytes_str: return to_hex_with_alignment(0) - # untested, taken from here: - # https://github.com/lidofinance/lido-dao/blob/a9a9ea50b5be7dba33e06d70cc068252ffd01b52/bytecode-verificator/bytecode_verificator.sh#L239 + # Calculate the length of the hex-encoded 32-bytes padded data + # since EVM uses 32-byte (256-bit) words count_of_bytes_from_hex = len(bytes_str) // 2 encoded_length = 0 if count_of_bytes_from_hex > 0: diff --git a/hardhat_configs/sepolia_optimizm_hardhat_config.js b/hardhat_configs/sepolia_optimizm_hardhat_config.js new file mode 100644 index 0000000..de60c95 --- /dev/null +++ b/hardhat_configs/sepolia_optimizm_hardhat_config.js @@ -0,0 +1,10 @@ +module.exports = { + solidity: "0.8.9", + networks: { + hardhat: { + chainId: 11155420, + blockGasLimit: 92000000, + hardfork: "cancun", + } + }, +}; From 4c6b2d6819f62f772e02482fdfd76898698fb3be Mon Sep 17 00:00:00 2001 From: alexvozhak Date: Wed, 25 Sep 2024 00:26:22 +0300 Subject: [PATCH 108/118] added automatic reconnection to Hardhat --- diffyscan/utils/hardhat.py | 45 +++++++++++++++++++++++--------------- 1 file changed, 27 insertions(+), 18 deletions(-) diff --git a/diffyscan/utils/hardhat.py b/diffyscan/utils/hardhat.py index e71cb48..e98e536 100644 --- a/diffyscan/utils/hardhat.py +++ b/diffyscan/utils/hardhat.py @@ -1,6 +1,7 @@ import os import subprocess import signal +import time from urllib.parse import urlparse from .logger import logger @@ -9,16 +10,8 @@ class Hardhat: sub_process = None - TIMEOUT_FOR_INIT_SEC = 5 - - def __init__(self): - pass - - def is_port_in_use(self, parsed_url) -> bool: - import socket - - with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: - return s.connect_ex((parsed_url.hostname, parsed_url.port)) == 0 + TIMEOUT_FOR_CONNECT_SEC = 10 + ATTEMPTS_FOR_CONNECT = 5 def start( self, @@ -43,7 +36,7 @@ def start( ) logger.info(f'Trying to start Hardhat: "{local_node_command}"') - is_port_used = self.is_port_in_use(parsed_url) + is_port_used = self._is_port_in_use_(parsed_url) if is_port_used: answer = input( f'Port {parsed_url.port} is busy. Kill the app instance occupying the port? write "yes": ' @@ -53,7 +46,7 @@ def start( f"exec npx kill-port {parsed_url.port}", shell=True ) if return_code == 0: - is_port_used = self.is_port_in_use(parsed_url) + is_port_used = self._is_port_in_use_(parsed_url) if is_port_used: raise HardhatError(f"{parsed_url.netloc} is busy") self.sub_process = subprocess.Popen( @@ -63,20 +56,36 @@ def start( stderr=subprocess.PIPE, ) try: - _, errs = self.sub_process.communicate(timeout=self.TIMEOUT_FOR_INIT_SEC) + _, errs = self.sub_process.communicate(timeout=self.TIMEOUT_FOR_CONNECT_SEC) if errs: raise HardhatError(f"{errs.decode()}") except subprocess.TimeoutExpired: - is_port_used = self.is_port_in_use(parsed_url) - if is_port_used: - logger.okay(f"Hardhat successfully started, PID {self.sub_process.pid}") - else: - raise HardhatError(f"something is wrong") + self._handle_timeout(parsed_url) 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}") + def _is_port_in_use_(self, parsed_url) -> bool: + import socket + + with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: + return s.connect_ex((parsed_url.hostname, parsed_url.port)) == 0 + + def _handle_timeout(self, parsed_url): + attempt = 1 + logger.info("The connection to Hardhat is taking longer than expected") + + while attempt <= self.ATTEMPTS_FOR_CONNECT: + logger.info(f"Reconnecting to Hardhat, attempt #{attempt}") + if self._is_port_in_use_(parsed_url): + logger.okay(f"Hardhat successfully started, PID {self.sub_process.pid}") + return + attempt += 1 + time.sleep(self.TIMEOUT_FOR_CONNECT_SEC) + + raise HardhatError("Something is wrong") + hardhat = Hardhat() From 3d99f67c37f28226f1efac789a85b94f46cd00bf Mon Sep 17 00:00:00 2001 From: alexvozhak Date: Wed, 25 Sep 2024 00:35:52 +0300 Subject: [PATCH 109/118] updated package.json and package-lock.json --- package-lock.json | 260 +++++++++++++++++++++++++++------------------- package.json | 4 +- 2 files changed, 152 insertions(+), 112 deletions(-) diff --git a/package-lock.json b/package-lock.json index 6098460..aca6d6c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,8 +9,12 @@ "kill-port": "^2.0.1" }, "devDependencies": { + "husky": "^9.1.6", "prettier": "^3.3.0", "prettier-plugin-solidity": "^1.3.1" + }, + "engines": { + "node": ">=20.0" } }, "node_modules/@ethersproject/abi": { @@ -433,74 +437,74 @@ ] }, "node_modules/@nomicfoundation/edr": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/@nomicfoundation/edr/-/edr-0.5.2.tgz", - "integrity": "sha512-hW/iLvUQZNTVjFyX/I40rtKvvDOqUEyIi96T28YaLfmPL+3LW2lxmYLUXEJ6MI14HzqxDqrLyhf6IbjAa2r3Dw==", + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/@nomicfoundation/edr/-/edr-0.6.1.tgz", + "integrity": "sha512-ILlhHzUgVQ+5kpZ35fxMI1xwaqxfZV8V8l+pKo1RZHvLKf80Azvq1OLg1RfxstLIA2QB+KBpch9QfPiD5fBpdw==", "dependencies": { - "@nomicfoundation/edr-darwin-arm64": "0.5.2", - "@nomicfoundation/edr-darwin-x64": "0.5.2", - "@nomicfoundation/edr-linux-arm64-gnu": "0.5.2", - "@nomicfoundation/edr-linux-arm64-musl": "0.5.2", - "@nomicfoundation/edr-linux-x64-gnu": "0.5.2", - "@nomicfoundation/edr-linux-x64-musl": "0.5.2", - "@nomicfoundation/edr-win32-x64-msvc": "0.5.2" + "@nomicfoundation/edr-darwin-arm64": "0.6.1", + "@nomicfoundation/edr-darwin-x64": "0.6.1", + "@nomicfoundation/edr-linux-arm64-gnu": "0.6.1", + "@nomicfoundation/edr-linux-arm64-musl": "0.6.1", + "@nomicfoundation/edr-linux-x64-gnu": "0.6.1", + "@nomicfoundation/edr-linux-x64-musl": "0.6.1", + "@nomicfoundation/edr-win32-x64-msvc": "0.6.1" }, "engines": { "node": ">= 18" } }, "node_modules/@nomicfoundation/edr-darwin-arm64": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-darwin-arm64/-/edr-darwin-arm64-0.5.2.tgz", - "integrity": "sha512-Gm4wOPKhbDjGTIRyFA2QUAPfCXA1AHxYOKt3yLSGJkQkdy9a5WW+qtqKeEKHc/+4wpJSLtsGQfpzyIzggFfo/A==", + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-darwin-arm64/-/edr-darwin-arm64-0.6.1.tgz", + "integrity": "sha512-ncZs0yRoxbiJB+sg7w2K6BLgMnAgOK/IcGuuZaNHKwiMHk19Kn2JDl+5fUOzkIGNpaCf8uvoEb2q6K7212KjQA==", "engines": { "node": ">= 18" } }, "node_modules/@nomicfoundation/edr-darwin-x64": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-darwin-x64/-/edr-darwin-x64-0.5.2.tgz", - "integrity": "sha512-ClyABq2dFCsrYEED3/UIO0c7p4H1/4vvlswFlqUyBpOkJccr75qIYvahOSJRM62WgUFRhbSS0OJXFRwc/PwmVg==", + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-darwin-x64/-/edr-darwin-x64-0.6.1.tgz", + "integrity": "sha512-Akubo27DS0D921aaApD+IRlv1niLiARWPrUDDBOKjCYKGVrJUKmAdH14qBzZwHBcQBhyVMXgxlSiyIgiqIHHjA==", "engines": { "node": ">= 18" } }, "node_modules/@nomicfoundation/edr-linux-arm64-gnu": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-linux-arm64-gnu/-/edr-linux-arm64-gnu-0.5.2.tgz", - "integrity": "sha512-HWMTVk1iOabfvU2RvrKLDgtFjJZTC42CpHiw2h6rfpsgRqMahvIlx2jdjWYzFNy1jZKPTN1AStQ/91MRrg5KnA==", + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-linux-arm64-gnu/-/edr-linux-arm64-gnu-0.6.1.tgz", + "integrity": "sha512-t+Lb5pyWYe4xJs9dA1jdhUOLxmgzFAa/SSmDZBC5vbCiDic5brUfgtPL//uMI8DKElXm9RSsRwXB5x/6+UhFEw==", "engines": { "node": ">= 18" } }, "node_modules/@nomicfoundation/edr-linux-arm64-musl": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-linux-arm64-musl/-/edr-linux-arm64-musl-0.5.2.tgz", - "integrity": "sha512-CwsQ10xFx/QAD5y3/g5alm9+jFVuhc7uYMhrZAu9UVF+KtVjeCvafj0PaVsZ8qyijjqVuVsJ8hD1x5ob7SMcGg==", + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-linux-arm64-musl/-/edr-linux-arm64-musl-0.6.1.tgz", + "integrity": "sha512-dxv2wtnb1vE7MLQfy7mmXObhX585gBGB+kJZRj+K5z+0uVNM1Xep0kH+XpuGHAT0C/w/7YEWXrrsjTpxadcpnw==", "engines": { "node": ">= 18" } }, "node_modules/@nomicfoundation/edr-linux-x64-gnu": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-linux-x64-gnu/-/edr-linux-x64-gnu-0.5.2.tgz", - "integrity": "sha512-CWVCEdhWJ3fmUpzWHCRnC0/VLBDbqtqTGTR6yyY1Ep3S3BOrHEAvt7h5gx85r2vLcztisu2vlDq51auie4IU1A==", + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-linux-x64-gnu/-/edr-linux-x64-gnu-0.6.1.tgz", + "integrity": "sha512-8XwZRYCcChHNrdWPdsyE8lotJ/0702hKMA7tueo5T2SSK8YRLAq8tbshDPxzrNKztP1I+SbH2Y+sucKCscJOUg==", "engines": { "node": ">= 18" } }, "node_modules/@nomicfoundation/edr-linux-x64-musl": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-linux-x64-musl/-/edr-linux-x64-musl-0.5.2.tgz", - "integrity": "sha512-+aJDfwhkddy2pP5u1ISg3IZVAm0dO836tRlDTFWtvvSMQ5hRGqPcWwlsbobhDQsIxhPJyT7phL0orCg5W3WMeA==", + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-linux-x64-musl/-/edr-linux-x64-musl-0.6.1.tgz", + "integrity": "sha512-aySKfZtDxaD365qkEVqqMXatDa0tsq3gVGUz18lvy+14lGCzEAPIQNo2lJAM26k0w3HbOuIFCzI2FbksAi245A==", "engines": { "node": ">= 18" } }, "node_modules/@nomicfoundation/edr-win32-x64-msvc": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-win32-x64-msvc/-/edr-win32-x64-msvc-0.5.2.tgz", - "integrity": "sha512-CcvvuA3sAv7liFNPsIR/68YlH6rrybKzYttLlMr80d4GKJjwJ5OKb3YgE6FdZZnOfP19HEHhsLcE0DPLtY3r0w==", + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-win32-x64-msvc/-/edr-win32-x64-msvc-0.6.1.tgz", + "integrity": "sha512-yNBdEjC6fi3dUizgTNRNcgs4y7CnGxkyC4bJkGcwZzJ7Hb88/ODay+RWcVpwyZNobzsiDc9zrKs2h3+4j/0FLQ==", "engines": { "node": ">= 18" } @@ -691,9 +695,9 @@ } }, "node_modules/@scure/base": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.1.7.tgz", - "integrity": "sha512-PPNYBslrLNNUQ/Yad37MHYsNQtK67EhWb6WtSvNLLPo7SdVZgkUjD6Dg+5On7zNwmskf8OX7I7Nx5oN+MIWE0g==", + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.1.9.tgz", + "integrity": "sha512-8YKhl8GHiNI/pU2VMaofa2Tor7PJRAjwQLBBuilkJ9L5+13yVbC7JO/wS7piioAvPSwR3JKM1IJ/u4xQzbcXKg==", "funding": { "url": "https://paulmillr.com/funding/" } @@ -825,15 +829,15 @@ } }, "node_modules/@solidity-parser/parser": { - "version": "0.17.0", - "resolved": "https://registry.npmjs.org/@solidity-parser/parser/-/parser-0.17.0.tgz", - "integrity": "sha512-Nko8R0/kUo391jsEHHxrGM07QFdnPGvlmox4rmH0kNiNAashItAilhy4Mv4pK5gQmW5f4sXAF58fwJbmlkGcVw==", + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/@solidity-parser/parser/-/parser-0.18.0.tgz", + "integrity": "sha512-yfORGUIPgLck41qyN7nbwJRAx17/jAIXCTanHOJZhB6PJ1iAk/84b/xlsVKFSyNyLXIj0dhppoE0+CRws7wlzA==", "dev": true }, "node_modules/@types/bn.js": { - "version": "5.1.5", - "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-5.1.5.tgz", - "integrity": "sha512-V46N0zwKRF5Q00AZ6hWtN0T8gGmDUaUzLWQvHFo5yThtVwK/VCenFY3wXVbOvNfajEpsTfQM4IN9k/d6gUVX3A==", + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-5.1.6.tgz", + "integrity": "sha512-Xh8vSwUeMKeYYrj3cX4lGQgFSF/N03r+tv4AiLl1SucqV+uTQpxRcnM8AkXKHwYP9ZPXOYXRr2KPXpVlIvqh9w==", "dependencies": { "@types/node": "*" } @@ -844,11 +848,11 @@ "integrity": "sha512-ssE3Vlrys7sdIzs5LOxCzTVMsU7i9oa/IaW92wF32JFb3CVczqOkru2xspuKczHEbG3nvmPY7IFqVmGGHdNbYw==" }, "node_modules/@types/node": { - "version": "22.1.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.1.0.tgz", - "integrity": "sha512-AOmuRF0R2/5j1knA3c6G3HOk523Ga+l+ZXltX8SF1+5oqcXijjfTd8fY3XRZqSihEu9XhtQnKYLmkFaoxgsJHw==", + "version": "22.6.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.6.1.tgz", + "integrity": "sha512-V48tCfcKb/e6cVUigLAaJDAILdMP0fUW6BidkPK4GpGjXcfbnoHasCZDwz3N3yVt5we2RHm4XTQCpv0KJz9zqw==", "dependencies": { - "undici-types": "~6.13.0" + "undici-types": "~6.19.2" } }, "node_modules/@types/pbkdf2": { @@ -1198,26 +1202,17 @@ } }, "node_modules/chokidar": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", - "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.1.tgz", + "integrity": "sha512-n8enUVCED/KVRQlab1hr3MVpcVMvxtZjmEa956u+4YijlmQED223XMSYj2tLuKvr4jcCTzNNMpQDUer72MMmzA==", "dependencies": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" + "readdirp": "^4.0.1" }, "engines": { - "node": ">= 8.10.0" + "node": ">= 14.16.0" }, "funding": { "url": "https://paulmillr.com/funding/" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" } }, "node_modules/ci-info": { @@ -1328,11 +1323,11 @@ } }, "node_modules/debug": { - "version": "4.3.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.6.tgz", - "integrity": "sha512-O/09Bd4Z1fBrU4VzkhFqVgpPzaGbw6Sm9FEkBT1A/YBXQFGuuSxa1dN2nxgxS34JmKXqYx8CZAwEVoJFImUXIg==", + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", "dependencies": { - "ms": "2.1.2" + "ms": "^2.1.3" }, "engines": { "node": ">=6.0" @@ -1415,9 +1410,9 @@ } }, "node_modules/escalade": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz", - "integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", "engines": { "node": ">=6" } @@ -1557,9 +1552,9 @@ } }, "node_modules/follow-redirects": { - "version": "1.15.6", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz", - "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==", + "version": "1.15.9", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", + "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==", "funding": [ { "type": "individual", @@ -1661,13 +1656,13 @@ "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==" }, "node_modules/hardhat": { - "version": "2.22.7", - "resolved": "https://registry.npmjs.org/hardhat/-/hardhat-2.22.7.tgz", - "integrity": "sha512-nrXQAl+qUr75TsCLDo8P41YXLc+5U7qQMMCIrbbmy1/uQaVPncdjDrD5BR0CENvHRj7EBqO+JkofpozXoIfJKg==", + "version": "2.22.12", + "resolved": "https://registry.npmjs.org/hardhat/-/hardhat-2.22.12.tgz", + "integrity": "sha512-yok65M+LsOeTBHQsjg//QreGCyrsaNmeLVzhTFqlOvZ4ZE5y69N0wRxH1b2BC9dGK8S8OPUJMNiL9X0RAvbm8w==", "dependencies": { "@ethersproject/abi": "^5.1.2", "@metamask/eth-sig-util": "^4.0.0", - "@nomicfoundation/edr": "^0.5.0", + "@nomicfoundation/edr": "^0.6.1", "@nomicfoundation/ethereumjs-common": "4.0.4", "@nomicfoundation/ethereumjs-tx": "5.0.4", "@nomicfoundation/ethereumjs-util": "9.0.4", @@ -1680,7 +1675,7 @@ "ansi-escapes": "^4.3.0", "boxen": "^5.1.2", "chalk": "^2.4.2", - "chokidar": "^3.4.0", + "chokidar": "^4.0.0", "ci-info": "^2.0.0", "debug": "^4.1.1", "enquirer": "^2.3.0", @@ -1693,6 +1688,7 @@ "glob": "7.2.0", "immutable": "^4.0.0-rc.12", "io-ts": "1.10.4", + "json-stream-stringify": "^3.1.4", "keccak": "^3.0.2", "lodash": "^4.17.11", "mnemonist": "^0.38.0", @@ -1800,6 +1796,21 @@ "node": ">= 6" } }, + "node_modules/husky": { + "version": "9.1.6", + "resolved": "https://registry.npmjs.org/husky/-/husky-9.1.6.tgz", + "integrity": "sha512-sqbjZKK7kf44hfdE94EoX8MZNk0n7HeW37O4YrVGCF4wzgQjp+akPAkfUK5LZ6KuR/6sqeAVuXHji+RzQgOn5A==", + "dev": true, + "bin": { + "husky": "bin.js" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/typicode" + } + }, "node_modules/iconv-lite": { "version": "0.4.24", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", @@ -1937,6 +1948,14 @@ "js-yaml": "bin/js-yaml.js" } }, + "node_modules/json-stream-stringify": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/json-stream-stringify/-/json-stream-stringify-3.1.4.tgz", + "integrity": "sha512-oGoz05ft577LolnXFQHD2CjnXDxXVA5b8lHwfEZgRXQUZeCMo6sObQQRq+NXuHQ3oTeMZHHmmPY2rjVwyqR62A==", + "engines": { + "node": ">=7.10.1" + } + }, "node_modules/jsonfile": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", @@ -2120,9 +2139,9 @@ } }, "node_modules/mocha": { - "version": "10.7.0", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.7.0.tgz", - "integrity": "sha512-v8/rBWr2VO5YkspYINnvu81inSz2y3ODJrhO175/Exzor1RcEZZkizgE2A+w/CAXXoESS8Kys5E62dOHGHzULA==", + "version": "10.7.3", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.7.3.tgz", + "integrity": "sha512-uQWxAu44wwiACGqjbPYmjo7Lg8sFrS3dQe7PP2FQI+woptP4vZXSMcfMyFL/e1yFEeEpV4RtyTpZROOKmxis+A==", "dependencies": { "ansi-colors": "^4.1.3", "browser-stdout": "^1.3.1", @@ -2161,6 +2180,29 @@ "balanced-match": "^1.0.0" } }, + "node_modules/mocha/node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, "node_modules/mocha/node_modules/escape-string-regexp": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", @@ -2239,11 +2281,6 @@ "node": ">=10" } }, - "node_modules/mocha/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" - }, "node_modules/mocha/node_modules/p-limit": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", @@ -2280,6 +2317,17 @@ "node": ">=8" } }, + "node_modules/mocha/node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, "node_modules/mocha/node_modules/supports-color": { "version": "8.1.1", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", @@ -2295,9 +2343,9 @@ } }, "node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" }, "node_modules/node-addon-api": { "version": "2.0.2", @@ -2305,9 +2353,9 @@ "integrity": "sha512-Ntyt4AIXyaLIuMHF6IOoTakB3K+RWxwtsHNRxllEoA6vPwP9o4866g6YWDLUdnucilZhmkxiHwHr11gAENw+QA==" }, "node_modules/node-gyp-build": { - "version": "4.8.1", - "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.1.tgz", - "integrity": "sha512-OSs33Z9yWr148JZcbZd5WiAXhh/n9z8TxQcdMhIOlpN9AhWpLfvVFO73+m77bBABQMaY9XSvIa+qk0jlI7Gcaw==", + "version": "4.8.2", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.2.tgz", + "integrity": "sha512-IRUxE4BVsHWXkV/SFOut4qTlagw2aM8T5/vnTsmrHJvVoKueJHRc/JaFND7QDDc61kLYUJ6qlZM3sqTSyx2dTw==", "bin": { "node-gyp-build": "bin.js", "node-gyp-build-optional": "optional.js", @@ -2450,14 +2498,13 @@ } }, "node_modules/prettier-plugin-solidity": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/prettier-plugin-solidity/-/prettier-plugin-solidity-1.3.1.tgz", - "integrity": "sha512-MN4OP5I2gHAzHZG1wcuJl0FsLS3c4Cc5494bbg+6oQWBPuEamjwDvmGfFMZ6NFzsh3Efd9UUxeT7ImgjNH4ozA==", + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/prettier-plugin-solidity/-/prettier-plugin-solidity-1.4.1.tgz", + "integrity": "sha512-Mq8EtfacVZ/0+uDKTtHZGW3Aa7vEbX/BNx63hmVg6YTiTXSiuKP0amj0G6pGwjmLaOfymWh3QgXEZkjQbU8QRg==", "dev": true, "dependencies": { - "@solidity-parser/parser": "^0.17.0", - "semver": "^7.5.4", - "solidity-comments-extractor": "^0.0.8" + "@solidity-parser/parser": "^0.18.0", + "semver": "^7.5.4" }, "engines": { "node": ">=16" @@ -2514,14 +2561,15 @@ } }, "node_modules/readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "dependencies": { - "picomatch": "^2.2.1" - }, + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.0.1.tgz", + "integrity": "sha512-GkMg9uOTpIWWKbSsgwb5fA4EavTR+SG/PMPoAY8hkhHfEEY0/vqljY+XHqtDf2cr2IJtoNRDbrrEpZUiZCkYRw==", "engines": { - "node": ">=8.10.0" + "node": ">= 14.16.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" } }, "node_modules/require-directory": { @@ -2677,12 +2725,6 @@ "semver": "bin/semver" } }, - "node_modules/solidity-comments-extractor": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/solidity-comments-extractor/-/solidity-comments-extractor-0.0.8.tgz", - "integrity": "sha512-htM7Vn6LhHreR+EglVMd2s+sZhcXAirB1Zlyrv5zBuTxieCvjfnRpd7iZk75m/u6NOlEyQ94C6TWbBn2cY7w8g==", - "dev": true - }, "node_modules/source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", @@ -2866,9 +2908,9 @@ } }, "node_modules/undici-types": { - "version": "6.13.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.13.0.tgz", - "integrity": "sha512-xtFJHudx8S2DSoujjMd1WeWvn7KKWFRESZTMeL1RptAYERu29D6jphMjjY+vn96jvN3kVPDNxU/E13VTaXj6jg==" + "version": "6.19.8", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", + "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==" }, "node_modules/universalify": { "version": "0.1.2", diff --git a/package.json b/package.json index 16ac488..7b30557 100644 --- a/package.json +++ b/package.json @@ -2,9 +2,7 @@ "devDependencies": { "husky": "^9.1.6", "prettier": "^3.3.0", - "prettier-plugin-solidity": "^1.3.1", - "mypy": "^1.11.2", - "types-requests": "^2.2.2" + "prettier-plugin-solidity": "^1.3.1" }, "dependencies": { "hardhat": "^2.22.7", From 82479e3a138047ee7266872f5d98ead3abc63263 Mon Sep 17 00:00:00 2001 From: alexvozhak Date: Sun, 13 Oct 2024 16:39:45 +0300 Subject: [PATCH 110/118] added trimming metadata, optimized algorithm, fixed sepolia config --- config_samples/lido_dao_sepolia_config.json | 4 +- diffyscan/diffyscan.py | 23 ++++--- diffyscan/utils/binary_verifier.py | 46 +++++++++---- diffyscan/utils/encoder.py | 73 +++++++++++---------- 4 files changed, 86 insertions(+), 60 deletions(-) diff --git a/config_samples/lido_dao_sepolia_config.json b/config_samples/lido_dao_sepolia_config.json index fff0ee7..35599d4 100644 --- a/config_samples/lido_dao_sepolia_config.json +++ b/config_samples/lido_dao_sepolia_config.json @@ -185,8 +185,8 @@ "0x8f6254332f69557A72b0DA2D5F0Bc07d4CA991E7" ], "0xbac2A471443F18aC5C31078b96C5797A78fCc680": [ - "0x28FAB2059C713A7F9D8c86Db49f9bb0e96Af1ef8", - "0x22896Bfc68814BFD855b1a167255eE497006e730", + "0x8f6254332f69557A72b0DA2D5F0Bc07d4CA991E7", + "0x6885e36bfcb68cb383dfe90023a462c03bcb2ae5", [ 1500, 500, diff --git a/diffyscan/diffyscan.py b/diffyscan/diffyscan.py index 10d2655..bb9e02b 100755 --- a/diffyscan/diffyscan.py +++ b/diffyscan/diffyscan.py @@ -3,6 +3,7 @@ import time import argparse import os +import traceback from .utils.common import load_config, load_env, prettify_solidity @@ -24,7 +25,7 @@ ) from .utils.helpers import create_dirs from .utils.logger import logger -from .utils.binary_verifier import match_bytecode +from .utils.binary_verifier import deep_match_bytecode from .utils.hardhat import hardhat from .utils.node_handler import get_bytecode_from_node, get_account, deploy_contract from .utils.calldata import get_calldata @@ -50,21 +51,18 @@ def run_bytecode_diff( logger.info(f"Binary bytecode comparison started for {address_name}") target_compiled_contract = compile_contract_from_explorer(contract_source_code) - contract_creation_code, deployed_bytecode, immutables = parse_compiled_contract( - target_compiled_contract + contract_creation_code, local_compiled_bytecode, immutables = ( + parse_compiled_contract(target_compiled_contract) ) remote_deployed_bytecode = get_bytecode_from_node( contract_address_from_config, remote_rpc_url ) - is_fully_matched = match_bytecode( - deployed_bytecode, - remote_deployed_bytecode, - immutables, - ) + is_fully_matched = local_compiled_bytecode == remote_deployed_bytecode if is_fully_matched: + logger.okay(f"Bytecodes are fully matched") return logger.info(f"Automated match hasn't worked out") @@ -85,7 +83,13 @@ def run_bytecode_diff( local_contract_address, local_rpc_url ) - match_bytecode( + is_fully_matched = local_deployed_bytecode == remote_deployed_bytecode + + if is_fully_matched: + logger.okay(f"Bytecodes are fully matched") + return + + deep_match_bytecode( local_deployed_bytecode, remote_deployed_bytecode, immutables, @@ -290,6 +294,7 @@ def process_config( ) except BaseCustomException as custom_exc: ExceptionHandler.raise_exception_or_log(custom_exc) + traceback.print_exc() except KeyboardInterrupt: logger.info(f"Keyboard interrupt by user") diff --git a/diffyscan/utils/binary_verifier.py b/diffyscan/utils/binary_verifier.py index 9658682..1bd7fef 100644 --- a/diffyscan/utils/binary_verifier.py +++ b/diffyscan/utils/binary_verifier.py @@ -9,11 +9,35 @@ def format_bytecode(bytecode): return "0x" + bytecode[2:] if len(bytecode) > 2 else "" -def match_bytecode(actual_bytecode, expected_bytecode, immutables): +def trim_solidity_meta(bytecode: str) -> dict: + meta_size = int(bytecode[-4:], 16) * 2 + 4 + + if meta_size > len(bytecode): + return {"bytecode": bytecode, "metadata": ""} + + return { + "bytecode": bytecode[:-meta_size], + "metadata": bytecode[-meta_size:], + } + + +def deep_match_bytecode( + actual_bytecode: str, expected_bytecode: str, immutables: dict +) -> None: logger.info("Comparing actual code with the expected one...") - actual_instructions, unknown_opcodes_first_half = parse(actual_bytecode) - expected_instructions, unknown_opcodes_second_half = parse(expected_bytecode) + actual_trimmed_bytecode = trim_solidity_meta(actual_bytecode) + expected_trimmed_bytecode = trim_solidity_meta(expected_bytecode) + + if actual_trimmed_bytecode["metadata"] or expected_trimmed_bytecode["metadata"]: + logger.info("Metadata has been detected and trimmed") + + actual_instructions, unknown_opcodes_first_half = parse( + actual_trimmed_bytecode["bytecode"] + ) + expected_instructions, unknown_opcodes_second_half = parse( + expected_trimmed_bytecode["bytecode"] + ) unknown_opcodes = ( unknown_opcodes_first_half or set() | unknown_opcodes_second_half or set() @@ -37,10 +61,6 @@ def match_bytecode(actual_bytecode, expected_bytecode, immutables): index for index, pair in enumerate(zipped_instructions) if is_mismatch(pair) ] - if not mismatches: - logger.okay(f"Bytecodes are fully matched") - return True - near_lines_count = 3 # context depth, i.e., the number of lines above and \below to be displayed for each diff checkpoints = {0, *mismatches} @@ -139,15 +159,13 @@ def match_bytecode(actual_bytecode, expected_bytecode, immutables): print(f"{to_hex(current_index, 4)} {opcode} {opname} {params}") else: raise BinVerifierError("Invalid bytecode difference data") - if is_matched_with_excluded_immutables: - logger.okay( - f"Bytecodes have differences only on the immutable reference position" + + if not is_matched_with_excluded_immutables: + raise BinVerifierError( + f"Bytecodes have differences not on the immutable reference position" ) - return False - raise BinVerifierError( - f"Bytecodes have differences not on the immutable reference position" - ) + logger.okay(f"Bytecodes have differences only on the immutable reference position") def parse(bytecode): diff --git a/diffyscan/utils/encoder.py b/diffyscan/utils/encoder.py index c9c024c..a60c237 100644 --- a/diffyscan/utils/encoder.py +++ b/diffyscan/utils/encoder.py @@ -79,44 +79,47 @@ def encode_constructor_arguments(constructor_abi: list, constructor_config_args: constructor_calldata = "" compl_data = [] - for argument_index in range(arg_length): - arg_type = constructor_abi[argument_index]["type"] - 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": - constructor_calldata += to_hex_with_alignment(arg_value) - elif arg_type == "bytes32": - constructor_calldata += encode_bytes32(arg_value) - elif arg_type == "bytes" or arg_type.endswith("[]"): - offset_to_start_of_data_part, encoded_value = encode_dynamic_type( - arg_value, argument_index - ) - constructor_calldata += offset_to_start_of_data_part - compl_data.append(encoded_value) - elif arg_type == "string": - offset_to_start_of_data_part, encoded_value_length, encoded_value = ( - encode_string(arg_length, compl_data, arg_value) - ) - constructor_calldata += offset_to_start_of_data_part - compl_data.append(encoded_value_length) - compl_data.append(encoded_value) - elif arg_type == "tuple": - args_tuple_types = [ - component["type"] - for component in constructor_abi[argument_index]["components"] - ] - if all(arg == "address[]" for arg in args_tuple_types): - argument_index = len(constructor_calldata) // 64 - offset_to_start_of_data_part = to_hex_with_alignment( - (argument_index + 1) * 32 + try: + for argument_index in range(arg_length): + arg_type = constructor_abi[argument_index]["type"] + 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": + constructor_calldata += to_hex_with_alignment(arg_value) + elif arg_type == "bytes32": + constructor_calldata += encode_bytes32(arg_value) + elif arg_type == "bytes" or arg_type.endswith("[]"): + offset_to_start_of_data_part, encoded_value = encode_dynamic_type( + arg_value, argument_index + ) + constructor_calldata += offset_to_start_of_data_part + compl_data.append(encoded_value) + elif arg_type == "string": + offset_to_start_of_data_part, encoded_value_length, encoded_value = ( + encode_string(arg_length, compl_data, arg_value) ) constructor_calldata += offset_to_start_of_data_part - compl_data.append(encode_tuple(args_tuple_types, arg_value)) + compl_data.append(encoded_value_length) + compl_data.append(encoded_value) + elif arg_type == "tuple": + args_tuple_types = [ + component["type"] + for component in constructor_abi[argument_index]["components"] + ] + if all(arg == "address[]" for arg in args_tuple_types): + argument_index = len(constructor_calldata) // 64 + offset_to_start_of_data_part = to_hex_with_alignment( + (argument_index + 1) * 32 + ) + constructor_calldata += offset_to_start_of_data_part + compl_data.append(encode_tuple(args_tuple_types, arg_value)) + else: + constructor_calldata += encode_tuple(args_tuple_types, arg_value) else: - constructor_calldata += encode_tuple(args_tuple_types, arg_value) - else: - raise EncoderError(f"Unknown constructor argument type: {arg_type}") + raise EncoderError(f"Unknown constructor argument type: {arg_type}") + except Exception as e: + raise EncoderError(e) from None for offset_to_start_of_data_part in compl_data: constructor_calldata += offset_to_start_of_data_part From f5466ff3de8a6c12eb1c60412c3725a301e26a8a Mon Sep 17 00:00:00 2001 From: Eugene M Date: Tue, 15 Oct 2024 10:18:12 +0300 Subject: [PATCH 111/118] fix: ignore cache artifacts --- .gitignore | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 7a374f4..2884294 100644 --- a/.gitignore +++ b/.gitignore @@ -4,4 +4,6 @@ digest config.json __pycache__ .vscode -node_modules \ No newline at end of file +node_modules +dist +*/cache/ From dd5bf9416ec1d796ec611c23c6efd3bc99852fe4 Mon Sep 17 00:00:00 2001 From: Eugene M Date: Tue, 15 Oct 2024 10:18:44 +0300 Subject: [PATCH 112/118] fix: enhance binary verification, make opt-in --- README.md | 4 +-- diffyscan/diffyscan.py | 39 +++++++++++++--------------- diffyscan/utils/custom_exceptions.py | 4 ++- diffyscan/utils/hardhat.py | 11 +++++--- diffyscan/utils/node_handler.py | 4 +-- 5 files changed, 33 insertions(+), 29 deletions(-) diff --git a/README.md b/README.md index 3c6cbf6..84c8f14 100644 --- a/README.md +++ b/README.md @@ -8,12 +8,12 @@ Diff deployed EVM-compatible smart contract sourcecode and bytecode against the specified GitHub repo commit. -Key features: +Key features: - retrieve and diff sources from the GitHub repo against the queried ones from a blockscan service (e.g. Etherscan) - compare the bytecode compiled and deployed on the forked network locally against remote (see section 'bytecode_comparison' in `./config_samples/lido_dao_sepolia_config.json` as an example) - preprocess solidity sourcecode by means of prettifier solidity plugin before comparing the sources (option `--prettify`) if needed. - preprocess imports to flat paths for Brownie compatibility (option `--support-brownie`) -- exclude binary comparison (option `--skip-binary-comparison`) +- enable binary comparison (option `--enable-binary-comparison`) - provide own Hardhat config as optional argument ## Install diff --git a/diffyscan/diffyscan.py b/diffyscan/diffyscan.py index bb9e02b..31bc593 100755 --- a/diffyscan/diffyscan.py +++ b/diffyscan/diffyscan.py @@ -219,7 +219,7 @@ def process_config( hardhat_config_path: str, recursive_parsing: bool, unify_formatting: bool, - skip_binary_comparison: bool, + enable_binary_comparison: bool, ): logger.info(f"Loading config {path}...") config = load_config(path) @@ -239,26 +239,21 @@ def process_config( f'Failed to find explorer token in env ("ETHERSCAN_EXPLORER_TOKEN")' ) - if not skip_binary_comparison: - if "bytecode_comparison" not in config: - raise ValueError(f'Failed to find "bytecode_comparison" section in config') - github_api_token = os.getenv("GITHUB_API_TOKEN", "") if not github_api_token: raise ValueError("GITHUB_API_TOKEN variable is not set") - local_rpc_url = os.getenv("LOCAL_RPC_URL", "") - if not local_rpc_url: - raise ValueError("LOCAL_RPC_URL variable is not set") + if enable_binary_comparison: + if "bytecode_comparison" not in config: + raise ValueError(f'Failed to find "bytecode_comparison" section in config') - remote_rpc_url = os.getenv("REMOTE_RPC_URL", "") - if not remote_rpc_url: - raise ValueError("REMOTE_RPC_URL variable is not set") + local_rpc_url = load_env("LOCAL_RPC_URL", masked=False, required=True) + remote_rpc_url = load_env("REMOTE_RPC_URL", masked=True, required=True) - ExceptionHandler.initialize(config["fail_on_comparison_error"]) + ExceptionHandler.initialize(config["fail_on_comparison_error"]) try: - if not skip_binary_comparison: + if enable_binary_comparison: hardhat.start( hardhat_config_path, local_rpc_url, @@ -282,7 +277,7 @@ def process_config( recursive_parsing, unify_formatting, ) - if not skip_binary_comparison: + if enable_binary_comparison: run_bytecode_diff( contract_address, contract_name, @@ -299,7 +294,7 @@ def process_config( logger.info(f"Keyboard interrupt by user") finally: - if not skip_binary_comparison: + if enable_binary_comparison: hardhat.stop() @@ -332,9 +327,9 @@ def parse_arguments(): action="store_true", ) parser.add_argument( - "--skip-binary-comparison", + "--enable-binary-comparison", "-B", - help="Skip binary bytecode comparison", + help="Enable binary bytecode comparison", action="store_true", ) return parser.parse_args() @@ -359,7 +354,7 @@ def main(): hardhat_config_path, args.support_brownie, args.prettify, - args.skip_binary_comparison, + args.enable_binary_comparison, ) elif os.path.isfile(args.path): process_config( @@ -367,18 +362,20 @@ def main(): hardhat_config_path, args.support_brownie, args.prettify, - args.skip_binary_comparison, + args.enable_binary_comparison, ) elif os.path.isdir(args.path): for filename in os.listdir(args.path): - config_path = os.path.join(args.path, filename, args.skip_binary_comparison) + config_path = os.path.join( + args.path, filename, args.enable_binary_comparison + ) if os.path.isfile(config_path): process_config( config_path, hardhat_config_path, args.support_brownie, args.prettify, - args.skip_binary_comparison, + args.enable_binary_comparison, ) else: logger.error(f"Specified config path {args.path} not found") diff --git a/diffyscan/utils/custom_exceptions.py b/diffyscan/utils/custom_exceptions.py index 5b1c7bc..9dc9d1f 100644 --- a/diffyscan/utils/custom_exceptions.py +++ b/diffyscan/utils/custom_exceptions.py @@ -14,7 +14,9 @@ def __init__(self, reason: str): class NodeError(BaseCustomException): def __init__(self, reason: str): - super().__init__(f"Failed to receive bytecode from node: {reason}") + super().__init__( + f"Failed to receive bytecode from node: {reason}, please check hardhat config and fork chain_id" + ) class CalldataError(BaseCustomException): diff --git a/diffyscan/utils/hardhat.py b/diffyscan/utils/hardhat.py index e98e536..a5404df 100644 --- a/diffyscan/utils/hardhat.py +++ b/diffyscan/utils/hardhat.py @@ -4,6 +4,7 @@ import time from urllib.parse import urlparse +from .common import mask_text from .logger import logger from .custom_exceptions import HardhatError @@ -28,14 +29,18 @@ def start( f"Failed to find Hardhat config by path '{hardhat_config_path}'" ) - local_node_command = ( + local_node_command_prefix = ( f"npx hardhat node --hostname {parsed_url.hostname} " f"--port {parsed_url.port} " f"--config {hardhat_config_path} " - f"--fork {remote_rpc_url}" ) - logger.info(f'Trying to start Hardhat: "{local_node_command}"') + 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}"') is_port_used = self._is_port_in_use_(parsed_url) if is_port_used: answer = input( diff --git a/diffyscan/utils/node_handler.py b/diffyscan/utils/node_handler.py index d1d0f05..b7d98ce 100644 --- a/diffyscan/utils/node_handler.py +++ b/diffyscan/utils/node_handler.py @@ -1,12 +1,12 @@ import json -from .common import pull +from .common import pull, mask_text from .logger import logger from .custom_exceptions import NodeError def get_bytecode_from_node(contract_address, rpc_url): - logger.info(f'Receiving the bytecode from "{rpc_url}" ...') + logger.info(f'Receiving the bytecode from "{mask_text(rpc_url)}" ...') payload = json.dumps( { From fc1e3a6803c6c130381ed94228abc16bca7bb0ad Mon Sep 17 00:00:00 2001 From: Eugene M Date: Tue, 15 Oct 2024 21:22:28 +0300 Subject: [PATCH 113/118] fix: improve linting --- .husky/commit-msg | 1 + .husky/pre-commit | 10 +- .nvmrc | 1 + .prettierignore | 4 + commitlint.config.ts | 1 + diffyscan/diffyscan.py | 2 + package-lock.json | 2108 ++++++++++++++++++++++++++++++++++++---- package.json | 15 +- 8 files changed, 1930 insertions(+), 212 deletions(-) create mode 100644 .husky/commit-msg create mode 100644 .nvmrc create mode 100644 .prettierignore create mode 100644 commitlint.config.ts diff --git a/.husky/commit-msg b/.husky/commit-msg new file mode 100644 index 0000000..e65df50 --- /dev/null +++ b/.husky/commit-msg @@ -0,0 +1 @@ +npx commitlint --edit ${1} diff --git a/.husky/pre-commit b/.husky/pre-commit index b6f9048..2312dc5 100644 --- a/.husky/pre-commit +++ b/.husky/pre-commit @@ -1,9 +1 @@ -#!/bin/bash - -json_files=$(git diff --cached --name-only | grep '\.json$') -if [ -n "$json_files" ]; then -for file in $json_files; do - jq . "$file" > tmp.$$.json && mv tmp.$$.json "$file" - git add "$file" -done -fi +npx lint-staged diff --git a/.nvmrc b/.nvmrc new file mode 100644 index 0000000..7795cad --- /dev/null +++ b/.nvmrc @@ -0,0 +1 @@ +20.12 diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..4c338ff --- /dev/null +++ b/.prettierignore @@ -0,0 +1,4 @@ +.gitignore +.prettierignore + +package-lock.json diff --git a/commitlint.config.ts b/commitlint.config.ts new file mode 100644 index 0000000..5073c20 --- /dev/null +++ b/commitlint.config.ts @@ -0,0 +1 @@ +module.exports = { extends: ["@commitlint/config-conventional"] }; diff --git a/diffyscan/diffyscan.py b/diffyscan/diffyscan.py index 31bc593..8baece9 100755 --- a/diffyscan/diffyscan.py +++ b/diffyscan/diffyscan.py @@ -5,6 +5,8 @@ import os import traceback +"s" + from .utils.common import load_config, load_env, prettify_solidity from .utils.constants import ( diff --git a/package-lock.json b/package-lock.json index aca6d6c..5bd44fa 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5,11 +5,14 @@ "packages": { "": { "dependencies": { - "hardhat": "^2.22.7", + "hardhat": "^2.22.13", "kill-port": "^2.0.1" }, "devDependencies": { + "@commitlint/cli": "^19.5.0", + "@commitlint/config-conventional": "^19.5.0", "husky": "^9.1.6", + "lint-staged": "^15.2.10", "prettier": "^3.3.0", "prettier-plugin-solidity": "^1.3.1" }, @@ -17,6 +20,339 @@ "node": ">=20.0" } }, + "node_modules/@babel/code-frame": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.25.7.tgz", + "integrity": "sha512-0xZJFNE5XMpENsgfHYTw8FbX4kv53mFLn2i3XPoq69LyhYSCBJtitaHx9QnsVTrsogI4Z3+HtEfZ2/GFPOtf5g==", + "dev": true, + "dependencies": { + "@babel/highlight": "^7.25.7", + "picocolors": "^1.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.7.tgz", + "integrity": "sha512-AM6TzwYqGChO45oiuPqwL2t20/HdMC1rTPAesnBCgPCSF1x3oN9MVUwQV2iyz4xqWrctwK5RNC8LV22kaQCNYg==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.25.7.tgz", + "integrity": "sha512-iYyACpW3iW8Fw+ZybQK+drQre+ns/tKpXbNESfrhNnPLIklLbXr7MYJ6gPEd0iETGLOK+SxMjVvKb/ffmk+FEw==", + "dev": true, + "dependencies": { + "@babel/helper-validator-identifier": "^7.25.7", + "chalk": "^2.4.2", + "js-tokens": "^4.0.0", + "picocolors": "^1.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@babel/highlight/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@commitlint/cli": { + "version": "19.5.0", + "resolved": "https://registry.npmjs.org/@commitlint/cli/-/cli-19.5.0.tgz", + "integrity": "sha512-gaGqSliGwB86MDmAAKAtV9SV1SHdmN8pnGq4EJU4+hLisQ7IFfx4jvU4s+pk6tl0+9bv6yT+CaZkufOinkSJIQ==", + "dev": true, + "dependencies": { + "@commitlint/format": "^19.5.0", + "@commitlint/lint": "^19.5.0", + "@commitlint/load": "^19.5.0", + "@commitlint/read": "^19.5.0", + "@commitlint/types": "^19.5.0", + "tinyexec": "^0.3.0", + "yargs": "^17.0.0" + }, + "bin": { + "commitlint": "cli.js" + }, + "engines": { + "node": ">=v18" + } + }, + "node_modules/@commitlint/config-conventional": { + "version": "19.5.0", + "resolved": "https://registry.npmjs.org/@commitlint/config-conventional/-/config-conventional-19.5.0.tgz", + "integrity": "sha512-OBhdtJyHNPryZKg0fFpZNOBM1ZDbntMvqMuSmpfyP86XSfwzGw4CaoYRG4RutUPg0BTK07VMRIkNJT6wi2zthg==", + "dev": true, + "dependencies": { + "@commitlint/types": "^19.5.0", + "conventional-changelog-conventionalcommits": "^7.0.2" + }, + "engines": { + "node": ">=v18" + } + }, + "node_modules/@commitlint/config-validator": { + "version": "19.5.0", + "resolved": "https://registry.npmjs.org/@commitlint/config-validator/-/config-validator-19.5.0.tgz", + "integrity": "sha512-CHtj92H5rdhKt17RmgALhfQt95VayrUo2tSqY9g2w+laAXyk7K/Ef6uPm9tn5qSIwSmrLjKaXK9eiNuxmQrDBw==", + "dev": true, + "dependencies": { + "@commitlint/types": "^19.5.0", + "ajv": "^8.11.0" + }, + "engines": { + "node": ">=v18" + } + }, + "node_modules/@commitlint/ensure": { + "version": "19.5.0", + "resolved": "https://registry.npmjs.org/@commitlint/ensure/-/ensure-19.5.0.tgz", + "integrity": "sha512-Kv0pYZeMrdg48bHFEU5KKcccRfKmISSm9MvgIgkpI6m+ohFTB55qZlBW6eYqh/XDfRuIO0x4zSmvBjmOwWTwkg==", + "dev": true, + "dependencies": { + "@commitlint/types": "^19.5.0", + "lodash.camelcase": "^4.3.0", + "lodash.kebabcase": "^4.1.1", + "lodash.snakecase": "^4.1.1", + "lodash.startcase": "^4.4.0", + "lodash.upperfirst": "^4.3.1" + }, + "engines": { + "node": ">=v18" + } + }, + "node_modules/@commitlint/execute-rule": { + "version": "19.5.0", + "resolved": "https://registry.npmjs.org/@commitlint/execute-rule/-/execute-rule-19.5.0.tgz", + "integrity": "sha512-aqyGgytXhl2ejlk+/rfgtwpPexYyri4t8/n4ku6rRJoRhGZpLFMqrZ+YaubeGysCP6oz4mMA34YSTaSOKEeNrg==", + "dev": true, + "engines": { + "node": ">=v18" + } + }, + "node_modules/@commitlint/format": { + "version": "19.5.0", + "resolved": "https://registry.npmjs.org/@commitlint/format/-/format-19.5.0.tgz", + "integrity": "sha512-yNy088miE52stCI3dhG/vvxFo9e4jFkU1Mj3xECfzp/bIS/JUay4491huAlVcffOoMK1cd296q0W92NlER6r3A==", + "dev": true, + "dependencies": { + "@commitlint/types": "^19.5.0", + "chalk": "^5.3.0" + }, + "engines": { + "node": ">=v18" + } + }, + "node_modules/@commitlint/is-ignored": { + "version": "19.5.0", + "resolved": "https://registry.npmjs.org/@commitlint/is-ignored/-/is-ignored-19.5.0.tgz", + "integrity": "sha512-0XQ7Llsf9iL/ANtwyZ6G0NGp5Y3EQ8eDQSxv/SRcfJ0awlBY4tHFAvwWbw66FVUaWICH7iE5en+FD9TQsokZ5w==", + "dev": true, + "dependencies": { + "@commitlint/types": "^19.5.0", + "semver": "^7.6.0" + }, + "engines": { + "node": ">=v18" + } + }, + "node_modules/@commitlint/lint": { + "version": "19.5.0", + "resolved": "https://registry.npmjs.org/@commitlint/lint/-/lint-19.5.0.tgz", + "integrity": "sha512-cAAQwJcRtiBxQWO0eprrAbOurtJz8U6MgYqLz+p9kLElirzSCc0vGMcyCaA1O7AqBuxo11l1XsY3FhOFowLAAg==", + "dev": true, + "dependencies": { + "@commitlint/is-ignored": "^19.5.0", + "@commitlint/parse": "^19.5.0", + "@commitlint/rules": "^19.5.0", + "@commitlint/types": "^19.5.0" + }, + "engines": { + "node": ">=v18" + } + }, + "node_modules/@commitlint/load": { + "version": "19.5.0", + "resolved": "https://registry.npmjs.org/@commitlint/load/-/load-19.5.0.tgz", + "integrity": "sha512-INOUhkL/qaKqwcTUvCE8iIUf5XHsEPCLY9looJ/ipzi7jtGhgmtH7OOFiNvwYgH7mA8osUWOUDV8t4E2HAi4xA==", + "dev": true, + "dependencies": { + "@commitlint/config-validator": "^19.5.0", + "@commitlint/execute-rule": "^19.5.0", + "@commitlint/resolve-extends": "^19.5.0", + "@commitlint/types": "^19.5.0", + "chalk": "^5.3.0", + "cosmiconfig": "^9.0.0", + "cosmiconfig-typescript-loader": "^5.0.0", + "lodash.isplainobject": "^4.0.6", + "lodash.merge": "^4.6.2", + "lodash.uniq": "^4.5.0" + }, + "engines": { + "node": ">=v18" + } + }, + "node_modules/@commitlint/message": { + "version": "19.5.0", + "resolved": "https://registry.npmjs.org/@commitlint/message/-/message-19.5.0.tgz", + "integrity": "sha512-R7AM4YnbxN1Joj1tMfCyBryOC5aNJBdxadTZkuqtWi3Xj0kMdutq16XQwuoGbIzL2Pk62TALV1fZDCv36+JhTQ==", + "dev": true, + "engines": { + "node": ">=v18" + } + }, + "node_modules/@commitlint/parse": { + "version": "19.5.0", + "resolved": "https://registry.npmjs.org/@commitlint/parse/-/parse-19.5.0.tgz", + "integrity": "sha512-cZ/IxfAlfWYhAQV0TwcbdR1Oc0/r0Ik1GEessDJ3Lbuma/MRO8FRQX76eurcXtmhJC//rj52ZSZuXUg0oIX0Fw==", + "dev": true, + "dependencies": { + "@commitlint/types": "^19.5.0", + "conventional-changelog-angular": "^7.0.0", + "conventional-commits-parser": "^5.0.0" + }, + "engines": { + "node": ">=v18" + } + }, + "node_modules/@commitlint/read": { + "version": "19.5.0", + "resolved": "https://registry.npmjs.org/@commitlint/read/-/read-19.5.0.tgz", + "integrity": "sha512-TjS3HLPsLsxFPQj6jou8/CZFAmOP2y+6V4PGYt3ihbQKTY1Jnv0QG28WRKl/d1ha6zLODPZqsxLEov52dhR9BQ==", + "dev": true, + "dependencies": { + "@commitlint/top-level": "^19.5.0", + "@commitlint/types": "^19.5.0", + "git-raw-commits": "^4.0.0", + "minimist": "^1.2.8", + "tinyexec": "^0.3.0" + }, + "engines": { + "node": ">=v18" + } + }, + "node_modules/@commitlint/resolve-extends": { + "version": "19.5.0", + "resolved": "https://registry.npmjs.org/@commitlint/resolve-extends/-/resolve-extends-19.5.0.tgz", + "integrity": "sha512-CU/GscZhCUsJwcKTJS9Ndh3AKGZTNFIOoQB2n8CmFnizE0VnEuJoum+COW+C1lNABEeqk6ssfc1Kkalm4bDklA==", + "dev": true, + "dependencies": { + "@commitlint/config-validator": "^19.5.0", + "@commitlint/types": "^19.5.0", + "global-directory": "^4.0.1", + "import-meta-resolve": "^4.0.0", + "lodash.mergewith": "^4.6.2", + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=v18" + } + }, + "node_modules/@commitlint/rules": { + "version": "19.5.0", + "resolved": "https://registry.npmjs.org/@commitlint/rules/-/rules-19.5.0.tgz", + "integrity": "sha512-hDW5TPyf/h1/EufSHEKSp6Hs+YVsDMHazfJ2azIk9tHPXS6UqSz1dIRs1gpqS3eMXgtkT7JH6TW4IShdqOwhAw==", + "dev": true, + "dependencies": { + "@commitlint/ensure": "^19.5.0", + "@commitlint/message": "^19.5.0", + "@commitlint/to-lines": "^19.5.0", + "@commitlint/types": "^19.5.0" + }, + "engines": { + "node": ">=v18" + } + }, + "node_modules/@commitlint/to-lines": { + "version": "19.5.0", + "resolved": "https://registry.npmjs.org/@commitlint/to-lines/-/to-lines-19.5.0.tgz", + "integrity": "sha512-R772oj3NHPkodOSRZ9bBVNq224DOxQtNef5Pl8l2M8ZnkkzQfeSTr4uxawV2Sd3ui05dUVzvLNnzenDBO1KBeQ==", + "dev": true, + "engines": { + "node": ">=v18" + } + }, + "node_modules/@commitlint/top-level": { + "version": "19.5.0", + "resolved": "https://registry.npmjs.org/@commitlint/top-level/-/top-level-19.5.0.tgz", + "integrity": "sha512-IP1YLmGAk0yWrImPRRc578I3dDUI5A2UBJx9FbSOjxe9sTlzFiwVJ+zeMLgAtHMtGZsC8LUnzmW1qRemkFU4ng==", + "dev": true, + "dependencies": { + "find-up": "^7.0.0" + }, + "engines": { + "node": ">=v18" + } + }, + "node_modules/@commitlint/types": { + "version": "19.5.0", + "resolved": "https://registry.npmjs.org/@commitlint/types/-/types-19.5.0.tgz", + "integrity": "sha512-DSHae2obMSMkAtTBSOulg5X7/z+rGLxcXQIkg3OmWvY6wifojge5uVMydfhUvs7yQj+V7jNmRZ2Xzl8GJyqRgg==", + "dev": true, + "dependencies": { + "@types/conventional-commits-parser": "^5.0.0", + "chalk": "^5.3.0" + }, + "engines": { + "node": ">=v18" + } + }, "node_modules/@ethersproject/abi": { "version": "5.7.0", "resolved": "https://registry.npmjs.org/@ethersproject/abi/-/abi-5.7.0.tgz", @@ -437,74 +773,74 @@ ] }, "node_modules/@nomicfoundation/edr": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/@nomicfoundation/edr/-/edr-0.6.1.tgz", - "integrity": "sha512-ILlhHzUgVQ+5kpZ35fxMI1xwaqxfZV8V8l+pKo1RZHvLKf80Azvq1OLg1RfxstLIA2QB+KBpch9QfPiD5fBpdw==", + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/@nomicfoundation/edr/-/edr-0.6.3.tgz", + "integrity": "sha512-hThe5ORR75WFYTXKL0K2AyLDxkTMrG+VQ1yL9BhQYsuh3OIH+3yNDxMz2LjfvrpOrMmJ4kk5NKdFewpqDojjXQ==", "dependencies": { - "@nomicfoundation/edr-darwin-arm64": "0.6.1", - "@nomicfoundation/edr-darwin-x64": "0.6.1", - "@nomicfoundation/edr-linux-arm64-gnu": "0.6.1", - "@nomicfoundation/edr-linux-arm64-musl": "0.6.1", - "@nomicfoundation/edr-linux-x64-gnu": "0.6.1", - "@nomicfoundation/edr-linux-x64-musl": "0.6.1", - "@nomicfoundation/edr-win32-x64-msvc": "0.6.1" + "@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" }, "engines": { "node": ">= 18" } }, "node_modules/@nomicfoundation/edr-darwin-arm64": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-darwin-arm64/-/edr-darwin-arm64-0.6.1.tgz", - "integrity": "sha512-ncZs0yRoxbiJB+sg7w2K6BLgMnAgOK/IcGuuZaNHKwiMHk19Kn2JDl+5fUOzkIGNpaCf8uvoEb2q6K7212KjQA==", + "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==", "engines": { "node": ">= 18" } }, "node_modules/@nomicfoundation/edr-darwin-x64": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-darwin-x64/-/edr-darwin-x64-0.6.1.tgz", - "integrity": "sha512-Akubo27DS0D921aaApD+IRlv1niLiARWPrUDDBOKjCYKGVrJUKmAdH14qBzZwHBcQBhyVMXgxlSiyIgiqIHHjA==", + "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==", "engines": { "node": ">= 18" } }, "node_modules/@nomicfoundation/edr-linux-arm64-gnu": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-linux-arm64-gnu/-/edr-linux-arm64-gnu-0.6.1.tgz", - "integrity": "sha512-t+Lb5pyWYe4xJs9dA1jdhUOLxmgzFAa/SSmDZBC5vbCiDic5brUfgtPL//uMI8DKElXm9RSsRwXB5x/6+UhFEw==", + "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==", "engines": { "node": ">= 18" } }, "node_modules/@nomicfoundation/edr-linux-arm64-musl": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-linux-arm64-musl/-/edr-linux-arm64-musl-0.6.1.tgz", - "integrity": "sha512-dxv2wtnb1vE7MLQfy7mmXObhX585gBGB+kJZRj+K5z+0uVNM1Xep0kH+XpuGHAT0C/w/7YEWXrrsjTpxadcpnw==", + "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==", "engines": { "node": ">= 18" } }, "node_modules/@nomicfoundation/edr-linux-x64-gnu": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-linux-x64-gnu/-/edr-linux-x64-gnu-0.6.1.tgz", - "integrity": "sha512-8XwZRYCcChHNrdWPdsyE8lotJ/0702hKMA7tueo5T2SSK8YRLAq8tbshDPxzrNKztP1I+SbH2Y+sucKCscJOUg==", + "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==", "engines": { "node": ">= 18" } }, "node_modules/@nomicfoundation/edr-linux-x64-musl": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-linux-x64-musl/-/edr-linux-x64-musl-0.6.1.tgz", - "integrity": "sha512-aySKfZtDxaD365qkEVqqMXatDa0tsq3gVGUz18lvy+14lGCzEAPIQNo2lJAM26k0w3HbOuIFCzI2FbksAi245A==", + "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==", "engines": { "node": ">= 18" } }, "node_modules/@nomicfoundation/edr-win32-x64-msvc": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-win32-x64-msvc/-/edr-win32-x64-msvc-0.6.1.tgz", - "integrity": "sha512-yNBdEjC6fi3dUizgTNRNcgs4y7CnGxkyC4bJkGcwZzJ7Hb88/ODay+RWcVpwyZNobzsiDc9zrKs2h3+4j/0FLQ==", + "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==", "engines": { "node": ">= 18" } @@ -842,15 +1178,24 @@ "@types/node": "*" } }, + "node_modules/@types/conventional-commits-parser": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@types/conventional-commits-parser/-/conventional-commits-parser-5.0.0.tgz", + "integrity": "sha512-loB369iXNmAZglwWATL+WRe+CRMmmBPtpolYzIebFaX4YA3x+BEfLqhUAV9WanycKI3TG1IMr5bMJDajDKLlUQ==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/lru-cache": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/@types/lru-cache/-/lru-cache-5.1.1.tgz", "integrity": "sha512-ssE3Vlrys7sdIzs5LOxCzTVMsU7i9oa/IaW92wF32JFb3CVczqOkru2xspuKczHEbG3nvmPY7IFqVmGGHdNbYw==" }, "node_modules/@types/node": { - "version": "22.6.1", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.6.1.tgz", - "integrity": "sha512-V48tCfcKb/e6cVUigLAaJDAILdMP0fUW6BidkPK4GpGjXcfbnoHasCZDwz3N3yVt5we2RHm4XTQCpv0KJz9zqw==", + "version": "22.7.5", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.7.5.tgz", + "integrity": "sha512-jML7s2NAzMWc//QSJ1a3prpk78cOPchGvXJsC3C6R6PSMoooztvRVQEz89gmBTBY1SPMaqo5teB4uNHPdetShQ==", "dependencies": { "undici-types": "~6.19.2" } @@ -902,6 +1247,22 @@ "node": ">=8" } }, + "node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, "node_modules/ansi-align": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.1.tgz", @@ -941,14 +1302,15 @@ } }, "node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dependencies": { - "color-convert": "^1.9.0" - }, + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true, "engines": { - "node": ">=4" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, "node_modules/anymatch": { @@ -968,6 +1330,12 @@ "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" }, + "node_modules/array-ify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/array-ify/-/array-ify-1.0.0.tgz", + "integrity": "sha512-c5AMf34bKdvPhQ7tBGhqkgKNUzMr4WUs+WDtC2ZUGOUncbxKMTvqxYctiseW3+L4bA8ec+GcZ6/A/FW4m8ukng==", + "dev": true + }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -1068,14 +1436,6 @@ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" }, - "node_modules/boxen/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "engines": { - "node": ">=8" - } - }, "node_modules/boxen/node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -1177,6 +1537,15 @@ "node": ">= 0.8" } }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, "node_modules/camelcase": { "version": "6.3.0", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", @@ -1189,16 +1558,15 @@ } }, "node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", + "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", + "dev": true, "engines": { - "node": ">=4" + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" } }, "node_modules/chokidar": { @@ -1248,22 +1616,107 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/cliui": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", - "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "node_modules/cli-cursor": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-5.0.0.tgz", + "integrity": "sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==", + "dev": true, "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^7.0.0" + "restore-cursor": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "node_modules/cli-truncate": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-4.0.0.tgz", + "integrity": "sha512-nPdaFdQ0h/GEigbPClz11D0v/ZJEwxmeVZGeMo3Z5StPtUTkA9o1lD6QwoirYiSDzbcwn2XcjwmCp68W1IS4TA==", + "dev": true, "dependencies": { - "color-name": "1.1.3" + "slice-ansi": "^5.0.0", + "string-width": "^7.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-truncate/node_modules/ansi-regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/cli-truncate/node_modules/emoji-regex": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.4.0.tgz", + "integrity": "sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw==", + "dev": true + }, + "node_modules/cli-truncate/node_modules/string-width": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", + "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", + "dev": true, + "dependencies": { + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-truncate/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dependencies": { + "color-name": "1.1.3" } }, "node_modules/color-name": { @@ -1271,17 +1724,34 @@ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" }, + "node_modules/colorette": { + "version": "2.0.20", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", + "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==", + "dev": true + }, "node_modules/command-exists": { "version": "1.2.9", "resolved": "https://registry.npmjs.org/command-exists/-/command-exists-1.2.9.tgz", "integrity": "sha512-LTQ/SGc+s0Xc0Fu5WaKnR0YiygZkm9eKFvyS+fRsU7/ZWFF8ykFM6Pc9aCVf1+xasOOZpO3BAVgVrKvsqKHV7w==" }, "node_modules/commander": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", - "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==", + "version": "12.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz", + "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==", + "dev": true, "engines": { - "node": ">= 12" + "node": ">=18" + } + }, + "node_modules/compare-func": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/compare-func/-/compare-func-2.0.0.tgz", + "integrity": "sha512-zHig5N+tPWARooBnb0Zx1MFcdfpyJrfTJ3Y5L+IFvUm8rM74hHz66z0gw0x4tijh5CorKkKUCnW82R2vmpeCRA==", + "dev": true, + "dependencies": { + "array-ify": "^1.0.0", + "dot-prop": "^5.1.0" } }, "node_modules/concat-map": { @@ -1289,6 +1759,48 @@ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" }, + "node_modules/conventional-changelog-angular": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/conventional-changelog-angular/-/conventional-changelog-angular-7.0.0.tgz", + "integrity": "sha512-ROjNchA9LgfNMTTFSIWPzebCwOGFdgkEq45EnvvrmSLvCtAw0HSmrCs7/ty+wAeYUZyNay0YMUNYFTRL72PkBQ==", + "dev": true, + "dependencies": { + "compare-func": "^2.0.0" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/conventional-changelog-conventionalcommits": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/conventional-changelog-conventionalcommits/-/conventional-changelog-conventionalcommits-7.0.2.tgz", + "integrity": "sha512-NKXYmMR/Hr1DevQegFB4MwfM5Vv0m4UIxKZTTYuD98lpTknaZlSRrDOG4X7wIXpGkfsYxZTghUN+Qq+T0YQI7w==", + "dev": true, + "dependencies": { + "compare-func": "^2.0.0" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/conventional-commits-parser": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/conventional-commits-parser/-/conventional-commits-parser-5.0.0.tgz", + "integrity": "sha512-ZPMl0ZJbw74iS9LuX9YIAiW8pfM5p3yh2o/NbXHbkFuZzY5jvdi5jFycEOkmBW5H5I7nA+D6f3UcsCLP2vvSEA==", + "dev": true, + "dependencies": { + "is-text-path": "^2.0.0", + "JSONStream": "^1.3.5", + "meow": "^12.0.1", + "split2": "^4.0.0" + }, + "bin": { + "conventional-commits-parser": "cli.mjs" + }, + "engines": { + "node": ">=16" + } + }, "node_modules/cookie": { "version": "0.4.2", "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz", @@ -1297,6 +1809,49 @@ "node": ">= 0.6" } }, + "node_modules/cosmiconfig": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-9.0.0.tgz", + "integrity": "sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg==", + "dev": true, + "dependencies": { + "env-paths": "^2.2.1", + "import-fresh": "^3.3.0", + "js-yaml": "^4.1.0", + "parse-json": "^5.2.0" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/d-fischer" + }, + "peerDependencies": { + "typescript": ">=4.9.5" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/cosmiconfig-typescript-loader": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cosmiconfig-typescript-loader/-/cosmiconfig-typescript-loader-5.0.0.tgz", + "integrity": "sha512-+8cK7jRAReYkMwMiG+bxhcNKiHJDM6bR9FD/nGBXOWdMLuYawjF5cGrtLilJ+LGd3ZjCXnJjR5DkfWPoIVlqJA==", + "dev": true, + "dependencies": { + "jiti": "^1.19.1" + }, + "engines": { + "node": ">=v16" + }, + "peerDependencies": { + "@types/node": "*", + "cosmiconfig": ">=8.2", + "typescript": ">=4" + } + }, "node_modules/create-hash": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", @@ -1322,6 +1877,32 @@ "sha.js": "^2.4.8" } }, + "node_modules/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/dargs": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/dargs/-/dargs-8.1.0.tgz", + "integrity": "sha512-wAV9QHOsNbwnWdNW2FYvE1P56wtgSbM+3SZcdGiWQILwVjACCXDCI3Ai8QlCjMDB8YK5zySiXZYBiwGmNY3lnw==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/debug": { "version": "4.3.7", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", @@ -1365,6 +1946,18 @@ "node": ">=0.3.1" } }, + "node_modules/dot-prop": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz", + "integrity": "sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==", + "dev": true, + "dependencies": { + "is-obj": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/elliptic": { "version": "6.5.4", "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz", @@ -1409,6 +2002,27 @@ "node": ">=6" } }, + "node_modules/environment": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/environment/-/environment-1.1.0.tgz", + "integrity": "sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, "node_modules/escalade": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", @@ -1418,11 +2032,14 @@ } }, "node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", "engines": { - "node": ">=0.8.0" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/ethereum-cryptography": { @@ -1512,6 +2129,12 @@ "npm": ">=3" } }, + "node_modules/eventemitter3": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", + "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==", + "dev": true + }, "node_modules/evp_bytestokey": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", @@ -1521,6 +2144,41 @@ "safe-buffer": "^5.1.1" } }, + "node_modules/execa": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-8.0.1.tgz", + "integrity": "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^8.0.1", + "human-signals": "^5.0.0", + "is-stream": "^3.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^5.1.0", + "onetime": "^6.0.0", + "signal-exit": "^4.1.0", + "strip-final-newline": "^3.0.0" + }, + "engines": { + "node": ">=16.17" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "node_modules/fast-uri": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.3.tgz", + "integrity": "sha512-aLrHthzCjH5He4Z2H9YZ+v6Ujb9ocRuW6ZzkJQOrTxleEijANq4v1TsaPaVG1PZcuurEzrLcWRyYBYXD5cEiaw==", + "dev": true + }, "node_modules/fill-range": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", @@ -1533,14 +2191,20 @@ } }, "node_modules/find-up": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", - "integrity": "sha512-NWzkk0jSJtTt08+FBFMvXoeZnOJD+jTtsRmBYbAIzJdX6l7dLgR7CTubCM5/eDdPUBvLCeVasP1brfVR/9/EZQ==", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-7.0.0.tgz", + "integrity": "sha512-YyZM99iHrqLKjmt4LJDj58KI+fYyufRLBSYcqycxf//KpBk9FoewoGX0450m9nB44qrZnovzC2oeP5hUibxc/g==", + "dev": true, "dependencies": { - "locate-path": "^2.0.0" + "locate-path": "^7.2.0", + "path-exists": "^5.0.0", + "unicorn-magic": "^0.1.0" }, "engines": { - "node": ">=4" + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/flat": { @@ -1614,11 +2278,52 @@ "node": "6.* || 8.* || >= 10.*" } }, + "node_modules/get-east-asian-width": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.3.0.tgz", + "integrity": "sha512-vpeMIQKxczTD/0s2CdEWHcb0eeJe6TFjxb+J5xgX7hScxqrGuyjmv4c1D4A/gelKfyox0gJJwIHF+fLjeaM8kQ==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/get-stream": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-8.0.1.tgz", + "integrity": "sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==", + "dev": true, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/get-them-args": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/get-them-args/-/get-them-args-1.3.2.tgz", "integrity": "sha512-LRn8Jlk+DwZE4GTlDbT3Hikd1wSHgLMme/+7ddlqKd7ldwR6LjJgTVWzBnR01wnYGe4KgrXjg287RaI22UHmAw==" }, + "node_modules/git-raw-commits": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/git-raw-commits/-/git-raw-commits-4.0.0.tgz", + "integrity": "sha512-ICsMM1Wk8xSGMowkOmPrzo2Fgmfo4bMHLNX6ytHjajRJUqvHOw/TFapQ+QG75c3X/tTDDhOSRPGC52dDbNM8FQ==", + "dev": true, + "dependencies": { + "dargs": "^8.0.0", + "meow": "^12.0.1", + "split2": "^4.0.0" + }, + "bin": { + "git-raw-commits": "cli.mjs" + }, + "engines": { + "node": ">=16" + } + }, "node_modules/glob": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", @@ -1650,19 +2355,34 @@ "node": ">= 6" } }, + "node_modules/global-directory": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/global-directory/-/global-directory-4.0.1.tgz", + "integrity": "sha512-wHTUcDUoZ1H5/0iVqEudYW4/kAlN5cZ3j/bXn0Dpbizl9iaUVeWSHqiOjsgk6OW2bkLclbBjzewBz6weQ1zA2Q==", + "dev": true, + "dependencies": { + "ini": "4.1.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/graceful-fs": { "version": "4.2.11", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==" }, "node_modules/hardhat": { - "version": "2.22.12", - "resolved": "https://registry.npmjs.org/hardhat/-/hardhat-2.22.12.tgz", - "integrity": "sha512-yok65M+LsOeTBHQsjg//QreGCyrsaNmeLVzhTFqlOvZ4ZE5y69N0wRxH1b2BC9dGK8S8OPUJMNiL9X0RAvbm8w==", + "version": "2.22.13", + "resolved": "https://registry.npmjs.org/hardhat/-/hardhat-2.22.13.tgz", + "integrity": "sha512-psVJX4FSXDpSXwsU8OcKTJN04pQEj9cFBMX5OPko+OFwbIoiOpvRmafa954/UaA1934npTj8sV3gaTSdx9bPbA==", "dependencies": { "@ethersproject/abi": "^5.1.2", "@metamask/eth-sig-util": "^4.0.0", - "@nomicfoundation/edr": "^0.6.1", + "@nomicfoundation/edr": "^0.6.3", "@nomicfoundation/ethereumjs-common": "4.0.4", "@nomicfoundation/ethereumjs-tx": "5.0.4", "@nomicfoundation/ethereumjs-util": "9.0.4", @@ -1721,35 +2441,147 @@ } } }, - "node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "node_modules/hardhat/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dependencies": { + "color-convert": "^1.9.0" + }, "engines": { "node": ">=4" } }, - "node_modules/hash-base": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.1.0.tgz", - "integrity": "sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA==", + "node_modules/hardhat/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", "dependencies": { - "inherits": "^2.0.4", - "readable-stream": "^3.6.0", - "safe-buffer": "^5.2.0" + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" }, "engines": { "node": ">=4" } }, - "node_modules/hash.js": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", - "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", - "dependencies": { - "inherits": "^2.0.3", - "minimalistic-assert": "^1.0.1" - } + "node_modules/hardhat/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/hardhat/node_modules/find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha512-NWzkk0jSJtTt08+FBFMvXoeZnOJD+jTtsRmBYbAIzJdX6l7dLgR7CTubCM5/eDdPUBvLCeVasP1brfVR/9/EZQ==", + "dependencies": { + "locate-path": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/hardhat/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "engines": { + "node": ">=4" + } + }, + "node_modules/hardhat/node_modules/locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha512-NCI2kiDkyR7VeEKm27Kda/iQHyKJe1Bu0FlTbYp3CqJu+9IFe9bLyAjMxf5ZDDbEg+iMPzB5zYyUTSm8wVTKmA==", + "dependencies": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/hardhat/node_modules/p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "dependencies": { + "p-try": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/hardhat/node_modules/p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha512-nQja7m7gSKuewoVRen45CtVfODR3crN3goVQ0DDZ9N3yHxgpkuBhZqsaiotSQRrADUrne346peY7kT3TSACykg==", + "dependencies": { + "p-limit": "^1.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/hardhat/node_modules/path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", + "engines": { + "node": ">=4" + } + }, + "node_modules/hardhat/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/hardhat/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/hash-base": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.1.0.tgz", + "integrity": "sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA==", + "dependencies": { + "inherits": "^2.0.4", + "readable-stream": "^3.6.0", + "safe-buffer": "^5.2.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/hash.js": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", + "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", + "dependencies": { + "inherits": "^2.0.3", + "minimalistic-assert": "^1.0.1" + } }, "node_modules/he": { "version": "1.2.0", @@ -1796,6 +2628,15 @@ "node": ">= 6" } }, + "node_modules/human-signals": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-5.0.0.tgz", + "integrity": "sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==", + "dev": true, + "engines": { + "node": ">=16.17.0" + } + }, "node_modules/husky": { "version": "9.1.6", "resolved": "https://registry.npmjs.org/husky/-/husky-9.1.6.tgz", @@ -1827,6 +2668,41 @@ "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.7.tgz", "integrity": "sha512-1hqclzwYwjRDFLjcFxOM5AYkkG0rpFPpr1RLPMEuGczoS7YA8gLhy8SWXYRAA/XwfEHpfo3cw5JGioS32fnMRw==" }, + "node_modules/import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/import-fresh/node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/import-meta-resolve": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/import-meta-resolve/-/import-meta-resolve-4.1.0.tgz", + "integrity": "sha512-I6fiaX09Xivtk+THaMfAwnA3MVA5Big1WHF1Dfx9hFuvNIWpXnorlkzhcQf6ehrqQiiZECRt1poOAkPmer3ruw==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/indent-string": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", @@ -1850,6 +2726,15 @@ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, + "node_modules/ini": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ini/-/ini-4.1.1.tgz", + "integrity": "sha512-QQnnxNyfvmHFIsj7gkPcYymR8Jdw/o7mp5ZFihxn6h8Ci6fh3Dx4E1gPjpQEpIuPo9XVNY/ZUwh4BPMjGyL01g==", + "dev": true, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, "node_modules/io-ts": { "version": "1.10.4", "resolved": "https://registry.npmjs.org/io-ts/-/io-ts-1.10.4.tgz", @@ -1858,6 +2743,12 @@ "fp-ts": "^1.0.0" } }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "dev": true + }, "node_modules/is-binary-path": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", @@ -1878,11 +2769,15 @@ } }, "node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-4.0.0.tgz", + "integrity": "sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==", + "dev": true, "engines": { - "node": ">=8" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/is-glob": { @@ -1913,6 +2808,15 @@ "node": ">=0.12.0" } }, + "node_modules/is-obj": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", + "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/is-plain-obj": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", @@ -1921,6 +2825,30 @@ "node": ">=8" } }, + "node_modules/is-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", + "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", + "dev": true, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-text-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-text-path/-/is-text-path-2.0.0.tgz", + "integrity": "sha512-+oDTluR6WEjdXEJMnC2z6A4FRwFoYuvShVVEGsS7ewc0UTi2QtAKMDJuL4BDEVt+5T7MjFo12RP8ghOM75oKJw==", + "dev": true, + "dependencies": { + "text-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/is-unicode-supported": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", @@ -1932,11 +2860,32 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, + "node_modules/jiti": { + "version": "1.21.6", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.6.tgz", + "integrity": "sha512-2yTgeWTWzMWkHu6Jp9NKgePDaYHbntiwvYuuJLbbN9vl7DC9DvXKOB2BC3ZZ92D3cvV/aflH0osDfwpHepQ53w==", + "dev": true, + "bin": { + "jiti": "bin/jiti.js" + } + }, "node_modules/js-sha3": { "version": "0.8.0", "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.8.0.tgz", "integrity": "sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q==" }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + }, "node_modules/js-yaml": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", @@ -1948,10 +2897,22 @@ "js-yaml": "bin/js-yaml.js" } }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true + }, + "node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true + }, "node_modules/json-stream-stringify": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/json-stream-stringify/-/json-stream-stringify-3.1.4.tgz", - "integrity": "sha512-oGoz05ft577LolnXFQHD2CjnXDxXVA5b8lHwfEZgRXQUZeCMo6sObQQRq+NXuHQ3oTeMZHHmmPY2rjVwyqR62A==", + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/json-stream-stringify/-/json-stream-stringify-3.1.6.tgz", + "integrity": "sha512-x7fpwxOkbhFCaJDJ8vb1fBY3DdSa4AlITaz+HHILQJzdPMnHEFjxPwVUi1ALIbcIxDE0PNe/0i7frnY8QnBQog==", "engines": { "node": ">=7.10.1" } @@ -1964,6 +2925,31 @@ "graceful-fs": "^4.1.6" } }, + "node_modules/jsonparse": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz", + "integrity": "sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==", + "dev": true, + "engines": [ + "node >= 0.2.0" + ] + }, + "node_modules/JSONStream": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz", + "integrity": "sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==", + "dev": true, + "dependencies": { + "jsonparse": "^1.2.0", + "through": ">=2.2.7 <3" + }, + "bin": { + "JSONStream": "bin.js" + }, + "engines": { + "node": "*" + } + }, "node_modules/keccak": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/keccak/-/keccak-3.0.4.tgz", @@ -1990,16 +2976,148 @@ "kill-port": "cli.js" } }, + "node_modules/lilconfig": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.2.tgz", + "integrity": "sha512-eop+wDAvpItUys0FWkHIKeC9ybYrTGbU41U5K7+bttZZeohvnY7M9dZ5kB21GNWiFT2q1OoPTvncPCgSOVO5ow==", + "dev": true, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antonk52" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true + }, + "node_modules/lint-staged": { + "version": "15.2.10", + "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-15.2.10.tgz", + "integrity": "sha512-5dY5t743e1byO19P9I4b3x8HJwalIznL5E1FWYnU6OWw33KxNBSLAc6Cy7F2PsFEO8FKnLwjwm5hx7aMF0jzZg==", + "dev": true, + "dependencies": { + "chalk": "~5.3.0", + "commander": "~12.1.0", + "debug": "~4.3.6", + "execa": "~8.0.1", + "lilconfig": "~3.1.2", + "listr2": "~8.2.4", + "micromatch": "~4.0.8", + "pidtree": "~0.6.0", + "string-argv": "~0.3.2", + "yaml": "~2.5.0" + }, + "bin": { + "lint-staged": "bin/lint-staged.js" + }, + "engines": { + "node": ">=18.12.0" + }, + "funding": { + "url": "https://opencollective.com/lint-staged" + } + }, + "node_modules/listr2": { + "version": "8.2.5", + "resolved": "https://registry.npmjs.org/listr2/-/listr2-8.2.5.tgz", + "integrity": "sha512-iyAZCeyD+c1gPyE9qpFu8af0Y+MRtmKOncdGoA2S5EY8iFq99dmmvkNnHiWo+pj0s7yH7l3KPIgee77tKpXPWQ==", + "dev": true, + "dependencies": { + "cli-truncate": "^4.0.0", + "colorette": "^2.0.20", + "eventemitter3": "^5.0.1", + "log-update": "^6.1.0", + "rfdc": "^1.4.1", + "wrap-ansi": "^9.0.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/listr2/node_modules/ansi-regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/listr2/node_modules/emoji-regex": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.4.0.tgz", + "integrity": "sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw==", + "dev": true + }, + "node_modules/listr2/node_modules/string-width": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", + "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", + "dev": true, + "dependencies": { + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/listr2/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/listr2/node_modules/wrap-ansi": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.0.tgz", + "integrity": "sha512-G8ura3S+3Z2G+mkgNRq8dqaFZAuxfsxpBB8OCTGRTCtp+l/v9nbFNmCUP1BZMts3G1142MsZfn6eeUKrr4PD1Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^6.2.1", + "string-width": "^7.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, "node_modules/locate-path": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", - "integrity": "sha512-NCI2kiDkyR7VeEKm27Kda/iQHyKJe1Bu0FlTbYp3CqJu+9IFe9bLyAjMxf5ZDDbEg+iMPzB5zYyUTSm8wVTKmA==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-7.2.0.tgz", + "integrity": "sha512-gvVijfZvn7R+2qyPX8mAuKcFGDf6Nc61GdvGafQsHL0sBIxfKzA+usWn4GFC/bk+QdwPUD4kWFJLhElipq+0VA==", + "dev": true, "dependencies": { - "p-locate": "^2.0.0", - "path-exists": "^3.0.0" + "p-locate": "^6.0.0" }, "engines": { - "node": ">=4" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/lodash": { @@ -2007,6 +3125,60 @@ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" }, + "node_modules/lodash.camelcase": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", + "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==", + "dev": true + }, + "node_modules/lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", + "dev": true + }, + "node_modules/lodash.kebabcase": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.kebabcase/-/lodash.kebabcase-4.1.1.tgz", + "integrity": "sha512-N8XRTIMMqqDgSy4VLKPnJ/+hpGZN+PHQiJnSenYqPaVV/NCqEogTnAdZLQiGKhxX+JCs8waWq2t1XHWKOmlY8g==", + "dev": true + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true + }, + "node_modules/lodash.mergewith": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.mergewith/-/lodash.mergewith-4.6.2.tgz", + "integrity": "sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ==", + "dev": true + }, + "node_modules/lodash.snakecase": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.snakecase/-/lodash.snakecase-4.1.1.tgz", + "integrity": "sha512-QZ1d4xoBHYUeuouhEq3lk3Uq7ldgyFXGBhg04+oRLnIz8o9T65Eh+8YdroUwn846zchkA9yDsDl5CVVaV2nqYw==", + "dev": true + }, + "node_modules/lodash.startcase": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.startcase/-/lodash.startcase-4.4.0.tgz", + "integrity": "sha512-+WKqsK294HMSc2jEbNgpHpd0JfIBhp7rEV4aqXWqFr6AlXov+SlcgB1Fv01y2kGe3Gc8nMW7VA0SrGuSkRfIEg==", + "dev": true + }, + "node_modules/lodash.uniq": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", + "integrity": "sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==", + "dev": true + }, + "node_modules/lodash.upperfirst": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/lodash.upperfirst/-/lodash.upperfirst-4.3.1.tgz", + "integrity": "sha512-sReKOYJIJf74dhJONhU4e0/shzi1trVbSWDOhKYE5XV2O+H7Sb2Dihwuc7xWxVl+DgFPyTqIN3zMfT9cq5iWDg==", + "dev": true + }, "node_modules/log-symbols": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", @@ -2067,14 +3239,6 @@ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" }, - "node_modules/log-symbols/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "engines": { - "node": ">=8" - } - }, "node_modules/log-symbols/node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -2086,6 +3250,138 @@ "node": ">=8" } }, + "node_modules/log-update": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/log-update/-/log-update-6.1.0.tgz", + "integrity": "sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w==", + "dev": true, + "dependencies": { + "ansi-escapes": "^7.0.0", + "cli-cursor": "^5.0.0", + "slice-ansi": "^7.1.0", + "strip-ansi": "^7.1.0", + "wrap-ansi": "^9.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-update/node_modules/ansi-escapes": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-7.0.0.tgz", + "integrity": "sha512-GdYO7a61mR0fOlAsvC9/rIHf7L96sBc6dEWzeOu+KAea5bZyQRPIpojrVoI4AXGJS/ycu/fBTdLrUkA4ODrvjw==", + "dev": true, + "dependencies": { + "environment": "^1.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-update/node_modules/ansi-regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/log-update/node_modules/emoji-regex": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.4.0.tgz", + "integrity": "sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw==", + "dev": true + }, + "node_modules/log-update/node_modules/is-fullwidth-code-point": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-5.0.0.tgz", + "integrity": "sha512-OVa3u9kkBbw7b8Xw5F9P+D/T9X+Z4+JruYVNapTjPYZYUznQ5YfWeFkOj606XYYW8yugTfC8Pj0hYqvi4ryAhA==", + "dev": true, + "dependencies": { + "get-east-asian-width": "^1.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-update/node_modules/slice-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-7.1.0.tgz", + "integrity": "sha512-bSiSngZ/jWeX93BqeIAbImyTbEihizcwNjFoRUIY/T1wWQsfsm2Vw1agPKylXvQTU7iASGdHhyqRlqQzfz+Htg==", + "dev": true, + "dependencies": { + "ansi-styles": "^6.2.1", + "is-fullwidth-code-point": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, + "node_modules/log-update/node_modules/string-width": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", + "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", + "dev": true, + "dependencies": { + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-update/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/log-update/node_modules/wrap-ansi": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.0.tgz", + "integrity": "sha512-G8ura3S+3Z2G+mkgNRq8dqaFZAuxfsxpBB8OCTGRTCtp+l/v9nbFNmCUP1BZMts3G1142MsZfn6eeUKrr4PD1Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^6.2.1", + "string-width": "^7.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, "node_modules/lru_map": { "version": "0.3.3", "resolved": "https://registry.npmjs.org/lru_map/-/lru_map-0.3.3.tgz", @@ -2109,6 +3405,61 @@ "node": ">= 0.10.0" } }, + "node_modules/meow": { + "version": "12.1.1", + "resolved": "https://registry.npmjs.org/meow/-/meow-12.1.1.tgz", + "integrity": "sha512-BhXM0Au22RwUneMPwSCnyhTOizdWoIEPU9sp0Aqa1PnDMR5Wv2FGXYDjuzJEIX+Eo2Rb8xuYe5jrnm5QowQFkw==", + "dev": true, + "engines": { + "node": ">=16.10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mimic-fn": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", + "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mimic-function": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/mimic-function/-/mimic-function-5.0.1.tgz", + "integrity": "sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/minimalistic-assert": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", @@ -2130,6 +3481,15 @@ "node": "*" } }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/mnemonist": { "version": "0.38.5", "resolved": "https://registry.npmjs.org/mnemonist/-/mnemonist-0.38.5.tgz", @@ -2203,15 +3563,14 @@ "fsevents": "~2.3.2" } }, - "node_modules/mocha/node_modules/escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node_modules/mocha/node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" } }, "node_modules/mocha/node_modules/find-up": { @@ -2248,14 +3607,6 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/mocha/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "engines": { - "node": ">=8" - } - }, "node_modules/mocha/node_modules/locate-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", @@ -2328,18 +3679,32 @@ "node": ">=8.10.0" } }, - "node_modules/mocha/node_modules/supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "node_modules/mocha/node_modules/yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", "dependencies": { - "has-flag": "^4.0.0" + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" }, + "engines": { + "node": ">=10" + } + }, + "node_modules/mocha/node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", "engines": { "node": ">=10" }, "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/ms": { @@ -2370,6 +3735,33 @@ "node": ">=0.10.0" } }, + "node_modules/npm-run-path": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.3.0.tgz", + "integrity": "sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==", + "dev": true, + "dependencies": { + "path-key": "^4.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm-run-path/node_modules/path-key": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", + "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/obliterator": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/obliterator/-/obliterator-2.0.4.tgz", @@ -2383,6 +3775,21 @@ "wrappy": "1" } }, + "node_modules/onetime": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", + "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", + "dev": true, + "dependencies": { + "mimic-fn": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/os-tmpdir": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", @@ -2390,27 +3797,35 @@ "engines": { "node": ">=0.10.0" } - }, - "node_modules/p-limit": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", - "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + }, + "node_modules/p-limit": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-4.0.0.tgz", + "integrity": "sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==", + "dev": true, "dependencies": { - "p-try": "^1.0.0" + "yocto-queue": "^1.0.0" }, "engines": { - "node": ">=4" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/p-locate": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", - "integrity": "sha512-nQja7m7gSKuewoVRen45CtVfODR3crN3goVQ0DDZ9N3yHxgpkuBhZqsaiotSQRrADUrne346peY7kT3TSACykg==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-6.0.0.tgz", + "integrity": "sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw==", + "dev": true, "dependencies": { - "p-limit": "^1.1.0" + "p-limit": "^4.0.0" }, "engines": { - "node": ">=4" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/p-map": { @@ -2435,12 +3850,43 @@ "node": ">=4" } }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-5.0.0.tgz", + "integrity": "sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ==", + "dev": true, "engines": { - "node": ">=4" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" } }, "node_modules/path-is-absolute": { @@ -2451,6 +3897,15 @@ "node": ">=0.10.0" } }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/path-parse": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", @@ -2471,6 +3926,12 @@ "node": ">=0.12" } }, + "node_modules/picocolors": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.0.tgz", + "integrity": "sha512-TQ92mBOW0l3LeMeyLV6mzy/kWr8lkd/hp3mTg7wYK7zJhuBStmGMBG0BdeDZS/dZx1IukaX6Bk11zcln25o1Aw==", + "dev": true + }, "node_modules/picomatch": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", @@ -2482,6 +3943,18 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/pidtree": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.6.0.tgz", + "integrity": "sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g==", + "dev": true, + "bin": { + "pidtree": "bin/pidtree.js" + }, + "engines": { + "node": ">=0.10" + } + }, "node_modules/prettier": { "version": "3.3.3", "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.3.3.tgz", @@ -2513,18 +3986,6 @@ "prettier": ">=2.3.0" } }, - "node_modules/prettier-plugin-solidity/node_modules/semver": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", - "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/randombytes": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", @@ -2561,9 +4022,9 @@ } }, "node_modules/readdirp": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.0.1.tgz", - "integrity": "sha512-GkMg9uOTpIWWKbSsgwb5fA4EavTR+SG/PMPoAY8hkhHfEEY0/vqljY+XHqtDf2cr2IJtoNRDbrrEpZUiZCkYRw==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.0.2.tgz", + "integrity": "sha512-yDMz9g+VaZkqBYS/ozoBJwaBhTbZo3UNYQHNRw1D3UFQB8oHB4uS/tAODO+ZLjGWmUbKnIlOWO+aaIiAxrUWHA==", "engines": { "node": ">= 14.16.0" }, @@ -2580,6 +4041,15 @@ "node": ">=0.10.0" } }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/resolve": { "version": "1.17.0", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz", @@ -2591,6 +4061,52 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/restore-cursor": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-5.1.0.tgz", + "integrity": "sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==", + "dev": true, + "dependencies": { + "onetime": "^7.0.0", + "signal-exit": "^4.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/restore-cursor/node_modules/onetime": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-7.0.0.tgz", + "integrity": "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==", + "dev": true, + "dependencies": { + "mimic-function": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/rfdc": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz", + "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==", + "dev": true + }, "node_modules/ripemd160": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", @@ -2655,11 +4171,15 @@ } }, "node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "dev": true, "bin": { "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" } }, "node_modules/serialize-javascript": { @@ -2692,11 +4212,60 @@ "sha.js": "bin.js" } }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/shell-exec": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/shell-exec/-/shell-exec-1.0.2.tgz", "integrity": "sha512-jyVd+kU2X+mWKMmGhx4fpWbPsjvD53k9ivqetutVW/BQ+WIZoDoP4d8vUMGezV6saZsiNoW2f9GIhg9Dondohg==" }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/slice-ansi": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-5.0.0.tgz", + "integrity": "sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^6.0.0", + "is-fullwidth-code-point": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, "node_modules/solc": { "version": "0.8.26", "resolved": "https://registry.npmjs.org/solc/-/solc-0.8.26.tgz", @@ -2717,6 +4286,14 @@ "node": ">=10.0.0" } }, + "node_modules/solc/node_modules/commander": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", + "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==", + "engines": { + "node": ">= 12" + } + }, "node_modules/solc/node_modules/semver": { "version": "5.7.2", "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", @@ -2742,6 +4319,15 @@ "source-map": "^0.6.0" } }, + "node_modules/split2": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", + "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==", + "dev": true, + "engines": { + "node": ">= 10.x" + } + }, "node_modules/stacktrace-parser": { "version": "0.1.10", "resolved": "https://registry.npmjs.org/stacktrace-parser/-/stacktrace-parser-0.1.10.tgz", @@ -2777,6 +4363,15 @@ "safe-buffer": "~5.2.0" } }, + "node_modules/string-argv": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.2.tgz", + "integrity": "sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==", + "dev": true, + "engines": { + "node": ">=0.6.19" + } + }, "node_modules/string-width": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", @@ -2790,6 +4385,14 @@ "node": ">=8" } }, + "node_modules/string-width/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "engines": { + "node": ">=8" + } + }, "node_modules/strip-ansi": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", @@ -2801,6 +4404,18 @@ "node": ">=8" } }, + "node_modules/strip-final-newline": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", + "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/strip-hex-prefix": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/strip-hex-prefix/-/strip-hex-prefix-1.0.0.tgz", @@ -2825,16 +4440,43 @@ } }, "node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", "dependencies": { - "has-flag": "^3.0.0" + "has-flag": "^4.0.0" }, "engines": { - "node": ">=4" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/text-extensions": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/text-extensions/-/text-extensions-2.4.0.tgz", + "integrity": "sha512-te/NtwBwfiNRLf9Ijqx3T0nlqZiQ2XrrtBvu+cLL8ZRrGkO0NHTug8MYFKyoSrv/sHTaSKfilUkizV6XhxMJ3g==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", + "dev": true + }, + "node_modules/tinyexec": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.0.tgz", + "integrity": "sha512-tVGE0mVJPGb0chKhqmsoosjsS+qUnJVGJpZgsHYQcGoPlG3B51R3PouqTgEGH2Dc9jjFyOqOpix6ZHNMXp1FZg==", + "dev": true + }, "node_modules/tmp": { "version": "0.0.33", "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", @@ -2896,6 +4538,20 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/typescript": { + "version": "5.6.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.3.tgz", + "integrity": "sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==", + "devOptional": true, + "peer": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, "node_modules/undici": { "version": "5.28.4", "resolved": "https://registry.npmjs.org/undici/-/undici-5.28.4.tgz", @@ -2912,6 +4568,18 @@ "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==" }, + "node_modules/unicorn-magic": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.1.0.tgz", + "integrity": "sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/universalify": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", @@ -2941,6 +4609,21 @@ "uuid": "dist/bin/uuid" } }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, "node_modules/widest-line": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-3.1.0.tgz", @@ -3036,21 +4719,34 @@ "node": ">=10" } }, + "node_modules/yaml": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.5.1.tgz", + "integrity": "sha512-bLQOjaX/ADgQ20isPJRvF0iRUHIxVhYvr53Of7wGcWlO2jvtUlH5m87DsmulFVxRpNLOnI4tB6p/oh8D7kpn9Q==", + "dev": true, + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14" + } + }, "node_modules/yargs": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", - "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, "dependencies": { - "cliui": "^7.0.2", + "cliui": "^8.0.1", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", - "string-width": "^4.2.0", + "string-width": "^4.2.3", "y18n": "^5.0.5", - "yargs-parser": "^20.2.2" + "yargs-parser": "^21.1.1" }, "engines": { - "node": ">=10" + "node": ">=12" } }, "node_modules/yargs-parser": { @@ -3075,12 +4771,22 @@ "node": ">=10" } }, + "node_modules/yargs/node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "engines": { + "node": ">=12" + } + }, "node_modules/yocto-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.1.1.tgz", + "integrity": "sha512-b4JR1PFR10y1mKjhHY9LaGo6tmrgjit7hxVIeAmyMw3jegXR4dhYqLaQF5zMXZxY7tLpMyJeLjr1C4rLmkVe8g==", + "dev": true, "engines": { - "node": ">=10" + "node": ">=12.20" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" diff --git a/package.json b/package.json index 7b30557..56c4683 100644 --- a/package.json +++ b/package.json @@ -2,10 +2,13 @@ "devDependencies": { "husky": "^9.1.6", "prettier": "^3.3.0", - "prettier-plugin-solidity": "^1.3.1" + "prettier-plugin-solidity": "^1.3.1", + "lint-staged": "^15.2.10", + "@commitlint/cli": "^19.5.0", + "@commitlint/config-conventional": "^19.5.0" }, "dependencies": { - "hardhat": "^2.22.7", + "hardhat": "^2.22.13", "kill-port": "^2.0.1" }, "packageManager": "npm@10.8.2", @@ -14,5 +17,13 @@ }, "scripts": { "prepare": "husky" + }, + "lint-staged": { + "./**/*.py": [ + "poetry run black" + ], + "./**/*.{ts,md,json}": [ + "prettier --write" + ] } } From a658ba8c7cd3bc25b7338683acd193b3c5926112 Mon Sep 17 00:00:00 2001 From: Eugene M Date: Tue, 15 Oct 2024 21:24:14 +0300 Subject: [PATCH 114/118] fix: rollback diffyscan comment --- diffyscan/diffyscan.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/diffyscan/diffyscan.py b/diffyscan/diffyscan.py index 8baece9..67df3b6 100755 --- a/diffyscan/diffyscan.py +++ b/diffyscan/diffyscan.py @@ -5,10 +5,7 @@ import os import traceback -"s" - from .utils.common import load_config, load_env, prettify_solidity - from .utils.constants import ( DIFFS_DIR, DEFAULT_CONFIG_PATH, From 1fe1ee9b1a9c5e63e040f37f98ccbc2945fa0ef5 Mon Sep 17 00:00:00 2001 From: Eugene M Date: Tue, 15 Oct 2024 21:28:19 +0300 Subject: [PATCH 115/118] fix: diffyscan.py 0755->0644 permissions --- diffyscan/diffyscan.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100755 => 100644 diffyscan/diffyscan.py diff --git a/diffyscan/diffyscan.py b/diffyscan/diffyscan.py old mode 100755 new mode 100644 From 94e22c2e359e719515c353b72edf3f466c7713a0 Mon Sep 17 00:00:00 2001 From: Eugene M Date: Tue, 15 Oct 2024 21:36:17 +0300 Subject: [PATCH 116/118] fix: add a note about hh config --- README.md | 97 +++++++++++++++++++++++++++++-------------------------- 1 file changed, 52 insertions(+), 45 deletions(-) diff --git a/README.md b/README.md index 69d18c4..6bc7d6b 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,7 @@ Diff deployed EVM-compatible smart contract sourcecode and bytecode against the specified GitHub repo commit. Key features: + - retrieve and diff sources from the GitHub repo against the queried ones from a blockscan service (e.g. Etherscan) - compare the bytecode compiled and deployed on the forked network locally against remote (see section 'bytecode_comparison' in `./config_samples/lido_dao_sepolia_config.json` as an example) - preprocess solidity sourcecode by means of prettifier solidity plugin before comparing the sources (option `--prettify`) if needed. @@ -51,67 +52,69 @@ export REMOTE_RPC_URL = Set local RPC URL to check immutables against the local deployment and provided constructor arguments, -```bash +````bash export LOCAL_RPC_URL = (example `http://127.0.0.1:7545`) Start script with one of the examples provided (or entire folder of configs) ```bash diffyscan config_samples/lido_dao_sepolia_config.json -``` +```` Alternatively, create a new config file named `config.json` near the diffyscan.py, ```json { - "contracts": { - "0x28FAB2059C713A7F9D8c86Db49f9bb0e96Af1ef8": "OssifiableProxy", - "0xDba5Ad530425bb1b14EECD76F1b4a517780de537": "LidoLocator" - }, - "explorer_hostname": "api-holesky.etherscan.io", - "explorer_token_env_var": "ETHERSCAN_EXPLORER_TOKEN", - "github_repo": { - "url": "https://github.com/lidofinance/lido-dao", - "commit": "cadffa46a2b8ed6cfa1127fca2468bae1a82d6bf", - "relative_root": "" - }, - "dependencies": { - "@openzeppelin/contracts-v4.4": { - "url": "https://github.com/OpenZeppelin/openzeppelin-contracts", - "commit": "6bd6b76d1156e20e45d1016f355d154141c7e5b9", - "relative_root": "contracts" - } + "contracts": { + "0x28FAB2059C713A7F9D8c86Db49f9bb0e96Af1ef8": "OssifiableProxy", + "0xDba5Ad530425bb1b14EECD76F1b4a517780de537": "LidoLocator" + }, + "explorer_hostname": "api-holesky.etherscan.io", + "explorer_token_env_var": "ETHERSCAN_EXPLORER_TOKEN", + "github_repo": { + "url": "https://github.com/lidofinance/lido-dao", + "commit": "cadffa46a2b8ed6cfa1127fca2468bae1a82d6bf", + "relative_root": "" + }, + "dependencies": { + "@openzeppelin/contracts-v4.4": { + "url": "https://github.com/OpenZeppelin/openzeppelin-contracts", + "commit": "6bd6b76d1156e20e45d1016f355d154141c7e5b9", + "relative_root": "contracts" + } + }, + "fail_on_comparison_error": true, + "bytecode_comparison": { + "hardhat_config_name": "holesky_hardhat.config.js", + "constructor_calldata": { + "0x28FAB2059C713A7F9D8c86Db49f9bb0e96Af1ef8": "000000000000000000000000ab89ed3d8f31bcf8bb7de53f02084d1e6f043d34000000000000000000000000e92329ec7ddb11d25e25b3c21eebf11f15eb325d00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" }, - "fail_on_comparison_error": true, - "bytecode_comparison": { - "hardhat_config_name": "holesky_hardhat.config.js", - "constructor_calldata": { - "0x28FAB2059C713A7F9D8c86Db49f9bb0e96Af1ef8": "000000000000000000000000ab89ed3d8f31bcf8bb7de53f02084d1e6f043d34000000000000000000000000e92329ec7ddb11d25e25b3c21eebf11f15eb325d00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000" - }, - "constructor_args": { - "0xDba5Ad530425bb1b14EECD76F1b4a517780de537": [ - [ - "0x4E97A3972ce8511D87F334dA17a2C332542a5246", - "0x045dd46212A178428c088573A7d102B9d89a022A", - "0xE73a3602b99f1f913e72F8bdcBC235e206794Ac8", - "0x072f72BE3AcFE2c52715829F2CD9061A6C8fF019", - "0x3F1c547b21f65e10480dE3ad8E19fAAC46C95034", - "0xF0d576c7d934bBeCc68FE15F1c5DAF98ea2B78bb", - "0x072f72BE3AcFE2c52715829F2CD9061A6C8fF019", - "0x4E46BD7147ccf666E1d73A3A456fC7a68de82eCA", - "0xd6EbF043D30A7fe46D1Db32BA90a0A51207FE229", - "0xE92329EC7ddB11D25e25b3c21eeBf11f15eB325d", - "0xffDDF7025410412deaa05E3E1cE68FE53208afcb", - "0xc7cc160b58F8Bb0baC94b80847E2CF2800565C50", - "0xF0179dEC45a37423EAD4FaD5fCb136197872EAd9", - "0xC01fC1F2787687Bc656EAc0356ba9Db6e6b7afb7" - ] - ] - } + "constructor_args": { + "0xDba5Ad530425bb1b14EECD76F1b4a517780de537": [ + [ + "0x4E97A3972ce8511D87F334dA17a2C332542a5246", + "0x045dd46212A178428c088573A7d102B9d89a022A", + "0xE73a3602b99f1f913e72F8bdcBC235e206794Ac8", + "0x072f72BE3AcFE2c52715829F2CD9061A6C8fF019", + "0x3F1c547b21f65e10480dE3ad8E19fAAC46C95034", + "0xF0d576c7d934bBeCc68FE15F1c5DAF98ea2B78bb", + "0x072f72BE3AcFE2c52715829F2CD9061A6C8fF019", + "0x4E46BD7147ccf666E1d73A3A456fC7a68de82eCA", + "0xd6EbF043D30A7fe46D1Db32BA90a0A51207FE229", + "0xE92329EC7ddB11D25e25b3c21eeBf11f15eB325d", + "0xffDDF7025410412deaa05E3E1cE68FE53208afcb", + "0xc7cc160b58F8Bb0baC94b80847E2CF2800565C50", + "0xF0179dEC45a37423EAD4FaD5fCb136197872EAd9", + "0xC01fC1F2787687Bc656EAc0356ba9Db6e6b7afb7" + ] + ] } + } } ``` + then create a new Hardhat config file named `hardhat_config.js` near the diffyscan.py + ```json module.exports = { solidity: "0.8.9", @@ -125,6 +128,10 @@ module.exports = { }; ``` +> Note: Hardhat config file is needed to avoid standard config generation routine to be launched. +> +> See also: https://hardhat.org/hardhat-runner/docs/config#configuration + Start the script ```bash From 15b9973f2bb21ba05098cb33d27597b25712eece Mon Sep 17 00:00:00 2001 From: Eugene M Date: Tue, 15 Oct 2024 21:37:33 +0300 Subject: [PATCH 117/118] fix: nested import --- diffyscan/utils/hardhat.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/diffyscan/utils/hardhat.py b/diffyscan/utils/hardhat.py index a5404df..0ec742f 100644 --- a/diffyscan/utils/hardhat.py +++ b/diffyscan/utils/hardhat.py @@ -2,6 +2,7 @@ import subprocess import signal import time +import socket from urllib.parse import urlparse from .common import mask_text @@ -73,8 +74,6 @@ def stop(self): logger.info(f"Hardhat stopped, PID {self.sub_process.pid}") def _is_port_in_use_(self, parsed_url) -> bool: - import socket - with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: return s.connect_ex((parsed_url.hostname, parsed_url.port)) == 0 From 3a2b94902b94f43ef7f8ad6da4eab4021d7740b0 Mon Sep 17 00:00:00 2001 From: Eugene M Date: Tue, 15 Oct 2024 21:39:13 +0300 Subject: [PATCH 118/118] fix: simplify get_key_from_value --- diffyscan/utils/constants.py | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/diffyscan/utils/constants.py b/diffyscan/utils/constants.py index 5c640c6..451570b 100644 --- a/diffyscan/utils/constants.py +++ b/diffyscan/utils/constants.py @@ -49,13 +49,8 @@ # fmt: on -def get_key_from_value(dictinary: dict, value: str): - keys = [ - dict_key for dict_key, dict_value in dictinary.items() if dict_value == value - ] - if keys: - return keys[0] - return None +def get_key_from_value(dictionary: dict, value: str): + return next((k for k, v in dictionary.items() if v == value), None) PUSH0 = get_key_from_value(OPCODES, "PUSH0")