@luffalab/luffa-tron-sdk
Version:
luffa tron ts sdk
299 lines (279 loc) • 9.2 kB
text/typescript
import { AccountInfo, LuffaTronSdk, MethodName } from '../index';
import type { IMessageData, TronSDKEventListenersType, TronSDKEventPayload, TronSDKEventType } from './types';
import { TronSDKEvent, IResponseMessageData } from './types';
import { isLuffaMiniProgramWebview, isLuffaMiniProgram, isLuffa, networkMap, getChainIdByName } from '../utils';
export class PostMessage {
private static _instance: PostMessage;
callbacks: {
[key: string]: (data: unknown) => void;
} = {};
private listeners: TronSDKEventListenersType = {};
constructor() {
if (PostMessage._instance) return PostMessage._instance;
PostMessage._instance = this;
if (isLuffa()) {
window.tronWallet = {
sendResponse: this.sendResponse.bind(this),
};
}
}
// wallet emit sdk
// SDK processing method itself
private readonly receive = (msg: { data: IResponseMessageData }) => {
const eventType = msg.data.methodName as TronSDKEventType | MethodName;
// sdk callbacks
if (this.callbacks[msg.data.uuid + eventType]) {
this.callbacks[msg.data.uuid + eventType](msg.data.data);
delete this.callbacks[msg.data.uuid + eventType];
}
// Some events are being monitored by dapp and require running a monitoring callback
switch (eventType) {
case TronSDKEvent.NETWORK_CHANGE: {
let network = msg.data.data.network;
if (!network) break;
if (isLuffaMiniProgram()) {
network = msg.data.data;
}
this.emit(eventType, network);
this.emit(TronSDKEvent.CHAIN_CHANGED, `0x${getChainIdByName(network)?.toString(16)}`);
break;
}
// wallet to sdk send onAccountChange | connect
// sdk to dapp send event
// CONNECT and ACCOUNT_CHANGE return account as AccountAddress
case TronSDKEvent.CONNECT:
case TronSDKEvent.ACCOUNT_CHANGE: {
const accountInfo: AccountInfo = {
...msg.data.data,
};
if (msg?.data?.data?.account) {
LuffaTronSdk.setAccountAddress(msg.data.data.account);
} else {
LuffaTronSdk.setAccountAddress(null);
}
this.emit(eventType, accountInfo);
break;
}
case TronSDKEvent.DISCONNECT: {
LuffaTronSdk.setAccountAddress(null);
this.emit(eventType, msg.data.data);
break;
}
default:
this.emit(eventType as TronSDKEventType, msg.data.data);
break;
}
};
readonly addListener = <K extends TronSDKEventType>(
methodName: K,
callback: (payload: TronSDKEventPayload<K>) => void
) => {
if (!this.listeners[methodName]) {
this.listeners[methodName] = [];
}
this.listeners[methodName].push(callback);
};
readonly removeListener = <K extends TronSDKEventType>(
methodName: K,
callback?: (payload: TronSDKEventPayload<K>) => void
) => {
if (callback) {
const index = this.listeners[methodName]?.indexOf(callback) ?? -1;
if (index > -1) {
this.listeners?.[methodName]?.splice(index, 1);
}
} else {
this.listeners[methodName] = [];
}
};
readonly emit = <K extends TronSDKEventType>(methodName: K, payload: TronSDKEventPayload<K>) => {
this.listeners?.[methodName]?.forEach((d) => d(payload));
this.emitTronEvent(methodName, payload);
};
private emitTronEvent<K extends TronSDKEventType>(methodName: K, payload: TronSDKEventPayload<K>) {
if (typeof window === 'undefined' || !window.tron) return;
let tronPayload: any;
switch (methodName) {
case TronSDKEvent.CONNECT:
case TronSDKEvent.ACCOUNT_CHANGE:
const account = (payload as AccountInfo)?.account || (payload as AccountInfo)?.address;
tronPayload = {
action: 'accountsChanged',
data: {
address: account,
}
};
break;
case TronSDKEvent.DISCONNECT:
tronPayload = undefined;
break;
default:
tronPayload = payload;
break;
}
try {
const event = new MessageEvent('message', {
data: {
isTronLink: true,
message: tronPayload,
}
});
window.dispatchEvent(event);
} catch (error) {
console.warn('Failed to emit tron event:', error);
}
}
readonly sendMessage = (data: IMessageData, callback?: (data: any) => void) => {
try {
if (isLuffa()) {
this.sendLuffaMessage(data, callback);
return;
}
if (isLuffaMiniProgram() || isLuffaMiniProgramWebview()) {
this.sendMiniProgramMessage(data, callback);
return;
}
} catch (error) {
console.error('sendMessage error: ', error);
}
};
private sendLuffaMessage(data: IMessageData, callback?: (data: any) => void) {
const initData = data?.initData || LuffaTronSdk.getIninData();
(data.initData = {
...(initData || {}),
network: networkMap[initData.network] ?? initData.network,
}),
(data.from = LuffaTronSdk.getAccountAddress());
if (callback) {
this.callbacks[data.uuid + data.methodName] = callback;
}
console.log('luffa tron sendLuffaMessage: ', data);
if (window?._tronWallet) {
window._tronWallet?.sendMessage(JSON.stringify(data));
} else if (window?.webkit && window?.webkit?.messageHandlers?._tronWallet) {
window.webkit.messageHandlers._tronWallet?.postMessage(data);
}
}
private sendResponse(response: string) {
console.log('luffa tron sendResponse: ', response);
const responseData = JSON.parse(response);
console.log('luffa tron responseData: ', responseData);
this.receive({
data: responseData,
});
}
private sendMiniProgramMessage(data: IMessageData, callback?: (data: any) => void) {
let funName = 'invokeNativePlugin';
const accountAddress = LuffaTronSdk.getAccountAddress();
const initData = LuffaTronSdk.getIninData();
let api_name = 'luffaWebRequest';
let params = {
api_name,
data: {
func: data.methodName,
chainType: 'endless',
...data,
initData: {
...initData,
network: networkMap[initData.network] ?? initData.network,
},
from: accountAddress,
},
};
let callbackSuccessFun = (res: any) => {
callback &&
callback({
status: 'success',
...(res.data || {}),
});
};
let callbackErrorFun = (res: any) => {
callback &&
callback({
status: 'error',
...(res.data || {}),
});
};
switch (data.methodName) {
case MethodName.CONNECT:
case MethodName.GETACCOUNT:
callbackSuccessFun = (res) => {
LuffaTronSdk.setAccountAddress(res.data.account);
callback &&
callback({
account: res?.data?.address || '',
...(res.data || {}),
});
};
callbackErrorFun = (res) => {
callback &&
callback({
account: '',
});
};
break;
case MethodName.SIGN_MESSAGE:
case MethodName.SIGN_TRANSACTION:
case MethodName.SIGN_BUILD_TRANSACTION:
case MethodName.SIGN_AND_SUBMIT_TRANSACTION:
case MethodName.EVM_APPROVE:
callbackSuccessFun = (res: any) => {
callback &&
callback({
status: 'success',
...(res.data || {}),
});
};
callbackErrorFun = (res: any) => {
callback &&
callback({
status: 'error',
...(res.data || {}),
});
};
break;
default:
break;
}
console.log('luffa tron funName: ', funName);
console.log('luffa tron params: ', params);
if (isLuffaMiniProgram()) {
wx[funName]({
...params,
complete: (res: any) => {
console.log(`luffa tron wx ${funName} res: `, res);
if (res.status === undefined) {
callback &&
callback({
...res,
});
} else if (res.status === 'success') {
callbackSuccessFun(res);
this.receive({
data: res,
});
} else {
callbackErrorFun(res);
}
},
});
} else if (isLuffaMiniProgramWebview()) {
window.WeixinJSBridge?.invoke(funName, params, (res) => {
console.log(`luffa tron WeixinJSBridge ${funName} res: `, res);
if (res.status === undefined) {
callback &&
callback({
...res,
});
} else if (res.status === 'success') {
callbackSuccessFun(res);
this.receive({
data: res,
});
} else {
callbackErrorFun(res);
}
});
}
}
}