@onekeyfe/hd-core 1.2.0-alpha.52 → 1.2.0-alpha.53

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 (251) hide show
  1. package/__tests__/AllNetworkGetAddressBase.tracing.test.ts +78 -0
  2. package/__tests__/DeviceCommands.test.ts +45 -0
  3. package/__tests__/DeviceEventRegistration.test.ts +6 -0
  4. package/__tests__/check-all-firmware-release-protocol-v2.test.ts +198 -3
  5. package/__tests__/connectSettings.test.ts +5 -47
  6. package/__tests__/device-lifecycle-events.test.ts +105 -3
  7. package/__tests__/deviceUploadNft.test.ts +293 -0
  8. package/__tests__/firmware-update/firmware-update-v2-download-before-boot.test.ts +31 -59
  9. package/__tests__/get-device-state.test.ts +132 -12
  10. package/__tests__/homescreen.test.ts +41 -1
  11. package/__tests__/logBlockEvent.test.ts +1 -1
  12. package/__tests__/method-protocol-support.test.ts +19 -0
  13. package/__tests__/open-wallet-session.test.ts +446 -33
  14. package/__tests__/pro2Nft.test.ts +108 -0
  15. package/__tests__/protocol-binding.test.ts +89 -0
  16. package/__tests__/protocol-v2-resources.test.ts +284 -0
  17. package/__tests__/protocol-v2-ui-lifecycle.test.ts +91 -0
  18. package/__tests__/protocol-v2-unlock-policy.test.ts +201 -35
  19. package/__tests__/protocol-v2.test.ts +1349 -1005
  20. package/__tests__/protocolV2UiInteraction.test.ts +63 -0
  21. package/__tests__/public-pro2-api-boundary.test.ts +1 -0
  22. package/__tests__/search-devices.test.ts +12 -3
  23. package/__tests__/uiProgressThrottle.test.ts +74 -0
  24. package/__tests__/uiPromiseRegistry.test.ts +86 -0
  25. package/dist/api/BaseMethod.d.ts +3 -1
  26. package/dist/api/BaseMethod.d.ts.map +1 -1
  27. package/dist/api/CheckAllFirmwareRelease.d.ts +2 -1
  28. package/dist/api/CheckAllFirmwareRelease.d.ts.map +1 -1
  29. package/dist/api/DetectDeviceConnectProtocol.d.ts +7 -0
  30. package/dist/api/DetectDeviceConnectProtocol.d.ts.map +1 -0
  31. package/dist/api/FirmwareUpdate.d.ts.map +1 -1
  32. package/dist/api/FirmwareUpdateV2.d.ts +0 -11
  33. package/dist/api/FirmwareUpdateV2.d.ts.map +1 -1
  34. package/dist/api/FirmwareUpdateV3.d.ts +3 -7
  35. package/dist/api/FirmwareUpdateV3.d.ts.map +1 -1
  36. package/dist/api/FirmwareUpdateV4.d.ts +11 -25
  37. package/dist/api/FirmwareUpdateV4.d.ts.map +1 -1
  38. package/dist/api/GetPassphraseState.d.ts.map +1 -1
  39. package/dist/api/OpenWalletSession.d.ts.map +1 -1
  40. package/dist/api/SearchDevices.d.ts.map +1 -1
  41. package/dist/api/allnetwork/AllNetworkGetAddressBase.d.ts.map +1 -1
  42. package/dist/api/device/DeviceUnlock.d.ts +2 -2
  43. package/dist/api/device/DeviceUnlock.d.ts.map +1 -1
  44. package/dist/api/device/DeviceUpdateBootloader.d.ts +1 -5
  45. package/dist/api/device/DeviceUpdateBootloader.d.ts.map +1 -1
  46. package/dist/api/firmware/FirmwareUpdateBaseMethod.d.ts +0 -9
  47. package/dist/api/firmware/FirmwareUpdateBaseMethod.d.ts.map +1 -1
  48. package/dist/api/firmware/getBinary.d.ts +0 -7
  49. package/dist/api/firmware/getBinary.d.ts.map +1 -1
  50. package/dist/api/firmware/updateBootloader.d.ts +0 -2
  51. package/dist/api/firmware/updateBootloader.d.ts.map +1 -1
  52. package/dist/api/firmware/uploadFirmware.d.ts +1 -9
  53. package/dist/api/firmware/uploadFirmware.d.ts.map +1 -1
  54. package/dist/api/helpers/protocolV2FileWrite.d.ts +2 -0
  55. package/dist/api/helpers/protocolV2FileWrite.d.ts.map +1 -1
  56. package/dist/api/index.d.ts +2 -0
  57. package/dist/api/index.d.ts.map +1 -1
  58. package/dist/api/protocol-v2/DeviceUploadNft.d.ts +31 -0
  59. package/dist/api/protocol-v2/DeviceUploadNft.d.ts.map +1 -0
  60. package/dist/api/protocol-v2/ProtocolInfoRequest.d.ts.map +1 -1
  61. package/dist/api/protocol-v2/helpers.d.ts.map +1 -1
  62. package/dist/api/stellar/StellarSignTransaction.d.ts +1 -1
  63. package/dist/core/deviceEventRegistration.d.ts +2 -0
  64. package/dist/core/deviceEventRegistration.d.ts.map +1 -1
  65. package/dist/core/index.d.ts.map +1 -1
  66. package/dist/core/uiPromiseRegistry.d.ts +4 -0
  67. package/dist/core/uiPromiseRegistry.d.ts.map +1 -0
  68. package/dist/data-manager/DataManager.d.ts +4 -2
  69. package/dist/data-manager/DataManager.d.ts.map +1 -1
  70. package/dist/data-manager/connectSettings.d.ts.map +1 -1
  71. package/dist/device/Device.d.ts +29 -6
  72. package/dist/device/Device.d.ts.map +1 -1
  73. package/dist/device/DeviceCommands.d.ts +4 -4
  74. package/dist/device/DeviceCommands.d.ts.map +1 -1
  75. package/dist/device/DevicePool.d.ts.map +1 -1
  76. package/dist/events/device.d.ts +4 -0
  77. package/dist/events/device.d.ts.map +1 -1
  78. package/dist/events/logBlockEvent.d.ts.map +1 -1
  79. package/dist/events/ui-promise.d.ts +4 -2
  80. package/dist/events/ui-promise.d.ts.map +1 -1
  81. package/dist/events/ui-request.d.ts +31 -2
  82. package/dist/events/ui-request.d.ts.map +1 -1
  83. package/dist/events/ui-response.d.ts +10 -2
  84. package/dist/events/ui-response.d.ts.map +1 -1
  85. package/dist/index.d.ts +214 -231
  86. package/dist/index.d.ts.map +1 -1
  87. package/dist/index.js +2559 -3497
  88. package/dist/inject.d.ts +6 -1
  89. package/dist/inject.d.ts.map +1 -1
  90. package/dist/lowLevelInject.d.ts.map +1 -1
  91. package/dist/protocols/protocol-v2/features.d.ts.map +1 -1
  92. package/dist/protocols/protocol-v2/resources.d.ts +30 -0
  93. package/dist/protocols/protocol-v2/resources.d.ts.map +1 -0
  94. package/dist/protocols/protocol-v2/uiInteraction.d.ts +7 -4
  95. package/dist/protocols/protocol-v2/uiInteraction.d.ts.map +1 -1
  96. package/dist/protocols/protocol-v2/unlockPolicy.d.ts +1 -3
  97. package/dist/protocols/protocol-v2/unlockPolicy.d.ts.map +1 -1
  98. package/dist/protocols/protocol-v2/unlockPolicyRunner.d.ts +22 -0
  99. package/dist/protocols/protocol-v2/unlockPolicyRunner.d.ts.map +1 -0
  100. package/dist/protocols/protocol-v2/walletSession.d.ts +5 -1
  101. package/dist/protocols/protocol-v2/walletSession.d.ts.map +1 -1
  102. package/dist/topLevelInject.d.ts.map +1 -1
  103. package/dist/types/api/checkAllFirmwareRelease.d.ts +2 -4
  104. package/dist/types/api/checkAllFirmwareRelease.d.ts.map +1 -1
  105. package/dist/types/api/detectDeviceConnectProtocol.d.ts +4 -0
  106. package/dist/types/api/detectDeviceConnectProtocol.d.ts.map +1 -0
  107. package/dist/types/api/deviceUnlock.d.ts +5 -1
  108. package/dist/types/api/deviceUnlock.d.ts.map +1 -1
  109. package/dist/types/api/deviceUpdateBootloader.d.ts +0 -6
  110. package/dist/types/api/deviceUpdateBootloader.d.ts.map +1 -1
  111. package/dist/types/api/export.d.ts +2 -4
  112. package/dist/types/api/export.d.ts.map +1 -1
  113. package/dist/types/api/firmwareUpdate.d.ts +2 -70
  114. package/dist/types/api/firmwareUpdate.d.ts.map +1 -1
  115. package/dist/types/api/index.d.ts +7 -10
  116. package/dist/types/api/index.d.ts.map +1 -1
  117. package/dist/types/api/protocolV2.d.ts +3 -0
  118. package/dist/types/api/protocolV2.d.ts.map +1 -1
  119. package/dist/types/device.d.ts.map +1 -1
  120. package/dist/types/params.d.ts +1 -0
  121. package/dist/types/params.d.ts.map +1 -1
  122. package/dist/types/settings.d.ts +22 -13
  123. package/dist/types/settings.d.ts.map +1 -1
  124. package/dist/utils/deviceFeaturesCompat.d.ts +0 -1
  125. package/dist/utils/deviceFeaturesCompat.d.ts.map +1 -1
  126. package/dist/utils/deviceFeaturesUtils.d.ts +4 -0
  127. package/dist/utils/deviceFeaturesUtils.d.ts.map +1 -1
  128. package/dist/utils/homescreen.d.ts +4 -0
  129. package/dist/utils/homescreen.d.ts.map +1 -1
  130. package/dist/utils/index.d.ts +1 -1
  131. package/dist/utils/index.d.ts.map +1 -1
  132. package/dist/utils/patch.d.ts +1 -1
  133. package/dist/utils/patch.d.ts.map +1 -1
  134. package/dist/utils/pro2Nft.d.ts +31 -0
  135. package/dist/utils/pro2Nft.d.ts.map +1 -0
  136. package/dist/utils/pro2Wallpaper.d.ts +10 -0
  137. package/dist/utils/pro2Wallpaper.d.ts.map +1 -1
  138. package/dist/utils/uiProgressThrottle.d.ts +3 -0
  139. package/dist/utils/uiProgressThrottle.d.ts.map +1 -0
  140. package/package.json +4 -4
  141. package/src/api/BaseMethod.ts +8 -10
  142. package/src/api/CheckAllFirmwareRelease.ts +54 -57
  143. package/src/api/DetectDeviceConnectProtocol.ts +18 -0
  144. package/src/api/FirmwareUpdate.ts +7 -15
  145. package/src/api/FirmwareUpdateV2.ts +126 -316
  146. package/src/api/FirmwareUpdateV3.ts +63 -265
  147. package/src/api/FirmwareUpdateV4.ts +566 -965
  148. package/src/api/GetPassphraseState.ts +7 -1
  149. package/src/api/OpenWalletSession.ts +54 -17
  150. package/src/api/SearchDevices.ts +3 -1
  151. package/src/api/allnetwork/AllNetworkGetAddressBase.ts +32 -11
  152. package/src/api/device/DeviceUnlock.ts +8 -3
  153. package/src/api/device/DeviceUpdateBootloader.ts +6 -122
  154. package/src/api/firmware/FirmwareUpdateBaseMethod.ts +16 -83
  155. package/src/api/firmware/updateBootloader.ts +4 -19
  156. package/src/api/firmware/uploadFirmware.ts +39 -144
  157. package/src/api/helpers/protocolV2FileWrite.ts +11 -4
  158. package/src/api/index.ts +2 -0
  159. package/src/api/protocol-v2/DeviceUploadNft.ts +189 -0
  160. package/src/api/protocol-v2/ProtocolInfoRequest.ts +3 -1
  161. package/src/api/protocol-v2/helpers.ts +1 -0
  162. package/src/core/deviceEventRegistration.ts +4 -0
  163. package/src/core/index.ts +167 -83
  164. package/src/core/uiPromiseRegistry.ts +41 -0
  165. package/src/data/messages/messages-protocol-v2.json +25 -0
  166. package/src/data-manager/DataManager.ts +67 -38
  167. package/src/data-manager/connectSettings.ts +0 -11
  168. package/src/device/Device.ts +258 -42
  169. package/src/device/DeviceCommands.ts +7 -1
  170. package/src/device/DevicePool.ts +7 -2
  171. package/src/events/device.ts +4 -0
  172. package/src/events/logBlockEvent.ts +1 -0
  173. package/src/events/ui-promise.ts +4 -2
  174. package/src/events/ui-request.ts +36 -2
  175. package/src/events/ui-response.ts +12 -2
  176. package/src/index.ts +0 -5
  177. package/src/inject.ts +60 -24
  178. package/src/lowLevelInject.ts +7 -8
  179. package/src/protocols/protocol-v2/features.ts +10 -3
  180. package/src/protocols/protocol-v2/resources.ts +359 -0
  181. package/src/protocols/protocol-v2/uiInteraction.ts +46 -6
  182. package/src/protocols/protocol-v2/unlockPolicy.ts +1 -105
  183. package/src/protocols/protocol-v2/unlockPolicyRunner.ts +118 -0
  184. package/src/protocols/protocol-v2/walletSession.ts +180 -30
  185. package/src/topLevelInject.ts +7 -8
  186. package/src/types/api/checkAllFirmwareRelease.ts +2 -4
  187. package/src/types/api/detectDeviceConnectProtocol.ts +7 -0
  188. package/src/types/api/deviceUnlock.ts +12 -1
  189. package/src/types/api/deviceUpdateBootloader.ts +0 -6
  190. package/src/types/api/export.ts +1 -18
  191. package/src/types/api/firmwareUpdate.ts +3 -76
  192. package/src/types/api/index.ts +13 -14
  193. package/src/types/api/protocolV2.ts +13 -0
  194. package/src/types/device.ts +3 -0
  195. package/src/types/params.ts +7 -1
  196. package/src/types/settings.ts +42 -13
  197. package/src/utils/deviceFeaturesCompat.ts +0 -6
  198. package/src/utils/deviceFeaturesUtils.ts +8 -0
  199. package/src/utils/homescreen.ts +32 -6
  200. package/src/utils/index.ts +6 -1
  201. package/src/utils/pro2Nft.ts +142 -0
  202. package/src/utils/pro2Wallpaper.ts +35 -8
  203. package/src/utils/uiProgressThrottle.ts +63 -0
  204. package/__tests__/device-initialize-timeout.test.ts +0 -56
  205. package/__tests__/firmware-update/check-all-firmware-release.test.ts +0 -175
  206. package/__tests__/firmware-update/firmware-artifact-source.test.ts +0 -119
  207. package/__tests__/firmware-update/firmware-host-binding.test.ts +0 -70
  208. package/__tests__/firmware-update/firmware-preparation-error.test.ts +0 -27
  209. package/__tests__/firmware-update/firmware-update-bootloader-poll.test.ts +0 -200
  210. package/__tests__/firmware-update/firmware-update-capabilities.test.ts +0 -13
  211. package/__tests__/firmware-update/firmware-update-plan-bootloader-identity.test.ts +0 -294
  212. package/__tests__/firmware-update/firmware-update-plan.test.ts +0 -593
  213. package/__tests__/firmware-update/firmware-update-prepared-plan.test.ts +0 -213
  214. package/__tests__/firmware-update/firmware-update-v3-reconnect.test.ts +0 -58
  215. package/__tests__/firmware-update/firmware-upload-source.test.ts +0 -70
  216. package/dist/api/firmware/FirmwareArtifactSource.d.ts +0 -25
  217. package/dist/api/firmware/FirmwareArtifactSource.d.ts.map +0 -1
  218. package/dist/api/firmware/FirmwareHostBinding.d.ts +0 -6
  219. package/dist/api/firmware/FirmwareHostBinding.d.ts.map +0 -1
  220. package/dist/api/firmware/FirmwarePreparationError.d.ts +0 -3
  221. package/dist/api/firmware/FirmwarePreparationError.d.ts.map +0 -1
  222. package/dist/api/firmware/FirmwareUpdateCapabilities.d.ts +0 -3
  223. package/dist/api/firmware/FirmwareUpdateCapabilities.d.ts.map +0 -1
  224. package/dist/api/firmware/FirmwareUpdatePlan.d.ts +0 -25
  225. package/dist/api/firmware/FirmwareUpdatePlan.d.ts.map +0 -1
  226. package/dist/api/firmware/FirmwareUpdatePreparedPlan.d.ts +0 -32
  227. package/dist/api/firmware/FirmwareUpdatePreparedPlan.d.ts.map +0 -1
  228. package/dist/api/firmware/progressThrottle.d.ts +0 -3
  229. package/dist/api/firmware/progressThrottle.d.ts.map +0 -1
  230. package/dist/protocols/protocol-v2/lockedError.d.ts +0 -2
  231. package/dist/protocols/protocol-v2/lockedError.d.ts.map +0 -1
  232. package/dist/protocols/protocol-v2/unlockRetry.d.ts +0 -10
  233. package/dist/protocols/protocol-v2/unlockRetry.d.ts.map +0 -1
  234. package/dist/types/api/firmwareUpdateCapabilities.d.ts +0 -9
  235. package/dist/types/api/firmwareUpdateCapabilities.d.ts.map +0 -1
  236. package/dist/types/api/firmwareUpdatePlan.d.ts +0 -28
  237. package/dist/types/api/firmwareUpdatePlan.d.ts.map +0 -1
  238. package/dist/types/api/firmwareUpdatePreparedPlan.d.ts +0 -42
  239. package/dist/types/api/firmwareUpdatePreparedPlan.d.ts.map +0 -1
  240. package/src/api/firmware/FirmwareArtifactSource.ts +0 -278
  241. package/src/api/firmware/FirmwareHostBinding.ts +0 -100
  242. package/src/api/firmware/FirmwarePreparationError.ts +0 -12
  243. package/src/api/firmware/FirmwareUpdateCapabilities.ts +0 -9
  244. package/src/api/firmware/FirmwareUpdatePlan.ts +0 -772
  245. package/src/api/firmware/FirmwareUpdatePreparedPlan.ts +0 -415
  246. package/src/api/firmware/progressThrottle.ts +0 -44
  247. package/src/protocols/protocol-v2/lockedError.ts +0 -10
  248. package/src/protocols/protocol-v2/unlockRetry.ts +0 -109
  249. package/src/types/api/firmwareUpdateCapabilities.ts +0 -9
  250. package/src/types/api/firmwareUpdatePlan.ts +0 -38
  251. package/src/types/api/firmwareUpdatePreparedPlan.ts +0 -53
@@ -1,10 +1,15 @@
1
1
  import { EDeviceType, HardwareErrorCode } from '@onekeyfe/hd-shared';
2
- import { DeviceSessionPinType } from '@onekeyfe/hd-transport';
2
+ import { DeviceSessionPinType, DeviceSessionSeedDomain } from '@onekeyfe/hd-transport';
3
3
 
4
4
  import GetPassphraseState from '../src/api/GetPassphraseState';
5
5
  import OpenWalletSession from '../src/api/OpenWalletSession';
6
6
  import { Device } from '../src/device/Device';
7
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';
8
13
 
9
14
  jest.mock('../src/data/config', () => ({
10
15
  getSDKVersion: jest.fn(() => '1.0.0'),
@@ -26,6 +31,7 @@ const createDevice = ({
26
31
  unlocked: true,
27
32
  passphraseProtection,
28
33
  attachToPinEnabled: false,
34
+ unlockedAttachPin: false,
29
35
  },
30
36
  commands: {
31
37
  typedCall: jest.fn((request: string, ...args: unknown[]) => {
@@ -43,10 +49,21 @@ const createDevice = ({
43
49
  }),
44
50
  promptPassphrase,
45
51
  },
46
- getDeviceState: jest.fn().mockResolvedValue({
47
- identity: { deviceId: 'device-1' },
48
- status: { passphraseProtection },
49
- }),
52
+ ensureProtocolV2RuntimeContext: jest.fn(() =>
53
+ device.commands.typedCall('ProtocolInfoRequest', 'ProtocolInfo', {
54
+ eventless_wallet_session: true,
55
+ })
56
+ ),
57
+ getDeviceState: jest.fn(() =>
58
+ Promise.resolve({
59
+ identity: { deviceId: 'device-1' },
60
+ status: {
61
+ unlocked: device.features.unlocked,
62
+ passphraseProtection: device.features.passphraseProtection,
63
+ unlockedAttachPin: device.features.unlockedAttachPin,
64
+ },
65
+ })
66
+ ),
50
67
  getCurrentDeviceId: () => 'device-1',
51
68
  getCurrentPassphraseProtection: () => passphraseProtection,
52
69
  getInternalState: () =>
@@ -82,6 +99,148 @@ describe('openWalletSession', () => {
82
99
  deviceWalletSessionStore.clear();
83
100
  });
84
101
 
102
+ test('selects the standard wallet explicitly when passphrase is enabled', async () => {
103
+ const typedCall = jest.fn((request: string) => {
104
+ if (request === 'ProtocolInfoRequest') {
105
+ return { message: { version: 2 } };
106
+ }
107
+ if (request === 'DeviceSessionAskPassphrase') {
108
+ return { message: {} };
109
+ }
110
+ if (request === 'DeviceSessionGet') {
111
+ return {
112
+ message: {
113
+ btc_test_address: 'standard-state',
114
+ session_id: 'standard-session',
115
+ },
116
+ };
117
+ }
118
+ throw new Error(`Unexpected request: ${request}`);
119
+ });
120
+ const device = createDevice({ typedCall });
121
+
122
+ await expect(
123
+ getProtocolV2WalletSession(device as any, {
124
+ onlyMainPin: true,
125
+ })
126
+ ).resolves.toMatchObject({
127
+ passphraseState: 'standard-state',
128
+ newSession: 'standard-session',
129
+ });
130
+
131
+ expect(device.unlockDevice).not.toHaveBeenCalled();
132
+ expect(typedCall).toHaveBeenCalledWith('DeviceSessionAskPassphrase', 'Success', {
133
+ passphrase: '',
134
+ on_device: false,
135
+ });
136
+ expect(typedCall).toHaveBeenCalledWith('DeviceSessionGet', 'DeviceSession', {
137
+ seed_domains: [],
138
+ });
139
+ });
140
+
141
+ test('opens the standard wallet directly when passphrase is disabled', async () => {
142
+ const typedCall = jest.fn((request: string) => {
143
+ if (request === 'ProtocolInfoRequest') {
144
+ return { message: { version: 2 } };
145
+ }
146
+ if (request === 'DeviceSessionGet') {
147
+ return {
148
+ message: {
149
+ btc_test_address: 'standard-state',
150
+ session_id: 'standard-session',
151
+ },
152
+ };
153
+ }
154
+ throw new Error(`Unexpected request: ${request}`);
155
+ });
156
+ const device = createDevice({ passphraseProtection: false, typedCall });
157
+
158
+ await getProtocolV2WalletSession(device as any, { onlyMainPin: true });
159
+
160
+ expect(typedCall).not.toHaveBeenCalledWith(
161
+ 'DeviceSessionAskPassphrase',
162
+ 'Success',
163
+ expect.anything()
164
+ );
165
+ expect(typedCall).toHaveBeenCalledWith('DeviceSessionGet', 'DeviceSession', {
166
+ seed_domains: [],
167
+ });
168
+ });
169
+
170
+ test('does not treat an Attach PIN DeviceStatus as the Main wallet', async () => {
171
+ const typedCall = jest.fn((request: string) => {
172
+ if (request === 'ProtocolInfoRequest') {
173
+ return { message: { version: 2 } };
174
+ }
175
+ if (request === 'DeviceSessionAskPassphrase') {
176
+ return { message: {} };
177
+ }
178
+ if (request === 'DeviceSessionGet') {
179
+ return {
180
+ message: {
181
+ btc_test_address: 'standard-state',
182
+ session_id: 'standard-session',
183
+ },
184
+ };
185
+ }
186
+ throw new Error(`Unexpected request: ${request}`);
187
+ });
188
+ const device = createDevice({ typedCall });
189
+ device.features.unlockedAttachPin = true;
190
+
191
+ await getProtocolV2WalletSession(device as any, { onlyMainPin: true });
192
+
193
+ expect(device.unlockDevice).toHaveBeenCalledWith(DeviceSessionPinType.Main, {
194
+ source: 'wallet-session-coordinator',
195
+ reason: 'open-wallet',
196
+ deviceOnly: true,
197
+ });
198
+ });
199
+
200
+ test('unlocks a locked Protocol V2 device before restoring a wallet session', async () => {
201
+ const device = {
202
+ commands: {
203
+ typedCall: jest.fn().mockResolvedValue({ message: { unlocked: false } }),
204
+ },
205
+ isBootloader: jest.fn().mockReturnValue(false),
206
+ isProtocolV2: jest.fn().mockReturnValue(true),
207
+ isRomloader: jest.fn().mockReturnValue(false),
208
+ state: { status: { unlocked: false } },
209
+ unlockDevice: jest.fn().mockResolvedValue(undefined),
210
+ updateProtocolV2Status: jest.fn(function updateProtocolV2Status(
211
+ this: { state: { status: { unlocked: boolean } } },
212
+ status: { unlocked?: boolean }
213
+ ) {
214
+ this.state.status.unlocked = status.unlocked ?? this.state.status.unlocked;
215
+ }),
216
+ };
217
+
218
+ await expect(ensureProtocolV2WalletSessionUnlocked(device as any)).resolves.toBe(true);
219
+ expect(device.commands.typedCall).toHaveBeenCalledWith('DeviceStatusGet', 'DeviceStatus', {});
220
+ expect(device.unlockDevice).toHaveBeenCalledWith(DeviceSessionPinType.Main, {
221
+ source: 'unlock-coordinator',
222
+ reason: 'device-locked',
223
+ deviceOnly: true,
224
+ });
225
+ });
226
+
227
+ test('does not request PIN when wallet-session recovery finds the device unlocked', async () => {
228
+ const device = {
229
+ commands: {
230
+ typedCall: jest.fn().mockResolvedValue({ message: { unlocked: true } }),
231
+ },
232
+ isBootloader: jest.fn().mockReturnValue(false),
233
+ isProtocolV2: jest.fn().mockReturnValue(true),
234
+ isRomloader: jest.fn().mockReturnValue(false),
235
+ state: { status: { unlocked: true } },
236
+ unlockDevice: jest.fn(),
237
+ updateProtocolV2Status: jest.fn(),
238
+ };
239
+
240
+ await expect(ensureProtocolV2WalletSessionUnlocked(device as any)).resolves.toBe(false);
241
+ expect(device.unlockDevice).not.toHaveBeenCalled();
242
+ });
243
+
85
244
  test.each([{ useEmptyPassphrase: true }, { initSession: true }, {}])(
86
245
  'requires the explicit mode in the new public API: %p',
87
246
  legacyParams => {
@@ -133,7 +292,9 @@ describe('openWalletSession', () => {
133
292
  passphrase: 'host hidden wallet',
134
293
  on_device: false,
135
294
  });
136
- expect(typedCall).toHaveBeenNthCalledWith(3, 'DeviceSessionGet', 'DeviceSession', {});
295
+ expect(typedCall).toHaveBeenNthCalledWith(3, 'DeviceSessionGet', 'DeviceSession', {
296
+ seed_domains: [],
297
+ });
137
298
  });
138
299
 
139
300
  test('keeps Legacy Protocol V1 getPassphraseState parameterless', async () => {
@@ -251,7 +412,9 @@ describe('openWalletSession', () => {
251
412
  passphrase: 'host hidden wallet',
252
413
  on_device: false,
253
414
  });
254
- expect(typedCall).toHaveBeenNthCalledWith(3, 'DeviceSessionGet', 'DeviceSession', {});
415
+ expect(typedCall).toHaveBeenNthCalledWith(3, 'DeviceSessionGet', 'DeviceSession', {
416
+ seed_domains: [],
417
+ });
255
418
  });
256
419
 
257
420
  test('refreshes Pro2 status and unlocks before selecting a hidden wallet when locked', async () => {
@@ -273,10 +436,24 @@ describe('openWalletSession', () => {
273
436
  typedCall,
274
437
  promptPassphrase: jest.fn().mockResolvedValue({ passphraseOnDevice: true }),
275
438
  });
276
- device.getDeviceState = jest.fn().mockResolvedValue({
277
- identity: { deviceId: 'device-1' },
278
- status: { unlocked: false, passphraseProtection: true },
279
- });
439
+ device.getDeviceState = jest
440
+ .fn()
441
+ .mockResolvedValueOnce({
442
+ identity: { deviceId: 'device-1' },
443
+ status: {
444
+ unlocked: false,
445
+ passphraseProtection: null,
446
+ unlockedAttachPin: null,
447
+ },
448
+ })
449
+ .mockResolvedValue({
450
+ identity: { deviceId: 'device-1' },
451
+ status: {
452
+ unlocked: true,
453
+ passphraseProtection: true,
454
+ unlockedAttachPin: false,
455
+ },
456
+ });
280
457
  method.device = device as any;
281
458
 
282
459
  await expect(method.run()).resolves.toMatchObject({
@@ -330,6 +507,12 @@ describe('openWalletSession', () => {
330
507
  status.passphrase_enabled ?? device.features.passphraseProtection;
331
508
  return device.features;
332
509
  });
510
+ device.unlockDevice = jest.fn().mockImplementation(() => {
511
+ device.features.unlocked = true;
512
+ device.features.passphraseProtection = true;
513
+ device.features.unlockedAttachPin = false;
514
+ return Promise.resolve(device.features);
515
+ });
333
516
  method.device = device as any;
334
517
 
335
518
  await expect(method.run()).resolves.toEqual({
@@ -339,10 +522,98 @@ describe('openWalletSession', () => {
339
522
  passphraseState: 'hidden-state-after-unlock',
340
523
  resumed: false,
341
524
  });
342
- expect(device.unlockDevice).not.toHaveBeenCalled();
525
+ expect(device.unlockDevice).toHaveBeenCalledTimes(1);
343
526
  expect(promptPassphrase).toHaveBeenCalledTimes(1);
344
527
  });
345
528
 
529
+ test('unlocks before wallet selection when the Protocol V2 wallet status is unknown', async () => {
530
+ const typedCall = jest
531
+ .fn()
532
+ .mockResolvedValueOnce({ message: { version: 2 } })
533
+ .mockResolvedValueOnce({ message: {} })
534
+ .mockResolvedValueOnce({
535
+ message: {
536
+ btc_test_address: 'hidden-state-after-unknown-status',
537
+ session_id: 'hidden-session-after-unknown-status',
538
+ },
539
+ });
540
+ const promptPassphrase = jest.fn().mockResolvedValue({ passphrase: 'host hidden wallet' });
541
+ const method = new OpenWalletSession({
542
+ payload: { method: 'openWalletSession', connectId: 'connect-id', mode: 'select-hidden' },
543
+ });
544
+ method.init();
545
+ const device = createDevice({ typedCall, promptPassphrase });
546
+ device.getDeviceState = jest
547
+ .fn()
548
+ .mockResolvedValueOnce({
549
+ identity: { deviceId: 'device-1' },
550
+ status: {
551
+ unlocked: undefined,
552
+ passphraseProtection: null,
553
+ unlockedAttachPin: null,
554
+ },
555
+ })
556
+ .mockResolvedValue({
557
+ identity: { deviceId: 'device-1' },
558
+ status: {
559
+ unlocked: true,
560
+ passphraseProtection: true,
561
+ unlockedAttachPin: false,
562
+ },
563
+ });
564
+ device.unlockDevice = jest.fn().mockImplementation(() => {
565
+ device.features.unlocked = true;
566
+ device.features.passphraseProtection = true;
567
+ device.features.unlockedAttachPin = false;
568
+ return Promise.resolve(device.features);
569
+ });
570
+ method.device = device as any;
571
+
572
+ await expect(method.run()).resolves.toMatchObject({
573
+ walletType: 'hidden',
574
+ passphraseState: 'hidden-state-after-unknown-status',
575
+ });
576
+ expect(device.unlockDevice).toHaveBeenCalledTimes(1);
577
+ expect(device.unlockDevice.mock.invocationCallOrder[0]).toBeLessThan(
578
+ promptPassphrase.mock.invocationCallOrder[0]
579
+ );
580
+ });
581
+
582
+ test('rejects wallet selection when the post-unlock Protocol V2 status remains incomplete', async () => {
583
+ const typedCall = jest
584
+ .fn()
585
+ .mockResolvedValueOnce({ message: { version: 2 } })
586
+ .mockResolvedValueOnce({ message: {} })
587
+ .mockResolvedValueOnce({
588
+ message: {
589
+ btc_test_address: 'hidden-state-from-incomplete-status',
590
+ session_id: 'hidden-session-from-incomplete-status',
591
+ },
592
+ });
593
+ const promptPassphrase = jest.fn().mockResolvedValue({ passphrase: 'host hidden wallet' });
594
+ const method = new OpenWalletSession({
595
+ payload: { method: 'openWalletSession', connectId: 'connect-id', mode: 'select-hidden' },
596
+ });
597
+ method.init();
598
+ const device = createDevice({ typedCall, promptPassphrase });
599
+ device.getDeviceState = jest.fn().mockResolvedValue({
600
+ identity: { deviceId: 'device-1' },
601
+ status: {
602
+ unlocked: true,
603
+ passphraseProtection: null,
604
+ unlockedAttachPin: null,
605
+ },
606
+ });
607
+ device.unlockDevice = jest.fn().mockResolvedValue(device.features);
608
+ method.device = device as any;
609
+
610
+ await expect(method.run()).rejects.toMatchObject({
611
+ errorCode: HardwareErrorCode.DeviceInitializeFailed,
612
+ });
613
+ expect(device.unlockDevice).toHaveBeenCalledTimes(1);
614
+ expect(promptPassphrase).not.toHaveBeenCalled();
615
+ });
616
+
346
617
  test('rejects a Pro2 standard-wallet fallback after selecting a hidden wallet', async () => {
347
618
  const typedCall = jest
348
619
  .fn()
@@ -408,7 +679,9 @@ describe('openWalletSession', () => {
408
679
  passphrase: 'host hidden wallet',
409
680
  on_device: false,
410
681
  });
411
- expect(typedCall).toHaveBeenCalledWith('DeviceSessionGet', 'DeviceSession', {});
682
+ expect(typedCall).toHaveBeenCalledWith('DeviceSessionGet', 'DeviceSession', {
683
+ seed_domains: [],
684
+ });
412
685
  expect(promptPassphrase).toHaveBeenCalled();
413
686
  expect(deviceWalletSessionStore.get('device-1', 'new-hidden-state')).toBe('new-hidden-session');
414
687
  });
@@ -479,6 +752,8 @@ describe('openWalletSession', () => {
479
752
  });
480
753
  expect(typedCall).toHaveBeenCalledWith('DeviceSessionGet', 'DeviceSession', {
481
754
  session_id: 'known-session',
755
+ btc_test_address: 'hidden-state',
756
+ seed_domains: [],
482
757
  });
483
758
  expect(promptPassphrase).not.toHaveBeenCalled();
484
759
  });
@@ -630,6 +905,7 @@ describe('openWalletSession', () => {
630
905
  const typedCall = jest
631
906
  .fn()
632
907
  .mockResolvedValueOnce({ message: { version: 2 } })
908
+ .mockResolvedValueOnce({ message: {} })
633
909
  .mockResolvedValueOnce({
634
910
  message: {
635
911
  btc_test_address: 'main-wallet-state',
@@ -652,12 +928,18 @@ describe('openWalletSession', () => {
652
928
  passphraseState: null,
653
929
  resumed: false,
654
930
  });
655
- expect(typedCall).toHaveBeenCalledTimes(2);
931
+ expect(typedCall).toHaveBeenCalledTimes(3);
656
932
  expect(typedCall).toHaveBeenCalledWith('ProtocolInfoRequest', 'ProtocolInfo', {
657
933
  eventless_wallet_session: true,
658
934
  });
659
- expect(device.unlockDevice).toHaveBeenCalledWith(DeviceSessionPinType.Main);
660
- expect(typedCall).toHaveBeenCalledWith('DeviceSessionGet', 'DeviceSession', {});
935
+ expect(device.unlockDevice).not.toHaveBeenCalled();
936
+ expect(typedCall).toHaveBeenCalledWith('DeviceSessionAskPassphrase', 'Success', {
937
+ passphrase: '',
938
+ on_device: false,
939
+ });
940
+ expect(typedCall).toHaveBeenCalledWith('DeviceSessionGet', 'DeviceSession', {
941
+ seed_domains: [],
942
+ });
661
943
  expect(promptPassphrase).not.toHaveBeenCalled();
662
944
  expect(device.passphraseState).toBeUndefined();
663
945
  });
@@ -666,6 +948,7 @@ describe('openWalletSession', () => {
666
948
  const typedCall = jest
667
949
  .fn()
668
950
  .mockResolvedValueOnce({ message: { version: 2 } })
951
+ .mockResolvedValueOnce({ message: {} })
669
952
  .mockResolvedValueOnce({
670
953
  message: {
671
954
  btc_test_address: 'main-wallet-state',
@@ -686,7 +969,11 @@ describe('openWalletSession', () => {
686
969
  })
687
970
  .mockResolvedValueOnce({
688
971
  identity: { deviceId: 'device-1' },
689
- status: { passphraseProtection: true },
972
+ status: {
973
+ unlocked: true,
974
+ passphraseProtection: true,
975
+ unlockedAttachPin: false,
976
+ },
690
977
  });
691
978
  device.unlockDevice = jest.fn().mockImplementation(() => {
692
979
  device.features.unlocked = true;
@@ -701,7 +988,11 @@ describe('openWalletSession', () => {
701
988
  passphraseState: null,
702
989
  resumed: false,
703
990
  });
704
- expect(device.unlockDevice).toHaveBeenCalledWith(DeviceSessionPinType.Main);
991
+ expect(device.unlockDevice).toHaveBeenCalledWith(DeviceSessionPinType.Main, {
992
+ source: 'wallet-session-coordinator',
993
+ reason: 'open-wallet',
994
+ deviceOnly: true,
995
+ });
705
996
  expect(device.getDeviceState).toHaveBeenCalledTimes(2);
706
997
  });
707
998
 
@@ -709,6 +1000,7 @@ describe('openWalletSession', () => {
709
1000
  const typedCall = jest
710
1001
  .fn()
711
1002
  .mockResolvedValueOnce({ message: { version: 2 } })
1003
+ .mockResolvedValueOnce({ message: {} })
712
1004
  .mockResolvedValueOnce({
713
1005
  message: {
714
1006
  btc_test_address: 'main-wallet-state',
@@ -721,6 +1013,24 @@ describe('openWalletSession', () => {
721
1013
  method.init();
722
1014
  const device = createDevice({ typedCall });
723
1015
  device.features.unlockedAttachPin = true;
1016
+ device.getDeviceState = jest
1017
+ .fn()
1018
+ .mockResolvedValueOnce({
1019
+ identity: { deviceId: 'device-1' },
1020
+ status: {
1021
+ unlocked: true,
1022
+ unlockedAttachPin: true,
1023
+ passphraseProtection: true,
1024
+ },
1025
+ })
1026
+ .mockResolvedValue({
1027
+ identity: { deviceId: 'device-1' },
1028
+ status: {
1029
+ unlocked: true,
1030
+ unlockedAttachPin: false,
1031
+ passphraseProtection: true,
1032
+ },
1033
+ });
724
1034
  device.unlockDevice = jest.fn().mockImplementation(() => {
725
1035
  device.features.unlockedAttachPin = false;
726
1036
  return Promise.resolve(device.features);
@@ -731,8 +1041,14 @@ describe('openWalletSession', () => {
731
1041
  walletType: 'standard',
732
1042
  passphraseState: null,
733
1043
  });
734
- expect(device.unlockDevice).toHaveBeenCalledWith(DeviceSessionPinType.Main);
735
- expect(typedCall).toHaveBeenCalledWith('DeviceSessionGet', 'DeviceSession', {});
1044
+ expect(device.unlockDevice).toHaveBeenCalledWith(DeviceSessionPinType.Main, {
1045
+ source: 'wallet-session-coordinator',
1046
+ reason: 'open-wallet',
1047
+ deviceOnly: true,
1048
+ });
1049
+ expect(typedCall).toHaveBeenCalledWith('DeviceSessionGet', 'DeviceSession', {
1050
+ seed_domains: [],
1051
+ });
736
1052
  });
737
1053
 
738
1054
  test('selects a hidden wallet without exposing the internal device session', async () => {
@@ -766,7 +1082,9 @@ describe('openWalletSession', () => {
766
1082
  passphrase: 'host hidden wallet',
767
1083
  on_device: false,
768
1084
  });
769
- expect(typedCall).toHaveBeenCalledWith('DeviceSessionGet', 'DeviceSession', {});
1085
+ expect(typedCall).toHaveBeenCalledWith('DeviceSessionGet', 'DeviceSession', {
1086
+ seed_domains: [],
1087
+ });
770
1088
  expect(promptPassphrase).toHaveBeenCalled();
771
1089
  expect(device.passphraseState).toBeUndefined();
772
1090
  expect(deviceWalletSessionStore.get('device-1', 'hidden-state')).toBe('hidden-session');
@@ -795,7 +1113,9 @@ describe('openWalletSession', () => {
795
1113
  passphrase: 'host hidden wallet',
796
1114
  on_device: false,
797
1115
  });
798
- expect(typedCall).toHaveBeenCalledWith('DeviceSessionGet', 'DeviceSession', {});
1116
+ expect(typedCall).toHaveBeenCalledWith('DeviceSessionGet', 'DeviceSession', {
1117
+ seed_domains: [],
1118
+ });
799
1119
  });
800
1120
 
801
1121
  test('selects an on-device passphrase wallet with an explicit on_device request', async () => {
@@ -811,7 +1131,28 @@ describe('openWalletSession', () => {
811
1131
  payload: { method: 'openWalletSession', connectId: 'connect-id', mode: 'select-hidden' },
812
1132
  });
813
1133
  method.init();
814
- method.device = createDevice({ typedCall, promptPassphrase }) as any;
1134
+ const device = createDevice({ typedCall, promptPassphrase });
1135
+ const passphraseInteraction = {
1136
+ interactionId: 'interaction-1',
1137
+ phaseId: 'interaction-1:phase-1',
1138
+ sequence: 1,
1139
+ phase: 'passphrase',
1140
+ transition: 'start',
1141
+ protocol: 'V2',
1142
+ } as const;
1143
+ const passphraseOnDeviceInteraction = {
1144
+ interactionId: 'interaction-1',
1145
+ phaseId: 'interaction-1:phase-2',
1146
+ sequence: 2,
1147
+ phase: 'passphrase-on-device',
1148
+ transition: 'start',
1149
+ protocol: 'V2',
1150
+ } as const;
1151
+ device.createProtocolV2UiPhaseMetadata = jest
1152
+ .fn()
1153
+ .mockReturnValueOnce(passphraseInteraction)
1154
+ .mockReturnValueOnce(passphraseOnDeviceInteraction);
1155
+ method.device = device as any;
815
1156
 
816
1157
  await expect(method.run()).resolves.toMatchObject({
817
1158
  walletType: 'hidden',
@@ -820,7 +1161,19 @@ describe('openWalletSession', () => {
820
1161
  expect(typedCall).toHaveBeenNthCalledWith(2, 'DeviceSessionAskPassphrase', 'Success', {
821
1162
  on_device: true,
822
1163
  });
823
- expect(typedCall).toHaveBeenNthCalledWith(3, 'DeviceSessionGet', 'DeviceSession', {});
1164
+ expect(typedCall).toHaveBeenNthCalledWith(3, 'DeviceSessionGet', 'DeviceSession', {
1165
+ seed_domains: [],
1166
+ });
1167
+ expect(device.createProtocolV2UiPhaseMetadata).toHaveBeenNthCalledWith(
1168
+ 2,
1169
+ 'passphrase-on-device',
1170
+ 'start'
1171
+ );
1172
+ expect(device.emit).toHaveBeenCalledWith(DEVICE.PASSPHRASE_ON_DEVICE, device, {
1173
+ source: 'wallet-session-coordinator',
1174
+ reason: 'open-wallet',
1175
+ interaction: passphraseOnDeviceInteraction,
1176
+ });
824
1177
  });
825
1178
 
826
1179
  test('forwards a host passphrase to Pro2 firmware', async () => {
@@ -862,7 +1215,9 @@ describe('openWalletSession', () => {
862
1215
  passphrase: 'host hidden wallet',
863
1216
  on_device: false,
864
1217
  });
865
- expect(typedCall).toHaveBeenNthCalledWith(3, 'DeviceSessionGet', 'DeviceSession', {});
1218
+ expect(typedCall).toHaveBeenNthCalledWith(3, 'DeviceSessionGet', 'DeviceSession', {
1219
+ seed_domains: [],
1220
+ });
866
1221
  });
867
1222
 
868
1223
  test('normalizes a Unicode Host passphrase before sending it to Pro2 firmware', async () => {
@@ -939,8 +1294,12 @@ describe('openWalletSession', () => {
939
1294
  },
940
1295
  { cancelDeviceOnReject: false }
941
1296
  );
942
- expect(device.unlockDevice).toHaveBeenCalledWith(DeviceSessionPinType.AttachToPin);
943
- expect(typedCall).toHaveBeenCalledWith('DeviceSessionGet', 'DeviceSession', {});
1297
+ expect(device.unlockDevice).toHaveBeenCalledWith(DeviceSessionPinType.AttachToPin, {
1298
+ emitUiEvent: false,
1299
+ });
1300
+ expect(typedCall).toHaveBeenCalledWith('DeviceSessionGet', 'DeviceSession', {
1301
+ seed_domains: [],
1302
+ });
944
1303
  });
945
1304
 
946
1305
  test('uses the complete Attach PIN wire flow without a main PIN unlock', async () => {
@@ -969,7 +1328,7 @@ describe('openWalletSession', () => {
969
1328
  ['ProtocolInfoRequest', 'ProtocolInfo', { eventless_wallet_session: true }],
970
1329
  ['DeviceSessionAskPin', 'Success', { type: DeviceSessionPinType.AttachToPin }],
971
1330
  ['DeviceStatusGet', 'DeviceStatus', {}],
972
- ['DeviceSessionGet', 'DeviceSession', {}],
1331
+ ['DeviceSessionGet', 'DeviceSession', { seed_domains: [] }],
973
1332
  ]);
974
1333
  expect(device.commands.typedCall).not.toHaveBeenCalledWith('DeviceSessionAskPin', 'Success', {
975
1334
  type: DeviceSessionPinType.Main,
@@ -1026,7 +1385,7 @@ describe('openWalletSession', () => {
1026
1385
 
1027
1386
  await expect(method.run()).rejects.toThrow(message);
1028
1387
  expect(typedCall).toHaveBeenCalledTimes(2);
1029
- expect(typedCall).not.toHaveBeenCalledWith('DeviceSessionGet', 'DeviceSession', {});
1388
+ expect(typedCall.mock.calls.some(([name]) => name === 'DeviceSessionGet')).toBe(false);
1030
1389
  }
1031
1390
  );
1032
1391
 
@@ -1180,6 +1539,8 @@ describe('openWalletSession', () => {
1180
1539
  });
1181
1540
  expect(typedCall).toHaveBeenCalledWith('DeviceSessionGet', 'DeviceSession', {
1182
1541
  session_id: 'known-session',
1542
+ btc_test_address: 'hidden-state',
1543
+ seed_domains: [],
1183
1544
  });
1184
1545
  expect(promptPassphrase).not.toHaveBeenCalled();
1185
1546
  });
@@ -1215,7 +1576,11 @@ describe('openWalletSession', () => {
1215
1576
  })
1216
1577
  .mockResolvedValueOnce({
1217
1578
  identity: { deviceId: 'device-1' },
1218
- status: { passphraseProtection: true },
1579
+ status: {
1580
+ unlocked: true,
1581
+ passphraseProtection: true,
1582
+ unlockedAttachPin: false,
1583
+ },
1219
1584
  });
1220
1585
  device.unlockDevice = jest.fn().mockImplementation(() => {
1221
1586
  device.features.unlocked = true;
@@ -1234,6 +1599,8 @@ describe('openWalletSession', () => {
1234
1599
  expect(device.getDeviceState).toHaveBeenCalledTimes(2);
1235
1600
  expect(typedCall).toHaveBeenCalledWith('DeviceSessionGet', 'DeviceSession', {
1236
1601
  session_id: 'known-session',
1602
+ btc_test_address: 'hidden-state',
1603
+ seed_domains: [],
1237
1604
  });
1238
1605
  const sessionGetCall = typedCall.mock.calls.findIndex(
1239
1606
  ([requestName]) => requestName === 'DeviceSessionGet'
@@ -1271,7 +1638,11 @@ describe('openWalletSession', () => {
1271
1638
  })
1272
1639
  .mockResolvedValueOnce({
1273
1640
  identity: { deviceId: 'other-device' },
1274
- status: { passphraseProtection: true },
1641
+ status: {
1642
+ unlocked: true,
1643
+ passphraseProtection: true,
1644
+ unlockedAttachPin: false,
1645
+ },
1275
1646
  });
1276
1647
  device.unlockDevice = jest.fn().mockImplementation(() => {
1277
1648
  device.features.unlocked = true;
@@ -1326,6 +1697,8 @@ describe('openWalletSession', () => {
1326
1697
  });
1327
1698
  expect(typedCall).toHaveBeenCalledWith('DeviceSessionGet', 'DeviceSession', {
1328
1699
  session_id: 'expired-session',
1700
+ btc_test_address: 'hidden-state',
1701
+ seed_domains: [],
1329
1702
  });
1330
1703
  expect(promptPassphrase).toHaveBeenCalledTimes(1);
1331
1704
  expect(deviceWalletSessionStore.get('device-1', 'hidden-state')).toBe('renewed-session');
@@ -1335,7 +1708,6 @@ describe('openWalletSession', () => {
1335
1708
  const typedCall = jest
1336
1709
  .fn()
1337
1710
  .mockResolvedValueOnce({ message: { version: 2 } })
1338
- .mockResolvedValueOnce({ message: {} })
1339
1711
  .mockResolvedValueOnce({
1340
1712
  message: {
1341
1713
  btc_test_address: 'hidden-state',
@@ -1362,7 +1734,48 @@ describe('openWalletSession', () => {
1362
1734
  passphraseState: 'hidden-state',
1363
1735
  resumed: false,
1364
1736
  });
1365
- expect(promptPassphrase).toHaveBeenCalledTimes(1);
1737
+ expect(typedCall).toHaveBeenCalledWith('DeviceSessionGet', 'DeviceSession', {
1738
+ btc_test_address: 'hidden-state',
1739
+ seed_domains: [],
1740
+ });
1741
+ expect(promptPassphrase).not.toHaveBeenCalled();
1366
1742
  expect(deviceWalletSessionStore.get('device-1', 'hidden-state')).toBe('new-session');
1367
1743
  });
1744
+
1745
+ test.each([false, true])('keeps deriveCardano API compatibility: %s', async deriveCardano => {
1746
+ const typedCall = jest
1747
+ .fn()
1748
+ .mockResolvedValueOnce({ message: { version: 2 } })
1749
+ .mockResolvedValueOnce({ message: {} })
1750
+ .mockResolvedValueOnce({
1751
+ message: {
1752
+ btc_test_address: 'hidden-state',
1753
+ session_id: 'new-session',
1754
+ },
1755
+ });
1756
+ const method = new OpenWalletSession({
1757
+ payload: {
1758
+ method: 'openWalletSession',
1759
+ connectId: 'connect-id',
1760
+ mode: 'select-hidden',
1761
+ deriveCardano,
1762
+ },
1763
+ });
1764
+ method.init();
1765
+ method.device = createDevice({
1766
+ typedCall,
1767
+ promptPassphrase: jest.fn().mockResolvedValue({ passphrase: 'host hidden wallet' }),
1768
+ }) as any;
1769
+
1770
+ await expect(method.run()).resolves.toMatchObject({
1771
+ walletType: 'hidden',
1772
+ passphraseState: 'hidden-state',
1773
+ });
1774
+ expect(typedCall).toHaveBeenCalledWith('DeviceSessionGet', 'DeviceSession', {
1775
+ seed_domains: [
1776
+ DeviceSessionSeedDomain.SeedDomain_Standard,
1777
+ ...(deriveCardano ? [DeviceSessionSeedDomain.SeedDomain_Cardano] : []),
1778
+ ],
1779
+ });
1780
+ });
1368
1781
  });