def compile_and_format(contracts, optimize=True, runs=200, minify=False, silent=True): '''Compiles contracts and returns build data. Args: contracts: a dictionary in the form of {path: 'source code'} optimize: enable solc optimizer runs: optimizer runs minify: minify source files silent: verbose reporting Returns: build data dict''' if not contracts: return {} compiler_data = { 'minify_source': minify, 'version': solcx.get_solc_version_string().strip('\n') } input_json = generate_input_json(contracts, optimize, runs, minify) output_json = compile_from_input_json(input_json, silent) build_json = generate_build_json(input_json, output_json, compiler_data, silent) return build_json
def set_solc_version(version): '''Sets the solc version. If not available it will be installed.''' try: solcx.set_solc_version(version) except solcx.exceptions.SolcNotInstalled: solcx.install_solc(version) solcx.set_solc_version(version) return solcx.get_solc_version_string().strip('\n')
def compile_into_ast(src_path): # Convert to absolute path src_path = os.path.join(os.getcwd(), src_path) output_selection = {"*": {"": ["ast"]}} if os.path.isdir(src_path): # Multiple file contract src_data = {} first = True with open(os.path.join(src_path, ".mapping"), "r") as f: file_mapping = json.load(f) for filepath, filename in file_mapping.items(): src_data[filepath] = { "urls": [os.path.join(src_path, filename)] } if first: with open(os.path.join(src_path, filename), "r") as f2: file_data = f2.read() version = re.findall( r"pragma solidity [^0-9]*([0-9]*\.[0-9]*\.[0-9]*).*;", file_data, )[0] first = False else: # Single file with open(src_path, "r") as f: src_file = f.read() _, src_name = os.path.split(src_path) src_data = {src_name: {"urls": [src_path]}} version = re.findall( r"pragma solidity [^0-9]*([0-9]*\.[0-9]*\.[0-9]*).*;", src_file)[0] print(json.dumps(src_data)) install_solc_pragma(version) set_solc_version_pragma(version) compiler_input = { "language": "Solidity", "sources": src_data, "settings": { "outputSelection": output_selection }, } compile_output = compile_standard(compiler_input, allow_paths="/") ast = compile_output["sources"][os.path.basename(src_path)]["ast"] with open(src_path, "rb") as file: ast["source"] = codecs.utf_8_decode(file.read())[0] ast["_solc_version"] = get_solc_version_string() return ast
def set_solc_version(version): '''Sets the solc version. If not available it will be installed.''' if Version(version.lstrip('v')) < Version('0.4.22'): raise IncompatibleSolcVersion( "Brownie only supports Solidity versions >=0.4.22") try: solcx.set_solc_version(version, silent=True) except solcx.exceptions.SolcNotInstalled: install_solc(version) solcx.set_solc_version(version, silent=True) return solcx.get_solc_version_string()
def compile_and_format( contracts: Dict[str, str], solc_version: Optional[str] = None, optimize: bool = True, runs: int = 200, evm_version: int = None, minify: bool = False, silent: bool = True, ) -> Dict: """Compiles contracts and returns build data. Args: contracts: a dictionary in the form of {'path': "source code"} solc_version: solc version to compile with (use None to set via pragmas) optimize: enable solc optimizer runs: optimizer runs evm_version: evm version to compile for minify: minify source files silent: verbose reporting Returns: build data dict """ if not contracts: return {} build_json: Dict = {} if solc_version is not None: path_versions = {solc_version: list(contracts)} else: path_versions = find_solc_versions(contracts, install_needed=True, silent=silent) for version, path_list in path_versions.items(): set_solc_version(version) compiler_data = { "minify_source": minify, "version": solcx.get_solc_version_string() } to_compile = dict( (k, v) for k, v in contracts.items() if k in path_list) input_json = generate_input_json(to_compile, optimize, runs, evm_version, minify) output_json = compile_from_input_json(input_json, silent) build_json.update( generate_build_json(input_json, output_json, compiler_data, silent)) return build_json
def _compile_and_format(input_json): try: compiled = solcx.compile_standard( input_json, optimize=CONFIG['solc']['optimize'], optimize_runs=CONFIG['solc']['runs'], allow_paths="." ) except solcx.exceptions.SolcError as e: raise CompilerError(e) compiled = generate_pcMap(compiled) result = {} compiler_info = CONFIG['solc'].copy() compiler_info['version'] = solcx.get_solc_version_string().strip('\n') for filename in input_json['sources']: for match in re.findall( "\n(?:contract|library|interface) [^ {]{1,}", input_json['sources'][filename]['content'] ): type_, name = match.strip('\n').split(' ') data = compiled['contracts'][filename][name] evm = data['evm'] ref = [ (k, x) for v in evm['bytecode']['linkReferences'].values() for k, x in v.items() ] for n, loc in [(i[0], x['start']*2) for i in ref for x in i[1]]: evm['bytecode']['object'] = "{}__{:_<36}__{}".format( evm['bytecode']['object'][:loc], n[:36], evm['bytecode']['object'][loc+40:] ) result[name] = { 'abi': data['abi'], 'ast': compiled['sources'][filename]['ast'], 'bytecode': evm['bytecode']['object'], 'compiler': compiler_info, 'contractName': name, 'deployedBytecode': evm['deployedBytecode']['object'], 'deployedSourceMap': evm['deployedBytecode']['sourceMap'], 'networks': {}, 'opcodes': evm['deployedBytecode']['opcodes'], 'sha1': sha1(input_json['sources'][filename]['content'].encode()).hexdigest(), 'source': input_json['sources'][filename]['content'], 'sourceMap': evm['bytecode']['sourceMap'], 'sourcePath': filename, 'type': type_, 'pcMap': evm['deployedBytecode']['pcMap'] } return result
def _check_changed(filename, contract, clear=None): if contract in _changed: return _changed[contract] json_file = 'build/contracts/{}.json'.format(contract) if not os.path.exists(json_file): _changed[contract] = True return True try: CONFIG['solc']['version'] = solcx.get_solc_version_string().strip('\n') compiled = json.load(open(json_file, encoding="utf-8")) if (compiled['compiler'] != CONFIG['solc'] or compiled['sha1'] != sha1( open(filename, 'rb').read()).hexdigest()): _changed[contract] = True return True _changed[contract] = False return False except (json.JSONDecodeError, FileNotFoundError, KeyError): _changed[contract] = True return True
def _check_changed(build, filename, contract, clear=None): if contract in _changed: return _changed[contract] build = build.joinpath('{}.json'.format(contract)) if not build.exists(): _changed[contract] = True return True try: CONFIG['solc']['version'] = solcx.get_solc_version_string().strip('\n') compiled = json.load(build.open()) if ( compiled['compiler'] != CONFIG['solc'] or compiled['sha1'] != sha1(filename.open('rb').read()).hexdigest() ): _changed[contract] = True return True _changed[contract] = False return False except (json.JSONDecodeError, FileNotFoundError, KeyError): _changed[contract] = True return True
def test_set_solc_version(): compiler.set_solc_version("0.5.7") assert "0.5.7" in solcx.get_solc_version_string() compiler.set_solc_version("0.4.25") assert "0.4.25" in solcx.get_solc_version_string()
def test_get_solc_version_string(all_versions): v = solcx.get_solc_version_string() assert isinstance(v, str)
def compile_contracts(folder="contracts"): ''' Compiles the project with solc and saves the results in the build/contracts folder. ''' if _contracts: return _contracts clear_persistence(None) contract_files = [ "{}/{}".format(i[0], x) for i in os.walk(folder) for x in i[2] ] if not contract_files: sys.exit("ERROR: Cannot find any .sol files in contracts folder") msg = False compiler_info = CONFIG['solc'].copy() compiler_info['version'] = solcx.get_solc_version_string().strip('\n') inheritance_map = {} for filename in contract_files: code = open(filename, encoding="utf-8").read() for name in (re.findall("\n(?:contract|library|interface) (.*?){", code, re.DOTALL)): names = [i.strip(',') for i in name.strip().split(' ')] if names[0] in inheritance_map: raise ValueError("Multiple contracts named {}".format( names[0])) inheritance_map[names[0]] = set(names[2:]) _check_changed(filename, names[0]) for i in range(len(inheritance_map)): for base, inherited in [(k, x) for k, v in inheritance_map.copy().items() if v for x in v]: inheritance_map[base] |= inheritance_map[inherited] for filename in contract_files: code = open(filename).read() input_json = {} for name in (re.findall("\n(?:contract|library|interface) (.*?)[ {]", code, re.DOTALL)): check = [ i for i in inheritance_map[name] if _check_changed(filename, i) ] if not check and not _check_changed(filename, name): _contracts[name] = _json_load(name + ".json") continue if not msg: print("Compiling contracts...") print("Optimizer: {}".format( "Enabled Runs: " + str(CONFIG['solc']['runs']) if CONFIG['solc']['optimize'] else "Disabled")) msg = True input_json = { 'language': "Solidity", 'sources': { filename: { 'content': open(filename, encoding="utf-8").read() } }, 'settings': { 'outputSelection': { '*': { '*': [ "abi", "evm.assembly", "evm.bytecode", "evm.deployedBytecode" ], '': ["ast"] } }, "optimizer": { "enabled": CONFIG['solc']['optimize'], "runs": CONFIG['solc']['runs'] } } } break if not input_json: continue print(" - {}...".format(filename.split('/')[-1])) try: compiled = solcx.compile_standard( input_json, optimize=CONFIG['solc']['optimize'], optimize_runs=CONFIG['solc']['runs'], allow_paths=".") except solcx.exceptions.SolcError as e: err = json.loads(e.stdout_data) print("\nUnable to compile {}:\n".format(filename)) for i in err['errors']: print(i['formattedMessage']) sys.exit(1) hash_ = sha1(open(filename, 'rb').read()).hexdigest() compiled = generate_pcMap(compiled) for match in (re.findall("\n(?:contract|library|interface) [^ {]{1,}", code)): type_, name = match.strip('\n').split(' ') data = compiled['contracts'][filename][name] json_file = "build/contracts/{}.json".format(name) evm = data['evm'] ref = [(k, x) for v in evm['bytecode']['linkReferences'].values() for k, x in v.items()] for n, loc in [(i[0], x['start'] * 2) for i in ref for x in i[1]]: evm['bytecode']['object'] = "{}__{:_<36}__{}".format( evm['bytecode']['object'][:loc], n[:36], evm['bytecode']['object'][loc + 40:]) _contracts[name] = { 'abi': data['abi'], 'ast': compiled['sources'][filename]['ast'], 'bytecode': evm['bytecode']['object'], 'compiler': compiler_info, 'contractName': name, 'deployedBytecode': evm['deployedBytecode']['object'], 'deployedSourceMap': evm['deployedBytecode']['sourceMap'], 'networks': {}, 'opcodes': evm['deployedBytecode']['opcodes'], 'sha1': hash_, 'source': input_json['sources'][filename]['content'], 'sourceMap': evm['bytecode']['sourceMap'], 'sourcePath': filename, 'type': type_, 'pcMap': evm['deployedBytecode']['pcMap'] } json.dump(_contracts[name], open(json_file, 'w', encoding="utf-8"), sort_keys=True, indent=4) return _contracts
def compile_contracts(folder): ''' Compiles the project with solc and saves the results in the build/contracts folder. ''' if _contracts: return deepcopy(_contracts) solcx.set_solc_version(CONFIG['solc']['version']) folder = Path(folder).resolve() build_folder = folder.parent.joinpath('build/contracts') contract_files = list(folder.glob('**/*.sol')) if not contract_files: return {} compiler_info = CONFIG['solc'].copy() compiler_info['version'] = solcx.get_solc_version_string().strip('\n') inheritance_map = {} for filename in contract_files: code = filename.open().read() for name in ( re.findall( "\n(?:contract|library|interface) (.*?){", code, re.DOTALL) ): names = [i.strip(',') for i in name.strip().split(' ')] if names[0] in inheritance_map: raise ValueError( "Multiple contracts named {}".format(names[0])) inheritance_map[names[0]] = set(names[2:]) _check_changed(build_folder, filename, names[0]) for i in range(len(inheritance_map)): for base, inherited in [ (k, x) for k, v in inheritance_map.copy().items() if v for x in v ]: inheritance_map[base] |= inheritance_map[inherited] to_compile = [] for filename in contract_files: code = filename.open().read() input_json = {} for name in (re.findall( "\n(?:contract|library|interface) (.*?)[ {]", code, re.DOTALL )): check = [i for i in inheritance_map[name] if _check_changed(build_folder, filename, i)] if not check and not _check_changed(build_folder, filename, name): _contracts[name] = _json_load(build_folder.joinpath('{}.json'.format(name))) continue to_compile.append(filename) break if not to_compile: return deepcopy(_contracts) print("Compiling contracts...") print("Optimizer: {}".format( "Enabled Runs: "+str(CONFIG['solc']['runs']) if CONFIG['solc']['optimize'] else "Disabled" )) print("\n".join(" - {}...".format(i.name) for i in to_compile)) input_json = STANDARD_JSON.copy() input_json['sources'] = dict((str(i), {'content': i.open().read()}) for i in to_compile) build_json = _compile_and_format(input_json) for name, data in build_json.items(): json.dump( data, build_folder.joinpath("{}.json".format(name)).open('w'), sort_keys=True, indent=4 ) _contracts.update(build_json) return deepcopy(_contracts)