def _get_unique_build_json(output_evm: Dict, contract_node: Any, stmt_nodes: Dict, branch_nodes: Dict, has_fallback: bool) -> Dict: paths = { str(i.contract_id): i.parent().absolutePath for i in [contract_node] + contract_node.dependencies } bytecode = _format_link_references(output_evm) without_metadata = _remove_metadata( output_evm["deployedBytecode"]["object"]) instruction_count = len(without_metadata) // 2 pc_map, statement_map, branch_map = _generate_coverage_data( output_evm["deployedBytecode"]["sourceMap"], output_evm["deployedBytecode"]["opcodes"], contract_node, stmt_nodes, branch_nodes, has_fallback, instruction_count, ) dependencies = [] for node in [ i for i in contract_node.dependencies if i.nodeType == "ContractDefinition" ]: # use contract aliases when recording dependencies, to avoid # potential namespace collisions when importing across projects name = node.name path_str = node.parent().absolutePath dependencies.append(_get_alias(name, path_str)) return { "allSourcePaths": paths, "bytecode": bytecode, "bytecodeSha1": sha1(_remove_metadata(bytecode).encode()).hexdigest(), "coverageMap": { "statements": statement_map, "branches": branch_map }, "dependencies": dependencies, "offset": contract_node.offset, "pcMap": pc_map, "type": contract_node.contractKind, }
def get_abi( contract_sources: Dict[str, str], allow_paths: Optional[str] = None, remappings: Optional[list] = None, silent: bool = True, ) -> Dict: """ Generate ABIs from contract interfaces. Arguments --------- contract_sources : dict a dictionary in the form of {'path': "source code"} allow_paths : str, optional Compiler allowed filesystem import path remappings : list, optional List of solidity path remappings silent : bool, optional Disable verbose reporting Returns ------- dict Compiled ABIs in the format `{'contractName': [ABI]}` """ final_output = { Path(k).stem: { "abi": json.loads(v), "contractName": Path(k).stem, "type": "interface", "source": None, "offset": None, "sha1": sha1(v.encode()).hexdigest(), } for k, v in contract_sources.items() if Path(k).suffix == ".json" } for path, source in [(k, v) for k, v in contract_sources.items() if Path(k).suffix == ".vy"]: input_json = generate_input_json({path: source}, language="Vyper") input_json["settings"]["outputSelection"]["*"] = {"*": ["abi"]} try: output_json = compile_from_input_json(input_json, silent, allow_paths) except Exception: # vyper interfaces do not convert to ABIs # https://github.com/vyperlang/vyper/issues/1944 continue name = Path(path).stem final_output[name] = { "abi": output_json["contracts"][path][name]["abi"], "contractName": name, "type": "interface", "source": source, "offset": [0, len(source)], "sha1": sha1(contract_sources[path].encode()).hexdigest(), } solc_sources = { k: v for k, v in contract_sources.items() if Path(k).suffix == ".sol" } if not solc_sources: return final_output compiler_targets = find_solc_versions(solc_sources, install_needed=True, silent=silent) for version, path_list in compiler_targets.items(): to_compile = { k: v for k, v in contract_sources.items() if k in path_list } set_solc_version(version) input_json = generate_input_json(to_compile, language="Solidity", remappings=remappings) input_json["settings"]["outputSelection"]["*"] = { "*": ["abi"], "": ["ast"] } output_json = compile_from_input_json(input_json, silent, allow_paths) source_nodes = solcast.from_standard_output(output_json) abi_json = { k: v for k, v in output_json["contracts"].items() if k in path_list } for path, name, data in [(k, x, y) for k, v in abi_json.items() for x, y in v.items()]: contract_node = next(i[name] for i in source_nodes if i.absolutePath == path) dependencies = [] for node in [ i for i in contract_node.dependencies if i.nodeType == "ContractDefinition" ]: dependency_name = node.name path_str = node.parent().absolutePath dependencies.append(_get_alias(dependency_name, path_str)) final_output[name] = { "abi": data["abi"], "ast": output_json["sources"][path]["ast"], "contractName": name, "dependencies": dependencies, "type": "interface", "source": contract_sources[path], "offset": contract_node.offset, "sha1": sha1(contract_sources[path].encode()).hexdigest(), } return final_output
def generate_build_json(input_json: Dict, output_json: Dict, compiler_data: Optional[Dict] = None, silent: bool = True) -> Dict: """Formats standard compiler output to the brownie build json. Args: input_json: solc input json used to compile output_json: output json returned by compiler compiler_data: additonal data to include under 'compiler' in build json silent: verbose reporting Returns: build json dict""" if input_json["language"] not in ("Solidity", "Vyper"): raise UnsupportedLanguage(f"{input_json['language']}") if not silent: print("Generating build data...") if compiler_data is None: compiler_data = {} compiler_data["evm_version"] = input_json["settings"]["evmVersion"] build_json: Dict = {} if input_json["language"] == "Solidity": compiler_data["optimizer"] = input_json["settings"]["optimizer"] source_nodes, statement_nodes, branch_nodes = solidity._get_nodes( output_json) for path_str, contract_name in [ (k, x) for k, v in output_json["contracts"].items() for x in v.keys() ]: contract_alias = contract_name if path_str in input_json["sources"]: source = input_json["sources"][path_str]["content"] else: with Path(path_str).open() as fp: source = fp.read() contract_alias = _get_alias(contract_name, path_str) if not silent: print(f" - {contract_alias}") abi = output_json["contracts"][path_str][contract_name]["abi"] natspec = merge_natspec( output_json["contracts"][path_str][contract_name].get( "devdoc", {}), output_json["contracts"][path_str][contract_name].get( "userdoc", {}), ) output_evm = output_json["contracts"][path_str][contract_name]["evm"] if contract_alias in build_json and not output_evm["deployedBytecode"][ "object"]: continue if input_json["language"] == "Solidity": contract_node = next(i[contract_name] for i in source_nodes if i.absolutePath == path_str) build_json[contract_alias] = solidity._get_unique_build_json( output_evm, contract_node, statement_nodes, branch_nodes, next((True for i in abi if i["type"] == "fallback"), False), ) else: if contract_name == "<stdin>": contract_name = contract_alias = "Vyper" build_json[contract_alias] = vyper._get_unique_build_json( output_evm, path_str, contract_alias, output_json["sources"][path_str]["ast"], (0, len(source)), ) build_json[contract_alias].update({ "abi": abi, "ast": output_json["sources"][path_str]["ast"], "compiler": compiler_data, "contractName": contract_name, "deployedBytecode": output_evm["deployedBytecode"]["object"], "deployedSourceMap": output_evm["deployedBytecode"]["sourceMap"], "language": input_json["language"], "natspec": natspec, "opcodes": output_evm["deployedBytecode"]["opcodes"], "sha1": sha1(source.encode()).hexdigest(), "source": source, "sourceMap": output_evm["bytecode"].get("sourceMap", ""), "sourcePath": path_str, }) size = len(remove_0x_prefix( output_evm["deployedBytecode"]["object"])) / 2 # type: ignore if size > 24577: notify( "WARNING", f"deployed size of {contract_name} is {size} bytes, exceeds EIP-170 limit of 24577", ) if not silent: print("") return build_json