@onekeyfe/hwk-ledger-adapter 1.2.3-alpha.4 → 1.2.3-alpha.6
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 -65
- package/dist/index.d.ts +29 -65
- package/dist/index.js +781 -1591
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +778 -1606
- package/dist/index.mjs.map +1 -1
- package/package.json +11 -14
package/dist/index.mjs
CHANGED
|
@@ -6,41 +6,30 @@ import {
|
|
|
6
6
|
DeviceJobQueue,
|
|
7
7
|
EConnectorInteraction,
|
|
8
8
|
HardwareErrorCode as HardwareErrorCode3,
|
|
9
|
-
OperationRegistry,
|
|
10
|
-
SDK,
|
|
11
9
|
TypedEventEmitter,
|
|
12
10
|
UI_REQUEST,
|
|
13
11
|
UI_REQUEST_PREEMPTED_TAG,
|
|
14
12
|
UiRequestRegistry,
|
|
15
|
-
canReplayHardwareMethodAfterTransportFailure,
|
|
16
13
|
createHwkError,
|
|
17
14
|
deriveDeviceFingerprint,
|
|
18
15
|
failure as failure2,
|
|
19
|
-
isHardwareOperationId,
|
|
20
|
-
isHwkRecoveryHint,
|
|
21
|
-
operationMayHaveCompletedParams,
|
|
22
16
|
rehydrateConnectorError,
|
|
23
|
-
requestBleDeviceSelection,
|
|
24
|
-
requestSaveDeviceBinding,
|
|
25
|
-
resolveSearchTargetReusePolicy,
|
|
26
17
|
success
|
|
27
18
|
} from "@onekeyfe/hwk-adapter-core";
|
|
28
19
|
|
|
29
20
|
// src/errors.ts
|
|
30
|
-
import {
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
}
|
|
36
|
-
|
|
21
|
+
import { HardwareErrorCode, enrichErrorMessage } from "@onekeyfe/hwk-adapter-core";
|
|
22
|
+
var MULTIPLE_USB_LEDGER_DEVICES_ERROR_MESSAGE = "Multiple Ledger USB devices are connected. Please connect only one Ledger device and try again.";
|
|
23
|
+
function createMultipleUsbLedgerDevicesError() {
|
|
24
|
+
return Object.assign(new Error(MULTIPLE_USB_LEDGER_DEVICES_ERROR_MESSAGE), {
|
|
25
|
+
code: HardwareErrorCode.DeviceOneDeviceOnly
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
function ledgerFailure(code, error, appName, tag, params) {
|
|
37
29
|
const payload = { error, code };
|
|
38
30
|
if (appName !== void 0) payload.appName = appName;
|
|
39
31
|
if (tag !== void 0) payload._tag = tag;
|
|
40
32
|
if (params !== void 0) payload.params = params;
|
|
41
|
-
const resolvedOrigin = origin ?? defaultOriginForCode(code);
|
|
42
|
-
if (resolvedOrigin !== void 0) payload.origin = resolvedOrigin;
|
|
43
|
-
payload.recovery = recovery ?? defaultRecoveryForCode(code);
|
|
44
33
|
return { success: false, payload };
|
|
45
34
|
}
|
|
46
35
|
var LOCKED_ERROR_CODES = /* @__PURE__ */ new Set(["5515", "21781", "6982", "27010", "5303", "21251"]);
|
|
@@ -224,6 +213,7 @@ function isBlePairingFailureError(err) {
|
|
|
224
213
|
return false;
|
|
225
214
|
}
|
|
226
215
|
var CONNECTION_LEVEL_TAGS = /* @__PURE__ */ new Set([
|
|
216
|
+
ERROR_TAG.DeviceLocked,
|
|
227
217
|
ERROR_TAG.DeviceNotAdvertising,
|
|
228
218
|
ERROR_TAG.BlePairingTimeout,
|
|
229
219
|
ERROR_TAG.BleGattBondingFailed,
|
|
@@ -458,30 +448,11 @@ function mapLedgerError(err, opts) {
|
|
|
458
448
|
}
|
|
459
449
|
const errAppName = err && typeof err === "object" ? err.appName : void 0;
|
|
460
450
|
const appName = errAppName ?? opts?.defaultAppName;
|
|
461
|
-
return {
|
|
462
|
-
code,
|
|
463
|
-
message: enrichErrorMessage(code, originalMessage),
|
|
464
|
-
origin: defaultOriginForCode(code),
|
|
465
|
-
appName
|
|
466
|
-
};
|
|
467
|
-
}
|
|
468
|
-
|
|
469
|
-
// src/utils/queueKey.ts
|
|
470
|
-
var LEDGER_DEFAULT_QUEUE_KEY = "__ledger_default__";
|
|
471
|
-
function ledgerQueueKey({
|
|
472
|
-
operationId,
|
|
473
|
-
connectId
|
|
474
|
-
}) {
|
|
475
|
-
return (operationId ?? connectId) || LEDGER_DEFAULT_QUEUE_KEY;
|
|
451
|
+
return { code, message: enrichErrorMessage(code, originalMessage), appName };
|
|
476
452
|
}
|
|
477
453
|
|
|
478
454
|
// src/adapter/methods/allNetworkGetAddress.ts
|
|
479
|
-
import {
|
|
480
|
-
HardwareErrorCode as HardwareErrorCode2,
|
|
481
|
-
failure,
|
|
482
|
-
resolveHardwareOperationTarget,
|
|
483
|
-
runAllNetworkGetAddress
|
|
484
|
-
} from "@onekeyfe/hwk-adapter-core";
|
|
455
|
+
import { HardwareErrorCode as HardwareErrorCode2, failure, runAllNetworkGetAddress } from "@onekeyfe/hwk-adapter-core";
|
|
485
456
|
|
|
486
457
|
// src/utils/sdkEventBus.ts
|
|
487
458
|
var listeners = /* @__PURE__ */ new Set();
|
|
@@ -538,102 +509,65 @@ var LEDGER_BTC_NETWORK_COIN_MAP = {
|
|
|
538
509
|
var LEDGER_UNSUPPORTED_ALLNETWORK_NETWORKS = /* @__PURE__ */ new Set(["doge", "dogecoin"]);
|
|
539
510
|
function createAllNetworkGetAddress({
|
|
540
511
|
callChain,
|
|
541
|
-
getChainFingerprint
|
|
542
|
-
retainOperation,
|
|
543
|
-
errorToFailure,
|
|
544
|
-
createCancelScope
|
|
512
|
+
getChainFingerprint
|
|
545
513
|
}) {
|
|
546
514
|
return async function allNetworkGetAddress(connectId, _deviceId, params) {
|
|
547
|
-
debugLog("[LedgerAdapter][REQ]", {
|
|
548
|
-
method: "allNetworkGetAddress",
|
|
549
|
-
connectId,
|
|
550
|
-
itemCount: params.bundle.length
|
|
551
|
-
});
|
|
552
|
-
const target = resolveHardwareOperationTarget(connectId, params.operationId, "ledger");
|
|
553
|
-
if (!target.success) return target;
|
|
554
|
-
const effectiveTargetId = target.payload.targetId ?? "";
|
|
555
|
-
let releaseOperationRetention;
|
|
556
|
-
try {
|
|
557
|
-
releaseOperationRetention = target.payload.operationId ? retainOperation(target.payload.operationId) : void 0;
|
|
558
|
-
} catch (error) {
|
|
559
|
-
return errorToFailure(error);
|
|
560
|
-
}
|
|
561
|
-
const cancelScope = createCancelScope(
|
|
562
|
-
ledgerQueueKey({ operationId: target.payload.operationId, connectId: effectiveTargetId })
|
|
563
|
-
);
|
|
515
|
+
debugLog("[LedgerAdapter][REQ]", { method: "allNetworkGetAddress", connectId, params });
|
|
564
516
|
const installContext = {};
|
|
565
517
|
const commonParams = {
|
|
566
|
-
autoInstallApp: params.autoInstallApp
|
|
567
|
-
operationId: target.payload.operationId,
|
|
568
|
-
knownConnections: params.knownConnections,
|
|
569
|
-
extra: params.extra,
|
|
570
|
-
allowDeviceSelection: params.allowDeviceSelection
|
|
518
|
+
autoInstallApp: params.autoInstallApp
|
|
571
519
|
};
|
|
572
520
|
const chainFingerprints = /* @__PURE__ */ new Map();
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
itemDeviceId,
|
|
587
|
-
method,
|
|
588
|
-
item,
|
|
589
|
-
commonParams,
|
|
590
|
-
installContext
|
|
591
|
-
);
|
|
592
|
-
},
|
|
593
|
-
attachIdentity: async ({ item, chain, payload }) => attachLedgerIdentity(
|
|
594
|
-
getChainFingerprint,
|
|
595
|
-
effectiveTargetId,
|
|
521
|
+
const result = await runAllNetworkGetAddress({
|
|
522
|
+
connectId,
|
|
523
|
+
deviceId: _deviceId,
|
|
524
|
+
params,
|
|
525
|
+
normalizeItem: normalizeLedgerAllNetworkItem,
|
|
526
|
+
buildUnsupportedNetworkResponse: (item) => isUnsupportedLedgerAllNetworkNetwork(item) ? buildUnsupportedNetworkResponse(item) : void 0,
|
|
527
|
+
callItem: async ({ method, chain, item }) => {
|
|
528
|
+
const itemDeviceId = getItemDeviceId(item) ?? chainFingerprints.get(chain) ?? "";
|
|
529
|
+
return callAllNetworkMethod(
|
|
530
|
+
callChain,
|
|
531
|
+
connectId,
|
|
532
|
+
itemDeviceId,
|
|
533
|
+
method,
|
|
596
534
|
item,
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
}
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
535
|
+
commonParams,
|
|
536
|
+
installContext
|
|
537
|
+
);
|
|
538
|
+
},
|
|
539
|
+
attachIdentity: async ({ item, chain, payload }) => attachLedgerIdentity(
|
|
540
|
+
getChainFingerprint,
|
|
541
|
+
connectId,
|
|
542
|
+
item,
|
|
543
|
+
chain,
|
|
544
|
+
payload,
|
|
545
|
+
chainFingerprints
|
|
546
|
+
),
|
|
547
|
+
shouldAbortBundle: isTopLevelAllNetworkFailure,
|
|
548
|
+
buildTopLevelFailure: (response) => {
|
|
549
|
+
const code = response.payload?.code ?? HardwareErrorCode2.DeviceMismatch;
|
|
550
|
+
return failure(
|
|
551
|
+
code,
|
|
552
|
+
response.payload?.error ?? "All-network get-address aborted",
|
|
553
|
+
response.payload?.params
|
|
554
|
+
);
|
|
555
|
+
}
|
|
556
|
+
});
|
|
557
|
+
debugLog("[LedgerAdapter][RES]", {
|
|
558
|
+
method: "allNetworkGetAddress",
|
|
559
|
+
success: result.success,
|
|
560
|
+
payload: result
|
|
561
|
+
});
|
|
562
|
+
return result;
|
|
623
563
|
};
|
|
624
564
|
}
|
|
625
|
-
function buildCancelledFailure(signal) {
|
|
626
|
-
const reason = signal.reason;
|
|
627
|
-
const code = typeof reason?.code === "number" ? reason.code : HardwareErrorCode2.UserAborted;
|
|
628
|
-
const message = typeof reason?.message === "string" ? reason.message : "";
|
|
629
|
-
return failure(code, message || "All-network get-address cancelled");
|
|
630
|
-
}
|
|
631
565
|
function isTopLevelAllNetworkFailure(response) {
|
|
632
566
|
if (response.success) {
|
|
633
567
|
return false;
|
|
634
568
|
}
|
|
635
569
|
const code = response.payload?.code;
|
|
636
|
-
return code === HardwareErrorCode2.DeviceMismatch || code === HardwareErrorCode2.
|
|
570
|
+
return code === HardwareErrorCode2.DeviceMismatch || code === HardwareErrorCode2.UserAborted || code === HardwareErrorCode2.UserRejected;
|
|
637
571
|
}
|
|
638
572
|
function getItemDeviceId(item) {
|
|
639
573
|
const { deviceId } = item;
|
|
@@ -663,18 +597,8 @@ function normalizeLedgerAllNetworkItem(method, item) {
|
|
|
663
597
|
const coin = LEDGER_BTC_NETWORK_COIN_MAP[item.network];
|
|
664
598
|
return coin ? { ...item, coin } : item;
|
|
665
599
|
}
|
|
666
|
-
async function attachLedgerIdentity(getChainFingerprint, connectId, item, chain, payload, chainFingerprints
|
|
667
|
-
const
|
|
668
|
-
if (!knownFingerprint && cancelSignal.aborted) {
|
|
669
|
-
const cancelled = buildCancelledFailure(cancelSignal);
|
|
670
|
-
return { ...item, success: false, payload: cancelled.payload };
|
|
671
|
-
}
|
|
672
|
-
const fingerprint = knownFingerprint || await bootstrapChainFingerprint(
|
|
673
|
-
getChainFingerprint,
|
|
674
|
-
context.connection?.connectId ?? connectId,
|
|
675
|
-
chain,
|
|
676
|
-
context
|
|
677
|
-
);
|
|
600
|
+
async function attachLedgerIdentity(getChainFingerprint, connectId, item, chain, payload, chainFingerprints) {
|
|
601
|
+
const fingerprint = getItemDeviceId(item) || chainFingerprints.get(chain) || await bootstrapChainFingerprint(getChainFingerprint, connectId, chain);
|
|
678
602
|
if (!fingerprint) {
|
|
679
603
|
return buildFingerprintBootstrapFailure(item, chain);
|
|
680
604
|
}
|
|
@@ -695,8 +619,8 @@ async function attachLedgerIdentity(getChainFingerprint, connectId, item, chain,
|
|
|
695
619
|
}
|
|
696
620
|
};
|
|
697
621
|
}
|
|
698
|
-
async function bootstrapChainFingerprint(getChainFingerprint, connectId, chain
|
|
699
|
-
const response = await getChainFingerprint(connectId, chain
|
|
622
|
+
async function bootstrapChainFingerprint(getChainFingerprint, connectId, chain) {
|
|
623
|
+
const response = await getChainFingerprint(connectId, chain);
|
|
700
624
|
return response.success ? response.payload : "";
|
|
701
625
|
}
|
|
702
626
|
function buildFingerprintBootstrapFailure(item, chain) {
|
|
@@ -801,27 +725,8 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
801
725
|
constructor(connector, options) {
|
|
802
726
|
this.vendor = "ledger";
|
|
803
727
|
this.emitter = new TypedEventEmitter();
|
|
804
|
-
this._operations = new OperationRegistry({
|
|
805
|
-
vendor: "ledger",
|
|
806
|
-
onEnded: (operation, reason) => {
|
|
807
|
-
const binding = this._pendingOperationBindings.get(operation.operationId);
|
|
808
|
-
if (binding?.selectedConnection?.requestId === this._bindingSelectionRequestId) {
|
|
809
|
-
this._finishBleBinding("cancelled");
|
|
810
|
-
}
|
|
811
|
-
this._pendingOperationBindings.delete(operation.operationId);
|
|
812
|
-
this.emitter.emit(SDK.OPERATION_ENDED, {
|
|
813
|
-
type: SDK.OPERATION_ENDED,
|
|
814
|
-
payload: { operationId: operation.operationId, reason }
|
|
815
|
-
});
|
|
816
|
-
if (reason === "timeout") {
|
|
817
|
-
void this._releaseOperationConnection(operation);
|
|
818
|
-
}
|
|
819
|
-
}
|
|
820
|
-
});
|
|
821
728
|
this._discoveredDevices = /* @__PURE__ */ new Map();
|
|
822
729
|
this._sessions = /* @__PURE__ */ new Map();
|
|
823
|
-
this._pendingOperationBindings = /* @__PURE__ */ new Map();
|
|
824
|
-
this._verifiedBleReconnectTargets = /* @__PURE__ */ new Map();
|
|
825
730
|
this._uiRegistry = new UiRequestRegistry();
|
|
826
731
|
// BTC App rejects account index >= 100 unless display=true. Cached per
|
|
827
732
|
// adapter instance: first 100+ path asks the user once via UI request,
|
|
@@ -835,31 +740,10 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
835
740
|
this._deviceAuthenticityQueueTail = Promise.resolve();
|
|
836
741
|
// Shared across concurrent callers — only `cancel()` aborts.
|
|
837
742
|
this._doConnectAbortController = null;
|
|
838
|
-
this._unsettledConnectorOperations = /* @__PURE__ */ new Map();
|
|
839
|
-
this._connectorIdleWaiters = /* @__PURE__ */ new Set();
|
|
840
|
-
this._resetPromise = null;
|
|
841
|
-
this._stateGeneration = 0;
|
|
842
|
-
this._connectorTeardownTail = Promise.resolve();
|
|
843
|
-
this._pendingConnectorTeardowns = 0;
|
|
844
|
-
this._activeOperationJobs = /* @__PURE__ */ new Set();
|
|
845
|
-
this._pendingOperationDisconnects = /* @__PURE__ */ new Set();
|
|
846
743
|
this._installProgressLastEmittedValue = -Infinity;
|
|
847
744
|
this.allNetworkGetAddress = createAllNetworkGetAddress({
|
|
848
745
|
callChain: this.callChain.bind(this),
|
|
849
|
-
getChainFingerprint:
|
|
850
|
-
try {
|
|
851
|
-
const fingerprint = await this._computeChainFingerprint(
|
|
852
|
-
chain,
|
|
853
|
-
(method, params) => this.connectorCall(connectId, method, params, void 0, void 0, void 0, context)
|
|
854
|
-
);
|
|
855
|
-
return success(fingerprint);
|
|
856
|
-
} catch (error) {
|
|
857
|
-
return this.errorToFailure(error);
|
|
858
|
-
}
|
|
859
|
-
},
|
|
860
|
-
retainOperation: (operationId) => this._operations.retain(operationId),
|
|
861
|
-
errorToFailure: (error) => this.errorToFailure(error),
|
|
862
|
-
createCancelScope: (queueKey) => this._jobQueue.createCancelScope(queueKey)
|
|
746
|
+
getChainFingerprint: (connectId, chain) => this.getChainFingerprint(connectId, "", chain)
|
|
863
747
|
});
|
|
864
748
|
// ---------------------------------------------------------------------------
|
|
865
749
|
// Private helpers
|
|
@@ -869,7 +753,7 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
869
753
|
*
|
|
870
754
|
* - If a session already exists for the given connectId, reuse it.
|
|
871
755
|
* - If ANY session exists (Ledger IDs are ephemeral), reuse it.
|
|
872
|
-
* - Otherwise: search →
|
|
756
|
+
* - Otherwise: search → exactly 1 USB device auto-connects; multiple or none throws.
|
|
873
757
|
*/
|
|
874
758
|
// Mutex for ensureConnected — prevents concurrent calls from establishing duplicate connections
|
|
875
759
|
this._connectingPromise = null;
|
|
@@ -886,12 +770,6 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
886
770
|
});
|
|
887
771
|
};
|
|
888
772
|
this.deviceDisconnectHandler = (data) => {
|
|
889
|
-
const activeOperation = this._operations.findActiveByConnectionKey(data.connectId);
|
|
890
|
-
if (activeOperation && this._activeOperationJobs.has(activeOperation.operationId)) {
|
|
891
|
-
this._pendingOperationDisconnects.add(activeOperation.operationId);
|
|
892
|
-
} else {
|
|
893
|
-
this._operations.endByConnectionKey(data.connectId, "disconnect");
|
|
894
|
-
}
|
|
895
773
|
this._discoveredDevices.delete(data.connectId);
|
|
896
774
|
this._sessions.delete(data.connectId);
|
|
897
775
|
this.emitter.emit(DEVICE.DISCONNECT, {
|
|
@@ -948,28 +826,11 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
948
826
|
this._jobQueue = new DeviceJobQueue();
|
|
949
827
|
this.registerEventListeners();
|
|
950
828
|
}
|
|
951
|
-
_finishBleBinding(status) {
|
|
952
|
-
const selectionRequestId = this._bindingSelectionRequestId;
|
|
953
|
-
this._bindingSelectionRequestId = void 0;
|
|
954
|
-
if (selectionRequestId)
|
|
955
|
-
this.emitter.emit(UI_REQUEST.DEVICE_BINDING_STATUS, {
|
|
956
|
-
type: UI_REQUEST.DEVICE_BINDING_STATUS,
|
|
957
|
-
payload: { selectionRequestId, status }
|
|
958
|
-
});
|
|
959
|
-
}
|
|
960
|
-
_isBleConnection() {
|
|
961
|
-
return isLedgerBleConnectionType(this._activeConnectionType ?? this.connector.connectionType);
|
|
962
|
-
}
|
|
963
829
|
// Transport
|
|
964
830
|
get activeTransport() {
|
|
965
|
-
return this.
|
|
831
|
+
return isLedgerBleConnectionType(this.connector.connectionType) ? "ble" : "hid";
|
|
966
832
|
}
|
|
967
833
|
getAvailableTransports() {
|
|
968
|
-
if (this.connector.availableTransports) {
|
|
969
|
-
return this.connector.availableTransports.map(
|
|
970
|
-
(transport) => transport === "ble" ? "ble" : "hid"
|
|
971
|
-
);
|
|
972
|
-
}
|
|
973
834
|
return this.activeTransport ? [this.activeTransport] : [];
|
|
974
835
|
}
|
|
975
836
|
// Connector is bound at construction; switching requires a new adapter.
|
|
@@ -986,39 +847,21 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
986
847
|
* The next operation will re-discover and re-connect automatically.
|
|
987
848
|
*/
|
|
988
849
|
resetState() {
|
|
989
|
-
void this._resetStateAndDisconnectSessions();
|
|
990
|
-
}
|
|
991
|
-
_resetStateAndDisconnectSessions() {
|
|
992
|
-
if (this._resetPromise) return this._resetPromise;
|
|
993
|
-
const sessionIds = new Set(this._sessions.values());
|
|
994
|
-
this._stateGeneration += 1;
|
|
995
|
-
this._finishBleBinding("cancelled");
|
|
996
|
-
this._operations.endAll("runtime-reset");
|
|
997
|
-
this._doConnectAbortController?.abort();
|
|
998
850
|
this._discoveredDevices.clear();
|
|
999
851
|
this._sessions.clear();
|
|
1000
|
-
this._verifiedBleReconnectTargets.clear();
|
|
1001
852
|
this._connectingPromise = null;
|
|
1002
|
-
this._doConnectAbortController = null;
|
|
1003
853
|
this._uiRegistry.reset();
|
|
1004
854
|
this._jobQueue.clear();
|
|
1005
855
|
this._btcHighIndexConfirmedThisSession = false;
|
|
1006
|
-
const resetPromise = this._runConnectorTeardown(async () => {
|
|
1007
|
-
for (const sessionId of sessionIds) {
|
|
1008
|
-
await this.connector.disconnect(sessionId).catch(() => void 0);
|
|
1009
|
-
}
|
|
1010
|
-
});
|
|
1011
|
-
this._resetPromise = resetPromise;
|
|
1012
|
-
return resetPromise.finally(() => {
|
|
1013
|
-
if (this._resetPromise === resetPromise) {
|
|
1014
|
-
this._resetPromise = null;
|
|
1015
|
-
}
|
|
1016
|
-
});
|
|
1017
856
|
}
|
|
1018
857
|
async dispose() {
|
|
1019
|
-
|
|
858
|
+
this._uiRegistry.reset();
|
|
859
|
+
this._jobQueue.clear();
|
|
1020
860
|
this.unregisterEventListeners();
|
|
1021
861
|
this.connector.reset();
|
|
862
|
+
this._discoveredDevices.clear();
|
|
863
|
+
this._sessions.clear();
|
|
864
|
+
this._btcHighIndexConfirmedThisSession = false;
|
|
1022
865
|
this.emitter.removeAllListeners();
|
|
1023
866
|
}
|
|
1024
867
|
uiResponse(response) {
|
|
@@ -1028,32 +871,17 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
1028
871
|
// Device management
|
|
1029
872
|
// ---------------------------------------------------------------------------
|
|
1030
873
|
async searchDevices(options) {
|
|
1031
|
-
return this._searchDevices(options);
|
|
1032
|
-
}
|
|
1033
|
-
async _searchDevices(options, signal) {
|
|
1034
874
|
debugLog("[LedgerAdapter][REQ]", { method: "searchDevices", params: options });
|
|
1035
875
|
try {
|
|
1036
876
|
if (options?.resetSession) {
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
if (signal) _LedgerAdapter._throwIfAborted(signal);
|
|
1043
|
-
const stateGeneration = this._stateGeneration;
|
|
1044
|
-
const devices = await this.connector.searchDevices(
|
|
1045
|
-
options?.transportType ? {
|
|
1046
|
-
transportType: options.transportType,
|
|
1047
|
-
waitForAll: options.waitForAllTransports
|
|
1048
|
-
} : void 0
|
|
1049
|
-
);
|
|
1050
|
-
if (signal) _LedgerAdapter._throwIfAborted(signal);
|
|
1051
|
-
if (stateGeneration !== this._stateGeneration) {
|
|
1052
|
-
throw createHwkError({
|
|
1053
|
-
code: HardwareErrorCode3.UserAborted,
|
|
1054
|
-
message: "Ledger discovery was reset"
|
|
1055
|
-
});
|
|
877
|
+
this._doConnectAbortController?.abort();
|
|
878
|
+
this._sessions.clear();
|
|
879
|
+
this._connectingPromise = null;
|
|
880
|
+
this._doConnectAbortController = null;
|
|
881
|
+
this._btcHighIndexConfirmedThisSession = false;
|
|
1056
882
|
}
|
|
883
|
+
await this._ensureDevicePermission();
|
|
884
|
+
const devices = await this.connector.searchDevices();
|
|
1057
885
|
this._discoveredDevices.clear();
|
|
1058
886
|
for (const d of devices) {
|
|
1059
887
|
if (d.connectId) {
|
|
@@ -1061,7 +889,7 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
1061
889
|
}
|
|
1062
890
|
}
|
|
1063
891
|
if (this._discoveredDevices.size === 0) {
|
|
1064
|
-
await this._ensureDevicePermission(
|
|
892
|
+
await this._ensureDevicePermission();
|
|
1065
893
|
}
|
|
1066
894
|
const result = Array.from(this._discoveredDevices.values());
|
|
1067
895
|
debugLog("[LedgerAdapter][RES]", {
|
|
@@ -1080,285 +908,41 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
1080
908
|
throw err;
|
|
1081
909
|
}
|
|
1082
910
|
}
|
|
1083
|
-
async searchDeviceTargets(options) {
|
|
1084
|
-
const devices = await this.searchDevices(options);
|
|
1085
|
-
return devices.map((device) => ({
|
|
1086
|
-
searchTargetId: device.connectId,
|
|
1087
|
-
searchTargetReusePolicy: resolveSearchTargetReusePolicy(device),
|
|
1088
|
-
vendor: "ledger",
|
|
1089
|
-
connectionType: device.connectionType,
|
|
1090
|
-
kind: "physical",
|
|
1091
|
-
label: device.label,
|
|
1092
|
-
model: device.model,
|
|
1093
|
-
modelName: device.modelName,
|
|
1094
|
-
serialNumber: device.serialNumber
|
|
1095
|
-
}));
|
|
1096
|
-
}
|
|
1097
|
-
async listConnectionTargets(options) {
|
|
1098
|
-
const targets = await this.searchDeviceTargets(options);
|
|
1099
|
-
return targets.map(({ searchTargetId, ...target }) => ({
|
|
1100
|
-
...target,
|
|
1101
|
-
targetId: searchTargetId
|
|
1102
|
-
}));
|
|
1103
|
-
}
|
|
1104
911
|
// USB single-session invariant: evict all sessions, best-effort (see connectDevice).
|
|
1105
|
-
async _evictAllSessions(
|
|
1106
|
-
this._operations.endAll("explicit", preserveOperationId);
|
|
912
|
+
async _evictAllSessions() {
|
|
1107
913
|
if (this._sessions.size === 0) return;
|
|
1108
914
|
const stale = [...this._sessions.values()];
|
|
1109
915
|
this._sessions.clear();
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
} catch {
|
|
1115
|
-
}
|
|
916
|
+
for (const sid of stale) {
|
|
917
|
+
try {
|
|
918
|
+
await this.connector.disconnect(sid);
|
|
919
|
+
} catch {
|
|
1116
920
|
}
|
|
1117
|
-
}
|
|
921
|
+
}
|
|
1118
922
|
}
|
|
1119
923
|
static _createDeviceBusyError(method) {
|
|
1120
924
|
return Object.assign(new Error(`Ledger device is busy while calling ${method}`), {
|
|
1121
925
|
code: HardwareErrorCode3.DeviceBusy
|
|
1122
926
|
});
|
|
1123
927
|
}
|
|
1124
|
-
async connectDevice(
|
|
1125
|
-
try {
|
|
1126
|
-
return await this._jobQueue.enqueue(
|
|
1127
|
-
searchTargetId || "__ledger_connect__",
|
|
1128
|
-
async () => {
|
|
1129
|
-
const connected = await this._connectTarget(searchTargetId);
|
|
1130
|
-
if (!connected.success) return connected;
|
|
1131
|
-
return this._createOperation(searchTargetId, connected.payload);
|
|
1132
|
-
},
|
|
1133
|
-
{
|
|
1134
|
-
label: "connectDevice",
|
|
1135
|
-
rejectIfBusy: true,
|
|
1136
|
-
busyError: _LedgerAdapter._createDeviceBusyError("connectDevice")
|
|
1137
|
-
}
|
|
1138
|
-
);
|
|
1139
|
-
} catch (error) {
|
|
1140
|
-
return this.errorToFailure(error);
|
|
1141
|
-
}
|
|
1142
|
-
}
|
|
1143
|
-
async bindBleDevice(params) {
|
|
1144
|
-
if (params.identity.vendor !== "ledger" || !params.identity.value) {
|
|
1145
|
-
return failure2(HardwareErrorCode3.InvalidParams, "Ledger wallet identity is required");
|
|
1146
|
-
}
|
|
1147
|
-
const { chain, value: expectedFingerprint } = params.identity;
|
|
1148
|
-
try {
|
|
1149
|
-
return await this._jobQueue.enqueue(
|
|
1150
|
-
expectedFingerprint,
|
|
1151
|
-
async (signal) => {
|
|
1152
|
-
if (!this.getAvailableTransports().includes("ble")) {
|
|
1153
|
-
throw createHwkError({
|
|
1154
|
-
code: HardwareErrorCode3.TransportNotAvailable,
|
|
1155
|
-
message: "Ledger Bluetooth transport is not available"
|
|
1156
|
-
});
|
|
1157
|
-
}
|
|
1158
|
-
this._activeConnectionType = "ble";
|
|
1159
|
-
await this._ensureDevicePermission(void 0, void 0, signal);
|
|
1160
|
-
const attempt = {
|
|
1161
|
-
extra: params.extra,
|
|
1162
|
-
bindingReason: "manual-rebind"
|
|
1163
|
-
};
|
|
1164
|
-
try {
|
|
1165
|
-
for (; ; ) {
|
|
1166
|
-
const connectId = await this._connectFirstOrSelect(
|
|
1167
|
-
[],
|
|
1168
|
-
void 0,
|
|
1169
|
-
true,
|
|
1170
|
-
void 0,
|
|
1171
|
-
attempt,
|
|
1172
|
-
signal
|
|
1173
|
-
);
|
|
1174
|
-
const sessionId = this._sessions.get(connectId);
|
|
1175
|
-
if (!sessionId) {
|
|
1176
|
-
throw createHwkError({
|
|
1177
|
-
code: HardwareErrorCode3.DeviceDisconnected,
|
|
1178
|
-
message: "Selected Ledger connection ended"
|
|
1179
|
-
});
|
|
1180
|
-
}
|
|
1181
|
-
let saved = false;
|
|
1182
|
-
try {
|
|
1183
|
-
const installContext = {
|
|
1184
|
-
connection: { connectId, sessionId }
|
|
1185
|
-
};
|
|
1186
|
-
const fingerprint = await this._computeChainFingerprint(
|
|
1187
|
-
chain,
|
|
1188
|
-
(method, callParams) => this._runConnectorCall(
|
|
1189
|
-
connectId,
|
|
1190
|
-
method,
|
|
1191
|
-
callParams,
|
|
1192
|
-
signal,
|
|
1193
|
-
void 0,
|
|
1194
|
-
void 0,
|
|
1195
|
-
{ autoInstallApp: true },
|
|
1196
|
-
installContext
|
|
1197
|
-
)
|
|
1198
|
-
);
|
|
1199
|
-
if (fingerprint !== expectedFingerprint) {
|
|
1200
|
-
attempt.rejectedConnectIds ?? (attempt.rejectedConnectIds = /* @__PURE__ */ new Set());
|
|
1201
|
-
attempt.rejectedConnectIds.add(connectId);
|
|
1202
|
-
attempt.rejectedConnectId = connectId;
|
|
1203
|
-
} else {
|
|
1204
|
-
const persisted = await this._publishVerifiedBleBinding(
|
|
1205
|
-
connectId,
|
|
1206
|
-
chain,
|
|
1207
|
-
fingerprint,
|
|
1208
|
-
attempt,
|
|
1209
|
-
void 0,
|
|
1210
|
-
signal
|
|
1211
|
-
);
|
|
1212
|
-
if (!persisted) {
|
|
1213
|
-
throw createHwkError({
|
|
1214
|
-
code: HardwareErrorCode3.UnknownError,
|
|
1215
|
-
message: "Bluetooth binding could not be saved",
|
|
1216
|
-
origin: "host"
|
|
1217
|
-
});
|
|
1218
|
-
}
|
|
1219
|
-
saved = true;
|
|
1220
|
-
return success(connectId);
|
|
1221
|
-
}
|
|
1222
|
-
} finally {
|
|
1223
|
-
if (!saved && this._sessions.get(connectId) === sessionId) {
|
|
1224
|
-
this._sessions.delete(connectId);
|
|
1225
|
-
const teardown = this._runConnectorTeardown(
|
|
1226
|
-
() => this.connector.disconnect(sessionId)
|
|
1227
|
-
).catch(() => void 0);
|
|
1228
|
-
if (!signal.aborted) await teardown;
|
|
1229
|
-
}
|
|
1230
|
-
}
|
|
1231
|
-
}
|
|
1232
|
-
} catch (error) {
|
|
1233
|
-
this._finishBleBinding(signal.aborted ? "cancelled" : "failed");
|
|
1234
|
-
throw error;
|
|
1235
|
-
}
|
|
1236
|
-
},
|
|
1237
|
-
{
|
|
1238
|
-
label: "bindBleDevice",
|
|
1239
|
-
rejectIfBusy: true,
|
|
1240
|
-
busyError: _LedgerAdapter._createDeviceBusyError("bindBleDevice")
|
|
1241
|
-
}
|
|
1242
|
-
);
|
|
1243
|
-
} catch (error) {
|
|
1244
|
-
return this.errorToFailure(error);
|
|
1245
|
-
}
|
|
1246
|
-
}
|
|
1247
|
-
async acquireOperation(connectId, context) {
|
|
1248
|
-
try {
|
|
1249
|
-
return await this._jobQueue.enqueue(
|
|
1250
|
-
connectId || "__ledger_acquire__",
|
|
1251
|
-
async (signal) => {
|
|
1252
|
-
const transport = this._isBleConnection() ? "ble" : "usb";
|
|
1253
|
-
const hint = context.knownConnections?.find(
|
|
1254
|
-
(connection) => connection.transport === transport
|
|
1255
|
-
);
|
|
1256
|
-
const target = hint && hint.transport !== "qr" ? hint.connectId : connectId;
|
|
1257
|
-
await this._ensureDevicePermission(target, void 0, signal);
|
|
1258
|
-
const attempt = {
|
|
1259
|
-
...context,
|
|
1260
|
-
extra: context.extra ? { ...context.extra } : void 0
|
|
1261
|
-
};
|
|
1262
|
-
try {
|
|
1263
|
-
const resolvedConnectId = await this.ensureConnected(
|
|
1264
|
-
target,
|
|
1265
|
-
signal,
|
|
1266
|
-
true,
|
|
1267
|
-
void 0,
|
|
1268
|
-
attempt
|
|
1269
|
-
);
|
|
1270
|
-
_LedgerAdapter._throwIfAborted(signal);
|
|
1271
|
-
const result = this._createOperation(connectId, resolvedConnectId);
|
|
1272
|
-
if (result.success && attempt.selectedConnection) {
|
|
1273
|
-
this._pendingOperationBindings.set(result.payload, attempt);
|
|
1274
|
-
}
|
|
1275
|
-
return result;
|
|
1276
|
-
} catch (error) {
|
|
1277
|
-
this._finishBleBinding(signal.aborted ? "cancelled" : "failed");
|
|
1278
|
-
throw error;
|
|
1279
|
-
}
|
|
1280
|
-
},
|
|
1281
|
-
{
|
|
1282
|
-
label: "acquireOperation",
|
|
1283
|
-
rejectIfBusy: true,
|
|
1284
|
-
busyError: _LedgerAdapter._createDeviceBusyError("acquireOperation")
|
|
1285
|
-
}
|
|
1286
|
-
);
|
|
1287
|
-
} catch (error) {
|
|
1288
|
-
return this.errorToFailure(error);
|
|
1289
|
-
}
|
|
1290
|
-
}
|
|
1291
|
-
_createOperation(searchTargetId, resolvedConnectId) {
|
|
1292
|
-
this._operations.endByConnectionKey(resolvedConnectId, "explicit");
|
|
1293
|
-
const sessionId = this._sessions.get(resolvedConnectId);
|
|
1294
|
-
if (sessionId) this._operations.endByConnectionKey(sessionId, "explicit");
|
|
1295
|
-
const device = this._discoveredDevices.get(resolvedConnectId) ?? {
|
|
1296
|
-
vendor: "ledger",
|
|
1297
|
-
model: "unknown",
|
|
1298
|
-
firmwareVersion: "",
|
|
1299
|
-
deviceId: "",
|
|
1300
|
-
connectId: resolvedConnectId,
|
|
1301
|
-
connectionType: this.connector.connectionType
|
|
1302
|
-
};
|
|
1303
|
-
const operation = this._operations.create({
|
|
1304
|
-
searchTargetId,
|
|
1305
|
-
connectId: resolvedConnectId,
|
|
1306
|
-
device,
|
|
1307
|
-
connectionKeys: [this._sessions.get(resolvedConnectId) ?? ""]
|
|
1308
|
-
});
|
|
1309
|
-
return success(operation.operationId);
|
|
1310
|
-
}
|
|
1311
|
-
async _connectTarget(connectId, preserveOperationId, signal) {
|
|
928
|
+
async connectDevice(connectId) {
|
|
1312
929
|
debugLog("[LedgerAdapter][REQ]", { method: "connectDevice", connectId, params: { connectId } });
|
|
1313
930
|
try {
|
|
1314
|
-
this.
|
|
1315
|
-
const discoveredType = this._discoveredDevices.get(connectId)?.connectionType;
|
|
1316
|
-
if (discoveredType === "usb" || discoveredType === "ble") {
|
|
1317
|
-
this._activeConnectionType = discoveredType;
|
|
1318
|
-
}
|
|
1319
|
-
if (this._isBleConnection() && !connectId) {
|
|
931
|
+
if (isLedgerBleConnectionType(this.connector.connectionType) && !connectId) {
|
|
1320
932
|
throw Object.assign(new Error("Ledger BLE connectId is required."), {
|
|
1321
933
|
code: HardwareErrorCode3.DeviceNotFound
|
|
1322
934
|
});
|
|
1323
935
|
}
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
if (this.connector.availableTransports && this.connector.availableTransports.length > 1) {
|
|
1327
|
-
await this._evictAllSessions(preserveOperationId);
|
|
1328
|
-
} else if (this._isBleConnection()) {
|
|
1329
|
-
const previousSessionId = this._sessions.get(connectId);
|
|
1330
|
-
this._operations.endByConnectionKey(connectId, "explicit", preserveOperationId);
|
|
1331
|
-
if (previousSessionId) {
|
|
1332
|
-
this._operations.endByConnectionKey(previousSessionId, "explicit", preserveOperationId);
|
|
1333
|
-
this._sessions.delete(connectId);
|
|
1334
|
-
await this.connector.disconnect(previousSessionId).catch(() => void 0);
|
|
1335
|
-
}
|
|
1336
|
-
} else {
|
|
1337
|
-
await this._evictAllSessions(preserveOperationId);
|
|
1338
|
-
}
|
|
1339
|
-
if (signal) _LedgerAdapter._throwIfAborted(signal);
|
|
1340
|
-
const stateGeneration = this._stateGeneration;
|
|
1341
|
-
const releaseOperation = this._retainConnectorOperation(`connect:${connectId}`);
|
|
1342
|
-
let session;
|
|
1343
|
-
try {
|
|
1344
|
-
session = this.connector.availableTransports?.length ? await this.connector.connect(connectId, {
|
|
1345
|
-
transportType: this._activeConnectionType ?? "usb"
|
|
1346
|
-
}) : await this.connector.connect(connectId);
|
|
1347
|
-
if (signal?.aborted || stateGeneration !== this._stateGeneration) {
|
|
1348
|
-
await this.connector.disconnect(session.sessionId).catch(() => void 0);
|
|
1349
|
-
throw Object.assign(new Error("Ledger connection aborted"), {
|
|
1350
|
-
code: HardwareErrorCode3.UserAborted
|
|
1351
|
-
});
|
|
1352
|
-
}
|
|
1353
|
-
} finally {
|
|
1354
|
-
releaseOperation();
|
|
936
|
+
if (!isLedgerBleConnectionType(this.connector.connectionType)) {
|
|
937
|
+
await this._evictAllSessions();
|
|
1355
938
|
}
|
|
1356
|
-
|
|
1357
|
-
this.
|
|
939
|
+
await this._ensureDevicePermission(connectId);
|
|
940
|
+
const session = await this.connector.connect(connectId);
|
|
941
|
+
this._sessions.set(connectId, session.sessionId);
|
|
1358
942
|
if (session.deviceInfo) {
|
|
1359
|
-
this._discoveredDevices.set(
|
|
943
|
+
this._discoveredDevices.set(connectId, session.deviceInfo);
|
|
1360
944
|
}
|
|
1361
|
-
const result = success(
|
|
945
|
+
const result = success(connectId);
|
|
1362
946
|
debugLog("[LedgerAdapter][RES]", { method: "connectDevice", success: true, payload: result });
|
|
1363
947
|
return result;
|
|
1364
948
|
} catch (err) {
|
|
@@ -1371,60 +955,30 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
1371
955
|
return failureResult;
|
|
1372
956
|
}
|
|
1373
957
|
}
|
|
1374
|
-
async
|
|
1375
|
-
const operation = this._operations.find(operationId);
|
|
1376
|
-
if (!operation) {
|
|
1377
|
-
this._operations.resolve(operationId);
|
|
1378
|
-
return;
|
|
1379
|
-
}
|
|
1380
|
-
const endedOperation = this._operations.end(operationId, "explicit");
|
|
1381
|
-
if (!endedOperation) return;
|
|
1382
|
-
const { connectId } = operation;
|
|
958
|
+
async disconnectDevice(connectId) {
|
|
1383
959
|
debugLog("[LedgerAdapter][REQ]", {
|
|
1384
|
-
method: "
|
|
960
|
+
method: "disconnectDevice",
|
|
1385
961
|
connectId,
|
|
1386
962
|
params: { connectId }
|
|
1387
963
|
});
|
|
1388
964
|
try {
|
|
1389
|
-
|
|
1390
|
-
|
|
965
|
+
const sessionId = this._sessions.get(connectId);
|
|
966
|
+
if (sessionId) {
|
|
967
|
+
await this.connector.disconnect(sessionId);
|
|
968
|
+
this._sessions.delete(connectId);
|
|
969
|
+
}
|
|
970
|
+
debugLog("[LedgerAdapter][RES]", { method: "disconnectDevice", success: true });
|
|
1391
971
|
} catch (err) {
|
|
1392
972
|
const e = err;
|
|
1393
973
|
debugLog("[LedgerAdapter][RES]", {
|
|
1394
|
-
method: "
|
|
974
|
+
method: "disconnectDevice",
|
|
1395
975
|
success: false,
|
|
1396
976
|
error: { message: e?.message, _tag: e?._tag, code: e?.code ?? e?.errorCode }
|
|
1397
977
|
});
|
|
1398
978
|
throw err;
|
|
1399
979
|
}
|
|
1400
980
|
}
|
|
1401
|
-
async
|
|
1402
|
-
const sessionIds = /* @__PURE__ */ new Set();
|
|
1403
|
-
for (const [connectId, sessionId] of this._sessions) {
|
|
1404
|
-
if (connectId === operation.connectId || operation.connectionKeys.includes(connectId) || operation.connectionKeys.includes(sessionId)) {
|
|
1405
|
-
this._sessions.delete(connectId);
|
|
1406
|
-
sessionIds.add(sessionId);
|
|
1407
|
-
}
|
|
1408
|
-
}
|
|
1409
|
-
for (const sessionId of sessionIds) {
|
|
1410
|
-
await this._runConnectorTeardown(
|
|
1411
|
-
() => this.connector.disconnect(sessionId).catch(() => void 0)
|
|
1412
|
-
);
|
|
1413
|
-
}
|
|
1414
|
-
}
|
|
1415
|
-
async _releaseLostOperationConnection(operationId) {
|
|
1416
|
-
const endedOperation = this._operations.end(operationId, "disconnect");
|
|
1417
|
-
if (!endedOperation) return;
|
|
1418
|
-
this._discoveredDevices.delete(endedOperation.connectId);
|
|
1419
|
-
await this._releaseOperationConnection(endedOperation);
|
|
1420
|
-
}
|
|
1421
|
-
async getDeviceInfo(connectIdOrOperationId, deviceId) {
|
|
1422
|
-
let connectId;
|
|
1423
|
-
try {
|
|
1424
|
-
connectId = isHardwareOperationId(connectIdOrOperationId) ? this._operations.resolve(connectIdOrOperationId).connectId : connectIdOrOperationId;
|
|
1425
|
-
} catch (error) {
|
|
1426
|
-
return this.errorToFailure(error);
|
|
1427
|
-
}
|
|
981
|
+
async getDeviceInfo(connectId, deviceId) {
|
|
1428
982
|
debugLog("[LedgerAdapter][REQ]", {
|
|
1429
983
|
method: "getDeviceInfo",
|
|
1430
984
|
connectId,
|
|
@@ -1462,6 +1016,9 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
1462
1016
|
throw err;
|
|
1463
1017
|
}
|
|
1464
1018
|
}
|
|
1019
|
+
getSupportedChains() {
|
|
1020
|
+
return ["evm", "btc", "sol", "tron"];
|
|
1021
|
+
}
|
|
1465
1022
|
// ---------------------------------------------------------------------------
|
|
1466
1023
|
// Chain call helper
|
|
1467
1024
|
// ---------------------------------------------------------------------------
|
|
@@ -1496,21 +1053,13 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
1496
1053
|
if (params && typeof params === "object") {
|
|
1497
1054
|
const {
|
|
1498
1055
|
autoInstallApp,
|
|
1499
|
-
operationId,
|
|
1500
1056
|
passphraseState: _passphraseState,
|
|
1501
1057
|
useEmptyPassphrase: _useEmptyPassphrase,
|
|
1502
|
-
knownConnections,
|
|
1503
|
-
extra,
|
|
1504
|
-
allowDeviceSelection,
|
|
1505
1058
|
...rest
|
|
1506
1059
|
} = params;
|
|
1507
1060
|
return {
|
|
1508
1061
|
commonParams: {
|
|
1509
|
-
autoInstallApp: typeof autoInstallApp === "boolean" ? autoInstallApp : void 0
|
|
1510
|
-
operationId: typeof operationId === "string" ? operationId : void 0,
|
|
1511
|
-
knownConnections,
|
|
1512
|
-
extra,
|
|
1513
|
-
allowDeviceSelection: typeof allowDeviceSelection === "boolean" ? allowDeviceSelection : void 0
|
|
1062
|
+
autoInstallApp: typeof autoInstallApp === "boolean" ? autoInstallApp : void 0
|
|
1514
1063
|
},
|
|
1515
1064
|
rest
|
|
1516
1065
|
};
|
|
@@ -1686,27 +1235,6 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
1686
1235
|
);
|
|
1687
1236
|
}
|
|
1688
1237
|
// ---------------------------------------------------------------------------
|
|
1689
|
-
// Zcash chain methods (viewing key + shielded address reads; Zcash app >= 3.8.0)
|
|
1690
|
-
// ---------------------------------------------------------------------------
|
|
1691
|
-
zcashGetFullViewingKey(connectId, deviceId, params) {
|
|
1692
|
-
return this.callChainWithMergedParams(
|
|
1693
|
-
connectId,
|
|
1694
|
-
deviceId,
|
|
1695
|
-
"zcash",
|
|
1696
|
-
"zcashGetFullViewingKey",
|
|
1697
|
-
params
|
|
1698
|
-
);
|
|
1699
|
-
}
|
|
1700
|
-
zcashGetShieldedAddress(connectId, deviceId, params) {
|
|
1701
|
-
return this.callChainWithMergedParams(
|
|
1702
|
-
connectId,
|
|
1703
|
-
deviceId,
|
|
1704
|
-
"zcash",
|
|
1705
|
-
"zcashGetShieldedAddress",
|
|
1706
|
-
params
|
|
1707
|
-
);
|
|
1708
|
-
}
|
|
1709
|
-
// ---------------------------------------------------------------------------
|
|
1710
1238
|
// App management — OS-level Ledger app install / list. Bypasses fingerprint
|
|
1711
1239
|
// and chain-handler dispatch; installApp progress is forwarded to the adapter
|
|
1712
1240
|
// emitter via 'ui-event' AppInstallProgress events.
|
|
@@ -1795,7 +1323,7 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
1795
1323
|
);
|
|
1796
1324
|
}
|
|
1797
1325
|
await this.connector.configure({ ledgerGenuineCheckWebSocketUrl: relayUrl });
|
|
1798
|
-
|
|
1326
|
+
this.resetState();
|
|
1799
1327
|
}
|
|
1800
1328
|
const result = await this.connectorCall(connectId, "getDeviceGenuineCheck", {});
|
|
1801
1329
|
if (!result.isGenuine) {
|
|
@@ -1821,10 +1349,10 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
1821
1349
|
if (relayUrl) {
|
|
1822
1350
|
try {
|
|
1823
1351
|
await this.connector.configure?.({ ledgerGenuineCheckWebSocketUrl: void 0 });
|
|
1824
|
-
|
|
1352
|
+
this.resetState();
|
|
1825
1353
|
} catch {
|
|
1826
1354
|
this.connector.reset();
|
|
1827
|
-
|
|
1355
|
+
this.resetState();
|
|
1828
1356
|
}
|
|
1829
1357
|
}
|
|
1830
1358
|
}
|
|
@@ -1836,20 +1364,6 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
1836
1364
|
this.emitter.off(event, listener);
|
|
1837
1365
|
}
|
|
1838
1366
|
cancel(connectId) {
|
|
1839
|
-
const namedOperationIsLive = (id) => {
|
|
1840
|
-
try {
|
|
1841
|
-
this._operations.resolve(id);
|
|
1842
|
-
return true;
|
|
1843
|
-
} catch {
|
|
1844
|
-
return false;
|
|
1845
|
-
}
|
|
1846
|
-
};
|
|
1847
|
-
if (isHardwareOperationId(connectId) && !namedOperationIsLive(connectId)) {
|
|
1848
|
-
debugLog("[LedgerAdapter] cancel target already ended; nothing to cancel", {
|
|
1849
|
-
connectId
|
|
1850
|
-
});
|
|
1851
|
-
return;
|
|
1852
|
-
}
|
|
1853
1367
|
const userAbortReason = Object.assign(new Error("User aborted operation"), {
|
|
1854
1368
|
code: HardwareErrorCode3.UserAborted,
|
|
1855
1369
|
_tag: ERROR_TAG.UserAborted
|
|
@@ -1860,46 +1374,14 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
1860
1374
|
this._lastCancelReason = void 0;
|
|
1861
1375
|
}
|
|
1862
1376
|
}, 2e3);
|
|
1863
|
-
const activeJobId = this._jobQueue.getActiveJob()?.deviceId;
|
|
1864
|
-
let operationId;
|
|
1865
|
-
if (isHardwareOperationId(connectId)) {
|
|
1866
|
-
operationId = connectId;
|
|
1867
|
-
} else if (!connectId && isHardwareOperationId(activeJobId)) {
|
|
1868
|
-
operationId = activeJobId;
|
|
1869
|
-
}
|
|
1870
|
-
let resolvedConnectId = connectId;
|
|
1871
|
-
if (operationId) {
|
|
1872
|
-
try {
|
|
1873
|
-
resolvedConnectId = this._operations.resolve(operationId).connectId;
|
|
1874
|
-
} catch {
|
|
1875
|
-
resolvedConnectId = void 0;
|
|
1876
|
-
}
|
|
1877
|
-
}
|
|
1878
1377
|
this._uiRegistry.cancel();
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
const pendingOperationId = operationId ?? interactionForPhysicalId?.operationId;
|
|
1882
|
-
if (!connectId) this._pendingOperationBindings.clear();
|
|
1883
|
-
else if (pendingOperationId) {
|
|
1884
|
-
this._pendingOperationBindings.delete(pendingOperationId);
|
|
1885
|
-
}
|
|
1886
|
-
const queueKeys = /* @__PURE__ */ new Set();
|
|
1887
|
-
if (connectId) queueKeys.add(ledgerQueueKey({ connectId }));
|
|
1888
|
-
if (pendingOperationId) queueKeys.add(ledgerQueueKey({ operationId: pendingOperationId }));
|
|
1889
|
-
if (queueKeys.size) {
|
|
1890
|
-
let cancelledAnyJob = false;
|
|
1891
|
-
for (const key of queueKeys) {
|
|
1892
|
-
cancelledAnyJob = this._jobQueue.cancelActiveAndPending(key, userAbortReason) || cancelledAnyJob;
|
|
1893
|
-
}
|
|
1894
|
-
debugLog("[LedgerAdapter] cancel routed to queue keys", {
|
|
1895
|
-
queueKeys: [...queueKeys],
|
|
1896
|
-
cancelledAnyJob
|
|
1897
|
-
});
|
|
1378
|
+
if (connectId) {
|
|
1379
|
+
this._jobQueue.cancelActiveAndPending(connectId, userAbortReason);
|
|
1898
1380
|
} else {
|
|
1899
1381
|
this._jobQueue.cancelActiveAndPending(void 0, userAbortReason);
|
|
1900
1382
|
}
|
|
1901
|
-
if (
|
|
1902
|
-
const sessionId = this._sessions.get(
|
|
1383
|
+
if (connectId) {
|
|
1384
|
+
const sessionId = this._sessions.get(connectId) ?? connectId;
|
|
1903
1385
|
void this.connector.cancel(sessionId);
|
|
1904
1386
|
} else {
|
|
1905
1387
|
for (const sid of this._sessions.values()) void this.connector.cancel(sid);
|
|
@@ -1911,98 +1393,41 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
1911
1393
|
// ---------------------------------------------------------------------------
|
|
1912
1394
|
// Chain fingerprint
|
|
1913
1395
|
// ---------------------------------------------------------------------------
|
|
1914
|
-
/** A non-empty deviceId is an expected chain fingerprint, not a transport identifier. */
|
|
1915
1396
|
async getChainFingerprint(connectId, deviceId, chain) {
|
|
1916
1397
|
try {
|
|
1917
1398
|
const fingerprint = await this._computeChainFingerprint(
|
|
1918
1399
|
chain,
|
|
1919
1400
|
(method, params) => this.connectorCall(connectId, method, params, void 0, deviceId)
|
|
1920
1401
|
);
|
|
1921
|
-
if (deviceId) {
|
|
1922
|
-
if (fingerprint !== deviceId) {
|
|
1923
|
-
return failure2(
|
|
1924
|
-
HardwareErrorCode3.DeviceMismatch,
|
|
1925
|
-
formatDeviceMismatchError(deviceId, fingerprint)
|
|
1926
|
-
);
|
|
1927
|
-
}
|
|
1928
|
-
if (isHardwareOperationId(connectId)) {
|
|
1929
|
-
const operation = this._operations.resolve(connectId);
|
|
1930
|
-
await this._publishVerifiedBleBinding(
|
|
1931
|
-
operation.connectId,
|
|
1932
|
-
chain,
|
|
1933
|
-
fingerprint,
|
|
1934
|
-
void 0,
|
|
1935
|
-
connectId
|
|
1936
|
-
);
|
|
1937
|
-
}
|
|
1938
|
-
}
|
|
1939
1402
|
return success(fingerprint);
|
|
1940
1403
|
} catch (err) {
|
|
1941
1404
|
debugError("[LedgerAdapter] getChainFingerprint error:", chain, err);
|
|
1942
1405
|
return this.errorToFailure(err);
|
|
1943
1406
|
}
|
|
1944
1407
|
}
|
|
1945
|
-
/**
|
|
1946
|
-
|
|
1947
|
-
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
}
|
|
1408
|
+
/**
|
|
1409
|
+
* Verify fingerprint using an existing sessionId directly.
|
|
1410
|
+
* Safe to call inside connectorCall without causing queue deadlock.
|
|
1411
|
+
*/
|
|
1412
|
+
async _verifyDeviceFingerprintWithSession(sessionId, deviceId, chain) {
|
|
1413
|
+
if (!deviceId) return { success: true };
|
|
1951
1414
|
try {
|
|
1952
|
-
const
|
|
1953
|
-
|
|
1954
|
-
this.
|
|
1955
|
-
{
|
|
1956
|
-
selectionRequestId: binding.selectedConnection.requestId,
|
|
1957
|
-
connection: { transport: "ble", connectId },
|
|
1958
|
-
identity: { vendor: "ledger", type: "chainFingerprint", chain, value: fingerprint },
|
|
1959
|
-
extra: binding.extra
|
|
1960
|
-
},
|
|
1961
|
-
signal
|
|
1415
|
+
const fingerprint = await this._computeChainFingerprint(
|
|
1416
|
+
chain,
|
|
1417
|
+
(method, params) => this._callConnector(sessionId, method, params)
|
|
1962
1418
|
);
|
|
1963
|
-
if (
|
|
1964
|
-
|
|
1965
|
-
debugLog("[LedgerAdapter] BLE binding not persisted by host", {
|
|
1966
|
-
connectId,
|
|
1967
|
-
reason: outcome.reason
|
|
1968
|
-
});
|
|
1969
|
-
}
|
|
1970
|
-
return outcome.saved;
|
|
1971
|
-
} catch (error) {
|
|
1972
|
-
if (operationId) {
|
|
1973
|
-
this._pendingOperationBindings.delete(operationId);
|
|
1974
|
-
this._operations.end(operationId, "explicit");
|
|
1419
|
+
if (fingerprint === deviceId) {
|
|
1420
|
+
return { success: true };
|
|
1975
1421
|
}
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
|
|
1981
|
-
).catch(() => void 0);
|
|
1982
|
-
if (!signal?.aborted) await teardown;
|
|
1983
|
-
}
|
|
1984
|
-
throw error;
|
|
1985
|
-
} finally {
|
|
1986
|
-
if (this._bindingSelectionRequestId === binding.selectedConnection.requestId) {
|
|
1987
|
-
this._bindingSelectionRequestId = void 0;
|
|
1988
|
-
}
|
|
1989
|
-
if (operationId) {
|
|
1990
|
-
this._pendingOperationBindings.delete(operationId);
|
|
1422
|
+
return { success: false, expected: deviceId, actual: fingerprint };
|
|
1423
|
+
} catch (err) {
|
|
1424
|
+
const mapped = mapLedgerError(err);
|
|
1425
|
+
if (mapped.code === HardwareErrorCode3.WrongApp || mapped.code === HardwareErrorCode3.DeviceLocked) {
|
|
1426
|
+
return { success: true };
|
|
1991
1427
|
}
|
|
1428
|
+
throw err;
|
|
1992
1429
|
}
|
|
1993
1430
|
}
|
|
1994
|
-
/** Verify on the acquired session without re-entering the job queue. */
|
|
1995
|
-
async _verifyDeviceFingerprintWithSession(sessionId, deviceId, chain) {
|
|
1996
|
-
if (!deviceId) return { success: true };
|
|
1997
|
-
const fingerprint = await this._computeChainFingerprint(
|
|
1998
|
-
chain,
|
|
1999
|
-
(method, params) => this._callConnector(sessionId, method, params)
|
|
2000
|
-
);
|
|
2001
|
-
if (fingerprint === deviceId) {
|
|
2002
|
-
return { success: true };
|
|
2003
|
-
}
|
|
2004
|
-
return { success: false, expected: deviceId, actual: fingerprint };
|
|
2005
|
-
}
|
|
2006
1431
|
/**
|
|
2007
1432
|
* Compute the chain fingerprint via a caller-supplied call strategy.
|
|
2008
1433
|
*
|
|
@@ -2030,8 +1455,6 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
2030
1455
|
address = (await callMethod("solGetAddress", { path, showOnDevice: false })).address;
|
|
2031
1456
|
} else if (chain === "tron") {
|
|
2032
1457
|
address = (await callMethod("tronGetAddress", { path, showOnDevice: false })).address;
|
|
2033
|
-
} else if (chain === "zcash") {
|
|
2034
|
-
address = (await callMethod("zcashGetShieldedAddress", { path, showOnDevice: false })).address;
|
|
2035
1458
|
} else {
|
|
2036
1459
|
throw new Error(`Unsupported chain for fingerprint: ${chain}`);
|
|
2037
1460
|
}
|
|
@@ -2161,17 +1584,16 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
2161
1584
|
// Layer 1 entry. Caller signal only races the outer awaiter; the shared
|
|
2162
1585
|
// `_doConnect` runs under its own internal controller so caller A's cancel
|
|
2163
1586
|
// doesn't kill caller B's await.
|
|
2164
|
-
async ensureConnected(connectId, signal, allowUsbEphemeralFallback = false
|
|
1587
|
+
async ensureConnected(connectId, signal, allowUsbEphemeralFallback = false) {
|
|
2165
1588
|
if (signal.aborted) _LedgerAdapter._throwIfAborted(signal);
|
|
2166
|
-
this.
|
|
2167
|
-
if (this._isBleConnection() && !connectId && !allowUsbEphemeralFallback) {
|
|
1589
|
+
if (isLedgerBleConnectionType(this.connector.connectionType) && !connectId) {
|
|
2168
1590
|
throw Object.assign(new Error("Ledger BLE connectId is required."), {
|
|
2169
1591
|
code: HardwareErrorCode3.DeviceNotFound
|
|
2170
1592
|
});
|
|
2171
1593
|
}
|
|
2172
1594
|
if (connectId && this._sessions.has(connectId)) return connectId;
|
|
2173
1595
|
if (!connectId && this._sessions.size > 0) {
|
|
2174
|
-
if (!this.
|
|
1596
|
+
if (!isLedgerBleConnectionType(this.connector.connectionType) && this._sessions.size > 1) {
|
|
2175
1597
|
throw Object.assign(
|
|
2176
1598
|
new Error(
|
|
2177
1599
|
"Ledger USB session invariant violated: more than one session is active. Please reconnect the device."
|
|
@@ -2181,24 +1603,15 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
2181
1603
|
}
|
|
2182
1604
|
return this._sessions.keys().next().value;
|
|
2183
1605
|
}
|
|
2184
|
-
if (!this._connectingPromise
|
|
2185
|
-
|
|
2186
|
-
this._doConnectAbortController
|
|
2187
|
-
const innerSignal = controller.signal;
|
|
1606
|
+
if (!this._connectingPromise) {
|
|
1607
|
+
this._doConnectAbortController = new AbortController();
|
|
1608
|
+
const innerSignal = this._doConnectAbortController.signal;
|
|
2188
1609
|
this._connectingPromise = (async () => {
|
|
2189
1610
|
try {
|
|
2190
|
-
return await this._doConnect(
|
|
2191
|
-
innerSignal,
|
|
2192
|
-
connectId,
|
|
2193
|
-
allowUsbEphemeralFallback,
|
|
2194
|
-
preserveOperationId,
|
|
2195
|
-
context
|
|
2196
|
-
);
|
|
1611
|
+
return await this._doConnect(innerSignal, connectId, allowUsbEphemeralFallback);
|
|
2197
1612
|
} finally {
|
|
2198
|
-
|
|
2199
|
-
|
|
2200
|
-
this._doConnectAbortController = null;
|
|
2201
|
-
}
|
|
1613
|
+
this._connectingPromise = null;
|
|
1614
|
+
this._doConnectAbortController = null;
|
|
2202
1615
|
}
|
|
2203
1616
|
})();
|
|
2204
1617
|
}
|
|
@@ -2207,51 +1620,22 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
2207
1620
|
// Layer 1 main loop — the ONLY place in SDK that emits unlock dialog.
|
|
2208
1621
|
// Bounded by MAX_DOCONNECT_CONFIRMS — after N Confirms with no progress,
|
|
2209
1622
|
// throw DeviceNotFound so the user is kicked out of the loop.
|
|
2210
|
-
async _doConnect(internalSignal, targetConnectId, allowUsbEphemeralFallback = false
|
|
2211
|
-
|
|
2212
|
-
|
|
2213
|
-
|
|
2214
|
-
|
|
2215
|
-
|
|
2216
|
-
|
|
2217
|
-
|
|
2218
|
-
|
|
2219
|
-
)
|
|
2220
|
-
|
|
2221
|
-
|
|
2222
|
-
|
|
2223
|
-
|
|
2224
|
-
|
|
2225
|
-
preserveOperationId,
|
|
2226
|
-
context,
|
|
2227
|
-
internalSignal
|
|
2228
|
-
);
|
|
2229
|
-
}
|
|
2230
|
-
this._activeConnectionType = "ble";
|
|
2231
|
-
const knownBle = context?.knownConnections?.find(
|
|
2232
|
-
(connection) => connection.transport === "ble"
|
|
2233
|
-
);
|
|
2234
|
-
if (knownBle?.transport === "ble") {
|
|
2235
|
-
return this._connectDeviceOrThrow(knownBle.connectId, preserveOperationId, internalSignal);
|
|
2236
|
-
}
|
|
2237
|
-
if (targetConnectId && context?.knownConnections === void 0) {
|
|
2238
|
-
throw createHwkError({
|
|
2239
|
-
code: HardwareErrorCode3.DeviceNotFound,
|
|
2240
|
-
message: "Ledger connection metadata is required before starting Bluetooth binding"
|
|
2241
|
-
});
|
|
1623
|
+
async _doConnect(internalSignal, targetConnectId, allowUsbEphemeralFallback = false) {
|
|
1624
|
+
if (isLedgerBleConnectionType(this.connector.connectionType) && targetConnectId) {
|
|
1625
|
+
try {
|
|
1626
|
+
return await this._connectDeviceOrThrow(targetConnectId);
|
|
1627
|
+
} catch (err) {
|
|
1628
|
+
if (!isDeviceLockedError(err) && !isDeviceNotAdvertisingError(err) && !isDeviceDisconnectedError(err)) {
|
|
1629
|
+
throw err;
|
|
1630
|
+
}
|
|
1631
|
+
this._discoveredDevices.delete(targetConnectId);
|
|
1632
|
+
if (isDeviceDisconnectedError(err)) {
|
|
1633
|
+
try {
|
|
1634
|
+
this.connector.reset?.();
|
|
1635
|
+
} catch {
|
|
1636
|
+
}
|
|
1637
|
+
}
|
|
2242
1638
|
}
|
|
2243
|
-
const bleDevices = await this._searchDevices({ transportType: "ble" }, internalSignal);
|
|
2244
|
-
return this._connectFirstOrSelect(
|
|
2245
|
-
bleDevices,
|
|
2246
|
-
void 0,
|
|
2247
|
-
allowUsbEphemeralFallback,
|
|
2248
|
-
preserveOperationId,
|
|
2249
|
-
context,
|
|
2250
|
-
internalSignal
|
|
2251
|
-
);
|
|
2252
|
-
}
|
|
2253
|
-
if (this._isBleConnection() && targetConnectId) {
|
|
2254
|
-
return this._connectDeviceOrThrow(targetConnectId, preserveOperationId, internalSignal);
|
|
2255
1639
|
}
|
|
2256
1640
|
let confirms = 0;
|
|
2257
1641
|
while (!internalSignal.aborted) {
|
|
@@ -2259,28 +1643,22 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
2259
1643
|
type: EConnectorInteraction.Searching,
|
|
2260
1644
|
payload: { sessionId: "" }
|
|
2261
1645
|
});
|
|
2262
|
-
let devices = await this.
|
|
2263
|
-
_LedgerAdapter._throwIfAborted(internalSignal);
|
|
1646
|
+
let devices = await this.searchDevices();
|
|
2264
1647
|
if (devices.length === 0) {
|
|
2265
1648
|
for (let i = 0; i < 3 && !internalSignal.aborted; i += 1) {
|
|
2266
1649
|
await new Promise((resolve) => {
|
|
2267
1650
|
setTimeout(resolve, DEVICE_CONNECT_RETRY_DELAY_MS);
|
|
2268
1651
|
});
|
|
2269
|
-
|
|
2270
|
-
devices = await this._searchDevices(void 0, internalSignal);
|
|
2271
|
-
_LedgerAdapter._throwIfAborted(internalSignal);
|
|
1652
|
+
devices = await this.searchDevices();
|
|
2272
1653
|
if (devices.length > 0) break;
|
|
2273
1654
|
}
|
|
2274
1655
|
}
|
|
2275
|
-
if (devices.length > 0
|
|
1656
|
+
if (devices.length > 0) {
|
|
2276
1657
|
try {
|
|
2277
1658
|
return await this._connectFirstOrSelect(
|
|
2278
1659
|
devices,
|
|
2279
1660
|
targetConnectId,
|
|
2280
|
-
allowUsbEphemeralFallback
|
|
2281
|
-
preserveOperationId,
|
|
2282
|
-
context,
|
|
2283
|
-
internalSignal
|
|
1661
|
+
allowUsbEphemeralFallback
|
|
2284
1662
|
);
|
|
2285
1663
|
} catch (err) {
|
|
2286
1664
|
if (!isDeviceLockedError(err) && !isDeviceNotAdvertisingError(err) && !isDeviceDisconnectedError(err)) {
|
|
@@ -2309,133 +1687,45 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
2309
1687
|
_LedgerAdapter._throwIfAborted(internalSignal);
|
|
2310
1688
|
throw new Error("_doConnect aborted");
|
|
2311
1689
|
}
|
|
2312
|
-
async _connectFirstOrSelect(devices, targetConnectId, allowUsbEphemeralFallback
|
|
2313
|
-
_LedgerAdapter._throwIfAborted(signal);
|
|
1690
|
+
async _connectFirstOrSelect(devices, targetConnectId, allowUsbEphemeralFallback = false) {
|
|
2314
1691
|
if (targetConnectId) {
|
|
2315
1692
|
const target = devices.find(
|
|
2316
1693
|
(d) => d.connectId === targetConnectId || d.deviceId === targetConnectId
|
|
2317
1694
|
);
|
|
2318
1695
|
if (target) {
|
|
2319
|
-
return this._connectDeviceOrThrow(target.connectId
|
|
1696
|
+
return this._connectDeviceOrThrow(target.connectId);
|
|
2320
1697
|
}
|
|
2321
|
-
if (!this.
|
|
1698
|
+
if (!isLedgerBleConnectionType(this.connector.connectionType) && devices.length === 1 && allowUsbEphemeralFallback) {
|
|
2322
1699
|
debugLog(
|
|
2323
1700
|
`[LedgerAdapter] target ${targetConnectId} not in fresh enumeration; accepting sole USB device ${devices[0].connectId} for fingerprint-verified recovery`
|
|
2324
1701
|
);
|
|
2325
|
-
return this._connectDeviceOrThrow(devices[0].connectId
|
|
1702
|
+
return this._connectDeviceOrThrow(devices[0].connectId);
|
|
2326
1703
|
}
|
|
2327
|
-
|
|
2328
|
-
|
|
2329
|
-
|
|
2330
|
-
|
|
2331
|
-
|
|
2332
|
-
err._tag = ERROR_TAG.DeviceNotAdvertising;
|
|
2333
|
-
}
|
|
2334
|
-
throw err;
|
|
1704
|
+
const err = Object.assign(new Error(`Target Ledger unavailable: ${targetConnectId}`), {
|
|
1705
|
+
code: HardwareErrorCode3.DeviceNotFound
|
|
1706
|
+
});
|
|
1707
|
+
if (isLedgerBleConnectionType(this.connector.connectionType)) {
|
|
1708
|
+
err._tag = ERROR_TAG.DeviceNotAdvertising;
|
|
2335
1709
|
}
|
|
1710
|
+
throw err;
|
|
2336
1711
|
}
|
|
2337
|
-
|
|
2338
|
-
if (requiresBleSelection && !allowUsbEphemeralFallback) {
|
|
1712
|
+
if (isLedgerBleConnectionType(this.connector.connectionType)) {
|
|
2339
1713
|
throw Object.assign(new Error("Ledger BLE connectId is required."), {
|
|
2340
1714
|
code: HardwareErrorCode3.DeviceNotFound
|
|
2341
1715
|
});
|
|
2342
1716
|
}
|
|
2343
|
-
if (
|
|
2344
|
-
|
|
2345
|
-
if (context) context.bindingSessionId = bindingSessionId;
|
|
2346
|
-
const allowUsbFallback = context?.bindingReason !== "manual-rebind" && Boolean(this.connector.availableTransports?.includes("usb"));
|
|
2347
|
-
const knownUsb = context?.knownConnections?.find(
|
|
2348
|
-
(connection) => connection.transport === "usb"
|
|
2349
|
-
);
|
|
2350
|
-
const usbConnectId = knownUsb?.transport === "usb" ? knownUsb.connectId : targetConnectId;
|
|
2351
|
-
const { device, requestId } = await requestBleDeviceSelection({
|
|
2352
|
-
emitter: this.emitter,
|
|
2353
|
-
registry: this._uiRegistry,
|
|
2354
|
-
signal,
|
|
2355
|
-
allowUsbFallback,
|
|
2356
|
-
scan: async () => {
|
|
2357
|
-
if (allowUsbFallback) {
|
|
2358
|
-
const usbDevices = await this._searchDevices({ transportType: "usb" }, signal);
|
|
2359
|
-
const candidate = usbDevices.find(
|
|
2360
|
-
(device2) => device2.connectionType === "usb" && device2.connectId === usbConnectId
|
|
2361
|
-
) ?? (usbDevices.length === 1 && usbDevices[0].connectionType === "usb" ? usbDevices[0] : void 0);
|
|
2362
|
-
if (candidate) return [candidate];
|
|
2363
|
-
}
|
|
2364
|
-
return (await this._searchDevices({ transportType: "ble", waitForAllTransports: true }, signal)).filter((device2) => !context?.rejectedConnectIds?.has(device2.connectId));
|
|
2365
|
-
},
|
|
2366
|
-
request: {
|
|
2367
|
-
devices: devices.filter((device2) => !context?.rejectedConnectIds?.has(device2.connectId)),
|
|
2368
|
-
bindingSessionId,
|
|
2369
|
-
rejectedConnectId: context?.rejectedConnectId,
|
|
2370
|
-
context: {
|
|
2371
|
-
kind: "bind-connection",
|
|
2372
|
-
transport: "ble",
|
|
2373
|
-
reason: context?.bindingReason ?? (targetConnectId ? "known-connection-unavailable" : "missing-binding")
|
|
2374
|
-
},
|
|
2375
|
-
extra: context?.extra
|
|
2376
|
-
}
|
|
2377
|
-
});
|
|
2378
|
-
if (device.connectionType === "usb") {
|
|
2379
|
-
this._activeConnectionType = "usb";
|
|
2380
|
-
if (context) context.selectedConnection = void 0;
|
|
2381
|
-
if (preserveOperationId) this._pendingOperationBindings.delete(preserveOperationId);
|
|
2382
|
-
this._bindingSelectionRequestId = void 0;
|
|
2383
|
-
this.emitter.emit(UI_REQUEST.DEVICE_BINDING_STATUS, {
|
|
2384
|
-
type: UI_REQUEST.DEVICE_BINDING_STATUS,
|
|
2385
|
-
payload: { selectionRequestId: requestId, status: "cancelled" }
|
|
2386
|
-
});
|
|
2387
|
-
return this._connectDeviceOrThrow(device.connectId, preserveOperationId, signal);
|
|
2388
|
-
}
|
|
2389
|
-
if (context) context.selectedConnection = { connectId: device.connectId, requestId };
|
|
2390
|
-
this._bindingSelectionRequestId = requestId;
|
|
2391
|
-
return this._connectDeviceOrThrow(device.connectId, preserveOperationId, signal);
|
|
2392
|
-
}
|
|
2393
|
-
if (devices.length > 0 && (devices.length > 1 || requiresBleSelection)) {
|
|
2394
|
-
if (context?.allowDeviceSelection === false || !this.emitter.listenerCount(UI_REQUEST.REQUEST_SELECT_DEVICE)) {
|
|
2395
|
-
throw createHwkError({
|
|
2396
|
-
code: HardwareErrorCode3.DeviceNotFound,
|
|
2397
|
-
message: "Select a Ledger device before continuing"
|
|
2398
|
-
});
|
|
2399
|
-
}
|
|
2400
|
-
const requestId = this._uiRegistry.createRequestId();
|
|
2401
|
-
const waitPromise = this._uiRegistry.wait(
|
|
2402
|
-
UI_REQUEST.REQUEST_SELECT_DEVICE,
|
|
2403
|
-
{ requestId }
|
|
2404
|
-
);
|
|
2405
|
-
this.emitter.emit(UI_REQUEST.REQUEST_SELECT_DEVICE, {
|
|
2406
|
-
type: UI_REQUEST.REQUEST_SELECT_DEVICE,
|
|
2407
|
-
payload: {
|
|
2408
|
-
devices,
|
|
2409
|
-
requestId,
|
|
2410
|
-
context: requiresBleSelection ? {
|
|
2411
|
-
kind: "bind-connection",
|
|
2412
|
-
transport: "ble",
|
|
2413
|
-
reason: targetConnectId ? "known-connection-unavailable" : "missing-binding"
|
|
2414
|
-
} : { kind: "select-device", transport: "usb", reason: "multiple-candidates" },
|
|
2415
|
-
extra: context?.extra
|
|
2416
|
-
}
|
|
2417
|
-
});
|
|
2418
|
-
const { sdkConnectId } = await (signal ? this._abortable(signal, waitPromise) : waitPromise);
|
|
2419
|
-
if (signal) _LedgerAdapter._throwIfAborted(signal);
|
|
2420
|
-
const selected = devices.find((device) => device.connectId === sdkConnectId);
|
|
2421
|
-
if (!selected) {
|
|
2422
|
-
throw Object.assign(new Error("Selected Ledger is no longer available"), {
|
|
2423
|
-
code: HardwareErrorCode3.DeviceNotFound
|
|
2424
|
-
});
|
|
2425
|
-
}
|
|
2426
|
-
if (context && requiresBleSelection)
|
|
2427
|
-
context.selectedConnection = { connectId: selected.connectId, requestId };
|
|
2428
|
-
return this._connectDeviceOrThrow(selected.connectId, preserveOperationId, signal);
|
|
1717
|
+
if (devices.length > 1) {
|
|
1718
|
+
throw createMultipleUsbLedgerDevicesError();
|
|
2429
1719
|
}
|
|
2430
1720
|
if (devices.length !== 1) {
|
|
2431
1721
|
throw Object.assign(new Error("Ledger device not found."), {
|
|
2432
1722
|
code: HardwareErrorCode3.DeviceNotFound
|
|
2433
1723
|
});
|
|
2434
1724
|
}
|
|
2435
|
-
return this._connectDeviceOrThrow(devices[0].connectId
|
|
1725
|
+
return this._connectDeviceOrThrow(devices[0].connectId);
|
|
2436
1726
|
}
|
|
2437
|
-
async _connectDeviceOrThrow(chosenConnectId
|
|
2438
|
-
const result = await this.
|
|
1727
|
+
async _connectDeviceOrThrow(chosenConnectId) {
|
|
1728
|
+
const result = await this.connectDevice(chosenConnectId);
|
|
2439
1729
|
if (!result.success) {
|
|
2440
1730
|
const payload = result.payload;
|
|
2441
1731
|
const rethrow = Object.assign(new Error(payload.error), {
|
|
@@ -2446,7 +1736,7 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
2446
1736
|
}
|
|
2447
1737
|
throw rethrow;
|
|
2448
1738
|
}
|
|
2449
|
-
return
|
|
1739
|
+
return chosenConnectId;
|
|
2450
1740
|
}
|
|
2451
1741
|
/**
|
|
2452
1742
|
* Call the connector with automatic session resolution and disconnect retry.
|
|
@@ -2476,122 +1766,26 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
2476
1766
|
* lives in one place.
|
|
2477
1767
|
*/
|
|
2478
1768
|
async _callConnector(sessionId, method, params, signal) {
|
|
2479
|
-
this.
|
|
2480
|
-
if (signal?.aborted) throw this._abortReason(signal);
|
|
2481
|
-
const releaseOperation = this._retainConnectorOperation(`call:${sessionId}`);
|
|
2482
|
-
let promise;
|
|
2483
|
-
try {
|
|
2484
|
-
promise = this.connector.call(sessionId, method, params).finally(releaseOperation);
|
|
2485
|
-
} catch (error) {
|
|
2486
|
-
releaseOperation();
|
|
2487
|
-
throw error;
|
|
2488
|
-
}
|
|
1769
|
+
const promise = this.connector.call(sessionId, method, params);
|
|
2489
1770
|
const result = signal ? await this._abortable(signal, promise) : await promise;
|
|
2490
1771
|
return this._unwrapConnectorResult(result);
|
|
2491
1772
|
}
|
|
2492
|
-
_assertConnectorReady(method) {
|
|
2493
|
-
if (this._resetPromise || this._pendingConnectorTeardowns > 0 || this._unsettledConnectorOperations.size > 0) {
|
|
2494
|
-
throw _LedgerAdapter._createDeviceBusyError(method);
|
|
2495
|
-
}
|
|
2496
|
-
}
|
|
2497
|
-
_runConnectorTeardown(task) {
|
|
2498
|
-
const previous = this._connectorTeardownTail;
|
|
2499
|
-
let releaseTail = () => void 0;
|
|
2500
|
-
this._connectorTeardownTail = new Promise((resolve) => {
|
|
2501
|
-
releaseTail = resolve;
|
|
2502
|
-
});
|
|
2503
|
-
this._pendingConnectorTeardowns += 1;
|
|
2504
|
-
return (async () => {
|
|
2505
|
-
try {
|
|
2506
|
-
await previous;
|
|
2507
|
-
await this._waitForConnectorOperationsToDrain();
|
|
2508
|
-
await task();
|
|
2509
|
-
} finally {
|
|
2510
|
-
this._pendingConnectorTeardowns -= 1;
|
|
2511
|
-
releaseTail();
|
|
2512
|
-
}
|
|
2513
|
-
})();
|
|
2514
|
-
}
|
|
2515
|
-
_waitForConnectorOperationsToDrain() {
|
|
2516
|
-
if (this._unsettledConnectorOperations.size === 0) {
|
|
2517
|
-
return Promise.resolve();
|
|
2518
|
-
}
|
|
2519
|
-
return new Promise((resolve) => {
|
|
2520
|
-
this._connectorIdleWaiters.add(resolve);
|
|
2521
|
-
});
|
|
2522
|
-
}
|
|
2523
|
-
_retainConnectorOperation(key) {
|
|
2524
|
-
this._unsettledConnectorOperations.set(
|
|
2525
|
-
key,
|
|
2526
|
-
(this._unsettledConnectorOperations.get(key) ?? 0) + 1
|
|
2527
|
-
);
|
|
2528
|
-
let released = false;
|
|
2529
|
-
return () => {
|
|
2530
|
-
if (released) return;
|
|
2531
|
-
released = true;
|
|
2532
|
-
const remaining = (this._unsettledConnectorOperations.get(key) ?? 1) - 1;
|
|
2533
|
-
if (remaining > 0) {
|
|
2534
|
-
this._unsettledConnectorOperations.set(key, remaining);
|
|
2535
|
-
} else {
|
|
2536
|
-
this._unsettledConnectorOperations.delete(key);
|
|
2537
|
-
}
|
|
2538
|
-
if (this._unsettledConnectorOperations.size === 0) {
|
|
2539
|
-
for (const resolve of this._connectorIdleWaiters) resolve();
|
|
2540
|
-
this._connectorIdleWaiters.clear();
|
|
2541
|
-
}
|
|
2542
|
-
};
|
|
2543
|
-
}
|
|
2544
1773
|
async connectorCall(connectId, method, params, fingerprint, permissionDeviceId, commonParams, installContext) {
|
|
2545
|
-
|
|
2546
|
-
|
|
2547
|
-
throw createHwkError({
|
|
2548
|
-
code: HardwareErrorCode3.InvalidParams,
|
|
2549
|
-
message: "Conflicting Ledger operation ids",
|
|
2550
|
-
params: {
|
|
2551
|
-
positionalOperationId,
|
|
2552
|
-
commonOperationId: commonParams.operationId
|
|
2553
|
-
}
|
|
2554
|
-
});
|
|
2555
|
-
}
|
|
2556
|
-
const operationId = commonParams?.operationId ?? positionalOperationId;
|
|
2557
|
-
const operation = operationId ? this._operations.resolve(operationId) : void 0;
|
|
2558
|
-
const releaseOperationRetention = operationId ? this._operations.retain(operationId) : void 0;
|
|
2559
|
-
const effectiveConnectId = operation?.connectId ?? connectId;
|
|
2560
|
-
debugLog("[LedgerAdapter][REQ]", {
|
|
2561
|
-
method,
|
|
2562
|
-
connectId: effectiveConnectId || "(empty)",
|
|
2563
|
-
params
|
|
2564
|
-
});
|
|
2565
|
-
const queueKey = ledgerQueueKey({ operationId, connectId: effectiveConnectId });
|
|
1774
|
+
debugLog("[LedgerAdapter][REQ]", { method, connectId: connectId || "(empty)", params });
|
|
1775
|
+
const queueKey = connectId || "__ledger_default__";
|
|
2566
1776
|
try {
|
|
2567
1777
|
const result = await this._jobQueue.enqueue(
|
|
2568
1778
|
queueKey,
|
|
2569
|
-
async (signal) =>
|
|
2570
|
-
|
|
2571
|
-
|
|
2572
|
-
|
|
2573
|
-
|
|
2574
|
-
|
|
2575
|
-
|
|
2576
|
-
|
|
2577
|
-
|
|
2578
|
-
|
|
2579
|
-
commonParams,
|
|
2580
|
-
installContext ?? {},
|
|
2581
|
-
operationId
|
|
2582
|
-
);
|
|
2583
|
-
} catch (error) {
|
|
2584
|
-
this._finishBleBinding(signal.aborted ? "cancelled" : "failed");
|
|
2585
|
-
throw error;
|
|
2586
|
-
} finally {
|
|
2587
|
-
if (operationId) {
|
|
2588
|
-
this._activeOperationJobs.delete(operationId);
|
|
2589
|
-
if (this._pendingOperationDisconnects.delete(operationId)) {
|
|
2590
|
-
await this._releaseLostOperationConnection(operationId);
|
|
2591
|
-
}
|
|
2592
|
-
}
|
|
2593
|
-
}
|
|
2594
|
-
},
|
|
1779
|
+
async (signal) => this._runConnectorCall(
|
|
1780
|
+
connectId,
|
|
1781
|
+
method,
|
|
1782
|
+
params,
|
|
1783
|
+
signal,
|
|
1784
|
+
fingerprint,
|
|
1785
|
+
permissionDeviceId,
|
|
1786
|
+
commonParams,
|
|
1787
|
+
installContext
|
|
1788
|
+
),
|
|
2595
1789
|
{
|
|
2596
1790
|
label: method,
|
|
2597
1791
|
rejectIfBusy: true,
|
|
@@ -2612,22 +1806,15 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
2612
1806
|
}
|
|
2613
1807
|
});
|
|
2614
1808
|
throw err;
|
|
2615
|
-
} finally {
|
|
2616
|
-
releaseOperationRetention?.();
|
|
2617
1809
|
}
|
|
2618
1810
|
}
|
|
2619
1811
|
/**
|
|
2620
1812
|
* Race a promise against an abort signal. On abort, rejects with
|
|
2621
1813
|
* signal.reason → instance _lastCancelReason → generic Error('Aborted').
|
|
2622
1814
|
*/
|
|
2623
|
-
/** Hermes/RN polyfills don't always populate signal.reason; fall back. */
|
|
2624
|
-
_abortReason(signal) {
|
|
2625
|
-
return signal.reason ?? this._lastCancelReason ?? new Error("Aborted");
|
|
2626
|
-
}
|
|
2627
1815
|
_abortable(signal, promise) {
|
|
2628
|
-
const getAbortReason = () => this.
|
|
1816
|
+
const getAbortReason = () => signal.reason ?? this._lastCancelReason ?? new Error("Aborted");
|
|
2629
1817
|
if (signal.aborted) {
|
|
2630
|
-
void promise.catch(() => void 0);
|
|
2631
1818
|
return Promise.reject(getAbortReason());
|
|
2632
1819
|
}
|
|
2633
1820
|
return new Promise((resolve, reject) => {
|
|
@@ -2654,15 +1841,13 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
2654
1841
|
}
|
|
2655
1842
|
}
|
|
2656
1843
|
/** Actual work done under the job queue — connection, fingerprint, call, and recovery. */
|
|
2657
|
-
async _runConnectorCall(connectId, method, params, signal, fingerprint, permissionDeviceId, commonParams, installContext
|
|
1844
|
+
async _runConnectorCall(connectId, method, params, signal, fingerprint, permissionDeviceId, commonParams, installContext) {
|
|
2658
1845
|
_LedgerAdapter._throwIfAborted(signal);
|
|
2659
|
-
|
|
2660
|
-
|
|
2661
|
-
|
|
2662
|
-
|
|
2663
|
-
|
|
2664
|
-
);
|
|
2665
|
-
}
|
|
1846
|
+
await this._ensureDevicePermission(
|
|
1847
|
+
connectId,
|
|
1848
|
+
permissionDeviceId ?? fingerprint?.deviceId,
|
|
1849
|
+
signal
|
|
1850
|
+
);
|
|
2666
1851
|
_LedgerAdapter._throwIfAborted(signal);
|
|
2667
1852
|
let effectiveParams = params;
|
|
2668
1853
|
if (method === "btcGetPublicKey") {
|
|
@@ -2676,132 +1861,36 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
2676
1861
|
effectiveParams = gatedParams;
|
|
2677
1862
|
}
|
|
2678
1863
|
const allowUsbEphemeralFallback = !!fingerprint?.deviceId && !fingerprint.skipFingerprint;
|
|
2679
|
-
|
|
2680
|
-
|
|
2681
|
-
const knownTransport = this._isBleConnection() ? "ble" : "usb";
|
|
2682
|
-
const hintedConnectId = commonParams?.knownConnections?.find(
|
|
2683
|
-
(connection) => connection.transport === knownTransport
|
|
2684
|
-
);
|
|
2685
|
-
const inputConnectId = hintedConnectId && hintedConnectId.transport !== "qr" ? hintedConnectId.connectId : connectId;
|
|
2686
|
-
const preferredConnectId = fingerprint && !fingerprint.skipFingerprint && verifiedBleTarget?.chain === fingerprint.chain && verifiedBleTarget.fingerprint === fingerprint.deviceId ? verifiedBleTarget.connectId : inputConnectId;
|
|
2687
|
-
const connectionAttempt = { ...commonParams };
|
|
2688
|
-
const bundleConnection = installContext?.connection;
|
|
2689
|
-
if (bundleConnection && this._sessions.get(bundleConnection.connectId) !== bundleConnection.sessionId) {
|
|
2690
|
-
throw createHwkError({
|
|
2691
|
-
code: HardwareErrorCode3.DeviceDisconnected,
|
|
2692
|
-
message: "Ledger all-network connection ended"
|
|
2693
|
-
});
|
|
2694
|
-
}
|
|
2695
|
-
let resolvedConnectId = operationId ? this._operations.resolve(operationId).connectId : bundleConnection?.connectId ?? await this.ensureConnected(
|
|
2696
|
-
preferredConnectId,
|
|
1864
|
+
const resolvedConnectId = await this.ensureConnected(
|
|
1865
|
+
connectId,
|
|
2697
1866
|
signal,
|
|
2698
|
-
allowUsbEphemeralFallback
|
|
2699
|
-
void 0,
|
|
2700
|
-
connectionAttempt
|
|
1867
|
+
allowUsbEphemeralFallback
|
|
2701
1868
|
);
|
|
2702
|
-
|
|
2703
|
-
if (sessionId && installContext && !installContext.connection) {
|
|
2704
|
-
installContext.connection = { connectId: resolvedConnectId, sessionId };
|
|
2705
|
-
}
|
|
1869
|
+
const sessionId = this._sessions.get(resolvedConnectId);
|
|
2706
1870
|
if (!sessionId) {
|
|
2707
|
-
if (operationId) {
|
|
2708
|
-
this._operations.end(operationId, "disconnect");
|
|
2709
|
-
throw createHwkError({
|
|
2710
|
-
code: HardwareErrorCode3.OperationEnded,
|
|
2711
|
-
message: "Ledger operation connection is no longer active",
|
|
2712
|
-
params: { operationId, reason: "disconnect" }
|
|
2713
|
-
});
|
|
2714
|
-
}
|
|
2715
1871
|
throw Object.assign(new Error("Auto-connect succeeded but no session found"), {
|
|
2716
1872
|
_tag: ERROR_TAG.DeviceSessionNotFound
|
|
2717
1873
|
});
|
|
2718
1874
|
}
|
|
2719
1875
|
try {
|
|
2720
1876
|
if (fingerprint && !fingerprint.skipFingerprint && fingerprint.deviceId) {
|
|
2721
|
-
|
|
2722
|
-
|
|
2723
|
-
|
|
2724
|
-
|
|
2725
|
-
|
|
2726
|
-
|
|
2727
|
-
|
|
2728
|
-
)
|
|
2729
|
-
);
|
|
2730
|
-
if (fp.success) break;
|
|
2731
|
-
const binding = operationId ? this._pendingOperationBindings.get(operationId) : connectionAttempt;
|
|
2732
|
-
if (!this._isBleConnection() || binding?.selectedConnection?.connectId !== resolvedConnectId) {
|
|
2733
|
-
throw Object.assign(new Error(formatDeviceMismatchError(fp.expected, fp.actual)), {
|
|
2734
|
-
code: HardwareErrorCode3.DeviceMismatch
|
|
2735
|
-
});
|
|
2736
|
-
}
|
|
2737
|
-
binding.rejectedConnectIds ?? (binding.rejectedConnectIds = /* @__PURE__ */ new Set());
|
|
2738
|
-
binding.rejectedConnectIds.add(resolvedConnectId);
|
|
2739
|
-
binding.rejectedConnectId = resolvedConnectId;
|
|
2740
|
-
this._sessions.delete(resolvedConnectId);
|
|
2741
|
-
await this.connector.disconnect(sessionId);
|
|
2742
|
-
if (operationId) this._pendingOperationDisconnects.delete(operationId);
|
|
2743
|
-
_LedgerAdapter._throwIfAborted(signal);
|
|
2744
|
-
resolvedConnectId = await this._connectFirstOrSelect(
|
|
2745
|
-
[],
|
|
2746
|
-
void 0,
|
|
2747
|
-
true,
|
|
2748
|
-
operationId,
|
|
2749
|
-
binding,
|
|
2750
|
-
signal
|
|
2751
|
-
);
|
|
2752
|
-
const selectedSession = this._sessions.get(resolvedConnectId);
|
|
2753
|
-
const selectedDevice = this._discoveredDevices.get(resolvedConnectId);
|
|
2754
|
-
if (!selectedSession || !selectedDevice)
|
|
2755
|
-
throw createHwkError({
|
|
2756
|
-
code: HardwareErrorCode3.DeviceDisconnected,
|
|
2757
|
-
message: "Selected Ledger connection ended"
|
|
2758
|
-
});
|
|
2759
|
-
sessionId = selectedSession;
|
|
2760
|
-
if (operationId)
|
|
2761
|
-
this._operations.rebind(operationId, {
|
|
2762
|
-
connectId: resolvedConnectId,
|
|
2763
|
-
device: selectedDevice,
|
|
2764
|
-
connectionKeys: [sessionId]
|
|
2765
|
-
});
|
|
2766
|
-
if (installContext)
|
|
2767
|
-
installContext.connection = { connectId: resolvedConnectId, sessionId };
|
|
2768
|
-
}
|
|
2769
|
-
await this._publishVerifiedBleBinding(
|
|
2770
|
-
resolvedConnectId,
|
|
2771
|
-
fingerprint.chain,
|
|
2772
|
-
fingerprint.deviceId,
|
|
2773
|
-
connectionAttempt,
|
|
2774
|
-
operationId,
|
|
2775
|
-
signal
|
|
1877
|
+
const fp = await this._abortable(
|
|
1878
|
+
signal,
|
|
1879
|
+
this._verifyDeviceFingerprintWithSession(
|
|
1880
|
+
sessionId,
|
|
1881
|
+
fingerprint.deviceId,
|
|
1882
|
+
fingerprint.chain
|
|
1883
|
+
)
|
|
2776
1884
|
);
|
|
2777
|
-
if (!
|
|
2778
|
-
|
|
2779
|
-
|
|
2780
|
-
chain: fingerprint.chain,
|
|
2781
|
-
fingerprint: fingerprint.deviceId
|
|
1885
|
+
if (!fp.success) {
|
|
1886
|
+
throw Object.assign(new Error(formatDeviceMismatchError(fp.expected, fp.actual)), {
|
|
1887
|
+
code: HardwareErrorCode3.DeviceMismatch
|
|
2782
1888
|
});
|
|
2783
1889
|
}
|
|
2784
1890
|
}
|
|
2785
|
-
businessCallStarted = true;
|
|
2786
1891
|
return await this._callConnector(sessionId, method, effectiveParams, signal);
|
|
2787
1892
|
} catch (err) {
|
|
2788
1893
|
if (signal.aborted) throw err;
|
|
2789
|
-
if (isDeviceDisconnectedError(err) || isDeviceNotAdvertisingError(err) || isTimeoutError(err) || isConnectionLevelError(err)) {
|
|
2790
|
-
this._discoveredDevices.delete(resolvedConnectId);
|
|
2791
|
-
if (operationId) await this._releaseLostOperationConnection(operationId);
|
|
2792
|
-
else {
|
|
2793
|
-
this._sessions.delete(resolvedConnectId);
|
|
2794
|
-
await this.connector.disconnect(sessionId).catch(() => void 0);
|
|
2795
|
-
}
|
|
2796
|
-
const ambiguous = businessCallStarted && !canReplayHardwareMethodAfterTransportFailure(method);
|
|
2797
|
-
const interactionParams = operationId ? { operationId, reason: "disconnect" } : void 0;
|
|
2798
|
-
throw createHwkError({
|
|
2799
|
-
code: operationId ? HardwareErrorCode3.OperationEnded : mapLedgerError(err).code,
|
|
2800
|
-
message: "Ledger operation connection was lost; start a new operation",
|
|
2801
|
-
params: ambiguous ? operationMayHaveCompletedParams(method, { operationId }) : interactionParams,
|
|
2802
|
-
recovery: ambiguous ? { scope: "unknown" } : void 0
|
|
2803
|
-
});
|
|
2804
|
-
}
|
|
2805
1894
|
const errObj = err;
|
|
2806
1895
|
debugLog("[LedgerAdapter] connectorCall error:", method, {
|
|
2807
1896
|
message: errObj?.message,
|
|
@@ -2813,65 +1902,6 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
2813
1902
|
isNotAdvertising: isDeviceNotAdvertisingError(err),
|
|
2814
1903
|
isStuckApp: isStuckAppStateError(err)
|
|
2815
1904
|
});
|
|
2816
|
-
if ((isDeviceLockedError(err) || errObj?.code === HardwareErrorCode3.DeviceLocked) && lockedRetryBudget > 0) {
|
|
2817
|
-
await this._waitForDeviceConnect(signal);
|
|
2818
|
-
if (this._sessions.get(resolvedConnectId) !== sessionId) {
|
|
2819
|
-
throw createHwkError({
|
|
2820
|
-
code: operationId ? HardwareErrorCode3.OperationEnded : HardwareErrorCode3.DeviceDisconnected,
|
|
2821
|
-
message: "Ledger connection ended while waiting for unlock"
|
|
2822
|
-
});
|
|
2823
|
-
}
|
|
2824
|
-
return this._runConnectorCall(
|
|
2825
|
-
resolvedConnectId,
|
|
2826
|
-
method,
|
|
2827
|
-
effectiveParams,
|
|
2828
|
-
signal,
|
|
2829
|
-
fingerprint,
|
|
2830
|
-
permissionDeviceId,
|
|
2831
|
-
commonParams,
|
|
2832
|
-
installContext,
|
|
2833
|
-
operationId,
|
|
2834
|
-
lockedRetryBudget - 1
|
|
2835
|
-
);
|
|
2836
|
-
}
|
|
2837
|
-
if (businessCallStarted && isStuckAppStateError(err)) {
|
|
2838
|
-
await this._sleepAbortable(_LedgerAdapter.STUCK_APP_RETRY_DELAY_MS, signal);
|
|
2839
|
-
if (this._sessions.get(resolvedConnectId) !== sessionId) {
|
|
2840
|
-
throw createHwkError({
|
|
2841
|
-
code: operationId ? HardwareErrorCode3.OperationEnded : HardwareErrorCode3.DeviceDisconnected,
|
|
2842
|
-
message: "Ledger connection ended during the app transition"
|
|
2843
|
-
});
|
|
2844
|
-
}
|
|
2845
|
-
try {
|
|
2846
|
-
return await this._callConnector(sessionId, method, effectiveParams, signal);
|
|
2847
|
-
} catch (retryErr) {
|
|
2848
|
-
if (isStuckAppStateError(retryErr)) throw err;
|
|
2849
|
-
if (isDeviceDisconnectedError(retryErr) || isDeviceNotAdvertisingError(retryErr) || isTimeoutError(retryErr) || isConnectionLevelError(retryErr)) {
|
|
2850
|
-
if (operationId) await this._releaseLostOperationConnection(operationId);
|
|
2851
|
-
else {
|
|
2852
|
-
this._sessions.delete(resolvedConnectId);
|
|
2853
|
-
this._discoveredDevices.delete(resolvedConnectId);
|
|
2854
|
-
await this.connector.disconnect(sessionId).catch(() => void 0);
|
|
2855
|
-
}
|
|
2856
|
-
throw createHwkError({
|
|
2857
|
-
code: operationId ? HardwareErrorCode3.OperationEnded : mapLedgerError(retryErr).code,
|
|
2858
|
-
message: `Ledger ${method} may have completed before the connection was lost`,
|
|
2859
|
-
params: !canReplayHardwareMethodAfterTransportFailure(method) ? operationMayHaveCompletedParams(method, {
|
|
2860
|
-
operationId,
|
|
2861
|
-
reason: "disconnect"
|
|
2862
|
-
}) : { operationId, reason: "disconnect" },
|
|
2863
|
-
recovery: !canReplayHardwareMethodAfterTransportFailure(method) ? { scope: "unknown" } : void 0
|
|
2864
|
-
});
|
|
2865
|
-
}
|
|
2866
|
-
throw retryErr;
|
|
2867
|
-
}
|
|
2868
|
-
}
|
|
2869
|
-
if (!operationId && err?.code === HardwareErrorCode3.DeviceMismatch) {
|
|
2870
|
-
this._sessions.delete(resolvedConnectId);
|
|
2871
|
-
this._discoveredDevices.delete(resolvedConnectId);
|
|
2872
|
-
await this.connector.disconnect(sessionId).catch(() => void 0);
|
|
2873
|
-
throw err;
|
|
2874
|
-
}
|
|
2875
1905
|
const autoInstallApp = commonParams?.autoInstallApp ?? this._defaultAutoInstallApp;
|
|
2876
1906
|
const isAppMissing = isAppNotInstalledError(err) || err?.code === HardwareErrorCode3.AppNotInstalled;
|
|
2877
1907
|
if (autoInstallApp && isAppMissing) {
|
|
@@ -2927,14 +1957,157 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
2927
1957
|
fingerprint,
|
|
2928
1958
|
permissionDeviceId,
|
|
2929
1959
|
commonParams,
|
|
2930
|
-
installContext
|
|
2931
|
-
|
|
1960
|
+
installContext
|
|
1961
|
+
);
|
|
1962
|
+
}
|
|
1963
|
+
}
|
|
1964
|
+
if (isStuckAppStateError(err)) {
|
|
1965
|
+
try {
|
|
1966
|
+
this._sessions.delete(resolvedConnectId);
|
|
1967
|
+
this._discoveredDevices.delete(resolvedConnectId);
|
|
1968
|
+
this.connector.reset?.();
|
|
1969
|
+
} catch {
|
|
1970
|
+
}
|
|
1971
|
+
debugLog(
|
|
1972
|
+
"[LedgerAdapter] stuck-app retry: method=",
|
|
1973
|
+
method,
|
|
1974
|
+
"delayMs=",
|
|
1975
|
+
_LedgerAdapter.STUCK_APP_RETRY_DELAY_MS,
|
|
1976
|
+
"_tag=",
|
|
1977
|
+
err?._tag
|
|
1978
|
+
);
|
|
1979
|
+
try {
|
|
1980
|
+
const retryResult = await this._retryAfterStuckApp(
|
|
1981
|
+
resolvedConnectId,
|
|
1982
|
+
method,
|
|
1983
|
+
effectiveParams,
|
|
1984
|
+
signal,
|
|
1985
|
+
err,
|
|
1986
|
+
fingerprint
|
|
1987
|
+
);
|
|
1988
|
+
debugLog("[LedgerAdapter] stuck-app retry succeeded: method=", method);
|
|
1989
|
+
return retryResult;
|
|
1990
|
+
} catch (retryErr) {
|
|
1991
|
+
if (signal.aborted) throw retryErr;
|
|
1992
|
+
if (isStuckAppStateError(retryErr)) {
|
|
1993
|
+
debugLog("[LedgerAdapter] stuck-app retry exhausted (2nd 6901): method=", method);
|
|
1994
|
+
throw err;
|
|
1995
|
+
}
|
|
1996
|
+
debugLog(
|
|
1997
|
+
"[LedgerAdapter] stuck-app retry threw non-stuck error: method=",
|
|
1998
|
+
method,
|
|
1999
|
+
"retryErrTag=",
|
|
2000
|
+
retryErr?._tag
|
|
2932
2001
|
);
|
|
2002
|
+
throw retryErr;
|
|
2003
|
+
}
|
|
2004
|
+
}
|
|
2005
|
+
if (isDeviceLockedError(err) || isDeviceNotAdvertisingError(err) || isDeviceDisconnectedError(err)) {
|
|
2006
|
+
let lastErr = err;
|
|
2007
|
+
for (let attempt = 0; attempt < _LedgerAdapter.MAX_BUSINESS_RETRY_BUDGET; attempt += 1) {
|
|
2008
|
+
if (signal.aborted) throw lastErr;
|
|
2009
|
+
try {
|
|
2010
|
+
this._sessions.delete(resolvedConnectId);
|
|
2011
|
+
this._discoveredDevices.delete(resolvedConnectId);
|
|
2012
|
+
if (isDeviceDisconnectedError(lastErr)) {
|
|
2013
|
+
try {
|
|
2014
|
+
this.connector.reset?.();
|
|
2015
|
+
} catch {
|
|
2016
|
+
}
|
|
2017
|
+
}
|
|
2018
|
+
const reConnectId = await this.ensureConnected(
|
|
2019
|
+
resolvedConnectId,
|
|
2020
|
+
signal,
|
|
2021
|
+
allowUsbEphemeralFallback
|
|
2022
|
+
);
|
|
2023
|
+
const reSessionId = this._sessions.get(reConnectId);
|
|
2024
|
+
if (!reSessionId) throw lastErr;
|
|
2025
|
+
if (fingerprint && !fingerprint.skipFingerprint && fingerprint.deviceId) {
|
|
2026
|
+
const fp = await this._abortable(
|
|
2027
|
+
signal,
|
|
2028
|
+
this._verifyDeviceFingerprintWithSession(
|
|
2029
|
+
reSessionId,
|
|
2030
|
+
fingerprint.deviceId,
|
|
2031
|
+
fingerprint.chain
|
|
2032
|
+
)
|
|
2033
|
+
);
|
|
2034
|
+
if (!fp.success) {
|
|
2035
|
+
throw Object.assign(new Error(formatDeviceMismatchError(fp.expected, fp.actual)), {
|
|
2036
|
+
code: HardwareErrorCode3.DeviceMismatch
|
|
2037
|
+
});
|
|
2038
|
+
}
|
|
2039
|
+
}
|
|
2040
|
+
return await this._callConnector(reSessionId, method, effectiveParams, signal);
|
|
2041
|
+
} catch (retryErr) {
|
|
2042
|
+
if (signal.aborted) throw retryErr;
|
|
2043
|
+
lastErr = retryErr;
|
|
2044
|
+
const canRetry = attempt < _LedgerAdapter.MAX_BUSINESS_RETRY_BUDGET - 1 && (isDeviceLockedError(retryErr) || isDeviceNotAdvertisingError(retryErr) || isDeviceDisconnectedError(retryErr));
|
|
2045
|
+
if (!canRetry) {
|
|
2046
|
+
throw retryErr;
|
|
2047
|
+
}
|
|
2048
|
+
}
|
|
2933
2049
|
}
|
|
2050
|
+
throw lastErr;
|
|
2051
|
+
}
|
|
2052
|
+
if (isTimeoutError(err)) {
|
|
2053
|
+
debugLog("[LedgerAdapter] timeout, retrying with fresh connection...");
|
|
2054
|
+
this._discoveredDevices.delete(resolvedConnectId);
|
|
2055
|
+
return this._retryWithFreshConnection(
|
|
2056
|
+
resolvedConnectId,
|
|
2057
|
+
method,
|
|
2058
|
+
effectiveParams,
|
|
2059
|
+
signal,
|
|
2060
|
+
err,
|
|
2061
|
+
fingerprint
|
|
2062
|
+
);
|
|
2063
|
+
}
|
|
2064
|
+
if (isConnectionLevelError(err)) {
|
|
2065
|
+
debugLog("[LedgerAdapter] connection-level fail-closed reset");
|
|
2066
|
+
this._sessions.delete(resolvedConnectId);
|
|
2067
|
+
this._discoveredDevices.delete(resolvedConnectId);
|
|
2068
|
+
this.connector.reset();
|
|
2069
|
+
const codeNum = err?.code;
|
|
2070
|
+
throw Object.assign(err, {
|
|
2071
|
+
code: codeNum ?? HardwareErrorCode3.DeviceDisconnected
|
|
2072
|
+
});
|
|
2934
2073
|
}
|
|
2935
2074
|
throw err;
|
|
2936
2075
|
}
|
|
2937
2076
|
}
|
|
2077
|
+
/**
|
|
2078
|
+
* Stuck-app recovery: pause for the device's UI transition, then retry once.
|
|
2079
|
+
*
|
|
2080
|
+
* Caller has already cleared the session + reset connector. We wait so Stax
|
|
2081
|
+
* finishes its post-CloseApp animation, then go through ensureConnected +
|
|
2082
|
+
* fingerprint check + call exactly once. Caller decides what to do on a
|
|
2083
|
+
* second stuck-app hit.
|
|
2084
|
+
*/
|
|
2085
|
+
async _retryAfterStuckApp(resolvedConnectId, method, params, signal, originalErr, fingerprint) {
|
|
2086
|
+
await this._sleepAbortable(_LedgerAdapter.STUCK_APP_RETRY_DELAY_MS, signal);
|
|
2087
|
+
const retryConnectId = await this.ensureConnected(
|
|
2088
|
+
resolvedConnectId,
|
|
2089
|
+
signal,
|
|
2090
|
+
!!fingerprint?.deviceId && !fingerprint.skipFingerprint
|
|
2091
|
+
);
|
|
2092
|
+
const retrySessionId = this._sessions.get(retryConnectId);
|
|
2093
|
+
if (!retrySessionId) throw originalErr;
|
|
2094
|
+
if (fingerprint && !fingerprint.skipFingerprint && fingerprint.deviceId) {
|
|
2095
|
+
const fp = await this._abortable(
|
|
2096
|
+
signal,
|
|
2097
|
+
this._verifyDeviceFingerprintWithSession(
|
|
2098
|
+
retrySessionId,
|
|
2099
|
+
fingerprint.deviceId,
|
|
2100
|
+
fingerprint.chain
|
|
2101
|
+
)
|
|
2102
|
+
);
|
|
2103
|
+
if (!fp.success) {
|
|
2104
|
+
throw Object.assign(new Error(formatDeviceMismatchError(fp.expected, fp.actual)), {
|
|
2105
|
+
code: HardwareErrorCode3.DeviceMismatch
|
|
2106
|
+
});
|
|
2107
|
+
}
|
|
2108
|
+
}
|
|
2109
|
+
return this._callConnector(retrySessionId, method, params, signal);
|
|
2110
|
+
}
|
|
2938
2111
|
_sleepAbortable(ms, signal) {
|
|
2939
2112
|
return new Promise((resolve, reject) => {
|
|
2940
2113
|
if (signal.aborted) {
|
|
@@ -2952,6 +2125,88 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
2952
2125
|
signal.addEventListener("abort", onAbort, { once: true });
|
|
2953
2126
|
});
|
|
2954
2127
|
}
|
|
2128
|
+
/**
|
|
2129
|
+
* Clear stale session, reconnect, and retry the call.
|
|
2130
|
+
*
|
|
2131
|
+
* Timeout recovery starts with a full connector reset. After an APDU
|
|
2132
|
+
* timeout, DMK/transport state may still emit malformed responses; retrying
|
|
2133
|
+
* on the same DMK can poison the next chain switch.
|
|
2134
|
+
*/
|
|
2135
|
+
async _retryWithFreshConnection(targetConnectId, method, params, signal, originalErr, fingerprint) {
|
|
2136
|
+
this.connector.reset();
|
|
2137
|
+
this._sessions.clear();
|
|
2138
|
+
this._discoveredDevices.clear();
|
|
2139
|
+
this._connectingPromise = null;
|
|
2140
|
+
const allowUsbEphemeralFallback = !!fingerprint?.deviceId && !fingerprint.skipFingerprint;
|
|
2141
|
+
const retryConnectId = await this.ensureConnected(
|
|
2142
|
+
targetConnectId,
|
|
2143
|
+
signal,
|
|
2144
|
+
allowUsbEphemeralFallback
|
|
2145
|
+
);
|
|
2146
|
+
const retrySessionId = this._sessions.get(retryConnectId);
|
|
2147
|
+
if (!retrySessionId) {
|
|
2148
|
+
throw originalErr;
|
|
2149
|
+
}
|
|
2150
|
+
try {
|
|
2151
|
+
if (fingerprint && !fingerprint.skipFingerprint && fingerprint.deviceId) {
|
|
2152
|
+
const fp = await this._abortable(
|
|
2153
|
+
signal,
|
|
2154
|
+
this._verifyDeviceFingerprintWithSession(
|
|
2155
|
+
retrySessionId,
|
|
2156
|
+
fingerprint.deviceId,
|
|
2157
|
+
fingerprint.chain
|
|
2158
|
+
)
|
|
2159
|
+
);
|
|
2160
|
+
if (!fp.success) {
|
|
2161
|
+
throw Object.assign(new Error(formatDeviceMismatchError(fp.expected, fp.actual)), {
|
|
2162
|
+
code: HardwareErrorCode3.DeviceMismatch
|
|
2163
|
+
});
|
|
2164
|
+
}
|
|
2165
|
+
}
|
|
2166
|
+
return await this._callConnector(retrySessionId, method, params, signal);
|
|
2167
|
+
} catch (retryErr) {
|
|
2168
|
+
if (signal.aborted) throw retryErr;
|
|
2169
|
+
this.connector.reset();
|
|
2170
|
+
this._sessions.clear();
|
|
2171
|
+
this._discoveredDevices.clear();
|
|
2172
|
+
this._connectingPromise = null;
|
|
2173
|
+
if (!isDeviceDisconnectedError(retryErr) && !isTimeoutError(retryErr)) {
|
|
2174
|
+
throw retryErr;
|
|
2175
|
+
}
|
|
2176
|
+
debugLog(
|
|
2177
|
+
"[LedgerAdapter] fresh-session retry still failed; resetting connector and rebuilding DMK"
|
|
2178
|
+
);
|
|
2179
|
+
this.connector.reset();
|
|
2180
|
+
this._sessions.clear();
|
|
2181
|
+
this._discoveredDevices.clear();
|
|
2182
|
+
this._connectingPromise = null;
|
|
2183
|
+
const finalConnectId = await this.ensureConnected(
|
|
2184
|
+
targetConnectId,
|
|
2185
|
+
signal,
|
|
2186
|
+
allowUsbEphemeralFallback
|
|
2187
|
+
);
|
|
2188
|
+
const finalSessionId = this._sessions.get(finalConnectId);
|
|
2189
|
+
if (!finalSessionId) {
|
|
2190
|
+
throw originalErr;
|
|
2191
|
+
}
|
|
2192
|
+
if (fingerprint && !fingerprint.skipFingerprint && fingerprint.deviceId) {
|
|
2193
|
+
const fp = await this._abortable(
|
|
2194
|
+
signal,
|
|
2195
|
+
this._verifyDeviceFingerprintWithSession(
|
|
2196
|
+
finalSessionId,
|
|
2197
|
+
fingerprint.deviceId,
|
|
2198
|
+
fingerprint.chain
|
|
2199
|
+
)
|
|
2200
|
+
);
|
|
2201
|
+
if (!fp.success) {
|
|
2202
|
+
throw Object.assign(new Error(formatDeviceMismatchError(fp.expected, fp.actual)), {
|
|
2203
|
+
code: HardwareErrorCode3.DeviceMismatch
|
|
2204
|
+
});
|
|
2205
|
+
}
|
|
2206
|
+
}
|
|
2207
|
+
return this._callConnector(finalSessionId, method, params, signal);
|
|
2208
|
+
}
|
|
2209
|
+
}
|
|
2955
2210
|
/**
|
|
2956
2211
|
* Ensure OS-level device permission (Bluetooth / USB) before proceeding.
|
|
2957
2212
|
*
|
|
@@ -3021,15 +2276,7 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
3021
2276
|
if (err && typeof err === "object" && "code" in err && typeof err.code === "number") {
|
|
3022
2277
|
const e = err;
|
|
3023
2278
|
const params = e.code === HardwareErrorCode3.DevicePermissionDenied && e.reason ? { permissionDeniedReason: e.reason } : e.params;
|
|
3024
|
-
return ledgerFailure(
|
|
3025
|
-
e.code,
|
|
3026
|
-
e.message ?? "Unknown error",
|
|
3027
|
-
e.appName,
|
|
3028
|
-
tag,
|
|
3029
|
-
params,
|
|
3030
|
-
void 0,
|
|
3031
|
-
isHwkRecoveryHint(e.recovery) ? e.recovery : void 0
|
|
3032
|
-
);
|
|
2279
|
+
return ledgerFailure(e.code, e.message ?? "Unknown error", e.appName, tag, params);
|
|
3033
2280
|
}
|
|
3034
2281
|
const mapped = mapLedgerError(err);
|
|
3035
2282
|
return ledgerFailure(mapped.code, mapped.message, mapped.appName, tag);
|
|
@@ -3056,7 +2303,7 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
3056
2303
|
deviceId: device.deviceId,
|
|
3057
2304
|
connectId: device.connectId,
|
|
3058
2305
|
label: device.name,
|
|
3059
|
-
connectionType:
|
|
2306
|
+
connectionType: this.connector.connectionType,
|
|
3060
2307
|
rssi: device.rssi,
|
|
3061
2308
|
isConnectable: device.isConnectable,
|
|
3062
2309
|
serialNumber: device.serialNumber,
|
|
@@ -3082,8 +2329,8 @@ var LedgerAdapter = _LedgerAdapter;
|
|
|
3082
2329
|
|
|
3083
2330
|
// src/connector/LedgerConnectorBase.ts
|
|
3084
2331
|
import {
|
|
3085
|
-
EConnectorInteraction as
|
|
3086
|
-
HardwareErrorCode as
|
|
2332
|
+
EConnectorInteraction as EConnectorInteraction4,
|
|
2333
|
+
HardwareErrorCode as HardwareErrorCode8,
|
|
3087
2334
|
isKnownNonTargetHardwareVendor,
|
|
3088
2335
|
serializeConnectorError
|
|
3089
2336
|
} from "@onekeyfe/hwk-adapter-core";
|
|
@@ -3356,7 +2603,7 @@ var LedgerDeviceManager = class {
|
|
|
3356
2603
|
|
|
3357
2604
|
// src/signer/SignerManager.ts
|
|
3358
2605
|
import { SignerEthBuilder } from "@ledgerhq/device-signer-kit-ethereum";
|
|
3359
|
-
import { ContextModuleBuilder
|
|
2606
|
+
import { ContextModuleBuilder } from "@ledgerhq/context-module";
|
|
3360
2607
|
|
|
3361
2608
|
// src/signer/SignerEth.ts
|
|
3362
2609
|
import { hexToBytes } from "@onekeyfe/hwk-adapter-core";
|
|
@@ -3622,7 +2869,7 @@ var SignerManager = class _SignerManager {
|
|
|
3622
2869
|
return (args) => new SignerEthBuilder(args);
|
|
3623
2870
|
}
|
|
3624
2871
|
static _createContextModule() {
|
|
3625
|
-
const contextModule = new ContextModuleBuilder({}).
|
|
2872
|
+
const contextModule = new ContextModuleBuilder({}).removeDefaultLoaders().build();
|
|
3626
2873
|
return _SignerManager.wrapBlindSigningReportNonBlocking(contextModule);
|
|
3627
2874
|
}
|
|
3628
2875
|
static wrapBlindSigningReportNonBlocking(contextModule) {
|
|
@@ -4092,8 +3339,7 @@ function _applySignaturesToPsbt(psbtHex, signatures) {
|
|
|
4092
3339
|
}
|
|
4093
3340
|
|
|
4094
3341
|
// src/connector/chains/sol.ts
|
|
4095
|
-
import { bytesToHex, hexToBytes as hexToBytes3
|
|
4096
|
-
import bs58 from "bs58";
|
|
3342
|
+
import { bytesToHex, hexToBytes as hexToBytes3 } from "@onekeyfe/hwk-adapter-core";
|
|
4097
3343
|
|
|
4098
3344
|
// src/signer/SignerSol.ts
|
|
4099
3345
|
var SignerSol = class {
|
|
@@ -4177,21 +3423,8 @@ async function solSignMessage(ctx, sessionId, params) {
|
|
|
4177
3423
|
const path = normalizePath(params.path);
|
|
4178
3424
|
const messageBytes = hexToBytes3(params.message);
|
|
4179
3425
|
try {
|
|
4180
|
-
if (params.messageVersion === 1) {
|
|
4181
|
-
const preparedMessage = prepareSolanaOffchainMessageV1({
|
|
4182
|
-
message: messageBytes,
|
|
4183
|
-
requiredSigners: params.requiredSigners
|
|
4184
|
-
});
|
|
4185
|
-
const { SignMessageVersion } = await ctx.importLedgerKit(
|
|
4186
|
-
"@ledgerhq/device-signer-kit-solana"
|
|
4187
|
-
);
|
|
4188
|
-
const result2 = await solSigner.signMessage(path, preparedMessage.serializedMessage, {
|
|
4189
|
-
version: SignMessageVersion.Raw
|
|
4190
|
-
});
|
|
4191
|
-
return { signature: decodeBase58Signature(result2.signature) };
|
|
4192
|
-
}
|
|
4193
3426
|
const result = await solSigner.signMessage(path, messageBytes);
|
|
4194
|
-
return { signature:
|
|
3427
|
+
return { signature: result.signature };
|
|
4195
3428
|
} catch (err) {
|
|
4196
3429
|
ctx.invalidateSession(sessionId);
|
|
4197
3430
|
throw ctx.wrapError(err);
|
|
@@ -4199,27 +3432,11 @@ async function solSignMessage(ctx, sessionId, params) {
|
|
|
4199
3432
|
ctx.clearCanceller(sessionId);
|
|
4200
3433
|
}
|
|
4201
3434
|
}
|
|
4202
|
-
function decodeBase58Signature(signature) {
|
|
4203
|
-
const bytes = bs58.decode(signature);
|
|
4204
|
-
if (bytes.length !== 64) {
|
|
4205
|
-
throw new Error(`Ledger Solana signature must be 64 bytes, received ${bytes.length}`);
|
|
4206
|
-
}
|
|
4207
|
-
return bytesToHex(bytes);
|
|
4208
|
-
}
|
|
4209
|
-
function decodeBase58EnvelopeSignature(envelope) {
|
|
4210
|
-
const bytes = bs58.decode(envelope);
|
|
4211
|
-
if (bytes.length < 65 || bytes[0] !== 1) {
|
|
4212
|
-
throw new Error("Ledger Solana signature envelope is invalid");
|
|
4213
|
-
}
|
|
4214
|
-
return bytesToHex(bytes.subarray(1, 65));
|
|
4215
|
-
}
|
|
4216
3435
|
async function _createSolSigner(ctx, sessionId) {
|
|
4217
3436
|
const dmk = await ctx.getOrCreateDmk();
|
|
4218
|
-
const { ContextModuleBuilder: ContextModuleBuilder2
|
|
4219
|
-
"@ledgerhq/context-module"
|
|
4220
|
-
);
|
|
3437
|
+
const { ContextModuleBuilder: ContextModuleBuilder2 } = await ctx.importLedgerKit("@ledgerhq/context-module");
|
|
4221
3438
|
const { SignerSolanaBuilder } = await ctx.importLedgerKit("@ledgerhq/device-signer-kit-solana");
|
|
4222
|
-
const contextModule = new ContextModuleBuilder2({}).
|
|
3439
|
+
const contextModule = new ContextModuleBuilder2({}).removeDefaultLoaders().build();
|
|
4223
3440
|
const sdkSigner = new SignerSolanaBuilder({ dmk, sessionId }).withContextModule(contextModule).build();
|
|
4224
3441
|
const signer = new SignerSol(sdkSigner);
|
|
4225
3442
|
signer.onInteraction = (interaction) => {
|
|
@@ -4235,192 +3452,361 @@ async function _createSolSigner(ctx, sessionId) {
|
|
|
4235
3452
|
return signer;
|
|
4236
3453
|
}
|
|
4237
3454
|
|
|
4238
|
-
// src/connector/chains/
|
|
4239
|
-
import {
|
|
3455
|
+
// src/connector/chains/tron.ts
|
|
3456
|
+
import { HardwareErrorCode as HardwareErrorCode7 } from "@onekeyfe/hwk-adapter-core";
|
|
3457
|
+
import Trx from "@ledgerhq/hw-app-trx";
|
|
4240
3458
|
|
|
4241
|
-
// src/
|
|
4242
|
-
|
|
4243
|
-
// eslint-disable-next-line no-useless-constructor, no-empty-function
|
|
4244
|
-
constructor(_sdk) {
|
|
4245
|
-
this._sdk = _sdk;
|
|
4246
|
-
}
|
|
4247
|
-
/** GET_VK: UFVK string (default) or raw 96-byte Orchard FVK. */
|
|
4248
|
-
async getFullViewingKey(derivationPath, options) {
|
|
4249
|
-
const action = this._sdk.getFullViewingKey(derivationPath, options);
|
|
4250
|
-
return deviceActionToPromise(
|
|
4251
|
-
action,
|
|
4252
|
-
this.onInteraction,
|
|
4253
|
-
void 0,
|
|
4254
|
-
this.onRegisterCanceller
|
|
4255
|
-
);
|
|
4256
|
-
}
|
|
4257
|
-
/** GET_SHIELDED_ADDRESS: single-Orchard-receiver UA for a 5-level transparent path. */
|
|
4258
|
-
async getShieldedAddress(derivationPath, options) {
|
|
4259
|
-
const action = this._sdk.getShieldedAddress(derivationPath, options);
|
|
4260
|
-
return deviceActionToPromise(
|
|
4261
|
-
action,
|
|
4262
|
-
this.onInteraction,
|
|
4263
|
-
void 0,
|
|
4264
|
-
this.onRegisterCanceller
|
|
4265
|
-
);
|
|
4266
|
-
}
|
|
4267
|
-
};
|
|
3459
|
+
// src/connector/chains/legacyChainCall.ts
|
|
3460
|
+
import { EConnectorInteraction as EConnectorInteraction3 } from "@onekeyfe/hwk-adapter-core";
|
|
4268
3461
|
|
|
4269
|
-
// src/
|
|
4270
|
-
|
|
4271
|
-
|
|
4272
|
-
|
|
4273
|
-
|
|
4274
|
-
|
|
4275
|
-
|
|
4276
|
-
|
|
4277
|
-
|
|
3462
|
+
// src/app/AppManager.ts
|
|
3463
|
+
import {
|
|
3464
|
+
CloseAppCommand,
|
|
3465
|
+
GetAppAndVersionCommand,
|
|
3466
|
+
OpenAppCommand,
|
|
3467
|
+
isSuccessCommandResult
|
|
3468
|
+
} from "@ledgerhq/device-management-kit";
|
|
3469
|
+
import { HardwareErrorCode as HardwareErrorCode6 } from "@onekeyfe/hwk-adapter-core";
|
|
3470
|
+
var APP_NAME_MAP = {
|
|
3471
|
+
ETH: "Ethereum",
|
|
3472
|
+
BTC: "Bitcoin",
|
|
3473
|
+
SOL: "Solana",
|
|
3474
|
+
TRX: "Tron",
|
|
3475
|
+
XRP: "XRP",
|
|
3476
|
+
ADA: "Cardano",
|
|
3477
|
+
DOT: "Polkadot",
|
|
3478
|
+
ATOM: "Cosmos"
|
|
3479
|
+
};
|
|
3480
|
+
var DASHBOARD_APP_NAME = "BOLOS";
|
|
3481
|
+
var AppManager = class {
|
|
3482
|
+
constructor(dmk, options) {
|
|
3483
|
+
this._dmk = dmk;
|
|
3484
|
+
this._waitMs = options?.waitMs ?? 1e3;
|
|
3485
|
+
this._maxRetries = options?.maxRetries ?? 10;
|
|
3486
|
+
}
|
|
3487
|
+
/**
|
|
3488
|
+
* Return the Ledger app name for a given chain ticker,
|
|
3489
|
+
* or undefined if the chain is not supported.
|
|
3490
|
+
*/
|
|
3491
|
+
static getAppName(chain) {
|
|
3492
|
+
return APP_NAME_MAP[chain];
|
|
3493
|
+
}
|
|
3494
|
+
/**
|
|
3495
|
+
* Ensure the target app is open on the device identified by `sessionId`.
|
|
3496
|
+
*
|
|
3497
|
+
* Flow:
|
|
3498
|
+
* 1. Check the currently running app.
|
|
3499
|
+
* 2. If it is already the target, return immediately.
|
|
3500
|
+
* 3. If a different app is running (not dashboard), close it first.
|
|
3501
|
+
* 4. Open the target app.
|
|
3502
|
+
* 5. Poll until the device confirms the target app is running.
|
|
3503
|
+
*/
|
|
3504
|
+
/**
|
|
3505
|
+
* @param onConfirmOnDevice Called BEFORE OpenAppCommand is issued — the
|
|
3506
|
+
* device is about to display "Open <app>" on screen and wait for the
|
|
3507
|
+
* user's button press. UI consumers should show their "open app" prompt
|
|
3508
|
+
* in response. NOT called when the target app is already open (no user
|
|
3509
|
+
* interaction needed in that case).
|
|
3510
|
+
*
|
|
3511
|
+
* Important: OpenAppCommand is blocking. It does not resolve until the user
|
|
3512
|
+
* has physically confirmed on the device, so anything that runs AFTER
|
|
3513
|
+
* `await this._openApp(...)` lands AFTER the prompt is already gone.
|
|
3514
|
+
* Hence the callback must fire BEFORE that await.
|
|
3515
|
+
*/
|
|
3516
|
+
async ensureAppOpen(sessionId, targetAppName, onConfirmOnDevice) {
|
|
3517
|
+
const currentApp = await this._getCurrentApp(sessionId);
|
|
3518
|
+
if (currentApp === targetAppName) {
|
|
3519
|
+
return;
|
|
4278
3520
|
}
|
|
4279
|
-
|
|
4280
|
-
|
|
4281
|
-
|
|
4282
|
-
|
|
4283
|
-
|
|
4284
|
-
|
|
3521
|
+
if (!this._isDashboard(currentApp)) {
|
|
3522
|
+
await this._closeCurrentApp(sessionId);
|
|
3523
|
+
await this._waitForApp(sessionId, DASHBOARD_APP_NAME);
|
|
3524
|
+
}
|
|
3525
|
+
onConfirmOnDevice?.();
|
|
3526
|
+
await this._openApp(sessionId, targetAppName);
|
|
3527
|
+
await this._waitForApp(sessionId, targetAppName);
|
|
4285
3528
|
}
|
|
4286
|
-
|
|
4287
|
-
|
|
4288
|
-
|
|
4289
|
-
|
|
4290
|
-
|
|
4291
|
-
|
|
4292
|
-
|
|
3529
|
+
// ---------------------------------------------------------------------------
|
|
3530
|
+
// Private helpers
|
|
3531
|
+
// ---------------------------------------------------------------------------
|
|
3532
|
+
async _getCurrentApp(sessionId) {
|
|
3533
|
+
const result = await this._dmk.sendCommand({
|
|
3534
|
+
sessionId,
|
|
3535
|
+
command: new GetAppAndVersionCommand()
|
|
4293
3536
|
});
|
|
4294
|
-
|
|
4295
|
-
|
|
4296
|
-
|
|
4297
|
-
|
|
4298
|
-
|
|
4299
|
-
|
|
3537
|
+
if (isSuccessCommandResult(result)) {
|
|
3538
|
+
debugLog("[AppManager] currentApp:", result.data.name);
|
|
3539
|
+
return result.data.name;
|
|
3540
|
+
}
|
|
3541
|
+
const errResult = result;
|
|
3542
|
+
const dmkErr = errResult.error ?? {};
|
|
3543
|
+
const original = dmkErr.originalError;
|
|
3544
|
+
debugLog(
|
|
3545
|
+
"[AppManager] _getCurrentApp failed sessionId=",
|
|
3546
|
+
sessionId,
|
|
3547
|
+
"tag=",
|
|
3548
|
+
dmkErr._tag,
|
|
3549
|
+
"errorCode=",
|
|
3550
|
+
dmkErr.errorCode,
|
|
3551
|
+
"message=",
|
|
3552
|
+
dmkErr.message,
|
|
3553
|
+
"originalErrorMessage=",
|
|
3554
|
+
original?.message ?? String(original ?? "")
|
|
3555
|
+
);
|
|
3556
|
+
throw Object.assign(
|
|
3557
|
+
new Error(
|
|
3558
|
+
dmkErr.message ?? "Failed to get current app from device"
|
|
3559
|
+
),
|
|
3560
|
+
{
|
|
3561
|
+
_tag: dmkErr._tag,
|
|
3562
|
+
errorCode: dmkErr.errorCode,
|
|
3563
|
+
originalError: original
|
|
3564
|
+
}
|
|
3565
|
+
);
|
|
3566
|
+
}
|
|
3567
|
+
async _openApp(sessionId, appName) {
|
|
3568
|
+
const result = await this._dmk.sendCommand({
|
|
3569
|
+
sessionId,
|
|
3570
|
+
command: new OpenAppCommand({ appName })
|
|
3571
|
+
});
|
|
3572
|
+
if (!isSuccessCommandResult(result)) {
|
|
3573
|
+
const dmkErr = result.error;
|
|
3574
|
+
const errorCode = "errorCode" in dmkErr && dmkErr.errorCode != null ? String(dmkErr.errorCode) : "";
|
|
3575
|
+
const message = "message" in dmkErr && typeof dmkErr.message === "string" ? dmkErr.message : "";
|
|
3576
|
+
debugLog(
|
|
3577
|
+
"[AppManager] openApp failed:",
|
|
3578
|
+
appName,
|
|
3579
|
+
"errorCode:",
|
|
3580
|
+
errorCode,
|
|
3581
|
+
"tag:",
|
|
3582
|
+
dmkErr._tag
|
|
3583
|
+
);
|
|
3584
|
+
let code;
|
|
3585
|
+
if (errorCode === "6807" || /unknown application/i.test(message)) {
|
|
3586
|
+
code = HardwareErrorCode6.AppNotInstalled;
|
|
3587
|
+
} else if (errorCode === "5501" || dmkErr._tag === "ActionRefusedError") {
|
|
3588
|
+
code = HardwareErrorCode6.UserRejected;
|
|
3589
|
+
}
|
|
3590
|
+
throw Object.assign(new Error(`Failed to open "${appName}"`), {
|
|
3591
|
+
_tag: ERROR_TAG.OpenAppCommand,
|
|
3592
|
+
code,
|
|
3593
|
+
errorCode,
|
|
3594
|
+
statusCode: errorCode,
|
|
3595
|
+
appName,
|
|
3596
|
+
originalError: dmkErr
|
|
3597
|
+
});
|
|
3598
|
+
}
|
|
3599
|
+
}
|
|
3600
|
+
async _closeCurrentApp(sessionId) {
|
|
3601
|
+
debugLog("[AppManager] closeCurrentApp");
|
|
3602
|
+
await this._dmk.sendCommand({
|
|
3603
|
+
sessionId,
|
|
3604
|
+
command: new CloseAppCommand()
|
|
3605
|
+
});
|
|
3606
|
+
}
|
|
3607
|
+
/**
|
|
3608
|
+
* Poll the device until the expected app is reported as running,
|
|
3609
|
+
* or throw after `_maxRetries` attempts.
|
|
3610
|
+
*/
|
|
3611
|
+
async _waitForApp(sessionId, expectedAppName) {
|
|
3612
|
+
let lastSeen = "";
|
|
3613
|
+
for (let i = 0; i < this._maxRetries; i++) {
|
|
3614
|
+
await this._wait();
|
|
3615
|
+
const current = await this._getCurrentApp(sessionId);
|
|
3616
|
+
lastSeen = current;
|
|
3617
|
+
if (current === expectedAppName) {
|
|
3618
|
+
return;
|
|
3619
|
+
}
|
|
3620
|
+
}
|
|
3621
|
+
debugLog(
|
|
3622
|
+
"[AppManager] waitForApp exhausted: expected=",
|
|
3623
|
+
expectedAppName,
|
|
3624
|
+
"lastSeen=",
|
|
3625
|
+
lastSeen
|
|
3626
|
+
);
|
|
3627
|
+
throw new Error(
|
|
3628
|
+
`Ledger: failed to open "${expectedAppName}" after ${this._maxRetries} retries (last seen: ${lastSeen})`
|
|
3629
|
+
);
|
|
3630
|
+
}
|
|
3631
|
+
_isDashboard(appName) {
|
|
3632
|
+
return appName === DASHBOARD_APP_NAME;
|
|
3633
|
+
}
|
|
3634
|
+
_wait() {
|
|
3635
|
+
return new Promise((resolve) => setTimeout(resolve, this._waitMs));
|
|
4300
3636
|
}
|
|
3637
|
+
};
|
|
3638
|
+
|
|
3639
|
+
// src/connector/chains/legacyChainCall.ts
|
|
3640
|
+
function isLegacyWrongAppError(err, _appName) {
|
|
3641
|
+
return isWrongAppError(err);
|
|
4301
3642
|
}
|
|
4302
|
-
async function
|
|
4303
|
-
const
|
|
4304
|
-
|
|
4305
|
-
const
|
|
4306
|
-
|
|
4307
|
-
signer.onInteraction = (interaction) => {
|
|
4308
|
-
debugLog("[LedgerConnector] zcash.onInteraction:", interaction);
|
|
3643
|
+
async function withLegacyChainCall(ctx, sessionId, options, action) {
|
|
3644
|
+
const { appName, needsConfirmation } = options;
|
|
3645
|
+
let openAppPromptShown = false;
|
|
3646
|
+
const onAppOpenPrompt = () => {
|
|
3647
|
+
openAppPromptShown = true;
|
|
4309
3648
|
ctx.emit("ui-event", {
|
|
4310
|
-
type:
|
|
3649
|
+
type: EConnectorInteraction3.ConfirmOpenApp,
|
|
4311
3650
|
payload: { sessionId }
|
|
4312
3651
|
});
|
|
4313
3652
|
};
|
|
4314
|
-
|
|
4315
|
-
|
|
3653
|
+
const closeOpenAppUiIfShown = () => {
|
|
3654
|
+
if (openAppPromptShown) {
|
|
3655
|
+
ctx.emit("ui-event", {
|
|
3656
|
+
type: EConnectorInteraction3.InteractionComplete,
|
|
3657
|
+
payload: { sessionId }
|
|
3658
|
+
});
|
|
3659
|
+
openAppPromptShown = false;
|
|
3660
|
+
}
|
|
3661
|
+
};
|
|
3662
|
+
try {
|
|
3663
|
+
await _ensureAppOpen(ctx, sessionId, appName, onAppOpenPrompt);
|
|
3664
|
+
} catch (err) {
|
|
3665
|
+
debugLog(
|
|
3666
|
+
"[LegacyChainCall] pre-flight ensureAppOpen failed:",
|
|
3667
|
+
appName,
|
|
3668
|
+
err?.message
|
|
3669
|
+
);
|
|
3670
|
+
closeOpenAppUiIfShown();
|
|
3671
|
+
throw ctx.wrapError(err, { defaultAppName: appName });
|
|
3672
|
+
}
|
|
3673
|
+
const runOnce = async () => {
|
|
3674
|
+
let confirmEmitted = false;
|
|
3675
|
+
if (needsConfirmation) {
|
|
3676
|
+
ctx.emit("ui-event", {
|
|
3677
|
+
type: EConnectorInteraction3.ConfirmOnDevice,
|
|
3678
|
+
payload: { sessionId }
|
|
3679
|
+
});
|
|
3680
|
+
confirmEmitted = true;
|
|
3681
|
+
}
|
|
3682
|
+
try {
|
|
3683
|
+
return await action(sessionId);
|
|
3684
|
+
} finally {
|
|
3685
|
+
if (confirmEmitted || openAppPromptShown) {
|
|
3686
|
+
ctx.emit("ui-event", {
|
|
3687
|
+
type: EConnectorInteraction3.InteractionComplete,
|
|
3688
|
+
payload: { sessionId }
|
|
3689
|
+
});
|
|
3690
|
+
openAppPromptShown = false;
|
|
3691
|
+
}
|
|
3692
|
+
}
|
|
3693
|
+
};
|
|
3694
|
+
try {
|
|
3695
|
+
return await runOnce();
|
|
3696
|
+
} catch (err) {
|
|
3697
|
+
if (!isLegacyWrongAppError(err, appName)) {
|
|
3698
|
+
debugLog("[LegacyChainCall] non-wrong-app failure:", appName, err?.message);
|
|
3699
|
+
ctx.invalidateSession(sessionId);
|
|
3700
|
+
throw ctx.wrapError(err, { defaultAppName: appName });
|
|
3701
|
+
}
|
|
3702
|
+
debugLog("[LegacyChainCall] wrong-app detected, retrying:", appName);
|
|
3703
|
+
try {
|
|
3704
|
+
await _ensureAppOpen(ctx, sessionId, appName, onAppOpenPrompt);
|
|
3705
|
+
} catch (switchErr) {
|
|
3706
|
+
debugLog(
|
|
3707
|
+
"[LegacyChainCall] retry ensureAppOpen failed:",
|
|
3708
|
+
appName,
|
|
3709
|
+
switchErr?.message
|
|
3710
|
+
);
|
|
3711
|
+
closeOpenAppUiIfShown();
|
|
3712
|
+
throw ctx.wrapError(switchErr, { defaultAppName: appName });
|
|
3713
|
+
}
|
|
3714
|
+
ctx.clearAllSigners();
|
|
3715
|
+
const result = await runOnce();
|
|
3716
|
+
debugLog("[LegacyChainCall] retry succeeded:", appName);
|
|
3717
|
+
return result;
|
|
3718
|
+
}
|
|
3719
|
+
}
|
|
3720
|
+
async function _ensureAppOpen(ctx, sessionId, appName, onPrompt) {
|
|
3721
|
+
const dmk = await ctx.getOrCreateDmk();
|
|
3722
|
+
const appManager = new AppManager(dmk);
|
|
3723
|
+
await appManager.ensureAppOpen(sessionId, appName, onPrompt);
|
|
4316
3724
|
}
|
|
4317
3725
|
|
|
4318
|
-
// src/
|
|
4319
|
-
import
|
|
4320
|
-
|
|
4321
|
-
|
|
4322
|
-
|
|
4323
|
-
|
|
4324
|
-
|
|
4325
|
-
this._sdk = _sdk;
|
|
4326
|
-
}
|
|
4327
|
-
/** Base58 Tron address plus the uncompressed public key at `derivationPath`. */
|
|
4328
|
-
async getAddress(derivationPath, options) {
|
|
4329
|
-
const action = this._sdk.getAddress(derivationPath, options);
|
|
4330
|
-
const result = await deviceActionToPromise(
|
|
4331
|
-
action,
|
|
4332
|
-
this.onInteraction,
|
|
4333
|
-
void 0,
|
|
4334
|
-
this.onRegisterCanceller
|
|
4335
|
-
);
|
|
4336
|
-
return { address: result.address, publicKey: result.publicKey };
|
|
3726
|
+
// src/transport/DmkTransport.ts
|
|
3727
|
+
import Transport from "@ledgerhq/hw-transport";
|
|
3728
|
+
var DmkTransport = class extends Transport {
|
|
3729
|
+
constructor(dmk, sessionId) {
|
|
3730
|
+
super();
|
|
3731
|
+
this._dmk = dmk;
|
|
3732
|
+
this._sessionId = sessionId;
|
|
4337
3733
|
}
|
|
4338
|
-
|
|
4339
|
-
|
|
4340
|
-
|
|
4341
|
-
|
|
4342
|
-
|
|
4343
|
-
|
|
4344
|
-
|
|
4345
|
-
|
|
4346
|
-
|
|
3734
|
+
async exchange(apdu) {
|
|
3735
|
+
const response = await this._dmk.sendApdu({
|
|
3736
|
+
sessionId: this._sessionId,
|
|
3737
|
+
apdu: new Uint8Array(apdu)
|
|
3738
|
+
});
|
|
3739
|
+
const { data, statusCode } = response;
|
|
3740
|
+
const result = Buffer.alloc(data.length + 2);
|
|
3741
|
+
if (data.length > 0) {
|
|
3742
|
+
result.set(data, 0);
|
|
3743
|
+
}
|
|
3744
|
+
result.set(statusCode, data.length);
|
|
3745
|
+
return result;
|
|
4347
3746
|
}
|
|
4348
|
-
|
|
4349
|
-
async signPersonalMessage(derivationPath, message, options) {
|
|
4350
|
-
const action = this._sdk.signPersonalMessage(derivationPath, message, options);
|
|
4351
|
-
return deviceActionToPromise(
|
|
4352
|
-
action,
|
|
4353
|
-
this.onInteraction,
|
|
4354
|
-
void 0,
|
|
4355
|
-
this.onRegisterCanceller
|
|
4356
|
-
);
|
|
3747
|
+
async close() {
|
|
4357
3748
|
}
|
|
4358
3749
|
};
|
|
4359
3750
|
|
|
4360
3751
|
// src/connector/chains/tron.ts
|
|
4361
3752
|
async function tronGetAddress(ctx, sessionId, params) {
|
|
4362
|
-
const tronSigner = await _createTronSigner(ctx, sessionId);
|
|
4363
3753
|
const path = normalizePath(params.path);
|
|
4364
|
-
|
|
4365
|
-
|
|
4366
|
-
|
|
4367
|
-
|
|
4368
|
-
|
|
4369
|
-
|
|
4370
|
-
|
|
4371
|
-
|
|
4372
|
-
|
|
4373
|
-
|
|
4374
|
-
|
|
3754
|
+
const showOnDevice = params.showOnDevice ?? false;
|
|
3755
|
+
return withLegacyChainCall(
|
|
3756
|
+
ctx,
|
|
3757
|
+
sessionId,
|
|
3758
|
+
{
|
|
3759
|
+
appName: "Tron",
|
|
3760
|
+
// Only show "confirm on device" UI when the device is actually going
|
|
3761
|
+
// to display the address for the user to verify.
|
|
3762
|
+
needsConfirmation: showOnDevice
|
|
3763
|
+
},
|
|
3764
|
+
async (sid) => {
|
|
3765
|
+
const trx = await _createTrx(ctx, sid);
|
|
3766
|
+
const result = await trx.getAddress(path, showOnDevice);
|
|
3767
|
+
return { address: result.address, publicKey: result.publicKey, path: params.path };
|
|
3768
|
+
}
|
|
3769
|
+
);
|
|
4375
3770
|
}
|
|
4376
3771
|
async function tronSignTransaction(ctx, sessionId, params) {
|
|
4377
3772
|
if (!params.rawTxHex) {
|
|
4378
3773
|
throw Object.assign(
|
|
4379
3774
|
new Error("TRON signing requires a protobuf-encoded raw transaction hex (rawTxHex)."),
|
|
4380
|
-
{ code:
|
|
3775
|
+
{ code: HardwareErrorCode7.InvalidParams }
|
|
4381
3776
|
);
|
|
4382
3777
|
}
|
|
4383
|
-
const tronSigner = await _createTronSigner(ctx, sessionId);
|
|
4384
3778
|
const path = normalizePath(params.path);
|
|
4385
|
-
|
|
4386
|
-
|
|
4387
|
-
|
|
4388
|
-
|
|
4389
|
-
|
|
4390
|
-
|
|
4391
|
-
|
|
4392
|
-
|
|
4393
|
-
|
|
3779
|
+
return withLegacyChainCall(
|
|
3780
|
+
ctx,
|
|
3781
|
+
sessionId,
|
|
3782
|
+
{ appName: "Tron", needsConfirmation: true },
|
|
3783
|
+
async (sid) => {
|
|
3784
|
+
const trx = await _createTrx(ctx, sid);
|
|
3785
|
+
const signature = await trx.signTransaction(
|
|
3786
|
+
path,
|
|
3787
|
+
params.rawTxHex,
|
|
3788
|
+
params.tokenSignatures ?? []
|
|
3789
|
+
);
|
|
3790
|
+
return { signature };
|
|
3791
|
+
}
|
|
3792
|
+
);
|
|
4394
3793
|
}
|
|
4395
3794
|
async function tronSignMessage(ctx, sessionId, params) {
|
|
4396
|
-
const tronSigner = await _createTronSigner(ctx, sessionId);
|
|
4397
3795
|
const path = normalizePath(params.path);
|
|
4398
|
-
|
|
4399
|
-
|
|
4400
|
-
|
|
4401
|
-
|
|
4402
|
-
|
|
4403
|
-
|
|
4404
|
-
|
|
4405
|
-
|
|
4406
|
-
|
|
3796
|
+
return withLegacyChainCall(
|
|
3797
|
+
ctx,
|
|
3798
|
+
sessionId,
|
|
3799
|
+
{ appName: "Tron", needsConfirmation: true },
|
|
3800
|
+
async (sid) => {
|
|
3801
|
+
const trx = await _createTrx(ctx, sid);
|
|
3802
|
+
const signature = await trx.signPersonalMessage(path, params.messageHex);
|
|
3803
|
+
return { signature };
|
|
3804
|
+
}
|
|
3805
|
+
);
|
|
4407
3806
|
}
|
|
4408
|
-
async function
|
|
3807
|
+
async function _createTrx(ctx, sessionId) {
|
|
4409
3808
|
const dmk = await ctx.getOrCreateDmk();
|
|
4410
|
-
|
|
4411
|
-
const sdkSigner = new SignerTrxBuilder({ dmk, sessionId }).build();
|
|
4412
|
-
const signer = new SignerTron(sdkSigner);
|
|
4413
|
-
signer.onInteraction = (interaction) => {
|
|
4414
|
-
debugLog("[LedgerConnector] tron.onInteraction:", interaction);
|
|
4415
|
-
ctx.emit("ui-event", {
|
|
4416
|
-
type: collapseSignerInteraction(interaction),
|
|
4417
|
-
payload: { sessionId }
|
|
4418
|
-
});
|
|
4419
|
-
};
|
|
4420
|
-
signer.onRegisterCanceller = (cancel) => {
|
|
4421
|
-
ctx.registerCanceller(sessionId, cancel);
|
|
4422
|
-
};
|
|
4423
|
-
return signer;
|
|
3809
|
+
return new Trx(new DmkTransport(dmk, sessionId));
|
|
4424
3810
|
}
|
|
4425
3811
|
|
|
4426
3812
|
// src/device-apps/customActions.ts
|
|
@@ -4599,7 +3985,7 @@ var DeviceApps = class {
|
|
|
4599
3985
|
seTargetId: v.seTargetId,
|
|
4600
3986
|
mcuTargetId: v.mcuTargetId,
|
|
4601
3987
|
seVersion: v.seVersion,
|
|
4602
|
-
seFlagsHex:
|
|
3988
|
+
seFlagsHex: bytesToHex2(v.seFlags),
|
|
4603
3989
|
mcuSephVersion: v.mcuSephVersion,
|
|
4604
3990
|
mcuBootloaderVersion: v.mcuBootloaderVersion,
|
|
4605
3991
|
hwVersion: v.hwVersion
|
|
@@ -4673,7 +4059,7 @@ var DeviceApps = class {
|
|
|
4673
4059
|
}
|
|
4674
4060
|
}
|
|
4675
4061
|
};
|
|
4676
|
-
function
|
|
4062
|
+
function bytesToHex2(bytes) {
|
|
4677
4063
|
if (!bytes) return "";
|
|
4678
4064
|
return Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join("");
|
|
4679
4065
|
}
|
|
@@ -4716,11 +4102,10 @@ var METHOD_PREFIX_TO_APP_NAME = {
|
|
|
4716
4102
|
evm: "Ethereum",
|
|
4717
4103
|
btc: "Bitcoin",
|
|
4718
4104
|
sol: "Solana",
|
|
4719
|
-
tron: "Tron"
|
|
4720
|
-
zcash: "Zcash"
|
|
4105
|
+
tron: "Tron"
|
|
4721
4106
|
};
|
|
4722
4107
|
var HARDWARE_ERROR_CODE_VALUES = new Set(
|
|
4723
|
-
Object.values(
|
|
4108
|
+
Object.values(HardwareErrorCode8).filter((value) => typeof value === "number")
|
|
4724
4109
|
);
|
|
4725
4110
|
var BLE_CONNECT_SCAN_TIMEOUT_MS = 1500;
|
|
4726
4111
|
var LEDGER_RELAY_ALLOWED_ROOT_DOMAINS = ["onekeytest.com", "onekey.com"];
|
|
@@ -4757,10 +4142,6 @@ async function defaultLedgerKitImporter(pkg) {
|
|
|
4757
4142
|
return import("@ledgerhq/device-signer-kit-bitcoin");
|
|
4758
4143
|
case "@ledgerhq/device-signer-kit-solana":
|
|
4759
4144
|
return import("@ledgerhq/device-signer-kit-solana");
|
|
4760
|
-
case "@ledgerhq/device-signer-kit-tron":
|
|
4761
|
-
return import("@ledgerhq/device-signer-kit-tron");
|
|
4762
|
-
case "@ledgerhq/device-signer-kit-zcash":
|
|
4763
|
-
return import("@ledgerhq/device-signer-kit-zcash");
|
|
4764
4145
|
case "@ledgerhq/context-module":
|
|
4765
4146
|
return import("@ledgerhq/context-module");
|
|
4766
4147
|
default:
|
|
@@ -4919,7 +4300,7 @@ var LedgerConnectorBase = class {
|
|
|
4919
4300
|
`Ledger BLE connect did not return within ${HANG_CEILING_MS / 6e4}min \u2014 DMK hang fallback.`
|
|
4920
4301
|
);
|
|
4921
4302
|
err._tag = ERROR_TAG.BlePairingTimeout;
|
|
4922
|
-
err.code =
|
|
4303
|
+
err.code = HardwareErrorCode8.BlePairingTimeout;
|
|
4923
4304
|
reject(err);
|
|
4924
4305
|
}, HANG_CEILING_MS);
|
|
4925
4306
|
});
|
|
@@ -4952,7 +4333,7 @@ var LedgerConnectorBase = class {
|
|
|
4952
4333
|
"Ledger device is not currently advertising. Wake up and unlock the device, keep it nearby, then try again."
|
|
4953
4334
|
);
|
|
4954
4335
|
err._tag = ERROR_TAG.DeviceNotAdvertising;
|
|
4955
|
-
err.code =
|
|
4336
|
+
err.code = HardwareErrorCode8.DeviceNotFound;
|
|
4956
4337
|
throw err;
|
|
4957
4338
|
};
|
|
4958
4339
|
const doConnect = async (path) => {
|
|
@@ -5016,7 +4397,7 @@ var LedgerConnectorBase = class {
|
|
|
5016
4397
|
"Ledger Bluetooth pairing failed. Make sure the device is unlocked and nearby, then try again."
|
|
5017
4398
|
);
|
|
5018
4399
|
wrapped._tag = ERROR_TAG.BleGattBondingFailed;
|
|
5019
|
-
wrapped.code =
|
|
4400
|
+
wrapped.code = HardwareErrorCode8.BlePairingTimeout;
|
|
5020
4401
|
wrapped.originalError = err;
|
|
5021
4402
|
throw wrapped;
|
|
5022
4403
|
}
|
|
@@ -5094,7 +4475,7 @@ var LedgerConnectorBase = class {
|
|
|
5094
4475
|
this._unwatchSessionState(sessionId);
|
|
5095
4476
|
this._signerManager?.invalidate(sessionId);
|
|
5096
4477
|
this._cancellers.get(sessionId)?.({
|
|
5097
|
-
code:
|
|
4478
|
+
code: HardwareErrorCode8.DeviceDisconnected,
|
|
5098
4479
|
tag: "DeviceDisconnected",
|
|
5099
4480
|
message: "Device disconnected"
|
|
5100
4481
|
});
|
|
@@ -5120,7 +4501,7 @@ var LedgerConnectorBase = class {
|
|
|
5120
4501
|
success: false,
|
|
5121
4502
|
error: serializeConnectorError(
|
|
5122
4503
|
Object.assign(new Error("Ledger app is unresponsive"), {
|
|
5123
|
-
code:
|
|
4504
|
+
code: HardwareErrorCode8.DeviceAppStuck,
|
|
5124
4505
|
_tag: ERROR_TAG.DeviceAppStuck,
|
|
5125
4506
|
originalError: err
|
|
5126
4507
|
})
|
|
@@ -5132,7 +4513,7 @@ var LedgerConnectorBase = class {
|
|
|
5132
4513
|
success: false,
|
|
5133
4514
|
error: serializeConnectorError(
|
|
5134
4515
|
Object.assign(new Error("Device communication interrupted, please retry"), {
|
|
5135
|
-
code:
|
|
4516
|
+
code: HardwareErrorCode8.TransportError,
|
|
5136
4517
|
_tag: ERROR_TAG.DeviceTransportStuck,
|
|
5137
4518
|
originalError: err
|
|
5138
4519
|
})
|
|
@@ -5178,11 +4559,6 @@ var LedgerConnectorBase = class {
|
|
|
5178
4559
|
return solSignTransaction(ctx, sessionId, params);
|
|
5179
4560
|
case "solSignMessage":
|
|
5180
4561
|
return solSignMessage(ctx, sessionId, params);
|
|
5181
|
-
// ZCASH
|
|
5182
|
-
case "zcashGetFullViewingKey":
|
|
5183
|
-
return zcashGetFullViewingKey(ctx, sessionId, params);
|
|
5184
|
-
case "zcashGetShieldedAddress":
|
|
5185
|
-
return zcashGetShieldedAddress(ctx, sessionId, params);
|
|
5186
4562
|
// TRON
|
|
5187
4563
|
case "tronGetAddress":
|
|
5188
4564
|
return tronGetAddress(ctx, sessionId, params);
|
|
@@ -5207,7 +4583,7 @@ var LedgerConnectorBase = class {
|
|
|
5207
4583
|
try {
|
|
5208
4584
|
return await apps.install(p.appName, ({ progress }) => {
|
|
5209
4585
|
ctx.emit("ui-event", {
|
|
5210
|
-
type:
|
|
4586
|
+
type: EConnectorInteraction4.AppInstallProgress,
|
|
5211
4587
|
payload: {
|
|
5212
4588
|
sessionId,
|
|
5213
4589
|
appName: p.appName,
|
|
@@ -5512,7 +4888,7 @@ var LedgerConnectorBase = class {
|
|
|
5512
4888
|
* at every catch site. Falls through unchanged for unknown methods.
|
|
5513
4889
|
*/
|
|
5514
4890
|
_ctxForMethod(method) {
|
|
5515
|
-
const prefix = /^(evm|btc|sol|tron
|
|
4891
|
+
const prefix = /^(evm|btc|sol|tron)/.exec(method)?.[1];
|
|
5516
4892
|
const defaultAppName = prefix ? METHOD_PREFIX_TO_APP_NAME[prefix] : void 0;
|
|
5517
4893
|
if (!defaultAppName) return this._ctx;
|
|
5518
4894
|
return {
|
|
@@ -5546,209 +4922,6 @@ var LedgerConnectorBase = class {
|
|
|
5546
4922
|
return error;
|
|
5547
4923
|
}
|
|
5548
4924
|
};
|
|
5549
|
-
|
|
5550
|
-
// src/transport/DmkTransport.ts
|
|
5551
|
-
import Transport from "@ledgerhq/hw-transport";
|
|
5552
|
-
var DmkTransport = class extends Transport {
|
|
5553
|
-
constructor(dmk, sessionId) {
|
|
5554
|
-
super();
|
|
5555
|
-
this._dmk = dmk;
|
|
5556
|
-
this._sessionId = sessionId;
|
|
5557
|
-
}
|
|
5558
|
-
async exchange(apdu) {
|
|
5559
|
-
const response = await this._dmk.sendApdu({
|
|
5560
|
-
sessionId: this._sessionId,
|
|
5561
|
-
apdu: new Uint8Array(apdu)
|
|
5562
|
-
});
|
|
5563
|
-
const { data, statusCode } = response;
|
|
5564
|
-
const result = Buffer.alloc(data.length + 2);
|
|
5565
|
-
if (data.length > 0) {
|
|
5566
|
-
result.set(data, 0);
|
|
5567
|
-
}
|
|
5568
|
-
result.set(statusCode, data.length);
|
|
5569
|
-
return result;
|
|
5570
|
-
}
|
|
5571
|
-
async close() {
|
|
5572
|
-
}
|
|
5573
|
-
};
|
|
5574
|
-
|
|
5575
|
-
// src/app/AppManager.ts
|
|
5576
|
-
import {
|
|
5577
|
-
CloseAppCommand,
|
|
5578
|
-
GetAppAndVersionCommand,
|
|
5579
|
-
OpenAppCommand,
|
|
5580
|
-
isSuccessCommandResult
|
|
5581
|
-
} from "@ledgerhq/device-management-kit";
|
|
5582
|
-
import { HardwareErrorCode as HardwareErrorCode8 } from "@onekeyfe/hwk-adapter-core";
|
|
5583
|
-
var APP_NAME_MAP = {
|
|
5584
|
-
ETH: "Ethereum",
|
|
5585
|
-
BTC: "Bitcoin",
|
|
5586
|
-
SOL: "Solana",
|
|
5587
|
-
TRX: "Tron",
|
|
5588
|
-
ZEC: "Zcash",
|
|
5589
|
-
XRP: "XRP",
|
|
5590
|
-
ADA: "Cardano",
|
|
5591
|
-
DOT: "Polkadot",
|
|
5592
|
-
ATOM: "Cosmos"
|
|
5593
|
-
};
|
|
5594
|
-
var DASHBOARD_APP_NAME = "BOLOS";
|
|
5595
|
-
var AppManager = class {
|
|
5596
|
-
constructor(dmk, options) {
|
|
5597
|
-
this._dmk = dmk;
|
|
5598
|
-
this._waitMs = options?.waitMs ?? 1e3;
|
|
5599
|
-
this._maxRetries = options?.maxRetries ?? 10;
|
|
5600
|
-
}
|
|
5601
|
-
/**
|
|
5602
|
-
* Return the Ledger app name for a given chain ticker,
|
|
5603
|
-
* or undefined if the chain is not supported.
|
|
5604
|
-
*/
|
|
5605
|
-
static getAppName(chain) {
|
|
5606
|
-
return APP_NAME_MAP[chain];
|
|
5607
|
-
}
|
|
5608
|
-
/**
|
|
5609
|
-
* Ensure the target app is open on the device identified by `sessionId`.
|
|
5610
|
-
*
|
|
5611
|
-
* Flow:
|
|
5612
|
-
* 1. Check the currently running app.
|
|
5613
|
-
* 2. If it is already the target, return immediately.
|
|
5614
|
-
* 3. If a different app is running (not dashboard), close it first.
|
|
5615
|
-
* 4. Open the target app.
|
|
5616
|
-
* 5. Poll until the device confirms the target app is running.
|
|
5617
|
-
*/
|
|
5618
|
-
/**
|
|
5619
|
-
* @param onConfirmOnDevice Called BEFORE OpenAppCommand is issued — the
|
|
5620
|
-
* device is about to display "Open <app>" on screen and wait for the
|
|
5621
|
-
* user's button press. UI consumers should show their "open app" prompt
|
|
5622
|
-
* in response. NOT called when the target app is already open (no user
|
|
5623
|
-
* interaction needed in that case).
|
|
5624
|
-
*
|
|
5625
|
-
* Important: OpenAppCommand is blocking. It does not resolve until the user
|
|
5626
|
-
* has physically confirmed on the device, so anything that runs AFTER
|
|
5627
|
-
* `await this._openApp(...)` lands AFTER the prompt is already gone.
|
|
5628
|
-
* Hence the callback must fire BEFORE that await.
|
|
5629
|
-
*/
|
|
5630
|
-
async ensureAppOpen(sessionId, targetAppName, onConfirmOnDevice) {
|
|
5631
|
-
const currentApp = await this._getCurrentApp(sessionId);
|
|
5632
|
-
if (currentApp === targetAppName) {
|
|
5633
|
-
return;
|
|
5634
|
-
}
|
|
5635
|
-
if (!this._isDashboard(currentApp)) {
|
|
5636
|
-
await this._closeCurrentApp(sessionId);
|
|
5637
|
-
await this._waitForApp(sessionId, DASHBOARD_APP_NAME);
|
|
5638
|
-
}
|
|
5639
|
-
onConfirmOnDevice?.();
|
|
5640
|
-
await this._openApp(sessionId, targetAppName);
|
|
5641
|
-
await this._waitForApp(sessionId, targetAppName);
|
|
5642
|
-
}
|
|
5643
|
-
// ---------------------------------------------------------------------------
|
|
5644
|
-
// Private helpers
|
|
5645
|
-
// ---------------------------------------------------------------------------
|
|
5646
|
-
async _getCurrentApp(sessionId) {
|
|
5647
|
-
const result = await this._dmk.sendCommand({
|
|
5648
|
-
sessionId,
|
|
5649
|
-
command: new GetAppAndVersionCommand()
|
|
5650
|
-
});
|
|
5651
|
-
if (isSuccessCommandResult(result)) {
|
|
5652
|
-
debugLog("[AppManager] currentApp:", result.data.name);
|
|
5653
|
-
return result.data.name;
|
|
5654
|
-
}
|
|
5655
|
-
const errResult = result;
|
|
5656
|
-
const dmkErr = errResult.error ?? {};
|
|
5657
|
-
const original = dmkErr.originalError;
|
|
5658
|
-
debugLog(
|
|
5659
|
-
"[AppManager] _getCurrentApp failed sessionId=",
|
|
5660
|
-
sessionId,
|
|
5661
|
-
"tag=",
|
|
5662
|
-
dmkErr._tag,
|
|
5663
|
-
"errorCode=",
|
|
5664
|
-
dmkErr.errorCode,
|
|
5665
|
-
"message=",
|
|
5666
|
-
dmkErr.message,
|
|
5667
|
-
"originalErrorMessage=",
|
|
5668
|
-
original?.message ?? String(original ?? "")
|
|
5669
|
-
);
|
|
5670
|
-
throw Object.assign(
|
|
5671
|
-
new Error(
|
|
5672
|
-
dmkErr.message ?? "Failed to get current app from device"
|
|
5673
|
-
),
|
|
5674
|
-
{
|
|
5675
|
-
_tag: dmkErr._tag,
|
|
5676
|
-
errorCode: dmkErr.errorCode,
|
|
5677
|
-
originalError: original
|
|
5678
|
-
}
|
|
5679
|
-
);
|
|
5680
|
-
}
|
|
5681
|
-
async _openApp(sessionId, appName) {
|
|
5682
|
-
const result = await this._dmk.sendCommand({
|
|
5683
|
-
sessionId,
|
|
5684
|
-
command: new OpenAppCommand({ appName })
|
|
5685
|
-
});
|
|
5686
|
-
if (!isSuccessCommandResult(result)) {
|
|
5687
|
-
const dmkErr = result.error;
|
|
5688
|
-
const errorCode = "errorCode" in dmkErr && dmkErr.errorCode != null ? String(dmkErr.errorCode) : "";
|
|
5689
|
-
const message = "message" in dmkErr && typeof dmkErr.message === "string" ? dmkErr.message : "";
|
|
5690
|
-
debugLog(
|
|
5691
|
-
"[AppManager] openApp failed:",
|
|
5692
|
-
appName,
|
|
5693
|
-
"errorCode:",
|
|
5694
|
-
errorCode,
|
|
5695
|
-
"tag:",
|
|
5696
|
-
dmkErr._tag
|
|
5697
|
-
);
|
|
5698
|
-
let code;
|
|
5699
|
-
if (errorCode === "6807" || /unknown application/i.test(message)) {
|
|
5700
|
-
code = HardwareErrorCode8.AppNotInstalled;
|
|
5701
|
-
} else if (errorCode === "5501" || dmkErr._tag === "ActionRefusedError") {
|
|
5702
|
-
code = HardwareErrorCode8.UserRejected;
|
|
5703
|
-
}
|
|
5704
|
-
throw Object.assign(new Error(`Failed to open "${appName}"`), {
|
|
5705
|
-
_tag: ERROR_TAG.OpenAppCommand,
|
|
5706
|
-
code,
|
|
5707
|
-
errorCode,
|
|
5708
|
-
statusCode: errorCode,
|
|
5709
|
-
appName,
|
|
5710
|
-
originalError: dmkErr
|
|
5711
|
-
});
|
|
5712
|
-
}
|
|
5713
|
-
}
|
|
5714
|
-
async _closeCurrentApp(sessionId) {
|
|
5715
|
-
debugLog("[AppManager] closeCurrentApp");
|
|
5716
|
-
await this._dmk.sendCommand({
|
|
5717
|
-
sessionId,
|
|
5718
|
-
command: new CloseAppCommand()
|
|
5719
|
-
});
|
|
5720
|
-
}
|
|
5721
|
-
/**
|
|
5722
|
-
* Poll the device until the expected app is reported as running,
|
|
5723
|
-
* or throw after `_maxRetries` attempts.
|
|
5724
|
-
*/
|
|
5725
|
-
async _waitForApp(sessionId, expectedAppName) {
|
|
5726
|
-
let lastSeen = "";
|
|
5727
|
-
for (let i = 0; i < this._maxRetries; i++) {
|
|
5728
|
-
await this._wait();
|
|
5729
|
-
const current = await this._getCurrentApp(sessionId);
|
|
5730
|
-
lastSeen = current;
|
|
5731
|
-
if (current === expectedAppName) {
|
|
5732
|
-
return;
|
|
5733
|
-
}
|
|
5734
|
-
}
|
|
5735
|
-
debugLog(
|
|
5736
|
-
"[AppManager] waitForApp exhausted: expected=",
|
|
5737
|
-
expectedAppName,
|
|
5738
|
-
"lastSeen=",
|
|
5739
|
-
lastSeen
|
|
5740
|
-
);
|
|
5741
|
-
throw new Error(
|
|
5742
|
-
`Ledger: failed to open "${expectedAppName}" after ${this._maxRetries} retries (last seen: ${lastSeen})`
|
|
5743
|
-
);
|
|
5744
|
-
}
|
|
5745
|
-
_isDashboard(appName) {
|
|
5746
|
-
return appName === DASHBOARD_APP_NAME;
|
|
5747
|
-
}
|
|
5748
|
-
_wait() {
|
|
5749
|
-
return new Promise((resolve) => setTimeout(resolve, this._waitMs));
|
|
5750
|
-
}
|
|
5751
|
-
};
|
|
5752
4925
|
export {
|
|
5753
4926
|
AppManager,
|
|
5754
4927
|
DmkTransport,
|
|
@@ -5759,7 +4932,6 @@ export {
|
|
|
5759
4932
|
SignerEth,
|
|
5760
4933
|
SignerManager,
|
|
5761
4934
|
SignerSol,
|
|
5762
|
-
SignerZcash,
|
|
5763
4935
|
debugLog,
|
|
5764
4936
|
deviceActionToPromise,
|
|
5765
4937
|
isDeviceLockedError,
|