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

Various fixes 04 01 2024 #31

Merged
merged 6 commits into from
Jan 4, 2024
Merged
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
63 changes: 54 additions & 9 deletions package-lock.json

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

6 changes: 4 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,9 @@
"@bonfida/name-offers": "0.0.10",
"@bonfida/name-tokenizer": "0.0.12",
"@bonfida/sns-emitter": "0.1.7",
"@bonfida/sns-react": "2.0.3",
"@bonfida/sns-react": "2.1.0",
"@bonfida/sns-records": "0.0.1-alpha.8",
"@bonfida/spl-name-service": "2.0.4",
"@bonfida/spl-name-service": "2.2.0",
"@coral-xyz/common-public": "0.2.0-latest.3375",
"@expo-google-fonts/dev": "*",
"@expo/html-elements": "0.5.1",
Expand All @@ -54,6 +54,7 @@
"@solana/wallet-adapter-react-ui": "0.9.34",
"@solana/web3.js": "1.87.6",
"axios": "1.4.0",
"bn.js": "^5.2.1",
"buffer": "6.0.3",
"expo": "49.0.18",
"expo-blur": "~12.4.1",
Expand Down Expand Up @@ -88,6 +89,7 @@
"@babel/core": "7.21.3",
"@bonfida/prettier-config": "^1.0.0",
"@expo/webpack-config": "^19.0.0",
"@types/bn.js": "^5.1.5",
"@types/lodash": "4.14.201",
"@types/react": "~18.2.14",
"@types/react-native-dotenv": "0.2.0",
Expand Down
2 changes: 1 addition & 1 deletion src/components/ProgressExplainerModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ export const ProgressExplainerModal = ({
source={stepsImages[currentStep].image}
/>

<View style={tw`flex flex-row items-start gap-2 mt-2`}>
<View style={tw`flex flex-col items-center gap-2 mt-2`}>
{stepsImages[currentStep].icon}

<Text style={tw`text-sm font-medium text-center`}>
Expand Down
154 changes: 75 additions & 79 deletions src/hooks/useSubdomains.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,36 +3,20 @@ import {
NAME_PROGRAM_ID,
findSubdomains,
getDomainKeySync,
NameRegistryState,
resolve,
reverseLookup,
getNameAccountKeySync,
ROOT_DOMAIN_ACCOUNT,
getHashedNameSync,
REVERSE_LOOKUP_CLASS,
} from "@bonfida/spl-name-service";
import { useSolanaConnection } from "./xnft-hooks";
import { Connection, PublicKey } from "@solana/web3.js";
import { AccountInfo, PublicKey } from "@solana/web3.js";
import BN from "bn.js";

export interface SubdomainResult {
key: string;
subdomain: string;
}

async function findOwnedNameAccountsForUser(
connection: Connection,
userAccount: PublicKey,
): Promise<PublicKey[]> {
const filters = [
{
memcmp: {
offset: 32,
bytes: userAccount.toBase58(),
},
},
];
const accounts = await connection.getProgramAccounts(NAME_PROGRAM_ID, {
filters,
});
return accounts.map((a) => a.pubkey);
}

export const useSubdomains = (domain: string) => {
const connection = useSolanaConnection();

Expand All @@ -49,73 +33,85 @@ export const useSubdomains = (domain: string) => {
return useAsync(fn, [!!connection, domain]);
};

const deserializeReverse = (
e: AccountInfo<Buffer> | null,
): string | undefined => {
if (!e?.data) return undefined;
const nameLength = new BN(e.data.slice(96, 96 + 4), "le").toNumber();
return e.data
.slice(96 + 4, 96 + 4 + nameLength)
.toString()
.replace("\0", "");
};

export const useSubdomainsFromUser = (owner: string) => {
const connection = useSolanaConnection();

const fn = async () => {
if (!connection) return;
const accounts = await connection.getProgramAccounts(NAME_PROGRAM_ID, {
filters: [{ memcmp: { offset: 32, bytes: owner } }],
});

const ownedAccounts = await findOwnedNameAccountsForUser(
connection,
new PublicKey(owner),
);

const nameRegistriesState = (
await NameRegistryState.retrieveBatch(connection, ownedAccounts)
).filter(
(registryState): registryState is NameRegistryState =>
registryState !== undefined,
);

const uniqueRegistryStateParents = nameRegistriesState.filter(
(nameRegistryState, idx) =>
nameRegistriesState.findIndex((e) =>
e.parentName.equals(nameRegistryState.parentName),
) === idx,
// Very likely .sol subs but can be something else
const maybeSubs = accounts.filter(
(e) =>
!e.account.data.slice(0, 32).equals(ROOT_DOMAIN_ACCOUNT.toBuffer()),
);

const userOwnedSubdomainsPromises = uniqueRegistryStateParents.map(
async (registryState) => {
const domain = await reverseLookup(
connection,
registryState.parentName,
// Get the reverse accounts
const subsRev = (
await connection.getMultipleAccountsInfo(
maybeSubs.map((e) => {
const hashed = getHashedNameSync(e.pubkey.toBase58());
const key = getNameAccountKeySync(
hashed,
REVERSE_LOOKUP_CLASS,
new PublicKey(e.account.data.slice(0, 32)),
);
return key;
}),
)
).map(deserializeReverse);

const parentsWithSubsRevKey = subsRev
.map((e, idx) => {
if (e !== undefined) {
const parentKey = new PublicKey(
maybeSubs[idx].account.data.slice(0, 32),
);
const hashed = getHashedNameSync(parentKey.toBase58());
const key = getNameAccountKeySync(hashed, REVERSE_LOOKUP_CLASS);
return key;
}
return undefined;
})
.filter((e) => !!e) as PublicKey[];

const parentRev = (
await connection.getMultipleAccountsInfo(parentsWithSubsRevKey)
)
.map(deserializeReverse)
.filter((e) => !!e) as string[];

const result = subsRev
.map((e, idx) => {
if (!e) return;
const parentKey = new PublicKey(
maybeSubs[idx].account.data.slice(0, 32),
);
const subdomains = await findSubdomains(
connection,
registryState.parentName,
const parent = parentRev.find((e) =>
getDomainKeySync(e).pubkey.equals(parentKey),
);

const ownedSubdomains: SubdomainResult[] = [];
for (let sub of subdomains) {
const subdomain = sub + "." + domain;
const subdomainOwner = await resolve(connection, subdomain);
const key = getDomainKeySync(subdomain).pubkey;

if (subdomainOwner.equals(new PublicKey(owner))) {
ownedSubdomains.push({
key: key.toBase58(),
subdomain,
});
}
}

return ownedSubdomains;
},
);

const userSubdomainsResult: SubdomainResult[] = [];
const userOwnedSubdomains = await Promise.allSettled(
userOwnedSubdomainsPromises,
);
userOwnedSubdomains.map((e) => {
if (e.status === "fulfilled") {
userSubdomainsResult.push(...e.value);
}
});

userSubdomainsResult.sort((a, b) => a.subdomain.localeCompare(b.subdomain));

return userSubdomainsResult;
if (!parent) return undefined;
const subdomain = e + "." + parent;
return {
subdomain,
key: getDomainKeySync(subdomain).pubkey.toBase58(),
};
})
.filter((e) => !!e) as SubdomainResult[];
return result;
};

return useAsync(fn, [!!connection, owner]);
Expand Down
Loading
Loading