From 69eb4f71cc8d2c95994ad9c4becc60b76e31f882 Mon Sep 17 00:00:00 2001 From: IF <139582705+infiniteflower@users.noreply.github.com> Date: Wed, 21 Jan 2026 14:51:14 -0500 Subject: [PATCH 01/14] fix: use txMeta.actionId as a temporary key to capture tx where it fails before submission --- .../bridge-status-controller.test.ts.snap | 18 ++ .../src/bridge-status-controller.test.ts | 12 ++ .../src/bridge-status-controller.ts | 180 +++++++++++++++--- .../bridge-status-controller/src/types.ts | 3 +- 4 files changed, 181 insertions(+), 32 deletions(-) diff --git a/packages/bridge-status-controller/src/__snapshots__/bridge-status-controller.test.ts.snap b/packages/bridge-status-controller/src/__snapshots__/bridge-status-controller.test.ts.snap index a3a47e29fa4..6fb064736cb 100644 --- a/packages/bridge-status-controller/src/__snapshots__/bridge-status-controller.test.ts.snap +++ b/packages/bridge-status-controller/src/__snapshots__/bridge-status-controller.test.ts.snap @@ -4,6 +4,7 @@ exports[`BridgeStatusController constructor rehydrates the tx history state 1`] Object { "bridgeTxMetaId1": Object { "account": "0xaccount1", + "actionId": undefined, "approvalTxId": undefined, "attempts": undefined, "batchId": undefined, @@ -208,6 +209,7 @@ exports[`BridgeStatusController startPollingForBridgeTxStatus sets the inital tx Object { "bridgeTxMetaId1": Object { "account": "0xaccount1", + "actionId": undefined, "approvalTxId": undefined, "batchId": undefined, "estimatedProcessingTimeInSeconds": 15, @@ -410,6 +412,7 @@ Object { exports[`BridgeStatusController submitTx: EVM bridge should call handleMobileHardwareWalletDelay for hardware wallet on mobile 2`] = ` Object { "account": "0xaccount1", + "actionId": "1234567890.456", "approvalTxId": "test-approval-tx-id", "batchId": undefined, "estimatedProcessingTimeInSeconds": 15, @@ -646,6 +649,7 @@ Object { exports[`BridgeStatusController submitTx: EVM bridge should delay after submitting base approval 2`] = ` Object { "account": "0xaccount1", + "actionId": "1234567890.456", "approvalTxId": "test-approval-tx-id", "batchId": undefined, "estimatedProcessingTimeInSeconds": 15, @@ -882,6 +886,7 @@ Object { exports[`BridgeStatusController submitTx: EVM bridge should delay after submitting linea approval 2`] = ` Object { "account": "0xaccount1", + "actionId": "1234567890.456", "approvalTxId": "test-approval-tx-id", "batchId": undefined, "estimatedProcessingTimeInSeconds": 15, @@ -1119,6 +1124,7 @@ Object { exports[`BridgeStatusController submitTx: EVM bridge should handle smart transactions and include quotesReceivedContext 2`] = ` Object { "account": "0xaccount1", + "actionId": undefined, "approvalTxId": undefined, "batchId": "batchId1", "estimatedProcessingTimeInSeconds": 15, @@ -1382,6 +1388,7 @@ Object { exports[`BridgeStatusController submitTx: EVM bridge should not call handleMobileHardwareWalletDelay on extension 2`] = ` Object { "account": "0xaccount1", + "actionId": "1234567890.456", "approvalTxId": "test-approval-tx-id", "batchId": undefined, "estimatedProcessingTimeInSeconds": 15, @@ -1618,6 +1625,7 @@ Object { exports[`BridgeStatusController submitTx: EVM bridge should not call handleMobileHardwareWalletDelay with true for non-hardware wallet on mobile 2`] = ` Object { "account": "0xaccount1", + "actionId": "1234567890.456", "approvalTxId": "test-approval-tx-id", "batchId": undefined, "estimatedProcessingTimeInSeconds": 15, @@ -1854,6 +1862,7 @@ Object { exports[`BridgeStatusController submitTx: EVM bridge should reset USDT allowance 2`] = ` Object { "account": "0xaccount1", + "actionId": "1234567890.456", "approvalTxId": "test-approval-tx-id", "batchId": undefined, "estimatedProcessingTimeInSeconds": 15, @@ -2194,6 +2203,7 @@ Object { exports[`BridgeStatusController submitTx: EVM bridge should successfully submit an EVM bridge transaction with approval 2`] = ` Object { "account": "0xaccount1", + "actionId": "1234567890.456", "approvalTxId": "test-approval-tx-id", "batchId": undefined, "estimatedProcessingTimeInSeconds": 15, @@ -2450,6 +2460,7 @@ Object { exports[`BridgeStatusController submitTx: EVM bridge should successfully submit an EVM bridge transaction with no approval 2`] = ` Object { "account": "0xaccount1", + "actionId": "1234567890.456", "approvalTxId": undefined, "batchId": undefined, "estimatedProcessingTimeInSeconds": 15, @@ -2857,6 +2868,7 @@ Object { exports[`BridgeStatusController submitTx: EVM swap should handle smart transactions 2`] = ` Object { "account": "0xaccount1", + "actionId": undefined, "approvalTxId": undefined, "batchId": "batchId1", "estimatedProcessingTimeInSeconds": 0, @@ -3210,6 +3222,7 @@ Object { exports[`BridgeStatusController submitTx: EVM swap should successfully submit an EVM swap transaction with no approval 2`] = ` Object { "account": "0xaccount1", + "actionId": "1234567890.456", "approvalTxId": undefined, "batchId": undefined, "estimatedProcessingTimeInSeconds": 0, @@ -3423,6 +3436,7 @@ Object { exports[`BridgeStatusController submitTx: EVM swap should use quote txFee when gasIncluded is true and STX is off (Max native token swap) 2`] = ` Object { "account": "0xaccount1", + "actionId": "1234567890.456", "approvalTxId": undefined, "batchId": undefined, "estimatedProcessingTimeInSeconds": 0, @@ -3722,6 +3736,7 @@ Object { exports[`BridgeStatusController submitTx: Solana bridge should successfully submit a transaction 4`] = ` Object { "account": "0x123...", + "actionId": undefined, "approvalTxId": undefined, "batchId": undefined, "estimatedProcessingTimeInSeconds": 300, @@ -4057,6 +4072,7 @@ Object { exports[`BridgeStatusController submitTx: Solana swap should successfully submit a transaction 3`] = ` Object { "account": "0x123...", + "actionId": undefined, "approvalTxId": undefined, "batchId": undefined, "estimatedProcessingTimeInSeconds": 300, @@ -4433,6 +4449,7 @@ Object { exports[`BridgeStatusController submitTx: Tron swap with approval should successfully submit a Tron bridge with approval transaction 4`] = ` Object { "account": "TRX123...", + "actionId": undefined, "approvalTxId": "approval-signature", "batchId": undefined, "estimatedProcessingTimeInSeconds": 30, @@ -4652,6 +4669,7 @@ Object { exports[`BridgeStatusController submitTx: Tron swap with approval should successfully submit a Tron swap with approval transaction 3`] = ` Object { "account": "TRX123...", + "actionId": undefined, "approvalTxId": "approval-signature", "batchId": undefined, "estimatedProcessingTimeInSeconds": 30, diff --git a/packages/bridge-status-controller/src/bridge-status-controller.test.ts b/packages/bridge-status-controller/src/bridge-status-controller.test.ts index d9695b5dfe9..a37110cc43c 100644 --- a/packages/bridge-status-controller/src/bridge-status-controller.test.ts +++ b/packages/bridge-status-controller/src/bridge-status-controller.test.ts @@ -342,12 +342,14 @@ const getMockStartPollingForBridgeTxStatusArgs = ({ const MockTxHistory = { getInitNoSrcTxHash: ({ txMetaId = 'bridgeTxMetaId1', + actionId = undefined, account = '0xaccount1', srcChainId = 42161, destChainId = 10, } = {}): Record => ({ [txMetaId]: { txMetaId, + actionId, originalTransactionId: txMetaId, quote: getMockQuote({ srcChainId, destChainId }), startTime: 1729964825189, @@ -366,12 +368,14 @@ const MockTxHistory = { }), getInit: ({ txMetaId = 'bridgeTxMetaId1', + actionId = undefined, account = '0xaccount1', srcChainId = 42161, destChainId = 10, } = {}): Record => ({ [txMetaId]: { txMetaId, + actionId, originalTransactionId: txMetaId, quote: getMockQuote({ srcChainId, destChainId }), startTime: 1729964825189, @@ -390,6 +394,7 @@ const MockTxHistory = { getPending: ({ txMetaId = 'bridgeTxMetaId1', batchId = undefined, + actionId = undefined, approvalTxId = undefined, srcTxHash = '0xsrcTxHash1', account = '0xaccount1', @@ -399,6 +404,7 @@ const MockTxHistory = { } = {}): Record => ({ [txMetaId]: { txMetaId, + actionId, originalTransactionId: txMetaId, batchId, quote: getMockQuote({ srcChainId, destChainId }), @@ -429,6 +435,7 @@ const MockTxHistory = { }), getUnknown: ({ txMetaId = 'bridgeTxMetaId2', + actionId = undefined, srcTxHash = '0xsrcTxHash2', account = '0xaccount1', srcChainId = 42161, @@ -436,6 +443,7 @@ const MockTxHistory = { } = {}): Record => ({ [txMetaId]: { txMetaId, + actionId, originalTransactionId: txMetaId, quote: getMockQuote({ srcChainId, destChainId }), startTime: 1729964825189, @@ -464,6 +472,7 @@ const MockTxHistory = { }), getPendingSwap: ({ txMetaId = 'swapTxMetaId1', + actionId = undefined, srcTxHash = '0xsrcTxHash1', account = '0xaccount1', srcChainId = 42161, @@ -472,6 +481,7 @@ const MockTxHistory = { } = {}): Record => ({ [txMetaId]: { txMetaId, + actionId, originalTransactionId: txMetaId, quote: getMockQuote({ srcChainId, destChainId }), startTime: 1729964825189, @@ -499,6 +509,7 @@ const MockTxHistory = { }), getComplete: ({ txMetaId = 'bridgeTxMetaId1', + actionId = undefined, batchId = undefined, srcTxHash = '0xsrcTxHash1', account = '0xaccount1', @@ -507,6 +518,7 @@ const MockTxHistory = { } = {}): Record => ({ [txMetaId]: { txMetaId, + actionId, originalTransactionId: txMetaId, batchId, featureId: undefined, diff --git a/packages/bridge-status-controller/src/bridge-status-controller.ts b/packages/bridge-status-controller/src/bridge-status-controller.ts index 555dde64118..5f5a561fe7b 100644 --- a/packages/bridge-status-controller/src/bridge-status-controller.ts +++ b/packages/bridge-status-controller/src/bridge-status-controller.ts @@ -213,7 +213,7 @@ export class BridgeStatusController extends StaticIntervalPollingController { - const { type, status, id } = transactionMeta; + const { type, status, id, actionId } = transactionMeta; if ( type && @@ -233,9 +233,17 @@ export class BridgeStatusController extends StaticIntervalPollingController { - const txHistoryKey = this.state.txHistory[id] + readonly #markTxAsFailed = ({ id, actionId }: TransactionMeta): void => { + // Look up by txMeta.id first + let txHistoryKey: string | undefined = this.state.txHistory[id] ? id - : Object.keys(this.state.txHistory).find( - (key) => this.state.txHistory[key].approvalTxId === id, - ); + : undefined; + + // If not found by id, try looking up by actionId (for pre-submission failures) + if (!txHistoryKey && actionId && this.state.txHistory[actionId]) { + txHistoryKey = actionId; + } + + // If still not found, try looking up by approvalTxId + txHistoryKey ??= Object.keys(this.state.txHistory).find( + (key) => this.state.txHistory[key].approvalTxId === id, + ); + if (!txHistoryKey) { return; } + const key = txHistoryKey; this.update((statusState) => { - statusState.txHistory[txHistoryKey].status.status = StatusTypes.FAILED; + statusState.txHistory[key].status.status = StatusTypes.FAILED; }); }; @@ -406,6 +425,13 @@ export class BridgeStatusController extends StaticIntervalPollingController + Boolean(historyItem.txMetaId), + ) .filter((historyItem) => { // Check if we are already polling this tx, if so, skip restarting polling for that const pollingToken = @@ -438,6 +464,7 @@ export class BridgeStatusController extends StaticIntervalPollingController { const { bridgeTxMeta, @@ -452,15 +479,25 @@ export class BridgeStatusController extends StaticIntervalPollingController { - // Use the txMeta.id as the key so we can reference the txMeta in TransactionController - state.txHistory[bridgeTxMeta.id] = txHistoryItem; + // Use actionId as key for pre-submission, or txMeta.id for post-submission + state.txHistory[historyKey] = txHistoryItem; + }); + }; + + /** + * Rekeys a history item from actionId to txMeta.id after successful submission. + * Also updates txMetaId and srcTxHash which weren't available pre-submission. + * + * @param actionId - The actionId used as the temporary key for the history item + * @param txMeta - The transaction meta from the successful submission + * @param txMeta.id - The transaction meta id to use as the new key + * @param txMeta.hash - The transaction hash to set on the history item + */ + readonly #rekeyHistoryItem = ( + actionId: string, + txMeta: { id: string; hash?: string }, + ): void => { + const historyItem = this.state.txHistory[actionId]; + if (!historyItem) { + return; + } + + this.update((state) => { + // Update fields that weren't available pre-submission + const updatedItem: BridgeHistoryItem = { + ...historyItem, + txMetaId: txMeta.id, + originalTransactionId: historyItem.originalTransactionId ?? txMeta.id, + status: { + ...historyItem.status, + srcChain: { + ...historyItem.status.srcChain, + txHash: txMeta.hash ?? historyItem.status.srcChain?.txHash, + }, + }, + }; + + // Add under new key (txMeta.id) + state.txHistory[txMeta.id] = updatedItem; + // Remove old key (actionId) + delete state.txHistory[actionId]; }); }; @@ -1157,6 +1234,7 @@ export class BridgeStatusController extends StaticIntervalPollingController => { - const actionId = generateActionId().toString(); + // Use provided actionId (for pre-submission history) or generate one + const actionId = providedActionId ?? generateActionId().toString(); const selectedAccount = this.messenger.call( 'AccountsController:getAccountByAddress', @@ -1534,9 +1615,31 @@ export class BridgeStatusController extends StaticIntervalPollingController; export type RefuelStatusResponse = object & StatusResponse; export type BridgeHistoryItem = { - txMetaId: string; // Need this to handle STX that might not have a txHash immediately + txMetaId?: string; // Optional: not available pre-submission or on sync failure + actionId?: string; // Only for non-batch EVM transactions originalTransactionId?: string; // Keep original transaction ID for intent transactions batchId?: string; quote: Quote; From 1b714388c53c679bf8003ace12c29bd564eaf085 Mon Sep 17 00:00:00 2001 From: IF <139582705+infiniteflower@users.noreply.github.com> Date: Wed, 21 Jan 2026 15:19:36 -0500 Subject: [PATCH 02/14] chore: temp pass in bridgeTxMeta --- .../bridge-status-controller/src/bridge-status-controller.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/bridge-status-controller/src/bridge-status-controller.ts b/packages/bridge-status-controller/src/bridge-status-controller.ts index 5f5a561fe7b..c53288d0a89 100644 --- a/packages/bridge-status-controller/src/bridge-status-controller.ts +++ b/packages/bridge-status-controller/src/bridge-status-controller.ts @@ -1623,7 +1623,7 @@ export class BridgeStatusController extends StaticIntervalPollingController Date: Thu, 22 Jan 2026 13:32:42 -0500 Subject: [PATCH 03/14] docs: update changelog for actionId pre-submission tracking fix --- packages/bridge-status-controller/CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/bridge-status-controller/CHANGELOG.md b/packages/bridge-status-controller/CHANGELOG.md index 11f4fec7421..559bff784c8 100644 --- a/packages/bridge-status-controller/CHANGELOG.md +++ b/packages/bridge-status-controller/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Fixed + +- Fix transaction failure tracking for pre-submission failures by using `actionId` as a temporary history key ([#7696](https://github.com/MetaMask/core/pull/7696)) + ## [64.4.4] ### Changed From 0067f08c7ee89ac880c8508f439255744904bb03 Mon Sep 17 00:00:00 2001 From: IF <139582705+infiniteflower@users.noreply.github.com> Date: Thu, 22 Jan 2026 13:36:25 -0500 Subject: [PATCH 04/14] test: add tests for actionId tracking and rekeying behavior --- .../bridge-status-controller.test.ts.snap | 40 +++ .../src/bridge-status-controller.test.ts | 230 ++++++++++++++++++ 2 files changed, 270 insertions(+) diff --git a/packages/bridge-status-controller/src/__snapshots__/bridge-status-controller.test.ts.snap b/packages/bridge-status-controller/src/__snapshots__/bridge-status-controller.test.ts.snap index 6fb064736cb..f8325db8bb3 100644 --- a/packages/bridge-status-controller/src/__snapshots__/bridge-status-controller.test.ts.snap +++ b/packages/bridge-status-controller/src/__snapshots__/bridge-status-controller.test.ts.snap @@ -4919,6 +4919,46 @@ Array [ ] `; +exports[`BridgeStatusController subscription handlers TransactionController:transactionFailed should find history by actionId when txMeta.id not in history (pre-submission failure) 1`] = ` +Array [ + "BridgeController:trackUnifiedSwapBridgeEvent", + "Unified SwapBridge Failed", + Object { + "action_type": "swapbridge-v1", + "actual_time_minutes": 0, + "allowance_reset_transaction": undefined, + "approval_transaction": undefined, + "chain_id_destination": "eip155:10", + "chain_id_source": "eip155:42161", + "custom_slippage": true, + "destination_transaction": "FAILED", + "error_message": "", + "gas_included": false, + "gas_included_7702": false, + "is_hardware_wallet": false, + "price_impact": 0, + "provider": "lifi_across", + "quote_vs_execution_ratio": 0, + "quoted_time_minutes": 0.25, + "quoted_vs_used_gas_ratio": 0, + "security_warnings": Array [], + "slippage_limit": 0, + "source_transaction": "COMPLETE", + "stx_enabled": false, + "swap_type": "crosschain", + "token_address_destination": "eip155:10/slip44:60", + "token_address_source": "eip155:42161/slip44:60", + "token_symbol_destination": "ETH", + "token_symbol_source": "ETH", + "usd_actual_gas": 0, + "usd_actual_return": 0, + "usd_amount_source": 0, + "usd_quoted_gas": 2.5778, + "usd_quoted_return": 0, + }, +] +`; + exports[`BridgeStatusController subscription handlers TransactionController:transactionFailed should not track failed event for approved status 1`] = `Array []`; exports[`BridgeStatusController subscription handlers TransactionController:transactionFailed should not track failed event for other transaction types 1`] = `Array []`; diff --git a/packages/bridge-status-controller/src/bridge-status-controller.test.ts b/packages/bridge-status-controller/src/bridge-status-controller.test.ts index a37110cc43c..f6101081e80 100644 --- a/packages/bridge-status-controller/src/bridge-status-controller.test.ts +++ b/packages/bridge-status-controller/src/bridge-status-controller.test.ts @@ -3051,6 +3051,131 @@ describe('BridgeStatusController', () => { expect(mockMessengerCall.mock.calls).toMatchSnapshot(); expect(mockTraceFn.mock.calls).toMatchSnapshot(); }); + + describe('actionId tracking and rekeying', () => { + it('should add pre-submission history keyed by actionId and rekey to txMeta.id after success', async () => { + // Mock generateActionId to return a predictable value + const mockActionId = '1234567890.456'; + jest + .spyOn(transactionUtils, 'generateActionId') + .mockReturnValue(mockActionId); + + setupEventTrackingMocks(mockMessengerCall); + // No approval for this test - direct to bridge tx + const { approval, ...quoteWithoutApproval } = mockEvmQuoteResponse; + setupBridgeMocks(mockMessengerCall); + + const { controller, startPollingForBridgeTxStatusSpy } = + getController(mockMessengerCall); + + const result = await controller.submitTx( + (quoteWithoutApproval.trade as TxData).from, + quoteWithoutApproval, + false, // STX disabled - uses non-batch path + ); + controller.stopAllPolling(); + + // Verify the final history is keyed by txMeta.id (not actionId) + expect(controller.state.txHistory[result.id]).toBeDefined(); + expect(controller.state.txHistory[result.id].txMetaId).toBe(result.id); + expect(controller.state.txHistory[result.id].actionId).toBe( + mockActionId, + ); + + // Verify the actionId key no longer exists (was rekeyed) + expect(controller.state.txHistory[mockActionId]).toBeUndefined(); + + // Verify srcTxHash was updated during rekey + expect( + controller.state.txHistory[result.id].status.srcChain.txHash, + ).toBe(result.hash); + + expect(startPollingForBridgeTxStatusSpy).toHaveBeenCalledTimes(0); + }); + + it('should preserve pre-submission history for tracking when trade tx submission fails', async () => { + const mockActionId = '9876543210.789'; + jest + .spyOn(transactionUtils, 'generateActionId') + .mockReturnValue(mockActionId); + + setupEventTrackingMocks(mockMessengerCall); + const { approval, ...quoteWithoutApproval } = mockEvmQuoteResponse; + + // Setup for trade tx (no approval) + mockMessengerCall.mockReturnValueOnce(mockSelectedAccount); + mockMessengerCall.mockReturnValueOnce('arbitrum-client-id'); + mockMessengerCall.mockReturnValueOnce({ + gasFeeEstimates: { estimatedBaseFee: '0x1234' }, + }); + estimateGasFeeFn.mockResolvedValueOnce({ + estimates: { + high: { + suggestedMaxFeePerGas: '0x1234', + suggestedMaxPriorityFeePerGas: '0x5678', + }, + }, + }); + + // Trade tx fails during submission + addTransactionFn.mockRejectedValueOnce( + new Error('Trade tx submission failed'), + ); + + const { controller, startPollingForBridgeTxStatusSpy } = + getController(mockMessengerCall); + + await expect( + controller.submitTx( + (quoteWithoutApproval.trade as TxData).from, + quoteWithoutApproval, + false, + ), + ).rejects.toThrow('Trade tx submission failed'); + + // Verify: Pre-submission history should still exist keyed by actionId + // This allows failed event tracking to find the quote data + expect(controller.state.txHistory[mockActionId]).toBeDefined(); + expect(controller.state.txHistory[mockActionId].actionId).toBe( + mockActionId, + ); + expect( + controller.state.txHistory[mockActionId].txMetaId, + ).toBeUndefined(); + expect( + controller.state.txHistory[mockActionId].status.srcChain.txHash, + ).toBe(''); // Empty since tx was never submitted + + expect(startPollingForBridgeTxStatusSpy).toHaveBeenCalledTimes(0); + }); + + it('should use provided actionId from addTransactionFn result', async () => { + const mockActionId = '1111111111.222'; + jest + .spyOn(transactionUtils, 'generateActionId') + .mockReturnValue(mockActionId); + + setupEventTrackingMocks(mockMessengerCall); + setupApprovalMocks(mockMessengerCall); + setupBridgeMocks(mockMessengerCall); + + const { controller, startPollingForBridgeTxStatusSpy } = + getController(mockMessengerCall); + + const result = await controller.submitTx( + (mockEvmQuoteResponse.trade as TxData).from, + mockEvmQuoteResponse, + false, // STX disabled + ); + controller.stopAllPolling(); + + // Verify actionId is stored in the history item + expect(controller.state.txHistory[result.id].actionId).toBe( + mockActionId, + ); + expect(startPollingForBridgeTxStatusSpy).toHaveBeenCalledTimes(0); + }); + }); }); describe('submitTx: EVM swap', () => { @@ -3969,6 +4094,10 @@ describe('BridgeStatusController', () => { mockFetchFn = jest .fn() .mockResolvedValueOnce(MockStatusResponse.getPending()); + + // Create base history item for actionId-keyed entries + const baseHistoryItem = MockTxHistory.getPending().bridgeTxMetaId1; + bridgeStatusController = new BridgeStatusController({ messenger: mockBridgeStatusMessenger, clientId: BridgeClientId.EXTENSION, @@ -3994,6 +4123,22 @@ describe('BridgeStatusController', () => { srcTxHash: '0xperpsSrcTxHash1', featureId: FeatureId.PERPS as never, }), + // ActionId-keyed entries for pre-submission failure tests + 'pre-submission-action-id': { + ...baseHistoryItem, + actionId: 'pre-submission-action-id', + txMetaId: undefined, + } as BridgeHistoryItem, + 'action-id-for-tracking': { + ...baseHistoryItem, + actionId: 'action-id-for-tracking', + txMetaId: undefined, + } as BridgeHistoryItem, + 'action-id-for-rejection': { + ...baseHistoryItem, + actionId: 'action-id-for-rejection', + txMetaId: undefined, + } as BridgeHistoryItem, }, }, }); @@ -4188,6 +4333,91 @@ describe('BridgeStatusController', () => { expect(messengerCallSpy.mock.calls).toMatchSnapshot(); }); + + it('should find history by actionId when txMeta.id not in history (pre-submission failure)', () => { + // The history entry keyed by actionId is set up in beforeEach + const actionId = 'pre-submission-action-id'; + const unknownTxMetaId = 'unknown-tx-meta-id'; + + const messengerCallSpy = jest.spyOn(mockBridgeStatusMessenger, 'call'); + + // Publish failure with an unknown txMeta.id but with matching actionId + mockMessenger.publish('TransactionController:transactionFailed', { + error: 'tx-error', + transactionMeta: { + chainId: CHAIN_IDS.ARBITRUM, + networkClientId: 'eth-id', + time: Date.now(), + txParams: {} as unknown as TransactionParams, + type: TransactionType.bridge, + status: TransactionStatus.failed, + id: unknownTxMetaId, + actionId, // ActionId matches the history entry + }, + }); + + // Verify: History entry keyed by actionId should be marked as failed + expect( + bridgeStatusController.state.txHistory[actionId].status.status, + ).toBe(StatusTypes.FAILED); + expect(messengerCallSpy.mock.lastCall).toMatchSnapshot(); + }); + + it('should track failed event using actionId lookup when id not found', () => { + // The history entry keyed by actionId is set up in beforeEach + const actionId = 'action-id-for-tracking'; + + const messengerCallSpy = jest.spyOn(mockBridgeStatusMessenger, 'call'); + + mockMessenger.publish('TransactionController:transactionFailed', { + error: 'tx-error', + transactionMeta: { + chainId: CHAIN_IDS.ARBITRUM, + networkClientId: 'eth-id', + time: Date.now(), + txParams: {} as unknown as TransactionParams, + type: TransactionType.bridge, + status: TransactionStatus.failed, + id: 'non-existent-tx-id', + actionId, + }, + }); + + // The Failed event should be tracked with the history data from actionId lookup + expect(messengerCallSpy).toHaveBeenCalled(); + expect( + bridgeStatusController.state.txHistory[actionId].status.status, + ).toBe(StatusTypes.FAILED); + }); + + it('should not track failed event when transaction is rejected', () => { + // The history entry keyed by actionId is set up in beforeEach + const actionId = 'action-id-for-rejection'; + + const messengerCallSpy = jest.spyOn(mockBridgeStatusMessenger, 'call'); + + mockMessenger.publish('TransactionController:transactionFailed', { + error: 'User rejected', + transactionMeta: { + chainId: CHAIN_IDS.ARBITRUM, + networkClientId: 'eth-id', + time: Date.now(), + txParams: {} as unknown as TransactionParams, + type: TransactionType.bridge, + status: TransactionStatus.rejected, + id: 'rejected-tx-id', + actionId, + }, + }); + + // Status should still be marked as failed + expect( + bridgeStatusController.state.txHistory[actionId].status.status, + ).toBe(StatusTypes.FAILED); + // But Failed event should NOT be tracked for rejected status + // (check that call was not made for tracking - only for marking failed) + expect(messengerCallSpy).not.toHaveBeenCalled(); + }); }); describe('TransactionController:transactionConfirmed', () => { From 98d7d6631db4d4d08323ab921a570c74e1366c88 Mon Sep 17 00:00:00 2001 From: IF <139582705+infiniteflower@users.noreply.github.com> Date: Thu, 22 Jan 2026 14:00:44 -0500 Subject: [PATCH 05/14] test: add test for addTxToHistory error when bridgeTxMeta.id is missing --- .../src/bridge-status-controller.test.ts | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/packages/bridge-status-controller/src/bridge-status-controller.test.ts b/packages/bridge-status-controller/src/bridge-status-controller.test.ts index f6101081e80..bae30b44c59 100644 --- a/packages/bridge-status-controller/src/bridge-status-controller.test.ts +++ b/packages/bridge-status-controller/src/bridge-status-controller.test.ts @@ -918,6 +918,30 @@ describe('BridgeStatusController', () => { jest.clearAllMocks(); }); + it('throws error when bridgeTxMeta.id is not provided', () => { + const bridgeStatusController = new BridgeStatusController({ + messenger: getMessengerMock(), + clientId: BridgeClientId.EXTENSION, + fetchFn: jest.fn(), + addTransactionFn: jest.fn(), + addTransactionBatchFn: jest.fn(), + updateTransactionFn: jest.fn(), + estimateGasFeeFn: jest.fn(), + }); + + const argsWithoutId = getMockStartPollingForBridgeTxStatusArgs(); + // Remove the id from bridgeTxMeta + argsWithoutId.bridgeTxMeta = {} as never; + + expect(() => { + bridgeStatusController.startPollingForBridgeTxStatus(argsWithoutId); + }).toThrow( + 'Cannot add tx to history: either actionId or bridgeTxMeta.id must be provided', + ); + + bridgeStatusController.stopAllPolling(); + }); + it('sets the inital tx history state', async () => { // Setup const bridgeStatusController = new BridgeStatusController({ From ef54173070f23ac0e0454465a59c490c0f21c717 Mon Sep 17 00:00:00 2001 From: IF <139582705+infiniteflower@users.noreply.github.com> Date: Thu, 22 Jan 2026 14:18:00 -0500 Subject: [PATCH 06/14] refactor: make bridgeTxMeta optional and add polling validation - Make bridgeTxMeta optional in StartPollingForBridgeTxStatusArgs type - Add explicit validation in startPollingForBridgeTxStatus requiring bridgeTxMeta.id - Add test for undefined bridgeTxMeta case --- .../src/bridge-status-controller.test.ts | 26 ++++++++++++++++--- .../src/bridge-status-controller.ts | 6 +++++ .../bridge-status-controller/src/types.ts | 2 +- 3 files changed, 30 insertions(+), 4 deletions(-) diff --git a/packages/bridge-status-controller/src/bridge-status-controller.test.ts b/packages/bridge-status-controller/src/bridge-status-controller.test.ts index bae30b44c59..f692fff933f 100644 --- a/packages/bridge-status-controller/src/bridge-status-controller.test.ts +++ b/packages/bridge-status-controller/src/bridge-status-controller.test.ts @@ -935,9 +935,29 @@ describe('BridgeStatusController', () => { expect(() => { bridgeStatusController.startPollingForBridgeTxStatus(argsWithoutId); - }).toThrow( - 'Cannot add tx to history: either actionId or bridgeTxMeta.id must be provided', - ); + }).toThrow('Cannot start polling: bridgeTxMeta.id is required for polling'); + + bridgeStatusController.stopAllPolling(); + }); + + it('throws error when bridgeTxMeta is undefined', () => { + const bridgeStatusController = new BridgeStatusController({ + messenger: getMessengerMock(), + clientId: BridgeClientId.EXTENSION, + fetchFn: jest.fn(), + addTransactionFn: jest.fn(), + addTransactionBatchFn: jest.fn(), + updateTransactionFn: jest.fn(), + estimateGasFeeFn: jest.fn(), + }); + + const argsWithoutMeta = getMockStartPollingForBridgeTxStatusArgs(); + // Remove bridgeTxMeta entirely + argsWithoutMeta.bridgeTxMeta = undefined as never; + + expect(() => { + bridgeStatusController.startPollingForBridgeTxStatus(argsWithoutMeta); + }).toThrow('Cannot start polling: bridgeTxMeta.id is required for polling'); bridgeStatusController.stopAllPolling(); }); diff --git a/packages/bridge-status-controller/src/bridge-status-controller.ts b/packages/bridge-status-controller/src/bridge-status-controller.ts index c53288d0a89..c4c7a56c9f6 100644 --- a/packages/bridge-status-controller/src/bridge-status-controller.ts +++ b/packages/bridge-status-controller/src/bridge-status-controller.ts @@ -609,6 +609,12 @@ export class BridgeStatusController extends StaticIntervalPollingController { const { bridgeTxMeta } = txHistoryMeta; + if (!bridgeTxMeta?.id) { + throw new Error( + 'Cannot start polling: bridgeTxMeta.id is required for polling', + ); + } + this.#addTxToHistory(txHistoryMeta); this.#startPollingForTxId(bridgeTxMeta.id); }; diff --git a/packages/bridge-status-controller/src/types.ts b/packages/bridge-status-controller/src/types.ts index 8f2318c9634..d6c66215d77 100644 --- a/packages/bridge-status-controller/src/types.ts +++ b/packages/bridge-status-controller/src/types.ts @@ -196,7 +196,7 @@ export type QuoteMetadataSerialized = { }; export type StartPollingForBridgeTxStatusArgs = { - bridgeTxMeta: TransactionMeta; + bridgeTxMeta?: TransactionMeta; statusRequest: StatusRequest; quoteResponse: QuoteResponse & QuoteMetadata; startTime?: BridgeHistoryItem['startTime']; From febb86a2bcbe7fa2c1bbbf60a9614c75ef3f9fdc Mon Sep 17 00:00:00 2001 From: IF <139582705+infiniteflower@users.noreply.github.com> Date: Thu, 22 Jan 2026 14:46:48 -0500 Subject: [PATCH 07/14] refactor: extract getHistoryKey as pure function for testability Extract history key determination logic into a pure function to improve test coverage. The function validates that either actionId or bridgeTxMetaId is provided, throwing if neither is available. --- .../src/bridge-status-controller.ts | 8 ++----- .../src/utils/transaction.test.ts | 21 ++++++++++++++++++ .../src/utils/transaction.ts | 22 +++++++++++++++++++ 3 files changed, 45 insertions(+), 6 deletions(-) diff --git a/packages/bridge-status-controller/src/bridge-status-controller.ts b/packages/bridge-status-controller/src/bridge-status-controller.ts index c4c7a56c9f6..9d9de1d02de 100644 --- a/packages/bridge-status-controller/src/bridge-status-controller.ts +++ b/packages/bridge-status-controller/src/bridge-status-controller.ts @@ -78,6 +78,7 @@ import { findAndUpdateTransactionsInBatch, getAddTransactionBatchParams, getClientRequest, + getHistoryKey, getStatusRequestParams, handleApprovalDelay, handleMobileHardwareWalletDelay, @@ -482,12 +483,7 @@ export class BridgeStatusController extends StaticIntervalPollingController { expect(mockUpdateTransactionFn).not.toHaveBeenCalled(); }); }); + + describe('getHistoryKey', () => { + it('returns actionId when both actionId and bridgeTxMetaId are provided', () => { + expect(getHistoryKey('action-123', 'tx-456')).toBe('action-123'); + }); + + it('returns bridgeTxMetaId when only bridgeTxMetaId is provided', () => { + expect(getHistoryKey(undefined, 'tx-456')).toBe('tx-456'); + }); + + it('returns actionId when only actionId is provided', () => { + expect(getHistoryKey('action-123', undefined)).toBe('action-123'); + }); + + it('throws error when neither actionId nor bridgeTxMetaId is provided', () => { + expect(() => getHistoryKey(undefined, undefined)).toThrow( + 'Cannot add tx to history: either actionId or bridgeTxMeta.id must be provided', + ); + }); + }); }); diff --git a/packages/bridge-status-controller/src/utils/transaction.ts b/packages/bridge-status-controller/src/utils/transaction.ts index 63248853447..8a7a01f4417 100644 --- a/packages/bridge-status-controller/src/utils/transaction.ts +++ b/packages/bridge-status-controller/src/utils/transaction.ts @@ -453,3 +453,25 @@ export const findAndUpdateTransactionsInBatch = ({ return txBatch; }; + +/** + * Determines the key to use for storing a bridge history item. + * Uses actionId for pre-submission tracking, or bridgeTxMetaId for post-submission. + * + * @param actionId - The action ID used for pre-submission tracking + * @param bridgeTxMetaId - The transaction meta ID from bridgeTxMeta + * @returns The key to use for the history item + * @throws Error if neither actionId nor bridgeTxMetaId is provided + */ +export function getHistoryKey( + actionId: string | undefined, + bridgeTxMetaId: string | undefined, +): string { + const historyKey = actionId ?? bridgeTxMetaId; + if (!historyKey) { + throw new Error( + 'Cannot add tx to history: either actionId or bridgeTxMeta.id must be provided', + ); + } + return historyKey; +} From 64c1ddf18c60403ceb8c60c2fd04562d0d2b174a Mon Sep 17 00:00:00 2001 From: IF <139582705+infiniteflower@users.noreply.github.com> Date: Thu, 22 Jan 2026 14:49:03 -0500 Subject: [PATCH 08/14] refactor: extract getIntentFromQuote as pure function for testability Extract intent validation logic into a pure function to improve test coverage. The function validates that intent data exists in the quote response, throwing if missing. --- .../src/bridge-status-controller.ts | 10 ++--- .../src/utils/transaction.test.ts | 43 +++++++++++++++++++ .../src/utils/transaction.ts | 18 ++++++++ 3 files changed, 65 insertions(+), 6 deletions(-) diff --git a/packages/bridge-status-controller/src/bridge-status-controller.ts b/packages/bridge-status-controller/src/bridge-status-controller.ts index 9d9de1d02de..4fb0a5fa95f 100644 --- a/packages/bridge-status-controller/src/bridge-status-controller.ts +++ b/packages/bridge-status-controller/src/bridge-status-controller.ts @@ -79,6 +79,7 @@ import { getAddTransactionBatchParams, getClientRequest, getHistoryKey, + getIntentFromQuote, getStatusRequestParams, handleApprovalDelay, handleMobileHardwareWalletDelay, @@ -1735,12 +1736,9 @@ export class BridgeStatusController extends StaticIntervalPollingController { ); }); }); + + describe('getIntentFromQuote', () => { + it('returns intent when present in quote response', () => { + const mockIntent = { protocol: 'cowswap', order: { some: 'data' } }; + const quoteResponse = { + quote: { + intent: mockIntent, + srcChainId: 1, + destChainId: 1, + }, + } as never; + + expect(getIntentFromQuote(quoteResponse)).toBe(mockIntent); + }); + + it('throws error when intent is missing from quote', () => { + const quoteResponse = { + quote: { + srcChainId: 1, + destChainId: 1, + }, + } as never; + + expect(() => getIntentFromQuote(quoteResponse)).toThrow( + 'submitIntent: missing intent data', + ); + }); + + it('throws error when intent is undefined', () => { + const quoteResponse = { + quote: { + intent: undefined, + srcChainId: 1, + destChainId: 1, + }, + } as never; + + expect(() => getIntentFromQuote(quoteResponse)).toThrow( + 'submitIntent: missing intent data', + ); + }); + }); }); diff --git a/packages/bridge-status-controller/src/utils/transaction.ts b/packages/bridge-status-controller/src/utils/transaction.ts index 8a7a01f4417..ed0cf176176 100644 --- a/packages/bridge-status-controller/src/utils/transaction.ts +++ b/packages/bridge-status-controller/src/utils/transaction.ts @@ -8,6 +8,7 @@ import { isCrossChain, } from '@metamask/bridge-controller'; import type { + Intent, QuoteMetadata, QuoteResponse, Trade, @@ -475,3 +476,20 @@ export function getHistoryKey( } return historyKey; } + +/** + * Extracts and validates the intent data from a quote response. + * + * @param quoteResponse - The quote response that may contain intent data + * @returns The intent data from the quote + * @throws Error if the quote does not contain intent data + */ +export function getIntentFromQuote( + quoteResponse: QuoteResponse & { quote: { intent?: Intent } }, +): Intent { + const { intent } = quoteResponse.quote; + if (!intent) { + throw new Error('submitIntent: missing intent data'); + } + return intent; +} From 24c8022ff2ff1b7c2f23560af1be459a8199ceef Mon Sep 17 00:00:00 2001 From: IF <139582705+infiniteflower@users.noreply.github.com> Date: Thu, 22 Jan 2026 14:49:43 -0500 Subject: [PATCH 09/14] chore: format --- .../src/bridge-status-controller.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/bridge-status-controller/src/bridge-status-controller.test.ts b/packages/bridge-status-controller/src/bridge-status-controller.test.ts index f692fff933f..d7cbec8ff6f 100644 --- a/packages/bridge-status-controller/src/bridge-status-controller.test.ts +++ b/packages/bridge-status-controller/src/bridge-status-controller.test.ts @@ -763,7 +763,7 @@ describe('BridgeStatusController', () => { jest.clearAllMocks(); jest.clearAllTimers(); // eslint-disable-next-line no-empty-function - consoleFnSpy = jest.spyOn(console, 'warn').mockImplementation(() => {}); + consoleFnSpy = jest.spyOn(console, 'warn').mockImplementation(() => { }); }); afterEach(() => { @@ -4091,7 +4091,7 @@ describe('BridgeStatusController', () => { jest.clearAllTimers(); jest.clearAllMocks(); // eslint-disable-next-line no-empty-function - consoleFnSpy = jest.spyOn(console, 'warn').mockImplementation(() => {}); + consoleFnSpy = jest.spyOn(console, 'warn').mockImplementation(() => { }); mockMessenger = new Messenger({ namespace: MOCK_ANY_NAMESPACE }); mockBridgeStatusMessenger = new Messenger({ namespace: BRIDGE_STATUS_CONTROLLER_NAME, From 9f5228d5ee5b8591ffa28b65eadbc8b99a4eab52 Mon Sep 17 00:00:00 2001 From: IF <139582705+infiniteflower@users.noreply.github.com> Date: Thu, 22 Jan 2026 14:57:03 -0500 Subject: [PATCH 10/14] fix: lint --- .../src/bridge-status-controller.test.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/bridge-status-controller/src/bridge-status-controller.test.ts b/packages/bridge-status-controller/src/bridge-status-controller.test.ts index d7cbec8ff6f..8da3ecd1223 100644 --- a/packages/bridge-status-controller/src/bridge-status-controller.test.ts +++ b/packages/bridge-status-controller/src/bridge-status-controller.test.ts @@ -935,7 +935,9 @@ describe('BridgeStatusController', () => { expect(() => { bridgeStatusController.startPollingForBridgeTxStatus(argsWithoutId); - }).toThrow('Cannot start polling: bridgeTxMeta.id is required for polling'); + }).toThrow( + 'Cannot start polling: bridgeTxMeta.id is required for polling', + ); bridgeStatusController.stopAllPolling(); }); @@ -957,7 +959,9 @@ describe('BridgeStatusController', () => { expect(() => { bridgeStatusController.startPollingForBridgeTxStatus(argsWithoutMeta); - }).toThrow('Cannot start polling: bridgeTxMeta.id is required for polling'); + }).toThrow( + 'Cannot start polling: bridgeTxMeta.id is required for polling', + ); bridgeStatusController.stopAllPolling(); }); From e543794f6d6db613210d7806d83c6b68f2c12e09 Mon Sep 17 00:00:00 2001 From: IF <139582705+infiniteflower@users.noreply.github.com> Date: Thu, 22 Jan 2026 15:02:19 -0500 Subject: [PATCH 11/14] fix: lint --- .../src/bridge-status-controller.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/bridge-status-controller/src/bridge-status-controller.test.ts b/packages/bridge-status-controller/src/bridge-status-controller.test.ts index 8da3ecd1223..5af1c07f817 100644 --- a/packages/bridge-status-controller/src/bridge-status-controller.test.ts +++ b/packages/bridge-status-controller/src/bridge-status-controller.test.ts @@ -763,7 +763,7 @@ describe('BridgeStatusController', () => { jest.clearAllMocks(); jest.clearAllTimers(); // eslint-disable-next-line no-empty-function - consoleFnSpy = jest.spyOn(console, 'warn').mockImplementation(() => { }); + consoleFnSpy = jest.spyOn(console, 'warn').mockImplementation(() => {}); }); afterEach(() => { @@ -4095,7 +4095,7 @@ describe('BridgeStatusController', () => { jest.clearAllTimers(); jest.clearAllMocks(); // eslint-disable-next-line no-empty-function - consoleFnSpy = jest.spyOn(console, 'warn').mockImplementation(() => { }); + consoleFnSpy = jest.spyOn(console, 'warn').mockImplementation(() => {}); mockMessenger = new Messenger({ namespace: MOCK_ANY_NAMESPACE }); mockBridgeStatusMessenger = new Messenger({ namespace: BRIDGE_STATUS_CONTROLLER_NAME, From d75bc9c8784656e02276f8f457288456dedd89cc Mon Sep 17 00:00:00 2001 From: IF <139582705+infiniteflower@users.noreply.github.com> Date: Thu, 22 Jan 2026 15:13:52 -0500 Subject: [PATCH 12/14] chore: make it more obvious when we're using txMetaId --- .../src/bridge-status-controller.ts | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/packages/bridge-status-controller/src/bridge-status-controller.ts b/packages/bridge-status-controller/src/bridge-status-controller.ts index 4fb0a5fa95f..1922c71dfeb 100644 --- a/packages/bridge-status-controller/src/bridge-status-controller.ts +++ b/packages/bridge-status-controller/src/bridge-status-controller.ts @@ -215,7 +215,7 @@ export class BridgeStatusController extends StaticIntervalPollingController { - const { type, status, id, actionId } = transactionMeta; + const { type, status, id: txMetaId, actionId } = transactionMeta; if ( type && @@ -235,17 +235,17 @@ export class BridgeStatusController extends StaticIntervalPollingController { - const { type, id, chainId } = transactionMeta; + const { type, id: txMetaId, chainId } = transactionMeta; if (type === TransactionType.swap) { this.#trackUnifiedSwapBridgeEvent( UnifiedSwapBridgeEventName.Completed, - id, + txMetaId, ); } if (type === TransactionType.bridge && !isNonEvmChainId(chainId)) { - this.#startPollingForTxId(id); + this.#startPollingForTxId(txMetaId); } }, ); From e02745e185b1eeb4f6834ff7dd03b09e2dddcc5f Mon Sep 17 00:00:00 2001 From: IF <139582705+infiniteflower@users.noreply.github.com> Date: Thu, 22 Jan 2026 15:16:26 -0500 Subject: [PATCH 13/14] chore: make it more obvious when txMetaId is being used --- .../src/bridge-status-controller.ts | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/packages/bridge-status-controller/src/bridge-status-controller.ts b/packages/bridge-status-controller/src/bridge-status-controller.ts index 1922c71dfeb..1d61f5ff657 100644 --- a/packages/bridge-status-controller/src/bridge-status-controller.ts +++ b/packages/bridge-status-controller/src/bridge-status-controller.ts @@ -276,20 +276,23 @@ export class BridgeStatusController extends StaticIntervalPollingController { - // Look up by txMeta.id first - let txHistoryKey: string | undefined = this.state.txHistory[id] - ? id + readonly #markTxAsFailed = ({ + id: txMetaId, + actionId, + }: TransactionMeta): void => { + // Look up by txMetaId first + let txHistoryKey: string | undefined = this.state.txHistory[txMetaId] + ? txMetaId : undefined; - // If not found by id, try looking up by actionId (for pre-submission failures) + // If not found by txMetaId, try looking up by actionId (for pre-submission failures) if (!txHistoryKey && actionId && this.state.txHistory[actionId]) { txHistoryKey = actionId; } // If still not found, try looking up by approvalTxId txHistoryKey ??= Object.keys(this.state.txHistory).find( - (key) => this.state.txHistory[key].approvalTxId === id, + (key) => this.state.txHistory[key].approvalTxId === txMetaId, ); if (!txHistoryKey) { From 385b6d86a04bc34f7351f3955ec1aa5f4932b7f5 Mon Sep 17 00:00:00 2001 From: IF <139582705+infiniteflower@users.noreply.github.com> Date: Thu, 22 Jan 2026 15:18:53 -0500 Subject: [PATCH 14/14] refactor: remove unnecessary empty bridgeTxMeta from pre-submission history Since bridgeTxMeta is now optional, we don't need to pass an empty object when adding pre-submission history. The actionId is used as the key instead. --- .../bridge-status-controller/src/bridge-status-controller.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/bridge-status-controller/src/bridge-status-controller.ts b/packages/bridge-status-controller/src/bridge-status-controller.ts index 1d61f5ff657..7383d6d963e 100644 --- a/packages/bridge-status-controller/src/bridge-status-controller.ts +++ b/packages/bridge-status-controller/src/bridge-status-controller.ts @@ -1629,7 +1629,6 @@ export class BridgeStatusController extends StaticIntervalPollingController