diff --git a/src/data-sources/morpho-api/market.ts b/src/data-sources/morpho-api/market.ts index 16a57a8a..4d8972c3 100644 --- a/src/data-sources/morpho-api/market.ts +++ b/src/data-sources/morpho-api/market.ts @@ -58,6 +58,7 @@ export const fetchMorphoMarket = async (uniqueKey: string, network: SupportedNet }; // Fetcher for multiple markets from Morpho API with pagination +// Uses whitelisted filter to avoid corrupted/junk market records that cause API errors export const fetchMorphoMarkets = async (network: SupportedNetworks): Promise => { const allMarkets: Market[] = []; let skip = 0; @@ -68,67 +69,49 @@ export const fetchMorphoMarkets = async (network: SupportedNetworks): Promise(marketsQuery, variables); - // Handle NOT_FOUND - break pagination loop - if (!response) { - console.warn(`No markets found in Morpho API for network ${network} at skip ${skip}.`); - break; - } - - if (!response.data || !response.data.markets) { - console.warn(`Market data not found in Morpho API response for network ${network} at skip ${skip}.`); + if (!response?.data?.markets?.items || !response.data.markets.pageInfo) { + console.warn(`[Markets] No data in response for network ${network} at skip ${skip}`); break; } const { items, pageInfo } = response.data.markets; - if (!items || !Array.isArray(items) || !pageInfo) { - console.warn(`No market items or page info found in response for network ${network} at skip ${skip}.`); - break; - } - - // Process and add markets to the collection const processedMarkets = items.map(processMarketData); allMarkets.push(...processedMarkets); - // Update pagination info totalCount = pageInfo.countTotal; skip += pageInfo.count; - console.log(`Query ${queryCount}: Fetched ${pageInfo.count} markets, total so far: ${allMarkets.length}/${totalCount}`); + console.log(`[Markets] Query ${queryCount}: Fetched ${pageInfo.count} markets, total: ${allMarkets.length}/${totalCount}`); - // Safety break if pageInfo.count is 0 to prevent infinite loop - if (pageInfo.count === 0 && skip < totalCount) { - console.warn('Received 0 items in a page, but not yet at total count. Breaking loop.'); - break; - } + if (pageInfo.count === 0) break; } while (skip < totalCount); - console.log(`Completed fetching all markets for network ${network}. Total queries: ${queryCount}, Total markets: ${allMarkets.length}`); + console.log(`[Markets] Completed for network ${network}. Queries: ${queryCount}, Markets: ${allMarkets.length}`); - // final filter: remove scam markets + // Final filter: remove scam markets return allMarkets.filter( (market) => !blacklistTokens.includes(market.collateralAsset?.address.toLowerCase() ?? '') && !blacklistTokens.includes(market.loanAsset?.address.toLowerCase() ?? ''), ); } catch (error) { - console.error(`Error fetching markets via Morpho API for network ${network}:`, error); + console.error(`[Markets] Error fetching markets for network ${network}:`, error); throw error; } }; diff --git a/src/hooks/useUserPositions.ts b/src/hooks/useUserPositions.ts index f8f40363..dc53f07f 100644 --- a/src/hooks/useUserPositions.ts +++ b/src/hooks/useUserPositions.ts @@ -170,12 +170,37 @@ const useUserPositions = (user: string | undefined, showEmpty = false, chainIds? marketsByChain.set(marketInfo.chainId, existing); }); - // Build market data map from allMarkets context (no need to fetch individually) + // Build market data map from allMarkets context const marketDataMap = new Map(); allMarkets.forEach((market) => { marketDataMap.set(market.uniqueKey.toLowerCase(), market); }); + // Find markets that need to be fetched individually (not in allMarkets cache) + const missingMarkets: PositionMarket[] = []; + finalMarketKeys.forEach((marketInfo) => { + if (!marketDataMap.has(marketInfo.marketUniqueKey.toLowerCase())) { + missingMarkets.push(marketInfo); + } + }); + + // Fetch missing markets individually (for non-whitelisted markets with positions) + if (missingMarkets.length > 0) { + console.log(`[Positions] Fetching ${missingMarkets.length} missing markets individually`); + const { fetchMorphoMarket } = await import('@/data-sources/morpho-api/market'); + + await Promise.all( + missingMarkets.map(async (marketInfo) => { + try { + const market = await fetchMorphoMarket(marketInfo.marketUniqueKey, marketInfo.chainId as SupportedNetworks); + marketDataMap.set(market.uniqueKey.toLowerCase(), market); + } catch (error) { + console.warn(`[Positions] Failed to fetch market ${marketInfo.marketUniqueKey}:`, error); + } + }), + ); + } + // Fetch snapshots for each chain using batched multicall const allSnapshots = new Map(); await Promise.all(