diff --git a/packages/bridge-status-controller/CHANGELOG.md b/packages/bridge-status-controller/CHANGELOG.md index d985aae18a2..adbf90ce4e8 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] +### Added + +- Bridge status controller now sets the polling duration to 5 minutes ([#5683](https://github.com/MetaMask/core/pull/5683)) + ## [15.0.0] ### Changed 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 78ed0cd6f05..ec9dc4fc89d 100644 --- a/packages/bridge-status-controller/src/bridge-status-controller.test.ts +++ b/packages/bridge-status-controller/src/bridge-status-controller.test.ts @@ -589,6 +589,7 @@ describe('BridgeStatusController', () => { // Assertion expect(bridgeStatusController.state.txHistory).toMatchSnapshot(); }); + it('restarts polling for history items that are not complete', async () => { // Setup jest.useFakeTimers(); @@ -625,7 +626,7 @@ describe('BridgeStatusController', () => { jest.clearAllMocks(); }); - it('sets the inital tx history state', async () => { + it('sets the initial tx history state', async () => { // Setup const bridgeStatusController = new BridgeStatusController({ messenger: getMessengerMock(), diff --git a/packages/bridge-status-controller/src/bridge-status-controller.ts b/packages/bridge-status-controller/src/bridge-status-controller.ts index b73efc5b742..5a12024113e 100644 --- a/packages/bridge-status-controller/src/bridge-status-controller.ts +++ b/packages/bridge-status-controller/src/bridge-status-controller.ts @@ -1,32 +1,31 @@ import type { StateMetadata } from '@metamask/base-controller'; -import type { - BridgeAsset, - QuoteMetadata, - RequiredEventContextFromClient, - TxData, - QuoteResponse, -} from '@metamask/bridge-controller'; import { formatChainIdToHex, getEthUsdtResetData, isEthUsdt, isNativeAddress, isSolanaChainId, + type QuoteResponse, + type BridgeAsset, + type QuoteMetadata, + type TxData, + type RequiredEventContextFromClient, StatusTypes, UnifiedSwapBridgeEventName, getActionType, } from '@metamask/bridge-controller'; import { toHex } from '@metamask/controller-utils'; import { EthAccountType } from '@metamask/keyring-api'; -import { StaticIntervalPollingController } from '@metamask/polling-controller'; -import type { - TransactionController, - TransactionParams, -} from '@metamask/transaction-controller'; +import { + StaticIntervalPollingController, + getKey, +} from '@metamask/polling-controller'; import { TransactionStatus, TransactionType, type TransactionMeta, + type TransactionController, + type TransactionParams, } from '@metamask/transaction-controller'; import type { UserOperationController } from '@metamask/user-operation-controller'; import { numberToHex, type Hex } from '@metamask/utils'; @@ -37,15 +36,16 @@ import { BRIDGE_STATUS_CONTROLLER_NAME, DEFAULT_BRIDGE_STATUS_CONTROLLER_STATE, REFRESH_INTERVAL_MS, + POLLING_DURATION, } from './constants'; -import { type BridgeStatusControllerMessenger } from './types'; -import type { - BridgeStatusControllerState, - StartPollingForBridgeTxStatusArgsSerialized, - FetchFunction, - BridgeClientId, - SolanaTransactionMeta, - BridgeHistoryItem, +import { + type BridgeStatusControllerMessenger, + type BridgeStatusControllerState, + type StartPollingForBridgeTxStatusArgsSerialized, + type FetchFunction, + type BridgeClientId, + type SolanaTransactionMeta, + type BridgeHistoryItem, } from './types'; import { fetchBridgeTxStatus, @@ -65,8 +65,8 @@ import { getTxMetaFields, handleLineaDelay, handleSolanaTxResponse, + generateActionId, } from './utils/transaction'; -import { generateActionId } from './utils/transaction'; const metadata: StateMetadata = { // We want to persist the bridge status state so that we can show the proper data for the Activity list @@ -226,11 +226,13 @@ export class BridgeStatusController extends StaticIntervalPollingController { const bridgeTxMetaId = historyItem.txMetaId; + const input = { + bridgeTxMetaId, + }; // We manually call startPolling() here rather than go through startPollingForBridgeTxStatus() // because we don't want to overwrite the existing historyItem in state - this.#pollingTokensByTxMetaId[bridgeTxMetaId] = this.startPolling({ - bridgeTxMetaId, - }); + this.#pollingTokensByTxMetaId[bridgeTxMetaId] = this.startPolling(input); + this.setKeyDuration(getKey(input), POLLING_DURATION); }); }; @@ -290,9 +292,14 @@ export class BridgeStatusController extends StaticIntervalPollingController { expect(controller._executePoll).toHaveBeenCalledTimes(1); controller.stopAllPolling(); }); + + it('should stop polling session if the key is assigned duration and goes beyond it', async () => { + const input = { + networkClientId: 'mainnet', + }; + const key = getKey(input); + + controller.startPolling(input); + controller.setKeyDuration(key, TICK_TIME * 2 - 1); + + await advanceTime({ clock, duration: 0 }); + expect(controller._executePoll).toHaveBeenCalledTimes(1); + controller.executePollPromises[0].resolve(); + await advanceTime({ clock, duration: TICK_TIME }); + expect(controller._executePoll).toHaveBeenCalledTimes(2); + controller.executePollPromises[1].resolve(); + await advanceTime({ clock, duration: TICK_TIME }); + expect(controller._executePoll).toHaveBeenCalledTimes(2); + await advanceTime({ clock, duration: TICK_TIME }); + expect(controller._executePoll).toHaveBeenCalledTimes(2); + }); }); describe('onPollingCompleteByNetworkClientId', () => { diff --git a/packages/polling-controller/src/StaticIntervalPollingController.ts b/packages/polling-controller/src/StaticIntervalPollingController.ts index 5076dfcffdf..4e41b15df73 100644 --- a/packages/polling-controller/src/StaticIntervalPollingController.ts +++ b/packages/polling-controller/src/StaticIntervalPollingController.ts @@ -30,8 +30,18 @@ function StaticIntervalPollingControllerMixin< { readonly #intervalIds: Record = {}; + #durationIds: Record = {}; + #intervalLength: number | undefined = 1000; + setKeyDuration(key: string, duration: number) { + this.#durationIds[key] = duration; + } + + getKeyDuration(key: string) { + return this.#durationIds[key]; + } + setIntervalLength(intervalLength: number) { this.#intervalLength = intervalLength; } @@ -54,6 +64,12 @@ function StaticIntervalPollingControllerMixin< // TODO: Either fix this lint violation or explain why it's necessary to ignore. // eslint-disable-next-line @typescript-eslint/no-misused-promises async () => { + if (this.#durationIds[key] && Date.now() > this.#durationIds[key]) { + this._stopPollingByPollingTokenSetId(key); + delete this.#durationIds[key]; + return; + } + try { await this._executePoll(input); } catch (error) { diff --git a/packages/polling-controller/src/index.ts b/packages/polling-controller/src/index.ts index ba1758c443b..e10bbcc8793 100644 --- a/packages/polling-controller/src/index.ts +++ b/packages/polling-controller/src/index.ts @@ -8,4 +8,6 @@ export { StaticIntervalPollingController, } from './StaticIntervalPollingController'; +export { getKey } from './AbstractPollingController'; + export type { IPollingController } from './types';