@bytezhang/hardware-ledger-connector-webhid 0.0.9 → 0.0.11
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/dist/index.d.mts +37 -0
- package/dist/index.d.ts +37 -0
- package/dist/index.js +14 -418
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +15 -425
- package/dist/index.mjs.map +1 -1
- package/package.json +3 -3
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { DeviceDescriptor, IConnector } from '@bytezhang/hardware-wallet-core';
|
|
2
|
+
import { LedgerConnectorBase, 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
|
+
* Extends LedgerConnectorBase with the WebHID transport factory.
|
|
16
|
+
* Overrides connectId resolution to handle BLE devices that may appear
|
|
17
|
+
* via a WebHID+BLE combo transport.
|
|
18
|
+
*/
|
|
19
|
+
declare class LedgerWebHidConnector extends LedgerConnectorBase {
|
|
20
|
+
constructor(options?: LedgerWebHidConnectorOptions);
|
|
21
|
+
/**
|
|
22
|
+
* Override connectId resolution for BLE devices discovered via WebHID+BLE combo.
|
|
23
|
+
* For USB devices, the DMK path (ephemeral UUID) is used as-is.
|
|
24
|
+
*/
|
|
25
|
+
protected _resolveConnectId(descriptor: DeviceDescriptor): string;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Create a LedgerWebHidConnector.
|
|
30
|
+
*
|
|
31
|
+
* @param dmk - Optional pre-built DMK instance. If omitted, the connector
|
|
32
|
+
* will lazily create one using `@ledgerhq/device-management-kit`
|
|
33
|
+
* and `@ledgerhq/device-transport-kit-web-hid`.
|
|
34
|
+
*/
|
|
35
|
+
declare function createLedgerWebHidConnector(dmk?: IDmk): IConnector;
|
|
36
|
+
|
|
37
|
+
export { LedgerWebHidConnector, type LedgerWebHidConnectorOptions, createLedgerWebHidConnector };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { DeviceDescriptor, IConnector } from '@bytezhang/hardware-wallet-core';
|
|
2
|
+
import { LedgerConnectorBase, 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
|
+
* Extends LedgerConnectorBase with the WebHID transport factory.
|
|
16
|
+
* Overrides connectId resolution to handle BLE devices that may appear
|
|
17
|
+
* via a WebHID+BLE combo transport.
|
|
18
|
+
*/
|
|
19
|
+
declare class LedgerWebHidConnector extends LedgerConnectorBase {
|
|
20
|
+
constructor(options?: LedgerWebHidConnectorOptions);
|
|
21
|
+
/**
|
|
22
|
+
* Override connectId resolution for BLE devices discovered via WebHID+BLE combo.
|
|
23
|
+
* For USB devices, the DMK path (ephemeral UUID) is used as-is.
|
|
24
|
+
*/
|
|
25
|
+
protected _resolveConnectId(descriptor: DeviceDescriptor): string;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Create a LedgerWebHidConnector.
|
|
30
|
+
*
|
|
31
|
+
* @param dmk - Optional pre-built DMK instance. If omitted, the connector
|
|
32
|
+
* will lazily create one using `@ledgerhq/device-management-kit`
|
|
33
|
+
* and `@ledgerhq/device-transport-kit-web-hid`.
|
|
34
|
+
*/
|
|
35
|
+
declare function createLedgerWebHidConnector(dmk?: IDmk): IConnector;
|
|
36
|
+
|
|
37
|
+
export { LedgerWebHidConnector, type LedgerWebHidConnectorOptions, createLedgerWebHidConnector };
|
package/dist/index.js
CHANGED
|
@@ -42,429 +42,25 @@ function extractBleHexId(name) {
|
|
|
42
42
|
const match = name.match(/\b([0-9A-Fa-f]{4})$/);
|
|
43
43
|
return match ? match[1].toUpperCase() : void 0;
|
|
44
44
|
}
|
|
45
|
-
|
|
46
|
-
return path.startsWith("m/") ? path.slice(2) : path;
|
|
47
|
-
}
|
|
48
|
-
function stripHex(hex) {
|
|
49
|
-
return hex.startsWith("0x") ? hex.slice(2) : hex;
|
|
50
|
-
}
|
|
51
|
-
function padHex64(hex) {
|
|
52
|
-
return `0x${stripHex(hex).padStart(64, "0")}`;
|
|
53
|
-
}
|
|
54
|
-
var LedgerWebHidConnector = class {
|
|
45
|
+
var LedgerWebHidConnector = class extends import_ledger_adapter.LedgerConnectorBase {
|
|
55
46
|
constructor(options) {
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
// ---------------------------------------------------------------------------
|
|
66
|
-
// IConnector — Device discovery
|
|
67
|
-
// ---------------------------------------------------------------------------
|
|
68
|
-
async searchDevices() {
|
|
69
|
-
const dm = await this._getDeviceManager();
|
|
70
|
-
let descriptors = await dm.enumerate();
|
|
71
|
-
if (descriptors.length === 0) {
|
|
72
|
-
try {
|
|
73
|
-
await dm.requestDevice();
|
|
74
|
-
} catch {
|
|
75
|
-
}
|
|
76
|
-
descriptors = await dm.enumerate();
|
|
77
|
-
}
|
|
78
|
-
const result = descriptors.map((d) => {
|
|
79
|
-
const isBle = d.transport === "BLE";
|
|
80
|
-
const bleHexId = isBle ? extractBleHexId(d.name) : void 0;
|
|
81
|
-
return {
|
|
82
|
-
connectId: bleHexId || d.path,
|
|
83
|
-
deviceId: d.path,
|
|
84
|
-
name: d.name || d.type || "Ledger",
|
|
85
|
-
model: d.type
|
|
86
|
-
};
|
|
87
|
-
});
|
|
88
|
-
return result;
|
|
89
|
-
}
|
|
90
|
-
// ---------------------------------------------------------------------------
|
|
91
|
-
// IConnector — Connection
|
|
92
|
-
// ---------------------------------------------------------------------------
|
|
93
|
-
async connect(deviceId) {
|
|
94
|
-
const dm = await this._getDeviceManager();
|
|
95
|
-
await this.searchDevices();
|
|
96
|
-
let resolvedDeviceId = deviceId;
|
|
97
|
-
if (!resolvedDeviceId) {
|
|
98
|
-
const descriptors = await dm.enumerate();
|
|
99
|
-
if (descriptors.length === 0) {
|
|
100
|
-
throw new Error(
|
|
101
|
-
"No Ledger device found. Make sure the device is connected via USB and unlocked."
|
|
102
|
-
);
|
|
103
|
-
}
|
|
104
|
-
resolvedDeviceId = descriptors[0].path;
|
|
105
|
-
}
|
|
106
|
-
try {
|
|
107
|
-
const sessionId = await dm.connect(resolvedDeviceId);
|
|
108
|
-
const session = {
|
|
109
|
-
sessionId,
|
|
110
|
-
deviceInfo: {
|
|
111
|
-
vendor: "ledger",
|
|
112
|
-
model: "unknown",
|
|
113
|
-
firmwareVersion: "unknown",
|
|
114
|
-
deviceId: resolvedDeviceId,
|
|
115
|
-
connectId: resolvedDeviceId,
|
|
116
|
-
connectionType: "usb",
|
|
117
|
-
capabilities: {
|
|
118
|
-
persistentDeviceIdentity: false
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
};
|
|
122
|
-
this._emit("device-connect", {
|
|
123
|
-
device: {
|
|
124
|
-
connectId: resolvedDeviceId,
|
|
125
|
-
deviceId: resolvedDeviceId,
|
|
126
|
-
name: "Ledger"
|
|
127
|
-
}
|
|
128
|
-
});
|
|
129
|
-
return session;
|
|
130
|
-
} catch (err) {
|
|
131
|
-
this._resetManagers();
|
|
132
|
-
const dm2 = await this._getDeviceManager();
|
|
133
|
-
await this.searchDevices();
|
|
134
|
-
const descriptors = await dm2.enumerate();
|
|
135
|
-
const retryId = deviceId ? descriptors.find((d) => d.path === deviceId)?.path : descriptors[0]?.path;
|
|
136
|
-
if (!retryId) {
|
|
137
|
-
throw new Error(
|
|
138
|
-
"No Ledger device found after retry. Make sure the device is connected via USB and unlocked."
|
|
139
|
-
);
|
|
140
|
-
}
|
|
141
|
-
const sessionId = await dm2.connect(retryId);
|
|
142
|
-
const session = {
|
|
143
|
-
sessionId,
|
|
144
|
-
deviceInfo: {
|
|
145
|
-
vendor: "ledger",
|
|
146
|
-
model: "unknown",
|
|
147
|
-
firmwareVersion: "unknown",
|
|
148
|
-
deviceId: retryId,
|
|
149
|
-
connectId: retryId,
|
|
150
|
-
connectionType: "usb"
|
|
151
|
-
}
|
|
152
|
-
};
|
|
153
|
-
this._emit("device-connect", {
|
|
154
|
-
device: {
|
|
155
|
-
connectId: retryId,
|
|
156
|
-
deviceId: retryId,
|
|
157
|
-
name: "Ledger"
|
|
158
|
-
}
|
|
159
|
-
});
|
|
160
|
-
return session;
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
async disconnect(sessionId) {
|
|
164
|
-
if (!this._deviceManager) return;
|
|
165
|
-
const deviceId = this._deviceManager.getDeviceId(sessionId);
|
|
166
|
-
this._signerManager?.invalidate(sessionId);
|
|
167
|
-
await this._deviceManager.disconnect(sessionId);
|
|
168
|
-
if (deviceId) {
|
|
169
|
-
this._emit("device-disconnect", { connectId: deviceId });
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
// ---------------------------------------------------------------------------
|
|
173
|
-
// IConnector — Method dispatch
|
|
174
|
-
// ---------------------------------------------------------------------------
|
|
175
|
-
async call(sessionId, method, params) {
|
|
176
|
-
switch (method) {
|
|
177
|
-
case "evmGetAddress":
|
|
178
|
-
return this._evmGetAddress(sessionId, params);
|
|
179
|
-
case "evmSignTransaction":
|
|
180
|
-
return this._evmSignTransaction(sessionId, params);
|
|
181
|
-
case "evmSignMessage":
|
|
182
|
-
return this._evmSignMessage(sessionId, params);
|
|
183
|
-
case "evmSignTypedData":
|
|
184
|
-
return this._evmSignTypedData(sessionId, params);
|
|
185
|
-
case "btcGetAddress":
|
|
186
|
-
return this._btcGetAddress(sessionId, params);
|
|
187
|
-
case "btcGetPublicKey":
|
|
188
|
-
return this._btcGetPublicKey(sessionId, params);
|
|
189
|
-
case "btcSignTransaction":
|
|
190
|
-
return this._btcSignTransaction(sessionId, params);
|
|
191
|
-
case "btcSignMessage":
|
|
192
|
-
return this._btcSignMessage(sessionId, params);
|
|
193
|
-
case "btcGetMasterFingerprint":
|
|
194
|
-
return this._btcGetMasterFingerprint(sessionId, params);
|
|
195
|
-
case "solGetAddress":
|
|
196
|
-
return this._solGetAddress(sessionId, params);
|
|
197
|
-
case "solSignTransaction":
|
|
198
|
-
return this._solSignTransaction(sessionId, params);
|
|
199
|
-
case "solSignMessage":
|
|
200
|
-
return this._solSignMessage(sessionId, params);
|
|
201
|
-
default:
|
|
202
|
-
throw new Error(`LedgerWebHidConnector: unknown method "${method}"`);
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
async cancel(_sessionId) {
|
|
206
|
-
}
|
|
207
|
-
uiResponse(_response) {
|
|
208
|
-
}
|
|
209
|
-
// ---------------------------------------------------------------------------
|
|
210
|
-
// IConnector — Events
|
|
211
|
-
// ---------------------------------------------------------------------------
|
|
212
|
-
on(event, handler) {
|
|
213
|
-
if (!this._eventHandlers.has(event)) {
|
|
214
|
-
this._eventHandlers.set(event, /* @__PURE__ */ new Set());
|
|
215
|
-
}
|
|
216
|
-
this._eventHandlers.get(event).add(handler);
|
|
217
|
-
}
|
|
218
|
-
off(event, handler) {
|
|
219
|
-
this._eventHandlers.get(event)?.delete(handler);
|
|
220
|
-
}
|
|
221
|
-
// ---------------------------------------------------------------------------
|
|
222
|
-
// IConnector — Reset
|
|
223
|
-
// ---------------------------------------------------------------------------
|
|
224
|
-
reset() {
|
|
225
|
-
this._resetManagers();
|
|
226
|
-
}
|
|
227
|
-
// ---------------------------------------------------------------------------
|
|
228
|
-
// Private — EVM methods
|
|
229
|
-
// ---------------------------------------------------------------------------
|
|
230
|
-
async _evmGetAddress(sessionId, params) {
|
|
231
|
-
const signer = await this._getEthSigner(sessionId);
|
|
232
|
-
const path = normalizePath(params.path);
|
|
233
|
-
try {
|
|
234
|
-
const result = await signer.getAddress(path, {
|
|
235
|
-
checkOnDevice: params.showOnDevice ?? false
|
|
236
|
-
});
|
|
237
|
-
return { address: result.address, publicKey: result.publicKey };
|
|
238
|
-
} catch (err) {
|
|
239
|
-
this._invalidateSession(sessionId);
|
|
240
|
-
throw this._wrapError(err);
|
|
241
|
-
}
|
|
242
|
-
}
|
|
243
|
-
async _evmSignTransaction(sessionId, params) {
|
|
244
|
-
const signer = await this._getEthSigner(sessionId);
|
|
245
|
-
const path = normalizePath(params.path);
|
|
246
|
-
try {
|
|
247
|
-
const result = await signer.signTransaction(
|
|
248
|
-
path,
|
|
249
|
-
params.serializedTx
|
|
250
|
-
);
|
|
251
|
-
return {
|
|
252
|
-
v: `0x${result.v.toString(16)}`,
|
|
253
|
-
r: padHex64(result.r),
|
|
254
|
-
s: padHex64(result.s)
|
|
255
|
-
};
|
|
256
|
-
} catch (err) {
|
|
257
|
-
this._invalidateSession(sessionId);
|
|
258
|
-
throw this._wrapError(err);
|
|
259
|
-
}
|
|
260
|
-
}
|
|
261
|
-
async _evmSignMessage(sessionId, params) {
|
|
262
|
-
const signer = await this._getEthSigner(sessionId);
|
|
263
|
-
const path = normalizePath(params.path);
|
|
264
|
-
try {
|
|
265
|
-
const result = await signer.signMessage(
|
|
266
|
-
path,
|
|
267
|
-
params.message
|
|
268
|
-
);
|
|
269
|
-
const rHex = stripHex(result.r).padStart(64, "0");
|
|
270
|
-
const sHex = stripHex(result.s).padStart(64, "0");
|
|
271
|
-
const vHex = result.v.toString(16).padStart(2, "0");
|
|
272
|
-
return { signature: `0x${rHex}${sHex}${vHex}` };
|
|
273
|
-
} catch (err) {
|
|
274
|
-
this._invalidateSession(sessionId);
|
|
275
|
-
throw this._wrapError(err);
|
|
276
|
-
}
|
|
47
|
+
super(
|
|
48
|
+
async () => {
|
|
49
|
+
const { webHidTransportFactory } = await import("@ledgerhq/device-transport-kit-web-hid");
|
|
50
|
+
return webHidTransportFactory;
|
|
51
|
+
},
|
|
52
|
+
{ connectionType: "usb", dmk: options?.dmk }
|
|
53
|
+
);
|
|
277
54
|
}
|
|
278
|
-
async _evmSignTypedData(sessionId, params) {
|
|
279
|
-
const signer = await this._getEthSigner(sessionId);
|
|
280
|
-
const path = normalizePath(params.path);
|
|
281
|
-
try {
|
|
282
|
-
const result = await signer.signTypedData(
|
|
283
|
-
path,
|
|
284
|
-
params.data
|
|
285
|
-
);
|
|
286
|
-
const rHex = stripHex(result.r).padStart(64, "0");
|
|
287
|
-
const sHex = stripHex(result.s).padStart(64, "0");
|
|
288
|
-
const vHex = result.v.toString(16).padStart(2, "0");
|
|
289
|
-
return { signature: `0x${rHex}${sHex}${vHex}` };
|
|
290
|
-
} catch (err) {
|
|
291
|
-
this._invalidateSession(sessionId);
|
|
292
|
-
throw this._wrapError(err);
|
|
293
|
-
}
|
|
294
|
-
}
|
|
295
|
-
// ---------------------------------------------------------------------------
|
|
296
|
-
// Private — BTC methods
|
|
297
|
-
// ---------------------------------------------------------------------------
|
|
298
|
-
async _btcGetAddress(sessionId, params) {
|
|
299
|
-
const btcSigner = await this._createBtcSigner(sessionId);
|
|
300
|
-
const path = normalizePath(params.path);
|
|
301
|
-
try {
|
|
302
|
-
const { DefaultWallet, DefaultDescriptorTemplate } = await import("@ledgerhq/device-signer-kit-bitcoin");
|
|
303
|
-
const purpose = path.split("/")[0]?.replace("'", "");
|
|
304
|
-
let template = DefaultDescriptorTemplate.NATIVE_SEGWIT;
|
|
305
|
-
if (purpose === "44") template = DefaultDescriptorTemplate.LEGACY;
|
|
306
|
-
else if (purpose === "49")
|
|
307
|
-
template = DefaultDescriptorTemplate.NESTED_SEGWIT;
|
|
308
|
-
else if (purpose === "86") template = DefaultDescriptorTemplate.TAPROOT;
|
|
309
|
-
const wallet = new DefaultWallet(path, template);
|
|
310
|
-
const result = await btcSigner.getWalletAddress(wallet, 0, {
|
|
311
|
-
checkOnDevice: params.showOnDevice ?? false,
|
|
312
|
-
change: false
|
|
313
|
-
});
|
|
314
|
-
return { address: result.address, path: params.path };
|
|
315
|
-
} catch (err) {
|
|
316
|
-
this._invalidateSession(sessionId);
|
|
317
|
-
throw this._wrapError(err);
|
|
318
|
-
}
|
|
319
|
-
}
|
|
320
|
-
async _btcGetPublicKey(sessionId, params) {
|
|
321
|
-
const btcSigner = await this._createBtcSigner(sessionId);
|
|
322
|
-
const path = normalizePath(params.path);
|
|
323
|
-
try {
|
|
324
|
-
const xpub = await btcSigner.getExtendedPublicKey(path, {
|
|
325
|
-
checkOnDevice: params.showOnDevice ?? false
|
|
326
|
-
});
|
|
327
|
-
return { xpub, path: params.path };
|
|
328
|
-
} catch (err) {
|
|
329
|
-
this._invalidateSession(sessionId);
|
|
330
|
-
throw this._wrapError(err);
|
|
331
|
-
}
|
|
332
|
-
}
|
|
333
|
-
async _btcSignTransaction(_sessionId, _params) {
|
|
334
|
-
throw new Error("LedgerWebHidConnector: btcSignTransaction is not yet implemented");
|
|
335
|
-
}
|
|
336
|
-
async _btcSignMessage(_sessionId, _params) {
|
|
337
|
-
throw new Error("LedgerWebHidConnector: btcSignMessage is not yet implemented");
|
|
338
|
-
}
|
|
339
|
-
async _btcGetMasterFingerprint(sessionId, params) {
|
|
340
|
-
const btcSigner = await this._createBtcSigner(sessionId);
|
|
341
|
-
try {
|
|
342
|
-
const fingerprint = await btcSigner.getMasterFingerprint({
|
|
343
|
-
skipOpenApp: params?.skipOpenApp
|
|
344
|
-
});
|
|
345
|
-
const hex = Array.from(fingerprint).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
346
|
-
return { masterFingerprint: hex };
|
|
347
|
-
} catch (err) {
|
|
348
|
-
this._invalidateSession(sessionId);
|
|
349
|
-
throw this._wrapError(err);
|
|
350
|
-
}
|
|
351
|
-
}
|
|
352
|
-
// ---------------------------------------------------------------------------
|
|
353
|
-
// Private — SOL methods
|
|
354
|
-
// ---------------------------------------------------------------------------
|
|
355
|
-
async _solGetAddress(sessionId, params) {
|
|
356
|
-
const solSigner = await this._createSolSigner(sessionId);
|
|
357
|
-
const path = normalizePath(params.path);
|
|
358
|
-
try {
|
|
359
|
-
const publicKey = await solSigner.getAddress(path, {
|
|
360
|
-
checkOnDevice: params.showOnDevice ?? false
|
|
361
|
-
});
|
|
362
|
-
return { address: publicKey, path: params.path };
|
|
363
|
-
} catch (err) {
|
|
364
|
-
this._invalidateSession(sessionId);
|
|
365
|
-
throw this._wrapError(err);
|
|
366
|
-
}
|
|
367
|
-
}
|
|
368
|
-
async _solSignTransaction(_sessionId, _params) {
|
|
369
|
-
throw new Error("LedgerWebHidConnector: solSignTransaction is not yet implemented");
|
|
370
|
-
}
|
|
371
|
-
async _solSignMessage(_sessionId, _params) {
|
|
372
|
-
throw new Error("LedgerWebHidConnector: solSignMessage is not yet implemented");
|
|
373
|
-
}
|
|
374
|
-
// ---------------------------------------------------------------------------
|
|
375
|
-
// Private — DMK / Manager lifecycle
|
|
376
|
-
// ---------------------------------------------------------------------------
|
|
377
55
|
/**
|
|
378
|
-
*
|
|
379
|
-
*
|
|
380
|
-
* Otherwise, one is created via dynamic import of the Ledger SDK.
|
|
56
|
+
* Override connectId resolution for BLE devices discovered via WebHID+BLE combo.
|
|
57
|
+
* For USB devices, the DMK path (ephemeral UUID) is used as-is.
|
|
381
58
|
*/
|
|
382
|
-
|
|
383
|
-
if (
|
|
384
|
-
|
|
385
|
-
this._dmk = this._providedDmk;
|
|
386
|
-
return this._dmk;
|
|
387
|
-
}
|
|
388
|
-
const { DeviceManagementKitBuilder } = await import("@ledgerhq/device-management-kit");
|
|
389
|
-
const { webHidTransportFactory } = await import("@ledgerhq/device-transport-kit-web-hid");
|
|
390
|
-
this._dmk = new DeviceManagementKitBuilder().addTransport(webHidTransportFactory).build();
|
|
391
|
-
return this._dmk;
|
|
392
|
-
}
|
|
393
|
-
_initManagers(dmk) {
|
|
394
|
-
this._dmk = dmk;
|
|
395
|
-
this._deviceManager = new import_ledger_adapter.LedgerDeviceManager(dmk);
|
|
396
|
-
this._signerManager = new import_ledger_adapter.SignerManager(dmk);
|
|
397
|
-
}
|
|
398
|
-
async _getDeviceManager() {
|
|
399
|
-
if (this._deviceManager) return this._deviceManager;
|
|
400
|
-
const dmk = await this._getOrCreateDmk();
|
|
401
|
-
this._initManagers(dmk);
|
|
402
|
-
return this._deviceManager;
|
|
403
|
-
}
|
|
404
|
-
async _getEthSigner(sessionId) {
|
|
405
|
-
if (!this._signerManager) {
|
|
406
|
-
const dmk = await this._getOrCreateDmk();
|
|
407
|
-
this._initManagers(dmk);
|
|
408
|
-
}
|
|
409
|
-
const signer = await this._signerManager.getOrCreate(sessionId);
|
|
410
|
-
signer.onInteraction = (interaction) => {
|
|
411
|
-
this._emit("ui-event", {
|
|
412
|
-
type: interaction,
|
|
413
|
-
payload: { sessionId }
|
|
414
|
-
});
|
|
415
|
-
};
|
|
416
|
-
return signer;
|
|
417
|
-
}
|
|
418
|
-
async _createBtcSigner(sessionId) {
|
|
419
|
-
const dmk = await this._getOrCreateDmk();
|
|
420
|
-
const { SignerBtcBuilder } = await import("@ledgerhq/device-signer-kit-bitcoin");
|
|
421
|
-
const sdkSigner = new SignerBtcBuilder({
|
|
422
|
-
dmk,
|
|
423
|
-
sessionId
|
|
424
|
-
}).build();
|
|
425
|
-
return new import_ledger_adapter.SignerBtc(sdkSigner);
|
|
426
|
-
}
|
|
427
|
-
async _createSolSigner(sessionId) {
|
|
428
|
-
const dmk = await this._getOrCreateDmk();
|
|
429
|
-
const { SignerSolanaBuilder } = await import("@ledgerhq/device-signer-kit-solana");
|
|
430
|
-
const sdkSigner = new SignerSolanaBuilder({
|
|
431
|
-
dmk,
|
|
432
|
-
sessionId
|
|
433
|
-
}).build();
|
|
434
|
-
return new import_ledger_adapter.SignerSol(sdkSigner);
|
|
435
|
-
}
|
|
436
|
-
_invalidateSession(sessionId) {
|
|
437
|
-
this._signerManager?.invalidate(sessionId);
|
|
438
|
-
}
|
|
439
|
-
_resetManagers() {
|
|
440
|
-
this._signerManager?.clearAll();
|
|
441
|
-
this._deviceManager?.dispose();
|
|
442
|
-
this._deviceManager = null;
|
|
443
|
-
this._signerManager = null;
|
|
444
|
-
this._dmk = null;
|
|
445
|
-
}
|
|
446
|
-
// ---------------------------------------------------------------------------
|
|
447
|
-
// Private — Events
|
|
448
|
-
// ---------------------------------------------------------------------------
|
|
449
|
-
_emit(event, data) {
|
|
450
|
-
const handlers = this._eventHandlers.get(event);
|
|
451
|
-
if (handlers) {
|
|
452
|
-
for (const handler of handlers) {
|
|
453
|
-
try {
|
|
454
|
-
handler(data);
|
|
455
|
-
} catch {
|
|
456
|
-
}
|
|
457
|
-
}
|
|
59
|
+
_resolveConnectId(descriptor) {
|
|
60
|
+
if (descriptor.transport === "BLE") {
|
|
61
|
+
return extractBleHexId(descriptor.name) || descriptor.path;
|
|
458
62
|
}
|
|
459
|
-
|
|
460
|
-
// ---------------------------------------------------------------------------
|
|
461
|
-
// Private — Error handling
|
|
462
|
-
// ---------------------------------------------------------------------------
|
|
463
|
-
_wrapError(err) {
|
|
464
|
-
const mapped = (0, import_ledger_adapter.mapLedgerError)(err);
|
|
465
|
-
const error = new Error(mapped.message);
|
|
466
|
-
error.code = mapped.code;
|
|
467
|
-
return error;
|
|
63
|
+
return descriptor.path;
|
|
468
64
|
}
|
|
469
65
|
};
|
|
470
66
|
|
package/dist/index.js.map
CHANGED
|
@@ -1 +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 SignerSol,\n mapLedgerError,\n} from '@bytezhang/ledger-adapter';\n\n// ---------------------------------------------------------------------------\n// Utilities\n// ---------------------------------------------------------------------------\n\n/**\n * Extract the stable 4-digit HEX identifier from a Ledger BLE device name.\n * e.g., \"Nano X 123A\" → \"123A\", \"Ledger Nano X AB12\" → \"AB12\"\n * Returns undefined if no valid HEX suffix found.\n */\nfunction extractBleHexId(name?: string): string | undefined {\n if (!name) return undefined;\n const match = name.match(/\\b([0-9A-Fa-f]{4})$/);\n return match ? match[1].toUpperCase() : undefined;\n}\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/** Parameters for btcSignTransaction */\ninterface BtcSignTransactionCallParams {\n psbt?: string;\n coin: string;\n}\n\n/** Parameters for btcSignMessage */\ninterface BtcSignMessageCallParams {\n path: string;\n message: string;\n coin?: string;\n}\n\n/** Parameters for solGetAddress */\ninterface SolGetAddressCallParams {\n path: string;\n showOnDevice?: boolean;\n}\n\n/** Parameters for solSignTransaction */\ninterface SolSignTransactionCallParams {\n path: string;\n /** Hex-encoded serialized transaction bytes (no 0x prefix) */\n serializedTx: string;\n}\n\n/** Parameters for solSignMessage */\ninterface SolSignMessageCallParams {\n path: string;\n /** Message bytes as hex string (no 0x prefix) */\n message: string;\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 const result: ConnectorDevice[] = descriptors.map((d) => {\n // For BLE: extract stable 4-digit HEX from device name (e.g., \"Nano X 123A\" → \"123A\")\n // For USB: d.path is a temporary DMK UUID, not persistent\n const isBle = d.transport === 'BLE';\n const bleHexId = isBle ? extractBleHexId(d.name) : undefined;\n\n return {\n connectId: bleHexId || d.path,\n deviceId: d.path,\n name: d.name || d.type || 'Ledger',\n model: d.type,\n };\n });\n return result;\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 // Empty or missing deviceId — use first available device's DMK path\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 capabilities: {\n persistentDeviceIdentity: false,\n },\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 case 'btcSignTransaction':\n return this._btcSignTransaction(sessionId, params as BtcSignTransactionCallParams);\n case 'btcSignMessage':\n return this._btcSignMessage(sessionId, params as BtcSignMessageCallParams);\n case 'btcGetMasterFingerprint':\n return this._btcGetMasterFingerprint(sessionId, params as { skipOpenApp?: boolean } | undefined);\n case 'solGetAddress':\n return this._solGetAddress(sessionId, params as SolGetAddressCallParams);\n case 'solSignTransaction':\n return this._solSignTransaction(sessionId, params as SolSignTransactionCallParams);\n case 'solSignMessage':\n return this._solSignMessage(sessionId, params as SolSignMessageCallParams);\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 private async _btcSignTransaction(\n _sessionId: string,\n _params: BtcSignTransactionCallParams,\n ): Promise<never> {\n throw new Error('LedgerWebHidConnector: btcSignTransaction is not yet implemented');\n }\n\n private async _btcSignMessage(\n _sessionId: string,\n _params: BtcSignMessageCallParams,\n ): Promise<never> {\n throw new Error('LedgerWebHidConnector: btcSignMessage is not yet implemented');\n }\n\n private async _btcGetMasterFingerprint(\n sessionId: string,\n params?: { skipOpenApp?: boolean },\n ): Promise<{ masterFingerprint: string }> {\n const btcSigner = await this._createBtcSigner(sessionId);\n\n try {\n const fingerprint: Uint8Array = await btcSigner.getMasterFingerprint({\n skipOpenApp: params?.skipOpenApp,\n });\n // Convert Uint8Array to hex string\n const hex = Array.from(fingerprint)\n .map((b) => b.toString(16).padStart(2, '0'))\n .join('');\n return { masterFingerprint: hex };\n } catch (err) {\n this._invalidateSession(sessionId);\n throw this._wrapError(err);\n }\n }\n\n // ---------------------------------------------------------------------------\n // Private — SOL methods\n // ---------------------------------------------------------------------------\n\n private async _solGetAddress(\n sessionId: string,\n params: SolGetAddressCallParams,\n ): Promise<{ address: string; path: string }> {\n const solSigner = await this._createSolSigner(sessionId);\n const path = normalizePath(params.path);\n\n try {\n // Ledger Solana signer returns a base58-encoded Ed25519 public key (= Solana address)\n const publicKey = await solSigner.getAddress(path, {\n checkOnDevice: params.showOnDevice ?? false,\n });\n return { address: publicKey, path: params.path };\n } catch (err) {\n this._invalidateSession(sessionId);\n throw this._wrapError(err);\n }\n }\n\n private async _solSignTransaction(\n _sessionId: string,\n _params: SolSignTransactionCallParams,\n ): Promise<never> {\n throw new Error('LedgerWebHidConnector: solSignTransaction is not yet implemented');\n }\n\n private async _solSignMessage(\n _sessionId: string,\n _params: SolSignMessageCallParams,\n ): Promise<never> {\n throw new Error('LedgerWebHidConnector: solSignMessage is not yet implemented');\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 const signer = await this._signerManager!.getOrCreate(sessionId);\n\n // Wire up interaction events (open-app, unlock, verify-address, sign, etc.)\n signer.onInteraction = (interaction: string) => {\n this._emit('ui-event', {\n type: interaction,\n payload: { sessionId },\n });\n };\n\n return signer;\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 async _createSolSigner(sessionId: string): Promise<SignerSol> {\n const dmk = await this._getOrCreateDmk();\n const { SignerSolanaBuilder } = await import(\n '@ledgerhq/device-signer-kit-solana'\n );\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const sdkSigner = new SignerSolanaBuilder({\n dmk: dmk as any,\n sessionId,\n }).build();\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n return new SignerSol(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":[]}
|
|
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 { DeviceDescriptor } from '@bytezhang/hardware-wallet-core';\nimport type { IDmk } from '@bytezhang/ledger-adapter';\nimport { LedgerConnectorBase } from '@bytezhang/ledger-adapter';\n\n// ---------------------------------------------------------------------------\n// Utilities\n// ---------------------------------------------------------------------------\n\n/**\n * Extract the stable 4-digit HEX identifier from a Ledger BLE device name.\n * e.g., \"Nano X 123A\" -> \"123A\", \"Ledger Nano X AB12\" -> \"AB12\"\n * Returns undefined if no valid HEX suffix found.\n */\nfunction extractBleHexId(name?: string): string | undefined {\n if (!name) return undefined;\n const match = name.match(/\\b([0-9A-Fa-f]{4})$/);\n return match ? match[1].toUpperCase() : undefined;\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 * Extends LedgerConnectorBase with the WebHID transport factory.\n * Overrides connectId resolution to handle BLE devices that may appear\n * via a WebHID+BLE combo transport.\n */\nexport class LedgerWebHidConnector extends LedgerConnectorBase {\n constructor(options?: LedgerWebHidConnectorOptions) {\n super(\n async () => {\n const { webHidTransportFactory } = await import(\n '@ledgerhq/device-transport-kit-web-hid'\n );\n return webHidTransportFactory;\n },\n { connectionType: 'usb', dmk: options?.dmk },\n );\n }\n\n /**\n * Override connectId resolution for BLE devices discovered via WebHID+BLE combo.\n * For USB devices, the DMK path (ephemeral UUID) is used as-is.\n */\n protected override _resolveConnectId(descriptor: DeviceDescriptor): string {\n if (descriptor.transport === 'BLE') {\n return extractBleHexId(descriptor.name) || descriptor.path;\n }\n return descriptor.path;\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACEA,4BAAoC;AAWpC,SAAS,gBAAgB,MAAmC;AAC1D,MAAI,CAAC,KAAM,QAAO;AAClB,QAAM,QAAQ,KAAK,MAAM,qBAAqB;AAC9C,SAAO,QAAQ,MAAM,CAAC,EAAE,YAAY,IAAI;AAC1C;AAsBO,IAAM,wBAAN,cAAoC,0CAAoB;AAAA,EAC7D,YAAY,SAAwC;AAClD;AAAA,MACE,YAAY;AACV,cAAM,EAAE,uBAAuB,IAAI,MAAM,OACvC,wCACF;AACA,eAAO;AAAA,MACT;AAAA,MACA,EAAE,gBAAgB,OAAO,KAAK,SAAS,IAAI;AAAA,IAC7C;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA,EAMmB,kBAAkB,YAAsC;AACzE,QAAI,WAAW,cAAc,OAAO;AAClC,aAAO,gBAAgB,WAAW,IAAI,KAAK,WAAW;AAAA,IACxD;AACA,WAAO,WAAW;AAAA,EACpB;AACF;;;AD5CO,SAAS,4BAA4B,KAAwB;AAClE,SAAO,IAAI,sBAAsB,EAAE,IAAI,CAAC;AAC1C;","names":[]}
|
package/dist/index.mjs
CHANGED
|
@@ -1,439 +1,29 @@
|
|
|
1
1
|
// src/LedgerWebHidConnector.ts
|
|
2
|
-
import {
|
|
3
|
-
LedgerDeviceManager,
|
|
4
|
-
SignerManager,
|
|
5
|
-
SignerBtc,
|
|
6
|
-
SignerSol,
|
|
7
|
-
mapLedgerError
|
|
8
|
-
} from "@bytezhang/ledger-adapter";
|
|
2
|
+
import { LedgerConnectorBase } from "@bytezhang/ledger-adapter";
|
|
9
3
|
function extractBleHexId(name) {
|
|
10
4
|
if (!name) return void 0;
|
|
11
5
|
const match = name.match(/\b([0-9A-Fa-f]{4})$/);
|
|
12
6
|
return match ? match[1].toUpperCase() : void 0;
|
|
13
7
|
}
|
|
14
|
-
|
|
15
|
-
return path.startsWith("m/") ? path.slice(2) : path;
|
|
16
|
-
}
|
|
17
|
-
function stripHex(hex) {
|
|
18
|
-
return hex.startsWith("0x") ? hex.slice(2) : hex;
|
|
19
|
-
}
|
|
20
|
-
function padHex64(hex) {
|
|
21
|
-
return `0x${stripHex(hex).padStart(64, "0")}`;
|
|
22
|
-
}
|
|
23
|
-
var LedgerWebHidConnector = class {
|
|
8
|
+
var LedgerWebHidConnector = class extends LedgerConnectorBase {
|
|
24
9
|
constructor(options) {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
// ---------------------------------------------------------------------------
|
|
35
|
-
// IConnector — Device discovery
|
|
36
|
-
// ---------------------------------------------------------------------------
|
|
37
|
-
async searchDevices() {
|
|
38
|
-
const dm = await this._getDeviceManager();
|
|
39
|
-
let descriptors = await dm.enumerate();
|
|
40
|
-
if (descriptors.length === 0) {
|
|
41
|
-
try {
|
|
42
|
-
await dm.requestDevice();
|
|
43
|
-
} catch {
|
|
44
|
-
}
|
|
45
|
-
descriptors = await dm.enumerate();
|
|
46
|
-
}
|
|
47
|
-
const result = descriptors.map((d) => {
|
|
48
|
-
const isBle = d.transport === "BLE";
|
|
49
|
-
const bleHexId = isBle ? extractBleHexId(d.name) : void 0;
|
|
50
|
-
return {
|
|
51
|
-
connectId: bleHexId || d.path,
|
|
52
|
-
deviceId: d.path,
|
|
53
|
-
name: d.name || d.type || "Ledger",
|
|
54
|
-
model: d.type
|
|
55
|
-
};
|
|
56
|
-
});
|
|
57
|
-
return result;
|
|
58
|
-
}
|
|
59
|
-
// ---------------------------------------------------------------------------
|
|
60
|
-
// IConnector — Connection
|
|
61
|
-
// ---------------------------------------------------------------------------
|
|
62
|
-
async connect(deviceId) {
|
|
63
|
-
const dm = await this._getDeviceManager();
|
|
64
|
-
await this.searchDevices();
|
|
65
|
-
let resolvedDeviceId = deviceId;
|
|
66
|
-
if (!resolvedDeviceId) {
|
|
67
|
-
const descriptors = await dm.enumerate();
|
|
68
|
-
if (descriptors.length === 0) {
|
|
69
|
-
throw new Error(
|
|
70
|
-
"No Ledger device found. Make sure the device is connected via USB and unlocked."
|
|
71
|
-
);
|
|
72
|
-
}
|
|
73
|
-
resolvedDeviceId = descriptors[0].path;
|
|
74
|
-
}
|
|
75
|
-
try {
|
|
76
|
-
const sessionId = await dm.connect(resolvedDeviceId);
|
|
77
|
-
const session = {
|
|
78
|
-
sessionId,
|
|
79
|
-
deviceInfo: {
|
|
80
|
-
vendor: "ledger",
|
|
81
|
-
model: "unknown",
|
|
82
|
-
firmwareVersion: "unknown",
|
|
83
|
-
deviceId: resolvedDeviceId,
|
|
84
|
-
connectId: resolvedDeviceId,
|
|
85
|
-
connectionType: "usb",
|
|
86
|
-
capabilities: {
|
|
87
|
-
persistentDeviceIdentity: false
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
};
|
|
91
|
-
this._emit("device-connect", {
|
|
92
|
-
device: {
|
|
93
|
-
connectId: resolvedDeviceId,
|
|
94
|
-
deviceId: resolvedDeviceId,
|
|
95
|
-
name: "Ledger"
|
|
96
|
-
}
|
|
97
|
-
});
|
|
98
|
-
return session;
|
|
99
|
-
} catch (err) {
|
|
100
|
-
this._resetManagers();
|
|
101
|
-
const dm2 = await this._getDeviceManager();
|
|
102
|
-
await this.searchDevices();
|
|
103
|
-
const descriptors = await dm2.enumerate();
|
|
104
|
-
const retryId = deviceId ? descriptors.find((d) => d.path === deviceId)?.path : descriptors[0]?.path;
|
|
105
|
-
if (!retryId) {
|
|
106
|
-
throw new Error(
|
|
107
|
-
"No Ledger device found after retry. Make sure the device is connected via USB and unlocked."
|
|
108
|
-
);
|
|
109
|
-
}
|
|
110
|
-
const sessionId = await dm2.connect(retryId);
|
|
111
|
-
const session = {
|
|
112
|
-
sessionId,
|
|
113
|
-
deviceInfo: {
|
|
114
|
-
vendor: "ledger",
|
|
115
|
-
model: "unknown",
|
|
116
|
-
firmwareVersion: "unknown",
|
|
117
|
-
deviceId: retryId,
|
|
118
|
-
connectId: retryId,
|
|
119
|
-
connectionType: "usb"
|
|
120
|
-
}
|
|
121
|
-
};
|
|
122
|
-
this._emit("device-connect", {
|
|
123
|
-
device: {
|
|
124
|
-
connectId: retryId,
|
|
125
|
-
deviceId: retryId,
|
|
126
|
-
name: "Ledger"
|
|
127
|
-
}
|
|
128
|
-
});
|
|
129
|
-
return session;
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
async disconnect(sessionId) {
|
|
133
|
-
if (!this._deviceManager) return;
|
|
134
|
-
const deviceId = this._deviceManager.getDeviceId(sessionId);
|
|
135
|
-
this._signerManager?.invalidate(sessionId);
|
|
136
|
-
await this._deviceManager.disconnect(sessionId);
|
|
137
|
-
if (deviceId) {
|
|
138
|
-
this._emit("device-disconnect", { connectId: deviceId });
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
// ---------------------------------------------------------------------------
|
|
142
|
-
// IConnector — Method dispatch
|
|
143
|
-
// ---------------------------------------------------------------------------
|
|
144
|
-
async call(sessionId, method, params) {
|
|
145
|
-
switch (method) {
|
|
146
|
-
case "evmGetAddress":
|
|
147
|
-
return this._evmGetAddress(sessionId, params);
|
|
148
|
-
case "evmSignTransaction":
|
|
149
|
-
return this._evmSignTransaction(sessionId, params);
|
|
150
|
-
case "evmSignMessage":
|
|
151
|
-
return this._evmSignMessage(sessionId, params);
|
|
152
|
-
case "evmSignTypedData":
|
|
153
|
-
return this._evmSignTypedData(sessionId, params);
|
|
154
|
-
case "btcGetAddress":
|
|
155
|
-
return this._btcGetAddress(sessionId, params);
|
|
156
|
-
case "btcGetPublicKey":
|
|
157
|
-
return this._btcGetPublicKey(sessionId, params);
|
|
158
|
-
case "btcSignTransaction":
|
|
159
|
-
return this._btcSignTransaction(sessionId, params);
|
|
160
|
-
case "btcSignMessage":
|
|
161
|
-
return this._btcSignMessage(sessionId, params);
|
|
162
|
-
case "btcGetMasterFingerprint":
|
|
163
|
-
return this._btcGetMasterFingerprint(sessionId, params);
|
|
164
|
-
case "solGetAddress":
|
|
165
|
-
return this._solGetAddress(sessionId, params);
|
|
166
|
-
case "solSignTransaction":
|
|
167
|
-
return this._solSignTransaction(sessionId, params);
|
|
168
|
-
case "solSignMessage":
|
|
169
|
-
return this._solSignMessage(sessionId, params);
|
|
170
|
-
default:
|
|
171
|
-
throw new Error(`LedgerWebHidConnector: unknown method "${method}"`);
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
async cancel(_sessionId) {
|
|
175
|
-
}
|
|
176
|
-
uiResponse(_response) {
|
|
177
|
-
}
|
|
178
|
-
// ---------------------------------------------------------------------------
|
|
179
|
-
// IConnector — Events
|
|
180
|
-
// ---------------------------------------------------------------------------
|
|
181
|
-
on(event, handler) {
|
|
182
|
-
if (!this._eventHandlers.has(event)) {
|
|
183
|
-
this._eventHandlers.set(event, /* @__PURE__ */ new Set());
|
|
184
|
-
}
|
|
185
|
-
this._eventHandlers.get(event).add(handler);
|
|
186
|
-
}
|
|
187
|
-
off(event, handler) {
|
|
188
|
-
this._eventHandlers.get(event)?.delete(handler);
|
|
189
|
-
}
|
|
190
|
-
// ---------------------------------------------------------------------------
|
|
191
|
-
// IConnector — Reset
|
|
192
|
-
// ---------------------------------------------------------------------------
|
|
193
|
-
reset() {
|
|
194
|
-
this._resetManagers();
|
|
195
|
-
}
|
|
196
|
-
// ---------------------------------------------------------------------------
|
|
197
|
-
// Private — EVM methods
|
|
198
|
-
// ---------------------------------------------------------------------------
|
|
199
|
-
async _evmGetAddress(sessionId, params) {
|
|
200
|
-
const signer = await this._getEthSigner(sessionId);
|
|
201
|
-
const path = normalizePath(params.path);
|
|
202
|
-
try {
|
|
203
|
-
const result = await signer.getAddress(path, {
|
|
204
|
-
checkOnDevice: params.showOnDevice ?? false
|
|
205
|
-
});
|
|
206
|
-
return { address: result.address, publicKey: result.publicKey };
|
|
207
|
-
} catch (err) {
|
|
208
|
-
this._invalidateSession(sessionId);
|
|
209
|
-
throw this._wrapError(err);
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
|
-
async _evmSignTransaction(sessionId, params) {
|
|
213
|
-
const signer = await this._getEthSigner(sessionId);
|
|
214
|
-
const path = normalizePath(params.path);
|
|
215
|
-
try {
|
|
216
|
-
const result = await signer.signTransaction(
|
|
217
|
-
path,
|
|
218
|
-
params.serializedTx
|
|
219
|
-
);
|
|
220
|
-
return {
|
|
221
|
-
v: `0x${result.v.toString(16)}`,
|
|
222
|
-
r: padHex64(result.r),
|
|
223
|
-
s: padHex64(result.s)
|
|
224
|
-
};
|
|
225
|
-
} catch (err) {
|
|
226
|
-
this._invalidateSession(sessionId);
|
|
227
|
-
throw this._wrapError(err);
|
|
228
|
-
}
|
|
229
|
-
}
|
|
230
|
-
async _evmSignMessage(sessionId, params) {
|
|
231
|
-
const signer = await this._getEthSigner(sessionId);
|
|
232
|
-
const path = normalizePath(params.path);
|
|
233
|
-
try {
|
|
234
|
-
const result = await signer.signMessage(
|
|
235
|
-
path,
|
|
236
|
-
params.message
|
|
237
|
-
);
|
|
238
|
-
const rHex = stripHex(result.r).padStart(64, "0");
|
|
239
|
-
const sHex = stripHex(result.s).padStart(64, "0");
|
|
240
|
-
const vHex = result.v.toString(16).padStart(2, "0");
|
|
241
|
-
return { signature: `0x${rHex}${sHex}${vHex}` };
|
|
242
|
-
} catch (err) {
|
|
243
|
-
this._invalidateSession(sessionId);
|
|
244
|
-
throw this._wrapError(err);
|
|
245
|
-
}
|
|
10
|
+
super(
|
|
11
|
+
async () => {
|
|
12
|
+
const { webHidTransportFactory } = await import("@ledgerhq/device-transport-kit-web-hid");
|
|
13
|
+
return webHidTransportFactory;
|
|
14
|
+
},
|
|
15
|
+
{ connectionType: "usb", dmk: options?.dmk }
|
|
16
|
+
);
|
|
246
17
|
}
|
|
247
|
-
async _evmSignTypedData(sessionId, params) {
|
|
248
|
-
const signer = await this._getEthSigner(sessionId);
|
|
249
|
-
const path = normalizePath(params.path);
|
|
250
|
-
try {
|
|
251
|
-
const result = await signer.signTypedData(
|
|
252
|
-
path,
|
|
253
|
-
params.data
|
|
254
|
-
);
|
|
255
|
-
const rHex = stripHex(result.r).padStart(64, "0");
|
|
256
|
-
const sHex = stripHex(result.s).padStart(64, "0");
|
|
257
|
-
const vHex = result.v.toString(16).padStart(2, "0");
|
|
258
|
-
return { signature: `0x${rHex}${sHex}${vHex}` };
|
|
259
|
-
} catch (err) {
|
|
260
|
-
this._invalidateSession(sessionId);
|
|
261
|
-
throw this._wrapError(err);
|
|
262
|
-
}
|
|
263
|
-
}
|
|
264
|
-
// ---------------------------------------------------------------------------
|
|
265
|
-
// Private — BTC methods
|
|
266
|
-
// ---------------------------------------------------------------------------
|
|
267
|
-
async _btcGetAddress(sessionId, params) {
|
|
268
|
-
const btcSigner = await this._createBtcSigner(sessionId);
|
|
269
|
-
const path = normalizePath(params.path);
|
|
270
|
-
try {
|
|
271
|
-
const { DefaultWallet, DefaultDescriptorTemplate } = await import("@ledgerhq/device-signer-kit-bitcoin");
|
|
272
|
-
const purpose = path.split("/")[0]?.replace("'", "");
|
|
273
|
-
let template = DefaultDescriptorTemplate.NATIVE_SEGWIT;
|
|
274
|
-
if (purpose === "44") template = DefaultDescriptorTemplate.LEGACY;
|
|
275
|
-
else if (purpose === "49")
|
|
276
|
-
template = DefaultDescriptorTemplate.NESTED_SEGWIT;
|
|
277
|
-
else if (purpose === "86") template = DefaultDescriptorTemplate.TAPROOT;
|
|
278
|
-
const wallet = new DefaultWallet(path, template);
|
|
279
|
-
const result = await btcSigner.getWalletAddress(wallet, 0, {
|
|
280
|
-
checkOnDevice: params.showOnDevice ?? false,
|
|
281
|
-
change: false
|
|
282
|
-
});
|
|
283
|
-
return { address: result.address, path: params.path };
|
|
284
|
-
} catch (err) {
|
|
285
|
-
this._invalidateSession(sessionId);
|
|
286
|
-
throw this._wrapError(err);
|
|
287
|
-
}
|
|
288
|
-
}
|
|
289
|
-
async _btcGetPublicKey(sessionId, params) {
|
|
290
|
-
const btcSigner = await this._createBtcSigner(sessionId);
|
|
291
|
-
const path = normalizePath(params.path);
|
|
292
|
-
try {
|
|
293
|
-
const xpub = await btcSigner.getExtendedPublicKey(path, {
|
|
294
|
-
checkOnDevice: params.showOnDevice ?? false
|
|
295
|
-
});
|
|
296
|
-
return { xpub, path: params.path };
|
|
297
|
-
} catch (err) {
|
|
298
|
-
this._invalidateSession(sessionId);
|
|
299
|
-
throw this._wrapError(err);
|
|
300
|
-
}
|
|
301
|
-
}
|
|
302
|
-
async _btcSignTransaction(_sessionId, _params) {
|
|
303
|
-
throw new Error("LedgerWebHidConnector: btcSignTransaction is not yet implemented");
|
|
304
|
-
}
|
|
305
|
-
async _btcSignMessage(_sessionId, _params) {
|
|
306
|
-
throw new Error("LedgerWebHidConnector: btcSignMessage is not yet implemented");
|
|
307
|
-
}
|
|
308
|
-
async _btcGetMasterFingerprint(sessionId, params) {
|
|
309
|
-
const btcSigner = await this._createBtcSigner(sessionId);
|
|
310
|
-
try {
|
|
311
|
-
const fingerprint = await btcSigner.getMasterFingerprint({
|
|
312
|
-
skipOpenApp: params?.skipOpenApp
|
|
313
|
-
});
|
|
314
|
-
const hex = Array.from(fingerprint).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
315
|
-
return { masterFingerprint: hex };
|
|
316
|
-
} catch (err) {
|
|
317
|
-
this._invalidateSession(sessionId);
|
|
318
|
-
throw this._wrapError(err);
|
|
319
|
-
}
|
|
320
|
-
}
|
|
321
|
-
// ---------------------------------------------------------------------------
|
|
322
|
-
// Private — SOL methods
|
|
323
|
-
// ---------------------------------------------------------------------------
|
|
324
|
-
async _solGetAddress(sessionId, params) {
|
|
325
|
-
const solSigner = await this._createSolSigner(sessionId);
|
|
326
|
-
const path = normalizePath(params.path);
|
|
327
|
-
try {
|
|
328
|
-
const publicKey = await solSigner.getAddress(path, {
|
|
329
|
-
checkOnDevice: params.showOnDevice ?? false
|
|
330
|
-
});
|
|
331
|
-
return { address: publicKey, path: params.path };
|
|
332
|
-
} catch (err) {
|
|
333
|
-
this._invalidateSession(sessionId);
|
|
334
|
-
throw this._wrapError(err);
|
|
335
|
-
}
|
|
336
|
-
}
|
|
337
|
-
async _solSignTransaction(_sessionId, _params) {
|
|
338
|
-
throw new Error("LedgerWebHidConnector: solSignTransaction is not yet implemented");
|
|
339
|
-
}
|
|
340
|
-
async _solSignMessage(_sessionId, _params) {
|
|
341
|
-
throw new Error("LedgerWebHidConnector: solSignMessage is not yet implemented");
|
|
342
|
-
}
|
|
343
|
-
// ---------------------------------------------------------------------------
|
|
344
|
-
// Private — DMK / Manager lifecycle
|
|
345
|
-
// ---------------------------------------------------------------------------
|
|
346
18
|
/**
|
|
347
|
-
*
|
|
348
|
-
*
|
|
349
|
-
* Otherwise, one is created via dynamic import of the Ledger SDK.
|
|
19
|
+
* Override connectId resolution for BLE devices discovered via WebHID+BLE combo.
|
|
20
|
+
* For USB devices, the DMK path (ephemeral UUID) is used as-is.
|
|
350
21
|
*/
|
|
351
|
-
|
|
352
|
-
if (
|
|
353
|
-
|
|
354
|
-
this._dmk = this._providedDmk;
|
|
355
|
-
return this._dmk;
|
|
356
|
-
}
|
|
357
|
-
const { DeviceManagementKitBuilder } = await import("@ledgerhq/device-management-kit");
|
|
358
|
-
const { webHidTransportFactory } = await import("@ledgerhq/device-transport-kit-web-hid");
|
|
359
|
-
this._dmk = new DeviceManagementKitBuilder().addTransport(webHidTransportFactory).build();
|
|
360
|
-
return this._dmk;
|
|
361
|
-
}
|
|
362
|
-
_initManagers(dmk) {
|
|
363
|
-
this._dmk = dmk;
|
|
364
|
-
this._deviceManager = new LedgerDeviceManager(dmk);
|
|
365
|
-
this._signerManager = new SignerManager(dmk);
|
|
366
|
-
}
|
|
367
|
-
async _getDeviceManager() {
|
|
368
|
-
if (this._deviceManager) return this._deviceManager;
|
|
369
|
-
const dmk = await this._getOrCreateDmk();
|
|
370
|
-
this._initManagers(dmk);
|
|
371
|
-
return this._deviceManager;
|
|
372
|
-
}
|
|
373
|
-
async _getEthSigner(sessionId) {
|
|
374
|
-
if (!this._signerManager) {
|
|
375
|
-
const dmk = await this._getOrCreateDmk();
|
|
376
|
-
this._initManagers(dmk);
|
|
377
|
-
}
|
|
378
|
-
const signer = await this._signerManager.getOrCreate(sessionId);
|
|
379
|
-
signer.onInteraction = (interaction) => {
|
|
380
|
-
this._emit("ui-event", {
|
|
381
|
-
type: interaction,
|
|
382
|
-
payload: { sessionId }
|
|
383
|
-
});
|
|
384
|
-
};
|
|
385
|
-
return signer;
|
|
386
|
-
}
|
|
387
|
-
async _createBtcSigner(sessionId) {
|
|
388
|
-
const dmk = await this._getOrCreateDmk();
|
|
389
|
-
const { SignerBtcBuilder } = await import("@ledgerhq/device-signer-kit-bitcoin");
|
|
390
|
-
const sdkSigner = new SignerBtcBuilder({
|
|
391
|
-
dmk,
|
|
392
|
-
sessionId
|
|
393
|
-
}).build();
|
|
394
|
-
return new SignerBtc(sdkSigner);
|
|
395
|
-
}
|
|
396
|
-
async _createSolSigner(sessionId) {
|
|
397
|
-
const dmk = await this._getOrCreateDmk();
|
|
398
|
-
const { SignerSolanaBuilder } = await import("@ledgerhq/device-signer-kit-solana");
|
|
399
|
-
const sdkSigner = new SignerSolanaBuilder({
|
|
400
|
-
dmk,
|
|
401
|
-
sessionId
|
|
402
|
-
}).build();
|
|
403
|
-
return new SignerSol(sdkSigner);
|
|
404
|
-
}
|
|
405
|
-
_invalidateSession(sessionId) {
|
|
406
|
-
this._signerManager?.invalidate(sessionId);
|
|
407
|
-
}
|
|
408
|
-
_resetManagers() {
|
|
409
|
-
this._signerManager?.clearAll();
|
|
410
|
-
this._deviceManager?.dispose();
|
|
411
|
-
this._deviceManager = null;
|
|
412
|
-
this._signerManager = null;
|
|
413
|
-
this._dmk = null;
|
|
414
|
-
}
|
|
415
|
-
// ---------------------------------------------------------------------------
|
|
416
|
-
// Private — Events
|
|
417
|
-
// ---------------------------------------------------------------------------
|
|
418
|
-
_emit(event, data) {
|
|
419
|
-
const handlers = this._eventHandlers.get(event);
|
|
420
|
-
if (handlers) {
|
|
421
|
-
for (const handler of handlers) {
|
|
422
|
-
try {
|
|
423
|
-
handler(data);
|
|
424
|
-
} catch {
|
|
425
|
-
}
|
|
426
|
-
}
|
|
22
|
+
_resolveConnectId(descriptor) {
|
|
23
|
+
if (descriptor.transport === "BLE") {
|
|
24
|
+
return extractBleHexId(descriptor.name) || descriptor.path;
|
|
427
25
|
}
|
|
428
|
-
|
|
429
|
-
// ---------------------------------------------------------------------------
|
|
430
|
-
// Private — Error handling
|
|
431
|
-
// ---------------------------------------------------------------------------
|
|
432
|
-
_wrapError(err) {
|
|
433
|
-
const mapped = mapLedgerError(err);
|
|
434
|
-
const error = new Error(mapped.message);
|
|
435
|
-
error.code = mapped.code;
|
|
436
|
-
return error;
|
|
26
|
+
return descriptor.path;
|
|
437
27
|
}
|
|
438
28
|
};
|
|
439
29
|
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +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 SignerSol,\n mapLedgerError,\n} from '@bytezhang/ledger-adapter';\n\n// ---------------------------------------------------------------------------\n// Utilities\n// ---------------------------------------------------------------------------\n\n/**\n * Extract the stable 4-digit HEX identifier from a Ledger BLE device name.\n * e.g., \"Nano X 123A\" → \"123A\", \"Ledger Nano X AB12\" → \"AB12\"\n * Returns undefined if no valid HEX suffix found.\n */\nfunction extractBleHexId(name?: string): string | undefined {\n if (!name) return undefined;\n const match = name.match(/\\b([0-9A-Fa-f]{4})$/);\n return match ? match[1].toUpperCase() : undefined;\n}\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/** Parameters for btcSignTransaction */\ninterface BtcSignTransactionCallParams {\n psbt?: string;\n coin: string;\n}\n\n/** Parameters for btcSignMessage */\ninterface BtcSignMessageCallParams {\n path: string;\n message: string;\n coin?: string;\n}\n\n/** Parameters for solGetAddress */\ninterface SolGetAddressCallParams {\n path: string;\n showOnDevice?: boolean;\n}\n\n/** Parameters for solSignTransaction */\ninterface SolSignTransactionCallParams {\n path: string;\n /** Hex-encoded serialized transaction bytes (no 0x prefix) */\n serializedTx: string;\n}\n\n/** Parameters for solSignMessage */\ninterface SolSignMessageCallParams {\n path: string;\n /** Message bytes as hex string (no 0x prefix) */\n message: string;\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 const result: ConnectorDevice[] = descriptors.map((d) => {\n // For BLE: extract stable 4-digit HEX from device name (e.g., \"Nano X 123A\" → \"123A\")\n // For USB: d.path is a temporary DMK UUID, not persistent\n const isBle = d.transport === 'BLE';\n const bleHexId = isBle ? extractBleHexId(d.name) : undefined;\n\n return {\n connectId: bleHexId || d.path,\n deviceId: d.path,\n name: d.name || d.type || 'Ledger',\n model: d.type,\n };\n });\n return result;\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 // Empty or missing deviceId — use first available device's DMK path\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 capabilities: {\n persistentDeviceIdentity: false,\n },\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 case 'btcSignTransaction':\n return this._btcSignTransaction(sessionId, params as BtcSignTransactionCallParams);\n case 'btcSignMessage':\n return this._btcSignMessage(sessionId, params as BtcSignMessageCallParams);\n case 'btcGetMasterFingerprint':\n return this._btcGetMasterFingerprint(sessionId, params as { skipOpenApp?: boolean } | undefined);\n case 'solGetAddress':\n return this._solGetAddress(sessionId, params as SolGetAddressCallParams);\n case 'solSignTransaction':\n return this._solSignTransaction(sessionId, params as SolSignTransactionCallParams);\n case 'solSignMessage':\n return this._solSignMessage(sessionId, params as SolSignMessageCallParams);\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 private async _btcSignTransaction(\n _sessionId: string,\n _params: BtcSignTransactionCallParams,\n ): Promise<never> {\n throw new Error('LedgerWebHidConnector: btcSignTransaction is not yet implemented');\n }\n\n private async _btcSignMessage(\n _sessionId: string,\n _params: BtcSignMessageCallParams,\n ): Promise<never> {\n throw new Error('LedgerWebHidConnector: btcSignMessage is not yet implemented');\n }\n\n private async _btcGetMasterFingerprint(\n sessionId: string,\n params?: { skipOpenApp?: boolean },\n ): Promise<{ masterFingerprint: string }> {\n const btcSigner = await this._createBtcSigner(sessionId);\n\n try {\n const fingerprint: Uint8Array = await btcSigner.getMasterFingerprint({\n skipOpenApp: params?.skipOpenApp,\n });\n // Convert Uint8Array to hex string\n const hex = Array.from(fingerprint)\n .map((b) => b.toString(16).padStart(2, '0'))\n .join('');\n return { masterFingerprint: hex };\n } catch (err) {\n this._invalidateSession(sessionId);\n throw this._wrapError(err);\n }\n }\n\n // ---------------------------------------------------------------------------\n // Private — SOL methods\n // ---------------------------------------------------------------------------\n\n private async _solGetAddress(\n sessionId: string,\n params: SolGetAddressCallParams,\n ): Promise<{ address: string; path: string }> {\n const solSigner = await this._createSolSigner(sessionId);\n const path = normalizePath(params.path);\n\n try {\n // Ledger Solana signer returns a base58-encoded Ed25519 public key (= Solana address)\n const publicKey = await solSigner.getAddress(path, {\n checkOnDevice: params.showOnDevice ?? false,\n });\n return { address: publicKey, path: params.path };\n } catch (err) {\n this._invalidateSession(sessionId);\n throw this._wrapError(err);\n }\n }\n\n private async _solSignTransaction(\n _sessionId: string,\n _params: SolSignTransactionCallParams,\n ): Promise<never> {\n throw new Error('LedgerWebHidConnector: solSignTransaction is not yet implemented');\n }\n\n private async _solSignMessage(\n _sessionId: string,\n _params: SolSignMessageCallParams,\n ): Promise<never> {\n throw new Error('LedgerWebHidConnector: solSignMessage is not yet implemented');\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 const signer = await this._signerManager!.getOrCreate(sessionId);\n\n // Wire up interaction events (open-app, unlock, verify-address, sign, etc.)\n signer.onInteraction = (interaction: string) => {\n this._emit('ui-event', {\n type: interaction,\n payload: { sessionId },\n });\n };\n\n return signer;\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 async _createSolSigner(sessionId: string): Promise<SignerSol> {\n const dmk = await this._getOrCreateDmk();\n const { SignerSolanaBuilder } = await import(\n '@ledgerhq/device-signer-kit-solana'\n );\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const sdkSigner = new SignerSolanaBuilder({\n dmk: dmk as any,\n sessionId,\n }).build();\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n return new SignerSol(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":[]}
|
|
1
|
+
{"version":3,"sources":["../src/LedgerWebHidConnector.ts","../src/index.ts"],"sourcesContent":["import type { DeviceDescriptor } from '@bytezhang/hardware-wallet-core';\nimport type { IDmk } from '@bytezhang/ledger-adapter';\nimport { LedgerConnectorBase } from '@bytezhang/ledger-adapter';\n\n// ---------------------------------------------------------------------------\n// Utilities\n// ---------------------------------------------------------------------------\n\n/**\n * Extract the stable 4-digit HEX identifier from a Ledger BLE device name.\n * e.g., \"Nano X 123A\" -> \"123A\", \"Ledger Nano X AB12\" -> \"AB12\"\n * Returns undefined if no valid HEX suffix found.\n */\nfunction extractBleHexId(name?: string): string | undefined {\n if (!name) return undefined;\n const match = name.match(/\\b([0-9A-Fa-f]{4})$/);\n return match ? match[1].toUpperCase() : undefined;\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 * Extends LedgerConnectorBase with the WebHID transport factory.\n * Overrides connectId resolution to handle BLE devices that may appear\n * via a WebHID+BLE combo transport.\n */\nexport class LedgerWebHidConnector extends LedgerConnectorBase {\n constructor(options?: LedgerWebHidConnectorOptions) {\n super(\n async () => {\n const { webHidTransportFactory } = await import(\n '@ledgerhq/device-transport-kit-web-hid'\n );\n return webHidTransportFactory;\n },\n { connectionType: 'usb', dmk: options?.dmk },\n );\n }\n\n /**\n * Override connectId resolution for BLE devices discovered via WebHID+BLE combo.\n * For USB devices, the DMK path (ephemeral UUID) is used as-is.\n */\n protected override _resolveConnectId(descriptor: DeviceDescriptor): string {\n if (descriptor.transport === 'BLE') {\n return extractBleHexId(descriptor.name) || descriptor.path;\n }\n return descriptor.path;\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":";AAEA,SAAS,2BAA2B;AAWpC,SAAS,gBAAgB,MAAmC;AAC1D,MAAI,CAAC,KAAM,QAAO;AAClB,QAAM,QAAQ,KAAK,MAAM,qBAAqB;AAC9C,SAAO,QAAQ,MAAM,CAAC,EAAE,YAAY,IAAI;AAC1C;AAsBO,IAAM,wBAAN,cAAoC,oBAAoB;AAAA,EAC7D,YAAY,SAAwC;AAClD;AAAA,MACE,YAAY;AACV,cAAM,EAAE,uBAAuB,IAAI,MAAM,OACvC,wCACF;AACA,eAAO;AAAA,MACT;AAAA,MACA,EAAE,gBAAgB,OAAO,KAAK,SAAS,IAAI;AAAA,IAC7C;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA,EAMmB,kBAAkB,YAAsC;AACzE,QAAI,WAAW,cAAc,OAAO;AAClC,aAAO,gBAAgB,WAAW,IAAI,KAAK,WAAW;AAAA,IACxD;AACA,WAAO,WAAW;AAAA,EACpB;AACF;;;AC5CO,SAAS,4BAA4B,KAAwB;AAClE,SAAO,IAAI,sBAAsB,EAAE,IAAI,CAAC;AAC1C;","names":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bytezhang/hardware-ledger-connector-webhid",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.11",
|
|
4
4
|
"description": "IConnector implementation for Ledger hardware wallets via WebHID (DMK)",
|
|
5
5
|
"author": "OneKey",
|
|
6
6
|
"license": "MIT",
|
|
@@ -44,8 +44,8 @@
|
|
|
44
44
|
"connector"
|
|
45
45
|
],
|
|
46
46
|
"dependencies": {
|
|
47
|
-
"@bytezhang/hardware-wallet-core": "0.0.
|
|
48
|
-
"@bytezhang/ledger-adapter": "0.0.
|
|
47
|
+
"@bytezhang/hardware-wallet-core": "0.0.11",
|
|
48
|
+
"@bytezhang/ledger-adapter": "0.0.11",
|
|
49
49
|
"@ledgerhq/device-management-kit": "^1.1.0",
|
|
50
50
|
"@ledgerhq/device-signer-kit-ethereum": "^1.9.0",
|
|
51
51
|
"@ledgerhq/device-signer-kit-bitcoin": "^1.0.0",
|