Skip to content

Commit f053689

Browse files
authored
fix(ramp): parse slip44 intent as native asset cp-7.61.0 (MetaMask#23689)
## **Description** This PR adds slip44 wildcard matching support to the Deposit `useCryptoCurrencies` hook, aligning it with the existing behavior in the Aggregator flow. **Reason for change:** When navigating to the Deposit flow from the Asset Details page with a native asset (e.g., ETH), the intent contains a wildcard asset ID like `eip155:1/slip44:.`. However, the Deposit SDK returns native tokens with specific slip44 coin types (e.g., `eip155:1/slip44:60`). The direct string comparison was failing to match these, causing the native token not to be pre-selected. **Solution:** Added fallback logic that parses the CAIP-19 asset ID and matches any token with the same `chainId` and `slip44` namespace when the direct match fails. This mirrors the existing behavior in the Aggregator `useCryptoCurrencies` hook. ## **Changelog** CHANGELOG entry: null ## **Related issues** Fixes: MetaMask#23441 ## **Manual testing steps** ```gherkin Feature: Native token selection in Deposit flow Scenario: user navigates to Deposit from Asset Details with native token Given the user is on the Asset Details page for ETH (or any native token) And the Deposit flow is enabled for the user's region When user taps the "Buy" button Then the Deposit flow opens with ETH pre-selected as the cryptocurrency ``` ## **Screenshots/Recordings** ### **Before** https://github.com/user-attachments/assets/5a7b1ca1-3939-4ab1-a693-bf8579814e98 ### **After** https://github.com/user-attachments/assets/293d0de4-a430-43d3-b0bd-c97315baecd4 ## **Pre-merge author checklist** - [x] 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). - [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-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] > Adds slip44 wildcard CAIP-19 intent matching to pre-select native tokens in the Deposit flow and introduces focused tests for this behavior. > > - **Hook** (`app/components/UI/Ramp/Deposit/hooks/useCryptoCurrencies.ts`): > - Add slip44 wildcard handling by parsing CAIP-19 (`parseCAIP19AssetId`) when direct `assetId` match fails, selecting a native token with the same `chainId` and `slip44` namespace. > - Preserve direct `assetId` match precedence over wildcard matching. > - Minor refactor to use a mutable `intentCrypto` before selection. > - **Tests** (`app/components/UI/Ramp/Deposit/hooks/useCryptoCurrencies.test.ts`): > - Add cases for slip44 wildcard selecting native token (same/different `chainId`). > - Add fallback to first token when no native match found. > - Confirm direct match is preferred over wildcard. > > <sup>Written by [Cursor Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit 7d6606a. This will update automatically on new commits. Configure [here](https://cursor.com/dashboard?tab=bugbot).</sup> <!-- /CURSOR_SUMMARY -->
1 parent fa3b09c commit f053689

2 files changed

Lines changed: 115 additions & 1 deletion

File tree

app/components/UI/Ramp/Deposit/hooks/useCryptoCurrencies.test.ts

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -429,5 +429,102 @@ describe('useCryptoCurrencies', () => {
429429
cryptosWithMissing[0],
430430
);
431431
});
432+
433+
it('selects native token when intent has slip44 wildcard assetId', () => {
434+
const intent = { assetId: 'eip155:1/slip44:.' };
435+
mockUseDepositSDK.mockReturnValue(
436+
createMockSDKReturn({
437+
selectedRegion: MOCK_US_REGION,
438+
selectedCryptoCurrency: null,
439+
setSelectedCryptoCurrency: mockSetSelectedCryptoCurrency,
440+
intent,
441+
setIntent: mockSetIntent,
442+
}),
443+
);
444+
445+
renderHook(() => useCryptoCurrencies());
446+
447+
expect(mockSetSelectedCryptoCurrency).toHaveBeenCalledWith(
448+
MOCK_ETH_TOKEN,
449+
);
450+
});
451+
452+
it('selects native token when intent has slip44 wildcard with different chainId', () => {
453+
const mockPolygonNativeToken = {
454+
assetId: 'eip155:137/slip44:966',
455+
chainId: 'eip155:137',
456+
name: 'Polygon',
457+
symbol: 'POL',
458+
decimals: 18,
459+
iconUrl: 'https://example.com/pol.png',
460+
};
461+
const cryptosWithPolygon = [
462+
...MOCK_CRYPTOCURRENCIES,
463+
mockPolygonNativeToken,
464+
];
465+
mockUseDepositSdkMethod.mockReturnValue([
466+
{ data: cryptosWithPolygon, error: null, isFetching: false },
467+
mockRetryFetchCryptoCurrencies,
468+
]);
469+
mockUseSelector.mockReturnValue({
470+
...mockNetworkConfigurations,
471+
'eip155:137': { name: 'Polygon', chainId: '0x89' },
472+
});
473+
474+
const intent = { assetId: 'eip155:137/slip44:.' };
475+
mockUseDepositSDK.mockReturnValue(
476+
createMockSDKReturn({
477+
selectedRegion: MOCK_US_REGION,
478+
selectedCryptoCurrency: null,
479+
setSelectedCryptoCurrency: mockSetSelectedCryptoCurrency,
480+
intent,
481+
setIntent: mockSetIntent,
482+
}),
483+
);
484+
485+
renderHook(() => useCryptoCurrencies());
486+
487+
expect(mockSetSelectedCryptoCurrency).toHaveBeenCalledWith(
488+
mockPolygonNativeToken,
489+
);
490+
});
491+
492+
it('falls back to first token when slip44 wildcard does not match any native token', () => {
493+
const intent = { assetId: 'eip155:999/slip44:.' };
494+
mockUseDepositSDK.mockReturnValue(
495+
createMockSDKReturn({
496+
selectedRegion: MOCK_US_REGION,
497+
selectedCryptoCurrency: null,
498+
setSelectedCryptoCurrency: mockSetSelectedCryptoCurrency,
499+
intent,
500+
setIntent: mockSetIntent,
501+
}),
502+
);
503+
504+
renderHook(() => useCryptoCurrencies());
505+
506+
expect(mockSetSelectedCryptoCurrency).toHaveBeenCalledWith(
507+
MOCK_CRYPTOCURRENCIES[0],
508+
);
509+
});
510+
511+
it('prefers direct match over slip44 wildcard matching', () => {
512+
const intent = { assetId: MOCK_USDC_TOKEN.assetId };
513+
mockUseDepositSDK.mockReturnValue(
514+
createMockSDKReturn({
515+
selectedRegion: MOCK_US_REGION,
516+
selectedCryptoCurrency: null,
517+
setSelectedCryptoCurrency: mockSetSelectedCryptoCurrency,
518+
intent,
519+
setIntent: mockSetIntent,
520+
}),
521+
);
522+
523+
renderHook(() => useCryptoCurrencies());
524+
525+
expect(mockSetSelectedCryptoCurrency).toHaveBeenCalledWith(
526+
MOCK_USDC_TOKEN,
527+
);
528+
});
432529
});
433530
});

app/components/UI/Ramp/Deposit/hooks/useCryptoCurrencies.ts

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import { isCaipChainId } from '@metamask/utils';
1111
import { toEvmCaipChainId } from '@metamask/multichain-network-controller';
1212
import { toHex } from '@metamask/controller-utils';
1313
import { toLowerCaseEquals } from '../../../../../util/general';
14+
import { parseCAIP19AssetId } from '../../Aggregator/utils/parseCaip19AssetId';
1415

1516
export interface UseCryptoCurrenciesResult {
1617
cryptoCurrencies: DepositCryptoCurrency[] | null;
@@ -56,10 +57,26 @@ export function useCryptoCurrencies(): UseCryptoCurrenciesResult {
5657
useEffect(() => {
5758
if (cryptoCurrencies && cryptoCurrencies.length > 0) {
5859
if (intent?.assetId) {
59-
const intentCrypto = cryptoCurrencies.find((token) =>
60+
let intentCrypto = cryptoCurrencies.find((token) =>
6061
toLowerCaseEquals(token.assetId, intent.assetId),
6162
);
6263

64+
// Handle slip44 wildcard matching any native asset
65+
if (!intentCrypto) {
66+
const intentParsedCaip19 = parseCAIP19AssetId(intent.assetId);
67+
if (intentParsedCaip19?.assetNamespace === 'slip44') {
68+
intentCrypto = cryptoCurrencies.find((token) => {
69+
const tokenParsed = parseCAIP19AssetId(token.assetId);
70+
return (
71+
tokenParsed &&
72+
tokenParsed.namespace === intentParsedCaip19.namespace &&
73+
tokenParsed.chainId === intentParsedCaip19.chainId &&
74+
tokenParsed.assetNamespace === 'slip44'
75+
);
76+
});
77+
}
78+
}
79+
6380
setIntent((prevIntent) =>
6481
prevIntent ? { ...prevIntent, assetId: undefined } : undefined,
6582
);

0 commit comments

Comments
 (0)