Skip to content

Commit

Permalink
Merge branch 'feat/mmassets-432_network-filter-extension--integration…
Browse files Browse the repository at this point in the history
…-balances' into 28534-asset-page-native-fix
  • Loading branch information
darkwing authored Nov 21, 2024
2 parents 87790df + 4d3aeba commit 2c711e2
Show file tree
Hide file tree
Showing 13 changed files with 197 additions and 74 deletions.
1 change: 0 additions & 1 deletion app/scripts/metamask-controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -6747,7 +6747,6 @@ export default class MetamaskController extends EventEmitter {
this.tokenListController.stopAllPolling();
this.tokenBalancesController.stopAllPolling();
this.appStateController.clearPollingTokens();
this.tokenBalancesController.stopAllPolling();
this.accountTrackerController.stopAllPolling();
} catch (error) {
console.error(error);
Expand Down
1 change: 0 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,6 @@
"attributions:generate": "./development/generate-attributions.sh"
},
"resolutions": {
"@metamask/assets-controllers": "patch:@metamask/assets-controllers@patch%3A@metamask/assets-controllers@npm%253A44.0.0%23~/.yarn/patches/@metamask-assets-controllers-npm-44.0.0-c223d56176.patch%3A%3Aversion=44.0.0&hash=5a94c2#~/.yarn/patches/@metamask-assets-controllers-patch-9e00573eb4.patch",
"chokidar": "^3.6.0",
"gridplus-sdk/elliptic": "^6.5.7",
"gridplus-sdk/secp256k1": "^5.0.1",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,11 @@
border-radius: 8px;
padding: 0 8px !important;
gap: 5px;
text-transform: lowercase;

span::first-letter {
text-transform: uppercase;
}
}

&__buttons {
Expand Down
4 changes: 4 additions & 0 deletions ui/components/app/assets/asset-list/asset-list.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import {
import { getIsNativeTokenBuyable } from '../../../../ducks/ramps';
///: END:ONLY_INCLUDE_IF
import AssetListControlBar from './asset-list-control-bar';
import NativeToken from './native-token';

export type TokenWithBalance = {
address: string;
Expand Down Expand Up @@ -109,6 +110,9 @@ const AssetList = ({ onClickAsset, showTokensLinks }: AssetListProps) => {
)}
<AssetListControlBar showTokensLinks={shouldShowTokensLinks} />
<TokenList
// nativeToken is still needed to avoid breaking flask build's support for bitcoin
// TODO: refactor this to no longer be needed for non-evm chains
nativeToken={!isEvm && <NativeToken onClickAsset={onClickAsset} />}
onTokenClick={(chainId: string, tokenAddress: string) => {
onClickAsset(chainId, tokenAddress);
trackEvent({
Expand Down
1 change: 1 addition & 0 deletions ui/components/app/assets/asset-list/native-token/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default } from './native-token';
52 changes: 52 additions & 0 deletions ui/components/app/assets/asset-list/native-token/native-token.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import React from 'react';
import { useSelector } from 'react-redux';
import {
getMultichainCurrentNetwork,
getMultichainNativeCurrency,
getMultichainIsEvm,
getMultichainCurrencyImage,
getMultichainIsMainnet,
getMultichainSelectedAccountCachedBalance,
} from '../../../../../selectors/multichain';
import { getPreferences } from '../../../../../selectors';
import { TokenListItem } from '../../../../multichain';
import { AssetListProps } from '../asset-list';
import { useNativeTokenBalance } from './use-native-token-balance';

const NativeToken = ({ onClickAsset }: AssetListProps) => {
const nativeCurrency = useSelector(getMultichainNativeCurrency);
const isMainnet = useSelector(getMultichainIsMainnet);
const { chainId } = useSelector(getMultichainCurrentNetwork);
const { privacyMode } = useSelector(getPreferences);
const balance = useSelector(getMultichainSelectedAccountCachedBalance);
const balanceIsLoading = !balance;

const { string, symbol, secondary } = useNativeTokenBalance();

const primaryTokenImage = useSelector(getMultichainCurrencyImage);

const isEvm = useSelector(getMultichainIsEvm);

let isStakeable = isMainnet && isEvm;
///: BEGIN:ONLY_INCLUDE_IF(build-mmi)
isStakeable = false;
///: END:ONLY_INCLUDE_IF

return (
<TokenListItem
chainId={chainId}
onClick={() => onClickAsset(chainId, nativeCurrency)}
title={nativeCurrency}
primary={string}
tokenSymbol={symbol}
secondary={secondary}
tokenImage={balanceIsLoading ? null : primaryTokenImage}
isNativeCurrency
isStakeable={isStakeable}
showPercentage
privacyMode={privacyMode}
/>
);
};

export default NativeToken;
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import currencyFormatter from 'currency-formatter';
import { useSelector } from 'react-redux';

import {
getMultichainCurrencyImage,
getMultichainCurrentNetwork,
getMultichainSelectedAccountCachedBalance,
getMultichainShouldShowFiat,
} from '../../../../../selectors/multichain';
import { getCurrentCurrency, getPreferences } from '../../../../../selectors';
import { useIsOriginalNativeTokenSymbol } from '../../../../../hooks/useIsOriginalNativeTokenSymbol';
import { PRIMARY, SECONDARY } from '../../../../../helpers/constants/common';
import { useUserPreferencedCurrency } from '../../../../../hooks/useUserPreferencedCurrency';
import { useCurrencyDisplay } from '../../../../../hooks/useCurrencyDisplay';
import { TokenWithBalance } from '../asset-list';

export const useNativeTokenBalance = () => {
const showFiat = useSelector(getMultichainShouldShowFiat);
const primaryTokenImage = useSelector(getMultichainCurrencyImage);
const { showNativeTokenAsMainBalance } = useSelector(getPreferences);
const { chainId, ticker, type, rpcUrl } = useSelector(
getMultichainCurrentNetwork,
);
const isOriginalNativeSymbol = useIsOriginalNativeTokenSymbol(
chainId,
ticker,
type,
rpcUrl,
);
const balance = useSelector(getMultichainSelectedAccountCachedBalance);
const currentCurrency = useSelector(getCurrentCurrency);
const {
currency: primaryCurrency,
numberOfDecimals: primaryNumberOfDecimals,
} = useUserPreferencedCurrency(PRIMARY, {
ethNumberOfDecimals: 4,
shouldCheckShowNativeToken: true,
});
const {
currency: secondaryCurrency,
numberOfDecimals: secondaryNumberOfDecimals,
} = useUserPreferencedCurrency(SECONDARY, {
ethNumberOfDecimals: 4,
shouldCheckShowNativeToken: true,
});

const [primaryCurrencyDisplay, primaryCurrencyProperties] =
useCurrencyDisplay(balance, {
numberOfDecimals: primaryNumberOfDecimals,
currency: primaryCurrency,
});

const [secondaryCurrencyDisplay, secondaryCurrencyProperties] =
useCurrencyDisplay(balance, {
numberOfDecimals: secondaryNumberOfDecimals,
currency: secondaryCurrency,
});

const primaryBalance = isOriginalNativeSymbol
? secondaryCurrencyDisplay
: undefined;

const secondaryBalance =
showFiat && isOriginalNativeSymbol ? primaryCurrencyDisplay : undefined;

const tokenSymbol = showNativeTokenAsMainBalance
? primaryCurrencyProperties.suffix
: secondaryCurrencyProperties.suffix;

const unformattedTokenFiatAmount = showNativeTokenAsMainBalance
? secondaryCurrencyDisplay.toString()
: primaryCurrencyDisplay.toString();

// useCurrencyDisplay passes along the symbol and formatting into the value here
// for sorting we need the raw value, without the currency and it should be decimal
// this is the easiest way to do this without extensive refactoring of useCurrencyDisplay
const tokenFiatAmount = currencyFormatter
.unformat(unformattedTokenFiatAmount, {
code: currentCurrency.toUpperCase(),
})
.toString();

const nativeTokenWithBalance: TokenWithBalance = {
address: '',
symbol: tokenSymbol ?? '',
string: primaryBalance,
image: primaryTokenImage,
secondary: secondaryBalance,
tokenFiatAmount,
isNative: true,
};

return nativeTokenWithBalance;
};
15 changes: 4 additions & 11 deletions ui/components/app/assets/token-cell/token-cell.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,7 @@ import { fireEvent } from '@testing-library/react';
import { useSelector } from 'react-redux';
import { renderWithProvider } from '../../../../../test/lib/render-helpers';
import { useTokenFiatAmount } from '../../../../hooks/useTokenFiatAmount';
import {
getTokenList,
getPreferences,
getCurrentCurrency,
} from '../../../../selectors';
import { getTokenList, getPreferences } from '../../../../selectors';
import {
getMultichainCurrentChainId,
getMultichainIsEvm,
Expand Down Expand Up @@ -43,7 +39,7 @@ describe('Token Cell', () => {
const mockState = {
metamask: {
marketData: {
'0x89': {
'0x1': {
'0xAnotherToken': { price: 0.015 },
},
},
Expand Down Expand Up @@ -89,7 +85,7 @@ describe('Token Cell', () => {
string: '5.000',
currentCurrency: 'usd',
image: '',
chainId: '0x89',
chainId: '0x1',
tokenFiatAmount: 5,
onClick: jest.fn(),
};
Expand All @@ -100,7 +96,7 @@ describe('Token Cell', () => {
string: '5000000',
currentCurrency: 'usd',
image: '',
chainId: '0x89',
chainId: '0x1',
tokenFiatAmount: 5000000,
onClick: jest.fn(),
};
Expand All @@ -121,9 +117,6 @@ describe('Token Cell', () => {
if (selector === getIntlLocale) {
return 'en-US';
}
if (selector === getCurrentCurrency) {
return 'usd';
}
return undefined;
});
(useTokenFiatAmount as jest.Mock).mockReturnValue('5.00');
Expand Down
18 changes: 16 additions & 2 deletions ui/components/app/assets/token-list/token-list.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import React, { ReactNode, useEffect, useMemo } from 'react';
import { shallowEqual, useSelector, useDispatch } from 'react-redux';
import { Hex } from '@metamask/utils';
import TokenCell from '../token-cell';
import { useI18nContext } from '../../../../hooks/useI18nContext';
import { TEST_CHAINS } from '../../../../../shared/constants/network';
import { sortAssets } from '../util/sort';
import {
Expand All @@ -24,6 +23,7 @@ import { calculateTokenFiatAmount } from '../util/calculateTokenFiatAmount';
import { endTrace, TraceName } from '../../../../../shared/lib/trace';
import { useTokenBalances } from '../../../../hooks/useTokenBalances';
import { setTokenNetworkFilter } from '../../../../store/actions';
import { useI18nContext } from '../../../../hooks/useI18nContext';

type TokenListProps = {
onTokenClick: (chainId: string, address: string) => void;
Expand Down Expand Up @@ -74,7 +74,10 @@ const useFilteredAccountTokens = (currentNetwork: { chainId: string }) => {
return filteredAccountTokensChains;
};

export default function TokenList({ onTokenClick }: TokenListProps) {
export default function TokenList({
onTokenClick,
nativeToken,
}: TokenListProps) {
const t = useI18nContext();
const dispatch = useDispatch();
const currentNetwork = useSelector(getCurrentNetwork);
Expand Down Expand Up @@ -198,6 +201,17 @@ export default function TokenList({ onTokenClick }: TokenListProps) {
}
}, [sortedFilteredTokens]);

// Displays nativeToken if provided
if (nativeToken) {
return React.cloneElement(nativeToken as React.ReactElement);
}

// TODO: We can remove this string. However it will result in a huge file 50+ file diff
// Lets remove it in a separate PR
if (sortedFilteredTokens === undefined) {
console.log(t('loadingTokens'));
}

return (
<div>
{sortedFilteredTokens.map((tokenData) => (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,6 @@ export default function UserPreferencedCurrencyDisplay({
data-testid={dataTestId}
numberOfDecimals={numberOfDecimals}
prefixComponent={prefixComponent}
hideLabel={!showCurrencySuffix}
suffix={showCurrencySuffix && !showEthLogo && currency}
privacyMode={privacyMode}
/>
Expand Down
35 changes: 17 additions & 18 deletions ui/hooks/useAccountTotalFiatBalance.js
Original file line number Diff line number Diff line change
Expand Up @@ -68,22 +68,21 @@ export const useAccountTotalFiatBalance = (
};

// Create fiat values for token balances
const tokenFiatBalances =
tokensWithBalances?.map((token) => {
const tokenExchangeRate = mergedRates[toChecksumAddress(token.address)];

const totalFiatValue = getTokenFiatAmount(
tokenExchangeRate,
conversionRate,
currentCurrency,
token.string,
token.symbol,
false,
false,
);
const tokenFiatBalances = tokensWithBalances.map((token) => {
const tokenExchangeRate = mergedRates[toChecksumAddress(token.address)];

return totalFiatValue;
}) || [];
const totalFiatValue = getTokenFiatAmount(
tokenExchangeRate,
conversionRate,
currentCurrency,
token.string,
token.symbol,
false,
false,
);

return totalFiatValue;
});

// Create an object with native token info. NOTE: Native token info is fetched from a separate controller
const nativeTokenValues = {
Expand All @@ -96,7 +95,7 @@ export const useAccountTotalFiatBalance = (
const findMatchingTokens = (tokenList, _tokensWithBalances) => {
const result = [];

_tokensWithBalances?.forEach((token) => {
_tokensWithBalances.forEach((token) => {
const matchingToken = tokenList[token.address.toLowerCase()];

if (matchingToken) {
Expand Down Expand Up @@ -136,13 +135,13 @@ export const useAccountTotalFiatBalance = (

// we need to append some values to tokensWithBalance for UI
// this code was ported from asset-list
tokensWithBalances?.forEach((token) => {
tokensWithBalances.forEach((token) => {
// token.string is the balance displayed in the TokenList UI
token.string = roundToDecimalPlacesRemovingExtraZeroes(token.string, 5);
});

// to sort by fiat balance, we need to compute this at this level
tokensWithBalances?.forEach((token) => {
tokensWithBalances.forEach((token) => {
const tokenExchangeRate = mergedRates[toChecksumAddress(token.address)];

token.tokenFiatAmount =
Expand Down
Loading

0 comments on commit 2c711e2

Please sign in to comment.