@onekeyfe/hd-core 1.1.27-alpha.4 → 1.1.27-alpha.41

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 (359) hide show
  1. package/__tests__/evmSignTransaction.test.ts +1 -1
  2. package/__tests__/evmSignTypedData.test.ts +1 -1
  3. package/__tests__/protocol-v2.test.ts +1688 -0
  4. package/dist/api/BaseMethod.d.ts +1 -7
  5. package/dist/api/BaseMethod.d.ts.map +1 -1
  6. package/dist/api/DirList.d.ts +10 -0
  7. package/dist/api/DirList.d.ts.map +1 -0
  8. package/dist/api/DirMake.d.ts +9 -0
  9. package/dist/api/DirMake.d.ts.map +1 -0
  10. package/dist/api/DirRemove.d.ts +9 -0
  11. package/dist/api/DirRemove.d.ts.map +1 -0
  12. package/dist/api/FileDelete.d.ts +9 -0
  13. package/dist/api/FileDelete.d.ts.map +1 -0
  14. package/dist/api/FileRead.d.ts +19 -0
  15. package/dist/api/FileRead.d.ts.map +1 -0
  16. package/dist/api/FileWrite.d.ts +23 -0
  17. package/dist/api/FileWrite.d.ts.map +1 -0
  18. package/dist/api/FirmwareUpdateV3.d.ts +1 -0
  19. package/dist/api/FirmwareUpdateV3.d.ts.map +1 -1
  20. package/dist/api/FirmwareUpdateV4.d.ts +32 -0
  21. package/dist/api/FirmwareUpdateV4.d.ts.map +1 -0
  22. package/dist/api/GetDeviceInfo.d.ts +9 -0
  23. package/dist/api/GetDeviceInfo.d.ts.map +1 -0
  24. package/dist/api/GetFeatures.d.ts +1 -1
  25. package/dist/api/GetOnekeyFeatures.d.ts.map +1 -1
  26. package/dist/api/GetPassphraseState.d.ts +6 -1
  27. package/dist/api/GetPassphraseState.d.ts.map +1 -1
  28. package/dist/api/PathInfo.d.ts +9 -0
  29. package/dist/api/PathInfo.d.ts.map +1 -0
  30. package/dist/api/SearchDevices.d.ts +2 -1
  31. package/dist/api/SearchDevices.d.ts.map +1 -1
  32. package/dist/api/alephium/AlephiumGetAddress.d.ts +2 -6
  33. package/dist/api/alephium/AlephiumGetAddress.d.ts.map +1 -1
  34. package/dist/api/alephium/AlephiumSignMessage.d.ts +2 -5
  35. package/dist/api/alephium/AlephiumSignMessage.d.ts.map +1 -1
  36. package/dist/api/alephium/AlephiumSignTransaction.d.ts +2 -5
  37. package/dist/api/alephium/AlephiumSignTransaction.d.ts.map +1 -1
  38. package/dist/api/algo/AlgoSignTransaction.d.ts.map +1 -1
  39. package/dist/api/allnetwork/AllNetworkGetAddressBase.d.ts.map +1 -1
  40. package/dist/api/aptos/AptosSignInMessage.d.ts.map +1 -1
  41. package/dist/api/aptos/AptosSignMessage.d.ts.map +1 -1
  42. package/dist/api/aptos/AptosSignTransaction.d.ts.map +1 -1
  43. package/dist/api/benfen/BenfenGetAddress.d.ts +2 -9
  44. package/dist/api/benfen/BenfenGetAddress.d.ts.map +1 -1
  45. package/dist/api/benfen/BenfenGetPublicKey.d.ts +2 -9
  46. package/dist/api/benfen/BenfenGetPublicKey.d.ts.map +1 -1
  47. package/dist/api/benfen/BenfenSignMessage.d.ts +2 -8
  48. package/dist/api/benfen/BenfenSignMessage.d.ts.map +1 -1
  49. package/dist/api/benfen/BenfenSignTransaction.d.ts +2 -8
  50. package/dist/api/benfen/BenfenSignTransaction.d.ts.map +1 -1
  51. package/dist/api/btc/BTCGetAddress.d.ts +1 -11
  52. package/dist/api/btc/BTCGetAddress.d.ts.map +1 -1
  53. package/dist/api/btc/BTCGetPublicKey.d.ts +1 -11
  54. package/dist/api/btc/BTCGetPublicKey.d.ts.map +1 -1
  55. package/dist/api/btc/BTCSignMessage.d.ts +1 -15
  56. package/dist/api/btc/BTCSignMessage.d.ts.map +1 -1
  57. package/dist/api/btc/BTCSignPsbt.d.ts.map +1 -1
  58. package/dist/api/btc/BTCSignTransaction.d.ts +1 -11
  59. package/dist/api/btc/BTCSignTransaction.d.ts.map +1 -1
  60. package/dist/api/btc/BTCVerifyMessage.d.ts +1 -11
  61. package/dist/api/btc/BTCVerifyMessage.d.ts.map +1 -1
  62. package/dist/api/btc/helpers/versionLimit.d.ts +2 -11
  63. package/dist/api/btc/helpers/versionLimit.d.ts.map +1 -1
  64. package/dist/api/cardano/CardanoSignMessage.d.ts.map +1 -1
  65. package/dist/api/cardano/CardanoSignTransaction.d.ts.map +1 -1
  66. package/dist/api/conflux/ConfluxSignMessage.d.ts.map +1 -1
  67. package/dist/api/conflux/ConfluxSignMessageCIP23.d.ts.map +1 -1
  68. package/dist/api/conflux/ConfluxSignTransaction.d.ts.map +1 -1
  69. package/dist/api/cosmos/CosmosSignTransaction.d.ts.map +1 -1
  70. package/dist/api/device/DeviceRebootToBoardloader.d.ts +1 -1
  71. package/dist/api/device/DeviceRebootToBoardloader.d.ts.map +1 -1
  72. package/dist/api/device/DeviceRebootToBootloader.d.ts.map +1 -1
  73. package/dist/api/dynex/DnxGetAddress.d.ts.map +1 -1
  74. package/dist/api/dynex/DnxSignTransaction.d.ts.map +1 -1
  75. package/dist/api/evm/EVMSignMessage.d.ts.map +1 -1
  76. package/dist/api/evm/EVMSignMessageEIP712.d.ts +2 -8
  77. package/dist/api/evm/EVMSignMessageEIP712.d.ts.map +1 -1
  78. package/dist/api/evm/EVMSignTransaction.d.ts.map +1 -1
  79. package/dist/api/evm/EVMSignTypedData.d.ts.map +1 -1
  80. package/dist/api/evm/latest/signTypedData.d.ts +1 -1
  81. package/dist/api/evm/latest/signTypedData.d.ts.map +1 -1
  82. package/dist/api/evm/legacyV1/signTypedData.d.ts +1 -1
  83. package/dist/api/evm/legacyV1/signTypedData.d.ts.map +1 -1
  84. package/dist/api/filecoin/FilecoinSignTransaction.d.ts.map +1 -1
  85. package/dist/api/firmware/FirmwareUpdateBaseMethod.d.ts +10 -2
  86. package/dist/api/firmware/FirmwareUpdateBaseMethod.d.ts.map +1 -1
  87. package/dist/api/helpers/deviceInfo.d.ts +15 -0
  88. package/dist/api/helpers/deviceInfo.d.ts.map +1 -0
  89. package/dist/api/helpers/filesystemValidation.d.ts +7 -0
  90. package/dist/api/helpers/filesystemValidation.d.ts.map +1 -0
  91. package/dist/api/index.d.ts +28 -1
  92. package/dist/api/index.d.ts.map +1 -1
  93. package/dist/api/kaspa/KaspaSignTransaction.d.ts.map +1 -1
  94. package/dist/api/near/NearSignTransaction.d.ts.map +1 -1
  95. package/dist/api/nem/NEMSignTransaction.d.ts.map +1 -1
  96. package/dist/api/neo/NeoGetAddress.d.ts +2 -8
  97. package/dist/api/neo/NeoGetAddress.d.ts.map +1 -1
  98. package/dist/api/neo/NeoSignTransaction.d.ts +2 -8
  99. package/dist/api/neo/NeoSignTransaction.d.ts.map +1 -1
  100. package/dist/api/nervos/NervosGetAddress.d.ts +2 -9
  101. package/dist/api/nervos/NervosGetAddress.d.ts.map +1 -1
  102. package/dist/api/nervos/NervosSignTransaction.d.ts +2 -9
  103. package/dist/api/nervos/NervosSignTransaction.d.ts.map +1 -1
  104. package/dist/api/nexa/NexaGetAddress.d.ts +2 -8
  105. package/dist/api/nexa/NexaGetAddress.d.ts.map +1 -1
  106. package/dist/api/nexa/NexaSignTransaction.d.ts +2 -9
  107. package/dist/api/nexa/NexaSignTransaction.d.ts.map +1 -1
  108. package/dist/api/nostr/NostrSignEvent.d.ts.map +1 -1
  109. package/dist/api/nostr/NostrSignSchnorr.d.ts.map +1 -1
  110. package/dist/api/polkadot/PolkadotSignTransaction.d.ts.map +1 -1
  111. package/dist/api/protocol-v2/DeviceFirmwareUpdate.d.ts +7 -0
  112. package/dist/api/protocol-v2/DeviceFirmwareUpdate.d.ts.map +1 -0
  113. package/dist/api/protocol-v2/DeviceGetDeviceInfo.d.ts +7 -0
  114. package/dist/api/protocol-v2/DeviceGetDeviceInfo.d.ts.map +1 -0
  115. package/dist/api/protocol-v2/DeviceGetFirmwareUpdateStatus.d.ts +6 -0
  116. package/dist/api/protocol-v2/DeviceGetFirmwareUpdateStatus.d.ts.map +1 -0
  117. package/dist/api/protocol-v2/DeviceGetOnboardingStatus.d.ts +6 -0
  118. package/dist/api/protocol-v2/DeviceGetOnboardingStatus.d.ts.map +1 -0
  119. package/dist/api/protocol-v2/DeviceReboot.d.ts +7 -0
  120. package/dist/api/protocol-v2/DeviceReboot.d.ts.map +1 -0
  121. package/dist/api/protocol-v2/FactoryDeviceInfoSettings.d.ts +7 -0
  122. package/dist/api/protocol-v2/FactoryDeviceInfoSettings.d.ts.map +1 -0
  123. package/dist/api/protocol-v2/FactoryGetDeviceInfo.d.ts +6 -0
  124. package/dist/api/protocol-v2/FactoryGetDeviceInfo.d.ts.map +1 -0
  125. package/dist/api/protocol-v2/FilesystemDiskControl.d.ts +10 -0
  126. package/dist/api/protocol-v2/FilesystemDiskControl.d.ts.map +1 -0
  127. package/dist/api/protocol-v2/FilesystemFixPermission.d.ts +6 -0
  128. package/dist/api/protocol-v2/FilesystemFixPermission.d.ts.map +1 -0
  129. package/dist/api/protocol-v2/FilesystemFormat.d.ts +6 -0
  130. package/dist/api/protocol-v2/FilesystemFormat.d.ts.map +1 -0
  131. package/dist/api/protocol-v2/GetProtoVersion.d.ts +6 -0
  132. package/dist/api/protocol-v2/GetProtoVersion.d.ts.map +1 -0
  133. package/dist/api/protocol-v2/Ping.d.ts +8 -0
  134. package/dist/api/protocol-v2/Ping.d.ts.map +1 -0
  135. package/dist/api/protocol-v2/helpers.d.ts +49 -0
  136. package/dist/api/protocol-v2/helpers.d.ts.map +1 -0
  137. package/dist/api/scdo/ScdoGetAddress.d.ts +2 -6
  138. package/dist/api/scdo/ScdoGetAddress.d.ts.map +1 -1
  139. package/dist/api/scdo/ScdoSignMessage.d.ts +2 -5
  140. package/dist/api/scdo/ScdoSignMessage.d.ts.map +1 -1
  141. package/dist/api/scdo/ScdoSignTransaction.d.ts +2 -5
  142. package/dist/api/scdo/ScdoSignTransaction.d.ts.map +1 -1
  143. package/dist/api/solana/SolGetAddress.d.ts +1 -0
  144. package/dist/api/solana/SolGetAddress.d.ts.map +1 -1
  145. package/dist/api/solana/SolSignMessage.d.ts +4 -0
  146. package/dist/api/solana/SolSignMessage.d.ts.map +1 -1
  147. package/dist/api/solana/SolSignOffchainMessage.d.ts +4 -0
  148. package/dist/api/solana/SolSignOffchainMessage.d.ts.map +1 -1
  149. package/dist/api/solana/SolSignTransaction.d.ts +8 -0
  150. package/dist/api/solana/SolSignTransaction.d.ts.map +1 -1
  151. package/dist/api/starcoin/StarcoinSignMessage.d.ts.map +1 -1
  152. package/dist/api/starcoin/StarcoinSignTransaction.d.ts.map +1 -1
  153. package/dist/api/stellar/StellarGetAddress.d.ts +2 -1
  154. package/dist/api/stellar/StellarGetAddress.d.ts.map +1 -1
  155. package/dist/api/stellar/StellarSignTransaction.d.ts +2 -1
  156. package/dist/api/stellar/StellarSignTransaction.d.ts.map +1 -1
  157. package/dist/api/sui/SuiSignMessage.d.ts.map +1 -1
  158. package/dist/api/sui/SuiSignTransaction.d.ts +2 -2
  159. package/dist/api/sui/SuiSignTransaction.d.ts.map +1 -1
  160. package/dist/api/ton/TonSignData.d.ts.map +1 -1
  161. package/dist/api/ton/TonSignMessage.d.ts.map +1 -1
  162. package/dist/api/ton/TonSignProof.d.ts.map +1 -1
  163. package/dist/api/tron/TronSignMessage.d.ts +4 -0
  164. package/dist/api/tron/TronSignMessage.d.ts.map +1 -1
  165. package/dist/api/tron/TronSignTransaction.d.ts +4 -0
  166. package/dist/api/tron/TronSignTransaction.d.ts.map +1 -1
  167. package/dist/api/xrp/XrpSignTransaction.d.ts.map +1 -1
  168. package/dist/core/RequestQueue.d.ts +1 -1
  169. package/dist/core/RequestQueue.d.ts.map +1 -1
  170. package/dist/core/index.d.ts.map +1 -1
  171. package/dist/data-manager/DataManager.d.ts +7 -4
  172. package/dist/data-manager/DataManager.d.ts.map +1 -1
  173. package/dist/data-manager/MessagesConfig.d.ts +2 -2
  174. package/dist/data-manager/MessagesConfig.d.ts.map +1 -1
  175. package/dist/data-manager/TransportManager.d.ts +5 -4
  176. package/dist/data-manager/TransportManager.d.ts.map +1 -1
  177. package/dist/device/Device.d.ts +5 -18
  178. package/dist/device/Device.d.ts.map +1 -1
  179. package/dist/device/DeviceCommands.d.ts +8 -8
  180. package/dist/device/DeviceCommands.d.ts.map +1 -1
  181. package/dist/device/DeviceConnector.d.ts +2 -1
  182. package/dist/device/DeviceConnector.d.ts.map +1 -1
  183. package/dist/events/ui-request.d.ts +8 -0
  184. package/dist/events/ui-request.d.ts.map +1 -1
  185. package/dist/index.d.ts +292 -43
  186. package/dist/index.js +16693 -1501
  187. package/dist/inject.d.ts.map +1 -1
  188. package/dist/protocols/protocol-v2/features.d.ts +104 -0
  189. package/dist/protocols/protocol-v2/features.d.ts.map +1 -0
  190. package/dist/protocols/protocol-v2/firmware.d.ts +12 -0
  191. package/dist/protocols/protocol-v2/firmware.d.ts.map +1 -0
  192. package/dist/protocols/protocol-v2/index.d.ts +3 -0
  193. package/dist/protocols/protocol-v2/index.d.ts.map +1 -0
  194. package/dist/types/api/export.d.ts +1 -1
  195. package/dist/types/api/export.d.ts.map +1 -1
  196. package/dist/types/api/firmwareUpdate.d.ts +7 -0
  197. package/dist/types/api/firmwareUpdate.d.ts.map +1 -1
  198. package/dist/types/api/getDeviceInfo.d.ts +84 -0
  199. package/dist/types/api/getDeviceInfo.d.ts.map +1 -0
  200. package/dist/types/api/getPassphraseState.d.ts +10 -1
  201. package/dist/types/api/getPassphraseState.d.ts.map +1 -1
  202. package/dist/types/api/index.d.ts +33 -3
  203. package/dist/types/api/index.d.ts.map +1 -1
  204. package/dist/types/api/protocolV2.d.ts +127 -0
  205. package/dist/types/api/protocolV2.d.ts.map +1 -0
  206. package/dist/types/api/searchDevices.d.ts +2 -2
  207. package/dist/types/api/searchDevices.d.ts.map +1 -1
  208. package/dist/types/device.d.ts +6 -2
  209. package/dist/types/device.d.ts.map +1 -1
  210. package/dist/types/params.d.ts +2 -1
  211. package/dist/types/params.d.ts.map +1 -1
  212. package/dist/types/settings.d.ts +1 -1
  213. package/dist/types/settings.d.ts.map +1 -1
  214. package/dist/utils/deviceFeaturesUtils.d.ts +5 -3
  215. package/dist/utils/deviceFeaturesUtils.d.ts.map +1 -1
  216. package/dist/utils/deviceInfoUtils.d.ts +1 -0
  217. package/dist/utils/deviceInfoUtils.d.ts.map +1 -1
  218. package/dist/utils/index.d.ts +1 -1
  219. package/dist/utils/index.d.ts.map +1 -1
  220. package/dist/utils/patch.d.ts +1 -1
  221. package/dist/utils/patch.d.ts.map +1 -1
  222. package/dist/utils/versionUtils.d.ts +1 -1
  223. package/package.json +4 -4
  224. package/src/api/BaseMethod.ts +7 -82
  225. package/src/api/DirList.ts +29 -0
  226. package/src/api/DirMake.ts +21 -0
  227. package/src/api/DirRemove.ts +21 -0
  228. package/src/api/FileDelete.ts +21 -0
  229. package/src/api/FileRead.ts +165 -0
  230. package/src/api/FileWrite.ts +203 -0
  231. package/src/api/FirmwareUpdateV3.ts +21 -4
  232. package/src/api/FirmwareUpdateV4.ts +810 -0
  233. package/src/api/GetDeviceInfo.ts +161 -0
  234. package/src/api/GetOnekeyFeatures.ts +75 -3
  235. package/src/api/GetPassphraseState.ts +16 -7
  236. package/src/api/PathInfo.ts +25 -0
  237. package/src/api/SearchDevices.ts +7 -2
  238. package/src/api/alephium/AlephiumGetAddress.ts +6 -2
  239. package/src/api/alephium/AlephiumSignMessage.ts +6 -2
  240. package/src/api/alephium/AlephiumSignTransaction.ts +6 -3
  241. package/src/api/algo/AlgoSignTransaction.ts +0 -1
  242. package/src/api/allnetwork/AllNetworkGetAddressBase.ts +18 -9
  243. package/src/api/aptos/AptosSignInMessage.ts +0 -1
  244. package/src/api/aptos/AptosSignMessage.ts +0 -1
  245. package/src/api/aptos/AptosSignTransaction.ts +0 -1
  246. package/src/api/benfen/BenfenGetAddress.ts +6 -2
  247. package/src/api/benfen/BenfenGetPublicKey.ts +6 -2
  248. package/src/api/benfen/BenfenSignMessage.ts +6 -2
  249. package/src/api/benfen/BenfenSignTransaction.ts +6 -2
  250. package/src/api/btc/BTCSignMessage.ts +0 -1
  251. package/src/api/btc/BTCSignPsbt.ts +0 -1
  252. package/src/api/btc/BTCSignTransaction.ts +0 -1
  253. package/src/api/btc/helpers/versionLimit.ts +7 -1
  254. package/src/api/cardano/CardanoSignMessage.ts +0 -1
  255. package/src/api/cardano/CardanoSignTransaction.ts +0 -1
  256. package/src/api/conflux/ConfluxSignMessage.ts +0 -1
  257. package/src/api/conflux/ConfluxSignMessageCIP23.ts +0 -1
  258. package/src/api/conflux/ConfluxSignTransaction.ts +5 -3
  259. package/src/api/cosmos/CosmosSignTransaction.ts +0 -1
  260. package/src/api/device/DeviceRebootToBoardloader.ts +10 -1
  261. package/src/api/device/DeviceRebootToBootloader.ts +10 -1
  262. package/src/api/dynex/DnxGetAddress.ts +7 -0
  263. package/src/api/dynex/DnxSignTransaction.ts +7 -1
  264. package/src/api/evm/EVMGetAddress.ts +1 -1
  265. package/src/api/evm/EVMGetPublicKey.ts +1 -1
  266. package/src/api/evm/EVMSignMessage.ts +1 -3
  267. package/src/api/evm/EVMSignMessageEIP712.ts +14 -2
  268. package/src/api/evm/EVMSignTransaction.ts +1 -3
  269. package/src/api/evm/EVMSignTypedData.ts +6 -8
  270. package/src/api/evm/EVMVerifyMessage.ts +1 -1
  271. package/src/api/filecoin/FilecoinSignTransaction.ts +0 -1
  272. package/src/api/firmware/FirmwareUpdateBaseMethod.ts +27 -4
  273. package/src/api/helpers/deviceInfo.ts +205 -0
  274. package/src/api/helpers/filesystemValidation.ts +51 -0
  275. package/src/api/index.ts +30 -1
  276. package/src/api/kaspa/KaspaSignTransaction.ts +0 -1
  277. package/src/api/near/NearSignTransaction.ts +0 -1
  278. package/src/api/nem/NEMSignTransaction.ts +0 -1
  279. package/src/api/neo/NeoGetAddress.ts +6 -1
  280. package/src/api/neo/NeoSignTransaction.ts +6 -2
  281. package/src/api/nervos/NervosGetAddress.ts +6 -2
  282. package/src/api/nervos/NervosSignTransaction.ts +6 -3
  283. package/src/api/nexa/NexaGetAddress.ts +6 -2
  284. package/src/api/nexa/NexaSignTransaction.ts +6 -4
  285. package/src/api/nostr/NostrSignEvent.ts +0 -1
  286. package/src/api/nostr/NostrSignSchnorr.ts +0 -1
  287. package/src/api/polkadot/PolkadotSignTransaction.ts +0 -1
  288. package/src/api/protocol-v2/DeviceFirmwareUpdate.ts +50 -0
  289. package/src/api/protocol-v2/DeviceGetDeviceInfo.ts +35 -0
  290. package/src/api/protocol-v2/DeviceGetFirmwareUpdateStatus.ts +18 -0
  291. package/src/api/protocol-v2/DeviceGetOnboardingStatus.ts +18 -0
  292. package/src/api/protocol-v2/DeviceReboot.ts +22 -0
  293. package/src/api/protocol-v2/FactoryDeviceInfoSettings.ts +27 -0
  294. package/src/api/protocol-v2/FactoryGetDeviceInfo.ts +18 -0
  295. package/src/api/protocol-v2/FilesystemDiskControl.ts +34 -0
  296. package/src/api/protocol-v2/FilesystemFixPermission.ts +14 -0
  297. package/src/api/protocol-v2/FilesystemFormat.ts +14 -0
  298. package/src/api/protocol-v2/GetProtoVersion.ts +14 -0
  299. package/src/api/protocol-v2/Ping.ts +16 -0
  300. package/src/api/protocol-v2/helpers.ts +161 -0
  301. package/src/api/scdo/ScdoGetAddress.ts +6 -2
  302. package/src/api/scdo/ScdoSignMessage.ts +6 -2
  303. package/src/api/scdo/ScdoSignTransaction.ts +6 -3
  304. package/src/api/solana/SolGetAddress.ts +4 -0
  305. package/src/api/solana/SolSignMessage.ts +4 -1
  306. package/src/api/solana/SolSignOffchainMessage.ts +4 -1
  307. package/src/api/solana/SolSignTransaction.ts +8 -1
  308. package/src/api/starcoin/StarcoinSignMessage.ts +0 -1
  309. package/src/api/starcoin/StarcoinSignTransaction.ts +0 -1
  310. package/src/api/stellar/StellarGetAddress.ts +10 -1
  311. package/src/api/stellar/StellarSignTransaction.ts +14 -2
  312. package/src/api/sui/SuiSignMessage.ts +0 -1
  313. package/src/api/sui/SuiSignTransaction.ts +12 -10
  314. package/src/api/ton/TonSignData.ts +0 -1
  315. package/src/api/ton/TonSignMessage.ts +0 -1
  316. package/src/api/ton/TonSignProof.ts +0 -1
  317. package/src/api/tron/TronSignMessage.ts +5 -2
  318. package/src/api/tron/TronSignTransaction.ts +4 -1
  319. package/src/api/xrp/XrpSignTransaction.ts +1 -2
  320. package/src/core/RequestQueue.ts +3 -10
  321. package/src/core/index.ts +62 -153
  322. package/src/data/messages/messages-protocol-v2.json +13128 -0
  323. package/src/data-manager/DataManager.ts +12 -7
  324. package/src/data-manager/MessagesConfig.ts +14 -14
  325. package/src/data-manager/TransportManager.ts +38 -12
  326. package/src/device/Device.ts +98 -82
  327. package/src/device/DeviceCommands.ts +166 -26
  328. package/src/device/DeviceConnector.ts +29 -4
  329. package/src/device/DevicePool.ts +1 -1
  330. package/src/events/ui-request.ts +8 -0
  331. package/src/inject.ts +46 -2
  332. package/src/protocols/protocol-v2/features.ts +287 -0
  333. package/src/protocols/protocol-v2/firmware.ts +26 -0
  334. package/src/protocols/protocol-v2/index.ts +2 -0
  335. package/src/types/api/export.ts +1 -0
  336. package/src/types/api/firmwareUpdate.ts +12 -0
  337. package/src/types/api/getDeviceInfo.ts +97 -0
  338. package/src/types/api/getPassphraseState.ts +13 -2
  339. package/src/types/api/index.ts +80 -3
  340. package/src/types/api/protocolV2.ts +226 -0
  341. package/src/types/api/searchDevices.ts +2 -2
  342. package/src/types/device.ts +33 -2
  343. package/src/types/params.ts +4 -2
  344. package/src/types/settings.ts +1 -1
  345. package/src/utils/deviceFeaturesUtils.ts +62 -21
  346. package/src/utils/deviceInfoUtils.ts +15 -8
  347. package/src/utils/index.ts +1 -0
  348. package/__tests__/DeviceCommands.test.ts +0 -99
  349. package/__tests__/evmLedgerLegacySafety.test.ts +0 -261
  350. package/__tests__/preInitialize.test.ts +0 -22
  351. package/dist/api/device/PreInitialize.d.ts +0 -6
  352. package/dist/api/device/PreInitialize.d.ts.map +0 -1
  353. package/dist/core/PollingStateManager.d.ts +0 -8
  354. package/dist/core/PollingStateManager.d.ts.map +0 -1
  355. package/dist/types/api/preInitialize.d.ts +0 -3
  356. package/dist/types/api/preInitialize.d.ts.map +0 -1
  357. package/src/api/device/PreInitialize.ts +0 -41
  358. package/src/core/PollingStateManager.ts +0 -47
  359. package/src/types/api/preInitialize.ts +0 -3
@@ -0,0 +1,1688 @@
1
+ import JSZip from 'jszip';
2
+ import { HardwareErrorCode } from '@onekeyfe/hd-shared';
3
+ import { DeviceRebootType } from '@onekeyfe/hd-transport';
4
+
5
+ import ConfluxSignTransaction from '../src/api/conflux/ConfluxSignTransaction';
6
+ import DnxGetAddress from '../src/api/dynex/DnxGetAddress';
7
+ import DnxSignTransaction from '../src/api/dynex/DnxSignTransaction';
8
+ import DirList from '../src/api/DirList';
9
+ import FileRead from '../src/api/FileRead';
10
+ import FileWrite from '../src/api/FileWrite';
11
+ import DeviceFirmwareUpdate from '../src/api/protocol-v2/DeviceFirmwareUpdate';
12
+ import DeviceGetOnboardingStatus from '../src/api/protocol-v2/DeviceGetOnboardingStatus';
13
+ import EVMSignMessageEIP712 from '../src/api/evm/EVMSignMessageEIP712';
14
+ import FirmwareUpdateV3 from '../src/api/FirmwareUpdateV3';
15
+ import FirmwareUpdateV4 from '../src/api/FirmwareUpdateV4';
16
+ import GetDeviceInfo from '../src/api/GetDeviceInfo';
17
+ import GetPassphraseState from '../src/api/GetPassphraseState';
18
+ import GetOnekeyFeatures from '../src/api/GetOnekeyFeatures';
19
+ import { batchGetPublickeys } from '../src/api/helpers/batchGetPublickeys';
20
+ import SuiSignTransaction from '../src/api/sui/SuiSignTransaction';
21
+ import SolGetAddress from '../src/api/solana/SolGetAddress';
22
+ import TronGetAddress from '../src/api/tron/TronGetAddress';
23
+ import TronSignMessage from '../src/api/tron/TronSignMessage';
24
+ import XrpSignTransaction from '../src/api/xrp/XrpSignTransaction';
25
+ import StellarGetAddress from '../src/api/stellar/StellarGetAddress';
26
+ import BenfenSignMessage from '../src/api/benfen/BenfenSignMessage';
27
+ import { getBitcoinForkVersionRange } from '../src/api/btc/helpers/versionLimit';
28
+ import { DataManager } from '../src/data-manager';
29
+ import { Device } from '../src/device/Device';
30
+ import { UI_REQUEST } from '../src/events/ui-request';
31
+ import { getProtocolV2Features, normalizeProtocolV2Features } from '../src/protocols/protocol-v2';
32
+ import { getMethodVersionRange, isMethodVersionRangeUnsupported } from '../src/utils';
33
+ import {
34
+ getPassphraseState,
35
+ getPassphraseStateWithRefreshDeviceInfo,
36
+ } from '../src/utils/deviceFeaturesUtils';
37
+
38
+ import type { DeviceCommands } from '../src/device/DeviceCommands';
39
+ import type { Features } from '../src/types';
40
+
41
+ jest.mock('../src/data/config', () => ({
42
+ getSDKVersion: jest.fn(() => '1.0.0'),
43
+ DEFAULT_DOMAIN: 'https://jssdk.onekey.so/1.0.0/',
44
+ }));
45
+
46
+ const descriptor = {
47
+ id: 'ble-id',
48
+ path: 'usb-path',
49
+ };
50
+
51
+ describe('Protocol V2 feature adapter', () => {
52
+ test('normalizes Protocol V2 DeviceInfo into existing Features fields', () => {
53
+ const features = normalizeProtocolV2Features(descriptor as any, {
54
+ protocol_version: 1,
55
+ hw: {
56
+ serial_no: 'PR2SERIAL',
57
+ },
58
+ fw: {
59
+ board: {
60
+ version: '0.1.0',
61
+ hash: [1, 2, 255],
62
+ },
63
+ boot: {
64
+ version: '0.2.0',
65
+ build_id: 'boot-build',
66
+ hash: new Uint8Array([10, 11]),
67
+ },
68
+ app: {
69
+ version: '1.2.3',
70
+ build_id: 'app-build',
71
+ hash: 'abc123',
72
+ },
73
+ },
74
+ bt: {
75
+ app: {
76
+ version: '4.5.6',
77
+ build_id: 'bt-build',
78
+ hash: [12, 13],
79
+ },
80
+ adv_name: 'Pro2 BLE',
81
+ },
82
+ se1: {
83
+ app: {
84
+ version: '7.8.9',
85
+ build_id: 'se-build',
86
+ hash: [14, 15],
87
+ },
88
+ state: 85,
89
+ },
90
+ se2: {
91
+ app: {
92
+ version: '8.0.0',
93
+ },
94
+ state: 0,
95
+ },
96
+ status: {
97
+ label: 'My Pro2',
98
+ language: 'en-US',
99
+ bt_enable: true,
100
+ init_states: false,
101
+ backup_required: true,
102
+ passphrase_protection: true,
103
+ },
104
+ });
105
+
106
+ expect(features.device_id).toBe('PR2SERIAL');
107
+ expect(features.serial_no).toBe('PR2SERIAL');
108
+ expect(features.onekey_serial_no).toBe('PR2SERIAL');
109
+ expect(features.onekey_device_type).toBe('pro2');
110
+ expect(features.protocol_version).toBe(1);
111
+ expect(features.major_version).toBe(1);
112
+ expect(features.minor_version).toBe(2);
113
+ expect(features.patch_version).toBe(3);
114
+ expect(features.onekey_firmware_version).toBe('1.2.3');
115
+ expect(features.onekey_firmware_build_id).toBe('app-build');
116
+ expect(features.onekey_firmware_hash).toBe('abc123');
117
+ expect(features.bootloader_version).toBe('0.2.0');
118
+ expect(features.onekey_boot_build_id).toBe('boot-build');
119
+ expect(features.onekey_boot_hash).toBe('0a0b');
120
+ expect(features.onekey_board_hash).toBe('0102ff');
121
+ expect(features.ble_name).toBe('Pro2 BLE');
122
+ expect(features.onekey_ble_version).toBe('4.5.6');
123
+ expect(features.onekey_ble_hash).toBe('0c0d');
124
+ expect(features.onekey_se01_version).toBe('7.8.9');
125
+ expect(features.onekey_se01_hash).toBe('0e0f');
126
+ expect(features.onekey_se01_state).toBe('APP');
127
+ expect(features.onekey_se02_state).toBe('BOOT');
128
+ expect(features.label).toBe('My Pro2');
129
+ expect(features.language).toBe('en-US');
130
+ expect(features.initialized).toBe(false);
131
+ expect(features.needs_backup).toBe(true);
132
+ expect(features.passphrase_protection).toBe(true);
133
+ expect(features.ble_enable).toBe(true);
134
+ });
135
+
136
+ test('uses GetPassphraseState payloads compatible with Pro1 passphrase flow', async () => {
137
+ const features = normalizeProtocolV2Features(descriptor as any);
138
+ const typedCall = jest.fn().mockResolvedValue({
139
+ type: 'PassphraseState',
140
+ message: {
141
+ passphrase_state: 'state-1',
142
+ session_id: 'session-1',
143
+ unlocked_attach_pin: false,
144
+ },
145
+ });
146
+ const commands = { typedCall } as unknown as DeviceCommands;
147
+
148
+ await getPassphraseState(features, commands, {
149
+ expectPassphraseState: 'state-1',
150
+ });
151
+ expect(typedCall).toHaveBeenLastCalledWith('GetPassphraseState', 'PassphraseState', {
152
+ passphrase_state: 'state-1',
153
+ });
154
+
155
+ await getPassphraseState(features, commands, {
156
+ expectPassphraseState: 'state-2',
157
+ allowCreateAttachPin: true,
158
+ });
159
+ expect(typedCall).toHaveBeenLastCalledWith('GetPassphraseState', 'PassphraseState', {
160
+ passphrase_state: 'state-2',
161
+ allow_create_attach_pin: true,
162
+ });
163
+
164
+ await getPassphraseState(features, commands, {
165
+ onlyMainPin: true,
166
+ });
167
+ expect(typedCall).toHaveBeenLastCalledWith('GetPassphraseState', 'PassphraseState', {
168
+ _only_main_pin: true,
169
+ });
170
+ });
171
+
172
+ test('returns unified GetPassphraseState object payload for existing Pro devices', async () => {
173
+ const features = {
174
+ device_id: 'pro-device-id',
175
+ onekey_device_type: 'PRO',
176
+ onekey_firmware_version: '4.15.0',
177
+ passphrase_protection: true,
178
+ session_id: 'feature-session',
179
+ unlocked_attach_pin: true,
180
+ };
181
+ const typedCall = jest.fn().mockResolvedValue({
182
+ type: 'PassphraseState',
183
+ message: {
184
+ passphrase_state: 'state-pro',
185
+ session_id: 'session-pro',
186
+ unlocked_attach_pin: false,
187
+ },
188
+ });
189
+ const updateInternalState = jest.fn();
190
+ const method = new GetPassphraseState({
191
+ payload: {
192
+ method: 'getPassphraseState',
193
+ connectId: 'connect-id',
194
+ },
195
+ });
196
+ method.device = {
197
+ features,
198
+ commands: { typedCall },
199
+ updateInternalState,
200
+ } as any;
201
+
202
+ await expect(method.run()).resolves.toEqual({
203
+ passphrase_state: 'state-pro',
204
+ session_id: 'session-pro',
205
+ unlocked_attach_pin: false,
206
+ passphrase_protection: true,
207
+ });
208
+ expect(updateInternalState).toHaveBeenCalledWith(
209
+ true,
210
+ 'state-pro',
211
+ 'pro-device-id',
212
+ 'session-pro',
213
+ 'feature-session'
214
+ );
215
+ });
216
+
217
+ test('stores Pro2 passphrase sessions without selecting them implicitly', async () => {
218
+ const device = Device.fromDescriptor({ ...descriptor, protocolType: 'V2' } as any);
219
+ const typedCall = jest.fn().mockResolvedValue({
220
+ type: 'PassphraseState',
221
+ message: {
222
+ passphrase_state: 'state-auto',
223
+ session_id: 'session-auto',
224
+ unlocked_attach_pin: false,
225
+ },
226
+ });
227
+
228
+ (device as any).features = normalizeProtocolV2Features({
229
+ ...descriptor,
230
+ protocolType: 'V2',
231
+ } as any);
232
+ (device as any).commands = { typedCall };
233
+
234
+ await expect(getPassphraseStateWithRefreshDeviceInfo(device)).resolves.toMatchObject({
235
+ passphraseState: 'state-auto',
236
+ newSession: 'session-auto',
237
+ });
238
+
239
+ expect(device.passphraseState).toBeUndefined();
240
+ expect(device.features?.passphrase_protection).toBe(true);
241
+ expect(device.features?.session_id).toBe('session-auto');
242
+ expect(device.getInternalState()).toBeUndefined();
243
+ device.passphraseState = 'state-auto';
244
+ expect(device.getInternalState()).toBe('session-auto');
245
+ expect(typedCall).toHaveBeenLastCalledWith('GetPassphraseState', 'PassphraseState', {
246
+ passphrase_state: undefined,
247
+ });
248
+ });
249
+
250
+ test('does not mark Pro2 passphrase enabled from a main PIN session alone', async () => {
251
+ const device = Device.fromDescriptor({ ...descriptor, protocolType: 'V2' } as any);
252
+ const typedCall = jest.fn().mockResolvedValue({
253
+ type: 'PassphraseState',
254
+ message: {
255
+ session_id: 'main-pin-session',
256
+ unlocked_attach_pin: false,
257
+ },
258
+ });
259
+
260
+ (device as any).features = normalizeProtocolV2Features(
261
+ {
262
+ ...descriptor,
263
+ protocolType: 'V2',
264
+ } as any,
265
+ {
266
+ status: {
267
+ passphrase_protection: false,
268
+ },
269
+ }
270
+ );
271
+ (device as any).commands = { typedCall };
272
+
273
+ await expect(
274
+ getPassphraseStateWithRefreshDeviceInfo(device, { onlyMainPin: true })
275
+ ).resolves.toMatchObject({
276
+ passphraseState: undefined,
277
+ newSession: 'main-pin-session',
278
+ });
279
+
280
+ expect(device.features?.passphrase_protection).toBe(false);
281
+ expect(device.features?.session_id).toBe('main-pin-session');
282
+ expect(device.getInternalState()).toBeUndefined();
283
+ expect(typedCall).toHaveBeenLastCalledWith('GetPassphraseState', 'PassphraseState', {
284
+ _only_main_pin: true,
285
+ });
286
+ });
287
+
288
+ test('does not let skipPassphraseCheck hide Pro2 passphrase state mismatch', async () => {
289
+ const device = Device.fromDescriptor({ ...descriptor, protocolType: 'V2' } as any);
290
+ const typedCall = jest.fn().mockResolvedValue({
291
+ type: 'PassphraseState',
292
+ message: {
293
+ passphrase_state: 'wrong-state',
294
+ session_id: 'wrong-session',
295
+ unlocked_attach_pin: false,
296
+ },
297
+ });
298
+
299
+ (device as any).features = normalizeProtocolV2Features({
300
+ ...descriptor,
301
+ protocolType: 'V2',
302
+ } as any);
303
+ (device as any).commands = { typedCall };
304
+
305
+ await expect(device.checkPassphraseStateSafety('expected-state', false, true)).resolves.toBe(
306
+ false
307
+ );
308
+
309
+ expect(device.getInternalState()).toBeUndefined();
310
+ expect(typedCall).toHaveBeenLastCalledWith('GetPassphraseState', 'PassphraseState', {
311
+ passphrase_state: 'expected-state',
312
+ });
313
+ });
314
+
315
+ test('marks fallback features as unavailable when DeviceInfo is missing', () => {
316
+ const features = normalizeProtocolV2Features(descriptor as any);
317
+
318
+ expect(features.device_id).toBe('usb-path');
319
+ expect(features.serial_no).toBe('usb-path');
320
+ expect(features.onekey_serial_no).toBe('usb-path');
321
+ expect(features.initialized).toBe(false);
322
+ expect(features.unlocked).toBe(false);
323
+ expect(features.firmware_present).toBe(false);
324
+ });
325
+
326
+ test('initializes Protocol V2 features from lightweight DeviceGetDeviceInfo', async () => {
327
+ const commands = {
328
+ typedCall: jest.fn().mockResolvedValueOnce({
329
+ type: 'DeviceInfo',
330
+ message: {
331
+ hw: { serial_no: 'PR2SERIAL' },
332
+ status: {
333
+ init_states: true,
334
+ passphrase_protection: true,
335
+ },
336
+ },
337
+ }),
338
+ };
339
+
340
+ const features = await getProtocolV2Features({
341
+ commands: commands as unknown as DeviceCommands,
342
+ descriptor: descriptor as any,
343
+ });
344
+
345
+ expect(features.device_id).toBe('PR2SERIAL');
346
+ expect(features.initialized).toBe(true);
347
+ expect(features.passphrase_protection).toBe(true);
348
+ expect(commands.typedCall).toHaveBeenCalledTimes(1);
349
+ expect(commands.typedCall).toHaveBeenNthCalledWith(1, 'DeviceGetDeviceInfo', 'DeviceInfo', {
350
+ targets: {
351
+ hw: true,
352
+ fw: true,
353
+ bt: true,
354
+ status: true,
355
+ },
356
+ types: {
357
+ version: true,
358
+ specific: true,
359
+ },
360
+ });
361
+ });
362
+
363
+ test('fails initialization when Protocol V2 DeviceGetDeviceInfo fails', async () => {
364
+ const commands = {
365
+ typedCall: jest.fn().mockRejectedValueOnce(new Error('DeviceInfo not supported')),
366
+ };
367
+
368
+ await expect(
369
+ getProtocolV2Features({
370
+ commands: commands as unknown as DeviceCommands,
371
+ descriptor: descriptor as any,
372
+ })
373
+ ).rejects.toThrow('DeviceInfo not supported');
374
+ });
375
+
376
+ test('refreshes Protocol V2 basic device info with the lightweight request', async () => {
377
+ const typedCall = jest.fn().mockResolvedValueOnce({
378
+ type: 'DeviceInfo',
379
+ message: {
380
+ hw: { serial_no: 'PR2SERIAL' },
381
+ status: { init_states: true },
382
+ },
383
+ });
384
+ const method = new GetDeviceInfo({
385
+ id: 1,
386
+ payload: {
387
+ method: 'getDeviceInfo',
388
+ scope: 'basic',
389
+ refresh: true,
390
+ },
391
+ });
392
+ method.init();
393
+ (method as any).device = {
394
+ originalDescriptor: { ...descriptor, protocolType: 'V2' },
395
+ features: normalizeProtocolV2Features({ ...descriptor, protocolType: 'V2' } as any),
396
+ commands: { typedCall },
397
+ _updateFeatures: jest.fn(),
398
+ };
399
+
400
+ await method.run();
401
+
402
+ expect(typedCall).toHaveBeenCalledWith('DeviceGetDeviceInfo', 'DeviceInfo', {
403
+ targets: {
404
+ hw: true,
405
+ fw: true,
406
+ bt: true,
407
+ status: true,
408
+ },
409
+ types: {
410
+ version: true,
411
+ specific: true,
412
+ },
413
+ });
414
+ });
415
+
416
+ test('reads full Protocol V2 device info only for verify scope', async () => {
417
+ const typedCall = jest.fn().mockResolvedValueOnce({
418
+ type: 'DeviceInfo',
419
+ message: {
420
+ hw: { serial_no: 'PR2SERIAL' },
421
+ status: { init_states: true },
422
+ },
423
+ });
424
+ const method = new GetDeviceInfo({
425
+ id: 1,
426
+ payload: {
427
+ method: 'getDeviceInfo',
428
+ scope: 'verify',
429
+ },
430
+ });
431
+ method.init();
432
+ (method as any).device = {
433
+ originalDescriptor: { ...descriptor, protocolType: 'V2' },
434
+ features: normalizeProtocolV2Features({ ...descriptor, protocolType: 'V2' } as any),
435
+ commands: { typedCall },
436
+ _updateFeatures: jest.fn(),
437
+ };
438
+
439
+ await method.run();
440
+
441
+ expect(typedCall).toHaveBeenCalledWith('DeviceGetDeviceInfo', 'DeviceInfo', {
442
+ targets: {
443
+ hw: true,
444
+ fw: true,
445
+ bt: true,
446
+ se1: true,
447
+ se2: true,
448
+ se3: true,
449
+ se4: true,
450
+ status: true,
451
+ },
452
+ types: {
453
+ version: true,
454
+ build_id: true,
455
+ hash: true,
456
+ specific: true,
457
+ },
458
+ });
459
+ });
460
+
461
+ test('does not inherit Pro or Pro model fallback ranges for Protocol V2 devices', () => {
462
+ const features = normalizeProtocolV2Features({ ...descriptor, protocolType: 'V2' } as any);
463
+ const checkedTypes: string[] = [];
464
+
465
+ const versionRange = getMethodVersionRange(features, type => {
466
+ checkedTypes.push(type);
467
+ if (type === 'pro' || type === 'model_touch') {
468
+ return { min: '4.10.0' };
469
+ }
470
+ return undefined;
471
+ });
472
+
473
+ expect(versionRange).toBeUndefined();
474
+ expect(checkedTypes).toEqual(['pro2']);
475
+ });
476
+
477
+ test('marks known unsupported public-chain methods as unsupported on Protocol V2', () => {
478
+ const features = normalizeProtocolV2Features({ ...descriptor, protocolType: 'V2' } as any, {
479
+ fw: {
480
+ app: {
481
+ version: '9.9.9',
482
+ },
483
+ },
484
+ });
485
+ const stellar = new StellarGetAddress({
486
+ id: 1,
487
+ payload: {
488
+ method: 'stellarGetAddress',
489
+ path: "m/44'/148'/0'",
490
+ },
491
+ });
492
+ const benfen = new BenfenSignMessage({
493
+ id: 1,
494
+ payload: {
495
+ method: 'benfenSignMessage',
496
+ path: "m/44'/728'/0'/0'/0'",
497
+ messageHex: '0x1234',
498
+ },
499
+ });
500
+
501
+ stellar.init();
502
+ benfen.init();
503
+
504
+ const stellarRange = getMethodVersionRange(features, type => stellar.getVersionRange()[type]);
505
+ const benfenRange = getMethodVersionRange(features, type => benfen.getVersionRange()[type]);
506
+ const neuraiRange = getMethodVersionRange(
507
+ features,
508
+ type => getBitcoinForkVersionRange(['Neurai'])[type]
509
+ );
510
+
511
+ expect(isMethodVersionRangeUnsupported(stellarRange)).toBe(true);
512
+ expect(isMethodVersionRangeUnsupported(benfenRange)).toBe(true);
513
+ expect(isMethodVersionRangeUnsupported(neuraiRange)).toBe(true);
514
+ });
515
+
516
+ test('does not block legacy batch public key support checks on Protocol V2', async () => {
517
+ const paths = [{ address_n: [0x8000002c, 0x80000000, 0x80000000] }] as any;
518
+ const typedCall = jest.fn().mockResolvedValue({
519
+ type: 'EcdsaPublicKeys',
520
+ message: {
521
+ root_fingerprint: 123,
522
+ public_keys: [],
523
+ hd_nodes: [{}],
524
+ },
525
+ });
526
+ const device = {
527
+ originalDescriptor: { protocolType: 'V2' },
528
+ features: normalizeProtocolV2Features({ ...descriptor, protocolType: 'V2' } as any, {
529
+ fw: {
530
+ app: {
531
+ version: '4.14.0',
532
+ },
533
+ },
534
+ }),
535
+ commands: { typedCall },
536
+ };
537
+
538
+ await expect(
539
+ batchGetPublickeys(device as any, paths, 'secp256k1', 0, { includeNode: true })
540
+ ).resolves.toMatchObject({
541
+ root_fingerprint: 123,
542
+ hd_nodes: [{}],
543
+ });
544
+ expect(typedCall).toHaveBeenCalledWith('BatchGetPublickeys', 'EcdsaPublicKeys', {
545
+ paths,
546
+ ecdsa_curve_name: 'secp256k1',
547
+ include_node: true,
548
+ });
549
+ });
550
+
551
+ test('returns Protocol V2 oneKey fields without calling legacy OnekeyGetFeatures', async () => {
552
+ const method = new GetOnekeyFeatures({
553
+ id: 1,
554
+ payload: {
555
+ method: 'getOnekeyFeatures',
556
+ },
557
+ });
558
+ const typedCall = jest.fn();
559
+
560
+ (method as any).device = {
561
+ originalDescriptor: { protocolType: 'V2' },
562
+ commands: { typedCall },
563
+ features: {
564
+ label: 'ignored label',
565
+ onekey_device_type: 'pro2',
566
+ onekey_firmware_version: '1.2.3',
567
+ onekey_firmware_build_id: 'app-build',
568
+ onekey_serial_no: 'PR2SERIAL',
569
+ onekey_ble_name: 'Pro2 BLE',
570
+ },
571
+ };
572
+
573
+ const message = await method.run();
574
+
575
+ expect(typedCall).not.toHaveBeenCalled();
576
+ expect(message).toMatchObject({
577
+ onekey_device_type: 'pro2',
578
+ onekey_firmware_version: '1.2.3',
579
+ onekey_firmware_build_id: 'app-build',
580
+ onekey_serial_no: 'PR2SERIAL',
581
+ onekey_ble_name: 'Pro2 BLE',
582
+ });
583
+ expect(message).not.toHaveProperty('label');
584
+ });
585
+
586
+ test('reuses cached Protocol V2 features after the first initialization', async () => {
587
+ const device = Device.fromDescriptor({
588
+ path: 'usb-path',
589
+ protocolType: 'V2',
590
+ } as any);
591
+ const typedCall = jest.fn().mockResolvedValueOnce({
592
+ type: 'DeviceInfo',
593
+ message: {
594
+ hw: { serial_no: 'PR2SERIAL' },
595
+ status: {
596
+ passphrase_protection: true,
597
+ },
598
+ },
599
+ });
600
+
601
+ (device as any).commands = { typedCall };
602
+
603
+ await device.initialize();
604
+ await device.initialize();
605
+
606
+ expect(device.features?.device_id).toBe('PR2SERIAL');
607
+ expect(device.features?.passphrase_protection).toBe(true);
608
+ expect(typedCall).toHaveBeenCalledTimes(1);
609
+ expect(typedCall).toHaveBeenNthCalledWith(
610
+ 1,
611
+ 'DeviceGetDeviceInfo',
612
+ 'DeviceInfo',
613
+ {
614
+ targets: {
615
+ hw: true,
616
+ fw: true,
617
+ bt: true,
618
+ status: true,
619
+ },
620
+ types: {
621
+ version: true,
622
+ specific: true,
623
+ },
624
+ },
625
+ {
626
+ timeoutMs: 10000,
627
+ }
628
+ );
629
+ });
630
+ });
631
+
632
+ describe('API compatibility handling', () => {
633
+ test('returns a typed unsupported error for deprecated EIP712 message signing on Protocol V2', async () => {
634
+ const method = new EVMSignMessageEIP712({
635
+ id: 1,
636
+ payload: {
637
+ method: 'evmSignMessageEIP712',
638
+ path: "m/44'/60'/0'/0/0",
639
+ domainHash: '0x'.concat('11'.repeat(32)),
640
+ messageHash: '0x'.concat('22'.repeat(32)),
641
+ },
642
+ });
643
+
644
+ method.init();
645
+ (method as any).device = {
646
+ features: {
647
+ onekey_device_type: 'pro2',
648
+ },
649
+ originalDescriptor: {
650
+ protocolType: 'V2',
651
+ },
652
+ };
653
+
654
+ await expect(method.run()).rejects.toEqual(
655
+ expect.objectContaining({
656
+ errorCode: HardwareErrorCode.DeviceNotSupportMethod,
657
+ })
658
+ );
659
+ });
660
+
661
+ test('returns a typed unsupported error for Tron sign message V1 before device binding', () => {
662
+ const method = new TronSignMessage({
663
+ id: 1,
664
+ payload: {
665
+ method: 'tronSignMessage',
666
+ path: "m/44'/195'/0'/0/0",
667
+ messageHex: '0x1234',
668
+ messageType: 'V1',
669
+ },
670
+ });
671
+
672
+ expect(() => method.init()).toThrow(
673
+ expect.objectContaining({
674
+ errorCode: HardwareErrorCode.DeviceNotSupportMethod,
675
+ })
676
+ );
677
+ });
678
+
679
+ test('does not mark Pro2 Tron and Solana address methods as unsupported', () => {
680
+ const features = {
681
+ onekey_device_type: 'pro2',
682
+ } as Features;
683
+
684
+ const tronMethod = new TronGetAddress({
685
+ id: 1,
686
+ payload: {
687
+ method: 'tronGetAddress',
688
+ path: "m/44'/195'/0'/0/0",
689
+ showOnOneKey: false,
690
+ },
691
+ });
692
+ const solMethod = new SolGetAddress({
693
+ id: 2,
694
+ payload: {
695
+ method: 'solGetAddress',
696
+ path: "m/44'/501'/0'/0'",
697
+ showOnOneKey: false,
698
+ },
699
+ });
700
+
701
+ expect(
702
+ isMethodVersionRangeUnsupported(
703
+ getMethodVersionRange(features, type => tronMethod.getVersionRange()[type])
704
+ )
705
+ ).toBe(false);
706
+ expect(
707
+ isMethodVersionRangeUnsupported(
708
+ getMethodVersionRange(features, type => solMethod.getVersionRange()[type])
709
+ )
710
+ ).toBe(false);
711
+ });
712
+
713
+ test('uses chunk transfer for large Sui transactions on Protocol V2', async () => {
714
+ const rawTx = '0x'.concat('ab'.repeat(5000));
715
+ const typedCall = jest.fn(() => ({
716
+ type: 'SuiSignedTx',
717
+ message: {
718
+ public_key: '',
719
+ signature: '',
720
+ },
721
+ }));
722
+ const method = new SuiSignTransaction({
723
+ id: 1,
724
+ payload: {
725
+ method: 'suiSignTransaction',
726
+ path: "m/44'/784'/0'/0'/0'",
727
+ rawTx,
728
+ },
729
+ });
730
+
731
+ method.init();
732
+ (method as any).device = {
733
+ features: {
734
+ onekey_device_type: 'pro2',
735
+ },
736
+ originalDescriptor: {
737
+ protocolType: 'V2',
738
+ },
739
+ getCommands: () => ({
740
+ typedCall,
741
+ }),
742
+ };
743
+
744
+ await method.run();
745
+
746
+ expect(typedCall).toHaveBeenCalledTimes(1);
747
+ const [, , params] = typedCall.mock.calls[0];
748
+ expect(params).toEqual(
749
+ expect.objectContaining({
750
+ raw_tx: '',
751
+ data_length: 5000,
752
+ })
753
+ );
754
+ expect(params.data_initial_chunk).toHaveLength(2048);
755
+ });
756
+
757
+ test('accepts string XRP payment amount values', () => {
758
+ const method = new XrpSignTransaction({
759
+ id: 1,
760
+ payload: {
761
+ method: 'xrpSignTransaction',
762
+ path: "m/44'/144'/0'/0/0",
763
+ transaction: {
764
+ fee: '100000',
765
+ flags: 2147483648,
766
+ sequence: 25,
767
+ maxLedgerVersion: 8820051,
768
+ payment: {
769
+ amount: '100000000',
770
+ destination: 'rBKz5MC2iXdoS3XgnNSYmF69K1Yo4NS3Ws',
771
+ },
772
+ },
773
+ },
774
+ });
775
+
776
+ expect(() => method.init()).not.toThrow();
777
+ expect(method.params.payment?.amount).toBe('100000000');
778
+ });
779
+
780
+ test('accepts Conflux base32 recipient addresses without hex formatting them', () => {
781
+ const to = 'cfx:aak2rra2njvd77ezwjvx04kkds9fzagfe6ku8scz91';
782
+ const method = new ConfluxSignTransaction({
783
+ id: 1,
784
+ payload: {
785
+ method: 'confluxSignTransaction',
786
+ path: "m/44'/503'/0'/0/0",
787
+ transaction: {
788
+ to,
789
+ value: '0x0',
790
+ data: '0x',
791
+ chainId: 1,
792
+ nonce: '0x00',
793
+ epochHeight: '0x00',
794
+ gasLimit: '0x5208',
795
+ storageLimit: '0x5208',
796
+ gasPrice: '0xbebc200',
797
+ },
798
+ },
799
+ });
800
+
801
+ expect(() => method.init()).not.toThrow();
802
+ expect(method.formattedTx?.to).toBe(to);
803
+ });
804
+
805
+ test('returns a typed unsupported error for Dynex signing on Protocol V2', async () => {
806
+ const method = new DnxSignTransaction({
807
+ id: 1,
808
+ payload: {
809
+ method: 'dnxSignTransaction',
810
+ path: "m/44'/29538'/0'/0/0",
811
+ inputs: [
812
+ {
813
+ prevIndex: 1,
814
+ globalIndex: 1,
815
+ txPubkey: '00',
816
+ prevOutPubkey: '00',
817
+ amount: '1',
818
+ },
819
+ ],
820
+ toAddress: 'dnx-address',
821
+ amount: '1',
822
+ fee: '1',
823
+ },
824
+ });
825
+
826
+ method.init();
827
+ (method as any).device = {
828
+ originalDescriptor: { protocolType: 'V2' },
829
+ features: normalizeProtocolV2Features({ ...descriptor, protocolType: 'V2' } as any),
830
+ };
831
+
832
+ await expect(method.run()).rejects.toMatchObject({
833
+ errorCode: HardwareErrorCode.DeviceNotSupportMethod,
834
+ });
835
+ });
836
+
837
+ test('returns a typed unsupported error for Dynex address on Protocol V2', async () => {
838
+ const method = new DnxGetAddress({
839
+ id: 1,
840
+ payload: {
841
+ method: 'dnxGetAddress',
842
+ path: "m/44'/29538'/0'/0/0",
843
+ showOnOneKey: false,
844
+ },
845
+ });
846
+
847
+ method.init();
848
+ (method as any).device = {
849
+ originalDescriptor: { protocolType: 'V2' },
850
+ features: normalizeProtocolV2Features({ ...descriptor, protocolType: 'V2' } as any),
851
+ };
852
+
853
+ await expect(method.run()).rejects.toMatchObject({
854
+ errorCode: HardwareErrorCode.DeviceNotSupportMethod,
855
+ });
856
+ });
857
+ });
858
+
859
+ describe('Protocol V2 firmware update targets', () => {
860
+ test('keeps Protocol V2 firmware updates off the legacy firmwareUpdateV3 path', async () => {
861
+ const method = new FirmwareUpdateV3({
862
+ id: 1,
863
+ payload: {
864
+ method: 'firmwareUpdateV3',
865
+ },
866
+ });
867
+ (method as any).device = {
868
+ originalDescriptor: { protocolType: 'V2' },
869
+ };
870
+
871
+ await expect(method.run()).rejects.toThrow('firmwareUpdateV4');
872
+ });
873
+
874
+ test('uses Protocol V2 features after BLE final reconnect without legacy Initialize', async () => {
875
+ const method = new FirmwareUpdateV4({
876
+ id: 1,
877
+ payload: {
878
+ method: 'firmwareUpdateV4',
879
+ },
880
+ });
881
+ const acquire = jest.fn().mockResolvedValue({ uuid: 'ble-session' });
882
+ const typedCall = jest.fn().mockImplementation((name: string) => {
883
+ if (name === 'DeviceGetDeviceInfo') {
884
+ return Promise.resolve({ type: 'DeviceInfo', message: {} });
885
+ }
886
+ return Promise.reject(new Error(`unexpected call ${name}`));
887
+ });
888
+ const commands = { typedCall };
889
+
890
+ (method as any).isBleReconnect = jest.fn(() => true);
891
+ (method as any).device = {
892
+ originalDescriptor: { id: 'ble-id', path: 'ble-path', protocolType: 'V2' },
893
+ deviceConnector: { acquire },
894
+ getCommands: () => commands,
895
+ _updateFeatures: jest.fn(),
896
+ };
897
+
898
+ const versions = await (method as any).waitForProtocolV2FinalFeatures();
899
+
900
+ expect(acquire).toHaveBeenCalledWith('ble-id', null, true, 'V2');
901
+ expect(typedCall).toHaveBeenNthCalledWith(
902
+ 1,
903
+ 'DeviceGetDeviceInfo',
904
+ 'DeviceInfo',
905
+ {
906
+ targets: {
907
+ hw: true,
908
+ fw: true,
909
+ bt: true,
910
+ status: true,
911
+ },
912
+ types: {
913
+ version: true,
914
+ specific: true,
915
+ },
916
+ },
917
+ { timeoutMs: 5000 }
918
+ );
919
+ expect(typedCall).toHaveBeenCalledTimes(1);
920
+ expect(typedCall).not.toHaveBeenCalledWith('Initialize', 'Features', {});
921
+ expect(versions).toEqual({
922
+ bootloaderVersion: '0.0.0',
923
+ bleVersion: '0.0.0',
924
+ firmwareVersion: '0.0.0',
925
+ });
926
+ });
927
+
928
+ test('runs Protocol V2 upload and install without rebooting to bootloader first', async () => {
929
+ const method = new FirmwareUpdateV4({
930
+ id: 1,
931
+ payload: {
932
+ method: 'firmwareUpdateV4',
933
+ },
934
+ });
935
+ method.init();
936
+
937
+ (method as any).device = {
938
+ originalDescriptor: { id: 'ble-id', path: 'ble-path', protocolType: 'V2' },
939
+ features: { capabilities: [] },
940
+ };
941
+ (method as any).prepareResourceBinary = jest.fn().mockResolvedValue(null);
942
+ (method as any).prepareFirmwareAndBleBinary = jest.fn().mockResolvedValue([
943
+ {
944
+ fileName: 'ble-firmware.bin',
945
+ binary: new Uint8Array([1, 2, 3]).buffer,
946
+ },
947
+ ]);
948
+ (method as any).prepareBootloaderBinary = jest.fn().mockResolvedValue(null);
949
+ (method as any).executeProtocolV2Update = jest.fn().mockResolvedValue(undefined);
950
+ (method as any).exitProtocolV2BootloaderToNormal = jest.fn().mockResolvedValue(undefined);
951
+ (method as any).waitForProtocolV2FinalFeatures = jest.fn().mockResolvedValue({
952
+ bootloaderVersion: '0.2.0',
953
+ bleVersion: '4.5.6',
954
+ firmwareVersion: '1.2.3',
955
+ });
956
+ (method as any).protocolV2Reboot = jest.fn();
957
+ method.postTipMessage = jest.fn();
958
+
959
+ await method.run();
960
+
961
+ expect((method as any).executeProtocolV2Update).toHaveBeenCalledWith({
962
+ resourceBinary: null,
963
+ fwBinaryMap: [
964
+ {
965
+ fileName: 'ble-firmware.bin',
966
+ binary: expect.any(ArrayBuffer),
967
+ },
968
+ ],
969
+ bootloaderBinary: null,
970
+ });
971
+ expect((method as any).protocolV2Reboot).not.toHaveBeenCalledWith(DeviceRebootType.Bootloader);
972
+ expect(method.postTipMessage).not.toHaveBeenCalledWith('AutoRebootToBootloader');
973
+ });
974
+
975
+ test('reboots Protocol V2 firmware flow back to normal before final feature polling', async () => {
976
+ const method = new FirmwareUpdateV4({
977
+ id: 1,
978
+ payload: {
979
+ method: 'firmwareUpdateV4',
980
+ },
981
+ });
982
+ method.postTipMessage = jest.fn();
983
+ (method as any).protocolV2Reboot = jest.fn().mockResolvedValue({
984
+ message: 'Device rebooted successfully',
985
+ });
986
+
987
+ await (method as any).exitProtocolV2BootloaderToNormal();
988
+
989
+ expect(method.postTipMessage).toHaveBeenCalledWith('SwitchFirmwareReconnectDevice');
990
+ expect((method as any).protocolV2Reboot).toHaveBeenCalledWith(DeviceRebootType.Normal);
991
+ });
992
+
993
+ test('treats iOS BLE RxError 6 during Protocol V2 reboot as expected disconnect', async () => {
994
+ const method = new FirmwareUpdateV4({
995
+ id: 1,
996
+ payload: {
997
+ method: 'firmwareUpdateV4',
998
+ },
999
+ });
1000
+ const typedCall = jest
1001
+ .fn()
1002
+ .mockRejectedValue(
1003
+ new Error("The operation couldn't be completed. (MultiplatformBleAdapter.RxError error 6.)")
1004
+ );
1005
+
1006
+ (method as any).device = {
1007
+ getCommands: () => ({ typedCall }),
1008
+ };
1009
+
1010
+ await expect((method as any).protocolV2Reboot(DeviceRebootType.Normal)).resolves.toEqual({
1011
+ message: 'Device rebooted successfully',
1012
+ });
1013
+ });
1014
+
1015
+ test('treats direct disconnect during Protocol V2 normal reboot as expected', async () => {
1016
+ const method = new FirmwareUpdateV4({
1017
+ id: 1,
1018
+ payload: {
1019
+ method: 'firmwareUpdateV4',
1020
+ },
1021
+ });
1022
+ const typedCall = jest
1023
+ .fn()
1024
+ .mockRejectedValue(new Error('Connection error has occured: Device disconnected'));
1025
+
1026
+ (method as any).device = {
1027
+ getCommands: () => ({ typedCall }),
1028
+ };
1029
+
1030
+ await expect((method as any).protocolV2Reboot(DeviceRebootType.Normal)).resolves.toEqual({
1031
+ message: 'Device rebooted successfully',
1032
+ });
1033
+ });
1034
+
1035
+ test('continues Protocol V2 install polling through temporary expected V2 probe failures', async () => {
1036
+ const method = new FirmwareUpdateV4({
1037
+ id: 1,
1038
+ payload: {
1039
+ method: 'firmwareUpdateV4',
1040
+ },
1041
+ });
1042
+ const typedCall = jest
1043
+ .fn()
1044
+ .mockRejectedValueOnce(
1045
+ new Error(
1046
+ 'Device protocol mismatch: expected V2, but device did not respond to expected protocol'
1047
+ )
1048
+ )
1049
+ .mockResolvedValueOnce({
1050
+ type: 'DeviceFirmwareUpdateStatus',
1051
+ message: {
1052
+ targets: [{ target_id: 2, status: 0 }],
1053
+ },
1054
+ });
1055
+ const reconnectProtocolV2Device = jest.fn().mockResolvedValue(undefined);
1056
+
1057
+ (method as any).device = {
1058
+ getCommands: () => ({ typedCall }),
1059
+ };
1060
+ (method as any).reconnectProtocolV2Device = reconnectProtocolV2Device;
1061
+ method.postProgressMessage = jest.fn();
1062
+
1063
+ await (method as any).waitForProtocolV2FirmwareUpdateComplete([
1064
+ { target_id: 2, path: 'vol1:ble-firmware.bin' },
1065
+ ]);
1066
+
1067
+ expect(reconnectProtocolV2Device).toHaveBeenCalledTimes(1);
1068
+ expect(typedCall).toHaveBeenCalledTimes(2);
1069
+ });
1070
+
1071
+ test('passes resource, bootloader, BLE, SE and app files to DeviceFirmwareUpdate targets', async () => {
1072
+ const resourceZip = new JSZip();
1073
+ resourceZip.file('icons/home.png', new Uint8Array([1, 2, 3]));
1074
+ const resourceBinary = await resourceZip.generateAsync({ type: 'arraybuffer' });
1075
+ const method = new FirmwareUpdateV4({
1076
+ id: 1,
1077
+ payload: {
1078
+ method: 'firmwareUpdateV4',
1079
+ },
1080
+ });
1081
+
1082
+ const writtenPaths: string[] = [];
1083
+ method.postTipMessage = jest.fn();
1084
+ (method as any).protocolV2CreateFolder = jest.fn().mockResolvedValue(undefined);
1085
+ (method as any).protocolV2CommonUpdateProcess = jest.fn().mockImplementation(params => {
1086
+ writtenPaths.push(params.filePath);
1087
+ return Number(params.processedSize ?? 0) + Number(params.payload.byteLength);
1088
+ });
1089
+ (method as any).protocolV2StartFirmwareUpdate = jest.fn().mockResolvedValue(undefined);
1090
+ (method as any).waitForProtocolV2FirmwareUpdateComplete = jest
1091
+ .fn()
1092
+ .mockResolvedValue(undefined);
1093
+
1094
+ await (method as any).executeProtocolV2Update({
1095
+ resourceBinary,
1096
+ bootloaderBinary: new Uint8Array([4, 5]).buffer,
1097
+ fwBinaryMap: [
1098
+ {
1099
+ fileName: 'ble-firmware.bin',
1100
+ binary: new Uint8Array([6]).buffer,
1101
+ },
1102
+ {
1103
+ fileName: 'se1-firmware.bin',
1104
+ binary: new Uint8Array([7]).buffer,
1105
+ },
1106
+ {
1107
+ fileName: 'firmware.bin',
1108
+ binary: new Uint8Array([8]).buffer,
1109
+ },
1110
+ ],
1111
+ });
1112
+
1113
+ expect((method as any).protocolV2CreateFolder).toHaveBeenCalledWith('vol1:res/');
1114
+ expect(writtenPaths).toEqual([
1115
+ 'vol1:res/home.png',
1116
+ 'vol1:bootloader.bin',
1117
+ 'vol1:ble-firmware.bin',
1118
+ 'vol1:se1-firmware.bin',
1119
+ 'vol1:firmware.bin',
1120
+ ]);
1121
+ expect((method as any).protocolV2StartFirmwareUpdate).toHaveBeenCalledWith({
1122
+ targets: [
1123
+ { target_id: 10, path: 'vol1:res/' },
1124
+ { target_id: 2, path: 'vol1:bootloader.bin' },
1125
+ { target_id: 5, path: 'vol1:ble-firmware.bin' },
1126
+ { target_id: 6, path: 'vol1:se1-firmware.bin' },
1127
+ { target_id: 3, path: 'vol1:firmware.bin' },
1128
+ ],
1129
+ });
1130
+ expect((method as any).waitForProtocolV2FirmwareUpdateComplete).toHaveBeenCalled();
1131
+ });
1132
+
1133
+ test('uses absolute processed_byte offsets and disables append for firmware file writes', async () => {
1134
+ const method = new FirmwareUpdateV4({
1135
+ id: 1,
1136
+ payload: {
1137
+ method: 'firmwareUpdateV4',
1138
+ },
1139
+ });
1140
+ const typedCall = jest.fn(
1141
+ (
1142
+ _name: string,
1143
+ _resType: string,
1144
+ params: { file: { offset: number; data: { byteLength: number } } }
1145
+ ) =>
1146
+ Promise.resolve({
1147
+ type: 'FilesystemFile',
1148
+ message: {
1149
+ processed_byte: params.file.offset + params.file.data.byteLength,
1150
+ },
1151
+ })
1152
+ );
1153
+
1154
+ (method as any).device = {
1155
+ getCommands: () => ({ typedCall }),
1156
+ };
1157
+ method.postProgressMessage = jest.fn();
1158
+
1159
+ await (method as any).protocolV2CommonUpdateProcess({
1160
+ payload: new Uint8Array(4097).buffer,
1161
+ filePath: 'vol1:firmware.bin',
1162
+ processedSize: 0,
1163
+ totalSize: 4097,
1164
+ transferStartTime: Date.now() - 1000,
1165
+ });
1166
+
1167
+ const writePayloads = typedCall.mock.calls.map(call => call[2]);
1168
+ expect(writePayloads.map(payload => payload.file.offset)).toEqual([0, 4096]);
1169
+ expect(writePayloads.map(payload => payload.file.data.byteLength)).toEqual([4096, 1]);
1170
+ expect(writePayloads.map(payload => payload.overwrite)).toEqual([true, false]);
1171
+ expect(writePayloads.every(payload => payload.append === false)).toBe(true);
1172
+ expect(method.postProgressMessage).toHaveBeenLastCalledWith(
1173
+ 99,
1174
+ 'transferData',
1175
+ expect.objectContaining({
1176
+ transferredBytes: 4097,
1177
+ totalBytes: 4097,
1178
+ rateBytesPerSecond: expect.any(Number),
1179
+ elapsedMs: expect.any(Number),
1180
+ })
1181
+ );
1182
+ });
1183
+
1184
+ test('caps native BLE firmware upload chunks below the WebUSB limit', async () => {
1185
+ const method = new FirmwareUpdateV4({
1186
+ id: 1,
1187
+ payload: {
1188
+ method: 'firmwareUpdateV4',
1189
+ },
1190
+ });
1191
+ const typedCall = jest.fn(
1192
+ (
1193
+ _name: string,
1194
+ _resType: string,
1195
+ params: { file: { offset: number; data: { byteLength: number } } }
1196
+ ) =>
1197
+ Promise.resolve({
1198
+ type: 'FilesystemFile',
1199
+ message: {
1200
+ processed_byte: params.file.offset + params.file.data.byteLength,
1201
+ },
1202
+ })
1203
+ );
1204
+
1205
+ (method as any).params = {
1206
+ platform: 'native',
1207
+ chunkSize: 4096,
1208
+ };
1209
+ (method as any).device = {
1210
+ getCommands: () => ({ typedCall }),
1211
+ };
1212
+ method.postProgressMessage = jest.fn();
1213
+
1214
+ await (method as any).protocolV2CommonUpdateProcess({
1215
+ payload: new Uint8Array(1801).buffer,
1216
+ filePath: 'vol1:ble-firmware.bin',
1217
+ processedSize: 0,
1218
+ totalSize: 1801,
1219
+ });
1220
+
1221
+ const writePayloads = typedCall.mock.calls.map(call => call[2]);
1222
+ expect(writePayloads.map(payload => payload.file.offset)).toEqual([0, 1800]);
1223
+ expect(writePayloads.map(payload => payload.file.data.byteLength)).toEqual([1800, 1]);
1224
+ });
1225
+
1226
+ test('consumes Protocol V2 install progress before final update success', async () => {
1227
+ const method = new FirmwareUpdateV4({
1228
+ id: 1,
1229
+ payload: {
1230
+ method: 'firmwareUpdateV4',
1231
+ },
1232
+ });
1233
+ const typedCall = jest.fn().mockResolvedValue({ type: 'Success', message: { message: 'ok' } });
1234
+
1235
+ (method as any).device = {
1236
+ getCommands: () => ({ typedCall }),
1237
+ };
1238
+ method.postProgressMessage = jest.fn();
1239
+ method.postTipMessage = jest.fn();
1240
+
1241
+ await (method as any).protocolV2StartFirmwareUpdate({
1242
+ targets: [{ target_id: 0, path: 'vol1:firmware.bin' }],
1243
+ });
1244
+
1245
+ const callOptions = typedCall.mock.calls[0][3];
1246
+ expect(typedCall.mock.calls[0][1]).toEqual(['Success', 'DeviceFirmwareUpdateStatus']);
1247
+ expect(callOptions.intermediateTypes).toEqual(['DeviceFirmwareInstallProgress']);
1248
+ callOptions.onIntermediateResponse({
1249
+ type: 'DeviceFirmwareInstallProgress',
1250
+ message: { target_id: 0, progress: 42 },
1251
+ });
1252
+
1253
+ expect(method.postProgressMessage).toHaveBeenCalledWith(42, 'installingFirmware');
1254
+ });
1255
+
1256
+ test('accepts Protocol V2 firmware update status as start response', async () => {
1257
+ const method = new FirmwareUpdateV4({
1258
+ id: 1,
1259
+ payload: {
1260
+ method: 'firmwareUpdateV4',
1261
+ },
1262
+ });
1263
+ const typedCall = jest.fn().mockResolvedValue({
1264
+ type: 'DeviceFirmwareUpdateStatus',
1265
+ message: { targets: [{ target_id: 0, status: 1 }] },
1266
+ });
1267
+
1268
+ (method as any).device = {
1269
+ getCommands: () => ({ typedCall }),
1270
+ };
1271
+ method.postProgressMessage = jest.fn();
1272
+ method.postTipMessage = jest.fn();
1273
+
1274
+ await (method as any).protocolV2StartFirmwareUpdate({
1275
+ targets: [{ target_id: 0, path: 'vol1:firmware.bin' }],
1276
+ });
1277
+
1278
+ expect(typedCall.mock.calls[0][1]).toEqual(['Success', 'DeviceFirmwareUpdateStatus']);
1279
+ expect(method.postTipMessage).toHaveBeenCalledWith('FirmwareUpdating');
1280
+ });
1281
+ });
1282
+
1283
+ describe('Protocol V2 firmware update method', () => {
1284
+ test('returns DeviceFirmwareUpdateStatus from low-level update trigger', async () => {
1285
+ const method = new DeviceFirmwareUpdate({
1286
+ id: 1,
1287
+ payload: {
1288
+ method: 'deviceFirmwareUpdate',
1289
+ targetId: 3,
1290
+ path: 'vol0:firmware.bin',
1291
+ },
1292
+ });
1293
+ method.init();
1294
+
1295
+ const typedCall = jest.fn().mockResolvedValue({
1296
+ type: 'DeviceFirmwareUpdateStatus',
1297
+ message: { targets: [{ target_id: 0, status: 1 }] },
1298
+ });
1299
+
1300
+ (method as any).device = {
1301
+ commands: { typedCall },
1302
+ };
1303
+
1304
+ await expect(method.run()).resolves.toEqual({
1305
+ targets: [{ target_id: 0, status: 1 }],
1306
+ });
1307
+ expect(typedCall.mock.calls[0][1]).toEqual(['Success', 'DeviceFirmwareUpdateStatus']);
1308
+ expect(typedCall.mock.calls[0][2]).toEqual({
1309
+ targets: [{ target_id: 3, path: 'vol0:firmware.bin' }],
1310
+ });
1311
+ });
1312
+
1313
+ test('rejects missing or invalid firmware targets before transport call', async () => {
1314
+ const typedCall = jest.fn();
1315
+ const method = new DeviceFirmwareUpdate({
1316
+ id: 1,
1317
+ payload: {
1318
+ method: 'deviceFirmwareUpdate',
1319
+ path: 'vol0:firmware.bin',
1320
+ },
1321
+ });
1322
+ method.init();
1323
+ (method as any).device = {
1324
+ commands: { typedCall },
1325
+ };
1326
+
1327
+ await expect(method.run()).rejects.toMatchObject({
1328
+ errorCode: HardwareErrorCode.CallMethodInvalidParameter,
1329
+ });
1330
+ expect(typedCall).not.toHaveBeenCalled();
1331
+ });
1332
+
1333
+ test('accepts targetId alias inside firmware targets', async () => {
1334
+ const typedCall = jest.fn().mockResolvedValue({ message: {} });
1335
+ const method = new DeviceFirmwareUpdate({
1336
+ id: 1,
1337
+ payload: {
1338
+ method: 'deviceFirmwareUpdate',
1339
+ targets: [
1340
+ {
1341
+ target_id: undefined,
1342
+ targetId: 3,
1343
+ path: 'vol0:firmware.bin',
1344
+ },
1345
+ ],
1346
+ } as any,
1347
+ });
1348
+ method.init();
1349
+ (method as any).device = {
1350
+ commands: { typedCall },
1351
+ };
1352
+
1353
+ await method.run();
1354
+ expect(typedCall.mock.calls[0][2]).toEqual({
1355
+ targets: [{ target_id: 3, path: 'vol0:firmware.bin' }],
1356
+ });
1357
+ });
1358
+ });
1359
+
1360
+ describe('Protocol V2 onboarding status method', () => {
1361
+ test('returns DeviceOnboardingStatus from low-level status query', async () => {
1362
+ const method = new DeviceGetOnboardingStatus({
1363
+ id: 1,
1364
+ payload: {
1365
+ method: 'deviceGetOnboardingStatus',
1366
+ },
1367
+ });
1368
+ method.init();
1369
+
1370
+ const typedCall = jest.fn().mockResolvedValue({
1371
+ type: 'DeviceOnboardingStatus',
1372
+ message: {
1373
+ step: 4,
1374
+ page_name: 'Recovery Phrase',
1375
+ },
1376
+ });
1377
+
1378
+ (method as any).device = {
1379
+ commands: { typedCall },
1380
+ };
1381
+
1382
+ await expect(method.run()).resolves.toEqual({
1383
+ step: 4,
1384
+ page_name: 'Recovery Phrase',
1385
+ });
1386
+ expect(typedCall).toHaveBeenCalledWith(
1387
+ 'DeviceGetOnboardingStatus',
1388
+ 'DeviceOnboardingStatus',
1389
+ {}
1390
+ );
1391
+ });
1392
+ });
1393
+
1394
+ describe('Protocol V2 file write method', () => {
1395
+ test('rejects invalid write parameters before transport call', () => {
1396
+ expect(() => {
1397
+ const method = new FileWrite({
1398
+ id: 1,
1399
+ payload: {
1400
+ method: 'fileWrite',
1401
+ path: 'vol1:test.bin',
1402
+ offset: -1,
1403
+ data: new Uint8Array([1]),
1404
+ },
1405
+ });
1406
+ method.init();
1407
+ }).toThrow(
1408
+ expect.objectContaining({ errorCode: HardwareErrorCode.CallMethodInvalidParameter })
1409
+ );
1410
+
1411
+ expect(() => {
1412
+ const method = new FileWrite({
1413
+ id: 1,
1414
+ payload: {
1415
+ method: 'fileWrite',
1416
+ path: 'vol1:test.bin',
1417
+ } as any,
1418
+ });
1419
+ method.init();
1420
+ }).toThrow(
1421
+ expect.objectContaining({ errorCode: HardwareErrorCode.CallMethodInvalidParameter })
1422
+ );
1423
+ });
1424
+
1425
+ test('uses demo-aligned overwrite and append defaults', async () => {
1426
+ const typedCall = jest.fn().mockResolvedValue({ message: { processed_byte: 1 } });
1427
+ const method = new FileWrite({
1428
+ id: 1,
1429
+ payload: {
1430
+ method: 'fileWrite',
1431
+ path: 'vol1:test.bin',
1432
+ offset: 1,
1433
+ totalSize: 2,
1434
+ data: new Uint8Array([1]),
1435
+ },
1436
+ });
1437
+ (method as any).device = { commands: { typedCall } };
1438
+ method.postMessage = jest.fn();
1439
+
1440
+ method.init();
1441
+ await method.run();
1442
+
1443
+ expect(typedCall).toHaveBeenCalledWith('FilesystemFileWrite', 'FilesystemFile', {
1444
+ file: {
1445
+ path: 'vol1:test.bin',
1446
+ offset: 1,
1447
+ total_size: 2,
1448
+ data: new Uint8Array([1]),
1449
+ },
1450
+ overwrite: false,
1451
+ append: false,
1452
+ ui_percentage: 99,
1453
+ });
1454
+ expect(method.postMessage).toHaveBeenCalledWith({
1455
+ event: 'UI_EVENT',
1456
+ type: UI_REQUEST.DEVICE_PROGRESS,
1457
+ payload: expect.objectContaining({
1458
+ progress: 100,
1459
+ transferredBytes: 1,
1460
+ totalBytes: 1,
1461
+ elapsedMs: expect.any(Number),
1462
+ }),
1463
+ });
1464
+ });
1465
+
1466
+ test('splits data larger than the Protocol V2 file payload limit', async () => {
1467
+ const data = new Uint8Array(4097);
1468
+ const typedCall = jest.fn().mockResolvedValue({ message: {} });
1469
+ const method = new FileWrite({
1470
+ id: 1,
1471
+ payload: {
1472
+ method: 'fileWrite',
1473
+ path: 'vol1:test.bin',
1474
+ offset: 0,
1475
+ totalSize: 4097,
1476
+ data,
1477
+ },
1478
+ });
1479
+ (method as any).device = { commands: { typedCall } };
1480
+ method.postMessage = jest.fn();
1481
+
1482
+ method.init();
1483
+ const result = await method.run();
1484
+
1485
+ expect(typedCall).toHaveBeenCalledTimes(2);
1486
+ expect(typedCall).toHaveBeenNthCalledWith(1, 'FilesystemFileWrite', 'FilesystemFile', {
1487
+ file: {
1488
+ path: 'vol1:test.bin',
1489
+ offset: 0,
1490
+ total_size: 4097,
1491
+ data: data.slice(0, 4096),
1492
+ },
1493
+ overwrite: true,
1494
+ append: false,
1495
+ ui_percentage: 99,
1496
+ });
1497
+ expect(typedCall).toHaveBeenNthCalledWith(2, 'FilesystemFileWrite', 'FilesystemFile', {
1498
+ file: {
1499
+ path: 'vol1:test.bin',
1500
+ offset: 4096,
1501
+ total_size: 4097,
1502
+ data: data.slice(4096),
1503
+ },
1504
+ overwrite: false,
1505
+ append: false,
1506
+ ui_percentage: 99,
1507
+ });
1508
+ expect(result).toMatchObject({
1509
+ path: 'vol1:test.bin',
1510
+ processed_byte: 4097,
1511
+ chunks: 2,
1512
+ });
1513
+ expect(method.postMessage).toHaveBeenNthCalledWith(1, {
1514
+ event: 'UI_EVENT',
1515
+ type: UI_REQUEST.DEVICE_PROGRESS,
1516
+ payload: expect.objectContaining({
1517
+ progress: 99,
1518
+ transferredBytes: 4096,
1519
+ totalBytes: 4097,
1520
+ elapsedMs: expect.any(Number),
1521
+ }),
1522
+ });
1523
+ expect(method.postMessage).toHaveBeenNthCalledWith(2, {
1524
+ event: 'UI_EVENT',
1525
+ type: UI_REQUEST.DEVICE_PROGRESS,
1526
+ payload: expect.objectContaining({
1527
+ progress: 100,
1528
+ transferredBytes: 4097,
1529
+ totalBytes: 4097,
1530
+ elapsedMs: expect.any(Number),
1531
+ }),
1532
+ });
1533
+ });
1534
+
1535
+ test('uses the BLE chunk limit by default in BLE environments', async () => {
1536
+ const getSettingsSpy = jest.spyOn(DataManager, 'getSettings').mockReturnValue('react-native');
1537
+ const data = new Uint8Array(1801);
1538
+ const typedCall = jest.fn().mockResolvedValue({ message: {} });
1539
+ const method = new FileWrite({
1540
+ id: 1,
1541
+ payload: {
1542
+ method: 'fileWrite',
1543
+ path: 'vol1:test.bin',
1544
+ offset: 0,
1545
+ totalSize: 1801,
1546
+ data,
1547
+ },
1548
+ });
1549
+ (method as any).device = { commands: { typedCall } };
1550
+ method.postMessage = jest.fn();
1551
+
1552
+ try {
1553
+ method.init();
1554
+ await method.run();
1555
+ } finally {
1556
+ getSettingsSpy.mockRestore();
1557
+ }
1558
+
1559
+ expect(typedCall).toHaveBeenCalledTimes(2);
1560
+ expect(typedCall.mock.calls[0][2].file.data.byteLength).toBe(1800);
1561
+ expect(typedCall.mock.calls[1][2].file.offset).toBe(1800);
1562
+ expect(typedCall.mock.calls[1][2].file.data.byteLength).toBe(1);
1563
+ });
1564
+ });
1565
+
1566
+ describe('Protocol V2 file read method', () => {
1567
+ test('rejects invalid read and directory parameters before transport call', () => {
1568
+ expect(() => {
1569
+ const method = new FileRead({
1570
+ id: 1,
1571
+ payload: {
1572
+ method: 'fileRead',
1573
+ path: '',
1574
+ offset: 0,
1575
+ },
1576
+ });
1577
+ method.init();
1578
+ }).toThrow(
1579
+ expect.objectContaining({ errorCode: HardwareErrorCode.CallMethodInvalidParameter })
1580
+ );
1581
+
1582
+ expect(() => {
1583
+ const method = new FileRead({
1584
+ id: 1,
1585
+ payload: {
1586
+ method: 'fileRead',
1587
+ path: 'vol1:test.bin',
1588
+ totalSize: -1,
1589
+ },
1590
+ });
1591
+ method.init();
1592
+ }).toThrow(
1593
+ expect.objectContaining({ errorCode: HardwareErrorCode.CallMethodInvalidParameter })
1594
+ );
1595
+
1596
+ expect(() => {
1597
+ const method = new DirList({
1598
+ id: 1,
1599
+ payload: {
1600
+ method: 'dirList',
1601
+ path: 'vol1:',
1602
+ depth: -1,
1603
+ },
1604
+ });
1605
+ method.init();
1606
+ }).toThrow(
1607
+ expect.objectContaining({ errorCode: HardwareErrorCode.CallMethodInvalidParameter })
1608
+ );
1609
+ });
1610
+
1611
+ test('reads full file in chunks when read length is 0', async () => {
1612
+ const firstChunk = new Uint8Array(64).fill(1);
1613
+ const typedCall = jest
1614
+ .fn()
1615
+ .mockResolvedValueOnce({ message: { exist: true, size: 65, directory: false } })
1616
+ .mockResolvedValueOnce({ message: { data: firstChunk } })
1617
+ .mockResolvedValueOnce({ message: { data: new Uint8Array([2]) } });
1618
+ const method = new FileRead({
1619
+ id: 1,
1620
+ payload: {
1621
+ method: 'fileRead',
1622
+ path: 'vol1:test.bin',
1623
+ offset: 0,
1624
+ totalSize: 0,
1625
+ chunkLen: 64,
1626
+ },
1627
+ });
1628
+ (method as any).device = { commands: { typedCall } };
1629
+
1630
+ method.init();
1631
+ const result = await method.run();
1632
+
1633
+ expect(typedCall).toHaveBeenNthCalledWith(1, 'FilesystemPathInfoQuery', 'FilesystemPathInfo', {
1634
+ path: 'vol1:test.bin',
1635
+ });
1636
+ expect(typedCall).toHaveBeenNthCalledWith(2, 'FilesystemFileRead', 'FilesystemFile', {
1637
+ file: {
1638
+ path: 'vol1:test.bin',
1639
+ offset: 0,
1640
+ total_size: 0,
1641
+ },
1642
+ chunk_len: 64,
1643
+ ui_percentage: 99,
1644
+ });
1645
+ expect(typedCall).toHaveBeenNthCalledWith(3, 'FilesystemFileRead', 'FilesystemFile', {
1646
+ file: {
1647
+ path: 'vol1:test.bin',
1648
+ offset: 64,
1649
+ total_size: 0,
1650
+ },
1651
+ chunk_len: 1,
1652
+ ui_percentage: 99,
1653
+ });
1654
+ expect(result.data.byteLength).toBe(65);
1655
+ expect(result.data[0]).toBe(1);
1656
+ expect(result.data[64]).toBe(2);
1657
+ expect(result).toMatchObject({
1658
+ path: 'vol1:test.bin',
1659
+ offset: 0,
1660
+ total_size: 65,
1661
+ chunks: 2,
1662
+ });
1663
+ });
1664
+
1665
+ test('decodes protobuf bytes hex string returned by transport', async () => {
1666
+ const typedCall = jest.fn().mockResolvedValue({
1667
+ message: {
1668
+ data: '0102ff',
1669
+ },
1670
+ });
1671
+ const method = new FileRead({
1672
+ id: 1,
1673
+ payload: {
1674
+ method: 'fileRead',
1675
+ path: 'vol0:test.bin',
1676
+ offset: 0,
1677
+ totalSize: 3,
1678
+ chunkLen: 512,
1679
+ },
1680
+ });
1681
+ (method as any).device = { commands: { typedCall } };
1682
+
1683
+ method.init();
1684
+ const result = await method.run();
1685
+
1686
+ expect(result.data).toEqual(new Uint8Array([1, 2, 255]));
1687
+ });
1688
+ });