import { SystemProgram , SYSVAR_RENT_PUBKEY , TransactionInstruction , } from '@solana/web3.js' ; import { deserializeUnchecked , serialize , BinaryReader , BinaryWriter } from 'borsh' ; import { PublicKey } from '@solana/web3.js' ; import BN from 'bn.js' ; import base58 from 'bs58' ; export type StringPublicKey = string ; 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 ) { 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 ) { const writer = this as unknown as BinaryWriter ; writer . writeFixedArray ( base58 . decode ( value ) ) ; } ; } ; extendBorsh ( ) ; export const METADATA_PREFIX = 'metadata' ; export const EDITION = 'edition' ; export const RESERVATION = 'reservation' ; export const MAX_NAME_LENGTH = 32 ; export const MAX_SYMBOL_LENGTH = 10 ; export const MAX_URI_LENGTH = 200 ; export const MAX_CREATOR_LIMIT = 5 ; export const MAX_CREATOR_LEN = 32 + 1 + 1 ; export const MAX_METADATA_LEN = 1 + 32 + 32 + MAX_NAME_LENGTH + MAX_SYMBOL_LENGTH + MAX_URI_LENGTH + MAX_CREATOR_LIMIT * MAX_CREATOR_LEN + 2 + 1 + 1 + 198 ; export const MAX_EDITION_LEN = 1 + 32 + 8 + 200 ; export const EDITION_MARKER_BIT_SIZE = 248 ; export enum MetadataKey { Uninitialized = 0 , MetadataV1 = 4 , EditionV1 = 1 , MasterEditionV1 = 2 , MasterEditionV2 = 6 , EditionMarker = 7 } export enum MetadataCategory { Audio = 'audio' , Video = 'video' , Image = 'image' , VR = 'vr' } export type MetadataFile = { uri : string ; type : string ; } ; export type FileOrString = MetadataFile | String ; export type Attribute = { trait_type ? : string ; display_type ? : string ; value : string | number ; } ; export interface IMetadataExtension { name : string ; symbol : string ; creators : Creator [ ] | null ; description : string ; image : string ; animation_url ? : string ; attributes ? : Attribute [ ] ; external_url : string ; seller_fee_basis_points : number ; properties : { files ? : FileOrString [ ] ; category : MetadataCategory ; maxSupply ? : number ; creators ? : { address : string ; shares : number ; } [ ] ; } ; } export class MasterEditionV1 { key : MetadataKey ; supply : typeof BN ; maxSupply ? : typeof BN ; printingMint : StringPublicKey ; oneTimePrintingAuthorizationMint : StringPublicKey ; constructor ( args ) { 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 : typeof BN ; maxSupply ? : typeof BN ; constructor ( args ) { this . key = MetadataKey . MasterEditionV2 ; this . supply = args . supply ; this . maxSupply = args . maxSupply ; } } export class EditionMarker { key : MetadataKey ; ledger : number [ ] ; constructor ( args ) { this . key = MetadataKey . EditionMarker ; this . ledger = args . ledger ; } editionTaken ( edition ) { 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 ; return appliedMask != 0 ; } } export class Edition { key : MetadataKey ; parent : StringPublicKey ; edition : typeof BN ; constructor ( args ) { this . key = MetadataKey . EditionV1 ; this . parent = args . parent ; this . edition = args . edition ; } } export class Creator { address : StringPublicKey ; verified : boolean ; share : number ; constructor ( args ) { 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 ) { 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 ; masterEdition ? : StringPublicKey ; edition ? : StringPublicKey ; constructor ( args ) { 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 ; } } class CreateMetadataArgs { instruction : number = 0 ; data : Data ; isMutable : boolean ; constructor ( args ) { this . data = args . data ; this . isMutable = args . isMutable ; } } class UpdateMetadataArgs { instruction : number = 1 ; data : Data | null ; updateAuthority : StringPublicKey | null ; primarySaleHappened : boolean | null ; constructor ( args ) { this . data = args . data ? args . data : null ; this . updateAuthority = args . updateAuthority ? args . updateAuthority : null ; this . primarySaleHappened = args . primarySaleHappened ; } } class CreateMasterEditionArgs { instruction : number = 10 ; maxSupply : typeof BN | null ; constructor ( args ) { this . maxSupply = args . maxSupply ; } } class MintPrintingTokensArgs { instruction : number = 9 ; supply : typeof BN ; constructor ( args ) { this . supply = args . supply ; } } export const METADATA_SCHEMA = new Map < any , any > ( [ [ CreateMetadataArgs , { kind : 'struct' , fields : [ [ 'instruction' , 'u8' ] , [ 'data' , Data ] , [ 'isMutable' , 'u8' ] , ] , } , ] , [ 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' ] , [ 'isMutable' , 'u8' ] , ] , } , ] , [ EditionMarker , { kind : 'struct' , fields : [ [ 'key' , 'u8' ] , [ 'ledger' , [ 31 ] ] , ] , } , ] , ] ) ; const METADATA_REPLACE = new RegExp ( '\u0000' , 'g' ) ; export const decodeMetadata = ( 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 ; } ;