tanglepaysdk-client
Version:
DApp SDK to interact with TanglePay wallets
200 lines (186 loc) • 6.24 kB
text/typescript
import { IotaReadyEventData, IotaResponse, SendToScriptParam, WindowSharedContext } from './types';
import { EventEmitter } from 'events';
import {EventCallback, JsonRpcEngine, JsonRpcId, JsonRpcResponse, WindowPostStream} from 'tanglepaysdk-common';
// context objects
const context:{ curTanglePayAddress?:string } = {};
const iotaRequests: Record<string, EventCallback> = {};
let _seq = 1;
const _rpcVersion = 101;
const _stream = new WindowPostStream();
const _rpcEngine = JsonRpcEngine
.builder<SendToScriptParam, unknown>()
.add(async (req, next)=>{
req.id = _seq++
req.version = _rpcVersion
req.params!.cmd = `injectToContent##${req.params!.cmd}`;
req.params!.origin = window.location.origin;
req.params!.id = req.id
return next!(req);
})
.add(async (req) => {
const { cmd, data, id } = req.params!;
_stream.write(req.params!);
if (cmd == 'injectToContent##iota_request') { // case request
const { method } = data;
return new Promise<JsonRpcResponse<unknown>>((resolve, reject)=>{
iotaRequests[`iota_request_${method}_${req.id??0}`] = (res: IotaResponse<any>, code:number) => {
if (code === 200) {
// cache iota address
if (method === 'iota_connect') {
const address = res.address || '';
context.curTanglePayAddress = address + '_' + res.nodeId;
// polyfill chainId
const nodeIdToChainId:Record<number, number> = {
1: 12345678903, // iota mainnet
102: 12345678901, // shimmer mainnet
101: 12345678900, // shimmer testnet
}
// if res.chainId is not set, set it to value of nodeIdToChainId[res.nodeId], if it exists
if (!res.chainId && nodeIdToChainId[res.nodeId]) {
res.chainId = nodeIdToChainId[res.nodeId]
}
}
resolve({ id, version:100, data: res });
} else {
reject(res);
}
};
});
} else {
return { id:req.id!, version:100, data: undefined };
}
})
.build();
const IotaSDK = {
//redirectAppStoreIfNotInstalled: false,
isTanglePay: false,
tanglePayVersion: '',
_events: new EventEmitter(),
_waitReady:Promise<void>,
request: async ({ method, params, timeout = 30000 }:{ method: string, timeout?: number, params: unknown }) => {
if (!IotaSDK.isTanglePay) {
console.log('tanglepay not installed');
return
//toInstall(IotaSDK.redirectAppStoreIfNotInstalled);
}
method = !['eth_sign', 'personal_sign'].includes(method) ? method : 'iota_sign';
// @ts-ignore
const res = await _rpcEngine.request({ params:{
cmd: 'iota_request',
data: { method, params },
} });
if(res.error) {
throw res.error
}
return res.data;
},
on: (event: string, callBack : EventCallback) => {
const key = `iota_event_${event}`;
IotaSDK._events.on(key, callBack);
},
onLoad: () => {
onLoad();
},
removeListener(event: string, callBack: EventCallback) {
const key = `iota_event_${event}`;
IotaSDK._events.removeListener(event, callBack);
},
removeAllListener(event: string) {
const key = `iota_event_${event}`;
IotaSDK._events.removeAllListeners(event);
},
};
// get message from content-script
_stream.on('data', (data_?:any)=>{
const cmd = (data_?.cmd || '').replace('contentToInject##', '');
const data = data_?.data;
const code = data_?.code;
const reqId = data_?.id;
switch (cmd) {
case 'getTanglePayInfo':
IotaSDK.tanglePayVersion = data?.version;
const eventData:IotaReadyEventData = {
isTanglePayInstalled: true,
tanglePayVersion: data?.version,
environment: _stream.isMobile ? 'app' : 'chrome',
}
window.dispatchEvent(new CustomEvent('iota-ready',{detail:eventData}));
IotaSDK._events.emit('iota-ready', eventData);
break;
case 'iota_request':
{
const callBack = iotaRequests[`iota_request_${data.method}_${reqId??0}`];
if (callBack) callBack(data.response, code);
}
break;
case 'iota_event':
{
const event = data.method;
const key = `iota_event_${data.method}`;
const res:IotaResponse<any> = data.response;
if (event === 'accountsChanged') {
const address = res.address + '_' + res.nodeId;
if (context.curTanglePayAddress !== address) {
IotaSDK._events.emit(key, res);
}
context.curTanglePayAddress = address || '';
} else {
IotaSDK._events.emit(key, res);
}
}
break;
default:
break;
}
});
let loadNum = 0;
const onLoad = () => {
console.log('onLoad', loadNum);
/*
let readyResolve:(value:unknown)=>void;
IotaSDK._waitReady = new Promise((resolve, reject)=>{
readyResolve = resolve;
setTimeout(()=>{
reject('timeout');
},15000);
})
*/
const sharedContext = window as unknown as WindowSharedContext;
const env = sharedContext.TanglePayEnv;
if (!env) {
loadNum++;
if (loadNum <= 10) {
setTimeout(onLoad, 300);
return;
}
}
console.log('onLoad for env:', env);
switch (env) {
case 'app':
case 'chrome':
{
IotaSDK.isTanglePay = true;
_stream.isMobile = env == 'app';
// @ts-ignore
_rpcEngine.request({ params:{
cmd: 'getTanglePayInfo',
} });
}
break;
default:
{
console.log('not tanglepay');
const eventData:IotaReadyEventData = {
isTanglePayInstalled: false,
tanglePayVersion: '',
environment: 'unknown',
}
window.dispatchEvent(new CustomEvent('iota-ready',{detail:eventData}));
IotaSDK._events.emit('iota-ready', eventData);
//toInstall(IotaSDK.redirectAppStoreIfNotInstalled);
}
break;
}
};
window.addEventListener('load', onLoad);
export default IotaSDK;