Skip to content

Commit 9dc36c2

Browse files
authored
feat: support returning market data from token search API (#7226)
## Explanation Right now the results from trending returns the following data: ``` { "assetId":"eip155:1/erc20:0xdac17f958d2ee523a2206206994597c13d831ec7", "decimals":6, "name":"Tether USD", "symbol":"USDT" } ``` This impacts the UI in the following way: <img height="700" alt="image" src="https://github.com/user-attachments/assets/19dd0865-55cc-4a45-9fe7-89476060dc47" /> As you can see the items returned from the search API do not have: - volume - market-cap - price - price change (24h) I have modified the search API [here](consensys-vertical-apps/va-mmcx-token-api#194) to return marketData optionally. This PR makes it possible for Mobile and Extension to fetch and use this data <!-- CURSOR_SUMMARY --> --- > [!NOTE] > Adds an `includeMarketData` option to `searchTokens` (default false) and propagates it to the token search API; updates tests and changelog. > > - **Token Service (`src/token-service.ts`)**: > - Extend `getTokenSearchURL` and `searchTokens` to support `includeMarketData` (defaults to `false`) and append `includeMarketData` to the `/tokens/search` query. > - **Tests (`src/token-service.test.ts`)**: > - Update expectations to include `includeMarketData=false` in requests. > - Add test covering `includeMarketData=true`. > - **Docs**: > - Update `CHANGELOG.md` to note optional market data support for `searchTokens`. > > <sup>Written by [Cursor Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit 322c5dd. This will update automatically on new commits. Configure [here](https://cursor.com/dashboard?tab=bugbot).</sup> <!-- /CURSOR_SUMMARY --> <!-- Thanks for your contribution! Take a moment to answer these questions so that reviewers have the information they need to properly understand your changes: * What is the current state of things and why does it need to change? * What is the solution your changes offer and how does it work? * Are there any changes whose purpose might not obvious to those unfamiliar with the domain? * If your primary goal was to update one package but you found you had to update another one along the way, why did you do so? * If you had to upgrade a dependency, why did you do so? --> ## References Fixes: https://consensyssoftware.atlassian.net/browse/ASSETS-1834 <!-- Are there any issues that this pull request is tied to? Are there other links that reviewers should consult to understand these changes better? Are there client or consumer pull requests to adopt any breaking changes? For example: * Fixes #12345 * Related to #67890 --> ## Checklist - [ ] I've updated the test suite for new or updated code as appropriate - [ ] I've updated documentation (JSDoc, Markdown, etc.) for new or updated code as appropriate - [ ] I've communicated my changes to consumers by [updating changelogs for packages I've changed](https://github.com/MetaMask/core/tree/main/docs/contributing.md#updating-changelogs) - [ ] I've introduced [breaking changes](https://github.com/MetaMask/core/tree/main/docs/breaking-changes.md) in this PR and have prepared draft pull requests for clients and consumer packages to resolve them
1 parent 61d1bb0 commit 9dc36c2

File tree

3 files changed

+55
-15
lines changed

3 files changed

+55
-15
lines changed

packages/assets-controllers/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
99

1010
### Added
1111

12+
- Support for optionally fetching market data when calling searchTokens ([#7226](https://github.com/MetaMask/core/pull/7226))
1213
- **BREAKING:** Add optional JWT token authentication to multi-chain accounts API calls ([#7165](https://github.com/MetaMask/core/pull/7165))
1314
- `fetchMultiChainBalances` and `fetchMultiChainBalancesV4` now accept an optional `jwtToken` parameter
1415
- `TokenDetectionController` fetches and passes JWT token from `AuthenticationController` when using Accounts API

packages/assets-controllers/src/token-service.test.ts

Lines changed: 38 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -499,7 +499,7 @@ describe('Token service', () => {
499499

500500
nock(TOKEN_END_POINT_API)
501501
.get(
502-
`/tokens/search?chainIds=${encodeURIComponent(sampleCaipChainId)}&query=${searchQuery}&limit=10`,
502+
`/tokens/search?chainIds=${encodeURIComponent(sampleCaipChainId)}&query=${searchQuery}&limit=10&includeMarketData=false`,
503503
)
504504
.reply(200, mockResponse)
505505
.persist();
@@ -523,7 +523,7 @@ describe('Token service', () => {
523523

524524
nock(TOKEN_END_POINT_API)
525525
.get(
526-
`/tokens/search?chainIds=${encodeURIComponent(sampleCaipChainId)}&query=${searchQuery}&limit=${customLimit}`,
526+
`/tokens/search?chainIds=${encodeURIComponent(sampleCaipChainId)}&query=${searchQuery}&limit=${customLimit}&includeMarketData=false`,
527527
)
528528
.reply(200, mockResponse)
529529
.persist();
@@ -549,7 +549,7 @@ describe('Token service', () => {
549549

550550
nock(TOKEN_END_POINT_API)
551551
.get(
552-
`/tokens/search?chainIds=${encodeURIComponent(sampleCaipChainId)}&query=${encodedQuery}&limit=10`,
552+
`/tokens/search?chainIds=${encodeURIComponent(sampleCaipChainId)}&query=${encodedQuery}&limit=10&includeMarketData=false`,
553553
)
554554
.reply(200, mockResponse)
555555
.persist();
@@ -575,7 +575,7 @@ describe('Token service', () => {
575575

576576
nock(TOKEN_END_POINT_API)
577577
.get(
578-
`/tokens/search?chainIds=${encodedChainIds}&query=${searchQuery}&limit=10`,
578+
`/tokens/search?chainIds=${encodedChainIds}&query=${searchQuery}&limit=10&includeMarketData=false`,
579579
)
580580
.reply(200, mockResponse)
581581
.persist();
@@ -595,7 +595,7 @@ describe('Token service', () => {
595595
const searchQuery = 'USD';
596596
nock(TOKEN_END_POINT_API)
597597
.get(
598-
`/tokens/search?chainIds=${encodeURIComponent(sampleCaipChainId)}&query=${searchQuery}&limit=10`,
598+
`/tokens/search?chainIds=${encodeURIComponent(sampleCaipChainId)}&query=${searchQuery}&limit=10&includeMarketData=false`,
599599
)
600600
.replyWithError('Example network error')
601601
.persist();
@@ -609,7 +609,7 @@ describe('Token service', () => {
609609
const searchQuery = 'USD';
610610
nock(TOKEN_END_POINT_API)
611611
.get(
612-
`/tokens/search?chainIds=${encodeURIComponent(sampleCaipChainId)}&query=${searchQuery}&limit=10`,
612+
`/tokens/search?chainIds=${encodeURIComponent(sampleCaipChainId)}&query=${searchQuery}&limit=10&includeMarketData=false`,
613613
)
614614
.reply(400, { error: 'Bad Request' })
615615
.persist();
@@ -623,7 +623,7 @@ describe('Token service', () => {
623623
const searchQuery = 'USD';
624624
nock(TOKEN_END_POINT_API)
625625
.get(
626-
`/tokens/search?chainIds=${encodeURIComponent(sampleCaipChainId)}&query=${searchQuery}&limit=10`,
626+
`/tokens/search?chainIds=${encodeURIComponent(sampleCaipChainId)}&query=${searchQuery}&limit=10&includeMarketData=false`,
627627
)
628628
.reply(500)
629629
.persist();
@@ -643,7 +643,7 @@ describe('Token service', () => {
643643

644644
nock(TOKEN_END_POINT_API)
645645
.get(
646-
`/tokens/search?chainIds=${encodeURIComponent(sampleCaipChainId)}&query=${searchQuery}&limit=10`,
646+
`/tokens/search?chainIds=${encodeURIComponent(sampleCaipChainId)}&query=${searchQuery}&limit=10&includeMarketData=false`,
647647
)
648648
.reply(200, mockResponse)
649649
.persist();
@@ -662,7 +662,9 @@ describe('Token service', () => {
662662
};
663663

664664
nock(TOKEN_END_POINT_API)
665-
.get(`/tokens/search?chainIds=&query=${searchQuery}&limit=10`)
665+
.get(
666+
`/tokens/search?chainIds=&query=${searchQuery}&limit=10&includeMarketData=false`,
667+
)
666668
.reply(200, mockResponse)
667669
.persist();
668670

@@ -676,7 +678,7 @@ describe('Token service', () => {
676678
const errorResponse = { error: 'Invalid search query' };
677679
nock(TOKEN_END_POINT_API)
678680
.get(
679-
`/tokens/search?chainIds=${encodeURIComponent(sampleCaipChainId)}&query=${searchQuery}&limit=10`,
681+
`/tokens/search?chainIds=${encodeURIComponent(sampleCaipChainId)}&query=${searchQuery}&limit=10&includeMarketData=false`,
680682
)
681683
.reply(200, errorResponse)
682684
.persist();
@@ -709,7 +711,7 @@ describe('Token service', () => {
709711

710712
nock(TOKEN_END_POINT_API)
711713
.get(
712-
`/tokens/search?chainIds=${encodedChainIds}&query=${searchQuery}&limit=10`,
714+
`/tokens/search?chainIds=${encodedChainIds}&query=${searchQuery}&limit=10&includeMarketData=false`,
713715
)
714716
.reply(200, mockResponse)
715717
.persist();
@@ -721,6 +723,31 @@ describe('Token service', () => {
721723
data: sampleSearchResults,
722724
});
723725
});
726+
727+
it('should include market data when includeMarketData is true', async () => {
728+
const searchQuery = 'USD';
729+
const mockResponse = {
730+
count: sampleSearchResults.length,
731+
data: sampleSearchResults,
732+
pageInfo: { hasNextPage: false, endCursor: null },
733+
};
734+
735+
nock(TOKEN_END_POINT_API)
736+
.get(
737+
`/tokens/search?chainIds=${encodeURIComponent(sampleCaipChainId)}&query=${searchQuery}&limit=10&includeMarketData=true`,
738+
)
739+
.reply(200, mockResponse)
740+
.persist();
741+
742+
const results = await searchTokens([sampleCaipChainId], searchQuery, {
743+
includeMarketData: true,
744+
});
745+
746+
expect(results).toStrictEqual({
747+
count: sampleSearchResults.length,
748+
data: sampleSearchResults,
749+
});
750+
});
724751
});
725752

726753
describe('getTrendingTokens', () => {

packages/assets-controllers/src/token-service.ts

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -54,14 +54,20 @@ export type SortTrendingBy =
5454
* @param chainIds - Array of CAIP format chain IDs (e.g., 'eip155:1', 'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp').
5555
* @param query - The search query (token name, symbol, or address).
5656
* @param limit - Optional limit for the number of results (defaults to 10).
57+
* @param includeMarketData - Optional flag to include market data in the results (defaults to false).
5758
* @returns The token search URL.
5859
*/
59-
function getTokenSearchURL(chainIds: CaipChainId[], query: string, limit = 10) {
60+
function getTokenSearchURL(
61+
chainIds: CaipChainId[],
62+
query: string,
63+
limit = 10,
64+
includeMarketData = false,
65+
) {
6066
const encodedQuery = encodeURIComponent(query);
6167
const encodedChainIds = chainIds
6268
.map((id) => encodeURIComponent(id))
6369
.join(',');
64-
return `${TOKEN_END_POINT_API}/tokens/search?chainIds=${encodedChainIds}&query=${encodedQuery}&limit=${limit}`;
70+
return `${TOKEN_END_POINT_API}/tokens/search?chainIds=${encodedChainIds}&query=${encodedQuery}&limit=${limit}&includeMarketData=${includeMarketData}`;
6571
}
6672

6773
/**
@@ -144,14 +150,20 @@ export async function fetchTokenListByChainId(
144150
* @param query - The search query (token name, symbol, or address).
145151
* @param options - Additional fetch options.
146152
* @param options.limit - The maximum number of results to return.
153+
* @param options.includeMarketData - Optional flag to include market data in the results (defaults to false).
147154
* @returns Object containing count and data array. Returns { count: 0, data: [] } if request fails.
148155
*/
149156
export async function searchTokens(
150157
chainIds: CaipChainId[],
151158
query: string,
152-
{ limit = 10 } = {},
159+
{ limit = 10, includeMarketData = false } = {},
153160
): Promise<{ count: number; data: unknown[] }> {
154-
const tokenSearchURL = getTokenSearchURL(chainIds, query, limit);
161+
const tokenSearchURL = getTokenSearchURL(
162+
chainIds,
163+
query,
164+
limit,
165+
includeMarketData,
166+
);
155167

156168
try {
157169
const result = await handleFetch(tokenSearchURL);

0 commit comments

Comments
 (0)