Beispiel #1
0
def add(plugin, version, skip_confirmation):
    if plugin.startswith("ape"):
        raise Abort(f"Namespace 'ape' in '{plugin}' is not required")

    # NOTE: Add namespace prefix (prevents arbitrary installs)
    plugin = f"ape_{clean_plugin_name(plugin)}"

    if version:
        plugin = f"{plugin}=={version}"

    if plugin in FIRST_CLASS_PLUGINS:
        raise Abort(f"Cannot add 1st class plugin '{plugin}'")

    elif is_plugin_installed(plugin):
        raise Abort(f"Plugin '{plugin}' already installed")

    elif (
        plugin in SECOND_CLASS_PLUGINS
        or skip_confirmation
        or click.confirm(f"Install unknown 3rd party plugin '{plugin}'?")
    ):
        notify("INFO", f"Installing {plugin}...")
        # NOTE: Be *extremely careful* with this command, as it modifies the user's
        #       installed packages, to potentially catastrophic results
        # NOTE: This is not abstracted into another function *on purpose*
        subprocess.call([sys.executable, "-m", "pip", "install", "--quiet", plugin])
Beispiel #2
0
    def compile(self,
                contract_filepaths: List[Path]) -> Dict[str, ContractType]:
        extensions = set(path.suffix for path in contract_filepaths)
        if extensions > set(self.registered_compilers):
            raise Exception("No compiler found for extension")

        contract_types = {}
        for extension in extensions:
            paths_to_compile = [
                path for path in contract_filepaths if path.suffix == extension
            ]
            for path in paths_to_compile:
                try:
                    notify(
                        "INFO",
                        f"Compiling '{path.relative_to(self.config.PROJECT_FOLDER)}'"
                    )
                except ValueError:
                    notify("INFO", f"Compiling '{path}'")
            for contract_type in self.registered_compilers[extension].compile(
                    paths_to_compile):

                if contract_type.contractName in contract_types:
                    raise Exception(
                        "ContractType collision across compiler plugins")

                contract_types[contract_type.contractName] = contract_type

        return contract_types
Beispiel #3
0
def _list():
    if len(accounts) == 0:
        notify("WARNING", "No accounts found.")
        return

    elif len(accounts) > 1:
        click.echo(f"Found {len(accounts)} accounts:")

    else:
        click.echo("Found 1 account:")

    for account in accounts:
        alias_display = f" (alias: '{account.alias}')" if account.alias else ""
        click.echo(f"  {account.address}{alias_display}")
Beispiel #4
0
def install(skip_confirmation):
    for plugin, version in config.get_config("plugins").items():
        if not plugin.startswith("ape-"):
            raise Abort(f"Namespace 'ape' required in config item '{plugin}'")

        if not is_plugin_installed(plugin.replace("-", "_")) and (
            plugin.replace("-", "_") in SECOND_CLASS_PLUGINS
            or skip_confirmation
            or click.confirm(f"Install unknown 3rd party plugin '{plugin}'?")
        ):
            notify("INFO", f"Installing {plugin}...")
            # NOTE: Be *extremely careful* with this command, as it modifies the user's
            #       installed packages, to potentially catastrophic results
            # NOTE: This is not abstracted into another function *on purpose*
            subprocess.call(
                [sys.executable, "-m", "pip", "install", "--quiet", f"{plugin}=={version}"]
            )
Beispiel #5
0
def _Contract(
    address: Union[str, AddressAPI, AddressType],
    networks: "NetworkManager",
    converters: "ConversionManager",
    contract_type: Optional[ContractType] = None,
) -> AddressAPI:
    """
    Function used to triage whether we have a contract type available for
    the given address/network combo, or explicitly provided. If none are found,
    returns a simple `Address` instance instead of throwing (provides a warning)
    """

    # Check contract cache (e.g. previously deployed/downloaded contracts)
    # TODO: Add `contract_cache` dict-like object to `NetworkAPI`
    # network = provider.network
    # if not contract_type and address in network.contract_cache:
    #    contract_type = network.contract_cache[address]

    # Check explorer API/cache (e.g. publicly published contracts)
    # TODO: Add `get_contract_type` to `ExplorerAPI`
    # TODO: Store in `NetworkAPI.contract_cache` to reduce API calls
    # explorer = provider.network.explorer
    # if not contract_type and explorer:
    #    contract_type = explorer.get_contract_type(address)

    # We have a contract type either:
    #   1) explicity provided,
    #   2) from network cache, or
    #   3) from explorer
    if contract_type:
        return ContractInstance(  # type: ignore
            _address=converters.convert(address, AddressType),
            _provider=networks.active_provider,
            _contract_type=contract_type,
        )

    else:
        # We don't have a contract type from any source, provide raw address instead
        notify("WARNING", f"No contract type found for {address}")
        return Address(  # type: ignore
            _address=converters.convert(address, AddressType),
            _provider=networks.active_provider,
        )
Beispiel #6
0
def generate(alias):
    if alias in accounts.aliases:
        notify("ERROR", f"Account with alias '{alias}' already exists")
        return

    path = container.data_folder.joinpath(f"{alias}.json")
    extra_entropy = click.prompt(
        "Add extra entropy for key generation...",
        hide_input=True,
    )
    account = EthAccount.create(extra_entropy)
    passphrase = click.prompt(
        "Create Passphrase",
        hide_input=True,
        confirmation_prompt=True,
    )
    path.write_text(json.dumps(EthAccount.encrypt(account.key, passphrase)))

    notify(
        "SUCCESS",
        f"A new account '{account.address}' has been added with the id '{alias}'"
    )
Beispiel #7
0
def _list(display_all):
    plugins = []
    for name, plugin in plugin_manager.list_name_plugin():
        version_str = ""
        version = get_package_version(name)

        if name in FIRST_CLASS_PLUGINS:
            if not display_all:
                continue  # NOTE: Skip 1st class plugins unless specified
            version_str = " (core)"

        elif version:
            version_str = f" ({version})"

        plugins.append(f"{name}{version_str}")

    if plugins:
        click.echo("Installed plugins:")
        click.echo("  " + "\n  ".join(plugins))

    else:
        notify("INFO", "No plugins installed")
Beispiel #8
0
def _import(alias):
    if alias in accounts.aliases:
        notify("ERROR", f"Account with alias '{alias}' already exists")
        return

    path = container.data_folder.joinpath(f"{alias}.json")
    key = click.prompt("Enter Private Key", hide_input=True)
    try:
        account = EthAccount.from_key(to_bytes(hexstr=key))
    except Exception as error:
        notify("ERROR", f"Key can't be imported {error}")
        return
    passphrase = click.prompt(
        "Create Passphrase",
        hide_input=True,
        confirmation_prompt=True,
    )
    path.write_text(json.dumps(EthAccount.encrypt(account.key, passphrase)))

    notify(
        "SUCCESS",
        f"A new account '{account.address}' has been added with the id '{alias}'"
    )
Beispiel #9
0
 def convert(self, value: str) -> AddressType:
     notify("WARNING", f"'{value}' is not in checksummed form")
     return to_checksum_address(value)
Beispiel #10
0
 def __post_init__(self):
     notify("INFO", f"Submitted {self.txn_hash.hex()}")
Beispiel #11
0
def delete(alias):
    account = accounts.load(alias)
    account.delete()
    notify("SUCCESS", f"Account '{alias}' has been deleted")
Beispiel #12
0
def change_password(alias):
    account = accounts.load(alias)
    account.change_password()
    notify("SUCCESS", f"Password has been changed for account '{alias}'")
Beispiel #13
0
}

# Plugins maintained OSS by ApeWorX (and trusted)
SECOND_CLASS_PLUGINS: Set[str] = set()

# TODO: Should the github client be in core?
# TODO: Handle failures with connecting to github (potentially cached on disk?)
if "GITHUB_ACCESS_TOKEN" in os.environ:
    author = Github(os.environ["GITHUB_ACCESS_TOKEN"]).get_organization("ApeWorX")

    SECOND_CLASS_PLUGINS = {
        repo.name.replace("-", "_") for repo in author.get_repos() if repo.name.startswith("ape-")
    }

else:
    notify("WARNING", "$GITHUB_ACCESS_TOKEN not set, skipping 2nd class plugins")


def is_plugin_installed(plugin: str) -> bool:
    try:
        __import__(plugin)
        return True
    except ImportError:
        return False


@click.group(short_help="Manage ape plugins")
def cli():
    """
    Command-line helper for managing installed plugins.
    """
Beispiel #14
0
def cli(filepaths, use_cache, display_size):
    """
    Compiles the manifest for this project and saves the results
    back to the manifest.

    Note that ape automatically recompiles any changed contracts each time
    a project is loaded. You do not have to manually trigger a recompile.
    """
    # NOTE: Lazy load so that testing works properly
    from ape import project

    # Expand source tree based on selection
    if not filepaths:
        if not (project.path / "contracts").exists():
            notify("WARNING", "No `contracts/` directory detected")
            return

        # If no paths are specified, use all local project sources
        contract_filepaths = project.sources

    else:
        # Expand any folder paths
        expanded_filepaths = flatten(
            [d.rglob("*.*") if d.is_dir() else [d] for d in filepaths])
        # Filter by what's in our project's source tree
        # NOTE: Make the paths absolute like `project.sources`
        contract_filepaths = [
            c.resolve() for c in expanded_filepaths
            if c.resolve() in project.sources
        ]

    if not contract_filepaths:
        if filepaths:
            selected_paths = "', '".join(str(p.resolve()) for p in filepaths)

        else:
            selected_paths = str(project.path / "contracts")

        extensions = ", ".join(set(f.suffix for f in selected_paths))
        notify(
            "WARNING",
            f"No compilers detected for the following extensions: {extensions}"
        )
        return

    # TODO: only compile selected contracts
    contract_types = project.load_contracts(use_cache)

    # Display bytecode size for *all* contract types (not just ones we compiled)
    if display_size:
        codesize = []
        for contract in contract_types.values():
            if not contract.deploymentBytecode:
                continue  # Skip if not bytecode to display

            bytecode = contract.deploymentBytecode.bytecode

            if bytecode:
                codesize.append((contract.contractName, len(bytecode) // 2))

        if not codesize:
            notify("INFO", "No contracts with bytecode to display")
            return

        click.echo()
        click.echo("============ Deployment Bytecode Sizes ============")
        indent = max(len(i[0]) for i in codesize)
        for name, size in sorted(codesize, key=lambda k: k[1], reverse=True):
            pct = size / 24577
            # pct_color = color(next((i[1] for i in CODESIZE_COLORS if pct >= i[0]), ""))
            # TODO Get colors fixed for bytecode size output
            # click.echo(f"  {name:<{indent}}  -  {size:>6,}B  ({pct_color}{pct:.2%}{color})")
            click.echo(f"  {name:<{indent}}  -  {size:>6,}B  ({pct:.2%})")

        click.echo()