@onekeyfe/hwk-ledger-adapter 1.2.2-alpha.9 → 1.2.3-alpha.2
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 +65 -29
- package/dist/index.d.ts +65 -29
- package/dist/index.js +1551 -778
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1570 -779
- package/dist/index.mjs.map +1 -1
- package/package.json +13 -9
package/dist/index.js
CHANGED
|
@@ -39,6 +39,7 @@ __export(index_exports, {
|
|
|
39
39
|
SignerEth: () => SignerEth,
|
|
40
40
|
SignerManager: () => SignerManager,
|
|
41
41
|
SignerSol: () => SignerSol,
|
|
42
|
+
SignerZcash: () => SignerZcash,
|
|
42
43
|
debugLog: () => debugLog,
|
|
43
44
|
deviceActionToPromise: () => deviceActionToPromise,
|
|
44
45
|
isDeviceLockedError: () => isDeviceLockedError,
|
|
@@ -57,17 +58,14 @@ var import_hwk_adapter_core3 = require("@onekeyfe/hwk-adapter-core");
|
|
|
57
58
|
|
|
58
59
|
// src/errors.ts
|
|
59
60
|
var import_hwk_adapter_core = require("@onekeyfe/hwk-adapter-core");
|
|
60
|
-
|
|
61
|
-
function createMultipleUsbLedgerDevicesError() {
|
|
62
|
-
return Object.assign(new Error(MULTIPLE_USB_LEDGER_DEVICES_ERROR_MESSAGE), {
|
|
63
|
-
code: import_hwk_adapter_core.HardwareErrorCode.DeviceOneDeviceOnly
|
|
64
|
-
});
|
|
65
|
-
}
|
|
66
|
-
function ledgerFailure(code, error, appName, tag, params) {
|
|
61
|
+
function ledgerFailure(code, error, appName, tag, params, origin, recovery) {
|
|
67
62
|
const payload = { error, code };
|
|
68
63
|
if (appName !== void 0) payload.appName = appName;
|
|
69
64
|
if (tag !== void 0) payload._tag = tag;
|
|
70
65
|
if (params !== void 0) payload.params = params;
|
|
66
|
+
const resolvedOrigin = origin ?? (0, import_hwk_adapter_core.defaultOriginForCode)(code);
|
|
67
|
+
if (resolvedOrigin !== void 0) payload.origin = resolvedOrigin;
|
|
68
|
+
payload.recovery = recovery ?? (0, import_hwk_adapter_core.defaultRecoveryForCode)(code);
|
|
71
69
|
return { success: false, payload };
|
|
72
70
|
}
|
|
73
71
|
var LOCKED_ERROR_CODES = /* @__PURE__ */ new Set(["5515", "21781", "6982", "27010", "5303", "21251"]);
|
|
@@ -251,7 +249,6 @@ function isBlePairingFailureError(err) {
|
|
|
251
249
|
return false;
|
|
252
250
|
}
|
|
253
251
|
var CONNECTION_LEVEL_TAGS = /* @__PURE__ */ new Set([
|
|
254
|
-
ERROR_TAG.DeviceLocked,
|
|
255
252
|
ERROR_TAG.DeviceNotAdvertising,
|
|
256
253
|
ERROR_TAG.BlePairingTimeout,
|
|
257
254
|
ERROR_TAG.BleGattBondingFailed,
|
|
@@ -486,7 +483,12 @@ function mapLedgerError(err, opts) {
|
|
|
486
483
|
}
|
|
487
484
|
const errAppName = err && typeof err === "object" ? err.appName : void 0;
|
|
488
485
|
const appName = errAppName ?? opts?.defaultAppName;
|
|
489
|
-
return {
|
|
486
|
+
return {
|
|
487
|
+
code,
|
|
488
|
+
message: (0, import_hwk_adapter_core.enrichErrorMessage)(code, originalMessage),
|
|
489
|
+
origin: (0, import_hwk_adapter_core.defaultOriginForCode)(code),
|
|
490
|
+
appName
|
|
491
|
+
};
|
|
490
492
|
}
|
|
491
493
|
|
|
492
494
|
// src/adapter/methods/allNetworkGetAddress.ts
|
|
@@ -547,57 +549,81 @@ var LEDGER_BTC_NETWORK_COIN_MAP = {
|
|
|
547
549
|
var LEDGER_UNSUPPORTED_ALLNETWORK_NETWORKS = /* @__PURE__ */ new Set(["doge", "dogecoin"]);
|
|
548
550
|
function createAllNetworkGetAddress({
|
|
549
551
|
callChain,
|
|
550
|
-
getChainFingerprint
|
|
552
|
+
getChainFingerprint,
|
|
553
|
+
retainOperation,
|
|
554
|
+
errorToFailure
|
|
551
555
|
}) {
|
|
552
556
|
return async function allNetworkGetAddress(connectId, _deviceId, params) {
|
|
553
|
-
debugLog("[LedgerAdapter][REQ]", {
|
|
557
|
+
debugLog("[LedgerAdapter][REQ]", {
|
|
558
|
+
method: "allNetworkGetAddress",
|
|
559
|
+
connectId,
|
|
560
|
+
itemCount: params.bundle.length
|
|
561
|
+
});
|
|
562
|
+
const target = (0, import_hwk_adapter_core2.resolveHardwareOperationTarget)(connectId, params.operationId, "ledger");
|
|
563
|
+
if (!target.success) return target;
|
|
564
|
+
const effectiveTargetId = target.payload.targetId ?? "";
|
|
565
|
+
let releaseOperationRetention;
|
|
566
|
+
try {
|
|
567
|
+
releaseOperationRetention = target.payload.operationId ? retainOperation(target.payload.operationId) : void 0;
|
|
568
|
+
} catch (error) {
|
|
569
|
+
return errorToFailure(error);
|
|
570
|
+
}
|
|
554
571
|
const installContext = {};
|
|
555
572
|
const commonParams = {
|
|
556
|
-
autoInstallApp: params.autoInstallApp
|
|
573
|
+
autoInstallApp: params.autoInstallApp,
|
|
574
|
+
operationId: target.payload.operationId,
|
|
575
|
+
knownConnections: params.knownConnections,
|
|
576
|
+
extra: params.extra,
|
|
577
|
+
allowDeviceSelection: params.allowDeviceSelection
|
|
557
578
|
};
|
|
558
579
|
const chainFingerprints = /* @__PURE__ */ new Map();
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
580
|
+
try {
|
|
581
|
+
const result = await (0, import_hwk_adapter_core2.runAllNetworkGetAddress)({
|
|
582
|
+
connectId: effectiveTargetId,
|
|
583
|
+
deviceId: _deviceId,
|
|
584
|
+
params,
|
|
585
|
+
normalizeItem: normalizeLedgerAllNetworkItem,
|
|
586
|
+
buildUnsupportedNetworkResponse: (item) => isUnsupportedLedgerAllNetworkNetwork(item) ? buildUnsupportedNetworkResponse(item) : void 0,
|
|
587
|
+
callItem: async ({ method, chain, item }) => {
|
|
588
|
+
const itemDeviceId = getItemDeviceId(item) ?? chainFingerprints.get(chain) ?? "";
|
|
589
|
+
return callAllNetworkMethod(
|
|
590
|
+
callChain,
|
|
591
|
+
effectiveTargetId,
|
|
592
|
+
itemDeviceId,
|
|
593
|
+
method,
|
|
594
|
+
item,
|
|
595
|
+
commonParams,
|
|
596
|
+
installContext
|
|
597
|
+
);
|
|
598
|
+
},
|
|
599
|
+
attachIdentity: async ({ item, chain, payload }) => attachLedgerIdentity(
|
|
600
|
+
getChainFingerprint,
|
|
601
|
+
effectiveTargetId,
|
|
572
602
|
item,
|
|
573
|
-
|
|
603
|
+
chain,
|
|
604
|
+
payload,
|
|
605
|
+
chainFingerprints,
|
|
574
606
|
installContext
|
|
575
|
-
)
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
}
|
|
595
|
-
debugLog("[LedgerAdapter][RES]", {
|
|
596
|
-
method: "allNetworkGetAddress",
|
|
597
|
-
success: result.success,
|
|
598
|
-
payload: result
|
|
599
|
-
});
|
|
600
|
-
return result;
|
|
607
|
+
),
|
|
608
|
+
shouldAbortBundle: isTopLevelAllNetworkFailure,
|
|
609
|
+
buildTopLevelFailure: (response) => {
|
|
610
|
+
const code = response.payload?.code ?? import_hwk_adapter_core2.HardwareErrorCode.DeviceMismatch;
|
|
611
|
+
return (0, import_hwk_adapter_core2.failure)(
|
|
612
|
+
code,
|
|
613
|
+
response.payload?.error ?? "All-network get-address aborted",
|
|
614
|
+
response.payload?.params
|
|
615
|
+
);
|
|
616
|
+
}
|
|
617
|
+
});
|
|
618
|
+
debugLog("[LedgerAdapter][RES]", {
|
|
619
|
+
method: "allNetworkGetAddress",
|
|
620
|
+
success: result.success,
|
|
621
|
+
payload: result
|
|
622
|
+
});
|
|
623
|
+
return result;
|
|
624
|
+
} finally {
|
|
625
|
+
releaseOperationRetention?.();
|
|
626
|
+
}
|
|
601
627
|
};
|
|
602
628
|
}
|
|
603
629
|
function isTopLevelAllNetworkFailure(response) {
|
|
@@ -605,7 +631,7 @@ function isTopLevelAllNetworkFailure(response) {
|
|
|
605
631
|
return false;
|
|
606
632
|
}
|
|
607
633
|
const code = response.payload?.code;
|
|
608
|
-
return code === import_hwk_adapter_core2.HardwareErrorCode.DeviceMismatch || code === import_hwk_adapter_core2.HardwareErrorCode.UserAborted || code === import_hwk_adapter_core2.HardwareErrorCode.UserRejected;
|
|
634
|
+
return code === import_hwk_adapter_core2.HardwareErrorCode.DeviceMismatch || code === import_hwk_adapter_core2.HardwareErrorCode.DeviceDisconnected || code === import_hwk_adapter_core2.HardwareErrorCode.OperationTimeout || code === import_hwk_adapter_core2.HardwareErrorCode.TransportError || code === import_hwk_adapter_core2.HardwareErrorCode.OperationEnded || code === import_hwk_adapter_core2.HardwareErrorCode.OperationNotFound || code === import_hwk_adapter_core2.HardwareErrorCode.UserAborted || code === import_hwk_adapter_core2.HardwareErrorCode.UserRejected;
|
|
609
635
|
}
|
|
610
636
|
function getItemDeviceId(item) {
|
|
611
637
|
const { deviceId } = item;
|
|
@@ -635,8 +661,13 @@ function normalizeLedgerAllNetworkItem(method, item) {
|
|
|
635
661
|
const coin = LEDGER_BTC_NETWORK_COIN_MAP[item.network];
|
|
636
662
|
return coin ? { ...item, coin } : item;
|
|
637
663
|
}
|
|
638
|
-
async function attachLedgerIdentity(getChainFingerprint, connectId, item, chain, payload, chainFingerprints) {
|
|
639
|
-
const fingerprint = getItemDeviceId(item) || chainFingerprints.get(chain) || await bootstrapChainFingerprint(
|
|
664
|
+
async function attachLedgerIdentity(getChainFingerprint, connectId, item, chain, payload, chainFingerprints, context) {
|
|
665
|
+
const fingerprint = getItemDeviceId(item) || chainFingerprints.get(chain) || await bootstrapChainFingerprint(
|
|
666
|
+
getChainFingerprint,
|
|
667
|
+
context.connection?.connectId ?? connectId,
|
|
668
|
+
chain,
|
|
669
|
+
context
|
|
670
|
+
);
|
|
640
671
|
if (!fingerprint) {
|
|
641
672
|
return buildFingerprintBootstrapFailure(item, chain);
|
|
642
673
|
}
|
|
@@ -657,8 +688,8 @@ async function attachLedgerIdentity(getChainFingerprint, connectId, item, chain,
|
|
|
657
688
|
}
|
|
658
689
|
};
|
|
659
690
|
}
|
|
660
|
-
async function bootstrapChainFingerprint(getChainFingerprint, connectId, chain) {
|
|
661
|
-
const response = await getChainFingerprint(connectId, chain);
|
|
691
|
+
async function bootstrapChainFingerprint(getChainFingerprint, connectId, chain, context) {
|
|
692
|
+
const response = await getChainFingerprint(connectId, chain, context);
|
|
662
693
|
return response.success ? response.payload : "";
|
|
663
694
|
}
|
|
664
695
|
function buildFingerprintBootstrapFailure(item, chain) {
|
|
@@ -763,8 +794,27 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
763
794
|
constructor(connector, options) {
|
|
764
795
|
this.vendor = "ledger";
|
|
765
796
|
this.emitter = new import_hwk_adapter_core3.TypedEventEmitter();
|
|
797
|
+
this._operations = new import_hwk_adapter_core3.OperationRegistry({
|
|
798
|
+
vendor: "ledger",
|
|
799
|
+
onEnded: (operation, reason) => {
|
|
800
|
+
const binding = this._pendingOperationBindings.get(operation.operationId);
|
|
801
|
+
if (binding?.selectedConnection?.requestId === this._bindingSelectionRequestId) {
|
|
802
|
+
this._finishBleBinding("cancelled");
|
|
803
|
+
}
|
|
804
|
+
this._pendingOperationBindings.delete(operation.operationId);
|
|
805
|
+
this.emitter.emit(import_hwk_adapter_core3.SDK.OPERATION_ENDED, {
|
|
806
|
+
type: import_hwk_adapter_core3.SDK.OPERATION_ENDED,
|
|
807
|
+
payload: { operationId: operation.operationId, reason }
|
|
808
|
+
});
|
|
809
|
+
if (reason === "timeout") {
|
|
810
|
+
void this._releaseOperationConnection(operation);
|
|
811
|
+
}
|
|
812
|
+
}
|
|
813
|
+
});
|
|
766
814
|
this._discoveredDevices = /* @__PURE__ */ new Map();
|
|
767
815
|
this._sessions = /* @__PURE__ */ new Map();
|
|
816
|
+
this._pendingOperationBindings = /* @__PURE__ */ new Map();
|
|
817
|
+
this._verifiedBleReconnectTargets = /* @__PURE__ */ new Map();
|
|
768
818
|
this._uiRegistry = new import_hwk_adapter_core3.UiRequestRegistry();
|
|
769
819
|
// BTC App rejects account index >= 100 unless display=true. Cached per
|
|
770
820
|
// adapter instance: first 100+ path asks the user once via UI request,
|
|
@@ -778,10 +828,30 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
778
828
|
this._deviceAuthenticityQueueTail = Promise.resolve();
|
|
779
829
|
// Shared across concurrent callers — only `cancel()` aborts.
|
|
780
830
|
this._doConnectAbortController = null;
|
|
831
|
+
this._unsettledConnectorOperations = /* @__PURE__ */ new Map();
|
|
832
|
+
this._connectorIdleWaiters = /* @__PURE__ */ new Set();
|
|
833
|
+
this._resetPromise = null;
|
|
834
|
+
this._stateGeneration = 0;
|
|
835
|
+
this._connectorTeardownTail = Promise.resolve();
|
|
836
|
+
this._pendingConnectorTeardowns = 0;
|
|
837
|
+
this._activeOperationJobs = /* @__PURE__ */ new Set();
|
|
838
|
+
this._pendingOperationDisconnects = /* @__PURE__ */ new Set();
|
|
781
839
|
this._installProgressLastEmittedValue = -Infinity;
|
|
782
840
|
this.allNetworkGetAddress = createAllNetworkGetAddress({
|
|
783
841
|
callChain: this.callChain.bind(this),
|
|
784
|
-
getChainFingerprint: (connectId, chain) =>
|
|
842
|
+
getChainFingerprint: async (connectId, chain, context) => {
|
|
843
|
+
try {
|
|
844
|
+
const fingerprint = await this._computeChainFingerprint(
|
|
845
|
+
chain,
|
|
846
|
+
(method, params) => this.connectorCall(connectId, method, params, void 0, void 0, void 0, context)
|
|
847
|
+
);
|
|
848
|
+
return (0, import_hwk_adapter_core3.success)(fingerprint);
|
|
849
|
+
} catch (error) {
|
|
850
|
+
return this.errorToFailure(error);
|
|
851
|
+
}
|
|
852
|
+
},
|
|
853
|
+
retainOperation: (operationId) => this._operations.retain(operationId),
|
|
854
|
+
errorToFailure: (error) => this.errorToFailure(error)
|
|
785
855
|
});
|
|
786
856
|
// ---------------------------------------------------------------------------
|
|
787
857
|
// Private helpers
|
|
@@ -791,7 +861,7 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
791
861
|
*
|
|
792
862
|
* - If a session already exists for the given connectId, reuse it.
|
|
793
863
|
* - If ANY session exists (Ledger IDs are ephemeral), reuse it.
|
|
794
|
-
* - Otherwise: search →
|
|
864
|
+
* - Otherwise: search → one USB device auto-connects; multiple asks the host to choose.
|
|
795
865
|
*/
|
|
796
866
|
// Mutex for ensureConnected — prevents concurrent calls from establishing duplicate connections
|
|
797
867
|
this._connectingPromise = null;
|
|
@@ -808,6 +878,12 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
808
878
|
});
|
|
809
879
|
};
|
|
810
880
|
this.deviceDisconnectHandler = (data) => {
|
|
881
|
+
const activeOperation = this._operations.findActiveByConnectionKey(data.connectId);
|
|
882
|
+
if (activeOperation && this._activeOperationJobs.has(activeOperation.operationId)) {
|
|
883
|
+
this._pendingOperationDisconnects.add(activeOperation.operationId);
|
|
884
|
+
} else {
|
|
885
|
+
this._operations.endByConnectionKey(data.connectId, "disconnect");
|
|
886
|
+
}
|
|
811
887
|
this._discoveredDevices.delete(data.connectId);
|
|
812
888
|
this._sessions.delete(data.connectId);
|
|
813
889
|
this.emitter.emit(import_hwk_adapter_core3.DEVICE.DISCONNECT, {
|
|
@@ -864,11 +940,28 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
864
940
|
this._jobQueue = new import_hwk_adapter_core3.DeviceJobQueue();
|
|
865
941
|
this.registerEventListeners();
|
|
866
942
|
}
|
|
943
|
+
_finishBleBinding(status) {
|
|
944
|
+
const selectionRequestId = this._bindingSelectionRequestId;
|
|
945
|
+
this._bindingSelectionRequestId = void 0;
|
|
946
|
+
if (selectionRequestId)
|
|
947
|
+
this.emitter.emit(import_hwk_adapter_core3.UI_REQUEST.DEVICE_BINDING_STATUS, {
|
|
948
|
+
type: import_hwk_adapter_core3.UI_REQUEST.DEVICE_BINDING_STATUS,
|
|
949
|
+
payload: { selectionRequestId, status }
|
|
950
|
+
});
|
|
951
|
+
}
|
|
952
|
+
_isBleConnection() {
|
|
953
|
+
return isLedgerBleConnectionType(this._activeConnectionType ?? this.connector.connectionType);
|
|
954
|
+
}
|
|
867
955
|
// Transport
|
|
868
956
|
get activeTransport() {
|
|
869
|
-
return
|
|
957
|
+
return this._isBleConnection() ? "ble" : "hid";
|
|
870
958
|
}
|
|
871
959
|
getAvailableTransports() {
|
|
960
|
+
if (this.connector.availableTransports) {
|
|
961
|
+
return this.connector.availableTransports.map(
|
|
962
|
+
(transport) => transport === "ble" ? "ble" : "hid"
|
|
963
|
+
);
|
|
964
|
+
}
|
|
872
965
|
return this.activeTransport ? [this.activeTransport] : [];
|
|
873
966
|
}
|
|
874
967
|
// Connector is bound at construction; switching requires a new adapter.
|
|
@@ -885,21 +978,39 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
885
978
|
* The next operation will re-discover and re-connect automatically.
|
|
886
979
|
*/
|
|
887
980
|
resetState() {
|
|
981
|
+
void this._resetStateAndDisconnectSessions();
|
|
982
|
+
}
|
|
983
|
+
_resetStateAndDisconnectSessions() {
|
|
984
|
+
if (this._resetPromise) return this._resetPromise;
|
|
985
|
+
const sessionIds = new Set(this._sessions.values());
|
|
986
|
+
this._stateGeneration += 1;
|
|
987
|
+
this._finishBleBinding("cancelled");
|
|
988
|
+
this._operations.endAll("runtime-reset");
|
|
989
|
+
this._doConnectAbortController?.abort();
|
|
888
990
|
this._discoveredDevices.clear();
|
|
889
991
|
this._sessions.clear();
|
|
992
|
+
this._verifiedBleReconnectTargets.clear();
|
|
890
993
|
this._connectingPromise = null;
|
|
994
|
+
this._doConnectAbortController = null;
|
|
891
995
|
this._uiRegistry.reset();
|
|
892
996
|
this._jobQueue.clear();
|
|
893
997
|
this._btcHighIndexConfirmedThisSession = false;
|
|
998
|
+
const resetPromise = this._runConnectorTeardown(async () => {
|
|
999
|
+
for (const sessionId of sessionIds) {
|
|
1000
|
+
await this.connector.disconnect(sessionId).catch(() => void 0);
|
|
1001
|
+
}
|
|
1002
|
+
});
|
|
1003
|
+
this._resetPromise = resetPromise;
|
|
1004
|
+
return resetPromise.finally(() => {
|
|
1005
|
+
if (this._resetPromise === resetPromise) {
|
|
1006
|
+
this._resetPromise = null;
|
|
1007
|
+
}
|
|
1008
|
+
});
|
|
894
1009
|
}
|
|
895
1010
|
async dispose() {
|
|
896
|
-
this.
|
|
897
|
-
this._jobQueue.clear();
|
|
1011
|
+
await this._resetStateAndDisconnectSessions();
|
|
898
1012
|
this.unregisterEventListeners();
|
|
899
1013
|
this.connector.reset();
|
|
900
|
-
this._discoveredDevices.clear();
|
|
901
|
-
this._sessions.clear();
|
|
902
|
-
this._btcHighIndexConfirmedThisSession = false;
|
|
903
1014
|
this.emitter.removeAllListeners();
|
|
904
1015
|
}
|
|
905
1016
|
uiResponse(response) {
|
|
@@ -909,17 +1020,32 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
909
1020
|
// Device management
|
|
910
1021
|
// ---------------------------------------------------------------------------
|
|
911
1022
|
async searchDevices(options) {
|
|
1023
|
+
return this._searchDevices(options);
|
|
1024
|
+
}
|
|
1025
|
+
async _searchDevices(options, signal) {
|
|
912
1026
|
debugLog("[LedgerAdapter][REQ]", { method: "searchDevices", params: options });
|
|
913
1027
|
try {
|
|
914
1028
|
if (options?.resetSession) {
|
|
915
|
-
this.
|
|
916
|
-
|
|
917
|
-
this.
|
|
918
|
-
|
|
919
|
-
|
|
1029
|
+
await this._resetStateAndDisconnectSessions();
|
|
1030
|
+
} else {
|
|
1031
|
+
await this._connectorTeardownTail;
|
|
1032
|
+
}
|
|
1033
|
+
await this._ensureDevicePermission(void 0, void 0, signal);
|
|
1034
|
+
if (signal) _LedgerAdapter._throwIfAborted(signal);
|
|
1035
|
+
const stateGeneration = this._stateGeneration;
|
|
1036
|
+
const devices = await this.connector.searchDevices(
|
|
1037
|
+
options?.transportType ? {
|
|
1038
|
+
transportType: options.transportType,
|
|
1039
|
+
waitForAll: options.waitForAllTransports
|
|
1040
|
+
} : void 0
|
|
1041
|
+
);
|
|
1042
|
+
if (signal) _LedgerAdapter._throwIfAborted(signal);
|
|
1043
|
+
if (stateGeneration !== this._stateGeneration) {
|
|
1044
|
+
throw (0, import_hwk_adapter_core3.createHwkError)({
|
|
1045
|
+
code: import_hwk_adapter_core3.HardwareErrorCode.UserAborted,
|
|
1046
|
+
message: "Ledger discovery was reset"
|
|
1047
|
+
});
|
|
920
1048
|
}
|
|
921
|
-
await this._ensureDevicePermission();
|
|
922
|
-
const devices = await this.connector.searchDevices();
|
|
923
1049
|
this._discoveredDevices.clear();
|
|
924
1050
|
for (const d of devices) {
|
|
925
1051
|
if (d.connectId) {
|
|
@@ -927,7 +1053,7 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
927
1053
|
}
|
|
928
1054
|
}
|
|
929
1055
|
if (this._discoveredDevices.size === 0) {
|
|
930
|
-
await this._ensureDevicePermission();
|
|
1056
|
+
await this._ensureDevicePermission(void 0, void 0, signal);
|
|
931
1057
|
}
|
|
932
1058
|
const result = Array.from(this._discoveredDevices.values());
|
|
933
1059
|
debugLog("[LedgerAdapter][RES]", {
|
|
@@ -946,41 +1072,285 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
946
1072
|
throw err;
|
|
947
1073
|
}
|
|
948
1074
|
}
|
|
1075
|
+
async searchDeviceTargets(options) {
|
|
1076
|
+
const devices = await this.searchDevices(options);
|
|
1077
|
+
return devices.map((device) => ({
|
|
1078
|
+
searchTargetId: device.connectId,
|
|
1079
|
+
searchTargetReusePolicy: (0, import_hwk_adapter_core3.resolveSearchTargetReusePolicy)(device),
|
|
1080
|
+
vendor: "ledger",
|
|
1081
|
+
connectionType: device.connectionType,
|
|
1082
|
+
kind: "physical",
|
|
1083
|
+
label: device.label,
|
|
1084
|
+
model: device.model,
|
|
1085
|
+
modelName: device.modelName,
|
|
1086
|
+
serialNumber: device.serialNumber
|
|
1087
|
+
}));
|
|
1088
|
+
}
|
|
1089
|
+
async listConnectionTargets(options) {
|
|
1090
|
+
const targets = await this.searchDeviceTargets(options);
|
|
1091
|
+
return targets.map(({ searchTargetId, ...target }) => ({
|
|
1092
|
+
...target,
|
|
1093
|
+
targetId: searchTargetId
|
|
1094
|
+
}));
|
|
1095
|
+
}
|
|
949
1096
|
// USB single-session invariant: evict all sessions, best-effort (see connectDevice).
|
|
950
|
-
async _evictAllSessions() {
|
|
1097
|
+
async _evictAllSessions(preserveOperationId) {
|
|
1098
|
+
this._operations.endAll("explicit", preserveOperationId);
|
|
951
1099
|
if (this._sessions.size === 0) return;
|
|
952
1100
|
const stale = [...this._sessions.values()];
|
|
953
1101
|
this._sessions.clear();
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
1102
|
+
await this._runConnectorTeardown(async () => {
|
|
1103
|
+
for (const sid of stale) {
|
|
1104
|
+
try {
|
|
1105
|
+
await this.connector.disconnect(sid);
|
|
1106
|
+
} catch {
|
|
1107
|
+
}
|
|
958
1108
|
}
|
|
959
|
-
}
|
|
1109
|
+
});
|
|
960
1110
|
}
|
|
961
1111
|
static _createDeviceBusyError(method) {
|
|
962
1112
|
return Object.assign(new Error(`Ledger device is busy while calling ${method}`), {
|
|
963
1113
|
code: import_hwk_adapter_core3.HardwareErrorCode.DeviceBusy
|
|
964
1114
|
});
|
|
965
1115
|
}
|
|
966
|
-
async connectDevice(
|
|
1116
|
+
async connectDevice(searchTargetId) {
|
|
1117
|
+
try {
|
|
1118
|
+
return await this._jobQueue.enqueue(
|
|
1119
|
+
searchTargetId || "__ledger_connect__",
|
|
1120
|
+
async () => {
|
|
1121
|
+
const connected = await this._connectTarget(searchTargetId);
|
|
1122
|
+
if (!connected.success) return connected;
|
|
1123
|
+
return this._createOperation(searchTargetId, connected.payload);
|
|
1124
|
+
},
|
|
1125
|
+
{
|
|
1126
|
+
label: "connectDevice",
|
|
1127
|
+
rejectIfBusy: true,
|
|
1128
|
+
busyError: _LedgerAdapter._createDeviceBusyError("connectDevice")
|
|
1129
|
+
}
|
|
1130
|
+
);
|
|
1131
|
+
} catch (error) {
|
|
1132
|
+
return this.errorToFailure(error);
|
|
1133
|
+
}
|
|
1134
|
+
}
|
|
1135
|
+
async bindBleDevice(params) {
|
|
1136
|
+
if (params.identity.vendor !== "ledger" || !params.identity.value) {
|
|
1137
|
+
return (0, import_hwk_adapter_core3.failure)(import_hwk_adapter_core3.HardwareErrorCode.InvalidParams, "Ledger wallet identity is required");
|
|
1138
|
+
}
|
|
1139
|
+
const { chain, value: expectedFingerprint } = params.identity;
|
|
1140
|
+
try {
|
|
1141
|
+
return await this._jobQueue.enqueue(
|
|
1142
|
+
expectedFingerprint,
|
|
1143
|
+
async (signal) => {
|
|
1144
|
+
if (!this.getAvailableTransports().includes("ble")) {
|
|
1145
|
+
throw (0, import_hwk_adapter_core3.createHwkError)({
|
|
1146
|
+
code: import_hwk_adapter_core3.HardwareErrorCode.TransportNotAvailable,
|
|
1147
|
+
message: "Ledger Bluetooth transport is not available"
|
|
1148
|
+
});
|
|
1149
|
+
}
|
|
1150
|
+
this._activeConnectionType = "ble";
|
|
1151
|
+
await this._ensureDevicePermission(void 0, void 0, signal);
|
|
1152
|
+
const attempt = {
|
|
1153
|
+
extra: params.extra,
|
|
1154
|
+
bindingReason: "manual-rebind"
|
|
1155
|
+
};
|
|
1156
|
+
try {
|
|
1157
|
+
for (; ; ) {
|
|
1158
|
+
const connectId = await this._connectFirstOrSelect(
|
|
1159
|
+
[],
|
|
1160
|
+
void 0,
|
|
1161
|
+
true,
|
|
1162
|
+
void 0,
|
|
1163
|
+
attempt,
|
|
1164
|
+
signal
|
|
1165
|
+
);
|
|
1166
|
+
const sessionId = this._sessions.get(connectId);
|
|
1167
|
+
if (!sessionId) {
|
|
1168
|
+
throw (0, import_hwk_adapter_core3.createHwkError)({
|
|
1169
|
+
code: import_hwk_adapter_core3.HardwareErrorCode.DeviceDisconnected,
|
|
1170
|
+
message: "Selected Ledger connection ended"
|
|
1171
|
+
});
|
|
1172
|
+
}
|
|
1173
|
+
let saved = false;
|
|
1174
|
+
try {
|
|
1175
|
+
const installContext = {
|
|
1176
|
+
connection: { connectId, sessionId }
|
|
1177
|
+
};
|
|
1178
|
+
const fingerprint = await this._computeChainFingerprint(
|
|
1179
|
+
chain,
|
|
1180
|
+
(method, callParams) => this._runConnectorCall(
|
|
1181
|
+
connectId,
|
|
1182
|
+
method,
|
|
1183
|
+
callParams,
|
|
1184
|
+
signal,
|
|
1185
|
+
void 0,
|
|
1186
|
+
void 0,
|
|
1187
|
+
{ autoInstallApp: true },
|
|
1188
|
+
installContext
|
|
1189
|
+
)
|
|
1190
|
+
);
|
|
1191
|
+
if (fingerprint !== expectedFingerprint) {
|
|
1192
|
+
attempt.rejectedConnectIds ?? (attempt.rejectedConnectIds = /* @__PURE__ */ new Set());
|
|
1193
|
+
attempt.rejectedConnectIds.add(connectId);
|
|
1194
|
+
attempt.rejectedConnectId = connectId;
|
|
1195
|
+
} else {
|
|
1196
|
+
const persisted = await this._publishVerifiedBleBinding(
|
|
1197
|
+
connectId,
|
|
1198
|
+
chain,
|
|
1199
|
+
fingerprint,
|
|
1200
|
+
attempt,
|
|
1201
|
+
void 0,
|
|
1202
|
+
signal
|
|
1203
|
+
);
|
|
1204
|
+
if (!persisted) {
|
|
1205
|
+
throw (0, import_hwk_adapter_core3.createHwkError)({
|
|
1206
|
+
code: import_hwk_adapter_core3.HardwareErrorCode.UnknownError,
|
|
1207
|
+
message: "Bluetooth binding could not be saved",
|
|
1208
|
+
origin: "host"
|
|
1209
|
+
});
|
|
1210
|
+
}
|
|
1211
|
+
saved = true;
|
|
1212
|
+
return (0, import_hwk_adapter_core3.success)(connectId);
|
|
1213
|
+
}
|
|
1214
|
+
} finally {
|
|
1215
|
+
if (!saved && this._sessions.get(connectId) === sessionId) {
|
|
1216
|
+
this._sessions.delete(connectId);
|
|
1217
|
+
const teardown = this._runConnectorTeardown(
|
|
1218
|
+
() => this.connector.disconnect(sessionId)
|
|
1219
|
+
).catch(() => void 0);
|
|
1220
|
+
if (!signal.aborted) await teardown;
|
|
1221
|
+
}
|
|
1222
|
+
}
|
|
1223
|
+
}
|
|
1224
|
+
} catch (error) {
|
|
1225
|
+
this._finishBleBinding(signal.aborted ? "cancelled" : "failed");
|
|
1226
|
+
throw error;
|
|
1227
|
+
}
|
|
1228
|
+
},
|
|
1229
|
+
{
|
|
1230
|
+
label: "bindBleDevice",
|
|
1231
|
+
rejectIfBusy: true,
|
|
1232
|
+
busyError: _LedgerAdapter._createDeviceBusyError("bindBleDevice")
|
|
1233
|
+
}
|
|
1234
|
+
);
|
|
1235
|
+
} catch (error) {
|
|
1236
|
+
return this.errorToFailure(error);
|
|
1237
|
+
}
|
|
1238
|
+
}
|
|
1239
|
+
async acquireOperation(connectId, context) {
|
|
1240
|
+
try {
|
|
1241
|
+
return await this._jobQueue.enqueue(
|
|
1242
|
+
connectId || "__ledger_acquire__",
|
|
1243
|
+
async (signal) => {
|
|
1244
|
+
const transport = this._isBleConnection() ? "ble" : "usb";
|
|
1245
|
+
const hint = context.knownConnections?.find(
|
|
1246
|
+
(connection) => connection.transport === transport
|
|
1247
|
+
);
|
|
1248
|
+
const target = hint && hint.transport !== "qr" ? hint.connectId : connectId;
|
|
1249
|
+
await this._ensureDevicePermission(target, void 0, signal);
|
|
1250
|
+
const attempt = {
|
|
1251
|
+
...context,
|
|
1252
|
+
extra: context.extra ? { ...context.extra } : void 0
|
|
1253
|
+
};
|
|
1254
|
+
try {
|
|
1255
|
+
const resolvedConnectId = await this.ensureConnected(
|
|
1256
|
+
target,
|
|
1257
|
+
signal,
|
|
1258
|
+
true,
|
|
1259
|
+
void 0,
|
|
1260
|
+
attempt
|
|
1261
|
+
);
|
|
1262
|
+
_LedgerAdapter._throwIfAborted(signal);
|
|
1263
|
+
const result = this._createOperation(connectId, resolvedConnectId);
|
|
1264
|
+
if (result.success && attempt.selectedConnection) {
|
|
1265
|
+
this._pendingOperationBindings.set(result.payload, attempt);
|
|
1266
|
+
}
|
|
1267
|
+
return result;
|
|
1268
|
+
} catch (error) {
|
|
1269
|
+
this._finishBleBinding(signal.aborted ? "cancelled" : "failed");
|
|
1270
|
+
throw error;
|
|
1271
|
+
}
|
|
1272
|
+
},
|
|
1273
|
+
{
|
|
1274
|
+
label: "acquireOperation",
|
|
1275
|
+
rejectIfBusy: true,
|
|
1276
|
+
busyError: _LedgerAdapter._createDeviceBusyError("acquireOperation")
|
|
1277
|
+
}
|
|
1278
|
+
);
|
|
1279
|
+
} catch (error) {
|
|
1280
|
+
return this.errorToFailure(error);
|
|
1281
|
+
}
|
|
1282
|
+
}
|
|
1283
|
+
_createOperation(searchTargetId, resolvedConnectId) {
|
|
1284
|
+
this._operations.endByConnectionKey(resolvedConnectId, "explicit");
|
|
1285
|
+
const sessionId = this._sessions.get(resolvedConnectId);
|
|
1286
|
+
if (sessionId) this._operations.endByConnectionKey(sessionId, "explicit");
|
|
1287
|
+
const device = this._discoveredDevices.get(resolvedConnectId) ?? {
|
|
1288
|
+
vendor: "ledger",
|
|
1289
|
+
model: "unknown",
|
|
1290
|
+
firmwareVersion: "",
|
|
1291
|
+
deviceId: "",
|
|
1292
|
+
connectId: resolvedConnectId,
|
|
1293
|
+
connectionType: this.connector.connectionType
|
|
1294
|
+
};
|
|
1295
|
+
const operation = this._operations.create({
|
|
1296
|
+
searchTargetId,
|
|
1297
|
+
connectId: resolvedConnectId,
|
|
1298
|
+
device,
|
|
1299
|
+
connectionKeys: [this._sessions.get(resolvedConnectId) ?? ""]
|
|
1300
|
+
});
|
|
1301
|
+
return (0, import_hwk_adapter_core3.success)(operation.operationId);
|
|
1302
|
+
}
|
|
1303
|
+
async _connectTarget(connectId, preserveOperationId, signal) {
|
|
967
1304
|
debugLog("[LedgerAdapter][REQ]", { method: "connectDevice", connectId, params: { connectId } });
|
|
968
1305
|
try {
|
|
969
|
-
|
|
1306
|
+
this._assertConnectorReady("connectDevice");
|
|
1307
|
+
const discoveredType = this._discoveredDevices.get(connectId)?.connectionType;
|
|
1308
|
+
if (discoveredType === "usb" || discoveredType === "ble") {
|
|
1309
|
+
this._activeConnectionType = discoveredType;
|
|
1310
|
+
}
|
|
1311
|
+
if (this._isBleConnection() && !connectId) {
|
|
970
1312
|
throw Object.assign(new Error("Ledger BLE connectId is required."), {
|
|
971
1313
|
code: import_hwk_adapter_core3.HardwareErrorCode.DeviceNotFound
|
|
972
1314
|
});
|
|
973
1315
|
}
|
|
974
|
-
|
|
975
|
-
|
|
1316
|
+
await this._ensureDevicePermission(connectId, void 0, signal);
|
|
1317
|
+
if (signal) _LedgerAdapter._throwIfAborted(signal);
|
|
1318
|
+
if (this.connector.availableTransports && this.connector.availableTransports.length > 1) {
|
|
1319
|
+
await this._evictAllSessions(preserveOperationId);
|
|
1320
|
+
} else if (this._isBleConnection()) {
|
|
1321
|
+
const previousSessionId = this._sessions.get(connectId);
|
|
1322
|
+
this._operations.endByConnectionKey(connectId, "explicit", preserveOperationId);
|
|
1323
|
+
if (previousSessionId) {
|
|
1324
|
+
this._operations.endByConnectionKey(previousSessionId, "explicit", preserveOperationId);
|
|
1325
|
+
this._sessions.delete(connectId);
|
|
1326
|
+
await this.connector.disconnect(previousSessionId).catch(() => void 0);
|
|
1327
|
+
}
|
|
1328
|
+
} else {
|
|
1329
|
+
await this._evictAllSessions(preserveOperationId);
|
|
1330
|
+
}
|
|
1331
|
+
if (signal) _LedgerAdapter._throwIfAborted(signal);
|
|
1332
|
+
const stateGeneration = this._stateGeneration;
|
|
1333
|
+
const releaseOperation = this._retainConnectorOperation(`connect:${connectId}`);
|
|
1334
|
+
let session;
|
|
1335
|
+
try {
|
|
1336
|
+
session = this.connector.availableTransports?.length ? await this.connector.connect(connectId, {
|
|
1337
|
+
transportType: this._activeConnectionType ?? "usb"
|
|
1338
|
+
}) : await this.connector.connect(connectId);
|
|
1339
|
+
if (signal?.aborted || stateGeneration !== this._stateGeneration) {
|
|
1340
|
+
await this.connector.disconnect(session.sessionId).catch(() => void 0);
|
|
1341
|
+
throw Object.assign(new Error("Ledger connection aborted"), {
|
|
1342
|
+
code: import_hwk_adapter_core3.HardwareErrorCode.UserAborted
|
|
1343
|
+
});
|
|
1344
|
+
}
|
|
1345
|
+
} finally {
|
|
1346
|
+
releaseOperation();
|
|
976
1347
|
}
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
this._sessions.set(connectId, session.sessionId);
|
|
1348
|
+
const resolvedConnectId = session.deviceInfo?.connectId || connectId;
|
|
1349
|
+
this._sessions.set(resolvedConnectId, session.sessionId);
|
|
980
1350
|
if (session.deviceInfo) {
|
|
981
|
-
this._discoveredDevices.set(
|
|
1351
|
+
this._discoveredDevices.set(resolvedConnectId, session.deviceInfo);
|
|
982
1352
|
}
|
|
983
|
-
const result = (0, import_hwk_adapter_core3.success)(
|
|
1353
|
+
const result = (0, import_hwk_adapter_core3.success)(resolvedConnectId);
|
|
984
1354
|
debugLog("[LedgerAdapter][RES]", { method: "connectDevice", success: true, payload: result });
|
|
985
1355
|
return result;
|
|
986
1356
|
} catch (err) {
|
|
@@ -993,30 +1363,60 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
993
1363
|
return failureResult;
|
|
994
1364
|
}
|
|
995
1365
|
}
|
|
996
|
-
async
|
|
1366
|
+
async releaseOperation(operationId) {
|
|
1367
|
+
const operation = this._operations.find(operationId);
|
|
1368
|
+
if (!operation) {
|
|
1369
|
+
this._operations.resolve(operationId);
|
|
1370
|
+
return;
|
|
1371
|
+
}
|
|
1372
|
+
const endedOperation = this._operations.end(operationId, "explicit");
|
|
1373
|
+
if (!endedOperation) return;
|
|
1374
|
+
const { connectId } = operation;
|
|
997
1375
|
debugLog("[LedgerAdapter][REQ]", {
|
|
998
|
-
method: "
|
|
1376
|
+
method: "releaseOperation",
|
|
999
1377
|
connectId,
|
|
1000
1378
|
params: { connectId }
|
|
1001
1379
|
});
|
|
1002
1380
|
try {
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
await this.connector.disconnect(sessionId);
|
|
1006
|
-
this._sessions.delete(connectId);
|
|
1007
|
-
}
|
|
1008
|
-
debugLog("[LedgerAdapter][RES]", { method: "disconnectDevice", success: true });
|
|
1381
|
+
await this._releaseOperationConnection(endedOperation);
|
|
1382
|
+
debugLog("[LedgerAdapter][RES]", { method: "releaseOperation", success: true });
|
|
1009
1383
|
} catch (err) {
|
|
1010
1384
|
const e = err;
|
|
1011
1385
|
debugLog("[LedgerAdapter][RES]", {
|
|
1012
|
-
method: "
|
|
1386
|
+
method: "releaseOperation",
|
|
1013
1387
|
success: false,
|
|
1014
1388
|
error: { message: e?.message, _tag: e?._tag, code: e?.code ?? e?.errorCode }
|
|
1015
1389
|
});
|
|
1016
1390
|
throw err;
|
|
1017
1391
|
}
|
|
1018
1392
|
}
|
|
1019
|
-
async
|
|
1393
|
+
async _releaseOperationConnection(operation) {
|
|
1394
|
+
const sessionIds = /* @__PURE__ */ new Set();
|
|
1395
|
+
for (const [connectId, sessionId] of this._sessions) {
|
|
1396
|
+
if (connectId === operation.connectId || operation.connectionKeys.includes(connectId) || operation.connectionKeys.includes(sessionId)) {
|
|
1397
|
+
this._sessions.delete(connectId);
|
|
1398
|
+
sessionIds.add(sessionId);
|
|
1399
|
+
}
|
|
1400
|
+
}
|
|
1401
|
+
for (const sessionId of sessionIds) {
|
|
1402
|
+
await this._runConnectorTeardown(
|
|
1403
|
+
() => this.connector.disconnect(sessionId).catch(() => void 0)
|
|
1404
|
+
);
|
|
1405
|
+
}
|
|
1406
|
+
}
|
|
1407
|
+
async _releaseLostOperationConnection(operationId) {
|
|
1408
|
+
const endedOperation = this._operations.end(operationId, "disconnect");
|
|
1409
|
+
if (!endedOperation) return;
|
|
1410
|
+
this._discoveredDevices.delete(endedOperation.connectId);
|
|
1411
|
+
await this._releaseOperationConnection(endedOperation);
|
|
1412
|
+
}
|
|
1413
|
+
async getDeviceInfo(connectIdOrOperationId, deviceId) {
|
|
1414
|
+
let connectId;
|
|
1415
|
+
try {
|
|
1416
|
+
connectId = (0, import_hwk_adapter_core3.isHardwareOperationId)(connectIdOrOperationId) ? this._operations.resolve(connectIdOrOperationId).connectId : connectIdOrOperationId;
|
|
1417
|
+
} catch (error) {
|
|
1418
|
+
return this.errorToFailure(error);
|
|
1419
|
+
}
|
|
1020
1420
|
debugLog("[LedgerAdapter][REQ]", {
|
|
1021
1421
|
method: "getDeviceInfo",
|
|
1022
1422
|
connectId,
|
|
@@ -1054,9 +1454,6 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
1054
1454
|
throw err;
|
|
1055
1455
|
}
|
|
1056
1456
|
}
|
|
1057
|
-
getSupportedChains() {
|
|
1058
|
-
return ["evm", "btc", "sol", "tron"];
|
|
1059
|
-
}
|
|
1060
1457
|
// ---------------------------------------------------------------------------
|
|
1061
1458
|
// Chain call helper
|
|
1062
1459
|
// ---------------------------------------------------------------------------
|
|
@@ -1091,13 +1488,21 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
1091
1488
|
if (params && typeof params === "object") {
|
|
1092
1489
|
const {
|
|
1093
1490
|
autoInstallApp,
|
|
1491
|
+
operationId,
|
|
1094
1492
|
passphraseState: _passphraseState,
|
|
1095
1493
|
useEmptyPassphrase: _useEmptyPassphrase,
|
|
1494
|
+
knownConnections,
|
|
1495
|
+
extra,
|
|
1496
|
+
allowDeviceSelection,
|
|
1096
1497
|
...rest
|
|
1097
1498
|
} = params;
|
|
1098
1499
|
return {
|
|
1099
1500
|
commonParams: {
|
|
1100
|
-
autoInstallApp: typeof autoInstallApp === "boolean" ? autoInstallApp : void 0
|
|
1501
|
+
autoInstallApp: typeof autoInstallApp === "boolean" ? autoInstallApp : void 0,
|
|
1502
|
+
operationId: typeof operationId === "string" ? operationId : void 0,
|
|
1503
|
+
knownConnections,
|
|
1504
|
+
extra,
|
|
1505
|
+
allowDeviceSelection: typeof allowDeviceSelection === "boolean" ? allowDeviceSelection : void 0
|
|
1101
1506
|
},
|
|
1102
1507
|
rest
|
|
1103
1508
|
};
|
|
@@ -1273,6 +1678,27 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
1273
1678
|
);
|
|
1274
1679
|
}
|
|
1275
1680
|
// ---------------------------------------------------------------------------
|
|
1681
|
+
// Zcash chain methods (viewing key + shielded address reads; Zcash app >= 3.8.0)
|
|
1682
|
+
// ---------------------------------------------------------------------------
|
|
1683
|
+
zcashGetFullViewingKey(connectId, deviceId, params) {
|
|
1684
|
+
return this.callChainWithMergedParams(
|
|
1685
|
+
connectId,
|
|
1686
|
+
deviceId,
|
|
1687
|
+
"zcash",
|
|
1688
|
+
"zcashGetFullViewingKey",
|
|
1689
|
+
params
|
|
1690
|
+
);
|
|
1691
|
+
}
|
|
1692
|
+
zcashGetShieldedAddress(connectId, deviceId, params) {
|
|
1693
|
+
return this.callChainWithMergedParams(
|
|
1694
|
+
connectId,
|
|
1695
|
+
deviceId,
|
|
1696
|
+
"zcash",
|
|
1697
|
+
"zcashGetShieldedAddress",
|
|
1698
|
+
params
|
|
1699
|
+
);
|
|
1700
|
+
}
|
|
1701
|
+
// ---------------------------------------------------------------------------
|
|
1276
1702
|
// App management — OS-level Ledger app install / list. Bypasses fingerprint
|
|
1277
1703
|
// and chain-handler dispatch; installApp progress is forwarded to the adapter
|
|
1278
1704
|
// emitter via 'ui-event' AppInstallProgress events.
|
|
@@ -1361,7 +1787,7 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
1361
1787
|
);
|
|
1362
1788
|
}
|
|
1363
1789
|
await this.connector.configure({ ledgerGenuineCheckWebSocketUrl: relayUrl });
|
|
1364
|
-
this.
|
|
1790
|
+
await this._resetStateAndDisconnectSessions();
|
|
1365
1791
|
}
|
|
1366
1792
|
const result = await this.connectorCall(connectId, "getDeviceGenuineCheck", {});
|
|
1367
1793
|
if (!result.isGenuine) {
|
|
@@ -1387,10 +1813,10 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
1387
1813
|
if (relayUrl) {
|
|
1388
1814
|
try {
|
|
1389
1815
|
await this.connector.configure?.({ ledgerGenuineCheckWebSocketUrl: void 0 });
|
|
1390
|
-
this.
|
|
1816
|
+
await this._resetStateAndDisconnectSessions();
|
|
1391
1817
|
} catch {
|
|
1392
1818
|
this.connector.reset();
|
|
1393
|
-
this.
|
|
1819
|
+
await this._resetStateAndDisconnectSessions();
|
|
1394
1820
|
}
|
|
1395
1821
|
}
|
|
1396
1822
|
}
|
|
@@ -1402,6 +1828,20 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
1402
1828
|
this.emitter.off(event, listener);
|
|
1403
1829
|
}
|
|
1404
1830
|
cancel(connectId) {
|
|
1831
|
+
const namedOperationIsLive = (id) => {
|
|
1832
|
+
try {
|
|
1833
|
+
this._operations.resolve(id);
|
|
1834
|
+
return true;
|
|
1835
|
+
} catch {
|
|
1836
|
+
return false;
|
|
1837
|
+
}
|
|
1838
|
+
};
|
|
1839
|
+
if ((0, import_hwk_adapter_core3.isHardwareOperationId)(connectId) && !namedOperationIsLive(connectId)) {
|
|
1840
|
+
debugLog("[LedgerAdapter] cancel target already ended; nothing to cancel", {
|
|
1841
|
+
connectId
|
|
1842
|
+
});
|
|
1843
|
+
return;
|
|
1844
|
+
}
|
|
1405
1845
|
const userAbortReason = Object.assign(new Error("User aborted operation"), {
|
|
1406
1846
|
code: import_hwk_adapter_core3.HardwareErrorCode.UserAborted,
|
|
1407
1847
|
_tag: ERROR_TAG.UserAborted
|
|
@@ -1412,14 +1852,37 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
1412
1852
|
this._lastCancelReason = void 0;
|
|
1413
1853
|
}
|
|
1414
1854
|
}, 2e3);
|
|
1855
|
+
const activeJobId = this._jobQueue.getActiveJob()?.deviceId;
|
|
1856
|
+
let operationId;
|
|
1857
|
+
if ((0, import_hwk_adapter_core3.isHardwareOperationId)(connectId)) {
|
|
1858
|
+
operationId = connectId;
|
|
1859
|
+
} else if (!connectId && (0, import_hwk_adapter_core3.isHardwareOperationId)(activeJobId)) {
|
|
1860
|
+
operationId = activeJobId;
|
|
1861
|
+
}
|
|
1862
|
+
let resolvedConnectId = connectId;
|
|
1863
|
+
if (operationId) {
|
|
1864
|
+
try {
|
|
1865
|
+
resolvedConnectId = this._operations.resolve(operationId).connectId;
|
|
1866
|
+
} catch {
|
|
1867
|
+
resolvedConnectId = void 0;
|
|
1868
|
+
}
|
|
1869
|
+
}
|
|
1415
1870
|
this._uiRegistry.cancel();
|
|
1416
|
-
|
|
1417
|
-
|
|
1871
|
+
this._finishBleBinding("cancelled");
|
|
1872
|
+
const interactionForPhysicalId = !operationId && connectId ? this._operations.findActiveByConnectionKey(connectId) : void 0;
|
|
1873
|
+
const queueKey = operationId ?? (activeJobId === connectId ? connectId : interactionForPhysicalId?.operationId) ?? connectId;
|
|
1874
|
+
const pendingOperationId = operationId ?? interactionForPhysicalId?.operationId;
|
|
1875
|
+
if (!connectId) this._pendingOperationBindings.clear();
|
|
1876
|
+
else if (pendingOperationId) {
|
|
1877
|
+
this._pendingOperationBindings.delete(pendingOperationId);
|
|
1878
|
+
}
|
|
1879
|
+
if (queueKey) {
|
|
1880
|
+
this._jobQueue.cancelActiveAndPending(queueKey, userAbortReason);
|
|
1418
1881
|
} else {
|
|
1419
1882
|
this._jobQueue.cancelActiveAndPending(void 0, userAbortReason);
|
|
1420
1883
|
}
|
|
1421
|
-
if (
|
|
1422
|
-
const sessionId = this._sessions.get(
|
|
1884
|
+
if (resolvedConnectId) {
|
|
1885
|
+
const sessionId = this._sessions.get(resolvedConnectId) ?? resolvedConnectId;
|
|
1423
1886
|
void this.connector.cancel(sessionId);
|
|
1424
1887
|
} else {
|
|
1425
1888
|
for (const sid of this._sessions.values()) void this.connector.cancel(sid);
|
|
@@ -1431,40 +1894,97 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
1431
1894
|
// ---------------------------------------------------------------------------
|
|
1432
1895
|
// Chain fingerprint
|
|
1433
1896
|
// ---------------------------------------------------------------------------
|
|
1897
|
+
/** A non-empty deviceId is an expected chain fingerprint, not a transport identifier. */
|
|
1434
1898
|
async getChainFingerprint(connectId, deviceId, chain) {
|
|
1435
1899
|
try {
|
|
1436
1900
|
const fingerprint = await this._computeChainFingerprint(
|
|
1437
1901
|
chain,
|
|
1438
1902
|
(method, params) => this.connectorCall(connectId, method, params, void 0, deviceId)
|
|
1439
1903
|
);
|
|
1904
|
+
if (deviceId) {
|
|
1905
|
+
if (fingerprint !== deviceId) {
|
|
1906
|
+
return (0, import_hwk_adapter_core3.failure)(
|
|
1907
|
+
import_hwk_adapter_core3.HardwareErrorCode.DeviceMismatch,
|
|
1908
|
+
formatDeviceMismatchError(deviceId, fingerprint)
|
|
1909
|
+
);
|
|
1910
|
+
}
|
|
1911
|
+
if ((0, import_hwk_adapter_core3.isHardwareOperationId)(connectId)) {
|
|
1912
|
+
const operation = this._operations.resolve(connectId);
|
|
1913
|
+
await this._publishVerifiedBleBinding(
|
|
1914
|
+
operation.connectId,
|
|
1915
|
+
chain,
|
|
1916
|
+
fingerprint,
|
|
1917
|
+
void 0,
|
|
1918
|
+
connectId
|
|
1919
|
+
);
|
|
1920
|
+
}
|
|
1921
|
+
}
|
|
1440
1922
|
return (0, import_hwk_adapter_core3.success)(fingerprint);
|
|
1441
1923
|
} catch (err) {
|
|
1442
1924
|
debugError("[LedgerAdapter] getChainFingerprint error:", chain, err);
|
|
1443
1925
|
return this.errorToFailure(err);
|
|
1444
1926
|
}
|
|
1445
1927
|
}
|
|
1446
|
-
/**
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1928
|
+
/** Discovery may select a BLE target before a later call verifies its wallet. */
|
|
1929
|
+
async _publishVerifiedBleBinding(connectId, chain, fingerprint, attempt, operationId, signal) {
|
|
1930
|
+
const binding = operationId ? this._pendingOperationBindings.get(operationId) : attempt;
|
|
1931
|
+
if (!this._isBleConnection() || binding?.selectedConnection?.connectId !== connectId) {
|
|
1932
|
+
return false;
|
|
1933
|
+
}
|
|
1452
1934
|
try {
|
|
1453
|
-
const
|
|
1454
|
-
|
|
1455
|
-
|
|
1935
|
+
const outcome = await (0, import_hwk_adapter_core3.requestSaveDeviceBinding)(
|
|
1936
|
+
this.emitter,
|
|
1937
|
+
this._uiRegistry,
|
|
1938
|
+
{
|
|
1939
|
+
selectionRequestId: binding.selectedConnection.requestId,
|
|
1940
|
+
connection: { transport: "ble", connectId },
|
|
1941
|
+
identity: { vendor: "ledger", type: "chainFingerprint", chain, value: fingerprint },
|
|
1942
|
+
extra: binding.extra
|
|
1943
|
+
},
|
|
1944
|
+
signal
|
|
1456
1945
|
);
|
|
1457
|
-
if (
|
|
1458
|
-
|
|
1946
|
+
if (operationId) this._operations.resolve(operationId);
|
|
1947
|
+
if (!outcome.saved) {
|
|
1948
|
+
debugLog("[LedgerAdapter] BLE binding not persisted by host", {
|
|
1949
|
+
connectId,
|
|
1950
|
+
reason: outcome.reason
|
|
1951
|
+
});
|
|
1459
1952
|
}
|
|
1460
|
-
return
|
|
1461
|
-
} catch (
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1953
|
+
return outcome.saved;
|
|
1954
|
+
} catch (error) {
|
|
1955
|
+
if (operationId) {
|
|
1956
|
+
this._pendingOperationBindings.delete(operationId);
|
|
1957
|
+
this._operations.end(operationId, "explicit");
|
|
1465
1958
|
}
|
|
1466
|
-
|
|
1959
|
+
const sessionId = this._sessions.get(connectId);
|
|
1960
|
+
this._sessions.delete(connectId);
|
|
1961
|
+
if (sessionId) {
|
|
1962
|
+
const teardown = this._runConnectorTeardown(
|
|
1963
|
+
() => this.connector.disconnect(sessionId)
|
|
1964
|
+
).catch(() => void 0);
|
|
1965
|
+
if (!signal?.aborted) await teardown;
|
|
1966
|
+
}
|
|
1967
|
+
throw error;
|
|
1968
|
+
} finally {
|
|
1969
|
+
if (this._bindingSelectionRequestId === binding.selectedConnection.requestId) {
|
|
1970
|
+
this._bindingSelectionRequestId = void 0;
|
|
1971
|
+
}
|
|
1972
|
+
if (operationId) {
|
|
1973
|
+
this._pendingOperationBindings.delete(operationId);
|
|
1974
|
+
}
|
|
1975
|
+
}
|
|
1976
|
+
}
|
|
1977
|
+
/** Verify on the acquired session without re-entering the job queue. */
|
|
1978
|
+
async _verifyDeviceFingerprintWithSession(sessionId, deviceId, chain) {
|
|
1979
|
+
if (!deviceId) return { success: true };
|
|
1980
|
+
const fingerprint = await this._computeChainFingerprint(
|
|
1981
|
+
chain,
|
|
1982
|
+
(method, params) => this._callConnector(sessionId, method, params)
|
|
1983
|
+
);
|
|
1984
|
+
if (fingerprint === deviceId) {
|
|
1985
|
+
return { success: true };
|
|
1467
1986
|
}
|
|
1987
|
+
return { success: false, expected: deviceId, actual: fingerprint };
|
|
1468
1988
|
}
|
|
1469
1989
|
/**
|
|
1470
1990
|
* Compute the chain fingerprint via a caller-supplied call strategy.
|
|
@@ -1493,6 +2013,8 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
1493
2013
|
address = (await callMethod("solGetAddress", { path, showOnDevice: false })).address;
|
|
1494
2014
|
} else if (chain === "tron") {
|
|
1495
2015
|
address = (await callMethod("tronGetAddress", { path, showOnDevice: false })).address;
|
|
2016
|
+
} else if (chain === "zcash") {
|
|
2017
|
+
address = (await callMethod("zcashGetShieldedAddress", { path, showOnDevice: false })).address;
|
|
1496
2018
|
} else {
|
|
1497
2019
|
throw new Error(`Unsupported chain for fingerprint: ${chain}`);
|
|
1498
2020
|
}
|
|
@@ -1622,16 +2144,17 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
1622
2144
|
// Layer 1 entry. Caller signal only races the outer awaiter; the shared
|
|
1623
2145
|
// `_doConnect` runs under its own internal controller so caller A's cancel
|
|
1624
2146
|
// doesn't kill caller B's await.
|
|
1625
|
-
async ensureConnected(connectId, signal, allowUsbEphemeralFallback = false) {
|
|
2147
|
+
async ensureConnected(connectId, signal, allowUsbEphemeralFallback = false, preserveOperationId, context) {
|
|
1626
2148
|
if (signal.aborted) _LedgerAdapter._throwIfAborted(signal);
|
|
1627
|
-
|
|
2149
|
+
this._assertConnectorReady("connectDevice");
|
|
2150
|
+
if (this._isBleConnection() && !connectId && !allowUsbEphemeralFallback) {
|
|
1628
2151
|
throw Object.assign(new Error("Ledger BLE connectId is required."), {
|
|
1629
2152
|
code: import_hwk_adapter_core3.HardwareErrorCode.DeviceNotFound
|
|
1630
2153
|
});
|
|
1631
2154
|
}
|
|
1632
2155
|
if (connectId && this._sessions.has(connectId)) return connectId;
|
|
1633
2156
|
if (!connectId && this._sessions.size > 0) {
|
|
1634
|
-
if (!
|
|
2157
|
+
if (!this._isBleConnection() && this._sessions.size > 1) {
|
|
1635
2158
|
throw Object.assign(
|
|
1636
2159
|
new Error(
|
|
1637
2160
|
"Ledger USB session invariant violated: more than one session is active. Please reconnect the device."
|
|
@@ -1641,15 +2164,24 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
1641
2164
|
}
|
|
1642
2165
|
return this._sessions.keys().next().value;
|
|
1643
2166
|
}
|
|
1644
|
-
if (!this._connectingPromise) {
|
|
1645
|
-
|
|
1646
|
-
|
|
2167
|
+
if (!this._connectingPromise || this._doConnectAbortController?.signal.aborted) {
|
|
2168
|
+
const controller = new AbortController();
|
|
2169
|
+
this._doConnectAbortController = controller;
|
|
2170
|
+
const innerSignal = controller.signal;
|
|
1647
2171
|
this._connectingPromise = (async () => {
|
|
1648
2172
|
try {
|
|
1649
|
-
return await this._doConnect(
|
|
2173
|
+
return await this._doConnect(
|
|
2174
|
+
innerSignal,
|
|
2175
|
+
connectId,
|
|
2176
|
+
allowUsbEphemeralFallback,
|
|
2177
|
+
preserveOperationId,
|
|
2178
|
+
context
|
|
2179
|
+
);
|
|
1650
2180
|
} finally {
|
|
1651
|
-
this.
|
|
1652
|
-
|
|
2181
|
+
if (this._doConnectAbortController === controller) {
|
|
2182
|
+
this._connectingPromise = null;
|
|
2183
|
+
this._doConnectAbortController = null;
|
|
2184
|
+
}
|
|
1653
2185
|
}
|
|
1654
2186
|
})();
|
|
1655
2187
|
}
|
|
@@ -1658,22 +2190,51 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
1658
2190
|
// Layer 1 main loop — the ONLY place in SDK that emits unlock dialog.
|
|
1659
2191
|
// Bounded by MAX_DOCONNECT_CONFIRMS — after N Confirms with no progress,
|
|
1660
2192
|
// throw DeviceNotFound so the user is kicked out of the loop.
|
|
1661
|
-
async _doConnect(internalSignal, targetConnectId, allowUsbEphemeralFallback = false) {
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
2193
|
+
async _doConnect(internalSignal, targetConnectId, allowUsbEphemeralFallback = false, preserveOperationId, context) {
|
|
2194
|
+
_LedgerAdapter._throwIfAborted(internalSignal);
|
|
2195
|
+
if (this.connector.availableTransports?.includes("usb") && this.connector.availableTransports.includes("ble")) {
|
|
2196
|
+
this._activeConnectionType = "usb";
|
|
2197
|
+
const usbDevices = await this._searchDevices({ transportType: "usb" }, internalSignal);
|
|
2198
|
+
_LedgerAdapter._throwIfAborted(internalSignal);
|
|
2199
|
+
if (usbDevices.length > 0) {
|
|
2200
|
+
const knownUsb = context?.knownConnections?.find(
|
|
2201
|
+
(connection) => connection.transport === "usb"
|
|
2202
|
+
);
|
|
2203
|
+
const usbTarget = knownUsb?.transport === "usb" ? knownUsb.connectId : usbDevices.find((device) => device.connectId === targetConnectId)?.connectId;
|
|
2204
|
+
return this._connectFirstOrSelect(
|
|
2205
|
+
usbDevices,
|
|
2206
|
+
usbTarget,
|
|
2207
|
+
allowUsbEphemeralFallback,
|
|
2208
|
+
preserveOperationId,
|
|
2209
|
+
context,
|
|
2210
|
+
internalSignal
|
|
2211
|
+
);
|
|
2212
|
+
}
|
|
2213
|
+
this._activeConnectionType = "ble";
|
|
2214
|
+
const knownBle = context?.knownConnections?.find(
|
|
2215
|
+
(connection) => connection.transport === "ble"
|
|
2216
|
+
);
|
|
2217
|
+
if (knownBle?.transport === "ble") {
|
|
2218
|
+
return this._connectDeviceOrThrow(knownBle.connectId, preserveOperationId, internalSignal);
|
|
2219
|
+
}
|
|
2220
|
+
if (targetConnectId && context?.knownConnections === void 0) {
|
|
2221
|
+
throw (0, import_hwk_adapter_core3.createHwkError)({
|
|
2222
|
+
code: import_hwk_adapter_core3.HardwareErrorCode.DeviceNotFound,
|
|
2223
|
+
message: "Ledger connection metadata is required before starting Bluetooth binding"
|
|
2224
|
+
});
|
|
1676
2225
|
}
|
|
2226
|
+
const bleDevices = await this._searchDevices({ transportType: "ble" }, internalSignal);
|
|
2227
|
+
return this._connectFirstOrSelect(
|
|
2228
|
+
bleDevices,
|
|
2229
|
+
void 0,
|
|
2230
|
+
allowUsbEphemeralFallback,
|
|
2231
|
+
preserveOperationId,
|
|
2232
|
+
context,
|
|
2233
|
+
internalSignal
|
|
2234
|
+
);
|
|
2235
|
+
}
|
|
2236
|
+
if (this._isBleConnection() && targetConnectId) {
|
|
2237
|
+
return this._connectDeviceOrThrow(targetConnectId, preserveOperationId, internalSignal);
|
|
1677
2238
|
}
|
|
1678
2239
|
let confirms = 0;
|
|
1679
2240
|
while (!internalSignal.aborted) {
|
|
@@ -1681,22 +2242,28 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
1681
2242
|
type: import_hwk_adapter_core3.EConnectorInteraction.Searching,
|
|
1682
2243
|
payload: { sessionId: "" }
|
|
1683
2244
|
});
|
|
1684
|
-
let devices = await this.
|
|
2245
|
+
let devices = await this._searchDevices(void 0, internalSignal);
|
|
2246
|
+
_LedgerAdapter._throwIfAborted(internalSignal);
|
|
1685
2247
|
if (devices.length === 0) {
|
|
1686
2248
|
for (let i = 0; i < 3 && !internalSignal.aborted; i += 1) {
|
|
1687
2249
|
await new Promise((resolve) => {
|
|
1688
2250
|
setTimeout(resolve, import_hwk_adapter_core3.DEVICE_CONNECT_RETRY_DELAY_MS);
|
|
1689
2251
|
});
|
|
1690
|
-
|
|
2252
|
+
_LedgerAdapter._throwIfAborted(internalSignal);
|
|
2253
|
+
devices = await this._searchDevices(void 0, internalSignal);
|
|
2254
|
+
_LedgerAdapter._throwIfAborted(internalSignal);
|
|
1691
2255
|
if (devices.length > 0) break;
|
|
1692
2256
|
}
|
|
1693
2257
|
}
|
|
1694
|
-
if (devices.length > 0) {
|
|
2258
|
+
if (devices.length > 0 || this._isBleConnection() && allowUsbEphemeralFallback) {
|
|
1695
2259
|
try {
|
|
1696
2260
|
return await this._connectFirstOrSelect(
|
|
1697
2261
|
devices,
|
|
1698
2262
|
targetConnectId,
|
|
1699
|
-
allowUsbEphemeralFallback
|
|
2263
|
+
allowUsbEphemeralFallback,
|
|
2264
|
+
preserveOperationId,
|
|
2265
|
+
context,
|
|
2266
|
+
internalSignal
|
|
1700
2267
|
);
|
|
1701
2268
|
} catch (err) {
|
|
1702
2269
|
if (!isDeviceLockedError(err) && !isDeviceNotAdvertisingError(err) && !isDeviceDisconnectedError(err)) {
|
|
@@ -1725,45 +2292,133 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
1725
2292
|
_LedgerAdapter._throwIfAborted(internalSignal);
|
|
1726
2293
|
throw new Error("_doConnect aborted");
|
|
1727
2294
|
}
|
|
1728
|
-
async _connectFirstOrSelect(devices, targetConnectId, allowUsbEphemeralFallback
|
|
2295
|
+
async _connectFirstOrSelect(devices, targetConnectId, allowUsbEphemeralFallback, preserveOperationId, context, signal) {
|
|
2296
|
+
_LedgerAdapter._throwIfAborted(signal);
|
|
1729
2297
|
if (targetConnectId) {
|
|
1730
2298
|
const target = devices.find(
|
|
1731
2299
|
(d) => d.connectId === targetConnectId || d.deviceId === targetConnectId
|
|
1732
2300
|
);
|
|
1733
2301
|
if (target) {
|
|
1734
|
-
return this._connectDeviceOrThrow(target.connectId);
|
|
2302
|
+
return this._connectDeviceOrThrow(target.connectId, preserveOperationId, signal);
|
|
1735
2303
|
}
|
|
1736
|
-
if (!
|
|
2304
|
+
if (!this._isBleConnection() && devices.length === 1 && allowUsbEphemeralFallback) {
|
|
1737
2305
|
debugLog(
|
|
1738
2306
|
`[LedgerAdapter] target ${targetConnectId} not in fresh enumeration; accepting sole USB device ${devices[0].connectId} for fingerprint-verified recovery`
|
|
1739
2307
|
);
|
|
1740
|
-
return this._connectDeviceOrThrow(devices[0].connectId);
|
|
2308
|
+
return this._connectDeviceOrThrow(devices[0].connectId, preserveOperationId, signal);
|
|
1741
2309
|
}
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
2310
|
+
if (!this._isBleConnection() || !allowUsbEphemeralFallback) {
|
|
2311
|
+
const err = Object.assign(new Error(`Target Ledger unavailable: ${targetConnectId}`), {
|
|
2312
|
+
code: import_hwk_adapter_core3.HardwareErrorCode.DeviceNotFound
|
|
2313
|
+
});
|
|
2314
|
+
if (this._isBleConnection()) {
|
|
2315
|
+
err._tag = ERROR_TAG.DeviceNotAdvertising;
|
|
2316
|
+
}
|
|
2317
|
+
throw err;
|
|
1747
2318
|
}
|
|
1748
|
-
throw err;
|
|
1749
2319
|
}
|
|
1750
|
-
|
|
2320
|
+
const requiresBleSelection = this._isBleConnection();
|
|
2321
|
+
if (requiresBleSelection && !allowUsbEphemeralFallback) {
|
|
1751
2322
|
throw Object.assign(new Error("Ledger BLE connectId is required."), {
|
|
1752
2323
|
code: import_hwk_adapter_core3.HardwareErrorCode.DeviceNotFound
|
|
1753
2324
|
});
|
|
1754
2325
|
}
|
|
1755
|
-
if (
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
2326
|
+
if (requiresBleSelection && context?.allowDeviceSelection !== false) {
|
|
2327
|
+
const bindingSessionId = context?.bindingSessionId ?? this._uiRegistry.createRequestId();
|
|
2328
|
+
if (context) context.bindingSessionId = bindingSessionId;
|
|
2329
|
+
const allowUsbFallback = context?.bindingReason !== "manual-rebind" && Boolean(this.connector.availableTransports?.includes("usb"));
|
|
2330
|
+
const knownUsb = context?.knownConnections?.find(
|
|
2331
|
+
(connection) => connection.transport === "usb"
|
|
2332
|
+
);
|
|
2333
|
+
const usbConnectId = knownUsb?.transport === "usb" ? knownUsb.connectId : targetConnectId;
|
|
2334
|
+
const { device, requestId } = await (0, import_hwk_adapter_core3.requestBleDeviceSelection)({
|
|
2335
|
+
emitter: this.emitter,
|
|
2336
|
+
registry: this._uiRegistry,
|
|
2337
|
+
signal,
|
|
2338
|
+
allowUsbFallback,
|
|
2339
|
+
scan: async () => {
|
|
2340
|
+
if (allowUsbFallback) {
|
|
2341
|
+
const usbDevices = await this._searchDevices({ transportType: "usb" }, signal);
|
|
2342
|
+
const candidate = usbDevices.find(
|
|
2343
|
+
(device2) => device2.connectionType === "usb" && device2.connectId === usbConnectId
|
|
2344
|
+
) ?? (usbDevices.length === 1 && usbDevices[0].connectionType === "usb" ? usbDevices[0] : void 0);
|
|
2345
|
+
if (candidate) return [candidate];
|
|
2346
|
+
}
|
|
2347
|
+
return (await this._searchDevices({ transportType: "ble", waitForAllTransports: true }, signal)).filter((device2) => !context?.rejectedConnectIds?.has(device2.connectId));
|
|
2348
|
+
},
|
|
2349
|
+
request: {
|
|
2350
|
+
devices: devices.filter((device2) => !context?.rejectedConnectIds?.has(device2.connectId)),
|
|
2351
|
+
bindingSessionId,
|
|
2352
|
+
rejectedConnectId: context?.rejectedConnectId,
|
|
2353
|
+
context: {
|
|
2354
|
+
kind: "bind-connection",
|
|
2355
|
+
transport: "ble",
|
|
2356
|
+
reason: context?.bindingReason ?? (targetConnectId ? "known-connection-unavailable" : "missing-binding")
|
|
2357
|
+
},
|
|
2358
|
+
extra: context?.extra
|
|
2359
|
+
}
|
|
2360
|
+
});
|
|
2361
|
+
if (device.connectionType === "usb") {
|
|
2362
|
+
this._activeConnectionType = "usb";
|
|
2363
|
+
if (context) context.selectedConnection = void 0;
|
|
2364
|
+
if (preserveOperationId) this._pendingOperationBindings.delete(preserveOperationId);
|
|
2365
|
+
this._bindingSelectionRequestId = void 0;
|
|
2366
|
+
this.emitter.emit(import_hwk_adapter_core3.UI_REQUEST.DEVICE_BINDING_STATUS, {
|
|
2367
|
+
type: import_hwk_adapter_core3.UI_REQUEST.DEVICE_BINDING_STATUS,
|
|
2368
|
+
payload: { selectionRequestId: requestId, status: "cancelled" }
|
|
2369
|
+
});
|
|
2370
|
+
return this._connectDeviceOrThrow(device.connectId, preserveOperationId, signal);
|
|
2371
|
+
}
|
|
2372
|
+
if (context) context.selectedConnection = { connectId: device.connectId, requestId };
|
|
2373
|
+
this._bindingSelectionRequestId = requestId;
|
|
2374
|
+
return this._connectDeviceOrThrow(device.connectId, preserveOperationId, signal);
|
|
2375
|
+
}
|
|
2376
|
+
if (devices.length > 0 && (devices.length > 1 || requiresBleSelection)) {
|
|
2377
|
+
if (context?.allowDeviceSelection === false || !this.emitter.listenerCount(import_hwk_adapter_core3.UI_REQUEST.REQUEST_SELECT_DEVICE)) {
|
|
2378
|
+
throw (0, import_hwk_adapter_core3.createHwkError)({
|
|
2379
|
+
code: import_hwk_adapter_core3.HardwareErrorCode.DeviceNotFound,
|
|
2380
|
+
message: "Select a Ledger device before continuing"
|
|
2381
|
+
});
|
|
2382
|
+
}
|
|
2383
|
+
const requestId = this._uiRegistry.createRequestId();
|
|
2384
|
+
const waitPromise = this._uiRegistry.wait(
|
|
2385
|
+
import_hwk_adapter_core3.UI_REQUEST.REQUEST_SELECT_DEVICE,
|
|
2386
|
+
{ requestId }
|
|
2387
|
+
);
|
|
2388
|
+
this.emitter.emit(import_hwk_adapter_core3.UI_REQUEST.REQUEST_SELECT_DEVICE, {
|
|
2389
|
+
type: import_hwk_adapter_core3.UI_REQUEST.REQUEST_SELECT_DEVICE,
|
|
2390
|
+
payload: {
|
|
2391
|
+
devices,
|
|
2392
|
+
requestId,
|
|
2393
|
+
context: requiresBleSelection ? {
|
|
2394
|
+
kind: "bind-connection",
|
|
2395
|
+
transport: "ble",
|
|
2396
|
+
reason: targetConnectId ? "known-connection-unavailable" : "missing-binding"
|
|
2397
|
+
} : { kind: "select-device", transport: "usb", reason: "multiple-candidates" },
|
|
2398
|
+
extra: context?.extra
|
|
2399
|
+
}
|
|
2400
|
+
});
|
|
2401
|
+
const { sdkConnectId } = await (signal ? this._abortable(signal, waitPromise) : waitPromise);
|
|
2402
|
+
if (signal) _LedgerAdapter._throwIfAborted(signal);
|
|
2403
|
+
const selected = devices.find((device) => device.connectId === sdkConnectId);
|
|
2404
|
+
if (!selected) {
|
|
2405
|
+
throw Object.assign(new Error("Selected Ledger is no longer available"), {
|
|
2406
|
+
code: import_hwk_adapter_core3.HardwareErrorCode.DeviceNotFound
|
|
2407
|
+
});
|
|
2408
|
+
}
|
|
2409
|
+
if (context && requiresBleSelection)
|
|
2410
|
+
context.selectedConnection = { connectId: selected.connectId, requestId };
|
|
2411
|
+
return this._connectDeviceOrThrow(selected.connectId, preserveOperationId, signal);
|
|
2412
|
+
}
|
|
2413
|
+
if (devices.length !== 1) {
|
|
1759
2414
|
throw Object.assign(new Error("Ledger device not found."), {
|
|
1760
2415
|
code: import_hwk_adapter_core3.HardwareErrorCode.DeviceNotFound
|
|
1761
2416
|
});
|
|
1762
2417
|
}
|
|
1763
|
-
return this._connectDeviceOrThrow(devices[0].connectId);
|
|
2418
|
+
return this._connectDeviceOrThrow(devices[0].connectId, preserveOperationId, signal);
|
|
1764
2419
|
}
|
|
1765
|
-
async _connectDeviceOrThrow(chosenConnectId) {
|
|
1766
|
-
const result = await this.
|
|
2420
|
+
async _connectDeviceOrThrow(chosenConnectId, preserveOperationId, signal) {
|
|
2421
|
+
const result = await this._connectTarget(chosenConnectId, preserveOperationId, signal);
|
|
1767
2422
|
if (!result.success) {
|
|
1768
2423
|
const payload = result.payload;
|
|
1769
2424
|
const rethrow = Object.assign(new Error(payload.error), {
|
|
@@ -1774,7 +2429,7 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
1774
2429
|
}
|
|
1775
2430
|
throw rethrow;
|
|
1776
2431
|
}
|
|
1777
|
-
return
|
|
2432
|
+
return result.payload;
|
|
1778
2433
|
}
|
|
1779
2434
|
/**
|
|
1780
2435
|
* Call the connector with automatic session resolution and disconnect retry.
|
|
@@ -1804,26 +2459,122 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
1804
2459
|
* lives in one place.
|
|
1805
2460
|
*/
|
|
1806
2461
|
async _callConnector(sessionId, method, params, signal) {
|
|
1807
|
-
|
|
2462
|
+
this._assertConnectorReady(method);
|
|
2463
|
+
if (signal?.aborted) throw this._abortReason(signal);
|
|
2464
|
+
const releaseOperation = this._retainConnectorOperation(`call:${sessionId}`);
|
|
2465
|
+
let promise;
|
|
2466
|
+
try {
|
|
2467
|
+
promise = this.connector.call(sessionId, method, params).finally(releaseOperation);
|
|
2468
|
+
} catch (error) {
|
|
2469
|
+
releaseOperation();
|
|
2470
|
+
throw error;
|
|
2471
|
+
}
|
|
1808
2472
|
const result = signal ? await this._abortable(signal, promise) : await promise;
|
|
1809
2473
|
return this._unwrapConnectorResult(result);
|
|
1810
2474
|
}
|
|
2475
|
+
_assertConnectorReady(method) {
|
|
2476
|
+
if (this._resetPromise || this._pendingConnectorTeardowns > 0 || this._unsettledConnectorOperations.size > 0) {
|
|
2477
|
+
throw _LedgerAdapter._createDeviceBusyError(method);
|
|
2478
|
+
}
|
|
2479
|
+
}
|
|
2480
|
+
_runConnectorTeardown(task) {
|
|
2481
|
+
const previous = this._connectorTeardownTail;
|
|
2482
|
+
let releaseTail = () => void 0;
|
|
2483
|
+
this._connectorTeardownTail = new Promise((resolve) => {
|
|
2484
|
+
releaseTail = resolve;
|
|
2485
|
+
});
|
|
2486
|
+
this._pendingConnectorTeardowns += 1;
|
|
2487
|
+
return (async () => {
|
|
2488
|
+
try {
|
|
2489
|
+
await previous;
|
|
2490
|
+
await this._waitForConnectorOperationsToDrain();
|
|
2491
|
+
await task();
|
|
2492
|
+
} finally {
|
|
2493
|
+
this._pendingConnectorTeardowns -= 1;
|
|
2494
|
+
releaseTail();
|
|
2495
|
+
}
|
|
2496
|
+
})();
|
|
2497
|
+
}
|
|
2498
|
+
_waitForConnectorOperationsToDrain() {
|
|
2499
|
+
if (this._unsettledConnectorOperations.size === 0) {
|
|
2500
|
+
return Promise.resolve();
|
|
2501
|
+
}
|
|
2502
|
+
return new Promise((resolve) => {
|
|
2503
|
+
this._connectorIdleWaiters.add(resolve);
|
|
2504
|
+
});
|
|
2505
|
+
}
|
|
2506
|
+
_retainConnectorOperation(key) {
|
|
2507
|
+
this._unsettledConnectorOperations.set(
|
|
2508
|
+
key,
|
|
2509
|
+
(this._unsettledConnectorOperations.get(key) ?? 0) + 1
|
|
2510
|
+
);
|
|
2511
|
+
let released = false;
|
|
2512
|
+
return () => {
|
|
2513
|
+
if (released) return;
|
|
2514
|
+
released = true;
|
|
2515
|
+
const remaining = (this._unsettledConnectorOperations.get(key) ?? 1) - 1;
|
|
2516
|
+
if (remaining > 0) {
|
|
2517
|
+
this._unsettledConnectorOperations.set(key, remaining);
|
|
2518
|
+
} else {
|
|
2519
|
+
this._unsettledConnectorOperations.delete(key);
|
|
2520
|
+
}
|
|
2521
|
+
if (this._unsettledConnectorOperations.size === 0) {
|
|
2522
|
+
for (const resolve of this._connectorIdleWaiters) resolve();
|
|
2523
|
+
this._connectorIdleWaiters.clear();
|
|
2524
|
+
}
|
|
2525
|
+
};
|
|
2526
|
+
}
|
|
1811
2527
|
async connectorCall(connectId, method, params, fingerprint, permissionDeviceId, commonParams, installContext) {
|
|
1812
|
-
|
|
1813
|
-
|
|
2528
|
+
const positionalOperationId = (0, import_hwk_adapter_core3.isHardwareOperationId)(connectId) ? connectId : void 0;
|
|
2529
|
+
if (positionalOperationId && commonParams?.operationId && positionalOperationId !== commonParams.operationId) {
|
|
2530
|
+
throw (0, import_hwk_adapter_core3.createHwkError)({
|
|
2531
|
+
code: import_hwk_adapter_core3.HardwareErrorCode.InvalidParams,
|
|
2532
|
+
message: "Conflicting Ledger operation ids",
|
|
2533
|
+
params: {
|
|
2534
|
+
positionalOperationId,
|
|
2535
|
+
commonOperationId: commonParams.operationId
|
|
2536
|
+
}
|
|
2537
|
+
});
|
|
2538
|
+
}
|
|
2539
|
+
const operationId = commonParams?.operationId ?? positionalOperationId;
|
|
2540
|
+
const operation = operationId ? this._operations.resolve(operationId) : void 0;
|
|
2541
|
+
const releaseOperationRetention = operationId ? this._operations.retain(operationId) : void 0;
|
|
2542
|
+
const effectiveConnectId = operation?.connectId ?? connectId;
|
|
2543
|
+
debugLog("[LedgerAdapter][REQ]", {
|
|
2544
|
+
method,
|
|
2545
|
+
connectId: effectiveConnectId || "(empty)",
|
|
2546
|
+
params
|
|
2547
|
+
});
|
|
2548
|
+
const queueKey = (operationId ?? effectiveConnectId) || "__ledger_default__";
|
|
1814
2549
|
try {
|
|
1815
2550
|
const result = await this._jobQueue.enqueue(
|
|
1816
2551
|
queueKey,
|
|
1817
|
-
async (signal) =>
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
2552
|
+
async (signal) => {
|
|
2553
|
+
if (operationId) this._activeOperationJobs.add(operationId);
|
|
2554
|
+
try {
|
|
2555
|
+
return await this._runConnectorCall(
|
|
2556
|
+
effectiveConnectId,
|
|
2557
|
+
method,
|
|
2558
|
+
params,
|
|
2559
|
+
signal,
|
|
2560
|
+
fingerprint,
|
|
2561
|
+
permissionDeviceId,
|
|
2562
|
+
commonParams,
|
|
2563
|
+
installContext ?? {},
|
|
2564
|
+
operationId
|
|
2565
|
+
);
|
|
2566
|
+
} catch (error) {
|
|
2567
|
+
this._finishBleBinding(signal.aborted ? "cancelled" : "failed");
|
|
2568
|
+
throw error;
|
|
2569
|
+
} finally {
|
|
2570
|
+
if (operationId) {
|
|
2571
|
+
this._activeOperationJobs.delete(operationId);
|
|
2572
|
+
if (this._pendingOperationDisconnects.delete(operationId)) {
|
|
2573
|
+
await this._releaseLostOperationConnection(operationId);
|
|
2574
|
+
}
|
|
2575
|
+
}
|
|
2576
|
+
}
|
|
2577
|
+
},
|
|
1827
2578
|
{
|
|
1828
2579
|
label: method,
|
|
1829
2580
|
rejectIfBusy: true,
|
|
@@ -1844,15 +2595,22 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
1844
2595
|
}
|
|
1845
2596
|
});
|
|
1846
2597
|
throw err;
|
|
2598
|
+
} finally {
|
|
2599
|
+
releaseOperationRetention?.();
|
|
1847
2600
|
}
|
|
1848
2601
|
}
|
|
1849
2602
|
/**
|
|
1850
2603
|
* Race a promise against an abort signal. On abort, rejects with
|
|
1851
2604
|
* signal.reason → instance _lastCancelReason → generic Error('Aborted').
|
|
1852
2605
|
*/
|
|
2606
|
+
/** Hermes/RN polyfills don't always populate signal.reason; fall back. */
|
|
2607
|
+
_abortReason(signal) {
|
|
2608
|
+
return signal.reason ?? this._lastCancelReason ?? new Error("Aborted");
|
|
2609
|
+
}
|
|
1853
2610
|
_abortable(signal, promise) {
|
|
1854
|
-
const getAbortReason = () =>
|
|
2611
|
+
const getAbortReason = () => this._abortReason(signal);
|
|
1855
2612
|
if (signal.aborted) {
|
|
2613
|
+
void promise.catch(() => void 0);
|
|
1856
2614
|
return Promise.reject(getAbortReason());
|
|
1857
2615
|
}
|
|
1858
2616
|
return new Promise((resolve, reject) => {
|
|
@@ -1879,13 +2637,15 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
1879
2637
|
}
|
|
1880
2638
|
}
|
|
1881
2639
|
/** Actual work done under the job queue — connection, fingerprint, call, and recovery. */
|
|
1882
|
-
async _runConnectorCall(connectId, method, params, signal, fingerprint, permissionDeviceId, commonParams, installContext) {
|
|
2640
|
+
async _runConnectorCall(connectId, method, params, signal, fingerprint, permissionDeviceId, commonParams, installContext, operationId, lockedRetryBudget = _LedgerAdapter.MAX_BUSINESS_RETRY_BUDGET) {
|
|
1883
2641
|
_LedgerAdapter._throwIfAborted(signal);
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
|
|
2642
|
+
if (!operationId) {
|
|
2643
|
+
await this._ensureDevicePermission(
|
|
2644
|
+
connectId,
|
|
2645
|
+
permissionDeviceId ?? fingerprint?.deviceId,
|
|
2646
|
+
signal
|
|
2647
|
+
);
|
|
2648
|
+
}
|
|
1889
2649
|
_LedgerAdapter._throwIfAborted(signal);
|
|
1890
2650
|
let effectiveParams = params;
|
|
1891
2651
|
if (method === "btcGetPublicKey") {
|
|
@@ -1899,36 +2659,132 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
1899
2659
|
effectiveParams = gatedParams;
|
|
1900
2660
|
}
|
|
1901
2661
|
const allowUsbEphemeralFallback = !!fingerprint?.deviceId && !fingerprint.skipFingerprint;
|
|
1902
|
-
|
|
1903
|
-
|
|
2662
|
+
let businessCallStarted = false;
|
|
2663
|
+
const verifiedBleTarget = connectId ? this._verifiedBleReconnectTargets.get(connectId) : void 0;
|
|
2664
|
+
const knownTransport = this._isBleConnection() ? "ble" : "usb";
|
|
2665
|
+
const hintedConnectId = commonParams?.knownConnections?.find(
|
|
2666
|
+
(connection) => connection.transport === knownTransport
|
|
2667
|
+
);
|
|
2668
|
+
const inputConnectId = hintedConnectId && hintedConnectId.transport !== "qr" ? hintedConnectId.connectId : connectId;
|
|
2669
|
+
const preferredConnectId = fingerprint && !fingerprint.skipFingerprint && verifiedBleTarget?.chain === fingerprint.chain && verifiedBleTarget.fingerprint === fingerprint.deviceId ? verifiedBleTarget.connectId : inputConnectId;
|
|
2670
|
+
const connectionAttempt = { ...commonParams };
|
|
2671
|
+
const bundleConnection = installContext?.connection;
|
|
2672
|
+
if (bundleConnection && this._sessions.get(bundleConnection.connectId) !== bundleConnection.sessionId) {
|
|
2673
|
+
throw (0, import_hwk_adapter_core3.createHwkError)({
|
|
2674
|
+
code: import_hwk_adapter_core3.HardwareErrorCode.DeviceDisconnected,
|
|
2675
|
+
message: "Ledger all-network connection ended"
|
|
2676
|
+
});
|
|
2677
|
+
}
|
|
2678
|
+
let resolvedConnectId = operationId ? this._operations.resolve(operationId).connectId : bundleConnection?.connectId ?? await this.ensureConnected(
|
|
2679
|
+
preferredConnectId,
|
|
1904
2680
|
signal,
|
|
1905
|
-
allowUsbEphemeralFallback
|
|
2681
|
+
allowUsbEphemeralFallback,
|
|
2682
|
+
void 0,
|
|
2683
|
+
connectionAttempt
|
|
1906
2684
|
);
|
|
1907
|
-
|
|
2685
|
+
let sessionId = this._sessions.get(resolvedConnectId);
|
|
2686
|
+
if (sessionId && installContext && !installContext.connection) {
|
|
2687
|
+
installContext.connection = { connectId: resolvedConnectId, sessionId };
|
|
2688
|
+
}
|
|
1908
2689
|
if (!sessionId) {
|
|
2690
|
+
if (operationId) {
|
|
2691
|
+
this._operations.end(operationId, "disconnect");
|
|
2692
|
+
throw (0, import_hwk_adapter_core3.createHwkError)({
|
|
2693
|
+
code: import_hwk_adapter_core3.HardwareErrorCode.OperationEnded,
|
|
2694
|
+
message: "Ledger operation connection is no longer active",
|
|
2695
|
+
params: { operationId, reason: "disconnect" }
|
|
2696
|
+
});
|
|
2697
|
+
}
|
|
1909
2698
|
throw Object.assign(new Error("Auto-connect succeeded but no session found"), {
|
|
1910
2699
|
_tag: ERROR_TAG.DeviceSessionNotFound
|
|
1911
2700
|
});
|
|
1912
2701
|
}
|
|
1913
2702
|
try {
|
|
1914
2703
|
if (fingerprint && !fingerprint.skipFingerprint && fingerprint.deviceId) {
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
|
|
1921
|
-
|
|
2704
|
+
for (; ; ) {
|
|
2705
|
+
const fp = await this._abortable(
|
|
2706
|
+
signal,
|
|
2707
|
+
this._verifyDeviceFingerprintWithSession(
|
|
2708
|
+
sessionId,
|
|
2709
|
+
fingerprint.deviceId,
|
|
2710
|
+
fingerprint.chain
|
|
2711
|
+
)
|
|
2712
|
+
);
|
|
2713
|
+
if (fp.success) break;
|
|
2714
|
+
const binding = operationId ? this._pendingOperationBindings.get(operationId) : connectionAttempt;
|
|
2715
|
+
if (!this._isBleConnection() || binding?.selectedConnection?.connectId !== resolvedConnectId) {
|
|
2716
|
+
throw Object.assign(new Error(formatDeviceMismatchError(fp.expected, fp.actual)), {
|
|
2717
|
+
code: import_hwk_adapter_core3.HardwareErrorCode.DeviceMismatch
|
|
2718
|
+
});
|
|
2719
|
+
}
|
|
2720
|
+
binding.rejectedConnectIds ?? (binding.rejectedConnectIds = /* @__PURE__ */ new Set());
|
|
2721
|
+
binding.rejectedConnectIds.add(resolvedConnectId);
|
|
2722
|
+
binding.rejectedConnectId = resolvedConnectId;
|
|
2723
|
+
this._sessions.delete(resolvedConnectId);
|
|
2724
|
+
await this.connector.disconnect(sessionId);
|
|
2725
|
+
if (operationId) this._pendingOperationDisconnects.delete(operationId);
|
|
2726
|
+
_LedgerAdapter._throwIfAborted(signal);
|
|
2727
|
+
resolvedConnectId = await this._connectFirstOrSelect(
|
|
2728
|
+
[],
|
|
2729
|
+
void 0,
|
|
2730
|
+
true,
|
|
2731
|
+
operationId,
|
|
2732
|
+
binding,
|
|
2733
|
+
signal
|
|
2734
|
+
);
|
|
2735
|
+
const selectedSession = this._sessions.get(resolvedConnectId);
|
|
2736
|
+
const selectedDevice = this._discoveredDevices.get(resolvedConnectId);
|
|
2737
|
+
if (!selectedSession || !selectedDevice)
|
|
2738
|
+
throw (0, import_hwk_adapter_core3.createHwkError)({
|
|
2739
|
+
code: import_hwk_adapter_core3.HardwareErrorCode.DeviceDisconnected,
|
|
2740
|
+
message: "Selected Ledger connection ended"
|
|
2741
|
+
});
|
|
2742
|
+
sessionId = selectedSession;
|
|
2743
|
+
if (operationId)
|
|
2744
|
+
this._operations.rebind(operationId, {
|
|
2745
|
+
connectId: resolvedConnectId,
|
|
2746
|
+
device: selectedDevice,
|
|
2747
|
+
connectionKeys: [sessionId]
|
|
2748
|
+
});
|
|
2749
|
+
if (installContext)
|
|
2750
|
+
installContext.connection = { connectId: resolvedConnectId, sessionId };
|
|
2751
|
+
}
|
|
2752
|
+
await this._publishVerifiedBleBinding(
|
|
2753
|
+
resolvedConnectId,
|
|
2754
|
+
fingerprint.chain,
|
|
2755
|
+
fingerprint.deviceId,
|
|
2756
|
+
connectionAttempt,
|
|
2757
|
+
operationId,
|
|
2758
|
+
signal
|
|
1922
2759
|
);
|
|
1923
|
-
if (!
|
|
1924
|
-
|
|
1925
|
-
|
|
2760
|
+
if (!operationId && connectionAttempt.selectedConnection?.connectId === resolvedConnectId && this._isBleConnection()) {
|
|
2761
|
+
this._verifiedBleReconnectTargets.set(connectId, {
|
|
2762
|
+
connectId: resolvedConnectId,
|
|
2763
|
+
chain: fingerprint.chain,
|
|
2764
|
+
fingerprint: fingerprint.deviceId
|
|
1926
2765
|
});
|
|
1927
2766
|
}
|
|
1928
2767
|
}
|
|
2768
|
+
businessCallStarted = true;
|
|
1929
2769
|
return await this._callConnector(sessionId, method, effectiveParams, signal);
|
|
1930
2770
|
} catch (err) {
|
|
1931
2771
|
if (signal.aborted) throw err;
|
|
2772
|
+
if (isDeviceDisconnectedError(err) || isDeviceNotAdvertisingError(err) || isTimeoutError(err) || isConnectionLevelError(err)) {
|
|
2773
|
+
this._discoveredDevices.delete(resolvedConnectId);
|
|
2774
|
+
if (operationId) await this._releaseLostOperationConnection(operationId);
|
|
2775
|
+
else {
|
|
2776
|
+
this._sessions.delete(resolvedConnectId);
|
|
2777
|
+
await this.connector.disconnect(sessionId).catch(() => void 0);
|
|
2778
|
+
}
|
|
2779
|
+
const ambiguous = businessCallStarted && !(0, import_hwk_adapter_core3.canReplayHardwareMethodAfterTransportFailure)(method);
|
|
2780
|
+
const interactionParams = operationId ? { operationId, reason: "disconnect" } : void 0;
|
|
2781
|
+
throw (0, import_hwk_adapter_core3.createHwkError)({
|
|
2782
|
+
code: operationId ? import_hwk_adapter_core3.HardwareErrorCode.OperationEnded : mapLedgerError(err).code,
|
|
2783
|
+
message: "Ledger operation connection was lost; start a new operation",
|
|
2784
|
+
params: ambiguous ? (0, import_hwk_adapter_core3.operationMayHaveCompletedParams)(method, { operationId }) : interactionParams,
|
|
2785
|
+
recovery: ambiguous ? { scope: "unknown" } : void 0
|
|
2786
|
+
});
|
|
2787
|
+
}
|
|
1932
2788
|
const errObj = err;
|
|
1933
2789
|
debugLog("[LedgerAdapter] connectorCall error:", method, {
|
|
1934
2790
|
message: errObj?.message,
|
|
@@ -1940,6 +2796,65 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
1940
2796
|
isNotAdvertising: isDeviceNotAdvertisingError(err),
|
|
1941
2797
|
isStuckApp: isStuckAppStateError(err)
|
|
1942
2798
|
});
|
|
2799
|
+
if ((isDeviceLockedError(err) || errObj?.code === import_hwk_adapter_core3.HardwareErrorCode.DeviceLocked) && lockedRetryBudget > 0) {
|
|
2800
|
+
await this._waitForDeviceConnect(signal);
|
|
2801
|
+
if (this._sessions.get(resolvedConnectId) !== sessionId) {
|
|
2802
|
+
throw (0, import_hwk_adapter_core3.createHwkError)({
|
|
2803
|
+
code: operationId ? import_hwk_adapter_core3.HardwareErrorCode.OperationEnded : import_hwk_adapter_core3.HardwareErrorCode.DeviceDisconnected,
|
|
2804
|
+
message: "Ledger connection ended while waiting for unlock"
|
|
2805
|
+
});
|
|
2806
|
+
}
|
|
2807
|
+
return this._runConnectorCall(
|
|
2808
|
+
resolvedConnectId,
|
|
2809
|
+
method,
|
|
2810
|
+
effectiveParams,
|
|
2811
|
+
signal,
|
|
2812
|
+
fingerprint,
|
|
2813
|
+
permissionDeviceId,
|
|
2814
|
+
commonParams,
|
|
2815
|
+
installContext,
|
|
2816
|
+
operationId,
|
|
2817
|
+
lockedRetryBudget - 1
|
|
2818
|
+
);
|
|
2819
|
+
}
|
|
2820
|
+
if (businessCallStarted && isStuckAppStateError(err)) {
|
|
2821
|
+
await this._sleepAbortable(_LedgerAdapter.STUCK_APP_RETRY_DELAY_MS, signal);
|
|
2822
|
+
if (this._sessions.get(resolvedConnectId) !== sessionId) {
|
|
2823
|
+
throw (0, import_hwk_adapter_core3.createHwkError)({
|
|
2824
|
+
code: operationId ? import_hwk_adapter_core3.HardwareErrorCode.OperationEnded : import_hwk_adapter_core3.HardwareErrorCode.DeviceDisconnected,
|
|
2825
|
+
message: "Ledger connection ended during the app transition"
|
|
2826
|
+
});
|
|
2827
|
+
}
|
|
2828
|
+
try {
|
|
2829
|
+
return await this._callConnector(sessionId, method, effectiveParams, signal);
|
|
2830
|
+
} catch (retryErr) {
|
|
2831
|
+
if (isStuckAppStateError(retryErr)) throw err;
|
|
2832
|
+
if (isDeviceDisconnectedError(retryErr) || isDeviceNotAdvertisingError(retryErr) || isTimeoutError(retryErr) || isConnectionLevelError(retryErr)) {
|
|
2833
|
+
if (operationId) await this._releaseLostOperationConnection(operationId);
|
|
2834
|
+
else {
|
|
2835
|
+
this._sessions.delete(resolvedConnectId);
|
|
2836
|
+
this._discoveredDevices.delete(resolvedConnectId);
|
|
2837
|
+
await this.connector.disconnect(sessionId).catch(() => void 0);
|
|
2838
|
+
}
|
|
2839
|
+
throw (0, import_hwk_adapter_core3.createHwkError)({
|
|
2840
|
+
code: operationId ? import_hwk_adapter_core3.HardwareErrorCode.OperationEnded : mapLedgerError(retryErr).code,
|
|
2841
|
+
message: `Ledger ${method} may have completed before the connection was lost`,
|
|
2842
|
+
params: !(0, import_hwk_adapter_core3.canReplayHardwareMethodAfterTransportFailure)(method) ? (0, import_hwk_adapter_core3.operationMayHaveCompletedParams)(method, {
|
|
2843
|
+
operationId,
|
|
2844
|
+
reason: "disconnect"
|
|
2845
|
+
}) : { operationId, reason: "disconnect" },
|
|
2846
|
+
recovery: !(0, import_hwk_adapter_core3.canReplayHardwareMethodAfterTransportFailure)(method) ? { scope: "unknown" } : void 0
|
|
2847
|
+
});
|
|
2848
|
+
}
|
|
2849
|
+
throw retryErr;
|
|
2850
|
+
}
|
|
2851
|
+
}
|
|
2852
|
+
if (!operationId && err?.code === import_hwk_adapter_core3.HardwareErrorCode.DeviceMismatch) {
|
|
2853
|
+
this._sessions.delete(resolvedConnectId);
|
|
2854
|
+
this._discoveredDevices.delete(resolvedConnectId);
|
|
2855
|
+
await this.connector.disconnect(sessionId).catch(() => void 0);
|
|
2856
|
+
throw err;
|
|
2857
|
+
}
|
|
1943
2858
|
const autoInstallApp = commonParams?.autoInstallApp ?? this._defaultAutoInstallApp;
|
|
1944
2859
|
const isAppMissing = isAppNotInstalledError(err) || err?.code === import_hwk_adapter_core3.HardwareErrorCode.AppNotInstalled;
|
|
1945
2860
|
if (autoInstallApp && isAppMissing) {
|
|
@@ -1995,157 +2910,14 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
1995
2910
|
fingerprint,
|
|
1996
2911
|
permissionDeviceId,
|
|
1997
2912
|
commonParams,
|
|
1998
|
-
installContext
|
|
1999
|
-
|
|
2000
|
-
}
|
|
2001
|
-
}
|
|
2002
|
-
if (isStuckAppStateError(err)) {
|
|
2003
|
-
try {
|
|
2004
|
-
this._sessions.delete(resolvedConnectId);
|
|
2005
|
-
this._discoveredDevices.delete(resolvedConnectId);
|
|
2006
|
-
this.connector.reset?.();
|
|
2007
|
-
} catch {
|
|
2008
|
-
}
|
|
2009
|
-
debugLog(
|
|
2010
|
-
"[LedgerAdapter] stuck-app retry: method=",
|
|
2011
|
-
method,
|
|
2012
|
-
"delayMs=",
|
|
2013
|
-
_LedgerAdapter.STUCK_APP_RETRY_DELAY_MS,
|
|
2014
|
-
"_tag=",
|
|
2015
|
-
err?._tag
|
|
2016
|
-
);
|
|
2017
|
-
try {
|
|
2018
|
-
const retryResult = await this._retryAfterStuckApp(
|
|
2019
|
-
resolvedConnectId,
|
|
2020
|
-
method,
|
|
2021
|
-
effectiveParams,
|
|
2022
|
-
signal,
|
|
2023
|
-
err,
|
|
2024
|
-
fingerprint
|
|
2025
|
-
);
|
|
2026
|
-
debugLog("[LedgerAdapter] stuck-app retry succeeded: method=", method);
|
|
2027
|
-
return retryResult;
|
|
2028
|
-
} catch (retryErr) {
|
|
2029
|
-
if (signal.aborted) throw retryErr;
|
|
2030
|
-
if (isStuckAppStateError(retryErr)) {
|
|
2031
|
-
debugLog("[LedgerAdapter] stuck-app retry exhausted (2nd 6901): method=", method);
|
|
2032
|
-
throw err;
|
|
2033
|
-
}
|
|
2034
|
-
debugLog(
|
|
2035
|
-
"[LedgerAdapter] stuck-app retry threw non-stuck error: method=",
|
|
2036
|
-
method,
|
|
2037
|
-
"retryErrTag=",
|
|
2038
|
-
retryErr?._tag
|
|
2913
|
+
installContext,
|
|
2914
|
+
operationId
|
|
2039
2915
|
);
|
|
2040
|
-
throw retryErr;
|
|
2041
|
-
}
|
|
2042
|
-
}
|
|
2043
|
-
if (isDeviceLockedError(err) || isDeviceNotAdvertisingError(err) || isDeviceDisconnectedError(err)) {
|
|
2044
|
-
let lastErr = err;
|
|
2045
|
-
for (let attempt = 0; attempt < _LedgerAdapter.MAX_BUSINESS_RETRY_BUDGET; attempt += 1) {
|
|
2046
|
-
if (signal.aborted) throw lastErr;
|
|
2047
|
-
try {
|
|
2048
|
-
this._sessions.delete(resolvedConnectId);
|
|
2049
|
-
this._discoveredDevices.delete(resolvedConnectId);
|
|
2050
|
-
if (isDeviceDisconnectedError(lastErr)) {
|
|
2051
|
-
try {
|
|
2052
|
-
this.connector.reset?.();
|
|
2053
|
-
} catch {
|
|
2054
|
-
}
|
|
2055
|
-
}
|
|
2056
|
-
const reConnectId = await this.ensureConnected(
|
|
2057
|
-
resolvedConnectId,
|
|
2058
|
-
signal,
|
|
2059
|
-
allowUsbEphemeralFallback
|
|
2060
|
-
);
|
|
2061
|
-
const reSessionId = this._sessions.get(reConnectId);
|
|
2062
|
-
if (!reSessionId) throw lastErr;
|
|
2063
|
-
if (fingerprint && !fingerprint.skipFingerprint && fingerprint.deviceId) {
|
|
2064
|
-
const fp = await this._abortable(
|
|
2065
|
-
signal,
|
|
2066
|
-
this._verifyDeviceFingerprintWithSession(
|
|
2067
|
-
reSessionId,
|
|
2068
|
-
fingerprint.deviceId,
|
|
2069
|
-
fingerprint.chain
|
|
2070
|
-
)
|
|
2071
|
-
);
|
|
2072
|
-
if (!fp.success) {
|
|
2073
|
-
throw Object.assign(new Error(formatDeviceMismatchError(fp.expected, fp.actual)), {
|
|
2074
|
-
code: import_hwk_adapter_core3.HardwareErrorCode.DeviceMismatch
|
|
2075
|
-
});
|
|
2076
|
-
}
|
|
2077
|
-
}
|
|
2078
|
-
return await this._callConnector(reSessionId, method, effectiveParams, signal);
|
|
2079
|
-
} catch (retryErr) {
|
|
2080
|
-
if (signal.aborted) throw retryErr;
|
|
2081
|
-
lastErr = retryErr;
|
|
2082
|
-
const canRetry = attempt < _LedgerAdapter.MAX_BUSINESS_RETRY_BUDGET - 1 && (isDeviceLockedError(retryErr) || isDeviceNotAdvertisingError(retryErr) || isDeviceDisconnectedError(retryErr));
|
|
2083
|
-
if (!canRetry) {
|
|
2084
|
-
throw retryErr;
|
|
2085
|
-
}
|
|
2086
|
-
}
|
|
2087
2916
|
}
|
|
2088
|
-
throw lastErr;
|
|
2089
|
-
}
|
|
2090
|
-
if (isTimeoutError(err)) {
|
|
2091
|
-
debugLog("[LedgerAdapter] timeout, retrying with fresh connection...");
|
|
2092
|
-
this._discoveredDevices.delete(resolvedConnectId);
|
|
2093
|
-
return this._retryWithFreshConnection(
|
|
2094
|
-
resolvedConnectId,
|
|
2095
|
-
method,
|
|
2096
|
-
effectiveParams,
|
|
2097
|
-
signal,
|
|
2098
|
-
err,
|
|
2099
|
-
fingerprint
|
|
2100
|
-
);
|
|
2101
|
-
}
|
|
2102
|
-
if (isConnectionLevelError(err)) {
|
|
2103
|
-
debugLog("[LedgerAdapter] connection-level fail-closed reset");
|
|
2104
|
-
this._sessions.delete(resolvedConnectId);
|
|
2105
|
-
this._discoveredDevices.delete(resolvedConnectId);
|
|
2106
|
-
this.connector.reset();
|
|
2107
|
-
const codeNum = err?.code;
|
|
2108
|
-
throw Object.assign(err, {
|
|
2109
|
-
code: codeNum ?? import_hwk_adapter_core3.HardwareErrorCode.DeviceDisconnected
|
|
2110
|
-
});
|
|
2111
2917
|
}
|
|
2112
2918
|
throw err;
|
|
2113
2919
|
}
|
|
2114
2920
|
}
|
|
2115
|
-
/**
|
|
2116
|
-
* Stuck-app recovery: pause for the device's UI transition, then retry once.
|
|
2117
|
-
*
|
|
2118
|
-
* Caller has already cleared the session + reset connector. We wait so Stax
|
|
2119
|
-
* finishes its post-CloseApp animation, then go through ensureConnected +
|
|
2120
|
-
* fingerprint check + call exactly once. Caller decides what to do on a
|
|
2121
|
-
* second stuck-app hit.
|
|
2122
|
-
*/
|
|
2123
|
-
async _retryAfterStuckApp(resolvedConnectId, method, params, signal, originalErr, fingerprint) {
|
|
2124
|
-
await this._sleepAbortable(_LedgerAdapter.STUCK_APP_RETRY_DELAY_MS, signal);
|
|
2125
|
-
const retryConnectId = await this.ensureConnected(
|
|
2126
|
-
resolvedConnectId,
|
|
2127
|
-
signal,
|
|
2128
|
-
!!fingerprint?.deviceId && !fingerprint.skipFingerprint
|
|
2129
|
-
);
|
|
2130
|
-
const retrySessionId = this._sessions.get(retryConnectId);
|
|
2131
|
-
if (!retrySessionId) throw originalErr;
|
|
2132
|
-
if (fingerprint && !fingerprint.skipFingerprint && fingerprint.deviceId) {
|
|
2133
|
-
const fp = await this._abortable(
|
|
2134
|
-
signal,
|
|
2135
|
-
this._verifyDeviceFingerprintWithSession(
|
|
2136
|
-
retrySessionId,
|
|
2137
|
-
fingerprint.deviceId,
|
|
2138
|
-
fingerprint.chain
|
|
2139
|
-
)
|
|
2140
|
-
);
|
|
2141
|
-
if (!fp.success) {
|
|
2142
|
-
throw Object.assign(new Error(formatDeviceMismatchError(fp.expected, fp.actual)), {
|
|
2143
|
-
code: import_hwk_adapter_core3.HardwareErrorCode.DeviceMismatch
|
|
2144
|
-
});
|
|
2145
|
-
}
|
|
2146
|
-
}
|
|
2147
|
-
return this._callConnector(retrySessionId, method, params, signal);
|
|
2148
|
-
}
|
|
2149
2921
|
_sleepAbortable(ms, signal) {
|
|
2150
2922
|
return new Promise((resolve, reject) => {
|
|
2151
2923
|
if (signal.aborted) {
|
|
@@ -2163,88 +2935,6 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
2163
2935
|
signal.addEventListener("abort", onAbort, { once: true });
|
|
2164
2936
|
});
|
|
2165
2937
|
}
|
|
2166
|
-
/**
|
|
2167
|
-
* Clear stale session, reconnect, and retry the call.
|
|
2168
|
-
*
|
|
2169
|
-
* Timeout recovery starts with a full connector reset. After an APDU
|
|
2170
|
-
* timeout, DMK/transport state may still emit malformed responses; retrying
|
|
2171
|
-
* on the same DMK can poison the next chain switch.
|
|
2172
|
-
*/
|
|
2173
|
-
async _retryWithFreshConnection(targetConnectId, method, params, signal, originalErr, fingerprint) {
|
|
2174
|
-
this.connector.reset();
|
|
2175
|
-
this._sessions.clear();
|
|
2176
|
-
this._discoveredDevices.clear();
|
|
2177
|
-
this._connectingPromise = null;
|
|
2178
|
-
const allowUsbEphemeralFallback = !!fingerprint?.deviceId && !fingerprint.skipFingerprint;
|
|
2179
|
-
const retryConnectId = await this.ensureConnected(
|
|
2180
|
-
targetConnectId,
|
|
2181
|
-
signal,
|
|
2182
|
-
allowUsbEphemeralFallback
|
|
2183
|
-
);
|
|
2184
|
-
const retrySessionId = this._sessions.get(retryConnectId);
|
|
2185
|
-
if (!retrySessionId) {
|
|
2186
|
-
throw originalErr;
|
|
2187
|
-
}
|
|
2188
|
-
try {
|
|
2189
|
-
if (fingerprint && !fingerprint.skipFingerprint && fingerprint.deviceId) {
|
|
2190
|
-
const fp = await this._abortable(
|
|
2191
|
-
signal,
|
|
2192
|
-
this._verifyDeviceFingerprintWithSession(
|
|
2193
|
-
retrySessionId,
|
|
2194
|
-
fingerprint.deviceId,
|
|
2195
|
-
fingerprint.chain
|
|
2196
|
-
)
|
|
2197
|
-
);
|
|
2198
|
-
if (!fp.success) {
|
|
2199
|
-
throw Object.assign(new Error(formatDeviceMismatchError(fp.expected, fp.actual)), {
|
|
2200
|
-
code: import_hwk_adapter_core3.HardwareErrorCode.DeviceMismatch
|
|
2201
|
-
});
|
|
2202
|
-
}
|
|
2203
|
-
}
|
|
2204
|
-
return await this._callConnector(retrySessionId, method, params, signal);
|
|
2205
|
-
} catch (retryErr) {
|
|
2206
|
-
if (signal.aborted) throw retryErr;
|
|
2207
|
-
this.connector.reset();
|
|
2208
|
-
this._sessions.clear();
|
|
2209
|
-
this._discoveredDevices.clear();
|
|
2210
|
-
this._connectingPromise = null;
|
|
2211
|
-
if (!isDeviceDisconnectedError(retryErr) && !isTimeoutError(retryErr)) {
|
|
2212
|
-
throw retryErr;
|
|
2213
|
-
}
|
|
2214
|
-
debugLog(
|
|
2215
|
-
"[LedgerAdapter] fresh-session retry still failed; resetting connector and rebuilding DMK"
|
|
2216
|
-
);
|
|
2217
|
-
this.connector.reset();
|
|
2218
|
-
this._sessions.clear();
|
|
2219
|
-
this._discoveredDevices.clear();
|
|
2220
|
-
this._connectingPromise = null;
|
|
2221
|
-
const finalConnectId = await this.ensureConnected(
|
|
2222
|
-
targetConnectId,
|
|
2223
|
-
signal,
|
|
2224
|
-
allowUsbEphemeralFallback
|
|
2225
|
-
);
|
|
2226
|
-
const finalSessionId = this._sessions.get(finalConnectId);
|
|
2227
|
-
if (!finalSessionId) {
|
|
2228
|
-
throw originalErr;
|
|
2229
|
-
}
|
|
2230
|
-
if (fingerprint && !fingerprint.skipFingerprint && fingerprint.deviceId) {
|
|
2231
|
-
const fp = await this._abortable(
|
|
2232
|
-
signal,
|
|
2233
|
-
this._verifyDeviceFingerprintWithSession(
|
|
2234
|
-
finalSessionId,
|
|
2235
|
-
fingerprint.deviceId,
|
|
2236
|
-
fingerprint.chain
|
|
2237
|
-
)
|
|
2238
|
-
);
|
|
2239
|
-
if (!fp.success) {
|
|
2240
|
-
throw Object.assign(new Error(formatDeviceMismatchError(fp.expected, fp.actual)), {
|
|
2241
|
-
code: import_hwk_adapter_core3.HardwareErrorCode.DeviceMismatch
|
|
2242
|
-
});
|
|
2243
|
-
}
|
|
2244
|
-
}
|
|
2245
|
-
return this._callConnector(finalSessionId, method, params, signal);
|
|
2246
|
-
}
|
|
2247
|
-
}
|
|
2248
2938
|
/**
|
|
2249
2939
|
* Ensure OS-level device permission (Bluetooth / USB) before proceeding.
|
|
2250
2940
|
*
|
|
@@ -2314,7 +3004,15 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
2314
3004
|
if (err && typeof err === "object" && "code" in err && typeof err.code === "number") {
|
|
2315
3005
|
const e = err;
|
|
2316
3006
|
const params = e.code === import_hwk_adapter_core3.HardwareErrorCode.DevicePermissionDenied && e.reason ? { permissionDeniedReason: e.reason } : e.params;
|
|
2317
|
-
return ledgerFailure(
|
|
3007
|
+
return ledgerFailure(
|
|
3008
|
+
e.code,
|
|
3009
|
+
e.message ?? "Unknown error",
|
|
3010
|
+
e.appName,
|
|
3011
|
+
tag,
|
|
3012
|
+
params,
|
|
3013
|
+
void 0,
|
|
3014
|
+
(0, import_hwk_adapter_core3.isHwkRecoveryHint)(e.recovery) ? e.recovery : void 0
|
|
3015
|
+
);
|
|
2318
3016
|
}
|
|
2319
3017
|
const mapped = mapLedgerError(err);
|
|
2320
3018
|
return ledgerFailure(mapped.code, mapped.message, mapped.appName, tag);
|
|
@@ -2341,7 +3039,7 @@ var _LedgerAdapter = class _LedgerAdapter {
|
|
|
2341
3039
|
deviceId: device.deviceId,
|
|
2342
3040
|
connectId: device.connectId,
|
|
2343
3041
|
label: device.name,
|
|
2344
|
-
connectionType: this.connector.connectionType,
|
|
3042
|
+
connectionType: device.connectionType ?? this.connector.connectionType,
|
|
2345
3043
|
rssi: device.rssi,
|
|
2346
3044
|
isConnectable: device.isConnectable,
|
|
2347
3045
|
serialNumber: device.serialNumber,
|
|
@@ -2366,7 +3064,7 @@ _LedgerAdapter.APP_INSTALL_PROGRESS_MIN_DELTA = 0.05;
|
|
|
2366
3064
|
var LedgerAdapter = _LedgerAdapter;
|
|
2367
3065
|
|
|
2368
3066
|
// src/connector/LedgerConnectorBase.ts
|
|
2369
|
-
var
|
|
3067
|
+
var import_hwk_adapter_core12 = require("@onekeyfe/hwk-adapter-core");
|
|
2370
3068
|
|
|
2371
3069
|
// src/device/LedgerDeviceManager.ts
|
|
2372
3070
|
var LedgerDeviceManager = class {
|
|
@@ -2902,7 +3600,7 @@ var SignerManager = class _SignerManager {
|
|
|
2902
3600
|
return (args) => new import_device_signer_kit_ethereum.SignerEthBuilder(args);
|
|
2903
3601
|
}
|
|
2904
3602
|
static _createContextModule() {
|
|
2905
|
-
const contextModule = new import_context_module.ContextModuleBuilder({}).removeDefaultLoaders().build();
|
|
3603
|
+
const contextModule = new import_context_module.ContextModuleBuilder({}).setChain(import_context_module.ContextModuleChainID.Ethereum).removeDefaultLoaders().build();
|
|
2906
3604
|
return _SignerManager.wrapBlindSigningReportNonBlocking(contextModule);
|
|
2907
3605
|
}
|
|
2908
3606
|
static wrapBlindSigningReportNonBlocking(contextModule) {
|
|
@@ -3373,6 +4071,7 @@ function _applySignaturesToPsbt(psbtHex, signatures) {
|
|
|
3373
4071
|
|
|
3374
4072
|
// src/connector/chains/sol.ts
|
|
3375
4073
|
var import_hwk_adapter_core9 = require("@onekeyfe/hwk-adapter-core");
|
|
4074
|
+
var import_bs58 = __toESM(require("bs58"));
|
|
3376
4075
|
|
|
3377
4076
|
// src/signer/SignerSol.ts
|
|
3378
4077
|
var SignerSol = class {
|
|
@@ -3456,8 +4155,21 @@ async function solSignMessage(ctx, sessionId, params) {
|
|
|
3456
4155
|
const path = normalizePath(params.path);
|
|
3457
4156
|
const messageBytes = (0, import_hwk_adapter_core9.hexToBytes)(params.message);
|
|
3458
4157
|
try {
|
|
4158
|
+
if (params.messageVersion === 1) {
|
|
4159
|
+
const preparedMessage = (0, import_hwk_adapter_core9.prepareSolanaOffchainMessageV1)({
|
|
4160
|
+
message: messageBytes,
|
|
4161
|
+
requiredSigners: params.requiredSigners
|
|
4162
|
+
});
|
|
4163
|
+
const { SignMessageVersion } = await ctx.importLedgerKit(
|
|
4164
|
+
"@ledgerhq/device-signer-kit-solana"
|
|
4165
|
+
);
|
|
4166
|
+
const result2 = await solSigner.signMessage(path, preparedMessage.serializedMessage, {
|
|
4167
|
+
version: SignMessageVersion.Raw
|
|
4168
|
+
});
|
|
4169
|
+
return { signature: decodeBase58Signature(result2.signature) };
|
|
4170
|
+
}
|
|
3459
4171
|
const result = await solSigner.signMessage(path, messageBytes);
|
|
3460
|
-
return { signature: result.signature };
|
|
4172
|
+
return { signature: decodeBase58EnvelopeSignature(result.signature) };
|
|
3461
4173
|
} catch (err) {
|
|
3462
4174
|
ctx.invalidateSession(sessionId);
|
|
3463
4175
|
throw ctx.wrapError(err);
|
|
@@ -3465,11 +4177,27 @@ async function solSignMessage(ctx, sessionId, params) {
|
|
|
3465
4177
|
ctx.clearCanceller(sessionId);
|
|
3466
4178
|
}
|
|
3467
4179
|
}
|
|
4180
|
+
function decodeBase58Signature(signature) {
|
|
4181
|
+
const bytes = import_bs58.default.decode(signature);
|
|
4182
|
+
if (bytes.length !== 64) {
|
|
4183
|
+
throw new Error(`Ledger Solana signature must be 64 bytes, received ${bytes.length}`);
|
|
4184
|
+
}
|
|
4185
|
+
return (0, import_hwk_adapter_core9.bytesToHex)(bytes);
|
|
4186
|
+
}
|
|
4187
|
+
function decodeBase58EnvelopeSignature(envelope) {
|
|
4188
|
+
const bytes = import_bs58.default.decode(envelope);
|
|
4189
|
+
if (bytes.length < 65 || bytes[0] !== 1) {
|
|
4190
|
+
throw new Error("Ledger Solana signature envelope is invalid");
|
|
4191
|
+
}
|
|
4192
|
+
return (0, import_hwk_adapter_core9.bytesToHex)(bytes.subarray(1, 65));
|
|
4193
|
+
}
|
|
3468
4194
|
async function _createSolSigner(ctx, sessionId) {
|
|
3469
4195
|
const dmk = await ctx.getOrCreateDmk();
|
|
3470
|
-
const { ContextModuleBuilder: ContextModuleBuilder2 } = await ctx.importLedgerKit(
|
|
4196
|
+
const { ContextModuleBuilder: ContextModuleBuilder2, ContextModuleChainID: ContextModuleChainID2 } = await ctx.importLedgerKit(
|
|
4197
|
+
"@ledgerhq/context-module"
|
|
4198
|
+
);
|
|
3471
4199
|
const { SignerSolanaBuilder } = await ctx.importLedgerKit("@ledgerhq/device-signer-kit-solana");
|
|
3472
|
-
const contextModule = new ContextModuleBuilder2({}).removeDefaultLoaders().build();
|
|
4200
|
+
const contextModule = new ContextModuleBuilder2({}).setChain(ContextModuleChainID2.Solana).removeDefaultLoaders().build();
|
|
3473
4201
|
const sdkSigner = new SignerSolanaBuilder({ dmk, sessionId }).withContextModule(contextModule).build();
|
|
3474
4202
|
const signer = new SignerSol(sdkSigner);
|
|
3475
4203
|
signer.onInteraction = (interaction) => {
|
|
@@ -3485,360 +4213,196 @@ async function _createSolSigner(ctx, sessionId) {
|
|
|
3485
4213
|
return signer;
|
|
3486
4214
|
}
|
|
3487
4215
|
|
|
3488
|
-
// src/connector/chains/
|
|
3489
|
-
var import_hwk_adapter_core12 = require("@onekeyfe/hwk-adapter-core");
|
|
3490
|
-
var import_hw_app_trx = __toESM(require("@ledgerhq/hw-app-trx"));
|
|
3491
|
-
|
|
3492
|
-
// src/connector/chains/legacyChainCall.ts
|
|
3493
|
-
var import_hwk_adapter_core11 = require("@onekeyfe/hwk-adapter-core");
|
|
3494
|
-
|
|
3495
|
-
// src/app/AppManager.ts
|
|
3496
|
-
var import_device_management_kit2 = require("@ledgerhq/device-management-kit");
|
|
4216
|
+
// src/connector/chains/zcash.ts
|
|
3497
4217
|
var import_hwk_adapter_core10 = require("@onekeyfe/hwk-adapter-core");
|
|
3498
|
-
|
|
3499
|
-
|
|
3500
|
-
|
|
3501
|
-
|
|
3502
|
-
|
|
3503
|
-
|
|
3504
|
-
ADA: "Cardano",
|
|
3505
|
-
DOT: "Polkadot",
|
|
3506
|
-
ATOM: "Cosmos"
|
|
3507
|
-
};
|
|
3508
|
-
var DASHBOARD_APP_NAME = "BOLOS";
|
|
3509
|
-
var AppManager = class {
|
|
3510
|
-
constructor(dmk, options) {
|
|
3511
|
-
this._dmk = dmk;
|
|
3512
|
-
this._waitMs = options?.waitMs ?? 1e3;
|
|
3513
|
-
this._maxRetries = options?.maxRetries ?? 10;
|
|
3514
|
-
}
|
|
3515
|
-
/**
|
|
3516
|
-
* Return the Ledger app name for a given chain ticker,
|
|
3517
|
-
* or undefined if the chain is not supported.
|
|
3518
|
-
*/
|
|
3519
|
-
static getAppName(chain) {
|
|
3520
|
-
return APP_NAME_MAP[chain];
|
|
3521
|
-
}
|
|
3522
|
-
/**
|
|
3523
|
-
* Ensure the target app is open on the device identified by `sessionId`.
|
|
3524
|
-
*
|
|
3525
|
-
* Flow:
|
|
3526
|
-
* 1. Check the currently running app.
|
|
3527
|
-
* 2. If it is already the target, return immediately.
|
|
3528
|
-
* 3. If a different app is running (not dashboard), close it first.
|
|
3529
|
-
* 4. Open the target app.
|
|
3530
|
-
* 5. Poll until the device confirms the target app is running.
|
|
3531
|
-
*/
|
|
3532
|
-
/**
|
|
3533
|
-
* @param onConfirmOnDevice Called BEFORE OpenAppCommand is issued — the
|
|
3534
|
-
* device is about to display "Open <app>" on screen and wait for the
|
|
3535
|
-
* user's button press. UI consumers should show their "open app" prompt
|
|
3536
|
-
* in response. NOT called when the target app is already open (no user
|
|
3537
|
-
* interaction needed in that case).
|
|
3538
|
-
*
|
|
3539
|
-
* Important: OpenAppCommand is blocking. It does not resolve until the user
|
|
3540
|
-
* has physically confirmed on the device, so anything that runs AFTER
|
|
3541
|
-
* `await this._openApp(...)` lands AFTER the prompt is already gone.
|
|
3542
|
-
* Hence the callback must fire BEFORE that await.
|
|
3543
|
-
*/
|
|
3544
|
-
async ensureAppOpen(sessionId, targetAppName, onConfirmOnDevice) {
|
|
3545
|
-
const currentApp = await this._getCurrentApp(sessionId);
|
|
3546
|
-
if (currentApp === targetAppName) {
|
|
3547
|
-
return;
|
|
3548
|
-
}
|
|
3549
|
-
if (!this._isDashboard(currentApp)) {
|
|
3550
|
-
await this._closeCurrentApp(sessionId);
|
|
3551
|
-
await this._waitForApp(sessionId, DASHBOARD_APP_NAME);
|
|
3552
|
-
}
|
|
3553
|
-
onConfirmOnDevice?.();
|
|
3554
|
-
await this._openApp(sessionId, targetAppName);
|
|
3555
|
-
await this._waitForApp(sessionId, targetAppName);
|
|
4218
|
+
|
|
4219
|
+
// src/signer/SignerZcash.ts
|
|
4220
|
+
var SignerZcash = class {
|
|
4221
|
+
// eslint-disable-next-line no-useless-constructor, no-empty-function
|
|
4222
|
+
constructor(_sdk) {
|
|
4223
|
+
this._sdk = _sdk;
|
|
3556
4224
|
}
|
|
3557
|
-
|
|
3558
|
-
|
|
3559
|
-
|
|
3560
|
-
|
|
3561
|
-
|
|
3562
|
-
|
|
3563
|
-
|
|
3564
|
-
|
|
3565
|
-
if ((0, import_device_management_kit2.isSuccessCommandResult)(result)) {
|
|
3566
|
-
debugLog("[AppManager] currentApp:", result.data.name);
|
|
3567
|
-
return result.data.name;
|
|
3568
|
-
}
|
|
3569
|
-
const errResult = result;
|
|
3570
|
-
const dmkErr = errResult.error ?? {};
|
|
3571
|
-
const original = dmkErr.originalError;
|
|
3572
|
-
debugLog(
|
|
3573
|
-
"[AppManager] _getCurrentApp failed sessionId=",
|
|
3574
|
-
sessionId,
|
|
3575
|
-
"tag=",
|
|
3576
|
-
dmkErr._tag,
|
|
3577
|
-
"errorCode=",
|
|
3578
|
-
dmkErr.errorCode,
|
|
3579
|
-
"message=",
|
|
3580
|
-
dmkErr.message,
|
|
3581
|
-
"originalErrorMessage=",
|
|
3582
|
-
original?.message ?? String(original ?? "")
|
|
3583
|
-
);
|
|
3584
|
-
throw Object.assign(
|
|
3585
|
-
new Error(
|
|
3586
|
-
dmkErr.message ?? "Failed to get current app from device"
|
|
3587
|
-
),
|
|
3588
|
-
{
|
|
3589
|
-
_tag: dmkErr._tag,
|
|
3590
|
-
errorCode: dmkErr.errorCode,
|
|
3591
|
-
originalError: original
|
|
3592
|
-
}
|
|
4225
|
+
/** GET_VK: UFVK string (default) or raw 96-byte Orchard FVK. */
|
|
4226
|
+
async getFullViewingKey(derivationPath, options) {
|
|
4227
|
+
const action = this._sdk.getFullViewingKey(derivationPath, options);
|
|
4228
|
+
return deviceActionToPromise(
|
|
4229
|
+
action,
|
|
4230
|
+
this.onInteraction,
|
|
4231
|
+
void 0,
|
|
4232
|
+
this.onRegisterCanceller
|
|
3593
4233
|
);
|
|
3594
4234
|
}
|
|
3595
|
-
|
|
3596
|
-
|
|
3597
|
-
|
|
3598
|
-
|
|
3599
|
-
|
|
3600
|
-
|
|
3601
|
-
|
|
3602
|
-
|
|
3603
|
-
const message = "message" in dmkErr && typeof dmkErr.message === "string" ? dmkErr.message : "";
|
|
3604
|
-
debugLog(
|
|
3605
|
-
"[AppManager] openApp failed:",
|
|
3606
|
-
appName,
|
|
3607
|
-
"errorCode:",
|
|
3608
|
-
errorCode,
|
|
3609
|
-
"tag:",
|
|
3610
|
-
dmkErr._tag
|
|
3611
|
-
);
|
|
3612
|
-
let code;
|
|
3613
|
-
if (errorCode === "6807" || /unknown application/i.test(message)) {
|
|
3614
|
-
code = import_hwk_adapter_core10.HardwareErrorCode.AppNotInstalled;
|
|
3615
|
-
} else if (errorCode === "5501" || dmkErr._tag === "ActionRefusedError") {
|
|
3616
|
-
code = import_hwk_adapter_core10.HardwareErrorCode.UserRejected;
|
|
3617
|
-
}
|
|
3618
|
-
throw Object.assign(new Error(`Failed to open "${appName}"`), {
|
|
3619
|
-
_tag: ERROR_TAG.OpenAppCommand,
|
|
3620
|
-
code,
|
|
3621
|
-
errorCode,
|
|
3622
|
-
statusCode: errorCode,
|
|
3623
|
-
appName,
|
|
3624
|
-
originalError: dmkErr
|
|
3625
|
-
});
|
|
3626
|
-
}
|
|
3627
|
-
}
|
|
3628
|
-
async _closeCurrentApp(sessionId) {
|
|
3629
|
-
debugLog("[AppManager] closeCurrentApp");
|
|
3630
|
-
await this._dmk.sendCommand({
|
|
3631
|
-
sessionId,
|
|
3632
|
-
command: new import_device_management_kit2.CloseAppCommand()
|
|
3633
|
-
});
|
|
3634
|
-
}
|
|
3635
|
-
/**
|
|
3636
|
-
* Poll the device until the expected app is reported as running,
|
|
3637
|
-
* or throw after `_maxRetries` attempts.
|
|
3638
|
-
*/
|
|
3639
|
-
async _waitForApp(sessionId, expectedAppName) {
|
|
3640
|
-
let lastSeen = "";
|
|
3641
|
-
for (let i = 0; i < this._maxRetries; i++) {
|
|
3642
|
-
await this._wait();
|
|
3643
|
-
const current = await this._getCurrentApp(sessionId);
|
|
3644
|
-
lastSeen = current;
|
|
3645
|
-
if (current === expectedAppName) {
|
|
3646
|
-
return;
|
|
3647
|
-
}
|
|
3648
|
-
}
|
|
3649
|
-
debugLog(
|
|
3650
|
-
"[AppManager] waitForApp exhausted: expected=",
|
|
3651
|
-
expectedAppName,
|
|
3652
|
-
"lastSeen=",
|
|
3653
|
-
lastSeen
|
|
3654
|
-
);
|
|
3655
|
-
throw new Error(
|
|
3656
|
-
`Ledger: failed to open "${expectedAppName}" after ${this._maxRetries} retries (last seen: ${lastSeen})`
|
|
4235
|
+
/** GET_SHIELDED_ADDRESS: single-Orchard-receiver UA for a 5-level transparent path. */
|
|
4236
|
+
async getShieldedAddress(derivationPath, options) {
|
|
4237
|
+
const action = this._sdk.getShieldedAddress(derivationPath, options);
|
|
4238
|
+
return deviceActionToPromise(
|
|
4239
|
+
action,
|
|
4240
|
+
this.onInteraction,
|
|
4241
|
+
void 0,
|
|
4242
|
+
this.onRegisterCanceller
|
|
3657
4243
|
);
|
|
3658
4244
|
}
|
|
3659
|
-
_isDashboard(appName) {
|
|
3660
|
-
return appName === DASHBOARD_APP_NAME;
|
|
3661
|
-
}
|
|
3662
|
-
_wait() {
|
|
3663
|
-
return new Promise((resolve) => setTimeout(resolve, this._waitMs));
|
|
3664
|
-
}
|
|
3665
4245
|
};
|
|
3666
4246
|
|
|
3667
|
-
// src/connector/chains/
|
|
3668
|
-
function
|
|
3669
|
-
|
|
3670
|
-
|
|
3671
|
-
|
|
3672
|
-
const { appName, needsConfirmation } = options;
|
|
3673
|
-
let openAppPromptShown = false;
|
|
3674
|
-
const onAppOpenPrompt = () => {
|
|
3675
|
-
openAppPromptShown = true;
|
|
3676
|
-
ctx.emit("ui-event", {
|
|
3677
|
-
type: import_hwk_adapter_core11.EConnectorInteraction.ConfirmOpenApp,
|
|
3678
|
-
payload: { sessionId }
|
|
3679
|
-
});
|
|
3680
|
-
};
|
|
3681
|
-
const closeOpenAppUiIfShown = () => {
|
|
3682
|
-
if (openAppPromptShown) {
|
|
3683
|
-
ctx.emit("ui-event", {
|
|
3684
|
-
type: import_hwk_adapter_core11.EConnectorInteraction.InteractionComplete,
|
|
3685
|
-
payload: { sessionId }
|
|
3686
|
-
});
|
|
3687
|
-
openAppPromptShown = false;
|
|
3688
|
-
}
|
|
3689
|
-
};
|
|
4247
|
+
// src/connector/chains/zcash.ts
|
|
4248
|
+
async function zcashGetFullViewingKey(ctx, sessionId, params) {
|
|
4249
|
+
const signer = await _createZcashSigner(ctx, sessionId);
|
|
4250
|
+
const path = normalizePath(params.path);
|
|
4251
|
+
const mode = params.mode ?? "ufvk";
|
|
3690
4252
|
try {
|
|
3691
|
-
await
|
|
3692
|
-
|
|
3693
|
-
|
|
3694
|
-
"[LegacyChainCall] pre-flight ensureAppOpen failed:",
|
|
3695
|
-
appName,
|
|
3696
|
-
err?.message
|
|
3697
|
-
);
|
|
3698
|
-
closeOpenAppUiIfShown();
|
|
3699
|
-
throw ctx.wrapError(err, { defaultAppName: appName });
|
|
3700
|
-
}
|
|
3701
|
-
const runOnce = async () => {
|
|
3702
|
-
let confirmEmitted = false;
|
|
3703
|
-
if (needsConfirmation) {
|
|
3704
|
-
ctx.emit("ui-event", {
|
|
3705
|
-
type: import_hwk_adapter_core11.EConnectorInteraction.ConfirmOnDevice,
|
|
3706
|
-
payload: { sessionId }
|
|
3707
|
-
});
|
|
3708
|
-
confirmEmitted = true;
|
|
4253
|
+
const result = await signer.getFullViewingKey(path, { mode });
|
|
4254
|
+
if (result.mode === "ufvk") {
|
|
4255
|
+
return { path: params.path, mode, ufvk: result.fullViewingKey };
|
|
3709
4256
|
}
|
|
3710
|
-
|
|
3711
|
-
|
|
3712
|
-
|
|
3713
|
-
|
|
3714
|
-
|
|
3715
|
-
|
|
3716
|
-
|
|
3717
|
-
|
|
3718
|
-
|
|
3719
|
-
|
|
3720
|
-
|
|
3721
|
-
};
|
|
4257
|
+
return { path: params.path, mode, orchardFvk: (0, import_hwk_adapter_core10.bytesToHex)(result.fullViewingKey) };
|
|
4258
|
+
} catch (err) {
|
|
4259
|
+
ctx.invalidateSession(sessionId);
|
|
4260
|
+
throw ctx.wrapError(err);
|
|
4261
|
+
} finally {
|
|
4262
|
+
ctx.clearCanceller(sessionId);
|
|
4263
|
+
}
|
|
4264
|
+
}
|
|
4265
|
+
async function zcashGetShieldedAddress(ctx, sessionId, params) {
|
|
4266
|
+
const signer = await _createZcashSigner(ctx, sessionId);
|
|
4267
|
+
const path = normalizePath(params.path);
|
|
3722
4268
|
try {
|
|
3723
|
-
|
|
4269
|
+
const result = await signer.getShieldedAddress(path, {
|
|
4270
|
+
checkOnDevice: params.showOnDevice ?? false
|
|
4271
|
+
});
|
|
4272
|
+
return { address: result.address, path: params.path };
|
|
3724
4273
|
} catch (err) {
|
|
3725
|
-
|
|
3726
|
-
|
|
3727
|
-
|
|
3728
|
-
|
|
3729
|
-
}
|
|
3730
|
-
debugLog("[LegacyChainCall] wrong-app detected, retrying:", appName);
|
|
3731
|
-
try {
|
|
3732
|
-
await _ensureAppOpen(ctx, sessionId, appName, onAppOpenPrompt);
|
|
3733
|
-
} catch (switchErr) {
|
|
3734
|
-
debugLog(
|
|
3735
|
-
"[LegacyChainCall] retry ensureAppOpen failed:",
|
|
3736
|
-
appName,
|
|
3737
|
-
switchErr?.message
|
|
3738
|
-
);
|
|
3739
|
-
closeOpenAppUiIfShown();
|
|
3740
|
-
throw ctx.wrapError(switchErr, { defaultAppName: appName });
|
|
3741
|
-
}
|
|
3742
|
-
ctx.clearAllSigners();
|
|
3743
|
-
const result = await runOnce();
|
|
3744
|
-
debugLog("[LegacyChainCall] retry succeeded:", appName);
|
|
3745
|
-
return result;
|
|
4274
|
+
ctx.invalidateSession(sessionId);
|
|
4275
|
+
throw ctx.wrapError(err);
|
|
4276
|
+
} finally {
|
|
4277
|
+
ctx.clearCanceller(sessionId);
|
|
3746
4278
|
}
|
|
3747
4279
|
}
|
|
3748
|
-
async function
|
|
4280
|
+
async function _createZcashSigner(ctx, sessionId) {
|
|
3749
4281
|
const dmk = await ctx.getOrCreateDmk();
|
|
3750
|
-
const
|
|
3751
|
-
|
|
4282
|
+
const { SignerZcashBuilder } = await ctx.importLedgerKit("@ledgerhq/device-signer-kit-zcash");
|
|
4283
|
+
const sdkSigner = new SignerZcashBuilder({ dmk, sessionId }).build();
|
|
4284
|
+
const signer = new SignerZcash(sdkSigner);
|
|
4285
|
+
signer.onInteraction = (interaction) => {
|
|
4286
|
+
debugLog("[LedgerConnector] zcash.onInteraction:", interaction);
|
|
4287
|
+
ctx.emit("ui-event", {
|
|
4288
|
+
type: collapseSignerInteraction(interaction),
|
|
4289
|
+
payload: { sessionId }
|
|
4290
|
+
});
|
|
4291
|
+
};
|
|
4292
|
+
signer.onRegisterCanceller = (cancel) => ctx.registerCanceller(sessionId, cancel);
|
|
4293
|
+
return signer;
|
|
3752
4294
|
}
|
|
3753
4295
|
|
|
3754
|
-
// src/
|
|
3755
|
-
var
|
|
3756
|
-
|
|
3757
|
-
|
|
3758
|
-
|
|
3759
|
-
|
|
3760
|
-
|
|
4296
|
+
// src/connector/chains/tron.ts
|
|
4297
|
+
var import_hwk_adapter_core11 = require("@onekeyfe/hwk-adapter-core");
|
|
4298
|
+
|
|
4299
|
+
// src/signer/SignerTron.ts
|
|
4300
|
+
var SignerTron = class {
|
|
4301
|
+
// eslint-disable-next-line no-useless-constructor, no-empty-function
|
|
4302
|
+
constructor(_sdk) {
|
|
4303
|
+
this._sdk = _sdk;
|
|
3761
4304
|
}
|
|
3762
|
-
|
|
3763
|
-
|
|
3764
|
-
|
|
3765
|
-
|
|
3766
|
-
|
|
3767
|
-
|
|
3768
|
-
|
|
3769
|
-
|
|
3770
|
-
|
|
3771
|
-
}
|
|
3772
|
-
result.set(statusCode, data.length);
|
|
3773
|
-
return result;
|
|
4305
|
+
/** Base58 Tron address plus the uncompressed public key at `derivationPath`. */
|
|
4306
|
+
async getAddress(derivationPath, options) {
|
|
4307
|
+
const action = this._sdk.getAddress(derivationPath, options);
|
|
4308
|
+
const result = await deviceActionToPromise(
|
|
4309
|
+
action,
|
|
4310
|
+
this.onInteraction,
|
|
4311
|
+
void 0,
|
|
4312
|
+
this.onRegisterCanceller
|
|
4313
|
+
);
|
|
4314
|
+
return { address: result.address, publicKey: result.publicKey };
|
|
3774
4315
|
}
|
|
3775
|
-
|
|
4316
|
+
/** Sign a protobuf-encoded raw transaction. */
|
|
4317
|
+
async signTransaction(derivationPath, transaction, options) {
|
|
4318
|
+
const action = this._sdk.signTransaction(derivationPath, transaction, options);
|
|
4319
|
+
return deviceActionToPromise(
|
|
4320
|
+
action,
|
|
4321
|
+
this.onInteraction,
|
|
4322
|
+
void 0,
|
|
4323
|
+
this.onRegisterCanceller
|
|
4324
|
+
);
|
|
4325
|
+
}
|
|
4326
|
+
/** Sign a personal message (TIP-191). */
|
|
4327
|
+
async signPersonalMessage(derivationPath, message, options) {
|
|
4328
|
+
const action = this._sdk.signPersonalMessage(derivationPath, message, options);
|
|
4329
|
+
return deviceActionToPromise(
|
|
4330
|
+
action,
|
|
4331
|
+
this.onInteraction,
|
|
4332
|
+
void 0,
|
|
4333
|
+
this.onRegisterCanceller
|
|
4334
|
+
);
|
|
3776
4335
|
}
|
|
3777
4336
|
};
|
|
3778
4337
|
|
|
3779
4338
|
// src/connector/chains/tron.ts
|
|
3780
4339
|
async function tronGetAddress(ctx, sessionId, params) {
|
|
4340
|
+
const tronSigner = await _createTronSigner(ctx, sessionId);
|
|
3781
4341
|
const path = normalizePath(params.path);
|
|
3782
|
-
|
|
3783
|
-
|
|
3784
|
-
|
|
3785
|
-
|
|
3786
|
-
{
|
|
3787
|
-
|
|
3788
|
-
|
|
3789
|
-
|
|
3790
|
-
|
|
3791
|
-
|
|
3792
|
-
|
|
3793
|
-
const trx = await _createTrx(ctx, sid);
|
|
3794
|
-
const result = await trx.getAddress(path, showOnDevice);
|
|
3795
|
-
return { address: result.address, publicKey: result.publicKey, path: params.path };
|
|
3796
|
-
}
|
|
3797
|
-
);
|
|
4342
|
+
try {
|
|
4343
|
+
const result = await tronSigner.getAddress(path, {
|
|
4344
|
+
checkOnDevice: params.showOnDevice ?? false
|
|
4345
|
+
});
|
|
4346
|
+
return { address: result.address, publicKey: result.publicKey, path: params.path };
|
|
4347
|
+
} catch (err) {
|
|
4348
|
+
ctx.invalidateSession(sessionId);
|
|
4349
|
+
throw ctx.wrapError(err);
|
|
4350
|
+
} finally {
|
|
4351
|
+
ctx.clearCanceller(sessionId);
|
|
4352
|
+
}
|
|
3798
4353
|
}
|
|
3799
4354
|
async function tronSignTransaction(ctx, sessionId, params) {
|
|
3800
4355
|
if (!params.rawTxHex) {
|
|
3801
4356
|
throw Object.assign(
|
|
3802
4357
|
new Error("TRON signing requires a protobuf-encoded raw transaction hex (rawTxHex)."),
|
|
3803
|
-
{ code:
|
|
4358
|
+
{ code: import_hwk_adapter_core11.HardwareErrorCode.InvalidParams }
|
|
3804
4359
|
);
|
|
3805
4360
|
}
|
|
4361
|
+
const tronSigner = await _createTronSigner(ctx, sessionId);
|
|
3806
4362
|
const path = normalizePath(params.path);
|
|
3807
|
-
|
|
3808
|
-
|
|
3809
|
-
|
|
3810
|
-
|
|
3811
|
-
|
|
3812
|
-
|
|
3813
|
-
|
|
3814
|
-
|
|
3815
|
-
|
|
3816
|
-
params.tokenSignatures ?? []
|
|
3817
|
-
);
|
|
3818
|
-
return { signature };
|
|
3819
|
-
}
|
|
3820
|
-
);
|
|
4363
|
+
try {
|
|
4364
|
+
const signature = await tronSigner.signTransaction(path, (0, import_hwk_adapter_core11.hexToBytes)(params.rawTxHex));
|
|
4365
|
+
return { signature: (0, import_hwk_adapter_core11.bytesToHex)(signature) };
|
|
4366
|
+
} catch (err) {
|
|
4367
|
+
ctx.invalidateSession(sessionId);
|
|
4368
|
+
throw ctx.wrapError(err);
|
|
4369
|
+
} finally {
|
|
4370
|
+
ctx.clearCanceller(sessionId);
|
|
4371
|
+
}
|
|
3821
4372
|
}
|
|
3822
4373
|
async function tronSignMessage(ctx, sessionId, params) {
|
|
4374
|
+
const tronSigner = await _createTronSigner(ctx, sessionId);
|
|
3823
4375
|
const path = normalizePath(params.path);
|
|
3824
|
-
|
|
3825
|
-
|
|
3826
|
-
|
|
3827
|
-
|
|
3828
|
-
|
|
3829
|
-
|
|
3830
|
-
|
|
3831
|
-
|
|
3832
|
-
|
|
3833
|
-
);
|
|
4376
|
+
try {
|
|
4377
|
+
const signature = await tronSigner.signPersonalMessage(path, (0, import_hwk_adapter_core11.hexToBytes)(params.messageHex));
|
|
4378
|
+
return { signature: (0, import_hwk_adapter_core11.bytesToHex)(signature) };
|
|
4379
|
+
} catch (err) {
|
|
4380
|
+
ctx.invalidateSession(sessionId);
|
|
4381
|
+
throw ctx.wrapError(err);
|
|
4382
|
+
} finally {
|
|
4383
|
+
ctx.clearCanceller(sessionId);
|
|
4384
|
+
}
|
|
3834
4385
|
}
|
|
3835
|
-
async function
|
|
4386
|
+
async function _createTronSigner(ctx, sessionId) {
|
|
3836
4387
|
const dmk = await ctx.getOrCreateDmk();
|
|
3837
|
-
|
|
4388
|
+
const { SignerTrxBuilder } = await ctx.importLedgerKit("@ledgerhq/device-signer-kit-tron");
|
|
4389
|
+
const sdkSigner = new SignerTrxBuilder({ dmk, sessionId }).build();
|
|
4390
|
+
const signer = new SignerTron(sdkSigner);
|
|
4391
|
+
signer.onInteraction = (interaction) => {
|
|
4392
|
+
debugLog("[LedgerConnector] tron.onInteraction:", interaction);
|
|
4393
|
+
ctx.emit("ui-event", {
|
|
4394
|
+
type: collapseSignerInteraction(interaction),
|
|
4395
|
+
payload: { sessionId }
|
|
4396
|
+
});
|
|
4397
|
+
};
|
|
4398
|
+
signer.onRegisterCanceller = (cancel) => {
|
|
4399
|
+
ctx.registerCanceller(sessionId, cancel);
|
|
4400
|
+
};
|
|
4401
|
+
return signer;
|
|
3838
4402
|
}
|
|
3839
4403
|
|
|
3840
4404
|
// src/device-apps/customActions.ts
|
|
3841
|
-
var
|
|
4405
|
+
var import_device_management_kit2 = require("@ledgerhq/device-management-kit");
|
|
3842
4406
|
var import_rxjs = require("rxjs");
|
|
3843
4407
|
var GetOsVersionDeviceAction = class {
|
|
3844
4408
|
// eslint-disable-next-line no-useless-constructor, no-empty-function
|
|
@@ -3852,7 +4416,7 @@ var GetOsVersionDeviceAction = class {
|
|
|
3852
4416
|
(async () => {
|
|
3853
4417
|
try {
|
|
3854
4418
|
subject.next({
|
|
3855
|
-
status:
|
|
4419
|
+
status: import_device_management_kit2.DeviceActionStatus.Pending,
|
|
3856
4420
|
intermediateValue: { requiredUserInteraction: "none" }
|
|
3857
4421
|
});
|
|
3858
4422
|
const result = await internalApi.sendCommand(new this._deps.GetOsVersionCommand());
|
|
@@ -3861,11 +4425,11 @@ var GetOsVersionDeviceAction = class {
|
|
|
3861
4425
|
const errObj = result?.error;
|
|
3862
4426
|
throw new Error(errObj?.message ?? "GetOsVersionCommand failed");
|
|
3863
4427
|
}
|
|
3864
|
-
subject.next({ status:
|
|
4428
|
+
subject.next({ status: import_device_management_kit2.DeviceActionStatus.Completed, output: result.data });
|
|
3865
4429
|
subject.complete();
|
|
3866
4430
|
} catch (err) {
|
|
3867
4431
|
if (cancelled) return;
|
|
3868
|
-
subject.next({ status:
|
|
4432
|
+
subject.next({ status: import_device_management_kit2.DeviceActionStatus.Error, error: err });
|
|
3869
4433
|
subject.complete();
|
|
3870
4434
|
}
|
|
3871
4435
|
})();
|
|
@@ -3873,7 +4437,7 @@ var GetOsVersionDeviceAction = class {
|
|
|
3873
4437
|
observable: subject.asObservable(),
|
|
3874
4438
|
cancel: () => {
|
|
3875
4439
|
cancelled = true;
|
|
3876
|
-
subject.next({ status:
|
|
4440
|
+
subject.next({ status: import_device_management_kit2.DeviceActionStatus.Stopped });
|
|
3877
4441
|
subject.complete();
|
|
3878
4442
|
}
|
|
3879
4443
|
};
|
|
@@ -3891,7 +4455,7 @@ var ListAvailableAppsDeviceAction = class {
|
|
|
3891
4455
|
(async () => {
|
|
3892
4456
|
try {
|
|
3893
4457
|
subject.next({
|
|
3894
|
-
status:
|
|
4458
|
+
status: import_device_management_kit2.DeviceActionStatus.Pending,
|
|
3895
4459
|
intermediateValue: { requiredUserInteraction: "none" }
|
|
3896
4460
|
});
|
|
3897
4461
|
const osVersionResult = await internalApi.sendCommand(new this._GetOsVersionCommand());
|
|
@@ -3908,11 +4472,11 @@ var ListAvailableAppsDeviceAction = class {
|
|
|
3908
4472
|
throw new Error(httpErr?.message ?? "Manager API getAppList failed");
|
|
3909
4473
|
}
|
|
3910
4474
|
const apps = either.extract();
|
|
3911
|
-
subject.next({ status:
|
|
4475
|
+
subject.next({ status: import_device_management_kit2.DeviceActionStatus.Completed, output: apps });
|
|
3912
4476
|
subject.complete();
|
|
3913
4477
|
} catch (err) {
|
|
3914
4478
|
if (cancelled) return;
|
|
3915
|
-
subject.next({ status:
|
|
4479
|
+
subject.next({ status: import_device_management_kit2.DeviceActionStatus.Error, error: err });
|
|
3916
4480
|
subject.complete();
|
|
3917
4481
|
}
|
|
3918
4482
|
})();
|
|
@@ -3920,7 +4484,7 @@ var ListAvailableAppsDeviceAction = class {
|
|
|
3920
4484
|
observable: subject.asObservable(),
|
|
3921
4485
|
cancel: () => {
|
|
3922
4486
|
cancelled = true;
|
|
3923
|
-
subject.next({ status:
|
|
4487
|
+
subject.next({ status: import_device_management_kit2.DeviceActionStatus.Stopped });
|
|
3924
4488
|
subject.complete();
|
|
3925
4489
|
}
|
|
3926
4490
|
};
|
|
@@ -4013,7 +4577,7 @@ var DeviceApps = class {
|
|
|
4013
4577
|
seTargetId: v.seTargetId,
|
|
4014
4578
|
mcuTargetId: v.mcuTargetId,
|
|
4015
4579
|
seVersion: v.seVersion,
|
|
4016
|
-
seFlagsHex:
|
|
4580
|
+
seFlagsHex: bytesToHex4(v.seFlags),
|
|
4017
4581
|
mcuSephVersion: v.mcuSephVersion,
|
|
4018
4582
|
mcuBootloaderVersion: v.mcuBootloaderVersion,
|
|
4019
4583
|
hwVersion: v.hwVersion
|
|
@@ -4087,7 +4651,7 @@ var DeviceApps = class {
|
|
|
4087
4651
|
}
|
|
4088
4652
|
}
|
|
4089
4653
|
};
|
|
4090
|
-
function
|
|
4654
|
+
function bytesToHex4(bytes) {
|
|
4091
4655
|
if (!bytes) return "";
|
|
4092
4656
|
return Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join("");
|
|
4093
4657
|
}
|
|
@@ -4130,10 +4694,11 @@ var METHOD_PREFIX_TO_APP_NAME = {
|
|
|
4130
4694
|
evm: "Ethereum",
|
|
4131
4695
|
btc: "Bitcoin",
|
|
4132
4696
|
sol: "Solana",
|
|
4133
|
-
tron: "Tron"
|
|
4697
|
+
tron: "Tron",
|
|
4698
|
+
zcash: "Zcash"
|
|
4134
4699
|
};
|
|
4135
4700
|
var HARDWARE_ERROR_CODE_VALUES = new Set(
|
|
4136
|
-
Object.values(
|
|
4701
|
+
Object.values(import_hwk_adapter_core12.HardwareErrorCode).filter((value) => typeof value === "number")
|
|
4137
4702
|
);
|
|
4138
4703
|
var BLE_CONNECT_SCAN_TIMEOUT_MS = 1500;
|
|
4139
4704
|
var LEDGER_RELAY_ALLOWED_ROOT_DOMAINS = ["onekeytest.com", "onekey.com"];
|
|
@@ -4170,6 +4735,10 @@ async function defaultLedgerKitImporter(pkg) {
|
|
|
4170
4735
|
return import("@ledgerhq/device-signer-kit-bitcoin");
|
|
4171
4736
|
case "@ledgerhq/device-signer-kit-solana":
|
|
4172
4737
|
return import("@ledgerhq/device-signer-kit-solana");
|
|
4738
|
+
case "@ledgerhq/device-signer-kit-tron":
|
|
4739
|
+
return import("@ledgerhq/device-signer-kit-tron");
|
|
4740
|
+
case "@ledgerhq/device-signer-kit-zcash":
|
|
4741
|
+
return import("@ledgerhq/device-signer-kit-zcash");
|
|
4173
4742
|
case "@ledgerhq/context-module":
|
|
4174
4743
|
return import("@ledgerhq/context-module");
|
|
4175
4744
|
default:
|
|
@@ -4280,7 +4849,7 @@ var LedgerConnectorBase = class {
|
|
|
4280
4849
|
async searchDevices() {
|
|
4281
4850
|
const dm = await this._getDeviceManager();
|
|
4282
4851
|
const descriptors = await this._discoverDescriptors(dm);
|
|
4283
|
-
const resolvedDescriptors = descriptors.filter((d) => !(0,
|
|
4852
|
+
const resolvedDescriptors = descriptors.filter((d) => !(0, import_hwk_adapter_core12.isKnownNonTargetHardwareVendor)(d, "ledger")).map((d) => ({
|
|
4284
4853
|
descriptor: d,
|
|
4285
4854
|
connectId: this._resolveConnectId(d)
|
|
4286
4855
|
}));
|
|
@@ -4328,7 +4897,7 @@ var LedgerConnectorBase = class {
|
|
|
4328
4897
|
`Ledger BLE connect did not return within ${HANG_CEILING_MS / 6e4}min \u2014 DMK hang fallback.`
|
|
4329
4898
|
);
|
|
4330
4899
|
err._tag = ERROR_TAG.BlePairingTimeout;
|
|
4331
|
-
err.code =
|
|
4900
|
+
err.code = import_hwk_adapter_core12.HardwareErrorCode.BlePairingTimeout;
|
|
4332
4901
|
reject(err);
|
|
4333
4902
|
}, HANG_CEILING_MS);
|
|
4334
4903
|
});
|
|
@@ -4361,7 +4930,7 @@ var LedgerConnectorBase = class {
|
|
|
4361
4930
|
"Ledger device is not currently advertising. Wake up and unlock the device, keep it nearby, then try again."
|
|
4362
4931
|
);
|
|
4363
4932
|
err._tag = ERROR_TAG.DeviceNotAdvertising;
|
|
4364
|
-
err.code =
|
|
4933
|
+
err.code = import_hwk_adapter_core12.HardwareErrorCode.DeviceNotFound;
|
|
4365
4934
|
throw err;
|
|
4366
4935
|
};
|
|
4367
4936
|
const doConnect = async (path) => {
|
|
@@ -4425,7 +4994,7 @@ var LedgerConnectorBase = class {
|
|
|
4425
4994
|
"Ledger Bluetooth pairing failed. Make sure the device is unlocked and nearby, then try again."
|
|
4426
4995
|
);
|
|
4427
4996
|
wrapped._tag = ERROR_TAG.BleGattBondingFailed;
|
|
4428
|
-
wrapped.code =
|
|
4997
|
+
wrapped.code = import_hwk_adapter_core12.HardwareErrorCode.BlePairingTimeout;
|
|
4429
4998
|
wrapped.originalError = err;
|
|
4430
4999
|
throw wrapped;
|
|
4431
5000
|
}
|
|
@@ -4503,7 +5072,7 @@ var LedgerConnectorBase = class {
|
|
|
4503
5072
|
this._unwatchSessionState(sessionId);
|
|
4504
5073
|
this._signerManager?.invalidate(sessionId);
|
|
4505
5074
|
this._cancellers.get(sessionId)?.({
|
|
4506
|
-
code:
|
|
5075
|
+
code: import_hwk_adapter_core12.HardwareErrorCode.DeviceDisconnected,
|
|
4507
5076
|
tag: "DeviceDisconnected",
|
|
4508
5077
|
message: "Device disconnected"
|
|
4509
5078
|
});
|
|
@@ -4527,9 +5096,9 @@ var LedgerConnectorBase = class {
|
|
|
4527
5096
|
if (isAppStuckByApdu(err)) {
|
|
4528
5097
|
return {
|
|
4529
5098
|
success: false,
|
|
4530
|
-
error: (0,
|
|
5099
|
+
error: (0, import_hwk_adapter_core12.serializeConnectorError)(
|
|
4531
5100
|
Object.assign(new Error("Ledger app is unresponsive"), {
|
|
4532
|
-
code:
|
|
5101
|
+
code: import_hwk_adapter_core12.HardwareErrorCode.DeviceAppStuck,
|
|
4533
5102
|
_tag: ERROR_TAG.DeviceAppStuck,
|
|
4534
5103
|
originalError: err
|
|
4535
5104
|
})
|
|
@@ -4539,16 +5108,16 @@ var LedgerConnectorBase = class {
|
|
|
4539
5108
|
if (isTransportStuck(err)) {
|
|
4540
5109
|
return {
|
|
4541
5110
|
success: false,
|
|
4542
|
-
error: (0,
|
|
5111
|
+
error: (0, import_hwk_adapter_core12.serializeConnectorError)(
|
|
4543
5112
|
Object.assign(new Error("Device communication interrupted, please retry"), {
|
|
4544
|
-
code:
|
|
5113
|
+
code: import_hwk_adapter_core12.HardwareErrorCode.TransportError,
|
|
4545
5114
|
_tag: ERROR_TAG.DeviceTransportStuck,
|
|
4546
5115
|
originalError: err
|
|
4547
5116
|
})
|
|
4548
5117
|
)
|
|
4549
5118
|
};
|
|
4550
5119
|
}
|
|
4551
|
-
return { success: false, error: (0,
|
|
5120
|
+
return { success: false, error: (0, import_hwk_adapter_core12.serializeConnectorError)(err) };
|
|
4552
5121
|
}
|
|
4553
5122
|
}
|
|
4554
5123
|
async _dispatch(sessionId, method, params) {
|
|
@@ -4587,6 +5156,11 @@ var LedgerConnectorBase = class {
|
|
|
4587
5156
|
return solSignTransaction(ctx, sessionId, params);
|
|
4588
5157
|
case "solSignMessage":
|
|
4589
5158
|
return solSignMessage(ctx, sessionId, params);
|
|
5159
|
+
// ZCASH
|
|
5160
|
+
case "zcashGetFullViewingKey":
|
|
5161
|
+
return zcashGetFullViewingKey(ctx, sessionId, params);
|
|
5162
|
+
case "zcashGetShieldedAddress":
|
|
5163
|
+
return zcashGetShieldedAddress(ctx, sessionId, params);
|
|
4590
5164
|
// TRON
|
|
4591
5165
|
case "tronGetAddress":
|
|
4592
5166
|
return tronGetAddress(ctx, sessionId, params);
|
|
@@ -4611,7 +5185,7 @@ var LedgerConnectorBase = class {
|
|
|
4611
5185
|
try {
|
|
4612
5186
|
return await apps.install(p.appName, ({ progress }) => {
|
|
4613
5187
|
ctx.emit("ui-event", {
|
|
4614
|
-
type:
|
|
5188
|
+
type: import_hwk_adapter_core12.EConnectorInteraction.AppInstallProgress,
|
|
4615
5189
|
payload: {
|
|
4616
5190
|
sessionId,
|
|
4617
5191
|
appName: p.appName,
|
|
@@ -4916,7 +5490,7 @@ var LedgerConnectorBase = class {
|
|
|
4916
5490
|
* at every catch site. Falls through unchanged for unknown methods.
|
|
4917
5491
|
*/
|
|
4918
5492
|
_ctxForMethod(method) {
|
|
4919
|
-
const prefix = /^(evm|btc|sol|tron)/.exec(method)?.[1];
|
|
5493
|
+
const prefix = /^(evm|btc|sol|tron|zcash)/.exec(method)?.[1];
|
|
4920
5494
|
const defaultAppName = prefix ? METHOD_PREFIX_TO_APP_NAME[prefix] : void 0;
|
|
4921
5495
|
if (!defaultAppName) return this._ctx;
|
|
4922
5496
|
return {
|
|
@@ -4950,6 +5524,204 @@ var LedgerConnectorBase = class {
|
|
|
4950
5524
|
return error;
|
|
4951
5525
|
}
|
|
4952
5526
|
};
|
|
5527
|
+
|
|
5528
|
+
// src/transport/DmkTransport.ts
|
|
5529
|
+
var import_hw_transport = __toESM(require("@ledgerhq/hw-transport"));
|
|
5530
|
+
var DmkTransport = class extends import_hw_transport.default {
|
|
5531
|
+
constructor(dmk, sessionId) {
|
|
5532
|
+
super();
|
|
5533
|
+
this._dmk = dmk;
|
|
5534
|
+
this._sessionId = sessionId;
|
|
5535
|
+
}
|
|
5536
|
+
async exchange(apdu) {
|
|
5537
|
+
const response = await this._dmk.sendApdu({
|
|
5538
|
+
sessionId: this._sessionId,
|
|
5539
|
+
apdu: new Uint8Array(apdu)
|
|
5540
|
+
});
|
|
5541
|
+
const { data, statusCode } = response;
|
|
5542
|
+
const result = Buffer.alloc(data.length + 2);
|
|
5543
|
+
if (data.length > 0) {
|
|
5544
|
+
result.set(data, 0);
|
|
5545
|
+
}
|
|
5546
|
+
result.set(statusCode, data.length);
|
|
5547
|
+
return result;
|
|
5548
|
+
}
|
|
5549
|
+
async close() {
|
|
5550
|
+
}
|
|
5551
|
+
};
|
|
5552
|
+
|
|
5553
|
+
// src/app/AppManager.ts
|
|
5554
|
+
var import_device_management_kit3 = require("@ledgerhq/device-management-kit");
|
|
5555
|
+
var import_hwk_adapter_core13 = require("@onekeyfe/hwk-adapter-core");
|
|
5556
|
+
var APP_NAME_MAP = {
|
|
5557
|
+
ETH: "Ethereum",
|
|
5558
|
+
BTC: "Bitcoin",
|
|
5559
|
+
SOL: "Solana",
|
|
5560
|
+
TRX: "Tron",
|
|
5561
|
+
ZEC: "Zcash",
|
|
5562
|
+
XRP: "XRP",
|
|
5563
|
+
ADA: "Cardano",
|
|
5564
|
+
DOT: "Polkadot",
|
|
5565
|
+
ATOM: "Cosmos"
|
|
5566
|
+
};
|
|
5567
|
+
var DASHBOARD_APP_NAME = "BOLOS";
|
|
5568
|
+
var AppManager = class {
|
|
5569
|
+
constructor(dmk, options) {
|
|
5570
|
+
this._dmk = dmk;
|
|
5571
|
+
this._waitMs = options?.waitMs ?? 1e3;
|
|
5572
|
+
this._maxRetries = options?.maxRetries ?? 10;
|
|
5573
|
+
}
|
|
5574
|
+
/**
|
|
5575
|
+
* Return the Ledger app name for a given chain ticker,
|
|
5576
|
+
* or undefined if the chain is not supported.
|
|
5577
|
+
*/
|
|
5578
|
+
static getAppName(chain) {
|
|
5579
|
+
return APP_NAME_MAP[chain];
|
|
5580
|
+
}
|
|
5581
|
+
/**
|
|
5582
|
+
* Ensure the target app is open on the device identified by `sessionId`.
|
|
5583
|
+
*
|
|
5584
|
+
* Flow:
|
|
5585
|
+
* 1. Check the currently running app.
|
|
5586
|
+
* 2. If it is already the target, return immediately.
|
|
5587
|
+
* 3. If a different app is running (not dashboard), close it first.
|
|
5588
|
+
* 4. Open the target app.
|
|
5589
|
+
* 5. Poll until the device confirms the target app is running.
|
|
5590
|
+
*/
|
|
5591
|
+
/**
|
|
5592
|
+
* @param onConfirmOnDevice Called BEFORE OpenAppCommand is issued — the
|
|
5593
|
+
* device is about to display "Open <app>" on screen and wait for the
|
|
5594
|
+
* user's button press. UI consumers should show their "open app" prompt
|
|
5595
|
+
* in response. NOT called when the target app is already open (no user
|
|
5596
|
+
* interaction needed in that case).
|
|
5597
|
+
*
|
|
5598
|
+
* Important: OpenAppCommand is blocking. It does not resolve until the user
|
|
5599
|
+
* has physically confirmed on the device, so anything that runs AFTER
|
|
5600
|
+
* `await this._openApp(...)` lands AFTER the prompt is already gone.
|
|
5601
|
+
* Hence the callback must fire BEFORE that await.
|
|
5602
|
+
*/
|
|
5603
|
+
async ensureAppOpen(sessionId, targetAppName, onConfirmOnDevice) {
|
|
5604
|
+
const currentApp = await this._getCurrentApp(sessionId);
|
|
5605
|
+
if (currentApp === targetAppName) {
|
|
5606
|
+
return;
|
|
5607
|
+
}
|
|
5608
|
+
if (!this._isDashboard(currentApp)) {
|
|
5609
|
+
await this._closeCurrentApp(sessionId);
|
|
5610
|
+
await this._waitForApp(sessionId, DASHBOARD_APP_NAME);
|
|
5611
|
+
}
|
|
5612
|
+
onConfirmOnDevice?.();
|
|
5613
|
+
await this._openApp(sessionId, targetAppName);
|
|
5614
|
+
await this._waitForApp(sessionId, targetAppName);
|
|
5615
|
+
}
|
|
5616
|
+
// ---------------------------------------------------------------------------
|
|
5617
|
+
// Private helpers
|
|
5618
|
+
// ---------------------------------------------------------------------------
|
|
5619
|
+
async _getCurrentApp(sessionId) {
|
|
5620
|
+
const result = await this._dmk.sendCommand({
|
|
5621
|
+
sessionId,
|
|
5622
|
+
command: new import_device_management_kit3.GetAppAndVersionCommand()
|
|
5623
|
+
});
|
|
5624
|
+
if ((0, import_device_management_kit3.isSuccessCommandResult)(result)) {
|
|
5625
|
+
debugLog("[AppManager] currentApp:", result.data.name);
|
|
5626
|
+
return result.data.name;
|
|
5627
|
+
}
|
|
5628
|
+
const errResult = result;
|
|
5629
|
+
const dmkErr = errResult.error ?? {};
|
|
5630
|
+
const original = dmkErr.originalError;
|
|
5631
|
+
debugLog(
|
|
5632
|
+
"[AppManager] _getCurrentApp failed sessionId=",
|
|
5633
|
+
sessionId,
|
|
5634
|
+
"tag=",
|
|
5635
|
+
dmkErr._tag,
|
|
5636
|
+
"errorCode=",
|
|
5637
|
+
dmkErr.errorCode,
|
|
5638
|
+
"message=",
|
|
5639
|
+
dmkErr.message,
|
|
5640
|
+
"originalErrorMessage=",
|
|
5641
|
+
original?.message ?? String(original ?? "")
|
|
5642
|
+
);
|
|
5643
|
+
throw Object.assign(
|
|
5644
|
+
new Error(
|
|
5645
|
+
dmkErr.message ?? "Failed to get current app from device"
|
|
5646
|
+
),
|
|
5647
|
+
{
|
|
5648
|
+
_tag: dmkErr._tag,
|
|
5649
|
+
errorCode: dmkErr.errorCode,
|
|
5650
|
+
originalError: original
|
|
5651
|
+
}
|
|
5652
|
+
);
|
|
5653
|
+
}
|
|
5654
|
+
async _openApp(sessionId, appName) {
|
|
5655
|
+
const result = await this._dmk.sendCommand({
|
|
5656
|
+
sessionId,
|
|
5657
|
+
command: new import_device_management_kit3.OpenAppCommand({ appName })
|
|
5658
|
+
});
|
|
5659
|
+
if (!(0, import_device_management_kit3.isSuccessCommandResult)(result)) {
|
|
5660
|
+
const dmkErr = result.error;
|
|
5661
|
+
const errorCode = "errorCode" in dmkErr && dmkErr.errorCode != null ? String(dmkErr.errorCode) : "";
|
|
5662
|
+
const message = "message" in dmkErr && typeof dmkErr.message === "string" ? dmkErr.message : "";
|
|
5663
|
+
debugLog(
|
|
5664
|
+
"[AppManager] openApp failed:",
|
|
5665
|
+
appName,
|
|
5666
|
+
"errorCode:",
|
|
5667
|
+
errorCode,
|
|
5668
|
+
"tag:",
|
|
5669
|
+
dmkErr._tag
|
|
5670
|
+
);
|
|
5671
|
+
let code;
|
|
5672
|
+
if (errorCode === "6807" || /unknown application/i.test(message)) {
|
|
5673
|
+
code = import_hwk_adapter_core13.HardwareErrorCode.AppNotInstalled;
|
|
5674
|
+
} else if (errorCode === "5501" || dmkErr._tag === "ActionRefusedError") {
|
|
5675
|
+
code = import_hwk_adapter_core13.HardwareErrorCode.UserRejected;
|
|
5676
|
+
}
|
|
5677
|
+
throw Object.assign(new Error(`Failed to open "${appName}"`), {
|
|
5678
|
+
_tag: ERROR_TAG.OpenAppCommand,
|
|
5679
|
+
code,
|
|
5680
|
+
errorCode,
|
|
5681
|
+
statusCode: errorCode,
|
|
5682
|
+
appName,
|
|
5683
|
+
originalError: dmkErr
|
|
5684
|
+
});
|
|
5685
|
+
}
|
|
5686
|
+
}
|
|
5687
|
+
async _closeCurrentApp(sessionId) {
|
|
5688
|
+
debugLog("[AppManager] closeCurrentApp");
|
|
5689
|
+
await this._dmk.sendCommand({
|
|
5690
|
+
sessionId,
|
|
5691
|
+
command: new import_device_management_kit3.CloseAppCommand()
|
|
5692
|
+
});
|
|
5693
|
+
}
|
|
5694
|
+
/**
|
|
5695
|
+
* Poll the device until the expected app is reported as running,
|
|
5696
|
+
* or throw after `_maxRetries` attempts.
|
|
5697
|
+
*/
|
|
5698
|
+
async _waitForApp(sessionId, expectedAppName) {
|
|
5699
|
+
let lastSeen = "";
|
|
5700
|
+
for (let i = 0; i < this._maxRetries; i++) {
|
|
5701
|
+
await this._wait();
|
|
5702
|
+
const current = await this._getCurrentApp(sessionId);
|
|
5703
|
+
lastSeen = current;
|
|
5704
|
+
if (current === expectedAppName) {
|
|
5705
|
+
return;
|
|
5706
|
+
}
|
|
5707
|
+
}
|
|
5708
|
+
debugLog(
|
|
5709
|
+
"[AppManager] waitForApp exhausted: expected=",
|
|
5710
|
+
expectedAppName,
|
|
5711
|
+
"lastSeen=",
|
|
5712
|
+
lastSeen
|
|
5713
|
+
);
|
|
5714
|
+
throw new Error(
|
|
5715
|
+
`Ledger: failed to open "${expectedAppName}" after ${this._maxRetries} retries (last seen: ${lastSeen})`
|
|
5716
|
+
);
|
|
5717
|
+
}
|
|
5718
|
+
_isDashboard(appName) {
|
|
5719
|
+
return appName === DASHBOARD_APP_NAME;
|
|
5720
|
+
}
|
|
5721
|
+
_wait() {
|
|
5722
|
+
return new Promise((resolve) => setTimeout(resolve, this._waitMs));
|
|
5723
|
+
}
|
|
5724
|
+
};
|
|
4953
5725
|
// Annotate the CommonJS export names for ESM import in node:
|
|
4954
5726
|
0 && (module.exports = {
|
|
4955
5727
|
AppManager,
|
|
@@ -4961,6 +5733,7 @@ var LedgerConnectorBase = class {
|
|
|
4961
5733
|
SignerEth,
|
|
4962
5734
|
SignerManager,
|
|
4963
5735
|
SignerSol,
|
|
5736
|
+
SignerZcash,
|
|
4964
5737
|
debugLog,
|
|
4965
5738
|
deviceActionToPromise,
|
|
4966
5739
|
isDeviceLockedError,
|