@trezor/connect 9.2.3 → 9.2.4-beta.1

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 (104) hide show
  1. package/CHANGELOG.md +43 -5
  2. package/README.md +1 -1
  3. package/lib/api/applyFlags.d.ts +8 -1
  4. package/lib/api/applyFlags.js +3 -8
  5. package/lib/api/applySettings.d.ts +8 -1
  6. package/lib/api/applySettings.js +3 -8
  7. package/lib/api/backupDevice.d.ts +10 -2
  8. package/lib/api/backupDevice.js +12 -9
  9. package/lib/api/binance/api/binanceGetAddress.d.ts +4 -3
  10. package/lib/api/binance/api/binanceGetAddress.js +8 -22
  11. package/lib/api/binance/api/binanceGetPublicKey.d.ts +4 -2
  12. package/lib/api/binance/api/binanceGetPublicKey.js +6 -18
  13. package/lib/api/cardano/api/cardanoGetAddress.d.ts +4 -3
  14. package/lib/api/cardano/api/cardanoGetAddress.js +8 -22
  15. package/lib/api/cardano/api/cardanoGetPublicKey.d.ts +4 -3
  16. package/lib/api/cardano/api/cardanoGetPublicKey.js +9 -31
  17. package/lib/api/changeLanguage.d.ts +8 -1
  18. package/lib/api/changeLanguage.js +3 -7
  19. package/lib/api/eos/api/eosGetPublicKey.d.ts +4 -2
  20. package/lib/api/eos/api/eosGetPublicKey.js +6 -18
  21. package/lib/api/ethereum/api/ethereumGetAddress.d.ts +4 -3
  22. package/lib/api/ethereum/api/ethereumGetAddress.js +4 -20
  23. package/lib/api/ethereum/api/ethereumGetPublicKey.d.ts +4 -2
  24. package/lib/api/ethereum/api/ethereumGetPublicKey.js +3 -10
  25. package/lib/api/firmware/index.d.ts +2 -0
  26. package/lib/api/firmware/index.js +5 -1
  27. package/lib/api/firmware/parseFirmwareHeaders.d.ts +5 -0
  28. package/lib/api/firmware/parseFirmwareHeaders.js +35 -0
  29. package/lib/api/firmware/uploadFirmware.d.ts +1 -1
  30. package/lib/api/firmware/uploadFirmware.js +9 -1
  31. package/lib/api/getAccountDescriptor.d.ts +5 -3
  32. package/lib/api/getAccountDescriptor.js +8 -21
  33. package/lib/api/getAccountInfo.d.ts +23 -7
  34. package/lib/api/getAccountInfo.js +12 -25
  35. package/lib/api/getAddress.d.ts +4 -3
  36. package/lib/api/getAddress.js +8 -22
  37. package/lib/api/getFeatures.d.ts +1 -0
  38. package/lib/api/getOwnershipId.d.ts +4 -2
  39. package/lib/api/getOwnershipId.js +4 -15
  40. package/lib/api/getOwnershipProof.d.ts +4 -2
  41. package/lib/api/getOwnershipProof.js +4 -15
  42. package/lib/api/getPublicKey.d.ts +5 -4
  43. package/lib/api/getPublicKey.js +12 -35
  44. package/lib/api/index.d.ts +0 -1
  45. package/lib/api/index.js +1 -3
  46. package/lib/api/nem/api/nemGetAddress.d.ts +4 -3
  47. package/lib/api/nem/api/nemGetAddress.js +4 -20
  48. package/lib/api/rebootToBootloader.d.ts +8 -2
  49. package/lib/api/rebootToBootloader.js +3 -10
  50. package/lib/api/recoveryDevice.d.ts +8 -1
  51. package/lib/api/recoveryDevice.js +3 -7
  52. package/lib/api/resetDevice.d.ts +4 -2
  53. package/lib/api/resetDevice.js +3 -10
  54. package/lib/api/ripple/api/rippleGetAddress.d.ts +4 -3
  55. package/lib/api/ripple/api/rippleGetAddress.js +4 -20
  56. package/lib/api/solana/api/solanaGetAddress.d.ts +4 -3
  57. package/lib/api/solana/api/solanaGetAddress.js +7 -28
  58. package/lib/api/solana/api/solanaGetPublicKey.d.ts +4 -3
  59. package/lib/api/solana/api/solanaGetPublicKey.js +7 -27
  60. package/lib/api/stellar/api/stellarGetAddress.d.ts +4 -3
  61. package/lib/api/stellar/api/stellarGetAddress.js +4 -20
  62. package/lib/api/tezos/api/tezosGetAddress.d.ts +4 -3
  63. package/lib/api/tezos/api/tezosGetAddress.js +4 -20
  64. package/lib/api/tezos/api/tezosGetPublicKey.d.ts +4 -2
  65. package/lib/api/tezos/api/tezosGetPublicKey.js +6 -18
  66. package/lib/api/wipeDevice.d.ts +8 -2
  67. package/lib/api/wipeDevice.js +3 -10
  68. package/lib/constants/errors.d.ts +1 -0
  69. package/lib/constants/errors.js +1 -0
  70. package/lib/core/AbstractMethod.d.ts +4 -8
  71. package/lib/core/AbstractMethod.js +6 -28
  72. package/lib/core/index.d.ts +2 -1
  73. package/lib/core/index.js +254 -225
  74. package/lib/core/onCallFirmwareUpdate.d.ts +31 -0
  75. package/lib/core/onCallFirmwareUpdate.js +259 -0
  76. package/lib/data/analyticsInfo.d.ts +2 -2
  77. package/lib/data/analyticsInfo.js +6 -7
  78. package/lib/data/deviceAuthenticityConfig.js +8 -4
  79. package/lib/data/firmwareInfo.js +1 -1
  80. package/lib/data/version.d.ts +1 -1
  81. package/lib/data/version.js +1 -1
  82. package/lib/device/Device.d.ts +1 -1
  83. package/lib/device/Device.js +2 -2
  84. package/lib/device/DeviceCommands.d.ts +2 -14
  85. package/lib/device/DeviceCommands.js +1 -43
  86. package/lib/device/DeviceList.d.ts +1 -1
  87. package/lib/device/DeviceList.js +20 -15
  88. package/lib/events/index.d.ts +1 -0
  89. package/lib/events/ui-request.d.ts +13 -1
  90. package/lib/events/ui-request.js +1 -0
  91. package/lib/index.js +2 -2
  92. package/lib/types/api/backupDevice.d.ts +2 -2
  93. package/lib/types/api/dispose.d.ts +1 -1
  94. package/lib/types/api/firmwareUpdate.d.ts +7 -6
  95. package/lib/types/api/firmwareUpdate.js +1 -3
  96. package/lib/utils/popupPromiseManager.d.ts +8 -0
  97. package/lib/utils/popupPromiseManager.js +21 -0
  98. package/lib/utils/uiPromiseManager.d.ts +38 -0
  99. package/lib/utils/uiPromiseManager.js +47 -0
  100. package/lib/workers/workers-browser.d.ts +3 -1
  101. package/lib/workers/workers-browser.js +2 -2
  102. package/package.json +6 -6
  103. package/lib/api/firmwareUpdate.d.ts +0 -19
  104. package/lib/api/firmwareUpdate.js +0 -78
package/lib/core/index.js CHANGED
@@ -14,13 +14,14 @@ const constants_1 = require("../constants");
14
14
  const events_2 = require("../events");
15
15
  const method_1 = require("./method");
16
16
  const promiseUtils_1 = require("../utils/promiseUtils");
17
+ const uiPromiseManager_1 = require("../utils/uiPromiseManager");
18
+ const popupPromiseManager_1 = require("../utils/popupPromiseManager");
17
19
  const debug_1 = require("../utils/debug");
18
20
  const BlockchainLink_1 = require("../backend/BlockchainLink");
19
21
  const interactionTimeout_1 = require("../utils/interactionTimeout");
22
+ const onCallFirmwareUpdate_1 = require("./onCallFirmwareUpdate");
20
23
  let _core;
21
24
  let _deviceList;
22
- let _popupPromise;
23
- const _uiPromises = [];
24
25
  const _callMethods = [];
25
26
  let _interactionTimeout;
26
27
  let _deviceListInitTimeout;
@@ -40,71 +41,16 @@ const postMessage = (message) => {
40
41
  }
41
42
  _core.emit(events_2.CORE_EVENT, message);
42
43
  };
43
- const getPopupPromise = (requestWindow = true) => {
44
- if (requestWindow) {
45
- postMessage((0, events_2.createUiMessage)(events_2.UI.REQUEST_UI_WINDOW));
46
- }
47
- if (!_popupPromise) {
48
- _popupPromise = (0, utils_1.createDeferred)();
49
- }
50
- return _popupPromise;
44
+ const popupPromise = (0, popupPromiseManager_1.createPopupPromiseManager)();
45
+ const waitForPopup = () => {
46
+ postMessage((0, events_2.createUiMessage)(events_2.UI.REQUEST_UI_WINDOW));
47
+ return popupPromise.wait();
51
48
  };
52
49
  const interactionTimeout = () => _interactionTimeout.start(() => {
53
50
  onPopupClosed('Interaction timeout');
54
51
  });
55
- const createUiPromise = (promiseEvent, device) => {
56
- const uiPromise = {
57
- ...(0, utils_1.createDeferred)(promiseEvent),
58
- device,
59
- };
60
- _uiPromises.push(uiPromise);
61
- interactionTimeout();
62
- return uiPromise;
63
- };
64
- const findUiPromise = (promiseEvent) => _uiPromises.find(p => p.id === promiseEvent);
65
- const removeUiPromise = (promise) => {
66
- _uiPromises.splice(0).push(..._uiPromises.filter(p => p !== promise));
67
- };
68
- const handleMessage = (message) => {
69
- _log.debug('handleMessage', message);
70
- switch (message.type) {
71
- case events_2.POPUP.HANDSHAKE:
72
- getPopupPromise(false).resolve();
73
- break;
74
- case events_2.POPUP.CLOSED:
75
- onPopupClosed(message.payload ? message.payload.error : null);
76
- break;
77
- case transport_1.TRANSPORT.DISABLE_WEBUSB:
78
- disableWebUSBTransport();
79
- break;
80
- case transport_1.TRANSPORT.REQUEST_DEVICE:
81
- _deviceList === null || _deviceList === void 0 ? void 0 : _deviceList.enumerate();
82
- break;
83
- case events_2.UI.RECEIVE_DEVICE:
84
- case events_2.UI.RECEIVE_CONFIRMATION:
85
- case events_2.UI.RECEIVE_PERMISSION:
86
- case events_2.UI.RECEIVE_PIN:
87
- case events_2.UI.RECEIVE_PASSPHRASE:
88
- case events_2.UI.INVALID_PASSPHRASE_ACTION:
89
- case events_2.UI.RECEIVE_ACCOUNT:
90
- case events_2.UI.RECEIVE_FEE:
91
- case events_2.UI.RECEIVE_WORD:
92
- case events_2.UI.LOGIN_CHALLENGE_RESPONSE: {
93
- const uiPromise = findUiPromise(message.type);
94
- if (uiPromise) {
95
- uiPromise.resolve(message);
96
- removeUiPromise(uiPromise);
97
- }
98
- break;
99
- }
100
- case events_2.IFRAME.CALL:
101
- onCall(message).catch(error => {
102
- _log.error('onCall', error);
103
- });
104
- break;
105
- }
106
- };
107
- const initDevice = async (method) => {
52
+ const uiPromises = (0, uiPromiseManager_1.createUiPromiseManager)(interactionTimeout);
53
+ const initDevice = async (devicePath) => {
108
54
  if (!_deviceList) {
109
55
  throw constants_1.ERRORS.TypedError('Transport_Missing');
110
56
  }
@@ -123,8 +69,8 @@ const initDevice = async (method) => {
123
69
  return store;
124
70
  });
125
71
  }
126
- if (method.devicePath) {
127
- device = _deviceList.getDevice(method.devicePath);
72
+ if (devicePath) {
73
+ device = _deviceList.getDevice(devicePath);
128
74
  showDeviceSelection =
129
75
  !device || !!(device === null || device === void 0 ? void 0 : device.unreadableError) || (device.isUnacquired() && !!isUsingPopup);
130
76
  }
@@ -140,8 +86,8 @@ const initDevice = async (method) => {
140
86
  }
141
87
  }
142
88
  if (showDeviceSelection) {
143
- createUiPromise(events_2.UI.RECEIVE_DEVICE);
144
- await getPopupPromise().promise;
89
+ uiPromises.create(events_2.UI.RECEIVE_DEVICE);
90
+ await waitForPopup();
145
91
  if (!_deviceList) {
146
92
  throw constants_1.ERRORS.TypedError('Transport_Missing');
147
93
  }
@@ -158,9 +104,8 @@ const initDevice = async (method) => {
158
104
  webusb: isWebUsb,
159
105
  devices: _deviceList.asArray(),
160
106
  }));
161
- const uiPromise = findUiPromise(events_2.UI.RECEIVE_DEVICE);
162
- if (uiPromise) {
163
- const { payload } = await uiPromise.promise;
107
+ if (uiPromises.exists(events_2.UI.RECEIVE_DEVICE)) {
108
+ const { payload } = await uiPromises.get(events_2.UI.RECEIVE_DEVICE);
164
109
  if (payload.remember) {
165
110
  const { label, path, state } = payload.device;
166
111
  connect_common_1.storage.save(store => {
@@ -180,14 +125,164 @@ const initDevice = async (method) => {
180
125
  }
181
126
  return device;
182
127
  };
128
+ const MAX_PIN_TRIES = 3;
129
+ const getInvalidDeviceState = async (device, network, preauthorized) => {
130
+ for (let i = 0; i < MAX_PIN_TRIES - 1; ++i) {
131
+ try {
132
+ return await device.validateState(network, preauthorized);
133
+ }
134
+ catch (error) {
135
+ if (error.message.includes('PIN invalid')) {
136
+ postMessage((0, events_2.createUiMessage)(events_2.UI.INVALID_PIN, { device: device.toMessageObject() }));
137
+ }
138
+ else {
139
+ throw error;
140
+ }
141
+ }
142
+ }
143
+ return device.validateState(network, preauthorized);
144
+ };
145
+ const inner = async (method, device) => {
146
+ var _a;
147
+ const trustedHost = DataManager_1.DataManager.getSettings('trustedHost');
148
+ const isUsingPopup = (_a = DataManager_1.DataManager.getSettings('popup')) !== null && _a !== void 0 ? _a : false;
149
+ const firmwareException = method.checkFirmwareRange();
150
+ if (firmwareException) {
151
+ if (isUsingPopup) {
152
+ if (firmwareException === events_2.UI.FIRMWARE_NOT_COMPATIBLE) {
153
+ await waitForPopup();
154
+ const uiPromise = uiPromises.create(events_2.UI.RECEIVE_CONFIRMATION, device);
155
+ postMessage((0, events_2.createUiMessage)(events_2.UI.FIRMWARE_NOT_COMPATIBLE, device.toMessageObject()));
156
+ const uiResp = await uiPromise.promise;
157
+ if (!uiResp.payload) {
158
+ throw constants_1.ERRORS.TypedError('Method_PermissionsNotGranted');
159
+ }
160
+ }
161
+ else {
162
+ await waitForPopup();
163
+ postMessage((0, events_2.createUiMessage)(firmwareException, device.toMessageObject()));
164
+ await uiPromises.create(events_2.DEVICE.DISCONNECT, device).promise;
165
+ return Promise.reject(constants_1.ERRORS.TypedError('Method_Cancel'));
166
+ }
167
+ }
168
+ else {
169
+ return Promise.reject(constants_1.ERRORS.TypedError('Device_FwException', firmwareException));
170
+ }
171
+ }
172
+ const unexpectedMode = device.hasUnexpectedMode(method.allowDeviceMode, method.requireDeviceMode);
173
+ if (unexpectedMode) {
174
+ device.keepSession = false;
175
+ if (isUsingPopup) {
176
+ await waitForPopup();
177
+ postMessage((0, events_2.createUiMessage)(unexpectedMode, device.toMessageObject()));
178
+ await uiPromises.create(events_2.DEVICE.DISCONNECT, device).promise;
179
+ return Promise.reject(constants_1.ERRORS.TypedError('Device_ModeException', unexpectedMode));
180
+ }
181
+ return Promise.reject(constants_1.ERRORS.TypedError('Device_ModeException', unexpectedMode));
182
+ }
183
+ method.checkPermissions();
184
+ if (!trustedHost && method.requiredPermissions.length > 0) {
185
+ await waitForPopup();
186
+ const uiPromise = uiPromises.create(events_2.UI.RECEIVE_PERMISSION, device);
187
+ postMessage((0, events_2.createUiMessage)(events_2.UI.REQUEST_PERMISSION, {
188
+ permissions: method.requiredPermissions,
189
+ device: device.toMessageObject(),
190
+ }));
191
+ const { granted, remember } = await uiPromise.promise.then(({ payload }) => payload);
192
+ if (granted) {
193
+ method.savePermissions(!remember);
194
+ }
195
+ else {
196
+ return Promise.reject(constants_1.ERRORS.TypedError('Method_PermissionsNotGranted'));
197
+ }
198
+ }
199
+ const deviceNeedsBackup = device.features.needs_backup;
200
+ if (deviceNeedsBackup) {
201
+ if (method.noBackupConfirmationMode === 'always' ||
202
+ (method.noBackupConfirmationMode === 'popup-only' && isUsingPopup)) {
203
+ await waitForPopup();
204
+ const uiPromise = uiPromises.create(events_2.UI.RECEIVE_CONFIRMATION, device);
205
+ postMessage((0, analyticsInfo_1.enhanceMessageWithAnalytics)((0, events_2.createUiMessage)(events_2.UI.REQUEST_CONFIRMATION, {
206
+ view: 'no-backup',
207
+ }), { device: device.toMessageObject() }));
208
+ const permitted = await uiPromise.promise.then(({ payload }) => payload);
209
+ if (!permitted) {
210
+ return Promise.reject(constants_1.ERRORS.TypedError('Method_PermissionsNotGranted'));
211
+ }
212
+ }
213
+ await waitForPopup();
214
+ postMessage((0, events_2.createUiMessage)(events_2.UI.DEVICE_NEEDS_BACKUP, device.toMessageObject()));
215
+ }
216
+ if (device.firmwareStatus === 'outdated') {
217
+ await waitForPopup();
218
+ postMessage((0, events_2.createUiMessage)(events_2.UI.FIRMWARE_OUTDATED, device.toMessageObject()));
219
+ }
220
+ if (!trustedHost) {
221
+ const requestConfirmation = method.confirmation;
222
+ if (requestConfirmation) {
223
+ await waitForPopup();
224
+ const uiPromise = uiPromises.create(events_2.UI.RECEIVE_CONFIRMATION, device);
225
+ postMessage((0, analyticsInfo_1.enhanceMessageWithAnalytics)((0, events_2.createUiMessage)(events_2.UI.REQUEST_CONFIRMATION, requestConfirmation), { device: device.toMessageObject() }));
226
+ const confirmed = await uiPromise.promise.then(({ payload }) => payload);
227
+ if (!confirmed) {
228
+ return Promise.reject(constants_1.ERRORS.TypedError('Method_Cancel'));
229
+ }
230
+ }
231
+ }
232
+ const isDeviceUnlocked = device.features.unlocked;
233
+ if (method.useDeviceState) {
234
+ try {
235
+ let invalidDeviceState = await getInvalidDeviceState(device, method.network, method.preauthorized);
236
+ if (isUsingPopup) {
237
+ while (invalidDeviceState) {
238
+ const uiPromise = uiPromises.create(events_2.UI.INVALID_PASSPHRASE_ACTION, device);
239
+ postMessage((0, events_2.createUiMessage)(events_2.UI.INVALID_PASSPHRASE, {
240
+ device: device.toMessageObject(),
241
+ }));
242
+ const uiResp = await uiPromise.promise;
243
+ if (uiResp.payload) {
244
+ device.setInternalState(undefined);
245
+ await device.initialize(method.useEmptyPassphrase, method.useCardanoDerivation);
246
+ invalidDeviceState = await getInvalidDeviceState(device, method.network, method.preauthorized);
247
+ }
248
+ else {
249
+ device.setExternalState(invalidDeviceState);
250
+ break;
251
+ }
252
+ }
253
+ }
254
+ else if (invalidDeviceState) {
255
+ throw constants_1.ERRORS.TypedError('Device_InvalidState');
256
+ }
257
+ }
258
+ catch (error) {
259
+ device.setInternalState(undefined);
260
+ return Promise.reject(error);
261
+ }
262
+ }
263
+ if (!isDeviceUnlocked && device.features.unlocked) {
264
+ postMessage((0, events_2.createDeviceMessage)(events_2.DEVICE.CHANGED, device.toMessageObject()));
265
+ }
266
+ if (method.useUi) {
267
+ await waitForPopup();
268
+ }
269
+ else {
270
+ postMessage((0, events_2.createPopupMessage)(events_2.POPUP.CANCEL_POPUP_REQUEST));
271
+ }
272
+ try {
273
+ const response = await method.run();
274
+ return (0, events_2.createResponseMessage)(method.responseID, true, response);
275
+ }
276
+ catch (error) {
277
+ return Promise.reject(error);
278
+ }
279
+ };
183
280
  const onCall = async (message) => {
184
281
  var _a;
185
282
  if (!message.id || !message.payload || message.type !== events_2.IFRAME.CALL) {
186
283
  throw constants_1.ERRORS.TypedError('Method_InvalidParameter', 'onCall: message.id or message.payload is missing');
187
284
  }
188
285
  const responseID = message.id;
189
- const trustedHost = DataManager_1.DataManager.getSettings('trustedHost');
190
- const isUsingPopup = DataManager_1.DataManager.getSettings('popup');
191
286
  const origin = DataManager_1.DataManager.getSettings('origin');
192
287
  const env = DataManager_1.DataManager.getSettings('env');
193
288
  const useCoreInPopup = DataManager_1.DataManager.getSettings('useCoreInPopup');
@@ -204,8 +299,7 @@ const onCall = async (message) => {
204
299
  const method = await (0, method_1.getMethod)(message);
205
300
  _log.debug('method selected', method.name);
206
301
  method.postMessage = postMessage;
207
- method.getPopupPromise = getPopupPromise;
208
- method.createUiPromise = createUiPromise;
302
+ method.createUiPromise = uiPromises.create;
209
303
  method.init();
210
304
  await ((_a = method.initAsync) === null || _a === void 0 ? void 0 : _a.call(method));
211
305
  return method;
@@ -221,7 +315,7 @@ const onCall = async (message) => {
221
315
  if (!method.useDevice) {
222
316
  try {
223
317
  if (method.useUi) {
224
- await getPopupPromise().promise;
318
+ await waitForPopup();
225
319
  }
226
320
  else {
227
321
  postMessage((0, events_2.createPopupMessage)(events_2.POPUP.CANCEL_POPUP_REQUEST));
@@ -247,11 +341,11 @@ const onCall = async (message) => {
247
341
  }
248
342
  let device;
249
343
  try {
250
- device = await initDevice(method);
344
+ device = await initDevice(method.devicePath);
251
345
  }
252
346
  catch (error) {
253
347
  if (error.code === 'Transport_Missing') {
254
- await getPopupPromise().promise;
348
+ await waitForPopup();
255
349
  postMessage((0, events_2.createUiMessage)(events_2.UI.TRANSPORT));
256
350
  }
257
351
  else {
@@ -260,7 +354,6 @@ const onCall = async (message) => {
260
354
  postMessage((0, events_2.createResponseMessage)(responseID, false, { error }));
261
355
  throw error;
262
356
  }
263
- method.postMessage = (message) => (0, analyticsInfo_1.enhancePostMessageWithAnalytics)(postMessage, message, { device: device.toMessageObject() });
264
357
  method.setDevice(device);
265
358
  const previousCall = _callMethods.filter(call => call && call !== method && call.devicePath === method.devicePath);
266
359
  if (previousCall.length > 0 && method.overridePreviousCall) {
@@ -324,112 +417,13 @@ const onCall = async (message) => {
324
417
  }
325
418
  }
326
419
  try {
327
- let PIN_TRIES = 1;
328
- const MAX_PIN_TRIES = 3;
329
- const inner = async () => {
330
- const firmwareException = await method.checkFirmwareRange(isUsingPopup);
331
- if (firmwareException) {
332
- if (isUsingPopup) {
333
- await getPopupPromise().promise;
334
- postMessage((0, events_2.createUiMessage)(firmwareException, device.toMessageObject()));
335
- await createUiPromise(events_2.DEVICE.DISCONNECT, device).promise;
336
- return Promise.reject(constants_1.ERRORS.TypedError('Method_Cancel'));
337
- }
338
- return Promise.reject(constants_1.ERRORS.TypedError('Device_FwException', firmwareException));
339
- }
340
- const unexpectedMode = device.hasUnexpectedMode(method.allowDeviceMode, method.requireDeviceMode);
341
- if (unexpectedMode) {
342
- device.keepSession = false;
343
- if (isUsingPopup) {
344
- await getPopupPromise().promise;
345
- postMessage((0, events_2.createUiMessage)(unexpectedMode, device.toMessageObject()));
346
- await createUiPromise(events_2.DEVICE.DISCONNECT, device).promise;
347
- return Promise.reject(constants_1.ERRORS.TypedError('Device_ModeException', unexpectedMode));
348
- }
349
- return Promise.reject(constants_1.ERRORS.TypedError('Device_ModeException', unexpectedMode));
350
- }
351
- method.checkPermissions();
352
- if (!trustedHost && method.requiredPermissions.length > 0) {
353
- const permitted = await method.requestPermissions();
354
- if (!permitted) {
355
- return Promise.reject(constants_1.ERRORS.TypedError('Method_PermissionsNotGranted'));
356
- }
357
- }
358
- const deviceNeedsBackup = device.features.needs_backup;
359
- if (deviceNeedsBackup && typeof method.noBackupConfirmation === 'function') {
360
- const permitted = await method.noBackupConfirmation(!isUsingPopup);
361
- if (!permitted) {
362
- return Promise.reject(constants_1.ERRORS.TypedError('Method_PermissionsNotGranted'));
363
- }
364
- }
365
- if (deviceNeedsBackup) {
366
- await getPopupPromise().promise;
367
- postMessage((0, events_2.createUiMessage)(events_2.UI.DEVICE_NEEDS_BACKUP, device.toMessageObject()));
368
- }
369
- if (device.firmwareStatus === 'outdated') {
370
- await getPopupPromise().promise;
371
- postMessage((0, events_2.createUiMessage)(events_2.UI.FIRMWARE_OUTDATED, device.toMessageObject()));
372
- }
373
- if (!trustedHost && typeof method.confirmation === 'function') {
374
- const confirmed = await method.confirmation();
375
- if (!confirmed) {
376
- return Promise.reject(constants_1.ERRORS.TypedError('Method_Cancel'));
377
- }
378
- }
379
- const isDeviceUnlocked = device.features.unlocked;
380
- try {
381
- const invalidDeviceState = method.useDeviceState
382
- ? await device.validateState(method.network, method.preauthorized)
383
- : undefined;
384
- if (invalidDeviceState) {
385
- if (isUsingPopup) {
386
- const uiPromise = createUiPromise(events_2.UI.INVALID_PASSPHRASE_ACTION, device);
387
- postMessage((0, events_2.createUiMessage)(events_2.UI.INVALID_PASSPHRASE, {
388
- device: device.toMessageObject(),
389
- }));
390
- const uiResp = await uiPromise.promise;
391
- if (uiResp.payload) {
392
- device.setInternalState(undefined);
393
- await device.initialize(method.useEmptyPassphrase, method.useCardanoDerivation);
394
- return inner();
395
- }
396
- device.setExternalState(invalidDeviceState);
397
- }
398
- else {
399
- throw constants_1.ERRORS.TypedError('Device_InvalidState');
400
- }
401
- }
402
- }
403
- catch (error) {
404
- if (error.message.includes('PIN invalid') && PIN_TRIES < MAX_PIN_TRIES) {
405
- PIN_TRIES++;
406
- postMessage((0, events_2.createUiMessage)(events_2.UI.INVALID_PIN, { device: device.toMessageObject() }));
407
- return inner();
408
- }
409
- device.setInternalState(undefined);
410
- return Promise.reject(error);
411
- }
412
- if (!isDeviceUnlocked && device.features.unlocked) {
413
- postMessage((0, events_2.createDeviceMessage)(events_2.DEVICE.CHANGED, device.toMessageObject()));
414
- }
415
- if (method.useUi) {
416
- await getPopupPromise().promise;
417
- }
418
- else {
419
- postMessage((0, events_2.createPopupMessage)(events_2.POPUP.CANCEL_POPUP_REQUEST));
420
- }
421
- try {
422
- const response = await method.run();
423
- messageResponse = (0, events_2.createResponseMessage)(method.responseID, true, response);
424
- }
425
- catch (error) {
426
- return Promise.reject(error);
427
- }
428
- };
429
420
  if (_overridePromise) {
430
421
  await _overridePromise;
431
422
  }
432
- await device.run(inner, {
423
+ const innerAction = () => inner(method, device).then(response => {
424
+ messageResponse = response;
425
+ });
426
+ await device.run(innerAction, {
433
427
  keepSession: method.keepSession,
434
428
  useEmptyPassphrase: method.useEmptyPassphrase,
435
429
  skipFinalReload: method.skipFinalReload,
@@ -477,13 +471,13 @@ const onCall = async (message) => {
477
471
  }
478
472
  };
479
473
  const cleanup = () => {
480
- _popupPromise = undefined;
481
- _uiPromises.splice(0);
474
+ popupPromise.clear();
475
+ uiPromises.clear();
482
476
  _interactionTimeout.stop();
483
477
  _log.debug('Cleanup...');
484
478
  };
485
479
  const closePopup = () => {
486
- if (_popupPromise) {
480
+ if (popupPromise.isWaiting()) {
487
481
  postMessage((0, events_2.createPopupMessage)(events_2.POPUP.CANCEL_POPUP_REQUEST));
488
482
  }
489
483
  postMessage((0, events_2.createUiMessage)(events_2.UI.CLOSE_UI_WINDOW));
@@ -491,7 +485,7 @@ const closePopup = () => {
491
485
  const onDeviceButtonHandler = async (...[device, request, method]) => {
492
486
  const addressRequest = request.code === 'ButtonRequest_Address';
493
487
  if (!addressRequest || (addressRequest && method.useUi)) {
494
- await getPopupPromise().promise;
488
+ await waitForPopup();
495
489
  }
496
490
  const data = typeof method.getButtonRequestData === 'function' && request.code
497
491
  ? method.getButtonRequestData(request.code)
@@ -508,22 +502,22 @@ const onDeviceButtonHandler = async (...[device, request, method]) => {
508
502
  }
509
503
  };
510
504
  const onDevicePinHandler = async (...[device, type, callback]) => {
511
- await getPopupPromise().promise;
512
- const uiPromise = createUiPromise(events_2.UI.RECEIVE_PIN, device);
505
+ await waitForPopup();
506
+ const uiPromise = uiPromises.create(events_2.UI.RECEIVE_PIN, device);
513
507
  postMessage((0, events_2.createUiMessage)(events_2.UI.REQUEST_PIN, { device: device.toMessageObject(), type }));
514
508
  const uiResp = await uiPromise.promise;
515
509
  callback(null, uiResp.payload);
516
510
  };
517
511
  const onDeviceWordHandler = async (...[device, type, callback]) => {
518
- await getPopupPromise().promise;
519
- const uiPromise = createUiPromise(events_2.UI.RECEIVE_WORD, device);
512
+ await waitForPopup();
513
+ const uiPromise = uiPromises.create(events_2.UI.RECEIVE_WORD, device);
520
514
  postMessage((0, events_2.createUiMessage)(events_2.UI.REQUEST_WORD, { device: device.toMessageObject(), type }));
521
515
  const uiResp = await uiPromise.promise;
522
516
  callback(null, uiResp.payload);
523
517
  };
524
518
  const onDevicePassphraseHandler = async (...[device, callback]) => {
525
- await getPopupPromise().promise;
526
- const uiPromise = createUiPromise(events_2.UI.RECEIVE_PASSPHRASE, device);
519
+ await waitForPopup();
520
+ const uiPromise = uiPromises.create(events_2.UI.RECEIVE_PASSPHRASE, device);
527
521
  postMessage((0, events_2.createUiMessage)(events_2.UI.REQUEST_PASSPHRASE, { device: device.toMessageObject() }));
528
522
  const uiResp = await uiPromise.promise;
529
523
  const { value, passphraseOnDevice, save } = uiResp.payload;
@@ -547,11 +541,8 @@ const onPopupClosed = (customErrorMessage) => {
547
541
  _overridePromise = d.interruptionFromUser(error);
548
542
  }
549
543
  else {
550
- const uiPromise = findUiPromise(events_2.DEVICE.DISCONNECT);
551
- if (uiPromise) {
552
- uiPromise.resolve({ type: events_2.DEVICE.DISCONNECT, payload: undefined });
553
- }
554
- else {
544
+ const success = uiPromises.resolve({ type: events_2.DEVICE.DISCONNECT, payload: undefined });
545
+ if (!success) {
555
546
  _callMethods.forEach(m => {
556
547
  postMessage((0, events_2.createResponseMessage)(m.responseID, false, { error }));
557
548
  });
@@ -560,33 +551,23 @@ const onPopupClosed = (customErrorMessage) => {
560
551
  }
561
552
  }
562
553
  });
563
- cleanup();
564
554
  }
565
555
  else {
566
- if (_uiPromises.length > 0) {
567
- _uiPromises.forEach(p => {
568
- p.reject(error);
569
- });
570
- _uiPromises.splice(0);
571
- }
572
- if (_popupPromise) {
573
- _popupPromise.reject(error);
574
- _popupPromise = undefined;
575
- }
576
- cleanup();
556
+ uiPromises.rejectAll(error);
557
+ popupPromise.reject(error);
577
558
  }
559
+ cleanup();
578
560
  };
579
561
  const handleDeviceSelectionChanges = (interruptDevice) => {
580
- const uiPromise = findUiPromise(events_2.UI.RECEIVE_DEVICE);
581
- if (uiPromise && _deviceList) {
562
+ const promiseExists = uiPromises.exists(events_2.UI.RECEIVE_DEVICE);
563
+ if (promiseExists && _deviceList) {
582
564
  const list = _deviceList.asArray();
583
565
  const isWebUsb = _deviceList.transportType() === 'WebUsbTransport';
584
566
  if (list.length === 1 && !isWebUsb) {
585
- uiPromise.resolve({
567
+ uiPromises.resolve({
586
568
  type: events_2.UI.RECEIVE_DEVICE,
587
569
  payload: { device: list[0] },
588
570
  });
589
- removeUiPromise(uiPromise);
590
571
  }
591
572
  else {
592
573
  postMessage((0, events_2.createUiMessage)(events_2.UI.SELECT_DEVICE, {
@@ -597,15 +578,7 @@ const handleDeviceSelectionChanges = (interruptDevice) => {
597
578
  }
598
579
  if (interruptDevice) {
599
580
  const { path } = interruptDevice;
600
- let shouldClosePopup = false;
601
- _uiPromises.forEach(p => {
602
- if (p.device && p.device.getDevicePath() === path) {
603
- if (p.id === events_2.DEVICE.DISCONNECT) {
604
- p.resolve({ type: events_2.DEVICE.DISCONNECT });
605
- }
606
- shouldClosePopup = true;
607
- }
608
- });
581
+ const shouldClosePopup = uiPromises.disconnected(path);
609
582
  if (shouldClosePopup) {
610
583
  closePopup();
611
584
  cleanup();
@@ -667,17 +640,73 @@ const initDeviceList = async (transportReconnect) => {
667
640
  }
668
641
  };
669
642
  class Core extends events_1.default {
643
+ constructor() {
644
+ super(...arguments);
645
+ this.abortController = new AbortController();
646
+ }
670
647
  handleMessage(message) {
671
- handleMessage(message);
648
+ _log.debug('handleMessage', message);
649
+ switch (message.type) {
650
+ case events_2.POPUP.HANDSHAKE:
651
+ popupPromise.resolve();
652
+ break;
653
+ case events_2.POPUP.CLOSED:
654
+ onPopupClosed(message.payload ? message.payload.error : null);
655
+ break;
656
+ case transport_1.TRANSPORT.DISABLE_WEBUSB:
657
+ disableWebUSBTransport();
658
+ break;
659
+ case transport_1.TRANSPORT.REQUEST_DEVICE:
660
+ _deviceList === null || _deviceList === void 0 ? void 0 : _deviceList.enumerate();
661
+ break;
662
+ case events_2.UI.RECEIVE_DEVICE:
663
+ case events_2.UI.RECEIVE_CONFIRMATION:
664
+ case events_2.UI.RECEIVE_PERMISSION:
665
+ case events_2.UI.RECEIVE_PIN:
666
+ case events_2.UI.RECEIVE_PASSPHRASE:
667
+ case events_2.UI.INVALID_PASSPHRASE_ACTION:
668
+ case events_2.UI.RECEIVE_ACCOUNT:
669
+ case events_2.UI.RECEIVE_FEE:
670
+ case events_2.UI.RECEIVE_WORD:
671
+ case events_2.UI.LOGIN_CHALLENGE_RESPONSE:
672
+ uiPromises.resolve(message);
673
+ break;
674
+ case events_2.IFRAME.CALL:
675
+ if (message.payload.method === 'firmwareUpdate') {
676
+ (0, onCallFirmwareUpdate_1.onCallFirmwareUpdate)({
677
+ params: message.payload,
678
+ context: {
679
+ deviceList: _deviceList,
680
+ postMessage,
681
+ initDevice,
682
+ log: _log,
683
+ abortSignal: this.abortController.signal,
684
+ },
685
+ })
686
+ .then(payload => {
687
+ postMessage((0, events_2.createResponseMessage)(message.id, true, payload));
688
+ })
689
+ .catch(error => {
690
+ postMessage((0, events_2.createResponseMessage)(message.id, false, { error }));
691
+ _log.error('onCallFirmwareUpdate', error);
692
+ });
693
+ }
694
+ else {
695
+ onCall(message).catch(error => {
696
+ _log.error('onCall', error);
697
+ });
698
+ }
699
+ }
672
700
  }
673
- async dispose() {
701
+ dispose() {
674
702
  (0, BlockchainLink_1.dispose)();
675
703
  if (_deviceListInitTimeout) {
676
704
  clearTimeout(_deviceListInitTimeout);
677
705
  }
678
706
  this.removeAllListeners();
707
+ this.abortController.abort();
679
708
  if (_deviceList) {
680
- await _deviceList.dispose();
709
+ _deviceList.dispose();
681
710
  }
682
711
  }
683
712
  async getCurrentMethod() {
@@ -745,7 +774,7 @@ const disableWebUSBTransport = async () => {
745
774
  }
746
775
  }
747
776
  try {
748
- await _deviceList.dispose();
777
+ _deviceList.dispose();
749
778
  await initDeviceList(settings.transportReconnect);
750
779
  }
751
780
  catch (error) {