|
7 | 7 | import shutil
|
8 | 8 | import subprocess
|
9 | 9 | from pathlib import Path
|
10 |
| -from typing import TYPE_CHECKING, List |
| 10 | +from typing import TYPE_CHECKING, Dict, List, Optional, Union |
11 | 11 |
|
12 | 12 | from crytic_compile.compiler.compiler import CompilerVersion
|
13 | 13 | from crytic_compile.platform.exceptions import InvalidCompilation
|
@@ -52,16 +52,20 @@ def compile(self, crytic_compile: "CryticCompile", **kwargs: str) -> None:
|
52 | 52 | "ignore_compile", False
|
53 | 53 | )
|
54 | 54 |
|
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 |
| - |
61 | 55 | base_cmd = ["hardhat"]
|
62 | 56 | if not kwargs.get("npx_disable", False):
|
63 | 57 | base_cmd = ["npx"] + base_cmd
|
64 | 58 |
|
| 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 | + |
65 | 69 | if not hardhat_ignore_compile:
|
66 | 70 | cmd = base_cmd + ["compile", "--force"]
|
67 | 71 |
|
@@ -217,3 +221,76 @@ def _guessed_tests(self) -> List[str]:
|
217 | 221 | List[str]: The guessed unit tests commands
|
218 | 222 | """
|
219 | 223 | return ["hardhat test"]
|
| 224 | + |
| 225 | + def _get_hardhat_paths( |
| 226 | + self, base_cmd: List[str], args: Dict[str, str] |
| 227 | + ) -> Dict[str, Union[Path, str]]: |
| 228 | + """Obtain hardhat configuration paths, defaulting to the |
| 229 | + standard config if needed. |
| 230 | +
|
| 231 | + Args: |
| 232 | + base_cmd ([str]): hardhat command |
| 233 | + args (Dict[str, str]): crytic-compile options that may affect paths |
| 234 | +
|
| 235 | + Returns: |
| 236 | + Dict[str, str]: hardhat paths configuration |
| 237 | + """ |
| 238 | + target_path = Path(self._target) |
| 239 | + default_paths = { |
| 240 | + "root": target_path, |
| 241 | + "configFile": target_path.joinpath("hardhat.config.js"), |
| 242 | + "sources": target_path.joinpath("contracts"), |
| 243 | + "cache": target_path.joinpath("cache"), |
| 244 | + "artifacts": target_path.joinpath("artifacts"), |
| 245 | + "tests": target_path.joinpath("test"), |
| 246 | + } |
| 247 | + override_paths = {} |
| 248 | + |
| 249 | + if args.get("hardhat_cache_directory", None): |
| 250 | + override_paths["cache"] = Path(target_path, args["hardhat_cache_directory"]) |
| 251 | + |
| 252 | + if args.get("hardhat_artifacts_directory", None): |
| 253 | + override_paths["artifacts"] = Path(target_path, args["hardhat_artifacts_directory"]) |
| 254 | + |
| 255 | + if args.get("hardhat_working_dir", None): |
| 256 | + override_paths["root"] = Path(target_path, args["hardhat_working_dir"]) |
| 257 | + |
| 258 | + print_paths = "console.log(JSON.stringify(config.paths))" |
| 259 | + config_str = self._run_hardhat_console(base_cmd, print_paths) |
| 260 | + |
| 261 | + try: |
| 262 | + paths = json.loads(config_str or "{}") |
| 263 | + return {**default_paths, **paths, **override_paths} |
| 264 | + except ValueError as e: |
| 265 | + LOGGER.info("Problem deserializing hardhat configuration: %s", e) |
| 266 | + return {**default_paths, **override_paths} |
| 267 | + |
| 268 | + def _run_hardhat_console(self, base_cmd: List[str], command: str) -> Optional[str]: |
| 269 | + """Run a JS command in the hardhat console |
| 270 | +
|
| 271 | + Args: |
| 272 | + base_cmd ([str]): hardhat command |
| 273 | + command (str): console command to run |
| 274 | +
|
| 275 | + Returns: |
| 276 | + Optional[str]: command output if execution succeeds |
| 277 | + """ |
| 278 | + with subprocess.Popen( |
| 279 | + base_cmd + ["console", "--no-compile"], |
| 280 | + stdin=subprocess.PIPE, |
| 281 | + stdout=subprocess.PIPE, |
| 282 | + stderr=subprocess.PIPE, |
| 283 | + cwd=self._target, |
| 284 | + executable=shutil.which(base_cmd[0]), |
| 285 | + ) as process: |
| 286 | + stdout_bytes, stderr_bytes = process.communicate(command.encode("utf-8")) |
| 287 | + stdout, stderr = ( |
| 288 | + stdout_bytes.decode(), |
| 289 | + stderr_bytes.decode(), |
| 290 | + ) |
| 291 | + |
| 292 | + if stderr: |
| 293 | + LOGGER.info("Problem executing hardhat: %s", stderr) |
| 294 | + return None |
| 295 | + |
| 296 | + return stdout |
0 commit comments