@onekeyfe/hd-core 1.2.0-alpha.67 → 1.2.0-alpha.69

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 (316) hide show
  1. package/__tests__/AllNetworkGetAddressBase.tracing.test.ts +313 -0
  2. package/__tests__/DeviceCommands.test.ts +59 -13
  3. package/__tests__/DeviceEventRegistration.test.ts +6 -0
  4. package/__tests__/check-all-firmware-release-protocol-v2.test.ts +226 -3
  5. package/__tests__/connectSettings.test.ts +5 -47
  6. package/__tests__/core-initialization.test.ts +23 -0
  7. package/__tests__/device-connector-protocol.test.ts +81 -0
  8. package/__tests__/device-lifecycle-events.test.ts +105 -3
  9. package/__tests__/device-pool-state.test.ts +22 -0
  10. package/__tests__/device-settings.test.ts +39 -18
  11. package/__tests__/device-state-contract.test.ts +1 -0
  12. package/__tests__/device-state-mapper.test.ts +13 -1
  13. package/__tests__/device-utils.test.ts +48 -1
  14. package/__tests__/deviceSettings.test.ts +11 -1
  15. package/__tests__/deviceUploadNft.test.ts +316 -0
  16. package/__tests__/firmware-update/firmware-update-v2-download-before-boot.test.ts +31 -59
  17. package/__tests__/get-device-state.test.ts +132 -12
  18. package/__tests__/homescreen.test.ts +41 -1
  19. package/__tests__/kaspa-use-tweak-pro2.test.ts +2 -2
  20. package/__tests__/kaspaSignTransaction.test.ts +3 -26
  21. package/__tests__/logBlockEvent.test.ts +1 -1
  22. package/__tests__/method-protocol-support.test.ts +19 -0
  23. package/__tests__/open-wallet-session.test.ts +568 -37
  24. package/__tests__/pro2Nft.test.ts +108 -0
  25. package/__tests__/protocol-binding.test.ts +89 -0
  26. package/__tests__/protocol-v2-firmware-targets.test.ts +33 -1
  27. package/__tests__/protocol-v2-resources.test.ts +343 -0
  28. package/__tests__/protocol-v2-ui-lifecycle.test.ts +91 -0
  29. package/__tests__/protocol-v2-unlock-policy.test.ts +300 -37
  30. package/__tests__/protocol-v2.test.ts +1586 -1213
  31. package/__tests__/protocolV2FileWrite.test.ts +193 -0
  32. package/__tests__/protocolV2UiInteraction.test.ts +63 -0
  33. package/__tests__/public-pro2-api-boundary.test.ts +1 -0
  34. package/__tests__/search-devices.test.ts +12 -3
  35. package/__tests__/tron-sign-message-init.test.ts +2 -2
  36. package/__tests__/uiProgressThrottle.test.ts +74 -0
  37. package/__tests__/uiPromiseRegistry.test.ts +115 -0
  38. package/dist/api/BaseMethod.d.ts +3 -1
  39. package/dist/api/BaseMethod.d.ts.map +1 -1
  40. package/dist/api/CheckAllFirmwareRelease.d.ts +3 -1
  41. package/dist/api/CheckAllFirmwareRelease.d.ts.map +1 -1
  42. package/dist/api/DetectDeviceConnectProtocol.d.ts +7 -0
  43. package/dist/api/DetectDeviceConnectProtocol.d.ts.map +1 -0
  44. package/dist/api/FirmwareUpdate.d.ts.map +1 -1
  45. package/dist/api/FirmwareUpdateV2.d.ts +0 -10
  46. package/dist/api/FirmwareUpdateV2.d.ts.map +1 -1
  47. package/dist/api/FirmwareUpdateV3.d.ts +3 -7
  48. package/dist/api/FirmwareUpdateV3.d.ts.map +1 -1
  49. package/dist/api/FirmwareUpdateV4.d.ts +15 -27
  50. package/dist/api/FirmwareUpdateV4.d.ts.map +1 -1
  51. package/dist/api/GetPassphraseState.d.ts.map +1 -1
  52. package/dist/api/OpenWalletSession.d.ts.map +1 -1
  53. package/dist/api/SearchDevices.d.ts.map +1 -1
  54. package/dist/api/allnetwork/AllNetworkGetAddressBase.d.ts.map +1 -1
  55. package/dist/api/device/DeviceSettings.d.ts.map +1 -1
  56. package/dist/api/device/DeviceUnlock.d.ts +2 -2
  57. package/dist/api/device/DeviceUnlock.d.ts.map +1 -1
  58. package/dist/api/device/DeviceUpdateBootloader.d.ts +1 -5
  59. package/dist/api/device/DeviceUpdateBootloader.d.ts.map +1 -1
  60. package/dist/api/firmware/FirmwareUpdateBaseMethod.d.ts +4 -11
  61. package/dist/api/firmware/FirmwareUpdateBaseMethod.d.ts.map +1 -1
  62. package/dist/api/firmware/getBinary.d.ts +0 -7
  63. package/dist/api/firmware/getBinary.d.ts.map +1 -1
  64. package/dist/api/firmware/updateBootloader.d.ts +0 -2
  65. package/dist/api/firmware/updateBootloader.d.ts.map +1 -1
  66. package/dist/api/firmware/uploadFirmware.d.ts +1 -9
  67. package/dist/api/firmware/uploadFirmware.d.ts.map +1 -1
  68. package/dist/api/helpers/protocolV2FileWrite.d.ts +9 -0
  69. package/dist/api/helpers/protocolV2FileWrite.d.ts.map +1 -1
  70. package/dist/api/index.d.ts +2 -0
  71. package/dist/api/index.d.ts.map +1 -1
  72. package/dist/api/kaspa/KaspaGetAddress.d.ts +1 -1
  73. package/dist/api/kaspa/KaspaSignTransaction.d.ts +1 -1
  74. package/dist/api/kaspa/KaspaSignTransaction.d.ts.map +1 -1
  75. package/dist/api/polkadot/PolkadotGetAddress.d.ts +1 -1
  76. package/dist/api/polkadot/networks.d.ts +1 -1
  77. package/dist/api/protocol-v2/DeviceUploadNft.d.ts +31 -0
  78. package/dist/api/protocol-v2/DeviceUploadNft.d.ts.map +1 -0
  79. package/dist/api/protocol-v2/ProtocolInfoRequest.d.ts.map +1 -1
  80. package/dist/api/protocol-v2/helpers.d.ts.map +1 -1
  81. package/dist/api/solana/SolSignMessage.d.ts +1 -1
  82. package/dist/api/solana/SolSignOffchainMessage.d.ts +1 -1
  83. package/dist/api/solana/SolSignTransaction.d.ts +2 -2
  84. package/dist/api/stellar/StellarSignTransaction.d.ts +1 -1
  85. package/dist/api/sui/SuiGetAddress.d.ts +1 -1
  86. package/dist/api/sui/SuiGetPublicKey.d.ts +1 -1
  87. package/dist/api/sui/SuiSignMessage.d.ts +1 -1
  88. package/dist/api/sui/SuiSignTransaction.d.ts +1 -1
  89. package/dist/api/ton/TonGetAddress.d.ts +1 -1
  90. package/dist/api/ton/TonSignMessage.d.ts +1 -1
  91. package/dist/api/ton/TonSignProof.d.ts +1 -1
  92. package/dist/api/tron/TronSignMessage.d.ts +2 -2
  93. package/dist/api/tron/TronSignTransaction.d.ts +1 -1
  94. package/dist/core/deviceEventRegistration.d.ts +2 -0
  95. package/dist/core/deviceEventRegistration.d.ts.map +1 -1
  96. package/dist/core/index.d.ts.map +1 -1
  97. package/dist/core/uiPromiseRegistry.d.ts +6 -0
  98. package/dist/core/uiPromiseRegistry.d.ts.map +1 -0
  99. package/dist/data-manager/DataManager.d.ts +9 -4
  100. package/dist/data-manager/DataManager.d.ts.map +1 -1
  101. package/dist/data-manager/TransportManager.d.ts.map +1 -1
  102. package/dist/data-manager/connectSettings.d.ts.map +1 -1
  103. package/dist/device/Device.d.ts +29 -6
  104. package/dist/device/Device.d.ts.map +1 -1
  105. package/dist/device/DeviceCommands.d.ts +4 -4
  106. package/dist/device/DeviceCommands.d.ts.map +1 -1
  107. package/dist/device/DeviceConnector.d.ts +2 -1
  108. package/dist/device/DeviceConnector.d.ts.map +1 -1
  109. package/dist/device/DevicePool.d.ts.map +1 -1
  110. package/dist/deviceProfile/protocolV2DeviceIdentity.d.ts.map +1 -1
  111. package/dist/events/device.d.ts +4 -0
  112. package/dist/events/device.d.ts.map +1 -1
  113. package/dist/events/logBlockEvent.d.ts.map +1 -1
  114. package/dist/events/ui-promise.d.ts +4 -2
  115. package/dist/events/ui-promise.d.ts.map +1 -1
  116. package/dist/events/ui-request.d.ts +31 -2
  117. package/dist/events/ui-request.d.ts.map +1 -1
  118. package/dist/events/ui-response.d.ts +10 -2
  119. package/dist/events/ui-response.d.ts.map +1 -1
  120. package/dist/index.d.ts +243 -247
  121. package/dist/index.d.ts.map +1 -1
  122. package/dist/index.js +3135 -3785
  123. package/dist/inject.d.ts +6 -1
  124. package/dist/inject.d.ts.map +1 -1
  125. package/dist/lowLevelInject.d.ts.map +1 -1
  126. package/dist/protocols/protocol-v2/features.d.ts +0 -4
  127. package/dist/protocols/protocol-v2/features.d.ts.map +1 -1
  128. package/dist/protocols/protocol-v2/firmware.d.ts.map +1 -1
  129. package/dist/protocols/protocol-v2/resources.d.ts +30 -0
  130. package/dist/protocols/protocol-v2/resources.d.ts.map +1 -0
  131. package/dist/protocols/protocol-v2/uiInteraction.d.ts +7 -4
  132. package/dist/protocols/protocol-v2/uiInteraction.d.ts.map +1 -1
  133. package/dist/protocols/protocol-v2/unlockPolicy.d.ts +1 -3
  134. package/dist/protocols/protocol-v2/unlockPolicy.d.ts.map +1 -1
  135. package/dist/protocols/protocol-v2/unlockPolicyRunner.d.ts +22 -0
  136. package/dist/protocols/protocol-v2/unlockPolicyRunner.d.ts.map +1 -0
  137. package/dist/protocols/protocol-v2/walletSession.d.ts +6 -1
  138. package/dist/protocols/protocol-v2/walletSession.d.ts.map +1 -1
  139. package/dist/topLevelInject.d.ts.map +1 -1
  140. package/dist/types/api/checkAllFirmwareRelease.d.ts +3 -5
  141. package/dist/types/api/checkAllFirmwareRelease.d.ts.map +1 -1
  142. package/dist/types/api/detectDeviceConnectProtocol.d.ts +4 -0
  143. package/dist/types/api/detectDeviceConnectProtocol.d.ts.map +1 -0
  144. package/dist/types/api/deviceUnlock.d.ts +5 -1
  145. package/dist/types/api/deviceUnlock.d.ts.map +1 -1
  146. package/dist/types/api/deviceUpdateBootloader.d.ts +0 -6
  147. package/dist/types/api/deviceUpdateBootloader.d.ts.map +1 -1
  148. package/dist/types/api/export.d.ts +2 -4
  149. package/dist/types/api/export.d.ts.map +1 -1
  150. package/dist/types/api/firmwareUpdate.d.ts +4 -70
  151. package/dist/types/api/firmwareUpdate.d.ts.map +1 -1
  152. package/dist/types/api/index.d.ts +7 -10
  153. package/dist/types/api/index.d.ts.map +1 -1
  154. package/dist/types/api/protocolV2.d.ts +3 -0
  155. package/dist/types/api/protocolV2.d.ts.map +1 -1
  156. package/dist/types/device.d.ts +2 -2
  157. package/dist/types/device.d.ts.map +1 -1
  158. package/dist/types/params.d.ts +1 -0
  159. package/dist/types/params.d.ts.map +1 -1
  160. package/dist/types/settings.d.ts +38 -21
  161. package/dist/types/settings.d.ts.map +1 -1
  162. package/dist/utils/deviceFeaturesCompat.d.ts +0 -1
  163. package/dist/utils/deviceFeaturesCompat.d.ts.map +1 -1
  164. package/dist/utils/deviceFeaturesUtils.d.ts +4 -0
  165. package/dist/utils/deviceFeaturesUtils.d.ts.map +1 -1
  166. package/dist/utils/deviceInfoUtils.d.ts.map +1 -1
  167. package/dist/utils/deviceSettings.d.ts.map +1 -1
  168. package/dist/utils/homescreen.d.ts +4 -0
  169. package/dist/utils/homescreen.d.ts.map +1 -1
  170. package/dist/utils/index.d.ts +1 -1
  171. package/dist/utils/index.d.ts.map +1 -1
  172. package/dist/utils/patch.d.ts +1 -1
  173. package/dist/utils/patch.d.ts.map +1 -1
  174. package/dist/utils/pro2Nft.d.ts +31 -0
  175. package/dist/utils/pro2Nft.d.ts.map +1 -0
  176. package/dist/utils/pro2Wallpaper.d.ts +10 -0
  177. package/dist/utils/pro2Wallpaper.d.ts.map +1 -1
  178. package/dist/utils/uiProgressThrottle.d.ts +3 -0
  179. package/dist/utils/uiProgressThrottle.d.ts.map +1 -0
  180. package/package.json +4 -4
  181. package/src/api/BaseMethod.ts +8 -10
  182. package/src/api/CheckAllFirmwareRelease.ts +78 -64
  183. package/src/api/DetectDeviceConnectProtocol.ts +18 -0
  184. package/src/api/FirmwareUpdate.ts +7 -15
  185. package/src/api/FirmwareUpdateV2.ts +126 -298
  186. package/src/api/FirmwareUpdateV3.ts +63 -265
  187. package/src/api/FirmwareUpdateV4.ts +645 -1018
  188. package/src/api/GetPassphraseState.ts +7 -1
  189. package/src/api/OpenWalletSession.ts +71 -17
  190. package/src/api/SearchDevices.ts +3 -1
  191. package/src/api/allnetwork/AllNetworkGetAddress.ts +21 -21
  192. package/src/api/allnetwork/AllNetworkGetAddressBase.ts +36 -11
  193. package/src/api/device/DeviceSettings.ts +5 -4
  194. package/src/api/device/DeviceUnlock.ts +8 -3
  195. package/src/api/device/DeviceUpdateBootloader.ts +6 -125
  196. package/src/api/firmware/FirmwareUpdateBaseMethod.ts +27 -83
  197. package/src/api/firmware/updateBootloader.ts +4 -19
  198. package/src/api/firmware/uploadFirmware.ts +39 -144
  199. package/src/api/helpers/protocolV2FileWrite.ts +252 -63
  200. package/src/api/index.ts +2 -0
  201. package/src/api/kaspa/KaspaGetAddress.ts +1 -1
  202. package/src/api/kaspa/KaspaSignTransaction.ts +2 -13
  203. package/src/api/polkadot/networks.ts +3 -3
  204. package/src/api/protocol-v2/DeviceUploadNft.ts +197 -0
  205. package/src/api/protocol-v2/ProtocolInfoRequest.ts +3 -1
  206. package/src/api/protocol-v2/helpers.ts +1 -1
  207. package/src/api/solana/SolSignMessage.ts +1 -1
  208. package/src/api/solana/SolSignOffchainMessage.ts +1 -1
  209. package/src/api/solana/SolSignTransaction.ts +2 -2
  210. package/src/api/sui/SuiGetAddress.ts +1 -1
  211. package/src/api/sui/SuiGetPublicKey.ts +1 -1
  212. package/src/api/sui/SuiSignMessage.ts +1 -1
  213. package/src/api/sui/SuiSignTransaction.ts +1 -1
  214. package/src/api/ton/TonGetAddress.ts +1 -1
  215. package/src/api/ton/TonSignMessage.ts +1 -1
  216. package/src/api/ton/TonSignProof.ts +1 -1
  217. package/src/api/tron/TronSignMessage.ts +2 -2
  218. package/src/api/tron/TronSignTransaction.ts +1 -1
  219. package/src/core/deviceEventRegistration.ts +4 -0
  220. package/src/core/index.ts +213 -111
  221. package/src/core/uiPromiseRegistry.ts +63 -0
  222. package/src/data/messages/messages-protocol-v2.json +25 -0
  223. package/src/data-manager/DataManager.ts +113 -41
  224. package/src/data-manager/TransportManager.ts +6 -0
  225. package/src/data-manager/connectSettings.ts +0 -11
  226. package/src/device/Device.ts +274 -49
  227. package/src/device/DeviceCommands.ts +31 -4
  228. package/src/device/DeviceConnector.ts +33 -13
  229. package/src/device/DevicePool.ts +21 -7
  230. package/src/deviceProfile/buildDeviceFeatures.ts +2 -2
  231. package/src/deviceProfile/protocolV2DeviceIdentity.ts +3 -0
  232. package/src/events/device.ts +4 -0
  233. package/src/events/logBlockEvent.ts +1 -0
  234. package/src/events/ui-promise.ts +4 -2
  235. package/src/events/ui-request.ts +36 -2
  236. package/src/events/ui-response.ts +12 -2
  237. package/src/index.ts +0 -5
  238. package/src/inject.ts +60 -24
  239. package/src/lowLevelInject.ts +7 -8
  240. package/src/protocols/protocol-v2/features.ts +10 -7
  241. package/src/protocols/protocol-v2/firmware.ts +2 -0
  242. package/src/protocols/protocol-v2/resources.ts +394 -0
  243. package/src/protocols/protocol-v2/uiInteraction.ts +46 -6
  244. package/src/protocols/protocol-v2/unlockPolicy.ts +1 -105
  245. package/src/protocols/protocol-v2/unlockPolicyRunner.ts +131 -0
  246. package/src/protocols/protocol-v2/walletSession.ts +214 -36
  247. package/src/topLevelInject.ts +7 -8
  248. package/src/types/api/checkAllFirmwareRelease.ts +3 -5
  249. package/src/types/api/detectDeviceConnectProtocol.ts +6 -0
  250. package/src/types/api/deviceUnlock.ts +12 -1
  251. package/src/types/api/deviceUpdateBootloader.ts +0 -6
  252. package/src/types/api/export.ts +1 -18
  253. package/src/types/api/firmwareUpdate.ts +6 -78
  254. package/src/types/api/index.ts +13 -14
  255. package/src/types/api/protocolV2.ts +13 -0
  256. package/src/types/device.ts +15 -3
  257. package/src/types/params.ts +7 -1
  258. package/src/types/settings.ts +61 -22
  259. package/src/utils/deviceFeaturesCompat.ts +5 -6
  260. package/src/utils/deviceFeaturesUtils.ts +14 -3
  261. package/src/utils/deviceInfoUtils.ts +2 -0
  262. package/src/utils/deviceSettings.ts +3 -0
  263. package/src/utils/homescreen.ts +32 -6
  264. package/src/utils/index.ts +6 -1
  265. package/src/utils/pro2Nft.ts +142 -0
  266. package/src/utils/pro2Wallpaper.ts +35 -8
  267. package/src/utils/uiProgressThrottle.ts +63 -0
  268. package/__tests__/device-initialize-timeout.test.ts +0 -56
  269. package/__tests__/firmware-update/check-all-firmware-release.test.ts +0 -175
  270. package/__tests__/firmware-update/device-update-bootloader.test.ts +0 -87
  271. package/__tests__/firmware-update/firmware-artifact-source.test.ts +0 -119
  272. package/__tests__/firmware-update/firmware-host-binding.test.ts +0 -70
  273. package/__tests__/firmware-update/firmware-preparation-error.test.ts +0 -27
  274. package/__tests__/firmware-update/firmware-update-bootloader-poll.test.ts +0 -200
  275. package/__tests__/firmware-update/firmware-update-capabilities.test.ts +0 -13
  276. package/__tests__/firmware-update/firmware-update-plan-bootloader-identity.test.ts +0 -394
  277. package/__tests__/firmware-update/firmware-update-plan.test.ts +0 -593
  278. package/__tests__/firmware-update/firmware-update-prepared-plan.test.ts +0 -231
  279. package/__tests__/firmware-update/firmware-update-v3-reconnect.test.ts +0 -58
  280. package/__tests__/firmware-update/firmware-upload-source.test.ts +0 -70
  281. package/dist/api/firmware/FirmwareArtifactSource.d.ts +0 -25
  282. package/dist/api/firmware/FirmwareArtifactSource.d.ts.map +0 -1
  283. package/dist/api/firmware/FirmwareHostBinding.d.ts +0 -6
  284. package/dist/api/firmware/FirmwareHostBinding.d.ts.map +0 -1
  285. package/dist/api/firmware/FirmwarePreparationError.d.ts +0 -3
  286. package/dist/api/firmware/FirmwarePreparationError.d.ts.map +0 -1
  287. package/dist/api/firmware/FirmwareUpdateCapabilities.d.ts +0 -3
  288. package/dist/api/firmware/FirmwareUpdateCapabilities.d.ts.map +0 -1
  289. package/dist/api/firmware/FirmwareUpdatePlan.d.ts +0 -25
  290. package/dist/api/firmware/FirmwareUpdatePlan.d.ts.map +0 -1
  291. package/dist/api/firmware/FirmwareUpdatePreparedPlan.d.ts +0 -32
  292. package/dist/api/firmware/FirmwareUpdatePreparedPlan.d.ts.map +0 -1
  293. package/dist/api/firmware/progressThrottle.d.ts +0 -3
  294. package/dist/api/firmware/progressThrottle.d.ts.map +0 -1
  295. package/dist/protocols/protocol-v2/lockedError.d.ts +0 -2
  296. package/dist/protocols/protocol-v2/lockedError.d.ts.map +0 -1
  297. package/dist/protocols/protocol-v2/unlockRetry.d.ts +0 -10
  298. package/dist/protocols/protocol-v2/unlockRetry.d.ts.map +0 -1
  299. package/dist/types/api/firmwareUpdateCapabilities.d.ts +0 -9
  300. package/dist/types/api/firmwareUpdateCapabilities.d.ts.map +0 -1
  301. package/dist/types/api/firmwareUpdatePlan.d.ts +0 -28
  302. package/dist/types/api/firmwareUpdatePlan.d.ts.map +0 -1
  303. package/dist/types/api/firmwareUpdatePreparedPlan.d.ts +0 -42
  304. package/dist/types/api/firmwareUpdatePreparedPlan.d.ts.map +0 -1
  305. package/src/api/firmware/FirmwareArtifactSource.ts +0 -278
  306. package/src/api/firmware/FirmwareHostBinding.ts +0 -100
  307. package/src/api/firmware/FirmwarePreparationError.ts +0 -12
  308. package/src/api/firmware/FirmwareUpdateCapabilities.ts +0 -9
  309. package/src/api/firmware/FirmwareUpdatePlan.ts +0 -772
  310. package/src/api/firmware/FirmwareUpdatePreparedPlan.ts +0 -449
  311. package/src/api/firmware/progressThrottle.ts +0 -44
  312. package/src/protocols/protocol-v2/lockedError.ts +0 -10
  313. package/src/protocols/protocol-v2/unlockRetry.ts +0 -109
  314. package/src/types/api/firmwareUpdateCapabilities.ts +0 -9
  315. package/src/types/api/firmwareUpdatePlan.ts +0 -38
  316. package/src/types/api/firmwareUpdatePreparedPlan.ts +0 -53
@@ -115,8 +115,8 @@ describe('DeviceSettings protocol routing', () => {
115
115
  );
116
116
  });
117
117
 
118
- it('uses DeviceSettingsSet and commits the confirmed patch for Protocol V2', async () => {
119
- const { device, typedCall, updateState } = createDevice({ protocol: 'V2' });
118
+ it('uses DeviceSettingsSet and reloads Protocol V2 status and settings from the device', async () => {
119
+ const { device, typedCall, updateState, getDeviceState } = createDevice({ protocol: 'V2' });
120
120
  const method = new DeviceSettings({
121
121
  id: 2,
122
122
  payload: {
@@ -141,21 +141,10 @@ describe('DeviceSettings protocol routing', () => {
141
141
  haptic_feedback: true,
142
142
  },
143
143
  });
144
- expect(updateState).toHaveBeenCalledWith(
145
- {
146
- identity: { label: 'Shared Label' },
147
- settings: {
148
- language: 'ja',
149
- bleEnabled: true,
150
- autoLockDelayMs: 60_000,
151
- hapticFeedback: true,
152
- },
153
- },
154
- 'settings-write'
155
- );
156
- expect(typedCall.mock.calls.map(call => call[0])).not.toEqual(
157
- expect.arrayContaining(['DeviceInfoGet', 'DeviceStatusGet', 'DeviceSettingsGet'])
158
- );
144
+ expect(getDeviceState).toHaveBeenCalledWith({
145
+ refreshSections: ['status', 'settings'],
146
+ });
147
+ expect(updateState).not.toHaveBeenCalled();
159
148
  });
160
149
 
161
150
  it('uses the Pro2 passphrase page as a device-side toggle and verifies the target state', async () => {
@@ -183,7 +172,39 @@ describe('DeviceSettings protocol routing', () => {
183
172
  });
184
173
  expect(typedCall).not.toHaveBeenCalledWith('DeviceSettingsSet', 'Success', expect.anything());
185
174
  expect(getDeviceState).toHaveBeenNthCalledWith(1, { refreshSections: ['status'] });
186
- expect(getDeviceState).toHaveBeenNthCalledWith(2, { refreshSections: ['status'] });
175
+ expect(getDeviceState).toHaveBeenNthCalledWith(2, {
176
+ refreshSections: ['status', 'settings'],
177
+ });
178
+ });
179
+
180
+ it('reloads Protocol V2 status and settings after changing air-gap mode', async () => {
181
+ const { device, typedCall, getDeviceState } = createDevice({ protocol: 'V2' });
182
+ getDeviceState
183
+ .mockResolvedValueOnce({
184
+ settings: { airgapMode: false },
185
+ })
186
+ .mockResolvedValueOnce({
187
+ status: { unlocked: true },
188
+ settings: { airgapMode: true },
189
+ });
190
+ const method = new DeviceSettings({
191
+ id: 4,
192
+ payload: {
193
+ method: 'deviceSettings',
194
+ airgapMode: true,
195
+ },
196
+ });
197
+ method.init();
198
+ (method as any).device = device;
199
+
200
+ await expect(method.run()).resolves.toEqual({ message: 'Success' });
201
+ expect(typedCall).toHaveBeenCalledWith('DeviceSettingsPageShow', 'Success', {
202
+ page: DeviceSettingsPage.DeviceAirgap,
203
+ });
204
+ expect(getDeviceState).toHaveBeenNthCalledWith(1, { refreshSections: ['settings'] });
205
+ expect(getDeviceState).toHaveBeenNthCalledWith(2, {
206
+ refreshSections: ['status', 'settings'],
207
+ });
187
208
  });
188
209
 
189
210
  it('does not open a Pro2 settings page when the hardware already matches the target', async () => {
@@ -1,4 +1,5 @@
1
1
  import { DEVICE } from '../src/events';
2
+
2
3
  import type { DeviceState, DeviceStateEvent } from '../src/types';
3
4
 
4
5
  describe('DeviceState contract', () => {
@@ -1,6 +1,6 @@
1
1
  import { EDeviceType, EFirmwareType } from '@onekeyfe/hd-shared';
2
-
3
2
  import { DeviceType, Enum_SafetyCheckLevel } from '@onekeyfe/hd-transport';
3
+
4
4
  import {
5
5
  mapApplySettingsToState,
6
6
  mapDeviceSettingsToState,
@@ -145,6 +145,18 @@ describe('DeviceStateMapper', () => {
145
145
  expect(patch.status?.unlocked).toBeUndefined();
146
146
  });
147
147
 
148
+ test('maps the Protocol V2 Neo device type without treating it as Pro2', () => {
149
+ const patch = mapProtocolV2DeviceInfoToState({
150
+ hw: { Device_type: DeviceType.NEO, serial_no: 'NEO-SERIAL-1' },
151
+ });
152
+
153
+ expect(patch.identity).toMatchObject({
154
+ deviceType: EDeviceType.Neo,
155
+ model: 'neo',
156
+ serialNo: 'NEO-SERIAL-1',
157
+ });
158
+ });
159
+
148
160
  test('maps the hardware model independently from Protocol V2', () => {
149
161
  const pro = mapProtocolV2DeviceInfoToState({
150
162
  protocol_version: 2,
@@ -1,10 +1,13 @@
1
1
  import { EDeviceType, EFirmwareType } from '@onekeyfe/hd-shared';
2
+ import { DeviceType } from '@onekeyfe/hd-transport';
2
3
 
3
4
  import {
4
5
  getDeviceSerialNo,
5
6
  getDeviceType,
7
+ getDeviceTypeByBleName,
6
8
  getDeviceUUID,
7
9
  getFirmwareType,
10
+ getMethodVersionRange,
8
11
  } from '../src/utils/deviceInfoUtils';
9
12
  import {
10
13
  getDeviceBLEFirmwareVersion,
@@ -85,7 +88,7 @@ describe('device feature selectors', () => {
85
88
  test('reads normalized Protocol V2 features through the same public selectors', () => {
86
89
  const features = buildProtocolV2FeaturesPayload({
87
90
  deviceInfo: {
88
- hw: { serial_no: 'P2-001' },
91
+ hw: { Device_type: DeviceType.PRO2, serial_no: 'P2-001' },
89
92
  fw: {
90
93
  application: { version: '5.0.0' },
91
94
  bootloader: { version: '2.0.0' },
@@ -104,4 +107,48 @@ describe('device feature selectors', () => {
104
107
  expect(getDeviceBoardloaderVersion(features)).toEqual([1, 0, 0]);
105
108
  expect(getDeviceBLEFirmwareVersion(features)).toEqual([3, 0, 0]);
106
109
  });
110
+
111
+ test('preserves the real Neo type in Protocol V2 compatibility features', () => {
112
+ const features = buildProtocolV2FeaturesPayload({
113
+ deviceInfo: {
114
+ hw: { Device_type: DeviceType.NEO, serial_no: 'NEO-001' },
115
+ },
116
+ });
117
+
118
+ expect(features.deviceType).toBe(EDeviceType.Neo);
119
+ expect(features.onekey_device_type).toBe('NEO');
120
+ expect(getDeviceType(features)).toBe(EDeviceType.Neo);
121
+ });
122
+
123
+ test('recognizes Neo discovery names before initialization', () => {
124
+ expect(getDeviceTypeByBleName('OneKey Neo 1234')).toBe(EDeviceType.Neo);
125
+ expect(getDeviceTypeByBleName('Neo 1234')).toBe(EDeviceType.Neo);
126
+ });
127
+
128
+ test.each([EDeviceType.Pro2, EDeviceType.Neo])(
129
+ 'uses the Pro2 product model as the chain capability fallback for %s',
130
+ deviceType => {
131
+ const checkedTypes: string[] = [];
132
+ const versionRange = getMethodVersionRange({ deviceType } as PROTO.Features, type => {
133
+ checkedTypes.push(type);
134
+ return type === 'model_pro2' ? { min: '1.0.0' } : undefined;
135
+ });
136
+
137
+ expect(versionRange).toEqual({ min: '1.0.0' });
138
+ expect(checkedTypes).toEqual([deviceType, 'model_pro2']);
139
+ }
140
+ );
141
+
142
+ test('prefers a Neo-specific chain range over the shared Pro2 product model', () => {
143
+ const versionRange = getMethodVersionRange(
144
+ { deviceType: EDeviceType.Neo } as PROTO.Features,
145
+ type => {
146
+ if (type === EDeviceType.Neo) return { min: '2.0.0' };
147
+ if (type === 'model_pro2') return { min: '1.0.0' };
148
+ return undefined;
149
+ }
150
+ );
151
+
152
+ expect(versionRange).toEqual({ min: '2.0.0' });
153
+ });
107
154
  });
@@ -1,13 +1,13 @@
1
1
  import { EDeviceType } from '@onekeyfe/hd-shared';
2
2
 
3
3
  import {
4
+ PROTOCOL_V2_NEVER_TIMEOUT_MS,
4
5
  getAutoLockOptions,
5
6
  getAutoShutDownOptions,
6
7
  getDeviceSettingsCapabilities,
7
8
  getLanguageConfig,
8
9
  mapLanguageFromProtocolV2,
9
10
  mapLanguageToProtocolV2,
10
- PROTOCOL_V2_NEVER_TIMEOUT_MS,
11
11
  } from '../src/utils/deviceSettings';
12
12
 
13
13
  describe('Pro 2 device settings options', () => {
@@ -72,6 +72,16 @@ describe('Pro 2 device settings options', () => {
72
72
  ]);
73
73
  });
74
74
 
75
+ test('uses the shared Protocol V2 settings model for Neo', () => {
76
+ expect(getLanguageConfig(EDeviceType.Neo)).toEqual(getLanguageConfig(EDeviceType.Pro2));
77
+ expect(getAutoLockOptions(EDeviceType.Neo, 'V2')).toEqual(
78
+ getAutoLockOptions(EDeviceType.Pro2, 'V2')
79
+ );
80
+ expect(getAutoShutDownOptions(EDeviceType.Neo, 'V2')).toEqual(
81
+ getAutoShutDownOptions(EDeviceType.Pro2, 'V2')
82
+ );
83
+ });
84
+
75
85
  test('keeps the legacy and Protocol V2 never values protocol-specific', () => {
76
86
  expect(getAutoLockOptions(EDeviceType.Pro, 'V1').slice(-1)[0]).toEqual({
77
87
  label: 'Never',
@@ -0,0 +1,316 @@
1
+ import { HardwareErrorCode } from '@onekeyfe/hd-shared';
2
+
3
+ import DeviceUploadNft from '../src/api/protocol-v2/DeviceUploadNft';
4
+
5
+ jest.mock('../src/data/config', () => ({
6
+ getSDKVersion: jest.fn(() => '1.0.0'),
7
+ DEFAULT_DOMAIN: 'https://jssdk.onekey.so/1.0.0/',
8
+ }));
9
+
10
+ const createRgba = (width: number, height: number) => {
11
+ const data = new Uint8Array(width * height * 4);
12
+ for (let index = 3; index < data.length; index += 4) data[index] = 0xff;
13
+ return data;
14
+ };
15
+
16
+ const createMethod = ({
17
+ typedCall,
18
+ supportedMessages = [60802, 60805, 60808, 61500],
19
+ useFullBundle = false,
20
+ }: {
21
+ typedCall: jest.Mock;
22
+ supportedMessages?: number[];
23
+ useFullBundle?: boolean;
24
+ }) => {
25
+ const method = new DeviceUploadNft({
26
+ id: 1,
27
+ payload: {
28
+ method: 'deviceUploadNft',
29
+ image: { width: 540, height: 540, rgba: createRgba(540, 540) },
30
+ thumbnail: { width: 263, height: 263, rgba: createRgba(263, 263) },
31
+ title: 'CryptoPunk #3100',
32
+ subtitle: 'CryptoPunks',
33
+ timestampMs: 1_760_000_000_000,
34
+ chunkSize: 2048,
35
+ paceMs: 0,
36
+ },
37
+ });
38
+ (method as any).device = {
39
+ commands: { typedCall },
40
+ ensureProtocolV2RuntimeContext: jest.fn(() =>
41
+ Promise.resolve({
42
+ version: 2,
43
+ build_fingerprint: 'application__1.0.0__test__DEV__DEBUG',
44
+ supported_messages: supportedMessages,
45
+ })
46
+ ),
47
+ getCurrentFirmwareType: jest.fn(),
48
+ };
49
+ method.postMessage = jest.fn();
50
+
51
+ if (useFullBundle) {
52
+ method.init();
53
+ } else {
54
+ (method as any).params = { chunkSize: 2048, paceMs: 0, timeoutMs: 15_000 };
55
+ (method as any).bundle = {
56
+ basename: 'nft-deadbeef-1760000000000',
57
+ image: new Uint8Array([1]),
58
+ thumbnail: new Uint8Array([2]),
59
+ metadata: new TextEncoder().encode('{"title":"NFT","subtitle":""}'),
60
+ };
61
+ }
62
+
63
+ return method;
64
+ };
65
+
66
+ const fileWriteSuccess = (params: { file: { offset: number; data: Uint8Array } }) => ({
67
+ message: {
68
+ processed_byte: params.file.offset + params.file.data.byteLength,
69
+ },
70
+ });
71
+
72
+ const nftFileList = (count: number, basenameAt?: number) =>
73
+ Array.from({ length: count }, (_, index) => {
74
+ const basename =
75
+ index === basenameAt
76
+ ? 'nft-deadbeef-1760000000000'
77
+ : `nft-${index.toString(16).padStart(8, '0')}-${1760000000001 + index}`;
78
+ return [`${basename}.bin`, `${basename}_m.bin`, `${basename}.json`];
79
+ })
80
+ .flat()
81
+ .join('\n');
82
+
83
+ describe('DeviceUploadNft', () => {
84
+ test('defaults to maximum-size chunks without artificial pacing', () => {
85
+ const method = new DeviceUploadNft({
86
+ id: 1,
87
+ payload: {
88
+ method: 'deviceUploadNft',
89
+ image: { width: 540, height: 540, rgba: createRgba(540, 540) },
90
+ thumbnail: { width: 263, height: 263, rgba: createRgba(263, 263) },
91
+ title: 'CryptoPunk #3100',
92
+ subtitle: 'CryptoPunks',
93
+ timestampMs: 1_760_000_000_000,
94
+ },
95
+ });
96
+
97
+ method.init();
98
+
99
+ expect((method as any).params).toMatchObject({
100
+ chunkSize: 2048,
101
+ paceMs: 0,
102
+ });
103
+ });
104
+
105
+ test('uploads the triplet in order without creating the firmware-owned directory', async () => {
106
+ const typedCall = jest.fn((request: string, _response: string, params: any) => {
107
+ if (request === 'FilesystemPathInfoQuery') {
108
+ return { message: { exist: true, directory: true } };
109
+ }
110
+ if (request === 'FilesystemDirList') {
111
+ return { message: { path: 'vol1:/nft', child_files: '' } };
112
+ }
113
+ if (request === 'FilesystemFileWrite') return fileWriteSuccess(params);
114
+ if (request === 'NftUpdate') return { message: { message: 'NFT updated' } };
115
+ throw new Error(`Unexpected request: ${request}`);
116
+ });
117
+ const method = createMethod({ typedCall, useFullBundle: true });
118
+
119
+ const result = await method.run();
120
+
121
+ const requests = typedCall.mock.calls.map(call => call[0]);
122
+ expect(requests[0]).toBe('FilesystemPathInfoQuery');
123
+ expect(typedCall).toHaveBeenNthCalledWith(
124
+ 1,
125
+ 'FilesystemPathInfoQuery',
126
+ 'FilesystemPathInfo',
127
+ { path: 'vol1:/nft' },
128
+ { timeoutMs: 15_000 }
129
+ );
130
+ expect(typedCall).toHaveBeenNthCalledWith(
131
+ 2,
132
+ 'FilesystemDirList',
133
+ 'FilesystemDir',
134
+ { path: 'vol1:/nft', depth: 1 },
135
+ { timeoutMs: 15_000 }
136
+ );
137
+ expect(requests).not.toContain('FilesystemDirMake');
138
+ expect(requests.at(-1)).toBe('NftUpdate');
139
+ const fileWrites = typedCall.mock.calls.filter(call => call[0] === 'FilesystemFileWrite');
140
+ const paths = fileWrites.map(call => call[2].file.path as string);
141
+ const imagePath = paths[0];
142
+ const thumbnailStart = paths.findIndex(path => path.endsWith('_m.bin'));
143
+ const metadataStart = paths.findIndex(path => path.endsWith('.json'));
144
+ expect(imagePath).toMatch(/^vol1:\/nft\/nft-[a-f0-9]{8}-1760000000000\.bin$/);
145
+ expect(thumbnailStart).toBeGreaterThan(0);
146
+ expect(metadataStart).toBeGreaterThan(thumbnailStart);
147
+ expect(new Set(paths.slice(0, thumbnailStart))).toEqual(new Set([imagePath]));
148
+ expect(new Set(paths.slice(thumbnailStart, metadataStart))).toEqual(
149
+ new Set([result.thumbnailPath])
150
+ );
151
+ expect(typedCall).toHaveBeenLastCalledWith(
152
+ 'NftUpdate',
153
+ 'Success',
154
+ { file_name_no_ext: result.basename },
155
+ { timeoutMs: 15_000 }
156
+ );
157
+ expect(result).toMatchObject({
158
+ nftUpdated: true,
159
+ message: 'NFT updated',
160
+ totalSize: 583_212 + 138_876 + 53,
161
+ });
162
+ expect(method.postMessage).toHaveBeenLastCalledWith({
163
+ event: 'UI_EVENT',
164
+ type: 'ui-device_progress',
165
+ payload: expect.objectContaining({
166
+ progress: 100,
167
+ transferredBytes: result.totalSize,
168
+ totalBytes: result.totalSize,
169
+ }),
170
+ });
171
+ });
172
+
173
+ test('treats a missing NFT directory as empty before the first upload', async () => {
174
+ const typedCall = jest.fn((request: string, _response: string, params: any) => {
175
+ if (request === 'FilesystemPathInfoQuery') {
176
+ return { message: { exist: false, directory: false } };
177
+ }
178
+ if (request === 'FilesystemFileWrite') return fileWriteSuccess(params);
179
+ if (request === 'NftUpdate') return { message: { message: 'NFT updated' } };
180
+ throw new Error(`Unexpected request: ${request}`);
181
+ });
182
+ const method = createMethod({ typedCall });
183
+
184
+ await expect(method.run()).resolves.toMatchObject({ nftUpdated: true });
185
+ expect(typedCall.mock.calls.map(call => call[0])).not.toContain('FilesystemDirList');
186
+ expect(typedCall.mock.calls.some(call => call[0] === 'FilesystemFileWrite')).toBe(true);
187
+ });
188
+
189
+ test('does not retry a timed-out NftUpdate', async () => {
190
+ const timeout = Object.assign(new Error('Protocol V2 response timeout'), {
191
+ code: 'response-timeout',
192
+ });
193
+ const typedCall = jest.fn((request: string, _response: string, params: any) => {
194
+ if (request === 'FilesystemPathInfoQuery') {
195
+ return { message: { exist: true, directory: true } };
196
+ }
197
+ if (request === 'FilesystemDirList') {
198
+ return { message: { path: 'vol1:/nft', child_files: '' } };
199
+ }
200
+ if (request === 'FilesystemFileWrite') return fileWriteSuccess(params);
201
+ if (request === 'NftUpdate') throw timeout;
202
+ throw new Error(`Unexpected request: ${request}`);
203
+ });
204
+ const method = createMethod({ typedCall });
205
+
206
+ await expect(method.run()).rejects.toBe(timeout);
207
+
208
+ const updateRequests = typedCall.mock.calls.filter(call => call[0] === 'NftUpdate');
209
+ expect(updateRequests).toHaveLength(1);
210
+ });
211
+
212
+ test('does not retry a non-timeout NftUpdate failure', async () => {
213
+ const typedCall = jest.fn((request: string, _response: string, params: any) => {
214
+ if (request === 'FilesystemPathInfoQuery') {
215
+ return { message: { exist: true, directory: true } };
216
+ }
217
+ if (request === 'FilesystemDirList') {
218
+ return { message: { path: 'vol1:/nft', child_files: '' } };
219
+ }
220
+ if (request === 'FilesystemFileWrite') return fileWriteSuccess(params);
221
+ if (request === 'NftUpdate') throw new Error('Invalid NFT metadata');
222
+ throw new Error(`Unexpected request: ${request}`);
223
+ });
224
+ const method = createMethod({ typedCall });
225
+
226
+ await expect(method.run()).rejects.toThrow('Invalid NFT metadata');
227
+ expect(typedCall.mock.calls.filter(call => call[0] === 'NftUpdate')).toHaveLength(1);
228
+ });
229
+
230
+ test('does not publish when a file write fails', async () => {
231
+ const typedCall = jest.fn((request: string) => {
232
+ if (request === 'FilesystemPathInfoQuery') {
233
+ return { message: { exist: true, directory: true } };
234
+ }
235
+ if (request === 'FilesystemDirList') {
236
+ return { message: { path: 'vol1:/nft', child_files: '' } };
237
+ }
238
+ if (request === 'FilesystemFileWrite') throw new Error('Filesystem full');
239
+ throw new Error(`Unexpected request: ${request}`);
240
+ });
241
+ const method = createMethod({ typedCall });
242
+
243
+ await expect(method.run()).rejects.toThrow('Filesystem full');
244
+ expect(typedCall.mock.calls.some(call => call[0] === 'NftUpdate')).toBe(false);
245
+ });
246
+
247
+ test('rejects unsupported firmware before writing files', async () => {
248
+ const typedCall = jest.fn();
249
+ const method = createMethod({ typedCall, supportedMessages: [60805] });
250
+
251
+ await expect(method.run()).rejects.toBeDefined();
252
+ expect(typedCall).not.toHaveBeenCalled();
253
+ });
254
+
255
+ test('rejects a new NFT before writing when ten complete NFT bundles exist', async () => {
256
+ const typedCall = jest.fn((request: string) => {
257
+ if (request === 'FilesystemPathInfoQuery') {
258
+ return { message: { exist: true, directory: true } };
259
+ }
260
+ if (request === 'FilesystemDirList') {
261
+ return { message: { path: 'vol1:/nft', child_files: nftFileList(10) } };
262
+ }
263
+ throw new Error(`Unexpected request: ${request}`);
264
+ });
265
+ const method = createMethod({ typedCall });
266
+
267
+ await expect(method.run()).rejects.toMatchObject({
268
+ errorCode: HardwareErrorCode.NftStorageLimitReached,
269
+ params: { count: 10, limit: 10 },
270
+ });
271
+ expect(typedCall.mock.calls.map(call => call[0])).toEqual([
272
+ 'FilesystemPathInfoQuery',
273
+ 'FilesystemDirList',
274
+ ]);
275
+ });
276
+
277
+ test('allows an idempotent retry for a basename already counted at the limit', async () => {
278
+ const typedCall = jest.fn((request: string, _response: string, params: any) => {
279
+ if (request === 'FilesystemPathInfoQuery') {
280
+ return { message: { exist: true, directory: true } };
281
+ }
282
+ if (request === 'FilesystemDirList') {
283
+ return { message: { path: 'vol1:/nft', child_files: nftFileList(10, 0) } };
284
+ }
285
+ if (request === 'FilesystemFileWrite') return fileWriteSuccess(params);
286
+ if (request === 'NftUpdate') return { message: {} };
287
+ throw new Error(`Unexpected request: ${request}`);
288
+ });
289
+ const method = createMethod({ typedCall });
290
+
291
+ await expect(method.run()).resolves.toMatchObject({ nftUpdated: true });
292
+ expect(typedCall.mock.calls.some(call => call[0] === 'FilesystemFileWrite')).toBe(true);
293
+ });
294
+
295
+ test('does not count unrelated or incomplete files as stored NFTs', async () => {
296
+ const typedCall = jest.fn((request: string, _response: string, params: any) => {
297
+ if (request === 'FilesystemPathInfoQuery') {
298
+ return { message: { exist: true, directory: true } };
299
+ }
300
+ if (request === 'FilesystemDirList') {
301
+ return {
302
+ message: {
303
+ path: 'vol1:/nft',
304
+ child_files: `${nftFileList(9)}\nnft-ffffffff-1760000009999.json\nnotes.txt`,
305
+ },
306
+ };
307
+ }
308
+ if (request === 'FilesystemFileWrite') return fileWriteSuccess(params);
309
+ if (request === 'NftUpdate') return { message: {} };
310
+ throw new Error(`Unexpected request: ${request}`);
311
+ });
312
+ const method = createMethod({ typedCall });
313
+
314
+ await expect(method.run()).resolves.toMatchObject({ nftUpdated: true });
315
+ });
316
+ });