|
|
@@ -0,0 +1,577 @@
|
|
|
+import { PublicKey, AccountInfo } from "@solana/web3.js";
|
|
|
+import BN from "bn.js";
|
|
|
+import { deserializeUnchecked } from "borsh";
|
|
|
+import { BinaryReader, BinaryWriter } from "borsh";
|
|
|
+const base58: any = require("bs58");
|
|
|
+
|
|
|
+// eslint-disable-next-line
|
|
|
+export const METADATA_REPLACE = new RegExp("\u0000", "g");
|
|
|
+export const EDITION_MARKER_BIT_SIZE = 248;
|
|
|
+export const METADATA_PREFIX = "metadata";
|
|
|
+export const EDITION = "edition";
|
|
|
+
|
|
|
+export class LazyAccountInfoProxy<T> {
|
|
|
+ executable: boolean = false;
|
|
|
+ owner: StringPublicKey = "";
|
|
|
+ lamports: number = 0;
|
|
|
+
|
|
|
+ get data() {
|
|
|
+ return undefined as unknown as T;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+export interface LazyAccountInfo {
|
|
|
+ executable: boolean;
|
|
|
+ owner: StringPublicKey;
|
|
|
+ lamports: number;
|
|
|
+ data: [string, string];
|
|
|
+}
|
|
|
+
|
|
|
+const PubKeysInternedMap = new Map<string, PublicKey>();
|
|
|
+
|
|
|
+export const toPublicKey = (key: string | PublicKey) => {
|
|
|
+ if (typeof key !== "string") {
|
|
|
+ return key;
|
|
|
+ }
|
|
|
+
|
|
|
+ let result = PubKeysInternedMap.get(key);
|
|
|
+ if (!result) {
|
|
|
+ result = new PublicKey(key);
|
|
|
+ PubKeysInternedMap.set(key, result);
|
|
|
+ }
|
|
|
+
|
|
|
+ return result;
|
|
|
+};
|
|
|
+
|
|
|
+export interface PublicKeyStringAndAccount<T> {
|
|
|
+ pubkey: string;
|
|
|
+ account: AccountInfo<T>;
|
|
|
+}
|
|
|
+
|
|
|
+export const WRAPPED_SOL_MINT = new PublicKey(
|
|
|
+ "So11111111111111111111111111111111111111112"
|
|
|
+);
|
|
|
+
|
|
|
+export const TOKEN_PROGRAM_ID = new PublicKey(
|
|
|
+ "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"
|
|
|
+);
|
|
|
+
|
|
|
+export const SPL_ASSOCIATED_TOKEN_ACCOUNT_PROGRAM_ID = new PublicKey(
|
|
|
+ "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"
|
|
|
+);
|
|
|
+
|
|
|
+export const BPF_UPGRADE_LOADER_ID = new PublicKey(
|
|
|
+ "BPFLoaderUpgradeab1e11111111111111111111111"
|
|
|
+);
|
|
|
+
|
|
|
+export const MEMO_ID = new PublicKey(
|
|
|
+ "MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr"
|
|
|
+);
|
|
|
+
|
|
|
+export const METADATA_PROGRAM_ID =
|
|
|
+ "metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s" as StringPublicKey;
|
|
|
+
|
|
|
+export const VAULT_ID =
|
|
|
+ "vau1zxA2LbssAUEF7Gpw91zMM1LvXrvpzJtmZ58rPsn" as StringPublicKey;
|
|
|
+
|
|
|
+export const AUCTION_ID =
|
|
|
+ "auctxRXPeJoc4817jDhf4HbjnhEcr1cCXenosMhK5R8" as StringPublicKey;
|
|
|
+
|
|
|
+export const METAPLEX_ID =
|
|
|
+ "p1exdMJcjVao65QdewkaZRUnU6VPSXhus9n2GzWfh98" as StringPublicKey;
|
|
|
+
|
|
|
+export const SYSTEM = new PublicKey("11111111111111111111111111111111");
|
|
|
+
|
|
|
+export const getStoreID = async (storeOwnerAddress?: string) => {
|
|
|
+ if (!storeOwnerAddress) {
|
|
|
+ return undefined;
|
|
|
+ }
|
|
|
+
|
|
|
+ const programs = await findProgramAddress(
|
|
|
+ [
|
|
|
+ Buffer.from("metaplex"),
|
|
|
+ toPublicKey(METAPLEX_ID).toBuffer(),
|
|
|
+ toPublicKey(storeOwnerAddress).toBuffer(),
|
|
|
+ ],
|
|
|
+ toPublicKey(METAPLEX_ID)
|
|
|
+ );
|
|
|
+ const storeAddress = programs[0];
|
|
|
+
|
|
|
+ return storeAddress;
|
|
|
+};
|
|
|
+
|
|
|
+export const setProgramIds = async (store?: string) => {
|
|
|
+ STORE = store ? toPublicKey(store) : undefined;
|
|
|
+};
|
|
|
+
|
|
|
+let STORE: PublicKey | undefined;
|
|
|
+
|
|
|
+export const programIds = () => {
|
|
|
+ return {
|
|
|
+ token: TOKEN_PROGRAM_ID,
|
|
|
+ associatedToken: SPL_ASSOCIATED_TOKEN_ACCOUNT_PROGRAM_ID,
|
|
|
+ bpf_upgrade_loader: BPF_UPGRADE_LOADER_ID,
|
|
|
+ system: SYSTEM,
|
|
|
+ metadata: METADATA_PROGRAM_ID,
|
|
|
+ memo: MEMO_ID,
|
|
|
+ vault: VAULT_ID,
|
|
|
+ auction: AUCTION_ID,
|
|
|
+ metaplex: METAPLEX_ID,
|
|
|
+ store: STORE,
|
|
|
+ };
|
|
|
+};
|
|
|
+
|
|
|
+export const findProgramAddress = async (
|
|
|
+ seeds: (Buffer | Uint8Array)[],
|
|
|
+ programId: PublicKey
|
|
|
+) => {
|
|
|
+ const key =
|
|
|
+ "pda-" +
|
|
|
+ seeds.reduce((agg, item) => agg + item.toString("hex"), "") +
|
|
|
+ programId.toString();
|
|
|
+ let cached = localStorage.getItem(key);
|
|
|
+ if (cached) {
|
|
|
+ const value = JSON.parse(cached);
|
|
|
+
|
|
|
+ return [value.key, parseInt(value.nonce)] as [string, number];
|
|
|
+ }
|
|
|
+
|
|
|
+ const result = await PublicKey.findProgramAddress(seeds, programId);
|
|
|
+
|
|
|
+ try {
|
|
|
+ localStorage.setItem(
|
|
|
+ key,
|
|
|
+ JSON.stringify({
|
|
|
+ key: result[0].toBase58(),
|
|
|
+ nonce: result[1],
|
|
|
+ })
|
|
|
+ );
|
|
|
+ } catch {
|
|
|
+ // ignore
|
|
|
+ }
|
|
|
+
|
|
|
+ return [result[0].toBase58(), result[1]] as [string, number];
|
|
|
+};
|
|
|
+
|
|
|
+export type StringPublicKey = string;
|
|
|
+
|
|
|
+export enum MetadataKey {
|
|
|
+ Uninitialized = 0,
|
|
|
+ MetadataV1 = 4,
|
|
|
+ EditionV1 = 1,
|
|
|
+ MasterEditionV1 = 2,
|
|
|
+ MasterEditionV2 = 6,
|
|
|
+ EditionMarker = 7,
|
|
|
+}
|
|
|
+
|
|
|
+export async function getEdition(
|
|
|
+ tokenMint: StringPublicKey
|
|
|
+): Promise<StringPublicKey> {
|
|
|
+ const PROGRAM_IDS = programIds();
|
|
|
+
|
|
|
+ return (
|
|
|
+ await findProgramAddress(
|
|
|
+ [
|
|
|
+ Buffer.from(METADATA_PREFIX),
|
|
|
+ toPublicKey(PROGRAM_IDS.metadata).toBuffer(),
|
|
|
+ toPublicKey(tokenMint).toBuffer(),
|
|
|
+ Buffer.from(EDITION),
|
|
|
+ ],
|
|
|
+ toPublicKey(PROGRAM_IDS.metadata)
|
|
|
+ )
|
|
|
+ )[0];
|
|
|
+}
|
|
|
+
|
|
|
+class CreateMetadataArgs {
|
|
|
+ instruction: number = 0;
|
|
|
+ data: Data;
|
|
|
+ isMutable: boolean;
|
|
|
+
|
|
|
+ constructor(args: { data: Data; isMutable: boolean }) {
|
|
|
+ this.data = args.data;
|
|
|
+ this.isMutable = args.isMutable;
|
|
|
+ }
|
|
|
+}
|
|
|
+class UpdateMetadataArgs {
|
|
|
+ instruction: number = 1;
|
|
|
+ data: Data | null;
|
|
|
+ // Not used by this app, just required for instruction
|
|
|
+ updateAuthority: StringPublicKey | null;
|
|
|
+ primarySaleHappened: boolean | null;
|
|
|
+ constructor(args: {
|
|
|
+ data?: Data;
|
|
|
+ updateAuthority?: string;
|
|
|
+ primarySaleHappened: boolean | null;
|
|
|
+ }) {
|
|
|
+ this.data = args.data ? args.data : null;
|
|
|
+ this.updateAuthority = args.updateAuthority ? args.updateAuthority : null;
|
|
|
+ this.primarySaleHappened = args.primarySaleHappened;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+export class Creator {
|
|
|
+ address: StringPublicKey;
|
|
|
+ verified: boolean;
|
|
|
+ share: number;
|
|
|
+
|
|
|
+ constructor(args: {
|
|
|
+ address: StringPublicKey;
|
|
|
+ verified: boolean;
|
|
|
+ share: number;
|
|
|
+ }) {
|
|
|
+ this.address = args.address;
|
|
|
+ this.verified = args.verified;
|
|
|
+ this.share = args.share;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+export class Data {
|
|
|
+ name: string;
|
|
|
+ symbol: string;
|
|
|
+ uri: string;
|
|
|
+ sellerFeeBasisPoints: number;
|
|
|
+ creators: Creator[] | null;
|
|
|
+ constructor(args: {
|
|
|
+ name: string;
|
|
|
+ symbol: string;
|
|
|
+ uri: string;
|
|
|
+ sellerFeeBasisPoints: number;
|
|
|
+ creators: Creator[] | null;
|
|
|
+ }) {
|
|
|
+ this.name = args.name;
|
|
|
+ this.symbol = args.symbol;
|
|
|
+ this.uri = args.uri;
|
|
|
+ this.sellerFeeBasisPoints = args.sellerFeeBasisPoints;
|
|
|
+ this.creators = args.creators;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+export class Metadata {
|
|
|
+ key: MetadataKey;
|
|
|
+ updateAuthority: StringPublicKey;
|
|
|
+ mint: StringPublicKey;
|
|
|
+ data: Data;
|
|
|
+ primarySaleHappened: boolean;
|
|
|
+ isMutable: boolean;
|
|
|
+ editionNonce: number | null;
|
|
|
+
|
|
|
+ // set lazy
|
|
|
+ masterEdition?: StringPublicKey;
|
|
|
+ edition?: StringPublicKey;
|
|
|
+
|
|
|
+ constructor(args: {
|
|
|
+ updateAuthority: StringPublicKey;
|
|
|
+ mint: StringPublicKey;
|
|
|
+ data: Data;
|
|
|
+ primarySaleHappened: boolean;
|
|
|
+ isMutable: boolean;
|
|
|
+ editionNonce: number | null;
|
|
|
+ }) {
|
|
|
+ this.key = MetadataKey.MetadataV1;
|
|
|
+ this.updateAuthority = args.updateAuthority;
|
|
|
+ this.mint = args.mint;
|
|
|
+ this.data = args.data;
|
|
|
+ this.primarySaleHappened = args.primarySaleHappened;
|
|
|
+ this.isMutable = args.isMutable;
|
|
|
+ this.editionNonce = args.editionNonce;
|
|
|
+ }
|
|
|
+
|
|
|
+ public async init() {
|
|
|
+ const edition = await getEdition(this.mint);
|
|
|
+ this.edition = edition;
|
|
|
+ this.masterEdition = edition;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+export class Edition {
|
|
|
+ key: MetadataKey;
|
|
|
+ /// Points at MasterEdition struct
|
|
|
+ parent: StringPublicKey;
|
|
|
+ /// Starting at 0 for master record, this is incremented for each edition minted.
|
|
|
+ edition: BN;
|
|
|
+
|
|
|
+ constructor(args: {
|
|
|
+ key: MetadataKey;
|
|
|
+ parent: StringPublicKey;
|
|
|
+ edition: BN;
|
|
|
+ }) {
|
|
|
+ this.key = MetadataKey.EditionV1;
|
|
|
+ this.parent = args.parent;
|
|
|
+ this.edition = args.edition;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+export class MasterEditionV1 {
|
|
|
+ key: MetadataKey;
|
|
|
+ supply: BN;
|
|
|
+ maxSupply?: BN;
|
|
|
+ /// Can be used to mint tokens that give one-time permission to mint a single limited edition.
|
|
|
+ printingMint: StringPublicKey;
|
|
|
+ /// If you don't know how many printing tokens you are going to need, but you do know
|
|
|
+ /// you are going to need some amount in the future, you can use a token from this mint.
|
|
|
+ /// Coming back to token metadata with one of these tokens allows you to mint (one time)
|
|
|
+ /// any number of printing tokens you want. This is used for instance by Auction Manager
|
|
|
+ /// with participation NFTs, where we dont know how many people will bid and need participation
|
|
|
+ /// printing tokens to redeem, so we give it ONE of these tokens to use after the auction is over,
|
|
|
+ /// because when the auction begins we just dont know how many printing tokens we will need,
|
|
|
+ /// but at the end we will. At the end it then burns this token with token-metadata to
|
|
|
+ /// get the printing tokens it needs to give to bidders. Each bidder then redeems a printing token
|
|
|
+ /// to get their limited editions.
|
|
|
+ oneTimePrintingAuthorizationMint: StringPublicKey;
|
|
|
+
|
|
|
+ constructor(args: {
|
|
|
+ key: MetadataKey;
|
|
|
+ supply: BN;
|
|
|
+ maxSupply?: BN;
|
|
|
+ printingMint: StringPublicKey;
|
|
|
+ oneTimePrintingAuthorizationMint: StringPublicKey;
|
|
|
+ }) {
|
|
|
+ this.key = MetadataKey.MasterEditionV1;
|
|
|
+ this.supply = args.supply;
|
|
|
+ this.maxSupply = args.maxSupply;
|
|
|
+ this.printingMint = args.printingMint;
|
|
|
+ this.oneTimePrintingAuthorizationMint =
|
|
|
+ args.oneTimePrintingAuthorizationMint;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+export class MasterEditionV2 {
|
|
|
+ key: MetadataKey;
|
|
|
+ supply: BN;
|
|
|
+ maxSupply?: BN;
|
|
|
+
|
|
|
+ constructor(args: { key: MetadataKey; supply: BN; maxSupply?: BN }) {
|
|
|
+ this.key = MetadataKey.MasterEditionV2;
|
|
|
+ this.supply = args.supply;
|
|
|
+ this.maxSupply = args.maxSupply;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+class CreateMasterEditionArgs {
|
|
|
+ instruction: number = 10;
|
|
|
+ maxSupply: BN | null;
|
|
|
+ constructor(args: { maxSupply: BN | null }) {
|
|
|
+ this.maxSupply = args.maxSupply;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+class MintPrintingTokensArgs {
|
|
|
+ instruction: number = 9;
|
|
|
+ supply: BN;
|
|
|
+
|
|
|
+ constructor(args: { supply: BN }) {
|
|
|
+ this.supply = args.supply;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+export class EditionMarker {
|
|
|
+ key: MetadataKey;
|
|
|
+ ledger: number[];
|
|
|
+
|
|
|
+ constructor(args: { key: MetadataKey; ledger: number[] }) {
|
|
|
+ this.key = MetadataKey.EditionMarker;
|
|
|
+ this.ledger = args.ledger;
|
|
|
+ }
|
|
|
+
|
|
|
+ editionTaken(edition: number) {
|
|
|
+ const editionOffset = edition % EDITION_MARKER_BIT_SIZE;
|
|
|
+ const indexOffset = Math.floor(editionOffset / 8);
|
|
|
+
|
|
|
+ if (indexOffset > 30) {
|
|
|
+ throw Error("bad index for edition");
|
|
|
+ }
|
|
|
+
|
|
|
+ const positionInBitsetFromRight = 7 - (editionOffset % 8);
|
|
|
+
|
|
|
+ const mask = Math.pow(2, positionInBitsetFromRight);
|
|
|
+
|
|
|
+ const appliedMask = this.ledger[indexOffset] & mask;
|
|
|
+
|
|
|
+ // eslint-disable-next-line
|
|
|
+ return appliedMask != 0;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+export const METADATA_SCHEMA = new Map<any, any>([
|
|
|
+ [
|
|
|
+ CreateMetadataArgs,
|
|
|
+ {
|
|
|
+ kind: "struct",
|
|
|
+ fields: [
|
|
|
+ ["instruction", "u8"],
|
|
|
+ ["data", Data],
|
|
|
+ ["isMutable", "u8"], // bool
|
|
|
+ ],
|
|
|
+ },
|
|
|
+ ],
|
|
|
+ [
|
|
|
+ UpdateMetadataArgs,
|
|
|
+ {
|
|
|
+ kind: "struct",
|
|
|
+ fields: [
|
|
|
+ ["instruction", "u8"],
|
|
|
+ ["data", { kind: "option", type: Data }],
|
|
|
+ ["updateAuthority", { kind: "option", type: "pubkeyAsString" }],
|
|
|
+ ["primarySaleHappened", { kind: "option", type: "u8" }],
|
|
|
+ ],
|
|
|
+ },
|
|
|
+ ],
|
|
|
+
|
|
|
+ [
|
|
|
+ CreateMasterEditionArgs,
|
|
|
+ {
|
|
|
+ kind: "struct",
|
|
|
+ fields: [
|
|
|
+ ["instruction", "u8"],
|
|
|
+ ["maxSupply", { kind: "option", type: "u64" }],
|
|
|
+ ],
|
|
|
+ },
|
|
|
+ ],
|
|
|
+ [
|
|
|
+ MintPrintingTokensArgs,
|
|
|
+ {
|
|
|
+ kind: "struct",
|
|
|
+ fields: [
|
|
|
+ ["instruction", "u8"],
|
|
|
+ ["supply", "u64"],
|
|
|
+ ],
|
|
|
+ },
|
|
|
+ ],
|
|
|
+ [
|
|
|
+ MasterEditionV1,
|
|
|
+ {
|
|
|
+ kind: "struct",
|
|
|
+ fields: [
|
|
|
+ ["key", "u8"],
|
|
|
+ ["supply", "u64"],
|
|
|
+ ["maxSupply", { kind: "option", type: "u64" }],
|
|
|
+ ["printingMint", "pubkeyAsString"],
|
|
|
+ ["oneTimePrintingAuthorizationMint", "pubkeyAsString"],
|
|
|
+ ],
|
|
|
+ },
|
|
|
+ ],
|
|
|
+ [
|
|
|
+ MasterEditionV2,
|
|
|
+ {
|
|
|
+ kind: "struct",
|
|
|
+ fields: [
|
|
|
+ ["key", "u8"],
|
|
|
+ ["supply", "u64"],
|
|
|
+ ["maxSupply", { kind: "option", type: "u64" }],
|
|
|
+ ],
|
|
|
+ },
|
|
|
+ ],
|
|
|
+ [
|
|
|
+ Edition,
|
|
|
+ {
|
|
|
+ kind: "struct",
|
|
|
+ fields: [
|
|
|
+ ["key", "u8"],
|
|
|
+ ["parent", "pubkeyAsString"],
|
|
|
+ ["edition", "u64"],
|
|
|
+ ],
|
|
|
+ },
|
|
|
+ ],
|
|
|
+ [
|
|
|
+ Data,
|
|
|
+ {
|
|
|
+ kind: "struct",
|
|
|
+ fields: [
|
|
|
+ ["name", "string"],
|
|
|
+ ["symbol", "string"],
|
|
|
+ ["uri", "string"],
|
|
|
+ ["sellerFeeBasisPoints", "u16"],
|
|
|
+ ["creators", { kind: "option", type: [Creator] }],
|
|
|
+ ],
|
|
|
+ },
|
|
|
+ ],
|
|
|
+ [
|
|
|
+ Creator,
|
|
|
+ {
|
|
|
+ kind: "struct",
|
|
|
+ fields: [
|
|
|
+ ["address", "pubkeyAsString"],
|
|
|
+ ["verified", "u8"],
|
|
|
+ ["share", "u8"],
|
|
|
+ ],
|
|
|
+ },
|
|
|
+ ],
|
|
|
+ [
|
|
|
+ Metadata,
|
|
|
+ {
|
|
|
+ kind: "struct",
|
|
|
+ fields: [
|
|
|
+ ["key", "u8"],
|
|
|
+ ["updateAuthority", "pubkeyAsString"],
|
|
|
+ ["mint", "pubkeyAsString"],
|
|
|
+ ["data", Data],
|
|
|
+ ["primarySaleHappened", "u8"], // bool
|
|
|
+ ["isMutable", "u8"], // bool
|
|
|
+ ],
|
|
|
+ },
|
|
|
+ ],
|
|
|
+ [
|
|
|
+ EditionMarker,
|
|
|
+ {
|
|
|
+ kind: "struct",
|
|
|
+ fields: [
|
|
|
+ ["key", "u8"],
|
|
|
+ ["ledger", [31]],
|
|
|
+ ],
|
|
|
+ },
|
|
|
+ ],
|
|
|
+]);
|
|
|
+
|
|
|
+export const extendBorsh = () => {
|
|
|
+ (BinaryReader.prototype as any).readPubkey = function () {
|
|
|
+ const reader = this as unknown as BinaryReader;
|
|
|
+ const array = reader.readFixedArray(32);
|
|
|
+ return new PublicKey(array);
|
|
|
+ };
|
|
|
+
|
|
|
+ (BinaryWriter.prototype as any).writePubkey = function (value: PublicKey) {
|
|
|
+ const writer = this as unknown as BinaryWriter;
|
|
|
+ writer.writeFixedArray(value.toBuffer());
|
|
|
+ };
|
|
|
+
|
|
|
+ (BinaryReader.prototype as any).readPubkeyAsString = function () {
|
|
|
+ const reader = this as unknown as BinaryReader;
|
|
|
+ const array = reader.readFixedArray(32);
|
|
|
+ return base58.encode(array) as StringPublicKey;
|
|
|
+ };
|
|
|
+
|
|
|
+ (BinaryWriter.prototype as any).writePubkeyAsString = function (
|
|
|
+ value: StringPublicKey
|
|
|
+ ) {
|
|
|
+ const writer = this as unknown as BinaryWriter;
|
|
|
+ writer.writeFixedArray(base58.decode(value));
|
|
|
+ };
|
|
|
+};
|
|
|
+
|
|
|
+extendBorsh();
|
|
|
+
|
|
|
+export const decodeMetadata = (buffer: Buffer): Metadata => {
|
|
|
+ const metadata = deserializeUnchecked(
|
|
|
+ METADATA_SCHEMA,
|
|
|
+ Metadata,
|
|
|
+ buffer
|
|
|
+ ) as Metadata;
|
|
|
+ metadata.data.name = metadata.data.name.replace(METADATA_REPLACE, "");
|
|
|
+ metadata.data.uri = metadata.data.uri.replace(METADATA_REPLACE, "");
|
|
|
+ metadata.data.symbol = metadata.data.symbol.replace(METADATA_REPLACE, "");
|
|
|
+ return metadata;
|
|
|
+};
|
|
|
+
|
|
|
+export const getMetadataAddress = async (
|
|
|
+ mintKey: string
|
|
|
+): Promise<[PublicKey, number]> => {
|
|
|
+ const seeds = [
|
|
|
+ Buffer.from("metadata"),
|
|
|
+ new PublicKey(METADATA_PROGRAM_ID).toBuffer(),
|
|
|
+ new PublicKey(mintKey).toBuffer(),
|
|
|
+ ];
|
|
|
+ return PublicKey.findProgramAddress(
|
|
|
+ seeds,
|
|
|
+ new PublicKey(METADATA_PROGRAM_ID)
|
|
|
+ );
|
|
|
+};
|