@onekeyfe/hwk-ledger-adapter 1.1.26-alpha.106 → 1.1.26-alpha.12

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
@@ -1,5 +1,5 @@
1
- import { IHardwareWallet, IConnector, TransportType, IUiHandler, UiResponseEvent, DeviceInfo, Response, ChainCapability, EvmGetAddressParams, EvmAddress, ProgressCallback, EvmSignTxParams, EvmSignedTx, EvmSignMsgParams, EvmSignature, EvmSignTypedDataParams, BtcGetAddressParams, BtcAddress, BtcGetPublicKeyParams, BtcPublicKey, BtcSignTxParams, BtcSignedTx, BtcSignPsbtParams, BtcSignedPsbt, BtcSignMsgParams, BtcSignature, SolGetAddressParams, SolAddress, SolSignTxParams, SolSignedTx, SolSignMsgParams, SolSignature, TronGetAddressParams, TronAddress, TronSignTxParams, TronSignedTx, TronSignMsgParams, TronSignature, HardwareEventMap, DeviceEventListener, ChainForFingerprint, ConnectionType, DeviceDescriptor, ConnectorDevice, ConnectorSession, ConnectorEventType, ConnectorEventMap, DeviceChangeEvent, HardwareErrorCode } from '@onekeyfe/hwk-adapter-core';
2
- import { DeviceManagementKit, DeviceActionState as DeviceActionState$1, DiscoveredDevice, ExecuteDeviceActionReturnType } from '@ledgerhq/device-management-kit';
1
+ import { IHardwareWallet, IConnector, TransportType, UiResponseEvent, DeviceInfo, Response, ChainCapability, EvmGetAddressParams, EvmAddress, EvmSignTxParams, EvmSignedTx, EvmSignMsgParams, EvmSignature, EvmSignTypedDataParams, BtcGetAddressParams, BtcAddress, BtcGetPublicKeyParams, BtcPublicKey, BtcSignTxParams, BtcSignedTx, BtcSignPsbtParams, BtcSignedPsbt, BtcSignMsgParams, BtcSignature, SolGetAddressParams, SolAddress, SolSignTxParams, SolSignedTx, SolSignMsgParams, SolSignature, TronGetAddressParams, TronAddress, TronSignTxParams, TronSignedTx, TronSignMsgParams, TronSignature, HardwareEventMap, DeviceEventListener, ChainForFingerprint, DeviceDescriptor, DeviceChangeEvent, ConnectionType, ConnectorDevice, ConnectorSession, ConnectorEventType, ConnectorEventMap, Failure, HardwareErrorCode } from '@onekeyfe/hwk-adapter-core';
2
+ import { DeviceActionState as DeviceActionState$1, DiscoveredDevice, ExecuteDeviceActionReturnType, DeviceManagementKit } from '@ledgerhq/device-management-kit';
3
3
  import { Address, Signature, SignerEth as SignerEth$1, TypedData } from '@ledgerhq/device-signer-kit-ethereum';
4
4
  import { SignerBtc as SignerBtc$1 } from '@ledgerhq/device-signer-kit-bitcoin';
5
5
  import { SignerSolana } from '@ledgerhq/device-signer-kit-solana';
@@ -13,30 +13,21 @@ interface LedgerAdapterOptions {
13
13
  */
14
14
  handleSelectDevice?: boolean;
15
15
  }
16
- /**
17
- * Ledger hardware wallet adapter that delegates to an IConnector.
18
- *
19
- * This is a thin translation layer that:
20
- * - Accepts a pre-configured IConnector (transport decisions are made at connector creation time)
21
- * - Translates IHardwareWallet method calls to connector.call() invocations
22
- * - Maps connector results/errors to our Response<T> format with enriched error messages
23
- * - Translates connector events to HardwareEventMap events
24
- * - Integrates with IUiHandler for permission flows
25
- */
26
16
  declare class LedgerAdapter implements IHardwareWallet {
27
17
  readonly vendor: "ledger";
28
18
  private readonly connector;
29
19
  private readonly emitter;
30
- private _uiHandler;
31
20
  private readonly _handleSelectDevice;
32
21
  private _discoveredDevices;
33
22
  private _sessions;
34
23
  private readonly _uiRegistry;
24
+ private _btcHighIndexConfirmedThisSession;
25
+ private readonly _jobQueue;
26
+ private _doConnectAbortController;
35
27
  constructor(connector: IConnector, options?: LedgerAdapterOptions);
36
28
  get activeTransport(): TransportType | null;
37
29
  getAvailableTransports(): TransportType[];
38
30
  switchTransport(_type: TransportType): Promise<void>;
39
- setUiHandler(handler: Partial<IUiHandler>): void;
40
31
  init(_config?: unknown): Promise<void>;
41
32
  /**
42
33
  * Clear cached device/session state without tearing down the adapter.
@@ -47,23 +38,20 @@ declare class LedgerAdapter implements IHardwareWallet {
47
38
  dispose(): Promise<void>;
48
39
  uiResponse(response: UiResponseEvent): void;
49
40
  searchDevices(): Promise<DeviceInfo[]>;
41
+ private static readonly MAX_BUSINESS_RETRY_BUDGET;
42
+ private static readonly MAX_DOCONNECT_CONFIRMS;
43
+ private _lastCancelReason;
44
+ private static _createDeviceBusyError;
50
45
  connectDevice(connectId: string): Promise<Response<string>>;
51
46
  disconnectDevice(connectId: string): Promise<void>;
52
47
  getDeviceInfo(connectId: string, deviceId: string): Promise<Response<DeviceInfo>>;
53
48
  getSupportedChains(): ChainCapability[];
54
49
  private callChain;
55
- /**
56
- * Batch version of callChain — checks permission once,
57
- * fingerprint is verified on the first call inside connectorCall.
58
- */
59
- private callChainBatch;
60
50
  evmGetAddress(connectId: string, deviceId: string, params: EvmGetAddressParams): Promise<Response<EvmAddress>>;
61
- evmGetAddresses(connectId: string, deviceId: string, params: EvmGetAddressParams[], onProgress?: ProgressCallback): Promise<Response<EvmAddress[]>>;
62
51
  evmSignTransaction(connectId: string, deviceId: string, params: EvmSignTxParams): Promise<Response<EvmSignedTx>>;
63
52
  evmSignMessage(connectId: string, deviceId: string, params: EvmSignMsgParams): Promise<Response<EvmSignature>>;
64
53
  evmSignTypedData(connectId: string, deviceId: string, params: EvmSignTypedDataParams): Promise<Response<EvmSignature>>;
65
54
  btcGetAddress(connectId: string, deviceId: string, params: BtcGetAddressParams): Promise<Response<BtcAddress>>;
66
- btcGetAddresses(connectId: string, deviceId: string, params: BtcGetAddressParams[], onProgress?: ProgressCallback): Promise<Response<BtcAddress[]>>;
67
55
  btcGetPublicKey(connectId: string, deviceId: string, params: BtcGetPublicKeyParams): Promise<Response<BtcPublicKey>>;
68
56
  btcSignTransaction(connectId: string, deviceId: string, params: BtcSignTxParams): Promise<Response<BtcSignedTx>>;
69
57
  btcSignPsbt(connectId: string, deviceId: string, params: BtcSignPsbtParams): Promise<Response<BtcSignedPsbt>>;
@@ -72,18 +60,16 @@ declare class LedgerAdapter implements IHardwareWallet {
72
60
  masterFingerprint: string;
73
61
  }>>;
74
62
  solGetAddress(connectId: string, deviceId: string, params: SolGetAddressParams): Promise<Response<SolAddress>>;
75
- solGetAddresses(connectId: string, deviceId: string, params: SolGetAddressParams[], onProgress?: ProgressCallback): Promise<Response<SolAddress[]>>;
76
63
  solSignTransaction(connectId: string, deviceId: string, params: SolSignTxParams): Promise<Response<SolSignedTx>>;
77
64
  solSignMessage(connectId: string, deviceId: string, params: SolSignMsgParams): Promise<Response<SolSignature>>;
78
65
  tronGetAddress(connectId: string, deviceId: string, params: TronGetAddressParams): Promise<Response<TronAddress>>;
79
- tronGetAddresses(connectId: string, deviceId: string, params: TronGetAddressParams[], onProgress?: ProgressCallback): Promise<Response<TronAddress[]>>;
80
66
  tronSignTransaction(connectId: string, deviceId: string, params: TronSignTxParams): Promise<Response<TronSignedTx>>;
81
67
  tronSignMessage(connectId: string, deviceId: string, params: TronSignMsgParams): Promise<Response<TronSignature>>;
82
68
  on<K extends keyof HardwareEventMap>(event: K, listener: (event: HardwareEventMap[K]) => void): void;
83
69
  on(event: string, listener: DeviceEventListener): void;
84
70
  off<K extends keyof HardwareEventMap>(event: K, listener: (event: HardwareEventMap[K]) => void): void;
85
71
  off(event: string, listener: DeviceEventListener): void;
86
- cancel(connectId: string): void;
72
+ cancel(connectId?: string): void;
87
73
  getChainFingerprint(connectId: string, deviceId: string, chain: ChainForFingerprint): Promise<Response<string>>;
88
74
  /**
89
75
  * Verify fingerprint using an existing sessionId directly.
@@ -112,12 +98,21 @@ declare class LedgerAdapter implements IHardwareWallet {
112
98
  * - If ANY session exists (Ledger IDs are ephemeral), reuse it.
113
99
  * - Otherwise: search → 1 device: auto-connect, multiple: ask user, 0: throw.
114
100
  */
115
- private static readonly MAX_DEVICE_RETRY;
116
101
  private _connectingPromise;
117
102
  private _waitForDeviceConnect;
103
+ /**
104
+ * Decide whether a BTC pubkey call needs `showOnDevice=true` because of
105
+ * the BTC App's account-index policy, asking the user once per session.
106
+ *
107
+ * Returns the params to pass through (with `showOnDevice` possibly
108
+ * promoted), or `null` if the user declined.
109
+ */
110
+ private _gateBtcHighIndex;
111
+ private _waitForBtcHighIndexConfirm;
118
112
  private ensureConnected;
119
113
  private _doConnect;
120
114
  private _connectFirstOrSelect;
115
+ private _connectDeviceOrThrow;
121
116
  private _chooseDeviceFromList;
122
117
  /**
123
118
  * Call the connector with automatic session resolution and disconnect retry.
@@ -128,13 +123,34 @@ declare class LedgerAdapter implements IHardwareWallet {
128
123
  * 4. On disconnect error: clears stale session, re-connects, retries once
129
124
  */
130
125
  private connectorCall;
131
- /** Clear stale session, reconnect, and retry the call. */
126
+ /**
127
+ * Race a promise against an abort signal. On abort, rejects with
128
+ * signal.reason → instance _lastCancelReason → generic Error('Aborted').
129
+ */
130
+ private _abortable;
131
+ /** Throw an AbortError if signal is already aborted. */
132
+ private static _throwIfAborted;
133
+ /** Actual work done under the job queue — connection, fingerprint, call, and recovery. */
134
+ private _runConnectorCall;
135
+ /**
136
+ * Clear stale session, reconnect, and retry the call.
137
+ *
138
+ * Timeout recovery starts with a full connector reset. After an APDU
139
+ * timeout, DMK/transport state may still emit malformed responses; retrying
140
+ * on the same DMK can poison the next chain switch.
141
+ */
132
142
  private _retryWithFreshConnection;
133
143
  /**
134
- * Ensure device permission before proceeding.
135
- * - No connectId (searchDevices): check environment-level permission
136
- * - With connectId (business methods): check device-level permission
137
- * If not granted, calls onDevicePermission so the consumer can request access.
144
+ * Ensure OS-level device permission (Bluetooth / USB) before proceeding.
145
+ *
146
+ * Emits `REQUEST_DEVICE_PERMISSION` and awaits the consumer's
147
+ * `RECEIVE_DEVICE_PERMISSION` reply (60s budget covers "probe system
148
+ * prompt → user tap" plus a generous margin). If the consumer never wires
149
+ * a handler or never replies, the wait times out and the operation fails
150
+ * fast so scanners/callers don't hang silently.
151
+ *
152
+ * - No connectId (searchDevices): environment-level permission
153
+ * - With connectId (business methods): device-level permission
138
154
  */
139
155
  private _ensureDevicePermission;
140
156
  /**
@@ -144,14 +160,107 @@ declare class LedgerAdapter implements IHardwareWallet {
144
160
  private errorToFailure;
145
161
  private deviceConnectHandler;
146
162
  private deviceDisconnectHandler;
147
- private uiRequestHandler;
148
- private uiEventHandler;
163
+ private uiEventForwarder;
149
164
  private registerEventListeners;
150
165
  private unregisterEventListeners;
151
- private handleUiEvent;
152
166
  private connectorDeviceToDeviceInfo;
153
- private extractDeviceInfoFromPayload;
154
- private unknownDevice;
167
+ }
168
+
169
+ /**
170
+ * Re-export DMK types under local aliases for backward compatibility.
171
+ * These replace the previous duck-typed interfaces with the real SDK types.
172
+ */
173
+ type DmkDiscoveredDevice = DiscoveredDevice;
174
+ /**
175
+ * DMK DeviceAction — the Observable-based return type of all DMK signer methods.
176
+ *
177
+ * This is a permissive alias for `ExecuteDeviceActionReturnType` that accepts
178
+ * any Error and IntermediateValue generics. Signer wrapper code only cares about
179
+ * the Output type and the observable/cancel shape.
180
+ */
181
+ type DeviceAction<T> = ExecuteDeviceActionReturnType<T, any, any>;
182
+ /**
183
+ * DMK DeviceActionState — re-exported with permissive Error/IntermediateValue.
184
+ * The real type is a discriminated union on `status`.
185
+ */
186
+ type DeviceActionState<T> = DeviceActionState$1<T, any, any>;
187
+ /** ETH address result — re-exported from DMK for backward compatibility. */
188
+ type SignerEvmAddress = Address;
189
+ /** ETH signature result — re-exported from DMK for backward compatibility. */
190
+ type SignerEvmSignature = Signature;
191
+ /** BTC address result (WalletAddress not exported from DMK top-level). */
192
+ interface SignerBtcAddress {
193
+ address: string;
194
+ }
195
+
196
+ /**
197
+ * Manages device discovery, connection, and session tracking.
198
+ * Wraps DMK's Observable APIs into simpler imperative calls.
199
+ */
200
+ declare class LedgerDeviceManager {
201
+ private readonly _dmk;
202
+ private readonly _discovered;
203
+ private readonly _sessions;
204
+ private readonly _sessionToDevice;
205
+ private _listenSub;
206
+ constructor(dmk: DeviceManagementKit);
207
+ /**
208
+ * One-shot enumeration: subscribe to listenToAvailableDevices,
209
+ * take the first emission, unsubscribe, return DeviceDescriptors.
210
+ */
211
+ enumerate(): Promise<DeviceDescriptor[]>;
212
+ /**
213
+ * Continuous listening: tracks device connect/disconnect via diffing.
214
+ */
215
+ listen(onChange: (event: DeviceChangeEvent) => void): void;
216
+ stopListening(): void;
217
+ /**
218
+ * Resolve to the latest snapshot of advertising devices observed within
219
+ * `timeoutMs`. Rewrites `_discovered` so it tracks the live list.
220
+ *
221
+ * `listenToAvailableDevices` is a BehaviorSubject — it emits the cached
222
+ * list on subscribe and *only* re-emits when the list changes. A stable
223
+ * list of currently-advertising devices therefore produces only the
224
+ * subscription frame; treating that frame as "fossil to be discarded"
225
+ * caused devices to flicker in/out of the UI as searches alternated
226
+ * between "saw a change frame" and "saw only the cached frame".
227
+ *
228
+ * The window still gives a chance for the active scan to update the list
229
+ * (e.g. drop a peripheral that just stopped broadcasting), but if nothing
230
+ * changes we trust the snapshot — DMK silence on a stable list means
231
+ * "everything's still there".
232
+ */
233
+ getLiveDevices(timeoutMs?: number): Promise<DmkDiscoveredDevice[]>;
234
+ /**
235
+ * Trigger browser device selection (WebHID requestDevice).
236
+ * Starts discovery for a short period, then stops.
237
+ */
238
+ /**
239
+ * Start BLE discovery if not already running.
240
+ * Does NOT stop — DMK keeps scanning in the background so that
241
+ * listenToAvailableDevices() always has fresh data.
242
+ */
243
+ private _discoverySub;
244
+ requestDevice(): Promise<void>;
245
+ /** Lookup a previously-discovered device's display name. */
246
+ getDeviceName(deviceId: string): string | undefined;
247
+ /** Connect to a previously discovered device. Returns sessionId. */
248
+ connect(deviceId: string): Promise<string>;
249
+ /** Disconnect a session. */
250
+ disconnect(sessionId: string): Promise<void>;
251
+ getSessionId(deviceId: string): string | undefined;
252
+ getDeviceId(sessionId: string): string | undefined;
253
+ /** Get the underlying DMK instance (needed by SignerManager). */
254
+ getDmk(): DeviceManagementKit;
255
+ stopDiscovery(): void;
256
+ dispose(): void;
257
+ /**
258
+ * Tear down RxJS subscriptions and local state without closing the DMK.
259
+ * For light-reset paths that want to drop session state but keep the
260
+ * underlying transport alive for retry. Call this instead of orphaning
261
+ * the manager — bare null assignment leaks _listenSub / _discoverySub.
262
+ */
263
+ disposeKeepingDmk(): void;
155
264
  }
156
265
 
157
266
  /**
@@ -188,15 +297,15 @@ declare class LedgerConnectorBase implements IConnector {
188
297
  private readonly _eventHandlers;
189
298
  private readonly _providedDmk;
190
299
  private readonly _createTransport;
191
- private readonly _connectionType;
300
+ readonly connectionType: ConnectionType;
192
301
  private _connectIdToPath;
193
302
  private _pathToConnectId;
194
- /** Register a connectId <-> path mapping from a device descriptor. */
195
- private _registerDeviceId;
196
303
  /** Get DMK path from external connectId. Falls back to connectId itself. */
197
304
  private _getPathForConnectId;
198
305
  /** Get external connectId from DMK path. Falls back to path itself. */
199
306
  private _getConnectIdForPath;
307
+ private readonly _cancellers;
308
+ private readonly _sessionStateSubs;
200
309
  /**
201
310
  * Resolves a Ledger signer kit module by package name.
202
311
  * Override via constructor to use CJS paths for Metro (React Native).
@@ -221,11 +330,31 @@ declare class LedgerConnectorBase implements IConnector {
221
330
  * Override in subclasses to extract stable identifiers (e.g. BLE hex ID).
222
331
  */
223
332
  protected _resolveConnectId(descriptor: DeviceDescriptor): string;
333
+ /**
334
+ * Authoritative discovery for this transport. Default = enumerate snapshot
335
+ * (USB/HID). BLE overrides to drive from a live scan window since DMK's
336
+ * enumerate() cache survives peripherals going offline.
337
+ */
338
+ protected _discoverDescriptors(dm: LedgerDeviceManager): Promise<DeviceDescriptor[]>;
224
339
  searchDevices(): Promise<ConnectorDevice[]>;
225
340
  connect(deviceId?: string): Promise<ConnectorSession>;
226
341
  disconnect(sessionId: string): Promise<void>;
342
+ /**
343
+ * Subscribe to DMK's per-session state observable so that any autonomous
344
+ * disconnect (USB unplug, BLE drop, device sleep, transport reset) is
345
+ * surfaced as a `device-disconnect` event — without this, the upstream
346
+ * `LedgerAdapter._sessions` map would hold a dead session entry until
347
+ * the next call hit `DeviceSessionNotFound`.
348
+ *
349
+ * Best-effort: any error subscribing is swallowed so a flaky DMK
350
+ * doesn't break the connect path.
351
+ */
352
+ private _watchSessionState;
353
+ private _unwatchSessionState;
354
+ private _handleAutonomousDisconnect;
227
355
  call(sessionId: string, method: string, params: unknown): Promise<unknown>;
228
- cancel(_sessionId: string): Promise<void>;
356
+ private _dispatch;
357
+ cancel(sessionId: string): Promise<void>;
229
358
  uiResponse(_response: UiResponseEvent): void;
230
359
  on<K extends ConnectorEventType>(event: K, handler: (data: ConnectorEventMap[K]) => void): void;
231
360
  off<K extends ConnectorEventType>(event: K, handler: (data: ConnectorEventMap[K]) => void): void;
@@ -247,85 +376,49 @@ declare class LedgerConnectorBase implements IConnector {
247
376
  */
248
377
  private _replaceSession;
249
378
  /**
250
- * Light reset: clear signer/session state but keep DMK, BLE scan, and ID mapping alive.
379
+ * Light reset: clear signer/session state but keep DMK and ID mapping alive.
251
380
  * Used by connect() retry — we want to re-discover with the same transport.
381
+ *
382
+ * Note: drops the device manager but tears down its RxJS subs first via
383
+ * disposeKeepingDmk(). Bare null-assignment would leak _listenSub /
384
+ * _discoverySub and double-scan after the next _initManagers().
252
385
  */
253
386
  private _resetSignersAndSessions;
254
387
  private _resetAll;
255
388
  protected _emit<K extends ConnectorEventType>(event: K, data: ConnectorEventMap[K]): void;
256
- private _wrapError;
257
- }
258
-
259
- /**
260
- * Manages device discovery, connection, and session tracking.
261
- * Wraps DMK's Observable APIs into simpler imperative calls.
262
- */
263
- declare class LedgerDeviceManager {
264
- private readonly _dmk;
265
- private readonly _discovered;
266
- private readonly _sessions;
267
- private readonly _sessionToDevice;
268
- private _listenSub;
269
- constructor(dmk: DeviceManagementKit);
270
- /**
271
- * One-shot enumeration: subscribe to listenToAvailableDevices,
272
- * take the first emission, unsubscribe, return DeviceDescriptors.
273
- */
274
- enumerate(): Promise<DeviceDescriptor[]>;
275
389
  /**
276
- * Continuous listening: tracks device connect/disconnect via diffing.
390
+ * Return a per-call ctx with the chain's Ledger app name pre-bound to
391
+ * wrapError, so chain handlers don't need to repeat `{ defaultAppName: 'X' }`
392
+ * at every catch site. Falls through unchanged for unknown methods.
277
393
  */
278
- listen(onChange: (event: DeviceChangeEvent) => void): void;
279
- stopListening(): void;
280
- /**
281
- * Trigger browser device selection (WebHID requestDevice).
282
- * Starts discovery for a short period, then stops.
283
- */
284
- /**
285
- * Start BLE discovery if not already running.
286
- * Does NOT stop — DMK keeps scanning in the background so that
287
- * listenToAvailableDevices() always has fresh data.
288
- */
289
- private _discoverySub;
290
- requestDevice(): Promise<void>;
291
- /** Connect to a previously discovered device. Returns sessionId. */
292
- connect(deviceId: string): Promise<string>;
293
- /** Disconnect a session. */
294
- disconnect(sessionId: string): Promise<void>;
295
- getSessionId(deviceId: string): string | undefined;
296
- getDeviceId(sessionId: string): string | undefined;
297
- /** Get the underlying DMK instance (needed by SignerManager). */
298
- getDmk(): DeviceManagementKit;
299
- stopDiscovery(): void;
300
- dispose(): void;
394
+ private _ctxForMethod;
395
+ private _wrapError;
301
396
  }
302
397
 
398
+ /** Optional context attached to the rejection when a canceller fires. */
399
+ interface CancelReason {
400
+ code?: number;
401
+ tag?: string;
402
+ message?: string;
403
+ }
303
404
  /**
304
- * Re-export DMK types under local aliases for backward compatibility.
305
- * These replace the previous duck-typed interfaces with the real SDK types.
306
- */
307
- type DmkDiscoveredDevice = DiscoveredDevice;
308
- /**
309
- * DMK DeviceAction — the Observable-based return type of all DMK signer methods.
405
+ * Convert a DMK DeviceAction (Observable-based) into a Promise.
406
+ * Handles pending -> completed/error state transitions and interaction callbacks.
310
407
  *
311
- * This is a permissive alias for `ExecuteDeviceActionReturnType` that accepts
312
- * any Error and IntermediateValue generics. Signer wrapper code only cares about
313
- * the Output type and the observable/cancel shape.
314
- */
315
- type DeviceAction<T> = ExecuteDeviceActionReturnType<T, any, any>;
316
- /**
317
- * DMK DeviceActionState re-exported with permissive Error/IntermediateValue.
318
- * The real type is a discriminated union on `status`.
408
+ * Tracks the last DMK step observed (e.g. `signer.eth.steps.blindSignTransactionFallback`)
409
+ * and attaches it to the rejected error as a non-enumerable `_lastStep` property,
410
+ * so upstream error classifiers can distinguish failure contexts (e.g. Blind signing
411
+ * disabled vs. generic Invalid data).
412
+ *
413
+ * @param timeoutMs Idle watchdog ms. Re-armed on each emission, paused
414
+ * during interactive pending. 0 disables. Default 65s
415
+ * (covers DMK's 60s + margin; we're a backstop only).
416
+ * @param onRegisterCanceller Optional callback that receives a function which, when
417
+ * called, unsubscribes and cancels the underlying DeviceAction
418
+ * (releases DMK's IntentQueue slot). Caller is responsible
419
+ * for invoking the canceller on timeout/abort scenarios.
319
420
  */
320
- type DeviceActionState<T> = DeviceActionState$1<T, any, any>;
321
- /** ETH address result — re-exported from DMK for backward compatibility. */
322
- type SignerEvmAddress = Address;
323
- /** ETH signature result — re-exported from DMK for backward compatibility. */
324
- type SignerEvmSignature = Signature;
325
- /** BTC address result (WalletAddress not exported from DMK top-level). */
326
- interface SignerBtcAddress {
327
- address: string;
328
- }
421
+ declare function deviceActionToPromise<T>(action: DeviceAction<T>, onInteraction?: (interaction: string) => void, timeoutMs?: number, onRegisterCanceller?: (cancel: (reason?: CancelReason) => void) => void): Promise<T>;
329
422
 
330
423
  /**
331
424
  * Wraps Ledger's SDK signer (Observable-based DeviceActions) into
@@ -335,6 +428,12 @@ declare class SignerEth {
335
428
  private readonly _sdk;
336
429
  onInteraction?: (interaction: string) => void;
337
430
  constructor(_sdk: SignerEth$1);
431
+ /**
432
+ * Optional callback registered per-call so the caller can obtain a canceller
433
+ * for the underlying DeviceAction. Caller can invoke it to release DMK's
434
+ * IntentQueue slot and unsubscribe from the action's observable.
435
+ */
436
+ onRegisterCanceller?: (cancel: (reason?: CancelReason) => void) => void;
338
437
  getAddress(derivationPath: string, options?: {
339
438
  checkOnDevice?: boolean;
340
439
  }): Promise<SignerEvmAddress>;
@@ -352,16 +451,16 @@ type SignerEthBuilderFn = (args: {
352
451
  build(): SignerEth$1;
353
452
  }>;
354
453
  /**
355
- * Manages per-sessionId SignerEth instances.
356
- * Creates on demand, caches for reuse, invalidates on session change.
454
+ * Per-sessionId SignerEth factory. Builds fresh on every call — DMK signers
455
+ * hold DeviceAction state that isn't safe to reuse, so `invalidate` /
456
+ * `clearAll` are kept as no-op lifecycle hooks for callers.
357
457
  */
358
458
  declare class SignerManager {
359
- private readonly _cache;
360
459
  private readonly _dmk;
361
460
  private readonly _builderFn;
362
461
  constructor(dmk: DeviceManagementKit, builderFn?: SignerEthBuilderFn);
363
462
  getOrCreate(sessionId: string): Promise<SignerEth>;
364
- invalidate(sessionId: string): void;
463
+ invalidate(_sessionId: string): void;
365
464
  clearAll(): void;
366
465
  private static _defaultBuilder;
367
466
  }
@@ -377,6 +476,7 @@ type BtcMessageOptions = Parameters<SignerBtc$1['signMessage']>[2];
377
476
  declare class SignerBtc {
378
477
  private readonly _sdk;
379
478
  onInteraction?: (interaction: string) => void;
479
+ onRegisterCanceller?: (cancel: (reason?: CancelReason) => void) => void;
380
480
  constructor(_sdk: SignerBtc$1);
381
481
  getWalletAddress(wallet: BtcWallet, addressIndex: number, options?: {
382
482
  checkOnDevice?: boolean;
@@ -422,6 +522,7 @@ type SolMsgOptions = Parameters<SignerSolana['signMessage']>[2];
422
522
  declare class SignerSol {
423
523
  private readonly _sdk;
424
524
  onInteraction?: (interaction: string) => void;
525
+ onRegisterCanceller?: (cancel: (reason?: CancelReason) => void) => void;
425
526
  constructor(_sdk: SignerSolana);
426
527
  /**
427
528
  * Get the Solana address (base58-encoded Ed25519 public key) at the given derivation path.
@@ -462,20 +563,6 @@ declare class DmkTransport extends Transport {
462
563
  close(): Promise<void>;
463
564
  }
464
565
 
465
- /**
466
- * Convert a DMK DeviceAction (Observable-based) into a Promise.
467
- * Handles pending -> completed/error state transitions and interaction callbacks.
468
- *
469
- * Tracks the last DMK step observed (e.g. `signer.eth.steps.blindSignTransactionFallback`)
470
- * and attaches it to the rejected error as a non-enumerable `_lastStep` property,
471
- * so upstream error classifiers can distinguish failure contexts (e.g. Blind signing
472
- * disabled vs. generic Invalid data).
473
- *
474
- * @param timeoutMs Timeout in ms. Resets each time the Observable emits (device is alive).
475
- * Pass 0 to disable. Default: 30s.
476
- */
477
- declare function deviceActionToPromise<T>(action: DeviceAction<T>, onInteraction?: (interaction: string) => void, timeoutMs?: number): Promise<T>;
478
-
479
566
  interface AppManagerOptions {
480
567
  waitMs?: number;
481
568
  maxRetries?: number;
@@ -505,9 +592,16 @@ declare class AppManager {
505
592
  * 5. Poll until the device confirms the target app is running.
506
593
  */
507
594
  /**
508
- * @param onConfirmOnDevice Called after OpenAppCommand succeeds
509
- * the device is now showing a confirmation prompt to the user.
510
- * NOT called if the app is already open or if the app is not installed.
595
+ * @param onConfirmOnDevice Called BEFORE OpenAppCommand is issued the
596
+ * device is about to display "Open <app>" on screen and wait for the
597
+ * user's button press. UI consumers should show their "open app" prompt
598
+ * in response. NOT called when the target app is already open (no user
599
+ * interaction needed in that case).
600
+ *
601
+ * Important: OpenAppCommand is blocking. It does not resolve until the user
602
+ * has physically confirmed on the device, so anything that runs AFTER
603
+ * `await this._openApp(...)` lands AFTER the prompt is already gone.
604
+ * Hence the callback must fire BEFORE that await.
511
605
  */
512
606
  ensureAppOpen(sessionId: string, targetAppName: string, onConfirmOnDevice?: () => void): Promise<void>;
513
607
  private _getCurrentApp;
@@ -522,16 +616,65 @@ declare class AppManager {
522
616
  private _wait;
523
617
  }
524
618
 
525
- /** Check if an error (or any error in its chain) represents a locked Ledger device. */
619
+ type LedgerFailure = Omit<Failure, 'payload'> & {
620
+ payload: Failure['payload'] & {
621
+ appName?: string;
622
+ _tag?: string;
623
+ };
624
+ };
625
+ interface WrapErrorOptions {
626
+ /** Fallback when err has no `appName` (DMK signer errors don't carry it). */
627
+ defaultAppName?: string;
628
+ }
629
+ declare function ledgerFailure(code: HardwareErrorCode, error: string, appName?: string, tag?: string, params?: Record<string, unknown>): LedgerFailure;
526
630
  declare function isDeviceLockedError(err: unknown): boolean;
527
631
  /**
528
- * Map a Ledger DMK error to a HardwareErrorCode and human-readable message
529
- * with actionable recovery information for the caller.
632
+ * Map a Ledger DMK error to a HardwareErrorCode and human-readable message.
633
+ * `opts.defaultAppName` fills `appName` when the raw error doesn't carry it
634
+ * (DMK signer errors don't).
530
635
  */
531
- declare function mapLedgerError(err: unknown): {
636
+ declare function mapLedgerError(err: unknown, opts?: WrapErrorOptions): {
532
637
  code: HardwareErrorCode;
533
638
  message: string;
534
639
  appName?: string;
535
640
  };
536
641
 
537
- export { AppManager, type DeviceActionState, type DmkDiscoveredDevice, DmkTransport, LedgerAdapter, LedgerConnectorBase, type LedgerConnectorBaseOptions, LedgerDeviceManager, SignerBtc, type SignerBtcAddress, SignerEth, type SignerEvmAddress, type SignerEvmSignature, SignerManager, SignerSol, type TransportFactory, deviceActionToPromise, isDeviceLockedError, mapLedgerError };
642
+ /**
643
+ * SDK-global event bus. Per-runtime singleton — each process subscribes
644
+ * independently; cross-process hosts forward events over their own IPC.
645
+ */
646
+ type SdkLogEvent = {
647
+ type: 'log';
648
+ level: 'debug' | 'error';
649
+ /** Pre-stringified payload. */
650
+ message: string;
651
+ };
652
+ /** Add new variants here; hosts dispatch on `event.type`. */
653
+ type SdkEvent = SdkLogEvent;
654
+ type SdkEventListener = (event: SdkEvent) => void;
655
+ declare function onSdkEvent(listener: SdkEventListener): () => void;
656
+ declare function offSdkEvent(listener: SdkEventListener): void;
657
+
658
+ /**
659
+ * Extract the stable 4-digit HEX identifier from a Ledger BLE device name.
660
+ * e.g., "Nano X 123A" -> "123A", "Ledger Nano X AB12" -> "AB12"
661
+ * Returns undefined if no valid HEX suffix found.
662
+ */
663
+ declare function extractBleHexId(name?: string): string | undefined;
664
+
665
+ /**
666
+ * Ledger DMK transport identifiers that represent BLE devices.
667
+ *
668
+ * Known today:
669
+ * - "BLE": generic/web BLE descriptors
670
+ * - "RN_BLE": @ledgerhq/device-transport-kit-react-native-ble
671
+ *
672
+ * Keep the suffix check here so future DMK BLE transports (for example
673
+ * "FOO_BLE") are handled in one place instead of scattering string checks.
674
+ */
675
+ declare function isLedgerDmkBleTransport(transport?: string): boolean;
676
+ declare function isLedgerBleConnectionType(connectionType?: ConnectionType): boolean;
677
+ declare function isLedgerBleDescriptor(connectionType: ConnectionType, descriptor: DeviceDescriptor): boolean;
678
+ declare function isValidLedgerBleConnectId(connectId?: string): boolean;
679
+
680
+ export { AppManager, type DeviceActionState, type DmkDiscoveredDevice, DmkTransport, LedgerAdapter, LedgerConnectorBase, type LedgerConnectorBaseOptions, LedgerDeviceManager, type LedgerFailure, type SdkEvent, type SdkEventListener, type SdkLogEvent, SignerBtc, type SignerBtcAddress, SignerEth, type SignerEvmAddress, type SignerEvmSignature, SignerManager, SignerSol, type TransportFactory, deviceActionToPromise, extractBleHexId, isDeviceLockedError, isLedgerBleConnectionType, isLedgerBleDescriptor, isLedgerDmkBleTransport, isValidLedgerBleConnectId, ledgerFailure, mapLedgerError, offSdkEvent, onSdkEvent };