UNPKG

@trezor/blockchain-link

Version:

High-level javascript interface for blockchain communication

237 lines 7.76 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const constants_1 = require("@trezor/blockchain-link-types/lib/constants"); const errors_1 = require("@trezor/blockchain-link-types/lib/constants/errors"); const utils_1 = require("@trezor/utils"); const workerWrapper = (factory) => { if (typeof factory === 'function') return factory(); if (typeof factory === 'string' && typeof Worker !== 'undefined') return new Worker(factory); throw new errors_1.CustomError('worker_not_found'); }; const initWorker = async (settings) => { const dfd = (0, utils_1.createDeferred)(-1); const worker = await workerWrapper(settings.worker); if (typeof worker !== 'object' || typeof worker.postMessage !== 'function') { throw new errors_1.CustomError('worker_invalid'); } const timeout = setTimeout(() => { worker.onmessage = null; worker.onerror = null; dfd.reject(new errors_1.CustomError('worker_timeout')); }, settings.timeout || 30000); worker.onmessage = (message) => { if (message.data.type !== constants_1.MESSAGES.HANDSHAKE) return; clearTimeout(timeout); worker.postMessage({ type: constants_1.MESSAGES.HANDSHAKE, settings: Object.assign(settings, { worker: undefined }), }); dfd.resolve(worker); }; worker.onerror = (error) => { clearTimeout(timeout); worker.onmessage = null; worker.onerror = null; try { worker.terminate(); } catch { } const message = error.message ? `Worker runtime error: Line ${error.lineno} in ${error.filename}: ${error.message}` : 'Worker handshake error'; dfd.reject(new errors_1.CustomError('worker_runtime', message)); }; return dfd.promise; }; class BlockchainLink extends utils_1.TypedEmitter { settings; lazyWorker = (0, utils_1.createLazy)(this.initWorker.bind(this), this.disposeWorker.bind(this)); deferred = (0, utils_1.createDeferredManager)(); throttler; constructor(settings) { super(); this.settings = settings; const throttleBlockEventTimeout = typeof settings.throttleBlockEvent === 'number' ? settings.throttleBlockEvent : 500; this.throttler = new utils_1.Throttler(throttleBlockEventTimeout); } async initWorker() { const worker = await initWorker(this.settings); worker.onmessage = this.onMessage.bind(this); worker.onerror = this.onError.bind(this); return worker; } disposeWorker(worker) { worker.terminate(); } async sendMessage(message) { const worker = await this.lazyWorker.getOrInit(); const { promiseId, promise } = this.deferred.create(); worker.postMessage({ id: promiseId, ...message }); return promise; } connect() { return this.sendMessage({ type: constants_1.MESSAGES.CONNECT, }); } getInfo() { return this.sendMessage({ type: constants_1.MESSAGES.GET_INFO, }); } getBlockHash(payload) { return this.sendMessage({ type: constants_1.MESSAGES.GET_BLOCK_HASH, payload, }); } getBlock(payload) { return this.sendMessage({ type: constants_1.MESSAGES.GET_BLOCK, payload, }); } getAccountInfo(payload) { return this.sendMessage({ type: constants_1.MESSAGES.GET_ACCOUNT_INFO, payload, }); } getAccountUtxo(payload) { return this.sendMessage({ type: constants_1.MESSAGES.GET_ACCOUNT_UTXO, payload, }); } getTransaction(payload) { return this.sendMessage({ type: constants_1.MESSAGES.GET_TRANSACTION, payload, }); } getTransactionHex(payload) { return this.sendMessage({ type: constants_1.MESSAGES.GET_TRANSACTION_HEX, payload, }); } getAccountBalanceHistory(payload) { return this.sendMessage({ type: constants_1.MESSAGES.GET_ACCOUNT_BALANCE_HISTORY, payload, }); } getCurrentFiatRates(payload) { return this.sendMessage({ type: constants_1.MESSAGES.GET_CURRENT_FIAT_RATES, payload, }); } getFiatRatesForTimestamps(payload) { return this.sendMessage({ type: constants_1.MESSAGES.GET_FIAT_RATES_FOR_TIMESTAMPS, payload, }); } getFiatRatesTickersList(payload) { return this.sendMessage({ type: constants_1.MESSAGES.GET_FIAT_RATES_TICKERS_LIST, payload, }); } estimateFee(payload) { return this.sendMessage({ type: constants_1.MESSAGES.ESTIMATE_FEE, payload, }); } rpcCall(payload) { return this.sendMessage({ type: constants_1.MESSAGES.RPC_CALL, payload, }); } subscribe(payload) { return this.sendMessage({ type: constants_1.MESSAGES.SUBSCRIBE, payload, }); } unsubscribe(payload) { return this.sendMessage({ type: constants_1.MESSAGES.UNSUBSCRIBE, payload, }); } pushTransaction(payload) { return this.sendMessage({ type: constants_1.MESSAGES.PUSH_TRANSACTION, payload, }); } async disconnect() { if (!this.lazyWorker.get()) return true; return this.sendMessage({ type: constants_1.MESSAGES.DISCONNECT, }); } onMessage = event => { if (!event.data) return; const { data } = event; if (data.id === -1) { this.onEvent(data); return; } if (data.type === constants_1.RESPONSES.ERROR) { this.deferred.reject(data.id, new errors_1.CustomError(data.payload.code, data.payload.message)); } else { this.deferred.resolve(data.id, data.payload); } }; onEvent = data => { if (data.type === constants_1.RESPONSES.CONNECTED) { this.emit('connected'); } if (data.type === constants_1.RESPONSES.DISCONNECTED) { this.emit('disconnected'); } if (data.type === constants_1.RESPONSES.NOTIFICATION) { const notification = data.payload; if (notification.type === 'block') { this.throttler.throttle('block', () => { this.emit(notification.type, notification.payload); }); } else if (notification.type === 'notification') { const txAccountId = `${notification.payload.descriptor}:${notification.payload.tx.txid}`; this.throttler.throttle(txAccountId, () => { this.emit(notification.type, notification.payload); }); } else { this.emit(notification.type, notification.payload); } } }; onError = error => { const message = error.message ? `Worker runtime error: Line ${error.lineno} in ${error.filename}: ${error.message}` : 'Worker handshake error'; const e = new errors_1.CustomError('worker_runtime', message); this.deferred.rejectAll(e); }; dispose() { this.removeAllListeners(); this.throttler.dispose(); this.lazyWorker.dispose(); } } exports.default = BlockchainLink; //# sourceMappingURL=index.js.map