crytic_compile.platform.solc_standard_json
Handle compilation through the standard solc json format
1""" 2Handle compilation through the standard solc json format 3""" 4import json 5import logging 6import os 7import shutil 8import subprocess 9from pathlib import Path 10from typing import TYPE_CHECKING, Any, Dict, List, Optional, Union 11 12from crytic_compile.compilation_unit import CompilationUnit 13from crytic_compile.compiler.compiler import CompilerVersion 14from crytic_compile.platform.exceptions import InvalidCompilation 15from crytic_compile.platform.solc import ( 16 Solc, 17 get_version, 18 is_optimized, 19 relative_to_short, 20) 21from crytic_compile.platform.types import Type 22from crytic_compile.utils.naming import convert_filename 23 24# Cycle dependency 25from crytic_compile.utils.natspec import Natspec 26 27if TYPE_CHECKING: 28 from crytic_compile import CryticCompile 29 30LOGGER = logging.getLogger("CryticCompile") 31 32 33# pylint: disable=too-many-arguments 34def standalone_compile( 35 filenames: List[str], 36 compilation_unit: CompilationUnit, 37 working_dir: Optional[str] = None, 38 remappings: Optional[List[str]] = None, 39 evm_version: Optional[str] = None, 40 via_ir: Optional[bool] = None, 41) -> None: 42 """ 43 Boilerplate function to run the the standardjson. compilation_unit.compiler_version must be set before calling this function 44 45 Example of usage: 46 compilation_unit = CompilationUnit(crytic_compile, name_target) 47 compilation_unit.compiler_version = CompilerVersion( 48 compiler="solc", version=compiler_version, optimized=optimization_used, optimize_runs=optimize_runs 49 ) 50 standalone_compile(filenames_to_compile, compilation_unit 51 52 Args: 53 filenames (List[str]): list of the files to compile 54 compilation_unit (CompilationUnit): compilation unit object to populate 55 working_dir (Optional[str]): working directory 56 remappings (Optional[List[str]]): list of solc remaps to use 57 evm_version (Optional[str]): EVM version to target. None for default 58 via_ir (Optional[bool]): whether to enable the viaIR compilation flag. None for unset 59 60 Returns: 61 62 """ 63 64 if compilation_unit.compiler_version.version == "N/A": 65 LOGGER.error("The compiler version of the compilation unit must be set") 66 return 67 68 standard_json_dict: Dict = {} 69 build_standard_json_default(standard_json_dict) 70 71 for filename in filenames: 72 add_source_file(standard_json_dict, filename) 73 74 if remappings is not None: 75 for remap in remappings: 76 add_remapping(standard_json_dict, remap) 77 78 if evm_version is not None: 79 add_evm_version(standard_json_dict, evm_version) 80 81 if via_ir is not None: 82 add_via_ir(standard_json_dict, via_ir) 83 84 add_optimization( 85 standard_json_dict, 86 compilation_unit.compiler_version.optimized, 87 compilation_unit.compiler_version.optimize_runs, 88 ) 89 90 targets_json = run_solc_standard_json( 91 standard_json_dict, 92 compiler_version=compilation_unit.compiler_version, 93 solc_disable_warnings=False, 94 working_dir=working_dir, 95 ) 96 97 parse_standard_json_output(targets_json, compilation_unit, solc_working_dir=working_dir) 98 99 100def build_standard_json_default(json_dict: Dict) -> None: 101 """ 102 Populate the given json_dict with the default values for the solc standard json input 103 Only write values for which the keys are not existing 104 105 Args: 106 json_dict (Dict): dictionary used for the solc standard input 107 108 Returns: 109 110 """ 111 if "language" not in json_dict: 112 json_dict["language"] = "Solidity" 113 if "sources" not in json_dict: 114 json_dict["sources"] = {} 115 if "settings" not in json_dict: 116 json_dict["settings"] = {} 117 118 if "remappings" not in json_dict["settings"]: 119 json_dict["settings"]["remappings"] = [] 120 if "optimizer" not in json_dict["settings"]: 121 json_dict["settings"]["optimizer"] = {"enabled": False} 122 if "outputSelection" not in json_dict["settings"]: 123 json_dict["settings"]["outputSelection"] = { 124 "*": { 125 "*": [ 126 "abi", 127 "metadata", 128 "devdoc", 129 "userdoc", 130 "evm.bytecode", 131 "evm.deployedBytecode", 132 ], 133 "": ["ast"], 134 } 135 } 136 137 138# pylint: disable=too-many-locals 139def run_solc_standard_json( 140 solc_input: Dict, 141 compiler_version: CompilerVersion, 142 solc_disable_warnings: bool = False, 143 working_dir: Optional[str] = None, 144) -> Dict: 145 """Run the solc standard json compilation. 146 Ensure that crytic_compile.compiler_version is set prior calling _run_solc 147 148 Args: 149 solc_input (Dict): standard json object 150 compiler_version (CompilerVersion): info regarding the compiler 151 solc_disable_warnings (bool): True to not print the solc warnings. Defaults to False. 152 working_dir (Optional[str], optional): Working directory to run solc. Defaults to None. 153 154 Raises: 155 InvalidCompilation: If the compilation failed 156 157 Returns: 158 Dict: Solc json output 159 """ 160 working_dir_resolved = Path(working_dir if working_dir else ".").resolve() 161 cmd = [compiler_version.compiler, "--standard-json", "--allow-paths", str(working_dir_resolved)] 162 additional_kwargs: Dict = {"cwd": working_dir} if working_dir else {} 163 164 env = dict(os.environ) 165 if compiler_version.version: 166 env["SOLC_VERSION"] = compiler_version.version 167 168 stderr = "" 169 LOGGER.info( 170 "'%s' running", 171 " ".join(cmd), 172 ) 173 try: 174 with subprocess.Popen( 175 cmd, 176 stdin=subprocess.PIPE, 177 stdout=subprocess.PIPE, 178 stderr=subprocess.PIPE, 179 env=env, 180 executable=shutil.which(cmd[0]), 181 **additional_kwargs, 182 ) as process: 183 stdout_b, stderr_b = process.communicate(json.dumps(solc_input).encode("utf-8")) 184 stdout, stderr = ( 185 stdout_b.decode(), 186 stderr_b.decode(errors="backslashreplace"), 187 ) # convert bytestrings to unicode strings 188 189 solc_json_output = json.loads(stdout) 190 191 # Check for errors and raise them if any exist. 192 solc_errors = solc_json_output.get("errors", []) 193 if solc_errors: 194 solc_error_occurred = False 195 solc_exception_str = "" 196 for solc_error in solc_errors: 197 if solc_error["severity"] != "warning": 198 solc_error_occurred = True 199 elif solc_disable_warnings: 200 continue 201 solc_exception_str += ( 202 f"{solc_error.get('type', 'UnknownExceptionType')}: " 203 f"{solc_error.get('formattedMessage', 'N/A')}\n" 204 ) 205 206 if solc_error_occurred: 207 raise InvalidCompilation(solc_exception_str) 208 if solc_exception_str: 209 LOGGER.warning(solc_exception_str) 210 211 return solc_json_output 212 213 except OSError as error: 214 # pylint: disable=raise-missing-from 215 raise InvalidCompilation(error) 216 217 except json.decoder.JSONDecodeError: 218 # pylint: disable=raise-missing-from 219 raise InvalidCompilation(f"Invalid solc compilation {stderr}") 220 221 222def add_source_file(json_dict: Dict, file_path: str) -> None: 223 """ 224 Add a path to the solc standard json input 225 226 Args: 227 json_dict (Dict): solc standard json input 228 file_path (str): file to add 229 230 Returns: 231 232 """ 233 json_dict["sources"][file_path] = {"urls": [file_path]} 234 235 236def add_remapping(json_dict: Dict, remapping: str) -> None: 237 """ 238 Add a remapping to the solc standard json input 239 240 Args: 241 json_dict (Dict): solc standard json input 242 remapping (str): remapping 243 244 Returns: 245 246 """ 247 json_dict["settings"]["remappings"].append(remapping) 248 249 250def add_optimization( 251 json_dict: Dict, optimize: Optional[bool], optimize_runs: Optional[int] 252) -> None: 253 """ 254 Add optimization settings to the solc standard json input 255 256 Args: 257 json_dict (Dict): solc standard json input 258 optimize (bool): true if optimization are enabled 259 optimize_runs (Optional[int]): number of optimize runs 260 261 Returns: 262 263 """ 264 if optimize: 265 json_dict["settings"]["optimizer"] = {"enabled": True} 266 if optimize_runs: 267 json_dict["settings"]["optimizer"]["runs"] = optimize_runs 268 return 269 json_dict["settings"]["optimizer"] = {"enabled": False} 270 271 272def add_evm_version(json_dict: Dict, version: str) -> None: 273 """ 274 Add the version of the EVM to compile for. 275 276 Can be one of the following values: homestead, tangerineWhistle, 277 spuriousDragon, byzantium, constantinople, petersburg, istanbul, 278 berlin, london or paris 279 280 Args: 281 json_dict (Dict): solc standard json input 282 version (str): the EVM version to target 283 284 Returns: 285 286 """ 287 json_dict["settings"]["evmVersion"] = version 288 289 290def add_via_ir(json_dict: Dict, enabled: bool) -> None: 291 """ 292 Enable or disable the "viaIR" compilation flag. 293 294 Args: 295 json_dict (Dict): solc standard json input 296 enabled (bool): whether viaIR is enabled 297 298 Returns: 299 300 """ 301 json_dict["settings"]["viaIR"] = enabled 302 303 304def parse_standard_json_output( 305 targets_json: Dict, compilation_unit: CompilationUnit, solc_working_dir: Optional[str] = None 306) -> None: 307 """ 308 Parse the targets_json output from solc, and populate compilation_unit accordingly 309 310 311 Args: 312 targets_json (Dict): output from solc 313 compilation_unit (CompilationUnit): compilation unit to populate 314 solc_working_dir (Optional[str]): working dir 315 316 Returns: 317 318 """ 319 320 skip_filename = compilation_unit.compiler_version.version in [f"0.4.{x}" for x in range(0, 10)] 321 322 if "sources" in targets_json: 323 for path, info in targets_json["sources"].items(): 324 if skip_filename: 325 path = convert_filename( 326 path, 327 relative_to_short, 328 compilation_unit.crytic_compile, 329 working_dir=solc_working_dir, 330 ) 331 else: 332 path = convert_filename( 333 path, 334 relative_to_short, 335 compilation_unit.crytic_compile, 336 working_dir=solc_working_dir, 337 ) 338 source_unit = compilation_unit.create_source_unit(path) 339 340 source_unit.ast = info.get("ast") 341 342 if "contracts" in targets_json: 343 for file_path, file_contracts in targets_json["contracts"].items(): 344 for contract_name, info in file_contracts.items(): 345 # for solc < 0.4.10 we cant retrieve the filename from the ast 346 if skip_filename: 347 filename = convert_filename( 348 file_path, 349 relative_to_short, 350 compilation_unit.crytic_compile, 351 working_dir=solc_working_dir, 352 ) 353 else: 354 filename = convert_filename( 355 file_path, 356 relative_to_short, 357 compilation_unit.crytic_compile, 358 working_dir=solc_working_dir, 359 ) 360 361 source_unit = compilation_unit.create_source_unit(filename) 362 363 source_unit.add_contract_name(contract_name) 364 compilation_unit.filename_to_contracts[filename].add(contract_name) 365 source_unit.abis[contract_name] = info["abi"] 366 367 userdoc = info.get("userdoc", {}) 368 devdoc = info.get("devdoc", {}) 369 natspec = Natspec(userdoc, devdoc) 370 source_unit.natspec[contract_name] = natspec 371 372 source_unit.bytecodes_init[contract_name] = info["evm"]["bytecode"]["object"] 373 source_unit.bytecodes_runtime[contract_name] = info["evm"]["deployedBytecode"][ 374 "object" 375 ] 376 source_unit.srcmaps_init[contract_name] = info["evm"]["bytecode"][ 377 "sourceMap" 378 ].split(";") 379 source_unit.srcmaps_runtime[contract_name] = info["evm"]["deployedBytecode"][ 380 "sourceMap" 381 ].split(";") 382 383 384# Inherits is_dependency/is_supported from Solc 385class SolcStandardJson(Solc): 386 """ 387 Represent the Standard solc Json object 388 """ 389 390 NAME = "Solc-json" 391 PROJECT_URL = "https://solidity.readthedocs.io/en/latest/using-the-compiler.html#compiler-input-and-output-json-description" 392 TYPE = Type.SOLC_STANDARD_JSON 393 394 def __init__(self, target: Union[str, dict] = None, **kwargs: str): 395 """Initializes an object which represents solc standard json 396 397 Args: 398 target (Union[str, dict], optional): A string path to a standard json, or a standard json. Defaults to None. 399 **kwargs: optional arguments. 400 401 Raises: 402 ValueError: If invalid json 403 """ 404 405 super().__init__(str(target), **kwargs) 406 407 if target is None: 408 self._json: Dict = {} 409 elif isinstance(target, str): 410 if os.path.isfile(target): 411 with open(target, mode="r", encoding="utf-8") as target_file: 412 self._json = json.load(target_file) 413 else: 414 self._json = json.loads(target) 415 416 elif isinstance(target, dict): 417 self._json = target 418 else: 419 raise ValueError("Invalid target for solc standard json input.") 420 421 build_standard_json_default(self._json) 422 423 def add_source_file(self, file_path: str) -> None: 424 """Append file 425 426 Args: 427 file_path (str): file to append 428 """ 429 add_source_file(self._json, file_path) 430 431 def add_source_files(self, files_path: List[str]) -> None: 432 """Append files 433 434 Args: 435 files_path (List[str]): files to append 436 """ 437 for file_path in files_path: 438 add_source_file(self._json, file_path) 439 440 def add_remapping(self, remapping: str) -> None: 441 """Append our remappings 442 443 Args: 444 remapping (str): remapping to add 445 """ 446 add_remapping(self._json, remapping) 447 448 def to_dict(self) -> Dict: 449 """Patch in our desired output types 450 451 Returns: 452 Dict: 453 """ 454 return self._json 455 456 # pylint: disable=too-many-locals 457 def compile(self, crytic_compile: "CryticCompile", **kwargs: Any) -> None: 458 """[summary] 459 460 Args: 461 crytic_compile (CryticCompile): Associated CryticCompile object 462 **kwargs: optional arguments. Used: "solc", "solc_disable_warnings", "solc_args", "solc_working_dir", 463 "solc_remaps", "solc_env" 464 """ 465 466 solc: str = kwargs.get("solc", "solc") 467 solc_disable_warnings: bool = kwargs.get("solc_disable_warnings", False) 468 solc_arguments: str = kwargs.get("solc_args", "") 469 470 solc_remaps: Optional[Union[str, List[str]]] = kwargs.get("solc_remaps", None) 471 solc_working_dir: Optional[str] = kwargs.get("solc_working_dir", None) 472 solc_env: Optional[Dict] = kwargs.get("solc_env", None) 473 474 compilation_unit = CompilationUnit(crytic_compile, "standard_json") 475 476 compilation_unit.compiler_version = CompilerVersion( 477 compiler="solc", 478 version=get_version(solc, solc_env), 479 optimized=is_optimized(solc_arguments) 480 or self.to_dict().get("settings", {}).get("optimizer", {}).get("enabled", False), 481 optimize_runs=self.to_dict().get("settings", {}).get("optimizer", {}).get("runs", None), 482 ) 483 484 add_optimization( 485 self._json, 486 compilation_unit.compiler_version.optimized, 487 compilation_unit.compiler_version.optimize_runs, 488 ) 489 490 # Add all remappings 491 if solc_remaps: 492 if isinstance(solc_remaps, str): 493 solc_remaps = solc_remaps.split(" ") 494 for solc_remap in solc_remaps: 495 self.add_remapping(solc_remap) 496 497 # Invoke solc 498 targets_json = run_solc_standard_json( 499 self.to_dict(), 500 compilation_unit.compiler_version, 501 solc_disable_warnings=solc_disable_warnings, 502 working_dir=solc_working_dir, 503 ) 504 505 parse_standard_json_output( 506 targets_json, compilation_unit, solc_working_dir=solc_working_dir 507 ) 508 509 def _guessed_tests(self) -> List[str]: 510 """Guess the potential unit tests commands 511 512 Returns: 513 List[str]: The guessed unit tests commands 514 """ 515 return []
35def standalone_compile( 36 filenames: List[str], 37 compilation_unit: CompilationUnit, 38 working_dir: Optional[str] = None, 39 remappings: Optional[List[str]] = None, 40 evm_version: Optional[str] = None, 41 via_ir: Optional[bool] = None, 42) -> None: 43 """ 44 Boilerplate function to run the the standardjson. compilation_unit.compiler_version must be set before calling this function 45 46 Example of usage: 47 compilation_unit = CompilationUnit(crytic_compile, name_target) 48 compilation_unit.compiler_version = CompilerVersion( 49 compiler="solc", version=compiler_version, optimized=optimization_used, optimize_runs=optimize_runs 50 ) 51 standalone_compile(filenames_to_compile, compilation_unit 52 53 Args: 54 filenames (List[str]): list of the files to compile 55 compilation_unit (CompilationUnit): compilation unit object to populate 56 working_dir (Optional[str]): working directory 57 remappings (Optional[List[str]]): list of solc remaps to use 58 evm_version (Optional[str]): EVM version to target. None for default 59 via_ir (Optional[bool]): whether to enable the viaIR compilation flag. None for unset 60 61 Returns: 62 63 """ 64 65 if compilation_unit.compiler_version.version == "N/A": 66 LOGGER.error("The compiler version of the compilation unit must be set") 67 return 68 69 standard_json_dict: Dict = {} 70 build_standard_json_default(standard_json_dict) 71 72 for filename in filenames: 73 add_source_file(standard_json_dict, filename) 74 75 if remappings is not None: 76 for remap in remappings: 77 add_remapping(standard_json_dict, remap) 78 79 if evm_version is not None: 80 add_evm_version(standard_json_dict, evm_version) 81 82 if via_ir is not None: 83 add_via_ir(standard_json_dict, via_ir) 84 85 add_optimization( 86 standard_json_dict, 87 compilation_unit.compiler_version.optimized, 88 compilation_unit.compiler_version.optimize_runs, 89 ) 90 91 targets_json = run_solc_standard_json( 92 standard_json_dict, 93 compiler_version=compilation_unit.compiler_version, 94 solc_disable_warnings=False, 95 working_dir=working_dir, 96 ) 97 98 parse_standard_json_output(targets_json, compilation_unit, solc_working_dir=working_dir)
Boilerplate function to run the the standardjson. compilation_unit.compiler_version must be set before calling this function
Example of usage: compilation_unit = CompilationUnit(crytic_compile, name_target) compilation_unit.compiler_version = CompilerVersion( compiler="solc", version=compiler_version, optimized=optimization_used, optimize_runs=optimize_runs ) standalone_compile(filenames_to_compile, compilation_unit
Args: filenames (List[str]): list of the files to compile compilation_unit (CompilationUnit): compilation unit object to populate working_dir (Optional[str]): working directory remappings (Optional[List[str]]): list of solc remaps to use evm_version (Optional[str]): EVM version to target. None for default via_ir (Optional[bool]): whether to enable the viaIR compilation flag. None for unset
Returns:
101def build_standard_json_default(json_dict: Dict) -> None: 102 """ 103 Populate the given json_dict with the default values for the solc standard json input 104 Only write values for which the keys are not existing 105 106 Args: 107 json_dict (Dict): dictionary used for the solc standard input 108 109 Returns: 110 111 """ 112 if "language" not in json_dict: 113 json_dict["language"] = "Solidity" 114 if "sources" not in json_dict: 115 json_dict["sources"] = {} 116 if "settings" not in json_dict: 117 json_dict["settings"] = {} 118 119 if "remappings" not in json_dict["settings"]: 120 json_dict["settings"]["remappings"] = [] 121 if "optimizer" not in json_dict["settings"]: 122 json_dict["settings"]["optimizer"] = {"enabled": False} 123 if "outputSelection" not in json_dict["settings"]: 124 json_dict["settings"]["outputSelection"] = { 125 "*": { 126 "*": [ 127 "abi", 128 "metadata", 129 "devdoc", 130 "userdoc", 131 "evm.bytecode", 132 "evm.deployedBytecode", 133 ], 134 "": ["ast"], 135 } 136 }
Populate the given json_dict with the default values for the solc standard json input Only write values for which the keys are not existing
Args: json_dict (Dict): dictionary used for the solc standard input
Returns:
140def run_solc_standard_json( 141 solc_input: Dict, 142 compiler_version: CompilerVersion, 143 solc_disable_warnings: bool = False, 144 working_dir: Optional[str] = None, 145) -> Dict: 146 """Run the solc standard json compilation. 147 Ensure that crytic_compile.compiler_version is set prior calling _run_solc 148 149 Args: 150 solc_input (Dict): standard json object 151 compiler_version (CompilerVersion): info regarding the compiler 152 solc_disable_warnings (bool): True to not print the solc warnings. Defaults to False. 153 working_dir (Optional[str], optional): Working directory to run solc. Defaults to None. 154 155 Raises: 156 InvalidCompilation: If the compilation failed 157 158 Returns: 159 Dict: Solc json output 160 """ 161 working_dir_resolved = Path(working_dir if working_dir else ".").resolve() 162 cmd = [compiler_version.compiler, "--standard-json", "--allow-paths", str(working_dir_resolved)] 163 additional_kwargs: Dict = {"cwd": working_dir} if working_dir else {} 164 165 env = dict(os.environ) 166 if compiler_version.version: 167 env["SOLC_VERSION"] = compiler_version.version 168 169 stderr = "" 170 LOGGER.info( 171 "'%s' running", 172 " ".join(cmd), 173 ) 174 try: 175 with subprocess.Popen( 176 cmd, 177 stdin=subprocess.PIPE, 178 stdout=subprocess.PIPE, 179 stderr=subprocess.PIPE, 180 env=env, 181 executable=shutil.which(cmd[0]), 182 **additional_kwargs, 183 ) as process: 184 stdout_b, stderr_b = process.communicate(json.dumps(solc_input).encode("utf-8")) 185 stdout, stderr = ( 186 stdout_b.decode(), 187 stderr_b.decode(errors="backslashreplace"), 188 ) # convert bytestrings to unicode strings 189 190 solc_json_output = json.loads(stdout) 191 192 # Check for errors and raise them if any exist. 193 solc_errors = solc_json_output.get("errors", []) 194 if solc_errors: 195 solc_error_occurred = False 196 solc_exception_str = "" 197 for solc_error in solc_errors: 198 if solc_error["severity"] != "warning": 199 solc_error_occurred = True 200 elif solc_disable_warnings: 201 continue 202 solc_exception_str += ( 203 f"{solc_error.get('type', 'UnknownExceptionType')}: " 204 f"{solc_error.get('formattedMessage', 'N/A')}\n" 205 ) 206 207 if solc_error_occurred: 208 raise InvalidCompilation(solc_exception_str) 209 if solc_exception_str: 210 LOGGER.warning(solc_exception_str) 211 212 return solc_json_output 213 214 except OSError as error: 215 # pylint: disable=raise-missing-from 216 raise InvalidCompilation(error) 217 218 except json.decoder.JSONDecodeError: 219 # pylint: disable=raise-missing-from 220 raise InvalidCompilation(f"Invalid solc compilation {stderr}")
Run the solc standard json compilation. Ensure that crytic_compile.compiler_version is set prior calling _run_solc
Args: solc_input (Dict): standard json object compiler_version (CompilerVersion): info regarding the compiler solc_disable_warnings (bool): True to not print the solc warnings. Defaults to False. working_dir (Optional[str], optional): Working directory to run solc. Defaults to None.
Raises: InvalidCompilation: If the compilation failed
Returns: Dict: Solc json output
223def add_source_file(json_dict: Dict, file_path: str) -> None: 224 """ 225 Add a path to the solc standard json input 226 227 Args: 228 json_dict (Dict): solc standard json input 229 file_path (str): file to add 230 231 Returns: 232 233 """ 234 json_dict["sources"][file_path] = {"urls": [file_path]}
Add a path to the solc standard json input
Args: json_dict (Dict): solc standard json input file_path (str): file to add
Returns:
237def add_remapping(json_dict: Dict, remapping: str) -> None: 238 """ 239 Add a remapping to the solc standard json input 240 241 Args: 242 json_dict (Dict): solc standard json input 243 remapping (str): remapping 244 245 Returns: 246 247 """ 248 json_dict["settings"]["remappings"].append(remapping)
Add a remapping to the solc standard json input
Args: json_dict (Dict): solc standard json input remapping (str): remapping
Returns:
251def add_optimization( 252 json_dict: Dict, optimize: Optional[bool], optimize_runs: Optional[int] 253) -> None: 254 """ 255 Add optimization settings to the solc standard json input 256 257 Args: 258 json_dict (Dict): solc standard json input 259 optimize (bool): true if optimization are enabled 260 optimize_runs (Optional[int]): number of optimize runs 261 262 Returns: 263 264 """ 265 if optimize: 266 json_dict["settings"]["optimizer"] = {"enabled": True} 267 if optimize_runs: 268 json_dict["settings"]["optimizer"]["runs"] = optimize_runs 269 return 270 json_dict["settings"]["optimizer"] = {"enabled": False}
Add optimization settings to the solc standard json input
Args: json_dict (Dict): solc standard json input optimize (bool): true if optimization are enabled optimize_runs (Optional[int]): number of optimize runs
Returns:
273def add_evm_version(json_dict: Dict, version: str) -> None: 274 """ 275 Add the version of the EVM to compile for. 276 277 Can be one of the following values: homestead, tangerineWhistle, 278 spuriousDragon, byzantium, constantinople, petersburg, istanbul, 279 berlin, london or paris 280 281 Args: 282 json_dict (Dict): solc standard json input 283 version (str): the EVM version to target 284 285 Returns: 286 287 """ 288 json_dict["settings"]["evmVersion"] = version
Add the version of the EVM to compile for.
Can be one of the following values: homestead, tangerineWhistle, spuriousDragon, byzantium, constantinople, petersburg, istanbul, berlin, london or paris
Args: json_dict (Dict): solc standard json input version (str): the EVM version to target
Returns:
291def add_via_ir(json_dict: Dict, enabled: bool) -> None: 292 """ 293 Enable or disable the "viaIR" compilation flag. 294 295 Args: 296 json_dict (Dict): solc standard json input 297 enabled (bool): whether viaIR is enabled 298 299 Returns: 300 301 """ 302 json_dict["settings"]["viaIR"] = enabled
Enable or disable the "viaIR" compilation flag.
Args: json_dict (Dict): solc standard json input enabled (bool): whether viaIR is enabled
Returns:
305def parse_standard_json_output( 306 targets_json: Dict, compilation_unit: CompilationUnit, solc_working_dir: Optional[str] = None 307) -> None: 308 """ 309 Parse the targets_json output from solc, and populate compilation_unit accordingly 310 311 312 Args: 313 targets_json (Dict): output from solc 314 compilation_unit (CompilationUnit): compilation unit to populate 315 solc_working_dir (Optional[str]): working dir 316 317 Returns: 318 319 """ 320 321 skip_filename = compilation_unit.compiler_version.version in [f"0.4.{x}" for x in range(0, 10)] 322 323 if "sources" in targets_json: 324 for path, info in targets_json["sources"].items(): 325 if skip_filename: 326 path = convert_filename( 327 path, 328 relative_to_short, 329 compilation_unit.crytic_compile, 330 working_dir=solc_working_dir, 331 ) 332 else: 333 path = convert_filename( 334 path, 335 relative_to_short, 336 compilation_unit.crytic_compile, 337 working_dir=solc_working_dir, 338 ) 339 source_unit = compilation_unit.create_source_unit(path) 340 341 source_unit.ast = info.get("ast") 342 343 if "contracts" in targets_json: 344 for file_path, file_contracts in targets_json["contracts"].items(): 345 for contract_name, info in file_contracts.items(): 346 # for solc < 0.4.10 we cant retrieve the filename from the ast 347 if skip_filename: 348 filename = convert_filename( 349 file_path, 350 relative_to_short, 351 compilation_unit.crytic_compile, 352 working_dir=solc_working_dir, 353 ) 354 else: 355 filename = convert_filename( 356 file_path, 357 relative_to_short, 358 compilation_unit.crytic_compile, 359 working_dir=solc_working_dir, 360 ) 361 362 source_unit = compilation_unit.create_source_unit(filename) 363 364 source_unit.add_contract_name(contract_name) 365 compilation_unit.filename_to_contracts[filename].add(contract_name) 366 source_unit.abis[contract_name] = info["abi"] 367 368 userdoc = info.get("userdoc", {}) 369 devdoc = info.get("devdoc", {}) 370 natspec = Natspec(userdoc, devdoc) 371 source_unit.natspec[contract_name] = natspec 372 373 source_unit.bytecodes_init[contract_name] = info["evm"]["bytecode"]["object"] 374 source_unit.bytecodes_runtime[contract_name] = info["evm"]["deployedBytecode"][ 375 "object" 376 ] 377 source_unit.srcmaps_init[contract_name] = info["evm"]["bytecode"][ 378 "sourceMap" 379 ].split(";") 380 source_unit.srcmaps_runtime[contract_name] = info["evm"]["deployedBytecode"][ 381 "sourceMap" 382 ].split(";")
Parse the targets_json output from solc, and populate compilation_unit accordingly
Args: targets_json (Dict): output from solc compilation_unit (CompilationUnit): compilation unit to populate solc_working_dir (Optional[str]): working dir
Returns:
386class SolcStandardJson(Solc): 387 """ 388 Represent the Standard solc Json object 389 """ 390 391 NAME = "Solc-json" 392 PROJECT_URL = "https://solidity.readthedocs.io/en/latest/using-the-compiler.html#compiler-input-and-output-json-description" 393 TYPE = Type.SOLC_STANDARD_JSON 394 395 def __init__(self, target: Union[str, dict] = None, **kwargs: str): 396 """Initializes an object which represents solc standard json 397 398 Args: 399 target (Union[str, dict], optional): A string path to a standard json, or a standard json. Defaults to None. 400 **kwargs: optional arguments. 401 402 Raises: 403 ValueError: If invalid json 404 """ 405 406 super().__init__(str(target), **kwargs) 407 408 if target is None: 409 self._json: Dict = {} 410 elif isinstance(target, str): 411 if os.path.isfile(target): 412 with open(target, mode="r", encoding="utf-8") as target_file: 413 self._json = json.load(target_file) 414 else: 415 self._json = json.loads(target) 416 417 elif isinstance(target, dict): 418 self._json = target 419 else: 420 raise ValueError("Invalid target for solc standard json input.") 421 422 build_standard_json_default(self._json) 423 424 def add_source_file(self, file_path: str) -> None: 425 """Append file 426 427 Args: 428 file_path (str): file to append 429 """ 430 add_source_file(self._json, file_path) 431 432 def add_source_files(self, files_path: List[str]) -> None: 433 """Append files 434 435 Args: 436 files_path (List[str]): files to append 437 """ 438 for file_path in files_path: 439 add_source_file(self._json, file_path) 440 441 def add_remapping(self, remapping: str) -> None: 442 """Append our remappings 443 444 Args: 445 remapping (str): remapping to add 446 """ 447 add_remapping(self._json, remapping) 448 449 def to_dict(self) -> Dict: 450 """Patch in our desired output types 451 452 Returns: 453 Dict: 454 """ 455 return self._json 456 457 # pylint: disable=too-many-locals 458 def compile(self, crytic_compile: "CryticCompile", **kwargs: Any) -> None: 459 """[summary] 460 461 Args: 462 crytic_compile (CryticCompile): Associated CryticCompile object 463 **kwargs: optional arguments. Used: "solc", "solc_disable_warnings", "solc_args", "solc_working_dir", 464 "solc_remaps", "solc_env" 465 """ 466 467 solc: str = kwargs.get("solc", "solc") 468 solc_disable_warnings: bool = kwargs.get("solc_disable_warnings", False) 469 solc_arguments: str = kwargs.get("solc_args", "") 470 471 solc_remaps: Optional[Union[str, List[str]]] = kwargs.get("solc_remaps", None) 472 solc_working_dir: Optional[str] = kwargs.get("solc_working_dir", None) 473 solc_env: Optional[Dict] = kwargs.get("solc_env", None) 474 475 compilation_unit = CompilationUnit(crytic_compile, "standard_json") 476 477 compilation_unit.compiler_version = CompilerVersion( 478 compiler="solc", 479 version=get_version(solc, solc_env), 480 optimized=is_optimized(solc_arguments) 481 or self.to_dict().get("settings", {}).get("optimizer", {}).get("enabled", False), 482 optimize_runs=self.to_dict().get("settings", {}).get("optimizer", {}).get("runs", None), 483 ) 484 485 add_optimization( 486 self._json, 487 compilation_unit.compiler_version.optimized, 488 compilation_unit.compiler_version.optimize_runs, 489 ) 490 491 # Add all remappings 492 if solc_remaps: 493 if isinstance(solc_remaps, str): 494 solc_remaps = solc_remaps.split(" ") 495 for solc_remap in solc_remaps: 496 self.add_remapping(solc_remap) 497 498 # Invoke solc 499 targets_json = run_solc_standard_json( 500 self.to_dict(), 501 compilation_unit.compiler_version, 502 solc_disable_warnings=solc_disable_warnings, 503 working_dir=solc_working_dir, 504 ) 505 506 parse_standard_json_output( 507 targets_json, compilation_unit, solc_working_dir=solc_working_dir 508 ) 509 510 def _guessed_tests(self) -> List[str]: 511 """Guess the potential unit tests commands 512 513 Returns: 514 List[str]: The guessed unit tests commands 515 """ 516 return []
Represent the Standard solc Json object
395 def __init__(self, target: Union[str, dict] = None, **kwargs: str): 396 """Initializes an object which represents solc standard json 397 398 Args: 399 target (Union[str, dict], optional): A string path to a standard json, or a standard json. Defaults to None. 400 **kwargs: optional arguments. 401 402 Raises: 403 ValueError: If invalid json 404 """ 405 406 super().__init__(str(target), **kwargs) 407 408 if target is None: 409 self._json: Dict = {} 410 elif isinstance(target, str): 411 if os.path.isfile(target): 412 with open(target, mode="r", encoding="utf-8") as target_file: 413 self._json = json.load(target_file) 414 else: 415 self._json = json.loads(target) 416 417 elif isinstance(target, dict): 418 self._json = target 419 else: 420 raise ValueError("Invalid target for solc standard json input.") 421 422 build_standard_json_default(self._json)
Initializes an object which represents solc standard json
Args: target (Union[str, dict], optional): A string path to a standard json, or a standard json. Defaults to None. **kwargs: optional arguments.
Raises: ValueError: If invalid json
424 def add_source_file(self, file_path: str) -> None: 425 """Append file 426 427 Args: 428 file_path (str): file to append 429 """ 430 add_source_file(self._json, file_path)
Append file
Args: file_path (str): file to append
432 def add_source_files(self, files_path: List[str]) -> None: 433 """Append files 434 435 Args: 436 files_path (List[str]): files to append 437 """ 438 for file_path in files_path: 439 add_source_file(self._json, file_path)
Append files
Args: files_path (List[str]): files to append
441 def add_remapping(self, remapping: str) -> None: 442 """Append our remappings 443 444 Args: 445 remapping (str): remapping to add 446 """ 447 add_remapping(self._json, remapping)
Append our remappings
Args: remapping (str): remapping to add
449 def to_dict(self) -> Dict: 450 """Patch in our desired output types 451 452 Returns: 453 Dict: 454 """ 455 return self._json
Patch in our desired output types
Returns: Dict:
458 def compile(self, crytic_compile: "CryticCompile", **kwargs: Any) -> None: 459 """[summary] 460 461 Args: 462 crytic_compile (CryticCompile): Associated CryticCompile object 463 **kwargs: optional arguments. Used: "solc", "solc_disable_warnings", "solc_args", "solc_working_dir", 464 "solc_remaps", "solc_env" 465 """ 466 467 solc: str = kwargs.get("solc", "solc") 468 solc_disable_warnings: bool = kwargs.get("solc_disable_warnings", False) 469 solc_arguments: str = kwargs.get("solc_args", "") 470 471 solc_remaps: Optional[Union[str, List[str]]] = kwargs.get("solc_remaps", None) 472 solc_working_dir: Optional[str] = kwargs.get("solc_working_dir", None) 473 solc_env: Optional[Dict] = kwargs.get("solc_env", None) 474 475 compilation_unit = CompilationUnit(crytic_compile, "standard_json") 476 477 compilation_unit.compiler_version = CompilerVersion( 478 compiler="solc", 479 version=get_version(solc, solc_env), 480 optimized=is_optimized(solc_arguments) 481 or self.to_dict().get("settings", {}).get("optimizer", {}).get("enabled", False), 482 optimize_runs=self.to_dict().get("settings", {}).get("optimizer", {}).get("runs", None), 483 ) 484 485 add_optimization( 486 self._json, 487 compilation_unit.compiler_version.optimized, 488 compilation_unit.compiler_version.optimize_runs, 489 ) 490 491 # Add all remappings 492 if solc_remaps: 493 if isinstance(solc_remaps, str): 494 solc_remaps = solc_remaps.split(" ") 495 for solc_remap in solc_remaps: 496 self.add_remapping(solc_remap) 497 498 # Invoke solc 499 targets_json = run_solc_standard_json( 500 self.to_dict(), 501 compilation_unit.compiler_version, 502 solc_disable_warnings=solc_disable_warnings, 503 working_dir=solc_working_dir, 504 ) 505 506 parse_standard_json_output( 507 targets_json, compilation_unit, solc_working_dir=solc_working_dir 508 )
[summary]
Args: crytic_compile (CryticCompile): Associated CryticCompile object **kwargs: optional arguments. Used: "solc", "solc_disable_warnings", "solc_args", "solc_working_dir", "solc_remaps", "solc_env"