@onekeyfe/hwk-ledger-adapter 1.2.5-alpha.2 → 1.2.5-alpha.4

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.mjs CHANGED
@@ -874,11 +874,6 @@ var _LedgerAdapter = class _LedgerAdapter {
874
874
  this._operations = new OperationRegistry({
875
875
  vendor: "ledger",
876
876
  onEnded: (operation, reason) => {
877
- const binding = this._pendingOperationBindings.get(operation.operationId);
878
- if (binding?.selectedConnection?.requestId === this._bindingSelectionRequestId) {
879
- this._finishBleBinding("cancelled");
880
- }
881
- this._pendingOperationBindings.delete(operation.operationId);
882
877
  this.emitter.emit(SDK.OPERATION_ENDED, {
883
878
  type: SDK.OPERATION_ENDED,
884
879
  payload: { operationId: operation.operationId, reason }
@@ -890,8 +885,10 @@ var _LedgerAdapter = class _LedgerAdapter {
890
885
  });
891
886
  this._discoveredDevices = /* @__PURE__ */ new Map();
892
887
  this._sessions = /* @__PURE__ */ new Map();
893
- this._pendingOperationBindings = /* @__PURE__ */ new Map();
894
- this._verifiedBleReconnectTargets = /* @__PURE__ */ new Map();
888
+ // Transport each session was opened on, by sessionId. The adapter-wide
889
+ // `_activeConnectionType` moves with every discovery and can go stale.
890
+ this._sessionTransports = /* @__PURE__ */ new Map();
891
+ this._selectedBleReconnectTargets = /* @__PURE__ */ new Map();
895
892
  this._uiRegistry = new UiRequestRegistry();
896
893
  // BTC App rejects account index >= 100 unless display=true. Cached per
897
894
  // adapter instance: first 100+ path asks the user once via UI request,
@@ -1065,7 +1062,8 @@ var _LedgerAdapter = class _LedgerAdapter {
1065
1062
  this._doConnectAbortController?.abort();
1066
1063
  this._discoveredDevices.clear();
1067
1064
  this._sessions.clear();
1068
- this._verifiedBleReconnectTargets.clear();
1065
+ this._sessionTransports.clear();
1066
+ this._selectedBleReconnectTargets.clear();
1069
1067
  this._connectingPromise = null;
1070
1068
  this._doConnectAbortController = null;
1071
1069
  this._uiRegistry.reset();
@@ -1175,6 +1173,7 @@ var _LedgerAdapter = class _LedgerAdapter {
1175
1173
  if (this._sessions.size === 0) return;
1176
1174
  const stale = [...this._sessions.values()];
1177
1175
  this._sessions.clear();
1176
+ for (const sid of stale) this._sessionTransports.delete(sid);
1178
1177
  await this._runConnectorTeardown(async () => {
1179
1178
  for (const sid of stale) {
1180
1179
  try {
@@ -1208,14 +1207,11 @@ var _LedgerAdapter = class _LedgerAdapter {
1208
1207
  return this.errorToFailure(error);
1209
1208
  }
1210
1209
  }
1210
+ /** Ledger binds whichever BLE device the user picks; its wallet is not checked. */
1211
1211
  async bindBleDevice(params) {
1212
- if (params.identity.vendor !== "ledger" || !params.identity.value) {
1213
- return failure2(HardwareErrorCode3.InvalidParams, "Ledger wallet identity is required");
1214
- }
1215
- const { chain, value: expectedFingerprint } = params.identity;
1216
1212
  try {
1217
1213
  return await this._jobQueue.enqueue(
1218
- expectedFingerprint,
1214
+ "__ledger_bind_ble__",
1219
1215
  async (signal) => {
1220
1216
  if (!this.getAvailableTransports().includes("ble")) {
1221
1217
  throw createHwkError({
@@ -1230,73 +1226,23 @@ var _LedgerAdapter = class _LedgerAdapter {
1230
1226
  bindingReason: "manual-rebind"
1231
1227
  };
1232
1228
  try {
1233
- for (; ; ) {
1234
- const connectId = await this._connectFirstOrSelect(
1235
- [],
1236
- void 0,
1237
- true,
1238
- void 0,
1239
- attempt,
1240
- signal
1241
- );
1242
- const sessionId = this._sessions.get(connectId);
1243
- if (!sessionId) {
1244
- throw createHwkError({
1245
- code: HardwareErrorCode3.DeviceDisconnected,
1246
- message: "Selected Ledger connection ended"
1247
- });
1248
- }
1249
- let saved = false;
1250
- try {
1251
- const installContext = {
1252
- connection: { connectId, sessionId }
1253
- };
1254
- const fingerprint = await this._computeChainFingerprint(
1255
- chain,
1256
- (method, callParams) => this._runConnectorCall(
1257
- connectId,
1258
- method,
1259
- callParams,
1260
- signal,
1261
- void 0,
1262
- void 0,
1263
- { autoInstallApp: true },
1264
- installContext
1265
- )
1266
- );
1267
- if (fingerprint !== expectedFingerprint) {
1268
- attempt.rejectedConnectIds ?? (attempt.rejectedConnectIds = /* @__PURE__ */ new Set());
1269
- attempt.rejectedConnectIds.add(connectId);
1270
- attempt.rejectedConnectId = connectId;
1271
- } else {
1272
- const persisted = await this._publishVerifiedBleBinding(
1273
- connectId,
1274
- chain,
1275
- fingerprint,
1276
- attempt,
1277
- void 0,
1278
- signal
1279
- );
1280
- if (!persisted) {
1281
- throw createHwkError({
1282
- code: HardwareErrorCode3.UnknownError,
1283
- message: "Bluetooth binding could not be saved",
1284
- origin: "host"
1285
- });
1286
- }
1287
- saved = true;
1288
- return success(connectId);
1289
- }
1290
- } finally {
1291
- if (!saved && this._sessions.get(connectId) === sessionId) {
1292
- this._sessions.delete(connectId);
1293
- const teardown = this._runConnectorTeardown(
1294
- () => this.connector.disconnect(sessionId)
1295
- ).catch(() => void 0);
1296
- if (!signal.aborted) await teardown;
1297
- }
1298
- }
1229
+ const connectId = await this._connectFirstOrSelect(
1230
+ [],
1231
+ void 0,
1232
+ true,
1233
+ void 0,
1234
+ attempt,
1235
+ signal
1236
+ );
1237
+ if (!attempt.bindingSaved) {
1238
+ await this._dropSession(connectId, signal);
1239
+ throw createHwkError({
1240
+ code: HardwareErrorCode3.UnknownError,
1241
+ message: "Bluetooth binding could not be saved",
1242
+ origin: "host"
1243
+ });
1299
1244
  }
1245
+ return success(connectId);
1300
1246
  } catch (error) {
1301
1247
  this._finishBleBinding(signal.aborted ? "cancelled" : "failed");
1302
1248
  throw error;
@@ -1336,11 +1282,7 @@ var _LedgerAdapter = class _LedgerAdapter {
1336
1282
  attempt
1337
1283
  );
1338
1284
  _LedgerAdapter._throwIfAborted(signal);
1339
- const result = this._createOperation(connectId, resolvedConnectId);
1340
- if (result.success && attempt.selectedConnection) {
1341
- this._pendingOperationBindings.set(result.payload, attempt);
1342
- }
1343
- return result;
1285
+ return this._createOperation(connectId, resolvedConnectId);
1344
1286
  } catch (error) {
1345
1287
  this._finishBleBinding(signal.aborted ? "cancelled" : "failed");
1346
1288
  throw error;
@@ -1360,7 +1302,7 @@ var _LedgerAdapter = class _LedgerAdapter {
1360
1302
  this._operations.endByConnectionKey(resolvedConnectId, "explicit");
1361
1303
  const sessionId = this._sessions.get(resolvedConnectId);
1362
1304
  if (sessionId) this._operations.endByConnectionKey(sessionId, "explicit");
1363
- const connectionType = this._isBleConnection() ? "ble" : "usb";
1305
+ const connectionType = sessionId && this._sessionTransports.get(sessionId) || (this._isBleConnection() ? "ble" : "usb");
1364
1306
  const device = this._discoveredDevices.get(resolvedConnectId) ?? {
1365
1307
  vendor: "ledger",
1366
1308
  model: "unknown",
@@ -1426,6 +1368,7 @@ var _LedgerAdapter = class _LedgerAdapter {
1426
1368
  }
1427
1369
  const resolvedConnectId = session.deviceInfo?.connectId || connectId;
1428
1370
  this._sessions.set(resolvedConnectId, session.sessionId);
1371
+ this._sessionTransports.set(session.sessionId, this._isBleConnection() ? "ble" : "usb");
1429
1372
  if (session.deviceInfo) {
1430
1373
  this._discoveredDevices.set(resolvedConnectId, session.deviceInfo);
1431
1374
  }
@@ -1954,10 +1897,6 @@ var _LedgerAdapter = class _LedgerAdapter {
1954
1897
  this._uiRegistry.cancel(void 0, void 0, pendingOperationId);
1955
1898
  }
1956
1899
  this._finishBleBinding("cancelled");
1957
- if (!connectId) this._pendingOperationBindings.clear();
1958
- else if (pendingOperationId) {
1959
- this._pendingOperationBindings.delete(pendingOperationId);
1960
- }
1961
1900
  const queueKeys = /* @__PURE__ */ new Set();
1962
1901
  if (connectId) queueKeys.add(ledgerQueueKey({ connectId }));
1963
1902
  if (pendingOperationId) queueKeys.add(ledgerQueueKey({ operationId: pendingOperationId }));
@@ -2002,16 +1941,6 @@ var _LedgerAdapter = class _LedgerAdapter {
2002
1941
  formatDeviceMismatchError(deviceId, fingerprint)
2003
1942
  );
2004
1943
  }
2005
- if (isHardwareOperationId(connectId)) {
2006
- const operation = this._operations.resolve(connectId);
2007
- await this._publishVerifiedBleBinding(
2008
- operation.connectId,
2009
- chain,
2010
- fingerprint,
2011
- void 0,
2012
- connectId
2013
- );
2014
- }
2015
1944
  }
2016
1945
  return success(fingerprint);
2017
1946
  } catch (err) {
@@ -2019,56 +1948,46 @@ var _LedgerAdapter = class _LedgerAdapter {
2019
1948
  return this.errorToFailure(err);
2020
1949
  }
2021
1950
  }
2022
- /** Discovery may select a BLE target before a later call verifies its wallet. */
2023
- async _publishVerifiedBleBinding(connectId, chain, fingerprint, attempt, operationId, signal) {
2024
- const binding = operationId ? this._pendingOperationBindings.get(operationId) : attempt;
2025
- if (!this._isBleConnection() || binding?.selectedConnection?.connectId !== connectId) {
2026
- return false;
2027
- }
1951
+ /** Ledger takes the selected BLE endpoint as the device and binds it right away. */
1952
+ async _saveSelectedBleBinding(connectId, selectionRequestId, context, operationId, signal) {
2028
1953
  try {
2029
1954
  const outcome = await requestSaveDeviceBinding(
2030
1955
  this.emitter,
2031
1956
  this._uiRegistry,
2032
1957
  {
2033
- selectionRequestId: binding.selectedConnection.requestId,
1958
+ selectionRequestId,
2034
1959
  connection: { transport: "ble", connectId },
2035
- identity: { vendor: "ledger", type: "chainFingerprint", chain, value: fingerprint },
2036
- extra: binding.extra,
1960
+ extra: context?.extra,
2037
1961
  operationId
2038
1962
  },
2039
1963
  signal
2040
1964
  );
2041
- if (operationId) this._operations.resolve(operationId);
1965
+ if (context) context.bindingSaved = outcome.saved;
2042
1966
  if (!outcome.saved) {
2043
1967
  debugLog("[LedgerAdapter] BLE binding not persisted by host", {
2044
1968
  connectId,
2045
1969
  reason: outcome.reason
2046
1970
  });
2047
1971
  }
2048
- return outcome.saved;
2049
1972
  } catch (error) {
2050
- if (operationId) {
2051
- this._pendingOperationBindings.delete(operationId);
2052
- this._operations.end(operationId, "explicit");
2053
- }
2054
- const sessionId = this._sessions.get(connectId);
2055
- this._sessions.delete(connectId);
2056
- if (sessionId) {
2057
- const teardown = this._runConnectorTeardown(
2058
- () => this.connector.disconnect(sessionId)
2059
- ).catch(() => void 0);
2060
- if (!signal?.aborted) await teardown;
2061
- }
1973
+ await this._dropSession(connectId, signal);
2062
1974
  throw error;
2063
1975
  } finally {
2064
- if (this._bindingSelectionRequestId === binding.selectedConnection.requestId) {
1976
+ if (this._bindingSelectionRequestId === selectionRequestId) {
2065
1977
  this._bindingSelectionRequestId = void 0;
2066
1978
  }
2067
- if (operationId) {
2068
- this._pendingOperationBindings.delete(operationId);
2069
- }
2070
1979
  }
2071
1980
  }
1981
+ async _dropSession(connectId, signal) {
1982
+ const sessionId = this._sessions.get(connectId);
1983
+ this._sessions.delete(connectId);
1984
+ if (!sessionId) return;
1985
+ this._sessionTransports.delete(sessionId);
1986
+ const teardown = this._runConnectorTeardown(() => this.connector.disconnect(sessionId)).catch(
1987
+ () => void 0
1988
+ );
1989
+ if (!signal.aborted) await teardown;
1990
+ }
2072
1991
  /** Verify on the acquired session without re-entering the job queue. */
2073
1992
  async _verifyDeviceFingerprintWithSession(sessionId, deviceId, chain) {
2074
1993
  if (!deviceId) return { success: true };
@@ -2463,12 +2382,11 @@ var _LedgerAdapter = class _LedgerAdapter {
2463
2382
  ) ?? (usbDevices.length === 1 && usbDevices[0].connectionType === "usb" ? usbDevices[0] : void 0);
2464
2383
  if (candidate) return [candidate];
2465
2384
  }
2466
- return (await this._searchDevices({ transportType: "ble", waitForAllTransports: true }, signal)).filter((device2) => !context?.rejectedConnectIds?.has(device2.connectId));
2385
+ return this._searchDevices({ transportType: "ble", waitForAllTransports: true }, signal);
2467
2386
  },
2468
2387
  request: {
2469
- devices: devices.filter((device2) => !context?.rejectedConnectIds?.has(device2.connectId)),
2388
+ devices,
2470
2389
  bindingSessionId,
2471
- rejectedConnectId: context?.rejectedConnectId,
2472
2390
  context: {
2473
2391
  kind: "bind-connection",
2474
2392
  transport: "ble",
@@ -2481,14 +2399,25 @@ var _LedgerAdapter = class _LedgerAdapter {
2481
2399
  if (device.connectionType === "usb") {
2482
2400
  this._activeConnectionType = "usb";
2483
2401
  if (context) context.selectedConnection = void 0;
2484
- if (preserveOperationId) this._pendingOperationBindings.delete(preserveOperationId);
2485
2402
  this._bindingSelectionRequestId = requestId;
2486
2403
  this._finishBleBinding("cancelled");
2487
2404
  return this._connectDeviceOrThrow(device.connectId, preserveOperationId, signal);
2488
2405
  }
2489
- if (context) context.selectedConnection = { connectId: device.connectId, requestId };
2490
2406
  this._bindingSelectionRequestId = requestId;
2491
- return this._connectDeviceOrThrow(device.connectId, preserveOperationId, signal);
2407
+ const connected = await this._connectDeviceOrThrow(
2408
+ device.connectId,
2409
+ preserveOperationId,
2410
+ signal
2411
+ );
2412
+ if (context) context.selectedConnection = { connectId: connected, requestId };
2413
+ await this._saveSelectedBleBinding(
2414
+ connected,
2415
+ requestId,
2416
+ context,
2417
+ preserveOperationId,
2418
+ signal
2419
+ );
2420
+ return connected;
2492
2421
  }
2493
2422
  if (devices.length > 0 && (devices.length > 1 || requiresBleSelection)) {
2494
2423
  if (context?.allowDeviceSelection === false || !this.emitter.listenerCount(UI_REQUEST.REQUEST_SELECT_DEVICE)) {
@@ -2783,13 +2712,13 @@ var _LedgerAdapter = class _LedgerAdapter {
2783
2712
  }
2784
2713
  const allowUsbEphemeralFallback = !!fingerprint?.deviceId && !fingerprint.skipFingerprint;
2785
2714
  let businessCallStarted = false;
2786
- const verifiedBleTarget = connectId ? this._verifiedBleReconnectTargets.get(connectId) : void 0;
2715
+ const selectedBleTarget = connectId ? this._selectedBleReconnectTargets.get(connectId) : void 0;
2787
2716
  const knownTransport = this._isBleConnection() ? "ble" : "usb";
2788
2717
  const hintedConnectId = commonParams?.knownConnections?.find(
2789
2718
  (connection) => connection.transport === knownTransport
2790
2719
  );
2791
2720
  const inputConnectId = hintedConnectId && hintedConnectId.transport !== "qr" ? hintedConnectId.connectId : connectId;
2792
- const preferredConnectId = fingerprint && !fingerprint.skipFingerprint && verifiedBleTarget?.chain === fingerprint.chain && verifiedBleTarget.fingerprint === fingerprint.deviceId ? verifiedBleTarget.connectId : inputConnectId;
2721
+ const preferredConnectId = fingerprint && !fingerprint.skipFingerprint && selectedBleTarget?.chain === fingerprint.chain && selectedBleTarget.fingerprint === fingerprint.deviceId ? selectedBleTarget.connectId : inputConnectId;
2793
2722
  const connectionAttempt = { ...commonParams };
2794
2723
  const bundleConnection = installContext?.connection;
2795
2724
  if (bundleConnection && this._sessions.get(bundleConnection.connectId) !== bundleConnection.sessionId) {
@@ -2798,14 +2727,14 @@ var _LedgerAdapter = class _LedgerAdapter {
2798
2727
  message: "Ledger all-network connection ended"
2799
2728
  });
2800
2729
  }
2801
- let resolvedConnectId = operationId ? this._operations.resolve(operationId).connectId : bundleConnection?.connectId ?? await this.ensureConnected(
2730
+ const resolvedConnectId = operationId ? this._operations.resolve(operationId).connectId : bundleConnection?.connectId ?? await this.ensureConnected(
2802
2731
  preferredConnectId,
2803
2732
  signal,
2804
2733
  allowUsbEphemeralFallback,
2805
2734
  void 0,
2806
2735
  connectionAttempt
2807
2736
  );
2808
- let sessionId = this._sessions.get(resolvedConnectId);
2737
+ const sessionId = this._sessions.get(resolvedConnectId);
2809
2738
  if (sessionId && installContext && !installContext.connection) {
2810
2739
  installContext.connection = { connectId: resolvedConnectId, sessionId };
2811
2740
  }
@@ -2823,74 +2752,29 @@ var _LedgerAdapter = class _LedgerAdapter {
2823
2752
  });
2824
2753
  }
2825
2754
  try {
2826
- if (fingerprint && !fingerprint.skipFingerprint && fingerprint.deviceId) {
2827
- for (; ; ) {
2828
- const fp = await this._abortable(
2829
- signal,
2830
- this._verifyDeviceFingerprintWithSession(
2831
- sessionId,
2832
- fingerprint.deviceId,
2833
- fingerprint.chain
2834
- )
2835
- );
2836
- if (fp.success) break;
2837
- const binding = operationId ? this._pendingOperationBindings.get(operationId) : connectionAttempt;
2838
- if (!this._isBleConnection() || binding?.selectedConnection?.connectId !== resolvedConnectId) {
2839
- throw Object.assign(new Error(formatDeviceMismatchError(fp.expected, fp.actual)), {
2840
- code: HardwareErrorCode3.DeviceMismatch
2841
- });
2842
- }
2843
- binding.rejectedConnectIds ?? (binding.rejectedConnectIds = /* @__PURE__ */ new Set());
2844
- binding.rejectedConnectIds.add(resolvedConnectId);
2845
- binding.rejectedConnectId = resolvedConnectId;
2846
- this._sessions.delete(resolvedConnectId);
2847
- await this.connector.disconnect(sessionId);
2848
- if (operationId) this._pendingOperationDisconnects.delete(operationId);
2849
- _LedgerAdapter._throwIfAborted(signal);
2850
- resolvedConnectId = await this._connectFirstOrSelect(
2851
- [],
2852
- void 0,
2853
- true,
2854
- operationId,
2855
- binding,
2856
- signal
2857
- );
2858
- const selectedSession = this._sessions.get(resolvedConnectId);
2859
- const selectedDevice = this._discoveredDevices.get(resolvedConnectId);
2860
- if (!selectedSession || !selectedDevice)
2861
- throw createHwkError({
2862
- code: HardwareErrorCode3.DeviceDisconnected,
2863
- message: "Selected Ledger connection ended"
2864
- });
2865
- sessionId = selectedSession;
2866
- if (operationId)
2867
- this._operations.rebind(operationId, {
2868
- connectId: resolvedConnectId,
2869
- device: selectedDevice,
2870
- // Same source as `_createOperation`: the selected transport, not
2871
- // the device snapshot a session connect overwrote.
2872
- connectionType: this._isBleConnection() ? "ble" : "usb",
2873
- connectionKeys: [sessionId]
2874
- });
2875
- if (installContext)
2876
- installContext.connection = { connectId: resolvedConnectId, sessionId };
2877
- }
2878
- await this._publishVerifiedBleBinding(
2879
- resolvedConnectId,
2880
- fingerprint.chain,
2881
- fingerprint.deviceId,
2882
- connectionAttempt,
2883
- operationId,
2884
- signal
2755
+ const isBleSession = this._sessionTransports.get(sessionId) === "ble";
2756
+ if (fingerprint && !fingerprint.skipFingerprint && fingerprint.deviceId && !isBleSession) {
2757
+ const fp = await this._abortable(
2758
+ signal,
2759
+ this._verifyDeviceFingerprintWithSession(
2760
+ sessionId,
2761
+ fingerprint.deviceId,
2762
+ fingerprint.chain
2763
+ )
2885
2764
  );
2886
- if (!operationId && connectionAttempt.selectedConnection?.connectId === resolvedConnectId && this._isBleConnection()) {
2887
- this._verifiedBleReconnectTargets.set(connectId, {
2888
- connectId: resolvedConnectId,
2889
- chain: fingerprint.chain,
2890
- fingerprint: fingerprint.deviceId
2765
+ if (!fp.success) {
2766
+ throw Object.assign(new Error(formatDeviceMismatchError(fp.expected, fp.actual)), {
2767
+ code: HardwareErrorCode3.DeviceMismatch
2891
2768
  });
2892
2769
  }
2893
2770
  }
2771
+ if (fingerprint && !fingerprint.skipFingerprint && fingerprint.deviceId && !operationId && isBleSession && connectionAttempt.selectedConnection?.connectId === resolvedConnectId) {
2772
+ this._selectedBleReconnectTargets.set(connectId, {
2773
+ connectId: resolvedConnectId,
2774
+ chain: fingerprint.chain,
2775
+ fingerprint: fingerprint.deviceId
2776
+ });
2777
+ }
2894
2778
  businessCallStarted = true;
2895
2779
  return await this._callConnector(sessionId, method, effectiveParams, signal);
2896
2780
  } catch (err) {