@exodus/hw-ledger 2.9.5 → 2.10.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 +6 -0
- package/lib/atoms/index.d.ts +12 -0
- package/lib/atoms/index.js +15 -1
- package/lib/index.d.ts +11 -1
- package/lib/index.js +4 -1
- package/lib/module/assets/bitcoin.d.ts +2 -1
- package/lib/module/assets/bitcoin.js +39 -8
- package/lib/module/assets/ethereum.js +1 -1
- package/lib/module/assets/index.d.ts +2 -1
- package/lib/module/assets/index.js +1 -0
- package/lib/module/assets/utils/psbt.d.ts +7 -3
- package/lib/module/assets/utils/psbt.js +42 -7
- package/lib/module/device.d.ts +3 -1
- package/lib/module/device.js +5 -3
- package/lib/module/discovery.d.ts +4 -3
- package/lib/module/discovery.js +15 -3
- package/package.json +5 -4
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
|
+
## [2.10.0](https://github.com/ExodusMovement/exodus-hydra/compare/@exodus/hw-ledger@2.9.5...@exodus/hw-ledger@2.10.0) (2024-09-16)
|
|
7
|
+
|
|
8
|
+
### Features
|
|
9
|
+
|
|
10
|
+
- add bitcoin multisig support to ledger ([#9218](https://github.com/ExodusMovement/exodus-hydra/issues/9218)) ([1b63e8a](https://github.com/ExodusMovement/exodus-hydra/commit/1b63e8a6ac7bab9cd580f602f2fd0cbff48ed01c))
|
|
11
|
+
|
|
6
12
|
## [2.9.5](https://github.com/ExodusMovement/exodus-hydra/compare/@exodus/hw-ledger@2.9.4...@exodus/hw-ledger@2.9.5) (2024-08-16)
|
|
7
13
|
|
|
8
14
|
### Bug Fixes
|
package/lib/atoms/index.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { Storage } from 'adapters/storage-interface';
|
|
1
2
|
export type BluetoothStatus = {
|
|
2
3
|
type: 'Unknown' | 'Resetting' | 'Unsupported' | 'Unauthorized' | 'PoweredOff' | 'PoweredOn';
|
|
3
4
|
available: boolean;
|
|
@@ -16,3 +17,14 @@ export declare const bluetoothScanningAtomDefinition: {
|
|
|
16
17
|
readonly dependencies: readonly [];
|
|
17
18
|
readonly public: true;
|
|
18
19
|
};
|
|
20
|
+
type Params = {
|
|
21
|
+
storage: Storage<Record<string, Buffer>>;
|
|
22
|
+
};
|
|
23
|
+
export declare const walletPolicyAtomDefinition: {
|
|
24
|
+
readonly id: "walletPolicyAtom";
|
|
25
|
+
readonly type: "atom";
|
|
26
|
+
readonly factory: ({ storage }: Params) => import("@exodus/atoms").Atom<Record<string, Buffer> | {}>;
|
|
27
|
+
readonly dependencies: readonly ["storage"];
|
|
28
|
+
readonly public: true;
|
|
29
|
+
};
|
|
30
|
+
export {};
|
package/lib/atoms/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { createInMemoryAtom } from '@exodus/atoms';
|
|
1
|
+
import { createInMemoryAtom, createStorageAtomFactory } from '@exodus/atoms';
|
|
2
2
|
export const bluetoothStatusAtomDefinition = {
|
|
3
3
|
id: 'bluetoothStatusAtom',
|
|
4
4
|
type: 'atom',
|
|
@@ -15,3 +15,17 @@ export const bluetoothScanningAtomDefinition = {
|
|
|
15
15
|
dependencies: [],
|
|
16
16
|
public: true,
|
|
17
17
|
};
|
|
18
|
+
export const walletPolicyAtomDefinition = {
|
|
19
|
+
id: 'walletPolicyAtom',
|
|
20
|
+
type: 'atom',
|
|
21
|
+
factory: ({ storage }) => {
|
|
22
|
+
const createStorageAtom = createStorageAtomFactory({ storage });
|
|
23
|
+
return createStorageAtom({
|
|
24
|
+
key: 'walletPolicy',
|
|
25
|
+
defaultValue: {},
|
|
26
|
+
isSoleWriter: true,
|
|
27
|
+
});
|
|
28
|
+
},
|
|
29
|
+
dependencies: ['storage'],
|
|
30
|
+
public: true,
|
|
31
|
+
};
|
package/lib/index.d.ts
CHANGED
|
@@ -17,6 +17,16 @@ declare const hardwareWalletLedger: ({ bluetoothScannerTimeout, }?: Partial<HwLe
|
|
|
17
17
|
readonly dependencies: readonly [];
|
|
18
18
|
readonly public: true;
|
|
19
19
|
};
|
|
20
|
+
}, {
|
|
21
|
+
readonly definition: {
|
|
22
|
+
readonly id: "walletPolicyAtom";
|
|
23
|
+
readonly type: "atom";
|
|
24
|
+
readonly factory: ({ storage }: {
|
|
25
|
+
storage: import("adapters/storage-interface").Storage<Record<string, Buffer>>;
|
|
26
|
+
}) => import("libraries/atoms/lib").Atom<Record<string, Buffer> | {}>;
|
|
27
|
+
readonly dependencies: readonly ["storage"];
|
|
28
|
+
readonly public: true;
|
|
29
|
+
};
|
|
20
30
|
}, {
|
|
21
31
|
readonly definition: {
|
|
22
32
|
readonly id: "hardwareWalletLedgerPlugin";
|
|
@@ -39,7 +49,7 @@ declare const hardwareWalletLedger: ({ bluetoothScannerTimeout, }?: Partial<HwLe
|
|
|
39
49
|
readonly id: "ledgerDiscovery";
|
|
40
50
|
readonly type: "module";
|
|
41
51
|
readonly factory: (deps: import("./module").Dependencies) => import("./module").LedgerDiscovery;
|
|
42
|
-
readonly dependencies: readonly ["config", "logger", "transportsFactories", "bluetoothStatusAtom", "bluetoothScanningAtom"];
|
|
52
|
+
readonly dependencies: readonly ["config", "logger", "transportsFactories", "bluetoothStatusAtom", "bluetoothScanningAtom", "walletPolicyAtom"];
|
|
43
53
|
readonly public: true;
|
|
44
54
|
};
|
|
45
55
|
readonly config: {
|
package/lib/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { bluetoothStatusAtomDefinition, bluetoothScanningAtomDefinition } from './atoms';
|
|
1
|
+
import { bluetoothStatusAtomDefinition, bluetoothScanningAtomDefinition, walletPolicyAtomDefinition, } from './atoms';
|
|
2
2
|
import ledgerDiscoveryDefinition from './module';
|
|
3
3
|
import hardwareWalletLedgerPluginDefinition from './plugin';
|
|
4
4
|
const hardwareWalletLedger = ({ bluetoothScannerTimeout = 10_000, } = {}) => {
|
|
@@ -11,6 +11,9 @@ const hardwareWalletLedger = ({ bluetoothScannerTimeout = 10_000, } = {}) => {
|
|
|
11
11
|
{
|
|
12
12
|
definition: bluetoothScanningAtomDefinition,
|
|
13
13
|
},
|
|
14
|
+
{
|
|
15
|
+
definition: walletPolicyAtomDefinition,
|
|
16
|
+
},
|
|
14
17
|
{
|
|
15
18
|
definition: hardwareWalletLedgerPluginDefinition,
|
|
16
19
|
},
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type Transport from '@ledgerhq/hw-transport';
|
|
2
|
+
import type { Atom } from '@exodus/atoms';
|
|
2
3
|
import type { Bytes, HardwareWalletAssetHandler } from '@exodus/hw-common';
|
|
3
4
|
export interface BitcoinMessage {
|
|
4
5
|
rawMessage?: Bytes;
|
|
@@ -7,7 +8,7 @@ export interface BitcoinMessage {
|
|
|
7
8
|
message: Bytes;
|
|
8
9
|
};
|
|
9
10
|
}
|
|
10
|
-
declare function createHandler(transport: Transport): Promise<HardwareWalletAssetHandler<BitcoinMessage>>;
|
|
11
|
+
declare function createHandler(transport: Transport, walletPolicyAtom?: Atom<Record<string, Buffer>>): Promise<HardwareWalletAssetHandler<BitcoinMessage>>;
|
|
11
12
|
declare const metadata: {
|
|
12
13
|
applications: {
|
|
13
14
|
applicationName: string;
|
|
@@ -3,17 +3,25 @@ import { AppClient, PsbtV2 } from '@exodus/ledger-bitcoin';
|
|
|
3
3
|
import { BIP322 } from '@exodus/bip322-js';
|
|
4
4
|
import { splitBip44DerivationPath } from './utils';
|
|
5
5
|
import assert from 'minimalistic-assert';
|
|
6
|
-
import { assertPsbtOnlyHasAllowedDerivationPaths, tagPsbtWithDerivationPaths, getWalletPolicy, derivationPathsToWalletPolicy, addDummyInputs, } from './utils/psbt';
|
|
6
|
+
import { assertPsbtOnlyHasAllowedDerivationPaths, tagPsbtWithDerivationPaths, getWalletPolicy, getMultisigWalletPolicy, derivationPathsToWalletPolicy, addDummyInputs, } from './utils/psbt';
|
|
7
7
|
import { buildBip322PSBT } from './utils/bip322';
|
|
8
8
|
import applications from './applications';
|
|
9
|
-
async function createHandler(transport) {
|
|
9
|
+
async function createHandler(transport, walletPolicyAtom) {
|
|
10
10
|
const app = new AppClient(transport);
|
|
11
11
|
const getAddress = async (params) => {
|
|
12
12
|
const [purpose, coin, account, change, address] = splitBip44DerivationPath(params.derivationPath);
|
|
13
13
|
const fpr = await app.getMasterFingerprint();
|
|
14
14
|
const xpub = await app.getExtendedPubkey(`m/${purpose}/${coin}/${account}`);
|
|
15
|
-
|
|
16
|
-
|
|
15
|
+
let walletPolicy;
|
|
16
|
+
let hmac = null;
|
|
17
|
+
if (params.multisigData) {
|
|
18
|
+
walletPolicy = getMultisigWalletPolicy(fpr, xpub, purpose, coin, account, params.multisigData);
|
|
19
|
+
hmac = await registerWalletPolicy(walletPolicy);
|
|
20
|
+
}
|
|
21
|
+
else {
|
|
22
|
+
walletPolicy = getWalletPolicy(fpr, xpub, purpose, coin, account);
|
|
23
|
+
}
|
|
24
|
+
return app.getWalletAddress(walletPolicy, hmac, Number(change), Number(address), params.displayOnDevice ?? false);
|
|
17
25
|
};
|
|
18
26
|
const getXPub = async (params) => {
|
|
19
27
|
return app.getExtendedPubkey(params.derivationPath);
|
|
@@ -28,19 +36,30 @@ async function createHandler(transport) {
|
|
|
28
36
|
psbt.deserialize(psbtBuffer);
|
|
29
37
|
const fpr = await app.getMasterFingerprint();
|
|
30
38
|
const derivationPathToPublicKeyMap = Object.create(null);
|
|
39
|
+
const addressToDerivationPathMap = Object.create(null);
|
|
31
40
|
for (const derivationPath of params.derivationPaths) {
|
|
32
|
-
|
|
41
|
+
if (params.multisigData) {
|
|
42
|
+
const address = await getAddress({
|
|
43
|
+
assetName: 'bitcoin',
|
|
44
|
+
derivationPath,
|
|
45
|
+
multisigData: params.multisigData,
|
|
46
|
+
});
|
|
47
|
+
addressToDerivationPathMap[address] = derivationPath;
|
|
48
|
+
}
|
|
49
|
+
const publicKey = await getPublicKey({
|
|
33
50
|
assetName: 'bitcoin',
|
|
34
51
|
derivationPath,
|
|
35
52
|
});
|
|
53
|
+
derivationPathToPublicKeyMap[derivationPath] = publicKey;
|
|
36
54
|
}
|
|
37
|
-
tagPsbtWithDerivationPaths(psbt, fpr, derivationPathToPublicKeyMap);
|
|
55
|
+
tagPsbtWithDerivationPaths(psbt, fpr, derivationPathToPublicKeyMap, addressToDerivationPathMap);
|
|
38
56
|
addDummyInputs(psbt);
|
|
39
57
|
assertPsbtOnlyHasAllowedDerivationPaths(psbt, fpr, params.derivationPaths);
|
|
40
58
|
const signatures = [];
|
|
41
|
-
const walletPolicies = await derivationPathsToWalletPolicy(fpr, app, params.derivationPaths);
|
|
59
|
+
const walletPolicies = await derivationPathsToWalletPolicy(fpr, app, params.derivationPaths, params.multisigData);
|
|
42
60
|
for (const walletPolicy of walletPolicies) {
|
|
43
|
-
const
|
|
61
|
+
const hmac = params.multisigData ? await registerWalletPolicy(walletPolicy) : null;
|
|
62
|
+
const _signatures = await app.signPsbt(psbt.serialize(), walletPolicy, hmac);
|
|
44
63
|
signatures.push(..._signatures);
|
|
45
64
|
}
|
|
46
65
|
const result = signatures.map(([inputIndex, partialSignature]) => ({
|
|
@@ -95,6 +114,18 @@ async function createHandler(transport) {
|
|
|
95
114
|
}
|
|
96
115
|
throw new Error('rawMessage or bip322Message must be defined');
|
|
97
116
|
};
|
|
117
|
+
const registerWalletPolicy = async (walletPolicy) => {
|
|
118
|
+
const walletPolicies = (await walletPolicyAtom?.get()) ?? {};
|
|
119
|
+
const id = walletPolicy.getId().toString('hex');
|
|
120
|
+
let hmac = walletPolicies[id];
|
|
121
|
+
if (!hmac) {
|
|
122
|
+
const [, h] = await app.registerWallet(walletPolicy);
|
|
123
|
+
hmac = h;
|
|
124
|
+
const updatedPolicies = { ...walletPolicies, [id]: hmac };
|
|
125
|
+
await walletPolicyAtom?.set(updatedPolicies);
|
|
126
|
+
}
|
|
127
|
+
return hmac;
|
|
128
|
+
};
|
|
98
129
|
return {
|
|
99
130
|
getAddress,
|
|
100
131
|
getPublicKey,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import secp256k1 from '@exodus/secp256k1';
|
|
2
2
|
import * as ethers from '@exodus/ethersproject-transactions';
|
|
3
3
|
import AppEth, { ledgerService } from '@exodus/ledgerhq-hw-app-eth';
|
|
4
|
-
import { TypedDataUtils, SignTypedDataVersion
|
|
4
|
+
import { TypedDataUtils, SignTypedDataVersion } from '@metamask/eth-sig-util';
|
|
5
5
|
import assert from 'minimalistic-assert';
|
|
6
6
|
import { buildFauxXPUB } from './utils';
|
|
7
7
|
import { RequiresBlindSigningError } from '../errors';
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type Transport from '@ledgerhq/hw-transport';
|
|
2
2
|
import type { HardwareWalletAssetHandler } from '@exodus/hw-common';
|
|
3
|
-
type
|
|
3
|
+
import type { Atom } from '@exodus/atoms';
|
|
4
|
+
type HandlerFactory = (transport: Transport, walletPolicyAtom?: Atom<Record<string, Buffer>>) => Promise<HardwareWalletAssetHandler>;
|
|
4
5
|
type AssetMetadata = {
|
|
5
6
|
applications: {
|
|
6
7
|
applicationName: string;
|
|
@@ -1,6 +1,10 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { PsbtV2 } from '@exodus/ledger-bitcoin';
|
|
2
|
+
import { DefaultWalletPolicy, WalletPolicy } from '@exodus/ledger-bitcoin';
|
|
3
|
+
import type { MultisigData } from 'libraries/hw-common/lib';
|
|
2
4
|
export declare function assertPsbtOnlyHasAllowedDerivationPaths(psbt: PsbtV2, masterFingerprint: string, derivationPaths: string[]): void;
|
|
3
|
-
export declare function tagPsbtWithDerivationPaths(psbt: PsbtV2, masterFingerprint: string, derivationPathToPublicKeyMap: Record<string, Buffer>): void;
|
|
5
|
+
export declare function tagPsbtWithDerivationPaths(psbt: PsbtV2, masterFingerprint: string, derivationPathToPublicKeyMap: Record<string, Buffer>, addressToDerivationPathMap?: Record<string, string>): void;
|
|
4
6
|
export declare const getWalletPolicy: (fpr: string, xpub: string, purpose: string, coin: string, account: string) => DefaultWalletPolicy;
|
|
5
|
-
export declare
|
|
7
|
+
export declare const DUMMY_TAPROOT_XPUB = "xpub661MyMwAqRbcEYS8w7XLSVeEsBXy79zSzH1J8vCdxAZningWLdN3zgtU6QgnecKFpJFPpdzxKrwoaZoV44qAJewsc4kX9vGaCaBExuvJH57";
|
|
8
|
+
export declare const getMultisigWalletPolicy: (fpr: string, xpub: string, purpose: string, coin: string, account: string, multisigData: MultisigData) => WalletPolicy;
|
|
9
|
+
export declare function derivationPathsToWalletPolicy(fpr: string, app: any, derivationPaths: string[], multisigData?: MultisigData): Promise<WalletPolicy[]>;
|
|
6
10
|
export declare function addDummyInputs(psbt: PsbtV2): void;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import assert from 'minimalistic-assert';
|
|
2
|
-
import { DefaultWalletPolicy, } from '@exodus/ledger-bitcoin';
|
|
3
|
-
import { payments } from '@exodus/bitcoinjs-lib';
|
|
2
|
+
import { DefaultWalletPolicy, WalletPolicy } from '@exodus/ledger-bitcoin';
|
|
3
|
+
import { payments, address as baddress } from '@exodus/bitcoinjs-lib';
|
|
4
4
|
import { buildBip44PathFromNumber, assertBip44PathNumber, splitBip44PathToNumber, splitBip44DerivationPath, } from './derivation-paths';
|
|
5
5
|
export function assertPsbtOnlyHasAllowedDerivationPaths(psbt, masterFingerprint, derivationPaths) {
|
|
6
6
|
for (let i = 0; i < psbt.getGlobalInputCount(); i++) {
|
|
@@ -31,14 +31,21 @@ function assertAllowedDerivation(bip32Derivation, masterFingerprint, derivationP
|
|
|
31
31
|
assert(derivationPaths.includes(derivationPath), `found unsupported derivation path: ${derivationPath}`);
|
|
32
32
|
}
|
|
33
33
|
}
|
|
34
|
-
export function tagPsbtWithDerivationPaths(psbt, masterFingerprint, derivationPathToPublicKeyMap) {
|
|
34
|
+
export function tagPsbtWithDerivationPaths(psbt, masterFingerprint, derivationPathToPublicKeyMap, addressToDerivationPathMap) {
|
|
35
35
|
const masterFingerprintBuffer = Buffer.from(masterFingerprint, 'hex');
|
|
36
36
|
const scriptPubKeyToDerivationPathMap = computeScriptPubKeyToDerivationPathMap(derivationPathToPublicKeyMap);
|
|
37
37
|
for (let inputIndex = 0; inputIndex < psbt.getGlobalInputCount(); inputIndex++) {
|
|
38
38
|
const witnessUtxo = psbt.getInputWitnessUtxo(inputIndex);
|
|
39
39
|
assert(witnessUtxo, `witnessUtxo is currently the only supported type`);
|
|
40
40
|
const scriptPubKey = witnessUtxo.scriptPubKey.toString('hex');
|
|
41
|
-
|
|
41
|
+
let derivationPath = scriptPubKeyToDerivationPathMap[scriptPubKey];
|
|
42
|
+
if (addressToDerivationPathMap && !derivationPath) {
|
|
43
|
+
try {
|
|
44
|
+
const address = baddress.fromOutputScript(witnessUtxo.scriptPubKey);
|
|
45
|
+
derivationPath = addressToDerivationPathMap[address];
|
|
46
|
+
}
|
|
47
|
+
catch { }
|
|
48
|
+
}
|
|
42
49
|
if (derivationPath) {
|
|
43
50
|
const publicKey = derivationPathToPublicKeyMap[derivationPath];
|
|
44
51
|
assert(Buffer.isBuffer(publicKey), `no public key found for derivation path`);
|
|
@@ -53,7 +60,14 @@ export function tagPsbtWithDerivationPaths(psbt, masterFingerprint, derivationPa
|
|
|
53
60
|
}
|
|
54
61
|
for (let outputIndex = 0; outputIndex < psbt.getGlobalOutputCount(); outputIndex++) {
|
|
55
62
|
const scriptPubKey = psbt.getOutputScript(outputIndex);
|
|
56
|
-
|
|
63
|
+
let derivationPath = scriptPubKeyToDerivationPathMap[scriptPubKey.toString('hex')];
|
|
64
|
+
if (addressToDerivationPathMap && !derivationPath) {
|
|
65
|
+
try {
|
|
66
|
+
const address = baddress.fromOutputScript(scriptPubKey);
|
|
67
|
+
derivationPath = addressToDerivationPathMap[address];
|
|
68
|
+
}
|
|
69
|
+
catch { }
|
|
70
|
+
}
|
|
57
71
|
if (derivationPath) {
|
|
58
72
|
const publicKey = derivationPathToPublicKeyMap[derivationPath];
|
|
59
73
|
assert(Buffer.isBuffer(publicKey), `no public key found for derivation path`);
|
|
@@ -104,7 +118,23 @@ export const getWalletPolicy = (fpr, xpub, purpose, coin, account) => {
|
|
|
104
118
|
}[purpose];
|
|
105
119
|
return new DefaultWalletPolicy(desc, `[${fpr}/${purpose}/${coin}/${account}]${xpub}`);
|
|
106
120
|
};
|
|
107
|
-
export
|
|
121
|
+
export const DUMMY_TAPROOT_XPUB = 'xpub661MyMwAqRbcEYS8w7XLSVeEsBXy79zSzH1J8vCdxAZningWLdN3zgtU6QgnecKFpJFPpdzxKrwoaZoV44qAJewsc4kX9vGaCaBExuvJH57';
|
|
122
|
+
export const getMultisigWalletPolicy = (fpr, xpub, purpose, coin, account, multisigData) => {
|
|
123
|
+
let template = `tr(@0/**,sortedmulti_a(${multisigData.threshold}`;
|
|
124
|
+
const keys = [DUMMY_TAPROOT_XPUB];
|
|
125
|
+
multisigData.xpubs.forEach((xpub2, i) => {
|
|
126
|
+
if (xpub === xpub2) {
|
|
127
|
+
keys.push(`[${fpr}/${purpose}/${coin}/${account}]${xpub}`);
|
|
128
|
+
}
|
|
129
|
+
else {
|
|
130
|
+
keys.push(xpub2);
|
|
131
|
+
}
|
|
132
|
+
template += `,@${i + 1}/**`;
|
|
133
|
+
});
|
|
134
|
+
template += '))';
|
|
135
|
+
return new WalletPolicy('Multisig', template, keys);
|
|
136
|
+
};
|
|
137
|
+
export async function derivationPathsToWalletPolicy(fpr, app, derivationPaths, multisigData) {
|
|
108
138
|
const splittedDerivationPaths = derivationPaths.map(splitBip44DerivationPath);
|
|
109
139
|
const dedupedPaths = Object.values(splittedDerivationPaths.reduce((prev, splittedDerivationPath) => {
|
|
110
140
|
const [purpose, coin, account] = splittedDerivationPath;
|
|
@@ -115,7 +145,12 @@ export async function derivationPathsToWalletPolicy(fpr, app, derivationPaths) {
|
|
|
115
145
|
for (const dedupedPath of dedupedPaths) {
|
|
116
146
|
const [purpose, coin, account] = dedupedPath;
|
|
117
147
|
const xpub = await app.getExtendedPubkey(`m/${purpose}/${coin}/${account}`);
|
|
118
|
-
|
|
148
|
+
if (multisigData) {
|
|
149
|
+
walletPolicies.push(getMultisigWalletPolicy(fpr, xpub, purpose, coin, account, multisigData));
|
|
150
|
+
}
|
|
151
|
+
else {
|
|
152
|
+
walletPolicies.push(getWalletPolicy(fpr, xpub, purpose, coin, account));
|
|
153
|
+
}
|
|
119
154
|
}
|
|
120
155
|
return walletPolicies;
|
|
121
156
|
}
|
package/lib/module/device.d.ts
CHANGED
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
import type { GetAddressParams, GetPublicKeyParams, GetXPubParams, HardwareWalletDevice, SignMessageParams, SignTransactionParams } from '@exodus/hw-common';
|
|
2
2
|
import type { TransportFactory } from './types';
|
|
3
|
+
import type { Atom } from '@exodus/atoms';
|
|
3
4
|
export type Descriptor = unknown;
|
|
4
5
|
export type Dependencies = {
|
|
5
6
|
transportFactory: TransportFactory;
|
|
6
7
|
descriptor: Descriptor;
|
|
8
|
+
walletPolicyAtom: Atom<Record<string, Buffer>>;
|
|
7
9
|
};
|
|
8
10
|
export declare class LedgerDevice implements HardwareWalletDevice {
|
|
9
11
|
#private;
|
|
10
|
-
constructor({ transportFactory, descriptor }: Dependencies);
|
|
12
|
+
constructor({ transportFactory, descriptor, walletPolicyAtom }: Dependencies);
|
|
11
13
|
ensureApplicationIsOpened: (baseAssetName: string) => Promise<void>;
|
|
12
14
|
listSupportedAssetNames: () => Promise<string[]>;
|
|
13
15
|
listInstalledAssetNames: () => Promise<string[]>;
|
package/lib/module/device.js
CHANGED
|
@@ -6,9 +6,11 @@ import { UnsupportedVersionError, ApplicationNotFoundError } from './errors';
|
|
|
6
6
|
export class LedgerDevice {
|
|
7
7
|
#transportFactory;
|
|
8
8
|
#descriptor;
|
|
9
|
-
|
|
9
|
+
#walletPolicyAtom;
|
|
10
|
+
constructor({ transportFactory, descriptor, walletPolicyAtom }) {
|
|
10
11
|
this.#transportFactory = transportFactory;
|
|
11
12
|
this.#descriptor = descriptor;
|
|
13
|
+
this.#walletPolicyAtom = walletPolicyAtom;
|
|
12
14
|
}
|
|
13
15
|
#runInSession = async (operation, expectErrorWhenClosing = false) => {
|
|
14
16
|
const transport = await this.#transportFactory.open(this.#descriptor);
|
|
@@ -119,7 +121,7 @@ export class LedgerDevice {
|
|
|
119
121
|
getAddress = async (params) => {
|
|
120
122
|
await this.ensureApplicationIsOpened(params.assetName);
|
|
121
123
|
return this.#runInSession(async (transport) => {
|
|
122
|
-
const handler = await this.#getAssetApplication(params.assetName).handler(transport);
|
|
124
|
+
const handler = await this.#getAssetApplication(params.assetName).handler(transport, this.#walletPolicyAtom);
|
|
123
125
|
return handler.getAddress(params);
|
|
124
126
|
});
|
|
125
127
|
};
|
|
@@ -140,7 +142,7 @@ export class LedgerDevice {
|
|
|
140
142
|
signTransaction = async (params) => {
|
|
141
143
|
await this.ensureApplicationIsOpened(params.assetName);
|
|
142
144
|
return this.#runInSession(async (transport) => {
|
|
143
|
-
const handler = await this.#getAssetApplication(params.assetName).handler(transport);
|
|
145
|
+
const handler = await this.#getAssetApplication(params.assetName).handler(transport, this.#walletPolicyAtom);
|
|
144
146
|
return handler.signTransaction(params);
|
|
145
147
|
});
|
|
146
148
|
};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Logger } from '../shared/types';
|
|
1
|
+
import type { Logger } from '../shared/types';
|
|
2
2
|
import type { Atom } from '@exodus/atoms';
|
|
3
3
|
import type { HardwareWalletDescriptor, HardwareWalletDiscovery } from '@exodus/hw-common';
|
|
4
4
|
import type { HwLedgerConfig, TransportFactories } from './types';
|
|
@@ -9,10 +9,11 @@ export type Dependencies = {
|
|
|
9
9
|
transportsFactories: TransportFactories;
|
|
10
10
|
bluetoothStatusAtom: Atom<BluetoothStatus>;
|
|
11
11
|
bluetoothScanningAtom: Atom<boolean>;
|
|
12
|
+
walletPolicyAtom: Atom<Record<string, Buffer>>;
|
|
12
13
|
};
|
|
13
14
|
export declare class LedgerDiscovery implements HardwareWalletDiscovery {
|
|
14
15
|
#private;
|
|
15
|
-
constructor({ config, logger, transportsFactories, bluetoothStatusAtom, bluetoothScanningAtom, }: Dependencies);
|
|
16
|
+
constructor({ config, logger, transportsFactories, bluetoothStatusAtom, bluetoothScanningAtom, walletPolicyAtom, }: Dependencies);
|
|
16
17
|
start: () => void;
|
|
17
18
|
scan: () => Promise<void>;
|
|
18
19
|
stopScan: (clearDevices?: boolean) => void;
|
|
@@ -24,7 +25,7 @@ declare const _default: {
|
|
|
24
25
|
readonly id: "ledgerDiscovery";
|
|
25
26
|
readonly type: "module";
|
|
26
27
|
readonly factory: (deps: Dependencies) => LedgerDiscovery;
|
|
27
|
-
readonly dependencies: readonly ["config", "logger", "transportsFactories", "bluetoothStatusAtom", "bluetoothScanningAtom"];
|
|
28
|
+
readonly dependencies: readonly ["config", "logger", "transportsFactories", "bluetoothStatusAtom", "bluetoothScanningAtom", "walletPolicyAtom"];
|
|
28
29
|
readonly public: true;
|
|
29
30
|
};
|
|
30
31
|
export default _default;
|
package/lib/module/discovery.js
CHANGED
|
@@ -11,11 +11,13 @@ export class LedgerDiscovery {
|
|
|
11
11
|
#bluetoothStatusAtom;
|
|
12
12
|
#scannerBluetooth;
|
|
13
13
|
#bluetoothScanningAtom;
|
|
14
|
+
#walletPolicyAtom;
|
|
14
15
|
#discoveredBluetoothDevices = Object.create(null);
|
|
15
|
-
constructor({ config, logger, transportsFactories, bluetoothStatusAtom, bluetoothScanningAtom, }) {
|
|
16
|
+
constructor({ config, logger, transportsFactories, bluetoothStatusAtom, bluetoothScanningAtom, walletPolicyAtom, }) {
|
|
16
17
|
this.#logger = logger;
|
|
17
18
|
this.#bluetoothStatusAtom = bluetoothStatusAtom;
|
|
18
19
|
this.#bluetoothScanningAtom = bluetoothScanningAtom;
|
|
20
|
+
this.#walletPolicyAtom = walletPolicyAtom;
|
|
19
21
|
if (!(typeof transportsFactories === 'object' &&
|
|
20
22
|
Object.keys(transportsFactories).length > 0 &&
|
|
21
23
|
Object.entries(transportsFactories).every(([transportName, transportFactory]) => ['usb', 'bluetooth', 'tcp'].includes(transportName) &&
|
|
@@ -101,7 +103,11 @@ export class LedgerDiscovery {
|
|
|
101
103
|
manufacturer: 'ledger',
|
|
102
104
|
transport: 'usb',
|
|
103
105
|
model: deviceModel?.id ?? 'unknown',
|
|
104
|
-
get: async () => new LedgerDevice({
|
|
106
|
+
get: async () => new LedgerDevice({
|
|
107
|
+
transportFactory,
|
|
108
|
+
descriptor: hidDevice,
|
|
109
|
+
walletPolicyAtom: this.#walletPolicyAtom,
|
|
110
|
+
}),
|
|
105
111
|
};
|
|
106
112
|
});
|
|
107
113
|
descriptors.push(...usbDeviceList);
|
|
@@ -115,7 +121,11 @@ export class LedgerDiscovery {
|
|
|
115
121
|
manufacturer: 'ledger',
|
|
116
122
|
transport: 'bluetooth',
|
|
117
123
|
model: deviceModel?.id ?? 'unknown',
|
|
118
|
-
get: async () => new LedgerDevice({
|
|
124
|
+
get: async () => new LedgerDevice({
|
|
125
|
+
transportFactory,
|
|
126
|
+
descriptor: bluetoothDevice,
|
|
127
|
+
walletPolicyAtom: this.#walletPolicyAtom,
|
|
128
|
+
}),
|
|
119
129
|
};
|
|
120
130
|
});
|
|
121
131
|
descriptors.push(...bluetoothDeviceList);
|
|
@@ -129,6 +139,7 @@ export class LedgerDiscovery {
|
|
|
129
139
|
get: async () => new LedgerDevice({
|
|
130
140
|
transportFactory,
|
|
131
141
|
descriptor: { apduPort: APDU_PORT },
|
|
142
|
+
walletPolicyAtom: this.#walletPolicyAtom,
|
|
132
143
|
}),
|
|
133
144
|
});
|
|
134
145
|
break;
|
|
@@ -160,6 +171,7 @@ export default {
|
|
|
160
171
|
'transportsFactories',
|
|
161
172
|
'bluetoothStatusAtom',
|
|
162
173
|
'bluetoothScanningAtom',
|
|
174
|
+
'walletPolicyAtom',
|
|
163
175
|
],
|
|
164
176
|
public: true,
|
|
165
177
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@exodus/hw-ledger",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.10.0",
|
|
4
4
|
"description": "The ledger module",
|
|
5
5
|
"author": "Exodus Movement, Inc.",
|
|
6
6
|
"repository": {
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
"test": "run -T jest --testMatch='**/*.unit.test.js' --coverageDirectory coverage/unit",
|
|
26
26
|
"zemu:install": "docker pull zondax/builder-zemu:speculos-745fe46f81cfc18114b94a37beaefb58a09c0fcd",
|
|
27
27
|
"test:integration": "yarn zemu:install && run -T jest --runInBand",
|
|
28
|
-
"lint": "run -T eslint .
|
|
28
|
+
"lint": "run -T eslint .",
|
|
29
29
|
"lint:fix": "yarn lint --fix",
|
|
30
30
|
"prepublishOnly": "yarn run -T build --scope @exodus/hw-ledger"
|
|
31
31
|
},
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
"@exodus/bip322-js": "^1.1.0-exodus.4",
|
|
35
35
|
"@exodus/bitcoinjs-lib": "^6.1.5-exodus.0",
|
|
36
36
|
"@exodus/ethersproject-transactions": "^5.4.2-exodus.0",
|
|
37
|
-
"@exodus/hw-common": "^2.
|
|
37
|
+
"@exodus/hw-common": "^2.4.0",
|
|
38
38
|
"@exodus/ledger-bitcoin": "^0.2.3-exodus.3",
|
|
39
39
|
"@exodus/ledgerhq-devices": "^8.1.0-exodus.3",
|
|
40
40
|
"@exodus/ledgerhq-hw-app-eth": "^6.33.7-exodus.8",
|
|
@@ -61,6 +61,7 @@
|
|
|
61
61
|
"@types/jest-when": "^3.5.2",
|
|
62
62
|
"@types/minimalistic-assert": "^1.0.1",
|
|
63
63
|
"@types/secp256k1": "^4.0.3",
|
|
64
|
+
"@types/semver": "^7.3.5",
|
|
64
65
|
"@zondax/zemu": "^0.43.1",
|
|
65
66
|
"bitcoinjs-lib": "^6.1.3",
|
|
66
67
|
"concurrently": "^7.6.0",
|
|
@@ -71,5 +72,5 @@
|
|
|
71
72
|
"tiny-secp256k1": "^2.2.3",
|
|
72
73
|
"ts-jest": "^29.0.3"
|
|
73
74
|
},
|
|
74
|
-
"gitHead": "
|
|
75
|
+
"gitHead": "d035797666e173e9cec145ddce8e137fe84dfb83"
|
|
75
76
|
}
|