UNPKG

@onekeyfe/blockchain-link

Version:

High-level javascript interface for blockchain communication

360 lines (359 loc) 14.3 kB
"use strict"; var __extends = (this && this.__extends) || (function () { var extendStatics = function (d, b) { extendStatics = Object.setPrototypeOf || ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; }; return extendStatics(d, b); }; return function (d, b) { extendStatics(d, b); function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); }; })(); var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __generator = (this && this.__generator) || function (thisArg, body) { var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; function verb(n) { return function (v) { return step([n, v]); }; } function step(op) { if (f) throw new TypeError("Generator is already executing."); while (_) try { if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; if (y = 0, t) op = [op[0] & 2, t.value]; switch (op[0]) { case 0: case 1: t = op; break; case 4: _.label++; return { value: op[1], done: false }; case 5: _.label++; y = op[1]; op = [0]; continue; case 7: op = _.ops.pop(); _.trys.pop(); continue; default: if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } if (t[2]) _.ops.pop(); _.trys.pop(); continue; } op = body.call(thisArg, _); } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; } }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); var ws_1 = __importDefault(require("ws")); var events_1 = require("events"); var errors_1 = require("../../constants/errors"); var deferred_1 = require("../../utils/deferred"); var NOT_INITIALIZED = new errors_1.CustomError('websocket_not_initialized'); var DEFAULT_TIMEOUT = 20 * 1000; var DEFAULT_PING_TIMEOUT = 50 * 1000; var Socket = (function (_super) { __extends(Socket, _super); function Socket(options) { var _this = _super.call(this) || this; _this.messageID = 0; _this.messages = []; _this.subscriptions = []; _this.send = function (method, params) { if (params === void 0) { params = {}; } var ws = _this.ws; if (!ws) throw NOT_INITIALIZED; var id = _this.messageID.toString(); var dfd = deferred_1.create(id); var req = { id: id, method: method, params: params, }; _this.messageID++; _this.messages.push(dfd); _this.setConnectionTimeout(); _this.setPingTimeout(); ws.send(JSON.stringify(req)); return dfd.promise; }; _this.setMaxListeners(Infinity); _this.options = options; return _this; } Socket.prototype.setConnectionTimeout = function () { this.clearConnectionTimeout(); this.connectionTimeout = setTimeout(this.onTimeout.bind(this), this.options.timeout || DEFAULT_TIMEOUT); }; Socket.prototype.clearConnectionTimeout = function () { if (this.connectionTimeout) { clearTimeout(this.connectionTimeout); this.connectionTimeout = undefined; } }; Socket.prototype.setPingTimeout = function () { if (this.pingTimeout) { clearTimeout(this.pingTimeout); } this.pingTimeout = setTimeout(this.onPing.bind(this), this.options.pingTimeout || DEFAULT_PING_TIMEOUT); }; Socket.prototype.onTimeout = function () { var ws = this.ws; if (!ws) return; if (ws.listenerCount('open') > 0) { ws.emit('error', 'Websocket timeout'); try { ws.close(); } catch (error) { } } else { this.messages.forEach(function (m) { return m.reject(new errors_1.CustomError('websocket_timeout')); }); ws.close(); } }; Socket.prototype.onPing = function () { return __awaiter(this, void 0, void 0, function () { return __generator(this, function (_a) { switch (_a.label) { case 0: if (!(this.ws && this.isConnected())) return [3, 3]; if (!(this.subscriptions.length > 0 || this.options.keepAlive)) return [3, 2]; return [4, this.getBlockHash(0)]; case 1: _a.sent(); return [3, 3]; case 2: try { this.ws.close(); } catch (error) { } _a.label = 3; case 3: return [2]; } }); }); }; Socket.prototype.onError = function () { this.dispose(); }; Socket.prototype.onmessage = function (message) { try { var resp = JSON.parse(message); var id_1 = resp.id, data = resp.data; var dfd = this.messages.find(function (m) { return m.id === id_1; }); if (dfd) { if (data.error) { dfd.reject(new errors_1.CustomError('websocket_error_message', data.error.message)); } else { dfd.resolve(data); } this.messages.splice(this.messages.indexOf(dfd), 1); } else { var subs = this.subscriptions.find(function (s) { return s && s.id === id_1; }); if (subs) { subs.callback(data); } } } catch (error) { } if (this.messages.length === 0) { this.clearConnectionTimeout(); } this.setPingTimeout(); }; Socket.prototype.connect = function () { var _this = this; var url = this.options.url; if (typeof url !== 'string') { throw new errors_1.CustomError('websocket_no_url'); } if (url.startsWith('https')) { url = url.replace('https', 'wss'); } if (url.startsWith('http')) { url = url.replace('http', 'ws'); } if (!url.endsWith('/websocket')) { var suffix = url.endsWith('/') ? 'websocket' : '/websocket'; url += suffix; } this.setConnectionTimeout(); var dfd = deferred_1.create(-1); var ws = new ws_1.default(url); if (typeof ws.setMaxListeners === 'function') { ws.setMaxListeners(Infinity); } ws.once('error', function (error) { _this.dispose(); dfd.reject(new errors_1.CustomError('websocket_runtime_error', error.message)); }); ws.on('open', function () { _this.init(); dfd.resolve(); }); this.ws = ws; return dfd.promise; }; Socket.prototype.init = function () { var _this = this; var ws = this.ws; if (!ws || !this.isConnected()) { throw Error('Blockbook websocket init cannot be called'); } this.clearConnectionTimeout(); ws.removeAllListeners(); ws.on('error', this.onError.bind(this)); ws.on('message', this.onmessage.bind(this)); ws.on('close', function () { _this.emit('disconnected'); _this.dispose(); }); }; Socket.prototype.disconnect = function () { if (this.ws) { this.ws.close(); } }; Socket.prototype.isConnected = function () { var ws = this.ws; return ws && ws.readyState === ws_1.default.OPEN; }; Socket.prototype.getServerInfo = function () { return this.send('getInfo'); }; Socket.prototype.getBlockHash = function (block) { return this.send('getBlockHash', { height: block }); }; Socket.prototype.getAccountInfo = function (payload) { return this.send('getAccountInfo', payload); }; Socket.prototype.getAccountUtxo = function (descriptor) { return this.send('getAccountUtxo', { descriptor: descriptor }); }; Socket.prototype.getTransaction = function (txid) { return this.send('getTransaction', { txid: txid }); }; Socket.prototype.pushTransaction = function (hex) { return this.send('sendTransaction', { hex: hex }); }; Socket.prototype.estimateFee = function (payload) { return this.send('estimateFee', payload); }; Socket.prototype.getCurrentFiatRates = function (payload) { return this.send('getCurrentFiatRates', payload); }; Socket.prototype.getAccountBalanceHistory = function (payload) { return this.send('getBalanceHistory', payload); }; Socket.prototype.getFiatRatesForTimestamps = function (payload) { return this.send('getFiatRatesForTimestamps', payload); }; Socket.prototype.getFiatRatesTickersList = function (payload) { return this.send('getFiatRatesTickersList', payload); }; Socket.prototype.subscribeAddresses = function (addresses) { var _this = this; var index = this.subscriptions.findIndex(function (s) { return s.type === 'notification'; }); if (index >= 0) { this.subscriptions.splice(index, 1); } var id = this.messageID.toString(); this.subscriptions.push({ id: id, type: 'notification', callback: function (result) { _this.emit('notification', result); }, }); return this.send('subscribeAddresses', { addresses: addresses }); }; Socket.prototype.unsubscribeAddresses = function () { var index = this.subscriptions.findIndex(function (s) { return s.type === 'notification'; }); if (index >= 0) { this.subscriptions.splice(index, 1); return this.send('unsubscribeAddresses'); } return { subscribed: false }; }; Socket.prototype.subscribeBlock = function () { var _this = this; var index = this.subscriptions.findIndex(function (s) { return s.type === 'block'; }); if (index >= 0) { this.subscriptions.splice(index, 1); } var id = this.messageID.toString(); this.subscriptions.push({ id: id, type: 'block', callback: function (result) { _this.emit('block', result); }, }); return this.send('subscribeNewBlock'); }; Socket.prototype.unsubscribeBlock = function () { var index = this.subscriptions.findIndex(function (s) { return s.type === 'block'; }); if (index >= 0) { this.subscriptions.splice(index, 1); return this.send('unsubscribeNewBlock'); } return { subscribed: false }; }; Socket.prototype.subscribeFiatRates = function (currency) { var _this = this; var index = this.subscriptions.findIndex(function (s) { return s.type === 'fiatRates'; }); if (index >= 0) { this.subscriptions.splice(index, 1); } var id = this.messageID.toString(); this.subscriptions.push({ id: id, type: 'fiatRates', callback: function (result) { _this.emit('fiatRates', result); }, }); return this.send('subscribeFiatRates', { currency: currency }); }; Socket.prototype.unsubscribeFiatRates = function () { var index = this.subscriptions.findIndex(function (s) { return s.type === 'fiatRates'; }); if (index >= 0) { this.subscriptions.splice(index, 1); return this.send('unsubscribeFiatRates'); } return { subscribed: false }; }; Socket.prototype.dispose = function () { if (this.pingTimeout) { clearTimeout(this.pingTimeout); } if (this.connectionTimeout) { clearTimeout(this.connectionTimeout); } var ws = this.ws; if (this.isConnected()) { this.disconnect(); } if (ws) { ws.removeAllListeners(); } this.removeAllListeners(); }; return Socket; }(events_1.EventEmitter)); exports.default = Socket;