Skip to content

Conversation

@djstrong
Copy link
Collaborator

@djstrong djstrong commented Dec 19, 2025

Tests fail because output from ENSNode API is different than from RPC.

================================================================ short test summary info =================================================================
FAILED tests/test_api.py::test_primary_name[0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045-unlikely-normalized-vitalik.eth-vitalik.eth-vitalik.eth-False-vitalik.eth] - AssertionError: assert None == 'unlikely'
FAILED tests/test_api.py::test_primary_name[0x9d32572997DA4948063E3Fc11c2552Eb82F7208E-unlikely-normalized-poet.base.eth-poet.base.eth-poet.base.eth-False-poet.base.eth] - AssertionError: assert None == 'unlikely'
FAILED tests/test_api.py::test_primary_name[0xfA9A134f997b3d48e122d043E12d04E909b11073-None-unnormalized-None-Unnamed fa9a-None-False-888\u200d\u200d.eth] - AssertionError: assert 'no_primary_name' == 'unnormalized'
FAILED tests/test_api.py::test_primary_name[0x76fd9b1B2d8F2cd9Ba06c925506627883F97B97C-None-unnormalized-None-Unnamed 76fd-None-False-\u200d\u200d\u2764\u200d\u200d.eth] - AssertionError: assert 'no_primary_name' == 'unnormalized'
FAILED tests/test_api.py::test_primary_name[0xf537a27F31d7A014c5b8008a0069c61f827fA7A1-None-unnormalized-None-Unnamed f537-None-False-\u0660\u0660\u06f1.eth] - AssertionError: assert 'no_primary_name' == 'unnormalized'
FAILED tests/test_api.py::test_primary_name[0x0ebDfD75d33c05025074fd7845848D44966AB367-None-unnormalized-None-Unnamed 0ebd-None-False-\u06f8\u06f8\u06f7\u06f5\u06f4\u06f2.eth] - AssertionError: assert 'no_primary_name' == 'unnormalized'
FAILED tests/test_api.py::test_primary_name[0xaf738F6C83d7D2C46723b727Ce794F9c79Cc47E6-None-unnormalized-None-Unnamed af73-None-False-\u0b68\u0b68\u0b68\u0b68\u0b68.eth] - AssertionError: assert 'no_primary_name' == 'unnormalized'
FAILED tests/test_api.py::test_primary_name[0xb281405429C3bc91e52707a21754cDaCeCbB035E-None-unnormalized-None-Unnamed b281-None-False-\u2523\u2587\u2587\u2587\u2550\u2500.eth] - AssertionError: assert 'no_primary_name' == 'unnormalized'
FAILED tests/test_api.py::test_primary_name[0x0d756ee0e8C250f88f5e0eDd7C723dc3A0BF75cF-None-unnormalized-None-Unnamed 0d75-None-False-\u0441\u0431\u0435\u0440.eth] - AssertionError: assert 'no_primary_name' == 'unnormalized'
FAILED tests/test_api.py::test_primary_name[0x7Da3CdE891a76416ec9D1c3354B8EfE550Bd4e20-None-unnormalized-None-Unnamed 7da3-vitalik.eth-True-vit\u0227lik.eth] - AssertionError: assert 'no_primary_name' == 'unnormalized'
FAILED tests/test_api.py::test_primary_name[0xC9f598BC5BB554B6A15A96D19954B041C9FDbF14-None-unnormalized-None-Unnamed c9f5-vitalik.eth-True-v\u0131talik.eth] - AssertionError: assert 'no_primary_name' == 'unnormalized'
FAILED tests/test_api.py::test_primary_name[0x7c7160A23b32402ad24ED5a617b8a83f434642d4-unlikely-normalized-vinc\u03bent.eth-vinc\u039ent.eth-vinc\u03bent.eth-False-vinc\u03bent.eth] - AssertionError: assert 'pudgyvincent.eth' == 'vincξnt.eth'
FAILED tests/test_api.py::test_primary_name[0x744Ec0A91D420c257aE3eE471B79B1A6a0312E36-None-unnormalized-None-Unnamed 744e-None-False-hello<world>!.eth] - AssertionError: assert 'no_primary_name' == 'unnormalized'
FAILED tests/test_api.py::test_primary_name_get - AssertionError: assert None == 'unlikely'
FAILED tests/test_api.py::test_primary_name_get_no_report - AssertionError: assert None == 'unlikely'
FAILED tests/test_api.py::test_primary_name_get_report - AssertionError: assert None == 'unlikely'
FAILED tests/test_api.py::test_primary_name_get_uppercase - AssertionError: assert None == 'unlikely'
FAILED tests/test_api.py::test_primary_name_get_offchain - AssertionError: assert None == 'unlikely'
FAILED tests/test_api.py::test_primary_name_get_unnormalized - AssertionError: assert 'no_primary_name' == 'unnormalized'
FAILED tests/test_api.py::test_primary_name_get_uninspected - AssertionError: assert 'no_primary_name' == 'uninspected'
FAILED tests/test_nameguard.py::test_dynamic_check_order - AttributeError: 'NoneType' object has no attribute 'checks'
FAILED tests/test_nameguard.py::test_secure_primary_name_error - AssertionError: assert <SecurePrimar... 'normalized'> == 'no_primary_name'
================================================= 22 failed, 190 passed, 2 xfailed in 115.80s (0:01:55) ==================================================

E.g. for address 0xFD9eE68000Dc92aa6c67F8f6EB5d9d1a24086fAd we got exampleprimary.cb.id from ENSNode but null from Alchemy.
https://api.alpha.ensnode.io/api/resolve/primary-name/0xFD9eE68000Dc92aa6c67F8f6EB5d9d1a24086fAd/1 ;
https://api.alpha.ensnode.io/api/resolve/primary-name/0xc9f598bc5bb554b6a15a96d19954b041c9fdbf14/1 gives null but Alchemy returns vıtalik.eth (unnormalized)

@changeset-bot
Copy link

changeset-bot bot commented Dec 19, 2025

⚠️ No Changeset found

Latest commit: d6b2448

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@vercel
Copy link
Contributor

vercel bot commented Dec 19, 2025

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Review Updated (UTC)
examples.nameguard.io Ready Ready Preview, Comment Jan 21, 2026 0:21am
nameai.io Ready Ready Preview, Comment Jan 21, 2026 0:21am
namegraph.dev Ready Ready Preview, Comment Jan 21, 2026 0:21am
nameguard.io Ready Ready Preview, Comment Jan 21, 2026 0:21am
namehashlabs.org Ready Ready Preview, Comment Jan 21, 2026 0:21am
namekit.io Ready Ready Preview, Comment Jan 21, 2026 0:21am
storybook.namekit.io Ready Ready Preview, Comment Jan 21, 2026 0:21am

@djstrong
Copy link
Collaborator Author

The decision is that ENSNode API behaviour is the correct one (if the primary name is unnormalized then we say there is no primary name) so we need to fix tests. Probably unnormalized state in the endpoint is no longer valid.
"Suggest to change that test (0xFD9eE68000Dc92aa6c67F8f6EB5d9d1a24086fAd) case to the address for jesse.base.eth
(0x2211d1D0020DAEA8039E46Cf1367962070d77DA9)"

the primary name is unnormalized.
"""
if network_name == NetworkName.MAINNET:
url = f'https://api.alpha.ensnode.io/api/resolve/primary-name/{address}/1'
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ethereum addresses are not being normalized to lowercase before being used in the ENSNode API URL, causing requests with checksummed/mixed-case addresses to fail.

View Details
📝 Patch Details
diff --git a/packages/nameguard-python/nameguard/nameguard.py b/packages/nameguard-python/nameguard/nameguard.py
index 1fff110..a6f64c5 100644
--- a/packages/nameguard-python/nameguard/nameguard.py
+++ b/packages/nameguard-python/nameguard/nameguard.py
@@ -452,6 +452,8 @@ class NameGuard:
             The normalized primary name, or None if no primary name exists or
             the primary name is unnormalized.
         """
+        # Normalize address to lowercase for ENSNode API compatibility
+        address = address.lower()
         if network_name == NetworkName.MAINNET:
             url = f'https://api.alpha.ensnode.io/api/resolve/primary-name/{address}/1'
         elif network_name == NetworkName.SEPOLIA:
diff --git a/packages/nameguard-python/tests/test_nameguard.py b/packages/nameguard-python/tests/test_nameguard.py
index fc3090f..5cb3481 100644
--- a/packages/nameguard-python/tests/test_nameguard.py
+++ b/packages/nameguard-python/tests/test_nameguard.py
@@ -694,10 +694,12 @@ async def test_secure_primary_name(nameguard: NameGuard):
 @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
-        )
+    # Test that mixed-case addresses are properly normalized and handled
+    r = await nameguard.secure_primary_name(
+        '0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96046', network, return_nameguard_report=True
+    )
+    # This address has no primary name
+    assert r.primary_name_status == 'no_primary_name'
 
 
 @pytest.mark.asyncio

Analysis

ENSNode API rejects mixed-case Ethereum addresses without normalization

What fails: The get_primary_name() method in nameguard/nameguard.py constructs ENSNode API URLs using address parameters without normalizing them to lowercase, causing requests with checksummed/mixed-case addresses to fail with HTTP 400 Bad Request errors.

How to reproduce:

# Call with mixed-case (checksummed) address
result = await nameguard.secure_primary_name(
    '0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96046',  # Mixed case
    'mainnet'
)
# Previously raised: ProviderUnavailable exception

Result: Before the fix, mixed-case addresses caused the ENSNode API to reject the request with HTTP 400, which was caught and converted to a ProviderUnavailable exception. This breaks legitimate use cases where Ethereum addresses are passed in checksummed format.

Expected: Mixed-case addresses should be automatically normalized to lowercase before being sent to the ENSNode API, allowing the API call to succeed.

Fix: Normalize the address parameter to lowercase in get_primary_name() method before embedding it in the API URL. The ENSNode API accepts lowercase addresses but rejects mixed-case ones. Updated test test_secure_primary_name_wrong_casing to verify that mixed-case addresses are now properly handled and return the expected primary name status.

);

it(
"should return error for wrong address casing",
Copy link
Contributor

@vercel vercel bot Jan 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Test expects securePrimaryName to throw error for mixed-case address, but function returns a result instead of throwing

Fix on Vercel



@pytest.mark.asyncio
async def test_secure_primary_name_wrong_casing(nameguard: NameGuard):
Copy link
Contributor

@vercel vercel bot Jan 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Test expects ProviderUnavailable exception when calling with mixed-case address, but address normalization should allow the lookup to succeed

Fix on Vercel

the primary name is unnormalized.
"""
if network_name == NetworkName.MAINNET:
url = f'https://api.alpha.ensnode.io/api/resolve/primary-name/{address}/1'
Copy link
Contributor

@vercel vercel bot Jan 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ethereum addresses are not being normalized to lowercase before being used in the ENSNode API URL, causing requests with checksummed/mixed-case addresses to fail with HTTP 400 Bad Request errors

Fix on Vercel

…vironment variable usage for ENSNode API URLs
Copy link
Member

@lightwalker-eth lightwalker-eth left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@djstrong Thanks for updates. Reviewed and shared feedback

# ENSNode URLs
ENSNODE_URL_MAINNET=https://api.alpha.ensnode.io
ENSNODE_URL_SEPOLIA=https://api.alpha-sepolia.ensnode.io

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@djstrong Several lines below I still see the following environment variables:

  1. ENS_SUBGRAPH_URL_MAINNET
  2. ENS_SUBGRAPH_URL_SEPOLIA

These should be fully removed because we can instead build ENS Subgraph API URLs as a function of ENSNODE_URL_* values by taking any ENSNode URL and just appending /subgraph to the end.

Ex:

  1. https://api.alpha.ensnode.io/subgraph
  2. https://api.alpha-sepolia.ensnode.io/subgraph

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"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should be able to completely remove any reference to any ENS Subgraphs that are hosted by The Graph.

```bash
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"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There shouldn't be any references to The Graph anymore.

# For more details, see:
# - https://docs.ens.domains/web/subgraph
# - https://discuss.ens.domains/t/ens-subgraph-migration-to-the-decentralised-version/19183
# - https://thegraph.com/explorer/subgraphs/5XqPmWe6gjyrJtFn9cLy237i4cWw2j9HcUJEXsP5qGtH?view=Query&chain=arbitrum-one
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is still an open issue

# - https://docs.ens.domains/web/subgraph
# - 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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These need to be updated. There's no need to configure subgraph URLs anymore.

Subgraph API URLs should be a function of ENSNODE_URL_MAINNET or ENSNODE_URL_SEPOLIA with the correct path added to talk to the subgraph API endpoint.

PROVIDER_URI_MAINNET=https://rpc.ankr.com/eth
PROVIDER_URI_SEPOLIA=https://rpc.ankr.com/eth_sepolia
# ENSNode URLs (optional - defaults to public ENSNode API endpoints)
# The ENSNode API is used for primary name lookups in secure-primary-name.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It should also be used for looking up labelhashes / namehashes

}

class NameGuardJS extends NameGuard {
private publicClient: PublicClient;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be creating an instance of ENSNodeClient as imported from ensnode-sdk.

Definition can be found here: https://github.com/namehash/ensnode/blob/main/packages/ensnode-sdk/src/client.ts

: 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}`;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We're supposed to use ENSNodeClient as imported from ensnode-sdk

"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";
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we make correct use of ENSNodeClient as imported from ensnode-sdk then there's no need to redefine a default URL here.

"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";
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, maybe it would help if ensnode-sdk gave a helper function to get the default URL for a particular ENS namespace. I'll work on a quick PR for this.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants