@onekeyfe/hwk-keystone-adapter 1.2.2-alpha.100

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.
@@ -0,0 +1,468 @@
1
+ import { ChainCapability, IHardwareWallet, IConnector, TransportType, SearchDevicesOptions, DeviceInfo, Response, ChainForFingerprint, UiResponseEvent, HardwareEventMap, DeviceEventListener, AllNetworkGetAddressParams, AllNetworkAddressResponse, NullableCallArg, IHardwareCallParams, 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 } from '@onekeyfe/hwk-adapter-core';
2
+
3
+ /**
4
+ * A UR payload in the wire shape the reserved `QrDisplayData`/`QrResponseData`
5
+ * (from `@onekeyfe/hwk-adapter-core`) already commit to: `urType` + `urData`.
6
+ *
7
+ * `urData` is the hex-encoded raw CBOR payload (equivalent to `ur.cbor.toString('hex')`),
8
+ * NOT a `ur:type/…` bech32-style string and NOT a pre-fragmented animated-QR frame
9
+ * list. Turning this into single- or multi-part QR frames is a rendering concern the
10
+ * app/UI layer owns (via `@ngraveio/bc-ur`'s `UREncoder`), not something the engine
11
+ * or the event payload should bake in — `QrDisplayData.animated` is only a hint that
12
+ * the payload is large enough to need fragmenting.
13
+ */
14
+ interface KeystoneUr {
15
+ urType: string;
16
+ urData: string;
17
+ }
18
+ interface KeystoneParsedAccount {
19
+ chain: string;
20
+ path: string;
21
+ publicKey: string;
22
+ extendedPublicKey?: string;
23
+ /** Per-key source fingerprint, hex. Usually equals the account's masterFingerprint. */
24
+ xfp?: string;
25
+ name?: string;
26
+ }
27
+ interface KeystoneParsedMultiAccounts {
28
+ /** BIP32 master fingerprint of the seed (lowercase hex, 8 chars) — the cross-channel wallet identity. */
29
+ masterFingerprint: string;
30
+ /** Model string (e.g. "Keystone 3 Pro"), present on both channels but not unique per unit. */
31
+ device?: string;
32
+ /**
33
+ * Hardware-derived id (sha256(sha256(serial))). Only populated in specific
34
+ * wallet-branded QR sync menus — firmware omits it on the generic
35
+ * KeyDerivation path USB uses. Enrichment only; never key identity on it.
36
+ */
37
+ deviceId?: string;
38
+ deviceVersion?: string;
39
+ accounts: KeystoneParsedAccount[];
40
+ }
41
+ interface KeystoneEthSignRequestInput {
42
+ requestId: string;
43
+ /** Hex, no 0x prefix — raw unsigned payload matching dataType. */
44
+ unsignedTxHex: string;
45
+ dataType: 'transaction' | 'typedTransaction' | 'personalMessage' | 'typedData';
46
+ path: string;
47
+ xfp: string;
48
+ chainId?: number;
49
+ address?: string;
50
+ origin?: string;
51
+ }
52
+ interface KeystoneEthSignatureResult {
53
+ requestId?: string;
54
+ r: string;
55
+ s: string;
56
+ /** Hex, no 0x prefix. Legacy tx: recovery id/27-28 form. EIP-1559/2930: 0/1 parity. */
57
+ v: string;
58
+ }
59
+ interface KeystoneBtcSignRequestAccount {
60
+ path: string;
61
+ xfp: string;
62
+ address?: string;
63
+ }
64
+ /**
65
+ * Standard BIP-44/49/84/86 purpose → script-type mapping. `p2tr` is a
66
+ * recognized value but `KeystoneUrEngine.deriveBtcAddressFromXpub` doesn't
67
+ * support it yet — taproot output-key tweaking needs an elliptic-curve
68
+ * library (`bitcoinjs-lib`'s `initEccLib`) this package doesn't wire in.
69
+ */
70
+ type BtcScriptType = 'p2pkh' | 'p2sh-p2wpkh' | 'p2wpkh' | 'p2tr';
71
+ interface KeystoneSolSignRequestInput {
72
+ requestId: string;
73
+ /** Hex, no 0x prefix. */
74
+ unsignedPayloadHex: string;
75
+ dataType: 'transaction' | 'message';
76
+ path: string;
77
+ xfp: string;
78
+ address?: string;
79
+ origin?: string;
80
+ }
81
+ interface KeystoneSolSignatureResult {
82
+ requestId?: string;
83
+ /** Hex, no 0x prefix. */
84
+ signature: string;
85
+ }
86
+ interface KeystoneBtcSignatureResult {
87
+ requestId: string;
88
+ publicKey: string;
89
+ /** Hex, no 0x prefix. */
90
+ signature: string;
91
+ }
92
+ /** SLIP-10 for secp256k1 (EVM/BTC) and ed25519 (SOL); Cardano-style BIP32-Ed25519 is out of scope. */
93
+ type KeystoneDerivationCurve = 'secp256k1' | 'ed25519';
94
+ interface KeystoneKeySchema {
95
+ path: string;
96
+ curve?: KeystoneDerivationCurve;
97
+ }
98
+ interface KeystoneKeyDerivationRequestInput {
99
+ schemas: KeystoneKeySchema[];
100
+ origin?: string;
101
+ }
102
+ interface KeystoneTronSignRequestInput {
103
+ requestId: string;
104
+ /**
105
+ * Hex, no 0x prefix — a standard TRON protobuf `Transaction.raw` message
106
+ * (the same bytes `TronSignTxParams.rawTxHex` already carries for Ledger).
107
+ */
108
+ rawTxHex: string;
109
+ path: string;
110
+ xfp: string;
111
+ origin?: string;
112
+ }
113
+ interface KeystoneTronSignatureResult {
114
+ requestId?: string;
115
+ /** Hex, no 0x prefix — 65-byte secp256k1 signature. */
116
+ signature: string;
117
+ }
118
+
119
+ /**
120
+ * Thin wrapper around `@keystonehq/keystone-sdk`. Owns every touch point with the
121
+ * vendor SDK so the rest of the adapter never imports it directly: same UR
122
+ * construction/parsing serves both the QR and USB channels (USB carries the
123
+ * identical UR payloads inside EAPDU framing — see the Keystone USB SDK's
124
+ * `sendURRequest`), so this engine has no channel awareness at all.
125
+ *
126
+ * Deliberately uses the bare constructor, never `KeystoneSDK.create()` — `create()`
127
+ * fetches remote fragment-size config from keyst.one at call time, which this SDK
128
+ * must not depend on.
129
+ */
130
+ declare class KeystoneUrEngine {
131
+ private readonly sdk;
132
+ constructor(origin?: string);
133
+ parseMultiAccounts(ur: KeystoneUr): KeystoneParsedMultiAccounts;
134
+ parseHDKey(ur: KeystoneUr): KeystoneParsedAccount;
135
+ /**
136
+ * Build a `qr-hardware-call` (KeyDerivation) request: the host asks for
137
+ * specific paths instead of waiting for whatever the device happens to be
138
+ * showing. The device replies with a `crypto-multi-accounts` UR — parse it
139
+ * with `parseMultiAccounts`. Used for the implicit "sync this wallet's xfp
140
+ * before the first sign" round trip as well as an explicit account import.
141
+ *
142
+ * `version: V1` is required — verified against real Keystone hardware and
143
+ * `keystone3-firmware`'s `CheckHardwareCallRequestIsLegal` source: an
144
+ * unversioned/V0 request is validated as a legacy Cardano-only request
145
+ * (`m/1852'/1815'/...`) and firmware rejects every other chain's path with
146
+ * `PRS_PARSING_ERROR` (device-shown message: "路径不受支持" / "path not
147
+ * supported"), regardless of the path's shape. V1 is what actually enables
148
+ * the general per-chain path whitelist (includes `m/44'/60'` for ETH, the
149
+ * standard BTC purposes, etc.). The SDK itself defaults to V0 unless a
150
+ * truthy `version` is passed — omitting this silently produces a request
151
+ * every non-Cardano device rejects.
152
+ */
153
+ buildKeyDerivationRequest(input: KeystoneKeyDerivationRequestInput): KeystoneUr;
154
+ /**
155
+ * Parse the response to a KeyDerivation request (or a device-initiated
156
+ * account export): `crypto-multi-accounts` for a multi-schema request,
157
+ * `crypto-hdkey` for a single-key response some firmware paths use instead.
158
+ * Both are normalized to the same `KeystoneParsedMultiAccounts` shape — a
159
+ * single `crypto-hdkey` becomes a one-entry account list, with its own
160
+ * `origin.sourceFingerprint` promoted to `masterFingerprint` (correct for a
161
+ * key derived directly from the seed, which every request this engine
162
+ * builds asks for).
163
+ */
164
+ parseAccountResponse(ur: KeystoneUr): KeystoneParsedMultiAccounts;
165
+ buildEthSignRequest(input: KeystoneEthSignRequestInput): KeystoneUr;
166
+ parseEthSignature(ur: KeystoneUr): KeystoneEthSignatureResult;
167
+ /**
168
+ * Derive one EVM address offline from an already-synced account xpub —
169
+ * verified against the same `@keystonehq/bc-ur-registry-eth` helper the
170
+ * Keystone-based OneKey air-gap demo uses in production
171
+ * (`generateAddressFromXpub`), so no unverified assumption about what a
172
+ * leaf-path KeyDerivation request would return. `relativeDerivePath` is
173
+ * relative to the xpub's own depth, e.g. `'0/0'` for an account xpub.
174
+ */
175
+ deriveEvmAddressFromXpub(xpub: string, relativeDerivePath: string): string;
176
+ /**
177
+ * Derive one BTC address offline from an already-synced account xpub, the
178
+ * same way `deriveEvmAddressFromXpub` does. Keystone's `CryptoHDKey`
179
+ * always emits standard mainnet-xpub version bytes (`0488B21E`) regardless
180
+ * of the account's purpose/script type (verified against
181
+ * `@keystonehq/bc-ur-registry`'s `CryptoHDKey.getBip32Key`, which hardcodes
182
+ * that version rather than switching to a SLIP-132 ypub/zpub prefix per
183
+ * script type) — so `hdkey.fromExtendedKey` parses it correctly for every
184
+ * `scriptType` without needing custom version bytes configured.
185
+ *
186
+ * `p2tr` is deliberately not handled: taproot output-key tweaking (BIP-341)
187
+ * needs an elliptic-curve library wired via bitcoinjs-lib's `initEccLib`,
188
+ * which this package doesn't set up yet — every other payment function
189
+ * here needs no such library.
190
+ */
191
+ deriveBtcAddressFromXpub(xpub: string, relativeDerivePath: string, scriptType: BtcScriptType): string;
192
+ buildBtcPsbtRequest(unsignedPsbtHex: string): KeystoneUr;
193
+ /** Returns the hex-encoded (possibly still-unsigned-in-part) PSBT the device replied with. */
194
+ parseBtcPsbt(ur: KeystoneUr): string;
195
+ buildBtcMessageSignRequest(params: {
196
+ requestId: string;
197
+ /** Hex, no 0x prefix. */
198
+ messageHex: string;
199
+ accounts: KeystoneBtcSignRequestAccount[];
200
+ origin?: string;
201
+ }): KeystoneUr;
202
+ parseBtcSignature(ur: KeystoneUr): KeystoneBtcSignatureResult;
203
+ buildSolSignRequest(input: KeystoneSolSignRequestInput): KeystoneUr;
204
+ parseSolSignature(ur: KeystoneUr): KeystoneSolSignatureResult;
205
+ /**
206
+ * `@keystonehq/keystone-sdk`'s own bundled `sdk.tron` module is
207
+ * deliberately NOT used here — see `TronSignRequest.ts`'s doc comment for
208
+ * why: it's a different (gzip/protobuf) protocol with response semantics
209
+ * this package has no way to verify, whereas `TronSignRequest`/
210
+ * `TronSignature` are a direct port of OneKey's own already-proven
211
+ * production QR-wallet TRON implementation (a plain CBOR-native
212
+ * sign-request/signature pair, same shape as eth/sol). The device decodes
213
+ * `rawTxHex` itself — no client-side contract-type pre-parsing or
214
+ * `tokenInfo` needed, unlike the public SDK's module.
215
+ */
216
+ buildTronSignRequest(input: KeystoneTronSignRequestInput): KeystoneUr;
217
+ parseTronSignature(ur: KeystoneUr): KeystoneTronSignatureResult;
218
+ /**
219
+ * Derive one TRON address offline from an already-synced account xpub.
220
+ * TRON reuses EVM's exact secp256k1-pubkey → keccak256 → last-20-bytes
221
+ * derivation (verified against Keystone's own `formatAddress()` in
222
+ * `keystone-sdk`'s TRON chain source) — only the final text encoding
223
+ * differs (base58check with a `0x41` version byte, not checksummed hex).
224
+ * Reusing `generateAddressFromXpub` here means no new hashing dependency:
225
+ * strip its "0x" and re-encode the same 20 bytes.
226
+ */
227
+ deriveTronAddressFromXpub(xpub: string, relativeDerivePath: string): string;
228
+ }
229
+
230
+ interface ImportFromQrOptions {
231
+ /**
232
+ * 'request': host asks for specific paths via a `qr-hardware-call`
233
+ * (KeyDerivation) UR — precise, works regardless of what screen the device
234
+ * is on. 'scan': just wait for whatever multi-account/HD-key export the
235
+ * device is already showing. Defaults to 'request' with `paths`, or the
236
+ * default EVM/BTC/SOL account set if `paths` is omitted.
237
+ */
238
+ mode?: 'request' | 'scan';
239
+ paths?: Array<{
240
+ hwkChain: ChainCapability;
241
+ path: string;
242
+ }>;
243
+ }
244
+ /**
245
+ * Keystone hardware wallet adapter — QR and USB channels merged behind one
246
+ * `IHardwareWallet` surface, keyed by the wallet's master fingerprint (mfp):
247
+ * a caller sees the same `evmSignTransaction(...)` call regardless of which
248
+ * channel actually carries it. Internally, a chain method's UR round trip
249
+ * either drives one or two `REQUEST_QR_DISPLAY`/`REQUEST_QR_SCAN` UI events
250
+ * (QR) or a direct `IConnector.call(sessionId, 'resolveUr', ur)` (USB) — see
251
+ * `_resolveUr`.
252
+ *
253
+ * QR needs no physical enumeration or explicit connect step: a caller can
254
+ * call any chain method with `connectId`/`deviceId` both null and the
255
+ * adapter drives its own implicit cold-start sync. USB is the opposite —
256
+ * WebUSB device pickers require a user gesture, so a USB session only comes
257
+ * into existence via an explicit `searchDevices()` + `connectDevice()` (see
258
+ * `_connectUsb`). Once a USB session exists for a wallet's mfp, later calls
259
+ * for that same wallet route over USB automatically (unless pinned via
260
+ * `switchTransport`) — matching docs/design/keystone-integration/README.md §4.3.
261
+ */
262
+ declare class KeystoneAdapter implements IHardwareWallet {
263
+ readonly vendor: "keystone";
264
+ private readonly urEngine;
265
+ private readonly emitter;
266
+ private readonly _uiRegistry;
267
+ private readonly _jobQueue;
268
+ private readonly _devices;
269
+ private readonly _origin;
270
+ /** How long to wait for the app to answer a `REQUEST_QR_DISPLAY`/`REQUEST_QR_SCAN` before failing. Defaults to the registry's own 10-minute default. */
271
+ private readonly _qrTimeoutMs;
272
+ /** Optional USB `IConnector` — supplied by the host app (DI, same pattern as Trezor/Ledger), e.g. via `createKeystoneWebUsbConnector()` from `@onekeyfe/hwk-keystone-connector-usb`. Undefined means QR-only. */
273
+ private readonly _usbConnector;
274
+ /** Explicit `switchTransport` pin. `undefined` means "auto": USB when a live session exists for the target wallet, else QR. */
275
+ private _forcedTransport;
276
+ constructor(options?: {
277
+ origin?: string;
278
+ qrTimeoutMs?: number;
279
+ usbConnector?: IConnector;
280
+ });
281
+ get activeTransport(): TransportType | null;
282
+ getAvailableTransports(): TransportType[];
283
+ /** Pins subsequent calls to 'qr' or 'usb' (routing otherwise defaults to "USB when the target wallet has a live session, else QR" — see `_resolveUr`). Any other value clears the pin back to auto. */
284
+ switchTransport(type: TransportType): Promise<void>;
285
+ init(_config?: unknown): Promise<void>;
286
+ dispose(): Promise<void>;
287
+ /**
288
+ * QR-synced wallets are always included (this instance's own state — no
289
+ * enumeration exists for QR). When a USB connector is configured, its raw
290
+ * scan results are appended as-is: a USB descriptor has no mfp until
291
+ * `connectDevice()` actually opens+claims it (see
292
+ * `KeystoneUsbConnectorBase.searchDevices`), so these entries carry an
293
+ * empty `deviceId` and exist purely so a host can list "plugged in, click
294
+ * to connect" candidates.
295
+ */
296
+ searchDevices(_options?: SearchDevicesOptions): Promise<DeviceInfo[]>;
297
+ connectDevice(connectId: string): Promise<Response<string>>;
298
+ /**
299
+ * QR has no persistent connection to tear down — the account cache
300
+ * survives so a later call resumes without re-syncing. For a USB session,
301
+ * this closes the connector session and either demotes the record back to
302
+ * QR-only (if it was ever QR-synced) or removes it entirely (pure-USB
303
+ * wallet that was never seen over QR) — see §4.2 of the design doc.
304
+ */
305
+ disconnectDevice(connectId: string): Promise<void>;
306
+ getDeviceInfo(connectId: string, deviceId: string): Promise<Response<DeviceInfo>>;
307
+ getSupportedChains(): ChainCapability[];
308
+ cancel(connectId?: string): void;
309
+ getChainFingerprint(connectId: string, deviceId: string, chain: ChainForFingerprint): Promise<Response<string>>;
310
+ uiResponse(response: UiResponseEvent): void;
311
+ on<K extends keyof HardwareEventMap>(event: K, listener: (event: HardwareEventMap[K]) => void): void;
312
+ on(event: string, listener: DeviceEventListener): void;
313
+ off<K extends keyof HardwareEventMap>(event: K, listener: (event: HardwareEventMap[K]) => void): void;
314
+ off(event: string, listener: DeviceEventListener): void;
315
+ importFromQr(options?: ImportFromQrOptions): Promise<Response<DeviceInfo>>;
316
+ allNetworkGetAddress: (connectId: string, deviceId: string, params: AllNetworkGetAddressParams) => Promise<Response<AllNetworkAddressResponse[]>>;
317
+ evmGetAddress(connectIdArg?: NullableCallArg<string>, deviceIdArg?: NullableCallArg<string>, paramsArg?: NullableCallArg<IHardwareCallParams<EvmGetAddressParams>>): Promise<Response<EvmAddress>>;
318
+ evmSignTransaction(connectIdArg?: NullableCallArg<string>, deviceIdArg?: NullableCallArg<string>, paramsArg?: NullableCallArg<IHardwareCallParams<EvmSignTxParams>>): Promise<Response<EvmSignedTx>>;
319
+ evmSignMessage(connectIdArg?: NullableCallArg<string>, deviceIdArg?: NullableCallArg<string>, paramsArg?: NullableCallArg<IHardwareCallParams<EvmSignMsgParams>>): Promise<Response<EvmSignature>>;
320
+ evmSignTypedData(connectIdArg?: NullableCallArg<string>, deviceIdArg?: NullableCallArg<string>, paramsArg?: NullableCallArg<IHardwareCallParams<EvmSignTypedDataParams>>): Promise<Response<EvmSignature>>;
321
+ btcGetAddress(connectIdArg?: NullableCallArg<string>, deviceIdArg?: NullableCallArg<string>, paramsArg?: NullableCallArg<IHardwareCallParams<BtcGetAddressParams>>): Promise<Response<BtcAddress>>;
322
+ btcGetPublicKey(_connectId?: NullableCallArg<string>, _deviceId?: NullableCallArg<string>, _params?: NullableCallArg<IHardwareCallParams<BtcGetPublicKeyParams>>): Promise<Response<BtcPublicKey>>;
323
+ btcSignTransaction(_connectId?: NullableCallArg<string>, _deviceId?: NullableCallArg<string>, _params?: NullableCallArg<IHardwareCallParams<BtcSignTxParams>>): Promise<Response<BtcSignedTx>>;
324
+ btcSignPsbt(connectIdArg?: NullableCallArg<string>, deviceIdArg?: NullableCallArg<string>, paramsArg?: NullableCallArg<IHardwareCallParams<BtcSignPsbtParams>>): Promise<Response<BtcSignedPsbt>>;
325
+ btcSignMessage(connectIdArg?: NullableCallArg<string>, deviceIdArg?: NullableCallArg<string>, paramsArg?: NullableCallArg<IHardwareCallParams<BtcSignMsgParams>>): Promise<Response<BtcSignature>>;
326
+ btcGetMasterFingerprint(connectIdArg?: NullableCallArg<string>, deviceIdArg?: NullableCallArg<string>): Promise<Response<{
327
+ masterFingerprint: string;
328
+ }>>;
329
+ solGetAddress(connectIdArg?: NullableCallArg<string>, deviceIdArg?: NullableCallArg<string>, paramsArg?: NullableCallArg<IHardwareCallParams<SolGetAddressParams>>): Promise<Response<SolAddress>>;
330
+ solSignTransaction(connectIdArg?: NullableCallArg<string>, deviceIdArg?: NullableCallArg<string>, paramsArg?: NullableCallArg<IHardwareCallParams<SolSignTxParams>>): Promise<Response<SolSignedTx>>;
331
+ solSignMessage(connectIdArg?: NullableCallArg<string>, deviceIdArg?: NullableCallArg<string>, paramsArg?: NullableCallArg<IHardwareCallParams<SolSignMsgParams>>): Promise<Response<SolSignature>>;
332
+ tronGetAddress(connectIdArg?: NullableCallArg<string>, deviceIdArg?: NullableCallArg<string>, paramsArg?: NullableCallArg<IHardwareCallParams<TronGetAddressParams>>): Promise<Response<TronAddress>>;
333
+ tronSignTransaction(connectIdArg?: NullableCallArg<string>, deviceIdArg?: NullableCallArg<string>, paramsArg?: NullableCallArg<IHardwareCallParams<TronSignTxParams>>): Promise<Response<TronSignedTx>>;
334
+ tronSignMessage(_connectId?: NullableCallArg<string>, _deviceId?: NullableCallArg<string>, _params?: NullableCallArg<IHardwareCallParams<TronSignMsgParams>>): Promise<Response<TronSignature>>;
335
+ private _mfpFromConnectId;
336
+ /**
337
+ * Handles both connectId shapes a caller might hand back: the QR-style
338
+ * `keystone-qr:<mfp>` prefix, and a bare mfp — which is exactly what a
339
+ * USB session's `sessionId`/`connectId` is (see `KeystoneUsbConnectorBase`
340
+ * and `_connectUsb`).
341
+ */
342
+ private _findByConnectId;
343
+ /**
344
+ * Folds a parsed account-response UR into the device table. `viaUsb`
345
+ * (defaults false) says which channel actually carried this round trip —
346
+ * `_resolveUr` routes a KeyDerivation sync over USB when the target record
347
+ * already has a live session, so this must NOT unconditionally mark
348
+ * `qrSynced`, or a USB-only wallet would wrongly survive a later USB
349
+ * disconnect as a "QR-synced, demote to QR-only" entry instead of being
350
+ * dropped outright (see `disconnectDevice`).
351
+ */
352
+ private _upsertDeviceRecord;
353
+ /**
354
+ * Opens+claims whatever Keystone the USB connector currently has
355
+ * permission for, learns its mfp via `getAppConfig`, and merges it into
356
+ * the device table by that mfp — a QR-synced entry becomes
357
+ * `{qr, usb}`-capable in place (one `device-changed`, not a second
358
+ * `device-connect`); a wallet never seen before becomes a new USB-only
359
+ * entry. See §4.2 of the design doc.
360
+ */
361
+ private _connectUsb;
362
+ /**
363
+ * The one place that decides QR vs. USB for a UR round trip and carries it
364
+ * out. `record` is the (possibly not-yet-existing, for a true cold start)
365
+ * device row for the target wallet — USB is only used when `record`
366
+ * already has a live `usbSessionId` (a session comes from an explicit
367
+ * `connectDevice()`, never conjured mid-call — see the class doc). A
368
+ * `switchTransport('qr')` pin forces QR even for a USB-attached wallet;
369
+ * `switchTransport('usb')` on a wallet with no live USB session fails
370
+ * closed rather than silently falling back to QR.
371
+ */
372
+ private _resolveUr;
373
+ /**
374
+ * Resolve (syncing over QR if needed) the account cached for `hwkChain` at
375
+ * `syncPath`. Drives the "implicit account sync, then the real request" two
376
+ * hop flow the first time a wallet/path pair is seen; a cache hit skips
377
+ * straight to the caller's own round trip.
378
+ */
379
+ private _ensureAccountSynced;
380
+ /**
381
+ * Like `_ensureAccountSynced`, but for operations (PSBT signing, master
382
+ * fingerprint) that only need to know WHICH wallet is attached, not a
383
+ * specific cached path. Syncs the account-level path for `chain` as a
384
+ * throwaway probe when the mfp isn't already known.
385
+ *
386
+ * `CHAIN_FINGERPRINT_PATHS[chain]` is a 5-segment LEAF path for `evm`
387
+ * (`m/44'/60'/0'/0/0`) — sending that verbatim as a KeyDerivation request
388
+ * asks Keystone for a non-standard path. Keystone's own docs
389
+ * (dev.keyst.one's multichain KeyDerivation example) show the ETH
390
+ * account-level path as `m/44'/60'/0'` (3 segments), same as what
391
+ * `DEFAULT_IMPORT_SCHEMAS`/`_ensureAccountSynced` already request — so
392
+ * truncate through `splitAccountPath` here too instead of using the raw
393
+ * fingerprint leaf path. `btc`/`sol` are already 3-segment account paths
394
+ * and pass through unchanged.
395
+ */
396
+ private _ensureMfpKnown;
397
+ private _requestQrDisplayAndAwaitResponse;
398
+ private _requestQrScanAndAwaitResponse;
399
+ private static _throwIfAborted;
400
+ private static _assertRequestIdMatches;
401
+ private _unsupported;
402
+ private _errorToFailure;
403
+ }
404
+
405
+ /** `keystone-qr:<mfp>` is the QR-only connectId; a merged USB session gets its own (phase 4). */
406
+ declare const QR_CONNECT_ID_PREFIX = "keystone-qr:";
407
+ declare function qrConnectId(masterFingerprint: string): string;
408
+ interface KeystoneAccountEntry extends KeystoneParsedAccount {
409
+ hwkChain: ChainCapability;
410
+ }
411
+ declare function accountKey(hwkChain: ChainCapability, path: string): string;
412
+ interface KeystoneDeviceRecord {
413
+ /** Lowercase hex BIP32 master fingerprint — the cross-channel wallet identity; doubles as `deviceId`. */
414
+ masterFingerprint: string;
415
+ connectId: string;
416
+ /** Model string from the device (e.g. "Keystone 3 Pro"); not unique per unit. */
417
+ model?: string;
418
+ deviceVersion?: string;
419
+ /** Keyed by `accountKey(hwkChain, path)`. Holds whatever was directly synced — usually account-level (3-segment) entries for EVM, exact leaf entries for SOL. */
420
+ accounts: Map<string, KeystoneAccountEntry>;
421
+ importedAt: number;
422
+ /**
423
+ * Set to the connector's `sessionId` (== this record's own mfp, per
424
+ * `KeystoneUsbConnectorBase.connect`) once a live USB session exists for
425
+ * this wallet. Cleared by `disconnectDevice`. Presence of this field is
426
+ * what `KeystoneAdapter._resolveUr` uses to route a call over USB instead
427
+ * of QR.
428
+ */
429
+ usbSessionId?: string;
430
+ /**
431
+ * True once this wallet has completed at least one QR round trip.
432
+ * Distinguishes "USB session dropped but this wallet was also QR-synced —
433
+ * fall back to a QR-only entry" from "this was a USB-only wallet that
434
+ * never synced over QR — drop the entry entirely" on USB disconnect.
435
+ */
436
+ qrSynced?: boolean;
437
+ }
438
+ declare function createDeviceRecord(masterFingerprint: string): KeystoneDeviceRecord;
439
+ declare function toDeviceInfo(record: KeystoneDeviceRecord): DeviceInfo;
440
+ /** A device row for a wallet the adapter hasn't synced yet — used while a cold-start round trip is in flight. */
441
+ declare function placeholderDeviceInfo(): DeviceInfo;
442
+
443
+ /** Always returns an `m/`-prefixed path, regardless of the input's casing/prefix. */
444
+ declare function normalizePath(path: string): string;
445
+ /**
446
+ * Split a full BIP-44 leaf path (`purpose'/coin'/account'/change/index`, 5
447
+ * segments) into its 3-segment account path and the relative `change/index`
448
+ * path from that account to the leaf. Matches the OneKey Keystone air-gap
449
+ * demo's `removePathLastSegment({removeCount: 2})` convention, which is
450
+ * verified against real Keystone hardware.
451
+ *
452
+ * A path with 3 or fewer segments IS already an account path (or shorter) —
453
+ * BIP-44's account level is exactly 3 hardened components — so there is
454
+ * nothing to split off: `relativeDerivePath` is empty and `accountPath` is
455
+ * the (normalized) input unchanged.
456
+ */
457
+ declare function splitAccountPath(path: string): {
458
+ accountPath: string;
459
+ relativeDerivePath: string;
460
+ };
461
+ /**
462
+ * Standard BIP-44/49/84/86 purpose → script-type mapping. Returns `undefined`
463
+ * for a path whose purpose isn't one of these four (or isn't parseable),
464
+ * rather than guessing.
465
+ */
466
+ declare function btcScriptTypeFromPath(path: string): BtcScriptType | undefined;
467
+
468
+ export { type BtcScriptType, type ImportFromQrOptions, type KeystoneAccountEntry, KeystoneAdapter, type KeystoneBtcSignRequestAccount, type KeystoneBtcSignatureResult, type KeystoneDerivationCurve, type KeystoneDeviceRecord, type KeystoneEthSignRequestInput, type KeystoneEthSignatureResult, type KeystoneKeyDerivationRequestInput, type KeystoneKeySchema, type KeystoneParsedAccount, type KeystoneParsedMultiAccounts, type KeystoneSolSignRequestInput, type KeystoneSolSignatureResult, type KeystoneTronSignRequestInput, type KeystoneTronSignatureResult, type KeystoneUr, KeystoneUrEngine, QR_CONNECT_ID_PREFIX, accountKey, btcScriptTypeFromPath, createDeviceRecord, normalizePath, placeholderDeviceInfo, qrConnectId, splitAccountPath, toDeviceInfo };