@onekeyfe/hwk-ledger-adapter 1.2.7 → 1.2.8-alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.mts +86 -39
- package/dist/index.d.ts +86 -39
- package/dist/index.js +1781 -834
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1797 -829
- package/dist/index.mjs.map +1 -1
- package/package.json +14 -11
package/dist/index.js
CHANGED
|
@@ -39,6 +39,7 @@ __export(index_exports, {
|
|
|
39
39
|
SignerEth: () => SignerEth,
|
|
40
40
|
SignerManager: () => SignerManager,
|
|
41
41
|
SignerSol: () => SignerSol,
|
|
42
|
+
SignerZcash: () => SignerZcash,
|
|
42
43
|
debugLog: () => debugLog,
|
|
43
44
|
deviceActionToPromise: () => deviceActionToPromise,
|
|
44
45
|
isDeviceLockedError: () => isDeviceLockedError,
|
|
@@ -57,17 +58,14 @@ var import_hwk_adapter_core3 = require("@onekeyfe/hwk-adapter-core");
|
|
|
57
58
|
|
|
58
59
|
// src/errors.ts
|
|
59
60
|
var import_hwk_adapter_core = require("@onekeyfe/hwk-adapter-core");
|
|
60
|
-
|
|
61
|
-
function createMultipleUsbLedgerDevicesError() {
|
|
62
|
-
return Object.assign(new Error(MULTIPLE_USB_LEDGER_DEVICES_ERROR_MESSAGE), {
|
|
63
|
-
code: import_hwk_adapter_core.HardwareErrorCode.DeviceOneDeviceOnly
|
|
64
|
-
});
|
|
65
|
-
}
|
|
66
|
-
function ledgerFailure(code, error, appName, tag, params) {
|
|
61
|
+
function ledgerFailure(code, error, appName, tag, params, origin, recovery) {
|
|
67
62
|
const payload = { error, code };
|
|
68
63
|
if (appName !== void 0) payload.appName = appName;
|
|
69
64
|
if (tag !== void 0) payload._tag = tag;
|
|
70
65
|
if (params !== void 0) payload.params = params;
|
|
66
|
+
const resolvedOrigin = origin ?? (0, import_hwk_adapter_core.defaultOriginForCode)(code);
|
|
67
|
+
if (resolvedOrigin !== void 0) payload.origin = resolvedOrigin;
|
|
68
|
+
payload.recovery = recovery ?? (0, import_hwk_adapter_core.defaultRecoveryForCode)(code);
|
|
71
69
|
return { success: false, payload };
|
|
72
70
|
}
|
|
73
71
|
var LOCKED_ERROR_CODES = /* @__PURE__ */ new Set(["5515", "21781", "6982", "27010", "5303", "21251"]);
|
|
@@ -208,7 +206,10 @@ var ERROR_TAG = {
|
|
|
208
206
|
UnknownDevice: "UnknownDeviceError",
|
|
209
207
|
DeviceSessionRefresher: "DeviceSessionRefresherError",
|
|
210
208
|
DeviceNotInitialized: "DeviceNotInitializedError",
|
|
211
|
-
|
|
209
|
+
// DMK class OpeningConnectionError carries `_tag` "ConnectionOpeningError"; both
|
|
210
|
+
// spellings are kept so a DMK release that aligns them still classifies.
|
|
211
|
+
OpeningConnection: "ConnectionOpeningError",
|
|
212
|
+
OpeningConnectionLegacy: "OpeningConnectionError",
|
|
212
213
|
DeviceDisconnectedBeforeSendingApdu: "DeviceDisconnectedBeforeSendingApdu",
|
|
213
214
|
DeviceDisconnectedWhileSending: "DeviceDisconnectedWhileSendingError",
|
|
214
215
|
Disconnect: "DisconnectError",
|
|
@@ -221,7 +222,16 @@ var ERROR_TAG = {
|
|
|
221
222
|
// DMK remote-network failures (manager-api HTTP / secure-channel WS).
|
|
222
223
|
WebSocketConnection: "WebSocketConnectionError",
|
|
223
224
|
HttpFetch: "FetchError",
|
|
224
|
-
|
|
225
|
+
NetworkDA: "NetworkDAError",
|
|
226
|
+
InvalidFirmwareMetadataResponse: "InvalidGetFirmwareMetadataResponseError",
|
|
227
|
+
ApplicationsMetadataTask: "GetApplicationsMetadataTaskError",
|
|
228
|
+
// DMK OS device actions. SecureChannelError is what mapInstallDAErrors() leaves
|
|
229
|
+
// after splitting out device answers, i.e. the relay itself broke.
|
|
230
|
+
SecureChannel: "SecureChannelError",
|
|
231
|
+
RefusedByUserDA: "RefusedByUserDAError",
|
|
232
|
+
AppAlreadyInstalledDA: "AppAlreadyInstalledDAError",
|
|
233
|
+
OutOfMemoryDA: "OutOfMemoryDAError",
|
|
234
|
+
DeviceNotOnboarded: "DeviceNotOnboardedError"
|
|
225
235
|
};
|
|
226
236
|
function isDeviceLockedError(err) {
|
|
227
237
|
if (!err || typeof err !== "object") return false;
|
|
@@ -251,7 +261,6 @@ function isBlePairingFailureError(err) {
|
|
|
251
261
|
return false;
|
|
252
262
|
}
|
|
253
263
|
var CONNECTION_LEVEL_TAGS = /* @__PURE__ */ new Set([
|
|
254
|
-
ERROR_TAG.DeviceLocked,
|
|
255
264
|
ERROR_TAG.DeviceNotAdvertising,
|
|
256
265
|
ERROR_TAG.BlePairingTimeout,
|
|
257
266
|
ERROR_TAG.BleGattBondingFailed,
|
|
@@ -263,6 +272,7 @@ var CONNECTION_LEVEL_TAGS = /* @__PURE__ */ new Set([
|
|
|
263
272
|
ERROR_TAG.DeviceSessionRefresher,
|
|
264
273
|
ERROR_TAG.DeviceNotInitialized,
|
|
265
274
|
ERROR_TAG.OpeningConnection,
|
|
275
|
+
ERROR_TAG.OpeningConnectionLegacy,
|
|
266
276
|
ERROR_TAG.DeviceDisconnectedBeforeSendingApdu,
|
|
267
277
|
ERROR_TAG.DeviceDisconnectedWhileSending,
|
|
268
278
|
ERROR_TAG.Disconnect,
|
|
@@ -277,7 +287,10 @@ var DEVICE_NOT_FOUND_TAGS = /* @__PURE__ */ new Set([
|
|
|
277
287
|
// Map to DeviceNotFound so non-BLE-direct paths get a sensible error code.
|
|
278
288
|
ERROR_TAG.DeviceNotInDiscoveryCache
|
|
279
289
|
]);
|
|
280
|
-
var DEVICE_BUSY_TAGS = /* @__PURE__ */ new Set([
|
|
290
|
+
var DEVICE_BUSY_TAGS = /* @__PURE__ */ new Set([
|
|
291
|
+
ERROR_TAG.OpeningConnection,
|
|
292
|
+
ERROR_TAG.OpeningConnectionLegacy
|
|
293
|
+
]);
|
|
281
294
|
var DEVICE_DISCONNECTED_TAGS = /* @__PURE__ */ new Set([
|
|
282
295
|
ERROR_TAG.DeviceNotRecognized,
|
|
283
296
|
ERROR_TAG.DeviceSessionNotFound,
|
|
@@ -289,17 +302,18 @@ var DEVICE_DISCONNECTED_TAGS = /* @__PURE__ */ new Set([
|
|
|
289
302
|
ERROR_TAG.WebHIDDisconnect
|
|
290
303
|
]);
|
|
291
304
|
function isConnectionLevelError(err) {
|
|
292
|
-
|
|
293
|
-
const tag = err._tag;
|
|
294
|
-
if (tag && CONNECTION_LEVEL_TAGS.has(tag)) return true;
|
|
295
|
-
const e = err;
|
|
296
|
-
if (e.originalError != null && isConnectionLevelError(e.originalError)) return true;
|
|
297
|
-
if (e.error != null && e._tag && isConnectionLevelError(e.error)) return true;
|
|
298
|
-
return false;
|
|
305
|
+
return hasErrorTag(err, CONNECTION_LEVEL_TAGS);
|
|
299
306
|
}
|
|
300
307
|
function isKnownConnectionTag(tag) {
|
|
301
308
|
return typeof tag === "string" && CONNECTION_LEVEL_TAGS.has(tag);
|
|
302
309
|
}
|
|
310
|
+
var CONNECTION_OPENING_TAGS = /* @__PURE__ */ new Set([
|
|
311
|
+
ERROR_TAG.OpeningConnection,
|
|
312
|
+
ERROR_TAG.OpeningConnectionLegacy
|
|
313
|
+
]);
|
|
314
|
+
function isConnectionOpeningTag(tag) {
|
|
315
|
+
return typeof tag === "string" && CONNECTION_OPENING_TAGS.has(tag);
|
|
316
|
+
}
|
|
303
317
|
function hasStatusCode(err, codeSet) {
|
|
304
318
|
if (!err || typeof err !== "object") return false;
|
|
305
319
|
const e = err;
|
|
@@ -335,28 +349,28 @@ function hasInvalidArgumentCode(err) {
|
|
|
335
349
|
if (e.error != null && e._tag && hasInvalidArgumentCode(e.error)) return true;
|
|
336
350
|
return false;
|
|
337
351
|
}
|
|
338
|
-
function
|
|
352
|
+
function hasErrorTag(err, tags) {
|
|
339
353
|
if (!err || typeof err !== "object") return false;
|
|
340
|
-
const tag = err._tag;
|
|
341
|
-
if (tag && DEVICE_NOT_FOUND_TAGS.has(tag)) return true;
|
|
342
354
|
const e = err;
|
|
343
|
-
if (e.
|
|
344
|
-
if (e.
|
|
355
|
+
if (typeof e._tag === "string" && tags.has(e._tag)) return true;
|
|
356
|
+
if (e.originalError != null && hasErrorTag(e.originalError, tags)) return true;
|
|
357
|
+
if (e.error != null && e._tag && hasErrorTag(e.error, tags)) return true;
|
|
345
358
|
return false;
|
|
346
359
|
}
|
|
360
|
+
function isDeviceNotFoundError(err) {
|
|
361
|
+
return hasErrorTag(err, DEVICE_NOT_FOUND_TAGS);
|
|
362
|
+
}
|
|
347
363
|
function isDeviceBusyError(err) {
|
|
348
|
-
|
|
349
|
-
const tag = err._tag;
|
|
350
|
-
if (tag && DEVICE_BUSY_TAGS.has(tag)) return true;
|
|
351
|
-
const e = err;
|
|
352
|
-
if (e.originalError != null && isDeviceBusyError(e.originalError)) return true;
|
|
353
|
-
if (e.error != null && e._tag && isDeviceBusyError(e.error)) return true;
|
|
354
|
-
return false;
|
|
364
|
+
return hasErrorTag(err, DEVICE_BUSY_TAGS);
|
|
355
365
|
}
|
|
366
|
+
var USER_REJECTED_TAGS = /* @__PURE__ */ new Set([
|
|
367
|
+
ERROR_TAG.UserRefusedOnDevice,
|
|
368
|
+
ERROR_TAG.RefusedByUserDA
|
|
369
|
+
]);
|
|
356
370
|
function isUserRejectedError(err) {
|
|
357
371
|
if (!err || typeof err !== "object") return false;
|
|
358
372
|
const e = err;
|
|
359
|
-
if (
|
|
373
|
+
if (hasErrorTag(err, USER_REJECTED_TAGS)) return true;
|
|
360
374
|
if (typeof e.message === "string" && /denied|rejected|refused/i.test(e.message)) return true;
|
|
361
375
|
if (hasStatusCode(err, USER_REJECTED_CODES)) return true;
|
|
362
376
|
return false;
|
|
@@ -387,15 +401,33 @@ function isAppNotInstalledError(err) {
|
|
|
387
401
|
if (hasStatusCode(err, APP_NOT_INSTALLED_CODES)) return true;
|
|
388
402
|
return false;
|
|
389
403
|
}
|
|
404
|
+
var OUT_OF_MEMORY_TAGS = /* @__PURE__ */ new Set([ERROR_TAG.OutOfMemoryDA]);
|
|
390
405
|
function isOutOfMemoryError(err) {
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
406
|
+
return hasErrorTag(err, OUT_OF_MEMORY_TAGS);
|
|
407
|
+
}
|
|
408
|
+
var APP_ALREADY_INSTALLED_TAGS = /* @__PURE__ */ new Set([ERROR_TAG.AppAlreadyInstalledDA]);
|
|
409
|
+
function isAppAlreadyInstalledError(err) {
|
|
410
|
+
return hasErrorTag(err, APP_ALREADY_INSTALLED_TAGS);
|
|
411
|
+
}
|
|
412
|
+
var DEVICE_NOT_ONBOARDED_TAGS = /* @__PURE__ */ new Set([ERROR_TAG.DeviceNotOnboarded]);
|
|
413
|
+
function isDeviceNotOnboardedError(err) {
|
|
414
|
+
return hasErrorTag(err, DEVICE_NOT_ONBOARDED_TAGS);
|
|
415
|
+
}
|
|
416
|
+
var FIRMWARE_METADATA_TAGS = /* @__PURE__ */ new Set([
|
|
417
|
+
ERROR_TAG.InvalidFirmwareMetadataResponse,
|
|
418
|
+
ERROR_TAG.ApplicationsMetadataTask
|
|
419
|
+
]);
|
|
420
|
+
function isFirmwareMetadataError(err) {
|
|
421
|
+
return hasErrorTag(err, FIRMWARE_METADATA_TAGS);
|
|
422
|
+
}
|
|
423
|
+
var SECURE_CHANNEL_TAGS = /* @__PURE__ */ new Set([ERROR_TAG.SecureChannel]);
|
|
424
|
+
function isSecureChannelError(err) {
|
|
425
|
+
return hasErrorTag(err, SECURE_CHANNEL_TAGS);
|
|
394
426
|
}
|
|
395
427
|
var NETWORK_ERROR_TAGS = /* @__PURE__ */ new Set([
|
|
396
428
|
ERROR_TAG.WebSocketConnection,
|
|
397
429
|
ERROR_TAG.HttpFetch,
|
|
398
|
-
ERROR_TAG.
|
|
430
|
+
ERROR_TAG.NetworkDA
|
|
399
431
|
]);
|
|
400
432
|
function isNetworkError(err) {
|
|
401
433
|
if (!err || typeof err !== "object") return false;
|
|
@@ -445,23 +477,34 @@ function isTransportStuck(err) {
|
|
|
445
477
|
function isStuckAppStateError(err) {
|
|
446
478
|
return isAppStuckByApdu(err) || isTransportStuck(err);
|
|
447
479
|
}
|
|
480
|
+
var DMK_PLACEHOLDER_MESSAGE = "Unknown error.";
|
|
481
|
+
function nestedErrorMessage(nested) {
|
|
482
|
+
if (!nested || typeof nested !== "object") return void 0;
|
|
483
|
+
const { message } = nested;
|
|
484
|
+
if (typeof message !== "string") return void 0;
|
|
485
|
+
const trimmed = message.trim();
|
|
486
|
+
if (!trimmed || trimmed === DMK_PLACEHOLDER_MESSAGE) return void 0;
|
|
487
|
+
return message;
|
|
488
|
+
}
|
|
448
489
|
function mapLedgerError(err, opts) {
|
|
449
490
|
let originalMessage = "Unknown Ledger error";
|
|
450
491
|
if (err instanceof Error) {
|
|
451
492
|
originalMessage = err.message;
|
|
452
493
|
} else if (err && typeof err === "object") {
|
|
453
494
|
const e = err;
|
|
454
|
-
originalMessage = String(
|
|
495
|
+
originalMessage = String(
|
|
496
|
+
e.message ?? nestedErrorMessage(e.originalError) ?? e._tag ?? e.type ?? JSON.stringify(err)
|
|
497
|
+
);
|
|
455
498
|
}
|
|
456
499
|
let code;
|
|
457
500
|
if (isDeviceLockedError(err)) {
|
|
458
501
|
code = import_hwk_adapter_core.HardwareErrorCode.DeviceLocked;
|
|
459
502
|
} else if (isDeviceNotAdvertisingError(err) || isDeviceNotFoundError(err)) {
|
|
460
503
|
code = import_hwk_adapter_core.HardwareErrorCode.DeviceNotFound;
|
|
461
|
-
} else if (isDeviceBusyError(err)) {
|
|
462
|
-
code = import_hwk_adapter_core.HardwareErrorCode.DeviceBusy;
|
|
463
504
|
} else if (isBlePairingFailureError(err)) {
|
|
464
505
|
code = import_hwk_adapter_core.HardwareErrorCode.BlePairingTimeout;
|
|
506
|
+
} else if (isDeviceBusyError(err)) {
|
|
507
|
+
code = import_hwk_adapter_core.HardwareErrorCode.DeviceBusy;
|
|
465
508
|
} else if (isUserAbortedError(err)) {
|
|
466
509
|
code = import_hwk_adapter_core.HardwareErrorCode.UserAborted;
|
|
467
510
|
} else if (isUserRejectedError(err)) {
|
|
@@ -470,10 +513,18 @@ function mapLedgerError(err, opts) {
|
|
|
470
513
|
code = import_hwk_adapter_core.HardwareErrorCode.WrongApp;
|
|
471
514
|
} else if (isAppNotInstalledError(err)) {
|
|
472
515
|
code = import_hwk_adapter_core.HardwareErrorCode.AppNotInstalled;
|
|
516
|
+
} else if (isAppAlreadyInstalledError(err)) {
|
|
517
|
+
code = import_hwk_adapter_core.HardwareErrorCode.AppAlreadyInstalled;
|
|
473
518
|
} else if (isOutOfMemoryError(err)) {
|
|
474
519
|
code = import_hwk_adapter_core.HardwareErrorCode.DeviceOutOfMemory;
|
|
520
|
+
} else if (isDeviceNotOnboardedError(err)) {
|
|
521
|
+
code = import_hwk_adapter_core.HardwareErrorCode.DeviceNotInitialized;
|
|
522
|
+
} else if (isFirmwareMetadataError(err)) {
|
|
523
|
+
code = import_hwk_adapter_core.HardwareErrorCode.LedgerFirmwareMetadataError;
|
|
475
524
|
} else if (isNetworkError(err)) {
|
|
476
525
|
code = import_hwk_adapter_core.HardwareErrorCode.NetworkError;
|
|
526
|
+
} else if (isSecureChannelError(err)) {
|
|
527
|
+
code = import_hwk_adapter_core.HardwareErrorCode.LedgerSecureChannelError;
|
|
477
528
|
} else if (isDeviceDisconnectedError(err)) {
|
|
478
529
|
code = import_hwk_adapter_core.HardwareErrorCode.DeviceDisconnected;
|
|
479
530
|
} else if (isTimeoutError(err)) {
|
|
@@ -486,7 +537,21 @@ function mapLedgerError(err, opts) {
|
|
|
486
537
|
}
|
|
487
538
|
const errAppName = err && typeof err === "object" ? err.appName : void 0;
|
|
488
539
|
const appName = errAppName ?? opts?.defaultAppName;
|
|
489
|
-
return {
|
|
540
|
+
return {
|
|
541
|
+
code,
|
|
542
|
+
message: (0, import_hwk_adapter_core.enrichErrorMessage)(code, originalMessage),
|
|
543
|
+
origin: (0, import_hwk_adapter_core.defaultOriginForCode)(code),
|
|
544
|
+
appName
|
|
545
|
+
};
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
// src/utils/queueKey.ts
|
|
549
|
+
var LEDGER_DEFAULT_QUEUE_KEY = "__ledger_default__";
|
|
550
|
+
function ledgerQueueKey({
|
|
551
|
+
operationId,
|
|
552
|
+
connectId
|
|
553
|
+
}) {
|
|
554
|
+
return (operationId ?? connectId) || LEDGER_DEFAULT_QUEUE_KEY;
|
|
490
555
|
}
|
|
491
556
|
|
|
492
557
|
// src/adapter/methods/allNetworkGetAddress.ts
|
|
@@ -547,65 +612,107 @@ var LEDGER_BTC_NETWORK_COIN_MAP = {
|
|
|
547
612
|
var LEDGER_UNSUPPORTED_ALLNETWORK_NETWORKS = /* @__PURE__ */ new Set(["doge", "dogecoin"]);
|
|
548
613
|
function createAllNetworkGetAddress({
|
|
549
614
|
callChain,
|
|
550
|
-
getChainFingerprint
|
|
615
|
+
getChainFingerprint,
|
|
616
|
+
retainOperation,
|
|
617
|
+
errorToFailure,
|
|
618
|
+
createCancelScope,
|
|
619
|
+
releaseBundle
|
|
551
620
|
}) {
|
|
552
621
|
return async function allNetworkGetAddress(connectId, _deviceId, params) {
|
|
553
|
-
debugLog("[LedgerAdapter][REQ]", {
|
|
554
|
-
|
|
622
|
+
debugLog("[LedgerAdapter][REQ]", {
|
|
623
|
+
method: "allNetworkGetAddress",
|
|
624
|
+
connectId,
|
|
625
|
+
itemCount: params.bundle.length
|
|
626
|
+
});
|
|
627
|
+
const target = (0, import_hwk_adapter_core2.resolveHardwareOperationTarget)(connectId, params.operationId, "ledger");
|
|
628
|
+
if (!target.success) return target;
|
|
629
|
+
const effectiveTargetId = target.payload.targetId ?? "";
|
|
630
|
+
let releaseOperationRetention;
|
|
631
|
+
try {
|
|
632
|
+
releaseOperationRetention = target.payload.operationId ? retainOperation(target.payload.operationId) : void 0;
|
|
633
|
+
} catch (error) {
|
|
634
|
+
return errorToFailure(error);
|
|
635
|
+
}
|
|
636
|
+
const cancelScope = createCancelScope(
|
|
637
|
+
ledgerQueueKey({ operationId: target.payload.operationId, connectId: effectiveTargetId })
|
|
638
|
+
);
|
|
639
|
+
const installContext = {
|
|
640
|
+
walletCheck: { expected: params.bundle.some((item) => Boolean(getItemDeviceId(item))) }
|
|
641
|
+
};
|
|
555
642
|
const commonParams = {
|
|
556
|
-
autoInstallApp: params.autoInstallApp
|
|
643
|
+
autoInstallApp: params.autoInstallApp,
|
|
644
|
+
operationId: target.payload.operationId,
|
|
645
|
+
knownConnections: params.knownConnections,
|
|
646
|
+
extra: params.extra,
|
|
647
|
+
allowDeviceSelection: params.allowDeviceSelection,
|
|
648
|
+
supportedTransports: params.supportedTransports
|
|
557
649
|
};
|
|
558
650
|
const chainFingerprints = /* @__PURE__ */ new Map();
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
651
|
+
try {
|
|
652
|
+
const result = await (0, import_hwk_adapter_core2.runAllNetworkGetAddress)({
|
|
653
|
+
connectId: effectiveTargetId,
|
|
654
|
+
deviceId: _deviceId,
|
|
655
|
+
params,
|
|
656
|
+
normalizeItem: normalizeLedgerAllNetworkItem,
|
|
657
|
+
buildUnsupportedNetworkResponse: (item) => isUnsupportedLedgerAllNetworkNetwork(item) ? buildUnsupportedNetworkResponse(item) : void 0,
|
|
658
|
+
callItem: async ({ method, chain, item }) => {
|
|
659
|
+
if (cancelScope.signal.aborted) return buildCancelledFailure(cancelScope.signal);
|
|
660
|
+
const itemDeviceId = getItemDeviceId(item) ?? chainFingerprints.get(chain) ?? "";
|
|
661
|
+
return callAllNetworkMethod(
|
|
662
|
+
callChain,
|
|
663
|
+
effectiveTargetId,
|
|
664
|
+
itemDeviceId,
|
|
665
|
+
method,
|
|
666
|
+
item,
|
|
667
|
+
commonParams,
|
|
668
|
+
installContext
|
|
669
|
+
);
|
|
670
|
+
},
|
|
671
|
+
attachIdentity: async ({ item, chain, payload }) => attachLedgerIdentity(
|
|
672
|
+
getChainFingerprint,
|
|
673
|
+
effectiveTargetId,
|
|
572
674
|
item,
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
}
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
}
|
|
600
|
-
return result;
|
|
675
|
+
chain,
|
|
676
|
+
payload,
|
|
677
|
+
chainFingerprints,
|
|
678
|
+
installContext,
|
|
679
|
+
cancelScope.signal
|
|
680
|
+
),
|
|
681
|
+
shouldAbortBundle: isTopLevelAllNetworkFailure,
|
|
682
|
+
buildTopLevelFailure: (response) => {
|
|
683
|
+
const code = response.payload?.code ?? import_hwk_adapter_core2.HardwareErrorCode.DeviceMismatch;
|
|
684
|
+
return (0, import_hwk_adapter_core2.failure)(
|
|
685
|
+
code,
|
|
686
|
+
response.payload?.error ?? "All-network get-address aborted",
|
|
687
|
+
response.payload?.params
|
|
688
|
+
);
|
|
689
|
+
}
|
|
690
|
+
});
|
|
691
|
+
debugLog("[LedgerAdapter][RES]", {
|
|
692
|
+
method: "allNetworkGetAddress",
|
|
693
|
+
success: result.success,
|
|
694
|
+
payload: result
|
|
695
|
+
});
|
|
696
|
+
return result;
|
|
697
|
+
} finally {
|
|
698
|
+
await releaseBundle(installContext, cancelScope.signal).catch(() => void 0);
|
|
699
|
+
cancelScope.release();
|
|
700
|
+
releaseOperationRetention?.();
|
|
701
|
+
}
|
|
601
702
|
};
|
|
602
703
|
}
|
|
704
|
+
function buildCancelledFailure(signal) {
|
|
705
|
+
const reason = signal.reason;
|
|
706
|
+
const code = typeof reason?.code === "number" ? reason.code : import_hwk_adapter_core2.HardwareErrorCode.UserAborted;
|
|
707
|
+
const message = typeof reason?.message === "string" ? reason.message : "";
|
|
708
|
+
return (0, import_hwk_adapter_core2.failure)(code, message || "All-network get-address cancelled");
|
|
709
|
+
}
|
|
603
710
|
function isTopLevelAllNetworkFailure(response) {
|
|
604
711
|
if (response.success) {
|
|
605
712
|
return false;
|
|
606
713
|
}
|
|
607
714
|
const code = response.payload?.code;
|
|
608
|
-
return
|
|
715
|
+
return (0, import_hwk_adapter_core2.isWalletSafetyFailure)(code) || (0, import_hwk_adapter_core2.isUserRefusal)(code) || (0, import_hwk_adapter_core2.isConnectionLost)(code);
|
|
609
716
|
}
|
|
610
717
|
function getItemDeviceId(item) {
|
|
611
718
|
const { deviceId } = item;
|
|
@@ -635,8 +742,18 @@ function normalizeLedgerAllNetworkItem(method, item) {
|
|
|
635
742
|
const coin = LEDGER_BTC_NETWORK_COIN_MAP[item.network];
|
|
636
743
|
return coin ? { ...item, coin } : item;
|
|
637
744
|
}
|
|
638
|
-
async function attachLedgerIdentity(getChainFingerprint, connectId, item, chain, payload, chainFingerprints) {
|
|
639
|
-
const
|
|
745
|
+
async function attachLedgerIdentity(getChainFingerprint, connectId, item, chain, payload, chainFingerprints, context, cancelSignal) {
|
|
746
|
+
const knownFingerprint = getItemDeviceId(item) || chainFingerprints.get(chain) || "";
|
|
747
|
+
if (!knownFingerprint && cancelSignal.aborted) {
|
|
748
|
+
const cancelled = buildCancelledFailure(cancelSignal);
|
|
749
|
+
return { ...item, success: false, payload: cancelled.payload };
|
|
750
|
+
}
|
|
751
|
+
const fingerprint = knownFingerprint || await bootstrapChainFingerprint(
|
|
752
|
+
getChainFingerprint,
|
|
753
|
+
context.connection?.connectId ?? connectId,
|
|
754
|
+
chain,
|
|
755
|
+
context
|
|
756
|
+
);
|
|
640
757
|
if (!fingerprint) {
|
|
641
758
|
return buildFingerprintBootstrapFailure(item, chain);
|
|
642
759
|
}
|
|
@@ -657,8 +774,8 @@ async function attachLedgerIdentity(getChainFingerprint, connectId, item, chain,
|
|
|
657
774
|
}
|
|
658
775
|
};
|
|
659
776
|
}
|
|
660
|
-
async function bootstrapChainFingerprint(getChainFingerprint, connectId, chain) {
|
|
661
|
-
const response = await getChainFingerprint(connectId, chain);
|
|
777
|
+
async function bootstrapChainFingerprint(getChainFingerprint, connectId, chain, context) {
|
|
778
|
+
const response = await getChainFingerprint(connectId, chain, context);
|
|
662
779
|
return response.success ? response.payload : "";
|
|
663
780
|
}
|
|
664
781
|
function buildFingerprintBootstrapFailure(item, chain) {
|
|
@@ -751,6 +868,16 @@ function isLedgerBleDescriptor(connectionType, descriptor) {
|
|
|
751
868
|
function formatDeviceMismatchError(expected, actual) {
|
|
752
869
|
return `Wrong device: expected ${expected}, got ${actual}`;
|
|
753
870
|
}
|
|
871
|
+
var LOG_REDACTED_RESULT_KEYS = ["ufvk", "orchardFvk"];
|
|
872
|
+
function redactResultForLog(result) {
|
|
873
|
+
if (!result || typeof result !== "object") return result;
|
|
874
|
+
const keys = LOG_REDACTED_RESULT_KEYS.filter((key) => key in result);
|
|
875
|
+
if (!keys.length) return result;
|
|
876
|
+
return { ...result, ...Object.fromEntries(keys.map((key) => [key, "[redacted]"])) };
|
|
877
|
+
}
|
|
878
|
+
function isLostConnectionError(err) {
|
|
879
|
+
return isDeviceDisconnectedError(err) || isDeviceNotAdvertisingError(err) || isTimeoutError(err) || isConnectionLevelError(err);
|
|
880
|
+
}
|
|
754
881
|
var BTC_HIGH_INDEX_THRESHOLD = 100;
|
|
755
882
|
function btcAccountIndexFromPath(path) {
|
|
756
883
|
const segments = path.replace(/^m\//, "").split("/");
|
|
@@ -762,9 +889,35 @@ function btcAccountIndexFromPath(path) {
|
|
|
762
889
|
var _LedgerAdapter = class _LedgerAdapter {
|
|
763
890
|
constructor(connector, options) {
|
|
764
891
|
this.vendor = "ledger";
|
|
892
|
+
// Cancel frees the DMK action and its intent-queue slot, but cannot retract a
|
|
893
|
+
// confirmation screen the device is already showing.
|
|
894
|
+
this.cancelCapability = "stops-waiting";
|
|
765
895
|
this.emitter = new import_hwk_adapter_core3.TypedEventEmitter();
|
|
896
|
+
this._operations = new import_hwk_adapter_core3.OperationRegistry({
|
|
897
|
+
vendor: "ledger",
|
|
898
|
+
onEnded: (operation, reason) => {
|
|
899
|
+
this._discardPendingBinding(this._pendingOperationBindings.get(operation.operationId));
|
|
900
|
+
this._pendingOperationBindings.delete(operation.operationId);
|
|
901
|
+
this.emitter.emit(import_hwk_adapter_core3.SDK.OPERATION_ENDED, {
|
|
902
|
+
type: import_hwk_adapter_core3.SDK.OPERATION_ENDED,
|
|
903
|
+
payload: { operationId: operation.operationId, reason }
|
|
904
|
+
});
|
|
905
|
+
if (reason === "timeout") {
|
|
906
|
+
void this._releaseOperationConnection(operation);
|
|
907
|
+
}
|
|
908
|
+
}
|
|
909
|
+
});
|
|
766
910
|
this._discoveredDevices = /* @__PURE__ */ new Map();
|
|
767
911
|
this._sessions = /* @__PURE__ */ new Map();
|
|
912
|
+
// An operation's BLE pick is saved by its first call that gets past the wallet check.
|
|
913
|
+
this._pendingOperationBindings = /* @__PURE__ */ new Map();
|
|
914
|
+
// A one-shot call's unchecked BLE pick, by connectId. It outlives the call only
|
|
915
|
+
// through an unlock retry; any other failure drops it with its session.
|
|
916
|
+
this._pendingPickBindings = /* @__PURE__ */ new Map();
|
|
917
|
+
// Transport each session was opened on, by sessionId. The adapter-wide
|
|
918
|
+
// `_activeConnectionType` moves with every discovery and can go stale.
|
|
919
|
+
this._sessionTransports = /* @__PURE__ */ new Map();
|
|
920
|
+
this._selectedBleReconnectTargets = /* @__PURE__ */ new Map();
|
|
768
921
|
this._uiRegistry = new import_hwk_adapter_core3.UiRequestRegistry();
|
|
769
922
|
// BTC App rejects account index >= 100 unless display=true. Cached per
|
|
770
923
|
// adapter instance: first 100+ path asks the user once via UI request,
|
|
@@ -778,10 +931,32 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
778
931
|
this._deviceAuthenticityQueueTail = Promise.resolve();
|
|
779
932
|
// Shared across concurrent callers — only `cancel()` aborts.
|
|
780
933
|
this._doConnectAbortController = null;
|
|
934
|
+
this._unsettledConnectorOperations = /* @__PURE__ */ new Map();
|
|
935
|
+
this._connectorIdleWaiters = /* @__PURE__ */ new Set();
|
|
936
|
+
this._resetPromise = null;
|
|
937
|
+
this._stateGeneration = 0;
|
|
938
|
+
this._connectorTeardownTail = Promise.resolve();
|
|
939
|
+
this._pendingConnectorTeardowns = 0;
|
|
940
|
+
this._activeOperationJobs = /* @__PURE__ */ new Set();
|
|
941
|
+
this._pendingOperationDisconnects = /* @__PURE__ */ new Set();
|
|
781
942
|
this._installProgressLastEmittedValue = -Infinity;
|
|
782
943
|
this.allNetworkGetAddress = createAllNetworkGetAddress({
|
|
783
944
|
callChain: this.callChain.bind(this),
|
|
784
|
-
getChainFingerprint: (connectId, chain) =>
|
|
945
|
+
getChainFingerprint: async (connectId, chain, context) => {
|
|
946
|
+
try {
|
|
947
|
+
const fingerprint = await this._computeChainFingerprint(
|
|
948
|
+
chain,
|
|
949
|
+
(method, params) => this.connectorCall(connectId, method, params, void 0, void 0, void 0, context)
|
|
950
|
+
);
|
|
951
|
+
return (0, import_hwk_adapter_core3.success)(fingerprint);
|
|
952
|
+
} catch (error) {
|
|
953
|
+
return this.errorToFailure(error);
|
|
954
|
+
}
|
|
955
|
+
},
|
|
956
|
+
retainOperation: (operationId) => this._operations.retain(operationId),
|
|
957
|
+
errorToFailure: (error) => this.errorToFailure(error),
|
|
958
|
+
createCancelScope: (queueKey) => this._jobQueue.createCancelScope(queueKey),
|
|
959
|
+
releaseBundle: (context, signal) => this._dropUncheckedBundlePick(context, signal)
|
|
785
960
|
});
|
|
786
961
|
// ---------------------------------------------------------------------------
|
|
787
962
|
// Private helpers
|
|
@@ -791,7 +966,7 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
791
966
|
*
|
|
792
967
|
* - If a session already exists for the given connectId, reuse it.
|
|
793
968
|
* - If ANY session exists (Ledger IDs are ephemeral), reuse it.
|
|
794
|
-
* - Otherwise: search →
|
|
969
|
+
* - Otherwise: search → one USB device auto-connects; multiple asks the host to choose.
|
|
795
970
|
*/
|
|
796
971
|
// Mutex for ensureConnected — prevents concurrent calls from establishing duplicate connections
|
|
797
972
|
this._connectingPromise = null;
|
|
@@ -808,6 +983,12 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
808
983
|
});
|
|
809
984
|
};
|
|
810
985
|
this.deviceDisconnectHandler = (data) => {
|
|
986
|
+
const activeOperation = this._operations.findActiveByConnectionKey(data.connectId);
|
|
987
|
+
if (activeOperation && this._activeOperationJobs.has(activeOperation.operationId)) {
|
|
988
|
+
this._pendingOperationDisconnects.add(activeOperation.operationId);
|
|
989
|
+
} else {
|
|
990
|
+
this._operations.endByConnectionKey(data.connectId, "disconnect");
|
|
991
|
+
}
|
|
811
992
|
this._discoveredDevices.delete(data.connectId);
|
|
812
993
|
this._sessions.delete(data.connectId);
|
|
813
994
|
this.emitter.emit(import_hwk_adapter_core3.DEVICE.DISCONNECT, {
|
|
@@ -816,10 +997,8 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
816
997
|
});
|
|
817
998
|
};
|
|
818
999
|
// Forward connector `ui-event` to the public hw.emitter so consumers only
|
|
819
|
-
// need to subscribe in one place.
|
|
820
|
-
//
|
|
821
|
-
// exists (race during teardown) we drop. All other variants pass through
|
|
822
|
-
// unchanged.
|
|
1000
|
+
// need to subscribe in one place. The AppInstallProgress variant re-keys
|
|
1001
|
+
// sessionId to connectId via `_sessions`, and drops if no mapping exists.
|
|
823
1002
|
this.uiEventForwarder = (event) => {
|
|
824
1003
|
if (event.type === import_hwk_adapter_core3.EConnectorInteraction.AppInstallProgress) {
|
|
825
1004
|
let connectId;
|
|
@@ -864,11 +1043,34 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
864
1043
|
this._jobQueue = new import_hwk_adapter_core3.DeviceJobQueue();
|
|
865
1044
|
this.registerEventListeners();
|
|
866
1045
|
}
|
|
1046
|
+
_discardPendingBinding(owner, status = "cancelled") {
|
|
1047
|
+
const pending = owner?.pendingBinding;
|
|
1048
|
+
if (!owner || !pending) return;
|
|
1049
|
+
owner.pendingBinding = void 0;
|
|
1050
|
+
if (pending.requestId === this._bindingSelectionRequestId) this._finishBleBinding(status);
|
|
1051
|
+
}
|
|
1052
|
+
_finishBleBinding(status) {
|
|
1053
|
+
const selectionRequestId = this._bindingSelectionRequestId;
|
|
1054
|
+
this._bindingSelectionRequestId = void 0;
|
|
1055
|
+
if (selectionRequestId)
|
|
1056
|
+
this.emitter.emit(import_hwk_adapter_core3.UI_REQUEST.DEVICE_BINDING_STATUS, {
|
|
1057
|
+
type: import_hwk_adapter_core3.UI_REQUEST.DEVICE_BINDING_STATUS,
|
|
1058
|
+
payload: { selectionRequestId, status }
|
|
1059
|
+
});
|
|
1060
|
+
}
|
|
1061
|
+
_isBleConnection() {
|
|
1062
|
+
return isLedgerBleConnectionType(this._activeConnectionType ?? this.connector.connectionType);
|
|
1063
|
+
}
|
|
867
1064
|
// Transport
|
|
868
1065
|
get activeTransport() {
|
|
869
|
-
return
|
|
1066
|
+
return this._isBleConnection() ? "ble" : "hid";
|
|
870
1067
|
}
|
|
871
1068
|
getAvailableTransports() {
|
|
1069
|
+
if (this.connector.availableTransports) {
|
|
1070
|
+
return this.connector.availableTransports.map(
|
|
1071
|
+
(transport) => transport === "ble" ? "ble" : "hid"
|
|
1072
|
+
);
|
|
1073
|
+
}
|
|
872
1074
|
return this.activeTransport ? [this.activeTransport] : [];
|
|
873
1075
|
}
|
|
874
1076
|
// Connector is bound at construction; switching requires a new adapter.
|
|
@@ -885,21 +1087,42 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
885
1087
|
* The next operation will re-discover and re-connect automatically.
|
|
886
1088
|
*/
|
|
887
1089
|
resetState() {
|
|
1090
|
+
void this._resetStateAndDisconnectSessions();
|
|
1091
|
+
}
|
|
1092
|
+
_resetStateAndDisconnectSessions() {
|
|
1093
|
+
if (this._resetPromise) return this._resetPromise;
|
|
1094
|
+
const sessionIds = new Set(this._sessions.values());
|
|
1095
|
+
this._stateGeneration += 1;
|
|
1096
|
+
this._finishBleBinding("cancelled");
|
|
1097
|
+
this._operations.endAll("runtime-reset");
|
|
1098
|
+
this._doConnectAbortController?.abort();
|
|
888
1099
|
this._discoveredDevices.clear();
|
|
889
1100
|
this._sessions.clear();
|
|
1101
|
+
this._sessionTransports.clear();
|
|
1102
|
+
this._pendingOperationBindings.clear();
|
|
1103
|
+
this._pendingPickBindings.clear();
|
|
1104
|
+
this._selectedBleReconnectTargets.clear();
|
|
890
1105
|
this._connectingPromise = null;
|
|
1106
|
+
this._doConnectAbortController = null;
|
|
891
1107
|
this._uiRegistry.reset();
|
|
892
1108
|
this._jobQueue.clear();
|
|
893
1109
|
this._btcHighIndexConfirmedThisSession = false;
|
|
1110
|
+
const resetPromise = this._runConnectorTeardown(async () => {
|
|
1111
|
+
for (const sessionId of sessionIds) {
|
|
1112
|
+
await this.connector.disconnect(sessionId).catch(() => void 0);
|
|
1113
|
+
}
|
|
1114
|
+
});
|
|
1115
|
+
this._resetPromise = resetPromise;
|
|
1116
|
+
return resetPromise.finally(() => {
|
|
1117
|
+
if (this._resetPromise === resetPromise) {
|
|
1118
|
+
this._resetPromise = null;
|
|
1119
|
+
}
|
|
1120
|
+
});
|
|
894
1121
|
}
|
|
895
1122
|
async dispose() {
|
|
896
|
-
this.
|
|
897
|
-
this._jobQueue.clear();
|
|
1123
|
+
await this._resetStateAndDisconnectSessions();
|
|
898
1124
|
this.unregisterEventListeners();
|
|
899
1125
|
this.connector.reset();
|
|
900
|
-
this._discoveredDevices.clear();
|
|
901
|
-
this._sessions.clear();
|
|
902
|
-
this._btcHighIndexConfirmedThisSession = false;
|
|
903
1126
|
this.emitter.removeAllListeners();
|
|
904
1127
|
}
|
|
905
1128
|
uiResponse(response) {
|
|
@@ -909,17 +1132,32 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
909
1132
|
// Device management
|
|
910
1133
|
// ---------------------------------------------------------------------------
|
|
911
1134
|
async searchDevices(options) {
|
|
1135
|
+
return this._searchDevices(options);
|
|
1136
|
+
}
|
|
1137
|
+
async _searchDevices(options, signal) {
|
|
912
1138
|
debugLog("[LedgerAdapter][REQ]", { method: "searchDevices", params: options });
|
|
913
1139
|
try {
|
|
914
1140
|
if (options?.resetSession) {
|
|
915
|
-
this.
|
|
916
|
-
|
|
917
|
-
this.
|
|
918
|
-
|
|
919
|
-
|
|
1141
|
+
await this._resetStateAndDisconnectSessions();
|
|
1142
|
+
} else {
|
|
1143
|
+
await this._connectorTeardownTail;
|
|
1144
|
+
}
|
|
1145
|
+
await this._ensureDevicePermission(void 0, void 0, signal);
|
|
1146
|
+
if (signal) _LedgerAdapter._throwIfAborted(signal);
|
|
1147
|
+
const stateGeneration = this._stateGeneration;
|
|
1148
|
+
const devices = await this.connector.searchDevices(
|
|
1149
|
+
options?.transportType ? {
|
|
1150
|
+
transportType: options.transportType,
|
|
1151
|
+
waitForAll: options.waitForAllTransports
|
|
1152
|
+
} : void 0
|
|
1153
|
+
);
|
|
1154
|
+
if (signal) _LedgerAdapter._throwIfAborted(signal);
|
|
1155
|
+
if (stateGeneration !== this._stateGeneration) {
|
|
1156
|
+
throw (0, import_hwk_adapter_core3.createHwkError)({
|
|
1157
|
+
code: import_hwk_adapter_core3.HardwareErrorCode.UserAborted,
|
|
1158
|
+
message: "Ledger discovery was reset"
|
|
1159
|
+
});
|
|
920
1160
|
}
|
|
921
|
-
await this._ensureDevicePermission();
|
|
922
|
-
const devices = await this.connector.searchDevices();
|
|
923
1161
|
this._discoveredDevices.clear();
|
|
924
1162
|
for (const d of devices) {
|
|
925
1163
|
if (d.connectId) {
|
|
@@ -927,7 +1165,7 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
927
1165
|
}
|
|
928
1166
|
}
|
|
929
1167
|
if (this._discoveredDevices.size === 0) {
|
|
930
|
-
await this._ensureDevicePermission();
|
|
1168
|
+
await this._ensureDevicePermission(void 0, void 0, signal);
|
|
931
1169
|
}
|
|
932
1170
|
const result = Array.from(this._discoveredDevices.values());
|
|
933
1171
|
debugLog("[LedgerAdapter][RES]", {
|
|
@@ -946,41 +1184,280 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
946
1184
|
throw err;
|
|
947
1185
|
}
|
|
948
1186
|
}
|
|
1187
|
+
async searchDeviceTargets(options) {
|
|
1188
|
+
const devices = await this.searchDevices(options);
|
|
1189
|
+
return devices.map((device) => ({
|
|
1190
|
+
searchTargetId: device.connectId,
|
|
1191
|
+
searchTargetReusePolicy: (0, import_hwk_adapter_core3.resolveSearchTargetReusePolicy)(device),
|
|
1192
|
+
vendor: "ledger",
|
|
1193
|
+
connectionType: device.connectionType,
|
|
1194
|
+
kind: "physical",
|
|
1195
|
+
label: device.label,
|
|
1196
|
+
model: device.model,
|
|
1197
|
+
modelName: device.modelName,
|
|
1198
|
+
serialNumber: device.serialNumber
|
|
1199
|
+
}));
|
|
1200
|
+
}
|
|
1201
|
+
async listConnectionTargets(options) {
|
|
1202
|
+
const targets = await this.searchDeviceTargets(options);
|
|
1203
|
+
return targets.map(({ searchTargetId, ...target }) => ({
|
|
1204
|
+
...target,
|
|
1205
|
+
targetId: searchTargetId
|
|
1206
|
+
}));
|
|
1207
|
+
}
|
|
949
1208
|
// USB single-session invariant: evict all sessions, best-effort (see connectDevice).
|
|
950
|
-
async _evictAllSessions() {
|
|
1209
|
+
async _evictAllSessions(preserveOperationId) {
|
|
1210
|
+
this._operations.endAll("explicit", preserveOperationId);
|
|
951
1211
|
if (this._sessions.size === 0) return;
|
|
952
1212
|
const stale = [...this._sessions.values()];
|
|
953
1213
|
this._sessions.clear();
|
|
954
|
-
for (const sid of stale)
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
1214
|
+
for (const sid of stale) this._sessionTransports.delete(sid);
|
|
1215
|
+
await this._runConnectorTeardown(async () => {
|
|
1216
|
+
for (const sid of stale) {
|
|
1217
|
+
try {
|
|
1218
|
+
await this.connector.disconnect(sid);
|
|
1219
|
+
} catch {
|
|
1220
|
+
}
|
|
958
1221
|
}
|
|
959
|
-
}
|
|
1222
|
+
});
|
|
960
1223
|
}
|
|
961
1224
|
static _createDeviceBusyError(method) {
|
|
962
1225
|
return Object.assign(new Error(`Ledger device is busy while calling ${method}`), {
|
|
963
1226
|
code: import_hwk_adapter_core3.HardwareErrorCode.DeviceBusy
|
|
964
1227
|
});
|
|
965
1228
|
}
|
|
966
|
-
async connectDevice(
|
|
1229
|
+
async connectDevice(searchTargetId) {
|
|
1230
|
+
try {
|
|
1231
|
+
return await this._jobQueue.enqueue(
|
|
1232
|
+
searchTargetId || "__ledger_connect__",
|
|
1233
|
+
async (signal) => {
|
|
1234
|
+
const connected = await this._connectTarget(searchTargetId, void 0, signal);
|
|
1235
|
+
if (!connected.success) return connected;
|
|
1236
|
+
return this._createOperation(searchTargetId, connected.payload);
|
|
1237
|
+
},
|
|
1238
|
+
{
|
|
1239
|
+
label: "connectDevice",
|
|
1240
|
+
rejectIfBusy: true,
|
|
1241
|
+
busyError: _LedgerAdapter._createDeviceBusyError("connectDevice")
|
|
1242
|
+
}
|
|
1243
|
+
);
|
|
1244
|
+
} catch (error) {
|
|
1245
|
+
return this.errorToFailure(error);
|
|
1246
|
+
}
|
|
1247
|
+
}
|
|
1248
|
+
/** Binds the picked BLE device; with a recorded wallet identity it must match first. */
|
|
1249
|
+
async bindBleDevice(params) {
|
|
1250
|
+
const identity = params.identity?.vendor === "ledger" ? params.identity : void 0;
|
|
1251
|
+
try {
|
|
1252
|
+
return await this._jobQueue.enqueue(
|
|
1253
|
+
"__ledger_bind_ble__",
|
|
1254
|
+
async (signal) => {
|
|
1255
|
+
if (!this.getAvailableTransports().includes("ble")) {
|
|
1256
|
+
throw (0, import_hwk_adapter_core3.createHwkError)({
|
|
1257
|
+
code: import_hwk_adapter_core3.HardwareErrorCode.TransportNotAvailable,
|
|
1258
|
+
message: "Ledger Bluetooth transport is not available"
|
|
1259
|
+
});
|
|
1260
|
+
}
|
|
1261
|
+
this._activeConnectionType = "ble";
|
|
1262
|
+
await this._ensureDevicePermission(void 0, void 0, signal);
|
|
1263
|
+
const attempt = {
|
|
1264
|
+
extra: params.extra,
|
|
1265
|
+
bindingReason: "manual-rebind",
|
|
1266
|
+
deferBindingSave: Boolean(identity)
|
|
1267
|
+
};
|
|
1268
|
+
try {
|
|
1269
|
+
const connectId = await this._connectFirstOrSelect(
|
|
1270
|
+
[],
|
|
1271
|
+
void 0,
|
|
1272
|
+
true,
|
|
1273
|
+
void 0,
|
|
1274
|
+
attempt,
|
|
1275
|
+
signal
|
|
1276
|
+
);
|
|
1277
|
+
const pending = attempt.pendingBinding;
|
|
1278
|
+
if (identity && pending) {
|
|
1279
|
+
attempt.pendingBinding = void 0;
|
|
1280
|
+
const sessionId = this._sessions.get(connectId);
|
|
1281
|
+
if (!sessionId) {
|
|
1282
|
+
throw (0, import_hwk_adapter_core3.createHwkError)({
|
|
1283
|
+
code: import_hwk_adapter_core3.HardwareErrorCode.DeviceDisconnected,
|
|
1284
|
+
message: "Selected Ledger connection ended"
|
|
1285
|
+
});
|
|
1286
|
+
}
|
|
1287
|
+
const fingerprint = await this._computeChainFingerprint(
|
|
1288
|
+
identity.chain,
|
|
1289
|
+
(method, callParams) => this._runConnectorCall(
|
|
1290
|
+
connectId,
|
|
1291
|
+
method,
|
|
1292
|
+
callParams,
|
|
1293
|
+
signal,
|
|
1294
|
+
void 0,
|
|
1295
|
+
void 0,
|
|
1296
|
+
{ autoInstallApp: true },
|
|
1297
|
+
{ connection: { connectId, sessionId } }
|
|
1298
|
+
)
|
|
1299
|
+
);
|
|
1300
|
+
if (fingerprint !== identity.value) {
|
|
1301
|
+
await this._dropSession(connectId, signal);
|
|
1302
|
+
throw (0, import_hwk_adapter_core3.createHwkError)({
|
|
1303
|
+
code: import_hwk_adapter_core3.HardwareErrorCode.DeviceMismatch,
|
|
1304
|
+
message: formatDeviceMismatchError(identity.value, fingerprint)
|
|
1305
|
+
});
|
|
1306
|
+
}
|
|
1307
|
+
await this._saveSelectedBleBinding(
|
|
1308
|
+
connectId,
|
|
1309
|
+
pending.requestId,
|
|
1310
|
+
attempt,
|
|
1311
|
+
void 0,
|
|
1312
|
+
signal,
|
|
1313
|
+
identity
|
|
1314
|
+
);
|
|
1315
|
+
}
|
|
1316
|
+
if (!attempt.bindingSaved) {
|
|
1317
|
+
await this._dropSession(connectId, signal);
|
|
1318
|
+
throw (0, import_hwk_adapter_core3.createHwkError)({
|
|
1319
|
+
code: import_hwk_adapter_core3.HardwareErrorCode.UnknownError,
|
|
1320
|
+
message: "Bluetooth binding could not be saved",
|
|
1321
|
+
origin: "host"
|
|
1322
|
+
});
|
|
1323
|
+
}
|
|
1324
|
+
return (0, import_hwk_adapter_core3.success)(connectId);
|
|
1325
|
+
} catch (error) {
|
|
1326
|
+
this._finishBleBinding(signal.aborted ? "cancelled" : "failed");
|
|
1327
|
+
throw error;
|
|
1328
|
+
}
|
|
1329
|
+
},
|
|
1330
|
+
{
|
|
1331
|
+
label: "bindBleDevice",
|
|
1332
|
+
rejectIfBusy: true,
|
|
1333
|
+
busyError: _LedgerAdapter._createDeviceBusyError("bindBleDevice")
|
|
1334
|
+
}
|
|
1335
|
+
);
|
|
1336
|
+
} catch (error) {
|
|
1337
|
+
return this.errorToFailure(error);
|
|
1338
|
+
}
|
|
1339
|
+
}
|
|
1340
|
+
async acquireOperation(connectId, context) {
|
|
1341
|
+
try {
|
|
1342
|
+
return await this._jobQueue.enqueue(
|
|
1343
|
+
connectId || "__ledger_acquire__",
|
|
1344
|
+
async (signal) => {
|
|
1345
|
+
const transport = this._isBleConnection() ? "ble" : "usb";
|
|
1346
|
+
const hint = context.knownConnections?.find(
|
|
1347
|
+
(connection) => connection.transport === transport
|
|
1348
|
+
);
|
|
1349
|
+
const target = hint && hint.transport !== "qr" ? hint.connectId : connectId;
|
|
1350
|
+
await this._ensureDevicePermission(target, void 0, signal);
|
|
1351
|
+
const attempt = {
|
|
1352
|
+
...context,
|
|
1353
|
+
extra: context.extra ? { ...context.extra } : void 0,
|
|
1354
|
+
// The operation's calls decide whether the pick is the right wallet.
|
|
1355
|
+
deferBindingSave: true
|
|
1356
|
+
};
|
|
1357
|
+
try {
|
|
1358
|
+
const resolvedConnectId = await this.ensureConnected(
|
|
1359
|
+
target,
|
|
1360
|
+
signal,
|
|
1361
|
+
true,
|
|
1362
|
+
void 0,
|
|
1363
|
+
attempt
|
|
1364
|
+
);
|
|
1365
|
+
_LedgerAdapter._throwIfAborted(signal);
|
|
1366
|
+
const result = this._createOperation(connectId, resolvedConnectId);
|
|
1367
|
+
if (result.success && attempt.pendingBinding) {
|
|
1368
|
+
this._pendingOperationBindings.set(result.payload, attempt);
|
|
1369
|
+
}
|
|
1370
|
+
return result;
|
|
1371
|
+
} catch (error) {
|
|
1372
|
+
this._finishBleBinding(signal.aborted ? "cancelled" : "failed");
|
|
1373
|
+
throw error;
|
|
1374
|
+
}
|
|
1375
|
+
},
|
|
1376
|
+
{
|
|
1377
|
+
label: "acquireOperation",
|
|
1378
|
+
rejectIfBusy: true,
|
|
1379
|
+
busyError: _LedgerAdapter._createDeviceBusyError("acquireOperation")
|
|
1380
|
+
}
|
|
1381
|
+
);
|
|
1382
|
+
} catch (error) {
|
|
1383
|
+
return this.errorToFailure(error);
|
|
1384
|
+
}
|
|
1385
|
+
}
|
|
1386
|
+
_createOperation(searchTargetId, resolvedConnectId) {
|
|
1387
|
+
this._operations.endByConnectionKey(resolvedConnectId, "explicit");
|
|
1388
|
+
const sessionId = this._sessions.get(resolvedConnectId);
|
|
1389
|
+
if (sessionId) this._operations.endByConnectionKey(sessionId, "explicit");
|
|
1390
|
+
const connectionType = sessionId && this._sessionTransports.get(sessionId) || (this._isBleConnection() ? "ble" : "usb");
|
|
1391
|
+
const device = this._discoveredDevices.get(resolvedConnectId) ?? {
|
|
1392
|
+
vendor: "ledger",
|
|
1393
|
+
model: "unknown",
|
|
1394
|
+
firmwareVersion: "",
|
|
1395
|
+
deviceId: "",
|
|
1396
|
+
connectId: resolvedConnectId,
|
|
1397
|
+
connectionType
|
|
1398
|
+
};
|
|
1399
|
+
const operation = this._operations.create({
|
|
1400
|
+
searchTargetId,
|
|
1401
|
+
connectId: resolvedConnectId,
|
|
1402
|
+
device,
|
|
1403
|
+
connectionType,
|
|
1404
|
+
connectionKeys: [this._sessions.get(resolvedConnectId) ?? ""]
|
|
1405
|
+
});
|
|
1406
|
+
return (0, import_hwk_adapter_core3.success)(operation.operationId);
|
|
1407
|
+
}
|
|
1408
|
+
async _connectTarget(connectId, preserveOperationId, signal) {
|
|
967
1409
|
debugLog("[LedgerAdapter][REQ]", { method: "connectDevice", connectId, params: { connectId } });
|
|
968
1410
|
try {
|
|
969
|
-
|
|
1411
|
+
this._assertConnectorReady("connectDevice");
|
|
1412
|
+
const discoveredType = this._discoveredDevices.get(connectId)?.connectionType;
|
|
1413
|
+
if (discoveredType === "usb" || discoveredType === "ble") {
|
|
1414
|
+
this._activeConnectionType = discoveredType;
|
|
1415
|
+
}
|
|
1416
|
+
if (this._isBleConnection() && !connectId) {
|
|
970
1417
|
throw Object.assign(new Error("Ledger BLE connectId is required."), {
|
|
971
1418
|
code: import_hwk_adapter_core3.HardwareErrorCode.DeviceNotFound
|
|
972
1419
|
});
|
|
973
1420
|
}
|
|
974
|
-
|
|
975
|
-
|
|
1421
|
+
await this._ensureDevicePermission(connectId, void 0, signal);
|
|
1422
|
+
if (signal) _LedgerAdapter._throwIfAborted(signal);
|
|
1423
|
+
const isMultiTransport = (this.connector.availableTransports?.length ?? 0) > 1;
|
|
1424
|
+
if (!isMultiTransport && this._isBleConnection()) {
|
|
1425
|
+
const previousSessionId = this._sessions.get(connectId);
|
|
1426
|
+
this._operations.endByConnectionKey(connectId, "explicit", preserveOperationId);
|
|
1427
|
+
if (previousSessionId) {
|
|
1428
|
+
this._operations.endByConnectionKey(previousSessionId, "explicit", preserveOperationId);
|
|
1429
|
+
this._sessions.delete(connectId);
|
|
1430
|
+
await this.connector.disconnect(previousSessionId).catch(() => void 0);
|
|
1431
|
+
}
|
|
1432
|
+
} else {
|
|
1433
|
+
await this._evictAllSessions(preserveOperationId);
|
|
1434
|
+
}
|
|
1435
|
+
if (signal) _LedgerAdapter._throwIfAborted(signal);
|
|
1436
|
+
const stateGeneration = this._stateGeneration;
|
|
1437
|
+
const releaseOperation = this._retainConnectorOperation(`connect:${connectId}`);
|
|
1438
|
+
let session;
|
|
1439
|
+
this._connectingConnectId = connectId;
|
|
1440
|
+
try {
|
|
1441
|
+
session = this.connector.availableTransports?.length ? await this.connector.connect(connectId, {
|
|
1442
|
+
transportType: this._activeConnectionType ?? "usb"
|
|
1443
|
+
}) : await this.connector.connect(connectId);
|
|
1444
|
+
if (signal?.aborted || stateGeneration !== this._stateGeneration) {
|
|
1445
|
+
await this.connector.disconnect(session.sessionId).catch(() => void 0);
|
|
1446
|
+
throw Object.assign(new Error("Ledger connection aborted"), {
|
|
1447
|
+
code: import_hwk_adapter_core3.HardwareErrorCode.UserAborted
|
|
1448
|
+
});
|
|
1449
|
+
}
|
|
1450
|
+
} finally {
|
|
1451
|
+
if (this._connectingConnectId === connectId) this._connectingConnectId = void 0;
|
|
1452
|
+
releaseOperation();
|
|
976
1453
|
}
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
this.
|
|
1454
|
+
const resolvedConnectId = session.deviceInfo?.connectId || connectId;
|
|
1455
|
+
this._sessions.set(resolvedConnectId, session.sessionId);
|
|
1456
|
+
this._sessionTransports.set(session.sessionId, this._isBleConnection() ? "ble" : "usb");
|
|
980
1457
|
if (session.deviceInfo) {
|
|
981
|
-
this._discoveredDevices.set(
|
|
1458
|
+
this._discoveredDevices.set(resolvedConnectId, session.deviceInfo);
|
|
982
1459
|
}
|
|
983
|
-
const result = (0, import_hwk_adapter_core3.success)(
|
|
1460
|
+
const result = (0, import_hwk_adapter_core3.success)(resolvedConnectId);
|
|
984
1461
|
debugLog("[LedgerAdapter][RES]", { method: "connectDevice", success: true, payload: result });
|
|
985
1462
|
return result;
|
|
986
1463
|
} catch (err) {
|
|
@@ -993,30 +1470,60 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
993
1470
|
return failureResult;
|
|
994
1471
|
}
|
|
995
1472
|
}
|
|
996
|
-
async
|
|
1473
|
+
async releaseOperation(operationId) {
|
|
1474
|
+
const operation = this._operations.find(operationId);
|
|
1475
|
+
if (!operation) {
|
|
1476
|
+
this._operations.resolve(operationId);
|
|
1477
|
+
return;
|
|
1478
|
+
}
|
|
1479
|
+
const endedOperation = this._operations.end(operationId, "explicit");
|
|
1480
|
+
if (!endedOperation) return;
|
|
1481
|
+
const { connectId } = operation;
|
|
997
1482
|
debugLog("[LedgerAdapter][REQ]", {
|
|
998
|
-
method: "
|
|
1483
|
+
method: "releaseOperation",
|
|
999
1484
|
connectId,
|
|
1000
1485
|
params: { connectId }
|
|
1001
1486
|
});
|
|
1002
1487
|
try {
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
await this.connector.disconnect(sessionId);
|
|
1006
|
-
this._sessions.delete(connectId);
|
|
1007
|
-
}
|
|
1008
|
-
debugLog("[LedgerAdapter][RES]", { method: "disconnectDevice", success: true });
|
|
1488
|
+
await this._releaseOperationConnection(endedOperation);
|
|
1489
|
+
debugLog("[LedgerAdapter][RES]", { method: "releaseOperation", success: true });
|
|
1009
1490
|
} catch (err) {
|
|
1010
1491
|
const e = err;
|
|
1011
1492
|
debugLog("[LedgerAdapter][RES]", {
|
|
1012
|
-
method: "
|
|
1493
|
+
method: "releaseOperation",
|
|
1013
1494
|
success: false,
|
|
1014
1495
|
error: { message: e?.message, _tag: e?._tag, code: e?.code ?? e?.errorCode }
|
|
1015
1496
|
});
|
|
1016
1497
|
throw err;
|
|
1017
1498
|
}
|
|
1018
1499
|
}
|
|
1019
|
-
async
|
|
1500
|
+
async _releaseOperationConnection(operation) {
|
|
1501
|
+
const sessionIds = /* @__PURE__ */ new Set();
|
|
1502
|
+
for (const [connectId, sessionId] of this._sessions) {
|
|
1503
|
+
if (connectId === operation.connectId || operation.connectionKeys.includes(connectId) || operation.connectionKeys.includes(sessionId)) {
|
|
1504
|
+
this._sessions.delete(connectId);
|
|
1505
|
+
sessionIds.add(sessionId);
|
|
1506
|
+
}
|
|
1507
|
+
}
|
|
1508
|
+
for (const sessionId of sessionIds) {
|
|
1509
|
+
await this._runConnectorTeardown(
|
|
1510
|
+
() => this.connector.disconnect(sessionId).catch(() => void 0)
|
|
1511
|
+
);
|
|
1512
|
+
}
|
|
1513
|
+
}
|
|
1514
|
+
async _releaseLostOperationConnection(operationId) {
|
|
1515
|
+
const endedOperation = this._operations.end(operationId, "disconnect");
|
|
1516
|
+
if (!endedOperation) return;
|
|
1517
|
+
this._discoveredDevices.delete(endedOperation.connectId);
|
|
1518
|
+
await this._releaseOperationConnection(endedOperation);
|
|
1519
|
+
}
|
|
1520
|
+
async getDeviceInfo(connectIdOrOperationId, deviceId) {
|
|
1521
|
+
let connectId;
|
|
1522
|
+
try {
|
|
1523
|
+
connectId = (0, import_hwk_adapter_core3.isHardwareOperationId)(connectIdOrOperationId) ? this._operations.resolve(connectIdOrOperationId).connectId : connectIdOrOperationId;
|
|
1524
|
+
} catch (error) {
|
|
1525
|
+
return this.errorToFailure(error);
|
|
1526
|
+
}
|
|
1020
1527
|
debugLog("[LedgerAdapter][REQ]", {
|
|
1021
1528
|
method: "getDeviceInfo",
|
|
1022
1529
|
connectId,
|
|
@@ -1051,11 +1558,19 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
1051
1558
|
success: false,
|
|
1052
1559
|
error: { message: e?.message, _tag: e?._tag, code: e?.code ?? e?.errorCode }
|
|
1053
1560
|
});
|
|
1054
|
-
|
|
1561
|
+
return this.errorToFailure(err);
|
|
1055
1562
|
}
|
|
1056
1563
|
}
|
|
1057
|
-
|
|
1058
|
-
|
|
1564
|
+
/** A bundle's BLE pick that no item got past the wallet check is not kept. */
|
|
1565
|
+
async _dropUncheckedBundlePick(context, signal) {
|
|
1566
|
+
const { connection } = context;
|
|
1567
|
+
const owner = connection ? this._pendingPickBindings.get(connection.connectId) : void 0;
|
|
1568
|
+
if (!connection || !owner) return;
|
|
1569
|
+
this._pendingPickBindings.delete(connection.connectId);
|
|
1570
|
+
this._discardPendingBinding(owner, signal.aborted ? "cancelled" : "failed");
|
|
1571
|
+
if (this._sessions.get(connection.connectId) === connection.sessionId) {
|
|
1572
|
+
await this._dropSession(connection.connectId, signal);
|
|
1573
|
+
}
|
|
1059
1574
|
}
|
|
1060
1575
|
// ---------------------------------------------------------------------------
|
|
1061
1576
|
// Chain call helper
|
|
@@ -1091,13 +1606,23 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
1091
1606
|
if (params && typeof params === "object") {
|
|
1092
1607
|
const {
|
|
1093
1608
|
autoInstallApp,
|
|
1609
|
+
operationId,
|
|
1094
1610
|
passphraseState: _passphraseState,
|
|
1095
1611
|
useEmptyPassphrase: _useEmptyPassphrase,
|
|
1612
|
+
knownConnections,
|
|
1613
|
+
extra,
|
|
1614
|
+
allowDeviceSelection,
|
|
1615
|
+
supportedTransports,
|
|
1096
1616
|
...rest
|
|
1097
1617
|
} = params;
|
|
1098
1618
|
return {
|
|
1099
1619
|
commonParams: {
|
|
1100
|
-
autoInstallApp: typeof autoInstallApp === "boolean" ? autoInstallApp : void 0
|
|
1620
|
+
autoInstallApp: typeof autoInstallApp === "boolean" ? autoInstallApp : void 0,
|
|
1621
|
+
operationId: typeof operationId === "string" ? operationId : void 0,
|
|
1622
|
+
knownConnections,
|
|
1623
|
+
extra,
|
|
1624
|
+
allowDeviceSelection: typeof allowDeviceSelection === "boolean" ? allowDeviceSelection : void 0,
|
|
1625
|
+
supportedTransports
|
|
1101
1626
|
},
|
|
1102
1627
|
rest
|
|
1103
1628
|
};
|
|
@@ -1273,9 +1798,28 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
1273
1798
|
);
|
|
1274
1799
|
}
|
|
1275
1800
|
// ---------------------------------------------------------------------------
|
|
1276
|
-
//
|
|
1277
|
-
//
|
|
1278
|
-
|
|
1801
|
+
// Zcash chain methods (viewing key + shielded address reads; Zcash app >= 3.8.0)
|
|
1802
|
+
// ---------------------------------------------------------------------------
|
|
1803
|
+
zcashGetFullViewingKey(connectId, deviceId, params) {
|
|
1804
|
+
return this.callChainWithMergedParams(
|
|
1805
|
+
connectId,
|
|
1806
|
+
deviceId,
|
|
1807
|
+
"zcash",
|
|
1808
|
+
"zcashGetFullViewingKey",
|
|
1809
|
+
params
|
|
1810
|
+
);
|
|
1811
|
+
}
|
|
1812
|
+
zcashGetShieldedAddress(connectId, deviceId, params) {
|
|
1813
|
+
return this.callChainWithMergedParams(
|
|
1814
|
+
connectId,
|
|
1815
|
+
deviceId,
|
|
1816
|
+
"zcash",
|
|
1817
|
+
"zcashGetShieldedAddress",
|
|
1818
|
+
params
|
|
1819
|
+
);
|
|
1820
|
+
}
|
|
1821
|
+
// ---------------------------------------------------------------------------
|
|
1822
|
+
// App management: OS-level app install/list, no fingerprint or chain dispatch.
|
|
1279
1823
|
// ---------------------------------------------------------------------------
|
|
1280
1824
|
async installApp(connectId, appName) {
|
|
1281
1825
|
try {
|
|
@@ -1361,7 +1905,7 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
1361
1905
|
);
|
|
1362
1906
|
}
|
|
1363
1907
|
await this.connector.configure({ ledgerGenuineCheckWebSocketUrl: relayUrl });
|
|
1364
|
-
this.
|
|
1908
|
+
await this._resetStateAndDisconnectSessions();
|
|
1365
1909
|
}
|
|
1366
1910
|
const result = await this.connectorCall(connectId, "getDeviceGenuineCheck", {});
|
|
1367
1911
|
if (!result.isGenuine) {
|
|
@@ -1387,10 +1931,10 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
1387
1931
|
if (relayUrl) {
|
|
1388
1932
|
try {
|
|
1389
1933
|
await this.connector.configure?.({ ledgerGenuineCheckWebSocketUrl: void 0 });
|
|
1390
|
-
this.
|
|
1934
|
+
await this._resetStateAndDisconnectSessions();
|
|
1391
1935
|
} catch {
|
|
1392
1936
|
this.connector.reset();
|
|
1393
|
-
this.
|
|
1937
|
+
await this._resetStateAndDisconnectSessions();
|
|
1394
1938
|
}
|
|
1395
1939
|
}
|
|
1396
1940
|
}
|
|
@@ -1402,6 +1946,20 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
1402
1946
|
this.emitter.off(event, listener);
|
|
1403
1947
|
}
|
|
1404
1948
|
cancel(connectId) {
|
|
1949
|
+
const namedOperationIsLive = (id) => {
|
|
1950
|
+
try {
|
|
1951
|
+
this._operations.resolve(id);
|
|
1952
|
+
return true;
|
|
1953
|
+
} catch {
|
|
1954
|
+
return false;
|
|
1955
|
+
}
|
|
1956
|
+
};
|
|
1957
|
+
if ((0, import_hwk_adapter_core3.isHardwareOperationId)(connectId) && !namedOperationIsLive(connectId)) {
|
|
1958
|
+
debugLog("[LedgerAdapter] cancel target already ended; nothing to cancel", {
|
|
1959
|
+
connectId
|
|
1960
|
+
});
|
|
1961
|
+
return;
|
|
1962
|
+
}
|
|
1405
1963
|
const userAbortReason = Object.assign(new Error("User aborted operation"), {
|
|
1406
1964
|
code: import_hwk_adapter_core3.HardwareErrorCode.UserAborted,
|
|
1407
1965
|
_tag: ERROR_TAG.UserAborted
|
|
@@ -1412,14 +1970,48 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
1412
1970
|
this._lastCancelReason = void 0;
|
|
1413
1971
|
}
|
|
1414
1972
|
}, 2e3);
|
|
1415
|
-
this.
|
|
1416
|
-
|
|
1417
|
-
|
|
1973
|
+
const activeJobId = this._jobQueue.getActiveJob()?.deviceId;
|
|
1974
|
+
let operationId;
|
|
1975
|
+
if ((0, import_hwk_adapter_core3.isHardwareOperationId)(connectId)) {
|
|
1976
|
+
operationId = connectId;
|
|
1977
|
+
} else if (!connectId && (0, import_hwk_adapter_core3.isHardwareOperationId)(activeJobId)) {
|
|
1978
|
+
operationId = activeJobId;
|
|
1979
|
+
}
|
|
1980
|
+
let resolvedConnectId = connectId;
|
|
1981
|
+
if (operationId) {
|
|
1982
|
+
try {
|
|
1983
|
+
resolvedConnectId = this._operations.resolve(operationId).connectId;
|
|
1984
|
+
} catch {
|
|
1985
|
+
resolvedConnectId = void 0;
|
|
1986
|
+
}
|
|
1987
|
+
}
|
|
1988
|
+
const interactionForPhysicalId = !operationId && connectId ? this._operations.findActiveByConnectionKey(connectId) : void 0;
|
|
1989
|
+
const pendingOperationId = operationId ?? interactionForPhysicalId?.operationId;
|
|
1990
|
+
if (!connectId) {
|
|
1991
|
+
this._uiRegistry.cancel();
|
|
1992
|
+
} else if (pendingOperationId) {
|
|
1993
|
+
this._uiRegistry.cancel(void 0, void 0, pendingOperationId);
|
|
1994
|
+
}
|
|
1995
|
+
this._finishBleBinding("cancelled");
|
|
1996
|
+
if (!connectId) this._pendingOperationBindings.clear();
|
|
1997
|
+
else if (pendingOperationId) this._pendingOperationBindings.delete(pendingOperationId);
|
|
1998
|
+
const queueKeys = /* @__PURE__ */ new Set();
|
|
1999
|
+
if (connectId) queueKeys.add(ledgerQueueKey({ connectId }));
|
|
2000
|
+
if (pendingOperationId) queueKeys.add(ledgerQueueKey({ operationId: pendingOperationId }));
|
|
2001
|
+
if (queueKeys.size) {
|
|
2002
|
+
let cancelledAnyJob = false;
|
|
2003
|
+
for (const key of queueKeys) {
|
|
2004
|
+
cancelledAnyJob = this._jobQueue.cancelActiveAndPending(key, userAbortReason) || cancelledAnyJob;
|
|
2005
|
+
}
|
|
2006
|
+
debugLog("[LedgerAdapter] cancel routed to queue keys", {
|
|
2007
|
+
queueKeys: [...queueKeys],
|
|
2008
|
+
cancelledAnyJob
|
|
2009
|
+
});
|
|
1418
2010
|
} else {
|
|
1419
2011
|
this._jobQueue.cancelActiveAndPending(void 0, userAbortReason);
|
|
1420
2012
|
}
|
|
1421
|
-
if (
|
|
1422
|
-
const sessionId = this._sessions.get(
|
|
2013
|
+
if (resolvedConnectId) {
|
|
2014
|
+
const sessionId = this._sessions.get(resolvedConnectId) ?? resolvedConnectId;
|
|
1423
2015
|
void this.connector.cancel(sessionId);
|
|
1424
2016
|
} else {
|
|
1425
2017
|
for (const sid of this._sessions.values()) void this.connector.cancel(sid);
|
|
@@ -1427,47 +2019,88 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
1427
2019
|
if (this._connectingPromise) {
|
|
1428
2020
|
this._doConnectAbortController?.abort(userAbortReason);
|
|
1429
2021
|
}
|
|
2022
|
+
const connectingConnectId = this._connectingConnectId;
|
|
2023
|
+
if (connectingConnectId) void this.connector.cancel(connectingConnectId);
|
|
1430
2024
|
}
|
|
1431
2025
|
// ---------------------------------------------------------------------------
|
|
1432
2026
|
// Chain fingerprint
|
|
1433
2027
|
// ---------------------------------------------------------------------------
|
|
2028
|
+
/** A non-empty deviceId is an expected chain fingerprint, not a transport identifier. */
|
|
1434
2029
|
async getChainFingerprint(connectId, deviceId, chain) {
|
|
1435
2030
|
try {
|
|
1436
2031
|
const fingerprint = await this._computeChainFingerprint(
|
|
1437
2032
|
chain,
|
|
1438
2033
|
(method, params) => this.connectorCall(connectId, method, params, void 0, deviceId)
|
|
1439
2034
|
);
|
|
2035
|
+
if (deviceId) {
|
|
2036
|
+
if (fingerprint !== deviceId) {
|
|
2037
|
+
return (0, import_hwk_adapter_core3.failure)(
|
|
2038
|
+
import_hwk_adapter_core3.HardwareErrorCode.DeviceMismatch,
|
|
2039
|
+
formatDeviceMismatchError(deviceId, fingerprint)
|
|
2040
|
+
);
|
|
2041
|
+
}
|
|
2042
|
+
}
|
|
1440
2043
|
return (0, import_hwk_adapter_core3.success)(fingerprint);
|
|
1441
2044
|
} catch (err) {
|
|
1442
2045
|
debugError("[LedgerAdapter] getChainFingerprint error:", chain, err);
|
|
1443
2046
|
return this.errorToFailure(err);
|
|
1444
2047
|
}
|
|
1445
2048
|
}
|
|
1446
|
-
/**
|
|
1447
|
-
|
|
1448
|
-
* Safe to call inside connectorCall without causing queue deadlock.
|
|
1449
|
-
*/
|
|
1450
|
-
async _verifyDeviceFingerprintWithSession(sessionId, deviceId, chain) {
|
|
1451
|
-
if (!deviceId) return { success: true };
|
|
2049
|
+
/** Saves a picked BLE endpoint; `identity` is set when its wallet was just checked. */
|
|
2050
|
+
async _saveSelectedBleBinding(connectId, selectionRequestId, context, operationId, signal, identity) {
|
|
1452
2051
|
try {
|
|
1453
|
-
const
|
|
1454
|
-
|
|
1455
|
-
|
|
2052
|
+
const outcome = await (0, import_hwk_adapter_core3.requestSaveDeviceBinding)(
|
|
2053
|
+
this.emitter,
|
|
2054
|
+
this._uiRegistry,
|
|
2055
|
+
{
|
|
2056
|
+
selectionRequestId,
|
|
2057
|
+
connection: { transport: "ble", connectId },
|
|
2058
|
+
...identity ? { identity } : {},
|
|
2059
|
+
extra: context?.extra,
|
|
2060
|
+
operationId
|
|
2061
|
+
},
|
|
2062
|
+
signal
|
|
1456
2063
|
);
|
|
1457
|
-
if (
|
|
1458
|
-
|
|
2064
|
+
if (context) context.bindingSaved = outcome.saved;
|
|
2065
|
+
if (!outcome.saved) {
|
|
2066
|
+
debugLog("[LedgerAdapter] BLE binding not persisted by host", {
|
|
2067
|
+
connectId,
|
|
2068
|
+
reason: outcome.reason
|
|
2069
|
+
});
|
|
1459
2070
|
}
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
2071
|
+
} catch (error) {
|
|
2072
|
+
await this._dropSession(connectId, signal);
|
|
2073
|
+
throw error;
|
|
2074
|
+
} finally {
|
|
2075
|
+
if (this._bindingSelectionRequestId === selectionRequestId) {
|
|
2076
|
+
this._bindingSelectionRequestId = void 0;
|
|
1465
2077
|
}
|
|
1466
|
-
throw err;
|
|
1467
2078
|
}
|
|
1468
2079
|
}
|
|
1469
|
-
|
|
1470
|
-
|
|
2080
|
+
async _dropSession(connectId, signal) {
|
|
2081
|
+
const sessionId = this._sessions.get(connectId);
|
|
2082
|
+
this._sessions.delete(connectId);
|
|
2083
|
+
if (!sessionId) return;
|
|
2084
|
+
this._sessionTransports.delete(sessionId);
|
|
2085
|
+
const teardown = this._runConnectorTeardown(() => this.connector.disconnect(sessionId)).catch(
|
|
2086
|
+
() => void 0
|
|
2087
|
+
);
|
|
2088
|
+
if (!signal.aborted) await teardown;
|
|
2089
|
+
}
|
|
2090
|
+
/** Verify on the acquired session without re-entering the job queue. */
|
|
2091
|
+
async _verifyDeviceFingerprintWithSession(sessionId, deviceId, chain) {
|
|
2092
|
+
if (!deviceId) return { success: true };
|
|
2093
|
+
const fingerprint = await this._computeChainFingerprint(
|
|
2094
|
+
chain,
|
|
2095
|
+
(method, params) => this._callConnector(sessionId, method, params)
|
|
2096
|
+
);
|
|
2097
|
+
if (fingerprint === deviceId) {
|
|
2098
|
+
return { success: true };
|
|
2099
|
+
}
|
|
2100
|
+
return { success: false, expected: deviceId, actual: fingerprint };
|
|
2101
|
+
}
|
|
2102
|
+
/**
|
|
2103
|
+
* Compute the chain fingerprint via a caller-supplied call strategy.
|
|
1471
2104
|
*
|
|
1472
2105
|
* Chains with a native device-side identity primitive (BTC → BIP32 master
|
|
1473
2106
|
* fingerprint) short-circuit at the top and return it verbatim, so the value
|
|
@@ -1493,11 +2126,23 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
1493
2126
|
address = (await callMethod("solGetAddress", { path, showOnDevice: false })).address;
|
|
1494
2127
|
} else if (chain === "tron") {
|
|
1495
2128
|
address = (await callMethod("tronGetAddress", { path, showOnDevice: false })).address;
|
|
2129
|
+
} else if (chain === "zcash") {
|
|
2130
|
+
address = (await callMethod("zcashGetShieldedAddress", { path, showOnDevice: false })).address;
|
|
1496
2131
|
} else {
|
|
1497
2132
|
throw new Error(`Unsupported chain for fingerprint: ${chain}`);
|
|
1498
2133
|
}
|
|
1499
2134
|
return (0, import_hwk_adapter_core3.deriveDeviceFingerprint)(address);
|
|
1500
2135
|
}
|
|
2136
|
+
/**
|
|
2137
|
+
* Operation owning the running job, so a mid-call UI request can name it.
|
|
2138
|
+
* Undefined at cold start when no operation owns the job.
|
|
2139
|
+
*/
|
|
2140
|
+
_activeOperationId() {
|
|
2141
|
+
const activeJobId = this._jobQueue.getActiveJob()?.deviceId;
|
|
2142
|
+
if (!activeJobId) return void 0;
|
|
2143
|
+
if ((0, import_hwk_adapter_core3.isHardwareOperationId)(activeJobId)) return activeJobId;
|
|
2144
|
+
return this._operations.findActiveByConnectionKey(activeJobId)?.operationId;
|
|
2145
|
+
}
|
|
1501
2146
|
// Ledger WebUSB won't expose a locked device, so we can't auto-detect unlock.
|
|
1502
2147
|
// The user must press Confirm after unlocking, which triggers a search retry.
|
|
1503
2148
|
// If `signal` is provided, an abort cancels the pending UI request so the
|
|
@@ -1507,15 +2152,18 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
1507
2152
|
if (signal?.aborted) {
|
|
1508
2153
|
_LedgerAdapter._throwIfAborted(signal);
|
|
1509
2154
|
}
|
|
2155
|
+
const operationId = this._activeOperationId();
|
|
1510
2156
|
const waitPromise = this._uiRegistry.wait(
|
|
1511
|
-
import_hwk_adapter_core3.UI_REQUEST.REQUEST_DEVICE_CONNECT
|
|
2157
|
+
import_hwk_adapter_core3.UI_REQUEST.REQUEST_DEVICE_CONNECT,
|
|
2158
|
+
{ operationId }
|
|
1512
2159
|
);
|
|
1513
2160
|
this.emitter.emit(import_hwk_adapter_core3.UI_REQUEST.REQUEST_DEVICE_CONNECT, {
|
|
1514
2161
|
type: import_hwk_adapter_core3.UI_REQUEST.REQUEST_DEVICE_CONNECT,
|
|
1515
2162
|
payload: {
|
|
1516
2163
|
vendor: "ledger",
|
|
1517
2164
|
reason: "device-not-found",
|
|
1518
|
-
message: "Please connect and unlock your Ledger device"
|
|
2165
|
+
message: "Please connect and unlock your Ledger device",
|
|
2166
|
+
operationId
|
|
1519
2167
|
}
|
|
1520
2168
|
});
|
|
1521
2169
|
let payload;
|
|
@@ -1576,15 +2224,18 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
1576
2224
|
return { ...params, showOnDevice: true };
|
|
1577
2225
|
}
|
|
1578
2226
|
async _waitForBtcHighIndexConfirm(path, accountIndex) {
|
|
2227
|
+
const operationId = this._activeOperationId();
|
|
1579
2228
|
const waitPromise = this._uiRegistry.wait(
|
|
1580
|
-
import_hwk_adapter_core3.UI_REQUEST.REQUEST_BTC_HIGH_INDEX_CONFIRM
|
|
2229
|
+
import_hwk_adapter_core3.UI_REQUEST.REQUEST_BTC_HIGH_INDEX_CONFIRM,
|
|
2230
|
+
{ operationId }
|
|
1581
2231
|
);
|
|
1582
2232
|
this.emitter.emit(import_hwk_adapter_core3.UI_REQUEST.REQUEST_BTC_HIGH_INDEX_CONFIRM, {
|
|
1583
2233
|
type: import_hwk_adapter_core3.UI_REQUEST.REQUEST_BTC_HIGH_INDEX_CONFIRM,
|
|
1584
2234
|
payload: {
|
|
1585
2235
|
vendor: "ledger",
|
|
1586
2236
|
path,
|
|
1587
|
-
accountIndex
|
|
2237
|
+
accountIndex,
|
|
2238
|
+
operationId
|
|
1588
2239
|
}
|
|
1589
2240
|
});
|
|
1590
2241
|
try {
|
|
@@ -1601,12 +2252,14 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
1601
2252
|
// Ask the user whether to install a missing app (autoInstallApp flow).
|
|
1602
2253
|
// Same register-then-emit ordering as the BTC high-index gate.
|
|
1603
2254
|
async _waitForInstallAppConfirm(appName) {
|
|
2255
|
+
const operationId = this._activeOperationId();
|
|
1604
2256
|
const waitPromise = this._uiRegistry.wait(
|
|
1605
|
-
import_hwk_adapter_core3.UI_REQUEST.REQUEST_INSTALL_APP
|
|
2257
|
+
import_hwk_adapter_core3.UI_REQUEST.REQUEST_INSTALL_APP,
|
|
2258
|
+
{ operationId }
|
|
1606
2259
|
);
|
|
1607
2260
|
this.emitter.emit(import_hwk_adapter_core3.UI_REQUEST.REQUEST_INSTALL_APP, {
|
|
1608
2261
|
type: import_hwk_adapter_core3.UI_REQUEST.REQUEST_INSTALL_APP,
|
|
1609
|
-
payload: { vendor: "ledger", appName }
|
|
2262
|
+
payload: { vendor: "ledger", appName, operationId }
|
|
1610
2263
|
});
|
|
1611
2264
|
try {
|
|
1612
2265
|
const payload = await waitPromise;
|
|
@@ -1622,16 +2275,17 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
1622
2275
|
// Layer 1 entry. Caller signal only races the outer awaiter; the shared
|
|
1623
2276
|
// `_doConnect` runs under its own internal controller so caller A's cancel
|
|
1624
2277
|
// doesn't kill caller B's await.
|
|
1625
|
-
async ensureConnected(connectId, signal, allowUsbEphemeralFallback = false) {
|
|
2278
|
+
async ensureConnected(connectId, signal, allowUsbEphemeralFallback = false, preserveOperationId, context) {
|
|
1626
2279
|
if (signal.aborted) _LedgerAdapter._throwIfAborted(signal);
|
|
1627
|
-
|
|
2280
|
+
this._assertConnectorReady("connectDevice");
|
|
2281
|
+
if (this._isBleConnection() && !connectId && !allowUsbEphemeralFallback) {
|
|
1628
2282
|
throw Object.assign(new Error("Ledger BLE connectId is required."), {
|
|
1629
2283
|
code: import_hwk_adapter_core3.HardwareErrorCode.DeviceNotFound
|
|
1630
2284
|
});
|
|
1631
2285
|
}
|
|
1632
2286
|
if (connectId && this._sessions.has(connectId)) return connectId;
|
|
1633
2287
|
if (!connectId && this._sessions.size > 0) {
|
|
1634
|
-
if (!
|
|
2288
|
+
if (!this._isBleConnection() && this._sessions.size > 1) {
|
|
1635
2289
|
throw Object.assign(
|
|
1636
2290
|
new Error(
|
|
1637
2291
|
"Ledger USB session invariant violated: more than one session is active. Please reconnect the device."
|
|
@@ -1641,15 +2295,24 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
1641
2295
|
}
|
|
1642
2296
|
return this._sessions.keys().next().value;
|
|
1643
2297
|
}
|
|
1644
|
-
if (!this._connectingPromise) {
|
|
1645
|
-
|
|
1646
|
-
|
|
2298
|
+
if (!this._connectingPromise || this._doConnectAbortController?.signal.aborted) {
|
|
2299
|
+
const controller = new AbortController();
|
|
2300
|
+
this._doConnectAbortController = controller;
|
|
2301
|
+
const innerSignal = controller.signal;
|
|
1647
2302
|
this._connectingPromise = (async () => {
|
|
1648
2303
|
try {
|
|
1649
|
-
return await this._doConnect(
|
|
2304
|
+
return await this._doConnect(
|
|
2305
|
+
innerSignal,
|
|
2306
|
+
connectId,
|
|
2307
|
+
allowUsbEphemeralFallback,
|
|
2308
|
+
preserveOperationId,
|
|
2309
|
+
context
|
|
2310
|
+
);
|
|
1650
2311
|
} finally {
|
|
1651
|
-
this.
|
|
1652
|
-
|
|
2312
|
+
if (this._doConnectAbortController === controller) {
|
|
2313
|
+
this._connectingPromise = null;
|
|
2314
|
+
this._doConnectAbortController = null;
|
|
2315
|
+
}
|
|
1653
2316
|
}
|
|
1654
2317
|
})();
|
|
1655
2318
|
}
|
|
@@ -1658,22 +2321,57 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
1658
2321
|
// Layer 1 main loop — the ONLY place in SDK that emits unlock dialog.
|
|
1659
2322
|
// Bounded by MAX_DOCONNECT_CONFIRMS — after N Confirms with no progress,
|
|
1660
2323
|
// throw DeviceNotFound so the user is kicked out of the loop.
|
|
1661
|
-
async _doConnect(internalSignal, targetConnectId, allowUsbEphemeralFallback = false) {
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
2324
|
+
async _doConnect(internalSignal, targetConnectId, allowUsbEphemeralFallback = false, preserveOperationId, context) {
|
|
2325
|
+
_LedgerAdapter._throwIfAborted(internalSignal);
|
|
2326
|
+
if (this.connector.availableTransports?.includes("usb") && this.connector.availableTransports.includes("ble")) {
|
|
2327
|
+
this._activeConnectionType = "usb";
|
|
2328
|
+
const usbDevices = await this._searchDevices({ transportType: "usb" }, internalSignal);
|
|
2329
|
+
_LedgerAdapter._throwIfAborted(internalSignal);
|
|
2330
|
+
const knownUsb = context?.knownConnections?.find(
|
|
2331
|
+
(connection) => connection.transport === "usb"
|
|
2332
|
+
);
|
|
2333
|
+
const usbTarget = knownUsb?.transport === "usb" ? knownUsb.connectId : usbDevices.find((device) => device.connectId === targetConnectId)?.connectId;
|
|
2334
|
+
if (usbDevices.length > 0 && (usbTarget || !targetConnectId || allowUsbEphemeralFallback)) {
|
|
2335
|
+
return this._connectFirstOrSelect(
|
|
2336
|
+
usbDevices,
|
|
2337
|
+
usbTarget,
|
|
2338
|
+
allowUsbEphemeralFallback,
|
|
2339
|
+
preserveOperationId,
|
|
2340
|
+
context,
|
|
2341
|
+
internalSignal
|
|
2342
|
+
);
|
|
2343
|
+
}
|
|
2344
|
+
if (context?.supportedTransports && !context.supportedTransports.includes("ble")) {
|
|
2345
|
+
throw (0, import_hwk_adapter_core3.createHwkError)({
|
|
2346
|
+
code: import_hwk_adapter_core3.HardwareErrorCode.DeviceNotFound,
|
|
2347
|
+
message: "This Ledger model has no Bluetooth transport; connect it over USB"
|
|
2348
|
+
});
|
|
2349
|
+
}
|
|
2350
|
+
this._activeConnectionType = "ble";
|
|
2351
|
+
const knownBle = context?.knownConnections?.find(
|
|
2352
|
+
(connection) => connection.transport === "ble"
|
|
2353
|
+
);
|
|
2354
|
+
if (knownBle?.transport === "ble") {
|
|
2355
|
+
return this._connectDeviceOrThrow(knownBle.connectId, preserveOperationId, internalSignal);
|
|
2356
|
+
}
|
|
2357
|
+
if (targetConnectId && context?.knownConnections === void 0) {
|
|
2358
|
+
throw (0, import_hwk_adapter_core3.createHwkError)({
|
|
2359
|
+
code: import_hwk_adapter_core3.HardwareErrorCode.DeviceNotFound,
|
|
2360
|
+
message: "Ledger connection metadata is required before starting Bluetooth binding"
|
|
2361
|
+
});
|
|
1676
2362
|
}
|
|
2363
|
+
const bleDevices = await this._searchDevices({ transportType: "ble" }, internalSignal);
|
|
2364
|
+
return this._connectFirstOrSelect(
|
|
2365
|
+
bleDevices,
|
|
2366
|
+
void 0,
|
|
2367
|
+
allowUsbEphemeralFallback,
|
|
2368
|
+
preserveOperationId,
|
|
2369
|
+
context,
|
|
2370
|
+
internalSignal
|
|
2371
|
+
);
|
|
2372
|
+
}
|
|
2373
|
+
if (this._isBleConnection() && targetConnectId) {
|
|
2374
|
+
return this._connectDeviceOrThrow(targetConnectId, preserveOperationId, internalSignal);
|
|
1677
2375
|
}
|
|
1678
2376
|
let confirms = 0;
|
|
1679
2377
|
while (!internalSignal.aborted) {
|
|
@@ -1681,22 +2379,28 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
1681
2379
|
type: import_hwk_adapter_core3.EConnectorInteraction.Searching,
|
|
1682
2380
|
payload: { sessionId: "" }
|
|
1683
2381
|
});
|
|
1684
|
-
let devices = await this.
|
|
2382
|
+
let devices = await this._searchDevices(void 0, internalSignal);
|
|
2383
|
+
_LedgerAdapter._throwIfAborted(internalSignal);
|
|
1685
2384
|
if (devices.length === 0) {
|
|
1686
2385
|
for (let i = 0; i < 3 && !internalSignal.aborted; i += 1) {
|
|
1687
2386
|
await new Promise((resolve) => {
|
|
1688
2387
|
setTimeout(resolve, import_hwk_adapter_core3.DEVICE_CONNECT_RETRY_DELAY_MS);
|
|
1689
2388
|
});
|
|
1690
|
-
|
|
2389
|
+
_LedgerAdapter._throwIfAborted(internalSignal);
|
|
2390
|
+
devices = await this._searchDevices(void 0, internalSignal);
|
|
2391
|
+
_LedgerAdapter._throwIfAborted(internalSignal);
|
|
1691
2392
|
if (devices.length > 0) break;
|
|
1692
2393
|
}
|
|
1693
2394
|
}
|
|
1694
|
-
if (devices.length > 0) {
|
|
2395
|
+
if (devices.length > 0 || this._isBleConnection() && allowUsbEphemeralFallback) {
|
|
1695
2396
|
try {
|
|
1696
2397
|
return await this._connectFirstOrSelect(
|
|
1697
2398
|
devices,
|
|
1698
2399
|
targetConnectId,
|
|
1699
|
-
allowUsbEphemeralFallback
|
|
2400
|
+
allowUsbEphemeralFallback,
|
|
2401
|
+
preserveOperationId,
|
|
2402
|
+
context,
|
|
2403
|
+
internalSignal
|
|
1700
2404
|
);
|
|
1701
2405
|
} catch (err) {
|
|
1702
2406
|
if (!isDeviceLockedError(err) && !isDeviceNotAdvertisingError(err) && !isDeviceDisconnectedError(err)) {
|
|
@@ -1725,45 +2429,147 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
1725
2429
|
_LedgerAdapter._throwIfAborted(internalSignal);
|
|
1726
2430
|
throw new Error("_doConnect aborted");
|
|
1727
2431
|
}
|
|
1728
|
-
async _connectFirstOrSelect(devices, targetConnectId, allowUsbEphemeralFallback
|
|
2432
|
+
async _connectFirstOrSelect(devices, targetConnectId, allowUsbEphemeralFallback, preserveOperationId, context, signal) {
|
|
2433
|
+
_LedgerAdapter._throwIfAborted(signal);
|
|
1729
2434
|
if (targetConnectId) {
|
|
1730
2435
|
const target = devices.find(
|
|
1731
2436
|
(d) => d.connectId === targetConnectId || d.deviceId === targetConnectId
|
|
1732
2437
|
);
|
|
1733
2438
|
if (target) {
|
|
1734
|
-
return this._connectDeviceOrThrow(target.connectId);
|
|
2439
|
+
return this._connectDeviceOrThrow(target.connectId, preserveOperationId, signal);
|
|
1735
2440
|
}
|
|
1736
|
-
if (!
|
|
2441
|
+
if (!this._isBleConnection() && devices.length === 1 && allowUsbEphemeralFallback) {
|
|
1737
2442
|
debugLog(
|
|
1738
2443
|
`[LedgerAdapter] target ${targetConnectId} not in fresh enumeration; accepting sole USB device ${devices[0].connectId} for fingerprint-verified recovery`
|
|
1739
2444
|
);
|
|
1740
|
-
return this._connectDeviceOrThrow(devices[0].connectId);
|
|
2445
|
+
return this._connectDeviceOrThrow(devices[0].connectId, preserveOperationId, signal);
|
|
1741
2446
|
}
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
2447
|
+
if (!this._isBleConnection() || !allowUsbEphemeralFallback) {
|
|
2448
|
+
const err = Object.assign(new Error(`Target Ledger unavailable: ${targetConnectId}`), {
|
|
2449
|
+
code: import_hwk_adapter_core3.HardwareErrorCode.DeviceNotFound
|
|
2450
|
+
});
|
|
2451
|
+
if (this._isBleConnection()) {
|
|
2452
|
+
err._tag = ERROR_TAG.DeviceNotAdvertising;
|
|
2453
|
+
}
|
|
2454
|
+
throw err;
|
|
1747
2455
|
}
|
|
1748
|
-
throw err;
|
|
1749
2456
|
}
|
|
1750
|
-
|
|
2457
|
+
const requiresBleSelection = this._isBleConnection();
|
|
2458
|
+
if (requiresBleSelection && !allowUsbEphemeralFallback) {
|
|
1751
2459
|
throw Object.assign(new Error("Ledger BLE connectId is required."), {
|
|
1752
2460
|
code: import_hwk_adapter_core3.HardwareErrorCode.DeviceNotFound
|
|
1753
2461
|
});
|
|
1754
2462
|
}
|
|
1755
|
-
if (
|
|
1756
|
-
|
|
2463
|
+
if (requiresBleSelection && context?.allowDeviceSelection !== false) {
|
|
2464
|
+
const bindingSessionId = context?.bindingSessionId ?? this._uiRegistry.createRequestId();
|
|
2465
|
+
if (context) context.bindingSessionId = bindingSessionId;
|
|
2466
|
+
const allowUsbFallback = context?.bindingReason !== "manual-rebind" && Boolean(this.connector.availableTransports?.includes("usb"));
|
|
2467
|
+
const knownUsb = context?.knownConnections?.find(
|
|
2468
|
+
(connection) => connection.transport === "usb"
|
|
2469
|
+
);
|
|
2470
|
+
const usbConnectId = knownUsb?.transport === "usb" ? knownUsb.connectId : targetConnectId;
|
|
2471
|
+
const { device, requestId } = await (0, import_hwk_adapter_core3.requestBleDeviceSelection)({
|
|
2472
|
+
emitter: this.emitter,
|
|
2473
|
+
registry: this._uiRegistry,
|
|
2474
|
+
signal,
|
|
2475
|
+
allowUsbFallback,
|
|
2476
|
+
scan: async () => {
|
|
2477
|
+
if (allowUsbFallback) {
|
|
2478
|
+
const usbDevices = await this._searchDevices({ transportType: "usb" }, signal);
|
|
2479
|
+
const candidate = usbDevices.find(
|
|
2480
|
+
(device2) => device2.connectionType === "usb" && device2.connectId === usbConnectId
|
|
2481
|
+
) ?? (usbDevices.length === 1 && usbDevices[0].connectionType === "usb" ? usbDevices[0] : void 0);
|
|
2482
|
+
if (candidate) return [candidate];
|
|
2483
|
+
}
|
|
2484
|
+
return this._searchDevices({ transportType: "ble", waitForAllTransports: true }, signal);
|
|
2485
|
+
},
|
|
2486
|
+
request: {
|
|
2487
|
+
devices,
|
|
2488
|
+
bindingSessionId,
|
|
2489
|
+
context: {
|
|
2490
|
+
kind: "bind-connection",
|
|
2491
|
+
transport: "ble",
|
|
2492
|
+
reason: context?.bindingReason ?? (targetConnectId ? "known-connection-unavailable" : "missing-binding")
|
|
2493
|
+
},
|
|
2494
|
+
extra: context?.extra,
|
|
2495
|
+
operationId: preserveOperationId
|
|
2496
|
+
}
|
|
2497
|
+
});
|
|
2498
|
+
if (device.connectionType === "usb") {
|
|
2499
|
+
this._activeConnectionType = "usb";
|
|
2500
|
+
if (context) context.selectedConnection = void 0;
|
|
2501
|
+
this._bindingSelectionRequestId = requestId;
|
|
2502
|
+
this._finishBleBinding("cancelled");
|
|
2503
|
+
return this._connectDeviceOrThrow(device.connectId, preserveOperationId, signal);
|
|
2504
|
+
}
|
|
2505
|
+
this._bindingSelectionRequestId = requestId;
|
|
2506
|
+
const connected = await this._connectDeviceOrThrow(
|
|
2507
|
+
device.connectId,
|
|
2508
|
+
preserveOperationId,
|
|
2509
|
+
signal
|
|
2510
|
+
);
|
|
2511
|
+
if (context) context.selectedConnection = { connectId: connected, requestId };
|
|
2512
|
+
if (context?.deferBindingSave) {
|
|
2513
|
+
context.pendingBinding = { connectId: connected, requestId };
|
|
2514
|
+
} else {
|
|
2515
|
+
await this._saveSelectedBleBinding(
|
|
2516
|
+
connected,
|
|
2517
|
+
requestId,
|
|
2518
|
+
context,
|
|
2519
|
+
preserveOperationId,
|
|
2520
|
+
signal
|
|
2521
|
+
);
|
|
2522
|
+
}
|
|
2523
|
+
return connected;
|
|
2524
|
+
}
|
|
2525
|
+
if (devices.length > 0 && (devices.length > 1 || requiresBleSelection)) {
|
|
2526
|
+
if (context?.allowDeviceSelection === false || !this.emitter.listenerCount(import_hwk_adapter_core3.UI_REQUEST.REQUEST_SELECT_DEVICE)) {
|
|
2527
|
+
throw (0, import_hwk_adapter_core3.createHwkError)({
|
|
2528
|
+
code: import_hwk_adapter_core3.HardwareErrorCode.DeviceNotFound,
|
|
2529
|
+
message: "Select a Ledger device before continuing"
|
|
2530
|
+
});
|
|
2531
|
+
}
|
|
2532
|
+
const requestId = this._uiRegistry.createRequestId();
|
|
2533
|
+
const operationId = this._activeOperationId();
|
|
2534
|
+
const waitPromise = this._uiRegistry.wait(
|
|
2535
|
+
import_hwk_adapter_core3.UI_REQUEST.REQUEST_SELECT_DEVICE,
|
|
2536
|
+
{ requestId, operationId }
|
|
2537
|
+
);
|
|
2538
|
+
this.emitter.emit(import_hwk_adapter_core3.UI_REQUEST.REQUEST_SELECT_DEVICE, {
|
|
2539
|
+
type: import_hwk_adapter_core3.UI_REQUEST.REQUEST_SELECT_DEVICE,
|
|
2540
|
+
payload: {
|
|
2541
|
+
devices,
|
|
2542
|
+
requestId,
|
|
2543
|
+
operationId,
|
|
2544
|
+
context: requiresBleSelection ? {
|
|
2545
|
+
kind: "bind-connection",
|
|
2546
|
+
transport: "ble",
|
|
2547
|
+
reason: targetConnectId ? "known-connection-unavailable" : "missing-binding"
|
|
2548
|
+
} : { kind: "select-device", transport: "usb", reason: "multiple-candidates" },
|
|
2549
|
+
extra: context?.extra
|
|
2550
|
+
}
|
|
2551
|
+
});
|
|
2552
|
+
const { sdkConnectId } = await this._abortable(signal, waitPromise);
|
|
2553
|
+
_LedgerAdapter._throwIfAborted(signal);
|
|
2554
|
+
const selected = devices.find((device) => device.connectId === sdkConnectId);
|
|
2555
|
+
if (!selected) {
|
|
2556
|
+
throw Object.assign(new Error("Selected Ledger is no longer available"), {
|
|
2557
|
+
code: import_hwk_adapter_core3.HardwareErrorCode.DeviceNotFound
|
|
2558
|
+
});
|
|
2559
|
+
}
|
|
2560
|
+
if (context && requiresBleSelection)
|
|
2561
|
+
context.selectedConnection = { connectId: selected.connectId, requestId };
|
|
2562
|
+
return this._connectDeviceOrThrow(selected.connectId, preserveOperationId, signal);
|
|
1757
2563
|
}
|
|
1758
2564
|
if (devices.length !== 1) {
|
|
1759
2565
|
throw Object.assign(new Error("Ledger device not found."), {
|
|
1760
2566
|
code: import_hwk_adapter_core3.HardwareErrorCode.DeviceNotFound
|
|
1761
2567
|
});
|
|
1762
2568
|
}
|
|
1763
|
-
return this._connectDeviceOrThrow(devices[0].connectId);
|
|
2569
|
+
return this._connectDeviceOrThrow(devices[0].connectId, preserveOperationId, signal);
|
|
1764
2570
|
}
|
|
1765
|
-
async _connectDeviceOrThrow(chosenConnectId) {
|
|
1766
|
-
const result = await this.
|
|
2571
|
+
async _connectDeviceOrThrow(chosenConnectId, preserveOperationId, signal) {
|
|
2572
|
+
const result = await this._connectTarget(chosenConnectId, preserveOperationId, signal);
|
|
1767
2573
|
if (!result.success) {
|
|
1768
2574
|
const payload = result.payload;
|
|
1769
2575
|
const rethrow = Object.assign(new Error(payload.error), {
|
|
@@ -1774,7 +2580,7 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
1774
2580
|
}
|
|
1775
2581
|
throw rethrow;
|
|
1776
2582
|
}
|
|
1777
|
-
return
|
|
2583
|
+
return result.payload;
|
|
1778
2584
|
}
|
|
1779
2585
|
/**
|
|
1780
2586
|
* Call the connector with automatic session resolution and disconnect retry.
|
|
@@ -1804,33 +2610,145 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
1804
2610
|
* lives in one place.
|
|
1805
2611
|
*/
|
|
1806
2612
|
async _callConnector(sessionId, method, params, signal) {
|
|
1807
|
-
|
|
2613
|
+
this._assertConnectorReady(method);
|
|
2614
|
+
if (signal?.aborted) throw this._abortReason(signal);
|
|
2615
|
+
const releaseOperation = this._retainConnectorOperation(`call:${sessionId}`);
|
|
2616
|
+
let promise;
|
|
2617
|
+
try {
|
|
2618
|
+
promise = this.connector.call(sessionId, method, params).finally(releaseOperation);
|
|
2619
|
+
} catch (error) {
|
|
2620
|
+
releaseOperation();
|
|
2621
|
+
throw error;
|
|
2622
|
+
}
|
|
1808
2623
|
const result = signal ? await this._abortable(signal, promise) : await promise;
|
|
1809
2624
|
return this._unwrapConnectorResult(result);
|
|
1810
2625
|
}
|
|
2626
|
+
_assertConnectorReady(method) {
|
|
2627
|
+
if (this._resetPromise || this._pendingConnectorTeardowns > 0 || this._unsettledConnectorOperations.size > 0) {
|
|
2628
|
+
throw _LedgerAdapter._createDeviceBusyError(method);
|
|
2629
|
+
}
|
|
2630
|
+
}
|
|
2631
|
+
_runConnectorTeardown(task) {
|
|
2632
|
+
const previous = this._connectorTeardownTail;
|
|
2633
|
+
let releaseTail = () => void 0;
|
|
2634
|
+
this._connectorTeardownTail = new Promise((resolve) => {
|
|
2635
|
+
releaseTail = resolve;
|
|
2636
|
+
});
|
|
2637
|
+
this._pendingConnectorTeardowns += 1;
|
|
2638
|
+
return (async () => {
|
|
2639
|
+
try {
|
|
2640
|
+
await previous;
|
|
2641
|
+
await this._waitForConnectorOperationsToDrain();
|
|
2642
|
+
await task();
|
|
2643
|
+
} finally {
|
|
2644
|
+
this._pendingConnectorTeardowns -= 1;
|
|
2645
|
+
releaseTail();
|
|
2646
|
+
}
|
|
2647
|
+
})();
|
|
2648
|
+
}
|
|
2649
|
+
_waitForConnectorOperationsToDrain() {
|
|
2650
|
+
if (this._unsettledConnectorOperations.size === 0) {
|
|
2651
|
+
return Promise.resolve();
|
|
2652
|
+
}
|
|
2653
|
+
return new Promise((resolve) => {
|
|
2654
|
+
this._connectorIdleWaiters.add(resolve);
|
|
2655
|
+
});
|
|
2656
|
+
}
|
|
2657
|
+
_retainConnectorOperation(key) {
|
|
2658
|
+
this._unsettledConnectorOperations.set(
|
|
2659
|
+
key,
|
|
2660
|
+
(this._unsettledConnectorOperations.get(key) ?? 0) + 1
|
|
2661
|
+
);
|
|
2662
|
+
let released = false;
|
|
2663
|
+
return () => {
|
|
2664
|
+
if (released) return;
|
|
2665
|
+
released = true;
|
|
2666
|
+
const remaining = (this._unsettledConnectorOperations.get(key) ?? 1) - 1;
|
|
2667
|
+
if (remaining > 0) {
|
|
2668
|
+
this._unsettledConnectorOperations.set(key, remaining);
|
|
2669
|
+
} else {
|
|
2670
|
+
this._unsettledConnectorOperations.delete(key);
|
|
2671
|
+
}
|
|
2672
|
+
if (this._unsettledConnectorOperations.size === 0) {
|
|
2673
|
+
for (const resolve of this._connectorIdleWaiters) resolve();
|
|
2674
|
+
this._connectorIdleWaiters.clear();
|
|
2675
|
+
}
|
|
2676
|
+
};
|
|
2677
|
+
}
|
|
1811
2678
|
async connectorCall(connectId, method, params, fingerprint, permissionDeviceId, commonParams, installContext) {
|
|
1812
|
-
|
|
1813
|
-
|
|
2679
|
+
const positionalOperationId = (0, import_hwk_adapter_core3.isHardwareOperationId)(connectId) ? connectId : void 0;
|
|
2680
|
+
if (positionalOperationId && commonParams?.operationId && positionalOperationId !== commonParams.operationId) {
|
|
2681
|
+
throw (0, import_hwk_adapter_core3.createHwkError)({
|
|
2682
|
+
code: import_hwk_adapter_core3.HardwareErrorCode.InvalidParams,
|
|
2683
|
+
message: "Conflicting Ledger operation ids",
|
|
2684
|
+
params: {
|
|
2685
|
+
positionalOperationId,
|
|
2686
|
+
commonOperationId: commonParams.operationId
|
|
2687
|
+
}
|
|
2688
|
+
});
|
|
2689
|
+
}
|
|
2690
|
+
const operationId = commonParams?.operationId ?? positionalOperationId;
|
|
2691
|
+
const operation = operationId ? this._operations.resolve(operationId) : void 0;
|
|
2692
|
+
const releaseOperationRetention = operationId ? this._operations.retain(operationId) : void 0;
|
|
2693
|
+
const effectiveConnectId = operation?.connectId ?? connectId;
|
|
2694
|
+
debugLog("[LedgerAdapter][REQ]", {
|
|
2695
|
+
method,
|
|
2696
|
+
connectId: effectiveConnectId || "(empty)",
|
|
2697
|
+
params
|
|
2698
|
+
});
|
|
2699
|
+
const queueKey = ledgerQueueKey({ operationId, connectId: effectiveConnectId });
|
|
1814
2700
|
try {
|
|
1815
2701
|
const result = await this._jobQueue.enqueue(
|
|
1816
2702
|
queueKey,
|
|
1817
|
-
async (signal) =>
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
2703
|
+
async (signal) => {
|
|
2704
|
+
if (operationId) this._activeOperationJobs.add(operationId);
|
|
2705
|
+
try {
|
|
2706
|
+
return await this._runConnectorCall(
|
|
2707
|
+
effectiveConnectId,
|
|
2708
|
+
method,
|
|
2709
|
+
params,
|
|
2710
|
+
signal,
|
|
2711
|
+
fingerprint,
|
|
2712
|
+
permissionDeviceId,
|
|
2713
|
+
commonParams,
|
|
2714
|
+
installContext ?? {},
|
|
2715
|
+
operationId
|
|
2716
|
+
);
|
|
2717
|
+
} catch (error) {
|
|
2718
|
+
const status = signal.aborted ? "cancelled" : "failed";
|
|
2719
|
+
const bundleConnectId = installContext?.connection?.connectId;
|
|
2720
|
+
let keptBundlePick = false;
|
|
2721
|
+
for (const [pickedConnectId, owner] of this._pendingPickBindings) {
|
|
2722
|
+
if (pickedConnectId === bundleConnectId) {
|
|
2723
|
+
keptBundlePick = true;
|
|
2724
|
+
} else {
|
|
2725
|
+
this._pendingPickBindings.delete(pickedConnectId);
|
|
2726
|
+
this._discardPendingBinding(owner, status);
|
|
2727
|
+
await this._dropSession(pickedConnectId, signal);
|
|
2728
|
+
}
|
|
2729
|
+
}
|
|
2730
|
+
if (!keptBundlePick) this._finishBleBinding(status);
|
|
2731
|
+
throw error;
|
|
2732
|
+
} finally {
|
|
2733
|
+
if (operationId) {
|
|
2734
|
+
this._activeOperationJobs.delete(operationId);
|
|
2735
|
+
if (this._pendingOperationDisconnects.delete(operationId)) {
|
|
2736
|
+
await this._releaseLostOperationConnection(operationId);
|
|
2737
|
+
}
|
|
2738
|
+
}
|
|
2739
|
+
}
|
|
2740
|
+
},
|
|
1827
2741
|
{
|
|
1828
2742
|
label: method,
|
|
1829
2743
|
rejectIfBusy: true,
|
|
1830
2744
|
busyError: _LedgerAdapter._createDeviceBusyError(method)
|
|
1831
2745
|
}
|
|
1832
2746
|
);
|
|
1833
|
-
debugLog("[LedgerAdapter][RES]", {
|
|
2747
|
+
debugLog("[LedgerAdapter][RES]", {
|
|
2748
|
+
method,
|
|
2749
|
+
success: true,
|
|
2750
|
+
payload: redactResultForLog(result)
|
|
2751
|
+
});
|
|
1834
2752
|
return result;
|
|
1835
2753
|
} catch (err) {
|
|
1836
2754
|
const e = err;
|
|
@@ -1844,15 +2762,22 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
1844
2762
|
}
|
|
1845
2763
|
});
|
|
1846
2764
|
throw err;
|
|
2765
|
+
} finally {
|
|
2766
|
+
releaseOperationRetention?.();
|
|
1847
2767
|
}
|
|
1848
2768
|
}
|
|
2769
|
+
/** Hermes/RN polyfills don't always populate signal.reason; fall back. */
|
|
2770
|
+
_abortReason(signal) {
|
|
2771
|
+
return signal.reason ?? this._lastCancelReason ?? new Error("Aborted");
|
|
2772
|
+
}
|
|
1849
2773
|
/**
|
|
1850
2774
|
* Race a promise against an abort signal. On abort, rejects with
|
|
1851
2775
|
* signal.reason → instance _lastCancelReason → generic Error('Aborted').
|
|
1852
2776
|
*/
|
|
1853
2777
|
_abortable(signal, promise) {
|
|
1854
|
-
const getAbortReason = () =>
|
|
2778
|
+
const getAbortReason = () => this._abortReason(signal);
|
|
1855
2779
|
if (signal.aborted) {
|
|
2780
|
+
void promise.catch(() => void 0);
|
|
1856
2781
|
return Promise.reject(getAbortReason());
|
|
1857
2782
|
}
|
|
1858
2783
|
return new Promise((resolve, reject) => {
|
|
@@ -1879,13 +2804,15 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
1879
2804
|
}
|
|
1880
2805
|
}
|
|
1881
2806
|
/** Actual work done under the job queue — connection, fingerprint, call, and recovery. */
|
|
1882
|
-
async _runConnectorCall(connectId, method, params, signal, fingerprint, permissionDeviceId, commonParams, installContext) {
|
|
2807
|
+
async _runConnectorCall(connectId, method, params, signal, fingerprint, permissionDeviceId, commonParams, installContext, operationId, lockedRetryBudget = _LedgerAdapter.MAX_BUSINESS_RETRY_BUDGET) {
|
|
1883
2808
|
_LedgerAdapter._throwIfAborted(signal);
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
|
|
2809
|
+
if (!operationId) {
|
|
2810
|
+
await this._ensureDevicePermission(
|
|
2811
|
+
connectId,
|
|
2812
|
+
permissionDeviceId ?? fingerprint?.deviceId,
|
|
2813
|
+
signal
|
|
2814
|
+
);
|
|
2815
|
+
}
|
|
1889
2816
|
_LedgerAdapter._throwIfAborted(signal);
|
|
1890
2817
|
let effectiveParams = params;
|
|
1891
2818
|
if (method === "btcGetPublicKey") {
|
|
@@ -1899,19 +2826,67 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
1899
2826
|
effectiveParams = gatedParams;
|
|
1900
2827
|
}
|
|
1901
2828
|
const allowUsbEphemeralFallback = !!fingerprint?.deviceId && !fingerprint.skipFingerprint;
|
|
1902
|
-
|
|
1903
|
-
|
|
2829
|
+
let businessCallStarted = false;
|
|
2830
|
+
const selectedBleTarget = connectId ? this._selectedBleReconnectTargets.get(connectId) : void 0;
|
|
2831
|
+
const knownTransport = this._isBleConnection() ? "ble" : "usb";
|
|
2832
|
+
const hintedConnectId = commonParams?.knownConnections?.find(
|
|
2833
|
+
(connection) => connection.transport === knownTransport
|
|
2834
|
+
);
|
|
2835
|
+
const inputConnectId = hintedConnectId && hintedConnectId.transport !== "qr" ? hintedConnectId.connectId : connectId;
|
|
2836
|
+
const preferredConnectId = fingerprint && !fingerprint.skipFingerprint && selectedBleTarget?.chain === fingerprint.chain && selectedBleTarget.fingerprint === fingerprint.deviceId ? selectedBleTarget.connectId : inputConnectId;
|
|
2837
|
+
const checksWallet = Boolean(
|
|
2838
|
+
fingerprint && !fingerprint.skipFingerprint && fingerprint.deviceId
|
|
2839
|
+
);
|
|
2840
|
+
const connectionAttempt = {
|
|
2841
|
+
...commonParams,
|
|
2842
|
+
deferBindingSave: checksWallet
|
|
2843
|
+
};
|
|
2844
|
+
const bundleConnection = installContext?.connection;
|
|
2845
|
+
if (bundleConnection && this._sessions.get(bundleConnection.connectId) !== bundleConnection.sessionId) {
|
|
2846
|
+
throw (0, import_hwk_adapter_core3.createHwkError)({
|
|
2847
|
+
code: import_hwk_adapter_core3.HardwareErrorCode.DeviceDisconnected,
|
|
2848
|
+
message: "Ledger all-network connection ended"
|
|
2849
|
+
});
|
|
2850
|
+
}
|
|
2851
|
+
const resolvedConnectId = operationId ? this._operations.resolve(operationId).connectId : bundleConnection?.connectId ?? await this.ensureConnected(
|
|
2852
|
+
preferredConnectId,
|
|
1904
2853
|
signal,
|
|
1905
|
-
allowUsbEphemeralFallback
|
|
2854
|
+
allowUsbEphemeralFallback,
|
|
2855
|
+
void 0,
|
|
2856
|
+
connectionAttempt
|
|
1906
2857
|
);
|
|
2858
|
+
const walletCheck = installContext?.walletCheck;
|
|
2859
|
+
if (walletCheck && !checksWallet && (walletCheck.verified === false || walletCheck.expected && !walletCheck.verified)) {
|
|
2860
|
+
throw (0, import_hwk_adapter_core3.createHwkError)({
|
|
2861
|
+
code: import_hwk_adapter_core3.HardwareErrorCode.DeviceMismatch,
|
|
2862
|
+
message: "Ledger wallet is not verified on this connection; refusing to derive an unverifiable address"
|
|
2863
|
+
});
|
|
2864
|
+
}
|
|
1907
2865
|
const sessionId = this._sessions.get(resolvedConnectId);
|
|
2866
|
+
if (sessionId && installContext && !installContext.connection) {
|
|
2867
|
+
installContext.connection = { connectId: resolvedConnectId, sessionId };
|
|
2868
|
+
}
|
|
1908
2869
|
if (!sessionId) {
|
|
2870
|
+
if (operationId) {
|
|
2871
|
+
this._operations.end(operationId, "disconnect");
|
|
2872
|
+
throw (0, import_hwk_adapter_core3.createHwkError)({
|
|
2873
|
+
code: import_hwk_adapter_core3.HardwareErrorCode.OperationEnded,
|
|
2874
|
+
message: "Ledger operation connection is no longer active",
|
|
2875
|
+
params: { operationId, reason: "disconnect" }
|
|
2876
|
+
});
|
|
2877
|
+
}
|
|
1909
2878
|
throw Object.assign(new Error("Auto-connect succeeded but no session found"), {
|
|
1910
2879
|
_tag: ERROR_TAG.DeviceSessionNotFound
|
|
1911
2880
|
});
|
|
1912
2881
|
}
|
|
1913
2882
|
try {
|
|
1914
|
-
|
|
2883
|
+
const isBleSession = this._sessionTransports.get(sessionId) === "ble";
|
|
2884
|
+
if (!operationId && connectionAttempt.pendingBinding) {
|
|
2885
|
+
this._pendingPickBindings.set(resolvedConnectId, connectionAttempt);
|
|
2886
|
+
}
|
|
2887
|
+
const bindingOwner = operationId ? this._pendingOperationBindings.get(operationId) : this._pendingPickBindings.get(resolvedConnectId);
|
|
2888
|
+
const pendingBinding = bindingOwner?.pendingBinding?.connectId === resolvedConnectId ? bindingOwner.pendingBinding : void 0;
|
|
2889
|
+
if (fingerprint && checksWallet) {
|
|
1915
2890
|
const fp = await this._abortable(
|
|
1916
2891
|
signal,
|
|
1917
2892
|
this._verifyDeviceFingerprintWithSession(
|
|
@@ -1921,14 +2896,68 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
1921
2896
|
)
|
|
1922
2897
|
);
|
|
1923
2898
|
if (!fp.success) {
|
|
2899
|
+
if (pendingBinding) {
|
|
2900
|
+
this._discardPendingBinding(bindingOwner, "failed");
|
|
2901
|
+
this._pendingPickBindings.delete(resolvedConnectId);
|
|
2902
|
+
}
|
|
2903
|
+
if (operationId) {
|
|
2904
|
+
this._pendingOperationBindings.delete(operationId);
|
|
2905
|
+
this._operations.end(operationId, "explicit");
|
|
2906
|
+
await this._dropSession(resolvedConnectId, signal);
|
|
2907
|
+
}
|
|
1924
2908
|
throw Object.assign(new Error(formatDeviceMismatchError(fp.expected, fp.actual)), {
|
|
1925
2909
|
code: import_hwk_adapter_core3.HardwareErrorCode.DeviceMismatch
|
|
1926
2910
|
});
|
|
1927
2911
|
}
|
|
2912
|
+
if (walletCheck) walletCheck.verified = true;
|
|
2913
|
+
}
|
|
2914
|
+
if (pendingBinding && bindingOwner) {
|
|
2915
|
+
bindingOwner.pendingBinding = void 0;
|
|
2916
|
+
if (operationId) this._pendingOperationBindings.delete(operationId);
|
|
2917
|
+
else this._pendingPickBindings.delete(resolvedConnectId);
|
|
2918
|
+
await this._saveSelectedBleBinding(
|
|
2919
|
+
pendingBinding.connectId,
|
|
2920
|
+
pendingBinding.requestId,
|
|
2921
|
+
bindingOwner,
|
|
2922
|
+
operationId,
|
|
2923
|
+
signal,
|
|
2924
|
+
fingerprint && checksWallet ? {
|
|
2925
|
+
vendor: "ledger",
|
|
2926
|
+
type: "chainFingerprint",
|
|
2927
|
+
chain: fingerprint.chain,
|
|
2928
|
+
value: fingerprint.deviceId
|
|
2929
|
+
} : void 0
|
|
2930
|
+
);
|
|
1928
2931
|
}
|
|
1929
|
-
|
|
2932
|
+
if (fingerprint && !fingerprint.skipFingerprint && fingerprint.deviceId && !operationId && isBleSession && connectionAttempt.selectedConnection?.connectId === resolvedConnectId) {
|
|
2933
|
+
this._selectedBleReconnectTargets.set(connectId, {
|
|
2934
|
+
connectId: resolvedConnectId,
|
|
2935
|
+
chain: fingerprint.chain,
|
|
2936
|
+
fingerprint: fingerprint.deviceId
|
|
2937
|
+
});
|
|
2938
|
+
}
|
|
2939
|
+
businessCallStarted = true;
|
|
2940
|
+
const result = await this._callConnector(sessionId, method, effectiveParams, signal);
|
|
2941
|
+
if (walletCheck) walletCheck.derived = true;
|
|
2942
|
+
return result;
|
|
1930
2943
|
} catch (err) {
|
|
1931
2944
|
if (signal.aborted) throw err;
|
|
2945
|
+
if (isLostConnectionError(err)) {
|
|
2946
|
+
this._discoveredDevices.delete(resolvedConnectId);
|
|
2947
|
+
if (operationId) await this._releaseLostOperationConnection(operationId);
|
|
2948
|
+
else {
|
|
2949
|
+
this._sessions.delete(resolvedConnectId);
|
|
2950
|
+
await this.connector.disconnect(sessionId).catch(() => void 0);
|
|
2951
|
+
}
|
|
2952
|
+
const ambiguous = businessCallStarted && !(0, import_hwk_adapter_core3.canReplayHardwareMethodAfterTransportFailure)(method);
|
|
2953
|
+
const interactionParams = operationId ? { operationId, reason: "disconnect" } : void 0;
|
|
2954
|
+
throw (0, import_hwk_adapter_core3.createHwkError)({
|
|
2955
|
+
code: operationId ? import_hwk_adapter_core3.HardwareErrorCode.OperationEnded : mapLedgerError(err).code,
|
|
2956
|
+
message: "Ledger operation connection was lost; start a new operation",
|
|
2957
|
+
params: ambiguous ? (0, import_hwk_adapter_core3.operationMayHaveCompletedParams)(method, { operationId }) : interactionParams,
|
|
2958
|
+
recovery: ambiguous ? { scope: "unknown" } : void 0
|
|
2959
|
+
});
|
|
2960
|
+
}
|
|
1932
2961
|
const errObj = err;
|
|
1933
2962
|
debugLog("[LedgerAdapter] connectorCall error:", method, {
|
|
1934
2963
|
message: errObj?.message,
|
|
@@ -1940,6 +2969,64 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
1940
2969
|
isNotAdvertising: isDeviceNotAdvertisingError(err),
|
|
1941
2970
|
isStuckApp: isStuckAppStateError(err)
|
|
1942
2971
|
});
|
|
2972
|
+
const assertSessionCurrent = (message) => {
|
|
2973
|
+
if (this._sessions.get(resolvedConnectId) === sessionId) return;
|
|
2974
|
+
throw (0, import_hwk_adapter_core3.createHwkError)({
|
|
2975
|
+
code: operationId ? import_hwk_adapter_core3.HardwareErrorCode.OperationEnded : import_hwk_adapter_core3.HardwareErrorCode.DeviceDisconnected,
|
|
2976
|
+
message
|
|
2977
|
+
});
|
|
2978
|
+
};
|
|
2979
|
+
if ((isDeviceLockedError(err) || errObj?.code === import_hwk_adapter_core3.HardwareErrorCode.DeviceLocked) && lockedRetryBudget > 0) {
|
|
2980
|
+
await this._waitForDeviceConnect(signal);
|
|
2981
|
+
assertSessionCurrent("Ledger connection ended while waiting for unlock");
|
|
2982
|
+
if (walletCheck?.derived) walletCheck.verified = false;
|
|
2983
|
+
return this._runConnectorCall(
|
|
2984
|
+
resolvedConnectId,
|
|
2985
|
+
method,
|
|
2986
|
+
effectiveParams,
|
|
2987
|
+
signal,
|
|
2988
|
+
fingerprint,
|
|
2989
|
+
permissionDeviceId,
|
|
2990
|
+
commonParams,
|
|
2991
|
+
installContext,
|
|
2992
|
+
operationId,
|
|
2993
|
+
lockedRetryBudget - 1
|
|
2994
|
+
);
|
|
2995
|
+
}
|
|
2996
|
+
if (businessCallStarted && isStuckAppStateError(err)) {
|
|
2997
|
+
await this._sleepAbortable(_LedgerAdapter.STUCK_APP_RETRY_DELAY_MS, signal);
|
|
2998
|
+
assertSessionCurrent("Ledger connection ended during the app transition");
|
|
2999
|
+
try {
|
|
3000
|
+
const retried = await this._callConnector(sessionId, method, effectiveParams, signal);
|
|
3001
|
+
if (walletCheck) walletCheck.derived = true;
|
|
3002
|
+
return retried;
|
|
3003
|
+
} catch (retryErr) {
|
|
3004
|
+
if (isStuckAppStateError(retryErr)) throw err;
|
|
3005
|
+
if (isLostConnectionError(retryErr)) {
|
|
3006
|
+
if (operationId) await this._releaseLostOperationConnection(operationId);
|
|
3007
|
+
else {
|
|
3008
|
+
this._sessions.delete(resolvedConnectId);
|
|
3009
|
+
this._discoveredDevices.delete(resolvedConnectId);
|
|
3010
|
+
await this.connector.disconnect(sessionId).catch(() => void 0);
|
|
3011
|
+
}
|
|
3012
|
+
const ambiguous = !(0, import_hwk_adapter_core3.canReplayHardwareMethodAfterTransportFailure)(method);
|
|
3013
|
+
throw (0, import_hwk_adapter_core3.createHwkError)({
|
|
3014
|
+
code: operationId ? import_hwk_adapter_core3.HardwareErrorCode.OperationEnded : mapLedgerError(retryErr).code,
|
|
3015
|
+
message: `Ledger ${method} may have completed before the connection was lost`,
|
|
3016
|
+
params: ambiguous ? (0, import_hwk_adapter_core3.operationMayHaveCompletedParams)(method, { operationId, reason: "disconnect" }) : { operationId, reason: "disconnect" },
|
|
3017
|
+
recovery: ambiguous ? { scope: "unknown" } : void 0
|
|
3018
|
+
});
|
|
3019
|
+
}
|
|
3020
|
+
throw retryErr;
|
|
3021
|
+
}
|
|
3022
|
+
}
|
|
3023
|
+
if (!operationId && errObj?.code === import_hwk_adapter_core3.HardwareErrorCode.DeviceMismatch) {
|
|
3024
|
+
this._sessions.delete(resolvedConnectId);
|
|
3025
|
+
this._sessionTransports.delete(sessionId);
|
|
3026
|
+
this._discoveredDevices.delete(resolvedConnectId);
|
|
3027
|
+
await this.connector.disconnect(sessionId).catch(() => void 0);
|
|
3028
|
+
throw err;
|
|
3029
|
+
}
|
|
1943
3030
|
const autoInstallApp = commonParams?.autoInstallApp ?? this._defaultAutoInstallApp;
|
|
1944
3031
|
const isAppMissing = isAppNotInstalledError(err) || err?.code === import_hwk_adapter_core3.HardwareErrorCode.AppNotInstalled;
|
|
1945
3032
|
if (autoInstallApp && isAppMissing) {
|
|
@@ -1995,157 +3082,14 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
1995
3082
|
fingerprint,
|
|
1996
3083
|
permissionDeviceId,
|
|
1997
3084
|
commonParams,
|
|
1998
|
-
installContext
|
|
3085
|
+
installContext,
|
|
3086
|
+
operationId
|
|
1999
3087
|
);
|
|
2000
3088
|
}
|
|
2001
3089
|
}
|
|
2002
|
-
if (isStuckAppStateError(err)) {
|
|
2003
|
-
try {
|
|
2004
|
-
this._sessions.delete(resolvedConnectId);
|
|
2005
|
-
this._discoveredDevices.delete(resolvedConnectId);
|
|
2006
|
-
this.connector.reset?.();
|
|
2007
|
-
} catch {
|
|
2008
|
-
}
|
|
2009
|
-
debugLog(
|
|
2010
|
-
"[LedgerAdapter] stuck-app retry: method=",
|
|
2011
|
-
method,
|
|
2012
|
-
"delayMs=",
|
|
2013
|
-
_LedgerAdapter.STUCK_APP_RETRY_DELAY_MS,
|
|
2014
|
-
"_tag=",
|
|
2015
|
-
err?._tag
|
|
2016
|
-
);
|
|
2017
|
-
try {
|
|
2018
|
-
const retryResult = await this._retryAfterStuckApp(
|
|
2019
|
-
resolvedConnectId,
|
|
2020
|
-
method,
|
|
2021
|
-
effectiveParams,
|
|
2022
|
-
signal,
|
|
2023
|
-
err,
|
|
2024
|
-
fingerprint
|
|
2025
|
-
);
|
|
2026
|
-
debugLog("[LedgerAdapter] stuck-app retry succeeded: method=", method);
|
|
2027
|
-
return retryResult;
|
|
2028
|
-
} catch (retryErr) {
|
|
2029
|
-
if (signal.aborted) throw retryErr;
|
|
2030
|
-
if (isStuckAppStateError(retryErr)) {
|
|
2031
|
-
debugLog("[LedgerAdapter] stuck-app retry exhausted (2nd 6901): method=", method);
|
|
2032
|
-
throw err;
|
|
2033
|
-
}
|
|
2034
|
-
debugLog(
|
|
2035
|
-
"[LedgerAdapter] stuck-app retry threw non-stuck error: method=",
|
|
2036
|
-
method,
|
|
2037
|
-
"retryErrTag=",
|
|
2038
|
-
retryErr?._tag
|
|
2039
|
-
);
|
|
2040
|
-
throw retryErr;
|
|
2041
|
-
}
|
|
2042
|
-
}
|
|
2043
|
-
if (isDeviceLockedError(err) || isDeviceNotAdvertisingError(err) || isDeviceDisconnectedError(err)) {
|
|
2044
|
-
let lastErr = err;
|
|
2045
|
-
for (let attempt = 0; attempt < _LedgerAdapter.MAX_BUSINESS_RETRY_BUDGET; attempt += 1) {
|
|
2046
|
-
if (signal.aborted) throw lastErr;
|
|
2047
|
-
try {
|
|
2048
|
-
this._sessions.delete(resolvedConnectId);
|
|
2049
|
-
this._discoveredDevices.delete(resolvedConnectId);
|
|
2050
|
-
if (isDeviceDisconnectedError(lastErr)) {
|
|
2051
|
-
try {
|
|
2052
|
-
this.connector.reset?.();
|
|
2053
|
-
} catch {
|
|
2054
|
-
}
|
|
2055
|
-
}
|
|
2056
|
-
const reConnectId = await this.ensureConnected(
|
|
2057
|
-
resolvedConnectId,
|
|
2058
|
-
signal,
|
|
2059
|
-
allowUsbEphemeralFallback
|
|
2060
|
-
);
|
|
2061
|
-
const reSessionId = this._sessions.get(reConnectId);
|
|
2062
|
-
if (!reSessionId) throw lastErr;
|
|
2063
|
-
if (fingerprint && !fingerprint.skipFingerprint && fingerprint.deviceId) {
|
|
2064
|
-
const fp = await this._abortable(
|
|
2065
|
-
signal,
|
|
2066
|
-
this._verifyDeviceFingerprintWithSession(
|
|
2067
|
-
reSessionId,
|
|
2068
|
-
fingerprint.deviceId,
|
|
2069
|
-
fingerprint.chain
|
|
2070
|
-
)
|
|
2071
|
-
);
|
|
2072
|
-
if (!fp.success) {
|
|
2073
|
-
throw Object.assign(new Error(formatDeviceMismatchError(fp.expected, fp.actual)), {
|
|
2074
|
-
code: import_hwk_adapter_core3.HardwareErrorCode.DeviceMismatch
|
|
2075
|
-
});
|
|
2076
|
-
}
|
|
2077
|
-
}
|
|
2078
|
-
return await this._callConnector(reSessionId, method, effectiveParams, signal);
|
|
2079
|
-
} catch (retryErr) {
|
|
2080
|
-
if (signal.aborted) throw retryErr;
|
|
2081
|
-
lastErr = retryErr;
|
|
2082
|
-
const canRetry = attempt < _LedgerAdapter.MAX_BUSINESS_RETRY_BUDGET - 1 && (isDeviceLockedError(retryErr) || isDeviceNotAdvertisingError(retryErr) || isDeviceDisconnectedError(retryErr));
|
|
2083
|
-
if (!canRetry) {
|
|
2084
|
-
throw retryErr;
|
|
2085
|
-
}
|
|
2086
|
-
}
|
|
2087
|
-
}
|
|
2088
|
-
throw lastErr;
|
|
2089
|
-
}
|
|
2090
|
-
if (isTimeoutError(err)) {
|
|
2091
|
-
debugLog("[LedgerAdapter] timeout, retrying with fresh connection...");
|
|
2092
|
-
this._discoveredDevices.delete(resolvedConnectId);
|
|
2093
|
-
return this._retryWithFreshConnection(
|
|
2094
|
-
resolvedConnectId,
|
|
2095
|
-
method,
|
|
2096
|
-
effectiveParams,
|
|
2097
|
-
signal,
|
|
2098
|
-
err,
|
|
2099
|
-
fingerprint
|
|
2100
|
-
);
|
|
2101
|
-
}
|
|
2102
|
-
if (isConnectionLevelError(err)) {
|
|
2103
|
-
debugLog("[LedgerAdapter] connection-level fail-closed reset");
|
|
2104
|
-
this._sessions.delete(resolvedConnectId);
|
|
2105
|
-
this._discoveredDevices.delete(resolvedConnectId);
|
|
2106
|
-
this.connector.reset();
|
|
2107
|
-
const codeNum = err?.code;
|
|
2108
|
-
throw Object.assign(err, {
|
|
2109
|
-
code: codeNum ?? import_hwk_adapter_core3.HardwareErrorCode.DeviceDisconnected
|
|
2110
|
-
});
|
|
2111
|
-
}
|
|
2112
3090
|
throw err;
|
|
2113
3091
|
}
|
|
2114
3092
|
}
|
|
2115
|
-
/**
|
|
2116
|
-
* Stuck-app recovery: pause for the device's UI transition, then retry once.
|
|
2117
|
-
*
|
|
2118
|
-
* Caller has already cleared the session + reset connector. We wait so Stax
|
|
2119
|
-
* finishes its post-CloseApp animation, then go through ensureConnected +
|
|
2120
|
-
* fingerprint check + call exactly once. Caller decides what to do on a
|
|
2121
|
-
* second stuck-app hit.
|
|
2122
|
-
*/
|
|
2123
|
-
async _retryAfterStuckApp(resolvedConnectId, method, params, signal, originalErr, fingerprint) {
|
|
2124
|
-
await this._sleepAbortable(_LedgerAdapter.STUCK_APP_RETRY_DELAY_MS, signal);
|
|
2125
|
-
const retryConnectId = await this.ensureConnected(
|
|
2126
|
-
resolvedConnectId,
|
|
2127
|
-
signal,
|
|
2128
|
-
!!fingerprint?.deviceId && !fingerprint.skipFingerprint
|
|
2129
|
-
);
|
|
2130
|
-
const retrySessionId = this._sessions.get(retryConnectId);
|
|
2131
|
-
if (!retrySessionId) throw originalErr;
|
|
2132
|
-
if (fingerprint && !fingerprint.skipFingerprint && fingerprint.deviceId) {
|
|
2133
|
-
const fp = await this._abortable(
|
|
2134
|
-
signal,
|
|
2135
|
-
this._verifyDeviceFingerprintWithSession(
|
|
2136
|
-
retrySessionId,
|
|
2137
|
-
fingerprint.deviceId,
|
|
2138
|
-
fingerprint.chain
|
|
2139
|
-
)
|
|
2140
|
-
);
|
|
2141
|
-
if (!fp.success) {
|
|
2142
|
-
throw Object.assign(new Error(formatDeviceMismatchError(fp.expected, fp.actual)), {
|
|
2143
|
-
code: import_hwk_adapter_core3.HardwareErrorCode.DeviceMismatch
|
|
2144
|
-
});
|
|
2145
|
-
}
|
|
2146
|
-
}
|
|
2147
|
-
return this._callConnector(retrySessionId, method, params, signal);
|
|
2148
|
-
}
|
|
2149
3093
|
_sleepAbortable(ms, signal) {
|
|
2150
3094
|
return new Promise((resolve, reject) => {
|
|
2151
3095
|
if (signal.aborted) {
|
|
@@ -2164,111 +3108,22 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
2164
3108
|
});
|
|
2165
3109
|
}
|
|
2166
3110
|
/**
|
|
2167
|
-
*
|
|
2168
|
-
*
|
|
2169
|
-
* Timeout recovery starts with a full connector reset. After an APDU
|
|
2170
|
-
* timeout, DMK/transport state may still emit malformed responses; retrying
|
|
2171
|
-
* on the same DMK can poison the next chain switch.
|
|
2172
|
-
*/
|
|
2173
|
-
async _retryWithFreshConnection(targetConnectId, method, params, signal, originalErr, fingerprint) {
|
|
2174
|
-
this.connector.reset();
|
|
2175
|
-
this._sessions.clear();
|
|
2176
|
-
this._discoveredDevices.clear();
|
|
2177
|
-
this._connectingPromise = null;
|
|
2178
|
-
const allowUsbEphemeralFallback = !!fingerprint?.deviceId && !fingerprint.skipFingerprint;
|
|
2179
|
-
const retryConnectId = await this.ensureConnected(
|
|
2180
|
-
targetConnectId,
|
|
2181
|
-
signal,
|
|
2182
|
-
allowUsbEphemeralFallback
|
|
2183
|
-
);
|
|
2184
|
-
const retrySessionId = this._sessions.get(retryConnectId);
|
|
2185
|
-
if (!retrySessionId) {
|
|
2186
|
-
throw originalErr;
|
|
2187
|
-
}
|
|
2188
|
-
try {
|
|
2189
|
-
if (fingerprint && !fingerprint.skipFingerprint && fingerprint.deviceId) {
|
|
2190
|
-
const fp = await this._abortable(
|
|
2191
|
-
signal,
|
|
2192
|
-
this._verifyDeviceFingerprintWithSession(
|
|
2193
|
-
retrySessionId,
|
|
2194
|
-
fingerprint.deviceId,
|
|
2195
|
-
fingerprint.chain
|
|
2196
|
-
)
|
|
2197
|
-
);
|
|
2198
|
-
if (!fp.success) {
|
|
2199
|
-
throw Object.assign(new Error(formatDeviceMismatchError(fp.expected, fp.actual)), {
|
|
2200
|
-
code: import_hwk_adapter_core3.HardwareErrorCode.DeviceMismatch
|
|
2201
|
-
});
|
|
2202
|
-
}
|
|
2203
|
-
}
|
|
2204
|
-
return await this._callConnector(retrySessionId, method, params, signal);
|
|
2205
|
-
} catch (retryErr) {
|
|
2206
|
-
if (signal.aborted) throw retryErr;
|
|
2207
|
-
this.connector.reset();
|
|
2208
|
-
this._sessions.clear();
|
|
2209
|
-
this._discoveredDevices.clear();
|
|
2210
|
-
this._connectingPromise = null;
|
|
2211
|
-
if (!isDeviceDisconnectedError(retryErr) && !isTimeoutError(retryErr)) {
|
|
2212
|
-
throw retryErr;
|
|
2213
|
-
}
|
|
2214
|
-
debugLog(
|
|
2215
|
-
"[LedgerAdapter] fresh-session retry still failed; resetting connector and rebuilding DMK"
|
|
2216
|
-
);
|
|
2217
|
-
this.connector.reset();
|
|
2218
|
-
this._sessions.clear();
|
|
2219
|
-
this._discoveredDevices.clear();
|
|
2220
|
-
this._connectingPromise = null;
|
|
2221
|
-
const finalConnectId = await this.ensureConnected(
|
|
2222
|
-
targetConnectId,
|
|
2223
|
-
signal,
|
|
2224
|
-
allowUsbEphemeralFallback
|
|
2225
|
-
);
|
|
2226
|
-
const finalSessionId = this._sessions.get(finalConnectId);
|
|
2227
|
-
if (!finalSessionId) {
|
|
2228
|
-
throw originalErr;
|
|
2229
|
-
}
|
|
2230
|
-
if (fingerprint && !fingerprint.skipFingerprint && fingerprint.deviceId) {
|
|
2231
|
-
const fp = await this._abortable(
|
|
2232
|
-
signal,
|
|
2233
|
-
this._verifyDeviceFingerprintWithSession(
|
|
2234
|
-
finalSessionId,
|
|
2235
|
-
fingerprint.deviceId,
|
|
2236
|
-
fingerprint.chain
|
|
2237
|
-
)
|
|
2238
|
-
);
|
|
2239
|
-
if (!fp.success) {
|
|
2240
|
-
throw Object.assign(new Error(formatDeviceMismatchError(fp.expected, fp.actual)), {
|
|
2241
|
-
code: import_hwk_adapter_core3.HardwareErrorCode.DeviceMismatch
|
|
2242
|
-
});
|
|
2243
|
-
}
|
|
2244
|
-
}
|
|
2245
|
-
return this._callConnector(finalSessionId, method, params, signal);
|
|
2246
|
-
}
|
|
2247
|
-
}
|
|
2248
|
-
/**
|
|
2249
|
-
* Ensure OS-level device permission (Bluetooth / USB) before proceeding.
|
|
2250
|
-
*
|
|
2251
|
-
* Emits `REQUEST_DEVICE_PERMISSION` and awaits the consumer's
|
|
2252
|
-
* `RECEIVE_DEVICE_PERMISSION` reply (60s budget covers "probe → system
|
|
2253
|
-
* prompt → user tap" plus a generous margin). If the consumer never wires
|
|
2254
|
-
* a handler or never replies, the wait times out and the operation fails
|
|
2255
|
-
* fast so scanners/callers don't hang silently.
|
|
2256
|
-
*
|
|
2257
|
-
* - No connectId (searchDevices): environment-level permission
|
|
2258
|
-
* - With connectId (business methods): device-level permission
|
|
3111
|
+
* Ensure OS-level Bluetooth/USB permission; times out after 60s if the host never
|
|
3112
|
+
* replies. Without connectId the request is environment-level, else device-level.
|
|
2259
3113
|
*/
|
|
2260
3114
|
async _ensureDevicePermission(connectId, deviceId, signal) {
|
|
2261
3115
|
if (signal?.aborted) {
|
|
2262
3116
|
_LedgerAdapter._throwIfAborted(signal);
|
|
2263
3117
|
}
|
|
2264
3118
|
const transportType = this.activeTransport ?? "hid";
|
|
3119
|
+
const operationId = this._activeOperationId();
|
|
2265
3120
|
const waitPromise = this._uiRegistry.wait(
|
|
2266
3121
|
import_hwk_adapter_core3.UI_REQUEST.REQUEST_DEVICE_PERMISSION,
|
|
2267
|
-
{ timeoutMs: 6e4 }
|
|
3122
|
+
{ timeoutMs: 6e4, operationId }
|
|
2268
3123
|
);
|
|
2269
3124
|
this.emitter.emit(import_hwk_adapter_core3.UI_REQUEST.REQUEST_DEVICE_PERMISSION, {
|
|
2270
3125
|
type: import_hwk_adapter_core3.UI_REQUEST.REQUEST_DEVICE_PERMISSION,
|
|
2271
|
-
payload: { transportType, connectId, deviceId }
|
|
3126
|
+
payload: { transportType, connectId, deviceId, operationId }
|
|
2272
3127
|
});
|
|
2273
3128
|
let response;
|
|
2274
3129
|
const onAbort = () => {
|
|
@@ -2314,7 +3169,15 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
2314
3169
|
if (err && typeof err === "object" && "code" in err && typeof err.code === "number") {
|
|
2315
3170
|
const e = err;
|
|
2316
3171
|
const params = e.code === import_hwk_adapter_core3.HardwareErrorCode.DevicePermissionDenied && e.reason ? { permissionDeniedReason: e.reason } : e.params;
|
|
2317
|
-
return ledgerFailure(
|
|
3172
|
+
return ledgerFailure(
|
|
3173
|
+
e.code,
|
|
3174
|
+
e.message ?? "Unknown error",
|
|
3175
|
+
e.appName,
|
|
3176
|
+
tag,
|
|
3177
|
+
params,
|
|
3178
|
+
void 0,
|
|
3179
|
+
(0, import_hwk_adapter_core3.isHwkRecoveryHint)(e.recovery) ? e.recovery : void 0
|
|
3180
|
+
);
|
|
2318
3181
|
}
|
|
2319
3182
|
const mapped = mapLedgerError(err);
|
|
2320
3183
|
return ledgerFailure(mapped.code, mapped.message, mapped.appName, tag);
|
|
@@ -2341,7 +3204,7 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
2341
3204
|
deviceId: device.deviceId,
|
|
2342
3205
|
connectId: device.connectId,
|
|
2343
3206
|
label: device.name,
|
|
2344
|
-
connectionType: this.connector.connectionType,
|
|
3207
|
+
connectionType: device.connectionType ?? this.connector.connectionType,
|
|
2345
3208
|
rssi: device.rssi,
|
|
2346
3209
|
isConnectable: device.isConnectable,
|
|
2347
3210
|
serialNumber: device.serialNumber,
|
|
@@ -2366,7 +3229,7 @@ _LedgerAdapter.APP_INSTALL_PROGRESS_MIN_DELTA = 0.05;
|
|
|
2366
3229
|
var LedgerAdapter = _LedgerAdapter;
|
|
2367
3230
|
|
|
2368
3231
|
// src/connector/LedgerConnectorBase.ts
|
|
2369
|
-
var
|
|
3232
|
+
var import_hwk_adapter_core12 = require("@onekeyfe/hwk-adapter-core");
|
|
2370
3233
|
|
|
2371
3234
|
// src/device/LedgerDeviceManager.ts
|
|
2372
3235
|
var LedgerDeviceManager = class {
|
|
@@ -2902,7 +3765,7 @@ var SignerManager = class _SignerManager {
|
|
|
2902
3765
|
return (args) => new import_device_signer_kit_ethereum.SignerEthBuilder(args);
|
|
2903
3766
|
}
|
|
2904
3767
|
static _createContextModule() {
|
|
2905
|
-
const contextModule = new import_context_module.ContextModuleBuilder({}).removeDefaultLoaders().build();
|
|
3768
|
+
const contextModule = new import_context_module.ContextModuleBuilder({}).setChain(import_context_module.ContextModuleChainID.Ethereum).removeDefaultLoaders().build();
|
|
2906
3769
|
return _SignerManager.wrapBlindSigningReportNonBlocking(contextModule);
|
|
2907
3770
|
}
|
|
2908
3771
|
static wrapBlindSigningReportNonBlocking(contextModule) {
|
|
@@ -2940,6 +3803,27 @@ function collapseSignerInteraction(interaction) {
|
|
|
2940
3803
|
return import_hwk_adapter_core5.EConnectorInteraction.ConfirmOnDevice;
|
|
2941
3804
|
}
|
|
2942
3805
|
}
|
|
3806
|
+
function wireSignerToSession(ctx, sessionId, chain, signer) {
|
|
3807
|
+
signer.onInteraction = (interaction) => {
|
|
3808
|
+
debugLog(`[LedgerConnector] ${chain}.onInteraction:`, interaction);
|
|
3809
|
+
ctx.emit("ui-event", {
|
|
3810
|
+
type: collapseSignerInteraction(interaction),
|
|
3811
|
+
payload: { sessionId }
|
|
3812
|
+
});
|
|
3813
|
+
};
|
|
3814
|
+
signer.onRegisterCanceller = (cancel) => ctx.registerCanceller(sessionId, cancel);
|
|
3815
|
+
return signer;
|
|
3816
|
+
}
|
|
3817
|
+
async function runSignerCall(ctx, sessionId, call) {
|
|
3818
|
+
try {
|
|
3819
|
+
return await call();
|
|
3820
|
+
} catch (err) {
|
|
3821
|
+
ctx.invalidateSession(sessionId);
|
|
3822
|
+
throw ctx.wrapError(err);
|
|
3823
|
+
} finally {
|
|
3824
|
+
ctx.clearCanceller(sessionId);
|
|
3825
|
+
}
|
|
3826
|
+
}
|
|
2943
3827
|
|
|
2944
3828
|
// src/connector/chains/evm.ts
|
|
2945
3829
|
async function evmGetAddress(ctx, sessionId, params) {
|
|
@@ -3164,7 +4048,21 @@ function _purposeToTemplate(purpose, DDT) {
|
|
|
3164
4048
|
});
|
|
3165
4049
|
}
|
|
3166
4050
|
}
|
|
4051
|
+
function assertBitcoinMainnet(coin) {
|
|
4052
|
+
if (coin !== void 0 && !["btc", "bitcoin"].includes(coin.toLowerCase())) {
|
|
4053
|
+
throw Object.assign(new Error(`Ledger BTC supports Bitcoin mainnet only, not ${coin}`), {
|
|
4054
|
+
code: import_hwk_adapter_core8.HardwareErrorCode.ChainNotSupported
|
|
4055
|
+
});
|
|
4056
|
+
}
|
|
4057
|
+
}
|
|
3167
4058
|
async function btcGetAddress(ctx, sessionId, params) {
|
|
4059
|
+
assertBitcoinMainnet(params.coin);
|
|
4060
|
+
if (normalizePath(params.path).split("/").length !== 3) {
|
|
4061
|
+
throw Object.assign(
|
|
4062
|
+
new Error("Ledger btcGetAddress takes an account path (e.g. m/84'/0'/0') plus addressIndex"),
|
|
4063
|
+
{ code: import_hwk_adapter_core8.HardwareErrorCode.InvalidParams }
|
|
4064
|
+
);
|
|
4065
|
+
}
|
|
3168
4066
|
const btcSigner = await _createBtcSigner(ctx, sessionId);
|
|
3169
4067
|
const path = normalizePath(params.path);
|
|
3170
4068
|
try {
|
|
@@ -3195,6 +4093,7 @@ async function btcGetAddress(ctx, sessionId, params) {
|
|
|
3195
4093
|
}
|
|
3196
4094
|
}
|
|
3197
4095
|
async function btcGetPublicKey(ctx, sessionId, params) {
|
|
4096
|
+
assertBitcoinMainnet(params.coin);
|
|
3198
4097
|
const btcSigner = await _createBtcSigner(ctx, sessionId);
|
|
3199
4098
|
const path = normalizePath(params.path);
|
|
3200
4099
|
debugLog("[LedgerConnector] btcGetPublicKey called, path:", path, "sessionId:", sessionId);
|
|
@@ -3373,6 +4272,7 @@ function _applySignaturesToPsbt(psbtHex, signatures) {
|
|
|
3373
4272
|
|
|
3374
4273
|
// src/connector/chains/sol.ts
|
|
3375
4274
|
var import_hwk_adapter_core9 = require("@onekeyfe/hwk-adapter-core");
|
|
4275
|
+
var import_bs58 = __toESM(require("bs58"));
|
|
3376
4276
|
|
|
3377
4277
|
// src/signer/SignerSol.ts
|
|
3378
4278
|
var SignerSol = class {
|
|
@@ -3456,8 +4356,21 @@ async function solSignMessage(ctx, sessionId, params) {
|
|
|
3456
4356
|
const path = normalizePath(params.path);
|
|
3457
4357
|
const messageBytes = (0, import_hwk_adapter_core9.hexToBytes)(params.message);
|
|
3458
4358
|
try {
|
|
4359
|
+
if (params.messageVersion === 1) {
|
|
4360
|
+
const preparedMessage = (0, import_hwk_adapter_core9.prepareSolanaOffchainMessageV1)({
|
|
4361
|
+
message: messageBytes,
|
|
4362
|
+
requiredSigners: params.requiredSigners
|
|
4363
|
+
});
|
|
4364
|
+
const { SignMessageVersion } = await ctx.importLedgerKit(
|
|
4365
|
+
"@ledgerhq/device-signer-kit-solana"
|
|
4366
|
+
);
|
|
4367
|
+
const result2 = await solSigner.signMessage(path, preparedMessage.serializedMessage, {
|
|
4368
|
+
version: SignMessageVersion.Raw
|
|
4369
|
+
});
|
|
4370
|
+
return { signature: decodeBase58Signature(result2.signature) };
|
|
4371
|
+
}
|
|
3459
4372
|
const result = await solSigner.signMessage(path, messageBytes);
|
|
3460
|
-
return { signature: result.signature };
|
|
4373
|
+
return { signature: decodeBase58EnvelopeSignature(result.signature) };
|
|
3461
4374
|
} catch (err) {
|
|
3462
4375
|
ctx.invalidateSession(sessionId);
|
|
3463
4376
|
throw ctx.wrapError(err);
|
|
@@ -3465,11 +4378,27 @@ async function solSignMessage(ctx, sessionId, params) {
|
|
|
3465
4378
|
ctx.clearCanceller(sessionId);
|
|
3466
4379
|
}
|
|
3467
4380
|
}
|
|
4381
|
+
function decodeBase58Signature(signature) {
|
|
4382
|
+
const bytes = import_bs58.default.decode(signature);
|
|
4383
|
+
if (bytes.length !== 64) {
|
|
4384
|
+
throw new Error(`Ledger Solana signature must be 64 bytes, received ${bytes.length}`);
|
|
4385
|
+
}
|
|
4386
|
+
return (0, import_hwk_adapter_core9.bytesToHex)(bytes);
|
|
4387
|
+
}
|
|
4388
|
+
function decodeBase58EnvelopeSignature(envelope) {
|
|
4389
|
+
const bytes = import_bs58.default.decode(envelope);
|
|
4390
|
+
if (bytes.length < 65 || bytes[0] !== 1) {
|
|
4391
|
+
throw new Error("Ledger Solana signature envelope is invalid");
|
|
4392
|
+
}
|
|
4393
|
+
return (0, import_hwk_adapter_core9.bytesToHex)(bytes.subarray(1, 65));
|
|
4394
|
+
}
|
|
3468
4395
|
async function _createSolSigner(ctx, sessionId) {
|
|
3469
4396
|
const dmk = await ctx.getOrCreateDmk();
|
|
3470
|
-
const { ContextModuleBuilder: ContextModuleBuilder2 } = await ctx.importLedgerKit(
|
|
4397
|
+
const { ContextModuleBuilder: ContextModuleBuilder2, ContextModuleChainID: ContextModuleChainID2 } = await ctx.importLedgerKit(
|
|
4398
|
+
"@ledgerhq/context-module"
|
|
4399
|
+
);
|
|
3471
4400
|
const { SignerSolanaBuilder } = await ctx.importLedgerKit("@ledgerhq/device-signer-kit-solana");
|
|
3472
|
-
const contextModule = new ContextModuleBuilder2({}).removeDefaultLoaders().build();
|
|
4401
|
+
const contextModule = new ContextModuleBuilder2({}).setChain(ContextModuleChainID2.Solana).removeDefaultLoaders().build();
|
|
3473
4402
|
const sdkSigner = new SignerSolanaBuilder({ dmk, sessionId }).withContextModule(contextModule).build();
|
|
3474
4403
|
const signer = new SignerSol(sdkSigner);
|
|
3475
4404
|
signer.onInteraction = (interaction) => {
|
|
@@ -3485,360 +4414,151 @@ async function _createSolSigner(ctx, sessionId) {
|
|
|
3485
4414
|
return signer;
|
|
3486
4415
|
}
|
|
3487
4416
|
|
|
3488
|
-
// src/connector/chains/
|
|
3489
|
-
var import_hwk_adapter_core12 = require("@onekeyfe/hwk-adapter-core");
|
|
3490
|
-
var import_hw_app_trx = __toESM(require("@ledgerhq/hw-app-trx"));
|
|
3491
|
-
|
|
3492
|
-
// src/connector/chains/legacyChainCall.ts
|
|
3493
|
-
var import_hwk_adapter_core11 = require("@onekeyfe/hwk-adapter-core");
|
|
3494
|
-
|
|
3495
|
-
// src/app/AppManager.ts
|
|
3496
|
-
var import_device_management_kit2 = require("@ledgerhq/device-management-kit");
|
|
4417
|
+
// src/connector/chains/zcash.ts
|
|
3497
4418
|
var import_hwk_adapter_core10 = require("@onekeyfe/hwk-adapter-core");
|
|
3498
|
-
|
|
3499
|
-
|
|
3500
|
-
|
|
3501
|
-
|
|
3502
|
-
|
|
3503
|
-
|
|
3504
|
-
ADA: "Cardano",
|
|
3505
|
-
DOT: "Polkadot",
|
|
3506
|
-
ATOM: "Cosmos"
|
|
3507
|
-
};
|
|
3508
|
-
var DASHBOARD_APP_NAME = "BOLOS";
|
|
3509
|
-
var AppManager = class {
|
|
3510
|
-
constructor(dmk, options) {
|
|
3511
|
-
this._dmk = dmk;
|
|
3512
|
-
this._waitMs = options?.waitMs ?? 1e3;
|
|
3513
|
-
this._maxRetries = options?.maxRetries ?? 10;
|
|
3514
|
-
}
|
|
3515
|
-
/**
|
|
3516
|
-
* Return the Ledger app name for a given chain ticker,
|
|
3517
|
-
* or undefined if the chain is not supported.
|
|
3518
|
-
*/
|
|
3519
|
-
static getAppName(chain) {
|
|
3520
|
-
return APP_NAME_MAP[chain];
|
|
3521
|
-
}
|
|
3522
|
-
/**
|
|
3523
|
-
* Ensure the target app is open on the device identified by `sessionId`.
|
|
3524
|
-
*
|
|
3525
|
-
* Flow:
|
|
3526
|
-
* 1. Check the currently running app.
|
|
3527
|
-
* 2. If it is already the target, return immediately.
|
|
3528
|
-
* 3. If a different app is running (not dashboard), close it first.
|
|
3529
|
-
* 4. Open the target app.
|
|
3530
|
-
* 5. Poll until the device confirms the target app is running.
|
|
3531
|
-
*/
|
|
3532
|
-
/**
|
|
3533
|
-
* @param onConfirmOnDevice Called BEFORE OpenAppCommand is issued — the
|
|
3534
|
-
* device is about to display "Open <app>" on screen and wait for the
|
|
3535
|
-
* user's button press. UI consumers should show their "open app" prompt
|
|
3536
|
-
* in response. NOT called when the target app is already open (no user
|
|
3537
|
-
* interaction needed in that case).
|
|
3538
|
-
*
|
|
3539
|
-
* Important: OpenAppCommand is blocking. It does not resolve until the user
|
|
3540
|
-
* has physically confirmed on the device, so anything that runs AFTER
|
|
3541
|
-
* `await this._openApp(...)` lands AFTER the prompt is already gone.
|
|
3542
|
-
* Hence the callback must fire BEFORE that await.
|
|
3543
|
-
*/
|
|
3544
|
-
async ensureAppOpen(sessionId, targetAppName, onConfirmOnDevice) {
|
|
3545
|
-
const currentApp = await this._getCurrentApp(sessionId);
|
|
3546
|
-
if (currentApp === targetAppName) {
|
|
3547
|
-
return;
|
|
3548
|
-
}
|
|
3549
|
-
if (!this._isDashboard(currentApp)) {
|
|
3550
|
-
await this._closeCurrentApp(sessionId);
|
|
3551
|
-
await this._waitForApp(sessionId, DASHBOARD_APP_NAME);
|
|
3552
|
-
}
|
|
3553
|
-
onConfirmOnDevice?.();
|
|
3554
|
-
await this._openApp(sessionId, targetAppName);
|
|
3555
|
-
await this._waitForApp(sessionId, targetAppName);
|
|
4419
|
+
|
|
4420
|
+
// src/signer/SignerZcash.ts
|
|
4421
|
+
var SignerZcash = class {
|
|
4422
|
+
// eslint-disable-next-line no-useless-constructor, no-empty-function
|
|
4423
|
+
constructor(_sdk) {
|
|
4424
|
+
this._sdk = _sdk;
|
|
3556
4425
|
}
|
|
3557
|
-
|
|
3558
|
-
|
|
3559
|
-
|
|
3560
|
-
|
|
3561
|
-
|
|
3562
|
-
|
|
3563
|
-
|
|
3564
|
-
|
|
3565
|
-
if ((0, import_device_management_kit2.isSuccessCommandResult)(result)) {
|
|
3566
|
-
debugLog("[AppManager] currentApp:", result.data.name);
|
|
3567
|
-
return result.data.name;
|
|
3568
|
-
}
|
|
3569
|
-
const errResult = result;
|
|
3570
|
-
const dmkErr = errResult.error ?? {};
|
|
3571
|
-
const original = dmkErr.originalError;
|
|
3572
|
-
debugLog(
|
|
3573
|
-
"[AppManager] _getCurrentApp failed sessionId=",
|
|
3574
|
-
sessionId,
|
|
3575
|
-
"tag=",
|
|
3576
|
-
dmkErr._tag,
|
|
3577
|
-
"errorCode=",
|
|
3578
|
-
dmkErr.errorCode,
|
|
3579
|
-
"message=",
|
|
3580
|
-
dmkErr.message,
|
|
3581
|
-
"originalErrorMessage=",
|
|
3582
|
-
original?.message ?? String(original ?? "")
|
|
3583
|
-
);
|
|
3584
|
-
throw Object.assign(
|
|
3585
|
-
new Error(
|
|
3586
|
-
dmkErr.message ?? "Failed to get current app from device"
|
|
3587
|
-
),
|
|
3588
|
-
{
|
|
3589
|
-
_tag: dmkErr._tag,
|
|
3590
|
-
errorCode: dmkErr.errorCode,
|
|
3591
|
-
originalError: original
|
|
3592
|
-
}
|
|
4426
|
+
/** GET_VK: UFVK string (default) or raw 96-byte Orchard FVK. */
|
|
4427
|
+
async getFullViewingKey(derivationPath, options) {
|
|
4428
|
+
const action = this._sdk.getFullViewingKey(derivationPath, options);
|
|
4429
|
+
return deviceActionToPromise(
|
|
4430
|
+
action,
|
|
4431
|
+
this.onInteraction,
|
|
4432
|
+
void 0,
|
|
4433
|
+
this.onRegisterCanceller
|
|
3593
4434
|
);
|
|
3594
4435
|
}
|
|
3595
|
-
|
|
3596
|
-
|
|
3597
|
-
|
|
3598
|
-
|
|
3599
|
-
|
|
3600
|
-
|
|
3601
|
-
|
|
3602
|
-
|
|
3603
|
-
const message = "message" in dmkErr && typeof dmkErr.message === "string" ? dmkErr.message : "";
|
|
3604
|
-
debugLog(
|
|
3605
|
-
"[AppManager] openApp failed:",
|
|
3606
|
-
appName,
|
|
3607
|
-
"errorCode:",
|
|
3608
|
-
errorCode,
|
|
3609
|
-
"tag:",
|
|
3610
|
-
dmkErr._tag
|
|
3611
|
-
);
|
|
3612
|
-
let code;
|
|
3613
|
-
if (errorCode === "6807" || /unknown application/i.test(message)) {
|
|
3614
|
-
code = import_hwk_adapter_core10.HardwareErrorCode.AppNotInstalled;
|
|
3615
|
-
} else if (errorCode === "5501" || dmkErr._tag === "ActionRefusedError") {
|
|
3616
|
-
code = import_hwk_adapter_core10.HardwareErrorCode.UserRejected;
|
|
3617
|
-
}
|
|
3618
|
-
throw Object.assign(new Error(`Failed to open "${appName}"`), {
|
|
3619
|
-
_tag: ERROR_TAG.OpenAppCommand,
|
|
3620
|
-
code,
|
|
3621
|
-
errorCode,
|
|
3622
|
-
statusCode: errorCode,
|
|
3623
|
-
appName,
|
|
3624
|
-
originalError: dmkErr
|
|
3625
|
-
});
|
|
3626
|
-
}
|
|
3627
|
-
}
|
|
3628
|
-
async _closeCurrentApp(sessionId) {
|
|
3629
|
-
debugLog("[AppManager] closeCurrentApp");
|
|
3630
|
-
await this._dmk.sendCommand({
|
|
3631
|
-
sessionId,
|
|
3632
|
-
command: new import_device_management_kit2.CloseAppCommand()
|
|
3633
|
-
});
|
|
3634
|
-
}
|
|
3635
|
-
/**
|
|
3636
|
-
* Poll the device until the expected app is reported as running,
|
|
3637
|
-
* or throw after `_maxRetries` attempts.
|
|
3638
|
-
*/
|
|
3639
|
-
async _waitForApp(sessionId, expectedAppName) {
|
|
3640
|
-
let lastSeen = "";
|
|
3641
|
-
for (let i = 0; i < this._maxRetries; i++) {
|
|
3642
|
-
await this._wait();
|
|
3643
|
-
const current = await this._getCurrentApp(sessionId);
|
|
3644
|
-
lastSeen = current;
|
|
3645
|
-
if (current === expectedAppName) {
|
|
3646
|
-
return;
|
|
3647
|
-
}
|
|
3648
|
-
}
|
|
3649
|
-
debugLog(
|
|
3650
|
-
"[AppManager] waitForApp exhausted: expected=",
|
|
3651
|
-
expectedAppName,
|
|
3652
|
-
"lastSeen=",
|
|
3653
|
-
lastSeen
|
|
3654
|
-
);
|
|
3655
|
-
throw new Error(
|
|
3656
|
-
`Ledger: failed to open "${expectedAppName}" after ${this._maxRetries} retries (last seen: ${lastSeen})`
|
|
4436
|
+
/** GET_SHIELDED_ADDRESS: single-Orchard-receiver UA for a 5-level transparent path. */
|
|
4437
|
+
async getShieldedAddress(derivationPath, options) {
|
|
4438
|
+
const action = this._sdk.getShieldedAddress(derivationPath, options);
|
|
4439
|
+
return deviceActionToPromise(
|
|
4440
|
+
action,
|
|
4441
|
+
this.onInteraction,
|
|
4442
|
+
void 0,
|
|
4443
|
+
this.onRegisterCanceller
|
|
3657
4444
|
);
|
|
3658
4445
|
}
|
|
3659
|
-
_isDashboard(appName) {
|
|
3660
|
-
return appName === DASHBOARD_APP_NAME;
|
|
3661
|
-
}
|
|
3662
|
-
_wait() {
|
|
3663
|
-
return new Promise((resolve) => setTimeout(resolve, this._waitMs));
|
|
3664
|
-
}
|
|
3665
4446
|
};
|
|
3666
4447
|
|
|
3667
|
-
// src/connector/chains/
|
|
3668
|
-
function
|
|
3669
|
-
|
|
4448
|
+
// src/connector/chains/zcash.ts
|
|
4449
|
+
async function zcashGetFullViewingKey(ctx, sessionId, params) {
|
|
4450
|
+
const signer = await _createZcashSigner(ctx, sessionId);
|
|
4451
|
+
const path = normalizePath(params.path);
|
|
4452
|
+
const mode = params.mode ?? "ufvk";
|
|
4453
|
+
return runSignerCall(ctx, sessionId, async () => {
|
|
4454
|
+
const result = await signer.getFullViewingKey(path, { mode });
|
|
4455
|
+
if (result.mode === "ufvk") {
|
|
4456
|
+
return { path: params.path, mode, ufvk: result.fullViewingKey };
|
|
4457
|
+
}
|
|
4458
|
+
return { path: params.path, mode, orchardFvk: (0, import_hwk_adapter_core10.bytesToHex)(result.fullViewingKey) };
|
|
4459
|
+
});
|
|
3670
4460
|
}
|
|
3671
|
-
async function
|
|
3672
|
-
const
|
|
3673
|
-
|
|
3674
|
-
|
|
3675
|
-
|
|
3676
|
-
|
|
3677
|
-
type: import_hwk_adapter_core11.EConnectorInteraction.ConfirmOpenApp,
|
|
3678
|
-
payload: { sessionId }
|
|
4461
|
+
async function zcashGetShieldedAddress(ctx, sessionId, params) {
|
|
4462
|
+
const signer = await _createZcashSigner(ctx, sessionId);
|
|
4463
|
+
const path = normalizePath(params.path);
|
|
4464
|
+
return runSignerCall(ctx, sessionId, async () => {
|
|
4465
|
+
const result = await signer.getShieldedAddress(path, {
|
|
4466
|
+
checkOnDevice: params.showOnDevice ?? false
|
|
3679
4467
|
});
|
|
3680
|
-
|
|
3681
|
-
|
|
3682
|
-
if (openAppPromptShown) {
|
|
3683
|
-
ctx.emit("ui-event", {
|
|
3684
|
-
type: import_hwk_adapter_core11.EConnectorInteraction.InteractionComplete,
|
|
3685
|
-
payload: { sessionId }
|
|
3686
|
-
});
|
|
3687
|
-
openAppPromptShown = false;
|
|
3688
|
-
}
|
|
3689
|
-
};
|
|
3690
|
-
try {
|
|
3691
|
-
await _ensureAppOpen(ctx, sessionId, appName, onAppOpenPrompt);
|
|
3692
|
-
} catch (err) {
|
|
3693
|
-
debugLog(
|
|
3694
|
-
"[LegacyChainCall] pre-flight ensureAppOpen failed:",
|
|
3695
|
-
appName,
|
|
3696
|
-
err?.message
|
|
3697
|
-
);
|
|
3698
|
-
closeOpenAppUiIfShown();
|
|
3699
|
-
throw ctx.wrapError(err, { defaultAppName: appName });
|
|
3700
|
-
}
|
|
3701
|
-
const runOnce = async () => {
|
|
3702
|
-
let confirmEmitted = false;
|
|
3703
|
-
if (needsConfirmation) {
|
|
3704
|
-
ctx.emit("ui-event", {
|
|
3705
|
-
type: import_hwk_adapter_core11.EConnectorInteraction.ConfirmOnDevice,
|
|
3706
|
-
payload: { sessionId }
|
|
3707
|
-
});
|
|
3708
|
-
confirmEmitted = true;
|
|
3709
|
-
}
|
|
3710
|
-
try {
|
|
3711
|
-
return await action(sessionId);
|
|
3712
|
-
} finally {
|
|
3713
|
-
if (confirmEmitted || openAppPromptShown) {
|
|
3714
|
-
ctx.emit("ui-event", {
|
|
3715
|
-
type: import_hwk_adapter_core11.EConnectorInteraction.InteractionComplete,
|
|
3716
|
-
payload: { sessionId }
|
|
3717
|
-
});
|
|
3718
|
-
openAppPromptShown = false;
|
|
3719
|
-
}
|
|
3720
|
-
}
|
|
3721
|
-
};
|
|
3722
|
-
try {
|
|
3723
|
-
return await runOnce();
|
|
3724
|
-
} catch (err) {
|
|
3725
|
-
if (!isLegacyWrongAppError(err, appName)) {
|
|
3726
|
-
debugLog("[LegacyChainCall] non-wrong-app failure:", appName, err?.message);
|
|
3727
|
-
ctx.invalidateSession(sessionId);
|
|
3728
|
-
throw ctx.wrapError(err, { defaultAppName: appName });
|
|
3729
|
-
}
|
|
3730
|
-
debugLog("[LegacyChainCall] wrong-app detected, retrying:", appName);
|
|
3731
|
-
try {
|
|
3732
|
-
await _ensureAppOpen(ctx, sessionId, appName, onAppOpenPrompt);
|
|
3733
|
-
} catch (switchErr) {
|
|
3734
|
-
debugLog(
|
|
3735
|
-
"[LegacyChainCall] retry ensureAppOpen failed:",
|
|
3736
|
-
appName,
|
|
3737
|
-
switchErr?.message
|
|
3738
|
-
);
|
|
3739
|
-
closeOpenAppUiIfShown();
|
|
3740
|
-
throw ctx.wrapError(switchErr, { defaultAppName: appName });
|
|
3741
|
-
}
|
|
3742
|
-
ctx.clearAllSigners();
|
|
3743
|
-
const result = await runOnce();
|
|
3744
|
-
debugLog("[LegacyChainCall] retry succeeded:", appName);
|
|
3745
|
-
return result;
|
|
3746
|
-
}
|
|
4468
|
+
return { address: result.address, path: params.path };
|
|
4469
|
+
});
|
|
3747
4470
|
}
|
|
3748
|
-
async function
|
|
4471
|
+
async function _createZcashSigner(ctx, sessionId) {
|
|
3749
4472
|
const dmk = await ctx.getOrCreateDmk();
|
|
3750
|
-
const
|
|
3751
|
-
|
|
4473
|
+
const { SignerZcashBuilder } = await ctx.importLedgerKit("@ledgerhq/device-signer-kit-zcash");
|
|
4474
|
+
const sdkSigner = new SignerZcashBuilder({ dmk, sessionId }).build();
|
|
4475
|
+
return wireSignerToSession(ctx, sessionId, "zcash", new SignerZcash(sdkSigner));
|
|
3752
4476
|
}
|
|
3753
4477
|
|
|
3754
|
-
// src/
|
|
3755
|
-
var
|
|
3756
|
-
|
|
3757
|
-
|
|
3758
|
-
|
|
3759
|
-
|
|
3760
|
-
|
|
4478
|
+
// src/connector/chains/tron.ts
|
|
4479
|
+
var import_hwk_adapter_core11 = require("@onekeyfe/hwk-adapter-core");
|
|
4480
|
+
|
|
4481
|
+
// src/signer/SignerTron.ts
|
|
4482
|
+
var SignerTron = class {
|
|
4483
|
+
// eslint-disable-next-line no-useless-constructor, no-empty-function
|
|
4484
|
+
constructor(_sdk) {
|
|
4485
|
+
this._sdk = _sdk;
|
|
3761
4486
|
}
|
|
3762
|
-
|
|
3763
|
-
|
|
3764
|
-
|
|
3765
|
-
|
|
3766
|
-
|
|
3767
|
-
|
|
3768
|
-
|
|
3769
|
-
|
|
3770
|
-
|
|
3771
|
-
}
|
|
3772
|
-
result.set(statusCode, data.length);
|
|
3773
|
-
return result;
|
|
4487
|
+
/** Base58 Tron address plus the uncompressed public key at `derivationPath`. */
|
|
4488
|
+
async getAddress(derivationPath, options) {
|
|
4489
|
+
const action = this._sdk.getAddress(derivationPath, options);
|
|
4490
|
+
const result = await deviceActionToPromise(
|
|
4491
|
+
action,
|
|
4492
|
+
this.onInteraction,
|
|
4493
|
+
void 0,
|
|
4494
|
+
this.onRegisterCanceller
|
|
4495
|
+
);
|
|
4496
|
+
return { address: result.address, publicKey: result.publicKey };
|
|
3774
4497
|
}
|
|
3775
|
-
|
|
4498
|
+
/** Sign a protobuf-encoded raw transaction. */
|
|
4499
|
+
async signTransaction(derivationPath, transaction, options) {
|
|
4500
|
+
const action = this._sdk.signTransaction(derivationPath, transaction, options);
|
|
4501
|
+
return deviceActionToPromise(
|
|
4502
|
+
action,
|
|
4503
|
+
this.onInteraction,
|
|
4504
|
+
void 0,
|
|
4505
|
+
this.onRegisterCanceller
|
|
4506
|
+
);
|
|
4507
|
+
}
|
|
4508
|
+
/** Sign a personal message (TIP-191). */
|
|
4509
|
+
async signPersonalMessage(derivationPath, message, options) {
|
|
4510
|
+
const action = this._sdk.signPersonalMessage(derivationPath, message, options);
|
|
4511
|
+
return deviceActionToPromise(
|
|
4512
|
+
action,
|
|
4513
|
+
this.onInteraction,
|
|
4514
|
+
void 0,
|
|
4515
|
+
this.onRegisterCanceller
|
|
4516
|
+
);
|
|
3776
4517
|
}
|
|
3777
4518
|
};
|
|
3778
4519
|
|
|
3779
4520
|
// src/connector/chains/tron.ts
|
|
3780
4521
|
async function tronGetAddress(ctx, sessionId, params) {
|
|
4522
|
+
const tronSigner = await _createTronSigner(ctx, sessionId);
|
|
3781
4523
|
const path = normalizePath(params.path);
|
|
3782
|
-
|
|
3783
|
-
|
|
3784
|
-
|
|
3785
|
-
|
|
3786
|
-
{
|
|
3787
|
-
|
|
3788
|
-
// Only show "confirm on device" UI when the device is actually going
|
|
3789
|
-
// to display the address for the user to verify.
|
|
3790
|
-
needsConfirmation: showOnDevice
|
|
3791
|
-
},
|
|
3792
|
-
async (sid) => {
|
|
3793
|
-
const trx = await _createTrx(ctx, sid);
|
|
3794
|
-
const result = await trx.getAddress(path, showOnDevice);
|
|
3795
|
-
return { address: result.address, publicKey: result.publicKey, path: params.path };
|
|
3796
|
-
}
|
|
3797
|
-
);
|
|
4524
|
+
return runSignerCall(ctx, sessionId, async () => {
|
|
4525
|
+
const result = await tronSigner.getAddress(path, {
|
|
4526
|
+
checkOnDevice: params.showOnDevice ?? false
|
|
4527
|
+
});
|
|
4528
|
+
return { address: result.address, publicKey: result.publicKey, path: params.path };
|
|
4529
|
+
});
|
|
3798
4530
|
}
|
|
3799
4531
|
async function tronSignTransaction(ctx, sessionId, params) {
|
|
3800
4532
|
if (!params.rawTxHex) {
|
|
3801
4533
|
throw Object.assign(
|
|
3802
4534
|
new Error("TRON signing requires a protobuf-encoded raw transaction hex (rawTxHex)."),
|
|
3803
|
-
{ code:
|
|
4535
|
+
{ code: import_hwk_adapter_core11.HardwareErrorCode.InvalidParams }
|
|
3804
4536
|
);
|
|
3805
4537
|
}
|
|
4538
|
+
const tronSigner = await _createTronSigner(ctx, sessionId);
|
|
3806
4539
|
const path = normalizePath(params.path);
|
|
3807
|
-
return
|
|
3808
|
-
|
|
3809
|
-
|
|
3810
|
-
|
|
3811
|
-
async (sid) => {
|
|
3812
|
-
const trx = await _createTrx(ctx, sid);
|
|
3813
|
-
const signature = await trx.signTransaction(
|
|
3814
|
-
path,
|
|
3815
|
-
params.rawTxHex,
|
|
3816
|
-
params.tokenSignatures ?? []
|
|
3817
|
-
);
|
|
3818
|
-
return { signature };
|
|
3819
|
-
}
|
|
3820
|
-
);
|
|
4540
|
+
return runSignerCall(ctx, sessionId, async () => {
|
|
4541
|
+
const signature = await tronSigner.signTransaction(path, (0, import_hwk_adapter_core11.hexToBytes)(params.rawTxHex));
|
|
4542
|
+
return { signature: (0, import_hwk_adapter_core11.bytesToHex)(signature) };
|
|
4543
|
+
});
|
|
3821
4544
|
}
|
|
3822
4545
|
async function tronSignMessage(ctx, sessionId, params) {
|
|
4546
|
+
const tronSigner = await _createTronSigner(ctx, sessionId);
|
|
3823
4547
|
const path = normalizePath(params.path);
|
|
3824
|
-
return
|
|
3825
|
-
|
|
3826
|
-
|
|
3827
|
-
|
|
3828
|
-
async (sid) => {
|
|
3829
|
-
const trx = await _createTrx(ctx, sid);
|
|
3830
|
-
const signature = await trx.signPersonalMessage(path, params.messageHex);
|
|
3831
|
-
return { signature };
|
|
3832
|
-
}
|
|
3833
|
-
);
|
|
4548
|
+
return runSignerCall(ctx, sessionId, async () => {
|
|
4549
|
+
const signature = await tronSigner.signPersonalMessage(path, (0, import_hwk_adapter_core11.hexToBytes)(params.messageHex));
|
|
4550
|
+
return { signature: (0, import_hwk_adapter_core11.bytesToHex)(signature) };
|
|
4551
|
+
});
|
|
3834
4552
|
}
|
|
3835
|
-
async function
|
|
4553
|
+
async function _createTronSigner(ctx, sessionId) {
|
|
3836
4554
|
const dmk = await ctx.getOrCreateDmk();
|
|
3837
|
-
|
|
4555
|
+
const { SignerTrxBuilder } = await ctx.importLedgerKit("@ledgerhq/device-signer-kit-tron");
|
|
4556
|
+
const sdkSigner = new SignerTrxBuilder({ dmk, sessionId }).build();
|
|
4557
|
+
return wireSignerToSession(ctx, sessionId, "tron", new SignerTron(sdkSigner));
|
|
3838
4558
|
}
|
|
3839
4559
|
|
|
3840
4560
|
// src/device-apps/customActions.ts
|
|
3841
|
-
var
|
|
4561
|
+
var import_device_management_kit2 = require("@ledgerhq/device-management-kit");
|
|
3842
4562
|
var import_rxjs = require("rxjs");
|
|
3843
4563
|
var GetOsVersionDeviceAction = class {
|
|
3844
4564
|
// eslint-disable-next-line no-useless-constructor, no-empty-function
|
|
@@ -3852,7 +4572,7 @@ var GetOsVersionDeviceAction = class {
|
|
|
3852
4572
|
(async () => {
|
|
3853
4573
|
try {
|
|
3854
4574
|
subject.next({
|
|
3855
|
-
status:
|
|
4575
|
+
status: import_device_management_kit2.DeviceActionStatus.Pending,
|
|
3856
4576
|
intermediateValue: { requiredUserInteraction: "none" }
|
|
3857
4577
|
});
|
|
3858
4578
|
const result = await internalApi.sendCommand(new this._deps.GetOsVersionCommand());
|
|
@@ -3861,11 +4581,11 @@ var GetOsVersionDeviceAction = class {
|
|
|
3861
4581
|
const errObj = result?.error;
|
|
3862
4582
|
throw new Error(errObj?.message ?? "GetOsVersionCommand failed");
|
|
3863
4583
|
}
|
|
3864
|
-
subject.next({ status:
|
|
4584
|
+
subject.next({ status: import_device_management_kit2.DeviceActionStatus.Completed, output: result.data });
|
|
3865
4585
|
subject.complete();
|
|
3866
4586
|
} catch (err) {
|
|
3867
4587
|
if (cancelled) return;
|
|
3868
|
-
subject.next({ status:
|
|
4588
|
+
subject.next({ status: import_device_management_kit2.DeviceActionStatus.Error, error: err });
|
|
3869
4589
|
subject.complete();
|
|
3870
4590
|
}
|
|
3871
4591
|
})();
|
|
@@ -3873,7 +4593,7 @@ var GetOsVersionDeviceAction = class {
|
|
|
3873
4593
|
observable: subject.asObservable(),
|
|
3874
4594
|
cancel: () => {
|
|
3875
4595
|
cancelled = true;
|
|
3876
|
-
subject.next({ status:
|
|
4596
|
+
subject.next({ status: import_device_management_kit2.DeviceActionStatus.Stopped });
|
|
3877
4597
|
subject.complete();
|
|
3878
4598
|
}
|
|
3879
4599
|
};
|
|
@@ -3891,7 +4611,7 @@ var ListAvailableAppsDeviceAction = class {
|
|
|
3891
4611
|
(async () => {
|
|
3892
4612
|
try {
|
|
3893
4613
|
subject.next({
|
|
3894
|
-
status:
|
|
4614
|
+
status: import_device_management_kit2.DeviceActionStatus.Pending,
|
|
3895
4615
|
intermediateValue: { requiredUserInteraction: "none" }
|
|
3896
4616
|
});
|
|
3897
4617
|
const osVersionResult = await internalApi.sendCommand(new this._GetOsVersionCommand());
|
|
@@ -3908,11 +4628,11 @@ var ListAvailableAppsDeviceAction = class {
|
|
|
3908
4628
|
throw new Error(httpErr?.message ?? "Manager API getAppList failed");
|
|
3909
4629
|
}
|
|
3910
4630
|
const apps = either.extract();
|
|
3911
|
-
subject.next({ status:
|
|
4631
|
+
subject.next({ status: import_device_management_kit2.DeviceActionStatus.Completed, output: apps });
|
|
3912
4632
|
subject.complete();
|
|
3913
4633
|
} catch (err) {
|
|
3914
4634
|
if (cancelled) return;
|
|
3915
|
-
subject.next({ status:
|
|
4635
|
+
subject.next({ status: import_device_management_kit2.DeviceActionStatus.Error, error: err });
|
|
3916
4636
|
subject.complete();
|
|
3917
4637
|
}
|
|
3918
4638
|
})();
|
|
@@ -3920,7 +4640,7 @@ var ListAvailableAppsDeviceAction = class {
|
|
|
3920
4640
|
observable: subject.asObservable(),
|
|
3921
4641
|
cancel: () => {
|
|
3922
4642
|
cancelled = true;
|
|
3923
|
-
subject.next({ status:
|
|
4643
|
+
subject.next({ status: import_device_management_kit2.DeviceActionStatus.Stopped });
|
|
3924
4644
|
subject.complete();
|
|
3925
4645
|
}
|
|
3926
4646
|
};
|
|
@@ -4013,7 +4733,7 @@ var DeviceApps = class {
|
|
|
4013
4733
|
seTargetId: v.seTargetId,
|
|
4014
4734
|
mcuTargetId: v.mcuTargetId,
|
|
4015
4735
|
seVersion: v.seVersion,
|
|
4016
|
-
seFlagsHex:
|
|
4736
|
+
seFlagsHex: bytesToHex4(v.seFlags),
|
|
4017
4737
|
mcuSephVersion: v.mcuSephVersion,
|
|
4018
4738
|
mcuBootloaderVersion: v.mcuBootloaderVersion,
|
|
4019
4739
|
hwVersion: v.hwVersion
|
|
@@ -4087,7 +4807,7 @@ var DeviceApps = class {
|
|
|
4087
4807
|
}
|
|
4088
4808
|
}
|
|
4089
4809
|
};
|
|
4090
|
-
function
|
|
4810
|
+
function bytesToHex4(bytes) {
|
|
4091
4811
|
if (!bytes) return "";
|
|
4092
4812
|
return Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join("");
|
|
4093
4813
|
}
|
|
@@ -4130,10 +4850,11 @@ var METHOD_PREFIX_TO_APP_NAME = {
|
|
|
4130
4850
|
evm: "Ethereum",
|
|
4131
4851
|
btc: "Bitcoin",
|
|
4132
4852
|
sol: "Solana",
|
|
4133
|
-
tron: "Tron"
|
|
4853
|
+
tron: "Tron",
|
|
4854
|
+
zcash: "Zcash"
|
|
4134
4855
|
};
|
|
4135
4856
|
var HARDWARE_ERROR_CODE_VALUES = new Set(
|
|
4136
|
-
Object.values(
|
|
4857
|
+
Object.values(import_hwk_adapter_core12.HardwareErrorCode).filter((value) => typeof value === "number")
|
|
4137
4858
|
);
|
|
4138
4859
|
var BLE_CONNECT_SCAN_TIMEOUT_MS = 1500;
|
|
4139
4860
|
var LEDGER_RELAY_ALLOWED_ROOT_DOMAINS = ["onekeytest.com", "onekey.com"];
|
|
@@ -4170,6 +4891,10 @@ async function defaultLedgerKitImporter(pkg) {
|
|
|
4170
4891
|
return import("@ledgerhq/device-signer-kit-bitcoin");
|
|
4171
4892
|
case "@ledgerhq/device-signer-kit-solana":
|
|
4172
4893
|
return import("@ledgerhq/device-signer-kit-solana");
|
|
4894
|
+
case "@ledgerhq/device-signer-kit-tron":
|
|
4895
|
+
return import("@ledgerhq/device-signer-kit-tron");
|
|
4896
|
+
case "@ledgerhq/device-signer-kit-zcash":
|
|
4897
|
+
return import("@ledgerhq/device-signer-kit-zcash");
|
|
4173
4898
|
case "@ledgerhq/context-module":
|
|
4174
4899
|
return import("@ledgerhq/context-module");
|
|
4175
4900
|
default:
|
|
@@ -4229,6 +4954,7 @@ var LedgerConnectorBase = class {
|
|
|
4229
4954
|
this._ctx = {
|
|
4230
4955
|
emit: (event, data) => this._emit(event, data),
|
|
4231
4956
|
invalidateSession: (sid) => this._invalidateSession(sid),
|
|
4957
|
+
teardownSecureChannelSession: (sid) => this._teardownSecureChannelSession(sid),
|
|
4232
4958
|
wrapError: (err, opts) => this._wrapError(err, opts),
|
|
4233
4959
|
getOrCreateDmk: () => this._getOrCreateDmk(),
|
|
4234
4960
|
getDeviceManager: () => this._getDeviceManager(),
|
|
@@ -4280,7 +5006,7 @@ var LedgerConnectorBase = class {
|
|
|
4280
5006
|
async searchDevices() {
|
|
4281
5007
|
const dm = await this._getDeviceManager();
|
|
4282
5008
|
const descriptors = await this._discoverDescriptors(dm);
|
|
4283
|
-
const resolvedDescriptors = descriptors.filter((d) => !(0,
|
|
5009
|
+
const resolvedDescriptors = descriptors.filter((d) => !(0, import_hwk_adapter_core12.isKnownNonTargetHardwareVendor)(d, "ledger")).map((d) => ({
|
|
4284
5010
|
descriptor: d,
|
|
4285
5011
|
connectId: this._resolveConnectId(d)
|
|
4286
5012
|
}));
|
|
@@ -4328,7 +5054,7 @@ var LedgerConnectorBase = class {
|
|
|
4328
5054
|
`Ledger BLE connect did not return within ${HANG_CEILING_MS / 6e4}min \u2014 DMK hang fallback.`
|
|
4329
5055
|
);
|
|
4330
5056
|
err._tag = ERROR_TAG.BlePairingTimeout;
|
|
4331
|
-
err.code =
|
|
5057
|
+
err.code = import_hwk_adapter_core12.HardwareErrorCode.BlePairingTimeout;
|
|
4332
5058
|
reject(err);
|
|
4333
5059
|
}, HANG_CEILING_MS);
|
|
4334
5060
|
});
|
|
@@ -4361,7 +5087,7 @@ var LedgerConnectorBase = class {
|
|
|
4361
5087
|
"Ledger device is not currently advertising. Wake up and unlock the device, keep it nearby, then try again."
|
|
4362
5088
|
);
|
|
4363
5089
|
err._tag = ERROR_TAG.DeviceNotAdvertising;
|
|
4364
|
-
err.code =
|
|
5090
|
+
err.code = import_hwk_adapter_core12.HardwareErrorCode.DeviceNotFound;
|
|
4365
5091
|
throw err;
|
|
4366
5092
|
};
|
|
4367
5093
|
const doConnect = async (path) => {
|
|
@@ -4418,14 +5144,14 @@ var LedgerConnectorBase = class {
|
|
|
4418
5144
|
this._resetSignersAndSessions();
|
|
4419
5145
|
if (isLedgerBleConnectionType(this.connectionType)) {
|
|
4420
5146
|
const tag = err?._tag;
|
|
4421
|
-
if (isKnownConnectionTag(tag)) {
|
|
5147
|
+
if (isKnownConnectionTag(tag) && !isConnectionOpeningTag(tag)) {
|
|
4422
5148
|
throw err;
|
|
4423
5149
|
}
|
|
4424
5150
|
const wrapped = new Error(
|
|
4425
5151
|
"Ledger Bluetooth pairing failed. Make sure the device is unlocked and nearby, then try again."
|
|
4426
5152
|
);
|
|
4427
5153
|
wrapped._tag = ERROR_TAG.BleGattBondingFailed;
|
|
4428
|
-
wrapped.code =
|
|
5154
|
+
wrapped.code = import_hwk_adapter_core12.HardwareErrorCode.BlePairingTimeout;
|
|
4429
5155
|
wrapped.originalError = err;
|
|
4430
5156
|
throw wrapped;
|
|
4431
5157
|
}
|
|
@@ -4503,7 +5229,7 @@ var LedgerConnectorBase = class {
|
|
|
4503
5229
|
this._unwatchSessionState(sessionId);
|
|
4504
5230
|
this._signerManager?.invalidate(sessionId);
|
|
4505
5231
|
this._cancellers.get(sessionId)?.({
|
|
4506
|
-
code:
|
|
5232
|
+
code: import_hwk_adapter_core12.HardwareErrorCode.DeviceDisconnected,
|
|
4507
5233
|
tag: "DeviceDisconnected",
|
|
4508
5234
|
message: "Device disconnected"
|
|
4509
5235
|
});
|
|
@@ -4527,9 +5253,9 @@ var LedgerConnectorBase = class {
|
|
|
4527
5253
|
if (isAppStuckByApdu(err)) {
|
|
4528
5254
|
return {
|
|
4529
5255
|
success: false,
|
|
4530
|
-
error: (0,
|
|
5256
|
+
error: (0, import_hwk_adapter_core12.serializeConnectorError)(
|
|
4531
5257
|
Object.assign(new Error("Ledger app is unresponsive"), {
|
|
4532
|
-
code:
|
|
5258
|
+
code: import_hwk_adapter_core12.HardwareErrorCode.DeviceAppStuck,
|
|
4533
5259
|
_tag: ERROR_TAG.DeviceAppStuck,
|
|
4534
5260
|
originalError: err
|
|
4535
5261
|
})
|
|
@@ -4539,16 +5265,16 @@ var LedgerConnectorBase = class {
|
|
|
4539
5265
|
if (isTransportStuck(err)) {
|
|
4540
5266
|
return {
|
|
4541
5267
|
success: false,
|
|
4542
|
-
error: (0,
|
|
5268
|
+
error: (0, import_hwk_adapter_core12.serializeConnectorError)(
|
|
4543
5269
|
Object.assign(new Error("Device communication interrupted, please retry"), {
|
|
4544
|
-
code:
|
|
5270
|
+
code: import_hwk_adapter_core12.HardwareErrorCode.TransportError,
|
|
4545
5271
|
_tag: ERROR_TAG.DeviceTransportStuck,
|
|
4546
5272
|
originalError: err
|
|
4547
5273
|
})
|
|
4548
5274
|
)
|
|
4549
5275
|
};
|
|
4550
5276
|
}
|
|
4551
|
-
return { success: false, error: (0,
|
|
5277
|
+
return { success: false, error: (0, import_hwk_adapter_core12.serializeConnectorError)(err) };
|
|
4552
5278
|
}
|
|
4553
5279
|
}
|
|
4554
5280
|
async _dispatch(sessionId, method, params) {
|
|
@@ -4587,6 +5313,11 @@ var LedgerConnectorBase = class {
|
|
|
4587
5313
|
return solSignTransaction(ctx, sessionId, params);
|
|
4588
5314
|
case "solSignMessage":
|
|
4589
5315
|
return solSignMessage(ctx, sessionId, params);
|
|
5316
|
+
// ZCASH
|
|
5317
|
+
case "zcashGetFullViewingKey":
|
|
5318
|
+
return zcashGetFullViewingKey(ctx, sessionId, params);
|
|
5319
|
+
case "zcashGetShieldedAddress":
|
|
5320
|
+
return zcashGetShieldedAddress(ctx, sessionId, params);
|
|
4590
5321
|
// TRON
|
|
4591
5322
|
case "tronGetAddress":
|
|
4592
5323
|
return tronGetAddress(ctx, sessionId, params);
|
|
@@ -4611,7 +5342,7 @@ var LedgerConnectorBase = class {
|
|
|
4611
5342
|
try {
|
|
4612
5343
|
return await apps.install(p.appName, ({ progress }) => {
|
|
4613
5344
|
ctx.emit("ui-event", {
|
|
4614
|
-
type:
|
|
5345
|
+
type: import_hwk_adapter_core12.EConnectorInteraction.AppInstallProgress,
|
|
4615
5346
|
payload: {
|
|
4616
5347
|
sessionId,
|
|
4617
5348
|
appName: p.appName,
|
|
@@ -4620,7 +5351,7 @@ var LedgerConnectorBase = class {
|
|
|
4620
5351
|
});
|
|
4621
5352
|
});
|
|
4622
5353
|
} catch (err) {
|
|
4623
|
-
ctx.
|
|
5354
|
+
ctx.teardownSecureChannelSession(sessionId);
|
|
4624
5355
|
throw ctx.wrapError(err);
|
|
4625
5356
|
} finally {
|
|
4626
5357
|
ctx.clearCanceller(sessionId);
|
|
@@ -4710,7 +5441,7 @@ var LedgerConnectorBase = class {
|
|
|
4710
5441
|
);
|
|
4711
5442
|
return { isGenuine: output.isGenuine, deviceId };
|
|
4712
5443
|
} catch (err) {
|
|
4713
|
-
ctx.
|
|
5444
|
+
ctx.teardownSecureChannelSession(sessionId);
|
|
4714
5445
|
throw ctx.wrapError(err);
|
|
4715
5446
|
} finally {
|
|
4716
5447
|
ctx.clearCanceller(sessionId);
|
|
@@ -4829,9 +5560,26 @@ var LedgerConnectorBase = class {
|
|
|
4829
5560
|
}
|
|
4830
5561
|
return this._deviceAppsManager;
|
|
4831
5562
|
}
|
|
5563
|
+
// DeviceAppsManager is a per-call factory with no cached session state, so
|
|
5564
|
+
// there is nothing to invalidate for it here.
|
|
4832
5565
|
_invalidateSession(sessionId) {
|
|
4833
5566
|
this._signerManager?.invalidate(sessionId);
|
|
4834
5567
|
}
|
|
5568
|
+
/**
|
|
5569
|
+
* Fire the canceller so DMK closes the secure-channel WebSocket. The DMK device
|
|
5570
|
+
* session is kept: unlock/retry recovery reuses it.
|
|
5571
|
+
*/
|
|
5572
|
+
_teardownSecureChannelSession(sessionId) {
|
|
5573
|
+
const cancel = this._cancellers.get(sessionId);
|
|
5574
|
+
this._cancellers.delete(sessionId);
|
|
5575
|
+
if (cancel) {
|
|
5576
|
+
try {
|
|
5577
|
+
cancel();
|
|
5578
|
+
} catch {
|
|
5579
|
+
}
|
|
5580
|
+
}
|
|
5581
|
+
this._invalidateSession(sessionId);
|
|
5582
|
+
}
|
|
4835
5583
|
/**
|
|
4836
5584
|
* Replace an old session with a new one after app switch.
|
|
4837
5585
|
* Emits device-connect so the adapter updates its _sessions Map.
|
|
@@ -4916,7 +5664,7 @@ var LedgerConnectorBase = class {
|
|
|
4916
5664
|
* at every catch site. Falls through unchanged for unknown methods.
|
|
4917
5665
|
*/
|
|
4918
5666
|
_ctxForMethod(method) {
|
|
4919
|
-
const prefix = /^(evm|btc|sol|tron)/.exec(method)?.[1];
|
|
5667
|
+
const prefix = /^(evm|btc|sol|tron|zcash)/.exec(method)?.[1];
|
|
4920
5668
|
const defaultAppName = prefix ? METHOD_PREFIX_TO_APP_NAME[prefix] : void 0;
|
|
4921
5669
|
if (!defaultAppName) return this._ctx;
|
|
4922
5670
|
return {
|
|
@@ -4950,6 +5698,204 @@ var LedgerConnectorBase = class {
|
|
|
4950
5698
|
return error;
|
|
4951
5699
|
}
|
|
4952
5700
|
};
|
|
5701
|
+
|
|
5702
|
+
// src/transport/DmkTransport.ts
|
|
5703
|
+
var import_hw_transport = __toESM(require("@ledgerhq/hw-transport"));
|
|
5704
|
+
var DmkTransport = class extends import_hw_transport.default {
|
|
5705
|
+
constructor(dmk, sessionId) {
|
|
5706
|
+
super();
|
|
5707
|
+
this._dmk = dmk;
|
|
5708
|
+
this._sessionId = sessionId;
|
|
5709
|
+
}
|
|
5710
|
+
async exchange(apdu) {
|
|
5711
|
+
const response = await this._dmk.sendApdu({
|
|
5712
|
+
sessionId: this._sessionId,
|
|
5713
|
+
apdu: new Uint8Array(apdu)
|
|
5714
|
+
});
|
|
5715
|
+
const { data, statusCode } = response;
|
|
5716
|
+
const result = Buffer.alloc(data.length + 2);
|
|
5717
|
+
if (data.length > 0) {
|
|
5718
|
+
result.set(data, 0);
|
|
5719
|
+
}
|
|
5720
|
+
result.set(statusCode, data.length);
|
|
5721
|
+
return result;
|
|
5722
|
+
}
|
|
5723
|
+
async close() {
|
|
5724
|
+
}
|
|
5725
|
+
};
|
|
5726
|
+
|
|
5727
|
+
// src/app/AppManager.ts
|
|
5728
|
+
var import_device_management_kit3 = require("@ledgerhq/device-management-kit");
|
|
5729
|
+
var import_hwk_adapter_core13 = require("@onekeyfe/hwk-adapter-core");
|
|
5730
|
+
var APP_NAME_MAP = {
|
|
5731
|
+
ETH: "Ethereum",
|
|
5732
|
+
BTC: "Bitcoin",
|
|
5733
|
+
SOL: "Solana",
|
|
5734
|
+
TRX: "Tron",
|
|
5735
|
+
ZEC: "Zcash",
|
|
5736
|
+
XRP: "XRP",
|
|
5737
|
+
ADA: "Cardano",
|
|
5738
|
+
DOT: "Polkadot",
|
|
5739
|
+
ATOM: "Cosmos"
|
|
5740
|
+
};
|
|
5741
|
+
var DASHBOARD_APP_NAME = "BOLOS";
|
|
5742
|
+
var AppManager = class {
|
|
5743
|
+
constructor(dmk, options) {
|
|
5744
|
+
this._dmk = dmk;
|
|
5745
|
+
this._waitMs = options?.waitMs ?? 1e3;
|
|
5746
|
+
this._maxRetries = options?.maxRetries ?? 10;
|
|
5747
|
+
}
|
|
5748
|
+
/**
|
|
5749
|
+
* Return the Ledger app name for a given chain ticker,
|
|
5750
|
+
* or undefined if the chain is not supported.
|
|
5751
|
+
*/
|
|
5752
|
+
static getAppName(chain) {
|
|
5753
|
+
return APP_NAME_MAP[chain];
|
|
5754
|
+
}
|
|
5755
|
+
/**
|
|
5756
|
+
* Ensure the target app is open on the device identified by `sessionId`.
|
|
5757
|
+
*
|
|
5758
|
+
* Flow:
|
|
5759
|
+
* 1. Check the currently running app.
|
|
5760
|
+
* 2. If it is already the target, return immediately.
|
|
5761
|
+
* 3. If a different app is running (not dashboard), close it first.
|
|
5762
|
+
* 4. Open the target app.
|
|
5763
|
+
* 5. Poll until the device confirms the target app is running.
|
|
5764
|
+
*/
|
|
5765
|
+
/**
|
|
5766
|
+
* @param onConfirmOnDevice Called BEFORE OpenAppCommand is issued — the
|
|
5767
|
+
* device is about to display "Open <app>" on screen and wait for the
|
|
5768
|
+
* user's button press. UI consumers should show their "open app" prompt
|
|
5769
|
+
* in response. NOT called when the target app is already open (no user
|
|
5770
|
+
* interaction needed in that case).
|
|
5771
|
+
*
|
|
5772
|
+
* Important: OpenAppCommand is blocking. It does not resolve until the user
|
|
5773
|
+
* has physically confirmed on the device, so anything that runs AFTER
|
|
5774
|
+
* `await this._openApp(...)` lands AFTER the prompt is already gone.
|
|
5775
|
+
* Hence the callback must fire BEFORE that await.
|
|
5776
|
+
*/
|
|
5777
|
+
async ensureAppOpen(sessionId, targetAppName, onConfirmOnDevice) {
|
|
5778
|
+
const currentApp = await this._getCurrentApp(sessionId);
|
|
5779
|
+
if (currentApp === targetAppName) {
|
|
5780
|
+
return;
|
|
5781
|
+
}
|
|
5782
|
+
if (!this._isDashboard(currentApp)) {
|
|
5783
|
+
await this._closeCurrentApp(sessionId);
|
|
5784
|
+
await this._waitForApp(sessionId, DASHBOARD_APP_NAME);
|
|
5785
|
+
}
|
|
5786
|
+
onConfirmOnDevice?.();
|
|
5787
|
+
await this._openApp(sessionId, targetAppName);
|
|
5788
|
+
await this._waitForApp(sessionId, targetAppName);
|
|
5789
|
+
}
|
|
5790
|
+
// ---------------------------------------------------------------------------
|
|
5791
|
+
// Private helpers
|
|
5792
|
+
// ---------------------------------------------------------------------------
|
|
5793
|
+
async _getCurrentApp(sessionId) {
|
|
5794
|
+
const result = await this._dmk.sendCommand({
|
|
5795
|
+
sessionId,
|
|
5796
|
+
command: new import_device_management_kit3.GetAppAndVersionCommand()
|
|
5797
|
+
});
|
|
5798
|
+
if ((0, import_device_management_kit3.isSuccessCommandResult)(result)) {
|
|
5799
|
+
debugLog("[AppManager] currentApp:", result.data.name);
|
|
5800
|
+
return result.data.name;
|
|
5801
|
+
}
|
|
5802
|
+
const errResult = result;
|
|
5803
|
+
const dmkErr = errResult.error ?? {};
|
|
5804
|
+
const original = dmkErr.originalError;
|
|
5805
|
+
debugLog(
|
|
5806
|
+
"[AppManager] _getCurrentApp failed sessionId=",
|
|
5807
|
+
sessionId,
|
|
5808
|
+
"tag=",
|
|
5809
|
+
dmkErr._tag,
|
|
5810
|
+
"errorCode=",
|
|
5811
|
+
dmkErr.errorCode,
|
|
5812
|
+
"message=",
|
|
5813
|
+
dmkErr.message,
|
|
5814
|
+
"originalErrorMessage=",
|
|
5815
|
+
original?.message ?? String(original ?? "")
|
|
5816
|
+
);
|
|
5817
|
+
throw Object.assign(
|
|
5818
|
+
new Error(
|
|
5819
|
+
dmkErr.message ?? "Failed to get current app from device"
|
|
5820
|
+
),
|
|
5821
|
+
{
|
|
5822
|
+
_tag: dmkErr._tag,
|
|
5823
|
+
errorCode: dmkErr.errorCode,
|
|
5824
|
+
originalError: original
|
|
5825
|
+
}
|
|
5826
|
+
);
|
|
5827
|
+
}
|
|
5828
|
+
async _openApp(sessionId, appName) {
|
|
5829
|
+
const result = await this._dmk.sendCommand({
|
|
5830
|
+
sessionId,
|
|
5831
|
+
command: new import_device_management_kit3.OpenAppCommand({ appName })
|
|
5832
|
+
});
|
|
5833
|
+
if (!(0, import_device_management_kit3.isSuccessCommandResult)(result)) {
|
|
5834
|
+
const dmkErr = result.error;
|
|
5835
|
+
const errorCode = "errorCode" in dmkErr && dmkErr.errorCode != null ? String(dmkErr.errorCode) : "";
|
|
5836
|
+
const message = "message" in dmkErr && typeof dmkErr.message === "string" ? dmkErr.message : "";
|
|
5837
|
+
debugLog(
|
|
5838
|
+
"[AppManager] openApp failed:",
|
|
5839
|
+
appName,
|
|
5840
|
+
"errorCode:",
|
|
5841
|
+
errorCode,
|
|
5842
|
+
"tag:",
|
|
5843
|
+
dmkErr._tag
|
|
5844
|
+
);
|
|
5845
|
+
let code;
|
|
5846
|
+
if (errorCode === "6807" || /unknown application/i.test(message)) {
|
|
5847
|
+
code = import_hwk_adapter_core13.HardwareErrorCode.AppNotInstalled;
|
|
5848
|
+
} else if (errorCode === "5501" || dmkErr._tag === "ActionRefusedError") {
|
|
5849
|
+
code = import_hwk_adapter_core13.HardwareErrorCode.UserRejected;
|
|
5850
|
+
}
|
|
5851
|
+
throw Object.assign(new Error(`Failed to open "${appName}"`), {
|
|
5852
|
+
_tag: ERROR_TAG.OpenAppCommand,
|
|
5853
|
+
code,
|
|
5854
|
+
errorCode,
|
|
5855
|
+
statusCode: errorCode,
|
|
5856
|
+
appName,
|
|
5857
|
+
originalError: dmkErr
|
|
5858
|
+
});
|
|
5859
|
+
}
|
|
5860
|
+
}
|
|
5861
|
+
async _closeCurrentApp(sessionId) {
|
|
5862
|
+
debugLog("[AppManager] closeCurrentApp");
|
|
5863
|
+
await this._dmk.sendCommand({
|
|
5864
|
+
sessionId,
|
|
5865
|
+
command: new import_device_management_kit3.CloseAppCommand()
|
|
5866
|
+
});
|
|
5867
|
+
}
|
|
5868
|
+
/**
|
|
5869
|
+
* Poll the device until the expected app is reported as running,
|
|
5870
|
+
* or throw after `_maxRetries` attempts.
|
|
5871
|
+
*/
|
|
5872
|
+
async _waitForApp(sessionId, expectedAppName) {
|
|
5873
|
+
let lastSeen = "";
|
|
5874
|
+
for (let i = 0; i < this._maxRetries; i++) {
|
|
5875
|
+
await this._wait();
|
|
5876
|
+
const current = await this._getCurrentApp(sessionId);
|
|
5877
|
+
lastSeen = current;
|
|
5878
|
+
if (current === expectedAppName) {
|
|
5879
|
+
return;
|
|
5880
|
+
}
|
|
5881
|
+
}
|
|
5882
|
+
debugLog(
|
|
5883
|
+
"[AppManager] waitForApp exhausted: expected=",
|
|
5884
|
+
expectedAppName,
|
|
5885
|
+
"lastSeen=",
|
|
5886
|
+
lastSeen
|
|
5887
|
+
);
|
|
5888
|
+
throw new Error(
|
|
5889
|
+
`Ledger: failed to open "${expectedAppName}" after ${this._maxRetries} retries (last seen: ${lastSeen})`
|
|
5890
|
+
);
|
|
5891
|
+
}
|
|
5892
|
+
_isDashboard(appName) {
|
|
5893
|
+
return appName === DASHBOARD_APP_NAME;
|
|
5894
|
+
}
|
|
5895
|
+
_wait() {
|
|
5896
|
+
return new Promise((resolve) => setTimeout(resolve, this._waitMs));
|
|
5897
|
+
}
|
|
5898
|
+
};
|
|
4953
5899
|
// Annotate the CommonJS export names for ESM import in node:
|
|
4954
5900
|
0 && (module.exports = {
|
|
4955
5901
|
AppManager,
|
|
@@ -4961,6 +5907,7 @@ var LedgerConnectorBase = class {
|
|
|
4961
5907
|
SignerEth,
|
|
4962
5908
|
SignerManager,
|
|
4963
5909
|
SignerSol,
|
|
5910
|
+
SignerZcash,
|
|
4964
5911
|
debugLog,
|
|
4965
5912
|
deviceActionToPromise,
|
|
4966
5913
|
isDeviceLockedError,
|