@onekeyfe/hd-core 1.2.0-alpha.9 → 1.2.0-alpha.90

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 (813) hide show
  1. package/__tests__/AllNetworkGetAddressBase.tracing.test.ts +390 -0
  2. package/__tests__/DeviceCommands.test.ts +563 -0
  3. package/__tests__/DeviceEventRegistration.test.ts +49 -0
  4. package/__tests__/chain-params-onekey-compatibility.test.ts +229 -0
  5. package/__tests__/check-all-firmware-release-protocol-v2.test.ts +825 -0
  6. package/__tests__/check-firmware-release-protocol-v2.test.ts +199 -0
  7. package/__tests__/connectSettings.test.ts +45 -5
  8. package/__tests__/core-dispose.test.ts +34 -0
  9. package/__tests__/core-error-output.test.ts +39 -0
  10. package/__tests__/core-initialization.test.ts +23 -0
  11. package/__tests__/device-connector-protocol.test.ts +81 -0
  12. package/__tests__/device-features-state.test.ts +78 -0
  13. package/__tests__/device-identity.test.ts +52 -0
  14. package/__tests__/device-initialize-timeout.test.ts +56 -0
  15. package/__tests__/device-lifecycle-events.test.ts +351 -0
  16. package/__tests__/device-pool-state.test.ts +166 -0
  17. package/__tests__/device-settings.test.ts +437 -0
  18. package/__tests__/device-state-contract.test.ts +28 -0
  19. package/__tests__/device-state-events.test.ts +286 -0
  20. package/__tests__/device-state-mapper.test.ts +288 -0
  21. package/__tests__/device-state-projector.test.ts +114 -0
  22. package/__tests__/device-state-store.test.ts +109 -0
  23. package/__tests__/device-utils.test.ts +154 -0
  24. package/__tests__/device-wallet-session-store.test.ts +348 -1
  25. package/__tests__/deviceFeaturesUtils.test.ts +39 -0
  26. package/__tests__/devicePoolBootloaderPath.test.ts +64 -0
  27. package/__tests__/deviceSettings.test.ts +113 -0
  28. package/__tests__/deviceUploadNft.test.ts +316 -0
  29. package/__tests__/evmLedgerLegacySafety.test.ts +12 -6
  30. package/__tests__/evmSignTransaction.test.ts +69 -0
  31. package/__tests__/evmSignTypedData.test.ts +80 -0
  32. package/__tests__/filesystemValidation.test.ts +53 -0
  33. package/__tests__/firmware-memory-host.test.ts +112 -0
  34. package/__tests__/firmware-update/check-all-firmware-release.test.ts +175 -0
  35. package/__tests__/firmware-update/device-update-bootloader.test.ts +91 -0
  36. package/__tests__/firmware-update/firmware-artifact-source.test.ts +119 -0
  37. package/__tests__/firmware-update/firmware-host-binding.test.ts +79 -0
  38. package/__tests__/firmware-update/firmware-preparation-error.test.ts +27 -0
  39. package/__tests__/firmware-update/firmware-prepared-host-digest.test.ts +209 -0
  40. package/__tests__/firmware-update/firmware-update-bootloader-poll.test.ts +200 -0
  41. package/__tests__/firmware-update/firmware-update-capabilities.test.ts +13 -0
  42. package/__tests__/firmware-update/firmware-update-plan-bootloader-identity.test.ts +394 -0
  43. package/__tests__/firmware-update/firmware-update-plan.test.ts +654 -0
  44. package/__tests__/firmware-update/firmware-update-prepared-plan.test.ts +283 -0
  45. package/__tests__/firmware-update/firmware-update-v2-download-before-boot.test.ts +457 -0
  46. package/__tests__/firmware-update/firmware-update-v3-reconnect.test.ts +121 -0
  47. package/__tests__/firmware-update/firmware-upload-source.test.ts +70 -0
  48. package/__tests__/get-device-state.test.ts +578 -0
  49. package/__tests__/homescreen.test.ts +81 -0
  50. package/__tests__/kaspa-public-types.type-test.ts +16 -0
  51. package/__tests__/kaspa-use-tweak-pro2.test.ts +20 -0
  52. package/__tests__/kaspaSignTransaction.test.ts +578 -0
  53. package/__tests__/known-device-public-types.type-test.ts +3 -0
  54. package/__tests__/logBlockEvent.test.ts +128 -1
  55. package/__tests__/method-protocol-support.test.ts +144 -0
  56. package/__tests__/networkUtils.test.ts +406 -0
  57. package/__tests__/open-wallet-session-error-response.test.ts +43 -0
  58. package/__tests__/open-wallet-session.test.ts +1899 -0
  59. package/__tests__/pro2Nft.test.ts +108 -0
  60. package/__tests__/pro2Wallpaper.test.ts +39 -0
  61. package/__tests__/protocol-binding.test.ts +89 -0
  62. package/__tests__/protocol-v2-bootloader-mode.test.ts +37 -0
  63. package/__tests__/protocol-v2-firmware-targets.test.ts +66 -0
  64. package/__tests__/protocol-v2-ping.test.ts +28 -0
  65. package/__tests__/protocol-v2-resources.test.ts +270 -0
  66. package/__tests__/protocol-v2-ui-lifecycle.test.ts +91 -0
  67. package/__tests__/protocol-v2-unlock-policy.test.ts +308 -0
  68. package/__tests__/protocol-v2.test.ts +6669 -1759
  69. package/__tests__/protocolV2FileWrite.test.ts +285 -0
  70. package/__tests__/protocolV2UiInteraction.test.ts +329 -0
  71. package/__tests__/public-device-state-api.test.ts +275 -0
  72. package/__tests__/public-pro2-api-boundary.test.ts +107 -0
  73. package/__tests__/refresh-device-state.test.ts +101 -0
  74. package/__tests__/request-context-logging.test.ts +16 -0
  75. package/__tests__/search-devices.test.ts +145 -0
  76. package/__tests__/tron-sign-message-init.test.ts +74 -0
  77. package/__tests__/uiProgressThrottle.test.ts +74 -0
  78. package/__tests__/uiPromiseRegistry.test.ts +115 -0
  79. package/dist/api/BaseMethod.d.ts +16 -1
  80. package/dist/api/BaseMethod.d.ts.map +1 -1
  81. package/dist/api/CheckAllFirmwareRelease.d.ts +25 -1
  82. package/dist/api/CheckAllFirmwareRelease.d.ts.map +1 -1
  83. package/dist/api/CheckBLEFirmwareRelease.d.ts +3 -2
  84. package/dist/api/CheckBLEFirmwareRelease.d.ts.map +1 -1
  85. package/dist/api/CheckBootloaderRelease.d.ts +2 -1
  86. package/dist/api/CheckBootloaderRelease.d.ts.map +1 -1
  87. package/dist/api/CheckFirmwareRelease.d.ts +3 -2
  88. package/dist/api/CheckFirmwareRelease.d.ts.map +1 -1
  89. package/dist/api/CipherKeyValue.d.ts +1 -0
  90. package/dist/api/CipherKeyValue.d.ts.map +1 -1
  91. package/dist/api/ClearSessionCache.d.ts.map +1 -1
  92. package/dist/api/DetectDeviceConnectProtocol.d.ts +7 -0
  93. package/dist/api/DetectDeviceConnectProtocol.d.ts.map +1 -0
  94. package/dist/api/DirList.d.ts +1 -0
  95. package/dist/api/DirList.d.ts.map +1 -1
  96. package/dist/api/DirMake.d.ts +1 -0
  97. package/dist/api/DirMake.d.ts.map +1 -1
  98. package/dist/api/DirRemove.d.ts +1 -0
  99. package/dist/api/DirRemove.d.ts.map +1 -1
  100. package/dist/api/FileDelete.d.ts +1 -0
  101. package/dist/api/FileDelete.d.ts.map +1 -1
  102. package/dist/api/FileRead.d.ts +1 -0
  103. package/dist/api/FileRead.d.ts.map +1 -1
  104. package/dist/api/FileWrite.d.ts +2 -0
  105. package/dist/api/FileWrite.d.ts.map +1 -1
  106. package/dist/api/FirmwareUpdate.d.ts.map +1 -1
  107. package/dist/api/FirmwareUpdateV2.d.ts +12 -2
  108. package/dist/api/FirmwareUpdateV2.d.ts.map +1 -1
  109. package/dist/api/FirmwareUpdateV3.d.ts +7 -3
  110. package/dist/api/FirmwareUpdateV3.d.ts.map +1 -1
  111. package/dist/api/FirmwareUpdateV4.d.ts +46 -16
  112. package/dist/api/FirmwareUpdateV4.d.ts.map +1 -1
  113. package/dist/api/GetDeviceState.d.ts +8 -0
  114. package/dist/api/GetDeviceState.d.ts.map +1 -0
  115. package/dist/api/GetFeatures.d.ts +1 -1
  116. package/dist/api/GetFeatures.d.ts.map +1 -1
  117. package/dist/api/GetOnekeyFeatures.d.ts.map +1 -1
  118. package/dist/api/GetPassphraseState.d.ts +2 -6
  119. package/dist/api/GetPassphraseState.d.ts.map +1 -1
  120. package/dist/api/OpenWalletSession.d.ts +8 -0
  121. package/dist/api/OpenWalletSession.d.ts.map +1 -0
  122. package/dist/api/PathInfo.d.ts +1 -0
  123. package/dist/api/PathInfo.d.ts.map +1 -1
  124. package/dist/api/PromptWebDeviceAccess.d.ts.map +1 -1
  125. package/dist/api/SearchDevices.d.ts +3 -0
  126. package/dist/api/SearchDevices.d.ts.map +1 -1
  127. package/dist/api/UploadPortfolio.d.ts +17 -0
  128. package/dist/api/UploadPortfolio.d.ts.map +1 -0
  129. package/dist/api/alephium/AlephiumGetAddress.d.ts.map +1 -1
  130. package/dist/api/alephium/AlephiumSignMessage.d.ts.map +1 -1
  131. package/dist/api/alephium/AlephiumSignTransaction.d.ts.map +1 -1
  132. package/dist/api/algo/AlgoGetAddress.d.ts +1 -0
  133. package/dist/api/algo/AlgoGetAddress.d.ts.map +1 -1
  134. package/dist/api/algo/AlgoSignTransaction.d.ts +1 -0
  135. package/dist/api/algo/AlgoSignTransaction.d.ts.map +1 -1
  136. package/dist/api/allnetwork/AllNetworkGetAddressBase.d.ts +1 -0
  137. package/dist/api/allnetwork/AllNetworkGetAddressBase.d.ts.map +1 -1
  138. package/dist/api/aptos/AptosGetAddress.d.ts +1 -0
  139. package/dist/api/aptos/AptosGetAddress.d.ts.map +1 -1
  140. package/dist/api/aptos/AptosGetPublicKey.d.ts +1 -0
  141. package/dist/api/aptos/AptosGetPublicKey.d.ts.map +1 -1
  142. package/dist/api/aptos/AptosSignInMessage.d.ts +1 -0
  143. package/dist/api/aptos/AptosSignInMessage.d.ts.map +1 -1
  144. package/dist/api/aptos/AptosSignMessage.d.ts +1 -0
  145. package/dist/api/aptos/AptosSignMessage.d.ts.map +1 -1
  146. package/dist/api/aptos/AptosSignTransaction.d.ts +1 -0
  147. package/dist/api/aptos/AptosSignTransaction.d.ts.map +1 -1
  148. package/dist/api/benfen/BenfenGetAddress.d.ts.map +1 -1
  149. package/dist/api/benfen/BenfenGetPublicKey.d.ts.map +1 -1
  150. package/dist/api/benfen/BenfenSignMessage.d.ts.map +1 -1
  151. package/dist/api/benfen/BenfenSignTransaction.d.ts.map +1 -1
  152. package/dist/api/btc/BTCGetAddress.d.ts +1 -0
  153. package/dist/api/btc/BTCGetAddress.d.ts.map +1 -1
  154. package/dist/api/btc/BTCGetPublicKey.d.ts +1 -0
  155. package/dist/api/btc/BTCGetPublicKey.d.ts.map +1 -1
  156. package/dist/api/btc/BTCSignMessage.d.ts +1 -0
  157. package/dist/api/btc/BTCSignMessage.d.ts.map +1 -1
  158. package/dist/api/btc/BTCSignPsbt.d.ts +1 -0
  159. package/dist/api/btc/BTCSignPsbt.d.ts.map +1 -1
  160. package/dist/api/btc/BTCSignTransaction.d.ts +1 -0
  161. package/dist/api/btc/BTCSignTransaction.d.ts.map +1 -1
  162. package/dist/api/btc/BTCVerifyMessage.d.ts +1 -0
  163. package/dist/api/btc/BTCVerifyMessage.d.ts.map +1 -1
  164. package/dist/api/btc/helpers/versionLimit.d.ts +2 -0
  165. package/dist/api/btc/helpers/versionLimit.d.ts.map +1 -1
  166. package/dist/api/cardano/CardanoGetAddress.d.ts +1 -0
  167. package/dist/api/cardano/CardanoGetAddress.d.ts.map +1 -1
  168. package/dist/api/cardano/CardanoGetPublicKey.d.ts +1 -0
  169. package/dist/api/cardano/CardanoGetPublicKey.d.ts.map +1 -1
  170. package/dist/api/cardano/CardanoSignMessage.d.ts +1 -0
  171. package/dist/api/cardano/CardanoSignMessage.d.ts.map +1 -1
  172. package/dist/api/cardano/CardanoSignTransaction.d.ts +1 -0
  173. package/dist/api/cardano/CardanoSignTransaction.d.ts.map +1 -1
  174. package/dist/api/conflux/ConfluxGetAddress.d.ts +2 -0
  175. package/dist/api/conflux/ConfluxGetAddress.d.ts.map +1 -1
  176. package/dist/api/conflux/ConfluxSignMessage.d.ts +2 -0
  177. package/dist/api/conflux/ConfluxSignMessage.d.ts.map +1 -1
  178. package/dist/api/conflux/ConfluxSignMessageCIP23.d.ts +2 -0
  179. package/dist/api/conflux/ConfluxSignMessageCIP23.d.ts.map +1 -1
  180. package/dist/api/conflux/ConfluxSignTransaction.d.ts +2 -0
  181. package/dist/api/conflux/ConfluxSignTransaction.d.ts.map +1 -1
  182. package/dist/api/cosmos/CosmosGetAddress.d.ts +1 -0
  183. package/dist/api/cosmos/CosmosGetAddress.d.ts.map +1 -1
  184. package/dist/api/cosmos/CosmosGetPublicKey.d.ts +1 -0
  185. package/dist/api/cosmos/CosmosGetPublicKey.d.ts.map +1 -1
  186. package/dist/api/cosmos/CosmosSignTransaction.d.ts +1 -0
  187. package/dist/api/cosmos/CosmosSignTransaction.d.ts.map +1 -1
  188. package/dist/api/device/DeviceCancel.d.ts +1 -0
  189. package/dist/api/device/DeviceCancel.d.ts.map +1 -1
  190. package/dist/api/device/DeviceChangePin.d.ts +1 -0
  191. package/dist/api/device/DeviceChangePin.d.ts.map +1 -1
  192. package/dist/api/device/DeviceFullyUploadResource.d.ts +1 -2
  193. package/dist/api/device/DeviceFullyUploadResource.d.ts.map +1 -1
  194. package/dist/api/device/DeviceLock.d.ts +1 -0
  195. package/dist/api/device/DeviceLock.d.ts.map +1 -1
  196. package/dist/api/device/DeviceRebootToBoardloader.d.ts +1 -0
  197. package/dist/api/device/DeviceRebootToBoardloader.d.ts.map +1 -1
  198. package/dist/api/device/DeviceRebootToBootloader.d.ts +1 -0
  199. package/dist/api/device/DeviceRebootToBootloader.d.ts.map +1 -1
  200. package/dist/api/device/DeviceSettings.d.ts +1 -0
  201. package/dist/api/device/DeviceSettings.d.ts.map +1 -1
  202. package/dist/api/device/DeviceSupportFeatures.d.ts +1 -0
  203. package/dist/api/device/DeviceSupportFeatures.d.ts.map +1 -1
  204. package/dist/api/device/DeviceUnlock.d.ts +3 -2
  205. package/dist/api/device/DeviceUnlock.d.ts.map +1 -1
  206. package/dist/api/device/DeviceUpdateBootloader.d.ts +5 -1
  207. package/dist/api/device/DeviceUpdateBootloader.d.ts.map +1 -1
  208. package/dist/api/device/DeviceWipe.d.ts +1 -0
  209. package/dist/api/device/DeviceWipe.d.ts.map +1 -1
  210. package/dist/api/device/PreInitialize.d.ts +1 -0
  211. package/dist/api/device/PreInitialize.d.ts.map +1 -1
  212. package/dist/api/dynex/DnxGetAddress.d.ts.map +1 -1
  213. package/dist/api/dynex/DnxSignTransaction.d.ts.map +1 -1
  214. package/dist/api/evm/EVMGetAddress.d.ts +1 -0
  215. package/dist/api/evm/EVMGetAddress.d.ts.map +1 -1
  216. package/dist/api/evm/EVMGetPublicKey.d.ts +1 -0
  217. package/dist/api/evm/EVMGetPublicKey.d.ts.map +1 -1
  218. package/dist/api/evm/EVMSignMessage.d.ts +1 -0
  219. package/dist/api/evm/EVMSignMessage.d.ts.map +1 -1
  220. package/dist/api/evm/EVMSignMessageEIP712.d.ts.map +1 -1
  221. package/dist/api/evm/EVMSignTransaction.d.ts +1 -0
  222. package/dist/api/evm/EVMSignTransaction.d.ts.map +1 -1
  223. package/dist/api/evm/EVMSignTypedData.d.ts +1 -0
  224. package/dist/api/evm/EVMSignTypedData.d.ts.map +1 -1
  225. package/dist/api/evm/EVMVerifyMessage.d.ts +1 -0
  226. package/dist/api/evm/EVMVerifyMessage.d.ts.map +1 -1
  227. package/dist/api/evm/protocol.d.ts +3 -0
  228. package/dist/api/evm/protocol.d.ts.map +1 -0
  229. package/dist/api/filecoin/FilecoinGetAddress.d.ts +1 -0
  230. package/dist/api/filecoin/FilecoinGetAddress.d.ts.map +1 -1
  231. package/dist/api/filecoin/FilecoinSignTransaction.d.ts +1 -0
  232. package/dist/api/filecoin/FilecoinSignTransaction.d.ts.map +1 -1
  233. package/dist/api/firmware/FirmwareArtifactSource.d.ts +25 -0
  234. package/dist/api/firmware/FirmwareArtifactSource.d.ts.map +1 -0
  235. package/dist/api/firmware/FirmwareHostBinding.d.ts +6 -0
  236. package/dist/api/firmware/FirmwareHostBinding.d.ts.map +1 -0
  237. package/dist/api/firmware/FirmwareMemoryHost.d.ts +22 -0
  238. package/dist/api/firmware/FirmwareMemoryHost.d.ts.map +1 -0
  239. package/dist/api/firmware/FirmwarePreparationError.d.ts +3 -0
  240. package/dist/api/firmware/FirmwarePreparationError.d.ts.map +1 -0
  241. package/dist/api/firmware/FirmwareUpdateBaseMethod.d.ts +12 -3
  242. package/dist/api/firmware/FirmwareUpdateBaseMethod.d.ts.map +1 -1
  243. package/dist/api/firmware/FirmwareUpdateCapabilities.d.ts +3 -0
  244. package/dist/api/firmware/FirmwareUpdateCapabilities.d.ts.map +1 -0
  245. package/dist/api/firmware/FirmwareUpdatePlan.d.ts +72 -0
  246. package/dist/api/firmware/FirmwareUpdatePlan.d.ts.map +1 -0
  247. package/dist/api/firmware/FirmwareUpdatePreparedPlan.d.ts +32 -0
  248. package/dist/api/firmware/FirmwareUpdatePreparedPlan.d.ts.map +1 -0
  249. package/dist/api/firmware/getBinary.d.ts +14 -4
  250. package/dist/api/firmware/getBinary.d.ts.map +1 -1
  251. package/dist/api/firmware/protocolV2Release.d.ts +33 -0
  252. package/dist/api/firmware/protocolV2Release.d.ts.map +1 -0
  253. package/dist/api/firmware/updateBootloader.d.ts +2 -0
  254. package/dist/api/firmware/updateBootloader.d.ts.map +1 -1
  255. package/dist/api/firmware/uploadFirmware.d.ts +9 -1
  256. package/dist/api/firmware/uploadFirmware.d.ts.map +1 -1
  257. package/dist/api/helpers/filesystemValidation.d.ts +3 -0
  258. package/dist/api/helpers/filesystemValidation.d.ts.map +1 -1
  259. package/dist/api/helpers/paramsValidator.d.ts +1 -0
  260. package/dist/api/helpers/paramsValidator.d.ts.map +1 -1
  261. package/dist/api/helpers/protocolV2FileWrite.d.ts +43 -0
  262. package/dist/api/helpers/protocolV2FileWrite.d.ts.map +1 -0
  263. package/dist/api/index.d.ts +8 -27
  264. package/dist/api/index.d.ts.map +1 -1
  265. package/dist/api/kaspa/KaspaGetAddress.d.ts +4 -0
  266. package/dist/api/kaspa/KaspaGetAddress.d.ts.map +1 -1
  267. package/dist/api/kaspa/KaspaSignTransaction.d.ts +9 -1
  268. package/dist/api/kaspa/KaspaSignTransaction.d.ts.map +1 -1
  269. package/dist/api/kaspa/helpers/TransferSerialize.d.ts.map +1 -1
  270. package/dist/api/lightning/LnurlAuth.d.ts +0 -4
  271. package/dist/api/lightning/LnurlAuth.d.ts.map +1 -1
  272. package/dist/api/near/NearGetAddress.d.ts +1 -0
  273. package/dist/api/near/NearGetAddress.d.ts.map +1 -1
  274. package/dist/api/near/NearSignTransaction.d.ts +1 -0
  275. package/dist/api/near/NearSignTransaction.d.ts.map +1 -1
  276. package/dist/api/nem/NEMGetAddress.d.ts +1 -0
  277. package/dist/api/nem/NEMGetAddress.d.ts.map +1 -1
  278. package/dist/api/nem/NEMSignTransaction.d.ts +1 -0
  279. package/dist/api/nem/NEMSignTransaction.d.ts.map +1 -1
  280. package/dist/api/neo/NeoGetAddress.d.ts.map +1 -1
  281. package/dist/api/neo/NeoSignTransaction.d.ts.map +1 -1
  282. package/dist/api/nervos/NervosGetAddress.d.ts.map +1 -1
  283. package/dist/api/nervos/NervosSignTransaction.d.ts.map +1 -1
  284. package/dist/api/nexa/NexaGetAddress.d.ts.map +1 -1
  285. package/dist/api/nexa/NexaSignTransaction.d.ts +1 -0
  286. package/dist/api/nexa/NexaSignTransaction.d.ts.map +1 -1
  287. package/dist/api/nostr/NostrDecryptMessage.d.ts +1 -0
  288. package/dist/api/nostr/NostrDecryptMessage.d.ts.map +1 -1
  289. package/dist/api/nostr/NostrEncryptMessage.d.ts +1 -0
  290. package/dist/api/nostr/NostrEncryptMessage.d.ts.map +1 -1
  291. package/dist/api/nostr/NostrGetPublicKey.d.ts +1 -0
  292. package/dist/api/nostr/NostrGetPublicKey.d.ts.map +1 -1
  293. package/dist/api/nostr/NostrSignEvent.d.ts +1 -0
  294. package/dist/api/nostr/NostrSignEvent.d.ts.map +1 -1
  295. package/dist/api/nostr/NostrSignSchnorr.d.ts +1 -0
  296. package/dist/api/nostr/NostrSignSchnorr.d.ts.map +1 -1
  297. package/dist/api/polkadot/PolkadotGetAddress.d.ts +2 -1
  298. package/dist/api/polkadot/PolkadotGetAddress.d.ts.map +1 -1
  299. package/dist/api/polkadot/PolkadotSignTransaction.d.ts +1 -0
  300. package/dist/api/polkadot/PolkadotSignTransaction.d.ts.map +1 -1
  301. package/dist/api/polkadot/networks.d.ts +1 -1
  302. package/dist/api/protocol-v2/DeviceFactoryInfoGet.d.ts +1 -0
  303. package/dist/api/protocol-v2/DeviceFactoryInfoGet.d.ts.map +1 -1
  304. package/dist/api/protocol-v2/DeviceFactoryInfoSet.d.ts +1 -0
  305. package/dist/api/protocol-v2/DeviceFactoryInfoSet.d.ts.map +1 -1
  306. package/dist/api/protocol-v2/DeviceFirmwareUpdate.d.ts +1 -0
  307. package/dist/api/protocol-v2/DeviceFirmwareUpdate.d.ts.map +1 -1
  308. package/dist/api/protocol-v2/DeviceGetFirmwareUpdateStatus.d.ts +1 -0
  309. package/dist/api/protocol-v2/DeviceGetFirmwareUpdateStatus.d.ts.map +1 -1
  310. package/dist/api/protocol-v2/DeviceGetOnboardingStatus.d.ts +7 -0
  311. package/dist/api/protocol-v2/DeviceGetOnboardingStatus.d.ts.map +1 -0
  312. package/dist/api/protocol-v2/DeviceInfoGet.d.ts +1 -1
  313. package/dist/api/protocol-v2/DeviceInfoGet.d.ts.map +1 -1
  314. package/dist/api/protocol-v2/DeviceReboot.d.ts +1 -0
  315. package/dist/api/protocol-v2/DeviceReboot.d.ts.map +1 -1
  316. package/dist/api/protocol-v2/DeviceStatusGet.d.ts +1 -0
  317. package/dist/api/protocol-v2/DeviceStatusGet.d.ts.map +1 -1
  318. package/dist/api/protocol-v2/DeviceUploadNft.d.ts +31 -0
  319. package/dist/api/protocol-v2/DeviceUploadNft.d.ts.map +1 -0
  320. package/dist/api/protocol-v2/DeviceUploadWallpaper.d.ts +27 -0
  321. package/dist/api/protocol-v2/DeviceUploadWallpaper.d.ts.map +1 -0
  322. package/dist/api/protocol-v2/FilesystemFormat.d.ts +1 -0
  323. package/dist/api/protocol-v2/FilesystemFormat.d.ts.map +1 -1
  324. package/dist/api/protocol-v2/FilesystemPermissionFix.d.ts +1 -0
  325. package/dist/api/protocol-v2/FilesystemPermissionFix.d.ts.map +1 -1
  326. package/dist/api/protocol-v2/Ping.d.ts +3 -0
  327. package/dist/api/protocol-v2/Ping.d.ts.map +1 -1
  328. package/dist/api/protocol-v2/ProtocolInfoRequest.d.ts +1 -0
  329. package/dist/api/protocol-v2/ProtocolInfoRequest.d.ts.map +1 -1
  330. package/dist/api/protocol-v2/helpers.d.ts.map +1 -1
  331. package/dist/api/scdo/ScdoGetAddress.d.ts.map +1 -1
  332. package/dist/api/scdo/ScdoSignMessage.d.ts.map +1 -1
  333. package/dist/api/scdo/ScdoSignTransaction.d.ts.map +1 -1
  334. package/dist/api/solana/SolGetAddress.d.ts +1 -0
  335. package/dist/api/solana/SolGetAddress.d.ts.map +1 -1
  336. package/dist/api/solana/SolSignMessage.d.ts +2 -1
  337. package/dist/api/solana/SolSignMessage.d.ts.map +1 -1
  338. package/dist/api/solana/SolSignOffchainMessage.d.ts +2 -1
  339. package/dist/api/solana/SolSignOffchainMessage.d.ts.map +1 -1
  340. package/dist/api/solana/SolSignTransaction.d.ts +3 -2
  341. package/dist/api/solana/SolSignTransaction.d.ts.map +1 -1
  342. package/dist/api/starcoin/StarcoinGetAddress.d.ts +1 -0
  343. package/dist/api/starcoin/StarcoinGetAddress.d.ts.map +1 -1
  344. package/dist/api/starcoin/StarcoinGetPublicKey.d.ts +1 -0
  345. package/dist/api/starcoin/StarcoinGetPublicKey.d.ts.map +1 -1
  346. package/dist/api/starcoin/StarcoinSignMessage.d.ts +1 -0
  347. package/dist/api/starcoin/StarcoinSignMessage.d.ts.map +1 -1
  348. package/dist/api/starcoin/StarcoinSignTransaction.d.ts +1 -0
  349. package/dist/api/starcoin/StarcoinSignTransaction.d.ts.map +1 -1
  350. package/dist/api/starcoin/StarcoinVerifyMessage.d.ts +1 -0
  351. package/dist/api/starcoin/StarcoinVerifyMessage.d.ts.map +1 -1
  352. package/dist/api/stellar/StellarGetAddress.d.ts +1 -2
  353. package/dist/api/stellar/StellarGetAddress.d.ts.map +1 -1
  354. package/dist/api/stellar/StellarSignTransaction.d.ts +2 -3
  355. package/dist/api/stellar/StellarSignTransaction.d.ts.map +1 -1
  356. package/dist/api/sui/SuiGetAddress.d.ts +2 -1
  357. package/dist/api/sui/SuiGetAddress.d.ts.map +1 -1
  358. package/dist/api/sui/SuiGetPublicKey.d.ts +2 -1
  359. package/dist/api/sui/SuiGetPublicKey.d.ts.map +1 -1
  360. package/dist/api/sui/SuiSignMessage.d.ts +2 -1
  361. package/dist/api/sui/SuiSignMessage.d.ts.map +1 -1
  362. package/dist/api/sui/SuiSignTransaction.d.ts +2 -1
  363. package/dist/api/sui/SuiSignTransaction.d.ts.map +1 -1
  364. package/dist/api/ton/TonGetAddress.d.ts +2 -1
  365. package/dist/api/ton/TonGetAddress.d.ts.map +1 -1
  366. package/dist/api/ton/TonSignData.d.ts +1 -0
  367. package/dist/api/ton/TonSignData.d.ts.map +1 -1
  368. package/dist/api/ton/TonSignMessage.d.ts +2 -1
  369. package/dist/api/ton/TonSignMessage.d.ts.map +1 -1
  370. package/dist/api/ton/TonSignProof.d.ts +2 -1
  371. package/dist/api/ton/TonSignProof.d.ts.map +1 -1
  372. package/dist/api/tron/TronGetAddress.d.ts +1 -0
  373. package/dist/api/tron/TronGetAddress.d.ts.map +1 -1
  374. package/dist/api/tron/TronSignMessage.d.ts +6 -1
  375. package/dist/api/tron/TronSignMessage.d.ts.map +1 -1
  376. package/dist/api/tron/TronSignTransaction.d.ts +2 -1
  377. package/dist/api/tron/TronSignTransaction.d.ts.map +1 -1
  378. package/dist/api/utils.d.ts +4 -1
  379. package/dist/api/utils.d.ts.map +1 -1
  380. package/dist/api/xrp/XrpGetAddress.d.ts +1 -0
  381. package/dist/api/xrp/XrpGetAddress.d.ts.map +1 -1
  382. package/dist/api/xrp/XrpSignTransaction.d.ts +1 -0
  383. package/dist/api/xrp/XrpSignTransaction.d.ts.map +1 -1
  384. package/dist/constants/index.d.ts +1 -1
  385. package/dist/constants/index.d.ts.map +1 -1
  386. package/dist/core/RequestQueue.d.ts.map +1 -1
  387. package/dist/core/deviceEventRegistration.d.ts +13 -0
  388. package/dist/core/deviceEventRegistration.d.ts.map +1 -0
  389. package/dist/core/index.d.ts +3 -1
  390. package/dist/core/index.d.ts.map +1 -1
  391. package/dist/core/uiPromiseRegistry.d.ts +6 -0
  392. package/dist/core/uiPromiseRegistry.d.ts.map +1 -0
  393. package/dist/data-manager/DataManager.d.ts +11 -3
  394. package/dist/data-manager/DataManager.d.ts.map +1 -1
  395. package/dist/data-manager/TransportManager.d.ts.map +1 -1
  396. package/dist/data-manager/connectSettings.d.ts.map +1 -1
  397. package/dist/device/Device.d.ts +71 -16
  398. package/dist/device/Device.d.ts.map +1 -1
  399. package/dist/device/DeviceCommands.d.ts +8 -6
  400. package/dist/device/DeviceCommands.d.ts.map +1 -1
  401. package/dist/device/DeviceConnector.d.ts +4 -3
  402. package/dist/device/DeviceConnector.d.ts.map +1 -1
  403. package/dist/device/DeviceFeaturesState.d.ts +3 -0
  404. package/dist/device/DeviceFeaturesState.d.ts.map +1 -0
  405. package/dist/device/DevicePool.d.ts +6 -2
  406. package/dist/device/DevicePool.d.ts.map +1 -1
  407. package/dist/device/DeviceStateMapper.d.ts +13 -0
  408. package/dist/device/DeviceStateMapper.d.ts.map +1 -0
  409. package/dist/device/DeviceStateProjector.d.ts +7 -0
  410. package/dist/device/DeviceStateProjector.d.ts.map +1 -0
  411. package/dist/device/DeviceStateStore.d.ts +21 -0
  412. package/dist/device/DeviceStateStore.d.ts.map +1 -0
  413. package/dist/device/DeviceWalletSessionStore.d.ts +13 -1
  414. package/dist/device/DeviceWalletSessionStore.d.ts.map +1 -1
  415. package/dist/device/cloneDeviceState.d.ts +2 -0
  416. package/dist/device/cloneDeviceState.d.ts.map +1 -0
  417. package/dist/device/deviceIdentity.d.ts +5 -0
  418. package/dist/device/deviceIdentity.d.ts.map +1 -0
  419. package/dist/deviceProfile/buildDeviceFeatures.d.ts +9 -3
  420. package/dist/deviceProfile/buildDeviceFeatures.d.ts.map +1 -1
  421. package/dist/deviceProfile/index.d.ts +0 -1
  422. package/dist/deviceProfile/index.d.ts.map +1 -1
  423. package/dist/deviceProfile/protocolV2DeviceIdentity.d.ts +7 -0
  424. package/dist/deviceProfile/protocolV2DeviceIdentity.d.ts.map +1 -0
  425. package/dist/events/device.d.ts +16 -2
  426. package/dist/events/device.d.ts.map +1 -1
  427. package/dist/events/logBlockEvent.d.ts +2 -0
  428. package/dist/events/logBlockEvent.d.ts.map +1 -1
  429. package/dist/events/ui-promise.d.ts +4 -2
  430. package/dist/events/ui-promise.d.ts.map +1 -1
  431. package/dist/events/ui-request.d.ts +50 -4
  432. package/dist/events/ui-request.d.ts.map +1 -1
  433. package/dist/events/ui-response.d.ts +10 -2
  434. package/dist/events/ui-response.d.ts.map +1 -1
  435. package/dist/index.d.ts +1786 -776
  436. package/dist/index.d.ts.map +1 -1
  437. package/dist/index.js +11556 -4498
  438. package/dist/inject.d.ts +6 -1
  439. package/dist/inject.d.ts.map +1 -1
  440. package/dist/lowLevelInject.d.ts.map +1 -1
  441. package/dist/protocols/protocol-v2/deviceSession.d.ts +7 -0
  442. package/dist/protocols/protocol-v2/deviceSession.d.ts.map +1 -0
  443. package/dist/protocols/protocol-v2/features.d.ts +21 -17
  444. package/dist/protocols/protocol-v2/features.d.ts.map +1 -1
  445. package/dist/protocols/protocol-v2/firmware.d.ts.map +1 -1
  446. package/dist/protocols/protocol-v2/index.d.ts +2 -2
  447. package/dist/protocols/protocol-v2/index.d.ts.map +1 -1
  448. package/dist/protocols/protocol-v2/resources.d.ts +12 -0
  449. package/dist/protocols/protocol-v2/resources.d.ts.map +1 -0
  450. package/dist/protocols/protocol-v2/settingsUnlockPolicy.d.ts +17 -0
  451. package/dist/protocols/protocol-v2/settingsUnlockPolicy.d.ts.map +1 -0
  452. package/dist/protocols/protocol-v2/uiInteraction.d.ts +42 -0
  453. package/dist/protocols/protocol-v2/uiInteraction.d.ts.map +1 -0
  454. package/dist/protocols/protocol-v2/unlockPolicy.d.ts +2 -0
  455. package/dist/protocols/protocol-v2/unlockPolicy.d.ts.map +1 -0
  456. package/dist/protocols/protocol-v2/unlockPolicyRunner.d.ts +22 -0
  457. package/dist/protocols/protocol-v2/unlockPolicyRunner.d.ts.map +1 -0
  458. package/dist/protocols/protocol-v2/walletSession.d.ts +18 -4
  459. package/dist/protocols/protocol-v2/walletSession.d.ts.map +1 -1
  460. package/dist/topLevelInject.d.ts.map +1 -1
  461. package/dist/types/api/checkAllFirmwareRelease.d.ts +48 -9
  462. package/dist/types/api/checkAllFirmwareRelease.d.ts.map +1 -1
  463. package/dist/types/api/checkBLEFirmwareRelease.d.ts +2 -13
  464. package/dist/types/api/checkBLEFirmwareRelease.d.ts.map +1 -1
  465. package/dist/types/api/checkBootloaderRelease.d.ts +2 -6
  466. package/dist/types/api/checkBootloaderRelease.d.ts.map +1 -1
  467. package/dist/types/api/checkFirmwareRelease.d.ts +2 -13
  468. package/dist/types/api/checkFirmwareRelease.d.ts.map +1 -1
  469. package/dist/types/api/checkFirmwareTypeAvailable.d.ts +2 -2
  470. package/dist/types/api/checkFirmwareTypeAvailable.d.ts.map +1 -1
  471. package/dist/types/api/detectDeviceConnectProtocol.d.ts +4 -0
  472. package/dist/types/api/detectDeviceConnectProtocol.d.ts.map +1 -0
  473. package/dist/types/api/deviceSettings.d.ts +11 -2
  474. package/dist/types/api/deviceSettings.d.ts.map +1 -1
  475. package/dist/types/api/deviceUnlock.d.ts +6 -2
  476. package/dist/types/api/deviceUnlock.d.ts.map +1 -1
  477. package/dist/types/api/deviceUpdateBootloader.d.ts +6 -0
  478. package/dist/types/api/deviceUpdateBootloader.d.ts.map +1 -1
  479. package/dist/types/api/export.d.ts +8 -3
  480. package/dist/types/api/export.d.ts.map +1 -1
  481. package/dist/types/api/firmwareUpdate.d.ts +76 -2
  482. package/dist/types/api/firmwareUpdate.d.ts.map +1 -1
  483. package/dist/types/api/firmwareUpdateCapabilities.d.ts +9 -0
  484. package/dist/types/api/firmwareUpdateCapabilities.d.ts.map +1 -0
  485. package/dist/types/api/firmwareUpdatePlan.d.ts +28 -0
  486. package/dist/types/api/firmwareUpdatePlan.d.ts.map +1 -0
  487. package/dist/types/api/firmwareUpdatePreparedPlan.d.ts +42 -0
  488. package/dist/types/api/firmwareUpdatePreparedPlan.d.ts.map +1 -0
  489. package/dist/types/api/getDeviceState.d.ts +12 -0
  490. package/dist/types/api/getDeviceState.d.ts.map +1 -0
  491. package/dist/types/api/getFeatures.d.ts.map +1 -1
  492. package/dist/types/api/getOnekeyFeatures.d.ts.map +1 -1
  493. package/dist/types/api/getPassphraseState.d.ts +2 -10
  494. package/dist/types/api/getPassphraseState.d.ts.map +1 -1
  495. package/dist/types/api/index.d.ts +29 -33
  496. package/dist/types/api/index.d.ts.map +1 -1
  497. package/dist/types/api/kaspaSignTransaction.d.ts +42 -4
  498. package/dist/types/api/kaspaSignTransaction.d.ts.map +1 -1
  499. package/dist/types/api/openWalletSession.d.ts +35 -0
  500. package/dist/types/api/openWalletSession.d.ts.map +1 -0
  501. package/dist/types/api/protocolV2.d.ts +18 -88
  502. package/dist/types/api/protocolV2.d.ts.map +1 -1
  503. package/dist/types/api/sessionCache.d.ts +4 -1
  504. package/dist/types/api/sessionCache.d.ts.map +1 -1
  505. package/dist/types/device.d.ts +142 -10
  506. package/dist/types/device.d.ts.map +1 -1
  507. package/dist/types/params.d.ts +1 -0
  508. package/dist/types/params.d.ts.map +1 -1
  509. package/dist/types/settings.d.ts +59 -16
  510. package/dist/types/settings.d.ts.map +1 -1
  511. package/dist/utils/assets.d.ts +2 -1
  512. package/dist/utils/assets.d.ts.map +1 -1
  513. package/dist/utils/deviceFeaturesCompat.d.ts +14 -0
  514. package/dist/utils/deviceFeaturesCompat.d.ts.map +1 -0
  515. package/dist/utils/deviceFeaturesUtils.d.ts +10 -3
  516. package/dist/utils/deviceFeaturesUtils.d.ts.map +1 -1
  517. package/dist/utils/deviceInfoUtils.d.ts +9 -9
  518. package/dist/utils/deviceInfoUtils.d.ts.map +1 -1
  519. package/dist/utils/deviceSettings.d.ts +38 -7
  520. package/dist/utils/deviceSettings.d.ts.map +1 -1
  521. package/dist/utils/deviceVersionUtils.d.ts +7 -5
  522. package/dist/utils/deviceVersionUtils.d.ts.map +1 -1
  523. package/dist/utils/homescreen.d.ts +4 -0
  524. package/dist/utils/homescreen.d.ts.map +1 -1
  525. package/dist/utils/index.d.ts +6 -3
  526. package/dist/utils/index.d.ts.map +1 -1
  527. package/dist/utils/networkUtils.d.ts +10 -1
  528. package/dist/utils/networkUtils.d.ts.map +1 -1
  529. package/dist/utils/patch.d.ts +1 -1
  530. package/dist/utils/patch.d.ts.map +1 -1
  531. package/dist/utils/pro2Nft.d.ts +31 -0
  532. package/dist/utils/pro2Nft.d.ts.map +1 -0
  533. package/dist/utils/pro2Wallpaper.d.ts +22 -0
  534. package/dist/utils/pro2Wallpaper.d.ts.map +1 -0
  535. package/dist/utils/tracing.d.ts.map +1 -1
  536. package/dist/utils/uiProgressThrottle.d.ts +3 -0
  537. package/dist/utils/uiProgressThrottle.d.ts.map +1 -0
  538. package/package.json +4 -4
  539. package/src/api/BaseMethod.ts +53 -20
  540. package/src/api/CheckAllFirmwareRelease.ts +437 -4
  541. package/src/api/CheckBLEFirmwareRelease.ts +37 -5
  542. package/src/api/CheckBootloaderRelease.ts +36 -5
  543. package/src/api/CheckFirmwareRelease.ts +35 -8
  544. package/src/api/CipherKeyValue.ts +4 -0
  545. package/src/api/ClearSessionCache.ts +4 -0
  546. package/src/api/DetectDeviceConnectProtocol.ts +18 -0
  547. package/src/api/DirList.ts +9 -4
  548. package/src/api/DirMake.ts +7 -4
  549. package/src/api/DirRemove.ts +7 -4
  550. package/src/api/FileDelete.ts +7 -4
  551. package/src/api/FileRead.ts +13 -7
  552. package/src/api/FileWrite.ts +26 -181
  553. package/src/api/FirmwareUpdate.ts +30 -15
  554. package/src/api/FirmwareUpdateV2.ts +390 -115
  555. package/src/api/FirmwareUpdateV3.ts +281 -71
  556. package/src/api/FirmwareUpdateV4.ts +2005 -732
  557. package/src/api/GetDeviceState.ts +59 -0
  558. package/src/api/GetFeatures.ts +6 -4
  559. package/src/api/GetOnekeyFeatures.ts +1 -75
  560. package/src/api/GetPassphraseState.ts +36 -21
  561. package/src/api/OpenWalletSession.ts +268 -0
  562. package/src/api/PathInfo.ts +9 -4
  563. package/src/api/PromptWebDeviceAccess.ts +2 -7
  564. package/src/api/SearchDevices.ts +31 -4
  565. package/src/api/UploadPortfolio.ts +58 -0
  566. package/src/api/alephium/AlephiumGetAddress.ts +0 -4
  567. package/src/api/alephium/AlephiumSignMessage.ts +0 -4
  568. package/src/api/alephium/AlephiumSignTransaction.ts +2 -6
  569. package/src/api/algo/AlgoGetAddress.ts +4 -0
  570. package/src/api/algo/AlgoSignTransaction.ts +4 -0
  571. package/src/api/allnetwork/AllNetworkGetAddress.ts +21 -21
  572. package/src/api/allnetwork/AllNetworkGetAddressBase.ts +45 -32
  573. package/src/api/aptos/AptosGetAddress.ts +4 -0
  574. package/src/api/aptos/AptosGetPublicKey.ts +4 -0
  575. package/src/api/aptos/AptosSignInMessage.ts +4 -0
  576. package/src/api/aptos/AptosSignMessage.ts +4 -0
  577. package/src/api/aptos/AptosSignTransaction.ts +4 -0
  578. package/src/api/benfen/BenfenGetAddress.ts +11 -7
  579. package/src/api/benfen/BenfenGetPublicKey.ts +0 -4
  580. package/src/api/benfen/BenfenSignMessage.ts +0 -4
  581. package/src/api/benfen/BenfenSignTransaction.ts +0 -4
  582. package/src/api/btc/BTCGetAddress.ts +8 -1
  583. package/src/api/btc/BTCGetPublicKey.ts +9 -2
  584. package/src/api/btc/BTCSignMessage.ts +8 -1
  585. package/src/api/btc/BTCSignPsbt.ts +4 -0
  586. package/src/api/btc/BTCSignTransaction.ts +8 -1
  587. package/src/api/btc/BTCVerifyMessage.ts +8 -1
  588. package/src/api/btc/helpers/versionLimit.ts +7 -4
  589. package/src/api/cardano/CardanoGetAddress.ts +4 -0
  590. package/src/api/cardano/CardanoGetPublicKey.ts +4 -0
  591. package/src/api/cardano/CardanoSignMessage.ts +5 -1
  592. package/src/api/cardano/CardanoSignTransaction.ts +5 -1
  593. package/src/api/conflux/ConfluxGetAddress.ts +6 -0
  594. package/src/api/conflux/ConfluxSignMessage.ts +6 -0
  595. package/src/api/conflux/ConfluxSignMessageCIP23.ts +6 -0
  596. package/src/api/conflux/ConfluxSignTransaction.ts +8 -5
  597. package/src/api/cosmos/CosmosGetAddress.ts +4 -0
  598. package/src/api/cosmos/CosmosGetPublicKey.ts +4 -0
  599. package/src/api/cosmos/CosmosSignTransaction.ts +4 -0
  600. package/src/api/device/DeviceCancel.ts +5 -0
  601. package/src/api/device/DeviceChangePin.ts +28 -0
  602. package/src/api/device/DeviceFullyUploadResource.ts +4 -5
  603. package/src/api/device/DeviceLock.ts +5 -0
  604. package/src/api/device/DeviceRebootToBoardloader.ts +5 -0
  605. package/src/api/device/DeviceRebootToBootloader.ts +5 -0
  606. package/src/api/device/DeviceSettings.ts +195 -1
  607. package/src/api/device/DeviceSupportFeatures.ts +4 -0
  608. package/src/api/device/DeviceUnlock.ts +21 -3
  609. package/src/api/device/DeviceUpdateBootloader.ts +133 -8
  610. package/src/api/device/DeviceWipe.ts +25 -0
  611. package/src/api/device/PreInitialize.ts +4 -0
  612. package/src/api/dynex/DnxGetAddress.ts +0 -6
  613. package/src/api/dynex/DnxSignTransaction.ts +0 -6
  614. package/src/api/evm/EVMGetAddress.ts +6 -2
  615. package/src/api/evm/EVMGetPublicKey.ts +6 -2
  616. package/src/api/evm/EVMSignMessage.ts +6 -2
  617. package/src/api/evm/EVMSignMessageEIP712.ts +1 -11
  618. package/src/api/evm/EVMSignTransaction.ts +6 -2
  619. package/src/api/evm/EVMSignTypedData.ts +42 -50
  620. package/src/api/evm/EVMVerifyMessage.ts +6 -2
  621. package/src/api/evm/legacyV1/getAddress.ts +2 -2
  622. package/src/api/evm/legacyV1/getPublicKey.ts +2 -2
  623. package/src/api/evm/legacyV1/signMessage.ts +2 -2
  624. package/src/api/evm/legacyV1/signTypedData.ts +2 -2
  625. package/src/api/evm/legacyV1/signTypedHash.ts +2 -2
  626. package/src/api/evm/legacyV1/verifyMessage.ts +2 -2
  627. package/src/api/evm/protocol.ts +6 -0
  628. package/src/api/filecoin/FilecoinGetAddress.ts +4 -0
  629. package/src/api/filecoin/FilecoinSignTransaction.ts +4 -0
  630. package/src/api/firmware/FirmwareArtifactSource.ts +278 -0
  631. package/src/api/firmware/FirmwareHostBinding.ts +113 -0
  632. package/src/api/firmware/FirmwareMemoryHost.ts +142 -0
  633. package/src/api/firmware/FirmwarePreparationError.ts +12 -0
  634. package/src/api/firmware/FirmwareUpdateBaseMethod.ts +102 -16
  635. package/src/api/firmware/FirmwareUpdateCapabilities.ts +9 -0
  636. package/src/api/firmware/FirmwareUpdatePlan.ts +910 -0
  637. package/src/api/firmware/FirmwareUpdatePreparedPlan.ts +456 -0
  638. package/src/api/firmware/getBinary.ts +8 -3
  639. package/src/api/firmware/protocolV2Release.ts +168 -0
  640. package/src/api/firmware/updateBootloader.ts +19 -4
  641. package/src/api/firmware/uploadFirmware.ts +157 -26
  642. package/src/api/helpers/batchGetPublickeys.ts +2 -2
  643. package/src/api/helpers/filesystemValidation.ts +61 -0
  644. package/src/api/helpers/paramsValidator.ts +1 -1
  645. package/src/api/helpers/protocolV2FileWrite.ts +392 -0
  646. package/src/api/index.ts +9 -28
  647. package/src/api/kaspa/KaspaGetAddress.ts +7 -0
  648. package/src/api/kaspa/KaspaSignTransaction.ts +369 -29
  649. package/src/api/kaspa/helpers/TransferSerialize.ts +17 -7
  650. package/src/api/lightning/LnurlAuth.ts +0 -4
  651. package/src/api/near/NearGetAddress.ts +4 -0
  652. package/src/api/near/NearSignTransaction.ts +4 -0
  653. package/src/api/nem/NEMGetAddress.ts +4 -0
  654. package/src/api/nem/NEMSignTransaction.ts +4 -0
  655. package/src/api/neo/NeoGetAddress.ts +0 -4
  656. package/src/api/neo/NeoSignTransaction.ts +0 -4
  657. package/src/api/nervos/NervosGetAddress.ts +0 -4
  658. package/src/api/nervos/NervosSignTransaction.ts +2 -6
  659. package/src/api/nexa/NexaGetAddress.ts +0 -4
  660. package/src/api/nexa/NexaSignTransaction.ts +3 -5
  661. package/src/api/nostr/NostrDecryptMessage.ts +4 -0
  662. package/src/api/nostr/NostrEncryptMessage.ts +4 -0
  663. package/src/api/nostr/NostrGetPublicKey.ts +4 -0
  664. package/src/api/nostr/NostrSignEvent.ts +4 -0
  665. package/src/api/nostr/NostrSignSchnorr.ts +4 -0
  666. package/src/api/polkadot/PolkadotGetAddress.ts +4 -0
  667. package/src/api/polkadot/PolkadotSignTransaction.ts +4 -0
  668. package/src/api/polkadot/networks.ts +3 -3
  669. package/src/api/protocol-v2/DeviceFactoryInfoGet.ts +5 -2
  670. package/src/api/protocol-v2/DeviceFactoryInfoSet.ts +5 -2
  671. package/src/api/protocol-v2/DeviceFirmwareUpdate.ts +8 -5
  672. package/src/api/protocol-v2/DeviceGetFirmwareUpdateStatus.ts +10 -6
  673. package/src/api/protocol-v2/DeviceGetOnboardingStatus.ts +21 -0
  674. package/src/api/protocol-v2/DeviceInfoGet.ts +8 -15
  675. package/src/api/protocol-v2/DeviceReboot.ts +7 -3
  676. package/src/api/protocol-v2/DeviceStatusGet.ts +6 -1
  677. package/src/api/protocol-v2/DeviceUploadNft.ts +197 -0
  678. package/src/api/protocol-v2/DeviceUploadWallpaper.ts +134 -0
  679. package/src/api/protocol-v2/FilesystemFormat.ts +5 -2
  680. package/src/api/protocol-v2/FilesystemPermissionFix.ts +5 -2
  681. package/src/api/protocol-v2/Ping.ts +37 -3
  682. package/src/api/protocol-v2/ProtocolInfoRequest.ts +8 -3
  683. package/src/api/protocol-v2/helpers.ts +1 -4
  684. package/src/api/scdo/ScdoGetAddress.ts +0 -4
  685. package/src/api/scdo/ScdoSignMessage.ts +0 -4
  686. package/src/api/scdo/ScdoSignTransaction.ts +0 -4
  687. package/src/api/solana/SolGetAddress.ts +4 -0
  688. package/src/api/solana/SolSignMessage.ts +5 -1
  689. package/src/api/solana/SolSignOffchainMessage.ts +5 -1
  690. package/src/api/solana/SolSignTransaction.ts +6 -2
  691. package/src/api/starcoin/StarcoinGetAddress.ts +4 -0
  692. package/src/api/starcoin/StarcoinGetPublicKey.ts +4 -0
  693. package/src/api/starcoin/StarcoinSignMessage.ts +4 -0
  694. package/src/api/starcoin/StarcoinSignTransaction.ts +4 -0
  695. package/src/api/starcoin/StarcoinVerifyMessage.ts +4 -0
  696. package/src/api/stellar/StellarGetAddress.ts +1 -10
  697. package/src/api/stellar/StellarSignTransaction.ts +1 -14
  698. package/src/api/sui/SuiGetAddress.ts +5 -1
  699. package/src/api/sui/SuiGetPublicKey.ts +5 -1
  700. package/src/api/sui/SuiSignMessage.ts +5 -1
  701. package/src/api/sui/SuiSignTransaction.ts +5 -1
  702. package/src/api/ton/TonGetAddress.ts +5 -1
  703. package/src/api/ton/TonSignData.ts +5 -1
  704. package/src/api/ton/TonSignMessage.ts +5 -1
  705. package/src/api/ton/TonSignProof.ts +5 -1
  706. package/src/api/tron/TronGetAddress.ts +4 -0
  707. package/src/api/tron/TronSignMessage.ts +19 -8
  708. package/src/api/tron/TronSignTransaction.ts +5 -1
  709. package/src/api/utils.ts +32 -3
  710. package/src/api/xrp/XrpGetAddress.ts +4 -0
  711. package/src/api/xrp/XrpSignTransaction.ts +5 -1
  712. package/src/constants/index.ts +1 -4
  713. package/src/core/RequestQueue.ts +1 -0
  714. package/src/core/deviceEventRegistration.ts +27 -0
  715. package/src/core/index.ts +381 -152
  716. package/src/core/uiPromiseRegistry.ts +63 -0
  717. package/src/data/messages/messages-protocol-v2.json +1113 -547
  718. package/src/data/messages/messages.json +286 -9
  719. package/src/data-manager/DataManager.ts +154 -43
  720. package/src/data-manager/TransportManager.ts +7 -1
  721. package/src/data-manager/connectSettings.ts +11 -0
  722. package/src/device/Device.ts +858 -264
  723. package/src/device/DeviceCommands.ts +193 -158
  724. package/src/device/DeviceConnector.ts +44 -21
  725. package/src/device/DeviceFeaturesState.ts +25 -0
  726. package/src/device/DevicePool.ts +101 -23
  727. package/src/device/DeviceStateMapper.ts +416 -0
  728. package/src/device/DeviceStateProjector.ts +152 -0
  729. package/src/device/DeviceStateStore.ts +232 -0
  730. package/src/device/DeviceWalletSessionStore.ts +72 -6
  731. package/src/device/cloneDeviceState.ts +39 -0
  732. package/src/device/deviceIdentity.ts +18 -0
  733. package/src/deviceProfile/buildDeviceFeatures.ts +223 -189
  734. package/src/deviceProfile/index.ts +0 -1
  735. package/src/deviceProfile/protocolV2DeviceIdentity.ts +35 -0
  736. package/src/events/device.ts +22 -1
  737. package/src/events/logBlockEvent.ts +80 -2
  738. package/src/events/ui-promise.ts +4 -2
  739. package/src/events/ui-request.ts +61 -4
  740. package/src/events/ui-response.ts +12 -2
  741. package/src/index.ts +17 -0
  742. package/src/inject.ts +94 -47
  743. package/src/lowLevelInject.ts +11 -4
  744. package/src/protocols/protocol-v2/deviceSession.ts +24 -0
  745. package/src/protocols/protocol-v2/features.ts +100 -33
  746. package/src/protocols/protocol-v2/firmware.ts +3 -1
  747. package/src/protocols/protocol-v2/index.ts +7 -1
  748. package/src/protocols/protocol-v2/resources.ts +210 -0
  749. package/src/protocols/protocol-v2/settingsUnlockPolicy.ts +65 -0
  750. package/src/protocols/protocol-v2/uiInteraction.ts +250 -0
  751. package/src/protocols/protocol-v2/unlockPolicy.ts +1 -0
  752. package/src/protocols/protocol-v2/unlockPolicyRunner.ts +131 -0
  753. package/src/protocols/protocol-v2/walletSession.ts +422 -42
  754. package/src/topLevelInject.ts +11 -4
  755. package/src/types/api/checkAllFirmwareRelease.ts +72 -9
  756. package/src/types/api/checkBLEFirmwareRelease.ts +4 -13
  757. package/src/types/api/checkBootloaderRelease.ts +2 -6
  758. package/src/types/api/checkFirmwareRelease.ts +2 -13
  759. package/src/types/api/checkFirmwareTypeAvailable.ts +2 -2
  760. package/src/types/api/detectDeviceConnectProtocol.ts +6 -0
  761. package/src/types/api/deviceSettings.ts +26 -2
  762. package/src/types/api/deviceUnlock.ts +13 -2
  763. package/src/types/api/deviceUpdateBootloader.ts +6 -0
  764. package/src/types/api/export.ts +33 -1
  765. package/src/types/api/firmwareUpdate.ts +105 -9
  766. package/src/types/api/firmwareUpdateCapabilities.ts +9 -0
  767. package/src/types/api/firmwareUpdatePlan.ts +38 -0
  768. package/src/types/api/firmwareUpdatePreparedPlan.ts +53 -0
  769. package/src/types/api/getDeviceState.ts +19 -0
  770. package/src/types/api/getFeatures.ts +3 -0
  771. package/src/types/api/getOnekeyFeatures.ts +3 -0
  772. package/src/types/api/getPassphraseState.ts +9 -11
  773. package/src/types/api/index.ts +49 -68
  774. package/src/types/api/kaspaSignTransaction.ts +60 -4
  775. package/src/types/api/openWalletSession.ts +54 -0
  776. package/src/types/api/protocolV2.ts +33 -155
  777. package/src/types/api/sessionCache.ts +9 -4
  778. package/src/types/device.ts +216 -10
  779. package/src/types/params.ts +9 -2
  780. package/src/types/settings.ts +67 -25
  781. package/src/utils/assets.ts +4 -1
  782. package/src/utils/deviceFeaturesCompat.ts +191 -0
  783. package/src/utils/deviceFeaturesUtils.ts +45 -17
  784. package/src/utils/deviceInfoUtils.ts +29 -69
  785. package/src/utils/deviceSettings.ts +174 -39
  786. package/src/utils/deviceVersionUtils.ts +25 -44
  787. package/src/utils/findDefectiveBatchDevice.ts +3 -3
  788. package/src/utils/homescreen.ts +40 -0
  789. package/src/utils/index.ts +27 -3
  790. package/src/utils/networkUtils.ts +274 -16
  791. package/src/utils/pro2Nft.ts +142 -0
  792. package/src/utils/pro2Wallpaper.ts +130 -0
  793. package/src/utils/tracing.ts +3 -1
  794. package/src/utils/uiProgressThrottle.ts +63 -0
  795. package/tsconfig.type-tests.json +12 -0
  796. package/dist/api/GetDeviceInfo.d.ts +0 -9
  797. package/dist/api/GetDeviceInfo.d.ts.map +0 -1
  798. package/dist/api/firmware/progressThrottle.d.ts +0 -3
  799. package/dist/api/firmware/progressThrottle.d.ts.map +0 -1
  800. package/dist/api/protocol-v2/DeviceSessionGet.d.ts +0 -9
  801. package/dist/api/protocol-v2/DeviceSessionGet.d.ts.map +0 -1
  802. package/dist/api/protocol-v2/FilesystemDiskControl.d.ts +0 -13
  803. package/dist/api/protocol-v2/FilesystemDiskControl.d.ts.map +0 -1
  804. package/dist/deviceProfile/buildDeviceProfile.d.ts +0 -21
  805. package/dist/deviceProfile/buildDeviceProfile.d.ts.map +0 -1
  806. package/dist/types/api/getDeviceInfo.d.ts +0 -87
  807. package/dist/types/api/getDeviceInfo.d.ts.map +0 -1
  808. package/src/api/GetDeviceInfo.ts +0 -145
  809. package/src/api/firmware/progressThrottle.ts +0 -44
  810. package/src/api/protocol-v2/DeviceSessionGet.ts +0 -26
  811. package/src/api/protocol-v2/FilesystemDiskControl.ts +0 -50
  812. package/src/deviceProfile/buildDeviceProfile.ts +0 -352
  813. package/src/types/api/getDeviceInfo.ts +0 -101
@@ -0,0 +1,1899 @@
1
+ import { EDeviceType, HardwareErrorCode } from '@onekeyfe/hd-shared';
2
+ import { DeviceSessionPinType, DeviceSessionSeedDomain } from '@onekeyfe/hd-transport';
3
+
4
+ import GetPassphraseState from '../src/api/GetPassphraseState';
5
+ import OpenWalletSession from '../src/api/OpenWalletSession';
6
+ import { Device } from '../src/device/Device';
7
+ import { deviceWalletSessionStore } from '../src/device/DeviceWalletSessionStore';
8
+ import { DEVICE } from '../src/events';
9
+ import {
10
+ ensureProtocolV2WalletSessionUnlocked,
11
+ getProtocolV2WalletSession,
12
+ } from '../src/protocols/protocol-v2/walletSession';
13
+
14
+ jest.mock('../src/data/config', () => ({
15
+ getSDKVersion: jest.fn(() => '1.0.0'),
16
+ DEFAULT_DOMAIN: 'https://jssdk.onekey.so/1.0.0/',
17
+ }));
18
+
19
+ const createDevice = ({
20
+ passphraseProtection = true,
21
+ unlockedAttachPin = false,
22
+ refreshedUnlockedAttachPin = unlockedAttachPin,
23
+ typedCall = jest.fn(),
24
+ promptPassphrase = jest.fn(),
25
+ }: {
26
+ passphraseProtection?: boolean;
27
+ unlockedAttachPin?: boolean;
28
+ refreshedUnlockedAttachPin?: boolean;
29
+ typedCall?: jest.Mock;
30
+ promptPassphrase?: jest.Mock;
31
+ } = {}) => {
32
+ const device: Record<string, any> = {
33
+ passphraseState: undefined,
34
+ features: {
35
+ unlocked: true,
36
+ passphraseProtection,
37
+ attachToPinEnabled: unlockedAttachPin,
38
+ unlockedAttachPin,
39
+ },
40
+ commands: {
41
+ typedCall: jest.fn((request: string, ...args: unknown[]) => {
42
+ if (request === 'DeviceStatusGet') {
43
+ return {
44
+ message: {
45
+ device_id: 'device-1',
46
+ unlocked: true,
47
+ attach_to_pin_enabled: unlockedAttachPin,
48
+ unlocked_attach_pin: refreshedUnlockedAttachPin,
49
+ unlocked_by_attach_to_pin: refreshedUnlockedAttachPin,
50
+ passphrase_enabled: passphraseProtection,
51
+ },
52
+ };
53
+ }
54
+ return typedCall(request, ...args);
55
+ }),
56
+ promptPassphrase,
57
+ },
58
+ ensureProtocolV2RuntimeContext: jest.fn(() =>
59
+ device.commands.typedCall('ProtocolInfoRequest', 'ProtocolInfo', {
60
+ eventless_wallet_session: true,
61
+ })
62
+ ),
63
+ getDeviceState: jest.fn(() =>
64
+ Promise.resolve({
65
+ identity: { deviceId: 'device-1' },
66
+ status: {
67
+ unlocked: device.features.unlocked,
68
+ passphraseProtection: device.features.passphraseProtection,
69
+ unlockedAttachPin: device.features.unlockedAttachPin,
70
+ },
71
+ })
72
+ ),
73
+ getCurrentDeviceId: () => 'device-1',
74
+ getCurrentPassphraseProtection: () => passphraseProtection,
75
+ getInternalState: () =>
76
+ deviceWalletSessionStore.get('device-1', device.passphraseState as string | undefined),
77
+ updateInternalState: jest.fn(
78
+ (_enabled: boolean, passphraseState: string, deviceId: string, sessionId: string) => {
79
+ deviceWalletSessionStore.set(deviceId, passphraseState, sessionId);
80
+ }
81
+ ),
82
+ clearInternalState: jest.fn(() => {
83
+ if (device.passphraseState) {
84
+ deviceWalletSessionStore.delete('device-1', device.passphraseState);
85
+ }
86
+ }),
87
+ lockDevice: jest.fn(),
88
+ unlockDevice: jest.fn(),
89
+ updateProtocolV2Status: jest.fn((status: Record<string, unknown>) => {
90
+ device.features.unlocked = status.unlocked ?? device.features.unlocked;
91
+ device.features.attachToPinEnabled =
92
+ status.attach_to_pin_enabled ?? device.features.attachToPinEnabled;
93
+ device.features.unlockedAttachPin =
94
+ status.unlocked_by_attach_to_pin ??
95
+ status.unlocked_attach_pin ??
96
+ device.features.unlockedAttachPin;
97
+ return device.features;
98
+ }),
99
+ initialize: jest.fn(),
100
+ emit: jest.fn(),
101
+ isProtocolV2: () => true,
102
+ getCurrentFirmwareType: () => 'universal',
103
+ };
104
+ return device;
105
+ };
106
+
107
+ describe('openWalletSession', () => {
108
+ beforeEach(() => {
109
+ deviceWalletSessionStore.clear();
110
+ });
111
+
112
+ test('selects the standard wallet explicitly when passphrase is enabled', async () => {
113
+ const typedCall = jest.fn((request: string) => {
114
+ if (request === 'ProtocolInfoRequest') {
115
+ return { message: { version: 2 } };
116
+ }
117
+ if (request === 'DeviceSessionAskPassphrase') {
118
+ return { message: {} };
119
+ }
120
+ if (request === 'DeviceSessionGet') {
121
+ return {
122
+ message: {
123
+ btc_test_address: 'standard-state',
124
+ session_id: 'standard-session',
125
+ },
126
+ };
127
+ }
128
+ throw new Error(`Unexpected request: ${request}`);
129
+ });
130
+ const device = createDevice({ typedCall });
131
+
132
+ await expect(
133
+ getProtocolV2WalletSession(device as any, {
134
+ onlyMainPin: true,
135
+ })
136
+ ).resolves.toMatchObject({
137
+ passphraseState: 'standard-state',
138
+ newSession: 'standard-session',
139
+ });
140
+
141
+ expect(device.unlockDevice).not.toHaveBeenCalled();
142
+ expect(typedCall).toHaveBeenCalledWith('DeviceSessionAskPassphrase', 'Success', {
143
+ passphrase: '',
144
+ on_device: false,
145
+ });
146
+ expect(typedCall).toHaveBeenCalledWith('DeviceSessionGet', 'DeviceSession', {
147
+ seed_domains: [],
148
+ });
149
+ });
150
+
151
+ test('opens the standard wallet directly when passphrase is disabled', async () => {
152
+ const typedCall = jest.fn((request: string) => {
153
+ if (request === 'ProtocolInfoRequest') {
154
+ return { message: { version: 2 } };
155
+ }
156
+ if (request === 'DeviceSessionGet') {
157
+ return {
158
+ message: {
159
+ btc_test_address: 'standard-state',
160
+ session_id: 'standard-session',
161
+ },
162
+ };
163
+ }
164
+ throw new Error(`Unexpected request: ${request}`);
165
+ });
166
+ const device = createDevice({ passphraseProtection: false, typedCall });
167
+
168
+ await getProtocolV2WalletSession(device as any, { onlyMainPin: true });
169
+
170
+ expect(typedCall).not.toHaveBeenCalledWith(
171
+ 'DeviceSessionAskPassphrase',
172
+ 'Success',
173
+ expect.anything()
174
+ );
175
+ expect(typedCall).toHaveBeenCalledWith('DeviceSessionGet', 'DeviceSession', {
176
+ seed_domains: [],
177
+ });
178
+ });
179
+
180
+ test('does not treat an Attach PIN DeviceStatus as the Main wallet', async () => {
181
+ const typedCall = jest.fn((request: string) => {
182
+ if (request === 'ProtocolInfoRequest') {
183
+ return { message: { version: 2 } };
184
+ }
185
+ if (request === 'DeviceSessionAskPassphrase') {
186
+ return { message: {} };
187
+ }
188
+ if (request === 'DeviceSessionGet') {
189
+ return {
190
+ message: {
191
+ btc_test_address: 'standard-state',
192
+ session_id: 'standard-session',
193
+ },
194
+ };
195
+ }
196
+ throw new Error(`Unexpected request: ${request}`);
197
+ });
198
+ const device = createDevice({ typedCall });
199
+ device.features.unlockedAttachPin = true;
200
+
201
+ await getProtocolV2WalletSession(device as any, { onlyMainPin: true });
202
+
203
+ expect(device.unlockDevice).toHaveBeenCalledWith(DeviceSessionPinType.Main, {
204
+ source: 'wallet-session-coordinator',
205
+ reason: 'open-wallet',
206
+ deviceOnly: true,
207
+ });
208
+ });
209
+
210
+ test('unlocks a locked Protocol V2 device before restoring a wallet session', async () => {
211
+ const device = {
212
+ commands: {
213
+ typedCall: jest.fn().mockResolvedValue({ message: { unlocked: false } }),
214
+ },
215
+ isBootloader: jest.fn().mockReturnValue(false),
216
+ isProtocolV2: jest.fn().mockReturnValue(true),
217
+ isRomloader: jest.fn().mockReturnValue(false),
218
+ state: { status: { unlocked: false } },
219
+ unlockDevice: jest.fn().mockResolvedValue(undefined),
220
+ updateProtocolV2Status: jest.fn(function updateProtocolV2Status(
221
+ this: { state: { status: { unlocked: boolean } } },
222
+ status: { unlocked?: boolean }
223
+ ) {
224
+ this.state.status.unlocked = status.unlocked ?? this.state.status.unlocked;
225
+ }),
226
+ };
227
+
228
+ await expect(ensureProtocolV2WalletSessionUnlocked(device as any)).resolves.toBe(true);
229
+ expect(device.commands.typedCall).toHaveBeenCalledWith('DeviceStatusGet', 'DeviceStatus', {});
230
+ expect(device.unlockDevice).toHaveBeenCalledWith(DeviceSessionPinType.Main, {
231
+ source: 'unlock-coordinator',
232
+ reason: 'device-locked',
233
+ deviceOnly: true,
234
+ });
235
+ });
236
+
237
+ test('does not request PIN when wallet-session recovery finds the device unlocked', async () => {
238
+ const device = {
239
+ commands: {
240
+ typedCall: jest.fn().mockResolvedValue({ message: { unlocked: true } }),
241
+ },
242
+ isBootloader: jest.fn().mockReturnValue(false),
243
+ isProtocolV2: jest.fn().mockReturnValue(true),
244
+ isRomloader: jest.fn().mockReturnValue(false),
245
+ state: { status: { unlocked: true } },
246
+ unlockDevice: jest.fn(),
247
+ updateProtocolV2Status: jest.fn(),
248
+ };
249
+
250
+ await expect(ensureProtocolV2WalletSessionUnlocked(device as any)).resolves.toBe(false);
251
+ expect(device.unlockDevice).not.toHaveBeenCalled();
252
+ });
253
+
254
+ test.each([{ useEmptyPassphrase: true }, { initSession: true }, {}])(
255
+ 'requires the explicit mode in the new public API: %p',
256
+ legacyParams => {
257
+ const method = new OpenWalletSession({
258
+ payload: {
259
+ method: 'openWalletSession',
260
+ connectId: 'connect-id',
261
+ ...legacyParams,
262
+ },
263
+ });
264
+
265
+ expect(() => method.init()).toThrow('Parameter [mode] is required');
266
+ }
267
+ );
268
+
269
+ test('keeps the legacy getPassphraseState App flow working on Protocol V2', async () => {
270
+ const typedCall = jest
271
+ .fn()
272
+ .mockResolvedValueOnce({ message: { version: 2 } })
273
+ .mockResolvedValueOnce({ message: {} })
274
+ .mockResolvedValueOnce({
275
+ message: {
276
+ btc_test_address: 'pro2-wallet-state',
277
+ session_id: 'pro2-wallet-session',
278
+ },
279
+ });
280
+ const promptPassphrase = jest.fn().mockResolvedValue({ passphrase: 'host hidden wallet' });
281
+ const method = new GetPassphraseState({
282
+ payload: {
283
+ method: 'getPassphraseState',
284
+ connectId: 'connect-id',
285
+ initSession: true,
286
+ useEmptyPassphrase: false,
287
+ },
288
+ });
289
+ method.device = createDevice({ typedCall, promptPassphrase }) as any;
290
+
291
+ await expect(method.run()).resolves.toBe('pro2-wallet-state');
292
+ expect(promptPassphrase).toHaveBeenCalledWith(
293
+ {
294
+ existsAttachPinUser: false,
295
+ deviceOnly: false,
296
+ source: 'wallet-session-coordinator',
297
+ reason: 'open-wallet',
298
+ },
299
+ { cancelDeviceOnReject: false }
300
+ );
301
+ expect(typedCall).toHaveBeenNthCalledWith(2, 'DeviceSessionAskPassphrase', 'Success', {
302
+ passphrase: 'host hidden wallet',
303
+ on_device: false,
304
+ });
305
+ expect(typedCall).toHaveBeenNthCalledWith(3, 'DeviceSessionGet', 'DeviceSession', {
306
+ seed_domains: [],
307
+ });
308
+ });
309
+
310
+ test('keeps Legacy Protocol V1 getPassphraseState parameterless', async () => {
311
+ const typedCall = jest.fn().mockResolvedValue({
312
+ type: 'PassphraseState',
313
+ message: {
314
+ passphrase_state: 'current-wallet-state',
315
+ session_id: 'current-wallet-session',
316
+ unlocked_attach_pin: false,
317
+ },
318
+ });
319
+ const method = new GetPassphraseState({
320
+ payload: {
321
+ method: 'getPassphraseState',
322
+ connectId: 'connect-id',
323
+ useEmptyPassphrase: true,
324
+ initSession: true,
325
+ passphraseState: 'ignored-wallet-state',
326
+ },
327
+ });
328
+ const device = createDevice({ typedCall });
329
+ device.isProtocolV2 = () => false;
330
+ device.features = {
331
+ ...device.features,
332
+ deviceId: 'device-1',
333
+ passphraseProtection: true,
334
+ unlocked: true,
335
+ sessionId: null,
336
+ };
337
+ device.getCurrentFirmwareVersionString = () => '4.15.0';
338
+ device.getCurrentDeviceType = () => EDeviceType.Pro;
339
+ device.getFeatures = jest.fn();
340
+ method.device = device as any;
341
+
342
+ await expect(method.run()).resolves.toBe('current-wallet-state');
343
+ expect(device.clearInternalState).not.toHaveBeenCalled();
344
+ expect(typedCall).toHaveBeenCalledWith('GetPassphraseState', 'PassphraseState', {
345
+ passphrase_state: undefined,
346
+ });
347
+ });
348
+
349
+ test('opens the standard wallet through the Protocol V1 empty-passphrase flow', async () => {
350
+ const typedCall = jest.fn().mockResolvedValue({
351
+ type: 'PassphraseState',
352
+ message: {
353
+ passphrase_state: 'main-wallet-state',
354
+ session_id: 'main-wallet-session',
355
+ unlocked_attach_pin: false,
356
+ },
357
+ });
358
+ const method = new OpenWalletSession({
359
+ payload: {
360
+ method: 'openWalletSession',
361
+ connectId: 'connect-id',
362
+ mode: 'standard',
363
+ },
364
+ });
365
+ method.init();
366
+ const device = createDevice({ typedCall });
367
+ device.isProtocolV2 = () => false;
368
+ device.features = {
369
+ ...device.features,
370
+ deviceId: 'device-1',
371
+ unlocked: true,
372
+ sessionId: null,
373
+ };
374
+ device.getCurrentFirmwareVersionString = () => '4.15.0';
375
+ device.getCurrentDeviceType = () => EDeviceType.Pro;
376
+ device.getFeatures = jest.fn();
377
+ method.device = device as any;
378
+
379
+ await expect(method.run()).resolves.toEqual({
380
+ protocol: 'V1',
381
+ walletType: 'standard',
382
+ deviceId: 'device-1',
383
+ passphraseState: null,
384
+ resumed: false,
385
+ });
386
+ expect(method.payload.useEmptyPassphrase).toBe(true);
387
+ expect(typedCall).toHaveBeenCalledWith('GetPassphraseState', 'PassphraseState', {
388
+ passphrase_state: undefined,
389
+ });
390
+ expect(device.clearInternalState).not.toHaveBeenCalled();
391
+ });
392
+
393
+ test('uses the explicit mode to select a hidden wallet on Protocol V2', async () => {
394
+ const typedCall = jest
395
+ .fn()
396
+ .mockResolvedValueOnce({ message: { version: 2 } })
397
+ .mockResolvedValueOnce({ message: {} })
398
+ .mockResolvedValueOnce({
399
+ message: {
400
+ btc_test_address: 'hidden-state',
401
+ session_id: 'hidden-session',
402
+ },
403
+ });
404
+ const promptPassphrase = jest.fn().mockResolvedValue({ passphrase: 'host hidden wallet' });
405
+ const method = new OpenWalletSession({
406
+ payload: {
407
+ method: 'openWalletSession',
408
+ connectId: 'connect-id',
409
+ mode: 'select-hidden',
410
+ },
411
+ });
412
+ method.init();
413
+ method.device = createDevice({ typedCall, promptPassphrase }) as any;
414
+
415
+ await expect(method.run()).resolves.toMatchObject({
416
+ protocol: 'V2',
417
+ walletType: 'hidden',
418
+ passphraseState: 'hidden-state',
419
+ });
420
+ expect(promptPassphrase).toHaveBeenCalled();
421
+ expect(typedCall).toHaveBeenNthCalledWith(2, 'DeviceSessionAskPassphrase', 'Success', {
422
+ passphrase: 'host hidden wallet',
423
+ on_device: false,
424
+ });
425
+ expect(typedCall).toHaveBeenNthCalledWith(3, 'DeviceSessionGet', 'DeviceSession', {
426
+ seed_domains: [],
427
+ });
428
+ });
429
+
430
+ test('refreshes Pro2 status and unlocks before selecting a hidden wallet when locked', async () => {
431
+ const typedCall = jest
432
+ .fn()
433
+ .mockResolvedValueOnce({ message: { version: 2 } })
434
+ .mockResolvedValueOnce({ message: {} })
435
+ .mockResolvedValueOnce({
436
+ message: {
437
+ btc_test_address: 'hidden-state-after-preflight',
438
+ session_id: 'hidden-session-after-preflight',
439
+ },
440
+ });
441
+ const method = new OpenWalletSession({
442
+ payload: { method: 'openWalletSession', connectId: 'connect-id', mode: 'select-hidden' },
443
+ });
444
+ method.init();
445
+ const device = createDevice({
446
+ typedCall,
447
+ promptPassphrase: jest.fn().mockResolvedValue({ passphraseOnDevice: true }),
448
+ });
449
+ device.getDeviceState = jest
450
+ .fn()
451
+ .mockResolvedValueOnce({
452
+ identity: { deviceId: 'device-1' },
453
+ status: {
454
+ unlocked: false,
455
+ passphraseProtection: null,
456
+ unlockedAttachPin: null,
457
+ },
458
+ })
459
+ .mockResolvedValue({
460
+ identity: { deviceId: 'device-1' },
461
+ status: {
462
+ unlocked: true,
463
+ passphraseProtection: true,
464
+ unlockedAttachPin: false,
465
+ },
466
+ });
467
+ method.device = device as any;
468
+
469
+ await expect(method.run()).resolves.toMatchObject({
470
+ walletType: 'hidden',
471
+ passphraseState: 'hidden-state-after-preflight',
472
+ });
473
+ expect(device.unlockDevice).toHaveBeenCalledTimes(1);
474
+ expect(device.unlockDevice.mock.invocationCallOrder[0]).toBeLessThan(
475
+ typedCall.mock.invocationCallOrder[0]
476
+ );
477
+ });
478
+
479
+ test('classifies the selected Pro2 wallet from the post-unlock device state', async () => {
480
+ const typedCall = jest
481
+ .fn()
482
+ .mockResolvedValueOnce({ message: { version: 2 } })
483
+ .mockResolvedValueOnce({ message: {} })
484
+ .mockResolvedValueOnce({
485
+ message: {
486
+ btc_test_address: 'hidden-state-after-unlock',
487
+ session_id: 'hidden-session-after-unlock',
488
+ },
489
+ });
490
+ const promptPassphrase = jest.fn().mockResolvedValue({ passphrase: 'host hidden wallet' });
491
+ const method = new OpenWalletSession({
492
+ payload: { method: 'openWalletSession', connectId: 'connect-id', mode: 'select-hidden' },
493
+ });
494
+ method.init();
495
+ const device = createDevice({
496
+ passphraseProtection: false,
497
+ typedCall,
498
+ promptPassphrase,
499
+ });
500
+ device.features.unlocked = false;
501
+ device.getCurrentPassphraseProtection = () => device.features.passphraseProtection;
502
+ device.commands.typedCall = jest.fn((request: string, ...args: unknown[]) => {
503
+ if (request === 'DeviceStatusGet') {
504
+ return {
505
+ message: {
506
+ device_id: 'device-1',
507
+ unlocked: true,
508
+ passphrase_enabled: true,
509
+ },
510
+ };
511
+ }
512
+ return typedCall(request, ...args);
513
+ });
514
+ device.updateProtocolV2Status = jest.fn((status: Record<string, unknown>) => {
515
+ device.features.unlocked = status.unlocked ?? device.features.unlocked;
516
+ device.features.passphraseProtection =
517
+ status.passphrase_enabled ?? device.features.passphraseProtection;
518
+ return device.features;
519
+ });
520
+ device.unlockDevice = jest.fn().mockImplementation(() => {
521
+ device.features.unlocked = true;
522
+ device.features.passphraseProtection = true;
523
+ device.features.unlockedAttachPin = false;
524
+ return Promise.resolve(device.features);
525
+ });
526
+ method.device = device as any;
527
+
528
+ await expect(method.run()).resolves.toEqual({
529
+ protocol: 'V2',
530
+ walletType: 'hidden',
531
+ deviceId: 'device-1',
532
+ passphraseState: 'hidden-state-after-unlock',
533
+ resumed: false,
534
+ });
535
+ expect(device.unlockDevice).toHaveBeenCalledTimes(1);
536
+ expect(promptPassphrase).toHaveBeenCalledTimes(1);
537
+ });
538
+
539
+ test('unlocks before wallet selection when the Protocol V2 wallet status is unknown', async () => {
540
+ const typedCall = jest
541
+ .fn()
542
+ .mockResolvedValueOnce({ message: { version: 2 } })
543
+ .mockResolvedValueOnce({ message: {} })
544
+ .mockResolvedValueOnce({
545
+ message: {
546
+ btc_test_address: 'hidden-state-after-unknown-status',
547
+ session_id: 'hidden-session-after-unknown-status',
548
+ },
549
+ });
550
+ const promptPassphrase = jest.fn().mockResolvedValue({ passphrase: 'host hidden wallet' });
551
+ const method = new OpenWalletSession({
552
+ payload: { method: 'openWalletSession', connectId: 'connect-id', mode: 'select-hidden' },
553
+ });
554
+ method.init();
555
+ const device = createDevice({ typedCall, promptPassphrase });
556
+ device.getDeviceState = jest
557
+ .fn()
558
+ .mockResolvedValueOnce({
559
+ identity: { deviceId: 'device-1' },
560
+ status: {
561
+ unlocked: undefined,
562
+ passphraseProtection: null,
563
+ unlockedAttachPin: null,
564
+ },
565
+ })
566
+ .mockResolvedValue({
567
+ identity: { deviceId: 'device-1' },
568
+ status: {
569
+ unlocked: true,
570
+ passphraseProtection: true,
571
+ unlockedAttachPin: false,
572
+ },
573
+ });
574
+ device.unlockDevice = jest.fn().mockImplementation(() => {
575
+ device.features.unlocked = true;
576
+ device.features.passphraseProtection = true;
577
+ device.features.unlockedAttachPin = false;
578
+ return Promise.resolve(device.features);
579
+ });
580
+ method.device = device as any;
581
+
582
+ await expect(method.run()).resolves.toMatchObject({
583
+ walletType: 'hidden',
584
+ passphraseState: 'hidden-state-after-unknown-status',
585
+ });
586
+ expect(device.unlockDevice).toHaveBeenCalledTimes(1);
587
+ expect(device.unlockDevice.mock.invocationCallOrder[0]).toBeLessThan(
588
+ promptPassphrase.mock.invocationCallOrder[0]
589
+ );
590
+ });
591
+
592
+ test('rejects wallet selection when the post-unlock Protocol V2 status remains incomplete', async () => {
593
+ const typedCall = jest
594
+ .fn()
595
+ .mockResolvedValueOnce({ message: { version: 2 } })
596
+ .mockResolvedValueOnce({ message: {} })
597
+ .mockResolvedValueOnce({
598
+ message: {
599
+ btc_test_address: 'hidden-state-from-incomplete-status',
600
+ session_id: 'hidden-session-from-incomplete-status',
601
+ },
602
+ });
603
+ const promptPassphrase = jest.fn().mockResolvedValue({ passphrase: 'host hidden wallet' });
604
+ const method = new OpenWalletSession({
605
+ payload: { method: 'openWalletSession', connectId: 'connect-id', mode: 'select-hidden' },
606
+ });
607
+ method.init();
608
+ const device = createDevice({ typedCall, promptPassphrase });
609
+ device.getDeviceState = jest.fn().mockResolvedValue({
610
+ identity: { deviceId: 'device-1' },
611
+ status: {
612
+ unlocked: true,
613
+ passphraseProtection: null,
614
+ unlockedAttachPin: null,
615
+ },
616
+ });
617
+ device.unlockDevice = jest.fn().mockResolvedValue(device.features);
618
+ method.device = device as any;
619
+
620
+ await expect(method.run()).rejects.toMatchObject({
621
+ errorCode: HardwareErrorCode.DeviceInitializeFailed,
622
+ });
623
+ expect(device.unlockDevice).toHaveBeenCalledTimes(1);
624
+ expect(promptPassphrase).not.toHaveBeenCalled();
625
+ });
626
+
627
+ test('rejects before prompting for a hidden wallet when Pro2 passphrase is disabled', async () => {
628
+ const typedCall = jest
629
+ .fn()
630
+ .mockResolvedValueOnce({ message: { version: 2 } })
631
+ .mockResolvedValueOnce({ message: {} })
632
+ .mockResolvedValueOnce({
633
+ message: {
634
+ btc_test_address: 'standard-wallet-state',
635
+ session_id: 'standard-wallet-session',
636
+ },
637
+ });
638
+ const promptPassphrase = jest.fn().mockResolvedValue({ passphraseOnDevice: true });
639
+ const method = new OpenWalletSession({
640
+ payload: { method: 'openWalletSession', connectId: 'connect-id', mode: 'select-hidden' },
641
+ });
642
+ method.init();
643
+ const device = createDevice({
644
+ passphraseProtection: false,
645
+ typedCall,
646
+ promptPassphrase,
647
+ });
648
+ method.device = device as any;
649
+
650
+ await expect(method.run()).rejects.toMatchObject({
651
+ errorCode: HardwareErrorCode.DeviceNotOpenedPassphrase,
652
+ });
653
+ expect(device.clearInternalState).toHaveBeenCalled();
654
+ expect(promptPassphrase).not.toHaveBeenCalled();
655
+ expect(typedCall).not.toHaveBeenCalledWith(
656
+ 'DeviceSessionAskPassphrase',
657
+ 'Success',
658
+ expect.anything()
659
+ );
660
+ });
661
+
662
+ test('reuses the current Attach PIN session without reopening wallet selection', async () => {
663
+ const typedCall = jest.fn((request: string) => {
664
+ if (request === 'ProtocolInfoRequest') {
665
+ return { message: { version: 2 } };
666
+ }
667
+ if (request === 'DeviceSessionAskPassphrase') {
668
+ return { message: {} };
669
+ }
670
+ if (request === 'DeviceSessionGet') {
671
+ return {
672
+ message: {
673
+ btc_test_address: 'attach-state',
674
+ session_id: 'attach-session',
675
+ },
676
+ };
677
+ }
678
+ throw new Error(`Unexpected request: ${request}`);
679
+ });
680
+ const promptPassphrase = jest.fn().mockResolvedValue({ passphrase: 'must not be requested' });
681
+ const method = new OpenWalletSession({
682
+ payload: { method: 'openWalletSession', connectId: 'connect-id', mode: 'select-hidden' },
683
+ });
684
+ method.init();
685
+ const device = createDevice({
686
+ typedCall,
687
+ promptPassphrase,
688
+ unlockedAttachPin: true,
689
+ });
690
+ method.device = device as any;
691
+
692
+ await expect(method.run()).resolves.toEqual({
693
+ protocol: 'V2',
694
+ walletType: 'hidden',
695
+ deviceId: 'device-1',
696
+ passphraseState: 'attach-state',
697
+ resumed: false,
698
+ });
699
+ expect(promptPassphrase).not.toHaveBeenCalled();
700
+ expect(device.unlockDevice).not.toHaveBeenCalled();
701
+ expect(device.commands.typedCall).toHaveBeenCalledWith('DeviceSessionGet', 'DeviceSession', {
702
+ seed_domains: [],
703
+ });
704
+ expect(device.commands.typedCall).not.toHaveBeenCalledWith(
705
+ 'DeviceSessionAskPassphrase',
706
+ 'Success',
707
+ expect.anything()
708
+ );
709
+ expect(device.commands.typedCall).not.toHaveBeenCalledWith(
710
+ 'DeviceSessionAskPin',
711
+ 'Success',
712
+ expect.anything()
713
+ );
714
+ });
715
+
716
+ test('fails closed when the Attach PIN state changes before reading the current session', async () => {
717
+ const typedCall = jest.fn((request: string) => {
718
+ if (request === 'ProtocolInfoRequest') {
719
+ return { message: { version: 2 } };
720
+ }
721
+ if (request === 'DeviceSessionAskPassphrase') {
722
+ return { message: {} };
723
+ }
724
+ if (request === 'DeviceSessionGet') {
725
+ return {
726
+ message: {
727
+ btc_test_address: 'unexpected-state',
728
+ session_id: 'unexpected-session',
729
+ },
730
+ };
731
+ }
732
+ throw new Error(`Unexpected request: ${request}`);
733
+ });
734
+ const promptPassphrase = jest.fn().mockResolvedValue({ passphrase: 'must not be requested' });
735
+ const method = new OpenWalletSession({
736
+ payload: { method: 'openWalletSession', connectId: 'connect-id', mode: 'select-hidden' },
737
+ });
738
+ method.init();
739
+ const device = createDevice({
740
+ typedCall,
741
+ promptPassphrase,
742
+ unlockedAttachPin: true,
743
+ refreshedUnlockedAttachPin: false,
744
+ });
745
+ method.device = device as any;
746
+
747
+ await expect(method.run()).rejects.toMatchObject({
748
+ errorCode: HardwareErrorCode.DeviceCheckUnlockTypeError,
749
+ });
750
+ expect(device.clearInternalState).toHaveBeenCalled();
751
+ expect(promptPassphrase).not.toHaveBeenCalled();
752
+ expect(device.commands.typedCall).not.toHaveBeenCalledWith(
753
+ 'DeviceSessionAskPassphrase',
754
+ 'Success',
755
+ expect.anything()
756
+ );
757
+ expect(device.commands.typedCall).not.toHaveBeenCalledWith(
758
+ 'DeviceSessionGet',
759
+ 'DeviceSession',
760
+ expect.anything()
761
+ );
762
+ });
763
+
764
+ test('select-hidden starts a fresh hidden-wallet session on Protocol V2', async () => {
765
+ const typedCall = jest
766
+ .fn()
767
+ .mockResolvedValueOnce({ message: { version: 2 } })
768
+ .mockResolvedValueOnce({ message: {} })
769
+ .mockResolvedValueOnce({
770
+ message: {
771
+ btc_test_address: 'new-hidden-state',
772
+ session_id: 'new-hidden-session',
773
+ },
774
+ });
775
+ const promptPassphrase = jest.fn().mockResolvedValue({ passphrase: 'host hidden wallet' });
776
+ const method = new OpenWalletSession({
777
+ payload: {
778
+ method: 'openWalletSession',
779
+ connectId: 'connect-id',
780
+ mode: 'select-hidden',
781
+ },
782
+ });
783
+ method.init();
784
+ method.device = createDevice({ typedCall, promptPassphrase }) as any;
785
+
786
+ await expect(method.run()).resolves.toEqual({
787
+ protocol: 'V2',
788
+ walletType: 'hidden',
789
+ deviceId: 'device-1',
790
+ passphraseState: 'new-hidden-state',
791
+ resumed: false,
792
+ });
793
+ expect(typedCall).toHaveBeenCalledWith('ProtocolInfoRequest', 'ProtocolInfo', {
794
+ eventless_wallet_session: true,
795
+ });
796
+ expect(typedCall).toHaveBeenCalledWith('DeviceSessionAskPassphrase', 'Success', {
797
+ passphrase: 'host hidden wallet',
798
+ on_device: false,
799
+ });
800
+ expect(typedCall).toHaveBeenCalledWith('DeviceSessionGet', 'DeviceSession', {
801
+ seed_domains: [],
802
+ });
803
+ expect(promptPassphrase).toHaveBeenCalled();
804
+ expect(deviceWalletSessionStore.get('device-1', 'new-hidden-state')).toBe('new-hidden-session');
805
+ });
806
+
807
+ test('select-hidden does not clear another device wallet', async () => {
808
+ const typedCall = jest
809
+ .fn()
810
+ .mockResolvedValueOnce({ message: { version: 2 } })
811
+ .mockResolvedValueOnce({ message: {} })
812
+ .mockResolvedValueOnce({
813
+ message: {
814
+ btc_test_address: 'current-device-state',
815
+ session_id: 'current-device-session',
816
+ },
817
+ });
818
+ const promptPassphrase = jest.fn().mockResolvedValue({ passphrase: 'host hidden wallet' });
819
+ const method = new OpenWalletSession({
820
+ payload: {
821
+ method: 'openWalletSession',
822
+ connectId: 'connect-id',
823
+ mode: 'select-hidden',
824
+ },
825
+ });
826
+ method.init();
827
+ deviceWalletSessionStore.set('other-device', 'other-device-state', 'other-device-session');
828
+ method.device = createDevice({ typedCall, promptPassphrase }) as any;
829
+
830
+ await expect(method.run()).resolves.toMatchObject({
831
+ deviceId: 'device-1',
832
+ passphraseState: 'current-device-state',
833
+ resumed: false,
834
+ });
835
+ expect(deviceWalletSessionStore.get('other-device', 'other-device-state')).toBe(
836
+ 'other-device-session'
837
+ );
838
+ });
839
+
840
+ test('uses an explicit wallet binding to resume on Protocol V2', async () => {
841
+ const typedCall = jest
842
+ .fn()
843
+ .mockResolvedValueOnce({ message: { version: 2 } })
844
+ .mockResolvedValueOnce({
845
+ message: {
846
+ btc_test_address: 'hidden-state',
847
+ session_id: 'renewed-session',
848
+ },
849
+ });
850
+ const promptPassphrase = jest.fn();
851
+ const method = new OpenWalletSession({
852
+ payload: {
853
+ method: 'openWalletSession',
854
+ connectId: 'connect-id',
855
+ mode: 'resume-hidden',
856
+ deviceId: 'device-1',
857
+ passphraseState: 'hidden-state',
858
+ },
859
+ });
860
+ method.init();
861
+ deviceWalletSessionStore.set('device-1', 'hidden-state', 'known-session');
862
+ method.device = createDevice({ typedCall, promptPassphrase }) as any;
863
+
864
+ await expect(method.run()).resolves.toEqual({
865
+ protocol: 'V2',
866
+ walletType: 'hidden',
867
+ deviceId: 'device-1',
868
+ passphraseState: 'hidden-state',
869
+ resumed: true,
870
+ });
871
+ expect(typedCall).toHaveBeenCalledWith('DeviceSessionGet', 'DeviceSession', {
872
+ session_id: 'known-session',
873
+ btc_test_address: 'hidden-state',
874
+ seed_domains: [],
875
+ });
876
+ expect(promptPassphrase).not.toHaveBeenCalled();
877
+ });
878
+
879
+ test.each([
880
+ {
881
+ mode: 'standard',
882
+ initSession: true,
883
+ },
884
+ {
885
+ mode: 'select-hidden',
886
+ useEmptyPassphrase: false,
887
+ },
888
+ {
889
+ mode: 'select-hidden',
890
+ deviceId: 'device-1',
891
+ passphraseState: 'hidden-state',
892
+ },
893
+ {
894
+ mode: 'resume-hidden',
895
+ deviceId: 'device-1',
896
+ passphraseState: 'hidden-state',
897
+ initSession: false,
898
+ },
899
+ ])('rejects legacy parameters combined with explicit mode: %o', params => {
900
+ const method = new OpenWalletSession({
901
+ payload: {
902
+ method: 'openWalletSession',
903
+ connectId: 'connect-id',
904
+ ...params,
905
+ } as any,
906
+ });
907
+
908
+ expect(() => method.init()).toThrow(
909
+ expect.objectContaining({
910
+ errorCode: HardwareErrorCode.CallMethodInvalidParameter,
911
+ })
912
+ );
913
+ });
914
+
915
+ test.each([{ initSession: 'true' }, { useEmptyPassphrase: 'false' }])(
916
+ 'rejects legacy flags without an explicit mode: %o',
917
+ params => {
918
+ const method = new OpenWalletSession({
919
+ payload: {
920
+ method: 'openWalletSession',
921
+ connectId: 'connect-id',
922
+ ...params,
923
+ } as any,
924
+ });
925
+
926
+ expect(() => method.init()).toThrow(
927
+ expect.objectContaining({
928
+ errorCode: HardwareErrorCode.CallMethodInvalidParameter,
929
+ })
930
+ );
931
+ }
932
+ );
933
+
934
+ test('reuses the SDK-managed Protocol V1 hidden-wallet session before validation', async () => {
935
+ const typedCall = jest.fn().mockResolvedValue({
936
+ type: 'PassphraseState',
937
+ message: {
938
+ passphrase_state: 'hidden-state',
939
+ session_id: 'known-session',
940
+ unlocked_attach_pin: false,
941
+ },
942
+ });
943
+ const method = new OpenWalletSession({
944
+ payload: {
945
+ method: 'openWalletSession',
946
+ connectId: 'connect-id',
947
+ mode: 'resume-hidden',
948
+ deviceId: 'device-1',
949
+ passphraseState: 'hidden-state',
950
+ },
951
+ });
952
+ method.init();
953
+ deviceWalletSessionStore.set('device-1', 'hidden-state', 'known-session');
954
+ const device = createDevice({ typedCall });
955
+ device.isProtocolV2 = () => false;
956
+ device.features = {
957
+ ...device.features,
958
+ deviceId: 'device-1',
959
+ unlocked: true,
960
+ sessionId: null,
961
+ };
962
+ device.getCurrentFirmwareVersionString = () => '4.15.0';
963
+ device.getCurrentDeviceType = () => EDeviceType.Pro;
964
+ device.getFeatures = jest.fn();
965
+ method.device = device as any;
966
+
967
+ await expect(method.run()).resolves.toMatchObject({
968
+ protocol: 'V1',
969
+ walletType: 'hidden',
970
+ passphraseState: 'hidden-state',
971
+ resumed: true,
972
+ });
973
+ expect(device.initialize).toHaveBeenCalledWith({
974
+ deviceId: 'device-1',
975
+ passphraseState: 'hidden-state',
976
+ });
977
+ expect(typedCall).toHaveBeenCalledWith('GetPassphraseState', 'PassphraseState', {
978
+ passphrase_state: 'hidden-state',
979
+ });
980
+ });
981
+
982
+ test('rejects a Protocol V1 resume when the device returns another hidden wallet', async () => {
983
+ const typedCall = jest.fn().mockResolvedValue({
984
+ type: 'PassphraseState',
985
+ message: {
986
+ passphrase_state: 'other-hidden-state',
987
+ session_id: 'other-session',
988
+ unlocked_attach_pin: false,
989
+ },
990
+ });
991
+ const method = new OpenWalletSession({
992
+ payload: {
993
+ method: 'openWalletSession',
994
+ connectId: 'connect-id',
995
+ mode: 'resume-hidden',
996
+ deviceId: 'device-1',
997
+ passphraseState: 'hidden-state',
998
+ },
999
+ });
1000
+ method.init();
1001
+ deviceWalletSessionStore.set('device-1', 'hidden-state', 'known-session');
1002
+ const device = createDevice({ typedCall });
1003
+ device.isProtocolV2 = () => false;
1004
+ device.features = {
1005
+ ...device.features,
1006
+ deviceId: 'device-1',
1007
+ unlocked: true,
1008
+ sessionId: null,
1009
+ };
1010
+ device.getCurrentFirmwareVersionString = () => '4.15.0';
1011
+ device.getCurrentDeviceType = () => EDeviceType.Pro;
1012
+ device.getFeatures = jest.fn();
1013
+ method.device = device as any;
1014
+
1015
+ await expect(method.run()).rejects.toMatchObject({
1016
+ errorCode: HardwareErrorCode.DeviceCheckPassphraseStateError,
1017
+ });
1018
+ expect(device.clearInternalState).toHaveBeenCalledTimes(1);
1019
+ expect(deviceWalletSessionStore.get('device-1', 'hidden-state')).toBeUndefined();
1020
+ });
1021
+
1022
+ test('opens the standard wallet without selecting a hidden wallet', async () => {
1023
+ const typedCall = jest
1024
+ .fn()
1025
+ .mockResolvedValueOnce({ message: { version: 2 } })
1026
+ .mockResolvedValueOnce({ message: {} })
1027
+ .mockResolvedValueOnce({
1028
+ message: {
1029
+ btc_test_address: 'main-wallet-state',
1030
+ session_id: 'main-wallet-session',
1031
+ },
1032
+ });
1033
+ const promptPassphrase = jest.fn();
1034
+ const method = new OpenWalletSession({
1035
+ payload: { method: 'openWalletSession', connectId: 'connect-id', mode: 'standard' },
1036
+ });
1037
+ method.init();
1038
+ const device = createDevice({ typedCall, promptPassphrase });
1039
+ device.passphraseState = 'previous-hidden-state';
1040
+ method.device = device as any;
1041
+
1042
+ await expect(method.run()).resolves.toEqual({
1043
+ protocol: 'V2',
1044
+ walletType: 'standard',
1045
+ deviceId: 'device-1',
1046
+ passphraseState: null,
1047
+ resumed: false,
1048
+ });
1049
+ expect(typedCall).toHaveBeenCalledTimes(3);
1050
+ expect(typedCall).toHaveBeenCalledWith('ProtocolInfoRequest', 'ProtocolInfo', {
1051
+ eventless_wallet_session: true,
1052
+ });
1053
+ expect(device.unlockDevice).not.toHaveBeenCalled();
1054
+ expect(typedCall).toHaveBeenCalledWith('DeviceSessionAskPassphrase', 'Success', {
1055
+ passphrase: '',
1056
+ on_device: false,
1057
+ });
1058
+ expect(typedCall).toHaveBeenCalledWith('DeviceSessionGet', 'DeviceSession', {
1059
+ seed_domains: [],
1060
+ });
1061
+ expect(promptPassphrase).not.toHaveBeenCalled();
1062
+ expect(device.passphraseState).toBeUndefined();
1063
+ });
1064
+
1065
+ test('opens a locked Protocol V2 standard wallet after deviceId becomes available', async () => {
1066
+ const typedCall = jest
1067
+ .fn()
1068
+ .mockResolvedValueOnce({ message: { version: 2 } })
1069
+ .mockResolvedValueOnce({ message: {} })
1070
+ .mockResolvedValueOnce({
1071
+ message: {
1072
+ btc_test_address: 'main-wallet-state',
1073
+ session_id: 'main-wallet-session',
1074
+ },
1075
+ });
1076
+ const method = new OpenWalletSession({
1077
+ payload: { method: 'openWalletSession', connectId: 'connect-id', mode: 'standard' },
1078
+ });
1079
+ method.init();
1080
+ const device = createDevice({ typedCall });
1081
+ device.features.unlocked = false;
1082
+ device.getDeviceState = jest
1083
+ .fn()
1084
+ .mockResolvedValueOnce({
1085
+ identity: { deviceId: undefined },
1086
+ status: { unlocked: false, passphraseProtection: true },
1087
+ })
1088
+ .mockResolvedValueOnce({
1089
+ identity: { deviceId: 'device-1' },
1090
+ status: {
1091
+ unlocked: true,
1092
+ passphraseProtection: true,
1093
+ unlockedAttachPin: false,
1094
+ },
1095
+ });
1096
+ device.unlockDevice = jest.fn().mockImplementation(() => {
1097
+ device.features.unlocked = true;
1098
+ return Promise.resolve(device.features);
1099
+ });
1100
+ method.device = device as any;
1101
+
1102
+ await expect(method.run()).resolves.toEqual({
1103
+ protocol: 'V2',
1104
+ walletType: 'standard',
1105
+ deviceId: 'device-1',
1106
+ passphraseState: null,
1107
+ resumed: false,
1108
+ });
1109
+ expect(device.unlockDevice).toHaveBeenCalledWith(DeviceSessionPinType.Main, {
1110
+ source: 'wallet-session-coordinator',
1111
+ reason: 'open-wallet',
1112
+ deviceOnly: true,
1113
+ });
1114
+ expect(device.getDeviceState).toHaveBeenCalledTimes(2);
1115
+ });
1116
+
1117
+ test('switches from Attach PIN to Main PIN before opening the standard wallet', async () => {
1118
+ const typedCall = jest
1119
+ .fn()
1120
+ .mockResolvedValueOnce({ message: { version: 2 } })
1121
+ .mockResolvedValueOnce({ message: {} })
1122
+ .mockResolvedValueOnce({
1123
+ message: {
1124
+ btc_test_address: 'main-wallet-state',
1125
+ session_id: 'main-wallet-session',
1126
+ },
1127
+ });
1128
+ const method = new OpenWalletSession({
1129
+ payload: { method: 'openWalletSession', connectId: 'connect-id', mode: 'standard' },
1130
+ });
1131
+ method.init();
1132
+ const device = createDevice({ typedCall });
1133
+ device.features.unlockedAttachPin = true;
1134
+ device.getDeviceState = jest
1135
+ .fn()
1136
+ .mockResolvedValueOnce({
1137
+ identity: { deviceId: 'device-1' },
1138
+ status: {
1139
+ unlocked: true,
1140
+ unlockedAttachPin: true,
1141
+ passphraseProtection: true,
1142
+ },
1143
+ })
1144
+ .mockResolvedValue({
1145
+ identity: { deviceId: 'device-1' },
1146
+ status: {
1147
+ unlocked: true,
1148
+ unlockedAttachPin: false,
1149
+ passphraseProtection: true,
1150
+ },
1151
+ });
1152
+ device.unlockDevice = jest.fn().mockImplementation(() => {
1153
+ device.features.unlockedAttachPin = false;
1154
+ return Promise.resolve(device.features);
1155
+ });
1156
+ method.device = device as any;
1157
+
1158
+ await expect(method.run()).resolves.toMatchObject({
1159
+ walletType: 'standard',
1160
+ passphraseState: null,
1161
+ });
1162
+ expect(device.unlockDevice).toHaveBeenCalledWith(DeviceSessionPinType.Main, {
1163
+ source: 'wallet-session-coordinator',
1164
+ reason: 'open-wallet',
1165
+ deviceOnly: true,
1166
+ });
1167
+ expect(typedCall).toHaveBeenCalledWith('DeviceSessionGet', 'DeviceSession', {
1168
+ seed_domains: [],
1169
+ });
1170
+ });
1171
+
1172
+ test('selects a hidden wallet without exposing the internal device session', async () => {
1173
+ const typedCall = jest
1174
+ .fn()
1175
+ .mockResolvedValueOnce({ message: { version: 2 } })
1176
+ .mockResolvedValueOnce({ message: {} })
1177
+ .mockResolvedValueOnce({
1178
+ message: {
1179
+ btc_test_address: 'hidden-state',
1180
+ session_id: 'hidden-session',
1181
+ },
1182
+ });
1183
+ const promptPassphrase = jest.fn().mockResolvedValue({ passphrase: 'host hidden wallet' });
1184
+ const method = new OpenWalletSession({
1185
+ payload: { method: 'openWalletSession', connectId: 'connect-id', mode: 'select-hidden' },
1186
+ });
1187
+ method.init();
1188
+ const device = createDevice({ typedCall, promptPassphrase });
1189
+ device.passphraseState = 'previous-hidden-state';
1190
+ method.device = device as any;
1191
+
1192
+ await expect(method.run()).resolves.toEqual({
1193
+ protocol: 'V2',
1194
+ walletType: 'hidden',
1195
+ deviceId: 'device-1',
1196
+ passphraseState: 'hidden-state',
1197
+ resumed: false,
1198
+ });
1199
+ expect(typedCall).toHaveBeenCalledWith('DeviceSessionAskPassphrase', 'Success', {
1200
+ passphrase: 'host hidden wallet',
1201
+ on_device: false,
1202
+ });
1203
+ expect(typedCall).toHaveBeenCalledWith('DeviceSessionGet', 'DeviceSession', {
1204
+ seed_domains: [],
1205
+ });
1206
+ expect(promptPassphrase).toHaveBeenCalled();
1207
+ expect(device.passphraseState).toBeUndefined();
1208
+ expect(deviceWalletSessionStore.get('device-1', 'hidden-state')).toBe('hidden-session');
1209
+ });
1210
+
1211
+ test('gets the prepared session after host passphrase entry', async () => {
1212
+ const typedCall = jest
1213
+ .fn()
1214
+ .mockResolvedValueOnce({ message: { version: 2 } })
1215
+ .mockResolvedValueOnce({ message: {} })
1216
+ .mockResolvedValueOnce({
1217
+ message: { btc_test_address: 'device-state', session_id: 'device-session' },
1218
+ });
1219
+ const promptPassphrase = jest.fn().mockResolvedValue({ passphrase: 'host hidden wallet' });
1220
+ const method = new OpenWalletSession({
1221
+ payload: { method: 'openWalletSession', connectId: 'connect-id', mode: 'select-hidden' },
1222
+ });
1223
+ method.init();
1224
+ method.device = createDevice({ typedCall, promptPassphrase }) as any;
1225
+
1226
+ await expect(method.run()).resolves.toMatchObject({
1227
+ walletType: 'hidden',
1228
+ passphraseState: 'device-state',
1229
+ });
1230
+ expect(typedCall).toHaveBeenCalledWith('DeviceSessionAskPassphrase', 'Success', {
1231
+ passphrase: 'host hidden wallet',
1232
+ on_device: false,
1233
+ });
1234
+ expect(typedCall).toHaveBeenCalledWith('DeviceSessionGet', 'DeviceSession', {
1235
+ seed_domains: [],
1236
+ });
1237
+ });
1238
+
1239
+ test('selects an on-device passphrase wallet with an explicit on_device request', async () => {
1240
+ const typedCall = jest
1241
+ .fn()
1242
+ .mockResolvedValueOnce({ message: { version: 2 } })
1243
+ .mockResolvedValueOnce({ message: {} })
1244
+ .mockResolvedValueOnce({
1245
+ message: { btc_test_address: 'device-state', session_id: 'device-session' },
1246
+ });
1247
+ const promptPassphrase = jest.fn().mockResolvedValue({ passphraseOnDevice: true });
1248
+ const method = new OpenWalletSession({
1249
+ payload: { method: 'openWalletSession', connectId: 'connect-id', mode: 'select-hidden' },
1250
+ });
1251
+ method.init();
1252
+ const device = createDevice({ typedCall, promptPassphrase });
1253
+ const passphraseInteraction = {
1254
+ interactionId: 'interaction-1',
1255
+ phaseId: 'interaction-1:phase-1',
1256
+ sequence: 1,
1257
+ phase: 'passphrase',
1258
+ transition: 'start',
1259
+ protocol: 'V2',
1260
+ } as const;
1261
+ const passphraseOnDeviceInteraction = {
1262
+ interactionId: 'interaction-1',
1263
+ phaseId: 'interaction-1:phase-2',
1264
+ sequence: 2,
1265
+ phase: 'passphrase-on-device',
1266
+ transition: 'start',
1267
+ protocol: 'V2',
1268
+ } as const;
1269
+ device.createProtocolV2UiPhaseMetadata = jest
1270
+ .fn()
1271
+ .mockReturnValueOnce(passphraseInteraction)
1272
+ .mockReturnValueOnce(passphraseOnDeviceInteraction);
1273
+ method.device = device as any;
1274
+
1275
+ await expect(method.run()).resolves.toMatchObject({
1276
+ walletType: 'hidden',
1277
+ passphraseState: 'device-state',
1278
+ });
1279
+ expect(typedCall).toHaveBeenNthCalledWith(2, 'DeviceSessionAskPassphrase', 'Success', {
1280
+ on_device: true,
1281
+ });
1282
+ expect(typedCall).toHaveBeenNthCalledWith(3, 'DeviceSessionGet', 'DeviceSession', {
1283
+ seed_domains: [],
1284
+ });
1285
+ expect(device.createProtocolV2UiPhaseMetadata).toHaveBeenNthCalledWith(
1286
+ 2,
1287
+ 'passphrase-on-device',
1288
+ 'start'
1289
+ );
1290
+ expect(device.emit).toHaveBeenCalledWith(DEVICE.PASSPHRASE_ON_DEVICE, device, {
1291
+ source: 'wallet-session-coordinator',
1292
+ reason: 'open-wallet',
1293
+ interaction: passphraseOnDeviceInteraction,
1294
+ });
1295
+ });
1296
+
1297
+ test('forwards a host passphrase to Pro2 firmware', async () => {
1298
+ const typedCall = jest
1299
+ .fn()
1300
+ .mockResolvedValueOnce({ message: { version: 2 } })
1301
+ .mockResolvedValueOnce({ message: {} })
1302
+ .mockResolvedValueOnce({
1303
+ message: { btc_test_address: 'host-state', session_id: 'host-session' },
1304
+ });
1305
+ const promptPassphrase = jest.fn().mockResolvedValue({ passphrase: 'host hidden wallet' });
1306
+ const method = new OpenWalletSession({
1307
+ payload: {
1308
+ method: 'openWalletSession',
1309
+ connectId: 'connect-id',
1310
+ mode: 'select-hidden',
1311
+ },
1312
+ });
1313
+ method.init();
1314
+ method.device = createDevice({
1315
+ typedCall,
1316
+ promptPassphrase,
1317
+ }) as any;
1318
+
1319
+ await expect(method.run()).resolves.toMatchObject({
1320
+ walletType: 'hidden',
1321
+ passphraseState: 'host-state',
1322
+ });
1323
+ expect(promptPassphrase).toHaveBeenCalledWith(
1324
+ {
1325
+ existsAttachPinUser: false,
1326
+ deviceOnly: false,
1327
+ source: 'wallet-session-coordinator',
1328
+ reason: 'open-wallet',
1329
+ },
1330
+ { cancelDeviceOnReject: false }
1331
+ );
1332
+ expect(typedCall).toHaveBeenNthCalledWith(2, 'DeviceSessionAskPassphrase', 'Success', {
1333
+ passphrase: 'host hidden wallet',
1334
+ on_device: false,
1335
+ });
1336
+ expect(typedCall).toHaveBeenNthCalledWith(3, 'DeviceSessionGet', 'DeviceSession', {
1337
+ seed_domains: [],
1338
+ });
1339
+ });
1340
+
1341
+ test('normalizes a Unicode Host passphrase before sending it to Pro2 firmware', async () => {
1342
+ const typedCall = jest
1343
+ .fn()
1344
+ .mockResolvedValueOnce({ message: { version: 2 } })
1345
+ .mockResolvedValueOnce({ message: {} })
1346
+ .mockResolvedValueOnce({
1347
+ message: { btc_test_address: 'host-state', session_id: 'host-session' },
1348
+ });
1349
+ const promptPassphrase = jest.fn().mockResolvedValue({ passphrase: 'caf\u00e9' });
1350
+ const method = new OpenWalletSession({
1351
+ payload: { method: 'openWalletSession', connectId: 'connect-id', mode: 'select-hidden' },
1352
+ });
1353
+ method.init();
1354
+ method.device = createDevice({ typedCall, promptPassphrase }) as any;
1355
+
1356
+ await expect(method.run()).resolves.toMatchObject({ passphraseState: 'host-state' });
1357
+ expect(typedCall).toHaveBeenNthCalledWith(2, 'DeviceSessionAskPassphrase', 'Success', {
1358
+ passphrase: 'cafe\u0301',
1359
+ on_device: false,
1360
+ });
1361
+ });
1362
+
1363
+ test('accepts a Host passphrase at the 50-byte firmware boundary', async () => {
1364
+ const passphrase = 'a'.repeat(50);
1365
+ const typedCall = jest
1366
+ .fn()
1367
+ .mockResolvedValueOnce({ message: { version: 2 } })
1368
+ .mockResolvedValueOnce({ message: {} })
1369
+ .mockResolvedValueOnce({
1370
+ message: { btc_test_address: 'host-state', session_id: 'host-session' },
1371
+ });
1372
+ const promptPassphrase = jest.fn().mockResolvedValue({ passphrase });
1373
+ const method = new OpenWalletSession({
1374
+ payload: { method: 'openWalletSession', connectId: 'connect-id', mode: 'select-hidden' },
1375
+ });
1376
+ method.init();
1377
+ method.device = createDevice({ typedCall, promptPassphrase }) as any;
1378
+
1379
+ await expect(method.run()).resolves.toMatchObject({ passphraseState: 'host-state' });
1380
+ expect(typedCall).toHaveBeenNthCalledWith(2, 'DeviceSessionAskPassphrase', 'Success', {
1381
+ passphrase,
1382
+ on_device: false,
1383
+ });
1384
+ });
1385
+
1386
+ test('selects Attach PIN only when the device reports an existing binding', async () => {
1387
+ const typedCall = jest
1388
+ .fn()
1389
+ .mockResolvedValueOnce({ message: { version: 2 } })
1390
+ .mockResolvedValueOnce({
1391
+ message: { btc_test_address: 'attach-state', session_id: 'attach-session' },
1392
+ });
1393
+ const promptPassphrase = jest.fn().mockResolvedValue({ attachPinOnDevice: true });
1394
+ const method = new OpenWalletSession({
1395
+ payload: { method: 'openWalletSession', connectId: 'connect-id', mode: 'select-hidden' },
1396
+ });
1397
+ method.init();
1398
+ const device = createDevice({ typedCall, promptPassphrase });
1399
+ device.features.attachToPinEnabled = true;
1400
+ method.device = device as any;
1401
+
1402
+ await expect(method.run()).resolves.toMatchObject({
1403
+ walletType: 'hidden',
1404
+ passphraseState: 'attach-state',
1405
+ });
1406
+ expect(promptPassphrase).toHaveBeenCalledWith(
1407
+ {
1408
+ existsAttachPinUser: true,
1409
+ deviceOnly: false,
1410
+ source: 'wallet-session-coordinator',
1411
+ reason: 'open-wallet',
1412
+ },
1413
+ { cancelDeviceOnReject: false }
1414
+ );
1415
+ expect(device.unlockDevice).toHaveBeenCalledWith(DeviceSessionPinType.AttachToPin, {
1416
+ emitUiEvent: false,
1417
+ });
1418
+ expect(typedCall).toHaveBeenCalledWith('DeviceSessionGet', 'DeviceSession', {
1419
+ seed_domains: [],
1420
+ });
1421
+ });
1422
+
1423
+ test('uses the complete Attach PIN wire flow without a main PIN unlock', async () => {
1424
+ const typedCall = jest
1425
+ .fn()
1426
+ .mockResolvedValueOnce({ message: { version: 2 } })
1427
+ .mockResolvedValueOnce({ message: { message: 'PIN verified' } })
1428
+ .mockResolvedValueOnce({
1429
+ message: { btc_test_address: 'attach-state', session_id: 'attach-session' },
1430
+ });
1431
+ const promptPassphrase = jest.fn().mockResolvedValue({ attachPinOnDevice: true });
1432
+ const method = new OpenWalletSession({
1433
+ payload: { method: 'openWalletSession', connectId: 'connect-id', mode: 'select-hidden' },
1434
+ });
1435
+ method.init();
1436
+ const device = createDevice({ typedCall, promptPassphrase });
1437
+ device.features.attachToPinEnabled = true;
1438
+ device.unlockDevice = Device.prototype.unlockDevice.bind(device);
1439
+ method.device = device as any;
1440
+
1441
+ await expect(method.run()).resolves.toMatchObject({
1442
+ walletType: 'hidden',
1443
+ passphraseState: 'attach-state',
1444
+ });
1445
+ expect(device.commands.typedCall.mock.calls).toEqual([
1446
+ ['ProtocolInfoRequest', 'ProtocolInfo', { eventless_wallet_session: true }],
1447
+ ['DeviceSessionAskPin', 'Success', { type: DeviceSessionPinType.AttachToPin }],
1448
+ ['DeviceStatusGet', 'DeviceStatus', {}],
1449
+ ['DeviceSessionGet', 'DeviceSession', { seed_domains: [] }],
1450
+ ]);
1451
+ expect(device.commands.typedCall).not.toHaveBeenCalledWith('DeviceSessionAskPin', 'Success', {
1452
+ type: DeviceSessionPinType.Main,
1453
+ });
1454
+ });
1455
+
1456
+ test.each([
1457
+ ['no selection', {}],
1458
+ ['an empty Host passphrase', { passphrase: '' }],
1459
+ ['more than one selection', { passphrase: 'host hidden wallet', passphraseOnDevice: true }],
1460
+ ])('rejects %s before sending a wallet selection command', async (_name, response) => {
1461
+ const typedCall = jest.fn().mockResolvedValueOnce({ message: { version: 2 } });
1462
+ const promptPassphrase = jest.fn().mockResolvedValue(response);
1463
+ const method = new OpenWalletSession({
1464
+ payload: { method: 'openWalletSession', connectId: 'connect-id', mode: 'select-hidden' },
1465
+ });
1466
+ method.init();
1467
+ method.device = createDevice({ typedCall, promptPassphrase }) as any;
1468
+
1469
+ await expect(method.run()).rejects.toMatchObject({
1470
+ errorCode: HardwareErrorCode.RuntimeError,
1471
+ message: 'Wallet selection must contain exactly one passphrase access mode.',
1472
+ });
1473
+ expect(typedCall).toHaveBeenCalledTimes(1);
1474
+ });
1475
+
1476
+ test('stops before sending a wallet command when the App cancels selection', async () => {
1477
+ const cancellation = new Error('Wallet selection cancelled');
1478
+ const typedCall = jest.fn().mockResolvedValueOnce({ message: { version: 2 } });
1479
+ const promptPassphrase = jest.fn().mockRejectedValue(cancellation);
1480
+ const method = new OpenWalletSession({
1481
+ payload: { method: 'openWalletSession', connectId: 'connect-id', mode: 'select-hidden' },
1482
+ });
1483
+ method.init();
1484
+ method.device = createDevice({ typedCall, promptPassphrase }) as any;
1485
+
1486
+ await expect(method.run()).rejects.toBe(cancellation);
1487
+ expect(typedCall).toHaveBeenCalledTimes(1);
1488
+ });
1489
+
1490
+ test.each(['User cancelled', 'Device disconnected'])(
1491
+ 'does not issue a resume request after the wallet selection reports %s',
1492
+ async message => {
1493
+ const typedCall = jest
1494
+ .fn()
1495
+ .mockResolvedValueOnce({ message: { version: 2 } })
1496
+ .mockRejectedValueOnce(new Error(message));
1497
+ const promptPassphrase = jest.fn().mockResolvedValue({ passphrase: 'host hidden wallet' });
1498
+ const method = new OpenWalletSession({
1499
+ payload: { method: 'openWalletSession', connectId: 'connect-id', mode: 'select-hidden' },
1500
+ });
1501
+ method.init();
1502
+ method.device = createDevice({ typedCall, promptPassphrase }) as any;
1503
+
1504
+ await expect(method.run()).rejects.toThrow(message);
1505
+ expect(typedCall).toHaveBeenCalledTimes(2);
1506
+ expect(typedCall.mock.calls.some(([name]) => name === 'DeviceSessionGet')).toBe(false);
1507
+ }
1508
+ );
1509
+
1510
+ test.each([
1511
+ ['a NUL byte', 'hidden\0wallet'],
1512
+ ['more than 50 UTF-8 bytes', 'a'.repeat(51)],
1513
+ ['more than 50 UTF-8 bytes after NFKD normalization', '\u00e9'.repeat(17)],
1514
+ ['an unpaired UTF-16 surrogate', '\ud800'],
1515
+ ])('rejects a Host passphrase containing %s', async (_name, passphrase) => {
1516
+ const typedCall = jest.fn().mockResolvedValueOnce({ message: { version: 2 } });
1517
+ const promptPassphrase = jest.fn().mockResolvedValue({ passphrase });
1518
+ const method = new OpenWalletSession({
1519
+ payload: { method: 'openWalletSession', connectId: 'connect-id', mode: 'select-hidden' },
1520
+ });
1521
+ method.init();
1522
+ method.device = createDevice({ typedCall, promptPassphrase }) as any;
1523
+
1524
+ await expect(method.run()).rejects.toMatchObject({
1525
+ errorCode: HardwareErrorCode.RuntimeError,
1526
+ message: 'Host passphrase must contain 1 to 50 valid UTF-8 bytes without NUL.',
1527
+ });
1528
+ expect(typedCall).toHaveBeenCalledTimes(1);
1529
+ });
1530
+
1531
+ test('rejects an unavailable Attach PIN selection before asking the device for a PIN', async () => {
1532
+ const typedCall = jest.fn().mockResolvedValueOnce({ message: { version: 2 } });
1533
+ const promptPassphrase = jest.fn().mockResolvedValue({ attachPinOnDevice: true });
1534
+ const method = new OpenWalletSession({
1535
+ payload: { method: 'openWalletSession', connectId: 'connect-id', mode: 'select-hidden' },
1536
+ });
1537
+ method.init();
1538
+ const device = createDevice({ typedCall, promptPassphrase });
1539
+ device.features.attachToPinEnabled = false;
1540
+ method.device = device as any;
1541
+
1542
+ await expect(method.run()).rejects.toMatchObject({
1543
+ errorCode: HardwareErrorCode.RuntimeError,
1544
+ message: 'Attach PIN wallet selection is unavailable on this device.',
1545
+ });
1546
+ expect(device.unlockDevice).not.toHaveBeenCalled();
1547
+ expect(typedCall).toHaveBeenCalledTimes(1);
1548
+ });
1549
+
1550
+ test('does not unlock or retry when wallet preparation reports a late DeviceLocked', async () => {
1551
+ const lockedError = { errorCode: HardwareErrorCode.DeviceLocked };
1552
+ const typedCall = jest
1553
+ .fn()
1554
+ .mockResolvedValueOnce({ message: { version: 2 } })
1555
+ .mockRejectedValueOnce(lockedError)
1556
+ .mockResolvedValueOnce({ message: {} })
1557
+ .mockResolvedValueOnce({
1558
+ message: { btc_test_address: 'hidden-state', session_id: 'hidden-session' },
1559
+ });
1560
+ const promptPassphrase = jest.fn().mockResolvedValue({ passphrase: 'host hidden wallet' });
1561
+ const method = new OpenWalletSession({
1562
+ payload: { method: 'openWalletSession', connectId: 'connect-id', mode: 'select-hidden' },
1563
+ });
1564
+ method.init();
1565
+ const device = createDevice({ typedCall, promptPassphrase });
1566
+ method.device = device as any;
1567
+
1568
+ await expect(method.run()).rejects.toBe(lockedError);
1569
+ expect(device.unlockDevice).not.toHaveBeenCalled();
1570
+ expect(typedCall).toHaveBeenCalledTimes(2);
1571
+ });
1572
+
1573
+ test('does not fall back when wallet preparation is rejected', async () => {
1574
+ const invalidSessionError = { errorCode: HardwareErrorCode.WalletSessionInvalid };
1575
+ const typedCall = jest
1576
+ .fn()
1577
+ .mockResolvedValueOnce({ message: { version: 2 } })
1578
+ .mockRejectedValueOnce(invalidSessionError);
1579
+ const promptPassphrase = jest.fn().mockResolvedValue({ passphrase: 'host hidden wallet' });
1580
+ const method = new OpenWalletSession({
1581
+ payload: { method: 'openWalletSession', connectId: 'connect-id', mode: 'select-hidden' },
1582
+ });
1583
+ method.init();
1584
+ const device = createDevice({ typedCall, promptPassphrase });
1585
+ method.device = device as any;
1586
+
1587
+ await expect(method.run()).rejects.toBe(invalidSessionError);
1588
+ expect(promptPassphrase).toHaveBeenCalledTimes(1);
1589
+ expect(typedCall).toHaveBeenCalledTimes(2);
1590
+ expect(device.updateInternalState).not.toHaveBeenCalled();
1591
+ });
1592
+
1593
+ test.each([
1594
+ {
1595
+ missingField: 'session_id',
1596
+ message: { btc_test_address: 'hidden-state' },
1597
+ },
1598
+ {
1599
+ missingField: 'btc_test_address',
1600
+ message: { session_id: 'hidden-session' },
1601
+ },
1602
+ ])(
1603
+ 'rejects an incomplete Protocol V2 hidden-wallet response missing $missingField',
1604
+ async ({ message }) => {
1605
+ const typedCall = jest
1606
+ .fn()
1607
+ .mockResolvedValueOnce({ message: { version: 2 } })
1608
+ .mockResolvedValueOnce({ message: {} })
1609
+ .mockResolvedValueOnce({ message });
1610
+ const promptPassphrase = jest.fn().mockResolvedValue({ passphrase: 'host hidden wallet' });
1611
+ const method = new OpenWalletSession({
1612
+ payload: { method: 'openWalletSession', connectId: 'connect-id', mode: 'select-hidden' },
1613
+ });
1614
+ method.init();
1615
+ const device = createDevice({ typedCall, promptPassphrase });
1616
+ method.device = device as any;
1617
+
1618
+ await expect(method.run()).rejects.toMatchObject({
1619
+ errorCode: HardwareErrorCode.RuntimeError,
1620
+ message: 'Device returned an incomplete DeviceSession response.',
1621
+ });
1622
+ expect(device.clearInternalState).toHaveBeenCalled();
1623
+ expect(device.updateInternalState).not.toHaveBeenCalled();
1624
+ }
1625
+ );
1626
+
1627
+ test('resumes a known hidden wallet without prompting or device selection', async () => {
1628
+ const typedCall = jest
1629
+ .fn()
1630
+ .mockResolvedValueOnce({ message: { version: 2 } })
1631
+ .mockResolvedValueOnce({
1632
+ message: {
1633
+ btc_test_address: 'hidden-state',
1634
+ session_id: 'renewed-session',
1635
+ },
1636
+ });
1637
+ const promptPassphrase = jest.fn();
1638
+ const method = new OpenWalletSession({
1639
+ payload: {
1640
+ method: 'openWalletSession',
1641
+ connectId: 'connect-id',
1642
+ mode: 'resume-hidden',
1643
+ deviceId: 'device-1',
1644
+ passphraseState: 'hidden-state',
1645
+ },
1646
+ });
1647
+ method.init();
1648
+ deviceWalletSessionStore.set('device-1', 'hidden-state', 'known-session');
1649
+ method.device = createDevice({ typedCall, promptPassphrase }) as any;
1650
+
1651
+ await expect(method.run()).resolves.toEqual({
1652
+ protocol: 'V2',
1653
+ walletType: 'hidden',
1654
+ deviceId: 'device-1',
1655
+ passphraseState: 'hidden-state',
1656
+ resumed: true,
1657
+ });
1658
+ expect(typedCall).toHaveBeenCalledWith('DeviceSessionGet', 'DeviceSession', {
1659
+ session_id: 'known-session',
1660
+ btc_test_address: 'hidden-state',
1661
+ seed_domains: [],
1662
+ });
1663
+ expect(promptPassphrase).not.toHaveBeenCalled();
1664
+ });
1665
+
1666
+ test('validates a locked Protocol V2 resume binding after deviceId refresh', async () => {
1667
+ const typedCall = jest
1668
+ .fn()
1669
+ .mockResolvedValueOnce({ message: { version: 2 } })
1670
+ .mockResolvedValueOnce({
1671
+ message: {
1672
+ btc_test_address: 'hidden-state',
1673
+ session_id: 'renewed-session',
1674
+ },
1675
+ });
1676
+ const method = new OpenWalletSession({
1677
+ payload: {
1678
+ method: 'openWalletSession',
1679
+ connectId: 'connect-id',
1680
+ mode: 'resume-hidden',
1681
+ deviceId: 'device-1',
1682
+ passphraseState: 'hidden-state',
1683
+ },
1684
+ });
1685
+ method.init();
1686
+ deviceWalletSessionStore.set('device-1', 'hidden-state', 'known-session');
1687
+ const device = createDevice({ typedCall });
1688
+ device.features.unlocked = false;
1689
+ device.getDeviceState = jest
1690
+ .fn()
1691
+ .mockResolvedValueOnce({
1692
+ identity: { deviceId: undefined },
1693
+ status: { unlocked: false, passphraseProtection: true },
1694
+ })
1695
+ .mockResolvedValueOnce({
1696
+ identity: { deviceId: 'device-1' },
1697
+ status: {
1698
+ unlocked: true,
1699
+ passphraseProtection: true,
1700
+ unlockedAttachPin: false,
1701
+ },
1702
+ });
1703
+ device.unlockDevice = jest.fn().mockImplementation(() => {
1704
+ device.features.unlocked = true;
1705
+ return Promise.resolve(device.features);
1706
+ });
1707
+ method.device = device as any;
1708
+
1709
+ await expect(method.run()).resolves.toMatchObject({
1710
+ protocol: 'V2',
1711
+ walletType: 'hidden',
1712
+ deviceId: 'device-1',
1713
+ passphraseState: 'hidden-state',
1714
+ resumed: true,
1715
+ });
1716
+ expect(device.unlockDevice).toHaveBeenCalledTimes(1);
1717
+ expect(device.getDeviceState).toHaveBeenCalledTimes(2);
1718
+ expect(typedCall).toHaveBeenCalledWith('DeviceSessionGet', 'DeviceSession', {
1719
+ session_id: 'known-session',
1720
+ btc_test_address: 'hidden-state',
1721
+ seed_domains: [],
1722
+ });
1723
+ const sessionGetCall = typedCall.mock.calls.findIndex(
1724
+ ([requestName]) => requestName === 'DeviceSessionGet'
1725
+ );
1726
+ expect(device.getDeviceState.mock.invocationCallOrder[1]).toBeLessThan(
1727
+ typedCall.mock.invocationCallOrder[sessionGetCall]
1728
+ );
1729
+ });
1730
+
1731
+ test('rejects a Protocol V2 resume when the refreshed deviceId does not match', async () => {
1732
+ const typedCall = jest.fn().mockResolvedValue({
1733
+ message: {
1734
+ btc_test_address: 'hidden-state',
1735
+ session_id: 'renewed-session',
1736
+ },
1737
+ });
1738
+ const method = new OpenWalletSession({
1739
+ payload: {
1740
+ method: 'openWalletSession',
1741
+ connectId: 'connect-id',
1742
+ mode: 'resume-hidden',
1743
+ deviceId: 'device-1',
1744
+ passphraseState: 'hidden-state',
1745
+ },
1746
+ });
1747
+ method.init();
1748
+ deviceWalletSessionStore.set('device-1', 'hidden-state', 'known-session');
1749
+ const device = createDevice({ typedCall });
1750
+ device.features.unlocked = false;
1751
+ device.getDeviceState = jest
1752
+ .fn()
1753
+ .mockResolvedValueOnce({
1754
+ identity: { deviceId: undefined },
1755
+ status: { passphraseProtection: true },
1756
+ })
1757
+ .mockResolvedValueOnce({
1758
+ identity: { deviceId: 'other-device' },
1759
+ status: {
1760
+ unlocked: true,
1761
+ passphraseProtection: true,
1762
+ unlockedAttachPin: false,
1763
+ },
1764
+ });
1765
+ device.unlockDevice = jest.fn().mockImplementation(() => {
1766
+ device.features.unlocked = true;
1767
+ return Promise.resolve(device.features);
1768
+ });
1769
+ method.device = device as any;
1770
+
1771
+ await expect(method.run()).rejects.toMatchObject({
1772
+ errorCode: HardwareErrorCode.DeviceCheckDeviceIdError,
1773
+ });
1774
+ expect(typedCall).not.toHaveBeenCalled();
1775
+ expect(device.clearInternalState).not.toHaveBeenCalled();
1776
+ expect(deviceWalletSessionStore.get('device-1', 'hidden-state')).toBeUndefined();
1777
+ });
1778
+
1779
+ test('reselects the expected hidden wallet when a cached Protocol V2 session is invalid', async () => {
1780
+ const typedCall = jest
1781
+ .fn()
1782
+ .mockResolvedValueOnce({ message: { version: 2 } })
1783
+ .mockRejectedValueOnce(
1784
+ Object.assign(new Error('Invalid session'), {
1785
+ errorCode: HardwareErrorCode.WalletSessionInvalid,
1786
+ })
1787
+ )
1788
+ .mockResolvedValueOnce({ message: {} })
1789
+ .mockResolvedValueOnce({
1790
+ message: {
1791
+ btc_test_address: 'hidden-state',
1792
+ session_id: 'renewed-session',
1793
+ },
1794
+ });
1795
+ const promptPassphrase = jest.fn().mockResolvedValue({ passphrase: 'host hidden wallet' });
1796
+ const method = new OpenWalletSession({
1797
+ payload: {
1798
+ method: 'openWalletSession',
1799
+ connectId: 'connect-id',
1800
+ mode: 'resume-hidden',
1801
+ deviceId: 'device-1',
1802
+ passphraseState: 'hidden-state',
1803
+ },
1804
+ });
1805
+ method.init();
1806
+ deviceWalletSessionStore.set('device-1', 'hidden-state', 'expired-session');
1807
+ method.device = createDevice({ typedCall, promptPassphrase }) as any;
1808
+
1809
+ await expect(method.run()).resolves.toEqual({
1810
+ protocol: 'V2',
1811
+ walletType: 'hidden',
1812
+ deviceId: 'device-1',
1813
+ passphraseState: 'hidden-state',
1814
+ resumed: false,
1815
+ });
1816
+ expect(typedCall).toHaveBeenCalledWith('DeviceSessionGet', 'DeviceSession', {
1817
+ session_id: 'expired-session',
1818
+ btc_test_address: 'hidden-state',
1819
+ seed_domains: [],
1820
+ });
1821
+ expect(promptPassphrase).toHaveBeenCalledTimes(1);
1822
+ expect(deviceWalletSessionStore.get('device-1', 'hidden-state')).toBe('renewed-session');
1823
+ });
1824
+
1825
+ test('selects the expected hidden wallet when Protocol V2 has no cached session', async () => {
1826
+ const typedCall = jest
1827
+ .fn()
1828
+ .mockResolvedValueOnce({ message: { version: 2 } })
1829
+ .mockResolvedValueOnce({
1830
+ message: {
1831
+ btc_test_address: 'hidden-state',
1832
+ session_id: 'new-session',
1833
+ },
1834
+ });
1835
+ const promptPassphrase = jest.fn().mockResolvedValue({ passphrase: 'host hidden wallet' });
1836
+ const method = new OpenWalletSession({
1837
+ payload: {
1838
+ method: 'openWalletSession',
1839
+ connectId: 'connect-id',
1840
+ mode: 'resume-hidden',
1841
+ deviceId: 'device-1',
1842
+ passphraseState: 'hidden-state',
1843
+ },
1844
+ });
1845
+ method.init();
1846
+ method.device = createDevice({ typedCall, promptPassphrase }) as any;
1847
+
1848
+ await expect(method.run()).resolves.toEqual({
1849
+ protocol: 'V2',
1850
+ walletType: 'hidden',
1851
+ deviceId: 'device-1',
1852
+ passphraseState: 'hidden-state',
1853
+ resumed: false,
1854
+ });
1855
+ expect(typedCall).toHaveBeenCalledWith('DeviceSessionGet', 'DeviceSession', {
1856
+ btc_test_address: 'hidden-state',
1857
+ seed_domains: [],
1858
+ });
1859
+ expect(promptPassphrase).not.toHaveBeenCalled();
1860
+ expect(deviceWalletSessionStore.get('device-1', 'hidden-state')).toBe('new-session');
1861
+ });
1862
+
1863
+ test.each([false, true])('keeps deriveCardano API compatibility: %s', async deriveCardano => {
1864
+ const typedCall = jest
1865
+ .fn()
1866
+ .mockResolvedValueOnce({ message: { version: 2 } })
1867
+ .mockResolvedValueOnce({ message: {} })
1868
+ .mockResolvedValueOnce({
1869
+ message: {
1870
+ btc_test_address: 'hidden-state',
1871
+ session_id: 'new-session',
1872
+ },
1873
+ });
1874
+ const method = new OpenWalletSession({
1875
+ payload: {
1876
+ method: 'openWalletSession',
1877
+ connectId: 'connect-id',
1878
+ mode: 'select-hidden',
1879
+ deriveCardano,
1880
+ },
1881
+ });
1882
+ method.init();
1883
+ method.device = createDevice({
1884
+ typedCall,
1885
+ promptPassphrase: jest.fn().mockResolvedValue({ passphrase: 'host hidden wallet' }),
1886
+ }) as any;
1887
+
1888
+ await expect(method.run()).resolves.toMatchObject({
1889
+ walletType: 'hidden',
1890
+ passphraseState: 'hidden-state',
1891
+ });
1892
+ expect(typedCall).toHaveBeenCalledWith('DeviceSessionGet', 'DeviceSession', {
1893
+ seed_domains: [
1894
+ DeviceSessionSeedDomain.SeedDomain_Standard,
1895
+ ...(deriveCardano ? [DeviceSessionSeedDomain.SeedDomain_Cardano] : []),
1896
+ ],
1897
+ });
1898
+ });
1899
+ });