Skip to content

Commit

Permalink
fix: issue where custom-networks defaulted to forked-providers when t…
Browse files Browse the repository at this point in the history
…hey were not forked networks. (ApeWorX#2239)
  • Loading branch information
antazoey authored Aug 23, 2024
1 parent 078b8f2 commit 151548b
Show file tree
Hide file tree
Showing 3 changed files with 64 additions and 5 deletions.
22 changes: 18 additions & 4 deletions src/ape/api/networks.py
Original file line number Diff line number Diff line change
Expand Up @@ -1017,15 +1017,26 @@ def providers(self): # -> dict[str, Partial[ProviderAPI]]
from ape.plugins._utils import clean_plugin_name

providers = {}
for _, plugin_tuple in self.plugin_manager.providers:
for _, plugin_tuple in self._get_plugin_providers():
ecosystem_name, network_name, provider_class = plugin_tuple
provider_name = clean_plugin_name(provider_class.__module__.split(".")[0])

is_custom_with_config = self._is_custom and self.default_provider_name == provider_name
# NOTE: Custom networks that are NOT from config must work with any provider.
# Also, ensure we are only adding forked providers for forked networks and
# non-forking providers for non-forked networks. For custom networks, it
# can be trickier (see last condition).
# TODO: In 0.9, add a better way for class-level ForkedProviders to define
# themselves as "Fork" providers.
if (
self.is_adhoc
or (self.ecosystem.name == ecosystem_name and self.name == network_name)
or (self._is_custom and self.default_provider_name == provider_name)
or (
is_custom_with_config
and (
(self.is_fork and "Fork" in provider_class.__name__)
or (not self.is_fork and "Fork" not in provider_class.__name__)
)
)
):
# NOTE: Lazily load provider config
providers[provider_name] = partial(
Expand All @@ -1037,6 +1048,10 @@ def providers(self): # -> dict[str, Partial[ProviderAPI]]

return providers

def _get_plugin_providers(self):
# NOTE: Abstracted for testing purposes.
return self.plugin_manager.providers

def get_provider(
self,
provider_name: Optional[str] = None,
Expand Down Expand Up @@ -1080,7 +1095,6 @@ def get_provider(
# If it can fork Ethereum (and we are asking for it) assume it can fork this one.
# TODO: Refactor this approach to work for custom-forked non-EVM networks.
common_forking_providers = self.network_manager.ethereum.mainnet_fork.providers

if provider_name in self.providers:
provider = self.providers[provider_name](provider_settings=provider_settings)
return _set_provider(provider)
Expand Down
6 changes: 5 additions & 1 deletion tests/functional/test_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -315,9 +315,13 @@ def cmd(network):
click.echo(f"Value is '{getattr(network, 'name', network)}'")

result = runner.invoke(cmd, ("--network", f"ethereum:{network_name}:node"))
assert result.exit_code == 0
assert f"Value is '{network_name}'" in result.output

# Fails because node is not a fork provider.
result = runner.invoke(cmd, ("--network", f"ethereum:{network_name}-fork:node"))
assert f"Value is '{network_name}-fork'" in result.output
assert result.exit_code != 0
assert f"No provider named 'node' in network '{network_name}-fork'" in result.output


def test_account_option(runner, keyfile_account):
Expand Down
41 changes: 41 additions & 0 deletions tests/functional/test_network_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -195,3 +195,44 @@ def test_create_network_type_fork():
actual = create_network_type(chain_id, chain_id, is_fork=True)
assert issubclass(actual, NetworkAPI)
assert issubclass(actual, ForkedNetworkAPI)


def test_providers(ethereum):
network = ethereum.local
providers = network.providers
assert "test" in providers
assert "node" in providers


def test_providers_custom_network(project, custom_networks_config_dict, ethereum):
with project.temp_config(**custom_networks_config_dict):
network = ethereum.apenet
actual = network.providers
assert "node" in actual


def test_providers_custom_non_fork_network_does_not_use_fork_provider(
mocker, project, custom_networks_config_dict, ethereum
):
# NOTE: Have to a mock a Fork provider since none ship with Ape core.
with project.temp_config(**custom_networks_config_dict):
network = ethereum.apenet
network.__dict__.pop("providers", None) # de-cache

# Setup mock fork provider.
orig = network._get_plugin_providers
network._get_plugin_providers = mocker.MagicMock()
name = "foobar"

class MyForkProvider:
__module__ = "foobar.test"

network._get_plugin_providers.return_value = iter(
[(name, ("ethereum", "local", MyForkProvider))]
)
try:
actual = network.providers
assert name not in actual
finally:
network._get_plugin_providers = orig
network.__dict__.pop("providers", None) # de-cache

0 comments on commit 151548b

Please sign in to comment.