@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
@@ -5,9 +5,12 @@ import {
5
5
  import { ERRORS, HardwareErrorCode } from '@onekeyfe/hd-shared';
6
6
 
7
7
  import { DataManager } from '../../data-manager';
8
+ import { LoggerNames, getLogger } from '../../utils/logger';
8
9
 
9
10
  import type { DeviceCommands } from '../../device/DeviceCommands';
10
11
 
12
+ const Log = getLogger(LoggerNames.Core);
13
+
11
14
  export type ProtocolV2FileWriteData = ArrayBuffer | Uint8Array | Blob | string;
12
15
 
13
16
  export type ProtocolV2FileWriteProgress = {
@@ -18,6 +21,12 @@ export type ProtocolV2FileWriteProgress = {
18
21
  elapsedMs: number;
19
22
  };
20
23
 
24
+ export type ProtocolV2FileWriteChunkContext = {
25
+ offset: number;
26
+ chunkLength: number;
27
+ totalSize: number;
28
+ };
29
+
21
30
  export type ProtocolV2FileWriteOptions = {
22
31
  commands: Pick<DeviceCommands, 'typedCall'>;
23
32
  path: string;
@@ -30,20 +39,77 @@ export type ProtocolV2FileWriteOptions = {
30
39
  append?: boolean;
31
40
  uiPercentage?: number;
32
41
  timeoutMs?: number;
42
+ writeWithResponse?: boolean;
33
43
  maxChunkRetries?: number;
44
+ paceMs?: number;
34
45
  throwIfAborted?: () => void;
46
+ getUiPercentage?: (context: ProtocolV2FileWriteChunkContext) => number | undefined;
35
47
  onProgress?: (progress: ProtocolV2FileWriteProgress) => void;
36
48
  };
37
49
 
38
50
  const MIN_FILE_CHUNK_SIZE = 64;
51
+ const FILE_TRANSFER_RATE_WINDOW_MS = 1000;
52
+ const FILE_TRANSFER_LOG_INTERVAL_MS = 10_000;
53
+ const SESSION_ERROR = 'session not found';
54
+
55
+ function formatFileTransferRate(bytesPerSecond: number) {
56
+ return (Math.max(bytesPerSecond, 0) / 1024).toFixed(2);
57
+ }
58
+
59
+ function getAverageFileTransferRate(transferredBytes: number, elapsedMs: number) {
60
+ if (elapsedMs <= 0) return 0;
61
+ return Math.round((Math.max(transferredBytes, 0) / elapsedMs) * 1000);
62
+ }
63
+
64
+ function getFileTransferTransport() {
65
+ const env = DataManager.getSettings('env');
66
+ return env && DataManager.isBleConnect(env) ? 'BLE' : String(env ?? 'unknown');
67
+ }
68
+
69
+ function logFileTransferMetrics({
70
+ transport,
71
+ status,
72
+ path,
73
+ transferredBytes,
74
+ totalBytes,
75
+ elapsedMs,
76
+ rateBytesPerSecond,
77
+ hostWriteElapsedMs,
78
+ responseWaitElapsedMs,
79
+ measuredAttempts,
80
+ }: {
81
+ transport: string;
82
+ status: 'progress' | 'completed' | 'failed';
83
+ path: string;
84
+ transferredBytes: number;
85
+ totalBytes: number;
86
+ elapsedMs: number;
87
+ rateBytesPerSecond: number;
88
+ hostWriteElapsedMs: number;
89
+ responseWaitElapsedMs: number;
90
+ measuredAttempts: number;
91
+ }) {
92
+ const segmentedMetrics =
93
+ measuredAttempts > 0
94
+ ? ` hostWriteTotal=${(hostWriteElapsedMs / 1000).toFixed(2)}s responseWaitTotal=${(
95
+ responseWaitElapsedMs / 1000
96
+ ).toFixed(2)}s measuredAttempts=${measuredAttempts}`
97
+ : '';
98
+ Log.log(
99
+ `[FileWrite] metrics transport=${transport} status=${status} path=${path} bytes=${transferredBytes}/${totalBytes} elapsed=${(
100
+ elapsedMs / 1000
101
+ ).toFixed(2)}s speed=${formatFileTransferRate(rateBytesPerSecond)} KiB/s${segmentedMetrics}`
102
+ );
103
+ }
39
104
 
40
- function isRetryableFileWriteTimeout(error: unknown) {
105
+ export function isProtocolV2ResponseTimeout(error: unknown) {
41
106
  if (!error || typeof error !== 'object') return false;
42
- const candidate = error as { errorCode?: number; code?: number; message?: string };
107
+ const candidate = error as { errorCode?: number; code?: number | string; message?: string };
43
108
  const code = candidate.errorCode ?? candidate.code;
44
109
  return (
45
110
  code === HardwareErrorCode.BleTimeoutError ||
46
- /Lowlevel response timeout/i.test(candidate.message ?? '')
111
+ code === 'response-timeout' ||
112
+ /(?:BLE|Lowlevel|Protocol V2) response timeout/i.test(candidate.message ?? '')
47
113
  );
48
114
  }
49
115
 
@@ -119,75 +185,198 @@ export async function writeProtocolV2File(options: ProtocolV2FileWriteOptions) {
119
185
  let chunks = 0;
120
186
  let lastMessage: Record<string, unknown> | undefined;
121
187
  const startTime = Date.now();
188
+ let rateWindowStartedAt = startTime;
189
+ let rateWindowStartedBytes = 0;
190
+ let rateBytesPerSecond: number | undefined;
191
+ let lastConfirmedAt = startTime;
192
+ let logWindowStartedAt = startTime;
193
+ let logWindowStartedBytes = 0;
194
+ let hostWriteElapsedMs = 0;
195
+ let responseWaitElapsedMs = 0;
196
+ let measuredAttempts = 0;
197
+ const transport = getFileTransferTransport();
198
+ Log.log(
199
+ `[FileWrite] started transport=${transport} path=${options.path} bytes=${dataLength} offset=${startOffset} chunk=${chunkSize}`
200
+ );
122
201
 
123
- while (written < dataLength) {
124
- options.throwIfAborted?.();
125
- const chunk = data.slice(written, Math.min(written + chunkSize, dataLength));
126
- const offset = startOffset + written;
127
- const progress =
128
- options.uiPercentage ??
129
- getDeviceTransferProgress(offset, offset + chunk.byteLength, totalSize);
130
- const request = {
131
- file: { path: options.path, offset, total_size: totalSize, data: chunk },
132
- overwrite: chunks === 0 ? options.overwrite ?? false : false,
133
- append: options.append ?? false,
134
- ui_percentage: progress,
135
- };
136
- const maxChunkRetries = Math.max(Math.floor(options.maxChunkRetries ?? 0), 0);
137
- let retryCount = 0;
138
- let response;
139
- let isWritePending = true;
140
- while (isWritePending) {
141
- try {
142
- response = await options.commands.typedCall(
143
- 'FilesystemFileWrite',
144
- 'FilesystemFile',
145
- request,
146
- { timeoutMs: options.timeoutMs }
202
+ try {
203
+ while (written < dataLength) {
204
+ options.throwIfAborted?.();
205
+ const chunk = data.slice(written, Math.min(written + chunkSize, dataLength));
206
+ const offset = startOffset + written;
207
+ const progress =
208
+ options.uiPercentage ??
209
+ options.getUiPercentage?.({
210
+ offset,
211
+ chunkLength: chunk.byteLength,
212
+ totalSize,
213
+ }) ??
214
+ getDeviceTransferProgress(offset, offset + chunk.byteLength, totalSize);
215
+ const request = {
216
+ file: { path: options.path, offset, total_size: totalSize, data: chunk },
217
+ overwrite: chunks === 0 ? options.overwrite ?? false : false,
218
+ append: options.append ?? false,
219
+ ui_percentage: progress,
220
+ };
221
+ const maxChunkRetries = Math.max(Math.floor(options.maxChunkRetries ?? 0), 0);
222
+ let retryCount = 0;
223
+ let response;
224
+ let isWritePending = true;
225
+ while (isWritePending) {
226
+ let currentHostWriteElapsedMs: number | undefined;
227
+ let responseWaitStartedAt: number | undefined;
228
+ try {
229
+ const callOptions =
230
+ options.writeWithResponse === undefined
231
+ ? {
232
+ timeoutMs: options.timeoutMs,
233
+ onWriteCompleted: ({ elapsedMs }: { elapsedMs: number }) => {
234
+ currentHostWriteElapsedMs = elapsedMs;
235
+ responseWaitStartedAt = Date.now();
236
+ },
237
+ }
238
+ : {
239
+ ...(options.timeoutMs === undefined ? {} : { timeoutMs: options.timeoutMs }),
240
+ writeWithResponse: options.writeWithResponse,
241
+ onWriteCompleted: ({ elapsedMs }: { elapsedMs: number }) => {
242
+ currentHostWriteElapsedMs = elapsedMs;
243
+ responseWaitStartedAt = Date.now();
244
+ },
245
+ };
246
+ response = await options.commands.typedCall(
247
+ 'FilesystemFileWrite',
248
+ 'FilesystemFile',
249
+ request,
250
+ callOptions
251
+ );
252
+ isWritePending = false;
253
+ } catch (error) {
254
+ if (retryCount >= maxChunkRetries || !isProtocolV2ResponseTimeout(error)) throw error;
255
+ retryCount += 1;
256
+ options.throwIfAborted?.();
257
+ } finally {
258
+ if (currentHostWriteElapsedMs !== undefined && responseWaitStartedAt !== undefined) {
259
+ hostWriteElapsedMs += currentHostWriteElapsedMs;
260
+ responseWaitElapsedMs += Math.max(Date.now() - responseWaitStartedAt, 0);
261
+ measuredAttempts += 1;
262
+ }
263
+ }
264
+ }
265
+ if (!response) {
266
+ throw ERRORS.TypedError(
267
+ HardwareErrorCode.RuntimeError,
268
+ 'FilesystemFileWrite completed without a response'
147
269
  );
148
- isWritePending = false;
149
- } catch (error) {
150
- if (retryCount >= maxChunkRetries || !isRetryableFileWriteTimeout(error)) throw error;
151
- retryCount += 1;
152
- options.throwIfAborted?.();
270
+ }
271
+ const responseType = (response as { type?: string }).type;
272
+ if (responseType && responseType !== 'FilesystemFile') {
273
+ const responseError = (response as { message?: { error?: unknown } }).message?.error;
274
+ if (typeof responseError === 'string' && responseError.includes(SESSION_ERROR)) {
275
+ throw ERRORS.TypedError(HardwareErrorCode.RuntimeError, SESSION_ERROR);
276
+ }
277
+ throw ERRORS.TypedError(
278
+ HardwareErrorCode.RuntimeError,
279
+ `FilesystemFileWrite received unexpected response ${responseType}`
280
+ );
281
+ }
282
+ options.throwIfAborted?.();
283
+ lastMessage = response.message;
284
+ const rawProcessedByte = response.message?.processed_byte;
285
+ const processedByte = Number(rawProcessedByte);
286
+ if (
287
+ rawProcessedByte !== undefined &&
288
+ (!Number.isFinite(processedByte) ||
289
+ processedByte <= offset ||
290
+ processedByte > offset + chunk.byteLength)
291
+ ) {
292
+ throw ERRORS.TypedError(
293
+ HardwareErrorCode.RuntimeError,
294
+ `FilesystemFileWrite invalid processed_byte ${processedByte}`
295
+ );
296
+ }
297
+ written =
298
+ rawProcessedByte === undefined ? written + chunk.byteLength : processedByte - startOffset;
299
+ chunks += 1;
300
+ const now = Date.now();
301
+ lastConfirmedAt = now;
302
+ const elapsedMs = now - startTime;
303
+ const transferredBytes = Math.min(written, dataLength);
304
+ const rateWindowElapsedMs = now - rateWindowStartedAt;
305
+ if (rateWindowElapsedMs >= FILE_TRANSFER_RATE_WINDOW_MS) {
306
+ const rateWindowBytes = Math.max(transferredBytes - rateWindowStartedBytes, 0);
307
+ rateBytesPerSecond = Math.round((rateWindowBytes / rateWindowElapsedMs) * 1000);
308
+ rateWindowStartedAt = now;
309
+ rateWindowStartedBytes = transferredBytes;
310
+ } else if (rateBytesPerSecond === undefined && elapsedMs > 0) {
311
+ rateBytesPerSecond = Math.round((transferredBytes / elapsedMs) * 1000);
312
+ }
313
+ options.onProgress?.({
314
+ progress: getConfirmedProgress(startOffset + written, totalSize, written, dataLength),
315
+ transferredBytes,
316
+ totalBytes: dataLength,
317
+ rateBytesPerSecond,
318
+ elapsedMs,
319
+ });
320
+ const logWindowElapsedMs = now - logWindowStartedAt;
321
+ if (logWindowElapsedMs >= FILE_TRANSFER_LOG_INTERVAL_MS && transferredBytes < dataLength) {
322
+ const logWindowBytes = Math.max(transferredBytes - logWindowStartedBytes, 0);
323
+ logFileTransferMetrics({
324
+ transport,
325
+ status: 'progress',
326
+ path: options.path,
327
+ transferredBytes,
328
+ totalBytes: dataLength,
329
+ elapsedMs,
330
+ rateBytesPerSecond: getAverageFileTransferRate(logWindowBytes, logWindowElapsedMs),
331
+ hostWriteElapsedMs,
332
+ responseWaitElapsedMs,
333
+ measuredAttempts,
334
+ });
335
+ logWindowStartedAt = now;
336
+ logWindowStartedBytes = transferredBytes;
337
+ }
338
+ if (options.paceMs && options.paceMs > 0) {
339
+ await new Promise(resolve => {
340
+ setTimeout(resolve, options.paceMs);
341
+ });
153
342
  }
154
343
  }
155
- if (!response) {
156
- throw ERRORS.TypedError(
157
- HardwareErrorCode.RuntimeError,
158
- 'FilesystemFileWrite completed without a response'
159
- );
160
- }
161
- options.throwIfAborted?.();
162
- lastMessage = response.message;
163
- const rawProcessedByte = response.message?.processed_byte;
164
- const processedByte = Number(rawProcessedByte);
165
- if (
166
- rawProcessedByte !== undefined &&
167
- (!Number.isFinite(processedByte) ||
168
- processedByte <= offset ||
169
- processedByte > offset + chunk.byteLength)
170
- ) {
171
- throw ERRORS.TypedError(
172
- HardwareErrorCode.RuntimeError,
173
- `FilesystemFileWrite invalid processed_byte ${processedByte}`
174
- );
175
- }
176
- written =
177
- rawProcessedByte === undefined ? written + chunk.byteLength : processedByte - startOffset;
178
- chunks += 1;
179
- const elapsedMs = Date.now() - startTime;
180
- const transferredBytes = Math.min(written, dataLength);
181
- options.onProgress?.({
182
- progress: getConfirmedProgress(startOffset + written, totalSize, written, dataLength),
183
- transferredBytes,
344
+ } catch (error) {
345
+ const now = Date.now();
346
+ const elapsedMs = Math.max(now - startTime, 0);
347
+ const logWindowElapsedMs = Math.max(now - logWindowStartedAt, 0);
348
+ const logWindowBytes = Math.max(written - logWindowStartedBytes, 0);
349
+ logFileTransferMetrics({
350
+ transport,
351
+ status: 'failed',
352
+ path: options.path,
353
+ transferredBytes: written,
184
354
  totalBytes: dataLength,
185
- rateBytesPerSecond:
186
- elapsedMs > 0 ? Math.round((transferredBytes / elapsedMs) * 1000) : undefined,
187
355
  elapsedMs,
356
+ rateBytesPerSecond: getAverageFileTransferRate(logWindowBytes, logWindowElapsedMs),
357
+ hostWriteElapsedMs,
358
+ responseWaitElapsedMs,
359
+ measuredAttempts,
188
360
  });
361
+ throw error;
189
362
  }
190
363
 
364
+ const elapsedMs = Math.max(lastConfirmedAt - startTime, 0);
365
+ const logWindowElapsedMs = Math.max(lastConfirmedAt - logWindowStartedAt, 0);
366
+ const logWindowBytes = Math.max(written - logWindowStartedBytes, 0);
367
+ logFileTransferMetrics({
368
+ transport,
369
+ status: 'completed',
370
+ path: options.path,
371
+ transferredBytes: written,
372
+ totalBytes: dataLength,
373
+ elapsedMs,
374
+ rateBytesPerSecond: getAverageFileTransferRate(logWindowBytes, logWindowElapsedMs),
375
+ hostWriteElapsedMs,
376
+ responseWaitElapsedMs,
377
+ measuredAttempts,
378
+ });
379
+
191
380
  return {
192
381
  ...lastMessage,
193
382
  path: options.path,
package/src/api/index.ts CHANGED
@@ -3,6 +3,7 @@ export { default as testProtocolV2Ping } from './protocol-v2/Ping';
3
3
  export { default as preInitialize } from './device/PreInitialize';
4
4
 
5
5
  export { default as searchDevices } from './SearchDevices';
6
+ export { default as detectDeviceConnectProtocol } from './DetectDeviceConnectProtocol';
6
7
  export { default as getFeatures } from './GetFeatures';
7
8
  export { default as getDeviceState } from './GetDeviceState';
8
9
  export { default as getOnekeyFeatures } from './GetOnekeyFeatures';
@@ -49,6 +50,7 @@ export { default as promptWebDeviceAccess } from './PromptWebDeviceAccess';
49
50
  export { default as deviceReboot } from './protocol-v2/DeviceReboot';
50
51
  export { default as deviceGetOnboardingStatus } from './protocol-v2/DeviceGetOnboardingStatus';
51
52
  export { default as deviceUploadWallpaper } from './protocol-v2/DeviceUploadWallpaper';
53
+ export { default as deviceUploadNft } from './protocol-v2/DeviceUploadNft';
52
54
  export { default as uploadPortfolio } from './UploadPortfolio';
53
55
 
54
56
  export { default as cipherKeyValue } from './CipherKeyValue';
@@ -67,7 +67,7 @@ export default class KaspaGetAddress extends BaseMethod<HardwareKaspaGetAddress[
67
67
  model_classic1s: {
68
68
  min: '3.12.0',
69
69
  },
70
- pro2: {
70
+ model_pro2: {
71
71
  min: '0.0.0',
72
72
  },
73
73
  };
@@ -190,7 +190,7 @@ export default class KaspaSignTransaction extends BaseMethod<KaspaSignTransactio
190
190
  model_classic1s: {
191
191
  min: '3.12.0',
192
192
  },
193
- pro2: {
193
+ model_pro2: {
194
194
  min: '0.0.0',
195
195
  },
196
196
  };
@@ -506,18 +506,7 @@ export default class KaspaSignTransaction extends BaseMethod<KaspaSignTransactio
506
506
  'KaspaSignTransaction: device firmware uses the streaming protocol; every output requires address or addressN'
507
507
  );
508
508
  }
509
- try {
510
- return await this.signTxStream(typedCall, response);
511
- } catch (error) {
512
- // Device rejected our refTxs; the caller decides whether to sign without them.
513
- if (
514
- error instanceof HardwareError &&
515
- String(error.message).toLowerCase().includes('previous transaction id mismatch')
516
- ) {
517
- throw ERRORS.TypedError(HardwareErrorCode.KaspaPrevTxIdMismatch, String(error.message));
518
- }
519
- throw error;
520
- }
509
+ return this.signTxStream(typedCall, response);
521
510
  }
522
511
 
523
512
  // Legacy answer to a streaming-only packet: no prehash material exists.
@@ -23,7 +23,7 @@ const baseVersionRange = {
23
23
  model_touch: {
24
24
  min: '4.3.0',
25
25
  },
26
- pro2: {
26
+ model_pro2: {
27
27
  min: '0.0.0',
28
28
  },
29
29
  };
@@ -36,7 +36,7 @@ const specialVersionRange: Record<string, DeviceFirmwareRange> = {
36
36
  model_touch: {
37
37
  min: '4.7.0',
38
38
  },
39
- pro2: {
39
+ model_pro2: {
40
40
  min: '0.0.0',
41
41
  },
42
42
  },
@@ -47,7 +47,7 @@ const specialVersionRange: Record<string, DeviceFirmwareRange> = {
47
47
  model_touch: {
48
48
  min: '4.9.0',
49
49
  },
50
- pro2: {
50
+ model_pro2: {
51
51
  min: '0.0.0',
52
52
  },
53
53
  },
@@ -0,0 +1,197 @@
1
+ import { ERRORS, HardwareErrorCode, createDeviceNotSupportMethodError } from '@onekeyfe/hd-shared';
2
+
3
+ import { UI_REQUEST, createUiMessage } from '../../events/ui-request';
4
+ import { supportsProtocolV2Message } from '../../protocols/protocol-v2/features';
5
+ import {
6
+ PRO2_NFT_DEFAULT_CHUNK_SIZE,
7
+ PRO2_NFT_DEFAULT_PACE_MS,
8
+ PRO2_NFT_DEFAULT_TIMEOUT_MS,
9
+ PRO2_NFT_DIRECTORY,
10
+ PRO2_NFT_MAX_CHUNK_SIZE,
11
+ PRO2_NFT_MAX_ITEMS,
12
+ PRO2_NFT_MIN_CHUNK_SIZE,
13
+ type Pro2NftBundle,
14
+ type Pro2NftImage,
15
+ buildPro2NftBundle,
16
+ getCompletePro2NftBasenames,
17
+ } from '../../utils/pro2Nft';
18
+ import { BaseMethod } from '../BaseMethod';
19
+ import { invalidParameter } from '../helpers/filesystemValidation';
20
+ import { writeProtocolV2File } from '../helpers/protocolV2FileWrite';
21
+
22
+ export type DeviceUploadNftParams = {
23
+ image: Pro2NftImage;
24
+ thumbnail: Pro2NftImage;
25
+ title: string;
26
+ subtitle: string;
27
+ timestampMs?: number;
28
+ chunkSize?: number;
29
+ paceMs?: number;
30
+ timeoutMs?: number;
31
+ };
32
+
33
+ export type DeviceUploadNftResponse = {
34
+ basename: string;
35
+ imagePath: string;
36
+ thumbnailPath: string;
37
+ metadataPath: string;
38
+ totalSize: number;
39
+ nftUpdated: true;
40
+ message?: string;
41
+ };
42
+
43
+ const FILESYSTEM_PATH_INFO_QUERY_MESSAGE_TYPE = 60802;
44
+ const FILESYSTEM_FILE_WRITE_MESSAGE_TYPE = 60805;
45
+ const FILESYSTEM_DIR_LIST_MESSAGE_TYPE = 60808;
46
+ const NFT_UPDATE_MESSAGE_TYPE = 61500;
47
+
48
+ export default class DeviceUploadNft extends BaseMethod<DeviceUploadNftParams> {
49
+ private bundle?: Pro2NftBundle;
50
+
51
+ getSupportedProtocols() {
52
+ return ['V2'] as const;
53
+ }
54
+
55
+ init() {
56
+ const {
57
+ image,
58
+ thumbnail,
59
+ title,
60
+ subtitle,
61
+ timestampMs = Date.now(),
62
+ chunkSize = PRO2_NFT_DEFAULT_CHUNK_SIZE,
63
+ paceMs = PRO2_NFT_DEFAULT_PACE_MS,
64
+ timeoutMs = PRO2_NFT_DEFAULT_TIMEOUT_MS,
65
+ } = this.payload;
66
+ if (
67
+ !Number.isInteger(chunkSize) ||
68
+ chunkSize < PRO2_NFT_MIN_CHUNK_SIZE ||
69
+ chunkSize > PRO2_NFT_MAX_CHUNK_SIZE
70
+ ) {
71
+ throw invalidParameter(
72
+ `Parameter [chunkSize] must be an integer between ${PRO2_NFT_MIN_CHUNK_SIZE} and ${PRO2_NFT_MAX_CHUNK_SIZE}.`
73
+ );
74
+ }
75
+ if (!Number.isInteger(paceMs) || paceMs < 0) {
76
+ throw invalidParameter('Parameter [paceMs] must be a non-negative integer.');
77
+ }
78
+ if (!Number.isInteger(timeoutMs) || timeoutMs <= 0) {
79
+ throw invalidParameter('Parameter [timeoutMs] must be a positive integer.');
80
+ }
81
+
82
+ this.bundle = buildPro2NftBundle({ image, thumbnail, title, subtitle, timestampMs });
83
+ this.params = { image, thumbnail, title, subtitle, timestampMs, chunkSize, paceMs, timeoutMs };
84
+ this.unlockPolicy = 'none';
85
+ this.skipForceUpdateCheck = true;
86
+ this.useDevicePassphraseState = false;
87
+ }
88
+
89
+ private async assertCapabilities() {
90
+ const protocolInfo = await this.device.ensureProtocolV2RuntimeContext();
91
+ const hasFileWrite = supportsProtocolV2Message(
92
+ protocolInfo,
93
+ FILESYSTEM_FILE_WRITE_MESSAGE_TYPE
94
+ );
95
+ const hasPathInfo = supportsProtocolV2Message(
96
+ protocolInfo,
97
+ FILESYSTEM_PATH_INFO_QUERY_MESSAGE_TYPE
98
+ );
99
+ const hasDirList = supportsProtocolV2Message(protocolInfo, FILESYSTEM_DIR_LIST_MESSAGE_TYPE);
100
+ const hasNftUpdate = supportsProtocolV2Message(protocolInfo, NFT_UPDATE_MESSAGE_TYPE);
101
+ if (!hasFileWrite || !hasPathInfo || !hasDirList || !hasNftUpdate) {
102
+ throw createDeviceNotSupportMethodError(this.name, this.device.getCurrentFirmwareType());
103
+ }
104
+ }
105
+
106
+ private async assertStorageCapacity(basename: string) {
107
+ const { message: pathInfo } = await this.device.commands.typedCall(
108
+ 'FilesystemPathInfoQuery',
109
+ 'FilesystemPathInfo',
110
+ { path: PRO2_NFT_DIRECTORY },
111
+ { timeoutMs: this.params.timeoutMs }
112
+ );
113
+ if (!pathInfo.exist) return;
114
+
115
+ const { message } = await this.device.commands.typedCall(
116
+ 'FilesystemDirList',
117
+ 'FilesystemDir',
118
+ { path: PRO2_NFT_DIRECTORY, depth: 1 },
119
+ { timeoutMs: this.params.timeoutMs }
120
+ );
121
+ const existingBasenames = getCompletePro2NftBasenames(message.child_files);
122
+ if (existingBasenames.size >= PRO2_NFT_MAX_ITEMS && !existingBasenames.has(basename)) {
123
+ throw ERRORS.TypedError(HardwareErrorCode.NftStorageLimitReached, undefined, {
124
+ count: existingBasenames.size,
125
+ limit: PRO2_NFT_MAX_ITEMS,
126
+ });
127
+ }
128
+ }
129
+
130
+ private async updateNft(basename: string) {
131
+ return this.device.commands.typedCall(
132
+ 'NftUpdate',
133
+ 'Success',
134
+ { file_name_no_ext: basename },
135
+ { timeoutMs: this.params.timeoutMs }
136
+ );
137
+ }
138
+
139
+ async run(): Promise<DeviceUploadNftResponse> {
140
+ const { bundle } = this;
141
+ if (!bundle) throw invalidParameter('NFT data has not been initialized.');
142
+
143
+ await this.assertCapabilities();
144
+ this.throwIfAborted();
145
+ await this.assertStorageCapacity(bundle.basename);
146
+ this.throwIfAborted();
147
+
148
+ const files = [
149
+ { path: `${PRO2_NFT_DIRECTORY}/${bundle.basename}.bin`, data: bundle.image },
150
+ { path: `${PRO2_NFT_DIRECTORY}/${bundle.basename}_m.bin`, data: bundle.thumbnail },
151
+ { path: `${PRO2_NFT_DIRECTORY}/${bundle.basename}.json`, data: bundle.metadata },
152
+ ];
153
+ const totalSize = files.reduce((sum, file) => sum + file.data.byteLength, 0);
154
+ let transferredBeforeFile = 0;
155
+
156
+ for (const file of files) {
157
+ const transferredAtFileStart = transferredBeforeFile;
158
+ await writeProtocolV2File({
159
+ commands: this.device.commands,
160
+ path: file.path,
161
+ data: file.data,
162
+ totalSize: file.data.byteLength,
163
+ chunkSize: this.params.chunkSize,
164
+ timeoutMs: this.params.timeoutMs,
165
+ paceMs: this.params.paceMs,
166
+ overwrite: true,
167
+ append: false,
168
+ throwIfAborted: () => this.throwIfAborted(),
169
+ onProgress: progress => {
170
+ if (typeof this.postMessage !== 'function') return;
171
+ const transferredBytes = transferredAtFileStart + progress.transferredBytes;
172
+ this.postMessage(
173
+ createUiMessage(UI_REQUEST.DEVICE_PROGRESS, {
174
+ ...progress,
175
+ progress: Math.floor((transferredBytes / totalSize) * 100),
176
+ transferredBytes,
177
+ totalBytes: totalSize,
178
+ })
179
+ );
180
+ },
181
+ });
182
+ transferredBeforeFile += file.data.byteLength;
183
+ }
184
+
185
+ this.throwIfAborted();
186
+ const response = await this.updateNft(bundle.basename);
187
+ return {
188
+ basename: bundle.basename,
189
+ imagePath: files[0].path,
190
+ thumbnailPath: files[1].path,
191
+ metadataPath: files[2].path,
192
+ totalSize,
193
+ nftUpdated: true,
194
+ message: response.message?.message,
195
+ };
196
+ }
197
+ }
@@ -13,7 +13,9 @@ export default class ProtocolInfoRequest extends BaseMethod {
13
13
  }
14
14
 
15
15
  async run() {
16
- const res = await this.device.commands.typedCall('ProtocolInfoRequest', 'ProtocolInfo', {});
16
+ const res = await this.device.commands.typedCall('ProtocolInfoRequest', 'ProtocolInfo', {
17
+ eventless_wallet_session: true,
18
+ });
17
19
  return Promise.resolve(res.message);
18
20
  }
19
21
  }
@@ -118,6 +118,7 @@ export const isProtocolV2DeviceDisconnectedError = (error: unknown) => {
118
118
  message.includes('connection has timed out unexpectedly') ||
119
119
  message.includes('connection error has occured') ||
120
120
  message.includes('connection error has occurred') ||
121
+ message.includes('react native ble transport released') ||
121
122
  message.includes('multiplatformbleadapter') ||
122
123
  message.includes('multipalformebleadapter') ||
123
124
  compactMessage.includes('rxerrorerror6') ||
@@ -137,7 +138,6 @@ export function normalizeRebootType(value: RebootTypeInput | undefined): DeviceR
137
138
 
138
139
  // 与 firmware-pro2 的 proto_target_to_firmware_target 安装白名单保持一致。
139
140
  const INSTALLABLE_FIRMWARE_TARGET_IDS = new Set<number>([
140
- ProtocolV2FirmwareTargetType.FW_MGMT_TARGET_CRATE,
141
141
  ProtocolV2FirmwareTargetType.FW_MGMT_TARGET_BOOTLOADER,
142
142
  ProtocolV2FirmwareTargetType.FW_MGMT_TARGET_APPLICATION_P1,
143
143
  ProtocolV2FirmwareTargetType.FW_MGMT_TARGET_APPLICATION_P2,
@@ -34,7 +34,7 @@ export default class SolSignMessage extends BaseMethod<HardwareSolSignUnsafeMess
34
34
 
35
35
  getVersionRange() {
36
36
  return {
37
- pro2: {
37
+ model_pro2: {
38
38
  min: '0.0.0',
39
39
  },
40
40
  pro: {