From c11e38336943d835edb3dca2c68b20fef0e521f7 Mon Sep 17 00:00:00 2001 From: antoncoding Date: Tue, 20 Jan 2026 11:01:55 +0800 Subject: [PATCH] refactor: remove dup fetching position keys --- src/data-sources/morpho-api/fetchers.ts | 11 ++++++++- src/data-sources/morpho-api/historical.ts | 11 ++++++--- .../morpho-api/market-borrowers.ts | 5 ++++ src/data-sources/morpho-api/market-borrows.ts | 5 ++++ .../morpho-api/market-liquidations.ts | 5 ++++ .../morpho-api/market-suppliers.ts | 5 ++++ .../morpho-api/market-supplies.ts | 5 ++++ src/data-sources/morpho-api/market.ts | 24 ++++++++++++------- src/data-sources/morpho-api/positions.ts | 12 +++++++++- src/data-sources/morpho-api/prices.ts | 11 ++++++--- src/data-sources/morpho-api/transactions.ts | 9 +++++++ src/data-sources/morpho-api/v2-vaults.ts | 9 +++++-- src/data-sources/morpho-api/vaults.ts | 10 ++++---- .../components/supplied-markets-detail.tsx | 9 ++----- src/hooks/useUserPositions.ts | 6 ++++- 15 files changed, 105 insertions(+), 32 deletions(-) diff --git a/src/data-sources/morpho-api/fetchers.ts b/src/data-sources/morpho-api/fetchers.ts index f2492042..9d551f62 100644 --- a/src/data-sources/morpho-api/fetchers.ts +++ b/src/data-sources/morpho-api/fetchers.ts @@ -4,7 +4,7 @@ import { URLS } from '@/utils/urls'; export const morphoGraphqlFetcher = async >( query: string, variables: Record, -): Promise => { +): Promise => { const response = await fetch(URLS.MORPHO_BLUE_API, { method: 'POST', headers: { 'Content-Type': 'application/json' }, @@ -20,8 +20,17 @@ export const morphoGraphqlFetcher = async >( // Check for GraphQL errors if ('errors' in result && Array.isArray((result as any).errors) && (result as any).errors.length > 0) { + // If it's known "NOT FOUND" error, handle gracefully + const notFoundError = (result as any).errors.find((err: { status?: string }) => err.status?.includes('NOT_FOUND')); + + if (notFoundError) { + console.log('Morpho API return Not Found error:', notFoundError); + return null; + } + // Log the full error for debugging console.error('Morpho API GraphQL Error:', result.errors); + throw new Error('Unknown GraphQL error from Morpho API'); } diff --git a/src/data-sources/morpho-api/historical.ts b/src/data-sources/morpho-api/historical.ts index 9647f961..f67af378 100644 --- a/src/data-sources/morpho-api/historical.ts +++ b/src/data-sources/morpho-api/historical.ts @@ -9,8 +9,8 @@ type MarketWithHistoricalState = Market & { }; type HistoricalDataGraphQLResponse = { - data: { - marketByUniqueKey: MarketWithHistoricalState; + data?: { + marketByUniqueKey?: MarketWithHistoricalState; }; errors?: { message: string }[]; }; @@ -36,7 +36,12 @@ export const fetchMorphoMarketHistoricalData = async ( chainId: network, }); - const historicalState = response?.data?.marketByUniqueKey?.historicalState; + // Handle NOT_FOUND - return null + if (!response) { + return null; + } + + const historicalState = response.data?.marketByUniqueKey?.historicalState; // Check if historicalState exists and has *any* relevant data points (e.g., supplyApy) // This check might need refinement based on what fields are essential diff --git a/src/data-sources/morpho-api/market-borrowers.ts b/src/data-sources/morpho-api/market-borrowers.ts index c4164d43..5af4ba5f 100644 --- a/src/data-sources/morpho-api/market-borrowers.ts +++ b/src/data-sources/morpho-api/market-borrowers.ts @@ -55,6 +55,11 @@ export const fetchMorphoMarketBorrowers = async ( // Use the shared fetcher const result = await morphoGraphqlFetcher(marketBorrowersQuery, variables); + // Handle NOT_FOUND - return empty result + if (!result) { + return { items: [], totalCount: 0 }; + } + // Fetcher handles network and basic GraphQL errors const items = result.data?.marketPositions?.items ?? []; const totalCount = result.data?.marketPositions?.pageInfo?.countTotal ?? 0; diff --git a/src/data-sources/morpho-api/market-borrows.ts b/src/data-sources/morpho-api/market-borrows.ts index c248fd9e..16a67929 100644 --- a/src/data-sources/morpho-api/market-borrows.ts +++ b/src/data-sources/morpho-api/market-borrows.ts @@ -54,6 +54,11 @@ export const fetchMorphoMarketBorrows = async ( try { const result = await morphoGraphqlFetcher(marketBorrowsQuery, variables); + // Handle NOT_FOUND - return empty result + if (!result) { + return { items: [], totalCount: 0 }; + } + const items = result.data?.transactions?.items ?? []; const totalCount = result.data?.transactions?.pageInfo?.countTotal ?? 0; diff --git a/src/data-sources/morpho-api/market-liquidations.ts b/src/data-sources/morpho-api/market-liquidations.ts index fae4f7c4..1feef13d 100644 --- a/src/data-sources/morpho-api/market-liquidations.ts +++ b/src/data-sources/morpho-api/market-liquidations.ts @@ -37,6 +37,11 @@ export const fetchMorphoMarketLiquidations = async (marketId: string): Promise(marketLiquidationsQuery, variables); + // Handle NOT_FOUND - return empty array + if (!result) { + return []; + } + const items = result.data?.transactions?.items ?? []; // Map to unified type diff --git a/src/data-sources/morpho-api/market-suppliers.ts b/src/data-sources/morpho-api/market-suppliers.ts index 5a62f378..d210baec 100644 --- a/src/data-sources/morpho-api/market-suppliers.ts +++ b/src/data-sources/morpho-api/market-suppliers.ts @@ -54,6 +54,11 @@ export const fetchMorphoMarketSuppliers = async ( // Use the shared fetcher const result = await morphoGraphqlFetcher(marketSuppliersQuery, variables); + // Handle NOT_FOUND - return empty result + if (!result) { + return { items: [], totalCount: 0 }; + } + // Fetcher handles network and basic GraphQL errors const items = result.data?.marketPositions?.items ?? []; const totalCount = result.data?.marketPositions?.pageInfo?.countTotal ?? 0; diff --git a/src/data-sources/morpho-api/market-supplies.ts b/src/data-sources/morpho-api/market-supplies.ts index eab2f2f9..a101dea2 100644 --- a/src/data-sources/morpho-api/market-supplies.ts +++ b/src/data-sources/morpho-api/market-supplies.ts @@ -56,6 +56,11 @@ export const fetchMorphoMarketSupplies = async ( // Use the shared fetcher const result = await morphoGraphqlFetcher(marketSuppliesQuery, variables); + // Handle NOT_FOUND - return empty result + if (!result) { + return { items: [], totalCount: 0 }; + } + // Fetcher handles network and basic GraphQL errors const items = result.data?.transactions?.items ?? []; const totalCount = result.data?.transactions?.pageInfo?.countTotal ?? 0; diff --git a/src/data-sources/morpho-api/market.ts b/src/data-sources/morpho-api/market.ts index c3ff4aa9..16a57a8a 100644 --- a/src/data-sources/morpho-api/market.ts +++ b/src/data-sources/morpho-api/market.ts @@ -12,18 +12,18 @@ type MorphoApiMarket = Omit & { }; type MarketGraphQLResponse = { - data: { - marketByUniqueKey: MorphoApiMarket; + data?: { + marketByUniqueKey?: MorphoApiMarket; }; errors?: { message: string }[]; }; // Define response type for multiple markets with pageInfo type MarketsGraphQLResponse = { - data: { - markets: { - items: MorphoApiMarket[]; - pageInfo: { + data?: { + markets?: { + items?: MorphoApiMarket[]; + pageInfo?: { countTotal: number; count: number; limit: number; @@ -51,7 +51,7 @@ export const fetchMorphoMarket = async (uniqueKey: string, network: SupportedNet uniqueKey, chainId: network, }); - if (!response.data || !response.data.marketByUniqueKey) { + if (!response || !response.data || !response.data.marketByUniqueKey) { throw new Error('Market data not found in Morpho API response'); } return processMarketData(response.data.marketByUniqueKey); @@ -84,6 +84,12 @@ 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}.`); break; @@ -91,8 +97,8 @@ export const fetchMorphoMarkets = async (network: SupportedNetworks): Promise(vaultV2Query, variables); + // Handle NOT_FOUND - vault not found in API + if (!response) { + return null; + } + if (response.errors && response.errors.length > 0) { console.error('GraphQL errors:', response.errors); return null; diff --git a/src/data-sources/morpho-api/vaults.ts b/src/data-sources/morpho-api/vaults.ts index 81bf23ef..7c6a178b 100644 --- a/src/data-sources/morpho-api/vaults.ts +++ b/src/data-sources/morpho-api/vaults.ts @@ -32,9 +32,9 @@ type ApiVault = { }; type AllVaultsApiResponse = { - data: { - vaults: { - items: ApiVault[]; + data?: { + vaults?: { + items?: ApiVault[]; }; }; errors?: { message: string }[]; @@ -71,8 +71,8 @@ export const fetchAllMorphoVaults = async (): Promise => { const response = await morphoGraphqlFetcher(allVaultsQuery, variables); - if (response.errors && response.errors.length > 0) { - console.error('GraphQL errors:', response.errors); + // Handle NOT_FOUND - return empty array + if (!response) { return []; } diff --git a/src/features/positions/components/supplied-markets-detail.tsx b/src/features/positions/components/supplied-markets-detail.tsx index 5917fb5a..56276edd 100644 --- a/src/features/positions/components/supplied-markets-detail.tsx +++ b/src/features/positions/components/supplied-markets-detail.tsx @@ -143,9 +143,7 @@ export function SuppliedMarketsDetail({ groupedPosition, showCollateralExposure style={{ width: `${collateral.percentage}%`, backgroundColor: - collateral.symbol === 'Others' - ? OTHER_COLOR - : getCollateralColorFromPalette(collateral.address, pieColors), + collateral.symbol === 'Others' ? OTHER_COLOR : getCollateralColorFromPalette(collateral.address, pieColors), }} title={`${collateral.symbol}: ${collateral.percentage.toFixed(2)}%`} /> @@ -159,10 +157,7 @@ export function SuppliedMarketsDetail({ groupedPosition, showCollateralExposure > ■ diff --git a/src/hooks/useUserPositions.ts b/src/hooks/useUserPositions.ts index d9d2256e..5b40b8de 100644 --- a/src/hooks/useUserPositions.ts +++ b/src/hooks/useUserPositions.ts @@ -61,6 +61,7 @@ const fetchSourceMarketKeys = async (user: string, chainIds?: SupportedNetworks[ const results = await Promise.allSettled( networksToFetch.map(async (network) => { let markets: PositionMarket[] = []; + let apiError = false; // Try Morpho API first if supported if (supportsMorphoApi(network)) { @@ -69,12 +70,13 @@ const fetchSourceMarketKeys = async (user: string, chainIds?: SupportedNetworks[ markets = await fetchMorphoUserPositionMarkets(user, network); } catch (morphoError) { console.error(`Failed to fetch positions via Morpho API for network ${network}:`, morphoError); + apiError = true; // Continue to Subgraph fallback } } // If Morpho API failed or not supported, try Subgraph - if (markets.length === 0) { + if (markets.length === 0 && apiError) { try { console.log(`Attempting to fetch positions via Subgraph for network ${network}`); markets = await fetchSubgraphUserPositionMarkets(user, network); @@ -127,6 +129,7 @@ const useUserPositions = (user: string | undefined, showEmpty = false, chainIds? const filteredCachedMarkets = chainIds ? cachedMarkets.filter((m) => chainIds.includes(m.chainId as SupportedNetworks)) : cachedMarkets; + // Combine and deduplicate const combinedMarkets = [...sourceMarketKeys, ...filteredCachedMarkets]; const uniqueMarketsMap = new Map(); @@ -136,6 +139,7 @@ const useUserPositions = (user: string | undefined, showEmpty = false, chainIds? uniqueMarketsMap.set(key, market); } }); + const finalMarketKeys = Array.from(uniqueMarketsMap.values()); // console.log(`[Positions] Query 1: Final unique keys count: ${finalMarketKeys.length}`); return { finalMarketKeys };