From 55ab8ec1f3f154b663af2160c478826243830c51 Mon Sep 17 00:00:00 2001 From: antoncoding Date: Mon, 16 Jun 2025 23:04:15 +0800 Subject: [PATCH 1/4] feat: always fallback to subgraph --- src/config/dataSources.ts | 13 ++++ src/contexts/MarketsContext.tsx | 31 ++++++--- src/hooks/useLiquidations.ts | 69 ++++++++++++-------- src/hooks/useMarketBorrows.ts | 46 +++++++------- src/hooks/useMarketData.ts | 43 +++++++------ src/hooks/useMarketHistoricalData.ts | 41 +++++++----- src/hooks/useMarketLiquidations.ts | 46 +++++++------- src/hooks/useMarketSupplies.ts | 58 ++++++++--------- src/hooks/useUserPosition.ts | 95 ++++++++++++++++------------ src/hooks/useUserPositions.ts | 60 +++++++++--------- 10 files changed, 283 insertions(+), 219 deletions(-) diff --git a/src/config/dataSources.ts b/src/config/dataSources.ts index 23cbe41f..abf5e1fe 100644 --- a/src/config/dataSources.ts +++ b/src/config/dataSources.ts @@ -28,3 +28,16 @@ export const getHistoricalDataSource = (network: SupportedNetworks): 'morpho' | return 'subgraph'; } }; + +/** + * Check if a network supports Morpho API as a data source + */ +export const supportsMorphoApi = (network: SupportedNetworks): boolean => { + switch (network) { + case SupportedNetworks.Mainnet: + case SupportedNetworks.Base: + return true; + default: + return false; + } +}; diff --git a/src/contexts/MarketsContext.tsx b/src/contexts/MarketsContext.tsx index 0050634b..b380c146 100644 --- a/src/contexts/MarketsContext.tsx +++ b/src/contexts/MarketsContext.tsx @@ -9,7 +9,7 @@ import { useState, useMemo, } from 'react'; -import { getMarketDataSource } from '@/config/dataSources'; +import { getMarketDataSource, supportsMorphoApi } from '@/config/dataSources'; import { fetchMorphoMarkets } from '@/data-sources/morpho-api/market'; import { fetchSubgraphMarkets } from '@/data-sources/subgraph/market'; import useLiquidations from '@/hooks/useLiquidations'; @@ -88,17 +88,28 @@ export function MarketsProvider({ children }: MarketsProviderProps) { await Promise.all( networksToFetch.map(async (network) => { try { - const dataSource = getMarketDataSource(network); let networkMarkets: Market[] = []; - console.log(`Fetching markets for ${network} via ${dataSource}`); + // Try Morpho API first if supported + if (supportsMorphoApi(network)) { + try { + console.log(`Attempting to fetch markets via Morpho API for ${network}`); + networkMarkets = await fetchMorphoMarkets(network); + } catch (morphoError) { + console.error(`Failed to fetch markets via Morpho API for ${network}:`, morphoError); + // Continue to Subgraph fallback + } + } - if (dataSource === 'morpho') { - networkMarkets = await fetchMorphoMarkets(network); - } else if (dataSource === 'subgraph') { - networkMarkets = await fetchSubgraphMarkets(network); - } else { - console.warn(`No valid data source found for network ${network}`); + // If Morpho API failed or not supported, try Subgraph + if (networkMarkets.length === 0) { + try { + console.log(`Attempting to fetch markets via Subgraph for ${network}`); + networkMarkets = await fetchSubgraphMarkets(network); + } catch (subgraphError) { + console.error(`Failed to fetch markets via Subgraph for ${network}:`, subgraphError); + throw subgraphError; // Throw to be caught by outer catch + } } combinedMarkets.push(...networkMarkets); @@ -114,7 +125,7 @@ export function MarketsProvider({ children }: MarketsProviderProps) { const filtered = combinedMarkets .filter((market) => market.collateralAsset != undefined) .filter((market) => isSupportedChain(market.morphoBlue.chain.id)) // Keep this filter - .filter((market) => !blacklistedMarkets.includes(market.uniqueKey)); // Filter out blacklisted markets + .filter((market) => !blacklistedMarkets.includes(market.uniqueKey)); const processedMarkets = filtered.map((market) => { const warningsWithDetail = getMarketWarningsWithDetail(market, true); // Recalculate warnings if needed, though fetchers might do this diff --git a/src/hooks/useLiquidations.ts b/src/hooks/useLiquidations.ts index 50a04c4c..f8a00931 100644 --- a/src/hooks/useLiquidations.ts +++ b/src/hooks/useLiquidations.ts @@ -1,5 +1,5 @@ import { useState, useEffect, useCallback } from 'react'; -import { getMarketDataSource } from '@/config/dataSources'; +import { supportsMorphoApi } from '@/config/dataSources'; import { fetchMorphoApiLiquidatedMarketKeys } from '@/data-sources/morpho-api/liquidations'; import { fetchSubgraphLiquidatedMarketKeys } from '@/data-sources/subgraph/liquidations'; import { SupportedNetworks } from '@/utils/networks'; @@ -31,57 +31,70 @@ const useLiquidations = () => { await Promise.all( networksToCheck.map(async (network) => { try { - const dataSource = getMarketDataSource(network); let networkLiquidatedKeys: Set; - console.log(`Fetching liquidated markets for ${network} via ${dataSource}`); - - if (dataSource === 'morpho') { - networkLiquidatedKeys = await fetchMorphoApiLiquidatedMarketKeys(network); - } else if (dataSource === 'subgraph') { - networkLiquidatedKeys = await fetchSubgraphLiquidatedMarketKeys(network); + // Try Morpho API first if supported + if (supportsMorphoApi(network)) { + try { + console.log(`Attempting to fetch liquidated markets via Morpho API for ${network}`); + networkLiquidatedKeys = await fetchMorphoApiLiquidatedMarketKeys(network); + } catch (morphoError) { + console.error(`Failed to fetch liquidated markets via Morpho API:`, morphoError); + // Continue to Subgraph fallback + networkLiquidatedKeys = new Set(); + } } else { - console.warn(`No valid data source found for network ${network} for liquidations.`); - networkLiquidatedKeys = new Set(); // Assume none if no source + networkLiquidatedKeys = new Set(); + } + + // If Morpho API failed or not supported, try Subgraph + if (networkLiquidatedKeys.size === 0) { + try { + console.log(`Attempting to fetch liquidated markets via Subgraph for ${network}`); + networkLiquidatedKeys = await fetchSubgraphLiquidatedMarketKeys(network); + } catch (subgraphError) { + console.error(`Failed to fetch liquidated markets via Subgraph:`, subgraphError); + throw subgraphError; // Throw to be caught by outer catch + } } - // Add keys from this network to the combined set + // Add the keys to the combined set networkLiquidatedKeys.forEach((key) => combinedLiquidatedKeys.add(key)); } catch (networkError) { - console.error( - `Failed to fetch liquidated market keys for network ${network}:`, - networkError, - ); - fetchErrors.push(networkError); // Collect errors + console.error(`Failed to fetch liquidated markets for network ${network}:`, networkError); + fetchErrors.push(networkError); } }), ); setLiquidatedMarketKeys(combinedLiquidatedKeys); - // Set overall error if any network fetch failed if (fetchErrors.length > 0) { - setError(fetchErrors[0]); // Or aggregate errors if needed + setError(fetchErrors[0]); } } catch (err) { - // Catch potential errors from Promise.all itself - console.error('Overall error fetching liquidations:', err); + console.error('Error fetching liquidated markets:', err); setError(err); } finally { - setLoading(false); - setIsRefetching(false); + if (isRefetch) { + setIsRefetching(false); + } else { + setLoading(false); + } } - }, []); // Dependencies: None needed directly, fetchers are self-contained + }, []); useEffect(() => { fetchLiquidations().catch(console.error); }, [fetchLiquidations]); - const refetch = useCallback(() => { - fetchLiquidations(true).catch(console.error); - }, [fetchLiquidations]); - - return { loading, isRefetching, liquidatedMarketKeys, error, refetch }; + return { + loading, + isRefetching, + liquidatedMarketKeys, + error, + refetch: () => fetchLiquidations(true), + }; }; export default useLiquidations; diff --git a/src/hooks/useMarketBorrows.ts b/src/hooks/useMarketBorrows.ts index c83d1e23..b3f12eea 100644 --- a/src/hooks/useMarketBorrows.ts +++ b/src/hooks/useMarketBorrows.ts @@ -1,5 +1,5 @@ import { useQuery } from '@tanstack/react-query'; -import { getMarketDataSource } from '@/config/dataSources'; +import { supportsMorphoApi } from '@/config/dataSources'; import { fetchMorphoMarketBorrows } from '@/data-sources/morpho-api/market-borrows'; import { fetchSubgraphMarketBorrows } from '@/data-sources/subgraph/market-borrows'; import { SupportedNetworks } from '@/utils/networks'; @@ -20,37 +20,40 @@ export const useMarketBorrows = ( ) => { const queryKey = ['marketBorrows', marketId, loanAssetId, network]; - // Determine the data source - const dataSource = network ? getMarketDataSource(network) : null; - const { data, isLoading, error, refetch } = useQuery({ queryKey: queryKey, queryFn: async (): Promise => { - // Guard clauses - if (!marketId || !loanAssetId || !network || !dataSource) { + if (!marketId || !loanAssetId || !network) { return null; } - console.log( - `Fetching market borrows for market ${marketId} (loan asset ${loanAssetId}) on ${network} via ${dataSource}`, - ); + let borrows: MarketActivityTransaction[] | null = null; - try { - if (dataSource === 'morpho') { - // Morpho API might only need marketId for borrows - return await fetchMorphoMarketBorrows(marketId); - } else if (dataSource === 'subgraph') { - return await fetchSubgraphMarketBorrows(marketId, loanAssetId, network); + // Try Morpho API first if supported + if (supportsMorphoApi(network)) { + try { + console.log(`Attempting to fetch borrows via Morpho API for ${marketId}`); + borrows = await fetchMorphoMarketBorrows(marketId); + } catch (morphoError) { + console.error(`Failed to fetch borrows via Morpho API:`, morphoError); + // Continue to Subgraph fallback + } + } + + // If Morpho API failed or not supported, try Subgraph + if (!borrows) { + try { + console.log(`Attempting to fetch borrows via Subgraph for ${marketId}`); + borrows = await fetchSubgraphMarketBorrows(marketId, loanAssetId, network); + } catch (subgraphError) { + console.error(`Failed to fetch borrows via Subgraph:`, subgraphError); + borrows = null; } - } catch (fetchError) { - console.error(`Failed to fetch market borrows via ${dataSource}:`, fetchError); - return null; } - console.warn('Unknown market data source determined for borrows'); - return null; + return borrows; }, - enabled: !!marketId && !!loanAssetId && !!network && !!dataSource, + enabled: !!marketId && !!loanAssetId && !!network, staleTime: 1000 * 60 * 2, // 2 minutes placeholderData: (previousData) => previousData ?? null, retry: 1, @@ -62,7 +65,6 @@ export const useMarketBorrows = ( isLoading: isLoading, error: error, refetch: refetch, - dataSource: dataSource, }; }; diff --git a/src/hooks/useMarketData.ts b/src/hooks/useMarketData.ts index e57997c7..ba343c09 100644 --- a/src/hooks/useMarketData.ts +++ b/src/hooks/useMarketData.ts @@ -1,5 +1,5 @@ import { useQuery } from '@tanstack/react-query'; -import { getMarketDataSource } from '@/config/dataSources'; +import { supportsMorphoApi } from '@/config/dataSources'; import { fetchMorphoMarket } from '@/data-sources/morpho-api/market'; import { fetchSubgraphMarket } from '@/data-sources/subgraph/market'; import { SupportedNetworks } from '@/utils/networks'; @@ -12,19 +12,15 @@ export const useMarketData = ( ) => { const queryKey = ['marketData', uniqueKey, network]; - const dataSource = network ? getMarketDataSource(network) : null; - const { data, isLoading, error, refetch } = useQuery({ queryKey: queryKey, queryFn: async (): Promise => { console.log('fetching market'); - if (!uniqueKey || !network || !dataSource) { + if (!uniqueKey || !network) { return null; } - console.log(`Fetching market data for ${uniqueKey} on ${network} via ${dataSource}`); - // 1. Try fetching the on-chain market snapshot first console.log(`Attempting fetchMarketSnapshot for market ${uniqueKey}`); let snapshot = null; @@ -38,19 +34,29 @@ export const useMarketData = ( let finalMarket: Market | null = null; - // 2. Fetch market data from fallback source + // 2. Try Morpho API first if supported, then fallback to Subgraph try { - if (dataSource === 'morpho') { + if (supportsMorphoApi(network)) { + console.log(`Attempting to fetch market data via Morpho API for ${uniqueKey}`); finalMarket = await fetchMorphoMarket(uniqueKey, network); - } else if (dataSource === 'subgraph') { + } + } catch (morphoError) { + console.error(`Failed to fetch market data via Morpho API:`, morphoError); + // Continue to Subgraph fallback + } + + // 3. If Morpho API failed or not supported, try Subgraph + if (!finalMarket) { + try { + console.log(`Attempting to fetch market data via Subgraph for ${uniqueKey}`); finalMarket = await fetchSubgraphMarket(uniqueKey, network); + } catch (subgraphError) { + console.error(`Failed to fetch market data via Subgraph:`, subgraphError); + finalMarket = null; } - } catch (fetchError) { - console.error(`Failed to fetch market data via ${dataSource}:`, fetchError); - finalMarket = null; } - // 3. If we have both snapshot and market data, override the state fields with snapshot + // 4. If we have both snapshot and market data, override the state fields with snapshot if (snapshot && finalMarket) { console.log(`Found market snapshot for ${uniqueKey}, overriding state with on-chain data.`); finalMarket = { @@ -66,21 +72,21 @@ export const useMarketData = ( }, }; } else if (!finalMarket) { - // Both snapshot and fallback failed + // Both data sources failed console.error( - `Failed to fetch market data for ${uniqueKey} via both snapshot and fallback.`, + `Failed to fetch market data for ${uniqueKey} via both Morpho API and Subgraph.`, ); finalMarket = null; } else if (!snapshot) { - // Snapshot failed but fallback succeeded - just use fallback - console.warn(`Market snapshot failed for ${uniqueKey}, using fallback data source only.`); + // Snapshot failed but data source succeeded - just use data source + console.warn(`Market snapshot failed for ${uniqueKey}, using data source only.`); } console.log(`Final market data for ${uniqueKey}:`, finalMarket ? 'Found' : 'Not Found'); return finalMarket; }, - enabled: !!uniqueKey && !!network && !!dataSource, + enabled: !!uniqueKey && !!network, staleTime: 1000 * 60 * 5, placeholderData: (previousData) => previousData ?? null, retry: 1, @@ -91,6 +97,5 @@ export const useMarketData = ( isLoading: isLoading, error: error, refetch: refetch, - dataSource: dataSource, }; }; diff --git a/src/hooks/useMarketHistoricalData.ts b/src/hooks/useMarketHistoricalData.ts index f1310465..32ecc6f7 100644 --- a/src/hooks/useMarketHistoricalData.ts +++ b/src/hooks/useMarketHistoricalData.ts @@ -1,5 +1,5 @@ import { useQuery } from '@tanstack/react-query'; -import { getHistoricalDataSource } from '@/config/dataSources'; +import { supportsMorphoApi } from '@/config/dataSources'; import { fetchMorphoMarketHistoricalData, HistoricalDataSuccessResult, @@ -22,33 +22,45 @@ export const useMarketHistoricalData = ( options?.interval, ]; - const dataSource = network ? getHistoricalDataSource(network) : null; - const { data, isLoading, error, refetch } = useQuery({ queryKey: queryKey, queryFn: async (): Promise => { - if (!uniqueKey || !network || !options || !dataSource) { - console.log('Historical data prerequisites not met or source unavailable.', { + if (!uniqueKey || !network || !options) { + console.log('Historical data prerequisites not met.', { uniqueKey, network, options, - dataSource, }); return null; } - console.log(`Fetching historical data for ${uniqueKey} on ${network} via ${dataSource}`); + let historicalData: HistoricalDataSuccessResult | null = null; + + // Try Morpho API first if supported + if (supportsMorphoApi(network)) { + try { + console.log(`Attempting to fetch historical data via Morpho API for ${uniqueKey}`); + historicalData = await fetchMorphoMarketHistoricalData(uniqueKey, network, options); + } catch (morphoError) { + console.error(`Failed to fetch historical data via Morpho API:`, morphoError); + // Continue to Subgraph fallback + } + } - if (dataSource === 'morpho') { - return fetchMorphoMarketHistoricalData(uniqueKey, network, options); - } else if (dataSource === 'subgraph') { - return fetchSubgraphMarketHistoricalData(uniqueKey, network, options); + // If Morpho API failed or not supported, try Subgraph + if (!historicalData) { + try { + console.log(`Attempting to fetch historical data via Subgraph for ${uniqueKey}`); + historicalData = await fetchSubgraphMarketHistoricalData(uniqueKey, network, options); + } catch (subgraphError) { + console.error(`Failed to fetch historical data via Subgraph:`, subgraphError); + historicalData = null; + } } - console.warn('Unknown historical data source determined'); - return null; + return historicalData; }, - enabled: !!uniqueKey && !!network && !!options && !!dataSource, + enabled: !!uniqueKey && !!network && !!options, staleTime: 1000 * 60 * 5, placeholderData: null, retry: 1, @@ -59,6 +71,5 @@ export const useMarketHistoricalData = ( isLoading: isLoading, error: error, refetch: refetch, - dataSource: dataSource, }; }; diff --git a/src/hooks/useMarketLiquidations.ts b/src/hooks/useMarketLiquidations.ts index 65da8d64..f39e72bb 100644 --- a/src/hooks/useMarketLiquidations.ts +++ b/src/hooks/useMarketLiquidations.ts @@ -1,5 +1,5 @@ import { useQuery } from '@tanstack/react-query'; -import { getMarketDataSource } from '@/config/dataSources'; +import { supportsMorphoApi } from '@/config/dataSources'; import { fetchMorphoMarketLiquidations } from '@/data-sources/morpho-api/market-liquidations'; import { fetchSubgraphMarketLiquidations } from '@/data-sources/subgraph/market-liquidations'; import { SupportedNetworks } from '@/utils/networks'; @@ -18,37 +18,40 @@ export const useMarketLiquidations = ( // Note: loanAssetId is not needed for liquidations query const queryKey = ['marketLiquidations', marketId, network]; - // Determine the data source - const dataSource = network ? getMarketDataSource(network) : null; - const { data, isLoading, error, refetch } = useQuery({ queryKey: queryKey, queryFn: async (): Promise => { - // Guard clauses - if (!marketId || !network || !dataSource) { + if (!marketId || !network) { return null; } - console.log( - `Fetching market liquidations for market ${marketId} on ${network} via ${dataSource}`, - ); + let liquidations: MarketLiquidationTransaction[] | null = null; - try { - if (dataSource === 'morpho') { - return await fetchMorphoMarketLiquidations(marketId); - } else if (dataSource === 'subgraph') { - console.log('fetching subgraph liquidations'); - return await fetchSubgraphMarketLiquidations(marketId, network); + // Try Morpho API first if supported + if (supportsMorphoApi(network)) { + try { + console.log(`Attempting to fetch liquidations via Morpho API for ${marketId}`); + liquidations = await fetchMorphoMarketLiquidations(marketId); + } catch (morphoError) { + console.error(`Failed to fetch liquidations via Morpho API:`, morphoError); + // Continue to Subgraph fallback + } + } + + // If Morpho API failed or not supported, try Subgraph + if (!liquidations) { + try { + console.log(`Attempting to fetch liquidations via Subgraph for ${marketId}`); + liquidations = await fetchSubgraphMarketLiquidations(marketId, network); + } catch (subgraphError) { + console.error(`Failed to fetch liquidations via Subgraph:`, subgraphError); + liquidations = null; } - } catch (fetchError) { - console.error(`Failed to fetch market liquidations via ${dataSource}:`, fetchError); - return null; } - console.warn('Unknown market data source determined for liquidations'); - return null; + return liquidations; }, - enabled: !!marketId && !!network && !!dataSource, + enabled: !!marketId && !!network, staleTime: 1000 * 60 * 5, // 5 minutes, liquidations are less frequent placeholderData: (previousData) => previousData ?? null, retry: 1, @@ -60,7 +63,6 @@ export const useMarketLiquidations = ( isLoading: isLoading, error: error, refetch: refetch, - dataSource: dataSource, }; }; diff --git a/src/hooks/useMarketSupplies.ts b/src/hooks/useMarketSupplies.ts index 1debc146..74043843 100644 --- a/src/hooks/useMarketSupplies.ts +++ b/src/hooks/useMarketSupplies.ts @@ -1,5 +1,5 @@ import { useQuery } from '@tanstack/react-query'; -import { getMarketDataSource } from '@/config/dataSources'; +import { supportsMorphoApi } from '@/config/dataSources'; import { fetchMorphoMarketSupplies } from '@/data-sources/morpho-api/market-supplies'; import { fetchSubgraphMarketSupplies } from '@/data-sources/subgraph/market-supplies'; import { SupportedNetworks } from '@/utils/networks'; @@ -20,46 +20,41 @@ export const useMarketSupplies = ( ) => { const queryKey = ['marketSupplies', marketId, loanAssetId, network]; - // Determine the data source - const dataSource = network ? getMarketDataSource(network) : null; - - const { data, isLoading, error, refetch } = useQuery< - MarketActivityTransaction[] | null // The hook returns the unified type - >({ + const { data, isLoading, error, refetch } = useQuery({ queryKey: queryKey, queryFn: async (): Promise => { - // Guard clauses - if (!marketId || !loanAssetId || !network || !dataSource) { + if (!marketId || !loanAssetId || !network) { return null; } - console.log( - `Fetching market supplies for market ${marketId} (loan asset ${loanAssetId}) on ${network} via ${dataSource}`, - ); + let supplies: MarketActivityTransaction[] | null = null; + + // Try Morpho API first if supported + if (supportsMorphoApi(network)) { + try { + console.log(`Attempting to fetch supplies via Morpho API for ${marketId}`); + supplies = await fetchMorphoMarketSupplies(marketId); + } catch (morphoError) { + console.error(`Failed to fetch supplies via Morpho API:`, morphoError); + // Continue to Subgraph fallback + } + } - try { - // Call the appropriate imported function - if (dataSource === 'morpho') { - return await fetchMorphoMarketSupplies(marketId); - } else if (dataSource === 'subgraph') { - return await fetchSubgraphMarketSupplies(marketId, loanAssetId, network); + // If Morpho API failed or not supported, try Subgraph + if (!supplies) { + try { + console.log(`Attempting to fetch supplies via Subgraph for ${marketId}`); + supplies = await fetchSubgraphMarketSupplies(marketId, loanAssetId, network); + } catch (subgraphError) { + console.error(`Failed to fetch supplies via Subgraph:`, subgraphError); + supplies = null; } - } catch (fetchError) { - // Log the specific error from the data source function - console.error( - `Failed to fetch market supplies via ${dataSource} for market ${marketId}:`, - fetchError, - ); - return null; // Return null on fetch error } - // This case should ideally not be reached if getMarketDataSource is exhaustive - console.warn('Unknown market data source determined for supplies'); - return null; + return supplies; }, - // enable query only if all parameters are present AND a valid data source exists - enabled: !!marketId && !!loanAssetId && !!network && !!dataSource, - staleTime: 1000 * 60 * 2, // 2 minutes + enabled: !!marketId && !!loanAssetId && !!network, + staleTime: 1000 * 60 * 2, placeholderData: (previousData) => previousData ?? null, retry: 1, }); @@ -69,7 +64,6 @@ export const useMarketSupplies = ( isLoading: isLoading, error: error, refetch: refetch, - dataSource: dataSource, }; }; diff --git a/src/hooks/useUserPosition.ts b/src/hooks/useUserPosition.ts index 29e4eac1..1580ba68 100644 --- a/src/hooks/useUserPosition.ts +++ b/src/hooks/useUserPosition.ts @@ -1,6 +1,6 @@ import { useQuery } from '@tanstack/react-query'; import { Address } from 'viem'; -import { getMarketDataSource } from '@/config/dataSources'; +import { supportsMorphoApi } from '@/config/dataSources'; import { fetchMorphoUserPositionForMarket } from '@/data-sources/morpho-api/positions'; import { fetchSubgraphUserPositionForMarket } from '@/data-sources/subgraph/positions'; import { SupportedNetworks } from '@/utils/networks'; @@ -83,37 +83,46 @@ const useUserPosition = ( console.warn( `Local market data not found for ${marketKey}. Fetching from fallback source to combine with snapshot.`, ); - const dataSource = getMarketDataSource(chainId); let fallbackPosition: MarketPosition | null = null; - try { - if (dataSource === 'morpho') { + + // Try Morpho API first if supported + if (supportsMorphoApi(chainId)) { + try { + console.log(`Attempting to fetch position via Morpho API for ${marketKey}`); fallbackPosition = await fetchMorphoUserPositionForMarket(marketKey, user, chainId); - } else if (dataSource === 'subgraph') { - fallbackPosition = await fetchSubgraphUserPositionForMarket(marketKey, user, chainId); + } catch (morphoError) { + console.error(`Failed to fetch position via Morpho API:`, morphoError); + // Continue to Subgraph fallback } - if (fallbackPosition) { - // Fallback succeeded, combine with snapshot state - finalPosition = { - ...fallbackPosition, - state: { - supplyAssets: snapshot.supplyAssets.toString(), - supplyShares: snapshot.supplyShares.toString(), - borrowAssets: snapshot.borrowAssets.toString(), - borrowShares: snapshot.borrowShares.toString(), - collateral: snapshot.collateral, - }, - }; - } else { - // Fallback failed even though snapshot existed - console.error( - `Snapshot exists for ${marketKey}, but fallback fetch failed. Cannot return full position.`, - ); - finalPosition = null; + } + + // If Morpho API failed or not supported, try Subgraph + if (!fallbackPosition) { + try { + console.log(`Attempting to fetch position via Subgraph for ${marketKey}`); + fallbackPosition = await fetchSubgraphUserPositionForMarket(marketKey, user, chainId); + } catch (subgraphError) { + console.error(`Failed to fetch position via Subgraph:`, subgraphError); + fallbackPosition = null; } - } catch (fetchError) { + } + + if (fallbackPosition) { + // Fallback succeeded, combine with snapshot state + finalPosition = { + ...fallbackPosition, + state: { + supplyAssets: snapshot.supplyAssets.toString(), + supplyShares: snapshot.supplyShares.toString(), + borrowAssets: snapshot.borrowAssets.toString(), + borrowShares: snapshot.borrowShares.toString(), + collateral: snapshot.collateral, + }, + }; + } else { + // Fallback failed even though snapshot existed console.error( - `Failed to fetch user position via fallback (${dataSource}) for ${user} on market ${marketKey} after snapshot success:`, - fetchError, + `Snapshot exists for ${marketKey}, but fallback fetch failed. Cannot return full position.`, ); finalPosition = null; } @@ -121,23 +130,27 @@ const useUserPosition = ( } else { // Snapshot failed, rely entirely on the fallback data source console.log(`Snapshot failed for ${marketKey}, fetching from fallback source.`); - const dataSource = getMarketDataSource(chainId); - try { - if (dataSource === 'morpho') { + + // Try Morpho API first if supported + if (supportsMorphoApi(chainId)) { + try { + console.log(`Attempting to fetch position via Morpho API for ${marketKey}`); finalPosition = await fetchMorphoUserPositionForMarket(marketKey, user, chainId); - } else if (dataSource === 'subgraph') { + } catch (morphoError) { + console.error(`Failed to fetch position via Morpho API:`, morphoError); + // Continue to Subgraph fallback + } + } + + // If Morpho API failed or not supported, try Subgraph + if (!finalPosition) { + try { + console.log(`Attempting to fetch position via Subgraph for ${marketKey}`); finalPosition = await fetchSubgraphUserPositionForMarket(marketKey, user, chainId); + } catch (subgraphError) { + console.error(`Failed to fetch position via Subgraph:`, subgraphError); + finalPosition = null; } - console.log( - `Fallback fetch result (after snapshot failure) for ${marketKey}:`, - finalPosition ? 'Found' : 'Not Found', - ); - } catch (fetchError) { - console.error( - `Failed to fetch user position via fallback (${dataSource}) for ${user} on market ${marketKey}:`, - fetchError, - ); - finalPosition = null; // Ensure null on error } } diff --git a/src/hooks/useUserPositions.ts b/src/hooks/useUserPositions.ts index 6b7515aa..b94e2631 100644 --- a/src/hooks/useUserPositions.ts +++ b/src/hooks/useUserPositions.ts @@ -1,7 +1,7 @@ import { useCallback } from 'react'; import { useQuery, useQueryClient } from '@tanstack/react-query'; import { Address } from 'viem'; -import { getMarketDataSource } from '@/config/dataSources'; +import { getMarketDataSource, supportsMorphoApi } from '@/config/dataSources'; import { fetchMorphoUserPositionMarkets } from '@/data-sources/morpho-api/positions'; import { fetchSubgraphUserPositionMarkets } from '@/data-sources/subgraph/positions'; import { SupportedNetworks } from '@/utils/networks'; @@ -69,43 +69,43 @@ const fetchSourceMarketKeys = async (user: string): Promise => (value) => typeof value === 'number', ) as SupportedNetworks[]; - const morphoNetworks: SupportedNetworks[] = []; - const subgraphNetworks: SupportedNetworks[] = []; - - allSupportedNetworks.forEach((network: SupportedNetworks) => { - const source = getMarketDataSource(network); - if (source === 'subgraph') { - subgraphNetworks.push(network); - } else { - morphoNetworks.push(network); - } - }); - - const fetchPromises: Promise[] = []; + const results = await Promise.allSettled( + allSupportedNetworks.map(async (network) => { + let markets: PositionMarket[] = []; + + // Try Morpho API first if supported + if (supportsMorphoApi(network)) { + try { + console.log(`Attempting to fetch positions via Morpho API for network ${network}`); + markets = await fetchMorphoUserPositionMarkets(user, network); + } catch (morphoError) { + console.error(`Failed to fetch positions via Morpho API for network ${network}:`, morphoError); + // Continue to Subgraph fallback + } + } - morphoNetworks.forEach((network) => { - fetchPromises.push(fetchMorphoUserPositionMarkets(user, network)); - }); - subgraphNetworks.forEach((network) => { - fetchPromises.push(fetchSubgraphUserPositionMarkets(user, network)); - }); + // If Morpho API failed or not supported, try Subgraph + if (markets.length === 0) { + try { + console.log(`Attempting to fetch positions via Subgraph for network ${network}`); + markets = await fetchSubgraphUserPositionMarkets(user, network); + } catch (subgraphError) { + console.error(`Failed to fetch positions via Subgraph for network ${network}:`, subgraphError); + return []; + } + } - const results = await Promise.allSettled(fetchPromises); + return markets; + }), + ); let sourcePositionMarkets: PositionMarket[] = []; - results.forEach((result, index) => { + results.forEach((result) => { if (result.status === 'fulfilled') { sourcePositionMarkets = sourcePositionMarkets.concat(result.value); - } else { - const network = [...morphoNetworks, ...subgraphNetworks][index]; - const source = getMarketDataSource(network); - console.error( - `[Positions] Failed to fetch from ${source} for network ${network}:`, - result.reason, - ); } }); - // console.log(`[Positions] Fetched ${sourcePositionMarkets.length} keys from sources.`); + return sourcePositionMarkets; }; From 76497c2a7581332cde970ce2b615cfa87804cfca Mon Sep 17 00:00:00 2001 From: antoncoding Date: Mon, 16 Jun 2025 23:30:23 +0800 Subject: [PATCH 2/4] chore: fix build --- app/market/[chainId]/[marketid]/content.tsx | 13 +- src/contexts/MarketsContext.tsx | 30 +++- src/hooks/useLiquidations.ts | 7 +- src/hooks/useUserPosition.ts | 2 +- src/hooks/useUserPositions.ts | 133 +++++++++--------- src/hooks/useUserTransactions.ts | 146 +++++++++++--------- src/utils/tokens.ts | 4 +- 7 files changed, 193 insertions(+), 142 deletions(-) diff --git a/app/market/[chainId]/[marketid]/content.tsx b/app/market/[chainId]/[marketid]/content.tsx index feafaf52..dacd59c3 100644 --- a/app/market/[chainId]/[marketid]/content.tsx +++ b/app/market/[chainId]/[marketid]/content.tsx @@ -131,7 +131,9 @@ function MarketContent() { // Non-async wrapper for components that expect void returns const handleRefreshAllSync = useCallback(() => { - void handleRefreshAll(); + void handleRefreshAll().catch((error) => { + console.error('Failed to refresh data:', error); + }); }, [handleRefreshAll]); // Unified handler for timeframe changes @@ -200,9 +202,12 @@ function MarketContent() {