diff --git a/ui/components/app/assets/asset-list/asset-list.tsx b/ui/components/app/assets/asset-list/asset-list.tsx index 4cbe529e3df2..c4afb36af244 100644 --- a/ui/components/app/assets/asset-list/asset-list.tsx +++ b/ui/components/app/assets/asset-list/asset-list.tsx @@ -4,6 +4,7 @@ import TokenList from '../token-list'; import { PRIMARY } from '../../../../helpers/constants/common'; import { useUserPreferencedCurrency } from '../../../../hooks/useUserPreferencedCurrency'; import { + getAllDetectedTokensForSelectedAddress, getDetectedTokensInCurrentNetwork, getIstokenDetectionInactiveOnNonMainnetSupportedNetwork, getSelectedAccount, @@ -103,9 +104,19 @@ const AssetList = ({ onClickAsset, showTokensLinks }: AssetListProps) => { // for EVM assets const shouldShowTokensLinks = showTokensLinks ?? isEvm; + const detectedTokensMultichain = useSelector( + getAllDetectedTokensForSelectedAddress, + ); + + const totalTokens = Object.values(detectedTokensMultichain).reduce( + // @ts-expect-error TS18046: 'tokenArray' is of type 'unknown' + (count, tokenArray) => count + tokenArray.length, + 0, + ) as number; + return ( <> - {detectedTokens.length > 0 && + {totalTokens > 0 && !isTokenDetectionInactiveOnNonMainnetSupportedNetwork && ( { const tokenList = useSelector(getTokenList); const tokenData = tokenList[token.address?.toLowerCase()]; const testNetworkBackgroundColor = useSelector(getTestNetworkBackgroundColor); - const currentNetwork = useSelector(getCurrentNetwork); + // const currentNetwork = useSelector(getCurrentNetwork); return ( } @@ -84,6 +86,7 @@ DetectedTokenDetails.propTypes = { }), handleTokenSelection: PropTypes.func.isRequired, tokensListDetected: PropTypes.object, + chainId: PropTypes.string, }; export default DetectedTokenDetails; diff --git a/ui/components/app/detected-token/detected-token-selection-popover/detected-token-selection-popover.js b/ui/components/app/detected-token/detected-token-selection-popover/detected-token-selection-popover.js index 2556cc767623..0d06470d4c66 100644 --- a/ui/components/app/detected-token/detected-token-selection-popover/detected-token-selection-popover.js +++ b/ui/components/app/detected-token/detected-token-selection-popover/detected-token-selection-popover.js @@ -10,6 +10,7 @@ import { MetaMetricsTokenEventSource, } from '../../../../../shared/constants/metametrics'; import { + getAllDetectedTokensForSelectedAddress, getCurrentChainId, getDetectedTokensInCurrentNetwork, } from '../../../../selectors'; @@ -26,13 +27,21 @@ const DetectedTokenSelectionPopover = ({ onIgnoreAll, setShowDetectedTokens, sortingBasedOnTokenSelection, + // chainId, }) => { const t = useI18nContext(); const trackEvent = useContext(MetaMetricsContext); - const chainId = useSelector(getCurrentChainId); - const detectedTokens = useSelector(getDetectedTokensInCurrentNetwork); + + const detectedTokensMultichain = useSelector( + getAllDetectedTokensForSelectedAddress, + ); + + const totalTokens = Object.values(detectedTokensMultichain).reduce( + (count, tokenArray) => count + tokenArray.length, + 0, + ); const { selected: selectedTokens = [] } = sortingBasedOnTokenSelection(tokensListDetected); @@ -46,7 +55,7 @@ const DetectedTokenSelectionPopover = ({ category: MetaMetricsEventCategory.Wallet, properties: { source_connection_method: MetaMetricsTokenEventSource.Detected, - chain_id: chainId, + // chain_id: chainId, tokens: eventTokensDetails, }, }); @@ -76,23 +85,24 @@ const DetectedTokenSelectionPopover = ({ - {detectedTokens.map((token, index) => { - return ( + {Object.entries(detectedTokensMultichain).map(([chainId, tokens]) => { + return tokens.map((token, index) => ( - ); + )); })} diff --git a/ui/components/app/detected-token/detected-token-values/detected-token-values.js b/ui/components/app/detected-token/detected-token-values/detected-token-values.js index 667c356b0e1f..614a13a9f1fe 100644 --- a/ui/components/app/detected-token/detected-token-values/detected-token-values.js +++ b/ui/components/app/detected-token/detected-token-values/detected-token-values.js @@ -23,6 +23,7 @@ const DetectedTokenValues = ({ const { tokensWithBalances } = useTokenTracker({ tokens: [token] }); const balanceString = tokensWithBalances[0]?.string; + const formattedFiatBalance = useTokenFiatAmount( token.address, balanceString, diff --git a/ui/components/app/detected-token/detected-token.js b/ui/components/app/detected-token/detected-token.js index 515355adb1e8..76682c1f2093 100644 --- a/ui/components/app/detected-token/detected-token.js +++ b/ui/components/app/detected-token/detected-token.js @@ -5,6 +5,7 @@ import { chain } from 'lodash'; import { addImportedTokens, + getNetworkConfigurationByNetworkClientId, ignoreTokens, setNewTokensImported, } from '../../../store/actions'; @@ -12,6 +13,7 @@ import { getAllDetectedTokensForSelectedAddress, getCurrentChainId, getDetectedTokensInCurrentNetwork, + getNetworkConfigurationsByChainId, getSelectedNetworkClientId, } from '../../../selectors'; import { MetaMetricsContext } from '../../../contexts/metametrics'; @@ -39,8 +41,8 @@ const sortingBasedOnTokenSelection = (tokensDetected) => { // ditch the 'selected' property and get just the tokens' .mapValues((group) => group.map(({ token }) => { - const { address, symbol, decimals, aggregators } = token; - return { address, symbol, decimals, aggregators }; + const { address, symbol, decimals, aggregators, chainId } = token; + return { address, symbol, decimals, aggregators, chainId }; }), ) // Exit the chain and get the underlying value, an object. @@ -52,20 +54,31 @@ const DetectedToken = ({ setShowDetectedTokens }) => { const dispatch = useDispatch(); const trackEvent = useContext(MetaMetricsContext); - const chainId = useSelector(getCurrentChainId); - const detectedTokens = useSelector(getDetectedTokensInCurrentNetwork); + // const chainId = useSelector(getCurrentChainId); + // const detectedTokens = useSelector(getDetectedTokensInCurrentNetwork); const detectedTokensMultichain = useSelector( getAllDetectedTokensForSelectedAddress, ); - console.log('detectedTokensMultichain ....', detectedTokensMultichain); - const networkClientId = useSelector(getSelectedNetworkClientId); + // const networkClientId = useSelector(getSelectedNetworkClientId); + + const configuration = useSelector(getNetworkConfigurationsByChainId); const [tokensListDetected, setTokensListDetected] = useState(() => - detectedTokens.reduce((tokenObj, token) => { - tokenObj[token.address] = { token, selected: true }; - return tokenObj; - }, {}), + Object.entries(detectedTokensMultichain).reduce( + (acc, [chainId, tokens]) => { + if (Array.isArray(tokens)) { + tokens.forEach((token) => { + acc[token.address] = { + token: { ...token, chainId }, + selected: true, + }; + }); + } + return acc; + }, + {}, + ), ); const [showDetectedTokenIgnoredPopover, setShowDetectedTokenIgnoredPopover] = useState(false); @@ -73,6 +86,8 @@ const DetectedToken = ({ setShowDetectedTokens }) => { useState(false); const importSelectedTokens = async (selectedTokens) => { + // console.log('selectedTokens .....', JSON.stringify(selectedTokens)); + selectedTokens.forEach((importedToken) => { trackEvent({ event: MetaMetricsEventName.TokenAdded, @@ -85,20 +100,48 @@ const DetectedToken = ({ setShowDetectedTokens }) => { token_standard: TokenStandard.ERC20, asset_type: AssetType.token, token_added_type: 'detected', - chain_id: chainId, + chain_id: importedToken.chainId, }, }); }); - await dispatch(addImportedTokens(selectedTokens, networkClientId)); - const tokenSymbols = selectedTokens.map(({ symbol }) => symbol); - dispatch(setNewTokensImported(tokenSymbols.join(', '))); + + // under feature flag + const tokensByChainId = selectedTokens.reduce((acc, token) => { + const { chainId } = token; + + if (!acc[chainId]) { + acc[chainId] = { tokens: [] }; + } + + acc[chainId].tokens.push(token); + + return acc; + }, {}); + + const importPromises = Object.entries(tokensByChainId).map( + async ([chainId, { tokens }]) => { + const chainConfig = configuration[chainId]; + const { defaultRpcEndpointIndex } = chainConfig; + const { networkClientId } = + chainConfig.rpcEndpoints[defaultRpcEndpointIndex]; + + await dispatch(addImportedTokens(tokens, networkClientId)); + const tokenSymbols = tokens.map(({ symbol }) => symbol); + dispatch(setNewTokensImported(tokenSymbols.join(', '))); + }, + ); + + await Promise.all(importPromises); }; const handleClearTokensSelection = async () => { const { selected: selectedTokens = [], deselected: deSelectedTokens = [] } = sortingBasedOnTokenSelection(tokensListDetected); - if (deSelectedTokens.length < detectedTokens.length) { + const detectedTokensCount = Object.values(detectedTokensMultichain).flat() + .length; + + if (deSelectedTokens.length < detectedTokensCount) { await importSelectedTokens(selectedTokens); } const tokensDetailsList = deSelectedTokens.map( @@ -141,7 +184,10 @@ const DetectedToken = ({ setShowDetectedTokens }) => { const { selected: selectedTokens = [] } = sortingBasedOnTokenSelection(tokensListDetected); - if (selectedTokens.length < detectedTokens.length) { + const detectedTokensCount = Object.values(detectedTokensMultichain).flat() + .length; + + if (selectedTokens.length < detectedTokensCount) { setShowDetectedTokenIgnoredPopover(true); setPartiallyIgnoreDetectedTokens(true); } else { @@ -175,9 +221,9 @@ const DetectedToken = ({ setShowDetectedTokens }) => { partiallyIgnoreDetectedTokens={partiallyIgnoreDetectedTokens} /> )} - {detectedTokens.length > 0 && ( + {Object.values(detectedTokensMultichain).flat().length > 0 && ( `${symbol} - ${address}`, - ); + const detectedTokensDetails = Object.values(detectedTokensMultichain) + .flat() + .map(({ address, symbol }) => `${symbol} - ${address}`); const chainId = useSelector(getCurrentChainId); @@ -62,7 +62,7 @@ export const DetectedTokensBanner = ({ data-testid="detected-token-banner" {...props} > - {detectedTokens.length === 1 + {totalTokens === 1 ? t('numberOfNewTokensDetectedSingular') : t('numberOfNewTokensDetectedPlural', [totalTokens])} diff --git a/ui/hooks/useTokenDetectionPolling.ts b/ui/hooks/useTokenDetectionPolling.ts index 793d7fc21429..3b2a686fbf5a 100644 --- a/ui/hooks/useTokenDetectionPolling.ts +++ b/ui/hooks/useTokenDetectionPolling.ts @@ -18,8 +18,6 @@ const useTokenDetectionPolling = () => { // Selectors returning state updated by the polling const detectedTokens = useSelector(getAllDetectedTokensForSelectedAddress); - console.log('detectedTokens *******', detectedTokens); - useMultiPolling({ startPolling: tokenDetectionStartPolling, stopPollingByPollingToken: tokenDetectionStopPollingByPollingToken, diff --git a/ui/selectors/selectors.js b/ui/selectors/selectors.js index 0e3292e66f00..b41810f88d72 100644 --- a/ui/selectors/selectors.js +++ b/ui/selectors/selectors.js @@ -2270,7 +2270,6 @@ export function getDetectedTokensInCurrentNetwork(state) { export function getAllDetectedTokensForSelectedAddress(state) { const completedOnboarding = getCompletedOnboarding(state); - // handle onboarding where selected address is not set yet if (!completedOnboarding) { return {}; } @@ -2282,7 +2281,10 @@ export function getAllDetectedTokensForSelectedAddress(state) { ).reduce((acc, [chainId, chainTokens]) => { const tokensForAddress = chainTokens[selectedAddress]; if (tokensForAddress) { - acc[chainId] = tokensForAddress; + acc[chainId] = tokensForAddress.map((token) => ({ + ...token, + chainId, + })); } return acc; }, {}); diff --git a/ui/store/actions.ts b/ui/store/actions.ts index 5f2a9136bc0e..acde0b0366bf 100644 --- a/ui/store/actions.ts +++ b/ui/store/actions.ts @@ -1970,12 +1970,17 @@ export function addImportedTokens( networkClientId?: NetworkClientId, ): ThunkAction { return async (dispatch: MetaMaskReduxDispatch) => { + console.log('tokensToImport -----', tokensToImport); + console.log('networkClientId -----', networkClientId); + + // debugger; try { await submitRequestToBackground('addImportedTokens', [ tokensToImport, networkClientId, ]); } catch (error) { + console.log('ERROR .....', error); logErrorWithMessage(error); } finally { await forceUpdateMetamaskState(dispatch);