@exodus/hardware-wallets 3.4.1 → 3.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -3,6 +3,16 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
+ ## [3.5.0](https://github.com/ExodusMovement/exodus-hydra/compare/@exodus/hardware-wallets@3.4.0...@exodus/hardware-wallets@3.5.0) (2025-12-11)
7
+
8
+ ### Features
9
+
10
+ - feat: throw device unininitialized error for hw (#14601)
11
+
12
+ ### Bug Fixes
13
+
14
+ - fix: various trezor and hardware-wallets fixes (#14502)
15
+
6
16
  ## [3.4.1](https://github.com/ExodusMovement/exodus-hydra/compare/@exodus/hardware-wallets@3.4.0...@exodus/hardware-wallets@3.4.1) (2025-11-28)
7
17
 
8
18
  ### Bug Fixes
@@ -3,7 +3,7 @@ import { WalletAccount } from '@exodus/models';
3
3
  import Emitter from '@exodus/wild-emitter';
4
4
  import { randomBytes } from '@exodus/crypto/randomBytes';
5
5
  import delay from 'delay';
6
- import { NoDeviceFoundError, UserRefusedError } from '@exodus/hw-common';
6
+ import { DeviceUninitializedError, NoDeviceFoundError, UserRefusedError } from '@exodus/hw-common';
7
7
  import restrictConcurrency from 'make-concurrent';
8
8
  import pDefer from 'p-defer';
9
9
  export class HardwareWallets {
@@ -31,12 +31,41 @@ export class HardwareWallets {
31
31
  this.#walletAccountsAtom = walletAccountsAtom;
32
32
  this.#walletAccounts = walletAccounts;
33
33
  }
34
- #getSelectedDevice = async () => {
35
- const [trezors, ledgers] = await Promise.all([
36
- this.#trezorDiscovery.list().catch(() => []),
37
- this.#ledgerDiscovery.list().catch(() => []),
38
- ]);
39
- const descriptors = [...trezors, ...ledgers];
34
+ #listTrezorDevicesOrEmpty = async () => {
35
+ try {
36
+ return await this.#trezorDiscovery.list();
37
+ }
38
+ catch (error) {
39
+ if (error instanceof DeviceUninitializedError) {
40
+ throw error;
41
+ }
42
+ return [];
43
+ }
44
+ };
45
+ #listLedgerDevicesOrEmpty = async () => {
46
+ try {
47
+ return await this.#ledgerDiscovery.list();
48
+ }
49
+ catch {
50
+ return [];
51
+ }
52
+ };
53
+ #getSelectedDevice = async (walletAccount) => {
54
+ let descriptors;
55
+ const manufacturer = walletAccount?.source;
56
+ if (manufacturer === 'ledger') {
57
+ descriptors = await this.#listLedgerDevicesOrEmpty();
58
+ }
59
+ else if (manufacturer === 'trezor') {
60
+ descriptors = await this.#listTrezorDevicesOrEmpty();
61
+ }
62
+ else {
63
+ const [trezors, ledgers] = await Promise.all([
64
+ this.#listTrezorDevicesOrEmpty(),
65
+ this.#listLedgerDevicesOrEmpty(),
66
+ ]);
67
+ descriptors = [...trezors, ...ledgers];
68
+ }
40
69
  if (descriptors[0]) {
41
70
  return { device: await descriptors[0].get() };
42
71
  }
@@ -70,7 +99,7 @@ export class HardwareWallets {
70
99
  this.#isRetrying = true;
71
100
  try {
72
101
  this.#logger.debug(`Attempting to get selected device for signing request with id: ${id}`);
73
- const { device } = await this.#getSelectedDevice();
102
+ const { device } = await this.#getSelectedDevice(request.walletAccount);
74
103
  this.#logger.debug(`Attempting to sign for signing request with id: ${id}`);
75
104
  const result = await request.sign({ device });
76
105
  await this.#deleteSigningRequest(id);
@@ -115,7 +144,7 @@ export class HardwareWallets {
115
144
  if (fromUI) {
116
145
  this.#logger.debug(`Cancelling signing request on device for id: ${id}`);
117
146
  try {
118
- const { device } = await this.#getSelectedDevice();
147
+ const { device } = await this.#getSelectedDevice(request.walletAccount);
119
148
  await device.cancelAction();
120
149
  this.#logger.debug(`Succesfully cancelled signing request on device for id: ${id}`);
121
150
  }
@@ -125,13 +154,14 @@ export class HardwareWallets {
125
154
  }
126
155
  request.reject(new UserRefusedError(!fromUI));
127
156
  };
128
- #signGeneric = restrictConcurrency(async ({ baseAssetName, scenario, sign }) => {
157
+ #signGeneric = restrictConcurrency(async ({ baseAssetName, scenario, sign, walletAccount }) => {
129
158
  const id = randomBytes(16).toString('hex');
130
159
  this.#logger.debug(`Starting signing request for ${baseAssetName} with scenario: ${scenario} and id: ${id}`);
131
160
  const deferred = pDefer();
132
161
  this.#signingRequest = {
133
162
  id,
134
163
  baseAssetName,
164
+ walletAccount,
135
165
  sign: async ({ device }) => {
136
166
  await this.#updateSigningRequest({
137
167
  id,
@@ -158,7 +188,12 @@ export class HardwareWallets {
158
188
  multisigData,
159
189
  });
160
190
  };
161
- return this.#signGeneric({ baseAssetName, scenario: 'signTransaction', sign });
191
+ return this.#signGeneric({
192
+ baseAssetName,
193
+ scenario: 'signTransaction',
194
+ sign,
195
+ walletAccount,
196
+ });
162
197
  };
163
198
  signMessage = async ({ assetName, derivationPath, message }) => {
164
199
  const baseAssetName = this.#assetsModule.getAsset(assetName).baseAsset.name;
@@ -178,7 +213,11 @@ export class HardwareWallets {
178
213
  return true;
179
214
  }
180
215
  }
181
- catch { }
216
+ catch (error) {
217
+ if (error instanceof DeviceUninitializedError) {
218
+ throw error;
219
+ }
220
+ }
182
221
  try {
183
222
  const devices = await this.#ledgerDiscovery.list();
184
223
  return devices.length > 0;
@@ -199,8 +238,8 @@ export class HardwareWallets {
199
238
  };
200
239
  getAvailableDevices = async () => {
201
240
  const [ledgers, trezors] = await Promise.all([
202
- this.#ledgerDiscovery.list().catch(() => []),
203
- this.#trezorDiscovery.list().catch(() => []),
241
+ this.#listLedgerDevicesOrEmpty(),
242
+ this.#listTrezorDevicesOrEmpty(),
204
243
  ]);
205
244
  return [...ledgers, ...trezors].map((device) => ({
206
245
  model: device.model,
@@ -84,6 +84,7 @@ export interface GenericSignParams {
84
84
  baseAssetName: string;
85
85
  scenario: 'signTransaction' | 'signMessage';
86
86
  sign: GenericSignCallback;
87
+ walletAccount?: WalletAccount;
87
88
  }
88
89
  export interface SigningRequestState {
89
90
  id: string;
@@ -94,6 +95,7 @@ export interface SigningRequestState {
94
95
  export interface SigningRequest {
95
96
  id: string;
96
97
  baseAssetName?: string;
98
+ walletAccount?: WalletAccount;
97
99
  sign: GenericSignCallback;
98
100
  resolve: (result: any) => void;
99
101
  reject: (error: Error) => void;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@exodus/hardware-wallets",
3
- "version": "3.4.1",
3
+ "version": "3.5.0",
4
4
  "description": "An Exodus SDK feature that provides a high level abstraction for interacting with hardware wallet devices",
5
5
  "author": "Exodus Movement, Inc.",
6
6
  "repository": {
@@ -32,7 +32,7 @@
32
32
  "@exodus/basic-utils": "^3.2.0",
33
33
  "@exodus/bip32": "^4.0.2",
34
34
  "@exodus/crypto": "^1.0.0-rc.14",
35
- "@exodus/hw-common": "^3.2.0",
35
+ "@exodus/hw-common": "^3.3.0",
36
36
  "@exodus/models": "^12.18.0",
37
37
  "@exodus/redux-dependency-injection": "^4.0.0",
38
38
  "@exodus/wild-emitter": "^1.1.0",
@@ -53,5 +53,5 @@
53
53
  "access": "public",
54
54
  "provenance": false
55
55
  },
56
- "gitHead": "7c1b8b88571d94cec15b9512e5301922ac1b6017"
56
+ "gitHead": "91c5651b997e904755c35158697523f816611d71"
57
57
  }