Repository: MetaMask/metamask-mobile
Pull request #14736: feat: cp-7.46.0 Solana Token Details
Author: login: gambinish
Labels: name: team-assets
name: release-7.47.0
description: Issue or pull request that will be included in release 7.47.0
Created: 2025-04-18T03:28:57Z | Merged: 2025-04-24T22:53:47Z

## **Description**

Integrates the Multichain AssetOverview screen, enabling support for Solana token details. This introduces the foundational UI and logic needed to support additional non-EVM chains such as Bitcoin in the future.
- Leverages new Multichain controllers for data sourcing
- Continues to handle EVM assets through separate state paths

## **Related issues**

Fixes:

## **Manual testing steps**

1. Add a prefunded Solana account
2. Navigate to the Solana AssetOverview page
3. Verify the following elements render correctly:
 - Token balances
 - Fiat rates
 - Price charts
 - Interval price difference
 - Market data (now fully dynamic)

## **Screenshots/Recordings**

https://github.com/user-attachments/assets/ffa7e17a-7ce2-47ee-b7ad-90eaefc0c34e

## **Pre-merge author checklist**

- [x] I’ve followed [MetaMask Contributor Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Mobile Coding Standards](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/CODING_GUIDELINES.md).
- [x] I've completed the PR template to the best of my ability
- [x] I’ve included tests if applicable
- [x] I’ve documented my code using [JSDoc](https://jsdoc.app/) format if applicable
- [x] I’ve applied the right labels on the PR (see [labeling guidelines](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/LABELING_GUIDELINES.md)). Not required for external contributors.

## **Pre-merge reviewer checklist**

- [ ] I've manually tested the PR (e.g. pull and build branch, run the app, test code being changed).
- [ ] I confirm that this PR addresses all acceptance criteria described in the ticket it closes and includes the necessary testing evidence such as recordings and or screenshots.



Diff:
diff --git a/app/components/UI/AssetOverview/AssetOverview.test.tsx b/app/components/UI/AssetOverview/AssetOverview.test.tsx
index cb0acf2404f7..ab3eb566edd9 100644
--- a/app/components/UI/AssetOverview/AssetOverview.test.tsx
+++ b/app/components/UI/AssetOverview/AssetOverview.test.tsx
@@ -73,8 +73,8 @@ const mockInitialState = {
           logo: 'https://upload.wikimedia.org/wikipedia/commons/0/05/Ethereum_logo_2014.svg',
           name: 'Ethereum',
           symbol: 'ETH',
-          price: {}
-        }
+          price: {},
+        },
       ],
     },
   },
@@ -129,12 +129,14 @@ jest.mock('../../../core/Engine', () => ({
   },
 }));
 
-const mockAddPopularNetwork = jest.fn().mockImplementation(() => Promise.resolve());
+const mockAddPopularNetwork = jest
+  .fn()
+  .mockImplementation(() => Promise.resolve());
 jest.mock('../../../components/hooks/useAddNetwork', () => ({
   useAddNetwork: jest.fn().mockImplementation(() => ({
-      addPopularNetwork: mockAddPopularNetwork,
-      networkModal: null,
-    }))
+    addPopularNetwork: mockAddPopularNetwork,
+    networkModal: null,
+  })),
 }));
 
 const asset = {
@@ -437,6 +439,7 @@ describe('AssetOverview', () => {
       <AssetOverview
         asset={{
           ...asset,
+          address: 'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp/slip44:501',
           chainId: SolScope.Mainnet,
           isNative: true,
         }}
@@ -455,6 +458,13 @@ describe('AssetOverview', () => {
               MultichainNetworkController: {
                 selectedMultichainNetworkChainId: SolScope.Mainnet,
               },
+              MultichainAssetsRatesController: {
+                conversionRates: {
+                  'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp/slip44:501': {
+                    rate: '151.23',
+                  },
+                },
+              },
             },
           },
         },
@@ -488,12 +498,15 @@ describe('AssetOverview', () => {
         destinationToken: assetFromSearch.address,
         sourcePage: 'MainView',
         chainId: assetFromSearch.chainId,
-      }
+      },
     });
   });
 
   it('should prompt to add the network if coming from search and on a different chain', async () => {
-    (Engine.context.NetworkController.getNetworkConfigurationByChainId as jest.Mock).mockReturnValueOnce(null);
+    (
+      Engine.context.NetworkController
+        .getNetworkConfigurationByChainId as jest.Mock
+    ).mockReturnValueOnce(null);
     const differentChainAssetFromSearch = {
       ...assetFromSearch,
       chainId: '0xa',
diff --git a/app/components/UI/AssetOverview/AssetOverview.tsx b/app/components/UI/AssetOverview/AssetOverview.tsx
index 6378c4d9d8a1..834e7b173e8d 100644
--- a/app/components/UI/AssetOverview/AssetOverview.tsx
+++ b/app/components/UI/AssetOverview/AssetOverview.tsx
@@ -1,8 +1,13 @@
-import React, { useCallback, useEffect } from 'react';
+import React, { useCallback, useEffect, useMemo } from 'react';
 import { TouchableOpacity, View } from 'react-native';
 import { useNavigation } from '@react-navigation/native';
 import { useDispatch, useSelector } from 'react-redux';
-import { Hex } from '@metamask/utils';
+import {
+  Hex,
+  ///: BEGIN:ONLY_INCLUDE_IF(keyring-snaps)
+  CaipAssetId,
+  ///: END:ONLY_INCLUDE_IF
+} from '@metamask/utils';
 import I18n, { strings } from '../../../../locales/i18n';
 import { TokenOverviewSelectorsIDs } from '../../../../e2e/selectors/wallet/TokenOverview.selectors';
 import { newAssetTransaction } from '../../../actions/transaction';
@@ -52,7 +57,10 @@ import { useMetrics } from '../../../components/hooks/useMetrics';
 import { createBuyNavigationDetails } from '../Ramp/routes/utils';
 import { TokenI } from '../Tokens/types';
 import AssetDetailsActions from '../../../components/Views/AssetDetails/AssetDetailsActions';
-import { isAssetFromSearch, selectTokenDisplayData } from '../../../selectors/tokenSearchDiscoveryDataController';
+import {
+  isAssetFromSearch,
+  selectTokenDisplayData,
+} from '../../../selectors/tokenSearchDiscoveryDataController';
 import { selectIsEvmNetworkSelected } from '../../../selectors/multichainNetworkController';
 import { formatWithThreshold } from '../../../util/assets';
 import {
@@ -61,6 +69,11 @@ import {
 } from '../Bridge/hooks/useSwapBridgeNavigation';
 import { swapsUtils } from '@metamask/swaps-controller';
 import { TraceName, endTrace } from '../../../util/trace';
+///: BEGIN:ONLY_INCLUDE_IF(keyring-snaps)
+import { selectMultichainAssetsRates } from '../../../selectors/multichain';
+///: END:ONLY_INCLUDE_IF
+import { calculateAssetPrice } from './utils/calculateAssetPrice';
+
 interface AssetOverviewProps {
   asset: TokenI;
   displayBuyButton?: boolean;
@@ -102,10 +115,20 @@ const AssetOverview: React.FC<AssetOverviewProps> = ({
   const ticker = nativeCurrency;
   const selectedNetworkClientId = useSelector(selectSelectedNetworkClientId);
   const isEvmSelected = useSelector(selectIsEvmNetworkSelected);
+  const tokenResult = useSelector((state: RootState) =>
+    selectTokenDisplayData(state, asset.chainId as Hex, asset.address as Hex),
+  );
+  ///: BEGIN:ONLY_INCLUDE_IF(keyring-snaps)
+  const multichainAssetsRates = useSelector(selectMultichainAssetsRates);
+
+  const multichainAssetRates =
+    multichainAssetsRates?.[asset.address as CaipAssetId];
+  ///: END:ONLY_INCLUDE_IF
 
   const currentAddress = asset.address as Hex;
 
   const { data: prices = [], isLoading } = useTokenHistoricalPrices({
+    asset,
     address: currentAddress,
     chainId,
     timePeriod,
@@ -237,13 +260,21 @@ const AssetOverview: React.FC<AssetOverviewProps> = ({
     </View>
   );
 
+  const chartNavigationButtons: TimePeriod[] = useMemo(
+    () =>
+      isEvmSelected
+        ? ['1d', '1w', '1m', '3m', '1y', '3y']
+        : ['1d', '1w', '1m', '3m', '1y'],
+    [isEvmSelected],
+  );
+
   const handleSelectTimePeriod = useCallback((_timePeriod: TimePeriod) => {
     setTimePeriod(_timePeriod);
   }, []);
 
   const renderChartNavigationButton = useCallback(
     () =>
-      (['1d', '1w', '1m', '3m', '1y', '3y'] as TimePeriod[]).map((label) => (
+      chartNavigationButtons.map((label) => (
         <ChartNavigationButton
           key={label}
           label={strings(
@@ -253,9 +284,12 @@ const AssetOverview: React.FC<AssetOverviewProps> = ({
           selected={timePeriod === label}
         />
       )),
-    [handleSelectTimePeriod, timePeriod],
+    [handleSelectTimePeriod, timePeriod, chartNavigationButtons],
   );
-  const itemAddress = safeToChecksumAddress(asset.address);
+
+  const itemAddress = isEvmSelected
+    ? safeToChecksumAddress(asset.address)
+    : asset.address;
 
   const currentChainId = chainId as Hex;
   const exchangeRate =
@@ -299,25 +333,38 @@ const AssetOverview: React.FC<AssetOverviewProps> = ({
     asset.isETH ? asset.ticker : asset.symbol
   }`;
 
-  const tokenResult = useSelector((state: RootState) => selectTokenDisplayData(state, asset.chainId as Hex, asset.address as Hex));
+  const convertedMultichainAssetRates =
+    !isEvmSelected && multichainAssetRates
+      ? {
+          rate: Number(multichainAssetRates.rate),
+          marketData: undefined,
+        }
+      : undefined;
 
   let currentPrice = 0;
   let priceDiff = 0;
+  let comparePrice = 0;
 
   if (isAssetFromSearch(asset) && tokenResult?.found) {
     currentPrice = tokenResult.price?.price || 0;
   } else {
-    const tickerConversionRate =
-      conversionRateByTicker?.[nativeCurrency]?.conversionRate ?? 0;
-    currentPrice =
-      exchangeRate && tickerConversionRate
-        ? exchangeRate * tickerConversionRate
-        : 0;
-  }
-
-  const comparePrice = prices[0]?.[1] || 0;
-  if (currentPrice !== undefined && currentPrice !== null) {
-    priceDiff = currentPrice - comparePrice;
+    const {
+      currentPrice: calculatedPrice,
+      priceDiff: calculatedPriceDiff,
+      comparePrice: calculatedComparePrice,
+    } = calculateAssetPrice({
+      _asset: asset,
+      isEvmNetworkSelected: isEvmSelected,
+      exchangeRate,
+      tickerConversionRate:
+        conversionRateByTicker?.[nativeCurrency]?.conversionRate ?? undefined,
+      prices,
+      multichainAssetRates: convertedMultichainAssetRates,
+      timePeriod,
+    });
+    currentPrice = calculatedPrice;
+    priceDiff = calculatedPriceDiff;
+    comparePrice = calculatedComparePrice;
   }
 
   return (
@@ -335,6 +382,10 @@ const AssetOverview: React.FC<AssetOverviewProps> = ({
             comparePrice={comparePrice}
             isLoading={isLoading}
             timePeriod={timePeriod}
+            ///: BEGIN:ONLY_INCLUDE_IF(keyring-snaps)
+            multichainAssetsRates={multichainAssetsRates}
+            ///: END:ONLY_INCLUDE_IF
+            isEvmNetworkSelected={isEvmSelected}
           />
           <View style={styles.chartNavigationWrapper}>
             {renderChartNavigationButton()}
@@ -358,10 +409,6 @@ const AssetOverview: React.FC<AssetOverviewProps> = ({
           <View style={styles.tokenDetailsWrapper}>
             <TokenDetails asset={asset} />
           </View>
-          {/*  Commented out since we are going to re enable it after curating content */}
-          {/* <View style={styles.aboutWrapper}>
-            // <AboutAsset asset={asset} chainId={chainId} />
-          </View> */}
           {networkModal}
         </View>
       )}
diff --git a/app/components/UI/AssetOverview/Price/Price.test.tsx b/app/components/UI/AssetOverview/Price/Price.test.tsx
index 80ccfa370d7f..fec6917c1372 100644
--- a/app/components/UI/AssetOverview/Price/Price.test.tsx
+++ b/app/components/UI/AssetOverview/Price/Price.test.tsx
@@ -6,6 +6,8 @@ import {
   TimePeriod,
   TokenPrice,
 } from '../../../../components/hooks/useTokenHistoricalPrices';
+import { AssetConversion } from '@metamask/snaps-sdk';
+import { CaipAssetId } from '@metamask/utils';
 
 const mockAsset: TokenI = {
   name: 'Ethereum',
@@ -36,6 +38,8 @@ const mockProps: {
   comparePrice: number;
   isLoading: boolean;
   timePeriod: TimePeriod;
+  isEvmNetworkSelected: boolean;
+  multichainAssetsRates: Record<CaipAssetId, AssetConversion>;
 } = {
   asset: mockAsset,
   prices: mockPrices,
@@ -45,6 +49,8 @@ const mockProps: {
   comparePrice: 100,
   isLoading: false,
   timePeriod: '1d',
+  isEvmNetworkSelected: true,
+  multichainAssetsRates: {},
 };
 
 describe('Price Component', () => {
diff --git a/app/components/UI/AssetOverview/Price/Price.tsx b/app/components/UI/AssetOverview/Price/Price.tsx
index 1fdc4db8be4a..e854073ad627 100644
--- a/app/components/UI/AssetOverview/Price/Price.tsx
+++ b/app/components/UI/AssetOverview/Price/Price.tsx
@@ -19,6 +19,10 @@ import { distributeDataPoints } from '../PriceChart/utils';
 import styleSheet from './Price.styles';
 import { TokenOverviewSelectorsIDs } from '../../../../../e2e/selectors/wallet/TokenOverview.selectors';
 import { TokenI } from '../../Tokens/types';
+///: BEGIN:ONLY_INCLUDE_IF(keyring-snaps)
+import { CaipAssetId } from '@metamask/utils';
+import { AssetConversion } from '@metamask/snaps-sdk';
+///: END:ONLY_INCLUDE_IF
 
 interface PriceProps {
   asset: TokenI;
@@ -29,6 +33,10 @@ interface PriceProps {
   comparePrice: number;
   isLoading: boolean;
   timePeriod: TimePeriod;
+  ///: BEGIN:ONLY_INCLUDE_IF(keyring-snaps)
+  multichainAssetsRates: Record<CaipAssetId, AssetConversion>;
+  ///: END:ONLY_INCLUDE_IF
+  isEvmNetworkSelected: boolean;
 }
 
 const Price = ({
@@ -40,7 +48,16 @@ const Price = ({
   comparePrice,
   isLoading,
   timePeriod,
+  ///: BEGIN:ONLY_INCLUDE_IF(keyring-snaps)
+  multichainAssetsRates,
+  ///: END:ONLY_INCLUDE_IF
+  isEvmNetworkSelected,
 }: PriceProps) => {
+  ///: BEGIN:ONLY_INCLUDE_IF(keyring-snaps)
+  const multichainAssetRates =
+    multichainAssetsRates[asset.address as CaipAssetId];
+  ///: END:ONLY_INCLUDE_IF
+
   const [activeChartIndex, setActiveChartIndex] = useState<number>(-1);
 
   const distributedPriceData = useMemo(() => {
@@ -64,8 +81,10 @@ const Price = ({
     '3y': strings('asset_overview.chart_time_period.3y'),
   };
 
-  const price: number =
-    distributedPriceData[activeChartIndex]?.[1] || currentPrice;
+  const price: number = isEvmNetworkSelected
+    ? distributedPriceData[activeChartIndex]?.[1] || currentPrice
+    : Number(multichainAssetRates.rate);
+
   const date: string | undefined = distributedPriceData[activeChartIndex]?.[0]
     ? toDateFormat(distributedPriceData[activeChartIndex]?.[0])
     : timePeriodTextDict[timePeriod];
diff --git a/app/components/UI/AssetOverview/TokenDetails/TokenDetails.test.tsx b/app/components/UI/AssetOverview/TokenDetails/TokenDetails.test.tsx
index faa21b64d073..d979dfea56a3 100644
--- a/app/components/UI/AssetOverview/TokenDetails/TokenDetails.test.tsx
+++ b/app/components/UI/AssetOverview/TokenDetails/TokenDetails.test.tsx
@@ -19,6 +19,8 @@ import * as reactRedux from 'react-redux';
 // eslint-disable-next-line import/no-namespace
 import * as StakeConstants from '../../Stake/constants';
 import { strings } from '../../../../../locales/i18n';
+import { selectMultichainAssetsRates } from '../../../../selectors/multichain';
+import { selectIsEvmNetworkSelected } from '../../../../selectors/multichainNetworkController';
 jest.mock('../../../../core/Engine', () => ({
   getTotalEvmFiatAccountBalance: jest.fn(),
   context: {
@@ -147,9 +149,22 @@ describe('TokenDetails', () => {
     const useSelectorSpy = jest.spyOn(reactRedux, 'useSelector');
     useSelectorSpy.mockImplementation((selectorOrCallback) => {
       const SELECTOR_MOCKS = {
-        selectTokenMarketDataByChainId: mockTokenMarketDataByChainId['0x1'],
+        selectIsEvmNetworkSelected: true,
+        selectEvmTokenMarketData: {
+          marketData:
+            mockTokenMarketDataByChainId['0x1'][
+              '0x6B175474E89094C44Da98b954EedeAC495271d0F'
+            ],
+          metadata: {
+            decimals: 18,
+            conversionRate: 2712.15,
+            aggregators: ['Metamask', 'Coinmarketcap'],
+          },
+        },
+        selectTokenMarketDataByChainId: {},
         selectConversionRateBySymbol: mockExchangeRate,
         selectNativeCurrencyByChainId: 'ETH',
+        selectMultichainAssetsRates: {},
       } as const;
 
       if (typeof selectorOrCallback === 'function') {
@@ -166,11 +181,19 @@ describe('TokenDetails', () => {
         case selectTokenList:
           return mockAssets;
         case selectContractExchangeRates:
-          return mockContractExchangeRates;
+          return {};
         case selectConversionRate:
           return mockExchangeRate;
         case selectCurrentCurrency:
           return mockCurrentCurrency;
+        case selectProviderConfig:
+          return { ticker: 'ETH' };
+        case selectEvmTicker:
+          return 'ETH';
+        case selectMultichainAssetsRates:
+          return {};
+        case selectIsEvmNetworkSelected:
+          return true;
         default:
           return undefined;
       }
@@ -196,19 +219,18 @@ describe('TokenDetails', () => {
     expect(getByText('Metamask, Coinmarketcap')).toBeDefined();
     expect(getByText('Market details')).toBeDefined();
     expect(getByText('Market Cap')).toBeDefined();
-    expect(getByText('5.22B')).toBeDefined();
     expect(getByText('Total Volume (24h)')).toBeDefined();
-    expect(getByText('147.65M')).toBeDefined();
+    expect(getByText('$147.65M')).toBeDefined();
     expect(getByText('Volume / Market Cap')).toBeDefined();
     expect(getByText('2.83%')).toBeDefined();
     expect(getByText('Circulating supply')).toBeDefined();
     expect(getByText('5.21B')).toBeDefined();
     expect(getByText('All time high')).toBeDefined();
+    expect(getByText('$1.22')).toBeDefined();
     expect(getByText('All time low')).toBeDefined();
     expect(getByText('$0.88')).toBeDefined();
     expect(getByText('2.83%')).toBeDefined();
     expect(getByText('Fully diluted')).toBeDefined();
-    expect(getByText('1.92M')).toBeDefined();
     expect(toJSON()).toMatchSnapshot();
   });
 
@@ -218,6 +240,7 @@ describe('TokenDetails', () => {
       selectTokenMarketDataByChainId: {},
       selectConversionRateBySymbol: mockExchangeRate,
       selectNativeCurrencyByChainId: 'ETH',
+      selectMultichainAssetsRates: {},
     } as const;
 
     useSelectorSpy.mockImplementation((selectorOrCallback) => {
@@ -244,6 +267,8 @@ describe('TokenDetails', () => {
           return { ticker: 'ETH' };
         case selectEvmTicker:
           return 'ETH';
+        case selectMultichainAssetsRates:
+          return {};
         default:
           return undefined;
       }
@@ -255,6 +280,7 @@ describe('TokenDetails', () => {
         state: initialState,
       },
     );
+
     expect(getByText('Token details')).toBeDefined();
     expect(queryByText('Market details')).toBeNull();
   });
@@ -263,7 +289,12 @@ describe('TokenDetails', () => {
     const useSelectorSpy = jest.spyOn(reactRedux, 'useSelector');
     useSelectorSpy.mockImplementation((selectorOrCallback) => {
       const SELECTOR_MOCKS = {
-        selectTokenMarketDataByChainId: mockTokenMarketDataByChainId['0x1'],
+        selectEvmTokenMarketData: {
+          marketData:
+            mockTokenMarketDataByChainId['0x1'][
+              '0x6B175474E89094C44Da98b954EedeAC495271d0F'
+            ],
+        },
         selectConversionRateBySymbol: mockExchangeRate,
         selectNativeCurrencyByChainId: 'ETH',
       } as const;
@@ -279,23 +310,26 @@ describe('TokenDetails', () => {
       }
 
       switch (selectorOrCallback) {
-        case selectTokenList:
-          return {};
         case selectContractExchangeRates:
           return mockContractExchangeRates;
         case selectConversionRate:
           return mockExchangeRate;
         case selectCurrentCurrency:
           return mockCurrentCurrency;
+        case selectMultichainAssetsRates:
+          return {};
+        case selectIsEvmNetworkSelected:
+          return true;
         default:
           return undefined;
       }
     });
 
-    const { getByText, queryByText } = renderWithProvider(
+    const { getByText, queryByText, debug } = renderWithProvider(
       <TokenDetails asset={mockDAI} />,
       { state: initialState },
     );
+    debug();
     expect(queryByText('Token details')).toBeNull();
     expect(getByText('Market details')).toBeDefined();
   });
diff --git a/app/components/UI/AssetOverview/TokenDetails/TokenDetails.tsx b/app/components/UI/AssetOverview/TokenDetails/TokenDetails.tsx
index 8abb605ade38..f73787a6e576 100644
--- a/app/components/UI/AssetOverview/TokenDetails/TokenDetails.tsx
+++ b/app/components/UI/AssetOverview/TokenDetails/TokenDetails.tsx
@@ -1,32 +1,43 @@
-import { zeroAddress } from 'ethereumjs-util';
-import { Hex } from '@metamask/utils';
+import {
+  ///: BEGIN:ONLY_INCLUDE_IF(keyring-snaps)
+  CaipAssetId,
+  ///: END:ONLY_INCLUDE_IF
+  Hex,
+} from '@metamask/utils';
 import { RootState } from '../../../../reducers';
-import React from 'react';
+import React, { useMemo } from 'react';
 import { View } from 'react-native';
 import { useSelector } from 'react-redux';
 import i18n from '../../../../../locales/i18n';
 import { useStyles } from '../../../../component-library/hooks';
 import styleSheet from './TokenDetails.styles';
 import { safeToChecksumAddress } from '../../../../util/address';
-import { selectTokenList } from '../../../../selectors/tokenListController';
-import { selectTokenMarketDataByChainId } from '../../../../selectors/tokenRatesController';
-import { selectConversionRateBySymbol, selectCurrentCurrency } from '../../../../selectors/currencyRateController';
-import { selectNativeCurrencyByChainId } from '../../../../selectors/networkController';
 import {
-  convertDecimalToPercentage,
-  localizeLargeNumber,
-} from '../../../../util/number';
-import { formatCurrency } from '../../../../util/confirm-tx';
+  selectConversionRateBySymbol,
+  selectCurrentCurrency,
+} from '../../../../selectors/currencyRateController';
+import { selectNativeCurrencyByChainId } from '../../../../selectors/networkController';
 import Logger from '../../../../util/Logger';
 import TokenDetailsList from './TokenDetailsList';
 import MarketDetailsList from './MarketDetailsList';
 import { TokenI } from '../../Tokens/types';
 import StakingEarnings from '../../Stake/components/StakingEarnings';
-import { isAssetFromSearch, selectTokenDisplayData } from '../../../../selectors/tokenSearchDiscoveryDataController';
+import {
+  isAssetFromSearch,
+  selectTokenDisplayData,
+} from '../../../../selectors/tokenSearchDiscoveryDataController';
 import { isSupportedLendingTokenByChainId } from '../../Earn/utils/token';
 import EarnEmptyStateCta from '../../Earn/components/EmptyStateCta';
 import { parseFloatSafe } from '../../Earn/utils';
 import { isStablecoinLendingFeatureEnabled } from '../../Stake/constants';
+import { selectIsEvmNetworkSelected } from '../../../../selectors/multichainNetworkController';
+import { selectEvmTokenMarketData } from '../../../../selectors/multichain/evm';
+///: BEGIN:ONLY_INCLUDE_IF(keyring-snaps)
+import { selectMultichainAssetsRates } from '../../../../selectors/multichain';
+///: END:ONLY_INCLUDE_IF
+import { MarketDataDetails } from '@metamask/assets-controllers';
+import { formatMarketDetails } from '../utils/marketDetails';
+import { getTokenDetails } from '../utils/getTokenDetails';
 
 export interface TokenDetails {
   contractAddress: string | null;
@@ -48,11 +59,13 @@ interface TokenDetailsProps {
   asset: TokenI;
 }
 
+interface EvmMarketData {
+  metadata?: Record<string, string | number | string[]>;
+  marketData?: MarketDataDetails;
+}
+
 const TokenDetails: React.FC<TokenDetailsProps> = ({ asset }) => {
   const { styles } = useStyles(styleSheet, {});
-  const tokenExchangeRatesByChainId = useSelector((state: RootState) =>
-    selectTokenMarketDataByChainId(state, asset.chainId as Hex),
-  );
   const nativeCurrency = useSelector((state: RootState) =>
     selectNativeCurrencyByChainId(state, asset.chainId as Hex),
   );
@@ -60,91 +73,111 @@ const TokenDetails: React.FC<TokenDetailsProps> = ({ asset }) => {
     selectConversionRateBySymbol(state, nativeCurrency),
   );
   const currentCurrency = useSelector(selectCurrentCurrency);
-  const tokenContractAddress = safeToChecksumAddress(asset.address);
-  const tokenList = useSelector(selectTokenList);
 
-  let conversionRate;
-  if (isAssetFromSearch(asset)) {
-    conversionRate = 1;
-  } else {
-    conversionRate = conversionRateBySymbol;
-  }
+  const isEvmNetworkSelected = useSelector(selectIsEvmNetworkSelected);
 
-  const tokenExchangeRates = tokenExchangeRatesByChainId;
+  const tokenContractAddress = isEvmNetworkSelected
+    ? safeToChecksumAddress(asset.address)
+    : asset.address;
 
-  let tokenMetadata;
-  let marketData;
+  const tokenSearchResult = useSelector((state: RootState) =>
+    selectTokenDisplayData(state, asset.chainId as Hex, asset.address as Hex),
+  );
+
+  ///: BEGIN:ONLY_INCLUDE_IF(keyring-snaps)
+  const allMultichainAssetsRates = useSelector(selectMultichainAssetsRates);
 
-  const tokenResult = useSelector((state: RootState) => selectTokenDisplayData(state, asset.chainId as Hex, asset.address as Hex));
+  const multichainAssetRates =
+    allMultichainAssetsRates[asset.address as CaipAssetId];
 
-  if (isAssetFromSearch(asset) && tokenResult?.found && tokenResult.price) {
-    marketData = tokenResult.price;
-    tokenMetadata = tokenResult.token;
-  } else if (asset.isETH) {
-    marketData = tokenExchangeRates?.[zeroAddress() as Hex];
-  } else if (tokenContractAddress) {
-    tokenMetadata = tokenList?.[tokenContractAddress.toLowerCase()];
-    marketData = tokenExchangeRates?.[tokenContractAddress as Hex];
+  const nonEvmMarketData = multichainAssetRates?.marketData;
+  const nonEvmMetadata = {
+    rate: Number(multichainAssetRates?.rate),
+    conversionTime: Number(multichainAssetRates?.conversionTime),
+  };
+  ///: END:ONLY_INCLUDE_IF
+
+  const evmMarketData = useSelector((state: RootState) =>
+    isEvmNetworkSelected
+      ? selectEvmTokenMarketData(state, {
+          chainId: asset.chainId as Hex,
+          tokenAddress: asset.address,
+        })
+      : null,
+  ) as EvmMarketData | null;
+
+  const evmConversionRate = isAssetFromSearch(asset)
+    ? 1
+    : conversionRateBySymbol;
+
+  const conversionRate = isEvmNetworkSelected
+    ? evmConversionRate
+    : nonEvmMetadata.rate;
+
+  let marketData;
+  let tokenMetadata;
+
+  if (
+    isAssetFromSearch(asset) &&
+    tokenSearchResult?.found &&
+    tokenSearchResult.price
+  ) {
+    marketData = tokenSearchResult.price;
+    tokenMetadata = tokenSearchResult.token;
   } else {
-    Logger.log('cannot find contract address');
-    return null;
+    tokenMetadata = isEvmNetworkSelected ? evmMarketData?.metadata : null;
+    marketData = isEvmNetworkSelected
+      ? evmMarketData?.marketData
+      : nonEvmMarketData;
   }
+  const tokenDetails = useMemo(
+    () =>
+      getTokenDetails(
+        asset,
+        isEvmNetworkSelected,
+        tokenContractAddress,
+        tokenMetadata as Record<string, string | number | string[]>,
+      ),
+    [asset, isEvmNetworkSelected, tokenContractAddress, tokenMetadata],
+  );
 
-  if (!conversionRate || conversionRate < 0) {
-    Logger.log('invalid conversion rate');
-    return null;
-  }
+  const marketDetails = useMemo(() => {
+    if (!marketData) return;
 
-  const tokenDetails: TokenDetails = asset.isETH
-    ? {
-        contractAddress: zeroAddress(),
-        tokenDecimal: 18,
-        tokenList: '',
-      }
-    : {
-        contractAddress: tokenContractAddress || null,
-        tokenDecimal: tokenMetadata?.decimals || null,
-        tokenList: tokenMetadata?.aggregators?.join(', ') || null,
-      };
-
-  const marketDetails: MarketDetails = {
-    marketCap:
-      marketData?.marketCap > 0
-        ? localizeLargeNumber(i18n, conversionRate * marketData.marketCap)
-        : null,
-    totalVolume:
-      marketData?.totalVolume > 0
-        ? localizeLargeNumber(i18n, conversionRate * marketData.totalVolume)
-        : null,
-    volumeToMarketCap:
-      marketData?.marketCap > 0
-        ? convertDecimalToPercentage(
-            marketData.totalVolume / marketData.marketCap,
-          )
-        : null,
-    circulatingSupply:
-      marketData?.circulatingSupply > 0
-        ? localizeLargeNumber(i18n, marketData.circulatingSupply)
-        : null,
-    allTimeHigh:
-      marketData?.allTimeHigh > 0
-        ? formatCurrency(
-            conversionRate * marketData.allTimeHigh,
-            currentCurrency,
-          )
-        : null,
-    allTimeLow:
-      marketData?.allTimeLow > 0
-        ? formatCurrency(
-            conversionRate * marketData.allTimeLow,
-            currentCurrency,
-          )
-        : null,
-    fullyDiluted:
-      marketData?.dilutedMarketCap > 0
-        ? localizeLargeNumber(i18n, marketData.dilutedMarketCap)
-        : null,
-  };
+    if (!conversionRate || conversionRate < 0) {
+      Logger.log('invalid conversion rate');
+      return;
+    }
+
+    return formatMarketDetails(
+      {
+        marketCap: marketData.marketCap
+          ? Number(marketData.marketCap)
+          : undefined,
+        totalVolume: marketData.totalVolume
+          ? Number(marketData.totalVolume)
+          : undefined,
+        circulatingSupply: marketData.circulatingSupply
+          ? Number(marketData.circulatingSupply)
+          : undefined,
+        allTimeHigh: marketData.allTimeHigh
+          ? Number(marketData.allTimeHigh)
+          : undefined,
+        allTimeLow: marketData.allTimeLow
+          ? Number(marketData.allTimeLow)
+          : undefined,
+        dilutedMarketCap: (marketData as MarketDataDetails)?.dilutedMarketCap
+          ? Number((marketData as MarketDataDetails).dilutedMarketCap)
+          : undefined,
+      },
+      {
+        locale: i18n.locale,
+        currentCurrency,
+        isEvmNetworkSelected,
+        conversionRate,
+      },
+    );
+  }, [marketData, currentCurrency, isEvmNetworkSelected, conversionRate]);
 
   const hasAssetBalance =
     asset.balanceFiat && parseFloatSafe(asset.balanceFiat) > 0;
@@ -155,10 +188,12 @@ const TokenDetails: React.FC<TokenDetailsProps> = ({ asset }) => {
       {isStablecoinLendingFeatureEnabled() &&
         isSupportedLendingTokenByChainId(asset.symbol, asset.chainId ?? '') &&
         hasAssetBalance && <EarnEmptyStateCta token={asset} />}
-      {(asset.isETH || tokenMetadata) && (
+      {(asset.isETH || tokenMetadata || !isEvmNetworkSelected) && (
         <TokenDetailsList tokenDetails={tokenDetails} />
       )}
-      {marketData && <MarketDetailsList marketDetails={marketDetails} />}
+      {marketData && marketDetails && (
+        <MarketDetailsList marketDetails={marketDetails} />
+      )}
     </View>
   );
 };
diff --git a/app/components/UI/AssetOverview/TokenDetails/__snapshots__/TokenDetails.test.tsx.snap b/app/components/UI/AssetOverview/TokenDetails/__snapshots__/TokenDetails.test.tsx.snap
index 8ab0d2b1e40b..22b087762e30 100644
--- a/app/components/UI/AssetOverview/TokenDetails/__snapshots__/TokenDetails.test.tsx.snap
+++ b/app/components/UI/AssetOverview/TokenDetails/__snapshots__/TokenDetails.test.tsx.snap
@@ -260,7 +260,7 @@ exports[`TokenDetails should render correctly 1`] = `
             }
           }
         >
-          5.22B
+          $5.22B
         </Text>
       </View>
       <View
@@ -300,7 +300,7 @@ exports[`TokenDetails should render correctly 1`] = `
             }
           }
         >
-          147.65M
+          $147.65M
         </Text>
       </View>
       <View
@@ -500,7 +500,7 @@ exports[`TokenDetails should render correctly 1`] = `
             }
           }
         >
-          1.92M
+          $5.22B
         </Text>
       </View>
     </View>
diff --git a/app/components/UI/AssetOverview/__snapshots__/AssetOverview.test.tsx.snap b/app/components/UI/AssetOverview/__snapshots__/AssetOverview.test.tsx.snap
index 470dc8ecd134..2ca14b187703 100644
--- a/app/components/UI/AssetOverview/__snapshots__/AssetOverview.test.tsx.snap
+++ b/app/components/UI/AssetOverview/__snapshots__/AssetOverview.test.tsx.snap
@@ -1142,7 +1142,16 @@ exports[`AssetOverview should render correctly 1`] = `
           "paddingHorizontal": 16,
         }
       }
-    />
+    >
+      <View
+        style={
+          {
+            "gap": 24,
+            "marginTop": 24,
+          }
+        }
+      />
+    </View>
   </View>
 </View>
 `;
@@ -2326,7 +2335,16 @@ exports[`AssetOverview should render native balances even if there are no accoun
           "paddingHorizontal": 16,
         }
       }
-    />
+    >
+      <View
+        style={
+          {
+            "gap": 24,
+            "marginTop": 24,
+          }
+        }
+      />
+    </View>
   </View>
 </View>
 `;
@@ -2380,41 +2398,7 @@ exports[`AssetOverview should render native balances when non evm network is sel
         }
         testID="token-price"
       >
-        <View
-          style={
-            {
-              "paddingTop": 8,
-            }
-          }
-        >
-          <View
-            onLayout={[Function]}
-          >
-            <View
-              style={
-                {
-                  "position": "relative",
-                }
-              }
-            >
-              <View
-                style={
-                  [
-                    {
-                      "borderRadius": 6,
-                      "height": 32,
-                      "width": 100,
-                    },
-                    {
-                      "backgroundColor": "#E1E9EE",
-                      "overflow": "hidden",
-                    },
-                  ]
-                }
-              />
-            </View>
-          </View>
-        </View>
+        $151.23
       </Text>
       <Text
         accessibilityRole="text"
@@ -2429,76 +2413,110 @@ exports[`AssetOverview should render native balances when non evm network is sel
           }
         }
       >
-        <View
+        <Text
+          accessibilityRole="text"
           style={
             {
-              "paddingTop": 2,
+              "color": "#1c7e33",
+              "fontFamily": "CentraNo1-Medium",
+              "fontSize": 16,
+              "fontWeight": "500",
+              "letterSpacing": 0,
+              "lineHeight": 24,
             }
           }
-          testID="loading-price-diff"
         >
-          <View
-            onLayout={[Function]}
-          >
-            <View
-              style={
+          <Text
+            allowFontScaling={false}
+            style={
+              [
                 {
-                  "position": "relative",
-                }
+                  "color": undefined,
+                  "fontSize": 16,
+                },
+                {
+                  "marginTop": 10,
+                },
+                {
+                  "fontFamily": "Feather",
+                  "fontStyle": "normal",
+                  "fontWeight": "normal",
+                },
+                {},
+              ]
+            }
+          >
+            
+          </Text>
+           
+          $151.23
+           (
+          +
+          Infinity
+          %)
+           
+          <Text
+            accessibilityRole="text"
+            style={
+              {
+                "color": "#686e7d",
+                "fontFamily": "CentraNo1-Medium",
+                "fontSize": 16,
+                "fontWeight": "500",
+                "letterSpacing": 0,
+                "lineHeight": 24,
               }
-            >
-              <View
-                style={
-                  [
-                    {
-                      "borderRadius": 6,
-                      "height": 18,
-                      "width": 150,
-                    },
-                    {
-                      "backgroundColor": "#E1E9EE",
-                      "overflow": "hidden",
-                    },
-                  ]
-                }
-              />
-            </View>
-          </View>
-        </View>
+            }
+          >
+            Today
+          </Text>
+        </Text>
       </Text>
     </View>
     <View
       style={
         {
-          "paddingHorizontal": 16,
-          "paddingTop": 10,
+          "height": 576.96,
+          "marginVertical": 10,
+          "paddingLeft": 0,
+          "paddingRight": 0,
+          "paddingTop": 0,
           "width": 750,
         }
       }
     >
       <View
-        onLayout={[Function]}
+        onMoveShouldSetResponder={[Function]}
+        onMoveShouldSetResponderCapture={[Function]}
+        onResponderEnd={[Function]}
+        onResponderGrant={[Function]}
+        onResponderMove={[Function]}
+        onResponderReject={[Function]}
+        onResponderRelease={[Function]}
+        onResponderStart={[Function]}
+        onResponderTerminate={[Function]}
+        onResponderTerminationRequest={[Function]}
+        onStartShouldSetResponder={[Function]}
+        onStartShouldSetResponderCapture={[Function]}
+        style={
+          {
+            "flex": 1,
+          }
+        }
       >
         <View
           style={
             {
-              "position": "relative",
+              "flex": 1,
             }
           }
         >
           <View
+            onLayout={[Function]}
             style={
-              [
-                {
-                  "borderRadius": 6,
-                  "height": 586.96,
-                  "width": 718,
-                },
-                {
-                  "backgroundColor": "#E1E9EE",
-                  "overflow": "hidden",
-                },
-              ]
+              {
+                "flex": 1,
+              }
             }
           />
         </View>
@@ -2670,37 +2688,6 @@ exports[`AssetOverview should render native balances when non evm network is sel
           1Y
         </Text>
       </TouchableOpacity>
-      <TouchableOpacity
-        onPress={[Function]}
-        style={
-          {
-            "alignItems": "center",
-            "backgroundColor": "#ffffff",
-            "borderRadius": 40,
-            "justifyContent": "center",
-            "paddingHorizontal": 8,
-            "paddingLeft": 10,
-            "paddingVertical": 2,
-          }
-        }
-      >
-        <Text
-          accessibilityRole="text"
-          style={
-            {
-              "color": "#686e7d",
-              "fontFamily": "CentraNo1-Book",
-              "fontSize": 14,
-              "fontWeight": "400",
-              "letterSpacing": 3,
-              "lineHeight": 22,
-              "textAlign": "center",
-            }
-          }
-        >
-          3Y
-        </Text>
-      </TouchableOpacity>
     </View>
     <View
       style={
@@ -3507,7 +3494,156 @@ exports[`AssetOverview should render native balances when non evm network is sel
           "paddingHorizontal": 16,
         }
       }
-    />
+    >
+      <View
+        style={
+          {
+            "gap": 24,
+            "marginTop": 24,
+          }
+        }
+      >
+        <View>
+          <Text
+            accessibilityRole="text"
+            style={
+              {
+                "color": "#121314",
+                "fontFamily": "CentraNo1-Bold",
+                "fontSize": 18,
+                "fontWeight": "700",
+                "letterSpacing": 0,
+                "lineHeight": 24,
+                "paddingVertical": 8,
+              }
+            }
+          >
+            Token details
+          </Text>
+          <View
+            style={
+              {
+                "paddingBottom": 8,
+                "paddingTop": 8,
+              }
+            }
+          >
+            <View
+              style={
+                [
+                  {
+                    "flexDirection": "row",
+                    "justifyContent": "space-between",
+                    "paddingVertical": 4,
+                  },
+                  {
+                    "paddingTop": 0,
+                  },
+                ]
+              }
+            >
+              <Text
+                accessibilityRole="text"
+                style={
+                  {
+                    "color": "#686e7d",
+                    "fontFamily": "CentraNo1-Medium",
+                    "fontSize": 16,
+                    "fontWeight": "500",
+                    "letterSpacing": 0,
+                    "lineHeight": 24,
+                  }
+                }
+              >
+                Contract address
+              </Text>
+              <TouchableOpacity
+                onPress={[Function]}
+                style={
+                  {
+                    "alignItems": "center",
+                    "backgroundColor": "#4459ff1a",
+                    "borderRadius": 20,
+                    "flexDirection": "row",
+                    "marginLeft": 8,
+                    "paddingHorizontal": 8,
+                  }
+                }
+              >
+                <Text
+                  accessibilityRole="text"
+                  style={
+                    {
+                      "color": "#4459ff",
+                      "fontFamily": "CentraNo1-Book",
+                      "fontSize": 14,
+                      "fontWeight": "400",
+                      "letterSpacing": 0,
+                      "lineHeight": 22,
+                    }
+                  }
+                >
+                  solana:...4:501
+                </Text>
+                <SvgMock
+                  color="#4459ff"
+                  height={16}
+                  name="Copy"
+                  style={
+                    {
+                      "height": 16,
+                      "marginLeft": 4,
+                      "width": 16,
+                    }
+                  }
+                  width={16}
+                />
+              </TouchableOpacity>
+            </View>
+            <View
+              style={
+                {
+                  "flexDirection": "row",
+                  "justifyContent": "space-between",
+                  "paddingVertical": 4,
+                }
+              }
+            >
+              <Text
+                accessibilityRole="text"
+                style={
+                  {
+                    "color": "#686e7d",
+                    "fontFamily": "CentraNo1-Medium",
+                    "fontSize": 16,
+                    "fontWeight": "500",
+                    "letterSpacing": 0,
+                    "lineHeight": 24,
+                  }
+                }
+              >
+                Token decimal
+              </Text>
+              <Text
+                accessibilityRole="text"
+                style={
+                  {
+                    "color": "#121314",
+                    "fontFamily": "CentraNo1-Book",
+                    "fontSize": 14,
+                    "fontWeight": "400",
+                    "letterSpacing": 0,
+                    "lineHeight": 22,
+                  }
+                }
+              >
+                18
+              </Text>
+            </View>
+          </View>
+        </View>
+      </View>
+    </View>
   </View>
 </View>
 `;
diff --git a/app/components/UI/AssetOverview/utils/calculateAssetPrice.test.ts b/app/components/UI/AssetOverview/utils/calculateAssetPrice.test.ts
new file mode 100644
index 000000000000..af301ac9946b
--- /dev/null
+++ b/app/components/UI/AssetOverview/utils/calculateAssetPrice.test.ts
@@ -0,0 +1,144 @@
+import { calculateAssetPrice } from './calculateAssetPrice';
+import { TokenI } from '../../Tokens/types';
+import { TokenPrice } from '../../../../components/hooks/useTokenHistoricalPrices';
+
+const mockAsset: TokenI = {
+  name: 'Ethereum',
+  ticker: 'ETH',
+  symbol: 'Ethereum',
+  address: '0x0',
+  aggregators: [],
+  decimals: 18,
+  image: '',
+  balance: '100',
+  balanceFiat: '$100',
+  logo: '',
+  isETH: true,
+  isNative: true,
+};
+
+const mockPrices: TokenPrice[] = [
+  ['1736761237983', 100],
+  ['1736761237986', 105],
+];
+
+describe('calculateAssetPrice', () => {
+  it('should calculate EVM asset price correctly', () => {
+    const result = calculateAssetPrice({
+      _asset: mockAsset,
+      isEvmNetworkSelected: true,
+      exchangeRate: 2000,
+      tickerConversionRate: 1.5,
+      prices: mockPrices,
+      timePeriod: '1d',
+    });
+
+    expect(result).toEqual({
+      currentPrice: 3000, // 2000 * 1.5
+      priceDiff: 2900, // 3000 - 100
+      comparePrice: 100,
+      pricePercentChange: undefined,
+    });
+  });
+
+  it('should calculate non-EVM asset price correctly with price percent change', () => {
+    const result = calculateAssetPrice({
+      _asset: mockAsset,
+      isEvmNetworkSelected: false,
+      prices: mockPrices,
+      timePeriod: '1d',
+      multichainAssetRates: {
+        rate: 2500,
+        marketData: {
+          pricePercentChange: {
+            P1D: 4.5,
+            P7D: 13.8,
+            P30D: 5.7,
+          },
+        },
+      },
+    });
+
+    expect(result).toEqual({
+      currentPrice: 2500,
+      priceDiff: 2400, // 2500 - 100
+      comparePrice: 100,
+      pricePercentChange: 4.5, // P1D for 1d timePeriod
+    });
+  });
+
+  it('should handle missing rates', () => {
+    const result = calculateAssetPrice({
+      _asset: mockAsset,
+      isEvmNetworkSelected: true,
+      prices: mockPrices,
+      timePeriod: '1d',
+    });
+
+    expect(result).toEqual({
+      currentPrice: 0,
+      priceDiff: -100, // 0 - 100
+      comparePrice: 100,
+      pricePercentChange: undefined,
+    });
+  });
+
+  it('should handle missing multichain rates', () => {
+    const result = calculateAssetPrice({
+      _asset: mockAsset,
+      isEvmNetworkSelected: false,
+      prices: mockPrices,
+      timePeriod: '1d',
+    });
+
+    expect(result).toEqual({
+      currentPrice: 0,
+      priceDiff: 0,
+      comparePrice: 100,
+      pricePercentChange: undefined,
+    });
+  });
+
+  it('should handle different time periods for price percent change', () => {
+    const multichainAssetRates = {
+      rate: 2500,
+      marketData: {
+        pricePercentChange: {
+          P1D: 4.5,
+          P7D: 13.8,
+          P30D: 5.7,
+          P200D: 6.3,
+          P1Y: -4.0,
+        },
+      },
+    };
+
+    const result1d = calculateAssetPrice({
+      _asset: mockAsset,
+      isEvmNetworkSelected: false,
+      prices: mockPrices,
+      timePeriod: '1d',
+      multichainAssetRates,
+    });
+
+    const result1w = calculateAssetPrice({
+      _asset: mockAsset,
+      isEvmNetworkSelected: false,
+      prices: mockPrices,
+      timePeriod: '1w',
+      multichainAssetRates,
+    });
+
+    const result1m = calculateAssetPrice({
+      _asset: mockAsset,
+      isEvmNetworkSelected: false,
+      prices: mockPrices,
+      timePeriod: '1m',
+      multichainAssetRates,
+    });
+
+    expect(result1d.pricePercentChange).toBe(4.5); // P1D
+    expect(result1w.pricePercentChange).toBe(13.8); // P7D
+    expect(result1m.pricePercentChange).toBe(5.7); // P30D
+  });
+});
diff --git a/app/components/UI/AssetOverview/utils/calculateAssetPrice.ts b/app/components/UI/AssetOverview/utils/calculateAssetPrice.ts
new file mode 100644
index 000000000000..d642d054e9c9
--- /dev/null
+++ b/app/components/UI/AssetOverview/utils/calculateAssetPrice.ts
@@ -0,0 +1,92 @@
+import { TokenI } from '../../Tokens/types';
+import {
+  TimePeriod,
+  TokenPrice,
+} from '../../../../components/hooks/useTokenHistoricalPrices';
+
+interface MarketData {
+  pricePercentChange?: {
+    PT1H?: number;
+    P1D?: number;
+    P7D?: number;
+    P14D?: number;
+    P30D?: number;
+    P200D?: number;
+    P1Y?: number;
+  };
+}
+
+interface CalculateAssetPriceParams {
+  _asset: TokenI; // Prefix with underscore to indicate it's intentionally unused
+  isEvmNetworkSelected: boolean;
+  exchangeRate?: number;
+  tickerConversionRate?: number;
+  prices: TokenPrice[];
+  multichainAssetRates?: {
+    rate: number;
+    marketData?: MarketData;
+  };
+  timePeriod: TimePeriod;
+}
+
+interface CalculateAssetPriceResult {
+  currentPrice: number;
+  priceDiff: number;
+  comparePrice: number;
+  pricePercentChange?: number;
+}
+
+const TIME_PERIOD_TO_MARKET_DATA_KEY: Record<
+  TimePeriod,
+  keyof NonNullable<MarketData['pricePercentChange']>
+> = {
+  '1d': 'P1D',
+  '1w': 'P7D',
+  '7d': 'P7D',
+  '1m': 'P30D',
+  '3m': 'P200D',
+  '1y': 'P1Y',
+  '3y': 'P1Y', // TODO: Add 3y market data key
+};
+
+export const calculateAssetPrice = ({
+  isEvmNetworkSelected,
+  exchangeRate,
+  tickerConversionRate,
+  prices,
+  multichainAssetRates,
+  timePeriod,
+}: CalculateAssetPriceParams): CalculateAssetPriceResult => {
+  let currentPrice = 0;
+  let priceDiff = 0;
+  const comparePrice = prices[0]?.[1] || 0;
+  let pricePercentChange: number | undefined;
+
+  if (isEvmNetworkSelected) {
+    // EVM price calculation
+    currentPrice =
+      exchangeRate && tickerConversionRate
+        ? exchangeRate * tickerConversionRate
+        : 0;
+
+    if (currentPrice !== undefined && currentPrice !== null) {
+      priceDiff = currentPrice - comparePrice;
+    }
+  } else if (multichainAssetRates?.rate) {
+    // Non-EVM price calculation
+    currentPrice = multichainAssetRates.rate;
+    priceDiff = currentPrice - comparePrice;
+
+    // Get price percent change from market data
+    const marketDataKey = TIME_PERIOD_TO_MARKET_DATA_KEY[timePeriod];
+    pricePercentChange =
+      multichainAssetRates.marketData?.pricePercentChange?.[marketDataKey];
+  }
+
+  return {
+    currentPrice,
+    priceDiff,
+    comparePrice,
+    pricePercentChange,
+  };
+};
diff --git a/app/components/UI/AssetOverview/utils/getTokenDetails.test.ts b/app/components/UI/AssetOverview/utils/getTokenDetails.test.ts
new file mode 100644
index 000000000000..cf08001215f6
--- /dev/null
+++ b/app/components/UI/AssetOverview/utils/getTokenDetails.test.ts
@@ -0,0 +1,193 @@
+import { zeroAddress } from 'ethereumjs-util';
+import { getTokenDetails } from './getTokenDetails';
+import { TokenI } from '../../Tokens/types';
+
+describe('getTokenDetails', () => {
+  // Base test data
+  const mockAsset: TokenI = {
+    address: '0x123',
+    symbol: 'TEST',
+    decimals: 18,
+    aggregators: ['uniswap', '1inch'],
+    isETH: false,
+    chainId: '0x1',
+    image: 'https://example.com/image.png',
+    name: 'Test Token',
+    balance: '1000000000000000000',
+    logo: 'https://example.com/logo.png',
+  };
+
+  const mockEvmMetadata = {
+    decimals: 18,
+    aggregators: ['uniswap', '1inch'],
+  };
+
+  describe('Network-specific behavior', () => {
+    it('should format token details for non-EVM networks', () => {
+      const result = getTokenDetails(
+        mockAsset,
+        false,
+        undefined,
+        mockEvmMetadata,
+      );
+
+      expect(result).toEqual({
+        contractAddress: '0x123',
+        tokenDecimal: 18,
+        tokenList: 'uniswap, 1inch',
+      });
+    });
+
+    it('should format ETH token details for EVM networks', () => {
+      const ethAsset: TokenI = {
+        ...mockAsset,
+        isETH: true,
+      };
+
+      const result = getTokenDetails(
+        ethAsset,
+        true,
+        undefined,
+        mockEvmMetadata,
+      );
+
+      expect(result).toEqual({
+        contractAddress: zeroAddress(),
+        tokenDecimal: 18,
+        tokenList: '',
+      });
+    });
+
+    it('should format regular token details for EVM networks', () => {
+      const result = getTokenDetails(mockAsset, true, '0x456', mockEvmMetadata);
+
+      expect(result).toEqual({
+        contractAddress: '0x456',
+        tokenDecimal: 18,
+        tokenList: 'uniswap, 1inch',
+      });
+    });
+  });
+
+  describe('Metadata handling', () => {
+    it('should handle missing decimals in token metadata', () => {
+      const metadataWithoutDecimals = {
+        aggregators: ['uniswap'],
+      };
+
+      const result = getTokenDetails(
+        mockAsset,
+        true,
+        '0x456',
+        metadataWithoutDecimals,
+      );
+
+      expect(result).toEqual({
+        contractAddress: '0x456',
+        tokenDecimal: null,
+        tokenList: 'uniswap',
+      });
+    });
+
+    it('should handle missing aggregators in token metadata', () => {
+      const metadataWithoutAggregators = {
+        decimals: 18,
+      };
+
+      const result = getTokenDetails(
+        mockAsset,
+        true,
+        '0x456',
+        metadataWithoutAggregators,
+      );
+
+      expect(result).toEqual({
+        contractAddress: '0x456',
+        tokenDecimal: 18,
+        tokenList: null,
+      });
+    });
+
+    it('should handle invalid aggregators type in token metadata', () => {
+      const metadataWithInvalidAggregators = {
+        decimals: 18,
+        aggregators: 'uniswap' as unknown as string[],
+      };
+
+      const result = getTokenDetails(
+        mockAsset,
+        true,
+        '0x456',
+        metadataWithInvalidAggregators,
+      );
+
+      expect(result).toEqual({
+        contractAddress: '0x456',
+        tokenDecimal: 18,
+        tokenList: null,
+      });
+    });
+  });
+
+  describe('Asset property handling', () => {
+    it('should handle empty address in asset', () => {
+      const assetWithoutAddress: TokenI = {
+        ...mockAsset,
+        address: '',
+      };
+
+      const result = getTokenDetails(
+        assetWithoutAddress,
+        false,
+        undefined,
+        mockEvmMetadata,
+      );
+
+      expect(result).toEqual({
+        contractAddress: null,
+        tokenDecimal: 18,
+        tokenList: 'uniswap, 1inch',
+      });
+    });
+
+    it('should handle zero decimals in asset', () => {
+      const assetWithoutDecimals: TokenI = {
+        ...mockAsset,
+        decimals: 0,
+      };
+
+      const result = getTokenDetails(
+        assetWithoutDecimals,
+        false,
+        undefined,
+        mockEvmMetadata,
+      );
+
+      expect(result).toEqual({
+        contractAddress: '0x123',
+        tokenDecimal: null,
+        tokenList: 'uniswap, 1inch',
+      });
+    });
+
+    it('should handle empty aggregators array in asset', () => {
+      const assetWithoutAggregators: TokenI = {
+        ...mockAsset,
+        aggregators: [],
+      };
+
+      const result = getTokenDetails(
+        assetWithoutAggregators,
+        false,
+        undefined,
+        mockEvmMetadata,
+      );
+
+      expect(result).toEqual({
+        contractAddress: '0x123',
+        tokenDecimal: 18,
+        tokenList: null,
+      });
+    });
+  });
+});
diff --git a/app/components/UI/AssetOverview/utils/getTokenDetails.ts b/app/components/UI/AssetOverview/utils/getTokenDetails.ts
new file mode 100644
index 000000000000..4b7ed05d70c6
--- /dev/null
+++ b/app/components/UI/AssetOverview/utils/getTokenDetails.ts
@@ -0,0 +1,37 @@
+import { zeroAddress } from 'ethereumjs-util';
+import { TokenI } from '../../Tokens/types';
+import { TokenDetails } from '../TokenDetails/TokenDetails';
+
+export const getTokenDetails = (
+  asset: TokenI,
+  isEvmNetworkSelected: boolean,
+  tokenContractAddress: string | undefined,
+  tokenMetadata: Record<string, string | number | string[]>,
+): TokenDetails => {
+  if (!isEvmNetworkSelected) {
+    return {
+      contractAddress: asset.address || null,
+      tokenDecimal: asset.decimals || null,
+      tokenList: asset.aggregators.join(', ') || null,
+    };
+  }
+
+  if (asset.isETH) {
+    return {
+      contractAddress: zeroAddress(),
+      tokenDecimal: 18,
+      tokenList: '',
+    };
+  }
+
+  return {
+    contractAddress: tokenContractAddress ?? null,
+    tokenDecimal:
+      typeof tokenMetadata?.decimals === 'number'
+        ? tokenMetadata.decimals
+        : null,
+    tokenList: Array.isArray(tokenMetadata?.aggregators)
+      ? tokenMetadata.aggregators.join(', ')
+      : null,
+  };
+};
diff --git a/app/components/UI/AssetOverview/utils/marketDetails.test.ts b/app/components/UI/AssetOverview/utils/marketDetails.test.ts
new file mode 100644
index 000000000000..e6fb8f142141
--- /dev/null
+++ b/app/components/UI/AssetOverview/utils/marketDetails.test.ts
@@ -0,0 +1,120 @@
+import { formatMarketDetails } from './marketDetails';
+
+describe('formatMarketDetails', () => {
+  const mockOptions = {
+    locale: 'en-US',
+    currentCurrency: 'USD',
+    isEvmNetworkSelected: true,
+    conversionRate: 1.5,
+  };
+
+  it('should format market details correctly with all values', () => {
+    const marketData = {
+      marketCap: 1000000,
+      totalVolume: 500000,
+      circulatingSupply: 1000000,
+      allTimeHigh: 100,
+      allTimeLow: 50,
+      dilutedMarketCap: 2000000,
+    };
+
+    const result = formatMarketDetails(marketData, mockOptions);
+
+    expect(result).toEqual({
+      marketCap: '$1.50M',
+      totalVolume: '$750.00K',
+      volumeToMarketCap: '50.00%',
+      circulatingSupply: '1.00M',
+      allTimeHigh: '$150.00',
+      allTimeLow: '$75.00',
+      fullyDiluted: '$3.00M',
+    });
+  });
+
+  it('should handle null values correctly', () => {
+    const marketData = {
+      marketCap: 0,
+      totalVolume: undefined,
+      circulatingSupply: undefined,
+      allTimeHigh: 0,
+      allTimeLow: undefined,
+      dilutedMarketCap: undefined,
+    };
+
+    const result = formatMarketDetails(marketData, mockOptions);
+
+    expect(result).toEqual({
+      marketCap: null,
+      totalVolume: null,
+      volumeToMarketCap: null,
+      circulatingSupply: null,
+      allTimeHigh: null,
+      allTimeLow: null,
+      fullyDiluted: null,
+    });
+  });
+
+  it('should handle non-EVM network correctly', () => {
+    const marketData = {
+      marketCap: 1000000,
+      totalVolume: 500000,
+      circulatingSupply: 1000000,
+      allTimeHigh: 100,
+      allTimeLow: 50,
+      dilutedMarketCap: 2000000,
+    };
+
+    const nonEvmOptions = {
+      ...mockOptions,
+      isEvmNetworkSelected: false,
+    };
+
+    const result = formatMarketDetails(marketData, nonEvmOptions);
+
+    expect(result).toEqual({
+      marketCap: '$1.00M',
+      totalVolume: '$500.00K',
+      volumeToMarketCap: '50.00%',
+      circulatingSupply: '1.00M',
+      allTimeHigh: '$100.00',
+      allTimeLow: '$50.00',
+      fullyDiluted: '$2.00M',
+    });
+  });
+
+  it('should format market details correctly for French locale', () => {
+    // Test data with realistic market values
+    const marketData = {
+      marketCap: 1000000,
+      totalVolume: 500000,
+      circulatingSupply: 1000000,
+      allTimeHigh: 100,
+      allTimeLow: 50,
+      dilutedMarketCap: 2000000,
+    };
+
+    const frenchLocaleOptions = {
+      ...mockOptions,
+      locale: 'fr-FR',
+      currentCurrency: 'EUR',
+    };
+
+    const formattedMarketDetails = formatMarketDetails(
+      marketData,
+      frenchLocaleOptions,
+    );
+
+    // Expected French-formatted values with currency
+    const expectedFormattedValues = {
+      marketCap: '1,50\xa0M\xa0€', // 1.5M EUR
+      totalVolume: '750,00\xa0k\xa0€', // 750K EUR
+      volumeToMarketCap: '50,00\xa0%', // 50% volume to market cap ratio
+      circulatingSupply: '1,00\xa0M', // 1M tokens
+      allTimeHigh: '150,00\xa0€', // 150 EUR
+      allTimeLow: '75,00\xa0€', // 75 EUR
+      fullyDiluted: '3,00\xa0M\xa0€', // 3M EUR
+    };
+
+    expect(formattedMarketDetails).toEqual(expectedFormattedValues);
+  });
+});
diff --git a/app/components/UI/AssetOverview/utils/marketDetails.ts b/app/components/UI/AssetOverview/utils/marketDetails.ts
new file mode 100644
index 000000000000..6fef2aaf459b
--- /dev/null
+++ b/app/components/UI/AssetOverview/utils/marketDetails.ts
@@ -0,0 +1,156 @@
+import { formatWithThreshold } from '../../../../util/assets';
+
+export interface MarketData {
+  marketCap?: number;
+  totalVolume?: number;
+  circulatingSupply?: number;
+  allTimeHigh?: number;
+  allTimeLow?: number;
+  dilutedMarketCap?: number;
+}
+
+export interface MarketDetails {
+  marketCap: string | null;
+  totalVolume: string | null;
+  volumeToMarketCap: string | null;
+  circulatingSupply: string | null;
+  allTimeHigh: string | null;
+  allTimeLow: string | null;
+  fullyDiluted: string | null;
+}
+
+interface FormatMarketDetailsOptions {
+  locale: string;
+  currentCurrency: string;
+  isEvmNetworkSelected: boolean;
+  conversionRate: number;
+}
+
+/**
+ * Formats market details with consistent formatting options
+ */
+export const formatMarketDetails = (
+  marketData: MarketData,
+  options: FormatMarketDetailsOptions,
+): MarketDetails => {
+  const { locale, currentCurrency, isEvmNetworkSelected, conversionRate } =
+    options;
+
+  const marketCap =
+    marketData.marketCap && marketData.marketCap > 0
+      ? formatWithThreshold(
+          isEvmNetworkSelected
+            ? marketData.marketCap * conversionRate
+            : marketData.marketCap,
+          0.01,
+          locale,
+          {
+            style: 'currency',
+            notation: 'compact',
+            currency: currentCurrency,
+            minimumFractionDigits: 2,
+            maximumFractionDigits: 2,
+          },
+        )
+      : null;
+
+  const totalVolume =
+    marketData.totalVolume && marketData.totalVolume > 0
+      ? formatWithThreshold(
+          isEvmNetworkSelected
+            ? marketData.totalVolume * conversionRate
+            : marketData.totalVolume,
+          0.01,
+          locale,
+          {
+            style: 'currency',
+            notation: 'compact',
+            currency: currentCurrency,
+            minimumFractionDigits: 2,
+            maximumFractionDigits: 2,
+          },
+        )
+      : null;
+
+  const volumeToMarketCap =
+    marketData.marketCap && marketData.totalVolume && marketData.marketCap > 0
+      ? formatWithThreshold(
+          marketData.totalVolume / marketData.marketCap,
+          0.0001,
+          locale,
+          {
+            style: 'percent',
+            minimumFractionDigits: 2,
+            maximumFractionDigits: 2,
+          },
+        )
+      : null;
+
+  const circulatingSupply =
+    marketData.circulatingSupply && marketData.circulatingSupply > 0
+      ? formatWithThreshold(marketData.circulatingSupply, 0.01, locale, {
+          style: 'decimal',
+          notation: 'compact',
+          maximumFractionDigits: 2,
+          minimumFractionDigits: 2,
+        })
+      : null;
+
+  const allTimeHigh =
+    marketData.allTimeHigh && marketData.allTimeHigh > 0
+      ? formatWithThreshold(
+          isEvmNetworkSelected
+            ? marketData.allTimeHigh * conversionRate
+            : marketData.allTimeHigh,
+          0.01,
+          locale,
+          {
+            style: 'currency',
+            currency: currentCurrency,
+          },
+        )
+      : null;
+
+  const allTimeLow =
+    marketData.allTimeLow && marketData.allTimeLow > 0
+      ? formatWithThreshold(
+          isEvmNetworkSelected
+            ? marketData.allTimeLow * conversionRate
+            : marketData.allTimeLow,
+          0.01,
+          locale,
+          {
+            style: 'currency',
+            currency: currentCurrency,
+          },
+        )
+      : null;
+
+  const fullyDiluted =
+    marketData.dilutedMarketCap && marketData.dilutedMarketCap > 0
+      ? formatWithThreshold(
+          isEvmNetworkSelected
+            ? marketData.dilutedMarketCap * conversionRate
+            : marketData.dilutedMarketCap,
+          0.01,
+          locale,
+          {
+            style: 'currency',
+            notation: 'compact',
+            currency: currentCurrency,
+            minimumFractionDigits: 2,
+            maximumFractionDigits: 2,
+          },
+        )
+      : null;
+
+  return {
+    marketCap,
+    totalVolume,
+    volumeToMarketCap,
+    circulatingSupply,
+    allTimeHigh,
+    allTimeLow,
+    fullyDiluted,
+  };
+};
diff --git a/app/components/UI/Tokens/TokenList/TokenListItem/index.tsx b/app/components/UI/Tokens/TokenList/TokenListItem/index.tsx
index 0dd7c31b4e58..e257232c64b5 100644
--- a/app/components/UI/Tokens/TokenList/TokenListItem/index.tsx
+++ b/app/components/UI/Tokens/TokenList/TokenListItem/index.tsx
@@ -265,13 +265,6 @@ export const TokenListItem = React.memo(
           .build(),
       );
 
-      // Token details are currently only supported for EVM networks.
-      // This early return prevents navigation to token details for non-EVM networks.
-      // TODO: Remove this when shipping the multichain token details feature, which is slated for 7.45 RC
-      if (!isEvmNetworkSelected) {
-        return;
-      }
-
       // if the asset is staked, navigate to the native asset details
       if (asset.isStaked) {
         return navigation.navigate('Asset', {
diff --git a/app/components/Views/Asset/index.js b/app/components/Views/Asset/index.js
index a4c8e4359f78..b2e8410701d2 100644
--- a/app/components/Views/Asset/index.js
+++ b/app/components/Views/Asset/index.js
@@ -69,7 +69,10 @@ import {
 } from '../../../selectors/transactionController';
 import Logger from '../../../util/Logger';
 import { TOKEN_CATEGORY_HASH } from '../../UI/TransactionElement/utils';
-import { isAssetFromSearch, selectSupportedSwapTokenAddressesForChainId } from '../../../selectors/tokenSearchDiscoveryDataController';
+import {
+  isAssetFromSearch,
+  selectSupportedSwapTokenAddressesForChainId,
+} from '../../../selectors/tokenSearchDiscoveryDataController';
 import { isNonEvmChainId } from '../../../core/Multichain/utils';
 import isBridgeAllowed from '../../UI/Bridge/utils/isBridgeAllowed';
 
@@ -233,7 +236,8 @@ class Asset extends PureComponent {
         false,
         navigation,
         colors,
-        shouldShowMoreOptionsInNavBar
+        // TODO: remove !isNonEvmChainId check once bottom sheet options are fixed for non-EVM chains
+        shouldShowMoreOptionsInNavBar && !isNonEvmChainId(chainId)
           ? () =>
               navigation.navigate(Routes.MODAL.ROOT_MODAL_FLOW, {
                 screen: 'AssetOptions',
@@ -525,7 +529,9 @@ class Asset extends PureComponent {
     if (asset.isETH || asset.isNative) {
       isAssetAllowed = true;
     } else if (isAssetFromSearch(asset)) {
-      isAssetAllowed = this.props.searchDiscoverySwapsTokens?.includes(asset.address?.toLowerCase());
+      isAssetAllowed = this.props.searchDiscoverySwapsTokens?.includes(
+        asset.address?.toLowerCase(),
+      );
     } else {
       isAssetAllowed = asset.address?.toLowerCase() in this.props.swapsTokens;
     }
@@ -588,7 +594,10 @@ const mapStateToProps = (state, { route }) => ({
   swapsTokens: isPortfolioViewEnabled()
     ? swapsTokensMultiChainObjectSelector(state)
     : swapsTokensObjectSelector(state),
-  searchDiscoverySwapsTokens: selectSupportedSwapTokenAddressesForChainId(state, route.params.chainId),
+  searchDiscoverySwapsTokens: selectSupportedSwapTokenAddressesForChainId(
+    state,
+    route.params.chainId,
+  ),
   swapsTransactions: selectSwapsTransactions(state),
   conversionRate: selectConversionRate(state),
   currentCurrency: selectCurrentCurrency(state),
diff --git a/app/components/hooks/useTokenHistoricalPrices.ts b/app/components/hooks/useTokenHistoricalPrices.ts
index 366a397a7788..c2381617191a 100644
--- a/app/components/hooks/useTokenHistoricalPrices.ts
+++ b/app/components/hooks/useTokenHistoricalPrices.ts
@@ -1,13 +1,43 @@
-import { Hex } from '@metamask/utils';
+import { CaipAssetId, Hex } from '@metamask/utils';
 import { getDecimalChainId } from '../../util/networks';
 import { useState, useEffect } from 'react';
+///: BEGIN:ONLY_INCLUDE_IF(keyring-snaps)
+import { selectMultichainHistoricalPrices } from '../../selectors/multichain';
+///: END:ONLY_INCLUDE_IF
+import { useSelector } from 'react-redux';
+import { selectIsEvmNetworkSelected } from '../../selectors/multichainNetworkController';
+import Engine from '../../core/Engine';
+import { TokenI } from '../UI/Tokens/types';
 
 export type TimePeriod = '1d' | '1w' | '7d' | '1m' | '3m' | '1y' | '3y';
 
 export type TokenPrice = [string, number];
 
 const placeholderPrices = Array(289).fill(['0', 0] as TokenPrice);
+
+export const standardizeTimeInterval = (timePeriod: TimePeriod) => {
+  switch (timePeriod) {
+    case '1d':
+      return 'P1D';
+    case '1w':
+      return 'P7D';
+    case '7d':
+      return 'P7D';
+    case '1m':
+      return 'P1M';
+    case '3m':
+      return 'P3M';
+    case '1y':
+      return 'P1Y';
+    case '3y':
+      return 'P3Y';
+    default:
+      return 'P1D';
+  }
+};
+
 const useTokenHistoricalPrices = ({
+  asset,
   address,
   chainId,
   timePeriod,
@@ -15,6 +45,7 @@ const useTokenHistoricalPrices = ({
   to,
   vsCurrency,
 }: {
+  asset: TokenI;
   address: string;
   chainId: Hex;
   timePeriod: TimePeriod;
@@ -26,43 +57,80 @@ const useTokenHistoricalPrices = ({
   isLoading: boolean;
   error: Error | undefined;
 } => {
+  ///: BEGIN:ONLY_INCLUDE_IF(keyring-snaps)
+  const multichainHistoricalPrices = useSelector(
+    selectMultichainHistoricalPrices,
+  );
+  ///: END:ONLY_INCLUDE_IF
+  const isEvmSelected = useSelector(selectIsEvmNetworkSelected);
   const [prices, setPrices] = useState<TokenPrice[]>(placeholderPrices);
   const [isLoading, setIsLoading] = useState(true);
   const [error, setError] = useState<Error>();
+
   useEffect(() => {
     const fetchPrices = async () => {
       setIsLoading(true);
       try {
-        const baseUri = 'https://price.api.cx.metamask.io/v1';
-        const uri = new URL(
-          `${baseUri}/chains/${getDecimalChainId(
-            chainId,
-          )}/historical-prices/${address}`,
-        );
-        uri.searchParams.set(
-          'timePeriod',
-          timePeriod === '1w' ? '7d' : timePeriod,
-        );
-        uri.searchParams.set('vsCurrency', vsCurrency);
-        if (from && to) {
-          uri.searchParams.set('from', from.toString());
-          uri.searchParams.set('to', to.toString());
-        }
+        if (!isEvmSelected) {
+          const caip19Address = asset.address as CaipAssetId;
+          const standardizedTimeInterval = standardizeTimeInterval(timePeriod);
+
+          await Engine.context.MultichainAssetsRatesController.fetchHistoricalPricesForAsset(
+            caip19Address,
+          );
+          const result =
+            multichainHistoricalPrices[caip19Address][vsCurrency].intervals[
+              standardizedTimeInterval
+            ];
 
-        const response = await fetch(uri.toString());
-        const data: { prices: TokenPrice[] } = await response.json();
+          // Transform to ensure first value is string and second is number with max precision
+          const transformedResult = result.map(
+            ([timestamp, price]) =>
+              [timestamp.toString(), Number(price)] as TokenPrice,
+          );
+          setPrices(transformedResult);
+        } else {
+          const baseUri = 'https://price.api.cx.metamask.io/v1';
+          const uri = new URL(
+            `${baseUri}/chains/${getDecimalChainId(
+              chainId,
+            )}/historical-prices/${address}`,
+          );
+          uri.searchParams.set(
+            'timePeriod',
+            timePeriod === '1w' ? '7d' : timePeriod,
+          );
+          uri.searchParams.set('vsCurrency', vsCurrency);
+          if (from && to) {
+            uri.searchParams.set('from', from.toString());
+            uri.searchParams.set('to', to.toString());
+          }
 
-        setPrices(data.prices as TokenPrice[]);
-        // TODO: Replace "any" with type
-        // eslint-disable-next-line @typescript-eslint/no-explicit-any
-      } catch (e: any) {
-        setError(e);
+          const response = await fetch(uri.toString());
+          const data: { prices: TokenPrice[] } = await response.json();
+          setPrices(data.prices as TokenPrice[]);
+        }
+      } catch (e: unknown) {
+        setError(e as Error);
       } finally {
         setIsLoading(false);
       }
     };
     fetchPrices();
-  }, [address, chainId, timePeriod, from, to, vsCurrency]);
+  }, [
+    address,
+    chainId,
+    timePeriod,
+    from,
+    to,
+    vsCurrency,
+    isEvmSelected,
+    asset.address,
+    ///: BEGIN:ONLY_INCLUDE_IF(keyring-snaps)
+    multichainHistoricalPrices,
+    ///: END:ONLY_INCLUDE_IF
+  ]);
+
   return { data: prices, isLoading, error };
 };
 
diff --git a/app/selectors/multichain/evm.ts b/app/selectors/multichain/evm.ts
index 065004dd21e5..5e0f70aa99e3 100644
--- a/app/selectors/multichain/evm.ts
+++ b/app/selectors/multichain/evm.ts
@@ -43,6 +43,8 @@ import { isTestNet } from '../../util/networks';
 import { selectTokenMarketData } from '../tokenRatesController';
 import { deriveBalanceFromAssetMarketDetails } from '../../components/UI/Tokens/util';
 import { RootState } from '../../reducers';
+import { selectTokenList } from '../tokenListController';
+import { safeToChecksumAddress } from '../../util/address';
 
 interface NativeTokenBalance {
   balance: string;
@@ -130,9 +132,13 @@ export const selectNativeTokensAcrossChainsForAddress = createSelector(
       const nativeChainId = token.chainId as Hex;
       const nativeTokenInfoByChainId =
         nativeTokenBalancesByChainId[nativeChainId];
-      const isETH = ['ETH', 'GOETH', 'SepoliaETH', 'LineaETH', 'MegaETH'].includes(
-        token.nativeCurrency || '',
-      );
+      const isETH = [
+        'ETH',
+        'GOETH',
+        'SepoliaETH',
+        'LineaETH',
+        'MegaETH',
+      ].includes(token.nativeCurrency || '');
 
       const name = isETH ? 'Ethereum' : token.nativeCurrency;
       const logo = isETH ? '../images/eth-logo-new.png' : '';
@@ -477,3 +483,33 @@ export const selectEvmTokenFiatBalances = createDeepEqualSelector(
           ).balanceFiatCalculation;
     }),
 );
+
+export const selectEvmTokenMarketData = createDeepEqualSelector(
+  [
+    selectTokenList,
+    selectTokenMarketData,
+    (_state: RootState, params: { chainId: Hex; tokenAddress?: string }) =>
+      params.chainId,
+    (_state: RootState, params: { chainId: Hex; tokenAddress?: string }) =>
+      params.tokenAddress,
+  ],
+  (tokenList, marketData, chainId, tokenAddress) => {
+    // Handle native token case (no address)
+    if (!tokenAddress) {
+      return marketData?.[chainId]?.[zeroAddress() as Hex];
+    }
+
+    // Get checksummed address
+    const checksumAddress = safeToChecksumAddress(tokenAddress);
+    if (!checksumAddress) return null;
+
+    // Get token metadata and market data
+    const tokenMetadata = tokenList?.[checksumAddress.toLowerCase()];
+    const tokenMarketData = marketData?.[chainId]?.[checksumAddress as Hex];
+
+    return {
+      metadata: tokenMetadata,
+      marketData: tokenMarketData,
+    };
+  },
+);


Reviews:
user: login: gambinish
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-18T05:10:40Z
user: login: sahar-fehri
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-18T17:14:40Z
user: login: sahar-fehri
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-24T17:40:01Z
user: login: sahar-fehri
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-04-24T17:45:37Z
user: login: infiniteflower
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-04-24T17:46:52Z

Review comments:
diff_hunk: @@ -99,12 +100,24 @@ const AssetOverview: React.FC<AssetOverviewProps> = ({
 
   const currentAddress = asset.address as Hex;
 
-  const { data: prices = [], isLoading } = useTokenHistoricalPrices({
-    address: currentAddress,
-    chainId,
-    timePeriod,
-    vsCurrency: currentCurrency,
-  });
+  const { data: pricesV1 = [], isLoading: isLoadingV1 } =
+    useTokenHistoricalPrices({
+      address: currentAddress,
+      chainId,
+      timePeriod,
+      vsCurrency: currentCurrency,
+    });
+
+  const { data: pricesV3 = [], isLoading: isLoadingV3 } =
+    useTokenHistoricalPricesV3({
+      address: currentAddress,
+      chainId,
+      timePeriod,
+      vsCurrency: currentCurrency,
+    });
path: app/components/UI/AssetOverview/AssetOverview.tsx
user: login: gambinish
body: We shouldn't hit the API twice here, and it probably makes sense to consolidate this into a single hook.

Should we migrate the historical price fetching for evm to v3 as well? https://docs.cx.metamask.io/api/price/#tag/Historical-Prices/operation/PriceController_getHistoricalPricesByTokenAddress
created_at: 2025-04-18T05:10:40Z
author_association: MEMBER
diff_hunk: @@ -99,12 +100,24 @@ const AssetOverview: React.FC<AssetOverviewProps> = ({
 
   const currentAddress = asset.address as Hex;
 
-  const { data: prices = [], isLoading } = useTokenHistoricalPrices({
-    address: currentAddress,
-    chainId,
-    timePeriod,
-    vsCurrency: currentCurrency,
-  });
+  const { data: pricesV1 = [], isLoading: isLoadingV1 } =
+    useTokenHistoricalPrices({
+      address: currentAddress,
+      chainId,
+      timePeriod,
+      vsCurrency: currentCurrency,
+    });
+
+  const { data: pricesV3 = [], isLoading: isLoadingV3 } =
+    useTokenHistoricalPricesV3({
+      address: currentAddress,
+      chainId,
+      timePeriod,
+      vsCurrency: currentCurrency,
+    });
path: app/components/UI/AssetOverview/AssetOverview.tsx
user: login: sahar-fehri
body: The historical prices we fetch for nonEvm tokens will now come from the snap.
The snap supports only nonEvm assets for now so we will keep the old flow for EVMs; but for non EVMs we will now go through the snap.
(`fetchHistoricalPricesForAsset` is the the fct to call on the multichainAssetsRatesController to get historical prices for non evm)
created_at: 2025-04-18T17:14:40Z
author_association: CONTRIBUTOR
diff_hunk: @@ -233,7 +236,8 @@ class Asset extends PureComponent {
         false,
         navigation,
         colors,
-        shouldShowMoreOptionsInNavBar
+        // TODO: remove !isNonEvmChainId check once bottom sheet options are fixed for non-EVM chains
path: app/components/Views/Asset/index.js
user: login: sahar-fehri
body: Nit te remove the todo, can be done on the follow up PR
created_at: 2025-04-24T17:40:01Z
author_association: CONTRIBUTOR

PR comments:
user: login: github-actions[bot]
created_at: 2025-04-18T03:29:20Z
body: **CLA Signature Action:** All authors have signed the CLA. You may need to manually re-run the blocking PR check if it doesn't pass in a few minutes.
author_association: CONTRIBUTOR
user: login: github-actions[bot]
created_at: 2025-04-24T02:51:22Z
body: ## [<img alt="https://bitrise.io/" src="https://assets-global.website-files.com/5db35de024bb983af1b4e151/5e6f9ccc3e129dfd8a205e4e_Bitrise%20Logo%20-%20Eggplant%20Bg.png" height="20">](https://app.bitrise.io/app/be69d4368ee7e86d/pipelines/c23723a2-eb7a-4efa-b31a-ab392af256b8) **Bitrise**

❌❌❌ `pr_smoke_e2e_pipeline` failed on Bitrise! ❌❌❌

Commit hash: cdb932793b71c17b278da2425c1e70015305ddb8
Build link: https://app.bitrise.io/app/be69d4368ee7e86d/pipelines/c23723a2-eb7a-4efa-b31a-ab392af256b8

>[!NOTE]
>- You can kick off another `pr_smoke_e2e_pipeline` on Bitrise by removing and re-applying the `Run Smoke E2E` label on the pull request

> [!TIP]
>- Check the [documentation](https://www.notion.so/metamask-consensys/Bitrise-Pipeline-Overview-43159500c43748a389556f0593e8834b#26052f2ea6e24f8c9cfdb57a7522dc1f) if you have any doubts on how to understand the failure on bitrise

<!-- BITRISE_TAG -->
<!-- BITRISE_FAIL_TAG -->

<!-- cdb932793b71c17b278da2425c1e70015305ddb8 -->
author_association: CONTRIBUTOR
user: login: github-actions[bot]
created_at: 2025-04-24T04:34:59Z
body: ## [<img alt="https://bitrise.io/" src="https://assets-global.website-files.com/5db35de024bb983af1b4e151/5e6f9ccc3e129dfd8a205e4e_Bitrise%20Logo%20-%20Eggplant%20Bg.png" height="20">](https://app.bitrise.io/app/be69d4368ee7e86d/pipelines/8b387303-f789-4db9-8034-33fd7115b464) **Bitrise**

✅✅✅ `pr_smoke_e2e_pipeline` passed on Bitrise! ✅✅✅

Commit hash: 283eb7b86b5ecc17c0bb7506df12add6408ab6d5
Build link: https://app.bitrise.io/app/be69d4368ee7e86d/pipelines/8b387303-f789-4db9-8034-33fd7115b464

>[!NOTE]
>- You can kick off another `pr_smoke_e2e_pipeline` on Bitrise by removing and re-applying the `Run Smoke E2E` label on the pull request

<!-- BITRISE_TAG -->
<!-- BITRISE_SUCCESS_TAG -->

<!-- 283eb7b86b5ecc17c0bb7506df12add6408ab6d5 -->
author_association: CONTRIBUTOR
user: login: github-actions[bot]
created_at: 2025-04-24T17:07:34Z
body: ## [<img alt="https://bitrise.io/" src="https://assets-global.website-files.com/5db35de024bb983af1b4e151/5e6f9ccc3e129dfd8a205e4e_Bitrise%20Logo%20-%20Eggplant%20Bg.png" height="20">](https://app.bitrise.io/app/be69d4368ee7e86d/pipelines/07cc9f3a-3f40-42e8-aee0-95cf0cfdc70c) **Bitrise**

✅✅✅ `pr_smoke_e2e_pipeline` passed on Bitrise! ✅✅✅

Commit hash: 6a45122e9bc496bdc4ad9f021f15b66257fbd048
Build link: https://app.bitrise.io/app/be69d4368ee7e86d/pipelines/07cc9f3a-3f40-42e8-aee0-95cf0cfdc70c

>[!NOTE]
>- You can kick off another `pr_smoke_e2e_pipeline` on Bitrise by removing and re-applying the `Run Smoke E2E` label on the pull request

<!-- BITRISE_TAG -->
<!-- BITRISE_SUCCESS_TAG -->

<!-- 6a45122e9bc496bdc4ad9f021f15b66257fbd048 -->
author_association: CONTRIBUTOR
user: login: sonarqubecloud[bot]
created_at: 2025-04-24T18:49:13Z
body: ## [![Quality Gate Passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/qg-passed-20px.png 'Quality Gate Passed')](https://sonarcloud.io/dashboard?id=metamask-mobile&pullRequest=14736) **Quality Gate passed**  
Issues  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [9 New issues](https://sonarcloud.io/project/issues?id=metamask-mobile&pullRequest=14736&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/accepted-16px.png '') [0 Accepted issues](https://sonarcloud.io/project/issues?id=metamask-mobile&pullRequest=14736&issueStatuses=ACCEPTED)

Measures  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=metamask-mobile&pullRequest=14736&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [86.2% Coverage on New Code](https://sonarcloud.io/component_measures?id=metamask-mobile&pullRequest=14736&metric=new_coverage&view=list)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0.0% Duplication on New Code](https://sonarcloud.io/component_measures?id=metamask-mobile&pullRequest=14736&metric=new_duplicated_lines_density&view=list)  
  
[See analysis details on SonarQube Cloud](https://sonarcloud.io/dashboard?id=metamask-mobile&pullRequest=14736)


author_association: NONE