crytic_compile.platform.dapp

  1"""
  2Dapp platform. https://github.com/dapphub/dapptools
  3"""
  4
  5import glob
  6import json
  7import logging
  8import os
  9import re
 10import shutil
 11import subprocess
 12from pathlib import Path
 13
 14# Cycle dependency
 15from typing import TYPE_CHECKING, List, Optional
 16
 17from crytic_compile.compilation_unit import CompilationUnit
 18from crytic_compile.compiler.compiler import CompilerVersion
 19from crytic_compile.platform.abstract_platform import AbstractPlatform
 20from crytic_compile.platform.types import Type
 21from crytic_compile.utils.naming import convert_filename, extract_name
 22
 23# Handle cycle
 24from crytic_compile.utils.natspec import Natspec
 25from crytic_compile.utils.subprocess import run
 26
 27if TYPE_CHECKING:
 28    from crytic_compile import CryticCompile
 29
 30LOGGER = logging.getLogger("CryticCompile")
 31
 32
 33class Dapp(AbstractPlatform):
 34    """
 35    Dapp class
 36    """
 37
 38    NAME = "Dapp"
 39    PROJECT_URL = "https://github.com/dapphub/dapptools"
 40    TYPE = Type.DAPP
 41
 42    # pylint: disable=too-many-locals
 43    def compile(self, crytic_compile: "CryticCompile", **kwargs: str) -> None:
 44        """Run the compilation
 45
 46        Args:
 47            crytic_compile (CryticCompile): Associated CryticCompile object
 48            **kwargs: optional arguments. Used: "dapp_ignore_compile", "ignore_compile"
 49        """
 50
 51        dapp_ignore_compile = kwargs.get("dapp_ignore_compile", False) or kwargs.get(
 52            "ignore_compile", False
 53        )
 54        directory = os.path.join(self._target, "out")
 55
 56        if not dapp_ignore_compile:
 57            _run_dapp(self._target)
 58
 59        compilation_unit = CompilationUnit(crytic_compile, str(self._target))
 60
 61        compilation_unit.compiler_version = _get_version(self._target)
 62
 63        optimized = False
 64
 65        with open(os.path.join(directory, "dapp.sol.json"), "r", encoding="utf8") as file_desc:
 66            targets_json = json.load(file_desc)
 67
 68            version: Optional[str] = None
 69            if "version" in targets_json:
 70                version = re.findall(r"\d+\.\d+\.\d+", targets_json["version"])[0]
 71
 72            for path, info in targets_json["sources"].items():
 73                path = convert_filename(
 74                    path, _relative_to_short, crytic_compile, working_dir=self._target
 75                )
 76                source_unit = compilation_unit.create_source_unit(path)
 77                source_unit.ast = info["ast"]
 78
 79            for original_filename, contracts_info in targets_json["contracts"].items():
 80
 81                filename = convert_filename(
 82                    original_filename, lambda x: x, crytic_compile, self._target
 83                )
 84
 85                source_unit = compilation_unit.create_source_unit(filename)
 86
 87                for original_contract_name, info in contracts_info.items():
 88                    if "metadata" in info:
 89                        metadata = json.loads(info["metadata"])
 90                        if (
 91                            "settings" in metadata
 92                            and "optimizer" in metadata["settings"]
 93                            and "enabled" in metadata["settings"]["optimizer"]
 94                        ):
 95                            optimized |= metadata["settings"]["optimizer"]["enabled"]
 96                    contract_name = extract_name(original_contract_name)
 97                    source_unit.add_contract_name(contract_name)
 98                    compilation_unit.filename_to_contracts[filename].add(contract_name)
 99
100                    source_unit.abis[contract_name] = info["abi"]
101                    source_unit.bytecodes_init[contract_name] = info["evm"]["bytecode"]["object"]
102                    source_unit.bytecodes_runtime[contract_name] = info["evm"]["deployedBytecode"][
103                        "object"
104                    ]
105                    source_unit.srcmaps_init[contract_name] = info["evm"]["bytecode"][
106                        "sourceMap"
107                    ].split(";")
108                    source_unit.srcmaps_runtime[contract_name] = info["evm"]["bytecode"][
109                        "sourceMap"
110                    ].split(";")
111                    userdoc = info.get("userdoc", {})
112                    devdoc = info.get("devdoc", {})
113                    natspec = Natspec(userdoc, devdoc)
114                    source_unit.natspec[contract_name] = natspec
115
116                    if version is None:
117                        metadata = json.loads(info["metadata"])
118                        version = re.findall(r"\d+\.\d+\.\d+", metadata["compiler"]["version"])[0]
119
120        compilation_unit.compiler_version = CompilerVersion(
121            compiler="solc", version=version, optimized=optimized
122        )
123
124    def clean(self, **kwargs: str) -> None:
125        """Clean compilation artifacts
126
127        Args:
128            **kwargs: optional arguments.
129        """
130
131        dapp_ignore_compile = kwargs.get("dapp_ignore_compile", False) or kwargs.get(
132            "ignore_compile", False
133        )
134        if dapp_ignore_compile:
135            return
136
137        run(["dapp", "clean"], cwd=self._target)
138
139    @staticmethod
140    def is_supported(target: str, **kwargs: str) -> bool:
141        """Check if the target is a dapp project
142
143        Args:
144            target (str): path to the target
145            **kwargs: optional arguments. Used: "dapp_ignore"
146
147        Returns:
148            bool: True if the target is a dapp project
149        """
150        dapp_ignore = kwargs.get("dapp_ignore", False)
151        if dapp_ignore:
152            return False
153        makefile = os.path.join(target, "Makefile")
154        if os.path.isfile(makefile):
155            with open(makefile, encoding="utf8") as file_desc:
156                txt = file_desc.read()
157                return "dapp " in txt
158        return False
159
160    def is_dependency(self, path: str) -> bool:
161        """Check if the path is a dependency (not supported for brownie)
162
163        Args:
164            path (str): path to the target
165
166        Returns:
167            bool: True if the target is a dependency
168        """
169        if path in self._cached_dependencies:
170            return self._cached_dependencies[path]
171        ret = "node_modules" in Path(path).parts
172        self._cached_dependencies[path] = ret
173        return "lib" in Path(path).parts
174
175    def _guessed_tests(self) -> List[str]:
176        """Guess the potential unit tests commands
177
178        Returns:
179            List[str]: The guessed unit tests commands
180        """
181        return ["dapp test"]
182
183
184def _run_dapp(target: str) -> None:
185    """Run the dapp compilation
186
187    Args:
188        target (str): path to the target
189
190    Raises:
191        InvalidCompilation: If dapp failed to run
192    """
193    # pylint: disable=import-outside-toplevel
194    from crytic_compile.platform.exceptions import InvalidCompilation
195
196    cmd = ["dapp", "build"]
197
198    try:
199        with subprocess.Popen(
200            cmd,
201            stdout=subprocess.PIPE,
202            stderr=subprocess.PIPE,
203            cwd=target,
204            executable=shutil.which(cmd[0]),
205        ) as process:
206            _, _ = process.communicate()
207    except OSError as error:
208        # pylint: disable=raise-missing-from
209        raise InvalidCompilation(error)
210
211
212def _get_version(target: str) -> CompilerVersion:
213    """Get the compiler version
214
215    Args:
216        target (str): path to the target
217
218    Returns:
219        CompilerVersion: compiler information
220    """
221    files = glob.glob(target + "/**/*meta.json", recursive=True)
222    version: Optional[str] = None
223    optimized = None
224    compiler = "solc"
225    for file in files:
226        if version is None:
227            with open(file, encoding="utf8") as file_desc:
228                config = json.load(file_desc)
229            if "compiler" in config:
230                if "version" in config["compiler"]:
231                    versions = re.findall(r"\d+\.\d+\.\d+", config["compiler"]["version"])
232                    if versions:
233                        version = versions[0]
234            if "settings" in config:
235                if "optimizer" in config["settings"]:
236                    if "enabled" in config["settings"]["optimizer"]:
237                        optimized = config["settings"]["optimizer"]["enabled"]
238
239    return CompilerVersion(compiler=compiler, version=version, optimized=optimized)
240
241
242def _relative_to_short(relative: Path) -> Path:
243    """Translate relative path to short (do nothing for brownie)
244
245    Args:
246        relative (Path): path to the target
247
248    Returns:
249        Path: Translated path
250    """
251    short = relative
252    try:
253        short = short.relative_to(Path("src"))
254    except ValueError:
255        try:
256            short = short.relative_to("lib")
257        except ValueError:
258            pass
259    return short
LOGGER = <Logger CryticCompile (WARNING)>
 34class Dapp(AbstractPlatform):
 35    """
 36    Dapp class
 37    """
 38
 39    NAME = "Dapp"
 40    PROJECT_URL = "https://github.com/dapphub/dapptools"
 41    TYPE = Type.DAPP
 42
 43    # pylint: disable=too-many-locals
 44    def compile(self, crytic_compile: "CryticCompile", **kwargs: str) -> None:
 45        """Run the compilation
 46
 47        Args:
 48            crytic_compile (CryticCompile): Associated CryticCompile object
 49            **kwargs: optional arguments. Used: "dapp_ignore_compile", "ignore_compile"
 50        """
 51
 52        dapp_ignore_compile = kwargs.get("dapp_ignore_compile", False) or kwargs.get(
 53            "ignore_compile", False
 54        )
 55        directory = os.path.join(self._target, "out")
 56
 57        if not dapp_ignore_compile:
 58            _run_dapp(self._target)
 59
 60        compilation_unit = CompilationUnit(crytic_compile, str(self._target))
 61
 62        compilation_unit.compiler_version = _get_version(self._target)
 63
 64        optimized = False
 65
 66        with open(os.path.join(directory, "dapp.sol.json"), "r", encoding="utf8") as file_desc:
 67            targets_json = json.load(file_desc)
 68
 69            version: Optional[str] = None
 70            if "version" in targets_json:
 71                version = re.findall(r"\d+\.\d+\.\d+", targets_json["version"])[0]
 72
 73            for path, info in targets_json["sources"].items():
 74                path = convert_filename(
 75                    path, _relative_to_short, crytic_compile, working_dir=self._target
 76                )
 77                source_unit = compilation_unit.create_source_unit(path)
 78                source_unit.ast = info["ast"]
 79
 80            for original_filename, contracts_info in targets_json["contracts"].items():
 81
 82                filename = convert_filename(
 83                    original_filename, lambda x: x, crytic_compile, self._target
 84                )
 85
 86                source_unit = compilation_unit.create_source_unit(filename)
 87
 88                for original_contract_name, info in contracts_info.items():
 89                    if "metadata" in info:
 90                        metadata = json.loads(info["metadata"])
 91                        if (
 92                            "settings" in metadata
 93                            and "optimizer" in metadata["settings"]
 94                            and "enabled" in metadata["settings"]["optimizer"]
 95                        ):
 96                            optimized |= metadata["settings"]["optimizer"]["enabled"]
 97                    contract_name = extract_name(original_contract_name)
 98                    source_unit.add_contract_name(contract_name)
 99                    compilation_unit.filename_to_contracts[filename].add(contract_name)
100
101                    source_unit.abis[contract_name] = info["abi"]
102                    source_unit.bytecodes_init[contract_name] = info["evm"]["bytecode"]["object"]
103                    source_unit.bytecodes_runtime[contract_name] = info["evm"]["deployedBytecode"][
104                        "object"
105                    ]
106                    source_unit.srcmaps_init[contract_name] = info["evm"]["bytecode"][
107                        "sourceMap"
108                    ].split(";")
109                    source_unit.srcmaps_runtime[contract_name] = info["evm"]["bytecode"][
110                        "sourceMap"
111                    ].split(";")
112                    userdoc = info.get("userdoc", {})
113                    devdoc = info.get("devdoc", {})
114                    natspec = Natspec(userdoc, devdoc)
115                    source_unit.natspec[contract_name] = natspec
116
117                    if version is None:
118                        metadata = json.loads(info["metadata"])
119                        version = re.findall(r"\d+\.\d+\.\d+", metadata["compiler"]["version"])[0]
120
121        compilation_unit.compiler_version = CompilerVersion(
122            compiler="solc", version=version, optimized=optimized
123        )
124
125    def clean(self, **kwargs: str) -> None:
126        """Clean compilation artifacts
127
128        Args:
129            **kwargs: optional arguments.
130        """
131
132        dapp_ignore_compile = kwargs.get("dapp_ignore_compile", False) or kwargs.get(
133            "ignore_compile", False
134        )
135        if dapp_ignore_compile:
136            return
137
138        run(["dapp", "clean"], cwd=self._target)
139
140    @staticmethod
141    def is_supported(target: str, **kwargs: str) -> bool:
142        """Check if the target is a dapp project
143
144        Args:
145            target (str): path to the target
146            **kwargs: optional arguments. Used: "dapp_ignore"
147
148        Returns:
149            bool: True if the target is a dapp project
150        """
151        dapp_ignore = kwargs.get("dapp_ignore", False)
152        if dapp_ignore:
153            return False
154        makefile = os.path.join(target, "Makefile")
155        if os.path.isfile(makefile):
156            with open(makefile, encoding="utf8") as file_desc:
157                txt = file_desc.read()
158                return "dapp " in txt
159        return False
160
161    def is_dependency(self, path: str) -> bool:
162        """Check if the path is a dependency (not supported for brownie)
163
164        Args:
165            path (str): path to the target
166
167        Returns:
168            bool: True if the target is a dependency
169        """
170        if path in self._cached_dependencies:
171            return self._cached_dependencies[path]
172        ret = "node_modules" in Path(path).parts
173        self._cached_dependencies[path] = ret
174        return "lib" in Path(path).parts
175
176    def _guessed_tests(self) -> List[str]:
177        """Guess the potential unit tests commands
178
179        Returns:
180            List[str]: The guessed unit tests commands
181        """
182        return ["dapp test"]

Dapp class

NAME: str = 'Dapp'
PROJECT_URL: str = 'https://github.com/dapphub/dapptools'
TYPE: crytic_compile.platform.types.Type = <Type.DAPP: 4>
def compile( self, crytic_compile: crytic_compile.crytic_compile.CryticCompile, **kwargs: str) -> None:
 44    def compile(self, crytic_compile: "CryticCompile", **kwargs: str) -> None:
 45        """Run the compilation
 46
 47        Args:
 48            crytic_compile (CryticCompile): Associated CryticCompile object
 49            **kwargs: optional arguments. Used: "dapp_ignore_compile", "ignore_compile"
 50        """
 51
 52        dapp_ignore_compile = kwargs.get("dapp_ignore_compile", False) or kwargs.get(
 53            "ignore_compile", False
 54        )
 55        directory = os.path.join(self._target, "out")
 56
 57        if not dapp_ignore_compile:
 58            _run_dapp(self._target)
 59
 60        compilation_unit = CompilationUnit(crytic_compile, str(self._target))
 61
 62        compilation_unit.compiler_version = _get_version(self._target)
 63
 64        optimized = False
 65
 66        with open(os.path.join(directory, "dapp.sol.json"), "r", encoding="utf8") as file_desc:
 67            targets_json = json.load(file_desc)
 68
 69            version: Optional[str] = None
 70            if "version" in targets_json:
 71                version = re.findall(r"\d+\.\d+\.\d+", targets_json["version"])[0]
 72
 73            for path, info in targets_json["sources"].items():
 74                path = convert_filename(
 75                    path, _relative_to_short, crytic_compile, working_dir=self._target
 76                )
 77                source_unit = compilation_unit.create_source_unit(path)
 78                source_unit.ast = info["ast"]
 79
 80            for original_filename, contracts_info in targets_json["contracts"].items():
 81
 82                filename = convert_filename(
 83                    original_filename, lambda x: x, crytic_compile, self._target
 84                )
 85
 86                source_unit = compilation_unit.create_source_unit(filename)
 87
 88                for original_contract_name, info in contracts_info.items():
 89                    if "metadata" in info:
 90                        metadata = json.loads(info["metadata"])
 91                        if (
 92                            "settings" in metadata
 93                            and "optimizer" in metadata["settings"]
 94                            and "enabled" in metadata["settings"]["optimizer"]
 95                        ):
 96                            optimized |= metadata["settings"]["optimizer"]["enabled"]
 97                    contract_name = extract_name(original_contract_name)
 98                    source_unit.add_contract_name(contract_name)
 99                    compilation_unit.filename_to_contracts[filename].add(contract_name)
100
101                    source_unit.abis[contract_name] = info["abi"]
102                    source_unit.bytecodes_init[contract_name] = info["evm"]["bytecode"]["object"]
103                    source_unit.bytecodes_runtime[contract_name] = info["evm"]["deployedBytecode"][
104                        "object"
105                    ]
106                    source_unit.srcmaps_init[contract_name] = info["evm"]["bytecode"][
107                        "sourceMap"
108                    ].split(";")
109                    source_unit.srcmaps_runtime[contract_name] = info["evm"]["bytecode"][
110                        "sourceMap"
111                    ].split(";")
112                    userdoc = info.get("userdoc", {})
113                    devdoc = info.get("devdoc", {})
114                    natspec = Natspec(userdoc, devdoc)
115                    source_unit.natspec[contract_name] = natspec
116
117                    if version is None:
118                        metadata = json.loads(info["metadata"])
119                        version = re.findall(r"\d+\.\d+\.\d+", metadata["compiler"]["version"])[0]
120
121        compilation_unit.compiler_version = CompilerVersion(
122            compiler="solc", version=version, optimized=optimized
123        )

Run the compilation

Args: crytic_compile (CryticCompile): Associated CryticCompile object **kwargs: optional arguments. Used: "dapp_ignore_compile", "ignore_compile"

def clean(self, **kwargs: str) -> None:
125    def clean(self, **kwargs: str) -> None:
126        """Clean compilation artifacts
127
128        Args:
129            **kwargs: optional arguments.
130        """
131
132        dapp_ignore_compile = kwargs.get("dapp_ignore_compile", False) or kwargs.get(
133            "ignore_compile", False
134        )
135        if dapp_ignore_compile:
136            return
137
138        run(["dapp", "clean"], cwd=self._target)

Clean compilation artifacts

Args: **kwargs: optional arguments.

@staticmethod
def is_supported(target: str, **kwargs: str) -> bool:
140    @staticmethod
141    def is_supported(target: str, **kwargs: str) -> bool:
142        """Check if the target is a dapp project
143
144        Args:
145            target (str): path to the target
146            **kwargs: optional arguments. Used: "dapp_ignore"
147
148        Returns:
149            bool: True if the target is a dapp project
150        """
151        dapp_ignore = kwargs.get("dapp_ignore", False)
152        if dapp_ignore:
153            return False
154        makefile = os.path.join(target, "Makefile")
155        if os.path.isfile(makefile):
156            with open(makefile, encoding="utf8") as file_desc:
157                txt = file_desc.read()
158                return "dapp " in txt
159        return False

Check if the target is a dapp project

Args: target (str): path to the target **kwargs: optional arguments. Used: "dapp_ignore"

Returns: bool: True if the target is a dapp project

def is_dependency(self, path: str) -> bool:
161    def is_dependency(self, path: str) -> bool:
162        """Check if the path is a dependency (not supported for brownie)
163
164        Args:
165            path (str): path to the target
166
167        Returns:
168            bool: True if the target is a dependency
169        """
170        if path in self._cached_dependencies:
171            return self._cached_dependencies[path]
172        ret = "node_modules" in Path(path).parts
173        self._cached_dependencies[path] = ret
174        return "lib" in Path(path).parts

Check if the path is a dependency (not supported for brownie)

Args: path (str): path to the target

Returns: bool: True if the target is a dependency