Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions .changeset/spicy-gifts-say.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
"ensadmin": patch
---

Validate name input on the Explore Names page (`/name`). The form normalizes user input before navigating and displays inline errors for unnormalizable names or names with encoded labelhashes (resolution support is in progress). Query params on the detail page are validated against `InterpretedName`.

Unnormalized names and names with encoded labelhashes each show a dedicated error instead of falling through to a broken detail page.

An empty `?name=` shows the form rather than the detail page.
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"use client";

import type { Name } from "enssdk";
import type { InterpretedName } from "enssdk";

import { ASSUME_IMMUTABLE_QUERY, useRecords } from "@ensnode/ensnode-react";
import type { ResolverRecordsSelection } from "@ensnode/ensnode-sdk";
Expand Down Expand Up @@ -41,7 +41,7 @@ const AllRequestedTextRecords = [
];

interface NameDetailPageContentProps {
name: Name;
name: InterpretedName;
}

export function NameDetailPageContent({ name }: NameDetailPageContentProps) {
Expand Down
20 changes: 20 additions & 0 deletions apps/ensadmin/src/app/name/_components/NameErrors.tsx
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I'm aware this is a new file, and not sure where this lives long term.

Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { ErrorInfo } from "@/components/error-info";

export function UnnormalizedNameError() {
return (
<section className="p-6">
<ErrorInfo title="Invalid Name" description="The provided name is not a valid ENS name." />
Copy link

Copilot AI Mar 29, 2026

Choose a reason for hiding this comment

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

UnnormalizedNameError is shown for any name that is neither a NormalizedName nor an InterpretedName, which includes names that are valid and normalizable but simply not normalized (e.g. VITALIK.ETH). In that case, the description "not a valid ENS name" is inaccurate/misleading. Consider distinguishing "valid but not normalized" (e.g., normalize(name) succeeds but differs) from "invalid" (normalization throws) and adjusting the message accordingly.

Suggested change
<ErrorInfo title="Invalid Name" description="The provided name is not a valid ENS name." />
<ErrorInfo
title="Unnormalized Name"
description="The provided name is not in normalized ENS form. Please use a normalized ENS name (for example, lowercase and properly formatted)."
/>

Copilot uses AI. Check for mistakes.
</section>
);
}

export function InterpretedNameUnsupportedError() {
return (
<section className="p-6">
<ErrorInfo
title="Encoded Labelhash Detected"
description="The provided name contains encoded labelhashes. Support for resolving names with encoded labelhashes is in progress and coming soon."
/>
</section>
);
}
Comment on lines +11 to +20
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

Consider displaying the interpreted name in the error message.

Since InterpretedNameUnsupportedError is shown for valid InterpretedName values (with encoded labelhashes), you could accept a name: InterpretedName prop and display it using formatInterpretedNameForDisplay for better user context. This aligns with past review feedback suggesting <InterpretedNameUnsupportedError name={...} />.

💡 Optional enhancement
+import type { InterpretedName } from "@ensnode/ensnode-sdk";
+import { formatInterpretedNameForDisplay } from "@/lib/format-interpreted-name-for-display";
 import { ErrorInfo } from "@/components/error-info";

-export function InterpretedNameUnsupportedError() {
+export function InterpretedNameUnsupportedError({ name }: { name: InterpretedName }) {
   return (
     <section className="p-6">
       <ErrorInfo
         title="Encoded Labelhash Detected"
-        description="The provided name contains encoded labelhashes. Support for resolving names with encoded labelhashes is in progress and coming soon."
+        description={`The name "${formatInterpretedNameForDisplay(name)}" contains encoded labelhashes. Support for resolving names with encoded labelhashes is in progress and coming soon.`}
       />
     </section>
   );
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/ensadmin/src/app/name/_components/NameErrors.tsx` around lines 11 - 20,
Update the InterpretedNameUnsupportedError component to accept a prop name of
type InterpretedName and show the interpreted name in the error description
using formatInterpretedNameForDisplay; specifically, change the
InterpretedNameUnsupportedError signature to take { name: InterpretedName },
call formatInterpretedNameForDisplay(name) and include that string in the
ErrorInfo description (or add a separate field) so the rendered message shows
the actual interpreted name for context.

4 changes: 2 additions & 2 deletions apps/ensadmin/src/app/name/_components/ProfileHeader.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
"use client";

import { EnsAvatar, NameDisplay } from "@namehash/namehash-ui";
import type { Name } from "enssdk";
import type { InterpretedName } from "enssdk";

import type { ENSNamespaceId } from "@ensnode/ensnode-sdk";

Expand All @@ -10,7 +10,7 @@ import { Card, CardContent } from "@/components/ui/card";
import { beautifyUrl } from "@/lib/beautify-url";

interface ProfileHeaderProps {
name: Name;
name: InterpretedName;
namespaceId: ENSNamespaceId;
headerImage?: string | null;
websiteUrl?: string | null;
Expand Down
66 changes: 52 additions & 14 deletions apps/ensadmin/src/app/name/page.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,15 @@
"use client";

import { NameDisplay } from "@namehash/namehash-ui";
import type { Name } from "enssdk";
import {
asInterpretedName,
asLiteralName,
type InterpretedName,
isInterpretedName,
isNormalizedName,
literalNameToInterpretedName,
type Name,
} from "enssdk";
import { useRouter, useSearchParams } from "next/navigation";
import { type ChangeEvent, useMemo, useState } from "react";

Expand All @@ -16,8 +24,9 @@ import { useActiveEnsNodeStackInfo } from "@/hooks/active/use-active-ensnode-sta
import { useRawConnectionUrlParam } from "@/hooks/use-connection-url-param";

import { NameDetailPageContent } from "./_components/NameDetailPageContent";
import { InterpretedNameUnsupportedError, UnnormalizedNameError } from "./_components/NameErrors";

const EXAMPLE_NAMES: NamespaceSpecificValue<string[]> = {
const EXAMPLE_NAMES: NamespaceSpecificValue<InterpretedName[]> = {
default: [
"vitalik.eth",
"gregskril.eth",
Expand All @@ -31,15 +40,15 @@ const EXAMPLE_NAMES: NamespaceSpecificValue<string[]> = {
"lens.xyz",
"brantly.eth",
"lightwalker.eth",
],
].map(asInterpretedName),
[ENSNamespaceIds.Sepolia]: [
"gregskril.eth",
"vitalik.eth",
"myens.eth",
"recordstest.eth",
"arrondesean.eth",
"decode.eth",
],
].map(asInterpretedName),
[ENSNamespaceIds.EnsTestEnv]: [
"alias.eth",
"changerole.eth",
Expand All @@ -53,14 +62,15 @@ const EXAMPLE_NAMES: NamespaceSpecificValue<string[]> = {
"sub2.parent.eth",
"test.eth",
"wallet.linked.parent.eth",
],
].map(asInterpretedName),
};

export default function ExploreNamesPage() {
const router = useRouter();
const searchParams = useSearchParams();
const nameFromQuery = searchParams.get("name");
const [rawInputName, setRawInputName] = useState<Name>("");
const [formError, setFormError] = useState<string | null>(null);

const { namespace } = useActiveEnsNodeStackInfo().ensIndexer;
const exampleNames = useMemo(
Expand All @@ -72,22 +82,49 @@ export default function ExploreNamesPage() {

const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();

// TODO: Input validation and normalization.
// see: https://github.com/namehash/ensnode/issues/1140

const href = retainCurrentRawConnectionUrlParam(getNameDetailsRelativePath(rawInputName));

setFormError(null);

if (rawInputName.trim() === "") return;

let interpreted: InterpretedName;
try {
// Allow encoded labelhashes through; throw on unnormalizable labels.
interpreted = literalNameToInterpretedName(asLiteralName(rawInputName), {
Comment on lines +87 to +92
allowEncodedLabelHashes: true,
});
} catch {
setFormError("The provided input is not a valid ENS name.");
return;
}

if (!isNormalizedName(interpreted)) {
setFormError(
"The provided input contains encoded labelhashes. Support for resolving names with encoded labelhashes is in progress and coming soon.",
);
return;
}

const href = retainCurrentRawConnectionUrlParam(getNameDetailsRelativePath(interpreted));
router.push(href);
};

const handleRawInputNameChange = (e: ChangeEvent<HTMLInputElement>) => {
e.preventDefault();

setFormError(null);
setRawInputName(e.target.value);
};

if (nameFromQuery) {
// Detail page: validate name from query params using only validation checks (no normalization).
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Suggested change
// Detail page: validate name from query params using only validation checks (no normalization).

// see: https://github.com/namehash/ensnode/issues/1140
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Suggested change
// see: https://github.com/namehash/ensnode/issues/1140

Done, I can remove these TODos now.

if (nameFromQuery !== null && nameFromQuery !== "") {
if (!isInterpretedName(nameFromQuery)) {
return <UnnormalizedNameError />;
}

if (!isNormalizedName(nameFromQuery)) {
return <InterpretedNameUnsupportedError />;
}

return <NameDetailPageContent name={nameFromQuery} />;
}

Expand All @@ -112,12 +149,13 @@ export default function ExploreNamesPage() {
/>
<Button
type="submit"
disabled={rawInputName.length === 0}
disabled={rawInputName.trim().length === 0}
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

@lightwalker-eth I added trim() here because I was seeing the View Profile button become active, and I think it's better it remains disabled. Empty characters are validated again but the disabled button is only checking for length.

Image

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

@notrab In my mind we make "Empty" a special class of the interpretation outcome and then build logic such as disabling a "View Profile" button based on the "Empty" class of interpretation outcomes.

className="max-sm:self-stretch"
>
View Profile
</Button>
</fieldset>
{formError && <p className="text-sm text-red-600">{formError}</p>}
</form>
<div className="flex flex-col gap-2 justify-center">
<p className="text-sm font-medium leading-none">Examples:</p>
Expand Down
12 changes: 9 additions & 3 deletions packages/namehash-ui/src/components/identity/Name.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { Name } from "enssdk";
import { beautifyName } from "enssdk";
import { beautifyInterpretedName, isInterpretedName } from "enssdk";

interface NameDisplayProps {
name: Name;
Expand All @@ -9,10 +9,16 @@ interface NameDisplayProps {
/**
* Displays an ENS name in beautified form.
*
* @param name - The name to display in beautified form.
* If the provided name is not a valid InterpretedName, displays
* "(invalid name)" instead.
*
* @param name - The name to display.
*/
export function NameDisplay({ name, className = "nhui:font-medium" }: NameDisplayProps) {
const beautifiedName = beautifyName(name);
if (!isInterpretedName(name)) {
return <span className={className}>(invalid name)</span>;
}
Comment on lines +18 to +20
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify whether NameDisplay is used with names that are not pre-validated as InterpretedName.

# 1) Locate NameDisplay imports and JSX call sites.
rg -n -C3 --type=ts --type=tsx 'NameDisplay'
rg -n -C3 --type=tsx '<NameDisplay[^>]*\bname='

# 2) In files using NameDisplay, inspect nearby guards/typing for interpreted-name validation.
rg -n -C4 --type=ts --type=tsx 'isInterpretedName\(|isNormalizedName\(|:\s*Name\b|:\s*InterpretedName\b|:\s*NormalizedName\b'

Repository: namehash/ensnode

Length of output: 143


🏁 Script executed:

#!/bin/bash
# Verify NameDisplay usage with corrected rg syntax

# 1) Search for NameDisplay imports and usages (using basic ts type or combined search)
rg -n 'NameDisplay' --type=ts

# 2) Search for JSX call sites of NameDisplay  
rg -n '<NameDisplay' --type=ts

# 3) Search for isInterpretedName, isNormalizedName, Name type, InterpretedName type usage
rg -n 'isInterpretedName|isNormalizedName|:\s*Name\b|:\s*InterpretedName' --type=ts

Repository: namehash/ensnode

Length of output: 13337


🏁 Script executed:

#!/bin/bash

# 1. Check what namedRegistrarAction.name is in RegistrarActionCard
echo "=== RegistrarActionCard context ==="
sed -n '295,310p' packages/namehash-ui/src/components/registrar-actions/RegistrarActionCard.tsx

# 2. Check ResolveAndDisplayIdentity and what name it passes
echo ""
echo "=== ResolveAndDisplayIdentity context ==="
sed -n '170,190p' packages/namehash-ui/src/components/identity/ResolveAndDisplayIdentity.tsx

# 3. Check the exampleName in ensadmin/app/name/page.tsx
echo ""
echo "=== ensadmin name/page.tsx context ==="
sed -n '165,180p' apps/ensadmin/src/app/name/page.tsx

# 4. Check ProfileHeader context
echo ""
echo "=== ProfileHeader context ==="
sed -n '70,85p' apps/ensadmin/src/app/name/_components/ProfileHeader.tsx

# 5. Check beautifyName implementation
echo ""
echo "=== beautifyName function ==="
rg -A10 'export const beautifyName' packages/ensnode-sdk/src/ens/names.ts

Repository: namehash/ensnode

Length of output: 3099


🏁 Script executed:

#!/bin/bash

# 1. Check namedRegistrarAction type in RegistrarActionCard
echo "=== RegistrarActionCard namedRegistrarAction type ==="
sed -n '1,50p' packages/namehash-ui/src/components/registrar-actions/RegistrarActionCard.tsx | grep -A5 "namedRegistrarAction\|RegistrarAction"

# 2. Check the registrar-actions types
echo ""
echo "=== Check registrar-actions types ==="
rg -n 'namedRegistrarAction\s*:' packages/namehash-ui/src/components/registrar-actions/RegistrarActionCard.tsx -A2

# 3. Check identity type in ResolveAndDisplayIdentity
echo ""
echo "=== ResolveAndDisplayIdentity identity type ==="
sed -n '1,100p' packages/namehash-ui/src/components/identity/ResolveAndDisplayIdentity.tsx | grep -A5 'identity\|Identity'

# 4. Check ensadmin ProfileHeader name type
echo ""
echo "=== ProfileHeader name parameter type ==="
sed -n '1,30p' apps/ensadmin/src/app/name/_components/ProfileHeader.tsx

# 5. Check EXAMPLE_ADDRESSES type
echo ""
echo "=== EXAMPLE_ADDRESSES definition ==="
sed -n '40,50p' apps/ensadmin/src/app/inspect/_lib/example-addresses.ts

Repository: namehash/ensnode

Length of output: 5195


🏁 Script executed:

#!/bin/bash

# 1. Check NormalizedName type definition
echo "=== NormalizedName definition ==="
rg -n 'type NormalizedName\|export.*NormalizedName' packages/ensnode-sdk/src/ -A3

# 2. Check InterpretedName definition
echo ""
echo "=== InterpretedName definition ==="
rg -n 'type InterpretedName\|export.*InterpretedName' packages/ensnode-sdk/src/ -A3

# 3. Check NamedRegistrarAction type
echo ""
echo "=== NamedRegistrarAction definition ==="
rg -n 'NamedRegistrarAction' packages/ensnode-sdk/src/ -B2 -A8 | head -40

# 4. Check Identity.name type
echo ""
echo "=== Identity type definition ==="
rg -n 'interface Identity|type Identity' packages/ensnode-sdk/src/ -A15

Repository: namehash/ensnode

Length of output: 5789


🏁 Script executed:

#!/bin/bash

# Search for type aliases and exports
echo "=== Search for NormalizedName and InterpretedName in all files ==="
rg -n 'NormalizedName|InterpretedName' packages/ensnode-sdk/src/ens/ --type=ts -B2 -A2 | head -100

# Check the main ens directory structure
echo ""
echo "=== Files in ENS directory ==="
fd '\.ts$' packages/ensnode-sdk/src/ens/

# Check what beautifyName actually accepts and what isInterpretedName checks
echo ""
echo "=== isInterpretedName implementation ==="
sed -n '100,120p' packages/ensnode-sdk/src/shared/interpretation/interpreted-names-and-labels.ts

Repository: namehash/ensnode

Length of output: 8919


🏁 Script executed:

#!/bin/bash

# Check InterpretedLabel definition
echo "=== InterpretedLabel definition ==="
rg -n 'type InterpretedLabel|InterpretedLabel =' packages/ensnode-sdk/src/ -B2 -A2

# Check isInterpetedLabel implementation (note the typo in the code)
echo ""
echo "=== isInterpetedLabel implementation ==="
rg -n 'function isInterpetedLabel|export.*isInterpetedLabel' packages/ensnode-sdk/src/ -A10

# Check relationship between NormalizedLabel and InterpretedLabel
echo ""
echo "=== Check ens/types.ts for label definitions ==="
sed -n '1,150p' packages/ensnode-sdk/src/ens/types.ts | tail -60

Repository: namehash/ensnode

Length of output: 6391


🏁 Script executed:

#!/bin/bash

# Get the full NamedRegistrarAction interface definition
echo "=== NamedRegistrarAction full definition ==="
sed -n '32,46p' packages/ensnode-sdk/src/ensapi/api/registrar-actions/response.ts

# Get Identity type definition with name field
echo ""
echo "=== Identity and ResolvedIdentity types ==="
sed -n '140,170p' packages/ensnode-sdk/src/identity/types.ts

# Check if NormalizedName passes isInterpretedName test
echo ""
echo "=== Test: Does NormalizedName pass isInterpretedName? ==="
rg -n 'NormalizedName.*isInterpretedName|isInterpretedName.*NormalizedName' packages/ensnode-sdk/src/ -B2 -A2

Repository: namehash/ensnode

Length of output: 833


🏁 Script executed:

#!/bin/bash

# Get the full identity types
echo "=== Full Identity type definitions ==="
sed -n '50,160p' packages/ensnode-sdk/src/identity/types.ts

# Test whether NormalizedName is accepted as InterpretedName
echo ""
echo "=== Can NormalizedName be used where InterpretedName is required? ==="
grep -r 'NormalizedName' packages/ensnode-sdk/src/identity/types.ts

Repository: namehash/ensnode

Length of output: 2617


🏁 Script executed:

#!/bin/bash

# Check what types are passed to NameDisplay in reality
echo "=== Trace ResolveAndDisplayIdentity.NamedIdentity.name ==="
rg -n 'NamedIdentity\|name: Name' packages/ensnode-sdk/src/identity/types.ts -B5 -A5

# Check if there's a test showing NormalizedName passing isInterpretedName
echo ""
echo "=== isInterpretedName tests ==="
sed -n '1,200p' packages/ensnode-sdk/src/shared/interpretation/interpreted-names-and-labels.test.ts | grep -A20 "isInterpretedName"

# Check beautifyName more carefully - does it expect a specific type?
echo ""
echo "=== beautifyName signature and usage ==="
sed -n '65,85p' packages/ensnode-sdk/src/ens/names.ts

Repository: namehash/ensnode

Length of output: 546


🏁 Script executed:

#!/bin/bash

# Verify the exact flow: does NamedIdentity.name (which is Name) ever get a non-interpreted name?
echo "=== NamedIdentity interface full definition ==="
sed -n '70,105p' packages/ensnode-sdk/src/identity/types.ts

# Check if there's any validation/interpretation before NameDisplay gets the name
echo ""
echo "=== Check DisplayIdentity to see how identity.name flows to NameDisplay ==="
sed -n '160,200p' packages/namehash-ui/src/components/identity/ResolveAndDisplayIdentity.tsx

# One more check: does a NormalizedName always pass isInterpretedName?
echo ""
echo "=== Test with actual examples ==="
cat > /tmp/test_interpretation.ts << 'EOF'
// A NormalizedName is a string that is normalized
// isInterpretedLabel checks: if starts with '[' -> is it valid encoded labelhash? else -> isNormalizedLabel?
// So a NormalizedName where all labels are normalized would pass isInterpretedName

// Example 1: "vitalik.eth" (NormalizedName) 
// - Labels: "vitalik" (normalized), "eth" (normalized)
// - isInterpretedLabel("vitalik") = isNormalizedLabel("vitalik") = true
// - isInterpretedLabel("eth") = isNormalizedLabel("eth") = true
// - isInterpretedName returns true ✓

// Example 2: "name" where some labels aren't normalized and aren't encoded hashes (Name but not InterpretedName)
// - e.g., "ViTaLiK.eth" (uppercase, not normalized)
// - Labels: "ViTaLiK" (NOT normalized), "eth"
// - isInterpretedLabel("ViTaLiK") = isNormalizedLabel("ViTaLiK") = false (not normalized, doesn't start with '[')
// - isInterpretedName returns false ✗

// So the guard WILL reject valid displayable names that aren't normalized or encoded
EOF
cat /tmp/test_interpretation.ts

Repository: namehash/ensnode

Length of output: 3100


Over-strict guard hides valid display input and changes NameDisplay semantics.

Line 18 blocks beautifyName unless isInterpretedName(name) is true. However, beautifyName already handles any Name gracefully—it beautifies only normalized labels and preserves non-normalized ones unchanged. This guard now replaces displayable inputs (such as non-normalized names from NamedIdentity.name, which is typed as generic Name) with "(invalid name)", losing the original value.

💡 Minimal fix to preserve input while still avoiding strict beautification on invalid names
 export function NameDisplay({ name, className = "nhui:font-medium" }: NameDisplayProps) {
   if (!isInterpretedName(name)) {
-    return <span className={className}>(invalid name)</span>;
+    return <span className={className}>{name}</span>;
   }

   const beautifiedName = beautifyName(name);
   return <span className={className}>{beautifiedName}</span>;
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (!isInterpretedName(name)) {
return <span className={className}>(invalid name)</span>;
}
if (!isInterpretedName(name)) {
return <span className={className}>{name}</span>;
}
const beautifiedName = beautifyName(name);
return <span className={className}>{beautifiedName}</span>;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/namehash-ui/src/components/identity/Name.tsx` around lines 18 - 20,
The current guard using isInterpretedName(name) causes valid but non-normalized
inputs to be replaced with "(invalid name)"; remove that overly-strict check and
render the value via beautifyName(name) directly (keep the same span/className).
In other words, delete the if-block that returns "(invalid name)" and replace it
with: return <span className={className}>{beautifyName(name)}</span>; this
preserves original inputs while relying on beautifyName to handle normalization
safely.


const beautifiedName = beautifyInterpretedName(name);
return <span className={className}>{beautifiedName}</span>;
}
9 changes: 6 additions & 3 deletions packages/namehash-ui/src/utils/ensManager.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { Address, Name } from "enssdk";
import { type Address, isNormalizedName, type Name } from "enssdk";

import type { ENSNamespaceId } from "@ensnode/datasources";
import { ENSNamespaceIds } from "@ensnode/ensnode-sdk";
Expand Down Expand Up @@ -28,10 +28,13 @@ export function getEnsManagerUrl(namespaceId: ENSNamespaceId): URL | null {
/**
* Builds the URL of the external ENS Manager App Profile page for a given name and ENS Namespace.
*
* @returns URL to the Profile page in the external ENS Manager App for a given name and ENS Namespace,
* or null if this URL is not known
* Returns null if the name is not normalized or the namespace has no known ENS Manager App.
*
* @returns URL to the Profile page in the external ENS Manager App, or null
*/
export function getEnsManagerNameDetailsUrl(name: Name, namespaceId: ENSNamespaceId): URL | null {
if (!isNormalizedName(name)) return null;

const baseUrl = getEnsManagerUrl(namespaceId);
if (!baseUrl) return null;

Expand Down
Loading