Example #1
0
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
Example #2
0
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')
Example #3
0
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
Example #4
0
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()
Example #5
0
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
Example #6
0
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
Example #7
0
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
Example #8
0
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
Example #9
0
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()
Example #10
0
def test_get_solc_version_string(all_versions):
    v = solcx.get_solc_version_string()
    assert isinstance(v, str)
Example #11
0
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
Example #12
0
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)