Skip to content

Commit

Permalink
fix: auto fork evmchains networks and better access (#2380)
Browse files Browse the repository at this point in the history
  • Loading branch information
antazoey authored Nov 20, 2024
1 parent e31c426 commit 9573398
Show file tree
Hide file tree
Showing 6 changed files with 69 additions and 27 deletions.
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ repos:
]

- repo: https://github.com/executablebooks/mdformat
rev: 0.7.18
rev: 0.7.19
hooks:
- id: mdformat
additional_dependencies: [mdformat-gfm, mdformat-frontmatter, mdformat-pyproject]
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@
"flake8-pydantic", # For detecting issues with Pydantic models
"flake8-type-checking", # Detect imports to move in/out of type-checking blocks
"isort>=5.13.2,<6", # Import sorting linter
"mdformat>=0.7.18", # Auto-formatter for markdown
"mdformat==0.7.18", # Auto-formatter for markdown
"mdformat-gfm>=0.3.5", # Needed for formatting GitHub-flavored markdown
"mdformat-frontmatter>=0.4.1", # Needed for frontmatters-style headers in issue templates
"mdformat-pyproject>=0.0.1", # Allows configuring in pyproject.toml
Expand Down
19 changes: 17 additions & 2 deletions src/ape/api/networks.py
Original file line number Diff line number Diff line change
Expand Up @@ -318,13 +318,29 @@ def _networks_from_plugins(self) -> dict[str, "NetworkAPI"]:
@cached_property
def _networks_from_evmchains(self) -> dict[str, "NetworkAPI"]:
# NOTE: Purposely exclude plugins here so we also prefer plugins.
return {
networks = {
network_name: create_network_type(data["chainId"], data["chainId"])(
name=network_name, ecosystem=self
)
for network_name, data in PUBLIC_CHAIN_META.get(self.name, {}).items()
if network_name not in self._networks_from_plugins
}
forked_networks: dict[str, ForkedNetworkAPI] = {}
for network_name, network in networks.items():
if network_name.endswith("-fork"):
# Already a fork.
continue

fork_network_name = f"{network_name}-fork"
if any(x == fork_network_name for x in networks):
# The forked version of this network is already known.
continue

forked_networks[fork_network_name] = ForkedNetworkAPI(
name=fork_network_name, ecosystem=self
)

return {**networks, **forked_networks}

def __post_init__(self):
if len(self.networks) == 0:
Expand Down Expand Up @@ -535,7 +551,6 @@ def get_network(self, network_name: str) -> "NetworkAPI":
Returns:
:class:`~ape.api.networks.NetworkAPI`
"""

names = {network_name, network_name.replace("-", "_"), network_name.replace("_", "-")}
networks = self.networks
for name in names:
Expand Down
47 changes: 25 additions & 22 deletions src/ape/managers/networks.py
Original file line number Diff line number Diff line change
Expand Up @@ -241,14 +241,38 @@ def ecosystems(self) -> dict[str, EcosystemAPI]:
)
plugin_ecosystems[ecosystem_name] = ecosystem_cls

return plugin_ecosystems
return {**plugin_ecosystems, **self._evmchains_ecosystems}

@cached_property
def _plugin_ecosystems(self) -> dict[str, EcosystemAPI]:
# Load plugins.
plugins = self.plugin_manager.ecosystems
return {n: cls(name=n) for n, cls in plugins} # type: ignore[operator]

@cached_property
def _evmchains_ecosystems(self) -> dict[str, EcosystemAPI]:
ecosystems: dict[str, EcosystemAPI] = {}
for name in PUBLIC_CHAIN_META:
ecosystem_name = name.lower().replace(" ", "-")
symbol = None
for net in PUBLIC_CHAIN_META[ecosystem_name].values():
if not (native_currency := net.get("nativeCurrency")):
continue

if "symbol" not in native_currency:
continue

symbol = native_currency["symbol"]
break

symbol = symbol or "ETH"

# Is an EVM chain, can automatically make a class using evm-chains.
evm_class = self._plugin_ecosystems["ethereum"].__class__
ecosystems[name] = evm_class(name=ecosystem_name, fee_token_symbol=symbol)

return ecosystems

def create_custom_provider(
self,
connection_str: str,
Expand Down Expand Up @@ -437,29 +461,9 @@ def get_ecosystem(self, ecosystem_name: str) -> EcosystemAPI:
Returns:
:class:`~ape.api.networks.EcosystemAPI`
"""

if ecosystem_name in self.ecosystem_names:
return self.ecosystems[ecosystem_name]

elif ecosystem_name.lower().replace(" ", "-") in PUBLIC_CHAIN_META:
ecosystem_name = ecosystem_name.lower().replace(" ", "-")
symbol = None
for net in PUBLIC_CHAIN_META[ecosystem_name].values():
if not (native_currency := net.get("nativeCurrency")):
continue

if "symbol" not in native_currency:
continue

symbol = native_currency["symbol"]
break

symbol = symbol or "ETH"

# Is an EVM chain, can automatically make a class using evm-chains.
evm_class = self._plugin_ecosystems["ethereum"].__class__
return evm_class(name=ecosystem_name, fee_token_symbol=symbol)

raise EcosystemNotFoundError(ecosystem_name, options=self.ecosystem_names)

def get_provider_from_choice(
Expand Down Expand Up @@ -606,7 +610,6 @@ def set_default_ecosystem(self, ecosystem_name: str):
ecosystem_name (str): The name of the ecosystem to set
as the default.
"""

if ecosystem_name in self.ecosystem_names:
self._default_ecosystem_name = ecosystem_name

Expand Down
5 changes: 5 additions & 0 deletions tests/functional/test_network_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -349,3 +349,8 @@ def supports_chain(cls, chain_id):
]
assert network.explorer is not None
assert network.explorer.name == NAME


def test_evm_chains_auto_forked_networks_exist(networks):
# NOTE: Moonbeam networks exist in evmchains only; that is how Ape knows about them.
assert isinstance(networks.moonbeam.moonriver_fork, ForkedNetworkAPI)
21 changes: 20 additions & 1 deletion tests/functional/test_network_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -240,7 +240,7 @@ def test_parse_network_choice_multiple_contexts(


def test_getattr_ecosystem_with_hyphenated_name(networks, ethereum):
networks.ecosystems["hyphen-in-name"] = networks.ecosystems["ethereum"]
networks._plugin_ecosystems["hyphen-in-name"] = networks.ecosystems["ethereum"]
assert networks.hyphen_in_name # Make sure does not raise AttributeError
del networks.ecosystems["hyphen-in-name"]

Expand Down Expand Up @@ -438,7 +438,26 @@ def test_custom_networks_defined_in_non_local_project(custom_networks_config_dic

with ape.Project.create_temporary_project(config_override=custom_networks) as temp_project:
nm = temp_project.network_manager

# Tests `.get_ecosystem()` for custom networks.
ecosystem = nm.get_ecosystem(eco_name)
assert ecosystem.name == eco_name

network = ecosystem.get_network(net_name)
assert network.name == net_name


def test_get_ecosystem(networks):
ethereum = networks.get_ecosystem("ethereum")
assert isinstance(ethereum, EcosystemAPI)
assert ethereum.name == "ethereum"


def test_get_ecosystem_from_evmchains(networks):
"""
Show we can call `.get_ecosystem()` for an ecosystem only
defined in evmchains.
"""
moonbeam = networks.get_ecosystem("moonbeam")
assert isinstance(moonbeam, EcosystemAPI)
assert moonbeam.name == "moonbeam"

0 comments on commit 9573398

Please sign in to comment.