fix(rwallet): skip spam filter when CoinGecko data unavailable
Revert per-address batching (rate limit cascade). Track cgAvailable flag in cache — only apply spam filter when CoinGecko successfully returned data. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
395623af66
commit
8ba14a0e15
|
|
@ -34,6 +34,7 @@ const NATIVE_COIN_ID: Record<string, string> = {
|
||||||
interface CacheEntry {
|
interface CacheEntry {
|
||||||
prices: Map<string, number>; // address (lowercase) → USD price
|
prices: Map<string, number>; // address (lowercase) → USD price
|
||||||
nativePrice: number;
|
nativePrice: number;
|
||||||
|
cgAvailable: boolean; // true if CoinGecko successfully returned token data
|
||||||
ts: number;
|
ts: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -74,32 +75,27 @@ export async function getNativePrice(chainId: string): Promise<number> {
|
||||||
return data?.[coinId]?.usd ?? 0;
|
return data?.[coinId]?.usd ?? 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Fetch token prices for contract addresses on a chain (1 per request for free tier) */
|
/** Fetch token prices for a batch of contract addresses on a chain */
|
||||||
export async function getTokenPrices(
|
export async function getTokenPrices(
|
||||||
chainId: string,
|
chainId: string,
|
||||||
addresses: string[],
|
addresses: string[],
|
||||||
): Promise<Map<string, number>> {
|
): Promise<{ prices: Map<string, number>; available: boolean }> {
|
||||||
const platform = CHAIN_PLATFORM[chainId];
|
const platform = CHAIN_PLATFORM[chainId];
|
||||||
if (!platform || addresses.length === 0) return new Map();
|
if (!platform || addresses.length === 0) return { prices: new Map(), available: false };
|
||||||
|
|
||||||
const lower = [...new Set(addresses.map((a) => a.toLowerCase()))];
|
const lower = [...new Set(addresses.map((a) => a.toLowerCase()))];
|
||||||
const result = new Map<string, number>();
|
const prices = new Map<string, number>();
|
||||||
|
|
||||||
// CoinGecko free tier: 1 contract address per request, ~30 req/min
|
const data = await cgFetch(
|
||||||
// Process in batches of 3 with a short delay between batches
|
`https://api.coingecko.com/api/v3/simple/token_price/${platform}?contract_addresses=${lower.join(",")}&vs_currencies=usd`,
|
||||||
const BATCH = 3;
|
);
|
||||||
for (let i = 0; i < lower.length; i += BATCH) {
|
if (data && !data.error_code) {
|
||||||
const batch = lower.slice(i, i + BATCH);
|
for (const addr of lower) {
|
||||||
const fetches = batch.map(async (addr) => {
|
if (data[addr]?.usd) prices.set(addr, data[addr].usd);
|
||||||
const data = await cgFetch(
|
}
|
||||||
`https://api.coingecko.com/api/v3/simple/token_price/${platform}?contract_addresses=${addr}&vs_currencies=usd`,
|
return { prices, available: true };
|
||||||
);
|
|
||||||
if (data?.[addr]?.usd) result.set(addr, data[addr].usd);
|
|
||||||
});
|
|
||||||
await Promise.allSettled(fetches);
|
|
||||||
if (i + BATCH < lower.length) await new Promise((r) => setTimeout(r, 1500));
|
|
||||||
}
|
}
|
||||||
return result;
|
return { prices, available: false };
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Fetch and cache all prices for a chain (native + tokens) */
|
/** Fetch and cache all prices for a chain (native + tokens) */
|
||||||
|
|
@ -117,13 +113,14 @@ async function fetchChainPrices(
|
||||||
|
|
||||||
const promise = (async (): Promise<CacheEntry> => {
|
const promise = (async (): Promise<CacheEntry> => {
|
||||||
try {
|
try {
|
||||||
const [nativePrice, tokenPrices] = await Promise.all([
|
const [nativePrice, tokenResult] = await Promise.all([
|
||||||
getNativePrice(chainId),
|
getNativePrice(chainId),
|
||||||
getTokenPrices(chainId, tokenAddresses),
|
getTokenPrices(chainId, tokenAddresses),
|
||||||
]);
|
]);
|
||||||
const entry: CacheEntry = {
|
const entry: CacheEntry = {
|
||||||
prices: tokenPrices,
|
prices: tokenResult.prices,
|
||||||
nativePrice,
|
nativePrice,
|
||||||
|
cgAvailable: tokenResult.available,
|
||||||
ts: Date.now(),
|
ts: Date.now(),
|
||||||
};
|
};
|
||||||
cache.set(chainId, entry);
|
cache.set(chainId, entry);
|
||||||
|
|
@ -202,7 +199,8 @@ export async function enrichWithPrices(
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
if (options?.filterSpam) {
|
// Only filter spam when CoinGecko data is available to verify against
|
||||||
|
if (options?.filterSpam && priceData.cgAvailable) {
|
||||||
return enriched.filter((b) => {
|
return enriched.filter((b) => {
|
||||||
// Native tokens always pass
|
// Native tokens always pass
|
||||||
if (!b.tokenAddress || b.tokenAddress === "0x0000000000000000000000000000000000000000") return true;
|
if (!b.tokenAddress || b.tokenAddress === "0x0000000000000000000000000000000000000000") return true;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue