@onekeyfe/hd-core 1.2.0-alpha.34 → 1.2.0-alpha.36

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 (91) hide show
  1. package/__tests__/AllNetworkGetAddressBase.tracing.test.ts +74 -0
  2. package/__tests__/DeviceCommands.test.ts +84 -0
  3. package/__tests__/DeviceEventRegistration.test.ts +6 -0
  4. package/__tests__/device-settings.test.ts +3 -0
  5. package/__tests__/device-wallet-session-store.test.ts +80 -7
  6. package/__tests__/evmSignTransaction.test.ts +69 -0
  7. package/__tests__/get-device-state.test.ts +180 -40
  8. package/__tests__/open-wallet-session.test.ts +314 -55
  9. package/__tests__/protocol-v2-ui-lifecycle.test.ts +56 -0
  10. package/__tests__/protocol-v2.test.ts +768 -139
  11. package/dist/api/FirmwareUpdateV4.d.ts +1 -0
  12. package/dist/api/FirmwareUpdateV4.d.ts.map +1 -1
  13. package/dist/api/GetPassphraseState.d.ts.map +1 -1
  14. package/dist/api/OpenWalletSession.d.ts.map +1 -1
  15. package/dist/api/allnetwork/AllNetworkGetAddressBase.d.ts.map +1 -1
  16. package/dist/api/evm/EVMSignTypedData.d.ts.map +1 -1
  17. package/dist/api/evm/protocol.d.ts +3 -0
  18. package/dist/api/evm/protocol.d.ts.map +1 -0
  19. package/dist/api/protocol-v2/DeviceGetOnboardingStatus.d.ts +1 -1
  20. package/dist/api/protocol-v2/ProtocolInfoRequest.d.ts.map +1 -1
  21. package/dist/core/deviceEventRegistration.d.ts +2 -0
  22. package/dist/core/deviceEventRegistration.d.ts.map +1 -1
  23. package/dist/core/index.d.ts.map +1 -1
  24. package/dist/device/Device.d.ts +25 -6
  25. package/dist/device/Device.d.ts.map +1 -1
  26. package/dist/device/DeviceCommands.d.ts.map +1 -1
  27. package/dist/device/DeviceWalletSessionStore.d.ts +0 -1
  28. package/dist/device/DeviceWalletSessionStore.d.ts.map +1 -1
  29. package/dist/events/device.d.ts +4 -0
  30. package/dist/events/device.d.ts.map +1 -1
  31. package/dist/events/ui-request.d.ts +27 -2
  32. package/dist/events/ui-request.d.ts.map +1 -1
  33. package/dist/index.d.ts +63 -12
  34. package/dist/index.js +1164 -904
  35. package/dist/protocols/protocol-v2/features.d.ts +17 -6
  36. package/dist/protocols/protocol-v2/features.d.ts.map +1 -1
  37. package/dist/protocols/protocol-v2/index.d.ts +2 -2
  38. package/dist/protocols/protocol-v2/index.d.ts.map +1 -1
  39. package/dist/protocols/protocol-v2/settingsUnlockPolicy.d.ts +1 -0
  40. package/dist/protocols/protocol-v2/settingsUnlockPolicy.d.ts.map +1 -1
  41. package/dist/protocols/protocol-v2/uiInteraction.d.ts +3 -3
  42. package/dist/protocols/protocol-v2/uiInteraction.d.ts.map +1 -1
  43. package/dist/protocols/protocol-v2/unlockRetry.d.ts.map +1 -1
  44. package/dist/protocols/protocol-v2/walletSession.d.ts +5 -1
  45. package/dist/protocols/protocol-v2/walletSession.d.ts.map +1 -1
  46. package/dist/types/api/openWalletSession.d.ts +1 -1
  47. package/dist/types/api/openWalletSession.d.ts.map +1 -1
  48. package/dist/types/api/protocolV2.d.ts +2 -2
  49. package/dist/types/api/protocolV2.d.ts.map +1 -1
  50. package/dist/types/device.d.ts +2 -1
  51. package/dist/types/device.d.ts.map +1 -1
  52. package/dist/utils/deviceFeaturesUtils.d.ts +2 -0
  53. package/dist/utils/deviceFeaturesUtils.d.ts.map +1 -1
  54. package/dist/utils/patch.d.ts +1 -1
  55. package/dist/utils/patch.d.ts.map +1 -1
  56. package/package.json +4 -4
  57. package/src/api/BaseMethod.ts +1 -1
  58. package/src/api/FirmwareUpdateV4.ts +39 -14
  59. package/src/api/GetPassphraseState.ts +17 -0
  60. package/src/api/OpenWalletSession.ts +31 -8
  61. package/src/api/allnetwork/AllNetworkGetAddressBase.ts +24 -2
  62. package/src/api/device/DeviceUnlock.ts +1 -1
  63. package/src/api/evm/EVMGetAddress.ts +2 -2
  64. package/src/api/evm/EVMGetPublicKey.ts +2 -2
  65. package/src/api/evm/EVMSignMessage.ts +2 -2
  66. package/src/api/evm/EVMSignTransaction.ts +2 -2
  67. package/src/api/evm/EVMSignTypedData.ts +35 -45
  68. package/src/api/evm/EVMVerifyMessage.ts +2 -2
  69. package/src/api/evm/protocol.ts +6 -0
  70. package/src/api/protocol-v2/DeviceGetOnboardingStatus.ts +2 -2
  71. package/src/api/protocol-v2/DeviceUploadWallpaper.ts +1 -1
  72. package/src/api/protocol-v2/ProtocolInfoRequest.ts +3 -1
  73. package/src/core/deviceEventRegistration.ts +4 -0
  74. package/src/core/index.ts +46 -4
  75. package/src/data/messages/messages-protocol-v2.json +408 -485
  76. package/src/device/Device.ts +266 -43
  77. package/src/device/DeviceCommands.ts +12 -2
  78. package/src/device/DeviceWalletSessionStore.ts +0 -14
  79. package/src/deviceProfile/buildDeviceFeatures.ts +1 -1
  80. package/src/events/device.ts +4 -0
  81. package/src/events/ui-request.ts +32 -2
  82. package/src/protocols/protocol-v2/features.ts +65 -16
  83. package/src/protocols/protocol-v2/index.ts +5 -0
  84. package/src/protocols/protocol-v2/settingsUnlockPolicy.ts +8 -3
  85. package/src/protocols/protocol-v2/uiInteraction.ts +31 -5
  86. package/src/protocols/protocol-v2/unlockRetry.ts +51 -18
  87. package/src/protocols/protocol-v2/walletSession.ts +172 -64
  88. package/src/types/api/openWalletSession.ts +1 -1
  89. package/src/types/api/protocolV2.ts +2 -2
  90. package/src/types/device.ts +2 -0
  91. package/src/utils/deviceFeaturesUtils.ts +4 -0
@@ -14,6 +14,8 @@ import { findMethod } from '../utils';
14
14
  import { DEVICE, IFRAME, createUiMessage } from '../../events';
15
15
  import { UI_REQUEST } from '../../constants/ui-request';
16
16
  import { onDeviceButtonHandler } from '../../core';
17
+ import { runMethodWithUnlockRetry } from '../../protocols/protocol-v2/unlockRetry';
18
+ import { ensureProtocolV2WalletSessionUnlocked } from '../../protocols/protocol-v2/walletSession';
17
19
  import {
18
20
  completeRequestContext,
19
21
  createRequestContext,
@@ -384,7 +386,25 @@ export default abstract class AllNetworkGetAddressBase extends BaseMethod<
384
386
  }
385
387
  }
386
388
 
387
- const response = await method.run();
389
+ // Protocol V2 hands a wallet session to exactly one blockchain request.
390
+ // The parent all-network call consumes its first handoff while fetching
391
+ // the root fingerprint, so each nested chain method must resume the
392
+ // requested hidden wallet before it sends its own device command.
393
+ if (this.device.isProtocolV2() && this.payload.passphraseState) {
394
+ await ensureProtocolV2WalletSessionUnlocked(this.device);
395
+ const passphraseStateSafety = await this.device.checkPassphraseStateSafety(
396
+ this.payload.passphraseState,
397
+ false,
398
+ this.payload.skipPassphraseCheck
399
+ );
400
+ if (!passphraseStateSafety) {
401
+ throw ERRORS.TypedError(HardwareErrorCode.DeviceCheckPassphraseStateError);
402
+ }
403
+ }
404
+
405
+ const response = this.device.isProtocolV2()
406
+ ? await runMethodWithUnlockRetry(method, this.device)
407
+ : await method.run();
388
408
 
389
409
  if (!Array.isArray(response) || response.length === 0) {
390
410
  throw new Error('No response');
@@ -439,7 +459,9 @@ export default abstract class AllNetworkGetAddressBase extends BaseMethod<
439
459
  show_display: false,
440
460
  });
441
461
 
442
- this.postMessage(createUiMessage(UI_REQUEST.CLOSE_UI_PIN_WINDOW));
462
+ if (!this.device.isProtocolV2()) {
463
+ this.postMessage(createUiMessage(UI_REQUEST.CLOSE_UI_PIN_WINDOW));
464
+ }
443
465
 
444
466
  if (res.message.root_fingerprint == null) {
445
467
  throw ERRORS.TypedError(HardwareErrorCode.CallMethodInvalidParameter);
@@ -21,6 +21,6 @@ export default class DeviceUnlock extends BaseMethod<LockDevice> {
21
21
  }
22
22
 
23
23
  async run() {
24
- return this.device.unlockDevice();
24
+ return this.device.unlockDevice(undefined, { emitUiEvent: false });
25
25
  }
26
26
  }
@@ -2,9 +2,9 @@ import { UI_REQUEST } from '../../constants/ui-request';
2
2
  import { serializedPath, validatePath } from '../helpers/pathUtils';
3
3
  import { BaseMethod } from '../BaseMethod';
4
4
  import { validateParams, validateResult } from '../helpers/paramsValidator';
5
- import TransportManager from '../../data-manager/TransportManager';
6
5
  import getAddressLegacyV1 from './legacyV1/getAddress';
7
6
  import getAddress from './latest/getAddress';
7
+ import { shouldUseLegacyV1EvmMessages } from './protocol';
8
8
 
9
9
  import type { EVMAddress, EVMGetAddressParams } from '../../types';
10
10
  import type { EthereumGetAddressOneKey } from '@onekeyfe/hd-transport';
@@ -48,7 +48,7 @@ export default class EvmGetAddress extends BaseMethod<EthereumGetAddressOneKey[]
48
48
  }
49
49
 
50
50
  async getEvmAddress(param: EthereumGetAddressOneKey) {
51
- if (TransportManager.getProtocolV1MessageSchema() === 'v1LegacySchema') {
51
+ if (shouldUseLegacyV1EvmMessages(this.device)) {
52
52
  return getAddressLegacyV1({
53
53
  typedCall: this.device.commands.typedCall.bind(this.device.commands),
54
54
  param,
@@ -2,9 +2,9 @@ import { UI_REQUEST } from '../../constants/ui-request';
2
2
  import { serializedPath, validatePath } from '../helpers/pathUtils';
3
3
  import { BaseMethod } from '../BaseMethod';
4
4
  import { validateParams, validateResult } from '../helpers/paramsValidator';
5
- import TransportManager from '../../data-manager/TransportManager';
6
5
  import getPublicKey from './latest/getPublicKey';
7
6
  import getPublicKeyLegacyV1 from './legacyV1/getPublicKey';
7
+ import { shouldUseLegacyV1EvmMessages } from './protocol';
8
8
  import { batchGetPublickeys, supportBatchPublicKeyByDevice } from '../helpers/batchGetPublickeys';
9
9
 
10
10
  import type { EVMGetPublicKeyParams, EVMPublicKey } from '../../types';
@@ -60,7 +60,7 @@ export default class EVMGetPublicKey extends BaseMethod<EthereumGetPublicKeyOneK
60
60
  }
61
61
 
62
62
  getEvmPublicKey(param: EthereumGetPublicKey) {
63
- if (TransportManager.getProtocolV1MessageSchema() === 'v1LegacySchema') {
63
+ if (shouldUseLegacyV1EvmMessages(this.device)) {
64
64
  return getPublicKeyLegacyV1({
65
65
  typedCall: this.device.commands.typedCall.bind(this.device.commands),
66
66
  param,
@@ -3,9 +3,9 @@ import { validatePath } from '../helpers/pathUtils';
3
3
  import { BaseMethod } from '../BaseMethod';
4
4
  import { validateParams } from '../helpers/paramsValidator';
5
5
  import { formatAnyHex } from '../helpers/hexUtils';
6
- import TransportManager from '../../data-manager/TransportManager';
7
6
  import signMessage from './latest/signMessage';
8
7
  import signMessageLegacyV1 from './legacyV1/signMessage';
8
+ import { shouldUseLegacyV1EvmMessages } from './protocol';
9
9
 
10
10
  import type { EthereumSignMessageOneKey } from '@onekeyfe/hd-transport';
11
11
 
@@ -40,7 +40,7 @@ export default class EVMSignMessage extends BaseMethod<EthereumSignMessageOneKey
40
40
  }
41
41
 
42
42
  async run() {
43
- if (TransportManager.getProtocolV1MessageSchema() === 'v1LegacySchema') {
43
+ if (shouldUseLegacyV1EvmMessages(this.device)) {
44
44
  return signMessageLegacyV1({
45
45
  typedCall: this.device.commands.typedCall.bind(this.device.commands),
46
46
  params: this.params,
@@ -5,9 +5,9 @@ import { validatePath } from '../helpers/pathUtils';
5
5
  import { BaseMethod } from '../BaseMethod';
6
6
  import { validateParams } from '../helpers/paramsValidator';
7
7
  import { formatAnyHex } from '../helpers/hexUtils';
8
- import TransportManager from '../../data-manager/TransportManager';
9
8
  import { signTransaction } from './latest/signTransaction';
10
9
  import { signTransaction as signTransactionLegacyV1 } from './legacyV1/signTransaction';
10
+ import { shouldUseLegacyV1EvmMessages } from './protocol';
11
11
 
12
12
  import type {
13
13
  EVMSignTransactionParams,
@@ -126,7 +126,7 @@ export default class EVMSignTransaction extends BaseMethod {
126
126
 
127
127
  if (formattedTx == null) throw ERRORS.TypedError('Runtime', 'formattedTx is not set');
128
128
 
129
- if (TransportManager.getProtocolV1MessageSchema() === 'v1LegacySchema') {
129
+ if (shouldUseLegacyV1EvmMessages(this.device)) {
130
130
  return signTransactionLegacyV1({
131
131
  typedCall: this.device.commands.typedCall.bind(this.device.commands),
132
132
  addressN,
@@ -15,8 +15,8 @@ import {
15
15
  type EthereumSignTypedDataMessage,
16
16
  type EthereumSignTypedDataTypes,
17
17
  } from '../../types';
18
- import TransportManager from '../../data-manager/TransportManager';
19
18
  import { signTypedHash as signTypedHashLegacyV1 } from './legacyV1/signTypedHash';
19
+ import { shouldUseLegacyV1EvmMessages } from './protocol';
20
20
  import { signTypedHash } from './latest/signTypedHash';
21
21
  import { signTypedData as signTypedDataLegacyV1 } from './legacyV1/signTypedData';
22
22
  import { signTypedData } from './latest/signTypedData';
@@ -278,29 +278,23 @@ export default class EVMSignTypedData extends BaseMethod<EVMSignTypedDataParams>
278
278
 
279
279
  let supportTrezor = false;
280
280
  let response: MessageResponse<MessageKey>;
281
- switch (TransportManager.getProtocolV1MessageSchema()) {
282
- case 'v1LegacySchema':
283
- supportTrezor = true;
284
- response = await signTypedDataLegacyV1({
285
- typedCall: this.device.commands.typedCall.bind(this.device.commands),
286
- addressN,
287
- data,
288
- metamaskV4Compat,
289
- chainId,
290
- });
291
- break;
292
-
293
- case 'v1CurrentSchema':
294
- default:
295
- supportTrezor = false;
296
- response = await signTypedData({
297
- typedCall: this.device.commands.typedCall.bind(this.device.commands),
298
- addressN,
299
- data,
300
- metamaskV4Compat,
301
- chainId,
302
- });
303
- break;
281
+ if (shouldUseLegacyV1EvmMessages(this.device)) {
282
+ supportTrezor = true;
283
+ response = await signTypedDataLegacyV1({
284
+ typedCall: this.device.commands.typedCall.bind(this.device.commands),
285
+ addressN,
286
+ data,
287
+ metamaskV4Compat,
288
+ chainId,
289
+ });
290
+ } else {
291
+ response = await signTypedData({
292
+ typedCall: this.device.commands.typedCall.bind(this.device.commands),
293
+ addressN,
294
+ data,
295
+ metamaskV4Compat,
296
+ chainId,
297
+ });
304
298
  }
305
299
 
306
300
  return this.handleSignTypedData({
@@ -326,28 +320,24 @@ export default class EVMSignTypedData extends BaseMethod<EVMSignTypedDataParams>
326
320
  }) {
327
321
  if (!domainHash) throw ERRORS.TypedError('Runtime', 'domainHash is required');
328
322
 
329
- switch (TransportManager.getProtocolV1MessageSchema()) {
330
- case 'v1LegacySchema':
331
- return signTypedHashLegacyV1({
332
- typedCall,
333
- addressN,
334
- domainHash,
335
- messageHash,
336
- chainId,
337
- device: this.device,
338
- });
339
-
340
- case 'v1CurrentSchema':
341
- default:
342
- return signTypedHash({
343
- typedCall,
344
- addressN,
345
- domainHash,
346
- messageHash,
347
- chainId,
348
- device: this.device,
349
- });
323
+ if (shouldUseLegacyV1EvmMessages(this.device)) {
324
+ return signTypedHashLegacyV1({
325
+ typedCall,
326
+ addressN,
327
+ domainHash,
328
+ messageHash,
329
+ chainId,
330
+ device: this.device,
331
+ });
350
332
  }
333
+ return signTypedHash({
334
+ typedCall,
335
+ addressN,
336
+ domainHash,
337
+ messageHash,
338
+ chainId,
339
+ device: this.device,
340
+ });
351
341
  }
352
342
 
353
343
  getVersionRange() {
@@ -2,9 +2,9 @@ import { UI_REQUEST } from '../../constants/ui-request';
2
2
  import { BaseMethod } from '../BaseMethod';
3
3
  import { validateParams } from '../helpers/paramsValidator';
4
4
  import { formatAnyHex } from '../helpers/hexUtils';
5
- import TransportManager from '../../data-manager/TransportManager';
6
5
  import verifyMessageLegacyV1 from './legacyV1/verifyMessage';
7
6
  import verifyMessage from './latest/verifyMessage';
7
+ import { shouldUseLegacyV1EvmMessages } from './protocol';
8
8
 
9
9
  import type { EthereumVerifyMessageOneKey } from '@onekeyfe/hd-transport';
10
10
 
@@ -35,7 +35,7 @@ export default class EVMSignMessage extends BaseMethod<EthereumVerifyMessageOneK
35
35
  }
36
36
 
37
37
  async run() {
38
- if (TransportManager.getProtocolV1MessageSchema() === 'v1LegacySchema') {
38
+ if (shouldUseLegacyV1EvmMessages(this.device)) {
39
39
  return verifyMessageLegacyV1({
40
40
  typedCall: this.device.commands.typedCall.bind(this.device.commands),
41
41
  params: this.params,
@@ -0,0 +1,6 @@
1
+ import TransportManager from '../../data-manager/TransportManager';
2
+
3
+ import type { Device } from '../../device/Device';
4
+
5
+ export const shouldUseLegacyV1EvmMessages = (device: Pick<Device, 'isProtocolV2'>): boolean =>
6
+ !device.isProtocolV2() && TransportManager.getProtocolV1MessageSchema() === 'v1LegacySchema';
@@ -12,8 +12,8 @@ export default class DeviceGetOnboardingStatus extends BaseMethod {
12
12
 
13
13
  async run() {
14
14
  const { message } = await this.device.commands.typedCall(
15
- 'DevGetOnboardingStatus',
16
- 'DevOnboardingStatus',
15
+ 'OnboardingStatusGet',
16
+ 'OnboardingStatus',
17
17
  {}
18
18
  );
19
19
  return message;
@@ -74,7 +74,7 @@ export default class DeviceUploadWallpaper extends BaseMethod<DeviceUploadWallpa
74
74
  this.encoded = encodePro2Wallpaper({ width, height, rgba: rgbaBytes });
75
75
  this.path = `${WALLPAPER_DIRECTORY}/${normalizeFileName(fileName, this.encoded.data)}`;
76
76
  this.params = { width, height, rgba: rgbaBytes, fileName, chunkSize };
77
- this.unlockPolicy = 'unlock-before-run';
77
+ this.unlockPolicy = 'none';
78
78
  this.skipForceUpdateCheck = true;
79
79
  this.useDevicePassphraseState = false;
80
80
  }
@@ -13,7 +13,9 @@ export default class ProtocolInfoRequest extends BaseMethod {
13
13
  }
14
14
 
15
15
  async run() {
16
- const res = await this.device.commands.typedCall('ProtocolInfoRequest', 'ProtocolInfo', {});
16
+ const res = await this.device.commands.typedCall('ProtocolInfoRequest', 'ProtocolInfo', {
17
+ eventless_wallet_session: true,
18
+ });
17
19
  return Promise.resolve(res.message);
18
20
  }
19
21
  }
@@ -4,6 +4,8 @@ import type { Device, DeviceEvents } from '../device/Device';
4
4
 
5
5
  export type HardwareUiEventHandlers = {
6
6
  pin: (...event: DeviceEvents[typeof DEVICE.PIN]) => void;
7
+ pinOnDevice: (...event: DeviceEvents[typeof DEVICE.PIN_ON_DEVICE]) => void;
8
+ pinOnDeviceComplete: (...event: DeviceEvents[typeof DEVICE.PIN_ON_DEVICE_COMPLETE]) => void;
7
9
  button: (...event: DeviceEvents[typeof DEVICE.BUTTON]) => void;
8
10
  passphrase: (...event: DeviceEvents[typeof DEVICE.PASSPHRASE]) => void;
9
11
  passphraseOnDevice: (...event: DeviceEvents[typeof DEVICE.PASSPHRASE_ON_DEVICE]) => void;
@@ -15,6 +17,8 @@ export function registerHardwareUiEventListeners(
15
17
  handlers: HardwareUiEventHandlers
16
18
  ) {
17
19
  device.on(DEVICE.PIN, handlers.pin);
20
+ device.on(DEVICE.PIN_ON_DEVICE, handlers.pinOnDevice);
21
+ device.on(DEVICE.PIN_ON_DEVICE_COMPLETE, handlers.pinOnDeviceComplete);
18
22
  device.on(DEVICE.BUTTON, handlers.button);
19
23
  device.on(DEVICE.PASSPHRASE, handlers.passphrase);
20
24
  device.on(DEVICE.PASSPHRASE_ON_DEVICE, handlers.passphraseOnDevice);
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,
@@ -56,6 +56,7 @@ import RequestQueue from './RequestQueue';
56
56
  import { registerHardwareUiEventListeners } from './deviceEventRegistration';
57
57
  import { getSynchronize } from '../utils/getSynchronize';
58
58
  import { runMethodWithUnlockRetry } from '../protocols/protocol-v2/unlockRetry';
59
+ import { ensureProtocolV2WalletSessionUnlocked } from '../protocols/protocol-v2/walletSession';
59
60
  import {
60
61
  ProtocolV2UiInteractionCoordinator,
61
62
  isProtocolV2UiEnabled,
@@ -406,6 +407,8 @@ const onCallDevice = async (
406
407
 
407
408
  registerHardwareUiEventListeners(device, {
408
409
  pin: onDevicePinHandler,
410
+ pinOnDevice: onEnterPinOnDeviceHandler,
411
+ pinOnDeviceComplete: onPinOnDeviceCompleteHandler,
409
412
  button: onDeviceButtonHandler,
410
413
  passphrase: message.payload.useEmptyPassphrase
411
414
  ? onEmptyPassphraseHandler
@@ -421,6 +424,7 @@ const onCallDevice = async (
421
424
  );
422
425
 
423
426
  const protocolV2UiCoordinator = new ProtocolV2UiInteractionCoordinator(device, postMessage);
427
+ device.beginProtocolV2UiInteraction();
424
428
  device.on(
425
429
  DEVICE.SELECT_DEVICE_FOR_SWITCH_FIRMWARE_WEB_DEVICE,
426
430
  onSelectDeviceForSwitchFirmwareWebDeviceHandler
@@ -587,11 +591,15 @@ const onCallDevice = async (
587
591
  );
588
592
  }
589
593
 
594
+ // Wallet-session recovery is part of the protected business call, not onboarding.
595
+ await ensureProtocolV2WalletSessionUnlocked(device);
596
+
590
597
  // Check Device passphrase State
591
598
  const passphraseStateSafety = await device.checkPassphraseStateSafety(
592
599
  method.payload?.passphraseState,
593
600
  method.payload?.useEmptyPassphrase,
594
- method.payload?.skipPassphraseCheck
601
+ method.payload?.skipPassphraseCheck,
602
+ hasDeriveCardano(method)
595
603
  );
596
604
 
597
605
  // Double check, handles the special case of Touch/Pro
@@ -604,8 +612,11 @@ const onCallDevice = async (
604
612
  );
605
613
  }
606
614
 
607
- // close pin popup window
608
- postMessage(createUiMessage(UI_REQUEST.CLOSE_UI_PIN_WINDOW));
615
+ // Protocol V2 emits the PIN phase completion from DeviceSessionAskPin.
616
+ // Keep the legacy compatibility close for Protocol V1 only.
617
+ if (!device.isProtocolV2()) {
618
+ postMessage(createUiMessage(UI_REQUEST.CLOSE_UI_PIN_WINDOW));
619
+ }
609
620
  }
610
621
 
611
622
  // Automatic check safety_check level for Kovan, Ropsten, Rinkeby, Goerli test networks.
@@ -1279,6 +1290,7 @@ const onDevicePassphraseHandler = async (
1279
1290
  source: requestPayload.source,
1280
1291
  reason: requestPayload.reason,
1281
1292
  expectedPassphraseState: requestPayload.expectedPassphraseState,
1293
+ ...(requestPayload.interaction ? { interaction: requestPayload.interaction } : {}),
1282
1294
  })
1283
1295
  );
1284
1296
  // wait for passphrase
@@ -1308,6 +1320,7 @@ const onEnterPassphraseOnDeviceHandler = (
1308
1320
  passphraseState: device.passphraseState,
1309
1321
  source: requestPayload?.source,
1310
1322
  reason: requestPayload?.reason,
1323
+ ...(requestPayload?.interaction ? { interaction: requestPayload.interaction } : {}),
1311
1324
  })
1312
1325
  );
1313
1326
  };
@@ -1321,10 +1334,39 @@ const onEnterAttachPinOnDeviceHandler = (
1321
1334
  type: 'ButtonRequest_AttachPin',
1322
1335
  source: requestPayload?.source,
1323
1336
  reason: requestPayload?.reason,
1337
+ ...(requestPayload?.interaction ? { interaction: requestPayload.interaction } : {}),
1324
1338
  })
1325
1339
  );
1326
1340
  };
1327
1341
 
1342
+ const onEnterPinOnDeviceHandler = (
1343
+ ...[device, pinType, metadata]: [...DeviceEvents['pin_on_device']]
1344
+ ) => {
1345
+ postMessage(
1346
+ createUiMessage(UI_REQUEST.REQUEST_PIN, {
1347
+ device: device.toMessageObject() as KnownDevice,
1348
+ type:
1349
+ pinType === DeviceSessionPinType.AttachToPin
1350
+ ? 'ButtonRequest_AttachPin'
1351
+ : 'ButtonRequest_PinEntry',
1352
+ source: metadata?.source,
1353
+ reason: metadata?.reason,
1354
+ deviceOnly: metadata?.deviceOnly ?? true,
1355
+ completion: metadata?.completion,
1356
+ method: metadata?.method,
1357
+ page: metadata?.page,
1358
+ operation: metadata?.operation,
1359
+ ...(metadata?.interaction ? { interaction: metadata.interaction } : {}),
1360
+ })
1361
+ );
1362
+ };
1363
+
1364
+ const onPinOnDeviceCompleteHandler = (
1365
+ ...[_, metadata]: [...DeviceEvents['pin_on_device_complete']]
1366
+ ) => {
1367
+ postMessage(createUiMessage(UI_REQUEST.CLOSE_UI_PIN_WINDOW, metadata));
1368
+ };
1369
+
1328
1370
  const onSelectDeviceInBootloaderForWebDeviceHandler = async (
1329
1371
  ...[device, callback]: [...DeviceEvents['select_device_in_bootloader_for_web_device']]
1330
1372
  ) => {