---
name: debug-diff
description: Debug a failed diffyscan verification run. Analyzes diffs, identifies root causes. Use when diffyscan exits with non-zero or shows unexpected diffs.
argument-hint: [config-path-or-contract-address]
---

Help debug a failed diffyscan verification. The user may provide a config path, contract address, or describe the failure.

## Diagnostic steps

### 1. Check the digest output
Each run creates a timestamped directory under `digest/`. The timestamp is `int(time.time())` captured at process start (see `diffyscan/utils/constants.py`):
- `digest/{timestamp}/logs.txt` -- full run log (written by `Logger` in `diffyscan/utils/logger.py`)
- `digest/{timestamp}/diffs/{contract_address}/{filename}.html` -- per-file HTML source diff reports

Find the most recent run:
```bash
ls -lt digest/ | grep "^d" | head -5
```

Then inspect its contents:
```bash
# View the log
cat digest/<timestamp>/logs.txt

# List HTML diff reports for a specific contract
ls digest/<timestamp>/diffs/<contract_address>/
```

### 2. Identify the type of failure

**Source code diffs** -- files differ between GitHub and the blockchain explorer:
- Check if the `commit` in `github_repo` matches what was actually deployed
- Check if `relative_root` is correct (sources may live in a subdirectory of the repo)
- Check if entries in `dependencies` have the right `commit` hashes and `relative_root` paths
- Look for import path mismatches (flat vs nested -- may need `--support-brownie` for brownie-verified contracts)
- Open the HTML diff files (`digest/{timestamp}/diffs/{address}/*.html`) to see exactly which lines differ
- The report table in logs shows columns: `#`, `Filename`, `Found`, `Diffs`, `Origin`, `Report`

**Bytecode diffs** -- compiled bytecode does not match on-chain:
- Missing or wrong constructor arguments -- see `constructor_calldata` or `constructor_args` under the `bytecode_comparison` config key
- Missing libraries -- check if the contract uses external libraries that need addresses in `bytecode_comparison.libraries`
- Wrong EVM version -- the explorer may report a different version than expected
- Immutable variables -- `deep_match_bytecode()` in `diffyscan/utils/binary_verifier.py` compares instruction-by-instruction and tolerates differences that fall within known immutable reference regions. If all diffs are in immutable positions it logs a warning and returns `False` (still reported as a non-match — use `--allow-bytecode-diff 0xAddr` to accept). Differences outside immutable regions raise `BinVerifierError`, which `process_config` catches and records as a non-match (`match=False`) — it does not abort the run
- Optimizer settings mismatch -- the solcInput from the explorer includes optimizer settings; the GitHub recompilation must match
- Flat-source contracts -- see **Known limitations** section below

**Compilation errors** (raised as `CompileError`):
- Missing GitHub sources -- a dependency is not configured or has a wrong `relative_root`. The error message is: `"missing GitHub sources for bytecode compilation; count=N; first=path1, path2..."` (from `run_bytecode_diff()` in `diffyscan/diffyscan.py`)
- Solc version mismatch -- check that the compiler version exists for the platform (solc binaries are cached in `~/.cache/diffyscan/solc/` or equivalent via `XDG_CACHE_HOME`)

**Constructor calldata errors** (raised as `CalldataError` from `diffyscan/utils/calldata.py`):
- `"No constructor calldata found for 0x... (not in config and not in explorer metadata)"` -- need to add `constructor_calldata` or `constructor_args` to the config
- `"Contract 0x... found in both 'constructor_args' and 'constructor_calldata'"` -- only one should be specified per contract

**Network/API errors**:
- Explorer API rate limiting -- try `--cache-explorer` to reuse cached responses (cached in `.diffyscan_cache/`)
- RPC errors -- check that `REMOTE_RPC_URL` env var is valid and the node supports `eth_call`
- Explorer token missing -- the config key `explorer_token_env_var` names the env var holding the API token; if absent, falls back to `ETHERSCAN_EXPLORER_TOKEN`

### 3. Common fixes

| Symptom | Likely fix |
|---------|-----------|
| `"missing GitHub sources for bytecode compilation"` | Add the missing dependency to `dependencies` in the config with correct `url`, `commit`, and `relative_root` |
| Source diffs in OpenZeppelin or other dependency imports | Check the dependency `commit` hash matches what was used at deploy time |
| Bytecode mismatch after constructor | Add `constructor_calldata` (raw hex) or `constructor_args` (ABI-typed values) for the contract under `bytecode_comparison` |
| `"Failed to infer source path for library '...' from explorer metadata"` | Add library addresses to `bytecode_comparison.libraries` keyed by `"path/to/File.sol": {"LibName": "0xAddr"}` |
| All files show diffs | Wrong `commit` or `relative_root` in `github_repo` |
| Single contract fails | May need a per-contract `constructor_calldata` or `constructor_args` entry |
| `"Failed in binary comparison: Bytecodes have differences not on the immutable reference position"` | Real bytecode mismatch -- check compiler version, optimizer settings, EVM version, and library addresses |
| `"Exiting with non-zero code due to unallowed diffs"` | Either fix the diffs or use `--allow-source-diff 0xAddr` / `--allow-bytecode-diff 0xAddr` for known acceptable diffs |
| `"Contract name in config does not match with blockchain explorer ... !="` | Contract not verified on explorer — comment it out or verify it on the explorer first |
| `"Failed to get calldata: Explorer metadata has empty constructor calldata for 0x..."` | Factory-created contract — Etherscan has no constructor args. Add `constructor_calldata` manually (extract via `getsourcecode` API `ConstructorArguments`, `debug_traceTransaction` trace, or cross-chain reuse) |
| `"HTTP error: 404 ... contents/<path>.sol"` | Missing or wrong `dependencies` entry — the explorer source uses a path prefix not covered by config dependencies. Add a mapping for that prefix |
| `"err: intrinsic gas too low"` | Chain gas model needs a higher deployment gas cap (known on Mantle) — set `deployment_gas_limit` in the config (e.g. `30000000000`) |
| All proxy bytecode diffs say "immutable reference position" | Normal for TransparentUpgradeableProxy — ProxyAdmin address baked in as immutable. Use `--allow-bytecode-diff` or accept as expected |

### 4. Suggest a re-run command
After fixing, suggest:
```bash
uv run diffyscan <config-path> --yes --cache-explorer --cache-github
```

- `--yes` (`-Y`) skips the interactive prompt before each contract
- `--cache-explorer` (`-E`) reuses cached explorer responses from `.diffyscan_cache/`
- `--cache-github` (`-G`) reuses cached GitHub file fetches
- `--support-brownie` enables recursive retrieval for brownie-verified contracts with flattened import paths
- `--allow-source-diff 0xAddr` accepts source diffs for a specific address (can be repeated)
- `--allow-bytecode-diff 0xAddr` accepts bytecode diffs for a specific address (can be repeated)

## Known limitations

If any of these apply, **explicitly tell the user** — these are inherent constraints of the tool, not bugs to debug.

### Flat-source (non-standard-JSON) contracts

When a contract was verified on the explorer as a single flattened file (the explorer's `SourceCode` is a plain string, not JSON wrapped in `{{}}`), diffyscan **cannot reliably reproduce the bytecode**. Two problems arise:

1. **Lost compiler settings** — diffyscan reconstructs a minimal solc input with only basic optimizer settings. Remappings, via-IR, and other original compiler settings are not available from the explorer for flat-verified contracts. Bytecode mismatches are **expected** even when source diffs are clean.
2. **Contract name used as source key** — the explorer's `ContractName` field is used as the solc source file key (e.g. `{"sources": {"MyContract": {...}}}`). If the explorer does not return `ContractName`, or the name does not match the actual `contract` declaration in the Solidity source, the compiled output cannot be remapped and `get_target_compiled_contract()` will fail.

**How to detect**: check the explorer response or logs — if the solc input has a source key that looks like a contract name (no `.sol` extension, no path separators) rather than a file path, the contract was flat-verified.

**What to tell the user**: explain that bytecode comparison is unreliable for this contract due to the flat-source limitation. Recommend using `--allow-bytecode-diff 0xAddr` to accept the mismatch, and note that source comparison is still valid.
