@metamask-previews/bridge-status-controller 70.0.5-preview-a45abf1e1 → 70.0.5-preview-92fa59a42

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 (70) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/dist/bridge-status-controller.cjs +197 -114
  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 +201 -118
  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 +6 -5
  18. package/dist/types.d.cts.map +1 -1
  19. package/dist/types.d.mts +6 -5
  20. package/dist/types.d.mts.map +1 -1
  21. package/dist/types.mjs.map +1 -1
  22. package/dist/utils/bridge-status.cjs +42 -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 +41 -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 +43 -2
  39. package/dist/utils/history.cjs.map +1 -1
  40. package/dist/utils/history.d.cts +19 -1
  41. package/dist/utils/history.d.cts.map +1 -1
  42. package/dist/utils/history.d.mts +19 -1
  43. package/dist/utils/history.d.mts.map +1 -1
  44. package/dist/utils/history.mjs +40 -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 +9 -8
  63. package/dist/utils/transaction.cjs.map +1 -1
  64. package/dist/utils/transaction.d.cts +1 -0
  65. package/dist/utils/transaction.d.cts.map +1 -1
  66. package/dist/utils/transaction.d.mts +1 -0
  67. package/dist/utils/transaction.d.mts.map +1 -1
  68. package/dist/utils/transaction.mjs +7 -7
  69. package/dist/utils/transaction.mjs.map +1 -1
  70. 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, the status is fetched once, then the src tx hash's receipt is retrieved. If there is no receipt, the history item's hash is presumed to be invalid and the entry 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_handleOldHistoryItem, _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,55 @@ 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
- }
73
+ _BridgeStatusController_onTransactionFailed.set(this, ({ txMeta, historyKey, isApprovalTxMeta, }) => {
74
+ // Check if the history item is already marked as a failure
75
+ const isHistoryItemAlreadyFailed = historyKey
76
+ ? this.state.txHistory[historyKey]?.status.status === bridge_controller_1.StatusTypes.FAILED
77
+ : false;
78
+ __classPrivateFieldGet(this, _BridgeStatusController_updateHistoryItem, "f").call(this, {
79
+ historyKey,
80
+ status: bridge_controller_1.StatusTypes.FAILED,
81
+ txHash: isApprovalTxMeta ? undefined : txMeta.hash,
82
+ });
83
+ if (txMeta.status === transaction_controller_1.TransactionStatus.rejected) {
84
+ return;
106
85
  }
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;
86
+ // Skip account lookup and tracking when featureId is set (e.g. PERPS)
87
+ if (historyKey && this.state.txHistory[historyKey]?.featureId) {
88
+ return;
117
89
  }
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) {
90
+ // Skip tracking if this is a duplicate failed event for the same history item
91
+ // This can happen if the transaction includes an approval tx that fails
92
+ if (isHistoryItemAlreadyFailed) {
121
93
  return;
122
94
  }
123
- const key = txHistoryKey;
124
- this.update((statusState) => {
125
- statusState.txHistory[key].status.status = bridge_controller_1.StatusTypes.FAILED;
95
+ __classPrivateFieldGet(this, _BridgeStatusController_trackUnifiedSwapBridgeEvent, "f").call(this, bridge_controller_1.UnifiedSwapBridgeEventName.Failed, historyKey, (0, metrics_1.getEVMTxPropertiesFromTransactionMeta)(txMeta));
96
+ });
97
+ // Only EVM txs
98
+ _BridgeStatusController_onTransactionConfirmed.set(this, ({ txMeta, historyKey, isApprovalTxMeta, }) => {
99
+ // Return early if the confirmed txMeta is for an approval since we
100
+ // still need to wait for the trade to be confirmed
101
+ if (isApprovalTxMeta) {
102
+ return;
103
+ }
104
+ __classPrivateFieldGet(this, _BridgeStatusController_updateHistoryItem, "f").call(this, {
105
+ historyKey,
106
+ txHash: txMeta.hash,
126
107
  });
108
+ switch (txMeta.type) {
109
+ case transaction_controller_1.TransactionType.swap:
110
+ __classPrivateFieldGet(this, _BridgeStatusController_updateHistoryItem, "f").call(this, {
111
+ historyKey,
112
+ status: bridge_controller_1.StatusTypes.COMPLETE,
113
+ });
114
+ __classPrivateFieldGet(this, _BridgeStatusController_trackUnifiedSwapBridgeEvent, "f").call(this, bridge_controller_1.UnifiedSwapBridgeEventName.Completed, historyKey);
115
+ break;
116
+ default:
117
+ if (historyKey) {
118
+ __classPrivateFieldGet(this, _BridgeStatusController_startPollingForTxId, "f").call(this, historyKey);
119
+ }
120
+ break;
121
+ }
127
122
  });
128
123
  this.resetState = () => {
129
124
  this.update((state) => {
@@ -224,30 +219,31 @@ class BridgeStatusController extends (0, polling_controller_1.StaticIntervalPoll
224
219
  _BridgeStatusController_restartPollingForIncompleteHistoryItems.set(this, () => {
225
220
  // Check for historyItems that do not have a status of complete and restart polling
226
221
  const { txHistory } = this.state;
227
- const historyItems = Object.values(txHistory);
222
+ const historyItems = Object.entries(txHistory);
228
223
  const incompleteHistoryItems = historyItems
229
- .filter((historyItem) => historyItem.status.status === bridge_controller_1.StatusTypes.PENDING ||
224
+ .filter(([_, historyItem]) => historyItem.status.status === bridge_controller_1.StatusTypes.PENDING ||
230
225
  historyItem.status.status === bridge_controller_1.StatusTypes.UNKNOWN)
231
226
  // Only poll items with txMetaId (post-submission items)
232
- .filter((historyItem) => Boolean(historyItem.txMetaId))
233
- .filter((historyItem) => {
227
+ .filter(([_, historyItem]) => {
228
+ if (!historyItem.txMetaId) {
229
+ return false;
230
+ }
234
231
  // Check if we are already polling this tx, if so, skip restarting polling for that
235
232
  const pollingToken = __classPrivateFieldGet(this, _BridgeStatusController_pollingTokensByTxMetaId, "f")[historyItem.txMetaId];
236
233
  return !pollingToken;
237
234
  })
238
235
  // Only restart polling for items that still require status updates
239
- .filter((historyItem) => {
236
+ .filter(([_, historyItem]) => {
240
237
  return (0, history_1.shouldPollHistoryItem)(historyItem);
241
238
  });
242
- incompleteHistoryItems.forEach((historyItem) => {
243
- const bridgeTxMetaId = historyItem.txMetaId;
239
+ incompleteHistoryItems.forEach(([historyKey, historyItem]) => {
244
240
  const shouldSkipFetch = (0, bridge_status_1.shouldSkipFetchDueToFetchFailures)(historyItem.attempts);
245
241
  if (shouldSkipFetch) {
246
242
  return;
247
243
  }
248
244
  // We manually call startPolling() here rather than go through startPollingForBridgeTxStatus()
249
245
  // because we don't want to overwrite the existing historyItem in state
250
- __classPrivateFieldGet(this, _BridgeStatusController_startPollingForTxId, "f").call(this, bridgeTxMetaId);
246
+ __classPrivateFieldGet(this, _BridgeStatusController_startPollingForTxId, "f").call(this, historyKey);
251
247
  });
252
248
  });
253
249
  _BridgeStatusController_addTxToHistory.set(this, (...args) => {
@@ -256,6 +252,7 @@ class BridgeStatusController extends (0, polling_controller_1.StaticIntervalPoll
256
252
  // Use actionId as key for pre-submission, or txMeta.id for post-submission
257
253
  state.txHistory[historyKey] = txHistoryItem;
258
254
  });
255
+ return historyKey;
259
256
  });
260
257
  /**
261
258
  * Rekeys a history item from actionId to txMeta.id after successful submission.
@@ -278,10 +275,7 @@ class BridgeStatusController extends (0, polling_controller_1.StaticIntervalPoll
278
275
  this.stopPollingByPollingToken(existingPollingToken);
279
276
  }
280
277
  const txHistoryItem = this.state.txHistory[txId];
281
- if (!txHistoryItem) {
282
- return;
283
- }
284
- if ((0, history_1.shouldPollHistoryItem)(txHistoryItem)) {
278
+ if (txHistoryItem && (0, history_1.shouldPollHistoryItem)(txHistoryItem)) {
285
279
  __classPrivateFieldGet(this, _BridgeStatusController_pollingTokensByTxMetaId, "f")[txId] = this.startPolling({
286
280
  bridgeTxMetaId: txId,
287
281
  });
@@ -302,8 +296,8 @@ class BridgeStatusController extends (0, polling_controller_1.StaticIntervalPoll
302
296
  if (!bridgeTxMeta?.id) {
303
297
  throw new Error('Cannot start polling: bridgeTxMeta.id is required for polling');
304
298
  }
305
- __classPrivateFieldGet(this, _BridgeStatusController_addTxToHistory, "f").call(this, txHistoryMeta);
306
- __classPrivateFieldGet(this, _BridgeStatusController_startPollingForTxId, "f").call(this, bridgeTxMeta.id);
299
+ const historyKey = __classPrivateFieldGet(this, _BridgeStatusController_addTxToHistory, "f").call(this, txHistoryMeta);
300
+ __classPrivateFieldGet(this, _BridgeStatusController_startPollingForTxId, "f").call(this, historyKey);
307
301
  };
308
302
  // This will be called after you call this.startPolling()
309
303
  // The args passed in are the args you passed in to startPolling()
@@ -338,29 +332,44 @@ class BridgeStatusController extends (0, polling_controller_1.StaticIntervalPoll
338
332
  // Track max polling reached event
339
333
  const historyItem = this.state.txHistory[bridgeTxMetaId];
340
334
  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
- });
335
+ // Track polling status updated event
336
+ __classPrivateFieldGet(this, _BridgeStatusController_trackPollingStatusUpdatedEvent, "f").call(this, bridgeTxMetaId, bridge_controller_1.PollingStatus.MaxPollingReached);
357
337
  }
358
338
  }
359
339
  // Update the attempts counter
360
- this.update((state) => {
361
- state.txHistory[bridgeTxMetaId].attempts = newAttempts;
340
+ __classPrivateFieldGet(this, _BridgeStatusController_updateHistoryItem, "f").call(this, {
341
+ historyKey: bridgeTxMetaId,
342
+ attempts: newAttempts,
362
343
  });
363
344
  });
345
+ /**
346
+ * Checks if the history item should be preserved so its status can be fetched.
347
+ *
348
+ * @param bridgeTxMetaId - The txMetaId of the bridge tx
349
+ */
350
+ _BridgeStatusController_handleOldHistoryItem.set(this, async (bridgeTxMetaId) => {
351
+ if (
352
+ // Skip if the history item is finalized or not old
353
+ !(this.state.txHistory[bridgeTxMetaId] &&
354
+ (0, history_1.isHistoryItemTooOld)(this.messenger, this.state.txHistory[bridgeTxMetaId]) &&
355
+ [bridge_controller_1.StatusTypes.PENDING, bridge_controller_1.StatusTypes.UNKNOWN].includes(this.state.txHistory[bridgeTxMetaId].status.status))) {
356
+ return;
357
+ }
358
+ // Continue polling on next restart if the history item is valid
359
+ if (await (0, bridge_status_1.shouldWaitForFinalBridgeStatus)(this.messenger, this.state.txHistory[bridgeTxMetaId])) {
360
+ return;
361
+ }
362
+ const pollingToken = __classPrivateFieldGet(this, _BridgeStatusController_pollingTokensByTxMetaId, "f")[bridgeTxMetaId];
363
+ // Track polling status updated event
364
+ __classPrivateFieldGet(this, _BridgeStatusController_trackPollingStatusUpdatedEvent, "f").call(this, bridgeTxMetaId, bridge_controller_1.PollingStatus.InvalidTransactionHash);
365
+ // If we've failed too many times, stop polling for the tx
366
+ if (pollingToken) {
367
+ this.stopPollingByPollingToken(pollingToken);
368
+ delete __classPrivateFieldGet(this, _BridgeStatusController_pollingTokensByTxMetaId, "f")[bridgeTxMetaId];
369
+ }
370
+ // Delete the history item so polling doesn't start over on the next restart
371
+ __classPrivateFieldGet(this, _BridgeStatusController_deleteHistoryItem, "f").call(this, bridgeTxMetaId);
372
+ });
364
373
  _BridgeStatusController_fetchBridgeTxStatus.set(this, async ({ bridgeTxMetaId, }) => {
365
374
  // 1. Check for history item
366
375
  const { txHistory } = this.state;
@@ -372,7 +381,7 @@ class BridgeStatusController extends (0, polling_controller_1.StaticIntervalPoll
372
381
  if ((0, bridge_status_1.shouldSkipFetchDueToFetchFailures)(historyItem.attempts)) {
373
382
  return;
374
383
  }
375
- // 3. Fetch transcation status
384
+ // 3. Fetch transaction status
376
385
  try {
377
386
  let status;
378
387
  let validationFailures = [];
@@ -388,11 +397,10 @@ class BridgeStatusController extends (0, polling_controller_1.StaticIntervalPoll
388
397
  // We try here because we receive 500 errors from Bridge API if we try to fetch immediately after submitting the source tx
389
398
  // Oddly mostly happens on Optimism, never on Arbitrum. By the 2nd fetch, the Bridge API responds properly.
390
399
  // 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);
400
+ const srcTxHash = __classPrivateFieldGet(this, _BridgeStatusController_setAndGetSrcTxHash, "f").call(this, bridgeTxMetaId);
392
401
  if (!srcTxHash) {
393
402
  return;
394
403
  }
395
- __classPrivateFieldGet(this, _BridgeStatusController_updateSrcTxHash, "f").call(this, bridgeTxMetaId, srcTxHash);
396
404
  const statusRequest = (0, bridge_status_1.getStatusRequestWithSrcTxHash)(historyItem.quote, srcTxHash);
397
405
  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
406
  status = response.status;
@@ -449,26 +457,65 @@ class BridgeStatusController extends (0, polling_controller_1.StaticIntervalPoll
449
457
  console.warn('Failed to fetch bridge tx status', error);
450
458
  __classPrivateFieldGet(this, _BridgeStatusController_handleFetchFailure, "f").call(this, bridgeTxMetaId);
451
459
  }
460
+ finally {
461
+ await __classPrivateFieldGet(this, _BridgeStatusController_handleOldHistoryItem, "f").call(this, bridgeTxMetaId);
462
+ }
452
463
  });
453
- _BridgeStatusController_getSrcTxHash.set(this, (bridgeTxMetaId) => {
464
+ /**
465
+ * Returns the srcTxHash for a non-STX EVM tx, the hash from the bridge status api,
466
+ * or the local hash from the TransactionController if the tx is in a finalized state
467
+ *
468
+ * @param bridgeTxMetaId - The bridge tx meta id
469
+ * @returns The srcTxHash
470
+ */
471
+ _BridgeStatusController_setAndGetSrcTxHash.set(this, (bridgeTxMetaId) => {
454
472
  const { txHistory } = this.state;
455
- // Prefer the srcTxHash from bridgeStatusState so we don't have to l ook up in TransactionController
473
+ // Prefer the srcTxHash from bridgeStatusState so we don't have to look up in TransactionController
456
474
  // But it is possible to have bridgeHistoryItem in state without the srcTxHash yet when it is an STX
457
475
  const srcTxHash = txHistory[bridgeTxMetaId].status.srcChain.txHash;
458
- if (srcTxHash) {
476
+ if (srcTxHash ||
477
+ (0, bridge_controller_1.isNonEvmChainId)(txHistory[bridgeTxMetaId].quote.srcChainId)) {
459
478
  return srcTxHash;
460
479
  }
461
- // Look up in TransactionController if txMeta has been updated with the srcTxHash
480
+ // Update history with TransactionController's hash if it has been updated
462
481
  const txMeta = (0, transaction_1.getTransactionMetaById)(this.messenger, bridgeTxMetaId);
463
- return txMeta?.hash;
482
+ if (!txMeta) {
483
+ return undefined;
484
+ }
485
+ // Wait for finalized status before updating the history item
486
+ const localTxHash = [
487
+ transaction_controller_1.TransactionStatus.confirmed,
488
+ transaction_controller_1.TransactionStatus.dropped,
489
+ transaction_controller_1.TransactionStatus.rejected,
490
+ transaction_controller_1.TransactionStatus.failed,
491
+ ].includes(txMeta.status)
492
+ ? txMeta.hash
493
+ : undefined;
494
+ __classPrivateFieldGet(this, _BridgeStatusController_updateHistoryItem, "f").call(this, {
495
+ historyKey: bridgeTxMetaId,
496
+ txHash: localTxHash,
497
+ });
498
+ return localTxHash;
464
499
  });
465
- _BridgeStatusController_updateSrcTxHash.set(this, (bridgeTxMetaId, srcTxHash) => {
466
- const { txHistory } = this.state;
467
- if (txHistory[bridgeTxMetaId].status.srcChain.txHash) {
500
+ _BridgeStatusController_updateHistoryItem.set(this, ({ historyKey, status, txHash, attempts, }) => {
501
+ if (!historyKey) {
468
502
  return;
469
503
  }
470
- this.update((state) => {
471
- state.txHistory[bridgeTxMetaId].status.srcChain.txHash = srcTxHash;
504
+ this.update((currentState) => {
505
+ if (status) {
506
+ currentState.txHistory[historyKey].status.status = status;
507
+ }
508
+ if (txHash) {
509
+ currentState.txHistory[historyKey].status.srcChain.txHash = txHash;
510
+ }
511
+ if (attempts) {
512
+ currentState.txHistory[historyKey].attempts = attempts;
513
+ }
514
+ });
515
+ });
516
+ _BridgeStatusController_deleteHistoryItem.set(this, (historyKey) => {
517
+ this.update((currentState) => {
518
+ delete currentState.txHistory[historyKey];
472
519
  });
473
520
  });
474
521
  // Wipes the bridge status for the given address and chainId
@@ -484,6 +531,7 @@ class BridgeStatusController extends (0, polling_controller_1.StaticIntervalPoll
484
531
  const pollingToken = __classPrivateFieldGet(this, _BridgeStatusController_pollingTokensByTxMetaId, "f")[sourceTxMetaId];
485
532
  if (pollingToken) {
486
533
  this.stopPollingByPollingToken(__classPrivateFieldGet(this, _BridgeStatusController_pollingTokensByTxMetaId, "f")[sourceTxMetaId]);
534
+ delete __classPrivateFieldGet(this, _BridgeStatusController_pollingTokensByTxMetaId, "f")[sourceTxMetaId];
487
535
  }
488
536
  });
489
537
  this.update((state) => {
@@ -650,7 +698,7 @@ class BridgeStatusController extends (0, polling_controller_1.StaticIntervalPoll
650
698
  const actionId = (0, transaction_1.generateActionId)().toString();
651
699
  // Add pre-submission history keyed by actionId
652
700
  // This ensures we have quote data available if transaction fails during submission
653
- __classPrivateFieldGet(this, _BridgeStatusController_addTxToHistory, "f").call(this, {
701
+ const historyKey = __classPrivateFieldGet(this, _BridgeStatusController_addTxToHistory, "f").call(this, {
654
702
  accountAddress: selectedAccount.address,
655
703
  quoteResponse,
656
704
  slippagePercentage: 0,
@@ -677,7 +725,7 @@ class BridgeStatusController extends (0, polling_controller_1.StaticIntervalPoll
677
725
  actionId,
678
726
  });
679
727
  // On success, rekey from actionId to txMeta.id and update srcTxHash
680
- __classPrivateFieldGet(this, _BridgeStatusController_rekeyHistoryItem, "f").call(this, actionId, tradeTxMeta);
728
+ __classPrivateFieldGet(this, _BridgeStatusController_rekeyHistoryItem, "f").call(this, historyKey, tradeTxMeta);
681
729
  return tradeTxMeta;
682
730
  });
683
731
  }
@@ -697,11 +745,12 @@ class BridgeStatusController extends (0, polling_controller_1.StaticIntervalPoll
697
745
  !isStxEnabledOnClient &&
698
746
  !quoteResponse.quote.gasIncluded7702 &&
699
747
  !isDelegatedAccount;
748
+ let historyKey = txMeta.id;
700
749
  if (!isNonBatchEvm) {
701
750
  // Add swap or bridge tx to history
702
- __classPrivateFieldGet(this, _BridgeStatusController_addTxToHistory, "f").call(this, {
751
+ historyKey = __classPrivateFieldGet(this, _BridgeStatusController_addTxToHistory, "f").call(this, {
703
752
  accountAddress: selectedAccount.address,
704
- bridgeTxMeta: txMeta, // Only the id field is used by the BridgeStatusController
753
+ bridgeTxMeta: txMeta, // Only the id and hash fields are used by the BridgeStatusController
705
754
  quoteResponse,
706
755
  slippagePercentage: 0, // TODO include slippage provided by quote if using dynamic slippage, or slippage from quote request
707
756
  isStxEnabled: isStxEnabledOnClient,
@@ -714,10 +763,10 @@ class BridgeStatusController extends (0, polling_controller_1.StaticIntervalPoll
714
763
  }
715
764
  if ((0, bridge_controller_1.isNonEvmChainId)(quoteResponse.quote.srcChainId)) {
716
765
  // Start polling for bridge tx status
717
- __classPrivateFieldGet(this, _BridgeStatusController_startPollingForTxId, "f").call(this, txMeta.id);
766
+ __classPrivateFieldGet(this, _BridgeStatusController_startPollingForTxId, "f").call(this, historyKey);
718
767
  // Track non-EVM Swap completed event
719
768
  if (!(isBridgeTx || isTronTx)) {
720
- __classPrivateFieldGet(this, _BridgeStatusController_trackUnifiedSwapBridgeEvent, "f").call(this, bridge_controller_1.UnifiedSwapBridgeEventName.Completed, txMeta.id);
769
+ __classPrivateFieldGet(this, _BridgeStatusController_trackUnifiedSwapBridgeEvent, "f").call(this, bridge_controller_1.UnifiedSwapBridgeEventName.Completed, historyKey);
721
770
  }
722
771
  }
723
772
  }
@@ -816,15 +865,14 @@ class BridgeStatusController extends (0, polling_controller_1.StaticIntervalPoll
816
865
  };
817
866
  // Record in bridge history with actual transaction metadata
818
867
  try {
819
- // Use orderId as the history key for intent transactions
820
- const bridgeHistoryKey = orderUid;
821
868
  // Create a bridge transaction metadata that includes the original txId
822
869
  const bridgeTxMetaForHistory = {
823
870
  ...syntheticMeta,
824
- id: bridgeHistoryKey,
871
+ id: orderUid,
825
872
  originalTransactionId: syntheticMeta.id, // Keep original txId for TransactionController updates
826
873
  };
827
- __classPrivateFieldGet(this, _BridgeStatusController_addTxToHistory, "f").call(this, {
874
+ // Use orderId as the history key for intent transactions
875
+ const historyKey = __classPrivateFieldGet(this, _BridgeStatusController_addTxToHistory, "f").call(this, {
828
876
  accountAddress,
829
877
  bridgeTxMeta: bridgeTxMetaForHistory,
830
878
  quoteResponse,
@@ -837,7 +885,7 @@ class BridgeStatusController extends (0, polling_controller_1.StaticIntervalPoll
837
885
  activeAbTests,
838
886
  });
839
887
  // Start polling using the orderId key to route to intent manager
840
- __classPrivateFieldGet(this, _BridgeStatusController_startPollingForTxId, "f").call(this, bridgeHistoryKey);
888
+ __classPrivateFieldGet(this, _BridgeStatusController_startPollingForTxId, "f").call(this, historyKey);
841
889
  }
842
890
  catch (error) {
843
891
  console.error('📝 [submitIntent] Failed to add to bridge history', error);
@@ -853,6 +901,13 @@ class BridgeStatusController extends (0, polling_controller_1.StaticIntervalPoll
853
901
  throw error;
854
902
  }
855
903
  };
904
+ _BridgeStatusController_trackPollingStatusUpdatedEvent.set(this, (historyKey, pollingStatus) => {
905
+ // Track polling status updated event
906
+ const historyItem = this.state.txHistory[historyKey];
907
+ if (historyItem && !historyItem.featureId) {
908
+ __classPrivateFieldGet(this, _BridgeStatusController_trackUnifiedSwapBridgeEvent, "f").call(this, bridge_controller_1.UnifiedSwapBridgeEventName.PollingStatusUpdated, historyKey, (0, metrics_1.getPollingStatusUpdatedProperties)(this.messenger, pollingStatus, historyItem));
909
+ }
910
+ });
856
911
  /**
857
912
  * Tracks post-submission events for a cross-chain swap based on the history item
858
913
  *
@@ -960,15 +1015,43 @@ class BridgeStatusController extends (0, polling_controller_1.StaticIntervalPoll
960
1015
  this.messenger.registerMethodActionHandlers(this, MESSENGER_EXPOSED_METHODS);
961
1016
  // Set interval
962
1017
  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);
1018
+ this.messenger.subscribe('TransactionController:transactionStatusUpdated', ({ txMeta, historyKey, historyItem, isApprovalTxMeta }) => {
1019
+ if (!txMeta) {
1020
+ return;
968
1021
  }
969
- if (type === transaction_controller_1.TransactionType.bridge && !(0, bridge_controller_1.isNonEvmChainId)(chainId)) {
970
- __classPrivateFieldGet(this, _BridgeStatusController_startPollingForTxId, "f").call(this, txMetaId);
1022
+ const { type, status } = txMeta;
1023
+ // Allow event publishing if the txMeta is a swap/bridge OR if the
1024
+ // corresponding history item exists
1025
+ const isSwapOrBridgeTransaction = type && (0, transaction_1.isCrossChainTx)(type);
1026
+ if (!isSwapOrBridgeTransaction && !historyKey && !historyItem) {
1027
+ return;
971
1028
  }
1029
+ switch (status) {
1030
+ case transaction_controller_1.TransactionStatus.confirmed:
1031
+ __classPrivateFieldGet(this, _BridgeStatusController_onTransactionConfirmed, "f").call(this, {
1032
+ txMeta,
1033
+ historyKey,
1034
+ isApprovalTxMeta,
1035
+ });
1036
+ break;
1037
+ case transaction_controller_1.TransactionStatus.failed:
1038
+ case transaction_controller_1.TransactionStatus.dropped:
1039
+ case transaction_controller_1.TransactionStatus.rejected:
1040
+ __classPrivateFieldGet(this, _BridgeStatusController_onTransactionFailed, "f").call(this, { txMeta, historyKey, isApprovalTxMeta });
1041
+ break;
1042
+ default:
1043
+ break;
1044
+ }
1045
+ }, ({ transactionMeta }) => {
1046
+ const entry = (0, history_1.getMatchingHistoryEntryForTxMeta)(this.state.txHistory, transactionMeta);
1047
+ const approvalEntry = (0, history_1.getMatchingHistoryEntryForApprovalTxMeta)(this.state.txHistory, transactionMeta);
1048
+ const entryToUse = entry ?? approvalEntry;
1049
+ return {
1050
+ historyKey: entryToUse?.[0],
1051
+ historyItem: entryToUse?.[1],
1052
+ txMeta: transactionMeta,
1053
+ isApprovalTxMeta: entryToUse?.[1]?.approvalTxId === transactionMeta.id,
1054
+ };
972
1055
  });
973
1056
  // If you close the extension, but keep the browser open, the polling continues
974
1057
  // If you close the browser, the polling stops
@@ -977,5 +1060,5 @@ class BridgeStatusController extends (0, polling_controller_1.StaticIntervalPoll
977
1060
  }
978
1061
  }
979
1062
  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();
1063
+ _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_handleOldHistoryItem = 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
1064
  //# sourceMappingURL=bridge-status-controller.cjs.map