@onekeyfe/hwk-ledger-adapter 1.2.3-alpha.3 → 1.2.3-alpha.5
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 -1554
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +778 -1569
- package/dist/index.mjs.map +1 -1
- package/package.json +9 -13
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,21 +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
|
-
};
|
|
451
|
+
return { code, message: enrichErrorMessage(code, originalMessage), appName };
|
|
467
452
|
}
|
|
468
453
|
|
|
469
454
|
// src/adapter/methods/allNetworkGetAddress.ts
|
|
470
|
-
import {
|
|
471
|
-
HardwareErrorCode as HardwareErrorCode2,
|
|
472
|
-
failure,
|
|
473
|
-
resolveHardwareOperationTarget,
|
|
474
|
-
runAllNetworkGetAddress
|
|
475
|
-
} from "@onekeyfe/hwk-adapter-core";
|
|
455
|
+
import { HardwareErrorCode as HardwareErrorCode2, failure, runAllNetworkGetAddress } from "@onekeyfe/hwk-adapter-core";
|
|
476
456
|
|
|
477
457
|
// src/utils/sdkEventBus.ts
|
|
478
458
|
var listeners = /* @__PURE__ */ new Set();
|
|
@@ -529,81 +509,57 @@ var LEDGER_BTC_NETWORK_COIN_MAP = {
|
|
|
529
509
|
var LEDGER_UNSUPPORTED_ALLNETWORK_NETWORKS = /* @__PURE__ */ new Set(["doge", "dogecoin"]);
|
|
530
510
|
function createAllNetworkGetAddress({
|
|
531
511
|
callChain,
|
|
532
|
-
getChainFingerprint
|
|
533
|
-
retainOperation,
|
|
534
|
-
errorToFailure
|
|
512
|
+
getChainFingerprint
|
|
535
513
|
}) {
|
|
536
514
|
return async function allNetworkGetAddress(connectId, _deviceId, params) {
|
|
537
|
-
debugLog("[LedgerAdapter][REQ]", {
|
|
538
|
-
method: "allNetworkGetAddress",
|
|
539
|
-
connectId,
|
|
540
|
-
itemCount: params.bundle.length
|
|
541
|
-
});
|
|
542
|
-
const target = resolveHardwareOperationTarget(connectId, params.operationId, "ledger");
|
|
543
|
-
if (!target.success) return target;
|
|
544
|
-
const effectiveTargetId = target.payload.targetId ?? "";
|
|
545
|
-
let releaseOperationRetention;
|
|
546
|
-
try {
|
|
547
|
-
releaseOperationRetention = target.payload.operationId ? retainOperation(target.payload.operationId) : void 0;
|
|
548
|
-
} catch (error) {
|
|
549
|
-
return errorToFailure(error);
|
|
550
|
-
}
|
|
515
|
+
debugLog("[LedgerAdapter][REQ]", { method: "allNetworkGetAddress", connectId, params });
|
|
551
516
|
const installContext = {};
|
|
552
517
|
const commonParams = {
|
|
553
|
-
autoInstallApp: params.autoInstallApp
|
|
554
|
-
operationId: target.payload.operationId,
|
|
555
|
-
knownConnections: params.knownConnections,
|
|
556
|
-
extra: params.extra,
|
|
557
|
-
allowDeviceSelection: params.allowDeviceSelection
|
|
518
|
+
autoInstallApp: params.autoInstallApp
|
|
558
519
|
};
|
|
559
520
|
const chainFingerprints = /* @__PURE__ */ new Map();
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
method,
|
|
574
|
-
item,
|
|
575
|
-
commonParams,
|
|
576
|
-
installContext
|
|
577
|
-
);
|
|
578
|
-
},
|
|
579
|
-
attachIdentity: async ({ item, chain, payload }) => attachLedgerIdentity(
|
|
580
|
-
getChainFingerprint,
|
|
581
|
-
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,
|
|
582
534
|
item,
|
|
583
|
-
|
|
584
|
-
payload,
|
|
585
|
-
chainFingerprints,
|
|
535
|
+
commonParams,
|
|
586
536
|
installContext
|
|
587
|
-
)
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
}
|
|
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;
|
|
607
563
|
};
|
|
608
564
|
}
|
|
609
565
|
function isTopLevelAllNetworkFailure(response) {
|
|
@@ -611,7 +567,7 @@ function isTopLevelAllNetworkFailure(response) {
|
|
|
611
567
|
return false;
|
|
612
568
|
}
|
|
613
569
|
const code = response.payload?.code;
|
|
614
|
-
return code === HardwareErrorCode2.DeviceMismatch || code === HardwareErrorCode2.
|
|
570
|
+
return code === HardwareErrorCode2.DeviceMismatch || code === HardwareErrorCode2.UserAborted || code === HardwareErrorCode2.UserRejected;
|
|
615
571
|
}
|
|
616
572
|
function getItemDeviceId(item) {
|
|
617
573
|
const { deviceId } = item;
|
|
@@ -641,13 +597,8 @@ function normalizeLedgerAllNetworkItem(method, item) {
|
|
|
641
597
|
const coin = LEDGER_BTC_NETWORK_COIN_MAP[item.network];
|
|
642
598
|
return coin ? { ...item, coin } : item;
|
|
643
599
|
}
|
|
644
|
-
async function attachLedgerIdentity(getChainFingerprint, connectId, item, chain, payload, chainFingerprints
|
|
645
|
-
const fingerprint = getItemDeviceId(item) || chainFingerprints.get(chain) || await bootstrapChainFingerprint(
|
|
646
|
-
getChainFingerprint,
|
|
647
|
-
context.connection?.connectId ?? connectId,
|
|
648
|
-
chain,
|
|
649
|
-
context
|
|
650
|
-
);
|
|
600
|
+
async function attachLedgerIdentity(getChainFingerprint, connectId, item, chain, payload, chainFingerprints) {
|
|
601
|
+
const fingerprint = getItemDeviceId(item) || chainFingerprints.get(chain) || await bootstrapChainFingerprint(getChainFingerprint, connectId, chain);
|
|
651
602
|
if (!fingerprint) {
|
|
652
603
|
return buildFingerprintBootstrapFailure(item, chain);
|
|
653
604
|
}
|
|
@@ -668,8 +619,8 @@ async function attachLedgerIdentity(getChainFingerprint, connectId, item, chain,
|
|
|
668
619
|
}
|
|
669
620
|
};
|
|
670
621
|
}
|
|
671
|
-
async function bootstrapChainFingerprint(getChainFingerprint, connectId, chain
|
|
672
|
-
const response = await getChainFingerprint(connectId, chain
|
|
622
|
+
async function bootstrapChainFingerprint(getChainFingerprint, connectId, chain) {
|
|
623
|
+
const response = await getChainFingerprint(connectId, chain);
|
|
673
624
|
return response.success ? response.payload : "";
|
|
674
625
|
}
|
|
675
626
|
function buildFingerprintBootstrapFailure(item, chain) {
|
|
@@ -774,27 +725,8 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
774
725
|
constructor(connector, options) {
|
|
775
726
|
this.vendor = "ledger";
|
|
776
727
|
this.emitter = new TypedEventEmitter();
|
|
777
|
-
this._operations = new OperationRegistry({
|
|
778
|
-
vendor: "ledger",
|
|
779
|
-
onEnded: (operation, reason) => {
|
|
780
|
-
const binding = this._pendingOperationBindings.get(operation.operationId);
|
|
781
|
-
if (binding?.selectedConnection?.requestId === this._bindingSelectionRequestId) {
|
|
782
|
-
this._finishBleBinding("cancelled");
|
|
783
|
-
}
|
|
784
|
-
this._pendingOperationBindings.delete(operation.operationId);
|
|
785
|
-
this.emitter.emit(SDK.OPERATION_ENDED, {
|
|
786
|
-
type: SDK.OPERATION_ENDED,
|
|
787
|
-
payload: { operationId: operation.operationId, reason }
|
|
788
|
-
});
|
|
789
|
-
if (reason === "timeout") {
|
|
790
|
-
void this._releaseOperationConnection(operation);
|
|
791
|
-
}
|
|
792
|
-
}
|
|
793
|
-
});
|
|
794
728
|
this._discoveredDevices = /* @__PURE__ */ new Map();
|
|
795
729
|
this._sessions = /* @__PURE__ */ new Map();
|
|
796
|
-
this._pendingOperationBindings = /* @__PURE__ */ new Map();
|
|
797
|
-
this._verifiedBleReconnectTargets = /* @__PURE__ */ new Map();
|
|
798
730
|
this._uiRegistry = new UiRequestRegistry();
|
|
799
731
|
// BTC App rejects account index >= 100 unless display=true. Cached per
|
|
800
732
|
// adapter instance: first 100+ path asks the user once via UI request,
|
|
@@ -808,30 +740,10 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
808
740
|
this._deviceAuthenticityQueueTail = Promise.resolve();
|
|
809
741
|
// Shared across concurrent callers — only `cancel()` aborts.
|
|
810
742
|
this._doConnectAbortController = null;
|
|
811
|
-
this._unsettledConnectorOperations = /* @__PURE__ */ new Map();
|
|
812
|
-
this._connectorIdleWaiters = /* @__PURE__ */ new Set();
|
|
813
|
-
this._resetPromise = null;
|
|
814
|
-
this._stateGeneration = 0;
|
|
815
|
-
this._connectorTeardownTail = Promise.resolve();
|
|
816
|
-
this._pendingConnectorTeardowns = 0;
|
|
817
|
-
this._activeOperationJobs = /* @__PURE__ */ new Set();
|
|
818
|
-
this._pendingOperationDisconnects = /* @__PURE__ */ new Set();
|
|
819
743
|
this._installProgressLastEmittedValue = -Infinity;
|
|
820
744
|
this.allNetworkGetAddress = createAllNetworkGetAddress({
|
|
821
745
|
callChain: this.callChain.bind(this),
|
|
822
|
-
getChainFingerprint:
|
|
823
|
-
try {
|
|
824
|
-
const fingerprint = await this._computeChainFingerprint(
|
|
825
|
-
chain,
|
|
826
|
-
(method, params) => this.connectorCall(connectId, method, params, void 0, void 0, void 0, context)
|
|
827
|
-
);
|
|
828
|
-
return success(fingerprint);
|
|
829
|
-
} catch (error) {
|
|
830
|
-
return this.errorToFailure(error);
|
|
831
|
-
}
|
|
832
|
-
},
|
|
833
|
-
retainOperation: (operationId) => this._operations.retain(operationId),
|
|
834
|
-
errorToFailure: (error) => this.errorToFailure(error)
|
|
746
|
+
getChainFingerprint: (connectId, chain) => this.getChainFingerprint(connectId, "", chain)
|
|
835
747
|
});
|
|
836
748
|
// ---------------------------------------------------------------------------
|
|
837
749
|
// Private helpers
|
|
@@ -841,7 +753,7 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
841
753
|
*
|
|
842
754
|
* - If a session already exists for the given connectId, reuse it.
|
|
843
755
|
* - If ANY session exists (Ledger IDs are ephemeral), reuse it.
|
|
844
|
-
* - Otherwise: search →
|
|
756
|
+
* - Otherwise: search → exactly 1 USB device auto-connects; multiple or none throws.
|
|
845
757
|
*/
|
|
846
758
|
// Mutex for ensureConnected — prevents concurrent calls from establishing duplicate connections
|
|
847
759
|
this._connectingPromise = null;
|
|
@@ -858,12 +770,6 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
858
770
|
});
|
|
859
771
|
};
|
|
860
772
|
this.deviceDisconnectHandler = (data) => {
|
|
861
|
-
const activeOperation = this._operations.findActiveByConnectionKey(data.connectId);
|
|
862
|
-
if (activeOperation && this._activeOperationJobs.has(activeOperation.operationId)) {
|
|
863
|
-
this._pendingOperationDisconnects.add(activeOperation.operationId);
|
|
864
|
-
} else {
|
|
865
|
-
this._operations.endByConnectionKey(data.connectId, "disconnect");
|
|
866
|
-
}
|
|
867
773
|
this._discoveredDevices.delete(data.connectId);
|
|
868
774
|
this._sessions.delete(data.connectId);
|
|
869
775
|
this.emitter.emit(DEVICE.DISCONNECT, {
|
|
@@ -920,28 +826,11 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
920
826
|
this._jobQueue = new DeviceJobQueue();
|
|
921
827
|
this.registerEventListeners();
|
|
922
828
|
}
|
|
923
|
-
_finishBleBinding(status) {
|
|
924
|
-
const selectionRequestId = this._bindingSelectionRequestId;
|
|
925
|
-
this._bindingSelectionRequestId = void 0;
|
|
926
|
-
if (selectionRequestId)
|
|
927
|
-
this.emitter.emit(UI_REQUEST.DEVICE_BINDING_STATUS, {
|
|
928
|
-
type: UI_REQUEST.DEVICE_BINDING_STATUS,
|
|
929
|
-
payload: { selectionRequestId, status }
|
|
930
|
-
});
|
|
931
|
-
}
|
|
932
|
-
_isBleConnection() {
|
|
933
|
-
return isLedgerBleConnectionType(this._activeConnectionType ?? this.connector.connectionType);
|
|
934
|
-
}
|
|
935
829
|
// Transport
|
|
936
830
|
get activeTransport() {
|
|
937
|
-
return this.
|
|
831
|
+
return isLedgerBleConnectionType(this.connector.connectionType) ? "ble" : "hid";
|
|
938
832
|
}
|
|
939
833
|
getAvailableTransports() {
|
|
940
|
-
if (this.connector.availableTransports) {
|
|
941
|
-
return this.connector.availableTransports.map(
|
|
942
|
-
(transport) => transport === "ble" ? "ble" : "hid"
|
|
943
|
-
);
|
|
944
|
-
}
|
|
945
834
|
return this.activeTransport ? [this.activeTransport] : [];
|
|
946
835
|
}
|
|
947
836
|
// Connector is bound at construction; switching requires a new adapter.
|
|
@@ -958,39 +847,21 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
958
847
|
* The next operation will re-discover and re-connect automatically.
|
|
959
848
|
*/
|
|
960
849
|
resetState() {
|
|
961
|
-
void this._resetStateAndDisconnectSessions();
|
|
962
|
-
}
|
|
963
|
-
_resetStateAndDisconnectSessions() {
|
|
964
|
-
if (this._resetPromise) return this._resetPromise;
|
|
965
|
-
const sessionIds = new Set(this._sessions.values());
|
|
966
|
-
this._stateGeneration += 1;
|
|
967
|
-
this._finishBleBinding("cancelled");
|
|
968
|
-
this._operations.endAll("runtime-reset");
|
|
969
|
-
this._doConnectAbortController?.abort();
|
|
970
850
|
this._discoveredDevices.clear();
|
|
971
851
|
this._sessions.clear();
|
|
972
|
-
this._verifiedBleReconnectTargets.clear();
|
|
973
852
|
this._connectingPromise = null;
|
|
974
|
-
this._doConnectAbortController = null;
|
|
975
853
|
this._uiRegistry.reset();
|
|
976
854
|
this._jobQueue.clear();
|
|
977
855
|
this._btcHighIndexConfirmedThisSession = false;
|
|
978
|
-
const resetPromise = this._runConnectorTeardown(async () => {
|
|
979
|
-
for (const sessionId of sessionIds) {
|
|
980
|
-
await this.connector.disconnect(sessionId).catch(() => void 0);
|
|
981
|
-
}
|
|
982
|
-
});
|
|
983
|
-
this._resetPromise = resetPromise;
|
|
984
|
-
return resetPromise.finally(() => {
|
|
985
|
-
if (this._resetPromise === resetPromise) {
|
|
986
|
-
this._resetPromise = null;
|
|
987
|
-
}
|
|
988
|
-
});
|
|
989
856
|
}
|
|
990
857
|
async dispose() {
|
|
991
|
-
|
|
858
|
+
this._uiRegistry.reset();
|
|
859
|
+
this._jobQueue.clear();
|
|
992
860
|
this.unregisterEventListeners();
|
|
993
861
|
this.connector.reset();
|
|
862
|
+
this._discoveredDevices.clear();
|
|
863
|
+
this._sessions.clear();
|
|
864
|
+
this._btcHighIndexConfirmedThisSession = false;
|
|
994
865
|
this.emitter.removeAllListeners();
|
|
995
866
|
}
|
|
996
867
|
uiResponse(response) {
|
|
@@ -1000,32 +871,17 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
1000
871
|
// Device management
|
|
1001
872
|
// ---------------------------------------------------------------------------
|
|
1002
873
|
async searchDevices(options) {
|
|
1003
|
-
return this._searchDevices(options);
|
|
1004
|
-
}
|
|
1005
|
-
async _searchDevices(options, signal) {
|
|
1006
874
|
debugLog("[LedgerAdapter][REQ]", { method: "searchDevices", params: options });
|
|
1007
875
|
try {
|
|
1008
876
|
if (options?.resetSession) {
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
if (signal) _LedgerAdapter._throwIfAborted(signal);
|
|
1015
|
-
const stateGeneration = this._stateGeneration;
|
|
1016
|
-
const devices = await this.connector.searchDevices(
|
|
1017
|
-
options?.transportType ? {
|
|
1018
|
-
transportType: options.transportType,
|
|
1019
|
-
waitForAll: options.waitForAllTransports
|
|
1020
|
-
} : void 0
|
|
1021
|
-
);
|
|
1022
|
-
if (signal) _LedgerAdapter._throwIfAborted(signal);
|
|
1023
|
-
if (stateGeneration !== this._stateGeneration) {
|
|
1024
|
-
throw createHwkError({
|
|
1025
|
-
code: HardwareErrorCode3.UserAborted,
|
|
1026
|
-
message: "Ledger discovery was reset"
|
|
1027
|
-
});
|
|
877
|
+
this._doConnectAbortController?.abort();
|
|
878
|
+
this._sessions.clear();
|
|
879
|
+
this._connectingPromise = null;
|
|
880
|
+
this._doConnectAbortController = null;
|
|
881
|
+
this._btcHighIndexConfirmedThisSession = false;
|
|
1028
882
|
}
|
|
883
|
+
await this._ensureDevicePermission();
|
|
884
|
+
const devices = await this.connector.searchDevices();
|
|
1029
885
|
this._discoveredDevices.clear();
|
|
1030
886
|
for (const d of devices) {
|
|
1031
887
|
if (d.connectId) {
|
|
@@ -1033,7 +889,7 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
1033
889
|
}
|
|
1034
890
|
}
|
|
1035
891
|
if (this._discoveredDevices.size === 0) {
|
|
1036
|
-
await this._ensureDevicePermission(
|
|
892
|
+
await this._ensureDevicePermission();
|
|
1037
893
|
}
|
|
1038
894
|
const result = Array.from(this._discoveredDevices.values());
|
|
1039
895
|
debugLog("[LedgerAdapter][RES]", {
|
|
@@ -1052,285 +908,41 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
1052
908
|
throw err;
|
|
1053
909
|
}
|
|
1054
910
|
}
|
|
1055
|
-
async searchDeviceTargets(options) {
|
|
1056
|
-
const devices = await this.searchDevices(options);
|
|
1057
|
-
return devices.map((device) => ({
|
|
1058
|
-
searchTargetId: device.connectId,
|
|
1059
|
-
searchTargetReusePolicy: resolveSearchTargetReusePolicy(device),
|
|
1060
|
-
vendor: "ledger",
|
|
1061
|
-
connectionType: device.connectionType,
|
|
1062
|
-
kind: "physical",
|
|
1063
|
-
label: device.label,
|
|
1064
|
-
model: device.model,
|
|
1065
|
-
modelName: device.modelName,
|
|
1066
|
-
serialNumber: device.serialNumber
|
|
1067
|
-
}));
|
|
1068
|
-
}
|
|
1069
|
-
async listConnectionTargets(options) {
|
|
1070
|
-
const targets = await this.searchDeviceTargets(options);
|
|
1071
|
-
return targets.map(({ searchTargetId, ...target }) => ({
|
|
1072
|
-
...target,
|
|
1073
|
-
targetId: searchTargetId
|
|
1074
|
-
}));
|
|
1075
|
-
}
|
|
1076
911
|
// USB single-session invariant: evict all sessions, best-effort (see connectDevice).
|
|
1077
|
-
async _evictAllSessions(
|
|
1078
|
-
this._operations.endAll("explicit", preserveOperationId);
|
|
912
|
+
async _evictAllSessions() {
|
|
1079
913
|
if (this._sessions.size === 0) return;
|
|
1080
914
|
const stale = [...this._sessions.values()];
|
|
1081
915
|
this._sessions.clear();
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
} catch {
|
|
1087
|
-
}
|
|
916
|
+
for (const sid of stale) {
|
|
917
|
+
try {
|
|
918
|
+
await this.connector.disconnect(sid);
|
|
919
|
+
} catch {
|
|
1088
920
|
}
|
|
1089
|
-
}
|
|
921
|
+
}
|
|
1090
922
|
}
|
|
1091
923
|
static _createDeviceBusyError(method) {
|
|
1092
924
|
return Object.assign(new Error(`Ledger device is busy while calling ${method}`), {
|
|
1093
925
|
code: HardwareErrorCode3.DeviceBusy
|
|
1094
926
|
});
|
|
1095
927
|
}
|
|
1096
|
-
async connectDevice(
|
|
1097
|
-
try {
|
|
1098
|
-
return await this._jobQueue.enqueue(
|
|
1099
|
-
searchTargetId || "__ledger_connect__",
|
|
1100
|
-
async () => {
|
|
1101
|
-
const connected = await this._connectTarget(searchTargetId);
|
|
1102
|
-
if (!connected.success) return connected;
|
|
1103
|
-
return this._createOperation(searchTargetId, connected.payload);
|
|
1104
|
-
},
|
|
1105
|
-
{
|
|
1106
|
-
label: "connectDevice",
|
|
1107
|
-
rejectIfBusy: true,
|
|
1108
|
-
busyError: _LedgerAdapter._createDeviceBusyError("connectDevice")
|
|
1109
|
-
}
|
|
1110
|
-
);
|
|
1111
|
-
} catch (error) {
|
|
1112
|
-
return this.errorToFailure(error);
|
|
1113
|
-
}
|
|
1114
|
-
}
|
|
1115
|
-
async bindBleDevice(params) {
|
|
1116
|
-
if (params.identity.vendor !== "ledger" || !params.identity.value) {
|
|
1117
|
-
return failure2(HardwareErrorCode3.InvalidParams, "Ledger wallet identity is required");
|
|
1118
|
-
}
|
|
1119
|
-
const { chain, value: expectedFingerprint } = params.identity;
|
|
1120
|
-
try {
|
|
1121
|
-
return await this._jobQueue.enqueue(
|
|
1122
|
-
expectedFingerprint,
|
|
1123
|
-
async (signal) => {
|
|
1124
|
-
if (!this.getAvailableTransports().includes("ble")) {
|
|
1125
|
-
throw createHwkError({
|
|
1126
|
-
code: HardwareErrorCode3.TransportNotAvailable,
|
|
1127
|
-
message: "Ledger Bluetooth transport is not available"
|
|
1128
|
-
});
|
|
1129
|
-
}
|
|
1130
|
-
this._activeConnectionType = "ble";
|
|
1131
|
-
await this._ensureDevicePermission(void 0, void 0, signal);
|
|
1132
|
-
const attempt = {
|
|
1133
|
-
extra: params.extra,
|
|
1134
|
-
bindingReason: "manual-rebind"
|
|
1135
|
-
};
|
|
1136
|
-
try {
|
|
1137
|
-
for (; ; ) {
|
|
1138
|
-
const connectId = await this._connectFirstOrSelect(
|
|
1139
|
-
[],
|
|
1140
|
-
void 0,
|
|
1141
|
-
true,
|
|
1142
|
-
void 0,
|
|
1143
|
-
attempt,
|
|
1144
|
-
signal
|
|
1145
|
-
);
|
|
1146
|
-
const sessionId = this._sessions.get(connectId);
|
|
1147
|
-
if (!sessionId) {
|
|
1148
|
-
throw createHwkError({
|
|
1149
|
-
code: HardwareErrorCode3.DeviceDisconnected,
|
|
1150
|
-
message: "Selected Ledger connection ended"
|
|
1151
|
-
});
|
|
1152
|
-
}
|
|
1153
|
-
let saved = false;
|
|
1154
|
-
try {
|
|
1155
|
-
const installContext = {
|
|
1156
|
-
connection: { connectId, sessionId }
|
|
1157
|
-
};
|
|
1158
|
-
const fingerprint = await this._computeChainFingerprint(
|
|
1159
|
-
chain,
|
|
1160
|
-
(method, callParams) => this._runConnectorCall(
|
|
1161
|
-
connectId,
|
|
1162
|
-
method,
|
|
1163
|
-
callParams,
|
|
1164
|
-
signal,
|
|
1165
|
-
void 0,
|
|
1166
|
-
void 0,
|
|
1167
|
-
{ autoInstallApp: true },
|
|
1168
|
-
installContext
|
|
1169
|
-
)
|
|
1170
|
-
);
|
|
1171
|
-
if (fingerprint !== expectedFingerprint) {
|
|
1172
|
-
attempt.rejectedConnectIds ?? (attempt.rejectedConnectIds = /* @__PURE__ */ new Set());
|
|
1173
|
-
attempt.rejectedConnectIds.add(connectId);
|
|
1174
|
-
attempt.rejectedConnectId = connectId;
|
|
1175
|
-
} else {
|
|
1176
|
-
const persisted = await this._publishVerifiedBleBinding(
|
|
1177
|
-
connectId,
|
|
1178
|
-
chain,
|
|
1179
|
-
fingerprint,
|
|
1180
|
-
attempt,
|
|
1181
|
-
void 0,
|
|
1182
|
-
signal
|
|
1183
|
-
);
|
|
1184
|
-
if (!persisted) {
|
|
1185
|
-
throw createHwkError({
|
|
1186
|
-
code: HardwareErrorCode3.UnknownError,
|
|
1187
|
-
message: "Bluetooth binding could not be saved",
|
|
1188
|
-
origin: "host"
|
|
1189
|
-
});
|
|
1190
|
-
}
|
|
1191
|
-
saved = true;
|
|
1192
|
-
return success(connectId);
|
|
1193
|
-
}
|
|
1194
|
-
} finally {
|
|
1195
|
-
if (!saved && this._sessions.get(connectId) === sessionId) {
|
|
1196
|
-
this._sessions.delete(connectId);
|
|
1197
|
-
const teardown = this._runConnectorTeardown(
|
|
1198
|
-
() => this.connector.disconnect(sessionId)
|
|
1199
|
-
).catch(() => void 0);
|
|
1200
|
-
if (!signal.aborted) await teardown;
|
|
1201
|
-
}
|
|
1202
|
-
}
|
|
1203
|
-
}
|
|
1204
|
-
} catch (error) {
|
|
1205
|
-
this._finishBleBinding(signal.aborted ? "cancelled" : "failed");
|
|
1206
|
-
throw error;
|
|
1207
|
-
}
|
|
1208
|
-
},
|
|
1209
|
-
{
|
|
1210
|
-
label: "bindBleDevice",
|
|
1211
|
-
rejectIfBusy: true,
|
|
1212
|
-
busyError: _LedgerAdapter._createDeviceBusyError("bindBleDevice")
|
|
1213
|
-
}
|
|
1214
|
-
);
|
|
1215
|
-
} catch (error) {
|
|
1216
|
-
return this.errorToFailure(error);
|
|
1217
|
-
}
|
|
1218
|
-
}
|
|
1219
|
-
async acquireOperation(connectId, context) {
|
|
1220
|
-
try {
|
|
1221
|
-
return await this._jobQueue.enqueue(
|
|
1222
|
-
connectId || "__ledger_acquire__",
|
|
1223
|
-
async (signal) => {
|
|
1224
|
-
const transport = this._isBleConnection() ? "ble" : "usb";
|
|
1225
|
-
const hint = context.knownConnections?.find(
|
|
1226
|
-
(connection) => connection.transport === transport
|
|
1227
|
-
);
|
|
1228
|
-
const target = hint && hint.transport !== "qr" ? hint.connectId : connectId;
|
|
1229
|
-
await this._ensureDevicePermission(target, void 0, signal);
|
|
1230
|
-
const attempt = {
|
|
1231
|
-
...context,
|
|
1232
|
-
extra: context.extra ? { ...context.extra } : void 0
|
|
1233
|
-
};
|
|
1234
|
-
try {
|
|
1235
|
-
const resolvedConnectId = await this.ensureConnected(
|
|
1236
|
-
target,
|
|
1237
|
-
signal,
|
|
1238
|
-
true,
|
|
1239
|
-
void 0,
|
|
1240
|
-
attempt
|
|
1241
|
-
);
|
|
1242
|
-
_LedgerAdapter._throwIfAborted(signal);
|
|
1243
|
-
const result = this._createOperation(connectId, resolvedConnectId);
|
|
1244
|
-
if (result.success && attempt.selectedConnection) {
|
|
1245
|
-
this._pendingOperationBindings.set(result.payload, attempt);
|
|
1246
|
-
}
|
|
1247
|
-
return result;
|
|
1248
|
-
} catch (error) {
|
|
1249
|
-
this._finishBleBinding(signal.aborted ? "cancelled" : "failed");
|
|
1250
|
-
throw error;
|
|
1251
|
-
}
|
|
1252
|
-
},
|
|
1253
|
-
{
|
|
1254
|
-
label: "acquireOperation",
|
|
1255
|
-
rejectIfBusy: true,
|
|
1256
|
-
busyError: _LedgerAdapter._createDeviceBusyError("acquireOperation")
|
|
1257
|
-
}
|
|
1258
|
-
);
|
|
1259
|
-
} catch (error) {
|
|
1260
|
-
return this.errorToFailure(error);
|
|
1261
|
-
}
|
|
1262
|
-
}
|
|
1263
|
-
_createOperation(searchTargetId, resolvedConnectId) {
|
|
1264
|
-
this._operations.endByConnectionKey(resolvedConnectId, "explicit");
|
|
1265
|
-
const sessionId = this._sessions.get(resolvedConnectId);
|
|
1266
|
-
if (sessionId) this._operations.endByConnectionKey(sessionId, "explicit");
|
|
1267
|
-
const device = this._discoveredDevices.get(resolvedConnectId) ?? {
|
|
1268
|
-
vendor: "ledger",
|
|
1269
|
-
model: "unknown",
|
|
1270
|
-
firmwareVersion: "",
|
|
1271
|
-
deviceId: "",
|
|
1272
|
-
connectId: resolvedConnectId,
|
|
1273
|
-
connectionType: this.connector.connectionType
|
|
1274
|
-
};
|
|
1275
|
-
const operation = this._operations.create({
|
|
1276
|
-
searchTargetId,
|
|
1277
|
-
connectId: resolvedConnectId,
|
|
1278
|
-
device,
|
|
1279
|
-
connectionKeys: [this._sessions.get(resolvedConnectId) ?? ""]
|
|
1280
|
-
});
|
|
1281
|
-
return success(operation.operationId);
|
|
1282
|
-
}
|
|
1283
|
-
async _connectTarget(connectId, preserveOperationId, signal) {
|
|
928
|
+
async connectDevice(connectId) {
|
|
1284
929
|
debugLog("[LedgerAdapter][REQ]", { method: "connectDevice", connectId, params: { connectId } });
|
|
1285
930
|
try {
|
|
1286
|
-
this.
|
|
1287
|
-
const discoveredType = this._discoveredDevices.get(connectId)?.connectionType;
|
|
1288
|
-
if (discoveredType === "usb" || discoveredType === "ble") {
|
|
1289
|
-
this._activeConnectionType = discoveredType;
|
|
1290
|
-
}
|
|
1291
|
-
if (this._isBleConnection() && !connectId) {
|
|
931
|
+
if (isLedgerBleConnectionType(this.connector.connectionType) && !connectId) {
|
|
1292
932
|
throw Object.assign(new Error("Ledger BLE connectId is required."), {
|
|
1293
933
|
code: HardwareErrorCode3.DeviceNotFound
|
|
1294
934
|
});
|
|
1295
935
|
}
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
if (this.connector.availableTransports && this.connector.availableTransports.length > 1) {
|
|
1299
|
-
await this._evictAllSessions(preserveOperationId);
|
|
1300
|
-
} else if (this._isBleConnection()) {
|
|
1301
|
-
const previousSessionId = this._sessions.get(connectId);
|
|
1302
|
-
this._operations.endByConnectionKey(connectId, "explicit", preserveOperationId);
|
|
1303
|
-
if (previousSessionId) {
|
|
1304
|
-
this._operations.endByConnectionKey(previousSessionId, "explicit", preserveOperationId);
|
|
1305
|
-
this._sessions.delete(connectId);
|
|
1306
|
-
await this.connector.disconnect(previousSessionId).catch(() => void 0);
|
|
1307
|
-
}
|
|
1308
|
-
} else {
|
|
1309
|
-
await this._evictAllSessions(preserveOperationId);
|
|
1310
|
-
}
|
|
1311
|
-
if (signal) _LedgerAdapter._throwIfAborted(signal);
|
|
1312
|
-
const stateGeneration = this._stateGeneration;
|
|
1313
|
-
const releaseOperation = this._retainConnectorOperation(`connect:${connectId}`);
|
|
1314
|
-
let session;
|
|
1315
|
-
try {
|
|
1316
|
-
session = this.connector.availableTransports?.length ? await this.connector.connect(connectId, {
|
|
1317
|
-
transportType: this._activeConnectionType ?? "usb"
|
|
1318
|
-
}) : await this.connector.connect(connectId);
|
|
1319
|
-
if (signal?.aborted || stateGeneration !== this._stateGeneration) {
|
|
1320
|
-
await this.connector.disconnect(session.sessionId).catch(() => void 0);
|
|
1321
|
-
throw Object.assign(new Error("Ledger connection aborted"), {
|
|
1322
|
-
code: HardwareErrorCode3.UserAborted
|
|
1323
|
-
});
|
|
1324
|
-
}
|
|
1325
|
-
} finally {
|
|
1326
|
-
releaseOperation();
|
|
936
|
+
if (!isLedgerBleConnectionType(this.connector.connectionType)) {
|
|
937
|
+
await this._evictAllSessions();
|
|
1327
938
|
}
|
|
1328
|
-
|
|
1329
|
-
this.
|
|
939
|
+
await this._ensureDevicePermission(connectId);
|
|
940
|
+
const session = await this.connector.connect(connectId);
|
|
941
|
+
this._sessions.set(connectId, session.sessionId);
|
|
1330
942
|
if (session.deviceInfo) {
|
|
1331
|
-
this._discoveredDevices.set(
|
|
943
|
+
this._discoveredDevices.set(connectId, session.deviceInfo);
|
|
1332
944
|
}
|
|
1333
|
-
const result = success(
|
|
945
|
+
const result = success(connectId);
|
|
1334
946
|
debugLog("[LedgerAdapter][RES]", { method: "connectDevice", success: true, payload: result });
|
|
1335
947
|
return result;
|
|
1336
948
|
} catch (err) {
|
|
@@ -1343,60 +955,30 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
1343
955
|
return failureResult;
|
|
1344
956
|
}
|
|
1345
957
|
}
|
|
1346
|
-
async
|
|
1347
|
-
const operation = this._operations.find(operationId);
|
|
1348
|
-
if (!operation) {
|
|
1349
|
-
this._operations.resolve(operationId);
|
|
1350
|
-
return;
|
|
1351
|
-
}
|
|
1352
|
-
const endedOperation = this._operations.end(operationId, "explicit");
|
|
1353
|
-
if (!endedOperation) return;
|
|
1354
|
-
const { connectId } = operation;
|
|
958
|
+
async disconnectDevice(connectId) {
|
|
1355
959
|
debugLog("[LedgerAdapter][REQ]", {
|
|
1356
|
-
method: "
|
|
960
|
+
method: "disconnectDevice",
|
|
1357
961
|
connectId,
|
|
1358
962
|
params: { connectId }
|
|
1359
963
|
});
|
|
1360
964
|
try {
|
|
1361
|
-
|
|
1362
|
-
|
|
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 });
|
|
1363
971
|
} catch (err) {
|
|
1364
972
|
const e = err;
|
|
1365
973
|
debugLog("[LedgerAdapter][RES]", {
|
|
1366
|
-
method: "
|
|
974
|
+
method: "disconnectDevice",
|
|
1367
975
|
success: false,
|
|
1368
976
|
error: { message: e?.message, _tag: e?._tag, code: e?.code ?? e?.errorCode }
|
|
1369
977
|
});
|
|
1370
978
|
throw err;
|
|
1371
979
|
}
|
|
1372
980
|
}
|
|
1373
|
-
async
|
|
1374
|
-
const sessionIds = /* @__PURE__ */ new Set();
|
|
1375
|
-
for (const [connectId, sessionId] of this._sessions) {
|
|
1376
|
-
if (connectId === operation.connectId || operation.connectionKeys.includes(connectId) || operation.connectionKeys.includes(sessionId)) {
|
|
1377
|
-
this._sessions.delete(connectId);
|
|
1378
|
-
sessionIds.add(sessionId);
|
|
1379
|
-
}
|
|
1380
|
-
}
|
|
1381
|
-
for (const sessionId of sessionIds) {
|
|
1382
|
-
await this._runConnectorTeardown(
|
|
1383
|
-
() => this.connector.disconnect(sessionId).catch(() => void 0)
|
|
1384
|
-
);
|
|
1385
|
-
}
|
|
1386
|
-
}
|
|
1387
|
-
async _releaseLostOperationConnection(operationId) {
|
|
1388
|
-
const endedOperation = this._operations.end(operationId, "disconnect");
|
|
1389
|
-
if (!endedOperation) return;
|
|
1390
|
-
this._discoveredDevices.delete(endedOperation.connectId);
|
|
1391
|
-
await this._releaseOperationConnection(endedOperation);
|
|
1392
|
-
}
|
|
1393
|
-
async getDeviceInfo(connectIdOrOperationId, deviceId) {
|
|
1394
|
-
let connectId;
|
|
1395
|
-
try {
|
|
1396
|
-
connectId = isHardwareOperationId(connectIdOrOperationId) ? this._operations.resolve(connectIdOrOperationId).connectId : connectIdOrOperationId;
|
|
1397
|
-
} catch (error) {
|
|
1398
|
-
return this.errorToFailure(error);
|
|
1399
|
-
}
|
|
981
|
+
async getDeviceInfo(connectId, deviceId) {
|
|
1400
982
|
debugLog("[LedgerAdapter][REQ]", {
|
|
1401
983
|
method: "getDeviceInfo",
|
|
1402
984
|
connectId,
|
|
@@ -1434,6 +1016,9 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
1434
1016
|
throw err;
|
|
1435
1017
|
}
|
|
1436
1018
|
}
|
|
1019
|
+
getSupportedChains() {
|
|
1020
|
+
return ["evm", "btc", "sol", "tron"];
|
|
1021
|
+
}
|
|
1437
1022
|
// ---------------------------------------------------------------------------
|
|
1438
1023
|
// Chain call helper
|
|
1439
1024
|
// ---------------------------------------------------------------------------
|
|
@@ -1468,21 +1053,13 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
1468
1053
|
if (params && typeof params === "object") {
|
|
1469
1054
|
const {
|
|
1470
1055
|
autoInstallApp,
|
|
1471
|
-
operationId,
|
|
1472
1056
|
passphraseState: _passphraseState,
|
|
1473
1057
|
useEmptyPassphrase: _useEmptyPassphrase,
|
|
1474
|
-
knownConnections,
|
|
1475
|
-
extra,
|
|
1476
|
-
allowDeviceSelection,
|
|
1477
1058
|
...rest
|
|
1478
1059
|
} = params;
|
|
1479
1060
|
return {
|
|
1480
1061
|
commonParams: {
|
|
1481
|
-
autoInstallApp: typeof autoInstallApp === "boolean" ? autoInstallApp : void 0
|
|
1482
|
-
operationId: typeof operationId === "string" ? operationId : void 0,
|
|
1483
|
-
knownConnections,
|
|
1484
|
-
extra,
|
|
1485
|
-
allowDeviceSelection: typeof allowDeviceSelection === "boolean" ? allowDeviceSelection : void 0
|
|
1062
|
+
autoInstallApp: typeof autoInstallApp === "boolean" ? autoInstallApp : void 0
|
|
1486
1063
|
},
|
|
1487
1064
|
rest
|
|
1488
1065
|
};
|
|
@@ -1658,27 +1235,6 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
1658
1235
|
);
|
|
1659
1236
|
}
|
|
1660
1237
|
// ---------------------------------------------------------------------------
|
|
1661
|
-
// Zcash chain methods (viewing key + shielded address reads; Zcash app >= 3.8.0)
|
|
1662
|
-
// ---------------------------------------------------------------------------
|
|
1663
|
-
zcashGetFullViewingKey(connectId, deviceId, params) {
|
|
1664
|
-
return this.callChainWithMergedParams(
|
|
1665
|
-
connectId,
|
|
1666
|
-
deviceId,
|
|
1667
|
-
"zcash",
|
|
1668
|
-
"zcashGetFullViewingKey",
|
|
1669
|
-
params
|
|
1670
|
-
);
|
|
1671
|
-
}
|
|
1672
|
-
zcashGetShieldedAddress(connectId, deviceId, params) {
|
|
1673
|
-
return this.callChainWithMergedParams(
|
|
1674
|
-
connectId,
|
|
1675
|
-
deviceId,
|
|
1676
|
-
"zcash",
|
|
1677
|
-
"zcashGetShieldedAddress",
|
|
1678
|
-
params
|
|
1679
|
-
);
|
|
1680
|
-
}
|
|
1681
|
-
// ---------------------------------------------------------------------------
|
|
1682
1238
|
// App management — OS-level Ledger app install / list. Bypasses fingerprint
|
|
1683
1239
|
// and chain-handler dispatch; installApp progress is forwarded to the adapter
|
|
1684
1240
|
// emitter via 'ui-event' AppInstallProgress events.
|
|
@@ -1767,7 +1323,7 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
1767
1323
|
);
|
|
1768
1324
|
}
|
|
1769
1325
|
await this.connector.configure({ ledgerGenuineCheckWebSocketUrl: relayUrl });
|
|
1770
|
-
|
|
1326
|
+
this.resetState();
|
|
1771
1327
|
}
|
|
1772
1328
|
const result = await this.connectorCall(connectId, "getDeviceGenuineCheck", {});
|
|
1773
1329
|
if (!result.isGenuine) {
|
|
@@ -1793,10 +1349,10 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
1793
1349
|
if (relayUrl) {
|
|
1794
1350
|
try {
|
|
1795
1351
|
await this.connector.configure?.({ ledgerGenuineCheckWebSocketUrl: void 0 });
|
|
1796
|
-
|
|
1352
|
+
this.resetState();
|
|
1797
1353
|
} catch {
|
|
1798
1354
|
this.connector.reset();
|
|
1799
|
-
|
|
1355
|
+
this.resetState();
|
|
1800
1356
|
}
|
|
1801
1357
|
}
|
|
1802
1358
|
}
|
|
@@ -1808,20 +1364,6 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
1808
1364
|
this.emitter.off(event, listener);
|
|
1809
1365
|
}
|
|
1810
1366
|
cancel(connectId) {
|
|
1811
|
-
const namedOperationIsLive = (id) => {
|
|
1812
|
-
try {
|
|
1813
|
-
this._operations.resolve(id);
|
|
1814
|
-
return true;
|
|
1815
|
-
} catch {
|
|
1816
|
-
return false;
|
|
1817
|
-
}
|
|
1818
|
-
};
|
|
1819
|
-
if (isHardwareOperationId(connectId) && !namedOperationIsLive(connectId)) {
|
|
1820
|
-
debugLog("[LedgerAdapter] cancel target already ended; nothing to cancel", {
|
|
1821
|
-
connectId
|
|
1822
|
-
});
|
|
1823
|
-
return;
|
|
1824
|
-
}
|
|
1825
1367
|
const userAbortReason = Object.assign(new Error("User aborted operation"), {
|
|
1826
1368
|
code: HardwareErrorCode3.UserAborted,
|
|
1827
1369
|
_tag: ERROR_TAG.UserAborted
|
|
@@ -1832,37 +1374,14 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
1832
1374
|
this._lastCancelReason = void 0;
|
|
1833
1375
|
}
|
|
1834
1376
|
}, 2e3);
|
|
1835
|
-
const activeJobId = this._jobQueue.getActiveJob()?.deviceId;
|
|
1836
|
-
let operationId;
|
|
1837
|
-
if (isHardwareOperationId(connectId)) {
|
|
1838
|
-
operationId = connectId;
|
|
1839
|
-
} else if (!connectId && isHardwareOperationId(activeJobId)) {
|
|
1840
|
-
operationId = activeJobId;
|
|
1841
|
-
}
|
|
1842
|
-
let resolvedConnectId = connectId;
|
|
1843
|
-
if (operationId) {
|
|
1844
|
-
try {
|
|
1845
|
-
resolvedConnectId = this._operations.resolve(operationId).connectId;
|
|
1846
|
-
} catch {
|
|
1847
|
-
resolvedConnectId = void 0;
|
|
1848
|
-
}
|
|
1849
|
-
}
|
|
1850
1377
|
this._uiRegistry.cancel();
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
const queueKey = operationId ?? (activeJobId === connectId ? connectId : interactionForPhysicalId?.operationId) ?? connectId;
|
|
1854
|
-
const pendingOperationId = operationId ?? interactionForPhysicalId?.operationId;
|
|
1855
|
-
if (!connectId) this._pendingOperationBindings.clear();
|
|
1856
|
-
else if (pendingOperationId) {
|
|
1857
|
-
this._pendingOperationBindings.delete(pendingOperationId);
|
|
1858
|
-
}
|
|
1859
|
-
if (queueKey) {
|
|
1860
|
-
this._jobQueue.cancelActiveAndPending(queueKey, userAbortReason);
|
|
1378
|
+
if (connectId) {
|
|
1379
|
+
this._jobQueue.cancelActiveAndPending(connectId, userAbortReason);
|
|
1861
1380
|
} else {
|
|
1862
1381
|
this._jobQueue.cancelActiveAndPending(void 0, userAbortReason);
|
|
1863
1382
|
}
|
|
1864
|
-
if (
|
|
1865
|
-
const sessionId = this._sessions.get(
|
|
1383
|
+
if (connectId) {
|
|
1384
|
+
const sessionId = this._sessions.get(connectId) ?? connectId;
|
|
1866
1385
|
void this.connector.cancel(sessionId);
|
|
1867
1386
|
} else {
|
|
1868
1387
|
for (const sid of this._sessions.values()) void this.connector.cancel(sid);
|
|
@@ -1874,98 +1393,41 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
1874
1393
|
// ---------------------------------------------------------------------------
|
|
1875
1394
|
// Chain fingerprint
|
|
1876
1395
|
// ---------------------------------------------------------------------------
|
|
1877
|
-
/** A non-empty deviceId is an expected chain fingerprint, not a transport identifier. */
|
|
1878
1396
|
async getChainFingerprint(connectId, deviceId, chain) {
|
|
1879
1397
|
try {
|
|
1880
1398
|
const fingerprint = await this._computeChainFingerprint(
|
|
1881
1399
|
chain,
|
|
1882
1400
|
(method, params) => this.connectorCall(connectId, method, params, void 0, deviceId)
|
|
1883
1401
|
);
|
|
1884
|
-
if (deviceId) {
|
|
1885
|
-
if (fingerprint !== deviceId) {
|
|
1886
|
-
return failure2(
|
|
1887
|
-
HardwareErrorCode3.DeviceMismatch,
|
|
1888
|
-
formatDeviceMismatchError(deviceId, fingerprint)
|
|
1889
|
-
);
|
|
1890
|
-
}
|
|
1891
|
-
if (isHardwareOperationId(connectId)) {
|
|
1892
|
-
const operation = this._operations.resolve(connectId);
|
|
1893
|
-
await this._publishVerifiedBleBinding(
|
|
1894
|
-
operation.connectId,
|
|
1895
|
-
chain,
|
|
1896
|
-
fingerprint,
|
|
1897
|
-
void 0,
|
|
1898
|
-
connectId
|
|
1899
|
-
);
|
|
1900
|
-
}
|
|
1901
|
-
}
|
|
1902
1402
|
return success(fingerprint);
|
|
1903
1403
|
} catch (err) {
|
|
1904
1404
|
debugError("[LedgerAdapter] getChainFingerprint error:", chain, err);
|
|
1905
1405
|
return this.errorToFailure(err);
|
|
1906
1406
|
}
|
|
1907
1407
|
}
|
|
1908
|
-
/**
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
}
|
|
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 };
|
|
1914
1414
|
try {
|
|
1915
|
-
const
|
|
1916
|
-
|
|
1917
|
-
this.
|
|
1918
|
-
{
|
|
1919
|
-
selectionRequestId: binding.selectedConnection.requestId,
|
|
1920
|
-
connection: { transport: "ble", connectId },
|
|
1921
|
-
identity: { vendor: "ledger", type: "chainFingerprint", chain, value: fingerprint },
|
|
1922
|
-
extra: binding.extra
|
|
1923
|
-
},
|
|
1924
|
-
signal
|
|
1415
|
+
const fingerprint = await this._computeChainFingerprint(
|
|
1416
|
+
chain,
|
|
1417
|
+
(method, params) => this._callConnector(sessionId, method, params)
|
|
1925
1418
|
);
|
|
1926
|
-
if (
|
|
1927
|
-
|
|
1928
|
-
debugLog("[LedgerAdapter] BLE binding not persisted by host", {
|
|
1929
|
-
connectId,
|
|
1930
|
-
reason: outcome.reason
|
|
1931
|
-
});
|
|
1419
|
+
if (fingerprint === deviceId) {
|
|
1420
|
+
return { success: true };
|
|
1932
1421
|
}
|
|
1933
|
-
return
|
|
1934
|
-
} catch (
|
|
1935
|
-
|
|
1936
|
-
|
|
1937
|
-
|
|
1938
|
-
}
|
|
1939
|
-
const sessionId = this._sessions.get(connectId);
|
|
1940
|
-
this._sessions.delete(connectId);
|
|
1941
|
-
if (sessionId) {
|
|
1942
|
-
const teardown = this._runConnectorTeardown(
|
|
1943
|
-
() => this.connector.disconnect(sessionId)
|
|
1944
|
-
).catch(() => void 0);
|
|
1945
|
-
if (!signal?.aborted) await teardown;
|
|
1946
|
-
}
|
|
1947
|
-
throw error;
|
|
1948
|
-
} finally {
|
|
1949
|
-
if (this._bindingSelectionRequestId === binding.selectedConnection.requestId) {
|
|
1950
|
-
this._bindingSelectionRequestId = void 0;
|
|
1951
|
-
}
|
|
1952
|
-
if (operationId) {
|
|
1953
|
-
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 };
|
|
1954
1427
|
}
|
|
1428
|
+
throw err;
|
|
1955
1429
|
}
|
|
1956
1430
|
}
|
|
1957
|
-
/** Verify on the acquired session without re-entering the job queue. */
|
|
1958
|
-
async _verifyDeviceFingerprintWithSession(sessionId, deviceId, chain) {
|
|
1959
|
-
if (!deviceId) return { success: true };
|
|
1960
|
-
const fingerprint = await this._computeChainFingerprint(
|
|
1961
|
-
chain,
|
|
1962
|
-
(method, params) => this._callConnector(sessionId, method, params)
|
|
1963
|
-
);
|
|
1964
|
-
if (fingerprint === deviceId) {
|
|
1965
|
-
return { success: true };
|
|
1966
|
-
}
|
|
1967
|
-
return { success: false, expected: deviceId, actual: fingerprint };
|
|
1968
|
-
}
|
|
1969
1431
|
/**
|
|
1970
1432
|
* Compute the chain fingerprint via a caller-supplied call strategy.
|
|
1971
1433
|
*
|
|
@@ -1993,8 +1455,6 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
1993
1455
|
address = (await callMethod("solGetAddress", { path, showOnDevice: false })).address;
|
|
1994
1456
|
} else if (chain === "tron") {
|
|
1995
1457
|
address = (await callMethod("tronGetAddress", { path, showOnDevice: false })).address;
|
|
1996
|
-
} else if (chain === "zcash") {
|
|
1997
|
-
address = (await callMethod("zcashGetShieldedAddress", { path, showOnDevice: false })).address;
|
|
1998
1458
|
} else {
|
|
1999
1459
|
throw new Error(`Unsupported chain for fingerprint: ${chain}`);
|
|
2000
1460
|
}
|
|
@@ -2124,17 +1584,16 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
2124
1584
|
// Layer 1 entry. Caller signal only races the outer awaiter; the shared
|
|
2125
1585
|
// `_doConnect` runs under its own internal controller so caller A's cancel
|
|
2126
1586
|
// doesn't kill caller B's await.
|
|
2127
|
-
async ensureConnected(connectId, signal, allowUsbEphemeralFallback = false
|
|
1587
|
+
async ensureConnected(connectId, signal, allowUsbEphemeralFallback = false) {
|
|
2128
1588
|
if (signal.aborted) _LedgerAdapter._throwIfAborted(signal);
|
|
2129
|
-
this.
|
|
2130
|
-
if (this._isBleConnection() && !connectId && !allowUsbEphemeralFallback) {
|
|
1589
|
+
if (isLedgerBleConnectionType(this.connector.connectionType) && !connectId) {
|
|
2131
1590
|
throw Object.assign(new Error("Ledger BLE connectId is required."), {
|
|
2132
1591
|
code: HardwareErrorCode3.DeviceNotFound
|
|
2133
1592
|
});
|
|
2134
1593
|
}
|
|
2135
1594
|
if (connectId && this._sessions.has(connectId)) return connectId;
|
|
2136
1595
|
if (!connectId && this._sessions.size > 0) {
|
|
2137
|
-
if (!this.
|
|
1596
|
+
if (!isLedgerBleConnectionType(this.connector.connectionType) && this._sessions.size > 1) {
|
|
2138
1597
|
throw Object.assign(
|
|
2139
1598
|
new Error(
|
|
2140
1599
|
"Ledger USB session invariant violated: more than one session is active. Please reconnect the device."
|
|
@@ -2144,24 +1603,15 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
2144
1603
|
}
|
|
2145
1604
|
return this._sessions.keys().next().value;
|
|
2146
1605
|
}
|
|
2147
|
-
if (!this._connectingPromise
|
|
2148
|
-
|
|
2149
|
-
this._doConnectAbortController
|
|
2150
|
-
const innerSignal = controller.signal;
|
|
1606
|
+
if (!this._connectingPromise) {
|
|
1607
|
+
this._doConnectAbortController = new AbortController();
|
|
1608
|
+
const innerSignal = this._doConnectAbortController.signal;
|
|
2151
1609
|
this._connectingPromise = (async () => {
|
|
2152
1610
|
try {
|
|
2153
|
-
return await this._doConnect(
|
|
2154
|
-
innerSignal,
|
|
2155
|
-
connectId,
|
|
2156
|
-
allowUsbEphemeralFallback,
|
|
2157
|
-
preserveOperationId,
|
|
2158
|
-
context
|
|
2159
|
-
);
|
|
1611
|
+
return await this._doConnect(innerSignal, connectId, allowUsbEphemeralFallback);
|
|
2160
1612
|
} finally {
|
|
2161
|
-
|
|
2162
|
-
|
|
2163
|
-
this._doConnectAbortController = null;
|
|
2164
|
-
}
|
|
1613
|
+
this._connectingPromise = null;
|
|
1614
|
+
this._doConnectAbortController = null;
|
|
2165
1615
|
}
|
|
2166
1616
|
})();
|
|
2167
1617
|
}
|
|
@@ -2170,51 +1620,22 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
2170
1620
|
// Layer 1 main loop — the ONLY place in SDK that emits unlock dialog.
|
|
2171
1621
|
// Bounded by MAX_DOCONNECT_CONFIRMS — after N Confirms with no progress,
|
|
2172
1622
|
// throw DeviceNotFound so the user is kicked out of the loop.
|
|
2173
|
-
async _doConnect(internalSignal, targetConnectId, allowUsbEphemeralFallback = false
|
|
2174
|
-
|
|
2175
|
-
|
|
2176
|
-
|
|
2177
|
-
|
|
2178
|
-
|
|
2179
|
-
|
|
2180
|
-
|
|
2181
|
-
|
|
2182
|
-
)
|
|
2183
|
-
|
|
2184
|
-
|
|
2185
|
-
|
|
2186
|
-
|
|
2187
|
-
|
|
2188
|
-
preserveOperationId,
|
|
2189
|
-
context,
|
|
2190
|
-
internalSignal
|
|
2191
|
-
);
|
|
2192
|
-
}
|
|
2193
|
-
this._activeConnectionType = "ble";
|
|
2194
|
-
const knownBle = context?.knownConnections?.find(
|
|
2195
|
-
(connection) => connection.transport === "ble"
|
|
2196
|
-
);
|
|
2197
|
-
if (knownBle?.transport === "ble") {
|
|
2198
|
-
return this._connectDeviceOrThrow(knownBle.connectId, preserveOperationId, internalSignal);
|
|
2199
|
-
}
|
|
2200
|
-
if (targetConnectId && context?.knownConnections === void 0) {
|
|
2201
|
-
throw createHwkError({
|
|
2202
|
-
code: HardwareErrorCode3.DeviceNotFound,
|
|
2203
|
-
message: "Ledger connection metadata is required before starting Bluetooth binding"
|
|
2204
|
-
});
|
|
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
|
+
}
|
|
2205
1638
|
}
|
|
2206
|
-
const bleDevices = await this._searchDevices({ transportType: "ble" }, internalSignal);
|
|
2207
|
-
return this._connectFirstOrSelect(
|
|
2208
|
-
bleDevices,
|
|
2209
|
-
void 0,
|
|
2210
|
-
allowUsbEphemeralFallback,
|
|
2211
|
-
preserveOperationId,
|
|
2212
|
-
context,
|
|
2213
|
-
internalSignal
|
|
2214
|
-
);
|
|
2215
|
-
}
|
|
2216
|
-
if (this._isBleConnection() && targetConnectId) {
|
|
2217
|
-
return this._connectDeviceOrThrow(targetConnectId, preserveOperationId, internalSignal);
|
|
2218
1639
|
}
|
|
2219
1640
|
let confirms = 0;
|
|
2220
1641
|
while (!internalSignal.aborted) {
|
|
@@ -2222,28 +1643,22 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
2222
1643
|
type: EConnectorInteraction.Searching,
|
|
2223
1644
|
payload: { sessionId: "" }
|
|
2224
1645
|
});
|
|
2225
|
-
let devices = await this.
|
|
2226
|
-
_LedgerAdapter._throwIfAborted(internalSignal);
|
|
1646
|
+
let devices = await this.searchDevices();
|
|
2227
1647
|
if (devices.length === 0) {
|
|
2228
1648
|
for (let i = 0; i < 3 && !internalSignal.aborted; i += 1) {
|
|
2229
1649
|
await new Promise((resolve) => {
|
|
2230
1650
|
setTimeout(resolve, DEVICE_CONNECT_RETRY_DELAY_MS);
|
|
2231
1651
|
});
|
|
2232
|
-
|
|
2233
|
-
devices = await this._searchDevices(void 0, internalSignal);
|
|
2234
|
-
_LedgerAdapter._throwIfAborted(internalSignal);
|
|
1652
|
+
devices = await this.searchDevices();
|
|
2235
1653
|
if (devices.length > 0) break;
|
|
2236
1654
|
}
|
|
2237
1655
|
}
|
|
2238
|
-
if (devices.length > 0
|
|
1656
|
+
if (devices.length > 0) {
|
|
2239
1657
|
try {
|
|
2240
1658
|
return await this._connectFirstOrSelect(
|
|
2241
1659
|
devices,
|
|
2242
1660
|
targetConnectId,
|
|
2243
|
-
allowUsbEphemeralFallback
|
|
2244
|
-
preserveOperationId,
|
|
2245
|
-
context,
|
|
2246
|
-
internalSignal
|
|
1661
|
+
allowUsbEphemeralFallback
|
|
2247
1662
|
);
|
|
2248
1663
|
} catch (err) {
|
|
2249
1664
|
if (!isDeviceLockedError(err) && !isDeviceNotAdvertisingError(err) && !isDeviceDisconnectedError(err)) {
|
|
@@ -2272,133 +1687,45 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
2272
1687
|
_LedgerAdapter._throwIfAborted(internalSignal);
|
|
2273
1688
|
throw new Error("_doConnect aborted");
|
|
2274
1689
|
}
|
|
2275
|
-
async _connectFirstOrSelect(devices, targetConnectId, allowUsbEphemeralFallback
|
|
2276
|
-
_LedgerAdapter._throwIfAborted(signal);
|
|
1690
|
+
async _connectFirstOrSelect(devices, targetConnectId, allowUsbEphemeralFallback = false) {
|
|
2277
1691
|
if (targetConnectId) {
|
|
2278
1692
|
const target = devices.find(
|
|
2279
1693
|
(d) => d.connectId === targetConnectId || d.deviceId === targetConnectId
|
|
2280
1694
|
);
|
|
2281
1695
|
if (target) {
|
|
2282
|
-
return this._connectDeviceOrThrow(target.connectId
|
|
1696
|
+
return this._connectDeviceOrThrow(target.connectId);
|
|
2283
1697
|
}
|
|
2284
|
-
if (!this.
|
|
1698
|
+
if (!isLedgerBleConnectionType(this.connector.connectionType) && devices.length === 1 && allowUsbEphemeralFallback) {
|
|
2285
1699
|
debugLog(
|
|
2286
1700
|
`[LedgerAdapter] target ${targetConnectId} not in fresh enumeration; accepting sole USB device ${devices[0].connectId} for fingerprint-verified recovery`
|
|
2287
1701
|
);
|
|
2288
|
-
return this._connectDeviceOrThrow(devices[0].connectId
|
|
2289
|
-
}
|
|
2290
|
-
if (!this._isBleConnection() || !allowUsbEphemeralFallback) {
|
|
2291
|
-
const err = Object.assign(new Error(`Target Ledger unavailable: ${targetConnectId}`), {
|
|
2292
|
-
code: HardwareErrorCode3.DeviceNotFound
|
|
2293
|
-
});
|
|
2294
|
-
if (this._isBleConnection()) {
|
|
2295
|
-
err._tag = ERROR_TAG.DeviceNotAdvertising;
|
|
2296
|
-
}
|
|
2297
|
-
throw err;
|
|
1702
|
+
return this._connectDeviceOrThrow(devices[0].connectId);
|
|
2298
1703
|
}
|
|
2299
|
-
|
|
2300
|
-
const requiresBleSelection = this._isBleConnection();
|
|
2301
|
-
if (requiresBleSelection && !allowUsbEphemeralFallback) {
|
|
2302
|
-
throw Object.assign(new Error("Ledger BLE connectId is required."), {
|
|
1704
|
+
const err = Object.assign(new Error(`Target Ledger unavailable: ${targetConnectId}`), {
|
|
2303
1705
|
code: HardwareErrorCode3.DeviceNotFound
|
|
2304
1706
|
});
|
|
2305
|
-
|
|
2306
|
-
|
|
2307
|
-
const bindingSessionId = context?.bindingSessionId ?? this._uiRegistry.createRequestId();
|
|
2308
|
-
if (context) context.bindingSessionId = bindingSessionId;
|
|
2309
|
-
const allowUsbFallback = context?.bindingReason !== "manual-rebind" && Boolean(this.connector.availableTransports?.includes("usb"));
|
|
2310
|
-
const knownUsb = context?.knownConnections?.find(
|
|
2311
|
-
(connection) => connection.transport === "usb"
|
|
2312
|
-
);
|
|
2313
|
-
const usbConnectId = knownUsb?.transport === "usb" ? knownUsb.connectId : targetConnectId;
|
|
2314
|
-
const { device, requestId } = await requestBleDeviceSelection({
|
|
2315
|
-
emitter: this.emitter,
|
|
2316
|
-
registry: this._uiRegistry,
|
|
2317
|
-
signal,
|
|
2318
|
-
allowUsbFallback,
|
|
2319
|
-
scan: async () => {
|
|
2320
|
-
if (allowUsbFallback) {
|
|
2321
|
-
const usbDevices = await this._searchDevices({ transportType: "usb" }, signal);
|
|
2322
|
-
const candidate = usbDevices.find(
|
|
2323
|
-
(device2) => device2.connectionType === "usb" && device2.connectId === usbConnectId
|
|
2324
|
-
) ?? (usbDevices.length === 1 && usbDevices[0].connectionType === "usb" ? usbDevices[0] : void 0);
|
|
2325
|
-
if (candidate) return [candidate];
|
|
2326
|
-
}
|
|
2327
|
-
return (await this._searchDevices({ transportType: "ble", waitForAllTransports: true }, signal)).filter((device2) => !context?.rejectedConnectIds?.has(device2.connectId));
|
|
2328
|
-
},
|
|
2329
|
-
request: {
|
|
2330
|
-
devices: devices.filter((device2) => !context?.rejectedConnectIds?.has(device2.connectId)),
|
|
2331
|
-
bindingSessionId,
|
|
2332
|
-
rejectedConnectId: context?.rejectedConnectId,
|
|
2333
|
-
context: {
|
|
2334
|
-
kind: "bind-connection",
|
|
2335
|
-
transport: "ble",
|
|
2336
|
-
reason: context?.bindingReason ?? (targetConnectId ? "known-connection-unavailable" : "missing-binding")
|
|
2337
|
-
},
|
|
2338
|
-
extra: context?.extra
|
|
2339
|
-
}
|
|
2340
|
-
});
|
|
2341
|
-
if (device.connectionType === "usb") {
|
|
2342
|
-
this._activeConnectionType = "usb";
|
|
2343
|
-
if (context) context.selectedConnection = void 0;
|
|
2344
|
-
if (preserveOperationId) this._pendingOperationBindings.delete(preserveOperationId);
|
|
2345
|
-
this._bindingSelectionRequestId = void 0;
|
|
2346
|
-
this.emitter.emit(UI_REQUEST.DEVICE_BINDING_STATUS, {
|
|
2347
|
-
type: UI_REQUEST.DEVICE_BINDING_STATUS,
|
|
2348
|
-
payload: { selectionRequestId: requestId, status: "cancelled" }
|
|
2349
|
-
});
|
|
2350
|
-
return this._connectDeviceOrThrow(device.connectId, preserveOperationId, signal);
|
|
1707
|
+
if (isLedgerBleConnectionType(this.connector.connectionType)) {
|
|
1708
|
+
err._tag = ERROR_TAG.DeviceNotAdvertising;
|
|
2351
1709
|
}
|
|
2352
|
-
|
|
2353
|
-
this._bindingSelectionRequestId = requestId;
|
|
2354
|
-
return this._connectDeviceOrThrow(device.connectId, preserveOperationId, signal);
|
|
1710
|
+
throw err;
|
|
2355
1711
|
}
|
|
2356
|
-
if (
|
|
2357
|
-
|
|
2358
|
-
|
|
2359
|
-
code: HardwareErrorCode3.DeviceNotFound,
|
|
2360
|
-
message: "Select a Ledger device before continuing"
|
|
2361
|
-
});
|
|
2362
|
-
}
|
|
2363
|
-
const requestId = this._uiRegistry.createRequestId();
|
|
2364
|
-
const waitPromise = this._uiRegistry.wait(
|
|
2365
|
-
UI_REQUEST.REQUEST_SELECT_DEVICE,
|
|
2366
|
-
{ requestId }
|
|
2367
|
-
);
|
|
2368
|
-
this.emitter.emit(UI_REQUEST.REQUEST_SELECT_DEVICE, {
|
|
2369
|
-
type: UI_REQUEST.REQUEST_SELECT_DEVICE,
|
|
2370
|
-
payload: {
|
|
2371
|
-
devices,
|
|
2372
|
-
requestId,
|
|
2373
|
-
context: requiresBleSelection ? {
|
|
2374
|
-
kind: "bind-connection",
|
|
2375
|
-
transport: "ble",
|
|
2376
|
-
reason: targetConnectId ? "known-connection-unavailable" : "missing-binding"
|
|
2377
|
-
} : { kind: "select-device", transport: "usb", reason: "multiple-candidates" },
|
|
2378
|
-
extra: context?.extra
|
|
2379
|
-
}
|
|
1712
|
+
if (isLedgerBleConnectionType(this.connector.connectionType)) {
|
|
1713
|
+
throw Object.assign(new Error("Ledger BLE connectId is required."), {
|
|
1714
|
+
code: HardwareErrorCode3.DeviceNotFound
|
|
2380
1715
|
});
|
|
2381
|
-
|
|
2382
|
-
|
|
2383
|
-
|
|
2384
|
-
if (!selected) {
|
|
2385
|
-
throw Object.assign(new Error("Selected Ledger is no longer available"), {
|
|
2386
|
-
code: HardwareErrorCode3.DeviceNotFound
|
|
2387
|
-
});
|
|
2388
|
-
}
|
|
2389
|
-
if (context && requiresBleSelection)
|
|
2390
|
-
context.selectedConnection = { connectId: selected.connectId, requestId };
|
|
2391
|
-
return this._connectDeviceOrThrow(selected.connectId, preserveOperationId, signal);
|
|
1716
|
+
}
|
|
1717
|
+
if (devices.length > 1) {
|
|
1718
|
+
throw createMultipleUsbLedgerDevicesError();
|
|
2392
1719
|
}
|
|
2393
1720
|
if (devices.length !== 1) {
|
|
2394
1721
|
throw Object.assign(new Error("Ledger device not found."), {
|
|
2395
1722
|
code: HardwareErrorCode3.DeviceNotFound
|
|
2396
1723
|
});
|
|
2397
1724
|
}
|
|
2398
|
-
return this._connectDeviceOrThrow(devices[0].connectId
|
|
1725
|
+
return this._connectDeviceOrThrow(devices[0].connectId);
|
|
2399
1726
|
}
|
|
2400
|
-
async _connectDeviceOrThrow(chosenConnectId
|
|
2401
|
-
const result = await this.
|
|
1727
|
+
async _connectDeviceOrThrow(chosenConnectId) {
|
|
1728
|
+
const result = await this.connectDevice(chosenConnectId);
|
|
2402
1729
|
if (!result.success) {
|
|
2403
1730
|
const payload = result.payload;
|
|
2404
1731
|
const rethrow = Object.assign(new Error(payload.error), {
|
|
@@ -2409,7 +1736,7 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
2409
1736
|
}
|
|
2410
1737
|
throw rethrow;
|
|
2411
1738
|
}
|
|
2412
|
-
return
|
|
1739
|
+
return chosenConnectId;
|
|
2413
1740
|
}
|
|
2414
1741
|
/**
|
|
2415
1742
|
* Call the connector with automatic session resolution and disconnect retry.
|
|
@@ -2439,122 +1766,26 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
2439
1766
|
* lives in one place.
|
|
2440
1767
|
*/
|
|
2441
1768
|
async _callConnector(sessionId, method, params, signal) {
|
|
2442
|
-
this.
|
|
2443
|
-
if (signal?.aborted) throw this._abortReason(signal);
|
|
2444
|
-
const releaseOperation = this._retainConnectorOperation(`call:${sessionId}`);
|
|
2445
|
-
let promise;
|
|
2446
|
-
try {
|
|
2447
|
-
promise = this.connector.call(sessionId, method, params).finally(releaseOperation);
|
|
2448
|
-
} catch (error) {
|
|
2449
|
-
releaseOperation();
|
|
2450
|
-
throw error;
|
|
2451
|
-
}
|
|
1769
|
+
const promise = this.connector.call(sessionId, method, params);
|
|
2452
1770
|
const result = signal ? await this._abortable(signal, promise) : await promise;
|
|
2453
1771
|
return this._unwrapConnectorResult(result);
|
|
2454
1772
|
}
|
|
2455
|
-
_assertConnectorReady(method) {
|
|
2456
|
-
if (this._resetPromise || this._pendingConnectorTeardowns > 0 || this._unsettledConnectorOperations.size > 0) {
|
|
2457
|
-
throw _LedgerAdapter._createDeviceBusyError(method);
|
|
2458
|
-
}
|
|
2459
|
-
}
|
|
2460
|
-
_runConnectorTeardown(task) {
|
|
2461
|
-
const previous = this._connectorTeardownTail;
|
|
2462
|
-
let releaseTail = () => void 0;
|
|
2463
|
-
this._connectorTeardownTail = new Promise((resolve) => {
|
|
2464
|
-
releaseTail = resolve;
|
|
2465
|
-
});
|
|
2466
|
-
this._pendingConnectorTeardowns += 1;
|
|
2467
|
-
return (async () => {
|
|
2468
|
-
try {
|
|
2469
|
-
await previous;
|
|
2470
|
-
await this._waitForConnectorOperationsToDrain();
|
|
2471
|
-
await task();
|
|
2472
|
-
} finally {
|
|
2473
|
-
this._pendingConnectorTeardowns -= 1;
|
|
2474
|
-
releaseTail();
|
|
2475
|
-
}
|
|
2476
|
-
})();
|
|
2477
|
-
}
|
|
2478
|
-
_waitForConnectorOperationsToDrain() {
|
|
2479
|
-
if (this._unsettledConnectorOperations.size === 0) {
|
|
2480
|
-
return Promise.resolve();
|
|
2481
|
-
}
|
|
2482
|
-
return new Promise((resolve) => {
|
|
2483
|
-
this._connectorIdleWaiters.add(resolve);
|
|
2484
|
-
});
|
|
2485
|
-
}
|
|
2486
|
-
_retainConnectorOperation(key) {
|
|
2487
|
-
this._unsettledConnectorOperations.set(
|
|
2488
|
-
key,
|
|
2489
|
-
(this._unsettledConnectorOperations.get(key) ?? 0) + 1
|
|
2490
|
-
);
|
|
2491
|
-
let released = false;
|
|
2492
|
-
return () => {
|
|
2493
|
-
if (released) return;
|
|
2494
|
-
released = true;
|
|
2495
|
-
const remaining = (this._unsettledConnectorOperations.get(key) ?? 1) - 1;
|
|
2496
|
-
if (remaining > 0) {
|
|
2497
|
-
this._unsettledConnectorOperations.set(key, remaining);
|
|
2498
|
-
} else {
|
|
2499
|
-
this._unsettledConnectorOperations.delete(key);
|
|
2500
|
-
}
|
|
2501
|
-
if (this._unsettledConnectorOperations.size === 0) {
|
|
2502
|
-
for (const resolve of this._connectorIdleWaiters) resolve();
|
|
2503
|
-
this._connectorIdleWaiters.clear();
|
|
2504
|
-
}
|
|
2505
|
-
};
|
|
2506
|
-
}
|
|
2507
1773
|
async connectorCall(connectId, method, params, fingerprint, permissionDeviceId, commonParams, installContext) {
|
|
2508
|
-
|
|
2509
|
-
|
|
2510
|
-
throw createHwkError({
|
|
2511
|
-
code: HardwareErrorCode3.InvalidParams,
|
|
2512
|
-
message: "Conflicting Ledger operation ids",
|
|
2513
|
-
params: {
|
|
2514
|
-
positionalOperationId,
|
|
2515
|
-
commonOperationId: commonParams.operationId
|
|
2516
|
-
}
|
|
2517
|
-
});
|
|
2518
|
-
}
|
|
2519
|
-
const operationId = commonParams?.operationId ?? positionalOperationId;
|
|
2520
|
-
const operation = operationId ? this._operations.resolve(operationId) : void 0;
|
|
2521
|
-
const releaseOperationRetention = operationId ? this._operations.retain(operationId) : void 0;
|
|
2522
|
-
const effectiveConnectId = operation?.connectId ?? connectId;
|
|
2523
|
-
debugLog("[LedgerAdapter][REQ]", {
|
|
2524
|
-
method,
|
|
2525
|
-
connectId: effectiveConnectId || "(empty)",
|
|
2526
|
-
params
|
|
2527
|
-
});
|
|
2528
|
-
const queueKey = (operationId ?? effectiveConnectId) || "__ledger_default__";
|
|
1774
|
+
debugLog("[LedgerAdapter][REQ]", { method, connectId: connectId || "(empty)", params });
|
|
1775
|
+
const queueKey = connectId || "__ledger_default__";
|
|
2529
1776
|
try {
|
|
2530
1777
|
const result = await this._jobQueue.enqueue(
|
|
2531
1778
|
queueKey,
|
|
2532
|
-
async (signal) =>
|
|
2533
|
-
|
|
2534
|
-
|
|
2535
|
-
|
|
2536
|
-
|
|
2537
|
-
|
|
2538
|
-
|
|
2539
|
-
|
|
2540
|
-
|
|
2541
|
-
|
|
2542
|
-
commonParams,
|
|
2543
|
-
installContext ?? {},
|
|
2544
|
-
operationId
|
|
2545
|
-
);
|
|
2546
|
-
} catch (error) {
|
|
2547
|
-
this._finishBleBinding(signal.aborted ? "cancelled" : "failed");
|
|
2548
|
-
throw error;
|
|
2549
|
-
} finally {
|
|
2550
|
-
if (operationId) {
|
|
2551
|
-
this._activeOperationJobs.delete(operationId);
|
|
2552
|
-
if (this._pendingOperationDisconnects.delete(operationId)) {
|
|
2553
|
-
await this._releaseLostOperationConnection(operationId);
|
|
2554
|
-
}
|
|
2555
|
-
}
|
|
2556
|
-
}
|
|
2557
|
-
},
|
|
1779
|
+
async (signal) => this._runConnectorCall(
|
|
1780
|
+
connectId,
|
|
1781
|
+
method,
|
|
1782
|
+
params,
|
|
1783
|
+
signal,
|
|
1784
|
+
fingerprint,
|
|
1785
|
+
permissionDeviceId,
|
|
1786
|
+
commonParams,
|
|
1787
|
+
installContext
|
|
1788
|
+
),
|
|
2558
1789
|
{
|
|
2559
1790
|
label: method,
|
|
2560
1791
|
rejectIfBusy: true,
|
|
@@ -2575,22 +1806,15 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
2575
1806
|
}
|
|
2576
1807
|
});
|
|
2577
1808
|
throw err;
|
|
2578
|
-
} finally {
|
|
2579
|
-
releaseOperationRetention?.();
|
|
2580
1809
|
}
|
|
2581
1810
|
}
|
|
2582
1811
|
/**
|
|
2583
1812
|
* Race a promise against an abort signal. On abort, rejects with
|
|
2584
1813
|
* signal.reason → instance _lastCancelReason → generic Error('Aborted').
|
|
2585
1814
|
*/
|
|
2586
|
-
/** Hermes/RN polyfills don't always populate signal.reason; fall back. */
|
|
2587
|
-
_abortReason(signal) {
|
|
2588
|
-
return signal.reason ?? this._lastCancelReason ?? new Error("Aborted");
|
|
2589
|
-
}
|
|
2590
1815
|
_abortable(signal, promise) {
|
|
2591
|
-
const getAbortReason = () => this.
|
|
1816
|
+
const getAbortReason = () => signal.reason ?? this._lastCancelReason ?? new Error("Aborted");
|
|
2592
1817
|
if (signal.aborted) {
|
|
2593
|
-
void promise.catch(() => void 0);
|
|
2594
1818
|
return Promise.reject(getAbortReason());
|
|
2595
1819
|
}
|
|
2596
1820
|
return new Promise((resolve, reject) => {
|
|
@@ -2617,15 +1841,13 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
2617
1841
|
}
|
|
2618
1842
|
}
|
|
2619
1843
|
/** Actual work done under the job queue — connection, fingerprint, call, and recovery. */
|
|
2620
|
-
async _runConnectorCall(connectId, method, params, signal, fingerprint, permissionDeviceId, commonParams, installContext
|
|
1844
|
+
async _runConnectorCall(connectId, method, params, signal, fingerprint, permissionDeviceId, commonParams, installContext) {
|
|
2621
1845
|
_LedgerAdapter._throwIfAborted(signal);
|
|
2622
|
-
|
|
2623
|
-
|
|
2624
|
-
|
|
2625
|
-
|
|
2626
|
-
|
|
2627
|
-
);
|
|
2628
|
-
}
|
|
1846
|
+
await this._ensureDevicePermission(
|
|
1847
|
+
connectId,
|
|
1848
|
+
permissionDeviceId ?? fingerprint?.deviceId,
|
|
1849
|
+
signal
|
|
1850
|
+
);
|
|
2629
1851
|
_LedgerAdapter._throwIfAborted(signal);
|
|
2630
1852
|
let effectiveParams = params;
|
|
2631
1853
|
if (method === "btcGetPublicKey") {
|
|
@@ -2639,132 +1861,36 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
2639
1861
|
effectiveParams = gatedParams;
|
|
2640
1862
|
}
|
|
2641
1863
|
const allowUsbEphemeralFallback = !!fingerprint?.deviceId && !fingerprint.skipFingerprint;
|
|
2642
|
-
|
|
2643
|
-
|
|
2644
|
-
const knownTransport = this._isBleConnection() ? "ble" : "usb";
|
|
2645
|
-
const hintedConnectId = commonParams?.knownConnections?.find(
|
|
2646
|
-
(connection) => connection.transport === knownTransport
|
|
2647
|
-
);
|
|
2648
|
-
const inputConnectId = hintedConnectId && hintedConnectId.transport !== "qr" ? hintedConnectId.connectId : connectId;
|
|
2649
|
-
const preferredConnectId = fingerprint && !fingerprint.skipFingerprint && verifiedBleTarget?.chain === fingerprint.chain && verifiedBleTarget.fingerprint === fingerprint.deviceId ? verifiedBleTarget.connectId : inputConnectId;
|
|
2650
|
-
const connectionAttempt = { ...commonParams };
|
|
2651
|
-
const bundleConnection = installContext?.connection;
|
|
2652
|
-
if (bundleConnection && this._sessions.get(bundleConnection.connectId) !== bundleConnection.sessionId) {
|
|
2653
|
-
throw createHwkError({
|
|
2654
|
-
code: HardwareErrorCode3.DeviceDisconnected,
|
|
2655
|
-
message: "Ledger all-network connection ended"
|
|
2656
|
-
});
|
|
2657
|
-
}
|
|
2658
|
-
let resolvedConnectId = operationId ? this._operations.resolve(operationId).connectId : bundleConnection?.connectId ?? await this.ensureConnected(
|
|
2659
|
-
preferredConnectId,
|
|
1864
|
+
const resolvedConnectId = await this.ensureConnected(
|
|
1865
|
+
connectId,
|
|
2660
1866
|
signal,
|
|
2661
|
-
allowUsbEphemeralFallback
|
|
2662
|
-
void 0,
|
|
2663
|
-
connectionAttempt
|
|
1867
|
+
allowUsbEphemeralFallback
|
|
2664
1868
|
);
|
|
2665
|
-
|
|
2666
|
-
if (sessionId && installContext && !installContext.connection) {
|
|
2667
|
-
installContext.connection = { connectId: resolvedConnectId, sessionId };
|
|
2668
|
-
}
|
|
1869
|
+
const sessionId = this._sessions.get(resolvedConnectId);
|
|
2669
1870
|
if (!sessionId) {
|
|
2670
|
-
if (operationId) {
|
|
2671
|
-
this._operations.end(operationId, "disconnect");
|
|
2672
|
-
throw createHwkError({
|
|
2673
|
-
code: HardwareErrorCode3.OperationEnded,
|
|
2674
|
-
message: "Ledger operation connection is no longer active",
|
|
2675
|
-
params: { operationId, reason: "disconnect" }
|
|
2676
|
-
});
|
|
2677
|
-
}
|
|
2678
1871
|
throw Object.assign(new Error("Auto-connect succeeded but no session found"), {
|
|
2679
1872
|
_tag: ERROR_TAG.DeviceSessionNotFound
|
|
2680
1873
|
});
|
|
2681
1874
|
}
|
|
2682
1875
|
try {
|
|
2683
1876
|
if (fingerprint && !fingerprint.skipFingerprint && fingerprint.deviceId) {
|
|
2684
|
-
|
|
2685
|
-
|
|
2686
|
-
|
|
2687
|
-
|
|
2688
|
-
|
|
2689
|
-
|
|
2690
|
-
|
|
2691
|
-
)
|
|
2692
|
-
);
|
|
2693
|
-
if (fp.success) break;
|
|
2694
|
-
const binding = operationId ? this._pendingOperationBindings.get(operationId) : connectionAttempt;
|
|
2695
|
-
if (!this._isBleConnection() || binding?.selectedConnection?.connectId !== resolvedConnectId) {
|
|
2696
|
-
throw Object.assign(new Error(formatDeviceMismatchError(fp.expected, fp.actual)), {
|
|
2697
|
-
code: HardwareErrorCode3.DeviceMismatch
|
|
2698
|
-
});
|
|
2699
|
-
}
|
|
2700
|
-
binding.rejectedConnectIds ?? (binding.rejectedConnectIds = /* @__PURE__ */ new Set());
|
|
2701
|
-
binding.rejectedConnectIds.add(resolvedConnectId);
|
|
2702
|
-
binding.rejectedConnectId = resolvedConnectId;
|
|
2703
|
-
this._sessions.delete(resolvedConnectId);
|
|
2704
|
-
await this.connector.disconnect(sessionId);
|
|
2705
|
-
if (operationId) this._pendingOperationDisconnects.delete(operationId);
|
|
2706
|
-
_LedgerAdapter._throwIfAborted(signal);
|
|
2707
|
-
resolvedConnectId = await this._connectFirstOrSelect(
|
|
2708
|
-
[],
|
|
2709
|
-
void 0,
|
|
2710
|
-
true,
|
|
2711
|
-
operationId,
|
|
2712
|
-
binding,
|
|
2713
|
-
signal
|
|
2714
|
-
);
|
|
2715
|
-
const selectedSession = this._sessions.get(resolvedConnectId);
|
|
2716
|
-
const selectedDevice = this._discoveredDevices.get(resolvedConnectId);
|
|
2717
|
-
if (!selectedSession || !selectedDevice)
|
|
2718
|
-
throw createHwkError({
|
|
2719
|
-
code: HardwareErrorCode3.DeviceDisconnected,
|
|
2720
|
-
message: "Selected Ledger connection ended"
|
|
2721
|
-
});
|
|
2722
|
-
sessionId = selectedSession;
|
|
2723
|
-
if (operationId)
|
|
2724
|
-
this._operations.rebind(operationId, {
|
|
2725
|
-
connectId: resolvedConnectId,
|
|
2726
|
-
device: selectedDevice,
|
|
2727
|
-
connectionKeys: [sessionId]
|
|
2728
|
-
});
|
|
2729
|
-
if (installContext)
|
|
2730
|
-
installContext.connection = { connectId: resolvedConnectId, sessionId };
|
|
2731
|
-
}
|
|
2732
|
-
await this._publishVerifiedBleBinding(
|
|
2733
|
-
resolvedConnectId,
|
|
2734
|
-
fingerprint.chain,
|
|
2735
|
-
fingerprint.deviceId,
|
|
2736
|
-
connectionAttempt,
|
|
2737
|
-
operationId,
|
|
2738
|
-
signal
|
|
1877
|
+
const fp = await this._abortable(
|
|
1878
|
+
signal,
|
|
1879
|
+
this._verifyDeviceFingerprintWithSession(
|
|
1880
|
+
sessionId,
|
|
1881
|
+
fingerprint.deviceId,
|
|
1882
|
+
fingerprint.chain
|
|
1883
|
+
)
|
|
2739
1884
|
);
|
|
2740
|
-
if (!
|
|
2741
|
-
|
|
2742
|
-
|
|
2743
|
-
chain: fingerprint.chain,
|
|
2744
|
-
fingerprint: fingerprint.deviceId
|
|
1885
|
+
if (!fp.success) {
|
|
1886
|
+
throw Object.assign(new Error(formatDeviceMismatchError(fp.expected, fp.actual)), {
|
|
1887
|
+
code: HardwareErrorCode3.DeviceMismatch
|
|
2745
1888
|
});
|
|
2746
1889
|
}
|
|
2747
1890
|
}
|
|
2748
|
-
businessCallStarted = true;
|
|
2749
1891
|
return await this._callConnector(sessionId, method, effectiveParams, signal);
|
|
2750
1892
|
} catch (err) {
|
|
2751
1893
|
if (signal.aborted) throw err;
|
|
2752
|
-
if (isDeviceDisconnectedError(err) || isDeviceNotAdvertisingError(err) || isTimeoutError(err) || isConnectionLevelError(err)) {
|
|
2753
|
-
this._discoveredDevices.delete(resolvedConnectId);
|
|
2754
|
-
if (operationId) await this._releaseLostOperationConnection(operationId);
|
|
2755
|
-
else {
|
|
2756
|
-
this._sessions.delete(resolvedConnectId);
|
|
2757
|
-
await this.connector.disconnect(sessionId).catch(() => void 0);
|
|
2758
|
-
}
|
|
2759
|
-
const ambiguous = businessCallStarted && !canReplayHardwareMethodAfterTransportFailure(method);
|
|
2760
|
-
const interactionParams = operationId ? { operationId, reason: "disconnect" } : void 0;
|
|
2761
|
-
throw createHwkError({
|
|
2762
|
-
code: operationId ? HardwareErrorCode3.OperationEnded : mapLedgerError(err).code,
|
|
2763
|
-
message: "Ledger operation connection was lost; start a new operation",
|
|
2764
|
-
params: ambiguous ? operationMayHaveCompletedParams(method, { operationId }) : interactionParams,
|
|
2765
|
-
recovery: ambiguous ? { scope: "unknown" } : void 0
|
|
2766
|
-
});
|
|
2767
|
-
}
|
|
2768
1894
|
const errObj = err;
|
|
2769
1895
|
debugLog("[LedgerAdapter] connectorCall error:", method, {
|
|
2770
1896
|
message: errObj?.message,
|
|
@@ -2776,65 +1902,6 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
2776
1902
|
isNotAdvertising: isDeviceNotAdvertisingError(err),
|
|
2777
1903
|
isStuckApp: isStuckAppStateError(err)
|
|
2778
1904
|
});
|
|
2779
|
-
if ((isDeviceLockedError(err) || errObj?.code === HardwareErrorCode3.DeviceLocked) && lockedRetryBudget > 0) {
|
|
2780
|
-
await this._waitForDeviceConnect(signal);
|
|
2781
|
-
if (this._sessions.get(resolvedConnectId) !== sessionId) {
|
|
2782
|
-
throw createHwkError({
|
|
2783
|
-
code: operationId ? HardwareErrorCode3.OperationEnded : HardwareErrorCode3.DeviceDisconnected,
|
|
2784
|
-
message: "Ledger connection ended while waiting for unlock"
|
|
2785
|
-
});
|
|
2786
|
-
}
|
|
2787
|
-
return this._runConnectorCall(
|
|
2788
|
-
resolvedConnectId,
|
|
2789
|
-
method,
|
|
2790
|
-
effectiveParams,
|
|
2791
|
-
signal,
|
|
2792
|
-
fingerprint,
|
|
2793
|
-
permissionDeviceId,
|
|
2794
|
-
commonParams,
|
|
2795
|
-
installContext,
|
|
2796
|
-
operationId,
|
|
2797
|
-
lockedRetryBudget - 1
|
|
2798
|
-
);
|
|
2799
|
-
}
|
|
2800
|
-
if (businessCallStarted && isStuckAppStateError(err)) {
|
|
2801
|
-
await this._sleepAbortable(_LedgerAdapter.STUCK_APP_RETRY_DELAY_MS, signal);
|
|
2802
|
-
if (this._sessions.get(resolvedConnectId) !== sessionId) {
|
|
2803
|
-
throw createHwkError({
|
|
2804
|
-
code: operationId ? HardwareErrorCode3.OperationEnded : HardwareErrorCode3.DeviceDisconnected,
|
|
2805
|
-
message: "Ledger connection ended during the app transition"
|
|
2806
|
-
});
|
|
2807
|
-
}
|
|
2808
|
-
try {
|
|
2809
|
-
return await this._callConnector(sessionId, method, effectiveParams, signal);
|
|
2810
|
-
} catch (retryErr) {
|
|
2811
|
-
if (isStuckAppStateError(retryErr)) throw err;
|
|
2812
|
-
if (isDeviceDisconnectedError(retryErr) || isDeviceNotAdvertisingError(retryErr) || isTimeoutError(retryErr) || isConnectionLevelError(retryErr)) {
|
|
2813
|
-
if (operationId) await this._releaseLostOperationConnection(operationId);
|
|
2814
|
-
else {
|
|
2815
|
-
this._sessions.delete(resolvedConnectId);
|
|
2816
|
-
this._discoveredDevices.delete(resolvedConnectId);
|
|
2817
|
-
await this.connector.disconnect(sessionId).catch(() => void 0);
|
|
2818
|
-
}
|
|
2819
|
-
throw createHwkError({
|
|
2820
|
-
code: operationId ? HardwareErrorCode3.OperationEnded : mapLedgerError(retryErr).code,
|
|
2821
|
-
message: `Ledger ${method} may have completed before the connection was lost`,
|
|
2822
|
-
params: !canReplayHardwareMethodAfterTransportFailure(method) ? operationMayHaveCompletedParams(method, {
|
|
2823
|
-
operationId,
|
|
2824
|
-
reason: "disconnect"
|
|
2825
|
-
}) : { operationId, reason: "disconnect" },
|
|
2826
|
-
recovery: !canReplayHardwareMethodAfterTransportFailure(method) ? { scope: "unknown" } : void 0
|
|
2827
|
-
});
|
|
2828
|
-
}
|
|
2829
|
-
throw retryErr;
|
|
2830
|
-
}
|
|
2831
|
-
}
|
|
2832
|
-
if (!operationId && err?.code === HardwareErrorCode3.DeviceMismatch) {
|
|
2833
|
-
this._sessions.delete(resolvedConnectId);
|
|
2834
|
-
this._discoveredDevices.delete(resolvedConnectId);
|
|
2835
|
-
await this.connector.disconnect(sessionId).catch(() => void 0);
|
|
2836
|
-
throw err;
|
|
2837
|
-
}
|
|
2838
1905
|
const autoInstallApp = commonParams?.autoInstallApp ?? this._defaultAutoInstallApp;
|
|
2839
1906
|
const isAppMissing = isAppNotInstalledError(err) || err?.code === HardwareErrorCode3.AppNotInstalled;
|
|
2840
1907
|
if (autoInstallApp && isAppMissing) {
|
|
@@ -2890,14 +1957,157 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
2890
1957
|
fingerprint,
|
|
2891
1958
|
permissionDeviceId,
|
|
2892
1959
|
commonParams,
|
|
2893
|
-
installContext
|
|
2894
|
-
|
|
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
|
|
2895
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
|
+
}
|
|
2896
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
|
+
});
|
|
2897
2073
|
}
|
|
2898
2074
|
throw err;
|
|
2899
2075
|
}
|
|
2900
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
|
+
}
|
|
2901
2111
|
_sleepAbortable(ms, signal) {
|
|
2902
2112
|
return new Promise((resolve, reject) => {
|
|
2903
2113
|
if (signal.aborted) {
|
|
@@ -2915,6 +2125,88 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
2915
2125
|
signal.addEventListener("abort", onAbort, { once: true });
|
|
2916
2126
|
});
|
|
2917
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
|
+
}
|
|
2918
2210
|
/**
|
|
2919
2211
|
* Ensure OS-level device permission (Bluetooth / USB) before proceeding.
|
|
2920
2212
|
*
|
|
@@ -2984,15 +2276,7 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
2984
2276
|
if (err && typeof err === "object" && "code" in err && typeof err.code === "number") {
|
|
2985
2277
|
const e = err;
|
|
2986
2278
|
const params = e.code === HardwareErrorCode3.DevicePermissionDenied && e.reason ? { permissionDeniedReason: e.reason } : e.params;
|
|
2987
|
-
return ledgerFailure(
|
|
2988
|
-
e.code,
|
|
2989
|
-
e.message ?? "Unknown error",
|
|
2990
|
-
e.appName,
|
|
2991
|
-
tag,
|
|
2992
|
-
params,
|
|
2993
|
-
void 0,
|
|
2994
|
-
isHwkRecoveryHint(e.recovery) ? e.recovery : void 0
|
|
2995
|
-
);
|
|
2279
|
+
return ledgerFailure(e.code, e.message ?? "Unknown error", e.appName, tag, params);
|
|
2996
2280
|
}
|
|
2997
2281
|
const mapped = mapLedgerError(err);
|
|
2998
2282
|
return ledgerFailure(mapped.code, mapped.message, mapped.appName, tag);
|
|
@@ -3019,7 +2303,7 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
3019
2303
|
deviceId: device.deviceId,
|
|
3020
2304
|
connectId: device.connectId,
|
|
3021
2305
|
label: device.name,
|
|
3022
|
-
connectionType:
|
|
2306
|
+
connectionType: this.connector.connectionType,
|
|
3023
2307
|
rssi: device.rssi,
|
|
3024
2308
|
isConnectable: device.isConnectable,
|
|
3025
2309
|
serialNumber: device.serialNumber,
|
|
@@ -3045,8 +2329,8 @@ var LedgerAdapter = _LedgerAdapter;
|
|
|
3045
2329
|
|
|
3046
2330
|
// src/connector/LedgerConnectorBase.ts
|
|
3047
2331
|
import {
|
|
3048
|
-
EConnectorInteraction as
|
|
3049
|
-
HardwareErrorCode as
|
|
2332
|
+
EConnectorInteraction as EConnectorInteraction4,
|
|
2333
|
+
HardwareErrorCode as HardwareErrorCode8,
|
|
3050
2334
|
isKnownNonTargetHardwareVendor,
|
|
3051
2335
|
serializeConnectorError
|
|
3052
2336
|
} from "@onekeyfe/hwk-adapter-core";
|
|
@@ -3319,7 +2603,7 @@ var LedgerDeviceManager = class {
|
|
|
3319
2603
|
|
|
3320
2604
|
// src/signer/SignerManager.ts
|
|
3321
2605
|
import { SignerEthBuilder } from "@ledgerhq/device-signer-kit-ethereum";
|
|
3322
|
-
import { ContextModuleBuilder
|
|
2606
|
+
import { ContextModuleBuilder } from "@ledgerhq/context-module";
|
|
3323
2607
|
|
|
3324
2608
|
// src/signer/SignerEth.ts
|
|
3325
2609
|
import { hexToBytes } from "@onekeyfe/hwk-adapter-core";
|
|
@@ -3585,7 +2869,7 @@ var SignerManager = class _SignerManager {
|
|
|
3585
2869
|
return (args) => new SignerEthBuilder(args);
|
|
3586
2870
|
}
|
|
3587
2871
|
static _createContextModule() {
|
|
3588
|
-
const contextModule = new ContextModuleBuilder({}).
|
|
2872
|
+
const contextModule = new ContextModuleBuilder({}).removeDefaultLoaders().build();
|
|
3589
2873
|
return _SignerManager.wrapBlindSigningReportNonBlocking(contextModule);
|
|
3590
2874
|
}
|
|
3591
2875
|
static wrapBlindSigningReportNonBlocking(contextModule) {
|
|
@@ -4055,8 +3339,7 @@ function _applySignaturesToPsbt(psbtHex, signatures) {
|
|
|
4055
3339
|
}
|
|
4056
3340
|
|
|
4057
3341
|
// src/connector/chains/sol.ts
|
|
4058
|
-
import { bytesToHex, hexToBytes as hexToBytes3
|
|
4059
|
-
import bs58 from "bs58";
|
|
3342
|
+
import { bytesToHex, hexToBytes as hexToBytes3 } from "@onekeyfe/hwk-adapter-core";
|
|
4060
3343
|
|
|
4061
3344
|
// src/signer/SignerSol.ts
|
|
4062
3345
|
var SignerSol = class {
|
|
@@ -4140,21 +3423,8 @@ async function solSignMessage(ctx, sessionId, params) {
|
|
|
4140
3423
|
const path = normalizePath(params.path);
|
|
4141
3424
|
const messageBytes = hexToBytes3(params.message);
|
|
4142
3425
|
try {
|
|
4143
|
-
if (params.messageVersion === 1) {
|
|
4144
|
-
const preparedMessage = prepareSolanaOffchainMessageV1({
|
|
4145
|
-
message: messageBytes,
|
|
4146
|
-
requiredSigners: params.requiredSigners
|
|
4147
|
-
});
|
|
4148
|
-
const { SignMessageVersion } = await ctx.importLedgerKit(
|
|
4149
|
-
"@ledgerhq/device-signer-kit-solana"
|
|
4150
|
-
);
|
|
4151
|
-
const result2 = await solSigner.signMessage(path, preparedMessage.serializedMessage, {
|
|
4152
|
-
version: SignMessageVersion.Raw
|
|
4153
|
-
});
|
|
4154
|
-
return { signature: decodeBase58Signature(result2.signature) };
|
|
4155
|
-
}
|
|
4156
3426
|
const result = await solSigner.signMessage(path, messageBytes);
|
|
4157
|
-
return { signature:
|
|
3427
|
+
return { signature: result.signature };
|
|
4158
3428
|
} catch (err) {
|
|
4159
3429
|
ctx.invalidateSession(sessionId);
|
|
4160
3430
|
throw ctx.wrapError(err);
|
|
@@ -4162,27 +3432,11 @@ async function solSignMessage(ctx, sessionId, params) {
|
|
|
4162
3432
|
ctx.clearCanceller(sessionId);
|
|
4163
3433
|
}
|
|
4164
3434
|
}
|
|
4165
|
-
function decodeBase58Signature(signature) {
|
|
4166
|
-
const bytes = bs58.decode(signature);
|
|
4167
|
-
if (bytes.length !== 64) {
|
|
4168
|
-
throw new Error(`Ledger Solana signature must be 64 bytes, received ${bytes.length}`);
|
|
4169
|
-
}
|
|
4170
|
-
return bytesToHex(bytes);
|
|
4171
|
-
}
|
|
4172
|
-
function decodeBase58EnvelopeSignature(envelope) {
|
|
4173
|
-
const bytes = bs58.decode(envelope);
|
|
4174
|
-
if (bytes.length < 65 || bytes[0] !== 1) {
|
|
4175
|
-
throw new Error("Ledger Solana signature envelope is invalid");
|
|
4176
|
-
}
|
|
4177
|
-
return bytesToHex(bytes.subarray(1, 65));
|
|
4178
|
-
}
|
|
4179
3435
|
async function _createSolSigner(ctx, sessionId) {
|
|
4180
3436
|
const dmk = await ctx.getOrCreateDmk();
|
|
4181
|
-
const { ContextModuleBuilder: ContextModuleBuilder2
|
|
4182
|
-
"@ledgerhq/context-module"
|
|
4183
|
-
);
|
|
3437
|
+
const { ContextModuleBuilder: ContextModuleBuilder2 } = await ctx.importLedgerKit("@ledgerhq/context-module");
|
|
4184
3438
|
const { SignerSolanaBuilder } = await ctx.importLedgerKit("@ledgerhq/device-signer-kit-solana");
|
|
4185
|
-
const contextModule = new ContextModuleBuilder2({}).
|
|
3439
|
+
const contextModule = new ContextModuleBuilder2({}).removeDefaultLoaders().build();
|
|
4186
3440
|
const sdkSigner = new SignerSolanaBuilder({ dmk, sessionId }).withContextModule(contextModule).build();
|
|
4187
3441
|
const signer = new SignerSol(sdkSigner);
|
|
4188
3442
|
signer.onInteraction = (interaction) => {
|
|
@@ -4198,192 +3452,361 @@ async function _createSolSigner(ctx, sessionId) {
|
|
|
4198
3452
|
return signer;
|
|
4199
3453
|
}
|
|
4200
3454
|
|
|
4201
|
-
// src/connector/chains/
|
|
4202
|
-
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";
|
|
4203
3458
|
|
|
4204
|
-
// src/
|
|
4205
|
-
|
|
4206
|
-
// eslint-disable-next-line no-useless-constructor, no-empty-function
|
|
4207
|
-
constructor(_sdk) {
|
|
4208
|
-
this._sdk = _sdk;
|
|
4209
|
-
}
|
|
4210
|
-
/** GET_VK: UFVK string (default) or raw 96-byte Orchard FVK. */
|
|
4211
|
-
async getFullViewingKey(derivationPath, options) {
|
|
4212
|
-
const action = this._sdk.getFullViewingKey(derivationPath, options);
|
|
4213
|
-
return deviceActionToPromise(
|
|
4214
|
-
action,
|
|
4215
|
-
this.onInteraction,
|
|
4216
|
-
void 0,
|
|
4217
|
-
this.onRegisterCanceller
|
|
4218
|
-
);
|
|
4219
|
-
}
|
|
4220
|
-
/** GET_SHIELDED_ADDRESS: single-Orchard-receiver UA for a 5-level transparent path. */
|
|
4221
|
-
async getShieldedAddress(derivationPath, options) {
|
|
4222
|
-
const action = this._sdk.getShieldedAddress(derivationPath, options);
|
|
4223
|
-
return deviceActionToPromise(
|
|
4224
|
-
action,
|
|
4225
|
-
this.onInteraction,
|
|
4226
|
-
void 0,
|
|
4227
|
-
this.onRegisterCanceller
|
|
4228
|
-
);
|
|
4229
|
-
}
|
|
4230
|
-
};
|
|
3459
|
+
// src/connector/chains/legacyChainCall.ts
|
|
3460
|
+
import { EConnectorInteraction as EConnectorInteraction3 } from "@onekeyfe/hwk-adapter-core";
|
|
4231
3461
|
|
|
4232
|
-
// src/
|
|
4233
|
-
|
|
4234
|
-
|
|
4235
|
-
|
|
4236
|
-
|
|
4237
|
-
|
|
4238
|
-
|
|
4239
|
-
|
|
4240
|
-
|
|
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;
|
|
4241
3520
|
}
|
|
4242
|
-
|
|
4243
|
-
|
|
4244
|
-
|
|
4245
|
-
|
|
4246
|
-
|
|
4247
|
-
|
|
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);
|
|
4248
3528
|
}
|
|
4249
|
-
|
|
4250
|
-
|
|
4251
|
-
|
|
4252
|
-
|
|
4253
|
-
|
|
4254
|
-
|
|
4255
|
-
|
|
3529
|
+
// ---------------------------------------------------------------------------
|
|
3530
|
+
// Private helpers
|
|
3531
|
+
// ---------------------------------------------------------------------------
|
|
3532
|
+
async _getCurrentApp(sessionId) {
|
|
3533
|
+
const result = await this._dmk.sendCommand({
|
|
3534
|
+
sessionId,
|
|
3535
|
+
command: new GetAppAndVersionCommand()
|
|
3536
|
+
});
|
|
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()
|
|
4256
3605
|
});
|
|
4257
|
-
return { address: result.address, path: params.path };
|
|
4258
|
-
} catch (err) {
|
|
4259
|
-
ctx.invalidateSession(sessionId);
|
|
4260
|
-
throw ctx.wrapError(err);
|
|
4261
|
-
} finally {
|
|
4262
|
-
ctx.clearCanceller(sessionId);
|
|
4263
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));
|
|
3636
|
+
}
|
|
3637
|
+
};
|
|
3638
|
+
|
|
3639
|
+
// src/connector/chains/legacyChainCall.ts
|
|
3640
|
+
function isLegacyWrongAppError(err, _appName) {
|
|
3641
|
+
return isWrongAppError(err);
|
|
4264
3642
|
}
|
|
4265
|
-
async function
|
|
4266
|
-
const
|
|
4267
|
-
|
|
4268
|
-
const
|
|
4269
|
-
|
|
4270
|
-
signer.onInteraction = (interaction) => {
|
|
4271
|
-
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;
|
|
4272
3648
|
ctx.emit("ui-event", {
|
|
4273
|
-
type:
|
|
3649
|
+
type: EConnectorInteraction3.ConfirmOpenApp,
|
|
4274
3650
|
payload: { sessionId }
|
|
4275
3651
|
});
|
|
4276
3652
|
};
|
|
4277
|
-
|
|
4278
|
-
|
|
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);
|
|
4279
3724
|
}
|
|
4280
3725
|
|
|
4281
|
-
// src/
|
|
4282
|
-
import
|
|
4283
|
-
|
|
4284
|
-
|
|
4285
|
-
|
|
4286
|
-
|
|
4287
|
-
|
|
4288
|
-
this._sdk = _sdk;
|
|
4289
|
-
}
|
|
4290
|
-
/** Base58 Tron address plus the uncompressed public key at `derivationPath`. */
|
|
4291
|
-
async getAddress(derivationPath, options) {
|
|
4292
|
-
const action = this._sdk.getAddress(derivationPath, options);
|
|
4293
|
-
const result = await deviceActionToPromise(
|
|
4294
|
-
action,
|
|
4295
|
-
this.onInteraction,
|
|
4296
|
-
void 0,
|
|
4297
|
-
this.onRegisterCanceller
|
|
4298
|
-
);
|
|
4299
|
-
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;
|
|
4300
3733
|
}
|
|
4301
|
-
|
|
4302
|
-
|
|
4303
|
-
|
|
4304
|
-
|
|
4305
|
-
|
|
4306
|
-
|
|
4307
|
-
|
|
4308
|
-
|
|
4309
|
-
|
|
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;
|
|
4310
3746
|
}
|
|
4311
|
-
|
|
4312
|
-
async signPersonalMessage(derivationPath, message, options) {
|
|
4313
|
-
const action = this._sdk.signPersonalMessage(derivationPath, message, options);
|
|
4314
|
-
return deviceActionToPromise(
|
|
4315
|
-
action,
|
|
4316
|
-
this.onInteraction,
|
|
4317
|
-
void 0,
|
|
4318
|
-
this.onRegisterCanceller
|
|
4319
|
-
);
|
|
3747
|
+
async close() {
|
|
4320
3748
|
}
|
|
4321
3749
|
};
|
|
4322
3750
|
|
|
4323
3751
|
// src/connector/chains/tron.ts
|
|
4324
3752
|
async function tronGetAddress(ctx, sessionId, params) {
|
|
4325
|
-
const tronSigner = await _createTronSigner(ctx, sessionId);
|
|
4326
3753
|
const path = normalizePath(params.path);
|
|
4327
|
-
|
|
4328
|
-
|
|
4329
|
-
|
|
4330
|
-
|
|
4331
|
-
|
|
4332
|
-
|
|
4333
|
-
|
|
4334
|
-
|
|
4335
|
-
|
|
4336
|
-
|
|
4337
|
-
|
|
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
|
+
);
|
|
4338
3770
|
}
|
|
4339
3771
|
async function tronSignTransaction(ctx, sessionId, params) {
|
|
4340
3772
|
if (!params.rawTxHex) {
|
|
4341
3773
|
throw Object.assign(
|
|
4342
3774
|
new Error("TRON signing requires a protobuf-encoded raw transaction hex (rawTxHex)."),
|
|
4343
|
-
{ code:
|
|
3775
|
+
{ code: HardwareErrorCode7.InvalidParams }
|
|
4344
3776
|
);
|
|
4345
3777
|
}
|
|
4346
|
-
const tronSigner = await _createTronSigner(ctx, sessionId);
|
|
4347
3778
|
const path = normalizePath(params.path);
|
|
4348
|
-
|
|
4349
|
-
|
|
4350
|
-
|
|
4351
|
-
|
|
4352
|
-
|
|
4353
|
-
|
|
4354
|
-
|
|
4355
|
-
|
|
4356
|
-
|
|
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
|
+
);
|
|
4357
3793
|
}
|
|
4358
3794
|
async function tronSignMessage(ctx, sessionId, params) {
|
|
4359
|
-
const tronSigner = await _createTronSigner(ctx, sessionId);
|
|
4360
3795
|
const path = normalizePath(params.path);
|
|
4361
|
-
|
|
4362
|
-
|
|
4363
|
-
|
|
4364
|
-
|
|
4365
|
-
|
|
4366
|
-
|
|
4367
|
-
|
|
4368
|
-
|
|
4369
|
-
|
|
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
|
+
);
|
|
4370
3806
|
}
|
|
4371
|
-
async function
|
|
3807
|
+
async function _createTrx(ctx, sessionId) {
|
|
4372
3808
|
const dmk = await ctx.getOrCreateDmk();
|
|
4373
|
-
|
|
4374
|
-
const sdkSigner = new SignerTrxBuilder({ dmk, sessionId }).build();
|
|
4375
|
-
const signer = new SignerTron(sdkSigner);
|
|
4376
|
-
signer.onInteraction = (interaction) => {
|
|
4377
|
-
debugLog("[LedgerConnector] tron.onInteraction:", interaction);
|
|
4378
|
-
ctx.emit("ui-event", {
|
|
4379
|
-
type: collapseSignerInteraction(interaction),
|
|
4380
|
-
payload: { sessionId }
|
|
4381
|
-
});
|
|
4382
|
-
};
|
|
4383
|
-
signer.onRegisterCanceller = (cancel) => {
|
|
4384
|
-
ctx.registerCanceller(sessionId, cancel);
|
|
4385
|
-
};
|
|
4386
|
-
return signer;
|
|
3809
|
+
return new Trx(new DmkTransport(dmk, sessionId));
|
|
4387
3810
|
}
|
|
4388
3811
|
|
|
4389
3812
|
// src/device-apps/customActions.ts
|
|
@@ -4562,7 +3985,7 @@ var DeviceApps = class {
|
|
|
4562
3985
|
seTargetId: v.seTargetId,
|
|
4563
3986
|
mcuTargetId: v.mcuTargetId,
|
|
4564
3987
|
seVersion: v.seVersion,
|
|
4565
|
-
seFlagsHex:
|
|
3988
|
+
seFlagsHex: bytesToHex2(v.seFlags),
|
|
4566
3989
|
mcuSephVersion: v.mcuSephVersion,
|
|
4567
3990
|
mcuBootloaderVersion: v.mcuBootloaderVersion,
|
|
4568
3991
|
hwVersion: v.hwVersion
|
|
@@ -4636,7 +4059,7 @@ var DeviceApps = class {
|
|
|
4636
4059
|
}
|
|
4637
4060
|
}
|
|
4638
4061
|
};
|
|
4639
|
-
function
|
|
4062
|
+
function bytesToHex2(bytes) {
|
|
4640
4063
|
if (!bytes) return "";
|
|
4641
4064
|
return Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join("");
|
|
4642
4065
|
}
|
|
@@ -4679,11 +4102,10 @@ var METHOD_PREFIX_TO_APP_NAME = {
|
|
|
4679
4102
|
evm: "Ethereum",
|
|
4680
4103
|
btc: "Bitcoin",
|
|
4681
4104
|
sol: "Solana",
|
|
4682
|
-
tron: "Tron"
|
|
4683
|
-
zcash: "Zcash"
|
|
4105
|
+
tron: "Tron"
|
|
4684
4106
|
};
|
|
4685
4107
|
var HARDWARE_ERROR_CODE_VALUES = new Set(
|
|
4686
|
-
Object.values(
|
|
4108
|
+
Object.values(HardwareErrorCode8).filter((value) => typeof value === "number")
|
|
4687
4109
|
);
|
|
4688
4110
|
var BLE_CONNECT_SCAN_TIMEOUT_MS = 1500;
|
|
4689
4111
|
var LEDGER_RELAY_ALLOWED_ROOT_DOMAINS = ["onekeytest.com", "onekey.com"];
|
|
@@ -4720,10 +4142,6 @@ async function defaultLedgerKitImporter(pkg) {
|
|
|
4720
4142
|
return import("@ledgerhq/device-signer-kit-bitcoin");
|
|
4721
4143
|
case "@ledgerhq/device-signer-kit-solana":
|
|
4722
4144
|
return import("@ledgerhq/device-signer-kit-solana");
|
|
4723
|
-
case "@ledgerhq/device-signer-kit-tron":
|
|
4724
|
-
return import("@ledgerhq/device-signer-kit-tron");
|
|
4725
|
-
case "@ledgerhq/device-signer-kit-zcash":
|
|
4726
|
-
return import("@ledgerhq/device-signer-kit-zcash");
|
|
4727
4145
|
case "@ledgerhq/context-module":
|
|
4728
4146
|
return import("@ledgerhq/context-module");
|
|
4729
4147
|
default:
|
|
@@ -4882,7 +4300,7 @@ var LedgerConnectorBase = class {
|
|
|
4882
4300
|
`Ledger BLE connect did not return within ${HANG_CEILING_MS / 6e4}min \u2014 DMK hang fallback.`
|
|
4883
4301
|
);
|
|
4884
4302
|
err._tag = ERROR_TAG.BlePairingTimeout;
|
|
4885
|
-
err.code =
|
|
4303
|
+
err.code = HardwareErrorCode8.BlePairingTimeout;
|
|
4886
4304
|
reject(err);
|
|
4887
4305
|
}, HANG_CEILING_MS);
|
|
4888
4306
|
});
|
|
@@ -4915,7 +4333,7 @@ var LedgerConnectorBase = class {
|
|
|
4915
4333
|
"Ledger device is not currently advertising. Wake up and unlock the device, keep it nearby, then try again."
|
|
4916
4334
|
);
|
|
4917
4335
|
err._tag = ERROR_TAG.DeviceNotAdvertising;
|
|
4918
|
-
err.code =
|
|
4336
|
+
err.code = HardwareErrorCode8.DeviceNotFound;
|
|
4919
4337
|
throw err;
|
|
4920
4338
|
};
|
|
4921
4339
|
const doConnect = async (path) => {
|
|
@@ -4979,7 +4397,7 @@ var LedgerConnectorBase = class {
|
|
|
4979
4397
|
"Ledger Bluetooth pairing failed. Make sure the device is unlocked and nearby, then try again."
|
|
4980
4398
|
);
|
|
4981
4399
|
wrapped._tag = ERROR_TAG.BleGattBondingFailed;
|
|
4982
|
-
wrapped.code =
|
|
4400
|
+
wrapped.code = HardwareErrorCode8.BlePairingTimeout;
|
|
4983
4401
|
wrapped.originalError = err;
|
|
4984
4402
|
throw wrapped;
|
|
4985
4403
|
}
|
|
@@ -5057,7 +4475,7 @@ var LedgerConnectorBase = class {
|
|
|
5057
4475
|
this._unwatchSessionState(sessionId);
|
|
5058
4476
|
this._signerManager?.invalidate(sessionId);
|
|
5059
4477
|
this._cancellers.get(sessionId)?.({
|
|
5060
|
-
code:
|
|
4478
|
+
code: HardwareErrorCode8.DeviceDisconnected,
|
|
5061
4479
|
tag: "DeviceDisconnected",
|
|
5062
4480
|
message: "Device disconnected"
|
|
5063
4481
|
});
|
|
@@ -5083,7 +4501,7 @@ var LedgerConnectorBase = class {
|
|
|
5083
4501
|
success: false,
|
|
5084
4502
|
error: serializeConnectorError(
|
|
5085
4503
|
Object.assign(new Error("Ledger app is unresponsive"), {
|
|
5086
|
-
code:
|
|
4504
|
+
code: HardwareErrorCode8.DeviceAppStuck,
|
|
5087
4505
|
_tag: ERROR_TAG.DeviceAppStuck,
|
|
5088
4506
|
originalError: err
|
|
5089
4507
|
})
|
|
@@ -5095,7 +4513,7 @@ var LedgerConnectorBase = class {
|
|
|
5095
4513
|
success: false,
|
|
5096
4514
|
error: serializeConnectorError(
|
|
5097
4515
|
Object.assign(new Error("Device communication interrupted, please retry"), {
|
|
5098
|
-
code:
|
|
4516
|
+
code: HardwareErrorCode8.TransportError,
|
|
5099
4517
|
_tag: ERROR_TAG.DeviceTransportStuck,
|
|
5100
4518
|
originalError: err
|
|
5101
4519
|
})
|
|
@@ -5141,11 +4559,6 @@ var LedgerConnectorBase = class {
|
|
|
5141
4559
|
return solSignTransaction(ctx, sessionId, params);
|
|
5142
4560
|
case "solSignMessage":
|
|
5143
4561
|
return solSignMessage(ctx, sessionId, params);
|
|
5144
|
-
// ZCASH
|
|
5145
|
-
case "zcashGetFullViewingKey":
|
|
5146
|
-
return zcashGetFullViewingKey(ctx, sessionId, params);
|
|
5147
|
-
case "zcashGetShieldedAddress":
|
|
5148
|
-
return zcashGetShieldedAddress(ctx, sessionId, params);
|
|
5149
4562
|
// TRON
|
|
5150
4563
|
case "tronGetAddress":
|
|
5151
4564
|
return tronGetAddress(ctx, sessionId, params);
|
|
@@ -5170,7 +4583,7 @@ var LedgerConnectorBase = class {
|
|
|
5170
4583
|
try {
|
|
5171
4584
|
return await apps.install(p.appName, ({ progress }) => {
|
|
5172
4585
|
ctx.emit("ui-event", {
|
|
5173
|
-
type:
|
|
4586
|
+
type: EConnectorInteraction4.AppInstallProgress,
|
|
5174
4587
|
payload: {
|
|
5175
4588
|
sessionId,
|
|
5176
4589
|
appName: p.appName,
|
|
@@ -5475,7 +4888,7 @@ var LedgerConnectorBase = class {
|
|
|
5475
4888
|
* at every catch site. Falls through unchanged for unknown methods.
|
|
5476
4889
|
*/
|
|
5477
4890
|
_ctxForMethod(method) {
|
|
5478
|
-
const prefix = /^(evm|btc|sol|tron
|
|
4891
|
+
const prefix = /^(evm|btc|sol|tron)/.exec(method)?.[1];
|
|
5479
4892
|
const defaultAppName = prefix ? METHOD_PREFIX_TO_APP_NAME[prefix] : void 0;
|
|
5480
4893
|
if (!defaultAppName) return this._ctx;
|
|
5481
4894
|
return {
|
|
@@ -5509,209 +4922,6 @@ var LedgerConnectorBase = class {
|
|
|
5509
4922
|
return error;
|
|
5510
4923
|
}
|
|
5511
4924
|
};
|
|
5512
|
-
|
|
5513
|
-
// src/transport/DmkTransport.ts
|
|
5514
|
-
import Transport from "@ledgerhq/hw-transport";
|
|
5515
|
-
var DmkTransport = class extends Transport {
|
|
5516
|
-
constructor(dmk, sessionId) {
|
|
5517
|
-
super();
|
|
5518
|
-
this._dmk = dmk;
|
|
5519
|
-
this._sessionId = sessionId;
|
|
5520
|
-
}
|
|
5521
|
-
async exchange(apdu) {
|
|
5522
|
-
const response = await this._dmk.sendApdu({
|
|
5523
|
-
sessionId: this._sessionId,
|
|
5524
|
-
apdu: new Uint8Array(apdu)
|
|
5525
|
-
});
|
|
5526
|
-
const { data, statusCode } = response;
|
|
5527
|
-
const result = Buffer.alloc(data.length + 2);
|
|
5528
|
-
if (data.length > 0) {
|
|
5529
|
-
result.set(data, 0);
|
|
5530
|
-
}
|
|
5531
|
-
result.set(statusCode, data.length);
|
|
5532
|
-
return result;
|
|
5533
|
-
}
|
|
5534
|
-
async close() {
|
|
5535
|
-
}
|
|
5536
|
-
};
|
|
5537
|
-
|
|
5538
|
-
// src/app/AppManager.ts
|
|
5539
|
-
import {
|
|
5540
|
-
CloseAppCommand,
|
|
5541
|
-
GetAppAndVersionCommand,
|
|
5542
|
-
OpenAppCommand,
|
|
5543
|
-
isSuccessCommandResult
|
|
5544
|
-
} from "@ledgerhq/device-management-kit";
|
|
5545
|
-
import { HardwareErrorCode as HardwareErrorCode8 } from "@onekeyfe/hwk-adapter-core";
|
|
5546
|
-
var APP_NAME_MAP = {
|
|
5547
|
-
ETH: "Ethereum",
|
|
5548
|
-
BTC: "Bitcoin",
|
|
5549
|
-
SOL: "Solana",
|
|
5550
|
-
TRX: "Tron",
|
|
5551
|
-
ZEC: "Zcash",
|
|
5552
|
-
XRP: "XRP",
|
|
5553
|
-
ADA: "Cardano",
|
|
5554
|
-
DOT: "Polkadot",
|
|
5555
|
-
ATOM: "Cosmos"
|
|
5556
|
-
};
|
|
5557
|
-
var DASHBOARD_APP_NAME = "BOLOS";
|
|
5558
|
-
var AppManager = class {
|
|
5559
|
-
constructor(dmk, options) {
|
|
5560
|
-
this._dmk = dmk;
|
|
5561
|
-
this._waitMs = options?.waitMs ?? 1e3;
|
|
5562
|
-
this._maxRetries = options?.maxRetries ?? 10;
|
|
5563
|
-
}
|
|
5564
|
-
/**
|
|
5565
|
-
* Return the Ledger app name for a given chain ticker,
|
|
5566
|
-
* or undefined if the chain is not supported.
|
|
5567
|
-
*/
|
|
5568
|
-
static getAppName(chain) {
|
|
5569
|
-
return APP_NAME_MAP[chain];
|
|
5570
|
-
}
|
|
5571
|
-
/**
|
|
5572
|
-
* Ensure the target app is open on the device identified by `sessionId`.
|
|
5573
|
-
*
|
|
5574
|
-
* Flow:
|
|
5575
|
-
* 1. Check the currently running app.
|
|
5576
|
-
* 2. If it is already the target, return immediately.
|
|
5577
|
-
* 3. If a different app is running (not dashboard), close it first.
|
|
5578
|
-
* 4. Open the target app.
|
|
5579
|
-
* 5. Poll until the device confirms the target app is running.
|
|
5580
|
-
*/
|
|
5581
|
-
/**
|
|
5582
|
-
* @param onConfirmOnDevice Called BEFORE OpenAppCommand is issued — the
|
|
5583
|
-
* device is about to display "Open <app>" on screen and wait for the
|
|
5584
|
-
* user's button press. UI consumers should show their "open app" prompt
|
|
5585
|
-
* in response. NOT called when the target app is already open (no user
|
|
5586
|
-
* interaction needed in that case).
|
|
5587
|
-
*
|
|
5588
|
-
* Important: OpenAppCommand is blocking. It does not resolve until the user
|
|
5589
|
-
* has physically confirmed on the device, so anything that runs AFTER
|
|
5590
|
-
* `await this._openApp(...)` lands AFTER the prompt is already gone.
|
|
5591
|
-
* Hence the callback must fire BEFORE that await.
|
|
5592
|
-
*/
|
|
5593
|
-
async ensureAppOpen(sessionId, targetAppName, onConfirmOnDevice) {
|
|
5594
|
-
const currentApp = await this._getCurrentApp(sessionId);
|
|
5595
|
-
if (currentApp === targetAppName) {
|
|
5596
|
-
return;
|
|
5597
|
-
}
|
|
5598
|
-
if (!this._isDashboard(currentApp)) {
|
|
5599
|
-
await this._closeCurrentApp(sessionId);
|
|
5600
|
-
await this._waitForApp(sessionId, DASHBOARD_APP_NAME);
|
|
5601
|
-
}
|
|
5602
|
-
onConfirmOnDevice?.();
|
|
5603
|
-
await this._openApp(sessionId, targetAppName);
|
|
5604
|
-
await this._waitForApp(sessionId, targetAppName);
|
|
5605
|
-
}
|
|
5606
|
-
// ---------------------------------------------------------------------------
|
|
5607
|
-
// Private helpers
|
|
5608
|
-
// ---------------------------------------------------------------------------
|
|
5609
|
-
async _getCurrentApp(sessionId) {
|
|
5610
|
-
const result = await this._dmk.sendCommand({
|
|
5611
|
-
sessionId,
|
|
5612
|
-
command: new GetAppAndVersionCommand()
|
|
5613
|
-
});
|
|
5614
|
-
if (isSuccessCommandResult(result)) {
|
|
5615
|
-
debugLog("[AppManager] currentApp:", result.data.name);
|
|
5616
|
-
return result.data.name;
|
|
5617
|
-
}
|
|
5618
|
-
const errResult = result;
|
|
5619
|
-
const dmkErr = errResult.error ?? {};
|
|
5620
|
-
const original = dmkErr.originalError;
|
|
5621
|
-
debugLog(
|
|
5622
|
-
"[AppManager] _getCurrentApp failed sessionId=",
|
|
5623
|
-
sessionId,
|
|
5624
|
-
"tag=",
|
|
5625
|
-
dmkErr._tag,
|
|
5626
|
-
"errorCode=",
|
|
5627
|
-
dmkErr.errorCode,
|
|
5628
|
-
"message=",
|
|
5629
|
-
dmkErr.message,
|
|
5630
|
-
"originalErrorMessage=",
|
|
5631
|
-
original?.message ?? String(original ?? "")
|
|
5632
|
-
);
|
|
5633
|
-
throw Object.assign(
|
|
5634
|
-
new Error(
|
|
5635
|
-
dmkErr.message ?? "Failed to get current app from device"
|
|
5636
|
-
),
|
|
5637
|
-
{
|
|
5638
|
-
_tag: dmkErr._tag,
|
|
5639
|
-
errorCode: dmkErr.errorCode,
|
|
5640
|
-
originalError: original
|
|
5641
|
-
}
|
|
5642
|
-
);
|
|
5643
|
-
}
|
|
5644
|
-
async _openApp(sessionId, appName) {
|
|
5645
|
-
const result = await this._dmk.sendCommand({
|
|
5646
|
-
sessionId,
|
|
5647
|
-
command: new OpenAppCommand({ appName })
|
|
5648
|
-
});
|
|
5649
|
-
if (!isSuccessCommandResult(result)) {
|
|
5650
|
-
const dmkErr = result.error;
|
|
5651
|
-
const errorCode = "errorCode" in dmkErr && dmkErr.errorCode != null ? String(dmkErr.errorCode) : "";
|
|
5652
|
-
const message = "message" in dmkErr && typeof dmkErr.message === "string" ? dmkErr.message : "";
|
|
5653
|
-
debugLog(
|
|
5654
|
-
"[AppManager] openApp failed:",
|
|
5655
|
-
appName,
|
|
5656
|
-
"errorCode:",
|
|
5657
|
-
errorCode,
|
|
5658
|
-
"tag:",
|
|
5659
|
-
dmkErr._tag
|
|
5660
|
-
);
|
|
5661
|
-
let code;
|
|
5662
|
-
if (errorCode === "6807" || /unknown application/i.test(message)) {
|
|
5663
|
-
code = HardwareErrorCode8.AppNotInstalled;
|
|
5664
|
-
} else if (errorCode === "5501" || dmkErr._tag === "ActionRefusedError") {
|
|
5665
|
-
code = HardwareErrorCode8.UserRejected;
|
|
5666
|
-
}
|
|
5667
|
-
throw Object.assign(new Error(`Failed to open "${appName}"`), {
|
|
5668
|
-
_tag: ERROR_TAG.OpenAppCommand,
|
|
5669
|
-
code,
|
|
5670
|
-
errorCode,
|
|
5671
|
-
statusCode: errorCode,
|
|
5672
|
-
appName,
|
|
5673
|
-
originalError: dmkErr
|
|
5674
|
-
});
|
|
5675
|
-
}
|
|
5676
|
-
}
|
|
5677
|
-
async _closeCurrentApp(sessionId) {
|
|
5678
|
-
debugLog("[AppManager] closeCurrentApp");
|
|
5679
|
-
await this._dmk.sendCommand({
|
|
5680
|
-
sessionId,
|
|
5681
|
-
command: new CloseAppCommand()
|
|
5682
|
-
});
|
|
5683
|
-
}
|
|
5684
|
-
/**
|
|
5685
|
-
* Poll the device until the expected app is reported as running,
|
|
5686
|
-
* or throw after `_maxRetries` attempts.
|
|
5687
|
-
*/
|
|
5688
|
-
async _waitForApp(sessionId, expectedAppName) {
|
|
5689
|
-
let lastSeen = "";
|
|
5690
|
-
for (let i = 0; i < this._maxRetries; i++) {
|
|
5691
|
-
await this._wait();
|
|
5692
|
-
const current = await this._getCurrentApp(sessionId);
|
|
5693
|
-
lastSeen = current;
|
|
5694
|
-
if (current === expectedAppName) {
|
|
5695
|
-
return;
|
|
5696
|
-
}
|
|
5697
|
-
}
|
|
5698
|
-
debugLog(
|
|
5699
|
-
"[AppManager] waitForApp exhausted: expected=",
|
|
5700
|
-
expectedAppName,
|
|
5701
|
-
"lastSeen=",
|
|
5702
|
-
lastSeen
|
|
5703
|
-
);
|
|
5704
|
-
throw new Error(
|
|
5705
|
-
`Ledger: failed to open "${expectedAppName}" after ${this._maxRetries} retries (last seen: ${lastSeen})`
|
|
5706
|
-
);
|
|
5707
|
-
}
|
|
5708
|
-
_isDashboard(appName) {
|
|
5709
|
-
return appName === DASHBOARD_APP_NAME;
|
|
5710
|
-
}
|
|
5711
|
-
_wait() {
|
|
5712
|
-
return new Promise((resolve) => setTimeout(resolve, this._waitMs));
|
|
5713
|
-
}
|
|
5714
|
-
};
|
|
5715
4925
|
export {
|
|
5716
4926
|
AppManager,
|
|
5717
4927
|
DmkTransport,
|
|
@@ -5722,7 +4932,6 @@ export {
|
|
|
5722
4932
|
SignerEth,
|
|
5723
4933
|
SignerManager,
|
|
5724
4934
|
SignerSol,
|
|
5725
|
-
SignerZcash,
|
|
5726
4935
|
debugLog,
|
|
5727
4936
|
deviceActionToPromise,
|
|
5728
4937
|
isDeviceLockedError,
|