Skip to content

Commit 2ff2004

Browse files
authored
chore: remove tokensChainsCache usage from usePerpsPaymentTokens (#28639)
<!-- Please submit this PR as a draft initially. Do not mark it as "Ready for review" until the template has been completely filled out, and PR status checks have passed at least once. --> ## **Description** Removes the dependency on `selectTokenList` (backed by `tokensChainsCache`) and `selectIsIpfsGatewayEnabled` from the `usePerpsPaymentTokens` hook, as part of the broader effort to eliminate `tokensChainsCache` usage across the codebase. The `tokenList` selector was used in two places inside the hook, both of which were effectively dead code: 1. **Hyperliquid USDC entry** — `enhanceTokenWithIcon` was called with the current EVM chain's token list to look up the USDC icon. This lookup would virtually never succeed because Hyperliquid tokens don't appear in EVM chain token lists. The symbol-based cross-chain fallback might occasionally match, but `USDC_TOKEN_ICON_URL` (already exported from `@metamask/perps-controller` and used elsewhere in the codebase for this exact purpose) is the correct, always-available icon. Replaced with the hardcoded constant. 2. **Other wallet tokens** — `enhanceTokenWithIcon` was guarded by `tokenList && !token.image`. However, `useTokensWithBalance` already populates `token.image` via `getTokenIconUrl` (constructing the MetaMask static CDN URL from the CAIP-19 asset ID) for every token it returns. The `!token.image` condition was therefore almost never true, making the entire enhancement block a no-op. Removed; tokens now pass through as-is from `useTokensWithBalance`. <!-- Write a short description of the changes included in this pull request, also include relevant motivation and context. Have in mind the following questions: 1. What is the reason for the change? 2. What is the improvement/solution? --> ## **Changelog** <!-- If this PR is not End-User-Facing and should not show up in the CHANGELOG, you can choose to either: 1. Write `CHANGELOG entry: null` 2. Label with `no-changelog` If this PR is End-User-Facing, please write a short User-Facing description in the past tense like: `CHANGELOG entry: Added a new tab for users to see their NFTs` `CHANGELOG entry: Fixed a bug that was causing some NFTs to flicker` (This helps the Release Engineer do their job more quickly and accurately) --> CHANGELOG entry: remove tokensChainsCache usage from usePerpsPaymentTokens ## **Related issues** Fixes: https://consensyssoftware.atlassian.net/browse/ASSETS-3049 ## **Manual testing steps** ```gherkin Feature: my feature name Scenario: user [verb for user action] Given [describe expected initial app state] When user [verb for user action] Then [describe expected outcome] ``` ## **Screenshots/Recordings** <!-- If applicable, add screenshots and/or recordings to visualize the before and after of your change. --> ### **Before** <!-- [screenshots/recordings] --> ### **After** <!-- [screenshots/recordings] --> ## **Pre-merge author checklist** - [ ] I've followed [MetaMask Contributor Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Mobile Coding Standards](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/CODING_GUIDELINES.md). - [ ] I've completed the PR template to the best of my ability - [ ] I've included tests if applicable - [ ] I've documented my code using [JSDoc](https://jsdoc.app/) format if applicable - [ ] I've applied the right labels on the PR (see [labeling guidelines](https://github.com/MetaMask/metamask-mobile/blob/main/.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. <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Medium Risk** > Moderate risk because it changes how token icons are sourced in the Perps payment token list (now relying on `USDC_TOKEN_ICON_URL` and `useTokensWithBalance` images) and removes token-list/IPFS fallbacks, which could affect UI rendering if assumptions are wrong. > > **Overview** > Removes `tokensChainsCache`-backed dependencies from `usePerpsPaymentTokens` by dropping `selectTokenList`/`selectIsIpfsGatewayEnabled` and the `enhanceTokenWithIcon` flow. > > Hyperliquid USDC now always uses `USDC_TOKEN_ICON_URL` for its `image`, and non-Hyperliquid tokens are passed through from `useTokensWithBalance` with only `balance`/`balanceFiat` fallbacks. Associated tests are updated, and `tokenIconUtils` (and its unit tests) are deleted. > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit f73a8f0. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup> <!-- /CURSOR_SUMMARY -->
1 parent 51e1608 commit 2ff2004

File tree

4 files changed

+27
-586
lines changed

4 files changed

+27
-586
lines changed

app/components/UI/Perps/hooks/usePerpsPaymentTokens.test.ts

Lines changed: 14 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,9 @@ jest.mock('react-redux', () => ({
88
useSelector: jest.fn(),
99
}));
1010
jest.mock('../../Bridge/hooks/useTokensWithBalance');
11-
jest.mock('../utils/tokenIconUtils');
1211
jest.mock('./index');
1312
jest.mock('./stream');
1413
jest.mock('../../../../selectors/networkController');
15-
jest.mock('../../../../selectors/tokenListController');
16-
jest.mock('../../../../selectors/preferencesController');
1714

1815
// Mock i18n
1916
jest.mock('../../../../../locales/i18n', () => ({
@@ -40,7 +37,6 @@ const mockUseSelector = jest.requireMock('react-redux').useSelector;
4037
const mockUseTokensWithBalance = jest.requireMock(
4138
'../../Bridge/hooks/useTokensWithBalance',
4239
);
43-
const mockEnhanceTokenWithIcon = jest.requireMock('../utils/tokenIconUtils');
4440
const mockUsePerpsLiveAccount =
4541
jest.requireMock('./stream').usePerpsLiveAccount;
4642
const mockUsePerpsNetwork = jest.requireMock('./index').usePerpsNetwork;
@@ -52,18 +48,6 @@ describe('usePerpsPaymentTokens', () => {
5248
'0x89': { chainId: '0x89', name: 'Polygon', ticker: 'MATIC' },
5349
};
5450

55-
const mockTokenList = {
56-
'0xaf88d065e77c8cC2239327C5EDb3A432268e5831': {
57-
address: '0xaf88d065e77c8cC2239327C5EDb3A432268e5831',
58-
symbol: 'USDC',
59-
decimals: 6,
60-
name: 'USD Coin',
61-
iconUrl: 'https://example.com/usdc-icon.png',
62-
aggregators: [],
63-
occurrences: 1,
64-
},
65-
};
66-
6751
const mockAccountState: AccountState = {
6852
availableBalance: '1000.50',
6953
marginUsed: '300.25',
@@ -109,10 +93,7 @@ describe('usePerpsPaymentTokens', () => {
10993
jest.clearAllMocks();
11094

11195
// Set up mock return values
112-
mockUseSelector
113-
.mockReturnValueOnce(mockNetworkConfigurations) // selectNetworkConfigurations
114-
.mockReturnValueOnce(mockTokenList) // selectTokenList
115-
.mockReturnValueOnce(false); // selectIsIpfsGatewayEnabled
96+
mockUseSelector.mockReturnValueOnce(mockNetworkConfigurations); // selectNetworkConfigurations
11697

11798
mockUseTokensWithBalance.useTokensWithBalance.mockReturnValue(
11899
mockTokensWithBalance,
@@ -122,12 +103,6 @@ describe('usePerpsPaymentTokens', () => {
122103
isInitialLoading: false,
123104
});
124105
mockUsePerpsNetwork.mockReturnValue('mainnet');
125-
mockEnhanceTokenWithIcon.enhanceTokenWithIcon.mockImplementation(
126-
({ token }: { token: BridgeToken }) => ({
127-
...token,
128-
image: `enhanced-${token.symbol?.toLowerCase()}.png`,
129-
}),
130-
);
131106
});
132107

133108
describe('Basic functionality', () => {
@@ -176,12 +151,8 @@ describe('usePerpsPaymentTokens', () => {
176151
});
177152

178153
it('handles missing network configurations', () => {
179-
// Clear previous mock setup and set new values
180154
mockUseSelector.mockClear();
181-
mockUseSelector
182-
.mockReturnValueOnce(null) // selectNetworkConfigurations
183-
.mockReturnValueOnce(mockTokenList) // selectTokenList
184-
.mockReturnValueOnce(false); // selectIsIpfsGatewayEnabled
155+
mockUseSelector.mockReturnValueOnce(null); // selectNetworkConfigurations
185156

186157
const { result } = renderHook(() => usePerpsPaymentTokens());
187158

@@ -288,20 +259,19 @@ describe('usePerpsPaymentTokens', () => {
288259
});
289260
});
290261

291-
describe('Token enhancement', () => {
292-
it('enhances tokens with icons', () => {
293-
renderHook(() => usePerpsPaymentTokens());
262+
describe('Token icons', () => {
263+
it('sets USDC_TOKEN_ICON_URL as image for Hyperliquid USDC', () => {
264+
const { result } = renderHook(() => usePerpsPaymentTokens());
294265

295-
expect(
296-
mockEnhanceTokenWithIcon.enhanceTokenWithIcon,
297-
).toHaveBeenCalledWith({
298-
token: expect.objectContaining({
299-
symbol: 'USDC',
300-
name: 'USDC • Hyperliquid',
301-
}),
302-
tokenList: mockTokenList,
303-
isIpfsGatewayEnabled: false,
304-
});
266+
const hyperliquidUsdc = result.current[0];
267+
expect(hyperliquidUsdc.image).toContain('tokenIcons');
268+
});
269+
270+
it('passes through image from useTokensWithBalance for other tokens', () => {
271+
const { result } = renderHook(() => usePerpsPaymentTokens());
272+
273+
const ethToken = result.current.find((t) => t.symbol === 'ETH');
274+
expect(ethToken?.image).toBe('https://example.com/eth.png');
305275
});
306276
});
307277

@@ -346,19 +316,6 @@ describe('usePerpsPaymentTokens', () => {
346316
expect(result.current[0].symbol).toBe('USDC');
347317
});
348318

349-
it('handles missing token list', () => {
350-
// Clear previous mock setup and set new values
351-
mockUseSelector.mockClear();
352-
mockUseSelector
353-
.mockReturnValueOnce(mockNetworkConfigurations) // selectNetworkConfigurations
354-
.mockReturnValueOnce(null) // selectTokenList
355-
.mockReturnValueOnce(false); // selectIsIpfsGatewayEnabled
356-
357-
const { result } = renderHook(() => usePerpsPaymentTokens());
358-
359-
expect(result.current.length).toBeGreaterThan(0);
360-
});
361-
362319
it('handles malformed balance fiat values', () => {
363320
const tokensWithMalformedFiat = [
364321
{

app/components/UI/Perps/hooks/usePerpsPaymentTokens.ts

Lines changed: 13 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,16 @@ import { isEqual } from 'lodash';
33
import { useMemo, useRef } from 'react';
44
import { useSelector } from 'react-redux';
55
import { selectNetworkConfigurations } from '../../../../selectors/networkController';
6-
import { selectIsIpfsGatewayEnabled } from '../../../../selectors/preferencesController';
7-
import { selectTokenList } from '../../../../selectors/tokenListController';
86
import { useTokensWithBalance } from '../../Bridge/hooks/useTokensWithBalance';
97
import {
108
HYPERLIQUID_MAINNET_CHAIN_ID,
119
HYPERLIQUID_TESTNET_CHAIN_ID,
1210
USDC_ARBITRUM_MAINNET_ADDRESS,
1311
USDC_DECIMALS,
1412
USDC_SYMBOL,
13+
USDC_TOKEN_ICON_URL,
1514
type PerpsToken,
1615
} from '@metamask/perps-controller';
17-
import { enhanceTokenWithIcon } from '../utils/tokenIconUtils';
1816
import { usePerpsNetwork } from './index';
1917
import { usePerpsLiveAccount } from './stream';
2018

@@ -32,8 +30,6 @@ import { usePerpsLiveAccount } from './stream';
3230
*/
3331
export function usePerpsPaymentTokens(): PerpsToken[] {
3432
const networkConfigurations = useSelector(selectNetworkConfigurations);
35-
const tokenList = useSelector(selectTokenList);
36-
const isIpfsGatewayEnabled = useSelector(selectIsIpfsGatewayEnabled);
3733

3834
// Use ref to store previous token array
3935
const previousTokensRef = useRef<PerpsToken[]>([]);
@@ -71,22 +67,17 @@ export function usePerpsPaymentTokens(): PerpsToken[] {
7167
? HYPERLIQUID_TESTNET_CHAIN_ID
7268
: HYPERLIQUID_MAINNET_CHAIN_ID;
7369

74-
const hyperliquidUsdcBase = {
70+
const hyperliquidUsdc: PerpsToken = {
7571
address: USDC_ARBITRUM_MAINNET_ADDRESS,
7672
symbol: USDC_SYMBOL,
7773
name: 'USDC • Hyperliquid',
7874
decimals: USDC_DECIMALS,
7975
chainId: hyperliquidChainId as Hex,
76+
image: USDC_TOKEN_ICON_URL,
8077
balance: (hyperliquidBalance * 1e6).toString(),
8178
balanceFiat: `$${hyperliquidBalance.toFixed(2)}`,
8279
};
8380

84-
const hyperliquidUsdc: PerpsToken = enhanceTokenWithIcon({
85-
token: hyperliquidUsdcBase,
86-
tokenList: tokenList || {},
87-
isIpfsGatewayEnabled,
88-
});
89-
9081
// Always show Hyperliquid USDC first (even if balance is 0)
9182
tokens.push(hyperliquidUsdc);
9283

@@ -99,30 +90,15 @@ export function usePerpsPaymentTokens(): PerpsToken[] {
9990
// Show all tokens with any balance (including zero balance for visibility)
10091
return true;
10192
})
102-
.map((token) => {
103-
// Enhance with icon if needed
104-
const enhanced =
105-
tokenList && !token.image
106-
? enhanceTokenWithIcon({
107-
token: {
108-
symbol: token.symbol,
109-
address: token.address,
110-
decimals: token.decimals,
111-
chainId: token.chainId as Hex,
112-
name: token.name,
113-
},
114-
tokenList,
115-
isIpfsGatewayEnabled,
116-
})
117-
: token;
118-
119-
return {
120-
...enhanced,
121-
// Ensure we have all required fields
122-
balance: token.balance || '0',
123-
balanceFiat: token.balanceFiat || '$0.00',
124-
} as PerpsToken;
125-
});
93+
.map(
94+
(token) =>
95+
({
96+
...token,
97+
// Ensure we have all required fields
98+
balance: token.balance || '0',
99+
balanceFiat: token.balanceFiat || '$0.00',
100+
}) as PerpsToken,
101+
);
126102

127103
// Sort tokens by priority:
128104
// 1. USDC tokens first (faster to bridge)
@@ -147,13 +123,7 @@ export function usePerpsPaymentTokens(): PerpsToken[] {
147123
// Future: Add other supported tokens here
148124

149125
return tokens;
150-
}, [
151-
tokensWithBalance,
152-
hyperliquidBalance,
153-
currentNetwork,
154-
tokenList,
155-
isIpfsGatewayEnabled,
156-
]);
126+
}, [tokensWithBalance, hyperliquidBalance, currentNetwork]);
157127

158128
// Check if tokens have actually changed using lodash deep equality
159129
const tokensChanged = !isEqual(previousTokensRef.current, paymentTokens);

0 commit comments

Comments
 (0)