@bytezhang/hardware-ledger-connector-webhid 0.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.
@@ -0,0 +1,76 @@
1
+ import { IConnector, ConnectorDevice, ConnectorSession, ConnectorEventType, ConnectorEventMap } from '@bytezhang/hardware-wallet-core';
2
+ import { IDmk } from '@bytezhang/ledger-adapter';
3
+
4
+ interface LedgerWebHidConnectorOptions {
5
+ /**
6
+ * Pre-built DMK instance. If not provided, a DMK will be created
7
+ * lazily on first use via `@ledgerhq/device-management-kit` and
8
+ * `@ledgerhq/device-transport-kit-web-hid`.
9
+ */
10
+ dmk?: IDmk;
11
+ }
12
+ /**
13
+ * IConnector implementation for Ledger hardware wallets via WebHID.
14
+ *
15
+ * Wraps the Ledger DMK (Device Management Kit) and signer kits into
16
+ * the unified IConnector interface used by the adapter layer.
17
+ *
18
+ * Design:
19
+ * - Constructor accepts an optional pre-built DMK instance (or creates one lazily).
20
+ * - searchDevices() uses DMK's listenToAvailableDevices() observable.
21
+ * - connect() calls dmk.connect({ device }), returns a ConnectorSession.
22
+ * - call() dispatches to chain-specific signer methods (EVM, BTC).
23
+ * - Internally uses LedgerDeviceManager for session tracking and
24
+ * SignerManager for per-session signer caching.
25
+ */
26
+ declare class LedgerWebHidConnector implements IConnector {
27
+ private _deviceManager;
28
+ private _signerManager;
29
+ private _dmk;
30
+ private readonly _eventHandlers;
31
+ private readonly _providedDmk;
32
+ constructor(options?: LedgerWebHidConnectorOptions);
33
+ searchDevices(): Promise<ConnectorDevice[]>;
34
+ connect(deviceId?: string): Promise<ConnectorSession>;
35
+ disconnect(sessionId: string): Promise<void>;
36
+ call(sessionId: string, method: string, params: unknown): Promise<unknown>;
37
+ cancel(_sessionId: string): Promise<void>;
38
+ uiResponse(_response: {
39
+ type: string;
40
+ payload: unknown;
41
+ }): void;
42
+ on<K extends ConnectorEventType>(event: K, handler: (data: ConnectorEventMap[K]) => void): void;
43
+ off<K extends ConnectorEventType>(event: K, handler: (data: ConnectorEventMap[K]) => void): void;
44
+ reset(): void;
45
+ private _evmGetAddress;
46
+ private _evmSignTransaction;
47
+ private _evmSignMessage;
48
+ private _evmSignTypedData;
49
+ private _btcGetAddress;
50
+ private _btcGetPublicKey;
51
+ /**
52
+ * Lazily create or return the DMK instance.
53
+ * If a DMK was provided via constructor, it is used directly.
54
+ * Otherwise, one is created via dynamic import of the Ledger SDK.
55
+ */
56
+ private _getOrCreateDmk;
57
+ private _initManagers;
58
+ private _getDeviceManager;
59
+ private _getEthSigner;
60
+ private _createBtcSigner;
61
+ private _invalidateSession;
62
+ private _resetManagers;
63
+ private _emit;
64
+ private _wrapError;
65
+ }
66
+
67
+ /**
68
+ * Create a LedgerWebHidConnector.
69
+ *
70
+ * @param dmk - Optional pre-built DMK instance. If omitted, the connector
71
+ * will lazily create one using `@ledgerhq/device-management-kit`
72
+ * and `@ledgerhq/device-transport-kit-web-hid`.
73
+ */
74
+ declare function createLedgerWebHidConnector(dmk?: IDmk): IConnector;
75
+
76
+ export { LedgerWebHidConnector, type LedgerWebHidConnectorOptions, createLedgerWebHidConnector };
@@ -0,0 +1,76 @@
1
+ import { IConnector, ConnectorDevice, ConnectorSession, ConnectorEventType, ConnectorEventMap } from '@bytezhang/hardware-wallet-core';
2
+ import { IDmk } from '@bytezhang/ledger-adapter';
3
+
4
+ interface LedgerWebHidConnectorOptions {
5
+ /**
6
+ * Pre-built DMK instance. If not provided, a DMK will be created
7
+ * lazily on first use via `@ledgerhq/device-management-kit` and
8
+ * `@ledgerhq/device-transport-kit-web-hid`.
9
+ */
10
+ dmk?: IDmk;
11
+ }
12
+ /**
13
+ * IConnector implementation for Ledger hardware wallets via WebHID.
14
+ *
15
+ * Wraps the Ledger DMK (Device Management Kit) and signer kits into
16
+ * the unified IConnector interface used by the adapter layer.
17
+ *
18
+ * Design:
19
+ * - Constructor accepts an optional pre-built DMK instance (or creates one lazily).
20
+ * - searchDevices() uses DMK's listenToAvailableDevices() observable.
21
+ * - connect() calls dmk.connect({ device }), returns a ConnectorSession.
22
+ * - call() dispatches to chain-specific signer methods (EVM, BTC).
23
+ * - Internally uses LedgerDeviceManager for session tracking and
24
+ * SignerManager for per-session signer caching.
25
+ */
26
+ declare class LedgerWebHidConnector implements IConnector {
27
+ private _deviceManager;
28
+ private _signerManager;
29
+ private _dmk;
30
+ private readonly _eventHandlers;
31
+ private readonly _providedDmk;
32
+ constructor(options?: LedgerWebHidConnectorOptions);
33
+ searchDevices(): Promise<ConnectorDevice[]>;
34
+ connect(deviceId?: string): Promise<ConnectorSession>;
35
+ disconnect(sessionId: string): Promise<void>;
36
+ call(sessionId: string, method: string, params: unknown): Promise<unknown>;
37
+ cancel(_sessionId: string): Promise<void>;
38
+ uiResponse(_response: {
39
+ type: string;
40
+ payload: unknown;
41
+ }): void;
42
+ on<K extends ConnectorEventType>(event: K, handler: (data: ConnectorEventMap[K]) => void): void;
43
+ off<K extends ConnectorEventType>(event: K, handler: (data: ConnectorEventMap[K]) => void): void;
44
+ reset(): void;
45
+ private _evmGetAddress;
46
+ private _evmSignTransaction;
47
+ private _evmSignMessage;
48
+ private _evmSignTypedData;
49
+ private _btcGetAddress;
50
+ private _btcGetPublicKey;
51
+ /**
52
+ * Lazily create or return the DMK instance.
53
+ * If a DMK was provided via constructor, it is used directly.
54
+ * Otherwise, one is created via dynamic import of the Ledger SDK.
55
+ */
56
+ private _getOrCreateDmk;
57
+ private _initManagers;
58
+ private _getDeviceManager;
59
+ private _getEthSigner;
60
+ private _createBtcSigner;
61
+ private _invalidateSession;
62
+ private _resetManagers;
63
+ private _emit;
64
+ private _wrapError;
65
+ }
66
+
67
+ /**
68
+ * Create a LedgerWebHidConnector.
69
+ *
70
+ * @param dmk - Optional pre-built DMK instance. If omitted, the connector
71
+ * will lazily create one using `@ledgerhq/device-management-kit`
72
+ * and `@ledgerhq/device-transport-kit-web-hid`.
73
+ */
74
+ declare function createLedgerWebHidConnector(dmk?: IDmk): IConnector;
75
+
76
+ export { LedgerWebHidConnector, type LedgerWebHidConnectorOptions, createLedgerWebHidConnector };
package/dist/index.js ADDED
@@ -0,0 +1,398 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // src/index.ts
31
+ var index_exports = {};
32
+ __export(index_exports, {
33
+ LedgerWebHidConnector: () => LedgerWebHidConnector,
34
+ createLedgerWebHidConnector: () => createLedgerWebHidConnector
35
+ });
36
+ module.exports = __toCommonJS(index_exports);
37
+
38
+ // src/LedgerWebHidConnector.ts
39
+ var import_ledger_adapter = require("@bytezhang/ledger-adapter");
40
+ function normalizePath(path) {
41
+ return path.startsWith("m/") ? path.slice(2) : path;
42
+ }
43
+ function stripHex(hex) {
44
+ return hex.startsWith("0x") ? hex.slice(2) : hex;
45
+ }
46
+ function padHex64(hex) {
47
+ return `0x${stripHex(hex).padStart(64, "0")}`;
48
+ }
49
+ var LedgerWebHidConnector = class {
50
+ constructor(options) {
51
+ this._deviceManager = null;
52
+ this._signerManager = null;
53
+ this._dmk = null;
54
+ this._eventHandlers = /* @__PURE__ */ new Map();
55
+ this._providedDmk = options?.dmk;
56
+ if (this._providedDmk) {
57
+ this._initManagers(this._providedDmk);
58
+ }
59
+ }
60
+ // ---------------------------------------------------------------------------
61
+ // IConnector — Device discovery
62
+ // ---------------------------------------------------------------------------
63
+ async searchDevices() {
64
+ const dm = await this._getDeviceManager();
65
+ let descriptors = await dm.enumerate();
66
+ if (descriptors.length === 0) {
67
+ try {
68
+ await dm.requestDevice();
69
+ } catch {
70
+ }
71
+ descriptors = await dm.enumerate();
72
+ }
73
+ return descriptors.map((d) => ({
74
+ connectId: d.path,
75
+ deviceId: d.path,
76
+ name: d.type ?? "Ledger",
77
+ model: d.type
78
+ }));
79
+ }
80
+ // ---------------------------------------------------------------------------
81
+ // IConnector — Connection
82
+ // ---------------------------------------------------------------------------
83
+ async connect(deviceId) {
84
+ const dm = await this._getDeviceManager();
85
+ await this.searchDevices();
86
+ let resolvedDeviceId = deviceId;
87
+ if (!resolvedDeviceId) {
88
+ const descriptors = await dm.enumerate();
89
+ if (descriptors.length === 0) {
90
+ throw new Error(
91
+ "No Ledger device found. Make sure the device is connected via USB and unlocked."
92
+ );
93
+ }
94
+ resolvedDeviceId = descriptors[0].path;
95
+ }
96
+ try {
97
+ const sessionId = await dm.connect(resolvedDeviceId);
98
+ const session = {
99
+ sessionId,
100
+ deviceInfo: {
101
+ vendor: "ledger",
102
+ model: "unknown",
103
+ firmwareVersion: "unknown",
104
+ deviceId: resolvedDeviceId,
105
+ connectId: resolvedDeviceId,
106
+ connectionType: "usb"
107
+ }
108
+ };
109
+ this._emit("device-connect", {
110
+ device: {
111
+ connectId: resolvedDeviceId,
112
+ deviceId: resolvedDeviceId,
113
+ name: "Ledger"
114
+ }
115
+ });
116
+ return session;
117
+ } catch (err) {
118
+ this._resetManagers();
119
+ const dm2 = await this._getDeviceManager();
120
+ await this.searchDevices();
121
+ const descriptors = await dm2.enumerate();
122
+ const retryId = deviceId ? descriptors.find((d) => d.path === deviceId)?.path : descriptors[0]?.path;
123
+ if (!retryId) {
124
+ throw new Error(
125
+ "No Ledger device found after retry. Make sure the device is connected via USB and unlocked."
126
+ );
127
+ }
128
+ const sessionId = await dm2.connect(retryId);
129
+ const session = {
130
+ sessionId,
131
+ deviceInfo: {
132
+ vendor: "ledger",
133
+ model: "unknown",
134
+ firmwareVersion: "unknown",
135
+ deviceId: retryId,
136
+ connectId: retryId,
137
+ connectionType: "usb"
138
+ }
139
+ };
140
+ this._emit("device-connect", {
141
+ device: {
142
+ connectId: retryId,
143
+ deviceId: retryId,
144
+ name: "Ledger"
145
+ }
146
+ });
147
+ return session;
148
+ }
149
+ }
150
+ async disconnect(sessionId) {
151
+ if (!this._deviceManager) return;
152
+ const deviceId = this._deviceManager.getDeviceId(sessionId);
153
+ this._signerManager?.invalidate(sessionId);
154
+ await this._deviceManager.disconnect(sessionId);
155
+ if (deviceId) {
156
+ this._emit("device-disconnect", { connectId: deviceId });
157
+ }
158
+ }
159
+ // ---------------------------------------------------------------------------
160
+ // IConnector — Method dispatch
161
+ // ---------------------------------------------------------------------------
162
+ async call(sessionId, method, params) {
163
+ switch (method) {
164
+ case "evmGetAddress":
165
+ return this._evmGetAddress(sessionId, params);
166
+ case "evmSignTransaction":
167
+ return this._evmSignTransaction(sessionId, params);
168
+ case "evmSignMessage":
169
+ return this._evmSignMessage(sessionId, params);
170
+ case "evmSignTypedData":
171
+ return this._evmSignTypedData(sessionId, params);
172
+ case "btcGetAddress":
173
+ return this._btcGetAddress(sessionId, params);
174
+ case "btcGetPublicKey":
175
+ return this._btcGetPublicKey(sessionId, params);
176
+ default:
177
+ throw new Error(`LedgerWebHidConnector: unknown method "${method}"`);
178
+ }
179
+ }
180
+ async cancel(_sessionId) {
181
+ }
182
+ uiResponse(_response) {
183
+ }
184
+ // ---------------------------------------------------------------------------
185
+ // IConnector — Events
186
+ // ---------------------------------------------------------------------------
187
+ on(event, handler) {
188
+ if (!this._eventHandlers.has(event)) {
189
+ this._eventHandlers.set(event, /* @__PURE__ */ new Set());
190
+ }
191
+ this._eventHandlers.get(event).add(handler);
192
+ }
193
+ off(event, handler) {
194
+ this._eventHandlers.get(event)?.delete(handler);
195
+ }
196
+ // ---------------------------------------------------------------------------
197
+ // IConnector — Reset
198
+ // ---------------------------------------------------------------------------
199
+ reset() {
200
+ this._resetManagers();
201
+ }
202
+ // ---------------------------------------------------------------------------
203
+ // Private — EVM methods
204
+ // ---------------------------------------------------------------------------
205
+ async _evmGetAddress(sessionId, params) {
206
+ const signer = await this._getEthSigner(sessionId);
207
+ const path = normalizePath(params.path);
208
+ try {
209
+ const result = await signer.getAddress(path, {
210
+ checkOnDevice: params.showOnDevice ?? false
211
+ });
212
+ return { address: result.address, publicKey: result.publicKey };
213
+ } catch (err) {
214
+ this._invalidateSession(sessionId);
215
+ throw this._wrapError(err);
216
+ }
217
+ }
218
+ async _evmSignTransaction(sessionId, params) {
219
+ const signer = await this._getEthSigner(sessionId);
220
+ const path = normalizePath(params.path);
221
+ try {
222
+ const result = await signer.signTransaction(
223
+ path,
224
+ params.serializedTx
225
+ );
226
+ return {
227
+ v: `0x${result.v.toString(16)}`,
228
+ r: padHex64(result.r),
229
+ s: padHex64(result.s)
230
+ };
231
+ } catch (err) {
232
+ this._invalidateSession(sessionId);
233
+ throw this._wrapError(err);
234
+ }
235
+ }
236
+ async _evmSignMessage(sessionId, params) {
237
+ const signer = await this._getEthSigner(sessionId);
238
+ const path = normalizePath(params.path);
239
+ try {
240
+ const result = await signer.signMessage(
241
+ path,
242
+ params.message
243
+ );
244
+ const rHex = stripHex(result.r).padStart(64, "0");
245
+ const sHex = stripHex(result.s).padStart(64, "0");
246
+ const vHex = result.v.toString(16).padStart(2, "0");
247
+ return { signature: `0x${rHex}${sHex}${vHex}` };
248
+ } catch (err) {
249
+ this._invalidateSession(sessionId);
250
+ throw this._wrapError(err);
251
+ }
252
+ }
253
+ async _evmSignTypedData(sessionId, params) {
254
+ const signer = await this._getEthSigner(sessionId);
255
+ const path = normalizePath(params.path);
256
+ try {
257
+ const result = await signer.signTypedData(
258
+ path,
259
+ params.data
260
+ );
261
+ const rHex = stripHex(result.r).padStart(64, "0");
262
+ const sHex = stripHex(result.s).padStart(64, "0");
263
+ const vHex = result.v.toString(16).padStart(2, "0");
264
+ return { signature: `0x${rHex}${sHex}${vHex}` };
265
+ } catch (err) {
266
+ this._invalidateSession(sessionId);
267
+ throw this._wrapError(err);
268
+ }
269
+ }
270
+ // ---------------------------------------------------------------------------
271
+ // Private — BTC methods
272
+ // ---------------------------------------------------------------------------
273
+ async _btcGetAddress(sessionId, params) {
274
+ const btcSigner = await this._createBtcSigner(sessionId);
275
+ const path = normalizePath(params.path);
276
+ try {
277
+ const { DefaultWallet, DefaultDescriptorTemplate } = await import("@ledgerhq/device-signer-kit-bitcoin");
278
+ const purpose = path.split("/")[0]?.replace("'", "");
279
+ let template = DefaultDescriptorTemplate.NATIVE_SEGWIT;
280
+ if (purpose === "44") template = DefaultDescriptorTemplate.LEGACY;
281
+ else if (purpose === "49")
282
+ template = DefaultDescriptorTemplate.NESTED_SEGWIT;
283
+ else if (purpose === "86") template = DefaultDescriptorTemplate.TAPROOT;
284
+ const wallet = new DefaultWallet(path, template);
285
+ const result = await btcSigner.getWalletAddress(wallet, 0, {
286
+ checkOnDevice: params.showOnDevice ?? false,
287
+ change: false
288
+ });
289
+ return { address: result.address, path: params.path };
290
+ } catch (err) {
291
+ this._invalidateSession(sessionId);
292
+ throw this._wrapError(err);
293
+ }
294
+ }
295
+ async _btcGetPublicKey(sessionId, params) {
296
+ const btcSigner = await this._createBtcSigner(sessionId);
297
+ const path = normalizePath(params.path);
298
+ try {
299
+ const xpub = await btcSigner.getExtendedPublicKey(path, {
300
+ checkOnDevice: params.showOnDevice ?? false
301
+ });
302
+ return { xpub, path: params.path };
303
+ } catch (err) {
304
+ this._invalidateSession(sessionId);
305
+ throw this._wrapError(err);
306
+ }
307
+ }
308
+ // ---------------------------------------------------------------------------
309
+ // Private — DMK / Manager lifecycle
310
+ // ---------------------------------------------------------------------------
311
+ /**
312
+ * Lazily create or return the DMK instance.
313
+ * If a DMK was provided via constructor, it is used directly.
314
+ * Otherwise, one is created via dynamic import of the Ledger SDK.
315
+ */
316
+ async _getOrCreateDmk() {
317
+ if (this._dmk) return this._dmk;
318
+ if (this._providedDmk) {
319
+ this._dmk = this._providedDmk;
320
+ return this._dmk;
321
+ }
322
+ const { DeviceManagementKitBuilder } = await import("@ledgerhq/device-management-kit");
323
+ const { webHidTransportFactory } = await import("@ledgerhq/device-transport-kit-web-hid");
324
+ this._dmk = new DeviceManagementKitBuilder().addTransport(webHidTransportFactory).build();
325
+ return this._dmk;
326
+ }
327
+ _initManagers(dmk) {
328
+ this._dmk = dmk;
329
+ this._deviceManager = new import_ledger_adapter.LedgerDeviceManager(dmk);
330
+ this._signerManager = new import_ledger_adapter.SignerManager(dmk);
331
+ }
332
+ async _getDeviceManager() {
333
+ if (this._deviceManager) return this._deviceManager;
334
+ const dmk = await this._getOrCreateDmk();
335
+ this._initManagers(dmk);
336
+ return this._deviceManager;
337
+ }
338
+ async _getEthSigner(sessionId) {
339
+ if (!this._signerManager) {
340
+ const dmk = await this._getOrCreateDmk();
341
+ this._initManagers(dmk);
342
+ }
343
+ return this._signerManager.getOrCreate(sessionId);
344
+ }
345
+ async _createBtcSigner(sessionId) {
346
+ const dmk = await this._getOrCreateDmk();
347
+ const { SignerBtcBuilder } = await import("@ledgerhq/device-signer-kit-bitcoin");
348
+ const sdkSigner = new SignerBtcBuilder({
349
+ dmk,
350
+ sessionId
351
+ }).build();
352
+ return new import_ledger_adapter.SignerBtc(sdkSigner);
353
+ }
354
+ _invalidateSession(sessionId) {
355
+ this._signerManager?.invalidate(sessionId);
356
+ }
357
+ _resetManagers() {
358
+ this._signerManager?.clearAll();
359
+ this._deviceManager?.dispose();
360
+ this._deviceManager = null;
361
+ this._signerManager = null;
362
+ this._dmk = null;
363
+ }
364
+ // ---------------------------------------------------------------------------
365
+ // Private — Events
366
+ // ---------------------------------------------------------------------------
367
+ _emit(event, data) {
368
+ const handlers = this._eventHandlers.get(event);
369
+ if (handlers) {
370
+ for (const handler of handlers) {
371
+ try {
372
+ handler(data);
373
+ } catch {
374
+ }
375
+ }
376
+ }
377
+ }
378
+ // ---------------------------------------------------------------------------
379
+ // Private — Error handling
380
+ // ---------------------------------------------------------------------------
381
+ _wrapError(err) {
382
+ const mapped = (0, import_ledger_adapter.mapLedgerError)(err);
383
+ const error = new Error(mapped.message);
384
+ error.code = mapped.code;
385
+ return error;
386
+ }
387
+ };
388
+
389
+ // src/index.ts
390
+ function createLedgerWebHidConnector(dmk) {
391
+ return new LedgerWebHidConnector({ dmk });
392
+ }
393
+ // Annotate the CommonJS export names for ESM import in node:
394
+ 0 && (module.exports = {
395
+ LedgerWebHidConnector,
396
+ createLedgerWebHidConnector
397
+ });
398
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts","../src/LedgerWebHidConnector.ts"],"sourcesContent":["import type { IConnector } from '@bytezhang/hardware-wallet-core';\nimport type { IDmk } from '@bytezhang/ledger-adapter';\n\nimport {\n LedgerWebHidConnector,\n} from './LedgerWebHidConnector';\nimport type { LedgerWebHidConnectorOptions } from './LedgerWebHidConnector';\n\nexport { LedgerWebHidConnector };\nexport type { LedgerWebHidConnectorOptions };\n\n/**\n * Create a LedgerWebHidConnector.\n *\n * @param dmk - Optional pre-built DMK instance. If omitted, the connector\n * will lazily create one using `@ledgerhq/device-management-kit`\n * and `@ledgerhq/device-transport-kit-web-hid`.\n */\nexport function createLedgerWebHidConnector(dmk?: IDmk): IConnector {\n return new LedgerWebHidConnector({ dmk });\n}\n","import type {\n IConnector,\n ConnectorDevice,\n ConnectorSession,\n ConnectorEventType,\n ConnectorEventMap,\n} from '@bytezhang/hardware-wallet-core';\nimport type { IDmk, SignerEvmSignature } from '@bytezhang/ledger-adapter';\nimport {\n LedgerDeviceManager,\n SignerManager,\n SignerBtc,\n mapLedgerError,\n} from '@bytezhang/ledger-adapter';\n\n// ---------------------------------------------------------------------------\n// Internal types\n// ---------------------------------------------------------------------------\n\ntype EventHandler<K extends ConnectorEventType> = (\n data: ConnectorEventMap[K],\n) => void;\n\n/** Parameters for evmGetAddress */\ninterface EvmGetAddressCallParams {\n path: string;\n showOnDevice?: boolean;\n}\n\n/** Parameters for evmSignTransaction */\ninterface EvmSignTransactionCallParams {\n path: string;\n /** RLP-serialized transaction hex (0x-prefixed or plain) */\n serializedTx: string;\n}\n\n/** Parameters for evmSignMessage */\ninterface EvmSignMessageCallParams {\n path: string;\n message: string;\n}\n\n/** Parameters for evmSignTypedData */\ninterface EvmSignTypedDataCallParams {\n path: string;\n data: unknown;\n}\n\n/** Parameters for btcGetAddress */\ninterface BtcGetAddressCallParams {\n path: string;\n showOnDevice?: boolean;\n}\n\n/** Parameters for btcGetPublicKey */\ninterface BtcGetPublicKeyCallParams {\n path: string;\n showOnDevice?: boolean;\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\n/** Strip the \"m/\" prefix from BIP-44 derivation paths. */\nfunction normalizePath(path: string): string {\n return path.startsWith('m/') ? path.slice(2) : path;\n}\n\n/** Remove `0x` prefix from a hex string if present. */\nfunction stripHex(hex: string): string {\n return hex.startsWith('0x') ? hex.slice(2) : hex;\n}\n\n/** Ensure a hex string is `0x`-prefixed and zero-padded to 64 hex chars (32 bytes). */\nfunction padHex64(hex: string): string {\n return `0x${stripHex(hex).padStart(64, '0')}`;\n}\n\n// ---------------------------------------------------------------------------\n// LedgerWebHidConnector\n// ---------------------------------------------------------------------------\n\nexport interface LedgerWebHidConnectorOptions {\n /**\n * Pre-built DMK instance. If not provided, a DMK will be created\n * lazily on first use via `@ledgerhq/device-management-kit` and\n * `@ledgerhq/device-transport-kit-web-hid`.\n */\n dmk?: IDmk;\n}\n\n/**\n * IConnector implementation for Ledger hardware wallets via WebHID.\n *\n * Wraps the Ledger DMK (Device Management Kit) and signer kits into\n * the unified IConnector interface used by the adapter layer.\n *\n * Design:\n * - Constructor accepts an optional pre-built DMK instance (or creates one lazily).\n * - searchDevices() uses DMK's listenToAvailableDevices() observable.\n * - connect() calls dmk.connect({ device }), returns a ConnectorSession.\n * - call() dispatches to chain-specific signer methods (EVM, BTC).\n * - Internally uses LedgerDeviceManager for session tracking and\n * SignerManager for per-session signer caching.\n */\nexport class LedgerWebHidConnector implements IConnector {\n private _deviceManager: LedgerDeviceManager | null = null;\n private _signerManager: SignerManager | null = null;\n private _dmk: IDmk | null = null;\n\n private readonly _eventHandlers = new Map<\n ConnectorEventType,\n Set<EventHandler<ConnectorEventType>>\n >();\n\n private readonly _providedDmk: IDmk | undefined;\n\n constructor(options?: LedgerWebHidConnectorOptions) {\n this._providedDmk = options?.dmk;\n if (this._providedDmk) {\n this._initManagers(this._providedDmk);\n }\n }\n\n // ---------------------------------------------------------------------------\n // IConnector — Device discovery\n // ---------------------------------------------------------------------------\n\n async searchDevices(): Promise<ConnectorDevice[]> {\n const dm = await this._getDeviceManager();\n\n let descriptors = await dm.enumerate();\n\n // If no devices found, trigger browser permission dialog via startDiscovering\n if (descriptors.length === 0) {\n try {\n await dm.requestDevice();\n } catch {\n // User may cancel the permission dialog — that's OK\n }\n descriptors = await dm.enumerate();\n }\n\n return descriptors.map((d) => ({\n connectId: d.path,\n deviceId: d.path,\n name: d.type ?? 'Ledger',\n model: d.type,\n }));\n }\n\n // ---------------------------------------------------------------------------\n // IConnector — Connection\n // ---------------------------------------------------------------------------\n\n async connect(deviceId?: string): Promise<ConnectorSession> {\n const dm = await this._getDeviceManager();\n\n // Ensure we have fresh device list\n await this.searchDevices();\n\n let resolvedDeviceId = deviceId;\n\n if (!resolvedDeviceId) {\n // Use the first available device\n const descriptors = await dm.enumerate();\n if (descriptors.length === 0) {\n throw new Error(\n 'No Ledger device found. Make sure the device is connected via USB and unlocked.',\n );\n }\n resolvedDeviceId = descriptors[0].path;\n }\n\n try {\n const sessionId = await dm.connect(resolvedDeviceId);\n\n const session: ConnectorSession = {\n sessionId,\n deviceInfo: {\n vendor: 'ledger',\n model: 'unknown',\n firmwareVersion: 'unknown',\n deviceId: resolvedDeviceId,\n connectId: resolvedDeviceId,\n connectionType: 'usb',\n },\n };\n\n this._emit('device-connect', {\n device: {\n connectId: resolvedDeviceId,\n deviceId: resolvedDeviceId,\n name: 'Ledger',\n },\n });\n\n return session;\n } catch (err) {\n // Retry once: reset and re-discover\n this._resetManagers();\n const dm2 = await this._getDeviceManager();\n await this.searchDevices();\n\n const descriptors = await dm2.enumerate();\n const retryId = deviceId\n ? descriptors.find((d) => d.path === deviceId)?.path\n : descriptors[0]?.path;\n\n if (!retryId) {\n throw new Error(\n 'No Ledger device found after retry. Make sure the device is connected via USB and unlocked.',\n );\n }\n\n const sessionId = await dm2.connect(retryId);\n\n const session: ConnectorSession = {\n sessionId,\n deviceInfo: {\n vendor: 'ledger',\n model: 'unknown',\n firmwareVersion: 'unknown',\n deviceId: retryId,\n connectId: retryId,\n connectionType: 'usb',\n },\n };\n\n this._emit('device-connect', {\n device: {\n connectId: retryId,\n deviceId: retryId,\n name: 'Ledger',\n },\n });\n\n return session;\n }\n }\n\n async disconnect(sessionId: string): Promise<void> {\n if (!this._deviceManager) return;\n\n const deviceId = this._deviceManager.getDeviceId(sessionId);\n this._signerManager?.invalidate(sessionId);\n await this._deviceManager.disconnect(sessionId);\n\n if (deviceId) {\n this._emit('device-disconnect', { connectId: deviceId });\n }\n }\n\n // ---------------------------------------------------------------------------\n // IConnector — Method dispatch\n // ---------------------------------------------------------------------------\n\n async call(\n sessionId: string,\n method: string,\n params: unknown,\n ): Promise<unknown> {\n switch (method) {\n case 'evmGetAddress':\n return this._evmGetAddress(sessionId, params as EvmGetAddressCallParams);\n case 'evmSignTransaction':\n return this._evmSignTransaction(sessionId, params as EvmSignTransactionCallParams);\n case 'evmSignMessage':\n return this._evmSignMessage(sessionId, params as EvmSignMessageCallParams);\n case 'evmSignTypedData':\n return this._evmSignTypedData(sessionId, params as EvmSignTypedDataCallParams);\n case 'btcGetAddress':\n return this._btcGetAddress(sessionId, params as BtcGetAddressCallParams);\n case 'btcGetPublicKey':\n return this._btcGetPublicKey(sessionId, params as BtcGetPublicKeyCallParams);\n default:\n throw new Error(`LedgerWebHidConnector: unknown method \"${method}\"`);\n }\n }\n\n async cancel(_sessionId: string): Promise<void> {\n // Ledger DMK doesn't expose a generic cancel mechanism\n }\n\n uiResponse(_response: { type: string; payload: unknown }): void {\n // Ledger does not use interactive UI responses (PIN/passphrase)\n }\n\n // ---------------------------------------------------------------------------\n // IConnector — Events\n // ---------------------------------------------------------------------------\n\n on<K extends ConnectorEventType>(\n event: K,\n handler: (data: ConnectorEventMap[K]) => void,\n ): void {\n if (!this._eventHandlers.has(event)) {\n this._eventHandlers.set(event, new Set());\n }\n this._eventHandlers\n .get(event)!\n .add(handler as EventHandler<ConnectorEventType>);\n }\n\n off<K extends ConnectorEventType>(\n event: K,\n handler: (data: ConnectorEventMap[K]) => void,\n ): void {\n this._eventHandlers\n .get(event)\n ?.delete(handler as EventHandler<ConnectorEventType>);\n }\n\n // ---------------------------------------------------------------------------\n // IConnector — Reset\n // ---------------------------------------------------------------------------\n\n reset(): void {\n this._resetManagers();\n }\n\n // ---------------------------------------------------------------------------\n // Private — EVM methods\n // ---------------------------------------------------------------------------\n\n private async _evmGetAddress(\n sessionId: string,\n params: EvmGetAddressCallParams,\n ): Promise<{ address: string; publicKey?: string }> {\n const signer = await this._getEthSigner(sessionId);\n const path = normalizePath(params.path);\n\n try {\n const result = await signer.getAddress(path, {\n checkOnDevice: params.showOnDevice ?? false,\n });\n return { address: result.address, publicKey: result.publicKey };\n } catch (err) {\n this._invalidateSession(sessionId);\n throw this._wrapError(err);\n }\n }\n\n private async _evmSignTransaction(\n sessionId: string,\n params: EvmSignTransactionCallParams,\n ): Promise<{ v: string; r: string; s: string }> {\n const signer = await this._getEthSigner(sessionId);\n const path = normalizePath(params.path);\n\n try {\n const result: SignerEvmSignature = await signer.signTransaction(\n path,\n params.serializedTx,\n );\n return {\n v: `0x${result.v.toString(16)}`,\n r: padHex64(result.r),\n s: padHex64(result.s),\n };\n } catch (err) {\n this._invalidateSession(sessionId);\n throw this._wrapError(err);\n }\n }\n\n private async _evmSignMessage(\n sessionId: string,\n params: EvmSignMessageCallParams,\n ): Promise<{ signature: string }> {\n const signer = await this._getEthSigner(sessionId);\n const path = normalizePath(params.path);\n\n try {\n const result: SignerEvmSignature = await signer.signMessage(\n path,\n params.message,\n );\n const rHex = stripHex(result.r).padStart(64, '0');\n const sHex = stripHex(result.s).padStart(64, '0');\n const vHex = result.v.toString(16).padStart(2, '0');\n return { signature: `0x${rHex}${sHex}${vHex}` };\n } catch (err) {\n this._invalidateSession(sessionId);\n throw this._wrapError(err);\n }\n }\n\n private async _evmSignTypedData(\n sessionId: string,\n params: EvmSignTypedDataCallParams,\n ): Promise<{ signature: string }> {\n const signer = await this._getEthSigner(sessionId);\n const path = normalizePath(params.path);\n\n try {\n const result: SignerEvmSignature = await signer.signTypedData(\n path,\n params.data,\n );\n const rHex = stripHex(result.r).padStart(64, '0');\n const sHex = stripHex(result.s).padStart(64, '0');\n const vHex = result.v.toString(16).padStart(2, '0');\n return { signature: `0x${rHex}${sHex}${vHex}` };\n } catch (err) {\n this._invalidateSession(sessionId);\n throw this._wrapError(err);\n }\n }\n\n // ---------------------------------------------------------------------------\n // Private — BTC methods\n // ---------------------------------------------------------------------------\n\n private async _btcGetAddress(\n sessionId: string,\n params: BtcGetAddressCallParams,\n ): Promise<{ address: string; path: string }> {\n const btcSigner = await this._createBtcSigner(sessionId);\n const path = normalizePath(params.path);\n\n try {\n const { DefaultWallet, DefaultDescriptorTemplate } = await import(\n '@ledgerhq/device-signer-kit-bitcoin'\n );\n const purpose = path.split('/')[0]?.replace(\"'\", '');\n let template = DefaultDescriptorTemplate.NATIVE_SEGWIT;\n if (purpose === '44') template = DefaultDescriptorTemplate.LEGACY;\n else if (purpose === '49')\n template = DefaultDescriptorTemplate.NESTED_SEGWIT;\n else if (purpose === '86') template = DefaultDescriptorTemplate.TAPROOT;\n const wallet = new DefaultWallet(path, template);\n\n const result = await btcSigner.getWalletAddress(wallet, 0, {\n checkOnDevice: params.showOnDevice ?? false,\n change: false,\n });\n return { address: result.address, path: params.path };\n } catch (err) {\n this._invalidateSession(sessionId);\n throw this._wrapError(err);\n }\n }\n\n private async _btcGetPublicKey(\n sessionId: string,\n params: BtcGetPublicKeyCallParams,\n ): Promise<{ xpub: string; path: string }> {\n const btcSigner = await this._createBtcSigner(sessionId);\n const path = normalizePath(params.path);\n\n try {\n const xpub = await btcSigner.getExtendedPublicKey(path, {\n checkOnDevice: params.showOnDevice ?? false,\n });\n return { xpub, path: params.path };\n } catch (err) {\n this._invalidateSession(sessionId);\n throw this._wrapError(err);\n }\n }\n\n // ---------------------------------------------------------------------------\n // Private — DMK / Manager lifecycle\n // ---------------------------------------------------------------------------\n\n /**\n * Lazily create or return the DMK instance.\n * If a DMK was provided via constructor, it is used directly.\n * Otherwise, one is created via dynamic import of the Ledger SDK.\n */\n private async _getOrCreateDmk(): Promise<IDmk> {\n if (this._dmk) return this._dmk;\n\n if (this._providedDmk) {\n this._dmk = this._providedDmk;\n return this._dmk;\n }\n\n const { DeviceManagementKitBuilder } = await import(\n '@ledgerhq/device-management-kit'\n );\n const { webHidTransportFactory } = await import(\n '@ledgerhq/device-transport-kit-web-hid'\n );\n\n this._dmk = new DeviceManagementKitBuilder()\n .addTransport(webHidTransportFactory)\n .build() as unknown as IDmk;\n\n return this._dmk;\n }\n\n private _initManagers(dmk: IDmk): void {\n this._dmk = dmk;\n this._deviceManager = new LedgerDeviceManager(dmk);\n this._signerManager = new SignerManager(dmk);\n }\n\n private async _getDeviceManager(): Promise<LedgerDeviceManager> {\n if (this._deviceManager) return this._deviceManager;\n\n const dmk = await this._getOrCreateDmk();\n this._initManagers(dmk);\n return this._deviceManager!;\n }\n\n private async _getEthSigner(sessionId: string) {\n if (!this._signerManager) {\n const dmk = await this._getOrCreateDmk();\n this._initManagers(dmk);\n }\n return this._signerManager!.getOrCreate(sessionId);\n }\n\n private async _createBtcSigner(sessionId: string): Promise<SignerBtc> {\n const dmk = await this._getOrCreateDmk();\n const { SignerBtcBuilder } = await import(\n '@ledgerhq/device-signer-kit-bitcoin'\n );\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const sdkSigner = new SignerBtcBuilder({\n dmk: dmk as any,\n sessionId,\n }).build();\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n return new SignerBtc(sdkSigner as any);\n }\n\n private _invalidateSession(sessionId: string): void {\n this._signerManager?.invalidate(sessionId);\n }\n\n private _resetManagers(): void {\n this._signerManager?.clearAll();\n this._deviceManager?.dispose();\n this._deviceManager = null;\n this._signerManager = null;\n this._dmk = null;\n }\n\n // ---------------------------------------------------------------------------\n // Private — Events\n // ---------------------------------------------------------------------------\n\n private _emit<K extends ConnectorEventType>(\n event: K,\n data: ConnectorEventMap[K],\n ): void {\n const handlers = this._eventHandlers.get(event);\n if (handlers) {\n for (const handler of handlers) {\n try {\n handler(data);\n } catch {\n // Don't let listener errors break the connector\n }\n }\n }\n }\n\n // ---------------------------------------------------------------------------\n // Private — Error handling\n // ---------------------------------------------------------------------------\n\n private _wrapError(err: unknown): Error {\n const mapped = mapLedgerError(err);\n const error = new Error(mapped.message);\n (error as any).code = mapped.code;\n return error;\n }\n}\n"],"mappings":"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","names":[]}
package/dist/index.mjs ADDED
@@ -0,0 +1,365 @@
1
+ // src/LedgerWebHidConnector.ts
2
+ import {
3
+ LedgerDeviceManager,
4
+ SignerManager,
5
+ SignerBtc,
6
+ mapLedgerError
7
+ } from "@bytezhang/ledger-adapter";
8
+ function normalizePath(path) {
9
+ return path.startsWith("m/") ? path.slice(2) : path;
10
+ }
11
+ function stripHex(hex) {
12
+ return hex.startsWith("0x") ? hex.slice(2) : hex;
13
+ }
14
+ function padHex64(hex) {
15
+ return `0x${stripHex(hex).padStart(64, "0")}`;
16
+ }
17
+ var LedgerWebHidConnector = class {
18
+ constructor(options) {
19
+ this._deviceManager = null;
20
+ this._signerManager = null;
21
+ this._dmk = null;
22
+ this._eventHandlers = /* @__PURE__ */ new Map();
23
+ this._providedDmk = options?.dmk;
24
+ if (this._providedDmk) {
25
+ this._initManagers(this._providedDmk);
26
+ }
27
+ }
28
+ // ---------------------------------------------------------------------------
29
+ // IConnector — Device discovery
30
+ // ---------------------------------------------------------------------------
31
+ async searchDevices() {
32
+ const dm = await this._getDeviceManager();
33
+ let descriptors = await dm.enumerate();
34
+ if (descriptors.length === 0) {
35
+ try {
36
+ await dm.requestDevice();
37
+ } catch {
38
+ }
39
+ descriptors = await dm.enumerate();
40
+ }
41
+ return descriptors.map((d) => ({
42
+ connectId: d.path,
43
+ deviceId: d.path,
44
+ name: d.type ?? "Ledger",
45
+ model: d.type
46
+ }));
47
+ }
48
+ // ---------------------------------------------------------------------------
49
+ // IConnector — Connection
50
+ // ---------------------------------------------------------------------------
51
+ async connect(deviceId) {
52
+ const dm = await this._getDeviceManager();
53
+ await this.searchDevices();
54
+ let resolvedDeviceId = deviceId;
55
+ if (!resolvedDeviceId) {
56
+ const descriptors = await dm.enumerate();
57
+ if (descriptors.length === 0) {
58
+ throw new Error(
59
+ "No Ledger device found. Make sure the device is connected via USB and unlocked."
60
+ );
61
+ }
62
+ resolvedDeviceId = descriptors[0].path;
63
+ }
64
+ try {
65
+ const sessionId = await dm.connect(resolvedDeviceId);
66
+ const session = {
67
+ sessionId,
68
+ deviceInfo: {
69
+ vendor: "ledger",
70
+ model: "unknown",
71
+ firmwareVersion: "unknown",
72
+ deviceId: resolvedDeviceId,
73
+ connectId: resolvedDeviceId,
74
+ connectionType: "usb"
75
+ }
76
+ };
77
+ this._emit("device-connect", {
78
+ device: {
79
+ connectId: resolvedDeviceId,
80
+ deviceId: resolvedDeviceId,
81
+ name: "Ledger"
82
+ }
83
+ });
84
+ return session;
85
+ } catch (err) {
86
+ this._resetManagers();
87
+ const dm2 = await this._getDeviceManager();
88
+ await this.searchDevices();
89
+ const descriptors = await dm2.enumerate();
90
+ const retryId = deviceId ? descriptors.find((d) => d.path === deviceId)?.path : descriptors[0]?.path;
91
+ if (!retryId) {
92
+ throw new Error(
93
+ "No Ledger device found after retry. Make sure the device is connected via USB and unlocked."
94
+ );
95
+ }
96
+ const sessionId = await dm2.connect(retryId);
97
+ const session = {
98
+ sessionId,
99
+ deviceInfo: {
100
+ vendor: "ledger",
101
+ model: "unknown",
102
+ firmwareVersion: "unknown",
103
+ deviceId: retryId,
104
+ connectId: retryId,
105
+ connectionType: "usb"
106
+ }
107
+ };
108
+ this._emit("device-connect", {
109
+ device: {
110
+ connectId: retryId,
111
+ deviceId: retryId,
112
+ name: "Ledger"
113
+ }
114
+ });
115
+ return session;
116
+ }
117
+ }
118
+ async disconnect(sessionId) {
119
+ if (!this._deviceManager) return;
120
+ const deviceId = this._deviceManager.getDeviceId(sessionId);
121
+ this._signerManager?.invalidate(sessionId);
122
+ await this._deviceManager.disconnect(sessionId);
123
+ if (deviceId) {
124
+ this._emit("device-disconnect", { connectId: deviceId });
125
+ }
126
+ }
127
+ // ---------------------------------------------------------------------------
128
+ // IConnector — Method dispatch
129
+ // ---------------------------------------------------------------------------
130
+ async call(sessionId, method, params) {
131
+ switch (method) {
132
+ case "evmGetAddress":
133
+ return this._evmGetAddress(sessionId, params);
134
+ case "evmSignTransaction":
135
+ return this._evmSignTransaction(sessionId, params);
136
+ case "evmSignMessage":
137
+ return this._evmSignMessage(sessionId, params);
138
+ case "evmSignTypedData":
139
+ return this._evmSignTypedData(sessionId, params);
140
+ case "btcGetAddress":
141
+ return this._btcGetAddress(sessionId, params);
142
+ case "btcGetPublicKey":
143
+ return this._btcGetPublicKey(sessionId, params);
144
+ default:
145
+ throw new Error(`LedgerWebHidConnector: unknown method "${method}"`);
146
+ }
147
+ }
148
+ async cancel(_sessionId) {
149
+ }
150
+ uiResponse(_response) {
151
+ }
152
+ // ---------------------------------------------------------------------------
153
+ // IConnector — Events
154
+ // ---------------------------------------------------------------------------
155
+ on(event, handler) {
156
+ if (!this._eventHandlers.has(event)) {
157
+ this._eventHandlers.set(event, /* @__PURE__ */ new Set());
158
+ }
159
+ this._eventHandlers.get(event).add(handler);
160
+ }
161
+ off(event, handler) {
162
+ this._eventHandlers.get(event)?.delete(handler);
163
+ }
164
+ // ---------------------------------------------------------------------------
165
+ // IConnector — Reset
166
+ // ---------------------------------------------------------------------------
167
+ reset() {
168
+ this._resetManagers();
169
+ }
170
+ // ---------------------------------------------------------------------------
171
+ // Private — EVM methods
172
+ // ---------------------------------------------------------------------------
173
+ async _evmGetAddress(sessionId, params) {
174
+ const signer = await this._getEthSigner(sessionId);
175
+ const path = normalizePath(params.path);
176
+ try {
177
+ const result = await signer.getAddress(path, {
178
+ checkOnDevice: params.showOnDevice ?? false
179
+ });
180
+ return { address: result.address, publicKey: result.publicKey };
181
+ } catch (err) {
182
+ this._invalidateSession(sessionId);
183
+ throw this._wrapError(err);
184
+ }
185
+ }
186
+ async _evmSignTransaction(sessionId, params) {
187
+ const signer = await this._getEthSigner(sessionId);
188
+ const path = normalizePath(params.path);
189
+ try {
190
+ const result = await signer.signTransaction(
191
+ path,
192
+ params.serializedTx
193
+ );
194
+ return {
195
+ v: `0x${result.v.toString(16)}`,
196
+ r: padHex64(result.r),
197
+ s: padHex64(result.s)
198
+ };
199
+ } catch (err) {
200
+ this._invalidateSession(sessionId);
201
+ throw this._wrapError(err);
202
+ }
203
+ }
204
+ async _evmSignMessage(sessionId, params) {
205
+ const signer = await this._getEthSigner(sessionId);
206
+ const path = normalizePath(params.path);
207
+ try {
208
+ const result = await signer.signMessage(
209
+ path,
210
+ params.message
211
+ );
212
+ const rHex = stripHex(result.r).padStart(64, "0");
213
+ const sHex = stripHex(result.s).padStart(64, "0");
214
+ const vHex = result.v.toString(16).padStart(2, "0");
215
+ return { signature: `0x${rHex}${sHex}${vHex}` };
216
+ } catch (err) {
217
+ this._invalidateSession(sessionId);
218
+ throw this._wrapError(err);
219
+ }
220
+ }
221
+ async _evmSignTypedData(sessionId, params) {
222
+ const signer = await this._getEthSigner(sessionId);
223
+ const path = normalizePath(params.path);
224
+ try {
225
+ const result = await signer.signTypedData(
226
+ path,
227
+ params.data
228
+ );
229
+ const rHex = stripHex(result.r).padStart(64, "0");
230
+ const sHex = stripHex(result.s).padStart(64, "0");
231
+ const vHex = result.v.toString(16).padStart(2, "0");
232
+ return { signature: `0x${rHex}${sHex}${vHex}` };
233
+ } catch (err) {
234
+ this._invalidateSession(sessionId);
235
+ throw this._wrapError(err);
236
+ }
237
+ }
238
+ // ---------------------------------------------------------------------------
239
+ // Private — BTC methods
240
+ // ---------------------------------------------------------------------------
241
+ async _btcGetAddress(sessionId, params) {
242
+ const btcSigner = await this._createBtcSigner(sessionId);
243
+ const path = normalizePath(params.path);
244
+ try {
245
+ const { DefaultWallet, DefaultDescriptorTemplate } = await import("@ledgerhq/device-signer-kit-bitcoin");
246
+ const purpose = path.split("/")[0]?.replace("'", "");
247
+ let template = DefaultDescriptorTemplate.NATIVE_SEGWIT;
248
+ if (purpose === "44") template = DefaultDescriptorTemplate.LEGACY;
249
+ else if (purpose === "49")
250
+ template = DefaultDescriptorTemplate.NESTED_SEGWIT;
251
+ else if (purpose === "86") template = DefaultDescriptorTemplate.TAPROOT;
252
+ const wallet = new DefaultWallet(path, template);
253
+ const result = await btcSigner.getWalletAddress(wallet, 0, {
254
+ checkOnDevice: params.showOnDevice ?? false,
255
+ change: false
256
+ });
257
+ return { address: result.address, path: params.path };
258
+ } catch (err) {
259
+ this._invalidateSession(sessionId);
260
+ throw this._wrapError(err);
261
+ }
262
+ }
263
+ async _btcGetPublicKey(sessionId, params) {
264
+ const btcSigner = await this._createBtcSigner(sessionId);
265
+ const path = normalizePath(params.path);
266
+ try {
267
+ const xpub = await btcSigner.getExtendedPublicKey(path, {
268
+ checkOnDevice: params.showOnDevice ?? false
269
+ });
270
+ return { xpub, path: params.path };
271
+ } catch (err) {
272
+ this._invalidateSession(sessionId);
273
+ throw this._wrapError(err);
274
+ }
275
+ }
276
+ // ---------------------------------------------------------------------------
277
+ // Private — DMK / Manager lifecycle
278
+ // ---------------------------------------------------------------------------
279
+ /**
280
+ * Lazily create or return the DMK instance.
281
+ * If a DMK was provided via constructor, it is used directly.
282
+ * Otherwise, one is created via dynamic import of the Ledger SDK.
283
+ */
284
+ async _getOrCreateDmk() {
285
+ if (this._dmk) return this._dmk;
286
+ if (this._providedDmk) {
287
+ this._dmk = this._providedDmk;
288
+ return this._dmk;
289
+ }
290
+ const { DeviceManagementKitBuilder } = await import("@ledgerhq/device-management-kit");
291
+ const { webHidTransportFactory } = await import("@ledgerhq/device-transport-kit-web-hid");
292
+ this._dmk = new DeviceManagementKitBuilder().addTransport(webHidTransportFactory).build();
293
+ return this._dmk;
294
+ }
295
+ _initManagers(dmk) {
296
+ this._dmk = dmk;
297
+ this._deviceManager = new LedgerDeviceManager(dmk);
298
+ this._signerManager = new SignerManager(dmk);
299
+ }
300
+ async _getDeviceManager() {
301
+ if (this._deviceManager) return this._deviceManager;
302
+ const dmk = await this._getOrCreateDmk();
303
+ this._initManagers(dmk);
304
+ return this._deviceManager;
305
+ }
306
+ async _getEthSigner(sessionId) {
307
+ if (!this._signerManager) {
308
+ const dmk = await this._getOrCreateDmk();
309
+ this._initManagers(dmk);
310
+ }
311
+ return this._signerManager.getOrCreate(sessionId);
312
+ }
313
+ async _createBtcSigner(sessionId) {
314
+ const dmk = await this._getOrCreateDmk();
315
+ const { SignerBtcBuilder } = await import("@ledgerhq/device-signer-kit-bitcoin");
316
+ const sdkSigner = new SignerBtcBuilder({
317
+ dmk,
318
+ sessionId
319
+ }).build();
320
+ return new SignerBtc(sdkSigner);
321
+ }
322
+ _invalidateSession(sessionId) {
323
+ this._signerManager?.invalidate(sessionId);
324
+ }
325
+ _resetManagers() {
326
+ this._signerManager?.clearAll();
327
+ this._deviceManager?.dispose();
328
+ this._deviceManager = null;
329
+ this._signerManager = null;
330
+ this._dmk = null;
331
+ }
332
+ // ---------------------------------------------------------------------------
333
+ // Private — Events
334
+ // ---------------------------------------------------------------------------
335
+ _emit(event, data) {
336
+ const handlers = this._eventHandlers.get(event);
337
+ if (handlers) {
338
+ for (const handler of handlers) {
339
+ try {
340
+ handler(data);
341
+ } catch {
342
+ }
343
+ }
344
+ }
345
+ }
346
+ // ---------------------------------------------------------------------------
347
+ // Private — Error handling
348
+ // ---------------------------------------------------------------------------
349
+ _wrapError(err) {
350
+ const mapped = mapLedgerError(err);
351
+ const error = new Error(mapped.message);
352
+ error.code = mapped.code;
353
+ return error;
354
+ }
355
+ };
356
+
357
+ // src/index.ts
358
+ function createLedgerWebHidConnector(dmk) {
359
+ return new LedgerWebHidConnector({ dmk });
360
+ }
361
+ export {
362
+ LedgerWebHidConnector,
363
+ createLedgerWebHidConnector
364
+ };
365
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/LedgerWebHidConnector.ts","../src/index.ts"],"sourcesContent":["import type {\n IConnector,\n ConnectorDevice,\n ConnectorSession,\n ConnectorEventType,\n ConnectorEventMap,\n} from '@bytezhang/hardware-wallet-core';\nimport type { IDmk, SignerEvmSignature } from '@bytezhang/ledger-adapter';\nimport {\n LedgerDeviceManager,\n SignerManager,\n SignerBtc,\n mapLedgerError,\n} from '@bytezhang/ledger-adapter';\n\n// ---------------------------------------------------------------------------\n// Internal types\n// ---------------------------------------------------------------------------\n\ntype EventHandler<K extends ConnectorEventType> = (\n data: ConnectorEventMap[K],\n) => void;\n\n/** Parameters for evmGetAddress */\ninterface EvmGetAddressCallParams {\n path: string;\n showOnDevice?: boolean;\n}\n\n/** Parameters for evmSignTransaction */\ninterface EvmSignTransactionCallParams {\n path: string;\n /** RLP-serialized transaction hex (0x-prefixed or plain) */\n serializedTx: string;\n}\n\n/** Parameters for evmSignMessage */\ninterface EvmSignMessageCallParams {\n path: string;\n message: string;\n}\n\n/** Parameters for evmSignTypedData */\ninterface EvmSignTypedDataCallParams {\n path: string;\n data: unknown;\n}\n\n/** Parameters for btcGetAddress */\ninterface BtcGetAddressCallParams {\n path: string;\n showOnDevice?: boolean;\n}\n\n/** Parameters for btcGetPublicKey */\ninterface BtcGetPublicKeyCallParams {\n path: string;\n showOnDevice?: boolean;\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\n/** Strip the \"m/\" prefix from BIP-44 derivation paths. */\nfunction normalizePath(path: string): string {\n return path.startsWith('m/') ? path.slice(2) : path;\n}\n\n/** Remove `0x` prefix from a hex string if present. */\nfunction stripHex(hex: string): string {\n return hex.startsWith('0x') ? hex.slice(2) : hex;\n}\n\n/** Ensure a hex string is `0x`-prefixed and zero-padded to 64 hex chars (32 bytes). */\nfunction padHex64(hex: string): string {\n return `0x${stripHex(hex).padStart(64, '0')}`;\n}\n\n// ---------------------------------------------------------------------------\n// LedgerWebHidConnector\n// ---------------------------------------------------------------------------\n\nexport interface LedgerWebHidConnectorOptions {\n /**\n * Pre-built DMK instance. If not provided, a DMK will be created\n * lazily on first use via `@ledgerhq/device-management-kit` and\n * `@ledgerhq/device-transport-kit-web-hid`.\n */\n dmk?: IDmk;\n}\n\n/**\n * IConnector implementation for Ledger hardware wallets via WebHID.\n *\n * Wraps the Ledger DMK (Device Management Kit) and signer kits into\n * the unified IConnector interface used by the adapter layer.\n *\n * Design:\n * - Constructor accepts an optional pre-built DMK instance (or creates one lazily).\n * - searchDevices() uses DMK's listenToAvailableDevices() observable.\n * - connect() calls dmk.connect({ device }), returns a ConnectorSession.\n * - call() dispatches to chain-specific signer methods (EVM, BTC).\n * - Internally uses LedgerDeviceManager for session tracking and\n * SignerManager for per-session signer caching.\n */\nexport class LedgerWebHidConnector implements IConnector {\n private _deviceManager: LedgerDeviceManager | null = null;\n private _signerManager: SignerManager | null = null;\n private _dmk: IDmk | null = null;\n\n private readonly _eventHandlers = new Map<\n ConnectorEventType,\n Set<EventHandler<ConnectorEventType>>\n >();\n\n private readonly _providedDmk: IDmk | undefined;\n\n constructor(options?: LedgerWebHidConnectorOptions) {\n this._providedDmk = options?.dmk;\n if (this._providedDmk) {\n this._initManagers(this._providedDmk);\n }\n }\n\n // ---------------------------------------------------------------------------\n // IConnector — Device discovery\n // ---------------------------------------------------------------------------\n\n async searchDevices(): Promise<ConnectorDevice[]> {\n const dm = await this._getDeviceManager();\n\n let descriptors = await dm.enumerate();\n\n // If no devices found, trigger browser permission dialog via startDiscovering\n if (descriptors.length === 0) {\n try {\n await dm.requestDevice();\n } catch {\n // User may cancel the permission dialog — that's OK\n }\n descriptors = await dm.enumerate();\n }\n\n return descriptors.map((d) => ({\n connectId: d.path,\n deviceId: d.path,\n name: d.type ?? 'Ledger',\n model: d.type,\n }));\n }\n\n // ---------------------------------------------------------------------------\n // IConnector — Connection\n // ---------------------------------------------------------------------------\n\n async connect(deviceId?: string): Promise<ConnectorSession> {\n const dm = await this._getDeviceManager();\n\n // Ensure we have fresh device list\n await this.searchDevices();\n\n let resolvedDeviceId = deviceId;\n\n if (!resolvedDeviceId) {\n // Use the first available device\n const descriptors = await dm.enumerate();\n if (descriptors.length === 0) {\n throw new Error(\n 'No Ledger device found. Make sure the device is connected via USB and unlocked.',\n );\n }\n resolvedDeviceId = descriptors[0].path;\n }\n\n try {\n const sessionId = await dm.connect(resolvedDeviceId);\n\n const session: ConnectorSession = {\n sessionId,\n deviceInfo: {\n vendor: 'ledger',\n model: 'unknown',\n firmwareVersion: 'unknown',\n deviceId: resolvedDeviceId,\n connectId: resolvedDeviceId,\n connectionType: 'usb',\n },\n };\n\n this._emit('device-connect', {\n device: {\n connectId: resolvedDeviceId,\n deviceId: resolvedDeviceId,\n name: 'Ledger',\n },\n });\n\n return session;\n } catch (err) {\n // Retry once: reset and re-discover\n this._resetManagers();\n const dm2 = await this._getDeviceManager();\n await this.searchDevices();\n\n const descriptors = await dm2.enumerate();\n const retryId = deviceId\n ? descriptors.find((d) => d.path === deviceId)?.path\n : descriptors[0]?.path;\n\n if (!retryId) {\n throw new Error(\n 'No Ledger device found after retry. Make sure the device is connected via USB and unlocked.',\n );\n }\n\n const sessionId = await dm2.connect(retryId);\n\n const session: ConnectorSession = {\n sessionId,\n deviceInfo: {\n vendor: 'ledger',\n model: 'unknown',\n firmwareVersion: 'unknown',\n deviceId: retryId,\n connectId: retryId,\n connectionType: 'usb',\n },\n };\n\n this._emit('device-connect', {\n device: {\n connectId: retryId,\n deviceId: retryId,\n name: 'Ledger',\n },\n });\n\n return session;\n }\n }\n\n async disconnect(sessionId: string): Promise<void> {\n if (!this._deviceManager) return;\n\n const deviceId = this._deviceManager.getDeviceId(sessionId);\n this._signerManager?.invalidate(sessionId);\n await this._deviceManager.disconnect(sessionId);\n\n if (deviceId) {\n this._emit('device-disconnect', { connectId: deviceId });\n }\n }\n\n // ---------------------------------------------------------------------------\n // IConnector — Method dispatch\n // ---------------------------------------------------------------------------\n\n async call(\n sessionId: string,\n method: string,\n params: unknown,\n ): Promise<unknown> {\n switch (method) {\n case 'evmGetAddress':\n return this._evmGetAddress(sessionId, params as EvmGetAddressCallParams);\n case 'evmSignTransaction':\n return this._evmSignTransaction(sessionId, params as EvmSignTransactionCallParams);\n case 'evmSignMessage':\n return this._evmSignMessage(sessionId, params as EvmSignMessageCallParams);\n case 'evmSignTypedData':\n return this._evmSignTypedData(sessionId, params as EvmSignTypedDataCallParams);\n case 'btcGetAddress':\n return this._btcGetAddress(sessionId, params as BtcGetAddressCallParams);\n case 'btcGetPublicKey':\n return this._btcGetPublicKey(sessionId, params as BtcGetPublicKeyCallParams);\n default:\n throw new Error(`LedgerWebHidConnector: unknown method \"${method}\"`);\n }\n }\n\n async cancel(_sessionId: string): Promise<void> {\n // Ledger DMK doesn't expose a generic cancel mechanism\n }\n\n uiResponse(_response: { type: string; payload: unknown }): void {\n // Ledger does not use interactive UI responses (PIN/passphrase)\n }\n\n // ---------------------------------------------------------------------------\n // IConnector — Events\n // ---------------------------------------------------------------------------\n\n on<K extends ConnectorEventType>(\n event: K,\n handler: (data: ConnectorEventMap[K]) => void,\n ): void {\n if (!this._eventHandlers.has(event)) {\n this._eventHandlers.set(event, new Set());\n }\n this._eventHandlers\n .get(event)!\n .add(handler as EventHandler<ConnectorEventType>);\n }\n\n off<K extends ConnectorEventType>(\n event: K,\n handler: (data: ConnectorEventMap[K]) => void,\n ): void {\n this._eventHandlers\n .get(event)\n ?.delete(handler as EventHandler<ConnectorEventType>);\n }\n\n // ---------------------------------------------------------------------------\n // IConnector — Reset\n // ---------------------------------------------------------------------------\n\n reset(): void {\n this._resetManagers();\n }\n\n // ---------------------------------------------------------------------------\n // Private — EVM methods\n // ---------------------------------------------------------------------------\n\n private async _evmGetAddress(\n sessionId: string,\n params: EvmGetAddressCallParams,\n ): Promise<{ address: string; publicKey?: string }> {\n const signer = await this._getEthSigner(sessionId);\n const path = normalizePath(params.path);\n\n try {\n const result = await signer.getAddress(path, {\n checkOnDevice: params.showOnDevice ?? false,\n });\n return { address: result.address, publicKey: result.publicKey };\n } catch (err) {\n this._invalidateSession(sessionId);\n throw this._wrapError(err);\n }\n }\n\n private async _evmSignTransaction(\n sessionId: string,\n params: EvmSignTransactionCallParams,\n ): Promise<{ v: string; r: string; s: string }> {\n const signer = await this._getEthSigner(sessionId);\n const path = normalizePath(params.path);\n\n try {\n const result: SignerEvmSignature = await signer.signTransaction(\n path,\n params.serializedTx,\n );\n return {\n v: `0x${result.v.toString(16)}`,\n r: padHex64(result.r),\n s: padHex64(result.s),\n };\n } catch (err) {\n this._invalidateSession(sessionId);\n throw this._wrapError(err);\n }\n }\n\n private async _evmSignMessage(\n sessionId: string,\n params: EvmSignMessageCallParams,\n ): Promise<{ signature: string }> {\n const signer = await this._getEthSigner(sessionId);\n const path = normalizePath(params.path);\n\n try {\n const result: SignerEvmSignature = await signer.signMessage(\n path,\n params.message,\n );\n const rHex = stripHex(result.r).padStart(64, '0');\n const sHex = stripHex(result.s).padStart(64, '0');\n const vHex = result.v.toString(16).padStart(2, '0');\n return { signature: `0x${rHex}${sHex}${vHex}` };\n } catch (err) {\n this._invalidateSession(sessionId);\n throw this._wrapError(err);\n }\n }\n\n private async _evmSignTypedData(\n sessionId: string,\n params: EvmSignTypedDataCallParams,\n ): Promise<{ signature: string }> {\n const signer = await this._getEthSigner(sessionId);\n const path = normalizePath(params.path);\n\n try {\n const result: SignerEvmSignature = await signer.signTypedData(\n path,\n params.data,\n );\n const rHex = stripHex(result.r).padStart(64, '0');\n const sHex = stripHex(result.s).padStart(64, '0');\n const vHex = result.v.toString(16).padStart(2, '0');\n return { signature: `0x${rHex}${sHex}${vHex}` };\n } catch (err) {\n this._invalidateSession(sessionId);\n throw this._wrapError(err);\n }\n }\n\n // ---------------------------------------------------------------------------\n // Private — BTC methods\n // ---------------------------------------------------------------------------\n\n private async _btcGetAddress(\n sessionId: string,\n params: BtcGetAddressCallParams,\n ): Promise<{ address: string; path: string }> {\n const btcSigner = await this._createBtcSigner(sessionId);\n const path = normalizePath(params.path);\n\n try {\n const { DefaultWallet, DefaultDescriptorTemplate } = await import(\n '@ledgerhq/device-signer-kit-bitcoin'\n );\n const purpose = path.split('/')[0]?.replace(\"'\", '');\n let template = DefaultDescriptorTemplate.NATIVE_SEGWIT;\n if (purpose === '44') template = DefaultDescriptorTemplate.LEGACY;\n else if (purpose === '49')\n template = DefaultDescriptorTemplate.NESTED_SEGWIT;\n else if (purpose === '86') template = DefaultDescriptorTemplate.TAPROOT;\n const wallet = new DefaultWallet(path, template);\n\n const result = await btcSigner.getWalletAddress(wallet, 0, {\n checkOnDevice: params.showOnDevice ?? false,\n change: false,\n });\n return { address: result.address, path: params.path };\n } catch (err) {\n this._invalidateSession(sessionId);\n throw this._wrapError(err);\n }\n }\n\n private async _btcGetPublicKey(\n sessionId: string,\n params: BtcGetPublicKeyCallParams,\n ): Promise<{ xpub: string; path: string }> {\n const btcSigner = await this._createBtcSigner(sessionId);\n const path = normalizePath(params.path);\n\n try {\n const xpub = await btcSigner.getExtendedPublicKey(path, {\n checkOnDevice: params.showOnDevice ?? false,\n });\n return { xpub, path: params.path };\n } catch (err) {\n this._invalidateSession(sessionId);\n throw this._wrapError(err);\n }\n }\n\n // ---------------------------------------------------------------------------\n // Private — DMK / Manager lifecycle\n // ---------------------------------------------------------------------------\n\n /**\n * Lazily create or return the DMK instance.\n * If a DMK was provided via constructor, it is used directly.\n * Otherwise, one is created via dynamic import of the Ledger SDK.\n */\n private async _getOrCreateDmk(): Promise<IDmk> {\n if (this._dmk) return this._dmk;\n\n if (this._providedDmk) {\n this._dmk = this._providedDmk;\n return this._dmk;\n }\n\n const { DeviceManagementKitBuilder } = await import(\n '@ledgerhq/device-management-kit'\n );\n const { webHidTransportFactory } = await import(\n '@ledgerhq/device-transport-kit-web-hid'\n );\n\n this._dmk = new DeviceManagementKitBuilder()\n .addTransport(webHidTransportFactory)\n .build() as unknown as IDmk;\n\n return this._dmk;\n }\n\n private _initManagers(dmk: IDmk): void {\n this._dmk = dmk;\n this._deviceManager = new LedgerDeviceManager(dmk);\n this._signerManager = new SignerManager(dmk);\n }\n\n private async _getDeviceManager(): Promise<LedgerDeviceManager> {\n if (this._deviceManager) return this._deviceManager;\n\n const dmk = await this._getOrCreateDmk();\n this._initManagers(dmk);\n return this._deviceManager!;\n }\n\n private async _getEthSigner(sessionId: string) {\n if (!this._signerManager) {\n const dmk = await this._getOrCreateDmk();\n this._initManagers(dmk);\n }\n return this._signerManager!.getOrCreate(sessionId);\n }\n\n private async _createBtcSigner(sessionId: string): Promise<SignerBtc> {\n const dmk = await this._getOrCreateDmk();\n const { SignerBtcBuilder } = await import(\n '@ledgerhq/device-signer-kit-bitcoin'\n );\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const sdkSigner = new SignerBtcBuilder({\n dmk: dmk as any,\n sessionId,\n }).build();\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n return new SignerBtc(sdkSigner as any);\n }\n\n private _invalidateSession(sessionId: string): void {\n this._signerManager?.invalidate(sessionId);\n }\n\n private _resetManagers(): void {\n this._signerManager?.clearAll();\n this._deviceManager?.dispose();\n this._deviceManager = null;\n this._signerManager = null;\n this._dmk = null;\n }\n\n // ---------------------------------------------------------------------------\n // Private — Events\n // ---------------------------------------------------------------------------\n\n private _emit<K extends ConnectorEventType>(\n event: K,\n data: ConnectorEventMap[K],\n ): void {\n const handlers = this._eventHandlers.get(event);\n if (handlers) {\n for (const handler of handlers) {\n try {\n handler(data);\n } catch {\n // Don't let listener errors break the connector\n }\n }\n }\n }\n\n // ---------------------------------------------------------------------------\n // Private — Error handling\n // ---------------------------------------------------------------------------\n\n private _wrapError(err: unknown): Error {\n const mapped = mapLedgerError(err);\n const error = new Error(mapped.message);\n (error as any).code = mapped.code;\n return error;\n }\n}\n","import type { IConnector } from '@bytezhang/hardware-wallet-core';\nimport type { IDmk } from '@bytezhang/ledger-adapter';\n\nimport {\n LedgerWebHidConnector,\n} from './LedgerWebHidConnector';\nimport type { LedgerWebHidConnectorOptions } from './LedgerWebHidConnector';\n\nexport { LedgerWebHidConnector };\nexport type { LedgerWebHidConnectorOptions };\n\n/**\n * Create a LedgerWebHidConnector.\n *\n * @param dmk - Optional pre-built DMK instance. If omitted, the connector\n * will lazily create one using `@ledgerhq/device-management-kit`\n * and `@ledgerhq/device-transport-kit-web-hid`.\n */\nexport function createLedgerWebHidConnector(dmk?: IDmk): IConnector {\n return new LedgerWebHidConnector({ dmk });\n}\n"],"mappings":"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","names":[]}
package/package.json ADDED
@@ -0,0 +1,58 @@
1
+ {
2
+ "name": "@bytezhang/hardware-ledger-connector-webhid",
3
+ "version": "0.0.1",
4
+ "description": "IConnector implementation for Ledger hardware wallets via WebHID (DMK)",
5
+ "author": "OneKey",
6
+ "license": "MIT",
7
+ "main": "dist/index.js",
8
+ "module": "dist/index.mjs",
9
+ "types": "dist/index.d.ts",
10
+ "exports": {
11
+ ".": {
12
+ "import": { "types": "./dist/index.d.mts", "default": "./dist/index.mjs" },
13
+ "require": { "types": "./dist/index.d.ts", "default": "./dist/index.js" }
14
+ }
15
+ },
16
+ "files": ["dist"],
17
+ "scripts": {
18
+ "build": "tsup",
19
+ "dev": "tsup --watch",
20
+ "clean": "rimraf dist",
21
+ "test": "vitest run"
22
+ },
23
+ "publishConfig": {
24
+ "access": "public"
25
+ },
26
+ "repository": {
27
+ "type": "git",
28
+ "url": "https://github.com/OneKeyHQ/hardware-wallet-kit.git",
29
+ "directory": "packages/ledger-connector-webhid"
30
+ },
31
+ "keywords": [
32
+ "ledger",
33
+ "webhid",
34
+ "hardware-wallet",
35
+ "onekey",
36
+ "connector"
37
+ ],
38
+ "dependencies": {
39
+ "@bytezhang/hardware-wallet-core": "0.0.1",
40
+ "@bytezhang/ledger-adapter": "0.0.1"
41
+ },
42
+ "peerDependencies": {
43
+ "@ledgerhq/device-management-kit": "^1.1.0",
44
+ "@ledgerhq/device-signer-kit-ethereum": "^1.9.0",
45
+ "@ledgerhq/device-signer-kit-bitcoin": "^1.0.0",
46
+ "@ledgerhq/device-transport-kit-web-hid": "^1.0.0"
47
+ },
48
+ "peerDependenciesMeta": {
49
+ "@ledgerhq/device-signer-kit-bitcoin": { "optional": true }
50
+ },
51
+ "devDependencies": {
52
+ "@ledgerhq/device-management-kit": "^1.1.0",
53
+ "@ledgerhq/device-signer-kit-ethereum": "^1.9.0",
54
+ "@ledgerhq/device-signer-kit-bitcoin": "^1.0.0",
55
+ "@ledgerhq/device-transport-kit-web-hid": "^1.0.0",
56
+ "rimraf": "^5.0.0"
57
+ }
58
+ }