1

I managed to deserialize everything with this custom getMetadata() function, except for CollectionDetails and ProgrammableConfig. Does anybody know the solutions?

/**
 * This blob of a file is pulled together from different files from the metaplex
 * repository.
 * Metaplex does not have a NPM package at the current time to make this easier, so instead of
 * trying to reference their stuff, I copied all of the minimum necessary code into this file
 */
import { BinaryReader, BinaryWriter, deserializeUnchecked } from "borsh";
import { PublicKey, Connection } from "@solana/web3.js";
import base58 from "bs58";
// import { CollectionDetails, ProgrammableConfig } from "@metaplex-foundation/mpl-token-metadata";


export const METADATA_PROGRAM_ID =
  "metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s" as StringPublicKey;
export const METADATA_PREFIX = "metadata";


// Borsh extension for pubkey stuff
(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));
};



export type StringPublicKey = string;

export enum TokenStandard {
    NonFungible = 0,
    FungibleAsset = 1,
    Fungible = 2,
    NonFungibleEdition = 3,
    ProgrammableNonFungible = 4
}

export enum MetadataKey {
    Uninitialized = 0,
    EditionV1 = 1,
    MasterEditionV1 = 2,
    ReservationListV1 = 3,
    MetadataV1 = 4,
    ReservationListV2 = 5,
    MasterEditionV2 = 6,
    EditionMarker = 7,
    UseAuthorityRecord = 8,
    CollectionAuthorityRecord = 9,
    TokenOwnedEscrow = 10,
    TokenRecord = 11,
    MetadataDelegate = 12
}

export enum UseMethod {
    Burn = 0,
    Multiple = 1,
    Single = 2
}


class Collection {
    verified: boolean;
    key: StringPublicKey;
    constructor(args: {
        verified: boolean;
        key: StringPublicKey;
      }) {
        this.verified = args.verified;
        this.key = args.key;
      }
}

class Uses {
    useMethod: UseMethod;
    remaining: number;
    total: number;    
    constructor(args: {
        useMethod: UseMethod;
        remaining: number;
        total: number;
      }) {
        this.useMethod = args.useMethod;
        this.remaining = args.remaining;
        this.total = args.total
      }
};


class ProgrammableConfig {
    V1: {
        ruleSet: StringPublicKey;
    };
    constructor(args: { V1: { 
        ruleSet: StringPublicKey;
    }
      }) {
        this.V1 = args.V1;
      }
};

class CollectionDetails {
    V1: {
        size: number;
    };
    constructor(args: { V1: { 
        size: number;
    }
      }) {
        this.V1 = args.V1;
      }
}

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;
  }
}

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;
  tokenStandard: TokenStandard;
  collection: Collection;
  uses: Uses;
  collectionDetails: CollectionDetails;
  programmableConfig: ProgrammableConfig;

  // set lazy
  masterEdition?: StringPublicKey;
  edition?: StringPublicKey;

  constructor(args: {
    updateAuthority: StringPublicKey;
    mint: StringPublicKey;
    data: Data;
    primarySaleHappened: boolean;
    isMutable: boolean;
    editionNonce: number | null;
    tokenStandard: TokenStandard;
    collection: Collection;
    uses: Uses;
    collectionDetails: CollectionDetails;
    programmableConfig: ProgrammableConfig;
  }) {
    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;  
    this.tokenStandard = args.tokenStandard;
    this.collection = args.collection;
    this.uses = args.uses;
    this.collectionDetails = args.collectionDetails;
    this.programmableConfig = args.programmableConfig;
  }
}

const METADATA_SCHEMA = new Map<any, any>([
    [
        Collection,
        {
            kind: "struct",
            fields: [
                ["verified", "u8"],
                ["key", "pubkeyAsString"]
            ]
        },
    ],
    [
        Uses,
        {
            kind: "struct",
            fields: [
                ["useMethod", "u8"],
                ["remaining", "u64"],
                ["total", "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"],
        ],
        },
    ],
    [ CollectionDetails, 
      {
        kind: "struct",
        fields: [
          [ "size", "u64"]
        ]
      }
    ],
    [ ProgrammableConfig, 
      {
        kind: "struct",
        fields: [
          [ "ruleSet", "pubkeyAsString"]
        ]
      }
    ],
    [
        Metadata,
        {
        kind: "struct",
        fields: [
            ["key", "u8"],
            ["updateAuthority", "pubkeyAsString"],
            ["mint", "pubkeyAsString"],
            ["data", Data],
            ["primarySaleHappened", "u8"], //bool
            ["isMutable", "u8"], //bool
            ["editionNonce", { kind : "option", type: "u8" }], 
            ["tokenStandard", { kind : "option", type: "u8" }],
            ["collection", { kind : "option", type: Collection }],
            ["uses", { kind : "option", type: Uses }],
            ["collectionDetails", { kind : "option", type: CollectionDetails }], 
            ["programmableConfig", { kind : "option", type: ProgrammableConfig }]
        ],
        },
    ],
]);


const METADATA_REPLACE = new RegExp("\u0000", "g");
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 async function getMetadata(connection: Connection, metadataAddresses: string[]
    ) : Promise<Metadata[]> {

    let metadataArray : Metadata[] = []

    for (let metadataAddress of metadataAddresses) {
        let metadataAccount = new PublicKey(metadataAddress)
        let accInfo = await connection.getAccountInfo(metadataAccount)
        console.log(new Uint8Array(accInfo?.data!))
        let metadata = decodeMetadata(accInfo!.data)
        metadataArray.push(metadata)

    }
 

    return metadataArray
}

 

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.