@metamask-previews/bridge-status-controller 70.0.5-preview-42c8fd288 → 70.0.5-preview-8e9c439

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 (66) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/dist/bridge-status-controller.cjs +197 -135
  3. package/dist/bridge-status-controller.cjs.map +1 -1
  4. package/dist/bridge-status-controller.d.cts.map +1 -1
  5. package/dist/bridge-status-controller.d.mts.map +1 -1
  6. package/dist/bridge-status-controller.mjs +200 -138
  7. package/dist/bridge-status-controller.mjs.map +1 -1
  8. package/dist/constants.cjs +2 -1
  9. package/dist/constants.cjs.map +1 -1
  10. package/dist/constants.d.cts +1 -0
  11. package/dist/constants.d.cts.map +1 -1
  12. package/dist/constants.d.mts +1 -0
  13. package/dist/constants.d.mts.map +1 -1
  14. package/dist/constants.mjs +1 -0
  15. package/dist/constants.mjs.map +1 -1
  16. package/dist/types.cjs.map +1 -1
  17. package/dist/types.d.cts +4 -3
  18. package/dist/types.d.cts.map +1 -1
  19. package/dist/types.d.mts +4 -3
  20. package/dist/types.d.mts.map +1 -1
  21. package/dist/types.mjs.map +1 -1
  22. package/dist/utils/bridge-status.cjs +39 -1
  23. package/dist/utils/bridge-status.cjs.map +1 -1
  24. package/dist/utils/bridge-status.d.cts +2 -1
  25. package/dist/utils/bridge-status.d.cts.map +1 -1
  26. package/dist/utils/bridge-status.d.mts +2 -1
  27. package/dist/utils/bridge-status.d.mts.map +1 -1
  28. package/dist/utils/bridge-status.mjs +38 -1
  29. package/dist/utils/bridge-status.mjs.map +1 -1
  30. package/dist/utils/feature-flags.cjs +12 -0
  31. package/dist/utils/feature-flags.cjs.map +1 -0
  32. package/dist/utils/feature-flags.d.cts +3 -0
  33. package/dist/utils/feature-flags.d.cts.map +1 -0
  34. package/dist/utils/feature-flags.d.mts +3 -0
  35. package/dist/utils/feature-flags.d.mts.map +1 -0
  36. package/dist/utils/feature-flags.mjs +8 -0
  37. package/dist/utils/feature-flags.mjs.map +1 -0
  38. package/dist/utils/history.cjs +49 -2
  39. package/dist/utils/history.cjs.map +1 -1
  40. package/dist/utils/history.d.cts +12 -1
  41. package/dist/utils/history.d.cts.map +1 -1
  42. package/dist/utils/history.d.mts +12 -1
  43. package/dist/utils/history.d.mts.map +1 -1
  44. package/dist/utils/history.mjs +46 -2
  45. package/dist/utils/history.mjs.map +1 -1
  46. package/dist/utils/metrics.cjs +27 -2
  47. package/dist/utils/metrics.cjs.map +1 -1
  48. package/dist/utils/metrics.d.cts +25 -2
  49. package/dist/utils/metrics.d.cts.map +1 -1
  50. package/dist/utils/metrics.d.mts +25 -2
  51. package/dist/utils/metrics.d.mts.map +1 -1
  52. package/dist/utils/metrics.mjs +25 -1
  53. package/dist/utils/metrics.mjs.map +1 -1
  54. package/dist/utils/network.cjs +7 -1
  55. package/dist/utils/network.cjs.map +1 -1
  56. package/dist/utils/network.d.cts +4 -2
  57. package/dist/utils/network.d.cts.map +1 -1
  58. package/dist/utils/network.d.mts +4 -2
  59. package/dist/utils/network.d.mts.map +1 -1
  60. package/dist/utils/network.mjs +5 -0
  61. package/dist/utils/network.mjs.map +1 -1
  62. package/dist/utils/transaction.cjs +1 -1
  63. package/dist/utils/transaction.cjs.map +1 -1
  64. package/dist/utils/transaction.mjs +1 -1
  65. package/dist/utils/transaction.mjs.map +1 -1
  66. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
9
9
 
10
10
  ### Added
11
11
 
12
+ - Remove stale bridge transactions from `txHistory` to prevent excessive polling. Once a history item exceeds the configured maximum age, polling backs off exponentially and also checks for the src tx hash's receipt. If there is no receipt, the history item's hash is presumed to be invalid and is deleted from state. ([#8479](https://github.com/MetaMask/core/pull/8479))
12
13
  - Add missing action types for public `BridgeStatusController` methods ([#8367](https://github.com/MetaMask/core/pull/8367))
13
14
  - The following types are now available:
14
15
  - `BridgeStatusControllerSubmitTxAction`
@@ -17,6 +18,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
17
18
 
18
19
  ### Changed
19
20
 
21
+ - **BREAKING:** Replace `transactionFailed` and `transactionConfirmed` event subscriptions with `TransactionController:transactionStatusUpdated` ([#8479](https://github.com/MetaMask/core/pull/8479))
22
+ - **BREAKING:** Add `RemoteFeatureFlags:getState` to allowed actions to retrieve max history item age config ([#8479](https://github.com/MetaMask/core/pull/8479))
20
23
  - Add `account_hardware_type` field to all cross-chain swap analytics events ([#8503](https://github.com/MetaMask/core/pull/8503))
21
24
  - `account_hardware_type` carries the specific hardware wallet brand (e.g. `'Ledger'`, `'QR Hardware'`) or `null` for software wallets
22
25
  - `is_hardware_wallet` is now derived from `account_hardware_type !== null`, keeping both fields in sync
@@ -28,6 +31,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
28
31
  - Bump `@metamask/base-controller` from `^9.0.1` to `^9.1.0` ([#8457](https://github.com/MetaMask/core/pull/8457))
29
32
  - Bump `@metamask/bridge-controller` from `^70.0.1` to `^70.1.1` ([#8466](https://github.com/MetaMask/core/pull/8466), [#8474](https://github.com/MetaMask/core/pull/8474))
30
33
 
34
+ ### Fixed
35
+
36
+ - Prevent invalid src hashes from being persisted in `txHistory` ([#8479](https://github.com/MetaMask/core/pull/8479))
37
+ - Make transaction status subscribers generic so that `txHistory` items get updated if there are any transaction updates matching by actionId, txMetaId, hash or type
38
+ - Skip saving smart transaction hashes on transaction submission. This used to make it possible for invalid src hashes to be stored in state and polled indefinitely. Instead, the txHistory item will now be updated with the confirmed tx hash when the `transactionStatusUpdated` event is published
39
+ - If there is no srcTxHash in state, attempt to set it based on the local TransactionController state
40
+
31
41
  ## [70.0.5]
32
42
 
33
43
  ### Changed
@@ -10,7 +10,7 @@ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (
10
10
  if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
11
11
  return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
12
12
  };
13
- var _BridgeStatusController_pollingTokensByTxMetaId, _BridgeStatusController_intentManager, _BridgeStatusController_clientId, _BridgeStatusController_fetchFn, _BridgeStatusController_config, _BridgeStatusController_addTransactionBatchFn, _BridgeStatusController_trace, _BridgeStatusController_onTransactionFailed, _BridgeStatusController_markTxAsFailed, _BridgeStatusController_restartPollingForIncompleteHistoryItems, _BridgeStatusController_addTxToHistory, _BridgeStatusController_rekeyHistoryItem, _BridgeStatusController_startPollingForTxId, _BridgeStatusController_handleFetchFailure, _BridgeStatusController_fetchBridgeTxStatus, _BridgeStatusController_getSrcTxHash, _BridgeStatusController_updateSrcTxHash, _BridgeStatusController_wipeBridgeStatusByChainId, _BridgeStatusController_handleApprovalTx, _BridgeStatusController_handleEvmTransactionBatch, _BridgeStatusController_trackUnifiedSwapBridgeEvent;
13
+ var _BridgeStatusController_pollingTokensByTxMetaId, _BridgeStatusController_intentManager, _BridgeStatusController_clientId, _BridgeStatusController_fetchFn, _BridgeStatusController_config, _BridgeStatusController_addTransactionBatchFn, _BridgeStatusController_trace, _BridgeStatusController_onTransactionFailed, _BridgeStatusController_onTransactionConfirmed, _BridgeStatusController_restartPollingForIncompleteHistoryItems, _BridgeStatusController_addTxToHistory, _BridgeStatusController_rekeyHistoryItem, _BridgeStatusController_startPollingForTxId, _BridgeStatusController_handleFetchFailure, _BridgeStatusController_fetchBridgeTxStatus, _BridgeStatusController_setAndGetSrcTxHash, _BridgeStatusController_updateHistoryItem, _BridgeStatusController_deleteHistoryItem, _BridgeStatusController_wipeBridgeStatusByChainId, _BridgeStatusController_handleApprovalTx, _BridgeStatusController_handleEvmTransactionBatch, _BridgeStatusController_trackPollingStatusUpdatedEvent, _BridgeStatusController_trackUnifiedSwapBridgeEvent;
14
14
  Object.defineProperty(exports, "__esModule", { value: true });
15
15
  exports.BridgeStatusController = void 0;
16
16
  const bridge_controller_1 = require("@metamask/bridge-controller");
@@ -70,60 +70,45 @@ class BridgeStatusController extends (0, polling_controller_1.StaticIntervalPoll
70
70
  _BridgeStatusController_config.set(this, void 0);
71
71
  _BridgeStatusController_addTransactionBatchFn.set(this, void 0);
72
72
  _BridgeStatusController_trace.set(this, void 0);
73
- _BridgeStatusController_onTransactionFailed.set(this, ({ transactionMeta, }) => {
74
- const { type, status, id: txMetaId, actionId } = transactionMeta;
75
- if (type &&
76
- [
77
- transaction_controller_1.TransactionType.bridge,
78
- transaction_controller_1.TransactionType.swap,
79
- transaction_controller_1.TransactionType.bridgeApproval,
80
- transaction_controller_1.TransactionType.swapApproval,
81
- ].includes(type) &&
82
- [
83
- transaction_controller_1.TransactionStatus.failed,
84
- transaction_controller_1.TransactionStatus.dropped,
85
- transaction_controller_1.TransactionStatus.rejected,
86
- ].includes(status)) {
87
- __classPrivateFieldGet(this, _BridgeStatusController_markTxAsFailed, "f").call(this, transactionMeta);
88
- if (status !== transaction_controller_1.TransactionStatus.rejected) {
89
- let historyKey;
90
- if (this.state.txHistory[txMetaId]) {
91
- historyKey = txMetaId;
92
- }
93
- else if (actionId && this.state.txHistory[actionId]) {
94
- historyKey = actionId;
95
- }
96
- const activeHistoryKey = historyKey ?? txMetaId;
97
- // Skip account lookup and tracking when featureId is set (e.g. PERPS)
98
- if (this.state.txHistory[activeHistoryKey]?.featureId) {
99
- return;
100
- }
101
- const from = transactionMeta.txParams?.from;
102
- __classPrivateFieldGet(this, _BridgeStatusController_trackUnifiedSwapBridgeEvent, "f").call(this, bridge_controller_1.UnifiedSwapBridgeEventName.Failed, activeHistoryKey, (0, metrics_1.getEVMTxPropertiesFromTransactionMeta)(transactionMeta, from
103
- ? this.messenger.call('AccountsController:getAccountByAddress', from)
104
- : undefined));
105
- }
106
- }
107
- });
108
- // Mark tx as failed in txHistory if either the approval or trade fails
109
- _BridgeStatusController_markTxAsFailed.set(this, ({ id: txMetaId, actionId, }) => {
110
- // Look up by txMetaId first
111
- let txHistoryKey = this.state.txHistory[txMetaId]
112
- ? txMetaId
113
- : undefined;
114
- // If not found by txMetaId, try looking up by actionId (for pre-submission failures)
115
- if (!txHistoryKey && actionId && this.state.txHistory[actionId]) {
116
- txHistoryKey = actionId;
73
+ _BridgeStatusController_onTransactionFailed.set(this, ({ txMeta, historyKey, }) => {
74
+ __classPrivateFieldGet(this, _BridgeStatusController_updateHistoryItem, "f").call(this, {
75
+ historyKey,
76
+ status: bridge_controller_1.StatusTypes.FAILED,
77
+ txHash: txMeta.type &&
78
+ [transaction_controller_1.TransactionType.bridge, transaction_controller_1.TransactionType.swap].includes(txMeta.type)
79
+ ? txMeta.hash
80
+ : undefined,
81
+ });
82
+ if (txMeta.status === transaction_controller_1.TransactionStatus.rejected) {
83
+ return;
117
84
  }
118
- // If still not found, try looking up by approvalTxId
119
- txHistoryKey ?? (txHistoryKey = Object.keys(this.state.txHistory).find((key) => this.state.txHistory[key].approvalTxId === txMetaId));
120
- if (!txHistoryKey) {
85
+ // Skip account lookup and tracking when featureId is set (e.g. PERPS)
86
+ if (historyKey && this.state.txHistory[historyKey]?.featureId) {
121
87
  return;
122
88
  }
123
- const key = txHistoryKey;
124
- this.update((statusState) => {
125
- statusState.txHistory[key].status.status = bridge_controller_1.StatusTypes.FAILED;
89
+ __classPrivateFieldGet(this, _BridgeStatusController_trackUnifiedSwapBridgeEvent, "f").call(this, bridge_controller_1.UnifiedSwapBridgeEventName.Failed, historyKey, (0, metrics_1.getEVMTxPropertiesFromTransactionMeta)(txMeta));
90
+ });
91
+ // Only EVM txs
92
+ _BridgeStatusController_onTransactionConfirmed.set(this, ({ txMeta, historyKey, }) => {
93
+ __classPrivateFieldGet(this, _BridgeStatusController_updateHistoryItem, "f").call(this, {
94
+ historyKey,
95
+ txHash: txMeta.hash,
126
96
  });
97
+ console.log('======TransactionController:transactionConfirmed', txMeta);
98
+ switch (txMeta.type) {
99
+ case transaction_controller_1.TransactionType.swap:
100
+ __classPrivateFieldGet(this, _BridgeStatusController_updateHistoryItem, "f").call(this, {
101
+ historyKey,
102
+ status: bridge_controller_1.StatusTypes.COMPLETE,
103
+ });
104
+ __classPrivateFieldGet(this, _BridgeStatusController_trackUnifiedSwapBridgeEvent, "f").call(this, bridge_controller_1.UnifiedSwapBridgeEventName.Completed, historyKey);
105
+ break;
106
+ default:
107
+ if (historyKey) {
108
+ __classPrivateFieldGet(this, _BridgeStatusController_startPollingForTxId, "f").call(this, historyKey);
109
+ }
110
+ break;
111
+ }
127
112
  });
128
113
  this.resetState = () => {
129
114
  this.update((state) => {
@@ -224,30 +209,31 @@ class BridgeStatusController extends (0, polling_controller_1.StaticIntervalPoll
224
209
  _BridgeStatusController_restartPollingForIncompleteHistoryItems.set(this, () => {
225
210
  // Check for historyItems that do not have a status of complete and restart polling
226
211
  const { txHistory } = this.state;
227
- const historyItems = Object.values(txHistory);
212
+ const historyItems = Object.entries(txHistory);
228
213
  const incompleteHistoryItems = historyItems
229
- .filter((historyItem) => historyItem.status.status === bridge_controller_1.StatusTypes.PENDING ||
214
+ .filter(([_, historyItem]) => historyItem.status.status === bridge_controller_1.StatusTypes.PENDING ||
230
215
  historyItem.status.status === bridge_controller_1.StatusTypes.UNKNOWN)
231
216
  // Only poll items with txMetaId (post-submission items)
232
- .filter((historyItem) => Boolean(historyItem.txMetaId))
233
- .filter((historyItem) => {
217
+ .filter(([_, historyItem]) => {
218
+ if (!historyItem.txMetaId) {
219
+ return false;
220
+ }
234
221
  // Check if we are already polling this tx, if so, skip restarting polling for that
235
222
  const pollingToken = __classPrivateFieldGet(this, _BridgeStatusController_pollingTokensByTxMetaId, "f")[historyItem.txMetaId];
236
223
  return !pollingToken;
237
224
  })
238
225
  // Only restart polling for items that still require status updates
239
- .filter((historyItem) => {
226
+ .filter(([_, historyItem]) => {
240
227
  return (0, history_1.shouldPollHistoryItem)(historyItem);
241
228
  });
242
- incompleteHistoryItems.forEach((historyItem) => {
243
- const bridgeTxMetaId = historyItem.txMetaId;
229
+ incompleteHistoryItems.forEach(([historyKey, historyItem]) => {
244
230
  const shouldSkipFetch = (0, bridge_status_1.shouldSkipFetchDueToFetchFailures)(historyItem.attempts);
245
231
  if (shouldSkipFetch) {
246
232
  return;
247
233
  }
248
234
  // We manually call startPolling() here rather than go through startPollingForBridgeTxStatus()
249
235
  // because we don't want to overwrite the existing historyItem in state
250
- __classPrivateFieldGet(this, _BridgeStatusController_startPollingForTxId, "f").call(this, bridgeTxMetaId);
236
+ __classPrivateFieldGet(this, _BridgeStatusController_startPollingForTxId, "f").call(this, historyKey);
251
237
  });
252
238
  });
253
239
  _BridgeStatusController_addTxToHistory.set(this, (...args) => {
@@ -256,6 +242,7 @@ class BridgeStatusController extends (0, polling_controller_1.StaticIntervalPoll
256
242
  // Use actionId as key for pre-submission, or txMeta.id for post-submission
257
243
  state.txHistory[historyKey] = txHistoryItem;
258
244
  });
245
+ return historyKey;
259
246
  });
260
247
  /**
261
248
  * Rekeys a history item from actionId to txMeta.id after successful submission.
@@ -278,9 +265,6 @@ class BridgeStatusController extends (0, polling_controller_1.StaticIntervalPoll
278
265
  this.stopPollingByPollingToken(existingPollingToken);
279
266
  }
280
267
  const txHistoryItem = this.state.txHistory[txId];
281
- if (!txHistoryItem) {
282
- return;
283
- }
284
268
  if ((0, history_1.shouldPollHistoryItem)(txHistoryItem)) {
285
269
  __classPrivateFieldGet(this, _BridgeStatusController_pollingTokensByTxMetaId, "f")[txId] = this.startPolling({
286
270
  bridgeTxMetaId: txId,
@@ -302,8 +286,8 @@ class BridgeStatusController extends (0, polling_controller_1.StaticIntervalPoll
302
286
  if (!bridgeTxMeta?.id) {
303
287
  throw new Error('Cannot start polling: bridgeTxMeta.id is required for polling');
304
288
  }
305
- __classPrivateFieldGet(this, _BridgeStatusController_addTxToHistory, "f").call(this, txHistoryMeta);
306
- __classPrivateFieldGet(this, _BridgeStatusController_startPollingForTxId, "f").call(this, bridgeTxMeta.id);
289
+ const historyKey = __classPrivateFieldGet(this, _BridgeStatusController_addTxToHistory, "f").call(this, txHistoryMeta);
290
+ __classPrivateFieldGet(this, _BridgeStatusController_startPollingForTxId, "f").call(this, historyKey);
307
291
  };
308
292
  // This will be called after you call this.startPolling()
309
293
  // The args passed in are the args you passed in to startPolling()
@@ -311,55 +295,45 @@ class BridgeStatusController extends (0, polling_controller_1.StaticIntervalPoll
311
295
  await __classPrivateFieldGet(this, _BridgeStatusController_fetchBridgeTxStatus, "f").call(this, pollingInput);
312
296
  };
313
297
  /**
314
- * Handles the failure to fetch the bridge tx status
315
- * We eventually stop polling for the tx if we fail too many times
298
+ * Handles the failure to fetch the bridge tx status or recognize the transaction hash
299
+ * We eventually stop polling for the tx if we fail too many times or if the transaction is stale
316
300
  * Failures (500 errors) can be due to:
317
301
  * - The srcTxHash not being available immediately for STX
318
302
  * - The srcTxHash being invalid for the chain. This case will never resolve so we stop polling for it to avoid hammering the Bridge API forever.
319
303
  *
320
304
  * @param bridgeTxMetaId - The txMetaId of the bridge tx
321
305
  */
322
- _BridgeStatusController_handleFetchFailure.set(this, (bridgeTxMetaId) => {
323
- const { attempts } = this.state.txHistory[bridgeTxMetaId];
324
- const newAttempts = attempts
325
- ? {
326
- counter: attempts.counter + 1,
327
- lastAttemptTime: Date.now(),
328
- }
329
- : {
330
- counter: 1,
331
- lastAttemptTime: Date.now(),
332
- };
333
- // If we've failed too many times, stop polling for the tx
306
+ _BridgeStatusController_handleFetchFailure.set(this, async (bridgeTxMetaId) => {
307
+ // Increment the polling attempts counter
308
+ const newAttempts = (0, history_1.incrementPollingAttempts)(this.state.txHistory[bridgeTxMetaId]);
309
+ __classPrivateFieldGet(this, _BridgeStatusController_updateHistoryItem, "f").call(this, {
310
+ historyKey: bridgeTxMetaId,
311
+ attempts: newAttempts,
312
+ });
313
+ // Continue polling every 10s until the max attempts is reached
314
+ if (newAttempts.counter < constants_1.MAX_ATTEMPTS) {
315
+ return;
316
+ }
334
317
  const pollingToken = __classPrivateFieldGet(this, _BridgeStatusController_pollingTokensByTxMetaId, "f")[bridgeTxMetaId];
335
- if (newAttempts.counter >= constants_1.MAX_ATTEMPTS && pollingToken) {
318
+ // After the attempts exceed the max, keep polling with exponential backoff
319
+ // If the historyItem age is less than the configured maxPendingHistoryItemAgeMs flag, wait for a valid srcTxHash
320
+ if (await (0, bridge_status_1.shouldWaitForFinalBridgeStatus)(this.messenger, this.state.txHistory[bridgeTxMetaId])) {
321
+ return;
322
+ }
323
+ // If we've failed too many times and the srcTxHash is invalid, stop polling for the tx
324
+ if (pollingToken) {
336
325
  this.stopPollingByPollingToken(pollingToken);
337
326
  delete __classPrivateFieldGet(this, _BridgeStatusController_pollingTokensByTxMetaId, "f")[bridgeTxMetaId];
338
- // Track max polling reached event
339
- const historyItem = this.state.txHistory[bridgeTxMetaId];
340
- if (historyItem && !historyItem.featureId) {
341
- const selectedAccount = (0, accounts_1.getAccountByAddress)(this.messenger, historyItem.account);
342
- const requestParams = (0, metrics_1.getRequestParamFromHistory)(historyItem);
343
- const requestMetadata = (0, metrics_1.getRequestMetadataFromHistory)(historyItem, selectedAccount);
344
- const { security_warnings: _, ...metadataWithoutWarnings } = requestMetadata;
345
- __classPrivateFieldGet(this, _BridgeStatusController_trackUnifiedSwapBridgeEvent, "f").call(this, bridge_controller_1.UnifiedSwapBridgeEventName.PollingStatusUpdated, bridgeTxMetaId, {
346
- ...(0, metrics_1.getTradeDataFromHistory)(historyItem),
347
- ...(0, metrics_1.getPriceImpactFromQuote)(historyItem.quote),
348
- ...metadataWithoutWarnings,
349
- chain_id_source: requestParams.chain_id_source,
350
- chain_id_destination: requestParams.chain_id_destination,
351
- token_symbol_source: requestParams.token_symbol_source,
352
- token_symbol_destination: requestParams.token_symbol_destination,
353
- action_type: bridge_controller_1.MetricsActionType.SWAPBRIDGE_V1,
354
- polling_status: bridge_controller_1.PollingStatus.MaxPollingReached,
355
- retry_attempts: newAttempts.counter,
356
- });
357
- }
358
327
  }
359
- // Update the attempts counter
360
- this.update((state) => {
361
- state.txHistory[bridgeTxMetaId].attempts = newAttempts;
362
- });
328
+ // Track polling status updated event
329
+ if (newAttempts.counter === constants_1.MAX_ATTEMPTS) {
330
+ __classPrivateFieldGet(this, _BridgeStatusController_trackPollingStatusUpdatedEvent, "f").call(this, bridgeTxMetaId, bridge_controller_1.PollingStatus.MaxPollingReached);
331
+ }
332
+ else {
333
+ __classPrivateFieldGet(this, _BridgeStatusController_trackPollingStatusUpdatedEvent, "f").call(this, bridgeTxMetaId, bridge_controller_1.PollingStatus.StaleTransactionHash);
334
+ // Delete the history item so polling doesn't start over on the next restart
335
+ __classPrivateFieldGet(this, _BridgeStatusController_deleteHistoryItem, "f").call(this, bridgeTxMetaId);
336
+ }
363
337
  });
364
338
  _BridgeStatusController_fetchBridgeTxStatus.set(this, async ({ bridgeTxMetaId, }) => {
365
339
  // 1. Check for history item
@@ -388,11 +362,14 @@ class BridgeStatusController extends (0, polling_controller_1.StaticIntervalPoll
388
362
  // We try here because we receive 500 errors from Bridge API if we try to fetch immediately after submitting the source tx
389
363
  // Oddly mostly happens on Optimism, never on Arbitrum. By the 2nd fetch, the Bridge API responds properly.
390
364
  // Also srcTxHash may not be available immediately for STX, so we don't want to fetch in those cases
391
- const srcTxHash = __classPrivateFieldGet(this, _BridgeStatusController_getSrcTxHash, "f").call(this, bridgeTxMetaId);
365
+ const srcTxHash = __classPrivateFieldGet(this, _BridgeStatusController_setAndGetSrcTxHash, "f").call(this, bridgeTxMetaId);
366
+ // Throw errors to increase polling attempts counter
367
+ if ((0, history_1.isHistoryItemTooOld)(this.messenger, historyItem)) {
368
+ throw new Error(`History item is too old: ${bridgeTxMetaId}}`);
369
+ }
392
370
  if (!srcTxHash) {
393
371
  return;
394
372
  }
395
- __classPrivateFieldGet(this, _BridgeStatusController_updateSrcTxHash, "f").call(this, bridgeTxMetaId, srcTxHash);
396
373
  const statusRequest = (0, bridge_status_1.getStatusRequestWithSrcTxHash)(historyItem.quote, srcTxHash);
397
374
  const response = await (0, bridge_status_1.fetchBridgeTxStatus)(statusRequest, __classPrivateFieldGet(this, _BridgeStatusController_clientId, "f"), await (0, authentication_1.getJwt)(this.messenger), __classPrivateFieldGet(this, _BridgeStatusController_fetchFn, "f"), __classPrivateFieldGet(this, _BridgeStatusController_config, "f").customBridgeApiBaseUrl);
398
375
  status = response.status;
@@ -447,28 +424,65 @@ class BridgeStatusController extends (0, polling_controller_1.StaticIntervalPoll
447
424
  }
448
425
  catch (error) {
449
426
  console.warn('Failed to fetch bridge tx status', error);
450
- __classPrivateFieldGet(this, _BridgeStatusController_handleFetchFailure, "f").call(this, bridgeTxMetaId);
427
+ await __classPrivateFieldGet(this, _BridgeStatusController_handleFetchFailure, "f").call(this, bridgeTxMetaId);
451
428
  }
452
429
  });
453
- _BridgeStatusController_getSrcTxHash.set(this, (bridgeTxMetaId) => {
430
+ /**
431
+ * Returns the srcTxHash for a non-STX EVM tx, the hash from the bridge status api,
432
+ * or the local hash from the TransactionController if the tx is in a finalized state
433
+ *
434
+ * @param bridgeTxMetaId - The bridge tx meta id
435
+ * @returns The srcTxHash
436
+ */
437
+ _BridgeStatusController_setAndGetSrcTxHash.set(this, (bridgeTxMetaId) => {
454
438
  const { txHistory } = this.state;
455
- // Prefer the srcTxHash from bridgeStatusState so we don't have to l ook up in TransactionController
439
+ // Prefer the srcTxHash from bridgeStatusState so we don't have to look up in TransactionController
456
440
  // But it is possible to have bridgeHistoryItem in state without the srcTxHash yet when it is an STX
457
441
  const srcTxHash = txHistory[bridgeTxMetaId].status.srcChain.txHash;
458
- if (srcTxHash) {
442
+ if (srcTxHash ||
443
+ (0, bridge_controller_1.isNonEvmChainId)(txHistory[bridgeTxMetaId].quote.srcChainId)) {
444
+ // throw new Error(`Src tx hash not found for non-EVM chain: ${srcTxHash}`);
459
445
  return srcTxHash;
460
446
  }
461
- // Look up in TransactionController if txMeta has been updated with the srcTxHash
447
+ // Update history with TransactionController's hash if it has been updated
462
448
  const txMeta = (0, transaction_1.getTransactionMetaById)(this.messenger, bridgeTxMetaId);
463
- return txMeta?.hash;
449
+ if (!txMeta) {
450
+ return undefined;
451
+ }
452
+ // Wait for finalized status before updating the history item
453
+ const localTxHash = [
454
+ transaction_controller_1.TransactionStatus.confirmed,
455
+ transaction_controller_1.TransactionStatus.dropped,
456
+ transaction_controller_1.TransactionStatus.rejected,
457
+ transaction_controller_1.TransactionStatus.failed,
458
+ ].includes(txMeta.status)
459
+ ? txMeta.hash
460
+ : undefined;
461
+ __classPrivateFieldGet(this, _BridgeStatusController_updateHistoryItem, "f").call(this, {
462
+ historyKey: bridgeTxMetaId,
463
+ txHash: localTxHash,
464
+ });
465
+ return localTxHash;
464
466
  });
465
- _BridgeStatusController_updateSrcTxHash.set(this, (bridgeTxMetaId, srcTxHash) => {
466
- const { txHistory } = this.state;
467
- if (txHistory[bridgeTxMetaId].status.srcChain.txHash) {
467
+ _BridgeStatusController_updateHistoryItem.set(this, ({ historyKey, status, txHash, attempts, }) => {
468
+ if (!historyKey) {
468
469
  return;
469
470
  }
470
- this.update((state) => {
471
- state.txHistory[bridgeTxMetaId].status.srcChain.txHash = srcTxHash;
471
+ this.update((currentState) => {
472
+ if (status) {
473
+ currentState.txHistory[historyKey].status.status = status;
474
+ }
475
+ if (txHash) {
476
+ currentState.txHistory[historyKey].status.srcChain.txHash = txHash;
477
+ }
478
+ if (attempts) {
479
+ currentState.txHistory[historyKey].attempts = attempts;
480
+ }
481
+ });
482
+ });
483
+ _BridgeStatusController_deleteHistoryItem.set(this, (historyKey) => {
484
+ this.update((currentState) => {
485
+ delete currentState.txHistory[historyKey];
472
486
  });
473
487
  });
474
488
  // Wipes the bridge status for the given address and chainId
@@ -484,6 +498,7 @@ class BridgeStatusController extends (0, polling_controller_1.StaticIntervalPoll
484
498
  const pollingToken = __classPrivateFieldGet(this, _BridgeStatusController_pollingTokensByTxMetaId, "f")[sourceTxMetaId];
485
499
  if (pollingToken) {
486
500
  this.stopPollingByPollingToken(__classPrivateFieldGet(this, _BridgeStatusController_pollingTokensByTxMetaId, "f")[sourceTxMetaId]);
501
+ delete __classPrivateFieldGet(this, _BridgeStatusController_pollingTokensByTxMetaId, "f")[sourceTxMetaId];
487
502
  }
488
503
  });
489
504
  this.update((state) => {
@@ -650,7 +665,7 @@ class BridgeStatusController extends (0, polling_controller_1.StaticIntervalPoll
650
665
  const actionId = (0, transaction_1.generateActionId)().toString();
651
666
  // Add pre-submission history keyed by actionId
652
667
  // This ensures we have quote data available if transaction fails during submission
653
- __classPrivateFieldGet(this, _BridgeStatusController_addTxToHistory, "f").call(this, {
668
+ const historyKey = __classPrivateFieldGet(this, _BridgeStatusController_addTxToHistory, "f").call(this, {
654
669
  accountAddress: selectedAccount.address,
655
670
  quoteResponse,
656
671
  slippagePercentage: 0,
@@ -677,7 +692,7 @@ class BridgeStatusController extends (0, polling_controller_1.StaticIntervalPoll
677
692
  actionId,
678
693
  });
679
694
  // On success, rekey from actionId to txMeta.id and update srcTxHash
680
- __classPrivateFieldGet(this, _BridgeStatusController_rekeyHistoryItem, "f").call(this, actionId, tradeTxMeta);
695
+ __classPrivateFieldGet(this, _BridgeStatusController_rekeyHistoryItem, "f").call(this, historyKey, tradeTxMeta);
681
696
  return tradeTxMeta;
682
697
  });
683
698
  }
@@ -697,11 +712,12 @@ class BridgeStatusController extends (0, polling_controller_1.StaticIntervalPoll
697
712
  !isStxEnabledOnClient &&
698
713
  !quoteResponse.quote.gasIncluded7702 &&
699
714
  !isDelegatedAccount;
715
+ let historyKey = txMeta.id;
700
716
  if (!isNonBatchEvm) {
701
717
  // Add swap or bridge tx to history
702
- __classPrivateFieldGet(this, _BridgeStatusController_addTxToHistory, "f").call(this, {
718
+ historyKey = __classPrivateFieldGet(this, _BridgeStatusController_addTxToHistory, "f").call(this, {
703
719
  accountAddress: selectedAccount.address,
704
- bridgeTxMeta: txMeta, // Only the id field is used by the BridgeStatusController
720
+ bridgeTxMeta: txMeta, // Only the id and hash fields are used by the BridgeStatusController
705
721
  quoteResponse,
706
722
  slippagePercentage: 0, // TODO include slippage provided by quote if using dynamic slippage, or slippage from quote request
707
723
  isStxEnabled: isStxEnabledOnClient,
@@ -714,10 +730,10 @@ class BridgeStatusController extends (0, polling_controller_1.StaticIntervalPoll
714
730
  }
715
731
  if ((0, bridge_controller_1.isNonEvmChainId)(quoteResponse.quote.srcChainId)) {
716
732
  // Start polling for bridge tx status
717
- __classPrivateFieldGet(this, _BridgeStatusController_startPollingForTxId, "f").call(this, txMeta.id);
733
+ __classPrivateFieldGet(this, _BridgeStatusController_startPollingForTxId, "f").call(this, historyKey);
718
734
  // Track non-EVM Swap completed event
719
735
  if (!(isBridgeTx || isTronTx)) {
720
- __classPrivateFieldGet(this, _BridgeStatusController_trackUnifiedSwapBridgeEvent, "f").call(this, bridge_controller_1.UnifiedSwapBridgeEventName.Completed, txMeta.id);
736
+ __classPrivateFieldGet(this, _BridgeStatusController_trackUnifiedSwapBridgeEvent, "f").call(this, bridge_controller_1.UnifiedSwapBridgeEventName.Completed, historyKey);
721
737
  }
722
738
  }
723
739
  }
@@ -816,15 +832,14 @@ class BridgeStatusController extends (0, polling_controller_1.StaticIntervalPoll
816
832
  };
817
833
  // Record in bridge history with actual transaction metadata
818
834
  try {
819
- // Use orderId as the history key for intent transactions
820
- const bridgeHistoryKey = orderUid;
821
835
  // Create a bridge transaction metadata that includes the original txId
822
836
  const bridgeTxMetaForHistory = {
823
837
  ...syntheticMeta,
824
- id: bridgeHistoryKey,
838
+ id: orderUid,
825
839
  originalTransactionId: syntheticMeta.id, // Keep original txId for TransactionController updates
826
840
  };
827
- __classPrivateFieldGet(this, _BridgeStatusController_addTxToHistory, "f").call(this, {
841
+ // Use orderId as the history key for intent transactions
842
+ const historyKey = __classPrivateFieldGet(this, _BridgeStatusController_addTxToHistory, "f").call(this, {
828
843
  accountAddress,
829
844
  bridgeTxMeta: bridgeTxMetaForHistory,
830
845
  quoteResponse,
@@ -837,7 +852,7 @@ class BridgeStatusController extends (0, polling_controller_1.StaticIntervalPoll
837
852
  activeAbTests,
838
853
  });
839
854
  // Start polling using the orderId key to route to intent manager
840
- __classPrivateFieldGet(this, _BridgeStatusController_startPollingForTxId, "f").call(this, bridgeHistoryKey);
855
+ __classPrivateFieldGet(this, _BridgeStatusController_startPollingForTxId, "f").call(this, historyKey);
841
856
  }
842
857
  catch (error) {
843
858
  console.error('📝 [submitIntent] Failed to add to bridge history', error);
@@ -853,6 +868,13 @@ class BridgeStatusController extends (0, polling_controller_1.StaticIntervalPoll
853
868
  throw error;
854
869
  }
855
870
  };
871
+ _BridgeStatusController_trackPollingStatusUpdatedEvent.set(this, (historyKey, pollingStatus) => {
872
+ // Track polling status updated event
873
+ const historyItem = this.state.txHistory[historyKey];
874
+ if (historyItem && !historyItem.featureId) {
875
+ __classPrivateFieldGet(this, _BridgeStatusController_trackUnifiedSwapBridgeEvent, "f").call(this, bridge_controller_1.UnifiedSwapBridgeEventName.PollingStatusUpdated, historyKey, (0, metrics_1.getPollingStatusUpdatedProperties)(this.messenger, pollingStatus, historyItem));
876
+ }
877
+ });
856
878
  /**
857
879
  * Tracks post-submission events for a cross-chain swap based on the history item
858
880
  *
@@ -960,15 +982,55 @@ class BridgeStatusController extends (0, polling_controller_1.StaticIntervalPoll
960
982
  this.messenger.registerMethodActionHandlers(this, MESSENGER_EXPOSED_METHODS);
961
983
  // Set interval
962
984
  this.setIntervalLength(constants_1.REFRESH_INTERVAL_MS);
963
- this.messenger.subscribe('TransactionController:transactionFailed', __classPrivateFieldGet(this, _BridgeStatusController_onTransactionFailed, "f"));
964
- this.messenger.subscribe('TransactionController:transactionConfirmed', (transactionMeta) => {
965
- const { type, id: txMetaId, chainId } = transactionMeta;
966
- if (type === transaction_controller_1.TransactionType.swap) {
967
- __classPrivateFieldGet(this, _BridgeStatusController_trackUnifiedSwapBridgeEvent, "f").call(this, bridge_controller_1.UnifiedSwapBridgeEventName.Completed, txMetaId);
985
+ this.messenger.subscribe('TransactionController:transactionStatusUpdated', ({ txMeta, historyKey, historyItem }) => {
986
+ if (!txMeta) {
987
+ console.error('======TransactionController:transactionStatusUpdated NOT FOUND');
988
+ return;
989
+ }
990
+ const { type, hash, status, id, actionId, batchId } = txMeta;
991
+ console.error('======TransactionController:transactionStatusUpdated', {
992
+ type,
993
+ hash,
994
+ status,
995
+ id,
996
+ actionId,
997
+ batchId,
998
+ });
999
+ // Allow event publishing if the txMeta is a swap/bridge OR if the
1000
+ // corresponding history item exists
1001
+ const isSwapOrBridgeTransaction = type &&
1002
+ [
1003
+ transaction_controller_1.TransactionType.swap,
1004
+ transaction_controller_1.TransactionType.bridge,
1005
+ transaction_controller_1.TransactionType.swapApproval,
1006
+ transaction_controller_1.TransactionType.bridgeApproval,
1007
+ ].includes(type);
1008
+ const isApprovalConfirmation = txMeta.id === historyItem?.approvalTxId &&
1009
+ status === transaction_controller_1.TransactionStatus.confirmed;
1010
+ if ((!isSwapOrBridgeTransaction && !historyKey && !historyItem) ||
1011
+ isApprovalConfirmation) {
1012
+ console.error('======approval tx confirmed');
1013
+ return;
968
1014
  }
969
- if (type === transaction_controller_1.TransactionType.bridge && !(0, bridge_controller_1.isNonEvmChainId)(chainId)) {
970
- __classPrivateFieldGet(this, _BridgeStatusController_startPollingForTxId, "f").call(this, txMetaId);
1015
+ switch (status) {
1016
+ case transaction_controller_1.TransactionStatus.confirmed:
1017
+ __classPrivateFieldGet(this, _BridgeStatusController_onTransactionConfirmed, "f").call(this, { txMeta, historyKey });
1018
+ break;
1019
+ case transaction_controller_1.TransactionStatus.failed:
1020
+ case transaction_controller_1.TransactionStatus.dropped:
1021
+ case transaction_controller_1.TransactionStatus.rejected:
1022
+ __classPrivateFieldGet(this, _BridgeStatusController_onTransactionFailed, "f").call(this, { txMeta, historyKey });
1023
+ break;
1024
+ default:
1025
+ break;
971
1026
  }
1027
+ }, ({ transactionMeta }) => {
1028
+ const entry = (0, history_1.getMatchingHistoryEntryForTxMeta)(this.state.txHistory, transactionMeta);
1029
+ return {
1030
+ historyKey: entry?.[0],
1031
+ historyItem: entry?.[1],
1032
+ txMeta: transactionMeta,
1033
+ };
972
1034
  });
973
1035
  // If you close the extension, but keep the browser open, the polling continues
974
1036
  // If you close the browser, the polling stops
@@ -977,5 +1039,5 @@ class BridgeStatusController extends (0, polling_controller_1.StaticIntervalPoll
977
1039
  }
978
1040
  }
979
1041
  exports.BridgeStatusController = BridgeStatusController;
980
- _BridgeStatusController_pollingTokensByTxMetaId = new WeakMap(), _BridgeStatusController_intentManager = new WeakMap(), _BridgeStatusController_clientId = new WeakMap(), _BridgeStatusController_fetchFn = new WeakMap(), _BridgeStatusController_config = new WeakMap(), _BridgeStatusController_addTransactionBatchFn = new WeakMap(), _BridgeStatusController_trace = new WeakMap(), _BridgeStatusController_onTransactionFailed = new WeakMap(), _BridgeStatusController_markTxAsFailed = new WeakMap(), _BridgeStatusController_restartPollingForIncompleteHistoryItems = new WeakMap(), _BridgeStatusController_addTxToHistory = new WeakMap(), _BridgeStatusController_rekeyHistoryItem = new WeakMap(), _BridgeStatusController_startPollingForTxId = new WeakMap(), _BridgeStatusController_handleFetchFailure = new WeakMap(), _BridgeStatusController_fetchBridgeTxStatus = new WeakMap(), _BridgeStatusController_getSrcTxHash = new WeakMap(), _BridgeStatusController_updateSrcTxHash = new WeakMap(), _BridgeStatusController_wipeBridgeStatusByChainId = new WeakMap(), _BridgeStatusController_handleApprovalTx = new WeakMap(), _BridgeStatusController_handleEvmTransactionBatch = new WeakMap(), _BridgeStatusController_trackUnifiedSwapBridgeEvent = new WeakMap();
1042
+ _BridgeStatusController_pollingTokensByTxMetaId = new WeakMap(), _BridgeStatusController_intentManager = new WeakMap(), _BridgeStatusController_clientId = new WeakMap(), _BridgeStatusController_fetchFn = new WeakMap(), _BridgeStatusController_config = new WeakMap(), _BridgeStatusController_addTransactionBatchFn = new WeakMap(), _BridgeStatusController_trace = new WeakMap(), _BridgeStatusController_onTransactionFailed = new WeakMap(), _BridgeStatusController_onTransactionConfirmed = new WeakMap(), _BridgeStatusController_restartPollingForIncompleteHistoryItems = new WeakMap(), _BridgeStatusController_addTxToHistory = new WeakMap(), _BridgeStatusController_rekeyHistoryItem = new WeakMap(), _BridgeStatusController_startPollingForTxId = new WeakMap(), _BridgeStatusController_handleFetchFailure = new WeakMap(), _BridgeStatusController_fetchBridgeTxStatus = new WeakMap(), _BridgeStatusController_setAndGetSrcTxHash = new WeakMap(), _BridgeStatusController_updateHistoryItem = new WeakMap(), _BridgeStatusController_deleteHistoryItem = new WeakMap(), _BridgeStatusController_wipeBridgeStatusByChainId = new WeakMap(), _BridgeStatusController_handleApprovalTx = new WeakMap(), _BridgeStatusController_handleEvmTransactionBatch = new WeakMap(), _BridgeStatusController_trackPollingStatusUpdatedEvent = new WeakMap(), _BridgeStatusController_trackUnifiedSwapBridgeEvent = new WeakMap();
981
1043
  //# sourceMappingURL=bridge-status-controller.cjs.map