Skip to content

Instantly share code, notes, and snippets.

@crypt0miester
Created October 16, 2024 06:44
Show Gist options
  • Save crypt0miester/6391ae0df3c6edb9ea1635ebbf7da5d7 to your computer and use it in GitHub Desktop.
Save crypt0miester/6391ae0df3c6edb9ea1635ebbf7da5d7 to your computer and use it in GitHub Desktop.
import {
ANS_PROGRAM_ID,
findNameHouse,
findNftRecord,
getHashedName,
getNameAccountKeyWithBump,
getParentAccountFromTldHouseAccountInfo,
getTldFromTldHouseAccountInfo,
NameRecordHeader,
NftRecord,
TLD_HOUSE_PROGRAM_ID,
} from "@onsol/tldparser";
import {
Connection,
GetProgramAccountsResponse,
PublicKey,
} from "@solana/web3.js";
import pLimit from "p-limit";
const connection = new Connection("https://mainnetbeta-rpc.eclipse.xyz/");
export async function getAllTld(connection: Connection): Promise<
Array<{
tldHouse: PublicKey;
tld: String;
parentAccount: PublicKey;
}>
> {
const tldHouseDiscriminator = [247, 144, 135, 1, 238, 173, 19, 249];
const filters: any = [
{
memcmp: {
offset: 0,
bytes: tldHouseDiscriminator,
},
},
];
const accounts = await connection.getProgramAccounts(TLD_HOUSE_PROGRAM_ID, {
filters: filters,
});
const tldsAndParentAccounts: {
tldHouse: PublicKey;
tld: String;
parentAccount: PublicKey;
}[] = [];
accounts.map(({ pubkey, account }) => {
const parentAccount = getParentAccountFromTldHouseAccountInfo(account);
const tld = getTldFromTldHouseAccountInfo(account);
tldsAndParentAccounts.push({ tldHouse: pubkey, tld, parentAccount });
});
return tldsAndParentAccounts;
}
async function findAllDomainsForTld(
connection: Connection,
parentAccount: PublicKey,
): Promise<{ pubkey: PublicKey; nameRecordHeader: NameRecordHeader }[]> {
const filters: any = [
{
memcmp: {
offset: 8,
bytes: parentAccount.toBase58(),
},
},
];
const accounts: GetProgramAccountsResponse =
await connection.getProgramAccounts(ANS_PROGRAM_ID, {
filters: filters,
});
return accounts.map((a) => {
return {
pubkey: a.pubkey,
nameRecordHeader: NameRecordHeader.fromAccountInfo(a.account),
};
});
}
export async function performReverseLookupBatchedTurbo(
connection: Connection,
nameAccounts: PublicKey[],
tldHouse: PublicKey,
): Promise<(string | undefined)[]> {
let reverseLookupDomains: (string | undefined)[] = [];
const getReverseLookUpAccounts = async (
accounts: PublicKey[],
): Promise<PublicKey[]> => {
const promises = accounts.map(async (nameAccount) => {
const reverseLookupHashedName = await getHashedName(
nameAccount.toBase58(),
);
const [reverseLookUpAccount] = await getNameAccountKeyWithBump(
reverseLookupHashedName,
tldHouse,
undefined,
);
return reverseLookUpAccount;
});
return Promise.all(promises);
};
const batches = [];
for (let i = 0; i < nameAccounts.length; i += 100) {
batches.push(nameAccounts.slice(i, i + 100));
}
const limit = pLimit(20);
const batchResults = await Promise.all(
batches.map((batch) =>
limit(async () => {
const reverseLookUpAccounts = await getReverseLookUpAccounts(batch);
const reverseLookupAccountInfos =
await connection.getMultipleAccountsInfo(reverseLookUpAccounts);
const batchDomains = reverseLookupAccountInfos.map(
(reverseLookupAccountInfo) => {
const domain = reverseLookupAccountInfo?.data
.subarray(200, reverseLookupAccountInfo?.data.length)
.toString();
return domain;
},
);
return batchDomains;
}),
),
);
reverseLookupDomains = batchResults.flat();
return reverseLookupDomains;
}
type DomainType = {
nameAccount: string;
domain: string;
owner?: string;
expiresAt: Date;
createdAt: Date;
};
// onlyDomains will grab only domains and no nfts
async function getAllRegisteredDomainsV2(
// specify TLD
tldExpected?: string,
// ignores NFTs
onlyDomains: boolean = false,
) {
// get all TLDs
const allTlds = await getAllTld(connection);
const domains: DomainType[] = [];
const nftDomains: DomainType[] = [];
for (const tld of allTlds) {
if (tldExpected && tld.tld !== tldExpected) continue;
// get all name accounts in a specific TLD
const allNameAccountsForTld = await findAllDomainsForTld(
connection,
tld.parentAccount,
);
// await setTimeout(50);
const [nameHouseAccount] = findNameHouse(tld.tldHouse);
const nameAccountPubkeys = allNameAccountsForTld.map((a) => a.pubkey);
const domainsReverse = await performReverseLookupBatchedTurbo(
connection,
nameAccountPubkeys,
tld.tldHouse,
);
for (let index = 0; index < domainsReverse.length; index++) {
const domain = domainsReverse[index];
if (!domain) continue;
const [nftRecord] = findNftRecord(
allNameAccountsForTld[index].pubkey,
nameHouseAccount,
);
const finalOwner =
allNameAccountsForTld[index].nameRecordHeader.owner?.toString();
if (finalOwner === nftRecord.toString()) {
nftDomains.push({
nameAccount: allNameAccountsForTld[index].pubkey.toString(),
domain: `${domain}${tld.tld}`,
owner: finalOwner,
expiresAt: allNameAccountsForTld[index].nameRecordHeader.expiresAt,
createdAt: allNameAccountsForTld[index].nameRecordHeader.createdAt,
});
} else if (!onlyDomains) {
domains.push({
nameAccount: allNameAccountsForTld[index].pubkey.toString(),
domain: `${domain}${tld.tld}`,
owner: finalOwner,
expiresAt: allNameAccountsForTld[index].nameRecordHeader.expiresAt,
createdAt: allNameAccountsForTld[index].nameRecordHeader.createdAt,
});
}
}
}
await processNftDomains(nftDomains);
return [...domains, ...nftDomains];
}
async function processNftDomains(nftDomains: DomainType[]) {
const limit = pLimit(20);
const batches = [];
for (let i = 0; i < nftDomains.length; i += 100) {
batches.push(nftDomains.slice(i, i + 100));
}
const batchResults = await Promise.all(
batches.map((batch) =>
limit(async () => {
const nftRecordBatch = batch.map(
(batchData) => new PublicKey(batchData.owner!),
);
const nftRecordBatchAccountsInfo =
await connection.getMultipleAccountsInfo(nftRecordBatch);
const batchDomains = nftRecordBatchAccountsInfo.map(
(nftRecordBatchAccountInfo) => {
if (!nftRecordBatchAccountInfo) return undefined;
const nftRecordData = NftRecord.fromAccountInfo(
nftRecordBatchAccountInfo,
)[0];
return nftRecordData;
},
);
return batchDomains;
}),
),
);
const nftRecordsData = batchResults.flat();
const domains = await Promise.all(
nftDomains.map((domain, index) =>
limit(async () => {
let domainOwner = domain.owner;
let nftRecordData = nftRecordsData[index];
if (!domainOwner || !nftRecordData) {
return domain;
}
const largestAccounts = await connection.getTokenLargestAccounts(
nftRecordData.nftMintAccount,
);
if (largestAccounts.value.length > 0) {
const largestAccountInfo = await connection.getParsedAccountInfo(
largestAccounts.value[0].address,
);
if (largestAccountInfo?.value?.data) {
domain.owner = new PublicKey(
// @ts-ignore
largestAccountInfo.value.data.parsed.info.owner,
).toString();
}
}
// await setTimeout(50);
// console.log(domainOwner)
return domain;
}),
),
);
return domains;
}
async function main() {
const domains = await getAllRegisteredDomainsV2(".turbo");
console.log(JSON.stringify(domains));
console.log(domains.length);
}
// get all domains registered on AllDomains
main();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment