Skip to content

Commit

Permalink
Merge pull request #731 from eth-brownie/feat-vvm
Browse files Browse the repository at this point in the history
Support multiple vyper versions
  • Loading branch information
iamdefinitelyahuman committed Aug 26, 2020
2 parents 02e1685 + 2995a71 commit c87f7c4
Show file tree
Hide file tree
Showing 30 changed files with 447 additions and 85 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ Brownie is a Python-based development and testing framework for smart contracts

## Features

* Full support for [Solidity](https://github.com/ethereum/solidity) (`>=0.4.22`) and [Vyper](https://github.com/vyperlang/vyper) (`0.2.x`)
* Full support for [Solidity](https://github.com/ethereum/solidity) (`>=0.4.22`) and [Vyper](https://github.com/vyperlang/vyper) (`>=0.1.0-beta.16`)
* Contract testing via [`pytest`](https://github.com/pytest-dev/pytest), including trace-based coverage evaluation
* Property-based and stateful testing via [`hypothesis`](https://github.com/HypothesisWorks/hypothesis/tree/master/hypothesis-python)
* Powerful debugging tools, including python-style tracebacks and custom error strings
Expand Down
2 changes: 2 additions & 0 deletions brownie/data/default-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ compiler:
enabled: true
runs: 200
remappings: null
vyper:
version: null

console:
show_colors: true
Expand Down
13 changes: 10 additions & 3 deletions brownie/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -159,15 +159,22 @@ class ProjectNotFound(Exception):


class CompilerError(Exception):
def __init__(self, e: Type[psutil.Popen]) -> None:
err = [i["formattedMessage"] for i in yaml.safe_load(e.stdout_data)["errors"]]
super().__init__("Compiler returned the following errors:\n\n" + "\n".join(err))
def __init__(self, e: Type[psutil.Popen], compiler: str = "Compiler") -> None:
self.compiler = compiler

err_json = yaml.safe_load(e.stdout_data)
err = [i.get("formattedMessage") or i["message"] for i in err_json["errors"]]
super().__init__(f"{compiler} returned the following errors:\n\n" + "\n".join(err))


class IncompatibleSolcVersion(Exception):
pass


class IncompatibleVyperVersion(Exception):
pass


class PragmaError(Exception):
pass

Expand Down
55 changes: 39 additions & 16 deletions brownie/network/contract.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
from eth_utils import remove_0x_prefix
from hexbytes import HexBytes
from semantic_version import Version
from vvm import get_installable_vyper_versions
from vvm.utils.convert import to_vyper_version

from brownie._config import CONFIG, REQUEST_HEADERS
from brownie.convert.datatypes import Wei
Expand Down Expand Up @@ -670,19 +672,30 @@ def from_explorer(
if not is_verified:
return cls.from_abi(name, address, abi, owner)

try:
version = Version(data["result"][0]["CompilerVersion"].lstrip("v")).truncate()
except Exception:
version = Version("0.0.0")
if version < Version("0.4.22") or (
# special case for OSX because installing 0.4.x versions is problematic
sys.platform == "darwin"
and version < Version("0.5.0")
and f"v{version}" not in solcx.get_installed_solc_versions()
):
compiler_str = data["result"][0]["CompilerVersion"]
if compiler_str.startswith("vyper:"):
try:
version = to_vyper_version(compiler_str[6:])
is_compilable = version in get_installable_vyper_versions()
except Exception:
is_compilable = False
else:
try:
version = Version(compiler_str.lstrip("v")).truncate()
if sys.platform == "darwin":
is_compilable = (
version >= Version("0.5.0")
or f"v{version}" in solcx.get_installed_solc_versions()
)
else:
is_compilable = f"v{version}" in solcx.get_available_solc_versions()
except Exception:
is_compilable = False

if not is_compilable:
if not silent:
warnings.warn(
f"{address}: target compiler '{data['result'][0]['CompilerVersion']}' is "
f"{address}: target compiler '{compiler_str}' is "
"unsupported by Brownie. Some functionality will not be available.",
BrownieCompilerWarning,
)
Expand All @@ -696,9 +709,10 @@ def from_explorer(
if evm_version == "Default":
evm_version = None

if data["result"][0]["SourceCode"].startswith("{"):
source_str = "\n".join(data["result"][0]["SourceCode"].splitlines())
if source_str.startswith("{"):
# source was verified using compiler standard JSON
input_json = json.loads(data["result"][0]["SourceCode"][1:-1])
input_json = json.loads(source_str[1:-1])
sources = {k: v["content"] for k, v in input_json["sources"].items()}
evm_version = input_json["settings"].get("evmVersion", evm_version)

Expand All @@ -709,10 +723,19 @@ def from_explorer(
output_json = compiler.compile_from_input_json(input_json)
build_json = compiler.generate_build_json(input_json, output_json)
else:
# source was submitted as a single flattened file
sources = {f"{name}-flattened.sol": data["result"][0]["SourceCode"]}
# source was submitted as a single file
if compiler_str.startswith("vyper"):
path_str = f"{name}.vy"
else:
path_str = f"{name}-flattened.sol"

sources = {path_str: source_str}
build_json = compiler.compile_and_format(
sources, solc_version=str(version), optimizer=optimizer, evm_version=evm_version
sources,
solc_version=str(version),
vyper_version=str(version),
optimizer=optimizer,
evm_version=evm_version,
)

build_json = build_json[name]
Expand Down
22 changes: 15 additions & 7 deletions brownie/project/compiler/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
set_solc_version,
)
from brownie.project.compiler.utils import merge_natspec
from brownie.project.compiler.vyper import find_vyper_versions, set_vyper_version
from brownie.utils import notify

from . import solidity, vyper
Expand Down Expand Up @@ -47,6 +48,7 @@
def compile_and_format(
contract_sources: Dict[str, str],
solc_version: Optional[str] = None,
vyper_version: Optional[str] = None,
optimize: bool = True,
runs: int = 200,
evm_version: Optional[str] = None,
Expand Down Expand Up @@ -86,10 +88,15 @@ def compile_and_format(
build_json: Dict = {}
compiler_targets = {}

vyper_paths = [i for i in contract_sources if Path(i).suffix == ".vy"]
if vyper_paths:
compiler_targets["vyper"] = vyper_paths

vyper_sources = {k: v for k, v in contract_sources.items() if Path(k).suffix == ".vy"}
if vyper_sources:
# TODO add `vyper_version` input arg to manually specify, support in config file
if vyper_version is None:
compiler_targets.update(
find_vyper_versions(vyper_sources, install_needed=True, silent=silent)
)
else:
compiler_targets[vyper_version] = list(vyper_sources)
solc_sources = {k: v for k, v in contract_sources.items() if Path(k).suffix == ".sol"}
if solc_sources:
if solc_version is None:
Expand All @@ -104,7 +111,8 @@ def compile_and_format(

for version, path_list in compiler_targets.items():
compiler_data: Dict = {}
if version == "vyper":
if path_list[0].endswith(".vy"):
set_vyper_version(version)
language = "Vyper"
compiler_data["version"] = str(vyper.get_version())
interfaces = {k: v for k, v in interface_sources.items() if Path(k).suffix != ".sol"}
Expand Down Expand Up @@ -287,8 +295,8 @@ def generate_build_json(

abi = output_json["contracts"][path_str][contract_name]["abi"]
natspec = merge_natspec(
output_json["contracts"][path_str][contract_name]["devdoc"],
output_json["contracts"][path_str][contract_name]["userdoc"],
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"]

Expand Down
2 changes: 1 addition & 1 deletion brownie/project/compiler/solidity.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ def compile_from_input_json(
allow_paths=allow_paths,
)
except solcx.exceptions.SolcError as e:
raise CompilerError(e)
raise CompilerError(e, "solc")


def set_solc_version(version: str) -> str:
Expand Down

0 comments on commit c87f7c4

Please sign in to comment.