-
Notifications
You must be signed in to change notification settings - Fork 4.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: show all tokens for all chains in the token list #27785
Conversation
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. |
Thank you for posting this WIP Jony. One approach that may make this easier is starting with storybook and adding an AssetList story with mock data across all chains and ensuring it looks good there. That will likely make the rest easier. |
@darkwing That's a good tip, thanks! Renderisation seems to be working well, I'm figuring out how to fetch the correct values for each chain's balances at the moment. The views seem to be fine at the present moment. |
Hey @jonybur nice work getting this started! I requested myself as reviewer here since we'll be working in the same area. I'll request you on my PRs shortly. I know this is still pretty early on, but a couple things I noticed to put on your radar. You are probably already investigating these things! We'll want to make sure that we include the native tokens in the Sort by function to avoid a regression there: Screen.Recording.2024-10-17.at.6.31.26.AM.movDoes the Additionally, we'll also need to consider how we're handling the native token balances for each chain, since that's being handled a bit differently than the erc20 tokens. Currently when I'm on Ethereum mainnet, it's calculating the POL balance at the exchange rate of ETH on mainnet, rather than POL on Polygon (and vice versa). I think this also happens at the Screen.Recording.2024-10-17.at.6.30.31.AM.movWe should also take into consideration the I think the main blocker with this task to get fully functional, is that Has something already been updated to handle this? |
ui/selectors/multichain.ts
Outdated
return `eip155:${chainIdDecimal}`; | ||
} | ||
|
||
export function getChains(state: MultichainState): MultichainNetwork[] { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Whats the reason for the changes in this file? I understood it was for non-evm support, so wondering why it needs to change vs just having a selector in selectors.js
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I moved this to selectors.js
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I moved this to selectors.js
Hi @jonybur , If I understand correctly, the process in this PR works like this:
This approach has a significant impact on performance because it handles a large dataset. To improve this, I suggest simplifying it by making the marketData multichain in the assetsController. The marketData already includes the detected tokens, tokens imported by the user, and all market-related information, but right now, it only handles data for the selected network.
In the meantime, I recommend using mock data until we can handle this properly on the backend (specifically in the assetsController). |
I believe |
return sortAssets( | ||
[nativeTokenWithBalance, ...tokensWithBalances], | ||
tokenSortConfig, | ||
); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why are we removing native tokens from the sorting functionality here? Was there a product request to change this behavior?
My understanding is that we will need to include the native tokens with their balances, along with all erc20's so that they can be sorted in the correct order by declining fiat balance (and potentially future sort criteria)
This is likely where our main integration point will be for filterAssets
as well, and will probably get added before the sorting functionality higher in the tree.
Per my other comment, I think that we should move this up a level, so that we can do this data manipulation once before render, while still including nativeTokens
in its calculation to maintain our sort functionality. Otherwise, we will end up with separate arbitrarily ordered sorted lists for each chain, with the native tokens pinned at the top.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a wip, I removed it to simplify my testing. I'm looking at hardcoding marketData now
type TokenListItemWithBalanceProps = { | ||
chain: any; // Replace 'any' with the actual type of 'chain' if available | ||
token: AssetWithDisplayData<ERC20Asset> | AssetWithDisplayData<NativeAsset>; | ||
primaryCurrency: string; | ||
secondaryCurrency: string; | ||
primaryNumberOfDecimals: number; | ||
secondaryNumberOfDecimals: number; | ||
nativeCurrency: string; | ||
isDisabled: boolean; | ||
}; | ||
|
||
function TokenListItemWithBalance({ | ||
chain, | ||
token, | ||
primaryCurrency, | ||
secondaryCurrency, | ||
primaryNumberOfDecimals, | ||
secondaryNumberOfDecimals, | ||
nativeCurrency, | ||
isDisabled, | ||
}: TokenListItemWithBalanceProps) { | ||
const { chainId } = chain.network; | ||
const balancesByChainId = useSelector( | ||
getSelectedAccountCachedBalancesByChainId, | ||
); | ||
const balanceValue = balancesByChainId?.[chainId]; | ||
|
||
console.log({ balancesByChainId }); | ||
|
||
const [, primaryCurrencyProperties] = useCurrencyDisplay(balanceValue, { | ||
numberOfDecimals: primaryNumberOfDecimals, | ||
currency: primaryCurrency, | ||
}); | ||
const [secondaryCurrencyDisplay] = useCurrencyDisplay(balanceValue, { | ||
numberOfDecimals: secondaryNumberOfDecimals, | ||
currency: secondaryCurrency, | ||
hideLabel: true, | ||
}); | ||
|
||
return ( | ||
<TokenListItem | ||
title={nativeCurrency} | ||
chain={chain} | ||
primary={primaryCurrencyProperties.value} | ||
tokenSymbol={primaryCurrency} | ||
secondary={secondaryCurrencyDisplay} | ||
tokenImage={token.image} | ||
isOriginalTokenSymbol | ||
tooltipText={isDisabled ? 'swapTokenNotAvailable' : undefined} | ||
/> | ||
); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a reason why these changes can't be incorporated into the existing TokenListItem
component? This seems to essentially wrap that component.
<>{nativeToken}</> | ||
{chains.map((chain) => ( | ||
<TokenListForChain | ||
key={chain.network.chainId} | ||
chain={chain} | ||
onTokenClick={onTokenClick} | ||
tokenSortConfig={tokenSortConfig} | ||
selectedAccount={selectedAccount} | ||
conversionRate={conversionRate} | ||
contractExchangeRates={contractExchangeRates} | ||
shouldHideZeroBalanceTokens={shouldHideZeroBalanceTokens} | ||
/> | ||
))} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Rather than mapping over chains, rendering a separate list for each chain, and then pinning the nativeTokens
to the top, I would suggest doing some data manipulation first. I think it would make sense to move the sortedAssets useMemo
function up to this level, before the return.
This will allow us to:
-
Make this into a flat array first, so that we don't have a separate
TokenList
for each chain. We should instead have a singleTokenList
, which is correctly sorted and filtered for all tokens for all chains -
Allow us to include the native tokens for each chain in the sorted/filtered array, rather than pinning them as a fragment to the top of the list as we are here.
if (tokenData?.isNative) { | ||
// we need cloneElement so that we can pass the unique key | ||
return React.cloneElement(nativeToken as React.ReactElement, { | ||
key: `${tokenData.symbol}-${tokenData.address}`, | ||
}); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think that if we include the nativeTokens
as an array of react nodes in TokenList
, rather than a fragment as we are now, we can retain this logic here to allow React to target them independently
The idea: when all tokens (including native tokens) get memoized into a sorted and filtered array together, each native token can be identified by the isNative
prop. React can then render them as a NativeToken
component, while still retaining it's sorted/filtered order within the list.
nativeToken={ | ||
<> | ||
{chains?.map((chain) => ( | ||
<NativeToken chain={chain} onClickAsset={onClickAsset} /> | ||
))} | ||
</> | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's rename this prop to nativeTokens
Rather than wrapping them in a fragment, let's just pass them in as an array of NativeToken
elements
nativeToken={ | |
<> | |
{chains?.map((chain) => ( | |
<NativeToken chain={chain} onClickAsset={onClickAsset} /> | |
))} | |
</> | |
} | |
nativeTokens={chains?.map((chain) => ( | |
<NativeToken chain={chain} onClickAsset={onClickAsset} /> | |
))} |
This will allow us to render each of them independently farther down the component tree, since they won't be bundled together in a single fragment. They will need to appear in different orders in the list depending on sort and filter criteria
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, if you see an alternate approach to solve for this problem, I'm open to discussing that as well 👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hey Jony, went over this again and I left a few suggestions mainly related to how we can avoid regressions to our sorting functionality. This sortedAssets useMemo
looks like it will be our main integration point for our features, and I plan to incorporate filteredAssets
there.
If we can do the data manipulation to covert all tokens for all chains into a flat array within/before we hit that useMemo
hook, I think it should be a pretty straightforward integration.
I'd really like to see that hook get used higher up in the tree, so that it only gets called once, while keeping the nativeTokenBalances
in its calculation to maintain it's behavior.
As an aside, I synced with @bergeron and @salimtb and I think that there are indeed some changes that we should make in the |
## **Description** Adds Token network filter controls. Note that this is not fully functional, and is currently blocked by two PRs before it can be fully integrated: 1. #27785 2. MetaMask/core#4832 In the meantime, this PR is set behind a feature flag `FILTER_TOKENS_TOGGLE` and can be run as follows: `FILTER_TOKENS_TOGGLE=1 yarn webpack --watch` Alternatively: `FILTER_TOKENS_TOGGLE=1 yarn start` [![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://codespaces.new/MetaMask/metamask-extension/pull/27884?quickstart=1) Included in this PR: 1. Adds new `tokenNetworkFilter` preference to PreferencesController to manage which chains should be considered when filtering tokens 2. Adds an action to update this preference by All Networks (no filters) and Current Network `{ [chainId]: true) }` this is meant to be flexible enough to support multiple chains in the future. 3. Adds `filterAssets` function in a similar style to `sortAssets` it should be configuration based, and should be extensible enough to support filtering assets by deeply nested values (NFT traits), and to also support complex filter types (like price ranges). 4. Dropdown should show the balance for the selected network Not included in this PR: 1. Aggregated balance across chains. Blocked by MetaMask/core#4832 and currently hardcoded to $1000 2. Token lists will not be filtered in this PR. Blocked by #27785 ## **Related issues** https://github.com/orgs/MetaMask/projects/85/views/35?pane=issue&itemId=82217837 https://consensyssoftware.atlassian.net/browse/MMASSETS-430 ## **Manual testing steps** Token Filter selection should persist through refresh Current chain balance should reflect the balance of the current chain Should visibly match designs: https://www.figma.com/design/aMYisczaJyEsYl1TYdcPUL/Portfolio-View?node-id=5750-47217&node-type=canvas&t=EjOUPnqy7tWZE6sV-0 ## **Screenshots/Recordings** https://github.com/user-attachments/assets/4b132e47-0dcf-4e9c-8755-ccb2be1d5dc1 ## **Pre-merge author checklist** - [x] I've followed [MetaMask Contributor Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Extension Coding Standards](https://github.com/MetaMask/metamask-extension/blob/develop/.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-extension/blob/develop/.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. --------- Co-authored-by: Salim TOUBAL <salim.toubal@outlook.com>
Closing this in favor of #28386 |
Description
Related issues
Fixes:
Manual testing steps
Screenshots/Recordings
Before
After
Pre-merge author checklist
Pre-merge reviewer checklist