@onekeyfe/hd-core 1.2.0-alpha.41 → 1.2.0-alpha.43

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 (202) hide show
  1. package/__tests__/AllNetworkGetAddressBase.tracing.test.ts +78 -0
  2. package/__tests__/DeviceEventRegistration.test.ts +6 -0
  3. package/__tests__/check-all-firmware-release-protocol-v2.test.ts +14 -1
  4. package/__tests__/connectSettings.test.ts +5 -47
  5. package/__tests__/deviceUploadNft.test.ts +272 -0
  6. package/__tests__/firmware-update/firmware-update-v2-download-before-boot.test.ts +31 -59
  7. package/__tests__/get-device-state.test.ts +132 -12
  8. package/__tests__/homescreen.test.ts +41 -1
  9. package/__tests__/logBlockEvent.test.ts +1 -1
  10. package/__tests__/open-wallet-session.test.ts +366 -31
  11. package/__tests__/pro2Nft.test.ts +108 -0
  12. package/__tests__/protocol-v2-resources.test.ts +204 -0
  13. package/__tests__/protocol-v2-ui-lifecycle.test.ts +91 -0
  14. package/__tests__/protocol-v2-unlock-policy.test.ts +196 -35
  15. package/__tests__/protocol-v2.test.ts +568 -957
  16. package/__tests__/protocolV2UiInteraction.test.ts +63 -0
  17. package/__tests__/public-pro2-api-boundary.test.ts +1 -0
  18. package/__tests__/uiProgressThrottle.test.ts +74 -0
  19. package/dist/api/BaseMethod.d.ts +3 -1
  20. package/dist/api/BaseMethod.d.ts.map +1 -1
  21. package/dist/api/CheckAllFirmwareRelease.d.ts.map +1 -1
  22. package/dist/api/FirmwareUpdateV2.d.ts +0 -11
  23. package/dist/api/FirmwareUpdateV2.d.ts.map +1 -1
  24. package/dist/api/FirmwareUpdateV3.d.ts +3 -7
  25. package/dist/api/FirmwareUpdateV3.d.ts.map +1 -1
  26. package/dist/api/FirmwareUpdateV4.d.ts +6 -25
  27. package/dist/api/FirmwareUpdateV4.d.ts.map +1 -1
  28. package/dist/api/GetPassphraseState.d.ts.map +1 -1
  29. package/dist/api/OpenWalletSession.d.ts.map +1 -1
  30. package/dist/api/allnetwork/AllNetworkGetAddressBase.d.ts.map +1 -1
  31. package/dist/api/device/DeviceUpdateBootloader.d.ts +1 -5
  32. package/dist/api/device/DeviceUpdateBootloader.d.ts.map +1 -1
  33. package/dist/api/firmware/FirmwareUpdateBaseMethod.d.ts +0 -8
  34. package/dist/api/firmware/FirmwareUpdateBaseMethod.d.ts.map +1 -1
  35. package/dist/api/firmware/getBinary.d.ts +0 -7
  36. package/dist/api/firmware/getBinary.d.ts.map +1 -1
  37. package/dist/api/firmware/updateBootloader.d.ts +0 -2
  38. package/dist/api/firmware/updateBootloader.d.ts.map +1 -1
  39. package/dist/api/firmware/uploadFirmware.d.ts +1 -9
  40. package/dist/api/firmware/uploadFirmware.d.ts.map +1 -1
  41. package/dist/api/helpers/protocolV2FileWrite.d.ts +2 -0
  42. package/dist/api/helpers/protocolV2FileWrite.d.ts.map +1 -1
  43. package/dist/api/index.d.ts +1 -0
  44. package/dist/api/index.d.ts.map +1 -1
  45. package/dist/api/protocol-v2/DeviceUploadNft.d.ts +31 -0
  46. package/dist/api/protocol-v2/DeviceUploadNft.d.ts.map +1 -0
  47. package/dist/api/protocol-v2/ProtocolInfoRequest.d.ts.map +1 -1
  48. package/dist/api/stellar/StellarSignTransaction.d.ts +1 -1
  49. package/dist/core/deviceEventRegistration.d.ts +2 -0
  50. package/dist/core/deviceEventRegistration.d.ts.map +1 -1
  51. package/dist/core/index.d.ts.map +1 -1
  52. package/dist/data-manager/DataManager.d.ts +3 -2
  53. package/dist/data-manager/DataManager.d.ts.map +1 -1
  54. package/dist/data-manager/connectSettings.d.ts.map +1 -1
  55. package/dist/device/Device.d.ts +24 -4
  56. package/dist/device/Device.d.ts.map +1 -1
  57. package/dist/device/DeviceCommands.d.ts +4 -4
  58. package/dist/events/device.d.ts +4 -0
  59. package/dist/events/device.d.ts.map +1 -1
  60. package/dist/events/logBlockEvent.d.ts.map +1 -1
  61. package/dist/events/ui-request.d.ts +28 -2
  62. package/dist/events/ui-request.d.ts.map +1 -1
  63. package/dist/index.d.ts +115 -184
  64. package/dist/index.d.ts.map +1 -1
  65. package/dist/index.js +2051 -3349
  66. package/dist/inject.d.ts.map +1 -1
  67. package/dist/lowLevelInject.d.ts.map +1 -1
  68. package/dist/protocols/protocol-v2/features.d.ts.map +1 -1
  69. package/dist/protocols/protocol-v2/resources.d.ts +30 -0
  70. package/dist/protocols/protocol-v2/resources.d.ts.map +1 -0
  71. package/dist/protocols/protocol-v2/uiInteraction.d.ts +7 -4
  72. package/dist/protocols/protocol-v2/uiInteraction.d.ts.map +1 -1
  73. package/dist/protocols/protocol-v2/unlockPolicy.d.ts +1 -3
  74. package/dist/protocols/protocol-v2/unlockPolicy.d.ts.map +1 -1
  75. package/dist/protocols/protocol-v2/unlockPolicyRunner.d.ts +22 -0
  76. package/dist/protocols/protocol-v2/unlockPolicyRunner.d.ts.map +1 -0
  77. package/dist/protocols/protocol-v2/walletSession.d.ts +4 -1
  78. package/dist/protocols/protocol-v2/walletSession.d.ts.map +1 -1
  79. package/dist/topLevelInject.d.ts.map +1 -1
  80. package/dist/types/api/checkAllFirmwareRelease.d.ts +1 -4
  81. package/dist/types/api/checkAllFirmwareRelease.d.ts.map +1 -1
  82. package/dist/types/api/deviceUpdateBootloader.d.ts +0 -6
  83. package/dist/types/api/deviceUpdateBootloader.d.ts.map +1 -1
  84. package/dist/types/api/export.d.ts +1 -4
  85. package/dist/types/api/export.d.ts.map +1 -1
  86. package/dist/types/api/firmwareUpdate.d.ts +0 -69
  87. package/dist/types/api/firmwareUpdate.d.ts.map +1 -1
  88. package/dist/types/api/index.d.ts +3 -10
  89. package/dist/types/api/index.d.ts.map +1 -1
  90. package/dist/types/api/protocolV2.d.ts +3 -0
  91. package/dist/types/api/protocolV2.d.ts.map +1 -1
  92. package/dist/types/settings.d.ts +12 -13
  93. package/dist/types/settings.d.ts.map +1 -1
  94. package/dist/utils/deviceFeaturesUtils.d.ts +2 -0
  95. package/dist/utils/deviceFeaturesUtils.d.ts.map +1 -1
  96. package/dist/utils/homescreen.d.ts +4 -0
  97. package/dist/utils/homescreen.d.ts.map +1 -1
  98. package/dist/utils/index.d.ts +1 -1
  99. package/dist/utils/index.d.ts.map +1 -1
  100. package/dist/utils/patch.d.ts +1 -1
  101. package/dist/utils/patch.d.ts.map +1 -1
  102. package/dist/utils/pro2Nft.d.ts +31 -0
  103. package/dist/utils/pro2Nft.d.ts.map +1 -0
  104. package/dist/utils/pro2Wallpaper.d.ts +10 -0
  105. package/dist/utils/pro2Wallpaper.d.ts.map +1 -1
  106. package/dist/utils/uiProgressThrottle.d.ts +3 -0
  107. package/dist/utils/uiProgressThrottle.d.ts.map +1 -0
  108. package/package.json +4 -4
  109. package/src/api/BaseMethod.ts +8 -10
  110. package/src/api/CheckAllFirmwareRelease.ts +40 -53
  111. package/src/api/FirmwareUpdateV2.ts +120 -298
  112. package/src/api/FirmwareUpdateV3.ts +57 -263
  113. package/src/api/FirmwareUpdateV4.ts +307 -912
  114. package/src/api/GetPassphraseState.ts +7 -1
  115. package/src/api/OpenWalletSession.ts +54 -17
  116. package/src/api/allnetwork/AllNetworkGetAddressBase.ts +32 -11
  117. package/src/api/device/DeviceUnlock.ts +1 -1
  118. package/src/api/device/DeviceUpdateBootloader.ts +6 -122
  119. package/src/api/firmware/FirmwareUpdateBaseMethod.ts +0 -57
  120. package/src/api/firmware/updateBootloader.ts +4 -19
  121. package/src/api/firmware/uploadFirmware.ts +22 -140
  122. package/src/api/helpers/protocolV2FileWrite.ts +11 -4
  123. package/src/api/index.ts +1 -0
  124. package/src/api/protocol-v2/DeviceUploadNft.ts +189 -0
  125. package/src/api/protocol-v2/ProtocolInfoRequest.ts +3 -1
  126. package/src/core/deviceEventRegistration.ts +4 -0
  127. package/src/core/index.ts +136 -77
  128. package/src/data/messages/messages-protocol-v2.json +68 -0
  129. package/src/data-manager/DataManager.ts +63 -38
  130. package/src/data-manager/connectSettings.ts +0 -11
  131. package/src/device/Device.ts +203 -21
  132. package/src/events/device.ts +4 -0
  133. package/src/events/logBlockEvent.ts +1 -0
  134. package/src/events/ui-request.ts +33 -2
  135. package/src/index.ts +0 -5
  136. package/src/inject.ts +3 -22
  137. package/src/lowLevelInject.ts +1 -5
  138. package/src/protocols/protocol-v2/features.ts +9 -2
  139. package/src/protocols/protocol-v2/resources.ts +233 -0
  140. package/src/protocols/protocol-v2/uiInteraction.ts +46 -6
  141. package/src/protocols/protocol-v2/unlockPolicy.ts +1 -105
  142. package/src/protocols/protocol-v2/unlockPolicyRunner.ts +124 -0
  143. package/src/protocols/protocol-v2/walletSession.ts +154 -30
  144. package/src/topLevelInject.ts +1 -5
  145. package/src/types/api/checkAllFirmwareRelease.ts +1 -4
  146. package/src/types/api/deviceUpdateBootloader.ts +0 -6
  147. package/src/types/api/export.ts +0 -18
  148. package/src/types/api/firmwareUpdate.ts +0 -76
  149. package/src/types/api/index.ts +2 -14
  150. package/src/types/api/protocolV2.ts +13 -0
  151. package/src/types/settings.ts +26 -13
  152. package/src/utils/deviceFeaturesUtils.ts +4 -0
  153. package/src/utils/homescreen.ts +32 -6
  154. package/src/utils/index.ts +6 -1
  155. package/src/utils/pro2Nft.ts +142 -0
  156. package/src/utils/pro2Wallpaper.ts +35 -8
  157. package/src/utils/uiProgressThrottle.ts +63 -0
  158. package/__tests__/firmware-update/check-all-firmware-release.test.ts +0 -175
  159. package/__tests__/firmware-update/firmware-artifact-source.test.ts +0 -119
  160. package/__tests__/firmware-update/firmware-host-binding.test.ts +0 -70
  161. package/__tests__/firmware-update/firmware-preparation-error.test.ts +0 -27
  162. package/__tests__/firmware-update/firmware-update-capabilities.test.ts +0 -13
  163. package/__tests__/firmware-update/firmware-update-plan.test.ts +0 -593
  164. package/__tests__/firmware-update/firmware-update-prepared-plan.test.ts +0 -213
  165. package/__tests__/firmware-update/firmware-update-v3-reconnect.test.ts +0 -58
  166. package/__tests__/firmware-update/firmware-upload-source.test.ts +0 -70
  167. package/dist/api/firmware/FirmwareArtifactSource.d.ts +0 -25
  168. package/dist/api/firmware/FirmwareArtifactSource.d.ts.map +0 -1
  169. package/dist/api/firmware/FirmwareHostBinding.d.ts +0 -6
  170. package/dist/api/firmware/FirmwareHostBinding.d.ts.map +0 -1
  171. package/dist/api/firmware/FirmwarePreparationError.d.ts +0 -3
  172. package/dist/api/firmware/FirmwarePreparationError.d.ts.map +0 -1
  173. package/dist/api/firmware/FirmwareUpdateCapabilities.d.ts +0 -3
  174. package/dist/api/firmware/FirmwareUpdateCapabilities.d.ts.map +0 -1
  175. package/dist/api/firmware/FirmwareUpdatePlan.d.ts +0 -25
  176. package/dist/api/firmware/FirmwareUpdatePlan.d.ts.map +0 -1
  177. package/dist/api/firmware/FirmwareUpdatePreparedPlan.d.ts +0 -30
  178. package/dist/api/firmware/FirmwareUpdatePreparedPlan.d.ts.map +0 -1
  179. package/dist/api/firmware/progressThrottle.d.ts +0 -3
  180. package/dist/api/firmware/progressThrottle.d.ts.map +0 -1
  181. package/dist/protocols/protocol-v2/lockedError.d.ts +0 -2
  182. package/dist/protocols/protocol-v2/lockedError.d.ts.map +0 -1
  183. package/dist/protocols/protocol-v2/unlockRetry.d.ts +0 -10
  184. package/dist/protocols/protocol-v2/unlockRetry.d.ts.map +0 -1
  185. package/dist/types/api/firmwareUpdateCapabilities.d.ts +0 -9
  186. package/dist/types/api/firmwareUpdateCapabilities.d.ts.map +0 -1
  187. package/dist/types/api/firmwareUpdatePlan.d.ts +0 -28
  188. package/dist/types/api/firmwareUpdatePlan.d.ts.map +0 -1
  189. package/dist/types/api/firmwareUpdatePreparedPlan.d.ts +0 -42
  190. package/dist/types/api/firmwareUpdatePreparedPlan.d.ts.map +0 -1
  191. package/src/api/firmware/FirmwareArtifactSource.ts +0 -278
  192. package/src/api/firmware/FirmwareHostBinding.ts +0 -100
  193. package/src/api/firmware/FirmwarePreparationError.ts +0 -12
  194. package/src/api/firmware/FirmwareUpdateCapabilities.ts +0 -9
  195. package/src/api/firmware/FirmwareUpdatePlan.ts +0 -745
  196. package/src/api/firmware/FirmwareUpdatePreparedPlan.ts +0 -392
  197. package/src/api/firmware/progressThrottle.ts +0 -44
  198. package/src/protocols/protocol-v2/lockedError.ts +0 -10
  199. package/src/protocols/protocol-v2/unlockRetry.ts +0 -109
  200. package/src/types/api/firmwareUpdateCapabilities.ts +0 -9
  201. package/src/types/api/firmwareUpdatePlan.ts +0 -38
  202. package/src/types/api/firmwareUpdatePreparedPlan.ts +0 -53
package/src/core/index.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import semver from 'semver';
2
2
  import EventEmitter from 'events';
3
- import { TRANSPORT_EVENT } from '@onekeyfe/hd-transport';
3
+ import { DeviceSessionPinType, TRANSPORT_EVENT } from '@onekeyfe/hd-transport';
4
4
  import {
5
5
  ERRORS,
6
6
  ERROR_CODES_REQUIRE_RELEASE,
@@ -55,11 +55,12 @@ import DeviceConnector from '../device/DeviceConnector';
55
55
  import RequestQueue from './RequestQueue';
56
56
  import { registerHardwareUiEventListeners } from './deviceEventRegistration';
57
57
  import { getSynchronize } from '../utils/getSynchronize';
58
- import { runMethodWithUnlockRetry } from '../protocols/protocol-v2/unlockRetry';
58
+ import { runMethodWithUnlockPolicy } from '../protocols/protocol-v2/unlockPolicyRunner';
59
59
  import {
60
60
  ProtocolV2UiInteractionCoordinator,
61
61
  isProtocolV2UiEnabled,
62
62
  } from '../protocols/protocol-v2/uiInteraction';
63
+ import { createUiProgressMessageFilter } from '../utils/uiProgressThrottle';
63
64
 
64
65
  import type { ConnectSettings, Features, KnownDevice } from '../types';
65
66
  import type { CoreMessage, IFrameCallMessage, UiPromise, UiPromiseResponse } from '../events';
@@ -154,7 +155,12 @@ export const callAPI = async (context: CoreContext, message: CoreMessage) => {
154
155
  try {
155
156
  method = findMethod(message as IFrameCallMessage);
156
157
  method.connector = _connector;
157
- method.postMessage = postMessage;
158
+ const shouldPostMessage = createUiProgressMessageFilter();
159
+ method.postMessage = event => {
160
+ if (shouldPostMessage(event)) {
161
+ postMessage(event);
162
+ }
163
+ };
158
164
  method.setContext?.(context);
159
165
 
160
166
  method.requestContext = createRequestContext(method.responseID, method.name, {
@@ -406,6 +412,8 @@ const onCallDevice = async (
406
412
 
407
413
  registerHardwareUiEventListeners(device, {
408
414
  pin: onDevicePinHandler,
415
+ pinOnDevice: onEnterPinOnDeviceHandler,
416
+ pinOnDeviceComplete: onPinOnDeviceCompleteHandler,
409
417
  button: onDeviceButtonHandler,
410
418
  passphrase: message.payload.useEmptyPassphrase
411
419
  ? onEmptyPassphraseHandler
@@ -421,6 +429,9 @@ const onCallDevice = async (
421
429
  );
422
430
 
423
431
  const protocolV2UiCoordinator = new ProtocolV2UiInteractionCoordinator(device, postMessage);
432
+ let protocolV2Operation = false;
433
+ let protocolV2CloseEmitted = false;
434
+ device.beginProtocolV2UiInteraction();
424
435
  device.on(
425
436
  DEVICE.SELECT_DEVICE_FOR_SWITCH_FIRMWARE_WEB_DEVICE,
426
437
  onSelectDeviceForSwitchFirmwareWebDeviceHandler
@@ -438,6 +449,7 @@ const onCallDevice = async (
438
449
  // Protocol is established from an active device response during acquire/initialize.
439
450
  // Reject unsupported methods before any method-specific device command is sent.
440
451
  method.assertProtocolSupported(device.getProtocol(), device.getCurrentFirmwareType());
452
+ protocolV2Operation = device.isProtocolV2();
441
453
 
442
454
  // check firmware version
443
455
  const versionRange = device.getCurrentMethodVersionRange(
@@ -547,89 +559,92 @@ const onCallDevice = async (
547
559
  );
548
560
  }
549
561
 
550
- if (method.deviceId && method.checkDeviceId) {
551
- const isSameDeviceID = await checkLiveDeviceId(device, method.deviceId);
552
- if (!isSameDeviceID) {
553
- return Promise.reject(ERRORS.TypedError(HardwareErrorCode.DeviceCheckDeviceIdError));
554
- }
555
- }
556
-
557
- /**
558
- * check firmware release info
559
- */
560
- method.checkFirmwareRelease();
562
+ try {
563
+ let deviceIdCheckedDuringUnlockPreflight = false;
564
+ const response = await runMethodWithUnlockPolicy<object>(method, device, {
565
+ uiCoordinator: protocolV2UiCoordinator,
566
+ afterStatusBeforeUnlock: () => {
567
+ if (method.deviceId && method.checkDeviceId) {
568
+ if (!device.checkDeviceId(method.deviceId)) {
569
+ throw ERRORS.TypedError(HardwareErrorCode.DeviceCheckDeviceIdError);
570
+ }
571
+ deviceIdCheckedDuringUnlockPreflight = true;
572
+ }
573
+ },
574
+ prepare: async () => {
575
+ if (method.deviceId && method.checkDeviceId && !deviceIdCheckedDuringUnlockPreflight) {
576
+ const isSameDeviceID = await checkLiveDeviceId(device, method.deviceId);
577
+ if (!isSameDeviceID) {
578
+ throw ERRORS.TypedError(HardwareErrorCode.DeviceCheckDeviceIdError);
579
+ }
580
+ }
561
581
 
562
- /**
563
- * check additional supported feature
564
- */
565
- method.checkDeviceSupportFeature();
582
+ /**
583
+ * check firmware release info
584
+ */
585
+ method.checkFirmwareRelease();
566
586
 
567
- // reconfigure messages
568
- if (_deviceList && device.features && !device.isProtocolV2()) {
569
- await TransportManager.reconfigure(device.features);
570
- }
587
+ /**
588
+ * check additional supported feature
589
+ */
590
+ method.checkDeviceSupportFeature();
571
591
 
572
- // Check to see if it is safe to use Passphrase
573
- checkPassphraseEnableState(method, device.features);
592
+ // reconfigure messages
593
+ if (_deviceList && device.features && !device.isProtocolV2()) {
594
+ await TransportManager.reconfigure(device.features);
595
+ }
574
596
 
575
- if (shouldCheckPassphraseState(method, device)) {
576
- // check version
577
- const support = device.supportNewPassphrase();
578
- if (!support.support) {
579
- return Promise.reject(
580
- ERRORS.TypedError(
581
- HardwareErrorCode.DeviceNotSupportPassphrase,
582
- `Device not support passphrase, please update to ${support.require}`,
583
- {
584
- require: support.require,
597
+ // Check to see if it is safe to use Passphrase
598
+ checkPassphraseEnableState(method, device.features);
599
+
600
+ if (shouldCheckPassphraseState(method, device)) {
601
+ // check version
602
+ const support = device.supportNewPassphrase();
603
+ if (!support.support) {
604
+ throw ERRORS.TypedError(
605
+ HardwareErrorCode.DeviceNotSupportPassphrase,
606
+ `Device not support passphrase, please update to ${support.require}`,
607
+ {
608
+ require: support.require,
609
+ }
610
+ );
585
611
  }
586
- )
587
- );
588
- }
589
612
 
590
- // Check Device passphrase State
591
- const passphraseStateSafety = await device.checkPassphraseStateSafety(
592
- method.payload?.passphraseState,
593
- method.payload?.useEmptyPassphrase,
594
- method.payload?.skipPassphraseCheck
595
- );
613
+ // Check Device passphrase State after the Protocol V2 pre-unlock gate.
614
+ const passphraseStateSafety = await device.checkPassphraseStateSafety(
615
+ method.payload?.passphraseState,
616
+ method.payload?.useEmptyPassphrase,
617
+ method.payload?.skipPassphraseCheck,
618
+ hasDeriveCardano(method)
619
+ );
596
620
 
597
- // Double check, handles the special case of Touch/Pro
598
- checkPassphraseEnableState(method, device.features);
621
+ // Double check, handles the special case of Touch/Pro
622
+ checkPassphraseEnableState(method, device.features);
599
623
 
600
- if (!passphraseStateSafety) {
601
- DevicePool.clearDeviceCache(method.payload.connectId);
602
- return Promise.reject(
603
- ERRORS.TypedError(HardwareErrorCode.DeviceCheckPassphraseStateError)
604
- );
605
- }
606
-
607
- // close pin popup window
608
- postMessage(createUiMessage(UI_REQUEST.CLOSE_UI_PIN_WINDOW));
609
- }
624
+ if (!passphraseStateSafety) {
625
+ DevicePool.clearDeviceCache(method.payload.connectId);
626
+ throw ERRORS.TypedError(HardwareErrorCode.DeviceCheckPassphraseStateError);
627
+ }
610
628
 
611
- // Automatic check safety_check level for Kovan, Ropsten, Rinkeby, Goerli test networks.
612
- try {
613
- await method.checkSafetyLevelOnTestNet();
614
- } catch (e) {
615
- const error =
616
- e instanceof HardwareError
617
- ? e
618
- : ERRORS.TypedError(HardwareErrorCode.RuntimeError, 'open safety check failed.');
619
- // messageResponse = createResponseMessage(method.responseID, false, { error });
620
- // requestQueue.resolveRequest(method.responseID, messageResponse);
621
- // return;
622
- throw error;
623
- }
629
+ // Protocol V2 emits the PIN phase completion from DeviceSessionAskPin.
630
+ // Keep the legacy compatibility close for Protocol V1 only.
631
+ if (!device.isProtocolV2()) {
632
+ postMessage(createUiMessage(UI_REQUEST.CLOSE_UI_PIN_WINDOW));
633
+ }
634
+ }
624
635
 
625
- method.device?.commands?.checkDisposed();
636
+ // Automatic check safety_check level for Kovan, Ropsten, Rinkeby, Goerli test networks.
637
+ try {
638
+ await method.checkSafetyLevelOnTestNet();
639
+ } catch (e) {
640
+ throw e instanceof HardwareError
641
+ ? e
642
+ : ERRORS.TypedError(HardwareErrorCode.RuntimeError, 'open safety check failed.');
643
+ }
626
644
 
627
- try {
628
- const response: object = await runMethodWithUnlockRetry(
629
- method,
630
- device,
631
- protocolV2UiCoordinator
632
- );
645
+ method.device?.commands?.checkDisposed();
646
+ },
647
+ });
633
648
  messageResponse = createResponseMessage(method.responseID, true, response);
634
649
  requestQueue.resolveRequest(method.responseID, messageResponse);
635
650
  completeMethodRequestContext(method);
@@ -647,7 +662,12 @@ const onCallDevice = async (
647
662
  throw error;
648
663
  }
649
664
  } finally {
650
- protocolV2UiCoordinator.close();
665
+ if (isProtocolV2UiEnabled(method)) {
666
+ protocolV2CloseEmitted = protocolV2UiCoordinator.close({
667
+ ensureOperationClose: protocolV2Operation,
668
+ protocolV2Operation,
669
+ });
670
+ }
651
671
  }
652
672
  };
653
673
  Log.debug('Call API - Device Run: ', device.mainId);
@@ -702,7 +722,15 @@ const onCallDevice = async (
702
722
  requestQueue.releaseTask(method.responseID);
703
723
 
704
724
  if (isProtocolV2UiEnabled(method)) {
705
- closePopup();
725
+ if (!protocolV2CloseEmitted && protocolV2Operation) {
726
+ protocolV2CloseEmitted = protocolV2UiCoordinator.close({
727
+ ensureOperationClose: true,
728
+ protocolV2Operation: true,
729
+ });
730
+ }
731
+ if (!protocolV2CloseEmitted) {
732
+ closePopup();
733
+ }
706
734
  }
707
735
 
708
736
  cleanup();
@@ -1279,6 +1307,7 @@ const onDevicePassphraseHandler = async (
1279
1307
  source: requestPayload.source,
1280
1308
  reason: requestPayload.reason,
1281
1309
  expectedPassphraseState: requestPayload.expectedPassphraseState,
1310
+ ...(requestPayload.interaction ? { interaction: requestPayload.interaction } : {}),
1282
1311
  })
1283
1312
  );
1284
1313
  // wait for passphrase
@@ -1308,6 +1337,7 @@ const onEnterPassphraseOnDeviceHandler = (
1308
1337
  passphraseState: device.passphraseState,
1309
1338
  source: requestPayload?.source,
1310
1339
  reason: requestPayload?.reason,
1340
+ ...(requestPayload?.interaction ? { interaction: requestPayload.interaction } : {}),
1311
1341
  })
1312
1342
  );
1313
1343
  };
@@ -1321,10 +1351,39 @@ const onEnterAttachPinOnDeviceHandler = (
1321
1351
  type: 'ButtonRequest_AttachPin',
1322
1352
  source: requestPayload?.source,
1323
1353
  reason: requestPayload?.reason,
1354
+ ...(requestPayload?.interaction ? { interaction: requestPayload.interaction } : {}),
1324
1355
  })
1325
1356
  );
1326
1357
  };
1327
1358
 
1359
+ const onEnterPinOnDeviceHandler = (
1360
+ ...[device, pinType, metadata]: [...DeviceEvents['pin_on_device']]
1361
+ ) => {
1362
+ postMessage(
1363
+ createUiMessage(UI_REQUEST.REQUEST_PIN, {
1364
+ device: device.toMessageObject() as KnownDevice,
1365
+ type:
1366
+ pinType === DeviceSessionPinType.AttachToPin
1367
+ ? 'ButtonRequest_AttachPin'
1368
+ : 'ButtonRequest_PinEntry',
1369
+ source: metadata?.source,
1370
+ reason: metadata?.reason,
1371
+ deviceOnly: metadata?.deviceOnly ?? true,
1372
+ completion: metadata?.completion,
1373
+ method: metadata?.method,
1374
+ page: metadata?.page,
1375
+ operation: metadata?.operation,
1376
+ ...(metadata?.interaction ? { interaction: metadata.interaction } : {}),
1377
+ })
1378
+ );
1379
+ };
1380
+
1381
+ const onPinOnDeviceCompleteHandler = (
1382
+ ...[_, metadata]: [...DeviceEvents['pin_on_device_complete']]
1383
+ ) => {
1384
+ postMessage(createUiMessage(UI_REQUEST.CLOSE_UI_PIN_WINDOW, metadata));
1385
+ };
1386
+
1328
1387
  const onSelectDeviceInBootloaderForWebDeviceHandler = async (
1329
1388
  ...[device, callback]: [...DeviceEvents['select_device_in_bootloader_for_web_device']]
1330
1389
  ) => {
@@ -450,6 +450,8 @@
450
450
  "MessageType_DeviceInfo": 60601,
451
451
  "MessageType_DeviceStatusGet": 60602,
452
452
  "MessageType_DeviceStatus": 60603,
453
+ "MessageType_ResourceInventoryGet": 60604,
454
+ "MessageType_ResourceInventory": 60605,
453
455
  "MessageType_FilesystemPermissionFix": 60800,
454
456
  "MessageType_FilesystemPathInfo": 60801,
455
457
  "MessageType_FilesystemPathInfoQuery": 60802,
@@ -470,6 +472,7 @@
470
472
  "MessageType_DeviceSessionAskPin": 61202,
471
473
  "MessageType_DeviceSessionAskPassphrase": 61203,
472
474
  "MessageType_PortfolioUpdate": 61400,
475
+ "MessageType_NftUpdate": 61500,
473
476
  "MessageType_OnboardingStatusGet": 61600,
474
477
  "MessageType_OnboardingStatus": 61601
475
478
  },
@@ -11993,6 +11996,16 @@
11993
11996
  "APP": 85
11994
11997
  }
11995
11998
  },
11999
+ "ResourceBundleType": {
12000
+ "values": {
12001
+ "IMAGES": 0,
12002
+ "ANIMATION": 1,
12003
+ "WALLPAPER": 2,
12004
+ "TRANSLATIONS": 3,
12005
+ "ROOBERT": 4,
12006
+ "NOTO": 5
12007
+ }
12008
+ },
11996
12009
  "DeviceFirmwareImageInfo": {
11997
12010
  "fields": {
11998
12011
  "version": {
@@ -12190,6 +12203,37 @@
12190
12203
  }
12191
12204
  }
12192
12205
  },
12206
+ "ResourceInventoryGet": {
12207
+ "fields": {}
12208
+ },
12209
+ "ResourceInventoryItem": {
12210
+ "fields": {
12211
+ "type": {
12212
+ "rule": "required",
12213
+ "type": "ResourceBundleType",
12214
+ "id": 1
12215
+ },
12216
+ "size": {
12217
+ "rule": "required",
12218
+ "type": "uint32",
12219
+ "id": 2
12220
+ },
12221
+ "header_hash": {
12222
+ "rule": "required",
12223
+ "type": "bytes",
12224
+ "id": 3
12225
+ }
12226
+ }
12227
+ },
12228
+ "ResourceInventory": {
12229
+ "fields": {
12230
+ "items": {
12231
+ "rule": "repeated",
12232
+ "type": "ResourceInventoryItem",
12233
+ "id": 1
12234
+ }
12235
+ }
12236
+ },
12193
12237
  "DeviceSessionErrorCode": {
12194
12238
  "values": {
12195
12239
  "DeviceSessionError_None": 0,
@@ -12200,11 +12244,26 @@
12200
12244
  "DeviceSessionError_Busy": 5
12201
12245
  }
12202
12246
  },
12247
+ "DeviceSessionSeedDomain": {
12248
+ "values": {
12249
+ "SeedDomain_Standard": 1,
12250
+ "SeedDomain_Cardano": 2
12251
+ }
12252
+ },
12203
12253
  "DeviceSessionGet": {
12204
12254
  "fields": {
12205
12255
  "session_id": {
12206
12256
  "type": "bytes",
12207
12257
  "id": 1
12258
+ },
12259
+ "btc_test_address": {
12260
+ "type": "string",
12261
+ "id": 2
12262
+ },
12263
+ "seed_domains": {
12264
+ "rule": "repeated",
12265
+ "type": "DeviceSessionSeedDomain",
12266
+ "id": 3
12208
12267
  }
12209
12268
  }
12210
12269
  },
@@ -12538,6 +12597,15 @@
12538
12597
  }
12539
12598
  }
12540
12599
  },
12600
+ "NftUpdate": {
12601
+ "fields": {
12602
+ "file_name_no_ext": {
12603
+ "rule": "required",
12604
+ "type": "string",
12605
+ "id": 1
12606
+ }
12607
+ }
12608
+ },
12541
12609
  "OnboardingStep": {
12542
12610
  "values": {
12543
12611
  "ONBOARDING_STEP_UNKNOWN": 0,
@@ -17,6 +17,7 @@ import {
17
17
  } from '../utils';
18
18
  import { DeviceModelToTypes } from '../types';
19
19
  import { findLatestRelease, getReleaseChangelog, getReleaseStatus } from '../utils/release';
20
+ import { parseProtocolV2Resources } from '../protocols/protocol-v2/resources';
20
21
 
21
22
  import type {
22
23
  AssetsMap,
@@ -397,20 +398,10 @@ export default class DataManager {
397
398
  return enrichedData;
398
399
  }
399
400
 
400
- static async load(settings: ConnectSettings) {
401
+ static async load(settings: ConnectSettings): Promise<boolean> {
401
402
  this.settings = settings;
402
- const manifestMode =
403
- settings.firmwareManifestMode ?? (settings.fetchConfig ? 'sdk-managed' : undefined);
404
- if (settings.preloadedConfig) {
405
- this.applyRemoteConfig(settings.preloadedConfig);
406
- Log.log('[DataConfig] Preloaded firmware config loaded');
407
- return;
408
- }
409
- if (manifestMode === 'external-only') {
410
- throw new Error('External firmware config snapshot is required');
411
- }
412
- if (manifestMode !== 'sdk-managed') {
413
- return;
403
+ if (!settings.fetchConfig) {
404
+ return false;
414
405
  }
415
406
 
416
407
  const url = settings.preRelease
@@ -419,48 +410,65 @@ export default class DataManager {
419
410
 
420
411
  const urlWithCache = `${url}?noCache=${getTimeStamp()}`;
421
412
  let data: RemoteConfigResponse | null = null;
413
+ let fetchMethod: 'configFetcher' | 'axios' | 'none' = 'none';
414
+
415
+ // 1. Try custom configFetcher first (client-side IP direct connection support)
422
416
  if (settings.configFetcher) {
423
- Log.debug('[DataConfig] Trying client configFetcher...');
417
+ Log.debug('[DataConfig] Trying configFetcher (client-side fetcher)...');
424
418
  try {
425
419
  data = await settings.configFetcher(urlWithCache);
420
+ if (data) {
421
+ fetchMethod = 'configFetcher';
422
+ Log.log('[DataConfig] ConfigFetcher success');
423
+ } else {
424
+ Log.debug('[DataConfig] ConfigFetcher returned null, will fallback to axios');
425
+ }
426
426
  } catch (e) {
427
- Log.warn('[DataConfig] Client configFetcher failed:', e);
427
+ Log.warn('[DataConfig] ConfigFetcher error, will fallback to axios:', e);
428
428
  }
429
429
  }
430
+
431
+ // 2. Fallback to default axios request
430
432
  if (!data) {
431
- Log.debug('[DataConfig] Trying SDK-managed config request...');
433
+ Log.debug('[DataConfig] Trying axios (SDK default fetcher)...');
432
434
  try {
433
435
  const response = await axios.get<RemoteConfigResponse>(urlWithCache, {
434
436
  // because of iframe timeout is 10000
435
437
  timeout: 7000,
436
438
  });
437
439
  data = response.data;
438
- Log.log('[DataConfig] SDK-managed config request succeeded');
440
+ fetchMethod = 'axios';
441
+ Log.log('[DataConfig] Axios fetch success');
439
442
  } catch (e) {
440
- Log.warn('[DataConfig] SDK-managed config request failed:', e);
443
+ Log.warn('[DataConfig] Axios fetch error:', e);
441
444
  }
442
445
  }
443
446
 
447
+ // 3. Apply config if available
444
448
  if (data) {
445
- this.applyRemoteConfig(data);
446
- } else {
447
- Log.warn('[DataConfig] SDK-managed config request failed, using built-in default config');
449
+ const pro2Resources = parseProtocolV2Resources(
450
+ (data.pro2 as { resources?: unknown } | undefined)?.resources
451
+ );
452
+ Log.log(`[DataConfig] Config loaded successfully via [${fetchMethod}]`);
453
+ this.deviceMap = {
454
+ [EDeviceType.Classic]: this.enrichFirmwareReleaseInfo(data.classic),
455
+ [EDeviceType.Classic1s]: this.enrichFirmwareReleaseInfo(data.classic1s),
456
+ [EDeviceType.ClassicPure]: this.enrichFirmwareReleaseInfo(data.classicpure),
457
+ [EDeviceType.Mini]: this.enrichFirmwareReleaseInfo(data.mini),
458
+ [EDeviceType.Touch]: this.enrichFirmwareReleaseInfo(data.touch),
459
+ [EDeviceType.Pro]: this.enrichFirmwareReleaseInfo(data.pro),
460
+ [EDeviceType.Pro2]: {
461
+ ...this.enrichFirmwareReleaseInfo(data.pro2),
462
+ ...(pro2Resources ? { resources: pro2Resources } : undefined),
463
+ },
464
+ };
465
+ this.assets = {
466
+ bridge: data.bridge,
467
+ };
468
+ return true;
448
469
  }
449
- }
450
-
451
- private static applyRemoteConfig(data: RemoteConfigResponse) {
452
- this.deviceMap = {
453
- [EDeviceType.Classic]: this.enrichFirmwareReleaseInfo(data.classic),
454
- [EDeviceType.Classic1s]: this.enrichFirmwareReleaseInfo(data.classic1s),
455
- [EDeviceType.ClassicPure]: this.enrichFirmwareReleaseInfo(data.classicpure),
456
- [EDeviceType.Mini]: this.enrichFirmwareReleaseInfo(data.mini),
457
- [EDeviceType.Touch]: this.enrichFirmwareReleaseInfo(data.touch),
458
- [EDeviceType.Pro]: this.enrichFirmwareReleaseInfo(data.pro),
459
- [EDeviceType.Pro2]: this.enrichFirmwareReleaseInfo(data.pro2),
460
- };
461
- this.assets = {
462
- bridge: data.bridge,
463
- };
470
+ Log.warn('[DataConfig] All fetch methods failed, using built-in default config');
471
+ return false;
464
472
  }
465
473
 
466
474
  static updateEnv(newEnv: ConnectSettings['env']) {
@@ -478,10 +486,27 @@ export default class DataManager {
478
486
 
479
487
  static async checkAndReloadData() {
480
488
  if (getTimeStamp() - this.lastCheckTimestamp > 1000 * 60 * 60 * 3) {
481
- await this.load(this.settings).then(() => {
489
+ const loaded = await this.load(this.settings);
490
+ if (loaded) {
482
491
  this.lastCheckTimestamp = getTimeStamp();
483
- });
492
+ }
493
+ }
494
+ }
495
+
496
+ /** Force a fresh remote config before an update is allowed to mutate the device. */
497
+ static async forceReloadData(): Promise<void> {
498
+ if (!this.settings) {
499
+ throw new Error('Remote config settings are not initialized');
500
+ }
501
+ const loaded = await this.load(this.settings);
502
+ if (!loaded) {
503
+ throw new Error('Unable to refresh the latest remote config');
484
504
  }
505
+ this.lastCheckTimestamp = getTimeStamp();
506
+ }
507
+
508
+ static getProtocolV2Resources() {
509
+ return this.deviceMap[EDeviceType.Pro2]?.resources?.stable;
485
510
  }
486
511
 
487
512
  static getProtobufMessages(schema: ProtobufMessageSchema = 'v1CurrentSchema'): JSON {
@@ -115,21 +115,10 @@ export const parseConnectSettings = (input: Partial<ConnectSettings> = {}) => {
115
115
  settings.fetchConfig = input.fetchConfig;
116
116
  }
117
117
 
118
- if (
119
- input.firmwareManifestMode === 'sdk-managed' ||
120
- input.firmwareManifestMode === 'external-only'
121
- ) {
122
- settings.firmwareManifestMode = input.firmwareManifestMode;
123
- }
124
-
125
118
  if (input.configFetcher) {
126
119
  settings.configFetcher = input.configFetcher;
127
120
  }
128
121
 
129
- if (input.preloadedConfig) {
130
- settings.preloadedConfig = input.preloadedConfig;
131
- }
132
-
133
122
  return settings;
134
123
  };
135
124