Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

AllDomains #2

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions app/api/domain-info/[domain]/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { Connection } from "@solana/web3.js"
import { NextResponse } from "next/server"

import { MAINNET_BETA_URL } from "@/app/utils/cluster"
import { getDomainInfo } from "@/app/utils/domain-info"
import { getANSDomainInfo, getDomainInfo } from "@/app/utils/domain-info"

type Params = {
params: {
Expand All @@ -20,7 +20,7 @@ export async function GET(
// This is an API route so won't affect client bundle
// We only fetch domains on mainnet
const connection = new Connection(MAINNET_BETA_URL);
const domainInfo = await getDomainInfo(domain, connection);
const domainInfo = await (domain.substring(domain.length - 4) === '.sol' ? getDomainInfo(domain, connection) : getANSDomainInfo(domain, connection));

return NextResponse.json(domainInfo, {
headers: {
Expand Down
2 changes: 1 addition & 1 deletion app/components/SearchBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ interface SearchOptions {
}

const hasDomainSyntax = (value: string) => {
return value.length > 4 && value.substring(value.length - 4) === '.sol';
return value.length > 3 && value.split('.').length === 2;
};

export function SearchBar() {
Expand Down
21 changes: 17 additions & 4 deletions app/components/account/DomainsCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,33 @@ import { useUserDomains } from '@utils/name-service';
import React from 'react';

import { DomainInfo } from '@/app/utils/domain-info';
import { useUserANSDomains } from '../../utils/ans-domains';

export function DomainsCard({ address }: { address: string }) {
const [domains, domainsLoading] = useUserDomains(address);
const [domainsANS, domainsANSLoading] = useUserANSDomains(address);

if (domainsLoading && (!domains || domains.length === 0)) {
if (
(domainsLoading && (!domains || domains.length === 0)) ||
(domainsANSLoading && (!domainsANS || domainsANS.length === 0))
) {
return <LoadingCard message="Loading domains" />;
} else if (!domains) {
} else if (!domains || !domainsANS) {
return <ErrorCard text="Failed to fetch domains" />;
}

if (domains.length === 0) {
if (domains.length === 0 && domainsANS.length === 0) {
return <ErrorCard text="No domain name found" />;
}

let allDomains = domains;

if (domainsANS) {
allDomains = [...allDomains, ...domainsANS];
}

allDomains.sort((a, b) => a.name.localeCompare(b.name));

return (
<div className="card">
<div className="card-header align-items-center">
Expand All @@ -35,7 +48,7 @@ export function DomainsCard({ address }: { address: string }) {
</tr>
</thead>
<tbody className="list">
{domains.map(domain => (
{allDomains.map(domain => (
<RenderDomainRow key={domain.address.toBase58()} domainInfo={domain} />
))}
</tbody>
Expand Down
76 changes: 76 additions & 0 deletions app/utils/ans-domains.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import { NameRecordHeader,TldParser } from '@onsol/tldparser';
import { Connection } from '@solana/web3.js';
import pLimit from 'p-limit';
import { useEffect,useState } from 'react';

import { useCluster } from '../providers/cluster';
import { Cluster } from './cluster';
import { DomainInfo } from './domain-info';


export const useUserANSDomains = (userAddress: string): [DomainInfo[] | null, boolean] => {
const { url, cluster } = useCluster();
const [result, setResult] = useState<DomainInfo[] | null>(null);
const [loading, setLoading] = useState(false);

useEffect(() => {
const resolve = async () => {
// Allow only mainnet and custom
if (![Cluster.MainnetBeta, Cluster.Custom].includes(cluster)) return;
const connection = new Connection(url, 'confirmed');
try {
setLoading(true);

const parser = new TldParser(connection);
const allDomains = await parser.getAllUserDomains(userAddress);

if (!allDomains) {
return;
}
const userDomains: DomainInfo[] = [];
const limit = pLimit(5);
const promises = allDomains.map(address =>
limit(async () => {
const domainRecord = await NameRecordHeader.fromAccountAddress(connection, address);

// expired or not found
if (!domainRecord?.owner) return;

const domainParentNameAccount = await NameRecordHeader.fromAccountAddress(
connection,
domainRecord?.parentName
);

// not found
if (!domainParentNameAccount?.owner) return;

const tld = await parser.getTldFromParentAccount(domainRecord?.parentName);

const domain = await parser.reverseLookupNameAccount(
address,
domainParentNameAccount?.owner
);

// domain not found or might be a subdomain.
if (!domain) return;

userDomains.push({
address,
name: `${domain}${tld}`,
});
})
);

await Promise.all(promises);
setResult(userDomains);
} catch (err) {
console.log(`Error fetching user domains ${err}`);
} finally {
setLoading(false);
}
};
resolve();
}, [userAddress, url]); // eslint-disable-line react-hooks/exhaustive-deps

return [result, loading];
};
20 changes: 19 additions & 1 deletion app/utils/domain-info.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { getHashedName, getNameAccountKey, getNameOwner } from '@bonfida/spl-name-service';
import { getDomainKey as getANSDomainKey, getNameOwner as getANSNameOwner } from '@onsol/tldparser';
import { Connection, PublicKey } from '@solana/web3.js';

// Address of the SOL TLD
Expand All @@ -16,7 +17,7 @@ export interface DomainInfo {
}

export const hasDomainSyntax = (value: string) => {
return value.length > 4 && value.substring(value.length - 4) === '.sol';
return value.length > 3 && value.split('.').length === 2;
};

// returns non empty wallet string if a given .sol domain is owned by a wallet
Expand All @@ -38,3 +39,20 @@ export async function getDomainInfo(domain: string, connection: Connection) {
return null;
}
}

// returns owner address and name account address.
export async function getANSDomainInfo(domainTld: string, connection: Connection) {
const derivedDomainKey = await getANSDomainKey(domainTld.toLowerCase());
try {
// returns only non expired domains,
const owner = await getANSNameOwner(connection, derivedDomainKey.pubkey);
return owner
? {
address: derivedDomainKey.pubkey.toString(),
owner: owner.toString(),
}
: null;
} catch {
return null;
}
}
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
"@solana/web3.js": "^1.66.0",
"@solflare-wallet/utl-sdk": "^1.4.0",
"@types/bn.js": "5.1.0",
"@onsol/tldparser": "^0.6.3",
"axios": "^0.27.2",
"bignumber.js": "^9.0.2",
"bn.js": "5.2.1",
Expand Down
25 changes: 25 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.