Skip to content

Commit

Permalink
feat: allow subs as
Browse files Browse the repository at this point in the history
  • Loading branch information
dr497 committed Jul 18, 2024
1 parent 90511ac commit a5cf633
Show file tree
Hide file tree
Showing 6 changed files with 100 additions and 26 deletions.
24 changes: 21 additions & 3 deletions js/src/bindings/registerFavorite.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { PublicKey, SystemProgram } from "@solana/web3.js";
import { Connection, PublicKey, SystemProgram } from "@solana/web3.js";
import { registerFavoriteInstruction } from "../instructions/registerFavoriteInstruction";
import { FavouriteDomain, NAME_OFFERS_ID } from "../favorite-domain";
import { NameRegistryState } from "../state";
import { ROOT_DOMAIN_ACCOUNT } from "../constants";

/**
* This function can be used to register a domain name as favorite
Expand All @@ -9,14 +11,30 @@ import { FavouriteDomain, NAME_OFFERS_ID } from "../favorite-domain";
* @param programId The name offer program ID
* @returns
*/
export const registerFavorite = (nameAccount: PublicKey, owner: PublicKey) => {
const [favKey] = FavouriteDomain.getKeySync(NAME_OFFERS_ID, owner);
export const registerFavorite = async (
connection: Connection,
nameAccount: PublicKey,
owner: PublicKey,
) => {
let parent: PublicKey | undefined = undefined;
const { registry } = await NameRegistryState.retrieve(
connection,
nameAccount,
);
if (!registry.parentName.equals(ROOT_DOMAIN_ACCOUNT)) {
parent = registry.parentName;
}

const [favKey] = await FavouriteDomain.getKey(NAME_OFFERS_ID, owner);
const ix = new registerFavoriteInstruction().getInstruction(
NAME_OFFERS_ID,
nameAccount,
favKey,
owner,
SystemProgram.programId,
parent,
);
return ix;
};

export { registerFavorite as setPrimaryDomain };
54 changes: 46 additions & 8 deletions js/src/favorite-domain.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { reverseLookup } from "./utils/reverseLookup";
import { FavouriteDomainNotFoundError } from "./error";
import { getDomainMint } from "./nft/getDomainMint";
import { NameRegistryState } from "./state";
import { NAME_PROGRAM_ID, ROOT_DOMAIN_ACCOUNT } from "./constants";

export const NAME_OFFERS_ID = new PublicKey(
"85iDfUvr3HJyLM2zcq5BXSiDvUWfw6cSE1FfNBo8Ap29",
Expand Down Expand Up @@ -83,6 +84,8 @@ export class FavouriteDomain {
}
}

export { FavouriteDomain as PrimaryDomain };

/**
* This function can be used to retrieve the favorite domain of a user
* @param connection The Solana RPC connection object
Expand All @@ -97,23 +100,35 @@ export const getFavoriteDomain = async (
NAME_OFFERS_ID,
new PublicKey(owner),
);

const favorite = await FavouriteDomain.retrieve(connection, favKey);

const reverse = await reverseLookup(connection, favorite.nameAccount);
const { registry, nftOwner } = await NameRegistryState.retrieve(
connection,
favorite.nameAccount,
);
const domainOwner = nftOwner || registry.owner;

let reverse = await reverseLookup(
connection,
favorite.nameAccount,
registry.parentName.equals(ROOT_DOMAIN_ACCOUNT)
? undefined
: registry.parentName,
);

if (!registry.parentName.equals(ROOT_DOMAIN_ACCOUNT)) {
const parentReverse = await reverseLookup(connection, registry.parentName);
reverse += `.${parentReverse}`;
}

return {
domain: favorite.nameAccount,
reverse,
stale: !owner.equals(domainOwner),
};
};

export { getFavoriteDomain as getPrimaryDomain };

/**
* This function can be used to retrieve the favorite domains for multiple wallets, up to a maximum of 100.
* If a wallet does not have a favorite domain, the result will be 'undefined' instead of the human readable domain as a string.
Expand All @@ -139,33 +154,54 @@ export const getMultipleFavoriteDomains = async (
return PublicKey.default;
},
);
const revKeys = favDomains.map((e) => getReverseKeyFromDomainKey(e));

const domainInfos = await connection.getMultipleAccountsInfo(favDomains);
const parentRevKeys: PublicKey[] = [];
const revKeys = domainInfos.map((e, idx) => {
const parent = new PublicKey(e?.data.slice(0, 32) ?? Buffer.alloc(32));
const isSub =
e?.owner.equals(NAME_PROGRAM_ID) && !parent.equals(ROOT_DOMAIN_ACCOUNT);
parentRevKeys.push(
isSub ? getReverseKeyFromDomainKey(parent) : PublicKey.default,
);
return getReverseKeyFromDomainKey(
favDomains[idx],
isSub ? parent : undefined,
);
});
const atas = favDomains.map((e, idx) => {
const mint = getDomainMint(e);
const ata = getAssociatedTokenAddressSync(mint, wallets[idx], true);
return ata;
});

const [domainInfos, revs, tokenAccs] = await Promise.all([
connection.getMultipleAccountsInfo(favDomains),
const [revs, tokenAccs, parentRevs] = await Promise.all([
connection.getMultipleAccountsInfo(revKeys),
connection.getMultipleAccountsInfo(atas),
connection.getMultipleAccountsInfo(parentRevKeys),
]);

for (let i = 0; i < wallets.length; i++) {
let parentRev = "";
const domainInfo = domainInfos[i];
const rev = revs[i];
const parentRevAccount = parentRevs[i];
const tokenAcc = tokenAccs[i];

if (!domainInfo || !rev) {
result.push(undefined);
continue;
}

if (parentRevAccount && parentRevAccount.owner.equals(NAME_PROGRAM_ID)) {
const des = deserializeReverse(parentRevAccount.data.slice(96));
parentRev += `.${des}`;
}

const nativeOwner = new PublicKey(domainInfo?.data.slice(32, 64));

if (nativeOwner.equals(wallets[i])) {
result.push(deserializeReverse(rev?.data.slice(96)));
result.push(deserializeReverse(rev?.data.slice(96)) + parentRev);
continue;
}
// Either tokenized or stale
Expand All @@ -177,7 +213,7 @@ export const getMultipleFavoriteDomains = async (
const decoded = AccountLayout.decode(tokenAcc.data);
// Tokenized
if (Number(decoded.amount) === 1) {
result.push(deserializeReverse(rev?.data.slice(96)));
result.push(deserializeReverse(rev?.data.slice(96)) + parentRev);
continue;
}

Expand All @@ -187,3 +223,5 @@ export const getMultipleFavoriteDomains = async (

return result;
};

export { getMultipleFavoriteDomains as getMultiplePrimaryDomains };
8 changes: 8 additions & 0 deletions js/src/instructions/registerFavoriteInstruction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ export class registerFavoriteInstruction {
favouriteAccount: PublicKey,
owner: PublicKey,
systemProgram: PublicKey,
optParent?: PublicKey,
): TransactionInstruction {
const data = Buffer.from(this.serialize());
let keys: AccountKey[] = [];
Expand All @@ -45,6 +46,13 @@ export class registerFavoriteInstruction {
isSigner: false,
isWritable: false,
});
if (!!optParent) {
keys.push({
pubkey: optParent,
isSigner: false,
isWritable: false,
});
}
return new TransactionInstruction({
keys,
programId,
Expand Down
5 changes: 4 additions & 1 deletion js/src/utils/deserializeReverse.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,8 @@ export function deserializeReverse(
): string | undefined {
if (!data) return undefined;
const nameLength = data.slice(0, 4).readUInt32LE(0);
return data.slice(4, 4 + nameLength).toString();
return data
.slice(4, 4 + nameLength)
.toString()
.replace(/\0/g, "");
}
17 changes: 4 additions & 13 deletions js/src/utils/reverseLookup.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,8 @@
import { Connection, PublicKey } from "@solana/web3.js";
import { NameRegistryState } from "../state";
import { REVERSE_LOOKUP_CLASS } from "../constants";
import { NoAccountDataError } from "../error";

import { getHashedNameSync } from "./getHashedNameSync";
import { getNameAccountKeySync } from "./getNameAccountKeySync";
import { deserializeReverse } from "./deserializeReverse";
import { getReverseKeyFromDomainKey } from "./getReverseKeyFromDomainKey";

/**
* This function can be used to perform a reverse look up
Expand All @@ -16,17 +13,11 @@ import { deserializeReverse } from "./deserializeReverse";
export async function reverseLookup(
connection: Connection,
nameAccount: PublicKey,
parent?: PublicKey,
): Promise<string> {
const hashedReverseLookup = getHashedNameSync(nameAccount.toBase58());
const reverseLookupAccount = getNameAccountKeySync(
hashedReverseLookup,
REVERSE_LOOKUP_CLASS,
);
const reverseKey = getReverseKeyFromDomainKey(nameAccount, parent);

const { registry } = await NameRegistryState.retrieve(
connection,
reverseLookupAccount,
);
const { registry } = await NameRegistryState.retrieve(connection, reverseKey);
if (!registry.data) {
throw new NoAccountDataError("The registry data is empty");
}
Expand Down
18 changes: 17 additions & 1 deletion js/tests/favorite.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,14 @@ test("Favorite domain", async () => {
stale: false,
},
},
{
user: new PublicKey("A41TAGFpQkFpJidLwH37ydunE7Q3jpBaS228RkoXiRQk"),
favorite: {
domain: new PublicKey("BaQq8Uib3Aw5SPBedC8MdYCvpfEC9iLkUMHc5M74sAjv"),
reverse: "1.00728",
stale: false,
},
},
];
for (let item of items) {
const fav = await getFavoriteDomain(connection, item.user);
Expand Down Expand Up @@ -59,6 +67,10 @@ test("Multiple favorite domains", async () => {
wallet: new PublicKey("36Dn3RWhB8x4c83W6ebQ2C2eH9sh5bQX2nMdkP2cWaA4"),
domain: "fav-tokenized",
},
{
wallet: new PublicKey("A41TAGFpQkFpJidLwH37ydunE7Q3jpBaS228RkoXiRQk"),
domain: "1.00728",
},
];
const result = await getMultipleFavoriteDomains(
connection,
Expand All @@ -70,7 +82,11 @@ test("Multiple favorite domains", async () => {
test("Register fav", async () => {
const owner = new PublicKey("Fxuoy3gFjfJALhwkRcuKjRdechcgffUApeYAfMWck6w8");
const tx = new Transaction();
const ix = registerFavorite(getDomainKeySync("wallet-guide-3").pubkey, owner);
const ix = await registerFavorite(
connection,
getDomainKeySync("wallet-guide-3").pubkey,
owner,
);
tx.add(ix);
const { blockhash } = await connection.getLatestBlockhash();
tx.recentBlockhash = blockhash;
Expand Down

0 comments on commit a5cf633

Please sign in to comment.