@onekeyfe/hwk-ledger-adapter 1.2.3-alpha.8 → 1.2.3
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 +29 -86
- package/dist/index.d.ts +29 -86
- package/dist/index.js +800 -1755
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +798 -1773
- package/dist/index.mjs.map +1 -1
- package/package.json +11 -14
package/dist/index.js
CHANGED
|
@@ -39,7 +39,6 @@ __export(index_exports, {
|
|
|
39
39
|
SignerEth: () => SignerEth,
|
|
40
40
|
SignerManager: () => SignerManager,
|
|
41
41
|
SignerSol: () => SignerSol,
|
|
42
|
-
SignerZcash: () => SignerZcash,
|
|
43
42
|
debugLog: () => debugLog,
|
|
44
43
|
deviceActionToPromise: () => deviceActionToPromise,
|
|
45
44
|
isDeviceLockedError: () => isDeviceLockedError,
|
|
@@ -58,14 +57,17 @@ var import_hwk_adapter_core3 = require("@onekeyfe/hwk-adapter-core");
|
|
|
58
57
|
|
|
59
58
|
// src/errors.ts
|
|
60
59
|
var import_hwk_adapter_core = require("@onekeyfe/hwk-adapter-core");
|
|
61
|
-
|
|
60
|
+
var MULTIPLE_USB_LEDGER_DEVICES_ERROR_MESSAGE = "Multiple Ledger USB devices are connected. Please connect only one Ledger device and try again.";
|
|
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) {
|
|
62
67
|
const payload = { error, code };
|
|
63
68
|
if (appName !== void 0) payload.appName = appName;
|
|
64
69
|
if (tag !== void 0) payload._tag = tag;
|
|
65
70
|
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);
|
|
69
71
|
return { success: false, payload };
|
|
70
72
|
}
|
|
71
73
|
var LOCKED_ERROR_CODES = /* @__PURE__ */ new Set(["5515", "21781", "6982", "27010", "5303", "21251"]);
|
|
@@ -206,12 +208,7 @@ var ERROR_TAG = {
|
|
|
206
208
|
UnknownDevice: "UnknownDeviceError",
|
|
207
209
|
DeviceSessionRefresher: "DeviceSessionRefresherError",
|
|
208
210
|
DeviceNotInitialized: "DeviceNotInitializedError",
|
|
209
|
-
|
|
210
|
-
// "ConnectionOpeningError" — and its typings widen `_tag` to `string`, which
|
|
211
|
-
// is why the class name looked authoritative. Both spellings are kept so a
|
|
212
|
-
// DMK version that aligns them does not silently drop back to UnknownError.
|
|
213
|
-
OpeningConnection: "ConnectionOpeningError",
|
|
214
|
-
OpeningConnectionLegacy: "OpeningConnectionError",
|
|
211
|
+
OpeningConnection: "OpeningConnectionError",
|
|
215
212
|
DeviceDisconnectedBeforeSendingApdu: "DeviceDisconnectedBeforeSendingApdu",
|
|
216
213
|
DeviceDisconnectedWhileSending: "DeviceDisconnectedWhileSendingError",
|
|
217
214
|
Disconnect: "DisconnectError",
|
|
@@ -224,18 +221,7 @@ var ERROR_TAG = {
|
|
|
224
221
|
// DMK remote-network failures (manager-api HTTP / secure-channel WS).
|
|
225
222
|
WebSocketConnection: "WebSocketConnectionError",
|
|
226
223
|
HttpFetch: "FetchError",
|
|
227
|
-
|
|
228
|
-
InvalidFirmwareMetadataResponse: "InvalidGetFirmwareMetadataResponseError",
|
|
229
|
-
ApplicationsMetadataTask: "GetApplicationsMetadataTaskError",
|
|
230
|
-
// DMK OS/secure-channel device actions. `SecureChannelError` is the residual
|
|
231
|
-
// bucket left by SecureChannelError.mapInstallDAErrors() after the locked /
|
|
232
|
-
// refused / already-installed / OOM cases have been split out, so it means
|
|
233
|
-
// "the relay itself broke", not "the device answered".
|
|
234
|
-
SecureChannel: "SecureChannelError",
|
|
235
|
-
RefusedByUserDA: "RefusedByUserDAError",
|
|
236
|
-
AppAlreadyInstalledDA: "AppAlreadyInstalledDAError",
|
|
237
|
-
OutOfMemoryDA: "OutOfMemoryDAError",
|
|
238
|
-
DeviceNotOnboarded: "DeviceNotOnboardedError"
|
|
224
|
+
InvalidFirmwareMetadataResponse: "InvalidGetFirmwareMetadataResponseError"
|
|
239
225
|
};
|
|
240
226
|
function isDeviceLockedError(err) {
|
|
241
227
|
if (!err || typeof err !== "object") return false;
|
|
@@ -265,6 +251,7 @@ function isBlePairingFailureError(err) {
|
|
|
265
251
|
return false;
|
|
266
252
|
}
|
|
267
253
|
var CONNECTION_LEVEL_TAGS = /* @__PURE__ */ new Set([
|
|
254
|
+
ERROR_TAG.DeviceLocked,
|
|
268
255
|
ERROR_TAG.DeviceNotAdvertising,
|
|
269
256
|
ERROR_TAG.BlePairingTimeout,
|
|
270
257
|
ERROR_TAG.BleGattBondingFailed,
|
|
@@ -276,7 +263,6 @@ var CONNECTION_LEVEL_TAGS = /* @__PURE__ */ new Set([
|
|
|
276
263
|
ERROR_TAG.DeviceSessionRefresher,
|
|
277
264
|
ERROR_TAG.DeviceNotInitialized,
|
|
278
265
|
ERROR_TAG.OpeningConnection,
|
|
279
|
-
ERROR_TAG.OpeningConnectionLegacy,
|
|
280
266
|
ERROR_TAG.DeviceDisconnectedBeforeSendingApdu,
|
|
281
267
|
ERROR_TAG.DeviceDisconnectedWhileSending,
|
|
282
268
|
ERROR_TAG.Disconnect,
|
|
@@ -291,10 +277,7 @@ var DEVICE_NOT_FOUND_TAGS = /* @__PURE__ */ new Set([
|
|
|
291
277
|
// Map to DeviceNotFound so non-BLE-direct paths get a sensible error code.
|
|
292
278
|
ERROR_TAG.DeviceNotInDiscoveryCache
|
|
293
279
|
]);
|
|
294
|
-
var DEVICE_BUSY_TAGS = /* @__PURE__ */ new Set([
|
|
295
|
-
ERROR_TAG.OpeningConnection,
|
|
296
|
-
ERROR_TAG.OpeningConnectionLegacy
|
|
297
|
-
]);
|
|
280
|
+
var DEVICE_BUSY_TAGS = /* @__PURE__ */ new Set([ERROR_TAG.OpeningConnection]);
|
|
298
281
|
var DEVICE_DISCONNECTED_TAGS = /* @__PURE__ */ new Set([
|
|
299
282
|
ERROR_TAG.DeviceNotRecognized,
|
|
300
283
|
ERROR_TAG.DeviceSessionNotFound,
|
|
@@ -317,13 +300,6 @@ function isConnectionLevelError(err) {
|
|
|
317
300
|
function isKnownConnectionTag(tag) {
|
|
318
301
|
return typeof tag === "string" && CONNECTION_LEVEL_TAGS.has(tag);
|
|
319
302
|
}
|
|
320
|
-
var CONNECTION_OPENING_TAGS = /* @__PURE__ */ new Set([
|
|
321
|
-
ERROR_TAG.OpeningConnection,
|
|
322
|
-
ERROR_TAG.OpeningConnectionLegacy
|
|
323
|
-
]);
|
|
324
|
-
function isConnectionOpeningTag(tag) {
|
|
325
|
-
return typeof tag === "string" && CONNECTION_OPENING_TAGS.has(tag);
|
|
326
|
-
}
|
|
327
303
|
function hasStatusCode(err, codeSet) {
|
|
328
304
|
if (!err || typeof err !== "object") return false;
|
|
329
305
|
const e = err;
|
|
@@ -359,14 +335,6 @@ function hasInvalidArgumentCode(err) {
|
|
|
359
335
|
if (e.error != null && e._tag && hasInvalidArgumentCode(e.error)) return true;
|
|
360
336
|
return false;
|
|
361
337
|
}
|
|
362
|
-
function hasErrorTag(err, tags) {
|
|
363
|
-
if (!err || typeof err !== "object") return false;
|
|
364
|
-
const e = err;
|
|
365
|
-
if (typeof e._tag === "string" && tags.has(e._tag)) return true;
|
|
366
|
-
if (e.originalError != null && hasErrorTag(e.originalError, tags)) return true;
|
|
367
|
-
if (e.error != null && e._tag && hasErrorTag(e.error, tags)) return true;
|
|
368
|
-
return false;
|
|
369
|
-
}
|
|
370
338
|
function isDeviceNotFoundError(err) {
|
|
371
339
|
if (!err || typeof err !== "object") return false;
|
|
372
340
|
const tag = err._tag;
|
|
@@ -385,14 +353,10 @@ function isDeviceBusyError(err) {
|
|
|
385
353
|
if (e.error != null && e._tag && isDeviceBusyError(e.error)) return true;
|
|
386
354
|
return false;
|
|
387
355
|
}
|
|
388
|
-
var USER_REJECTED_TAGS = /* @__PURE__ */ new Set([
|
|
389
|
-
ERROR_TAG.UserRefusedOnDevice,
|
|
390
|
-
ERROR_TAG.RefusedByUserDA
|
|
391
|
-
]);
|
|
392
356
|
function isUserRejectedError(err) {
|
|
393
357
|
if (!err || typeof err !== "object") return false;
|
|
394
358
|
const e = err;
|
|
395
|
-
if (
|
|
359
|
+
if (e._tag === ERROR_TAG.UserRefusedOnDevice) return true;
|
|
396
360
|
if (typeof e.message === "string" && /denied|rejected|refused/i.test(e.message)) return true;
|
|
397
361
|
if (hasStatusCode(err, USER_REJECTED_CODES)) return true;
|
|
398
362
|
return false;
|
|
@@ -423,37 +387,15 @@ function isAppNotInstalledError(err) {
|
|
|
423
387
|
if (hasStatusCode(err, APP_NOT_INSTALLED_CODES)) return true;
|
|
424
388
|
return false;
|
|
425
389
|
}
|
|
426
|
-
var OUT_OF_MEMORY_TAGS = /* @__PURE__ */ new Set([ERROR_TAG.OutOfMemoryDA]);
|
|
427
390
|
function isOutOfMemoryError(err) {
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
ERROR_TAG.AppAlreadyInstalledDA
|
|
432
|
-
]);
|
|
433
|
-
function isAppAlreadyInstalledError(err) {
|
|
434
|
-
return hasErrorTag(err, APP_ALREADY_INSTALLED_TAGS);
|
|
435
|
-
}
|
|
436
|
-
var DEVICE_NOT_ONBOARDED_TAGS = /* @__PURE__ */ new Set([
|
|
437
|
-
ERROR_TAG.DeviceNotOnboarded
|
|
438
|
-
]);
|
|
439
|
-
function isDeviceNotOnboardedError(err) {
|
|
440
|
-
return hasErrorTag(err, DEVICE_NOT_ONBOARDED_TAGS);
|
|
441
|
-
}
|
|
442
|
-
var FIRMWARE_METADATA_TAGS = /* @__PURE__ */ new Set([
|
|
443
|
-
ERROR_TAG.InvalidFirmwareMetadataResponse,
|
|
444
|
-
ERROR_TAG.ApplicationsMetadataTask
|
|
445
|
-
]);
|
|
446
|
-
function isFirmwareMetadataError(err) {
|
|
447
|
-
return hasErrorTag(err, FIRMWARE_METADATA_TAGS);
|
|
448
|
-
}
|
|
449
|
-
var SECURE_CHANNEL_TAGS = /* @__PURE__ */ new Set([ERROR_TAG.SecureChannel]);
|
|
450
|
-
function isSecureChannelError(err) {
|
|
451
|
-
return hasErrorTag(err, SECURE_CHANNEL_TAGS);
|
|
391
|
+
if (!err || typeof err !== "object") return false;
|
|
392
|
+
const e = err;
|
|
393
|
+
return e._tag === "OutOfMemoryDAError";
|
|
452
394
|
}
|
|
453
395
|
var NETWORK_ERROR_TAGS = /* @__PURE__ */ new Set([
|
|
454
396
|
ERROR_TAG.WebSocketConnection,
|
|
455
397
|
ERROR_TAG.HttpFetch,
|
|
456
|
-
ERROR_TAG.
|
|
398
|
+
ERROR_TAG.InvalidFirmwareMetadataResponse
|
|
457
399
|
]);
|
|
458
400
|
function isNetworkError(err) {
|
|
459
401
|
if (!err || typeof err !== "object") return false;
|
|
@@ -503,34 +445,23 @@ function isTransportStuck(err) {
|
|
|
503
445
|
function isStuckAppStateError(err) {
|
|
504
446
|
return isAppStuckByApdu(err) || isTransportStuck(err);
|
|
505
447
|
}
|
|
506
|
-
var DMK_PLACEHOLDER_MESSAGE = "Unknown error.";
|
|
507
|
-
function nestedErrorMessage(nested) {
|
|
508
|
-
if (!nested || typeof nested !== "object") return void 0;
|
|
509
|
-
const { message } = nested;
|
|
510
|
-
if (typeof message !== "string") return void 0;
|
|
511
|
-
const trimmed = message.trim();
|
|
512
|
-
if (!trimmed || trimmed === DMK_PLACEHOLDER_MESSAGE) return void 0;
|
|
513
|
-
return message;
|
|
514
|
-
}
|
|
515
448
|
function mapLedgerError(err, opts) {
|
|
516
449
|
let originalMessage = "Unknown Ledger error";
|
|
517
450
|
if (err instanceof Error) {
|
|
518
451
|
originalMessage = err.message;
|
|
519
452
|
} else if (err && typeof err === "object") {
|
|
520
453
|
const e = err;
|
|
521
|
-
originalMessage = String(
|
|
522
|
-
e.message ?? nestedErrorMessage(e.originalError) ?? e._tag ?? e.type ?? JSON.stringify(err)
|
|
523
|
-
);
|
|
454
|
+
originalMessage = String(e.message ?? e._tag ?? e.type ?? JSON.stringify(err));
|
|
524
455
|
}
|
|
525
456
|
let code;
|
|
526
457
|
if (isDeviceLockedError(err)) {
|
|
527
458
|
code = import_hwk_adapter_core.HardwareErrorCode.DeviceLocked;
|
|
528
459
|
} else if (isDeviceNotAdvertisingError(err) || isDeviceNotFoundError(err)) {
|
|
529
460
|
code = import_hwk_adapter_core.HardwareErrorCode.DeviceNotFound;
|
|
530
|
-
} else if (isBlePairingFailureError(err)) {
|
|
531
|
-
code = import_hwk_adapter_core.HardwareErrorCode.BlePairingTimeout;
|
|
532
461
|
} else if (isDeviceBusyError(err)) {
|
|
533
462
|
code = import_hwk_adapter_core.HardwareErrorCode.DeviceBusy;
|
|
463
|
+
} else if (isBlePairingFailureError(err)) {
|
|
464
|
+
code = import_hwk_adapter_core.HardwareErrorCode.BlePairingTimeout;
|
|
534
465
|
} else if (isUserAbortedError(err)) {
|
|
535
466
|
code = import_hwk_adapter_core.HardwareErrorCode.UserAborted;
|
|
536
467
|
} else if (isUserRejectedError(err)) {
|
|
@@ -539,18 +470,10 @@ function mapLedgerError(err, opts) {
|
|
|
539
470
|
code = import_hwk_adapter_core.HardwareErrorCode.WrongApp;
|
|
540
471
|
} else if (isAppNotInstalledError(err)) {
|
|
541
472
|
code = import_hwk_adapter_core.HardwareErrorCode.AppNotInstalled;
|
|
542
|
-
} else if (isAppAlreadyInstalledError(err)) {
|
|
543
|
-
code = import_hwk_adapter_core.HardwareErrorCode.AppAlreadyInstalled;
|
|
544
473
|
} else if (isOutOfMemoryError(err)) {
|
|
545
474
|
code = import_hwk_adapter_core.HardwareErrorCode.DeviceOutOfMemory;
|
|
546
|
-
} else if (isDeviceNotOnboardedError(err)) {
|
|
547
|
-
code = import_hwk_adapter_core.HardwareErrorCode.DeviceNotInitialized;
|
|
548
|
-
} else if (isFirmwareMetadataError(err)) {
|
|
549
|
-
code = import_hwk_adapter_core.HardwareErrorCode.LedgerFirmwareMetadataError;
|
|
550
475
|
} else if (isNetworkError(err)) {
|
|
551
476
|
code = import_hwk_adapter_core.HardwareErrorCode.NetworkError;
|
|
552
|
-
} else if (isSecureChannelError(err)) {
|
|
553
|
-
code = import_hwk_adapter_core.HardwareErrorCode.LedgerSecureChannelError;
|
|
554
477
|
} else if (isDeviceDisconnectedError(err)) {
|
|
555
478
|
code = import_hwk_adapter_core.HardwareErrorCode.DeviceDisconnected;
|
|
556
479
|
} else if (isTimeoutError(err)) {
|
|
@@ -563,21 +486,7 @@ function mapLedgerError(err, opts) {
|
|
|
563
486
|
}
|
|
564
487
|
const errAppName = err && typeof err === "object" ? err.appName : void 0;
|
|
565
488
|
const appName = errAppName ?? opts?.defaultAppName;
|
|
566
|
-
return {
|
|
567
|
-
code,
|
|
568
|
-
message: (0, import_hwk_adapter_core.enrichErrorMessage)(code, originalMessage),
|
|
569
|
-
origin: (0, import_hwk_adapter_core.defaultOriginForCode)(code),
|
|
570
|
-
appName
|
|
571
|
-
};
|
|
572
|
-
}
|
|
573
|
-
|
|
574
|
-
// src/utils/queueKey.ts
|
|
575
|
-
var LEDGER_DEFAULT_QUEUE_KEY = "__ledger_default__";
|
|
576
|
-
function ledgerQueueKey({
|
|
577
|
-
operationId,
|
|
578
|
-
connectId
|
|
579
|
-
}) {
|
|
580
|
-
return (operationId ?? connectId) || LEDGER_DEFAULT_QUEUE_KEY;
|
|
489
|
+
return { code, message: (0, import_hwk_adapter_core.enrichErrorMessage)(code, originalMessage), appName };
|
|
581
490
|
}
|
|
582
491
|
|
|
583
492
|
// src/adapter/methods/allNetworkGetAddress.ts
|
|
@@ -638,102 +547,65 @@ var LEDGER_BTC_NETWORK_COIN_MAP = {
|
|
|
638
547
|
var LEDGER_UNSUPPORTED_ALLNETWORK_NETWORKS = /* @__PURE__ */ new Set(["doge", "dogecoin"]);
|
|
639
548
|
function createAllNetworkGetAddress({
|
|
640
549
|
callChain,
|
|
641
|
-
getChainFingerprint
|
|
642
|
-
retainOperation,
|
|
643
|
-
errorToFailure,
|
|
644
|
-
createCancelScope
|
|
550
|
+
getChainFingerprint
|
|
645
551
|
}) {
|
|
646
552
|
return async function allNetworkGetAddress(connectId, _deviceId, params) {
|
|
647
|
-
debugLog("[LedgerAdapter][REQ]", {
|
|
648
|
-
method: "allNetworkGetAddress",
|
|
649
|
-
connectId,
|
|
650
|
-
itemCount: params.bundle.length
|
|
651
|
-
});
|
|
652
|
-
const target = (0, import_hwk_adapter_core2.resolveHardwareOperationTarget)(connectId, params.operationId, "ledger");
|
|
653
|
-
if (!target.success) return target;
|
|
654
|
-
const effectiveTargetId = target.payload.targetId ?? "";
|
|
655
|
-
let releaseOperationRetention;
|
|
656
|
-
try {
|
|
657
|
-
releaseOperationRetention = target.payload.operationId ? retainOperation(target.payload.operationId) : void 0;
|
|
658
|
-
} catch (error) {
|
|
659
|
-
return errorToFailure(error);
|
|
660
|
-
}
|
|
661
|
-
const cancelScope = createCancelScope(
|
|
662
|
-
ledgerQueueKey({ operationId: target.payload.operationId, connectId: effectiveTargetId })
|
|
663
|
-
);
|
|
553
|
+
debugLog("[LedgerAdapter][REQ]", { method: "allNetworkGetAddress", connectId, params });
|
|
664
554
|
const installContext = {};
|
|
665
555
|
const commonParams = {
|
|
666
|
-
autoInstallApp: params.autoInstallApp
|
|
667
|
-
operationId: target.payload.operationId,
|
|
668
|
-
knownConnections: params.knownConnections,
|
|
669
|
-
extra: params.extra,
|
|
670
|
-
allowDeviceSelection: params.allowDeviceSelection
|
|
556
|
+
autoInstallApp: params.autoInstallApp
|
|
671
557
|
};
|
|
672
558
|
const chainFingerprints = /* @__PURE__ */ new Map();
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
itemDeviceId,
|
|
687
|
-
method,
|
|
688
|
-
item,
|
|
689
|
-
commonParams,
|
|
690
|
-
installContext
|
|
691
|
-
);
|
|
692
|
-
},
|
|
693
|
-
attachIdentity: async ({ item, chain, payload }) => attachLedgerIdentity(
|
|
694
|
-
getChainFingerprint,
|
|
695
|
-
effectiveTargetId,
|
|
559
|
+
const result = await (0, import_hwk_adapter_core2.runAllNetworkGetAddress)({
|
|
560
|
+
connectId,
|
|
561
|
+
deviceId: _deviceId,
|
|
562
|
+
params,
|
|
563
|
+
normalizeItem: normalizeLedgerAllNetworkItem,
|
|
564
|
+
buildUnsupportedNetworkResponse: (item) => isUnsupportedLedgerAllNetworkNetwork(item) ? buildUnsupportedNetworkResponse(item) : void 0,
|
|
565
|
+
callItem: async ({ method, chain, item }) => {
|
|
566
|
+
const itemDeviceId = getItemDeviceId(item) ?? chainFingerprints.get(chain) ?? "";
|
|
567
|
+
return callAllNetworkMethod(
|
|
568
|
+
callChain,
|
|
569
|
+
connectId,
|
|
570
|
+
itemDeviceId,
|
|
571
|
+
method,
|
|
696
572
|
item,
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
}
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
573
|
+
commonParams,
|
|
574
|
+
installContext
|
|
575
|
+
);
|
|
576
|
+
},
|
|
577
|
+
attachIdentity: async ({ item, chain, payload }) => attachLedgerIdentity(
|
|
578
|
+
getChainFingerprint,
|
|
579
|
+
connectId,
|
|
580
|
+
item,
|
|
581
|
+
chain,
|
|
582
|
+
payload,
|
|
583
|
+
chainFingerprints
|
|
584
|
+
),
|
|
585
|
+
shouldAbortBundle: isTopLevelAllNetworkFailure,
|
|
586
|
+
buildTopLevelFailure: (response) => {
|
|
587
|
+
const code = response.payload?.code ?? import_hwk_adapter_core2.HardwareErrorCode.DeviceMismatch;
|
|
588
|
+
return (0, import_hwk_adapter_core2.failure)(
|
|
589
|
+
code,
|
|
590
|
+
response.payload?.error ?? "All-network get-address aborted",
|
|
591
|
+
response.payload?.params
|
|
592
|
+
);
|
|
593
|
+
}
|
|
594
|
+
});
|
|
595
|
+
debugLog("[LedgerAdapter][RES]", {
|
|
596
|
+
method: "allNetworkGetAddress",
|
|
597
|
+
success: result.success,
|
|
598
|
+
payload: result
|
|
599
|
+
});
|
|
600
|
+
return result;
|
|
723
601
|
};
|
|
724
602
|
}
|
|
725
|
-
function buildCancelledFailure(signal) {
|
|
726
|
-
const reason = signal.reason;
|
|
727
|
-
const code = typeof reason?.code === "number" ? reason.code : import_hwk_adapter_core2.HardwareErrorCode.UserAborted;
|
|
728
|
-
const message = typeof reason?.message === "string" ? reason.message : "";
|
|
729
|
-
return (0, import_hwk_adapter_core2.failure)(code, message || "All-network get-address cancelled");
|
|
730
|
-
}
|
|
731
603
|
function isTopLevelAllNetworkFailure(response) {
|
|
732
604
|
if (response.success) {
|
|
733
605
|
return false;
|
|
734
606
|
}
|
|
735
607
|
const code = response.payload?.code;
|
|
736
|
-
return code === import_hwk_adapter_core2.HardwareErrorCode.DeviceMismatch ||
|
|
608
|
+
return code === import_hwk_adapter_core2.HardwareErrorCode.DeviceMismatch || code === import_hwk_adapter_core2.HardwareErrorCode.UserAborted || code === import_hwk_adapter_core2.HardwareErrorCode.UserRejected;
|
|
737
609
|
}
|
|
738
610
|
function getItemDeviceId(item) {
|
|
739
611
|
const { deviceId } = item;
|
|
@@ -763,18 +635,8 @@ function normalizeLedgerAllNetworkItem(method, item) {
|
|
|
763
635
|
const coin = LEDGER_BTC_NETWORK_COIN_MAP[item.network];
|
|
764
636
|
return coin ? { ...item, coin } : item;
|
|
765
637
|
}
|
|
766
|
-
async function attachLedgerIdentity(getChainFingerprint, connectId, item, chain, payload, chainFingerprints
|
|
767
|
-
const
|
|
768
|
-
if (!knownFingerprint && cancelSignal.aborted) {
|
|
769
|
-
const cancelled = buildCancelledFailure(cancelSignal);
|
|
770
|
-
return { ...item, success: false, payload: cancelled.payload };
|
|
771
|
-
}
|
|
772
|
-
const fingerprint = knownFingerprint || await bootstrapChainFingerprint(
|
|
773
|
-
getChainFingerprint,
|
|
774
|
-
context.connection?.connectId ?? connectId,
|
|
775
|
-
chain,
|
|
776
|
-
context
|
|
777
|
-
);
|
|
638
|
+
async function attachLedgerIdentity(getChainFingerprint, connectId, item, chain, payload, chainFingerprints) {
|
|
639
|
+
const fingerprint = getItemDeviceId(item) || chainFingerprints.get(chain) || await bootstrapChainFingerprint(getChainFingerprint, connectId, chain);
|
|
778
640
|
if (!fingerprint) {
|
|
779
641
|
return buildFingerprintBootstrapFailure(item, chain);
|
|
780
642
|
}
|
|
@@ -795,8 +657,8 @@ async function attachLedgerIdentity(getChainFingerprint, connectId, item, chain,
|
|
|
795
657
|
}
|
|
796
658
|
};
|
|
797
659
|
}
|
|
798
|
-
async function bootstrapChainFingerprint(getChainFingerprint, connectId, chain
|
|
799
|
-
const response = await getChainFingerprint(connectId, chain
|
|
660
|
+
async function bootstrapChainFingerprint(getChainFingerprint, connectId, chain) {
|
|
661
|
+
const response = await getChainFingerprint(connectId, chain);
|
|
800
662
|
return response.success ? response.payload : "";
|
|
801
663
|
}
|
|
802
664
|
function buildFingerprintBootstrapFailure(item, chain) {
|
|
@@ -900,32 +762,9 @@ function btcAccountIndexFromPath(path) {
|
|
|
900
762
|
var _LedgerAdapter = class _LedgerAdapter {
|
|
901
763
|
constructor(connector, options) {
|
|
902
764
|
this.vendor = "ledger";
|
|
903
|
-
// A cancel releases the DMK device action and its intent-queue slot, so the
|
|
904
|
-
// next call is not blocked behind it. It still cannot retract a confirmation
|
|
905
|
-
// screen the device is already showing.
|
|
906
|
-
this.cancelCapability = "stops-waiting";
|
|
907
765
|
this.emitter = new import_hwk_adapter_core3.TypedEventEmitter();
|
|
908
|
-
this._operations = new import_hwk_adapter_core3.OperationRegistry({
|
|
909
|
-
vendor: "ledger",
|
|
910
|
-
onEnded: (operation, reason) => {
|
|
911
|
-
const binding = this._pendingOperationBindings.get(operation.operationId);
|
|
912
|
-
if (binding?.selectedConnection?.requestId === this._bindingSelectionRequestId) {
|
|
913
|
-
this._finishBleBinding("cancelled");
|
|
914
|
-
}
|
|
915
|
-
this._pendingOperationBindings.delete(operation.operationId);
|
|
916
|
-
this.emitter.emit(import_hwk_adapter_core3.SDK.OPERATION_ENDED, {
|
|
917
|
-
type: import_hwk_adapter_core3.SDK.OPERATION_ENDED,
|
|
918
|
-
payload: { operationId: operation.operationId, reason }
|
|
919
|
-
});
|
|
920
|
-
if (reason === "timeout") {
|
|
921
|
-
void this._releaseOperationConnection(operation);
|
|
922
|
-
}
|
|
923
|
-
}
|
|
924
|
-
});
|
|
925
766
|
this._discoveredDevices = /* @__PURE__ */ new Map();
|
|
926
767
|
this._sessions = /* @__PURE__ */ new Map();
|
|
927
|
-
this._pendingOperationBindings = /* @__PURE__ */ new Map();
|
|
928
|
-
this._verifiedBleReconnectTargets = /* @__PURE__ */ new Map();
|
|
929
768
|
this._uiRegistry = new import_hwk_adapter_core3.UiRequestRegistry();
|
|
930
769
|
// BTC App rejects account index >= 100 unless display=true. Cached per
|
|
931
770
|
// adapter instance: first 100+ path asks the user once via UI request,
|
|
@@ -939,31 +778,10 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
939
778
|
this._deviceAuthenticityQueueTail = Promise.resolve();
|
|
940
779
|
// Shared across concurrent callers — only `cancel()` aborts.
|
|
941
780
|
this._doConnectAbortController = null;
|
|
942
|
-
this._unsettledConnectorOperations = /* @__PURE__ */ new Map();
|
|
943
|
-
this._connectorIdleWaiters = /* @__PURE__ */ new Set();
|
|
944
|
-
this._resetPromise = null;
|
|
945
|
-
this._stateGeneration = 0;
|
|
946
|
-
this._connectorTeardownTail = Promise.resolve();
|
|
947
|
-
this._pendingConnectorTeardowns = 0;
|
|
948
|
-
this._activeOperationJobs = /* @__PURE__ */ new Set();
|
|
949
|
-
this._pendingOperationDisconnects = /* @__PURE__ */ new Set();
|
|
950
781
|
this._installProgressLastEmittedValue = -Infinity;
|
|
951
782
|
this.allNetworkGetAddress = createAllNetworkGetAddress({
|
|
952
783
|
callChain: this.callChain.bind(this),
|
|
953
|
-
getChainFingerprint:
|
|
954
|
-
try {
|
|
955
|
-
const fingerprint = await this._computeChainFingerprint(
|
|
956
|
-
chain,
|
|
957
|
-
(method, params) => this.connectorCall(connectId, method, params, void 0, void 0, void 0, context)
|
|
958
|
-
);
|
|
959
|
-
return (0, import_hwk_adapter_core3.success)(fingerprint);
|
|
960
|
-
} catch (error) {
|
|
961
|
-
return this.errorToFailure(error);
|
|
962
|
-
}
|
|
963
|
-
},
|
|
964
|
-
retainOperation: (operationId) => this._operations.retain(operationId),
|
|
965
|
-
errorToFailure: (error) => this.errorToFailure(error),
|
|
966
|
-
createCancelScope: (queueKey) => this._jobQueue.createCancelScope(queueKey)
|
|
784
|
+
getChainFingerprint: (connectId, chain) => this.getChainFingerprint(connectId, "", chain)
|
|
967
785
|
});
|
|
968
786
|
// ---------------------------------------------------------------------------
|
|
969
787
|
// Private helpers
|
|
@@ -973,7 +791,7 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
973
791
|
*
|
|
974
792
|
* - If a session already exists for the given connectId, reuse it.
|
|
975
793
|
* - If ANY session exists (Ledger IDs are ephemeral), reuse it.
|
|
976
|
-
* - Otherwise: search →
|
|
794
|
+
* - Otherwise: search → exactly 1 USB device auto-connects; multiple or none throws.
|
|
977
795
|
*/
|
|
978
796
|
// Mutex for ensureConnected — prevents concurrent calls from establishing duplicate connections
|
|
979
797
|
this._connectingPromise = null;
|
|
@@ -990,12 +808,6 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
990
808
|
});
|
|
991
809
|
};
|
|
992
810
|
this.deviceDisconnectHandler = (data) => {
|
|
993
|
-
const activeOperation = this._operations.findActiveByConnectionKey(data.connectId);
|
|
994
|
-
if (activeOperation && this._activeOperationJobs.has(activeOperation.operationId)) {
|
|
995
|
-
this._pendingOperationDisconnects.add(activeOperation.operationId);
|
|
996
|
-
} else {
|
|
997
|
-
this._operations.endByConnectionKey(data.connectId, "disconnect");
|
|
998
|
-
}
|
|
999
811
|
this._discoveredDevices.delete(data.connectId);
|
|
1000
812
|
this._sessions.delete(data.connectId);
|
|
1001
813
|
this.emitter.emit(import_hwk_adapter_core3.DEVICE.DISCONNECT, {
|
|
@@ -1052,28 +864,11 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
1052
864
|
this._jobQueue = new import_hwk_adapter_core3.DeviceJobQueue();
|
|
1053
865
|
this.registerEventListeners();
|
|
1054
866
|
}
|
|
1055
|
-
_finishBleBinding(status) {
|
|
1056
|
-
const selectionRequestId = this._bindingSelectionRequestId;
|
|
1057
|
-
this._bindingSelectionRequestId = void 0;
|
|
1058
|
-
if (selectionRequestId)
|
|
1059
|
-
this.emitter.emit(import_hwk_adapter_core3.UI_REQUEST.DEVICE_BINDING_STATUS, {
|
|
1060
|
-
type: import_hwk_adapter_core3.UI_REQUEST.DEVICE_BINDING_STATUS,
|
|
1061
|
-
payload: { selectionRequestId, status }
|
|
1062
|
-
});
|
|
1063
|
-
}
|
|
1064
|
-
_isBleConnection() {
|
|
1065
|
-
return isLedgerBleConnectionType(this._activeConnectionType ?? this.connector.connectionType);
|
|
1066
|
-
}
|
|
1067
867
|
// Transport
|
|
1068
868
|
get activeTransport() {
|
|
1069
|
-
return this.
|
|
869
|
+
return isLedgerBleConnectionType(this.connector.connectionType) ? "ble" : "hid";
|
|
1070
870
|
}
|
|
1071
871
|
getAvailableTransports() {
|
|
1072
|
-
if (this.connector.availableTransports) {
|
|
1073
|
-
return this.connector.availableTransports.map(
|
|
1074
|
-
(transport) => transport === "ble" ? "ble" : "hid"
|
|
1075
|
-
);
|
|
1076
|
-
}
|
|
1077
872
|
return this.activeTransport ? [this.activeTransport] : [];
|
|
1078
873
|
}
|
|
1079
874
|
// Connector is bound at construction; switching requires a new adapter.
|
|
@@ -1090,39 +885,21 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
1090
885
|
* The next operation will re-discover and re-connect automatically.
|
|
1091
886
|
*/
|
|
1092
887
|
resetState() {
|
|
1093
|
-
void this._resetStateAndDisconnectSessions();
|
|
1094
|
-
}
|
|
1095
|
-
_resetStateAndDisconnectSessions() {
|
|
1096
|
-
if (this._resetPromise) return this._resetPromise;
|
|
1097
|
-
const sessionIds = new Set(this._sessions.values());
|
|
1098
|
-
this._stateGeneration += 1;
|
|
1099
|
-
this._finishBleBinding("cancelled");
|
|
1100
|
-
this._operations.endAll("runtime-reset");
|
|
1101
|
-
this._doConnectAbortController?.abort();
|
|
1102
888
|
this._discoveredDevices.clear();
|
|
1103
889
|
this._sessions.clear();
|
|
1104
|
-
this._verifiedBleReconnectTargets.clear();
|
|
1105
890
|
this._connectingPromise = null;
|
|
1106
|
-
this._doConnectAbortController = null;
|
|
1107
891
|
this._uiRegistry.reset();
|
|
1108
892
|
this._jobQueue.clear();
|
|
1109
893
|
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
|
-
});
|
|
1121
894
|
}
|
|
1122
895
|
async dispose() {
|
|
1123
|
-
|
|
896
|
+
this._uiRegistry.reset();
|
|
897
|
+
this._jobQueue.clear();
|
|
1124
898
|
this.unregisterEventListeners();
|
|
1125
899
|
this.connector.reset();
|
|
900
|
+
this._discoveredDevices.clear();
|
|
901
|
+
this._sessions.clear();
|
|
902
|
+
this._btcHighIndexConfirmedThisSession = false;
|
|
1126
903
|
this.emitter.removeAllListeners();
|
|
1127
904
|
}
|
|
1128
905
|
uiResponse(response) {
|
|
@@ -1132,32 +909,17 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
1132
909
|
// Device management
|
|
1133
910
|
// ---------------------------------------------------------------------------
|
|
1134
911
|
async searchDevices(options) {
|
|
1135
|
-
return this._searchDevices(options);
|
|
1136
|
-
}
|
|
1137
|
-
async _searchDevices(options, signal) {
|
|
1138
912
|
debugLog("[LedgerAdapter][REQ]", { method: "searchDevices", params: options });
|
|
1139
913
|
try {
|
|
1140
914
|
if (options?.resetSession) {
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
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
|
-
});
|
|
915
|
+
this._doConnectAbortController?.abort();
|
|
916
|
+
this._sessions.clear();
|
|
917
|
+
this._connectingPromise = null;
|
|
918
|
+
this._doConnectAbortController = null;
|
|
919
|
+
this._btcHighIndexConfirmedThisSession = false;
|
|
1160
920
|
}
|
|
921
|
+
await this._ensureDevicePermission();
|
|
922
|
+
const devices = await this.connector.searchDevices();
|
|
1161
923
|
this._discoveredDevices.clear();
|
|
1162
924
|
for (const d of devices) {
|
|
1163
925
|
if (d.connectId) {
|
|
@@ -1165,7 +927,7 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
1165
927
|
}
|
|
1166
928
|
}
|
|
1167
929
|
if (this._discoveredDevices.size === 0) {
|
|
1168
|
-
await this._ensureDevicePermission(
|
|
930
|
+
await this._ensureDevicePermission();
|
|
1169
931
|
}
|
|
1170
932
|
const result = Array.from(this._discoveredDevices.values());
|
|
1171
933
|
debugLog("[LedgerAdapter][RES]", {
|
|
@@ -1184,287 +946,41 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
1184
946
|
throw err;
|
|
1185
947
|
}
|
|
1186
948
|
}
|
|
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
|
-
}
|
|
1208
949
|
// USB single-session invariant: evict all sessions, best-effort (see connectDevice).
|
|
1209
|
-
async _evictAllSessions(
|
|
1210
|
-
this._operations.endAll("explicit", preserveOperationId);
|
|
950
|
+
async _evictAllSessions() {
|
|
1211
951
|
if (this._sessions.size === 0) return;
|
|
1212
952
|
const stale = [...this._sessions.values()];
|
|
1213
953
|
this._sessions.clear();
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
} catch {
|
|
1219
|
-
}
|
|
954
|
+
for (const sid of stale) {
|
|
955
|
+
try {
|
|
956
|
+
await this.connector.disconnect(sid);
|
|
957
|
+
} catch {
|
|
1220
958
|
}
|
|
1221
|
-
}
|
|
959
|
+
}
|
|
1222
960
|
}
|
|
1223
961
|
static _createDeviceBusyError(method) {
|
|
1224
962
|
return Object.assign(new Error(`Ledger device is busy while calling ${method}`), {
|
|
1225
963
|
code: import_hwk_adapter_core3.HardwareErrorCode.DeviceBusy
|
|
1226
964
|
});
|
|
1227
965
|
}
|
|
1228
|
-
async connectDevice(
|
|
1229
|
-
try {
|
|
1230
|
-
return await this._jobQueue.enqueue(
|
|
1231
|
-
searchTargetId || "__ledger_connect__",
|
|
1232
|
-
async () => {
|
|
1233
|
-
const connected = await this._connectTarget(searchTargetId);
|
|
1234
|
-
if (!connected.success) return connected;
|
|
1235
|
-
return this._createOperation(searchTargetId, connected.payload);
|
|
1236
|
-
},
|
|
1237
|
-
{
|
|
1238
|
-
label: "connectDevice",
|
|
1239
|
-
rejectIfBusy: true,
|
|
1240
|
-
busyError: _LedgerAdapter._createDeviceBusyError("connectDevice")
|
|
1241
|
-
}
|
|
1242
|
-
);
|
|
1243
|
-
} catch (error) {
|
|
1244
|
-
return this.errorToFailure(error);
|
|
1245
|
-
}
|
|
1246
|
-
}
|
|
1247
|
-
async bindBleDevice(params) {
|
|
1248
|
-
if (params.identity.vendor !== "ledger" || !params.identity.value) {
|
|
1249
|
-
return (0, import_hwk_adapter_core3.failure)(import_hwk_adapter_core3.HardwareErrorCode.InvalidParams, "Ledger wallet identity is required");
|
|
1250
|
-
}
|
|
1251
|
-
const { chain, value: expectedFingerprint } = params.identity;
|
|
1252
|
-
try {
|
|
1253
|
-
return await this._jobQueue.enqueue(
|
|
1254
|
-
expectedFingerprint,
|
|
1255
|
-
async (signal) => {
|
|
1256
|
-
if (!this.getAvailableTransports().includes("ble")) {
|
|
1257
|
-
throw (0, import_hwk_adapter_core3.createHwkError)({
|
|
1258
|
-
code: import_hwk_adapter_core3.HardwareErrorCode.TransportNotAvailable,
|
|
1259
|
-
message: "Ledger Bluetooth transport is not available"
|
|
1260
|
-
});
|
|
1261
|
-
}
|
|
1262
|
-
this._activeConnectionType = "ble";
|
|
1263
|
-
await this._ensureDevicePermission(void 0, void 0, signal);
|
|
1264
|
-
const attempt = {
|
|
1265
|
-
extra: params.extra,
|
|
1266
|
-
bindingReason: "manual-rebind"
|
|
1267
|
-
};
|
|
1268
|
-
try {
|
|
1269
|
-
for (; ; ) {
|
|
1270
|
-
const connectId = await this._connectFirstOrSelect(
|
|
1271
|
-
[],
|
|
1272
|
-
void 0,
|
|
1273
|
-
true,
|
|
1274
|
-
void 0,
|
|
1275
|
-
attempt,
|
|
1276
|
-
signal
|
|
1277
|
-
);
|
|
1278
|
-
const sessionId = this._sessions.get(connectId);
|
|
1279
|
-
if (!sessionId) {
|
|
1280
|
-
throw (0, import_hwk_adapter_core3.createHwkError)({
|
|
1281
|
-
code: import_hwk_adapter_core3.HardwareErrorCode.DeviceDisconnected,
|
|
1282
|
-
message: "Selected Ledger connection ended"
|
|
1283
|
-
});
|
|
1284
|
-
}
|
|
1285
|
-
let saved = false;
|
|
1286
|
-
try {
|
|
1287
|
-
const installContext = {
|
|
1288
|
-
connection: { connectId, sessionId }
|
|
1289
|
-
};
|
|
1290
|
-
const fingerprint = await this._computeChainFingerprint(
|
|
1291
|
-
chain,
|
|
1292
|
-
(method, callParams) => this._runConnectorCall(
|
|
1293
|
-
connectId,
|
|
1294
|
-
method,
|
|
1295
|
-
callParams,
|
|
1296
|
-
signal,
|
|
1297
|
-
void 0,
|
|
1298
|
-
void 0,
|
|
1299
|
-
{ autoInstallApp: true },
|
|
1300
|
-
installContext
|
|
1301
|
-
)
|
|
1302
|
-
);
|
|
1303
|
-
if (fingerprint !== expectedFingerprint) {
|
|
1304
|
-
attempt.rejectedConnectIds ?? (attempt.rejectedConnectIds = /* @__PURE__ */ new Set());
|
|
1305
|
-
attempt.rejectedConnectIds.add(connectId);
|
|
1306
|
-
attempt.rejectedConnectId = connectId;
|
|
1307
|
-
} else {
|
|
1308
|
-
const persisted = await this._publishVerifiedBleBinding(
|
|
1309
|
-
connectId,
|
|
1310
|
-
chain,
|
|
1311
|
-
fingerprint,
|
|
1312
|
-
attempt,
|
|
1313
|
-
void 0,
|
|
1314
|
-
signal
|
|
1315
|
-
);
|
|
1316
|
-
if (!persisted) {
|
|
1317
|
-
throw (0, import_hwk_adapter_core3.createHwkError)({
|
|
1318
|
-
code: import_hwk_adapter_core3.HardwareErrorCode.UnknownError,
|
|
1319
|
-
message: "Bluetooth binding could not be saved",
|
|
1320
|
-
origin: "host"
|
|
1321
|
-
});
|
|
1322
|
-
}
|
|
1323
|
-
saved = true;
|
|
1324
|
-
return (0, import_hwk_adapter_core3.success)(connectId);
|
|
1325
|
-
}
|
|
1326
|
-
} finally {
|
|
1327
|
-
if (!saved && this._sessions.get(connectId) === sessionId) {
|
|
1328
|
-
this._sessions.delete(connectId);
|
|
1329
|
-
const teardown = this._runConnectorTeardown(
|
|
1330
|
-
() => this.connector.disconnect(sessionId)
|
|
1331
|
-
).catch(() => void 0);
|
|
1332
|
-
if (!signal.aborted) await teardown;
|
|
1333
|
-
}
|
|
1334
|
-
}
|
|
1335
|
-
}
|
|
1336
|
-
} catch (error) {
|
|
1337
|
-
this._finishBleBinding(signal.aborted ? "cancelled" : "failed");
|
|
1338
|
-
throw error;
|
|
1339
|
-
}
|
|
1340
|
-
},
|
|
1341
|
-
{
|
|
1342
|
-
label: "bindBleDevice",
|
|
1343
|
-
rejectIfBusy: true,
|
|
1344
|
-
busyError: _LedgerAdapter._createDeviceBusyError("bindBleDevice")
|
|
1345
|
-
}
|
|
1346
|
-
);
|
|
1347
|
-
} catch (error) {
|
|
1348
|
-
return this.errorToFailure(error);
|
|
1349
|
-
}
|
|
1350
|
-
}
|
|
1351
|
-
async acquireOperation(connectId, context) {
|
|
1352
|
-
try {
|
|
1353
|
-
return await this._jobQueue.enqueue(
|
|
1354
|
-
connectId || "__ledger_acquire__",
|
|
1355
|
-
async (signal) => {
|
|
1356
|
-
const transport = this._isBleConnection() ? "ble" : "usb";
|
|
1357
|
-
const hint = context.knownConnections?.find(
|
|
1358
|
-
(connection) => connection.transport === transport
|
|
1359
|
-
);
|
|
1360
|
-
const target = hint && hint.transport !== "qr" ? hint.connectId : connectId;
|
|
1361
|
-
await this._ensureDevicePermission(target, void 0, signal);
|
|
1362
|
-
const attempt = {
|
|
1363
|
-
...context,
|
|
1364
|
-
extra: context.extra ? { ...context.extra } : void 0
|
|
1365
|
-
};
|
|
1366
|
-
try {
|
|
1367
|
-
const resolvedConnectId = await this.ensureConnected(
|
|
1368
|
-
target,
|
|
1369
|
-
signal,
|
|
1370
|
-
true,
|
|
1371
|
-
void 0,
|
|
1372
|
-
attempt
|
|
1373
|
-
);
|
|
1374
|
-
_LedgerAdapter._throwIfAborted(signal);
|
|
1375
|
-
const result = this._createOperation(connectId, resolvedConnectId);
|
|
1376
|
-
if (result.success && attempt.selectedConnection) {
|
|
1377
|
-
this._pendingOperationBindings.set(result.payload, attempt);
|
|
1378
|
-
}
|
|
1379
|
-
return result;
|
|
1380
|
-
} catch (error) {
|
|
1381
|
-
this._finishBleBinding(signal.aborted ? "cancelled" : "failed");
|
|
1382
|
-
throw error;
|
|
1383
|
-
}
|
|
1384
|
-
},
|
|
1385
|
-
{
|
|
1386
|
-
label: "acquireOperation",
|
|
1387
|
-
rejectIfBusy: true,
|
|
1388
|
-
busyError: _LedgerAdapter._createDeviceBusyError("acquireOperation")
|
|
1389
|
-
}
|
|
1390
|
-
);
|
|
1391
|
-
} catch (error) {
|
|
1392
|
-
return this.errorToFailure(error);
|
|
1393
|
-
}
|
|
1394
|
-
}
|
|
1395
|
-
_createOperation(searchTargetId, resolvedConnectId) {
|
|
1396
|
-
this._operations.endByConnectionKey(resolvedConnectId, "explicit");
|
|
1397
|
-
const sessionId = this._sessions.get(resolvedConnectId);
|
|
1398
|
-
if (sessionId) this._operations.endByConnectionKey(sessionId, "explicit");
|
|
1399
|
-
const connectionType = this._isBleConnection() ? "ble" : "usb";
|
|
1400
|
-
const device = this._discoveredDevices.get(resolvedConnectId) ?? {
|
|
1401
|
-
vendor: "ledger",
|
|
1402
|
-
model: "unknown",
|
|
1403
|
-
firmwareVersion: "",
|
|
1404
|
-
deviceId: "",
|
|
1405
|
-
connectId: resolvedConnectId,
|
|
1406
|
-
connectionType
|
|
1407
|
-
};
|
|
1408
|
-
const operation = this._operations.create({
|
|
1409
|
-
searchTargetId,
|
|
1410
|
-
connectId: resolvedConnectId,
|
|
1411
|
-
device,
|
|
1412
|
-
connectionType,
|
|
1413
|
-
connectionKeys: [this._sessions.get(resolvedConnectId) ?? ""]
|
|
1414
|
-
});
|
|
1415
|
-
return (0, import_hwk_adapter_core3.success)(operation.operationId);
|
|
1416
|
-
}
|
|
1417
|
-
async _connectTarget(connectId, preserveOperationId, signal) {
|
|
966
|
+
async connectDevice(connectId) {
|
|
1418
967
|
debugLog("[LedgerAdapter][REQ]", { method: "connectDevice", connectId, params: { connectId } });
|
|
1419
968
|
try {
|
|
1420
|
-
this.
|
|
1421
|
-
const discoveredType = this._discoveredDevices.get(connectId)?.connectionType;
|
|
1422
|
-
if (discoveredType === "usb" || discoveredType === "ble") {
|
|
1423
|
-
this._activeConnectionType = discoveredType;
|
|
1424
|
-
}
|
|
1425
|
-
if (this._isBleConnection() && !connectId) {
|
|
969
|
+
if (isLedgerBleConnectionType(this.connector.connectionType) && !connectId) {
|
|
1426
970
|
throw Object.assign(new Error("Ledger BLE connectId is required."), {
|
|
1427
971
|
code: import_hwk_adapter_core3.HardwareErrorCode.DeviceNotFound
|
|
1428
972
|
});
|
|
1429
973
|
}
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
if (this.connector.availableTransports && this.connector.availableTransports.length > 1) {
|
|
1433
|
-
await this._evictAllSessions(preserveOperationId);
|
|
1434
|
-
} else if (this._isBleConnection()) {
|
|
1435
|
-
const previousSessionId = this._sessions.get(connectId);
|
|
1436
|
-
this._operations.endByConnectionKey(connectId, "explicit", preserveOperationId);
|
|
1437
|
-
if (previousSessionId) {
|
|
1438
|
-
this._operations.endByConnectionKey(previousSessionId, "explicit", preserveOperationId);
|
|
1439
|
-
this._sessions.delete(connectId);
|
|
1440
|
-
await this.connector.disconnect(previousSessionId).catch(() => void 0);
|
|
1441
|
-
}
|
|
1442
|
-
} else {
|
|
1443
|
-
await this._evictAllSessions(preserveOperationId);
|
|
1444
|
-
}
|
|
1445
|
-
if (signal) _LedgerAdapter._throwIfAborted(signal);
|
|
1446
|
-
const stateGeneration = this._stateGeneration;
|
|
1447
|
-
const releaseOperation = this._retainConnectorOperation(`connect:${connectId}`);
|
|
1448
|
-
let session;
|
|
1449
|
-
try {
|
|
1450
|
-
session = this.connector.availableTransports?.length ? await this.connector.connect(connectId, {
|
|
1451
|
-
transportType: this._activeConnectionType ?? "usb"
|
|
1452
|
-
}) : await this.connector.connect(connectId);
|
|
1453
|
-
if (signal?.aborted || stateGeneration !== this._stateGeneration) {
|
|
1454
|
-
await this.connector.disconnect(session.sessionId).catch(() => void 0);
|
|
1455
|
-
throw Object.assign(new Error("Ledger connection aborted"), {
|
|
1456
|
-
code: import_hwk_adapter_core3.HardwareErrorCode.UserAborted
|
|
1457
|
-
});
|
|
1458
|
-
}
|
|
1459
|
-
} finally {
|
|
1460
|
-
releaseOperation();
|
|
974
|
+
if (!isLedgerBleConnectionType(this.connector.connectionType)) {
|
|
975
|
+
await this._evictAllSessions();
|
|
1461
976
|
}
|
|
1462
|
-
|
|
1463
|
-
this.
|
|
977
|
+
await this._ensureDevicePermission(connectId);
|
|
978
|
+
const session = await this.connector.connect(connectId);
|
|
979
|
+
this._sessions.set(connectId, session.sessionId);
|
|
1464
980
|
if (session.deviceInfo) {
|
|
1465
|
-
this._discoveredDevices.set(
|
|
981
|
+
this._discoveredDevices.set(connectId, session.deviceInfo);
|
|
1466
982
|
}
|
|
1467
|
-
const result = (0, import_hwk_adapter_core3.success)(
|
|
983
|
+
const result = (0, import_hwk_adapter_core3.success)(connectId);
|
|
1468
984
|
debugLog("[LedgerAdapter][RES]", { method: "connectDevice", success: true, payload: result });
|
|
1469
985
|
return result;
|
|
1470
986
|
} catch (err) {
|
|
@@ -1477,60 +993,30 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
1477
993
|
return failureResult;
|
|
1478
994
|
}
|
|
1479
995
|
}
|
|
1480
|
-
async
|
|
1481
|
-
const operation = this._operations.find(operationId);
|
|
1482
|
-
if (!operation) {
|
|
1483
|
-
this._operations.resolve(operationId);
|
|
1484
|
-
return;
|
|
1485
|
-
}
|
|
1486
|
-
const endedOperation = this._operations.end(operationId, "explicit");
|
|
1487
|
-
if (!endedOperation) return;
|
|
1488
|
-
const { connectId } = operation;
|
|
996
|
+
async disconnectDevice(connectId) {
|
|
1489
997
|
debugLog("[LedgerAdapter][REQ]", {
|
|
1490
|
-
method: "
|
|
998
|
+
method: "disconnectDevice",
|
|
1491
999
|
connectId,
|
|
1492
1000
|
params: { connectId }
|
|
1493
1001
|
});
|
|
1494
1002
|
try {
|
|
1495
|
-
|
|
1496
|
-
|
|
1003
|
+
const sessionId = this._sessions.get(connectId);
|
|
1004
|
+
if (sessionId) {
|
|
1005
|
+
await this.connector.disconnect(sessionId);
|
|
1006
|
+
this._sessions.delete(connectId);
|
|
1007
|
+
}
|
|
1008
|
+
debugLog("[LedgerAdapter][RES]", { method: "disconnectDevice", success: true });
|
|
1497
1009
|
} catch (err) {
|
|
1498
1010
|
const e = err;
|
|
1499
1011
|
debugLog("[LedgerAdapter][RES]", {
|
|
1500
|
-
method: "
|
|
1012
|
+
method: "disconnectDevice",
|
|
1501
1013
|
success: false,
|
|
1502
1014
|
error: { message: e?.message, _tag: e?._tag, code: e?.code ?? e?.errorCode }
|
|
1503
1015
|
});
|
|
1504
1016
|
throw err;
|
|
1505
1017
|
}
|
|
1506
1018
|
}
|
|
1507
|
-
async
|
|
1508
|
-
const sessionIds = /* @__PURE__ */ new Set();
|
|
1509
|
-
for (const [connectId, sessionId] of this._sessions) {
|
|
1510
|
-
if (connectId === operation.connectId || operation.connectionKeys.includes(connectId) || operation.connectionKeys.includes(sessionId)) {
|
|
1511
|
-
this._sessions.delete(connectId);
|
|
1512
|
-
sessionIds.add(sessionId);
|
|
1513
|
-
}
|
|
1514
|
-
}
|
|
1515
|
-
for (const sessionId of sessionIds) {
|
|
1516
|
-
await this._runConnectorTeardown(
|
|
1517
|
-
() => this.connector.disconnect(sessionId).catch(() => void 0)
|
|
1518
|
-
);
|
|
1519
|
-
}
|
|
1520
|
-
}
|
|
1521
|
-
async _releaseLostOperationConnection(operationId) {
|
|
1522
|
-
const endedOperation = this._operations.end(operationId, "disconnect");
|
|
1523
|
-
if (!endedOperation) return;
|
|
1524
|
-
this._discoveredDevices.delete(endedOperation.connectId);
|
|
1525
|
-
await this._releaseOperationConnection(endedOperation);
|
|
1526
|
-
}
|
|
1527
|
-
async getDeviceInfo(connectIdOrOperationId, deviceId) {
|
|
1528
|
-
let connectId;
|
|
1529
|
-
try {
|
|
1530
|
-
connectId = (0, import_hwk_adapter_core3.isHardwareOperationId)(connectIdOrOperationId) ? this._operations.resolve(connectIdOrOperationId).connectId : connectIdOrOperationId;
|
|
1531
|
-
} catch (error) {
|
|
1532
|
-
return this.errorToFailure(error);
|
|
1533
|
-
}
|
|
1019
|
+
async getDeviceInfo(connectId, deviceId) {
|
|
1534
1020
|
debugLog("[LedgerAdapter][REQ]", {
|
|
1535
1021
|
method: "getDeviceInfo",
|
|
1536
1022
|
connectId,
|
|
@@ -1568,6 +1054,9 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
1568
1054
|
throw err;
|
|
1569
1055
|
}
|
|
1570
1056
|
}
|
|
1057
|
+
getSupportedChains() {
|
|
1058
|
+
return ["evm", "btc", "sol", "tron"];
|
|
1059
|
+
}
|
|
1571
1060
|
// ---------------------------------------------------------------------------
|
|
1572
1061
|
// Chain call helper
|
|
1573
1062
|
// ---------------------------------------------------------------------------
|
|
@@ -1602,21 +1091,13 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
1602
1091
|
if (params && typeof params === "object") {
|
|
1603
1092
|
const {
|
|
1604
1093
|
autoInstallApp,
|
|
1605
|
-
operationId,
|
|
1606
1094
|
passphraseState: _passphraseState,
|
|
1607
1095
|
useEmptyPassphrase: _useEmptyPassphrase,
|
|
1608
|
-
knownConnections,
|
|
1609
|
-
extra,
|
|
1610
|
-
allowDeviceSelection,
|
|
1611
1096
|
...rest
|
|
1612
1097
|
} = params;
|
|
1613
1098
|
return {
|
|
1614
1099
|
commonParams: {
|
|
1615
|
-
autoInstallApp: typeof autoInstallApp === "boolean" ? autoInstallApp : void 0
|
|
1616
|
-
operationId: typeof operationId === "string" ? operationId : void 0,
|
|
1617
|
-
knownConnections,
|
|
1618
|
-
extra,
|
|
1619
|
-
allowDeviceSelection: typeof allowDeviceSelection === "boolean" ? allowDeviceSelection : void 0
|
|
1100
|
+
autoInstallApp: typeof autoInstallApp === "boolean" ? autoInstallApp : void 0
|
|
1620
1101
|
},
|
|
1621
1102
|
rest
|
|
1622
1103
|
};
|
|
@@ -1792,27 +1273,6 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
1792
1273
|
);
|
|
1793
1274
|
}
|
|
1794
1275
|
// ---------------------------------------------------------------------------
|
|
1795
|
-
// Zcash chain methods (viewing key + shielded address reads; Zcash app >= 3.8.0)
|
|
1796
|
-
// ---------------------------------------------------------------------------
|
|
1797
|
-
zcashGetFullViewingKey(connectId, deviceId, params) {
|
|
1798
|
-
return this.callChainWithMergedParams(
|
|
1799
|
-
connectId,
|
|
1800
|
-
deviceId,
|
|
1801
|
-
"zcash",
|
|
1802
|
-
"zcashGetFullViewingKey",
|
|
1803
|
-
params
|
|
1804
|
-
);
|
|
1805
|
-
}
|
|
1806
|
-
zcashGetShieldedAddress(connectId, deviceId, params) {
|
|
1807
|
-
return this.callChainWithMergedParams(
|
|
1808
|
-
connectId,
|
|
1809
|
-
deviceId,
|
|
1810
|
-
"zcash",
|
|
1811
|
-
"zcashGetShieldedAddress",
|
|
1812
|
-
params
|
|
1813
|
-
);
|
|
1814
|
-
}
|
|
1815
|
-
// ---------------------------------------------------------------------------
|
|
1816
1276
|
// App management — OS-level Ledger app install / list. Bypasses fingerprint
|
|
1817
1277
|
// and chain-handler dispatch; installApp progress is forwarded to the adapter
|
|
1818
1278
|
// emitter via 'ui-event' AppInstallProgress events.
|
|
@@ -1901,7 +1361,7 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
1901
1361
|
);
|
|
1902
1362
|
}
|
|
1903
1363
|
await this.connector.configure({ ledgerGenuineCheckWebSocketUrl: relayUrl });
|
|
1904
|
-
|
|
1364
|
+
this.resetState();
|
|
1905
1365
|
}
|
|
1906
1366
|
const result = await this.connectorCall(connectId, "getDeviceGenuineCheck", {});
|
|
1907
1367
|
if (!result.isGenuine) {
|
|
@@ -1927,10 +1387,10 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
1927
1387
|
if (relayUrl) {
|
|
1928
1388
|
try {
|
|
1929
1389
|
await this.connector.configure?.({ ledgerGenuineCheckWebSocketUrl: void 0 });
|
|
1930
|
-
|
|
1390
|
+
this.resetState();
|
|
1931
1391
|
} catch {
|
|
1932
1392
|
this.connector.reset();
|
|
1933
|
-
|
|
1393
|
+
this.resetState();
|
|
1934
1394
|
}
|
|
1935
1395
|
}
|
|
1936
1396
|
}
|
|
@@ -1942,20 +1402,6 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
1942
1402
|
this.emitter.off(event, listener);
|
|
1943
1403
|
}
|
|
1944
1404
|
cancel(connectId) {
|
|
1945
|
-
const namedOperationIsLive = (id) => {
|
|
1946
|
-
try {
|
|
1947
|
-
this._operations.resolve(id);
|
|
1948
|
-
return true;
|
|
1949
|
-
} catch {
|
|
1950
|
-
return false;
|
|
1951
|
-
}
|
|
1952
|
-
};
|
|
1953
|
-
if ((0, import_hwk_adapter_core3.isHardwareOperationId)(connectId) && !namedOperationIsLive(connectId)) {
|
|
1954
|
-
debugLog("[LedgerAdapter] cancel target already ended; nothing to cancel", {
|
|
1955
|
-
connectId
|
|
1956
|
-
});
|
|
1957
|
-
return;
|
|
1958
|
-
}
|
|
1959
1405
|
const userAbortReason = Object.assign(new Error("User aborted operation"), {
|
|
1960
1406
|
code: import_hwk_adapter_core3.HardwareErrorCode.UserAborted,
|
|
1961
1407
|
_tag: ERROR_TAG.UserAborted
|
|
@@ -1966,50 +1412,14 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
1966
1412
|
this._lastCancelReason = void 0;
|
|
1967
1413
|
}
|
|
1968
1414
|
}, 2e3);
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
operationId = connectId;
|
|
1973
|
-
} else if (!connectId && (0, import_hwk_adapter_core3.isHardwareOperationId)(activeJobId)) {
|
|
1974
|
-
operationId = activeJobId;
|
|
1975
|
-
}
|
|
1976
|
-
let resolvedConnectId = connectId;
|
|
1977
|
-
if (operationId) {
|
|
1978
|
-
try {
|
|
1979
|
-
resolvedConnectId = this._operations.resolve(operationId).connectId;
|
|
1980
|
-
} catch {
|
|
1981
|
-
resolvedConnectId = void 0;
|
|
1982
|
-
}
|
|
1983
|
-
}
|
|
1984
|
-
const interactionForPhysicalId = !operationId && connectId ? this._operations.findActiveByConnectionKey(connectId) : void 0;
|
|
1985
|
-
const pendingOperationId = operationId ?? interactionForPhysicalId?.operationId;
|
|
1986
|
-
if (!connectId) {
|
|
1987
|
-
this._uiRegistry.cancel();
|
|
1988
|
-
} else if (pendingOperationId) {
|
|
1989
|
-
this._uiRegistry.cancel(void 0, void 0, pendingOperationId);
|
|
1990
|
-
}
|
|
1991
|
-
this._finishBleBinding("cancelled");
|
|
1992
|
-
if (!connectId) this._pendingOperationBindings.clear();
|
|
1993
|
-
else if (pendingOperationId) {
|
|
1994
|
-
this._pendingOperationBindings.delete(pendingOperationId);
|
|
1995
|
-
}
|
|
1996
|
-
const queueKeys = /* @__PURE__ */ new Set();
|
|
1997
|
-
if (connectId) queueKeys.add(ledgerQueueKey({ connectId }));
|
|
1998
|
-
if (pendingOperationId) queueKeys.add(ledgerQueueKey({ operationId: pendingOperationId }));
|
|
1999
|
-
if (queueKeys.size) {
|
|
2000
|
-
let cancelledAnyJob = false;
|
|
2001
|
-
for (const key of queueKeys) {
|
|
2002
|
-
cancelledAnyJob = this._jobQueue.cancelActiveAndPending(key, userAbortReason) || cancelledAnyJob;
|
|
2003
|
-
}
|
|
2004
|
-
debugLog("[LedgerAdapter] cancel routed to queue keys", {
|
|
2005
|
-
queueKeys: [...queueKeys],
|
|
2006
|
-
cancelledAnyJob
|
|
2007
|
-
});
|
|
1415
|
+
this._uiRegistry.cancel();
|
|
1416
|
+
if (connectId) {
|
|
1417
|
+
this._jobQueue.cancelActiveAndPending(connectId, userAbortReason);
|
|
2008
1418
|
} else {
|
|
2009
1419
|
this._jobQueue.cancelActiveAndPending(void 0, userAbortReason);
|
|
2010
1420
|
}
|
|
2011
|
-
if (
|
|
2012
|
-
const sessionId = this._sessions.get(
|
|
1421
|
+
if (connectId) {
|
|
1422
|
+
const sessionId = this._sessions.get(connectId) ?? connectId;
|
|
2013
1423
|
void this.connector.cancel(sessionId);
|
|
2014
1424
|
} else {
|
|
2015
1425
|
for (const sid of this._sessions.values()) void this.connector.cancel(sid);
|
|
@@ -2021,99 +1431,41 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
2021
1431
|
// ---------------------------------------------------------------------------
|
|
2022
1432
|
// Chain fingerprint
|
|
2023
1433
|
// ---------------------------------------------------------------------------
|
|
2024
|
-
/** A non-empty deviceId is an expected chain fingerprint, not a transport identifier. */
|
|
2025
1434
|
async getChainFingerprint(connectId, deviceId, chain) {
|
|
2026
1435
|
try {
|
|
2027
1436
|
const fingerprint = await this._computeChainFingerprint(
|
|
2028
1437
|
chain,
|
|
2029
1438
|
(method, params) => this.connectorCall(connectId, method, params, void 0, deviceId)
|
|
2030
1439
|
);
|
|
2031
|
-
if (deviceId) {
|
|
2032
|
-
if (fingerprint !== deviceId) {
|
|
2033
|
-
return (0, import_hwk_adapter_core3.failure)(
|
|
2034
|
-
import_hwk_adapter_core3.HardwareErrorCode.DeviceMismatch,
|
|
2035
|
-
formatDeviceMismatchError(deviceId, fingerprint)
|
|
2036
|
-
);
|
|
2037
|
-
}
|
|
2038
|
-
if ((0, import_hwk_adapter_core3.isHardwareOperationId)(connectId)) {
|
|
2039
|
-
const operation = this._operations.resolve(connectId);
|
|
2040
|
-
await this._publishVerifiedBleBinding(
|
|
2041
|
-
operation.connectId,
|
|
2042
|
-
chain,
|
|
2043
|
-
fingerprint,
|
|
2044
|
-
void 0,
|
|
2045
|
-
connectId
|
|
2046
|
-
);
|
|
2047
|
-
}
|
|
2048
|
-
}
|
|
2049
1440
|
return (0, import_hwk_adapter_core3.success)(fingerprint);
|
|
2050
1441
|
} catch (err) {
|
|
2051
1442
|
debugError("[LedgerAdapter] getChainFingerprint error:", chain, err);
|
|
2052
1443
|
return this.errorToFailure(err);
|
|
2053
1444
|
}
|
|
2054
1445
|
}
|
|
2055
|
-
/**
|
|
2056
|
-
|
|
2057
|
-
|
|
2058
|
-
|
|
2059
|
-
|
|
2060
|
-
}
|
|
1446
|
+
/**
|
|
1447
|
+
* Verify fingerprint using an existing sessionId directly.
|
|
1448
|
+
* Safe to call inside connectorCall without causing queue deadlock.
|
|
1449
|
+
*/
|
|
1450
|
+
async _verifyDeviceFingerprintWithSession(sessionId, deviceId, chain) {
|
|
1451
|
+
if (!deviceId) return { success: true };
|
|
2061
1452
|
try {
|
|
2062
|
-
const
|
|
2063
|
-
|
|
2064
|
-
this.
|
|
2065
|
-
{
|
|
2066
|
-
selectionRequestId: binding.selectedConnection.requestId,
|
|
2067
|
-
connection: { transport: "ble", connectId },
|
|
2068
|
-
identity: { vendor: "ledger", type: "chainFingerprint", chain, value: fingerprint },
|
|
2069
|
-
extra: binding.extra,
|
|
2070
|
-
operationId
|
|
2071
|
-
},
|
|
2072
|
-
signal
|
|
1453
|
+
const fingerprint = await this._computeChainFingerprint(
|
|
1454
|
+
chain,
|
|
1455
|
+
(method, params) => this._callConnector(sessionId, method, params)
|
|
2073
1456
|
);
|
|
2074
|
-
if (
|
|
2075
|
-
|
|
2076
|
-
debugLog("[LedgerAdapter] BLE binding not persisted by host", {
|
|
2077
|
-
connectId,
|
|
2078
|
-
reason: outcome.reason
|
|
2079
|
-
});
|
|
2080
|
-
}
|
|
2081
|
-
return outcome.saved;
|
|
2082
|
-
} catch (error) {
|
|
2083
|
-
if (operationId) {
|
|
2084
|
-
this._pendingOperationBindings.delete(operationId);
|
|
2085
|
-
this._operations.end(operationId, "explicit");
|
|
2086
|
-
}
|
|
2087
|
-
const sessionId = this._sessions.get(connectId);
|
|
2088
|
-
this._sessions.delete(connectId);
|
|
2089
|
-
if (sessionId) {
|
|
2090
|
-
const teardown = this._runConnectorTeardown(
|
|
2091
|
-
() => this.connector.disconnect(sessionId)
|
|
2092
|
-
).catch(() => void 0);
|
|
2093
|
-
if (!signal?.aborted) await teardown;
|
|
2094
|
-
}
|
|
2095
|
-
throw error;
|
|
2096
|
-
} finally {
|
|
2097
|
-
if (this._bindingSelectionRequestId === binding.selectedConnection.requestId) {
|
|
2098
|
-
this._bindingSelectionRequestId = void 0;
|
|
1457
|
+
if (fingerprint === deviceId) {
|
|
1458
|
+
return { success: true };
|
|
2099
1459
|
}
|
|
2100
|
-
|
|
2101
|
-
|
|
1460
|
+
return { success: false, expected: deviceId, actual: fingerprint };
|
|
1461
|
+
} catch (err) {
|
|
1462
|
+
const mapped = mapLedgerError(err);
|
|
1463
|
+
if (mapped.code === import_hwk_adapter_core3.HardwareErrorCode.WrongApp || mapped.code === import_hwk_adapter_core3.HardwareErrorCode.DeviceLocked) {
|
|
1464
|
+
return { success: true };
|
|
2102
1465
|
}
|
|
1466
|
+
throw err;
|
|
2103
1467
|
}
|
|
2104
1468
|
}
|
|
2105
|
-
/** Verify on the acquired session without re-entering the job queue. */
|
|
2106
|
-
async _verifyDeviceFingerprintWithSession(sessionId, deviceId, chain) {
|
|
2107
|
-
if (!deviceId) return { success: true };
|
|
2108
|
-
const fingerprint = await this._computeChainFingerprint(
|
|
2109
|
-
chain,
|
|
2110
|
-
(method, params) => this._callConnector(sessionId, method, params)
|
|
2111
|
-
);
|
|
2112
|
-
if (fingerprint === deviceId) {
|
|
2113
|
-
return { success: true };
|
|
2114
|
-
}
|
|
2115
|
-
return { success: false, expected: deviceId, actual: fingerprint };
|
|
2116
|
-
}
|
|
2117
1469
|
/**
|
|
2118
1470
|
* Compute the chain fingerprint via a caller-supplied call strategy.
|
|
2119
1471
|
*
|
|
@@ -2141,27 +1493,12 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
2141
1493
|
address = (await callMethod("solGetAddress", { path, showOnDevice: false })).address;
|
|
2142
1494
|
} else if (chain === "tron") {
|
|
2143
1495
|
address = (await callMethod("tronGetAddress", { path, showOnDevice: false })).address;
|
|
2144
|
-
} else if (chain === "zcash") {
|
|
2145
|
-
address = (await callMethod("zcashGetShieldedAddress", { path, showOnDevice: false })).address;
|
|
2146
1496
|
} else {
|
|
2147
1497
|
throw new Error(`Unsupported chain for fingerprint: ${chain}`);
|
|
2148
1498
|
}
|
|
2149
1499
|
return (0, import_hwk_adapter_core3.deriveDeviceFingerprint)(address);
|
|
2150
1500
|
}
|
|
2151
1501
|
// Ledger WebUSB won't expose a locked device, so we can't auto-detect unlock.
|
|
2152
|
-
/**
|
|
2153
|
-
* The operation the running device job belongs to, so a UI request raised
|
|
2154
|
-
* mid-call can name it. A call pinned to an operation queues under its id
|
|
2155
|
-
* (`ledgerQueueKey`); a call without one queues under the connectId, and the
|
|
2156
|
-
* live operation on that connection is still the owner. Only work with no
|
|
2157
|
-
* operation at all (cold start) comes back undefined.
|
|
2158
|
-
*/
|
|
2159
|
-
_activeOperationId() {
|
|
2160
|
-
const activeJobId = this._jobQueue.getActiveJob()?.deviceId;
|
|
2161
|
-
if (!activeJobId) return void 0;
|
|
2162
|
-
if ((0, import_hwk_adapter_core3.isHardwareOperationId)(activeJobId)) return activeJobId;
|
|
2163
|
-
return this._operations.findActiveByConnectionKey(activeJobId)?.operationId;
|
|
2164
|
-
}
|
|
2165
1502
|
// The user must press Confirm after unlocking, which triggers a search retry.
|
|
2166
1503
|
// If `signal` is provided, an abort cancels the pending UI request so the
|
|
2167
1504
|
// registry slot is released and a stale RECEIVE_DEVICE_CONNECT won't land in
|
|
@@ -2170,18 +1507,15 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
2170
1507
|
if (signal?.aborted) {
|
|
2171
1508
|
_LedgerAdapter._throwIfAborted(signal);
|
|
2172
1509
|
}
|
|
2173
|
-
const operationId = this._activeOperationId();
|
|
2174
1510
|
const waitPromise = this._uiRegistry.wait(
|
|
2175
|
-
import_hwk_adapter_core3.UI_REQUEST.REQUEST_DEVICE_CONNECT
|
|
2176
|
-
{ operationId }
|
|
1511
|
+
import_hwk_adapter_core3.UI_REQUEST.REQUEST_DEVICE_CONNECT
|
|
2177
1512
|
);
|
|
2178
1513
|
this.emitter.emit(import_hwk_adapter_core3.UI_REQUEST.REQUEST_DEVICE_CONNECT, {
|
|
2179
1514
|
type: import_hwk_adapter_core3.UI_REQUEST.REQUEST_DEVICE_CONNECT,
|
|
2180
1515
|
payload: {
|
|
2181
1516
|
vendor: "ledger",
|
|
2182
1517
|
reason: "device-not-found",
|
|
2183
|
-
message: "Please connect and unlock your Ledger device"
|
|
2184
|
-
operationId
|
|
1518
|
+
message: "Please connect and unlock your Ledger device"
|
|
2185
1519
|
}
|
|
2186
1520
|
});
|
|
2187
1521
|
let payload;
|
|
@@ -2242,18 +1576,15 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
2242
1576
|
return { ...params, showOnDevice: true };
|
|
2243
1577
|
}
|
|
2244
1578
|
async _waitForBtcHighIndexConfirm(path, accountIndex) {
|
|
2245
|
-
const operationId = this._activeOperationId();
|
|
2246
1579
|
const waitPromise = this._uiRegistry.wait(
|
|
2247
|
-
import_hwk_adapter_core3.UI_REQUEST.REQUEST_BTC_HIGH_INDEX_CONFIRM
|
|
2248
|
-
{ operationId }
|
|
1580
|
+
import_hwk_adapter_core3.UI_REQUEST.REQUEST_BTC_HIGH_INDEX_CONFIRM
|
|
2249
1581
|
);
|
|
2250
1582
|
this.emitter.emit(import_hwk_adapter_core3.UI_REQUEST.REQUEST_BTC_HIGH_INDEX_CONFIRM, {
|
|
2251
1583
|
type: import_hwk_adapter_core3.UI_REQUEST.REQUEST_BTC_HIGH_INDEX_CONFIRM,
|
|
2252
1584
|
payload: {
|
|
2253
1585
|
vendor: "ledger",
|
|
2254
1586
|
path,
|
|
2255
|
-
accountIndex
|
|
2256
|
-
operationId
|
|
1587
|
+
accountIndex
|
|
2257
1588
|
}
|
|
2258
1589
|
});
|
|
2259
1590
|
try {
|
|
@@ -2270,14 +1601,12 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
2270
1601
|
// Ask the user whether to install a missing app (autoInstallApp flow).
|
|
2271
1602
|
// Same register-then-emit ordering as the BTC high-index gate.
|
|
2272
1603
|
async _waitForInstallAppConfirm(appName) {
|
|
2273
|
-
const operationId = this._activeOperationId();
|
|
2274
1604
|
const waitPromise = this._uiRegistry.wait(
|
|
2275
|
-
import_hwk_adapter_core3.UI_REQUEST.REQUEST_INSTALL_APP
|
|
2276
|
-
{ operationId }
|
|
1605
|
+
import_hwk_adapter_core3.UI_REQUEST.REQUEST_INSTALL_APP
|
|
2277
1606
|
);
|
|
2278
1607
|
this.emitter.emit(import_hwk_adapter_core3.UI_REQUEST.REQUEST_INSTALL_APP, {
|
|
2279
1608
|
type: import_hwk_adapter_core3.UI_REQUEST.REQUEST_INSTALL_APP,
|
|
2280
|
-
payload: { vendor: "ledger", appName
|
|
1609
|
+
payload: { vendor: "ledger", appName }
|
|
2281
1610
|
});
|
|
2282
1611
|
try {
|
|
2283
1612
|
const payload = await waitPromise;
|
|
@@ -2293,17 +1622,16 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
2293
1622
|
// Layer 1 entry. Caller signal only races the outer awaiter; the shared
|
|
2294
1623
|
// `_doConnect` runs under its own internal controller so caller A's cancel
|
|
2295
1624
|
// doesn't kill caller B's await.
|
|
2296
|
-
async ensureConnected(connectId, signal, allowUsbEphemeralFallback = false
|
|
1625
|
+
async ensureConnected(connectId, signal, allowUsbEphemeralFallback = false) {
|
|
2297
1626
|
if (signal.aborted) _LedgerAdapter._throwIfAborted(signal);
|
|
2298
|
-
this.
|
|
2299
|
-
if (this._isBleConnection() && !connectId && !allowUsbEphemeralFallback) {
|
|
1627
|
+
if (isLedgerBleConnectionType(this.connector.connectionType) && !connectId) {
|
|
2300
1628
|
throw Object.assign(new Error("Ledger BLE connectId is required."), {
|
|
2301
1629
|
code: import_hwk_adapter_core3.HardwareErrorCode.DeviceNotFound
|
|
2302
1630
|
});
|
|
2303
1631
|
}
|
|
2304
1632
|
if (connectId && this._sessions.has(connectId)) return connectId;
|
|
2305
1633
|
if (!connectId && this._sessions.size > 0) {
|
|
2306
|
-
if (!this.
|
|
1634
|
+
if (!isLedgerBleConnectionType(this.connector.connectionType) && this._sessions.size > 1) {
|
|
2307
1635
|
throw Object.assign(
|
|
2308
1636
|
new Error(
|
|
2309
1637
|
"Ledger USB session invariant violated: more than one session is active. Please reconnect the device."
|
|
@@ -2313,24 +1641,15 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
2313
1641
|
}
|
|
2314
1642
|
return this._sessions.keys().next().value;
|
|
2315
1643
|
}
|
|
2316
|
-
if (!this._connectingPromise
|
|
2317
|
-
|
|
2318
|
-
this._doConnectAbortController
|
|
2319
|
-
const innerSignal = controller.signal;
|
|
1644
|
+
if (!this._connectingPromise) {
|
|
1645
|
+
this._doConnectAbortController = new AbortController();
|
|
1646
|
+
const innerSignal = this._doConnectAbortController.signal;
|
|
2320
1647
|
this._connectingPromise = (async () => {
|
|
2321
1648
|
try {
|
|
2322
|
-
return await this._doConnect(
|
|
2323
|
-
innerSignal,
|
|
2324
|
-
connectId,
|
|
2325
|
-
allowUsbEphemeralFallback,
|
|
2326
|
-
preserveOperationId,
|
|
2327
|
-
context
|
|
2328
|
-
);
|
|
1649
|
+
return await this._doConnect(innerSignal, connectId, allowUsbEphemeralFallback);
|
|
2329
1650
|
} finally {
|
|
2330
|
-
|
|
2331
|
-
|
|
2332
|
-
this._doConnectAbortController = null;
|
|
2333
|
-
}
|
|
1651
|
+
this._connectingPromise = null;
|
|
1652
|
+
this._doConnectAbortController = null;
|
|
2334
1653
|
}
|
|
2335
1654
|
})();
|
|
2336
1655
|
}
|
|
@@ -2339,51 +1658,22 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
2339
1658
|
// Layer 1 main loop — the ONLY place in SDK that emits unlock dialog.
|
|
2340
1659
|
// Bounded by MAX_DOCONNECT_CONFIRMS — after N Confirms with no progress,
|
|
2341
1660
|
// throw DeviceNotFound so the user is kicked out of the loop.
|
|
2342
|
-
async _doConnect(internalSignal, targetConnectId, allowUsbEphemeralFallback = false
|
|
2343
|
-
|
|
2344
|
-
|
|
2345
|
-
|
|
2346
|
-
|
|
2347
|
-
|
|
2348
|
-
|
|
2349
|
-
|
|
2350
|
-
|
|
2351
|
-
)
|
|
2352
|
-
|
|
2353
|
-
|
|
2354
|
-
|
|
2355
|
-
|
|
2356
|
-
|
|
2357
|
-
preserveOperationId,
|
|
2358
|
-
context,
|
|
2359
|
-
internalSignal
|
|
2360
|
-
);
|
|
2361
|
-
}
|
|
2362
|
-
this._activeConnectionType = "ble";
|
|
2363
|
-
const knownBle = context?.knownConnections?.find(
|
|
2364
|
-
(connection) => connection.transport === "ble"
|
|
2365
|
-
);
|
|
2366
|
-
if (knownBle?.transport === "ble") {
|
|
2367
|
-
return this._connectDeviceOrThrow(knownBle.connectId, preserveOperationId, internalSignal);
|
|
2368
|
-
}
|
|
2369
|
-
if (targetConnectId && context?.knownConnections === void 0) {
|
|
2370
|
-
throw (0, import_hwk_adapter_core3.createHwkError)({
|
|
2371
|
-
code: import_hwk_adapter_core3.HardwareErrorCode.DeviceNotFound,
|
|
2372
|
-
message: "Ledger connection metadata is required before starting Bluetooth binding"
|
|
2373
|
-
});
|
|
1661
|
+
async _doConnect(internalSignal, targetConnectId, allowUsbEphemeralFallback = false) {
|
|
1662
|
+
if (isLedgerBleConnectionType(this.connector.connectionType) && targetConnectId) {
|
|
1663
|
+
try {
|
|
1664
|
+
return await this._connectDeviceOrThrow(targetConnectId);
|
|
1665
|
+
} catch (err) {
|
|
1666
|
+
if (!isDeviceLockedError(err) && !isDeviceNotAdvertisingError(err) && !isDeviceDisconnectedError(err)) {
|
|
1667
|
+
throw err;
|
|
1668
|
+
}
|
|
1669
|
+
this._discoveredDevices.delete(targetConnectId);
|
|
1670
|
+
if (isDeviceDisconnectedError(err)) {
|
|
1671
|
+
try {
|
|
1672
|
+
this.connector.reset?.();
|
|
1673
|
+
} catch {
|
|
1674
|
+
}
|
|
1675
|
+
}
|
|
2374
1676
|
}
|
|
2375
|
-
const bleDevices = await this._searchDevices({ transportType: "ble" }, internalSignal);
|
|
2376
|
-
return this._connectFirstOrSelect(
|
|
2377
|
-
bleDevices,
|
|
2378
|
-
void 0,
|
|
2379
|
-
allowUsbEphemeralFallback,
|
|
2380
|
-
preserveOperationId,
|
|
2381
|
-
context,
|
|
2382
|
-
internalSignal
|
|
2383
|
-
);
|
|
2384
|
-
}
|
|
2385
|
-
if (this._isBleConnection() && targetConnectId) {
|
|
2386
|
-
return this._connectDeviceOrThrow(targetConnectId, preserveOperationId, internalSignal);
|
|
2387
1677
|
}
|
|
2388
1678
|
let confirms = 0;
|
|
2389
1679
|
while (!internalSignal.aborted) {
|
|
@@ -2391,28 +1681,22 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
2391
1681
|
type: import_hwk_adapter_core3.EConnectorInteraction.Searching,
|
|
2392
1682
|
payload: { sessionId: "" }
|
|
2393
1683
|
});
|
|
2394
|
-
let devices = await this.
|
|
2395
|
-
_LedgerAdapter._throwIfAborted(internalSignal);
|
|
1684
|
+
let devices = await this.searchDevices();
|
|
2396
1685
|
if (devices.length === 0) {
|
|
2397
1686
|
for (let i = 0; i < 3 && !internalSignal.aborted; i += 1) {
|
|
2398
1687
|
await new Promise((resolve) => {
|
|
2399
1688
|
setTimeout(resolve, import_hwk_adapter_core3.DEVICE_CONNECT_RETRY_DELAY_MS);
|
|
2400
1689
|
});
|
|
2401
|
-
|
|
2402
|
-
devices = await this._searchDevices(void 0, internalSignal);
|
|
2403
|
-
_LedgerAdapter._throwIfAborted(internalSignal);
|
|
1690
|
+
devices = await this.searchDevices();
|
|
2404
1691
|
if (devices.length > 0) break;
|
|
2405
1692
|
}
|
|
2406
1693
|
}
|
|
2407
|
-
if (devices.length > 0
|
|
1694
|
+
if (devices.length > 0) {
|
|
2408
1695
|
try {
|
|
2409
1696
|
return await this._connectFirstOrSelect(
|
|
2410
1697
|
devices,
|
|
2411
1698
|
targetConnectId,
|
|
2412
|
-
allowUsbEphemeralFallback
|
|
2413
|
-
preserveOperationId,
|
|
2414
|
-
context,
|
|
2415
|
-
internalSignal
|
|
1699
|
+
allowUsbEphemeralFallback
|
|
2416
1700
|
);
|
|
2417
1701
|
} catch (err) {
|
|
2418
1702
|
if (!isDeviceLockedError(err) && !isDeviceNotAdvertisingError(err) && !isDeviceDisconnectedError(err)) {
|
|
@@ -2441,136 +1725,45 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
2441
1725
|
_LedgerAdapter._throwIfAborted(internalSignal);
|
|
2442
1726
|
throw new Error("_doConnect aborted");
|
|
2443
1727
|
}
|
|
2444
|
-
async _connectFirstOrSelect(devices, targetConnectId, allowUsbEphemeralFallback
|
|
2445
|
-
_LedgerAdapter._throwIfAborted(signal);
|
|
1728
|
+
async _connectFirstOrSelect(devices, targetConnectId, allowUsbEphemeralFallback = false) {
|
|
2446
1729
|
if (targetConnectId) {
|
|
2447
1730
|
const target = devices.find(
|
|
2448
1731
|
(d) => d.connectId === targetConnectId || d.deviceId === targetConnectId
|
|
2449
1732
|
);
|
|
2450
1733
|
if (target) {
|
|
2451
|
-
return this._connectDeviceOrThrow(target.connectId
|
|
1734
|
+
return this._connectDeviceOrThrow(target.connectId);
|
|
2452
1735
|
}
|
|
2453
|
-
if (!this.
|
|
1736
|
+
if (!isLedgerBleConnectionType(this.connector.connectionType) && devices.length === 1 && allowUsbEphemeralFallback) {
|
|
2454
1737
|
debugLog(
|
|
2455
1738
|
`[LedgerAdapter] target ${targetConnectId} not in fresh enumeration; accepting sole USB device ${devices[0].connectId} for fingerprint-verified recovery`
|
|
2456
1739
|
);
|
|
2457
|
-
return this._connectDeviceOrThrow(devices[0].connectId
|
|
1740
|
+
return this._connectDeviceOrThrow(devices[0].connectId);
|
|
2458
1741
|
}
|
|
2459
|
-
|
|
2460
|
-
|
|
2461
|
-
|
|
2462
|
-
|
|
2463
|
-
|
|
2464
|
-
err._tag = ERROR_TAG.DeviceNotAdvertising;
|
|
2465
|
-
}
|
|
2466
|
-
throw err;
|
|
1742
|
+
const err = Object.assign(new Error(`Target Ledger unavailable: ${targetConnectId}`), {
|
|
1743
|
+
code: import_hwk_adapter_core3.HardwareErrorCode.DeviceNotFound
|
|
1744
|
+
});
|
|
1745
|
+
if (isLedgerBleConnectionType(this.connector.connectionType)) {
|
|
1746
|
+
err._tag = ERROR_TAG.DeviceNotAdvertising;
|
|
2467
1747
|
}
|
|
1748
|
+
throw err;
|
|
2468
1749
|
}
|
|
2469
|
-
|
|
2470
|
-
if (requiresBleSelection && !allowUsbEphemeralFallback) {
|
|
1750
|
+
if (isLedgerBleConnectionType(this.connector.connectionType)) {
|
|
2471
1751
|
throw Object.assign(new Error("Ledger BLE connectId is required."), {
|
|
2472
1752
|
code: import_hwk_adapter_core3.HardwareErrorCode.DeviceNotFound
|
|
2473
1753
|
});
|
|
2474
1754
|
}
|
|
2475
|
-
if (
|
|
2476
|
-
|
|
2477
|
-
if (context) context.bindingSessionId = bindingSessionId;
|
|
2478
|
-
const allowUsbFallback = context?.bindingReason !== "manual-rebind" && Boolean(this.connector.availableTransports?.includes("usb"));
|
|
2479
|
-
const knownUsb = context?.knownConnections?.find(
|
|
2480
|
-
(connection) => connection.transport === "usb"
|
|
2481
|
-
);
|
|
2482
|
-
const usbConnectId = knownUsb?.transport === "usb" ? knownUsb.connectId : targetConnectId;
|
|
2483
|
-
const { device, requestId } = await (0, import_hwk_adapter_core3.requestBleDeviceSelection)({
|
|
2484
|
-
emitter: this.emitter,
|
|
2485
|
-
registry: this._uiRegistry,
|
|
2486
|
-
signal,
|
|
2487
|
-
allowUsbFallback,
|
|
2488
|
-
scan: async () => {
|
|
2489
|
-
if (allowUsbFallback) {
|
|
2490
|
-
const usbDevices = await this._searchDevices({ transportType: "usb" }, signal);
|
|
2491
|
-
const candidate = usbDevices.find(
|
|
2492
|
-
(device2) => device2.connectionType === "usb" && device2.connectId === usbConnectId
|
|
2493
|
-
) ?? (usbDevices.length === 1 && usbDevices[0].connectionType === "usb" ? usbDevices[0] : void 0);
|
|
2494
|
-
if (candidate) return [candidate];
|
|
2495
|
-
}
|
|
2496
|
-
return (await this._searchDevices({ transportType: "ble", waitForAllTransports: true }, signal)).filter((device2) => !context?.rejectedConnectIds?.has(device2.connectId));
|
|
2497
|
-
},
|
|
2498
|
-
request: {
|
|
2499
|
-
devices: devices.filter((device2) => !context?.rejectedConnectIds?.has(device2.connectId)),
|
|
2500
|
-
bindingSessionId,
|
|
2501
|
-
rejectedConnectId: context?.rejectedConnectId,
|
|
2502
|
-
context: {
|
|
2503
|
-
kind: "bind-connection",
|
|
2504
|
-
transport: "ble",
|
|
2505
|
-
reason: context?.bindingReason ?? (targetConnectId ? "known-connection-unavailable" : "missing-binding")
|
|
2506
|
-
},
|
|
2507
|
-
extra: context?.extra,
|
|
2508
|
-
operationId: preserveOperationId
|
|
2509
|
-
}
|
|
2510
|
-
});
|
|
2511
|
-
if (device.connectionType === "usb") {
|
|
2512
|
-
this._activeConnectionType = "usb";
|
|
2513
|
-
if (context) context.selectedConnection = void 0;
|
|
2514
|
-
if (preserveOperationId) this._pendingOperationBindings.delete(preserveOperationId);
|
|
2515
|
-
this._bindingSelectionRequestId = void 0;
|
|
2516
|
-
this.emitter.emit(import_hwk_adapter_core3.UI_REQUEST.DEVICE_BINDING_STATUS, {
|
|
2517
|
-
type: import_hwk_adapter_core3.UI_REQUEST.DEVICE_BINDING_STATUS,
|
|
2518
|
-
payload: { selectionRequestId: requestId, status: "cancelled" }
|
|
2519
|
-
});
|
|
2520
|
-
return this._connectDeviceOrThrow(device.connectId, preserveOperationId, signal);
|
|
2521
|
-
}
|
|
2522
|
-
if (context) context.selectedConnection = { connectId: device.connectId, requestId };
|
|
2523
|
-
this._bindingSelectionRequestId = requestId;
|
|
2524
|
-
return this._connectDeviceOrThrow(device.connectId, preserveOperationId, signal);
|
|
2525
|
-
}
|
|
2526
|
-
if (devices.length > 0 && (devices.length > 1 || requiresBleSelection)) {
|
|
2527
|
-
if (context?.allowDeviceSelection === false || !this.emitter.listenerCount(import_hwk_adapter_core3.UI_REQUEST.REQUEST_SELECT_DEVICE)) {
|
|
2528
|
-
throw (0, import_hwk_adapter_core3.createHwkError)({
|
|
2529
|
-
code: import_hwk_adapter_core3.HardwareErrorCode.DeviceNotFound,
|
|
2530
|
-
message: "Select a Ledger device before continuing"
|
|
2531
|
-
});
|
|
2532
|
-
}
|
|
2533
|
-
const requestId = this._uiRegistry.createRequestId();
|
|
2534
|
-
const operationId = this._activeOperationId();
|
|
2535
|
-
const waitPromise = this._uiRegistry.wait(
|
|
2536
|
-
import_hwk_adapter_core3.UI_REQUEST.REQUEST_SELECT_DEVICE,
|
|
2537
|
-
{ requestId, operationId }
|
|
2538
|
-
);
|
|
2539
|
-
this.emitter.emit(import_hwk_adapter_core3.UI_REQUEST.REQUEST_SELECT_DEVICE, {
|
|
2540
|
-
type: import_hwk_adapter_core3.UI_REQUEST.REQUEST_SELECT_DEVICE,
|
|
2541
|
-
payload: {
|
|
2542
|
-
devices,
|
|
2543
|
-
requestId,
|
|
2544
|
-
operationId,
|
|
2545
|
-
context: requiresBleSelection ? {
|
|
2546
|
-
kind: "bind-connection",
|
|
2547
|
-
transport: "ble",
|
|
2548
|
-
reason: targetConnectId ? "known-connection-unavailable" : "missing-binding"
|
|
2549
|
-
} : { kind: "select-device", transport: "usb", reason: "multiple-candidates" },
|
|
2550
|
-
extra: context?.extra
|
|
2551
|
-
}
|
|
2552
|
-
});
|
|
2553
|
-
const { sdkConnectId } = await (signal ? this._abortable(signal, waitPromise) : waitPromise);
|
|
2554
|
-
if (signal) _LedgerAdapter._throwIfAborted(signal);
|
|
2555
|
-
const selected = devices.find((device) => device.connectId === sdkConnectId);
|
|
2556
|
-
if (!selected) {
|
|
2557
|
-
throw Object.assign(new Error("Selected Ledger is no longer available"), {
|
|
2558
|
-
code: import_hwk_adapter_core3.HardwareErrorCode.DeviceNotFound
|
|
2559
|
-
});
|
|
2560
|
-
}
|
|
2561
|
-
if (context && requiresBleSelection)
|
|
2562
|
-
context.selectedConnection = { connectId: selected.connectId, requestId };
|
|
2563
|
-
return this._connectDeviceOrThrow(selected.connectId, preserveOperationId, signal);
|
|
1755
|
+
if (devices.length > 1) {
|
|
1756
|
+
throw createMultipleUsbLedgerDevicesError();
|
|
2564
1757
|
}
|
|
2565
1758
|
if (devices.length !== 1) {
|
|
2566
1759
|
throw Object.assign(new Error("Ledger device not found."), {
|
|
2567
1760
|
code: import_hwk_adapter_core3.HardwareErrorCode.DeviceNotFound
|
|
2568
1761
|
});
|
|
2569
1762
|
}
|
|
2570
|
-
return this._connectDeviceOrThrow(devices[0].connectId
|
|
1763
|
+
return this._connectDeviceOrThrow(devices[0].connectId);
|
|
2571
1764
|
}
|
|
2572
|
-
async _connectDeviceOrThrow(chosenConnectId
|
|
2573
|
-
const result = await this.
|
|
1765
|
+
async _connectDeviceOrThrow(chosenConnectId) {
|
|
1766
|
+
const result = await this.connectDevice(chosenConnectId);
|
|
2574
1767
|
if (!result.success) {
|
|
2575
1768
|
const payload = result.payload;
|
|
2576
1769
|
const rethrow = Object.assign(new Error(payload.error), {
|
|
@@ -2581,7 +1774,7 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
2581
1774
|
}
|
|
2582
1775
|
throw rethrow;
|
|
2583
1776
|
}
|
|
2584
|
-
return
|
|
1777
|
+
return chosenConnectId;
|
|
2585
1778
|
}
|
|
2586
1779
|
/**
|
|
2587
1780
|
* Call the connector with automatic session resolution and disconnect retry.
|
|
@@ -2611,122 +1804,26 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
2611
1804
|
* lives in one place.
|
|
2612
1805
|
*/
|
|
2613
1806
|
async _callConnector(sessionId, method, params, signal) {
|
|
2614
|
-
this.
|
|
2615
|
-
if (signal?.aborted) throw this._abortReason(signal);
|
|
2616
|
-
const releaseOperation = this._retainConnectorOperation(`call:${sessionId}`);
|
|
2617
|
-
let promise;
|
|
2618
|
-
try {
|
|
2619
|
-
promise = this.connector.call(sessionId, method, params).finally(releaseOperation);
|
|
2620
|
-
} catch (error) {
|
|
2621
|
-
releaseOperation();
|
|
2622
|
-
throw error;
|
|
2623
|
-
}
|
|
1807
|
+
const promise = this.connector.call(sessionId, method, params);
|
|
2624
1808
|
const result = signal ? await this._abortable(signal, promise) : await promise;
|
|
2625
1809
|
return this._unwrapConnectorResult(result);
|
|
2626
1810
|
}
|
|
2627
|
-
_assertConnectorReady(method) {
|
|
2628
|
-
if (this._resetPromise || this._pendingConnectorTeardowns > 0 || this._unsettledConnectorOperations.size > 0) {
|
|
2629
|
-
throw _LedgerAdapter._createDeviceBusyError(method);
|
|
2630
|
-
}
|
|
2631
|
-
}
|
|
2632
|
-
_runConnectorTeardown(task) {
|
|
2633
|
-
const previous = this._connectorTeardownTail;
|
|
2634
|
-
let releaseTail = () => void 0;
|
|
2635
|
-
this._connectorTeardownTail = new Promise((resolve) => {
|
|
2636
|
-
releaseTail = resolve;
|
|
2637
|
-
});
|
|
2638
|
-
this._pendingConnectorTeardowns += 1;
|
|
2639
|
-
return (async () => {
|
|
2640
|
-
try {
|
|
2641
|
-
await previous;
|
|
2642
|
-
await this._waitForConnectorOperationsToDrain();
|
|
2643
|
-
await task();
|
|
2644
|
-
} finally {
|
|
2645
|
-
this._pendingConnectorTeardowns -= 1;
|
|
2646
|
-
releaseTail();
|
|
2647
|
-
}
|
|
2648
|
-
})();
|
|
2649
|
-
}
|
|
2650
|
-
_waitForConnectorOperationsToDrain() {
|
|
2651
|
-
if (this._unsettledConnectorOperations.size === 0) {
|
|
2652
|
-
return Promise.resolve();
|
|
2653
|
-
}
|
|
2654
|
-
return new Promise((resolve) => {
|
|
2655
|
-
this._connectorIdleWaiters.add(resolve);
|
|
2656
|
-
});
|
|
2657
|
-
}
|
|
2658
|
-
_retainConnectorOperation(key) {
|
|
2659
|
-
this._unsettledConnectorOperations.set(
|
|
2660
|
-
key,
|
|
2661
|
-
(this._unsettledConnectorOperations.get(key) ?? 0) + 1
|
|
2662
|
-
);
|
|
2663
|
-
let released = false;
|
|
2664
|
-
return () => {
|
|
2665
|
-
if (released) return;
|
|
2666
|
-
released = true;
|
|
2667
|
-
const remaining = (this._unsettledConnectorOperations.get(key) ?? 1) - 1;
|
|
2668
|
-
if (remaining > 0) {
|
|
2669
|
-
this._unsettledConnectorOperations.set(key, remaining);
|
|
2670
|
-
} else {
|
|
2671
|
-
this._unsettledConnectorOperations.delete(key);
|
|
2672
|
-
}
|
|
2673
|
-
if (this._unsettledConnectorOperations.size === 0) {
|
|
2674
|
-
for (const resolve of this._connectorIdleWaiters) resolve();
|
|
2675
|
-
this._connectorIdleWaiters.clear();
|
|
2676
|
-
}
|
|
2677
|
-
};
|
|
2678
|
-
}
|
|
2679
1811
|
async connectorCall(connectId, method, params, fingerprint, permissionDeviceId, commonParams, installContext) {
|
|
2680
|
-
|
|
2681
|
-
|
|
2682
|
-
throw (0, import_hwk_adapter_core3.createHwkError)({
|
|
2683
|
-
code: import_hwk_adapter_core3.HardwareErrorCode.InvalidParams,
|
|
2684
|
-
message: "Conflicting Ledger operation ids",
|
|
2685
|
-
params: {
|
|
2686
|
-
positionalOperationId,
|
|
2687
|
-
commonOperationId: commonParams.operationId
|
|
2688
|
-
}
|
|
2689
|
-
});
|
|
2690
|
-
}
|
|
2691
|
-
const operationId = commonParams?.operationId ?? positionalOperationId;
|
|
2692
|
-
const operation = operationId ? this._operations.resolve(operationId) : void 0;
|
|
2693
|
-
const releaseOperationRetention = operationId ? this._operations.retain(operationId) : void 0;
|
|
2694
|
-
const effectiveConnectId = operation?.connectId ?? connectId;
|
|
2695
|
-
debugLog("[LedgerAdapter][REQ]", {
|
|
2696
|
-
method,
|
|
2697
|
-
connectId: effectiveConnectId || "(empty)",
|
|
2698
|
-
params
|
|
2699
|
-
});
|
|
2700
|
-
const queueKey = ledgerQueueKey({ operationId, connectId: effectiveConnectId });
|
|
1812
|
+
debugLog("[LedgerAdapter][REQ]", { method, connectId: connectId || "(empty)", params });
|
|
1813
|
+
const queueKey = connectId || "__ledger_default__";
|
|
2701
1814
|
try {
|
|
2702
1815
|
const result = await this._jobQueue.enqueue(
|
|
2703
1816
|
queueKey,
|
|
2704
|
-
async (signal) =>
|
|
2705
|
-
|
|
2706
|
-
|
|
2707
|
-
|
|
2708
|
-
|
|
2709
|
-
|
|
2710
|
-
|
|
2711
|
-
|
|
2712
|
-
|
|
2713
|
-
|
|
2714
|
-
commonParams,
|
|
2715
|
-
installContext ?? {},
|
|
2716
|
-
operationId
|
|
2717
|
-
);
|
|
2718
|
-
} catch (error) {
|
|
2719
|
-
this._finishBleBinding(signal.aborted ? "cancelled" : "failed");
|
|
2720
|
-
throw error;
|
|
2721
|
-
} finally {
|
|
2722
|
-
if (operationId) {
|
|
2723
|
-
this._activeOperationJobs.delete(operationId);
|
|
2724
|
-
if (this._pendingOperationDisconnects.delete(operationId)) {
|
|
2725
|
-
await this._releaseLostOperationConnection(operationId);
|
|
2726
|
-
}
|
|
2727
|
-
}
|
|
2728
|
-
}
|
|
2729
|
-
},
|
|
1817
|
+
async (signal) => this._runConnectorCall(
|
|
1818
|
+
connectId,
|
|
1819
|
+
method,
|
|
1820
|
+
params,
|
|
1821
|
+
signal,
|
|
1822
|
+
fingerprint,
|
|
1823
|
+
permissionDeviceId,
|
|
1824
|
+
commonParams,
|
|
1825
|
+
installContext
|
|
1826
|
+
),
|
|
2730
1827
|
{
|
|
2731
1828
|
label: method,
|
|
2732
1829
|
rejectIfBusy: true,
|
|
@@ -2747,22 +1844,15 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
2747
1844
|
}
|
|
2748
1845
|
});
|
|
2749
1846
|
throw err;
|
|
2750
|
-
} finally {
|
|
2751
|
-
releaseOperationRetention?.();
|
|
2752
1847
|
}
|
|
2753
1848
|
}
|
|
2754
1849
|
/**
|
|
2755
1850
|
* Race a promise against an abort signal. On abort, rejects with
|
|
2756
1851
|
* signal.reason → instance _lastCancelReason → generic Error('Aborted').
|
|
2757
1852
|
*/
|
|
2758
|
-
/** Hermes/RN polyfills don't always populate signal.reason; fall back. */
|
|
2759
|
-
_abortReason(signal) {
|
|
2760
|
-
return signal.reason ?? this._lastCancelReason ?? new Error("Aborted");
|
|
2761
|
-
}
|
|
2762
1853
|
_abortable(signal, promise) {
|
|
2763
|
-
const getAbortReason = () => this.
|
|
1854
|
+
const getAbortReason = () => signal.reason ?? this._lastCancelReason ?? new Error("Aborted");
|
|
2764
1855
|
if (signal.aborted) {
|
|
2765
|
-
void promise.catch(() => void 0);
|
|
2766
1856
|
return Promise.reject(getAbortReason());
|
|
2767
1857
|
}
|
|
2768
1858
|
return new Promise((resolve, reject) => {
|
|
@@ -2789,15 +1879,13 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
2789
1879
|
}
|
|
2790
1880
|
}
|
|
2791
1881
|
/** Actual work done under the job queue — connection, fingerprint, call, and recovery. */
|
|
2792
|
-
async _runConnectorCall(connectId, method, params, signal, fingerprint, permissionDeviceId, commonParams, installContext
|
|
1882
|
+
async _runConnectorCall(connectId, method, params, signal, fingerprint, permissionDeviceId, commonParams, installContext) {
|
|
2793
1883
|
_LedgerAdapter._throwIfAborted(signal);
|
|
2794
|
-
|
|
2795
|
-
|
|
2796
|
-
|
|
2797
|
-
|
|
2798
|
-
|
|
2799
|
-
);
|
|
2800
|
-
}
|
|
1884
|
+
await this._ensureDevicePermission(
|
|
1885
|
+
connectId,
|
|
1886
|
+
permissionDeviceId ?? fingerprint?.deviceId,
|
|
1887
|
+
signal
|
|
1888
|
+
);
|
|
2801
1889
|
_LedgerAdapter._throwIfAborted(signal);
|
|
2802
1890
|
let effectiveParams = params;
|
|
2803
1891
|
if (method === "btcGetPublicKey") {
|
|
@@ -2811,135 +1899,36 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
2811
1899
|
effectiveParams = gatedParams;
|
|
2812
1900
|
}
|
|
2813
1901
|
const allowUsbEphemeralFallback = !!fingerprint?.deviceId && !fingerprint.skipFingerprint;
|
|
2814
|
-
|
|
2815
|
-
|
|
2816
|
-
const knownTransport = this._isBleConnection() ? "ble" : "usb";
|
|
2817
|
-
const hintedConnectId = commonParams?.knownConnections?.find(
|
|
2818
|
-
(connection) => connection.transport === knownTransport
|
|
2819
|
-
);
|
|
2820
|
-
const inputConnectId = hintedConnectId && hintedConnectId.transport !== "qr" ? hintedConnectId.connectId : connectId;
|
|
2821
|
-
const preferredConnectId = fingerprint && !fingerprint.skipFingerprint && verifiedBleTarget?.chain === fingerprint.chain && verifiedBleTarget.fingerprint === fingerprint.deviceId ? verifiedBleTarget.connectId : inputConnectId;
|
|
2822
|
-
const connectionAttempt = { ...commonParams };
|
|
2823
|
-
const bundleConnection = installContext?.connection;
|
|
2824
|
-
if (bundleConnection && this._sessions.get(bundleConnection.connectId) !== bundleConnection.sessionId) {
|
|
2825
|
-
throw (0, import_hwk_adapter_core3.createHwkError)({
|
|
2826
|
-
code: import_hwk_adapter_core3.HardwareErrorCode.DeviceDisconnected,
|
|
2827
|
-
message: "Ledger all-network connection ended"
|
|
2828
|
-
});
|
|
2829
|
-
}
|
|
2830
|
-
let resolvedConnectId = operationId ? this._operations.resolve(operationId).connectId : bundleConnection?.connectId ?? await this.ensureConnected(
|
|
2831
|
-
preferredConnectId,
|
|
1902
|
+
const resolvedConnectId = await this.ensureConnected(
|
|
1903
|
+
connectId,
|
|
2832
1904
|
signal,
|
|
2833
|
-
allowUsbEphemeralFallback
|
|
2834
|
-
void 0,
|
|
2835
|
-
connectionAttempt
|
|
1905
|
+
allowUsbEphemeralFallback
|
|
2836
1906
|
);
|
|
2837
|
-
|
|
2838
|
-
if (sessionId && installContext && !installContext.connection) {
|
|
2839
|
-
installContext.connection = { connectId: resolvedConnectId, sessionId };
|
|
2840
|
-
}
|
|
1907
|
+
const sessionId = this._sessions.get(resolvedConnectId);
|
|
2841
1908
|
if (!sessionId) {
|
|
2842
|
-
if (operationId) {
|
|
2843
|
-
this._operations.end(operationId, "disconnect");
|
|
2844
|
-
throw (0, import_hwk_adapter_core3.createHwkError)({
|
|
2845
|
-
code: import_hwk_adapter_core3.HardwareErrorCode.OperationEnded,
|
|
2846
|
-
message: "Ledger operation connection is no longer active",
|
|
2847
|
-
params: { operationId, reason: "disconnect" }
|
|
2848
|
-
});
|
|
2849
|
-
}
|
|
2850
1909
|
throw Object.assign(new Error("Auto-connect succeeded but no session found"), {
|
|
2851
1910
|
_tag: ERROR_TAG.DeviceSessionNotFound
|
|
2852
1911
|
});
|
|
2853
1912
|
}
|
|
2854
1913
|
try {
|
|
2855
1914
|
if (fingerprint && !fingerprint.skipFingerprint && fingerprint.deviceId) {
|
|
2856
|
-
|
|
2857
|
-
|
|
2858
|
-
|
|
2859
|
-
|
|
2860
|
-
|
|
2861
|
-
|
|
2862
|
-
|
|
2863
|
-
)
|
|
2864
|
-
);
|
|
2865
|
-
if (fp.success) break;
|
|
2866
|
-
const binding = operationId ? this._pendingOperationBindings.get(operationId) : connectionAttempt;
|
|
2867
|
-
if (!this._isBleConnection() || binding?.selectedConnection?.connectId !== resolvedConnectId) {
|
|
2868
|
-
throw Object.assign(new Error(formatDeviceMismatchError(fp.expected, fp.actual)), {
|
|
2869
|
-
code: import_hwk_adapter_core3.HardwareErrorCode.DeviceMismatch
|
|
2870
|
-
});
|
|
2871
|
-
}
|
|
2872
|
-
binding.rejectedConnectIds ?? (binding.rejectedConnectIds = /* @__PURE__ */ new Set());
|
|
2873
|
-
binding.rejectedConnectIds.add(resolvedConnectId);
|
|
2874
|
-
binding.rejectedConnectId = resolvedConnectId;
|
|
2875
|
-
this._sessions.delete(resolvedConnectId);
|
|
2876
|
-
await this.connector.disconnect(sessionId);
|
|
2877
|
-
if (operationId) this._pendingOperationDisconnects.delete(operationId);
|
|
2878
|
-
_LedgerAdapter._throwIfAborted(signal);
|
|
2879
|
-
resolvedConnectId = await this._connectFirstOrSelect(
|
|
2880
|
-
[],
|
|
2881
|
-
void 0,
|
|
2882
|
-
true,
|
|
2883
|
-
operationId,
|
|
2884
|
-
binding,
|
|
2885
|
-
signal
|
|
2886
|
-
);
|
|
2887
|
-
const selectedSession = this._sessions.get(resolvedConnectId);
|
|
2888
|
-
const selectedDevice = this._discoveredDevices.get(resolvedConnectId);
|
|
2889
|
-
if (!selectedSession || !selectedDevice)
|
|
2890
|
-
throw (0, import_hwk_adapter_core3.createHwkError)({
|
|
2891
|
-
code: import_hwk_adapter_core3.HardwareErrorCode.DeviceDisconnected,
|
|
2892
|
-
message: "Selected Ledger connection ended"
|
|
2893
|
-
});
|
|
2894
|
-
sessionId = selectedSession;
|
|
2895
|
-
if (operationId)
|
|
2896
|
-
this._operations.rebind(operationId, {
|
|
2897
|
-
connectId: resolvedConnectId,
|
|
2898
|
-
device: selectedDevice,
|
|
2899
|
-
// Same source as `_createOperation`: the selected transport, not
|
|
2900
|
-
// the device snapshot a session connect overwrote.
|
|
2901
|
-
connectionType: this._isBleConnection() ? "ble" : "usb",
|
|
2902
|
-
connectionKeys: [sessionId]
|
|
2903
|
-
});
|
|
2904
|
-
if (installContext)
|
|
2905
|
-
installContext.connection = { connectId: resolvedConnectId, sessionId };
|
|
2906
|
-
}
|
|
2907
|
-
await this._publishVerifiedBleBinding(
|
|
2908
|
-
resolvedConnectId,
|
|
2909
|
-
fingerprint.chain,
|
|
2910
|
-
fingerprint.deviceId,
|
|
2911
|
-
connectionAttempt,
|
|
2912
|
-
operationId,
|
|
2913
|
-
signal
|
|
1915
|
+
const fp = await this._abortable(
|
|
1916
|
+
signal,
|
|
1917
|
+
this._verifyDeviceFingerprintWithSession(
|
|
1918
|
+
sessionId,
|
|
1919
|
+
fingerprint.deviceId,
|
|
1920
|
+
fingerprint.chain
|
|
1921
|
+
)
|
|
2914
1922
|
);
|
|
2915
|
-
if (!
|
|
2916
|
-
|
|
2917
|
-
|
|
2918
|
-
chain: fingerprint.chain,
|
|
2919
|
-
fingerprint: fingerprint.deviceId
|
|
1923
|
+
if (!fp.success) {
|
|
1924
|
+
throw Object.assign(new Error(formatDeviceMismatchError(fp.expected, fp.actual)), {
|
|
1925
|
+
code: import_hwk_adapter_core3.HardwareErrorCode.DeviceMismatch
|
|
2920
1926
|
});
|
|
2921
1927
|
}
|
|
2922
1928
|
}
|
|
2923
|
-
businessCallStarted = true;
|
|
2924
1929
|
return await this._callConnector(sessionId, method, effectiveParams, signal);
|
|
2925
1930
|
} catch (err) {
|
|
2926
1931
|
if (signal.aborted) throw err;
|
|
2927
|
-
if (isDeviceDisconnectedError(err) || isDeviceNotAdvertisingError(err) || isTimeoutError(err) || isConnectionLevelError(err)) {
|
|
2928
|
-
this._discoveredDevices.delete(resolvedConnectId);
|
|
2929
|
-
if (operationId) await this._releaseLostOperationConnection(operationId);
|
|
2930
|
-
else {
|
|
2931
|
-
this._sessions.delete(resolvedConnectId);
|
|
2932
|
-
await this.connector.disconnect(sessionId).catch(() => void 0);
|
|
2933
|
-
}
|
|
2934
|
-
const ambiguous = businessCallStarted && !(0, import_hwk_adapter_core3.canReplayHardwareMethodAfterTransportFailure)(method);
|
|
2935
|
-
const interactionParams = operationId ? { operationId, reason: "disconnect" } : void 0;
|
|
2936
|
-
throw (0, import_hwk_adapter_core3.createHwkError)({
|
|
2937
|
-
code: operationId ? import_hwk_adapter_core3.HardwareErrorCode.OperationEnded : mapLedgerError(err).code,
|
|
2938
|
-
message: "Ledger operation connection was lost; start a new operation",
|
|
2939
|
-
params: ambiguous ? (0, import_hwk_adapter_core3.operationMayHaveCompletedParams)(method, { operationId }) : interactionParams,
|
|
2940
|
-
recovery: ambiguous ? { scope: "unknown" } : void 0
|
|
2941
|
-
});
|
|
2942
|
-
}
|
|
2943
1932
|
const errObj = err;
|
|
2944
1933
|
debugLog("[LedgerAdapter] connectorCall error:", method, {
|
|
2945
1934
|
message: errObj?.message,
|
|
@@ -2951,65 +1940,6 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
2951
1940
|
isNotAdvertising: isDeviceNotAdvertisingError(err),
|
|
2952
1941
|
isStuckApp: isStuckAppStateError(err)
|
|
2953
1942
|
});
|
|
2954
|
-
if ((isDeviceLockedError(err) || errObj?.code === import_hwk_adapter_core3.HardwareErrorCode.DeviceLocked) && lockedRetryBudget > 0) {
|
|
2955
|
-
await this._waitForDeviceConnect(signal);
|
|
2956
|
-
if (this._sessions.get(resolvedConnectId) !== sessionId) {
|
|
2957
|
-
throw (0, import_hwk_adapter_core3.createHwkError)({
|
|
2958
|
-
code: operationId ? import_hwk_adapter_core3.HardwareErrorCode.OperationEnded : import_hwk_adapter_core3.HardwareErrorCode.DeviceDisconnected,
|
|
2959
|
-
message: "Ledger connection ended while waiting for unlock"
|
|
2960
|
-
});
|
|
2961
|
-
}
|
|
2962
|
-
return this._runConnectorCall(
|
|
2963
|
-
resolvedConnectId,
|
|
2964
|
-
method,
|
|
2965
|
-
effectiveParams,
|
|
2966
|
-
signal,
|
|
2967
|
-
fingerprint,
|
|
2968
|
-
permissionDeviceId,
|
|
2969
|
-
commonParams,
|
|
2970
|
-
installContext,
|
|
2971
|
-
operationId,
|
|
2972
|
-
lockedRetryBudget - 1
|
|
2973
|
-
);
|
|
2974
|
-
}
|
|
2975
|
-
if (businessCallStarted && isStuckAppStateError(err)) {
|
|
2976
|
-
await this._sleepAbortable(_LedgerAdapter.STUCK_APP_RETRY_DELAY_MS, signal);
|
|
2977
|
-
if (this._sessions.get(resolvedConnectId) !== sessionId) {
|
|
2978
|
-
throw (0, import_hwk_adapter_core3.createHwkError)({
|
|
2979
|
-
code: operationId ? import_hwk_adapter_core3.HardwareErrorCode.OperationEnded : import_hwk_adapter_core3.HardwareErrorCode.DeviceDisconnected,
|
|
2980
|
-
message: "Ledger connection ended during the app transition"
|
|
2981
|
-
});
|
|
2982
|
-
}
|
|
2983
|
-
try {
|
|
2984
|
-
return await this._callConnector(sessionId, method, effectiveParams, signal);
|
|
2985
|
-
} catch (retryErr) {
|
|
2986
|
-
if (isStuckAppStateError(retryErr)) throw err;
|
|
2987
|
-
if (isDeviceDisconnectedError(retryErr) || isDeviceNotAdvertisingError(retryErr) || isTimeoutError(retryErr) || isConnectionLevelError(retryErr)) {
|
|
2988
|
-
if (operationId) await this._releaseLostOperationConnection(operationId);
|
|
2989
|
-
else {
|
|
2990
|
-
this._sessions.delete(resolvedConnectId);
|
|
2991
|
-
this._discoveredDevices.delete(resolvedConnectId);
|
|
2992
|
-
await this.connector.disconnect(sessionId).catch(() => void 0);
|
|
2993
|
-
}
|
|
2994
|
-
throw (0, import_hwk_adapter_core3.createHwkError)({
|
|
2995
|
-
code: operationId ? import_hwk_adapter_core3.HardwareErrorCode.OperationEnded : mapLedgerError(retryErr).code,
|
|
2996
|
-
message: `Ledger ${method} may have completed before the connection was lost`,
|
|
2997
|
-
params: !(0, import_hwk_adapter_core3.canReplayHardwareMethodAfterTransportFailure)(method) ? (0, import_hwk_adapter_core3.operationMayHaveCompletedParams)(method, {
|
|
2998
|
-
operationId,
|
|
2999
|
-
reason: "disconnect"
|
|
3000
|
-
}) : { operationId, reason: "disconnect" },
|
|
3001
|
-
recovery: !(0, import_hwk_adapter_core3.canReplayHardwareMethodAfterTransportFailure)(method) ? { scope: "unknown" } : void 0
|
|
3002
|
-
});
|
|
3003
|
-
}
|
|
3004
|
-
throw retryErr;
|
|
3005
|
-
}
|
|
3006
|
-
}
|
|
3007
|
-
if (!operationId && err?.code === import_hwk_adapter_core3.HardwareErrorCode.DeviceMismatch) {
|
|
3008
|
-
this._sessions.delete(resolvedConnectId);
|
|
3009
|
-
this._discoveredDevices.delete(resolvedConnectId);
|
|
3010
|
-
await this.connector.disconnect(sessionId).catch(() => void 0);
|
|
3011
|
-
throw err;
|
|
3012
|
-
}
|
|
3013
1943
|
const autoInstallApp = commonParams?.autoInstallApp ?? this._defaultAutoInstallApp;
|
|
3014
1944
|
const isAppMissing = isAppNotInstalledError(err) || err?.code === import_hwk_adapter_core3.HardwareErrorCode.AppNotInstalled;
|
|
3015
1945
|
if (autoInstallApp && isAppMissing) {
|
|
@@ -3065,14 +1995,157 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
3065
1995
|
fingerprint,
|
|
3066
1996
|
permissionDeviceId,
|
|
3067
1997
|
commonParams,
|
|
3068
|
-
installContext
|
|
3069
|
-
|
|
1998
|
+
installContext
|
|
1999
|
+
);
|
|
2000
|
+
}
|
|
2001
|
+
}
|
|
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
|
|
3070
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
|
+
}
|
|
3071
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
|
+
});
|
|
3072
2111
|
}
|
|
3073
2112
|
throw err;
|
|
3074
2113
|
}
|
|
3075
2114
|
}
|
|
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
|
+
}
|
|
3076
2149
|
_sleepAbortable(ms, signal) {
|
|
3077
2150
|
return new Promise((resolve, reject) => {
|
|
3078
2151
|
if (signal.aborted) {
|
|
@@ -3090,6 +2163,88 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
3090
2163
|
signal.addEventListener("abort", onAbort, { once: true });
|
|
3091
2164
|
});
|
|
3092
2165
|
}
|
|
2166
|
+
/**
|
|
2167
|
+
* Clear stale session, reconnect, and retry the call.
|
|
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
|
+
}
|
|
3093
2248
|
/**
|
|
3094
2249
|
* Ensure OS-level device permission (Bluetooth / USB) before proceeding.
|
|
3095
2250
|
*
|
|
@@ -3107,14 +2262,13 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
3107
2262
|
_LedgerAdapter._throwIfAborted(signal);
|
|
3108
2263
|
}
|
|
3109
2264
|
const transportType = this.activeTransport ?? "hid";
|
|
3110
|
-
const operationId = this._activeOperationId();
|
|
3111
2265
|
const waitPromise = this._uiRegistry.wait(
|
|
3112
2266
|
import_hwk_adapter_core3.UI_REQUEST.REQUEST_DEVICE_PERMISSION,
|
|
3113
|
-
{ timeoutMs: 6e4
|
|
2267
|
+
{ timeoutMs: 6e4 }
|
|
3114
2268
|
);
|
|
3115
2269
|
this.emitter.emit(import_hwk_adapter_core3.UI_REQUEST.REQUEST_DEVICE_PERMISSION, {
|
|
3116
2270
|
type: import_hwk_adapter_core3.UI_REQUEST.REQUEST_DEVICE_PERMISSION,
|
|
3117
|
-
payload: { transportType, connectId, deviceId
|
|
2271
|
+
payload: { transportType, connectId, deviceId }
|
|
3118
2272
|
});
|
|
3119
2273
|
let response;
|
|
3120
2274
|
const onAbort = () => {
|
|
@@ -3160,15 +2314,7 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
3160
2314
|
if (err && typeof err === "object" && "code" in err && typeof err.code === "number") {
|
|
3161
2315
|
const e = err;
|
|
3162
2316
|
const params = e.code === import_hwk_adapter_core3.HardwareErrorCode.DevicePermissionDenied && e.reason ? { permissionDeniedReason: e.reason } : e.params;
|
|
3163
|
-
return ledgerFailure(
|
|
3164
|
-
e.code,
|
|
3165
|
-
e.message ?? "Unknown error",
|
|
3166
|
-
e.appName,
|
|
3167
|
-
tag,
|
|
3168
|
-
params,
|
|
3169
|
-
void 0,
|
|
3170
|
-
(0, import_hwk_adapter_core3.isHwkRecoveryHint)(e.recovery) ? e.recovery : void 0
|
|
3171
|
-
);
|
|
2317
|
+
return ledgerFailure(e.code, e.message ?? "Unknown error", e.appName, tag, params);
|
|
3172
2318
|
}
|
|
3173
2319
|
const mapped = mapLedgerError(err);
|
|
3174
2320
|
return ledgerFailure(mapped.code, mapped.message, mapped.appName, tag);
|
|
@@ -3195,7 +2341,7 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
3195
2341
|
deviceId: device.deviceId,
|
|
3196
2342
|
connectId: device.connectId,
|
|
3197
2343
|
label: device.name,
|
|
3198
|
-
connectionType:
|
|
2344
|
+
connectionType: this.connector.connectionType,
|
|
3199
2345
|
rssi: device.rssi,
|
|
3200
2346
|
isConnectable: device.isConnectable,
|
|
3201
2347
|
serialNumber: device.serialNumber,
|
|
@@ -3220,7 +2366,7 @@ _LedgerAdapter.APP_INSTALL_PROGRESS_MIN_DELTA = 0.05;
|
|
|
3220
2366
|
var LedgerAdapter = _LedgerAdapter;
|
|
3221
2367
|
|
|
3222
2368
|
// src/connector/LedgerConnectorBase.ts
|
|
3223
|
-
var
|
|
2369
|
+
var import_hwk_adapter_core13 = require("@onekeyfe/hwk-adapter-core");
|
|
3224
2370
|
|
|
3225
2371
|
// src/device/LedgerDeviceManager.ts
|
|
3226
2372
|
var LedgerDeviceManager = class {
|
|
@@ -3756,7 +2902,7 @@ var SignerManager = class _SignerManager {
|
|
|
3756
2902
|
return (args) => new import_device_signer_kit_ethereum.SignerEthBuilder(args);
|
|
3757
2903
|
}
|
|
3758
2904
|
static _createContextModule() {
|
|
3759
|
-
const contextModule = new import_context_module.ContextModuleBuilder({}).
|
|
2905
|
+
const contextModule = new import_context_module.ContextModuleBuilder({}).removeDefaultLoaders().build();
|
|
3760
2906
|
return _SignerManager.wrapBlindSigningReportNonBlocking(contextModule);
|
|
3761
2907
|
}
|
|
3762
2908
|
static wrapBlindSigningReportNonBlocking(contextModule) {
|
|
@@ -4227,7 +3373,6 @@ function _applySignaturesToPsbt(psbtHex, signatures) {
|
|
|
4227
3373
|
|
|
4228
3374
|
// src/connector/chains/sol.ts
|
|
4229
3375
|
var import_hwk_adapter_core9 = require("@onekeyfe/hwk-adapter-core");
|
|
4230
|
-
var import_bs58 = __toESM(require("bs58"));
|
|
4231
3376
|
|
|
4232
3377
|
// src/signer/SignerSol.ts
|
|
4233
3378
|
var SignerSol = class {
|
|
@@ -4311,21 +3456,8 @@ async function solSignMessage(ctx, sessionId, params) {
|
|
|
4311
3456
|
const path = normalizePath(params.path);
|
|
4312
3457
|
const messageBytes = (0, import_hwk_adapter_core9.hexToBytes)(params.message);
|
|
4313
3458
|
try {
|
|
4314
|
-
if (params.messageVersion === 1) {
|
|
4315
|
-
const preparedMessage = (0, import_hwk_adapter_core9.prepareSolanaOffchainMessageV1)({
|
|
4316
|
-
message: messageBytes,
|
|
4317
|
-
requiredSigners: params.requiredSigners
|
|
4318
|
-
});
|
|
4319
|
-
const { SignMessageVersion } = await ctx.importLedgerKit(
|
|
4320
|
-
"@ledgerhq/device-signer-kit-solana"
|
|
4321
|
-
);
|
|
4322
|
-
const result2 = await solSigner.signMessage(path, preparedMessage.serializedMessage, {
|
|
4323
|
-
version: SignMessageVersion.Raw
|
|
4324
|
-
});
|
|
4325
|
-
return { signature: decodeBase58Signature(result2.signature) };
|
|
4326
|
-
}
|
|
4327
3459
|
const result = await solSigner.signMessage(path, messageBytes);
|
|
4328
|
-
return { signature:
|
|
3460
|
+
return { signature: result.signature };
|
|
4329
3461
|
} catch (err) {
|
|
4330
3462
|
ctx.invalidateSession(sessionId);
|
|
4331
3463
|
throw ctx.wrapError(err);
|
|
@@ -4333,27 +3465,11 @@ async function solSignMessage(ctx, sessionId, params) {
|
|
|
4333
3465
|
ctx.clearCanceller(sessionId);
|
|
4334
3466
|
}
|
|
4335
3467
|
}
|
|
4336
|
-
function decodeBase58Signature(signature) {
|
|
4337
|
-
const bytes = import_bs58.default.decode(signature);
|
|
4338
|
-
if (bytes.length !== 64) {
|
|
4339
|
-
throw new Error(`Ledger Solana signature must be 64 bytes, received ${bytes.length}`);
|
|
4340
|
-
}
|
|
4341
|
-
return (0, import_hwk_adapter_core9.bytesToHex)(bytes);
|
|
4342
|
-
}
|
|
4343
|
-
function decodeBase58EnvelopeSignature(envelope) {
|
|
4344
|
-
const bytes = import_bs58.default.decode(envelope);
|
|
4345
|
-
if (bytes.length < 65 || bytes[0] !== 1) {
|
|
4346
|
-
throw new Error("Ledger Solana signature envelope is invalid");
|
|
4347
|
-
}
|
|
4348
|
-
return (0, import_hwk_adapter_core9.bytesToHex)(bytes.subarray(1, 65));
|
|
4349
|
-
}
|
|
4350
3468
|
async function _createSolSigner(ctx, sessionId) {
|
|
4351
3469
|
const dmk = await ctx.getOrCreateDmk();
|
|
4352
|
-
const { ContextModuleBuilder: ContextModuleBuilder2
|
|
4353
|
-
"@ledgerhq/context-module"
|
|
4354
|
-
);
|
|
3470
|
+
const { ContextModuleBuilder: ContextModuleBuilder2 } = await ctx.importLedgerKit("@ledgerhq/context-module");
|
|
4355
3471
|
const { SignerSolanaBuilder } = await ctx.importLedgerKit("@ledgerhq/device-signer-kit-solana");
|
|
4356
|
-
const contextModule = new ContextModuleBuilder2({}).
|
|
3472
|
+
const contextModule = new ContextModuleBuilder2({}).removeDefaultLoaders().build();
|
|
4357
3473
|
const sdkSigner = new SignerSolanaBuilder({ dmk, sessionId }).withContextModule(contextModule).build();
|
|
4358
3474
|
const signer = new SignerSol(sdkSigner);
|
|
4359
3475
|
signer.onInteraction = (interaction) => {
|
|
@@ -4369,196 +3485,360 @@ async function _createSolSigner(ctx, sessionId) {
|
|
|
4369
3485
|
return signer;
|
|
4370
3486
|
}
|
|
4371
3487
|
|
|
4372
|
-
// src/connector/chains/
|
|
4373
|
-
var
|
|
3488
|
+
// src/connector/chains/tron.ts
|
|
3489
|
+
var import_hwk_adapter_core12 = require("@onekeyfe/hwk-adapter-core");
|
|
3490
|
+
var import_hw_app_trx = __toESM(require("@ledgerhq/hw-app-trx"));
|
|
4374
3491
|
|
|
4375
|
-
// src/
|
|
4376
|
-
var
|
|
4377
|
-
|
|
4378
|
-
|
|
4379
|
-
|
|
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");
|
|
3497
|
+
var import_hwk_adapter_core10 = require("@onekeyfe/hwk-adapter-core");
|
|
3498
|
+
var APP_NAME_MAP = {
|
|
3499
|
+
ETH: "Ethereum",
|
|
3500
|
+
BTC: "Bitcoin",
|
|
3501
|
+
SOL: "Solana",
|
|
3502
|
+
TRX: "Tron",
|
|
3503
|
+
XRP: "XRP",
|
|
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;
|
|
4380
3514
|
}
|
|
4381
|
-
/**
|
|
4382
|
-
|
|
4383
|
-
|
|
4384
|
-
|
|
4385
|
-
|
|
4386
|
-
|
|
4387
|
-
|
|
4388
|
-
|
|
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);
|
|
3556
|
+
}
|
|
3557
|
+
// ---------------------------------------------------------------------------
|
|
3558
|
+
// Private helpers
|
|
3559
|
+
// ---------------------------------------------------------------------------
|
|
3560
|
+
async _getCurrentApp(sessionId) {
|
|
3561
|
+
const result = await this._dmk.sendCommand({
|
|
3562
|
+
sessionId,
|
|
3563
|
+
command: new import_device_management_kit2.GetAppAndVersionCommand()
|
|
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
|
+
}
|
|
4389
3593
|
);
|
|
4390
3594
|
}
|
|
4391
|
-
|
|
4392
|
-
|
|
4393
|
-
|
|
4394
|
-
|
|
4395
|
-
|
|
4396
|
-
|
|
4397
|
-
|
|
4398
|
-
|
|
3595
|
+
async _openApp(sessionId, appName) {
|
|
3596
|
+
const result = await this._dmk.sendCommand({
|
|
3597
|
+
sessionId,
|
|
3598
|
+
command: new import_device_management_kit2.OpenAppCommand({ appName })
|
|
3599
|
+
});
|
|
3600
|
+
if (!(0, import_device_management_kit2.isSuccessCommandResult)(result)) {
|
|
3601
|
+
const dmkErr = result.error;
|
|
3602
|
+
const errorCode = "errorCode" in dmkErr && dmkErr.errorCode != null ? String(dmkErr.errorCode) : "";
|
|
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})`
|
|
4399
3657
|
);
|
|
4400
3658
|
}
|
|
3659
|
+
_isDashboard(appName) {
|
|
3660
|
+
return appName === DASHBOARD_APP_NAME;
|
|
3661
|
+
}
|
|
3662
|
+
_wait() {
|
|
3663
|
+
return new Promise((resolve) => setTimeout(resolve, this._waitMs));
|
|
3664
|
+
}
|
|
4401
3665
|
};
|
|
4402
3666
|
|
|
4403
|
-
// src/connector/chains/
|
|
4404
|
-
|
|
4405
|
-
|
|
4406
|
-
|
|
4407
|
-
|
|
4408
|
-
|
|
4409
|
-
|
|
4410
|
-
|
|
4411
|
-
|
|
3667
|
+
// src/connector/chains/legacyChainCall.ts
|
|
3668
|
+
function isLegacyWrongAppError(err, _appName) {
|
|
3669
|
+
return isWrongAppError(err);
|
|
3670
|
+
}
|
|
3671
|
+
async function withLegacyChainCall(ctx, sessionId, options, action) {
|
|
3672
|
+
const { appName, needsConfirmation } = options;
|
|
3673
|
+
let openAppPromptShown = false;
|
|
3674
|
+
const onAppOpenPrompt = () => {
|
|
3675
|
+
openAppPromptShown = true;
|
|
3676
|
+
ctx.emit("ui-event", {
|
|
3677
|
+
type: import_hwk_adapter_core11.EConnectorInteraction.ConfirmOpenApp,
|
|
3678
|
+
payload: { sessionId }
|
|
3679
|
+
});
|
|
3680
|
+
};
|
|
3681
|
+
const closeOpenAppUiIfShown = () => {
|
|
3682
|
+
if (openAppPromptShown) {
|
|
3683
|
+
ctx.emit("ui-event", {
|
|
3684
|
+
type: import_hwk_adapter_core11.EConnectorInteraction.InteractionComplete,
|
|
3685
|
+
payload: { sessionId }
|
|
3686
|
+
});
|
|
3687
|
+
openAppPromptShown = false;
|
|
4412
3688
|
}
|
|
4413
|
-
|
|
3689
|
+
};
|
|
3690
|
+
try {
|
|
3691
|
+
await _ensureAppOpen(ctx, sessionId, appName, onAppOpenPrompt);
|
|
4414
3692
|
} catch (err) {
|
|
4415
|
-
|
|
4416
|
-
|
|
4417
|
-
|
|
4418
|
-
|
|
4419
|
-
|
|
4420
|
-
|
|
4421
|
-
|
|
4422
|
-
|
|
4423
|
-
const
|
|
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
|
+
};
|
|
4424
3722
|
try {
|
|
4425
|
-
|
|
4426
|
-
checkOnDevice: params.showOnDevice ?? false
|
|
4427
|
-
});
|
|
4428
|
-
return { address: result.address, path: params.path };
|
|
3723
|
+
return await runOnce();
|
|
4429
3724
|
} catch (err) {
|
|
4430
|
-
|
|
4431
|
-
|
|
4432
|
-
|
|
4433
|
-
|
|
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;
|
|
4434
3746
|
}
|
|
4435
3747
|
}
|
|
4436
|
-
async function
|
|
3748
|
+
async function _ensureAppOpen(ctx, sessionId, appName, onPrompt) {
|
|
4437
3749
|
const dmk = await ctx.getOrCreateDmk();
|
|
4438
|
-
const
|
|
4439
|
-
|
|
4440
|
-
const signer = new SignerZcash(sdkSigner);
|
|
4441
|
-
signer.onInteraction = (interaction) => {
|
|
4442
|
-
debugLog("[LedgerConnector] zcash.onInteraction:", interaction);
|
|
4443
|
-
ctx.emit("ui-event", {
|
|
4444
|
-
type: collapseSignerInteraction(interaction),
|
|
4445
|
-
payload: { sessionId }
|
|
4446
|
-
});
|
|
4447
|
-
};
|
|
4448
|
-
signer.onRegisterCanceller = (cancel) => ctx.registerCanceller(sessionId, cancel);
|
|
4449
|
-
return signer;
|
|
3750
|
+
const appManager = new AppManager(dmk);
|
|
3751
|
+
await appManager.ensureAppOpen(sessionId, appName, onPrompt);
|
|
4450
3752
|
}
|
|
4451
3753
|
|
|
4452
|
-
// src/
|
|
4453
|
-
var
|
|
4454
|
-
|
|
4455
|
-
|
|
4456
|
-
|
|
4457
|
-
|
|
4458
|
-
|
|
4459
|
-
this._sdk = _sdk;
|
|
4460
|
-
}
|
|
4461
|
-
/** Base58 Tron address plus the uncompressed public key at `derivationPath`. */
|
|
4462
|
-
async getAddress(derivationPath, options) {
|
|
4463
|
-
const action = this._sdk.getAddress(derivationPath, options);
|
|
4464
|
-
const result = await deviceActionToPromise(
|
|
4465
|
-
action,
|
|
4466
|
-
this.onInteraction,
|
|
4467
|
-
void 0,
|
|
4468
|
-
this.onRegisterCanceller
|
|
4469
|
-
);
|
|
4470
|
-
return { address: result.address, publicKey: result.publicKey };
|
|
3754
|
+
// src/transport/DmkTransport.ts
|
|
3755
|
+
var import_hw_transport = __toESM(require("@ledgerhq/hw-transport"));
|
|
3756
|
+
var DmkTransport = class extends import_hw_transport.default {
|
|
3757
|
+
constructor(dmk, sessionId) {
|
|
3758
|
+
super();
|
|
3759
|
+
this._dmk = dmk;
|
|
3760
|
+
this._sessionId = sessionId;
|
|
4471
3761
|
}
|
|
4472
|
-
|
|
4473
|
-
|
|
4474
|
-
|
|
4475
|
-
|
|
4476
|
-
|
|
4477
|
-
|
|
4478
|
-
|
|
4479
|
-
|
|
4480
|
-
|
|
3762
|
+
async exchange(apdu) {
|
|
3763
|
+
const response = await this._dmk.sendApdu({
|
|
3764
|
+
sessionId: this._sessionId,
|
|
3765
|
+
apdu: new Uint8Array(apdu)
|
|
3766
|
+
});
|
|
3767
|
+
const { data, statusCode } = response;
|
|
3768
|
+
const result = Buffer.alloc(data.length + 2);
|
|
3769
|
+
if (data.length > 0) {
|
|
3770
|
+
result.set(data, 0);
|
|
3771
|
+
}
|
|
3772
|
+
result.set(statusCode, data.length);
|
|
3773
|
+
return result;
|
|
4481
3774
|
}
|
|
4482
|
-
|
|
4483
|
-
async signPersonalMessage(derivationPath, message, options) {
|
|
4484
|
-
const action = this._sdk.signPersonalMessage(derivationPath, message, options);
|
|
4485
|
-
return deviceActionToPromise(
|
|
4486
|
-
action,
|
|
4487
|
-
this.onInteraction,
|
|
4488
|
-
void 0,
|
|
4489
|
-
this.onRegisterCanceller
|
|
4490
|
-
);
|
|
3775
|
+
async close() {
|
|
4491
3776
|
}
|
|
4492
3777
|
};
|
|
4493
3778
|
|
|
4494
3779
|
// src/connector/chains/tron.ts
|
|
4495
3780
|
async function tronGetAddress(ctx, sessionId, params) {
|
|
4496
|
-
const tronSigner = await _createTronSigner(ctx, sessionId);
|
|
4497
3781
|
const path = normalizePath(params.path);
|
|
4498
|
-
|
|
4499
|
-
|
|
4500
|
-
|
|
4501
|
-
|
|
4502
|
-
|
|
4503
|
-
|
|
4504
|
-
|
|
4505
|
-
|
|
4506
|
-
|
|
4507
|
-
|
|
4508
|
-
|
|
3782
|
+
const showOnDevice = params.showOnDevice ?? false;
|
|
3783
|
+
return withLegacyChainCall(
|
|
3784
|
+
ctx,
|
|
3785
|
+
sessionId,
|
|
3786
|
+
{
|
|
3787
|
+
appName: "Tron",
|
|
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
|
+
);
|
|
4509
3798
|
}
|
|
4510
3799
|
async function tronSignTransaction(ctx, sessionId, params) {
|
|
4511
3800
|
if (!params.rawTxHex) {
|
|
4512
3801
|
throw Object.assign(
|
|
4513
3802
|
new Error("TRON signing requires a protobuf-encoded raw transaction hex (rawTxHex)."),
|
|
4514
|
-
{ code:
|
|
3803
|
+
{ code: import_hwk_adapter_core12.HardwareErrorCode.InvalidParams }
|
|
4515
3804
|
);
|
|
4516
3805
|
}
|
|
4517
|
-
const tronSigner = await _createTronSigner(ctx, sessionId);
|
|
4518
3806
|
const path = normalizePath(params.path);
|
|
4519
|
-
|
|
4520
|
-
|
|
4521
|
-
|
|
4522
|
-
|
|
4523
|
-
|
|
4524
|
-
|
|
4525
|
-
|
|
4526
|
-
|
|
4527
|
-
|
|
3807
|
+
return withLegacyChainCall(
|
|
3808
|
+
ctx,
|
|
3809
|
+
sessionId,
|
|
3810
|
+
{ appName: "Tron", needsConfirmation: true },
|
|
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
|
+
);
|
|
4528
3821
|
}
|
|
4529
3822
|
async function tronSignMessage(ctx, sessionId, params) {
|
|
4530
|
-
const tronSigner = await _createTronSigner(ctx, sessionId);
|
|
4531
3823
|
const path = normalizePath(params.path);
|
|
4532
|
-
|
|
4533
|
-
|
|
4534
|
-
|
|
4535
|
-
|
|
4536
|
-
|
|
4537
|
-
|
|
4538
|
-
|
|
4539
|
-
|
|
4540
|
-
|
|
3824
|
+
return withLegacyChainCall(
|
|
3825
|
+
ctx,
|
|
3826
|
+
sessionId,
|
|
3827
|
+
{ appName: "Tron", needsConfirmation: true },
|
|
3828
|
+
async (sid) => {
|
|
3829
|
+
const trx = await _createTrx(ctx, sid);
|
|
3830
|
+
const signature = await trx.signPersonalMessage(path, params.messageHex);
|
|
3831
|
+
return { signature };
|
|
3832
|
+
}
|
|
3833
|
+
);
|
|
4541
3834
|
}
|
|
4542
|
-
async function
|
|
3835
|
+
async function _createTrx(ctx, sessionId) {
|
|
4543
3836
|
const dmk = await ctx.getOrCreateDmk();
|
|
4544
|
-
|
|
4545
|
-
const sdkSigner = new SignerTrxBuilder({ dmk, sessionId }).build();
|
|
4546
|
-
const signer = new SignerTron(sdkSigner);
|
|
4547
|
-
signer.onInteraction = (interaction) => {
|
|
4548
|
-
debugLog("[LedgerConnector] tron.onInteraction:", interaction);
|
|
4549
|
-
ctx.emit("ui-event", {
|
|
4550
|
-
type: collapseSignerInteraction(interaction),
|
|
4551
|
-
payload: { sessionId }
|
|
4552
|
-
});
|
|
4553
|
-
};
|
|
4554
|
-
signer.onRegisterCanceller = (cancel) => {
|
|
4555
|
-
ctx.registerCanceller(sessionId, cancel);
|
|
4556
|
-
};
|
|
4557
|
-
return signer;
|
|
3837
|
+
return new import_hw_app_trx.default(new DmkTransport(dmk, sessionId));
|
|
4558
3838
|
}
|
|
4559
3839
|
|
|
4560
3840
|
// src/device-apps/customActions.ts
|
|
4561
|
-
var
|
|
3841
|
+
var import_device_management_kit3 = require("@ledgerhq/device-management-kit");
|
|
4562
3842
|
var import_rxjs = require("rxjs");
|
|
4563
3843
|
var GetOsVersionDeviceAction = class {
|
|
4564
3844
|
// eslint-disable-next-line no-useless-constructor, no-empty-function
|
|
@@ -4572,7 +3852,7 @@ var GetOsVersionDeviceAction = class {
|
|
|
4572
3852
|
(async () => {
|
|
4573
3853
|
try {
|
|
4574
3854
|
subject.next({
|
|
4575
|
-
status:
|
|
3855
|
+
status: import_device_management_kit3.DeviceActionStatus.Pending,
|
|
4576
3856
|
intermediateValue: { requiredUserInteraction: "none" }
|
|
4577
3857
|
});
|
|
4578
3858
|
const result = await internalApi.sendCommand(new this._deps.GetOsVersionCommand());
|
|
@@ -4581,11 +3861,11 @@ var GetOsVersionDeviceAction = class {
|
|
|
4581
3861
|
const errObj = result?.error;
|
|
4582
3862
|
throw new Error(errObj?.message ?? "GetOsVersionCommand failed");
|
|
4583
3863
|
}
|
|
4584
|
-
subject.next({ status:
|
|
3864
|
+
subject.next({ status: import_device_management_kit3.DeviceActionStatus.Completed, output: result.data });
|
|
4585
3865
|
subject.complete();
|
|
4586
3866
|
} catch (err) {
|
|
4587
3867
|
if (cancelled) return;
|
|
4588
|
-
subject.next({ status:
|
|
3868
|
+
subject.next({ status: import_device_management_kit3.DeviceActionStatus.Error, error: err });
|
|
4589
3869
|
subject.complete();
|
|
4590
3870
|
}
|
|
4591
3871
|
})();
|
|
@@ -4593,7 +3873,7 @@ var GetOsVersionDeviceAction = class {
|
|
|
4593
3873
|
observable: subject.asObservable(),
|
|
4594
3874
|
cancel: () => {
|
|
4595
3875
|
cancelled = true;
|
|
4596
|
-
subject.next({ status:
|
|
3876
|
+
subject.next({ status: import_device_management_kit3.DeviceActionStatus.Stopped });
|
|
4597
3877
|
subject.complete();
|
|
4598
3878
|
}
|
|
4599
3879
|
};
|
|
@@ -4611,7 +3891,7 @@ var ListAvailableAppsDeviceAction = class {
|
|
|
4611
3891
|
(async () => {
|
|
4612
3892
|
try {
|
|
4613
3893
|
subject.next({
|
|
4614
|
-
status:
|
|
3894
|
+
status: import_device_management_kit3.DeviceActionStatus.Pending,
|
|
4615
3895
|
intermediateValue: { requiredUserInteraction: "none" }
|
|
4616
3896
|
});
|
|
4617
3897
|
const osVersionResult = await internalApi.sendCommand(new this._GetOsVersionCommand());
|
|
@@ -4628,11 +3908,11 @@ var ListAvailableAppsDeviceAction = class {
|
|
|
4628
3908
|
throw new Error(httpErr?.message ?? "Manager API getAppList failed");
|
|
4629
3909
|
}
|
|
4630
3910
|
const apps = either.extract();
|
|
4631
|
-
subject.next({ status:
|
|
3911
|
+
subject.next({ status: import_device_management_kit3.DeviceActionStatus.Completed, output: apps });
|
|
4632
3912
|
subject.complete();
|
|
4633
3913
|
} catch (err) {
|
|
4634
3914
|
if (cancelled) return;
|
|
4635
|
-
subject.next({ status:
|
|
3915
|
+
subject.next({ status: import_device_management_kit3.DeviceActionStatus.Error, error: err });
|
|
4636
3916
|
subject.complete();
|
|
4637
3917
|
}
|
|
4638
3918
|
})();
|
|
@@ -4640,7 +3920,7 @@ var ListAvailableAppsDeviceAction = class {
|
|
|
4640
3920
|
observable: subject.asObservable(),
|
|
4641
3921
|
cancel: () => {
|
|
4642
3922
|
cancelled = true;
|
|
4643
|
-
subject.next({ status:
|
|
3923
|
+
subject.next({ status: import_device_management_kit3.DeviceActionStatus.Stopped });
|
|
4644
3924
|
subject.complete();
|
|
4645
3925
|
}
|
|
4646
3926
|
};
|
|
@@ -4733,7 +4013,7 @@ var DeviceApps = class {
|
|
|
4733
4013
|
seTargetId: v.seTargetId,
|
|
4734
4014
|
mcuTargetId: v.mcuTargetId,
|
|
4735
4015
|
seVersion: v.seVersion,
|
|
4736
|
-
seFlagsHex:
|
|
4016
|
+
seFlagsHex: bytesToHex2(v.seFlags),
|
|
4737
4017
|
mcuSephVersion: v.mcuSephVersion,
|
|
4738
4018
|
mcuBootloaderVersion: v.mcuBootloaderVersion,
|
|
4739
4019
|
hwVersion: v.hwVersion
|
|
@@ -4807,7 +4087,7 @@ var DeviceApps = class {
|
|
|
4807
4087
|
}
|
|
4808
4088
|
}
|
|
4809
4089
|
};
|
|
4810
|
-
function
|
|
4090
|
+
function bytesToHex2(bytes) {
|
|
4811
4091
|
if (!bytes) return "";
|
|
4812
4092
|
return Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join("");
|
|
4813
4093
|
}
|
|
@@ -4850,11 +4130,10 @@ var METHOD_PREFIX_TO_APP_NAME = {
|
|
|
4850
4130
|
evm: "Ethereum",
|
|
4851
4131
|
btc: "Bitcoin",
|
|
4852
4132
|
sol: "Solana",
|
|
4853
|
-
tron: "Tron"
|
|
4854
|
-
zcash: "Zcash"
|
|
4133
|
+
tron: "Tron"
|
|
4855
4134
|
};
|
|
4856
4135
|
var HARDWARE_ERROR_CODE_VALUES = new Set(
|
|
4857
|
-
Object.values(
|
|
4136
|
+
Object.values(import_hwk_adapter_core13.HardwareErrorCode).filter((value) => typeof value === "number")
|
|
4858
4137
|
);
|
|
4859
4138
|
var BLE_CONNECT_SCAN_TIMEOUT_MS = 1500;
|
|
4860
4139
|
var LEDGER_RELAY_ALLOWED_ROOT_DOMAINS = ["onekeytest.com", "onekey.com"];
|
|
@@ -4891,10 +4170,6 @@ async function defaultLedgerKitImporter(pkg) {
|
|
|
4891
4170
|
return import("@ledgerhq/device-signer-kit-bitcoin");
|
|
4892
4171
|
case "@ledgerhq/device-signer-kit-solana":
|
|
4893
4172
|
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");
|
|
4898
4173
|
case "@ledgerhq/context-module":
|
|
4899
4174
|
return import("@ledgerhq/context-module");
|
|
4900
4175
|
default:
|
|
@@ -4954,7 +4229,6 @@ var LedgerConnectorBase = class {
|
|
|
4954
4229
|
this._ctx = {
|
|
4955
4230
|
emit: (event, data) => this._emit(event, data),
|
|
4956
4231
|
invalidateSession: (sid) => this._invalidateSession(sid),
|
|
4957
|
-
teardownSecureChannelSession: (sid) => this._teardownSecureChannelSession(sid),
|
|
4958
4232
|
wrapError: (err, opts) => this._wrapError(err, opts),
|
|
4959
4233
|
getOrCreateDmk: () => this._getOrCreateDmk(),
|
|
4960
4234
|
getDeviceManager: () => this._getDeviceManager(),
|
|
@@ -5006,7 +4280,7 @@ var LedgerConnectorBase = class {
|
|
|
5006
4280
|
async searchDevices() {
|
|
5007
4281
|
const dm = await this._getDeviceManager();
|
|
5008
4282
|
const descriptors = await this._discoverDescriptors(dm);
|
|
5009
|
-
const resolvedDescriptors = descriptors.filter((d) => !(0,
|
|
4283
|
+
const resolvedDescriptors = descriptors.filter((d) => !(0, import_hwk_adapter_core13.isKnownNonTargetHardwareVendor)(d, "ledger")).map((d) => ({
|
|
5010
4284
|
descriptor: d,
|
|
5011
4285
|
connectId: this._resolveConnectId(d)
|
|
5012
4286
|
}));
|
|
@@ -5054,7 +4328,7 @@ var LedgerConnectorBase = class {
|
|
|
5054
4328
|
`Ledger BLE connect did not return within ${HANG_CEILING_MS / 6e4}min \u2014 DMK hang fallback.`
|
|
5055
4329
|
);
|
|
5056
4330
|
err._tag = ERROR_TAG.BlePairingTimeout;
|
|
5057
|
-
err.code =
|
|
4331
|
+
err.code = import_hwk_adapter_core13.HardwareErrorCode.BlePairingTimeout;
|
|
5058
4332
|
reject(err);
|
|
5059
4333
|
}, HANG_CEILING_MS);
|
|
5060
4334
|
});
|
|
@@ -5087,7 +4361,7 @@ var LedgerConnectorBase = class {
|
|
|
5087
4361
|
"Ledger device is not currently advertising. Wake up and unlock the device, keep it nearby, then try again."
|
|
5088
4362
|
);
|
|
5089
4363
|
err._tag = ERROR_TAG.DeviceNotAdvertising;
|
|
5090
|
-
err.code =
|
|
4364
|
+
err.code = import_hwk_adapter_core13.HardwareErrorCode.DeviceNotFound;
|
|
5091
4365
|
throw err;
|
|
5092
4366
|
};
|
|
5093
4367
|
const doConnect = async (path) => {
|
|
@@ -5144,14 +4418,14 @@ var LedgerConnectorBase = class {
|
|
|
5144
4418
|
this._resetSignersAndSessions();
|
|
5145
4419
|
if (isLedgerBleConnectionType(this.connectionType)) {
|
|
5146
4420
|
const tag = err?._tag;
|
|
5147
|
-
if (isKnownConnectionTag(tag)
|
|
4421
|
+
if (isKnownConnectionTag(tag)) {
|
|
5148
4422
|
throw err;
|
|
5149
4423
|
}
|
|
5150
4424
|
const wrapped = new Error(
|
|
5151
4425
|
"Ledger Bluetooth pairing failed. Make sure the device is unlocked and nearby, then try again."
|
|
5152
4426
|
);
|
|
5153
4427
|
wrapped._tag = ERROR_TAG.BleGattBondingFailed;
|
|
5154
|
-
wrapped.code =
|
|
4428
|
+
wrapped.code = import_hwk_adapter_core13.HardwareErrorCode.BlePairingTimeout;
|
|
5155
4429
|
wrapped.originalError = err;
|
|
5156
4430
|
throw wrapped;
|
|
5157
4431
|
}
|
|
@@ -5229,7 +4503,7 @@ var LedgerConnectorBase = class {
|
|
|
5229
4503
|
this._unwatchSessionState(sessionId);
|
|
5230
4504
|
this._signerManager?.invalidate(sessionId);
|
|
5231
4505
|
this._cancellers.get(sessionId)?.({
|
|
5232
|
-
code:
|
|
4506
|
+
code: import_hwk_adapter_core13.HardwareErrorCode.DeviceDisconnected,
|
|
5233
4507
|
tag: "DeviceDisconnected",
|
|
5234
4508
|
message: "Device disconnected"
|
|
5235
4509
|
});
|
|
@@ -5253,9 +4527,9 @@ var LedgerConnectorBase = class {
|
|
|
5253
4527
|
if (isAppStuckByApdu(err)) {
|
|
5254
4528
|
return {
|
|
5255
4529
|
success: false,
|
|
5256
|
-
error: (0,
|
|
4530
|
+
error: (0, import_hwk_adapter_core13.serializeConnectorError)(
|
|
5257
4531
|
Object.assign(new Error("Ledger app is unresponsive"), {
|
|
5258
|
-
code:
|
|
4532
|
+
code: import_hwk_adapter_core13.HardwareErrorCode.DeviceAppStuck,
|
|
5259
4533
|
_tag: ERROR_TAG.DeviceAppStuck,
|
|
5260
4534
|
originalError: err
|
|
5261
4535
|
})
|
|
@@ -5265,16 +4539,16 @@ var LedgerConnectorBase = class {
|
|
|
5265
4539
|
if (isTransportStuck(err)) {
|
|
5266
4540
|
return {
|
|
5267
4541
|
success: false,
|
|
5268
|
-
error: (0,
|
|
4542
|
+
error: (0, import_hwk_adapter_core13.serializeConnectorError)(
|
|
5269
4543
|
Object.assign(new Error("Device communication interrupted, please retry"), {
|
|
5270
|
-
code:
|
|
4544
|
+
code: import_hwk_adapter_core13.HardwareErrorCode.TransportError,
|
|
5271
4545
|
_tag: ERROR_TAG.DeviceTransportStuck,
|
|
5272
4546
|
originalError: err
|
|
5273
4547
|
})
|
|
5274
4548
|
)
|
|
5275
4549
|
};
|
|
5276
4550
|
}
|
|
5277
|
-
return { success: false, error: (0,
|
|
4551
|
+
return { success: false, error: (0, import_hwk_adapter_core13.serializeConnectorError)(err) };
|
|
5278
4552
|
}
|
|
5279
4553
|
}
|
|
5280
4554
|
async _dispatch(sessionId, method, params) {
|
|
@@ -5313,11 +4587,6 @@ var LedgerConnectorBase = class {
|
|
|
5313
4587
|
return solSignTransaction(ctx, sessionId, params);
|
|
5314
4588
|
case "solSignMessage":
|
|
5315
4589
|
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);
|
|
5321
4590
|
// TRON
|
|
5322
4591
|
case "tronGetAddress":
|
|
5323
4592
|
return tronGetAddress(ctx, sessionId, params);
|
|
@@ -5342,7 +4611,7 @@ var LedgerConnectorBase = class {
|
|
|
5342
4611
|
try {
|
|
5343
4612
|
return await apps.install(p.appName, ({ progress }) => {
|
|
5344
4613
|
ctx.emit("ui-event", {
|
|
5345
|
-
type:
|
|
4614
|
+
type: import_hwk_adapter_core13.EConnectorInteraction.AppInstallProgress,
|
|
5346
4615
|
payload: {
|
|
5347
4616
|
sessionId,
|
|
5348
4617
|
appName: p.appName,
|
|
@@ -5351,7 +4620,7 @@ var LedgerConnectorBase = class {
|
|
|
5351
4620
|
});
|
|
5352
4621
|
});
|
|
5353
4622
|
} catch (err) {
|
|
5354
|
-
ctx.
|
|
4623
|
+
ctx.invalidateSession(sessionId);
|
|
5355
4624
|
throw ctx.wrapError(err);
|
|
5356
4625
|
} finally {
|
|
5357
4626
|
ctx.clearCanceller(sessionId);
|
|
@@ -5441,7 +4710,7 @@ var LedgerConnectorBase = class {
|
|
|
5441
4710
|
);
|
|
5442
4711
|
return { isGenuine: output.isGenuine, deviceId };
|
|
5443
4712
|
} catch (err) {
|
|
5444
|
-
ctx.
|
|
4713
|
+
ctx.invalidateSession(sessionId);
|
|
5445
4714
|
throw ctx.wrapError(err);
|
|
5446
4715
|
} finally {
|
|
5447
4716
|
ctx.clearCanceller(sessionId);
|
|
@@ -5560,34 +4829,9 @@ var LedgerConnectorBase = class {
|
|
|
5560
4829
|
}
|
|
5561
4830
|
return this._deviceAppsManager;
|
|
5562
4831
|
}
|
|
5563
|
-
// DeviceAppsManager is intentionally absent here: it is a per-call factory
|
|
5564
|
-
// with no cached session state, so there is nothing to invalidate. Cancelling
|
|
5565
|
-
// the device action is what closes the secure channel.
|
|
5566
4832
|
_invalidateSession(sessionId) {
|
|
5567
4833
|
this._signerManager?.invalidate(sessionId);
|
|
5568
4834
|
}
|
|
5569
|
-
/**
|
|
5570
|
-
* Teardown for the OS-level actions that open a manager-api secure channel
|
|
5571
|
-
* (install, uninstall, genuine check). Cancelling the device action is what
|
|
5572
|
-
* stops DMK's xstate actor, which unsubscribes the secure-channel observable
|
|
5573
|
-
* and closes its WebSocket; dropping the canceller without firing it leaves
|
|
5574
|
-
* that teardown to run only if the action settled on its own.
|
|
5575
|
-
*
|
|
5576
|
-
* The DMK device session is deliberately kept: the secure channel is a
|
|
5577
|
-
* separate WebSocket per call, and disconnecting here would break the bounded
|
|
5578
|
-
* unlock/retry recovery the adapter runs on the original session.
|
|
5579
|
-
*/
|
|
5580
|
-
_teardownSecureChannelSession(sessionId) {
|
|
5581
|
-
const cancel = this._cancellers.get(sessionId);
|
|
5582
|
-
this._cancellers.delete(sessionId);
|
|
5583
|
-
if (cancel) {
|
|
5584
|
-
try {
|
|
5585
|
-
cancel();
|
|
5586
|
-
} catch {
|
|
5587
|
-
}
|
|
5588
|
-
}
|
|
5589
|
-
this._invalidateSession(sessionId);
|
|
5590
|
-
}
|
|
5591
4835
|
/**
|
|
5592
4836
|
* Replace an old session with a new one after app switch.
|
|
5593
4837
|
* Emits device-connect so the adapter updates its _sessions Map.
|
|
@@ -5672,7 +4916,7 @@ var LedgerConnectorBase = class {
|
|
|
5672
4916
|
* at every catch site. Falls through unchanged for unknown methods.
|
|
5673
4917
|
*/
|
|
5674
4918
|
_ctxForMethod(method) {
|
|
5675
|
-
const prefix = /^(evm|btc|sol|tron
|
|
4919
|
+
const prefix = /^(evm|btc|sol|tron)/.exec(method)?.[1];
|
|
5676
4920
|
const defaultAppName = prefix ? METHOD_PREFIX_TO_APP_NAME[prefix] : void 0;
|
|
5677
4921
|
if (!defaultAppName) return this._ctx;
|
|
5678
4922
|
return {
|
|
@@ -5706,204 +4950,6 @@ var LedgerConnectorBase = class {
|
|
|
5706
4950
|
return error;
|
|
5707
4951
|
}
|
|
5708
4952
|
};
|
|
5709
|
-
|
|
5710
|
-
// src/transport/DmkTransport.ts
|
|
5711
|
-
var import_hw_transport = __toESM(require("@ledgerhq/hw-transport"));
|
|
5712
|
-
var DmkTransport = class extends import_hw_transport.default {
|
|
5713
|
-
constructor(dmk, sessionId) {
|
|
5714
|
-
super();
|
|
5715
|
-
this._dmk = dmk;
|
|
5716
|
-
this._sessionId = sessionId;
|
|
5717
|
-
}
|
|
5718
|
-
async exchange(apdu) {
|
|
5719
|
-
const response = await this._dmk.sendApdu({
|
|
5720
|
-
sessionId: this._sessionId,
|
|
5721
|
-
apdu: new Uint8Array(apdu)
|
|
5722
|
-
});
|
|
5723
|
-
const { data, statusCode } = response;
|
|
5724
|
-
const result = Buffer.alloc(data.length + 2);
|
|
5725
|
-
if (data.length > 0) {
|
|
5726
|
-
result.set(data, 0);
|
|
5727
|
-
}
|
|
5728
|
-
result.set(statusCode, data.length);
|
|
5729
|
-
return result;
|
|
5730
|
-
}
|
|
5731
|
-
async close() {
|
|
5732
|
-
}
|
|
5733
|
-
};
|
|
5734
|
-
|
|
5735
|
-
// src/app/AppManager.ts
|
|
5736
|
-
var import_device_management_kit3 = require("@ledgerhq/device-management-kit");
|
|
5737
|
-
var import_hwk_adapter_core13 = require("@onekeyfe/hwk-adapter-core");
|
|
5738
|
-
var APP_NAME_MAP = {
|
|
5739
|
-
ETH: "Ethereum",
|
|
5740
|
-
BTC: "Bitcoin",
|
|
5741
|
-
SOL: "Solana",
|
|
5742
|
-
TRX: "Tron",
|
|
5743
|
-
ZEC: "Zcash",
|
|
5744
|
-
XRP: "XRP",
|
|
5745
|
-
ADA: "Cardano",
|
|
5746
|
-
DOT: "Polkadot",
|
|
5747
|
-
ATOM: "Cosmos"
|
|
5748
|
-
};
|
|
5749
|
-
var DASHBOARD_APP_NAME = "BOLOS";
|
|
5750
|
-
var AppManager = class {
|
|
5751
|
-
constructor(dmk, options) {
|
|
5752
|
-
this._dmk = dmk;
|
|
5753
|
-
this._waitMs = options?.waitMs ?? 1e3;
|
|
5754
|
-
this._maxRetries = options?.maxRetries ?? 10;
|
|
5755
|
-
}
|
|
5756
|
-
/**
|
|
5757
|
-
* Return the Ledger app name for a given chain ticker,
|
|
5758
|
-
* or undefined if the chain is not supported.
|
|
5759
|
-
*/
|
|
5760
|
-
static getAppName(chain) {
|
|
5761
|
-
return APP_NAME_MAP[chain];
|
|
5762
|
-
}
|
|
5763
|
-
/**
|
|
5764
|
-
* Ensure the target app is open on the device identified by `sessionId`.
|
|
5765
|
-
*
|
|
5766
|
-
* Flow:
|
|
5767
|
-
* 1. Check the currently running app.
|
|
5768
|
-
* 2. If it is already the target, return immediately.
|
|
5769
|
-
* 3. If a different app is running (not dashboard), close it first.
|
|
5770
|
-
* 4. Open the target app.
|
|
5771
|
-
* 5. Poll until the device confirms the target app is running.
|
|
5772
|
-
*/
|
|
5773
|
-
/**
|
|
5774
|
-
* @param onConfirmOnDevice Called BEFORE OpenAppCommand is issued — the
|
|
5775
|
-
* device is about to display "Open <app>" on screen and wait for the
|
|
5776
|
-
* user's button press. UI consumers should show their "open app" prompt
|
|
5777
|
-
* in response. NOT called when the target app is already open (no user
|
|
5778
|
-
* interaction needed in that case).
|
|
5779
|
-
*
|
|
5780
|
-
* Important: OpenAppCommand is blocking. It does not resolve until the user
|
|
5781
|
-
* has physically confirmed on the device, so anything that runs AFTER
|
|
5782
|
-
* `await this._openApp(...)` lands AFTER the prompt is already gone.
|
|
5783
|
-
* Hence the callback must fire BEFORE that await.
|
|
5784
|
-
*/
|
|
5785
|
-
async ensureAppOpen(sessionId, targetAppName, onConfirmOnDevice) {
|
|
5786
|
-
const currentApp = await this._getCurrentApp(sessionId);
|
|
5787
|
-
if (currentApp === targetAppName) {
|
|
5788
|
-
return;
|
|
5789
|
-
}
|
|
5790
|
-
if (!this._isDashboard(currentApp)) {
|
|
5791
|
-
await this._closeCurrentApp(sessionId);
|
|
5792
|
-
await this._waitForApp(sessionId, DASHBOARD_APP_NAME);
|
|
5793
|
-
}
|
|
5794
|
-
onConfirmOnDevice?.();
|
|
5795
|
-
await this._openApp(sessionId, targetAppName);
|
|
5796
|
-
await this._waitForApp(sessionId, targetAppName);
|
|
5797
|
-
}
|
|
5798
|
-
// ---------------------------------------------------------------------------
|
|
5799
|
-
// Private helpers
|
|
5800
|
-
// ---------------------------------------------------------------------------
|
|
5801
|
-
async _getCurrentApp(sessionId) {
|
|
5802
|
-
const result = await this._dmk.sendCommand({
|
|
5803
|
-
sessionId,
|
|
5804
|
-
command: new import_device_management_kit3.GetAppAndVersionCommand()
|
|
5805
|
-
});
|
|
5806
|
-
if ((0, import_device_management_kit3.isSuccessCommandResult)(result)) {
|
|
5807
|
-
debugLog("[AppManager] currentApp:", result.data.name);
|
|
5808
|
-
return result.data.name;
|
|
5809
|
-
}
|
|
5810
|
-
const errResult = result;
|
|
5811
|
-
const dmkErr = errResult.error ?? {};
|
|
5812
|
-
const original = dmkErr.originalError;
|
|
5813
|
-
debugLog(
|
|
5814
|
-
"[AppManager] _getCurrentApp failed sessionId=",
|
|
5815
|
-
sessionId,
|
|
5816
|
-
"tag=",
|
|
5817
|
-
dmkErr._tag,
|
|
5818
|
-
"errorCode=",
|
|
5819
|
-
dmkErr.errorCode,
|
|
5820
|
-
"message=",
|
|
5821
|
-
dmkErr.message,
|
|
5822
|
-
"originalErrorMessage=",
|
|
5823
|
-
original?.message ?? String(original ?? "")
|
|
5824
|
-
);
|
|
5825
|
-
throw Object.assign(
|
|
5826
|
-
new Error(
|
|
5827
|
-
dmkErr.message ?? "Failed to get current app from device"
|
|
5828
|
-
),
|
|
5829
|
-
{
|
|
5830
|
-
_tag: dmkErr._tag,
|
|
5831
|
-
errorCode: dmkErr.errorCode,
|
|
5832
|
-
originalError: original
|
|
5833
|
-
}
|
|
5834
|
-
);
|
|
5835
|
-
}
|
|
5836
|
-
async _openApp(sessionId, appName) {
|
|
5837
|
-
const result = await this._dmk.sendCommand({
|
|
5838
|
-
sessionId,
|
|
5839
|
-
command: new import_device_management_kit3.OpenAppCommand({ appName })
|
|
5840
|
-
});
|
|
5841
|
-
if (!(0, import_device_management_kit3.isSuccessCommandResult)(result)) {
|
|
5842
|
-
const dmkErr = result.error;
|
|
5843
|
-
const errorCode = "errorCode" in dmkErr && dmkErr.errorCode != null ? String(dmkErr.errorCode) : "";
|
|
5844
|
-
const message = "message" in dmkErr && typeof dmkErr.message === "string" ? dmkErr.message : "";
|
|
5845
|
-
debugLog(
|
|
5846
|
-
"[AppManager] openApp failed:",
|
|
5847
|
-
appName,
|
|
5848
|
-
"errorCode:",
|
|
5849
|
-
errorCode,
|
|
5850
|
-
"tag:",
|
|
5851
|
-
dmkErr._tag
|
|
5852
|
-
);
|
|
5853
|
-
let code;
|
|
5854
|
-
if (errorCode === "6807" || /unknown application/i.test(message)) {
|
|
5855
|
-
code = import_hwk_adapter_core13.HardwareErrorCode.AppNotInstalled;
|
|
5856
|
-
} else if (errorCode === "5501" || dmkErr._tag === "ActionRefusedError") {
|
|
5857
|
-
code = import_hwk_adapter_core13.HardwareErrorCode.UserRejected;
|
|
5858
|
-
}
|
|
5859
|
-
throw Object.assign(new Error(`Failed to open "${appName}"`), {
|
|
5860
|
-
_tag: ERROR_TAG.OpenAppCommand,
|
|
5861
|
-
code,
|
|
5862
|
-
errorCode,
|
|
5863
|
-
statusCode: errorCode,
|
|
5864
|
-
appName,
|
|
5865
|
-
originalError: dmkErr
|
|
5866
|
-
});
|
|
5867
|
-
}
|
|
5868
|
-
}
|
|
5869
|
-
async _closeCurrentApp(sessionId) {
|
|
5870
|
-
debugLog("[AppManager] closeCurrentApp");
|
|
5871
|
-
await this._dmk.sendCommand({
|
|
5872
|
-
sessionId,
|
|
5873
|
-
command: new import_device_management_kit3.CloseAppCommand()
|
|
5874
|
-
});
|
|
5875
|
-
}
|
|
5876
|
-
/**
|
|
5877
|
-
* Poll the device until the expected app is reported as running,
|
|
5878
|
-
* or throw after `_maxRetries` attempts.
|
|
5879
|
-
*/
|
|
5880
|
-
async _waitForApp(sessionId, expectedAppName) {
|
|
5881
|
-
let lastSeen = "";
|
|
5882
|
-
for (let i = 0; i < this._maxRetries; i++) {
|
|
5883
|
-
await this._wait();
|
|
5884
|
-
const current = await this._getCurrentApp(sessionId);
|
|
5885
|
-
lastSeen = current;
|
|
5886
|
-
if (current === expectedAppName) {
|
|
5887
|
-
return;
|
|
5888
|
-
}
|
|
5889
|
-
}
|
|
5890
|
-
debugLog(
|
|
5891
|
-
"[AppManager] waitForApp exhausted: expected=",
|
|
5892
|
-
expectedAppName,
|
|
5893
|
-
"lastSeen=",
|
|
5894
|
-
lastSeen
|
|
5895
|
-
);
|
|
5896
|
-
throw new Error(
|
|
5897
|
-
`Ledger: failed to open "${expectedAppName}" after ${this._maxRetries} retries (last seen: ${lastSeen})`
|
|
5898
|
-
);
|
|
5899
|
-
}
|
|
5900
|
-
_isDashboard(appName) {
|
|
5901
|
-
return appName === DASHBOARD_APP_NAME;
|
|
5902
|
-
}
|
|
5903
|
-
_wait() {
|
|
5904
|
-
return new Promise((resolve) => setTimeout(resolve, this._waitMs));
|
|
5905
|
-
}
|
|
5906
|
-
};
|
|
5907
4953
|
// Annotate the CommonJS export names for ESM import in node:
|
|
5908
4954
|
0 && (module.exports = {
|
|
5909
4955
|
AppManager,
|
|
@@ -5915,7 +4961,6 @@ var AppManager = class {
|
|
|
5915
4961
|
SignerEth,
|
|
5916
4962
|
SignerManager,
|
|
5917
4963
|
SignerSol,
|
|
5918
|
-
SignerZcash,
|
|
5919
4964
|
debugLog,
|
|
5920
4965
|
deviceActionToPromise,
|
|
5921
4966
|
isDeviceLockedError,
|