@luffalab/luffa-endless-sdk
Version:
luffa endless ts sdk
382 lines (361 loc) • 12.2 kB
text/typescript
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,
} from './types';
// web and WebView
/// #if BUILD_PLATFORM !== 'MINIPROGRAM'
import {
Endless,
EndlessConfig,
} from '@endlesslab/endless-ts-sdk';
/// #endif
import { isLuffa, isLuffaMiniProgram, isLuffaMiniProgramWebview } 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 = 'switchNetwork',
SIGN_MESSAGE = 'signMessage',
SEND_TRANSACTION = 'sendTransaction',
SIGN_AND_SUBMIT_TRANSACTION = 'signAndSubmitTransaction',
SIGN_BUILD_TRANSACTION = 'signBuildTransaction',
ACCOUNT_CHANGE = 'accountChange',
}
export class EndlessLuffaSdk {
static readonly version: string = '1.0.5';
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.getMetadata();
this.initConfig(initData);
EndlessLuffaSdk._instance = this;
}
private initConfig(initData: IInitData) {
if (isLuffaMiniProgram()) {
this._initData.network = initData.network;
return;
}
// web and WebView
/// #if BUILD_PLATFORM !== 'MINIPROGRAM'
this._initData.network = initData.network;
this._endlessConfig = new EndlessConfig({
network: initData.network as unknown as any,
miniprogram: initData.miniprogram
});
this._endless = new Endless(this._endlessConfig);
/// #endif
}
private initWalletEvent() {
this.on(EndLessSDKEvent.NETWORK_CHANGE, (payload) => {
this.initConfig({
network: payload.name,
});
});
}
changeNetwork(initData: IInitData) {
this.message?.sendMessage({
uuid: new Date().getTime().toString(),
methodName: MethodName.NETWORK_CHANGE,
metadata: this._metadata,
data: {
...this._initData,
...initData,
},
});
}
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._metadata.title = window.document.title;
this._metadata.url = window.location.href;
this._metadata.origin = window.location.origin;
this._metadata.icon = iconUrl;
}
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((resolve) => {
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: {
fullMessage: data.message,
signature: res.signature,
publicKey: res.publicKey,
nonce: '',
message: data.message,
prefix: 'Endless',
},
};
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) {
const result: UserRejection = { status: UserResponseStatus.REJECTED, message: 'Wallet not linked' };
resolve(result);
return;
}
let transaction;
if (isLuffa() || isLuffaMiniProgramWebview()) {
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();
} else if (isLuffaMiniProgram()) {
// TODO MiniProgram build transaction
transaction = data;
}
this.message?.sendMessage(
{
uuid: new Date().getTime().toString(),
methodName: MethodName.SIGN_AND_SUBMIT_TRANSACTION,
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);
};
}