@trezor/connect
Version:
High-level javascript interface for Trezor hardware wallet.
316 lines • 12.6 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