Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: use target to get config, drop toml for json #515

Merged
merged 9 commits into from
Jan 16, 2024
Merged
104 changes: 49 additions & 55 deletions crytic_compile/platform/foundry.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
from pathlib import Path
from typing import TYPE_CHECKING, List, Optional, Dict, TypeVar

import toml
import json

from crytic_compile.platform.abstract_platform import AbstractPlatform, PlatformConfig
from crytic_compile.platform.types import Type
Expand Down Expand Up @@ -63,7 +63,7 @@ def compile(self, crytic_compile: "CryticCompile", **kwargs: str) -> None:
compile_all = kwargs.get("foundry_compile_all", False)

if not compile_all:
foundry_config = self.config(str(crytic_compile.working_dir.absolute()))
foundry_config = self.config(self._target)
if foundry_config:
compilation_command += [
"--skip",
Expand Down Expand Up @@ -118,68 +118,62 @@ def is_supported(target: str, **kwargs: str) -> bool:
return os.path.isfile(os.path.join(target, "foundry.toml"))

@staticmethod
def config(working_dir: str) -> Optional[PlatformConfig]:
def config(target: str) -> Optional[PlatformConfig]:
"""Return configuration data that should be passed to solc, such as remappings.

Args:
working_dir (str): path to the working directory
target (str): path to the target

Returns:
Optional[PlatformConfig]: Platform configuration data such as optimization, remappings...
"""
result = PlatformConfig()
result.remappings = (
subprocess.run(["forge", "remappings"], stdout=subprocess.PIPE, check=True)
.stdout.decode("utf-8")
.replace("\n", " ")
.strip()
LOGGER.info("'forge config --json' running")
json_config = json.loads(
subprocess.run(["forge", "config", "--json"], stdout=subprocess.PIPE, check=True).stdout
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should run with cwd=target or something like that, right? Otherwise you may get the foundry config from the current directory, which may not work or may not correspond to your target. If target can be a file, then you may need to preprocess it a bit:

target = target if os.path.isdir(target) else os.path.dirname(target)
subprocess.run(..., cwd=target)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On a second look, I think target is always a directory here, so scratch that last part of my comment.

Unrelated to this PR, but I think the check here may be a bit off, wouldn't it make more sense to look for the foundry/etc project in the path of the file rather than in cwd? e.g. if the file is /a/b/c/d/contracts/foo.sol, I'd check to see if any of [/a/b/c/d/contracts, /a/b/c/d, /a/b/c, /a/b, /a, /] is a project. Most users are probably going to be running the tool with cwd=the project folder, so it may work as-is now, but it doesn't feel completely correct.

)
with open("foundry.toml", "r", encoding="utf-8") as f:
foundry_toml = toml.loads(f.read())
default_profile = foundry_toml["profile"]["default"]

def lookup_by_keys(keys: List[str], dictionary: Dict[str, T]) -> Optional[T]:
for key in keys:
if key in dictionary:
return dictionary[key]
return None

# Foundry supports snake and kebab case.
result.solc_version = lookup_by_keys(
["solc", "solc_version", "solc-version"], default_profile
)
via_ir = lookup_by_keys(["via_ir", "via-ir"], default_profile)
if via_ir:
result.via_ir = via_ir
result.allow_paths = lookup_by_keys(["allow_paths", "allow-paths"], default_profile)

if "offline" in default_profile:
result.offline = default_profile["offline"]
if "optimizer" in default_profile:
result.optimizer = default_profile["optimizer"]
else:
# Default to true
result.optimizer = True
optimizer_runs = lookup_by_keys(["optimizer_runs", "optimizer-runs"], default_profile)
if optimizer_runs is None:
# Default to 200
result.optimizer_runs = 200
else:
result.optimizer_runs = optimizer_runs
evm_version = lookup_by_keys(["evm_version", "evm-version"], default_profile)
if evm_version is None:
result.evm_version = evm_version
else:
# Default to london
result.evm_version = "london"
if "src" in default_profile:
result.src_path = default_profile["src"]
if "test" in default_profile:
result.tests_path = default_profile["test"]
if "libs" in default_profile:
result.libs_path = default_profile["libs"]
if "script" in default_profile:
result.scripts_path = default_profile["script"]
result.remappings = json_config["remappings"]

def lookup_by_keys(keys: List[str], dictionary: Dict[str, T]) -> Optional[T]:
for key in keys:
if key in dictionary:
return dictionary[key]
return None

# Foundry supports snake and kebab case.
result.solc_version = lookup_by_keys(["solc", "solc_version", "solc-version"], json_config)
via_ir = lookup_by_keys(["via_ir", "via-ir"], json_config)
if via_ir:
result.via_ir = via_ir
result.allow_paths = lookup_by_keys(["allow_paths", "allow-paths"], json_config)

if "offline" in json_config:
result.offline = json_config["offline"]
if "optimizer" in json_config:
result.optimizer = json_config["optimizer"]
else:
# Default to true
result.optimizer = True
optimizer_runs = lookup_by_keys(["optimizer_runs", "optimizer-runs"], json_config)
if optimizer_runs is None:
# Default to 200
result.optimizer_runs = 200
else:
result.optimizer_runs = optimizer_runs
evm_version = lookup_by_keys(["evm_version", "evm-version"], json_config)
if evm_version is None:
result.evm_version = evm_version
else:
# Default to london
result.evm_version = "london"
if "src" in json_config:
result.src_path = json_config["src"]
if "test" in json_config:
result.tests_path = json_config["test"]
if "libs" in json_config:
result.libs_path = json_config["libs"]
if "script" in json_config:
result.scripts_path = json_config["script"]

return result

Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
version="0.3.4",
packages=find_packages(),
python_requires=">=3.8",
install_requires=["pycryptodome>=3.4.6", "cbor2", "solc-select>=v1.0.4", "toml>=0.10.2"],
install_requires=["pycryptodome>=3.4.6", "cbor2", "solc-select>=v1.0.4"],
extras_require={
"test": [
"pytest",
Expand Down