Skip to content

Support for Hardhat 3 #629

@imadarchid

Description

@imadarchid

Currently, crytic-compile fails in hardhat 3 projects. One easy way to re-create this is to create two Hardhat starter projects (one in v2 and the other in v3).

Potential cause

On compilation, hardhat v2 produced one file under build-info with the "output" parameter. Hardhat v3 introduces a new way to generate build info with a different format.

  • Hardhat 2 produces a single artifacts/build-info/.json that mixed compiler input and output. Hardhat 3 keeps input (+ metadata) in solc-….json and writes the actual compiler output to a sibling solc-….output.json. The main file now declares _format: "hh3-sol-build-info-1".

  • In Hardhat 2 the top-level object had { _format: "hh-sol-build-info-1", id, solcVersion, input, output }. Version 3 removes output entirely, adds the hh3… format tag, and expects consumers to read the separate *.output.json

This leads to crytic-compile throwing the following error:

Traceback (most recent call last):
 File "/root/.crytic/bin/crytic-compile", line 8, in <module>
   sys.exit(main())
 File "/root/.crytic/lib/python3.10/site-packages/crytic_compile/__main__.py", line 220, in main
   compilations = compile_all(**vars(args))
 File "/root/.crytic/lib/python3.10/site-packages/crytic_compile/crytic_compile.py", line 722, in compile_all
   compilations.append(CryticCompile(target, **kwargs))
 File "/root/.crytic/lib/python3.10/site-packages/crytic_compile/crytic_compile.py", line 211, in __init__
   self._compile(**kwargs)
 File "/root/.crytic/lib/python3.10/site-packages/crytic_compile/crytic_compile.py", line 633, in _compile
   self._platform.compile(self, **kwargs)
 File "/root/.crytic/lib/python3.10/site-packages/crytic_compile/platform/hardhat.py", line 184, in compile
   hardhat_like_parsing(crytic_compile, self._target, build_directory, hardhat_working_dir)
 File "/root/.crytic/lib/python3.10/site-packages/crytic_compile/platform/hardhat.py", line 72, in hardhat_like_parsing
   targets_json = loaded_json["output"]
KeyError: 'output'

Potential remediation

  • Read both build-info JSONs instead if we detect that the project is run using hardhat 3. Changes need to happen at the parser level.

for file in files:
build_info = Path(build_directory, file)
# The file here should always ends .json, but just in case use ife
uniq_id = file if ".json" not in file else file[0:-5]
compilation_unit = CompilationUnit(crytic_compile, uniq_id)
with open(build_info, encoding="utf8") as file_desc:
loaded_json = json.load(file_desc)
targets_json = loaded_json["output"]
version_from_config = loaded_json["solcVersion"] # TODO supper vyper
input_json = loaded_json["input"]
compiler = "solc" if input_json["language"] == "Solidity" else "vyper"
# Foundry has the optimizer dict empty when the "optimizer" key is not set in foundry.toml
optimized = input_json["settings"]["optimizer"].get("enabled", False)
compilation_unit.compiler_version = CompilerVersion(
compiler=compiler, version=version_from_config, optimized=optimized
)
skip_filename = compilation_unit.compiler_version.version in [
f"0.4.{x}" for x in range(0, 10)
]
if "sources" in targets_json:
for path, info in targets_json["sources"].items():
if skip_filename:
path = convert_filename(
target,
relative_to_short,
crytic_compile,
working_dir=working_dir,
)
else:
path = convert_filename(
path,
relative_to_short,
crytic_compile,
working_dir=working_dir,
)
source_unit = compilation_unit.create_source_unit(path)
source_unit.ast = info.get("ast", info.get("legacyAST"))
if source_unit.ast is None:
raise InvalidCompilation(
f"AST not found for {path} in {build_info} directory"
)
if "contracts" in targets_json:

Am I missing anything? Otherwise I can take a stab at fixing this.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions