diff --git a/packages/assets-controllers/CHANGELOG.md b/packages/assets-controllers/CHANGELOG.md index 9419685a3e..b78c54edcc 100644 --- a/packages/assets-controllers/CHANGELOG.md +++ b/packages/assets-controllers/CHANGELOG.md @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed +- Extend `TokenBalancesController` `isDeprecated` handling to `startPolling`, `updateChainPollingConfigs`, and `TransactionController:transactionConfirmed`, so polling and post-transaction balance fetches are skipped and stale `tokenBalances` are cleared when the controller is deprecated ([#9327](https://github.com/MetaMask/core/pull/9327)) - Bump `@metamask/keyring-api` from `^23.1.0` to `^23.3.0` ([#9249](https://github.com/MetaMask/core/pull/9249)) - Bump `@metamask/transaction-controller` from `^68.1.1` to `^68.2.0` ([#9253](https://github.com/MetaMask/core/pull/9253)) - Bump `@metamask/multichain-account-service` from `^11.0.0` to `^11.1.0` ([#9264](https://github.com/MetaMask/core/pull/9264)) diff --git a/packages/assets-controllers/src/TokenBalancesController.test.ts b/packages/assets-controllers/src/TokenBalancesController.test.ts index 56656ee144..f67e865e0b 100644 --- a/packages/assets-controllers/src/TokenBalancesController.test.ts +++ b/packages/assets-controllers/src/TokenBalancesController.test.ts @@ -2035,6 +2035,68 @@ describe('TokenBalancesController', () => { expect(controller.state.tokenBalances).toStrictEqual({}); }); + + it('does not start polling and clears stale tokenBalances on startPolling when isDeprecated toggles to true at runtime', () => { + let deprecated = false; + const { controller } = setupController({ + config: { state: initialState, isDeprecated: () => deprecated }, + }); + + const executePollSpy = jest.spyOn(controller, '_executePoll'); + + deprecated = true; + + controller.startPolling({ chainIds: ['0x1'] }); + + expect(executePollSpy).not.toHaveBeenCalled(); + expect(controller.state.tokenBalances).toStrictEqual({}); + }); + + it('does not restart polling and clears stale tokenBalances on updateChainPollingConfigs when isDeprecated toggles to true at runtime', () => { + jest.useFakeTimers(); + let deprecated = false; + const { controller } = setupController({ + config: { state: initialState, isDeprecated: () => deprecated }, + }); + + controller.startPolling({ chainIds: ['0x1'] }); + + const executePollSpy = jest.spyOn(controller, '_executePoll'); + executePollSpy.mockClear(); + + deprecated = true; + + controller.updateChainPollingConfigs( + { '0x1': { interval: 60_000 } }, + { immediateUpdate: true }, + ); + + expect(executePollSpy).not.toHaveBeenCalled(); + expect(controller.state.tokenBalances).toStrictEqual({}); + }); + + it('does not fetch and clears stale tokenBalances on TransactionController:transactionConfirmed when isDeprecated toggles to true at runtime', async () => { + let deprecated = false; + const { controller, messenger } = setupController({ + config: { state: initialState, isDeprecated: () => deprecated }, + }); + + const fetchSpy = jest.spyOn( + multicall, + 'getTokenBalancesForMultipleAddresses', + ); + + deprecated = true; + + messenger.publish('TransactionController:transactionConfirmed', { + chainId: '0x1', + }); + + await flushPromises(); + + expect(fetchSpy).not.toHaveBeenCalled(); + expect(controller.state.tokenBalances).toStrictEqual({}); + }); }); describe('when accountRemoved is published', () => { diff --git a/packages/assets-controllers/src/TokenBalancesController.ts b/packages/assets-controllers/src/TokenBalancesController.ts index f04e5d7fc8..479741a290 100644 --- a/packages/assets-controllers/src/TokenBalancesController.ts +++ b/packages/assets-controllers/src/TokenBalancesController.ts @@ -516,13 +516,7 @@ export class TokenBalancesController extends StaticIntervalPollingController<{ this.messenger.subscribe( 'TransactionController:transactionConfirmed', - (transactionMeta) => { - this.updateBalances({ - chainIds: [transactionMeta.chainId], - }).catch(() => { - // Silently handle balance update errors - }); - }, + this.#onTransactionConfirmed, ); } @@ -630,6 +624,10 @@ export class TokenBalancesController extends StaticIntervalPollingController<{ }; override _startPolling({ chainIds }: { chainIds: ChainIdHex[] }): void { + if (this.#isDeprecated()) { + this.#enforceDisabledState(); + return; + } this.#requestedChainIds = [...chainIds]; this.#isControllerPollingActive = true; this.#startIntervalGroupPolling(chainIds, true); @@ -759,6 +757,10 @@ export class TokenBalancesController extends StaticIntervalPollingController<{ configs: Record, options: UpdateChainPollingConfigsOptions = { immediateUpdate: true }, ): void { + if (this.#isDeprecated()) { + this.#enforceDisabledState(); + return; + } Object.assign(this.#chainPollingConfig, configs); if (this.#isControllerPollingActive) { @@ -1428,6 +1430,20 @@ export class TokenBalancesController extends StaticIntervalPollingController<{ }); }; + readonly #onTransactionConfirmed = (transactionMeta: { + chainId: ChainIdHex; + }): void => { + if (this.#isDeprecated()) { + this.#enforceDisabledState(); + return; + } + this.updateBalances({ + chainIds: [transactionMeta.chainId], + }).catch(() => { + // Silently handle balance update errors + }); + }; + readonly #onAccountChanged = (): void => { if (this.#isDeprecated()) { this.#enforceDisabledState();