Skip to content

Commit 8d3f15a

Browse files
committed
feat: add dynamic assets categories using coingecko api & fix mobile view
1 parent 09067bb commit 8d3f15a

File tree

6 files changed

+172
-142
lines changed

6 files changed

+172
-142
lines changed

src/components/MarketAssetCategoryFilter.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,9 @@ export const MarketAssetCategoryFilter = ({
1515
<Box sx={{ mt: 2, mb: 2 }}>
1616
<ButtonGroup
1717
sx={{
18+
width: '100%',
1819
'& .MuiButton-root': {
20+
flex: 1,
1921
textTransform: 'none',
2022
whiteSpace: 'nowrap',
2123
minWidth: 'auto',

src/modules/markets/MarketAssetsListContainer.tsx

Lines changed: 30 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { API_ETH_MOCK_ADDRESS } from '@aave/contract-helpers';
22
import { Trans } from '@lingui/macro';
33
import { Box, Switch, Typography, useMediaQuery, useTheme } from '@mui/material';
4-
import { useEffect, useState } from 'react';
4+
import { useState } from 'react';
55
import { ListWrapper } from 'src/components/lists/ListWrapper';
66
import { MarketAssetCategoryFilter } from 'src/components/MarketAssetCategoryFilter';
77
import { NoSearchResults } from 'src/components/NoSearchResults';
@@ -18,14 +18,9 @@ import { useShallow } from 'zustand/shallow';
1818

1919
import { GENERAL } from '../../utils/events';
2020
import { SavingsGhoBanner } from './Gho/GhoBanner';
21-
import {
22-
AssetCategory,
23-
ETH_CORRELATED_SYMBOLS_FALLBACK,
24-
isAssetInCategoryDynamic,
25-
STABLECOINS_SYMBOLS_FALLBACK,
26-
} from './utils/assetCategories';
27-
import { fetchCoinGeckoEthCorrelated } from './utils/useCoinGeckoEthCorrelatedCat';
28-
import { fetchCoinGeckoStablecoins } from './utils/useCoinGeckoStablecoinCat';
21+
import { AssetCategory, isAssetInCategoryDynamic } from './utils/assetCategories';
22+
import { useCoinGeckoEthCorrelatedCat } from './utils/useCoinGeckoEthCorrelatedCat';
23+
import { useCoinGeckoStablecoinCat } from './utils/useCoinGeckoStablecoinCat';
2924

3025
function shouldDisplayGhoBanner(marketTitle: string, searchTerm: string): boolean {
3126
// GHO banner is only displayed on markets where new GHO is mintable (i.e. Ethereum)
@@ -56,37 +51,16 @@ export const MarketAssetsListContainer = () => {
5651
);
5752
const [searchTerm, setSearchTerm] = useState('');
5853
const [selectedCategory, setSelectedCategory] = useState<AssetCategory>(AssetCategory.ALL);
59-
const [stablecoinSymbols, setStablecoinSymbols] = useState<string[]>(
60-
STABLECOINS_SYMBOLS_FALLBACK
61-
);
62-
const [ethCorrelatedSymbols, setEthCorrelatedSymbols] = useState<string[]>(
63-
ETH_CORRELATED_SYMBOLS_FALLBACK
64-
);
65-
const { breakpoints } = useTheme();
6654

67-
useEffect(() => {
68-
const loadStablecoins = async () => {
69-
try {
70-
const symbols = await fetchCoinGeckoStablecoins();
71-
setStablecoinSymbols(symbols);
72-
const ethSymbols = await fetchCoinGeckoEthCorrelated();
73-
setEthCorrelatedSymbols(ethSymbols);
74-
} catch (error) {
75-
console.error('Error loading stablecoins from CoinGecko, using fallback:', error);
76-
// Mantiene los símbolos hardcodeados como fallback
77-
}
78-
};
55+
const { breakpoints } = useTheme();
7956

80-
loadStablecoins();
81-
}, []);
57+
const { stablecoinSymbols } = useCoinGeckoStablecoinCat();
58+
const { ethCorrelatedSymbols } = useCoinGeckoEthCorrelatedCat();
8259

8360
const sm = useMediaQuery(breakpoints.down('sm'));
8461

8562
const displayGhoBanner = shouldDisplayGhoBanner(currentMarket, searchTerm);
86-
console.log(
87-
'ETH reserves:',
88-
reserves.map((r) => r.symbol)
89-
);
63+
9064
const filteredData = reserves
9165
// Filter out any non-active reserves
9266
.filter((res) => res.isActive)
@@ -138,30 +112,37 @@ export const MarketAssetsListContainer = () => {
138112
<Box
139113
sx={{
140114
width: '100%',
115+
display: 'flex',
116+
flexDirection: 'column',
117+
gap: 2,
118+
px: { xs: '1.6px', xsm: '1.6px' },
141119
}}
142120
>
143-
<TitleWithSearchBar
144-
onSearchTermChange={setSearchTerm}
145-
title={
146-
<>
147-
{currentMarketData.marketTitle} <Trans>assets</Trans>
148-
</>
149-
}
150-
searchPlaceholder={sm ? 'Search asset' : 'Search asset name, symbol, or address'}
151-
/>
121+
<Box sx={{ width: '100%' }}>
122+
<TitleWithSearchBar
123+
onSearchTermChange={setSearchTerm}
124+
title={
125+
<>
126+
{currentMarketData.marketTitle} <Trans>assets</Trans>
127+
</>
128+
}
129+
searchPlaceholder={sm ? 'Search asset' : 'Search asset name, symbol, or address'}
130+
/>
131+
</Box>
152132

153133
<Box
154134
sx={{
135+
width: '100%',
155136
display: 'flex',
156137
justifyContent: 'center',
157-
width: '100%',
158-
pt: 1,
159138
}}
160139
>
161-
<MarketAssetCategoryFilter
162-
selectedCategory={selectedCategory}
163-
onCategoryChange={setSelectedCategory}
164-
/>
140+
<Box sx={{ width: '100%' }}>
141+
<MarketAssetCategoryFilter
142+
selectedCategory={selectedCategory}
143+
onCategoryChange={setSelectedCategory}
144+
/>
145+
</Box>
165146
</Box>
166147
</Box>
167148
) : (

src/modules/markets/utils/assetCategories.ts

Lines changed: 13 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -91,15 +91,21 @@ export const ETH_CORRELATED_SYMBOLS_FALLBACK = [
9191
//proto_soneium_v3
9292
//proto_etherfi_v3
9393
];
94-
function normalizeSymbol(symbol: string): string {
94+
function normalizeStableSymbol(symbol: string): string {
9595
return symbol
9696
.toUpperCase()
97-
.replace(/\.E$/, '') // DAI.E → DAI, USDC.E → USDC
98-
.replace(/^M\./, '') // M.USDC → USDC
99-
.replace(/^W/, '') // WETH, WBTC → ETH, BTC
97+
.replace(/\.E$/, '')
98+
.replace(/^M\./, '')
99+
.replace(/^W/, '')
100100
.replace(/USD0/g, 'USDT')
101101
.replace(/USD/g, 'USDT');
102102
}
103+
function normalizeEthCorrelatedSymbol(symbol: string): string {
104+
return symbol
105+
.toUpperCase()
106+
.replace(/\.E$/, '')
107+
.replace(/WRSETH/g, 'RSETH');
108+
}
103109

104110
export const categorizeAssetDynamic = (
105111
symbol: string,
@@ -109,19 +115,18 @@ export const categorizeAssetDynamic = (
109115
const categories: AssetCategory[] = [AssetCategory.ALL];
110116

111117
const upperSymbol = symbol.toUpperCase();
112-
const normalizedStablecoinSymbol = normalizeSymbol(upperSymbol);
118+
const normalizedStablecoinSymbol = normalizeStableSymbol(upperSymbol);
113119
if (stablecoinCoinGeckoSymbols.includes(normalizedStablecoinSymbol)) {
114120
categories.push(AssetCategory.STABLECOINS);
115121
}
116-
117-
if (ethCorrelatedCoinGeckoSymbols.includes(upperSymbol)) {
122+
const normalizedEthCorrelatedSymbol = normalizeEthCorrelatedSymbol(upperSymbol);
123+
if (ethCorrelatedCoinGeckoSymbols.includes(normalizedEthCorrelatedSymbol)) {
118124
categories.push(AssetCategory.ETH_CORRELATED);
119125
}
120126

121127
return categories;
122128
};
123129

124-
// Nueva función para verificar categoría dinámicamente
125130
export const isAssetInCategoryDynamic = (
126131
symbol: string,
127132
category: AssetCategory,
@@ -137,25 +142,3 @@ export const isAssetInCategoryDynamic = (
137142
ethCorrelatedCoinGeckoSymbols
138143
).includes(category);
139144
};
140-
141-
// export const categorizeAsset = (symbol: string): AssetCategory[] => {
142-
// const categories: AssetCategory[] = [AssetCategory.ALL];
143-
144-
// const normalizedSymbol = symbol.toUpperCase();
145-
// if (STABLECOINS_SYMBOLS_FALLBACK.includes(normalizedSymbol)) {
146-
// categories.push(AssetCategory.STABLECOINS);
147-
// }
148-
149-
// if (ETH_CORRELATED_SYMBOLS.includes(normalizedSymbol)) {
150-
// categories.push(AssetCategory.ETH_CORRELATED);
151-
// }
152-
153-
// return categories;
154-
// };
155-
156-
// export const isAssetInCategory = (symbol: string, category: AssetCategory): boolean => {
157-
// if (category === AssetCategory.ALL) {
158-
// return true;
159-
// }
160-
// return categorizeAsset(symbol).includes(category);
161-
// };

src/modules/markets/utils/test.ts

Whitespace-only changes.
Lines changed: 76 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
import { useEffect, useState } from 'react';
2+
3+
import { ETH_CORRELATED_SYMBOLS_FALLBACK } from './assetCategories';
4+
15
const CG_ENDPOINT = 'https://pro-api.coingecko.com/api/v3/coins/markets';
26
const HEADERS: HeadersInit = {
37
accept: 'application/json',
@@ -8,42 +12,76 @@ interface CoinGeckoCoin {
812
symbol: string;
913
name: string;
1014
}
11-
export async function fetchCoinGeckoEthCorrelated(): Promise<string[]> {
12-
// Fetch ambas categorías en paralelo
13-
const [response1, response2] = await Promise.all([
14-
fetch(`${CG_ENDPOINT}?vs_currency=usd&category=liquid-staked-eth&per_page=250&page=1`, {
15-
method: 'GET',
16-
headers: HEADERS,
17-
}),
18-
fetch(`${CG_ENDPOINT}?vs_currency=usd&category=ether-fi-ecosystem&per_page=250&page=1`, {
19-
method: 'GET',
20-
headers: HEADERS,
21-
}),
22-
]);
23-
24-
// Verificar que ambas respuestas sean exitosas
25-
if (!response1.ok) {
26-
throw new Error(`Error fetching liquid-staked-eth: ${response1.statusText}`);
27-
}
28-
if (!response2.ok) {
29-
throw new Error(`Error fetching ether-fi-ecosystem: ${response2.statusText}`);
30-
}
31-
32-
// Obtener datos de ambas respuestas
33-
const [data1, data2] = await Promise.all([response1.json(), response2.json()]);
34-
35-
// Combinar ambos arrays de datos
36-
const combinedData = [...data1, ...data2];
37-
38-
// Procesar símbolos y eliminar duplicados
39-
const symbols = combinedData
40-
.map((coin: CoinGeckoCoin) => coin.symbol?.toUpperCase())
41-
.filter((symbol: string) => symbol);
42-
43-
// Eliminar duplicados con Set
44-
const uniqueSymbols = [...new Set([...symbols, 'WETH'])];
45-
46-
// console.log('ETH Correlated Symbols:', uniqueSymbols);
47-
48-
return uniqueSymbols;
15+
export function useCoinGeckoEthCorrelatedCat() {
16+
const [ethCorrelatedSymbols, setEthCorrelatedSymbols] = useState<string[]>(
17+
ETH_CORRELATED_SYMBOLS_FALLBACK
18+
);
19+
const [loading, setLoading] = useState(false);
20+
const [error, setError] = useState<Error | null>(null);
21+
22+
useEffect(() => {
23+
const loadEthCorrelatedCoins = async () => {
24+
setLoading(true);
25+
setError(null);
26+
27+
try {
28+
const [response1, response2, response3] = await Promise.all([
29+
fetch(`${CG_ENDPOINT}?vs_currency=usd&category=liquid-staked-eth&per_page=250&page=1`, {
30+
method: 'GET',
31+
headers: HEADERS,
32+
}),
33+
fetch(`${CG_ENDPOINT}?vs_currency=usd&category=ether-fi-ecosystem&per_page=250&page=1`, {
34+
method: 'GET',
35+
headers: HEADERS,
36+
}),
37+
fetch(
38+
`${CG_ENDPOINT}?vs_currency=usd&category=liquid-staking-tokens&per_page=250&page=1`,
39+
{
40+
method: 'GET',
41+
headers: HEADERS,
42+
}
43+
),
44+
]);
45+
46+
if (!response1.ok) {
47+
throw new Error(`Error fetching liquid-staked-eth: ${response1.statusText}`);
48+
}
49+
if (!response2.ok) {
50+
throw new Error(`Error fetching ether-fi-ecosystem: ${response2.statusText}`);
51+
}
52+
if (!response3.ok) {
53+
throw new Error(`Error fetching liquid-staking-tokens: ${response3.statusText}`);
54+
}
55+
56+
const [data1, data2, data3] = await Promise.all([
57+
response1.json(),
58+
response2.json(),
59+
response3.json(),
60+
]);
61+
// Filter category 'liquid-staking-tokens' to only include coins correlated to ETH
62+
const filteredData3 = data3.filter((coin: CoinGeckoCoin) => {
63+
const symbol = coin.symbol?.toUpperCase();
64+
return symbol?.includes('ETH');
65+
});
66+
67+
const combinedData = [...data1, ...data2, ...filteredData3];
68+
69+
const symbols = combinedData
70+
.map((coin: CoinGeckoCoin) => coin.symbol?.toUpperCase())
71+
.filter((symbol: string) => symbol);
72+
73+
const uniqueSymbols = [...new Set([...symbols, 'WETH'])];
74+
setEthCorrelatedSymbols(uniqueSymbols);
75+
} catch (err) {
76+
setError(err as Error);
77+
console.error('Error loading Eth Correlated from CoinGecko, using fallback:', err);
78+
} finally {
79+
setLoading(false);
80+
}
81+
};
82+
83+
loadEthCorrelatedCoins();
84+
}, []);
85+
86+
return { ethCorrelatedSymbols, loading, error };
4987
}

0 commit comments

Comments
 (0)