diff --git a/packages/assets-controllers/CHANGELOG.md b/packages/assets-controllers/CHANGELOG.md index 5085c13ace1..39c58bceec2 100644 --- a/packages/assets-controllers/CHANGELOG.md +++ b/packages/assets-controllers/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Added + +- Added optional filtering of Tron staking tokens (energy, bandwidth) in asset selectors `selectAssetsBySelectedAccountGroup` (defaults to `true`) ([#7198](https://github.com/MetaMask/core/pull/7198)) + ## [89.0.1] ### Fixed diff --git a/packages/assets-controllers/src/selectors/__fixtures__/arrange-tron-state.ts b/packages/assets-controllers/src/selectors/__fixtures__/arrange-tron-state.ts new file mode 100644 index 00000000000..774ea49d741 --- /dev/null +++ b/packages/assets-controllers/src/selectors/__fixtures__/arrange-tron-state.ts @@ -0,0 +1,327 @@ +export const MOCK_TRON_TOKENS = { + 'tron:728126428': [ + { + accountType: 'tron:eoa', + assetId: 'tron:728126428/slip44:195', + isNative: true, + image: + 'https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/tron/info/logo.png', + name: 'Tron', + symbol: 'TRX', + accountId: 'de5c3465-d01e-4091-a219-232903e982bb', + decimals: 6, + rawBalance: '0x0', + balance: '0', + fiat: { + balance: 0, + currency: 'usd', + conversionRate: 0.28516, + }, + chainId: 'tron:728126428', + }, + { + accountType: 'tron:eoa', + assetId: 'tron:728126428/slip44:195-staked-for-bandwidth', + isNative: true, + image: + 'https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/tron/info/logo.png', + name: 'Staked for Bandwidth', + symbol: 'sTRX-BANDWIDTH', + accountId: 'de5c3465-d01e-4091-a219-232903e982bb', + decimals: 6, + rawBalance: '0x0', + balance: '0', + chainId: 'tron:728126428', + fiat: undefined, + }, + { + accountType: 'tron:eoa', + assetId: 'tron:728126428/slip44:195-staked-for-energy', + isNative: true, + image: + 'https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/tron/info/logo.png', + name: 'Staked for Energy', + symbol: 'sTRX-ENERGY', + accountId: 'de5c3465-d01e-4091-a219-232903e982bb', + decimals: 6, + rawBalance: '0x0', + balance: '0', + chainId: 'tron:728126428', + fiat: undefined, + }, + { + accountType: 'tron:eoa', + assetId: 'tron:728126428/slip44:bandwidth', + isNative: true, + image: + 'https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/tron/info/logo.png', + name: 'Bandwidth', + symbol: 'BANDWIDTH', + accountId: 'de5c3465-d01e-4091-a219-232903e982bb', + decimals: 0, + rawBalance: '0x0', + balance: '0', + chainId: 'tron:728126428', + fiat: undefined, + }, + { + accountType: 'tron:eoa', + assetId: 'tron:728126428/slip44:maximum-bandwidth', + isNative: true, + image: + 'https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/tron/info/logo.png', + name: 'Max Bandwidth', + symbol: 'MAX-BANDWIDTH', + accountId: 'de5c3465-d01e-4091-a219-232903e982bb', + decimals: 0, + rawBalance: '0x0', + balance: '0', + chainId: 'tron:728126428', + fiat: undefined, + }, + { + accountType: 'tron:eoa', + assetId: 'tron:728126428/slip44:energy', + isNative: true, + image: + 'https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/tron/info/logo.png', + name: 'Energy', + symbol: 'ENERGY', + accountId: 'de5c3465-d01e-4091-a219-232903e982bb', + decimals: 0, + rawBalance: '0x0', + balance: '0', + chainId: 'tron:728126428', + fiat: undefined, + }, + { + accountType: 'tron:eoa', + assetId: 'tron:728126428/slip44:maximum-energy', + isNative: true, + image: + 'https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/tron/info/logo.png', + name: 'Max Energy', + symbol: 'MAX-ENERGY', + accountId: 'de5c3465-d01e-4091-a219-232903e982bb', + decimals: 0, + rawBalance: '0x0', + balance: '0', + chainId: 'tron:728126428', + fiat: undefined, + }, + ], + 'tron:3448148188': [ + { + accountType: 'tron:eoa', + assetId: 'tron:3448148188/slip44:195', + isNative: true, + image: + 'https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/tron/info/logo.png', + name: 'Tron', + symbol: 'TRX', + accountId: 'de5c3465-d01e-4091-a219-232903e982bb', + decimals: 6, + rawBalance: '0x0', + balance: '0', + chainId: 'tron:3448148188', + fiat: undefined, + }, + { + accountType: 'tron:eoa', + assetId: 'tron:3448148188/slip44:195-staked-for-bandwidth', + isNative: true, + image: + 'https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/tron/info/logo.png', + name: 'Staked for Bandwidth', + symbol: 'sTRX-BANDWIDTH', + accountId: 'de5c3465-d01e-4091-a219-232903e982bb', + decimals: 6, + rawBalance: '0x0', + balance: '0', + chainId: 'tron:3448148188', + fiat: undefined, + }, + { + accountType: 'tron:eoa', + assetId: 'tron:3448148188/slip44:195-staked-for-energy', + isNative: true, + image: + 'https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/tron/info/logo.png', + name: 'Staked for Energy', + symbol: 'sTRX-ENERGY', + accountId: 'de5c3465-d01e-4091-a219-232903e982bb', + decimals: 6, + rawBalance: '0x0', + balance: '0', + chainId: 'tron:3448148188', + fiat: undefined, + }, + { + accountType: 'tron:eoa', + assetId: 'tron:3448148188/slip44:bandwidth', + isNative: true, + image: + 'https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/tron/info/logo.png', + name: 'Bandwidth', + symbol: 'BANDWIDTH', + accountId: 'de5c3465-d01e-4091-a219-232903e982bb', + decimals: 0, + rawBalance: '0x0', + balance: '0', + chainId: 'tron:3448148188', + fiat: undefined, + }, + { + accountType: 'tron:eoa', + assetId: 'tron:3448148188/slip44:maximum-bandwidth', + isNative: true, + image: + 'https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/tron/info/logo.png', + name: 'Max Bandwidth', + symbol: 'MAX-BANDWIDTH', + accountId: 'de5c3465-d01e-4091-a219-232903e982bb', + decimals: 0, + rawBalance: '0x0', + balance: '0', + chainId: 'tron:3448148188', + fiat: undefined, + }, + { + accountType: 'tron:eoa', + assetId: 'tron:3448148188/slip44:energy', + isNative: true, + image: + 'https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/tron/info/logo.png', + name: 'Energy', + symbol: 'ENERGY', + accountId: 'de5c3465-d01e-4091-a219-232903e982bb', + decimals: 0, + rawBalance: '0x0', + balance: '0', + chainId: 'tron:3448148188', + fiat: undefined, + }, + { + accountType: 'tron:eoa', + assetId: 'tron:3448148188/slip44:maximum-energy', + isNative: true, + image: + 'https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/tron/info/logo.png', + name: 'Max Energy', + symbol: 'MAX-ENERGY', + accountId: 'de5c3465-d01e-4091-a219-232903e982bb', + decimals: 0, + rawBalance: '0x0', + balance: '0', + chainId: 'tron:3448148188', + fiat: undefined, + }, + ], + 'tron:2494104990': [ + { + accountType: 'tron:eoa', + assetId: 'tron:2494104990/slip44:195', + isNative: true, + image: + 'https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/tron/info/logo.png', + name: 'Tron', + symbol: 'TRX', + accountId: 'de5c3465-d01e-4091-a219-232903e982bb', + decimals: 6, + rawBalance: '0x0', + balance: '0', + chainId: 'tron:2494104990', + fiat: undefined, + }, + { + accountType: 'tron:eoa', + assetId: 'tron:2494104990/slip44:195-staked-for-bandwidth', + isNative: true, + image: + 'https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/tron/info/logo.png', + name: 'Staked for Bandwidth', + symbol: 'sTRX-BANDWIDTH', + accountId: 'de5c3465-d01e-4091-a219-232903e982bb', + decimals: 6, + rawBalance: '0x0', + balance: '0', + chainId: 'tron:2494104990', + fiat: undefined, + }, + { + accountType: 'tron:eoa', + assetId: 'tron:2494104990/slip44:195-staked-for-energy', + isNative: true, + image: + 'https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/tron/info/logo.png', + name: 'Staked for Energy', + symbol: 'sTRX-ENERGY', + accountId: 'de5c3465-d01e-4091-a219-232903e982bb', + decimals: 6, + rawBalance: '0x0', + balance: '0', + chainId: 'tron:2494104990', + fiat: undefined, + }, + { + accountType: 'tron:eoa', + assetId: 'tron:2494104990/slip44:bandwidth', + isNative: true, + image: + 'https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/tron/info/logo.png', + name: 'Bandwidth', + symbol: 'BANDWIDTH', + accountId: 'de5c3465-d01e-4091-a219-232903e982bb', + decimals: 0, + rawBalance: '0x0', + balance: '0', + chainId: 'tron:2494104990', + fiat: undefined, + }, + { + accountType: 'tron:eoa', + assetId: 'tron:2494104990/slip44:maximum-bandwidth', + isNative: true, + image: + 'https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/tron/info/logo.png', + name: 'Max Bandwidth', + symbol: 'MAX-BANDWIDTH', + accountId: 'de5c3465-d01e-4091-a219-232903e982bb', + decimals: 0, + rawBalance: '0x0', + balance: '0', + chainId: 'tron:2494104990', + fiat: undefined, + }, + { + accountType: 'tron:eoa', + assetId: 'tron:2494104990/slip44:energy', + isNative: true, + image: + 'https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/tron/info/logo.png', + name: 'Energy', + symbol: 'ENERGY', + accountId: 'de5c3465-d01e-4091-a219-232903e982bb', + decimals: 0, + rawBalance: '0x0', + balance: '0', + chainId: 'tron:2494104990', + fiat: undefined, + }, + { + accountType: 'tron:eoa', + assetId: 'tron:2494104990/slip44:maximum-energy', + isNative: true, + image: + 'https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/tron/info/logo.png', + name: 'Max Energy', + symbol: 'MAX-ENERGY', + accountId: 'de5c3465-d01e-4091-a219-232903e982bb', + decimals: 0, + rawBalance: '0x0', + balance: '0', + chainId: 'tron:2494104990', + fiat: undefined, + }, + ], +} as const; diff --git a/packages/assets-controllers/src/selectors/token-selectors.test.ts b/packages/assets-controllers/src/selectors/token-selectors.test.ts index 778abaae2b6..a912b14da1e 100644 --- a/packages/assets-controllers/src/selectors/token-selectors.test.ts +++ b/packages/assets-controllers/src/selectors/token-selectors.test.ts @@ -5,9 +5,12 @@ import type { AccountWalletObject, } from '@metamask/account-tree-controller'; import type { AccountsControllerState } from '@metamask/accounts-controller'; +import { TrxScope } from '@metamask/keyring-api'; import type { NetworkState } from '@metamask/network-controller'; import type { Hex } from '@metamask/utils'; +import { cloneDeep } from 'lodash'; +import { MOCK_TRON_TOKENS } from './__fixtures__/arrange-tron-state'; import { selectAssetsBySelectedAccountGroup } from './token-selectors'; import type { AccountGroupMultichainAccountObject } from '../../../account-tree-controller/src/group'; import type { CurrencyRateState } from '../CurrencyRateController'; @@ -918,5 +921,108 @@ describe('token-selectors', () => { expect(result).toStrictEqual(expectedMockResult); }); + + const arrangeTronState = () => { + const state = cloneDeep(mockedMergedState); + + // Add Tron account to the selected account group + state.accountTree.wallets['entropy:01K1TJY9QPSCKNBSVGZNG510GJ'].groups[ + 'entropy:01K1TJY9QPSCKNBSVGZNG510GJ/0' + ].accounts.push('de5c3465-d01e-4091-a219-232903e982bb'); + + // Add Tron account to accounts controller + state.internalAccounts.accounts['de5c3465-d01e-4091-a219-232903e982bb'] = + { + id: 'de5c3465-d01e-4091-a219-232903e982bb', + address: 'TYasKMTpukV8rLfkSzqrFH7VcCwjBFJ7s9', + type: 'tron:eoa', + scopes: ['tron:728126428', 'tron:3448148188', 'tron:2494104990'], + methods: ['tron_signTransaction', 'tron_signMessage'], + options: { + entropySource: '01K1TJY9QPSCKNBSVGZNG510GJ', + groupIndex: 0, + }, + metadata: { + name: 'Tron Account 1', + keyring: { type: 'Snap Keyring' }, + importTime: Date.now(), + }, + }; + + // Extract asset IDs from MOCK_TRON_TOKENS and add to accountsAssets + const allTronAssetIds = Object.values(MOCK_TRON_TOKENS) + .flat() + .map((token) => token.assetId); + state.accountsAssets['de5c3465-d01e-4091-a219-232903e982bb'] = + allTronAssetIds; + + // Create metadata from MOCK_TRON_TOKENS + Object.values(MOCK_TRON_TOKENS) + .flat() + .forEach((token) => { + state.assetsMetadata[token.assetId] = { + fungible: true, + iconUrl: token.image, + name: token.name, + symbol: token.symbol, + units: [ + { + decimals: token.decimals, + name: token.name, + symbol: token.symbol, + }, + ], + }; + }); + + // Create balances from MOCK_TRON_TOKENS + state.balances['de5c3465-d01e-4091-a219-232903e982bb'] = {}; + Object.values(MOCK_TRON_TOKENS) + .flat() + .forEach((token) => { + state.balances['de5c3465-d01e-4091-a219-232903e982bb'][ + token.assetId + ] = { + amount: token.balance, + unit: token.symbol, + }; + }); + + // Create conversion rates from MOCK_TRON_TOKENS (only for tokens with fiat data) + Object.values(MOCK_TRON_TOKENS) + .flat() + .forEach((token) => { + if (token.fiat && token.fiat.conversionRate) { + state.conversionRates[token.assetId] = { + rate: token.fiat.conversionRate.toString(), + conversionTime: Date.now(), + }; + } + }); + + return state; + }; + + it('filters out tron staked tokens', () => { + const state = arrangeTronState(); + + const result = selectAssetsBySelectedAccountGroup(state); + + expect(result[TrxScope.Mainnet]).toHaveLength(1); + expect(result[TrxScope.Nile]).toHaveLength(1); + expect(result[TrxScope.Shasta]).toHaveLength(1); + }); + + it('does not filter out tron staked tokens', () => { + const state = arrangeTronState(); + + const result = selectAssetsBySelectedAccountGroup(state, { + filterTronStakedTokens: false, + }); + + expect(result[TrxScope.Mainnet].length > 1).toBe(true); + expect(result[TrxScope.Nile].length > 1).toBe(true); + expect(result[TrxScope.Shasta].length > 1).toBe(true); + }); }); }); diff --git a/packages/assets-controllers/src/selectors/token-selectors.ts b/packages/assets-controllers/src/selectors/token-selectors.ts index 123c26bb027..741d1174e3f 100644 --- a/packages/assets-controllers/src/selectors/token-selectors.ts +++ b/packages/assets-controllers/src/selectors/token-selectors.ts @@ -2,10 +2,11 @@ import type { AccountGroupId } from '@metamask/account-api'; import type { AccountTreeControllerState } from '@metamask/account-tree-controller'; import type { AccountsControllerState } from '@metamask/accounts-controller'; import { convertHexToDecimal } from '@metamask/controller-utils'; +import { TrxScope } from '@metamask/keyring-api'; import type { InternalAccount } from '@metamask/keyring-internal-api'; import type { NetworkState } from '@metamask/network-controller'; import { hexToBigInt, parseCaipAssetType, type Hex } from '@metamask/utils'; -import { createSelector } from 'reselect'; +import { createSelector, weakMapMemoize } from 'reselect'; import { parseBalanceWithDecimals, @@ -20,6 +21,26 @@ import type { TokenBalancesControllerState } from '../TokenBalancesController'; import type { Token, TokenRatesControllerState } from '../TokenRatesController'; import type { TokensControllerState } from '../TokensController'; +// Asset Tron Filters +export const TRON_RESOURCE = { + ENERGY: 'energy', + BANDWIDTH: 'bandwidth', + MAX_ENERGY: 'max-energy', + MAX_BANDWIDTH: 'max-bandwidth', + STRX_ENERGY: 'strx-energy', + STRX_BANDWIDTH: 'strx-bandwidth', +} as const; + +export type TronResourceSymbol = + (typeof TRON_RESOURCE)[keyof typeof TRON_RESOURCE]; + +export const TRON_RESOURCE_SYMBOLS = Object.values( + TRON_RESOURCE, +) as readonly TronResourceSymbol[]; + +export const TRON_RESOURCE_SYMBOLS_SET: ReadonlySet = + new Set(TRON_RESOURCE_SYMBOLS); + export type AssetsByAccountGroup = { [accountGroupId: AccountGroupId]: AccountGroupAssets; }; @@ -438,14 +459,66 @@ const selectAllAssets = createAssetListSelector( }, ); +export type SelectAccountGroupAssetOpts = { + filterTronStakedTokens: boolean; +}; + +const defaultSelectAccountGroupAssetOpts: SelectAccountGroupAssetOpts = { + filterTronStakedTokens: true, +}; + +const filterTronStakedTokens = (assetsByAccountGroup: AccountGroupAssets) => { + const newAssetsByAccountGroup = { ...assetsByAccountGroup }; + + Object.values(TrxScope).forEach((tronChainId) => { + if (!newAssetsByAccountGroup[tronChainId]) { + return; + } + + newAssetsByAccountGroup[tronChainId] = newAssetsByAccountGroup[ + tronChainId + ].filter((asset: Asset) => { + if ( + asset.chainId.startsWith('tron:') && + TRON_RESOURCE_SYMBOLS_SET.has( + asset.symbol?.toLowerCase() as TronResourceSymbol, + ) + ) { + return false; + } + return true; + }); + }); + + return newAssetsByAccountGroup; +}; + export const selectAssetsBySelectedAccountGroup = createAssetListSelector( - [selectAllAssets, (state) => state.accountTree], - (groupAssets, accountTree) => { + [ + selectAllAssets, + (state) => state.accountTree, + ( + _state, + opts: SelectAccountGroupAssetOpts = defaultSelectAccountGroupAssetOpts, + ) => opts, + ], + (groupAssets, accountTree, opts) => { const { selectedAccountGroup } = accountTree; if (!selectedAccountGroup) { return {}; } - return groupAssets[selectedAccountGroup] || {}; + + let result = groupAssets[selectedAccountGroup] || {}; + + if (opts.filterTronStakedTokens) { + result = filterTronStakedTokens(result); + } + + return result; + }, + { + memoize: weakMapMemoize, + argsMemoize: weakMapMemoize, }, );