@exodus/hardware-wallets 1.2.1 → 1.3.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,12 @@
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.3.0](https://github.com/ExodusMovement/exodus-hydra/compare/@exodus/hardware-wallets@1.2.1...@exodus/hardware-wallets@1.3.0) (2024-09-19)
7
+
8
+ ### Features
9
+
10
+ - add multisig support to hardware-wallets ([#9312](https://github.com/ExodusMovement/exodus-hydra/issues/9312)) ([62afbf1](https://github.com/ExodusMovement/exodus-hydra/commit/62afbf1fa0295e3267b807c38a756a9169fdd816))
11
+
6
12
  ## [1.2.1](https://github.com/ExodusMovement/exodus-hydra/compare/@exodus/hardware-wallets@1.2.0...@exodus/hardware-wallets@1.2.1) (2024-09-13)
7
13
 
8
14
  ### Bug Fixes
@@ -18,9 +18,9 @@ declare const hardwareWalletsApiDefinition: {
18
18
  canAccessAsset: ({ assetName }: import("../module/interfaces.js").CanAccessAssetParams) => Promise<boolean>;
19
19
  ensureApplicationIsOpened: ({ assetName }: import("../module/interfaces.js").EnsureApplicationIsOpenedParams) => Promise<void>;
20
20
  scan: ({ assetName, accountIndexes, addressLimit, addressOffset, }: import("../module/interfaces.js").ScanParams) => Promise<import("../module/interfaces.js").ScanResult>;
21
- sync: ({ accountIndex }: import("../module/interfaces.js").SyncParams) => Promise<import("../module/interfaces.js").SyncedKeysId>;
21
+ sync: ({ accountIndex: index, isMultisig }?: import("../module/interfaces.js").SyncParams) => Promise<import("../module/interfaces.js").SyncedKeysId>;
22
22
  addPublicKeysToWalletAccount: ({ walletAccountName, syncedKeysId, }: import("../module/interfaces.js").StoreSyncedKeysParams) => Promise<void>;
23
- create: ({ syncedKeysId }: import("../module/interfaces.js").CreateParams) => Promise<void>;
23
+ create: ({ syncedKeysId, isMultisig }: import("../module/interfaces.js").CreateParams) => Promise<import("@exodus/models").WalletAccount>;
24
24
  };
25
25
  };
26
26
  readonly dependencies: readonly ["hardwareWallets", "txLogMonitors", "restoreProgressTracker"];
package/lib/index.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import hardwareWalletsApiDefinition from './api/index.js';
1
2
  declare const hardwareWallets: () => {
2
3
  readonly id: "hardwareWallets";
3
4
  readonly definitions: readonly [{
@@ -20,9 +21,9 @@ declare const hardwareWallets: () => {
20
21
  canAccessAsset: ({ assetName }: import("./module/interfaces.js").CanAccessAssetParams) => Promise<boolean>;
21
22
  ensureApplicationIsOpened: ({ assetName }: import("./module/interfaces.js").EnsureApplicationIsOpenedParams) => Promise<void>;
22
23
  scan: ({ assetName, accountIndexes, addressLimit, addressOffset, }: import("./module/interfaces.js").ScanParams) => Promise<import("./module/interfaces.js").ScanResult>;
23
- sync: ({ accountIndex }: import("./module/interfaces.js").SyncParams) => Promise<import("./module/interfaces.js").SyncedKeysId>;
24
+ sync: ({ accountIndex: index, isMultisig }?: import("./module/interfaces.js").SyncParams) => Promise<import("./module/interfaces.js").SyncedKeysId>;
24
25
  addPublicKeysToWalletAccount: ({ walletAccountName, syncedKeysId, }: import("./module/interfaces.js").StoreSyncedKeysParams) => Promise<void>;
25
- create: ({ syncedKeysId }: import("./module/interfaces.js").CreateParams) => Promise<void>;
26
+ create: ({ syncedKeysId, isMultisig }: import("./module/interfaces.js").CreateParams) => Promise<import("@exodus/models").WalletAccount>;
26
27
  };
27
28
  };
28
29
  readonly dependencies: readonly ["hardwareWallets", "txLogMonitors", "restoreProgressTracker"];
@@ -36,4 +37,5 @@ declare const hardwareWallets: () => {
36
37
  };
37
38
  }];
38
39
  };
40
+ export type HardwareWalletsApi = ReturnType<typeof hardwareWalletsApiDefinition.factory>;
39
41
  export default hardwareWallets;
@@ -1,6 +1,6 @@
1
1
  import { WalletAccount } from '@exodus/models';
2
2
  import Emitter from '@exodus/wild-emitter';
3
- import type { HardwareSignerProvider, CanAccessAssetParams, CreateParams, StoreSyncedKeysParams, ScanParams, SyncParams, EnsureApplicationIsOpenedParams, SignTransactionParams, ScanResult, SyncedKeysId, RequireDeviceForParams } from './interfaces.js';
3
+ import type { HardwareSignerProvider, CanAccessAssetParams, CreateParams, StoreSyncedKeysParams, ScanParams, SyncParams, EnsureApplicationIsOpenedParams, SignTransactionParams, ScanResult, SyncedKeysId, GetAddressParams, RequireDeviceForParams } from './interfaces.js';
4
4
  import type { HardwareWalletDiscovery, SignMessageParams } from '@exodus/hw-common';
5
5
  import type { Atom } from '@exodus/atoms';
6
6
  import type { IPublicKeyStore } from '@exodus/public-key-store';
@@ -21,7 +21,7 @@ export declare class HardwareWallets implements HardwareSignerProvider {
21
21
  #private;
22
22
  readonly events: Emitter<string, any>;
23
23
  constructor({ assetsModule, ledgerDiscovery, logger, userInterface, publicKeyStore, wallet, walletAccountsAtom, walletAccounts, }: Dependencies);
24
- signTransaction: ({ baseAssetName, unsignedTx, walletAccount }: SignTransactionParams) => Promise<any>;
24
+ signTransaction: ({ baseAssetName, unsignedTx, walletAccount, multisigData, }: SignTransactionParams) => Promise<any>;
25
25
  signMessage: ({ assetName, derivationPath, message }: SignMessageParams) => Promise<any>;
26
26
  isDeviceConnected: () => Promise<boolean>;
27
27
  scanForDevices: () => Promise<void>;
@@ -32,12 +32,13 @@ export declare class HardwareWallets implements HardwareSignerProvider {
32
32
  canAccessAsset: ({ assetName }: CanAccessAssetParams) => Promise<boolean>;
33
33
  listUseableAssetNames: () => Promise<string[]>;
34
34
  ensureApplicationIsOpened: ({ assetName }: EnsureApplicationIsOpenedParams) => Promise<void>;
35
+ getAddress: ({ assetName, accountIndex, addressIndex, multisigData, displayOnDevice, }: GetAddressParams) => Promise<string>;
35
36
  scan: ({ assetName, accountIndexes, addressLimit, addressOffset, }: ScanParams) => Promise<ScanResult>;
36
- sync: ({ accountIndex }: SyncParams) => Promise<SyncedKeysId>;
37
+ sync: ({ accountIndex: index, isMultisig }?: SyncParams) => Promise<SyncedKeysId>;
37
38
  addPublicKeysToWalletAccount: ({ walletAccountName, syncedKeysId, }: StoreSyncedKeysParams) => Promise<void>;
38
- create: ({ syncedKeysId }: CreateParams) => Promise<void>;
39
+ create: ({ syncedKeysId, isMultisig }: CreateParams) => Promise<WalletAccount>;
39
40
  requireDeviceFor: ({ walletAccount }: RequireDeviceForParams) => Promise<{
40
- signTransaction: ({ baseAssetName, unsignedTx, walletAccount }: SignTransactionParams) => Promise<any>;
41
+ signTransaction: ({ baseAssetName, unsignedTx, walletAccount, multisigData, }: SignTransactionParams) => Promise<any>;
41
42
  signMessage: ({ assetName, derivationPath, message }: SignMessageParams) => Promise<any>;
42
43
  }>;
43
44
  }
@@ -82,7 +82,7 @@ export class HardwareWallets {
82
82
  this.#requestUserAction({ scenario: 'completed' });
83
83
  throw new UserCancelledError();
84
84
  };
85
- signTransaction = async ({ baseAssetName, unsignedTx, walletAccount }) => {
85
+ signTransaction = async ({ baseAssetName, unsignedTx, walletAccount, multisigData, }) => {
86
86
  const baseAsset = this.#assetsModule.getAsset(baseAssetName);
87
87
  const accountIndex = walletAccount.index;
88
88
  const sign = async ({ hardwareDevice }) => {
@@ -90,6 +90,7 @@ export class HardwareWallets {
90
90
  unsignedTx,
91
91
  hardwareDevice,
92
92
  accountIndex,
93
+ multisigData,
93
94
  });
94
95
  };
95
96
  return this.#signGeneric({ baseAssetName, scenario: 'signTransaction', sign });
@@ -152,10 +153,11 @@ export class HardwareWallets {
152
153
  }
153
154
  }
154
155
  };
155
- #getAddress = async ({ assetName, accountIndex, addressIndex }) => {
156
+ getAddress = async ({ assetName, accountIndex, addressIndex, multisigData, displayOnDevice, }) => {
156
157
  const asset = this.#assetsModule.getAsset(assetName);
157
158
  const supportedPurposes = asset.baseAsset.api.getSupportedPurposes({
158
159
  compatibilityMode: 'ledger',
160
+ isMultisig: !!multisigData,
159
161
  });
160
162
  const { derivationPath } = asset.baseAsset.api.getKeyIdentifier({
161
163
  compatibilityMode: 'ledger',
@@ -168,6 +170,8 @@ export class HardwareWallets {
168
170
  return device.getAddress({
169
171
  assetName,
170
172
  derivationPath,
173
+ multisigData,
174
+ displayOnDevice,
171
175
  });
172
176
  };
173
177
  #getXPUB = async ({ device, baseAsset, purpose, accountIndex, }) => {
@@ -220,7 +224,7 @@ export class HardwareWallets {
220
224
  const startIndex = offsetBy;
221
225
  const addresses = [];
222
226
  for (let idx = startIndex; idx < startIndex + n; idx++) {
223
- addresses.push(await this.#getAddress({ assetName, accountIndex, addressIndex: idx }));
227
+ addresses.push(await this.getAddress({ assetName, accountIndex, addressIndex: idx }));
224
228
  }
225
229
  const fetchedBalances = await Promise.all(addresses.map(async (address) => ({
226
230
  address,
@@ -253,9 +257,10 @@ export class HardwareWallets {
253
257
  }
254
258
  return accounts;
255
259
  };
256
- sync = async ({ accountIndex }) => {
260
+ sync = async ({ accountIndex: index, isMultisig } = {}) => {
257
261
  const keysToSync = [];
258
262
  const device = await this.#ledgerDiscovery.getFirstDevice();
263
+ const accountIndex = index ?? this.#walletAccounts.getNextIndex({ source: WalletAccount.LEDGER_SRC });
259
264
  const useableAssetNames = new Set(await device.listUseableAssetNames());
260
265
  for (const assetName of useableAssetNames) {
261
266
  const asset = this.#assetsModule.getAsset(assetName);
@@ -263,6 +268,7 @@ export class HardwareWallets {
263
268
  const baseAsset = asset.baseAsset;
264
269
  const supportedPurposes = baseAsset.api.getSupportedPurposes({
265
270
  compatibilityMode: 'ledger',
271
+ isMultisig,
266
272
  });
267
273
  for (const purpose of supportedPurposes) {
268
274
  const { keyIdentifier, xpub, publicKey, } = (await this.#getXPUB({ device, baseAsset, purpose, accountIndex })) ||
@@ -287,7 +293,7 @@ export class HardwareWallets {
287
293
  }
288
294
  this.events.emit('syncAssets', { assetNames });
289
295
  };
290
- create = async ({ syncedKeysId }) => {
296
+ create = async ({ syncedKeysId, isMultisig }) => {
291
297
  assert(this.#syncedKeysMap.has(syncedKeysId), `no synchronized keys found for id ${syncedKeysId}`);
292
298
  const { accountIndex } = this.#syncedKeysMap.get(syncedKeysId);
293
299
  const walletAccount = new WalletAccount({
@@ -296,11 +302,13 @@ export class HardwareWallets {
296
302
  source: WalletAccount.LEDGER_SRC,
297
303
  index: accountIndex,
298
304
  id: randomBytes(32).toString('hex'),
305
+ isMultisig: !!isMultisig,
299
306
  });
300
307
  const walletAccountName = walletAccount.toString();
301
308
  await this.#walletAccounts.create(walletAccount);
302
309
  await this.#walletAccounts.setActive(walletAccountName);
303
310
  await this.addPublicKeysToWalletAccount({ walletAccountName, syncedKeysId });
311
+ return walletAccount;
304
312
  };
305
313
  requireDeviceFor = async ({ walletAccount }) => {
306
314
  return {
@@ -1,5 +1,5 @@
1
1
  import type { WalletAccount } from '@exodus/models';
2
- import type { HardwareWalletDevice, SignMessageParams } from '@exodus/hw-common';
2
+ import type { 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>;
@@ -10,7 +10,7 @@ export interface HardwareSignerProvider {
10
10
  scan: ({ assetName, accountIndexes, addressOffset }: ScanParams) => Promise<ScanResult>;
11
11
  sync: ({ accountIndex }: SyncParams) => Promise<SyncedKeysId>;
12
12
  addPublicKeysToWalletAccount: ({ walletAccountName, syncedKeysId, }: StoreSyncedKeysParams) => Promise<void>;
13
- create: ({ syncedKeysId }: CreateParams) => Promise<void>;
13
+ create: ({ syncedKeysId, isMultisig }: CreateParams) => Promise<WalletAccount>;
14
14
  signTransaction: ({ baseAssetName, unsignedTx, walletAccount, }: SignTransactionParams) => Promise<any>;
15
15
  signMessage: ({ assetName, derivationPath, message }: SignMessageParams) => Promise<any>;
16
16
  }
@@ -53,7 +53,8 @@ export type ScannedAccount = {
53
53
  };
54
54
  export type ScanResult = ScannedAccount[];
55
55
  export interface SyncParams {
56
- accountIndex: number;
56
+ accountIndex?: number;
57
+ isMultisig?: boolean;
57
58
  }
58
59
  export type SyncedKeysId = string;
59
60
  export interface SyncedKeysData {
@@ -71,6 +72,7 @@ export interface StoreSyncedKeysParams {
71
72
  }
72
73
  export interface CreateParams {
73
74
  syncedKeysId: SyncedKeysId;
75
+ isMultisig?: boolean;
74
76
  }
75
77
  export type GenericSignCallback = ({ hardwareDevice, }: {
76
78
  hardwareDevice: HardwareWalletDevice;
@@ -84,6 +86,7 @@ export interface SignTransactionParams {
84
86
  baseAssetName: string;
85
87
  unsignedTx: UnsignedTransaction;
86
88
  walletAccount: WalletAccount;
89
+ multisigData?: MultisigData;
87
90
  }
88
91
  export interface UnsignedTransaction {
89
92
  txData: any;
@@ -93,6 +96,8 @@ export interface GetAddressParams {
93
96
  assetName: string;
94
97
  accountIndex: number;
95
98
  addressIndex: number;
99
+ multisigData?: MultisigData;
100
+ displayOnDevice?: boolean;
96
101
  }
97
102
  export interface GetXPUBParams {
98
103
  device: HardwareWalletDevice;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@exodus/hardware-wallets",
3
- "version": "1.2.1",
3
+ "version": "1.3.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": {
@@ -41,5 +41,5 @@
41
41
  "@exodus/public-key-store": "^1.2.2",
42
42
  "@types/randombytes": "^2.0.3"
43
43
  },
44
- "gitHead": "4b5ea436184efe7a8c6017705893436905cd8c2f"
44
+ "gitHead": "758ea1b22f91994c5982c19071ff1f80f63e48be"
45
45
  }