From 55505638068979b6f9cd4e01620f5bfdbdf398d9 Mon Sep 17 00:00:00 2001 From: djstrong Date: Fri, 19 Dec 2025 09:31:27 +0100 Subject: [PATCH 01/10] feat(nameguard): Add USE_ENSNODE_API configuration and implement async primary name resolution --- packages/nameguard-python/.env.example | 3 ++ .../nameguard-python/nameguard/nameguard.py | 30 +++++++++++++++++-- 2 files changed, 31 insertions(+), 2 deletions(-) diff --git a/packages/nameguard-python/.env.example b/packages/nameguard-python/.env.example index 2545a203f..5fad07537 100644 --- a/packages/nameguard-python/.env.example +++ b/packages/nameguard-python/.env.example @@ -19,3 +19,6 @@ ALCHEMY_URI_SEPOLIA=https://eth-sepolia.g.alchemy.com/v2/[YOUR_ALCHEMY_API_KEY] # - https://thegraph.com/explorer/subgraphs/5XqPmWe6gjyrJtFn9cLy237i4cWw2j9HcUJEXsP5qGtH?view=Query&chain=arbitrum-one ENS_SUBGRAPH_URL_MAINNET=https://api.thegraph.com/subgraphs/name/ensdomains/ens ENS_SUBGRAPH_URL_SEPOLIA=https://api.studio.thegraph.com/query/49574/enssepolia/version/latest + +# Use ENSNode API (default is true) +USE_ENSNODE_API=true \ No newline at end of file diff --git a/packages/nameguard-python/nameguard/nameguard.py b/packages/nameguard-python/nameguard/nameguard.py index 90139e48f..7ce075fd7 100644 --- a/packages/nameguard-python/nameguard/nameguard.py +++ b/packages/nameguard-python/nameguard/nameguard.py @@ -1,12 +1,13 @@ import os import re -from typing import Union +from typing import Union, Optional from nameguard.models.checks import UNINSPECTED_CHECK_RESULT from nameguard.models.result import UninspectedNameGuardReport from nameguard.our_ens import OurENS from ens_normalize import is_ens_normalized, ens_cure, DisallowedSequence, ens_process +import httpx import requests from label_inspector.inspector import Inspector from label_inspector.config import initialize_inspector_config @@ -59,6 +60,8 @@ ) from nameguard.generic_utils import capitalize_words +USE_ENSNODE_API = os.environ.get('USE_ENSNODE_API', 'true').lower() in ('true', '1', 'yes') + DNA_CHECKS = [ (checks.dna.normalized.check_grapheme, checks.dna.normalized.check_label, checks.dna.normalized.check_name), (None, checks.dna.punycode.check_label, checks.dna.punycode.check_name), @@ -447,11 +450,34 @@ def _inspect_confusable(self, grapheme: InspectorConfusableGraphemeResult) -> Co is_canonical=False, ) + async def get_primary_name(self, address: str, network_name: NetworkName) -> Optional[str]: + # Check environment variable to determine whether to use ENSNode API or RPC + # Default is True (use ENSNode API) + + if not USE_ENSNODE_API: + return self.ns[network_name].name(address) + + if network_name == NetworkName.MAINNET: + url = f'https://api.alpha.ensnode.io/api/resolve/primary-name/{address}/1' + elif network_name == NetworkName.SEPOLIA: + url = f'http://api.alpha-sepolia.ensnode.io/api/resolve/primary-name/{address}/11155111' + else: + return self.ns[network_name].name(address) + + async with httpx.AsyncClient() as client: + response = await client.get(url, params={'accelerate': 'true'}) + if response.status_code == 404: + return None + response.raise_for_status() + return response.json().get('name') + async def secure_primary_name( self, address: str, network_name: str, return_nameguard_report: bool = False ) -> SecurePrimaryNameResult: try: - domain = self.ns[network_name].name(address) + domain = await self.get_primary_name(address, network_name) + except (httpx.RequestError, httpx.HTTPStatusError) as ex: + raise ProviderUnavailable(f'Communication error with ENSNode API occurred: {ex}') except requests.exceptions.ConnectionError as ex: raise ProviderUnavailable(f'Communication error with provider occurred: {ex}') except ContractLogicError: From 127a5f7a99b1d4eb2549645302466435aaa01071 Mon Sep 17 00:00:00 2001 From: djstrong Date: Sun, 4 Jan 2026 23:49:55 +0100 Subject: [PATCH 02/10] refactor(nameguard): Remove OurENS class and update primary name handling to use ENSNode API exclusively --- apps/api.nameai.io/README.md | 4 +- packages/nameguard-python/README.md | 6 +- .../nameguard/models/result.py | 6 +- .../nameguard-python/nameguard/nameguard.py | 36 ++- .../nameguard-python/nameguard/our_ens.py | 205 ------------------ .../nameguard-python/nameguard/web_api.py | 5 +- packages/nameguard-python/tests/conftest.py | 16 +- ...56ee0e8c250f88f5e0edd7c723dc3a0bf75cf.json | 1 - ...dfd75d33c05025074fd7845848d44966ab367.json | 1 - ...ec0a91d420c257ae3ee471b79b1a6a0312e36.json | 1 - ...d9b1b2d8f2cd9ba06c925506627883f97b97c.json | 1 - ...160a23b32402ad24ed5a617b8a83f434642d4.json | 1 - ...3cde891a76416ec9d1c3354b8efe550bd4e20.json | 1 - ...2572997da4948063e3fc11c2552eb82f7208e.json | 1 - ...38f6c83d7d2c46723b727ce794f9c79cc47e6.json | 1 - ...1405429c3bc91e52707a21754cdacecbb035e.json | 1 - ...598bc5bb554b6a15a96d19954b041c9fdbf14.json | 1 - ...7a27f31d7a014c5b8008a0069c61f827fa7a1.json | 1 - ...a134f997b3d48e122d043e12d04e909b11073.json | 1 - ...6ee0e8c250f88f5e0edd7c723dc3a0bf75cf.json} | 0 ...dfd75d33c05025074fd7845848d44966ab367.json | 1 + ...1d1d0020daea8039e46cf1367962070d77da9.json | 1 + ...3f5843ad57d756097ef102a2886f05c7a29c.json} | 0 ...ec0a91d420c257ae3ee471b79b1a6a0312e36.json | 1 + ...d9b1b2d8f2cd9ba06c925506627883f97b97c.json | 1 + ...160a23b32402ad24ed5a617b8a83f434642d4.json | 1 + ...3cde891a76416ec9d1c3354b8efe550bd4e20.json | 1 + ...e6dd8eace27045d9e017c8cf6daa9d08c776.json} | 0 ...63d67e1083ee1becbdd277cbbff1c1ccb631.json} | 0 ...2572997da4948063e3fc11c2552eb82f7208e.json | 1 + ...38f6c83d7d2c46723b727ce794f9c79cc47e6.json | 1 + ...1405429c3bc91e52707a21754cdacecbb035e.json | 1 + ...598bc5bb554b6a15a96d19954b041c9fdbf14.json | 1 + ...6bf26964af9d7eed9e03e53415d37aa96045.json} | 0 ...a6bf26964af9d7eed9e03e53415d37aa96046.json | 1 + ...d9c75da65d507cfcd5ff0acb73d40d3a3bcb.json} | 0 ...7a27f31d7a014c5b8008a0069c61f827fa7a1.json | 1 + ...a134f997b3d48e122d043e12d04e909b11073.json | 1 + packages/nameguard-python/tests/test_api.py | 77 +++---- .../nameguard-python/tests/test_nameguard.py | 59 ++++- packages/nameguard-sdk/src/index.test.ts | 31 +-- packages/nameguard-sdk/src/index.ts | 9 +- 42 files changed, 143 insertions(+), 336 deletions(-) delete mode 100644 packages/nameguard-python/nameguard/our_ens.py delete mode 100644 packages/nameguard-python/tests/data/name__0x0d756ee0e8c250f88f5e0edd7c723dc3a0bf75cf.json delete mode 100644 packages/nameguard-python/tests/data/name__0x0ebdfd75d33c05025074fd7845848d44966ab367.json delete mode 100644 packages/nameguard-python/tests/data/name__0x744ec0a91d420c257ae3ee471b79b1a6a0312e36.json delete mode 100644 packages/nameguard-python/tests/data/name__0x76fd9b1b2d8f2cd9ba06c925506627883f97b97c.json delete mode 100644 packages/nameguard-python/tests/data/name__0x7c7160a23b32402ad24ed5a617b8a83f434642d4.json delete mode 100644 packages/nameguard-python/tests/data/name__0x7da3cde891a76416ec9d1c3354b8efe550bd4e20.json delete mode 100644 packages/nameguard-python/tests/data/name__0x9d32572997da4948063e3fc11c2552eb82f7208e.json delete mode 100644 packages/nameguard-python/tests/data/name__0xaf738f6c83d7d2c46723b727ce794f9c79cc47e6.json delete mode 100644 packages/nameguard-python/tests/data/name__0xb281405429c3bc91e52707a21754cdacecbb035e.json delete mode 100644 packages/nameguard-python/tests/data/name__0xc9f598bc5bb554b6a15a96d19954b041c9fdbf14.json delete mode 100644 packages/nameguard-python/tests/data/name__0xf537a27f31d7a014c5b8008a0069c61f827fa7a1.json delete mode 100644 packages/nameguard-python/tests/data/name__0xfa9a134f997b3d48e122d043e12d04e909b11073.json rename packages/nameguard-python/tests/data/{name__0xd8da6bf26964af9d7eed9e03e53415d37aa96046.json => primary_name__0x0d756ee0e8c250f88f5e0edd7c723dc3a0bf75cf.json} (100%) create mode 100644 packages/nameguard-python/tests/data/primary_name__0x0ebdfd75d33c05025074fd7845848d44966ab367.json create mode 100644 packages/nameguard-python/tests/data/primary_name__0x2211d1d0020daea8039e46cf1367962070d77da9.json rename packages/nameguard-python/tests/data/{name__0x63a93f5843ad57d756097ef102a2886f05c7a29c.json => primary_name__0x63a93f5843ad57d756097ef102a2886f05c7a29c.json} (100%) create mode 100644 packages/nameguard-python/tests/data/primary_name__0x744ec0a91d420c257ae3ee471b79b1a6a0312e36.json create mode 100644 packages/nameguard-python/tests/data/primary_name__0x76fd9b1b2d8f2cd9ba06c925506627883f97b97c.json create mode 100644 packages/nameguard-python/tests/data/primary_name__0x7c7160a23b32402ad24ed5a617b8a83f434642d4.json create mode 100644 packages/nameguard-python/tests/data/primary_name__0x7da3cde891a76416ec9d1c3354b8efe550bd4e20.json rename packages/nameguard-python/tests/data/{name__0x8ae0e6dd8eace27045d9e017c8cf6daa9d08c776.json => primary_name__0x8ae0e6dd8eace27045d9e017c8cf6daa9d08c776.json} (100%) rename packages/nameguard-python/tests/data/{name__0x8b7863d67e1083ee1becbdd277cbbff1c1ccb631.json => primary_name__0x8b7863d67e1083ee1becbdd277cbbff1c1ccb631.json} (100%) create mode 100644 packages/nameguard-python/tests/data/primary_name__0x9d32572997da4948063e3fc11c2552eb82f7208e.json create mode 100644 packages/nameguard-python/tests/data/primary_name__0xaf738f6c83d7d2c46723b727ce794f9c79cc47e6.json create mode 100644 packages/nameguard-python/tests/data/primary_name__0xb281405429c3bc91e52707a21754cdacecbb035e.json create mode 100644 packages/nameguard-python/tests/data/primary_name__0xc9f598bc5bb554b6a15a96d19954b041c9fdbf14.json rename packages/nameguard-python/tests/data/{name__0xd8da6bf26964af9d7eed9e03e53415d37aa96045.json => primary_name__0xd8da6bf26964af9d7eed9e03e53415d37aa96045.json} (100%) create mode 100644 packages/nameguard-python/tests/data/primary_name__0xd8da6bf26964af9d7eed9e03e53415d37aa96046.json rename packages/nameguard-python/tests/data/{name__0xf4a4d9c75da65d507cfcd5ff0acb73d40d3a3bcb.json => primary_name__0xf4a4d9c75da65d507cfcd5ff0acb73d40d3a3bcb.json} (100%) create mode 100644 packages/nameguard-python/tests/data/primary_name__0xf537a27f31d7a014c5b8008a0069c61f827fa7a1.json create mode 100644 packages/nameguard-python/tests/data/primary_name__0xfa9a134f997b3d48e122d043e12d04e909b11073.json diff --git a/apps/api.nameai.io/README.md b/apps/api.nameai.io/README.md index 607e9c08f..95bc77673 100644 --- a/apps/api.nameai.io/README.md +++ b/apps/api.nameai.io/README.md @@ -78,8 +78,6 @@ Ensure that Docker and the AWS CLI are installed and configured on your machine. You need to set up the following environment variables before running the script: -- `PROVIDER_URI_MAINNET` -- `PROVIDER_URI_SEPOLIA` - `ALCHEMY_URI_MAINNET` - `ALCHEMY_URI_SEPOLIA` - `ENS_SUBGRAPH_URL_MAINNET` @@ -93,7 +91,7 @@ If you are using GitHub Actions for deployment, you need to configure the follow - `AWS_ROLE` - The IAM role to assume for AWS actions. - `AWS_REGION` - The AWS region where your resources are located. -- `PROVIDER_URI_MAINNET`, `PROVIDER_URI_SEPOLIA`, `ALCHEMY_URI_MAINNET`, `ALCHEMY_URI_SEPOLIA`, `ENS_SUBGRAPH_URL_MAINNET`, `ENS_SUBGRAPH_URL_SEPOLIA` - The respective URIs for your application. +- `ALCHEMY_URI_MAINNET`, `ALCHEMY_URI_SEPOLIA`, `ENS_SUBGRAPH_URL_MAINNET`, `ENS_SUBGRAPH_URL_SEPOLIA` - The respective URIs for your application. - `CERTIFICATE_NAME` - The name of the ACM certificate. - `HOSTED_ZONE_NAME` - The name of your Route 53 hosted zone. - `PROD_DOMAIN_NAME` - The production domain name. diff --git a/packages/nameguard-python/README.md b/packages/nameguard-python/README.md index ad0e8fe4d..0ee6d1d29 100644 --- a/packages/nameguard-python/README.md +++ b/packages/nameguard-python/README.md @@ -75,11 +75,11 @@ See the [NameGuard Python README](./apps/api.nameguard.io/README.md) for more de ### Environment Variables -NameGuard uses the specified Provider endpoints (e.g. Alchemy, Infura, your own Ethereum node, etc...) for `secure-primary-name/`. Provider endpoints have to be set by environment variables, e.g.: +NameGuard uses the ENSNode API for primary name lookups in `secure-primary-name`. The ENSNode API only returns normalized primary names, so unnormalized primary names are treated as having no primary name. + +For `fake-eth-name-check`, NameGuard uses Alchemy endpoints. Alchemy endpoints have to be set by environment variables, e.g.: ```bash -export PROVIDER_URI_MAINNET=https://eth-mainnet.g.alchemy.com/v2/[YOUR_ALCHEMY_API_KEY] -export PROVIDER_URI_SEPOLIA=https://eth-sepolia.g.alchemy.com/v2/[YOUR_ALCHEMY_API_KEY] export ALCHEMY_URI_MAINNET=https://eth-mainnet.g.alchemy.com/v2/[YOUR_ALCHEMY_API_KEY] export ALCHEMY_URI_SEPOLIA=https://eth-sepolia.g.alchemy.com/v2/[YOUR_ALCHEMY_API_KEY] export ENS_SUBGRAPH_URL_MAINNET="https://gateway-arbitrum.network.thegraph.com/api/[YOUR_SUBGRAPH_API_KEY]/subgraphs/id/5XqPmWe6gjyrJtFn9cLy237i4cWw2j9HcUJEXsP5qGtH" diff --git a/packages/nameguard-python/nameguard/models/result.py b/packages/nameguard-python/nameguard/models/result.py index 4432a9f5e..e0b0de05e 100644 --- a/packages/nameguard-python/nameguard/models/result.py +++ b/packages/nameguard-python/nameguard/models/result.py @@ -344,14 +344,14 @@ class SecurePrimaryNameStatus(str, Enum): The status of a secure primary ENS name lookup performed by NameGuard. * `normalized`: The ENS primary name was found and it is normalized. - * `no_primary_name`: The ENS primary name was not found. - * `unnormalized`: The ENS primary name was found, but it is not normalized. + * `no_primary_name`: The ENS primary name was not found, or the primary name is unnormalized. + The ENSNode API only returns normalized primary names, so unnormalized primary names + are treated as having no primary name. * `uninspected`: The name was exceptionally long and was not inspected for performance reasons. """ NORMALIZED = 'normalized' NO_PRIMARY_NAME = 'no_primary_name' - UNNORMALIZED = 'unnormalized' UNINSPECTED = 'uninspected' diff --git a/packages/nameguard-python/nameguard/nameguard.py b/packages/nameguard-python/nameguard/nameguard.py index 7ce075fd7..481f87e52 100644 --- a/packages/nameguard-python/nameguard/nameguard.py +++ b/packages/nameguard-python/nameguard/nameguard.py @@ -4,7 +4,6 @@ from nameguard.models.checks import UNINSPECTED_CHECK_RESULT from nameguard.models.result import UninspectedNameGuardReport -from nameguard.our_ens import OurENS from ens_normalize import is_ens_normalized, ens_cure, DisallowedSequence, ens_process import httpx @@ -12,7 +11,6 @@ from label_inspector.inspector import Inspector from label_inspector.config import initialize_inspector_config from label_inspector.models import InspectorConfusableGraphemeResult, InspectorResult -from web3 import HTTPProvider from web3.exceptions import ContractLogicError from dotenv import load_dotenv @@ -60,8 +58,6 @@ ) from nameguard.generic_utils import capitalize_words -USE_ENSNODE_API = os.environ.get('USE_ENSNODE_API', 'true').lower() in ('true', '1', 'yes') - DNA_CHECKS = [ (checks.dna.normalized.check_grapheme, checks.dna.normalized.check_label, checks.dna.normalized.check_name), (None, checks.dna.punycode.check_label, checks.dna.punycode.check_name), @@ -172,15 +168,6 @@ class NameGuard: def __init__(self): self._inspector = init_inspector() load_dotenv() - # TODO use web sockets and async - self.ns = {} - for network_name, env_var in ( - (NetworkName.MAINNET, 'PROVIDER_URI_MAINNET'), - (NetworkName.SEPOLIA, 'PROVIDER_URI_SEPOLIA'), - ): - if os.environ.get(env_var) is None: - logger.warning(f'Environment variable {env_var} is not set') - self.ns[network_name] = OurENS(HTTPProvider(os.environ.get(env_var))) # optimization self.eth_label = self._inspector.analyse_label('eth', simple_confusables=True, omit_cure=True) @@ -451,18 +438,26 @@ def _inspect_confusable(self, grapheme: InspectorConfusableGraphemeResult) -> Co ) async def get_primary_name(self, address: str, network_name: NetworkName) -> Optional[str]: - # Check environment variable to determine whether to use ENSNode API or RPC - # Default is True (use ENSNode API) + """ + Looks up the primary ENS name for an address using the ENSNode API. + + The ENSNode API only returns normalized primary names. If the primary name + is unnormalized, this method returns None. - if not USE_ENSNODE_API: - return self.ns[network_name].name(address) + Args: + address: The Ethereum address to look up + network_name: The network to query (MAINNET or SEPOLIA) + Returns: + The normalized primary name, or None if no primary name exists or + the primary name is unnormalized. + """ if network_name == NetworkName.MAINNET: url = f'https://api.alpha.ensnode.io/api/resolve/primary-name/{address}/1' elif network_name == NetworkName.SEPOLIA: url = f'http://api.alpha-sepolia.ensnode.io/api/resolve/primary-name/{address}/11155111' else: - return self.ns[network_name].name(address) + raise ValueError(f'Unsupported network: {network_name}') async with httpx.AsyncClient() as client: response = await client.get(url, params={'accelerate': 'true'}) @@ -488,17 +483,16 @@ async def secure_primary_name( primary_name = None nameguard_report = None if domain is None: + # ENSNode API returns None for addresses with no primary name or unnormalized primary names status = SecurePrimaryNameStatus.NO_PRIMARY_NAME impersonation_estimate = None else: + # If ENSNode API returns a name, it's guaranteed to be normalized nameguard_report = await self.inspect_name(network_name, domain) if nameguard_report.highest_risk and nameguard_report.highest_risk.check.name == Check.UNINSPECTED.name: status = SecurePrimaryNameStatus.UNINSPECTED impersonation_estimate = None - elif nameguard_report.normalization == Normalization.UNNORMALIZED: - status = SecurePrimaryNameStatus.UNNORMALIZED - impersonation_estimate = None else: display_name = nameguard_report.beautiful_name status = SecurePrimaryNameStatus.NORMALIZED diff --git a/packages/nameguard-python/nameguard/our_ens.py b/packages/nameguard-python/nameguard/our_ens.py deleted file mode 100644 index dabef27e3..000000000 --- a/packages/nameguard-python/nameguard/our_ens.py +++ /dev/null @@ -1,205 +0,0 @@ -# The MIT License (MIT) -# -# Copyright (c) 2016 The Ethereum Foundation -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in all -# copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. - -# import json -from functools import wraps -from typing import Optional, Union, Tuple, cast - -from ens.constants import ENS_EXTENDED_RESOLVER_INTERFACE_ID, EMPTY_SHA3_BYTES -from ens.ens import _resolver_supports_interface, ENS -from ens.exceptions import ENSValidationError, ENSValueError -from ens.utils import address_to_reverse_domain, is_empty_name, is_none_or_zero_address, Web3 - -# from ens.utils import normalize_name -from eth_typing import ChecksumAddress -from eth_utils import to_checksum_address, is_address, to_bytes -from hexbytes import HexBytes -from web3.contract import Contract - - -def label_to_hash(label: str) -> HexBytes: - # label = normalize_name(label) - if '.' in label: - raise ENSValueError(f"Cannot generate hash for label {label!r} with a '.'") - return Web3().keccak(text=label) - - -def normal_name_to_hash(name: str) -> HexBytes: - """ - Hashes a pre-normalized name. - The normalization of the name is a prerequisite and is not handled by this function. - - :param str name: A normalized name string to be hashed. - :return: namehash - the hash of the name - :rtype: HexBytes - """ - node = EMPTY_SHA3_BYTES - if not is_empty_name(name): - labels = name.split('.') - for label in reversed(labels): - labelhash = label_to_hash(label) - assert isinstance(labelhash, bytes) - assert isinstance(node, bytes) - node = Web3().keccak(node + labelhash) - return node - - -def raw_name_to_hash(name: str) -> HexBytes: - """ - Generate the namehash. This is also known as the ``node`` in ENS contracts. - - In normal operation, generating the namehash is handled - behind the scenes. For advanced usage, it is a helpful utility. - - This normalizes the name with `nameprep - `_ # blocklint: pragma # noqa: E501 - before hashing. - - :param str name: ENS name to hash - :return: the namehash - :rtype: bytes - :raises InvalidName: if ``name`` has invalid syntax - """ - # normalized_name = normalize_name(name) - normalized_name = name - return normal_name_to_hash(normalized_name) - - -def ens_encode_name(name: str) -> bytes: - r""" - Encode a name according to DNS standards specified in section 3.1 - of RFC1035 with the following validations: - - - There is no limit on the total length of the encoded name - and the limit on labels is the ENS standard of 255. - - - Return a single 0-octet, b'\x00', if empty name. - - :param str name: the dot-separated ENS name - """ - if is_empty_name(name): - return b'\x00' - - # normalized_name = normalize_name(name) - normalized_name = name - - labels = normalized_name.split('.') - labels_as_bytes = [to_bytes(text=label) for label in labels] - - # raises if len(label) > 255: - for index, label in enumerate(labels): - if len(label) > 255: - raise ENSValidationError(f'Label at position {index} too long after encoding.') - - # concat label size in bytes to each label: - dns_prepped_labels = [to_bytes(len(label)) + label for label in labels_as_bytes] - - # return the joined prepped labels in order and append the zero byte at the end: - return b''.join(dns_prepped_labels) + b'\x00' - - -class OurENS(ENS): - """ - Name normalization is disabled. - """ - - def name(self, address: ChecksumAddress) -> Optional[str]: - """ - Look up the name that the address points to, using a - reverse lookup. Reverse lookup is opt-in for name owners. - - :param address: - :type address: hex-string - """ - reversed_domain = address_to_reverse_domain(address) - name = self._resolve(reversed_domain, fn_name='name') - - # To be absolutely certain of the name, via reverse resolution, - # the address must match in the forward resolution - return name if to_checksum_address(address) == self.address(name) else None - - def resolver(self, name: str) -> Optional['Contract']: - """ - Get the resolver for an ENS name. - - :param str name: The ENS name - """ - # normal_name = normalize_name(name) - normal_name = name - return self._get_resolver(normal_name)[0] - - @staticmethod - @wraps(raw_name_to_hash) - def namehash(name: str) -> HexBytes: - return raw_name_to_hash(name) - - def _resolve(self, name: str, fn_name: str = 'addr') -> Optional[Union[ChecksumAddress, str]]: # OK - # normal_name = normalize_name(name) - normal_name = name - resolver, current_name = self._get_resolver(normal_name, fn_name) - if not resolver: - return None - - node = self.namehash(normal_name) - - # handle extended resolver case - if _resolver_supports_interface(resolver, ENS_EXTENDED_RESOLVER_INTERFACE_ID): - contract_func_with_args = (fn_name, [node]) - - calldata = resolver.encode_abi(*contract_func_with_args) - contract_call_result = resolver.caller.resolve( - ens_encode_name(normal_name), - calldata, - ) - result = self._decode_ensip10_resolve_data(contract_call_result, resolver, fn_name) - return to_checksum_address(result) if is_address(result) else result - elif normal_name == current_name: - lookup_function = getattr(resolver.functions, fn_name) - result = lookup_function(node).call() - if is_none_or_zero_address(result): - return None - return to_checksum_address(result) if is_address(result) else result - return None - - def _get_resolver( - self, - normal_name: str, - fn_name: str = 'addr', - ) -> Tuple[Optional['Contract'], str]: - current_name = normal_name - - # look for a resolver, starting at the full name and taking the parent - # each time that no resolver is found - while True: - if is_empty_name(current_name): - # if no resolver found across all iterations, current_name - # will eventually be the empty string '' which returns here - return None, current_name - - resolver_addr = self.ens.caller.resolver(normal_name_to_hash(current_name)) - if not is_none_or_zero_address(resolver_addr): - # if resolver found, return it - resolver = cast('Contract', self._type_aware_resolver(resolver_addr, fn_name)) - return resolver, current_name - - # set current_name to parent and try again - current_name = self.parent(current_name) diff --git a/packages/nameguard-python/nameguard/web_api.py b/packages/nameguard-python/nameguard/web_api.py index 2fa52ada3..f5ae3fab5 100644 --- a/packages/nameguard-python/nameguard/web_api.py +++ b/packages/nameguard-python/nameguard/web_api.py @@ -494,9 +494,8 @@ async def secure_primary_name_get( """ ## Performs a reverse lookup of an Ethereum `address` to a primary name. - Data sources for the primary name lookup include: - 1. The Ethereum Provider configured in the NameGuard instance. - 2. For ENS names using CCIP-Read: requests to externally defined gateway servers. + The primary name lookup uses the ENSNode API, which only returns normalized primary names. + If an address has an unnormalized primary name, it will be treated as having no primary name. Returns `display_name` to be shown to users and estimates `impersonation_estimate`. diff --git a/packages/nameguard-python/tests/conftest.py b/packages/nameguard-python/tests/conftest.py index 210fb1d99..40306b1bf 100644 --- a/packages/nameguard-python/tests/conftest.py +++ b/packages/nameguard-python/tests/conftest.py @@ -46,20 +46,22 @@ async def mock_get_nft_metadata(contract_address: str, token_id: str) -> dict: monkeypatch.setattr('nameguard.nameguard.get_nft_metadata', mock_get_nft_metadata) - original_name = nameguard.nameguard.OurENS.name + original_get_primary_name = nameguard.nameguard.NameGuard.get_primary_name - def mock_name(self, address): + async def mock_get_primary_name(self, address: str, network_name): try: - return json.load(open(f'{TESTS_DATA_PATH}/name__{address}.json')) + return json.load(open(f'{TESTS_DATA_PATH}/primary_name__{address.lower()}.json')) except FileNotFoundError: - result = original_name(self, address) + result = await original_get_primary_name(self, address, network_name) json.dump( - result, open(f'{TESTS_DATA_PATH}/NEW-name__{address}.json', 'w'), indent=2, ensure_ascii=False + result, + open(f'{TESTS_DATA_PATH}/NEW-primary_name__{address.lower()}.json', 'w'), + indent=2, + ensure_ascii=False, ) raise - # monkeypatch.setattr('nameguard.our_ens.OurENS.name', mock_name) - monkeypatch.setattr('nameguard.nameguard.OurENS.name', mock_name) + monkeypatch.setattr('nameguard.nameguard.NameGuard.get_primary_name', mock_get_primary_name) original_call_subgraph = nameguard.subgraph.call_subgraph diff --git a/packages/nameguard-python/tests/data/name__0x0d756ee0e8c250f88f5e0edd7c723dc3a0bf75cf.json b/packages/nameguard-python/tests/data/name__0x0d756ee0e8c250f88f5e0edd7c723dc3a0bf75cf.json deleted file mode 100644 index 06d40c591..000000000 --- a/packages/nameguard-python/tests/data/name__0x0d756ee0e8c250f88f5e0edd7c723dc3a0bf75cf.json +++ /dev/null @@ -1 +0,0 @@ -"сбер.eth" \ No newline at end of file diff --git a/packages/nameguard-python/tests/data/name__0x0ebdfd75d33c05025074fd7845848d44966ab367.json b/packages/nameguard-python/tests/data/name__0x0ebdfd75d33c05025074fd7845848d44966ab367.json deleted file mode 100644 index 16aa8a8b2..000000000 --- a/packages/nameguard-python/tests/data/name__0x0ebdfd75d33c05025074fd7845848d44966ab367.json +++ /dev/null @@ -1 +0,0 @@ -"۸۸۷۵۴۲.eth" \ No newline at end of file diff --git a/packages/nameguard-python/tests/data/name__0x744ec0a91d420c257ae3ee471b79b1a6a0312e36.json b/packages/nameguard-python/tests/data/name__0x744ec0a91d420c257ae3ee471b79b1a6a0312e36.json deleted file mode 100644 index 03078d635..000000000 --- a/packages/nameguard-python/tests/data/name__0x744ec0a91d420c257ae3ee471b79b1a6a0312e36.json +++ /dev/null @@ -1 +0,0 @@ -"hello!.eth" \ No newline at end of file diff --git a/packages/nameguard-python/tests/data/name__0x76fd9b1b2d8f2cd9ba06c925506627883f97b97c.json b/packages/nameguard-python/tests/data/name__0x76fd9b1b2d8f2cd9ba06c925506627883f97b97c.json deleted file mode 100644 index f0193cb2b..000000000 --- a/packages/nameguard-python/tests/data/name__0x76fd9b1b2d8f2cd9ba06c925506627883f97b97c.json +++ /dev/null @@ -1 +0,0 @@ -"‍‍❤‍‍.eth" \ No newline at end of file diff --git a/packages/nameguard-python/tests/data/name__0x7c7160a23b32402ad24ed5a617b8a83f434642d4.json b/packages/nameguard-python/tests/data/name__0x7c7160a23b32402ad24ed5a617b8a83f434642d4.json deleted file mode 100644 index 38eb973cb..000000000 --- a/packages/nameguard-python/tests/data/name__0x7c7160a23b32402ad24ed5a617b8a83f434642d4.json +++ /dev/null @@ -1 +0,0 @@ -"vincξnt.eth" \ No newline at end of file diff --git a/packages/nameguard-python/tests/data/name__0x7da3cde891a76416ec9d1c3354b8efe550bd4e20.json b/packages/nameguard-python/tests/data/name__0x7da3cde891a76416ec9d1c3354b8efe550bd4e20.json deleted file mode 100644 index 5a1e00fc6..000000000 --- a/packages/nameguard-python/tests/data/name__0x7da3cde891a76416ec9d1c3354b8efe550bd4e20.json +++ /dev/null @@ -1 +0,0 @@ -"vitȧlik.eth" \ No newline at end of file diff --git a/packages/nameguard-python/tests/data/name__0x9d32572997da4948063e3fc11c2552eb82f7208e.json b/packages/nameguard-python/tests/data/name__0x9d32572997da4948063e3fc11c2552eb82f7208e.json deleted file mode 100644 index 560c2c6c2..000000000 --- a/packages/nameguard-python/tests/data/name__0x9d32572997da4948063e3fc11c2552eb82f7208e.json +++ /dev/null @@ -1 +0,0 @@ -"poet.base.eth" \ No newline at end of file diff --git a/packages/nameguard-python/tests/data/name__0xaf738f6c83d7d2c46723b727ce794f9c79cc47e6.json b/packages/nameguard-python/tests/data/name__0xaf738f6c83d7d2c46723b727ce794f9c79cc47e6.json deleted file mode 100644 index d17efd43a..000000000 --- a/packages/nameguard-python/tests/data/name__0xaf738f6c83d7d2c46723b727ce794f9c79cc47e6.json +++ /dev/null @@ -1 +0,0 @@ -"୨୨୨୨୨.eth" \ No newline at end of file diff --git a/packages/nameguard-python/tests/data/name__0xb281405429c3bc91e52707a21754cdacecbb035e.json b/packages/nameguard-python/tests/data/name__0xb281405429c3bc91e52707a21754cdacecbb035e.json deleted file mode 100644 index 61244c056..000000000 --- a/packages/nameguard-python/tests/data/name__0xb281405429c3bc91e52707a21754cdacecbb035e.json +++ /dev/null @@ -1 +0,0 @@ -"┣▇▇▇═─.eth" \ No newline at end of file diff --git a/packages/nameguard-python/tests/data/name__0xc9f598bc5bb554b6a15a96d19954b041c9fdbf14.json b/packages/nameguard-python/tests/data/name__0xc9f598bc5bb554b6a15a96d19954b041c9fdbf14.json deleted file mode 100644 index a7bb1543d..000000000 --- a/packages/nameguard-python/tests/data/name__0xc9f598bc5bb554b6a15a96d19954b041c9fdbf14.json +++ /dev/null @@ -1 +0,0 @@ -"vıtalik.eth" \ No newline at end of file diff --git a/packages/nameguard-python/tests/data/name__0xf537a27f31d7a014c5b8008a0069c61f827fa7a1.json b/packages/nameguard-python/tests/data/name__0xf537a27f31d7a014c5b8008a0069c61f827fa7a1.json deleted file mode 100644 index 616459ebb..000000000 --- a/packages/nameguard-python/tests/data/name__0xf537a27f31d7a014c5b8008a0069c61f827fa7a1.json +++ /dev/null @@ -1 +0,0 @@ -"٠٠۱.eth" \ No newline at end of file diff --git a/packages/nameguard-python/tests/data/name__0xfa9a134f997b3d48e122d043e12d04e909b11073.json b/packages/nameguard-python/tests/data/name__0xfa9a134f997b3d48e122d043e12d04e909b11073.json deleted file mode 100644 index 52ba0c2cf..000000000 --- a/packages/nameguard-python/tests/data/name__0xfa9a134f997b3d48e122d043e12d04e909b11073.json +++ /dev/null @@ -1 +0,0 @@ -"888‍‍.eth" \ No newline at end of file diff --git a/packages/nameguard-python/tests/data/name__0xd8da6bf26964af9d7eed9e03e53415d37aa96046.json b/packages/nameguard-python/tests/data/primary_name__0x0d756ee0e8c250f88f5e0edd7c723dc3a0bf75cf.json similarity index 100% rename from packages/nameguard-python/tests/data/name__0xd8da6bf26964af9d7eed9e03e53415d37aa96046.json rename to packages/nameguard-python/tests/data/primary_name__0x0d756ee0e8c250f88f5e0edd7c723dc3a0bf75cf.json diff --git a/packages/nameguard-python/tests/data/primary_name__0x0ebdfd75d33c05025074fd7845848d44966ab367.json b/packages/nameguard-python/tests/data/primary_name__0x0ebdfd75d33c05025074fd7845848d44966ab367.json new file mode 100644 index 000000000..ec747fa47 --- /dev/null +++ b/packages/nameguard-python/tests/data/primary_name__0x0ebdfd75d33c05025074fd7845848d44966ab367.json @@ -0,0 +1 @@ +null \ No newline at end of file diff --git a/packages/nameguard-python/tests/data/primary_name__0x2211d1d0020daea8039e46cf1367962070d77da9.json b/packages/nameguard-python/tests/data/primary_name__0x2211d1d0020daea8039e46cf1367962070d77da9.json new file mode 100644 index 000000000..0d1a8b339 --- /dev/null +++ b/packages/nameguard-python/tests/data/primary_name__0x2211d1d0020daea8039e46cf1367962070d77da9.json @@ -0,0 +1 @@ +"jesse.base.eth" \ No newline at end of file diff --git a/packages/nameguard-python/tests/data/name__0x63a93f5843ad57d756097ef102a2886f05c7a29c.json b/packages/nameguard-python/tests/data/primary_name__0x63a93f5843ad57d756097ef102a2886f05c7a29c.json similarity index 100% rename from packages/nameguard-python/tests/data/name__0x63a93f5843ad57d756097ef102a2886f05c7a29c.json rename to packages/nameguard-python/tests/data/primary_name__0x63a93f5843ad57d756097ef102a2886f05c7a29c.json diff --git a/packages/nameguard-python/tests/data/primary_name__0x744ec0a91d420c257ae3ee471b79b1a6a0312e36.json b/packages/nameguard-python/tests/data/primary_name__0x744ec0a91d420c257ae3ee471b79b1a6a0312e36.json new file mode 100644 index 000000000..ec747fa47 --- /dev/null +++ b/packages/nameguard-python/tests/data/primary_name__0x744ec0a91d420c257ae3ee471b79b1a6a0312e36.json @@ -0,0 +1 @@ +null \ No newline at end of file diff --git a/packages/nameguard-python/tests/data/primary_name__0x76fd9b1b2d8f2cd9ba06c925506627883f97b97c.json b/packages/nameguard-python/tests/data/primary_name__0x76fd9b1b2d8f2cd9ba06c925506627883f97b97c.json new file mode 100644 index 000000000..ec747fa47 --- /dev/null +++ b/packages/nameguard-python/tests/data/primary_name__0x76fd9b1b2d8f2cd9ba06c925506627883f97b97c.json @@ -0,0 +1 @@ +null \ No newline at end of file diff --git a/packages/nameguard-python/tests/data/primary_name__0x7c7160a23b32402ad24ed5a617b8a83f434642d4.json b/packages/nameguard-python/tests/data/primary_name__0x7c7160a23b32402ad24ed5a617b8a83f434642d4.json new file mode 100644 index 000000000..27dca9a91 --- /dev/null +++ b/packages/nameguard-python/tests/data/primary_name__0x7c7160a23b32402ad24ed5a617b8a83f434642d4.json @@ -0,0 +1 @@ +"pudgyvincent.eth" \ No newline at end of file diff --git a/packages/nameguard-python/tests/data/primary_name__0x7da3cde891a76416ec9d1c3354b8efe550bd4e20.json b/packages/nameguard-python/tests/data/primary_name__0x7da3cde891a76416ec9d1c3354b8efe550bd4e20.json new file mode 100644 index 000000000..ec747fa47 --- /dev/null +++ b/packages/nameguard-python/tests/data/primary_name__0x7da3cde891a76416ec9d1c3354b8efe550bd4e20.json @@ -0,0 +1 @@ +null \ No newline at end of file diff --git a/packages/nameguard-python/tests/data/name__0x8ae0e6dd8eace27045d9e017c8cf6daa9d08c776.json b/packages/nameguard-python/tests/data/primary_name__0x8ae0e6dd8eace27045d9e017c8cf6daa9d08c776.json similarity index 100% rename from packages/nameguard-python/tests/data/name__0x8ae0e6dd8eace27045d9e017c8cf6daa9d08c776.json rename to packages/nameguard-python/tests/data/primary_name__0x8ae0e6dd8eace27045d9e017c8cf6daa9d08c776.json diff --git a/packages/nameguard-python/tests/data/name__0x8b7863d67e1083ee1becbdd277cbbff1c1ccb631.json b/packages/nameguard-python/tests/data/primary_name__0x8b7863d67e1083ee1becbdd277cbbff1c1ccb631.json similarity index 100% rename from packages/nameguard-python/tests/data/name__0x8b7863d67e1083ee1becbdd277cbbff1c1ccb631.json rename to packages/nameguard-python/tests/data/primary_name__0x8b7863d67e1083ee1becbdd277cbbff1c1ccb631.json diff --git a/packages/nameguard-python/tests/data/primary_name__0x9d32572997da4948063e3fc11c2552eb82f7208e.json b/packages/nameguard-python/tests/data/primary_name__0x9d32572997da4948063e3fc11c2552eb82f7208e.json new file mode 100644 index 000000000..ec747fa47 --- /dev/null +++ b/packages/nameguard-python/tests/data/primary_name__0x9d32572997da4948063e3fc11c2552eb82f7208e.json @@ -0,0 +1 @@ +null \ No newline at end of file diff --git a/packages/nameguard-python/tests/data/primary_name__0xaf738f6c83d7d2c46723b727ce794f9c79cc47e6.json b/packages/nameguard-python/tests/data/primary_name__0xaf738f6c83d7d2c46723b727ce794f9c79cc47e6.json new file mode 100644 index 000000000..ec747fa47 --- /dev/null +++ b/packages/nameguard-python/tests/data/primary_name__0xaf738f6c83d7d2c46723b727ce794f9c79cc47e6.json @@ -0,0 +1 @@ +null \ No newline at end of file diff --git a/packages/nameguard-python/tests/data/primary_name__0xb281405429c3bc91e52707a21754cdacecbb035e.json b/packages/nameguard-python/tests/data/primary_name__0xb281405429c3bc91e52707a21754cdacecbb035e.json new file mode 100644 index 000000000..ec747fa47 --- /dev/null +++ b/packages/nameguard-python/tests/data/primary_name__0xb281405429c3bc91e52707a21754cdacecbb035e.json @@ -0,0 +1 @@ +null \ No newline at end of file diff --git a/packages/nameguard-python/tests/data/primary_name__0xc9f598bc5bb554b6a15a96d19954b041c9fdbf14.json b/packages/nameguard-python/tests/data/primary_name__0xc9f598bc5bb554b6a15a96d19954b041c9fdbf14.json new file mode 100644 index 000000000..ec747fa47 --- /dev/null +++ b/packages/nameguard-python/tests/data/primary_name__0xc9f598bc5bb554b6a15a96d19954b041c9fdbf14.json @@ -0,0 +1 @@ +null \ No newline at end of file diff --git a/packages/nameguard-python/tests/data/name__0xd8da6bf26964af9d7eed9e03e53415d37aa96045.json b/packages/nameguard-python/tests/data/primary_name__0xd8da6bf26964af9d7eed9e03e53415d37aa96045.json similarity index 100% rename from packages/nameguard-python/tests/data/name__0xd8da6bf26964af9d7eed9e03e53415d37aa96045.json rename to packages/nameguard-python/tests/data/primary_name__0xd8da6bf26964af9d7eed9e03e53415d37aa96045.json diff --git a/packages/nameguard-python/tests/data/primary_name__0xd8da6bf26964af9d7eed9e03e53415d37aa96046.json b/packages/nameguard-python/tests/data/primary_name__0xd8da6bf26964af9d7eed9e03e53415d37aa96046.json new file mode 100644 index 000000000..ec747fa47 --- /dev/null +++ b/packages/nameguard-python/tests/data/primary_name__0xd8da6bf26964af9d7eed9e03e53415d37aa96046.json @@ -0,0 +1 @@ +null \ No newline at end of file diff --git a/packages/nameguard-python/tests/data/name__0xf4a4d9c75da65d507cfcd5ff0acb73d40d3a3bcb.json b/packages/nameguard-python/tests/data/primary_name__0xf4a4d9c75da65d507cfcd5ff0acb73d40d3a3bcb.json similarity index 100% rename from packages/nameguard-python/tests/data/name__0xf4a4d9c75da65d507cfcd5ff0acb73d40d3a3bcb.json rename to packages/nameguard-python/tests/data/primary_name__0xf4a4d9c75da65d507cfcd5ff0acb73d40d3a3bcb.json diff --git a/packages/nameguard-python/tests/data/primary_name__0xf537a27f31d7a014c5b8008a0069c61f827fa7a1.json b/packages/nameguard-python/tests/data/primary_name__0xf537a27f31d7a014c5b8008a0069c61f827fa7a1.json new file mode 100644 index 000000000..ec747fa47 --- /dev/null +++ b/packages/nameguard-python/tests/data/primary_name__0xf537a27f31d7a014c5b8008a0069c61f827fa7a1.json @@ -0,0 +1 @@ +null \ No newline at end of file diff --git a/packages/nameguard-python/tests/data/primary_name__0xfa9a134f997b3d48e122d043e12d04e909b11073.json b/packages/nameguard-python/tests/data/primary_name__0xfa9a134f997b3d48e122d043e12d04e909b11073.json new file mode 100644 index 000000000..ec747fa47 --- /dev/null +++ b/packages/nameguard-python/tests/data/primary_name__0xfa9a134f997b3d48e122d043e12d04e909b11073.json @@ -0,0 +1 @@ +null \ No newline at end of file diff --git a/packages/nameguard-python/tests/test_api.py b/packages/nameguard-python/tests/test_api.py index e2809b096..92e0e0e94 100644 --- a/packages/nameguard-python/tests/test_api.py +++ b/packages/nameguard-python/tests/test_api.py @@ -313,11 +313,11 @@ def test_inspect_namehash_get( 'unknown', '[3fddf465ed81d79ae943b35800b1d187dc0b5d69614bf7e8ebddbae19d72cae8].genevaswis.eth', ), - ( - '0x00f52438ae09d2f909ee2efc19ba8af75058e74ca4507aa091bd8282aa490e77', - 'unknown', - '🥛.[2e8eaa68c7e128861299162323c29c29672f5c094aceaf22d9c0935e4bbd3f85].🛸.👽.enspunks.eth', - ), + # ( + # '0x00f52438ae09d2f909ee2efc19ba8af75058e74ca4507aa091bd8282aa490e77', + # 'unknown', + # '🥛.[2e8eaa68c7e128861299162323c29c29672f5c094aceaf22d9c0935e4bbd3f85].🛸.👽.enspunks.eth', + # ), # now it is known pytest.param( '0x1bc53f6413409d078ec18a29b17f981eafab341598a4e970ac9efab7d29258af', 'unnormalized', @@ -387,10 +387,10 @@ def test_inspect_namehash_get_root(test_client): '0xb2636b6e3b1abdd3fbec454d4f4b1a904e7b15e3609cb208bcfc5a5487293308', '[3fddf465ed81d79ae943b35800b1d187dc0b5d69614bf7e8ebddbae19d72cae8].genevaswis.eth', ), - ( - '0x00f52438ae09d2f909ee2efc19ba8af75058e74ca4507aa091bd8282aa490e77', - '[7710d5ebf94bcebcf1996bb7a3f5e24a6d24435b314b3cec815da03640c2940c].[2e8eaa68c7e128861299162323c29c29672f5c094aceaf22d9c0935e4bbd3f85].[a64d2b5a93eda272d27734cc2fb8d1c468562e279f1e97e759eea1a5a410f8e3].[462a1d6391f7ea5916874504f3b5fc8cd43626f6bbabc8a22fe4312dc1585362].enspunks.eth', - ), + # ( + # '0x00f52438ae09d2f909ee2efc19ba8af75058e74ca4507aa091bd8282aa490e77', + # '[7710d5ebf94bcebcf1996bb7a3f5e24a6d24435b314b3cec815da03640c2940c].[2e8eaa68c7e128861299162323c29c29672f5c094aceaf22d9c0935e4bbd3f85].[a64d2b5a93eda272d27734cc2fb8d1c468562e279f1e97e759eea1a5a410f8e3].[462a1d6391f7ea5916874504f3b5fc8cd43626f6bbabc8a22fe4312dc1585362].enspunks.eth', + # ), # now it is known ], ) def test_inspect_namehash_get_unknown(test_client, namehash: str, expected_name: str): @@ -644,14 +644,14 @@ def test_inspect_grapheme_multi(test_client): '٧٣٧.eth', ), ( - '0x9d32572997DA4948063E3Fc11c2552Eb82F7208E', + '0x2211d1D0020DAEA8039E46Cf1367962070d77DA9', 'unlikely', 'normalized', - 'poet.base.eth', - 'poet.base.eth', - 'poet.base.eth', + 'jesse.base.eth', + 'jesse.base.eth', + 'jesse.base.eth', False, - 'poet.base.eth', + 'jesse.base.eth', ), ( '0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96046', @@ -666,7 +666,7 @@ def test_inspect_grapheme_multi(test_client): ( '0xfA9A134f997b3d48e122d043E12d04E909b11073', None, - 'unnormalized', + 'no_primary_name', None, 'Unnamed fa9a', None, @@ -676,7 +676,7 @@ def test_inspect_grapheme_multi(test_client): ( '0x76fd9b1B2d8F2cd9Ba06c925506627883F97B97C', None, - 'unnormalized', + 'no_primary_name', None, 'Unnamed 76fd', None, @@ -686,7 +686,7 @@ def test_inspect_grapheme_multi(test_client): ( '0xf537a27F31d7A014c5b8008a0069c61f827fA7A1', None, - 'unnormalized', + 'no_primary_name', None, 'Unnamed f537', None, @@ -696,7 +696,7 @@ def test_inspect_grapheme_multi(test_client): ( '0x0ebDfD75d33c05025074fd7845848D44966AB367', None, - 'unnormalized', + 'no_primary_name', None, 'Unnamed 0ebd', None, @@ -706,7 +706,7 @@ def test_inspect_grapheme_multi(test_client): ( '0xaf738F6C83d7D2C46723b727Ce794F9c79Cc47E6', None, - 'unnormalized', + 'no_primary_name', None, 'Unnamed af73', None, @@ -716,7 +716,7 @@ def test_inspect_grapheme_multi(test_client): ( '0xb281405429C3bc91e52707a21754cDaCeCbB035E', None, - 'unnormalized', + 'no_primary_name', None, 'Unnamed b281', None, @@ -726,7 +726,7 @@ def test_inspect_grapheme_multi(test_client): ( '0x0d756ee0e8C250f88f5e0eDd7C723dc3A0BF75cF', None, - 'unnormalized', + 'no_primary_name', None, 'Unnamed 0d75', None, @@ -736,7 +736,7 @@ def test_inspect_grapheme_multi(test_client): ( '0x7Da3CdE891a76416ec9D1c3354B8EfE550Bd4e20', None, - 'unnormalized', + 'no_primary_name', None, 'Unnamed 7da3', 'vitalik.eth', @@ -746,7 +746,7 @@ def test_inspect_grapheme_multi(test_client): ( '0xC9f598BC5BB554B6A15A96D19954B041C9FDbF14', None, - 'unnormalized', + 'no_primary_name', None, 'Unnamed c9f5', 'vitalik.eth', @@ -757,16 +757,16 @@ def test_inspect_grapheme_multi(test_client): '0x7c7160A23b32402ad24ED5a617b8a83f434642d4', 'unlikely', 'normalized', - 'vincξnt.eth', - 'vincΞnt.eth', - 'vincξnt.eth', + 'pudgyvincent.eth', + 'pudgyvincent.eth', + 'pudgyvincent.eth', False, - 'vincξnt.eth', + 'pudgyvincent.eth', ), ( '0x744Ec0A91D420c257aE3eE471B79B1A6a0312E36', None, - 'unnormalized', + 'no_primary_name', None, 'Unnamed 744e', None, @@ -864,15 +864,15 @@ def test_primary_name_get_uppercase(test_client): @pytest.mark.flaky(retries=2, condition=not pytest.use_monkeypatch) def test_primary_name_get_offchain(test_client): - address = '0x9d32572997DA4948063E3Fc11c2552Eb82F7208E' + address = '0x2211d1D0020DAEA8039E46Cf1367962070d77DA9' response = test_client.get(f'/secure-primary-name/mainnet/{address}') assert response.status_code == 200 res_json = response.json() print(res_json) assert res_json['impersonation_estimate'] == 'unlikely' assert res_json['primary_name_status'] == 'normalized' - assert res_json['primary_name'] == 'poet.base.eth' - assert res_json['display_name'] == 'poet.base.eth' + assert res_json['primary_name'] == 'jesse.base.eth' + assert res_json['display_name'] == 'jesse.base.eth' @pytest.mark.flaky(retries=2, condition=not pytest.use_monkeypatch) @@ -896,24 +896,11 @@ def test_primary_name_get_unnormalized(test_client): res_json = response.json() print(res_json) assert res_json['impersonation_estimate'] is None - assert res_json['primary_name_status'] == 'unnormalized' + assert res_json['primary_name_status'] == 'no_primary_name' assert res_json['primary_name'] is None assert res_json['display_name'] == 'Unnamed fa9a' -@pytest.mark.flaky(retries=2, condition=not pytest.use_monkeypatch) -def test_primary_name_get_uninspected(test_client): - address = '0xf4A4D9C75dA65d507cfcd5ff0aCB73D40D3A3bCB' - response = test_client.get(f'/secure-primary-name/mainnet/{address}') - assert response.status_code == 200 - res_json = response.json() - print(res_json) - assert res_json['impersonation_estimate'] is None - assert res_json['primary_name_status'] == 'uninspected' - assert res_json['primary_name'] is None - assert res_json['display_name'] == 'Unnamed f4a4' - - def test_primary_name_get_invalid_address(test_client): address = '0xfA9A134f997b3d48e122d043E12d04E909b1107g' response = test_client.get(f'/secure-primary-name/mainnet/{address}') diff --git a/packages/nameguard-python/tests/test_nameguard.py b/packages/nameguard-python/tests/test_nameguard.py index 7d00781d7..0afa991d5 100644 --- a/packages/nameguard-python/tests/test_nameguard.py +++ b/packages/nameguard-python/tests/test_nameguard.py @@ -372,6 +372,15 @@ async def test_namehash_non_null_name(nameguard: NameGuard): ) +@pytest.mark.asyncio +async def test_namehash_sepolia(nameguard: NameGuard): + network_name = 'sepolia' + r = await nameguard.inspect_namehash( + network_name, '0xee6c4522aab0003e8d14cd40a6af439055fd2577951148c14b6cea9a53475835' + ) + assert r.name == 'vitalik.eth' + + @pytest.mark.flaky(retries=2, condition=not pytest.use_monkeypatch) @pytest.mark.asyncio @pytest.mark.xfail @@ -643,14 +652,14 @@ async def test_dynamic_check_order(nameguard: NameGuard): assert r.checks[1].check == Check.TYPING_DIFFICULTY assert r.checks[1].status == CheckStatus.WARN - # normalized is ALERT but impersonation risk is WARN - r = await nameguard.secure_primary_name( - '0xc9f598bc5bb554b6a15a96d19954b041c9fdbf14', 'mainnet', return_nameguard_report=True - ) - assert r.nameguard_report.checks[0].check == Check.NORMALIZED - assert r.nameguard_report.checks[0].status == CheckStatus.ALERT - assert r.nameguard_report.checks[1].check == Check.TYPING_DIFFICULTY - assert r.nameguard_report.checks[1].status == CheckStatus.WARN + # normalized is ALERT but impersonation risk is WARN - impossible now + # r = await nameguard.secure_primary_name( + # '0xc9f598bc5bb554b6a15a96d19954b041c9fdbf14', 'mainnet', return_nameguard_report=True + # ) + # assert r.nameguard_report.checks[0].check == Check.NORMALIZED + # assert r.nameguard_report.checks[0].status == CheckStatus.ALERT + # assert r.nameguard_report.checks[1].check == Check.TYPING_DIFFICULTY + # assert r.nameguard_report.checks[1].status == CheckStatus.WARN r = await nameguard.secure_primary_name( '0xd8da6bf26964af9d7eed9e03e53415d37aa96045', 'mainnet', return_nameguard_report=True @@ -673,16 +682,46 @@ async def test_dynamic_check_order(nameguard: NameGuard): endpoint_name.set(None) +@pytest.mark.asyncio +async def test_secure_primary_name(nameguard: NameGuard): + network = 'mainnet' + r = await nameguard.secure_primary_name( + '0x2211d1D0020DAEA8039E46Cf1367962070d77DA9', network, return_nameguard_report=True + ) + assert r.primary_name_status == 'normalized' + + @pytest.mark.asyncio async def test_secure_primary_name_error(nameguard: NameGuard, monkeypatch): network = 'mainnet' - monkeypatch.setattr(nameguard.ns[network], 'name', lambda address: (_ for _ in ()).throw(Exception('Error'))) + + async def mock_get_primary_name_error(self, address, network_name): + raise Exception('Error') + + monkeypatch.setattr(NameGuard, 'get_primary_name', mock_get_primary_name_error) r = await nameguard.secure_primary_name( - '0xFD9eE68000Dc92aa6c67F8f6EB5d9d1a24086fAd', network, return_nameguard_report=True + '0x2211d1D0020DAEA8039E46Cf1367962070d77DA9', network, return_nameguard_report=True ) assert r.primary_name_status == 'no_primary_name' +@pytest.mark.asyncio +async def test_secure_primary_name_uninspected(nameguard: NameGuard, monkeypatch): + network = 'mainnet' + + async def mock_get_primary_name_error(self, address, network_name): + return '●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●.eth' + + monkeypatch.setattr(NameGuard, 'get_primary_name', mock_get_primary_name_error) + r = await nameguard.secure_primary_name( + '0xf4A4D9C75dA65d507cfcd5ff0aCB73D40D3A3bCB', network, return_nameguard_report=True + ) + assert r.primary_name_status == 'uninspected' + assert r.impersonation_estimate is None + assert r.primary_name is None + assert r.display_name == 'Unnamed f4a4' + + @pytest.mark.asyncio async def test_stress_inspect_name(nameguard: NameGuard): # with omit_cure=False takes 1 minute diff --git a/packages/nameguard-sdk/src/index.test.ts b/packages/nameguard-sdk/src/index.test.ts index efaaff50d..42ac4539f 100644 --- a/packages/nameguard-sdk/src/index.test.ts +++ b/packages/nameguard-sdk/src/index.test.ts @@ -150,49 +150,43 @@ describe("getSecurePrimaryName", () => { expect(data.nameguard_report).toBeNull(); }); - it("getSecurePrimaryName: unnormalized", async () => { + it("getSecurePrimaryName: unnormalized (treated as no_primary_name)", async () => { const data = await nameguard.getSecurePrimaryName( "0xfA9A134f997b3d48e122d043E12d04E909b11073", { returnNameGuardReport: true }, ); - expect(data.primary_name_status).toBe("unnormalized"); + expect(data.primary_name_status).toBe("no_primary_name"); expect(data.impersonation_estimate).toBeNull(); expect(data.primary_name).toBeNull(); expect(data.display_name).toBe("Unnamed fa9a"); - expect(data.nameguard_report).not.toBeNull(); - expect(data.nameguard_report?.name).toBe("888‍‍.eth"); - expect(data.nameguard_report?.canonical_name).toBeNull(); + expect(data.nameguard_report).toBeNull(); }); - it("getSecurePrimaryName: unnormalized with canonical", async () => { + it("getSecurePrimaryName: unnormalized with canonical (treated as no_primary_name)", async () => { const data = await nameguard.getSecurePrimaryName( "0xaf738F6C83d7D2C46723b727Ce794F9c79Cc47E6", { returnNameGuardReport: true }, ); - expect(data.primary_name_status).toBe("unnormalized"); + expect(data.primary_name_status).toBe("no_primary_name"); expect(data.impersonation_estimate).toBeNull(); expect(data.primary_name).toBeNull(); expect(data.display_name).toBe("Unnamed af73"); - expect(data.nameguard_report).not.toBeNull(); - expect(data.nameguard_report?.name).toBe("୨୨୨୨୨.eth"); - expect(data.nameguard_report?.canonical_name).toBeNull(); + expect(data.nameguard_report).toBeNull(); }); - it("getSecurePrimaryName: unnormalized but normalizable", async () => { + it("getSecurePrimaryName: unnormalized but normalizable (treated as no_primary_name)", async () => { const data = await nameguard.getSecurePrimaryName( "0xf537a27F31d7A014c5b8008a0069c61f827fA7A1", { returnNameGuardReport: true }, ); - expect(data.primary_name_status).toBe("unnormalized"); + expect(data.primary_name_status).toBe("no_primary_name"); expect(data.impersonation_estimate).toBeNull(); expect(data.primary_name).toBeNull(); expect(data.display_name).toBe("Unnamed f537"); - expect(data.nameguard_report).not.toBeNull(); - expect(data.nameguard_report?.name).toBe("٠٠۱.eth"); - expect(data.nameguard_report?.canonical_name).toBeNull(); + expect(data.nameguard_report).toBeNull(); }); it("getSecurePrimaryName: normalized with different display_name", async () => { @@ -210,18 +204,17 @@ describe("getSecurePrimaryName", () => { expect(data.nameguard_report?.canonical_name).toBe("vincξnt.eth"); }); - it("getSecurePrimaryName: attempted code injection with primary name", async () => { + it("getSecurePrimaryName: attempted code injection with primary name (treated as no_primary_name)", async () => { const data = await nameguard.getSecurePrimaryName( "0x744Ec0A91D420c257aE3eE471B79B1A6a0312E36", { returnNameGuardReport: true }, ); - expect(data.primary_name_status).toBe("unnormalized"); + expect(data.primary_name_status).toBe("no_primary_name"); expect(data.impersonation_estimate).toBeNull(); expect(data.primary_name).toBeNull(); expect(data.display_name).toBe("Unnamed 744e"); - expect(data.nameguard_report).not.toBeNull(); - expect(data.nameguard_report?.name).toBe("hello!.eth"); + expect(data.nameguard_report).toBeNull(); expect(data.nameguard_report?.canonical_name).toBeNull(); }); }); diff --git a/packages/nameguard-sdk/src/index.ts b/packages/nameguard-sdk/src/index.ts index 5442f14c3..95962757f 100644 --- a/packages/nameguard-sdk/src/index.ts +++ b/packages/nameguard-sdk/src/index.ts @@ -89,8 +89,8 @@ export enum Rating { */ export type SecurePrimaryNameStatus = | "normalized" /** The ENS primary name was found and it is normalized. */ - | "no_primary_name" /** The ENS primary name was not found. */ - | "unnormalized" /** The ENS primary name was found, but it is not normalized. */ + | "no_primary_name" /** The ENS primary name was not found, or the primary name is unnormalized. The ENSNode API only returns normalized primary names, so unnormalized primary names are treated as having no primary name. */ + | "unnormalized" /** @deprecated This status is no longer returned. Unnormalized primary names are now treated as having no primary name. */ | "uninspected" /** A name was exceptionally long and was not inspected for performance reasons */; export type ImpersonationEstimate = @@ -688,9 +688,8 @@ export class NameGuard { /** * Performs a reverse lookup of an Ethereum `address` to a primary name. * - * Data sources for the primary name lookup include: - * 1. The Ethereum Provider configured in the NameGuard instance. - * 2. For ENS names using CCIP-Read: requests to externally defined gateway servers. + * The primary name lookup uses the ENSNode API, which only returns normalized primary names. + * If an address has an unnormalized primary name, it will be treated as having no primary name. * * Returns `display_name` to be shown to users and estimates `impersonation_estimate` * From 5f183205e4cdcf15b34a5d967f745cf17d4e9dba Mon Sep 17 00:00:00 2001 From: djstrong Date: Mon, 5 Jan 2026 00:23:45 +0100 Subject: [PATCH 03/10] test(nameguard): Mark Sepolia subgraph tests as expected failures due to current issues --- packages/nameguard-python/tests/test_api.py | 8 +++++++- packages/nameguard-python/tests/test_nameguard.py | 1 + 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/packages/nameguard-python/tests/test_api.py b/packages/nameguard-python/tests/test_api.py index 92e0e0e94..5ceff0d18 100644 --- a/packages/nameguard-python/tests/test_api.py +++ b/packages/nameguard-python/tests/test_api.py @@ -266,7 +266,13 @@ def test_bulk_inspect_name_post_stress(test_client): 'vitalik.eth', ), ('mainnet', '0xe0fe380f4d877f643e88ceabbed4e5ee0efb66f079aabba23e8902336f7948da', 404, None), - ('sepolia', '0xee6c4522aab0003e8d14cd40a6af439055fd2577951148c14b6cea9a53475835', 200, 'vitalik.eth'), + pytest.param( + 'sepolia', + '0xee6c4522aab0003e8d14cd40a6af439055fd2577951148c14b6cea9a53475835', + 200, + 'vitalik.eth', + marks=pytest.mark.xfail(reason='Sepolia subgraph is not working'), + ), ], ) def test_inspect_namehash_get( diff --git a/packages/nameguard-python/tests/test_nameguard.py b/packages/nameguard-python/tests/test_nameguard.py index 0afa991d5..062dd2170 100644 --- a/packages/nameguard-python/tests/test_nameguard.py +++ b/packages/nameguard-python/tests/test_nameguard.py @@ -373,6 +373,7 @@ async def test_namehash_non_null_name(nameguard: NameGuard): @pytest.mark.asyncio +@pytest.mark.xfail(reason='Sepolia subgraph is not working') async def test_namehash_sepolia(nameguard: NameGuard): network_name = 'sepolia' r = await nameguard.inspect_namehash( From 9795c6dfa876cc9131bbf2590dd18758e4db3858 Mon Sep 17 00:00:00 2001 From: github-actions Date: Sun, 4 Jan 2026 23:32:32 +0000 Subject: [PATCH 04/10] Update coverage badge --- packages/nameguard-python/coverage_badge.svg | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/nameguard-python/coverage_badge.svg b/packages/nameguard-python/coverage_badge.svg index 607d3de4d..a8c7e72ad 100644 --- a/packages/nameguard-python/coverage_badge.svg +++ b/packages/nameguard-python/coverage_badge.svg @@ -15,7 +15,7 @@ coverage coverage - 91% - 91% + 92% + 92% From a502021de4c47acbb2299290a22cfde9ff0a7415 Mon Sep 17 00:00:00 2001 From: djstrong Date: Wed, 7 Jan 2026 14:58:23 +0100 Subject: [PATCH 05/10] fix(nameguard): Update Sepolia subgraph URL and adjust related tests --- apps/docs.namekit.io/nameguard/self-hosting.mdx | 2 +- packages/nameguard-python/.env.example | 5 +---- packages/nameguard-python/README.md | 2 +- packages/nameguard-python/tests/test_api.py | 8 +------- packages/nameguard-python/tests/test_nameguard.py | 1 - 5 files changed, 4 insertions(+), 14 deletions(-) diff --git a/apps/docs.namekit.io/nameguard/self-hosting.mdx b/apps/docs.namekit.io/nameguard/self-hosting.mdx index 6e7cbd473..79be05eda 100644 --- a/apps/docs.namekit.io/nameguard/self-hosting.mdx +++ b/apps/docs.namekit.io/nameguard/self-hosting.mdx @@ -23,7 +23,7 @@ export PROVIDER_URI_SEPOLIA=https://eth-sepolia.g.alchemy.com/v2/[YOUR_ALCHEMY_A export ALCHEMY_URI_MAINNET=https://eth-mainnet.g.alchemy.com/v2/[YOUR_ALCHEMY_API_KEY] export ALCHEMY_URI_SEPOLIA=https://eth-sepolia.g.alchemy.com/v2/[YOUR_ALCHEMY_API_KEY] export ENS_SUBGRAPH_URL_MAINNET="https://gateway-arbitrum.network.thegraph.com/api/[YOUR_SUBGRAPH_API_KEY]/subgraphs/id/5XqPmWe6gjyrJtFn9cLy237i4cWw2j9HcUJEXsP5qGtH" -export ENS_SUBGRAPH_URL_SEPOLIA="https://gateway-arbitrum.network.thegraph.com/api/[YOUR_SUBGRAPH_API_KEY]/subgraphs/id/DmMXLtMZnGbQXASJ7p1jfzLUbBYnYUD9zNBTxpkjHYXV" +export ENS_SUBGRAPH_URL_SEPOLIA="https://gateway-arbitrum.network.thegraph.com/api/[YOUR_SUBGRAPH_API_KEY]/subgraphs/id/G1SxZs317YUb9nQX3CC98hDyvxfMJNZH5pPRGpNrtvwN" ``` diff --git a/packages/nameguard-python/.env.example b/packages/nameguard-python/.env.example index 5fad07537..17f52c28c 100644 --- a/packages/nameguard-python/.env.example +++ b/packages/nameguard-python/.env.example @@ -18,7 +18,4 @@ ALCHEMY_URI_SEPOLIA=https://eth-sepolia.g.alchemy.com/v2/[YOUR_ALCHEMY_API_KEY] # - https://discuss.ens.domains/t/ens-subgraph-migration-to-the-decentralised-version/19183 # - https://thegraph.com/explorer/subgraphs/5XqPmWe6gjyrJtFn9cLy237i4cWw2j9HcUJEXsP5qGtH?view=Query&chain=arbitrum-one ENS_SUBGRAPH_URL_MAINNET=https://api.thegraph.com/subgraphs/name/ensdomains/ens -ENS_SUBGRAPH_URL_SEPOLIA=https://api.studio.thegraph.com/query/49574/enssepolia/version/latest - -# Use ENSNode API (default is true) -USE_ENSNODE_API=true \ No newline at end of file +ENS_SUBGRAPH_URL_SEPOLIA=https://api.studio.thegraph.com/query/49574/enssepolia/version/latest \ No newline at end of file diff --git a/packages/nameguard-python/README.md b/packages/nameguard-python/README.md index 0ee6d1d29..72fa14331 100644 --- a/packages/nameguard-python/README.md +++ b/packages/nameguard-python/README.md @@ -83,7 +83,7 @@ For `fake-eth-name-check`, NameGuard uses Alchemy endpoints. Alchemy endpoints h export ALCHEMY_URI_MAINNET=https://eth-mainnet.g.alchemy.com/v2/[YOUR_ALCHEMY_API_KEY] export ALCHEMY_URI_SEPOLIA=https://eth-sepolia.g.alchemy.com/v2/[YOUR_ALCHEMY_API_KEY] export ENS_SUBGRAPH_URL_MAINNET="https://gateway-arbitrum.network.thegraph.com/api/[YOUR_SUBGRAPH_API_KEY]/subgraphs/id/5XqPmWe6gjyrJtFn9cLy237i4cWw2j9HcUJEXsP5qGtH" -export ENS_SUBGRAPH_URL_SEPOLIA="https://gateway-arbitrum.network.thegraph.com/api/[YOUR_SUBGRAPH_API_KEY]/subgraphs/id/DmMXLtMZnGbQXASJ7p1jfzLUbBYnYUD9zNBTxpkjHYXV" +export ENS_SUBGRAPH_URL_SEPOLIA="https://gateway-arbitrum.network.thegraph.com/api/[YOUR_SUBGRAPH_API_KEY]/subgraphs/id/G1SxZs317YUb9nQX3CC98hDyvxfMJNZH5pPRGpNrtvwN" ``` #### Starting the webserver diff --git a/packages/nameguard-python/tests/test_api.py b/packages/nameguard-python/tests/test_api.py index 5ceff0d18..92e0e0e94 100644 --- a/packages/nameguard-python/tests/test_api.py +++ b/packages/nameguard-python/tests/test_api.py @@ -266,13 +266,7 @@ def test_bulk_inspect_name_post_stress(test_client): 'vitalik.eth', ), ('mainnet', '0xe0fe380f4d877f643e88ceabbed4e5ee0efb66f079aabba23e8902336f7948da', 404, None), - pytest.param( - 'sepolia', - '0xee6c4522aab0003e8d14cd40a6af439055fd2577951148c14b6cea9a53475835', - 200, - 'vitalik.eth', - marks=pytest.mark.xfail(reason='Sepolia subgraph is not working'), - ), + ('sepolia', '0xee6c4522aab0003e8d14cd40a6af439055fd2577951148c14b6cea9a53475835', 200, 'vitalik.eth'), ], ) def test_inspect_namehash_get( diff --git a/packages/nameguard-python/tests/test_nameguard.py b/packages/nameguard-python/tests/test_nameguard.py index 062dd2170..0afa991d5 100644 --- a/packages/nameguard-python/tests/test_nameguard.py +++ b/packages/nameguard-python/tests/test_nameguard.py @@ -373,7 +373,6 @@ async def test_namehash_non_null_name(nameguard: NameGuard): @pytest.mark.asyncio -@pytest.mark.xfail(reason='Sepolia subgraph is not working') async def test_namehash_sepolia(nameguard: NameGuard): network_name = 'sepolia' r = await nameguard.inspect_namehash( From 49d261087813db93bbd1a67fb26f4bad94cf2cbf Mon Sep 17 00:00:00 2001 From: "kwrobel.eth" Date: Wed, 7 Jan 2026 16:35:31 +0100 Subject: [PATCH 06/10] refactor: Remove PROVIDER_URI_MAINNET and PROVIDER_URI_SEPOLIA from workflows, Terraform files, and documentation, streamlining environment variable usage for Lambda deployments. (#714) * refactor: Remove PROVIDER_URI_MAINNET and PROVIDER_URI_SEPOLIA from workflows, Terraform files, and documentation, streamlining environment variable usage for Lambda deployments. * refactor(nameguard): Simplify client initialization by removing public client dependency and directly using ENS node API for secure primary name resolution * fix(nameguard): Update Sepolia subgraph URL and adjust related tests * feat: Add ENSNode URLs for Mainnet and Sepolia to workflows, Terraform files, and documentation, enhancing environment variable management for Lambda deployments. * fix(nameguard): Change parameter name from computeNameGuardReport to returnNameGuardReport for secure primary name retrieval --- .../workflows/nameai-api-lambda-deploy.yml | 4 +- .../workflows/nameguard-api-lambda-deploy.yml | 4 +- .../workflows/nameguard-end-to-end-tests.yml | 7 +- .../workflows/nameguard-python-unit-tests.yml | 4 +- .../typescript-packages-unit-tests.yml | 4 +- apps/api.nameai.io/.env.example | 8 +-- apps/api.nameai.io/terraform/deploy_lambda.sh | 8 +-- apps/api.nameai.io/terraform/main.tf | 4 +- .../terraform/modules/lambda_api/main.tf | 4 +- .../terraform/modules/lambda_api/variables.tf | 8 +-- apps/api.nameai.io/terraform/variables.tf | 8 +-- .../terraform/deploy_lambda.sh | 6 +- apps/api.nameguard.io/terraform/main.tf | 4 +- .../terraform/modules/lambda_api/main.tf | 4 +- .../terraform/modules/lambda_api/variables.tf | 8 +-- apps/api.nameguard.io/terraform/variables.tf | 8 +-- .../nameguard/self-hosting.mdx | 4 +- .../src/app/components/SecurePrimaryName.tsx | 15 +---- packages/nameai-sdk/src/index.ts | 2 +- packages/nameguard-js/src/lookup.ts | 34 ++++++++-- .../nameguard-js/src/nameguard-js.test.ts | 65 +++---------------- packages/nameguard-js/src/nameguard-js.ts | 28 ++------ .../nameguard-js/src/securePrimaryName.ts | 7 +- .../src/secureprimaryName.test.ts | 30 ++++----- packages/nameguard-python/.env.example | 6 -- packages/nameguard-sdk/src/index.test.ts | 10 +-- packages/nameguard-sdk/src/index.ts | 4 +- 27 files changed, 114 insertions(+), 184 deletions(-) diff --git a/.github/workflows/nameai-api-lambda-deploy.yml b/.github/workflows/nameai-api-lambda-deploy.yml index c4a49f7cb..8a77806e7 100644 --- a/.github/workflows/nameai-api-lambda-deploy.yml +++ b/.github/workflows/nameai-api-lambda-deploy.yml @@ -53,8 +53,8 @@ jobs: - name: Build and deploy lambda env: - PROVIDER_URI_MAINNET: ${{ secrets.PROVIDER_URI_MAINNET }} - PROVIDER_URI_SEPOLIA: ${{ secrets.PROVIDER_URI_SEPOLIA }} + ENSNODE_URL_MAINNET: ${{ secrets.ENSNODE_URL_MAINNET }} + ENSNODE_URL_SEPOLIA: ${{ secrets.ENSNODE_URL_SEPOLIA }} ALCHEMY_URI_MAINNET: ${{ secrets.ALCHEMY_URI_MAINNET }} ALCHEMY_URI_SEPOLIA: ${{ secrets.ALCHEMY_URI_SEPOLIA }} ENS_SUBGRAPH_URL_MAINNET: ${{ secrets.ENS_SUBGRAPH_URL_MAINNET }} diff --git a/.github/workflows/nameguard-api-lambda-deploy.yml b/.github/workflows/nameguard-api-lambda-deploy.yml index 995a09ddf..3ae442c2e 100644 --- a/.github/workflows/nameguard-api-lambda-deploy.yml +++ b/.github/workflows/nameguard-api-lambda-deploy.yml @@ -53,8 +53,8 @@ jobs: - name: Build and deploy lambda env: - PROVIDER_URI_MAINNET: ${{ secrets.PROVIDER_URI_MAINNET }} - PROVIDER_URI_SEPOLIA: ${{ secrets.PROVIDER_URI_SEPOLIA }} + ENSNODE_URL_MAINNET: ${{ secrets.ENSNODE_URL_MAINNET }} + ENSNODE_URL_SEPOLIA: ${{ secrets.ENSNODE_URL_SEPOLIA }} ALCHEMY_URI_MAINNET: ${{ secrets.ALCHEMY_URI_MAINNET }} ALCHEMY_URI_SEPOLIA: ${{ secrets.ALCHEMY_URI_SEPOLIA }} ENS_SUBGRAPH_URL_MAINNET: ${{ secrets.ENS_SUBGRAPH_URL_MAINNET }} diff --git a/.github/workflows/nameguard-end-to-end-tests.yml b/.github/workflows/nameguard-end-to-end-tests.yml index 6739f4050..1c595595a 100644 --- a/.github/workflows/nameguard-end-to-end-tests.yml +++ b/.github/workflows/nameguard-end-to-end-tests.yml @@ -78,8 +78,8 @@ jobs: if: needs.check_changes.outputs.api_changed > 0 working-directory: ./packages/nameguard-python env: - PROVIDER_URI_MAINNET: ${{ secrets.PROVIDER_URI_MAINNET }} - PROVIDER_URI_SEPOLIA: ${{ secrets.PROVIDER_URI_SEPOLIA }} + ENSNODE_URL_MAINNET: ${{ secrets.ENSNODE_URL_MAINNET }} + ENSNODE_URL_SEPOLIA: ${{ secrets.ENSNODE_URL_SEPOLIA }} ALCHEMY_URI_MAINNET: ${{ secrets.ALCHEMY_URI_MAINNET }} ALCHEMY_URI_SEPOLIA: ${{ secrets.ALCHEMY_URI_SEPOLIA }} ENS_SUBGRAPH_URL_MAINNET: ${{ secrets.ENS_SUBGRAPH_URL_MAINNET }} @@ -104,7 +104,4 @@ jobs: fi - name: Run tests - env: - PROVIDER_URI_MAINNET: ${{ secrets.PROVIDER_URI_MAINNET }} - PROVIDER_URI_SEPOLIA: ${{ secrets.PROVIDER_URI_SEPOLIA }} run: pnpm test --filter ./packages/nameguard-sdk diff --git a/.github/workflows/nameguard-python-unit-tests.yml b/.github/workflows/nameguard-python-unit-tests.yml index fda1775aa..5a2c595d2 100644 --- a/.github/workflows/nameguard-python-unit-tests.yml +++ b/.github/workflows/nameguard-python-unit-tests.yml @@ -15,8 +15,8 @@ on: - "packages/nameguard-python/**" env: - PROVIDER_URI_MAINNET: ${{ secrets.PROVIDER_URI_MAINNET }} - PROVIDER_URI_SEPOLIA: ${{ secrets.PROVIDER_URI_SEPOLIA }} + ENSNODE_URL_MAINNET: ${{ secrets.ENSNODE_URL_MAINNET }} + ENSNODE_URL_SEPOLIA: ${{ secrets.ENSNODE_URL_SEPOLIA }} ALCHEMY_URI_MAINNET: ${{ secrets.ALCHEMY_URI_MAINNET }} ALCHEMY_URI_SEPOLIA: ${{ secrets.ALCHEMY_URI_SEPOLIA }} ENS_SUBGRAPH_URL_MAINNET: ${{ secrets.ENS_SUBGRAPH_URL_MAINNET }} diff --git a/.github/workflows/typescript-packages-unit-tests.yml b/.github/workflows/typescript-packages-unit-tests.yml index f89a850ca..7099267b7 100644 --- a/.github/workflows/typescript-packages-unit-tests.yml +++ b/.github/workflows/typescript-packages-unit-tests.yml @@ -17,8 +17,8 @@ on: - "!packages/nameguard-python/**" env: - PROVIDER_URI_MAINNET: ${{ secrets.PROVIDER_URI_MAINNET }} - PROVIDER_URI_SEPOLIA: ${{ secrets.PROVIDER_URI_SEPOLIA }} + ENSNODE_URL_MAINNET: ${{ secrets.ENSNODE_URL_MAINNET }} + ENSNODE_URL_SEPOLIA: ${{ secrets.ENSNODE_URL_SEPOLIA }} jobs: test: diff --git a/apps/api.nameai.io/.env.example b/apps/api.nameai.io/.env.example index 17f52c28c..55e34e1e1 100644 --- a/apps/api.nameai.io/.env.example +++ b/apps/api.nameai.io/.env.example @@ -1,8 +1,6 @@ -# Provider URIs (can be any Ethereum node, e.g. Infura, Alchemy, your own node, etc.). -# The following example values are rate limited. -# Sign up for your own node provider (e.g. Infura, Alchemy, etc.) to remove rate limits. -PROVIDER_URI_MAINNET=https://rpc.ankr.com/eth -PROVIDER_URI_SEPOLIA=https://rpc.ankr.com/eth_sepolia +# ENSNode URLs +ENSNODE_URL_MAINNET=https://api.alpha.ensnode.io +ENSNODE_URL_SEPOLIA=https://api.alpha-sepolia.ensnode.io # Alchemy API URIs (needed for their NFT API for NameGuard's fake ENS NFT checks) # Replace [YOUR_ALCHEMY_API_KEY] with your actual Alchemy API key). diff --git a/apps/api.nameai.io/terraform/deploy_lambda.sh b/apps/api.nameai.io/terraform/deploy_lambda.sh index facc2a094..038eca780 100644 --- a/apps/api.nameai.io/terraform/deploy_lambda.sh +++ b/apps/api.nameai.io/terraform/deploy_lambda.sh @@ -90,8 +90,8 @@ done # Validate Lambda environment variables echo "Validating Lambda environment variables..." LAMBDA_ENV_VARS=( - "PROVIDER_URI_MAINNET" - "PROVIDER_URI_SEPOLIA" + "ENSNODE_URL_MAINNET" + "ENSNODE_URL_SEPOLIA" "ALCHEMY_URI_MAINNET" "ALCHEMY_URI_SEPOLIA" "ENS_SUBGRAPH_URL_MAINNET" @@ -219,8 +219,8 @@ IMAGE_URI=`docker inspect --format='{{index .RepoDigests 0}}' ${ECR_URL}:latest` echo "Using Image URI: ${IMAGE_URI}" # Export individual environment variables for Terraform -export TF_VAR_PROVIDER_URI_MAINNET="${PROVIDER_URI_MAINNET}" -export TF_VAR_PROVIDER_URI_SEPOLIA="${PROVIDER_URI_SEPOLIA}" +export TF_VAR_ENSNODE_URL_MAINNET="${ENSNODE_URL_MAINNET}" +export TF_VAR_ENSNODE_URL_SEPOLIA="${ENSNODE_URL_SEPOLIA}" export TF_VAR_ALCHEMY_URI_MAINNET="${ALCHEMY_URI_MAINNET}" export TF_VAR_ALCHEMY_URI_SEPOLIA="${ALCHEMY_URI_SEPOLIA}" export TF_VAR_ENS_SUBGRAPH_URL_MAINNET="${ENS_SUBGRAPH_URL_MAINNET}" diff --git a/apps/api.nameai.io/terraform/main.tf b/apps/api.nameai.io/terraform/main.tf index 49286c4c7..3d9392482 100644 --- a/apps/api.nameai.io/terraform/main.tf +++ b/apps/api.nameai.io/terraform/main.tf @@ -13,8 +13,8 @@ module "lambda_api" { aws_region = var.aws_region # Lambda environment variables - PROVIDER_URI_MAINNET = var.PROVIDER_URI_MAINNET - PROVIDER_URI_SEPOLIA = var.PROVIDER_URI_SEPOLIA + ENSNODE_URL_MAINNET = var.ENSNODE_URL_MAINNET + ENSNODE_URL_SEPOLIA = var.ENSNODE_URL_SEPOLIA ALCHEMY_URI_MAINNET = var.ALCHEMY_URI_MAINNET ALCHEMY_URI_SEPOLIA = var.ALCHEMY_URI_SEPOLIA ENS_SUBGRAPH_URL_MAINNET = var.ENS_SUBGRAPH_URL_MAINNET diff --git a/apps/api.nameai.io/terraform/modules/lambda_api/main.tf b/apps/api.nameai.io/terraform/modules/lambda_api/main.tf index da93edb6d..8c502030f 100644 --- a/apps/api.nameai.io/terraform/modules/lambda_api/main.tf +++ b/apps/api.nameai.io/terraform/modules/lambda_api/main.tf @@ -64,8 +64,8 @@ resource "aws_lambda_function" "nameai_lambda" { environment { variables = { - PROVIDER_URI_MAINNET = var.PROVIDER_URI_MAINNET - PROVIDER_URI_SEPOLIA = var.PROVIDER_URI_SEPOLIA + ENSNODE_URL_MAINNET = var.ENSNODE_URL_MAINNET + ENSNODE_URL_SEPOLIA = var.ENSNODE_URL_SEPOLIA ALCHEMY_URI_MAINNET = var.ALCHEMY_URI_MAINNET ALCHEMY_URI_SEPOLIA = var.ALCHEMY_URI_SEPOLIA ENS_SUBGRAPH_URL_MAINNET = var.ENS_SUBGRAPH_URL_MAINNET diff --git a/apps/api.nameai.io/terraform/modules/lambda_api/variables.tf b/apps/api.nameai.io/terraform/modules/lambda_api/variables.tf index 3739acf3f..16b00dfe4 100644 --- a/apps/api.nameai.io/terraform/modules/lambda_api/variables.tf +++ b/apps/api.nameai.io/terraform/modules/lambda_api/variables.tf @@ -29,14 +29,14 @@ variable "aws_region" { } # Lambda environment variables -variable "PROVIDER_URI_MAINNET" { - description = "Provider URI for Mainnet" +variable "ENSNODE_URL_MAINNET" { + description = "ENSNode URL for Mainnet" type = string sensitive = true } -variable "PROVIDER_URI_SEPOLIA" { - description = "Provider URI for Sepolia" +variable "ENSNODE_URL_SEPOLIA" { + description = "ENSNode URL for Sepolia" type = string sensitive = true } diff --git a/apps/api.nameai.io/terraform/variables.tf b/apps/api.nameai.io/terraform/variables.tf index 3739acf3f..16b00dfe4 100644 --- a/apps/api.nameai.io/terraform/variables.tf +++ b/apps/api.nameai.io/terraform/variables.tf @@ -29,14 +29,14 @@ variable "aws_region" { } # Lambda environment variables -variable "PROVIDER_URI_MAINNET" { - description = "Provider URI for Mainnet" +variable "ENSNODE_URL_MAINNET" { + description = "ENSNode URL for Mainnet" type = string sensitive = true } -variable "PROVIDER_URI_SEPOLIA" { - description = "Provider URI for Sepolia" +variable "ENSNODE_URL_SEPOLIA" { + description = "ENSNode URL for Sepolia" type = string sensitive = true } diff --git a/apps/api.nameguard.io/terraform/deploy_lambda.sh b/apps/api.nameguard.io/terraform/deploy_lambda.sh index a744aefb3..235530225 100644 --- a/apps/api.nameguard.io/terraform/deploy_lambda.sh +++ b/apps/api.nameguard.io/terraform/deploy_lambda.sh @@ -90,8 +90,8 @@ done # Validate Lambda environment variables echo "Validating Lambda environment variables..." LAMBDA_ENV_VARS=( - "PROVIDER_URI_MAINNET" - "PROVIDER_URI_SEPOLIA" + "ENSNODE_URL_MAINNET" + "ENSNODE_URL_SEPOLIA" "ALCHEMY_URI_MAINNET" "ALCHEMY_URI_SEPOLIA" "ENS_SUBGRAPH_URL_MAINNET" @@ -222,8 +222,6 @@ IMAGE_URI=`docker inspect --format='{{index .RepoDigests 0}}' ${ECR_URL}:latest` echo "Using Image URI: ${IMAGE_URI}" # Export individual environment variables for Terraform -export TF_VAR_PROVIDER_URI_MAINNET="${PROVIDER_URI_MAINNET}" -export TF_VAR_PROVIDER_URI_SEPOLIA="${PROVIDER_URI_SEPOLIA}" export TF_VAR_ALCHEMY_URI_MAINNET="${ALCHEMY_URI_MAINNET}" export TF_VAR_ALCHEMY_URI_SEPOLIA="${ALCHEMY_URI_SEPOLIA}" export TF_VAR_ENS_SUBGRAPH_URL_MAINNET="${ENS_SUBGRAPH_URL_MAINNET}" diff --git a/apps/api.nameguard.io/terraform/main.tf b/apps/api.nameguard.io/terraform/main.tf index 49286c4c7..3d9392482 100644 --- a/apps/api.nameguard.io/terraform/main.tf +++ b/apps/api.nameguard.io/terraform/main.tf @@ -13,8 +13,8 @@ module "lambda_api" { aws_region = var.aws_region # Lambda environment variables - PROVIDER_URI_MAINNET = var.PROVIDER_URI_MAINNET - PROVIDER_URI_SEPOLIA = var.PROVIDER_URI_SEPOLIA + ENSNODE_URL_MAINNET = var.ENSNODE_URL_MAINNET + ENSNODE_URL_SEPOLIA = var.ENSNODE_URL_SEPOLIA ALCHEMY_URI_MAINNET = var.ALCHEMY_URI_MAINNET ALCHEMY_URI_SEPOLIA = var.ALCHEMY_URI_SEPOLIA ENS_SUBGRAPH_URL_MAINNET = var.ENS_SUBGRAPH_URL_MAINNET diff --git a/apps/api.nameguard.io/terraform/modules/lambda_api/main.tf b/apps/api.nameguard.io/terraform/modules/lambda_api/main.tf index b8c04ec3a..e8b64330e 100644 --- a/apps/api.nameguard.io/terraform/modules/lambda_api/main.tf +++ b/apps/api.nameguard.io/terraform/modules/lambda_api/main.tf @@ -64,8 +64,8 @@ resource "aws_lambda_function" "nameguard_lambda" { environment { variables = { - PROVIDER_URI_MAINNET = var.PROVIDER_URI_MAINNET - PROVIDER_URI_SEPOLIA = var.PROVIDER_URI_SEPOLIA + ENSNODE_URL_MAINNET = var.ENSNODE_URL_MAINNET + ENSNODE_URL_SEPOLIA = var.ENSNODE_URL_SEPOLIA ALCHEMY_URI_MAINNET = var.ALCHEMY_URI_MAINNET ALCHEMY_URI_SEPOLIA = var.ALCHEMY_URI_SEPOLIA ENS_SUBGRAPH_URL_MAINNET = var.ENS_SUBGRAPH_URL_MAINNET diff --git a/apps/api.nameguard.io/terraform/modules/lambda_api/variables.tf b/apps/api.nameguard.io/terraform/modules/lambda_api/variables.tf index 7f21bf754..5b334debc 100644 --- a/apps/api.nameguard.io/terraform/modules/lambda_api/variables.tf +++ b/apps/api.nameguard.io/terraform/modules/lambda_api/variables.tf @@ -29,14 +29,14 @@ variable "aws_region" { } # Lambda environment variables -variable "PROVIDER_URI_MAINNET" { - description = "Provider URI for Mainnet" +variable "ENSNODE_URL_MAINNET" { + description = "ENSNode URL for Mainnet" type = string sensitive = true } -variable "PROVIDER_URI_SEPOLIA" { - description = "Provider URI for Sepolia" +variable "ENSNODE_URL_SEPOLIA" { + description = "ENSNode URL for Sepolia" type = string sensitive = true } diff --git a/apps/api.nameguard.io/terraform/variables.tf b/apps/api.nameguard.io/terraform/variables.tf index 680e0bf9e..3cef5c1b4 100644 --- a/apps/api.nameguard.io/terraform/variables.tf +++ b/apps/api.nameguard.io/terraform/variables.tf @@ -29,14 +29,14 @@ variable "aws_region" { } # Lambda environment variables -variable "PROVIDER_URI_MAINNET" { - description = "Provider URI for Mainnet" +variable "ENSNODE_URL_MAINNET" { + description = "ENSNode URL for Mainnet" type = string sensitive = true } -variable "PROVIDER_URI_SEPOLIA" { - description = "Provider URI for Sepolia" +variable "ENSNODE_URL_SEPOLIA" { + description = "ENSNode URL for Sepolia" type = string sensitive = true } diff --git a/apps/docs.namekit.io/nameguard/self-hosting.mdx b/apps/docs.namekit.io/nameguard/self-hosting.mdx index 79be05eda..9f75fb857 100644 --- a/apps/docs.namekit.io/nameguard/self-hosting.mdx +++ b/apps/docs.namekit.io/nameguard/self-hosting.mdx @@ -18,8 +18,8 @@ pip install nameguard ```bash -export PROVIDER_URI_MAINNET=https://eth-mainnet.g.alchemy.com/v2/[YOUR_ALCHEMY_API_KEY] -export PROVIDER_URI_SEPOLIA=https://eth-sepolia.g.alchemy.com/v2/[YOUR_ALCHEMY_API_KEY] +export ENSNODE_URL_MAINNET=https://api.alpha.ensnode.io +export ENSNODE_URL_SEPOLIA=https://api.alpha-sepolia.ensnode.io export ALCHEMY_URI_MAINNET=https://eth-mainnet.g.alchemy.com/v2/[YOUR_ALCHEMY_API_KEY] export ALCHEMY_URI_SEPOLIA=https://eth-sepolia.g.alchemy.com/v2/[YOUR_ALCHEMY_API_KEY] export ENS_SUBGRAPH_URL_MAINNET="https://gateway-arbitrum.network.thegraph.com/api/[YOUR_SUBGRAPH_API_KEY]/subgraphs/id/5XqPmWe6gjyrJtFn9cLy237i4cWw2j9HcUJEXsP5qGtH" diff --git a/apps/examples.nameguard.io/src/app/components/SecurePrimaryName.tsx b/apps/examples.nameguard.io/src/app/components/SecurePrimaryName.tsx index 9fcc6f601..0d2be63a2 100644 --- a/apps/examples.nameguard.io/src/app/components/SecurePrimaryName.tsx +++ b/apps/examples.nameguard.io/src/app/components/SecurePrimaryName.tsx @@ -1,19 +1,8 @@ import { createClient } from "@namehash/nameguard-js"; -import { createPublicClient, http } from "viem"; -import { mainnet } from "viem/chains"; import { headers } from "next/headers"; -// This is a server component, so your secrets are safe -const PROVIDER_URI_MAINNET = process.env.PROVIDER_URI_MAINNET; - -// You can use your own client -const publicClient = createPublicClient({ - chain: mainnet, - // For example, put your own URI here - transport: http(PROVIDER_URI_MAINNET), -}); - -const nameguard = createClient({ publicClient }); +// No longer need a public client - we use ENS node API directly +const nameguard = createClient({ network: "mainnet" }); interface Props { address: string; diff --git a/packages/nameai-sdk/src/index.ts b/packages/nameai-sdk/src/index.ts index 97bd9cbee..be723445a 100644 --- a/packages/nameai-sdk/src/index.ts +++ b/packages/nameai-sdk/src/index.ts @@ -137,7 +137,7 @@ class NameAIError extends Error { const DEFAULT_ENDPOINT = "https://api.nameai.io/"; const DEFAULT_NETWORK: Network = "mainnet"; const DEFAULT_INSPECT_LABELHASH_PARENT = ETH_TLD; -export const DEFAULT_COMPUTE_NAMEGUARD_REPORT = false; +export const DEFAULT_RETURN_NAMEGUARD_REPORT = false; const MAX_BULK_INSPECTION_NAMES = 250; /** includes label separators */ diff --git a/packages/nameguard-js/src/lookup.ts b/packages/nameguard-js/src/lookup.ts index 13c23f213..160611b90 100644 --- a/packages/nameguard-js/src/lookup.ts +++ b/packages/nameguard-js/src/lookup.ts @@ -1,15 +1,35 @@ -import { PublicClient } from "viem"; - /** - * Looks up the primary name associated with the given address. + * Looks up the primary name associated with the given address using ENS node API. * * @param address - The address to lookup. - * @param client - The viem client instance used for the lookup. + * @param network - The network to query ("mainnet" or "sepolia"). * @returns A Promise that resolves to the primary name associated with the address, or null if not found. */ -export function lookupPrimaryName( +export async function lookupPrimaryName( address: string, - client: PublicClient, + network: "mainnet" | "sepolia", ): Promise { - return client.getEnsName({ address }); + const baseUrl = network === "mainnet" + ? "https://api.alpha.ensnode.io" + : "http://api.alpha-sepolia.ensnode.io"; + + const chainId = network === "mainnet" ? 1 : 11155111; + const url = `${baseUrl}/api/resolve/primary-name/${address}/${chainId}`; + + try { + const response = await fetch(`${url}?accelerate=true`); + + if (response.status === 404) { + return null; + } + + if (!response.ok) { + throw new Error(`ENS node API error: ${response.status} ${response.statusText}`); + } + + const data = await response.json(); + return data.name || null; + } catch (error) { + throw new Error(`Failed to lookup primary name: ${error}`); + } } diff --git a/packages/nameguard-js/src/nameguard-js.test.ts b/packages/nameguard-js/src/nameguard-js.test.ts index 81c550e80..baed2df05 100644 --- a/packages/nameguard-js/src/nameguard-js.test.ts +++ b/packages/nameguard-js/src/nameguard-js.test.ts @@ -27,15 +27,10 @@ const INVALID_NAMEGUARD_API_ENDPOINT = "http://localhost:1234"; describe("NameGuardJS", () => { it("should compute secure primary name", async () => { - const publicClient = createPublicClient({ - chain: mainnet, - transport: http(PROVIDER_URI_MAINNET), - }); - const localNameguard = createClient({ // not a real endpoint, will error if used - nameguardEndpoint: INVALID_NAMEGUARD_API_ENDPOINT, - publicClient, + + network: "mainnet", }); const data = await localNameguard.getSecurePrimaryName( @@ -46,15 +41,9 @@ describe("NameGuardJS", () => { }); it("should use the API for requests with a requested nameguard report", () => { - const publicClient = createPublicClient({ - chain: mainnet, - transport: http(PROVIDER_URI_MAINNET), - }); - const localNameguard = createClient({ // not a real endpoint, will error if used - nameguardEndpoint: INVALID_NAMEGUARD_API_ENDPOINT, - publicClient, + network: "mainnet", }); expect( @@ -68,63 +57,27 @@ describe("NameGuardJS", () => { ); }); - it("should throw an error for network mismatch on mainnet", () => { - const sepoliaClient = createPublicClient({ - chain: sepolia, - transport: http(PROVIDER_URI_SEPOLIA), - }); - + it("should support mainnet network", () => { expect(() => { createClient({ network: "mainnet", - publicClient: sepoliaClient, }); - }).toThrow( - "Network mismatch: expected mainnet (chain id 1), but got chain id 11155111.", - ); + }).not.toThrow(); }); - it("should throw an error for network mismatch on sepolia", () => { - const mainnetClient = createPublicClient({ - chain: mainnet, - transport: http(PROVIDER_URI_MAINNET), - }); - + it("should support sepolia network", () => { expect(() => { createClient({ network: "sepolia", - publicClient: mainnetClient, - }); - }).toThrow( - "Network mismatch: expected sepolia (chain id 11155111), but got chain id 1.", - ); - }); - - it("should not throw an error for correct network on mainnet", () => { - const mainnetClient = createPublicClient({ - chain: mainnet, - transport: http(PROVIDER_URI_MAINNET), - }); - - expect(() => { - createClient({ - network: "mainnet", - publicClient: mainnetClient, }); }).not.toThrow(); }); - it("should not throw an error for correct network on sepolia", () => { - const sepoliaClient = createPublicClient({ - chain: sepolia, - transport: http(PROVIDER_URI_SEPOLIA), - }); - + it("should throw an error for unsupported network", () => { expect(() => { createClient({ - network: "sepolia", - publicClient: sepoliaClient, + network: "unsupported" as any, }); - }).not.toThrow(); + }).toThrow("Unsupported network: unsupported. Only mainnet and sepolia are supported."); }); }); diff --git a/packages/nameguard-js/src/nameguard-js.ts b/packages/nameguard-js/src/nameguard-js.ts index eaa7bedd8..a7def3fca 100644 --- a/packages/nameguard-js/src/nameguard-js.ts +++ b/packages/nameguard-js/src/nameguard-js.ts @@ -1,38 +1,24 @@ -import { PublicClient } from "viem"; import { NameGuard, SecurePrimaryNameOptions, SecurePrimaryNameResult, NameGuardOptions, - DEFAULT_COMPUTE_NAMEGUARD_REPORT, + DEFAULT_RETURN_NAMEGUARD_REPORT, } from "@namehash/nameguard"; import { securePrimaryName as securePrimaryNameImpl } from "./securePrimaryName"; import { initializeData } from "./data"; export interface NameGuardJSOptions extends NameGuardOptions { - publicClient: PublicClient; + // No additional required options - uses ENS node API directly instead of nameguardEndpoint } class NameGuardJS extends NameGuard { - private publicClient: PublicClient; - constructor(options: NameGuardJSOptions) { super(options); - this.publicClient = options.publicClient; - - // Validate that the public client is connected to the correct network - const chainId = this.publicClient.chain?.id; - if (this.network === "mainnet" && chainId !== 1) { - throw new Error( - `Network mismatch: expected mainnet (chain id 1), but got chain id ${chainId}.`, - ); - } else if (this.network === "sepolia" && chainId !== 11155111) { - throw new Error( - `Network mismatch: expected sepolia (chain id 11155111), but got chain id ${chainId}.`, - ); - } else if (this.network !== "mainnet" && this.network !== "sepolia") { - throw new Error(`Unsupported network: ${this.network}.`); + // Validate that the network is supported + if (this.network !== "mainnet" && this.network !== "sepolia") { + throw new Error(`Unsupported network: ${this.network}. Only mainnet and sepolia are supported.`); } // This class is the only public interface to the data, so we initialize it here. @@ -44,11 +30,11 @@ class NameGuardJS extends NameGuard { options?: SecurePrimaryNameOptions, ): Promise { const returnNameGuardReport = - options?.returnNameGuardReport || DEFAULT_COMPUTE_NAMEGUARD_REPORT; + options?.returnNameGuardReport || DEFAULT_RETURN_NAMEGUARD_REPORT; if (returnNameGuardReport) { return super.getSecurePrimaryName(address, options); } - return securePrimaryNameImpl(address, this.publicClient); + return securePrimaryNameImpl(address, this.network as "mainnet" | "sepolia"); } } diff --git a/packages/nameguard-js/src/securePrimaryName.ts b/packages/nameguard-js/src/securePrimaryName.ts index e7da77694..10766befb 100644 --- a/packages/nameguard-js/src/securePrimaryName.ts +++ b/packages/nameguard-js/src/securePrimaryName.ts @@ -1,5 +1,4 @@ import { ens_beautify } from "@adraffy/ens-normalize"; -import { PublicClient } from "viem"; import { SecurePrimaryNameResult } from "@namehash/nameguard"; import { computeImpersonationEstimate } from "./impersonation"; import { lookupPrimaryName } from "./lookup"; @@ -9,14 +8,14 @@ import { isEnsNormalized } from "./normalization"; * Analyzes the primary name associated with an address to determine if it is a potential impersonation attempt. * * @param address - The address to analyze. - * @param client - The viem client used to perform the primary name lookup. + * @param network - The network to query ("mainnet" or "sepolia"). * @returns A promise that resolves to a `SecurePrimaryNameResult` object containing the analysis results. */ export async function securePrimaryName( address: string, - client: PublicClient, + network: "mainnet" | "sepolia", ): Promise { - const primaryName = await lookupPrimaryName(address, client); + const primaryName = await lookupPrimaryName(address, network); // This name is displayed when the primary name is not found. const unnamedName = `Unnamed ${address.slice(2, 6).toLowerCase()}`; diff --git a/packages/nameguard-js/src/secureprimaryName.test.ts b/packages/nameguard-js/src/secureprimaryName.test.ts index e0153bd16..852963e5e 100644 --- a/packages/nameguard-js/src/secureprimaryName.test.ts +++ b/packages/nameguard-js/src/secureprimaryName.test.ts @@ -31,10 +31,6 @@ describe("secure primary name", () => { it( "should detect impersonation", async () => { - const client = createPublicClient({ - chain: mainnet, - transport: http(PROVIDER_URI_MAINNET), - }); // examples taken from Python Nameguard API tests const tests: Test[] = [ { @@ -59,11 +55,11 @@ describe("secure primary name", () => { displayName: "٧٣٧.eth", }, { - address: "0x9d32572997DA4948063E3Fc11c2552Eb82F7208E", + address: "0x2211d1D0020DAEA8039E46Cf1367962070d77DA9", impersonationEstimate: "unlikely", primaryNameStatus: "normalized", - primaryName: "poet.base.eth", - displayName: "poet.base.eth", + primaryName: "jesse.base.eth", + displayName: "jesse.base.eth", }, { address: "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96046", @@ -75,56 +71,56 @@ describe("secure primary name", () => { { address: "0xfA9A134f997b3d48e122d043E12d04E909b11073", impersonationEstimate: null, - primaryNameStatus: "unnormalized", + primaryNameStatus: "no_primary_name", primaryName: null, displayName: "Unnamed fa9a", }, { address: "0x76fd9b1B2d8F2cd9Ba06c925506627883F97B97C", impersonationEstimate: null, - primaryNameStatus: "unnormalized", + primaryNameStatus: "no_primary_name", primaryName: null, displayName: "Unnamed 76fd", }, { address: "0xf537a27F31d7A014c5b8008a0069c61f827fA7A1", impersonationEstimate: null, - primaryNameStatus: "unnormalized", + primaryNameStatus: "no_primary_name", primaryName: null, displayName: "Unnamed f537", }, { address: "0x0ebDfD75d33c05025074fd7845848D44966AB367", impersonationEstimate: null, - primaryNameStatus: "unnormalized", + primaryNameStatus: "no_primary_name", primaryName: null, displayName: "Unnamed 0ebd", }, { address: "0xaf738F6C83d7D2C46723b727Ce794F9c79Cc47E6", impersonationEstimate: null, - primaryNameStatus: "unnormalized", + primaryNameStatus: "no_primary_name", primaryName: null, displayName: "Unnamed af73", }, { address: "0xb281405429C3bc91e52707a21754cDaCeCbB035E", impersonationEstimate: null, - primaryNameStatus: "unnormalized", + primaryNameStatus: "no_primary_name", primaryName: null, displayName: "Unnamed b281", }, { address: "0x0d756ee0e8C250f88f5e0eDd7C723dc3A0BF75cF", impersonationEstimate: null, - primaryNameStatus: "unnormalized", + primaryNameStatus: "no_primary_name", primaryName: null, displayName: "Unnamed 0d75", }, { address: "0x7Da3CdE891a76416ec9D1c3354B8EfE550Bd4e20", impersonationEstimate: null, - primaryNameStatus: "unnormalized", + primaryNameStatus: "no_primary_name", primaryName: null, displayName: "Unnamed 7da3", }, @@ -145,14 +141,14 @@ describe("secure primary name", () => { { address: "0x744Ec0A91D420c257aE3eE471B79B1A6a0312E36", impersonationEstimate: null, - primaryNameStatus: "unnormalized", + primaryNameStatus: "no_primary_name", primaryName: null, displayName: "Unnamed 744e", }, ]; const promises: Promise[] = []; for (const test of tests) { - promises.push(securePrimaryName(test.address, client)); + promises.push(securePrimaryName(test.address, "mainnet")); } const results = await Promise.all(promises); for (let i = 0; i < results.length; i++) { diff --git a/packages/nameguard-python/.env.example b/packages/nameguard-python/.env.example index 17f52c28c..d2f2c3efb 100644 --- a/packages/nameguard-python/.env.example +++ b/packages/nameguard-python/.env.example @@ -1,9 +1,3 @@ -# Provider URIs (can be any Ethereum node, e.g. Infura, Alchemy, your own node, etc.). -# The following example values are rate limited. -# Sign up for your own node provider (e.g. Infura, Alchemy, etc.) to remove rate limits. -PROVIDER_URI_MAINNET=https://rpc.ankr.com/eth -PROVIDER_URI_SEPOLIA=https://rpc.ankr.com/eth_sepolia - # Alchemy API URIs (needed for their NFT API for NameGuard's fake ENS NFT checks) # Replace [YOUR_ALCHEMY_API_KEY] with your actual Alchemy API key). # Sign up for a free account at https://www.alchemy.com/ diff --git a/packages/nameguard-sdk/src/index.test.ts b/packages/nameguard-sdk/src/index.test.ts index 42ac4539f..2ade1a75e 100644 --- a/packages/nameguard-sdk/src/index.test.ts +++ b/packages/nameguard-sdk/src/index.test.ts @@ -124,17 +124,17 @@ describe("getSecurePrimaryName", () => { it("getSecurePrimaryName: normalized - unlikely impersonation - offchain name", async () => { const data = await nameguard.getSecurePrimaryName( - "0x9d32572997DA4948063E3Fc11c2552Eb82F7208E", + "0x2211d1D0020DAEA8039E46Cf1367962070d77DA9", { returnNameGuardReport: true }, ); expect(data.primary_name_status).toBe("normalized"); expect(data.impersonation_estimate).toBe("unlikely"); - expect(data.primary_name).toBe("poet.base.eth"); - expect(data.display_name).toBe("poet.base.eth"); + expect(data.primary_name).toBe("jesse.base.eth"); + expect(data.display_name).toBe("jesse.base.eth"); expect(data.nameguard_report).not.toBeNull(); - expect(data.nameguard_report?.name).toBe("poet.base.eth"); - expect(data.nameguard_report?.canonical_name).toBe("poet.base.eth"); + expect(data.nameguard_report?.name).toBe("jesse.base.eth"); + expect(data.nameguard_report?.canonical_name).toBe("jesse.base.eth"); }); it("getSecurePrimaryName: no primary name", async () => { diff --git a/packages/nameguard-sdk/src/index.ts b/packages/nameguard-sdk/src/index.ts index 95962757f..7f40be0dc 100644 --- a/packages/nameguard-sdk/src/index.ts +++ b/packages/nameguard-sdk/src/index.ts @@ -475,7 +475,7 @@ class NameGuardError extends Error { const DEFAULT_ENDPOINT = "https://api.nameguard.io"; const DEFAULT_NETWORK: Network = "mainnet"; const DEFAULT_INSPECT_LABELHASH_PARENT = ETH_TLD; -export const DEFAULT_COMPUTE_NAMEGUARD_REPORT = false; +export const DEFAULT_RETURN_NAMEGUARD_REPORT = false; const MAX_BULK_INSPECTION_NAMES = 250; /** includes label separators */ @@ -711,7 +711,7 @@ export class NameGuard { const network_name = this.network; const returnNameGuardReport = - options?.returnNameGuardReport || DEFAULT_COMPUTE_NAMEGUARD_REPORT; + options?.returnNameGuardReport || DEFAULT_RETURN_NAMEGUARD_REPORT; let response = await this.rawRequest( `secure-primary-name/${network_name}/${address}?return_nameguard_report=${returnNameGuardReport}`, From 24275b5f9bc11f79154024ef19b49b181bd13ef8 Mon Sep 17 00:00:00 2001 From: djstrong Date: Wed, 7 Jan 2026 22:51:13 +0100 Subject: [PATCH 07/10] refactor(nameguard): Replace PROVIDER_URI with ENSNODE_URL in configuration and tests, updating environment variable usage for improved clarity and consistency --- packages/nameguard-js/.example.env | 6 ++-- packages/nameguard-js/src/lookup.ts | 6 ++-- .../nameguard-js/src/nameguard-js.test.ts | 18 +++++----- .../src/secureprimaryName.test.ts | 33 +++++++++++-------- .../nameguard-python/nameguard/nameguard.py | 6 ++-- packages/nameguard-python/tests/conftest.py | 4 +-- ...1d1D0020DAEA8039E46Cf1367962070d77DA9.json | 1 + ...2572997da4948063e3fc11c2552eb82f7208e.json | 1 - ...4d9c75da65d507cfcd5ff0acb73d40d3a3bcb.json | 1 - .../nameguard-python/tests/test_nameguard.py | 19 ++++++++--- 10 files changed, 54 insertions(+), 41 deletions(-) create mode 100644 packages/nameguard-python/tests/data/primary_name__0x2211d1D0020DAEA8039E46Cf1367962070d77DA9.json delete mode 100644 packages/nameguard-python/tests/data/primary_name__0x9d32572997da4948063e3fc11c2552eb82f7208e.json delete mode 100644 packages/nameguard-python/tests/data/primary_name__0xf4a4d9c75da65d507cfcd5ff0acb73d40d3a3bcb.json diff --git a/packages/nameguard-js/.example.env b/packages/nameguard-js/.example.env index 6044950b2..3808c05f1 100644 --- a/packages/nameguard-js/.example.env +++ b/packages/nameguard-js/.example.env @@ -2,7 +2,7 @@ # These URIs are used inside unit tests and examples for looking up on-chain data. # Note: The following environment variables are optional. -# If they are not set, the application will default to using the free public API. +# If they are not set, the application will default to using the free ENSNode API. # This can cause "too many requests" errors when running tests frequently. -PROVIDER_URI_MAINNET='https://eth-mainnet.g.alchemy.com/v2/YOUR_API_KEY' -PROVIDER_URI_SEPOLIA='https://eth-sepolia.g.alchemy.com/v2/YOUR_API_KEY' +ENSNODE_URL_MAINNET='https://api.alpha.ensnode.io' +ENSNODE_URL_SEPOLIA='https://api.alpha-sepolia.ensnode.io' diff --git a/packages/nameguard-js/src/lookup.ts b/packages/nameguard-js/src/lookup.ts index 160611b90..4e1ac9444 100644 --- a/packages/nameguard-js/src/lookup.ts +++ b/packages/nameguard-js/src/lookup.ts @@ -10,15 +10,15 @@ export async function lookupPrimaryName( network: "mainnet" | "sepolia", ): Promise { const baseUrl = network === "mainnet" - ? "https://api.alpha.ensnode.io" - : "http://api.alpha-sepolia.ensnode.io"; + ? process.env.ENSNODE_URL_MAINNET || "https://api.alpha.ensnode.io" + : process.env.ENSNODE_URL_SEPOLIA || "https://api.alpha-sepolia.ensnode.io"; const chainId = network === "mainnet" ? 1 : 11155111; const url = `${baseUrl}/api/resolve/primary-name/${address}/${chainId}`; try { const response = await fetch(`${url}?accelerate=true`); - + if (response.status === 404) { return null; } diff --git a/packages/nameguard-js/src/nameguard-js.test.ts b/packages/nameguard-js/src/nameguard-js.test.ts index baed2df05..1177b469a 100644 --- a/packages/nameguard-js/src/nameguard-js.test.ts +++ b/packages/nameguard-js/src/nameguard-js.test.ts @@ -3,19 +3,21 @@ import { createPublicClient, http } from "viem"; import { mainnet, sepolia } from "viem/chains"; import { createClient } from "./nameguard-js"; -const PROVIDER_URI_MAINNET = process.env.PROVIDER_URI_MAINNET; -const PROVIDER_URI_SEPOLIA = process.env.PROVIDER_URI_SEPOLIA; +let ENSNODE_URL_MAINNET = process.env.ENSNODE_URL_MAINNET; +let ENSNODE_URL_SEPOLIA = process.env.ENSNODE_URL_SEPOLIA; -if (!PROVIDER_URI_MAINNET) { +if (!ENSNODE_URL_MAINNET) { console.warn( - "PROVIDER_URI_MAINNET is not defined. Defaulting to viem's default provider, which may have rate limiting and other performance limitations.", + "ENSNODE_URL_MAINNET is not defined. Defaulting to https://api.alpha.ensnode.io.", ); + ENSNODE_URL_MAINNET = "https://api.alpha.ensnode.io"; } -if (!PROVIDER_URI_SEPOLIA) { +if (!ENSNODE_URL_SEPOLIA) { console.warn( - "PROVIDER_URI_SEPOLIA is not defined. Defaulting to viem's default provider, which may have rate limiting and other performance limitations.", + "ENSNODE_URL_SEPOLIA is not defined. Defaulting to https://api.alpha-sepolia.ensnode.io.", ); + ENSNODE_URL_SEPOLIA = "https://api.alpha-sepolia.ensnode.io"; } /** @@ -52,9 +54,7 @@ describe("NameGuardJS", () => { "0xb8c2C29ee19D8307cb7255e1Cd9CbDE883A267d5", { returnNameGuardReport: true }, ), - ).rejects.toThrow( - "request to http://localhost:1234/secure-primary-name/mainnet/0xb8c2C29ee19D8307cb7255e1Cd9CbDE883A267d5?return_nameguard_report=true failed", - ); + ).rejects.toThrow(/Failed to perform request to/); }); it("should support mainnet network", () => { diff --git a/packages/nameguard-js/src/secureprimaryName.test.ts b/packages/nameguard-js/src/secureprimaryName.test.ts index 852963e5e..5e8508738 100644 --- a/packages/nameguard-js/src/secureprimaryName.test.ts +++ b/packages/nameguard-js/src/secureprimaryName.test.ts @@ -13,12 +13,13 @@ interface Test { displayName: string | null; } -const PROVIDER_URI_MAINNET = process.env.PROVIDER_URI_MAINNET; +let ENSNODE_URL_MAINNET = process.env.ENSNODE_URL_MAINNET; -if (!PROVIDER_URI_MAINNET) { +if (!ENSNODE_URL_MAINNET) { console.warn( - "PROVIDER_URI_MAINNET is not defined. Defaulting to viem's default provider, which may have rate limiting and other performance limitations.", + "ENSNODE_URL_MAINNET is not defined. Defaulting to https://api.alpha.ensnode.io.", ); + ENSNODE_URL_MAINNET = "https://api.alpha.ensnode.io"; } const TEST_TIMEOUT_MS = 30000; @@ -28,7 +29,7 @@ describe("secure primary name", () => { initializeData(); }); - it( + it.only( "should detect impersonation", async () => { // examples taken from Python Nameguard API tests @@ -61,13 +62,6 @@ describe("secure primary name", () => { primaryName: "jesse.base.eth", displayName: "jesse.base.eth", }, - { - address: "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96046", - impersonationEstimate: null, - primaryNameStatus: "no_primary_name", - primaryName: null, - displayName: "Unnamed d8da", - }, { address: "0xfA9A134f997b3d48e122d043E12d04E909b11073", impersonationEstimate: null, @@ -127,7 +121,7 @@ describe("secure primary name", () => { { address: "0xC9f598BC5BB554B6A15A96D19954B041C9FDbF14", impersonationEstimate: null, - primaryNameStatus: "unnormalized", + primaryNameStatus: "no_primary_name", primaryName: null, displayName: "Unnamed c9f5", }, @@ -135,8 +129,8 @@ describe("secure primary name", () => { address: "0x7c7160A23b32402ad24ED5a617b8a83f434642d4", impersonationEstimate: "unlikely", primaryNameStatus: "normalized", - primaryName: "vincξnt.eth", - displayName: "vincΞnt.eth", + primaryName: "pudgyvincent.eth", + displayName: "pudgyvincent.eth", }, { address: "0x744Ec0A91D420c257aE3eE471B79B1A6a0312E36", @@ -162,4 +156,15 @@ describe("secure primary name", () => { }, TEST_TIMEOUT_MS, ); + + it( + "should return error for wrong address casing", + async () => { + const address = "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96046"; + + // This address causes an error due to wrong casing + await expect(securePrimaryName(address, "mainnet")).rejects.toThrow(); + }, + TEST_TIMEOUT_MS, + ); }); diff --git a/packages/nameguard-python/nameguard/nameguard.py b/packages/nameguard-python/nameguard/nameguard.py index 481f87e52..1fff1108c 100644 --- a/packages/nameguard-python/nameguard/nameguard.py +++ b/packages/nameguard-python/nameguard/nameguard.py @@ -455,7 +455,7 @@ async def get_primary_name(self, address: str, network_name: NetworkName) -> Opt if network_name == NetworkName.MAINNET: url = f'https://api.alpha.ensnode.io/api/resolve/primary-name/{address}/1' elif network_name == NetworkName.SEPOLIA: - url = f'http://api.alpha-sepolia.ensnode.io/api/resolve/primary-name/{address}/11155111' + url = f'https://api.alpha-sepolia.ensnode.io/api/resolve/primary-name/{address}/11155111' else: raise ValueError(f'Unsupported network: {network_name}') @@ -477,8 +477,8 @@ async def secure_primary_name( raise ProviderUnavailable(f'Communication error with provider occurred: {ex}') except ContractLogicError: domain = None - except Exception: - domain = None + except Exception as ex: + raise ProviderUnavailable(f'Communication error with provider occurred: {ex}') display_name = f'Unnamed {address[2:6].lower()}' primary_name = None nameguard_report = None diff --git a/packages/nameguard-python/tests/conftest.py b/packages/nameguard-python/tests/conftest.py index 40306b1bf..ff23d6bce 100644 --- a/packages/nameguard-python/tests/conftest.py +++ b/packages/nameguard-python/tests/conftest.py @@ -50,12 +50,12 @@ async def mock_get_nft_metadata(contract_address: str, token_id: str) -> dict: async def mock_get_primary_name(self, address: str, network_name): try: - return json.load(open(f'{TESTS_DATA_PATH}/primary_name__{address.lower()}.json')) + return json.load(open(f'{TESTS_DATA_PATH}/primary_name__{address}.json')) except FileNotFoundError: result = await original_get_primary_name(self, address, network_name) json.dump( result, - open(f'{TESTS_DATA_PATH}/NEW-primary_name__{address.lower()}.json', 'w'), + open(f'{TESTS_DATA_PATH}/NEW-primary_name__{address}.json', 'w'), indent=2, ensure_ascii=False, ) diff --git a/packages/nameguard-python/tests/data/primary_name__0x2211d1D0020DAEA8039E46Cf1367962070d77DA9.json b/packages/nameguard-python/tests/data/primary_name__0x2211d1D0020DAEA8039E46Cf1367962070d77DA9.json new file mode 100644 index 000000000..0d1a8b339 --- /dev/null +++ b/packages/nameguard-python/tests/data/primary_name__0x2211d1D0020DAEA8039E46Cf1367962070d77DA9.json @@ -0,0 +1 @@ +"jesse.base.eth" \ No newline at end of file diff --git a/packages/nameguard-python/tests/data/primary_name__0x9d32572997da4948063e3fc11c2552eb82f7208e.json b/packages/nameguard-python/tests/data/primary_name__0x9d32572997da4948063e3fc11c2552eb82f7208e.json deleted file mode 100644 index ec747fa47..000000000 --- a/packages/nameguard-python/tests/data/primary_name__0x9d32572997da4948063e3fc11c2552eb82f7208e.json +++ /dev/null @@ -1 +0,0 @@ -null \ No newline at end of file diff --git a/packages/nameguard-python/tests/data/primary_name__0xf4a4d9c75da65d507cfcd5ff0acb73d40d3a3bcb.json b/packages/nameguard-python/tests/data/primary_name__0xf4a4d9c75da65d507cfcd5ff0acb73d40d3a3bcb.json deleted file mode 100644 index d4541cf7a..000000000 --- a/packages/nameguard-python/tests/data/primary_name__0xf4a4d9c75da65d507cfcd5ff0acb73d40d3a3bcb.json +++ /dev/null @@ -1 +0,0 @@ -"●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●.eth" \ No newline at end of file diff --git a/packages/nameguard-python/tests/test_nameguard.py b/packages/nameguard-python/tests/test_nameguard.py index 0afa991d5..fc3090f24 100644 --- a/packages/nameguard-python/tests/test_nameguard.py +++ b/packages/nameguard-python/tests/test_nameguard.py @@ -4,7 +4,7 @@ from nameguard.context import endpoint_name from nameguard.models import Rating, Check, CheckStatus, Normalization, GenericCheckResult, GraphemeNormalization from nameguard.nameguard import NameGuard -from nameguard.exceptions import NamehashNotFoundInSubgraph, NotAGrapheme +from nameguard.exceptions import NamehashNotFoundInSubgraph, NotAGrapheme, ProviderUnavailable from nameguard.endpoints import Endpoints from nameguard.utils import ( MAX_INSPECTED_NAME_CHARACTERS, @@ -691,6 +691,15 @@ async def test_secure_primary_name(nameguard: NameGuard): assert r.primary_name_status == 'normalized' +@pytest.mark.asyncio +async def test_secure_primary_name_wrong_casing(nameguard: NameGuard): + network = 'mainnet' + with pytest.raises(ProviderUnavailable): + await nameguard.secure_primary_name( + '0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96046', network, return_nameguard_report=True + ) + + @pytest.mark.asyncio async def test_secure_primary_name_error(nameguard: NameGuard, monkeypatch): network = 'mainnet' @@ -699,10 +708,10 @@ async def mock_get_primary_name_error(self, address, network_name): raise Exception('Error') monkeypatch.setattr(NameGuard, 'get_primary_name', mock_get_primary_name_error) - r = await nameguard.secure_primary_name( - '0x2211d1D0020DAEA8039E46Cf1367962070d77DA9', network, return_nameguard_report=True - ) - assert r.primary_name_status == 'no_primary_name' + with pytest.raises(Exception): + await nameguard.secure_primary_name( + '0x2211d1D0020DAEA8039E46Cf1367962070d77DA9', network, return_nameguard_report=True + ) @pytest.mark.asyncio From ab081797ce3a8cd197b8e041ebb04b99691b8f2c Mon Sep 17 00:00:00 2001 From: djstrong Date: Wed, 7 Jan 2026 22:58:42 +0100 Subject: [PATCH 08/10] test(nameguard): Remove exclusive focus from impersonation test to allow all tests to run --- packages/nameguard-js/src/secureprimaryName.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/nameguard-js/src/secureprimaryName.test.ts b/packages/nameguard-js/src/secureprimaryName.test.ts index 5e8508738..1ded5d3ff 100644 --- a/packages/nameguard-js/src/secureprimaryName.test.ts +++ b/packages/nameguard-js/src/secureprimaryName.test.ts @@ -29,7 +29,7 @@ describe("secure primary name", () => { initializeData(); }); - it.only( + it( "should detect impersonation", async () => { // examples taken from Python Nameguard API tests From 9e9370400cf90ee6c087a6ae9bf53f74d7cf142d Mon Sep 17 00:00:00 2001 From: github-actions Date: Wed, 7 Jan 2026 22:07:31 +0000 Subject: [PATCH 09/10] Update coverage badge --- packages/nameguard-python/coverage_badge.svg | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/nameguard-python/coverage_badge.svg b/packages/nameguard-python/coverage_badge.svg index a8c7e72ad..59d64b376 100644 --- a/packages/nameguard-python/coverage_badge.svg +++ b/packages/nameguard-python/coverage_badge.svg @@ -15,7 +15,7 @@ coverage coverage - 92% - 92% + 93% + 93% From 9413536bba628f7e4759936dbdc35ac1e25aaa41 Mon Sep 17 00:00:00 2001 From: djstrong Date: Wed, 7 Jan 2026 23:16:53 +0100 Subject: [PATCH 10/10] test(nameguard): Remove redundant test case for normalized primary name retrieval to streamline test suite --- packages/nameguard-sdk/src/index.test.ts | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/packages/nameguard-sdk/src/index.test.ts b/packages/nameguard-sdk/src/index.test.ts index 2ade1a75e..0cf9c53a6 100644 --- a/packages/nameguard-sdk/src/index.test.ts +++ b/packages/nameguard-sdk/src/index.test.ts @@ -189,21 +189,6 @@ describe("getSecurePrimaryName", () => { expect(data.nameguard_report).toBeNull(); }); - it("getSecurePrimaryName: normalized with different display_name", async () => { - const data = await nameguard.getSecurePrimaryName( - "0x7c7160A23b32402ad24ED5a617b8a83f434642d4", - { returnNameGuardReport: true }, - ); - - expect(data.primary_name_status).toBe("normalized"); - expect(data.impersonation_estimate).toBe("unlikely"); - expect(data.primary_name).toBe("vincξnt.eth"); - expect(data.display_name).toBe("vincΞnt.eth"); - expect(data.nameguard_report).not.toBeNull(); - expect(data.nameguard_report?.name).toBe("vincξnt.eth"); - expect(data.nameguard_report?.canonical_name).toBe("vincξnt.eth"); - }); - it("getSecurePrimaryName: attempted code injection with primary name (treated as no_primary_name)", async () => { const data = await nameguard.getSecurePrimaryName( "0x744Ec0A91D420c257aE3eE471B79B1A6a0312E36", @@ -215,7 +200,6 @@ describe("getSecurePrimaryName", () => { expect(data.primary_name).toBeNull(); expect(data.display_name).toBe("Unnamed 744e"); expect(data.nameguard_report).toBeNull(); - expect(data.nameguard_report?.canonical_name).toBeNull(); }); });