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: issue where custom-networks defaulted to forked-providers when they were not forked networks. #2239

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: 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
Loading