@onekeyfe/hd-core 1.2.2-alpha.9 → 1.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (81) hide show
  1. package/README.md +1 -1
  2. package/__tests__/AllNetworkGetAddressBase.tracing.test.ts +503 -10
  3. package/__tests__/DeviceCommands.test.ts +254 -1
  4. package/__tests__/core-error-output.test.ts +169 -1
  5. package/__tests__/device-lifecycle-events.test.ts +395 -15
  6. package/__tests__/logBlockEvent.test.ts +45 -122
  7. package/__tests__/open-wallet-session-error-response.test.ts +2 -2
  8. package/__tests__/open-wallet-session.test.ts +8 -358
  9. package/__tests__/protocol-v2.test.ts +130 -0
  10. package/__tests__/public-device-state-api.test.ts +2 -7
  11. package/__tests__/search-devices.test.ts +196 -8
  12. package/__tests__/sol-sign-offchain-message.test.ts +64 -0
  13. package/dist/api/FirmwareUpdateV4.d.ts +1 -0
  14. package/dist/api/FirmwareUpdateV4.d.ts.map +1 -1
  15. package/dist/api/GetFeatures.d.ts.map +1 -1
  16. package/dist/api/GetPassphraseState.d.ts.map +1 -1
  17. package/dist/api/OpenWalletSession.d.ts.map +1 -1
  18. package/dist/api/SearchDevices.d.ts +2 -15
  19. package/dist/api/SearchDevices.d.ts.map +1 -1
  20. package/dist/api/UploadPortfolio.d.ts +1 -0
  21. package/dist/api/UploadPortfolio.d.ts.map +1 -1
  22. package/dist/api/allnetwork/AllNetworkGetAddress.d.ts +2 -0
  23. package/dist/api/allnetwork/AllNetworkGetAddress.d.ts.map +1 -1
  24. package/dist/api/allnetwork/AllNetworkGetAddressBase.d.ts +7 -1
  25. package/dist/api/allnetwork/AllNetworkGetAddressBase.d.ts.map +1 -1
  26. package/dist/api/allnetwork/AllNetworkGetAddressByLoop.d.ts.map +1 -1
  27. package/dist/api/device/DeviceVerify.d.ts.map +1 -1
  28. package/dist/api/solana/SolSignOffchainMessage.d.ts.map +1 -1
  29. package/dist/core/RequestQueue.d.ts +1 -0
  30. package/dist/core/RequestQueue.d.ts.map +1 -1
  31. package/dist/core/index.d.ts +3 -1
  32. package/dist/core/index.d.ts.map +1 -1
  33. package/dist/core/uiPromiseRegistry.d.ts +1 -1
  34. package/dist/data-manager/TransportManager.d.ts +2 -0
  35. package/dist/data-manager/TransportManager.d.ts.map +1 -1
  36. package/dist/device/Device.d.ts +2 -0
  37. package/dist/device/Device.d.ts.map +1 -1
  38. package/dist/device/DeviceCommands.d.ts +5 -4
  39. package/dist/device/DeviceCommands.d.ts.map +1 -1
  40. package/dist/events/logBlockEvent.d.ts.map +1 -1
  41. package/dist/index.d.ts +35 -30
  42. package/dist/index.js +587 -314
  43. package/dist/protocols/protocol-v2/walletSession.d.ts.map +1 -1
  44. package/dist/types/api/getFeatures.d.ts.map +1 -1
  45. package/dist/types/api/getPassphraseState.d.ts.map +1 -1
  46. package/dist/types/api/openWalletSession.d.ts +1 -10
  47. package/dist/types/api/openWalletSession.d.ts.map +1 -1
  48. package/dist/types/api/protocolV2.d.ts +3 -4
  49. package/dist/types/api/protocolV2.d.ts.map +1 -1
  50. package/dist/types/api/solSignOffchainMessage.d.ts +1 -0
  51. package/dist/types/api/solSignOffchainMessage.d.ts.map +1 -1
  52. package/dist/types/params.d.ts.map +1 -1
  53. package/dist/utils/patch.d.ts +1 -1
  54. package/dist/utils/patch.d.ts.map +1 -1
  55. package/package.json +4 -4
  56. package/src/api/FirmwareUpdateV4.ts +9 -4
  57. package/src/api/GetFeatures.ts +1 -0
  58. package/src/api/GetPassphraseState.ts +1 -0
  59. package/src/api/OpenWalletSession.ts +7 -77
  60. package/src/api/SearchDevices.ts +121 -27
  61. package/src/api/UploadPortfolio.ts +5 -4
  62. package/src/api/allnetwork/AllNetworkGetAddress.ts +79 -45
  63. package/src/api/allnetwork/AllNetworkGetAddressBase.ts +95 -24
  64. package/src/api/allnetwork/AllNetworkGetAddressByLoop.ts +3 -0
  65. package/src/api/device/DeviceVerify.ts +8 -0
  66. package/src/api/solana/SolSignOffchainMessage.ts +44 -4
  67. package/src/core/RequestQueue.ts +20 -0
  68. package/src/core/index.ts +159 -52
  69. package/src/data/messages/messages-protocol-v2.json +49 -33
  70. package/src/data/messages/messages.json +8 -5
  71. package/src/data-manager/TransportManager.ts +14 -3
  72. package/src/device/Device.ts +58 -27
  73. package/src/device/DeviceCommands.ts +29 -2
  74. package/src/events/logBlockEvent.ts +6 -75
  75. package/src/protocols/protocol-v2/walletSession.ts +14 -2
  76. package/src/types/api/getFeatures.ts +2 -1
  77. package/src/types/api/getPassphraseState.ts +2 -5
  78. package/src/types/api/openWalletSession.ts +7 -19
  79. package/src/types/api/protocolV2.ts +3 -4
  80. package/src/types/api/solSignOffchainMessage.ts +2 -0
  81. package/src/types/params.ts +7 -0
@@ -297,6 +297,8 @@ export class Device extends EventEmitter {
297
297
  /** Resolves only after the active run has completed its release path. */
298
298
  private runCleanupPromise?: Promise<void>;
299
299
 
300
+ private userInterruption?: { attempt: number; promise: Promise<void> };
301
+
300
302
  externalState: string[] = [];
301
303
 
302
304
  unavailableCapabilities: UnavailableCapabilities = {};
@@ -1462,6 +1464,10 @@ export class Device extends EventEmitter {
1462
1464
  this.invalidateProtocolV2RuntimeState();
1463
1465
  }
1464
1466
 
1467
+ waitForRunCleanup(): Promise<void> {
1468
+ return this.runCleanupPromise ?? Promise.resolve();
1469
+ }
1470
+
1465
1471
  async run(fn?: () => Promise<void>, options?: RunOptions) {
1466
1472
  if (this.runPromise) {
1467
1473
  await this.interruptionFromOutside();
@@ -1596,45 +1602,70 @@ export class Device extends EventEmitter {
1596
1602
  }
1597
1603
  }
1598
1604
 
1599
- async interruptionFromUser() {
1605
+ interruptionFromUser(): Promise<void> {
1606
+ const attempt = this.connectionAttempt;
1607
+ if (this.userInterruption?.attempt === attempt) {
1608
+ return this.userInterruption.promise;
1609
+ }
1600
1610
  const error = ERRORS.TypedError(HardwareErrorCode.DeviceInterruptedFromUser);
1601
- this.interruptedAttempt = this.connectionAttempt;
1611
+ this.interruptedAttempt = attempt;
1602
1612
  const cleanupPromise = this.runCleanupPromise;
1603
- const { cancelableAction } = this;
1604
- if (cancelableAction) {
1605
- await cancelableAction(error);
1606
- } else if (this.shouldSendFallbackProtocolCancel()) {
1607
- await this.commands?.cancelDevice?.().catch(cancelError => {
1608
- Log.debug('Protocol V2 fallback cancel error', cancelError);
1609
- });
1610
- } else if (!this.hasDeviceAcquire()) {
1611
- // Pairing / connect-native / probe: drop the physical link only.
1612
- // Never acquire or send protocol Cancel just to abort setup.
1613
- if (this.mainId && this.deviceConnector?.disconnect) {
1614
- await this.deviceConnector.disconnect(this.mainId).catch(disconnectError => {
1615
- Log.debug('Ignored disconnect during user cancel without acquire', disconnectError);
1616
- });
1613
+ const { cancelableAction, commands, runPromise, deviceConnector, mainId } = this;
1614
+ const sendFallbackCancel = this.shouldSendFallbackProtocolCancel();
1615
+ const acquired = this.hasDeviceAcquire();
1616
+ const promise = (async () => {
1617
+ try {
1618
+ if (cancelableAction) {
1619
+ await cancelableAction(error);
1620
+ } else if (sendFallbackCancel) {
1621
+ await commands?.cancelDevice?.().catch(cancelError => {
1622
+ Log.debug('Protocol V2 fallback cancel error', cancelError);
1623
+ });
1624
+ } else if (!acquired) {
1625
+ // Abort setup without acquiring a session just to send Cancel.
1626
+ if (mainId && deviceConnector?.disconnect) {
1627
+ // The connector drops the link silently, so without this line a
1628
+ // user-cancel teardown is indistinguishable in field logs from an
1629
+ // idle keep-alive release or a device that left on its own.
1630
+ Log.debug(
1631
+ 'interruptionFromUser: disconnecting device without acquire, mainId:',
1632
+ mainId
1633
+ );
1634
+ await deviceConnector.disconnect(mainId);
1635
+ }
1636
+ if (this.connectionAttempt === attempt) this.markTransportDisconnected();
1637
+ }
1638
+ await commands?.cancel();
1639
+ } catch (cleanupError) {
1640
+ Log.warn('User cancellation cleanup failed; disconnecting device', cleanupError);
1641
+ if (mainId && deviceConnector?.disconnect) {
1642
+ await deviceConnector.disconnect(mainId).catch(disconnectError => {
1643
+ Log.debug('User cancellation fallback disconnect failed', disconnectError);
1644
+ });
1645
+ }
1646
+ if (this.connectionAttempt === attempt) this.markTransportDisconnected();
1647
+ } finally {
1648
+ runPromise?.reject(error);
1649
+ if (this.runPromise === runPromise) this.runPromise = null;
1650
+ await cleanupPromise?.catch(() => undefined);
1617
1651
  }
1618
- this.markTransportDisconnected();
1619
- }
1620
- await this.commands?.cancel();
1621
-
1622
- if (this.runPromise) {
1623
- this.runPromise.reject(error);
1624
- this.runPromise = null;
1625
- }
1626
- await cleanupPromise?.catch(() => undefined);
1652
+ })();
1653
+ // Keep the settled promise for this attempt so a duplicate close/finally
1654
+ // cannot send a second wire Cancel. A new attempt gets its own cleanup.
1655
+ this.userInterruption = { attempt, promise };
1656
+ return promise;
1627
1657
  }
1628
1658
 
1629
1659
  setCancelableAction(callback: (err?: Error) => Promise<unknown>) {
1630
- this.cancelableAction = (e?: Error) =>
1660
+ const action = (e?: Error) =>
1631
1661
  callback(e)
1632
1662
  .catch(e2 => {
1633
1663
  Log.debug('cancelableAction error', e2);
1634
1664
  })
1635
1665
  .finally(() => {
1636
- this.clearCancelableAction();
1666
+ if (this.cancelableAction === action) this.clearCancelableAction();
1637
1667
  });
1668
+ this.cancelableAction = action;
1638
1669
  }
1639
1670
 
1640
1671
  clearCancelableAction() {
@@ -11,7 +11,7 @@ import {
11
11
  } from '@onekeyfe/hd-transport';
12
12
 
13
13
  import TransportManager from '../data-manager/TransportManager';
14
- import { LoggerNames, getLogger, patchFeatures } from '../utils';
14
+ import { LoggerNames, getLogger, patchFeatures, wait } from '../utils';
15
15
  import { DEVICE, type PassphraseRequestPayload } from '../events';
16
16
  import { DeviceModelToTypes } from '../types';
17
17
  import {
@@ -198,6 +198,8 @@ export class DeviceCommands {
198
198
 
199
199
  disposed: boolean;
200
200
 
201
+ private disposalToken?: object;
202
+
201
203
  callPromise?: Promise<DefaultMessageResponse>;
202
204
 
203
205
  constructor(device: Device, mainId: string) {
@@ -212,6 +214,7 @@ export class DeviceCommands {
212
214
 
213
215
  async dispose(_cancelRequest: boolean) {
214
216
  this.disposed = true;
217
+ this.disposalToken = {};
215
218
  await this.transport.cancel?.();
216
219
  }
217
220
 
@@ -439,7 +442,31 @@ export class DeviceCommands {
439
442
  msg?: DefaultMessageResponse['message'],
440
443
  options?: TransportCallOptions
441
444
  ) {
442
- const resp = await this.call(type, msg, options);
445
+ const { disposalToken } = this;
446
+ let resp = await this.call(type, msg, options);
447
+ // Session busy failures precede wallet derivation or reject an occupied flow.
448
+ // Three retries cover the 240 ms PIN dismissal animation on older V2 firmware.
449
+ for (
450
+ let retry = 0;
451
+ retry < 3 &&
452
+ this.device.isProtocolV2() &&
453
+ DEVICE_SESSION_CALLS.has(type) &&
454
+ resp.type === 'Failure' &&
455
+ (resp.message.code as string | FailureType) === 'Failure_ProcessError' &&
456
+ resp.message.subcode === DeviceSessionErrorCode.DeviceSessionError_Busy;
457
+ retry += 1
458
+ ) {
459
+ await wait(100);
460
+ if (this.device.wasInterruptedByUser()) {
461
+ throw ERRORS.TypedError(HardwareErrorCode.DeviceInterruptedFromUser);
462
+ }
463
+ this.checkDisposed();
464
+ if (this.disposalToken !== disposalToken) {
465
+ // React Native can revive this instance. A stale retry must not release the new run.
466
+ throw ERRORS.TypedError(HardwareErrorCode.RuntimeError, 'DeviceCommands lifecycle changed');
467
+ }
468
+ resp = await this.call(type, msg, options);
469
+ }
443
470
  return this._filterCommonTypes(resp, type, options);
444
471
  }
445
472
 
@@ -8,8 +8,9 @@ export const LogBlockEvent: Set<string> = new Set([
8
8
  UI_RESPONSE.RECEIVE_PASSPHRASE,
9
9
  ]);
10
10
 
11
- const LogLabelMethod: Set<string> = new Set([
12
- 'openWalletSession',
11
+ // These resource/file APIs did not exist in 1.1.32. Skip the payload so
12
+ // the log layer does not copy huge Base64 or binary data.
13
+ const LogPayloadBlockMethod: Set<string> = new Set([
13
14
  'deviceUploadNft',
14
15
  'deviceUploadWallpaper',
15
16
  'uploadPortfolio',
@@ -17,61 +18,6 @@ const LogLabelMethod: Set<string> = new Set([
17
18
  'fileRead',
18
19
  ]);
19
20
 
20
- // 资源上传参数可能包含很大的 Base64 字符串。这里按方法整段跳过,避免日志层
21
- // 递归复制和序列化这些数据;资源 API 与传输内容本身保持不变。
22
- const LogPayloadBlockMethod: Set<string> = new Set([
23
- 'deviceUploadNft',
24
- 'deviceUploadWallpaper',
25
- 'uploadPortfolio',
26
- ]);
27
-
28
- const SensitiveLogKeys: Set<string> = new Set([
29
- 'devicestate',
30
- 'entropy',
31
- 'expectedpassphrasestate',
32
- 'mnemonic',
33
- 'passphrase',
34
- 'passphrasestate',
35
- 'password',
36
- 'pin',
37
- 'privatekey',
38
- 'seed',
39
- 'session',
40
- 'sessionid',
41
- 'walletsessionid',
42
- 'xprv',
43
- ]);
44
-
45
- const normalizeLogKey = (key: string) => key.replace(/[_-]/g, '').toLowerCase();
46
-
47
- const isSigningMethod = (methodName: string) => /sign/i.test(methodName);
48
-
49
- const redactLogValue = (value: unknown, seen: WeakSet<object>): unknown => {
50
- if (ArrayBuffer.isView(value)) {
51
- return `[BINARY:${value.byteLength}]`;
52
- }
53
- if (value instanceof ArrayBuffer) {
54
- return `[BINARY:${value.byteLength}]`;
55
- }
56
- if (Array.isArray(value)) {
57
- return value.map(item => redactLogValue(item, seen));
58
- }
59
- if (!value || typeof value !== 'object') return value;
60
- if (seen.has(value)) return '[CIRCULAR]';
61
-
62
- seen.add(value);
63
- const redacted = Object.fromEntries(
64
- Object.entries(value as Record<string, unknown>).map(([key, item]) => [
65
- key,
66
- SensitiveLogKeys.has(normalizeLogKey(key)) && item !== null && item !== undefined
67
- ? '[REDACTED]'
68
- : redactLogValue(item, seen),
69
- ])
70
- );
71
- seen.delete(value);
72
- return redacted;
73
- };
74
-
75
21
  export function getLogBlockLabel(message: unknown): string | undefined {
76
22
  if (!message || typeof message !== 'object') return undefined;
77
23
 
@@ -86,7 +32,7 @@ export function getLogBlockLabel(message: unknown): string | undefined {
86
32
  }
87
33
 
88
34
  const methodName = method ?? payload?.method;
89
- if (methodName && (LogLabelMethod.has(methodName) || isSigningMethod(methodName))) {
35
+ if (methodName && LogPayloadBlockMethod.has(methodName)) {
90
36
  return methodName;
91
37
  }
92
38
 
@@ -94,25 +40,10 @@ export function getLogBlockLabel(message: unknown): string | undefined {
94
40
  }
95
41
 
96
42
  export function getSafeLogPayload(value: unknown, blockLabel?: string): unknown {
97
- if (
98
- blockLabel &&
99
- (LogBlockEvent.has(blockLabel) ||
100
- LogPayloadBlockMethod.has(blockLabel) ||
101
- isSigningMethod(blockLabel))
102
- ) {
43
+ if (blockLabel) {
103
44
  return { method: blockLabel, payload: '[REDACTED]' };
104
45
  }
105
-
106
- const redactedValue = redactLogValue(value, new WeakSet());
107
- if (
108
- blockLabel &&
109
- redactedValue &&
110
- typeof redactedValue === 'object' &&
111
- !Array.isArray(redactedValue)
112
- ) {
113
- return { ...redactedValue, method: blockLabel };
114
- }
115
- return redactedValue;
46
+ return value;
116
47
  }
117
48
 
118
49
  export function formatLogMethodLabel(label: string, methodName?: string): string {
@@ -234,9 +234,9 @@ export async function getProtocolV2WalletSession(
234
234
  const forceWalletSelection =
235
235
  options?.forceWalletSelection === true || options?.initSession === true;
236
236
  const readCurrentAttachPinSession = options?.readCurrentAttachPinSession === true;
237
- const sessionIsAttachPinWallet = (session?: { viaAttachPin?: boolean }) =>
237
+ const sessionIsAttachPinWallet = (session?: unknown) =>
238
238
  readCurrentAttachPinSession ||
239
- session?.viaAttachPin === true ||
239
+ (session as { viaAttachPin?: boolean } | undefined)?.viaAttachPin === true ||
240
240
  device.features?.unlockedAttachPin === true;
241
241
 
242
242
  if (forceWalletSelection) {
@@ -268,6 +268,7 @@ export async function getProtocolV2WalletSession(
268
268
  ? device.getInternalState()
269
269
  : undefined;
270
270
  let response;
271
+ let walletSelectionRequested = false;
271
272
  let resumed = false;
272
273
  let walletStatusRefreshed = false;
273
274
  const markWalletStatusRefreshed = () => {
@@ -455,6 +456,11 @@ export async function getProtocolV2WalletSession(
455
456
  throw error;
456
457
  }
457
458
  resumed = false;
459
+ if (device.features?.unlockedAttachPin === true) {
460
+ // Locking invalidates the old handle, but Attach PIN already selected the wallet.
461
+ // Read the current session and validate its passphrase state below.
462
+ response = await getDeviceSession(device, sessionGetRequest());
463
+ }
458
464
  }
459
465
  } else if (expectedPassphraseState) {
460
466
  try {
@@ -477,6 +483,7 @@ export async function getProtocolV2WalletSession(
477
483
  throw ERRORS.TypedError(HardwareErrorCode.WalletSessionInvalid);
478
484
  }
479
485
  await lockAttachPinBeforePassphraseSelection();
486
+ walletSelectionRequested = true;
480
487
  response = await selectDeviceSession(
481
488
  device,
482
489
  expectedPassphraseState,
@@ -503,6 +510,11 @@ export async function getProtocolV2WalletSession(
503
510
  device.clearInternalState();
504
511
  throw ERRORS.TypedError(HardwareErrorCode.WalletSessionInvalid);
505
512
  }
513
+ // Report a fresh selection mismatch before asking for input again.
514
+ if (walletSelectionRequested) {
515
+ clearCurrentWalletSession();
516
+ throw ERRORS.TypedError(HardwareErrorCode.DeviceCheckPassphraseStateError);
517
+ }
506
518
  if (options?.onlyMainPin) {
507
519
  device.clearStandardInternalState?.();
508
520
  await selectStandardWallet(true);
@@ -2,6 +2,7 @@ import type { CommonParams, Response } from '../params';
2
2
  import type { Features } from '../device';
3
3
 
4
4
  /**
5
- * @deprecated Use `getDeviceState` for both Protocol V1 and Protocol V2 devices.
5
+ * @deprecated Use `getDeviceState` for both Protocol V1 and Protocol V2.
6
+ * Existing calls still work. Read `payload.identity.deviceId` from `getDeviceState`.
6
7
  */
7
8
  export declare function getFeatures(connectId?: string, params?: CommonParams): Response<Features>;
@@ -3,11 +3,8 @@ import type { CommonParams, Response } from '../params';
3
3
  export type GetPassphraseStateParams = CommonParams;
4
4
 
5
5
  /**
6
- * Compatibility wallet-state API for existing App integrations.
7
- *
8
- * Protocol V1 keeps its parameterless firmware flow. Protocol V2 maps the existing
9
- * `useEmptyPassphrase` and `initSession` intent to the device-only wallet-session flow.
10
- * New integrations should prefer `openWalletSession` for explicit wallet intent.
6
+ * @deprecated Use `openWalletSession` for both Protocol V1 and Protocol V2.
7
+ * Existing calls still work. Persist `deviceId + passphraseState`; do not persist firmware `session_id`.
11
8
  */
12
9
  export declare function getPassphraseState(
13
10
  connectId?: string,
@@ -3,28 +3,16 @@ import type { CommonParams, Response } from '../params';
3
3
  export const OpenWalletSessionMode = {
4
4
  Standard: 'standard',
5
5
  SelectHidden: 'select-hidden',
6
- ResumeHidden: 'resume-hidden',
7
6
  } as const;
8
7
 
9
8
  export type OpenWalletSessionModeValue =
10
9
  (typeof OpenWalletSessionMode)[keyof typeof OpenWalletSessionMode];
11
10
 
12
- export type OpenWalletSessionParams =
13
- | {
14
- mode: typeof OpenWalletSessionMode.Standard;
15
- deviceId?: never;
16
- passphraseState?: never;
17
- }
18
- | {
19
- mode: typeof OpenWalletSessionMode.SelectHidden;
20
- deviceId?: never;
21
- passphraseState?: never;
22
- }
23
- | {
24
- mode: typeof OpenWalletSessionMode.ResumeHidden;
25
- deviceId: string;
26
- passphraseState: string;
27
- };
11
+ export type OpenWalletSessionParams = {
12
+ mode: OpenWalletSessionModeValue;
13
+ deviceId?: never;
14
+ passphraseState?: never;
15
+ };
28
16
 
29
17
  type OpenWalletSessionPayloadBase = {
30
18
  protocol: 'V1' | 'V2';
@@ -45,8 +33,8 @@ export type OpenWalletSessionPayload = OpenWalletSessionPayloadBase &
45
33
  );
46
34
 
47
35
  /**
48
- * Opens the standard, hidden, or Attach-to-PIN wallet flow through a unified
49
- * Protocol V1/V2 API.
36
+ * Opens the standard or hidden wallet through a unified Protocol V1/V2 API.
37
+ * Resume a bound hidden wallet by passing passphraseState on later methods.
50
38
  */
51
39
  export declare function openWalletSession(
52
40
  connectId: string,
@@ -1,6 +1,7 @@
1
1
  import type { CommonParams, Response } from '../params';
2
2
  import type { OnboardingStatus, Success } from '@onekeyfe/hd-transport';
3
3
  import type { DeviceRebootParams } from '../../api/protocol-v2/helpers';
4
+ import type { UploadPortfolioParams } from '../../api/UploadPortfolio';
4
5
  import type {
5
6
  DeviceUploadWallpaperParams,
6
7
  DeviceUploadWallpaperResponse,
@@ -12,6 +13,7 @@ import type {
12
13
 
13
14
  // Re-export implementation parameter types as the single source of truth.
14
15
  export type { DeviceRebootParams, RebootTypeInput } from '../../api/protocol-v2/helpers';
16
+ export type { UploadPortfolioParams } from '../../api/UploadPortfolio';
15
17
  export type {
16
18
  DeviceUploadWallpaperParams,
17
19
  DeviceUploadWallpaperResponse,
@@ -67,8 +69,5 @@ export declare function deviceUploadNft(
67
69
 
68
70
  export declare function uploadPortfolio(
69
71
  connectId: string,
70
- params: {
71
- packageBase64: string;
72
- timeoutMs?: number | string;
73
- }
72
+ params: UploadPortfolioParams
74
73
  ): Response<FileInfo & { portfolioUpdated: true }>;
@@ -15,6 +15,8 @@ export type SolSignOffchainMessageParams = {
15
15
  messageVersion?: SolanaOffChainMessageVersion;
16
16
  messageFormat?: SolanaOffChainMessageFormat;
17
17
  applicationDomainHex?: string;
18
+ /** 32-byte public keys encoded as hex, strictly sorted and unique. */
19
+ requiredSigners?: string[];
18
20
  };
19
21
 
20
22
  export declare function solSignOffchainMessage(
@@ -1,6 +1,13 @@
1
1
  import type { HardwareConnectProtocol } from '@onekeyfe/hd-shared';
2
2
 
3
3
  export interface CommonParams {
4
+ /**
5
+ * Keep the transport session after this call instead of releasing it.
6
+ * This is a Device.run() hold flag, not a wallet identity. It does not replace
7
+ * `useEmptyPassphrase`, `passphraseState`, or `openWalletSession`.
8
+ * USB multi-step flows and Electron BLE firmware windows still need this to
9
+ * avoid dropping the link between calls.
10
+ */
4
11
  keepSession?: boolean;
5
12
  /**
6
13
  * polling connect max retry count