@onekeyfe/hd-core 1.2.0-alpha.19 → 1.2.0-alpha.20

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 (256) hide show
  1. package/__tests__/AllNetworkGetAddressBase.tracing.test.ts +75 -0
  2. package/__tests__/DeviceCommands.test.ts +143 -7
  3. package/__tests__/RequestQueue.test.ts +106 -0
  4. package/__tests__/UploadPortfolio.test.ts +46 -0
  5. package/__tests__/core-dispose.test.ts +34 -0
  6. package/__tests__/core-error-output.test.ts +39 -0
  7. package/__tests__/device-lifecycle-events.test.ts +117 -0
  8. package/__tests__/device-pool-state.test.ts +20 -1
  9. package/__tests__/device-settings.test.ts +184 -6
  10. package/__tests__/device-state-events.test.ts +1 -2
  11. package/__tests__/device-state-mapper.test.ts +33 -26
  12. package/__tests__/device-state-store.test.ts +1 -34
  13. package/__tests__/device-utils.test.ts +106 -0
  14. package/__tests__/device-wallet-session-store.test.ts +65 -29
  15. package/__tests__/deviceSettings.test.ts +21 -12
  16. package/__tests__/evmLedgerLegacySafety.test.ts +2 -2
  17. package/__tests__/evmSignTypedData.test.ts +80 -0
  18. package/__tests__/filesystemValidation.test.ts +53 -0
  19. package/__tests__/firmware-update/firmware-update-v2-download-before-boot.test.ts +402 -0
  20. package/__tests__/get-device-state.test.ts +77 -41
  21. package/__tests__/logBlockEvent.test.ts +24 -11
  22. package/__tests__/networkUtils.test.ts +386 -0
  23. package/__tests__/open-wallet-session-error-response.test.ts +43 -0
  24. package/__tests__/open-wallet-session.test.ts +559 -0
  25. package/__tests__/protocol-v2.test.ts +756 -452
  26. package/__tests__/public-device-state-api.test.ts +113 -13
  27. package/__tests__/public-pro2-api-boundary.test.ts +91 -0
  28. package/__tests__/search-devices.test.ts +43 -0
  29. package/dist/api/BaseMethod.d.ts +3 -0
  30. package/dist/api/BaseMethod.d.ts.map +1 -1
  31. package/dist/api/CheckBootloaderRelease.d.ts.map +1 -1
  32. package/dist/api/CheckFirmwareRelease.d.ts.map +1 -1
  33. package/dist/api/ClearSessionCache.d.ts.map +1 -1
  34. package/dist/api/DirList.d.ts.map +1 -1
  35. package/dist/api/FileRead.d.ts.map +1 -1
  36. package/dist/api/FirmwareUpdateV2.d.ts +2 -2
  37. package/dist/api/FirmwareUpdateV2.d.ts.map +1 -1
  38. package/dist/api/FirmwareUpdateV3.d.ts.map +1 -1
  39. package/dist/api/FirmwareUpdateV4.d.ts +3 -2
  40. package/dist/api/FirmwareUpdateV4.d.ts.map +1 -1
  41. package/dist/api/GetPassphraseState.d.ts.map +1 -1
  42. package/dist/api/OpenWalletSession.d.ts +14 -0
  43. package/dist/api/OpenWalletSession.d.ts.map +1 -0
  44. package/dist/api/PathInfo.d.ts.map +1 -1
  45. package/dist/api/PromptWebDeviceAccess.d.ts.map +1 -1
  46. package/dist/api/SearchDevices.d.ts +3 -0
  47. package/dist/api/SearchDevices.d.ts.map +1 -1
  48. package/dist/api/UploadPortfolio.d.ts.map +1 -1
  49. package/dist/api/allnetwork/AllNetworkGetAddressBase.d.ts.map +1 -1
  50. package/dist/api/device/DeviceChangePin.d.ts.map +1 -1
  51. package/dist/api/device/DeviceFullyUploadResource.d.ts +1 -2
  52. package/dist/api/device/DeviceFullyUploadResource.d.ts.map +1 -1
  53. package/dist/api/device/DeviceSettings.d.ts.map +1 -1
  54. package/dist/api/device/DeviceUpdateBootloader.d.ts.map +1 -1
  55. package/dist/api/device/DeviceWipe.d.ts.map +1 -1
  56. package/dist/api/evm/EVMSignTypedData.d.ts.map +1 -1
  57. package/dist/api/firmware/getBinary.d.ts +7 -3
  58. package/dist/api/firmware/getBinary.d.ts.map +1 -1
  59. package/dist/api/helpers/filesystemValidation.d.ts +3 -0
  60. package/dist/api/helpers/filesystemValidation.d.ts.map +1 -1
  61. package/dist/api/helpers/paramsValidator.d.ts +1 -0
  62. package/dist/api/helpers/paramsValidator.d.ts.map +1 -1
  63. package/dist/api/helpers/protocolV2FileWrite.d.ts.map +1 -1
  64. package/dist/api/index.d.ts +7 -11
  65. package/dist/api/index.d.ts.map +1 -1
  66. package/dist/api/protocol-v2/DeviceGetFirmwareUpdateStatus.d.ts.map +1 -1
  67. package/dist/api/protocol-v2/DeviceSettingsPageShow.d.ts.map +1 -1
  68. package/dist/api/protocol-v2/DeviceSettingsSet.d.ts.map +1 -1
  69. package/dist/api/utils.d.ts.map +1 -1
  70. package/dist/core/RequestQueue.d.ts +17 -1
  71. package/dist/core/RequestQueue.d.ts.map +1 -1
  72. package/dist/core/index.d.ts +3 -1
  73. package/dist/core/index.d.ts.map +1 -1
  74. package/dist/device/Device.d.ts +6 -5
  75. package/dist/device/Device.d.ts.map +1 -1
  76. package/dist/device/DeviceCommands.d.ts +5 -5
  77. package/dist/device/DeviceCommands.d.ts.map +1 -1
  78. package/dist/device/DevicePool.d.ts +1 -0
  79. package/dist/device/DevicePool.d.ts.map +1 -1
  80. package/dist/device/DeviceStateMapper.d.ts +2 -2
  81. package/dist/device/DeviceStateMapper.d.ts.map +1 -1
  82. package/dist/device/DeviceStateProjector.d.ts +1 -2
  83. package/dist/device/DeviceStateProjector.d.ts.map +1 -1
  84. package/dist/device/DeviceStateStore.d.ts +1 -3
  85. package/dist/device/DeviceStateStore.d.ts.map +1 -1
  86. package/dist/device/DeviceWalletSessionStore.d.ts +6 -1
  87. package/dist/device/DeviceWalletSessionStore.d.ts.map +1 -1
  88. package/dist/device/cloneDeviceState.d.ts +2 -0
  89. package/dist/device/cloneDeviceState.d.ts.map +1 -0
  90. package/dist/deviceProfile/buildDeviceFeatures.d.ts +3 -1
  91. package/dist/deviceProfile/buildDeviceFeatures.d.ts.map +1 -1
  92. package/dist/events/logBlockEvent.d.ts.map +1 -1
  93. package/dist/index.d.ts +608 -75
  94. package/dist/index.js +2287 -1706
  95. package/dist/inject.d.ts.map +1 -1
  96. package/dist/protocols/protocol-v2/deviceSession.d.ts +7 -0
  97. package/dist/protocols/protocol-v2/deviceSession.d.ts.map +1 -0
  98. package/dist/protocols/protocol-v2/features.d.ts +11 -4
  99. package/dist/protocols/protocol-v2/features.d.ts.map +1 -1
  100. package/dist/protocols/protocol-v2/index.d.ts +2 -2
  101. package/dist/protocols/protocol-v2/index.d.ts.map +1 -1
  102. package/dist/protocols/protocol-v2/settingsUnlockPolicy.d.ts +16 -0
  103. package/dist/protocols/protocol-v2/settingsUnlockPolicy.d.ts.map +1 -0
  104. package/dist/protocols/protocol-v2/uiInteraction.d.ts.map +1 -1
  105. package/dist/protocols/protocol-v2/walletSession.d.ts +8 -3
  106. package/dist/protocols/protocol-v2/walletSession.d.ts.map +1 -1
  107. package/dist/types/api/checkAllFirmwareRelease.d.ts +1 -2
  108. package/dist/types/api/checkAllFirmwareRelease.d.ts.map +1 -1
  109. package/dist/types/api/deviceSettings.d.ts +1 -0
  110. package/dist/types/api/deviceSettings.d.ts.map +1 -1
  111. package/dist/types/api/deviceUnlock.d.ts +1 -1
  112. package/dist/types/api/deviceUnlock.d.ts.map +1 -1
  113. package/dist/types/api/getFeatures.d.ts.map +1 -1
  114. package/dist/types/api/getOnekeyFeatures.d.ts.map +1 -1
  115. package/dist/types/api/getPassphraseState.d.ts.map +1 -1
  116. package/dist/types/api/index.d.ts +13 -15
  117. package/dist/types/api/index.d.ts.map +1 -1
  118. package/dist/types/api/openWalletSession.d.ts +41 -0
  119. package/dist/types/api/openWalletSession.d.ts.map +1 -0
  120. package/dist/types/api/protocolV2.d.ts +6 -11
  121. package/dist/types/api/protocolV2.d.ts.map +1 -1
  122. package/dist/types/device.d.ts +2 -9
  123. package/dist/types/device.d.ts.map +1 -1
  124. package/dist/utils/assets.d.ts +2 -1
  125. package/dist/utils/assets.d.ts.map +1 -1
  126. package/dist/utils/deviceFeaturesCompat.d.ts +13 -0
  127. package/dist/utils/deviceFeaturesCompat.d.ts.map +1 -0
  128. package/dist/utils/deviceFeaturesUtils.d.ts +11 -1
  129. package/dist/utils/deviceFeaturesUtils.d.ts.map +1 -1
  130. package/dist/utils/deviceInfoUtils.d.ts +9 -8
  131. package/dist/utils/deviceInfoUtils.d.ts.map +1 -1
  132. package/dist/utils/deviceSettings.d.ts +2 -0
  133. package/dist/utils/deviceSettings.d.ts.map +1 -1
  134. package/dist/utils/deviceVersionUtils.d.ts +6 -5
  135. package/dist/utils/deviceVersionUtils.d.ts.map +1 -1
  136. package/dist/utils/index.d.ts +2 -2
  137. package/dist/utils/index.d.ts.map +1 -1
  138. package/dist/utils/networkUtils.d.ts +9 -1
  139. package/dist/utils/networkUtils.d.ts.map +1 -1
  140. package/dist/utils/patch.d.ts +1 -1
  141. package/dist/utils/patch.d.ts.map +1 -1
  142. package/dist/utils/pro2Wallpaper.d.ts.map +1 -1
  143. package/package.json +4 -4
  144. package/src/api/BaseMethod.ts +17 -10
  145. package/src/api/CheckBootloaderRelease.ts +1 -2
  146. package/src/api/CheckFirmwareRelease.ts +1 -2
  147. package/src/api/ClearSessionCache.ts +0 -4
  148. package/src/api/DirList.ts +5 -3
  149. package/src/api/DirMake.ts +3 -3
  150. package/src/api/DirRemove.ts +3 -3
  151. package/src/api/FileDelete.ts +3 -3
  152. package/src/api/FileRead.ts +9 -6
  153. package/src/api/FileWrite.ts +3 -3
  154. package/src/api/FirmwareUpdate.ts +4 -4
  155. package/src/api/FirmwareUpdateV2.ts +133 -45
  156. package/src/api/FirmwareUpdateV3.ts +3 -5
  157. package/src/api/FirmwareUpdateV4.ts +192 -154
  158. package/src/api/GetPassphraseState.ts +10 -16
  159. package/src/api/OpenWalletSession.ts +221 -0
  160. package/src/api/PathInfo.ts +5 -3
  161. package/src/api/PromptWebDeviceAccess.ts +2 -3
  162. package/src/api/SearchDevices.ts +6 -2
  163. package/src/api/UploadPortfolio.ts +12 -3
  164. package/src/api/alephium/AlephiumSignTransaction.ts +2 -2
  165. package/src/api/allnetwork/AllNetworkGetAddressBase.ts +4 -8
  166. package/src/api/benfen/BenfenGetAddress.ts +1 -1
  167. package/src/api/btc/BTCGetPublicKey.ts +1 -1
  168. package/src/api/device/DeviceChangePin.ts +1 -0
  169. package/src/api/device/DeviceFullyUploadResource.ts +4 -5
  170. package/src/api/device/DeviceSettings.ts +93 -0
  171. package/src/api/device/DeviceUpdateBootloader.ts +1 -2
  172. package/src/api/device/DeviceWipe.ts +1 -0
  173. package/src/api/evm/EVMSignTypedData.ts +3 -5
  174. package/src/api/evm/legacyV1/getAddress.ts +2 -2
  175. package/src/api/evm/legacyV1/getPublicKey.ts +2 -2
  176. package/src/api/evm/legacyV1/signMessage.ts +2 -2
  177. package/src/api/evm/legacyV1/signTypedData.ts +2 -2
  178. package/src/api/evm/legacyV1/signTypedHash.ts +2 -2
  179. package/src/api/evm/legacyV1/verifyMessage.ts +2 -2
  180. package/src/api/firmware/FirmwareUpdateBaseMethod.ts +3 -3
  181. package/src/api/firmware/getBinary.ts +8 -3
  182. package/src/api/helpers/batchGetPublickeys.ts +2 -2
  183. package/src/api/helpers/filesystemValidation.ts +61 -0
  184. package/src/api/helpers/paramsValidator.ts +1 -1
  185. package/src/api/helpers/protocolV2FileWrite.ts +3 -2
  186. package/src/api/index.ts +9 -12
  187. package/src/api/nervos/NervosSignTransaction.ts +2 -2
  188. package/src/api/nexa/NexaSignTransaction.ts +1 -1
  189. package/src/api/protocol-v2/DeviceFactoryInfoGet.ts +1 -1
  190. package/src/api/protocol-v2/DeviceFactoryInfoSet.ts +1 -1
  191. package/src/api/protocol-v2/DeviceFirmwareUpdate.ts +4 -4
  192. package/src/api/protocol-v2/DeviceGetFirmwareUpdateStatus.ts +6 -5
  193. package/src/api/protocol-v2/DeviceInfoGet.ts +4 -4
  194. package/src/api/protocol-v2/DeviceReboot.ts +1 -1
  195. package/src/api/protocol-v2/DeviceSettingsPageShow.ts +11 -8
  196. package/src/api/protocol-v2/DeviceSettingsSet.ts +4 -11
  197. package/src/api/protocol-v2/FilesystemFormat.ts +1 -1
  198. package/src/api/protocol-v2/FilesystemPermissionFix.ts +1 -1
  199. package/src/api/protocol-v2/Ping.ts +1 -1
  200. package/src/api/protocol-v2/ProtocolInfoRequest.ts +1 -1
  201. package/src/api/utils.ts +1 -3
  202. package/src/core/RequestQueue.ts +107 -6
  203. package/src/core/index.ts +91 -46
  204. package/src/data/messages/messages-protocol-v2.json +25 -79
  205. package/src/data-manager/DataManager.ts +6 -6
  206. package/src/device/Device.ts +186 -102
  207. package/src/device/DeviceCommands.ts +51 -21
  208. package/src/device/DeviceFeaturesState.ts +4 -4
  209. package/src/device/DevicePool.ts +17 -3
  210. package/src/device/DeviceStateMapper.ts +13 -29
  211. package/src/device/DeviceStateProjector.ts +11 -8
  212. package/src/device/DeviceStateStore.ts +11 -52
  213. package/src/device/DeviceWalletSessionStore.ts +31 -4
  214. package/src/device/cloneDeviceState.ts +39 -0
  215. package/src/device/deviceIdentity.ts +2 -2
  216. package/src/deviceProfile/buildDeviceFeatures.ts +29 -88
  217. package/src/events/logBlockEvent.ts +8 -1
  218. package/src/inject.ts +14 -24
  219. package/src/protocols/protocol-v2/deviceSession.ts +24 -0
  220. package/src/protocols/protocol-v2/features.ts +37 -49
  221. package/src/protocols/protocol-v2/firmware.ts +1 -1
  222. package/src/protocols/protocol-v2/index.ts +2 -3
  223. package/src/protocols/protocol-v2/settingsUnlockPolicy.ts +61 -0
  224. package/src/protocols/protocol-v2/uiInteraction.ts +4 -3
  225. package/src/protocols/protocol-v2/walletSession.ts +57 -146
  226. package/src/types/api/checkAllFirmwareRelease.ts +1 -2
  227. package/src/types/api/deviceSettings.ts +1 -0
  228. package/src/types/api/deviceUnlock.ts +1 -1
  229. package/src/types/api/firmwareUpdate.ts +5 -5
  230. package/src/types/api/getDeviceState.ts +1 -1
  231. package/src/types/api/getFeatures.ts +3 -0
  232. package/src/types/api/getOnekeyFeatures.ts +3 -0
  233. package/src/types/api/getPassphraseState.ts +6 -0
  234. package/src/types/api/index.ts +16 -23
  235. package/src/types/api/openWalletSession.ts +64 -0
  236. package/src/types/api/protocolV2.ts +36 -18
  237. package/src/types/device.ts +20 -14
  238. package/src/types/params.ts +1 -1
  239. package/src/types/settings.ts +8 -8
  240. package/src/utils/assets.ts +4 -1
  241. package/src/utils/deviceFeaturesCompat.ts +173 -0
  242. package/src/utils/deviceFeaturesUtils.ts +9 -8
  243. package/src/utils/deviceInfoUtils.ts +26 -64
  244. package/src/utils/deviceSettings.ts +18 -11
  245. package/src/utils/deviceVersionUtils.ts +25 -44
  246. package/src/utils/findDefectiveBatchDevice.ts +3 -3
  247. package/src/utils/index.ts +2 -5
  248. package/src/utils/networkUtils.ts +270 -14
  249. package/src/utils/pro2Wallpaper.ts +11 -13
  250. package/src/utils/tracing.ts +2 -2
  251. package/dist/api/internal.d.ts +0 -4
  252. package/dist/api/internal.d.ts.map +0 -1
  253. package/dist/api/protocol-v2/DeviceSessionOpen.d.ts +0 -28
  254. package/dist/api/protocol-v2/DeviceSessionOpen.d.ts.map +0 -1
  255. package/src/api/internal.ts +0 -9
  256. package/src/api/protocol-v2/DeviceSessionOpen.ts +0 -100
@@ -0,0 +1,221 @@
1
+ import { ERRORS, HardwareErrorCode } from '@onekeyfe/hd-shared';
2
+
3
+ import { deviceWalletSessionStore } from '../device/DeviceWalletSessionStore';
4
+ import { getProtocolV2WalletSession } from '../protocols/protocol-v2/walletSession';
5
+ import { getPassphraseStateWithRefreshDeviceInfo } from '../utils/deviceFeaturesUtils';
6
+ import { BaseMethod } from './BaseMethod';
7
+ import { invalidParameter } from './helpers/paramsValidator';
8
+
9
+ import type {
10
+ OpenWalletSessionParams,
11
+ OpenWalletSessionPayload,
12
+ } from '../types/api/openWalletSession';
13
+
14
+ const requiredString = (value: unknown, name: string) => {
15
+ if (value === undefined || value === null) {
16
+ throw invalidParameter(`Missing required parameter: ${name}`);
17
+ }
18
+ if (typeof value !== 'string' || !value.trim()) {
19
+ throw invalidParameter(`Parameter [${name}] must be a non-empty string.`);
20
+ }
21
+ return value.trim();
22
+ };
23
+
24
+ const optionalBoolean = (value: unknown, name: string) => {
25
+ if (value !== undefined && typeof value !== 'boolean') {
26
+ throw invalidParameter(`Parameter [${name}] must be a boolean.`);
27
+ }
28
+ return value;
29
+ };
30
+
31
+ const wasResumed = (session: unknown) =>
32
+ !!session &&
33
+ typeof session === 'object' &&
34
+ 'resumed' in session &&
35
+ (session as { resumed?: unknown }).resumed === true;
36
+
37
+ type NormalizedOpenWalletSessionParams = OpenWalletSessionParams & {
38
+ legacySessionToClear?: {
39
+ deviceId?: string;
40
+ passphraseState: string;
41
+ };
42
+ };
43
+
44
+ const normalizeParams = (payload: Record<string, unknown>): NormalizedOpenWalletSessionParams => {
45
+ if (payload.mode !== undefined) {
46
+ if (
47
+ payload.mode !== 'standard' &&
48
+ payload.mode !== 'select-hidden' &&
49
+ payload.mode !== 'resume-hidden'
50
+ ) {
51
+ throw invalidParameter(
52
+ 'Parameter [mode] must be one of standard, select-hidden, or resume-hidden.'
53
+ );
54
+ }
55
+ if (payload.useEmptyPassphrase !== undefined || payload.initSession !== undefined) {
56
+ throw invalidParameter(
57
+ 'Parameters [useEmptyPassphrase] and [initSession] cannot be used with [mode].'
58
+ );
59
+ }
60
+ if (payload.mode === 'standard' || payload.mode === 'select-hidden') {
61
+ if (payload.deviceId !== undefined || payload.passphraseState !== undefined) {
62
+ throw invalidParameter(
63
+ 'Parameters [deviceId] and [passphraseState] are only allowed with mode [resume-hidden].'
64
+ );
65
+ }
66
+ return { mode: payload.mode };
67
+ }
68
+ return {
69
+ mode: 'resume-hidden',
70
+ deviceId: requiredString(payload.deviceId, 'deviceId'),
71
+ passphraseState: requiredString(payload.passphraseState, 'passphraseState'),
72
+ };
73
+ }
74
+
75
+ const useEmptyPassphrase = optionalBoolean(payload.useEmptyPassphrase, 'useEmptyPassphrase');
76
+ const initSession = optionalBoolean(payload.initSession, 'initSession');
77
+ if (useEmptyPassphrase === true) {
78
+ return { mode: 'standard' };
79
+ }
80
+ if (initSession === true) {
81
+ const deviceId =
82
+ payload.deviceId === undefined ? undefined : requiredString(payload.deviceId, 'deviceId');
83
+ const passphraseState =
84
+ payload.passphraseState === undefined
85
+ ? undefined
86
+ : requiredString(payload.passphraseState, 'passphraseState');
87
+ return {
88
+ mode: 'select-hidden',
89
+ ...(passphraseState
90
+ ? {
91
+ legacySessionToClear: {
92
+ deviceId,
93
+ passphraseState,
94
+ },
95
+ }
96
+ : {}),
97
+ };
98
+ }
99
+
100
+ const hasWalletBinding = payload.deviceId !== undefined || payload.passphraseState !== undefined;
101
+ if (!hasWalletBinding) {
102
+ return { mode: 'select-hidden' };
103
+ }
104
+ return {
105
+ mode: 'resume-hidden',
106
+ deviceId: requiredString(payload.deviceId, 'deviceId'),
107
+ passphraseState: requiredString(payload.passphraseState, 'passphraseState'),
108
+ };
109
+ };
110
+
111
+ export default class OpenWalletSession extends BaseMethod<NormalizedOpenWalletSessionParams> {
112
+ init() {
113
+ this.useDevicePassphraseState = false;
114
+ this.skipForceUpdateCheck = true;
115
+ this.params = normalizeParams(this.payload as unknown as Record<string, unknown>);
116
+ this.payload.useEmptyPassphrase = this.params.mode === 'standard';
117
+ }
118
+
119
+ async run(): Promise<OpenWalletSessionPayload> {
120
+ const state = await this.device.getDeviceState({ refreshSections: ['status'] });
121
+ const currentDeviceId = state.identity.deviceId;
122
+ if (!currentDeviceId) {
123
+ throw ERRORS.TypedError(HardwareErrorCode.DeviceInitializeFailed);
124
+ }
125
+
126
+ const { legacySessionToClear } = this.params;
127
+ if (
128
+ legacySessionToClear &&
129
+ (!legacySessionToClear.deviceId || legacySessionToClear.deviceId === currentDeviceId)
130
+ ) {
131
+ deviceWalletSessionStore.delete(currentDeviceId, legacySessionToClear.passphraseState);
132
+ }
133
+
134
+ const protocol = this.device.isProtocolV2() ? 'V2' : 'V1';
135
+
136
+ if (this.params.mode === 'standard') {
137
+ this.device.passphraseState = undefined;
138
+ const session = this.device.isProtocolV2()
139
+ ? await getProtocolV2WalletSession(this.device, { onlyMainPin: true })
140
+ : await getPassphraseStateWithRefreshDeviceInfo(this.device, {
141
+ onlyMainPin: true,
142
+ initSession: this.payload.initSession,
143
+ });
144
+ if (session.unlockedAttachPin) {
145
+ try {
146
+ await this.device.lockDevice();
147
+ } catch {
148
+ // The wallet context is rejected even when an older device cannot be locked.
149
+ }
150
+ this.device.clearInternalState();
151
+ throw ERRORS.TypedError(HardwareErrorCode.DeviceCheckUnlockTypeError);
152
+ }
153
+ return {
154
+ protocol,
155
+ walletType: 'standard',
156
+ deviceId: currentDeviceId,
157
+ passphraseState: null,
158
+ resumed: wasResumed(session),
159
+ };
160
+ }
161
+
162
+ if (this.params.mode === 'resume-hidden') {
163
+ if (currentDeviceId !== this.params.deviceId) {
164
+ throw ERRORS.TypedError(HardwareErrorCode.DeviceCheckDeviceIdError);
165
+ }
166
+ this.device.passphraseState = this.params.passphraseState;
167
+ const cachedSessionId = deviceWalletSessionStore.get(
168
+ this.params.deviceId,
169
+ this.params.passphraseState
170
+ );
171
+ if (!cachedSessionId) {
172
+ throw ERRORS.TypedError(HardwareErrorCode.WalletSessionInvalid);
173
+ }
174
+ if (!this.device.isProtocolV2()) {
175
+ await this.device.initialize({
176
+ deviceId: this.params.deviceId,
177
+ passphraseState: this.params.passphraseState,
178
+ });
179
+ }
180
+ const session = this.device.isProtocolV2()
181
+ ? await getProtocolV2WalletSession(this.device, {
182
+ expectedPassphraseState: this.params.passphraseState,
183
+ })
184
+ : await getPassphraseStateWithRefreshDeviceInfo(this.device, {
185
+ expectPassphraseState: this.params.passphraseState,
186
+ });
187
+ if (!session.passphraseState) {
188
+ this.device.clearInternalState();
189
+ throw ERRORS.TypedError(HardwareErrorCode.DeviceCheckPassphraseStateError);
190
+ }
191
+ return {
192
+ protocol,
193
+ walletType: 'hidden',
194
+ deviceId: currentDeviceId,
195
+ passphraseState: session.passphraseState ?? null,
196
+ resumed: wasResumed(session) || session.newSession === cachedSessionId,
197
+ };
198
+ }
199
+
200
+ this.device.passphraseState = undefined;
201
+ const session = this.device.isProtocolV2()
202
+ ? await getProtocolV2WalletSession(this.device, { initSession: true })
203
+ : await getPassphraseStateWithRefreshDeviceInfo(this.device, { initSession: true });
204
+ const responseBase = {
205
+ protocol,
206
+ deviceId: currentDeviceId,
207
+ resumed: wasResumed(session),
208
+ } as const;
209
+ return session.passphraseState
210
+ ? {
211
+ ...responseBase,
212
+ walletType: 'hidden',
213
+ passphraseState: session.passphraseState,
214
+ }
215
+ : {
216
+ ...responseBase,
217
+ walletType: 'standard',
218
+ passphraseState: null,
219
+ };
220
+ }
221
+ }
@@ -1,7 +1,7 @@
1
1
  import { BaseMethod } from './BaseMethod';
2
2
  import {
3
- validateNonEmptyString,
4
3
  validateOptionalNonNegativeInteger,
4
+ validateProtocolV2FilesystemPath,
5
5
  } from './helpers/filesystemValidation';
6
6
 
7
7
  export type PathInfoParams = {
@@ -11,12 +11,14 @@ export type PathInfoParams = {
11
11
 
12
12
  export default class PathInfo extends BaseMethod<PathInfoParams> {
13
13
  init() {
14
- // Protocol V2 (Pro2) 专属方法,core 调度层统一做非 V2 设备守卫
14
+ // Protocol V2 (Pro2) only; Core rejects non-V2 devices.
15
15
  this.requireProtocolV2 = true;
16
16
  this.skipForceUpdateCheck = true;
17
17
  this.useDevicePassphraseState = false;
18
18
  this.params = {
19
- path: validateNonEmptyString(this.payload.path, 'path'),
19
+ path: validateProtocolV2FilesystemPath(this.payload.path, 'path', {
20
+ allowVolumeRoot: true,
21
+ }),
20
22
  timeoutMs: validateOptionalNonNegativeInteger(this.payload.timeoutMs, 'timeoutMs'),
21
23
  };
22
24
  }
@@ -54,9 +54,8 @@ export default class PromptWebDeviceAccess extends BaseMethod {
54
54
  const usbDevice = device as USBDevice;
55
55
  let path = usbDevice.serialNumber ?? '';
56
56
  if (!path) {
57
- // 早期 Pro2 工程板 USB descriptor 没有 serial number
58
- // 授权后重新枚举,transport 会为空 serial 设备生成会话内稳定的 mock path,
59
- // 这里按 USBDevice 对象身份找回该 path,保证后续 acquire 能匹配。
57
+ // Early Pro2 boards have no USB serial number. Re-enumeration assigns a
58
+ // session-stable mock path, which is recovered by USBDevice identity here.
60
59
  const diff = await this.connector?.enumerate();
61
60
  const matched = diff?.descriptors?.find(d => (d as any).device === usbDevice);
62
61
  path = matched?.path ?? '';
@@ -41,6 +41,10 @@ export default class SearchDevices extends BaseMethod {
41
41
  devices.push({
42
42
  ...device,
43
43
  connectId: device.id,
44
+ serialNo: null,
45
+ // Legacy BLE discovery identifier. The physical serial number is unavailable here.
46
+ uuid: device.id,
47
+ deviceId: null,
44
48
  name: bleName || device.name,
45
49
  deviceType: getDeviceTypeByBleName(bleName),
46
50
  });
@@ -52,8 +56,8 @@ export default class SearchDevices extends BaseMethod {
52
56
  const deviceList = [];
53
57
  for (const descriptor of devicesDescriptor) {
54
58
  try {
55
- // 设备发现是尽力而为的操作。浏览器可能保留已经离线、忙碌或尚未就绪的
56
- // WebUSB 授权记录,单个描述符初始化失败不应中断其余设备的发现。
59
+ // Discovery is best effort. Browsers may retain WebUSB grants for devices that
60
+ // are offline, busy, or not ready, so one descriptor must not abort the scan.
57
61
  const result = await DevicePool.getDevices([descriptor], descriptor.path, {
58
62
  connectProtocol: this.payload.connectProtocol,
59
63
  refreshRuntimeState: true,
@@ -8,10 +8,16 @@ export type UploadPortfolioParams = {
8
8
 
9
9
  const PORTFOLIO_PENDING_PATH = 'vol1:/portfolio/portfolio.okpkg.pending';
10
10
  const PORTFOLIO_CHUNK_SIZE = 2048;
11
+ const PORTFOLIO_RESPONSE_TIMEOUT_MS = 5_000;
12
+ const PORTFOLIO_MAX_ABORT_LATENCY_MS = PORTFOLIO_RESPONSE_TIMEOUT_MS * 2 + 2_000;
11
13
 
12
14
  export default class UploadPortfolio extends FileWrite {
13
15
  init() {
14
16
  const { packageBytes, timeoutMs } = this.payload as UploadPortfolioParams;
17
+ const responseTimeoutMs = Math.min(
18
+ timeoutMs === undefined ? PORTFOLIO_RESPONSE_TIMEOUT_MS : Number(timeoutMs),
19
+ PORTFOLIO_RESPONSE_TIMEOUT_MS
20
+ );
15
21
  this.payload = {
16
22
  ...this.payload,
17
23
  path: PORTFOLIO_PENDING_PATH,
@@ -21,18 +27,21 @@ export default class UploadPortfolio extends FileWrite {
21
27
  overwrite: true,
22
28
  append: false,
23
29
  emitProgress: false,
24
- timeoutMs,
30
+ timeoutMs: responseTimeoutMs,
25
31
  };
26
32
  super.init();
33
+ this.executionPriority = 'background';
34
+ this.maxAbortLatencyMs = PORTFOLIO_MAX_ABORT_LATENCY_MS;
27
35
  this.unlockPolicy = 'retry-on-locked';
28
- // Portfolio 是后台数据写入与应用流程,设备不需要用户确认;包括自动解锁阶段在内均不合成 UI Event。
36
+ // Portfolio is a background write/apply flow and never synthesizes UI events.
29
37
  this.protocolV2UiMode = 'none';
30
38
  }
31
39
 
32
40
  async run() {
33
41
  const stagedFile = await super.run();
34
42
  this.throwIfAborted();
35
- await this.device.commands.typedCall('PortfolioUpdate', 'Success', {});
43
+ const timeoutMs = Number(this.params.timeoutMs);
44
+ await this.device.commands.typedCall('PortfolioUpdate', 'Success', {}, { timeoutMs });
36
45
  return {
37
46
  ...stagedFile,
38
47
  portfolioUpdated: true,
@@ -63,8 +63,8 @@ export default class AlephiumSignTransaction extends BaseMethod<HardwareAlephium
63
63
  data: Buffer,
64
64
  scriptOpt?: Buffer,
65
65
  dataOffset = 0
66
- // 设备可能在最后一个 AlephiumTxRequest(无 data_length)里返回签名,
67
- // 该消息没有 address 字段,返回类型如实声明为联合类型
66
+ // The final AlephiumTxRequest may return a signature without data_length or address,
67
+ // so the result accurately uses a union type.
68
68
  ): Promise<AlephiumSignedTx | AlephiumTxRequest> => {
69
69
  const responseType = res.type;
70
70
  if (res.type === 'AlephiumSignedTx') {
@@ -352,6 +352,7 @@ export default abstract class AllNetworkGetAddressBase extends BaseMethod<
352
352
  };
353
353
 
354
354
  let result: AllNetworkAddress[];
355
+ let requestError: Error | undefined;
355
356
  try {
356
357
  method.init();
357
358
  method.setDevice?.(this.device);
@@ -394,10 +395,8 @@ export default abstract class AllNetworkGetAddressBase extends BaseMethod<
394
395
  rootFingerprint,
395
396
  },
396
397
  }));
397
- if (method.requestContext) {
398
- completeRequestContext(method.requestContext.responseID);
399
- }
400
398
  } catch (e: any) {
399
+ requestError = e instanceof Error ? e : new Error(String(e));
401
400
  const error = handleSkippableHardwareError(e, this.device, method);
402
401
 
403
402
  if (error) {
@@ -415,13 +414,10 @@ export default abstract class AllNetworkGetAddressBase extends BaseMethod<
415
414
  } else {
416
415
  throw e;
417
416
  }
417
+ } finally {
418
418
  if (method.requestContext) {
419
- completeRequestContext(
420
- method.requestContext.responseID,
421
- e instanceof Error ? e : new Error(String(e))
422
- );
419
+ completeRequestContext(method.requestContext.responseID, requestError);
423
420
  }
424
- } finally {
425
421
  this.device.off(DEVICE.BUTTON, buttonListener);
426
422
  this.device.off(DEVICE.PIN, onSignalAbort);
427
423
  this.device.off(DEVICE.PASSPHRASE, onSignalAbort);
@@ -78,7 +78,7 @@ export default class BenfenGetAddress extends BaseMethod<HardwareBenfenGetAddres
78
78
  'BenfenAddress',
79
79
  param
80
80
  );
81
- // proto 里 BenfenAddress.address optional;正常响应必然携带
81
+ // BenfenAddress.address is optional in proto but always present on success.
82
82
  address = addressRes.message.address ?? '';
83
83
  } else {
84
84
  address = publicKeyToAddress(publicKey);
@@ -78,7 +78,7 @@ export default class BTCGetPublicKey extends BaseMethod<GetPublicKey[]> {
78
78
  }
79
79
 
80
80
  for (const param of this.params) {
81
- // init() 必然设置 coin_name;生成类型里该字段为 optional,这里兜底空串
81
+ // init() sets coin_name; keep an empty fallback for the generated optional type.
82
82
  const versionBytes = getVersionBytes(param.coin_name ?? '', param.script_type);
83
83
  if (!versionBytes) {
84
84
  throw new Error(
@@ -8,6 +8,7 @@ import type { ChangePin } from '@onekeyfe/hd-transport';
8
8
 
9
9
  export default class DeviceChangePin extends BaseMethod<ChangePin> {
10
10
  init() {
11
+ this.unlockPolicy = 'retry-on-locked';
11
12
  this.useDevicePassphraseState = false;
12
13
 
13
14
  // check payload
@@ -5,12 +5,11 @@ import { UI_REQUEST } from '../../constants/ui-request';
5
5
  import { BaseMethod } from '../BaseMethod';
6
6
  import { getSysResourceBinary } from '../firmware/getBinary';
7
7
  import { updateResources } from '../firmware/uploadFirmware';
8
- import { getDeviceFirmwareVersion, getFirmwareType } from '../../utils';
9
8
  import { createUiMessage } from '../../events/ui-request';
10
9
  import { DataManager } from '../../data-manager';
11
10
 
12
11
  import type { Deferred } from '@onekeyfe/hd-shared';
13
- import type { Features, KnownDevice } from '../../types';
12
+ import type { KnownDevice } from '../../types';
14
13
  import type { DeviceFullyUploadResourceParams } from '../../types/api/deviceFullyUploadResource';
15
14
 
16
15
  export default class DeviceFullyUploadResource extends BaseMethod {
@@ -33,12 +32,12 @@ export default class DeviceFullyUploadResource extends BaseMethod {
33
32
  );
34
33
  };
35
34
 
36
- isSupportResourceUpdate(features: Features, updateType: string) {
35
+ isSupportResourceUpdate(updateType: string) {
37
36
  if (updateType !== 'firmware') return false;
38
37
 
39
38
  const deviceType = this.device.getCurrentDeviceType();
40
39
  const isTouchMode = deviceType === EDeviceType.Touch || deviceType === EDeviceType.Pro;
41
- const currentVersion = getDeviceFirmwareVersion(features).join('.');
40
+ const currentVersion = this.device.getCurrentFirmwareVersionString() ?? '0.0.0';
42
41
 
43
42
  return isTouchMode && semver.gte(currentVersion, '3.4.0');
44
43
  }
@@ -49,7 +48,7 @@ export default class DeviceFullyUploadResource extends BaseMethod {
49
48
 
50
49
  const payload = this.payload as DeviceFullyUploadResourceParams;
51
50
 
52
- const deviceFirmwareType = getFirmwareType(features);
51
+ const deviceFirmwareType = device.getCurrentFirmwareType();
53
52
  const firmwareType = payload.firmwareType ?? deviceFirmwareType;
54
53
 
55
54
  if (!device.isBootloader() && features) {
@@ -1,4 +1,5 @@
1
1
  import { HardwareErrorCode, TypedError } from '@onekeyfe/hd-shared';
2
+ import { DeviceSettingsPage } from '@onekeyfe/hd-transport';
2
3
 
3
4
  import { BaseMethod } from '../BaseMethod';
4
5
  import { invalidParameter } from '../helpers/filesystemValidation';
@@ -8,9 +9,23 @@ import {
8
9
  mapCommonSettingsToProtocolV2,
9
10
  mapDeviceSettingsToState,
10
11
  } from '../../device/DeviceStateMapper';
12
+ import { getProtocolV2SettingsBehavior } from '../../protocols/protocol-v2/settingsUnlockPolicy';
11
13
  import { LANGUAGE_LABELS } from '../../utils/deviceSettings';
12
14
 
13
15
  import type { ApplySettings } from '@onekeyfe/hd-transport';
16
+ import type { DeviceSettingsParams } from '../../types/api/deviceSettings';
17
+
18
+ const settingsPageForPayload = (
19
+ payload: DeviceSettingsParams
20
+ ): DeviceSettingsPage.DevicePassphrase | DeviceSettingsPage.DeviceAirgap | undefined => {
21
+ if (payload.usePassphrase !== undefined) {
22
+ return DeviceSettingsPage.DevicePassphrase;
23
+ }
24
+ if (payload.airgapMode !== undefined) {
25
+ return DeviceSettingsPage.DeviceAirgap;
26
+ }
27
+ return undefined;
28
+ };
14
29
 
15
30
  export default class DeviceSettings extends BaseMethod<ApplySettings> {
16
31
  init() {
@@ -33,6 +48,7 @@ export default class DeviceSettings extends BaseMethod<ApplySettings> {
33
48
  { name: 'brightness', type: 'number' },
34
49
  { name: 'hapticFeedback', type: 'boolean' },
35
50
  { name: 'bluetoothEnabled', type: 'boolean' },
51
+ { name: 'airgapMode', type: 'boolean' },
36
52
  { name: 'animationEnabled', type: 'boolean' },
37
53
  { name: 'tapToWake', type: 'boolean' },
38
54
  { name: 'deviceNameDisplayEnabled', type: 'boolean' },
@@ -59,6 +75,28 @@ export default class DeviceSettings extends BaseMethod<ApplySettings> {
59
75
  : undefined),
60
76
  haptic_feedback: this.payload.hapticFeedback,
61
77
  };
78
+ const page = settingsPageForPayload(this.payload);
79
+ const directSettings = mapCommonSettingsToProtocolV2(this.payload);
80
+ const hasConflictingPages =
81
+ this.payload.usePassphrase !== undefined && this.payload.airgapMode !== undefined;
82
+ const combinesPageWithDirectSettings =
83
+ page !== undefined && Object.keys(directSettings).length > 0;
84
+ if (hasConflictingPages || combinesPageWithDirectSettings) {
85
+ // V1 may still accept passphrase plus direct settings. Suppress only the
86
+ // Protocol V2 pre-run interaction; run() returns the protocol-specific error.
87
+ this.unlockPolicy = 'none';
88
+ this.protocolV2UiInteraction = undefined;
89
+ return;
90
+ }
91
+ const behavior =
92
+ page === undefined
93
+ ? getProtocolV2SettingsBehavior({
94
+ kind: 'direct',
95
+ settings: directSettings,
96
+ })
97
+ : getProtocolV2SettingsBehavior({ kind: 'page', page });
98
+ this.unlockPolicy = behavior.unlockPolicy;
99
+ this.protocolV2UiInteraction = behavior.uiInteraction;
62
100
  }
63
101
 
64
102
  getVersionRange() {
@@ -76,6 +114,57 @@ export default class DeviceSettings extends BaseMethod<ApplySettings> {
76
114
  try {
77
115
  if (this.device.isProtocolV2()) {
78
116
  const settings = mapCommonSettingsToProtocolV2(this.payload);
117
+ const requestedPassphrase = this.payload.usePassphrase;
118
+ const requestedAirgap = this.payload.airgapMode;
119
+ const hasPassphrasePage = requestedPassphrase !== undefined;
120
+ const hasAirgapPage = requestedAirgap !== undefined;
121
+
122
+ if (hasPassphrasePage && hasAirgapPage) {
123
+ throw invalidParameter(
124
+ 'Protocol V2 passphrase and air-gap settings must be changed in separate calls.'
125
+ );
126
+ }
127
+ if ((hasPassphrasePage || hasAirgapPage) && Object.keys(settings).length > 0) {
128
+ throw invalidParameter(
129
+ 'Protocol V2 on-device settings must not be combined with direct settings.'
130
+ );
131
+ }
132
+ if (requestedPassphrase !== undefined) {
133
+ const current = await this.device.getDeviceState({ refreshSections: ['status'] });
134
+ if (current.status.passphraseProtection === requestedPassphrase) {
135
+ return { message: 'Settings already match requested value.' };
136
+ }
137
+
138
+ const res = await this.device.commands.typedCall('DeviceSettingsPageShow', 'Success', {
139
+ page: DeviceSettingsPage.DevicePassphrase,
140
+ });
141
+ const updated = await this.device.getDeviceState({ refreshSections: ['status'] });
142
+ if (updated.status.passphraseProtection !== requestedPassphrase) {
143
+ throw TypedError(
144
+ HardwareErrorCode.RuntimeError,
145
+ 'Protocol V2 passphrase setting did not reach the requested value.'
146
+ );
147
+ }
148
+ return res.message;
149
+ }
150
+ if (requestedAirgap !== undefined) {
151
+ const current = await this.device.getDeviceState({ refreshSections: ['settings'] });
152
+ if (current.settings.airgapMode === requestedAirgap) {
153
+ return { message: 'Settings already match requested value.' };
154
+ }
155
+
156
+ const res = await this.device.commands.typedCall('DeviceSettingsPageShow', 'Success', {
157
+ page: DeviceSettingsPage.DeviceAirgap,
158
+ });
159
+ const updated = await this.device.getDeviceState({ refreshSections: ['settings'] });
160
+ if (updated.settings.airgapMode !== requestedAirgap) {
161
+ throw TypedError(
162
+ HardwareErrorCode.RuntimeError,
163
+ 'Protocol V2 air-gap setting did not reach the requested value.'
164
+ );
165
+ }
166
+ return res.message;
167
+ }
79
168
  if (Object.keys(settings).length === 0) {
80
169
  throw invalidParameter('No Protocol V2 compatible setting provided.');
81
170
  }
@@ -87,6 +176,10 @@ export default class DeviceSettings extends BaseMethod<ApplySettings> {
87
176
  return res.message;
88
177
  }
89
178
 
179
+ if (this.payload.airgapMode !== undefined) {
180
+ throw invalidParameter('Air-gap settings are not supported by Protocol V1.');
181
+ }
182
+
90
183
  const res = await this.device.commands.typedCall('ApplySettings', 'Success', {
91
184
  ...this.params,
92
185
  });
@@ -9,7 +9,6 @@ import { updateBootloader } from '../firmware/uploadFirmware';
9
9
  import { DeviceModelToTypes } from '../../types';
10
10
  import { DataManager } from '../../data-manager';
11
11
  import { checkBootloaderLength } from '../firmware/updateBootloader';
12
- import { getFirmwareType } from '../../utils';
13
12
 
14
13
  import type { DeviceUpdateBootloaderParams } from '../../types/api/deviceUpdateBootloader';
15
14
  import type { EFirmwareType } from '@onekeyfe/hd-shared';
@@ -99,7 +98,7 @@ export default class DeviceUpdateBootloader extends FirmwareUpdateBaseMethod<any
99
98
  const payload = this.payload as DeviceUpdateBootloaderParams;
100
99
 
101
100
  const deviceType = device.getCurrentDeviceType();
102
- const deviceFirmwareType = getFirmwareType(features);
101
+ const deviceFirmwareType = device.getCurrentFirmwareType();
103
102
  const firmwareType = payload.firmwareType ?? deviceFirmwareType;
104
103
 
105
104
  if (DeviceModelToTypes.model_touch.includes(deviceType)) {
@@ -6,6 +6,7 @@ import type { WipeDevice } from '@onekeyfe/hd-transport';
6
6
 
7
7
  export default class DeviceWipe extends BaseMethod<WipeDevice> {
8
8
  init() {
9
+ this.unlockPolicy = 'retry-on-locked';
9
10
  this.useDevicePassphraseState = false;
10
11
  this.protocolV2UiInteraction = {
11
12
  request: 'button',
@@ -32,9 +32,8 @@ import type {
32
32
  } from '@onekeyfe/hd-transport';
33
33
 
34
34
  /**
35
- * EthereumTypedDataStructAckOneKey EthereumTypedDataStructAck 的字段结构与
36
- * 枚举数值完全一致(生成产物的 OneKey/Trezor 双份消息),仅枚举名义类型不同;
37
- * 这里做无运行时成本的名义转换,避免在调用点散落 any。
35
+ * EthereumTypedDataStructAckOneKey and EthereumTypedDataStructAck have identical
36
+ * fields and enum values. This zero-cost nominal conversion avoids scattered `any`.
38
37
  */
39
38
  const toOneKeyStructAck = (ack: EthereumTypedDataStructAck): EthereumTypedDataStructAckOneKey =>
40
39
  ack as unknown as EthereumTypedDataStructAckOneKey;
@@ -227,8 +226,7 @@ export default class EVMSignTypedData extends BaseMethod<EVMSignTypedDataParams>
227
226
  if (response.type === 'EthereumGnosisSafeTxRequest') {
228
227
  const { data } = this.params;
229
228
  const verifyingContract = data.domain?.verifyingContract;
230
- // EthereumGnosisSafeTxAck.verifyingContract proto 中是 required 字段,
231
- // Gnosis Safe 签名缺少 verifyingContract 没有意义,这里给出明确的参数错误。
229
+ // verifyingContract is required by proto and essential to a Gnosis Safe signature.
232
230
  if (!verifyingContract) {
233
231
  throw ERRORS.TypedError(
234
232
  HardwareErrorCode.CallMethodInvalidParameter,
@@ -7,8 +7,8 @@ export default async function ({
7
7
  typedCall: TypedCall;
8
8
  param: EthereumGetAddressOneKey;
9
9
  }): Promise<MessageResponse<'EthereumAddress'>> {
10
- // legacy EthereumGetAddress 的生成类型没有 chain_id 字段,但旧固件按 OneKey 扩展
11
- // 接受该字段;通过预先声明的对象(非 fresh literal)携带额外字段,保持原有运行时行为。
10
+ // The generated legacy type omits chain_id, but old firmware accepts the OneKey
11
+ // extension. A non-fresh object preserves that runtime field without a type cast.
12
12
  const message = {
13
13
  address_n: param.address_n,
14
14
  show_display: param.show_display,
@@ -11,8 +11,8 @@ export default async function ({
11
11
  typedCall: TypedCall;
12
12
  param: EthereumGetPublicKeyOneKey;
13
13
  }): Promise<MessageResponse<'EthereumPublicKey'>> {
14
- // legacy EthereumGetPublicKey 的生成类型没有 chain_id 字段,但旧固件按 OneKey 扩展
15
- // 接受该字段;通过预先声明的对象(非 fresh literal)携带额外字段,保持原有运行时行为。
14
+ // The generated legacy type omits chain_id, but old firmware accepts the OneKey
15
+ // extension. A non-fresh object preserves that runtime field without a type cast.
16
16
  const message = {
17
17
  address_n: param.address_n,
18
18
  show_display: param.show_display,
@@ -11,8 +11,8 @@ export default async function ({
11
11
  typedCall: TypedCall;
12
12
  params: EthereumSignMessageOneKey;
13
13
  }): Promise<EthereumMessageSignature> {
14
- // legacy EthereumSignMessage 的生成类型没有 chain_id 字段,但旧固件按 OneKey 扩展
15
- // 接受该字段;通过预先声明的对象(非 fresh literal)携带额外字段,保持原有运行时行为。
14
+ // The generated legacy type omits chain_id, but old firmware accepts the OneKey
15
+ // extension. A non-fresh object preserves that runtime field without a type cast.
16
16
  const message = {
17
17
  address_n: params.address_n,
18
18
  message: params.message,