diff --git a/app/components/UI/Tokens/TokenList/TokenListFooter/index.tsx b/app/components/UI/Tokens/TokenList/TokenListFooter/index.tsx
index 5ab9d0aba4d..5f405e38754 100644
--- a/app/components/UI/Tokens/TokenList/TokenListFooter/index.tsx
+++ b/app/components/UI/Tokens/TokenList/TokenListFooter/index.tsx
@@ -8,10 +8,6 @@ import Text, {
import { WalletViewSelectorsIDs } from '../../../../../../e2e/selectors/wallet/WalletView.selectors';
import { strings } from '../../../../../../locales/i18n';
import { useSelector } from 'react-redux';
-import {
- selectDetectedTokens,
- selectAllDetectedTokensFlat,
-} from '../../../../../selectors/tokensController';
import { isZero } from '../../../../../util/lodash';
import useRampNetwork from '../../../Ramp/hooks/useRampNetwork';
import { createBuyNavigationDetails } from '../../../Ramp/routes/utils';
@@ -25,45 +21,19 @@ import {
MetaMetricsEvents,
useMetrics,
} from '../../../../../components/hooks/useMetrics';
-import {
- getDecimalChainId,
- isPortfolioViewEnabled,
-} from '../../../../../util/networks';
-import {
- selectChainId,
- selectIsAllNetworks,
- selectIsPopularNetwork,
-} from '../../../../../selectors/networkController';
+import { getDecimalChainId } from '../../../../../util/networks';
+import { selectChainId } from '../../../../../selectors/networkController';
import { TokenI } from '../../types';
-import { selectUseTokenDetection } from '../../../../../selectors/preferencesController';
interface TokenListFooterProps {
tokens: TokenI[];
goToAddToken: () => void;
- showDetectedTokens: () => void;
isAddTokenEnabled: boolean;
}
-const getDetectedTokensCount = (
- isPortfolioEnabled: boolean,
- isAllNetworksSelected: boolean,
- allTokens: TokenI[],
- filteredTokens: TokenI[] | undefined,
- isPopularNetworks: boolean,
-): number => {
- if (!isPortfolioEnabled) {
- return filteredTokens?.length ?? 0;
- }
-
- return isAllNetworksSelected && isPopularNetworks
- ? allTokens.length
- : filteredTokens?.length ?? 0;
-};
-
export const TokenListFooter = ({
tokens,
goToAddToken,
- showDetectedTokens,
isAddTokenEnabled,
}: TokenListFooterProps) => {
const navigation = useNavigation();
@@ -71,16 +41,7 @@ export const TokenListFooter = ({
const { trackEvent, createEventBuilder } = useMetrics();
const [isNetworkRampSupported, isNativeTokenRampSupported] = useRampNetwork();
- const detectedTokens = useSelector(selectDetectedTokens) as TokenI[];
- const allDetectedTokens = useSelector(
- selectAllDetectedTokensFlat,
- ) as TokenI[];
-
- const isTokenDetectionEnabled = useSelector(selectUseTokenDetection);
const chainId = useSelector(selectChainId);
- const isAllNetworks = useSelector(selectIsAllNetworks);
- const isPopularNetworks = useSelector(selectIsPopularNetwork);
-
const styles = createStyles(colors);
const mainToken = tokens.find(({ isETH }) => isETH);
@@ -103,35 +64,8 @@ export const TokenListFooter = ({
);
};
- const tokenCount = getDetectedTokensCount(
- isPortfolioViewEnabled(),
- isAllNetworks,
- allDetectedTokens,
- detectedTokens,
- isPopularNetworks,
- );
-
- const areTokensDetected = tokenCount > 0;
-
return (
<>
- {/* renderTokensDetectedSection */}
- {areTokensDetected && isTokenDetectionEnabled && (
-
-
- {strings('wallet.tokens_detected_in_account', {
- tokenCount,
- tokensLabel: tokenCount > 1 ? 'tokens' : 'token',
- })}
-
-
- )}
{/* render buy button */}
{isBuyableToken && (
diff --git a/app/components/Views/Wallet/index.tsx b/app/components/Views/Wallet/index.tsx
index 0aec38bdeae..c8af59a20b9 100644
--- a/app/components/Views/Wallet/index.tsx
+++ b/app/components/Views/Wallet/index.tsx
@@ -43,6 +43,9 @@ import {
} from '../../../util/networks';
import {
selectChainId,
+ selectIsAllNetworks,
+ selectIsPopularNetwork,
+ selectNetworkClientId,
selectNetworkConfigurations,
selectProviderConfig,
selectTicker,
@@ -52,6 +55,8 @@ import {
selectNetworkImageSource,
} from '../../../selectors/networkInfos';
import {
+ selectAllDetectedTokensFlat,
+ selectDetectedTokens,
selectTokens,
selectTokensByChainIdAndAddress,
} from '../../../selectors/tokensController';
@@ -72,7 +77,10 @@ import Text, {
import { useMetrics } from '../../../components/hooks/useMetrics';
import { RootState } from '../../../reducers';
import usePrevious from '../../hooks/usePrevious';
-import { selectSelectedInternalAccount } from '../../../selectors/accountsController';
+import {
+ selectSelectedInternalAccount,
+ selectSelectedInternalAccountFormattedAddress,
+} from '../../../selectors/accountsController';
import { selectAccountBalanceByChainId } from '../../../selectors/accountTrackerController';
import {
hideNftFetchingLoadingIndicator as hideNftFetchingLoadingIndicatorAction,
@@ -92,7 +100,13 @@ import { PortfolioBalance } from '../../UI/Tokens/TokenList/PortfolioBalance';
import useCheckNftAutoDetectionModal from '../../hooks/useCheckNftAutoDetectionModal';
import useCheckMultiRpcModal from '../../hooks/useCheckMultiRpcModal';
import { selectContractBalances } from '../../../selectors/tokenBalancesController';
-import { selectTokenNetworkFilter } from '../../../selectors/preferencesController';
+import {
+ selectTokenNetworkFilter,
+ selectUseTokenDetection,
+} from '../../../selectors/preferencesController';
+import { TokenI } from '../../UI/Tokens/types';
+import { Hex } from '@metamask/utils';
+import { Token } from '@metamask/assets-controllers';
const createStyles = ({ colors, typography }: Theme) =>
StyleSheet.create({
@@ -311,6 +325,24 @@ const Wallet = ({
const networkImageSource = useSelector(selectNetworkImageSource);
const tokenNetworkFilter = useSelector(selectTokenNetworkFilter);
+ const isAllNetworks = useSelector(selectIsAllNetworks);
+ const isTokenDetectionEnabled = useSelector(selectUseTokenDetection);
+ const isPopularNetworks = useSelector(selectIsPopularNetwork);
+ const detectedTokens = useSelector(selectDetectedTokens) as TokenI[];
+
+ const allDetectedTokens = useSelector(
+ selectAllDetectedTokensFlat,
+ ) as TokenI[];
+ const currentDetectedTokens =
+ isPortfolioViewEnabled() && isAllNetworks && isPopularNetworks
+ ? allDetectedTokens
+ : detectedTokens;
+ const allNetworks = useSelector(selectNetworkConfigurations);
+ const selectedNetworkClientId = useSelector(selectNetworkClientId);
+ const selectedAddress = useSelector(
+ selectSelectedInternalAccountFormattedAddress,
+ );
+
/**
* Shows Nft auto detect modal if the user is on mainnet, never saw the modal and have nft detection off
*/
@@ -444,6 +476,74 @@ const Wallet = ({
readNotificationCount,
]);
+ useEffect(() => {
+ const importAllDetectedTokens = async () => {
+ // If autodetect tokens toggle is OFF, return
+ if (!isTokenDetectionEnabled) {
+ return;
+ }
+ const { TokensController } = Engine.context;
+ if (currentDetectedTokens.length > 0) {
+ if (isPortfolioViewEnabled()) {
+ // Group tokens by their `chainId` using a plain object
+ const tokensByChainId: Record = {};
+
+ for (const token of currentDetectedTokens) {
+ const tokenChainId: Hex =
+ (token as TokenI & { chainId: Hex }).chainId ?? chainId;
+
+ if (!tokensByChainId[tokenChainId]) {
+ tokensByChainId[tokenChainId] = [];
+ }
+
+ tokensByChainId[tokenChainId].push(token);
+ }
+
+ // Process grouped tokens in parallel
+ const importPromises = Object.entries(tokensByChainId).map(
+ async ([networkId, allTokens]) => {
+ const chainConfig = allNetworks[networkId as Hex];
+ const { defaultRpcEndpointIndex } = chainConfig;
+ const { networkClientId: networkInstanceId } =
+ chainConfig.rpcEndpoints[defaultRpcEndpointIndex];
+
+ await TokensController.addTokens(allTokens, networkInstanceId);
+ },
+ );
+
+ await Promise.all(importPromises);
+ } else {
+ await TokensController.addTokens(
+ currentDetectedTokens,
+ selectedNetworkClientId,
+ );
+ }
+
+ currentDetectedTokens.forEach(({ address, symbol }) =>
+ trackEvent(
+ createEventBuilder(MetaMetricsEvents.TOKEN_ADDED)
+ .addProperties({
+ token_address: address,
+ token_symbol: symbol,
+ chain_id: getDecimalChainId(chainId),
+ source: 'detected',
+ })
+ .build(),
+ ),
+ );
+ }
+ };
+ importAllDetectedTokens();
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, [
+ selectedAddress,
+ isTokenDetectionEnabled,
+ allNetworks,
+ chainId,
+ currentDetectedTokens,
+ selectedNetworkClientId,
+ ]);
+
const renderTabBar = useCallback(
(props) => (