Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 20 additions & 18 deletions app/components/UI/Bridge/utils/isTradableToken/index.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { TrxScope } from '@metamask/keyring-api';
import { isTradableToken } from './index';
import { BridgeToken } from '../../types';
import { KnownCaip19Id } from '../../../../../core/Multichain/constants';

// Helper function to create test tokens
const createTestToken = (
Expand Down Expand Up @@ -170,68 +171,69 @@ describe('isTradableToken', () => {

expect(result).toBe(true);
});
});

describe('non-tradable Tron resource tokens', () => {
it('returns false for Tron Energy token', () => {
it('returns true for native TRX', () => {
const token = createTestToken({
chainId: TrxScope.Mainnet,
symbol: 'energy',
address: `${TrxScope.Mainnet}/slip44:195`,
symbol: 'TRX',
});

const result = isTradableToken(token);

expect(result).toBe(false);
expect(result).toBe(true);
});
});

it('returns false for Tron Bandwidth token', () => {
describe('non-tradable Tron resource tokens', () => {
it('returns false for Tron Energy token', () => {
const token = createTestToken({
chainId: TrxScope.Mainnet,
symbol: 'bandwidth',
address: KnownCaip19Id.EnergyMainnet,
});

const result = isTradableToken(token);

expect(result).toBe(false);
});

it('returns false for Tron Max Bandwidth token', () => {
it('returns false for Tron Bandwidth token', () => {
const token = createTestToken({
chainId: TrxScope.Mainnet,
symbol: 'max-bandwidth',
address: KnownCaip19Id.BandwidthMainnet,
});

const result = isTradableToken(token);

expect(result).toBe(false);
});

it('returns false for Tron energy token with lowercase', () => {
it('returns false for Tron Maximum Bandwidth token', () => {
const token = createTestToken({
chainId: TrxScope.Mainnet,
symbol: 'energy',
address: KnownCaip19Id.MaximumBandwidthMainnet,
});

const result = isTradableToken(token);

expect(result).toBe(false);
});

it('returns false for Tron bandwidth token with uppercase', () => {
it('returns false for Tron Maximum Energy token', () => {
const token = createTestToken({
chainId: TrxScope.Mainnet,
symbol: 'BANDWIDTH',
address: KnownCaip19Id.MaximumEnergyMainnet,
});

const result = isTradableToken(token);

expect(result).toBe(false);
});

it('returns false for Tron max bandwidth token with mixed case', () => {
it('returns false for TRX staked for energy', () => {
const token = createTestToken({
chainId: TrxScope.Mainnet,
symbol: 'MaX-BaNdWiDtH',
address: KnownCaip19Id.TrxStakedForEnergyMainnet,
});

const result = isTradableToken(token);
Expand All @@ -242,7 +244,7 @@ describe('isTradableToken', () => {
it('returns false for Tron Ready for Withdrawal token', () => {
const token = createTestToken({
chainId: TrxScope.Mainnet,
symbol: 'TRX-READY-FOR-WITHDRAWAL',
address: KnownCaip19Id.TrxReadyForWithdrawalMainnet,
});

const result = isTradableToken(token);
Expand All @@ -253,7 +255,7 @@ describe('isTradableToken', () => {
it('returns false for Tron Staking Rewards token', () => {
const token = createTestToken({
chainId: TrxScope.Mainnet,
symbol: 'TRX-STAKING-REWARDS',
address: KnownCaip19Id.TrxStakingRewardsMainnet,
});

const result = isTradableToken(token);
Expand All @@ -264,7 +266,7 @@ describe('isTradableToken', () => {
it('returns false for Tron In Lock Period token', () => {
const token = createTestToken({
chainId: TrxScope.Mainnet,
symbol: 'TRX-IN-LOCK-PERIOD',
address: KnownCaip19Id.TrxInLockPeriodMainnet,
});

const result = isTradableToken(token);
Expand Down
4 changes: 1 addition & 3 deletions app/components/UI/Bridge/utils/isTradableToken/index.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
import { TrxScope } from '@metamask/keyring-api';
import { BridgeToken } from '../../types';
import { isTronSpecialAsset } from '../../../../../core/Multichain/utils';
import { TokenI } from '../../../Tokens/types';

export const isTradableToken = (token: BridgeToken | TokenI) =>
token.chainId !== TrxScope.Mainnet ||
!isTronSpecialAsset(token.chainId, token.symbol);
!isTronSpecialAsset(token.address);
58 changes: 40 additions & 18 deletions app/core/Multichain/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -155,27 +155,49 @@ export const PRICE_API_CURRENCIES = [
* These are virtual resources and staking state assets passed from the Tron Snap
* to the extension for informational purposes, not actual tradeable tokens.
*/
export const TRON_SPECIAL_ASSET_SYMBOLS = {
ENERGY: 'energy',
BANDWIDTH: 'bandwidth',
MAX_ENERGY: 'max-energy',
MAX_BANDWIDTH: 'max-bandwidth',
STRX_ENERGY: 'strx-energy',
STRX_BANDWIDTH: 'strx-bandwidth',
TRX_READY_FOR_WITHDRAWAL: 'trx-ready-for-withdrawal',
TRX_STAKING_REWARDS: 'trx-staking-rewards',
TRX_IN_LOCK_PERIOD: 'trx-in-lock-period',
} as const;
export enum KnownCaip19Id {
TrxStakedForBandwidthMainnet = `${TrxScope.Mainnet}/slip44:195-staked-for-bandwidth`,
TrxStakedForBandwidthNile = `${TrxScope.Nile}/slip44:195-staked-for-bandwidth`,
TrxStakedForBandwidthShasta = `${TrxScope.Shasta}/slip44:195-staked-for-bandwidth`,

TrxStakedForEnergyMainnet = `${TrxScope.Mainnet}/slip44:195-staked-for-energy`,
TrxStakedForEnergyNile = `${TrxScope.Nile}/slip44:195-staked-for-energy`,
TrxStakedForEnergyShasta = `${TrxScope.Shasta}/slip44:195-staked-for-energy`,

TrxReadyForWithdrawalMainnet = `${TrxScope.Mainnet}/slip44:195-ready-for-withdrawal`,
TrxReadyForWithdrawalNile = `${TrxScope.Nile}/slip44:195-ready-for-withdrawal`,
TrxReadyForWithdrawalShasta = `${TrxScope.Shasta}/slip44:195-ready-for-withdrawal`,

TrxStakingRewardsMainnet = `${TrxScope.Mainnet}/slip44:195-staking-rewards`,
TrxStakingRewardsNile = `${TrxScope.Nile}/slip44:195-staking-rewards`,
TrxStakingRewardsShasta = `${TrxScope.Shasta}/slip44:195-staking-rewards`,

TrxInLockPeriodMainnet = `${TrxScope.Mainnet}/slip44:195-in-lock-period`,
TrxInLockPeriodNile = `${TrxScope.Nile}/slip44:195-in-lock-period`,
TrxInLockPeriodShasta = `${TrxScope.Shasta}/slip44:195-in-lock-period`,

EnergyMainnet = `${TrxScope.Mainnet}/slip44:energy`,
EnergyNile = `${TrxScope.Nile}/slip44:energy`,
EnergyShasta = `${TrxScope.Shasta}/slip44:energy`,

MaximumEnergyMainnet = `${TrxScope.Mainnet}/slip44:maximum-energy`,
MaximumEnergyNile = `${TrxScope.Nile}/slip44:maximum-energy`,
MaximumEnergyShasta = `${TrxScope.Shasta}/slip44:maximum-energy`,

BandwidthMainnet = `${TrxScope.Mainnet}/slip44:bandwidth`,
BandwidthNile = `${TrxScope.Nile}/slip44:bandwidth`,
BandwidthShasta = `${TrxScope.Shasta}/slip44:bandwidth`,

MaximumBandwidthMainnet = `${TrxScope.Mainnet}/slip44:maximum-bandwidth`,
MaximumBandwidthNile = `${TrxScope.Nile}/slip44:maximum-bandwidth`,
MaximumBandwidthShasta = `${TrxScope.Shasta}/slip44:maximum-bandwidth`,
}

export enum TronResourceType {
ENERGY = 'ENERGY',
BANDWIDTH = 'BANDWIDTH',
}

export type TronSpecialAssetSymbol =
(typeof TRON_SPECIAL_ASSET_SYMBOLS)[keyof typeof TRON_SPECIAL_ASSET_SYMBOLS];

export const TRON_SPECIAL_ASSET_SYMBOLS_SET: ReadonlySet<TronSpecialAssetSymbol> =
new Set(
Object.values(TRON_SPECIAL_ASSET_SYMBOLS) as TronSpecialAssetSymbol[],
);
export const TRON_SPECIAL_ASSET_IDS_SET: ReadonlySet<string> = new Set(
Object.values(KnownCaip19Id),
);
29 changes: 28 additions & 1 deletion app/core/Multichain/test/utils.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,15 @@ import {
getAddressUrl,
getTransactionUrl,
isTronAddress,
isTronSpecialAsset,
} from '../utils';
import { KeyringTypes } from '@metamask/keyring-controller';
import { toChecksumHexAddress } from '@metamask/controller-utils';
import Engine from '../../Engine';
import { MULTICHAIN_NETWORK_BLOCK_EXPLORER_FORMAT_URLS_MAP } from '../constants';
import {
MULTICHAIN_NETWORK_BLOCK_EXPLORER_FORMAT_URLS_MAP,
KnownCaip19Id,
} from '../constants';
import { formatAddress } from '../../../util/address';
import {
formatBlockExplorerAddressUrl,
Expand Down Expand Up @@ -525,4 +529,27 @@ describe('MultiChain utils', () => {
});
});
});

describe('isTronSpecialAsset', () => {
it('detects staked TRX assets by CAIP-19 ID', () => {
expect(isTronSpecialAsset(KnownCaip19Id.TrxStakedForEnergyMainnet)).toBe(
true,
);
expect(isTronSpecialAsset(KnownCaip19Id.EnergyNile)).toBe(true);
expect(isTronSpecialAsset(KnownCaip19Id.MaximumEnergyMainnet)).toBe(true);
});

it('does not treat native TRX, USDT, or unrecognized IDs as special assets', () => {
expect(isTronSpecialAsset('tron:728126428/slip44:195')).toBe(false);
expect(
isTronSpecialAsset(
'tron:728126428/trc20:TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t',
),
).toBe(false);
expect(isTronSpecialAsset('tron:728126428/slip44:strx-energy')).toBe(
false,
);
expect(isTronSpecialAsset(undefined)).toBe(false);
});
});
});
21 changes: 6 additions & 15 deletions app/core/Multichain/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,7 @@ import { CaipChainId, Hex } from '@metamask/utils';
import { validate, Network } from 'bitcoin-address-validation';
import {
MULTICHAIN_NETWORK_BLOCK_EXPLORER_FORMAT_URLS_MAP,
TRON_SPECIAL_ASSET_SYMBOLS_SET,
TronSpecialAssetSymbol,
TRON_SPECIAL_ASSET_IDS_SET,
} from './constants';
import { formatAddress, isEthAddress } from '../../util/address';
import {
Expand Down Expand Up @@ -288,18 +287,10 @@ export function shortenTransactionId(txId: string) {
* Checks if a token is a Tron special asset (resources, staking state, etc.)
* that should be filtered out from user-facing asset lists.
*
* @param chainId - The chain ID to check
* @param symbol - The token symbol to check
* @returns true if the token is a Tron special asset
* Matching is by CAIP-19 asset ID only — symbols are not stable across
* AssetsController migrations.
*/
export const isTronSpecialAsset = (
chainId: string | undefined,
symbol: string | undefined,
): boolean => {
if (!chainId?.startsWith('tron:') || !symbol) {
return false;
}
return TRON_SPECIAL_ASSET_SYMBOLS_SET.has(
symbol.toLowerCase() as TronSpecialAssetSymbol,
);
};
assetId: string | undefined,
): assetId is string =>
Boolean(assetId && TRON_SPECIAL_ASSET_IDS_SET.has(assetId));
46 changes: 24 additions & 22 deletions app/selectors/assets/assets-list.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1995,10 +1995,10 @@ describe('selectTronSpecialAssetsBySelectedAccountGroup', () => {
'2d89e6a0-b4e6-45a8-a707-f10cef143b42': [
'tron:728126428/slip44:energy',
'tron:728126428/slip44:bandwidth',
'tron:728126428/slip44:max-energy',
'tron:728126428/slip44:max-bandwidth',
'tron:728126428/slip44:strx-energy',
'tron:728126428/slip44:strx-bandwidth',
'tron:728126428/slip44:maximum-energy',
'tron:728126428/slip44:maximum-bandwidth',
'tron:728126428/slip44:195-staked-for-energy',
'tron:728126428/slip44:195-staked-for-bandwidth',
'tron:728126428/slip44:195-ready-for-withdrawal',
'tron:728126428/slip44:195-staking-rewards',
'tron:728126428/slip44:195-in-lock-period',
Expand All @@ -2022,7 +2022,7 @@ describe('selectTronSpecialAssetsBySelectedAccountGroup', () => {
{ name: 'Bandwidth', symbol: 'BANDWIDTH', decimals: 0 },
],
},
'tron:728126428/slip44:max-energy': {
'tron:728126428/slip44:maximum-energy': {
name: 'Max Energy',
symbol: 'MAX-ENERGY',
fungible: true as const,
Expand All @@ -2031,7 +2031,7 @@ describe('selectTronSpecialAssetsBySelectedAccountGroup', () => {
{ name: 'Max Energy', symbol: 'MAX-ENERGY', decimals: 0 },
],
},
'tron:728126428/slip44:max-bandwidth': {
'tron:728126428/slip44:maximum-bandwidth': {
name: 'Max Bandwidth',
symbol: 'MAX-BANDWIDTH',
fungible: true as const,
Expand All @@ -2044,28 +2044,28 @@ describe('selectTronSpecialAssetsBySelectedAccountGroup', () => {
},
],
},
'tron:728126428/slip44:strx-energy': {
'tron:728126428/slip44:195-staked-for-energy': {
name: 'Staked TRX Energy',
symbol: 'STRX-ENERGY',
symbol: 'sTRX-ENERGY',
fungible: true as const,
iconUrl: 'test-url',
units: [
{
name: 'Staked TRX Energy',
symbol: 'STRX-ENERGY',
symbol: 'sTRX-ENERGY',
decimals: 6,
},
],
},
'tron:728126428/slip44:strx-bandwidth': {
'tron:728126428/slip44:195-staked-for-bandwidth': {
name: 'Staked TRX Bandwidth',
symbol: 'STRX-BANDWIDTH',
symbol: 'sTRX-BANDWIDTH',
fungible: true as const,
iconUrl: 'test-url',
units: [
{
name: 'Staked TRX Bandwidth',
symbol: 'STRX-BANDWIDTH',
symbol: 'sTRX-BANDWIDTH',
decimals: 6,
},
],
Expand Down Expand Up @@ -2130,21 +2130,21 @@ describe('selectTronSpecialAssetsBySelectedAccountGroup', () => {
amount: '560',
unit: 'BANDWIDTH',
},
'tron:728126428/slip44:max-energy': {
'tron:728126428/slip44:maximum-energy': {
amount: '200000',
unit: 'MAX-ENERGY',
},
'tron:728126428/slip44:max-bandwidth': {
'tron:728126428/slip44:maximum-bandwidth': {
amount: '1000',
unit: 'MAX-BANDWIDTH',
},
'tron:728126428/slip44:strx-energy': {
'tron:728126428/slip44:195-staked-for-energy': {
amount: '65.48463',
unit: 'STRX-ENERGY',
unit: 'sTRX-ENERGY',
},
'tron:728126428/slip44:strx-bandwidth': {
'tron:728126428/slip44:195-staked-for-bandwidth': {
amount: '65.48463',
unit: 'STRX-BANDWIDTH',
unit: 'sTRX-BANDWIDTH',
},
'tron:728126428/slip44:195-ready-for-withdrawal': {
amount: '25.5',
Expand Down Expand Up @@ -2191,15 +2191,17 @@ describe('selectTronSpecialAssetsBySelectedAccountGroup', () => {
// All 9 special assets should be mapped
expect(result.energy?.assetId).toBe('tron:728126428/slip44:energy');
expect(result.bandwidth?.assetId).toBe('tron:728126428/slip44:bandwidth');
expect(result.maxEnergy?.assetId).toBe('tron:728126428/slip44:max-energy');
expect(result.maxEnergy?.assetId).toBe(
'tron:728126428/slip44:maximum-energy',
);
expect(result.maxBandwidth?.assetId).toBe(
'tron:728126428/slip44:max-bandwidth',
'tron:728126428/slip44:maximum-bandwidth',
);
expect(result.stakedTrxForEnergy?.assetId).toBe(
'tron:728126428/slip44:strx-energy',
'tron:728126428/slip44:195-staked-for-energy',
);
expect(result.stakedTrxForBandwidth?.assetId).toBe(
'tron:728126428/slip44:strx-bandwidth',
'tron:728126428/slip44:195-staked-for-bandwidth',
);
expect(result.trxReadyForWithdrawal?.assetId).toBe(
'tron:728126428/slip44:195-ready-for-withdrawal',
Expand Down
Loading
Loading