Skip to content
Open
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
7 changes: 7 additions & 0 deletions packages/assets-controllers/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Added

- Export new type `TrendingTokensQueryParams` for extensible trending token query parameters ([#8729](https://github.com/MetaMask/core/pull/8729))

### Changed

- **BREAKING:** `getTrendingTokens` now accepts `sort` instead of `sortBy` to match the API parameter name ([#8729](https://github.com/MetaMask/core/pull/8729))
Comment thread
sahar-fehri marked this conversation as resolved.
- `getTrendingTokens` and `getTrendingTokensURL` now accept arbitrary query parameters via an index signature on `TrendingTokensQueryParams`, allowing new API parameters to pass through without a core release ([#8729](https://github.com/MetaMask/core/pull/8729))

- Bump `@metamask/account-tree-controller` from `^7.2.0` to `^7.3.0` ([#8722](https://github.com/MetaMask/core/pull/8722))
- Bump `@metamask/keyring-controller` from `^25.4.0` to `^25.5.0` ([#8722](https://github.com/MetaMask/core/pull/8722))
- Bump `@metamask/multichain-account-service` from `^8.0.1` to `^9.0.0` ([#8722](https://github.com/MetaMask/core/pull/8722))
Expand Down
1 change: 1 addition & 0 deletions packages/assets-controllers/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -292,6 +292,7 @@ export { createFormatters } from './utils/formatters';
export type {
SortTrendingBy,
TrendingAsset,
TrendingTokensQueryParams,
TokenSearchItem,
TokenAsset,
TokenRwaData,
Expand Down
29 changes: 23 additions & 6 deletions packages/assets-controllers/src/token-service.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1139,7 +1139,7 @@ describe('Token service', () => {
it('returns empty array if api returns non-array response', async () => {
nock(TOKEN_END_POINT_API)
.get(
`/v3/tokens/trending?chainIds=${encodeURIComponent(sampleCaipChainId)}`,
`/v3/tokens/trending?chainIds=${encodeURIComponent(sampleCaipChainId)}&includeRwaData=true&usePriceApiData=true`,
)
.reply(200, { error: 'Invalid response' })
.persist();
Expand All @@ -1151,7 +1151,7 @@ describe('Token service', () => {
it('returns empty array if the fetch fails', async () => {
nock(TOKEN_END_POINT_API)
.get(
`/v3/tokens/trending?chainIds=${encodeURIComponent(sampleCaipChainId)}`,
`/v3/tokens/trending?chainIds=${encodeURIComponent(sampleCaipChainId)}&includeRwaData=true&usePriceApiData=true`,
)
.reply(500)
.persist();
Expand All @@ -1162,22 +1162,22 @@ describe('Token service', () => {

it('returns the list of trending tokens if the fetch succeeds', async () => {
const testChainId = 'eip155:1';
const sortBy: SortTrendingBy = 'm5_trending';
const sort: SortTrendingBy = 'm5_trending';
const testMinLiquidity = 1000000;
const testMinVolume24hUsd = 1000000;
const testMaxVolume24hUsd = 1000000;
const testMinMarketCap = 1000000;
const testMaxMarketCap = 1000000;
nock(TOKEN_END_POINT_API)
.get(
`/v3/tokens/trending?chainIds=${encodeURIComponent(testChainId)}&sort=${sortBy}&minLiquidity=${testMinLiquidity}&minVolume24hUsd=${testMinVolume24hUsd}&maxVolume24hUsd=${testMaxVolume24hUsd}&minMarketCap=${testMinMarketCap}&maxMarketCap=${testMaxMarketCap}&includeRwaData=true&usePriceApiData=true`,
`/v3/tokens/trending?chainIds=${encodeURIComponent(testChainId)}&sort=${sort}&minLiquidity=${testMinLiquidity}&minVolume24hUsd=${testMinVolume24hUsd}&maxVolume24hUsd=${testMaxVolume24hUsd}&minMarketCap=${testMinMarketCap}&maxMarketCap=${testMaxMarketCap}&includeRwaData=true&usePriceApiData=true`,
)
.reply(200, sampleTrendingTokens)
.persist();

const result = await getTrendingTokens({
chainIds: [testChainId],
sortBy,
sort,
minLiquidity: testMinLiquidity,
minVolume24hUsd: testMinVolume24hUsd,
maxVolume24hUsd: testMaxVolume24hUsd,
Expand Down Expand Up @@ -1292,14 +1292,31 @@ describe('Token service', () => {

const result = await getTrendingTokens({
chainIds: [testChainId],
sortBy: 'h6_trending',
sort: 'h6_trending',
minLiquidity: testMinLiquidity,
minVolume24hUsd: testMinVolume,
includeRwaData: false,
includeTokenSecurityData: true,
});
expect(result).toStrictEqual(sampleTrendingTokensWithSecurityData);
});

it('passes unknown query params through to the URL', async () => {
const testChainId = 'eip155:1';

nock(TOKEN_END_POINT_API)
.get(
`/v3/tokens/trending?chainIds=${encodeURIComponent(testChainId)}&includeRwaData=true&usePriceApiData=true&vsCurrency=eur`,
)
.reply(200, sampleTrendingTokens)
.persist();

const result = await getTrendingTokens({
chainIds: [testChainId],
vsCurrency: 'eur',
});
expect(result).toStrictEqual(sampleTrendingTokens);
});
});

describe('searchTokens with includeTokenSecurityData', () => {
Expand Down
97 changes: 32 additions & 65 deletions packages/assets-controllers/src/token-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -138,24 +138,14 @@ function getTokenAssetsURL(options: {
}

/**
* Get the trending tokens URL for the given networks and search query.
* Shared query-parameter type for the v3 trending tokens endpoint.
*
* @param options - Options for getting trending tokens.
* @param options.chainIds - Array of CAIP format chain IDs (e.g., ['eip155:1', 'eip155:137', 'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp']).
* @param options.sort - The sort field.
* @param options.minLiquidity - The minimum liquidity.
* @param options.minVolume24hUsd - The minimum volume 24h in USD.
* @param options.maxVolume24hUsd - The maximum volume 24h in USD.
* @param options.minMarketCap - The minimum market cap.
* @param options.maxMarketCap - The maximum market cap.
* @param options.excludeLabels - Array of labels to exclude (e.g., ['stable_coin', 'blue_chip']).
* @param options.includeRwaData - Optional flag to include RWA data in the results (defaults to false).
* @param options.usePriceApiData - Optional flag to use price API data in the results (defaults to false).
* @param options.includeTokenSecurityData - Optional flag to include token security data in the results (defaults to false).
* @returns The trending tokens URL.
* Known parameters are explicitly typed for autocomplete and documentation.
* The index signature allows new API parameters to pass through without
* requiring a core release — callers can add any additional key/value and
* it will be forwarded as a query parameter.
*/
function getTrendingTokensURL(options: {
chainIds: CaipChainId[];
export type TrendingTokensQueryParams = {
sort?: SortTrendingBy;
minLiquidity?: number;
minVolume24hUsd?: number;
Expand All @@ -166,11 +156,21 @@ function getTrendingTokensURL(options: {
includeRwaData?: boolean;
usePriceApiData?: boolean;
includeTokenSecurityData?: boolean;
}): string {
[key: string]: string | number | boolean | string[] | undefined;
};

/**
* Get the trending tokens URL for the given networks and search query.
*
* @param options - Options bag: `chainIds` (required) plus any query params.
* @returns The trending tokens URL.
*/
function getTrendingTokensURL(
options: { chainIds: CaipChainId[] } & TrendingTokensQueryParams,
): string {
const encodedChainIds = options.chainIds
.map((id) => encodeURIComponent(id))
.join(',');
// Add the rest of query params if they are defined
const queryParams = new URLSearchParams();
const { chainIds, excludeLabels, ...rest } = options;
Object.entries(rest).forEach(([key, value]) => {
Expand Down Expand Up @@ -391,63 +391,30 @@ export type TrendingAsset = {
/**
* Get the trending tokens for the given chains.
*
* @param options - Options for getting trending tokens.
* @param options.chainIds - The chains to get the trending tokens for.
* @param options.sortBy - The sort by field.
* @param options.minLiquidity - The minimum liquidity.
* @param options.minVolume24hUsd - The minimum volume 24h in USD.
* @param options.maxVolume24hUsd - The maximum volume 24h in USD.
* @param options.minMarketCap - The minimum market cap.
* @param options.maxMarketCap - The maximum market cap.
* @param options.excludeLabels - Array of labels to exclude (e.g., ['stable_coin', 'blue_chip']).
* @param options.includeRwaData - Optional flag to include RWA data in the results (defaults to true).
* @param options.usePriceApiData - Optional flag to use price API data in the results (defaults to true).
* @param options.includeTokenSecurityData - Optional flag to include token security data in the results (defaults to false).
* Accepts all known query parameters plus any additional ones via the
* index signature on {@link TrendingTokensQueryParams}. New API parameters
* can be passed without updating this function.
*
* @param options - Options bag: `chainIds` (required) plus any query params
* supported by the v3 trending endpoint.
* @returns The trending tokens.
* @throws Will throw if the request fails.
*/
export async function getTrendingTokens({
chainIds,
sortBy,
minLiquidity,
minVolume24hUsd,
maxVolume24hUsd,
minMarketCap,
maxMarketCap,
excludeLabels,
includeRwaData = true,
Comment thread
cursor[bot] marked this conversation as resolved.
usePriceApiData = true,
includeTokenSecurityData,
}: {
chainIds: CaipChainId[];
sortBy?: SortTrendingBy;
minLiquidity?: number;
minVolume24hUsd?: number;
maxVolume24hUsd?: number;
minMarketCap?: number;
maxMarketCap?: number;
excludeLabels?: string[];
includeRwaData?: boolean;
usePriceApiData?: boolean;
includeTokenSecurityData?: boolean;
}): Promise<TrendingAsset[]> {
export async function getTrendingTokens(
options: { chainIds: CaipChainId[] } & TrendingTokensQueryParams,
): Promise<TrendingAsset[]> {
const { chainIds, ...rest } = options;

if (chainIds.length === 0) {
console.error('No chains provided');
return [];
}

const trendingTokensURL = getTrendingTokensURL({
chainIds,
sort: sortBy,
minLiquidity,
minVolume24hUsd,
maxVolume24hUsd,
minMarketCap,
maxMarketCap,
excludeLabels,
includeRwaData,
usePriceApiData,
includeTokenSecurityData,
...rest,
includeRwaData: rest.includeRwaData ?? true,
usePriceApiData: rest.usePriceApiData ?? true,
});

try {
Expand Down
Loading