Skip to content

Commit 19b2f85

Browse files
authored
Merge pull request #311 from crytic/dev-hardhat-improvements
hardhat: auto-detect paths
2 parents e53d03c + 9520549 commit 19b2f85

File tree

2 files changed

+86
-9
lines changed

2 files changed

+86
-9
lines changed

crytic_compile/cryticparser/defaults.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,8 @@
4040
"buidler_cache_directory": "cache",
4141
"buidler_skip_directory_name_fix": False,
4242
"hardhat_ignore_compile": False,
43-
"hardhat_cache_directory": "cache",
44-
"hardhat_artifacts_directory": "artifacts",
43+
"hardhat_cache_directory": None,
44+
"hardhat_artifacts_directory": None,
4545
"foundry_ignore_compile": False,
4646
"foundry_out_directory": "out",
4747
"export_dir": "crytic-export",

crytic_compile/platform/hardhat.py

Lines changed: 84 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
import shutil
88
import subprocess
99
from pathlib import Path
10-
from typing import TYPE_CHECKING, List
10+
from typing import TYPE_CHECKING, Dict, List, Optional, Union
1111

1212
from crytic_compile.compiler.compiler import CompilerVersion
1313
from crytic_compile.platform.exceptions import InvalidCompilation
@@ -52,16 +52,20 @@ def compile(self, crytic_compile: "CryticCompile", **kwargs: str) -> None:
5252
"ignore_compile", False
5353
)
5454

55-
build_directory = Path(
56-
self._target, kwargs.get("hardhat_artifacts_directory", "artifacts"), "build-info"
57-
)
58-
59-
hardhat_working_dir = kwargs.get("hardhat_working_dir", self._target)
60-
6155
base_cmd = ["hardhat"]
6256
if not kwargs.get("npx_disable", False):
6357
base_cmd = ["npx"] + base_cmd
6458

59+
detected_paths = self._get_hardhat_paths(base_cmd, kwargs)
60+
61+
build_directory = Path(
62+
self._target,
63+
detected_paths["artifacts"],
64+
"build-info",
65+
)
66+
67+
hardhat_working_dir = Path(self._target, detected_paths["root"])
68+
6569
if not hardhat_ignore_compile:
6670
cmd = base_cmd + ["compile", "--force"]
6771

@@ -216,3 +220,76 @@ def _guessed_tests(self) -> List[str]:
216220
List[str]: The guessed unit tests commands
217221
"""
218222
return ["hardhat test"]
223+
224+
def _get_hardhat_paths(
225+
self, base_cmd: List[str], args: Dict[str, str]
226+
) -> Dict[str, Union[Path, str]]:
227+
"""Obtain hardhat configuration paths, defaulting to the
228+
standard config if needed.
229+
230+
Args:
231+
base_cmd ([str]): hardhat command
232+
args (Dict[str, str]): crytic-compile options that may affect paths
233+
234+
Returns:
235+
Dict[str, str]: hardhat paths configuration
236+
"""
237+
target_path = Path(self._target)
238+
default_paths = {
239+
"root": target_path,
240+
"configFile": target_path.joinpath("hardhat.config.js"),
241+
"sources": target_path.joinpath("contracts"),
242+
"cache": target_path.joinpath("cache"),
243+
"artifacts": target_path.joinpath("artifacts"),
244+
"tests": target_path.joinpath("test"),
245+
}
246+
override_paths = {}
247+
248+
if args.get("hardhat_cache_directory", None):
249+
override_paths["cache"] = Path(target_path, args["hardhat_cache_directory"])
250+
251+
if args.get("hardhat_artifacts_directory", None):
252+
override_paths["artifacts"] = Path(target_path, args["hardhat_artifacts_directory"])
253+
254+
if args.get("hardhat_working_dir", None):
255+
override_paths["root"] = Path(target_path, args["hardhat_working_dir"])
256+
257+
print_paths = "console.log(JSON.stringify(config.paths))"
258+
config_str = self._run_hardhat_console(base_cmd, print_paths)
259+
260+
try:
261+
paths = json.loads(config_str or "{}")
262+
return {**default_paths, **paths, **override_paths}
263+
except ValueError as e:
264+
LOGGER.info("Problem deserializing hardhat configuration: %s", e)
265+
return {**default_paths, **override_paths}
266+
267+
def _run_hardhat_console(self, base_cmd: List[str], command: str) -> Optional[str]:
268+
"""Run a JS command in the hardhat console
269+
270+
Args:
271+
base_cmd ([str]): hardhat command
272+
command (str): console command to run
273+
274+
Returns:
275+
Optional[str]: command output if execution succeeds
276+
"""
277+
with subprocess.Popen(
278+
base_cmd + ["console", "--no-compile"],
279+
stdin=subprocess.PIPE,
280+
stdout=subprocess.PIPE,
281+
stderr=subprocess.PIPE,
282+
cwd=self._target,
283+
executable=shutil.which(base_cmd[0]),
284+
) as process:
285+
stdout_bytes, stderr_bytes = process.communicate(command.encode("utf-8"))
286+
stdout, stderr = (
287+
stdout_bytes.decode(),
288+
stderr_bytes.decode(),
289+
)
290+
291+
if stderr:
292+
LOGGER.info("Problem executing hardhat: %s", stderr)
293+
return None
294+
295+
return stdout

0 commit comments

Comments
 (0)