@exodus/hw-ledger 3.9.2 → 4.0.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 +37 -0
- package/README.md +1 -1
- package/lib/atoms/index.d.ts +3 -3
- package/lib/atoms/index.js +7 -10
- package/lib/index.d.ts +5 -5
- package/lib/index.js +9 -14
- package/lib/module/assets/applications.js +1 -3
- package/lib/module/assets/basemainnet.d.ts +1 -1
- package/lib/module/assets/basemainnet.js +5 -10
- package/lib/module/assets/bitcoin.d.ts +1 -1
- package/lib/module/assets/bitcoin.js +31 -36
- package/lib/module/assets/ethereum.d.ts +1 -1
- package/lib/module/assets/ethereum.js +31 -64
- package/lib/module/assets/index.d.ts +1 -1
- package/lib/module/assets/index.js +22 -28
- package/lib/module/assets/matic.d.ts +1 -1
- package/lib/module/assets/matic.js +5 -10
- package/lib/module/assets/ripple.d.ts +1 -1
- package/lib/module/assets/ripple.js +13 -17
- package/lib/module/assets/solana.d.ts +1 -1
- package/lib/module/assets/solana.js +16 -19
- package/lib/module/assets/tronmainnet.d.ts +1 -1
- package/lib/module/assets/tronmainnet.js +14 -50
- package/lib/module/assets/utils/bip322.js +7 -43
- package/lib/module/assets/utils/derivation-paths.js +17 -28
- package/lib/module/assets/utils/index.js +3 -19
- package/lib/module/assets/utils/psbt.d.ts +3 -3
- package/lib/module/assets/utils/psbt.js +34 -46
- package/lib/module/assets/utils/xpub.js +3 -6
- package/lib/module/constants.js +1 -4
- package/lib/module/device.d.ts +1 -1
- package/lib/module/device.js +21 -28
- package/lib/module/discovery.js +14 -18
- package/lib/module/errors.js +5 -12
- package/lib/module/index.js +2 -23
- package/lib/module/management/application.d.ts +2 -2
- package/lib/module/management/application.js +1 -4
- package/lib/module/types.d.ts +3 -2
- package/lib/module/types.js +1 -2
- package/lib/plugin/index.js +4 -6
- package/lib/redux/id.js +1 -3
- package/lib/redux/index.js +7 -12
- package/lib/redux/initial-state.js +1 -3
- package/lib/redux/selectors/bluetoothScanning.js +3 -5
- package/lib/redux/selectors/bluetoothStatus.js +3 -5
- package/lib/redux/selectors/index.js +4 -9
- package/package.json +23 -15
- package/redux.js +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,43 @@
|
|
|
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
|
+
## [4.0.1](https://github.com/ExodusMovement/exodus-hydra/compare/@exodus/hw-ledger@4.0.0...@exodus/hw-ledger@4.0.1) (2026-06-29)
|
|
7
|
+
|
|
8
|
+
### ⚠ BREAKING CHANGES
|
|
9
|
+
|
|
10
|
+
- **eslint-config-exodus:** @exodus/eslint-config-exodus no longer enforces
|
|
11
|
+
no-floating-promises or no-unnecessary-condition and no longer sets
|
|
12
|
+
parserOptions.project. Consumers relying on these without oxlint
|
|
13
|
+
--type-aware lose the coverage; adopt oxlint type-aware.
|
|
14
|
+
|
|
15
|
+
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
|
16
|
+
|
|
17
|
+
- chore: drop redundant local no-floating-promises disable
|
|
18
|
+
|
|
19
|
+
@exodus/eslint-config-exodus no longer enables no-floating-promises, so
|
|
20
|
+
|
|
21
|
+
### Features
|
|
22
|
+
|
|
23
|
+
- **eslint-config-exodus:** retire type-aware eslint rules, drop parserOptions.project ([#17273](https://github.com/ExodusMovement/exodus-hydra/issues/17273)) ([622ec93](https://github.com/ExodusMovement/exodus-hydra/commit/622ec935f14f0b7d54ee6f2b034995606383397c)), closes [#7693](https://github.com/ExodusMovement/exodus-hydra/issues/7693) [#17253](https://github.com/ExodusMovement/exodus-hydra/issues/17253)
|
|
24
|
+
|
|
25
|
+
### Bug Fixes
|
|
26
|
+
|
|
27
|
+
- **hw-ledger:** bump ledgerhq deps + re-enable skipped test suites ([#17394](https://github.com/ExodusMovement/exodus-hydra/issues/17394)) ([2d9f631](https://github.com/ExodusMovement/exodus-hydra/commit/2d9f631af8451a2cac4a8aca9e843f86a5cda87d))
|
|
28
|
+
|
|
29
|
+
## [4.0.0](https://github.com/ExodusMovement/exodus-hydra/compare/@exodus/hw-ledger@3.9.2...@exodus/hw-ledger@4.0.0) (2026-06-09)
|
|
30
|
+
|
|
31
|
+
### ⚠ BREAKING CHANGES
|
|
32
|
+
|
|
33
|
+
- **hw-ledger:** migrate to native ESM (#16981)
|
|
34
|
+
|
|
35
|
+
### Bug Fixes
|
|
36
|
+
|
|
37
|
+
- **hw-ledger:** disambiguate ERC-20 vs NFT clear-signing with an isNft hint ([#17014](https://github.com/ExodusMovement/exodus-hydra/issues/17014)) ([723a0e4](https://github.com/ExodusMovement/exodus-hydra/commit/723a0e4a845ef9463ba6994f52005ee4f5d1faca))
|
|
38
|
+
|
|
39
|
+
### Code Refactoring
|
|
40
|
+
|
|
41
|
+
- **hw-ledger:** migrate to native ESM ([#16981](https://github.com/ExodusMovement/exodus-hydra/issues/16981)) ([2a37eef](https://github.com/ExodusMovement/exodus-hydra/commit/2a37eef2d30e86b59d9dfd70b3ec2f8727986d5c))
|
|
42
|
+
|
|
6
43
|
## [3.9.2](https://github.com/ExodusMovement/exodus-hydra/compare/@exodus/hw-ledger@3.9.1...@exodus/hw-ledger@3.9.2) (2026-05-28)
|
|
7
44
|
|
|
8
45
|
### Features
|
package/README.md
CHANGED
package/lib/atoms/index.d.ts
CHANGED
|
@@ -6,14 +6,14 @@ export type BluetoothStatus = {
|
|
|
6
6
|
export declare const bluetoothStatusAtomDefinition: {
|
|
7
7
|
readonly id: "bluetoothStatusAtom";
|
|
8
8
|
readonly type: "atom";
|
|
9
|
-
readonly factory: () => import("@exodus/atoms"
|
|
9
|
+
readonly factory: () => import("@exodus/atoms").Atom<BluetoothStatus>;
|
|
10
10
|
readonly dependencies: readonly [];
|
|
11
11
|
readonly public: true;
|
|
12
12
|
};
|
|
13
13
|
export declare const bluetoothScanningAtomDefinition: {
|
|
14
14
|
readonly id: "bluetoothScanningAtom";
|
|
15
15
|
readonly type: "atom";
|
|
16
|
-
readonly factory: () => import("@exodus/atoms"
|
|
16
|
+
readonly factory: () => import("@exodus/atoms").Atom<boolean>;
|
|
17
17
|
readonly dependencies: readonly [];
|
|
18
18
|
readonly public: true;
|
|
19
19
|
};
|
|
@@ -23,7 +23,7 @@ type Params = {
|
|
|
23
23
|
export declare const walletPolicyAtomDefinition: {
|
|
24
24
|
readonly id: "walletPolicyAtom";
|
|
25
25
|
readonly type: "atom";
|
|
26
|
-
readonly factory: ({ storage }: Params) => import("@exodus/atoms"
|
|
26
|
+
readonly factory: ({ storage }: Params) => import("@exodus/atoms").Atom<Record<string, Buffer<ArrayBufferLike>> | {}>;
|
|
27
27
|
readonly dependencies: readonly ["storage"];
|
|
28
28
|
readonly public: true;
|
|
29
29
|
};
|
package/lib/atoms/index.js
CHANGED
|
@@ -1,28 +1,25 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
exports.walletPolicyAtomDefinition = exports.bluetoothScanningAtomDefinition = exports.bluetoothStatusAtomDefinition = void 0;
|
|
4
|
-
const atoms_1 = require("@exodus/atoms");
|
|
5
|
-
exports.bluetoothStatusAtomDefinition = {
|
|
1
|
+
import { createInMemoryAtom, createStorageAtomFactory } from '@exodus/atoms';
|
|
2
|
+
export const bluetoothStatusAtomDefinition = {
|
|
6
3
|
id: 'bluetoothStatusAtom',
|
|
7
4
|
type: 'atom',
|
|
8
|
-
factory: () =>
|
|
5
|
+
factory: () => createInMemoryAtom({
|
|
9
6
|
defaultValue: { type: 'Unknown', available: false },
|
|
10
7
|
}),
|
|
11
8
|
dependencies: [],
|
|
12
9
|
public: true,
|
|
13
10
|
};
|
|
14
|
-
|
|
11
|
+
export const bluetoothScanningAtomDefinition = {
|
|
15
12
|
id: 'bluetoothScanningAtom',
|
|
16
13
|
type: 'atom',
|
|
17
|
-
factory: () =>
|
|
14
|
+
factory: () => createInMemoryAtom({ defaultValue: false }),
|
|
18
15
|
dependencies: [],
|
|
19
16
|
public: true,
|
|
20
17
|
};
|
|
21
|
-
|
|
18
|
+
export const walletPolicyAtomDefinition = {
|
|
22
19
|
id: 'walletPolicyAtom',
|
|
23
20
|
type: 'atom',
|
|
24
21
|
factory: ({ storage }) => {
|
|
25
|
-
const createStorageAtom =
|
|
22
|
+
const createStorageAtom = createStorageAtomFactory({ storage });
|
|
26
23
|
return createStorageAtom({
|
|
27
24
|
key: 'walletPolicy',
|
|
28
25
|
defaultValue: {},
|
package/lib/index.d.ts
CHANGED
|
@@ -5,7 +5,7 @@ declare const hardwareWalletLedger: ({ bluetoothScannerTimeout, }?: Partial<HwLe
|
|
|
5
5
|
readonly definition: {
|
|
6
6
|
readonly id: "bluetoothStatusAtom";
|
|
7
7
|
readonly type: "atom";
|
|
8
|
-
readonly factory: () => import("@exodus/atoms"
|
|
8
|
+
readonly factory: () => import("@exodus/atoms").Atom<import("./atoms/index.js").BluetoothStatus>;
|
|
9
9
|
readonly dependencies: readonly [];
|
|
10
10
|
readonly public: true;
|
|
11
11
|
};
|
|
@@ -13,7 +13,7 @@ declare const hardwareWalletLedger: ({ bluetoothScannerTimeout, }?: Partial<HwLe
|
|
|
13
13
|
readonly definition: {
|
|
14
14
|
readonly id: "bluetoothScanningAtom";
|
|
15
15
|
readonly type: "atom";
|
|
16
|
-
readonly factory: () => import("@exodus/atoms"
|
|
16
|
+
readonly factory: () => import("@exodus/atoms").Atom<boolean>;
|
|
17
17
|
readonly dependencies: readonly [];
|
|
18
18
|
readonly public: true;
|
|
19
19
|
};
|
|
@@ -23,7 +23,7 @@ declare const hardwareWalletLedger: ({ bluetoothScannerTimeout, }?: Partial<HwLe
|
|
|
23
23
|
readonly type: "atom";
|
|
24
24
|
readonly factory: ({ storage }: {
|
|
25
25
|
storage: import("@exodus/storage-interface").Storage<Record<string, Buffer>>;
|
|
26
|
-
}) => import("@exodus/atoms"
|
|
26
|
+
}) => import("@exodus/atoms").Atom<Record<string, Buffer<ArrayBufferLike>> | {}>;
|
|
27
27
|
readonly dependencies: readonly ["storage"];
|
|
28
28
|
readonly public: true;
|
|
29
29
|
};
|
|
@@ -32,8 +32,8 @@ declare const hardwareWalletLedger: ({ bluetoothScannerTimeout, }?: Partial<HwLe
|
|
|
32
32
|
readonly id: "hardwareWalletLedgerPlugin";
|
|
33
33
|
readonly type: "plugin";
|
|
34
34
|
readonly factory: ({ bluetoothStatusAtom, bluetoothScanningAtom, ledgerDiscovery, port, }: {
|
|
35
|
-
bluetoothStatusAtom: import("@exodus/atoms"
|
|
36
|
-
bluetoothScanningAtom: import("@exodus/atoms"
|
|
35
|
+
bluetoothStatusAtom: import("@exodus/atoms").Atom<import("./atoms/index.js").BluetoothStatus>;
|
|
36
|
+
bluetoothScanningAtom: import("@exodus/atoms").Atom<boolean>;
|
|
37
37
|
ledgerDiscovery: import("./module/discovery.js").LedgerDiscovery;
|
|
38
38
|
port: import("./shared/types.js").Port;
|
|
39
39
|
}) => {
|
package/lib/index.js
CHANGED
|
@@ -1,32 +1,27 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
};
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
const index_js_1 = require("./atoms/index.js");
|
|
7
|
-
const index_js_2 = __importDefault(require("./module/index.js"));
|
|
8
|
-
const index_js_3 = __importDefault(require("./plugin/index.js"));
|
|
1
|
+
import { bluetoothStatusAtomDefinition, bluetoothScanningAtomDefinition, walletPolicyAtomDefinition, } from './atoms/index.js';
|
|
2
|
+
import ledgerDiscoveryDefinition from './module/index.js';
|
|
3
|
+
import hardwareWalletLedgerPluginDefinition from './plugin/index.js';
|
|
9
4
|
const hardwareWalletLedger = ({ bluetoothScannerTimeout = 10_000, } = {}) => {
|
|
10
5
|
return {
|
|
11
6
|
id: 'hardwareWalletLedger',
|
|
12
7
|
definitions: [
|
|
13
8
|
{
|
|
14
|
-
definition:
|
|
9
|
+
definition: bluetoothStatusAtomDefinition,
|
|
15
10
|
},
|
|
16
11
|
{
|
|
17
|
-
definition:
|
|
12
|
+
definition: bluetoothScanningAtomDefinition,
|
|
18
13
|
},
|
|
19
14
|
{
|
|
20
|
-
definition:
|
|
15
|
+
definition: walletPolicyAtomDefinition,
|
|
21
16
|
},
|
|
22
17
|
{
|
|
23
|
-
definition:
|
|
18
|
+
definition: hardwareWalletLedgerPluginDefinition,
|
|
24
19
|
},
|
|
25
20
|
{
|
|
26
|
-
definition:
|
|
21
|
+
definition: ledgerDiscoveryDefinition,
|
|
27
22
|
config: { bluetoothScannerTimeout },
|
|
28
23
|
},
|
|
29
24
|
],
|
|
30
25
|
};
|
|
31
26
|
};
|
|
32
|
-
|
|
27
|
+
export default hardwareWalletLedger;
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
1
|
const Bitcoin = {
|
|
4
2
|
applicationName: 'Bitcoin',
|
|
5
3
|
supportedVersions: '^2.1.2',
|
|
@@ -38,4 +36,4 @@ const applications = {
|
|
|
38
36
|
Tron,
|
|
39
37
|
XRP,
|
|
40
38
|
};
|
|
41
|
-
|
|
39
|
+
export default applications;
|
|
@@ -4,6 +4,6 @@ declare const metadata: {
|
|
|
4
4
|
supportedVersions: string;
|
|
5
5
|
primaryAssetName: string;
|
|
6
6
|
}[];
|
|
7
|
-
handler: (transport: import("
|
|
7
|
+
handler: (transport: import("../types.js").Transport) => Promise<import("@exodus/hw-common").HardwareWalletAssetHandler>;
|
|
8
8
|
};
|
|
9
9
|
export default metadata;
|
|
@@ -1,12 +1,7 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
const applications_js_1 = __importDefault(require("./applications.js"));
|
|
7
|
-
const ethereum_js_1 = __importDefault(require("./ethereum.js"));
|
|
1
|
+
import applications from './applications.js';
|
|
2
|
+
import ethereum from './ethereum.js';
|
|
8
3
|
const metadata = {
|
|
9
|
-
applications: [
|
|
10
|
-
handler:
|
|
4
|
+
applications: [applications.Ethereum],
|
|
5
|
+
handler: ethereum.handler,
|
|
11
6
|
};
|
|
12
|
-
|
|
7
|
+
export default metadata;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type Transport from '@ledgerhq/hw-transport';
|
|
2
1
|
import type { Atom } from '@exodus/atoms';
|
|
2
|
+
import type { Transport } from '../types.js';
|
|
3
3
|
import type { Bytes, HardwareWalletAssetHandler } from '@exodus/hw-common';
|
|
4
4
|
export interface BitcoinMessage {
|
|
5
5
|
rawMessage?: Bytes;
|
|
@@ -1,30 +1,25 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
};
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
const index_js_1 = require("./utils/index.js");
|
|
10
|
-
const minimalistic_assert_1 = __importDefault(require("minimalistic-assert"));
|
|
11
|
-
const psbt_js_1 = require("./utils/psbt.js");
|
|
12
|
-
const bip322_js_2 = require("./utils/bip322.js");
|
|
13
|
-
const applications_js_1 = __importDefault(require("./applications.js"));
|
|
1
|
+
import { fromBase58 } from '@exodus/bytes/base58.js';
|
|
2
|
+
import { AppClient, PsbtV2 } from '@exodus/ledger-bitcoin';
|
|
3
|
+
import { BIP322 } from '@exodus/bip322-js';
|
|
4
|
+
import { splitBip44DerivationPath } from './utils/index.js';
|
|
5
|
+
import assert from 'minimalistic-assert';
|
|
6
|
+
import { assertPsbtOnlyHasAllowedDerivationPaths, tagPsbtWithDerivationPaths, getWalletPolicy, getMultisigWalletPolicy, derivationPathsToWalletPolicy, addDummyInputs, } from './utils/psbt.js';
|
|
7
|
+
import { buildBip322PSBT } from './utils/bip322.js';
|
|
8
|
+
import applications from './applications.js';
|
|
14
9
|
async function createHandler(transport, walletPolicyAtom) {
|
|
15
|
-
const app = new
|
|
10
|
+
const app = new AppClient(transport);
|
|
16
11
|
const getAddress = async (params) => {
|
|
17
|
-
const [purpose, coin, account, change, address] =
|
|
12
|
+
const [purpose, coin, account, change, address] = splitBip44DerivationPath(params.derivationPath);
|
|
18
13
|
const fpr = await app.getMasterFingerprint();
|
|
19
14
|
const xpub = await app.getExtendedPubkey(`m/${purpose}/${coin}/${account}`);
|
|
20
15
|
let walletPolicy;
|
|
21
16
|
let hmac = null;
|
|
22
17
|
if (params.multisigData) {
|
|
23
|
-
walletPolicy =
|
|
18
|
+
walletPolicy = getMultisigWalletPolicy(fpr, xpub, purpose, coin, account, params.multisigData);
|
|
24
19
|
hmac = await registerWalletPolicy(walletPolicy);
|
|
25
20
|
}
|
|
26
21
|
else {
|
|
27
|
-
walletPolicy =
|
|
22
|
+
walletPolicy = getWalletPolicy(fpr, xpub, purpose, coin, account);
|
|
28
23
|
}
|
|
29
24
|
return app.getWalletAddress(walletPolicy, hmac, Number(change), Number(address), params.displayOnDevice ?? false);
|
|
30
25
|
};
|
|
@@ -33,12 +28,12 @@ async function createHandler(transport, walletPolicyAtom) {
|
|
|
33
28
|
};
|
|
34
29
|
const getPublicKey = async (params) => {
|
|
35
30
|
const pubString = await app.getExtendedPubkey(params.derivationPath);
|
|
36
|
-
return
|
|
31
|
+
return fromBase58(pubString, 'buffer').subarray(45, 45 + 33);
|
|
37
32
|
};
|
|
38
33
|
const signTransaction = async (params) => {
|
|
39
|
-
(
|
|
34
|
+
assert(Buffer.isBuffer(params.signableTransaction), 'signableTransaction must be PSBT buffer');
|
|
40
35
|
const psbtBuffer = params.signableTransaction;
|
|
41
|
-
const psbt = new
|
|
36
|
+
const psbt = new PsbtV2();
|
|
42
37
|
psbt.deserialize(psbtBuffer);
|
|
43
38
|
const fpr = await app.getMasterFingerprint();
|
|
44
39
|
const derivationPathToPublicKeyMap = Object.create(null);
|
|
@@ -58,10 +53,10 @@ async function createHandler(transport, walletPolicyAtom) {
|
|
|
58
53
|
});
|
|
59
54
|
derivationPathToPublicKeyMap[derivationPath] = publicKey;
|
|
60
55
|
}
|
|
61
|
-
const derivationPathsToSignFor =
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
const walletPolicies = await
|
|
56
|
+
const derivationPathsToSignFor = tagPsbtWithDerivationPaths(psbt, fpr, derivationPathToPublicKeyMap, addressToDerivationPathMap);
|
|
57
|
+
addDummyInputs(psbt);
|
|
58
|
+
assertPsbtOnlyHasAllowedDerivationPaths(psbt, fpr, params.derivationPaths);
|
|
59
|
+
const walletPolicies = await derivationPathsToWalletPolicy(fpr, app, derivationPathsToSignFor, params.multisigData);
|
|
65
60
|
const signatures = [];
|
|
66
61
|
for (const walletPolicy of walletPolicies) {
|
|
67
62
|
const hmac = params.multisigData ? await registerWalletPolicy(walletPolicy) : null;
|
|
@@ -78,21 +73,21 @@ async function createHandler(transport, walletPolicyAtom) {
|
|
|
78
73
|
};
|
|
79
74
|
const signMessage = async (params) => {
|
|
80
75
|
if (params.message.rawMessage) {
|
|
81
|
-
(
|
|
76
|
+
assert(Buffer.isBuffer(params.message.rawMessage), 'rawMessage must be buffer');
|
|
82
77
|
const signedMessageHex = await app.signMessage(params.message.rawMessage, params.derivationPath);
|
|
83
78
|
return Buffer.from(signedMessageHex, 'base64');
|
|
84
79
|
}
|
|
85
80
|
if (params.message.bip322Message) {
|
|
86
81
|
const { message, address } = params.message.bip322Message;
|
|
87
82
|
const { derivationPath } = params;
|
|
88
|
-
(
|
|
89
|
-
(
|
|
90
|
-
const [purpose, coin, account] =
|
|
83
|
+
assert(Buffer.isBuffer(message), 'bip322Message.message must be buffer');
|
|
84
|
+
assert(typeof address === 'string', 'bip322Message.address must be string');
|
|
85
|
+
const [purpose, coin, account] = splitBip44DerivationPath(derivationPath);
|
|
91
86
|
const fpr = await app.getMasterFingerprint();
|
|
92
87
|
const xpub = await app.getExtendedPubkey(`m/${purpose}/${coin}/${account}`);
|
|
93
|
-
const walletPolicy =
|
|
94
|
-
const psbtBip322 =
|
|
95
|
-
const psbt = new
|
|
88
|
+
const walletPolicy = getWalletPolicy(fpr, xpub, purpose, coin, account);
|
|
89
|
+
const psbtBip322 = buildBip322PSBT(message.toString(), address);
|
|
90
|
+
const psbt = new PsbtV2();
|
|
96
91
|
psbt.deserialize(psbtBip322.toBuffer());
|
|
97
92
|
const derivationPathToPublicKeyMap = {
|
|
98
93
|
[derivationPath]: await getPublicKey({
|
|
@@ -100,7 +95,7 @@ async function createHandler(transport, walletPolicyAtom) {
|
|
|
100
95
|
derivationPath,
|
|
101
96
|
}),
|
|
102
97
|
};
|
|
103
|
-
|
|
98
|
+
tagPsbtWithDerivationPaths(psbt, fpr, derivationPathToPublicKeyMap);
|
|
104
99
|
const signatures = await app.signPsbt(psbt.serialize(), walletPolicy, null);
|
|
105
100
|
const signature = signatures[0][1];
|
|
106
101
|
const isTaprootSig = signature.pubkey.length === 32;
|
|
@@ -115,7 +110,7 @@ async function createHandler(transport, walletPolicyAtom) {
|
|
|
115
110
|
});
|
|
116
111
|
}
|
|
117
112
|
psbtBip322.finalizeAllInputs();
|
|
118
|
-
const encodedWitness =
|
|
113
|
+
const encodedWitness = BIP322.encodeWitness(psbtBip322);
|
|
119
114
|
return Buffer.from(encodedWitness, 'base64');
|
|
120
115
|
}
|
|
121
116
|
throw new Error('rawMessage or bip322Message must be defined');
|
|
@@ -127,7 +122,7 @@ async function createHandler(transport, walletPolicyAtom) {
|
|
|
127
122
|
if (cached)
|
|
128
123
|
return cached;
|
|
129
124
|
const [, hmac] = await app.registerWallet(walletPolicy);
|
|
130
|
-
(
|
|
125
|
+
assert(hmac, 'app.registerWallet returned no HMAC');
|
|
131
126
|
const updatedPolicies = { ...walletPolicies, [id]: hmac };
|
|
132
127
|
await walletPolicyAtom?.set(updatedPolicies);
|
|
133
128
|
return hmac;
|
|
@@ -141,7 +136,7 @@ async function createHandler(transport, walletPolicyAtom) {
|
|
|
141
136
|
};
|
|
142
137
|
}
|
|
143
138
|
const metadata = {
|
|
144
|
-
applications: [
|
|
139
|
+
applications: [applications.Bitcoin],
|
|
145
140
|
handler: createHandler,
|
|
146
141
|
};
|
|
147
|
-
|
|
142
|
+
export default metadata;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type Transport from '@ledgerhq/hw-transport';
|
|
2
1
|
import type { HardwareWalletAssetHandler } from '@exodus/hw-common';
|
|
2
|
+
import type { Transport } from '../types.js';
|
|
3
3
|
declare function createHandler(transport: Transport): Promise<HardwareWalletAssetHandler>;
|
|
4
4
|
declare const metadata: {
|
|
5
5
|
applications: {
|
|
@@ -1,52 +1,15 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
if (k2 === undefined) k2 = k;
|
|
11
|
-
o[k2] = m[k];
|
|
12
|
-
}));
|
|
13
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
-
}) : function(o, v) {
|
|
16
|
-
o["default"] = v;
|
|
17
|
-
});
|
|
18
|
-
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
-
var ownKeys = function(o) {
|
|
20
|
-
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
-
var ar = [];
|
|
22
|
-
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
-
return ar;
|
|
24
|
-
};
|
|
25
|
-
return ownKeys(o);
|
|
26
|
-
};
|
|
27
|
-
return function (mod) {
|
|
28
|
-
if (mod && mod.__esModule) return mod;
|
|
29
|
-
var result = {};
|
|
30
|
-
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
-
__setModuleDefault(result, mod);
|
|
32
|
-
return result;
|
|
33
|
-
};
|
|
34
|
-
})();
|
|
35
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
|
-
};
|
|
38
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
-
const secp256k1 = __importStar(require("@exodus/crypto/secp256k1"));
|
|
40
|
-
const ethers = __importStar(require("@exodus/ethersproject-transactions"));
|
|
41
|
-
const ledgerhq_hw_app_eth_1 = __importStar(require("@exodus/ledgerhq-hw-app-eth"));
|
|
42
|
-
const eth_sig_util_1 = require("@exodus/ethereumjs/eth-sig-util");
|
|
43
|
-
const minimalistic_assert_1 = __importDefault(require("minimalistic-assert"));
|
|
44
|
-
const index_js_1 = require("./utils/index.js");
|
|
45
|
-
const errors_js_1 = require("../errors.js");
|
|
46
|
-
const applications_js_1 = __importDefault(require("./applications.js"));
|
|
1
|
+
import * as secp256k1 from '@exodus/crypto/secp256k1';
|
|
2
|
+
import * as ethers from '@exodus/ethersproject-transactions';
|
|
3
|
+
import AppEthImport, { ledgerService } from '@exodus/ledgerhq-hw-app-eth';
|
|
4
|
+
import { TypedDataUtils, SignTypedDataVersion } from '@exodus/ethereumjs/eth-sig-util';
|
|
5
|
+
import assert from 'minimalistic-assert';
|
|
6
|
+
import { buildFauxXPUB } from './utils/index.js';
|
|
7
|
+
import { RequiresBlindSigningError } from '../errors.js';
|
|
8
|
+
import applications from './applications.js';
|
|
9
|
+
const AppEth = (AppEthImport.default ?? AppEthImport);
|
|
47
10
|
function hashEIP712Message(typedData) {
|
|
48
|
-
const version =
|
|
49
|
-
const { sanitizeData, hashStruct } =
|
|
11
|
+
const version = SignTypedDataVersion.V4;
|
|
12
|
+
const { sanitizeData, hashStruct } = TypedDataUtils;
|
|
50
13
|
const sanitizedData = sanitizeData(typedData);
|
|
51
14
|
const domainSeparator = hashStruct('EIP712Domain', sanitizedData.domain, sanitizedData.types, version);
|
|
52
15
|
let hashStructMessage = Buffer.alloc(0);
|
|
@@ -66,7 +29,7 @@ function isEIP712Message(message) {
|
|
|
66
29
|
return (candidateKeys.length === ownKeys.length && candidateKeys.every((key) => ownKeys.includes(key)));
|
|
67
30
|
}
|
|
68
31
|
async function createHandler(transport) {
|
|
69
|
-
const app = new
|
|
32
|
+
const app = new AppEth(transport);
|
|
70
33
|
return {
|
|
71
34
|
getAddress: async (params) => {
|
|
72
35
|
const { address } = await app.getAddress(params.derivationPath.replace('m/', ''));
|
|
@@ -79,7 +42,7 @@ async function createHandler(transport) {
|
|
|
79
42
|
compressed: true,
|
|
80
43
|
format: 'buffer',
|
|
81
44
|
});
|
|
82
|
-
return
|
|
45
|
+
return buildFauxXPUB(compressedPublicKey, Buffer.from(chainCode, 'hex'));
|
|
83
46
|
},
|
|
84
47
|
getPublicKey: async (params) => {
|
|
85
48
|
const { publicKey } = await app.getAddress(params.derivationPath.replace('m/', ''));
|
|
@@ -90,8 +53,8 @@ async function createHandler(transport) {
|
|
|
90
53
|
});
|
|
91
54
|
},
|
|
92
55
|
signTransaction: async (params) => {
|
|
93
|
-
(
|
|
94
|
-
(
|
|
56
|
+
assert(Array.isArray(params.derivationPaths) && params.derivationPaths.length === 1, 'derivationsPath array must be one element long');
|
|
57
|
+
assert(Buffer.isBuffer(params.signableTransaction), 'signableTransaction must be Buffer');
|
|
95
58
|
const deserializedTransaction = ethers.parse(params.signableTransaction);
|
|
96
59
|
switch (params.assetName) {
|
|
97
60
|
case 'ethereum': {
|
|
@@ -111,20 +74,23 @@ async function createHandler(transport) {
|
|
|
111
74
|
delete deserializedTransaction.s;
|
|
112
75
|
delete deserializedTransaction.v;
|
|
113
76
|
const serializedTransactionHex = ethers.serialize(deserializedTransaction).slice(2);
|
|
114
|
-
const signTransaction = async ({ derivationPath, serializedTransactionHex,
|
|
115
|
-
const resolution = await
|
|
77
|
+
const signTransaction = async ({ derivationPath, serializedTransactionHex, erc20 = true, nft = true, }) => {
|
|
78
|
+
const resolution = await ledgerService.resolveTransaction(serializedTransactionHex, app.loadConfig, {
|
|
116
79
|
externalPlugins: true,
|
|
117
|
-
erc20
|
|
118
|
-
nft
|
|
80
|
+
erc20,
|
|
81
|
+
nft,
|
|
119
82
|
});
|
|
120
83
|
return app.signTransaction(derivationPath, serializedTransactionHex, resolution);
|
|
121
84
|
};
|
|
85
|
+
const resolveErc20 = params.isNft === undefined ? true : !params.isNft;
|
|
86
|
+
const resolveNft = params.isNft ?? true;
|
|
122
87
|
let response = null;
|
|
123
88
|
try {
|
|
124
89
|
response = await signTransaction({
|
|
125
90
|
derivationPath: params.derivationPaths[0],
|
|
126
91
|
serializedTransactionHex,
|
|
127
|
-
|
|
92
|
+
erc20: resolveErc20,
|
|
93
|
+
nft: resolveNft,
|
|
128
94
|
});
|
|
129
95
|
}
|
|
130
96
|
catch (err) {
|
|
@@ -132,11 +98,12 @@ async function createHandler(transport) {
|
|
|
132
98
|
response = await signTransaction({
|
|
133
99
|
derivationPath: params.derivationPaths[0],
|
|
134
100
|
serializedTransactionHex,
|
|
135
|
-
|
|
101
|
+
erc20: resolveErc20,
|
|
102
|
+
nft: false,
|
|
136
103
|
});
|
|
137
104
|
}
|
|
138
105
|
else if (err.name === 'EthAppPleaseEnableContractData') {
|
|
139
|
-
throw new
|
|
106
|
+
throw new RequiresBlindSigningError();
|
|
140
107
|
}
|
|
141
108
|
else {
|
|
142
109
|
throw err;
|
|
@@ -157,11 +124,11 @@ async function createHandler(transport) {
|
|
|
157
124
|
const message = params.message;
|
|
158
125
|
let signingPromise;
|
|
159
126
|
if (message.rawMessage) {
|
|
160
|
-
(
|
|
127
|
+
assert(Buffer.isBuffer(params.message.rawMessage), 'rawMessage must be a buffer');
|
|
161
128
|
signingPromise = app.signPersonalMessage(params.derivationPath, Buffer.from(message.rawMessage).toString('hex'));
|
|
162
129
|
}
|
|
163
130
|
else if (message.EIP712Message) {
|
|
164
|
-
(
|
|
131
|
+
assert(isEIP712Message(message.EIP712Message), 'EIP712Message must be EIP712 conformant message');
|
|
165
132
|
signingPromise = app
|
|
166
133
|
.signEIP712Message(params.derivationPath, message.EIP712Message)
|
|
167
134
|
.catch((err) => {
|
|
@@ -173,7 +140,7 @@ async function createHandler(transport) {
|
|
|
173
140
|
});
|
|
174
141
|
}
|
|
175
142
|
else {
|
|
176
|
-
(
|
|
143
|
+
assert(false, 'rawMessage or EIP712Message must be specified');
|
|
177
144
|
}
|
|
178
145
|
const { r, s, v } = await signingPromise;
|
|
179
146
|
const vNorm = v.toString(16);
|
|
@@ -183,7 +150,7 @@ async function createHandler(transport) {
|
|
|
183
150
|
};
|
|
184
151
|
}
|
|
185
152
|
const metadata = {
|
|
186
|
-
applications: [
|
|
153
|
+
applications: [applications.Ethereum],
|
|
187
154
|
handler: createHandler,
|
|
188
155
|
};
|
|
189
|
-
|
|
156
|
+
export default metadata;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import type Transport from '@ledgerhq/hw-transport';
|
|
2
1
|
import type { HardwareWalletAssetHandler } from '@exodus/hw-common';
|
|
3
2
|
import type { Atom } from '@exodus/atoms';
|
|
3
|
+
import type { Transport } from '../types.js';
|
|
4
4
|
type HandlerFactory = (transport: Transport, walletPolicyAtom?: Atom<Record<string, Buffer>>) => Promise<HardwareWalletAssetHandler>;
|
|
5
5
|
type AssetMetadata = {
|
|
6
6
|
applications: {
|
|
@@ -1,36 +1,30 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
import bitcoin from './bitcoin.js';
|
|
2
|
+
import basemainnet from './basemainnet.js';
|
|
3
|
+
import ethereum from './ethereum.js';
|
|
4
|
+
import matic from './matic.js';
|
|
5
|
+
import ripple from './ripple.js';
|
|
6
|
+
import solana from './solana.js';
|
|
7
|
+
import tronmainnet from './tronmainnet.js';
|
|
8
|
+
import applications from './applications.js';
|
|
9
|
+
export const assetApplications = {
|
|
10
|
+
basemainnet,
|
|
11
|
+
bitcoin,
|
|
12
|
+
bitcointestnet: bitcoin,
|
|
13
|
+
bitcoinregtest: bitcoin,
|
|
14
|
+
matic,
|
|
15
|
+
ripple,
|
|
16
|
+
solana,
|
|
17
|
+
ethereum,
|
|
18
|
+
ethereumsepolia: ethereum,
|
|
19
|
+
tronmainnet,
|
|
4
20
|
};
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
const bitcoin_js_1 = __importDefault(require("./bitcoin.js"));
|
|
8
|
-
const basemainnet_js_1 = __importDefault(require("./basemainnet.js"));
|
|
9
|
-
const ethereum_js_1 = __importDefault(require("./ethereum.js"));
|
|
10
|
-
const matic_js_1 = __importDefault(require("./matic.js"));
|
|
11
|
-
const ripple_js_1 = __importDefault(require("./ripple.js"));
|
|
12
|
-
const solana_js_1 = __importDefault(require("./solana.js"));
|
|
13
|
-
const tronmainnet_js_1 = __importDefault(require("./tronmainnet.js"));
|
|
14
|
-
const applications_js_1 = __importDefault(require("./applications.js"));
|
|
15
|
-
exports.assetApplications = {
|
|
16
|
-
basemainnet: basemainnet_js_1.default,
|
|
17
|
-
bitcoin: bitcoin_js_1.default,
|
|
18
|
-
bitcointestnet: bitcoin_js_1.default,
|
|
19
|
-
bitcoinregtest: bitcoin_js_1.default,
|
|
20
|
-
matic: matic_js_1.default,
|
|
21
|
-
ripple: ripple_js_1.default,
|
|
22
|
-
solana: solana_js_1.default,
|
|
23
|
-
ethereum: ethereum_js_1.default,
|
|
24
|
-
ethereumsepolia: ethereum_js_1.default,
|
|
25
|
-
tronmainnet: tronmainnet_js_1.default,
|
|
26
|
-
};
|
|
27
|
-
exports.supportedBaseAssetNames = Object.keys(exports.assetApplications);
|
|
28
|
-
exports.applicationNameToBaseAssetNames = Object.entries(exports.assetApplications).reduce((mapping, [assetName, metadata]) => {
|
|
21
|
+
export const supportedBaseAssetNames = Object.keys(assetApplications);
|
|
22
|
+
export const applicationNameToBaseAssetNames = Object.entries(assetApplications).reduce((mapping, [assetName, metadata]) => {
|
|
29
23
|
for (const { applicationName } of metadata.applications) {
|
|
30
24
|
if (!Array.isArray(mapping[applicationName])) {
|
|
31
25
|
mapping[applicationName] = [];
|
|
32
26
|
}
|
|
33
|
-
const isPrimaryAssetForApplication =
|
|
27
|
+
const isPrimaryAssetForApplication = applications[applicationName]?.primaryAssetName === assetName;
|
|
34
28
|
if (isPrimaryAssetForApplication) {
|
|
35
29
|
mapping[applicationName].unshift(assetName);
|
|
36
30
|
}
|