From 084edb6e59784940eff53cfd5b510d0974da0ed0 Mon Sep 17 00:00:00 2001 From: Jongsun Suh Date: Mon, 18 Dec 2023 16:30:07 -0800 Subject: [PATCH 01/28] Replace callbacks `onNetworkDidChange`, `onTokenListStateChange`, `getNetworkClientById` with messenger actions/events --- .../src/TokensController.ts | 104 ++++++++++-------- 1 file changed, 57 insertions(+), 47 deletions(-) diff --git a/packages/assets-controllers/src/TokensController.ts b/packages/assets-controllers/src/TokensController.ts index f263f331ca2..4138eeb3387 100644 --- a/packages/assets-controllers/src/TokensController.ts +++ b/packages/assets-controllers/src/TokensController.ts @@ -22,8 +22,8 @@ import { import { abiERC721 } from '@metamask/metamask-eth-abis'; import type { NetworkClientId, - NetworkController, - NetworkState, + NetworkControllerGetNetworkClientByIdAction, + NetworkControllerNetworkDidChangeEvent, } from '@metamask/network-controller'; import type { PreferencesState } from '@metamask/preferences-controller'; import { rpcErrors } from '@metamask/rpc-errors'; @@ -41,7 +41,7 @@ import { } from './token-service'; import type { TokenListMap, - TokenListState, + TokenListStateChange, TokenListToken, } from './TokenListController'; import type { Token } from './TokenRatesController'; @@ -112,17 +112,30 @@ const controllerName = 'TokensController'; /** * The external actions available to the {@link TokensController}. */ -type AllowedActions = AddApprovalRequest; +export type AllowedActions = + | AddApprovalRequest + | NetworkControllerGetNetworkClientByIdAction; + +export type TokensControllerStateChangeEvent = ControllerStateChangeEvent< + typeof controllerName, + TokensState +>; + +export type TokensControllerEvents = TokensControllerStateChangeEvent; + +export type AllowedEvents = + | NetworkControllerNetworkDidChangeEvent + | TokenListStateChange; /** * The messenger of the {@link TokensController}. */ export type TokensControllerMessenger = RestrictedControllerMessenger< typeof controllerName, - AllowedActions, - never, + TokensControllerActions | AllowedActions, + TokensControllerEvents | AllowedEvents, AllowedActions['type'], - never + AllowedEvents['type'] >; export const getDefaultTokensState = (): TokensState => { @@ -186,17 +199,12 @@ export class TokensController extends BaseControllerV1< */ override name = 'TokensController'; - private readonly getNetworkClientById: NetworkController['getNetworkClientById']; - /** * Creates a TokensController instance. * * @param options - The controller options. * @param options.chainId - The chain ID of the current network. * @param options.onPreferencesStateChange - Allows subscribing to preference controller state changes. - * @param options.onNetworkDidChange - Allows subscribing to network controller networkDidChange events. - * @param options.onTokenListStateChange - Allows subscribing to token list controller state changes. - * @param options.getNetworkClientById - Gets the network client with the given id from the NetworkController. * @param options.config - Initial options used to configure this controller. * @param options.state - Initial state to set on this controller. * @param options.messenger - The controller messenger. @@ -204,9 +212,6 @@ export class TokensController extends BaseControllerV1< constructor({ chainId: initialChainId, onPreferencesStateChange, - onNetworkDidChange, - onTokenListStateChange, - getNetworkClientById, config, state, messenger, @@ -215,13 +220,6 @@ export class TokensController extends BaseControllerV1< onPreferencesStateChange: ( listener: (preferencesState: PreferencesState) => void, ) => void; - onNetworkDidChange: ( - listener: (networkState: NetworkState) => void, - ) => void; - onTokenListStateChange: ( - listener: (tokenListState: TokenListState) => void, - ) => void; - getNetworkClientById: NetworkController['getNetworkClientById']; config?: Partial; state?: Partial; messenger: TokensControllerMessenger; @@ -242,7 +240,6 @@ export class TokensController extends BaseControllerV1< this.initialize(); this.abortController = new AbortController(); - this.getNetworkClientById = getNetworkClientById; this.messagingSystem = messenger; @@ -257,26 +254,32 @@ export class TokensController extends BaseControllerV1< }); }); - onNetworkDidChange(({ providerConfig }) => { - const { allTokens, allIgnoredTokens, allDetectedTokens } = this.state; - const { selectedAddress } = this.config; - const { chainId } = providerConfig; - this.abortController.abort(); - this.abortController = new AbortController(); - this.configure({ chainId }); - this.update({ - tokens: allTokens[chainId]?.[selectedAddress] || [], - ignoredTokens: allIgnoredTokens[chainId]?.[selectedAddress] || [], - detectedTokens: allDetectedTokens[chainId]?.[selectedAddress] || [], - }); - }); + this.messagingSystem.subscribe( + 'NetworkController:networkDidChange', + ({ providerConfig }) => { + const { allTokens, allIgnoredTokens, allDetectedTokens } = this.state; + const { selectedAddress } = this.config; + const { chainId } = providerConfig; + this.abortController.abort(); + this.abortController = new AbortController(); + this.configure({ chainId }); + this.update({ + tokens: allTokens[chainId]?.[selectedAddress] || [], + ignoredTokens: allIgnoredTokens[chainId]?.[selectedAddress] || [], + detectedTokens: allDetectedTokens[chainId]?.[selectedAddress] || [], + }); + }, + ); - onTokenListStateChange(({ tokenList }) => { - const { tokens } = this.state; - if (tokens.length && !tokens[0].name) { - this.updateTokensAttribute(tokenList, 'name'); - } - }); + this.messagingSystem.subscribe( + 'TokenListController:stateChange', + ({ tokenList }) => { + const { tokens } = this.state; + if (tokens.length && !tokens[0].name) { + this.updateTokensAttribute(tokenList, 'name'); + } + }, + ); } /** @@ -314,8 +317,10 @@ export class TokensController extends BaseControllerV1< const { allTokens, allIgnoredTokens, allDetectedTokens } = this.state; let currentChainId = chainId; if (networkClientId) { - currentChainId = - this.getNetworkClientById(networkClientId).configuration.chainId; + currentChainId = this.messagingSystem.call( + 'NetworkController:getNetworkClientById', + networkClientId, + ).configuration.chainId; } const accountAddress = interactingAddress || selectedAddress; @@ -445,8 +450,10 @@ export class TokensController extends BaseControllerV1< let interactingChainId; if (networkClientId) { - interactingChainId = - this.getNetworkClientById(networkClientId).configuration.chainId; + interactingChainId = this.messagingSystem.call( + 'NetworkController:getNetworkClientById', + networkClientId, + ).configuration.chainId; } const { newAllTokens, newAllDetectedTokens, newAllIgnoredTokens } = @@ -695,7 +702,10 @@ export class TokensController extends BaseControllerV1< _getProvider(networkClientId?: NetworkClientId): Web3Provider { return new Web3Provider( networkClientId - ? this.getNetworkClientById(networkClientId).provider + ? this.messagingSystem.call( + 'NetworkController:getNetworkClientById', + networkClientId, + ).provider : this.config?.provider, ); } From 51ea6bd3de56a865094d8fe6e268003ba629c88f Mon Sep 17 00:00:00 2001 From: Jongsun Suh Date: Mon, 18 Dec 2023 17:19:43 -0800 Subject: [PATCH 02/28] [token-balances-controller] Adjust tests to updates in tokens-controller --- .../src/TokenBalancesController.test.ts | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/packages/assets-controllers/src/TokenBalancesController.test.ts b/packages/assets-controllers/src/TokenBalancesController.test.ts index 93c08b7508f..2cc40bf0383 100644 --- a/packages/assets-controllers/src/TokenBalancesController.test.ts +++ b/packages/assets-controllers/src/TokenBalancesController.test.ts @@ -1,9 +1,15 @@ import { ControllerMessenger } from '@metamask/base-controller'; import { toHex } from '@metamask/controller-utils'; +import type { + NetworkControllerActions, + NetworkControllerEvents, +} from '@metamask/network-controller'; +import {} from '@metamask/network-controller'; import { BN } from 'ethereumjs-util'; import { flushPromises } from '../../../tests/helpers'; import { TokenBalancesController } from './TokenBalancesController'; +import type { TokenListStateChange } from './TokenListController'; import type { Token } from './TokenRatesController'; import { getDefaultTokensState, type TokensState } from './TokensController'; @@ -25,12 +31,21 @@ function getMessenger() { } describe('TokenBalancesController', () => { + let controllerMessenger: ControllerMessenger< + NetworkControllerActions, + NetworkControllerEvents | TokenListStateChange + >; + beforeEach(() => { jest.useFakeTimers(); + controllerMessenger = new ControllerMessenger(); }); afterEach(() => { jest.useRealTimers(); + controllerMessenger.clearEventSubscriptions( + 'NetworkController:networkDidChange', + ); }); it('should set default state', () => { From 435279101f7dda602065aaa2c812c0c035181786 Mon Sep 17 00:00:00 2001 From: Jongsun Suh Date: Tue, 19 Dec 2023 05:27:52 -0800 Subject: [PATCH 03/28] test: Mock `TokenListController:stateChange` event --- packages/assets-controllers/src/TokensController.test.ts | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/packages/assets-controllers/src/TokensController.test.ts b/packages/assets-controllers/src/TokensController.test.ts index 3f5aaaa35db..a81b8bf441c 100644 --- a/packages/assets-controllers/src/TokensController.test.ts +++ b/packages/assets-controllers/src/TokensController.test.ts @@ -1957,8 +1957,13 @@ describe('TokensController', () => { aggregators: ['Aave'], }, }; - - await tokenListStateChangeListener({ tokenList: sampleMainnetTokenList }); + messenger.publish( + 'TokenListController:stateChange', + { + tokenList: sampleMainnetTokenList, + } as unknown as TokenListState, + [], + ); expect(tokensController.state.tokens[0]).toStrictEqual({ address: '0x01', From f743aea0f9d15f6b394cd73100a57cb504a749cb Mon Sep 17 00:00:00 2001 From: Jongsun Suh Date: Tue, 19 Dec 2023 05:28:34 -0800 Subject: [PATCH 04/28] test: Mock `NetworkController:getNetworkClientById` action handler --- .../src/TokensController.test.ts | 139 +++++++++++------- 1 file changed, 88 insertions(+), 51 deletions(-) diff --git a/packages/assets-controllers/src/TokensController.test.ts b/packages/assets-controllers/src/TokensController.test.ts index a81b8bf441c..671dbd92e83 100644 --- a/packages/assets-controllers/src/TokensController.test.ts +++ b/packages/assets-controllers/src/TokensController.test.ts @@ -1,8 +1,7 @@ +import type { ApprovalControllerEvents } from '@metamask/approval-controller'; import { ApprovalController, - type AddApprovalRequest, type ApprovalControllerState, - type ApprovalControllerEvents, } from '@metamask/approval-controller'; import { ControllerMessenger } from '@metamask/base-controller'; import contractMaps from '@metamask/contract-metadata'; @@ -17,8 +16,13 @@ import { toHex, } from '@metamask/controller-utils'; import type { - NetworkState, + BlockTrackerProxy, ProviderConfig, + ProviderProxy, +} from '@metamask/network-controller'; +import { + defaultState as defaultNetworkState, + NetworkClientType, } from '@metamask/network-controller'; import { defaultState as defaultNetworkState } from '@metamask/network-controller'; import { @@ -31,9 +35,15 @@ import * as sinon from 'sinon'; import { ERC20Standard } from './Standards/ERC20Standard'; import { ERC1155Standard } from './Standards/NftStandards/ERC1155/ERC1155Standard'; import { TOKEN_END_POINT_API } from './token-service'; +import type { TokenListState } from './TokenListController'; import type { Token } from './TokenRatesController'; import { TokensController } from './TokensController'; -import type { TokensControllerMessenger } from './TokensController'; +import type { + AllowedActions, + AllowedEvents, + TokensControllerEvents, + TokensControllerActions, +} from './TokensController'; jest.mock('uuid', () => { return { @@ -51,7 +61,21 @@ const stubCreateEthers = (ctrl: TokensController, res: () => boolean) => { } as any; }); }; - +const MAINNET = { + chainId: ChainId.mainnet, + type: NetworkType.mainnet, + ticker: NetworksTicker.mainnet, +}; +const mockMainnetClient = { + configuration: { + network: 'mainnet', + ...MAINNET, + type: NetworkClientType.Infura, + }, + provider: {} as ProviderProxy, + blockTracker: {} as BlockTrackerProxy, + destroy: jest.fn(), +}; const SEPOLIA = { chainId: toHex(11155111), type: NetworkType.sepolia, @@ -65,17 +89,19 @@ const GOERLI = { const controllerName = 'TokensController' as const; -type ApprovalActions = AddApprovalRequest; - describe('TokensController', () => { let tokensController: TokensController; let triggerPreferencesStateChange: (state: PreferencesState) => void; const messenger = new ControllerMessenger< - ApprovalActions, - ApprovalControllerEvents + TokensControllerActions | AllowedActions, + TokensControllerEvents | AllowedEvents | ApprovalControllerEvents >(); - const approvalControllerMessenger = messenger.getRestricted({ + const approvalControllerMessenger = messenger.getRestricted< + 'ApprovalController', + never, + never + >({ name: 'ApprovalController', }); @@ -85,30 +111,26 @@ describe('TokensController', () => { typesExcludedFromRateLimiting: [ApprovalType.WatchAsset], }); - const tokensControllerMessenger = messenger.getRestricted< - typeof controllerName, - ApprovalActions['type'], - never - >({ + const tokensControllerMessenger = messenger.getRestricted({ name: controllerName, - allowedActions: ['ApprovalController:addRequest'], - }) as TokensControllerMessenger; + allowedActions: [ + 'ApprovalController:addRequest', + 'NetworkController:getNetworkClientById', + ], + allowedEvents: [ + 'NetworkController:networkDidChange', + 'TokenListController:stateChange', + ], + }); - let onNetworkDidChangeListener: (state: NetworkState) => void; const changeNetwork = (providerConfig: ProviderConfig) => { - onNetworkDidChangeListener({ + messenger.publish(`NetworkController:networkDidChange`, { ...defaultNetworkState, providerConfig, }); }; - // TODO: Replace `any` with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any - let tokenListStateChangeListener: (state: any) => void; - const onTokenListStateChange = sinon.stub().callsFake((listener) => { - tokenListStateChangeListener = listener; - }); - + const getNetworkClientByIdHandler = jest.fn(); beforeEach(async () => { const defaultSelectedAddress = '0x1'; const preferencesStateChangeListeners: (( @@ -119,15 +141,10 @@ describe('TokensController', () => { onPreferencesStateChange: (listener) => { preferencesStateChangeListeners.push(listener); }, - onNetworkDidChange: (listener) => (onNetworkDidChangeListener = listener), - onTokenListStateChange, config: { selectedAddress: defaultSelectedAddress, provider: sinon.stub(), }, - // TODO: Replace `any` with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any - getNetworkClientById: sinon.stub() as any, messenger: tokensControllerMessenger, }); triggerPreferencesStateChange = (state: PreferencesState) => { @@ -135,10 +152,16 @@ describe('TokensController', () => { listener(state); } }; + + messenger.registerActionHandler( + `NetworkController:getNetworkClientById`, + getNetworkClientByIdHandler.mockReturnValue(mockMainnetClient), + ); }); afterEach(() => { sinon.restore(); + messenger.unregisterActionHandler(`NetworkController:getNetworkClientById`); }); it('should set default state', () => { @@ -398,11 +421,13 @@ describe('TokensController', () => { it('should add token to the correct chainId when passed a networkClientId', async () => { const stub = stubCreateEthers(tokensController, () => false); - const getNetworkClientByIdStub = jest - // TODO: Replace `any` with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any - .spyOn(tokensController as any, 'getNetworkClientById') - .mockReturnValue({ configuration: { chainId: '0x5' } }); + messenger.unregisterActionHandler(`NetworkController:getNetworkClientById`); + messenger.registerActionHandler( + `NetworkController:getNetworkClientById`, + getNetworkClientByIdHandler.mockReturnValue({ + configuration: { chainId: '0x5' }, + }), + ); await tokensController.addToken({ address: '0x01', symbol: 'bar', @@ -432,7 +457,9 @@ describe('TokensController', () => { }, ]); - expect(getNetworkClientByIdStub).toHaveBeenCalledWith('networkClientId1'); + expect(getNetworkClientByIdHandler).toHaveBeenCalledWith( + 'networkClientId1', + ); stub.restore(); }); @@ -1083,12 +1110,15 @@ describe('TokensController', () => { }); it('should add tokens to the correct chainId when passed a networkClientId', async () => { - const getNetworkClientByIdStub = jest - // TODO: Replace `any` with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any - .spyOn(tokensController as any, 'getNetworkClientById') - .mockReturnValue({ configuration: { chainId: '0x5' } }); - + messenger.unregisterActionHandler( + `NetworkController:getNetworkClientById`, + ); + messenger.registerActionHandler( + `NetworkController:getNetworkClientById`, + getNetworkClientByIdHandler.mockReturnValue({ + configuration: { chainId: '0x5' }, + }), + ); const dummyTokens: Token[] = [ { address: '0x01', @@ -1114,7 +1144,9 @@ describe('TokensController', () => { expect(tokensController.state.allTokens['0x5']['0x1']).toStrictEqual( dummyTokens, ); - expect(getNetworkClientByIdStub).toHaveBeenCalledWith('networkClientId1'); + expect(getNetworkClientByIdHandler).toHaveBeenCalledWith( + 'networkClientId1', + ); }); }); @@ -1539,14 +1571,17 @@ describe('TokensController', () => { }); it('stores token correctly when passed a networkClientId', async function () { - const getNetworkClientByIdStub = jest - // TODO: Replace `any` with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any - .spyOn(tokensController as any, 'getNetworkClientById') - .mockReturnValue({ + messenger.unregisterActionHandler( + `NetworkController:getNetworkClientById`, + ); + messenger.registerActionHandler( + `NetworkController:getNetworkClientById`, + getNetworkClientByIdHandler.mockReturnValue({ configuration: { chainId: '0x5' }, provider: sinon.stub(), - }); + }), + ); + const generateRandomIdStub = jest .spyOn(tokensController, '_generateRandomId') .mockReturnValue(requestId); @@ -1591,7 +1626,9 @@ describe('TokensController', () => { }, true, ); - expect(getNetworkClientByIdStub).toHaveBeenCalledWith('networkClientId1'); + expect(getNetworkClientByIdHandler).toHaveBeenCalledWith( + 'networkClientId1', + ); generateRandomIdStub.mockRestore(); }); From 6ef2f537ba03e8b691882404f13a4db72cce9a96 Mon Sep 17 00:00:00 2001 From: Jongsun Suh Date: Tue, 19 Dec 2023 12:36:04 -0800 Subject: [PATCH 05/28] Use type assertion to mock `getNetworkClientById` return value --- .../src/TokensController.test.ts | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/packages/assets-controllers/src/TokensController.test.ts b/packages/assets-controllers/src/TokensController.test.ts index 671dbd92e83..a3c5b988a0c 100644 --- a/packages/assets-controllers/src/TokensController.test.ts +++ b/packages/assets-controllers/src/TokensController.test.ts @@ -17,6 +17,7 @@ import { } from '@metamask/controller-utils'; import type { BlockTrackerProxy, + NetworkController, ProviderConfig, ProviderProxy, } from '@metamask/network-controller'; @@ -32,6 +33,7 @@ import { import nock from 'nock'; import * as sinon from 'sinon'; +import { FakeProvider } from '../../../tests/fake-provider'; import { ERC20Standard } from './Standards/ERC20Standard'; import { ERC1155Standard } from './Standards/NftStandards/ERC1155/ERC1155Standard'; import { TOKEN_END_POINT_API } from './token-service'; @@ -130,7 +132,10 @@ describe('TokensController', () => { }); }; - const getNetworkClientByIdHandler = jest.fn(); + const getNetworkClientByIdHandler = jest.fn< + ReturnType, + Parameters + >(); beforeEach(async () => { const defaultSelectedAddress = '0x1'; const preferencesStateChangeListeners: (( @@ -155,7 +160,9 @@ describe('TokensController', () => { messenger.registerActionHandler( `NetworkController:getNetworkClientById`, - getNetworkClientByIdHandler.mockReturnValue(mockMainnetClient), + getNetworkClientByIdHandler.mockReturnValue( + mockMainnetClient as unknown as AutoManagedNetworkClient, + ), ); }); @@ -426,7 +433,7 @@ describe('TokensController', () => { `NetworkController:getNetworkClientById`, getNetworkClientByIdHandler.mockReturnValue({ configuration: { chainId: '0x5' }, - }), + } as unknown as AutoManagedNetworkClient), ); await tokensController.addToken({ address: '0x01', @@ -1117,7 +1124,7 @@ describe('TokensController', () => { `NetworkController:getNetworkClientById`, getNetworkClientByIdHandler.mockReturnValue({ configuration: { chainId: '0x5' }, - }), + } as unknown as AutoManagedNetworkClient), ); const dummyTokens: Token[] = [ { @@ -1578,8 +1585,8 @@ describe('TokensController', () => { `NetworkController:getNetworkClientById`, getNetworkClientByIdHandler.mockReturnValue({ configuration: { chainId: '0x5' }, - provider: sinon.stub(), - }), + provider: new FakeProvider({ stubs: [] }), + } as unknown as AutoManagedNetworkClient), ); const generateRandomIdStub = jest From 51a84016881ab90955fe06331ffa4f1a934c2603 Mon Sep 17 00:00:00 2001 From: Jongsun Suh Date: Tue, 19 Dec 2023 12:36:28 -0800 Subject: [PATCH 06/28] Remove unnecessary `?` operator --- packages/assets-controllers/src/TokensController.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/assets-controllers/src/TokensController.ts b/packages/assets-controllers/src/TokensController.ts index 4138eeb3387..378fd14ff0c 100644 --- a/packages/assets-controllers/src/TokensController.ts +++ b/packages/assets-controllers/src/TokensController.ts @@ -706,7 +706,7 @@ export class TokensController extends BaseControllerV1< 'NetworkController:getNetworkClientById', networkClientId, ).provider - : this.config?.provider, + : this.config.provider, ); } From aeab7e23d0c0b04056184e9c81b0774ec5527b85 Mon Sep 17 00:00:00 2001 From: Jongsun Suh Date: Thu, 18 Jan 2024 11:56:28 -0500 Subject: [PATCH 07/28] Fix typing for `TokensState` --- .../src/TokensController.ts | 20 +++++++++---------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/packages/assets-controllers/src/TokensController.ts b/packages/assets-controllers/src/TokensController.ts index 378fd14ff0c..4f76ad08bbd 100644 --- a/packages/assets-controllers/src/TokensController.ts +++ b/packages/assets-controllers/src/TokensController.ts @@ -92,17 +92,15 @@ type SuggestedAssetMeta = { * @property allIgnoredTokens - Object containing hidden/ignored tokens by network and account * @property allDetectedTokens - Object containing tokens detected with non-zero balances */ -// This interface was created before this ESLint rule was added. -// Convert to a `type` in a future major version. -// eslint-disable-next-line @typescript-eslint/consistent-type-definitions -export interface TokensState extends BaseState { - tokens: Token[]; - ignoredTokens: string[]; - detectedTokens: Token[]; - allTokens: { [chainId: Hex]: { [key: string]: Token[] } }; - allIgnoredTokens: { [chainId: Hex]: { [key: string]: string[] } }; - allDetectedTokens: { [chainId: Hex]: { [key: string]: Token[] } }; -} +export type TokensState = BaseState & + Record & { + tokens: Token[]; + ignoredTokens: string[]; + detectedTokens: Token[]; + allTokens: { [chainId: Hex]: { [key: string]: Token[] } }; + allIgnoredTokens: { [chainId: Hex]: { [key: string]: string[] } }; + allDetectedTokens: { [chainId: Hex]: { [key: string]: Token[] } }; + }; /** * The name of the {@link TokensController}. From e6f0fca8396ff42ca161e55b954a74e4723f963c Mon Sep 17 00:00:00 2001 From: Jongsun Suh Date: Thu, 18 Jan 2024 11:56:47 -0500 Subject: [PATCH 08/28] Add `TokensControllerGetStateAction`, `TokensControllerActions` types --- packages/assets-controllers/src/TokensController.ts | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/packages/assets-controllers/src/TokensController.ts b/packages/assets-controllers/src/TokensController.ts index 4f76ad08bbd..94f1741ba4f 100644 --- a/packages/assets-controllers/src/TokensController.ts +++ b/packages/assets-controllers/src/TokensController.ts @@ -5,6 +5,8 @@ import type { BaseConfig, BaseState, RestrictedControllerMessenger, + ControllerGetStateAction, + ControllerStateChangeEvent, } from '@metamask/base-controller'; import { BaseControllerV1 } from '@metamask/base-controller'; import contractsMap from '@metamask/contract-metadata'; @@ -107,6 +109,13 @@ export type TokensState = BaseState & */ const controllerName = 'TokensController'; +export type TokensControllerActions = TokensControllerGetStateAction; + +export type TokensControllerGetStateAction = ControllerGetStateAction< + typeof controllerName, + TokensState +>; + /** * The external actions available to the {@link TokensController}. */ From f3891de35ffdd7f00e8304ec2b4ce87e35f991ca Mon Sep 17 00:00:00 2001 From: Jongsun Suh Date: Thu, 18 Jan 2024 15:33:43 -0500 Subject: [PATCH 09/28] test: Adjust tests to updates in tokens-controller --- .../src/TokensController.test.ts | 158 +++++++++--------- 1 file changed, 83 insertions(+), 75 deletions(-) diff --git a/packages/assets-controllers/src/TokensController.test.ts b/packages/assets-controllers/src/TokensController.test.ts index a3c5b988a0c..674b9b66c7a 100644 --- a/packages/assets-controllers/src/TokensController.test.ts +++ b/packages/assets-controllers/src/TokensController.test.ts @@ -25,7 +25,6 @@ import { defaultState as defaultNetworkState, NetworkClientType, } from '@metamask/network-controller'; -import { defaultState as defaultNetworkState } from '@metamask/network-controller'; import { getDefaultPreferencesState, type PreferencesState, @@ -33,6 +32,7 @@ import { import nock from 'nock'; import * as sinon from 'sinon'; +import { FakeBlockTracker } from '../../../tests/fake-block-tracker'; import { FakeProvider } from '../../../tests/fake-provider'; import { ERC20Standard } from './Standards/ERC20Standard'; import { ERC1155Standard } from './Standards/NftStandards/ERC1155/ERC1155Standard'; @@ -92,38 +92,15 @@ const GOERLI = { const controllerName = 'TokensController' as const; describe('TokensController', () => { - let tokensController: TokensController; let triggerPreferencesStateChange: (state: PreferencesState) => void; - const messenger = new ControllerMessenger< + let tokensController: TokensController; + let approvalController: ApprovalController; + let messenger: ControllerMessenger< TokensControllerActions | AllowedActions, TokensControllerEvents | AllowedEvents | ApprovalControllerEvents - >(); - - const approvalControllerMessenger = messenger.getRestricted< - 'ApprovalController', - never, - never - >({ - name: 'ApprovalController', - }); - - const approvalController = new ApprovalController({ - messenger: approvalControllerMessenger, - showApprovalRequest: jest.fn(), - typesExcludedFromRateLimiting: [ApprovalType.WatchAsset], - }); - - const tokensControllerMessenger = messenger.getRestricted({ - name: controllerName, - allowedActions: [ - 'ApprovalController:addRequest', - 'NetworkController:getNetworkClientById', - ], - allowedEvents: [ - 'NetworkController:networkDidChange', - 'TokenListController:stateChange', - ], - }); + >; + let tokensControllerMessenger; + let approvalControllerMessenger; const changeNetwork = (providerConfig: ProviderConfig) => { messenger.publish(`NetworkController:networkDidChange`, { @@ -136,11 +113,33 @@ describe('TokensController', () => { ReturnType, Parameters >(); + beforeEach(async () => { const defaultSelectedAddress = '0x1'; const preferencesStateChangeListeners: (( state: PreferencesState, ) => void)[] = []; + messenger = new ControllerMessenger(); + + approvalControllerMessenger = messenger.getRestricted< + 'ApprovalController', + never, + never + >({ + name: 'ApprovalController', + }); + + tokensControllerMessenger = messenger.getRestricted({ + name: controllerName, + allowedActions: [ + 'ApprovalController:addRequest', + 'NetworkController:getNetworkClientById', + ], + allowedEvents: [ + 'NetworkController:networkDidChange', + 'TokenListController:stateChange', + ], + }); tokensController = new TokensController({ chainId: ChainId.mainnet, onPreferencesStateChange: (listener) => { @@ -158,6 +157,12 @@ describe('TokensController', () => { } }; + approvalController = new ApprovalController({ + messenger: approvalControllerMessenger, + showApprovalRequest: jest.fn(), + typesExcludedFromRateLimiting: [ApprovalType.WatchAsset], + }); + messenger.registerActionHandler( `NetworkController:getNetworkClientById`, getNetworkClientByIdHandler.mockReturnValue( @@ -1243,6 +1248,7 @@ describe('TokensController', () => { .mockImplementationOnce(() => a.decimals?.toString()); }); + let addRequestHandler: jest.Mock; let createEthersStub: sinon.SinonStub; beforeEach(function () { type = ERC20; @@ -1253,6 +1259,7 @@ describe('TokensController', () => { image: 'image', name: undefined, }; + addRequestHandler = jest.fn(); isERC721 = false; isERC1155 = false; @@ -1583,20 +1590,29 @@ describe('TokensController', () => { ); messenger.registerActionHandler( `NetworkController:getNetworkClientById`, - getNetworkClientByIdHandler.mockReturnValue({ - configuration: { chainId: '0x5' }, - provider: new FakeProvider({ stubs: [] }), - } as unknown as AutoManagedNetworkClient), + getNetworkClientByIdHandler.mockImplementation((networkClientId) => { + expect(networkClientId).toBe('networkClientId1'); + return { + configuration: { chainId: '0x5' }, + provider: new FakeProvider({ + stubs: [], + }), + blockTracker: new FakeBlockTracker(), + destroy: jest.fn(), + } as unknown as AutoManagedNetworkClient; + }), + ); + + messenger.unregisterActionHandler(`ApprovalController:addRequest`); + messenger.registerActionHandler( + `ApprovalController:addRequest`, + addRequestHandler, ); const generateRandomIdStub = jest .spyOn(tokensController, '_generateRandomId') .mockReturnValue(requestId); - const callActionSpy = jest - .spyOn(messenger, 'call') - .mockResolvedValue(undefined); - await tokensController.watchAsset({ asset, type, @@ -1604,6 +1620,20 @@ describe('TokensController', () => { networkClientId: 'networkClientId1', }); + expect(addRequestHandler).toHaveBeenCalledWith( + { + id: requestId, + origin: ORIGIN_METAMASK, + type: ApprovalType.WatchAsset, + requestData: { + id: requestId, + interactingAddress, + asset, + }, + }, + true, + ); + expect(tokensController.state.tokens).toHaveLength(0); expect(tokensController.state.tokens).toStrictEqual([]); expect( @@ -1618,24 +1648,6 @@ describe('TokensController', () => { ...asset, }, ]); - expect(callActionSpy).toHaveBeenCalledTimes(1); - expect(callActionSpy).toHaveBeenCalledWith( - 'ApprovalController:addRequest', - { - id: requestId, - origin: ORIGIN_METAMASK, - type: ApprovalType.WatchAsset, - requestData: { - id: requestId, - interactingAddress, - asset, - }, - }, - true, - ); - expect(getNetworkClientByIdHandler).toHaveBeenCalledWith( - 'networkClientId1', - ); generateRandomIdStub.mockRestore(); }); @@ -1674,7 +1686,7 @@ describe('TokensController', () => { generateRandomIdStub.mockRestore(); }); - it('stores multiple tokens from a batched watchAsset confirmation screen correctly when user confirms', async function () { + it('stores multiple tokens from a batched watchAsset confirmation screen correctly when user confirms', async () => { const generateRandomIdStub = jest .spyOn(tokensController, '_generateRandomId') .mockImplementationOnce(() => requestId) @@ -1701,36 +1713,32 @@ describe('TokensController', () => { mockContract([asset, anotherAsset]); + const registerListeners = new Promise((resolve) => { + const listener = (state: ApprovalControllerState) => { + if (state.pendingApprovalCount === 2) { + messenger.unsubscribe('ApprovalController:stateChange', listener); + resolve(); + } + }; + messenger.subscribe('ApprovalController:stateChange', listener); + }); + + // eslint-disable-next-line @typescript-eslint/no-floating-promises tokensController.watchAsset({ asset, type, interactingAddress }); + + // eslint-disable-next-line @typescript-eslint/no-floating-promises tokensController.watchAsset({ asset: anotherAsset, type, interactingAddress, }); - await new Promise((resolve) => { - const listener = (state: ApprovalControllerState) => { - if (state.pendingApprovalCount === 2) { - approvalControllerMessenger.unsubscribe( - 'ApprovalController:stateChange', - listener, - ); - resolve(); - } - }; - approvalControllerMessenger.subscribe( - 'ApprovalController:stateChange', - listener, - ); - }); + await registerListeners; await approvalController.accept(requestId); await approvalController.accept('67890'); await acceptedRequest; - expect( - tokensController.state.allTokens[ChainId.mainnet][interactingAddress], - ).toHaveLength(2); expect( tokensController.state.allTokens[ChainId.mainnet][interactingAddress], ).toStrictEqual([ From cf8d49a60b3d2f982e5080b9de1d99114f21ba1b Mon Sep 17 00:00:00 2001 From: Jongsun Suh Date: Sun, 21 Jan 2024 15:48:31 -0500 Subject: [PATCH 10/28] Replace `onPreferencesStateChange` callback with messenger event --- .../src/TokensController.ts | 31 +++++++++---------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/packages/assets-controllers/src/TokensController.ts b/packages/assets-controllers/src/TokensController.ts index 94f1741ba4f..eb34d29f984 100644 --- a/packages/assets-controllers/src/TokensController.ts +++ b/packages/assets-controllers/src/TokensController.ts @@ -27,7 +27,7 @@ import type { NetworkControllerGetNetworkClientByIdAction, NetworkControllerNetworkDidChangeEvent, } from '@metamask/network-controller'; -import type { PreferencesState } from '@metamask/preferences-controller'; +import type { PreferencesControllerStateChangeEvent } from '@metamask/preferences-controller'; import { rpcErrors } from '@metamask/rpc-errors'; import type { Hex } from '@metamask/utils'; import { Mutex } from 'async-mutex'; @@ -132,6 +132,7 @@ export type TokensControllerEvents = TokensControllerStateChangeEvent; export type AllowedEvents = | NetworkControllerNetworkDidChangeEvent + | PreferencesControllerStateChangeEvent | TokenListStateChange; /** @@ -211,22 +212,17 @@ export class TokensController extends BaseControllerV1< * * @param options - The controller options. * @param options.chainId - The chain ID of the current network. - * @param options.onPreferencesStateChange - Allows subscribing to preference controller state changes. * @param options.config - Initial options used to configure this controller. * @param options.state - Initial state to set on this controller. * @param options.messenger - The controller messenger. */ constructor({ chainId: initialChainId, - onPreferencesStateChange, config, state, messenger, }: { chainId: Hex; - onPreferencesStateChange: ( - listener: (preferencesState: PreferencesState) => void, - ) => void; config?: Partial; state?: Partial; messenger: TokensControllerMessenger; @@ -250,16 +246,19 @@ export class TokensController extends BaseControllerV1< this.messagingSystem = messenger; - onPreferencesStateChange(({ selectedAddress }) => { - const { allTokens, allIgnoredTokens, allDetectedTokens } = this.state; - const { chainId } = this.config; - this.configure({ selectedAddress }); - this.update({ - tokens: allTokens[chainId]?.[selectedAddress] || [], - ignoredTokens: allIgnoredTokens[chainId]?.[selectedAddress] || [], - detectedTokens: allDetectedTokens[chainId]?.[selectedAddress] || [], - }); - }); + this.messagingSystem.subscribe( + 'PreferencesController:stateChange', + ({ selectedAddress }) => { + const { allTokens, allIgnoredTokens, allDetectedTokens } = this.state; + const { chainId } = this.config; + this.configure({ selectedAddress }); + this.update({ + tokens: allTokens[chainId]?.[selectedAddress] ?? [], + ignoredTokens: allIgnoredTokens[chainId]?.[selectedAddress] ?? [], + detectedTokens: allDetectedTokens[chainId]?.[selectedAddress] ?? [], + }); + }, + ); this.messagingSystem.subscribe( 'NetworkController:networkDidChange', From 4066b2690dae2e49baf03ae11da13c59b18e93d9 Mon Sep 17 00:00:00 2001 From: Jongsun Suh Date: Sun, 21 Jan 2024 15:51:25 -0500 Subject: [PATCH 11/28] Expose `addDetectedTokens` action --- .../assets-controllers/src/TokensController.ts | 14 +++++++++++++- packages/assets-controllers/src/index.ts | 12 +++++++++++- 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/packages/assets-controllers/src/TokensController.ts b/packages/assets-controllers/src/TokensController.ts index eb34d29f984..3f33d864fea 100644 --- a/packages/assets-controllers/src/TokensController.ts +++ b/packages/assets-controllers/src/TokensController.ts @@ -109,13 +109,20 @@ export type TokensState = BaseState & */ const controllerName = 'TokensController'; -export type TokensControllerActions = TokensControllerGetStateAction; +export type TokensControllerActions = + | TokensControllerGetStateAction + | TokensControllerAddDetectedTokensAction; export type TokensControllerGetStateAction = ControllerGetStateAction< typeof controllerName, TokensState >; +export type TokensControllerAddDetectedTokensAction = { + type: `${typeof controllerName}:addDetectedTokens`; + handler: TokensController['addDetectedTokens']; +}; + /** * The external actions available to the {@link TokensController}. */ @@ -246,6 +253,11 @@ export class TokensController extends BaseControllerV1< this.messagingSystem = messenger; + this.messagingSystem.registerActionHandler( + `${controllerName}:addDetectedTokens` as const, + this.addDetectedTokens.bind(this), + ); + this.messagingSystem.subscribe( 'PreferencesController:stateChange', ({ selectedAddress }) => { diff --git a/packages/assets-controllers/src/index.ts b/packages/assets-controllers/src/index.ts index 1f2786d784c..79510584bdb 100644 --- a/packages/assets-controllers/src/index.ts +++ b/packages/assets-controllers/src/index.ts @@ -21,7 +21,17 @@ export type { export { TokenDetectionController } from './TokenDetectionController'; export * from './TokenListController'; export * from './TokenRatesController'; -export * from './TokensController'; +export type { + TokensConfig, + TokensState, + TokensControllerActions, + TokensControllerGetStateAction, + TokensControllerAddDetectedTokensAction, + TokensControllerEvents, + TokensControllerStateChangeEvent, + TokensControllerMessenger, +} from './TokensController'; +export { TokensController } from './TokensController'; export { isTokenDetectionSupportedForNetwork, formatIconUrlWithProxy, From 99563e03748581c7025fcb317cebe7145053df63 Mon Sep 17 00:00:00 2001 From: Jongsun Suh Date: Sun, 21 Jan 2024 15:52:38 -0500 Subject: [PATCH 12/28] test: Use `PreferencesController:stateChange` event instead of `onPreferencesStateChange` callback --- .../src/TokensController.test.ts | 38 +++++++------------ 1 file changed, 14 insertions(+), 24 deletions(-) diff --git a/packages/assets-controllers/src/TokensController.test.ts b/packages/assets-controllers/src/TokensController.test.ts index 674b9b66c7a..3ce62668419 100644 --- a/packages/assets-controllers/src/TokensController.test.ts +++ b/packages/assets-controllers/src/TokensController.test.ts @@ -40,12 +40,7 @@ import { TOKEN_END_POINT_API } from './token-service'; import type { TokenListState } from './TokenListController'; import type { Token } from './TokenRatesController'; import { TokensController } from './TokensController'; -import type { - AllowedActions, - AllowedEvents, - TokensControllerEvents, - TokensControllerActions, -} from './TokensController'; +import type { AllowedActions, AllowedEvents } from './TokensController'; jest.mock('uuid', () => { return { @@ -92,12 +87,11 @@ const GOERLI = { const controllerName = 'TokensController' as const; describe('TokensController', () => { - let triggerPreferencesStateChange: (state: PreferencesState) => void; let tokensController: TokensController; let approvalController: ApprovalController; let messenger: ControllerMessenger< - TokensControllerActions | AllowedActions, - TokensControllerEvents | AllowedEvents | ApprovalControllerEvents + AllowedActions, + AllowedEvents | ApprovalControllerEvents >; let tokensControllerMessenger; let approvalControllerMessenger; @@ -109,6 +103,10 @@ describe('TokensController', () => { }); }; + const triggerPreferencesStateChange = (state: PreferencesState) => { + messenger.publish('PreferencesController:stateChange', state, []); + }; + const getNetworkClientByIdHandler = jest.fn< ReturnType, Parameters @@ -116,9 +114,6 @@ describe('TokensController', () => { beforeEach(async () => { const defaultSelectedAddress = '0x1'; - const preferencesStateChangeListeners: (( - state: PreferencesState, - ) => void)[] = []; messenger = new ControllerMessenger(); approvalControllerMessenger = messenger.getRestricted< @@ -137,25 +132,18 @@ describe('TokensController', () => { ], allowedEvents: [ 'NetworkController:networkDidChange', + 'PreferencesController:stateChange', 'TokenListController:stateChange', ], }); tokensController = new TokensController({ chainId: ChainId.mainnet, - onPreferencesStateChange: (listener) => { - preferencesStateChangeListeners.push(listener); - }, config: { selectedAddress: defaultSelectedAddress, provider: sinon.stub(), }, messenger: tokensControllerMessenger, }); - triggerPreferencesStateChange = (state: PreferencesState) => { - for (const listener of preferencesStateChangeListeners) { - listener(state); - } - }; approvalController = new ApprovalController({ messenger: approvalControllerMessenger, @@ -166,7 +154,9 @@ describe('TokensController', () => { messenger.registerActionHandler( `NetworkController:getNetworkClientById`, getNetworkClientByIdHandler.mockReturnValue( - mockMainnetClient as unknown as AutoManagedNetworkClient, + mockMainnetClient as unknown as ReturnType< + NetworkController['getNetworkClientById'] + >, ), ); }); @@ -438,7 +428,7 @@ describe('TokensController', () => { `NetworkController:getNetworkClientById`, getNetworkClientByIdHandler.mockReturnValue({ configuration: { chainId: '0x5' }, - } as unknown as AutoManagedNetworkClient), + } as unknown as ReturnType), ); await tokensController.addToken({ address: '0x01', @@ -1129,7 +1119,7 @@ describe('TokensController', () => { `NetworkController:getNetworkClientById`, getNetworkClientByIdHandler.mockReturnValue({ configuration: { chainId: '0x5' }, - } as unknown as AutoManagedNetworkClient), + } as unknown as ReturnType), ); const dummyTokens: Token[] = [ { @@ -1599,7 +1589,7 @@ describe('TokensController', () => { }), blockTracker: new FakeBlockTracker(), destroy: jest.fn(), - } as unknown as AutoManagedNetworkClient; + } as unknown as ReturnType; }), ); From 59b8cfa67645563ff51847c5839b2a16712f8eaf Mon Sep 17 00:00:00 2001 From: Jongsun Suh Date: Sun, 21 Jan 2024 15:54:00 -0500 Subject: [PATCH 13/28] [token-detection-controller] Replace tokens-controller callbacks with `getState`, `addDetectedTokens` messenger actions --- .../src/TokenDetectionController.ts | 41 ++++++++++--------- 1 file changed, 21 insertions(+), 20 deletions(-) diff --git a/packages/assets-controllers/src/TokenDetectionController.ts b/packages/assets-controllers/src/TokenDetectionController.ts index 13175385f28..c2edecce98f 100644 --- a/packages/assets-controllers/src/TokenDetectionController.ts +++ b/packages/assets-controllers/src/TokenDetectionController.ts @@ -35,7 +35,10 @@ import type { TokenListToken, } from './TokenListController'; import type { Token } from './TokenRatesController'; -import type { TokensController, TokensState } from './TokensController'; +import type { + TokensControllerAddDetectedTokensAction, + TokensControllerGetStateAction, +} from './TokensController'; const DEFAULT_INTERVAL = 180000; @@ -90,7 +93,9 @@ export type AllowedActions = | NetworkControllerGetNetworkConfigurationByNetworkClientId | GetTokenListState | KeyringControllerGetStateAction - | PreferencesControllerGetStateAction; + | PreferencesControllerGetStateAction + | TokensControllerGetStateAction + | TokensControllerAddDetectedTokensAction; export type TokenDetectionControllerStateChangeEvent = ControllerStateChangeEvent; @@ -146,12 +151,8 @@ export class TokenDetectionController extends StaticIntervalPollingController< #isDetectionEnabledForNetwork: boolean; - readonly #addDetectedTokens: TokensController['addDetectedTokens']; - readonly #getBalancesInSingleCall: AssetsContractController['getBalancesInSingleCall']; - readonly #getTokensState: () => TokensState; - readonly #trackMetaMetricsEvent: (options: { event: string; category: string; @@ -171,9 +172,7 @@ export class TokenDetectionController extends StaticIntervalPollingController< * @param options.interval - Polling interval used to fetch new token rates * @param options.networkClientId - The selected network client ID of the current network * @param options.selectedAddress - Vault selected address - * @param options.addDetectedTokens - Add a list of detected tokens. * @param options.getBalancesInSingleCall - Gets the balances of a list of tokens for the given address. - * @param options.getTokensState - Gets the current state of the Tokens controller. * @param options.trackMetaMetricsEvent - Sets options for MetaMetrics event tracking. */ constructor({ @@ -182,8 +181,6 @@ export class TokenDetectionController extends StaticIntervalPollingController< interval = DEFAULT_INTERVAL, disabled = true, getBalancesInSingleCall, - addDetectedTokens, - getTokensState, trackMetaMetricsEvent, messenger, }: { @@ -191,9 +188,7 @@ export class TokenDetectionController extends StaticIntervalPollingController< selectedAddress?: string; interval?: number; disabled?: boolean; - addDetectedTokens: TokensController['addDetectedTokens']; getBalancesInSingleCall: AssetsContractController['getBalancesInSingleCall']; - getTokensState: () => TokensState; trackMetaMetricsEvent: (options: { event: string; category: string; @@ -226,9 +221,7 @@ export class TokenDetectionController extends StaticIntervalPollingController< this.#chainId, ); - this.#addDetectedTokens = addDetectedTokens; this.#getBalancesInSingleCall = getBalancesInSingleCall; - this.#getTokensState = getTokensState; this.#trackMetaMetricsEvent = trackMetaMetricsEvent; @@ -460,7 +453,9 @@ export class TokenDetectionController extends StaticIntervalPollingController< ? STATIC_MAINNET_TOKEN_LIST : tokenList; - const { tokens, detectedTokens } = this.#getTokensState(); + const { tokens, detectedTokens } = this.messagingSystem.call( + 'TokensController:getState', + ); const tokensToDetect: string[] = []; for (const tokenAddress of Object.keys(tokenListUsed)) { @@ -504,7 +499,9 @@ export class TokenDetectionController extends StaticIntervalPollingController< for (const tokenAddress of Object.keys(balances)) { let ignored; /* istanbul ignore else */ - const { ignoredTokens } = this.#getTokensState(); + const { ignoredTokens } = this.messagingSystem.call( + 'TokensController:getState', + ); if (ignoredTokens.length) { ignored = ignoredTokens.find( (ignoredTokenAddress) => @@ -543,10 +540,14 @@ export class TokenDetectionController extends StaticIntervalPollingController< asset_type: 'TOKEN', }, }); - await this.#addDetectedTokens(tokensToAdd, { - selectedAddress, - chainId, - }); + await this.messagingSystem.call( + 'TokensController:addDetectedTokens', + tokensToAdd, + { + selectedAddress, + chainId, + }, + ); } }); } From b8fcf21a54f65694eff2e706d8908f21ee02f128 Mon Sep 17 00:00:00 2001 From: Jongsun Suh Date: Sun, 21 Jan 2024 15:56:15 -0500 Subject: [PATCH 14/28] [token-detection-controller] test: Mock `TokensController:getState`, `TokensController:addDetectedTokens` messenger actions --- .../src/TokenDetectionController.test.ts | 391 ++++++++++-------- 1 file changed, 221 insertions(+), 170 deletions(-) diff --git a/packages/assets-controllers/src/TokenDetectionController.test.ts b/packages/assets-controllers/src/TokenDetectionController.test.ts index be648263448..6e4f2cd0661 100644 --- a/packages/assets-controllers/src/TokenDetectionController.test.ts +++ b/packages/assets-controllers/src/TokenDetectionController.test.ts @@ -40,6 +40,7 @@ import { type TokenListState, type TokenListToken, } from './TokenListController'; +import type { TokensState } from './TokensController'; import { getDefaultTokensState } from './TokensController'; const DEFAULT_INTERVAL = 180000; @@ -138,6 +139,8 @@ function buildTokenDetectionControllerMessenger( allowedActions: [ 'KeyringController:getState', 'NetworkController:getNetworkConfigurationByNetworkClientId', + 'TokensController:getState', + 'TokensController:addDetectedTokens', 'TokenListController:getState', 'PreferencesController:getState', ], @@ -222,18 +225,20 @@ describe('TokenDetectionController', () => { const mockGetBalancesInSingleCall = jest.fn().mockResolvedValue({ [sampleTokenA.address]: new BN(1), }); - const mockAddDetectedTokens = jest.fn(); const selectedAddress = '0x0000000000000000000000000000000000000001'; await withController( { options: { - addDetectedTokens: mockAddDetectedTokens, getBalancesInSingleCall: mockGetBalancesInSingleCall, networkClientId: NetworkType.mainnet, selectedAddress, }, }, - async ({ controller, mockTokenListGetState }) => { + async ({ + controller, + mockTokenListGetState, + mockAddDetectedTokens, + }) => { mockTokenListGetState({ ...getDefaultTokenListState(), tokenList: { @@ -251,10 +256,14 @@ describe('TokenDetectionController', () => { await controller.start(); - expect(mockAddDetectedTokens).toHaveBeenCalledWith([sampleTokenA], { - chainId: ChainId.mainnet, - selectedAddress, - }); + expect(mockAddDetectedTokens).toHaveBeenCalledWith( + 'TokensController:addDetectedTokens', + [sampleTokenA], + { + chainId: ChainId.mainnet, + selectedAddress, + }, + ); }, ); }); @@ -263,18 +272,20 @@ describe('TokenDetectionController', () => { const mockGetBalancesInSingleCall = jest.fn().mockResolvedValue({ [sampleTokenA.address]: new BN(1), }); - const mockAddDetectedTokens = jest.fn(); const selectedAddress = '0x0000000000000000000000000000000000000001'; await withController( { options: { - addDetectedTokens: mockAddDetectedTokens, getBalancesInSingleCall: mockGetBalancesInSingleCall, networkClientId: 'polygon', selectedAddress, }, }, - async ({ controller, mockTokenListGetState }) => { + async ({ + controller, + mockTokenListGetState, + mockAddDetectedTokens, + }) => { mockTokenListGetState({ ...getDefaultTokenListState(), tokenList: { @@ -292,10 +303,14 @@ describe('TokenDetectionController', () => { await controller.start(); - expect(mockAddDetectedTokens).toHaveBeenCalledWith([sampleTokenA], { - chainId: '0x89', - selectedAddress, - }); + expect(mockAddDetectedTokens).toHaveBeenCalledWith( + 'TokensController:addDetectedTokens', + [sampleTokenA], + { + chainId: '0x89', + selectedAddress, + }, + ); }, ); }); @@ -305,20 +320,22 @@ describe('TokenDetectionController', () => { [sampleTokenA.address]: new BN(1), [sampleTokenB.address]: new BN(1), }); - const mockAddDetectedTokens = jest.fn(); const selectedAddress = '0x0000000000000000000000000000000000000001'; const interval = 100; await withController( { options: { - addDetectedTokens: mockAddDetectedTokens, getBalancesInSingleCall: mockGetBalancesInSingleCall, interval, networkClientId: NetworkType.mainnet, selectedAddress, }, }, - async ({ controller, mockTokenListGetState }) => { + async ({ + controller, + mockTokenListGetState, + mockAddDetectedTokens, + }) => { const tokenListState = { ...getDefaultTokenListState(), tokenList: { @@ -335,7 +352,6 @@ describe('TokenDetectionController', () => { }; mockTokenListGetState(tokenListState); await controller.start(); - mockAddDetectedTokens.mockReset(); tokenListState.tokenList[sampleTokenB.address] = { name: sampleTokenB.name as string, @@ -350,6 +366,7 @@ describe('TokenDetectionController', () => { await advanceTime({ clock, duration: interval }); expect(mockAddDetectedTokens).toHaveBeenCalledWith( + 'TokensController:addDetectedTokens', [sampleTokenA, sampleTokenB], { chainId: ChainId.mainnet, @@ -364,23 +381,25 @@ describe('TokenDetectionController', () => { const mockGetBalancesInSingleCall = jest.fn().mockResolvedValue({ [sampleTokenA.address]: new BN(1), }); - const mockAddDetectedTokens = jest.fn(); - const mockGetTokensState = jest.fn().mockReturnValue({ - ...getDefaultTokensState(), - ignoredTokens: [sampleTokenA.address], - }); const selectedAddress = '0x0000000000000000000000000000000000000001'; await withController( { options: { - addDetectedTokens: mockAddDetectedTokens, getBalancesInSingleCall: mockGetBalancesInSingleCall, - getTokensState: mockGetTokensState, networkClientId: NetworkType.mainnet, selectedAddress, }, }, - async ({ controller, mockTokenListGetState }) => { + async ({ + controller, + mockTokensGetState, + mockTokenListGetState, + mockAddDetectedTokens, + }) => { + mockTokensGetState({ + ...getDefaultTokensState(), + ignoredTokens: [sampleTokenA.address], + }); mockTokenListGetState({ ...getDefaultTokenListState(), tokenList: { @@ -398,7 +417,9 @@ describe('TokenDetectionController', () => { await controller.start(); - expect(mockAddDetectedTokens).not.toHaveBeenCalled(); + expect(mockAddDetectedTokens).not.toHaveBeenCalledWith( + 'TokensController:addDetectedTokens', + ); }, ); }); @@ -407,17 +428,19 @@ describe('TokenDetectionController', () => { const mockGetBalancesInSingleCall = jest.fn().mockResolvedValue({ [sampleTokenA.address]: new BN(1), }); - const mockAddDetectedTokens = jest.fn(); await withController( { options: { - addDetectedTokens: mockAddDetectedTokens, getBalancesInSingleCall: mockGetBalancesInSingleCall, networkClientId: NetworkType.mainnet, selectedAddress: '', }, }, - async ({ controller, mockTokenListGetState }) => { + async ({ + controller, + mockTokenListGetState, + mockAddDetectedTokens, + }) => { mockTokenListGetState({ ...getDefaultTokenListState(), tokenList: { @@ -435,7 +458,9 @@ describe('TokenDetectionController', () => { await controller.start(); - expect(mockAddDetectedTokens).not.toHaveBeenCalled(); + expect(mockAddDetectedTokens).not.toHaveBeenCalledWith( + 'TokensController:addDetectedTokens', + ); }, ); }); @@ -456,7 +481,6 @@ describe('TokenDetectionController', () => { const mockGetBalancesInSingleCall = jest.fn().mockResolvedValue({ [sampleTokenA.address]: new BN(1), }); - const mockAddDetectedTokens = jest.fn(); const firstSelectedAddress = '0x0000000000000000000000000000000000000001'; const secondSelectedAddress = @@ -464,14 +488,17 @@ describe('TokenDetectionController', () => { await withController( { options: { - addDetectedTokens: mockAddDetectedTokens, disabled: false, getBalancesInSingleCall: mockGetBalancesInSingleCall, networkClientId: NetworkType.mainnet, selectedAddress: firstSelectedAddress, }, }, - async ({ mockTokenListGetState, triggerPreferencesStateChange }) => { + async ({ + mockTokenListGetState, + triggerPreferencesStateChange, + mockAddDetectedTokens, + }) => { mockTokenListGetState({ ...getDefaultTokenListState(), tokenList: { @@ -494,10 +521,14 @@ describe('TokenDetectionController', () => { }); await advanceTime({ clock, duration: 1 }); - expect(mockAddDetectedTokens).toHaveBeenCalledWith([sampleTokenA], { - chainId: ChainId.mainnet, - selectedAddress: secondSelectedAddress, - }); + expect(mockAddDetectedTokens).toHaveBeenCalledWith( + 'TokensController:addDetectedTokens', + [sampleTokenA], + { + chainId: ChainId.mainnet, + selectedAddress: secondSelectedAddress, + }, + ); }, ); }); @@ -506,19 +537,21 @@ describe('TokenDetectionController', () => { const mockGetBalancesInSingleCall = jest.fn().mockResolvedValue({ [sampleTokenA.address]: new BN(1), }); - const mockAddDetectedTokens = jest.fn(); const selectedAddress = '0x0000000000000000000000000000000000000001'; await withController( { options: { - addDetectedTokens: mockAddDetectedTokens, disabled: false, getBalancesInSingleCall: mockGetBalancesInSingleCall, networkClientId: NetworkType.mainnet, selectedAddress, }, }, - async ({ mockTokenListGetState, triggerPreferencesStateChange }) => { + async ({ + mockTokenListGetState, + triggerPreferencesStateChange, + mockAddDetectedTokens, + }) => { mockTokenListGetState({ ...getDefaultTokenListState(), tokenList: { @@ -548,10 +581,14 @@ describe('TokenDetectionController', () => { }); await advanceTime({ clock, duration: 1 }); - expect(mockAddDetectedTokens).toHaveBeenCalledWith([sampleTokenA], { - chainId: ChainId.mainnet, - selectedAddress, - }); + expect(mockAddDetectedTokens).toHaveBeenCalledWith( + 'TokensController:addDetectedTokens', + [sampleTokenA], + { + chainId: ChainId.mainnet, + selectedAddress, + }, + ); }, ); }); @@ -560,7 +597,6 @@ describe('TokenDetectionController', () => { const mockGetBalancesInSingleCall = jest.fn().mockResolvedValue({ [sampleTokenA.address]: new BN(1), }); - const mockAddDetectedTokens = jest.fn(); const firstSelectedAddress = '0x0000000000000000000000000000000000000001'; const secondSelectedAddress = @@ -568,14 +604,17 @@ describe('TokenDetectionController', () => { await withController( { options: { - addDetectedTokens: mockAddDetectedTokens, disabled: false, getBalancesInSingleCall: mockGetBalancesInSingleCall, networkClientId: NetworkType.mainnet, selectedAddress: firstSelectedAddress, }, }, - async ({ mockTokenListGetState, triggerPreferencesStateChange }) => { + async ({ + mockTokenListGetState, + triggerPreferencesStateChange, + mockAddDetectedTokens, + }) => { mockTokenListGetState({ ...getDefaultTokenListState(), tokenList: { @@ -598,7 +637,9 @@ describe('TokenDetectionController', () => { }); await advanceTime({ clock, duration: 1 }); - expect(mockAddDetectedTokens).not.toHaveBeenCalled(); + expect(mockAddDetectedTokens).not.toHaveBeenCalledWith( + 'TokensController:addDetectedTokens', + ); }, ); }); @@ -607,19 +648,21 @@ describe('TokenDetectionController', () => { const mockGetBalancesInSingleCall = jest.fn().mockResolvedValue({ [sampleTokenA.address]: new BN(1), }); - const mockAddDetectedTokens = jest.fn(); const selectedAddress = '0x0000000000000000000000000000000000000001'; await withController( { options: { - addDetectedTokens: mockAddDetectedTokens, disabled: false, getBalancesInSingleCall: mockGetBalancesInSingleCall, networkClientId: NetworkType.mainnet, selectedAddress, }, }, - async ({ mockTokenListGetState, triggerPreferencesStateChange }) => { + async ({ + mockTokenListGetState, + triggerPreferencesStateChange, + mockAddDetectedTokens, + }) => { mockTokenListGetState({ ...getDefaultTokenListState(), tokenList: { @@ -642,7 +685,9 @@ describe('TokenDetectionController', () => { }); await advanceTime({ clock, duration: 1 }); - expect(mockAddDetectedTokens).not.toHaveBeenCalled(); + expect(mockAddDetectedTokens).not.toHaveBeenCalledWith( + 'TokensController:addDetectedTokens', + ); }, ); }); @@ -653,7 +698,6 @@ describe('TokenDetectionController', () => { const mockGetBalancesInSingleCall = jest.fn().mockResolvedValue({ [sampleTokenA.address]: new BN(1), }); - const mockAddDetectedTokens = jest.fn(); const firstSelectedAddress = '0x0000000000000000000000000000000000000001'; const secondSelectedAddress = @@ -661,14 +705,17 @@ describe('TokenDetectionController', () => { await withController( { options: { - addDetectedTokens: mockAddDetectedTokens, disabled: true, getBalancesInSingleCall: mockGetBalancesInSingleCall, networkClientId: NetworkType.mainnet, selectedAddress: firstSelectedAddress, }, }, - async ({ mockTokenListGetState, triggerPreferencesStateChange }) => { + async ({ + mockTokenListGetState, + triggerPreferencesStateChange, + mockAddDetectedTokens, + }) => { mockTokenListGetState({ ...getDefaultTokenListState(), tokenList: { @@ -691,7 +738,9 @@ describe('TokenDetectionController', () => { }); await advanceTime({ clock, duration: 1 }); - expect(mockAddDetectedTokens).not.toHaveBeenCalled(); + expect(mockAddDetectedTokens).not.toHaveBeenCalledWith( + 'TokensController:addDetectedTokens', + ); }, ); }); @@ -700,19 +749,21 @@ describe('TokenDetectionController', () => { const mockGetBalancesInSingleCall = jest.fn().mockResolvedValue({ [sampleTokenA.address]: new BN(1), }); - const mockAddDetectedTokens = jest.fn(); const selectedAddress = '0x0000000000000000000000000000000000000001'; await withController( { options: { - addDetectedTokens: mockAddDetectedTokens, disabled: true, getBalancesInSingleCall: mockGetBalancesInSingleCall, networkClientId: NetworkType.mainnet, selectedAddress, }, }, - async ({ mockTokenListGetState, triggerPreferencesStateChange }) => { + async ({ + mockTokenListGetState, + triggerPreferencesStateChange, + mockAddDetectedTokens, + }) => { mockTokenListGetState({ ...getDefaultTokenListState(), tokenList: { @@ -742,7 +793,9 @@ describe('TokenDetectionController', () => { }); await advanceTime({ clock, duration: 1 }); - expect(mockAddDetectedTokens).not.toHaveBeenCalled(); + expect(mockAddDetectedTokens).not.toHaveBeenCalledWith( + 'TokensController:addDetectedTokens', + ); }, ); }); @@ -764,24 +817,21 @@ describe('TokenDetectionController', () => { const mockGetBalancesInSingleCall = jest.fn().mockResolvedValue({ [sampleTokenA.address]: new BN(1), }); - const mockAddDetectedTokens = jest.fn(); const selectedAddress = '0x0000000000000000000000000000000000000001'; - const messenger = new ControllerMessenger< - AllowedActions, - AllowedEvents - >(); await withController( { options: { - addDetectedTokens: mockAddDetectedTokens, disabled: false, getBalancesInSingleCall: mockGetBalancesInSingleCall, networkClientId: NetworkType.mainnet, selectedAddress, }, - messenger, }, - async ({ mockTokenListGetState }) => { + async ({ + mockTokenListGetState, + mockAddDetectedTokens, + triggerNetworkDidChange, + }) => { mockTokenListGetState({ ...getDefaultTokenListState(), tokenList: { @@ -797,16 +847,20 @@ describe('TokenDetectionController', () => { }, }); - messenger.publish('NetworkController:networkDidChange', { + triggerNetworkDidChange({ ...defaultNetworkState, selectedNetworkClientId: 'polygon', }); await advanceTime({ clock, duration: 1 }); - expect(mockAddDetectedTokens).toHaveBeenCalledWith([sampleTokenA], { - chainId: '0x89', - selectedAddress, - }); + expect(mockAddDetectedTokens).toHaveBeenCalledWith( + 'TokensController:addDetectedTokens', + [sampleTokenA], + { + chainId: '0x89', + selectedAddress, + }, + ); }, ); }); @@ -815,24 +869,21 @@ describe('TokenDetectionController', () => { const mockGetBalancesInSingleCall = jest.fn().mockResolvedValue({ [sampleTokenA.address]: new BN(1), }); - const mockAddDetectedTokens = jest.fn(); const selectedAddress = '0x0000000000000000000000000000000000000001'; - const messenger = new ControllerMessenger< - AllowedActions, - AllowedEvents - >(); await withController( { options: { - addDetectedTokens: mockAddDetectedTokens, disabled: false, getBalancesInSingleCall: mockGetBalancesInSingleCall, networkClientId: NetworkType.mainnet, selectedAddress, }, - messenger, }, - async ({ mockTokenListGetState }) => { + async ({ + mockTokenListGetState, + mockAddDetectedTokens, + triggerNetworkDidChange, + }) => { mockTokenListGetState({ ...getDefaultTokenListState(), tokenList: { @@ -848,13 +899,15 @@ describe('TokenDetectionController', () => { }, }); - messenger.publish('NetworkController:networkDidChange', { + triggerNetworkDidChange({ ...defaultNetworkState, selectedNetworkClientId: 'goerli', }); await advanceTime({ clock, duration: 1 }); - expect(mockAddDetectedTokens).not.toHaveBeenCalled(); + expect(mockAddDetectedTokens).not.toHaveBeenCalledWith( + 'TokensController:addDetectedTokens', + ); }, ); }); @@ -863,24 +916,21 @@ describe('TokenDetectionController', () => { const mockGetBalancesInSingleCall = jest.fn().mockResolvedValue({ [sampleTokenA.address]: new BN(1), }); - const mockAddDetectedTokens = jest.fn(); const selectedAddress = '0x0000000000000000000000000000000000000001'; - const messenger = new ControllerMessenger< - AllowedActions, - AllowedEvents - >(); await withController( { options: { - addDetectedTokens: mockAddDetectedTokens, disabled: false, getBalancesInSingleCall: mockGetBalancesInSingleCall, networkClientId: NetworkType.mainnet, selectedAddress, }, - messenger, }, - async ({ mockTokenListGetState }) => { + async ({ + mockTokenListGetState, + mockAddDetectedTokens, + triggerNetworkDidChange, + }) => { mockTokenListGetState({ ...getDefaultTokenListState(), tokenList: { @@ -896,13 +946,15 @@ describe('TokenDetectionController', () => { }, }); - messenger.publish('NetworkController:networkDidChange', { + triggerNetworkDidChange({ ...defaultNetworkState, selectedNetworkClientId: 'mainnet', }); await advanceTime({ clock, duration: 1 }); - expect(mockAddDetectedTokens).not.toHaveBeenCalled(); + expect(mockAddDetectedTokens).not.toHaveBeenCalledWith( + 'TokensController:addDetectedTokens', + ); }, ); }); @@ -913,24 +965,21 @@ describe('TokenDetectionController', () => { const mockGetBalancesInSingleCall = jest.fn().mockResolvedValue({ [sampleTokenA.address]: new BN(1), }); - const mockAddDetectedTokens = jest.fn(); const selectedAddress = '0x0000000000000000000000000000000000000001'; - const messenger = new ControllerMessenger< - AllowedActions, - AllowedEvents - >(); await withController( { options: { - addDetectedTokens: mockAddDetectedTokens, disabled: true, getBalancesInSingleCall: mockGetBalancesInSingleCall, networkClientId: NetworkType.mainnet, selectedAddress, }, - messenger, }, - async ({ mockTokenListGetState }) => { + async ({ + mockTokenListGetState, + mockAddDetectedTokens, + triggerNetworkDidChange, + }) => { mockTokenListGetState({ ...getDefaultTokenListState(), tokenList: { @@ -946,13 +995,15 @@ describe('TokenDetectionController', () => { }, }); - messenger.publish('NetworkController:networkDidChange', { + triggerNetworkDidChange({ ...defaultNetworkState, selectedNetworkClientId: 'polygon', }); await advanceTime({ clock, duration: 1 }); - expect(mockAddDetectedTokens).not.toHaveBeenCalled(); + expect(mockAddDetectedTokens).not.toHaveBeenCalledWith( + 'TokensController:addDetectedTokens', + ); }, ); }); @@ -974,24 +1025,21 @@ describe('TokenDetectionController', () => { const mockGetBalancesInSingleCall = jest.fn().mockResolvedValue({ [sampleTokenA.address]: new BN(1), }); - const mockAddDetectedTokens = jest.fn(); const selectedAddress = '0x0000000000000000000000000000000000000001'; - const messenger = new ControllerMessenger< - AllowedActions, - AllowedEvents - >(); await withController( { options: { - addDetectedTokens: mockAddDetectedTokens, disabled: false, getBalancesInSingleCall: mockGetBalancesInSingleCall, networkClientId: NetworkType.mainnet, selectedAddress, }, - messenger, }, - async ({ mockTokenListGetState }) => { + async ({ + mockTokenListGetState, + mockAddDetectedTokens, + triggerTokenListStateChange, + }) => { const tokenListState = { ...getDefaultTokenListState(), tokenList: { @@ -1008,17 +1056,17 @@ describe('TokenDetectionController', () => { }; mockTokenListGetState(tokenListState); - messenger.publish( - 'TokenListController:stateChange', - tokenListState, - [], - ); + triggerTokenListStateChange(tokenListState); await advanceTime({ clock, duration: 1 }); - expect(mockAddDetectedTokens).toHaveBeenCalledWith([sampleTokenA], { - chainId: ChainId.mainnet, - selectedAddress, - }); + expect(mockAddDetectedTokens).toHaveBeenCalledWith( + 'TokensController:addDetectedTokens', + [sampleTokenA], + { + chainId: ChainId.mainnet, + selectedAddress, + }, + ); }, ); }); @@ -1027,38 +1075,33 @@ describe('TokenDetectionController', () => { const mockGetBalancesInSingleCall = jest.fn().mockResolvedValue({ [sampleTokenA.address]: new BN(1), }); - const mockAddDetectedTokens = jest.fn(); const selectedAddress = '0x0000000000000000000000000000000000000001'; - const messenger = new ControllerMessenger< - AllowedActions, - AllowedEvents - >(); await withController( { options: { - addDetectedTokens: mockAddDetectedTokens, disabled: false, getBalancesInSingleCall: mockGetBalancesInSingleCall, networkClientId: NetworkType.mainnet, selectedAddress, }, - messenger, }, - async ({ mockTokenListGetState }) => { + async ({ + mockTokenListGetState, + mockAddDetectedTokens, + triggerTokenListStateChange, + }) => { const tokenListState = { ...getDefaultTokenListState(), tokenList: {}, }; mockTokenListGetState(tokenListState); - messenger.publish( - 'TokenListController:stateChange', - tokenListState, - [], - ); + triggerTokenListStateChange(tokenListState); await advanceTime({ clock, duration: 1 }); - expect(mockAddDetectedTokens).not.toHaveBeenCalled(); + expect(mockAddDetectedTokens).not.toHaveBeenCalledWith( + 'TokensController:addDetectedTokens', + ); }, ); }); @@ -1069,24 +1112,21 @@ describe('TokenDetectionController', () => { const mockGetBalancesInSingleCall = jest.fn().mockResolvedValue({ [sampleTokenA.address]: new BN(1), }); - const mockAddDetectedTokens = jest.fn(); const selectedAddress = '0x0000000000000000000000000000000000000001'; - const messenger = new ControllerMessenger< - AllowedActions, - AllowedEvents - >(); await withController( { options: { - addDetectedTokens: mockAddDetectedTokens, disabled: true, getBalancesInSingleCall: mockGetBalancesInSingleCall, networkClientId: NetworkType.mainnet, selectedAddress, }, - messenger, }, - async ({ mockTokenListGetState }) => { + async ({ + mockTokenListGetState, + mockAddDetectedTokens, + triggerTokenListStateChange, + }) => { const tokenListState = { ...getDefaultTokenListState(), tokenList: { @@ -1103,14 +1143,12 @@ describe('TokenDetectionController', () => { }; mockTokenListGetState(tokenListState); - messenger.publish( - 'TokenListController:stateChange', - tokenListState, - [], - ); + triggerTokenListStateChange(tokenListState); await advanceTime({ clock, duration: 1 }); - expect(mockAddDetectedTokens).not.toHaveBeenCalled(); + expect(mockAddDetectedTokens).not.toHaveBeenCalledWith( + 'TokensController:addDetectedTokens', + ); }, ); }); @@ -1131,22 +1169,15 @@ describe('TokenDetectionController', () => { const mockGetBalancesInSingleCall = jest.fn().mockResolvedValue({ [sampleTokenA.address]: new BN(1), }); - const mockAddDetectedTokens = jest.fn(); const selectedAddress = '0x0000000000000000000000000000000000000001'; - const messenger = new ControllerMessenger< - AllowedActions, - AllowedEvents - >(); await withController( { options: { - addDetectedTokens: mockAddDetectedTokens, disabled: false, getBalancesInSingleCall: mockGetBalancesInSingleCall, networkClientId: NetworkType.mainnet, selectedAddress, }, - messenger, }, async ({ controller, mockTokenListGetState }) => { mockTokenListGetState({ @@ -1205,24 +1236,21 @@ describe('TokenDetectionController', () => { const mockGetBalancesInSingleCall = jest.fn().mockResolvedValue({ [sampleTokenA.address]: new BN(1), }); - const mockAddDetectedTokens = jest.fn(); const selectedAddress = '0x0000000000000000000000000000000000000001'; - const messenger = new ControllerMessenger< - AllowedActions, - AllowedEvents - >(); await withController( { options: { - addDetectedTokens: mockAddDetectedTokens, disabled: false, getBalancesInSingleCall: mockGetBalancesInSingleCall, networkClientId: NetworkType.mainnet, selectedAddress, }, - messenger, }, - async ({ controller, mockTokenListGetState }) => { + async ({ + controller, + mockTokenListGetState, + mockAddDetectedTokens, + }) => { mockTokenListGetState({ ...getDefaultTokenListState(), tokenList: { @@ -1243,10 +1271,14 @@ describe('TokenDetectionController', () => { accountAddress: selectedAddress, }); - expect(mockAddDetectedTokens).toHaveBeenCalledWith([sampleTokenA], { - chainId: ChainId.mainnet, - selectedAddress, - }); + expect(mockAddDetectedTokens).toHaveBeenCalledWith( + 'TokensController:addDetectedTokens', + [sampleTokenA], + { + chainId: ChainId.mainnet, + selectedAddress, + }, + ); }, ); }); @@ -1268,8 +1300,10 @@ function getTokensPath(chainId: Hex) { type WithControllerCallback = ({ controller, mockKeyringGetState, + mockTokensGetState, mockTokenListGetState, mockPreferencesGetState, + mockAddDetectedTokens, triggerKeyringUnlock, triggerKeyringLock, triggerTokenListStateChange, @@ -1279,11 +1313,13 @@ type WithControllerCallback = ({ }: { controller: TokenDetectionController; mockKeyringGetState: (state: KeyringControllerState) => void; + mockTokensGetState: (state: TokensState) => void; mockTokenListGetState: (state: TokenListState) => void; mockPreferencesGetState: (state: PreferencesState) => void; mockGetNetworkConfigurationByNetworkClientId: ( handler: (networkClientId: string) => NetworkConfiguration, ) => void; + mockAddDetectedTokens: jest.SpyInstance; triggerKeyringUnlock: () => void; triggerKeyringLock: () => void; triggerTokenListStateChange: (state: TokenListState) => void; @@ -1337,6 +1373,11 @@ async function withController( }, ), ); + const mockTokensState = jest.fn(); + controllerMessenger.registerActionHandler( + 'TokensController:getState', + mockTokensState.mockReturnValue({ ...getDefaultTokensState() }), + ); const mockTokenListState = jest.fn(); controllerMessenger.registerActionHandler( 'TokenListController:getState', @@ -1350,11 +1391,11 @@ async function withController( }), ); + const mockAddDetectedTokens = jest.spyOn(controllerMessenger, 'call'); + const controller = new TokenDetectionController({ networkClientId: NetworkType.mainnet, getBalancesInSingleCall: jest.fn(), - addDetectedTokens: jest.fn(), - getTokensState: jest.fn().mockReturnValue(getDefaultTokensState()), trackMetaMetricsEvent: jest.fn(), messenger: buildTokenDetectionControllerMessenger(controllerMessenger), ...options, @@ -1368,6 +1409,15 @@ async function withController( mockPreferencesGetState: (state: PreferencesState) => { mockPreferencesState.mockReturnValue(state); }, + mockTokensGetState: (state: TokensState) => { + controllerMessenger.unregisterActionHandler( + 'TokensController:getState', + ); + controllerMessenger.registerActionHandler( + 'TokensController:getState', + mockTokensState.mockReturnValue(state), + ); + }, mockTokenListGetState: (state: TokenListState) => { mockTokenListState.mockReturnValue(state); }, @@ -1378,6 +1428,7 @@ async function withController( handler, ); }, + mockAddDetectedTokens, triggerKeyringUnlock: () => { controllerMessenger.publish('KeyringController:unlock'); }, From 0979215c3600a09a265c9d18655943740d161d49 Mon Sep 17 00:00:00 2001 From: Jongsun Suh Date: Sun, 21 Jan 2024 15:57:51 -0500 Subject: [PATCH 15/28] [token-balances-controller] Rename and export `getDefaultTokenBalancesState` --- packages/assets-controllers/src/TokenBalancesController.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/assets-controllers/src/TokenBalancesController.ts b/packages/assets-controllers/src/TokenBalancesController.ts index 82d94b57a82..493c2c87c65 100644 --- a/packages/assets-controllers/src/TokenBalancesController.ts +++ b/packages/assets-controllers/src/TokenBalancesController.ts @@ -82,7 +82,7 @@ export type TokenBalancesControllerMessenger = RestrictedControllerMessenger< * * @returns The default TokenBalancesController state. */ -function getDefaultState(): TokenBalancesControllerState { +export function getDefaultTokenBalancesState(): TokenBalancesControllerState { return { contractBalances: {}, }; @@ -137,7 +137,7 @@ export class TokenBalancesController extends BaseController< metadata, messenger, state: { - ...getDefaultState(), + ...getDefaultTokenBalancesState(), ...state, }, }); From c14d91ddedd77f8a300776f8eba884b138e59d1b Mon Sep 17 00:00:00 2001 From: Jongsun Suh Date: Sun, 21 Jan 2024 16:00:35 -0500 Subject: [PATCH 16/28] [token-balances-controller] Replace tokens-controller, preferences-controller callbacks with messenger actions/events. - `getSelectedAddress` replaced with `PreferencesController:getState` action - `onTokensStateChange` replaced with `TokensController:stateChange` event --- .../src/TokenBalancesController.ts | 48 ++++++++----------- 1 file changed, 21 insertions(+), 27 deletions(-) diff --git a/packages/assets-controllers/src/TokenBalancesController.ts b/packages/assets-controllers/src/TokenBalancesController.ts index 493c2c87c65..1acc2f226cd 100644 --- a/packages/assets-controllers/src/TokenBalancesController.ts +++ b/packages/assets-controllers/src/TokenBalancesController.ts @@ -5,11 +5,11 @@ import { BaseController, } from '@metamask/base-controller'; import { safelyExecute, toHex } from '@metamask/controller-utils'; -import type { PreferencesState } from '@metamask/preferences-controller'; +import type { PreferencesControllerGetStateAction } from '@metamask/preferences-controller'; import type { AssetsContractController } from './AssetsContractController'; import type { Token } from './TokenRatesController'; -import type { TokensState } from './TokensController'; +import type { TokensControllerStateChangeEvent } from './TokensController'; const DEFAULT_INTERVAL = 180000; @@ -24,16 +24,12 @@ const metadata = { * @property interval - Polling interval used to fetch new token balances. * @property tokens - List of tokens to track balances for. * @property disabled - If set to true, all tracked tokens contract balances updates are blocked. - * @property onTokensStateChange - Allows subscribing to assets controller state changes. - * @property getSelectedAddress - Gets the current selected address. * @property getERC20BalanceOf - Gets the balance of the given account at the given contract address. */ type TokenBalancesControllerOptions = { interval?: number; tokens?: Token[]; disabled?: boolean; - onTokensStateChange: (listener: (tokenState: TokensState) => void) => void; - getSelectedAddress: () => PreferencesState['selectedAddress']; getERC20BalanceOf: AssetsContractController['getERC20BalanceOf']; messenger: TokenBalancesControllerMessenger; state?: Partial; @@ -60,6 +56,8 @@ export type TokenBalancesControllerGetStateAction = ControllerGetStateAction< export type TokenBalancesControllerActions = TokenBalancesControllerGetStateAction; +export type AllowedActions = PreferencesControllerGetStateAction; + export type TokenBalancesControllerStateChangeEvent = ControllerStateChangeEvent< typeof controllerName, @@ -69,12 +67,14 @@ export type TokenBalancesControllerStateChangeEvent = export type TokenBalancesControllerEvents = TokenBalancesControllerStateChangeEvent; +export type AllowedEvents = TokensControllerStateChangeEvent; + export type TokenBalancesControllerMessenger = RestrictedControllerMessenger< typeof controllerName, - TokenBalancesControllerActions, - TokenBalancesControllerEvents, - never, - never + TokenBalancesControllerActions | AllowedActions, + TokenBalancesControllerEvents | AllowedEvents, + AllowedActions['type'], + AllowedEvents['type'] >; /** @@ -99,8 +99,6 @@ export class TokenBalancesController extends BaseController< > { #handle?: ReturnType; - #getSelectedAddress: () => PreferencesState['selectedAddress']; - #getERC20BalanceOf: AssetsContractController['getERC20BalanceOf']; #interval: number; @@ -116,8 +114,6 @@ export class TokenBalancesController extends BaseController< * @param options.interval - Polling interval used to fetch new token balances. * @param options.tokens - List of tokens to track balances for. * @param options.disabled - If set to true, all tracked tokens contract balances updates are blocked. - * @param options.onTokensStateChange - Allows subscribing to assets controller state changes. - * @param options.getSelectedAddress - Gets the current selected address. * @param options.getERC20BalanceOf - Gets the balance of the given account at the given contract address. * @param options.state - Initial state to set on this controller. * @param options.messenger - The controller restricted messenger. @@ -126,8 +122,6 @@ export class TokenBalancesController extends BaseController< interval = DEFAULT_INTERVAL, tokens = [], disabled = false, - onTokensStateChange, - getSelectedAddress, getERC20BalanceOf, messenger, state = {}, @@ -146,22 +140,19 @@ export class TokenBalancesController extends BaseController< this.#interval = interval; this.#tokens = tokens; - onTokensStateChange(this.#tokensStateChangeListener.bind(this)); + this.messagingSystem.subscribe( + 'TokensController:stateChange', + ({ tokens: newTokens, detectedTokens }) => { + this.#tokens = [...newTokens, ...detectedTokens]; + this.updateBalances(); + }, + ); - this.#getSelectedAddress = getSelectedAddress; this.#getERC20BalanceOf = getERC20BalanceOf; this.poll(); } - /* - * Tokens state changes listener. - */ - #tokensStateChangeListener({ tokens, detectedTokens }: TokensState) { - this.#tokens = [...tokens, ...detectedTokens]; - this.updateBalances(); - } - /** * Allows controller to update tracked tokens contract balances. */ @@ -208,9 +199,12 @@ export class TokenBalancesController extends BaseController< const newContractBalances: ContractBalances = {}; for (const token of this.#tokens) { const { address } = token; + const { selectedAddress } = this.messagingSystem.call( + 'PreferencesController:getState', + ); try { newContractBalances[address] = toHex( - await this.#getERC20BalanceOf(address, this.#getSelectedAddress()), + await this.#getERC20BalanceOf(address, selectedAddress), ); token.balanceError = null; } catch (error) { From e6cf266c47a29e64a8d4a7153b1c2a601fac4e0f Mon Sep 17 00:00:00 2001 From: Jongsun Suh Date: Sun, 21 Jan 2024 16:08:39 -0500 Subject: [PATCH 17/28] [token-balances-controller] test: Use `PreferencesController:getState` action, `TokensController:stateChange` event --- .../src/TokenBalancesController.test.ts | 170 +++++++++--------- 1 file changed, 88 insertions(+), 82 deletions(-) diff --git a/packages/assets-controllers/src/TokenBalancesController.test.ts b/packages/assets-controllers/src/TokenBalancesController.test.ts index 2cc40bf0383..0797292ccb5 100644 --- a/packages/assets-controllers/src/TokenBalancesController.test.ts +++ b/packages/assets-controllers/src/TokenBalancesController.test.ts @@ -1,15 +1,14 @@ import { ControllerMessenger } from '@metamask/base-controller'; import { toHex } from '@metamask/controller-utils'; -import type { - NetworkControllerActions, - NetworkControllerEvents, -} from '@metamask/network-controller'; -import {} from '@metamask/network-controller'; import { BN } from 'ethereumjs-util'; import { flushPromises } from '../../../tests/helpers'; +import type { + AllowedActions, + AllowedEvents, + TokenBalancesControllerMessenger, +} from './TokenBalancesController'; import { TokenBalancesController } from './TokenBalancesController'; -import type { TokenListStateChange } from './TokenListController'; import type { Token } from './TokenRatesController'; import { getDefaultTokensState, type TokensState } from './TokensController'; @@ -18,48 +17,54 @@ const controllerName = 'TokenBalancesController'; /** * Constructs a restricted controller messenger. * + * @param controllerMessenger - The controller messenger to restrict. * @returns A restricted controller messenger. */ -function getMessenger() { - return new ControllerMessenger().getRestricted< - typeof controllerName, - never, - never - >({ +function getMessenger( + controllerMessenger = new ControllerMessenger< + AllowedActions, + AllowedEvents + >(), +): TokenBalancesControllerMessenger { + return controllerMessenger.getRestricted({ name: controllerName, + allowedActions: ['PreferencesController:getState'], + allowedEvents: ['TokensController:stateChange'], }); } describe('TokenBalancesController', () => { - let controllerMessenger: ControllerMessenger< - NetworkControllerActions, - NetworkControllerEvents | TokenListStateChange - >; + let controllerMessenger: ControllerMessenger; + let messenger: TokenBalancesControllerMessenger; beforeEach(() => { jest.useFakeTimers(); controllerMessenger = new ControllerMessenger(); + messenger = getMessenger(controllerMessenger); }); afterEach(() => { jest.useRealTimers(); - controllerMessenger.clearEventSubscriptions( - 'NetworkController:networkDidChange', - ); }); it('should set default state', () => { + controllerMessenger.registerActionHandler( + 'PreferencesController:getState', + jest.fn().mockReturnValue({ selectedAddress: '0x1234' }), + ); const controller = new TokenBalancesController({ - onTokensStateChange: jest.fn(), - getSelectedAddress: () => '0x1234', getERC20BalanceOf: jest.fn(), - messenger: getMessenger(), + messenger, }); expect(controller.state).toStrictEqual({ contractBalances: {} }); }); it('should poll and update balances in the right interval', async () => { + controllerMessenger.registerActionHandler( + 'PreferencesController:getState', + jest.fn().mockReturnValue({ selectedAddress: '0x1234' }), + ); const updateBalancesSpy = jest.spyOn( TokenBalancesController.prototype, 'updateBalances', @@ -67,10 +72,8 @@ describe('TokenBalancesController', () => { new TokenBalancesController({ interval: 10, - onTokensStateChange: jest.fn(), - getSelectedAddress: () => '0x1234', getERC20BalanceOf: jest.fn(), - messenger: getMessenger(), + messenger, }); await flushPromises(); @@ -84,14 +87,16 @@ describe('TokenBalancesController', () => { it('should update balances if enabled', async () => { const address = '0x86fa049857e0209aa7d9e616f7eb3b3b78ecfdb0'; + controllerMessenger.registerActionHandler( + 'PreferencesController:getState', + jest.fn().mockReturnValue({ selectedAddress: '0x1234' }), + ); const controller = new TokenBalancesController({ disabled: false, tokens: [{ address, decimals: 18, symbol: 'EOS', aggregators: [] }], interval: 10, - onTokensStateChange: jest.fn(), - getSelectedAddress: () => '0x1234', getERC20BalanceOf: jest.fn().mockReturnValue(new BN(1)), - messenger: getMessenger(), + messenger, }); await controller.updateBalances(); @@ -103,14 +108,16 @@ describe('TokenBalancesController', () => { it('should not update balances if disabled', async () => { const address = '0x86fa049857e0209aa7d9e616f7eb3b3b78ecfdb0'; + controllerMessenger.registerActionHandler( + 'PreferencesController:getState', + jest.fn().mockReturnValue({ selectedAddress: '0x1234' }), + ); const controller = new TokenBalancesController({ disabled: true, tokens: [{ address, decimals: 18, symbol: 'EOS', aggregators: [] }], interval: 10, - onTokensStateChange: jest.fn(), - getSelectedAddress: () => '0x1234', getERC20BalanceOf: jest.fn().mockReturnValue(new BN(1)), - messenger: getMessenger(), + messenger, }); await controller.updateBalances(); @@ -120,14 +127,16 @@ describe('TokenBalancesController', () => { it('should update balances if controller is manually enabled', async () => { const address = '0x86fa049857e0209aa7d9e616f7eb3b3b78ecfdb0'; + controllerMessenger.registerActionHandler( + 'PreferencesController:getState', + jest.fn().mockReturnValue({ selectedAddress: '0x1234' }), + ); const controller = new TokenBalancesController({ disabled: true, tokens: [{ address, decimals: 18, symbol: 'EOS', aggregators: [] }], interval: 10, - onTokensStateChange: jest.fn(), - getSelectedAddress: () => '0x1234', getERC20BalanceOf: jest.fn().mockReturnValue(new BN(1)), - messenger: getMessenger(), + messenger, }); await controller.updateBalances(); @@ -144,14 +153,16 @@ describe('TokenBalancesController', () => { it('should not update balances if controller is manually disabled', async () => { const address = '0x86fa049857e0209aa7d9e616f7eb3b3b78ecfdb0'; + controllerMessenger.registerActionHandler( + 'PreferencesController:getState', + jest.fn().mockReturnValue({ selectedAddress: '0x1234' }), + ); const controller = new TokenBalancesController({ disabled: false, tokens: [{ address, decimals: 18, symbol: 'EOS', aggregators: [] }], interval: 10, - onTokensStateChange: jest.fn(), - getSelectedAddress: () => '0x1234', getERC20BalanceOf: jest.fn().mockReturnValue(new BN(1)), - messenger: getMessenger(), + messenger, }); await controller.updateBalances(); @@ -169,23 +180,20 @@ describe('TokenBalancesController', () => { }); it('should update balances if tokens change and controller is manually enabled', async () => { - const tokensStateChangeListeners: ((state: TokensState) => void)[] = []; const address = '0x86fa049857e0209aa7d9e616f7eb3b3b78ecfdb0'; + controllerMessenger.registerActionHandler( + 'PreferencesController:getState', + jest.fn().mockReturnValue({ selectedAddress: '0x1234' }), + ); const controller = new TokenBalancesController({ disabled: true, tokens: [{ address, decimals: 18, symbol: 'EOS', aggregators: [] }], interval: 10, - getSelectedAddress: () => '0x1234', getERC20BalanceOf: jest.fn().mockReturnValue(new BN(1)), - onTokensStateChange: (listener) => { - tokensStateChangeListeners.push(listener); - }, - messenger: getMessenger(), + messenger, }); const triggerTokensStateChange = async (state: TokensState) => { - for (const listener of tokensStateChangeListeners) { - listener(state); - } + controllerMessenger.publish('TokensController:stateChange', state, []); }; await controller.updateBalances(); @@ -210,23 +218,20 @@ describe('TokenBalancesController', () => { }); it('should not update balances if tokens change and controller is manually disabled', async () => { - const tokensStateChangeListeners: ((state: TokensState) => void)[] = []; const address = '0x86fa049857e0209aa7d9e616f7eb3b3b78ecfdb0'; + controllerMessenger.registerActionHandler( + 'PreferencesController:getState', + jest.fn().mockReturnValue({ selectedAddress: '0x1234' }), + ); const controller = new TokenBalancesController({ disabled: false, tokens: [{ address, decimals: 18, symbol: 'EOS', aggregators: [] }], interval: 10, - getSelectedAddress: () => '0x1234', getERC20BalanceOf: jest.fn().mockReturnValue(new BN(1)), - onTokensStateChange: (listener) => { - tokensStateChangeListeners.push(listener); - }, - messenger: getMessenger(), + messenger, }); const triggerTokensStateChange = async (state: TokensState) => { - for (const listener of tokensStateChangeListeners) { - listener(state); - } + controllerMessenger.publish('TokensController:stateChange', state, []); }; await controller.updateBalances(); @@ -253,12 +258,14 @@ describe('TokenBalancesController', () => { }); it('should clear previous interval', async () => { + controllerMessenger.registerActionHandler( + 'PreferencesController:getState', + jest.fn().mockReturnValue({ selectedAddress: '0x1234' }), + ); const controller = new TokenBalancesController({ interval: 1337, - onTokensStateChange: jest.fn(), - getSelectedAddress: () => '0x1234', getERC20BalanceOf: jest.fn(), - messenger: getMessenger(), + messenger, }); const mockClearTimeout = jest.spyOn(global, 'clearTimeout'); @@ -281,13 +288,15 @@ describe('TokenBalancesController', () => { aggregators: [], }, ]; + controllerMessenger.registerActionHandler( + 'PreferencesController:getState', + jest.fn().mockReturnValue({ selectedAddress }), + ); const controller = new TokenBalancesController({ interval: 1337, tokens, - onTokensStateChange: jest.fn(), - getSelectedAddress: () => selectedAddress, getERC20BalanceOf: jest.fn().mockReturnValue(new BN(1)), - messenger: getMessenger(), + messenger, }); expect(controller.state.contractBalances).toStrictEqual({}); @@ -313,13 +322,16 @@ describe('TokenBalancesController', () => { aggregators: [], }, ]; + + controllerMessenger.registerActionHandler( + 'PreferencesController:getState', + jest.fn().mockReturnValue({}), + ); const controller = new TokenBalancesController({ interval: 1337, tokens, - onTokensStateChange: jest.fn(), - getSelectedAddress: jest.fn(), getERC20BalanceOf: getERC20BalanceOfStub, - messenger: getMessenger(), + messenger, }); expect(controller.state.contractBalances).toStrictEqual({}); @@ -340,20 +352,17 @@ describe('TokenBalancesController', () => { }); it('should update balances when tokens change', async () => { - const tokensStateChangeListeners: ((state: TokensState) => void)[] = []; + controllerMessenger.registerActionHandler( + 'PreferencesController:getState', + jest.fn().mockReturnValue({ selectedAddress: '0x1234' }), + ); const controller = new TokenBalancesController({ - onTokensStateChange: (listener) => { - tokensStateChangeListeners.push(listener); - }, - getSelectedAddress: jest.fn(), getERC20BalanceOf: jest.fn(), interval: 1337, - messenger: getMessenger(), + messenger, }); const triggerTokensStateChange = async (state: TokensState) => { - for (const listener of tokensStateChangeListeners) { - listener(state); - } + controllerMessenger.publish('TokensController:stateChange', state, []); }; const updateBalancesSpy = jest.spyOn(controller, 'updateBalances'); @@ -372,20 +381,17 @@ describe('TokenBalancesController', () => { }); it('should update token balances when detected tokens are added', async () => { - const tokensStateChangeListeners: ((state: TokensState) => void)[] = []; + controllerMessenger.registerActionHandler( + 'PreferencesController:getState', + jest.fn().mockReturnValue({ selectedAddress: '0x1234' }), + ); const controller = new TokenBalancesController({ interval: 1337, - onTokensStateChange: (listener) => { - tokensStateChangeListeners.push(listener); - }, - getSelectedAddress: () => '0x1234', getERC20BalanceOf: jest.fn().mockReturnValue(new BN(1)), - messenger: getMessenger(), + messenger, }); const triggerTokensStateChange = async (state: TokensState) => { - for (const listener of tokensStateChangeListeners) { - listener(state); - } + controllerMessenger.publish('TokensController:stateChange', state, []); }; expect(controller.state.contractBalances).toStrictEqual({}); From 232235f0fc71bb9346847b5214fabdc2a5c29f9a Mon Sep 17 00:00:00 2001 From: Jongsun Suh Date: Sun, 21 Jan 2024 16:40:58 -0500 Subject: [PATCH 18/28] [token-list-controller,token-rates-controller] Enumerate package-level exports --- packages/assets-controllers/src/index.ts | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/packages/assets-controllers/src/index.ts b/packages/assets-controllers/src/index.ts index 79510584bdb..242498de3e5 100644 --- a/packages/assets-controllers/src/index.ts +++ b/packages/assets-controllers/src/index.ts @@ -19,8 +19,24 @@ export type { TokenDetectionControllerStateChangeEvent, } from './TokenDetectionController'; export { TokenDetectionController } from './TokenDetectionController'; -export * from './TokenListController'; -export * from './TokenRatesController'; +export type { + TokenListState, + TokenListToken, + TokenListMap, + TokenListStateChange, + TokenListControllerEvents, + GetTokenListState, + TokenListControllerActions, + TokenListControllerMessenger, +} from './TokenListController'; +export { TokenListController } from './TokenListController'; +export type { + Token, + TokenRatesConfig, + ContractExchangeRates, + TokenRatesState, +} from './TokenRatesController'; +export { TokenRatesController } from './TokenRatesController'; export type { TokensConfig, TokensState, From a262be2421bec7d4eb5c2673ba31d4f739b6b662 Mon Sep 17 00:00:00 2001 From: Jongsun Suh Date: Sun, 21 Jan 2024 16:42:21 -0500 Subject: [PATCH 19/28] Add changelog entries --- packages/assets-controllers/CHANGELOG.md | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/packages/assets-controllers/CHANGELOG.md b/packages/assets-controllers/CHANGELOG.md index 2f879da7c79..7b9c5488e21 100644 --- a/packages/assets-controllers/CHANGELOG.md +++ b/packages/assets-controllers/CHANGELOG.md @@ -26,6 +26,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - **BREAKING:** `TokenDetectionController` constructor no longer accepts options `onPreferencesStateChange`, `getPreferencesState`. ([#3775](https://github.com/MetaMask/core/pull/3775/)) - **BREAKING:** `TokenDetectionController` no longer allows the `NetworkController:stateChange` event. The `NetworkController:networkDidChange` event can be used instead. ([#3775](https://github.com/MetaMask/core/pull/3775/)) +- **BREAKING:** `TokensController` constructor no longer accepts options `onPreferencesStateChange`, `onNetworkDidChange`, `onTokenListStateChange`, `getNetworkClientById`. ([#3690](https://github.com/MetaMask/core/pull/3690/)) +- **BREAKING:** `TokenBalancesController` constructor no longer accepts options `onTokensStateChange`, `getSelectedAddress`. ([#3690](https://github.com/MetaMask/core/pull/3690/)) ## [25.0.0] @@ -136,8 +138,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - These are needed for the new "polling by `networkClientId`" feature - **BREAKING:** `AccountTrackerController` has a new required state property, `accountByChainId`([#3586](https://github.com/MetaMask/core/pull/3586)) - This is needed to track balances accross chains. It was introduced for the "polling by `networkClientId`" feature, but is useful on its own as well. -- **BREAKING**: `AccountTrackerController` adds a mutex to `refresh` making it only possible for one call to be executed at time ([#3586](https://github.com/MetaMask/core/pull/3586)) -- **BREAKING**: `TokensController.watchAsset` now performs on-chain validation of the asset's symbol and decimals, if they're defined in the contract ([#1745](https://github.com/MetaMask/core/pull/1745)) +- **BREAKING:** `AccountTrackerController` adds a mutex to `refresh` making it only possible for one call to be executed at time ([#3586](https://github.com/MetaMask/core/pull/3586)) +- **BREAKING:** `TokensController.watchAsset` now performs on-chain validation of the asset's symbol and decimals, if they're defined in the contract ([#1745](https://github.com/MetaMask/core/pull/1745)) - The `TokensController` constructor no longer accepts a `getERC20TokenName` option. It was no longer needed due to this change. - Add new method `_getProvider`, though this is intended for internal use and should not be called externally. - Additionally, if the symbol and decimals are defined in the contract, they are no longer required to be passed to `watchAsset` @@ -168,10 +170,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - This method was previously used in TokenRatesController to access the CoinGecko API. There is no equivalent. - **BREAKING:** Remove `CoinGeckoResponse` and `CoinGeckoPlatform` types ([#3600](https://github.com/MetaMask/core/pull/3600)) - These types were previously used in TokenRatesController to represent data returned from the CoinGecko API. There is no equivalent. -- **BREAKING**: The TokenRatesController now only supports updating and polling rates for tokens tracked by the TokensController ([#3639](https://github.com/MetaMask/core/pull/3639)) +- **BREAKING:** The TokenRatesController now only supports updating and polling rates for tokens tracked by the TokensController ([#3639](https://github.com/MetaMask/core/pull/3639)) - The `tokenAddresses` option has been removed from `startPollingByNetworkClientId` - The `tokenContractAddresses` option has been removed from `updateExchangeRatesByChainId` -- **BREAKING**: `TokenRatesController.fetchAndMapExchangeRates` is no longer exposed publicly ([#3621](https://github.com/MetaMask/core/pull/3621)) +- **BREAKING:** `TokenRatesController.fetchAndMapExchangeRates` is no longer exposed publicly ([#3621](https://github.com/MetaMask/core/pull/3621)) ### Fixed @@ -255,7 +257,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ``` - **BREAKING**: `CurrencyRateController` now extends `PollingController` ([#1805](https://github.com/MetaMask/core/pull/1805)) - `start()` and `stop()` methods replaced with `startPollingByNetworkClientId()`, `stopPollingByPollingToken()`, and `stopAllPolling()` -- **BREAKING**: `CurrencyRateController` now sends the `NetworkController:getNetworkClientById` action via messaging controller ([#1805](https://github.com/MetaMask/core/pull/1805)) +- **BREAKING:** `CurrencyRateController` now sends the `NetworkController:getNetworkClientById` action via messaging controller ([#1805](https://github.com/MetaMask/core/pull/1805)) ### Fixed @@ -360,7 +362,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 networkClientId?: NetworkClientId; } ``` -- **BREAKING**: Bump peer dependency on `@metamask/network-controller` to ^13.0.0 ([#1633](https://github.com/MetaMask/core/pull/1633)) +- **BREAKING:** Bump peer dependency on `@metamask/network-controller` to ^13.0.0 ([#1633](https://github.com/MetaMask/core/pull/1633)) - **CHANGED**: `TokensController.addToken` will use the chain ID value derived from state for `networkClientId` if provided ([#1676](https://github.com/MetaMask/core/pull/1676)) - **CHANGED**: `TokensController.addTokens` now accepts an optional `networkClientId` as the last parameter ([#1676](https://github.com/MetaMask/core/pull/1676)) - **CHANGED**: `TokensController.addTokens` will use the chain ID value derived from state for `networkClientId` if provided ([#1676](https://github.com/MetaMask/core/pull/1676)) @@ -425,13 +427,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - **BREAKING**: New required constructor parameters for the `TokenRatesController` ([#1497](https://github.com/MetaMask/core/pull/1497), [#1511](https://github.com/MetaMask/core/pull/1511)) - The new required parameters are `ticker`, `onSelectedAddress`, and `onPreferencesStateChange` -- **BREAKING**: Remove `onCurrencyRateStateChange` constructor parameter from `TokenRatesController` ([#1496](https://github.com/MetaMask/core/pull/1496)) -- **BREAKING**: Disable `TokenRatesController` automatic polling ([#1501](https://github.com/MetaMask/core/pull/1501)) +- **BREAKING:** Remove `onCurrencyRateStateChange` constructor parameter from `TokenRatesController` ([#1496](https://github.com/MetaMask/core/pull/1496)) +- **BREAKING:** Disable `TokenRatesController` automatic polling ([#1501](https://github.com/MetaMask/core/pull/1501)) - Polling must be started explicitly by calling the `start` method - The token rates are not updated upon state changes when polling is disabled. -- **BREAKING**: Replace the `poll` method with `start` ([#1501](https://github.com/MetaMask/core/pull/1501)) +- **BREAKING:** Replace the `poll` method with `start` ([#1501](https://github.com/MetaMask/core/pull/1501)) - The `start` method does not offer a way to change the interval. That must be done by calling `.configure` instead -- **BREAKING**: Remove `TokenRatecontroller` setter for `chainId` and `tokens` properties ([#1505](https://github.com/MetaMask/core/pull/1505)) +- **BREAKING:** Remove `TokenRatecontroller` setter for `chainId` and `tokens` properties ([#1505](https://github.com/MetaMask/core/pull/1505)) - Bump @metamask/abi-utils from 1.2.0 to 2.0.1 ([#1525](https://github.com/MetaMask/core/pull/1525)) - Update `@metamask/utils` to `^6.2.0` ([#1514](https://github.com/MetaMask/core/pull/1514)) - Remove unnecessary `babel-runtime` dependency ([#1504](https://github.com/MetaMask/core/pull/1504)) @@ -520,7 +522,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - The tokens controller `addDetectedTokens` method now accepts the `chainId` property of the `detectionDetails` parameter to be of type `Hex` rather than decimal `string`. - The tokens controller state properties `allTokens`, `allIgnoredTokens`, and `allDetectedTokens` are now keyed by chain ID in `Hex` format rather than decimal `string`. - This requires a state migration -- **BREAKING**: Use approval controller for suggested assets ([#1261](https://github.com/MetaMask/core/pull/1261), [#1268](https://github.com/MetaMask/core/pull/1268)) +- **BREAKING:** Use approval controller for suggested assets ([#1261](https://github.com/MetaMask/core/pull/1261), [#1268](https://github.com/MetaMask/core/pull/1268)) - The actions `ApprovalController:acceptRequest` and `ApprovalController:rejectRequest` are no longer required by the token controller messenger. - The `suggestedAssets` state has been removed, which means that suggested assets are no longer persisted in state - The return type for `watchAsset` has changed. It now returns a Promise that settles after the request has been confirmed or rejected. From e4234a512f8f6ad83db55af359bc0e41fd90d7b0 Mon Sep 17 00:00:00 2001 From: Jongsun Suh Date: Fri, 26 Jan 2024 18:20:23 -0500 Subject: [PATCH 20/28] Remove `BaseState` from `TokensState` --- .../src/TokensController.ts | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/packages/assets-controllers/src/TokensController.ts b/packages/assets-controllers/src/TokensController.ts index 3f33d864fea..02153e952de 100644 --- a/packages/assets-controllers/src/TokensController.ts +++ b/packages/assets-controllers/src/TokensController.ts @@ -94,15 +94,14 @@ type SuggestedAssetMeta = { * @property allIgnoredTokens - Object containing hidden/ignored tokens by network and account * @property allDetectedTokens - Object containing tokens detected with non-zero balances */ -export type TokensState = BaseState & - Record & { - tokens: Token[]; - ignoredTokens: string[]; - detectedTokens: Token[]; - allTokens: { [chainId: Hex]: { [key: string]: Token[] } }; - allIgnoredTokens: { [chainId: Hex]: { [key: string]: string[] } }; - allDetectedTokens: { [chainId: Hex]: { [key: string]: Token[] } }; - }; +export type TokensState = { + tokens: Token[]; + ignoredTokens: string[]; + detectedTokens: Token[]; + allTokens: { [chainId: Hex]: { [key: string]: Token[] } }; + allIgnoredTokens: { [chainId: Hex]: { [key: string]: string[] } }; + allDetectedTokens: { [chainId: Hex]: { [key: string]: Token[] } }; +}; /** * The name of the {@link TokensController}. @@ -169,7 +168,7 @@ export const getDefaultTokensState = (): TokensState => { */ export class TokensController extends BaseControllerV1< TokensConfig, - TokensState + TokensState & BaseState > { private readonly mutex = new Mutex(); From 9d46c9b07fa4d0a37b6b35871365621a0e6d7d5b Mon Sep 17 00:00:00 2001 From: Jongsun Suh Date: Mon, 29 Jan 2024 12:02:35 -0500 Subject: [PATCH 21/28] test: initialize `getNetworkClientByIdHandler` in `beforeEach` --- .../assets-controllers/src/TokensController.test.ts | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/packages/assets-controllers/src/TokensController.test.ts b/packages/assets-controllers/src/TokensController.test.ts index 3ce62668419..f55a87aea49 100644 --- a/packages/assets-controllers/src/TokensController.test.ts +++ b/packages/assets-controllers/src/TokensController.test.ts @@ -95,6 +95,10 @@ describe('TokensController', () => { >; let tokensControllerMessenger; let approvalControllerMessenger; + let getNetworkClientByIdHandler: jest.Mock< + ReturnType, + Parameters + >; const changeNetwork = (providerConfig: ProviderConfig) => { messenger.publish(`NetworkController:networkDidChange`, { @@ -107,11 +111,6 @@ describe('TokensController', () => { messenger.publish('PreferencesController:stateChange', state, []); }; - const getNetworkClientByIdHandler = jest.fn< - ReturnType, - Parameters - >(); - beforeEach(async () => { const defaultSelectedAddress = '0x1'; messenger = new ControllerMessenger(); @@ -151,6 +150,7 @@ describe('TokensController', () => { typesExcludedFromRateLimiting: [ApprovalType.WatchAsset], }); + getNetworkClientByIdHandler = jest.fn(); messenger.registerActionHandler( `NetworkController:getNetworkClientById`, getNetworkClientByIdHandler.mockReturnValue( @@ -163,7 +163,6 @@ describe('TokensController', () => { afterEach(() => { sinon.restore(); - messenger.unregisterActionHandler(`NetworkController:getNetworkClientById`); }); it('should set default state', () => { From 6c85cc8f5b7a753ed12c917fea1240d73f8cb375 Mon Sep 17 00:00:00 2001 From: Jongsun Suh Date: Mon, 29 Jan 2024 12:03:19 -0500 Subject: [PATCH 22/28] test: move `addRequestHandler` initialization to individual test --- packages/assets-controllers/src/TokensController.test.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/assets-controllers/src/TokensController.test.ts b/packages/assets-controllers/src/TokensController.test.ts index f55a87aea49..3c7bb5eb49b 100644 --- a/packages/assets-controllers/src/TokensController.test.ts +++ b/packages/assets-controllers/src/TokensController.test.ts @@ -1236,8 +1236,6 @@ describe('TokensController', () => { .spyOn(ERC20Standard.prototype as any, 'getTokenDecimals') .mockImplementationOnce(() => a.decimals?.toString()); }); - - let addRequestHandler: jest.Mock; let createEthersStub: sinon.SinonStub; beforeEach(function () { type = ERC20; @@ -1248,7 +1246,6 @@ describe('TokensController', () => { image: 'image', name: undefined, }; - addRequestHandler = jest.fn(); isERC721 = false; isERC1155 = false; @@ -1592,6 +1589,7 @@ describe('TokensController', () => { }), ); + const addRequestHandler = jest.fn(); messenger.unregisterActionHandler(`ApprovalController:addRequest`); messenger.registerActionHandler( `ApprovalController:addRequest`, From 3e731d6c491a08fcd23ac20373d35e0337ae6db0 Mon Sep 17 00:00:00 2001 From: Jongsun Suh Date: Mon, 29 Jan 2024 12:03:39 -0500 Subject: [PATCH 23/28] test: rename `registerListeners` to `promiseForApprovals` --- packages/assets-controllers/src/TokensController.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/assets-controllers/src/TokensController.test.ts b/packages/assets-controllers/src/TokensController.test.ts index 3c7bb5eb49b..bdc8c6a7c9d 100644 --- a/packages/assets-controllers/src/TokensController.test.ts +++ b/packages/assets-controllers/src/TokensController.test.ts @@ -1700,7 +1700,7 @@ describe('TokensController', () => { mockContract([asset, anotherAsset]); - const registerListeners = new Promise((resolve) => { + const promiseForApprovals = new Promise((resolve) => { const listener = (state: ApprovalControllerState) => { if (state.pendingApprovalCount === 2) { messenger.unsubscribe('ApprovalController:stateChange', listener); @@ -1720,7 +1720,7 @@ describe('TokensController', () => { interactingAddress, }); - await registerListeners; + await promiseForApprovals; await approvalController.accept(requestId); await approvalController.accept('67890'); From 5f82082a3e65e95d95a2c83252c670b57bb3765f Mon Sep 17 00:00:00 2001 From: Jongsun Suh Date: Fri, 2 Feb 2024 16:45:08 -0500 Subject: [PATCH 24/28] Update packages/assets-controllers/CHANGELOG.md Co-authored-by: Elliot Winkler --- packages/assets-controllers/CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/assets-controllers/CHANGELOG.md b/packages/assets-controllers/CHANGELOG.md index 7b9c5488e21..68ce22a7d73 100644 --- a/packages/assets-controllers/CHANGELOG.md +++ b/packages/assets-controllers/CHANGELOG.md @@ -21,6 +21,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - **BREAKING:** The `detectTokens` method now excludes tokens that are already included in the `TokensController`'s `detectedTokens` list from the batch of incoming tokens it sends to the `TokensController` `addDetectedTokens` method. - **BREAKING:** The constructor for `TokenDetectionController` expects a new required proprerty `trackMetaMetricsEvent`, which defines the callback that is called in the `detectTokens` method. - **BREAKING:** In Mainnet, even if the `PreferenceController`'s `useTokenDetection` option is set to false, automatic token detection is performed on the legacy token list (token data from the contract-metadata repo). + - **BREAKING:** The `TokensState` type is now defined as a type alias rather than an interface. ([#3690](https://github.com/MetaMask/core/pull/3690/)) + - This is breaking because it could affect how this type is used with other types, such as `Json`, which does not support TypeScript interfaces. ### Removed From a1f87ccf70599be2fa3fdec20a35264cd3cfa72e Mon Sep 17 00:00:00 2001 From: Jongsun Suh Date: Mon, 5 Feb 2024 10:05:23 -0500 Subject: [PATCH 25/28] [token-detection-controller] test: remove unnecessary `as unknown as` cast --- .../assets-controllers/src/TokenDetectionController.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/assets-controllers/src/TokenDetectionController.test.ts b/packages/assets-controllers/src/TokenDetectionController.test.ts index 6e4f2cd0661..c000f10f5bc 100644 --- a/packages/assets-controllers/src/TokenDetectionController.test.ts +++ b/packages/assets-controllers/src/TokenDetectionController.test.ts @@ -1359,7 +1359,7 @@ async function withController( 'KeyringController:getState', mockKeyringState.mockReturnValue({ isUnlocked: true, - } as unknown as KeyringControllerState), + } as KeyringControllerState), ); const mockGetNetworkConfigurationByNetworkClientId = jest.fn< ReturnType, From 654579dd5b7d3969b587a8d63e78ff130404b739 Mon Sep 17 00:00:00 2001 From: Jongsun Suh Date: Tue, 6 Feb 2024 16:49:37 -0500 Subject: [PATCH 26/28] [token-detection-controller] Fix `detectTokens` to remove redundant `TokensController:getState` calls for `ignoredTokens` --- .../src/TokenDetectionController.ts | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/packages/assets-controllers/src/TokenDetectionController.ts b/packages/assets-controllers/src/TokenDetectionController.ts index c2edecce98f..e150840b33f 100644 --- a/packages/assets-controllers/src/TokenDetectionController.ts +++ b/packages/assets-controllers/src/TokenDetectionController.ts @@ -453,7 +453,7 @@ export class TokenDetectionController extends StaticIntervalPollingController< ? STATIC_MAINNET_TOKEN_LIST : tokenList; - const { tokens, detectedTokens } = this.messagingSystem.call( + const { tokens, detectedTokens, ignoredTokens } = this.messagingSystem.call( 'TokensController:getState', ); const tokensToDetect: string[] = []; @@ -495,13 +495,9 @@ export class TokenDetectionController extends StaticIntervalPollingController< tokensSlice, ); const tokensToAdd: Token[] = []; - const eventTokensDetails = []; + const eventTokensDetails: string[] = []; + let ignored; for (const tokenAddress of Object.keys(balances)) { - let ignored; - /* istanbul ignore else */ - const { ignoredTokens } = this.messagingSystem.call( - 'TokensController:getState', - ); if (ignoredTokens.length) { ignored = ignoredTokens.find( (ignoredTokenAddress) => @@ -516,7 +512,7 @@ export class TokenDetectionController extends StaticIntervalPollingController< if (ignored === undefined) { const { decimals, symbol, aggregators, iconUrl, name } = - tokenList[caseInsensitiveTokenKey]; + tokenListUsed[caseInsensitiveTokenKey]; eventTokensDetails.push(`${symbol} - ${tokenAddress}`); tokensToAdd.push({ address: tokenAddress, From 2108233ae1f4c1e1b9cec2f907e08ae8e6da4463 Mon Sep 17 00:00:00 2001 From: Jongsun Suh Date: Tue, 6 Feb 2024 16:50:02 -0500 Subject: [PATCH 27/28] [token-detection-controller] Fix typing for `STATIC_MAINNET_TOKEN_LIST` --- .../assets-controllers/src/TokenDetectionController.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/packages/assets-controllers/src/TokenDetectionController.ts b/packages/assets-controllers/src/TokenDetectionController.ts index e150840b33f..10bc7dd74c6 100644 --- a/packages/assets-controllers/src/TokenDetectionController.ts +++ b/packages/assets-controllers/src/TokenDetectionController.ts @@ -64,7 +64,12 @@ type LegacyToken = Omit< export const STATIC_MAINNET_TOKEN_LIST = Object.entries( contractMap, -).reduce>>((acc, [base, contract]) => { +).reduce< + Record< + string, + Partial & Pick + > +>((acc, [base, contract]) => { const { logo, ...tokenMetadata } = contract; return { ...acc, From 09cac7f1e3393b275a0785d0a2e0f012974e0d70 Mon Sep 17 00:00:00 2001 From: Jongsun Suh Date: Thu, 8 Feb 2024 16:35:01 -0500 Subject: [PATCH 28/28] Squashed commit of the following: commit 5f9f474d2211de4306ae2507d193d854f3090561 Merge: 2108233ae a3863017c Author: Jongsun Suh Date: Thu Feb 8 16:28:06 2024 -0500 Merge remote-tracking branch 'origin/231220-TokensController-v2-migration' into 231220-TokensController-v2-migration commit a3863017c058ce071d36ce7ba09dc46dd3bef25d Merge: 5200f854f 582b370ec Author: Jongsun Suh Date: Thu Feb 8 13:09:06 2024 -0500 Merge branch '230113-consolidate-TokenDetectionController-DetectTokensController' into 231220-TokensController-v2-migration commit 582b370ec95cf6785c492f1543e9cbadd11410d1 Merge: 4a944d887 065fd912f Author: Jongsun Suh Date: Thu Feb 8 13:08:19 2024 -0500 Merge branch 'main' into 230113-consolidate-TokenDetectionController-DetectTokensController commit 5200f854f933a809d59f32299ac9a34a7416c685 Merge: 68999bed4 4a944d887 Author: Jongsun Suh Date: Wed Feb 7 13:54:44 2024 -0500 Merge branch '230113-consolidate-TokenDetectionController-DetectTokensController' into 231220-TokensController-v2-migration commit 4a944d887c1c95f9e9bb003760237de609aefdbf Author: Jongsun Suh Date: Wed Feb 7 13:44:59 2024 -0500 Apply suggestions to CHANGELOG Co-authored-by: Elliot Winkler commit 68999bed480b67becf9c90d37e5ff76d7021a46c Merge: 11da214f5 e01f9933f Author: Jongsun Suh Date: Tue Feb 6 17:58:58 2024 -0500 Merge branch '230113-consolidate-TokenDetectionController-DetectTokensController' into 231220-TokensController-v2-migration commit e01f9933fc81b5c9b12461e30a33292cd1b8021e Author: Jongsun Suh Date: Tue Feb 6 17:56:39 2024 -0500 test: adjust coverage thresholds commit 11da214f5cf173d15b3a657353f89b6ae96071d5 Author: Jongsun Suh Date: Tue Feb 6 17:54:53 2024 -0500 test: adjust coverage thresholds commit 01cf7967c948244a6a95d16b72c3511dd3ecae5e Merge: d501062c5 88eb7cac7 Author: Jongsun Suh Date: Tue Feb 6 17:53:49 2024 -0500 Merge branch '230113-consolidate-TokenDetectionController-DetectTokensController' into 231220-TokensController-v2-migration commit d501062c585dc3716c45b541369e0f3e63f10853 Author: Jongsun Suh Date: Tue Feb 6 16:50:02 2024 -0500 [token-detection-controller] Fix typing for `STATIC_MAINNET_TOKEN_LIST` commit 8556d7af546cf9f0aa19c107f7160fd73bdde487 Author: Jongsun Suh Date: Tue Feb 6 16:49:37 2024 -0500 [token-detection-controller] Fix `detectTokens` to remove redundant `TokensController:getState` calls for `ignoredTokens` commit 88eb7cac766db2fea4b5f3f3047d637a96902f2d Author: Jongsun Suh Date: Tue Feb 6 15:31:40 2024 -0500 Replace `detectTokens` with `#restartTokenDetection` in `networkDidChange` listener commit 91be38550f3cd9c9f16054e39849bbff170d44a0 Author: Jongsun Suh Date: Tue Feb 6 09:19:18 2024 -0500 Include `#stopPolling()` call in `KeyringController:lock` listener commit 9a4ae931a761342460e91c4ee9ee2efc3ea7e30f Author: Jongsun Suh Date: Wed Jan 31 10:28:32 2024 -0500 In changelog, move new actions/events under "Added" heading commit 312e450a7a25627d0d266c65c4fd90fd7d382969 Author: Jongsun Suh Date: Mon Jan 29 13:31:43 2024 -0500 In networkDidChange event listener, detect tokens if `networkClientId` is changed instead of `chainId`, and avoid resetting polling interval commit 04df2782bd706ad072369768610a36bed1c1e878 Author: Jongsun Suh Date: Fri Jan 26 16:24:46 2024 -0500 Changelog linter fix commit 907b4dfa0c1d6dcac18a279953061c6739d372da Author: Jongsun Suh Date: Fri Jan 26 11:02:38 2024 -0500 test: remove unnecessary action handler unregisters commit f1af71a398bde4cbf980b36e335f9e244f34f358 Author: Jongsun Suh Date: Fri Jan 26 10:51:50 2024 -0500 Use `networkClientId` instead of `chainId` in `#restartTokenDetection` commit 7c25f3c84946f3267442403548da438deb0ac505 Author: Jongsun Suh Date: Fri Jan 26 10:49:10 2024 -0500 Extract `findCaseInsensitiveMatch` helper function commit 2db0081b93fbe4c02f01e5a7d80f31440ca8c4cd Author: Jongsun Suh Date: Mon Jan 22 10:08:17 2024 -0500 Update packages/assets-controllers/CHANGELOG.md Update packages/assets-controllers/CHANGELOG.md commit 968c0c079d83909b79fb3b9f8ec3ee5b78a97e1f Author: Jongsun Suh Date: Mon Jan 22 10:05:04 2024 -0500 [assets-controllers] Bump `@metamask/kerying-api` to ^3.0.0 and remove yarn resolutions entry for `@metamask/providers` commit 2be2e244a3e4f91c2a2768ba5437d49c9b867385 Author: Jongsun Suh Date: Sun Jan 21 20:14:09 2024 -0500 Add CHANGELOG entries commit 1173eb95b8ac2a8a739adb793008faa6c5b89afe Author: Jongsun Suh Date: Fri Jan 19 21:39:47 2024 -0500 test: adjust for removal of `getPreferencesState` callback commit c90de3ecd24528a9c5c0013da9eaf0e2292f29f9 Author: Jongsun Suh Date: Fri Jan 19 21:38:58 2024 -0500 test: add callbacks for all allowed actions, events as `WithControllerCallback` arguments commit ad11ac7c384168dd2731b01c97fe541dc3c9bd5a Author: Jongsun Suh Date: Fri Jan 19 21:36:59 2024 -0500 test: adjust action,event allowlist in `buildTokenDetectionControllerMessenger` commit 4b303b4b8a0bd7e4aa936ae984638cc52f433db6 Author: Jongsun Suh Date: Fri Jan 19 21:32:46 2024 -0500 Minor refactors to event listeners for consistency commit beacf9b97b7a3ee8c484ea07093d2d6eb1da32a7 Author: Jongsun Suh Date: Fri Jan 19 21:31:23 2024 -0500 Replace preferences controller callbacks in constructor with `getState` action and `stateChange` event commit b5bd0e823cd750ace131c31692eaa20a42d5a1d3 Author: Jongsun Suh Date: Fri Jan 19 21:29:42 2024 -0500 Remove unused `NetworkControllerStateChange` event - **BREAKING**: use `NetworkControllerNetworkDidChangeEvent` instead commit 3fbdffab8cf64c0eb028745037996f1503bccfa3 Author: Jongsun Suh Date: Fri Jan 19 21:25:37 2024 -0500 Add `@metamask/keyring-api` as devDep (for `InternalAccount` type in tests) commit a44ccf72e108404d1599be2dd85de8d344234dc3 Author: Jongsun Suh Date: Thu Jan 18 20:31:40 2024 -0500 Add yarn resolutions entry to resolve conflicting `@metamask/providers` versions used by `@metamask/accounts-controller` commit 7016835516bdb72600597acf62a93d4738b82036 Author: Jongsun Suh Date: Wed Jan 17 17:04:04 2024 -0500 Define `#registerEventListeners` constructor helper private method commit adc8dca3397742b3c01b5d5154286a5f7fae9ae7 Author: Jongsun Suh Date: Wed Jan 17 16:09:45 2024 -0500 Make `#isUnlocked` property non-optional commit 78e9fde10578da301ea9566b64902c6175a1f9c7 Author: Jongsun Suh Date: Tue Jan 16 19:56:10 2024 -0500 test: adjust coverage thresholds commit 3fd21385bdc0a68d4fb6ab8285bcea17d7c62884 Author: Jongsun Suh Date: Tue Jan 16 19:39:20 2024 -0500 test: Fix incorrect mock for `KeyringController:getState` commit 5df6b8101d4a7c705abf10ffc315279c890c41bb Author: Jongsun Suh Date: Fri Jan 12 14:33:07 2024 -0500 test: add actions, events, callbacks to test messenger commit 33818aa5f7501b2c925cea3db862a97aef356ddf Author: Jongsun Suh Date: Thu Jan 11 18:53:02 2024 -0500 Exclude `detectedTokens` already stored in tokens-controller state from `tokensToDetect` commit 129c82eeefde33fd52097e5e880d58eab4dc1a7c Author: Jongsun Suh Date: Thu Jan 11 18:46:43 2024 -0500 Use legacy token list from contract-metadata repo to perform auto-detection if active network is mainnet and token detection is disabled in preferences commit 0f46fdcaf9c3270518fb4a500b266e47fb7b7dab Author: Jongsun Suh Date: Thu Jan 11 11:35:16 2024 -0500 Add `#trackMetaMetricsEvent` tracker property and call commit 2485341ce04784046434fb00efbf23b850dbee28 Author: Jongsun Suh Date: Thu Jan 11 11:28:33 2024 -0500 Define `#registerKeyringListeners`, `isActive` methods commit 8e204e67fd6207ca3a2744bf030e1b8d8d9b458c Author: Jongsun Suh Date: Thu Jan 11 10:56:20 2024 -0500 Subscribe to `AccountsController:selectedAccountChange` event commit ea9602512ca67caa1ece1e72c29069805f5c69b3 Author: Jongsun Suh Date: Thu Jan 11 10:54:47 2024 -0500 Define and apply `#restartTokenDetection` method commit 2d31ccecb8584366913deda26d6fb701ed6556c2 Author: Jongsun Suh Date: Thu Jan 11 10:48:41 2024 -0500 [assets-controllers] Install `{accounts,keyring}-controller` as deps and peerDeps commit 6fde16167ff647f7cdabe35047d598213783bdcc Author: Jongsun Suh Date: Mon Feb 5 10:05:23 2024 -0500 [token-detection-controller] test: remove unnecessary `as unknown as` cast commit e504d2e0674851ff82679f67bbc75896cffce8a6 Author: Jongsun Suh Date: Fri Feb 2 16:45:08 2024 -0500 Update packages/assets-controllers/CHANGELOG.md Co-authored-by: Elliot Winkler commit 129fda6628ca89c06d74e6c34d10e5e07146bd1c Merge: 1d7a580de 5efcc1ea5 Author: Jongsun Suh Date: Wed Jan 31 10:30:13 2024 -0500 Merge branch '230113-consolidate-TokenDetectionController-DetectTokensController' into 231220-TokensController-v2-migration commit 5efcc1ea5944174228604af859c2db331f744f1c Author: Jongsun Suh Date: Wed Jan 31 10:28:32 2024 -0500 In changelog, move new actions/events under "Added" heading commit 1d7a580dee60ece978fc6d24b1cf094dafb593e3 Author: Jongsun Suh Date: Mon Jan 29 15:25:17 2024 -0500 test: adjust coverage thresholds commit f149b296623e6a4b67207b45ff427105a6408741 Merge: 1b7d9a50b ed7473212 Author: Jongsun Suh Date: Mon Jan 29 13:38:56 2024 -0500 Merge branch '230113-consolidate-TokenDetectionController-DetectTokensController' into 231220-TokensController-v2-migration commit ed7473212958384a266ebec6d10097795e555e09 Merge: aed885a16 8c09b1609 Author: Jongsun Suh Date: Mon Jan 29 13:31:47 2024 -0500 Merge branch 'main' into 230113-consolidate-TokenDetectionController-DetectTokensController commit aed885a169537987db3e2b86a6a6712638e060a6 Author: Jongsun Suh Date: Mon Jan 29 13:31:43 2024 -0500 In networkDidChange event listener, detect tokens if `networkClientId` is changed instead of `chainId`, and avoid resetting polling interval commit 1b7d9a50b35d19a9ec663f54cb1d76181980498b Author: Jongsun Suh Date: Mon Jan 29 12:03:39 2024 -0500 test: rename `registerListeners` to `promiseForApprovals` commit 41f4f1de42b29b822c1adca2095db8b5c2a70fdc Author: Jongsun Suh Date: Mon Jan 29 12:03:19 2024 -0500 test: move `addRequestHandler` initialization to individual test commit 1c8617990656cb6cfa4a0a98dd2242f43b08a98d Author: Jongsun Suh Date: Mon Jan 29 12:02:35 2024 -0500 test: initialize `getNetworkClientByIdHandler` in `beforeEach` commit a66fd2635ded676ce10f4af490da9a39ea92a72d Author: Jongsun Suh Date: Mon Jan 29 12:01:48 2024 -0500 test: remove unnecessary action handler unregisters commit 25fe76d8c2cd8ffaca3a05fc22f301ede73f15c9 Author: Jongsun Suh Date: Fri Jan 26 18:20:23 2024 -0500 Remove `BaseState` from `TokensState` commit c8f9a719a5f43a69326e02ab55becd50ab1b48dd Author: Jongsun Suh Date: Fri Jan 26 16:59:59 2024 -0500 Linter fix commit d4a65de2b65a15e82912e7dd8349088a533848e2 Merge: 2575c724e ca19f99c8 Author: Jongsun Suh Date: Fri Jan 26 16:59:13 2024 -0500 Merge branch '230113-consolidate-TokenDetectionController-DetectTokensController' into 231220-TokensController-v2-migration commit ca19f99c830012d8323834b2286751d353cdc9bf Author: Jongsun Suh Date: Fri Jan 26 16:24:46 2024 -0500 Changelog linter fix commit b49a545064d317b1a93ca2f77d50fc123f313637 Author: Jongsun Suh Date: Fri Jan 26 11:02:38 2024 -0500 test: remove unnecessary action handler unregisters commit 571c3b2ceb14b607e5dcff27417dd59db2a7f81f Author: Jongsun Suh Date: Fri Jan 26 10:51:50 2024 -0500 Use `networkClientId` instead of `chainId` in `#restartTokenDetection` commit 8eff112bae4774e6e801ceed6fa63e57e681a6ce Author: Jongsun Suh Date: Fri Jan 26 10:49:10 2024 -0500 Extract `findCaseInsensitiveMatch` helper function commit 6df8aa4c52850a3dfa22c2825cee0eb33c96ada1 Author: Jongsun Suh Date: Mon Jan 22 10:08:17 2024 -0500 Update packages/assets-controllers/CHANGELOG.md Update packages/assets-controllers/CHANGELOG.md commit d77bce095c25de990275a373f91625d9c2f86973 Author: Jongsun Suh Date: Mon Jan 22 10:05:04 2024 -0500 [assets-controllers] Bump `@metamask/kerying-api` to ^3.0.0 and remove yarn resolutions entry for `@metamask/providers` commit c434e8f648da5c7a006bce175c8a9d7e72f16f74 Author: Jongsun Suh Date: Sun Jan 21 20:14:09 2024 -0500 Add CHANGELOG entries commit 392140e7039be20b2ec5371d5585b303687c9c6b Author: Jongsun Suh Date: Fri Jan 19 21:39:47 2024 -0500 test: adjust for removal of `getPreferencesState` callback commit 169554f4205a76e57d1b58110f7317f0ab2617f4 Author: Jongsun Suh Date: Fri Jan 19 21:38:58 2024 -0500 test: add callbacks for all allowed actions, events as `WithControllerCallback` arguments commit fc9b6317af65d282620c404d1e882ca01791adf4 Author: Jongsun Suh Date: Fri Jan 19 21:36:59 2024 -0500 test: adjust action,event allowlist in `buildTokenDetectionControllerMessenger` commit 99d35f3af11ff0bf3c6373a54db7562ee04605f0 Author: Jongsun Suh Date: Fri Jan 19 21:32:46 2024 -0500 Minor refactors to event listeners for consistency commit 3d74e54d621942d6fd93b2c49431b0b2dee95af2 Author: Jongsun Suh Date: Fri Jan 19 21:31:23 2024 -0500 Replace preferences controller callbacks in constructor with `getState` action and `stateChange` event commit b92cd73f762fbef7576b94926aab8a9a6d9078f5 Author: Jongsun Suh Date: Fri Jan 19 21:29:42 2024 -0500 Remove unused `NetworkControllerStateChange` event - **BREAKING**: use `NetworkControllerNetworkDidChangeEvent` instead commit e947207fc533669bf7e861a331a54c8a6068cd4d Author: Jongsun Suh Date: Fri Jan 19 21:25:37 2024 -0500 Add `@metamask/keyring-api` as devDep (for `InternalAccount` type in tests) commit f4360322fdb47ba8d998575334fc6a56d858a56d Author: Jongsun Suh Date: Thu Jan 18 20:31:40 2024 -0500 Add yarn resolutions entry to resolve conflicting `@metamask/providers` versions used by `@metamask/accounts-controller` commit d19f5d6b367ebda1b8b38c9aa763ab62b6346c39 Author: Jongsun Suh Date: Wed Jan 17 17:04:04 2024 -0500 Define `#registerEventListeners` constructor helper private method commit 1270acab72c5bb8138aae46d08798c5e72e3b6fb Author: Jongsun Suh Date: Wed Jan 17 16:09:45 2024 -0500 Make `#isUnlocked` property non-optional commit f6c2ef086fea0c356fdb5c996fa42d76a5861ec0 Author: Jongsun Suh Date: Tue Jan 16 19:56:10 2024 -0500 test: adjust coverage thresholds commit 41fee270f11bd1df8720f716c5c711f0a3b14e01 Author: Jongsun Suh Date: Tue Jan 16 19:39:20 2024 -0500 test: Fix incorrect mock for `KeyringController:getState` commit a590dcca1619935b07613574d9b6fa79d603b772 Author: Jongsun Suh Date: Fri Jan 12 14:33:07 2024 -0500 test: add actions, events, callbacks to test messenger commit 6ba98de92f6e10a007687cf77f810d95748fc162 Author: Jongsun Suh Date: Thu Jan 11 18:53:02 2024 -0500 Exclude `detectedTokens` already stored in tokens-controller state from `tokensToDetect` commit 32465fd57be1ccecaceade0dee6938dc519400d5 Author: Jongsun Suh Date: Thu Jan 11 18:46:43 2024 -0500 Use legacy token list from contract-metadata repo to perform auto-detection if active network is mainnet and token detection is disabled in preferences commit 2673a3bf88b793c5e03052c10b3c6085616bbec2 Author: Jongsun Suh Date: Thu Jan 11 11:35:16 2024 -0500 Add `#trackMetaMetricsEvent` tracker property and call commit 0ded3cbafe087badbffe11a8edc777717346fbed Author: Jongsun Suh Date: Thu Jan 11 11:28:33 2024 -0500 Define `#registerKeyringListeners`, `isActive` methods commit f7718fd6288c586617d7d31bb7bc4e005927404d Author: Jongsun Suh Date: Thu Jan 11 10:56:20 2024 -0500 Subscribe to `AccountsController:selectedAccountChange` event commit 78e82e79e9ff0a2cabc8968bc6d3d596d2c80743 Author: Jongsun Suh Date: Thu Jan 11 10:54:47 2024 -0500 Define and apply `#restartTokenDetection` method commit 2d08c2d290b977c2c74ee3aabcc1eec85d1dfd10 Author: Jongsun Suh Date: Thu Jan 11 10:48:41 2024 -0500 [assets-controllers] Install `{accounts,keyring}-controller` as deps and peerDeps commit 2575c724eb9c9922f516c29e3a31bc0d378dd91f Author: Jongsun Suh Date: Wed Jan 24 12:04:03 2024 -0500 [assets-controllers] test: adjust coverage thresholds commit 9c61747b8f1278ad2d0e3b7aa99f68908f052910 Author: Jongsun Suh Date: Sun Jan 21 16:42:21 2024 -0500 Add changelog entries commit 0d50480135dcec717b1e008d165b197ae62df3b7 Author: Jongsun Suh Date: Sun Jan 21 16:40:58 2024 -0500 [token-list-controller,token-rates-controller] Enumerate package-level exports commit 669bb4f42c5a16d653194039f758c9b9691cb7e8 Author: Jongsun Suh Date: Sun Jan 21 16:08:39 2024 -0500 [token-balances-controller] test: Use `PreferencesController:getState` action, `TokensController:stateChange` event commit 12971a52e85d0e278de3661580f722054078a2f2 Author: Jongsun Suh Date: Sun Jan 21 16:00:35 2024 -0500 [token-balances-controller] Replace tokens-controller, preferences-controller callbacks with messenger actions/events. - `getSelectedAddress` replaced with `PreferencesController:getState` action - `onTokensStateChange` replaced with `TokensController:stateChange` event commit d1a303a88ed2fd85707ca8e803dd622422c08a12 Author: Jongsun Suh Date: Sun Jan 21 15:57:51 2024 -0500 [token-balances-controller] Rename and export `getDefaultTokenBalancesState` commit 1b0cf68156885d6f60b4a302246aa38b42637fc7 Author: Jongsun Suh Date: Sun Jan 21 15:56:15 2024 -0500 [token-detection-controller] test: Mock `TokensController:getState`, `TokensController:addDetectedTokens` messenger actions commit 83e87ad4a1afd666aaee75f42c46699eba76e0bf Author: Jongsun Suh Date: Sun Jan 21 15:54:00 2024 -0500 [token-detection-controller] Replace tokens-controller callbacks with `getState`, `addDetectedTokens` messenger actions commit bf87104bb2006038a70c3f7bd5fc6f420623540b Author: Jongsun Suh Date: Sun Jan 21 15:52:38 2024 -0500 test: Use `PreferencesController:stateChange` event instead of `onPreferencesStateChange` callback commit f230718496927fd64d8dc4d5836ec930129f3b81 Author: Jongsun Suh Date: Sun Jan 21 15:51:25 2024 -0500 Expose `addDetectedTokens` action commit 5aed7e010967f7bb20e79e30f8eee351175a6302 Author: Jongsun Suh Date: Sun Jan 21 15:48:31 2024 -0500 Replace `onPreferencesStateChange` callback with messenger event commit 5b8fb07a9e9f71983cdfc1d7dc0c80f901efd27a Author: Jongsun Suh Date: Thu Jan 18 15:33:43 2024 -0500 test: Adjust tests to updates in tokens-controller commit dccfe623ca3770a3dedaa6cb78e2b3f1e38d1ebf Author: Jongsun Suh Date: Thu Jan 18 11:56:47 2024 -0500 Add `TokensControllerGetStateAction`, `TokensControllerActions` types commit 48551b91fbfa730047e29a183db6a8d95b4b114f Author: Jongsun Suh Date: Thu Jan 18 11:56:28 2024 -0500 Fix typing for `TokensState` commit 4d65ee6a09c8c89ada4553540cb3698c56984c00 Author: Jongsun Suh Date: Tue Dec 19 14:42:48 2023 -0800 [token-detection-controller] test: Restore publishes for `networkDidChange` event, mock polygon network client commit 56813f4ed9b5eb6e356e9a6b58f0f81293ad81cb Author: Jongsun Suh Date: Tue Dec 19 12:36:28 2023 -0800 Remove unnecessary `?` operator commit 1405a53729b7c9e84ef946992f808921ac087503 Author: Jongsun Suh Date: Tue Dec 19 12:36:04 2023 -0800 Use type assertion to mock `getNetworkClientById` return value commit e6c966388fb7203b3ffe9686496b913c31144419 Author: Jongsun Suh Date: Tue Dec 19 05:28:34 2023 -0800 test: Mock `NetworkController:getNetworkClientById` action handler commit 8039cb87541c13c12a1e95aa0b5732367d33ecf3 Author: Jongsun Suh Date: Tue Dec 19 05:27:52 2023 -0800 test: Mock `TokenListController:stateChange` event commit 77a853164c856a9e0955bbc3e3e1f25afa615196 Author: Jongsun Suh Date: Mon Dec 18 17:19:43 2023 -0800 [token-balances-controller] Adjust tests to updates in tokens-controller commit 2d58e1e4e02902ede324d87f0f763d270e46e382 Author: Jongsun Suh Date: Mon Dec 18 16:30:07 2023 -0800 Replace callbacks `onNetworkDidChange`, `onTokenListStateChange`, `getNetworkClientById` with messenger actions/events commit 8231533adf2bbf02e0876f9314c957e90599132a Author: Jongsun Suh Date: Mon Jan 22 10:08:17 2024 -0500 Update packages/assets-controllers/CHANGELOG.md Update packages/assets-controllers/CHANGELOG.md commit 396d0106a89edb2bebee15035b4c4dfba259568c Author: Jongsun Suh Date: Mon Jan 22 10:05:04 2024 -0500 [assets-controllers] Bump `@metamask/kerying-api` to ^3.0.0 and remove yarn resolutions entry for `@metamask/providers` commit 4097960f1de9d8429f29534ffe26afbc5d8ca280 Author: Jongsun Suh Date: Sun Jan 21 20:14:09 2024 -0500 Add CHANGELOG entries commit 524b1b9571c3e63cef904450e002bbfd38c4e5bf Author: Jongsun Suh Date: Fri Jan 19 21:39:47 2024 -0500 test: adjust for removal of `getPreferencesState` callback commit c08cadd2c63cf7b4957071133bbed09ec36ccb57 Author: Jongsun Suh Date: Fri Jan 19 21:38:58 2024 -0500 test: add callbacks for all allowed actions, events as `WithControllerCallback` arguments commit 324aa97999c630eaad9370fd2d3c51b85cb5393b Author: Jongsun Suh Date: Fri Jan 19 21:36:59 2024 -0500 test: adjust action,event allowlist in `buildTokenDetectionControllerMessenger` commit 412bb682e38d324e33f9adb71663aa6a6b4e0062 Author: Jongsun Suh Date: Fri Jan 19 21:32:46 2024 -0500 Minor refactors to event listeners for consistency commit f1dd4258f3d20ac363deb3d127b3664e4d4eb3b2 Author: Jongsun Suh Date: Fri Jan 19 21:31:23 2024 -0500 Replace preferences controller callbacks in constructor with `getState` action and `stateChange` event commit 7ec7246f65652367cdb1d8819137221001245fb6 Author: Jongsun Suh Date: Fri Jan 19 21:29:42 2024 -0500 Remove unused `NetworkControllerStateChange` event - **BREAKING**: use `NetworkControllerNetworkDidChangeEvent` instead commit f0ba7615ec2f26a06b900af03498e15b8e694d43 Author: Jongsun Suh Date: Fri Jan 19 21:25:37 2024 -0500 Add `@metamask/keyring-api` as devDep (for `InternalAccount` type in tests) commit 9354e699e4f6d7e32eab6241f872bcc92a8790bb Author: Jongsun Suh Date: Thu Jan 18 20:31:40 2024 -0500 Add yarn resolutions entry to resolve conflicting `@metamask/providers` versions used by `@metamask/accounts-controller` commit c6ba49afaedef2db9af34d28952028fcc8469fba Author: Jongsun Suh Date: Wed Jan 17 17:04:04 2024 -0500 Define `#registerEventListeners` constructor helper private method commit e217169634c21c0d60cf14be06613e24d1205257 Author: Jongsun Suh Date: Wed Jan 17 16:09:45 2024 -0500 Make `#isUnlocked` property non-optional commit 68533dd151c1b4c06189f9825ca051edda304b70 Author: Jongsun Suh Date: Tue Jan 16 19:56:10 2024 -0500 test: adjust coverage thresholds commit 2acc5a109c12b2239a4bcc1e5e0d3ce9abaec2f9 Author: Jongsun Suh Date: Tue Jan 16 19:39:20 2024 -0500 test: Fix incorrect mock for `KeyringController:getState` commit 284c9769ebfc7c1b59843c2a361701645efcda88 Author: Jongsun Suh Date: Fri Jan 12 14:33:07 2024 -0500 test: add actions, events, callbacks to test messenger commit d3448d0aff271e52272ec6c704deeb9cc55f53af Author: Jongsun Suh Date: Thu Jan 11 18:53:02 2024 -0500 Exclude `detectedTokens` already stored in tokens-controller state from `tokensToDetect` commit 1e9e2a393adc6b2637f067b9fa666a4c73fecc8d Author: Jongsun Suh Date: Thu Jan 11 18:46:43 2024 -0500 Use legacy token list from contract-metadata repo to perform auto-detection if active network is mainnet and token detection is disabled in preferences commit 48d733aec0788acf643bda997bd7b5c89801db9c Author: Jongsun Suh Date: Thu Jan 11 11:35:16 2024 -0500 Add `#trackMetaMetricsEvent` tracker property and call commit 3eb9a64854eee366c74ac8814fd99f2485f16c8d Author: Jongsun Suh Date: Thu Jan 11 11:28:33 2024 -0500 Define `#registerKeyringListeners`, `isActive` methods commit feb1ff9184fa6f61ea7b90f82923af71554dbcc1 Author: Jongsun Suh Date: Thu Jan 11 10:56:20 2024 -0500 Subscribe to `AccountsController:selectedAccountChange` event commit 3b987d1faad15b7731c7b836c13c53925374f91a Author: Jongsun Suh Date: Thu Jan 11 10:54:47 2024 -0500 Define and apply `#restartTokenDetection` method commit 26e461f51c73775db18074e064886d5ae828cecb Author: Jongsun Suh Date: Thu Jan 11 10:48:41 2024 -0500 [assets-controllers] Install `{accounts,keyring}-controller` as deps and peerDeps --- packages/assets-controllers/CHANGELOG.md | 3 +- packages/assets-controllers/jest.config.js | 6 +-- .../src/TokenDetectionController.test.ts | 13 ++--- .../src/TokenDetectionController.ts | 1 - .../src/TokensController.test.ts | 50 +++++++------------ 5 files changed, 25 insertions(+), 48 deletions(-) diff --git a/packages/assets-controllers/CHANGELOG.md b/packages/assets-controllers/CHANGELOG.md index 68ce22a7d73..9c4e2b57c0d 100644 --- a/packages/assets-controllers/CHANGELOG.md +++ b/packages/assets-controllers/CHANGELOG.md @@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - **BREAKING:** Adds `@metamask/accounts-controller` ^8.0.0 and `@metamask/keyring-controller` ^12.0.0 as dependencies and peer dependencies. ([#3775](https://github.com/MetaMask/core/pull/3775/)). - **BREAKING:** `TokenDetectionController` newly subscribes to the `PreferencesController:stateChange`, `AccountsController:selectedAccountChange`, `KeyringController:lock`, `KeyringController:unlock` events, and allows the `PreferencesController:getState` messenger action. ([#3775](https://github.com/MetaMask/core/pull/3775/)) +- `TokensController` now exports `TokensControllerActions`, `TokensControllerGetStateAction`, `TokensControllerAddDetectedTokensAction`, `TokensControllerEvents`, `TokensControllerStateChangeEvent`. ([#3690](https://github.com/MetaMask/core/pull/3690/)) ### Changed @@ -26,7 +27,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Removed -- **BREAKING:** `TokenDetectionController` constructor no longer accepts options `onPreferencesStateChange`, `getPreferencesState`. ([#3775](https://github.com/MetaMask/core/pull/3775/)) +- **BREAKING:** `TokenDetectionController` constructor no longer accepts options `onPreferencesStateChange`, `getPreferencesState`, `getTokensState`, `addDetectedTokens`. ([#3690](https://github.com/MetaMask/core/pull/3690/), [#3775](https://github.com/MetaMask/core/pull/3775/)) - **BREAKING:** `TokenDetectionController` no longer allows the `NetworkController:stateChange` event. The `NetworkController:networkDidChange` event can be used instead. ([#3775](https://github.com/MetaMask/core/pull/3775/)) - **BREAKING:** `TokensController` constructor no longer accepts options `onPreferencesStateChange`, `onNetworkDidChange`, `onTokenListStateChange`, `getNetworkClientById`. ([#3690](https://github.com/MetaMask/core/pull/3690/)) - **BREAKING:** `TokenBalancesController` constructor no longer accepts options `onTokensStateChange`, `getSelectedAddress`. ([#3690](https://github.com/MetaMask/core/pull/3690/)) diff --git a/packages/assets-controllers/jest.config.js b/packages/assets-controllers/jest.config.js index 34e91df6918..71baa6a1b61 100644 --- a/packages/assets-controllers/jest.config.js +++ b/packages/assets-controllers/jest.config.js @@ -17,10 +17,10 @@ module.exports = merge(baseConfig, { // An object that configures minimum threshold enforcement for coverage results coverageThreshold: { global: { - branches: 88.3, + branches: 88.22, functions: 95.32, - lines: 96.69, - statements: 96.7, + lines: 96.68, + statements: 96.68, }, }, diff --git a/packages/assets-controllers/src/TokenDetectionController.test.ts b/packages/assets-controllers/src/TokenDetectionController.test.ts index c000f10f5bc..ba6522cedbe 100644 --- a/packages/assets-controllers/src/TokenDetectionController.test.ts +++ b/packages/assets-controllers/src/TokenDetectionController.test.ts @@ -1390,7 +1390,6 @@ async function withController( ...getDefaultPreferencesState(), }), ); - const mockAddDetectedTokens = jest.spyOn(controllerMessenger, 'call'); const controller = new TokenDetectionController({ @@ -1406,18 +1405,12 @@ async function withController( mockKeyringGetState: (state: KeyringControllerState) => { mockKeyringState.mockReturnValue(state); }, + mockTokensGetState: (state: TokensState) => { + mockTokensState.mockReturnValue(state); + }, mockPreferencesGetState: (state: PreferencesState) => { mockPreferencesState.mockReturnValue(state); }, - mockTokensGetState: (state: TokensState) => { - controllerMessenger.unregisterActionHandler( - 'TokensController:getState', - ); - controllerMessenger.registerActionHandler( - 'TokensController:getState', - mockTokensState.mockReturnValue(state), - ); - }, mockTokenListGetState: (state: TokenListState) => { mockTokenListState.mockReturnValue(state); }, diff --git a/packages/assets-controllers/src/TokenDetectionController.ts b/packages/assets-controllers/src/TokenDetectionController.ts index 10bc7dd74c6..e570cce454f 100644 --- a/packages/assets-controllers/src/TokenDetectionController.ts +++ b/packages/assets-controllers/src/TokenDetectionController.ts @@ -462,7 +462,6 @@ export class TokenDetectionController extends StaticIntervalPollingController< 'TokensController:getState', ); const tokensToDetect: string[] = []; - for (const tokenAddress of Object.keys(tokenListUsed)) { if ( !findCaseInsensitiveMatch( diff --git a/packages/assets-controllers/src/TokensController.test.ts b/packages/assets-controllers/src/TokensController.test.ts index bdc8c6a7c9d..78c050f92f2 100644 --- a/packages/assets-controllers/src/TokensController.test.ts +++ b/packages/assets-controllers/src/TokensController.test.ts @@ -422,13 +422,9 @@ describe('TokensController', () => { it('should add token to the correct chainId when passed a networkClientId', async () => { const stub = stubCreateEthers(tokensController, () => false); - messenger.unregisterActionHandler(`NetworkController:getNetworkClientById`); - messenger.registerActionHandler( - `NetworkController:getNetworkClientById`, - getNetworkClientByIdHandler.mockReturnValue({ - configuration: { chainId: '0x5' }, - } as unknown as ReturnType), - ); + getNetworkClientByIdHandler.mockReturnValue({ + configuration: { chainId: '0x5' }, + } as unknown as ReturnType); await tokensController.addToken({ address: '0x01', symbol: 'bar', @@ -1111,15 +1107,9 @@ describe('TokensController', () => { }); it('should add tokens to the correct chainId when passed a networkClientId', async () => { - messenger.unregisterActionHandler( - `NetworkController:getNetworkClientById`, - ); - messenger.registerActionHandler( - `NetworkController:getNetworkClientById`, - getNetworkClientByIdHandler.mockReturnValue({ - configuration: { chainId: '0x5' }, - } as unknown as ReturnType), - ); + getNetworkClientByIdHandler.mockReturnValue({ + configuration: { chainId: '0x5' }, + } as unknown as ReturnType); const dummyTokens: Token[] = [ { address: '0x01', @@ -1571,23 +1561,17 @@ describe('TokensController', () => { }); it('stores token correctly when passed a networkClientId', async function () { - messenger.unregisterActionHandler( - `NetworkController:getNetworkClientById`, - ); - messenger.registerActionHandler( - `NetworkController:getNetworkClientById`, - getNetworkClientByIdHandler.mockImplementation((networkClientId) => { - expect(networkClientId).toBe('networkClientId1'); - return { - configuration: { chainId: '0x5' }, - provider: new FakeProvider({ - stubs: [], - }), - blockTracker: new FakeBlockTracker(), - destroy: jest.fn(), - } as unknown as ReturnType; - }), - ); + getNetworkClientByIdHandler.mockImplementation((networkClientId) => { + expect(networkClientId).toBe('networkClientId1'); + return { + configuration: { chainId: '0x5' }, + provider: new FakeProvider({ + stubs: [], + }), + blockTracker: new FakeBlockTracker(), + destroy: jest.fn(), + } as unknown as ReturnType; + }); const addRequestHandler = jest.fn(); messenger.unregisterActionHandler(`ApprovalController:addRequest`);