Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

[token-detection-controller] Apply recent DetectTokensController updates #3923

Merged
merged 18 commits into from
Feb 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
0fa46b3
Initialize `#selectedAddress` using `AccountsController:getSelectedAc…
MajorLift Feb 17, 2024
e40ae46
[detectTokens] Align variable names with extension `detectNewTokens` …
MajorLift Feb 17, 2024
a10e4e0
[detectTokens] Apply extension-side fix for getting correct `networkC…
MajorLift Feb 17, 2024
a26ea0f
[detectTokens] Apply and refactor fixes for detected tokens being add…
MajorLift Feb 17, 2024
99e01da
Replace `#getCorrectChainId` with `#getCorrectChainIdAndNetworkClientId`
MajorLift Feb 17, 2024
904e4b7
test: register action handlers for `getSelectedAccount`, `findNetwork…
MajorLift Feb 20, 2024
7324749
Fix erroneous reference to `tokenList`
MajorLift Feb 20, 2024
e2be829
Fix typo
MajorLift Feb 20, 2024
a3b5c55
Update changelog
MajorLift Feb 17, 2024
f452964
test: adjust coverage thresholds
MajorLift Feb 21, 2024
366e56c
test: Fix unmocked `ProviderConfig` for changed `chainId`
MajorLift Feb 21, 2024
857142b
Update packages/assets-controllers/CHANGELOG.md
MajorLift Feb 22, 2024
2b88d05
Rename `accoutAddress` option of `detectTokens` to `selectedAddress` …
MajorLift Feb 23, 2024
1379987
Update `this.#chainId` on `networkDidChange` event
MajorLift Feb 23, 2024
e9bbf1d
Remove reference to `this.#chainId` from `#getCorrectChainIdAndNetwor…
MajorLift Feb 23, 2024
3f9de16
Merge branch 'main' into 240215-TokenDetectionController-sync-with-ex…
MajorLift Feb 26, 2024
7d15225
Merge branch 'main' into 240215-TokenDetectionController-sync-with-ex…
MajorLift Feb 27, 2024
2b4a6f7
Merge branch 'main' into 240215-TokenDetectionController-sync-with-ex…
MajorLift Feb 27, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions packages/assets-controllers/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,19 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added

- **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/))
- **BREAKING:** `TokenDetectionController` newly subscribes to the `PreferencesController:stateChange`, `AccountsController:selectedAccountChange`, `KeyringController:lock`, `KeyringController:unlock` events, and allows messenger actions `AccountsController:getSelectedAccount`, `NetworkController:findNetworkClientIdByChainId`, `NetworkController:getNetworkConfigurationByNetworkClientId`, `NetworkController:getProviderConfig`, `KeyringController:getState`, `PreferencesController:getState`, `TokenListController:getState`, `TokensController:getState`, `TokensController:addDetectedTokens`. ([#3775](https://github.com/MetaMask/core/pull/3775/)), ([#3923](https://github.com/MetaMask/core/pull/3923/))
- `TokensController` now exports `TokensControllerActions`, `TokensControllerGetStateAction`, `TokensControllerAddDetectedTokensAction`, `TokensControllerEvents`, `TokensControllerStateChangeEvent`. ([#3690](https://github.com/MetaMask/core/pull/3690/))

### Changed

- **BREAKING:** `TokenDetectionController` is merged with `DetectTokensController` from the `metamask-extension` repo. ([#3775](https://github.com/MetaMask/core/pull/3775/))
- **BREAKING:** `TokenDetectionController` is merged with `DetectTokensController` from the `metamask-extension` repo. ([#3775](https://github.com/MetaMask/core/pull/3775/), [#3923](https://github.com/MetaMask/core/pull/3923)), ([#3938](https://github.com/MetaMask/core/pull/3938))
- **BREAKING:** `TokenDetectionController` now resets its polling interval to the default value of 3 minutes when token detection is triggered by external controller events `KeyringController:unlock`, `TokenListController:stateChange`, `PreferencesController:stateChange`, `AccountsController:selectedAccountChange`.
- **BREAKING:** `TokenDetectionController` now refetches tokens on `NetworkController:networkDidChange` if the `networkClientId` is changed instead of `chainId`.
- **BREAKING:** `TokenDetectionController` cannot initiate polling or token detection if `KeyringController` state is locked.
- **BREAKING:** The `detectTokens` method input option `accountAddress` has been renamed to `selectedAddress`.
- **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.
- The constructor option `selectedAddress` no longer defaults to `''` if omitted. Instead, the correct address is assigned using the `AccountsController:getSelectedAccount` messenger action.
- **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.
Expand Down
6 changes: 3 additions & 3 deletions packages/assets-controllers/jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,9 @@ module.exports = merge(baseConfig, {
// An object that configures minimum threshold enforcement for coverage results
coverageThreshold: {
global: {
branches: 88.8,
functions: 96.71,
lines: 97.34,
branches: 88.58,
functions: 96.98,
lines: 97.35,
statements: 97.4,
},
},
Expand Down
96 changes: 75 additions & 21 deletions packages/assets-controllers/src/TokenDetectionController.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,14 @@ import {
} from '@metamask/controller-utils';
import type { InternalAccount } from '@metamask/keyring-api';
import type { KeyringControllerState } from '@metamask/keyring-controller';
import {
defaultState as defaultNetworkState,
type NetworkState,
type NetworkConfiguration,
type NetworkController,
import type {
NetworkState,
NetworkConfiguration,
NetworkController,
ProviderConfig,
NetworkClientId,
} from '@metamask/network-controller';
import { defaultState as defaultNetworkState } from '@metamask/network-controller';
import {
getDefaultPreferencesState,
type PreferencesState,
Expand Down Expand Up @@ -138,8 +140,11 @@ function buildTokenDetectionControllerMessenger(
return controllerMessenger.getRestricted({
name: controllerName,
allowedActions: [
'AccountsController:getSelectedAccount',
'KeyringController:getState',
'NetworkController:findNetworkClientIdByChainId',
'NetworkController:getNetworkConfigurationByNetworkClientId',
'NetworkController:getProviderConfig',
'TokensController:getState',
'TokensController:addDetectedTokens',
'TokenListController:getState',
Expand Down Expand Up @@ -338,7 +343,16 @@ describe('TokenDetectionController', () => {
selectedAddress,
},
},
async ({ controller, mockTokenListGetState, callActionSpy }) => {
async ({
controller,
mockGetProviderConfig,
mockTokenListGetState,
callActionSpy,
}) => {
mockGetProviderConfig({
chainId: '0x89',
} as unknown as ProviderConfig);
Copy link
Contributor

Choose a reason for hiding this comment

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

👍🏻


mockTokenListGetState({
...getDefaultTokenListState(),
tokensChainsCache: {
Expand Down Expand Up @@ -1748,19 +1762,19 @@ describe('TokenDetectionController', () => {
await advanceTime({ clock, duration: 0 });

expect(spy.mock.calls).toMatchObject([
[{ networkClientId: 'mainnet', accountAddress: '0x1' }],
[{ networkClientId: 'sepolia', accountAddress: '0xdeadbeef' }],
[{ networkClientId: 'goerli', accountAddress: '0x3' }],
[{ networkClientId: 'mainnet', selectedAddress: '0x1' }],
[{ networkClientId: 'sepolia', selectedAddress: '0xdeadbeef' }],
[{ networkClientId: 'goerli', selectedAddress: '0x3' }],
]);

await advanceTime({ clock, duration: DEFAULT_INTERVAL });
expect(spy.mock.calls).toMatchObject([
[{ networkClientId: 'mainnet', accountAddress: '0x1' }],
[{ networkClientId: 'sepolia', accountAddress: '0xdeadbeef' }],
[{ networkClientId: 'goerli', accountAddress: '0x3' }],
[{ networkClientId: 'mainnet', accountAddress: '0x1' }],
[{ networkClientId: 'sepolia', accountAddress: '0xdeadbeef' }],
[{ networkClientId: 'goerli', accountAddress: '0x3' }],
[{ networkClientId: 'mainnet', selectedAddress: '0x1' }],
[{ networkClientId: 'sepolia', selectedAddress: '0xdeadbeef' }],
[{ networkClientId: 'goerli', selectedAddress: '0x3' }],
[{ networkClientId: 'mainnet', selectedAddress: '0x1' }],
[{ networkClientId: 'sepolia', selectedAddress: '0xdeadbeef' }],
[{ networkClientId: 'goerli', selectedAddress: '0x3' }],
]);
},
);
Expand Down Expand Up @@ -1793,7 +1807,7 @@ describe('TokenDetectionController', () => {
});
await controller.detectTokens({
networkClientId: NetworkType.goerli,
accountAddress: selectedAddress,
selectedAddress,
});
expect(callActionSpy).not.toHaveBeenCalledWith(
'TokensController:addDetectedTokens',
Expand Down Expand Up @@ -1833,7 +1847,7 @@ describe('TokenDetectionController', () => {
});
await controller.detectTokens({
networkClientId: NetworkType.mainnet,
accountAddress: selectedAddress,
selectedAddress,
});
expect(callActionSpy).toHaveBeenLastCalledWith(
'TokensController:addDetectedTokens',
Expand Down Expand Up @@ -1896,7 +1910,7 @@ describe('TokenDetectionController', () => {

await controller.detectTokens({
networkClientId: NetworkType.mainnet,
accountAddress: selectedAddress,
selectedAddress,
});

expect(callActionSpy).toHaveBeenCalledWith(
Expand Down Expand Up @@ -1951,7 +1965,7 @@ describe('TokenDetectionController', () => {

await controller.detectTokens({
networkClientId: NetworkType.mainnet,
accountAddress: selectedAddress,
selectedAddress,
});

expect(mockTrackMetaMetricsEvent).toHaveBeenCalledWith({
Expand Down Expand Up @@ -1983,10 +1997,14 @@ function getTokensPath(chainId: Hex) {

type WithControllerCallback<ReturnValue> = ({
controller,
mockGetSelectedAccount,
mockKeyringGetState,
mockTokensGetState,
mockTokenListGetState,
mockPreferencesGetState,
mockFindNetworkClientIdByChainId,
mockGetNetworkConfigurationByNetworkClientId,
mockGetProviderConfig,
callActionSpy,
triggerKeyringUnlock,
triggerKeyringLock,
Expand All @@ -1996,13 +2014,18 @@ type WithControllerCallback<ReturnValue> = ({
triggerNetworkDidChange,
}: {
controller: TokenDetectionController;
mockGetSelectedAccount: (address: string) => void;
mockKeyringGetState: (state: KeyringControllerState) => void;
mockTokensGetState: (state: TokensState) => void;
mockTokenListGetState: (state: TokenListState) => void;
mockPreferencesGetState: (state: PreferencesState) => void;
mockFindNetworkClientIdByChainId: (
handler: (chainId: Hex) => NetworkClientId,
) => void;
mockGetNetworkConfigurationByNetworkClientId: (
handler: (networkClientId: string) => NetworkConfiguration,
) => void;
mockGetProviderConfig: (config: ProviderConfig) => void;
callActionSpy: jest.SpyInstance;
triggerKeyringUnlock: () => void;
triggerKeyringLock: () => void;
Expand Down Expand Up @@ -2039,25 +2062,45 @@ async function withController<ReturnValue>(
const controllerMessenger =
messenger ?? new ControllerMessenger<AllowedActions, AllowedEvents>();

const mockGetSelectedAccount = jest.fn<InternalAccount, []>();
controllerMessenger.registerActionHandler(
'AccountsController:getSelectedAccount',
mockGetSelectedAccount.mockReturnValue({
address: '0x1',
} as InternalAccount),
mcmire marked this conversation as resolved.
Show resolved Hide resolved
);
const mockKeyringState = jest.fn<KeyringControllerState, []>();
controllerMessenger.registerActionHandler(
'KeyringController:getState',
mockKeyringState.mockReturnValue({
isUnlocked: isKeyringUnlocked ?? true,
} as KeyringControllerState),
);
const mockFindNetworkClientIdByChainId = jest.fn<NetworkClientId, [Hex]>();
controllerMessenger.registerActionHandler(
'NetworkController:findNetworkClientIdByChainId',
mockFindNetworkClientIdByChainId.mockReturnValue(NetworkType.mainnet),
);
const mockGetNetworkConfigurationByNetworkClientId = jest.fn<
ReturnType<NetworkController['getNetworkConfigurationByNetworkClientId']>,
Parameters<NetworkController['getNetworkConfigurationByNetworkClientId']>
>();
controllerMessenger.registerActionHandler(
'NetworkController:getNetworkConfigurationByNetworkClientId',
mockGetNetworkConfigurationByNetworkClientId.mockImplementation(
(networkClientId: string) => {
(networkClientId: NetworkClientId) => {
return mockNetworkConfigurations[networkClientId];
},
),
);
const mockGetProviderConfig = jest.fn<ProviderConfig, []>();
controllerMessenger.registerActionHandler(
'NetworkController:getProviderConfig',
mockGetProviderConfig.mockReturnValue({
type: NetworkType.mainnet,
chainId: '0x1',
} as unknown as ProviderConfig),
);
const mockTokensState = jest.fn<TokensState, []>();
controllerMessenger.registerActionHandler(
'TokensController:getState',
Expand Down Expand Up @@ -2096,6 +2139,9 @@ async function withController<ReturnValue>(
try {
return await fn({
controller,
mockGetSelectedAccount: (address: string) => {
mockGetSelectedAccount.mockReturnValue({ address } as InternalAccount);
},
mockKeyringGetState: (state: KeyringControllerState) => {
mockKeyringState.mockReturnValue(state);
},
Expand All @@ -2108,13 +2154,21 @@ async function withController<ReturnValue>(
mockTokenListGetState: (state: TokenListState) => {
mockTokenListState.mockReturnValue(state);
},
mockFindNetworkClientIdByChainId: (
handler: (chainId: Hex) => NetworkClientId,
) => {
mockFindNetworkClientIdByChainId.mockImplementation(handler);
},
mockGetNetworkConfigurationByNetworkClientId: (
handler: (networkClientId: string) => NetworkConfiguration,
handler: (networkClientId: NetworkClientId) => NetworkConfiguration,
) => {
mockGetNetworkConfigurationByNetworkClientId.mockImplementation(
handler,
);
},
mockGetProviderConfig: (config: ProviderConfig) => {
mockGetProviderConfig.mockReturnValue(config);
},
callActionSpy,
triggerKeyringUnlock: () => {
controllerMessenger.publish('KeyringController:unlock');
Expand Down
Loading
Loading