UNPKG

@luffalab/luffa-endless-sdk

Version:

luffa endless ts sdk

397 lines (375 loc) 12.6 kB
import { PostMessage } from './message'; import type { IRequestData } from './message/types'; import type { EndLessSDKEventType, EndLessSDKEventPayload } from './message/types'; import { EndLessSDKEvent } from './message/types'; import { IInitData, UserResponse, AccountInfo, UserResponseStatus, EndlessSignMessageInput, EndlessSignMessageOutput, EndlessSignAndSubmitTransactionInput, EndlessWalletTransactionType, UserRejection, NetworkInfo, ChainData, } from './types'; // web and WebView /// #if BUILD_PLATFORM !== 'MINIPROGRAM' import { Endless, EndlessConfig } from '@endlesslab/endless-ts-sdk'; /// #endif import { isLuffa, isLuffaMiniProgram, isLuffaMiniProgramWebview, networkMap } from './utils'; export { isLuffa, isLuffaMiniProgram, isLuffaMiniProgramWebview } from './utils'; export interface Metadata { title: string; url: string; origin: string; icon: string; gameId: string; userId: string; walletAddress: string; } export { EndLessSDKEvent } from './message/types'; export { UserResponseStatus, EndlessSendTransactionType, EndlessWalletTransactionType } from './types'; export type { UserResponse, AccountInfo, EndlessSignAndSubmitTransactionInput } from './types'; export enum MethodName { CONNECT = 'connect', GETACCOUNT = 'getAccount', DISCONNECT = 'disconnect', NETWORK_CHANGE = 'luffa_switchChain', SIGN_MESSAGE = 'signMessage', SEND_TRANSACTION = 'sendTransaction', SIGN_AND_SUBMIT_TRANSACTION = 'signAndSubmitTransaction', SIGN_BUILD_TRANSACTION = 'signBuildTransaction', PACKAGETRANSACTIONV2 = 'packageTransactionV2', ACCOUNT_CHANGE = 'accountChange', } export class EndlessLuffaSdk { static readonly version: string = '1.1.9'; private static _instance: EndlessLuffaSdk; private message: PostMessage | null = null; private _metadata: Metadata = {} as Metadata; private _initData: IInitData = {} as IInitData; private _endless: any | null = null; private _endlessConfig: EndlessConfig | null = null; private accountAddress: string | null = null; static getIninData = (): IInitData => { if (EndlessLuffaSdk._instance) { return EndlessLuffaSdk._instance._initData; } else { return {} as IInitData; } }; static getAccountAddress = () => { if (EndlessLuffaSdk._instance) { return EndlessLuffaSdk._instance.accountAddress; } else { return null; } }; static setAccountAddress = (accountAddress: string | null) => { if (EndlessLuffaSdk._instance) { EndlessLuffaSdk._instance.accountAddress = accountAddress; } }; constructor(initData: IInitData) { if (EndlessLuffaSdk._instance) return EndlessLuffaSdk._instance; this.message = new PostMessage(); this.initWalletEvent(); this.initConfig(initData); EndlessLuffaSdk._instance = this; } private initConfig(initData: IInitData) { this._initData = { ...(initData || {}), callbackWalletName: 'endlessWallet' }; // web and WebView /// #if BUILD_PLATFORM !== 'MINIPROGRAM' this._endlessConfig = new EndlessConfig({ network: initData.network as unknown as any, miniprogram: initData.miniprogram, }); this._endless = new Endless(this._endlessConfig); this.getMetadata(); /// #endif } private initWalletEvent() { this.on(EndLessSDKEvent.NETWORK_CHANGE, (payload) => { this.initConfig({ network: payload.name, }); }); } async changeNetwork(data: IInitData) { return new Promise((resolve) => { this.message?.sendMessage( { uuid: new Date().getTime().toString(), methodName: MethodName.NETWORK_CHANGE, metadata: this._metadata, data: { targetNet: networkMap[data.network] ?? data.network, }, }, (res) => { if (res?.network) { this._initData.network = res.network; } const result = { ...res }; resolve(result); } ); }); } private getMetadata() { if (!window) { return; } const iconLink = document.querySelector('link[rel="icon"]') || document.querySelector('link[rel="shortcut icon"]'); let iconUrl = iconLink?.getAttribute('href') || ''; if (iconUrl && !iconUrl.startsWith('http')) { iconUrl = new URL(iconUrl, window.location.origin).href; } this.setMetaData({ title: window.document.title, url: window.location.href, origin: window.location.origin, icon: iconUrl, }) } setMetaData(data: Partial<Metadata>) { Object.assign(this._metadata, data); } request = (data: IRequestData, callback?: (data: unknown) => void) => { this.message?.sendMessage( { uuid: new Date().getTime().toString(), methodName: data.method, metadata: this._metadata, data: data.data, initData: data?.initData, }, callback ); }; getAccount = (): Promise<UserResponse<AccountInfo>> => { return new Promise((resolve) => { this.message?.sendMessage( { uuid: new Date().getTime().toString(), methodName: MethodName.GETACCOUNT, metadata: this._metadata, data: {}, }, (data) => { if (data?.account) { this.accountAddress = data.account; const res: UserResponse<AccountInfo> = { status: UserResponseStatus.APPROVED, args: { ...data }, }; resolve(res); } else { const res: UserResponse<AccountInfo> = { status: UserResponseStatus.REJECTED, message: data?.message || 'Wallet is not connected', }; resolve(res); } } ); }); }; connect = (callback?: (data: AccountInfo) => void): Promise<UserResponse<AccountInfo>> => { return new Promise((resolve) => { this.message?.sendMessage( { uuid: new Date().getTime().toString(), methodName: MethodName.CONNECT, metadata: this._metadata, data: {}, }, (data) => { if (data?.account) { this.accountAddress = data.account; const res: UserResponse<AccountInfo> = { status: UserResponseStatus.APPROVED, args: { ...data }, }; resolve(res); } else { const res: UserResponse<AccountInfo> = { status: UserResponseStatus.REJECTED, message: data?.message }; resolve(res); } if (callback) callback({ ...(data || {}) }); } ); }); }; disconnect = (callback?: (data: unknown) => void): Promise<void> => { return new Promise((resolve, reject) => { this.message?.sendMessage( { uuid: new Date().getTime().toString(), methodName: MethodName.DISCONNECT, metadata: this._metadata, data: {}, }, (data: unknown) => { this.accountAddress = null; if (callback) callback(data); resolve(); } ); }); }; signMessage = ( data: EndlessSignMessageInput, callback?: (data: unknown) => void ): Promise<UserResponse<EndlessSignMessageOutput>> => { return new Promise(async (resolve) => { if (!this.accountAddress) { await this.connect(); } this.message?.sendMessage( { uuid: new Date().getTime().toString(), methodName: MethodName.SIGN_MESSAGE, metadata: this._metadata, data: data, }, (res) => { console.log('signMessage res: ', res); if (res?.signature) { const result: UserResponse<EndlessSignMessageOutput> = { status: UserResponseStatus.APPROVED, args: res, }; resolve(result); } else { const result: UserRejection = { status: UserResponseStatus.REJECTED, message: res?.message }; resolve(result); } if (callback) callback(res); } ); }); }; on = <K extends EndLessSDKEventType>(methodName: K, callback: (payload: EndLessSDKEventPayload<K>) => void) => { if (this.message?.addListener) { this.message?.addListener(methodName, callback); } }; off = <K extends EndLessSDKEventType>(methodName: K, callback?: (payload: EndLessSDKEventPayload<K>) => void) => { if (this.message?.removeListener) { this.message?.removeListener(methodName, callback); } }; signAndSubmitTransaction = (data: EndlessSignAndSubmitTransactionInput): Promise<UserResponse<{ hash: string }>> => { return new Promise(async (resolve) => { if (!this.accountAddress) { await this.connect(); } if (!this.accountAddress) { const result: UserRejection = { status: UserResponseStatus.REJECTED, message: 'Wallet not linked' }; resolve(result); return; } let transaction; let methodName = MethodName.SIGN_AND_SUBMIT_TRANSACTION; // TODO MiniProgram build transaction /// #if BUILD_PLATFORM === 'MINIPROGRAM' methodName = MethodName.PACKAGETRANSACTIONV2; transaction = data; /// #endif /// #if BUILD_PLATFORM !== 'MINIPROGRAM' if (!this._endless) { const result: UserRejection = { status: UserResponseStatus.REJECTED, message: 'Wallet not linked' }; resolve(result); return; } transaction = await this._endless.transaction.build.simple({ sender: this.accountAddress, data: data.payload, options: { ...(data.options || {}), }, }); transaction = transaction.bcsToHex().toString(); /// #endif this.message?.sendMessage( { uuid: new Date().getTime().toString(), methodName, metadata: this._metadata, data: { serializedTransaction: { data: transaction, sender: this.accountAddress, }, }, }, (res) => { console.log('signAndSubmitTransaction res: ', res); if (res?.hash) { const result: UserResponse<{ hash: string }> = { status: UserResponseStatus.APPROVED, args: { hash: res.hash, }, }; resolve(result); } else { const result: UserRejection = { status: UserResponseStatus.REJECTED, message: res?.message }; resolve(result); } } ); }); }; signTransaction = ( transactionHex: string, transactionType: EndlessWalletTransactionType = EndlessWalletTransactionType.SIMPLE ): Promise<UserResponse<{ data: string }>> => { return new Promise(async (resolve) => { if (!this.accountAddress || !this._endless) { const result: UserRejection = { status: UserResponseStatus.REJECTED, message: 'Wallet not linked' }; resolve(result); return; } this.message?.sendMessage( { uuid: new Date().getTime().toString(), methodName: MethodName.SIGN_BUILD_TRANSACTION, metadata: this._metadata, data: { transactionData: transactionHex, sender: this.accountAddress, type: transactionType, }, }, (res) => { if (res?.signature) { const result = { status: UserResponseStatus.APPROVED, args: { data: res.signature, }, }; resolve(result); } else { const result: UserRejection = { status: UserResponseStatus.REJECTED, message: res?.message }; resolve(result); } } ); }); }; onAccountChange = (callback: (data: AccountInfo) => void) => { this.on(EndLessSDKEvent.ACCOUNT_CHANGE, callback); }; onNetworkChange = (callback: (data: NetworkInfo) => void) => { this.on(EndLessSDKEvent.NETWORK_CHANGE, callback); }; }