import { BinaryReader , BorshError , Schema , serialize } from 'borsh' ; import { PublicKey } from '@solana/web3.js' ; import { bytesToBase58 } from '../crypto/utils' ; export abstract class Assignable { constructor ( properties ) { Object . keys ( properties ) . forEach ( ( key ) => { this [ key ] = properties [ key ] ; } ) ; } encode ( ) { return Buffer . from ( serialize ( SCHEMA , this ) ) ; } static decode < T extends Assignable > ( data ) { return deserializeExtraBytes ( SCHEMA , this , data ) ; } } export interface ChainStorage < T > { toChainData : ( ) => T ; } export abstract class Enum extends Assignable { enum : string ; constructor ( properties ) { super ( properties ) ; if ( Object . keys ( properties ) . length !== 1 ) { throw new Error ( 'Enum can only take single value' ) ; } this . enum = '' ; Object . keys ( properties ) . forEach ( key => { this . enum = key ; } ) ; } } export const SCHEMA = new Map ( ) ; const capitalizeFirstLetter = ( s ) : string => s . charAt ( 0 ) . toUpperCase ( ) + s . slice ( 1 ) ; function deserializeField ( schema , fieldName , fieldType , reader ) { try { if ( typeof fieldType === 'string' ) { return reader [ `read${ capitalizeFirstLetter ( fieldType ) }` ] ( ) ; } if ( fieldType instanceof Array ) { if ( typeof fieldType [ 0 ] === 'number' ) { return reader . readFixedArray ( fieldType [ 0 ] ) ; } return reader . readArray ( ( ) => deserializeField ( schema , fieldName , fieldType [ 0 ] , reader ) ) ; } return deserializeStruct ( schema , fieldType , reader ) ; } catch ( error ) { if ( error instanceof BorshError ) { error . addToFieldPath ( fieldName ) ; } throw error ; } } function deserializeStruct ( schema , classType , reader ) { const structSchema = schema . get ( classType ) ; if ( ! structSchema ) { throw new BorshError ( `Class ${ classType . name } is missing in schema` ) ; } if ( structSchema . kind === 'struct' ) { const result = { } ; for ( const [ fieldName , fieldType ] of schema . get ( classType ) . fields ) { result [ fieldName ] = deserializeField ( schema , fieldName , fieldType , reader ) ; } return new classType ( result ) ; } if ( structSchema . kind === 'enum' ) { const idx = reader . readU8 ( ) ; if ( idx >= structSchema . values . length ) { throw new BorshError ( `Enum index: ${ idx } is out of range` ) ; } const [ fieldName , fieldType ] = structSchema . values [ idx ] ; const fieldValue = deserializeField ( schema , fieldName , fieldType , reader ) ; return new classType ( { [ fieldName ] : fieldValue } ) ; } throw new BorshError ( `Unexpected schema kind: ${ structSchema . kind } for ${ classType . constructor . name }` ) ; } export function deserializeExtraBytes < T extends Assignable > ( schema , classType , buffer ) { const reader = new BinaryReader ( buffer ) ; return deserializeStruct ( schema , classType , reader ) ; } export class AssignablePublicKey extends Assignable { bytes : number [ ] ; toPublicKey ( ) { return new PublicKey ( this . bytes ) ; } toString ( ) { return bytesToBase58 ( Uint8Array . from ( this . bytes ) ) ; } static parse ( pubkey ) { return AssignablePublicKey . fromPublicKey ( new PublicKey ( pubkey ) ) ; } static fromPublicKey ( publicKey ) { return new AssignablePublicKey ( { bytes : Uint8Array . from ( publicKey . toBuffer ( ) ) , } ) ; } static empty ( ) { const bytes = new Array ( 32 ) ; bytes . fill ( 0 ) ; return new AssignablePublicKey ( { bytes } ) ; } } SCHEMA . set ( AssignablePublicKey , { kind : 'struct' , fields : [ [ 'bytes' , [ 32 ] ] ] , } ) ;