@solana/spl-name-service
Version:
SPL Name Service JavaScript API
188 lines • 10.3 kB
JavaScript
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