solana-program-library/name-service/js/src/twitter.ts

344 lines
9.1 KiB
TypeScript

import {
Connection,
PublicKey,
SystemProgram,
TransactionInstruction,
} from '@solana/web3.js';
import { NAME_PROGRAM_ID, VERIFICATION_AUTHORITY_OFFSET } from './bindings';
import {
createInstruction,
deleteInstruction,
transferInstruction,
updateInstruction,
} from './instructions';
import { NameRegistryState } from './state';
import {
getFilteredProgramAccounts,
getHashedName,
getNameAccountKey,
Numberu32,
Numberu64,
} from './utils';
export const TWITTER_VERIFICATION_AUTHORITY = new PublicKey(
'867BLob5b52i81SNaV9Awm5ejkZV6VGSv9SxLcwukDDJ'
);
// 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 it's name is irrelevant
export const TWITTER_ROOT_PARENT_REGISTRY_KEY = new PublicKey(
'AFrGkxNmVLBn3mKhvfJJABvm8RJkTtRhHDoaF97pQZaA'
);
// Signed by the authority and the payer
export async function createVerifiedTwitterRegistry(
connection: Connection,
twitterHandle: string,
verifiedPubkey: PublicKey,
space: number, // The space that the user will have to write data into the verified registry
payerKey: PublicKey
): Promise<TransactionInstruction[]> {
const hashedTwitterHandle = await getHashedName(twitterHandle);
const twitterHandleRegistryKey = await getNameAccountKey(
hashedTwitterHandle,
undefined,
TWITTER_ROOT_PARENT_REGISTRY_KEY
);
const hashedVerifiedPubkey = await getHashedName(
verifiedPubkey.toString().concat(twitterHandle)
);
const reverseRegistryKey = await getNameAccountKey(
hashedVerifiedPubkey,
TWITTER_VERIFICATION_AUTHORITY,
undefined
);
space += 96; // Accounting for the Registry State Header
const instructions = [
// Create user facing registry
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 // Twitter authority acts as owner of the parent for all user-facing registries
),
// Create reverse lookup registry
createInstruction(
NAME_PROGRAM_ID,
SystemProgram.programId,
reverseRegistryKey,
verifiedPubkey,
payerKey,
hashedVerifiedPubkey,
new Numberu64(
await connection.getMinimumBalanceForRentExemption(96 + 18)
),
new Numberu32(96 + 18), // maximum length of a twitter handle
TWITTER_VERIFICATION_AUTHORITY, // Twitter authority acts as class for all reverse-lookup registries
undefined,
undefined
),
// Write the twitter handle into the reverse lookup registry
updateInstruction(
NAME_PROGRAM_ID,
reverseRegistryKey,
new Numberu32(0),
Buffer.from(twitterHandle),
TWITTER_VERIFICATION_AUTHORITY
),
];
return instructions;
}
// Overwrite the data that is written in the user facing registry
// Signed by the verified pubkey
export async function changeTwitterRegistryData(
twitterHandle: string,
verifiedPubkey: PublicKey,
offset: number, // The offset at which to write the input data into the NameRegistryData
input_data: Buffer
): Promise<TransactionInstruction[]> {
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
),
];
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: Connection,
twitterHandle: string,
currentVerifiedPubkey: PublicKey,
newVerifiedPubkey: PublicKey,
payerKey: PublicKey
): Promise<TransactionInstruction[]> {
const hashedTwitterHandle = await getHashedName(twitterHandle);
const twitterHandleRegistryKey = await getNameAccountKey(
hashedTwitterHandle,
undefined,
TWITTER_ROOT_PARENT_REGISTRY_KEY
);
const currentHashedVerifiedPubkey = await getHashedName(
currentVerifiedPubkey.toString().concat(twitterHandle)
);
const currentReverseRegistryKey = await getNameAccountKey(
currentHashedVerifiedPubkey,
TWITTER_VERIFICATION_AUTHORITY,
undefined
);
const newHashedVerifiedPubkey = await getHashedName(
newVerifiedPubkey.toString().concat(twitterHandle)
);
const newReverseRegistryKey = await getNameAccountKey(
newHashedVerifiedPubkey,
TWITTER_VERIFICATION_AUTHORITY,
undefined
);
const instructions = [
// Transfer the user-facing registry ownership
transferInstruction(
NAME_PROGRAM_ID,
twitterHandleRegistryKey,
newVerifiedPubkey,
currentVerifiedPubkey,
undefined
),
// Delete the current reverse registry
deleteInstruction(
NAME_PROGRAM_ID,
currentReverseRegistryKey,
payerKey,
currentVerifiedPubkey
),
// Create the new reverse lookup registry
createInstruction(
NAME_PROGRAM_ID,
SystemProgram.programId,
newReverseRegistryKey,
TWITTER_VERIFICATION_AUTHORITY,
payerKey,
newHashedVerifiedPubkey,
new Numberu64(await connection.getMinimumBalanceForRentExemption(18)),
new Numberu32(18), // maximum length of a twitter handle
TWITTER_VERIFICATION_AUTHORITY, // Twitter authority acts as class for all reverse-lookup registries
undefined,
undefined
),
// Write the twitter handle into the new reverse lookup registry
updateInstruction(
NAME_PROGRAM_ID,
newReverseRegistryKey,
new Numberu32(0),
Buffer.from(twitterHandle),
TWITTER_VERIFICATION_AUTHORITY
),
];
return instructions;
}
// Delete the verified registry for a given twitter handle
// Signed by the verified pubkey
export async function deleteTwitterRegistry(
twitterHandle: string,
verifiedPubkey: PublicKey
): Promise<TransactionInstruction[]> {
const hashedTwitterHandle = await getHashedName(twitterHandle);
const twitterHandleRegistryKey = await getNameAccountKey(
hashedTwitterHandle,
undefined,
TWITTER_ROOT_PARENT_REGISTRY_KEY
);
const hashedVerifiedPubkey = await getHashedName(
verifiedPubkey.toString().concat(twitterHandle)
);
const reverseRegistryKey = await getNameAccountKey(
hashedVerifiedPubkey,
TWITTER_VERIFICATION_AUTHORITY,
undefined
);
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;
}
export async function getTwitterHandle(
connection: Connection,
verifiedPubkey: PublicKey
): Promise<string> {
const filters = [
{
memcmp: {
offset: 32,
bytes: verifiedPubkey.toBase58(),
},
},
{
memcmp: {
offset: VERIFICATION_AUTHORITY_OFFSET,
bytes: TWITTER_VERIFICATION_AUTHORITY.toBase58(),
},
},
];
const filteredAccounts = await getFilteredProgramAccounts(
connection,
NAME_PROGRAM_ID,
filters
);
for (const f of filteredAccounts) {
if (f.accountInfo.data.length == 114) {
return f.accountInfo.data.slice(96, 114).toString();
}
}
throw 'Could not find the twitter handle';
}
// Returns the key of the user-facing registry
export async function getTwitterRegistryKey(
twitter_handle: string
): Promise<PublicKey> {
const hashedTwitterHandle = await getHashedName(twitter_handle);
return await getNameAccountKey(
hashedTwitterHandle,
undefined,
TWITTER_ROOT_PARENT_REGISTRY_KEY
);
}
export async function getTwitterRegistry(
connection: Connection,
twitter_handle: string
): Promise<NameRegistryState> {
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 getTwitterRegistryData(
connection: Connection,
verifiedPubkey: PublicKey
): Promise<Buffer> {
// Does not give you the name, but is faster than getTwitterHandle + getTwitterRegistry to get the data
const filters = [
{
memcmp: {
offset: 0,
bytes: TWITTER_ROOT_PARENT_REGISTRY_KEY.toBytes(),
},
},
{
memcmp: {
offset: 32,
bytes: verifiedPubkey.toBytes(),
},
},
];
const filteredAccounts = await getFilteredProgramAccounts(
connection,
NAME_PROGRAM_ID,
filters
);
if (filteredAccounts.length > 1) {
throw 'Found more than one twitter handle';
}
return filteredAccounts[0].accountInfo.data;
}