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 = {}; let _seq = 1; const _rpcVersion = 101; const _stream = new WindowPostStream(); const _rpcEngine = JsonRpcEngine .builder() .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>((resolve, reject)=>{ iotaRequests[`iota_request_${method}_${req.id??0}`] = (res: IotaResponse, 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 = { 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, 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 = 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;