@trezor/connect
Version:
High-level javascript interface for Trezor hardware wallet.
243 lines • 9.56 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.AbstractMethod = exports.DEFAULT_FIRMWARE_RANGE = void 0;
const connect_common_1 = require("@trezor/connect-common");
const utils_1 = require("@trezor/utils");
const constants_1 = require("../constants");
const config_1 = require("../data/config");
const events_1 = require("../events");
const urlUtils_1 = require("../utils/urlUtils");
exports.DEFAULT_FIRMWARE_RANGE = {
UNKNOWN: { min: '1.0.0', max: '0' },
T1B1: { min: '1.0.0', max: '0' },
T2T1: { min: '2.0.0', max: '0' },
T2B1: { min: '2.6.1', max: '0' },
T3B1: { min: '2.8.1', max: '0' },
T3T1: { min: '2.7.1', max: '0' },
T3W1: { min: '2.7.1', max: '0' },
};
function validateStaticSessionId(input) {
if (typeof input !== 'string')
throw constants_1.ERRORS.TypedError('Method_InvalidParameter', 'DeviceState: invalid staticSessionId: ' + input);
const [firstTestnetAddress, rest] = input.split('@');
const [deviceId, instance] = rest.split(':');
if (typeof firstTestnetAddress === 'string' &&
typeof deviceId === 'string' &&
typeof instance === 'string' &&
Number.parseInt(instance) >= 0) {
return input;
}
throw constants_1.ERRORS.TypedError('Method_InvalidParameter', 'DeviceState: invalid staticSessionId: ' + input);
}
function validateDeviceState(input) {
if (typeof input === 'string') {
return { staticSessionId: validateStaticSessionId(input) };
}
if (input && typeof input === 'object') {
const state = {};
if ('staticSessionId' in input) {
state.staticSessionId = validateStaticSessionId(input.staticSessionId);
}
if ('sessionId' in input && typeof input.sessionId === 'string') {
state.sessionId = input.sessionId;
}
if ('deriveCardano' in input && typeof input.deriveCardano === 'boolean') {
state.deriveCardano = input.deriveCardano;
}
return state;
}
return undefined;
}
class AbstractMethod {
responseID;
device;
params;
deviceState;
hasExpectedDeviceState;
keepSession;
skipFinalReload;
skipFirmwareCheck;
overridePreviousCall;
overridden;
name;
payload;
get info() {
return '';
}
get confirmation() {
return undefined;
}
useUi;
useDevice;
useDeviceState;
preauthorized;
useEmptyPassphrase;
allowSeedlessDevice;
firmwareRange;
requiredPermissions;
allowDeviceMode;
requireDeviceMode;
requiredDeviceCapabilities = [];
network;
useCardanoDerivation;
noBackupConfirmationMode;
postMessage;
createUiPromise;
constructor(message) {
const { payload } = message;
this.name = payload.method;
this.payload = payload;
this.responseID = message.id || 0;
this.deviceState = validateDeviceState(payload.device?.state);
this.hasExpectedDeviceState = payload.device
? Object.prototype.hasOwnProperty.call(payload.device, 'state')
: false;
this.keepSession = typeof payload.keepSession === 'boolean' ? payload.keepSession : false;
this.skipFinalReload =
typeof payload.skipFinalReload === 'boolean' ? payload.skipFinalReload : true;
this.skipFirmwareCheck = false;
this.overridePreviousCall =
typeof payload.override === 'boolean' ? payload.override : false;
this.overridden = false;
this.useEmptyPassphrase =
typeof payload.useEmptyPassphrase === 'boolean' ? payload.useEmptyPassphrase : false;
this.allowSeedlessDevice =
typeof payload.allowSeedlessDevice === 'boolean' ? payload.allowSeedlessDevice : false;
this.allowDeviceMode = [];
this.requireDeviceMode = [];
if (this.allowSeedlessDevice) {
this.allowDeviceMode = [events_1.UI.SEEDLESS];
}
this.network = 'bitcoin';
(0, utils_1.typedObjectKeys)(constants_1.NETWORK.TYPES).forEach(key => {
if (this.name.startsWith(key)) {
this.network = key;
}
});
this.firmwareRange = exports.DEFAULT_FIRMWARE_RANGE;
this.requiredPermissions = [];
this.useDevice = true;
this.useDeviceState = true;
this.useUi = true;
this.useCardanoDerivation =
typeof payload.useCardanoDerivation === 'boolean'
? payload.useCardanoDerivation
: payload.method.startsWith('cardano');
this.noBackupConfirmationMode = 'never';
}
setDevice(device) {
this.device = device;
const originalFn = this.createUiPromise;
this.createUiPromise = (t, d) => originalFn(t, d || device);
}
getOriginPermissions({ origin }) {
if (!origin) {
return [];
}
return connect_common_1.storage.loadForOrigin(origin)?.permissions || [];
}
checkPermissions({ origin }) {
const originPermissions = this.getOriginPermissions({ origin });
let notPermitted = [...this.requiredPermissions];
if (originPermissions.length > 0) {
notPermitted = notPermitted.filter(np => {
const granted = originPermissions.find(p => p.type === np && p.device === this.device.features.device_id);
return !granted;
});
}
this.requiredPermissions = notPermitted;
}
savePermissions(temporary = false, { origin }) {
const originPermissions = this.getOriginPermissions({ origin });
let permissionsToSave = this.requiredPermissions.map(p => ({
type: p,
device: this.device.features.device_id || undefined,
}));
let emitEvent = false;
if (this.requiredPermissions.indexOf('read') >= 0) {
const wasAlreadyGranted = originPermissions.filter(p => p.type === 'read' && p.device === this.device.features.device_id);
if (wasAlreadyGranted.length < 1) {
emitEvent = true;
}
}
if (originPermissions.length > 0) {
permissionsToSave = permissionsToSave.filter(p2s => {
const granted = originPermissions.find(p => p.type === p2s.type && p.device === p2s.device);
return !granted;
});
}
connect_common_1.storage.saveForOrigin(state => ({
...state,
permissions: [...(state.permissions || []), ...permissionsToSave],
}), origin, temporary);
if (emitEvent) {
this.postMessage((0, events_1.createDeviceMessage)(events_1.DEVICE.CONNECT, this.device.toMessageObject()));
}
}
checkFirmwareRange() {
if (this.skipFirmwareCheck) {
return;
}
const { device } = this;
if (!device.features || device.isBootloader())
return;
if (device.isSeedless())
return;
const range = this.firmwareRange[device.features.internal_model];
if (device.firmwareStatus === 'none') {
return events_1.UI.FIRMWARE_NOT_INSTALLED;
}
if (!range) {
return;
}
if (range.min === '0') {
return events_1.UI.FIRMWARE_NOT_SUPPORTED;
}
const version = device.getVersion();
if (!version)
return;
if (this.name !== 'backupDevice' &&
this.name !== 'recoveryDevice' &&
(device.firmwareStatus === 'required' ||
!utils_1.versionUtils.isNewerOrEqual(version, range.min))) {
return events_1.UI.FIRMWARE_OLD;
}
if (range.max !== '0' && utils_1.versionUtils.isNewer(version, range.max)) {
return events_1.UI.FIRMWARE_NOT_COMPATIBLE;
}
}
isManagementRestricted({ popup, origin }) {
if (popup && this.requiredPermissions.includes('management')) {
const host = (0, urlUtils_1.getHost)(origin);
const allowed = config_1.config.management.find(item => item.origin === host || item.origin === origin);
return !allowed;
}
}
async getMethodInfo() {
return {
useUi: this.useUi,
useDevice: this.useDevice,
useDeviceState: this.useDeviceState,
name: this.name,
requiredPermissions: this.requiredPermissions,
info: this.info,
confirmation: this.confirmation,
precomposed: await this.payloadToPrecomposed(),
};
}
payloadToPrecomposed() {
return Promise.resolve(undefined);
}
checkDeviceCapability() {
const deviceHasAllRequiredCapabilities = (this.requiredDeviceCapabilities || []).every(capability => this.device.features.capabilities.includes(capability));
if (!deviceHasAllRequiredCapabilities) {
if (this.device.firmwareType === 'bitcoin-only') {
throw constants_1.ERRORS.TypedError('Device_MissingCapabilityBtcOnly', `Trezor has Bitcoin-only firmware installed, which does not support this operation. Please install Universal firmware through Trezor Suite.`);
}
throw constants_1.ERRORS.TypedError('Device_MissingCapability', 'Device does not have capability to call this method. Make sure you have the latest firmware installed.');
}
}
dispose() { }
}
exports.AbstractMethod = AbstractMethod;
//# sourceMappingURL=AbstractMethod.js.map