@luffalab/luffa-tron-sdk
Version:
luffa tron ts sdk
373 lines (350 loc) • 11.4 kB
text/typescript
/* import { Buffer } from 'buffer';
globalThis.Buffer = Buffer; */
import { PostMessage } from './message';
import {
IInitData,
UserResponse,
AccountInfo,
UserResponseStatus,
EndlessSignMessageInput,
EndlessSignMessageOutput,
EndlessSignAndSubmitTransactionInput,
EndlessWalletTransactionType,
UserRejection,
NetworkInfo,
ChainData,
} from './types';
// web and WebView
/// #if BUILD_PLATFORM !== 'MINIPROGRAM'
/// #endif
import { isApproveTx, isLuffa, isLuffaMiniProgram, isLuffaMiniProgramWebview, normalizeMessageForDisplay } from './utils';
import { TronWebOptions } from 'tronWeb/lib/esm/types';
import { TronSDKEvent, TronSDKEventPayload, TronSDKEventType, IRequestData } from './message/types';
export { isLuffa, isLuffaMiniProgram, isLuffaMiniProgramWebview } from './utils';
export interface Metadata {
title: string;
url: string;
origin: string;
icon: string;
gameId: string;
userId: string;
walletAddress: string;
}
export interface TronRequestParams {
method: 'eth_requestAccounts' | string;
params?: any;
}
export { TronSDKEvent } 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_TRANSACTION = 'signTransaction',
SIGN_BUILD_TRANSACTION = 'signBuildTransaction',
EVM_APPROVE = 'evmApprove',
ACCOUNT_CHANGE = 'accountChange',
}
export class LuffaTronSdk {
static readonly version: string = '1.0.5';
private static _instance: LuffaTronSdk;
private message: PostMessage | null = null;
private _metadata: Metadata = {} as Metadata;
private _initData: IInitData = {} as IInitData;
private accountAddress: string | null = null;
private _wallet: any | null = null;
static getIninData = (): IInitData => {
if (LuffaTronSdk._instance) {
return LuffaTronSdk._instance._initData;
} else {
return {} as IInitData;
}
};
static getAccountAddress = () => {
if (LuffaTronSdk._instance) {
return LuffaTronSdk._instance.accountAddress;
} else {
return null;
}
};
static setAccountAddress = (accountAddress: string | null) => {
if (LuffaTronSdk._instance) {
LuffaTronSdk._instance?._wallet?.setAddress(accountAddress);
LuffaTronSdk._instance.accountAddress = accountAddress;
}
};
constructor(initData: TronWebOptions & { network: string }) {
if (LuffaTronSdk._instance) return LuffaTronSdk._instance;
this.message = new PostMessage();
this.getMetadata();
this.initConfig(initData);
LuffaTronSdk._instance = this;
}
private initConfig(initData: TronWebOptions & { network: string }) {
this._initData.callbackWalletName = 'tronWallet';
this._initData.network = initData.network;
if (isLuffaMiniProgram()) {
return;
}
// web and WebView
/// #if BUILD_PLATFORM !== 'MINIPROGRAM'
this._wallet = new window.TronWeb.TronWeb(initData);
this._wallet.trx.sign = this.sendTransaction.bind(this);
this._wallet.trx.signTransaction = this.sendTransaction.bind(this);
const tronProvider = Object.freeze({
isTronLink: false,
request: async (params: TronRequestParams) => {
switch (params.method) {
case 'eth_requestAccounts':
return this.connect();
default:
return { code: 4200, message: 'Unknown method called' };
}
},
tronWeb: this._wallet,
on: this.on,
removeListener: this.off,
});
Object.defineProperty(globalThis, 'tronWeb', {
value: this._wallet,
writable: false,
configurable: false,
enumerable: true
});
Object.defineProperty(globalThis, 'tron', {
value: tronProvider,
writable: false,
configurable: false,
enumerable: true
});
/// #endif
}
async sendTransaction(params: any) {
if (!this.accountAddress) {
await this.connect();
}
console.log('this.accountAddress: ', this.accountAddress);
if (typeof params === 'string') {
return this.signMessage(params);
}
const { isApprove, spender } = isApproveTx(params);
console.log('isApprove: ', isApprove, spender);
if (isApprove) {
params.to = spender;
return this.signTransaction(params, MethodName.EVM_APPROVE)
} else {
return this.signTransaction(params)
}
}
changeNetwork(chainData: ChainData) {
this.message?.sendMessage({
uuid: new Date().getTime().toString(),
methodName: MethodName.NETWORK_CHANGE,
metadata: this._metadata,
data: {
...chainData,
},
});
}
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<string[]> => {
return new Promise((resolve) => {
this.message?.sendMessage(
{
uuid: new Date().getTime().toString(),
methodName: MethodName.GETACCOUNT,
metadata: this._metadata,
data: {},
},
(data) => {
if (data?.account) {
this._wallet.setAddress(data.account);
this.accountAddress = data.account;
resolve([data.account]);
} else {
const res: UserResponse<AccountInfo> = {
status: UserResponseStatus.REJECTED,
message: data?.message || 'Wallet is not connected',
};
resolve([]);
}
}
);
});
};
connect = (): Promise<[string]> => {
return new Promise((resolve, reject) => {
this.message?.sendMessage(
{
uuid: new Date().getTime().toString(),
methodName: MethodName.CONNECT,
metadata: this._metadata,
data: {},
},
(data) => {
let res;
if (data?.account) {
this._wallet.setAddress(data.account);
this.accountAddress = data.account;
res = [data.account] as [string];
resolve(res);
} else {
res = {
code: 4001,
message: 'User rejected the request.',
};
reject(res);
}
}
);
});
};
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();
}
);
});
};
on = <K extends TronSDKEventType>(methodName: K, callback: (payload: TronSDKEventPayload<K>) => void) => {
if (this.message?.addListener) {
this.message?.addListener(methodName, callback);
}
};
off = <K extends TronSDKEventType>(methodName: K, callback?: (payload: TronSDKEventPayload<K>) => void) => {
if (this.message?.removeListener) {
this.message?.removeListener(methodName, callback);
}
};
signAndSubmitTransaction = (params: any): Promise<any> => {
console.log('signAndSubmitTransaction data: ', params);
return new Promise(async (resolve) => {
this.message?.sendMessage(
{
uuid: new Date().getTime().toString(),
methodName: MethodName.SIGN_AND_SUBMIT_TRANSACTION,
metadata: this._metadata,
data: params
},
(res) => {
if (res?.hash) {
resolve(res.hash);
} else {
resolve({
code: 4001,
message: res?.message
});
}
}
);
});
};
signTransaction = (params: any, methodName = MethodName.SIGN_BUILD_TRANSACTION): Promise<any> => {
return new Promise(async (resolve, reject) => {
const data: { raw_data_hex: string; to?: string } = {
raw_data_hex: params.raw_data_hex
}
if (params.to) {
data.to = params.to;
}
this.message?.sendMessage(
{
uuid: new Date().getTime().toString(),
methodName,
metadata: this._metadata,
data,
},
(res) => {
const signature = res?.signature?.split(',') || [];
console.log('luffa tron signature: ', signature);
if (Array.isArray(signature) && signature.length > 0) {
const result = {
...params,
signature
};
resolve(result);
} else {
reject('Confirmation declined by user');
}
}
);
});
};
signMessage = (
data: string,
callback?: (data: unknown) => void
): Promise<UserResponse<any>> => {
return new Promise(async (resolve) => {
this.message?.sendMessage(
{
uuid: new Date().getTime().toString(),
methodName: MethodName.SIGN_MESSAGE,
metadata: this._metadata,
data: {
message: normalizeMessageForDisplay(data)
}
},
(res) => {
console.log('signMessage res: ', res);
if (res?.signature) {
const result: UserResponse<any> = {
status: UserResponseStatus.APPROVED,
args: res,
};
resolve(result);
} else {
const result: UserRejection = { status: UserResponseStatus.REJECTED, message: res?.message };
resolve(result);
}
if (callback) callback(res);
}
);
});
};
onAccountChange = (callback: (data: AccountInfo) => void) => {
this.on(TronSDKEvent.ACCOUNT_CHANGE, callback);
};
onNetworkChange = (callback: (data: NetworkInfo) => void) => {
this.on(TronSDKEvent.NETWORK_CHANGE, callback);
};
}