Skip to content

Commit

Permalink
feat: updated asset details to show correct info in charts for both n…
Browse files Browse the repository at this point in the history
…ative and non-native tokens
  • Loading branch information
vinnyhoward committed Nov 18, 2024
1 parent 4c0f35c commit 9b42569
Show file tree
Hide file tree
Showing 6 changed files with 197 additions and 69 deletions.
126 changes: 85 additions & 41 deletions app/components/UI/AssetOverview/AssetOverview.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { zeroAddress } from 'ethereumjs-util';
import React, { useCallback, useEffect } from 'react';
import { TouchableOpacity, View } from 'react-native';
import { useDispatch, useSelector } from 'react-redux';
import { Hex } from '@metamask/utils';
import { strings } from '../../../../locales/i18n';
import { TokenOverviewSelectorsIDs } from '../../../../e2e/selectors/TokenOverview.selectors';
import { newAssetTransaction } from '../../../actions/transaction';
Expand All @@ -14,8 +14,12 @@ import {
import {
selectConversionRate,
selectCurrentCurrency,
selectCurrencyRates,
} from '../../../selectors/currencyRateController';
import { selectContractExchangeRates } from '../../../selectors/tokenRatesController';
import {
selectContractExchangeRates,
selectTokenMarketData,
} from '../../../selectors/tokenRatesController';
import { selectAccountsByChainId } from '../../../selectors/accountTrackerController';
import { selectContractBalances } from '../../../selectors/tokenBalancesController';
import { selectSelectedInternalAccountChecksummedAddress } from '../../../selectors/accountsController';
Expand Down Expand Up @@ -53,6 +57,8 @@ import { createBuyNavigationDetails } from '../Ramp/routes/utils';
import { TokenI } from '../Tokens/types';
import AssetDetailsActions from '../../../components/Views/AssetDetails/AssetDetailsActions';

const isPortfolioViewEnabled = process.env.PORTFOLIO_VIEW === 'true';

interface AssetOverviewProps {
navigation: {
navigate: (route: string, params: Record<string, unknown>) => void;
Expand All @@ -69,8 +75,9 @@ const AssetOverview: React.FC<AssetOverviewProps> = ({
displaySwapsButton,
}: AssetOverviewProps) => {
const [timePeriod, setTimePeriod] = React.useState<TimePeriod>('1d');
const currentCurrency = useSelector(selectCurrentCurrency);
const conversionRate = useSelector(selectConversionRate);
const conversionRateByTicker = useSelector(selectCurrencyRates);
const currentCurrency = useSelector(selectCurrentCurrency);
const accountsByChainId = useSelector(selectAccountsByChainId);
const primaryCurrency = useSelector(
(state: RootState) => state.settings.primaryCurrency,
Expand All @@ -81,12 +88,20 @@ const AssetOverview: React.FC<AssetOverviewProps> = ({
);
const { trackEvent } = useMetrics();
const tokenExchangeRates = useSelector(selectContractExchangeRates);
const tokenExchangeRateByChainId = useSelector(selectTokenMarketData);
const tokenBalances = useSelector(selectContractBalances);
const chainId = useSelector((state: RootState) => selectChainId(state));
const ticker = useSelector((state: RootState) => selectTicker(state));
const selectedChainId = useSelector((state: RootState) =>
selectChainId(state),
);
const selectedTicker = useSelector((state: RootState) => selectTicker(state));

const chainId = isPortfolioViewEnabled
? (asset.chainId as Hex)
: selectedChainId;
const ticker = isPortfolioViewEnabled ? asset.symbol : selectedTicker;

const { data: prices = [], isLoading } = useTokenHistoricalPrices({
address: asset.isETH ? zeroAddress() : asset.address,
address: asset.address,
chainId,
timePeriod,
vsCurrency: currentCurrency,
Expand Down Expand Up @@ -201,55 +216,84 @@ const AssetOverview: React.FC<AssetOverviewProps> = ({
);

const itemAddress = safeToChecksumAddress(asset.address);
const exchangeRate = itemAddress
? tokenExchangeRates?.[itemAddress]?.price
: undefined;

let exchangeRate;
if (!isPortfolioViewEnabled) {
exchangeRate = itemAddress
? tokenExchangeRates?.[itemAddress]?.price
: undefined;
} else {
exchangeRate =
tokenExchangeRateByChainId?.[chainId]?.[itemAddress as Hex]?.price;
}

let balance, balanceFiat;
if (asset.isETH) {
balance = renderFromWei(
//@ts-expect-error - This should be fixed at the accountsController selector level, ongoing discussion
accountsByChainId[toHexadecimal(chainId)][selectedAddress]?.balance,
);
balanceFiat = weiToFiat(
hexToBN(
if (!isPortfolioViewEnabled) {
if (asset.isETH) {
balance = renderFromWei(
//@ts-expect-error - This should be fixed at the accountsController selector level, ongoing discussion
accountsByChainId[toHexadecimal(chainId)][selectedAddress]?.balance,
),
conversionRate,
currentCurrency,
);
);
balanceFiat = weiToFiat(
hexToBN(
//@ts-expect-error - This should be fixed at the accountsController selector level, ongoing discussion
accountsByChainId[toHexadecimal(chainId)][selectedAddress]?.balance,
),
conversionRate,
currentCurrency,
);
} else {
balance =
itemAddress && tokenBalances?.[itemAddress]
? renderFromTokenMinimalUnit(
tokenBalances[itemAddress],
asset.decimals,
)
: 0;
balanceFiat = balanceToFiat(
balance,
conversionRateByTicker[asset.symbol].conversionRate,
exchangeRate,
currentCurrency,
);
}
} else {
balance =
itemAddress && tokenBalances?.[itemAddress]
? renderFromTokenMinimalUnit(tokenBalances[itemAddress], asset.decimals)
: 0;
balanceFiat = balanceToFiat(
balance,
conversionRate,
exchangeRate,
currentCurrency,
);
balance = asset.balance;
balanceFiat = asset.balanceFiat;
}

let mainBalance, secondaryBalance;
if (primaryCurrency === 'ETH') {
mainBalance = `${balance} ${asset.symbol}`;
secondaryBalance = balanceFiat;
if (!isPortfolioViewEnabled) {
if (primaryCurrency === 'ETH') {
mainBalance = `${balance} ${asset.symbol}`;
secondaryBalance = balanceFiat;
} else {
mainBalance = !balanceFiat ? `${balance} ${asset.symbol}` : balanceFiat;
secondaryBalance = !balanceFiat
? balanceFiat
: `${balance} ${asset.symbol}`;
}
} else {
mainBalance = !balanceFiat ? `${balance} ${asset.symbol}` : balanceFiat;
secondaryBalance = !balanceFiat
? balanceFiat
: `${balance} ${asset.symbol}`;
mainBalance = `${balance} ${asset.symbol}`;
secondaryBalance = asset.balanceFiat;
}

let currentPrice = 0;
let priceDiff = 0;

if (asset.isETH) {
currentPrice = conversionRate || 0;
} else if (exchangeRate && conversionRate) {
currentPrice = exchangeRate * conversionRate;
if (!isPortfolioViewEnabled) {
if (asset.isETH) {
currentPrice = conversionRate || 0;
} else if (exchangeRate && conversionRate) {
currentPrice = exchangeRate * conversionRate;
}
} else {
const tickerConversionRate =
conversionRateByTicker[asset.symbol].conversionRate;
currentPrice =
exchangeRate && tickerConversionRate
? exchangeRate * tickerConversionRate
: 0;
}

const comparePrice = prices[0]?.[1] || 0;
Expand Down
112 changes: 100 additions & 12 deletions app/components/UI/AssetOverview/Balance/Balance.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import React from 'react';
import React, { useCallback } from 'react';
import { View } from 'react-native';
import { Hex } from '@metamask/utils';
import { strings } from '../../../../../locales/i18n';
import { useStyles } from '../../../../component-library/hooks';
import styleSheet from './Balance.styles';
Expand All @@ -9,6 +10,7 @@ import { selectNetworkName } from '../../../../selectors/networkInfos';
import { selectChainId } from '../../../../selectors/networkController';
import {
getTestNetImageByChainId,
getDefaultNetworkByChainId,
isLineaMainnetByChainId,
isMainnetByChainId,
isTestNet,
Expand All @@ -20,20 +22,28 @@ import Badge from '../../../../component-library/components/Badges/Badge/Badge';
import NetworkMainAssetLogo from '../../NetworkMainAssetLogo';
import AvatarToken from '../../../../component-library/components/Avatars/Avatar/variants/AvatarToken';
import { AvatarSize } from '../../../../component-library/components/Avatars/Avatar';
import NetworkAssetLogo from '../../NetworkAssetLogo';
import Text, {
TextVariant,
} from '../../../../component-library/components/Texts/Text';
import { TokenI } from '../../Tokens/types';
import { useNavigation } from '@react-navigation/native';
import { isPooledStakingFeatureEnabled } from '../../Stake/constants';
import StakingBalance from '../../Stake/components/StakingBalance/StakingBalance';
import {
PopularList,
UnpopularNetworkList,
CustomNetworkImgMapping,
} from '../../../../util/networks/customNetworks';

interface BalanceProps {
asset: TokenI;
mainBalance: string;
secondaryBalance?: string;
}

const isPortfolioViewEnabled = process.env.PORTFOLIO_VIEW === 'true';

export const NetworkBadgeSource = (chainId: string, ticker: string) => {
const isMainnet = isMainnetByChainId(chainId);
const isLineaMainnet = isLineaMainnetByChainId(chainId);
Expand All @@ -51,7 +61,93 @@ const Balance = ({ asset, mainBalance, secondaryBalance }: BalanceProps) => {
const { styles } = useStyles(styleSheet, {});
const navigation = useNavigation();
const networkName = useSelector(selectNetworkName);
const chainId = useSelector(selectChainId);
const selectedChainId = useSelector(selectChainId);

const chainId = isPortfolioViewEnabled
? (asset.chainId as Hex)
: selectedChainId;

const isMainnet = isMainnetByChainId(chainId);
const isLineaMainnet = isLineaMainnetByChainId(chainId);
const ticker = asset.symbol;

const renderNetworkAvatar = useCallback(() => {
if (!isPortfolioViewEnabled && asset.isETH) {
return <NetworkMainAssetLogo style={styles.ethLogo} />;
}

if (isPortfolioViewEnabled && asset.isNative) {
return (
<NetworkAssetLogo
chainId={asset.chainId as Hex}
style={styles.ethLogo}
ticker={asset.symbol}
big={false}
biggest={false}
testID={'PLACE HOLDER'}
/>
);
}

return (
<AvatarToken
name={asset.symbol}
imageSource={{ uri: asset.image }}
size={AvatarSize.Md}
/>
);
}, [
asset.isETH,
asset.image,
asset.symbol,
asset.isNative,
asset.chainId,
styles.ethLogo,
]);

const networkBadgeSource = useCallback(
(currentChainId: Hex) => {
if (!isPortfolioViewEnabled) {
if (isTestNet(chainId)) return getTestNetImageByChainId(chainId);
if (isMainnet) return images.ETHEREUM;

if (isLineaMainnet) return images['LINEA-MAINNET'];

if (CustomNetworkImgMapping[chainId]) {
return CustomNetworkImgMapping[chainId];
}

return ticker ? images[ticker as keyof typeof images] : undefined;
}
if (isTestNet(currentChainId))
return getTestNetImageByChainId(currentChainId);
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const defaultNetwork = getDefaultNetworkByChainId(currentChainId) as any;

if (defaultNetwork) {
return defaultNetwork.imageSource;
}

const unpopularNetwork = UnpopularNetworkList.find(
(networkConfig) => networkConfig.chainId === currentChainId,
);

const customNetworkImg = CustomNetworkImgMapping[currentChainId];

const popularNetwork = PopularList.find(
(networkConfig) => networkConfig.chainId === currentChainId,
);

const network = unpopularNetwork || popularNetwork;
if (network) {
return network.rpcPrefs.imageSource;
}
if (customNetworkImg) {
return customNetworkImg;
}
},
[chainId, isLineaMainnet, isMainnet, ticker],
);

return (
<View style={styles.wrapper}>
Expand All @@ -69,20 +165,12 @@ const Balance = ({ asset, mainBalance, secondaryBalance }: BalanceProps) => {
badgeElement={
<Badge
variant={BadgeVariant.Network}
imageSource={NetworkBadgeSource(chainId, asset.symbol)}
imageSource={networkBadgeSource(chainId)}
name={networkName}
/>
}
>
{asset.isETH ? (
<NetworkMainAssetLogo style={styles.ethLogo} />
) : (
<AvatarToken
name={asset.symbol}
imageSource={{ uri: asset.image }}
size={AvatarSize.Md}
/>
)}
{renderNetworkAvatar()}
</BadgeWrapper>
<Text style={styles.balances} variant={TextVariant.BodyLGMedium}>
{asset.name || asset.symbol}
Expand Down
4 changes: 2 additions & 2 deletions app/components/UI/Tokens/TokenList/TokenListItem/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -175,13 +175,13 @@ export const TokenListItem = ({
mainBalance = balanceValueFormatted;
secondaryBalance = strings('wallet.unable_to_find_conversion_rate');
}

asset = { ...asset, balanceFiat };
} else {
mainBalance = asset.balance;
secondaryBalance = asset.balanceFiat;
}

asset = { ...asset, balanceFiat };

const isMainnet = isMainnetByChainId(chainId);
const isLineaMainnet = isLineaMainnetByChainId(chainId);

Expand Down
4 changes: 2 additions & 2 deletions app/components/UI/Tokens/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,6 @@ export interface TokenI {
hasBalanceError?: boolean;
isStaked?: boolean | undefined;
nativeAsset?: TokenI | undefined;
chainId?: string; // TODO: may need to remove optional
isNative?: boolean; // TODO: may need to remove optional
chainId?: string;
isNative?: boolean;
}
10 changes: 0 additions & 10 deletions app/selectors/currencyRateController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,13 +56,3 @@ export const selectConversionRateByTicker = createSelector(
0
: 0,
);

export const selectConversionRateByTickerAlt = createSelector(
selectCurrencyRateControllerState,
(_: RootState, ticker: string) => ticker,
(currencyRateControllerState: CurrencyRateState, ticker: string) =>
ticker
? currencyRateControllerState?.currencyRates?.[ticker]?.conversionRate ||
0
: 0,
);
Loading

0 comments on commit 9b42569

Please sign in to comment.