Skip to content

Commit

Permalink
fix: issue with address lookup fail
Browse files Browse the repository at this point in the history
  • Loading branch information
antazoey committed Apr 29, 2024
1 parent 2d07cba commit 301dd9e
Show file tree
Hide file tree
Showing 5 changed files with 66 additions and 41 deletions.
12 changes: 6 additions & 6 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -1,27 +1,27 @@
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.5.0
rev: v4.6.0
hooks:
- id: check-yaml

- repo: https://github.com/pre-commit/mirrors-isort
rev: v5.10.1
- repo: https://github.com/PyCQA/isort
rev: 5.13.2
hooks:
- id: isort

- repo: https://github.com/psf/black
rev: 23.12.0
rev: 24.4.2
hooks:
- id: black
name: black

- repo: https://github.com/pycqa/flake8
rev: 6.1.0
rev: 7.0.0
hooks:
- id: flake8

- repo: https://github.com/pre-commit/mirrors-mypy
rev: v1.7.1
rev: v1.10.0
hooks:
- id: mypy
additional_dependencies: [types-setuptools, pydantic]
Expand Down
44 changes: 27 additions & 17 deletions ape_ens/converter.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from ape.types import AddressType
from ape.utils import cached_property
from ape_ethereum.provider import Web3Provider
from web3.exceptions import CannotHandleRequest
from web3.main import ENS


Expand All @@ -27,22 +28,28 @@ def mainnet_provider(self) -> Optional[Web3Provider]:

# Connect to mainnet for ENS resolution
# NOTE: May not work unless the user configures their default mainnet provider.
provider = self.network_manager.get_provider_from_choice("ethereum:mainnet")
if not isinstance(provider, Web3Provider):
ecosystem = self.network_manager.get_ecosystem("ethereum")
mainnet = ecosystem.get_network("mainnet")
if not (provider := mainnet.default_provider):
return None

elif not isinstance(provider, Web3Provider):
logger.warning(
"Unable to connect to mainnet provider to "
"perform ENS lookup (must be a Web3Provider)"
)
return None

try:
provider.connect()
except ProviderError:
logger.warning(
"Unable to connect to mainnet provider to perform ENS lookup. "
"Try changing your default mainnet provider."
)
return None
if not provider.is_connected:
# Connect if needed.
try:
provider.connect()
except ProviderError:
logger.warning(
"Unable to connect to mainnet provider to perform ENS lookup. "
"Try changing your default mainnet provider."
)
return None

return provider

Expand All @@ -65,32 +72,35 @@ def is_convertible(self, value: Any) -> bool:
if not provider:
return False

ens = provider.web3.ens
if not ens:
elif not (ens := provider.web3.ens):
return False

address = ens.address(value)
try:
address = ens.address(value)
except CannotHandleRequest:
# Either this is not actually mainnet or our head is
# pointed before ENS existed.
return False

if address is not None:
self.address_cache[value] = address
return True

return False

except (NetworkError, ProviderError):
return False

def convert(self, value: str) -> AddressType:
if value in self.address_cache:
return self.address_cache[value]

provider = self.mainnet_provider
if not provider:
elif not (provider := self.mainnet_provider):
# Should never get here.
raise ValueError(f"Unable to convert ENS value '{value}'.")

# TODO: Switch to using ENS SDK
ens = provider.web3.ens
if not ens:
if not (ens := provider.web3.ens):
# Should never get here.
raise ValueError(f"Unable to convert ENS value '{value}'.")

Expand Down
8 changes: 4 additions & 4 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,13 @@
"ape-polygon", # For testing against another network named 'mainnet'
],
"lint": [
"black>=23.12.0,<24", # Auto-formatter and linter
"mypy>=1.7.1,<2", # Static type analyzer
"black>=24.4.2,<25", # Auto-formatter and linter
"mypy>=1.10.0,<2", # Static type analyzer
"types-setuptools", # Needed for mypy type shed
"flake8>=6.1.0,<7", # Style linter
"flake8>=7.0.0,<8", # Style linter
"flake8-breakpoint>=1.1.0,<2", # Detect breakpoints left in code
"flake8-print>=5.0.0,<6", # Detect print statements left in code
"isort>=5.10.1,<6", # Import sorting linter
"isort>=5.13.2,<6", # Import sorting linter
"mdformat>=0.7.17", # 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
Expand Down
6 changes: 6 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -108,3 +108,9 @@ def delete_caches():
yield ens

delete_caches()


@pytest.fixture
def mainnet_provider(converter):
_ = converter.is_convertible("test.eth") # Set mainnet_provider
return converter.__dict__["mainnet_provider"]
37 changes: 23 additions & 14 deletions tests/test_ens.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,9 @@
import ape
import pytest
from web3.exceptions import CannotHandleRequest

from tests.conftest import negative_tests


@pytest.fixture
def connection_spy(mocker, converter):
return mocker.spy(converter.network_manager, "get_provider_from_choice")


def test_is_convertible(converter):
assert converter.is_convertible("test.eth")

Expand All @@ -18,24 +13,38 @@ def test_is_not_convertible(converter, value):
assert not converter.is_convertible(value)


def test_is_not_convertible_when_ens_address_call_fails(converter, mainnet_provider):
"""
Tests against a bug where this would fail in an uncaught way rather
than saying ``is_convertible=False``.
"""
mainnet_provider.web3.ens.address.side_effect = CannotHandleRequest
# NOTE: Using test2.eth since it should be free from the cache (not used elsewhere)
assert not converter.is_convertible("test2.eth")


def test_address_cache(converter, address):
converter.address_cache["test.eth"] = address
assert converter.convert("test.eth") == address


def test_mainnet_fork(converter, connection_spy):
with ape.networks.parse_network_choice("ethereum:mainnet-fork:mock-mainnet-fork"):
connection_spy.reset_mock()
def test_mainnet_fork(converter, mocker):
get_eco_spy = mocker.spy(converter.network_manager, "get_ecosystem")

with ape.networks.ethereum.mainnet_fork.use_provider("mock-mainnet-fork"):
get_eco_spy.reset_mock()
converter.convert("test.eth")

# Should not have to reconnect
assert not connection_spy.call_count
assert not get_eco_spy.call_count


def test_other_ecosystem_mainnet(converter, mocker):
get_eco_spy = mocker.spy(converter.network_manager, "get_ecosystem")

def test_other_ecosystem_mainnet(converter, connection_spy):
with ape.networks.parse_network_choice("polygon:mainnet:mock-polygon-mainnet"):
connection_spy.reset_mock()
with ape.networks.polygon.mainnet.use_provider("mock-polygon-mainnet"):
get_eco_spy.reset_mock()
converter.convert("test.eth")

# It has to re-connect to Ethereum mainnet temporarily
connection_spy.assert_called_once_with("ethereum:mainnet")
get_eco_spy.assert_called_once_with("ethereum")

0 comments on commit 301dd9e

Please sign in to comment.