Skip to content

Chore/poll timeout duration #5683

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 11 commits into
base: main
Choose a base branch
from
4 changes: 4 additions & 0 deletions packages/bridge-status-controller/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down Expand Up @@ -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(),
Expand Down
59 changes: 33 additions & 26 deletions packages/bridge-status-controller/src/bridge-status-controller.ts
Original file line number Diff line number Diff line change
@@ -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';
Expand All @@ -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,
Expand All @@ -65,8 +65,8 @@ import {
getTxMetaFields,
handleLineaDelay,
handleSolanaTxResponse,
generateActionId,
} from './utils/transaction';
import { generateActionId } from './utils/transaction';

const metadata: StateMetadata<BridgeStatusControllerState> = {
// We want to persist the bridge status state so that we can show the proper data for the Activity list
Expand Down Expand Up @@ -226,11 +226,13 @@ export class BridgeStatusController extends StaticIntervalPollingController<Brid
incompleteHistoryItems.forEach((historyItem) => {
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);
});
};

Expand Down Expand Up @@ -290,9 +292,14 @@ export class BridgeStatusController extends StaticIntervalPollingController<Brid
state.txHistory[bridgeTxMeta.id] = txHistoryItem;
});

this.#pollingTokensByTxMetaId[bridgeTxMeta.id] = this.startPolling({
const input = {
bridgeTxMetaId: bridgeTxMeta.id,
});
};

this.#pollingTokensByTxMetaId[bridgeTxMeta.id] = this.startPolling(input);

// set the polling max time duration to five minutes
this.setKeyDuration(getKey(input), POLLING_DURATION);
};

// This will be called after you call this.startPolling()
Expand Down
4 changes: 4 additions & 0 deletions packages/bridge-status-controller/src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@ import type { BridgeStatusControllerState } from './types';

export const REFRESH_INTERVAL_MS = 10 * 1000;

// The time duration for polling the bridge status API
// defaults to 5 minutes
export const POLLING_DURATION = 5 * 60 * 1000;

export const BRIDGE_STATUS_CONTROLLER_NAME = 'BridgeStatusController';

export const DEFAULT_BRIDGE_STATUS_CONTROLLER_STATE: BridgeStatusControllerState =
Expand Down
1 change: 1 addition & 0 deletions packages/polling-controller/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Changed

- Bump `@metamask/controller-utils` to `^11.7.0` ([#5583](https://github.com/MetaMask/core/pull/5583))
- StaticIntervalPollingController now allows to set duration to key ([#5683](https://github.com/MetaMask/core/pull/5683))

## [13.0.0]

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { Messenger } from '@metamask/base-controller';
import { createDeferredPromise } from '@metamask/utils';
import { useFakeTimers } from 'sinon';

import { getKey } from './AbstractPollingController';
import { StaticIntervalPollingController } from './StaticIntervalPollingController';
import { advanceTime } from '../../../tests/helpers';

Expand Down Expand Up @@ -310,6 +311,27 @@ describe('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', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,18 @@ function StaticIntervalPollingControllerMixin<
{
readonly #intervalIds: Record<PollingTokenSetId, NodeJS.Timeout> = {};

#durationIds: Record<PollingTokenSetId, number> = {};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we add a comment on what this is?

Also I think maybe the name can be more clear. Calling it durationIds reads to me like the value of this key-value pair is an id, but it's really the duration and the key is id.

Maybe durationsById


#intervalLength: number | undefined = 1000;

setKeyDuration(key: string, duration: number) {
Copy link
Contributor

@infiniteflower infiniteflower Apr 21, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
setKeyDuration(key: string, duration: number) {
setDurationForId(id: string, duration: number) {

Just to keep in line with the id terminology of PollingTokenSetId.

this.#durationIds[key] = duration;
}

getKeyDuration(key: string) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
getKeyDuration(key: string) {
getDurationForId(id: string) {

return this.#durationIds[key];
}

setIntervalLength(intervalLength: number) {
this.#intervalLength = intervalLength;
}
Expand All @@ -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) {
Expand Down
2 changes: 2 additions & 0 deletions packages/polling-controller/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,6 @@ export {
StaticIntervalPollingController,
} from './StaticIntervalPollingController';

export { getKey } from './AbstractPollingController';

export type { IPollingController } from './types';
Loading