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

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 CHANGED
@@ -102,6 +102,8 @@ declare class LedgerAdapter implements IHardwareWallet {
102
102
  private readonly _operations;
103
103
  private _discoveredDevices;
104
104
  private _sessions;
105
+ private readonly _pendingOperationBindings;
106
+ private readonly _pendingPickBindings;
105
107
  private readonly _sessionTransports;
106
108
  private readonly _selectedBleReconnectTargets;
107
109
  private readonly _uiRegistry;
@@ -119,6 +121,7 @@ declare class LedgerAdapter implements IHardwareWallet {
119
121
  private _pendingConnectorTeardowns;
120
122
  private _activeConnectionType;
121
123
  private _bindingSelectionRequestId;
124
+ private _discardPendingBinding;
122
125
  private _finishBleBinding;
123
126
  private _isBleConnection;
124
127
  constructor(connector: IConnector, options?: {
@@ -153,7 +156,7 @@ declare class LedgerAdapter implements IHardwareWallet {
153
156
  private _installProgressLastKey;
154
157
  private static _createDeviceBusyError;
155
158
  connectDevice(searchTargetId: string): Promise<Response<string>>;
156
- /** Ledger binds whichever BLE device the user picks; its wallet is not checked. */
159
+ /** Binds the picked BLE device; with a recorded wallet identity it must match first. */
157
160
  bindBleDevice(params: BindBleDeviceParams): Promise<Response<string>>;
158
161
  acquireOperation(connectId: string, context: IHardwareConnectionContext): Promise<Response<string>>;
159
162
  private _createOperation;
@@ -213,7 +216,7 @@ declare class LedgerAdapter implements IHardwareWallet {
213
216
  cancel(connectId?: string): void;
214
217
  /** A non-empty deviceId is an expected chain fingerprint, not a transport identifier. */
215
218
  getChainFingerprint(connectId: string, deviceId: string, chain: ChainForFingerprint): Promise<Response<string>>;
216
- /** Ledger takes the selected BLE endpoint as the device and binds it right away. */
219
+ /** Saves a picked BLE endpoint; `identity` is set when its wallet was just checked. */
217
220
  private _saveSelectedBleBinding;
218
221
  private _dropSession;
219
222
  /** Verify on the acquired session without re-entering the job queue. */
package/dist/index.d.ts CHANGED
@@ -102,6 +102,8 @@ declare class LedgerAdapter implements IHardwareWallet {
102
102
  private readonly _operations;
103
103
  private _discoveredDevices;
104
104
  private _sessions;
105
+ private readonly _pendingOperationBindings;
106
+ private readonly _pendingPickBindings;
105
107
  private readonly _sessionTransports;
106
108
  private readonly _selectedBleReconnectTargets;
107
109
  private readonly _uiRegistry;
@@ -119,6 +121,7 @@ declare class LedgerAdapter implements IHardwareWallet {
119
121
  private _pendingConnectorTeardowns;
120
122
  private _activeConnectionType;
121
123
  private _bindingSelectionRequestId;
124
+ private _discardPendingBinding;
122
125
  private _finishBleBinding;
123
126
  private _isBleConnection;
124
127
  constructor(connector: IConnector, options?: {
@@ -153,7 +156,7 @@ declare class LedgerAdapter implements IHardwareWallet {
153
156
  private _installProgressLastKey;
154
157
  private static _createDeviceBusyError;
155
158
  connectDevice(searchTargetId: string): Promise<Response<string>>;
156
- /** Ledger binds whichever BLE device the user picks; its wallet is not checked. */
159
+ /** Binds the picked BLE device; with a recorded wallet identity it must match first. */
157
160
  bindBleDevice(params: BindBleDeviceParams): Promise<Response<string>>;
158
161
  acquireOperation(connectId: string, context: IHardwareConnectionContext): Promise<Response<string>>;
159
162
  private _createOperation;
@@ -213,7 +216,7 @@ declare class LedgerAdapter implements IHardwareWallet {
213
216
  cancel(connectId?: string): void;
214
217
  /** A non-empty deviceId is an expected chain fingerprint, not a transport identifier. */
215
218
  getChainFingerprint(connectId: string, deviceId: string, chain: ChainForFingerprint): Promise<Response<string>>;
216
- /** Ledger takes the selected BLE endpoint as the device and binds it right away. */
219
+ /** Saves a picked BLE endpoint; `identity` is set when its wallet was just checked. */
217
220
  private _saveSelectedBleBinding;
218
221
  private _dropSession;
219
222
  /** Verify on the acquired session without re-entering the job queue. */
package/dist/index.js CHANGED
@@ -892,6 +892,8 @@ var _LedgerAdapter = class _LedgerAdapter {
892
892
  this._operations = new import_hwk_adapter_core3.OperationRegistry({
893
893
  vendor: "ledger",
894
894
  onEnded: (operation, reason) => {
895
+ this._discardPendingBinding(this._pendingOperationBindings.get(operation.operationId));
896
+ this._pendingOperationBindings.delete(operation.operationId);
895
897
  this.emitter.emit(import_hwk_adapter_core3.SDK.OPERATION_ENDED, {
896
898
  type: import_hwk_adapter_core3.SDK.OPERATION_ENDED,
897
899
  payload: { operationId: operation.operationId, reason }
@@ -903,6 +905,11 @@ var _LedgerAdapter = class _LedgerAdapter {
903
905
  });
904
906
  this._discoveredDevices = /* @__PURE__ */ new Map();
905
907
  this._sessions = /* @__PURE__ */ new Map();
908
+ // An operation's BLE pick is saved by its first call that gets past the wallet check.
909
+ this._pendingOperationBindings = /* @__PURE__ */ new Map();
910
+ // A one-shot call's unchecked BLE pick, by connectId. It outlives the call only
911
+ // through an unlock retry; any other failure drops it with its session.
912
+ this._pendingPickBindings = /* @__PURE__ */ new Map();
906
913
  // Transport each session was opened on, by sessionId. The adapter-wide
907
914
  // `_activeConnectionType` moves with every discovery and can go stale.
908
915
  this._sessionTransports = /* @__PURE__ */ new Map();
@@ -1031,6 +1038,12 @@ var _LedgerAdapter = class _LedgerAdapter {
1031
1038
  this._jobQueue = new import_hwk_adapter_core3.DeviceJobQueue();
1032
1039
  this.registerEventListeners();
1033
1040
  }
1041
+ _discardPendingBinding(owner, status = "cancelled") {
1042
+ const pending = owner?.pendingBinding;
1043
+ if (!owner || !pending) return;
1044
+ owner.pendingBinding = void 0;
1045
+ if (pending.requestId === this._bindingSelectionRequestId) this._finishBleBinding(status);
1046
+ }
1034
1047
  _finishBleBinding(status) {
1035
1048
  const selectionRequestId = this._bindingSelectionRequestId;
1036
1049
  this._bindingSelectionRequestId = void 0;
@@ -1081,6 +1094,8 @@ var _LedgerAdapter = class _LedgerAdapter {
1081
1094
  this._discoveredDevices.clear();
1082
1095
  this._sessions.clear();
1083
1096
  this._sessionTransports.clear();
1097
+ this._pendingOperationBindings.clear();
1098
+ this._pendingPickBindings.clear();
1084
1099
  this._selectedBleReconnectTargets.clear();
1085
1100
  this._connectingPromise = null;
1086
1101
  this._doConnectAbortController = null;
@@ -1225,8 +1240,9 @@ var _LedgerAdapter = class _LedgerAdapter {
1225
1240
  return this.errorToFailure(error);
1226
1241
  }
1227
1242
  }
1228
- /** Ledger binds whichever BLE device the user picks; its wallet is not checked. */
1243
+ /** Binds the picked BLE device; with a recorded wallet identity it must match first. */
1229
1244
  async bindBleDevice(params) {
1245
+ const identity = params.identity?.vendor === "ledger" ? params.identity : void 0;
1230
1246
  try {
1231
1247
  return await this._jobQueue.enqueue(
1232
1248
  "__ledger_bind_ble__",
@@ -1241,7 +1257,8 @@ var _LedgerAdapter = class _LedgerAdapter {
1241
1257
  await this._ensureDevicePermission(void 0, void 0, signal);
1242
1258
  const attempt = {
1243
1259
  extra: params.extra,
1244
- bindingReason: "manual-rebind"
1260
+ bindingReason: "manual-rebind",
1261
+ deferBindingSave: Boolean(identity)
1245
1262
  };
1246
1263
  try {
1247
1264
  const connectId = await this._connectFirstOrSelect(
@@ -1252,6 +1269,45 @@ var _LedgerAdapter = class _LedgerAdapter {
1252
1269
  attempt,
1253
1270
  signal
1254
1271
  );
1272
+ const pending = attempt.pendingBinding;
1273
+ if (identity && pending) {
1274
+ attempt.pendingBinding = void 0;
1275
+ const sessionId = this._sessions.get(connectId);
1276
+ if (!sessionId) {
1277
+ throw (0, import_hwk_adapter_core3.createHwkError)({
1278
+ code: import_hwk_adapter_core3.HardwareErrorCode.DeviceDisconnected,
1279
+ message: "Selected Ledger connection ended"
1280
+ });
1281
+ }
1282
+ const fingerprint = await this._computeChainFingerprint(
1283
+ identity.chain,
1284
+ (method, callParams) => this._runConnectorCall(
1285
+ connectId,
1286
+ method,
1287
+ callParams,
1288
+ signal,
1289
+ void 0,
1290
+ void 0,
1291
+ { autoInstallApp: true },
1292
+ { connection: { connectId, sessionId } }
1293
+ )
1294
+ );
1295
+ if (fingerprint !== identity.value) {
1296
+ await this._dropSession(connectId, signal);
1297
+ throw (0, import_hwk_adapter_core3.createHwkError)({
1298
+ code: import_hwk_adapter_core3.HardwareErrorCode.DeviceMismatch,
1299
+ message: formatDeviceMismatchError(identity.value, fingerprint)
1300
+ });
1301
+ }
1302
+ await this._saveSelectedBleBinding(
1303
+ connectId,
1304
+ pending.requestId,
1305
+ attempt,
1306
+ void 0,
1307
+ signal,
1308
+ identity
1309
+ );
1310
+ }
1255
1311
  if (!attempt.bindingSaved) {
1256
1312
  await this._dropSession(connectId, signal);
1257
1313
  throw (0, import_hwk_adapter_core3.createHwkError)({
@@ -1289,7 +1345,9 @@ var _LedgerAdapter = class _LedgerAdapter {
1289
1345
  await this._ensureDevicePermission(target, void 0, signal);
1290
1346
  const attempt = {
1291
1347
  ...context,
1292
- extra: context.extra ? { ...context.extra } : void 0
1348
+ extra: context.extra ? { ...context.extra } : void 0,
1349
+ // The operation's calls decide whether the pick is the right wallet.
1350
+ deferBindingSave: true
1293
1351
  };
1294
1352
  try {
1295
1353
  const resolvedConnectId = await this.ensureConnected(
@@ -1300,7 +1358,11 @@ var _LedgerAdapter = class _LedgerAdapter {
1300
1358
  attempt
1301
1359
  );
1302
1360
  _LedgerAdapter._throwIfAborted(signal);
1303
- return this._createOperation(connectId, resolvedConnectId);
1361
+ const result = this._createOperation(connectId, resolvedConnectId);
1362
+ if (result.success && attempt.pendingBinding) {
1363
+ this._pendingOperationBindings.set(result.payload, attempt);
1364
+ }
1365
+ return result;
1304
1366
  } catch (error) {
1305
1367
  this._finishBleBinding(signal.aborted ? "cancelled" : "failed");
1306
1368
  throw error;
@@ -1915,6 +1977,8 @@ var _LedgerAdapter = class _LedgerAdapter {
1915
1977
  this._uiRegistry.cancel(void 0, void 0, pendingOperationId);
1916
1978
  }
1917
1979
  this._finishBleBinding("cancelled");
1980
+ if (!connectId) this._pendingOperationBindings.clear();
1981
+ else if (pendingOperationId) this._pendingOperationBindings.delete(pendingOperationId);
1918
1982
  const queueKeys = /* @__PURE__ */ new Set();
1919
1983
  if (connectId) queueKeys.add(ledgerQueueKey({ connectId }));
1920
1984
  if (pendingOperationId) queueKeys.add(ledgerQueueKey({ operationId: pendingOperationId }));
@@ -1966,8 +2030,8 @@ var _LedgerAdapter = class _LedgerAdapter {
1966
2030
  return this.errorToFailure(err);
1967
2031
  }
1968
2032
  }
1969
- /** Ledger takes the selected BLE endpoint as the device and binds it right away. */
1970
- async _saveSelectedBleBinding(connectId, selectionRequestId, context, operationId, signal) {
2033
+ /** Saves a picked BLE endpoint; `identity` is set when its wallet was just checked. */
2034
+ async _saveSelectedBleBinding(connectId, selectionRequestId, context, operationId, signal, identity) {
1971
2035
  try {
1972
2036
  const outcome = await (0, import_hwk_adapter_core3.requestSaveDeviceBinding)(
1973
2037
  this.emitter,
@@ -1975,6 +2039,7 @@ var _LedgerAdapter = class _LedgerAdapter {
1975
2039
  {
1976
2040
  selectionRequestId,
1977
2041
  connection: { transport: "ble", connectId },
2042
+ ...identity ? { identity } : {},
1978
2043
  extra: context?.extra,
1979
2044
  operationId
1980
2045
  },
@@ -2428,13 +2493,17 @@ var _LedgerAdapter = class _LedgerAdapter {
2428
2493
  signal
2429
2494
  );
2430
2495
  if (context) context.selectedConnection = { connectId: connected, requestId };
2431
- await this._saveSelectedBleBinding(
2432
- connected,
2433
- requestId,
2434
- context,
2435
- preserveOperationId,
2436
- signal
2437
- );
2496
+ if (context?.deferBindingSave) {
2497
+ context.pendingBinding = { connectId: connected, requestId };
2498
+ } else {
2499
+ await this._saveSelectedBleBinding(
2500
+ connected,
2501
+ requestId,
2502
+ context,
2503
+ preserveOperationId,
2504
+ signal
2505
+ );
2506
+ }
2438
2507
  return connected;
2439
2508
  }
2440
2509
  if (devices.length > 0 && (devices.length > 1 || requiresBleSelection)) {
@@ -2630,7 +2699,13 @@ var _LedgerAdapter = class _LedgerAdapter {
2630
2699
  operationId
2631
2700
  );
2632
2701
  } catch (error) {
2633
- this._finishBleBinding(signal.aborted ? "cancelled" : "failed");
2702
+ const status = signal.aborted ? "cancelled" : "failed";
2703
+ for (const [pickedConnectId, owner] of this._pendingPickBindings) {
2704
+ this._pendingPickBindings.delete(pickedConnectId);
2705
+ this._discardPendingBinding(owner, status);
2706
+ await this._dropSession(pickedConnectId, signal);
2707
+ }
2708
+ this._finishBleBinding(status);
2634
2709
  throw error;
2635
2710
  } finally {
2636
2711
  if (operationId) {
@@ -2737,7 +2812,13 @@ var _LedgerAdapter = class _LedgerAdapter {
2737
2812
  );
2738
2813
  const inputConnectId = hintedConnectId && hintedConnectId.transport !== "qr" ? hintedConnectId.connectId : connectId;
2739
2814
  const preferredConnectId = fingerprint && !fingerprint.skipFingerprint && selectedBleTarget?.chain === fingerprint.chain && selectedBleTarget.fingerprint === fingerprint.deviceId ? selectedBleTarget.connectId : inputConnectId;
2740
- const connectionAttempt = { ...commonParams };
2815
+ const checksWallet = Boolean(
2816
+ fingerprint && !fingerprint.skipFingerprint && fingerprint.deviceId
2817
+ );
2818
+ const connectionAttempt = {
2819
+ ...commonParams,
2820
+ deferBindingSave: checksWallet
2821
+ };
2741
2822
  const bundleConnection = installContext?.connection;
2742
2823
  if (bundleConnection && this._sessions.get(bundleConnection.connectId) !== bundleConnection.sessionId) {
2743
2824
  throw (0, import_hwk_adapter_core3.createHwkError)({
@@ -2771,7 +2852,12 @@ var _LedgerAdapter = class _LedgerAdapter {
2771
2852
  }
2772
2853
  try {
2773
2854
  const isBleSession = this._sessionTransports.get(sessionId) === "ble";
2774
- if (fingerprint && !fingerprint.skipFingerprint && fingerprint.deviceId && !isBleSession) {
2855
+ if (!operationId && connectionAttempt.pendingBinding) {
2856
+ this._pendingPickBindings.set(resolvedConnectId, connectionAttempt);
2857
+ }
2858
+ const bindingOwner = operationId ? this._pendingOperationBindings.get(operationId) : this._pendingPickBindings.get(resolvedConnectId);
2859
+ const pendingBinding = bindingOwner?.pendingBinding?.connectId === resolvedConnectId ? bindingOwner.pendingBinding : void 0;
2860
+ if (fingerprint && checksWallet) {
2775
2861
  const fp = await this._abortable(
2776
2862
  signal,
2777
2863
  this._verifyDeviceFingerprintWithSession(
@@ -2781,11 +2867,38 @@ var _LedgerAdapter = class _LedgerAdapter {
2781
2867
  )
2782
2868
  );
2783
2869
  if (!fp.success) {
2870
+ if (pendingBinding) {
2871
+ this._discardPendingBinding(bindingOwner, "failed");
2872
+ this._pendingPickBindings.delete(resolvedConnectId);
2873
+ if (operationId) {
2874
+ this._pendingOperationBindings.delete(operationId);
2875
+ this._operations.end(operationId, "explicit");
2876
+ await this._dropSession(resolvedConnectId, signal);
2877
+ }
2878
+ }
2784
2879
  throw Object.assign(new Error(formatDeviceMismatchError(fp.expected, fp.actual)), {
2785
2880
  code: import_hwk_adapter_core3.HardwareErrorCode.DeviceMismatch
2786
2881
  });
2787
2882
  }
2788
2883
  }
2884
+ if (pendingBinding && bindingOwner) {
2885
+ bindingOwner.pendingBinding = void 0;
2886
+ if (operationId) this._pendingOperationBindings.delete(operationId);
2887
+ else this._pendingPickBindings.delete(resolvedConnectId);
2888
+ await this._saveSelectedBleBinding(
2889
+ pendingBinding.connectId,
2890
+ pendingBinding.requestId,
2891
+ bindingOwner,
2892
+ operationId,
2893
+ signal,
2894
+ fingerprint && checksWallet ? {
2895
+ vendor: "ledger",
2896
+ type: "chainFingerprint",
2897
+ chain: fingerprint.chain,
2898
+ value: fingerprint.deviceId
2899
+ } : void 0
2900
+ );
2901
+ }
2789
2902
  if (fingerprint && !fingerprint.skipFingerprint && fingerprint.deviceId && !operationId && isBleSession && connectionAttempt.selectedConnection?.connectId === resolvedConnectId) {
2790
2903
  this._selectedBleReconnectTargets.set(connectId, {
2791
2904
  connectId: resolvedConnectId,
@@ -2874,6 +2987,7 @@ var _LedgerAdapter = class _LedgerAdapter {
2874
2987
  }
2875
2988
  if (!operationId && errObj?.code === import_hwk_adapter_core3.HardwareErrorCode.DeviceMismatch) {
2876
2989
  this._sessions.delete(resolvedConnectId);
2990
+ this._sessionTransports.delete(sessionId);
2877
2991
  this._discoveredDevices.delete(resolvedConnectId);
2878
2992
  await this.connector.disconnect(sessionId).catch(() => void 0);
2879
2993
  throw err;