@onekeyfe/hd-core 1.2.0-alpha.2 → 1.2.0-alpha.21

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 (406) hide show
  1. package/__tests__/AllNetworkGetAddressBase.tracing.test.ts +75 -0
  2. package/__tests__/DeviceCommands.test.ts +285 -0
  3. package/__tests__/DeviceEventRegistration.test.ts +43 -0
  4. package/__tests__/connectSettings.test.ts +19 -0
  5. package/__tests__/core-dispose.test.ts +34 -0
  6. package/__tests__/core-error-output.test.ts +39 -0
  7. package/__tests__/device-features-state.test.ts +78 -0
  8. package/__tests__/device-identity.test.ts +30 -0
  9. package/__tests__/device-lifecycle-events.test.ts +117 -0
  10. package/__tests__/device-pool-state.test.ts +132 -0
  11. package/__tests__/device-settings.test.ts +391 -0
  12. package/__tests__/device-state-contract.test.ts +27 -0
  13. package/__tests__/device-state-events.test.ts +162 -0
  14. package/__tests__/device-state-mapper.test.ts +241 -0
  15. package/__tests__/device-state-projector.test.ts +94 -0
  16. package/__tests__/device-state-store.test.ts +82 -0
  17. package/__tests__/device-utils.test.ts +106 -0
  18. package/__tests__/device-wallet-session-store.test.ts +213 -0
  19. package/__tests__/deviceFeaturesUtils.test.ts +39 -0
  20. package/__tests__/deviceSettings.test.ts +103 -0
  21. package/__tests__/evmLedgerLegacySafety.test.ts +10 -6
  22. package/__tests__/evmSignTypedData.test.ts +80 -0
  23. package/__tests__/filesystemValidation.test.ts +53 -0
  24. package/__tests__/firmware-update/firmware-update-v2-download-before-boot.test.ts +402 -0
  25. package/__tests__/get-device-state.test.ts +410 -0
  26. package/__tests__/homescreen.test.ts +24 -0
  27. package/__tests__/kaspaSignTransaction.test.ts +591 -0
  28. package/__tests__/logBlockEvent.test.ts +26 -0
  29. package/__tests__/networkUtils.test.ts +386 -0
  30. package/__tests__/open-wallet-session-error-response.test.ts +43 -0
  31. package/__tests__/open-wallet-session.test.ts +559 -0
  32. package/__tests__/preInitialize.test.ts +33 -0
  33. package/__tests__/pro2Wallpaper.test.ts +50 -0
  34. package/__tests__/protocol-v2-bootloader-mode.test.ts +37 -0
  35. package/__tests__/protocol-v2-firmware-targets.test.ts +19 -0
  36. package/__tests__/protocol-v2.test.ts +3954 -1191
  37. package/__tests__/protocolV2FileWrite.test.ts +92 -0
  38. package/__tests__/protocolV2UiInteraction.test.ts +266 -0
  39. package/__tests__/public-device-state-api.test.ts +257 -0
  40. package/__tests__/public-pro2-api-boundary.test.ts +110 -0
  41. package/__tests__/refresh-device-state.test.ts +101 -0
  42. package/__tests__/request-context-logging.test.ts +16 -0
  43. package/__tests__/search-devices.test.ts +136 -0
  44. package/dist/api/BaseMethod.d.ts +8 -0
  45. package/dist/api/BaseMethod.d.ts.map +1 -1
  46. package/dist/api/CheckBootloaderRelease.d.ts.map +1 -1
  47. package/dist/api/CheckFirmwareRelease.d.ts.map +1 -1
  48. package/dist/api/ClearSessionCache.d.ts +9 -0
  49. package/dist/api/ClearSessionCache.d.ts.map +1 -0
  50. package/dist/api/DirList.d.ts.map +1 -1
  51. package/dist/api/FileRead.d.ts.map +1 -1
  52. package/dist/api/FileWrite.d.ts +1 -0
  53. package/dist/api/FileWrite.d.ts.map +1 -1
  54. package/dist/api/FirmwareUpdateV2.d.ts +2 -2
  55. package/dist/api/FirmwareUpdateV2.d.ts.map +1 -1
  56. package/dist/api/FirmwareUpdateV3.d.ts.map +1 -1
  57. package/dist/api/FirmwareUpdateV4.d.ts +25 -5
  58. package/dist/api/FirmwareUpdateV4.d.ts.map +1 -1
  59. package/dist/api/GetDeviceState.d.ts +7 -0
  60. package/dist/api/GetDeviceState.d.ts.map +1 -0
  61. package/dist/api/GetFeatures.d.ts +1 -1
  62. package/dist/api/GetFeatures.d.ts.map +1 -1
  63. package/dist/api/GetOnekeyFeatures.d.ts.map +1 -1
  64. package/dist/api/GetPassphraseState.d.ts +1 -6
  65. package/dist/api/GetPassphraseState.d.ts.map +1 -1
  66. package/dist/api/OpenWalletSession.d.ts +14 -0
  67. package/dist/api/OpenWalletSession.d.ts.map +1 -0
  68. package/dist/api/PathInfo.d.ts.map +1 -1
  69. package/dist/api/PromptWebDeviceAccess.d.ts.map +1 -1
  70. package/dist/api/SearchDevices.d.ts +3 -0
  71. package/dist/api/SearchDevices.d.ts.map +1 -1
  72. package/dist/api/UploadPortfolio.d.ts +17 -0
  73. package/dist/api/UploadPortfolio.d.ts.map +1 -0
  74. package/dist/api/allnetwork/AllNetworkGetAddressBase.d.ts.map +1 -1
  75. package/dist/api/device/DeviceCancel.d.ts.map +1 -1
  76. package/dist/api/device/DeviceChangePin.d.ts.map +1 -1
  77. package/dist/api/device/DeviceFullyUploadResource.d.ts +1 -2
  78. package/dist/api/device/DeviceFullyUploadResource.d.ts.map +1 -1
  79. package/dist/api/device/DeviceLock.d.ts.map +1 -1
  80. package/dist/api/device/DeviceRebootToBoardloader.d.ts.map +1 -1
  81. package/dist/api/device/DeviceRebootToBootloader.d.ts.map +1 -1
  82. package/dist/api/device/DeviceSettings.d.ts.map +1 -1
  83. package/dist/api/device/DeviceUnlock.d.ts +1 -1
  84. package/dist/api/device/DeviceUnlock.d.ts.map +1 -1
  85. package/dist/api/device/DeviceUpdateBootloader.d.ts.map +1 -1
  86. package/dist/api/device/DeviceWipe.d.ts.map +1 -1
  87. package/dist/api/evm/EVMSignTypedData.d.ts.map +1 -1
  88. package/dist/api/firmware/FirmwareUpdateBaseMethod.d.ts +1 -0
  89. package/dist/api/firmware/FirmwareUpdateBaseMethod.d.ts.map +1 -1
  90. package/dist/api/firmware/getBinary.d.ts +11 -3
  91. package/dist/api/firmware/getBinary.d.ts.map +1 -1
  92. package/dist/api/firmware/progressThrottle.d.ts +3 -0
  93. package/dist/api/firmware/progressThrottle.d.ts.map +1 -0
  94. package/dist/api/helpers/filesystemValidation.d.ts +3 -0
  95. package/dist/api/helpers/filesystemValidation.d.ts.map +1 -1
  96. package/dist/api/helpers/paramsValidator.d.ts +1 -0
  97. package/dist/api/helpers/paramsValidator.d.ts.map +1 -1
  98. package/dist/api/helpers/protocolV2FileWrite.d.ts +33 -0
  99. package/dist/api/helpers/protocolV2FileWrite.d.ts.map +1 -0
  100. package/dist/api/index.d.ts +12 -19
  101. package/dist/api/index.d.ts.map +1 -1
  102. package/dist/api/kaspa/KaspaSignTransaction.d.ts +5 -1
  103. package/dist/api/kaspa/KaspaSignTransaction.d.ts.map +1 -1
  104. package/dist/api/kaspa/helpers/TransferSerialize.d.ts.map +1 -1
  105. package/dist/api/protocol-v2/DeviceFactoryInfoGet.d.ts +6 -0
  106. package/dist/api/protocol-v2/DeviceFactoryInfoGet.d.ts.map +1 -0
  107. package/dist/api/protocol-v2/DeviceFactoryInfoSet.d.ts +7 -0
  108. package/dist/api/protocol-v2/DeviceFactoryInfoSet.d.ts.map +1 -0
  109. package/dist/api/protocol-v2/DeviceFirmwareUpdate.d.ts +1 -1
  110. package/dist/api/protocol-v2/DeviceFirmwareUpdate.d.ts.map +1 -1
  111. package/dist/api/protocol-v2/DeviceGetFirmwareUpdateStatus.d.ts +3 -2
  112. package/dist/api/protocol-v2/DeviceGetFirmwareUpdateStatus.d.ts.map +1 -1
  113. package/dist/api/protocol-v2/DeviceGetOnboardingStatus.d.ts +1 -1
  114. package/dist/api/protocol-v2/DeviceGetOnboardingStatus.d.ts.map +1 -1
  115. package/dist/api/protocol-v2/DeviceInfoGet.d.ts +28 -0
  116. package/dist/api/protocol-v2/DeviceInfoGet.d.ts.map +1 -0
  117. package/dist/api/protocol-v2/DeviceReboot.d.ts.map +1 -1
  118. package/dist/api/protocol-v2/DeviceStatusGet.d.ts +6 -0
  119. package/dist/api/protocol-v2/DeviceStatusGet.d.ts.map +1 -0
  120. package/dist/api/protocol-v2/DeviceUploadWallpaper.d.ts +26 -0
  121. package/dist/api/protocol-v2/DeviceUploadWallpaper.d.ts.map +1 -0
  122. package/dist/api/protocol-v2/FilesystemFormat.d.ts.map +1 -1
  123. package/dist/api/protocol-v2/{FilesystemFixPermission.d.ts → FilesystemPermissionFix.d.ts} +2 -2
  124. package/dist/api/protocol-v2/FilesystemPermissionFix.d.ts.map +1 -0
  125. package/dist/api/protocol-v2/ProtocolInfoRequest.d.ts +6 -0
  126. package/dist/api/protocol-v2/ProtocolInfoRequest.d.ts.map +1 -0
  127. package/dist/api/protocol-v2/helpers.d.ts +27 -19
  128. package/dist/api/protocol-v2/helpers.d.ts.map +1 -1
  129. package/dist/api/ton/TonSignData.d.ts +1 -5
  130. package/dist/api/ton/TonSignData.d.ts.map +1 -1
  131. package/dist/api/utils.d.ts +2 -1
  132. package/dist/api/utils.d.ts.map +1 -1
  133. package/dist/constants/index.d.ts +2 -1
  134. package/dist/constants/index.d.ts.map +1 -1
  135. package/dist/core/RequestQueue.d.ts.map +1 -1
  136. package/dist/core/deviceEventRegistration.d.ts +11 -0
  137. package/dist/core/deviceEventRegistration.d.ts.map +1 -0
  138. package/dist/core/index.d.ts +3 -1
  139. package/dist/core/index.d.ts.map +1 -1
  140. package/dist/data-manager/DataManager.d.ts +1 -1
  141. package/dist/data-manager/DataManager.d.ts.map +1 -1
  142. package/dist/data-manager/connectSettings.d.ts.map +1 -1
  143. package/dist/device/Device.d.ts +35 -11
  144. package/dist/device/Device.d.ts.map +1 -1
  145. package/dist/device/DeviceCommands.d.ts +8 -6
  146. package/dist/device/DeviceCommands.d.ts.map +1 -1
  147. package/dist/device/DeviceFeaturesState.d.ts +3 -0
  148. package/dist/device/DeviceFeaturesState.d.ts.map +1 -0
  149. package/dist/device/DevicePool.d.ts +4 -2
  150. package/dist/device/DevicePool.d.ts.map +1 -1
  151. package/dist/device/DeviceStateMapper.d.ts +13 -0
  152. package/dist/device/DeviceStateMapper.d.ts.map +1 -0
  153. package/dist/device/DeviceStateProjector.d.ts +7 -0
  154. package/dist/device/DeviceStateProjector.d.ts.map +1 -0
  155. package/dist/device/DeviceStateStore.d.ts +21 -0
  156. package/dist/device/DeviceStateStore.d.ts.map +1 -0
  157. package/dist/device/DeviceWalletSessionStore.d.ts +20 -0
  158. package/dist/device/DeviceWalletSessionStore.d.ts.map +1 -0
  159. package/dist/device/cloneDeviceState.d.ts +2 -0
  160. package/dist/device/cloneDeviceState.d.ts.map +1 -0
  161. package/dist/device/deviceIdentity.d.ts +5 -0
  162. package/dist/device/deviceIdentity.d.ts.map +1 -0
  163. package/dist/deviceProfile/buildDeviceFeatures.d.ts +9 -3
  164. package/dist/deviceProfile/buildDeviceFeatures.d.ts.map +1 -1
  165. package/dist/deviceProfile/index.d.ts +0 -1
  166. package/dist/deviceProfile/index.d.ts.map +1 -1
  167. package/dist/events/device.d.ts +11 -2
  168. package/dist/events/device.d.ts.map +1 -1
  169. package/dist/events/logBlockEvent.d.ts.map +1 -1
  170. package/dist/events/ui-request.d.ts +18 -2
  171. package/dist/events/ui-request.d.ts.map +1 -1
  172. package/dist/index.d.ts +1361 -609
  173. package/dist/index.js +5898 -2860
  174. package/dist/inject.d.ts.map +1 -1
  175. package/dist/protocols/protocol-v2/deviceSession.d.ts +7 -0
  176. package/dist/protocols/protocol-v2/deviceSession.d.ts.map +1 -0
  177. package/dist/protocols/protocol-v2/features.d.ts +23 -28
  178. package/dist/protocols/protocol-v2/features.d.ts.map +1 -1
  179. package/dist/protocols/protocol-v2/firmware.d.ts +11 -9
  180. package/dist/protocols/protocol-v2/firmware.d.ts.map +1 -1
  181. package/dist/protocols/protocol-v2/index.d.ts +3 -2
  182. package/dist/protocols/protocol-v2/index.d.ts.map +1 -1
  183. package/dist/protocols/protocol-v2/lockedError.d.ts +2 -0
  184. package/dist/protocols/protocol-v2/lockedError.d.ts.map +1 -0
  185. package/dist/protocols/protocol-v2/settingsUnlockPolicy.d.ts +16 -0
  186. package/dist/protocols/protocol-v2/settingsUnlockPolicy.d.ts.map +1 -0
  187. package/dist/protocols/protocol-v2/uiInteraction.d.ts +39 -0
  188. package/dist/protocols/protocol-v2/uiInteraction.d.ts.map +1 -0
  189. package/dist/protocols/protocol-v2/unlockRetry.d.ts +11 -0
  190. package/dist/protocols/protocol-v2/unlockRetry.d.ts.map +1 -0
  191. package/dist/protocols/protocol-v2/walletSession.d.ts +19 -0
  192. package/dist/protocols/protocol-v2/walletSession.d.ts.map +1 -0
  193. package/dist/types/api/checkAllFirmwareRelease.d.ts +1 -2
  194. package/dist/types/api/checkAllFirmwareRelease.d.ts.map +1 -1
  195. package/dist/types/api/deviceSettings.d.ts +11 -2
  196. package/dist/types/api/deviceSettings.d.ts.map +1 -1
  197. package/dist/types/api/deviceUnlock.d.ts +2 -2
  198. package/dist/types/api/deviceUnlock.d.ts.map +1 -1
  199. package/dist/types/api/firmwareUpdate.d.ts +6 -6
  200. package/dist/types/api/firmwareUpdate.d.ts.map +1 -1
  201. package/dist/types/api/getDeviceState.d.ts +12 -0
  202. package/dist/types/api/getDeviceState.d.ts.map +1 -0
  203. package/dist/types/api/getFeatures.d.ts.map +1 -1
  204. package/dist/types/api/getOnekeyFeatures.d.ts.map +1 -1
  205. package/dist/types/api/getPassphraseState.d.ts +2 -10
  206. package/dist/types/api/getPassphraseState.d.ts.map +1 -1
  207. package/dist/types/api/index.d.ts +22 -25
  208. package/dist/types/api/index.d.ts.map +1 -1
  209. package/dist/types/api/kaspaSignTransaction.d.ts +27 -2
  210. package/dist/types/api/kaspaSignTransaction.d.ts.map +1 -1
  211. package/dist/types/api/openWalletSession.d.ts +41 -0
  212. package/dist/types/api/openWalletSession.d.ts.map +1 -0
  213. package/dist/types/api/protocolV2.d.ts +23 -28
  214. package/dist/types/api/protocolV2.d.ts.map +1 -1
  215. package/dist/types/api/sessionCache.d.ts +10 -0
  216. package/dist/types/api/sessionCache.d.ts.map +1 -0
  217. package/dist/types/device.d.ts +121 -5
  218. package/dist/types/device.d.ts.map +1 -1
  219. package/dist/types/params.d.ts +1 -0
  220. package/dist/types/params.d.ts.map +1 -1
  221. package/dist/types/settings.d.ts +21 -1
  222. package/dist/types/settings.d.ts.map +1 -1
  223. package/dist/utils/assets.d.ts +2 -1
  224. package/dist/utils/assets.d.ts.map +1 -1
  225. package/dist/utils/deviceFeaturesCompat.d.ts +13 -0
  226. package/dist/utils/deviceFeaturesCompat.d.ts.map +1 -0
  227. package/dist/utils/deviceFeaturesUtils.d.ts +13 -3
  228. package/dist/utils/deviceFeaturesUtils.d.ts.map +1 -1
  229. package/dist/utils/deviceInfoUtils.d.ts +9 -8
  230. package/dist/utils/deviceInfoUtils.d.ts.map +1 -1
  231. package/dist/utils/deviceSettings.d.ts +38 -7
  232. package/dist/utils/deviceSettings.d.ts.map +1 -1
  233. package/dist/utils/deviceVersionUtils.d.ts +6 -5
  234. package/dist/utils/deviceVersionUtils.d.ts.map +1 -1
  235. package/dist/utils/homescreen.d.ts.map +1 -1
  236. package/dist/utils/index.d.ts +5 -2
  237. package/dist/utils/index.d.ts.map +1 -1
  238. package/dist/utils/networkUtils.d.ts +9 -1
  239. package/dist/utils/networkUtils.d.ts.map +1 -1
  240. package/dist/utils/patch.d.ts +1 -1
  241. package/dist/utils/patch.d.ts.map +1 -1
  242. package/dist/utils/pro2Wallpaper.d.ts +12 -0
  243. package/dist/utils/pro2Wallpaper.d.ts.map +1 -0
  244. package/dist/utils/tracing.d.ts.map +1 -1
  245. package/package.json +4 -4
  246. package/src/api/BaseMethod.ts +36 -9
  247. package/src/api/CheckBootloaderRelease.ts +1 -2
  248. package/src/api/CheckFirmwareRelease.ts +1 -2
  249. package/src/api/ClearSessionCache.ts +28 -0
  250. package/src/api/DirList.ts +5 -3
  251. package/src/api/DirMake.ts +3 -3
  252. package/src/api/DirRemove.ts +3 -3
  253. package/src/api/FileDelete.ts +3 -3
  254. package/src/api/FileRead.ts +23 -7
  255. package/src/api/FileWrite.ts +23 -168
  256. package/src/api/FirmwareUpdate.ts +4 -4
  257. package/src/api/FirmwareUpdateV2.ts +133 -45
  258. package/src/api/FirmwareUpdateV3.ts +3 -5
  259. package/src/api/FirmwareUpdateV4.ts +1138 -357
  260. package/src/api/GetDeviceState.ts +55 -0
  261. package/src/api/GetFeatures.ts +8 -3
  262. package/src/api/GetOnekeyFeatures.ts +3 -72
  263. package/src/api/GetPassphraseState.ts +11 -18
  264. package/src/api/OpenWalletSession.ts +221 -0
  265. package/src/api/PathInfo.ts +5 -3
  266. package/src/api/PromptWebDeviceAccess.ts +2 -3
  267. package/src/api/SearchDevices.ts +29 -4
  268. package/src/api/UploadPortfolio.ts +40 -0
  269. package/src/api/alephium/AlephiumSignTransaction.ts +2 -2
  270. package/src/api/allnetwork/AllNetworkGetAddressBase.ts +4 -8
  271. package/src/api/benfen/BenfenGetAddress.ts +1 -1
  272. package/src/api/btc/BTCGetPublicKey.ts +1 -1
  273. package/src/api/cardano/CardanoSignMessage.ts +1 -1
  274. package/src/api/cardano/CardanoSignTransaction.ts +1 -1
  275. package/src/api/device/DeviceCancel.ts +1 -0
  276. package/src/api/device/DeviceChangePin.ts +24 -0
  277. package/src/api/device/DeviceFullyUploadResource.ts +4 -5
  278. package/src/api/device/DeviceLock.ts +1 -0
  279. package/src/api/device/DeviceRebootToBoardloader.ts +5 -4
  280. package/src/api/device/DeviceRebootToBootloader.ts +5 -4
  281. package/src/api/device/DeviceSettings.ts +183 -1
  282. package/src/api/device/DeviceUnlock.ts +11 -1
  283. package/src/api/device/DeviceUpdateBootloader.ts +1 -2
  284. package/src/api/device/DeviceWipe.ts +19 -0
  285. package/src/api/evm/EVMSignTypedData.ts +3 -5
  286. package/src/api/evm/legacyV1/getAddress.ts +2 -2
  287. package/src/api/evm/legacyV1/getPublicKey.ts +2 -2
  288. package/src/api/evm/legacyV1/signMessage.ts +2 -2
  289. package/src/api/evm/legacyV1/signTypedData.ts +2 -2
  290. package/src/api/evm/legacyV1/signTypedHash.ts +2 -2
  291. package/src/api/evm/legacyV1/verifyMessage.ts +2 -2
  292. package/src/api/firmware/FirmwareUpdateBaseMethod.ts +21 -7
  293. package/src/api/firmware/getBinary.ts +8 -3
  294. package/src/api/firmware/progressThrottle.ts +44 -0
  295. package/src/api/helpers/batchGetPublickeys.ts +2 -2
  296. package/src/api/helpers/filesystemValidation.ts +61 -0
  297. package/src/api/helpers/paramsValidator.ts +1 -1
  298. package/src/api/helpers/protocolV2FileWrite.ts +193 -0
  299. package/src/api/index.ts +14 -20
  300. package/src/api/kaspa/KaspaSignTransaction.ts +341 -22
  301. package/src/api/kaspa/helpers/TransferSerialize.ts +14 -0
  302. package/src/api/nervos/NervosSignTransaction.ts +2 -2
  303. package/src/api/nexa/NexaSignTransaction.ts +1 -1
  304. package/src/api/protocol-v2/{FactoryGetDeviceInfo.ts → DeviceFactoryInfoGet.ts} +4 -4
  305. package/src/api/protocol-v2/DeviceFactoryInfoSet.ts +32 -0
  306. package/src/api/protocol-v2/DeviceFirmwareUpdate.ts +7 -9
  307. package/src/api/protocol-v2/DeviceGetFirmwareUpdateStatus.ts +41 -6
  308. package/src/api/protocol-v2/DeviceGetOnboardingStatus.ts +6 -4
  309. package/src/api/protocol-v2/DeviceInfoGet.ts +145 -0
  310. package/src/api/protocol-v2/DeviceReboot.ts +5 -3
  311. package/src/api/protocol-v2/DeviceStatusGet.ts +16 -0
  312. package/src/api/protocol-v2/DeviceUploadWallpaper.ts +131 -0
  313. package/src/api/protocol-v2/FilesystemFormat.ts +5 -2
  314. package/src/api/protocol-v2/{FilesystemFixPermission.ts → FilesystemPermissionFix.ts} +3 -3
  315. package/src/api/protocol-v2/Ping.ts +1 -1
  316. package/src/api/protocol-v2/{GetProtoVersion.ts → ProtocolInfoRequest.ts} +3 -3
  317. package/src/api/protocol-v2/helpers.ts +58 -62
  318. package/src/api/ton/TonSignData.ts +1 -5
  319. package/src/api/utils.ts +7 -3
  320. package/src/constants/index.ts +7 -1
  321. package/src/core/RequestQueue.ts +1 -0
  322. package/src/core/deviceEventRegistration.ts +23 -0
  323. package/src/core/index.ts +140 -35
  324. package/src/data/messages/messages-protocol-v2.json +825 -709
  325. package/src/data/messages/messages.json +286 -1
  326. package/src/data-manager/DataManager.ts +13 -6
  327. package/src/data-manager/connectSettings.ts +4 -0
  328. package/src/device/Device.ts +480 -192
  329. package/src/device/DeviceCommands.ts +81 -25
  330. package/src/device/DeviceFeaturesState.ts +25 -0
  331. package/src/device/DevicePool.ts +67 -19
  332. package/src/device/DeviceStateMapper.ts +349 -0
  333. package/src/device/DeviceStateProjector.ts +95 -0
  334. package/src/device/DeviceStateStore.ts +201 -0
  335. package/src/device/DeviceWalletSessionStore.ts +105 -0
  336. package/src/device/cloneDeviceState.ts +39 -0
  337. package/src/device/deviceIdentity.ts +18 -0
  338. package/src/deviceProfile/buildDeviceFeatures.ts +235 -210
  339. package/src/deviceProfile/index.ts +0 -1
  340. package/src/events/device.ts +17 -1
  341. package/src/events/logBlockEvent.ts +8 -1
  342. package/src/events/ui-request.ts +24 -2
  343. package/src/inject.ts +23 -34
  344. package/src/protocols/protocol-v2/deviceSession.ts +24 -0
  345. package/src/protocols/protocol-v2/features.ts +68 -82
  346. package/src/protocols/protocol-v2/firmware.ts +12 -36
  347. package/src/protocols/protocol-v2/index.ts +3 -1
  348. package/src/protocols/protocol-v2/lockedError.ts +10 -0
  349. package/src/protocols/protocol-v2/settingsUnlockPolicy.ts +61 -0
  350. package/src/protocols/protocol-v2/uiInteraction.ts +210 -0
  351. package/src/protocols/protocol-v2/unlockRetry.ts +76 -0
  352. package/src/protocols/protocol-v2/walletSession.ts +107 -0
  353. package/src/types/api/checkAllFirmwareRelease.ts +1 -2
  354. package/src/types/api/deviceSettings.ts +26 -2
  355. package/src/types/api/deviceUnlock.ts +5 -2
  356. package/src/types/api/firmwareUpdate.ts +28 -20
  357. package/src/types/api/getDeviceState.ts +19 -0
  358. package/src/types/api/getFeatures.ts +3 -0
  359. package/src/types/api/getOnekeyFeatures.ts +3 -0
  360. package/src/types/api/getPassphraseState.ts +8 -11
  361. package/src/types/api/index.ts +30 -53
  362. package/src/types/api/kaspaSignTransaction.ts +40 -2
  363. package/src/types/api/openWalletSession.ts +64 -0
  364. package/src/types/api/protocolV2.ts +58 -55
  365. package/src/types/api/sessionCache.ts +14 -0
  366. package/src/types/device.ts +170 -4
  367. package/src/types/params.ts +4 -0
  368. package/src/types/settings.ts +42 -30
  369. package/src/utils/assets.ts +4 -1
  370. package/src/utils/deviceFeaturesCompat.ts +173 -0
  371. package/src/utils/deviceFeaturesUtils.ts +68 -24
  372. package/src/utils/deviceInfoUtils.ts +26 -64
  373. package/src/utils/deviceSettings.ts +161 -39
  374. package/src/utils/deviceVersionUtils.ts +25 -44
  375. package/src/utils/findDefectiveBatchDevice.ts +3 -3
  376. package/src/utils/homescreen.ts +10 -0
  377. package/src/utils/index.ts +21 -1
  378. package/src/utils/networkUtils.ts +270 -14
  379. package/src/utils/pro2Wallpaper.ts +109 -0
  380. package/src/utils/tracing.ts +3 -1
  381. package/dist/api/GetDeviceInfo.d.ts +0 -9
  382. package/dist/api/GetDeviceInfo.d.ts.map +0 -1
  383. package/dist/api/protocol-v2/DevReboot.d.ts +0 -7
  384. package/dist/api/protocol-v2/DevReboot.d.ts.map +0 -1
  385. package/dist/api/protocol-v2/DeviceGetDeviceInfo.d.ts +0 -29
  386. package/dist/api/protocol-v2/DeviceGetDeviceInfo.d.ts.map +0 -1
  387. package/dist/api/protocol-v2/FactoryDeviceInfoSettings.d.ts +0 -7
  388. package/dist/api/protocol-v2/FactoryDeviceInfoSettings.d.ts.map +0 -1
  389. package/dist/api/protocol-v2/FactoryGetDeviceInfo.d.ts +0 -6
  390. package/dist/api/protocol-v2/FactoryGetDeviceInfo.d.ts.map +0 -1
  391. package/dist/api/protocol-v2/FilesystemDiskControl.d.ts +0 -13
  392. package/dist/api/protocol-v2/FilesystemDiskControl.d.ts.map +0 -1
  393. package/dist/api/protocol-v2/FilesystemFixPermission.d.ts.map +0 -1
  394. package/dist/api/protocol-v2/GetProtoVersion.d.ts +0 -6
  395. package/dist/api/protocol-v2/GetProtoVersion.d.ts.map +0 -1
  396. package/dist/deviceProfile/buildDeviceProfile.d.ts +0 -21
  397. package/dist/deviceProfile/buildDeviceProfile.d.ts.map +0 -1
  398. package/dist/types/api/getDeviceInfo.d.ts +0 -85
  399. package/dist/types/api/getDeviceInfo.d.ts.map +0 -1
  400. package/src/api/GetDeviceInfo.ts +0 -145
  401. package/src/api/protocol-v2/DevReboot.ts +0 -24
  402. package/src/api/protocol-v2/DeviceGetDeviceInfo.ts +0 -118
  403. package/src/api/protocol-v2/FactoryDeviceInfoSettings.ts +0 -29
  404. package/src/api/protocol-v2/FilesystemDiskControl.ts +0 -50
  405. package/src/deviceProfile/buildDeviceProfile.ts +0 -334
  406. package/src/types/api/getDeviceInfo.ts +0 -99
@@ -1,6 +1,6 @@
1
1
  import EventEmitter from 'events';
2
2
  import semver from 'semver';
3
- import { Enum_Capability } from '@onekeyfe/hd-transport';
3
+ import { DeviceRebootType, Enum_Capability } from '@onekeyfe/hd-transport';
4
4
  import {
5
5
  EDeviceType,
6
6
  ERRORS,
@@ -9,16 +9,18 @@ import {
9
9
  HardwareError,
10
10
  HardwareErrorCode,
11
11
  createDeferred,
12
+ createDeviceNotSupportMethodError,
12
13
  } from '@onekeyfe/hd-shared';
13
14
 
14
15
  import {
15
16
  LoggerNames,
16
17
  getDeviceBLEFirmwareVersion,
17
18
  getDeviceBleName,
19
+ getDeviceBootloaderVersion,
18
20
  getDeviceFirmwareVersion,
19
21
  getDeviceLabel,
22
+ getDeviceSerialNo,
20
23
  getDeviceType,
21
- getDeviceUUID,
22
24
  getFirmwareType,
23
25
  getLogger,
24
26
  } from '../utils';
@@ -31,9 +33,24 @@ import {
31
33
  import { generateInstanceId } from '../utils/tracing';
32
34
  // eslint-disable-next-line import/no-cycle
33
35
  import { DeviceCommands } from './DeviceCommands';
36
+ import { mergeDeviceFeaturesPatch } from './DeviceFeaturesState';
37
+ import {
38
+ mapDeviceSettingsToState,
39
+ mapFeaturesToState,
40
+ mapProtocolV1OnekeyFeaturesToState,
41
+ mapProtocolV2DeviceInfoToState,
42
+ mapProtocolV2DeviceStatusToState,
43
+ } from './DeviceStateMapper';
44
+ import { projectFeatures } from './DeviceStateProjector';
45
+ import { DeviceStateStore, createPublicDeviceState } from './DeviceStateStore';
46
+ import { cloneDeviceState } from './cloneDeviceState';
47
+ import { deviceWalletSessionStore } from './DeviceWalletSessionStore';
34
48
  import {
35
49
  type DeviceFirmwareRange,
36
50
  DeviceModelToTypes,
51
+ type DeviceStateEvent,
52
+ type DeviceStatePatch,
53
+ type DeviceStateUpdateSource,
37
54
  DeviceTypeToModels,
38
55
  type Device as DeviceTyped,
39
56
  EOneKeyDeviceMode,
@@ -50,12 +67,18 @@ import TransportManager from '../data-manager/TransportManager';
50
67
  import { toHardened } from '../api/helpers/pathUtils';
51
68
  import { existCapability } from '../utils/capabilitieUtils';
52
69
  import {
53
- PROTOCOL_V2_STATUS_DEVICE_INFO_REQUEST,
70
+ PROTOCOL_V2_FEATURES_DEVICE_INFO_REQUEST,
71
+ PROTOCOL_V2_FULL_DEVICE_INFO_REQUEST,
72
+ PROTOCOL_V2_VERSIONS_DEVICE_INFO_REQUEST,
73
+ type ProtocolV2RuntimeMode,
74
+ getProtocolV2RuntimeMode,
54
75
  requestProtocolV2DeviceInfo,
76
+ requestProtocolV2DeviceStatus,
55
77
  } from '../protocols/protocol-v2/features';
56
- import { buildProtocolV1FeaturesPayload, buildProtocolV2FeaturesPayload } from '../deviceProfile';
78
+ import { buildProtocolV1FeaturesPayload } from '../deviceProfile';
57
79
 
58
80
  import type { PROTO } from '../constants';
81
+ import type { DeviceStateReadOptions } from '../types/api/getDeviceState';
59
82
  import type {
60
83
  DeviceButtonRequestPayload,
61
84
  DeviceFeaturesPayload,
@@ -65,6 +88,7 @@ import type { PassphrasePromptResponse } from './DeviceCommands';
65
88
  import type { Deferred, HardwareConnectProtocol } from '@onekeyfe/hd-shared';
66
89
  import type {
67
90
  OneKeyDeviceInfo as DeviceDescriptor,
91
+ DeviceStatus,
68
92
  ProtocolV2DeviceInfo,
69
93
  Success,
70
94
  } from '@onekeyfe/hd-transport';
@@ -76,6 +100,9 @@ export type InitOptions = {
76
100
  passphraseState?: string;
77
101
  deriveCardano?: boolean;
78
102
  connectProtocol?: HardwareConnectProtocol;
103
+ protocolV2DeviceInfoTimeoutMs?: number;
104
+ /** Refresh Protocol V2 runtime state before returning discovery results. */
105
+ refreshRuntimeState?: boolean;
79
106
  };
80
107
 
81
108
  export type RunOptions = {
@@ -92,9 +119,11 @@ const Log = getLogger(LoggerNames.Device);
92
119
 
93
120
  export interface DeviceEvents {
94
121
  [DEVICE.PIN]: [Device, PROTO.PinMatrixRequestType | undefined, (err: any, pin: string) => void];
95
- [DEVICE.PASSPHRASE_ON_DEVICE]: [Device, ((response: any) => void)?];
122
+ [DEVICE.PASSPHRASE_ON_DEVICE]: [Device, PassphraseRequestPayload?];
123
+ [DEVICE.ATTACH_PIN_ON_DEVICE]: [Device, PassphraseRequestPayload?];
96
124
  [DEVICE.BUTTON]: [Device, DeviceButtonRequestPayload];
97
125
  [DEVICE.FEATURES]: [Device, DeviceFeaturesPayload];
126
+ [DEVICE.STATE]: [Device, DeviceStateEvent];
98
127
  [DEVICE.PASSPHRASE]: [
99
128
  Device,
100
129
  PassphraseRequestPayload,
@@ -118,8 +147,6 @@ export interface Device {
118
147
  emit<K extends keyof DeviceEvents>(type: K, ...args: DeviceEvents[K]): boolean;
119
148
  }
120
149
 
121
- const deviceSessionCache: Record<string, string> = {};
122
-
123
150
  /**
124
151
  * Pre-populate the device session cache with a known session ID.
125
152
  *
@@ -137,8 +164,7 @@ export function preloadSessionCache(
137
164
  passphraseState: string,
138
165
  sessionId: string
139
166
  ): void {
140
- const key = `${deviceId}@${passphraseState}`;
141
- deviceSessionCache[key] = sessionId;
167
+ deviceWalletSessionStore.set(deviceId, passphraseState, sessionId);
142
168
  }
143
169
 
144
170
  export class Device extends EventEmitter {
@@ -184,22 +210,51 @@ export class Device extends EventEmitter {
184
210
  */
185
211
  private deviceAcquired = false;
186
212
 
187
- /**
188
- * 唯一设备状态缓存。
189
- *
190
- * V1 直接保存原生 Features;V2 保存由 DevGetDeviceInfo 映射出的
191
- * Features 视图。Device 不再保存 profile,结构化 DeviceProfile 只作为
192
- * getDeviceInfo() 的 API 返回值存在。
193
- */
194
- features: Features | undefined = undefined;
195
-
196
- /**
197
- * 是否需要更新设备信息。
198
- *
199
- * 历史名称保留用于兼容现有调用语义;对 V2 表示 features 需要由
200
- * DevGetDeviceInfo 刷新。
201
- */
202
- featuresNeedsReload = false;
213
+ /** Canonical device-state cache; legacy Features is a compatibility projection. */
214
+ private stateStore = new DeviceStateStore();
215
+
216
+ /** Force the next initialization to reload DeviceInfo after reconnect or reboot. */
217
+ private protocolV2StateNeedsReload = false;
218
+
219
+ get state() {
220
+ return this.stateStore.getState();
221
+ }
222
+
223
+ get features(): Features | undefined {
224
+ if (!this.state) return undefined;
225
+ const features = projectFeatures(this.state);
226
+ const sessionCacheDeviceKey =
227
+ this.state.identity.deviceId ||
228
+ (this.isProtocolV2()
229
+ ? this.originalDescriptor.path || this.originalDescriptor.id
230
+ : undefined);
231
+ const sessionId =
232
+ sessionCacheDeviceKey && this.passphraseState
233
+ ? deviceWalletSessionStore.get(sessionCacheDeviceKey, this.passphraseState)
234
+ : undefined;
235
+ return sessionId
236
+ ? {
237
+ ...features,
238
+ sessionId,
239
+ session_id: sessionId,
240
+ passphraseState: this.passphraseState,
241
+ }
242
+ : features;
243
+ }
244
+
245
+ set features(features: Features | undefined) {
246
+ this.stateStore = new DeviceStateStore();
247
+ if (features) {
248
+ this.stateStore.update(mapFeaturesToState(features), 'compatibility');
249
+ const { passphraseState } = features;
250
+ const sessionId = features.sessionId ?? features.session_id;
251
+ const deviceKey = this.getSessionCacheDeviceKey(features.deviceId ?? undefined);
252
+ if (passphraseState && sessionId && deviceKey) {
253
+ this.passphraseState = passphraseState;
254
+ deviceWalletSessionStore.set(deviceKey, passphraseState, sessionId);
255
+ }
256
+ }
257
+ }
203
258
 
204
259
  runPromise?: Deferred<void> | null;
205
260
 
@@ -261,14 +316,18 @@ export class Device extends EventEmitter {
261
316
  const bleName = this.getCurrentBleName();
262
317
  const label = this.getCurrentLabel();
263
318
  const serialNo = this.getCurrentSerialNo();
319
+ const connectId = this.getConnectId();
264
320
  const deviceId = this.getCurrentDeviceId() || null;
265
321
 
266
- const features = this.features;
322
+ const { features } = this;
323
+ const state = this.state ? createPublicDeviceState(this.state) : undefined;
267
324
 
268
325
  return {
269
- /** Android uses Mac address, iOS uses uuid, USB uses uuid */
270
- connectId: DataManager.isBleConnect(env) ? this.mainId || null : serialNo,
271
- /** Hardware ID, will not change at any time */
326
+ /** Android uses a MAC address, while iOS and USB use transport-specific identifiers. */
327
+ connectId: DataManager.isBleConnect(env) ? this.mainId || null : connectId,
328
+ /** Stable physical-device identity. */
329
+ serialNo,
330
+ /** @deprecated Use serialNo instead. */
272
331
  uuid: serialNo,
273
332
  commType: this.originalDescriptor.commType,
274
333
  sdkInstanceId: this.sdkInstanceId,
@@ -283,7 +342,7 @@ export class Device extends EventEmitter {
283
342
  label: label || 'OneKey',
284
343
  mode: this.getMode(),
285
344
  features,
286
- sessionId: this.features?.sessionId ?? null,
345
+ state,
287
346
  firmwareVersion: this.getFirmwareVersion(),
288
347
  bleFirmwareVersion: this.getBLEFirmwareVersion(),
289
348
  unavailableCapabilities: this.unavailableCapabilities,
@@ -325,18 +384,20 @@ export class Device extends EventEmitter {
325
384
  });
326
385
  }
327
386
 
328
- async acquire(connectProtocol?: HardwareConnectProtocol) {
387
+ async acquire(
388
+ connectProtocol?: HardwareConnectProtocol,
389
+ options?: { throwOnRunPromiseError?: boolean }
390
+ ) {
329
391
  const env = DataManager.getSettings('env');
330
392
  const mainIdKey = DataManager.isBleConnect(env) ? 'id' : 'session';
331
393
  const expectedProtocol = connectProtocol ?? this.originalDescriptor.protocolType;
332
394
  try {
333
395
  let acquireResult: unknown;
334
396
  if (DataManager.isBleConnect(env)) {
335
- // forceCleanRunPromise=true(自 e21b83c6 引入,修复 Pro2 BLE 重连):
336
- // acquire 意味着开启一个全新会话,transport 里残留的上一次 runPromise
337
- // 必然属于已死亡的会话(如固件升级重启、探测中断),不清理会让新会话的
338
- // 调用被旧 promise 卡死。无法在 Device 层面区分“重连恢复”与普通 acquire,
339
- // 因此对 BLE acquire 恒清理是有意为之。
397
+ // acquire starts a new BLE session. Any previous runPromise belongs to a dead
398
+ // session, such as an interrupted probe or firmware-update reboot, and would
399
+ // otherwise block the new session. Device cannot distinguish recovery from a
400
+ // regular acquire, so BLE always clears it intentionally.
340
401
  acquireResult = await this.deviceConnector?.acquire(
341
402
  this.originalDescriptor.id,
342
403
  undefined,
@@ -374,6 +435,9 @@ export class Device extends EventEmitter {
374
435
 
375
436
  this.commands = new DeviceCommands(this, this.mainId ?? '');
376
437
  } catch (error) {
438
+ if (options?.throwOnRunPromiseError) {
439
+ throw error;
440
+ }
377
441
  if (this.runPromise) {
378
442
  this.runPromise.reject(error);
379
443
  } else {
@@ -474,12 +538,11 @@ export class Device extends EventEmitter {
474
538
  }
475
539
 
476
540
  /**
477
- * 唯一协议判别器。
541
+ * Canonical protocol discriminator.
478
542
  *
479
- * descriptor.protocolType 是协议探测后的结果;V2 features DevGetDeviceInfo
480
- * 映射产生。
481
- * SDK 的协议分支都必须走这里,不要直接读 originalDescriptor.protocolType
482
- * 或从 features 反推。
543
+ * descriptor.protocolType is established by active probing; V2 features are mapped
544
+ * from DeviceInfoGet. All protocol branches must use this method instead of reading
545
+ * originalDescriptor.protocolType directly or inferring a protocol from features.
483
546
  */
484
547
  getProtocol(): 'V1' | 'V2' {
485
548
  return this.originalDescriptor.protocolType === 'V2' ? 'V2' : 'V1';
@@ -498,7 +561,16 @@ export class Device extends EventEmitter {
498
561
  }
499
562
 
500
563
  getCurrentSerialNo() {
501
- return this.features ? getDeviceUUID(this.features) : '';
564
+ return getDeviceSerialNo(this.features);
565
+ }
566
+
567
+ getConnectId() {
568
+ const serialNo = this.getCurrentSerialNo();
569
+ if (serialNo) return serialNo;
570
+
571
+ // connectId is an internal routing key. Falling back to the transport descriptor
572
+ // does not change the business meaning of features.serialNo or deviceId.
573
+ return this.originalDescriptor.path || this.originalDescriptor.id || '';
502
574
  }
503
575
 
504
576
  getCurrentBleName() {
@@ -526,6 +598,11 @@ export class Device extends EventEmitter {
526
598
  return getDeviceBLEFirmwareVersion(this.features).join('.');
527
599
  }
528
600
 
601
+ getCurrentBootloaderVersionString() {
602
+ if (!this.features) return undefined;
603
+ return getDeviceBootloaderVersion(this.features).join('.');
604
+ }
605
+
529
606
  getCurrentSafetyChecks() {
530
607
  return this.features?.safetyChecks;
531
608
  }
@@ -591,9 +668,8 @@ export class Device extends EventEmitter {
591
668
  }
592
669
 
593
670
  supportModifyHomescreen(): SupportFeatureType {
594
- // Pro2 走独立 1.x 版本线,不能套用 Touch/Pro 3.4.0 门槛(恒 false 且未来会误判)。
595
- // 依据:firmware-pro2 协议 schema(messages-protocol-v2.json)的 ApplySettings
596
- // 包含 homescreen 字段,V2 固件从首个版本即支持修改主屏。
671
+ // Pro2 has an independent 1.x version line and must not use the Touch/Pro 3.4.0
672
+ // threshold. ApplySettings includes homescreen from the first V2 firmware version.
597
673
  if (this.isProtocolV2()) {
598
674
  return { support: true };
599
675
  }
@@ -611,13 +687,6 @@ export class Device extends EventEmitter {
611
687
  };
612
688
  }
613
689
 
614
- private generateStateKey(deviceId: string, passphraseState?: string) {
615
- if (passphraseState) {
616
- return `${deviceId}@${passphraseState}`;
617
- }
618
- return deviceId;
619
- }
620
-
621
690
  private getSessionCacheDeviceKey(_deviceId?: string) {
622
691
  const deviceId = _deviceId || this.getCurrentDeviceId();
623
692
  if (deviceId) return deviceId;
@@ -627,13 +696,22 @@ export class Device extends EventEmitter {
627
696
  return undefined;
628
697
  }
629
698
 
699
+ private reconcileSessionCacheDeviceIdentity(previousDeviceId?: string) {
700
+ deviceWalletSessionStore.reconcileDeviceIdentity({
701
+ temporaryKey: this.isProtocolV2()
702
+ ? this.originalDescriptor.path || this.originalDescriptor.id
703
+ : undefined,
704
+ previousDeviceId,
705
+ nextDeviceId: this.getCurrentDeviceId(),
706
+ });
707
+ }
708
+
630
709
  getInternalState(_deviceId?: string) {
631
- Log.debug('getInternalState session cache: ', deviceSessionCache);
632
710
  Log.debug(
633
711
  'getInternalState session param: ',
634
712
  `device_id: ${_deviceId}`,
635
713
  `features.deviceId: ${this.features?.deviceId}`,
636
- `passphraseState: ${this.passphraseState}`
714
+ `hasPassphraseState: ${Boolean(this.passphraseState)}`
637
715
  );
638
716
 
639
717
  const deviceId = this.getSessionCacheDeviceKey(_deviceId);
@@ -646,8 +724,7 @@ export class Device extends EventEmitter {
646
724
  // downstream call carries it and hits the primary lookup below.
647
725
  if (!this.passphraseState) return undefined;
648
726
 
649
- const usePassKey = this.generateStateKey(deviceId, this.passphraseState);
650
- return deviceSessionCache[usePassKey];
727
+ return deviceWalletSessionStore.get(deviceId, this.passphraseState);
651
728
  }
652
729
 
653
730
  // attach to pin to fix internal state
@@ -662,40 +739,30 @@ export class Device extends EventEmitter {
662
739
  'updateInternalState session param: ',
663
740
  `device_id: ${deviceId}`,
664
741
  `enablePassphrase: ${enablePassphrase}`,
665
- `passphraseState: ${passphraseState}`,
666
- `sessionId: ${sessionId}`,
667
- `featuresSessionId: ${featuresSessionId}`
742
+ `hasPassphraseState: ${Boolean(passphraseState)}`,
743
+ `hasSessionId: ${Boolean(sessionId)}`,
744
+ `hasFeaturesSessionId: ${Boolean(featuresSessionId)}`
668
745
  );
669
746
 
670
747
  const cacheDeviceKey = this.getSessionCacheDeviceKey(deviceId);
671
748
  if (!cacheDeviceKey) return;
672
749
 
673
750
  if (enablePassphrase) {
674
- // update the sessionId
675
- if (sessionId) {
676
- deviceSessionCache[this.generateStateKey(cacheDeviceKey, passphraseState)] = sessionId;
677
- } else if (featuresSessionId) {
678
- deviceSessionCache[this.generateStateKey(cacheDeviceKey, passphraseState)] =
679
- featuresSessionId;
680
- }
681
- }
682
-
683
- // delete the old sessionId
684
- const oldKey = `${cacheDeviceKey}`;
685
- if (deviceSessionCache[oldKey]) {
686
- delete deviceSessionCache[oldKey];
751
+ const walletSessionId =
752
+ sessionId || featuresSessionId || deviceWalletSessionStore.getPending(cacheDeviceKey);
753
+ deviceWalletSessionStore.set(cacheDeviceKey, passphraseState, walletSessionId ?? undefined);
687
754
  }
688
755
 
689
- Log.debug('updateInternalState session cache: ', deviceSessionCache);
756
+ deviceWalletSessionStore.deletePending(cacheDeviceKey);
690
757
  }
691
758
 
692
759
  private setInternalState(state: string, initSession?: boolean) {
693
760
  Log.debug(
694
761
  'setInternalState session param: ',
695
- `state: ${state}`,
762
+ `hasState: ${Boolean(state)}`,
696
763
  `initSession: ${initSession}`,
697
764
  `deviceId: ${this.features?.deviceId}`,
698
- `passphraseState: ${this.passphraseState}`
765
+ `hasPassphraseState: ${Boolean(this.passphraseState)}`
699
766
  );
700
767
 
701
768
  if (!this.passphraseState && !initSession) return;
@@ -703,12 +770,11 @@ export class Device extends EventEmitter {
703
770
  const deviceId = this.getSessionCacheDeviceKey();
704
771
  if (!deviceId) return;
705
772
 
706
- const key = this.generateStateKey(deviceId, this.passphraseState);
707
-
708
- if (state) {
709
- deviceSessionCache[key] = state;
773
+ if (this.passphraseState) {
774
+ deviceWalletSessionStore.set(deviceId, this.passphraseState, state);
775
+ } else if (initSession) {
776
+ deviceWalletSessionStore.setPending(deviceId, state);
710
777
  }
711
- Log.debug('setInternalState done session cache: ', deviceSessionCache);
712
778
  }
713
779
 
714
780
  clearInternalState(_deviceId?: string) {
@@ -716,27 +782,27 @@ export class Device extends EventEmitter {
716
782
 
717
783
  const deviceId = this.getSessionCacheDeviceKey(_deviceId);
718
784
  if (!deviceId) return;
719
- const key = `${deviceId}`;
720
- delete deviceSessionCache[key];
785
+ deviceWalletSessionStore.deletePending(deviceId);
721
786
 
722
787
  if (this.passphraseState) {
723
- const usePassKey = this.generateStateKey(deviceId, this.passphraseState);
724
- delete deviceSessionCache[usePassKey];
788
+ deviceWalletSessionStore.delete(deviceId, this.passphraseState);
725
789
  }
726
790
  }
727
791
 
728
792
  async initialize(options?: InitOptions) {
729
- // Protocol V2 不支持传统 Initialize,直接使用协议专用初始化流程。
793
+ // Protocol V2 does not support legacy Initialize; use its dedicated flow.
730
794
  if (this.isProtocolV2()) {
731
795
  this.passphraseState = options?.passphraseState;
732
- if (this.features && !this.featuresNeedsReload && !options?.initSession) {
733
- // 不能直接信任缓存 features:设备端 wipe / 完成初始化 / 改 label 后
734
- // features 会永久陈旧。每次 run 做一次轻量 status 刷新(不含 fw/SE),
735
- // 用字段级合并保留已有版本和 SE 信息。
736
- await this._refreshProtocolV2Status();
796
+ if (this.state && !options?.initSession && !this.protocolV2StateNeedsReload) {
797
+ if (this.features?.bootloaderMode) {
798
+ return;
799
+ }
800
+ // Normal calls reuse the cache. Explicit getDeviceState refreshes, unlock flow,
801
+ // and device events update dynamic state without polling on every SDK call.
737
802
  return;
738
803
  }
739
- await this._initializeProtocolV2();
804
+ await this._initializeProtocolV2(options);
805
+ this.protocolV2StateNeedsReload = false;
740
806
  return;
741
807
  }
742
808
 
@@ -786,57 +852,38 @@ export class Device extends EventEmitter {
786
852
  /**
787
853
  * Device initialization over Protocol V2.
788
854
  *
789
- * Protocol V2 不走传统 Initialize/GetFeatures;直接用 DevGetDeviceInfo
790
- * 生成唯一的 features 状态。
855
+ * Protocol V2 bypasses legacy Initialize/GetFeatures and builds its canonical
856
+ * features state directly from DeviceInfoGet.
791
857
  */
792
- private async _initializeProtocolV2() {
858
+ private async _initializeProtocolV2(options?: InitOptions) {
793
859
  Log.debug('Initialize device via Protocol V2 features adapter');
794
860
 
795
861
  try {
796
- // 超时由 requestProtocolV2DeviceInfo 内部的 typedCall timeoutMs(默认 10s)负责,
797
- // 不再额外包一层 Promise.race:外层 race timer 不会清理,
798
- // 且 reject 后底层调用仍会残留。
862
+ // typedCall applies the request timeout. An outer Promise.race would leak its
863
+ // timer and leave the underlying call active after rejection.
799
864
  const deviceInfo = await requestProtocolV2DeviceInfo({
800
865
  commands: this.commands,
866
+ timeoutMs: options?.protocolV2DeviceInfoTimeoutMs,
801
867
  });
802
- // 默认请求不含 SE/hash 数据,scope 如实标注为 basic
803
- // 完整数据由 getDeviceInfo(scope:'verify'|'full') 获取。
804
- const features = this.updateProtocolV2Features(deviceInfo);
868
+ // The default request excludes SE/hash data and therefore uses basic scope.
869
+ // Full version and verification data require getDeviceState({ scope: 'firmware' }).
870
+ const features = await this.probeProtocolV2RuntimeState(
871
+ deviceInfo,
872
+ options?.protocolV2DeviceInfoTimeoutMs
873
+ );
805
874
  Log.debug('Protocol V2 features:', features);
806
- this.featuresNeedsReload = false;
807
875
  } catch (error) {
808
876
  Log.error('Protocol V2 initialization failed:', error);
809
877
  throw error;
810
878
  }
811
879
  }
812
880
 
813
- /**
814
- * Protocol V2 的轻量状态刷新(每次 run 前调用)。
815
- *
816
- * 请求 hw + bt + status(不含 fw/SE target):status 提供 init_states / label /
817
- * passphrase_protection 等会在设备端变化的字段;hw/bt 提供 serialNo / bleName。
818
- * versions 为空时按字段级合并保留旧值,verify 数据不会被降级。
819
- */
820
- private async _refreshProtocolV2Status() {
821
- try {
822
- const deviceInfo = await requestProtocolV2DeviceInfo({
823
- commands: this.commands,
824
- request: PROTOCOL_V2_STATUS_DEVICE_INFO_REQUEST,
825
- });
826
- const features = this.updateProtocolV2Features(deviceInfo);
827
- Log.debug('Protocol V2 features (status refresh):', features);
828
- } catch (error) {
829
- Log.error('Protocol V2 status refresh failed:', error);
830
- throw error;
831
- }
832
- }
833
-
834
881
  async getFeatures() {
835
882
  if (this.isProtocolV2()) {
836
883
  const deviceInfo = await requestProtocolV2DeviceInfo({
837
884
  commands: this.commands,
838
885
  });
839
- return this.updateProtocolV2Features(deviceInfo);
886
+ return this.probeProtocolV2RuntimeState(deviceInfo);
840
887
  }
841
888
 
842
889
  const { message } = await this.commands.typedCall('GetFeatures', 'Features', {});
@@ -844,7 +891,82 @@ export class Device extends EventEmitter {
844
891
  return this.features;
845
892
  }
846
893
 
894
+ async getDeviceState(params: DeviceStateReadOptions = {}) {
895
+ const refresh = new Set(params.refreshSections ?? []);
896
+ const getProtocolV2DeviceInfoRequest = () => {
897
+ if (refresh.has('verification')) return PROTOCOL_V2_FULL_DEVICE_INFO_REQUEST;
898
+ if (refresh.has('versions')) return PROTOCOL_V2_VERSIONS_DEVICE_INFO_REQUEST;
899
+ return PROTOCOL_V2_FEATURES_DEVICE_INFO_REQUEST;
900
+ };
901
+ let initializedWithDeviceInfo = false;
902
+ let refreshedDeviceInfo: ProtocolV2DeviceInfo | undefined;
903
+
904
+ if (!this.state) {
905
+ if (this.isProtocolV2()) {
906
+ const deviceInfo = await requestProtocolV2DeviceInfo({
907
+ commands: this.commands,
908
+ request: getProtocolV2DeviceInfoRequest(),
909
+ });
910
+ await this.probeProtocolV2RuntimeState(deviceInfo);
911
+ refreshedDeviceInfo = deviceInfo;
912
+ initializedWithDeviceInfo = true;
913
+ } else {
914
+ await this.getFeatures();
915
+ }
916
+ } else if (!this.isProtocolV2() && refresh.size > 0) {
917
+ await this.getFeatures();
918
+ }
919
+
920
+ if (!this.isProtocolV2() && refresh.has('verification')) {
921
+ const { message } = await this.commands.typedCall('OnekeyGetFeatures', 'OnekeyFeatures');
922
+ this.updateState(mapProtocolV1OnekeyFeaturesToState(message), 'device-info');
923
+ }
924
+
925
+ if (this.isProtocolV2()) {
926
+ const refreshDeviceInfo =
927
+ refresh.has('identity') || refresh.has('versions') || refresh.has('verification');
928
+ if (refreshDeviceInfo && !initializedWithDeviceInfo) {
929
+ const deviceInfo = await requestProtocolV2DeviceInfo({
930
+ commands: this.commands,
931
+ request: getProtocolV2DeviceInfoRequest(),
932
+ });
933
+ refreshedDeviceInfo = deviceInfo;
934
+ if (!refresh.has('status')) {
935
+ this.updateState(
936
+ mapProtocolV2DeviceInfoToState(deviceInfo, this.state?.status.mode),
937
+ 'device-info'
938
+ );
939
+ }
940
+ }
941
+
942
+ if (refresh.has('status') && !initializedWithDeviceInfo) {
943
+ const deviceInfo =
944
+ refreshedDeviceInfo ??
945
+ (await requestProtocolV2DeviceInfo({
946
+ commands: this.commands,
947
+ request: getProtocolV2DeviceInfoRequest(),
948
+ }));
949
+ await this.probeProtocolV2RuntimeState(deviceInfo);
950
+ }
951
+
952
+ if (refresh.has('settings') && this.state?.status.mode === 'normal') {
953
+ const { message } = await this.commands.typedCall(
954
+ 'DeviceSettingsGet',
955
+ 'DeviceSettings',
956
+ {}
957
+ );
958
+ this.updateState(mapDeviceSettingsToState(message), 'settings-read');
959
+ }
960
+ }
961
+
962
+ if (!this.state) {
963
+ throw ERRORS.TypedError(HardwareErrorCode.DeviceInitializeFailed);
964
+ }
965
+ return params.includeRaw ? cloneDeviceState(this.state) : createPublicDeviceState(this.state);
966
+ }
967
+
847
968
  _updateFeatures(protoFeatures: PROTO.Features | Features, initSession?: boolean) {
969
+ const previousDeviceId = this.getCurrentDeviceId();
848
970
  let feat =
849
971
  'protocol' in protoFeatures
850
972
  ? protoFeatures
@@ -854,26 +976,151 @@ export class Device extends EventEmitter {
854
976
  if (this.features?.sessionId && !feat.sessionId) {
855
977
  feat.sessionId = this.features.sessionId;
856
978
  }
857
- if (this.getCurrentDeviceId() && feat.sessionId) {
858
- this.setInternalState(feat.sessionId, initSession);
859
- }
860
979
  feat.unlocked = feat.unlocked ?? true;
861
980
 
862
981
  feat = fixFeaturesFirmwareVersion(feat);
863
982
 
864
- this.features = feat;
865
- this.featuresNeedsReload = false;
866
- this.emit(DEVICE.FEATURES, this, feat);
983
+ this.updateState(mapFeaturesToState(feat), 'initialize');
984
+ this.reconcileSessionCacheDeviceIdentity(previousDeviceId);
985
+ if (feat.deviceId && feat.sessionId) {
986
+ this.setInternalState(feat.sessionId, initSession);
987
+ }
988
+ }
989
+
990
+ updateState(patch: DeviceStatePatch, source: DeviceStateUpdateSource) {
991
+ const result = this.stateStore.update(patch, source);
992
+ if (result.changedKeys.length === 0) return result.state;
993
+
994
+ const event: DeviceStateEvent = {
995
+ connectId: this.getConnectId() ?? null,
996
+ state: createPublicDeviceState(result.state),
997
+ revision: result.revision,
998
+ source,
999
+ changedKeys: result.changedKeys,
1000
+ };
1001
+ Log.debug('Device state patch committed', {
1002
+ source,
1003
+ keys: result.changedKeys,
1004
+ });
1005
+ this.emit(DEVICE.STATE, this, event);
1006
+ if (result.state.protocol === 'V1') {
1007
+ this.emit(DEVICE.FEATURES, this, projectFeatures(result.state));
1008
+ }
1009
+ return result.state;
867
1010
  }
868
1011
 
869
- updateProtocolV2Features(deviceInfo?: ProtocolV2DeviceInfo) {
870
- const features = fixFeaturesFirmwareVersion(
871
- buildProtocolV2FeaturesPayload(deviceInfo, this.features)
1012
+ updateFeaturesPatch(patch: Partial<Features>, source: DeviceStateUpdateSource) {
1013
+ const currentFeatures = this.features;
1014
+ if (!currentFeatures) return undefined;
1015
+
1016
+ const features = mergeDeviceFeaturesPatch(currentFeatures, patch);
1017
+ if (features === currentFeatures) return currentFeatures;
1018
+
1019
+ const normalized = fixFeaturesFirmwareVersion(features);
1020
+ this.updateState(mapFeaturesToState(normalized), source);
1021
+ return this.features;
1022
+ }
1023
+
1024
+ async probeProtocolV2RuntimeState(deviceInfo: ProtocolV2DeviceInfo, timeoutMs?: number) {
1025
+ let deviceStatus: DeviceStatus;
1026
+ try {
1027
+ deviceStatus = await requestProtocolV2DeviceStatus({
1028
+ commands: this.commands,
1029
+ timeoutMs,
1030
+ });
1031
+ } catch (error) {
1032
+ const runtimeMode = getProtocolV2RuntimeMode({
1033
+ deviceInfo,
1034
+ deviceStatusAvailable: false,
1035
+ });
1036
+ Log.debug('Protocol V2 DeviceStatusGet unavailable; use temporary loader fallback', {
1037
+ runtimeMode,
1038
+ error: error instanceof Error ? error.message : String(error),
1039
+ });
1040
+ return this.updateProtocolV2Features(deviceInfo, null, runtimeMode);
1041
+ }
1042
+ return this.updateProtocolV2Features(deviceInfo, deviceStatus, 'normal');
1043
+ }
1044
+
1045
+ updateProtocolV2Features(
1046
+ deviceInfo?: ProtocolV2DeviceInfo,
1047
+ deviceStatus?: DeviceStatus | null,
1048
+ runtimeMode?: ProtocolV2RuntimeMode
1049
+ ) {
1050
+ const previousDeviceId = this.getCurrentDeviceId();
1051
+ const resolvedMode =
1052
+ runtimeMode ??
1053
+ (deviceStatus
1054
+ ? getProtocolV2RuntimeMode({ deviceInfo, deviceStatusAvailable: true })
1055
+ : this.state?.status.mode);
1056
+ if (deviceInfo) {
1057
+ this.updateState(mapProtocolV2DeviceInfoToState(deviceInfo, resolvedMode), 'device-info');
1058
+ }
1059
+ if (deviceStatus) {
1060
+ this.updateState(mapProtocolV2DeviceStatusToState(deviceStatus), 'device-status');
1061
+ }
1062
+ this.reconcileSessionCacheDeviceIdentity(previousDeviceId);
1063
+ return this.features as Features;
1064
+ }
1065
+
1066
+ updateProtocolV2Status(status: DeviceStatus) {
1067
+ const previousDeviceInfo = this.state?.raw?.protocolV2DeviceInfo;
1068
+ const previousStatus = this.state?.raw?.protocolV2DeviceStatus;
1069
+ return this.updateProtocolV2Features(previousDeviceInfo, {
1070
+ ...previousStatus,
1071
+ ...status,
1072
+ });
1073
+ }
1074
+
1075
+ markTransportDisconnected() {
1076
+ if (!this.isProtocolV2()) return;
1077
+ this.protocolV2StateNeedsReload = true;
1078
+ this.clearPreInitialized();
1079
+ }
1080
+
1081
+ markProtocolV2Reboot(rebootType: DeviceRebootType) {
1082
+ if (!this.isProtocolV2()) return;
1083
+
1084
+ this.protocolV2StateNeedsReload = true;
1085
+ this.clearPreInitialized();
1086
+ let loaderMode: 'bootloader' | 'romloader' | undefined;
1087
+ if (rebootType === DeviceRebootType.Bootloader) {
1088
+ loaderMode = 'bootloader';
1089
+ } else if (rebootType === DeviceRebootType.Romloader) {
1090
+ loaderMode = 'romloader';
1091
+ }
1092
+ if (loaderMode) {
1093
+ this.updateState(
1094
+ {
1095
+ identity: { deviceId: null },
1096
+ status: {
1097
+ mode: loaderMode,
1098
+ initialized: null,
1099
+ unlocked: null,
1100
+ firmwarePresent: null,
1101
+ backupRequired: null,
1102
+ noBackup: null,
1103
+ unfinishedBackup: null,
1104
+ recoveryMode: null,
1105
+ passphraseProtection: null,
1106
+ pinProtection: null,
1107
+ attachToPinEnabled: null,
1108
+ unlockedAttachPin: null,
1109
+ },
1110
+ raw: { protocolV2DeviceStatus: null },
1111
+ },
1112
+ 'transport-reconnect'
1113
+ );
1114
+ return;
1115
+ }
1116
+
1117
+ this.updateState(
1118
+ {
1119
+ status: { mode: 'normal', unlocked: null },
1120
+ raw: { protocolV2DeviceStatus: null },
1121
+ },
1122
+ 'transport-reconnect'
872
1123
  );
873
- this.features = features;
874
- this.featuresNeedsReload = false;
875
- this.emit(DEVICE.FEATURES, this, features);
876
- return features;
877
1124
  }
878
1125
 
879
1126
  /**
@@ -894,8 +1141,7 @@ export class Device extends EventEmitter {
894
1141
  }
895
1142
 
896
1143
  if (forceUpdate) {
897
- // 枚举得到的 descriptor 可能不带 protocolType(如 WebUSB enumerate),
898
- // 不能让覆盖丢掉已探测的协议结果。
1144
+ // Enumerated descriptors may omit protocolType, so preserve an actively probed value.
899
1145
  this.originalDescriptor = {
900
1146
  ...descriptor,
901
1147
  protocolType: descriptor.protocolType ?? this.originalDescriptor.protocolType,
@@ -920,19 +1166,37 @@ export class Device extends EventEmitter {
920
1166
 
921
1167
  options = parseRunOptions(options);
922
1168
 
923
- this.runPromise = createDeferred(this._runInner.bind(this, fn, options));
924
- return this.runPromise.promise;
1169
+ const runPromise = createDeferred<void>();
1170
+ this.runPromise = runPromise;
1171
+ this._runInner(fn, options, runPromise).catch(error => {
1172
+ if (this.runPromise === runPromise) {
1173
+ this.runPromise = null;
1174
+ }
1175
+ runPromise.reject(error);
1176
+ });
1177
+ return runPromise.promise;
925
1178
  }
926
1179
 
927
- async _runInner<T>(fn: (() => Promise<T>) | undefined, options: RunOptions) {
1180
+ async _runInner<T>(
1181
+ fn: (() => Promise<T>) | undefined,
1182
+ options: RunOptions,
1183
+ runPromise: Deferred<void>
1184
+ ) {
1185
+ const clearRunPromise = () => {
1186
+ if (this.runPromise === runPromise) {
1187
+ this.runPromise = null;
1188
+ }
1189
+ };
1190
+
928
1191
  if (!this.isUsedHere() || this.commands.disposed) {
929
1192
  const env = DataManager.getSettings('env');
930
1193
  if (env !== 'react-native') {
931
1194
  try {
932
1195
  await this.acquire(options.connectProtocol);
933
1196
  } catch (error) {
934
- this.runPromise = null;
935
- return Promise.reject(error);
1197
+ clearRunPromise();
1198
+ runPromise.reject(error);
1199
+ return;
936
1200
  }
937
1201
 
938
1202
  try {
@@ -942,16 +1206,19 @@ export class Device extends EventEmitter {
942
1206
  }
943
1207
  }
944
1208
  } catch (error) {
945
- this.runPromise = null;
1209
+ await this.release();
1210
+ clearRunPromise();
946
1211
  if (error instanceof HardwareError) {
947
- return Promise.reject(error);
1212
+ runPromise.reject(error);
1213
+ return;
948
1214
  }
949
- return Promise.reject(
1215
+ runPromise.reject(
950
1216
  ERRORS.TypedError(
951
1217
  HardwareErrorCode.DeviceInitializeFailed,
952
1218
  `Initialize failed: ${error.message as string}, code: ${error.code as string}`
953
1219
  )
954
1220
  );
1221
+ return;
955
1222
  }
956
1223
  } else if (env === 'react-native') {
957
1224
  // TODO: implement react-native acquire
@@ -970,9 +1237,7 @@ export class Device extends EventEmitter {
970
1237
  try {
971
1238
  await fn();
972
1239
  } catch (e) {
973
- if (this.runPromise) {
974
- this.runPromise.reject(e);
975
- }
1240
+ runPromise.reject(e);
976
1241
 
977
1242
  if (
978
1243
  e instanceof HardwareError &&
@@ -985,7 +1250,7 @@ export class Device extends EventEmitter {
985
1250
  Log.debug(`error code ${e.errorCode} release device, mainId: ${this.mainId}`);
986
1251
  }
987
1252
 
988
- this.runPromise = null;
1253
+ clearRunPromise();
989
1254
  return;
990
1255
  }
991
1256
  }
@@ -999,11 +1264,8 @@ export class Device extends EventEmitter {
999
1264
  Log.debug('release device, mainId: ', this.mainId);
1000
1265
  }
1001
1266
 
1002
- if (this.runPromise) {
1003
- this.runPromise.resolve();
1004
- }
1005
-
1006
- this.runPromise = null;
1267
+ runPromise.resolve();
1268
+ clearRunPromise();
1007
1269
  }
1008
1270
 
1009
1271
  async interruptionFromOutside() {
@@ -1042,22 +1304,23 @@ export class Device extends EventEmitter {
1042
1304
  }
1043
1305
 
1044
1306
  getMode() {
1045
- if (this.features?.bootloaderMode) {
1046
- // bootloader mode
1047
- return EOneKeyDeviceMode.bootloader;
1048
- }
1049
-
1050
- if (!this.features?.initialized) {
1051
- // not initialized
1052
- return EOneKeyDeviceMode.notInitialized;
1053
- }
1054
-
1055
- if (this.features?.noBackup) {
1056
- // backup mode
1057
- return EOneKeyDeviceMode.backupMode;
1307
+ switch (this.state?.status.mode) {
1308
+ case 'bootloader':
1309
+ case 'romloader':
1310
+ return EOneKeyDeviceMode.bootloader;
1311
+ case 'notInitialized':
1312
+ return EOneKeyDeviceMode.notInitialized;
1313
+ case 'backupMode':
1314
+ return EOneKeyDeviceMode.backupMode;
1315
+ case 'normal':
1316
+ return EOneKeyDeviceMode.normal;
1317
+ default:
1318
+ break;
1058
1319
  }
1059
1320
 
1060
- // normal mode
1321
+ if (this.features?.bootloaderMode) return EOneKeyDeviceMode.bootloader;
1322
+ if (this.features?.initialized === false) return EOneKeyDeviceMode.notInitialized;
1323
+ if (this.features?.noBackup) return EOneKeyDeviceMode.backupMode;
1061
1324
  return EOneKeyDeviceMode.normal;
1062
1325
  }
1063
1326
 
@@ -1115,7 +1378,7 @@ export class Device extends EventEmitter {
1115
1378
  // both allow and require cases might generate single unexpected mode
1116
1379
  if (!this.isUnacquired()) {
1117
1380
  // allow cases
1118
- if (this.isBootloader() && !allow.includes(UI_REQUEST.BOOTLOADER)) {
1381
+ if (this.isBootloader() && !this.isProtocolV2() && !allow.includes(UI_REQUEST.BOOTLOADER)) {
1119
1382
  return UI_REQUEST.BOOTLOADER;
1120
1383
  }
1121
1384
  if (!this.isInitialized() && !allow.includes(UI_REQUEST.NOT_INITIALIZE)) {
@@ -1152,12 +1415,12 @@ export class Device extends EventEmitter {
1152
1415
 
1153
1416
  async lockDevice(): Promise<Success> {
1154
1417
  const res = await this.commands.typedCall('LockDevice', 'Success', {});
1418
+ this.updateState({ status: { unlocked: false } }, 'lock');
1155
1419
  return res.message;
1156
1420
  }
1157
1421
 
1158
1422
  supportUnlockVersionRange(): DeviceFirmwareRange {
1159
- // 仅适用于 Protocol V1 Pro 系列;Pro2 走独立版本线,
1160
- // 且 Protocol V2 固件从首个版本即支持 UnLockDevice(见 unlockDevice 的 isProtocolV2 短路)。
1423
+ // This range applies to Protocol V1 Pro devices; Pro2 has a dedicated unlock flow.
1161
1424
  return {
1162
1425
  pro: {
1163
1426
  min: '4.15.0',
@@ -1166,6 +1429,26 @@ export class Device extends EventEmitter {
1166
1429
  }
1167
1430
 
1168
1431
  async unlockDevice() {
1432
+ if (this.isProtocolV2()) {
1433
+ try {
1434
+ await this.commands.typedCall('DeviceSessionAskPin', 'Success', undefined, {
1435
+ timeoutMs: 120_000,
1436
+ });
1437
+ } catch (error) {
1438
+ const errorText =
1439
+ error instanceof Error
1440
+ ? `${error.name} ${error.message}`
1441
+ : String((error as { message?: unknown } | null)?.message ?? error ?? '');
1442
+ if (errorText.includes('Failure_UnexpectedMessage')) {
1443
+ throw createDeviceNotSupportMethodError('deviceUnlock', this.getCurrentFirmwareType());
1444
+ }
1445
+ throw error;
1446
+ }
1447
+
1448
+ const status = await requestProtocolV2DeviceStatus({ commands: this.commands });
1449
+ return this.updateProtocolV2Status(status);
1450
+ }
1451
+
1169
1452
  const firmwareVersion = this.getCurrentFirmwareVersionString() ?? '0.0.0';
1170
1453
  const versionRange = this.getCurrentMethodVersionRange(
1171
1454
  type => this.supportUnlockVersionRange()[type]
@@ -1175,9 +1458,7 @@ export class Device extends EventEmitter {
1175
1458
  this.features,
1176
1459
  Enum_Capability.Capability_AttachToPin
1177
1460
  );
1178
- // Pro2 (Protocol V2) 版本线独立于 Pro 系列,固件从首个版本即支持 UnLockDevice
1179
1461
  const supportUnlock =
1180
- this.isProtocolV2() ||
1181
1462
  supportAttachPinCapability ||
1182
1463
  (versionRange &&
1183
1464
  semver.valid(firmwareVersion) &&
@@ -1186,11 +1467,20 @@ export class Device extends EventEmitter {
1186
1467
  if (supportUnlock) {
1187
1468
  const res = await this.commands.typedCall('UnLockDevice', 'UnLockDeviceResponse');
1188
1469
  if (this.features) {
1189
- this.features.unlocked = res.message.unlocked == null ? null : res.message.unlocked;
1190
- this.features.unlockedAttachPin =
1191
- res.message.unlocked_attach_pin == null ? undefined : res.message.unlocked_attach_pin;
1192
- this.features.passphraseProtection =
1193
- res.message.passphrase_protection == null ? null : res.message.passphrase_protection;
1470
+ this.updateState(
1471
+ {
1472
+ status: {
1473
+ unlocked: res.message.unlocked == null ? null : res.message.unlocked,
1474
+ unlockedAttachPin:
1475
+ res.message.unlocked_attach_pin == null ? null : res.message.unlocked_attach_pin,
1476
+ passphraseProtection:
1477
+ res.message.passphrase_protection == null
1478
+ ? null
1479
+ : res.message.passphrase_protection,
1480
+ },
1481
+ },
1482
+ 'unlock'
1483
+ );
1194
1484
 
1195
1485
  return Promise.resolve(this.features);
1196
1486
  }
@@ -1199,14 +1489,6 @@ export class Device extends EventEmitter {
1199
1489
  return Promise.resolve(features);
1200
1490
  }
1201
1491
 
1202
- // legacy 解锁探测仅适用于 Protocol V1 老固件;V2 固件必然支持 UnLockDevice
1203
- if (this.isProtocolV2()) {
1204
- throw ERRORS.TypedError(
1205
- HardwareErrorCode.RuntimeError,
1206
- 'unlock device error: device firmware does not support UnLockDevice'
1207
- );
1208
- }
1209
-
1210
1492
  const { type } = await this.commands.typedCall('GetAddress', 'Address', {
1211
1493
  address_n: [toHardened(44), toHardened(1), toHardened(0), 0, 0],
1212
1494
  coin_name: 'Testnet',
@@ -1229,21 +1511,27 @@ export class Device extends EventEmitter {
1229
1511
  ) {
1230
1512
  if (this.isUnacquired()) return false;
1231
1513
 
1514
+ const expectedPassphraseState = useEmptyPassphrase ? undefined : passphraseState;
1232
1515
  const { passphraseState: newPassphraseState, unlockedAttachPin } =
1233
1516
  await getPassphraseStateWithRefreshDeviceInfo(this, {
1234
- expectPassphraseState: passphraseState,
1517
+ expectPassphraseState: expectedPassphraseState,
1235
1518
  onlyMainPin: useEmptyPassphrase,
1236
1519
  });
1237
1520
 
1238
1521
  // Main wallet and unlock Attach Pin, throw safe error
1239
1522
  const mainWalletUseAttachPin = unlockedAttachPin && useEmptyPassphrase;
1240
1523
  const useErrorAttachPin =
1241
- unlockedAttachPin && passphraseState && passphraseState !== newPassphraseState;
1242
- const passphraseStateMismatch = !!passphraseState && passphraseState !== newPassphraseState;
1524
+ unlockedAttachPin &&
1525
+ expectedPassphraseState &&
1526
+ expectedPassphraseState !== newPassphraseState;
1527
+ const passphraseStateMismatch =
1528
+ !!expectedPassphraseState && expectedPassphraseState !== newPassphraseState;
1243
1529
 
1244
1530
  Log.debug('Check passphrase state safety: ', {
1245
- passphraseState,
1246
- newPassphraseState,
1531
+ hasExpectedPassphraseState: Boolean(expectedPassphraseState),
1532
+ hasNewPassphraseState: Boolean(newPassphraseState),
1533
+ passphraseStateMatches:
1534
+ Boolean(expectedPassphraseState) && expectedPassphraseState === newPassphraseState,
1247
1535
  unlockedAttachPin,
1248
1536
  useEmptyPassphrase,
1249
1537
  });