(done => {
+ jest.resetModules();
- const mock = f.config;
- jest.mock('../../../data/config', () => {
- const actualConfig = jest.requireActual('../../../data/config').config;
+ const mock = f.config;
+ jest.mock('../../../data/config', () => {
+ const actualConfig = jest.requireActual('../../../data/config').config;
- return {
- __esModule: true,
- config: mock || actualConfig,
- };
- });
+ return {
+ __esModule: true,
+ config: mock || actualConfig,
+ };
+ });
- import('../paramsValidator').then(({ getFirmwareRange }) => {
- // added new capability
- // @ts-expect-error
- expect(getFirmwareRange(...f.params)).toEqual(f.result);
- done();
+ import('../paramsValidator').then(({ getFirmwareRange }) => {
+ // added new capability
+ // @ts-expect-error
+ expect(getFirmwareRange(...f.params)).toEqual(f.result);
+ done();
+ });
});
});
});
diff --git a/packages/connect/src/api/common/paramsValidator.ts b/packages/connect/src/api/common/paramsValidator.ts
index 7af8bd6978f..b23a0bc0882 100644
--- a/packages/connect/src/api/common/paramsValidator.ts
+++ b/packages/connect/src/api/common/paramsValidator.ts
@@ -42,7 +42,7 @@ export function validateParams>(params: P, schema:
validateParams(p, [{ name: field.name, type: t }]);
return count + 1;
- } catch (e) {
+ } catch {
return count;
}
}, 0);
@@ -166,7 +166,9 @@ export const getFirmwareRange = (
// 0 may be confusing. means: no-support for "min" and unlimited support for "max"
if (rule.min) {
models.forEach(model => {
- const modelMin = rule.min[model];
+ const modelMin = (rule.min as Record)[
+ model
+ ];
if (modelMin) {
if (
modelMin === '0' ||
diff --git a/packages/connect/src/api/composeTransaction.ts b/packages/connect/src/api/composeTransaction.ts
index f1b7ef42604..f72749c222d 100644
--- a/packages/connect/src/api/composeTransaction.ts
+++ b/packages/connect/src/api/composeTransaction.ts
@@ -1,6 +1,8 @@
// origin: https://github.com/trezor/connect/blob/develop/src/js/core/methods/ComposeTransaction.js
import { BigNumber } from '@trezor/utils/src/bigNumber';
+import type { ComposeOutput, TransactionInputOutputSortingStrategy } from '@trezor/utxo-lib';
+
import { AbstractMethod } from '../core/AbstractMethod';
import { ERRORS } from '../constants';
import { UI, createUiMessage } from '../events';
@@ -25,7 +27,6 @@ import {
verifyTx,
parseTransactionHexes,
} from './bitcoin';
-import type { ComposeOutput } from '@trezor/utxo-lib';
import type { BitcoinNetworkInfo, DiscoveryAccount, AccountUtxo } from '../types';
import type {
SignedTransaction,
@@ -34,6 +35,7 @@ import type {
PrecomposedResult,
} from '../types/api/composeTransaction';
import type { RefTransaction } from '../types/api/bitcoin';
+import { DEFAULT_SORTING_STRATEGY } from '../constants/utxo';
type Params = {
outputs: ComposeOutput[];
@@ -45,9 +47,20 @@ type Params = {
baseFee?: PrecomposeParams['baseFee'];
floorBaseFee?: PrecomposeParams['floorBaseFee'];
sequence?: PrecomposeParams['sequence'];
- skipPermutation?: PrecomposeParams['skipPermutation'];
total: BigNumber;
-};
+ sortingStrategy: PrecomposeParams['sortingStrategy'];
+} & (
+ | {
+ /** @deprecated: use sortingStrategy=none instead */
+ skipPermutation?: PrecomposeParams['skipPermutation'];
+ sortingStrategy?: undefined;
+ }
+ | {
+ /** @deprecated: use sortingStrategy=none instead */
+ skipPermutation?: undefined;
+ sortingStrategy?: TransactionInputOutputSortingStrategy;
+ }
+);
export default class ComposeTransaction extends AbstractMethod<'composeTransaction', Params> {
discovery?: Discovery;
@@ -68,6 +81,7 @@ export default class ComposeTransaction extends AbstractMethod<'composeTransacti
{ name: 'floorBaseFee', type: 'boolean' },
{ name: 'sequence', type: 'number' },
{ name: 'skipPermutation', type: 'boolean' },
+ { name: 'sortingStrategy', type: 'string' },
]);
const coinInfo = getBitcoinNetwork(payload.coin);
@@ -115,7 +129,7 @@ export default class ComposeTransaction extends AbstractMethod<'composeTransacti
baseFee: payload.baseFee,
floorBaseFee: payload.floorBaseFee,
sequence: payload.sequence,
- skipPermutation: payload.skipPermutation,
+ sortingStrategy: payload.skipPermutation === true ? 'none' : payload.sortingStrategy,
push: typeof payload.push === 'boolean' ? payload.push : false,
total,
};
@@ -143,7 +157,7 @@ export default class ComposeTransaction extends AbstractMethod<'composeTransacti
account: PrecomposeParams['account'],
feeLevels: PrecomposeParams['feeLevels'],
): Promise {
- const { coinInfo, outputs, baseFee, skipPermutation } = this.params;
+ const { coinInfo, outputs, baseFee, sortingStrategy } = this.params;
const address_n = pathUtils.validatePath(account.path);
const composer = new TransactionComposer({
account: {
@@ -157,7 +171,7 @@ export default class ComposeTransaction extends AbstractMethod<'composeTransacti
coinInfo,
outputs,
baseFee,
- skipPermutation,
+ sortingStrategy: sortingStrategy ?? DEFAULT_SORTING_STRATEGY,
});
// This is mandatory, @trezor/utxo-lib/compose expects current block height
@@ -301,7 +315,7 @@ export default class ComposeTransaction extends AbstractMethod<'composeTransacti
}
async selectFee(account: DiscoveryAccount, utxos: AccountUtxo[]) {
- const { coinInfo, outputs } = this.params;
+ const { coinInfo, outputs, sortingStrategy, skipPermutation } = this.params;
// get backend instance (it should be initialized before)
const blockchain = await this.getBlockchain();
@@ -310,6 +324,8 @@ export default class ComposeTransaction extends AbstractMethod<'composeTransacti
utxos,
coinInfo,
outputs,
+ sortingStrategy:
+ skipPermutation === true ? 'none' : sortingStrategy ?? DEFAULT_SORTING_STRATEGY,
});
await composer.init(blockchain);
diff --git a/packages/connect/src/api/eos/api/eosGetPublicKey.ts b/packages/connect/src/api/eos/api/eosGetPublicKey.ts
index 95196493b53..e4a61d2e140 100644
--- a/packages/connect/src/api/eos/api/eosGetPublicKey.ts
+++ b/packages/connect/src/api/eos/api/eosGetPublicKey.ts
@@ -1,12 +1,13 @@
// origin: https://github.com/trezor/connect/blob/develop/src/js/core/methods/EosGetPublicKey.js
+import { Assert } from '@trezor/schema-utils';
+
import { AbstractMethod, MethodReturnType } from '../../../core/AbstractMethod';
import { getFirmwareRange } from '../../common/paramsValidator';
import { getMiscNetwork } from '../../../data/coinInfo';
import { validatePath, fromHardened, getSerializedPath } from '../../../utils/pathUtils';
import { UI, createUiMessage } from '../../../events';
import type { PROTO } from '../../../constants';
-import { Assert } from '@trezor/schema-utils';
import { Bundle, GetPublicKey as GetPublicKeySchema } from '../../../types';
export default class EosGetPublicKey extends AbstractMethod<
@@ -17,6 +18,7 @@ export default class EosGetPublicKey extends AbstractMethod<
init() {
this.requiredPermissions = ['read'];
+ this.requiredDeviceCapabilities = ['Capability_EOS'];
this.firmwareRange = getFirmwareRange(this.name, getMiscNetwork('EOS'), this.firmwareRange);
// create a bundle with only one batch if bundle doesn't exists
diff --git a/packages/connect/src/api/eos/api/eosSignTransaction.ts b/packages/connect/src/api/eos/api/eosSignTransaction.ts
index 32b4581608f..67d3653764d 100644
--- a/packages/connect/src/api/eos/api/eosSignTransaction.ts
+++ b/packages/connect/src/api/eos/api/eosSignTransaction.ts
@@ -1,12 +1,13 @@
// origin: https://github.com/trezor/connect/blob/develop/src/js/core/methods/EosSignTransaction.js
+import { AssertWeak } from '@trezor/schema-utils';
+
import { AbstractMethod } from '../../../core/AbstractMethod';
import { getFirmwareRange } from '../../common/paramsValidator';
import { getMiscNetwork } from '../../../data/coinInfo';
import { validatePath } from '../../../utils/pathUtils';
import * as helper from '../eosSignTx';
import type { PROTO } from '../../../constants';
-import { AssertWeak } from '@trezor/schema-utils';
import { EosSignTransaction as EosSignTransactionSchema } from '../../../types/api/eos';
type Params = {
@@ -20,6 +21,7 @@ type Params = {
export default class EosSignTransaction extends AbstractMethod<'eosSignTransaction', Params> {
init() {
this.requiredPermissions = ['read', 'write'];
+ this.requiredDeviceCapabilities = ['Capability_EOS'];
this.firmwareRange = getFirmwareRange(this.name, getMiscNetwork('EOS'), this.firmwareRange);
const { payload } = this;
diff --git a/packages/connect/src/api/ethereum/__fixtures__/ethereumSignTx.ts b/packages/connect/src/api/ethereum/__fixtures__/ethereumSignTx.ts
index dea0ca0011b..b8e26456300 100644
--- a/packages/connect/src/api/ethereum/__fixtures__/ethereumSignTx.ts
+++ b/packages/connect/src/api/ethereum/__fixtures__/ethereumSignTx.ts
@@ -1,12 +1,21 @@
-export const serializeEthereumTx = [
+import { LegacyTxData } from '@ethereumjs/tx';
+
+interface EthereumTxFixture {
+ description: string;
+ chainId: number;
+ tx: LegacyTxData; // + TODO: FeeMarketEIP1559TxData
+ result: string;
+}
+
+export const serializeEthereumTx: EthereumTxFixture[] = [
{
// https://eth1.trezor.io/tx/0xf6652a681b4474132b8b96512eb0bd5311f5ed8414af59e715c9738a3b3673f3
- description: 'ETH regular',
+ description: 'Legacy tx - ETH regular',
+ chainId: 1,
tx: {
// data sent to TrezorConnect.ethereumSignTransaction
to: '0x4dC573D5DB497C0bF0674599E81c7dB91151D4e6',
value: '0x3905f13a8f0e',
- chainId: 1,
nonce: '0x12',
gasLimit: '0x5208',
gasPrice: '0x104c533c00',
@@ -20,11 +29,11 @@ export const serializeEthereumTx = [
},
{
// https://eth1.trezor.io/tx/0xdcaf3eba690a3cdbad8c2926a8f5a95cd20003c5ba2aace91d8c5fe8048e395b
- description: 'Eth with ERC20',
+ description: 'Legacy tx - ETH with ERC20',
+ chainId: 1,
tx: {
to: '0xa74476443119A942dE498590Fe1f2454d7D4aC0d',
value: '0x0',
- chainId: 1,
nonce: '0xb',
gasLimit: '0x30d40',
gasPrice: '0x12a05f200',
@@ -37,11 +46,11 @@ export const serializeEthereumTx = [
},
{
// https://etc1.trezor.io/tx/0xebd7ef20c4358a6fdb09a951d6e77b8e88b37ac0f7a8d4e3b68f1666bf4c1d1a
- description: 'ETC regular',
+ description: 'Legacy tx - ETC regular',
+ chainId: 61,
tx: {
to: '0xABE894C18832edbe9B7926D729FA950673faD1EC',
value: '0x56c212a8e4628',
- chainId: 61,
nonce: '0x0',
gasLimit: '0x5208',
gasPrice: '0x5409c6a7b',
@@ -53,3 +62,5 @@ export const serializeEthereumTx = [
result: '0xebd7ef20c4358a6fdb09a951d6e77b8e88b37ac0f7a8d4e3b68f1666bf4c1d1a',
},
];
+
+// TODO: add EIP1559 tx type
diff --git a/packages/connect/src/api/ethereum/__fixtures__/ethereumSignTypedData.ts b/packages/connect/src/api/ethereum/__fixtures__/ethereumSignTypedData.ts
index d28b3e5d9ca..caf948a62be 100644
--- a/packages/connect/src/api/ethereum/__fixtures__/ethereumSignTypedData.ts
+++ b/packages/connect/src/api/ethereum/__fixtures__/ethereumSignTypedData.ts
@@ -1,5 +1,5 @@
-/* eslint-disable @typescript-eslint/prefer-ts-expect-error */
import { BigNumber } from '@trezor/utils/src/bigNumber';
+
import { TrezorError } from '../../../constants/errors';
import { PROTO } from '../../../constants';
@@ -254,7 +254,6 @@ export const encodeData = [
input: {
typeName: 'int256',
// `1n << 254n` instead of `2n ** 254n` since Babel replaces ** with Math.pow()
- // @ts-ignore, BigInt literals are not available when targeting lower than ES2020
data: -(1n << 254n) + 1n,
},
// Python (-(2 ** 254) + 1).to_bytes(32, "big", signed=True).hex()
@@ -264,7 +263,6 @@ export const encodeData = [
description: `should encode int from string`,
input: {
typeName: 'int256',
- // @ts-ignore, BigInt literals are not available when targeting lower than ES2020
data: `${-(1n << 254n) + 1n}`,
},
// Python (-(2 ** 254) + 1).to_bytes(32, "big", signed=True).hex()
diff --git a/packages/connect/src/api/ethereum/__tests__/ethereumSignTx.test.ts b/packages/connect/src/api/ethereum/__tests__/ethereumSignTx.test.ts
index 1556d0358ab..b218957b39c 100644
--- a/packages/connect/src/api/ethereum/__tests__/ethereumSignTx.test.ts
+++ b/packages/connect/src/api/ethereum/__tests__/ethereumSignTx.test.ts
@@ -2,22 +2,22 @@ import { TransactionFactory } from '@ethereumjs/tx';
import { keccak256, toHex } from 'web3-utils';
import { serializeEthereumTx } from '../ethereumSignTx';
-
import * as fixtures from '../__fixtures__/ethereumSignTx';
describe('helpers/ethereumSignTx', () => {
describe('serializeEthereumTx', () => {
fixtures.serializeEthereumTx.forEach(f => {
it(f.description, () => {
- // verify hash using 2 different tools
- if (f.tx.chainId !== 61) {
- // ETC is not supported
+ // ETC is not supported
+ if (f.chainId !== 61) {
const tx = TransactionFactory.fromTxData(f.tx);
const hash1 = Buffer.from(tx.hash()).toString('hex');
expect(`0x${hash1}`).toEqual(f.result);
}
- const serialized = serializeEthereumTx({ ...f.tx, type: 0 }, f.tx.chainId);
- const hash2 = toHex(keccak256(Buffer.from(serialized.slice(2), 'hex')));
+ const serialized = serializeEthereumTx({ ...f.tx, type: 0 }, f.chainId);
+ const hash2 = toHex(
+ keccak256(Uint8Array.from(Buffer.from(serialized.slice(2), 'hex'))),
+ );
expect(hash2).toEqual(f.result);
});
});
diff --git a/packages/connect/src/api/ethereum/__tests__/ethereumSignTypedData.test.ts b/packages/connect/src/api/ethereum/__tests__/ethereumSignTypedData.test.ts
index 1b5d88f24ea..20e59de5575 100644
--- a/packages/connect/src/api/ethereum/__tests__/ethereumSignTypedData.test.ts
+++ b/packages/connect/src/api/ethereum/__tests__/ethereumSignTypedData.test.ts
@@ -6,7 +6,7 @@ describe('helpers/ethereumSignTypeData', () => {
fixtures.parseArrayType.forEach(f => {
it(`${f.description} - ${f.input}`, () => {
if (f.error) {
- expect(() => parseArrayType(f.input)).toThrowError(...f.error);
+ expect(() => parseArrayType(f.input)).toThrow(...f.error);
} else {
expect(parseArrayType(f.input)).toEqual(f.output);
}
@@ -19,7 +19,7 @@ describe('helpers/ethereumSignTypeData', () => {
const { typeName, types } = f.input;
it(`${f.description} - ${typeName}`, () => {
if (f.error) {
- expect(() => getFieldType(typeName, types as any)).toThrowError(...f.error);
+ expect(() => getFieldType(typeName, types as any)).toThrow(...f.error);
} else {
expect(getFieldType(typeName, types as any)).toEqual(f.output);
}
@@ -32,7 +32,7 @@ describe('helpers/ethereumSignTypeData', () => {
const { typeName, data } = f.input;
it(`${f.description}`, () => {
if (f.error) {
- expect(() => encodeData(typeName, data)).toThrowError(...f.error);
+ expect(() => encodeData(typeName, data)).toThrow(...f.error);
} else {
expect(encodeData(typeName, data)).toEqual(f.output);
}
diff --git a/packages/connect/src/api/ethereum/api/ethereumGetAddress.ts b/packages/connect/src/api/ethereum/api/ethereumGetAddress.ts
index 9fc0bc46144..03c46401a43 100644
--- a/packages/connect/src/api/ethereum/api/ethereumGetAddress.ts
+++ b/packages/connect/src/api/ethereum/api/ethereumGetAddress.ts
@@ -1,5 +1,7 @@
// origin: https://github.com/trezor/connect/blob/develop/src/js/core/methods/EthereumGetAddress.js
+import { Assert } from '@trezor/schema-utils';
+
import { AbstractMethod, MethodReturnType } from '../../../core/AbstractMethod';
import { getFirmwareRange } from '../../common/paramsValidator';
import { validatePath, getSerializedPath, getSlip44ByPath } from '../../../utils/pathUtils';
@@ -14,7 +16,6 @@ import {
decodeEthereumDefinition,
ethereumNetworkInfoFromDefinition,
} from '../ethereumDefinitions';
-import { Assert } from '@trezor/schema-utils';
import { Bundle } from '../../../types';
import { GetAddress as GetAddressSchema } from '../../../types/api/getAddress';
@@ -31,6 +32,7 @@ export default class EthereumGetAddress extends AbstractMethod<'ethereumGetAddre
init() {
this.noBackupConfirmationMode = 'always';
this.requiredPermissions = ['read'];
+ this.requiredDeviceCapabilities = ['Capability_Ethereum'];
// create a bundle with only one batch if bundle doesn't exists
this.hasBundle = !!this.payload.bundle;
diff --git a/packages/connect/src/api/ethereum/api/ethereumGetPublicKey.ts b/packages/connect/src/api/ethereum/api/ethereumGetPublicKey.ts
index c087c861524..f0f7d919bf8 100644
--- a/packages/connect/src/api/ethereum/api/ethereumGetPublicKey.ts
+++ b/packages/connect/src/api/ethereum/api/ethereumGetPublicKey.ts
@@ -1,5 +1,7 @@
// origin: https://github.com/trezor/connect/blob/develop/src/js/core/methods/EthereumGetPublicKey.js
+import { Assert } from '@trezor/schema-utils';
+
import { AbstractMethod, MethodReturnType } from '../../../core/AbstractMethod';
import { getFirmwareRange } from '../../common/paramsValidator';
import { validatePath } from '../../../utils/pathUtils';
@@ -8,7 +10,6 @@ import { getEthereumNetwork, getUniqueNetworks } from '../../../data/coinInfo';
import { UI, createUiMessage } from '../../../events';
import type { PROTO } from '../../../constants';
import type { EthereumNetworkInfo } from '../../../types';
-import { Assert } from '@trezor/schema-utils';
import { Bundle, GetPublicKey as GetPublicKeySchema } from '../../../types';
type Params = PROTO.EthereumGetPublicKey & {
@@ -20,6 +21,7 @@ export default class EthereumGetPublicKey extends AbstractMethod<'ethereumGetPub
init() {
this.requiredPermissions = ['read'];
+ this.requiredDeviceCapabilities = ['Capability_Ethereum'];
// create a bundle with only one batch if bundle doesn't exists
this.hasBundle = !!this.payload.bundle;
diff --git a/packages/connect/src/api/ethereum/api/ethereumSignMessage.ts b/packages/connect/src/api/ethereum/api/ethereumSignMessage.ts
index c1cf32ecfea..2b5d9a23437 100644
--- a/packages/connect/src/api/ethereum/api/ethereumSignMessage.ts
+++ b/packages/connect/src/api/ethereum/api/ethereumSignMessage.ts
@@ -1,5 +1,8 @@
// origin: https://github.com/trezor/connect/blob/develop/src/js/core/methods/EthereumSignMessage.js
+import { MessagesSchema } from '@trezor/protobuf';
+import { Assert } from '@trezor/schema-utils';
+
import { AbstractMethod } from '../../../core/AbstractMethod';
import { getFirmwareRange } from '../../common/paramsValidator';
import { getSlip44ByPath, validatePath } from '../../../utils/pathUtils';
@@ -12,8 +15,6 @@ import {
EthereumNetworkInfo,
EthereumSignMessage as EthereumSignMessageSchema,
} from '../../../types';
-import { MessagesSchema } from '@trezor/protobuf';
-import { Assert } from '@trezor/schema-utils';
type Params = PROTO.EthereumSignMessage & {
network?: EthereumNetworkInfo;
@@ -23,6 +24,7 @@ type Params = PROTO.EthereumSignMessage & {
export default class EthereumSignMessage extends AbstractMethod<'ethereumSignMessage', Params> {
init() {
this.requiredPermissions = ['read', 'write'];
+ this.requiredDeviceCapabilities = ['Capability_Ethereum'];
const { payload } = this;
diff --git a/packages/connect/src/api/ethereum/api/ethereumSignTransaction.ts b/packages/connect/src/api/ethereum/api/ethereumSignTransaction.ts
index 2d2f4a26d24..611182bd16e 100644
--- a/packages/connect/src/api/ethereum/api/ethereumSignTransaction.ts
+++ b/packages/connect/src/api/ethereum/api/ethereumSignTransaction.ts
@@ -1,5 +1,10 @@
// origin: https://github.com/trezor/connect/blob/develop/src/js/core/methods/EthereumSignTransaction.js
+import { FeeMarketEIP1559TxData, LegacyTxData } from '@ethereumjs/tx';
+
+import { MessagesSchema } from '@trezor/protobuf';
+import { Assert } from '@trezor/schema-utils';
+
import { AbstractMethod } from '../../../core/AbstractMethod';
import { getFirmwareRange } from '../../common/paramsValidator';
import { getSlip44ByPath, validatePath } from '../../../utils/pathUtils';
@@ -17,8 +22,6 @@ import {
EthereumNetworkInfo,
EthereumSignTransaction as EthereumSignTransactionSchema,
} from '../../../types';
-import { MessagesSchema } from '@trezor/protobuf';
-import { Assert } from '@trezor/schema-utils';
type Params = {
path: number[];
@@ -52,6 +55,7 @@ export default class EthereumSignTransaction extends AbstractMethod<
> {
init() {
this.requiredPermissions = ['read', 'write'];
+ this.requiredDeviceCapabilities = ['Capability_Ethereum'];
const { payload } = this;
// validate incoming parameters
@@ -118,46 +122,49 @@ export default class EthereumSignTransaction extends AbstractMethod<
async run() {
const { type, tx, definitions, chunkify } = this.params;
- const signature =
- type === 'eip1559'
- ? await helper.ethereumSignTxEIP1559(
- this.device.getCommands().typedCall.bind(this.device.getCommands()),
- this.params.path,
- tx.to,
- tx.value,
- tx.gasLimit,
- tx.maxFeePerGas,
- tx.maxPriorityFeePerGas,
- tx.nonce,
- tx.chainId,
- chunkify,
- tx.data,
- tx.accessList,
- definitions,
- )
- : await helper.ethereumSignTx(
- this.device.getCommands().typedCall.bind(this.device.getCommands()),
- this.params.path,
- tx.to,
- tx.value,
- tx.gasLimit,
- tx.gasPrice,
- tx.nonce,
- tx.chainId,
- chunkify,
- tx.data,
- tx.txType,
- definitions,
- );
-
- const serializedTx = helper.serializeEthereumTx(
- {
- ...tx,
- ...signature,
- type: type === 'legacy' ? 0 : 2,
- },
- tx.chainId,
- );
+ const isLegacy = type === 'legacy';
+
+ const signature = isLegacy
+ ? await helper.ethereumSignTx(
+ this.device.getCommands().typedCall.bind(this.device.getCommands()),
+ this.params.path,
+ tx.to,
+ tx.value,
+ tx.gasLimit,
+ tx.gasPrice,
+ tx.nonce,
+ tx.chainId,
+ chunkify,
+ tx.data,
+ tx.txType,
+ definitions,
+ )
+ : await helper.ethereumSignTxEIP1559(
+ this.device.getCommands().typedCall.bind(this.device.getCommands()),
+ this.params.path,
+ tx.to,
+ tx.value,
+ tx.gasLimit,
+ tx.maxFeePerGas,
+ tx.maxPriorityFeePerGas,
+ tx.nonce,
+ tx.chainId,
+ chunkify,
+ tx.data,
+ tx.accessList,
+ definitions,
+ );
+
+ const txData = {
+ ...tx,
+ ...signature,
+ type: isLegacy ? 0 : 2, // 0 for legacy, 2 for EIP-1559
+ gasPrice: isLegacy ? tx.gasPrice : null,
+ maxFeePerGas: isLegacy ? tx.maxFeePerGas : tx.maxPriorityFeePerGas,
+ maxPriorityFeePerGas: !isLegacy ? tx.maxPriorityFeePerGas : undefined,
+ } as LegacyTxData | FeeMarketEIP1559TxData;
+
+ const serializedTx = helper.serializeEthereumTx(txData, tx.chainId);
return { ...signature, serializedTx };
}
diff --git a/packages/connect/src/api/ethereum/api/ethereumSignTypedData.ts b/packages/connect/src/api/ethereum/api/ethereumSignTypedData.ts
index 1fdfb042a7d..ab27f21c2e2 100644
--- a/packages/connect/src/api/ethereum/api/ethereumSignTypedData.ts
+++ b/packages/connect/src/api/ethereum/api/ethereumSignTypedData.ts
@@ -1,5 +1,8 @@
// origin: https://github.com/trezor/connect/blob/develop/src/js/core/methods/EthereumSignTypedData.js
+import { MessagesSchema } from '@trezor/protobuf';
+import { Assert, Type } from '@trezor/schema-utils';
+
import { AbstractMethod } from '../../../core/AbstractMethod';
import { getFirmwareRange } from '../../common/paramsValidator';
import { getSlip44ByPath, validatePath } from '../../../utils/pathUtils';
@@ -15,8 +18,6 @@ import { getFieldType, parseArrayType, encodeData } from '../ethereumSignTypedDa
import { messageToHex } from '../../../utils/formatUtils';
import { getEthereumDefinitions } from '../ethereumDefinitions';
import { EthereumNetworkInfo, DeviceModelInternal } from '../../../types';
-import { MessagesSchema } from '@trezor/protobuf';
-import { Assert, Type } from '@trezor/schema-utils';
// This type is not inferred, because it internally uses types that are generic
type Params = (
@@ -42,6 +43,7 @@ const Params = Type.Intersect([
export default class EthereumSignTypedData extends AbstractMethod<'ethereumSignTypedData', Params> {
init() {
this.requiredPermissions = ['read', 'write'];
+ this.requiredDeviceCapabilities = ['Capability_Ethereum'];
const { payload } = this;
diff --git a/packages/connect/src/api/ethereum/api/ethereumVerifyMessage.ts b/packages/connect/src/api/ethereum/api/ethereumVerifyMessage.ts
index 9891a50f5ad..ba89fa88e77 100644
--- a/packages/connect/src/api/ethereum/api/ethereumVerifyMessage.ts
+++ b/packages/connect/src/api/ethereum/api/ethereumVerifyMessage.ts
@@ -1,10 +1,11 @@
// origin: https://github.com/trezor/connect/blob/develop/src/js/core/methods/EthereumVerifyMessage.js
+import { Assert } from '@trezor/schema-utils';
+
import { AbstractMethod } from '../../../core/AbstractMethod';
import { getFirmwareRange } from '../../common/paramsValidator';
import { stripHexPrefix, messageToHex } from '../../../utils/formatUtils';
import type { PROTO } from '../../../constants';
-import { Assert } from '@trezor/schema-utils';
import { EthereumVerifyMessage as EthereumVerifyMessageSchema } from '../../../types';
export default class EthereumVerifyMessage extends AbstractMethod<
@@ -14,6 +15,7 @@ export default class EthereumVerifyMessage extends AbstractMethod<
init() {
this.requiredPermissions = ['read', 'write'];
this.firmwareRange = getFirmwareRange(this.name, null, this.firmwareRange);
+ this.requiredDeviceCapabilities = ['Capability_Ethereum'];
const { payload } = this;
diff --git a/packages/connect/src/api/ethereum/ethereumDefinitions.ts b/packages/connect/src/api/ethereum/ethereumDefinitions.ts
index 2240b875b1a..3a1aaf98501 100644
--- a/packages/connect/src/api/ethereum/ethereumDefinitions.ts
+++ b/packages/connect/src/api/ethereum/ethereumDefinitions.ts
@@ -1,12 +1,12 @@
import fetch from 'cross-fetch';
-import { MessagesSchema } from '@trezor/protobuf';
+import { MessagesSchema, parseConfigure, decode as decodeProtobuf } from '@trezor/protobuf';
import { trzd } from '@trezor/protocol';
-import { parseConfigure, decode as decodeProtobuf } from '@trezor/protobuf';
+import { Type, Static, Assert } from '@trezor/schema-utils';
+
import { DataManager } from '../../data/DataManager';
import { EthereumNetworkInfo } from '../../types';
import { ethereumNetworkInfoBase } from '../../data/coinInfo';
-import { Type, Static, Assert } from '@trezor/schema-utils';
interface GetEthereumDefinitions {
chainId?: number;
@@ -152,9 +152,10 @@ export const ethereumNetworkInfoFromDefinition = (
connect: true,
T1B1: '1.6.2',
T2T1: '2.0.7',
- T2B1: '2.6.1',
- T3B1: '2.8.1',
- T3T1: '2.7.1',
+ T2B1: '2.0.0',
+ T3B1: '2.0.0',
+ T3T1: '2.0.0',
+ T3W1: '2.0.0',
},
blockchainLink: undefined,
});
diff --git a/packages/connect/src/api/ethereum/ethereumSignTx.ts b/packages/connect/src/api/ethereum/ethereumSignTx.ts
index 82205b30d9b..a844bc1d308 100644
--- a/packages/connect/src/api/ethereum/ethereumSignTx.ts
+++ b/packages/connect/src/api/ethereum/ethereumSignTx.ts
@@ -4,6 +4,7 @@ import { Common, Chain, Hardfork } from '@ethereumjs/common';
import { FeeMarketEIP1559TxData, TransactionFactory, LegacyTxData } from '@ethereumjs/tx';
import { MessagesSchema } from '@trezor/protobuf';
+
import { PROTO, ERRORS } from '../../constants';
import type { TypedCall } from '../../device/DeviceCommands';
import type { EthereumAccessList } from '../../types/api/ethereum';
diff --git a/packages/connect/src/api/ethereum/ethereumSignTypedData.ts b/packages/connect/src/api/ethereum/ethereumSignTypedData.ts
index de0979f0a62..b65dbaa1975 100644
--- a/packages/connect/src/api/ethereum/ethereumSignTypedData.ts
+++ b/packages/connect/src/api/ethereum/ethereumSignTypedData.ts
@@ -1,6 +1,7 @@
// origin: https://github.com/trezor/connect/blob/develop/src/js/core/methods/helpers/ethereumSignTypedData.js
import { BigNumber } from '@trezor/utils/src/bigNumber';
+
import { PROTO, ERRORS } from '../../constants';
import { messageToHex } from '../../utils/formatUtils';
import type { EthereumSignTypedDataTypes } from '../../types/api/ethereum';
diff --git a/packages/connect/src/api/firmware/__tests__/calculateFirmwareHash.test.ts b/packages/connect/src/api/firmware/__tests__/calculateFirmwareHash.test.ts
index ebc42d89079..4e1f9f53821 100644
--- a/packages/connect/src/api/firmware/__tests__/calculateFirmwareHash.test.ts
+++ b/packages/connect/src/api/firmware/__tests__/calculateFirmwareHash.test.ts
@@ -43,8 +43,6 @@ describe('firmware/calculateFirmwareHash', () => {
});
it('Firmware too big', () => {
- expect(() => calculateFirmwareHash(2, Buffer.alloc(100000000))).toThrowError(
- 'Firmware too big',
- );
+ expect(() => calculateFirmwareHash(2, Buffer.alloc(100000000))).toThrow('Firmware too big');
});
});
diff --git a/packages/connect/src/api/firmware/__tests__/verifyAuthenticityProof.test.ts b/packages/connect/src/api/firmware/__tests__/verifyAuthenticityProof.test.ts
index 6f4263a8922..fd3804e275d 100644
--- a/packages/connect/src/api/firmware/__tests__/verifyAuthenticityProof.test.ts
+++ b/packages/connect/src/api/firmware/__tests__/verifyAuthenticityProof.test.ts
@@ -28,6 +28,10 @@ const CONFIG = {
'041b36cc98d5e3d1a20677aaf26254ef3756f27c9d63080c93ad3e7d39d3ad23bf00497b924789bc8e3f87834994e16780ad4eae7e75db1f03835ca64363e980b4',
],
},
+ T3W1: {
+ rootPubKeys: ['you shall not pass'], // TODO T3W1
+ caPubKeys: ['you shall not pass'], // TODO T3W1
+ },
} as DeviceAuthenticityConfig;
describe('firmware/verifyAuthenticityProof', () => {
diff --git a/packages/connect/src/api/firmware/getBinary.ts b/packages/connect/src/api/firmware/getBinary.ts
index a6568610826..50264a7eb43 100644
--- a/packages/connect/src/api/firmware/getBinary.ts
+++ b/packages/connect/src/api/firmware/getBinary.ts
@@ -1,42 +1,23 @@
-import { versionUtils } from '@trezor/utils';
import { httpRequest } from '../../utils/assets';
-import { FirmwareRelease, IntermediaryVersion } from '../../types';
+import { FirmwareRelease } from '../../types';
interface GetBinaryProps {
baseUrl: string;
btcOnly?: boolean;
- version?: number[];
- intermediaryVersion?: IntermediaryVersion;
- releases: FirmwareRelease[];
+ release: FirmwareRelease;
}
-export const getBinary = ({
- releases,
- baseUrl,
- version,
- btcOnly,
- intermediaryVersion,
-}: GetBinaryProps) => {
- if (intermediaryVersion) {
- return httpRequest(
- `${baseUrl}/firmware/t1b1/trezor-t1b1-inter-v${intermediaryVersion}.bin`,
- 'binary',
- );
- }
-
- const releaseByFirmware = releases.find(
- r =>
- version &&
- versionUtils.isVersionArray(version) &&
- versionUtils.isEqual(r.version, version),
- );
-
- if (releaseByFirmware === undefined) {
- throw new Error('no firmware found for this device');
- }
-
- const fwUrl = releaseByFirmware[btcOnly ? 'url_bitcoinonly' : 'url'];
+export const getBinary = ({ baseUrl, btcOnly, release }: GetBinaryProps) => {
+ const fwUrl = release[btcOnly ? 'url_bitcoinonly' : 'url'];
const url = `${baseUrl}/${fwUrl}`;
return httpRequest(url, 'binary');
};
+
+export const getBinaryOptional = async (props: GetBinaryProps) => {
+ try {
+ return await getBinary(props);
+ } catch {
+ return null;
+ }
+};
diff --git a/packages/connect/src/api/firmware/getBinaryForFirmwareUpgrade.ts b/packages/connect/src/api/firmware/getBinaryForFirmwareUpgrade.ts
index 87751c891d2..1217761bf8d 100644
--- a/packages/connect/src/api/firmware/getBinaryForFirmwareUpgrade.ts
+++ b/packages/connect/src/api/firmware/getBinaryForFirmwareUpgrade.ts
@@ -3,13 +3,13 @@ import { versionUtils } from '@trezor/utils';
import { httpRequest } from '../../utils/assets';
import { isStrictFeatures } from '../../utils/firmwareUtils';
import { getInfo, GetInfoProps } from '../../data/firmwareInfo';
-import { IntermediaryVersion } from '../../types';
+import { IntermediaryVersion, VersionArray } from '../../types';
import { getBinary } from './getBinary';
interface GetBinaryForFirmwareUpgradeProps extends GetInfoProps {
baseUrl: string;
btcOnly?: boolean;
- version?: number[];
+ version?: VersionArray;
intermediaryVersion?: IntermediaryVersion;
}
@@ -19,12 +19,12 @@ interface GetBinaryForFirmwareUpgradeProps extends GetInfoProps {
* is safe.
*/
export const getBinaryForFirmwareUpgrade = ({
- features,
releases,
baseUrl,
version,
btcOnly,
intermediaryVersion,
+ features,
}: GetBinaryForFirmwareUpgradeProps) => {
if (!isStrictFeatures(features)) {
throw new Error('Features of unexpected shape provided');
@@ -63,5 +63,5 @@ export const getBinaryForFirmwareUpgrade = ({
);
}
- return getBinary({ releases, baseUrl, version, btcOnly, intermediaryVersion });
+ return getBinary({ release: releaseByFirmware, baseUrl, btcOnly });
};
diff --git a/packages/connect/src/api/firmware/index.ts b/packages/connect/src/api/firmware/index.ts
index 47130d817b2..fe8e1d9aab7 100644
--- a/packages/connect/src/api/firmware/index.ts
+++ b/packages/connect/src/api/firmware/index.ts
@@ -1,5 +1,5 @@
export { getBinaryForFirmwareUpgrade } from './getBinaryForFirmwareUpgrade';
-export { getBinary } from './getBinary';
+export { getBinary, getBinaryOptional } from './getBinary';
export { getLanguage } from '../../data/getLanguage';
export { shouldStripFwHeaders, stripFwHeaders } from './modifyFirmware';
export { uploadFirmware } from './uploadFirmware';
diff --git a/packages/connect/src/api/firmware/verifyAuthenticityProof.ts b/packages/connect/src/api/firmware/verifyAuthenticityProof.ts
index 33e28ea740e..f86cd6d2a4b 100644
--- a/packages/connect/src/api/firmware/verifyAuthenticityProof.ts
+++ b/packages/connect/src/api/firmware/verifyAuthenticityProof.ts
@@ -1,4 +1,5 @@
import * as crypto from 'crypto';
+
import { bufferUtils } from '@trezor/utils';
import { PROTO } from '../../constants';
diff --git a/packages/connect/src/api/firmware/x509certificate.ts b/packages/connect/src/api/firmware/x509certificate.ts
index 6f93bb86891..c01e48170e7 100644
--- a/packages/connect/src/api/firmware/x509certificate.ts
+++ b/packages/connect/src/api/firmware/x509certificate.ts
@@ -81,12 +81,9 @@ const derToAsn1 = (byteArray: Uint8Array): Asn1 => {
throw new Error('Unsupported length encoding');
}
- let length = getLength(); // As encoded, which may be special value 0
- let byteLength;
- let contents;
-
- byteLength = position + length;
- contents = byteArray.subarray(position, byteLength);
+ const length = getLength(); // As encoded, which may be special value 0
+ const byteLength = position + length;
+ const contents = byteArray.subarray(position, byteLength);
const raw = byteArray.subarray(0, byteLength); // May not be the whole input array
diff --git a/packages/connect/src/api/getAccountDescriptor.ts b/packages/connect/src/api/getAccountDescriptor.ts
index a78ba51ea53..1e5417a4916 100644
--- a/packages/connect/src/api/getAccountDescriptor.ts
+++ b/packages/connect/src/api/getAccountDescriptor.ts
@@ -1,3 +1,5 @@
+import { Assert } from '@trezor/schema-utils';
+
import { AbstractMethod, MethodReturnType, DEFAULT_FIRMWARE_RANGE } from '../core/AbstractMethod';
import { getFirmwareRange } from './common/paramsValidator';
import { validatePath, getSerializedPath } from '../utils/pathUtils';
@@ -10,7 +12,6 @@ import {
GetAccountDescriptorParams,
GetAccountDescriptorResponse,
} from '../types/api/getAccountDescriptor';
-import { Assert } from '@trezor/schema-utils';
type Request = GetAccountDescriptorParams & { address_n: number[]; coinInfo: CoinInfo };
diff --git a/packages/connect/src/api/getAddress.ts b/packages/connect/src/api/getAddress.ts
index d759a9ad7ac..bdf7140ecb3 100644
--- a/packages/connect/src/api/getAddress.ts
+++ b/packages/connect/src/api/getAddress.ts
@@ -1,5 +1,7 @@
// origin: https://github.com/trezor/connect/blob/develop/src/js/core/methods/GetAddress.js
+import { Assert } from '@trezor/schema-utils';
+
import { AbstractMethod, MethodReturnType } from '../core/AbstractMethod';
import { validateCoinPath, getFirmwareRange } from './common/paramsValidator';
import { validatePath, getLabel, getSerializedPath } from '../utils/pathUtils';
@@ -7,7 +9,6 @@ import { getBitcoinNetwork, fixCoinInfoNetwork, getUniqueNetworks } from '../dat
import { PROTO, ERRORS } from '../constants';
import { UI, createUiMessage } from '../events';
import { Bundle, type BitcoinNetworkInfo } from '../types';
-import { Assert } from '@trezor/schema-utils';
import { GetAddress as GetAddressSchema } from '../types/api/getAddress';
type Params = PROTO.GetAddress & {
diff --git a/packages/connect/src/api/getCoinInfo.ts b/packages/connect/src/api/getCoinInfo.ts
index a948e487062..2b32220bb92 100644
--- a/packages/connect/src/api/getCoinInfo.ts
+++ b/packages/connect/src/api/getCoinInfo.ts
@@ -1,10 +1,11 @@
// origin: https://github.com/trezor/connect/blob/develop/src/js/core/methods/GetCoinInfo.js
+import { Assert } from '@trezor/schema-utils';
+
import { AbstractMethod } from '../core/AbstractMethod';
import { ERRORS } from '../constants';
import { getCoinInfo } from '../data/coinInfo';
import { CoinObj, CoinInfo } from '../types';
-import { Assert } from '@trezor/schema-utils';
type Params = {
coinInfo: CoinInfo;
diff --git a/packages/connect/src/api/getFirmwareHash.ts b/packages/connect/src/api/getFirmwareHash.ts
index e44ef009d6f..933ba5c4384 100644
--- a/packages/connect/src/api/getFirmwareHash.ts
+++ b/packages/connect/src/api/getFirmwareHash.ts
@@ -1,8 +1,9 @@
+import { Assert } from '@trezor/schema-utils';
+
import { AbstractMethod } from '../core/AbstractMethod';
import { PROTO } from '../constants';
import { UI } from '../events';
import { getFirmwareRange } from './common/paramsValidator';
-import { Assert } from '@trezor/schema-utils';
export default class GetFirmwareHash extends AbstractMethod<
'getFirmwareHash',
diff --git a/packages/connect/src/api/getOwnershipId.ts b/packages/connect/src/api/getOwnershipId.ts
index f36d2615d1e..440fbea6423 100644
--- a/packages/connect/src/api/getOwnershipId.ts
+++ b/packages/connect/src/api/getOwnershipId.ts
@@ -1,10 +1,11 @@
+import { Assert } from '@trezor/schema-utils';
+
import { AbstractMethod, MethodReturnType } from '../core/AbstractMethod';
import { getFirmwareRange } from './common/paramsValidator';
import { validatePath, getScriptType, getSerializedPath } from '../utils/pathUtils';
import { getBitcoinNetwork } from '../data/coinInfo';
import { PROTO } from '../constants';
import { UI, createUiMessage } from '../events';
-import { Assert } from '@trezor/schema-utils';
import { Bundle } from '../exports';
import { GetOwnershipId as GetOwnershipIdSchema } from '../types/api/getOwnershipId';
diff --git a/packages/connect/src/api/getOwnershipProof.ts b/packages/connect/src/api/getOwnershipProof.ts
index 6e923cb69f5..eb8c4c7a195 100644
--- a/packages/connect/src/api/getOwnershipProof.ts
+++ b/packages/connect/src/api/getOwnershipProof.ts
@@ -1,10 +1,11 @@
+import { Assert } from '@trezor/schema-utils';
+
import { AbstractMethod, MethodReturnType } from '../core/AbstractMethod';
import { getFirmwareRange } from './common/paramsValidator';
import { validatePath, getScriptType, getSerializedPath } from '../utils/pathUtils';
import { getBitcoinNetwork } from '../data/coinInfo';
import { PROTO } from '../constants';
import { UI, createUiMessage } from '../events';
-import { Assert } from '@trezor/schema-utils';
import { Bundle } from '../exports';
import { GetOwnershipProof as GetOwnershipProofSchema } from '../types/api/getOwnershipProof';
diff --git a/packages/connect/src/api/getPublicKey.ts b/packages/connect/src/api/getPublicKey.ts
index d04b2aed617..c21b4a0455e 100644
--- a/packages/connect/src/api/getPublicKey.ts
+++ b/packages/connect/src/api/getPublicKey.ts
@@ -1,5 +1,7 @@
// origin: https://github.com/trezor/connect/blob/develop/src/js/core/methods/GetPublicKey.js
+import { Assert } from '@trezor/schema-utils';
+
import { AbstractMethod, MethodReturnType } from '../core/AbstractMethod';
import { validateCoinPath, getFirmwareRange } from './common/paramsValidator';
import { validatePath } from '../utils/pathUtils';
@@ -8,7 +10,6 @@ import { getBitcoinNetwork } from '../data/coinInfo';
import { getPublicKeyLabel } from '../utils/accountUtils';
import type { BitcoinNetworkInfo } from '../types';
import type { PROTO } from '../constants';
-import { Assert } from '@trezor/schema-utils';
import { Bundle } from '../types';
import { GetPublicKey as GetPublicKeySchema } from '../types/api/getPublicKey';
diff --git a/packages/connect/src/api/index.ts b/packages/connect/src/api/index.ts
index faa7c69d647..561e4bcde8f 100644
--- a/packages/connect/src/api/index.ts
+++ b/packages/connect/src/api/index.ts
@@ -9,6 +9,7 @@ export { default as blockchainDisconnect } from './blockchainDisconnect';
export { default as blockchainEstimateFee } from './blockchainEstimateFee';
export { default as blockchainGetAccountBalanceHistory } from './blockchainGetAccountBalanceHistory';
export { default as blockchainGetCurrentFiatRates } from './blockchainGetCurrentFiatRates';
+export { default as blockchainEvmRpcCall } from './blockchainEvmRpcCall';
export { default as blockchainGetFiatRatesForTimestamps } from './blockchainGetFiatRatesForTimestamps';
export { default as blockchainGetTransactions } from './blockchainGetTransactions';
export { default as blockchainSetCustomBackend } from './blockchainSetCustomBackend';
@@ -40,12 +41,12 @@ export { default as getSettings } from './getSettings';
// export { default as off } from './off';
// export { default as on } from './on';
export { default as pushTransaction } from './pushTransaction';
-export { default as rebootToBootloader } from './rebootToBootloader';
export { default as recoveryDevice } from './recoveryDevice';
// export { default as removeAllListeners } from './composeTransaction';
// export { default as renderWebUSBButton } from './composeTransaction';
export { default as requestLogin } from './requestLogin';
export { default as resetDevice } from './resetDevice';
+export { default as loadDevice } from './loadDevice';
export { default as setBrightness } from './setBrightness';
export { default as setBusy } from './setBusy';
export { default as setProxy } from './setProxy';
@@ -55,4 +56,3 @@ export { default as unlockPath } from './unlockPath';
// export { default as uiResponse } from './uiResponse';
export { default as verifyMessage } from './verifyMessage';
export { default as wipeDevice } from './wipeDevice';
-export { default as checkFirmwareAuthenticity } from './checkFirmwareAuthenticity';
diff --git a/packages/connect/src/api/loadDevice.ts b/packages/connect/src/api/loadDevice.ts
new file mode 100644
index 00000000000..53827718a22
--- /dev/null
+++ b/packages/connect/src/api/loadDevice.ts
@@ -0,0 +1,49 @@
+import { Assert } from '@trezor/schema-utils';
+
+import { AbstractMethod } from '../core/AbstractMethod';
+import { UI } from '../events';
+import { getFirmwareRange } from './common/paramsValidator';
+import { PROTO } from '../constants';
+
+export default class LoadDevice extends AbstractMethod<'loadDevice', PROTO.LoadDevice> {
+ init() {
+ this.allowDeviceMode = [UI.INITIALIZE];
+ this.useDeviceState = false;
+ this.requiredPermissions = ['management'];
+ this.firmwareRange = getFirmwareRange(this.name, null, this.firmwareRange);
+
+ const { payload } = this;
+ // validate bundle type
+ Assert(PROTO.LoadDevice, payload);
+
+ this.params = {
+ mnemonics: payload.mnemonics,
+ pin: payload.pin,
+ passphrase_protection: payload.passphrase_protection,
+ language: payload.language,
+ label: payload.label,
+ skip_checksum: payload.skip_checksum,
+ u2f_counter: payload.u2f_counter,
+ needs_backup: payload.needs_backup,
+ no_backup: payload.no_backup,
+ };
+ }
+
+ get info() {
+ return 'Load seed and related internal settings.';
+ }
+
+ get confirmation() {
+ return {
+ view: 'device-management' as const,
+ label: 'Do you really you want to load device?',
+ };
+ }
+
+ async run() {
+ const cmd = this.device.getCommands();
+ const response = await cmd.typedCall('LoadDevice', 'Success', this.params);
+
+ return response.message;
+ }
+}
diff --git a/packages/connect/src/api/nem/api/nemGetAddress.ts b/packages/connect/src/api/nem/api/nemGetAddress.ts
index baef0a318ef..931aea0c0be 100644
--- a/packages/connect/src/api/nem/api/nemGetAddress.ts
+++ b/packages/connect/src/api/nem/api/nemGetAddress.ts
@@ -1,12 +1,13 @@
// origin: https://github.com/trezor/connect/blob/develop/src/js/core/methods/NEMGetAddress.js
+import { Assert } from '@trezor/schema-utils';
+
import { AbstractMethod, MethodReturnType } from '../../../core/AbstractMethod';
import { getFirmwareRange } from '../../common/paramsValidator';
import { getMiscNetwork } from '../../../data/coinInfo';
import { validatePath, fromHardened, getSerializedPath } from '../../../utils/pathUtils';
import { PROTO, ERRORS } from '../../../constants';
import { UI, createUiMessage } from '../../../events';
-import { Assert } from '@trezor/schema-utils';
import { Bundle } from '../../../types';
import { GetAddress as GetAddressSchema } from '../../../types/api/getAddress';
@@ -25,6 +26,7 @@ export default class NEMGetAddress extends AbstractMethod<'nemGetAddress', Param
init() {
this.noBackupConfirmationMode = 'always';
this.requiredPermissions = ['read'];
+ this.requiredDeviceCapabilities = ['Capability_NEM'];
this.firmwareRange = getFirmwareRange(this.name, getMiscNetwork('NEM'), this.firmwareRange);
// create a bundle with only one batch if bundle doesn't exists
diff --git a/packages/connect/src/api/nem/api/nemSignTransaction.ts b/packages/connect/src/api/nem/api/nemSignTransaction.ts
index 0d962218d7d..086371e2b02 100644
--- a/packages/connect/src/api/nem/api/nemSignTransaction.ts
+++ b/packages/connect/src/api/nem/api/nemSignTransaction.ts
@@ -1,12 +1,13 @@
// origin: https://github.com/trezor/connect/blob/develop/src/js/core/methods/NEMSignTransaction.js
+import { AssertWeak } from '@trezor/schema-utils';
+
import { AbstractMethod } from '../../../core/AbstractMethod';
import { getFirmwareRange } from '../../common/paramsValidator';
import { getMiscNetwork } from '../../../data/coinInfo';
import { validatePath } from '../../../utils/pathUtils';
import * as helper from '../nemSignTx';
import type { PROTO } from '../../../constants';
-import { AssertWeak } from '@trezor/schema-utils';
import { NEMSignTransaction as NEMSignTransactionSchema } from '../../../types/api/nem';
export default class NEMSignTransaction extends AbstractMethod<
@@ -15,6 +16,7 @@ export default class NEMSignTransaction extends AbstractMethod<
> {
init() {
this.requiredPermissions = ['read', 'write'];
+ this.requiredDeviceCapabilities = ['Capability_NEM'];
this.firmwareRange = getFirmwareRange(this.name, getMiscNetwork('NEM'), this.firmwareRange);
const { payload } = this;
diff --git a/packages/connect/src/api/pushTransaction.ts b/packages/connect/src/api/pushTransaction.ts
index 14369bcdf2d..277624e9b3d 100644
--- a/packages/connect/src/api/pushTransaction.ts
+++ b/packages/connect/src/api/pushTransaction.ts
@@ -1,11 +1,12 @@
// origin: https://github.com/trezor/connect/blob/develop/src/js/core/methods/PushTransaction.js
+import { Assert } from '@trezor/schema-utils';
+
import { AbstractMethod } from '../core/AbstractMethod';
import { getCoinInfo } from '../data/coinInfo';
import { ERRORS } from '../constants';
import { isBackendSupported, initBlockchain } from '../backend/BlockchainLink';
import type { CoinInfo } from '../types';
-import { Assert } from '@trezor/schema-utils';
import { PushTransaction as PushTransactionSchema } from '../types/api/pushTransaction';
type Params = {
diff --git a/packages/connect/src/api/rebootToBootloader.ts b/packages/connect/src/api/rebootToBootloader.ts
deleted file mode 100644
index e8036de919d..00000000000
--- a/packages/connect/src/api/rebootToBootloader.ts
+++ /dev/null
@@ -1,52 +0,0 @@
-// origin: https://github.com/trezor/connect/blob/develop/src/js/core/methods/RebootToBootloader.js
-
-import { AbstractMethod } from '../core/AbstractMethod';
-import { getFirmwareRange } from './common/paramsValidator';
-import { UI } from '../events';
-import { Assert } from '@trezor/schema-utils';
-import { PROTO } from '../constants';
-
-export default class RebootToBootloader extends AbstractMethod<
- 'rebootToBootloader',
- PROTO.RebootToBootloader
-> {
- init() {
- this.allowDeviceMode = [UI.INITIALIZE, UI.SEEDLESS];
- this.skipFinalReload = true;
- this.keepSession = false;
- this.requiredPermissions = ['management'];
- this.useDeviceState = false;
- this.firmwareRange = getFirmwareRange(this.name, null, this.firmwareRange);
-
- const { payload } = this;
- Assert(PROTO.RebootToBootloader, payload);
-
- this.params = {
- boot_command: payload.boot_command,
- firmware_header: payload.firmware_header,
- language_data_length: payload.language_data_length,
- };
- }
-
- get info() {
- return 'Reboot to bootloader';
- }
-
- get confirmation() {
- return {
- view: 'device-management' as const,
- customConfirmButton: {
- className: 'confirm',
- label: `Reboot`,
- },
- label: 'Are you sure you want to reboot to bootloader?',
- };
- }
-
- async run() {
- const cmd = this.device.getCommands();
- const response = await cmd.typedCall('RebootToBootloader', 'Success', this.params);
-
- return response.message;
- }
-}
diff --git a/packages/connect/src/api/recoveryDevice.ts b/packages/connect/src/api/recoveryDevice.ts
index 337557d7a70..559d7d9abe2 100644
--- a/packages/connect/src/api/recoveryDevice.ts
+++ b/packages/connect/src/api/recoveryDevice.ts
@@ -1,8 +1,9 @@
// origin: https://github.com/trezor/connect/blob/develop/src/js/core/methods/RecoveryDevice.js
+import { Assert } from '@trezor/schema-utils';
+
import { AbstractMethod } from '../core/AbstractMethod';
import { UI } from '../events';
-import { Assert } from '@trezor/schema-utils';
import type { PROTO } from '../constants';
import { RecoveryDevice as RecoveryDeviceSchema } from '../types/api/recoveryDevice';
diff --git a/packages/connect/src/api/requestLogin.ts b/packages/connect/src/api/requestLogin.ts
index b835cf6d1dd..0dbf2e8d719 100644
--- a/packages/connect/src/api/requestLogin.ts
+++ b/packages/connect/src/api/requestLogin.ts
@@ -1,5 +1,7 @@
// origin: https://github.com/trezor/connect/blob/develop/src/js/core/methods/RequestLogin.js
+import { Assert, Type } from '@trezor/schema-utils';
+
import { AbstractMethod } from '../core/AbstractMethod';
import { getFirmwareRange } from './common/paramsValidator';
import { ERRORS } from '../constants';
@@ -7,7 +9,6 @@ import { UI, createUiMessage } from '../events';
import { DataManager } from '../data/DataManager';
import type { ConnectSettings } from '../types';
import type { PROTO } from '../constants';
-import { Assert, Type } from '@trezor/schema-utils';
import { RequestLoginSchema } from '../types/api/requestLogin';
export default class RequestLogin extends AbstractMethod<'requestLogin', PROTO.SignIdentity> {
diff --git a/packages/connect/src/api/resetDevice.ts b/packages/connect/src/api/resetDevice.ts
index 84d16c8e073..d19f695c271 100644
--- a/packages/connect/src/api/resetDevice.ts
+++ b/packages/connect/src/api/resetDevice.ts
@@ -1,10 +1,11 @@
// origin: https://github.com/trezor/connect/blob/develop/src/js/core/methods/ResetDevice.js
+import { Assert } from '@trezor/schema-utils';
+
import { AbstractMethod } from '../core/AbstractMethod';
import { UI } from '../events';
import { getFirmwareRange } from './common/paramsValidator';
import { PROTO } from '../constants';
-import { Assert } from '@trezor/schema-utils';
export default class ResetDevice extends AbstractMethod<'resetDevice', PROTO.ResetDevice> {
init() {
@@ -18,7 +19,6 @@ export default class ResetDevice extends AbstractMethod<'resetDevice', PROTO.Res
Assert(PROTO.ResetDevice, payload);
this.params = {
- display_random: payload.display_random,
strength: payload.strength || 256,
passphrase_protection: payload.passphrase_protection,
pin_protection: payload.pin_protection,
diff --git a/packages/connect/src/api/ripple/api/rippleGetAddress.ts b/packages/connect/src/api/ripple/api/rippleGetAddress.ts
index a2e8b5bc5c2..d8ca192d78e 100644
--- a/packages/connect/src/api/ripple/api/rippleGetAddress.ts
+++ b/packages/connect/src/api/ripple/api/rippleGetAddress.ts
@@ -1,12 +1,13 @@
// origin: https://github.com/trezor/connect/blob/develop/src/js/core/methods/RippleGetAddress.js
+import { Assert } from '@trezor/schema-utils';
+
import { AbstractMethod, MethodReturnType } from '../../../core/AbstractMethod';
import { getFirmwareRange } from '../../common/paramsValidator';
import { getMiscNetwork } from '../../../data/coinInfo';
import { validatePath, fromHardened, getSerializedPath } from '../../../utils/pathUtils';
import { PROTO, ERRORS } from '../../../constants';
import { UI, createUiMessage } from '../../../events';
-import { Assert } from '@trezor/schema-utils';
import { Bundle } from '../../../types';
import { GetAddress as GetAddressSchema } from '../../../types/api/getAddress';
@@ -21,6 +22,7 @@ export default class RippleGetAddress extends AbstractMethod<'rippleGetAddress',
init() {
this.noBackupConfirmationMode = 'always';
this.requiredPermissions = ['read'];
+ this.requiredDeviceCapabilities = ['Capability_Ripple'];
this.firmwareRange = getFirmwareRange(
this.name,
getMiscNetwork('Ripple'),
diff --git a/packages/connect/src/api/ripple/api/rippleSignTransaction.ts b/packages/connect/src/api/ripple/api/rippleSignTransaction.ts
index 241f0475935..a53e64a3971 100644
--- a/packages/connect/src/api/ripple/api/rippleSignTransaction.ts
+++ b/packages/connect/src/api/ripple/api/rippleSignTransaction.ts
@@ -1,11 +1,12 @@
// origin: https://github.com/trezor/connect/blob/develop/src/js/core/methods/RippleSignTransaction.js
+import { AssertWeak } from '@trezor/schema-utils';
+
import { AbstractMethod } from '../../../core/AbstractMethod';
import { getFirmwareRange } from '../../common/paramsValidator';
import { getMiscNetwork } from '../../../data/coinInfo';
import { validatePath } from '../../../utils/pathUtils';
import type { PROTO } from '../../../constants';
-import { AssertWeak } from '@trezor/schema-utils';
import { RippleSignTransaction as RippleSignTransactionSchema } from '../../../types/api/ripple';
export default class RippleSignTransaction extends AbstractMethod<
@@ -14,6 +15,7 @@ export default class RippleSignTransaction extends AbstractMethod<
> {
init() {
this.requiredPermissions = ['read', 'write'];
+ this.requiredDeviceCapabilities = ['Capability_Ripple'];
this.firmwareRange = getFirmwareRange(
this.name,
getMiscNetwork('Ripple'),
diff --git a/packages/connect/src/api/setBrightness.ts b/packages/connect/src/api/setBrightness.ts
index 97a8fb89f71..ee0665c7eec 100644
--- a/packages/connect/src/api/setBrightness.ts
+++ b/packages/connect/src/api/setBrightness.ts
@@ -1,8 +1,9 @@
// origin: https://github.com/trezor/connect/blob/develop/src/js/core/methods/SetBrightness.js
+import { Assert } from '@trezor/schema-utils';
+
import { AbstractMethod } from '../core/AbstractMethod';
import { PROTO } from '../constants';
-import { Assert } from '@trezor/schema-utils';
export default class SetBrightness extends AbstractMethod<'setBrightness', PROTO.SetBrightness> {
init() {
diff --git a/packages/connect/src/api/setBusy.ts b/packages/connect/src/api/setBusy.ts
index 576aac81b1a..67940bbfedb 100644
--- a/packages/connect/src/api/setBusy.ts
+++ b/packages/connect/src/api/setBusy.ts
@@ -1,8 +1,9 @@
+import { Assert } from '@trezor/schema-utils';
+
import { AbstractMethod } from '../core/AbstractMethod';
import { DEVICE, createDeviceMessage } from '../events';
import { PROTO } from '../constants';
import { getFirmwareRange } from './common/paramsValidator';
-import { Assert } from '@trezor/schema-utils';
export default class SetBusy extends AbstractMethod<'setBusy', PROTO.SetBusy> {
init() {
diff --git a/packages/connect/src/api/signMessage.ts b/packages/connect/src/api/signMessage.ts
index 3bf9e74b80a..d8c9b2eefa7 100644
--- a/packages/connect/src/api/signMessage.ts
+++ b/packages/connect/src/api/signMessage.ts
@@ -1,5 +1,7 @@
// origin: https://github.com/trezor/connect/blob/develop/src/js/core/methods/SignMessage.js
+import { Assert } from '@trezor/schema-utils';
+
import { AbstractMethod } from '../core/AbstractMethod';
import { validateCoinPath, getFirmwareRange } from './common/paramsValidator';
import { validatePath, getLabel, getScriptType } from '../utils/pathUtils';
@@ -8,7 +10,6 @@ import { messageToHex } from '../utils/formatUtils';
import type { BitcoinNetworkInfo } from '../types';
import type { PROTO } from '../constants';
import { SignMessage as SignMessageSchema } from '../types';
-import { Assert } from '@trezor/schema-utils';
export default class SignMessage extends AbstractMethod<'signMessage', PROTO.SignMessage> {
init() {
diff --git a/packages/connect/src/api/solana/additionalInfo.ts b/packages/connect/src/api/solana/additionalInfo.ts
index c49490af41d..5a86efabeb9 100644
--- a/packages/connect/src/api/solana/additionalInfo.ts
+++ b/packages/connect/src/api/solana/additionalInfo.ts
@@ -1,4 +1,5 @@
import { Assert } from '@trezor/schema-utils';
+
import { SolanaTxAdditionalInfo } from '../../types/api/solana';
export const transformAdditionalInfo = (additionalInfo?: SolanaTxAdditionalInfo) => {
diff --git a/packages/connect/src/api/solana/api/solanaGetAddress.ts b/packages/connect/src/api/solana/api/solanaGetAddress.ts
index c874c5038b9..793954c9cca 100644
--- a/packages/connect/src/api/solana/api/solanaGetAddress.ts
+++ b/packages/connect/src/api/solana/api/solanaGetAddress.ts
@@ -1,10 +1,11 @@
+import { Assert } from '@trezor/schema-utils';
+
import { ERRORS, PROTO } from '../../../constants';
import { AbstractMethod, MethodReturnType } from '../../../core/AbstractMethod';
import { getFirmwareRange } from '../../common/paramsValidator';
import { getMiscNetwork } from '../../../data/coinInfo';
import { validatePath, fromHardened, getSerializedPath } from '../../../utils/pathUtils';
import { UI, createUiMessage } from '../../../events';
-import { Assert } from '@trezor/schema-utils';
import { Bundle } from '../../../types';
import { GetAddress as GetAddressSchema } from '../../../types/api/getAddress';
@@ -19,6 +20,7 @@ export default class SolanaGetAddress extends AbstractMethod<'solanaGetAddress',
init() {
this.noBackupConfirmationMode = 'always';
this.requiredPermissions = ['read'];
+ this.requiredDeviceCapabilities = ['Capability_Solana'];
this.firmwareRange = getFirmwareRange(
this.name,
getMiscNetwork('Solana'),
diff --git a/packages/connect/src/api/solana/api/solanaGetPublicKey.ts b/packages/connect/src/api/solana/api/solanaGetPublicKey.ts
index 411c38901b0..de1fcc753ec 100644
--- a/packages/connect/src/api/solana/api/solanaGetPublicKey.ts
+++ b/packages/connect/src/api/solana/api/solanaGetPublicKey.ts
@@ -1,10 +1,11 @@
+import { Assert } from '@trezor/schema-utils';
+
import { PROTO } from '../../../constants';
import { AbstractMethod, MethodReturnType } from '../../../core/AbstractMethod';
import { getFirmwareRange } from '../../common/paramsValidator';
import { getMiscNetwork } from '../../../data/coinInfo';
import { validatePath, fromHardened, getSerializedPath } from '../../../utils/pathUtils';
import { UI, createUiMessage } from '../../../events';
-import { Assert } from '@trezor/schema-utils';
import { Bundle, GetPublicKey as GetPublicKeySchema } from '../../../types';
export default class SolanaGetPublicKey extends AbstractMethod<
@@ -16,6 +17,7 @@ export default class SolanaGetPublicKey extends AbstractMethod<
init() {
this.noBackupConfirmationMode = 'always';
this.requiredPermissions = ['read'];
+ this.requiredDeviceCapabilities = ['Capability_Solana'];
this.firmwareRange = getFirmwareRange(
this.name,
getMiscNetwork('Solana'),
diff --git a/packages/connect/src/api/solana/api/solanaSignTransaction.ts b/packages/connect/src/api/solana/api/solanaSignTransaction.ts
index e17f8dacd26..26a64d3fb5f 100644
--- a/packages/connect/src/api/solana/api/solanaSignTransaction.ts
+++ b/packages/connect/src/api/solana/api/solanaSignTransaction.ts
@@ -1,10 +1,11 @@
+import { AssertWeak } from '@trezor/schema-utils';
+
import { PROTO } from '../../../constants';
import { AbstractMethod } from '../../../core/AbstractMethod';
import { getFirmwareRange } from '../../common/paramsValidator';
import { getMiscNetwork } from '../../../data/coinInfo';
import { validatePath } from '../../../utils/pathUtils';
import { transformAdditionalInfo } from '../additionalInfo';
-import { AssertWeak } from '@trezor/schema-utils';
import { SolanaSignTransaction as SolanaSignTransactionSchema } from '../../../types/api/solana';
export default class SolanaSignTransaction extends AbstractMethod<
@@ -13,6 +14,7 @@ export default class SolanaSignTransaction extends AbstractMethod<
> {
init() {
this.requiredPermissions = ['read', 'write'];
+ this.requiredDeviceCapabilities = ['Capability_Solana'];
this.firmwareRange = getFirmwareRange(
this.name,
getMiscNetwork('Solana'),
diff --git a/packages/connect/src/api/stellar/api/stellarGetAddress.ts b/packages/connect/src/api/stellar/api/stellarGetAddress.ts
index 064485f2488..4cf73a86675 100644
--- a/packages/connect/src/api/stellar/api/stellarGetAddress.ts
+++ b/packages/connect/src/api/stellar/api/stellarGetAddress.ts
@@ -1,12 +1,13 @@
// origin: https://github.com/trezor/connect/blob/develop/src/js/core/methods/StellarGetAddress.js
+import { Assert } from '@trezor/schema-utils';
+
import { AbstractMethod, MethodReturnType } from '../../../core/AbstractMethod';
import { getFirmwareRange } from '../../common/paramsValidator';
import { getMiscNetwork } from '../../../data/coinInfo';
import { validatePath, fromHardened, getSerializedPath } from '../../../utils/pathUtils';
import { PROTO, ERRORS } from '../../../constants';
import { UI, createUiMessage } from '../../../events';
-import { Assert } from '@trezor/schema-utils';
import { Bundle } from '../../../types';
import { GetAddress as GetAddressSchema } from '../../../types/api/getAddress';
@@ -21,6 +22,7 @@ export default class StellarGetAddress extends AbstractMethod<'stellarGetAddress
init() {
this.noBackupConfirmationMode = 'always';
this.requiredPermissions = ['read'];
+ this.requiredDeviceCapabilities = ['Capability_Stellar'];
this.firmwareRange = getFirmwareRange(
this.name,
getMiscNetwork('Stellar'),
diff --git a/packages/connect/src/api/stellar/api/stellarSignTransaction.ts b/packages/connect/src/api/stellar/api/stellarSignTransaction.ts
index 85264da1b82..1bd087a8347 100644
--- a/packages/connect/src/api/stellar/api/stellarSignTransaction.ts
+++ b/packages/connect/src/api/stellar/api/stellarSignTransaction.ts
@@ -1,5 +1,7 @@
// origin: https://github.com/trezor/connect/blob/develop/src/js/core/methods/StellarSignTransaction.js
+import { AssertWeak } from '@trezor/schema-utils';
+
import { AbstractMethod } from '../../../core/AbstractMethod';
import { getFirmwareRange } from '../../common/paramsValidator';
import { getMiscNetwork } from '../../../data/coinInfo';
@@ -10,7 +12,6 @@ import {
StellarTransaction,
StellarSignTransaction as StellarSignTransactionSchema,
} from '../../../types/api/stellar';
-import { AssertWeak } from '@trezor/schema-utils';
type Params = {
path: number[];
@@ -29,6 +30,7 @@ export default class StellarSignTransaction extends AbstractMethod<
> {
init() {
this.requiredPermissions = ['read', 'write'];
+ this.requiredDeviceCapabilities = ['Capability_Stellar'];
this.firmwareRange = getFirmwareRange(
this.name,
getMiscNetwork('Stellar'),
diff --git a/packages/connect/src/api/stellar/stellarSignTx.ts b/packages/connect/src/api/stellar/stellarSignTx.ts
index c7b8197437c..9cf84b881dc 100644
--- a/packages/connect/src/api/stellar/stellarSignTx.ts
+++ b/packages/connect/src/api/stellar/stellarSignTx.ts
@@ -1,5 +1,7 @@
// origin: https://github.com/trezor/connect/blob/develop/src/js/core/methods/helpers/stellarSignTx.js
+import { Assert } from '@trezor/schema-utils';
+
import { PROTO, ERRORS } from '../../constants';
import type { TypedCall } from '../../device/DeviceCommands';
import {
@@ -7,7 +9,6 @@ import {
StellarOperation,
StellarOperationMessage,
} from '../../types/api/stellar';
-import { Assert } from '@trezor/schema-utils';
const processTxRequest = async (
typedCall: TypedCall,
diff --git a/packages/connect/src/api/tezos/api/tezosGetAddress.ts b/packages/connect/src/api/tezos/api/tezosGetAddress.ts
index dc669fbb3b1..07862cfa696 100644
--- a/packages/connect/src/api/tezos/api/tezosGetAddress.ts
+++ b/packages/connect/src/api/tezos/api/tezosGetAddress.ts
@@ -1,12 +1,13 @@
// origin: https://github.com/trezor/connect/blob/develop/src/js/core/methods/TezosGetAddress.js
+import { Assert } from '@trezor/schema-utils';
+
import { AbstractMethod, MethodReturnType } from '../../../core/AbstractMethod';
import { getFirmwareRange } from '../../common/paramsValidator';
import { getMiscNetwork } from '../../../data/coinInfo';
import { validatePath, fromHardened, getSerializedPath } from '../../../utils/pathUtils';
import { PROTO, ERRORS } from '../../../constants';
import { UI, createUiMessage } from '../../../events';
-import { Assert } from '@trezor/schema-utils';
import { Bundle } from '../../../types';
import { GetAddress as GetAddressSchema } from '../../../types/api/getAddress';
@@ -21,6 +22,7 @@ export default class TezosGetAddress extends AbstractMethod<'tezosGetAddress', P
init() {
this.noBackupConfirmationMode = 'always';
this.requiredPermissions = ['read'];
+ this.requiredDeviceCapabilities = ['Capability_Tezos'];
this.firmwareRange = getFirmwareRange(
this.name,
getMiscNetwork('Tezos'),
diff --git a/packages/connect/src/api/tezos/api/tezosGetPublicKey.ts b/packages/connect/src/api/tezos/api/tezosGetPublicKey.ts
index c9a7400010c..beb8a5a1700 100644
--- a/packages/connect/src/api/tezos/api/tezosGetPublicKey.ts
+++ b/packages/connect/src/api/tezos/api/tezosGetPublicKey.ts
@@ -1,12 +1,13 @@
// origin: https://github.com/trezor/connect/blob/develop/src/js/core/methods/TezosGetPublicKey.js
+import { Assert } from '@trezor/schema-utils';
+
import { AbstractMethod, MethodReturnType } from '../../../core/AbstractMethod';
import { getFirmwareRange } from '../../common/paramsValidator';
import { getMiscNetwork } from '../../../data/coinInfo';
import { validatePath, fromHardened, getSerializedPath } from '../../../utils/pathUtils';
import { UI, createUiMessage } from '../../../events';
import type { PROTO } from '../../../constants';
-import { Assert } from '@trezor/schema-utils';
import { Bundle, GetPublicKey as GetPublicKeySchema } from '../../../types';
export default class TezosGetPublicKey extends AbstractMethod<
@@ -17,6 +18,7 @@ export default class TezosGetPublicKey extends AbstractMethod<
init() {
this.requiredPermissions = ['read'];
+ this.requiredDeviceCapabilities = ['Capability_Tezos'];
this.firmwareRange = getFirmwareRange(
this.name,
getMiscNetwork('Tezos'),
diff --git a/packages/connect/src/api/tezos/api/tezosSignTransaction.ts b/packages/connect/src/api/tezos/api/tezosSignTransaction.ts
index e307d2e8560..f2200be4f1a 100644
--- a/packages/connect/src/api/tezos/api/tezosSignTransaction.ts
+++ b/packages/connect/src/api/tezos/api/tezosSignTransaction.ts
@@ -1,12 +1,13 @@
// origin: https://github.com/trezor/connect/blob/develop/src/js/core/methods/TezosSignTransaction.js
+import { AssertWeak } from '@trezor/schema-utils';
+
import { AbstractMethod } from '../../../core/AbstractMethod';
import { getFirmwareRange } from '../../common/paramsValidator';
import { getMiscNetwork } from '../../../data/coinInfo';
import { validatePath } from '../../../utils/pathUtils';
import * as helper from '../tezosSignTx';
import type { PROTO } from '../../../constants';
-import { AssertWeak } from '@trezor/schema-utils';
import { TezosSignTransaction as TezosSignTransactionSchema } from '../../../types/api/tezos';
export default class TezosSignTransaction extends AbstractMethod<
@@ -15,6 +16,7 @@ export default class TezosSignTransaction extends AbstractMethod<
> {
init() {
this.requiredPermissions = ['read', 'write'];
+ this.requiredDeviceCapabilities = ['Capability_Tezos'];
this.firmwareRange = getFirmwareRange(
this.name,
getMiscNetwork('Tezos'),
diff --git a/packages/connect/src/api/tezos/tezosSignTx.ts b/packages/connect/src/api/tezos/tezosSignTx.ts
index 268f2198c00..68951bb79b7 100644
--- a/packages/connect/src/api/tezos/tezosSignTx.ts
+++ b/packages/connect/src/api/tezos/tezosSignTx.ts
@@ -1,7 +1,9 @@
import bs58check from 'bs58check';
+
+import { Assert } from '@trezor/schema-utils';
+
import { PROTO, ERRORS } from '../../constants';
import { TezosOperation } from '../../types/api/tezos';
-import { Assert } from '@trezor/schema-utils';
const PREFIX = {
B: new Uint8Array([1, 52]),
diff --git a/packages/connect/src/api/unlockPath.ts b/packages/connect/src/api/unlockPath.ts
index a445a67807b..89e852f3037 100644
--- a/packages/connect/src/api/unlockPath.ts
+++ b/packages/connect/src/api/unlockPath.ts
@@ -1,8 +1,9 @@
+import { Assert } from '@trezor/schema-utils';
+
import { AbstractMethod } from '../core/AbstractMethod';
import { PROTO } from '../constants';
import { validatePath } from '../utils/pathUtils';
import { getFirmwareRange } from './common/paramsValidator';
-import { Assert } from '@trezor/schema-utils';
import { UnlockPathParams } from '../types/api/unlockPath';
export default class UnlockPath extends AbstractMethod<'unlockPath', PROTO.UnlockPath> {
diff --git a/packages/connect/src/api/verifyMessage.ts b/packages/connect/src/api/verifyMessage.ts
index 08a90a81a6f..9c6be6ff739 100644
--- a/packages/connect/src/api/verifyMessage.ts
+++ b/packages/connect/src/api/verifyMessage.ts
@@ -1,5 +1,7 @@
// origin: https://github.com/trezor/connect/blob/develop/src/js/core/methods/VerifyMessage.js
+import { Assert } from '@trezor/schema-utils';
+
import { AbstractMethod } from '../core/AbstractMethod';
import { getFirmwareRange } from './common/paramsValidator';
import { getBitcoinNetwork } from '../data/coinInfo';
@@ -7,7 +9,6 @@ import { getLabel } from '../utils/pathUtils';
import { messageToHex } from '../utils/formatUtils';
import { PROTO, ERRORS } from '../constants';
import { VerifyMessage as VerifyMessageSchema } from '../types';
-import { Assert } from '@trezor/schema-utils';
export default class VerifyMessage extends AbstractMethod<'verifyMessage', PROTO.VerifyMessage> {
init() {
diff --git a/packages/connect/src/backend/BackendManager.ts b/packages/connect/src/backend/BackendManager.ts
index d2457e10358..fbe4767963f 100644
--- a/packages/connect/src/backend/BackendManager.ts
+++ b/packages/connect/src/backend/BackendManager.ts
@@ -2,7 +2,6 @@ import { DataManager } from '../data/DataManager';
import { ERRORS } from '../constants';
import { Blockchain, BlockchainOptions } from './Blockchain';
import { createBlockchainMessage, BLOCKCHAIN } from '../events';
-
import type { CoinInfo, BlockchainLink } from '../types';
type CoinShortcut = CoinInfo['shortcut'];
diff --git a/packages/connect/src/backend/Blockchain.ts b/packages/connect/src/backend/Blockchain.ts
index dc373202d33..a20ed76536c 100644
--- a/packages/connect/src/backend/Blockchain.ts
+++ b/packages/connect/src/backend/Blockchain.ts
@@ -4,6 +4,7 @@ import BlockchainLink, {
BlockchainLinkParams,
BlockchainLinkResponse,
} from '@trezor/blockchain-link';
+
import { createBlockchainMessage, BLOCKCHAIN, CoreEventMessage } from '../events';
import { ERRORS } from '../constants';
import {
@@ -13,7 +14,6 @@ import {
ElectrumWorker,
SolanaWorker,
} from '../workers/workers';
-
import type { CoinInfo, Proxy } from '../types';
const getWorker = (type: string) => {
@@ -40,13 +40,8 @@ const getNormalizedTrezorShortcut = (shortcut: string) => {
return 'XRP';
}
- return shortcut;
-};
-
-const getNormalizedBackendShortcut = (shortcut: string) => {
- // Can be safely removed when both Polygon PoS Blockbooks return POL as shortcut
- if (shortcut.toLowerCase() === 'matic') {
- return 'pol';
+ if (shortcut === 'OP') {
+ return 'ETH';
}
return shortcut;
@@ -138,7 +133,7 @@ export class Blockchain {
this.serverInfo = info;
const trezorShortcut = getNormalizedTrezorShortcut(this.coinInfo.shortcut);
- const backendShortcut = getNormalizedBackendShortcut(this.serverInfo.shortcut);
+ const backendShortcut = this.serverInfo.shortcut;
if (trezorShortcut.toLowerCase() !== backendShortcut.toLowerCase()) {
throw ERRORS.TypedError('Backend_Invalid');
@@ -222,6 +217,10 @@ export class Blockchain {
return this.link.getAccountUtxo(descriptor);
}
+ rpcCall(params: BlockchainLinkParams<'rpcCall'>) {
+ return this.link.rpcCall(params);
+ }
+
async estimateFee(request: Parameters[0]) {
const { blocks } = request;
// cache should be used if there is no specific data (ethereum case) and requested blocks are already cached/downloaded
diff --git a/packages/connect/src/backend/BlockchainLink.ts b/packages/connect/src/backend/BlockchainLink.ts
index 7efc4df87ab..3b57ed5e2c8 100644
--- a/packages/connect/src/backend/BlockchainLink.ts
+++ b/packages/connect/src/backend/BlockchainLink.ts
@@ -1,5 +1,4 @@
import { BackendManager } from './BackendManager';
-
import type { BlockchainOptions as Options } from './Blockchain';
import type { CoinInfo } from '../types';
diff --git a/packages/connect/src/backend/__tests__/Blockchain.test.ts b/packages/connect/src/backend/__tests__/Blockchain.test.ts
index 363307a7a78..3d8e4086bd0 100644
--- a/packages/connect/src/backend/__tests__/Blockchain.test.ts
+++ b/packages/connect/src/backend/__tests__/Blockchain.test.ts
@@ -1,6 +1,7 @@
import coinsJSON from '@trezor/connect-common/files/coins.json';
import coinsJSONEth from '@trezor/connect-common/files/coins-eth.json';
import BlockchainLink from '@trezor/blockchain-link';
+
import { parseCoinsJson, getBitcoinNetwork, getEthereumNetwork } from '../../data/coinInfo';
import { initBlockchain } from '../BlockchainLink';
diff --git a/packages/connect/src/constants/errors.ts b/packages/connect/src/constants/errors.ts
index eccb6436d4d..95ecf1fc038 100644
--- a/packages/connect/src/constants/errors.ts
+++ b/packages/connect/src/constants/errors.ts
@@ -42,13 +42,18 @@ export const ERROR_CODES = {
Device_InvalidState: 'Passphrase is incorrect', // authorization error (device state comparison)
Device_CallInProgress: 'Device call in progress', // thrown when trying to make another call while current is still running
Device_MultipleNotSupported: 'Multiple devices are not supported', // thrown by methods which require single device
+ Device_MissingCapability: 'Device is missing capability', // thrown by methods which require specific capability
+ Device_MissingCapabilityBtcOnly: 'Device is missing capability (BTC only)', // thrown by methods which require specific capability when using BTC only firmware
Failure_ActionCancelled: 'Action cancelled by user',
Failure_FirmwareError: 'Firmware installation failed',
Failure_UnknownCode: 'Unknown error',
Failure_PinCancelled: 'PIN cancelled',
+ Failure_PinInvalid: 'PIN invalid',
Failure_PinMismatch: 'PIN mismatch',
Failure_WipeCodeMismatch: 'Wipe code mismatch',
+
+ Deeplink_VersionMismatch: 'Not compatible with current version of the app',
} as const;
export type ErrorCode = keyof typeof ERROR_CODES;
diff --git a/packages/connect/src/constants/firmware.ts b/packages/connect/src/constants/firmware.ts
new file mode 100644
index 00000000000..d6473fe1e80
--- /dev/null
+++ b/packages/connect/src/constants/firmware.ts
@@ -0,0 +1,3 @@
+// given that firmware hash is a security feature, we prefer to hardcode the version rather than to query the device capabilities
+// (a counterfeit device could simply declare it's incapable of hash check)
+export const FW_HASH_SUPPORTED_VERSIONS = ['1.11.1', '2.5.1'];
diff --git a/packages/connect/src/constants/index.ts b/packages/connect/src/constants/index.ts
index b8003479228..ffaeb61b37f 100644
--- a/packages/connect/src/constants/index.ts
+++ b/packages/connect/src/constants/index.ts
@@ -2,4 +2,6 @@ export * as ERRORS from './errors';
export * as NETWORK from './network';
export * as CARDANO from './cardano';
export * as NEM from './nem';
+export * as FIRMWARE from './firmware';
+export { DEFAULT_SORTING_STRATEGY } from './utxo';
export { MessagesSchema as PROTO } from '@trezor/protobuf';
diff --git a/packages/connect/src/constants/utxo.ts b/packages/connect/src/constants/utxo.ts
new file mode 100644
index 00000000000..f0f86f46db2
--- /dev/null
+++ b/packages/connect/src/constants/utxo.ts
@@ -0,0 +1,3 @@
+import type { TransactionInputOutputSortingStrategy } from '@trezor/utxo-lib';
+
+export const DEFAULT_SORTING_STRATEGY: TransactionInputOutputSortingStrategy = 'random';
diff --git a/packages/connect/src/core/AbstractMethod.ts b/packages/connect/src/core/AbstractMethod.ts
index 2976cfe8271..95713119a46 100644
--- a/packages/connect/src/core/AbstractMethod.ts
+++ b/packages/connect/src/core/AbstractMethod.ts
@@ -1,7 +1,8 @@
import { storage } from '@trezor/connect-common';
import { versionUtils } from '@trezor/utils';
-import { DataManager } from '../data/DataManager';
-import { NETWORK } from '../constants';
+import { Capability } from '@trezor/protobuf/src/messages';
+
+import { NETWORK, ERRORS } from '../constants';
import {
UI,
DEVICE,
@@ -15,8 +16,14 @@ import {
} from '../events';
import { getHost } from '../utils/urlUtils';
import type { Device } from '../device/Device';
-import type { FirmwareRange, DeviceState, StaticSessionId } from '../types';
-import { ERRORS } from '../constants';
+import type {
+ FirmwareRange,
+ DeviceState,
+ StaticSessionId,
+ DeviceUniquePath,
+ ConnectSettings,
+} from '../types';
+import { config } from '../data/config';
export type Payload = Extract & { override?: boolean };
export type MethodReturnType = CallMethodResponse;
@@ -30,6 +37,7 @@ export const DEFAULT_FIRMWARE_RANGE: FirmwareRange = {
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' }, // TODO T3W1
};
function validateStaticSessionId(input: unknown): StaticSessionId {
@@ -53,6 +61,30 @@ function validateStaticSessionId(input: unknown): StaticSessionId {
'DeviceState: invalid staticSessionId: ' + input,
);
}
+// validate expected state from method parameter.
+// it could be undefined
+function validateDeviceState(input: unknown): DeviceState | undefined {
+ if (typeof input === 'string') {
+ return { staticSessionId: validateStaticSessionId(input) };
+ }
+ if (input && typeof input === 'object') {
+ const state: DeviceState = {};
+ 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;
+}
+
export abstract class AbstractMethod {
responseID: number;
@@ -61,7 +93,7 @@ export abstract class AbstractMethod originalFn(t, d || device);
}
- private getOriginPermissions() {
- const origin = DataManager.getSettings('origin');
+ private getOriginPermissions({ origin }: Pick) {
if (!origin) {
return [];
}
@@ -202,8 +222,8 @@ export abstract class AbstractMethod) {
+ const originPermissions = this.getOriginPermissions({ origin });
let notPermitted = [...this.requiredPermissions];
if (originPermissions.length > 0) {
// check if permission was granted
@@ -218,8 +238,8 @@ export abstract class AbstractMethod) {
+ const originPermissions = this.getOriginPermissions({ origin });
let permissionsToSave = this.requiredPermissions.map(p => ({
type: p,
@@ -248,13 +268,12 @@ export abstract class AbstractMethod ({
...state,
permissions: [...(state.permissions || []), ...permissionsToSave],
}),
- origin,
+ origin!,
temporary,
);
@@ -299,11 +318,10 @@ export abstract class AbstractMethod) {
if (popup && this.requiredPermissions.includes('management')) {
const host = getHost(origin);
- const allowed = DataManager.getConfig().management.find(
+ const allowed = config.management.find(
item => item.origin === host || item.origin === origin,
);
@@ -313,6 +331,33 @@ export abstract class AbstractMethod this.device.features.capabilities.includes(capability),
+ );
+ if (!deviceHasAllRequiredCapabilities) {
+ if (this.device.firmwareType === 'bitcoin-only') {
+ throw ERRORS.TypedError(
+ 'Device_MissingCapabilityBtcOnly',
+ `Trezor has Bitcoin-only firmware installed, which does not support this operation. Please install Universal firmware through Trezor Suite.`,
+ );
+ }
+ throw ERRORS.TypedError(
+ 'Device_MissingCapability',
+ 'Device does not have capability to call this method. Make sure you have the latest firmware installed.',
+ );
+ }
+ }
+
abstract run(): Promise>;
dispose() {}
diff --git a/packages/connect/src/core/__tests__/Core.test.ts b/packages/connect/src/core/__tests__/Core.test.ts
index 68b80aaa1b4..bcd9e4dbcd8 100644
--- a/packages/connect/src/core/__tests__/Core.test.ts
+++ b/packages/connect/src/core/__tests__/Core.test.ts
@@ -7,7 +7,11 @@ import { initCoreState } from '../index';
const { createTestTransport } = global.JestMocks;
const getSettings = (partial: Partial = {}) =>
- parseConnectSettings({ transports: [createTestTransport()], ...partial });
+ parseConnectSettings({
+ transports: [createTestTransport()],
+ transportReconnect: false,
+ ...partial,
+ });
describe('Core', () => {
beforeAll(async () => {});
diff --git a/packages/connect/src/core/index.ts b/packages/connect/src/core/index.ts
index 561767f337c..7230c1d4e26 100644
--- a/packages/connect/src/core/index.ts
+++ b/packages/connect/src/core/index.ts
@@ -2,8 +2,7 @@
import EventEmitter from 'events';
import { TRANSPORT, TRANSPORT_ERROR } from '@trezor/transport';
-import { createLazy, createDeferred, throwError } from '@trezor/utils';
-import { getSynchronize } from '@trezor/utils';
+import { createLazy, createDeferred, throwError, getSynchronize } from '@trezor/utils';
import { storage } from '@trezor/connect-common';
import { DataManager } from '../data/DataManager';
@@ -29,14 +28,18 @@ import {
} from '../events';
import { getMethod } from './method';
import { AbstractMethod } from './AbstractMethod';
-import { resolveAfter } from '../utils/promiseUtils';
import { createUiPromiseManager } from '../utils/uiPromiseManager';
import { createPopupPromiseManager } from '../utils/popupPromiseManager';
import { initLog, enableLog, setLogWriter, LogWriter } from '../utils/debug';
import { dispose as disposeBackend } from '../backend/BlockchainLink';
import { InteractionTimeout } from '../utils/interactionTimeout';
import type { DeviceEvents, Device } from '../device/Device';
-import type { ConnectSettings, Device as DeviceTyped, StaticSessionId } from '../types';
+import type {
+ ConnectSettings,
+ Device as DeviceTyped,
+ DeviceUniquePath,
+ StaticSessionId,
+} from '../types';
import { onCallFirmwareUpdate } from './onCallFirmwareUpdate';
import { WebextensionStateStorage } from '../device/StateStorage';
@@ -65,7 +68,7 @@ const startInteractionTimeout = (context: CoreContext) =>
* @returns {Promise}
* @memberof Core
*/
-const initDevice = async (context: CoreContext, devicePath?: string) => {
+const initDevice = async (context: CoreContext, devicePath?: DeviceUniquePath) => {
const { uiPromises, deviceList, sendCoreMessage } = context;
assertDeviceListConnected(deviceList);
@@ -77,7 +80,8 @@ const initDevice = async (context: CoreContext, devicePath?: string) => {
const origin = DataManager.getSettings('origin')!;
const useCoreInPopup = DataManager.getSettings('useCoreInPopup');
const { preferredDevice } = storage.load().origin[origin] || {};
- const preferredDeviceInList = preferredDevice && deviceList.getDevice(preferredDevice.path);
+ const preferredDeviceInList =
+ preferredDevice && deviceList.getDeviceByPath(preferredDevice.path);
// we detected that there is a preferred device (user stored previously) but it's not in the list anymore (disconnected now)
// we treat this situation as implicit forget
@@ -90,18 +94,18 @@ const initDevice = async (context: CoreContext, devicePath?: string) => {
}
if (devicePath) {
- device = deviceList.getDevice(devicePath);
+ device = deviceList.getDeviceByPath(devicePath);
showDeviceSelection =
- !device || !!device?.unreadableError || (device.isUnacquired() && !!isUsingPopup);
+ !device || device.isUnreadable() || (device.isUnacquired() && !!isUsingPopup);
} else {
- const devices = deviceList.asArray();
- if (devices.length === 1 && (!isWebUsb || !isUsingPopup)) {
+ const onlyDevice = deviceList.getOnlyDevice();
+ if (onlyDevice && (!isWebUsb || !isUsingPopup)) {
// there is only one device available. use it
- device = deviceList.getDevice(devices[0].path);
+ device = onlyDevice;
// Show device selection if device is unreadable or unacquired
// Also in case of core in popup, so user can press "Remember device"
showDeviceSelection =
- !!device?.unreadableError || device.isUnacquired() || !!useCoreInPopup;
+ device.isUnreadable() || device.isUnacquired() || !!useCoreInPopup;
} else {
showDeviceSelection = true;
}
@@ -124,22 +128,22 @@ const initDevice = async (context: CoreContext, devicePath?: string) => {
// check again for available devices
// there is a possible race condition before popup open
- const devices = deviceList.asArray();
+ const onlyDevice = deviceList.getOnlyDevice();
if (
- devices.length === 1 &&
- devices[0].type !== 'unreadable' &&
- devices[0].features &&
+ onlyDevice &&
+ !onlyDevice.isUnreadable() &&
+ !onlyDevice.isUnacquired() &&
!isWebUsb &&
!useCoreInPopup
) {
// there is one device available. use it
- device = deviceList.getDevice(devices[0].path);
+ device = onlyDevice;
} else {
// request select device view
sendCoreMessage(
createUiMessage(UI.SELECT_DEVICE, {
webusb: isWebUsb,
- devices: deviceList.asArray(),
+ devices: deviceList.getAllDevices().map(d => d.toMessageObject()),
}),
);
@@ -157,7 +161,7 @@ const initDevice = async (context: CoreContext, devicePath?: string) => {
return store;
});
}
- device = deviceList.getDevice(payload.device.path);
+ device = deviceList.getDeviceByPath(payload.device.path);
}
}
} else if (uiPromises.exists(UI.RECEIVE_DEVICE)) {
@@ -246,7 +250,7 @@ const inner = async (context: CoreContext, method: AbstractMethod, device:
method.requireDeviceMode,
);
if (unexpectedMode) {
- device.keepTransportSession = false;
+ device.releaseTransportSession();
if (isUsingPopup) {
// wait for popup handshake
await waitForPopup(context);
@@ -264,8 +268,10 @@ const inner = async (context: CoreContext, method: AbstractMethod, device:
return Promise.reject(ERRORS.TypedError('Device_ModeException', unexpectedMode));
}
+ method.checkDeviceCapability();
+
// check and request permissions [read, write...]
- method.checkPermissions();
+ method.checkPermissions({ origin: DataManager.getSettings('origin') });
if (!trustedHost && method.requiredPermissions.length > 0) {
// wait for popup window
await waitForPopup(context);
@@ -281,7 +287,7 @@ const inner = async (context: CoreContext, method: AbstractMethod, device:
const { granted, remember } = await uiPromise.promise.then(({ payload }) => payload);
if (granted) {
- method.savePermissions(!remember);
+ method.savePermissions(!remember, { origin: DataManager.getSettings('origin') });
} else {
// interrupt process and go to "final" block
return Promise.reject(ERRORS.TypedError('Method_PermissionsNotGranted'));
@@ -468,16 +474,16 @@ const onCall = async (context: CoreContext, message: IFrameCallMessage) => {
try {
method = await methodSynchronize(async () => {
_log.debug('loading method...');
- const method = await getMethod(message);
- _log.debug('method selected', method.name);
+ const method2 = await getMethod(message);
+ _log.debug('method selected', method2.name);
// bind callbacks
- method.postMessage = sendCoreMessage;
- method.createUiPromise = uiPromises.create;
+ method2.postMessage = sendCoreMessage;
+ method2.createUiPromise = uiPromises.create;
// start validation process
- method.init();
- await method.initAsync?.();
+ method2.init();
+ await method2.initAsync?.();
- return method;
+ return method2;
});
resolveWaitForFirstMethod();
callMethods.push(method);
@@ -488,6 +494,13 @@ const onCall = async (context: CoreContext, message: IFrameCallMessage) => {
return Promise.resolve();
}
+ if (method.payload.__info) {
+ const response = method.getMethodInfo();
+ sendCoreMessage(createResponseMessage(method.responseID, true, response));
+
+ return Promise.resolve();
+ }
+
// this method is not using the device, there is no need to acquire
if (!method.useDevice) {
try {
@@ -498,6 +511,7 @@ const onCall = async (context: CoreContext, message: IFrameCallMessage) => {
// cancel popup request
sendCoreMessage(createPopupMessage(POPUP.CANCEL_POPUP_REQUEST));
}
+
const response = await method.run();
sendCoreMessage(createResponseMessage(method.responseID, true, response));
} catch (error) {
@@ -507,7 +521,7 @@ const onCall = async (context: CoreContext, message: IFrameCallMessage) => {
return Promise.resolve();
}
- if (method.isManagementRestricted()) {
+ if (method.isManagementRestricted({ origin: DataManager.getSettings('origin') })) {
sendCoreMessage(createPopupMessage(POPUP.CANCEL_POPUP_REQUEST));
sendCoreMessage(
createResponseMessage(responseID, false, {
@@ -681,25 +695,20 @@ const onCallDevice = async (
}
// Work done
+ if (
+ method.keepSession &&
+ method.deviceState &&
+ method.deviceState.sessionId !== device.getState()?.sessionId
+ ) {
+ // if session was changed from the one that was sent, send a device changed event
+ sendCoreMessage(createDeviceMessage(DEVICE.CHANGED, device.toMessageObject()));
+ }
+
// TODO: This requires a massive refactoring https://github.com/trezor/trezor-suite/issues/5323
// @ts-expect-error TODO: messageResponse should be assigned from the response of "inner" function
const response = messageResponse;
if (response) {
- if (method.name === 'rebootToBootloader' && response.success) {
- // Wait for device to switch to bootloader
- // This delay is crucial see https://github.com/trezor/trezor-firmware/issues/1983
- await resolveAfter(1000).promise;
- // call Device.run with empty function to fetch new Features
- // (acquire > Initialize > nothing > release)
- try {
- await device.run(() => Promise.resolve(), { skipFinalReload: true });
- } catch (err) {
- // ignore. on model T, this block of code is probably not needed at all. but I am keeping it here for
- // backwards compatibility
- }
- }
-
await device.cleanup();
if (useCoreInPopup) {
@@ -896,9 +905,9 @@ const onPopupClosed = (context: CoreContext, customErrorMessage?: string) => {
? ERRORS.TypedError('Method_Cancel', customErrorMessage)
: ERRORS.TypedError('Method_Interrupted');
// Device was already acquired. Try to interrupt running action which will throw error from onCall try/catch block
- if (deviceList.isConnected() && deviceList.asArray().length > 0) {
- deviceList.allDevices().forEach(d => {
- d.keepTransportSession = false; // clear transportSession on release
+ if (deviceList.isConnected() && deviceList.getDeviceCount() > 0) {
+ deviceList.getAllDevices().forEach(d => {
+ d.releaseTransportSession(); // clear transportSession on release
if (d.isUsedHere()) {
setOverridePromise(d.interruptionFromUser(error));
} else {
@@ -933,22 +942,22 @@ const handleDeviceSelectionChanges = (context: CoreContext, interruptDevice?: De
// update list of devices in popup
const promiseExists = uiPromises.exists(UI.RECEIVE_DEVICE);
if (promiseExists && deviceList.isConnected()) {
- const list = deviceList.asArray();
+ const onlyDevice = deviceList.getOnlyDevice();
const isWebUsb = deviceList.transportType() === 'WebUsbTransport';
- if (list.length === 1 && !isWebUsb) {
+ if (onlyDevice && !isWebUsb) {
// there is only one device. use it
// resolve uiPromise to looks like it's a user choice (see: handleMessage function)
uiPromises.resolve({
type: UI.RECEIVE_DEVICE,
- payload: { device: list[0] },
+ payload: { device: onlyDevice.toMessageObject() },
});
} else {
// update device selection list view
sendCoreMessage(
createUiMessage(UI.SELECT_DEVICE, {
webusb: isWebUsb,
- devices: list,
+ devices: deviceList.getAllDevices().map(d => d.toMessageObject()),
}),
);
}
@@ -1192,7 +1201,7 @@ export class Core extends EventEmitter {
try {
await DataManager.load(settings);
- const { debug, priority, _sessionsBackgroundUrl } = DataManager.getSettings();
+ const { debug, priority, _sessionsBackgroundUrl, manifest } = DataManager.getSettings();
const messages = DataManager.getProtobufMessages();
enableLog(debug);
@@ -1207,6 +1216,7 @@ export class Core extends EventEmitter {
messages,
priority,
_sessionsBackgroundUrl,
+ manifest,
});
initDeviceList(this.getCoreContext());
diff --git a/packages/connect/src/core/onCallFirmwareUpdate.ts b/packages/connect/src/core/onCallFirmwareUpdate.ts
index 0abdc690c2b..cc9531ea04b 100644
--- a/packages/connect/src/core/onCallFirmwareUpdate.ts
+++ b/packages/connect/src/core/onCallFirmwareUpdate.ts
@@ -1,6 +1,7 @@
import { randomBytes } from 'crypto';
import { createTimeoutPromise } from '@trezor/utils';
+import { isNewer } from '@trezor/utils/src/versionUtils';
import { DeviceList } from '../device/DeviceList';
import { UI, DEVICE, createUiMessage, createDeviceMessage, CoreEventMessage } from '../events';
@@ -14,11 +15,10 @@ import {
stripFwHeaders,
} from '../api/firmware';
import { getReleases } from '../data/firmwareInfo';
-import { CommonParams, IntermediaryVersion } from '../types';
-import { PROTO, ERRORS } from '../constants';
+import { CommonParams, DeviceUniquePath, IntermediaryVersion } from '../types';
+import { FIRMWARE, PROTO, ERRORS } from '../constants';
import type { Log } from '../utils/debug';
import type { Device } from '../device/Device';
-import { isNewer } from '@trezor/utils/src/versionUtils';
type PostMessage = (message: CoreEventMessage) => void;
@@ -91,9 +91,10 @@ const waitForReconnectedDevice = async (
await createTimeoutPromise(2000);
try {
- const path = deviceList.getFirstDevicePath();
- reconnectedDevice = deviceList.getDevice(path);
- } catch {}
+ reconnectedDevice = deviceList.getOnlyDevice();
+ } catch {
+ /* empty */
+ }
i++;
log.debug('onCallFirmwareUpdate', 'waiting for device to reconnect', i);
} while (
@@ -287,7 +288,7 @@ export type Params = {
type Context = {
deviceList: DeviceList;
postMessage: PostMessage;
- initDevice: (path?: string) => Promise;
+ initDevice: (path?: DeviceUniquePath) => Promise;
log: Log;
abortSignal: AbortSignal;
};
@@ -306,7 +307,7 @@ export const onCallFirmwareUpdate = async ({
throw ERRORS.TypedError('Runtime', 'device.firmwareRelease is not set');
}
- if (deviceList.allDevices().length > 1) {
+ if (deviceList.getDeviceCount() > 1) {
throw ERRORS.TypedError(
'Device_MultipleNotSupported',
'Firmware update allowed with only 1 device connected',
@@ -493,7 +494,8 @@ export const onCallFirmwareUpdate = async ({
}
}
- const checkSupported = reconnectedDevice.atLeast(['1.11.1', '2.5.1']) && !params.binary;
+ const checkSupported =
+ reconnectedDevice.atLeast(FIRMWARE.FW_HASH_SUPPORTED_VERSIONS) && !params.binary;
if (checkSupported) {
try {
@@ -513,8 +515,7 @@ export const onCallFirmwareUpdate = async ({
return { check: 'mismatch' as const };
}
} catch (err) {
- // TrezorConnect error. Only 'softly' inform user that we were not able to
- // validate firmware hash
+ // device failed to respond to the hash check, consider the firmware counterfeit
return { check: 'other-error' as const, checkError: err.message };
}
} else {
diff --git a/packages/connect/src/data/DataManager.ts b/packages/connect/src/data/DataManager.ts
index cc17c9ecf1e..2413c2037e7 100644
--- a/packages/connect/src/data/DataManager.ts
+++ b/packages/connect/src/data/DataManager.ts
@@ -4,12 +4,49 @@ import { httpRequest } from '../utils/assets';
import { parseCoinsJson } from './coinInfo';
import { parseFirmware } from './firmwareInfo';
import { parseBridgeJSON } from './transportInfo';
-import { config } from './config';
-
import { ConnectSettings, DeviceModelInternal } from '../types';
type AssetCollection = { [key: string]: Record };
+const assets = [
+ {
+ name: 'coins',
+ url: './data/coins.json',
+ },
+ {
+ name: 'coinsEth',
+ url: './data/coins-eth.json',
+ },
+ {
+ name: 'bridge',
+ url: './data/bridge/releases.json',
+ },
+ {
+ name: 'firmware-t1b1',
+ url: './data/firmware/t1b1/releases.json',
+ },
+ {
+ name: 'firmware-t2t1',
+ url: './data/firmware/t2t1/releases.json',
+ },
+ {
+ name: 'firmware-t2b1',
+ url: './data/firmware/t2b1/releases.json',
+ },
+ {
+ name: 'firmware-t3b1',
+ url: './data/firmware/t3b1/releases.json',
+ },
+ {
+ name: 'firmware-t3t1',
+ url: './data/firmware/t3t1/releases.json',
+ },
+ {
+ name: 'firmware-t3tw1',
+ url: './data/firmware/t3w1/releases.json',
+ },
+];
+
export class DataManager {
static assets: AssetCollection = {};
@@ -22,13 +59,13 @@ export class DataManager {
if (!withAssets) return;
- const assetPromises = config.assets.map(async asset => {
+ const assetPromises = assets.map(async asset => {
const json = await httpRequest(`${asset.url}${ts}`, 'json');
this.assets[asset.name] = json;
});
await Promise.all(assetPromises);
- this.messages = await httpRequest(`${config.messages}${ts}`, 'json');
+ this.messages = await httpRequest('./data/messages/messages.json', 'json');
// parse bridge JSON
parseBridgeJSON(this.assets.bridge);
@@ -64,8 +101,4 @@ export class DataManager {
return this.settings;
}
-
- static getConfig() {
- return config;
- }
}
diff --git a/packages/connect/src/data/__tests__/DataManager.test.ts b/packages/connect/src/data/__tests__/DataManager.test.ts
index a06976cc4bb..7265de88317 100644
--- a/packages/connect/src/data/__tests__/DataManager.test.ts
+++ b/packages/connect/src/data/__tests__/DataManager.test.ts
@@ -17,6 +17,7 @@ const settings = {
iframeSrc: '',
popupSrc: '',
webusbSrc: '',
+ deeplinkUrl: '',
version: '9.0.0',
priority: 1,
trustedHost: true,
@@ -31,6 +32,7 @@ describe('data/DataManager', () => {
try {
await DataManager.load(settings, false);
} catch (err) {
+ // eslint-disable-next-line jest/no-standalone-expect
expect(err).toBe(undefined);
}
});
diff --git a/packages/connect/src/data/__tests__/coinInfo.test.ts b/packages/connect/src/data/__tests__/coinInfo.test.ts
index ec23950cd45..da5b40e139b 100644
--- a/packages/connect/src/data/__tests__/coinInfo.test.ts
+++ b/packages/connect/src/data/__tests__/coinInfo.test.ts
@@ -1,4 +1,5 @@
import coinsJSON from '@trezor/connect-common/files/coins.json';
+
import { parseCoinsJson, getCoinInfo, getUniqueNetworks, getAllNetworks } from '../coinInfo';
describe('data/coinInfo', () => {
diff --git a/packages/connect/src/data/__tests__/firmwareInfo.test.ts b/packages/connect/src/data/__tests__/firmwareInfo.test.ts
index 469b77fb26b..d345601e503 100644
--- a/packages/connect/src/data/__tests__/firmwareInfo.test.ts
+++ b/packages/connect/src/data/__tests__/firmwareInfo.test.ts
@@ -1,5 +1,6 @@
-import { getReleases, parseFirmware, getFirmwareStatus } from '../firmwareInfo';
import * as releases2 from '@trezor/connect-common/files/firmware/t2t1/releases.json';
+
+import { getReleases, parseFirmware, getFirmwareStatus } from '../firmwareInfo';
import { DeviceModelInternal } from '../../types';
describe('data/firmwareInfo', () => {
diff --git a/packages/connect/src/data/__tests__/transportInfo.test.ts b/packages/connect/src/data/__tests__/transportInfo.test.ts
index c70d4348667..83add78dfca 100644
--- a/packages/connect/src/data/__tests__/transportInfo.test.ts
+++ b/packages/connect/src/data/__tests__/transportInfo.test.ts
@@ -1,6 +1,7 @@
-import { parseBridgeJSON } from '../transportInfo';
import * as releases from '@trezor/connect-common/files/bridge/releases.json';
+import { parseBridgeJSON } from '../transportInfo';
+
describe('data/transportInfo', () => {
test('parseBridgeJSON', () => {
expect(parseBridgeJSON(releases)).toEqual({
diff --git a/packages/connect/src/data/analyticsInfo.ts b/packages/connect/src/data/analyticsInfo.ts
index 928a277bd0d..b6cdf34b55b 100644
--- a/packages/connect/src/data/analyticsInfo.ts
+++ b/packages/connect/src/data/analyticsInfo.ts
@@ -1,28 +1,22 @@
-// import { EventType } from '@trezor/connect-analytics';
+import { EventType } from '@trezor/connect-analytics';
+
import { CoreEventMessage, UI_REQUEST } from '../events';
import type { Device } from '../types';
-// TODO: imho this belongs somewhere to packages/connect-iframe package.
-// There I can freely import from anyplace within monorepo without needing
-// to release new packages. Having it here means that I would need to
-// release 2 new packages to npm which is unjustifiable burden
export const enhanceMessageWithAnalytics = (
message: CoreEventMessage,
data: { device?: Device },
): CoreEventMessage => {
switch (message.type) {
- case UI_REQUEST.REQUEST_CONFIRMATION:
+ case UI_REQUEST.REQUEST_CONFIRMATION: {
const { device } = data;
return {
...message,
- // @ts-expect-error (EventType.DeviceSelected is inlined here)
payload: {
...message.payload,
analytics: {
- // todo: type inlined temporarily
- // type: EventType.DeviceSelected,
- type: 'device/selected',
+ type: EventType.DeviceSelected,
payload: {
mode: device?.mode || '',
pinProtection: device?.features?.pin_protection || '',
@@ -44,6 +38,7 @@ export const enhanceMessageWithAnalytics = (
},
},
};
+ }
default:
return message;
diff --git a/packages/connect/src/data/coinInfo.ts b/packages/connect/src/data/coinInfo.ts
index e937b9c08fc..92216cdf56c 100644
--- a/packages/connect/src/data/coinInfo.ts
+++ b/packages/connect/src/data/coinInfo.ts
@@ -1,5 +1,6 @@
// origin: https://github.com/trezor/connect/blob/develop/src/js/data/CoinInfo.js
import { cloneObject } from '@trezor/utils';
+
import { getBitcoinFeeLevels, getEthereumFeeLevels, getMiscFeeLevels } from './defaultFeeLevels';
import { ERRORS } from '../constants';
import { toHardened, fromHardened } from '../utils/pathUtils';
diff --git a/packages/connect/src/data/config.ts b/packages/connect/src/data/config.ts
index c4adf818de4..93d17614379 100644
--- a/packages/connect/src/data/config.ts
+++ b/packages/connect/src/data/config.ts
@@ -66,41 +66,6 @@ export const config = {
onionDomains: {
'trezor.io': 'trezoriovpjcahpzkrewelclulmszwbqpzmzgub37gbcjlvluxtruqad.onion',
},
- assets: [
- {
- name: 'coins',
- url: './data/coins.json',
- },
- {
- name: 'coinsEth',
- url: './data/coins-eth.json',
- },
- {
- name: 'bridge',
- url: './data/bridge/releases.json',
- },
- {
- name: 'firmware-t1b1',
- url: './data/firmware/t1b1/releases.json',
- },
- {
- name: 'firmware-t2t1',
- url: './data/firmware/t2t1/releases.json',
- },
- {
- name: 'firmware-t2b1',
- url: './data/firmware/t2b1/releases.json',
- },
- {
- name: 'firmware-t3b1',
- url: './data/firmware/t3b1/releases.json',
- },
- {
- name: 'firmware-t3t1',
- url: './data/firmware/t3t1/releases.json',
- },
- ],
- messages: './data/messages/messages.json',
supportedBrowsers: {
chrome: {
version: 59,
@@ -257,11 +222,11 @@ export const config = {
},
{
methods: ['showDeviceTutorial', 'authenticateDevice'],
- min: { T1B1: '0', T2T1: '0', T2B1: '2.6.1', T3B1: '2.8.1', T3T1: '2.8.0' },
- },
- {
- methods: ['rebootToBootloader'],
- min: { T1B1: '1.10.0', T2T1: '2.6.0' },
+ min: {
+ T1B1: '0',
+ T2T1: '0',
+ T3T1: '2.8.0',
+ },
},
{
methods: ['getFirmwareHash'],
@@ -269,11 +234,19 @@ export const config = {
},
{
methods: ['solanaGetPublicKey', 'solanaGetAddress', 'solanaSignTransaction'],
- min: { T1B1: '0', T2T1: '2.6.4', T2B1: '2.6.4', T3B1: '2.8.1', T3T1: '2.7.2' },
+ min: {
+ T1B1: '0',
+ T2T1: '2.6.4',
+ T2B1: '2.6.4',
+ },
},
{
capabilities: ['chunkify'],
- min: { T1B1: '0', T2T1: '2.6.3', T2B1: '2.6.3', T3B1: '2.8.1', T3T1: '2.7.2' },
+ min: {
+ T1B1: '0',
+ T2T1: '2.6.3',
+ T2B1: '2.6.3',
+ },
comment: [
"Since firmware 2.6.3 there is a new protobuf field 'chunkify' in almost all getAddress and signTx methods",
],
@@ -284,8 +257,6 @@ export const config = {
T1B1: '0',
T2T1: '2.7.0',
T2B1: '2.7.0',
- T3B1: '2.8.1', // adding T3B1 to the list so that it gets inferred as type
- T3T1: '2.7.2',
},
},
],
diff --git a/packages/connect/src/data/connectSettings.ts b/packages/connect/src/data/connectSettings.ts
index f278b0eb778..1886127aa15 100644
--- a/packages/connect/src/data/connectSettings.ts
+++ b/packages/connect/src/data/connectSettings.ts
@@ -1,7 +1,7 @@
// origin: https://github.com/trezor/connect/blob/develop/src/js/data/ConnectSettings.js
import type { Manifest, ConnectSettings } from '../types';
-import { VERSION, DEFAULT_DOMAIN } from './version';
+import { VERSION, DEFAULT_DOMAIN, DEEPLINK_VERSION } from './version';
/*
* Initial settings for connect.
@@ -28,6 +28,8 @@ const initialSettings: ConnectSettings = {
timestamp: new Date().getTime(),
interactionTimeout: 600, // 5 minutes
sharedLogger: true,
+ deeplinkUrl: `${DEFAULT_DOMAIN}deeplink/${DEEPLINK_VERSION}/`,
+ transportReconnect: true,
};
const parseManifest = (manifest?: Manifest) => {
@@ -83,16 +85,12 @@ export const parseConnectSettings = (input: Partial = {}) => {
settings.iframeSrc = `${src}iframe.html`;
settings.popupSrc = `${src}popup.html`;
settings.webusbSrc = `${src}webusb.html`;
+ settings.deeplinkUrl = `${src}deeplink/${DEEPLINK_VERSION}/`;
if (typeof input.transportReconnect === 'boolean') {
settings.transportReconnect = input.transportReconnect;
}
- // deprecated, settings.transport should be used instead
- if (typeof input.webusb === 'boolean') {
- settings.webusb = input.webusb;
- }
-
if (Array.isArray(input.transports)) {
settings.transports = input.transports;
}
@@ -144,9 +142,17 @@ export const parseConnectSettings = (input: Partial = {}) => {
settings._extendWebextensionLifetime = input._extendWebextensionLifetime;
}
- if (typeof input._sessionsBackgroundUrl === 'string') {
+ if (typeof input._sessionsBackgroundUrl === 'string' || input._sessionsBackgroundUrl === null) {
settings._sessionsBackgroundUrl = input._sessionsBackgroundUrl;
}
+ if (typeof input.binFilesBaseUrl === 'string') {
+ settings.binFilesBaseUrl = input.binFilesBaseUrl;
+ }
+
+ if (typeof input.enableFirmwareHashCheck === 'boolean') {
+ settings.enableFirmwareHashCheck = Boolean(input.enableFirmwareHashCheck);
+ }
+
return settings;
};
diff --git a/packages/connect/src/data/deviceAuthenticityConfig.ts b/packages/connect/src/data/deviceAuthenticityConfig.ts
index ba0c1ce6580..606a0c13d91 100644
--- a/packages/connect/src/data/deviceAuthenticityConfig.ts
+++ b/packages/connect/src/data/deviceAuthenticityConfig.ts
@@ -52,6 +52,12 @@ export const deviceAuthenticityConfig: DeviceAuthenticityConfig = {
'04c243743ba6326a443c34d503ea5d2d3036e1fbc8dbb072d4ae0f9b8016910db38e234641b87557fb44bcddee612722a67831bbac9758982a5843d9077be9434d',
'04f1a32b84249a4b909974e32b33cc78aeef8144ef57744e792e6203fb8acce348f0d5d5535ed9a65ac741744a264de2073e49dbcd989819cddc90285b5e97a28a',
'04894fa385d11809244da4f3d7de8ddd70c25abd5d4d3054b438d09016f2fd0a98729e15ea1483fbde83489fec6c64f8be014db8dc22227babcecdb5fd996ea671',
+ '0488c8f35649583d68c01c3b34223f11cc61389bcc71707960a6b1c30854ac1a0b5da916c47ee4cb1a78a9bac3d602aec67a5cd49b34732b6d1cc4fb2cb6b04361',
+ '04e4be5f15b528c594bfe732e4fcae4e65d63580051bb0235e6c1ca91c2663d54afa4b873dfeacbc4df98c57a923f7b308277f9d6bd064e81ed8e6890a1c582532',
+ '041c6ff76562b8042cf0e678d4bf977da09947d531e628450acbda0f0609c242c63db0be7fa1c601940af499b455e207c252706b37487c41fa9086621c3bd5c131',
+ '041b31c4fc213b27a34106e5ee8c7516941fdf8b15e3410987592ee488081c8dde5f42d397901c6d8b1d029d89bd2286b9183091a1edf12914be29aea40968d039',
+ '045de24c0d64b611bee13f34d74a3df7b24a658e6fea1bf1b9b3b530c23177759ba757e86494ff51c688bd63c1c85e93148bd81f39c5db80d3b5a0c05a6da4f380',
+ '049fb194cd0f7d79b3aae0b681d9f324c3dd086bb3f568067070bed195e1d7f0c722f86f7f9476da68b4c57e42536e5ab8c7fc74ee4726208814897849cac77b47',
],
debug: {
rootPubKeys: [
@@ -99,6 +105,9 @@ export const deviceAuthenticityConfig: DeviceAuthenticityConfig = {
'04b427eb0e1484b7127f82820edddbbb538c11d3c330b5f68acef96c8cf4601b30390311f2d48090f3f5c5e4bf56ca0396984797cd92ab0d12164600c9f43d2ba3',
'0438e670e9eb3cd19f8579b3202caf9aa766c8d5735ab97bd3682155437499e062587bded86c732789345538581c83e9e42af0a15a4ad81bc42e1f28a7859ebce0',
'04ac2a7e88ffc08186b3cb461966a96cacdb2c07b383018d708a6f0ce0b74f59dca97a6f31fc8d243a781c7fe5006dc6b8ef05347972082b32fb29c636c595ffbc',
+ '047c32e92dc23894b60b6c182f589a12e4e57f82e48176936f56646c14dd8f0d300fd4737e17501d71deb84127c272c5b797ccf30a7a4531846b9c79866f2892c6',
+ '046482d01c4dbdac60327352d150b5e6d5772cd68732616a0abc5649c49111bc3ba0eb28aa424100efa27cdb98675395df4b9097097e92adecc39f5587306a9fac',
+ '04f96a9fdecc4247c67e083a69037d1794230441270e4bda6d4151eb2e385d576d046d36734dd262e45c8cf3bbe24bdd11c1dc0e58437107d55cea3942f14b7d61',
],
debug: {
rootPubKeys: [
@@ -109,4 +118,12 @@ export const deviceAuthenticityConfig: DeviceAuthenticityConfig = {
],
},
},
+ T3W1: {
+ rootPubKeys: ['you shall not pass'], // TODO T3W1
+ caPubKeys: ['you shall not pass'], // TODO T3W1
+ debug: {
+ rootPubKeys: ['you shall not pass'], // TODO T3W1
+ caPubKeys: ['you shall not pass'], // TODO T3W1
+ },
+ },
};
diff --git a/packages/connect/src/data/deviceAuthenticityConfigTypes.ts b/packages/connect/src/data/deviceAuthenticityConfigTypes.ts
index 7f61ba02d1a..004b30612c8 100644
--- a/packages/connect/src/data/deviceAuthenticityConfigTypes.ts
+++ b/packages/connect/src/data/deviceAuthenticityConfigTypes.ts
@@ -1,4 +1,5 @@
import { Static, Type } from '@trezor/schema-utils';
+
import { PROTO } from '../constants';
type CertPubKeys = Static;
diff --git a/packages/connect/src/data/firmwareInfo.ts b/packages/connect/src/data/firmwareInfo.ts
index de9b904a450..a852e878a00 100644
--- a/packages/connect/src/data/firmwareInfo.ts
+++ b/packages/connect/src/data/firmwareInfo.ts
@@ -1,6 +1,7 @@
// origin: https://github.com/trezor/connect/blob/develop/src/js/data/FirmwareInfo.js
import { versionUtils } from '@trezor/utils';
+
import {
filterSafeListByFirmware,
filterSafeListByBootloader,
@@ -34,7 +35,7 @@ export const parseFirmware = (json: any, deviceModel: DeviceModelInternal) => {
export const getReleases = (deviceModel: DeviceModelInternal) => releases[deviceModel] || [];
-const getChangelog = (releases: FirmwareRelease[], features: StrictFeatures) => {
+const getChangelog = (releases2: FirmwareRelease[], features: StrictFeatures) => {
// releases are already filtered, so they can be considered "safe".
// so lets build changelog! It should include only those firmwares, that are
// newer than currently installed firmware.
@@ -51,7 +52,7 @@ const getChangelog = (releases: FirmwareRelease[], features: StrictFeatures) =>
if (features.firmware_present && features.major_version === 2) {
// little different situation is with model 2, where in bootloader (and with some fw installed)
// we actually know the firmware version
- return releases.filter(r =>
+ return releases2.filter(r =>
versionUtils.isNewer(r.version, [
features.fw_major,
features.fw_minor,
@@ -61,13 +62,13 @@ const getChangelog = (releases: FirmwareRelease[], features: StrictFeatures) =>
}
// for fresh devices, we can assume that all releases are actually "new"
- return releases;
+ return releases2;
}
// otherwise we are in firmware mode and because each release in releases list has
// version higher than the previous one, we can filter out the version that is already
// installed and show only what's new!
- return releases.filter(r =>
+ return releases2.filter(r =>
versionUtils.isNewer(r.version, [
features.major_version,
features.minor_version,
@@ -98,7 +99,7 @@ const isEqual = (release: FirmwareRelease, latest: FirmwareRelease) =>
versionUtils.isEqual(release.version, latest.version);
const getT1BootloaderVersion = (
- releases: FirmwareRelease[],
+ releases2: FirmwareRelease[],
features: StrictFeatures,
): VersionArray => {
const { bootloader_mode, major_version, minor_version, patch_version } = features;
@@ -108,7 +109,7 @@ const getT1BootloaderVersion = (
return versionArray;
}
- const release = releases.find(({ version }) => versionUtils.isEqual(version, versionArray));
+ const release = releases2.find(({ version }) => versionUtils.isEqual(version, versionArray));
/**
* FW version 1.6.0 and below don't have bootloader_version listed, so default to 1.0.0,
@@ -123,7 +124,7 @@ const getT1BootloaderVersion = (
* v3 - bootloader >= 1.12.0
*/
const getIntermediaryVersion = (
- releases: FirmwareRelease[],
+ releases2: FirmwareRelease[],
features: StrictFeatures,
offerLatest: boolean,
): IntermediaryVersion | undefined => {
@@ -132,7 +133,7 @@ const getIntermediaryVersion = (
return;
}
- const bootloaderVersion = getT1BootloaderVersion(releases, features);
+ const bootloaderVersion = getT1BootloaderVersion(releases2, features);
if (versionUtils.isNewerOrEqual(bootloaderVersion, [1, 12, 0])) {
return 3;
@@ -150,6 +151,7 @@ export interface GetInfoProps {
releases: FirmwareRelease[];
}
+// eslint-disable-next-line @typescript-eslint/no-shadow
const getSafeReleases = ({ features, releases }: GetInfoProps) => {
const {
bootloader_mode,
@@ -193,6 +195,7 @@ const getSafeReleases = ({ features, releases }: GetInfoProps) => {
* @param features
* @param releases
*/
+// eslint-disable-next-line @typescript-eslint/no-shadow
export const getInfo = ({ features, releases }: GetInfoProps): ReleaseInfo | null => {
if (!Array.isArray(releases) || releases.length < 1) {
// no available releases - should never happen for official firmware, only custom
diff --git a/packages/connect/src/data/models.ts b/packages/connect/src/data/models.ts
index c4cfe96d8e3..9882b244b46 100644
--- a/packages/connect/src/data/models.ts
+++ b/packages/connect/src/data/models.ts
@@ -29,4 +29,13 @@ export const models = {
'4': 'Bitcoin Orange',
},
},
+ T3W1: {
+ name: 'Trezor Safe 7',
+ colors: {
+ '1': 'Fantastic Ethereum', // TODO T3W1
+ '2': 'Lunatic Dogecoin', // TODO T3W1
+ '3': 'Galactic Litecoin', // TODO T3W1
+ '4': 'Majestic Bitcoin', // TODO T3W1
+ },
+ },
};
diff --git a/packages/connect/src/data/transportInfo.ts b/packages/connect/src/data/transportInfo.ts
index 7bae65e314d..ea1b1be5930 100644
--- a/packages/connect/src/data/transportInfo.ts
+++ b/packages/connect/src/data/transportInfo.ts
@@ -32,17 +32,17 @@ export const parseBridgeJSON = (json: any) => {
export const getBridgeInfo = (): BridgeInfo => info;
export const suggestBridgeInstaller = (platform?: string) => {
- const info = getBridgeInfo();
+ const info2 = getBridgeInfo();
// check if preferred field was already added
- if (!info.packages.find(p => p.preferred)) {
+ if (!info2.packages.find(p => p.preferred)) {
if (platform) {
// override BridgeInfo packages, add preferred field
- info.packages = info.packages.map(p => ({
+ info2.packages = info2.packages.map(p => ({
...p,
preferred: p.platform.indexOf(platform) >= 0,
}));
}
}
- return info;
+ return info2;
};
diff --git a/packages/connect/src/data/udevInfo.ts b/packages/connect/src/data/udevInfo.ts
index f5147e45884..75cd9f2fc87 100644
--- a/packages/connect/src/data/udevInfo.ts
+++ b/packages/connect/src/data/udevInfo.ts
@@ -24,17 +24,17 @@ const info: UdevInfo = {
export const getUdevInfo = () => info;
export const suggestUdevInstaller = (platform?: string) => {
- const info = getUdevInfo();
+ const info2 = getUdevInfo();
// check if preferred field was already added
- if (!info.packages.find(p => p.preferred)) {
+ if (!info2.packages.find(p => p.preferred)) {
if (platform) {
// override UdevInfo packages, add preferred field
- info.packages = info.packages.map(p => ({
+ info2.packages = info2.packages.map(p => ({
...p,
preferred: p.platform.indexOf(platform) >= 0,
}));
}
}
- return info;
+ return info2;
};
diff --git a/packages/connect/src/data/version.ts b/packages/connect/src/data/version.ts
index 2f5e69f32b4..9e247ebce7b 100644
--- a/packages/connect/src/data/version.ts
+++ b/packages/connect/src/data/version.ts
@@ -1,4 +1,4 @@
-export const VERSION = '9.4.2';
+export const VERSION = '9.4.3-beta.2';
const versionN = VERSION.split('.').map(s => parseInt(s, 10));
@@ -10,3 +10,5 @@ export const DEFAULT_DOMAIN = isBeta
// Increment with content script changes
export const CONTENT_SCRIPT_VERSION = 1;
+// Increment with deeplink protocol changes
+export const DEEPLINK_VERSION = 1;
diff --git a/packages/connect/src/device/Device.ts b/packages/connect/src/device/Device.ts
index 19a338d616d..60bc96b2b89 100644
--- a/packages/connect/src/device/Device.ts
+++ b/packages/connect/src/device/Device.ts
@@ -1,9 +1,19 @@
// original file https://github.com/trezor/connect/blob/develop/src/js/device/Device.js
-import { versionUtils, createDeferred, Deferred, TypedEmitter } from '@trezor/utils';
+import { randomBytes } from 'crypto';
+
+import {
+ versionUtils,
+ createDeferred,
+ Deferred,
+ TypedEmitter,
+ createTimeoutPromise,
+} from '@trezor/utils';
import { Session } from '@trezor/transport';
import { TransportProtocol, v1 as v1Protocol } from '@trezor/protocol';
+import { type Transport, type Descriptor, TRANSPORT_ERROR } from '@trezor/transport';
+
import { DeviceCommands } from './DeviceCommands';
-import { PROTO, ERRORS, NETWORK } from '../constants';
+import { PROTO, ERRORS, FIRMWARE } from '../constants';
import {
DEVICE,
DeviceButtonRequestPayload,
@@ -22,7 +32,6 @@ import {
ensureInternalModelFeature,
} from '../utils/deviceFeaturesUtils';
import { initLog } from '../utils/debug';
-import { type Transport, type Descriptor, TRANSPORT_ERROR } from '@trezor/transport';
import {
Device as DeviceTyped,
DeviceFirmwareStatus,
@@ -35,12 +44,16 @@ import {
VersionArray,
KnownDevice,
StaticSessionId,
+ FirmwareHashCheckResult,
+ FirmwareHashCheckError,
+ DeviceUniquePath,
} from '../types';
import { models } from '../data/models';
import { getLanguage } from '../data/getLanguage';
import { checkFirmwareRevision } from './checkFirmwareRevision';
import { IStateStorage } from './StateStorage';
import type { PromptCallback } from './prompts';
+import { calculateFirmwareHash, getBinaryOptional, stripFwHeaders } from '../api/firmware';
// custom log
const _log = initLog('Device');
@@ -91,182 +104,318 @@ export interface DeviceEvents {
) => void;
[DEVICE.PASSPHRASE_ON_DEVICE]: () => void;
[DEVICE.BUTTON]: (device: Device, payload: DeviceButtonRequestPayload) => void;
- [DEVICE.ACQUIRED]: () => void;
}
+type DeviceLifecycle =
+ | typeof DEVICE.CONNECT
+ | typeof DEVICE.CONNECT_UNACQUIRED
+ | typeof DEVICE.DISCONNECT
+ | typeof DEVICE.CHANGED;
+
+type DeviceLifecycleListener = (lifecycle: DeviceLifecycle) => void;
+
+type DeviceParams = {
+ id: DeviceUniquePath;
+ transport: Transport;
+ descriptor: Descriptor;
+ listener: DeviceLifecycleListener;
+};
+
/**
* @export
* @class Device
* @extends {EventEmitter}
*/
export class Device extends TypedEmitter {
- transport: Transport;
- protocol: TransportProtocol;
-
- originalDescriptor: Descriptor;
+ public readonly transport: Transport;
+ public readonly protocol: TransportProtocol;
+ private readonly transportPath;
+ private readonly transportSessionOwner;
+ private readonly transportDescriptorType;
+ private session;
+ private lastAcquiredHere;
/**
* descriptor was detected on transport layer but sending any messages (such as GetFeatures) to it failed either
* with some expected error, for example HID device, LIBUSB_ERROR, or it simply timeout out. such device can't be worked
* with and user needs to take some action. for example reconnect the device, update firmware or change transport type
*/
- unreadableError?: string;
+ private unreadableError?: string;
// @ts-expect-error: strictPropertyInitialization
- firmwareStatus: DeviceFirmwareStatus;
+ private _firmwareStatus: DeviceFirmwareStatus;
+ public get firmwareStatus() {
+ return this._firmwareStatus;
+ }
- firmwareRelease?: ReleaseInfo | null;
+ private _firmwareRelease?: ReleaseInfo | null;
+ public get firmwareRelease() {
+ return this._firmwareRelease;
+ }
// @ts-expect-error: strictPropertyInitialization
- features: Features;
+ private _features: Features;
+ public get features() {
+ return this._features;
+ }
- featuresNeedsReload = false;
+ private _featuresNeedsReload = false;
// variables used in one workflow: acquire -> transportSession -> commands -> run -> keepTransportSession -> release
private acquirePromise?: ReturnType;
private releasePromise?: ReturnType;
private runPromise?: Deferred;
- transportSession?: Session | null;
- keepTransportSession = false;
+
+ private keepTransportSession = false;
public commands?: DeviceCommands;
private cancelableAction?: (err?: Error) => Promise;
- loaded = false;
+ private loaded = false;
- inconsistent = false;
+ private inconsistent = false;
- firstRunPromise: Deferred;
- instance = 0;
+ private firstRunPromise: Deferred;
+ private instance = 0;
// DeviceState list [this.instance]: DeviceState | undefined
private state: DeviceState[] = [];
private stateStorage?: IStateStorage = undefined;
- unavailableCapabilities: UnavailableCapabilities = {};
-
- networkTypeState: NETWORK.NetworkType[] = [];
+ private _unavailableCapabilities: UnavailableCapabilities = {};
+ public get unavailableCapabilities(): Readonly {
+ return this._unavailableCapabilities;
+ }
- firmwareType?: FirmwareType;
+ private _firmwareType?: FirmwareType;
+ public get firmwareType() {
+ return this._firmwareType;
+ }
- name = 'Trezor';
+ private name = 'Trezor';
- color?: string;
+ private color?: string;
- availableTranslations: string[] = [];
+ private availableTranslations: string[] = [];
- authenticityChecks: NonNullable = {
+ private authenticityChecks: NonNullable = {
firmwareRevision: null,
+ firmwareHash: null,
};
- private useCardanoDerivation = false;
+ private readonly uniquePath;
+
+ private readonly emitLifecycle;
- constructor(transport: Transport, descriptor: Descriptor) {
+ private sessionDfd?: Deferred;
+
+ constructor({ id, transport, descriptor, listener }: DeviceParams) {
super();
+ this.emitLifecycle = listener;
this.protocol = v1Protocol;
// === immutable properties
+ this.uniquePath = id;
this.transport = transport;
- this.originalDescriptor = descriptor;
+ this.transportPath = descriptor.path;
+ this.transportSessionOwner = descriptor.sessionOwner;
+ this.transportDescriptorType = descriptor.type;
+
+ this.session = descriptor.session;
+ this.lastAcquiredHere = false;
// this will be released after first run
this.firstRunPromise = createDeferred();
}
- static fromDescriptor(transport: Transport, originalDescriptor: Descriptor) {
- const descriptor = { ...originalDescriptor, session: null };
- try {
- const device: Device = new Device(transport, descriptor);
-
- return device;
- } catch (error) {
- _log.error('Device.fromDescriptor', error);
- throw error;
+ private getSessionChangePromise() {
+ if (!this.sessionDfd) {
+ this.sessionDfd = createDeferred();
+ this.sessionDfd.promise.finally(() => {
+ this.sessionDfd = undefined;
+ });
}
- }
-
- static createUnacquired(
- transport: Transport,
- descriptor: Descriptor,
- unreadableError?: string,
- ) {
- const device = new Device(transport, descriptor);
- device.unreadableError = unreadableError;
- return device;
+ return this.sessionDfd.promise;
}
- async acquire() {
- this.acquirePromise = this.transport.acquire({
- input: {
- path: this.originalDescriptor.path,
- previous: this.originalDescriptor.session,
- },
- });
- const acquireResult = await this.acquirePromise;
- this.acquirePromise = undefined;
- if (!acquireResult.success) {
- if (this.runPromise) {
- this.runPromise.reject(new Error(acquireResult.error));
- delete this.runPromise;
+ private async waitAndCompareSession<
+ T extends { success: true; payload: Session | null } | { success: false },
+ >(response: T, sessionPromise: Promise) {
+ if (response.success) {
+ try {
+ if ((await sessionPromise) !== response.payload) {
+ return {
+ success: false,
+ error: TRANSPORT_ERROR.SESSION_WRONG_PREVIOUS,
+ } as const;
+ }
+ } catch {
+ return {
+ success: false,
+ error: TRANSPORT_ERROR.DEVICE_DISCONNECTED_DURING_ACTION,
+ } as const;
}
- throw acquireResult.error;
}
- const transportSession = acquireResult.payload;
+ return response;
+ }
+
+ acquire() {
+ const sessionPromise = this.getSessionChangePromise();
- _log.debug('Expected workflow id:', transportSession);
- this.transportSession = transportSession;
- // note: this.originalDescriptor is updated here and also in TRANSPORT.UPDATE listener.
- // I would like to update it only in one place (listener) but it some cases (unchained test),
- // listen response is not triggered by device acquire. not sure why.
- this.originalDescriptor.session = transportSession;
+ this.acquirePromise = this.transport
+ .acquire({ input: { path: this.transportPath, previous: this.session } })
+ .then(result => this.waitAndCompareSession(result, sessionPromise))
+ .then(result => {
+ if (result.success) {
+ this.session = result.payload;
+ this.lastAcquiredHere = true;
+
+ this.commands?.dispose();
+ this.commands = new DeviceCommands(this, this.transport, this.session);
+
+ return result;
+ } else {
+ if (this.runPromise) {
+ this.runPromise.reject(new Error(result.error));
+ delete this.runPromise;
+ }
+ throw result.error;
+ }
+ })
+ .finally(() => {
+ this.acquirePromise = undefined;
+ });
+
+ return this.acquirePromise;
+ }
+
+ async release() {
+ const localSession = this.getLocalSession();
+ if (!localSession || this.keepTransportSession || this.releasePromise) {
+ return;
+ }
if (this.commands) {
this.commands.dispose();
+ if (this.commands.callPromise) {
+ await this.commands.callPromise;
+ }
}
- this.commands = new DeviceCommands(this, this.transport, transportSession);
- }
- async release() {
- if (
- this.isUsedHere() &&
- this.transportSession &&
- !this.keepTransportSession &&
- !this.releasePromise
- ) {
- if (this.commands) {
- this.commands.dispose();
- if (this.commands.callPromise) {
- await this.commands.callPromise;
+ const sessionPromise = this.getSessionChangePromise();
+
+ this.releasePromise = this.transport
+ .release({ session: localSession, path: this.transportPath })
+ .then(result => this.waitAndCompareSession(result, sessionPromise))
+ .then(result => {
+ if (result.success) {
+ this.session = null;
}
- }
- this.releasePromise = this.transport.release({
- session: this.transportSession,
- path: this.originalDescriptor.path,
+ return result;
+ })
+ .finally(() => {
+ this.releasePromise = undefined;
});
- const releaseResponse = await this.releasePromise;
- this.releasePromise = undefined;
- if (releaseResponse.success) {
- this.transportSession = null;
- this.originalDescriptor.session = null;
- }
- }
+ return this.releasePromise;
+ }
+
+ releaseTransportSession() {
+ this.keepTransportSession = false;
}
async cleanup() {
- // remove all listeners **except** DEVICE.ACQUIRED - waiting for acquired Device in DeviceList
- const acquiredListeners = this.listeners(DEVICE.ACQUIRED);
- this.removeAllListeners();
+ // remove all listeners
+ this.eventNames().forEach(e => this.removeAllListeners(e as keyof DeviceEvents));
+
// make sure that Device_CallInProgress will not be thrown
delete this.runPromise;
await this.release();
- // restore DEVICE.ACQUIRED listeners
- acquiredListeners.forEach(l => this.once(DEVICE.ACQUIRED, l));
}
+ // call only once, right after device creation
+ async handshake(delay?: number) {
+ if (delay) {
+ await createTimeoutPromise(501 + delay);
+ }
+
+ while (true) {
+ if (this.isUsedElsewhere()) {
+ this.emitLifecycle(DEVICE.CONNECT_UNACQUIRED);
+ } else {
+ try {
+ await this.run();
+ } catch (error) {
+ if (
+ error.code === 'Device_NotFound' ||
+ error.message === TRANSPORT_ERROR.DEVICE_NOT_FOUND ||
+ error.message === TRANSPORT_ERROR.DEVICE_DISCONNECTED_DURING_ACTION ||
+ error.message === TRANSPORT_ERROR.UNEXPECTED_ERROR ||
+ error.message === TRANSPORT_ERROR.DESCRIPTOR_NOT_FOUND ||
+ error.message === TRANSPORT_ERROR.HTTP_ERROR // bridge died during device initialization
+ ) {
+ // disconnected, do nothing
+ } else if (
+ error.message === TRANSPORT_ERROR.SESSION_WRONG_PREVIOUS ||
+ error.code === 'Device_UsedElsewhere' // most common error - someone else took the device at the same time
+ ) {
+ // TODO needed only for TRANSPORT_ERROR.SESSION_WRONG_PREVIOUS
+ // this.enumerate(transport);
+ this.emitLifecycle(DEVICE.CONNECT_UNACQUIRED);
+ } else if (
+ // device was claimed by another application on transport api layer (claimInterface in usb nomenclature) but never released (releaseInterface in usb nomenclature)
+ // the only remedy for this is to reconnect device manually
+ // or possibly there are 2 applications without common sessions background
+ error.message === TRANSPORT_ERROR.INTERFACE_UNABLE_TO_OPEN_DEVICE ||
+ // catch one of trezord LIBUSB_ERRORs
+ error.message?.indexOf(ERRORS.LIBUSB_ERROR_MESSAGE) >= 0 ||
+ // we tried to initialize device (either automatically after enumeration or after user click)
+ // but it did not work out. this device is effectively unreadable and user should do something about it
+ error.code === 'Device_InitializeFailed'
+ ) {
+ this.unreadableError = error?.message;
+ this.emitLifecycle(DEVICE.CONNECT_UNACQUIRED);
+ } else {
+ await createTimeoutPromise(501);
+ continue;
+ }
+ }
+ }
+
+ return;
+ }
+ }
+
+ async updateDescriptor(descriptor: Descriptor) {
+ this.sessionDfd?.resolve(descriptor.session);
+
+ await Promise.all([this.acquirePromise, this.releasePromise]);
+
+ // TODO improve these conditions
+
+ // Session changed to different than the current one
+ // -> acquired by someone else
+ if (descriptor.session && descriptor.session !== this.session) {
+ this.usedElsewhere();
+ }
+
+ // Session changed to null
+ // -> released
+ if (!descriptor.session) {
+ const methodStillRunning = !this.commands?.isDisposed();
+ if (methodStillRunning) {
+ this.releaseTransportSession();
+ }
+ }
+
+ this.session = descriptor.session;
+ this.emitLifecycle(DEVICE.CHANGED);
+ }
+
+ // TODO empty fn variant can be split/removed
run(fn?: () => Promise, options?: RunOptions) {
if (this.runPromise) {
_log.warn('Previous call is still running');
@@ -275,12 +424,19 @@ export class Device extends TypedEmitter {
options = parseRunOptions(options);
+ const wasUnacquired = this.isUnacquired();
const runPromise = createDeferred();
this.runPromise = runPromise;
- this._runInner(fn, options).catch(err => {
- runPromise.reject(err);
- });
+ this._runInner(fn, options)
+ .then(() => {
+ if (wasUnacquired && !this.isUnacquired()) {
+ this.emitLifecycle(DEVICE.CONNECT);
+ }
+ })
+ .catch(err => {
+ runPromise.reject(err);
+ });
return runPromise.promise;
}
@@ -301,8 +457,8 @@ export class Device extends TypedEmitter {
setCancelableAction(callback: NonNullable) {
this.cancelableAction = (e?: Error) =>
callback(e)
- .catch(e => {
- _log.debug('cancelableAction error', e);
+ .catch(e2 => {
+ _log.debug('cancelableAction error', e2);
})
.finally(() => {
this.clearCancelableAction();
@@ -326,14 +482,14 @@ export class Device extends TypedEmitter {
}
}
- /**
- * TODO: this does not work properly (even before transport-refactor)
- * one of the problem here is, that this.runPromise.reject is caught in src/core finally block that triggers
- * device release. This is not right because we know that somebody else has already taken control of device
- * which means that session management does not make sense anymore. releasing device, on the other hand
- * makes sense, because this instance of connect might be the only one who has the right to do it.
- */
- interruptionFromOutside() {
+ public usedElsewhere() {
+ // only makes sense to continue when device held by this instance
+ if (!this.lastAcquiredHere) {
+ return;
+ }
+ this.lastAcquiredHere = false;
+ this._featuresNeedsReload = true;
+
_log.debug('interruptionFromOutside');
if (this.commands) {
@@ -347,8 +503,8 @@ export class Device extends TypedEmitter {
// session was acquired by another instance. but another might not have power to release interface
// so it only notified about its session acquiral and the interrupted instance should cooperate
// and release device too.
- if (this.originalDescriptor.session) {
- this.transport.releaseDevice(this.originalDescriptor.session);
+ if (this.session) {
+ this.transport.releaseDevice(this.session);
}
}
@@ -360,15 +516,14 @@ export class Device extends TypedEmitter {
await this.releasePromise;
}
- if (
- !this.isUsedHere() ||
- this.commands?.disposed ||
- !this.getState()?.staticSessionId ||
- this.useCardanoDerivation != !!options.useCardanoDerivation
- ) {
+ const acquireNeeded = !this.isUsedHere() || this.commands?.disposed;
+ if (acquireNeeded) {
// acquire session
await this.acquire();
+ }
+ const { staticSessionId, deriveCardano } = this.getState() || {};
+ if (acquireNeeded || !staticSessionId || (!deriveCardano && options.useCardanoDerivation)) {
// update features
try {
if (fn) {
@@ -434,12 +589,6 @@ export class Device extends TypedEmitter {
this.keepTransportSession = true;
}
- // if we were waiting for device to be acquired, it should be guaranteed here that it had already happened
- // (features are reloaded too)
- if (this.listeners(DEVICE.ACQUIRED).length > 0) {
- this.emit(DEVICE.ACQUIRED);
- }
-
// call inner function
if (fn) {
await fn();
@@ -484,7 +633,7 @@ export class Device extends TypedEmitter {
// and device wasn't released in previous call (example: interrupted discovery which set "keepSession" to true but never released)
// clear "keepTransportSession" and reset "transportSession" to ensure that "initialize" will be called
if (this.keepTransportSession) {
- this.transportSession = null;
+ this.lastAcquiredHere = false;
this.keepTransportSession = false;
}
}
@@ -543,12 +692,12 @@ export class Device extends TypedEmitter {
async initialize(useCardanoDerivation: boolean) {
let payload: PROTO.Initialize | undefined;
if (this.features) {
- const sessionId = this.getState()?.sessionId;
- payload = {};
+ const { sessionId, deriveCardano } = this.getState() || {};
// If the user has BIP-39 seed, and Initialize(derive_cardano=True) is not sent,
// all Cardano calls will fail because the root secret will not be available.
- payload.derive_cardano = useCardanoDerivation;
- this.useCardanoDerivation = useCardanoDerivation;
+ payload = {
+ derive_cardano: deriveCardano || useCardanoDerivation,
+ };
if (sessionId) {
payload.session_id = sessionId;
}
@@ -556,6 +705,7 @@ export class Device extends TypedEmitter {
const { message } = await this.getCommands().typedCall('Initialize', 'Features', payload);
this._updateFeatures(message);
+ this.setState({ deriveCardano: payload?.derive_cardano });
}
initStorage(storage: IStateStorage) {
@@ -567,6 +717,10 @@ export class Device extends TypedEmitter {
const { message } = await this.getCommands().typedCall('GetFeatures', 'Features', {});
this._updateFeatures(message);
+ if (this.authenticityChecks.firmwareHash === null) {
+ this.authenticityChecks.firmwareHash = await this.checkFirmwareHash();
+ }
+
if (
// The check was not yet performed
this.authenticityChecks.firmwareRevision === null ||
@@ -595,6 +749,68 @@ export class Device extends TypedEmitter {
}
}
+ async checkFirmwareHash(): Promise {
+ const createFailResult = (error: FirmwareHashCheckError) => ({ success: false, error });
+
+ const baseUrl = DataManager.getSettings('binFilesBaseUrl');
+ const enabled = DataManager.getSettings('enableFirmwareHashCheck');
+ if (!enabled || baseUrl === undefined) return createFailResult('check-skipped');
+
+ const firmwareVersion = this.getVersion();
+ // device has no features (not yet connected) or no firmware
+ if (firmwareVersion === undefined || !this.features || this.features.bootloader_mode) {
+ return null;
+ }
+
+ const checkSupported = this.atLeast(FIRMWARE.FW_HASH_SUPPORTED_VERSIONS);
+ if (!checkSupported) return createFailResult('check-unsupported');
+
+ const release = getReleases(this.features.internal_model).find(r =>
+ versionUtils.isEqual(r.version, firmwareVersion),
+ );
+ // if version is expected to support hash check, but the release is unknown, then firmware is considered unofficial
+ if (release === undefined) return createFailResult('unknown-release');
+
+ const btcOnly = this.firmwareType === FirmwareType.BitcoinOnly;
+ const binary = await getBinaryOptional({ baseUrl, btcOnly, release });
+ // release was found, but not its binary - happens on desktop, where only local files are searched
+ if (binary === null) {
+ return createFailResult('check-unsupported');
+ }
+ // binary was found, but it's likely a git LFS pointer (can happen on dev) - see onCallFirmwareUpdate.ts
+ if (binary.byteLength < 200) {
+ _log.warn(`Firmware binary for hash check suspiciously small (< 200 b)`);
+
+ return createFailResult('check-unsupported');
+ }
+
+ const strippedBinary = stripFwHeaders(binary);
+ const { hash: expectedHash, challenge } = calculateFirmwareHash(
+ this.features.major_version,
+ strippedBinary,
+ randomBytes(32),
+ );
+
+ // handle rejection of call by a counterfeit device. If unhandled, it crashes device initialization,
+ // so device can't be used, but it's preferable to display proper message about counterfeit device
+ const getFirmwareHashOptional = async () => {
+ try {
+ return await this.getCommands().typedCall('GetFirmwareHash', 'FirmwareHash', {
+ challenge,
+ });
+ } catch {
+ return null;
+ }
+ };
+ const deviceResponse = await getFirmwareHashOptional();
+
+ if (!deviceResponse?.message?.hash) return createFailResult('other-error');
+
+ if (deviceResponse.message.hash !== expectedHash) return createFailResult('hash-mismatch');
+
+ return { success: true };
+ }
+
async checkFirmwareRevision() {
const firmwareVersion = this.getVersion();
@@ -724,24 +940,24 @@ export class Device extends TypedEmitter {
// check if FW version or capabilities did change
if (!version || !versionUtils.isEqual(version, newVersion)) {
- this.unavailableCapabilities = getUnavailableCapabilities(feat, getAllNetworks());
- this.firmwareStatus = getFirmwareStatus(feat);
- this.firmwareRelease = getRelease(feat);
+ this._unavailableCapabilities = getUnavailableCapabilities(feat, getAllNetworks());
+ this._firmwareStatus = getFirmwareStatus(feat);
+ this._firmwareRelease = getRelease(feat);
this.availableTranslations = this.firmwareRelease?.translations ?? [];
}
- this.features = feat;
- this.featuresNeedsReload = false;
+ this._features = feat;
+ this._featuresNeedsReload = false;
// Vendor headers have been changed in 2.6.3.
if (feat.fw_vendor === 'Trezor Bitcoin-only') {
- this.firmwareType = FirmwareType.BitcoinOnly;
+ this._firmwareType = FirmwareType.BitcoinOnly;
} else if (feat.fw_vendor === 'Trezor') {
- this.firmwareType = FirmwareType.Regular;
+ this._firmwareType = FirmwareType.Regular;
} else if (this.getMode() !== 'bootloader') {
// Relevant for T1B1, T2T1 and custom firmware with a different vendor header. Capabilities do not work in bootloader mode.
- this.firmwareType =
+ this._firmwareType =
feat.capabilities &&
feat.capabilities.length > 0 &&
!feat.capabilities.includes('Capability_Bitcoin_like')
@@ -770,11 +986,19 @@ export class Device extends TypedEmitter {
return this.features === undefined;
}
+ isUnreadable() {
+ return !!this.unreadableError;
+ }
+
disconnect() {
// TODO: cleanup everything
_log.debug('Disconnect cleanup');
- this.transportSession = null; // set to null to prevent transport.release and cancelableAction
+ this.sessionDfd?.reject(new Error());
+
+ this.lastAcquiredHere = false; // set to null to prevent transport.release and cancelableAction
+
+ this.emitLifecycle(DEVICE.DISCONNECT);
return this.interruptionFromUser(ERRORS.TypedError('Device_Disconnected'));
}
@@ -815,15 +1039,15 @@ export class Device extends TypedEmitter {
}
isUsed() {
- return typeof this.originalDescriptor.session === 'string';
+ return typeof this.session === 'string';
}
isUsedHere() {
- return this.isUsed() && this.originalDescriptor.session === this.transportSession;
+ return this.isUsed() && this.lastAcquiredHere;
}
isUsedElsewhere() {
- return this.isUsed() && !this.isUsedHere();
+ return this.isUsed() && !this.lastAcquiredHere;
}
isRunning() {
@@ -838,8 +1062,12 @@ export class Device extends TypedEmitter {
return this.firstRunPromise.promise;
}
- getDevicePath() {
- return this.originalDescriptor.path;
+ getLocalSession() {
+ return this.lastAcquiredHere ? this.session : null;
+ }
+
+ getUniquePath() {
+ return this.uniquePath;
}
isT1() {
@@ -869,28 +1097,19 @@ export class Device extends TypedEmitter {
return null;
}
- updateDescriptor(descriptor: Descriptor) {
- this.originalDescriptor = {
- session: descriptor.session,
- path: descriptor.path,
- product: descriptor.product,
- type: descriptor.type,
- };
- }
-
async dispose() {
this.removeAllListeners();
- if (this.isUsedHere() && this.transportSession) {
+ if (this.session && this.lastAcquiredHere) {
try {
await this.cancelableAction?.();
await this.commands?.cancel();
return this.transport.release({
- session: this.transportSession,
- path: this.originalDescriptor.path,
+ session: this.session,
+ path: this.transportPath,
onClose: true,
});
- } catch (err) {
+ } catch {
// empty
}
}
@@ -906,39 +1125,42 @@ export class Device extends TypedEmitter {
// simplified object to pass via postMessage
toMessageObject(): DeviceTyped {
+ const { name, uniquePath: path } = this;
+ const base = { path, name };
+
if (this.unreadableError) {
return {
+ ...base,
type: 'unreadable',
- path: this.originalDescriptor.path,
error: this.unreadableError, // provide error details
label: 'Unreadable device',
- name: this.name,
+ transportDescriptorType: this.transportDescriptorType,
};
}
if (this.isUnacquired()) {
return {
+ ...base,
type: 'unacquired',
- path: this.originalDescriptor.path,
label: 'Unacquired device',
name: this.name,
+ transportSessionOwner: this.transportSessionOwner,
};
}
const defaultLabel = 'My Trezor';
const label =
this.features.label === '' || !this.features.label ? defaultLabel : this.features.label;
let status: DeviceStatus = this.isUsedElsewhere() ? 'occupied' : 'available';
- if (this.featuresNeedsReload) status = 'used';
+ if (this._featuresNeedsReload) status = 'used';
return {
+ ...base,
type: 'acquired',
id: this.features.device_id,
- path: this.originalDescriptor.path,
label,
_state: this.getState(),
state: this.getState()?.staticSessionId,
status,
mode: this.getMode(),
- name: this.name,
color: this.color,
firmware: this.firmwareStatus,
firmwareRelease: this.firmwareRelease,
diff --git a/packages/connect/src/device/DeviceCommands.ts b/packages/connect/src/device/DeviceCommands.ts
index 64a125c1b5f..ae99de4c7e3 100644
--- a/packages/connect/src/device/DeviceCommands.ts
+++ b/packages/connect/src/device/DeviceCommands.ts
@@ -1,9 +1,12 @@
// original file https://github.com/trezor/connect/blob/develop/src/js/device/DeviceCommands.js
import { randomBytes } from 'crypto';
+
import { Transport, Session } from '@trezor/transport';
import { MessagesSchema as Messages } from '@trezor/protobuf';
import { createTimeoutPromise, versionUtils } from '@trezor/utils';
+import { Assert } from '@trezor/schema-utils';
+
import { ERRORS } from '../constants';
import { DEVICE } from '../events';
import * as hdnodeUtils from '../utils/hdnodeUtils';
@@ -11,11 +14,9 @@ import { isTaprootPath, getSerializedPath, getScriptType, toHardened } from '../
import { getAccountAddressN } from '../utils/accountUtils';
import { getSegwitNetwork, getBech32Network } from '../data/coinInfo';
import { initLog } from '../utils/debug';
-
import { Device } from './Device';
import type { CoinInfo, BitcoinNetworkInfo, Network } from '../types';
import type { HDNodeResponse } from '../types/api/getPublicKey';
-import { Assert } from '@trezor/schema-utils';
import { resolveDescriptorForTaproot } from './resolveDescriptorForTaproot';
import { promptPin, promptPassphrase, promptWord, cancelPrompt } from './prompts';
@@ -45,7 +46,7 @@ const assertType = (res: DefaultPayloadMessage, resType: MessageKey | MessageKey
const generateEntropy = (len: number) => {
try {
return randomBytes(len);
- } catch (err) {
+ } catch {
throw ERRORS.TypedError(
'Runtime',
'generateEntropy: Environment does not support crypto random',
@@ -373,10 +374,10 @@ export class DeviceCommands {
}
async _commonCall(type: MessageKey, msg?: DefaultPayloadMessage['message']) {
- const resp = await this.call(type, msg);
if (this.disposed) {
throw ERRORS.TypedError('Runtime', 'typedCall: DeviceCommands already disposed');
}
+ const resp = await this.call(type, msg);
return this._filterCommonTypes(resp);
}
@@ -563,7 +564,7 @@ export class DeviceCommands {
await createTimeoutPromise(1);
await this.device.acquire();
await cancelPrompt(this.device, false);
- } catch (err) {
+ } catch {
// ignore whatever happens
}
} else {
diff --git a/packages/connect/src/device/DeviceList.ts b/packages/connect/src/device/DeviceList.ts
index 86277a773dc..5160eece0ab 100644
--- a/packages/connect/src/device/DeviceList.ts
+++ b/packages/connect/src/device/DeviceList.ts
@@ -1,6 +1,6 @@
// original file https://github.com/trezor/connect/blob/develop/src/js/device/DeviceList.js
-import { TypedEmitter, Deferred, createDeferred, promiseAllSequence } from '@trezor/utils';
+import { TypedEmitter, createDeferred, getSynchronize } from '@trezor/utils';
import {
BridgeTransport,
WebUsbTransport,
@@ -8,19 +8,18 @@ import {
UdpTransport,
Transport,
TRANSPORT,
- Descriptor,
- TRANSPORT_ERROR,
isTransportInstance,
- DeviceDescriptorDiff,
} from '@trezor/transport';
+import { Descriptor, PathPublic } from '@trezor/transport/src/types';
+
import { ERRORS } from '../constants';
import { DEVICE, TransportInfo } from '../events';
import { Device } from './Device';
-import type { ConnectSettings, Device as DeviceTyped } from '../types';
-
+import { ConnectSettings, DeviceUniquePath, Device as DeviceTyped } from '../types';
import { getBridgeInfo } from '../data/transportInfo';
import { initLog } from '../utils/debug';
import { resolveAfter } from '../utils/promiseUtils';
+import { typedObjectKeys } from '../types/utils';
// custom log
const _log = initLog('DeviceList');
@@ -60,8 +59,6 @@ interface DeviceListEvents {
[DEVICE.CONNECT_UNACQUIRED]: DeviceTyped;
[DEVICE.DISCONNECT]: DeviceTyped;
[DEVICE.CHANGED]: DeviceTyped;
- [DEVICE.RELEASED]: DeviceTyped;
- [DEVICE.ACQUIRED]: DeviceTyped;
}
export interface IDeviceList {
@@ -82,7 +79,10 @@ export const assertDeviceListConnected: (
if (!deviceList.isConnected()) throw ERRORS.TypedError('Transport_Missing');
};
-type ConstructorParams = Pick & {
+type ConstructorParams = Pick<
+ ConnectSettings,
+ 'priority' | 'debug' | '_sessionsBackgroundUrl' | 'manifest'
+> & {
messages: Record;
};
type InitParams = Pick;
@@ -94,11 +94,10 @@ export class DeviceList extends TypedEmitter implements IDevic
// array of transport that might be used in this environment
private transports: Transport[];
- private devices: { [path: string]: Device } = {};
-
- private creatingDevicesDescriptors: { [k: string]: Descriptor } = {};
- private createDevicesQueue: Deferred[] = [];
+ private devices: Record = {};
+ private deviceCounter = Date.now();
+ private readonly handshakeLock;
private readonly authPenaltyManager;
private initPromise?: Promise;
@@ -115,16 +114,24 @@ export class DeviceList extends TypedEmitter implements IDevic
return this.initPromise;
}
- constructor({ messages, priority, debug, _sessionsBackgroundUrl }: ConstructorParams) {
+ constructor({
+ messages,
+ priority,
+ debug,
+ _sessionsBackgroundUrl,
+ manifest,
+ }: ConstructorParams) {
super();
const transportLogger = initLog('@trezor/transport', debug);
+ this.handshakeLock = getSynchronize();
this.authPenaltyManager = createAuthPenaltyManager(priority);
this.transportCommonArgs = {
messages,
logger: transportLogger,
sessionsBackgroundUrl: _sessionsBackgroundUrl,
+ id: manifest?.appUrl || 'unknown app',
};
this.transports = [
@@ -182,120 +189,46 @@ export class DeviceList extends TypedEmitter implements IDevic
this.transports = transportTypes.map(this.createTransport.bind(this));
}
- private onTransportUpdate(diff: DeviceDescriptorDiff, transport: Transport) {
- diff.forEach(async ({ descriptor, ...category }) => {
- // whenever descriptors change we need to update them so that we can use them
- // in subsequent transport.acquire calls
- const path = descriptor.path.toString();
- const device = this.devices[path] as Device | undefined;
-
- // creatingDevicesDescriptors is needed, so that if *during* creating of Device,
- // other application acquires the device and changes the descriptor,
- // the new unacquired device has correct descriptor
- this.creatingDevicesDescriptors[path] = descriptor;
-
- switch (category.type) {
- case 'disconnected':
- this.removeFromCreateDevicesQueue(path);
-
- if (!device) break;
-
- device.disconnect();
- delete this.devices[path];
- this.emit(DEVICE.DISCONNECT, device.toMessageObject());
- break;
-
- case 'connected':
- if (!(await this.waitForCreateDevicesQueue(path))) {
- break;
- }
-
- const penalty = this.authPenaltyManager.get();
-
- if (penalty) {
- await resolveAfter(501 + penalty, null).promise;
- }
- if (this.creatingDevicesDescriptors[path].session == null) {
- await this._createAndSaveDevice(descriptor, transport);
- } else {
- const device = this._createUnacquiredDevice(descriptor, transport);
- this.devices[path] = device;
- this.emit(DEVICE.CONNECT_UNACQUIRED, device.toMessageObject());
- }
-
- this.removeFromCreateDevicesQueue(path);
- break;
-
- case 'acquired':
- if (category.subtype === 'elsewhere') {
- this.removeFromCreateDevicesQueue(path);
- if (device) {
- device.featuresNeedsReload = true;
- device.interruptionFromOutside();
- }
- }
-
- if (!device) break;
-
- _log.debug('Event', DEVICE.CHANGED, device.toMessageObject());
- this.emit(DEVICE.CHANGED, device.toMessageObject());
-
- _log.debug('Event', DEVICE.ACQUIRED, device.toMessageObject());
- this.emit(DEVICE.ACQUIRED, device.toMessageObject());
-
- break;
-
- case 'released':
- if (!device) break;
-
- const methodStillRunning = !device.commands?.isDisposed();
- if (methodStillRunning) {
- device.keepTransportSession = false;
- }
-
- _log.debug('Event', DEVICE.CHANGED, device.toMessageObject());
- this.emit(DEVICE.CHANGED, device.toMessageObject());
-
- _log.debug('Event', DEVICE.RELEASED, device.toMessageObject());
- this.emit(DEVICE.RELEASED, device.toMessageObject());
-
- if (category.subtype === 'elsewhere') {
- await resolveAfter(1000, null).promise;
- // after device was released in another window wait for a while (the other window might
- // have the intention of acquiring it again)
- // and if the device is still released and has never been acquired before, acquire it here.
- if (!device.isUsed() && device.isUnacquired() && !device.isInconsistent()) {
- _log.debug('Create device from unacquired', device.toMessageObject());
- await this._createAndSaveDevice(descriptor, transport);
- }
- }
- break;
- }
+ private onDeviceConnected(descriptor: Descriptor, transport: Transport) {
+ const { path } = descriptor;
+ const id = (this.deviceCounter++).toString(16).slice(-8);
+ const device = new Device({
+ id: DeviceUniquePath(id),
+ transport,
+ descriptor,
+ listener: lifecycle => this.emit(lifecycle, device.toMessageObject()),
+ });
+ this.devices[path] = device;
- device?.updateDescriptor(descriptor);
+ const penalty = this.authPenaltyManager.get();
+ this.handshakeLock(async () => {
+ if (this.devices[path]) {
+ // device wasn't removed while waiting for lock
+ await device.handshake(penalty);
+ }
});
}
- private async waitForCreateDevicesQueue(path: string) {
- const dfd = createDeferred(path);
- const prevQueue = this.createDevicesQueue.slice();
- this.createDevicesQueue.push(dfd);
-
- await promiseAllSequence(prevQueue.map(pr => () => pr.promise));
-
- // Return whether current pending action still in queue or it was
- // removed by disconnected/acquiredElsewhere events or dispose
- return this.createDevicesQueue.includes(dfd);
+ private onDeviceDisconnected(descriptor: Descriptor) {
+ const { path } = descriptor;
+ const device = this.devices[path];
+ if (device) {
+ device.disconnect();
+ delete this.devices[path];
+ }
}
- private removeFromCreateDevicesQueue(path: string) {
- const index = this.createDevicesQueue.findIndex(dfd => dfd.id === path);
- if (index >= 0) {
- const [dfd] = this.createDevicesQueue.splice(index, 1);
- dfd.resolve();
+ private onDeviceSessionChanged(descriptor: Descriptor) {
+ const device = this.devices[descriptor.path];
+ if (device) {
+ device.updateDescriptor(descriptor);
}
}
+ private onDeviceRequestRelease(descriptor: Descriptor) {
+ this.devices[descriptor.path]?.usedElsewhere();
+ }
+
/**
* Init @trezor/transport and do something with its results
*/
@@ -330,7 +263,9 @@ export class DeviceList extends TypedEmitter implements IDevic
const { promise, reject } = resolveAfter(1000, initParams);
this.rejectPending = reject;
- return promise.then(this.createInitPromise.bind(this));
+ return promise.then(this.createInitPromise.bind(this)).finally(() => {
+ this.rejectPending = undefined;
+ });
}
private async selectTransport([transport, ...rest]: Transport[]): Promise {
@@ -349,7 +284,10 @@ export class DeviceList extends TypedEmitter implements IDevic
* releasing/acquiring device by this application is not solved here but directly
* where transport.acquire, transport.release is called
*/
- transport.on(TRANSPORT.UPDATE, diff => this.onTransportUpdate(diff, transport));
+ transport.on(TRANSPORT.DEVICE_CONNECTED, d => this.onDeviceConnected(d, transport));
+ transport.on(TRANSPORT.DEVICE_DISCONNECTED, this.onDeviceDisconnected.bind(this));
+ transport.on(TRANSPORT.DEVICE_SESSION_CHANGED, this.onDeviceSessionChanged.bind(this));
+ transport.on(TRANSPORT.DEVICE_REQUEST_RELEASE, this.onDeviceRequestRelease.bind(this));
// just like transport emits updates, it may also start producing errors, for example bridge process crashes.
transport.on(TRANSPORT.ERROR, error => {
@@ -375,7 +313,6 @@ export class DeviceList extends TypedEmitter implements IDevic
? this.waitForDevices(descriptors.length, 10000)
: Promise.resolve();
- // TODO handleDescriptorChange can emit TRANSPORT.UPDATE before TRANSPORT.START is emitted, check whether acceptable
transport.handleDescriptorsChange(descriptors);
transport.listen();
@@ -417,50 +354,20 @@ export class DeviceList extends TypedEmitter implements IDevic
});
}
- private async _createAndSaveDevice(descriptor: Descriptor, transport: Transport) {
- _log.debug('Creating Device', descriptor);
- await this.handle(descriptor, transport);
- }
-
- private _createUnacquiredDevice(descriptor: Descriptor, transport: Transport) {
- _log.debug('Creating Unacquired Device', descriptor);
- const device = Device.createUnacquired(transport, descriptor);
- device.once(DEVICE.ACQUIRED, () => {
- // emit connect event once device becomes acquired
- this.emit(DEVICE.CONNECT, device.toMessageObject());
- });
-
- return device;
- }
-
- private _createUnreadableDevice(
- descriptor: Descriptor,
- transport: Transport,
- unreadableError: string,
- ) {
- _log.debug('Creating Unreadable Device', descriptor, unreadableError);
-
- return Device.createUnacquired(transport, descriptor, unreadableError);
- }
-
- getDevice(path: string) {
- return this.devices[path];
+ getDeviceCount() {
+ return Object.keys(this.devices).length;
}
- getFirstDevicePath() {
- return this.asArray()[0].path;
+ getAllDevices(): Device[] {
+ return typedObjectKeys(this.devices).map(key => this.devices[key]);
}
- asArray(): DeviceTyped[] {
- return this.allDevices().map(device => device.toMessageObject());
+ getOnlyDevice(): Device | undefined {
+ return this.getDeviceCount() === 1 ? Object.values(this.devices)[0] : undefined;
}
- allDevices(): Device[] {
- return Object.keys(this.devices).map(key => this.devices[key]);
- }
-
- length() {
- return this.asArray().length;
+ getDeviceByPath(path: DeviceUniquePath): Device | undefined {
+ return this.getAllDevices().find(d => d.getUniquePath() === path);
}
transportType() {
@@ -483,12 +390,12 @@ export class DeviceList extends TypedEmitter implements IDevic
async cleanup() {
const { transport } = this;
- const devices = this.allDevices();
+ const devices = this.getAllDevices();
// @ts-expect-error will be fixed later
this.transport = undefined;
this.authPenaltyManager.clear();
- Object.keys(this.devices).forEach(key => delete this.devices[key]);
+ typedObjectKeys(this.devices).forEach(key => delete this.devices[key]);
this.rejectPending?.(new Error('Disposed'));
@@ -498,9 +405,6 @@ export class DeviceList extends TypedEmitter implements IDevic
this.emit(DEVICE.DISCONNECT, device.toMessageObject());
});
- this.createDevicesQueue.forEach(dfd => dfd.resolve());
- this.createDevicesQueue = [];
-
// release all devices
await Promise.all(devices.map(device => device.dispose()));
@@ -518,11 +422,7 @@ export class DeviceList extends TypedEmitter implements IDevic
}
res.payload.forEach(d => {
- if (this.devices[d.path]) {
- this.devices[d.path].updateDescriptor(d);
- // TODO: is this ok? transportSession should be set only as result of acquire/release
- // this.devices[d.path].transportSession = d.session;
- }
+ this.devices[d.path]?.updateDescriptor(d);
});
}
@@ -533,80 +433,4 @@ export class DeviceList extends TypedEmitter implements IDevic
removeAuthPenalty(device: Device) {
return this.authPenaltyManager.remove(device);
}
-
- // main logic
- private async handle(descriptor: Descriptor, transport: Transport) {
- const path = descriptor.path.toString();
- try {
- // "regular" device creation
- await this._takeAndCreateDevice(descriptor, transport);
- } catch (error) {
- _log.debug('Cannot create device', error);
- if (
- error.code === 'Device_NotFound' ||
- error.message === TRANSPORT_ERROR.DEVICE_NOT_FOUND ||
- error.message === TRANSPORT_ERROR.DEVICE_DISCONNECTED_DURING_ACTION ||
- error.message === TRANSPORT_ERROR.UNEXPECTED_ERROR ||
- error.message === TRANSPORT_ERROR.DESCRIPTOR_NOT_FOUND ||
- // bridge died during device initialization
- error.message === TRANSPORT_ERROR.HTTP_ERROR
- ) {
- // do nothing
- // For example:
- // 1. connect device
- // 2. _createAndSaveDevice => handle => _takeAndCreateDevice => device.run()
- // 3. disconnect device
- // 4. some of the above mentioned errors is returned.
- delete this.devices[path];
- } else if (error.message === TRANSPORT_ERROR.SESSION_WRONG_PREVIOUS) {
- this.enumerate(transport);
- this._handleUsedElsewhere(descriptor, transport);
- } else if (
- // device was claimed by another application on transport api layer (claimInterface in usb nomenclature) but never released (releaseInterface in usb nomenclature)
- // the only remedy for this is to reconnect device manually
- // or possibly there are 2 applications without common sessions background
- error.message === TRANSPORT_ERROR.INTERFACE_UNABLE_TO_OPEN_DEVICE ||
- // catch one of trezord LIBUSB_ERRORs
- error.message?.indexOf(ERRORS.LIBUSB_ERROR_MESSAGE) >= 0 ||
- // we tried to initialize device (either automatically after enumeration or after user click)
- // but it did not work out. this device is effectively unreadable and user should do something about it
- error.code === 'Device_InitializeFailed'
- ) {
- const device = this._createUnreadableDevice(
- this.creatingDevicesDescriptors[path],
- transport,
- error.message,
- );
- this.devices[path] = device;
- this.emit(DEVICE.CONNECT_UNACQUIRED, device.toMessageObject());
- } else if (error.code === 'Device_UsedElsewhere') {
- // most common error - someone else took the device at the same time
- this._handleUsedElsewhere(descriptor, transport);
- } else {
- await resolveAfter(501, null).promise;
- await this.handle(descriptor, transport);
- }
- }
- }
-
- private async _takeAndCreateDevice(descriptor: Descriptor, transport: Transport) {
- const device = Device.fromDescriptor(transport, descriptor);
- const path = descriptor.path.toString();
- this.devices[path] = device;
- const promise = device.run();
- await promise;
-
- this.emit(DEVICE.CONNECT, device.toMessageObject());
- }
-
- private _handleUsedElsewhere(descriptor: Descriptor, transport: Transport) {
- const path = descriptor.path.toString();
-
- const device = this._createUnacquiredDevice(
- this.creatingDevicesDescriptors[path],
- transport,
- );
- this.devices[path] = device;
- this.emit(DEVICE.CONNECT_UNACQUIRED, device.toMessageObject());
- }
}
diff --git a/packages/connect/src/device/StateStorage.ts b/packages/connect/src/device/StateStorage.ts
index f030c0c0007..7b13cd5a794 100644
--- a/packages/connect/src/device/StateStorage.ts
+++ b/packages/connect/src/device/StateStorage.ts
@@ -1,6 +1,7 @@
+import { storage } from '@trezor/connect-common';
+
import { DeviceState } from '../types';
import { Device } from './Device';
-import { storage } from '@trezor/connect-common';
export interface IStateStorage {
saveState(device: Device, state: DeviceState): void;
diff --git a/packages/connect/src/device/__tests__/DeviceList.test.ts b/packages/connect/src/device/__tests__/DeviceList.test.ts
index 696367e7f47..9c90023a4d0 100644
--- a/packages/connect/src/device/__tests__/DeviceList.test.ts
+++ b/packages/connect/src/device/__tests__/DeviceList.test.ts
@@ -21,13 +21,7 @@ const waitForNthEventOfType = (
});
};
-const DEVICE_CONNECTION_SEQUENCE = [
- 'device-changed',
- 'device-acquired',
- 'device-changed',
- 'device-released',
- 'device-connect',
-];
+const DEVICE_CONNECTION_SEQUENCE = ['device-changed', 'device-changed', 'device-connect'];
describe('DeviceList', () => {
beforeAll(async () => {
@@ -56,11 +50,9 @@ describe('DeviceList', () => {
[
'transport-start',
'transport-error',
- 'device-changed',
'device-connect',
'device-connect_unacquired',
- 'device-acquired',
- 'device-released',
+ 'device-changed',
'device-disconnect',
] as const
).forEach(event => {
@@ -186,12 +178,7 @@ describe('DeviceList', () => {
await list.pendingConnection();
const events = eventsSpy.mock.calls.map(call => call[0]);
- expect(events).toEqual([
- 'device-changed',
- 'device-acquired',
- 'device-connect_unacquired',
- 'transport-start',
- ]);
+ expect(events).toEqual(['device-changed', 'device-connect_unacquired', 'transport-start']);
});
it('.init() with pendingTransportEvent (multiple acquired devices)', async () => {
@@ -210,9 +197,9 @@ describe('DeviceList', () => {
// note: acquire - release - connect should be ok.
// acquire - deviceList._takeAndCreateDevice start (run -> rurInner -> getFeatures -> release) -> deviceList._takeAndCreateDevice end => emit DEVICE.CONNECT
expect(events).toEqual([
- ...DEVICE_CONNECTION_SEQUENCE.map(e => [e, '1']), // path 1
- ...DEVICE_CONNECTION_SEQUENCE.map(e => [e, '2']), // path 2
- ...DEVICE_CONNECTION_SEQUENCE.map(e => [e, '3']), // path 3
+ ...DEVICE_CONNECTION_SEQUENCE.map(e => [e, events[0][1]]), // path 1
+ ...DEVICE_CONNECTION_SEQUENCE.map(e => [e, events[3][1]]), // path 2
+ ...DEVICE_CONNECTION_SEQUENCE.map(e => [e, events[6][1]]), // path 3
['transport-start', undefined],
]);
});
@@ -240,15 +227,11 @@ describe('DeviceList', () => {
await transportFirstEvent;
jest.useRealTimers();
- expect(eventsSpy).toHaveBeenCalledTimes(8);
- const events = eventsSpy.mock.calls.map(call => call[0]);
+ // expect(eventsSpy).toHaveBeenCalledTimes(5);
+ const events = eventsSpy.mock.calls.map(([event]) => event);
expect(events).toEqual([
'device-changed',
- 'device-acquired',
- 'device-changed',
- 'device-acquired',
'device-changed',
- 'device-released',
'device-connect',
'transport-start',
]);
@@ -299,9 +282,10 @@ describe('DeviceList', () => {
expect(events).toEqual([
['transport-start', undefined],
- ...DEVICE_CONNECTION_SEQUENCE.map(e => [e, '1']), // path 1
- ...DEVICE_CONNECTION_SEQUENCE.map(e => [e, '3']), // path 3
- ...DEVICE_CONNECTION_SEQUENCE.map(e => [e, '4']), // path 4
+ ['device-disconnect', events[1][1]],
+ ...DEVICE_CONNECTION_SEQUENCE.map(e => [e, events[2][1]]), // path 1
+ ...DEVICE_CONNECTION_SEQUENCE.map(e => [e, events[5][1]]), // path 3
+ ...DEVICE_CONNECTION_SEQUENCE.map(e => [e, events[8][1]]), // path 4
]);
});
});
diff --git a/packages/connect/src/device/__tests__/checkFirmwareRevision.test.ts b/packages/connect/src/device/__tests__/checkFirmwareRevision.test.ts
index d5ad53d5c91..e54f5350669 100644
--- a/packages/connect/src/device/__tests__/checkFirmwareRevision.test.ts
+++ b/packages/connect/src/device/__tests__/checkFirmwareRevision.test.ts
@@ -1,6 +1,7 @@
import { FetchError } from 'node-fetch';
import { DeviceModelInternal } from '@trezor/protobuf';
+
import { checkFirmwareRevision, CheckFirmwareRevisionParams } from '../checkFirmwareRevision';
import { FirmwareRelease, FirmwareRevisionCheckResult } from '../../exports';
import * as utilsAssets from '../../utils/assets';
diff --git a/packages/connect/src/device/__tests__/resolveDescriptorForTaproot.test.ts b/packages/connect/src/device/__tests__/resolveDescriptorForTaproot.test.ts
index 4d2fe5ccb67..b7e2a936a0a 100644
--- a/packages/connect/src/device/__tests__/resolveDescriptorForTaproot.test.ts
+++ b/packages/connect/src/device/__tests__/resolveDescriptorForTaproot.test.ts
@@ -1,6 +1,7 @@
+import { MessagesSchema as Messages } from '@trezor/protobuf';
+
import { resolveDescriptorForTaproot } from '../resolveDescriptorForTaproot';
import { HDNodeResponse } from '../../types/api/getPublicKey';
-import { MessagesSchema as Messages } from '@trezor/protobuf';
const originalResponse: HDNodeResponse = {
path: [2147483734, 2147483648, 2147483648],
diff --git a/packages/connect/src/device/calculateRevisionForDevice.ts b/packages/connect/src/device/calculateRevisionForDevice.ts
index a8d46948dae..93cfd5283b0 100644
--- a/packages/connect/src/device/calculateRevisionForDevice.ts
+++ b/packages/connect/src/device/calculateRevisionForDevice.ts
@@ -1,4 +1,5 @@
import { isNewer } from '@trezor/utils/src/versionUtils';
+
import { VersionArray } from '../exports';
type calculateRevisionForDeviceParams = {
diff --git a/packages/connect/src/device/checkFirmwareRevision.ts b/packages/connect/src/device/checkFirmwareRevision.ts
index ade88aa48f1..e7da0dcdfc4 100644
--- a/packages/connect/src/device/checkFirmwareRevision.ts
+++ b/packages/connect/src/device/checkFirmwareRevision.ts
@@ -1,4 +1,5 @@
import { isEqual } from '@trezor/utils/src/versionUtils';
+
import { PROTO } from '../constants';
import { downloadReleasesMetadata } from '../data/downloadReleasesMetadata';
import { FirmwareRelease, VersionArray } from '../types';
diff --git a/packages/connect/src/device/prompts.ts b/packages/connect/src/device/prompts.ts
index cd163d78d86..1205969c223 100644
--- a/packages/connect/src/device/prompts.ts
+++ b/packages/connect/src/device/prompts.ts
@@ -20,7 +20,9 @@ type DeviceEventCallback = DeviceEvents[K] extends
: never;
export const cancelPrompt = (device: Device, expectResponse = true) => {
- if (!device.transportSession) {
+ const session = device.getLocalSession();
+
+ if (!session) {
// device disconnected or acquired by someone else
return Promise.resolve({
success: false,
@@ -29,7 +31,7 @@ export const cancelPrompt = (device: Device, expectResponse = true) => {
}
const cancelArgs = {
- session: device.transportSession,
+ session,
name: 'Cancel',
data: {},
protocol: device.protocol,
diff --git a/packages/connect/src/device/resolveDescriptorForTaproot.ts b/packages/connect/src/device/resolveDescriptorForTaproot.ts
index 275212c437d..28bb8ec2e74 100644
--- a/packages/connect/src/device/resolveDescriptorForTaproot.ts
+++ b/packages/connect/src/device/resolveDescriptorForTaproot.ts
@@ -1,6 +1,7 @@
-import { HDNodeResponse } from '../types/api/getPublicKey';
import { MessagesSchema as Messages } from '@trezor/protobuf';
+import { HDNodeResponse } from '../types/api/getPublicKey';
+
interface Params {
response: HDNodeResponse;
publicKey: Messages.PublicKey;
diff --git a/packages/connect/src/events/blockchain.ts b/packages/connect/src/events/blockchain.ts
index a7a3cecb20b..624c7a06aed 100644
--- a/packages/connect/src/events/blockchain.ts
+++ b/packages/connect/src/events/blockchain.ts
@@ -4,6 +4,7 @@ import type {
FiatRatesBySymbol,
NotificationEvent,
} from '@trezor/blockchain-link';
+
import type { CoinInfo } from '../types/coinInfo';
import type { MessageFactoryFn } from '../types/utils';
diff --git a/packages/connect/src/events/call.ts b/packages/connect/src/events/call.ts
index 70471b8e1cc..36b0072bd06 100644
--- a/packages/connect/src/events/call.ts
+++ b/packages/connect/src/events/call.ts
@@ -5,24 +5,28 @@ import type { CommonParams, DeviceIdentity } from '../types/params';
import { Device } from '../device/Device';
// conditionally unwrap TrezorConnect api method Success response
-type UnwrappedResponse =
- T extends Promise ? (R extends { success: true; payload: infer P } ? P : never) : void;
+type UnwrappedResponse =
+ Response extends Promise
+ ? R extends { success: true; payload: infer P }
+ ? P
+ : never
+ : void;
// https://github.com/microsoft/TypeScript/issues/32164
// there is no native way how to get Parameters for overloaded function
// current TrezorConnect api methods have exactly 2 overloads (if any)
-type OverloadedMethod> = T extends {
+type OverloadedMethod> = Method extends {
(params: infer P1): infer R1;
(params: infer P2): infer R2;
}
- ? ((params: P1 & E) => R1) | ((params: P2 & E) => R2) // - method IS overloaded, result depends on params (example: getAddress)
- : T extends (...args: infer P) => infer R
- ? (params: E & P[0]) => R // - method in NOT overloaded, one set of params and one set of result (example: signTransaction)
+ ? ((params: P1 & Params) => R1) | ((params: P2 & Params) => R2) // - method IS overloaded, result depends on params (example: getAddress)
+ : Method extends (...args: infer P) => infer R
+ ? (params: Params & P[0]) => R // - method in NOT overloaded, one set of params and one set of result (example: signTransaction)
: never;
-type UnwrappedMethod> = T extends () => infer R
- ? (params: M & CommonParams) => R // - method doesn't have params (example: dispose, disableWebUSB)
- : OverloadedMethod;
+type UnwrappedMethod> = Method extends () => infer R
+ ? (params: Params & CommonParams) => R // - method doesn't have params (example: dispose, disableWebUSB)
+ : OverloadedMethod;
type IsMethodCallable = T extends (...args: any[]) => infer R
? R extends Promise<{ success: boolean }>
@@ -76,9 +80,9 @@ export const createResponseMessage = (
payload: success ? payload : serializeError(payload),
device: device
? {
- path: device?.originalDescriptor.path,
+ path: device?.getUniquePath(),
state: device?.getState(),
- instance: device?.instance,
+ instance: device?.getInstance(),
}
: undefined,
});
diff --git a/packages/connect/src/events/device.ts b/packages/connect/src/events/device.ts
index 1cec7002019..aa94479a514 100644
--- a/packages/connect/src/events/device.ts
+++ b/packages/connect/src/events/device.ts
@@ -9,13 +9,6 @@ export const DEVICE = {
CONNECT_UNACQUIRED: 'device-connect_unacquired',
DISCONNECT: 'device-disconnect',
CHANGED: 'device-changed',
- ACQUIRE: 'device-acquire',
- RELEASE: 'device-release',
- ACQUIRED: 'device-acquired',
- RELEASED: 'device-released',
- USED_ELSEWHERE: 'device-used_elsewhere',
-
- LOADING: 'device-loading',
// trezor-link events in protobuf format
BUTTON: 'button',
diff --git a/packages/connect/src/events/transport.ts b/packages/connect/src/events/transport.ts
index 38cb887e3be..f0d83f8442f 100644
--- a/packages/connect/src/events/transport.ts
+++ b/packages/connect/src/events/transport.ts
@@ -1,9 +1,9 @@
-import { serializeError } from '../constants/errors';
-import type { MessageFactoryFn } from '../types/utils';
import type { Transport } from '@trezor/transport';
-
import { TRANSPORT } from '@trezor/transport/src/constants';
+import { serializeError } from '../constants/errors';
+import type { MessageFactoryFn } from '../types/utils';
+
export { TRANSPORT } from '@trezor/transport/src/constants';
export const TRANSPORT_EVENT = 'TRANSPORT_EVENT';
diff --git a/packages/connect/src/events/ui-promise.ts b/packages/connect/src/events/ui-promise.ts
index 6604a6b7e2e..af125d0d9c7 100644
--- a/packages/connect/src/events/ui-promise.ts
+++ b/packages/connect/src/events/ui-promise.ts
@@ -1,4 +1,5 @@
import type { Deferred } from '@trezor/utils';
+
import type { DEVICE } from './device';
import type { Device } from '../device/Device';
import type { UiResponseEvent } from './ui-response';
diff --git a/packages/connect/src/factory.ts b/packages/connect/src/factory.ts
index 02c8f550a82..c3b8ed7bd4a 100644
--- a/packages/connect/src/factory.ts
+++ b/packages/connect/src/factory.ts
@@ -1,274 +1,264 @@
-import { UI } from './events';
import type { EventEmitter } from 'events';
+
+import { UI } from './events';
import type { TrezorConnect } from './types';
import type { CallMethod } from './events/call';
+import type { InitType } from './types/api/init';
-export interface ConnectFactoryDependencies {
+export interface ConnectFactoryDependencies> {
+ init: InitType;
call: CallMethod;
eventEmitter: EventEmitter;
manifest: TrezorConnect['manifest'];
- init: TrezorConnect['init'];
requestLogin: TrezorConnect['requestLogin'];
uiResponse: TrezorConnect['uiResponse'];
- renderWebUSBButton: TrezorConnect['renderWebUSBButton'];
- disableWebUSB: TrezorConnect['disableWebUSB'];
- requestWebUSBDevice: TrezorConnect['requestWebUSBDevice'];
cancel: TrezorConnect['cancel'];
dispose: TrezorConnect['dispose'];
}
-export const factory = ({
- eventEmitter,
- manifest,
- init,
- call,
- requestLogin,
- uiResponse,
- renderWebUSBButton,
- disableWebUSB,
- requestWebUSBDevice,
- cancel,
- dispose,
-}: ConnectFactoryDependencies): TrezorConnect => {
- const api: TrezorConnect = {
+export const factory = <
+ SettingsType extends Record,
+ ExtraMethodsType extends Record,
+>(
+ {
+ eventEmitter,
manifest,
init,
- getSettings: () => call({ method: 'getSettings' }),
-
- on: any>(type: T, fn: P) => {
- eventEmitter.on(type, fn);
- },
-
- off: (type, fn) => {
- eventEmitter.removeListener(type, fn);
- },
+ call,
+ requestLogin,
+ uiResponse,
+ cancel,
+ dispose,
+ }: ConnectFactoryDependencies,
+ extraMethods: ExtraMethodsType = {} as ExtraMethodsType,
+): Omit & { init: InitType } & ExtraMethodsType => ({
+ manifest,
+ init,
- removeAllListeners: type => {
- if (typeof type === 'string') {
- eventEmitter.removeAllListeners(type);
- } else {
- eventEmitter.removeAllListeners();
- }
- },
+ on: any>(type: T, fn: P) => {
+ eventEmitter.on(type, fn);
+ },
- uiResponse,
+ off: (type, fn) => {
+ eventEmitter.removeListener(type, fn);
+ },
- // methods
+ removeAllListeners: type => {
+ if (typeof type === 'string') {
+ eventEmitter.removeAllListeners(type);
+ } else {
+ eventEmitter.removeAllListeners();
+ }
+ },
- blockchainGetAccountBalanceHistory: params =>
- call({ ...params, method: 'blockchainGetAccountBalanceHistory' }),
+ uiResponse,
- blockchainGetCurrentFiatRates: params =>
- call({ ...params, method: 'blockchainGetCurrentFiatRates' }),
+ // methods
- blockchainGetFiatRatesForTimestamps: params =>
- call({ ...params, method: 'blockchainGetFiatRatesForTimestamps' }),
+ blockchainGetAccountBalanceHistory: params =>
+ call({ ...params, method: 'blockchainGetAccountBalanceHistory' }),
- blockchainDisconnect: params => call({ ...params, method: 'blockchainDisconnect' }),
+ blockchainGetCurrentFiatRates: params =>
+ call({ ...params, method: 'blockchainGetCurrentFiatRates' }),
- blockchainEstimateFee: params => call({ ...params, method: 'blockchainEstimateFee' }),
+ blockchainGetFiatRatesForTimestamps: params =>
+ call({ ...params, method: 'blockchainGetFiatRatesForTimestamps' }),
- blockchainGetTransactions: params =>
- call({ ...params, method: 'blockchainGetTransactions' }),
+ blockchainEvmRpcCall: params => call({ ...params, method: 'blockchainEvmRpcCall' }),
- blockchainSetCustomBackend: params =>
- call({ ...params, method: 'blockchainSetCustomBackend' }),
+ blockchainDisconnect: params => call({ ...params, method: 'blockchainDisconnect' }),
- blockchainSubscribe: params => call({ ...params, method: 'blockchainSubscribe' }),
+ blockchainEstimateFee: params => call({ ...params, method: 'blockchainEstimateFee' }),
- blockchainSubscribeFiatRates: params =>
- call({ ...params, method: 'blockchainSubscribeFiatRates' }),
+ blockchainGetTransactions: params => call({ ...params, method: 'blockchainGetTransactions' }),
- blockchainUnsubscribe: params => call({ ...params, method: 'blockchainUnsubscribe' }),
+ blockchainSetCustomBackend: params => call({ ...params, method: 'blockchainSetCustomBackend' }),
- blockchainUnsubscribeFiatRates: params =>
- call({ ...params, method: 'blockchainUnsubscribeFiatRates' }),
+ blockchainSubscribe: params => call({ ...params, method: 'blockchainSubscribe' }),
- requestLogin: params => requestLogin(params),
+ blockchainSubscribeFiatRates: params =>
+ call({ ...params, method: 'blockchainSubscribeFiatRates' }),
- cardanoGetAddress: params =>
- call({
- ...params,
- method: 'cardanoGetAddress',
- useEventListener: eventEmitter.listenerCount(UI.ADDRESS_VALIDATION) > 0,
- }),
+ blockchainUnsubscribe: params => call({ ...params, method: 'blockchainUnsubscribe' }),
- cardanoGetNativeScriptHash: params =>
- call({ ...params, method: 'cardanoGetNativeScriptHash' }),
+ blockchainUnsubscribeFiatRates: params =>
+ call({ ...params, method: 'blockchainUnsubscribeFiatRates' }),
- cardanoGetPublicKey: params => call({ ...params, method: 'cardanoGetPublicKey' }),
+ requestLogin: params => requestLogin(params),
- cardanoSignTransaction: params => call({ ...params, method: 'cardanoSignTransaction' }),
+ cardanoGetAddress: params =>
+ call({
+ ...params,
+ method: 'cardanoGetAddress',
+ useEventListener: eventEmitter.listenerCount(UI.ADDRESS_VALIDATION) > 0,
+ }),
- cardanoComposeTransaction: params =>
- call({ ...params, method: 'cardanoComposeTransaction' }),
+ cardanoGetNativeScriptHash: params => call({ ...params, method: 'cardanoGetNativeScriptHash' }),
- cipherKeyValue: params => call({ ...params, method: 'cipherKeyValue' }),
+ cardanoGetPublicKey: params => call({ ...params, method: 'cardanoGetPublicKey' }),
- composeTransaction: params => call({ ...params, method: 'composeTransaction' }),
+ cardanoSignTransaction: params => call({ ...params, method: 'cardanoSignTransaction' }),
- ethereumGetAddress: params =>
- call({
- ...params,
- method: 'ethereumGetAddress',
- useEventListener: eventEmitter.listenerCount(UI.ADDRESS_VALIDATION) > 0,
- }),
+ cardanoComposeTransaction: params => call({ ...params, method: 'cardanoComposeTransaction' }),
- ethereumGetPublicKey: params => call({ ...params, method: 'ethereumGetPublicKey' }),
+ cipherKeyValue: params => call({ ...params, method: 'cipherKeyValue' }),
- ethereumSignMessage: params => call({ ...params, method: 'ethereumSignMessage' }),
+ composeTransaction: params => call({ ...params, method: 'composeTransaction' }),
- ethereumSignTransaction: params => call({ ...params, method: 'ethereumSignTransaction' }),
+ ethereumGetAddress: params =>
+ call({
+ ...params,
+ method: 'ethereumGetAddress',
+ useEventListener: eventEmitter.listenerCount(UI.ADDRESS_VALIDATION) > 0,
+ }),
- // @ts-expect-error generic param
- ethereumSignTypedData: params => call({ ...params, method: 'ethereumSignTypedData' }),
+ ethereumGetPublicKey: params => call({ ...params, method: 'ethereumGetPublicKey' }),
- ethereumVerifyMessage: params => call({ ...params, method: 'ethereumVerifyMessage' }),
+ ethereumSignMessage: params => call({ ...params, method: 'ethereumSignMessage' }),
- getAccountDescriptor: params => call({ ...params, method: 'getAccountDescriptor' }),
+ ethereumSignTransaction: params => call({ ...params, method: 'ethereumSignTransaction' }),
- getAccountInfo: params => call({ ...params, method: 'getAccountInfo' }),
+ // @ts-expect-error generic param
+ ethereumSignTypedData: params => call({ ...params, method: 'ethereumSignTypedData' }),
- getAddress: params =>
- call({
- ...params,
- method: 'getAddress',
- useEventListener: eventEmitter.listenerCount(UI.ADDRESS_VALIDATION) > 0,
- }),
+ ethereumVerifyMessage: params => call({ ...params, method: 'ethereumVerifyMessage' }),
- getDeviceState: params => call({ ...params, method: 'getDeviceState' }),
+ getAccountDescriptor: params => call({ ...params, method: 'getAccountDescriptor' }),
- getFeatures: params => call({ ...params, method: 'getFeatures' }),
+ getAccountInfo: params => call({ ...params, method: 'getAccountInfo' }),
- getFirmwareHash: params => call({ ...params, method: 'getFirmwareHash' }),
+ getAddress: params =>
+ call({
+ ...params,
+ method: 'getAddress',
+ useEventListener: eventEmitter.listenerCount(UI.ADDRESS_VALIDATION) > 0,
+ }),
- getOwnershipId: params => call({ ...params, method: 'getOwnershipId' }),
+ getDeviceState: params => call({ ...params, method: 'getDeviceState' }),
- getOwnershipProof: params => call({ ...params, method: 'getOwnershipProof' }),
+ getFeatures: params => call({ ...params, method: 'getFeatures' }),
- getPublicKey: params => call({ ...params, method: 'getPublicKey' }),
+ getFirmwareHash: params => call({ ...params, method: 'getFirmwareHash' }),
- nemGetAddress: params =>
- call({
- ...params,
- method: 'nemGetAddress',
- useEventListener: eventEmitter.listenerCount(UI.ADDRESS_VALIDATION) > 0,
- }),
+ getOwnershipId: params => call({ ...params, method: 'getOwnershipId' }),
- nemSignTransaction: params => call({ ...params, method: 'nemSignTransaction' }),
+ getOwnershipProof: params => call({ ...params, method: 'getOwnershipProof' }),
- pushTransaction: params => call({ ...params, method: 'pushTransaction' }),
+ getPublicKey: params => call({ ...params, method: 'getPublicKey' }),
- rippleGetAddress: params =>
- call({
- ...params,
- method: 'rippleGetAddress',
- useEventListener: eventEmitter.listenerCount(UI.ADDRESS_VALIDATION) > 0,
- }),
+ nemGetAddress: params =>
+ call({
+ ...params,
+ method: 'nemGetAddress',
+ useEventListener: eventEmitter.listenerCount(UI.ADDRESS_VALIDATION) > 0,
+ }),
- rippleSignTransaction: params => call({ ...params, method: 'rippleSignTransaction' }),
+ nemSignTransaction: params => call({ ...params, method: 'nemSignTransaction' }),
- signMessage: params => call({ ...params, method: 'signMessage' }),
+ pushTransaction: params => call({ ...params, method: 'pushTransaction' }),
- signTransaction: params => call({ ...params, method: 'signTransaction' }),
+ rippleGetAddress: params =>
+ call({
+ ...params,
+ method: 'rippleGetAddress',
+ useEventListener: eventEmitter.listenerCount(UI.ADDRESS_VALIDATION) > 0,
+ }),
- solanaGetPublicKey: params => call({ ...params, method: 'solanaGetPublicKey' }),
+ rippleSignTransaction: params => call({ ...params, method: 'rippleSignTransaction' }),
- solanaGetAddress: params => call({ ...params, method: 'solanaGetAddress' }),
+ signMessage: params => call({ ...params, method: 'signMessage' }),
- solanaSignTransaction: params => call({ ...params, method: 'solanaSignTransaction' }),
+ signTransaction: params => call({ ...params, method: 'signTransaction' }),
- stellarGetAddress: params =>
- call({
- ...params,
- method: 'stellarGetAddress',
- useEventListener: eventEmitter.listenerCount(UI.ADDRESS_VALIDATION) > 0,
- }),
+ solanaGetPublicKey: params => call({ ...params, method: 'solanaGetPublicKey' }),
- stellarSignTransaction: params => call({ ...params, method: 'stellarSignTransaction' }),
+ solanaGetAddress: params => call({ ...params, method: 'solanaGetAddress' }),
- tezosGetAddress: params =>
- call({
- ...params,
- method: 'tezosGetAddress',
- useEventListener: eventEmitter.listenerCount(UI.ADDRESS_VALIDATION) > 0,
- }),
+ solanaSignTransaction: params => call({ ...params, method: 'solanaSignTransaction' }),
- tezosGetPublicKey: params => call({ ...params, method: 'tezosGetPublicKey' }),
+ stellarGetAddress: params =>
+ call({
+ ...params,
+ method: 'stellarGetAddress',
+ useEventListener: eventEmitter.listenerCount(UI.ADDRESS_VALIDATION) > 0,
+ }),
- tezosSignTransaction: params => call({ ...params, method: 'tezosSignTransaction' }),
+ stellarSignTransaction: params => call({ ...params, method: 'stellarSignTransaction' }),
- unlockPath: params => call({ ...params, method: 'unlockPath' }),
+ tezosGetAddress: params =>
+ call({
+ ...params,
+ method: 'tezosGetAddress',
+ useEventListener: eventEmitter.listenerCount(UI.ADDRESS_VALIDATION) > 0,
+ }),
- eosGetPublicKey: params => call({ ...params, method: 'eosGetPublicKey' }),
+ tezosGetPublicKey: params => call({ ...params, method: 'tezosGetPublicKey' }),
- eosSignTransaction: params => call({ ...params, method: 'eosSignTransaction' }),
+ tezosSignTransaction: params => call({ ...params, method: 'tezosSignTransaction' }),
- binanceGetAddress: params =>
- call({
- ...params,
- method: 'binanceGetAddress',
- useEventListener: eventEmitter.listenerCount(UI.ADDRESS_VALIDATION) > 0,
- }),
+ unlockPath: params => call({ ...params, method: 'unlockPath' }),
- binanceGetPublicKey: params => call({ ...params, method: 'binanceGetPublicKey' }),
+ eosGetPublicKey: params => call({ ...params, method: 'eosGetPublicKey' }),
- binanceSignTransaction: params => call({ ...params, method: 'binanceSignTransaction' }),
+ eosSignTransaction: params => call({ ...params, method: 'eosSignTransaction' }),
- verifyMessage: params => call({ ...params, method: 'verifyMessage' }),
+ binanceGetAddress: params =>
+ call({
+ ...params,
+ method: 'binanceGetAddress',
+ useEventListener: eventEmitter.listenerCount(UI.ADDRESS_VALIDATION) > 0,
+ }),
- resetDevice: params => call({ ...params, method: 'resetDevice' }),
+ binanceGetPublicKey: params => call({ ...params, method: 'binanceGetPublicKey' }),
- wipeDevice: params => call({ ...params, method: 'wipeDevice' }),
+ binanceSignTransaction: params => call({ ...params, method: 'binanceSignTransaction' }),
- checkFirmwareAuthenticity: params =>
- call({ ...params, method: 'checkFirmwareAuthenticity' }),
+ verifyMessage: params => call({ ...params, method: 'verifyMessage' }),
- applyFlags: params => call({ ...params, method: 'applyFlags' }),
+ resetDevice: params => call({ ...params, method: 'resetDevice' }),
- applySettings: params => call({ ...params, method: 'applySettings' }),
+ loadDevice: params => call({ ...params, method: 'loadDevice' }),
- authenticateDevice: params => call({ ...params, method: 'authenticateDevice' }),
+ wipeDevice: params => call({ ...params, method: 'wipeDevice' }),
- authorizeCoinjoin: params => call({ ...params, method: 'authorizeCoinjoin' }),
+ applyFlags: params => call({ ...params, method: 'applyFlags' }),
- cancelCoinjoinAuthorization: params =>
- call({ ...params, method: 'cancelCoinjoinAuthorization' }),
+ applySettings: params => call({ ...params, method: 'applySettings' }),
- showDeviceTutorial: params => call({ ...params, method: 'showDeviceTutorial' }),
+ getSettings: () => call({ method: 'getSettings' }),
- backupDevice: params => call({ ...params, method: 'backupDevice' }),
+ authenticateDevice: params => call({ ...params, method: 'authenticateDevice' }),
- changeLanguage: params => call({ ...params, method: 'changeLanguage' }),
+ authorizeCoinjoin: params => call({ ...params, method: 'authorizeCoinjoin' }),
- changePin: params => call({ ...params, method: 'changePin' }),
+ cancelCoinjoinAuthorization: params =>
+ call({ ...params, method: 'cancelCoinjoinAuthorization' }),
- changeWipeCode: params => call({ ...params, method: 'changeWipeCode' }),
+ showDeviceTutorial: params => call({ ...params, method: 'showDeviceTutorial' }),
- firmwareUpdate: params => call({ ...params, method: 'firmwareUpdate' }),
+ backupDevice: params => call({ ...params, method: 'backupDevice' }),
- recoveryDevice: params => call({ ...params, method: 'recoveryDevice' }),
+ changeLanguage: params => call({ ...params, method: 'changeLanguage' }),
- getCoinInfo: params => call({ ...params, method: 'getCoinInfo' }),
+ changePin: params => call({ ...params, method: 'changePin' }),
- rebootToBootloader: params => call({ ...params, method: 'rebootToBootloader' }),
+ changeWipeCode: params => call({ ...params, method: 'changeWipeCode' }),
- setBrightness: params => call({ ...params, method: 'setBrightness' }),
+ firmwareUpdate: params => call({ ...params, method: 'firmwareUpdate' }),
- setBusy: params => call({ ...params, method: 'setBusy' }),
+ recoveryDevice: params => call({ ...params, method: 'recoveryDevice' }),
- setProxy: params => call({ ...params, method: 'setProxy' }),
+ getCoinInfo: params => call({ ...params, method: 'getCoinInfo' }),
- dispose,
+ setBrightness: params => call({ ...params, method: 'setBrightness' }),
- cancel,
+ setBusy: params => call({ ...params, method: 'setBusy' }),
- renderWebUSBButton,
+ setProxy: params => call({ ...params, method: 'setProxy' }),
- disableWebUSB,
+ dispose,
- requestWebUSBDevice,
- };
+ cancel,
- return api;
-};
+ ...extraMethods,
+});
diff --git a/packages/connect/src/impl/dynamic.ts b/packages/connect/src/impl/dynamic.ts
new file mode 100644
index 00000000000..30a13f388f7
--- /dev/null
+++ b/packages/connect/src/impl/dynamic.ts
@@ -0,0 +1,145 @@
+import EventEmitter from 'events';
+
+import { ConnectFactoryDependencies } from '../factory';
+import type { Manifest } from '../types/settings';
+import { CallMethodPayload } from '../events';
+import { ERRORS } from '../constants';
+import { ProxyEventEmitter } from '../utils/proxy-event-emitter';
+import { InitFullSettings } from '../types/api/init';
+
+type TrezorConnectDynamicParams<
+ ImplType,
+ SettingsType extends Record,
+ ImplInterface extends ConnectFactoryDependencies,
+> = {
+ implementations: {
+ type: ImplType;
+ impl: ImplInterface;
+ }[];
+ getInitTarget: (settings: InitFullSettings) => ImplType;
+ handleErrorFallback: (errorCode: string) => Promise;
+};
+
+/**
+ * Implementation of TrezorConnect that can dynamically switch between different implementations.
+ *
+ */
+export class TrezorConnectDynamic<
+ ImplType,
+ SettingsType extends Record,
+ ImplInterface extends ConnectFactoryDependencies,
+> implements ConnectFactoryDependencies
+{
+ public eventEmitter: EventEmitter;
+
+ private currentTarget: ImplType;
+ private implementations: TrezorConnectDynamicParams<
+ ImplType,
+ SettingsType,
+ ImplInterface
+ >['implementations'];
+ private getInitTarget: TrezorConnectDynamicParams<
+ ImplType,
+ SettingsType,
+ ImplInterface
+ >['getInitTarget'];
+ private handleErrorFallback: TrezorConnectDynamicParams<
+ ImplType,
+ SettingsType,
+ ImplInterface
+ >['handleErrorFallback'];
+
+ public lastSettings?: InitFullSettings;
+
+ public constructor({
+ implementations,
+ getInitTarget,
+ handleErrorFallback,
+ }: TrezorConnectDynamicParams) {
+ this.implementations = implementations;
+ this.currentTarget = this.implementations[0].type;
+ this.getInitTarget = getInitTarget;
+ this.handleErrorFallback = handleErrorFallback;
+ this.eventEmitter = new ProxyEventEmitter(
+ this.implementations.map(impl => impl.impl.eventEmitter),
+ );
+ }
+
+ public getTarget() {
+ return this.implementations.find(impl => impl.type === this.currentTarget)!.impl;
+ }
+
+ public async switchTarget(target: ImplType) {
+ if (this.currentTarget === target) {
+ return;
+ }
+
+ if (!this.lastSettings) {
+ throw ERRORS.TypedError('Init_NotInitialized');
+ }
+ await this.getTarget().dispose();
+ this.currentTarget = target;
+ await this.getTarget().init(this.lastSettings);
+ }
+
+ public manifest(manifest: Manifest) {
+ // @ts-expect-error hell knows why this is not working
+ this.lastSettings = {
+ ...this.lastSettings,
+ manifest,
+ };
+
+ this.getTarget().manifest(manifest);
+ }
+
+ public async init(settings: InitFullSettings) {
+ if (!settings?.manifest) {
+ throw ERRORS.TypedError('Init_ManifestMissing');
+ }
+ // Save settings for later use
+ this.lastSettings = settings;
+
+ this.currentTarget = this.getInitTarget(settings);
+
+ // Initialize the target
+ try {
+ return await this.getTarget().init(this.lastSettings);
+ } catch (error) {
+ // Handle error by switching to other implementation if available as defined in `handleErrorFallback`.
+ if (await this.handleErrorFallback(error.code)) {
+ return await this.getTarget().init(settings);
+ }
+
+ throw error;
+ }
+ }
+
+ public async call(params: CallMethodPayload) {
+ const response = await this.getTarget().call(params);
+ if (!response.success) {
+ if (await this.handleErrorFallback(response.payload.code)) {
+ return await this.getTarget().call(params);
+ }
+ }
+
+ return response;
+ }
+
+ public requestLogin(params: any) {
+ return this.getTarget().requestLogin(params);
+ }
+
+ public uiResponse(params: any) {
+ return this.getTarget().uiResponse(params);
+ }
+
+ public cancel(error?: string) {
+ return this.getTarget().cancel(error);
+ }
+
+ public dispose() {
+ this.eventEmitter.removeAllListeners();
+
+ return this.getTarget().dispose();
+ }
+}
diff --git a/packages/connect/src/index-browser.ts b/packages/connect/src/index-browser.ts
index 02e6249dc98..bdf93de6ff4 100644
--- a/packages/connect/src/index-browser.ts
+++ b/packages/connect/src/index-browser.ts
@@ -17,10 +17,7 @@ const TrezorConnect = factory({
init: fallback,
call: fallback,
requestLogin: fallback,
- requestWebUSBDevice: fallback,
uiResponse: fallback,
- renderWebUSBButton: fallback,
- disableWebUSB: fallback,
cancel: fallback,
dispose: fallback,
});
diff --git a/packages/connect/src/index.ts b/packages/connect/src/index.ts
index 20fe0b18ae6..96988bcf66f 100644
--- a/packages/connect/src/index.ts
+++ b/packages/connect/src/index.ts
@@ -208,31 +208,19 @@ const cancel = (error?: string) => {
});
};
-const renderWebUSBButton = (_className?: string) => {
- throw ERRORS.TypedError('Method_InvalidPackage');
-};
-
-const disableWebUSB = () => {
- throw ERRORS.TypedError('Method_InvalidPackage');
-};
-
-const requestWebUSBDevice = () => {
- throw ERRORS.TypedError('Method_InvalidPackage');
-};
-
-const TrezorConnect = factory({
- eventEmitter,
- manifest,
- init,
- call,
- requestLogin,
- uiResponse,
- renderWebUSBButton,
- disableWebUSB,
- requestWebUSBDevice,
- cancel,
- dispose,
-});
+const TrezorConnect = factory(
+ {
+ eventEmitter,
+ manifest,
+ init,
+ call,
+ requestLogin,
+ uiResponse,
+ cancel,
+ dispose,
+ },
+ {},
+);
export default TrezorConnect;
export * from './exports';
diff --git a/packages/connect/src/types/api/__tests__/binance.ts b/packages/connect/src/types/api/__tests__/binance.ts
index 44c9df72139..507d9df4a43 100644
--- a/packages/connect/src/types/api/__tests__/binance.ts
+++ b/packages/connect/src/types/api/__tests__/binance.ts
@@ -1,4 +1,4 @@
-import { TrezorConnect } from '../../..';
+import { DeviceUniquePath, TrezorConnect } from '../../..';
export const binanceGetAddress = async (api: TrezorConnect) => {
// regular
@@ -29,7 +29,7 @@ export const binanceGetAddress = async (api: TrezorConnect) => {
// with all possible params
api.binanceGetAddress({
device: {
- path: '1',
+ path: DeviceUniquePath('1'),
instance: 1,
state: 'state@device-id:1',
},
diff --git a/packages/connect/src/types/api/__tests__/bitcoin.ts b/packages/connect/src/types/api/__tests__/bitcoin.ts
index 64adc77b2ad..279abc3d425 100644
--- a/packages/connect/src/types/api/__tests__/bitcoin.ts
+++ b/packages/connect/src/types/api/__tests__/bitcoin.ts
@@ -1,4 +1,4 @@
-import { TrezorConnect } from '../../..';
+import { DeviceUniquePath, TrezorConnect } from '../../..';
export const getAddress = async (api: TrezorConnect) => {
// regular
@@ -29,7 +29,7 @@ export const getAddress = async (api: TrezorConnect) => {
// with all possible params
api.getAddress({
device: {
- path: '1',
+ path: DeviceUniquePath('1'),
instance: 1,
state: 'state@device-id:1',
},
diff --git a/packages/connect/src/types/api/__tests__/cardano.ts b/packages/connect/src/types/api/__tests__/cardano.ts
index ab59c6d05be..b39c80f628e 100644
--- a/packages/connect/src/types/api/__tests__/cardano.ts
+++ b/packages/connect/src/types/api/__tests__/cardano.ts
@@ -1,4 +1,4 @@
-import { TrezorConnect, PROTO } from '../../..';
+import { TrezorConnect, PROTO, DeviceUniquePath } from '../../..';
const {
CardanoAddressType,
@@ -122,7 +122,7 @@ export const cardanoGetAddress = async (api: TrezorConnect) => {
// with all possible params
api.cardanoGetAddress({
device: {
- path: '1',
+ path: DeviceUniquePath('1'),
instance: 1,
state: 'state@device-id:1',
},
diff --git a/packages/connect/src/types/api/__tests__/ethereum.ts b/packages/connect/src/types/api/__tests__/ethereum.ts
index cb6776ac942..ae6008f5ba3 100644
--- a/packages/connect/src/types/api/__tests__/ethereum.ts
+++ b/packages/connect/src/types/api/__tests__/ethereum.ts
@@ -1,4 +1,4 @@
-import { TrezorConnect } from '../../..';
+import { DeviceUniquePath, TrezorConnect } from '../../..';
export const ethereumGetAddress = async (api: TrezorConnect) => {
// regular
@@ -30,7 +30,7 @@ export const ethereumGetAddress = async (api: TrezorConnect) => {
// with all possible params
api.ethereumGetAddress({
device: {
- path: '1',
+ path: DeviceUniquePath('1'),
instance: 1,
state: 'state@device-id:1',
},
diff --git a/packages/connect/src/types/api/__tests__/index.ts b/packages/connect/src/types/api/__tests__/index.ts
index 35cae9718f5..5b7c9a225f8 100644
--- a/packages/connect/src/types/api/__tests__/index.ts
+++ b/packages/connect/src/types/api/__tests__/index.ts
@@ -32,6 +32,4 @@ export const init = async (api: TrezorConnect) => {
api.dispose();
api.cancel();
api.cancel('Interruption error');
- api.renderWebUSBButton();
- api.disableWebUSB();
};
diff --git a/packages/connect/src/types/api/__tests__/management.ts b/packages/connect/src/types/api/__tests__/management.ts
index 232101e9ed5..d72095ded2e 100644
--- a/packages/connect/src/types/api/__tests__/management.ts
+++ b/packages/connect/src/types/api/__tests__/management.ts
@@ -70,7 +70,4 @@ export const management = async (api: TrezorConnect) => {
word_count: 24,
});
if (recovery.success) recovery.payload.message.toLowerCase();
-
- const reboot = await api.rebootToBootloader();
- if (reboot.success) reboot.payload.message.toLowerCase();
};
diff --git a/packages/connect/src/types/api/__tests__/nem.ts b/packages/connect/src/types/api/__tests__/nem.ts
index 6fb7a3350af..ab1e8d4c647 100644
--- a/packages/connect/src/types/api/__tests__/nem.ts
+++ b/packages/connect/src/types/api/__tests__/nem.ts
@@ -1,4 +1,4 @@
-import { TrezorConnect, NEM } from '../../..';
+import { TrezorConnect, NEM, DeviceUniquePath } from '../../..';
export const nemGetAddress = async (api: TrezorConnect) => {
// regular
@@ -33,7 +33,7 @@ export const nemGetAddress = async (api: TrezorConnect) => {
// with all possible params
api.nemGetAddress({
device: {
- path: '1',
+ path: DeviceUniquePath('1'),
instance: 1,
state: 'state@device-id:1',
},
diff --git a/packages/connect/src/types/api/__tests__/ripple.ts b/packages/connect/src/types/api/__tests__/ripple.ts
index f84d8a6ab31..7a63fc0d5e6 100644
--- a/packages/connect/src/types/api/__tests__/ripple.ts
+++ b/packages/connect/src/types/api/__tests__/ripple.ts
@@ -1,4 +1,4 @@
-import { TrezorConnect } from '../../..';
+import { DeviceUniquePath, TrezorConnect } from '../../..';
export const rippleGetAddress = async (api: TrezorConnect) => {
// regular
@@ -31,7 +31,7 @@ export const rippleGetAddress = async (api: TrezorConnect) => {
// with all possible params
api.rippleGetAddress({
device: {
- path: '1',
+ path: DeviceUniquePath('1'),
instance: 1,
state: 'state@device-id:1',
},
diff --git a/packages/connect/src/types/api/__tests__/stellar.ts b/packages/connect/src/types/api/__tests__/stellar.ts
index 4fc3811d59c..1164974b88b 100644
--- a/packages/connect/src/types/api/__tests__/stellar.ts
+++ b/packages/connect/src/types/api/__tests__/stellar.ts
@@ -1,4 +1,4 @@
-import { TrezorConnect } from '../../..';
+import { DeviceUniquePath, TrezorConnect } from '../../..';
export const stellarGetAddress = async (api: TrezorConnect) => {
// regular
@@ -29,7 +29,7 @@ export const stellarGetAddress = async (api: TrezorConnect) => {
// with all possible params
api.stellarGetAddress({
device: {
- path: '1',
+ path: DeviceUniquePath('1'),
instance: 1,
state: 'state@device-id:1',
},
diff --git a/packages/connect/src/types/api/__tests__/tezos.ts b/packages/connect/src/types/api/__tests__/tezos.ts
index 7396ce5418d..e4887411f96 100644
--- a/packages/connect/src/types/api/__tests__/tezos.ts
+++ b/packages/connect/src/types/api/__tests__/tezos.ts
@@ -1,4 +1,4 @@
-import { TrezorConnect } from '../../..';
+import { DeviceUniquePath, TrezorConnect } from '../../..';
export const tezosGetAddress = async (api: TrezorConnect) => {
// regular
@@ -29,7 +29,7 @@ export const tezosGetAddress = async (api: TrezorConnect) => {
// with all possible params
api.tezosGetAddress({
device: {
- path: '1',
+ path: DeviceUniquePath('1'),
instance: 1,
state: 'state@device-id:1',
},
diff --git a/packages/connect/src/types/api/applySettings.ts b/packages/connect/src/types/api/applySettings.ts
index e004e802b75..cd46568e8f3 100644
--- a/packages/connect/src/types/api/applySettings.ts
+++ b/packages/connect/src/types/api/applySettings.ts
@@ -1,4 +1,5 @@
import { Type, Static } from '@trezor/schema-utils';
+
import { PROTO } from '../../constants';
import type { Params, Response } from '../params';
diff --git a/packages/connect/src/types/api/authenticateDevice.ts b/packages/connect/src/types/api/authenticateDevice.ts
index f81852dc68e..8a0cd30485c 100644
--- a/packages/connect/src/types/api/authenticateDevice.ts
+++ b/packages/connect/src/types/api/authenticateDevice.ts
@@ -1,4 +1,5 @@
import { Static, Type } from '@trezor/schema-utils';
+
import type { Params, Response } from '../params';
import { DeviceAuthenticityConfig } from '../../data/deviceAuthenticityConfigTypes';
diff --git a/packages/connect/src/types/api/authorizeCoinjoin.ts b/packages/connect/src/types/api/authorizeCoinjoin.ts
index a1aea105f65..ed0b47b17a9 100644
--- a/packages/connect/src/types/api/authorizeCoinjoin.ts
+++ b/packages/connect/src/types/api/authorizeCoinjoin.ts
@@ -1,6 +1,6 @@
-import type { Params, Response } from '../params';
-
import { Static, Type } from '@trezor/schema-utils';
+
+import type { Params, Response } from '../params';
import { DerivationPath } from '../params';
import { PROTO } from '../../constants';
diff --git a/packages/connect/src/types/api/binance/index.ts b/packages/connect/src/types/api/binance/index.ts
index 503c1ef1038..89497418438 100644
--- a/packages/connect/src/types/api/binance/index.ts
+++ b/packages/connect/src/types/api/binance/index.ts
@@ -1,6 +1,7 @@
+import { Type, Static } from '@trezor/schema-utils';
+
import { PROTO } from '../../../constants';
import { DerivationPath } from '../../params';
-import { Type, Static } from '@trezor/schema-utils';
// BinanceSDKTransaction from https://github.com/binance-chain/javascript-sdk/blob/master/src/tx/index.js
diff --git a/packages/connect/src/types/api/bitcoin/index.ts b/packages/connect/src/types/api/bitcoin/index.ts
index 4877c0189bb..d7a1c33974e 100644
--- a/packages/connect/src/types/api/bitcoin/index.ts
+++ b/packages/connect/src/types/api/bitcoin/index.ts
@@ -1,9 +1,10 @@
import type { AccountAddresses } from '@trezor/blockchain-link';
import type { Transaction as BlockbookTransaction } from '@trezor/blockchain-link-types/src/blockbook';
+import { Static, Type } from '@trezor/schema-utils';
+
import type { PROTO } from '../../../constants';
import type { AccountTransaction } from '../../account';
import { DerivationPath, ProtoWithDerivationPath } from '../../params';
-import { Static, Type } from '@trezor/schema-utils';
// signMessage
diff --git a/packages/connect/src/types/api/blockchainEstimateFee.ts b/packages/connect/src/types/api/blockchainEstimateFee.ts
index a3e36f5efb5..e46fa9ebcaa 100644
--- a/packages/connect/src/types/api/blockchainEstimateFee.ts
+++ b/packages/connect/src/types/api/blockchainEstimateFee.ts
@@ -1,4 +1,5 @@
import type { BlockchainLinkParams, BlockchainLinkResponse } from '@trezor/blockchain-link';
+
import type { FeeLevel, FeeInfo } from '../fees';
import type { CommonParamsWithCoin, Response } from '../params';
diff --git a/packages/connect/src/types/api/blockchainEvmRpcCall.ts b/packages/connect/src/types/api/blockchainEvmRpcCall.ts
new file mode 100644
index 00000000000..4573be6583c
--- /dev/null
+++ b/packages/connect/src/types/api/blockchainEvmRpcCall.ts
@@ -0,0 +1,7 @@
+import { BlockchainLinkParams, BlockchainLinkResponse } from '@trezor/blockchain-link';
+
+import type { CommonParamsWithCoin, Response } from '../params';
+
+export declare function blockchainEvmRpcCall(
+ params: CommonParamsWithCoin & BlockchainLinkParams<'rpcCall'>,
+): Response>;
diff --git a/packages/connect/src/types/api/blockchainGetAccountBalanceHistory.ts b/packages/connect/src/types/api/blockchainGetAccountBalanceHistory.ts
index f2782d7f292..7e61b91450f 100644
--- a/packages/connect/src/types/api/blockchainGetAccountBalanceHistory.ts
+++ b/packages/connect/src/types/api/blockchainGetAccountBalanceHistory.ts
@@ -1,4 +1,5 @@
import type { BlockchainLinkParams, BlockchainLinkResponse } from '@trezor/blockchain-link';
+
import type { CommonParamsWithCoin, Response } from '../params';
export declare function blockchainGetAccountBalanceHistory(
diff --git a/packages/connect/src/types/api/blockchainGetCurrentFiatRates.ts b/packages/connect/src/types/api/blockchainGetCurrentFiatRates.ts
index 08035337f1e..d98612dbd4a 100644
--- a/packages/connect/src/types/api/blockchainGetCurrentFiatRates.ts
+++ b/packages/connect/src/types/api/blockchainGetCurrentFiatRates.ts
@@ -1,4 +1,5 @@
import type { BlockchainLinkParams, BlockchainLinkResponse } from '@trezor/blockchain-link';
+
import type { CommonParamsWithCoin, Response } from '../params';
export declare function blockchainGetCurrentFiatRates(
diff --git a/packages/connect/src/types/api/blockchainGetFiatRatesForTimestamps.ts b/packages/connect/src/types/api/blockchainGetFiatRatesForTimestamps.ts
index 6f9617afdd3..0c44f7c06c6 100644
--- a/packages/connect/src/types/api/blockchainGetFiatRatesForTimestamps.ts
+++ b/packages/connect/src/types/api/blockchainGetFiatRatesForTimestamps.ts
@@ -1,4 +1,5 @@
import type { BlockchainLinkParams, BlockchainLinkResponse } from '@trezor/blockchain-link';
+
import type { CommonParamsWithCoin, Response } from '../params';
export declare function blockchainGetFiatRatesForTimestamps(
diff --git a/packages/connect/src/types/api/blockchainGetTransactions.ts b/packages/connect/src/types/api/blockchainGetTransactions.ts
index 51c2a9eeaed..294cef72b98 100644
--- a/packages/connect/src/types/api/blockchainGetTransactions.ts
+++ b/packages/connect/src/types/api/blockchainGetTransactions.ts
@@ -1,4 +1,5 @@
import type { Transaction } from '@trezor/blockchain-link';
+
import type { CommonParamsWithCoin, Response } from '../params';
export type BlockchainGetTransactions = CommonParamsWithCoin & {
diff --git a/packages/connect/src/types/api/blockchainSubscribe.ts b/packages/connect/src/types/api/blockchainSubscribe.ts
index 63be8510669..ab66828cd3b 100644
--- a/packages/connect/src/types/api/blockchainSubscribe.ts
+++ b/packages/connect/src/types/api/blockchainSubscribe.ts
@@ -1,4 +1,5 @@
import type { SubscriptionAccountInfo, BlockchainLinkResponse } from '@trezor/blockchain-link';
+
import type { CommonParamsWithCoin, Response } from '../params';
export type BlockchainSubscribe = CommonParamsWithCoin & {
diff --git a/packages/connect/src/types/api/blockchainSubscribeFiatRates.ts b/packages/connect/src/types/api/blockchainSubscribeFiatRates.ts
index 77c2312baa1..13e0d01af11 100644
--- a/packages/connect/src/types/api/blockchainSubscribeFiatRates.ts
+++ b/packages/connect/src/types/api/blockchainSubscribeFiatRates.ts
@@ -1,4 +1,5 @@
import type { BlockchainLinkResponse } from '@trezor/blockchain-link';
+
import type { CommonParamsWithCoin, Response } from '../params';
export type BlockchainSubscribeFiatRates = CommonParamsWithCoin & {
diff --git a/packages/connect/src/types/api/blockchainUnsubscribe.ts b/packages/connect/src/types/api/blockchainUnsubscribe.ts
index 66c1290ea90..4b30fa3088a 100644
--- a/packages/connect/src/types/api/blockchainUnsubscribe.ts
+++ b/packages/connect/src/types/api/blockchainUnsubscribe.ts
@@ -1,4 +1,5 @@
import type { BlockchainLinkResponse } from '@trezor/blockchain-link';
+
import type { Response } from '../params';
import type { BlockchainSubscribe } from './blockchainSubscribe';
diff --git a/packages/connect/src/types/api/blockchainUnsubscribeFiatRates.ts b/packages/connect/src/types/api/blockchainUnsubscribeFiatRates.ts
index f7b5052e83f..88e8d87dd17 100644
--- a/packages/connect/src/types/api/blockchainUnsubscribeFiatRates.ts
+++ b/packages/connect/src/types/api/blockchainUnsubscribeFiatRates.ts
@@ -1,4 +1,5 @@
import type { BlockchainLinkResponse } from '@trezor/blockchain-link';
+
import type { Response } from '../params';
import type { BlockchainSubscribeFiatRates } from './blockchainSubscribeFiatRates';
diff --git a/packages/connect/src/types/api/cancelCoinjoinAuthorization.ts b/packages/connect/src/types/api/cancelCoinjoinAuthorization.ts
index 56de14b2300..c8faf394f3c 100644
--- a/packages/connect/src/types/api/cancelCoinjoinAuthorization.ts
+++ b/packages/connect/src/types/api/cancelCoinjoinAuthorization.ts
@@ -1,4 +1,5 @@
import { Static, Type } from '@trezor/schema-utils';
+
import type { PROTO } from '../../constants';
import type { Params, Response } from '../params';
diff --git a/packages/connect/src/types/api/cardano/index.ts b/packages/connect/src/types/api/cardano/index.ts
index 7170a522713..8a836516863 100644
--- a/packages/connect/src/types/api/cardano/index.ts
+++ b/packages/connect/src/types/api/cardano/index.ts
@@ -1,4 +1,5 @@
import { Type, Static } from '@trezor/schema-utils';
+
import { PROTO } from '../../../constants';
import { GetPublicKey, PublicKey, DerivationPath } from '../../params';
diff --git a/packages/connect/src/types/api/cardanoComposeTransaction.ts b/packages/connect/src/types/api/cardanoComposeTransaction.ts
index a9f01b7eef1..418d45f7e07 100644
--- a/packages/connect/src/types/api/cardanoComposeTransaction.ts
+++ b/packages/connect/src/types/api/cardanoComposeTransaction.ts
@@ -1,4 +1,5 @@
import type { types, trezorUtils } from '@fivebinaries/coin-selection';
+
import type { AccountAddresses, AccountUtxo } from '../../exports';
import type { Params, Response } from '../params';
import type { CardanoCertificate, CardanoInput, CardanoOutput } from './cardano';
diff --git a/packages/connect/src/types/api/changeLanguage.ts b/packages/connect/src/types/api/changeLanguage.ts
index 5709faaf2bf..1d1d2a37e52 100644
--- a/packages/connect/src/types/api/changeLanguage.ts
+++ b/packages/connect/src/types/api/changeLanguage.ts
@@ -1,4 +1,5 @@
import { Type, Static } from '@trezor/schema-utils';
+
import type { Params, Response } from '../params';
import { PROTO } from '../../constants';
diff --git a/packages/connect/src/types/api/checkFirmwareAuthenticity.ts b/packages/connect/src/types/api/checkFirmwareAuthenticity.ts
deleted file mode 100644
index a8584e567d8..00000000000
--- a/packages/connect/src/types/api/checkFirmwareAuthenticity.ts
+++ /dev/null
@@ -1,13 +0,0 @@
-import type { Response, CommonParams } from '../params';
-
-export interface CheckFirmwareAuthenticityResponse {
- expectedFirmwareHash: string;
- actualFirmwareHash: string;
- valid: boolean;
-}
-
-export type CheckFirmwareAuthenticityParams = CommonParams & { baseUrl?: string };
-
-export declare function checkFirmwareAuthenticity(
- params: CheckFirmwareAuthenticityParams,
-): Response;
diff --git a/packages/connect/src/types/api/cipherKeyValue.ts b/packages/connect/src/types/api/cipherKeyValue.ts
index dff6e40e849..1a652d5b76a 100644
--- a/packages/connect/src/types/api/cipherKeyValue.ts
+++ b/packages/connect/src/types/api/cipherKeyValue.ts
@@ -1,6 +1,7 @@
-import { Params, BundledParams, Response, DerivationPath } from '../params';
import { Static, Type } from '@trezor/schema-utils';
+import { Params, BundledParams, Response, DerivationPath } from '../params';
+
export type CipherKeyValue = Static;
export const CipherKeyValue = Type.Object({
path: DerivationPath,
diff --git a/packages/connect/src/types/api/composeTransaction.ts b/packages/connect/src/types/api/composeTransaction.ts
index b8aa202444a..fae24cb4adb 100644
--- a/packages/connect/src/types/api/composeTransaction.ts
+++ b/packages/connect/src/types/api/composeTransaction.ts
@@ -9,7 +9,9 @@ import type {
ComposeResultError as ComposeResultErrorBase,
ComposeResultFinal as ComposeResultFinalBase,
ComposeResultNonFinal as ComposeResultNonFinalBase,
+ TransactionInputOutputSortingStrategy,
} from '@trezor/utxo-lib';
+
import type { PROTO } from '../../constants';
import type { Params, Response } from '../params';
@@ -20,7 +22,19 @@ export type ComposeOutputPayment = Omit | ComposeOutputPayment;
-export interface ComposeParams {
+type SortingStrategyPropsWithBackCompatibility =
+ | {
+ /** @deprecated use sortingStrategy=none instead */
+ skipPermutation?: boolean;
+ sortingStrategy?: undefined;
+ }
+ | {
+ /** @deprecated use sortingStrategy=none instead */
+ skipPermutation?: undefined;
+ sortingStrategy?: TransactionInputOutputSortingStrategy;
+ };
+
+export type ComposeParams = {
outputs: ComposeOutput[];
coin: string;
identity?: string;
@@ -30,8 +44,7 @@ export interface ComposeParams {
sequence?: number;
baseFee?: number;
floorBaseFee?: boolean;
- skipPermutation?: boolean;
-}
+} & SortingStrategyPropsWithBackCompatibility;
export type SignedTransaction = {
signatures: string[];
@@ -42,7 +55,7 @@ export type SignedTransaction = {
// @trezor/utxo-lib `composeTx` ComposeInput required fields intersects AccountUtxo
export type ComposeUtxo = AccountUtxo & Partial;
-export interface PrecomposeParams {
+export type PrecomposeParams = {
outputs: ComposeOutput[];
coin: string;
identity?: string;
@@ -56,8 +69,7 @@ export interface PrecomposeParams {
baseFee?: number;
floorBaseFee?: boolean;
sequence?: number;
- skipPermutation?: boolean;
-}
+} & SortingStrategyPropsWithBackCompatibility;
// @trezor/utxo-lib `composeTx` transaction.input (ComposeInput) response intersects AccountUtxo
export type ComposedInputs = AccountUtxo & ComposeInputBase;
diff --git a/packages/connect/src/types/api/eos/index.ts b/packages/connect/src/types/api/eos/index.ts
index 468b0405dee..2e005b1578c 100644
--- a/packages/connect/src/types/api/eos/index.ts
+++ b/packages/connect/src/types/api/eos/index.ts
@@ -1,6 +1,7 @@
+import { Type, Static } from '@trezor/schema-utils';
+
import { PROTO } from '../../../constants';
import { DerivationPath } from '../../params';
-import { Type, Static } from '@trezor/schema-utils';
// eosGetPublicKey
diff --git a/packages/connect/src/types/api/ethereum/index.ts b/packages/connect/src/types/api/ethereum/index.ts
index 777a78055f4..97de567044f 100644
--- a/packages/connect/src/types/api/ethereum/index.ts
+++ b/packages/connect/src/types/api/ethereum/index.ts
@@ -1,4 +1,5 @@
import { Type, Static } from '@trezor/schema-utils';
+
import { DerivationPath } from '../../params';
// ethereumSignMessage
diff --git a/packages/connect/src/types/api/firmwareUpdate.ts b/packages/connect/src/types/api/firmwareUpdate.ts
index b2aaf881692..ff5238e53af 100644
--- a/packages/connect/src/types/api/firmwareUpdate.ts
+++ b/packages/connect/src/types/api/firmwareUpdate.ts
@@ -1,4 +1,5 @@
import { Static, Type } from '@trezor/schema-utils';
+
import type { Params, Response } from '../params';
export type FirmwareUpdate = Static;
diff --git a/packages/connect/src/types/api/getAccountDescriptor.ts b/packages/connect/src/types/api/getAccountDescriptor.ts
index f80e193e8a7..80f043b37fe 100644
--- a/packages/connect/src/types/api/getAccountDescriptor.ts
+++ b/packages/connect/src/types/api/getAccountDescriptor.ts
@@ -1,4 +1,5 @@
import { Static, Type } from '@trezor/schema-utils';
+
import { PROTO } from '../../constants';
import { Params, BundledParams, Response, DerivationPath } from '../params';
diff --git a/packages/connect/src/types/api/getAccountInfo.ts b/packages/connect/src/types/api/getAccountInfo.ts
index 4c607b989d9..5d0d540d7a0 100644
--- a/packages/connect/src/types/api/getAccountInfo.ts
+++ b/packages/connect/src/types/api/getAccountInfo.ts
@@ -1,4 +1,5 @@
import type { BlockchainLinkParams } from '@trezor/blockchain-link';
+
import type { PROTO } from '../../constants';
import type { Params, BundledParams, Response } from '../params';
import type { AccountInfo, DiscoveryAccountType } from '../account';
diff --git a/packages/connect/src/types/api/getAddress.ts b/packages/connect/src/types/api/getAddress.ts
index 6ab2e1e2b1f..162196f3407 100644
--- a/packages/connect/src/types/api/getAddress.ts
+++ b/packages/connect/src/types/api/getAddress.ts
@@ -1,4 +1,5 @@
import { Static, Type } from '@trezor/schema-utils';
+
import { PROTO } from '../../constants';
import {
GetAddress as GetAddressShared,
diff --git a/packages/connect/src/types/api/getOwnershipId.ts b/packages/connect/src/types/api/getOwnershipId.ts
index 81e7b77e057..9d7b4cda194 100644
--- a/packages/connect/src/types/api/getOwnershipId.ts
+++ b/packages/connect/src/types/api/getOwnershipId.ts
@@ -1,4 +1,5 @@
import { Static, Type } from '@trezor/schema-utils';
+
import { PROTO } from '../../constants';
import { Params, BundledParams, Response, DerivationPath } from '../params';
diff --git a/packages/connect/src/types/api/getOwnershipProof.ts b/packages/connect/src/types/api/getOwnershipProof.ts
index a14d1879050..682588682a2 100644
--- a/packages/connect/src/types/api/getOwnershipProof.ts
+++ b/packages/connect/src/types/api/getOwnershipProof.ts
@@ -1,4 +1,5 @@
import { Static, Type } from '@trezor/schema-utils';
+
import { PROTO } from '../../constants';
import { Params, BundledParams, Response, DerivationPath } from '../params';
diff --git a/packages/connect/src/types/api/getPublicKey.ts b/packages/connect/src/types/api/getPublicKey.ts
index a7a07d115b7..2d0eaacb7a9 100644
--- a/packages/connect/src/types/api/getPublicKey.ts
+++ b/packages/connect/src/types/api/getPublicKey.ts
@@ -1,6 +1,7 @@
+import { Type, Static } from '@trezor/schema-utils';
+
import { PROTO } from '../../constants';
import { GetPublicKey as GetPublicKeyShared, Params, BundledParams, Response } from '../params';
-import { Type, Static } from '@trezor/schema-utils';
export type GetPublicKey = Static;
export const GetPublicKey = Type.Intersect([
diff --git a/packages/connect/src/types/api/index.ts b/packages/connect/src/types/api/index.ts
index 0b4de7a08ce..131a38ca99e 100644
--- a/packages/connect/src/types/api/index.ts
+++ b/packages/connect/src/types/api/index.ts
@@ -10,6 +10,7 @@ import { blockchainDisconnect } from './blockchainDisconnect';
import { blockchainEstimateFee } from './blockchainEstimateFee';
import { blockchainGetAccountBalanceHistory } from './blockchainGetAccountBalanceHistory';
import { blockchainGetCurrentFiatRates } from './blockchainGetCurrentFiatRates';
+import { blockchainEvmRpcCall } from './blockchainEvmRpcCall';
import { blockchainGetFiatRatesForTimestamps } from './blockchainGetFiatRatesForTimestamps';
import { blockchainGetTransactions } from './blockchainGetTransactions';
import { blockchainSetCustomBackend } from './blockchainSetCustomBackend';
@@ -27,10 +28,8 @@ import { cardanoSignTransaction } from './cardanoSignTransaction';
import { changeLanguage } from './changeLanguage';
import { changePin } from './changePin';
import { changeWipeCode } from './changeWipeCode';
-import { checkFirmwareAuthenticity } from './checkFirmwareAuthenticity';
import { cipherKeyValue } from './cipherKeyValue';
import { composeTransaction } from './composeTransaction';
-import { disableWebUSB } from './disableWebUSB';
import { dispose } from './dispose';
import { eosGetPublicKey } from './eosGetPublicKey';
import { eosSignTransaction } from './eosSignTransaction';
@@ -59,13 +58,11 @@ import { nemSignTransaction } from './nemSignTransaction';
import { off } from './off';
import { on } from './on';
import { pushTransaction } from './pushTransaction';
-import { rebootToBootloader } from './rebootToBootloader';
import { recoveryDevice } from './recoveryDevice';
import { removeAllListeners } from './removeAllListeners';
-import { renderWebUSBButton } from './renderWebUSBButton';
import { requestLogin } from './requestLogin';
-import { requestWebUSBDevice } from './requestWebUSBDevice';
import { resetDevice } from './resetDevice';
+import { loadDevice } from './loadDevice';
import { rippleGetAddress } from './rippleGetAddress';
import { rippleSignTransaction } from './rippleSignTransaction';
import { setBrightness } from './setBrightness';
@@ -88,265 +85,257 @@ import { verifyMessage } from './verifyMessage';
import { wipeDevice } from './wipeDevice';
export interface TrezorConnect {
- // https://github.com/trezor/trezor-suite/blob/develop/docs/packages/connect/methods/applyFlags.md
+ // https://connect.trezor.io/9/methods/device/applyFlags/
applyFlags: typeof applyFlags;
- // https://github.com/trezor/trezor-suite/blob/develop/docs/packages/connect/methods/applySettings.md
+ // https://connect.trezor.io/9/methods/device/applySettings/
applySettings: typeof applySettings;
- // https://github.com/trezor/trezor-suite/blob/develop/docs/packages/connect/methods/authenticateDevice.md
+ // https://connect.trezor.io/9/methods/device/authenticateDevice/
authenticateDevice: typeof authenticateDevice;
- // https://github.com/trezor/trezor-suite/blob/develop/docs/packages/connect/methods/authorizeCoinjoin.md
+ // https://connect.trezor.io/9/methods/bitcoin/authorizeCoinjoin/
authorizeCoinjoin: typeof authorizeCoinjoin;
- // https://github.com/trezor/trezor-suite/blob/develop/docs/packages/connect/methods/cancelCoinjoinAuthorization.md
+ // https://connect.trezor.io/9/methods/bitcoin/cancelCoinjoinAuthorization/
cancelCoinjoinAuthorization: typeof cancelCoinjoinAuthorization;
- // https://github.com/trezor/trezor-suite/blob/develop/docs/packages/connect/methods/showDeviceTutorial.md
+ // https://connect.trezor.io/9/methods/device/showDeviceTutorial/
showDeviceTutorial: typeof showDeviceTutorial;
- // https://github.com/trezor/trezor-suite/blob/develop/docs/packages/connect/methods/backupDevice.md
+ // https://connect.trezor.io/9/methods/device/backupDevice/
backupDevice: typeof backupDevice;
- // https://github.com/trezor/trezor-suite/blob/develop/docs/packages/connect/methods/binanceGetAddress.md
+ // https://connect.trezor.io/9/methods/binance/binanceGetAddress/
binanceGetAddress: typeof binanceGetAddress;
- // https://github.com/trezor/trezor-suite/blob/develop/docs/packages/connect/methods/binanceGetPublicKey.md
+ // https://connect.trezor.io/9/methods/binance/binanceGetPublicKey/
binanceGetPublicKey: typeof binanceGetPublicKey;
- // https://github.com/trezor/trezor-suite/blob/develop/docs/packages/connect/methods/binanceSignTransaction.md
+ // https://connect.trezor.io/9/methods/binance/binanceSignTransaction/
binanceSignTransaction: typeof binanceSignTransaction;
- // https://github.com/trezor/trezor-suite/blob/develop/docs/packages/connect/methods/blockchainDisconnect.md
+ // todo: link docs
blockchainDisconnect: typeof blockchainDisconnect;
- // https://github.com/trezor/trezor-suite/blob/develop/docs/packages/connect/methods/blockchainEstimateFee.md
+ // todo: link docs
blockchainEstimateFee: typeof blockchainEstimateFee;
- // https://github.com/trezor/trezor-suite/blob/develop/docs/packages/connect/methods/blockchainGetAccountBalanceHistory.md
+ // todo: link docs
blockchainGetAccountBalanceHistory: typeof blockchainGetAccountBalanceHistory;
- // https://github.com/trezor/trezor-suite/blob/develop/docs/packages/connect/methods/blockchainGetCurrentFiatRates.md
+ // todo: link docs
blockchainGetCurrentFiatRates: typeof blockchainGetCurrentFiatRates;
- // https://github.com/trezor/trezor-suite/blob/develop/docs/packages/connect/methods/blockchainGetFiatRatesForTimestamps.md
+ blockchainEvmRpcCall: typeof blockchainEvmRpcCall;
+
+ // todo: link docs
blockchainGetFiatRatesForTimestamps: typeof blockchainGetFiatRatesForTimestamps;
- // https://github.com/trezor/trezor-suite/blob/develop/docs/packages/connect/methods/blockchainGetTransactions.md
+ // todo: link docs
blockchainGetTransactions: typeof blockchainGetTransactions;
- // https://github.com/trezor/trezor-suite/blob/develop/docs/packages/connect/methods/blockchainSetCustomBackend.md
+ // todo: link docs
blockchainSetCustomBackend: typeof blockchainSetCustomBackend;
- // https://github.com/trezor/trezor-suite/blob/develop/docs/packages/connect/methods/blockchainSubscribe.md
+ // todo: link docs
blockchainSubscribe: typeof blockchainSubscribe;
- // https://github.com/trezor/trezor-suite/blob/develop/docs/packages/connect/methods/blockchainSubscribeFiatRates.md
+ // todo: link docs
blockchainSubscribeFiatRates: typeof blockchainSubscribeFiatRates;
- // https://github.com/trezor/trezor-suite/blob/develop/docs/packages/connect/methods/blockchainUnsubscribe.md
+ // todo: link docs
blockchainUnsubscribe: typeof blockchainUnsubscribe;
- // https://github.com/trezor/trezor-suite/blob/develop/docs/packages/connect/methods/blockchainUnsubscribeFiatRates.md
+ // todo: link docs
blockchainUnsubscribeFiatRates: typeof blockchainUnsubscribeFiatRates;
- // https://github.com/trezor/trezor-suite/blob/develop/docs/packages/connect/methods/cancel.md
+ // todo: link docs
cancel: typeof cancel;
- // https://github.com/trezor/trezor-suite/blob/develop/docs/packages/connect/methods/cardanoGetAddress.md
+ // https://connect.trezor.io/9/methods/cardano/cardanoGetAddress/
cardanoGetAddress: typeof cardanoGetAddress;
- // https://github.com/trezor/trezor-suite/blob/develop/docs/packages/connect/methods/cardanoGetNativeScriptHash.md
+ // https://connect.trezor.io/9/methods/cardano/cardanoGetNativeScriptHash/
cardanoGetNativeScriptHash: typeof cardanoGetNativeScriptHash;
- // https://github.com/trezor/trezor-suite/blob/develop/docs/packages/connect/methods/cardanoGetPublicKey.md
+ // https://connect.trezor.io/9/methods/cardano/cardanoGetPublicKey/
cardanoGetPublicKey: typeof cardanoGetPublicKey;
- // https://github.com/trezor/trezor-suite/blob/develop/docs/packages/connect/methods/cardanoSignTransaction.md
+ // https://connect.trezor.io/9/methods/cardano/cardanoSignTransaction/
cardanoSignTransaction: typeof cardanoSignTransaction;
+ // todo: link docs
cardanoComposeTransaction: typeof cardanoComposeTransaction;
- // https://github.com/trezor/trezor-suite/blob/develop/docs/packages/connect/methods/changeLanguage.md
+ // https://connect.trezor.io/9/methods/device/changeLanguage/
changeLanguage: typeof changeLanguage;
- // https://github.com/trezor/trezor-suite/blob/develop/docs/packages/connect/methods/changePin.md
+ // https://connect.trezor.io/9/methods/device/changePin/
changePin: typeof changePin;
- // https://github.com/trezor/trezor-suite/blob/develop/docs/packages/connect/methods/changeWipeCode.md
+ // https://connect.trezor.io/9/methods/device/changeWipeCode/
changeWipeCode: typeof changeWipeCode;
- // https://github.com/trezor/trezor-suite/blob/develop/docs/packages/connect/methods/cipherKeyValue.md
+ // https://connect.trezor.io/9/methods/other/cipherKeyValue/
cipherKeyValue: typeof cipherKeyValue;
- // https://github.com/trezor/trezor-suite/blob/develop/docs/packages/connect/methods/composeTransaction.md
+ // https://connect.trezor.io/9/methods/bitcoin/composeTransaction/
composeTransaction: typeof composeTransaction;
- // https://github.com/trezor/trezor-suite/blob/develop/docs/packages/connect/methods/disableWebUSB.md
- disableWebUSB: typeof disableWebUSB;
-
- requestWebUSBDevice: typeof requestWebUSBDevice;
-
- // https://github.com/trezor/trezor-suite/blob/develop/docs/packages/connect/methods/dispose.md
+ // todo: link docs
dispose: typeof dispose;
- // https://github.com/trezor/trezor-suite/blob/develop/docs/packages/connect/methods/eosGetPublicKey.md
+ // https://connect.trezor.io/9/methods/eos/eosGetPublicKey/
eosGetPublicKey: typeof eosGetPublicKey;
- // https://github.com/trezor/trezor-suite/blob/develop/docs/packages/connect/methods/eosSignTransaction.md
+ // https://connect.trezor.io/9/methods/eos/eosSignTransaction/
eosSignTransaction: typeof eosSignTransaction;
- // https://github.com/trezor/trezor-suite/blob/develop/docs/packages/connect/methods/ethereumGetAddress.md
+ // https://connect.trezor.io/9/methods/ethereum/ethereumGetAddress/
ethereumGetAddress: typeof ethereumGetAddress;
- // https://github.com/trezor/trezor-suite/blob/develop/docs/packages/connect/methods/ethereumGetPublicKey.md
+ // https://connect.trezor.io/9/methods/ethereum/ethereumGetPublicKey/
ethereumGetPublicKey: typeof ethereumGetPublicKey;
- // https://github.com/trezor/trezor-suite/blob/develop/docs/packages/connect/methods/ethereumSignMessage.md
+ // https://connect.trezor.io/9/methods/ethereum/ethereumSignMessage/
ethereumSignMessage: typeof ethereumSignMessage;
- // https://github.com/trezor/trezor-suite/blob/develop/docs/packages/connect/methods/ethereumSignTransaction.md
+ // https://connect.trezor.io/9/methods/ethereum/ethereumSignTransaction/
ethereumSignTransaction: typeof ethereumSignTransaction;
- // https://github.com/trezor/trezor-suite/blob/develop/docs/packages/connect/methods/ethereumSignTypedData.md
+ // https://connect.trezor.io/9/methods/ethereum/ethereumSignTypedData/
ethereumSignTypedData: typeof ethereumSignTypedData;
- // https://github.com/trezor/trezor-suite/blob/develop/docs/packages/connect/methods/ethereumVerifyMessage.md
+ // https://connect.trezor.io/9/methods/ethereum/ethereumVerifyMessage/
ethereumVerifyMessage: typeof ethereumVerifyMessage;
- // https://github.com/trezor/trezor-suite/blob/develop/docs/packages/connect/methods/firmwareUpdate.md
+ // https://connect.trezor.io/9/methods/device/firmwareUpdate/
firmwareUpdate: typeof firmwareUpdate;
- // https://github.com/trezor/trezor-suite/blob/develop/docs/packages/connect/methods/getAccountDescriptor.md
+ // https://connect.trezor.io/9/methods/other/getAccountDescriptor/
getAccountDescriptor: typeof getAccountDescriptor;
- // https://github.com/trezor/trezor-suite/blob/develop/docs/packages/connect/methods/getAccountInfo.md
+ // https://connect.trezor.io/9/methods/bitcoin/getAccountInfo/
getAccountInfo: typeof getAccountInfo;
- // https://github.com/trezor/trezor-suite/blob/develop/docs/packages/connect/methods/getAddress.md
+ // https://connect.trezor.io/9/methods/bitcoin/getAddress/
getAddress: typeof getAddress;
- // https://github.com/trezor/trezor-suite/blob/develop/docs/packages/connect/methods/getCoinInfo.md
+ // https://connect.trezor.io/9/methods/other/getCoinInfo/
getCoinInfo: typeof getCoinInfo;
- // https://github.com/trezor/trezor-suite/blob/develop/docs/packages/connect/methods/getDeviceState.md
+ // https://connect.trezor.io/9/methods/device/getDeviceState/
getDeviceState: typeof getDeviceState;
- // https://github.com/trezor/trezor-suite/blob/develop/docs/packages/connect/methods/getFeatures.md
+ // https://connect.trezor.io/9/methods/device/getFeatures/
getFeatures: typeof getFeatures;
- // https://github.com/trezor/trezor-suite/blob/develop/docs/packages/connect/methods/getFirmwareHash.md
+ // https://connect.trezor.io/9/methods/device/getFirmwareHash/
getFirmwareHash: typeof getFirmwareHash;
- // https://github.com/trezor/trezor-suite/blob/develop/docs/packages/connect/methods/getOwnershipId.md
+ // https://connect.trezor.io/9/methods/other/getOwnershipId/
getOwnershipId: typeof getOwnershipId;
- // https://github.com/trezor/trezor-suite/blob/develop/docs/packages/connect/methods/getOwnershipProof.md
+ // https://connect.trezor.io/9/methods/other/getOwnershipProof/
getOwnershipProof: typeof getOwnershipProof;
- // https://github.com/trezor/trezor-suite/blob/develop/docs/packages/connect/methods/getPublicKey.md
+ // https://connect.trezor.io/9/methods/bitcoin/getPublicKey/
getPublicKey: typeof getPublicKey;
- // https://github.com/trezor/trezor-suite/blob/develop/docs/packages/connect/methods/getSettings.md
+ // todo: link docs
getSettings: typeof getSettings;
- // https://github.com/trezor/trezor-suite/blob/develop/docs/packages/connect/methods/init.md
+ // https://connect.trezor.io/9/methods/other/init/
init: typeof init;
- // https://github.com/trezor/trezor-suite/blob/develop/docs/packages/connect/methods/manifest.md
+ // https://connect.trezor.io/9/methods/other/manifest/
manifest: typeof manifest;
- // https://github.com/trezor/trezor-suite/blob/develop/docs/packages/connect/methods/nemGetAddress.md
+ // https://connect.trezor.io/9/methods/nem/nemGetAddress/
nemGetAddress: typeof nemGetAddress;
- // https://github.com/trezor/trezor-suite/blob/develop/docs/packages/connect/methods/nemSignTransaction.md
+ // https://connect.trezor.io/9/methods/nem/nemSignTransaction/
nemSignTransaction: typeof nemSignTransaction;
- // https://github.com/trezor/trezor-suite/blob/develop/docs/packages/connect/methods/off.md
+ // todo: link docs
off: typeof off;
- // https://github.com/trezor/trezor-suite/blob/develop/docs/packages/connect/methods/on.md
+ // todo: link docs
on: typeof on;
- // https://github.com/trezor/trezor-suite/blob/develop/docs/packages/connect/methods/pushTransaction.md
+ // https://connect.trezor.io/9/methods/bitcoin/pushTransaction/
pushTransaction: typeof pushTransaction;
- // https://github.com/trezor/trezor-suite/blob/develop/docs/packages/connect/methods/rebootToBootloader.md
- rebootToBootloader: typeof rebootToBootloader;
-
- // https://github.com/trezor/trezor-suite/blob/develop/docs/packages/connect/methods/recoveryDevice.md
+ // todo: link docs
recoveryDevice: typeof recoveryDevice;
- // https://github.com/trezor/trezor-suite/blob/develop/docs/packages/connect/methods/removeAllListeners.md
+ // todo link docs
removeAllListeners: typeof removeAllListeners;
- // https://github.com/trezor/trezor-suite/blob/develop/docs/packages/connect/methods/renderWebUSBButton.md
- renderWebUSBButton: typeof renderWebUSBButton;
-
- // https://github.com/trezor/trezor-suite/blob/develop/docs/packages/connect/methods/requestLogin.md
+ // https://connect.trezor.io/9/methods/other/requestLogin/
requestLogin: typeof requestLogin;
- // https://github.com/trezor/trezor-suite/blob/develop/docs/packages/connect/methods/resetDevice.md
+ // https://connect.trezor.io/9/methods/device/resetDevice/
resetDevice: typeof resetDevice;
- // https://github.com/trezor/trezor-suite/blob/develop/docs/packages/connect/methods/rippleGetAddress.md
+ // https://connect.trezor.io/9/methods/device/loadDevice/
+ loadDevice: typeof loadDevice;
+
+ // https://connect.trezor.io/9/methods/ripple/rippleGetAddress/
rippleGetAddress: typeof rippleGetAddress;
- // https://github.com/trezor/trezor-suite/blob/develop/docs/packages/connect/methods/rippleSignTransaction.md
+ // https://connect.trezor.io/9/methods/ripple/rippleSignTransaction/
rippleSignTransaction: typeof rippleSignTransaction;
- // https://github.com/trezor/trezor-suite/blob/develop/docs/packages/connect/methods/setBrightness.md
+ // todo: link docs
setBrightness: typeof setBrightness;
- // https://github.com/trezor/trezor-suite/blob/develop/docs/packages/connect/methods/setBusy.md
+ // https://connect.trezor.io/9/methods/device/setBusy/
setBusy: typeof setBusy;
- // https://github.com/trezor/trezor-suite/blob/develop/docs/packages/connect/methods/setProxy.md
+ // todo: link docs
setProxy: typeof setProxy;
- // https://github.com/trezor/trezor-suite/blob/develop/docs/packages/connect/methods/signMessage.md
+ // https://connect.trezor.io/9/methods/bitcoin/signMessage/
signMessage: typeof signMessage;
- // https://github.com/trezor/trezor-suite/blob/develop/docs/packages/connect/methods/signTransaction.md
+ // https://connect.trezor.io/9/methods/bitcoin/signTransaction/
signTransaction: typeof signTransaction;
- // https://github.com/trezor/trezor-suite/blob/develop/docs/packages/connect/methods/solanaGetPublicKey.md
+ // https://connect.trezor.io/9/methods/solana/solanaGetPublicKey/
solanaGetPublicKey: typeof solanaGetPublicKey;
- // https://github.com/trezor/trezor-suite/blob/develop/docs/packages/connect/methods/solanaGetAddress.md
+ // https://connect.trezor.io/9/methods/solana/solanaGetAddress/
solanaGetAddress: typeof solanaGetAddress;
- // https://github.com/trezor/trezor-suite/blob/develop/docs/packages/connect/methods/solanaSignTransaction.md
+ // https://connect.trezor.io/9/methods/solana/solanaSignTransaction/
solanaSignTransaction: typeof solanaSignTransaction;
- // https://github.com/trezor/trezor-suite/blob/develop/docs/packages/connect/methods/stellarGetAddress.md
+ // https://connect.trezor.io/9/methods/stellar/stellarGetAddress/
stellarGetAddress: typeof stellarGetAddress;
- // https://github.com/trezor/trezor-suite/blob/develop/docs/packages/connect/methods/stellarSignTransaction.md
+ // https://connect.trezor.io/9/methods/stellar/stellarSignTransaction/
stellarSignTransaction: typeof stellarSignTransaction;
- // https://github.com/trezor/trezor-suite/blob/develop/docs/packages/connect/methods/tezosGetAddress.md
+ // https://connect.trezor.io/9/methods/tezos/tezosGetAddress/
tezosGetAddress: typeof tezosGetAddress;
- // https://github.com/trezor/trezor-suite/blob/develop/docs/packages/connect/methods/tezosGetPublicKey.md
+ // https://connect.trezor.io/9/methods/tezos/tezosGetPublicKey/
tezosGetPublicKey: typeof tezosGetPublicKey;
- // https://github.com/trezor/trezor-suite/blob/develop/docs/packages/connect/methods/tezosSignTransaction.md
+ // https://connect.trezor.io/9/methods/tezos/tezosSignTransaction/
tezosSignTransaction: typeof tezosSignTransaction;
- // https://github.com/trezor/trezor-suite/blob/develop/docs/packages/connect/methods/uiResponse.md
+ // todo: link docs
uiResponse: typeof uiResponse;
- // https://github.com/trezor/trezor-suite/blob/develop/docs/packages/connect/methods/unlockPath.md
+ // https://connect.trezor.io/9/methods/other/unlockPath/
unlockPath: typeof unlockPath;
- // https://github.com/trezor/trezor-suite/blob/develop/docs/packages/connect/methods/verifyMessage.md
+ // https://connect.trezor.io/9/methods/bitcoin/verifyMessage/
verifyMessage: typeof verifyMessage;
- // https://github.com/trezor/trezor-suite/blob/develop/docs/packages/connect/methods/wipeDevice.md
+ // https://connect.trezor.io/9/methods/device/wipeDevice/
wipeDevice: typeof wipeDevice;
-
- // todo: link docs
- checkFirmwareAuthenticity: typeof checkFirmwareAuthenticity;
}
diff --git a/packages/connect/src/types/api/init.ts b/packages/connect/src/types/api/init.ts
index bfd421126fd..25556144701 100644
--- a/packages/connect/src/types/api/init.ts
+++ b/packages/connect/src/types/api/init.ts
@@ -5,6 +5,17 @@
import type { ConnectSettingsPublic, Manifest } from '../settings';
-export declare function init(
- settings: { manifest: Manifest } & Partial,
+// explicitly don't overlap types
+export type InitFullSettings> = {
+ manifest: Manifest;
+} & Partial<
+ Omit & Omit
+>;
+
+export type InitType> = (
+ settings: InitFullSettings,
+) => Promise;
+
+export declare function init>(
+ settings: InitFullSettings,
): Promise;
diff --git a/packages/connect/src/types/api/loadDevice.ts b/packages/connect/src/types/api/loadDevice.ts
new file mode 100644
index 00000000000..b0209a6db24
--- /dev/null
+++ b/packages/connect/src/types/api/loadDevice.ts
@@ -0,0 +1,8 @@
+/**
+ * Performs device setup and generates a new seed.
+ */
+
+import { PROTO } from '../../constants';
+import type { Params, Response } from '../params';
+
+export declare function loadDevice(params: Params): Response;
diff --git a/packages/connect/src/types/api/nem/index.ts b/packages/connect/src/types/api/nem/index.ts
index 4aac576f3ea..56b13a9f8e7 100644
--- a/packages/connect/src/types/api/nem/index.ts
+++ b/packages/connect/src/types/api/nem/index.ts
@@ -1,6 +1,7 @@
+import { Type, Static } from '@trezor/schema-utils';
+
import { PROTO, NEM } from '../../../constants';
import { DerivationPath } from '../../params';
-import { Type, Static } from '@trezor/schema-utils';
// NEM types from nem-sdk
// https://nemproject.github.io/#transferTransaction
diff --git a/packages/connect/src/types/api/nemGetAddress.ts b/packages/connect/src/types/api/nemGetAddress.ts
index 9c4c25041b5..80d10b409e0 100644
--- a/packages/connect/src/types/api/nemGetAddress.ts
+++ b/packages/connect/src/types/api/nemGetAddress.ts
@@ -1,4 +1,5 @@
import { Static, Type } from '@trezor/schema-utils';
+
import { GetAddress, Address, Params, BundledParams, Response } from '../params';
export type NEMGetAddress = Static;
diff --git a/packages/connect/src/types/api/pushTransaction.ts b/packages/connect/src/types/api/pushTransaction.ts
index f62a9abaf76..aa991098b95 100644
--- a/packages/connect/src/types/api/pushTransaction.ts
+++ b/packages/connect/src/types/api/pushTransaction.ts
@@ -3,6 +3,7 @@
* Broadcasts the transaction to the selected network.
*/
import { Static, Type } from '@trezor/schema-utils';
+
import type { Params, Response } from '../params';
export type PushTransaction = Static;
diff --git a/packages/connect/src/types/api/rebootToBootloader.ts b/packages/connect/src/types/api/rebootToBootloader.ts
deleted file mode 100644
index 855b9d467bc..00000000000
--- a/packages/connect/src/types/api/rebootToBootloader.ts
+++ /dev/null
@@ -1,10 +0,0 @@
-/**
- * Reboots device (currently only T1B1 with fw >= 1.10.0) in bootloader mode
- */
-
-import type { PROTO } from '../../constants';
-import type { Params, Response } from '../params';
-
-export declare function rebootToBootloader(
- params?: Params,
-): Response;
diff --git a/packages/connect/src/types/api/recoveryDevice.ts b/packages/connect/src/types/api/recoveryDevice.ts
index e1cd1350607..1316f77ca99 100644
--- a/packages/connect/src/types/api/recoveryDevice.ts
+++ b/packages/connect/src/types/api/recoveryDevice.ts
@@ -3,6 +3,7 @@
*/
import { Static, Type } from '@trezor/schema-utils';
+
import { PROTO } from '../../constants';
import type { Params, Response } from '../params';
diff --git a/packages/connect/src/types/api/requestLogin.ts b/packages/connect/src/types/api/requestLogin.ts
index f5acbd76a8b..ba6acf441e8 100644
--- a/packages/connect/src/types/api/requestLogin.ts
+++ b/packages/connect/src/types/api/requestLogin.ts
@@ -6,6 +6,7 @@
*/
import { Static, Type } from '@trezor/schema-utils';
+
import type { Params, Response } from '../params';
export type LoginChallenge = Static;
diff --git a/packages/connect/src/types/api/ripple/index.ts b/packages/connect/src/types/api/ripple/index.ts
index fa9d3955344..6ccf8d4f524 100644
--- a/packages/connect/src/types/api/ripple/index.ts
+++ b/packages/connect/src/types/api/ripple/index.ts
@@ -1,6 +1,7 @@
-import { DerivationPath } from '../../params';
import { Type, Static } from '@trezor/schema-utils';
+import { DerivationPath } from '../../params';
+
export type RipplePayment = Static;
export const RipplePayment = Type.Object({
amount: Type.String(),
diff --git a/packages/connect/src/types/api/solana/index.ts b/packages/connect/src/types/api/solana/index.ts
index 1e9e5d2b1a2..f1498e78359 100644
--- a/packages/connect/src/types/api/solana/index.ts
+++ b/packages/connect/src/types/api/solana/index.ts
@@ -1,6 +1,7 @@
-import { PublicKey } from '../../params';
import { Type, Static } from '@trezor/schema-utils';
+import { PublicKey } from '../../params';
+
// solanaGetPublicKey
export type SolanaPublicKey = Static;
diff --git a/packages/connect/src/types/api/stellar/index.ts b/packages/connect/src/types/api/stellar/index.ts
index bf26040150c..33247e2f72b 100644
--- a/packages/connect/src/types/api/stellar/index.ts
+++ b/packages/connect/src/types/api/stellar/index.ts
@@ -1,9 +1,10 @@
// Stellar types from stellar-sdk
// https://github.com/stellar/js-stellar-base
+import { Type, Static } from '@trezor/schema-utils';
+
import { PROTO } from '../../../constants';
import { DerivationPath } from '../../params';
-import { Type, Static } from '@trezor/schema-utils';
export type StellarAsset = Static;
export const StellarAsset = Type.Object({
diff --git a/packages/connect/src/types/api/stellarSignTransaction.ts b/packages/connect/src/types/api/stellarSignTransaction.ts
index 2771aa9b106..19931f1b77c 100644
--- a/packages/connect/src/types/api/stellarSignTransaction.ts
+++ b/packages/connect/src/types/api/stellarSignTransaction.ts
@@ -1,5 +1,4 @@
import type { Params, Response } from '../params';
-
// todo:maybe we could unify terminology SignTransaction vs SignTx (across all methods)
import type { StellarSignTransaction, StellarSignedTx } from './stellar';
diff --git a/packages/connect/src/types/api/tezos/index.ts b/packages/connect/src/types/api/tezos/index.ts
index 4f50db8e2d7..7630a640a34 100644
--- a/packages/connect/src/types/api/tezos/index.ts
+++ b/packages/connect/src/types/api/tezos/index.ts
@@ -1,6 +1,7 @@
-import { DerivationPath } from '../../params';
import { Type, Static } from '@trezor/schema-utils';
+import { DerivationPath } from '../../params';
+
export type TezosRevealOperation = Static;
export const TezosRevealOperation = Type.Object({
source: Type.String(),
diff --git a/packages/connect/src/types/api/unlockPath.ts b/packages/connect/src/types/api/unlockPath.ts
index 9e83ed30a5b..705648e71d3 100644
--- a/packages/connect/src/types/api/unlockPath.ts
+++ b/packages/connect/src/types/api/unlockPath.ts
@@ -1,4 +1,5 @@
import { Static, Type } from '@trezor/schema-utils';
+
import { Params, Response, DerivationPath } from '../params';
import type { PROTO } from '../../constants';
diff --git a/packages/connect/src/types/coinInfo.ts b/packages/connect/src/types/coinInfo.ts
index f7afdf736d7..933d67595a5 100644
--- a/packages/connect/src/types/coinInfo.ts
+++ b/packages/connect/src/types/coinInfo.ts
@@ -1,4 +1,5 @@
import { Type, Static } from '@trezor/schema-utils';
+
import { FeeLevel } from './fees';
import { DeviceModelInternal } from './device';
diff --git a/packages/connect/src/types/device.ts b/packages/connect/src/types/device.ts
index d1d4737d7e1..405a9c2a477 100644
--- a/packages/connect/src/types/device.ts
+++ b/packages/connect/src/types/device.ts
@@ -1,3 +1,5 @@
+import { Descriptor } from '@trezor/transport';
+
import type { PROTO } from '../constants';
import type { ReleaseInfo } from './firmware';
@@ -35,6 +37,7 @@ export type DeviceState = {
sessionId?: string; // dynamic value: Features.session_id
// ${first testnet address}@${device.features.device_id}:${device.instance}
staticSessionId?: StaticSessionId;
+ deriveCardano?: boolean;
};
// NOTE: unavailableCapabilities is an object with information what is NOT supported by this device.
@@ -55,17 +58,33 @@ export type FirmwareRevisionCheckResult =
error: FirmwareRevisionCheckError;
};
-export type KnownDevice = {
+export type FirmwareHashCheckError =
+ | 'hash-mismatch'
+ | 'check-skipped'
+ | 'check-unsupported'
+ | 'unknown-release'
+ | 'other-error';
+export type FirmwareHashCheckResult =
+ | { success: true }
+ | { success: false; error: FirmwareHashCheckError };
+
+export type DeviceUniquePath = string & { __type: 'DeviceUniquePath' };
+export const DeviceUniquePath = (id: string) => id as DeviceUniquePath;
+
+type BaseDevice = {
+ path: DeviceUniquePath;
+ name: string;
+};
+
+export type KnownDevice = BaseDevice & {
type: 'acquired';
id: string | null;
- path: string;
/** @deprecated, use features.label instead */
label: string;
error?: typeof undefined;
firmware: DeviceFirmwareStatus;
firmwareRelease?: ReleaseInfo | null;
firmwareType?: FirmwareType;
- name: string;
color?: string;
status: DeviceStatus;
mode: DeviceMode;
@@ -76,23 +95,23 @@ export type KnownDevice = {
availableTranslations: string[];
authenticityChecks?: {
firmwareRevision: FirmwareRevisionCheckResult | null;
- // Maybe add FirmwareHashCheck result here?
+ firmwareHash: FirmwareHashCheckResult | null;
// Maybe add AuthenticityCheck result here?
};
+ transportSessionOwner?: undefined;
+ transportDescriptorType?: typeof undefined;
};
-export type UnknownDevice = {
+export type UnknownDevice = BaseDevice & {
type: 'unacquired';
- id?: null;
- path: string;
/** @deprecated, use features.label instead */
- label: string;
+ label: 'Unacquired device';
+ id?: typeof undefined;
error?: typeof undefined;
features?: typeof undefined;
firmware?: typeof undefined;
firmwareRelease?: typeof undefined;
firmwareType?: typeof undefined;
- name: string;
color?: typeof undefined;
status?: typeof undefined;
mode?: typeof undefined;
@@ -100,20 +119,20 @@ export type UnknownDevice = {
state?: typeof undefined;
unavailableCapabilities?: typeof undefined;
availableTranslations?: typeof undefined;
+ transportSessionOwner?: string;
+ transportDescriptorType?: typeof undefined;
};
-export type UnreadableDevice = {
+export type UnreadableDevice = BaseDevice & {
type: 'unreadable';
- id?: null;
- path: string;
/** @deprecated, use features.label instead */
- label: string;
+ label: 'Unreadable device';
error: string;
+ id?: typeof undefined;
features?: typeof undefined;
firmware?: typeof undefined;
firmwareRelease?: typeof undefined;
firmwareType?: typeof undefined;
- name: string;
color?: typeof undefined;
status?: typeof undefined;
mode?: typeof undefined;
@@ -121,6 +140,8 @@ export type UnreadableDevice = {
state?: typeof undefined;
unavailableCapabilities?: typeof undefined;
availableTranslations?: typeof undefined;
+ transportSessionOwner?: undefined;
+ transportDescriptorType: Descriptor['type'];
};
export type Device = KnownDevice | UnknownDevice | UnreadableDevice;
diff --git a/packages/connect/src/types/params.ts b/packages/connect/src/types/params.ts
index 22ad6d7455c..21b86638679 100644
--- a/packages/connect/src/types/params.ts
+++ b/packages/connect/src/types/params.ts
@@ -1,17 +1,18 @@
// API params
import { Type, TSchema, Static } from '@trezor/schema-utils';
-import { DeviceState } from './device';
+
+import { DeviceState, DeviceUniquePath } from './device';
import { ErrorCode } from '../constants/errors';
export interface DeviceIdentity {
- path?: string;
- state?: string | DeviceState;
+ path?: DeviceUniquePath;
+ state?: DeviceState;
instance?: number;
}
export interface CommonParams {
- device?: DeviceIdentity;
+ device?: DeviceIdentity & { state?: DeviceState | string }; // Note: state as string should be removed https://github.com/trezor/trezor-suite/issues/12710
useEmptyPassphrase?: boolean;
useEventListener?: boolean; // this param is set automatically in factory
allowSeedlessDevice?: boolean;
@@ -20,6 +21,11 @@ export interface CommonParams {
skipFinalReload?: boolean;
useCardanoDerivation?: boolean;
chunkify?: boolean;
+ /**
+ * internal flag. if set to true, call will only return info about the method, not execute it.
+ * todo: this should be moved to another argument instead of mixing this with params
+ */
+ __info?: boolean;
}
export type Params = CommonParams & T & { bundle?: undefined };
diff --git a/packages/connect/src/types/settings.ts b/packages/connect/src/types/settings.ts
index 842b2fdbe66..867cc44f6a9 100644
--- a/packages/connect/src/types/settings.ts
+++ b/packages/connect/src/types/settings.ts
@@ -13,27 +13,22 @@ export interface ConnectSettingsPublic {
manifest?: Manifest;
connectSrc?: string;
debug?: boolean;
- hostLabel?: string;
- hostIcon?: string;
popup?: boolean;
transportReconnect?: boolean;
- webusb?: boolean; // deprecated
transports?: (Transport['name'] | Transport | (new (...args: any[]) => Transport))[];
pendingTransportEvent?: boolean;
lazyLoad?: boolean;
interactionTimeout?: number;
trustedHost: boolean;
- coreMode?: 'auto' | 'popup' | 'iframe';
- /* _extendWebextensionLifetime features makes the service worker in @trezor/connect-webextension stay alive longer */
- _extendWebextensionLifetime?: boolean;
/**
* for internal use only!
* in some exotic setups (suite-web where iframe is embedded locally), you might need to tell connect where it should search for sessions background shared-worker
*/
- _sessionsBackgroundUrl?: string;
- deeplinkOpen?: (url: string) => void;
- deeplinkCallbackUrl?: string;
- deeplinkUrl?: string;
+ _sessionsBackgroundUrl?: null | string;
+ // URL for binary files such as firmware, may be local or remote
+ binFilesBaseUrl?: string;
+ // enable firmware hash check automatically when device connects. Requires binFilesBaseUrl to be set.
+ enableFirmwareHashCheck?: boolean;
}
// internal part, not to be accepted from .init()
@@ -53,4 +48,23 @@ export interface ConnectSettingsInternal {
useCoreInPopup?: boolean;
}
-export type ConnectSettings = ConnectSettingsPublic & ConnectSettingsInternal;
+export interface ConnectSettingsWeb {
+ hostLabel?: string;
+ hostIcon?: string;
+ coreMode?: 'auto' | 'popup' | 'iframe' | 'deeplink';
+}
+export interface ConnectSettingsWebextension {
+ /** _extendWebextensionLifetime features makes the service worker in @trezor/connect-webextension stay alive longer */
+ _extendWebextensionLifetime?: boolean;
+}
+export interface ConnectSettingsMobile {
+ deeplinkUrl: string;
+ deeplinkOpen?: (url: string) => void;
+ deeplinkCallbackUrl?: string;
+}
+
+export type ConnectSettings = ConnectSettingsPublic &
+ ConnectSettingsInternal &
+ ConnectSettingsWeb &
+ ConnectSettingsWebextension &
+ ConnectSettingsMobile;
diff --git a/packages/connect/src/types/utils.ts b/packages/connect/src/types/utils.ts
index f2f63f42afb..0c0a70d8631 100644
--- a/packages/connect/src/types/utils.ts
+++ b/packages/connect/src/types/utils.ts
@@ -18,3 +18,6 @@ export type MessageFactoryFn = UnionToIntersection<
) => { event: Group; type: Event['type']; payload: undefined }
: never
>;
+
+export const typedObjectKeys = >(obj: T): Array =>
+ Object.keys(obj) as Array;
diff --git a/packages/connect/src/utils/__fixtures__/accountUtils.ts b/packages/connect/src/utils/__fixtures__/accountUtils.ts
index bb875aacee5..367b60e0154 100644
--- a/packages/connect/src/utils/__fixtures__/accountUtils.ts
+++ b/packages/connect/src/utils/__fixtures__/accountUtils.ts
@@ -2,7 +2,6 @@ import coinsJSON from '@trezor/connect-common/files/coins.json';
import coinsJSONEth from '@trezor/connect-common/files/coins-eth.json';
import { getAccountLabel, isUtxoBased } from '../accountUtils';
-
import {
parseCoinsJson,
getBitcoinNetwork,
diff --git a/packages/connect/src/utils/__fixtures__/ethereumUtils.ts b/packages/connect/src/utils/__fixtures__/ethereumUtils.ts
index d796759e323..cb2e164f79d 100644
--- a/packages/connect/src/utils/__fixtures__/ethereumUtils.ts
+++ b/packages/connect/src/utils/__fixtures__/ethereumUtils.ts
@@ -2,7 +2,6 @@ import coinsJSON from '@trezor/connect-common/files/coins.json';
import coinsJSONEth from '@trezor/connect-common/files/coins-eth.json';
import { getNetworkLabel } from '../ethereumUtils';
-
import { parseCoinsJson, getEthereumNetwork } from '../../data/coinInfo';
parseCoinsJson({
diff --git a/packages/connect/src/utils/__fixtures__/formatUtils.ts b/packages/connect/src/utils/__fixtures__/formatUtils.ts
index 52d9a6fd123..1bcab841b21 100644
--- a/packages/connect/src/utils/__fixtures__/formatUtils.ts
+++ b/packages/connect/src/utils/__fixtures__/formatUtils.ts
@@ -1,7 +1,6 @@
import coinsJSON from '@trezor/connect-common/files/coins.json';
import { formatAmount } from '../formatUtils';
-
import { parseCoinsJson, getBitcoinNetwork } from '../../data/coinInfo';
parseCoinsJson(coinsJSON);
diff --git a/packages/connect/src/utils/__tests__/accountUtils.test.ts b/packages/connect/src/utils/__tests__/accountUtils.test.ts
index 52293b79030..6136ef991a1 100644
--- a/packages/connect/src/utils/__tests__/accountUtils.test.ts
+++ b/packages/connect/src/utils/__tests__/accountUtils.test.ts
@@ -1,5 +1,4 @@
import { getAccountLabel, isUtxoBased } from '../accountUtils';
-
import * as fixtures from '../__fixtures__/accountUtils';
describe('utils/accountUtils', () => {
@@ -20,6 +19,5 @@ describe('utils/accountUtils', () => {
// todo:
describe.skip('getAccountAddressN', () => {});
- describe.skip('getAccountLabel', () => {});
describe.skip('getPublicKeyLabel', () => {});
});
diff --git a/packages/connect/src/utils/__tests__/addressUtils.test.ts b/packages/connect/src/utils/__tests__/addressUtils.test.ts
index 392d87fb38f..2676b5e00d4 100644
--- a/packages/connect/src/utils/__tests__/addressUtils.test.ts
+++ b/packages/connect/src/utils/__tests__/addressUtils.test.ts
@@ -1,4 +1,5 @@
import coinsJSON from '@trezor/connect-common/files/coins.json';
+
import { parseCoinsJson, getBitcoinNetwork } from '../../data/coinInfo';
import * as utils from '../addressUtils';
import * as fixtures from '../__fixtures__/addressUtils';
diff --git a/packages/connect/src/utils/__tests__/deviceFeaturesUtils.test.ts b/packages/connect/src/utils/__tests__/deviceFeaturesUtils.test.ts
index 6ea27f6dd3e..7340ea089c5 100644
--- a/packages/connect/src/utils/__tests__/deviceFeaturesUtils.test.ts
+++ b/packages/connect/src/utils/__tests__/deviceFeaturesUtils.test.ts
@@ -2,7 +2,6 @@ import coinsJSON from '@trezor/connect-common/files/coins.json';
import coinsJSONEth from '@trezor/connect-common/files/coins-eth.json';
import { parseCoinsJson, getAllNetworks } from '../../data/coinInfo';
-
import {
getUnavailableCapabilities,
parseCapabilities,
@@ -118,16 +117,16 @@ describe('utils/deviceFeaturesUtils', () => {
const featT2B1 = {
major_version: 2,
minor_version: 6,
- patch_version: 1,
+ patch_version: 2,
capabilities: undefined,
internal_model: DeviceModelInternal.T2B1,
} as unknown as Features;
featT2B1.capabilities = parseCapabilities(featT2B1);
it('default T1B1', () => {
- const coins = getAllNetworks();
+ const coins2 = getAllNetworks();
- expect(getUnavailableCapabilities(featT1B1, coins)).toEqual({
+ expect(getUnavailableCapabilities(featT1B1, coins2)).toEqual({
ada: 'no-support',
tada: 'no-support',
bnb: 'update-required',
@@ -135,6 +134,7 @@ describe('utils/deviceFeaturesUtils', () => {
eos: 'no-support',
maid: 'no-capability',
pol: 'update-required',
+ op: 'update-required',
omni: 'no-capability',
ppc: 'update-required',
sol: 'no-support',
@@ -165,9 +165,9 @@ describe('utils/deviceFeaturesUtils', () => {
});
it('default T2T1', () => {
- const coins = getAllNetworks();
+ const coins2 = getAllNetworks();
- expect(getUnavailableCapabilities(featT2T1, coins)).toEqual({
+ expect(getUnavailableCapabilities(featT2T1, coins2)).toEqual({
replaceTransaction: 'update-required',
amountUnit: 'update-required',
bnb: 'update-required',
@@ -176,6 +176,7 @@ describe('utils/deviceFeaturesUtils', () => {
'eip712-domain-only': 'update-required',
maid: 'no-capability',
pol: 'update-required',
+ op: 'update-required',
omni: 'no-capability',
taproot: 'update-required',
tsep: 'update-required',
@@ -191,9 +192,9 @@ describe('utils/deviceFeaturesUtils', () => {
});
it('default T2B1', () => {
- const coins = getAllNetworks();
+ const coins2 = getAllNetworks();
- expect(getUnavailableCapabilities(featT2B1, coins)).toEqual({
+ expect(getUnavailableCapabilities(featT2B1, coins2)).toEqual({
breeze: 'no-support',
btg: 'no-support',
tbtg: 'no-support',
@@ -221,53 +222,57 @@ describe('utils/deviceFeaturesUtils', () => {
});
});
- it('T2T1 update-required', done => {
- jest.resetModules();
-
- jest.mock('../../data/config', () => ({
- __esModule: true,
- config: {
- supportedFirmware: [
- {
- min: { T1B1: '0', T2T1: '2.99.99' },
- capabilities: ['newCapabilityOrFeature'],
- },
- ],
- },
- }));
-
- import('../deviceFeaturesUtils').then(({ getUnavailableCapabilities }) => {
- // added new capability
- expect(getUnavailableCapabilities(featT2T1, coins)).toEqual({
- newCapabilityOrFeature: 'update-required',
+ it('T2T1 update-required', () =>
+ new Promise(done => {
+ jest.resetModules();
+
+ jest.mock('../../data/config', () => ({
+ __esModule: true,
+ config: {
+ supportedFirmware: [
+ {
+ min: { T1B1: '0', T2T1: '2.99.99' },
+ capabilities: ['newCapabilityOrFeature'],
+ },
+ ],
+ },
+ }));
+
+ // eslint-disable-next-line @typescript-eslint/no-shadow
+ import('../deviceFeaturesUtils').then(({ getUnavailableCapabilities }) => {
+ // added new capability
+ expect(getUnavailableCapabilities(featT2T1, coins)).toEqual({
+ newCapabilityOrFeature: 'update-required',
+ });
+ done();
});
- done();
- });
- });
-
- it('T2T1 no-support', done => {
- jest.resetModules();
-
- jest.mock('../../data/config', () => ({
- __esModule: true,
- config: {
- supportedFirmware: [
- {
- min: { T1B1: '0', T2T1: '0' },
- capabilities: ['newCapabilityOrFeature'],
- },
- ],
- },
}));
- import('../deviceFeaturesUtils').then(({ getUnavailableCapabilities }) => {
- // added new capability
- expect(getUnavailableCapabilities(featT2T1, coins)).toEqual({
- newCapabilityOrFeature: 'no-support',
+ it('T2T1 no-support', () =>
+ new Promise