@onekeyfe/hd-core 1.2.0-alpha.0 → 1.2.0-alpha.10

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 (192) hide show
  1. package/__tests__/connectSettings.test.ts +19 -0
  2. package/__tests__/device-wallet-session-store.test.ts +131 -0
  3. package/__tests__/preInitialize.test.ts +33 -0
  4. package/__tests__/protocol-v2-firmware-targets.test.ts +19 -0
  5. package/__tests__/protocol-v2.test.ts +1930 -765
  6. package/dist/api/ClearSessionCache.d.ts +9 -0
  7. package/dist/api/ClearSessionCache.d.ts.map +1 -0
  8. package/dist/api/FileRead.d.ts.map +1 -1
  9. package/dist/api/FileWrite.d.ts.map +1 -1
  10. package/dist/api/FirmwareUpdateV2.d.ts.map +1 -1
  11. package/dist/api/FirmwareUpdateV3.d.ts.map +1 -1
  12. package/dist/api/FirmwareUpdateV4.d.ts +26 -5
  13. package/dist/api/FirmwareUpdateV4.d.ts.map +1 -1
  14. package/dist/api/GetDeviceInfo.d.ts.map +1 -1
  15. package/dist/api/GetFeatures.d.ts +1 -1
  16. package/dist/api/GetOnekeyFeatures.d.ts.map +1 -1
  17. package/dist/api/GetPassphraseState.d.ts +4 -4
  18. package/dist/api/GetPassphraseState.d.ts.map +1 -1
  19. package/dist/api/device/DeviceUnlock.d.ts +1 -1
  20. package/dist/api/firmware/FirmwareUpdateBaseMethod.d.ts +1 -0
  21. package/dist/api/firmware/FirmwareUpdateBaseMethod.d.ts.map +1 -1
  22. package/dist/api/firmware/bootloaderHelper.d.ts.map +1 -1
  23. package/dist/api/firmware/getBinary.d.ts +4 -0
  24. package/dist/api/firmware/getBinary.d.ts.map +1 -1
  25. package/dist/api/firmware/progressThrottle.d.ts +3 -0
  26. package/dist/api/firmware/progressThrottle.d.ts.map +1 -0
  27. package/dist/api/firmware/uploadFirmware.d.ts.map +1 -1
  28. package/dist/api/index.d.ts +8 -10
  29. package/dist/api/index.d.ts.map +1 -1
  30. package/dist/api/protocol-v2/DeviceFactoryInfoGet.d.ts +6 -0
  31. package/dist/api/protocol-v2/DeviceFactoryInfoGet.d.ts.map +1 -0
  32. package/dist/api/protocol-v2/DeviceFactoryInfoSet.d.ts +7 -0
  33. package/dist/api/protocol-v2/DeviceFactoryInfoSet.d.ts.map +1 -0
  34. package/dist/api/protocol-v2/DeviceFirmwareUpdate.d.ts +1 -1
  35. package/dist/api/protocol-v2/DeviceFirmwareUpdate.d.ts.map +1 -1
  36. package/dist/api/protocol-v2/DeviceGetFirmwareUpdateStatus.d.ts +3 -2
  37. package/dist/api/protocol-v2/DeviceGetFirmwareUpdateStatus.d.ts.map +1 -1
  38. package/dist/api/protocol-v2/{DeviceGetDeviceInfo.d.ts → DeviceInfoGet.d.ts} +10 -10
  39. package/dist/api/protocol-v2/DeviceInfoGet.d.ts.map +1 -0
  40. package/dist/api/protocol-v2/DeviceSessionGet.d.ts +9 -0
  41. package/dist/api/protocol-v2/DeviceSessionGet.d.ts.map +1 -0
  42. package/dist/api/protocol-v2/DeviceStatusGet.d.ts +6 -0
  43. package/dist/api/protocol-v2/DeviceStatusGet.d.ts.map +1 -0
  44. package/dist/api/protocol-v2/FilesystemFormat.d.ts.map +1 -1
  45. package/dist/api/protocol-v2/{FilesystemFixPermission.d.ts → FilesystemPermissionFix.d.ts} +2 -2
  46. package/dist/api/protocol-v2/FilesystemPermissionFix.d.ts.map +1 -0
  47. package/dist/api/protocol-v2/ProtocolInfoRequest.d.ts +6 -0
  48. package/dist/api/protocol-v2/ProtocolInfoRequest.d.ts.map +1 -0
  49. package/dist/api/protocol-v2/helpers.d.ts +27 -19
  50. package/dist/api/protocol-v2/helpers.d.ts.map +1 -1
  51. package/dist/api/ton/TonSignData.d.ts +1 -5
  52. package/dist/api/ton/TonSignData.d.ts.map +1 -1
  53. package/dist/api/tron/TronSignMessage.d.ts +0 -1
  54. package/dist/api/tron/TronSignMessage.d.ts.map +1 -1
  55. package/dist/api/tron/TronSignTransaction.d.ts +0 -1
  56. package/dist/api/tron/TronSignTransaction.d.ts.map +1 -1
  57. package/dist/constants/index.d.ts +2 -1
  58. package/dist/constants/index.d.ts.map +1 -1
  59. package/dist/core/index.d.ts.map +1 -1
  60. package/dist/data-manager/DataManager.d.ts +1 -1
  61. package/dist/data-manager/DataManager.d.ts.map +1 -1
  62. package/dist/data-manager/connectSettings.d.ts.map +1 -1
  63. package/dist/device/Device.d.ts +17 -18
  64. package/dist/device/Device.d.ts.map +1 -1
  65. package/dist/device/DeviceWalletSessionStore.d.ts +15 -0
  66. package/dist/device/DeviceWalletSessionStore.d.ts.map +1 -0
  67. package/dist/deviceProfile/buildDeviceFeatures.d.ts +6 -0
  68. package/dist/deviceProfile/buildDeviceFeatures.d.ts.map +1 -0
  69. package/dist/deviceProfile/buildDeviceProfile.d.ts +3 -4
  70. package/dist/deviceProfile/buildDeviceProfile.d.ts.map +1 -1
  71. package/dist/deviceProfile/index.d.ts +1 -1
  72. package/dist/deviceProfile/index.d.ts.map +1 -1
  73. package/dist/index.d.ts +657 -539
  74. package/dist/index.js +2579 -2161
  75. package/dist/inject.d.ts.map +1 -1
  76. package/dist/protocols/protocol-v2/features.d.ts +12 -14
  77. package/dist/protocols/protocol-v2/features.d.ts.map +1 -1
  78. package/dist/protocols/protocol-v2/firmware.d.ts +11 -9
  79. package/dist/protocols/protocol-v2/firmware.d.ts.map +1 -1
  80. package/dist/protocols/protocol-v2/index.d.ts +1 -0
  81. package/dist/protocols/protocol-v2/index.d.ts.map +1 -1
  82. package/dist/protocols/protocol-v2/walletSession.d.ts +13 -0
  83. package/dist/protocols/protocol-v2/walletSession.d.ts.map +1 -0
  84. package/dist/types/api/firmwareUpdate.d.ts +4 -6
  85. package/dist/types/api/firmwareUpdate.d.ts.map +1 -1
  86. package/dist/types/api/getDeviceInfo.d.ts +4 -2
  87. package/dist/types/api/getDeviceInfo.d.ts.map +1 -1
  88. package/dist/types/api/getPassphraseState.d.ts +4 -4
  89. package/dist/types/api/getPassphraseState.d.ts.map +1 -1
  90. package/dist/types/api/index.d.ts +11 -11
  91. package/dist/types/api/index.d.ts.map +1 -1
  92. package/dist/types/api/protocolV2.d.ts +16 -17
  93. package/dist/types/api/protocolV2.d.ts.map +1 -1
  94. package/dist/types/api/sessionCache.d.ts +10 -0
  95. package/dist/types/api/sessionCache.d.ts.map +1 -0
  96. package/dist/types/device.d.ts +88 -8
  97. package/dist/types/device.d.ts.map +1 -1
  98. package/dist/types/params.d.ts +1 -0
  99. package/dist/types/params.d.ts.map +1 -1
  100. package/dist/types/settings.d.ts +21 -1
  101. package/dist/types/settings.d.ts.map +1 -1
  102. package/dist/utils/capabilitieUtils.d.ts.map +1 -1
  103. package/dist/utils/deviceFeaturesUtils.d.ts +2 -0
  104. package/dist/utils/deviceFeaturesUtils.d.ts.map +1 -1
  105. package/dist/utils/deviceInfoUtils.d.ts.map +1 -1
  106. package/dist/utils/deviceVersionUtils.d.ts.map +1 -1
  107. package/dist/utils/findDefectiveBatchDevice.d.ts +1 -1
  108. package/dist/utils/patch.d.ts +1 -1
  109. package/dist/utils/patch.d.ts.map +1 -1
  110. package/package.json +4 -4
  111. package/src/api/ClearSessionCache.ts +28 -0
  112. package/src/api/FileRead.ts +16 -3
  113. package/src/api/FileWrite.ts +14 -1
  114. package/src/api/FirmwareUpdateV2.ts +9 -2
  115. package/src/api/FirmwareUpdateV3.ts +2 -1
  116. package/src/api/FirmwareUpdateV4.ts +1089 -285
  117. package/src/api/GetDeviceInfo.ts +6 -13
  118. package/src/api/GetOnekeyFeatures.ts +3 -14
  119. package/src/api/GetPassphraseState.ts +7 -4
  120. package/src/api/device/DeviceRebootToBoardloader.ts +4 -4
  121. package/src/api/device/DeviceRebootToBootloader.ts +4 -4
  122. package/src/api/firmware/FirmwareUpdateBaseMethod.ts +18 -4
  123. package/src/api/firmware/bootloaderHelper.ts +3 -2
  124. package/src/api/firmware/getBinary.ts +1 -1
  125. package/src/api/firmware/progressThrottle.ts +44 -0
  126. package/src/api/firmware/releaseHelper.ts +3 -3
  127. package/src/api/firmware/uploadFirmware.ts +5 -2
  128. package/src/api/index.ts +8 -10
  129. package/src/api/protocol-v2/{FactoryGetDeviceInfo.ts → DeviceFactoryInfoGet.ts} +3 -3
  130. package/src/api/protocol-v2/DeviceFactoryInfoSet.ts +32 -0
  131. package/src/api/protocol-v2/DeviceFirmwareUpdate.ts +3 -5
  132. package/src/api/protocol-v2/DeviceGetFirmwareUpdateStatus.ts +39 -5
  133. package/src/api/protocol-v2/{DeviceGetDeviceInfo.ts → DeviceInfoGet.ts} +52 -22
  134. package/src/api/protocol-v2/DeviceReboot.ts +2 -2
  135. package/src/api/protocol-v2/DeviceSessionGet.ts +26 -0
  136. package/src/api/protocol-v2/DeviceStatusGet.ts +14 -0
  137. package/src/api/protocol-v2/FilesystemFormat.ts +4 -1
  138. package/src/api/protocol-v2/{FilesystemFixPermission.ts → FilesystemPermissionFix.ts} +2 -2
  139. package/src/api/protocol-v2/{GetProtoVersion.ts → ProtocolInfoRequest.ts} +2 -2
  140. package/src/api/protocol-v2/helpers.ts +58 -59
  141. package/src/api/ton/TonSignData.ts +1 -5
  142. package/src/api/tron/TronSignMessage.ts +0 -1
  143. package/src/api/tron/TronSignTransaction.ts +0 -1
  144. package/src/constants/index.ts +10 -1
  145. package/src/core/index.ts +22 -10
  146. package/src/data/messages/messages-protocol-v2.json +992 -1420
  147. package/src/data/messages/messages.json +8 -0
  148. package/src/data-manager/DataManager.ts +11 -4
  149. package/src/data-manager/connectSettings.ts +4 -0
  150. package/src/device/Device.ts +158 -294
  151. package/src/device/DevicePool.ts +9 -9
  152. package/src/device/DeviceWalletSessionStore.ts +78 -0
  153. package/src/deviceProfile/buildDeviceFeatures.ts +365 -0
  154. package/src/deviceProfile/buildDeviceProfile.ts +165 -200
  155. package/src/deviceProfile/index.ts +4 -1
  156. package/src/inject.ts +13 -16
  157. package/src/protocols/protocol-v2/features.ts +28 -67
  158. package/src/protocols/protocol-v2/firmware.ts +12 -36
  159. package/src/protocols/protocol-v2/index.ts +1 -0
  160. package/src/protocols/protocol-v2/walletSession.ts +83 -0
  161. package/src/types/api/firmwareUpdate.ts +16 -20
  162. package/src/types/api/getDeviceInfo.ts +4 -2
  163. package/src/types/api/getPassphraseState.ts +4 -4
  164. package/src/types/api/index.ts +17 -20
  165. package/src/types/api/protocolV2.ts +35 -40
  166. package/src/types/api/sessionCache.ts +14 -0
  167. package/src/types/device.ts +98 -34
  168. package/src/types/params.ts +4 -0
  169. package/src/types/settings.ts +42 -30
  170. package/src/utils/capabilitieUtils.ts +1 -2
  171. package/src/utils/deviceFeaturesUtils.ts +54 -29
  172. package/src/utils/deviceInfoUtils.ts +11 -29
  173. package/src/utils/deviceVersionUtils.ts +7 -25
  174. package/src/utils/findDefectiveBatchDevice.ts +6 -6
  175. package/dist/api/protocol-v2/DevReboot.d.ts +0 -7
  176. package/dist/api/protocol-v2/DevReboot.d.ts.map +0 -1
  177. package/dist/api/protocol-v2/DeviceGetDeviceInfo.d.ts.map +0 -1
  178. package/dist/api/protocol-v2/DeviceGetOnboardingStatus.d.ts +0 -6
  179. package/dist/api/protocol-v2/DeviceGetOnboardingStatus.d.ts.map +0 -1
  180. package/dist/api/protocol-v2/FactoryDeviceInfoSettings.d.ts +0 -7
  181. package/dist/api/protocol-v2/FactoryDeviceInfoSettings.d.ts.map +0 -1
  182. package/dist/api/protocol-v2/FactoryGetDeviceInfo.d.ts +0 -6
  183. package/dist/api/protocol-v2/FactoryGetDeviceInfo.d.ts.map +0 -1
  184. package/dist/api/protocol-v2/FilesystemFixPermission.d.ts.map +0 -1
  185. package/dist/api/protocol-v2/GetProtoVersion.d.ts +0 -6
  186. package/dist/api/protocol-v2/GetProtoVersion.d.ts.map +0 -1
  187. package/dist/deviceProfile/legacyFeaturesView.d.ts +0 -5
  188. package/dist/deviceProfile/legacyFeaturesView.d.ts.map +0 -1
  189. package/src/api/protocol-v2/DevReboot.ts +0 -24
  190. package/src/api/protocol-v2/DeviceGetOnboardingStatus.ts +0 -16
  191. package/src/api/protocol-v2/FactoryDeviceInfoSettings.ts +0 -29
  192. package/src/deviceProfile/legacyFeaturesView.ts +0 -123
@@ -9,6 +9,7 @@ import {
9
9
  HardwareError,
10
10
  HardwareErrorCode,
11
11
  createDeferred,
12
+ createDeviceNotSupportMethodError,
12
13
  } from '@onekeyfe/hd-shared';
13
14
 
14
15
  import {
@@ -31,6 +32,7 @@ import {
31
32
  import { generateInstanceId } from '../utils/tracing';
32
33
  // eslint-disable-next-line import/no-cycle
33
34
  import { DeviceCommands } from './DeviceCommands';
35
+ import { deviceWalletSessionStore } from './DeviceWalletSessionStore';
34
36
  import {
35
37
  type DeviceFirmwareRange,
36
38
  DeviceModelToTypes,
@@ -40,7 +42,6 @@ import {
40
42
  type Features,
41
43
  type IDeviceModel,
42
44
  type IDeviceType,
43
- type IVersionArray,
44
45
  type IVersionRange,
45
46
  type SupportFeatureType,
46
47
  type UnavailableCapabilities,
@@ -54,11 +55,8 @@ import {
54
55
  PROTOCOL_V2_STATUS_DEVICE_INFO_REQUEST,
55
56
  requestProtocolV2DeviceInfo,
56
57
  } from '../protocols/protocol-v2/features';
57
- import {
58
- buildProfileFromProtocolV1,
59
- buildProfileFromProtocolV2,
60
- buildProtocolV2GetFeaturesPayload,
61
- } from '../deviceProfile';
58
+ import { refreshProtocolV2DeviceStatus } from '../protocols/protocol-v2/walletSession';
59
+ import { buildProtocolV1FeaturesPayload, buildProtocolV2FeaturesPayload } from '../deviceProfile';
62
60
 
63
61
  import type { PROTO } from '../constants';
64
62
  import type {
@@ -70,11 +68,11 @@ import type { PassphrasePromptResponse } from './DeviceCommands';
70
68
  import type { Deferred, HardwareConnectProtocol } from '@onekeyfe/hd-shared';
71
69
  import type {
72
70
  OneKeyDeviceInfo as DeviceDescriptor,
71
+ DeviceStatus,
73
72
  ProtocolV2DeviceInfo,
74
73
  Success,
75
74
  } from '@onekeyfe/hd-transport';
76
75
  import type DeviceConnector from './DeviceConnector';
77
- import type { DeviceProfile } from '../types/api/getDeviceInfo';
78
76
 
79
77
  export type InitOptions = {
80
78
  initSession?: boolean;
@@ -82,6 +80,7 @@ export type InitOptions = {
82
80
  passphraseState?: string;
83
81
  deriveCardano?: boolean;
84
82
  connectProtocol?: HardwareConnectProtocol;
83
+ protocolV2DeviceInfoTimeoutMs?: number;
85
84
  };
86
85
 
87
86
  export type RunOptions = {
@@ -96,11 +95,6 @@ const parseRunOptions = (options?: RunOptions): RunOptions => {
96
95
 
97
96
  const Log = getLogger(LoggerNames.Device);
98
97
 
99
- const profileVersionToArray = (version?: string | null): IVersionArray | null => {
100
- if (!version) return null;
101
- return version.split('.').map(part => Number(part) || 0) as IVersionArray;
102
- };
103
-
104
98
  export interface DeviceEvents {
105
99
  [DEVICE.PIN]: [Device, PROTO.PinMatrixRequestType | undefined, (err: any, pin: string) => void];
106
100
  [DEVICE.PASSPHRASE_ON_DEVICE]: [Device, ((response: any) => void)?];
@@ -129,8 +123,6 @@ export interface Device {
129
123
  emit<K extends keyof DeviceEvents>(type: K, ...args: DeviceEvents[K]): boolean;
130
124
  }
131
125
 
132
- const deviceSessionCache: Record<string, string> = {};
133
-
134
126
  /**
135
127
  * Pre-populate the device session cache with a known session ID.
136
128
  *
@@ -148,8 +140,7 @@ export function preloadSessionCache(
148
140
  passphraseState: string,
149
141
  sessionId: string
150
142
  ): void {
151
- const key = `${deviceId}@${passphraseState}`;
152
- deviceSessionCache[key] = sessionId;
143
+ deviceWalletSessionStore.set(deviceId, passphraseState, sessionId);
153
144
  }
154
145
 
155
146
  export class Device extends EventEmitter {
@@ -198,37 +189,17 @@ export class Device extends EventEmitter {
198
189
  /**
199
190
  * 唯一设备状态缓存。
200
191
  *
201
- * V1 直接保存原生 Features;V2 保存由 DevGetDeviceInfo/profile 映射出的
202
- * legacy Features 兼容视图。profile 作为结构化视图按需由 features 派生,
203
- * 不再单独维护第二份缓存。
192
+ * V1 直接保存原生 Features;V2 保存由 DeviceInfoGet 映射出的
193
+ * Features 视图。Device 不再保存 profile,结构化 DeviceProfile 只作为
194
+ * getDeviceInfo() 的 API 返回值存在。
204
195
  */
205
196
  features: Features | undefined = undefined;
206
197
 
207
- private getFeaturesProfile(): DeviceProfile | undefined {
208
- if (!this.features) return undefined;
209
- const protocol = this.originalDescriptor.protocolType === 'V2' ? 'V2' : 'V1';
210
- return buildProfileFromProtocolV1({
211
- protocol,
212
- features: this.features,
213
- onekeyFeatures: this.features as Parameters<typeof buildProfileFromProtocolV1>[0]['onekeyFeatures'],
214
- sources: ['features'],
215
- scope: 'verify',
216
- });
217
- }
218
-
219
- get profile(): DeviceProfile | undefined {
220
- return this.getFeaturesProfile();
221
- }
222
-
223
- set profile(profile: DeviceProfile | undefined) {
224
- this.updateProfile(profile);
225
- }
226
-
227
198
  /**
228
199
  * 是否需要更新设备信息。
229
200
  *
230
201
  * 历史名称保留用于兼容现有调用语义;对 V2 表示 features 需要由
231
- * DevGetDeviceInfo 刷新。
202
+ * DeviceInfoGet 刷新。
232
203
  */
233
204
  featuresNeedsReload = false;
234
205
 
@@ -292,13 +263,14 @@ export class Device extends EventEmitter {
292
263
  const bleName = this.getCurrentBleName();
293
264
  const label = this.getCurrentLabel();
294
265
  const serialNo = this.getCurrentSerialNo();
266
+ const connectId = this.getConnectId();
295
267
  const deviceId = this.getCurrentDeviceId() || null;
296
268
 
297
- const features = this.features;
269
+ const { features } = this;
298
270
 
299
271
  return {
300
272
  /** Android uses Mac address, iOS uses uuid, USB uses uuid */
301
- connectId: DataManager.isBleConnect(env) ? this.mainId || null : serialNo,
273
+ connectId: DataManager.isBleConnect(env) ? this.mainId || null : connectId,
302
274
  /** Hardware ID, will not change at any time */
303
275
  uuid: serialNo,
304
276
  commType: this.originalDescriptor.commType,
@@ -314,8 +286,7 @@ export class Device extends EventEmitter {
314
286
  label: label || 'OneKey',
315
287
  mode: this.getMode(),
316
288
  features,
317
- profile: this.profile,
318
- sessionId: this.features?.session_id ?? null,
289
+ sessionId: this.features?.sessionId ?? null,
319
290
  firmwareVersion: this.getFirmwareVersion(),
320
291
  bleFirmwareVersion: this.getBLEFirmwareVersion(),
321
292
  unavailableCapabilities: this.unavailableCapabilities,
@@ -357,7 +328,10 @@ export class Device extends EventEmitter {
357
328
  });
358
329
  }
359
330
 
360
- async acquire(connectProtocol?: HardwareConnectProtocol) {
331
+ async acquire(
332
+ connectProtocol?: HardwareConnectProtocol,
333
+ options?: { throwOnRunPromiseError?: boolean }
334
+ ) {
361
335
  const env = DataManager.getSettings('env');
362
336
  const mainIdKey = DataManager.isBleConnect(env) ? 'id' : 'session';
363
337
  const expectedProtocol = connectProtocol ?? this.originalDescriptor.protocolType;
@@ -406,6 +380,9 @@ export class Device extends EventEmitter {
406
380
 
407
381
  this.commands = new DeviceCommands(this, this.mainId ?? '');
408
382
  } catch (error) {
383
+ if (options?.throwOnRunPromiseError) {
384
+ throw error;
385
+ }
409
386
  if (this.runPromise) {
410
387
  this.runPromise.reject(error);
411
388
  } else {
@@ -508,10 +485,10 @@ export class Device extends EventEmitter {
508
485
  /**
509
486
  * 唯一协议判别器。
510
487
  *
511
- * descriptor.protocolType 是协议探测后的结果;V2 features 由 DevGetDeviceInfo
512
- * 映射产生,profile 只是 features 的结构化视图。
488
+ * descriptor.protocolType 是协议探测后的结果;V2 features 由 DeviceInfoGet
489
+ * 映射产生。
513
490
  * 全 SDK 的协议分支都必须走这里,不要直接读 originalDescriptor.protocolType
514
- * profile.protocol。
491
+ * 或从 features 反推。
515
492
  */
516
493
  getProtocol(): 'V1' | 'V2' {
517
494
  return this.originalDescriptor.protocolType === 'V2' ? 'V2' : 'V1';
@@ -522,57 +499,53 @@ export class Device extends EventEmitter {
522
499
  }
523
500
 
524
501
  getCurrentDeviceType() {
525
- return this.profile?.deviceType ?? getDeviceType(this.features);
502
+ return getDeviceType(this.features);
526
503
  }
527
504
 
528
505
  getCurrentDeviceId() {
529
- if (this.profile) {
530
- return this.profile.deviceId || undefined;
531
- }
532
- return this.features?.device_id || undefined;
506
+ return this.features?.deviceId || undefined;
533
507
  }
534
508
 
535
509
  getCurrentSerialNo() {
536
- if (this.profile) {
537
- return this.profile.serialNo || '';
538
- }
539
510
  return this.features ? getDeviceUUID(this.features) : '';
540
511
  }
541
512
 
513
+ getConnectId() {
514
+ const serialNo = this.getCurrentSerialNo();
515
+ if (serialNo) return serialNo;
516
+
517
+ // connectId 是 SDK 内部连接路由 key。这里兜底到 transport descriptor,
518
+ // 不改变 features.serialNo / deviceId 的业务语义。
519
+ return this.originalDescriptor.path || this.originalDescriptor.id || '';
520
+ }
521
+
542
522
  getCurrentBleName() {
543
- // V2 不回退 legacy features,避免缓存残留的 V1 数据泄漏到 Pro2 视图
544
- if (this.isProtocolV2()) return this.profile?.bleName ?? null;
545
- return this.profile?.bleName ?? getDeviceBleName(this.features);
523
+ return getDeviceBleName(this.features);
546
524
  }
547
525
 
548
526
  getCurrentLabel() {
549
- if (this.isProtocolV2()) return this.profile?.label ?? null;
550
- return this.profile?.label ?? getDeviceLabel(this.features);
527
+ return getDeviceLabel(this.features);
551
528
  }
552
529
 
553
530
  getCurrentPassphraseProtection() {
554
- if (this.profile) {
555
- return this.profile.status.passphraseProtection;
556
- }
557
- return this.features?.passphrase_protection;
531
+ return this.features?.passphraseProtection;
558
532
  }
559
533
 
560
534
  getCurrentFirmwareType() {
561
- return this.profile?.firmwareType ?? getFirmwareType(this.features);
535
+ return getFirmwareType(this.features);
562
536
  }
563
537
 
564
538
  getCurrentFirmwareVersionString() {
565
- return this.profile?.versions.firmware ?? getDeviceFirmwareVersion(this.features)?.join('.');
539
+ return getDeviceFirmwareVersion(this.features)?.join('.');
566
540
  }
567
541
 
568
542
  getCurrentBLEFirmwareVersionString() {
569
- if (this.profile?.versions.ble) return this.profile.versions.ble;
570
543
  if (!this.features) return undefined;
571
544
  return getDeviceBLEFirmwareVersion(this.features).join('.');
572
545
  }
573
546
 
574
547
  getCurrentSafetyChecks() {
575
- return this.features?.safety_checks;
548
+ return this.features?.safetyChecks;
576
549
  }
577
550
 
578
551
  getCurrentMethodVersionRange(
@@ -656,24 +629,24 @@ export class Device extends EventEmitter {
656
629
  };
657
630
  }
658
631
 
659
- private generateStateKey(deviceId: string, passphraseState?: string) {
660
- if (passphraseState) {
661
- return `${deviceId}@${passphraseState}`;
632
+ private getSessionCacheDeviceKey(_deviceId?: string) {
633
+ const deviceId = _deviceId || this.getCurrentDeviceId();
634
+ if (deviceId) return deviceId;
635
+ if (this.isProtocolV2()) {
636
+ return this.originalDescriptor.path || this.originalDescriptor.id;
662
637
  }
663
- return deviceId;
638
+ return undefined;
664
639
  }
665
640
 
666
641
  getInternalState(_deviceId?: string) {
667
- Log.debug('getInternalState session cache: ', deviceSessionCache);
668
642
  Log.debug(
669
643
  'getInternalState session param: ',
670
644
  `device_id: ${_deviceId}`,
671
- `features.device_id: ${this.features?.device_id}`,
672
- `profile.deviceId: ${this.profile?.deviceId}`,
645
+ `features.deviceId: ${this.features?.deviceId}`,
673
646
  `passphraseState: ${this.passphraseState}`
674
647
  );
675
648
 
676
- const deviceId = _deviceId || this.getCurrentDeviceId();
649
+ const deviceId = this.getSessionCacheDeviceKey(_deviceId);
677
650
  if (!deviceId) return undefined;
678
651
  // Security invariant: no passphraseState → no session lookup.
679
652
  // A previous fallback that scanned `${deviceId}@*` keys could silently
@@ -683,15 +656,14 @@ export class Device extends EventEmitter {
683
656
  // downstream call carries it and hits the primary lookup below.
684
657
  if (!this.passphraseState) return undefined;
685
658
 
686
- const usePassKey = this.generateStateKey(deviceId, this.passphraseState);
687
- return deviceSessionCache[usePassKey];
659
+ return deviceWalletSessionStore.get(deviceId, this.passphraseState);
688
660
  }
689
661
 
690
662
  // attach to pin to fix internal state
691
663
  updateInternalState(
692
664
  enablePassphrase: boolean,
693
665
  passphraseState: string | undefined,
694
- deviceId: string,
666
+ deviceId: string | undefined,
695
667
  sessionId: string | null = null,
696
668
  featuresSessionId: string | null = null
697
669
  ) {
@@ -700,62 +672,52 @@ export class Device extends EventEmitter {
700
672
  `device_id: ${deviceId}`,
701
673
  `enablePassphrase: ${enablePassphrase}`,
702
674
  `passphraseState: ${passphraseState}`,
703
- `sessionId: ${sessionId}`,
704
- `featuresSessionId: ${featuresSessionId}`
675
+ `hasSessionId: ${Boolean(sessionId)}`,
676
+ `hasFeaturesSessionId: ${Boolean(featuresSessionId)}`
705
677
  );
706
678
 
707
- if (enablePassphrase) {
708
- // update the sessionId
709
- if (sessionId) {
710
- deviceSessionCache[this.generateStateKey(deviceId, passphraseState)] = sessionId;
711
- } else if (featuresSessionId) {
712
- deviceSessionCache[this.generateStateKey(deviceId, passphraseState)] = featuresSessionId;
713
- }
714
- }
679
+ const cacheDeviceKey = this.getSessionCacheDeviceKey(deviceId);
680
+ if (!cacheDeviceKey) return;
715
681
 
716
- // delete the old sessionId
717
- const oldKey = `${deviceId}`;
718
- if (deviceSessionCache[oldKey]) {
719
- delete deviceSessionCache[oldKey];
682
+ if (enablePassphrase) {
683
+ const walletSessionId =
684
+ sessionId || featuresSessionId || deviceWalletSessionStore.getPending(cacheDeviceKey);
685
+ deviceWalletSessionStore.set(cacheDeviceKey, passphraseState, walletSessionId ?? undefined);
720
686
  }
721
687
 
722
- Log.debug('updateInternalState session cache: ', deviceSessionCache);
688
+ deviceWalletSessionStore.deletePending(cacheDeviceKey);
723
689
  }
724
690
 
725
691
  private setInternalState(state: string, initSession?: boolean) {
726
692
  Log.debug(
727
693
  'setInternalState session param: ',
728
- `state: ${state}`,
694
+ `hasState: ${Boolean(state)}`,
729
695
  `initSession: ${initSession}`,
730
- `device_id: ${this.features?.device_id}`,
731
- `profile.deviceId: ${this.profile?.deviceId}`,
696
+ `deviceId: ${this.features?.deviceId}`,
732
697
  `passphraseState: ${this.passphraseState}`
733
698
  );
734
699
 
735
700
  if (!this.passphraseState && !initSession) return;
736
701
 
737
- const deviceId = this.getCurrentDeviceId();
702
+ const deviceId = this.getSessionCacheDeviceKey();
738
703
  if (!deviceId) return;
739
704
 
740
- const key = this.generateStateKey(deviceId, this.passphraseState);
741
-
742
- if (state) {
743
- deviceSessionCache[key] = state;
705
+ if (this.passphraseState) {
706
+ deviceWalletSessionStore.set(deviceId, this.passphraseState, state);
707
+ } else if (initSession) {
708
+ deviceWalletSessionStore.setPending(deviceId, state);
744
709
  }
745
- Log.debug('setInternalState done session cache: ', deviceSessionCache);
746
710
  }
747
711
 
748
712
  clearInternalState(_deviceId?: string) {
749
713
  Log.debug('clearInternalState param: ', _deviceId);
750
714
 
751
- const deviceId = _deviceId || this.getCurrentDeviceId();
715
+ const deviceId = this.getSessionCacheDeviceKey(_deviceId);
752
716
  if (!deviceId) return;
753
- const key = `${deviceId}`;
754
- delete deviceSessionCache[key];
717
+ deviceWalletSessionStore.deletePending(deviceId);
755
718
 
756
719
  if (this.passphraseState) {
757
- const usePassKey = this.generateStateKey(deviceId, this.passphraseState);
758
- delete deviceSessionCache[usePassKey];
720
+ deviceWalletSessionStore.delete(deviceId, this.passphraseState);
759
721
  }
760
722
  }
761
723
 
@@ -763,15 +725,17 @@ export class Device extends EventEmitter {
763
725
  // Protocol V2 不支持传统 Initialize,直接使用协议专用初始化流程。
764
726
  if (this.isProtocolV2()) {
765
727
  this.passphraseState = options?.passphraseState;
766
- if (this.profile && !this.featuresNeedsReload && !options?.initSession) {
767
- // 不能直接信任缓存 profile:设备端 wipe / 完成初始化 / 改 label 后
768
- // profile 会永久陈旧。每次 run 做一次轻量 status 刷新(不含 fw/SE,
769
- // 单帧请求开销很小),用 applyProfileUpdate 字段级合并,
770
- // 不会降级已有的 verify / SE versions 数据。
771
- await this._refreshProtocolV2Status();
728
+ if (this.features && !this.featuresNeedsReload && !options?.initSession) {
729
+ if (this.features.bootloaderMode) {
730
+ return;
731
+ }
732
+ // 不能直接信任缓存 features:设备端 wipe / 完成初始化 / 改 label 后
733
+ // features 会永久陈旧。每次 run 做一次轻量 status 刷新(不含 fw/SE),
734
+ // 用字段级合并保留已有版本和 SE 信息。
735
+ await this._refreshProtocolV2Status(options);
772
736
  return;
773
737
  }
774
- await this._initializeProtocolV2();
738
+ await this._initializeProtocolV2(options);
775
739
  return;
776
740
  }
777
741
 
@@ -821,11 +785,11 @@ export class Device extends EventEmitter {
821
785
  /**
822
786
  * Device initialization over Protocol V2.
823
787
  *
824
- * Protocol V2 不走传统 Initialize/GetFeatures;DeviceProfile 作为标准模型,
825
- * 同时维护一份由 profile/deviceInfo 适配出的 legacy Features,兼容旧方法内部判断。
788
+ * Protocol V2 不走传统 Initialize/GetFeatures;直接用 DeviceInfoGet
789
+ * 生成唯一的 features 状态。
826
790
  */
827
- private async _initializeProtocolV2() {
828
- Log.debug('Initialize device via Protocol V2 profile adapter');
791
+ private async _initializeProtocolV2(options?: InitOptions) {
792
+ Log.debug('Initialize device via Protocol V2 features adapter');
829
793
 
830
794
  try {
831
795
  // 超时由 requestProtocolV2DeviceInfo 内部的 typedCall timeoutMs(默认 10s)负责,
@@ -833,19 +797,12 @@ export class Device extends EventEmitter {
833
797
  // 且 reject 后底层调用仍会残留。
834
798
  const deviceInfo = await requestProtocolV2DeviceInfo({
835
799
  commands: this.commands,
800
+ timeoutMs: options?.protocolV2DeviceInfoTimeoutMs,
836
801
  });
837
802
  // 默认请求不含 SE/hash 数据,scope 如实标注为 basic;
838
803
  // 完整数据由 getDeviceInfo(scope:'verify'|'full') 获取。
839
- const profile = this.applyProfileUpdate(
840
- buildProfileFromProtocolV2({
841
- deviceInfo,
842
- sources: ['deviceInfo'],
843
- scope: 'basic',
844
- fallbackSerialNo: this.originalDescriptor?.path,
845
- }),
846
- deviceInfo
847
- );
848
- Log.debug('Protocol V2 profile:', profile);
804
+ const features = this.updateProtocolV2Features(deviceInfo);
805
+ Log.debug('Protocol V2 features:', features);
849
806
  this.featuresNeedsReload = false;
850
807
  } catch (error) {
851
808
  Log.error('Protocol V2 initialization failed:', error);
@@ -856,27 +813,19 @@ export class Device extends EventEmitter {
856
813
  /**
857
814
  * Protocol V2 的轻量状态刷新(每次 run 前调用)。
858
815
  *
859
- * 请求 hw + bt + status(不含 fw/SE target):status 提供 init_states / label /
860
- * passphrase_protection 等会在设备端变化的字段;hw/bt 提供 serialNo / bleName
861
- * 避免 applyProfileUpdate 的顶层字段覆盖把已有身份字段清空。
816
+ * 请求 hw + coprocessor + status(不含 fw/SE target):status 提供 init_states / label /
817
+ * passphrase_enabled 等会在设备端变化的字段;hw/coprocessor 提供 serialNo / bleName
862
818
  * versions 为空时按字段级合并保留旧值,verify 数据不会被降级。
863
819
  */
864
- private async _refreshProtocolV2Status() {
820
+ private async _refreshProtocolV2Status(options?: InitOptions) {
865
821
  try {
866
822
  const deviceInfo = await requestProtocolV2DeviceInfo({
867
823
  commands: this.commands,
868
824
  request: PROTOCOL_V2_STATUS_DEVICE_INFO_REQUEST,
825
+ timeoutMs: options?.protocolV2DeviceInfoTimeoutMs,
869
826
  });
870
- const profile = this.applyProfileUpdate(
871
- buildProfileFromProtocolV2({
872
- deviceInfo,
873
- sources: ['deviceInfo'],
874
- scope: 'basic',
875
- fallbackSerialNo: this.originalDescriptor?.path,
876
- }),
877
- deviceInfo
878
- );
879
- Log.debug('Protocol V2 profile (status refresh):', profile);
827
+ const features = this.updateProtocolV2Features(deviceInfo);
828
+ Log.debug('Protocol V2 features (status refresh):', features);
880
829
  } catch (error) {
881
830
  Log.error('Protocol V2 status refresh failed:', error);
882
831
  throw error;
@@ -888,109 +837,67 @@ export class Device extends EventEmitter {
888
837
  const deviceInfo = await requestProtocolV2DeviceInfo({
889
838
  commands: this.commands,
890
839
  });
891
- const profile = this.applyProfileUpdate(
892
- buildProfileFromProtocolV2({
893
- deviceInfo,
894
- sources: ['deviceInfo'],
895
- scope: 'basic',
896
- fallbackSerialNo: this.originalDescriptor?.path,
897
- }),
898
- deviceInfo
899
- );
900
- return this.features ?? this.updateProtocolV2Features(profile, deviceInfo);
840
+ return this.updateProtocolV2Features(deviceInfo);
901
841
  }
902
842
 
903
843
  const { message } = await this.commands.typedCall('GetFeatures', 'Features', {});
904
844
  this._updateFeatures(message);
905
- return message;
845
+ return this.features;
906
846
  }
907
847
 
908
- _updateFeatures(feat: Features, initSession?: boolean) {
848
+ _updateFeatures(protoFeatures: PROTO.Features | Features, initSession?: boolean) {
849
+ const previousCacheDeviceKey = this.getSessionCacheDeviceKey();
850
+ let feat =
851
+ 'protocol' in protoFeatures
852
+ ? protoFeatures
853
+ : buildProtocolV1FeaturesPayload(protoFeatures, this.features);
854
+
909
855
  // GetFeatures doesn't return 'session_id'
910
- if (this.features && this.features.session_id && !feat.session_id) {
911
- feat.session_id = this.features.session_id;
912
- }
913
- if (this.getCurrentDeviceId() && feat.session_id) {
914
- this.setInternalState(feat.session_id, initSession);
856
+ if (this.features?.sessionId && !feat.sessionId) {
857
+ feat.sessionId = this.features.sessionId;
915
858
  }
916
859
  feat.unlocked = feat.unlocked ?? true;
917
860
 
918
861
  feat = fixFeaturesFirmwareVersion(feat);
919
862
 
920
863
  this.features = feat;
921
- if (!this.isProtocolV2()) {
922
- this.updateProfile(
923
- buildProfileFromProtocolV1({
924
- features: feat,
925
- sources: ['features'],
926
- })
927
- );
864
+ const nextCacheDeviceKey = this.getSessionCacheDeviceKey();
865
+ if (previousCacheDeviceKey && nextCacheDeviceKey) {
866
+ deviceWalletSessionStore.migrateDeviceKey(previousCacheDeviceKey, nextCacheDeviceKey);
867
+ }
868
+ if (feat.deviceId && feat.sessionId) {
869
+ this.setInternalState(feat.sessionId, initSession);
928
870
  }
929
871
  this.featuresNeedsReload = false;
930
872
  this.emit(DEVICE.FEATURES, this, feat);
931
873
  }
932
874
 
933
- updateProfile(profile: DeviceProfile | undefined) {
934
- if (!profile) {
935
- this.features = undefined;
936
- return;
937
- }
938
- if (profile.protocol === 'V2') {
939
- this.updateProtocolV2Features(profile, profile.raw?.protocolV2DeviceInfo);
940
- return;
941
- }
942
- if (profile.raw?.features) {
943
- this.features = fixFeaturesFirmwareVersion(profile.raw.features);
875
+ updateProtocolV2Features(deviceInfo?: ProtocolV2DeviceInfo) {
876
+ const previousCacheDeviceKey = this.getSessionCacheDeviceKey();
877
+ const features = fixFeaturesFirmwareVersion(
878
+ buildProtocolV2FeaturesPayload(deviceInfo, this.features)
879
+ );
880
+ this.features = features;
881
+ const nextCacheDeviceKey = this.getSessionCacheDeviceKey();
882
+ if (previousCacheDeviceKey && nextCacheDeviceKey) {
883
+ deviceWalletSessionStore.migrateDeviceKey(previousCacheDeviceKey, nextCacheDeviceKey);
944
884
  }
885
+ this.featuresNeedsReload = false;
886
+ this.emit(DEVICE.FEATURES, this, features);
887
+ return features;
945
888
  }
946
889
 
947
- /**
948
- * 字段级合并刷新 profile,并返回合并后的结果。
949
- *
950
- * basic 范围的刷新(initialize / getFeatures)拿不到 SE 版本和 verify 数据,
951
- * 不能整体替换掉 getDeviceInfo(scope:'verify'|'full') 建立的完整 profile。
952
- */
953
- applyProfileUpdate(next: DeviceProfile, deviceInfo?: ProtocolV2DeviceInfo): DeviceProfile {
954
- const prev = this.profile;
955
- if (!prev || prev.protocol !== next.protocol) {
956
- this.updateProfile({
957
- ...next,
958
- raw: {
959
- ...next.raw,
960
- ...(deviceInfo ? { protocolV2DeviceInfo: deviceInfo } : {}),
961
- },
962
- });
963
- return next;
964
- }
965
-
966
- const versions = { ...prev.versions };
967
- for (const [key, value] of Object.entries(next.versions)) {
968
- if (value != null) {
969
- (versions as Record<string, string | null | undefined>)[key] = value;
970
- }
971
- }
972
-
973
- const merged: DeviceProfile = {
974
- ...prev,
975
- ...next,
976
- versions,
977
- verify: next.verify ?? prev.verify,
978
- raw: {
979
- ...prev.raw,
980
- ...next.raw,
981
- ...(deviceInfo ? { protocolV2DeviceInfo: deviceInfo } : {}),
890
+ updateProtocolV2Status(status: DeviceStatus) {
891
+ const previousDeviceInfo = this.features?.raw?.protocolV2DeviceInfo;
892
+ const previousStatus = previousDeviceInfo?.status;
893
+ return this.updateProtocolV2Features({
894
+ ...(previousDeviceInfo ?? {}),
895
+ protocol_version: previousDeviceInfo?.protocol_version ?? this.features?.protocolVersion ?? 2,
896
+ status: {
897
+ ...previousStatus,
898
+ ...status,
982
899
  },
983
- };
984
- this.updateProfile(merged);
985
- return merged;
986
- }
987
-
988
- private updateProtocolV2Features(profile: DeviceProfile, deviceInfo?: ProtocolV2DeviceInfo) {
989
- const features = fixFeaturesFirmwareVersion(
990
- buildProtocolV2GetFeaturesPayload(profile, deviceInfo)
991
- );
992
- this._updateFeatures(features);
993
- return features;
900
+ });
994
901
  }
995
902
 
996
903
  /**
@@ -1027,7 +934,6 @@ export class Device extends EventEmitter {
1027
934
  if (device.features) {
1028
935
  this._updateFeatures(device.features);
1029
936
  }
1030
- this.updateProfile(device.profile);
1031
937
  }
1032
938
 
1033
939
  async run(fn?: () => Promise<void>, options?: RunOptions) {
@@ -1160,17 +1066,7 @@ export class Device extends EventEmitter {
1160
1066
  }
1161
1067
 
1162
1068
  getMode() {
1163
- if (this.profile) {
1164
- if (this.profile.status.mode === 'bootloader') return EOneKeyDeviceMode.bootloader;
1165
- if (this.profile.status.mode === 'notInitialized') return EOneKeyDeviceMode.notInitialized;
1166
- if (this.profile.status.noBackup === true) return EOneKeyDeviceMode.backupMode;
1167
- if (this.profile.status.mode === 'normal') return EOneKeyDeviceMode.normal;
1168
- // mode 'unknown'(V2 设备未上报 init_states)保守按未初始化处理,
1169
- // 与 isInitialized() 的 fail-closed 行为保持一致。
1170
- if (this.isProtocolV2()) return EOneKeyDeviceMode.notInitialized;
1171
- }
1172
-
1173
- if (this.features?.bootloader_mode) {
1069
+ if (this.features?.bootloaderMode) {
1174
1070
  // bootloader mode
1175
1071
  return EOneKeyDeviceMode.bootloader;
1176
1072
  }
@@ -1180,7 +1076,7 @@ export class Device extends EventEmitter {
1180
1076
  return EOneKeyDeviceMode.notInitialized;
1181
1077
  }
1182
1078
 
1183
- if (this.features?.no_backup) {
1079
+ if (this.features?.noBackup) {
1184
1080
  // backup mode
1185
1081
  return EOneKeyDeviceMode.backupMode;
1186
1082
  }
@@ -1190,17 +1086,11 @@ export class Device extends EventEmitter {
1190
1086
  }
1191
1087
 
1192
1088
  getFirmwareVersion() {
1193
- const profileVersion = profileVersionToArray(this.profile?.versions.firmware);
1194
- if (profileVersion) return profileVersion;
1195
- if (this.isProtocolV2()) return null;
1196
1089
  if (!this.features) return null;
1197
1090
  return getDeviceFirmwareVersion(this.features);
1198
1091
  }
1199
1092
 
1200
1093
  getBLEFirmwareVersion() {
1201
- const profileVersion = profileVersionToArray(this.profile?.versions.ble);
1202
- if (profileVersion) return profileVersion;
1203
- if (this.isProtocolV2()) return null;
1204
1094
  if (!this.features) return null;
1205
1095
  return getDeviceBLEFirmwareVersion(this.features);
1206
1096
  }
@@ -1230,37 +1120,19 @@ export class Device extends EventEmitter {
1230
1120
  }
1231
1121
 
1232
1122
  isBootloader() {
1233
- if (this.profile) {
1234
- return (
1235
- this.profile.status.mode === 'bootloader' || this.profile.status.bootloaderMode === true
1236
- );
1237
- }
1238
- return this.features && !!this.features.bootloader_mode;
1123
+ return this.features && !!this.features.bootloaderMode;
1239
1124
  }
1240
1125
 
1241
1126
  isInitialized() {
1242
- if (this.profile) {
1243
- if (this.profile.status.initialized != null) return this.profile.status.initialized;
1244
- if (this.profile.status.mode === 'normal') return true;
1245
- if (this.profile.status.mode === 'notInitialized') return false;
1246
- // V2 设备未上报 init_states 时按未初始化处理(fail-closed):
1247
- // 未知状态放行会让未初始化设备绕过 NOT_INITIALIZE 门禁。
1248
- if (this.isProtocolV2()) return false;
1249
- if (this.features) return !!this.features.initialized;
1250
- return false;
1251
- }
1252
1127
  return this.features && !!this.features.initialized;
1253
1128
  }
1254
1129
 
1255
1130
  isSeedless() {
1256
- if (this.profile) {
1257
- return this.profile.status.noBackup === true;
1258
- }
1259
- return this.features && !!this.features.no_backup;
1131
+ return this.features && !!this.features.noBackup;
1260
1132
  }
1261
1133
 
1262
1134
  isUnacquired(): boolean {
1263
- return this.features === undefined && this.profile === undefined;
1135
+ return this.features === undefined;
1264
1136
  }
1265
1137
 
1266
1138
  hasUnexpectedMode(allow: string[], require: string[]) {
@@ -1291,7 +1163,7 @@ export class Device extends EventEmitter {
1291
1163
  deviceType === EDeviceType.Touch ||
1292
1164
  deviceType === EDeviceType.Pro ||
1293
1165
  deviceType === EDeviceType.Pro2;
1294
- const unlocked = this.profile ? this.profile.status.unlocked : this.features?.unlocked;
1166
+ const unlocked = this.features?.unlocked;
1295
1167
  const preCheckTouch = isModeT && unlocked === false;
1296
1168
  const passphraseProtection = this.getCurrentPassphraseProtection();
1297
1169
 
@@ -1308,8 +1180,7 @@ export class Device extends EventEmitter {
1308
1180
  }
1309
1181
 
1310
1182
  supportUnlockVersionRange(): DeviceFirmwareRange {
1311
- // 仅适用于 Protocol V1 的 Pro 系列;Pro2 走独立版本线,
1312
- // 且 Protocol V2 固件从首个版本即支持 UnLockDevice(见 unlockDevice 的 isProtocolV2 短路)。
1183
+ // 仅适用于 Protocol V1 的 Pro 系列;Pro2 走独立的 Protocol V2 解锁流程。
1313
1184
  return {
1314
1185
  pro: {
1315
1186
  min: '4.15.0',
@@ -1318,8 +1189,24 @@ export class Device extends EventEmitter {
1318
1189
  }
1319
1190
 
1320
1191
  async unlockDevice() {
1192
+ if (this.isProtocolV2()) {
1193
+ try {
1194
+ await this.commands.typedCall('DeviceSessionAskPin', 'DeviceSessionPinResult');
1195
+ } catch (error) {
1196
+ const errorText =
1197
+ error instanceof Error
1198
+ ? `${error.name} ${error.message}`
1199
+ : String((error as { message?: unknown } | null)?.message ?? error ?? '');
1200
+ if (errorText.includes('Failure_UnexpectedMessage')) {
1201
+ throw createDeviceNotSupportMethodError('deviceUnlock', this.getCurrentFirmwareType());
1202
+ }
1203
+ throw error;
1204
+ }
1205
+
1206
+ return refreshProtocolV2DeviceStatus(this);
1207
+ }
1208
+
1321
1209
  const firmwareVersion = this.getCurrentFirmwareVersionString() ?? '0.0.0';
1322
- // profile 优先的版本范围解析;features 仅作为 V1 capability 判断来源
1323
1210
  const versionRange = this.getCurrentMethodVersionRange(
1324
1211
  type => this.supportUnlockVersionRange()[type]
1325
1212
  );
@@ -1328,9 +1215,7 @@ export class Device extends EventEmitter {
1328
1215
  this.features,
1329
1216
  Enum_Capability.Capability_AttachToPin
1330
1217
  );
1331
- // Pro2 (Protocol V2) 版本线独立于 Pro 系列,固件从首个版本即支持 UnLockDevice
1332
1218
  const supportUnlock =
1333
- this.isProtocolV2() ||
1334
1219
  supportAttachPinCapability ||
1335
1220
  (versionRange &&
1336
1221
  semver.valid(firmwareVersion) &&
@@ -1338,24 +1223,11 @@ export class Device extends EventEmitter {
1338
1223
 
1339
1224
  if (supportUnlock) {
1340
1225
  const res = await this.commands.typedCall('UnLockDevice', 'UnLockDeviceResponse');
1341
- // 解锁结果同步到 profile(标准模型),features 仅在 V1 缓存存在时回写
1342
- if (this.profile) {
1343
- this.updateProfile({
1344
- ...this.profile,
1345
- status: {
1346
- ...this.profile.status,
1347
- unlocked: res.message.unlocked == null ? null : res.message.unlocked,
1348
- ...(res.message.passphrase_protection != null
1349
- ? { passphraseProtection: res.message.passphrase_protection }
1350
- : {}),
1351
- },
1352
- });
1353
- }
1354
1226
  if (this.features) {
1355
1227
  this.features.unlocked = res.message.unlocked == null ? null : res.message.unlocked;
1356
- this.features.unlocked_attach_pin =
1228
+ this.features.unlockedAttachPin =
1357
1229
  res.message.unlocked_attach_pin == null ? undefined : res.message.unlocked_attach_pin;
1358
- this.features.passphrase_protection =
1230
+ this.features.passphraseProtection =
1359
1231
  res.message.passphrase_protection == null ? null : res.message.passphrase_protection;
1360
1232
 
1361
1233
  return Promise.resolve(this.features);
@@ -1365,14 +1237,6 @@ export class Device extends EventEmitter {
1365
1237
  return Promise.resolve(features);
1366
1238
  }
1367
1239
 
1368
- // legacy 解锁探测仅适用于 Protocol V1 老固件;V2 固件必然支持 UnLockDevice
1369
- if (this.isProtocolV2()) {
1370
- throw ERRORS.TypedError(
1371
- HardwareErrorCode.RuntimeError,
1372
- 'unlock device error: device firmware does not support UnLockDevice'
1373
- );
1374
- }
1375
-
1376
1240
  const { type } = await this.commands.typedCall('GetAddress', 'Address', {
1377
1241
  address_n: [toHardened(44), toHardened(1), toHardened(0), 0, 0],
1378
1242
  coin_name: 'Testnet',