@exodus/hardware-wallets 1.4.0 → 1.5.1
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 +17 -0
- package/lib/api/index.d.ts +1 -1
- package/lib/index.d.ts +1 -1
- package/lib/module/hardware-wallets.d.ts +1 -1
- package/lib/module/hardware-wallets.js +38 -28
- package/lib/module/interfaces.d.ts +4 -3
- package/package.json +6 -5
- package/lib/module/errors.d.ts +0 -4
- package/lib/module/errors.js +0 -6
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,23 @@
|
|
|
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
|
+
## [1.5.1](https://github.com/ExodusMovement/exodus-hydra/compare/@exodus/hardware-wallets@1.5.0...@exodus/hardware-wallets@1.5.1) (2024-10-17)
|
|
7
|
+
|
|
8
|
+
### Bug Fixes
|
|
9
|
+
|
|
10
|
+
- support cancelling transaction through ui ([#10058](https://github.com/ExodusMovement/exodus-hydra/issues/10058)) ([beb7d2d](https://github.com/ExodusMovement/exodus-hydra/commit/beb7d2d1aafba17678a34febcf2458163d9182d2))
|
|
11
|
+
|
|
12
|
+
## [1.5.0](https://github.com/ExodusMovement/exodus-hydra/compare/@exodus/hardware-wallets@1.4.0...@exodus/hardware-wallets@1.5.0) (2024-10-17)
|
|
13
|
+
|
|
14
|
+
### Features
|
|
15
|
+
|
|
16
|
+
- add `getSelectedDevice` ([#9822](https://github.com/ExodusMovement/exodus-hydra/issues/9822)) ([2c18c22](https://github.com/ExodusMovement/exodus-hydra/commit/2c18c22d419ba9136cd8b264e946b151df438c59))
|
|
17
|
+
- support adding model to wallet account ([#9936](https://github.com/ExodusMovement/exodus-hydra/issues/9936)) ([8c858f6](https://github.com/ExodusMovement/exodus-hydra/commit/8c858f6e08e41bee3261f444c3d25e8bdd385014))
|
|
18
|
+
|
|
19
|
+
### Bug Fixes
|
|
20
|
+
|
|
21
|
+
- unify user refused error handling ([#10052](https://github.com/ExodusMovement/exodus-hydra/issues/10052)) ([db85d10](https://github.com/ExodusMovement/exodus-hydra/commit/db85d108333630d09bad545c5ec1169b937e08fe))
|
|
22
|
+
|
|
6
23
|
## [1.4.0](https://github.com/ExodusMovement/exodus-hydra/compare/@exodus/hardware-wallets@1.3.0...@exodus/hardware-wallets@1.4.0) (2024-10-07)
|
|
7
24
|
|
|
8
25
|
### Features
|
package/lib/api/index.d.ts
CHANGED
|
@@ -10,7 +10,7 @@ declare const hardwareWalletsApiDefinition: {
|
|
|
10
10
|
hardwareWallets: {
|
|
11
11
|
isDeviceConnected: () => Promise<boolean>;
|
|
12
12
|
getAvailableDevices: () => Promise<{
|
|
13
|
-
model: ("
|
|
13
|
+
model: import("libraries/hw-common/lib/types.js").HardwareWalletDeviceModels;
|
|
14
14
|
name: string;
|
|
15
15
|
}[]>;
|
|
16
16
|
listUseableAssetNames: () => Promise<string[]>;
|
package/lib/index.d.ts
CHANGED
|
@@ -13,7 +13,7 @@ declare const hardwareWallets: () => {
|
|
|
13
13
|
hardwareWallets: {
|
|
14
14
|
isDeviceConnected: () => Promise<boolean>;
|
|
15
15
|
getAvailableDevices: () => Promise<{
|
|
16
|
-
model: ("
|
|
16
|
+
model: import("libraries/hw-common/lib/types.js").HardwareWalletDeviceModels;
|
|
17
17
|
name: string;
|
|
18
18
|
}[]>;
|
|
19
19
|
listUseableAssetNames: () => Promise<string[]>;
|
|
@@ -26,7 +26,7 @@ export declare class HardwareWallets implements HardwareSignerProvider {
|
|
|
26
26
|
isDeviceConnected: () => Promise<boolean>;
|
|
27
27
|
scanForDevices: () => Promise<void>;
|
|
28
28
|
getAvailableDevices: () => Promise<{
|
|
29
|
-
model: ("
|
|
29
|
+
model: import("@exodus/hw-common").HardwareWalletDeviceModels;
|
|
30
30
|
name: string;
|
|
31
31
|
}[]>;
|
|
32
32
|
canAccessAsset: ({ assetName }: CanAccessAssetParams) => Promise<boolean>;
|
|
@@ -3,7 +3,7 @@ import { WalletAccount } from '@exodus/models';
|
|
|
3
3
|
import Emitter from '@exodus/wild-emitter';
|
|
4
4
|
import randomBytes from 'randombytes';
|
|
5
5
|
import delay from 'delay';
|
|
6
|
-
import {
|
|
6
|
+
import { NoDeviceFoundError, UserRefusedError } from '@exodus/hw-common';
|
|
7
7
|
export class HardwareWallets {
|
|
8
8
|
#assetsModule;
|
|
9
9
|
#ledgerDiscovery;
|
|
@@ -25,26 +25,35 @@ export class HardwareWallets {
|
|
|
25
25
|
this.#walletAccountsAtom = walletAccountsAtom;
|
|
26
26
|
this.#walletAccounts = walletAccounts;
|
|
27
27
|
}
|
|
28
|
+
#getSelectedDevice = async () => {
|
|
29
|
+
const descriptors = await this.#ledgerDiscovery.list();
|
|
30
|
+
if (descriptors[0]) {
|
|
31
|
+
return { device: await descriptors[0].get() };
|
|
32
|
+
}
|
|
33
|
+
throw new NoDeviceFoundError();
|
|
34
|
+
};
|
|
28
35
|
#requestUserAction = async (params) => {
|
|
29
36
|
const rpc = this.#userInterface.getRPC();
|
|
30
37
|
return rpc.callMethod('handle-hardware-wallet', params);
|
|
31
38
|
};
|
|
32
39
|
#signGeneric = async ({ baseAssetName, scenario, sign }) => {
|
|
33
|
-
let needUserApproval = true;
|
|
34
40
|
let attempts = 0;
|
|
35
41
|
const MAX_ATTEMPTS = 50;
|
|
36
42
|
while (attempts < MAX_ATTEMPTS) {
|
|
37
43
|
try {
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
+
const approvePromise = this.#requestUserAction({
|
|
45
|
+
scenario,
|
|
46
|
+
baseAssetName,
|
|
47
|
+
});
|
|
48
|
+
const { device } = await this.#getSelectedDevice();
|
|
49
|
+
const runSign = async () => {
|
|
50
|
+
await device.ensureApplicationIsOpened(baseAssetName);
|
|
51
|
+
return sign({ device });
|
|
52
|
+
};
|
|
53
|
+
const result = await Promise.race([approvePromise, runSign()]);
|
|
54
|
+
if (typeof result === 'object' && result.tryAgain === false) {
|
|
55
|
+
throw new UserRefusedError(false);
|
|
44
56
|
}
|
|
45
|
-
const hardwareDevice = await this.#ledgerDiscovery.getFirstDevice();
|
|
46
|
-
await hardwareDevice.ensureApplicationIsOpened(baseAssetName);
|
|
47
|
-
const result = await sign({ hardwareDevice });
|
|
48
57
|
this.#requestUserAction({ scenario: 'completed' });
|
|
49
58
|
return result;
|
|
50
59
|
}
|
|
@@ -52,11 +61,10 @@ export class HardwareWallets {
|
|
|
52
61
|
if (error.message.includes('timeout')) {
|
|
53
62
|
break;
|
|
54
63
|
}
|
|
55
|
-
if (error.name === '
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
});
|
|
64
|
+
else if (error.name === 'UserRefusedError') {
|
|
65
|
+
throw error;
|
|
66
|
+
}
|
|
67
|
+
if (['DisconnectedDevice', 'DisconnectedDeviceDuringOperation'].includes(error.name)) {
|
|
60
68
|
continue;
|
|
61
69
|
}
|
|
62
70
|
try {
|
|
@@ -80,15 +88,15 @@ export class HardwareWallets {
|
|
|
80
88
|
}
|
|
81
89
|
}
|
|
82
90
|
this.#requestUserAction({ scenario: 'completed' });
|
|
83
|
-
throw new
|
|
91
|
+
throw new UserRefusedError(false);
|
|
84
92
|
};
|
|
85
93
|
signTransaction = async ({ baseAssetName, unsignedTx, walletAccount, multisigData, }) => {
|
|
86
94
|
const baseAsset = this.#assetsModule.getAsset(baseAssetName);
|
|
87
95
|
const accountIndex = walletAccount.index;
|
|
88
|
-
const sign = async ({
|
|
96
|
+
const sign = async ({ device }) => {
|
|
89
97
|
return baseAsset.api.signHardware({
|
|
90
98
|
unsignedTx,
|
|
91
|
-
hardwareDevice,
|
|
99
|
+
hardwareDevice: device,
|
|
92
100
|
accountIndex,
|
|
93
101
|
multisigData,
|
|
94
102
|
});
|
|
@@ -97,8 +105,8 @@ export class HardwareWallets {
|
|
|
97
105
|
};
|
|
98
106
|
signMessage = async ({ assetName, derivationPath, message }) => {
|
|
99
107
|
const baseAssetName = this.#assetsModule.getAsset(assetName).baseAsset.name;
|
|
100
|
-
const sign = async ({
|
|
101
|
-
return
|
|
108
|
+
const sign = async ({ device }) => {
|
|
109
|
+
return device.signMessage({
|
|
102
110
|
assetName: baseAssetName,
|
|
103
111
|
derivationPath,
|
|
104
112
|
message,
|
|
@@ -128,12 +136,12 @@ export class HardwareWallets {
|
|
|
128
136
|
};
|
|
129
137
|
canAccessAsset = async ({ assetName }) => {
|
|
130
138
|
const asset = this.#assetsModule.getAsset(assetName);
|
|
131
|
-
const device = await this.#
|
|
139
|
+
const { device } = await this.#getSelectedDevice();
|
|
132
140
|
const useableAssetNames = new Set(await device.listUseableAssetNames());
|
|
133
141
|
return useableAssetNames.has(asset.baseAsset.name);
|
|
134
142
|
};
|
|
135
143
|
listUseableAssetNames = async () => {
|
|
136
|
-
const device = await this.#
|
|
144
|
+
const { device } = await this.#getSelectedDevice();
|
|
137
145
|
return device.listUseableAssetNames();
|
|
138
146
|
};
|
|
139
147
|
ensureApplicationIsOpened = async ({ assetName }) => {
|
|
@@ -141,8 +149,8 @@ export class HardwareWallets {
|
|
|
141
149
|
let i = 0;
|
|
142
150
|
while (i < 3) {
|
|
143
151
|
try {
|
|
144
|
-
const
|
|
145
|
-
await
|
|
152
|
+
const { device } = await this.#getSelectedDevice();
|
|
153
|
+
await device.ensureApplicationIsOpened(asset.baseAsset.name);
|
|
146
154
|
}
|
|
147
155
|
catch (error) {
|
|
148
156
|
this.#logger.log(error);
|
|
@@ -166,7 +174,7 @@ export class HardwareWallets {
|
|
|
166
174
|
chainIndex: 0,
|
|
167
175
|
addressIndex,
|
|
168
176
|
});
|
|
169
|
-
const device = await this.#
|
|
177
|
+
const { device } = await this.#getSelectedDevice();
|
|
170
178
|
return device.getAddress({
|
|
171
179
|
assetName,
|
|
172
180
|
derivationPath,
|
|
@@ -259,7 +267,7 @@ export class HardwareWallets {
|
|
|
259
267
|
};
|
|
260
268
|
sync = async ({ accountIndex: index, isMultisig } = {}) => {
|
|
261
269
|
const keysToSync = [];
|
|
262
|
-
const device = await this.#
|
|
270
|
+
const { device } = await this.#getSelectedDevice();
|
|
263
271
|
const accountIndex = index ?? this.#walletAccounts.getNextIndex({ source: WalletAccount.LEDGER_SRC });
|
|
264
272
|
const useableAssetNames = new Set(await device.listUseableAssetNames());
|
|
265
273
|
for (const assetName of useableAssetNames) {
|
|
@@ -279,6 +287,7 @@ export class HardwareWallets {
|
|
|
279
287
|
const id = randomBytes(16).toString('hex');
|
|
280
288
|
this.#syncedKeysMap.set(id, {
|
|
281
289
|
accountIndex,
|
|
290
|
+
model: device.descriptor.model,
|
|
282
291
|
assetNames: useableAssetNames,
|
|
283
292
|
keysToSync,
|
|
284
293
|
});
|
|
@@ -295,11 +304,12 @@ export class HardwareWallets {
|
|
|
295
304
|
};
|
|
296
305
|
create = async ({ syncedKeysId, isMultisig }) => {
|
|
297
306
|
assert(this.#syncedKeysMap.has(syncedKeysId), `no synchronized keys found for id ${syncedKeysId}`);
|
|
298
|
-
const { accountIndex } = this.#syncedKeysMap.get(syncedKeysId);
|
|
307
|
+
const { accountIndex, model } = this.#syncedKeysMap.get(syncedKeysId);
|
|
299
308
|
const walletAccount = new WalletAccount({
|
|
300
309
|
label: `Ledger${accountIndex === 0 ? '' : ' ' + accountIndex}`,
|
|
301
310
|
icon: 'ledger',
|
|
302
311
|
source: WalletAccount.LEDGER_SRC,
|
|
312
|
+
model,
|
|
303
313
|
index: accountIndex,
|
|
304
314
|
id: randomBytes(32).toString('hex'),
|
|
305
315
|
isMultisig: !!isMultisig,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { WalletAccount } from '@exodus/models';
|
|
2
|
-
import type { HardwareWalletDevice, MultisigData, SignMessageParams } from '@exodus/hw-common';
|
|
2
|
+
import type { HardwareWalletDeviceModels, HardwareWalletDevice, MultisigData, SignMessageParams } from '@exodus/hw-common';
|
|
3
3
|
import type KeyIdentifier from '@exodus/key-identifier';
|
|
4
4
|
export interface HardwareSignerProvider {
|
|
5
5
|
isDeviceConnected: () => Promise<boolean>;
|
|
@@ -59,6 +59,7 @@ export interface SyncParams {
|
|
|
59
59
|
export type SyncedKeysId = string;
|
|
60
60
|
export interface SyncedKeysData {
|
|
61
61
|
accountIndex: number;
|
|
62
|
+
model: HardwareWalletDeviceModels;
|
|
62
63
|
assetNames: Set<string>;
|
|
63
64
|
keysToSync: KeyToSyncData[];
|
|
64
65
|
}
|
|
@@ -74,8 +75,8 @@ export interface CreateParams {
|
|
|
74
75
|
syncedKeysId: SyncedKeysId;
|
|
75
76
|
isMultisig?: boolean;
|
|
76
77
|
}
|
|
77
|
-
export type GenericSignCallback = ({
|
|
78
|
-
|
|
78
|
+
export type GenericSignCallback = ({ device }: {
|
|
79
|
+
device: HardwareWalletDevice;
|
|
79
80
|
}) => Promise<any>;
|
|
80
81
|
export interface GenericSignParams {
|
|
81
82
|
baseAssetName: string;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@exodus/hardware-wallets",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.5.1",
|
|
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": {
|
|
@@ -24,10 +24,11 @@
|
|
|
24
24
|
"clean": "run -T tsc --build --clean",
|
|
25
25
|
"lint": "run -T eslint .",
|
|
26
26
|
"lint:fix": "yarn lint --fix",
|
|
27
|
-
"test": "run -T jest",
|
|
27
|
+
"test": "run -T exodus-test --jest --esbuild",
|
|
28
28
|
"prepublishOnly": "yarn run -T build --scope @exodus/hardware-wallets"
|
|
29
29
|
},
|
|
30
30
|
"dependencies": {
|
|
31
|
+
"@exodus/hw-common": "^3.0.0",
|
|
31
32
|
"@exodus/models": "^12.0.1",
|
|
32
33
|
"@exodus/wild-emitter": "^1.1.0",
|
|
33
34
|
"delay": "^5.0.0",
|
|
@@ -37,11 +38,11 @@
|
|
|
37
38
|
"devDependencies": {
|
|
38
39
|
"@exodus/atoms": "^9.0.0",
|
|
39
40
|
"@exodus/dependency-types": "^2.1.0",
|
|
40
|
-
"@exodus/hw-common": "^2.5.0",
|
|
41
41
|
"@exodus/key-identifier": "^1.3.0",
|
|
42
42
|
"@exodus/logger": "^1.2.2",
|
|
43
43
|
"@exodus/public-key-provider": "^3.0.0",
|
|
44
|
-
"@types/randombytes": "^2.0.3"
|
|
44
|
+
"@types/randombytes": "^2.0.3",
|
|
45
|
+
"p-defer": "^4.0.1"
|
|
45
46
|
},
|
|
46
|
-
"gitHead": "
|
|
47
|
+
"gitHead": "c74a9968861467c805398c25b4017985e976ba31"
|
|
47
48
|
}
|
package/lib/module/errors.d.ts
DELETED