UNPKG

@solana/spl-name-service

Version:

SPL Name Service JavaScript API

188 lines 10.3 kB
import { PublicKey, SystemProgram, } from '@solana/web3.js'; import { deserialize, serialize } from 'borsh'; import { deleteNameRegistry, NAME_PROGRAM_ID } from './bindings'; import { createInstruction, deleteInstruction, transferInstruction, updateInstruction, } from './instructions'; import { NameRegistryState } from './state'; import { getFilteredProgramAccounts, getHashedName, getNameAccountKey, Numberu32, Numberu64, } from './utils'; //////////////////////////////////////////////////// // Global Variables export const TWITTER_VERIFICATION_AUTHORITY = new PublicKey('FvPH7PrVrLGKPfqaf3xJodFTjZriqrAXXLTVWEorTFBi'); // The address of the name registry that will be a parent to all twitter handle registries, // it should be owned by the TWITTER_VERIFICATION_AUTHORITY and its name is irrelevant export const TWITTER_ROOT_PARENT_REGISTRY_KEY = new PublicKey('4YcexoW3r78zz16J2aqmukBLRwGq6rAvWzJpkYAXqebv'); //////////////////////////////////////////////////// // Bindings // Signed by the authority, the payer and the verified pubkey export async function createVerifiedTwitterRegistry(connection, twitterHandle, verifiedPubkey, space, // The space that the user will have to write data into the verified registry payerKey) { // Create user facing registry const hashedTwitterHandle = await getHashedName(twitterHandle); const twitterHandleRegistryKey = await getNameAccountKey(hashedTwitterHandle, undefined, TWITTER_ROOT_PARENT_REGISTRY_KEY); let instructions = [ createInstruction(NAME_PROGRAM_ID, SystemProgram.programId, twitterHandleRegistryKey, verifiedPubkey, payerKey, hashedTwitterHandle, new Numberu64(await connection.getMinimumBalanceForRentExemption(space)), new Numberu32(space), undefined, TWITTER_ROOT_PARENT_REGISTRY_KEY, TWITTER_VERIFICATION_AUTHORITY), ]; instructions = instructions.concat(await createReverseTwitterRegistry(connection, twitterHandle, twitterHandleRegistryKey, verifiedPubkey, payerKey)); return instructions; } // Overwrite the data that is written in the user facing registry // Signed by the verified pubkey export async function changeTwitterRegistryData(twitterHandle, verifiedPubkey, offset, // The offset at which to write the input data into the NameRegistryData input_data) { const hashedTwitterHandle = await getHashedName(twitterHandle); const twitterHandleRegistryKey = await getNameAccountKey(hashedTwitterHandle, undefined, TWITTER_ROOT_PARENT_REGISTRY_KEY); const instructions = [ updateInstruction(NAME_PROGRAM_ID, twitterHandleRegistryKey, new Numberu32(offset), input_data, verifiedPubkey, undefined), ]; return instructions; } // Change the verified pubkey for a given twitter handle // Signed by the Authority, the verified pubkey and the payer export async function changeVerifiedPubkey(connection, twitterHandle, currentVerifiedPubkey, newVerifiedPubkey, payerKey) { const hashedTwitterHandle = await getHashedName(twitterHandle); const twitterHandleRegistryKey = await getNameAccountKey(hashedTwitterHandle, undefined, TWITTER_ROOT_PARENT_REGISTRY_KEY); // Transfer the user-facing registry ownership let instructions = [ transferInstruction(NAME_PROGRAM_ID, twitterHandleRegistryKey, newVerifiedPubkey, currentVerifiedPubkey, undefined), ]; // Delete the current reverse registry instructions.push(await deleteNameRegistry(connection, currentVerifiedPubkey.toString(), payerKey, TWITTER_VERIFICATION_AUTHORITY, TWITTER_ROOT_PARENT_REGISTRY_KEY)); // Create the new reverse registry instructions = instructions.concat(await createReverseTwitterRegistry(connection, twitterHandle, twitterHandleRegistryKey, newVerifiedPubkey, payerKey)); return instructions; } // Delete the verified registry for a given twitter handle // Signed by the verified pubkey export async function deleteTwitterRegistry(twitterHandle, verifiedPubkey) { const hashedTwitterHandle = await getHashedName(twitterHandle); const twitterHandleRegistryKey = await getNameAccountKey(hashedTwitterHandle, undefined, TWITTER_ROOT_PARENT_REGISTRY_KEY); const hashedVerifiedPubkey = await getHashedName(verifiedPubkey.toString()); const reverseRegistryKey = await getNameAccountKey(hashedVerifiedPubkey, TWITTER_VERIFICATION_AUTHORITY, TWITTER_ROOT_PARENT_REGISTRY_KEY); const instructions = [ // Delete the user facing registry deleteInstruction(NAME_PROGRAM_ID, twitterHandleRegistryKey, verifiedPubkey, verifiedPubkey), // Delete the reverse registry deleteInstruction(NAME_PROGRAM_ID, reverseRegistryKey, verifiedPubkey, verifiedPubkey), ]; return instructions; } ////////////////////////////////////////// // Getter Functions // Returns the key of the user-facing registry export async function getTwitterRegistryKey(twitter_handle) { const hashedTwitterHandle = await getHashedName(twitter_handle); return await getNameAccountKey(hashedTwitterHandle, undefined, TWITTER_ROOT_PARENT_REGISTRY_KEY); } export async function getTwitterRegistry(connection, twitter_handle) { const hashedTwitterHandle = await getHashedName(twitter_handle); const twitterHandleRegistryKey = await getNameAccountKey(hashedTwitterHandle, undefined, TWITTER_ROOT_PARENT_REGISTRY_KEY); const registry = NameRegistryState.retrieve(connection, twitterHandleRegistryKey); return registry; } export async function getHandleAndRegistryKey(connection, verifiedPubkey) { const hashedVerifiedPubkey = await getHashedName(verifiedPubkey.toString()); const reverseRegistryKey = await getNameAccountKey(hashedVerifiedPubkey, TWITTER_VERIFICATION_AUTHORITY, TWITTER_ROOT_PARENT_REGISTRY_KEY); const reverseRegistryState = await ReverseTwitterRegistryState.retrieve(connection, reverseRegistryKey); return [ reverseRegistryState.twitterHandle, new PublicKey(reverseRegistryState.twitterRegistryKey), ]; } // Uses the RPC node filtering feature, execution speed may vary export async function getTwitterHandleandRegistryKeyViaFilters(connection, verifiedPubkey) { const filters = [ { memcmp: { offset: 0, bytes: TWITTER_ROOT_PARENT_REGISTRY_KEY.toBase58(), }, }, { memcmp: { offset: 32, bytes: verifiedPubkey.toBase58(), }, }, { memcmp: { offset: 64, bytes: TWITTER_VERIFICATION_AUTHORITY.toBase58(), }, }, ]; const filteredAccounts = await getFilteredProgramAccounts(connection, NAME_PROGRAM_ID, filters); for (const f of filteredAccounts) { if (f.accountInfo.data.length > NameRegistryState.HEADER_LEN + 32) { const data = f.accountInfo.data.slice(NameRegistryState.HEADER_LEN); const state = deserialize(ReverseTwitterRegistryState.schema, data); return [state.twitterHandle, new PublicKey(state.twitterRegistryKey)]; } } throw new Error('Registry not found.'); } // Uses the RPC node filtering feature, execution speed may vary // Does not give you the handle, but is an alternative to getHandlesAndKeysFromVerifiedPubkey + getTwitterRegistry to get the data export async function getTwitterRegistryData(connection, verifiedPubkey) { const filters = [ { memcmp: { offset: 0, bytes: TWITTER_ROOT_PARENT_REGISTRY_KEY.toBase58(), }, }, { memcmp: { offset: 32, bytes: verifiedPubkey.toBase58(), }, }, { memcmp: { offset: 64, bytes: new PublicKey(Buffer.alloc(32, 0)).toBase58(), }, }, ]; const filteredAccounts = await getFilteredProgramAccounts(connection, NAME_PROGRAM_ID, filters); if (filteredAccounts.length > 1) { throw new Error('Found more than one registry.'); } return filteredAccounts[0].accountInfo.data.slice(NameRegistryState.HEADER_LEN); } ////////////////////////////////////////////// // Utils export class ReverseTwitterRegistryState { constructor(obj) { this.twitterRegistryKey = obj.twitterRegistryKey; this.twitterHandle = obj.twitterHandle; } static async retrieve(connection, reverseTwitterAccountKey) { const reverseTwitterAccount = await connection.getAccountInfo(reverseTwitterAccountKey, 'processed'); if (!reverseTwitterAccount) { throw new Error('Invalid reverse Twitter account provided'); } const res = deserialize(this.schema, reverseTwitterAccount.data.slice(NameRegistryState.HEADER_LEN)); return res; } } ReverseTwitterRegistryState.schema = { struct: { twitterRegistryKey: { array: { type: 'u8', len: 32 } }, twitterHandle: 'string', }, }; export async function createReverseTwitterRegistry(connection, twitterHandle, twitterRegistryKey, verifiedPubkey, payerKey) { // Create the reverse lookup registry const hashedVerifiedPubkey = await getHashedName(verifiedPubkey.toString()); const reverseRegistryKey = await getNameAccountKey(hashedVerifiedPubkey, TWITTER_VERIFICATION_AUTHORITY, TWITTER_ROOT_PARENT_REGISTRY_KEY); const reverseTwitterRegistryStateBuff = serialize(ReverseTwitterRegistryState.schema, new ReverseTwitterRegistryState({ twitterRegistryKey: twitterRegistryKey.toBytes(), twitterHandle, })); return [ createInstruction(NAME_PROGRAM_ID, SystemProgram.programId, reverseRegistryKey, verifiedPubkey, payerKey, hashedVerifiedPubkey, new Numberu64(await connection.getMinimumBalanceForRentExemption(reverseTwitterRegistryStateBuff.length)), new Numberu32(reverseTwitterRegistryStateBuff.length), TWITTER_VERIFICATION_AUTHORITY, // Twitter authority acts as class for all reverse-lookup registries TWITTER_ROOT_PARENT_REGISTRY_KEY, // Reverse registries are also children of the root TWITTER_VERIFICATION_AUTHORITY), updateInstruction(NAME_PROGRAM_ID, reverseRegistryKey, new Numberu32(0), Buffer.from(reverseTwitterRegistryStateBuff), TWITTER_VERIFICATION_AUTHORITY, undefined), ]; } //# sourceMappingURL=twitter.js.map