From 3b875e7bac33ff593c5de7cef4ad6fdb285fec09 Mon Sep 17 00:00:00 2001 From: Eddort Date: Fri, 24 Apr 2026 15:12:46 +0200 Subject: [PATCH 01/12] feat: Add deployment_from configuration and srv3 config for hoodi --- README.md | 5 +- .../hoodi/srv3/hoodi_srv3_config.json | 219 ++++++++++++++++++ diffyscan/diffyscan.py | 53 ++++- diffyscan/utils/common.py | 14 +- diffyscan/utils/custom_types.py | 1 + tests/fixtures/full_config.json | 3 + tests/fixtures/full_config.yaml | 2 + tests/test_bytecode_metadata.py | 21 ++ tests/test_config_loading.py | 26 +++ tests/test_deployment_from_config.py | 41 ++++ 10 files changed, 382 insertions(+), 3 deletions(-) create mode 100644 config_samples/ethereum/hoodi/srv3/hoodi_srv3_config.json create mode 100644 tests/test_deployment_from_config.py diff --git a/README.md b/README.md index a2b67d9d..7f18d187 100644 --- a/README.md +++ b/README.md @@ -88,7 +88,7 @@ diffyscan config_samples/lido_dao_sepolia_config.json When no path is given, diffyscan looks for `config.json`, `config.yaml`, or `config.yml` in the current directory. When a directory is given, all `.json`, `.yaml`, and `.yml` files inside it are processed. -Alternatively, create a new config file near `diffyscan.py`. Configs can be written in JSON or YAML. The `bytecode_comparison` section is optional and only needed for manual overrides when explorer metadata is missing or you want to override it: +Alternatively, create a new config file near `diffyscan.py`. Configs can be written in JSON or YAML. The `bytecode_comparison` section is optional and only needed for manual overrides when explorer metadata is missing or you want to override it. `deployment_from` can be used per contract when constructor simulation depends on `msg.sender`: **JSON** (`config.json`): @@ -137,6 +137,9 @@ Alternatively, create a new config file near `diffyscan.py`. Configs can be writ "0xC01fC1F2787687Bc656EAc0356ba9Db6e6b7afb7" ] ] + }, + "deployment_from": { + "0x045dd46212A178428c088573A7d102B9d89a022A": "0xE92329EC7ddB11D25e25b3c21eeBf11f15eB325d" } } } diff --git a/config_samples/ethereum/hoodi/srv3/hoodi_srv3_config.json b/config_samples/ethereum/hoodi/srv3/hoodi_srv3_config.json new file mode 100644 index 00000000..c4674e5f --- /dev/null +++ b/config_samples/ethereum/hoodi/srv3/hoodi_srv3_config.json @@ -0,0 +1,219 @@ +{ + "contracts": { + "0xDB47544d5813f15116bf95c1cF2ff4dEdb2226fD": "Accounting", + "0x41bF10F28A1312f2241f86A2537A04b08e343C0a": "AccountingOracle", + "0x6147270470A9Ee5b55c33EA71e32000E5d6D8E6B": "Lido", + "0x27Ff16a465c1A00a727dd3Dbd479c5F3De275a1f": "ConsolidationBus", + "0xC9991Bb865d025364BCbBd99f36e85889Fb68e69": "ConsolidationGateway", + "0x2A8585201BFD6830944f0bf008B774e7e32b380d": "ConsolidationMigrator", + "0x1a629bB7C0563650e46406Eb6764A2ba207a0eFE": "DepositSecurityModule", + "0x2C33BE7c09bfBC8e41E7648d611d857fD4831b68": "LidoLocator", + "0x8E6FDB231D7CE30C2459319c0d4c4Eb4B681f9C9": "MinFirstAllocationStrategy", + "0x049A972e9cBEfFFc1c2543dFD0Fa892C2E9Ed6C5": "OracleReportSanityChecker", + "0x44d0b2B95d2C2bDF73FE4f5cD7E3A930494E5B1f": "StakingRouter", + "0xFd1b63657dda65C4E6fDEF9d1f37064D078e9B49": "TopUpGateway", + "0x86aeA211B30174b3ee5d294ECeaDbD7f1C575eF3": "ValidatorsExitBusOracle", + "0xB97e67CC20bd2970E30341c0ECc7497d8A5b7342": "WithdrawalVault" + }, + "network": "hoodi", + "explorer_hostname": "api.etherscan.io", + "explorer_token_env_var": "ETHERSCAN_EXPLORER_TOKEN", + "explorer_chain_id": 560048, + "github_repo": { + "url": "https://github.com/lidofinance/core", + "commit": "13cc8fa31cedc797575b107d576c7a63b96192e4", + "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" + }, + "@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/contracts-v5.2": { + "url": "https://github.com/OpenZeppelin/openzeppelin-contracts", + "commit": "acd4ff74de833399287ed6b31b4debf6b2b35527", + "relative_root": "contracts" + } + }, + "fail_on_bytecode_comparison_error": true, + "bytecode_comparison": { + "constructor_calldata": {}, + "deployment_from": { + "0x1a629bB7C0563650e46406Eb6764A2ba207a0eFE": "0x83BCE68B4e8b7071b2a664a26e6D3Bc17eEe3102" + }, + "constructor_args": { + "0xDB47544d5813f15116bf95c1cF2ff4dEdb2226fD": [ + "0xe2EF9536DAAAEBFf5b1c130957AB3E80056b06D8", + "0x3508A952176b3c15387C97BE809eaffB1982176a" + ], + "0x41bF10F28A1312f2241f86A2537A04b08e343C0a": [ + "0xe2EF9536DAAAEBFf5b1c130957AB3E80056b06D8", + 12, + 1742213400 + ], + "0x6147270470A9Ee5b55c33EA71e32000E5d6D8E6B": [], + "0x27Ff16a465c1A00a727dd3Dbd479c5F3De275a1f": [ + "0xC9991Bb865d025364BCbBd99f36e85889Fb68e69" + ], + "0xC9991Bb865d025364BCbBd99f36e85889Fb68e69": [ + "0xC676167aAea6de6Af3e1ED34C0595de449E0de7b", + "0xe2EF9536DAAAEBFf5b1c130957AB3E80056b06D8", + 2900, + 1, + 30, + "0x0000000000000000000000000000000000000000000000000096000000000028", + "0x0000000000000000000000000000000000000000000000000096000000000028", + 0 + ], + "0x2A8585201BFD6830944f0bf008B774e7e32b380d": [ + "0xCc820558B39ee15C7C45B59390B503b83fb499A8", + "0xe09fBcE63826468867eE66Eda491E444829E022A", + 1, + 5 + ], + "0x1a629bB7C0563650e46406Eb6764A2ba207a0eFE": [ + "0x3508A952176b3c15387C97BE809eaffB1982176a", + "0x00000000219ab540356cBB839Cbe05303d7705Fa", + "0xCc820558B39ee15C7C45B59390B503b83fb499A8", + 6646, + 200 + ], + "0x2C33BE7c09bfBC8e41E7648d611d857fD4831b68": [ + [ + "0xcb883B1bD0a41512b42D2dB267F2A2cd919FB216", + "0x1a629bB7C0563650e46406Eb6764A2ba207a0eFE", + "0x9b108015fe433F173696Af3Aa0CF7CDb3E104258", + "0x3508A952176b3c15387C97BE809eaffB1982176a", + "0x049A972e9cBEfFFc1c2543dFD0Fa892C2E9Ed6C5", + "0x9c53d0075eA00ad77dDAd1b71E67bb97AaBC1e3D", + "0xb2c99cd38a2636a6281a849C8de938B3eF4A7C3D", + "0xCc820558B39ee15C7C45B59390B503b83fb499A8", + "0x0534aA41907c9631fae990960bCC72d75fA7cfeD", + "0x8664d394C2B3278F26A1B44B967aEf99707eeAB2", + "0xfe56573178f1bcdf53F01A6E9977670dcBBD9186", + "0x4473dCDDbf77679A643BdB654dbd86D67F8d32f2", + "0x2a833402e3F46fFC1ecAb3598c599147a78731a9", + "0xa5F5A9360275390fF9728262a29384399f38d2f0", + "0x6679090D92b08a2a686eF8614feECD8cDFE209db", + "0xC9991Bb865d025364BCbBd99f36e85889Fb68e69", + "0x9b5b78D1C9A3238bF24662067e34c57c83E8c354", + "0xa5F55f3402beA2B14AE15Dae1b6811457D43581d", + "0x7E99eE3C66636DE415D2d7C880938F2f40f94De4", + "0x4C9fFC325392090F789255b9948Ab1659b797964", + "0x7Ba269a03eeD86f2f54CB04CA3b4b7626636Df4E", + "0xf41491C79C30e8f4862d3F4A5b790171adB8e04A", + "0x501e678182bB5dF3f733281521D3f3D1aDe69917", + "0x10DBEb3367876826d00D21718D1d893e0fbD2956" + ] + ], + "0x8E6FDB231D7CE30C2459319c0d4c4Eb4B681f9C9": [], + "0x049A972e9cBEfFFc1c2543dFD0Fa892C2E9Ed6C5": [ + "0xe2EF9536DAAAEBFf5b1c130957AB3E80056b06D8", + "0x9b5b78D1C9A3238bF24662067e34c57c83E8c354", + "0x0534aA41907c9631fae990960bCC72d75fA7cfeD", + [ + 57600, + 57600, + "0x3e8", + "0x32", + 19200, + 32, + 2048, + "0x8", + "0x18", + "0x80", + "0xb71b0", + 360, + "0x32", + 93375, + 32, + 300 + ] + ], + "0x44d0b2B95d2C2bDF73FE4f5cD7E3A930494E5B1f": [ + "0x00000000219ab540356cBB839Cbe05303d7705Fa", + "0x3508A952176b3c15387C97BE809eaffB1982176a", + "0xe2EF9536DAAAEBFf5b1c130957AB3E80056b06D8", + "0x1bc16d674ec800000", + "0x6f05b59d3b20000000" + ], + "0xFd1b63657dda65C4E6fDEF9d1f37064D078e9B49": [ + "0xe2EF9536DAAAEBFf5b1c130957AB3E80056b06D8", + "0x0000000000000000000000000000000000000000000000000096000000000028", + "0x0000000000000000000000000000000000000000000000000096000000000028", + 0, + 32 + ], + "0x86aeA211B30174b3ee5d294ECeaDbD7f1C575eF3": [ + 12, + 1742213400, + "0xe2EF9536DAAAEBFf5b1c130957AB3E80056b06D8" + ], + "0xB97e67CC20bd2970E30341c0ECc7497d8A5b7342": [ + "0x3508A952176b3c15387C97BE809eaffB1982176a", + "0x0534aA41907c9631fae990960bCC72d75fA7cfeD", + "0x6679090D92b08a2a686eF8614feECD8cDFE209db", + "0xce93710b849e0dC202AaC513837e05bEA9D7DdFD", + "0x00000961Ef480Eb55e80D19ad83579A64c007002", + "0x0000BBdDc7CE488642fb579F8B00f3a590007251" + ] + }, + "libraries": { + "contracts/common/lib/MinFirstAllocationStrategy.sol": { + "MinFirstAllocationStrategy": "0x8E6FDB231D7CE30C2459319c0d4c4Eb4B681f9C9" + }, + "contracts/0.8.25/lib/BeaconChainDepositor.sol": { + "BeaconChainDepositor": "0x29e702f163d4Ca47a0813e09BDfdAb960fb1B90b" + }, + "contracts/0.8.25/sr/SRLib.sol": { + "SRLib": "0xA43DeC6250D4B59C345c8515569983E3e24d6990" + } + } + } +} diff --git a/diffyscan/diffyscan.py b/diffyscan/diffyscan.py index 6d41b144..9da89174 100644 --- a/diffyscan/diffyscan.py +++ b/diffyscan/diffyscan.py @@ -40,6 +40,7 @@ ExceptionHandler, BaseCustomException, CompileError, + CalldataError, ) @@ -172,7 +173,18 @@ def run_bytecode_diff( ) deployment_call_data = _append_calldata(contract_creation_code, calldata) - local_deployed_bytecode = simulate_deployment(deployment_call_data, remote_rpc_url) + deployment_from = _get_deployment_from( + config.get("bytecode_comparison"), contract_address_from_config + ) + if deployment_from: + logger.info("Using deployment simulation from config", deployment_from) + local_deployed_bytecode = simulate_deployment( + deployment_call_data, remote_rpc_url, caller=deployment_from + ) + else: + local_deployed_bytecode = simulate_deployment( + deployment_call_data, remote_rpc_url + ) is_fully_matched = local_deployed_bytecode == remote_deployed_bytecode @@ -366,6 +378,45 @@ def _append_calldata(creation_code: str, calldata: str | None) -> str: return creation_code + calldata +def _validate_config_address(value: object, field_name: str) -> str: + if not isinstance(value, str): + raise CalldataError(f"{field_name} must be a hex string address") + + address = value.strip() + if not address.startswith("0x") or len(address) != 42: + raise CalldataError(f"{field_name} must be a 20-byte hex address") + + try: + int(address[2:], 16) + except ValueError as exc: + raise CalldataError(f"{field_name} is not valid hex") from exc + + return address + + +def _get_deployment_from( + binary_config: dict | None, contract_address: str +) -> str | None: + if not isinstance(binary_config, dict): + return None + + deployment_from = binary_config.get("deployment_from") + if deployment_from is None: + return None + if not isinstance(deployment_from, dict): + raise CalldataError( + 'Config key "bytecode_comparison.deployment_from" must be an object' + ) + + if contract_address not in deployment_from: + return None + + return _validate_config_address( + deployment_from[contract_address], + f"bytecode_comparison.deployment_from.{contract_address}", + ) + + def _log_explorer_bytecode_metadata( constructor_arguments: str | None, evm_version: str | None, diff --git a/diffyscan/utils/common.py b/diffyscan/utils/common.py index b26ff133..3420bec2 100644 --- a/diffyscan/utils/common.py +++ b/diffyscan/utils/common.py @@ -102,13 +102,25 @@ def _validate_yaml_hex_keys(config: dict, path: str) -> None: if not isinstance(bytecode, dict): return - for section in ("constructor_args", "constructor_calldata"): + for section in ("constructor_args", "constructor_calldata", "deployment_from"): _validate_yaml_address_keys( bytecode.get(section), path, f"bytecode_comparison.{section}", ) + deployment_from = bytecode.get("deployment_from") + if isinstance(deployment_from, dict): + for contract_addr, caller in deployment_from.items(): + _raise_if_yaml_int( + caller, + lambda parsed_addr: ( + f"{path}: bytecode_comparison.deployment_from.{contract_addr} " + f"was parsed as integer ({parsed_addr:#x}). " + f"Quote it: {_quote_hex(parsed_addr)}" + ), + ) + libraries = bytecode.get("libraries") if isinstance(libraries, dict): for key, libs in libraries.items(): diff --git a/diffyscan/utils/custom_types.py b/diffyscan/utils/custom_types.py index de98d6eb..90268b7e 100644 --- a/diffyscan/utils/custom_types.py +++ b/diffyscan/utils/custom_types.py @@ -5,6 +5,7 @@ class BinaryConfig(TypedDict): hardhat_config_name: NotRequired[str] constructor_calldata: NotRequired[dict[str, str]] constructor_args: NotRequired[dict[str, list]] + deployment_from: NotRequired[dict[str, str]] libraries: NotRequired[dict[str, dict[str, str]]] diff --git a/tests/fixtures/full_config.json b/tests/fixtures/full_config.json index 1c40a080..1fd28ceb 100644 --- a/tests/fixtures/full_config.json +++ b/tests/fixtures/full_config.json @@ -46,6 +46,9 @@ "00000000000000000000000000000000219ab540356cbb839cbe05303d7705fa" ] }, + "deployment_from": { + "0xe561152e8d3f618b386ef4dd6e3fb980eb2f9e61": "0x13Ac97663d19fF20fe467BFa580748505e664beB" + }, "libraries": { "contracts/common/lib/MinFirstAllocationStrategy.sol": { "MinFirstAllocationStrategy": "0x7e70De6D1877B3711b2bEDa7BA00013C7142d993" diff --git a/tests/fixtures/full_config.yaml b/tests/fixtures/full_config.yaml index 9b39892f..ed652ba3 100644 --- a/tests/fixtures/full_config.yaml +++ b/tests/fixtures/full_config.yaml @@ -45,6 +45,8 @@ bytecode_comparison: - "0x13Ac97663d19fF20fe467BFa580748505e664beB" "0x442af784A788A5bd6F42A01Ebe9F287a871243fb": - "00000000000000000000000000000000219ab540356cbb839cbe05303d7705fa" + deployment_from: + "0xe561152e8d3f618b386ef4dd6e3fb980eb2f9e61": "0x13Ac97663d19fF20fe467BFa580748505e664beB" libraries: contracts/common/lib/MinFirstAllocationStrategy.sol: MinFirstAllocationStrategy: "0x7e70De6D1877B3711b2bEDa7BA00013C7142d993" diff --git a/tests/test_bytecode_metadata.py b/tests/test_bytecode_metadata.py index 2a06de0c..145709f4 100644 --- a/tests/test_bytecode_metadata.py +++ b/tests/test_bytecode_metadata.py @@ -105,6 +105,27 @@ def fake_pull(rpc_url, payload, headers): assert captured["payload"]["params"][0]["data"] == "0x6001600055" +def test_simulate_deployment_uses_custom_caller(monkeypatch): + captured = {} + + def fake_pull(rpc_url, payload, headers): + captured["payload"] = json.loads(payload) + return DummyResponse({"result": "0x60016000"}) + + monkeypatch.setattr("diffyscan.utils.node_handler.pull", fake_pull) + + simulate_deployment( + "0x6001600055", + "https://rpc.example", + caller="0x0000000000000000000000000000000000000002", + ) + + assert ( + captured["payload"]["params"][0]["from"] + == "0x0000000000000000000000000000000000000002" + ) + + def test_simulate_deployment_surfaces_rpc_errors(monkeypatch): def fake_pull(rpc_url, payload, headers): return DummyResponse( diff --git a/tests/test_config_loading.py b/tests/test_config_loading.py index 9ce4a065..15af4318 100644 --- a/tests/test_config_loading.py +++ b/tests/test_config_loading.py @@ -234,6 +234,27 @@ def test_bytecode_comparison_library_unquoted_hex_raises(tmp_path): load_config(str(path)) +def test_bytecode_comparison_deployment_from_unquoted_hex_raises(tmp_path): + """Unquoted hex in bytecode_comparison.deployment_from values should be caught.""" + path = tmp_path / "config.yaml" + path.write_text("""\ +contracts: + "0x0000000000000000000000000000000000000001": TestContract +explorer_hostname: api.etherscan.io +explorer_token_env_var: ETHERSCAN_EXPLORER_TOKEN +github_repo: + url: https://github.com/example/repo + commit: abc123 + relative_root: "" +dependencies: {} +bytecode_comparison: + deployment_from: + "0x0000000000000000000000000000000000000001": 0x00000000000000000000000000000000000000AB +""") + with pytest.raises(ValueError, match="bytecode_comparison.deployment_from"): + load_config(str(path)) + + # --- Full-fixture tests (max properties) --- @@ -280,6 +301,11 @@ def test_full_fixture_nested_types(): for arg in args: assert isinstance(arg, str), f"constructor arg {arg!r} should be str" + # bytecode_comparison.deployment_from maps contract addresses to caller addresses + for addr, caller in result["bytecode_comparison"]["deployment_from"].items(): + assert isinstance(addr, str) and addr.startswith("0x") + assert isinstance(caller, str) and caller.startswith("0x") + # bytecode_comparison.libraries nested dict of str -> str for path, libs in result["bytecode_comparison"]["libraries"].items(): assert isinstance(libs, dict) diff --git a/tests/test_deployment_from_config.py b/tests/test_deployment_from_config.py new file mode 100644 index 00000000..00644528 --- /dev/null +++ b/tests/test_deployment_from_config.py @@ -0,0 +1,41 @@ +import pytest + +from diffyscan.diffyscan import _get_deployment_from +from diffyscan.utils.custom_exceptions import CalldataError + + +def test_get_deployment_from_returns_contract_specific_caller(): + binary_config = { + "deployment_from": { + "0x0000000000000000000000000000000000000001": ( + "0x0000000000000000000000000000000000000002" + ) + } + } + + assert ( + _get_deployment_from( + binary_config, "0x0000000000000000000000000000000000000001" + ) + == "0x0000000000000000000000000000000000000002" + ) + + +def test_get_deployment_from_returns_none_when_missing(): + assert ( + _get_deployment_from( + {"deployment_from": {}}, "0x0000000000000000000000000000000000000001" + ) + is None + ) + + +def test_get_deployment_from_rejects_invalid_caller(): + binary_config = { + "deployment_from": {"0x0000000000000000000000000000000000000001": "0x1234"} + } + + with pytest.raises(CalldataError, match="20-byte hex address"): + _get_deployment_from( + binary_config, "0x0000000000000000000000000000000000000001" + ) From 860e20f82ae6c80686b01c91326f39a3d88c4e38 Mon Sep 17 00:00:00 2001 From: Eddort Date: Fri, 24 Apr 2026 15:36:19 +0200 Subject: [PATCH 02/12] feat: Add hoodi configuration for srv3 in regression workflow --- .github/workflows/regression.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/regression.yml b/.github/workflows/regression.yml index 61e8eb23..f9c9038a 100644 --- a/.github/workflows/regression.yml +++ b/.github/workflows/regression.yml @@ -96,6 +96,10 @@ jobs: --allow-source-diff 0x83DfE5Fe8ac8b7DB38c020F4F54BF09b65D92c63 --allow-source-diff 0xa11906bBBBaC5207b8FDA4F7F294d7EcB8dcc758 --allow-source-diff 0xc5dCd2A9642ceA9B71A632BF5b8ff52424Ea1B40 + - config: config_samples/ethereum/hoodi/srv3/hoodi_srv3_config.json + network: hoodi + flags: > + --allow-bytecode-diff 0x1a629bB7C0563650e46406Eb6764A2ba207a0eFE steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 From 721fa32d93839bba53785f9765255ad629b393a8 Mon Sep 17 00:00:00 2001 From: Eddort Date: Wed, 13 May 2026 23:22:06 +0200 Subject: [PATCH 03/12] feat: Add hoodi configuration for srv3 easy tracks --- .github/workflows/regression.yml | 12 +++ .../srv3/hoodi_srv3_easy_track_config.json | 75 +++++++++++++++++++ 2 files changed, 87 insertions(+) create mode 100644 config_samples/ethereum/hoodi/srv3/hoodi_srv3_easy_track_config.json diff --git a/.github/workflows/regression.yml b/.github/workflows/regression.yml index f9c9038a..0d09e969 100644 --- a/.github/workflows/regression.yml +++ b/.github/workflows/regression.yml @@ -100,6 +100,18 @@ jobs: network: hoodi flags: > --allow-bytecode-diff 0x1a629bB7C0563650e46406Eb6764A2ba207a0eFE + - config: config_samples/ethereum/hoodi/srv3/hoodi_srv3_easy_track_config.json + network: hoodi + flags: > + --allow-source-diff 0x22D36e7616F541A527989C5652fDA4d527bB461C + --allow-source-diff 0xD63cf25df1bA6144db27A81A98120Dfc53dE4540 + --allow-source-diff 0xf71fcB20B9FB8468653Bcb24E31F39bc069D5995 + --allow-source-diff 0x4EaB04775837A6F0218750A10454119f349258FE + --allow-source-diff 0xd0c38B2F0C1F760976dA010C1c35D828331Ff9E2 + --allow-source-diff 0x5194cC02B6F477B4a23DFA422fFC238c8B5b1736 + --allow-source-diff 0x6E40FED7c28bAA93a798cA10f8A93965a19eC52e + --allow-source-diff 0x3486B872768D361309e405A046C4BF995c21CC6c + --allow-source-diff 0x44D9b39bBdc2182Aa1af6f16f8F55E0eA038294d steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 diff --git a/config_samples/ethereum/hoodi/srv3/hoodi_srv3_easy_track_config.json b/config_samples/ethereum/hoodi/srv3/hoodi_srv3_easy_track_config.json new file mode 100644 index 00000000..3d8d23b4 --- /dev/null +++ b/config_samples/ethereum/hoodi/srv3/hoodi_srv3_easy_track_config.json @@ -0,0 +1,75 @@ +{ + "contracts": { + "0x22D36e7616F541A527989C5652fDA4d527bB461C": "AllowConsolidationPair", + "0xD63cf25df1bA6144db27A81A98120Dfc53dE4540": "UpdateStakingModuleShareLimits", + "0xf71fcB20B9FB8468653Bcb24E31F39bc069D5995": "SetMerkleGateTree", + "0x4EaB04775837A6F0218750A10454119f349258FE": "ReportWithdrawalsForSlashedValidators", + "0xd0c38B2F0C1F760976dA010C1c35D828331Ff9E2": "SettleGeneralDelayedPenalty", + "0x5194cC02B6F477B4a23DFA422fFC238c8B5b1736": "SetMerkleGateTree", + "0x6E40FED7c28bAA93a798cA10f8A93965a19eC52e": "ReportWithdrawalsForSlashedValidators", + "0x3486B872768D361309e405A046C4BF995c21CC6c": "SettleGeneralDelayedPenalty", + "0x44D9b39bBdc2182Aa1af6f16f8F55E0eA038294d": "CreateOrUpdateOperatorGroup" + }, + "explorer_hostname": "api.etherscan.io", + "explorer_token_env_var": "ETHERSCAN_EXPLORER_TOKEN", + "explorer_chain_id": 560048, + "github_repo": { + "url": "https://github.com/lidofinance/easy-track", + "commit": "e06629e4e4ba62751968dfbcb8586faab83b5eb9", + "relative_root": "" + }, + "dependencies": {}, + "fail_on_bytecode_comparison_error": true, + "bytecode_comparison": { + "constructor_calldata": {}, + "constructor_args": { + "0x22D36e7616F541A527989C5652fDA4d527bB461C": [ + "0x747d357F5b6410B80Eb63406FaC5E2A91131B4f8" + ], + "0xD63cf25df1bA6144db27A81A98120Dfc53dE4540": [ + "0x4AF43Ee34a6fcD1fEcA1e1F832124C763561dA53", + "CSM", + "0xCc820558B39ee15C7C45B59390B503b83fb499A8", + 4, + 500, + 500, + 600, + 600 + ], + "0xf71fcB20B9FB8468653Bcb24E31F39bc069D5995": [ + "0x4AF43Ee34a6fcD1fEcA1e1F832124C763561dA53", + "CSM" + ], + "0x4EaB04775837A6F0218750A10454119f349258FE": [ + "0x4AF43Ee34a6fcD1fEcA1e1F832124C763561dA53", + "CSM", + "0x79CEf36D84743222f37765204Bec41E92a93E59d" + ], + "0xd0c38B2F0C1F760976dA010C1c35D828331Ff9E2": [ + "0x4AF43Ee34a6fcD1fEcA1e1F832124C763561dA53", + "CSM", + "0x79CEf36D84743222f37765204Bec41E92a93E59d" + ], + "0x5194cC02B6F477B4a23DFA422fFC238c8B5b1736": [ + "0x84DffcfB232594975C608DE92544Ff239a24c9E9", + "CM" + ], + "0x6E40FED7c28bAA93a798cA10f8A93965a19eC52e": [ + "0x84DffcfB232594975C608DE92544Ff239a24c9E9", + "CM", + "0x87EB69Ae51317405FD285efD2326a4a11f6173b9" + ], + "0x3486B872768D361309e405A046C4BF995c21CC6c": [ + "0x84DffcfB232594975C608DE92544Ff239a24c9E9", + "CM", + "0x87EB69Ae51317405FD285efD2326a4a11f6173b9" + ], + "0x44D9b39bBdc2182Aa1af6f16f8F55E0eA038294d": [ + "0x84DffcfB232594975C608DE92544Ff239a24c9E9", + "CM", + "0x87EB69Ae51317405FD285efD2326a4a11f6173b9", + 1 + ] + } + } +} From 6dacf6d1c57ca3811b0e51345c1f3861b1f36c4f Mon Sep 17 00:00:00 2001 From: Eddort Date: Wed, 13 May 2026 23:44:18 +0200 Subject: [PATCH 04/12] feat: Update hoodi configuration for srv3 with new source diff allowances and WV contract --- .github/workflows/regression.yml | 2 ++ config_samples/ethereum/hoodi/srv3/hoodi_srv3_config.json | 8 ++++---- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/.github/workflows/regression.yml b/.github/workflows/regression.yml index 0d09e969..a3c8d86d 100644 --- a/.github/workflows/regression.yml +++ b/.github/workflows/regression.yml @@ -100,6 +100,8 @@ jobs: network: hoodi flags: > --allow-bytecode-diff 0x1a629bB7C0563650e46406Eb6764A2ba207a0eFE + --allow-source-diff 0x44d0b2B95d2C2bDF73FE4f5cD7E3A930494E5B1f + --allow-source-diff 0x6147270470A9Ee5b55c33EA71e32000E5d6D8E6B - config: config_samples/ethereum/hoodi/srv3/hoodi_srv3_easy_track_config.json network: hoodi flags: > diff --git a/config_samples/ethereum/hoodi/srv3/hoodi_srv3_config.json b/config_samples/ethereum/hoodi/srv3/hoodi_srv3_config.json index c4674e5f..0d15639d 100644 --- a/config_samples/ethereum/hoodi/srv3/hoodi_srv3_config.json +++ b/config_samples/ethereum/hoodi/srv3/hoodi_srv3_config.json @@ -13,7 +13,7 @@ "0x44d0b2B95d2C2bDF73FE4f5cD7E3A930494E5B1f": "StakingRouter", "0xFd1b63657dda65C4E6fDEF9d1f37064D078e9B49": "TopUpGateway", "0x86aeA211B30174b3ee5d294ECeaDbD7f1C575eF3": "ValidatorsExitBusOracle", - "0xB97e67CC20bd2970E30341c0ECc7497d8A5b7342": "WithdrawalVault" + "0x6724DaD16f7b05157dF72783F99cA6B813742330": "WithdrawalVault" }, "network": "hoodi", "explorer_hostname": "api.etherscan.io", @@ -21,7 +21,7 @@ "explorer_chain_id": 560048, "github_repo": { "url": "https://github.com/lidofinance/core", - "commit": "13cc8fa31cedc797575b107d576c7a63b96192e4", + "commit": "b25e808e1049e924275852b01be8963463335af9", "relative_root": "" }, "dependencies": { @@ -195,11 +195,11 @@ 1742213400, "0xe2EF9536DAAAEBFf5b1c130957AB3E80056b06D8" ], - "0xB97e67CC20bd2970E30341c0ECc7497d8A5b7342": [ + "0x6724DaD16f7b05157dF72783F99cA6B813742330": [ "0x3508A952176b3c15387C97BE809eaffB1982176a", "0x0534aA41907c9631fae990960bCC72d75fA7cfeD", "0x6679090D92b08a2a686eF8614feECD8cDFE209db", - "0xce93710b849e0dC202AaC513837e05bEA9D7DdFD", + "0xC9991Bb865d025364BCbBd99f36e85889Fb68e69", "0x00000961Ef480Eb55e80D19ad83579A64c007002", "0x0000BBdDc7CE488642fb579F8B00f3a590007251" ] From dd9c9b5d79158af3ea55f94b587ac0f766a5e0a8 Mon Sep 17 00:00:00 2001 From: Eddort Date: Thu, 25 Jun 2026 16:45:03 +0200 Subject: [PATCH 05/12] feat: Update hoodi configuration for srv3 with new contract addresses --- .../hoodi/srv3/hoodi_srv3_config.json | 60 +++++++++---------- 1 file changed, 30 insertions(+), 30 deletions(-) diff --git a/config_samples/ethereum/hoodi/srv3/hoodi_srv3_config.json b/config_samples/ethereum/hoodi/srv3/hoodi_srv3_config.json index 0d15639d..9e948171 100644 --- a/config_samples/ethereum/hoodi/srv3/hoodi_srv3_config.json +++ b/config_samples/ethereum/hoodi/srv3/hoodi_srv3_config.json @@ -1,18 +1,18 @@ { "contracts": { - "0xDB47544d5813f15116bf95c1cF2ff4dEdb2226fD": "Accounting", - "0x41bF10F28A1312f2241f86A2537A04b08e343C0a": "AccountingOracle", - "0x6147270470A9Ee5b55c33EA71e32000E5d6D8E6B": "Lido", - "0x27Ff16a465c1A00a727dd3Dbd479c5F3De275a1f": "ConsolidationBus", + "0xA39fe063A6d1420E59d218d318d52D84Fbd9202F": "Accounting", + "0xfdF72d92B55555B02e89D61177D5138B144815dE": "AccountingOracle", + "0xB9A2Fb8336f3775d790b3FdD6151e3F193AA7352": "Lido", + "0x2908c4B32548D8799fDc601C1a75E7d5C2FbC556": "ConsolidationBus", "0xC9991Bb865d025364BCbBd99f36e85889Fb68e69": "ConsolidationGateway", - "0x2A8585201BFD6830944f0bf008B774e7e32b380d": "ConsolidationMigrator", - "0x1a629bB7C0563650e46406Eb6764A2ba207a0eFE": "DepositSecurityModule", - "0x2C33BE7c09bfBC8e41E7648d611d857fD4831b68": "LidoLocator", - "0x8E6FDB231D7CE30C2459319c0d4c4Eb4B681f9C9": "MinFirstAllocationStrategy", - "0x049A972e9cBEfFFc1c2543dFD0Fa892C2E9Ed6C5": "OracleReportSanityChecker", - "0x44d0b2B95d2C2bDF73FE4f5cD7E3A930494E5B1f": "StakingRouter", - "0xFd1b63657dda65C4E6fDEF9d1f37064D078e9B49": "TopUpGateway", - "0x86aeA211B30174b3ee5d294ECeaDbD7f1C575eF3": "ValidatorsExitBusOracle", + "0x8BF11ead77fFe142AB27BE486Bc5aB22D9baF520": "ConsolidationMigrator", + "0xf738F86009Ec704880c9Aa175fc5869F020FEe4e": "DepositSecurityModule", + "0x1dB06d15976954D48765f40d4bDd840C257CD4B9": "LidoLocator", + "0xB2A07615cDe70Dc99f493aA5556175405537B21b": "MinFirstAllocationStrategy", + "0xD0261b0032A00a7449ee7fbE14d3f98702996441": "OracleReportSanityChecker", + "0x2107dffDf8C2934A5197C8C6c5eC646099C51204": "StakingRouter", + "0x8621D8a402fdf2a131E38e16ac50f4C97660Fc2b": "TopUpGateway", + "0xA881F320E17Aa97d6Ce0bE76B0e2620bd2FC555A": "ValidatorsExitBusOracle", "0x6724DaD16f7b05157dF72783F99cA6B813742330": "WithdrawalVault" }, "network": "hoodi", @@ -85,20 +85,20 @@ "bytecode_comparison": { "constructor_calldata": {}, "deployment_from": { - "0x1a629bB7C0563650e46406Eb6764A2ba207a0eFE": "0x83BCE68B4e8b7071b2a664a26e6D3Bc17eEe3102" + "0xf738F86009Ec704880c9Aa175fc5869F020FEe4e": "0x83BCE68B4e8b7071b2a664a26e6D3Bc17eEe3102" }, "constructor_args": { - "0xDB47544d5813f15116bf95c1cF2ff4dEdb2226fD": [ + "0xA39fe063A6d1420E59d218d318d52D84Fbd9202F": [ "0xe2EF9536DAAAEBFf5b1c130957AB3E80056b06D8", "0x3508A952176b3c15387C97BE809eaffB1982176a" ], - "0x41bF10F28A1312f2241f86A2537A04b08e343C0a": [ + "0xfdF72d92B55555B02e89D61177D5138B144815dE": [ "0xe2EF9536DAAAEBFf5b1c130957AB3E80056b06D8", 12, 1742213400 ], - "0x6147270470A9Ee5b55c33EA71e32000E5d6D8E6B": [], - "0x27Ff16a465c1A00a727dd3Dbd479c5F3De275a1f": [ + "0xB9A2Fb8336f3775d790b3FdD6151e3F193AA7352": [], + "0x2908c4B32548D8799fDc601C1a75E7d5C2FbC556": [ "0xC9991Bb865d025364BCbBd99f36e85889Fb68e69" ], "0xC9991Bb865d025364BCbBd99f36e85889Fb68e69": [ @@ -111,26 +111,26 @@ "0x0000000000000000000000000000000000000000000000000096000000000028", 0 ], - "0x2A8585201BFD6830944f0bf008B774e7e32b380d": [ + "0x8BF11ead77fFe142AB27BE486Bc5aB22D9baF520": [ "0xCc820558B39ee15C7C45B59390B503b83fb499A8", "0xe09fBcE63826468867eE66Eda491E444829E022A", 1, 5 ], - "0x1a629bB7C0563650e46406Eb6764A2ba207a0eFE": [ + "0xf738F86009Ec704880c9Aa175fc5869F020FEe4e": [ "0x3508A952176b3c15387C97BE809eaffB1982176a", "0x00000000219ab540356cBB839Cbe05303d7705Fa", "0xCc820558B39ee15C7C45B59390B503b83fb499A8", 6646, 200 ], - "0x2C33BE7c09bfBC8e41E7648d611d857fD4831b68": [ + "0x1dB06d15976954D48765f40d4bDd840C257CD4B9": [ [ "0xcb883B1bD0a41512b42D2dB267F2A2cd919FB216", - "0x1a629bB7C0563650e46406Eb6764A2ba207a0eFE", + "0xf738F86009Ec704880c9Aa175fc5869F020FEe4e", "0x9b108015fe433F173696Af3Aa0CF7CDb3E104258", "0x3508A952176b3c15387C97BE809eaffB1982176a", - "0x049A972e9cBEfFFc1c2543dFD0Fa892C2E9Ed6C5", + "0xD0261b0032A00a7449ee7fbE14d3f98702996441", "0x9c53d0075eA00ad77dDAd1b71E67bb97AaBC1e3D", "0xb2c99cd38a2636a6281a849C8de938B3eF4A7C3D", "0xCc820558B39ee15C7C45B59390B503b83fb499A8", @@ -152,8 +152,8 @@ "0x10DBEb3367876826d00D21718D1d893e0fbD2956" ] ], - "0x8E6FDB231D7CE30C2459319c0d4c4Eb4B681f9C9": [], - "0x049A972e9cBEfFFc1c2543dFD0Fa892C2E9Ed6C5": [ + "0xB2A07615cDe70Dc99f493aA5556175405537B21b": [], + "0xD0261b0032A00a7449ee7fbE14d3f98702996441": [ "0xe2EF9536DAAAEBFf5b1c130957AB3E80056b06D8", "0x9b5b78D1C9A3238bF24662067e34c57c83E8c354", "0x0534aA41907c9631fae990960bCC72d75fA7cfeD", @@ -176,21 +176,21 @@ 300 ] ], - "0x44d0b2B95d2C2bDF73FE4f5cD7E3A930494E5B1f": [ + "0x2107dffDf8C2934A5197C8C6c5eC646099C51204": [ "0x00000000219ab540356cBB839Cbe05303d7705Fa", "0x3508A952176b3c15387C97BE809eaffB1982176a", "0xe2EF9536DAAAEBFf5b1c130957AB3E80056b06D8", "0x1bc16d674ec800000", "0x6f05b59d3b20000000" ], - "0xFd1b63657dda65C4E6fDEF9d1f37064D078e9B49": [ + "0x8621D8a402fdf2a131E38e16ac50f4C97660Fc2b": [ "0xe2EF9536DAAAEBFf5b1c130957AB3E80056b06D8", "0x0000000000000000000000000000000000000000000000000096000000000028", "0x0000000000000000000000000000000000000000000000000096000000000028", 0, 32 ], - "0x86aeA211B30174b3ee5d294ECeaDbD7f1C575eF3": [ + "0xA881F320E17Aa97d6Ce0bE76B0e2620bd2FC555A": [ 12, 1742213400, "0xe2EF9536DAAAEBFf5b1c130957AB3E80056b06D8" @@ -206,13 +206,13 @@ }, "libraries": { "contracts/common/lib/MinFirstAllocationStrategy.sol": { - "MinFirstAllocationStrategy": "0x8E6FDB231D7CE30C2459319c0d4c4Eb4B681f9C9" + "MinFirstAllocationStrategy": "0xB2A07615cDe70Dc99f493aA5556175405537B21b" }, "contracts/0.8.25/lib/BeaconChainDepositor.sol": { - "BeaconChainDepositor": "0x29e702f163d4Ca47a0813e09BDfdAb960fb1B90b" + "BeaconChainDepositor": "0x963ea462c33684Df516405B4f59c718EE4E22932" }, "contracts/0.8.25/sr/SRLib.sol": { - "SRLib": "0xA43DeC6250D4B59C345c8515569983E3e24d6990" + "SRLib": "0x1AF72A21223FAb0A7bB5ec6F2C3dE0C018803F0D" } } } From c6cd24ac4bb6ede5c223c2c0b2d1e07731711008 Mon Sep 17 00:00:00 2001 From: Eddort Date: Fri, 26 Jun 2026 12:19:54 +0200 Subject: [PATCH 06/12] feat: Update hoodi configuration for srv3 with corrected contract addresses and commit hash --- config_samples/ethereum/hoodi/srv3/hoodi_srv3_config.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/config_samples/ethereum/hoodi/srv3/hoodi_srv3_config.json b/config_samples/ethereum/hoodi/srv3/hoodi_srv3_config.json index 9e948171..63b28f2e 100644 --- a/config_samples/ethereum/hoodi/srv3/hoodi_srv3_config.json +++ b/config_samples/ethereum/hoodi/srv3/hoodi_srv3_config.json @@ -1,7 +1,7 @@ { "contracts": { "0xA39fe063A6d1420E59d218d318d52D84Fbd9202F": "Accounting", - "0xfdF72d92B55555B02e89D61177D5138B144815dE": "AccountingOracle", + "0xD00dD90651f031ED3158Cf75AC6e4361B4CCcBD8": "AccountingOracle", "0xB9A2Fb8336f3775d790b3FdD6151e3F193AA7352": "Lido", "0x2908c4B32548D8799fDc601C1a75E7d5C2FbC556": "ConsolidationBus", "0xC9991Bb865d025364BCbBd99f36e85889Fb68e69": "ConsolidationGateway", @@ -21,7 +21,7 @@ "explorer_chain_id": 560048, "github_repo": { "url": "https://github.com/lidofinance/core", - "commit": "b25e808e1049e924275852b01be8963463335af9", + "commit": "a9885382fde2d6c8a82a90cc2e9924d7e9e28d40", "relative_root": "" }, "dependencies": { @@ -92,7 +92,7 @@ "0xe2EF9536DAAAEBFf5b1c130957AB3E80056b06D8", "0x3508A952176b3c15387C97BE809eaffB1982176a" ], - "0xfdF72d92B55555B02e89D61177D5138B144815dE": [ + "0xD00dD90651f031ED3158Cf75AC6e4361B4CCcBD8": [ "0xe2EF9536DAAAEBFf5b1c130957AB3E80056b06D8", 12, 1742213400 From 9198ea98b7dce3e5b6c834457dbe055dba047ee3 Mon Sep 17 00:00:00 2001 From: Eddort Date: Fri, 26 Jun 2026 16:31:16 +0200 Subject: [PATCH 07/12] fix: types and update sr config --- .../hoodi/srv3/hoodi_srv3_config.json | 277 ++++++++++++++++++ diffyscan/diffyscan.py | 3 +- 2 files changed, 279 insertions(+), 1 deletion(-) diff --git a/config_samples/ethereum/hoodi/srv3/hoodi_srv3_config.json b/config_samples/ethereum/hoodi/srv3/hoodi_srv3_config.json index 63b28f2e..48fd4816 100644 --- a/config_samples/ethereum/hoodi/srv3/hoodi_srv3_config.json +++ b/config_samples/ethereum/hoodi/srv3/hoodi_srv3_config.json @@ -215,5 +215,282 @@ "SRLib": "0x1AF72A21223FAb0A7bB5ec6F2C3dE0C018803F0D" } } + }, + "allowed_diffs": { + "bytecode": { + "0xA39fe063A6d1420E59d218d318d52D84Fbd9202F": [ + { + "reason": "Trailing CBOR metadata (Solidity compiler/source-path hash appended after runtime code) differs; executable runtime bytecode is identical.", + "cbor_metadata": true + } + ], + "0xD00dD90651f031ED3158Cf75AC6e4361B4CCcBD8": [ + { + "reason": "Trailing CBOR metadata (Solidity compiler/source-path hash appended after runtime code) differs; executable runtime bytecode is identical.", + "cbor_metadata": true + } + ], + "0xB9A2Fb8336f3775d790b3FdD6151e3F193AA7352": [ + { + "reason": "Trailing CBOR metadata (Solidity compiler/source-path hash appended after runtime code) differs; executable runtime bytecode is identical.", + "cbor_metadata": true + } + ], + "0x2908c4B32548D8799fDc601C1a75E7d5C2FbC556": [ + { + "reason": "Trailing CBOR metadata (Solidity compiler/source-path hash appended after runtime code) differs; executable runtime bytecode is identical.", + "cbor_metadata": true + } + ], + "0x8BF11ead77fFe142AB27BE486Bc5aB22D9baF520": [ + { + "reason": "Trailing CBOR metadata (Solidity compiler/source-path hash appended after runtime code) differs; executable runtime bytecode is identical.", + "cbor_metadata": true + } + ], + "0xf738F86009Ec704880c9Aa175fc5869F020FEe4e": [ + { + "reason": "Constructor-set immutable values (addresses/hashes baked into the bytecode at deploy time) differ from the placeholders in freshly compiled bytecode, and the trailing CBOR metadata hash differs; both are expected and carry no executable-logic difference.", + "immutables": [ + { + "offset": 643, + "value": "0xfed594227b745906546cc94cefc1c0e8b525a7d631311ddbd07b8ccdcc73d6f3" + }, + { + "offset": 706, + "value": "0x00000000000000000000000000000000219ab540356cbb839cbe05303d7705fa" + }, + { + "offset": 824, + "value": "0x67a2d8939f08188c60891a393c419cb4f438a4a3fd25ab9050702c39fa368dcd" + }, + { + "offset": 882, + "value": "0x17f7c014faa6a92ed0cdbcc5d87e37b6f45827c3414e7d20832be74ea7d08185" + }, + { + "offset": 929, + "value": "0x000000000000000000000000cc820558b39ee15c7c45b59390b503b83fb499a8" + }, + { + "offset": 1388, + "value": "0x67a2d8939f08188c60891a393c419cb4f438a4a3fd25ab9050702c39fa368dcd" + }, + { + "offset": 1720, + "value": "0x000000000000000000000000cc820558b39ee15c7c45b59390b503b83fb499a8" + }, + { + "offset": 2023, + "value": "0xfed594227b745906546cc94cefc1c0e8b525a7d631311ddbd07b8ccdcc73d6f3" + }, + { + "offset": 2246, + "value": "0x000000000000000000000000cc820558b39ee15c7c45b59390b503b83fb499a8" + }, + { + "offset": 2507, + "value": "0x00000000000000000000000000000000219ab540356cbb839cbe05303d7705fa" + }, + { + "offset": 2709, + "value": "0x000000000000000000000000cc820558b39ee15c7c45b59390b503b83fb499a8" + }, + { + "offset": 3170, + "value": "0x000000000000000000000000cc820558b39ee15c7c45b59390b503b83fb499a8" + }, + { + "offset": 4642, + "value": "0x000000000000000000000000cc820558b39ee15c7c45b59390b503b83fb499a8" + }, + { + "offset": 4800, + "value": "0x000000000000000000000000cc820558b39ee15c7c45b59390b503b83fb499a8" + }, + { + "offset": 4976, + "value": "0x17f7c014faa6a92ed0cdbcc5d87e37b6f45827c3414e7d20832be74ea7d08185" + } + ], + "cbor_metadata": true + } + ], + "0x1dB06d15976954D48765f40d4bDd840C257CD4B9": [ + { + "reason": "Trailing CBOR metadata (Solidity compiler/source-path hash appended after runtime code) differs; executable runtime bytecode is identical.", + "cbor_metadata": true + } + ], + "0xB2A07615cDe70Dc99f493aA5556175405537B21b": [ + { + "reason": "Trailing CBOR metadata (Solidity compiler/source-path hash appended after runtime code) differs; executable runtime bytecode is identical.", + "cbor_metadata": true + } + ], + "0xD0261b0032A00a7449ee7fbE14d3f98702996441": [ + { + "reason": "Trailing CBOR metadata (Solidity compiler/source-path hash appended after runtime code) differs; executable runtime bytecode is identical.", + "cbor_metadata": true + } + ], + "0x2107dffDf8C2934A5197C8C6c5eC646099C51204": [ + { + "reason": "Trailing CBOR metadata (Solidity compiler/source-path hash appended after runtime code) differs; executable runtime bytecode is identical.", + "cbor_metadata": true + } + ], + "0x8621D8a402fdf2a131E38e16ac50f4C97660Fc2b": [ + { + "reason": "Trailing CBOR metadata (Solidity compiler/source-path hash appended after runtime code) differs; executable runtime bytecode is identical.", + "cbor_metadata": true + } + ], + "0xA881F320E17Aa97d6Ce0bE76B0e2620bd2FC555A": [ + { + "reason": "Trailing CBOR metadata (Solidity compiler/source-path hash appended after runtime code) differs; executable runtime bytecode is identical.", + "cbor_metadata": true + } + ], + "0x6724DaD16f7b05157dF72783F99cA6B813742330": [ + { + "reason": "Trailing CBOR metadata (Solidity compiler/source-path hash appended after runtime code) differs; executable runtime bytecode is identical.", + "cbor_metadata": true + } + ] + }, + "source": { + "0xA39fe063A6d1420E59d218d318d52D84Fbd9202F": [ + { + "reason": "Verified-source IOracleReportSanityChecker interface declares an extra method (checkCLPendingBalanceIncrease) not present at this GitHub commit; the method is unused by Accounting and has no bytecode impact (runtime bytecode matches modulo CBOR metadata).", + "line_ranges": [ + { + "file": "contracts/common/interfaces/IOracleReportSanityChecker.sol", + "github": { + "start": 36, + "count": 0 + }, + "explorer": { + "start": 36, + "count": 11 + } + } + ] + } + ], + "0xC9991Bb865d025364BCbBd99f36e85889Fb68e69": [ + { + "reason": "NatSpec comment wording differences in ConsolidationGateway.sol, plus the 'is IPausableUntil' interface-inheritance declaration / import present only in the GitHub source of PausableUntil.sol. Interface inheritance is declaration-only and produces no runtime bytecode (bytecode matches modulo CBOR metadata).", + "line_ranges": [ + { + "file": "contracts/0.8.25/consolidation/ConsolidationGateway.sol", + "github": { + "start": 50, + "count": 1 + }, + "explorer": { + "start": 50, + "count": 1 + } + }, + { + "file": "contracts/0.8.25/consolidation/ConsolidationGateway.sol", + "github": { + "start": 64, + "count": 1 + }, + "explorer": { + "start": 64, + "count": 1 + } + }, + { + "file": "contracts/0.8.25/consolidation/ConsolidationGateway.sol", + "github": { + "start": 144, + "count": 1 + }, + "explorer": { + "start": 144, + "count": 1 + } + }, + { + "file": "contracts/common/utils/PausableUntil.sol", + "github": { + "start": 7, + "count": 1 + }, + "explorer": { + "start": 7, + "count": 0 + } + }, + { + "file": "contracts/common/utils/PausableUntil.sol", + "github": { + "start": 13, + "count": 1 + }, + "explorer": { + "start": 12, + "count": 1 + } + } + ] + } + ], + "0x6724DaD16f7b05157dF72783F99cA6B813742330": [ + { + "reason": "NatSpec comment-only wording differences in WithdrawalVault.sol (migration label v2/v3, 'this contract' vs 'burner contract', 'withdrawal' vs 'consolidation' fee wording). No code changes; runtime bytecode matches modulo CBOR metadata.", + "line_ranges": [ + { + "file": "contracts/0.8.9/WithdrawalVault.sol", + "github": { + "start": 86, + "count": 1 + }, + "explorer": { + "start": 86, + "count": 1 + } + }, + { + "file": "contracts/0.8.9/WithdrawalVault.sol", + "github": { + "start": 121, + "count": 1 + }, + "explorer": { + "start": 121, + "count": 1 + } + }, + { + "file": "contracts/0.8.9/WithdrawalVault.sol", + "github": { + "start": 138, + "count": 1 + }, + "explorer": { + "start": 138, + "count": 1 + } + }, + { + "file": "contracts/0.8.9/WithdrawalVault.sol", + "github": { + "start": 193, + "count": 1 + }, + "explorer": { + "start": 193, + "count": 1 + } + } + ] + } + ] + } } } diff --git a/diffyscan/diffyscan.py b/diffyscan/diffyscan.py index 00e491ac..6cb808b1 100644 --- a/diffyscan/diffyscan.py +++ b/diffyscan/diffyscan.py @@ -6,6 +6,7 @@ import time import traceback from pathlib import Path +from typing import Any from dotenv import load_dotenv @@ -217,7 +218,7 @@ def exact_match() -> dict: config.get("bytecode_comparison"), contract_address_from_config ) gas_limit = config.get("deployment_gas_limit") - extra = {} + extra: dict[str, Any] = {} if deployment_from: logger.info("Using deployment simulation from config", deployment_from) extra["caller"] = deployment_from From e4da25e9b23ffb48136aa394880ab8186a3775c5 Mon Sep 17 00:00:00 2001 From: Eddort Date: Fri, 26 Jun 2026 16:31:57 +0200 Subject: [PATCH 08/12] feat: add extra_sources config support --- CLAUDE.md | 4 +- .../hoodi/srv3/hoodi_srv3_config.json | 47 +++++++++------- diffyscan/diffyscan.py | 24 ++++++++- diffyscan/utils/common.py | 7 ++- diffyscan/utils/custom_types.py | 1 + tests/test_diffyscan_allowlist_runtime.py | 54 +++++++++++++++++++ 6 files changed, 116 insertions(+), 21 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index f0092ca6..65f5be7a 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -68,10 +68,12 @@ explorer_hostname: "api.etherscan.io" explorer_token_env_var: "ETHERSCAN_EXPLORER_TOKEN" github_repo: { url, commit, relative_root } dependencies: { "dep_name": { url, commit, relative_root } } -bytecode_comparison: { constructor_calldata, constructor_args, libraries } +bytecode_comparison: { constructor_calldata, constructor_args, libraries, deployment_from, extra_sources } allowed_diffs: { bytecode: {...}, source: {...} } # optional; see below ``` +`bytecode_comparison.extra_sources` (`{ "0xaddr": ["path/to/File.sol"] }`, optional) names additional source files to fetch from the configured GitHub repo/commit and add to the compilation — for contracts whose explorer-verified source set omits a file the pinned GitHub source imports (e.g. a newly-added interface). The files are fetched the same way as all others (honoring `dependencies`), so verification stays honest. + `allowed_diffs` declares known/expected diffs per contract so a run still passes while everything else stays verified (validated by `diffyscan/utils/allowed_diffs.py`). Each rule needs a `reason`. **Prefer the tightest facet** — bytecode: `immutables`, `byte_ranges`, `cbor_metadata`, `constructor_args`/`constructor_calldata`; source: `line_ranges`, `files`. `any: true` is a blanket wildcard that hides all future drift — avoid it unless a diff genuinely can't be scoped, and say why in the `reason`. The deprecated `--allow-source-diff`/`--allow-bytecode-diff` CLI flags are just shorthands for `any: true`. `tests/test_no_wildcard_regression.py` guards against new wildcards. ### Environment variables diff --git a/config_samples/ethereum/hoodi/srv3/hoodi_srv3_config.json b/config_samples/ethereum/hoodi/srv3/hoodi_srv3_config.json index 48fd4816..657c7d79 100644 --- a/config_samples/ethereum/hoodi/srv3/hoodi_srv3_config.json +++ b/config_samples/ethereum/hoodi/srv3/hoodi_srv3_config.json @@ -87,6 +87,11 @@ "deployment_from": { "0xf738F86009Ec704880c9Aa175fc5869F020FEe4e": "0x83BCE68B4e8b7071b2a664a26e6D3Bc17eEe3102" }, + "extra_sources": { + "0xC9991Bb865d025364BCbBd99f36e85889Fb68e69": [ + "contracts/common/interfaces/IPausableUntil.sol" + ] + }, "constructor_args": { "0xA39fe063A6d1420E59d218d318d52D84Fbd9202F": [ "0xe2EF9536DAAAEBFf5b1c130957AB3E80056b06D8", @@ -218,6 +223,12 @@ }, "allowed_diffs": { "bytecode": { + "0xC9991Bb865d025364BCbBd99f36e85889Fb68e69": [ + { + "reason": "Trailing CBOR metadata hash differs (compiled with IPausableUntil supplied via extra_sources); runtime bytecode is identical \u2014 'is IPausableUntil' adds no executable code.", + "cbor_metadata": true + } + ], "0xA39fe063A6d1420E59d218d318d52D84Fbd9202F": [ { "reason": "Trailing CBOR metadata (Solidity compiler/source-path hash appended after runtime code) differs; executable runtime bytecode is identical.", @@ -360,24 +371,6 @@ ] }, "source": { - "0xA39fe063A6d1420E59d218d318d52D84Fbd9202F": [ - { - "reason": "Verified-source IOracleReportSanityChecker interface declares an extra method (checkCLPendingBalanceIncrease) not present at this GitHub commit; the method is unused by Accounting and has no bytecode impact (runtime bytecode matches modulo CBOR metadata).", - "line_ranges": [ - { - "file": "contracts/common/interfaces/IOracleReportSanityChecker.sol", - "github": { - "start": 36, - "count": 0 - }, - "explorer": { - "start": 36, - "count": 11 - } - } - ] - } - ], "0xC9991Bb865d025364BCbBd99f36e85889Fb68e69": [ { "reason": "NatSpec comment wording differences in ConsolidationGateway.sol, plus the 'is IPausableUntil' interface-inheritance declaration / import present only in the GitHub source of PausableUntil.sol. Interface inheritance is declaration-only and produces no runtime bytecode (bytecode matches modulo CBOR metadata).", @@ -440,6 +433,24 @@ ] } ], + "0xA39fe063A6d1420E59d218d318d52D84Fbd9202F": [ + { + "reason": "Verified-source IOracleReportSanityChecker interface declares an extra method (checkCLPendingBalanceIncrease) not present at this GitHub commit; the method is unused by Accounting and has no bytecode impact (runtime bytecode matches modulo CBOR metadata).", + "line_ranges": [ + { + "file": "contracts/common/interfaces/IOracleReportSanityChecker.sol", + "github": { + "start": 36, + "count": 0 + }, + "explorer": { + "start": 36, + "count": 11 + } + } + ] + } + ], "0x6724DaD16f7b05157dF72783F99cA6B813742330": [ { "reason": "NatSpec comment-only wording differences in WithdrawalVault.sol (migration label v2/v3, 'this contract' vs 'burner contract', 'withdrawal' vs 'consolidation' fee wording). No code changes; runtime bytecode matches modulo CBOR metadata.", diff --git a/diffyscan/diffyscan.py b/diffyscan/diffyscan.py index 6cb808b1..d3b167e4 100644 --- a/diffyscan/diffyscan.py +++ b/diffyscan/diffyscan.py @@ -79,13 +79,22 @@ def _build_github_solc_input( github_api_token, recursive_parsing, cache_github, + extra_source_paths=None, ): solc_input = contract_source_code["solcInput"] sources = get_solc_sources(solc_input) updated_sources = {} missing = [] - for path_to_file in sources: + # Extra sources first so they cannot be silently shadowed by an explorer + # entry of the same path; the explorer loop below skips already-fetched paths. + paths = list(sources) + [ + path for path in (extra_source_paths or []) if path not in sources + ] + + for path_to_file in paths: + if path_to_file in updated_sources: + continue github_file = _fetch_github_source( path_to_file, config, @@ -160,12 +169,25 @@ def run_bytecode_diff( manual_libraries, ) + extra_sources_cfg = (config.get("bytecode_comparison") or {}).get( + "extra_sources" + ) or {} + extra_source_paths: list[str] = next( + ( + paths + for addr, paths in extra_sources_cfg.items() + if addr.lower() == contract_address_from_config.lower() + ), + [], + ) + github_solc_input, missing_sources = _build_github_solc_input( contract_source_code, config, github_api_token, recursive_parsing, cache_github, + extra_source_paths, ) if missing_sources: missing_preview = ", ".join(missing_sources[:5]) diff --git a/diffyscan/utils/common.py b/diffyscan/utils/common.py index 66bdf8aa..80fa9052 100644 --- a/diffyscan/utils/common.py +++ b/diffyscan/utils/common.py @@ -122,7 +122,12 @@ def _validate_yaml_hex_keys(config: dict, path: str) -> None: bytecode = config.get("bytecode_comparison") if isinstance(bytecode, dict): - for section in ("constructor_args", "constructor_calldata", "deployment_from"): + for section in ( + "constructor_args", + "constructor_calldata", + "deployment_from", + "extra_sources", + ): _validate_yaml_address_keys( bytecode.get(section), path, diff --git a/diffyscan/utils/custom_types.py b/diffyscan/utils/custom_types.py index 92c2b003..4737f978 100644 --- a/diffyscan/utils/custom_types.py +++ b/diffyscan/utils/custom_types.py @@ -7,6 +7,7 @@ class BinaryConfig(TypedDict): constructor_args: NotRequired[dict[str, list]] deployment_from: NotRequired[dict[str, str]] libraries: NotRequired[dict[str, dict[str, str]]] + extra_sources: NotRequired[dict[str, list[str]]] class ImmutableRule(TypedDict): diff --git a/tests/test_diffyscan_allowlist_runtime.py b/tests/test_diffyscan_allowlist_runtime.py index 5fde51fc..9e41e3d0 100644 --- a/tests/test_diffyscan_allowlist_runtime.py +++ b/tests/test_diffyscan_allowlist_runtime.py @@ -168,3 +168,57 @@ def fake_simulate_deployment(data, rpc_url, **kwargs): assert result["status"] == "allowed" assert result["matched_facets"] == ["exact_match", "constructor_args"] assert [call["gas_limit"] for call in simulate_calls] == [12345, 12345] + + +def _fetcher(monkeypatch, content_by_path): + calls = [] + + def fake_fetch(path_to_file, *args, **kwargs): + calls.append(path_to_file) + return content_by_path.get(path_to_file) + + monkeypatch.setattr(runner, "_fetch_github_source", fake_fetch) + return calls + + +def test_extra_sources_are_added_to_github_compilation(monkeypatch): + monkeypatch.setattr( + runner, "get_solc_sources", lambda solc_input: ["A.sol", "B.sol"] + ) + calls = _fetcher(monkeypatch, {"A.sol": "a", "B.sol": "b", "C.sol": "c"}) + + solc_input, missing = runner._build_github_solc_input( + {"solcInput": {"sources": {}}}, + {}, + "github-token", + False, + False, + ["C.sol"], + ) + + assert missing == [] + assert set(solc_input["sources"]) == {"A.sol", "B.sol", "C.sol"} + assert "C.sol" in calls + + +def test_extra_source_already_in_explorer_list_is_not_fetched_twice(monkeypatch): + monkeypatch.setattr(runner, "get_solc_sources", lambda solc_input: ["A.sol"]) + calls = _fetcher(monkeypatch, {"A.sol": "a"}) + + solc_input, missing = runner._build_github_solc_input( + {"solcInput": {}}, {}, "github-token", False, False, ["A.sol"] + ) + + assert calls == ["A.sol"] + assert set(solc_input["sources"]) == {"A.sol"} + + +def test_missing_extra_source_is_reported(monkeypatch): + monkeypatch.setattr(runner, "get_solc_sources", lambda solc_input: ["A.sol"]) + _fetcher(monkeypatch, {"A.sol": "a"}) # "C.sol" returns None -> missing + + _, missing = runner._build_github_solc_input( + {"solcInput": {}}, {}, "github-token", False, False, ["C.sol"] + ) + + assert missing == ["C.sol"] From 205adba7f0f79981af4dc8f421ec5b17bad52a8e Mon Sep 17 00:00:00 2001 From: Eddort Date: Fri, 26 Jun 2026 16:55:39 +0200 Subject: [PATCH 09/12] feat: update srv3 easy track config --- .../srv3/hoodi_srv3_easy_track_config.json | 244 ++++++++++++++++++ 1 file changed, 244 insertions(+) diff --git a/config_samples/ethereum/hoodi/srv3/hoodi_srv3_easy_track_config.json b/config_samples/ethereum/hoodi/srv3/hoodi_srv3_easy_track_config.json index 3d8d23b4..421f9b25 100644 --- a/config_samples/ethereum/hoodi/srv3/hoodi_srv3_easy_track_config.json +++ b/config_samples/ethereum/hoodi/srv3/hoodi_srv3_easy_track_config.json @@ -71,5 +71,249 @@ 1 ] } + }, + "allowed_diffs": { + "bytecode": { + "0x22D36e7616F541A527989C5652fDA4d527bB461C": [ + { + "reason": "Trailing CBOR metadata hash differs (compiler/source-path fingerprint); executable runtime bytecode is identical.", + "cbor_metadata": true + } + ], + "0xD63cf25df1bA6144db27A81A98120Dfc53dE4540": [ + { + "reason": "Trailing CBOR metadata hash differs (compiler/source-path fingerprint); executable runtime bytecode is identical.", + "cbor_metadata": true + } + ], + "0xf71fcB20B9FB8468653Bcb24E31F39bc069D5995": [ + { + "reason": "Trailing CBOR metadata hash differs (compiler/source-path fingerprint); executable runtime bytecode is identical.", + "cbor_metadata": true + } + ], + "0x4EaB04775837A6F0218750A10454119f349258FE": [ + { + "reason": "Trailing CBOR metadata hash differs (compiler/source-path fingerprint); executable runtime bytecode is identical.", + "cbor_metadata": true + } + ], + "0xd0c38B2F0C1F760976dA010C1c35D828331Ff9E2": [ + { + "reason": "Trailing CBOR metadata hash differs (compiler/source-path fingerprint); executable runtime bytecode is identical.", + "cbor_metadata": true + } + ], + "0x5194cC02B6F477B4a23DFA422fFC238c8B5b1736": [ + { + "reason": "Trailing CBOR metadata hash differs (compiler/source-path fingerprint); executable runtime bytecode is identical.", + "cbor_metadata": true + } + ], + "0x6E40FED7c28bAA93a798cA10f8A93965a19eC52e": [ + { + "reason": "Trailing CBOR metadata hash differs (compiler/source-path fingerprint); executable runtime bytecode is identical.", + "cbor_metadata": true + } + ], + "0x3486B872768D361309e405A046C4BF995c21CC6c": [ + { + "reason": "Trailing CBOR metadata hash differs (compiler/source-path fingerprint); executable runtime bytecode is identical.", + "cbor_metadata": true + } + ], + "0x44D9b39bBdc2182Aa1af6f16f8F55E0eA038294d": [ + { + "reason": "Trailing CBOR metadata hash differs (compiler/source-path fingerprint); executable runtime bytecode is identical.", + "cbor_metadata": true + } + ] + }, + "source": { + "0x22D36e7616F541A527989C5652fDA4d527bB461C": [ + { + "reason": "Import-path differences only: the GitHub source uses relative imports (../, ./) while the Etherscan verified source rewrote them to absolute paths (contracts/...). Same files resolved; no executable-logic change (runtime bytecode matches modulo CBOR metadata).", + "line_ranges": [ + { + "file": "contracts/EVMScriptFactories/AllowConsolidationPair.sol", + "github": { + "start": 6, + "count": 7 + }, + "explorer": { + "start": 6, + "count": 7 + } + }, + { + "file": "contracts/interfaces/ICuratedModule.sol", + "github": { + "start": 6, + "count": 2 + }, + "explorer": { + "start": 6, + "count": 2 + } + } + ] + } + ], + "0xD63cf25df1bA6144db27A81A98120Dfc53dE4540": [ + { + "reason": "Import-path differences only: the GitHub source uses relative imports (../, ./) while the Etherscan verified source rewrote them to absolute paths (contracts/...). Same files resolved; no executable-logic change (runtime bytecode matches modulo CBOR metadata).", + "line_ranges": [ + { + "file": "contracts/EVMScriptFactories/UpdateStakingModuleShareLimits.sol", + "github": { + "start": 6, + "count": 4 + }, + "explorer": { + "start": 6, + "count": 4 + } + } + ] + } + ], + "0xf71fcB20B9FB8468653Bcb24E31F39bc069D5995": [ + { + "reason": "Import-path differences only: the GitHub source uses relative imports (../, ./) while the Etherscan verified source rewrote them to absolute paths (contracts/...). Same files resolved; no executable-logic change (runtime bytecode matches modulo CBOR metadata).", + "line_ranges": [ + { + "file": "contracts/EVMScriptFactories/SetMerkleGateTree.sol", + "github": { + "start": 6, + "count": 4 + }, + "explorer": { + "start": 6, + "count": 4 + } + } + ] + } + ], + "0x4EaB04775837A6F0218750A10454119f349258FE": [ + { + "reason": "Import-path differences only: the GitHub source uses relative imports (../, ./) while the Etherscan verified source rewrote them to absolute paths (contracts/...). Same files resolved; no executable-logic change (runtime bytecode matches modulo CBOR metadata).", + "line_ranges": [ + { + "file": "contracts/EVMScriptFactories/ReportWithdrawalsForSlashedValidators.sol", + "github": { + "start": 6, + "count": 4 + }, + "explorer": { + "start": 6, + "count": 4 + } + } + ] + } + ], + "0xd0c38B2F0C1F760976dA010C1c35D828331Ff9E2": [ + { + "reason": "Import-path differences only: the GitHub source uses relative imports (../, ./) while the Etherscan verified source rewrote them to absolute paths (contracts/...). Same files resolved; no executable-logic change (runtime bytecode matches modulo CBOR metadata).", + "line_ranges": [ + { + "file": "contracts/EVMScriptFactories/SettleGeneralDelayedPenalty.sol", + "github": { + "start": 6, + "count": 5 + }, + "explorer": { + "start": 6, + "count": 5 + } + } + ] + } + ], + "0x5194cC02B6F477B4a23DFA422fFC238c8B5b1736": [ + { + "reason": "Import-path differences only: the GitHub source uses relative imports (../, ./) while the Etherscan verified source rewrote them to absolute paths (contracts/...). Same files resolved; no executable-logic change (runtime bytecode matches modulo CBOR metadata).", + "line_ranges": [ + { + "file": "contracts/EVMScriptFactories/SetMerkleGateTree.sol", + "github": { + "start": 6, + "count": 4 + }, + "explorer": { + "start": 6, + "count": 4 + } + } + ] + } + ], + "0x6E40FED7c28bAA93a798cA10f8A93965a19eC52e": [ + { + "reason": "Import-path differences only: the GitHub source uses relative imports (../, ./) while the Etherscan verified source rewrote them to absolute paths (contracts/...). Same files resolved; no executable-logic change (runtime bytecode matches modulo CBOR metadata).", + "line_ranges": [ + { + "file": "contracts/EVMScriptFactories/ReportWithdrawalsForSlashedValidators.sol", + "github": { + "start": 6, + "count": 4 + }, + "explorer": { + "start": 6, + "count": 4 + } + } + ] + } + ], + "0x3486B872768D361309e405A046C4BF995c21CC6c": [ + { + "reason": "Import-path differences only: the GitHub source uses relative imports (../, ./) while the Etherscan verified source rewrote them to absolute paths (contracts/...). Same files resolved; no executable-logic change (runtime bytecode matches modulo CBOR metadata).", + "line_ranges": [ + { + "file": "contracts/EVMScriptFactories/SettleGeneralDelayedPenalty.sol", + "github": { + "start": 6, + "count": 5 + }, + "explorer": { + "start": 6, + "count": 5 + } + } + ] + } + ], + "0x44D9b39bBdc2182Aa1af6f16f8F55E0eA038294d": [ + { + "reason": "Import-path differences only: the GitHub source uses relative imports (../, ./) while the Etherscan verified source rewrote them to absolute paths (contracts/...). Same files resolved; no executable-logic change (runtime bytecode matches modulo CBOR metadata).", + "line_ranges": [ + { + "file": "contracts/EVMScriptFactories/CreateOrUpdateOperatorGroup.sol", + "github": { + "start": 6, + "count": 7 + }, + "explorer": { + "start": 6, + "count": 7 + } + }, + { + "file": "contracts/interfaces/ICuratedModule.sol", + "github": { + "start": 6, + "count": 2 + }, + "explorer": { + "start": 6, + "count": 2 + } + } + ] + } + ] + } } } From 73c09337acc62bd57d0a6410ad5e77e5137c70cb Mon Sep 17 00:00:00 2001 From: Eddort Date: Fri, 26 Jun 2026 17:47:15 +0200 Subject: [PATCH 10/12] fix: improve contract address validation in deployment configuration --- .github/workflows/regression.yml | 25 ------------------------- diffyscan/diffyscan.py | 13 +++++++------ 2 files changed, 7 insertions(+), 31 deletions(-) diff --git a/.github/workflows/regression.yml b/.github/workflows/regression.yml index 4b5161cc..93ac978f 100644 --- a/.github/workflows/regression.yml +++ b/.github/workflows/regression.yml @@ -45,35 +45,10 @@ jobs: network: hoodi - config: config_samples/ethereum/hoodi/vaults/hoodi_vaults_easy_track_config.json network: hoodi - flags: > - --allow-source-diff 0xfEF8B796Fea42b3C68E342364Adcf88F1d6145a6 - --allow-source-diff 0x56Ff87F41a8CF795764E15E496124240Ac17695b - --allow-source-diff 0x4DF806111AC58e93d90E6D2fBE8522a76be6F499 - --allow-source-diff 0xF21f98cac0Ba38f02b4d5be1667cc345929E8877 - --allow-source-diff 0xBC2bb8310730F3D2b514Cb26f7e0A8776De879Ac - --allow-source-diff 0xc3FA83D65a900303e1d99cDBBF762c6630562c04 - --allow-source-diff 0x351426775c75aB5127de860Cdcaf1953F1D622a2 - --allow-source-diff 0x83DfE5Fe8ac8b7DB38c020F4F54BF09b65D92c63 - --allow-source-diff 0xa11906bBBBaC5207b8FDA4F7F294d7EcB8dcc758 - --allow-source-diff 0xc5dCd2A9642ceA9B71A632BF5b8ff52424Ea1B40 - config: config_samples/ethereum/hoodi/srv3/hoodi_srv3_config.json network: hoodi - flags: > - --allow-bytecode-diff 0x1a629bB7C0563650e46406Eb6764A2ba207a0eFE - --allow-source-diff 0x44d0b2B95d2C2bDF73FE4f5cD7E3A930494E5B1f - --allow-source-diff 0x6147270470A9Ee5b55c33EA71e32000E5d6D8E6B - config: config_samples/ethereum/hoodi/srv3/hoodi_srv3_easy_track_config.json network: hoodi - flags: > - --allow-source-diff 0x22D36e7616F541A527989C5652fDA4d527bB461C - --allow-source-diff 0xD63cf25df1bA6144db27A81A98120Dfc53dE4540 - --allow-source-diff 0xf71fcB20B9FB8468653Bcb24E31F39bc069D5995 - --allow-source-diff 0x4EaB04775837A6F0218750A10454119f349258FE - --allow-source-diff 0xd0c38B2F0C1F760976dA010C1c35D828331Ff9E2 - --allow-source-diff 0x5194cC02B6F477B4a23DFA422fFC238c8B5b1736 - --allow-source-diff 0x6E40FED7c28bAA93a798cA10f8A93965a19eC52e - --allow-source-diff 0x3486B872768D361309e405A046C4BF995c21CC6c - --allow-source-diff 0x44D9b39bBdc2182Aa1af6f16f8F55E0eA038294d steps: - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 diff --git a/diffyscan/diffyscan.py b/diffyscan/diffyscan.py index d3b167e4..c1e4df83 100644 --- a/diffyscan/diffyscan.py +++ b/diffyscan/diffyscan.py @@ -555,13 +555,14 @@ def _get_deployment_from( 'Config key "bytecode_comparison.deployment_from" must be an object' ) - if contract_address not in deployment_from: - return None + for addr, caller in deployment_from.items(): + if addr.lower() == contract_address.lower(): + return _validate_config_address( + caller, + f"bytecode_comparison.deployment_from.{addr}", + ) - return _validate_config_address( - deployment_from[contract_address], - f"bytecode_comparison.deployment_from.{contract_address}", - ) + return None def _log_explorer_bytecode_metadata( From 825064a3c99c7a68671c0f55f624d6caa707171a Mon Sep 17 00:00:00 2001 From: Eddort Date: Fri, 26 Jun 2026 17:49:50 +0200 Subject: [PATCH 11/12] refactor: clarify comment on source path handling in _build_github_solc_input --- diffyscan/diffyscan.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/diffyscan/diffyscan.py b/diffyscan/diffyscan.py index c1e4df83..f94b8477 100644 --- a/diffyscan/diffyscan.py +++ b/diffyscan/diffyscan.py @@ -86,8 +86,7 @@ def _build_github_solc_input( updated_sources = {} missing = [] - # Extra sources first so they cannot be silently shadowed by an explorer - # entry of the same path; the explorer loop below skips already-fetched paths. + # Explorer source list plus any configured extras not already in it. paths = list(sources) + [ path for path in (extra_source_paths or []) if path not in sources ] From c606a6393ab98e4b56c4689695c815a4cbf68a7e Mon Sep 17 00:00:00 2001 From: Eddort Date: Fri, 26 Jun 2026 17:59:52 +0200 Subject: [PATCH 12/12] refactor: clarify comment on extra source handling in _build_github_solc_input --- diffyscan/diffyscan.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/diffyscan/diffyscan.py b/diffyscan/diffyscan.py index f94b8477..71e2f68c 100644 --- a/diffyscan/diffyscan.py +++ b/diffyscan/diffyscan.py @@ -86,7 +86,8 @@ def _build_github_solc_input( updated_sources = {} missing = [] - # Explorer source list plus any configured extras not already in it. + # Append extra sources after the explorer set, skipping paths already + # present so the same file isn't fetched twice. paths = list(sources) + [ path for path in (extra_source_paths or []) if path not in sources ]