add project
This commit is contained in:
70
apps/web/src/lib/balances/prices.ts
Normal file
70
apps/web/src/lib/balances/prices.ts
Normal file
@@ -0,0 +1,70 @@
|
||||
const PRICE_CACHE_TTL_MS = 60_000;
|
||||
const PRICE_REQUEST_TIMEOUT_MS = 10_000;
|
||||
|
||||
let cachedPrices: Record<string, number> | null = null;
|
||||
let cachedAt = 0;
|
||||
|
||||
interface CoinGeckoPriceResponse {
|
||||
[coinId: string]: {
|
||||
usd?: number;
|
||||
};
|
||||
}
|
||||
|
||||
export async function fetchUsdPrices(coinIds: string[]): Promise<Record<string, number>> {
|
||||
const uniqueCoinIds = Array.from(new Set(coinIds.filter(Boolean)));
|
||||
|
||||
if (!uniqueCoinIds.length) {
|
||||
return {};
|
||||
}
|
||||
|
||||
if (
|
||||
cachedPrices &&
|
||||
Date.now() - cachedAt < PRICE_CACHE_TTL_MS &&
|
||||
uniqueCoinIds.every((coinId) => coinId in cachedPrices!)
|
||||
) {
|
||||
return uniqueCoinIds.reduce<Record<string, number>>((acc, coinId) => {
|
||||
acc[coinId] = cachedPrices![coinId];
|
||||
return acc;
|
||||
}, {});
|
||||
}
|
||||
|
||||
const controller = new AbortController();
|
||||
const timeoutId = setTimeout(() => controller.abort(), PRICE_REQUEST_TIMEOUT_MS);
|
||||
|
||||
try {
|
||||
const url = new URL('https://api.coingecko.com/api/v3/simple/price');
|
||||
url.searchParams.set('ids', uniqueCoinIds.join(','));
|
||||
url.searchParams.set('vs_currencies', 'usd');
|
||||
|
||||
const response = await fetch(url.toString(), {
|
||||
signal: controller.signal,
|
||||
cache: 'no-store',
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
},
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`CoinGecko returned ${response.status}`);
|
||||
}
|
||||
|
||||
const payload = (await response.json()) as CoinGeckoPriceResponse;
|
||||
const prices = uniqueCoinIds.reduce<Record<string, number>>((acc, coinId) => {
|
||||
const price = payload[coinId]?.usd;
|
||||
if (typeof price === 'number') {
|
||||
acc[coinId] = price;
|
||||
}
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
cachedPrices = {
|
||||
...(cachedPrices ?? {}),
|
||||
...prices,
|
||||
};
|
||||
cachedAt = Date.now();
|
||||
|
||||
return prices;
|
||||
} finally {
|
||||
clearTimeout(timeoutId);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user