From f024224052ed3d91d46f9ea3dd6c9f64efdc00dd Mon Sep 17 00:00:00 2001 From: Brian Bergeron Date: Wed, 13 Nov 2024 12:29:35 -0800 Subject: [PATCH 01/23] upgrade --- ...s-controllers-npm-43.1.1-c223d56176.patch} | 6 +- app/scripts/metamask-controller.js | 53 ++++++++++------ package.json | 2 +- ui/contexts/assetPolling.tsx | 4 ++ ui/hooks/useTokenDetectionPolling.ts | 27 +++++++++ ui/hooks/useTokenListPolling.ts | 33 ++++++++++ ui/selectors/selectors.js | 4 ++ ui/store/actions.ts | 60 +++++++++++++++++++ yarn.lock | 52 ++++++++++------ 9 files changed, 202 insertions(+), 39 deletions(-) rename .yarn/patches/{@metamask-assets-controllers-npm-42.0.0-57b3d695bb.patch => @metamask-assets-controllers-npm-43.1.1-c223d56176.patch} (84%) create mode 100644 ui/hooks/useTokenDetectionPolling.ts create mode 100644 ui/hooks/useTokenListPolling.ts diff --git a/.yarn/patches/@metamask-assets-controllers-npm-42.0.0-57b3d695bb.patch b/.yarn/patches/@metamask-assets-controllers-npm-43.1.1-c223d56176.patch similarity index 84% rename from .yarn/patches/@metamask-assets-controllers-npm-42.0.0-57b3d695bb.patch rename to .yarn/patches/@metamask-assets-controllers-npm-43.1.1-c223d56176.patch index 7a5837cd4818..2a6310c2db69 100644 --- a/.yarn/patches/@metamask-assets-controllers-npm-42.0.0-57b3d695bb.patch +++ b/.yarn/patches/@metamask-assets-controllers-npm-43.1.1-c223d56176.patch @@ -1,5 +1,5 @@ diff --git a/dist/assetsUtil.cjs b/dist/assetsUtil.cjs -index e90a1b6767bc8ac54b7a4d580035cf5db6861dca..a5e0f03d2541b4e3540431ef2e6e4b60fb7ae9fe 100644 +index c2e83cf6caee19152aa164f1333cfef7b681e900..590b6de6e9d20ca402b82ac56b0929ab8c16c932 100644 --- a/dist/assetsUtil.cjs +++ b/dist/assetsUtil.cjs @@ -3,6 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) { @@ -7,10 +7,10 @@ index e90a1b6767bc8ac54b7a4d580035cf5db6861dca..a5e0f03d2541b4e3540431ef2e6e4b60 }; Object.defineProperty(exports, "__esModule", { value: true }); +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { newObj[key] = obj[key]; } } } newObj.default = obj; return newObj; } } - exports.fetchTokenContractExchangeRates = exports.reduceInBatchesSerially = exports.divideIntoBatches = exports.ethersBigNumberToBN = exports.addUrlProtocolPrefix = exports.getFormattedIpfsUrl = exports.getIpfsCIDv1AndPath = exports.removeIpfsProtocolPrefix = exports.isTokenListSupportedForNetwork = exports.isTokenDetectionSupportedForNetwork = exports.SupportedTokenDetectionNetworks = exports.formatIconUrlWithProxy = exports.formatAggregatorNames = exports.hasNewCollectionFields = exports.compareNftMetadata = exports.TOKEN_PRICES_BATCH_SIZE = void 0; + exports.fetchTokenContractExchangeRates = exports.reduceInBatchesSerially = exports.divideIntoBatches = exports.ethersBigNumberToBN = exports.addUrlProtocolPrefix = exports.getFormattedIpfsUrl = exports.getIpfsCIDv1AndPath = exports.removeIpfsProtocolPrefix = exports.isTokenListSupportedForNetwork = exports.isTokenDetectionSupportedForNetwork = exports.SupportedStakedBalanceNetworks = exports.SupportedTokenDetectionNetworks = exports.formatIconUrlWithProxy = exports.formatAggregatorNames = exports.hasNewCollectionFields = exports.compareNftMetadata = exports.TOKEN_PRICES_BATCH_SIZE = void 0; const controller_utils_1 = require("@metamask/controller-utils"); const utils_1 = require("@metamask/utils"); -@@ -221,7 +222,7 @@ async function getIpfsCIDv1AndPath(ipfsUrl) { +@@ -233,7 +234,7 @@ async function getIpfsCIDv1AndPath(ipfsUrl) { const index = url.indexOf('/'); const cid = index !== -1 ? url.substring(0, index) : url; const path = index !== -1 ? url.substring(index) : undefined; diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index 9632e072c70b..a558ff055ab2 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -2600,25 +2600,25 @@ export default class MetamaskController extends EventEmitter { triggerNetworkrequests() { this.accountTrackerController.start(); this.txController.startIncomingTransactionPolling(); - this.tokenDetectionController.enable(); + // this.tokenDetectionController.enable(); - const preferencesControllerState = this.preferencesController.state; + // const preferencesControllerState = this.preferencesController.state; - if (this.#isTokenListPollingRequired(preferencesControllerState)) { - this.tokenListController.start(); - } + // if (this.#isTokenListPollingRequired(preferencesControllerState)) { + // this.tokenListController.start(); + // } } stopNetworkRequests() { this.accountTrackerController.stop(); this.txController.stopIncomingTransactionPolling(); - this.tokenDetectionController.disable(); + // this.tokenDetectionController.disable(); - const preferencesControllerState = this.preferencesController.state; + // const preferencesControllerState = this.preferencesController.state; - if (this.#isTokenListPollingRequired(preferencesControllerState)) { - this.tokenListController.stop(); - } + // if (this.#isTokenListPollingRequired(preferencesControllerState)) { + // this.tokenListController.stop(); + // } } resetStates(resetMethods) { @@ -3238,6 +3238,7 @@ export default class MetamaskController extends EventEmitter { currencyRateController, tokenDetectionController, ensController, + tokenListController, gasFeeController, metaMetricsController, networkController, @@ -4040,6 +4041,19 @@ export default class MetamaskController extends EventEmitter { tokenRatesController, ), + tokenDetectionStartPolling: tokenDetectionController.startPolling.bind( + tokenDetectionController, + ), + tokenDetectionStopPollingByPollingToken: + tokenDetectionController.stopPollingByPollingToken.bind( + tokenDetectionController, + ), + + tokenListStartPolling: + tokenListController.startPolling.bind(tokenListController), + tokenListStopPollingByPollingToken: + tokenListController.stopPollingByPollingToken.bind(tokenListController), + // GasFeeController gasFeeStartPollingByNetworkClientId: gasFeeController.startPollingByNetworkClientId.bind(gasFeeController), @@ -4403,6 +4417,7 @@ export default class MetamaskController extends EventEmitter { if (balance === '0x0') { // This account has no balance, so check for tokens await this.tokenDetectionController.detectTokens({ + chainIds: [chainId], selectedAddress: address, }); @@ -6669,6 +6684,8 @@ export default class MetamaskController extends EventEmitter { this.gasFeeController.stopAllPolling(); this.currencyRateController.stopAllPolling(); this.tokenRatesController.stopAllPolling(); + this.tokenDetectionController.stopAllPolling(); + this.tokenListController.stopAllPolling(); this.appStateController.clearPollingTokens(); } catch (error) { console.error(error); @@ -7205,14 +7222,14 @@ export default class MetamaskController extends EventEmitter { this.tokenListController.updatePreventPollingOnNetworkRestart(!newEnabled); - if (newEnabled) { - log.debug('Started token list controller polling'); - this.tokenListController.start(); - } else { - log.debug('Stopped token list controller polling'); - this.tokenListController.clearingTokenListData(); - this.tokenListController.stop(); - } + // if (newEnabled) { + // log.debug('Started token list controller polling'); + // this.tokenListController.start(); + // } else { + // log.debug('Stopped token list controller polling'); + // this.tokenListController.clearingTokenListData(); + // this.tokenListController.stop(); + // } } #isTokenListPollingRequired(preferencesControllerState) { diff --git a/package.json b/package.json index 6c9811cb3ed6..a477e3200b0e 100644 --- a/package.json +++ b/package.json @@ -292,7 +292,7 @@ "@metamask/address-book-controller": "^6.0.0", "@metamask/announcement-controller": "^7.0.0", "@metamask/approval-controller": "^7.0.0", - "@metamask/assets-controllers": "patch:@metamask/assets-controllers@npm%3A42.0.0#~/.yarn/patches/@metamask-assets-controllers-npm-42.0.0-57b3d695bb.patch", + "@metamask/assets-controllers": "patch:@metamask/assets-controllers@npm%3A43.1.1#~/.yarn/patches/@metamask-assets-controllers-npm-43.1.1-c223d56176.patch", "@metamask/base-controller": "^7.0.0", "@metamask/bitcoin-wallet-snap": "^0.8.2", "@metamask/browser-passworder": "^4.3.0", diff --git a/ui/contexts/assetPolling.tsx b/ui/contexts/assetPolling.tsx index 63cef9667fbd..be1954a37ec5 100644 --- a/ui/contexts/assetPolling.tsx +++ b/ui/contexts/assetPolling.tsx @@ -1,6 +1,8 @@ import React, { ReactNode } from 'react'; import useCurrencyRatePolling from '../hooks/useCurrencyRatePolling'; import useTokenRatesPolling from '../hooks/useTokenRatesPolling'; +import useTokenDetectionPolling from '../hooks/useTokenDetectionPolling'; +import useTokenListPolling from '../hooks/useTokenListPolling'; // This provider is a step towards making controller polling fully UI based. // Eventually, individual UI components will call the use*Polling hooks to @@ -8,6 +10,8 @@ import useTokenRatesPolling from '../hooks/useTokenRatesPolling'; export const AssetPollingProvider = ({ children }: { children: ReactNode }) => { useCurrencyRatePolling(); useTokenRatesPolling(); + useTokenDetectionPolling(); + useTokenListPolling(); return <>{children}; }; diff --git a/ui/hooks/useTokenDetectionPolling.ts b/ui/hooks/useTokenDetectionPolling.ts new file mode 100644 index 000000000000..e04562cf3965 --- /dev/null +++ b/ui/hooks/useTokenDetectionPolling.ts @@ -0,0 +1,27 @@ +import { useSelector } from 'react-redux'; +import { + getNetworkConfigurationsByChainId, + getUseTokenDetection, +} from '../selectors'; +import { + tokenDetectionStartPolling, + tokenDetectionStopPollingByPollingToken, +} from '../store/actions'; +import useMultiPolling from './useMultiPolling'; + +const useTokenDetectionPolling = () => { + const useTokenDetection = useSelector(getUseTokenDetection); + const networkConfigurations = useSelector(getNetworkConfigurationsByChainId); + const chainIds = Object.keys(networkConfigurations); + + useMultiPolling({ + startPolling: tokenDetectionStartPolling, + stopPollingByPollingToken: tokenDetectionStopPollingByPollingToken, + input: useTokenDetection ? [chainIds] : [], + }); + + return { + }; +}; + +export default useTokenDetectionPolling; diff --git a/ui/hooks/useTokenListPolling.ts b/ui/hooks/useTokenListPolling.ts new file mode 100644 index 000000000000..a8eabcd5d4e3 --- /dev/null +++ b/ui/hooks/useTokenListPolling.ts @@ -0,0 +1,33 @@ +import { useSelector } from 'react-redux'; +import { + getNetworkConfigurationsByChainId, + getPetnamesEnabled, + getUseTokenDetection, + getUseTransactionSimulations, +} from '../selectors'; +import { + tokenListStartPolling, + tokenListStopPollingByPollingToken, +} from '../store/actions'; +import useMultiPolling from './useMultiPolling'; + +const useTokenListPolling = () => { + const networkConfigurations = useSelector(getNetworkConfigurationsByChainId); + const useTokenDetection = useSelector(getUseTokenDetection); + const useTransactionSimulations = useSelector(getUseTransactionSimulations); + const petnamesEnabled = useSelector(getPetnamesEnabled); + + const enabled = + useTokenDetection || petnamesEnabled || useTransactionSimulations; + + useMultiPolling({ + startPolling: tokenListStartPolling, + stopPollingByPollingToken: tokenListStopPollingByPollingToken, + input: enabled ? Object.keys(networkConfigurations) : [], + }); + + return { + }; +}; + +export default useTokenListPolling; diff --git a/ui/selectors/selectors.js b/ui/selectors/selectors.js index 7c407eae4dc4..aad920b6cb8b 100644 --- a/ui/selectors/selectors.js +++ b/ui/selectors/selectors.js @@ -954,6 +954,10 @@ export function getPetnamesEnabled(state) { return petnamesEnabled; } +export function getUseTransactionSimulations(state) { + return Boolean(state.metamask.useTransactionSimulations); +} + export function getRedesignedConfirmationsEnabled(state) { const { redesignedConfirmationsEnabled } = getPreferences(state); return redesignedConfirmationsEnabled; diff --git a/ui/store/actions.ts b/ui/store/actions.ts index 67de497817c8..c4e546b8fd8e 100644 --- a/ui/store/actions.ts +++ b/ui/store/actions.ts @@ -4564,6 +4564,66 @@ export async function currencyRateStopPollingByPollingToken( await removePollingTokenFromAppState(pollingToken); } +/** + * Informs the TokenDetectionController that the UI requires token detection polling + * + * @param chainIds - An array of chain ids to poll token detection on. + * @returns polling token that can be used to stop polling. + */ +export async function tokenDetectionStartPolling( + chainIds: string[], +): Promise { + const pollingToken = await submitRequestToBackground( + 'tokenDetectionStartPolling', + [{ chainIds }], + ); + + await addPollingTokenToAppState(pollingToken); + return pollingToken; +} + +/** + * Informs the TokenDetectionController that the UI no longer token detection polling + * + * @param pollingToken - Poll token received from calling tokenDetectionStartPolling + */ +export async function tokenDetectionStopPollingByPollingToken( + pollingToken: string, +) { + await submitRequestToBackground('tokenDetectionStopPollingByPollingToken', [ + pollingToken, + ]); + await removePollingTokenFromAppState(pollingToken); +} + +/** + * Informs the TokenListController that the UI requires token list polling + * + * @param chainId + * @returns polling token that can be used to stop polling + */ +export async function tokenListStartPolling(chainId: string): Promise { + const pollingToken = await submitRequestToBackground( + 'tokenListStartPolling', + [{ chainId }], + ); + + await addPollingTokenToAppState(pollingToken); + return pollingToken; +} + +/** + * Informs the TokenListController that the UI no longer token list polling + * + * @param pollingToken - Poll token received from calling tokenListStartPolling + */ +export async function tokenListStopPollingByPollingToken(pollingToken: string) { + await submitRequestToBackground('tokenListStopPollingByPollingToken', [ + pollingToken, + ]); + await removePollingTokenFromAppState(pollingToken); +} + /** * Informs the TokenRatesController that the UI requires * token rate polling for the given chain id. diff --git a/yarn.lock b/yarn.lock index d40270b12e9c..62c83ba2f925 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4934,9 +4934,9 @@ __metadata: languageName: node linkType: hard -"@metamask/assets-controllers@npm:42.0.0": - version: 42.0.0 - resolution: "@metamask/assets-controllers@npm:42.0.0" +"@metamask/assets-controllers@npm:43.1.1": + version: 43.1.1 + resolution: "@metamask/assets-controllers@npm:43.1.1" dependencies: "@ethereumjs/util": "npm:^8.1.0" "@ethersproject/address": "npm:^5.7.0" @@ -4946,7 +4946,7 @@ __metadata: "@metamask/abi-utils": "npm:^2.0.3" "@metamask/base-controller": "npm:^7.0.2" "@metamask/contract-metadata": "npm:^2.4.0" - "@metamask/controller-utils": "npm:^11.4.2" + "@metamask/controller-utils": "npm:^11.4.3" "@metamask/eth-query": "npm:^4.0.0" "@metamask/metamask-eth-abis": "npm:^3.1.1" "@metamask/polling-controller": "npm:^12.0.1" @@ -4963,18 +4963,18 @@ __metadata: single-call-balance-checker-abi: "npm:^1.0.0" uuid: "npm:^8.3.2" peerDependencies: - "@metamask/accounts-controller": ^18.0.0 + "@metamask/accounts-controller": ^19.0.0 "@metamask/approval-controller": ^7.0.0 - "@metamask/keyring-controller": ^17.0.0 + "@metamask/keyring-controller": ^18.0.0 "@metamask/network-controller": ^22.0.0 - "@metamask/preferences-controller": ^13.0.0 - checksum: 10/64d2bd43139ee5c19bd665b07212cd5d5dd41b457dedde3b5db31442292c4d064dc015011f5f001bb423683675fb20898ff652e91d2339ad1d21cc45fa93487a + "@metamask/preferences-controller": ^14.0.0 + checksum: 10/e8f37928085a243f2f3a9d3b09b486f31737814d6257ee49bc2d841d2f467733b8c533c056e9ca24acdcc80414503b34b00e10abb1cdfeb8483e6fe30bc4a62f languageName: node linkType: hard -"@metamask/assets-controllers@patch:@metamask/assets-controllers@npm%3A42.0.0#~/.yarn/patches/@metamask-assets-controllers-npm-42.0.0-57b3d695bb.patch": - version: 42.0.0 - resolution: "@metamask/assets-controllers@patch:@metamask/assets-controllers@npm%3A42.0.0#~/.yarn/patches/@metamask-assets-controllers-npm-42.0.0-57b3d695bb.patch::version=42.0.0&hash=e14ff8" +"@metamask/assets-controllers@patch:@metamask/assets-controllers@npm%3A43.1.1#~/.yarn/patches/@metamask-assets-controllers-npm-43.1.1-c223d56176.patch": + version: 43.1.1 + resolution: "@metamask/assets-controllers@patch:@metamask/assets-controllers@npm%3A43.1.1#~/.yarn/patches/@metamask-assets-controllers-npm-43.1.1-c223d56176.patch::version=43.1.1&hash=5a94c2" dependencies: "@ethereumjs/util": "npm:^8.1.0" "@ethersproject/address": "npm:^5.7.0" @@ -4984,7 +4984,7 @@ __metadata: "@metamask/abi-utils": "npm:^2.0.3" "@metamask/base-controller": "npm:^7.0.2" "@metamask/contract-metadata": "npm:^2.4.0" - "@metamask/controller-utils": "npm:^11.4.2" + "@metamask/controller-utils": "npm:^11.4.3" "@metamask/eth-query": "npm:^4.0.0" "@metamask/metamask-eth-abis": "npm:^3.1.1" "@metamask/polling-controller": "npm:^12.0.1" @@ -5001,12 +5001,12 @@ __metadata: single-call-balance-checker-abi: "npm:^1.0.0" uuid: "npm:^8.3.2" peerDependencies: - "@metamask/accounts-controller": ^18.0.0 + "@metamask/accounts-controller": ^19.0.0 "@metamask/approval-controller": ^7.0.0 - "@metamask/keyring-controller": ^17.0.0 + "@metamask/keyring-controller": ^18.0.0 "@metamask/network-controller": ^22.0.0 - "@metamask/preferences-controller": ^13.0.0 - checksum: 10/9a6727b28f88fd2df3f4b1628dd5d8c2f3e73fd4b9cd090f22d175c2522faa6c6b7e9a93d0ec2b2d123a263c8f4116fbfe97f196b99401b28ac8597f522651eb + "@metamask/preferences-controller": ^14.0.0 + checksum: 10/1a3672cb721c6716d33c1c6c5e7ffc2859689407e70af7503204220afe41f6c0d20f883ad7d51089af7376e4005de7479525b3faa49113c942cf3ab1bceba154 languageName: node linkType: hard @@ -5105,6 +5105,24 @@ __metadata: languageName: node linkType: hard +"@metamask/controller-utils@npm:^11.4.3": + version: 11.4.3 + resolution: "@metamask/controller-utils@npm:11.4.3" + dependencies: + "@ethereumjs/util": "npm:^8.1.0" + "@metamask/eth-query": "npm:^4.0.0" + "@metamask/ethjs-unit": "npm:^0.3.0" + "@metamask/utils": "npm:^10.0.0" + "@spruceid/siwe-parser": "npm:2.1.0" + "@types/bn.js": "npm:^5.1.5" + bignumber.js: "npm:^9.1.2" + bn.js: "npm:^5.2.1" + eth-ens-namehash: "npm:^2.0.8" + fast-deep-equal: "npm:^3.1.3" + checksum: 10/5703b0721daf679cf44affc690f2b313e40893b64b0aafaf203e69ee51438197cc3634ef7094145f580a8a8aaadcb79026b2fbd4065c1bb4a8c26627a2c4c69a + languageName: node + linkType: hard + "@metamask/design-tokens@npm:^4.0.0": version: 4.0.0 resolution: "@metamask/design-tokens@npm:4.0.0" @@ -26699,7 +26717,7 @@ __metadata: "@metamask/announcement-controller": "npm:^7.0.0" "@metamask/api-specs": "npm:^0.9.3" "@metamask/approval-controller": "npm:^7.0.0" - "@metamask/assets-controllers": "patch:@metamask/assets-controllers@npm%3A42.0.0#~/.yarn/patches/@metamask-assets-controllers-npm-42.0.0-57b3d695bb.patch" + "@metamask/assets-controllers": "patch:@metamask/assets-controllers@npm%3A43.1.1#~/.yarn/patches/@metamask-assets-controllers-npm-43.1.1-c223d56176.patch" "@metamask/auto-changelog": "npm:^2.1.0" "@metamask/base-controller": "npm:^7.0.0" "@metamask/bitcoin-wallet-snap": "npm:^0.8.2" From cfb46d1036f2ed6d53833da5e2ba16e54662af0a Mon Sep 17 00:00:00 2001 From: Brian Bergeron Date: Wed, 13 Nov 2024 12:42:02 -0800 Subject: [PATCH 02/23] remove unit test --- app/scripts/metamask-controller.test.js | 109 ------------------------ 1 file changed, 109 deletions(-) diff --git a/app/scripts/metamask-controller.test.js b/app/scripts/metamask-controller.test.js index 750f8771568b..2ccf864b0edd 100644 --- a/app/scripts/metamask-controller.test.js +++ b/app/scripts/metamask-controller.test.js @@ -2301,115 +2301,6 @@ describe('MetaMaskController', () => { }); }); - describe('token list controller', () => { - it('stops polling if petnames, simulations, and token detection disabled', async () => { - expect(TokenListController.prototype.stop).not.toHaveBeenCalled(); - - expect( - TokenListController.prototype.clearingTokenListData, - ).not.toHaveBeenCalled(); - - await simulatePreferencesChange({ - useTransactionSimulations: false, - useTokenDetection: false, - preferences: { - petnamesEnabled: false, - }, - }); - - expect(TokenListController.prototype.stop).toHaveBeenCalledTimes(1); - - expect( - TokenListController.prototype.clearingTokenListData, - ).toHaveBeenCalledTimes(1); - }); - - it.each([ - [ - 'petnames', - { - preferences: { petnamesEnabled: false }, - useTokenDetection: true, - useTransactionSimulations: true, - }, - ], - [ - 'simulations', - { - preferences: { petnamesEnabled: true }, - useTokenDetection: true, - useTransactionSimulations: false, - }, - ], - [ - 'token detection', - { - preferences: { petnamesEnabled: true }, - useTokenDetection: false, - useTransactionSimulations: true, - }, - ], - ])( - 'does not stop polling if only %s disabled', - async (_, preferences) => { - expect(TokenListController.prototype.stop).not.toHaveBeenCalled(); - - expect( - TokenListController.prototype.clearingTokenListData, - ).not.toHaveBeenCalled(); - - await simulatePreferencesChange(preferences); - - expect(TokenListController.prototype.stop).not.toHaveBeenCalled(); - - expect( - TokenListController.prototype.clearingTokenListData, - ).not.toHaveBeenCalled(); - }, - ); - - it.each([ - [ - 'petnames', - { - preferences: { petnamesEnabled: true }, - useTokenDetection: false, - useTransactionSimulations: false, - }, - ], - [ - 'simulations', - { - preferences: { petnamesEnabled: false }, - useTokenDetection: false, - useTransactionSimulations: true, - }, - ], - [ - 'token detection', - { - preferences: { petnamesEnabled: false }, - useTokenDetection: true, - useTransactionSimulations: false, - }, - ], - ])('starts polling if only %s enabled', async (_, preferences) => { - expect(TokenListController.prototype.start).not.toHaveBeenCalled(); - - await simulatePreferencesChange({ - useTransactionSimulations: false, - useTokenDetection: false, - preferences: { - petnamesEnabled: false, - }, - }); - - await simulatePreferencesChange(preferences); - - expect(TokenListController.prototype.start).toHaveBeenCalledTimes(1); - }); - }); - describe('MultichainRatesController start/stop', () => { const mockEvmAccount = createMockInternalAccount(); const mockNonEvmAccount = { From 25f5ed0f7247cd78b3f70d302e86f71f4291995a Mon Sep 17 00:00:00 2001 From: MetaMask Bot Date: Wed, 13 Nov 2024 20:54:39 +0000 Subject: [PATCH 03/23] Update LavaMoat policies --- lavamoat/browserify/beta/policy.json | 21 ++++++++++++++++++++- lavamoat/browserify/flask/policy.json | 21 ++++++++++++++++++++- lavamoat/browserify/main/policy.json | 21 ++++++++++++++++++++- lavamoat/browserify/mmi/policy.json | 21 ++++++++++++++++++++- 4 files changed, 80 insertions(+), 4 deletions(-) diff --git a/lavamoat/browserify/beta/policy.json b/lavamoat/browserify/beta/policy.json index 94bf575e610a..d40ce999f7ab 100644 --- a/lavamoat/browserify/beta/policy.json +++ b/lavamoat/browserify/beta/policy.json @@ -686,13 +686,14 @@ "packages": { "@ensdomains/content-hash>multicodec>uint8arrays>multiformats": true, "@ethereumjs/tx>@ethereumjs/util": true, + "@ethersproject/bignumber": true, "@ethersproject/contracts": true, "@ethersproject/providers": true, "@metamask/abi-utils": true, + "@metamask/assets-controllers>@metamask/controller-utils": true, "@metamask/assets-controllers>@metamask/polling-controller": true, "@metamask/base-controller": true, "@metamask/contract-metadata": true, - "@metamask/controller-utils": true, "@metamask/eth-query": true, "@metamask/metamask-eth-abis": true, "@metamask/name-controller>async-mutex": true, @@ -706,6 +707,24 @@ "uuid": true } }, + "@metamask/assets-controllers>@metamask/controller-utils": { + "globals": { + "URL": true, + "console.error": true, + "fetch": true, + "setTimeout": true + }, + "packages": { + "@ethereumjs/tx>@ethereumjs/util": true, + "@metamask/controller-utils>@spruceid/siwe-parser": true, + "@metamask/ethjs>@metamask/ethjs-unit": true, + "@metamask/utils": true, + "bn.js": true, + "browserify>buffer": true, + "eslint>fast-deep-equal": true, + "eth-ens-namehash": true + } + }, "@metamask/assets-controllers>@metamask/polling-controller": { "globals": { "clearTimeout": true, diff --git a/lavamoat/browserify/flask/policy.json b/lavamoat/browserify/flask/policy.json index 94bf575e610a..d40ce999f7ab 100644 --- a/lavamoat/browserify/flask/policy.json +++ b/lavamoat/browserify/flask/policy.json @@ -686,13 +686,14 @@ "packages": { "@ensdomains/content-hash>multicodec>uint8arrays>multiformats": true, "@ethereumjs/tx>@ethereumjs/util": true, + "@ethersproject/bignumber": true, "@ethersproject/contracts": true, "@ethersproject/providers": true, "@metamask/abi-utils": true, + "@metamask/assets-controllers>@metamask/controller-utils": true, "@metamask/assets-controllers>@metamask/polling-controller": true, "@metamask/base-controller": true, "@metamask/contract-metadata": true, - "@metamask/controller-utils": true, "@metamask/eth-query": true, "@metamask/metamask-eth-abis": true, "@metamask/name-controller>async-mutex": true, @@ -706,6 +707,24 @@ "uuid": true } }, + "@metamask/assets-controllers>@metamask/controller-utils": { + "globals": { + "URL": true, + "console.error": true, + "fetch": true, + "setTimeout": true + }, + "packages": { + "@ethereumjs/tx>@ethereumjs/util": true, + "@metamask/controller-utils>@spruceid/siwe-parser": true, + "@metamask/ethjs>@metamask/ethjs-unit": true, + "@metamask/utils": true, + "bn.js": true, + "browserify>buffer": true, + "eslint>fast-deep-equal": true, + "eth-ens-namehash": true + } + }, "@metamask/assets-controllers>@metamask/polling-controller": { "globals": { "clearTimeout": true, diff --git a/lavamoat/browserify/main/policy.json b/lavamoat/browserify/main/policy.json index 94bf575e610a..d40ce999f7ab 100644 --- a/lavamoat/browserify/main/policy.json +++ b/lavamoat/browserify/main/policy.json @@ -686,13 +686,14 @@ "packages": { "@ensdomains/content-hash>multicodec>uint8arrays>multiformats": true, "@ethereumjs/tx>@ethereumjs/util": true, + "@ethersproject/bignumber": true, "@ethersproject/contracts": true, "@ethersproject/providers": true, "@metamask/abi-utils": true, + "@metamask/assets-controllers>@metamask/controller-utils": true, "@metamask/assets-controllers>@metamask/polling-controller": true, "@metamask/base-controller": true, "@metamask/contract-metadata": true, - "@metamask/controller-utils": true, "@metamask/eth-query": true, "@metamask/metamask-eth-abis": true, "@metamask/name-controller>async-mutex": true, @@ -706,6 +707,24 @@ "uuid": true } }, + "@metamask/assets-controllers>@metamask/controller-utils": { + "globals": { + "URL": true, + "console.error": true, + "fetch": true, + "setTimeout": true + }, + "packages": { + "@ethereumjs/tx>@ethereumjs/util": true, + "@metamask/controller-utils>@spruceid/siwe-parser": true, + "@metamask/ethjs>@metamask/ethjs-unit": true, + "@metamask/utils": true, + "bn.js": true, + "browserify>buffer": true, + "eslint>fast-deep-equal": true, + "eth-ens-namehash": true + } + }, "@metamask/assets-controllers>@metamask/polling-controller": { "globals": { "clearTimeout": true, diff --git a/lavamoat/browserify/mmi/policy.json b/lavamoat/browserify/mmi/policy.json index 9acbc67302ab..addfeb142ab3 100644 --- a/lavamoat/browserify/mmi/policy.json +++ b/lavamoat/browserify/mmi/policy.json @@ -778,13 +778,14 @@ "packages": { "@ensdomains/content-hash>multicodec>uint8arrays>multiformats": true, "@ethereumjs/tx>@ethereumjs/util": true, + "@ethersproject/bignumber": true, "@ethersproject/contracts": true, "@ethersproject/providers": true, "@metamask/abi-utils": true, + "@metamask/assets-controllers>@metamask/controller-utils": true, "@metamask/assets-controllers>@metamask/polling-controller": true, "@metamask/base-controller": true, "@metamask/contract-metadata": true, - "@metamask/controller-utils": true, "@metamask/eth-query": true, "@metamask/metamask-eth-abis": true, "@metamask/name-controller>async-mutex": true, @@ -798,6 +799,24 @@ "uuid": true } }, + "@metamask/assets-controllers>@metamask/controller-utils": { + "globals": { + "URL": true, + "console.error": true, + "fetch": true, + "setTimeout": true + }, + "packages": { + "@ethereumjs/tx>@ethereumjs/util": true, + "@metamask/controller-utils>@spruceid/siwe-parser": true, + "@metamask/ethjs>@metamask/ethjs-unit": true, + "@metamask/utils": true, + "bn.js": true, + "browserify>buffer": true, + "eslint>fast-deep-equal": true, + "eth-ens-namehash": true + } + }, "@metamask/assets-controllers>@metamask/polling-controller": { "globals": { "clearTimeout": true, From 6c216f8372304cd91a709017a68f9be3e2d6d5c5 Mon Sep 17 00:00:00 2001 From: Brian Bergeron Date: Wed, 13 Nov 2024 13:01:22 -0800 Subject: [PATCH 04/23] yarn dedupe --- yarn.lock | 20 +------------------- 1 file changed, 1 insertion(+), 19 deletions(-) diff --git a/yarn.lock b/yarn.lock index 62c83ba2f925..67bc765ad824 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5087,25 +5087,7 @@ __metadata: languageName: node linkType: hard -"@metamask/controller-utils@npm:^11.0.0, @metamask/controller-utils@npm:^11.0.2, @metamask/controller-utils@npm:^11.1.0, @metamask/controller-utils@npm:^11.2.0, @metamask/controller-utils@npm:^11.3.0, @metamask/controller-utils@npm:^11.4.0, @metamask/controller-utils@npm:^11.4.1, @metamask/controller-utils@npm:^11.4.2": - version: 11.4.2 - resolution: "@metamask/controller-utils@npm:11.4.2" - dependencies: - "@ethereumjs/util": "npm:^8.1.0" - "@metamask/eth-query": "npm:^4.0.0" - "@metamask/ethjs-unit": "npm:^0.3.0" - "@metamask/utils": "npm:^10.0.0" - "@spruceid/siwe-parser": "npm:2.1.0" - "@types/bn.js": "npm:^5.1.5" - bignumber.js: "npm:^9.1.2" - bn.js: "npm:^5.2.1" - eth-ens-namehash: "npm:^2.0.8" - fast-deep-equal: "npm:^3.1.3" - checksum: 10/fdae49ee97e7a2a1bb6414011ca59932f8712a768a9c4c43673a2504c9fa9e61d83df53a21ff0506ef6a8cf774704f2df58a6d71385c8786ec5cab4359c051e1 - languageName: node - linkType: hard - -"@metamask/controller-utils@npm:^11.4.3": +"@metamask/controller-utils@npm:^11.0.0, @metamask/controller-utils@npm:^11.0.2, @metamask/controller-utils@npm:^11.1.0, @metamask/controller-utils@npm:^11.2.0, @metamask/controller-utils@npm:^11.3.0, @metamask/controller-utils@npm:^11.4.0, @metamask/controller-utils@npm:^11.4.1, @metamask/controller-utils@npm:^11.4.2, @metamask/controller-utils@npm:^11.4.3": version: 11.4.3 resolution: "@metamask/controller-utils@npm:11.4.3" dependencies: From 2822c588c221d60ba21da538a7e48129c58993d9 Mon Sep 17 00:00:00 2001 From: Brian Bergeron Date: Wed, 13 Nov 2024 13:24:19 -0800 Subject: [PATCH 05/23] add unit test --- ui/hooks/useTokenListPolling.test.ts | 78 ++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) create mode 100644 ui/hooks/useTokenListPolling.test.ts diff --git a/ui/hooks/useTokenListPolling.test.ts b/ui/hooks/useTokenListPolling.test.ts new file mode 100644 index 000000000000..efe373c5d29f --- /dev/null +++ b/ui/hooks/useTokenListPolling.test.ts @@ -0,0 +1,78 @@ +import { renderWithProvider } from '../../test/jest'; +import { renderHookWithProvider } from '../../test/lib/render-helpers'; +import { + tokenListStartPolling, + tokenListStopPollingByPollingToken, +} from '../store/actions'; +import useTokenListPolling from './useTokenListPolling'; + +let mockPromises: Promise[]; + +jest.mock('../store/actions', () => ({ + tokenListStartPolling: jest.fn().mockImplementation((input) => { + const promise = Promise.resolve(`${input}_token`); + mockPromises.push(promise); + return promise; + }), + tokenListStopPollingByPollingToken: jest.fn(), +})); + +describe('useTokenListPolling', () => { + beforeEach(() => (mockPromises = [])); + + it('should poll for token lists on each chain when enabled, and stop on dismount', async () => { + const state = { + metamask: { + useTokenDetection: true, + networkConfigurationsByChainId: { + '0x1': {}, + '0x89': {}, + }, + }, + }; + + const { unmount } = renderHookWithProvider( + () => useTokenListPolling(), + state, + ); + + // Should poll each chain + await Promise.all(mockPromises); + expect(tokenListStartPolling).toHaveBeenCalledTimes(2); + expect(tokenListStartPolling).toHaveBeenCalledWith('0x1'); + expect(tokenListStartPolling).toHaveBeenCalledWith('0x89'); + + // Stop polling on dismount + unmount(); + expect(tokenListStopPollingByPollingToken).toHaveBeenCalledTimes(2); + expect(tokenListStopPollingByPollingToken).toHaveBeenCalledWith( + '0x1_token', + ); + expect(tokenListStopPollingByPollingToken).toHaveBeenCalledWith( + '0x89_token', + ); + }); + + it('should not poll when disabled', async () => { + // disabled when detection, petnames, and simulations are all disabled + const state = { + metamask: { + useTokenDetection: false, + useTransactionSimulations: false, + preferences: { + petnamesEnabled: false, + }, + networkConfigurationsByChainId: { + '0x1': {}, + '0x89': {}, + }, + }, + }; + + renderHookWithProvider(() => useTokenListPolling(), state); + + await Promise.all(mockPromises); + expect(tokenListStartPolling).toHaveBeenCalledTimes(0); + expect(tokenListStopPollingByPollingToken).toHaveBeenCalledTimes(0); + }); +}); From 3d5323ae16007d3a3ae34215baf81d661e3966a5 Mon Sep 17 00:00:00 2001 From: MetaMask Bot Date: Wed, 13 Nov 2024 21:38:01 +0000 Subject: [PATCH 06/23] Update LavaMoat policies --- lavamoat/browserify/beta/policy.json | 20 +------------------- lavamoat/browserify/flask/policy.json | 20 +------------------- lavamoat/browserify/main/policy.json | 20 +------------------- lavamoat/browserify/mmi/policy.json | 20 +------------------- 4 files changed, 4 insertions(+), 76 deletions(-) diff --git a/lavamoat/browserify/beta/policy.json b/lavamoat/browserify/beta/policy.json index d40ce999f7ab..4ee322383aa0 100644 --- a/lavamoat/browserify/beta/policy.json +++ b/lavamoat/browserify/beta/policy.json @@ -690,10 +690,10 @@ "@ethersproject/contracts": true, "@ethersproject/providers": true, "@metamask/abi-utils": true, - "@metamask/assets-controllers>@metamask/controller-utils": true, "@metamask/assets-controllers>@metamask/polling-controller": true, "@metamask/base-controller": true, "@metamask/contract-metadata": true, + "@metamask/controller-utils": true, "@metamask/eth-query": true, "@metamask/metamask-eth-abis": true, "@metamask/name-controller>async-mutex": true, @@ -707,24 +707,6 @@ "uuid": true } }, - "@metamask/assets-controllers>@metamask/controller-utils": { - "globals": { - "URL": true, - "console.error": true, - "fetch": true, - "setTimeout": true - }, - "packages": { - "@ethereumjs/tx>@ethereumjs/util": true, - "@metamask/controller-utils>@spruceid/siwe-parser": true, - "@metamask/ethjs>@metamask/ethjs-unit": true, - "@metamask/utils": true, - "bn.js": true, - "browserify>buffer": true, - "eslint>fast-deep-equal": true, - "eth-ens-namehash": true - } - }, "@metamask/assets-controllers>@metamask/polling-controller": { "globals": { "clearTimeout": true, diff --git a/lavamoat/browserify/flask/policy.json b/lavamoat/browserify/flask/policy.json index d40ce999f7ab..4ee322383aa0 100644 --- a/lavamoat/browserify/flask/policy.json +++ b/lavamoat/browserify/flask/policy.json @@ -690,10 +690,10 @@ "@ethersproject/contracts": true, "@ethersproject/providers": true, "@metamask/abi-utils": true, - "@metamask/assets-controllers>@metamask/controller-utils": true, "@metamask/assets-controllers>@metamask/polling-controller": true, "@metamask/base-controller": true, "@metamask/contract-metadata": true, + "@metamask/controller-utils": true, "@metamask/eth-query": true, "@metamask/metamask-eth-abis": true, "@metamask/name-controller>async-mutex": true, @@ -707,24 +707,6 @@ "uuid": true } }, - "@metamask/assets-controllers>@metamask/controller-utils": { - "globals": { - "URL": true, - "console.error": true, - "fetch": true, - "setTimeout": true - }, - "packages": { - "@ethereumjs/tx>@ethereumjs/util": true, - "@metamask/controller-utils>@spruceid/siwe-parser": true, - "@metamask/ethjs>@metamask/ethjs-unit": true, - "@metamask/utils": true, - "bn.js": true, - "browserify>buffer": true, - "eslint>fast-deep-equal": true, - "eth-ens-namehash": true - } - }, "@metamask/assets-controllers>@metamask/polling-controller": { "globals": { "clearTimeout": true, diff --git a/lavamoat/browserify/main/policy.json b/lavamoat/browserify/main/policy.json index d40ce999f7ab..4ee322383aa0 100644 --- a/lavamoat/browserify/main/policy.json +++ b/lavamoat/browserify/main/policy.json @@ -690,10 +690,10 @@ "@ethersproject/contracts": true, "@ethersproject/providers": true, "@metamask/abi-utils": true, - "@metamask/assets-controllers>@metamask/controller-utils": true, "@metamask/assets-controllers>@metamask/polling-controller": true, "@metamask/base-controller": true, "@metamask/contract-metadata": true, + "@metamask/controller-utils": true, "@metamask/eth-query": true, "@metamask/metamask-eth-abis": true, "@metamask/name-controller>async-mutex": true, @@ -707,24 +707,6 @@ "uuid": true } }, - "@metamask/assets-controllers>@metamask/controller-utils": { - "globals": { - "URL": true, - "console.error": true, - "fetch": true, - "setTimeout": true - }, - "packages": { - "@ethereumjs/tx>@ethereumjs/util": true, - "@metamask/controller-utils>@spruceid/siwe-parser": true, - "@metamask/ethjs>@metamask/ethjs-unit": true, - "@metamask/utils": true, - "bn.js": true, - "browserify>buffer": true, - "eslint>fast-deep-equal": true, - "eth-ens-namehash": true - } - }, "@metamask/assets-controllers>@metamask/polling-controller": { "globals": { "clearTimeout": true, diff --git a/lavamoat/browserify/mmi/policy.json b/lavamoat/browserify/mmi/policy.json index addfeb142ab3..8bf99d3a532b 100644 --- a/lavamoat/browserify/mmi/policy.json +++ b/lavamoat/browserify/mmi/policy.json @@ -782,10 +782,10 @@ "@ethersproject/contracts": true, "@ethersproject/providers": true, "@metamask/abi-utils": true, - "@metamask/assets-controllers>@metamask/controller-utils": true, "@metamask/assets-controllers>@metamask/polling-controller": true, "@metamask/base-controller": true, "@metamask/contract-metadata": true, + "@metamask/controller-utils": true, "@metamask/eth-query": true, "@metamask/metamask-eth-abis": true, "@metamask/name-controller>async-mutex": true, @@ -799,24 +799,6 @@ "uuid": true } }, - "@metamask/assets-controllers>@metamask/controller-utils": { - "globals": { - "URL": true, - "console.error": true, - "fetch": true, - "setTimeout": true - }, - "packages": { - "@ethereumjs/tx>@ethereumjs/util": true, - "@metamask/controller-utils>@spruceid/siwe-parser": true, - "@metamask/ethjs>@metamask/ethjs-unit": true, - "@metamask/utils": true, - "bn.js": true, - "browserify>buffer": true, - "eslint>fast-deep-equal": true, - "eth-ens-namehash": true - } - }, "@metamask/assets-controllers>@metamask/polling-controller": { "globals": { "clearTimeout": true, From ae198fb0f135ff4641c725357631b4d72f2d006e Mon Sep 17 00:00:00 2001 From: Brian Bergeron Date: Wed, 13 Nov 2024 13:48:55 -0800 Subject: [PATCH 07/23] lint --- ui/hooks/useTokenListPolling.test.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/ui/hooks/useTokenListPolling.test.ts b/ui/hooks/useTokenListPolling.test.ts index efe373c5d29f..9e8b4c8ae3d7 100644 --- a/ui/hooks/useTokenListPolling.test.ts +++ b/ui/hooks/useTokenListPolling.test.ts @@ -1,4 +1,3 @@ -import { renderWithProvider } from '../../test/jest'; import { renderHookWithProvider } from '../../test/lib/render-helpers'; import { tokenListStartPolling, From b090b3c1a1d563a554a2da1795b79d4a9ec2160e Mon Sep 17 00:00:00 2001 From: Brian Bergeron Date: Wed, 13 Nov 2024 14:04:52 -0800 Subject: [PATCH 08/23] lint and fix test --- ui/hooks/useTokenDetectionPolling.ts | 3 +-- ui/hooks/useTokenListPolling.test.ts | 5 ++++- ui/hooks/useTokenListPolling.ts | 3 +-- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/ui/hooks/useTokenDetectionPolling.ts b/ui/hooks/useTokenDetectionPolling.ts index e04562cf3965..95c9db9d257d 100644 --- a/ui/hooks/useTokenDetectionPolling.ts +++ b/ui/hooks/useTokenDetectionPolling.ts @@ -20,8 +20,7 @@ const useTokenDetectionPolling = () => { input: useTokenDetection ? [chainIds] : [], }); - return { - }; + return {}; }; export default useTokenDetectionPolling; diff --git a/ui/hooks/useTokenListPolling.test.ts b/ui/hooks/useTokenListPolling.test.ts index 9e8b4c8ae3d7..46473c8656b7 100644 --- a/ui/hooks/useTokenListPolling.test.ts +++ b/ui/hooks/useTokenListPolling.test.ts @@ -17,7 +17,10 @@ jest.mock('../store/actions', () => ({ })); describe('useTokenListPolling', () => { - beforeEach(() => (mockPromises = [])); + beforeEach(() => { + mockPromises = []; + jest.clearAllMocks(); + }); it('should poll for token lists on each chain when enabled, and stop on dismount', async () => { const state = { diff --git a/ui/hooks/useTokenListPolling.ts b/ui/hooks/useTokenListPolling.ts index a8eabcd5d4e3..32c0f5a2af2e 100644 --- a/ui/hooks/useTokenListPolling.ts +++ b/ui/hooks/useTokenListPolling.ts @@ -26,8 +26,7 @@ const useTokenListPolling = () => { input: enabled ? Object.keys(networkConfigurations) : [], }); - return { - }; + return {}; }; export default useTokenListPolling; From 38fc2542dd2f4690ff13e83bc041a8ba17b54fd9 Mon Sep 17 00:00:00 2001 From: Brian Bergeron Date: Wed, 13 Nov 2024 15:58:16 -0800 Subject: [PATCH 09/23] onboarding --- ui/hooks/useTokenDetectionPolling.ts | 7 +++++-- ui/hooks/useTokenListPolling.test.ts | 24 ++++++++++++++++++++++++ ui/hooks/useTokenListPolling.ts | 8 +++++++- 3 files changed, 36 insertions(+), 3 deletions(-) diff --git a/ui/hooks/useTokenDetectionPolling.ts b/ui/hooks/useTokenDetectionPolling.ts index 95c9db9d257d..50eca07469d2 100644 --- a/ui/hooks/useTokenDetectionPolling.ts +++ b/ui/hooks/useTokenDetectionPolling.ts @@ -7,17 +7,20 @@ import { tokenDetectionStartPolling, tokenDetectionStopPollingByPollingToken, } from '../store/actions'; +import { getCompletedOnboarding } from '../ducks/metamask/metamask'; import useMultiPolling from './useMultiPolling'; const useTokenDetectionPolling = () => { const useTokenDetection = useSelector(getUseTokenDetection); + const completedOnboarding = useSelector(getCompletedOnboarding); const networkConfigurations = useSelector(getNetworkConfigurationsByChainId); - const chainIds = Object.keys(networkConfigurations); + + const enabled = completedOnboarding && useTokenDetection; useMultiPolling({ startPolling: tokenDetectionStartPolling, stopPollingByPollingToken: tokenDetectionStopPollingByPollingToken, - input: useTokenDetection ? [chainIds] : [], + input: enabled ? [Object.keys(networkConfigurations)] : [], }); return {}; diff --git a/ui/hooks/useTokenListPolling.test.ts b/ui/hooks/useTokenListPolling.test.ts index 46473c8656b7..bf30342b29c9 100644 --- a/ui/hooks/useTokenListPolling.test.ts +++ b/ui/hooks/useTokenListPolling.test.ts @@ -25,6 +25,8 @@ describe('useTokenListPolling', () => { it('should poll for token lists on each chain when enabled, and stop on dismount', async () => { const state = { metamask: { + completedOnboarding: true, + useExternalServices: true, useTokenDetection: true, networkConfigurationsByChainId: { '0x1': {}, @@ -55,10 +57,32 @@ describe('useTokenListPolling', () => { ); }); + it('should not poll before onboarding is completed', async () => { + const state = { + metamask: { + completedOnboarding: false, + useExternalServices: true, + useTokenDetection: true, + networkConfigurationsByChainId: { + '0x1': {}, + '0x89': {}, + }, + }, + }; + + renderHookWithProvider(() => useTokenListPolling(), state); + + await Promise.all(mockPromises); + expect(tokenListStartPolling).toHaveBeenCalledTimes(0); + expect(tokenListStopPollingByPollingToken).toHaveBeenCalledTimes(0); + }); + it('should not poll when disabled', async () => { // disabled when detection, petnames, and simulations are all disabled const state = { metamask: { + completedOnboarding: true, + useExternalServices: true, useTokenDetection: false, useTransactionSimulations: false, preferences: { diff --git a/ui/hooks/useTokenListPolling.ts b/ui/hooks/useTokenListPolling.ts index 32c0f5a2af2e..8305ebf0ee0a 100644 --- a/ui/hooks/useTokenListPolling.ts +++ b/ui/hooks/useTokenListPolling.ts @@ -2,6 +2,7 @@ import { useSelector } from 'react-redux'; import { getNetworkConfigurationsByChainId, getPetnamesEnabled, + getUseExternalServices, getUseTokenDetection, getUseTransactionSimulations, } from '../selectors'; @@ -9,6 +10,7 @@ import { tokenListStartPolling, tokenListStopPollingByPollingToken, } from '../store/actions'; +import { getCompletedOnboarding } from '../ducks/metamask/metamask'; import useMultiPolling from './useMultiPolling'; const useTokenListPolling = () => { @@ -16,9 +18,13 @@ const useTokenListPolling = () => { const useTokenDetection = useSelector(getUseTokenDetection); const useTransactionSimulations = useSelector(getUseTransactionSimulations); const petnamesEnabled = useSelector(getPetnamesEnabled); + const completedOnboarding = useSelector(getCompletedOnboarding); + const useExternalServices = useSelector(getUseExternalServices); const enabled = - useTokenDetection || petnamesEnabled || useTransactionSimulations; + completedOnboarding && + useExternalServices && + (useTokenDetection || petnamesEnabled || useTransactionSimulations); useMultiPolling({ startPolling: tokenListStartPolling, From 6338a00ac21e9f1b7ec66214cb42ea490ce343cf Mon Sep 17 00:00:00 2001 From: Brian Bergeron Date: Wed, 13 Nov 2024 16:41:25 -0800 Subject: [PATCH 10/23] fix race condition --- .../onboarding-flow/pin-extension/pin-extension.js | 4 +++- ui/store/actions.ts | 13 +++++++------ 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/ui/pages/onboarding-flow/pin-extension/pin-extension.js b/ui/pages/onboarding-flow/pin-extension/pin-extension.js index c9ad1806d49d..775095c2aa61 100644 --- a/ui/pages/onboarding-flow/pin-extension/pin-extension.js +++ b/ui/pages/onboarding-flow/pin-extension/pin-extension.js @@ -69,7 +69,9 @@ export default function OnboardingPinExtension() { if (selectedIndex === 0) { setSelectedIndex(1); } else { - dispatch(toggleExternalServices(externalServicesOnboardingToggleState)); + await dispatch( + toggleExternalServices(externalServicesOnboardingToggleState), + ); await dispatch(setCompletedOnboarding()); if (externalServicesOnboardingToggleState) { diff --git a/ui/store/actions.ts b/ui/store/actions.ts index c4e546b8fd8e..32434d3a1d59 100644 --- a/ui/store/actions.ts +++ b/ui/store/actions.ts @@ -3548,13 +3548,14 @@ export function setIpfsGateway( export function toggleExternalServices( val: boolean, ): ThunkAction { - return (dispatch: MetaMaskReduxDispatch) => { + return async (dispatch: MetaMaskReduxDispatch) => { log.debug(`background.toggleExternalServices`); - callBackgroundMethod('toggleExternalServices', [val], (err) => { - if (err) { - dispatch(displayWarning(err)); - } - }); + try { + await submitRequestToBackground('toggleExternalServices', [val]); + await forceUpdateMetamaskState(dispatch); + } catch (err) { + dispatch(displayWarning(err)); + } }; } From 6b22521820eb0ce6c36c1c1fea9f9db3980e6b20 Mon Sep 17 00:00:00 2001 From: Brian Bergeron Date: Wed, 13 Nov 2024 17:43:13 -0800 Subject: [PATCH 11/23] temporarily disable token detection polling --- ui/contexts/assetPolling.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/contexts/assetPolling.tsx b/ui/contexts/assetPolling.tsx index be1954a37ec5..eb78245d4578 100644 --- a/ui/contexts/assetPolling.tsx +++ b/ui/contexts/assetPolling.tsx @@ -10,7 +10,7 @@ import useTokenListPolling from '../hooks/useTokenListPolling'; export const AssetPollingProvider = ({ children }: { children: ReactNode }) => { useCurrencyRatePolling(); useTokenRatesPolling(); - useTokenDetectionPolling(); + // useTokenDetectionPolling(); useTokenListPolling(); return <>{children}; From 5206a35de52d3843cebd10ef98e9bf9f4ef11158 Mon Sep 17 00:00:00 2001 From: Brian Bergeron Date: Wed, 13 Nov 2024 18:58:50 -0800 Subject: [PATCH 12/23] patch --- ...-assets-controllers-patch-9e00573eb4.patch | 40 ++++++++++++++++++ app/scripts/metamask-controller.js | 4 +- ui/contexts/assetPolling.tsx | 2 +- yarn.lock | 42 ++++++++++++++++++- 4 files changed, 83 insertions(+), 5 deletions(-) create mode 100644 .yarn/patches/@metamask-assets-controllers-patch-9e00573eb4.patch diff --git a/.yarn/patches/@metamask-assets-controllers-patch-9e00573eb4.patch b/.yarn/patches/@metamask-assets-controllers-patch-9e00573eb4.patch new file mode 100644 index 000000000000..7d128ac520cc --- /dev/null +++ b/.yarn/patches/@metamask-assets-controllers-patch-9e00573eb4.patch @@ -0,0 +1,40 @@ +diff --git a/dist/TokenDetectionController.cjs b/dist/TokenDetectionController.cjs +index ab23c95d667357db365f925c4c4acce4736797f8..54916fd37337e79c344bdcd1eac764bd547ab117 100644 +--- a/dist/TokenDetectionController.cjs ++++ b/dist/TokenDetectionController.cjs +@@ -203,14 +203,7 @@ class TokenDetectionController extends (0, polling_controller_1.StaticIntervalPo + const { chainsToDetectUsingRpc, chainsToDetectUsingAccountAPI } = __classPrivateFieldGet(this, _TokenDetectionController_instances, "m", _TokenDetectionController_getChainsToDetect).call(this, clientNetworks, supportedNetworks); + // Try detecting tokens via Account API first if conditions allow + if (supportedNetworks && chainsToDetectUsingAccountAPI.length > 0) { +- const apiResult = await __classPrivateFieldGet(this, _TokenDetectionController_instances, "m", _TokenDetectionController_attemptAccountAPIDetection).call(this, chainsToDetectUsingAccountAPI, addressToDetect, supportedNetworks); +- // If API succeeds and no chains are left for RPC detection, we can return early +- if (apiResult?.result === 'success' && +- chainsToDetectUsingRpc.length === 0) { +- return; +- } +- // If API fails or chainsToDetectUsingRpc still has items, add chains to RPC detection +- __classPrivateFieldGet(this, _TokenDetectionController_instances, "m", _TokenDetectionController_addChainsToRpcDetection).call(this, chainsToDetectUsingRpc, chainsToDetectUsingAccountAPI, clientNetworks); ++ await __classPrivateFieldGet(this, _TokenDetectionController_instances, "m", _TokenDetectionController_attemptAccountAPIDetection).call(this, chainsToDetectUsingAccountAPI, addressToDetect, supportedNetworks); + } + // Proceed with RPC detection if there are chains remaining in chainsToDetectUsingRpc + if (chainsToDetectUsingRpc.length > 0) { +diff --git a/dist/TokenDetectionController.mjs b/dist/TokenDetectionController.mjs +index f75eb5c2c74f2a9d15a79760985111171dc938e1..983acd0a63bac2ceedc9ed4896d1a342ac71c990 100644 +--- a/dist/TokenDetectionController.mjs ++++ b/dist/TokenDetectionController.mjs +@@ -204,14 +204,7 @@ export class TokenDetectionController extends StaticIntervalPollingController() + const { chainsToDetectUsingRpc, chainsToDetectUsingAccountAPI } = __classPrivateFieldGet(this, _TokenDetectionController_instances, "m", _TokenDetectionController_getChainsToDetect).call(this, clientNetworks, supportedNetworks); + // Try detecting tokens via Account API first if conditions allow + if (supportedNetworks && chainsToDetectUsingAccountAPI.length > 0) { +- const apiResult = await __classPrivateFieldGet(this, _TokenDetectionController_instances, "m", _TokenDetectionController_attemptAccountAPIDetection).call(this, chainsToDetectUsingAccountAPI, addressToDetect, supportedNetworks); +- // If API succeeds and no chains are left for RPC detection, we can return early +- if (apiResult?.result === 'success' && +- chainsToDetectUsingRpc.length === 0) { +- return; +- } +- // If API fails or chainsToDetectUsingRpc still has items, add chains to RPC detection +- __classPrivateFieldGet(this, _TokenDetectionController_instances, "m", _TokenDetectionController_addChainsToRpcDetection).call(this, chainsToDetectUsingRpc, chainsToDetectUsingAccountAPI, clientNetworks); ++ await __classPrivateFieldGet(this, _TokenDetectionController_instances, "m", _TokenDetectionController_attemptAccountAPIDetection).call(this, chainsToDetectUsingAccountAPI, addressToDetect, supportedNetworks); + } + // Proceed with RPC detection if there are chains remaining in chainsToDetectUsingRpc + if (chainsToDetectUsingRpc.length > 0) { diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index a558ff055ab2..f1083ab6cd43 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -2600,7 +2600,7 @@ export default class MetamaskController extends EventEmitter { triggerNetworkrequests() { this.accountTrackerController.start(); this.txController.startIncomingTransactionPolling(); - // this.tokenDetectionController.enable(); + this.tokenDetectionController.enable(); // const preferencesControllerState = this.preferencesController.state; @@ -2612,7 +2612,7 @@ export default class MetamaskController extends EventEmitter { stopNetworkRequests() { this.accountTrackerController.stop(); this.txController.stopIncomingTransactionPolling(); - // this.tokenDetectionController.disable(); + this.tokenDetectionController.disable(); // const preferencesControllerState = this.preferencesController.state; diff --git a/ui/contexts/assetPolling.tsx b/ui/contexts/assetPolling.tsx index eb78245d4578..be1954a37ec5 100644 --- a/ui/contexts/assetPolling.tsx +++ b/ui/contexts/assetPolling.tsx @@ -10,7 +10,7 @@ import useTokenListPolling from '../hooks/useTokenListPolling'; export const AssetPollingProvider = ({ children }: { children: ReactNode }) => { useCurrencyRatePolling(); useTokenRatesPolling(); - // useTokenDetectionPolling(); + useTokenDetectionPolling(); useTokenListPolling(); return <>{children}; diff --git a/yarn.lock b/yarn.lock index 67bc765ad824..4a5e23bd2a19 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4972,7 +4972,7 @@ __metadata: languageName: node linkType: hard -"@metamask/assets-controllers@patch:@metamask/assets-controllers@npm%3A43.1.1#~/.yarn/patches/@metamask-assets-controllers-npm-43.1.1-c223d56176.patch": +"@metamask/assets-controllers@patch:@metamask/assets-controllers@npm%3A43.1.1#~/.yarn/patches/@metamask-assets-controllers-npm-43.1.1-c223d56176.patch::version=43.1.1&hash=5a94c2": version: 43.1.1 resolution: "@metamask/assets-controllers@patch:@metamask/assets-controllers@npm%3A43.1.1#~/.yarn/patches/@metamask-assets-controllers-npm-43.1.1-c223d56176.patch::version=43.1.1&hash=5a94c2" dependencies: @@ -5010,6 +5010,44 @@ __metadata: languageName: node linkType: hard +"@metamask/assets-controllers@patch:@metamask/assets-controllers@patch%3A@metamask/assets-controllers@npm%253A43.1.1%23~/.yarn/patches/@metamask-assets-controllers-npm-43.1.1-c223d56176.patch%3A%3Aversion=43.1.1&hash=5a94c2#~/.yarn/patches/@metamask-assets-controllers-patch-9e00573eb4.patch": + version: 43.1.1 + resolution: "@metamask/assets-controllers@patch:@metamask/assets-controllers@patch%3A@metamask/assets-controllers@npm%253A43.1.1%23~/.yarn/patches/@metamask-assets-controllers-npm-43.1.1-c223d56176.patch%3A%3Aversion=43.1.1&hash=5a94c2#~/.yarn/patches/@metamask-assets-controllers-patch-9e00573eb4.patch::version=43.1.1&hash=dc8e48" + dependencies: + "@ethereumjs/util": "npm:^8.1.0" + "@ethersproject/address": "npm:^5.7.0" + "@ethersproject/bignumber": "npm:^5.7.0" + "@ethersproject/contracts": "npm:^5.7.0" + "@ethersproject/providers": "npm:^5.7.0" + "@metamask/abi-utils": "npm:^2.0.3" + "@metamask/base-controller": "npm:^7.0.2" + "@metamask/contract-metadata": "npm:^2.4.0" + "@metamask/controller-utils": "npm:^11.4.3" + "@metamask/eth-query": "npm:^4.0.0" + "@metamask/metamask-eth-abis": "npm:^3.1.1" + "@metamask/polling-controller": "npm:^12.0.1" + "@metamask/rpc-errors": "npm:^7.0.1" + "@metamask/utils": "npm:^10.0.0" + "@types/bn.js": "npm:^5.1.5" + "@types/uuid": "npm:^8.3.0" + async-mutex: "npm:^0.5.0" + bn.js: "npm:^5.2.1" + cockatiel: "npm:^3.1.2" + immer: "npm:^9.0.6" + lodash: "npm:^4.17.21" + multiformats: "npm:^13.1.0" + single-call-balance-checker-abi: "npm:^1.0.0" + uuid: "npm:^8.3.2" + peerDependencies: + "@metamask/accounts-controller": ^19.0.0 + "@metamask/approval-controller": ^7.0.0 + "@metamask/keyring-controller": ^18.0.0 + "@metamask/network-controller": ^22.0.0 + "@metamask/preferences-controller": ^14.0.0 + checksum: 10/f315ebf72d8d3e53855e6c3fa5257e298e6b925d418dba5f849f22b8aa100b7c292ac2686d7a6c9acd72c55674d64824a94a09af4a311fe4f183caadef9225db + languageName: node + linkType: hard + "@metamask/auto-changelog@npm:^2.1.0": version: 2.6.1 resolution: "@metamask/auto-changelog@npm:2.6.1" @@ -26699,7 +26737,7 @@ __metadata: "@metamask/announcement-controller": "npm:^7.0.0" "@metamask/api-specs": "npm:^0.9.3" "@metamask/approval-controller": "npm:^7.0.0" - "@metamask/assets-controllers": "patch:@metamask/assets-controllers@npm%3A43.1.1#~/.yarn/patches/@metamask-assets-controllers-npm-43.1.1-c223d56176.patch" + "@metamask/assets-controllers": "patch:@metamask/assets-controllers@patch%3A@metamask/assets-controllers@npm%253A43.1.1%23~/.yarn/patches/@metamask-assets-controllers-npm-43.1.1-c223d56176.patch%3A%3Aversion=43.1.1&hash=5a94c2#~/.yarn/patches/@metamask-assets-controllers-patch-9e00573eb4.patch" "@metamask/auto-changelog": "npm:^2.1.0" "@metamask/base-controller": "npm:^7.0.0" "@metamask/bitcoin-wallet-snap": "npm:^0.8.2" From 757d880b8dd24465c9846f4d5b925aacb40ac1db Mon Sep 17 00:00:00 2001 From: Brian Bergeron Date: Wed, 13 Nov 2024 19:02:13 -0800 Subject: [PATCH 13/23] package json --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index a477e3200b0e..af02477887cc 100644 --- a/package.json +++ b/package.json @@ -292,7 +292,7 @@ "@metamask/address-book-controller": "^6.0.0", "@metamask/announcement-controller": "^7.0.0", "@metamask/approval-controller": "^7.0.0", - "@metamask/assets-controllers": "patch:@metamask/assets-controllers@npm%3A43.1.1#~/.yarn/patches/@metamask-assets-controllers-npm-43.1.1-c223d56176.patch", + "@metamask/assets-controllers": "patch:@metamask/assets-controllers@patch%3A@metamask/assets-controllers@npm%253A43.1.1%23~/.yarn/patches/@metamask-assets-controllers-npm-43.1.1-c223d56176.patch%3A%3Aversion=43.1.1&hash=5a94c2#~/.yarn/patches/@metamask-assets-controllers-patch-9e00573eb4.patch", "@metamask/base-controller": "^7.0.0", "@metamask/bitcoin-wallet-snap": "^0.8.2", "@metamask/browser-passworder": "^4.3.0", From abf8e110ba3222863705a71003139a23440c8d11 Mon Sep 17 00:00:00 2001 From: Brian Bergeron Date: Wed, 13 Nov 2024 20:10:02 -0800 Subject: [PATCH 14/23] fix unit test --- .../pin-extension/pin-extension.test.js | 36 +++++++++++++------ 1 file changed, 25 insertions(+), 11 deletions(-) diff --git a/ui/pages/onboarding-flow/pin-extension/pin-extension.test.js b/ui/pages/onboarding-flow/pin-extension/pin-extension.test.js index 00c7c38cf1d0..22be22bd98ac 100644 --- a/ui/pages/onboarding-flow/pin-extension/pin-extension.test.js +++ b/ui/pages/onboarding-flow/pin-extension/pin-extension.test.js @@ -3,15 +3,31 @@ import { fireEvent } from '@testing-library/react'; import reactRouterDom from 'react-router-dom'; import configureMockStore from 'redux-mock-store'; import thunk from 'redux-thunk'; -import { setBackgroundConnection } from '../../../store/background-connection'; import { renderWithProvider } from '../../../../test/jest'; +import { + setCompletedOnboarding, + toggleExternalServices, +} from '../../../store/actions'; import PinExtension from './pin-extension'; -const completeOnboardingStub = jest - .fn() - .mockImplementation(() => Promise.resolve()); +jest.mock('../../../store/actions', () => ({ + toggleExternalServices: jest.fn(), + setCompletedOnboarding: jest.fn(), + performSignIn: jest.fn(), +})); + +const mockPromises = []; + +const mockDispatch = jest.fn().mockImplementation(() => { + const promise = Promise.resolve(); + mockPromises.push(promise); + return promise; +}); -const toggleExternalServicesStub = jest.fn(); +jest.mock('react-redux', () => ({ + ...jest.requireActual('react-redux'), + useDispatch: () => mockDispatch, +})); jest.mock('react-router-dom', () => ({ ...jest.requireActual('react-router-dom'), @@ -30,10 +46,6 @@ describe('Creation Successful Onboarding View', () => { }, }; const store = configureMockStore([thunk])(mockStore); - setBackgroundConnection({ - completeOnboarding: completeOnboardingStub, - toggleExternalServices: toggleExternalServicesStub, - }); const pushMock = jest.fn(); beforeAll(() => { @@ -43,12 +55,14 @@ describe('Creation Successful Onboarding View', () => { .mockReturnValue({ push: pushMock }); }); - it('should call completeOnboarding in the background when Done" button is clicked', () => { + it('should call completeOnboarding in the background when Done" button is clicked', async () => { const { getByText } = renderWithProvider(, store); const nextButton = getByText('Next'); fireEvent.click(nextButton); const gotItButton = getByText('Done'); fireEvent.click(gotItButton); - expect(completeOnboardingStub).toHaveBeenCalledTimes(1); + await Promise.all(mockPromises); + expect(toggleExternalServices).toHaveBeenCalledTimes(1); + expect(setCompletedOnboarding).toHaveBeenCalledTimes(1); }); }); From c662098f77c15415fe85906daaa6b43f373671f0 Mon Sep 17 00:00:00 2001 From: Brian Bergeron Date: Wed, 13 Nov 2024 20:35:26 -0800 Subject: [PATCH 15/23] update sentry state --- .../errors-after-init-opt-in-background-state.json | 7 +++++-- .../state-snapshots/errors-after-init-opt-in-ui-state.json | 7 +++++-- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/test/e2e/tests/metrics/state-snapshots/errors-after-init-opt-in-background-state.json b/test/e2e/tests/metrics/state-snapshots/errors-after-init-opt-in-background-state.json index bb1640d99365..4da6eb4f871d 100644 --- a/test/e2e/tests/metrics/state-snapshots/errors-after-init-opt-in-background-state.json +++ b/test/e2e/tests/metrics/state-snapshots/errors-after-init-opt-in-background-state.json @@ -145,8 +145,11 @@ "MultichainBalancesController": { "balances": "object" }, "MultichainRatesController": { "fiatCurrency": "usd", - "rates": { "btc": { "conversionDate": 0, "conversionRate": 0 } }, - "cryptocurrencies": ["btc"] + "rates": { + "btc": { "conversionDate": 0, "conversionRate": 0 }, + "sol": { "conversionDate": 0, "conversionRate": 0 } + }, + "cryptocurrencies": ["btc", "sol"] }, "NameController": { "names": "object", "nameSources": "object" }, "NetworkController": { diff --git a/test/e2e/tests/metrics/state-snapshots/errors-after-init-opt-in-ui-state.json b/test/e2e/tests/metrics/state-snapshots/errors-after-init-opt-in-ui-state.json index 3d36d5fc7592..160882703e04 100644 --- a/test/e2e/tests/metrics/state-snapshots/errors-after-init-opt-in-ui-state.json +++ b/test/e2e/tests/metrics/state-snapshots/errors-after-init-opt-in-ui-state.json @@ -198,8 +198,11 @@ "lastFetchedBlockNumbers": "object", "submitHistory": "object", "fiatCurrency": "usd", - "rates": { "btc": { "conversionDate": 0, "conversionRate": 0 } }, - "cryptocurrencies": ["btc"], + "rates": { + "btc": { "conversionDate": 0, "conversionRate": 0 }, + "sol": { "conversionDate": 0, "conversionRate": 0 } + }, + "cryptocurrencies": ["btc", "sol"], "snaps": "object", "jobs": "object", "database": null, From 8f1cfb19bc296cfc39885ff78566fa1396784bb6 Mon Sep 17 00:00:00 2001 From: Brian Bergeron Date: Wed, 13 Nov 2024 20:55:54 -0800 Subject: [PATCH 16/23] sentry state --- .../errors-after-init-opt-in-background-state.json | 8 +++++++- .../errors-after-init-opt-in-ui-state.json | 8 +++++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/test/e2e/tests/metrics/state-snapshots/errors-after-init-opt-in-background-state.json b/test/e2e/tests/metrics/state-snapshots/errors-after-init-opt-in-background-state.json index 4da6eb4f871d..999dce99ca0c 100644 --- a/test/e2e/tests/metrics/state-snapshots/errors-after-init-opt-in-background-state.json +++ b/test/e2e/tests/metrics/state-snapshots/errors-after-init-opt-in-background-state.json @@ -316,7 +316,13 @@ }, "TokenListController": { "tokenList": "object", - "tokensChainsCache": {}, + "tokensChainsCache": { + "0x1": "object", + "0x539": "object", + "0xaa36a7": "object", + "0xe705": "object", + "0xe708": "object" + }, "preventPollingOnNetworkRestart": false }, "TokenRatesController": { "marketData": "object" }, diff --git a/test/e2e/tests/metrics/state-snapshots/errors-after-init-opt-in-ui-state.json b/test/e2e/tests/metrics/state-snapshots/errors-after-init-opt-in-ui-state.json index 160882703e04..acd9d6f8d074 100644 --- a/test/e2e/tests/metrics/state-snapshots/errors-after-init-opt-in-ui-state.json +++ b/test/e2e/tests/metrics/state-snapshots/errors-after-init-opt-in-ui-state.json @@ -174,7 +174,13 @@ "gasEstimateType": "none", "nonRPCGasFeeApisDisabled": "boolean", "tokenList": "object", - "tokensChainsCache": {}, + "tokensChainsCache": { + "0x1": "object", + "0x539": "object", + "0xaa36a7": "object", + "0xe705": "object", + "0xe708": "object" + }, "preventPollingOnNetworkRestart": false, "tokens": "object", "ignoredTokens": "object", From 729d07d6ad32b60c02075f88688034e96f0ef3a3 Mon Sep 17 00:00:00 2001 From: Brian Bergeron Date: Thu, 14 Nov 2024 13:52:50 -0800 Subject: [PATCH 17/23] update patch --- ...-assets-controllers-patch-9e00573eb4.patch | 46 ++++++++++++++----- yarn.lock | 4 +- 2 files changed, 36 insertions(+), 14 deletions(-) diff --git a/.yarn/patches/@metamask-assets-controllers-patch-9e00573eb4.patch b/.yarn/patches/@metamask-assets-controllers-patch-9e00573eb4.patch index 7d128ac520cc..1b9e5a4ba848 100644 --- a/.yarn/patches/@metamask-assets-controllers-patch-9e00573eb4.patch +++ b/.yarn/patches/@metamask-assets-controllers-patch-9e00573eb4.patch @@ -1,40 +1,62 @@ diff --git a/dist/TokenDetectionController.cjs b/dist/TokenDetectionController.cjs -index ab23c95d667357db365f925c4c4acce4736797f8..54916fd37337e79c344bdcd1eac764bd547ab117 100644 +index ab23c95d667357db365f925c4c4acce4736797f8..8fd5efde7a3c24080f8a43f79d10300e8c271245 100644 --- a/dist/TokenDetectionController.cjs +++ b/dist/TokenDetectionController.cjs -@@ -203,14 +203,7 @@ class TokenDetectionController extends (0, polling_controller_1.StaticIntervalPo - const { chainsToDetectUsingRpc, chainsToDetectUsingAccountAPI } = __classPrivateFieldGet(this, _TokenDetectionController_instances, "m", _TokenDetectionController_getChainsToDetect).call(this, clientNetworks, supportedNetworks); +@@ -204,13 +204,10 @@ class TokenDetectionController extends (0, polling_controller_1.StaticIntervalPo // Try detecting tokens via Account API first if conditions allow if (supportedNetworks && chainsToDetectUsingAccountAPI.length > 0) { -- const apiResult = await __classPrivateFieldGet(this, _TokenDetectionController_instances, "m", _TokenDetectionController_attemptAccountAPIDetection).call(this, chainsToDetectUsingAccountAPI, addressToDetect, supportedNetworks); + const apiResult = await __classPrivateFieldGet(this, _TokenDetectionController_instances, "m", _TokenDetectionController_attemptAccountAPIDetection).call(this, chainsToDetectUsingAccountAPI, addressToDetect, supportedNetworks); - // If API succeeds and no chains are left for RPC detection, we can return early - if (apiResult?.result === 'success' && - chainsToDetectUsingRpc.length === 0) { - return; -- } ++ // If the account API call failed, have those chains fall back to RPC detection ++ if (apiResult?.result === 'failed') { ++ __classPrivateFieldGet(this, _TokenDetectionController_instances, "m", _TokenDetectionController_addChainsToRpcDetection).call(this, chainsToDetectUsingRpc, chainsToDetectUsingAccountAPI, clientNetworks); + } - // If API fails or chainsToDetectUsingRpc still has items, add chains to RPC detection - __classPrivateFieldGet(this, _TokenDetectionController_instances, "m", _TokenDetectionController_addChainsToRpcDetection).call(this, chainsToDetectUsingRpc, chainsToDetectUsingAccountAPI, clientNetworks); -+ await __classPrivateFieldGet(this, _TokenDetectionController_instances, "m", _TokenDetectionController_attemptAccountAPIDetection).call(this, chainsToDetectUsingAccountAPI, addressToDetect, supportedNetworks); } // Proceed with RPC detection if there are chains remaining in chainsToDetectUsingRpc if (chainsToDetectUsingRpc.length > 0) { +@@ -446,8 +443,7 @@ async function _TokenDetectionController_addDetectedTokensViaAPI({ selectedAddre + const tokenBalancesByChain = await __classPrivateFieldGet(this, _TokenDetectionController_accountsAPI, "f") + .getMultiNetworksBalances(selectedAddress, chainIds, supportedNetworks) + .catch(() => null); +- if (!tokenBalancesByChain || +- Object.keys(tokenBalancesByChain).length === 0) { ++ if (tokenBalancesByChain === null) { + return { result: 'failed' }; + } + // Process each chain ID individually diff --git a/dist/TokenDetectionController.mjs b/dist/TokenDetectionController.mjs -index f75eb5c2c74f2a9d15a79760985111171dc938e1..983acd0a63bac2ceedc9ed4896d1a342ac71c990 100644 +index f75eb5c2c74f2a9d15a79760985111171dc938e1..ebc30bb915cc39dabf49f9e0da84a7948ae1ed48 100644 --- a/dist/TokenDetectionController.mjs +++ b/dist/TokenDetectionController.mjs -@@ -204,14 +204,7 @@ export class TokenDetectionController extends StaticIntervalPollingController() - const { chainsToDetectUsingRpc, chainsToDetectUsingAccountAPI } = __classPrivateFieldGet(this, _TokenDetectionController_instances, "m", _TokenDetectionController_getChainsToDetect).call(this, clientNetworks, supportedNetworks); +@@ -205,13 +205,10 @@ export class TokenDetectionController extends StaticIntervalPollingController() // Try detecting tokens via Account API first if conditions allow if (supportedNetworks && chainsToDetectUsingAccountAPI.length > 0) { -- const apiResult = await __classPrivateFieldGet(this, _TokenDetectionController_instances, "m", _TokenDetectionController_attemptAccountAPIDetection).call(this, chainsToDetectUsingAccountAPI, addressToDetect, supportedNetworks); + const apiResult = await __classPrivateFieldGet(this, _TokenDetectionController_instances, "m", _TokenDetectionController_attemptAccountAPIDetection).call(this, chainsToDetectUsingAccountAPI, addressToDetect, supportedNetworks); - // If API succeeds and no chains are left for RPC detection, we can return early - if (apiResult?.result === 'success' && - chainsToDetectUsingRpc.length === 0) { - return; -- } ++ // If the account API call failed, have those chains fall back to RPC detection ++ if (apiResult?.result === 'failed') { ++ __classPrivateFieldGet(this, _TokenDetectionController_instances, "m", _TokenDetectionController_addChainsToRpcDetection).call(this, chainsToDetectUsingRpc, chainsToDetectUsingAccountAPI, clientNetworks); + } - // If API fails or chainsToDetectUsingRpc still has items, add chains to RPC detection - __classPrivateFieldGet(this, _TokenDetectionController_instances, "m", _TokenDetectionController_addChainsToRpcDetection).call(this, chainsToDetectUsingRpc, chainsToDetectUsingAccountAPI, clientNetworks); -+ await __classPrivateFieldGet(this, _TokenDetectionController_instances, "m", _TokenDetectionController_attemptAccountAPIDetection).call(this, chainsToDetectUsingAccountAPI, addressToDetect, supportedNetworks); } // Proceed with RPC detection if there are chains remaining in chainsToDetectUsingRpc if (chainsToDetectUsingRpc.length > 0) { +@@ -446,8 +443,7 @@ async function _TokenDetectionController_addDetectedTokensViaAPI({ selectedAddre + const tokenBalancesByChain = await __classPrivateFieldGet(this, _TokenDetectionController_accountsAPI, "f") + .getMultiNetworksBalances(selectedAddress, chainIds, supportedNetworks) + .catch(() => null); +- if (!tokenBalancesByChain || +- Object.keys(tokenBalancesByChain).length === 0) { ++ if (tokenBalancesByChain === null) { + return { result: 'failed' }; + } + // Process each chain ID individually diff --git a/yarn.lock b/yarn.lock index 4780951a96d1..8d622a3d2d25 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5012,7 +5012,7 @@ __metadata: "@metamask/assets-controllers@patch:@metamask/assets-controllers@patch%3A@metamask/assets-controllers@npm%253A43.1.1%23~/.yarn/patches/@metamask-assets-controllers-npm-43.1.1-c223d56176.patch%3A%3Aversion=43.1.1&hash=5a94c2#~/.yarn/patches/@metamask-assets-controllers-patch-9e00573eb4.patch": version: 43.1.1 - resolution: "@metamask/assets-controllers@patch:@metamask/assets-controllers@patch%3A@metamask/assets-controllers@npm%253A43.1.1%23~/.yarn/patches/@metamask-assets-controllers-npm-43.1.1-c223d56176.patch%3A%3Aversion=43.1.1&hash=5a94c2#~/.yarn/patches/@metamask-assets-controllers-patch-9e00573eb4.patch::version=43.1.1&hash=dc8e48" + resolution: "@metamask/assets-controllers@patch:@metamask/assets-controllers@patch%3A@metamask/assets-controllers@npm%253A43.1.1%23~/.yarn/patches/@metamask-assets-controllers-npm-43.1.1-c223d56176.patch%3A%3Aversion=43.1.1&hash=5a94c2#~/.yarn/patches/@metamask-assets-controllers-patch-9e00573eb4.patch::version=43.1.1&hash=c4e407" dependencies: "@ethereumjs/util": "npm:^8.1.0" "@ethersproject/address": "npm:^5.7.0" @@ -5044,7 +5044,7 @@ __metadata: "@metamask/keyring-controller": ^18.0.0 "@metamask/network-controller": ^22.0.0 "@metamask/preferences-controller": ^14.0.0 - checksum: 10/f315ebf72d8d3e53855e6c3fa5257e298e6b925d418dba5f849f22b8aa100b7c292ac2686d7a6c9acd72c55674d64824a94a09af4a311fe4f183caadef9225db + checksum: 10/189f45b9afefef9dc54f29920eb64fcebbc1dd13d792627ab2649c973a94ca310c848411fe4b294419c881d1956ff50bd6107f7411faa2a953da005662269e40 languageName: node linkType: hard From 21fcda30fd7756cbffc99521c8ebdd2a69a16f83 Mon Sep 17 00:00:00 2001 From: Brian Bergeron Date: Thu, 14 Nov 2024 13:53:28 -0800 Subject: [PATCH 18/23] dont poll when locked --- ui/hooks/useTokenDetectionPolling.ts | 8 ++++++-- ui/hooks/useTokenListPolling.ts | 7 ++++++- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/ui/hooks/useTokenDetectionPolling.ts b/ui/hooks/useTokenDetectionPolling.ts index 50eca07469d2..790384e21cbf 100644 --- a/ui/hooks/useTokenDetectionPolling.ts +++ b/ui/hooks/useTokenDetectionPolling.ts @@ -7,15 +7,19 @@ import { tokenDetectionStartPolling, tokenDetectionStopPollingByPollingToken, } from '../store/actions'; -import { getCompletedOnboarding } from '../ducks/metamask/metamask'; +import { + getCompletedOnboarding, + getIsUnlocked, +} from '../ducks/metamask/metamask'; import useMultiPolling from './useMultiPolling'; const useTokenDetectionPolling = () => { const useTokenDetection = useSelector(getUseTokenDetection); const completedOnboarding = useSelector(getCompletedOnboarding); + const isUnlocked = useSelector(getIsUnlocked); const networkConfigurations = useSelector(getNetworkConfigurationsByChainId); - const enabled = completedOnboarding && useTokenDetection; + const enabled = completedOnboarding && isUnlocked && useTokenDetection; useMultiPolling({ startPolling: tokenDetectionStartPolling, diff --git a/ui/hooks/useTokenListPolling.ts b/ui/hooks/useTokenListPolling.ts index 8305ebf0ee0a..9b43c3c6959a 100644 --- a/ui/hooks/useTokenListPolling.ts +++ b/ui/hooks/useTokenListPolling.ts @@ -10,7 +10,10 @@ import { tokenListStartPolling, tokenListStopPollingByPollingToken, } from '../store/actions'; -import { getCompletedOnboarding } from '../ducks/metamask/metamask'; +import { + getCompletedOnboarding, + getIsUnlocked, +} from '../ducks/metamask/metamask'; import useMultiPolling from './useMultiPolling'; const useTokenListPolling = () => { @@ -19,10 +22,12 @@ const useTokenListPolling = () => { const useTransactionSimulations = useSelector(getUseTransactionSimulations); const petnamesEnabled = useSelector(getPetnamesEnabled); const completedOnboarding = useSelector(getCompletedOnboarding); + const isUnlocked = useSelector(getIsUnlocked); const useExternalServices = useSelector(getUseExternalServices); const enabled = completedOnboarding && + isUnlocked && useExternalServices && (useTokenDetection || petnamesEnabled || useTransactionSimulations); From 8b6391f0a18df41efeae7e9773f9974e088bd6b9 Mon Sep 17 00:00:00 2001 From: Brian Bergeron Date: Thu, 14 Nov 2024 14:01:20 -0800 Subject: [PATCH 19/23] remove commented out code --- app/scripts/metamask-controller.js | 21 --------------------- 1 file changed, 21 deletions(-) diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index 8eb0119e58b5..622f4715aaad 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -2606,24 +2606,12 @@ export default class MetamaskController extends EventEmitter { this.accountTrackerController.start(); this.txController.startIncomingTransactionPolling(); this.tokenDetectionController.enable(); - - // const preferencesControllerState = this.preferencesController.state; - - // if (this.#isTokenListPollingRequired(preferencesControllerState)) { - // this.tokenListController.start(); - // } } stopNetworkRequests() { this.accountTrackerController.stop(); this.txController.stopIncomingTransactionPolling(); this.tokenDetectionController.disable(); - - // const preferencesControllerState = this.preferencesController.state; - - // if (this.#isTokenListPollingRequired(preferencesControllerState)) { - // this.tokenListController.stop(); - // } } resetStates(resetMethods) { @@ -7228,15 +7216,6 @@ export default class MetamaskController extends EventEmitter { } this.tokenListController.updatePreventPollingOnNetworkRestart(!newEnabled); - - // if (newEnabled) { - // log.debug('Started token list controller polling'); - // this.tokenListController.start(); - // } else { - // log.debug('Stopped token list controller polling'); - // this.tokenListController.clearingTokenListData(); - // this.tokenListController.stop(); - // } } #isTokenListPollingRequired(preferencesControllerState) { From b635ab846884e6de139f2f27ec4aee5b25723a36 Mon Sep 17 00:00:00 2001 From: Brian Bergeron Date: Thu, 14 Nov 2024 14:58:35 -0800 Subject: [PATCH 20/23] fix e2e tests --- test/e2e/tests/tokens/add-hide-token.spec.js | 9 ++++++++ test/e2e/tests/tokens/import-tokens.spec.js | 23 +++++++++++++++++++- 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/test/e2e/tests/tokens/add-hide-token.spec.js b/test/e2e/tests/tokens/add-hide-token.spec.js index c773c560949a..6bd0c8744fba 100644 --- a/test/e2e/tests/tokens/add-hide-token.spec.js +++ b/test/e2e/tests/tokens/add-hide-token.spec.js @@ -109,6 +109,15 @@ describe('Add existing token using search', function () { { fixtures: new FixtureBuilder({ inputChainId: CHAIN_IDS.BSC }) .withPreferencesController({ useTokenDetection: true }) + .withTokenListController({ + tokenList: [ + { + name: 'Basic Attention Token', + symbol: 'BAT', + address: '0x0d8775f648430679a709e98d2b0cb6250d2887ef', + }, + ], + }) .build(), ganacheOptions: { ...defaultGanacheOptions, diff --git a/test/e2e/tests/tokens/import-tokens.spec.js b/test/e2e/tests/tokens/import-tokens.spec.js index a1eb2782f9db..3055f7109551 100644 --- a/test/e2e/tests/tokens/import-tokens.spec.js +++ b/test/e2e/tests/tokens/import-tokens.spec.js @@ -37,7 +37,28 @@ describe('Import flow', function () { it('allows importing multiple tokens from search', async function () { await withFixtures( { - fixtures: new FixtureBuilder().withNetworkControllerOnMainnet().build(), + fixtures: new FixtureBuilder() + .withNetworkControllerOnMainnet() + .withTokensController({ + tokenList: [ + { + name: 'Chain Games', + symbol: 'CHAIN', + address: '0xc4c2614e694cf534d407ee49f8e44d125e4681c4', + }, + { + address: '0x7051faed0775f664a0286af4f75ef5ed74e02754', + symbol: 'CHANGE', + name: 'ChangeX', + }, + { + name: 'Chai', + symbol: 'CHAI', + address: '0x06af07097c9eeb7fd685c692751d5c66db49c215', + }, + ], + }) + .build(), ganacheOptions: defaultGanacheOptions, title: this.test.fullTitle(), testSpecificMock: mockPriceFetch, From ec38722cd1cf58652c205008888787355c6afa96 Mon Sep 17 00:00:00 2001 From: Brian Bergeron Date: Thu, 14 Nov 2024 15:17:11 -0800 Subject: [PATCH 21/23] fix unit test --- ui/hooks/useTokenListPolling.test.ts | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/ui/hooks/useTokenListPolling.test.ts b/ui/hooks/useTokenListPolling.test.ts index bf30342b29c9..09a22fffea50 100644 --- a/ui/hooks/useTokenListPolling.test.ts +++ b/ui/hooks/useTokenListPolling.test.ts @@ -25,6 +25,7 @@ describe('useTokenListPolling', () => { it('should poll for token lists on each chain when enabled, and stop on dismount', async () => { const state = { metamask: { + isUnlocked: true, completedOnboarding: true, useExternalServices: true, useTokenDetection: true, @@ -60,6 +61,7 @@ describe('useTokenListPolling', () => { it('should not poll before onboarding is completed', async () => { const state = { metamask: { + isUnlocked: true, completedOnboarding: false, useExternalServices: true, useTokenDetection: true, @@ -77,10 +79,32 @@ describe('useTokenListPolling', () => { expect(tokenListStopPollingByPollingToken).toHaveBeenCalledTimes(0); }); + it('should not poll when locked', async () => { + const state = { + metamask: { + isUnlocked: false, + completedOnboarding: true, + useExternalServices: true, + useTokenDetection: true, + networkConfigurationsByChainId: { + '0x1': {}, + '0x89': {}, + }, + }, + }; + + renderHookWithProvider(() => useTokenListPolling(), state); + + await Promise.all(mockPromises); + expect(tokenListStartPolling).toHaveBeenCalledTimes(0); + expect(tokenListStopPollingByPollingToken).toHaveBeenCalledTimes(0); + }); + it('should not poll when disabled', async () => { // disabled when detection, petnames, and simulations are all disabled const state = { metamask: { + isUnlocked: true, completedOnboarding: true, useExternalServices: true, useTokenDetection: false, From 79a451f9177af54707e09cd12354a09f97f869af Mon Sep 17 00:00:00 2001 From: Brian Bergeron Date: Thu, 14 Nov 2024 16:01:37 -0800 Subject: [PATCH 22/23] avoid calling hook different order --- ui/pages/asset/useHistoricalPrices.ts | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/ui/pages/asset/useHistoricalPrices.ts b/ui/pages/asset/useHistoricalPrices.ts index e4b28add0bc7..baf20d0b223f 100644 --- a/ui/pages/asset/useHistoricalPrices.ts +++ b/ui/pages/asset/useHistoricalPrices.ts @@ -31,8 +31,8 @@ export const useHistoricalPrices = ({ const [loading, setLoading] = useState(chainSupported); const [data, setData] = useState({}); - if (chainSupported) { - useEffect(() => { + useEffect(() => { + if (chainSupported) { setLoading(true); fetchWithCache({ url: `https://price.api.cx.metamask.io/v1/chains/${chainId}/historical-prices/${address}?vsCurrency=${currency}&timePeriod=${timeRange}`, @@ -59,7 +59,11 @@ export const useHistoricalPrices = ({ setData({ prices, edges }); setLoading(false); }); - }, [chainId, address, currency, timeRange]); - } + } else { + setLoading(false); + setData({}); + } + }, [chainSupported, chainId, address, currency, timeRange]); + return { loading, data }; }; From 979a3af1ae806a8f6027661e611853c3620c8a00 Mon Sep 17 00:00:00 2001 From: Brian Bergeron Date: Thu, 14 Nov 2024 16:02:37 -0800 Subject: [PATCH 23/23] lint --- ui/pages/asset/useHistoricalPrices.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/pages/asset/useHistoricalPrices.ts b/ui/pages/asset/useHistoricalPrices.ts index baf20d0b223f..febf99a9daed 100644 --- a/ui/pages/asset/useHistoricalPrices.ts +++ b/ui/pages/asset/useHistoricalPrices.ts @@ -60,8 +60,8 @@ export const useHistoricalPrices = ({ setLoading(false); }); } else { - setLoading(false); setData({}); + setLoading(false); } }, [chainSupported, chainId, address, currency, timeRange]);