@trezor/connect
Version:
High-level javascript interface for Trezor hardware wallet.
345 lines (344 loc) • 10.7 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
const resolveAfter_1 = require("@trezor/utils/lib/resolveAfter");
const BlockchainLink_1 = require("../backend/BlockchainLink");
const constants_1 = require("../constants");
const AbstractMethod_1 = require("../core/AbstractMethod");
const coinInfo_1 = require("../data/coinInfo");
const events_1 = require("../events");
const Discovery_1 = require("./common/Discovery");
const paramsValidator_1 = require("./common/paramsValidator");
const accountUtils_1 = require("../utils/accountUtils");
const pathUtils_1 = require("../utils/pathUtils");
class GetAccountInfo extends AbstractMethod_1.AbstractMethod {
disposed = false;
hasBundle;
discovery;
init() {
this.requiredPermissions = ['read'];
this.useDevice = true;
this.useUi = true;
let willUseDevice = false;
this.hasBundle = !!this.payload.bundle;
const payload = !this.payload.bundle ? {
...this.payload,
bundle: [this.payload]
} : this.payload;
(0, paramsValidator_1.validateParams)(payload, [{
name: 'bundle',
type: 'array'
}]);
this.params = payload.bundle.map(batch => {
(0, paramsValidator_1.validateParams)(batch, [{
name: 'coin',
type: 'string',
required: true
}, {
name: 'identity',
type: 'string'
}, {
name: 'descriptor',
type: 'string'
}, {
name: 'path',
type: 'string'
}, {
name: 'details',
type: 'string'
}, {
name: 'tokens',
type: 'string'
}, {
name: 'page',
type: 'number'
}, {
name: 'pageSize',
type: 'number'
}, {
name: 'from',
type: 'number'
}, {
name: 'to',
type: 'number'
}, {
name: 'contractFilter',
type: 'string'
}, {
name: 'gap',
type: 'number'
}, {
name: 'marker',
type: 'object'
}, {
name: 'defaultAccountType',
type: 'string'
}, {
name: 'derivationType',
type: 'number'
}, {
name: 'suppressBackupWarning',
type: 'boolean'
}]);
const coinInfo = (0, coinInfo_1.getCoinInfo)(batch.coin);
if (!coinInfo) {
throw constants_1.ERRORS.TypedError('Method_UnknownCoin');
}
(0, BlockchainLink_1.isBackendSupported)(coinInfo);
let address_n = [];
if (batch.path) {
address_n = (0, pathUtils_1.validatePath)(batch.path, 3);
willUseDevice = typeof batch.descriptor !== 'string';
}
if (!batch.path && !batch.descriptor) {
if (payload.bundle.length > 1) {
throw Error('Discovery for multiple coins in not supported');
}
willUseDevice = true;
}
this.firmwareRange = (0, paramsValidator_1.getFirmwareRange)(this.name, coinInfo, this.firmwareRange);
return {
...batch,
address_n,
coinInfo
};
});
this.useDevice = willUseDevice;
this.useDeviceState = willUseDevice;
this.useUi = willUseDevice;
this.noBackupConfirmationMode = this.params.every(batch => batch.suppressBackupWarning) ? 'popup-only' : 'always';
}
get info() {
return 'Export account info';
}
get confirmation() {
if (this.params.length === 1 && !this.params[0].path && !this.params[0].descriptor) {
return {
view: 'export-account-info',
label: `Export info for ${this.params[0].coinInfo.label} account of your selection`,
customConfirmButton: {
label: 'Proceed to account selection',
className: 'not-empty-css'
}
};
} else {
const keys = {};
this.params.forEach(b => {
if (!keys[b.coinInfo.label]) {
keys[b.coinInfo.label] = {
coinInfo: b.coinInfo,
values: []
};
}
keys[b.coinInfo.label].values.push(b.descriptor || b.address_n);
});
const str = [];
Object.keys(keys).forEach((k, _i, _a) => {
const details = keys[k];
details.values.forEach(acc => {
str.push(k);
str.push(' ');
if (typeof acc === 'string') {
str.push(acc);
} else {
str.push((0, accountUtils_1.getAccountLabel)(acc, details.coinInfo));
}
});
});
return {
view: 'export-account-info',
label: `Export info for: ${str.join('')}`
};
}
}
checkFirmwareRange() {
if (this.params.length === 1) {
return super.checkFirmwareRange();
}
const invalid = [];
for (let i = 0; i < this.params.length; i++) {
this.firmwareRange = (0, paramsValidator_1.getFirmwareRange)(this.name, this.params[i].coinInfo, AbstractMethod_1.DEFAULT_FIRMWARE_RANGE);
const exception = super.checkFirmwareRange();
if (exception) {
invalid.push({
index: i,
exception,
coin: this.params[i].coin
});
}
}
if (invalid.length > 0) {
throw constants_1.ERRORS.TypedError('Method_Discovery_BundleException', JSON.stringify(invalid));
}
}
async run() {
if (this.params.length === 1 && !this.params[0].path && !this.params[0].descriptor) {
return this.discover(this.params[0]);
}
const responses = [];
const sendProgress = (progress, response, error) => {
if (!this.hasBundle || this.device?.getCurrentSession().isDisposed()) return;
this.postMessage((0, events_1.createUiMessage)(events_1.UI.BUNDLE_PROGRESS, {
total: this.params.length,
progress,
response,
error
}));
};
for (let i = 0; i < this.params.length; i++) {
const request = this.params[i];
const {
address_n
} = request;
let {
descriptor
} = request;
let legacyXpub;
let descriptorChecksum;
if (this.disposed) break;
if (address_n && typeof descriptor !== 'string') {
try {
const accountDescriptor = await this.device.getCommands().getAccountDescriptor(request.coinInfo, address_n, request.derivationType);
if (accountDescriptor) {
descriptor = accountDescriptor.descriptor;
legacyXpub = accountDescriptor.legacyXpub;
descriptorChecksum = accountDescriptor.descriptorChecksum;
}
} catch (error) {
if (this.hasBundle) {
responses.push(null);
sendProgress(i, null, error.message);
continue;
} else {
throw error;
}
}
}
if (this.disposed) break;
try {
if (typeof descriptor !== 'string') {
throw constants_1.ERRORS.TypedError('Runtime', 'GetAccountInfo: descriptor not found');
}
const blockchain = await (0, BlockchainLink_1.initBlockchain)(request.coinInfo, this.postMessage, request.identity);
if (this.disposed) break;
const info = await blockchain.getAccountInfo({
descriptor,
details: request.details,
tokens: request.tokens,
page: request.page,
pageSize: request.pageSize,
pageCursor: request.pageCursor,
from: request.from,
to: request.to,
contractFilter: request.contractFilter,
gap: request.gap,
marker: request.marker,
tokenAccountsPubKeys: request.tokenAccountsPubKeys
});
if (this.disposed) break;
let utxo;
if ((0, accountUtils_1.isUtxoBased)(request.coinInfo) && typeof request.details === 'string' && request.details !== 'basic') {
utxo = await blockchain.getAccountUtxo(descriptor);
}
if (this.disposed) break;
const account = {
path: request.path,
...info,
descriptor,
legacyXpub,
utxo,
descriptorChecksum
};
responses.push(account);
sendProgress(i, account);
} catch (error) {
if (this.hasBundle) {
responses.push(null);
sendProgress(i, null, error.message);
continue;
} else {
throw error;
}
}
}
if (this.disposed) return new Promise(() => []);
return this.hasBundle ? responses : responses[0];
}
async discover(request) {
const {
coinInfo,
identity,
defaultAccountType,
derivationType
} = request;
const blockchain = await (0, BlockchainLink_1.initBlockchain)(coinInfo, this.postMessage, identity);
const dfd = this.createUiPromise(events_1.UI.RECEIVE_ACCOUNT);
const discovery = new Discovery_1.Discovery({
blockchain,
getDescriptor: path => this.device.getCommands().getAccountDescriptor(coinInfo, path, derivationType)
});
discovery.on('progress', accounts => {
this.postMessage((0, events_1.createUiMessage)(events_1.UI.SELECT_ACCOUNT, {
type: 'progress',
coinInfo,
accounts
}));
});
discovery.on('complete', () => {
this.postMessage((0, events_1.createUiMessage)(events_1.UI.SELECT_ACCOUNT, {
type: 'end',
coinInfo
}));
});
discovery.start().catch(error => {
dfd.reject(error);
});
this.postMessage((0, events_1.createUiMessage)(events_1.UI.SELECT_ACCOUNT, {
type: 'start',
accountTypes: discovery.types.map(t => t.type),
defaultAccountType,
coinInfo
}));
const uiResp = await dfd.promise;
discovery.stop();
const account = discovery.accounts[uiResp.payload];
if (!discovery.completed) {
await (0, resolveAfter_1.resolveAfter)(501);
}
const info = await blockchain.getAccountInfo({
descriptor: account.descriptor,
details: request.details,
tokens: request.tokens,
page: request.page,
pageSize: request.pageSize,
pageCursor: request.pageCursor,
from: request.from,
to: request.to,
contractFilter: request.contractFilter,
gap: request.gap,
marker: request.marker
});
let utxo;
if ((0, accountUtils_1.isUtxoBased)(coinInfo) && typeof request.details === 'string' && request.details !== 'basic') {
utxo = await blockchain.getAccountUtxo(account.descriptor);
}
return {
path: (0, pathUtils_1.getSerializedPath)(account.address_n),
...info,
utxo
};
}
dispose() {
this.disposed = true;
const {
discovery
} = this;
if (discovery) {
discovery.removeAllListeners();
discovery.stop();
}
}
}
exports.default = GetAccountInfo;
//# sourceMappingURL=getAccountInfo.js.map