Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix/zksync tx fail #90

Merged
merged 11 commits into from
Oct 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ Issues, feedback, and sharing that you're using Titanoboa and Vyper on social me
- [ZKSync requirements](#zksync-requirements)
- [Installing for local development](#installing-for-local-development)
- [Running Tests](#running-tests)
- [Local Tests](#local-tests)
- [Integration Tests](#integration-tests)
- [ZKSync Tests](#zksync-tests)
- [Code Style Guide](#code-style-guide)
- [Where do you get the `typecheck` and `format` command?](#where-do-you-get-the-typecheck-and-format-command)
- [Thank you!](#thank-you)
Expand Down Expand Up @@ -99,13 +102,32 @@ However, if you run tests and scripts using the `uv` or `just` commands as we wi

First, you'll need to make sure you have the `anvil1` keystore in your `~/.moccasin/keystores` folder. You can [find it here](./tests/data/keystores/anvil1). Please move it there.

### Local Tests

Run the following:

```bash
just test # Check out the justfile to see the command this runs
```
This is equivalent to running `pytest` in the root directory of the project.

### Integration Tests

Read the [README.md in the integration folder](./tests/integration/README.md) to see how to run the integration tests.

```bash
just test-i # Check out the justfile to see the command this runs
```

### ZKSync Tests

These will be the zksync tests that require the [ZKsync requirements](#zksync-requirements) to be installed.

```bash
just test-z # Check out the justfile to see the command this runs

```

# Code Style Guide

We will run the `.github/workflows` before merging your PR to ensure that your code is up to standard. Be sure to run the scripts in there before submitting a PR.
Expand Down
1 change: 1 addition & 0 deletions docs/source/all_moccasin_toml_parameters.rst
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ All possible options
unsafe_password_file = "/home/user/.moccasin/password" # Replace with actual path
explorer_uri = "https://api.etherscan.io/api" # path for the supported explorer
explorer_api_key = "your_api_key" # api key for the supported explorer, overrides the main one
explorer_type = "blockscout" # If the explorer URL has "blockscout" or "etherscan" in the name, you don't need this
prompt_live = false # A flag that will prompt you before sending a transaction, it defaults to true for "non-testing" networks
save_to_db = true # A flag that will save the deployment to the database, it defaults to true for "non-testing" networks (not pyevm, eravm, or a fork network)

Expand Down
6 changes: 3 additions & 3 deletions justfile
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,14 @@ format-check:

# Run unit and CLI tests, fail on first test failure
test:
uv run pytest -x -s --ignore=tests/data/ --ignore=tests/integration/ --ignore=tests/zksync/
uv run pytest -x -nauto --ignore=tests/integration/ --ignore=tests/zksync/

# Run integration tests, read the README.md in the tests/integration directory for more information
test-i:
uv run pytest tests/integration -x -s --ignore=tests/data/ --ignore=tests/zksync/
uv run pytest tests/integration -x --ignore=tests/zksync/

test-z:
uv run pytest tests/zksync -x -s --ignore=tests/data/ --ignore=tests/integration/
uv run pytest tests/zksync -nauto --ignore=tests/integration/

# Run both unit and integration tests
test-all:
Expand Down
29 changes: 26 additions & 3 deletions moccasin/__main__.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import argparse
import importlib
import sys
import tomllib
from importlib import metadata
from importlib import import_module, metadata
from pathlib import Path
from typing import Tuple

Expand All @@ -16,6 +15,8 @@
"c": "compile",
"script": "run",
"config": "config_",
"u": "utils",
"util": "utils",
}

PRINT_HELP_ON_NO_SUB_COMMAND = ["run", "wallet", "explorer", "deployments"]
Expand Down Expand Up @@ -54,7 +55,7 @@ def main(argv: list) -> int:
if args.command:
command_to_run = ALIAS_TO_COMMAND.get(args.command, args.command)
logger.info(f"Running {command_to_run} command...")
importlib.import_module(f"moccasin.commands.{command_to_run}").main(args)
import_module(f"moccasin.commands.{command_to_run}").main(args)
else:
main_parser.print_help()
return 0
Expand Down Expand Up @@ -611,6 +612,28 @@ def generate_main_parser_and_sub_parsers() -> (
"--limit", default=None, help="Limit the number of deployments to get."
)
add_network_args_to_parser(deployments_parser)
# ------------------------------------------------------------------
# UTILS COMMAND
# ------------------------------------------------------------------
utils_paraser = sub_parsers.add_parser(
"utils",
aliases=["u", "util"],
help="Helpful utilities - right now it's just the one.",
description="Helpful utilities.\n",
parents=[parent_parser],
)
utils_subparaser = utils_paraser.add_subparsers(dest="utils_command")

# Zero
utils_subparaser.add_parser(
"zero",
aliases=["zero-address", "zero_address", "address-zero", "address_zero"],
help="Get the zero address.",
)

# ------------------------------------------------------------------
# RETURN
# ------------------------------------------------------------------
return main_parser, sub_parsers


Expand Down
1 change: 1 addition & 0 deletions moccasin/commands/deployments.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ def print_deployments_from_cli(
return []

deployments_list = []

if checked:
deployments_list = active_network.get_deployments_checked(
contract_name, limit=limit
Expand Down
18 changes: 18 additions & 0 deletions moccasin/commands/utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
from argparse import Namespace

from eth import constants

ALIAS_TO_COMMAND = {
"zero-address": "zero",
"zero_address": "zero",
"address-zero": "zero",
"address_zero": "zero",
}


def main(args: Namespace) -> int:
command = args.utils_command.strip().lower()
utils_command = ALIAS_TO_COMMAND.get(command, command)
if utils_command.strip().lower() == "zero":
print("0x" + constants.ZERO_ADDRESS.hex())
return 0
110 changes: 97 additions & 13 deletions moccasin/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,15 @@

import boa
import tomlkit

# If we want to know the specific deployer that the active env is working with
# from boa.interpret import _get_default_deployer_class
from boa.contracts.abi.abi_contract import ABIContract, ABIContractFactory
from boa.contracts.vyper.vyper_contract import VyperContract, VyperDeployer
from boa.deployments import Deployment, get_deployments_db
from boa.environment import Env
from boa.util.abi import Address
from boa.verifiers import get_verification_bundle
from boa_zksync import set_zksync_fork, set_zksync_test_env
from boa_zksync import set_zksync_env, set_zksync_fork, set_zksync_test_env

# REVIEW: Could/should these be optionally imported?
PatrickAlphaC marked this conversation as resolved.
Show resolved Hide resolved
from boa_zksync.contract import ZksyncContract
from boa_zksync.deployer import ZksyncDeployer
from dotenv import load_dotenv
Expand Down Expand Up @@ -49,9 +48,12 @@

if TYPE_CHECKING:
from boa.network import NetworkEnv
from boa.verifiers import Blockscout, VerificationResult
from boa_zksync import ZksyncEnv
from boa_zksync.verifiers import ZksyncExplorer

_AnyEnv = Union["NetworkEnv", "Env", "ZksyncEnv"]
VERIFIERS = Union["Blockscout", "ZksyncExplorer"]


@dataclass
Expand All @@ -63,9 +65,10 @@ class Network:
is_zksync: bool = False
default_account_name: str | None = None
unsafe_password_file: Path | None = None
explorer_uri: str | None = None
save_abi_path: str | None = None
explorer_uri: str | None = None
explorer_api_key: str | None = None
explorer_type: str | None = None
contracts: dict[str, NamedContract] = field(default_factory=dict)
prompt_live: bool = True
save_to_db: bool = True
Expand All @@ -80,7 +83,6 @@ def _set_boa_env_and_db(self) -> _AnyEnv:
# to switch networks
from boa.deployments import DeploymentsDB, set_deployments_db
from boa.network import EthereumRPC, NetworkEnv
from boa_zksync import ZksyncEnv

# 0. Set the database
# The local networks should be validated at this point
Expand All @@ -107,8 +109,11 @@ def _set_boa_env_and_db(self) -> _AnyEnv:

# 3. Finally, "true" networks
elif self.is_zksync:
env = ZksyncEnv(EthereumRPC(self.url), nickname=self.name)
boa.set_env(env)
if self.explorer_type != "zksyncexplorer":
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the string should be in a constant. Btw, why is it lower case here?
If imported, we could also use ZksyncExplorer.__name__

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

They are able to set the explorer_type in the moccasin.toml. It's hard to know what type of explorer it is based on the URL alone.

logger.warning(
"Explorer type is not zksyncexplorer, as of today, only the zksync explorer is supported with zksync."
)
set_zksync_env(self.url, explorer_url=self.explorer_uri, nickname=self.name)
else:
env = NetworkEnv(EthereumRPC(self.url), nickname=self.name)
boa.set_env(env)
Expand All @@ -122,9 +127,74 @@ def _set_boa_env_and_db(self) -> _AnyEnv:
)
self.chain_id = expected_chain_id if not self.chain_id else self.chain_id

# 5. Set the explorer type and uri if not set by default ones
if self.chain_id is not None and (
self.explorer_uri is None or self.explorer_uri is None
):
# Review: Is this down here even good?
from moccasin.constants.vars import DEFAULT_NETWORKS_BY_CHAIN_ID

default_network_info = DEFAULT_NETWORKS_BY_CHAIN_ID.get(
self.chain_id, {}
)
self.explorer_uri = (
self.explorer_uri
if self.explorer_uri is not None
else default_network_info.get("explorer", None)
)
self.explorer_type = (
self.explorer_type
if self.explorer_type is not None
else default_network_info.get("explorer_type", None)
)

boa.env.nickname = self.name
return boa.env

def moccasin_verify(
self, contract: VyperContract | ZksyncContract
) -> "VerificationResult":
"""Verifies a contract using your moccasin.toml config."""
verifier_class = self.get_verifier_class()
verifier_instance = verifier_class(self.explorer_uri, self.explorer_api_key)
if self.is_zksync:
import boa_zksync

return boa_zksync.verify(contract, verifier_instance)
return boa.verify(contract, verifier_instance)

def get_verifier_class(self) -> Any:
if self.explorer_type is None:
if self.explorer_uri is not None:
if "blockscout" in self.explorer_uri:
self.explorer_type = "blockscout"
elif "zksync" in self.explorer_uri:
self.explorer_type = "zksyncexplorer"

if self.explorer_type is None:
raise ValueError(
f"No explorer type found. Please set the explorer_type in your {CONFIG_NAME}."
)
if self.explorer_uri is None:
raise ValueError(
f"No explorer_uri found. Please set the `explorer_uri` in your {CONFIG_NAME}."
)

verifier_name = self._to_verifier_name(self.explorer_type)
from importlib import import_module

module = import_module("moccasin.supported_verifiers")
return getattr(module, verifier_name)

def _to_verifier_name(self, verifier_string: str) -> str:
if verifier_string.lower().strip() == "blockscout":
return "Blockscout"
if verifier_string.lower().strip() == "zksyncexplorer":
return "ZksyncExplorer"
raise ValueError(
f"Verifier {verifier_string} is not supported. Please use 'blockscout' or 'zksyncexplorer'."
)

def get_default_account(self) -> MoccasinAccount | Any:
"""Returns an 'account-like' object."""
if hasattr(boa.env, "_accounts"):
Expand Down Expand Up @@ -162,6 +232,8 @@ def _fetch_contracts_from_db(
limit: int | None = None,
) -> Iterator[Deployment]:
limit_str = ""
if not isinstance(limit, int) and not isinstance(limit, type(None)):
raise ValueError(f"Limit must be an integer, not {type(limit)}.")
if limit is not None:
limit_str = f"LIMIT {limit};"
db = get_deployments_db()
Expand All @@ -174,9 +246,8 @@ def _fetch_contracts_from_db(
chain_id = to_hex(self.chain_id)
else:
chain_id = to_hex(chain_id)
return db._get_deployments_from_sql(
GET_CONTRACT_SQL.format(field_names, limit_str), (contract_name, chain_id)
)
final_sql = GET_CONTRACT_SQL.format(field_names, limit_str)
return db._get_deployments_from_sql(final_sql, (contract_name, chain_id))

def has_matching_integrity(
self, deployment: Deployment, contract_name: str, config: "Config" = None
Expand Down Expand Up @@ -429,6 +500,9 @@ def is_local_or_forked_network(self) -> bool:
"""
return self.name in [PYEVM, ERAVM] or self.is_fork

def has_explorer(self) -> bool:
return self.explorer_uri is not None

def _deploy_named_contract(
self, named_contract: NamedContract, deployer_script: str | Path
) -> VyperContract | ZksyncContract:
Expand Down Expand Up @@ -547,6 +621,7 @@ def __init__(self, toml_data: dict, db_path: Path):

default_explorer_api_key = project_data.get("explorer_api_key", None)
default_explorer_uri = project_data.get("explorer_uri", None)
default_explorer_type = project_data.get("explorer_type", None)
default_save_abi_path = project_data.get("save_abi_path", None)
default_contracts = toml_data.get("networks", {}).get("contracts", {})
self.default_network_name = project_data.get(
Expand Down Expand Up @@ -590,15 +665,18 @@ def __init__(self, toml_data: dict, db_path: Path):
name=network_name,
is_fork=network_data.get("fork", False),
url=network_data.get("url", None),
is_zksync=network_data.get("zksync", False),
is_zksync=network_data.get("is_zksync", False),
chain_id=network_data.get("chain_id", None),
explorer_uri=network_data.get("explorer_uri", default_explorer_uri),
save_abi_path=network_data.get(
SAVE_ABI_PATH, default_save_abi_path
),
explorer_uri=network_data.get("explorer_uri", default_explorer_uri),
explorer_api_key=network_data.get(
"explorer_api_key", default_explorer_api_key
),
explorer_type=network_data.get(
"explorer_type", default_explorer_type
),
default_account_name=network_data.get("default_account_name", None),
unsafe_password_file=network_data.get("unsafe_password_file", None),
prompt_live=network_data.get("prompt_live", True),
Expand Down Expand Up @@ -1036,5 +1114,11 @@ def get_active_network() -> Network:
def initialize_global_config(config_path: Path | None = None) -> Config:
global _config
assert _config is None
_set_global_config(config_path)
return get_config()


def _set_global_config(config_path: Path | None = None) -> Config:
global _config
_config = Config.load_config_from_path(config_path)
return _config
Loading