@trezor/blockchain-link
Version:
High-level javascript interface for blockchain communication
237 lines • 7.76 kB
JavaScript
"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