Small script to identify unlinked Solidity libraries in a compiled artifact and optionally generate an Echidna config with the required linking information.
This CLI scans a Forge/standard-json artifact (for example, out/Contract.sol/Contract.json), finds library link placeholders (the
- Detects modern Solidity external library placeholders in bytecode:
$<34 hex>$ . - Computes the exact Keccak-256 that solc uses for ":".
- Robust path candidate generation for common layouts (Foundry, Hardhat, Truffle):
- Project-relative, remapped alias, stripped prefixes (node_modules/, lib/, src/, contracts/, packages/, test/, forge-std/).
- Absolute, realpath, and URI-like forms (file://, vfs://).
- Handles ./ and ../ relative imports and Foundry remappings.txt.
- Extracts actual library names from imported Solidity files when accessible.
- Optional Echidna config generation with library deployments and --compile-libraries args.
- Pure-Python Keccak-256 fallback (no native deps required). If available, uses eth-hash or pysha3.
This directory is a minimal Python package. You can install it locally with pip.
-
Using pip (editable): cd tools/identify-unlinked-libs pip install -e . Optionally install faster Keccak providers: pip install -e .[keccak]
-
Using pipx (recommended for a standalone CLI): cd tools/identify-unlinked-libs pipx install . Or with extras: pipx install .[keccak]
This will provide the identify-unlinked-libs command on your PATH.
-
Basic identification (project root is current directory by default): identify-unlinked-libs out/Contract.sol/Contract.json .
-
Print a directory tree to help diagnose remappings/paths: identify-unlinked-libs --generate-tree -- out/Contract.sol/Contract.json .
-
Generate an Echidna config if all libraries are matched: identify-unlinked-libs out/Contract.sol/Contract.json . --output-config echidna.config.yaml
If some libraries are unmatched, the tool will warn and skip config generation.
forge build
python identify-unlinked-libs out/WakuRlnV2.sol/WakuRlnV2.json . --output-config echidna.config.yaml
Expected: placeholders for LazyIMT and PoseidonT3 map to something like:
- node_modules/@zk-kit/imt.sol/contracts/LazyIMT.sol:LazyIMT
- node_modules/poseidon-solidity/PoseidonT3.sol:PoseidonT3
- Placeholder format: Targets the modern scheme
$<34 hex>$ (Solidity 0.4.24+). Legacy padded placeholders like LibName___________________ are not currently handled. - Bytecode shape: Expects Foundry/standard-json artifacts (bytecode.object or deployedBytecode.object). If you use Hardhat-only artifacts with a top-level bytecode string, you may need to compile with Foundry (forge build) or adjust the script to read that field.
- Canonical paths: Extremely unusual solc source keys (for example, custom URIs) not covered by our variants may need small additions.
- Monorepos/hoisted deps: If dependencies live outside the project root, extracting real library names from source files may fail and we fall back to the filename as the library name.
- Hex case: The current regex expects lowercase hex in placeholders. If your toolchain emits uppercase, extend the regex to [A-Fa-f0-9].
- Generate a tree to visualize the layout: identify-unlinked-libs --generate-tree -- out/...json .
- Check Foundry remappings: ensure remappings.txt contains the aliases used in imports.
- Confirm the project actually uses external libraries (not fully inlined):
grep -R "
$[A-Fa-f0-9]{34}$ " -n out/ || true - Install optional keccak dependencies for speed: pip install .[keccak]
Distributed under the MIT license. See the repository's LICENSE.