Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 10 additions & 1 deletion src/data-sources/morpho-api/fetchers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { URLS } from '@/utils/urls';
export const morphoGraphqlFetcher = async <T extends Record<string, any>>(
query: string,
variables: Record<string, unknown>,
): Promise<T> => {
): Promise<T | null> => {
const response = await fetch(URLS.MORPHO_BLUE_API, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
Expand All @@ -20,8 +20,17 @@ export const morphoGraphqlFetcher = async <T extends Record<string, any>>(

// 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;
}
Comment thread
antoncoding marked this conversation as resolved.

// Log the full error for debugging
console.error('Morpho API GraphQL Error:', result.errors);

throw new Error('Unknown GraphQL error from Morpho API');
}

Expand Down
11 changes: 8 additions & 3 deletions src/data-sources/morpho-api/historical.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ type MarketWithHistoricalState = Market & {
};

type HistoricalDataGraphQLResponse = {
data: {
marketByUniqueKey: MarketWithHistoricalState;
data?: {
marketByUniqueKey?: MarketWithHistoricalState;
};
errors?: { message: string }[];
};
Expand All @@ -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
Expand Down
5 changes: 5 additions & 0 deletions src/data-sources/morpho-api/market-borrowers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,11 @@ export const fetchMorphoMarketBorrowers = async (
// Use the shared fetcher
const result = await morphoGraphqlFetcher<MorphoAPIBorrowersResponse>(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;
Expand Down
5 changes: 5 additions & 0 deletions src/data-sources/morpho-api/market-borrows.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,11 @@ export const fetchMorphoMarketBorrows = async (
try {
const result = await morphoGraphqlFetcher<MorphoAPIBorrowsResponse>(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;

Expand Down
5 changes: 5 additions & 0 deletions src/data-sources/morpho-api/market-liquidations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,11 @@ export const fetchMorphoMarketLiquidations = async (marketId: string): Promise<M
try {
const result = await morphoGraphqlFetcher<MorphoAPILiquidationsResponse>(marketLiquidationsQuery, variables);

// Handle NOT_FOUND - return empty array
if (!result) {
return [];
}

const items = result.data?.transactions?.items ?? [];

// Map to unified type
Expand Down
5 changes: 5 additions & 0 deletions src/data-sources/morpho-api/market-suppliers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,11 @@ export const fetchMorphoMarketSuppliers = async (
// Use the shared fetcher
const result = await morphoGraphqlFetcher<MorphoAPISuppliersResponse>(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;
Expand Down
5 changes: 5 additions & 0 deletions src/data-sources/morpho-api/market-supplies.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,11 @@ export const fetchMorphoMarketSupplies = async (
// Use the shared fetcher
const result = await morphoGraphqlFetcher<MorphoAPISuppliesResponse>(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;
Expand Down
24 changes: 15 additions & 9 deletions src/data-sources/morpho-api/market.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,18 +12,18 @@ type MorphoApiMarket = Omit<Market, 'oracleAddress' | 'whitelisted'> & {
};

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;
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -84,15 +84,21 @@ export const fetchMorphoMarkets = async (network: SupportedNetworks): Promise<Ma

const response = await morphoGraphqlFetcher<MarketsGraphQLResponse>(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;
}

const { items, pageInfo } = response.data.markets;

if (!items || !Array.isArray(items)) {
console.warn(`No market items found in response for network ${network} at skip ${skip}.`);
if (!items || !Array.isArray(items) || !pageInfo) {
console.warn(`No market items or page info found in response for network ${network} at skip ${skip}.`);
break;
}

Expand Down
12 changes: 11 additions & 1 deletion src/data-sources/morpho-api/positions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,11 @@ export const fetchMorphoUserPositionMarkets = async (
chainId: network,
});

// Handle NOT_FOUND - return empty array
if (!result) {
return [];
}

const marketPositions = result.data?.userByAddress?.marketPositions ?? [];

// Filter for valid positions and extract market key and chain ID
Expand All @@ -58,7 +63,7 @@ export const fetchMorphoUserPositionMarkets = async (
return positionMarkets;
} catch (error) {
console.error(`Failed to fetch position markets from Morpho API for ${userAddress} on ${network}:`, error);
return []; // Return empty array on error
throw error; // Re-throw to allow caller to handle fallback
}
};

Expand All @@ -77,6 +82,11 @@ export const fetchMorphoUserPositionForMarket = async (
marketKey: marketUniqueKey,
});

// Handle NOT_FOUND - return null
if (!result) {
return null;
}

const marketPosition = result.data?.marketPosition;

// Check if the position state has zero balances - API might return structure even with no actual position
Expand Down
11 changes: 8 additions & 3 deletions src/data-sources/morpho-api/prices.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,9 @@ type AssetPriceItem = {
};

type AssetPricesResponse = {
data: {
assets: {
items: AssetPriceItem[];
data?: {
assets?: {
items?: AssetPriceItem[];
};
};
};
Expand Down Expand Up @@ -66,6 +66,11 @@ export const fetchTokenPrices = async (tokens: TokenPriceInput[]): Promise<Map<s
},
});

// Handle NOT_FOUND - skip this chain
if (!response) {
return;
}

if (!response.data?.assets?.items) {
console.warn(`No price data returned for chain ${chainId}`);
return;
Expand Down
9 changes: 9 additions & 0 deletions src/data-sources/morpho-api/transactions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,15 @@ export const fetchMorphoTransactions = async (filters: TransactionFilters): Prom
skip: filters.skip ?? 0,
});

// Handle NOT_FOUND - return empty result
if (!result) {
return {
items: [],
pageInfo: { count: 0, countTotal: 0 },
error: null,
};
}

const transactions = result.data?.transactions;
if (!transactions) {
return {
Expand Down
9 changes: 7 additions & 2 deletions src/data-sources/morpho-api/v2-vaults.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,8 @@ type ApiVaultV2 = {
};

type VaultV2ApiResponse = {
data: {
vaultV2ByAddress: ApiVaultV2 | null;
data?: {
vaultV2ByAddress?: ApiVaultV2 | null;
};
errors?: { message: string }[];
};
Expand Down Expand Up @@ -128,6 +128,11 @@ const fetchVaultV2DetailsCore = async (vaultAddresses: string[], network: Suppor

const response = await morphoGraphqlFetcher<VaultV2ApiResponse>(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;
Expand Down
10 changes: 5 additions & 5 deletions src/data-sources/morpho-api/vaults.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,9 @@ type ApiVault = {
};

type AllVaultsApiResponse = {
data: {
vaults: {
items: ApiVault[];
data?: {
vaults?: {
items?: ApiVault[];
};
};
errors?: { message: string }[];
Expand Down Expand Up @@ -71,8 +71,8 @@ export const fetchAllMorphoVaults = async (): Promise<MorphoVault[]> => {

const response = await morphoGraphqlFetcher<AllVaultsApiResponse>(allVaultsQuery, variables);

if (response.errors && response.errors.length > 0) {
console.error('GraphQL errors:', response.errors);
// Handle NOT_FOUND - return empty array
if (!response) {
return [];
}

Expand Down
9 changes: 2 additions & 7 deletions src/features/positions/components/supplied-markets-detail.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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)}%`}
/>
Expand All @@ -159,10 +157,7 @@ export function SuppliedMarketsDetail({ groupedPosition, showCollateralExposure
>
<span
style={{
color:
collateral.symbol === 'Others'
? OTHER_COLOR
: getCollateralColorFromPalette(collateral.address, pieColors),
color: collateral.symbol === 'Others' ? OTHER_COLOR : getCollateralColorFromPalette(collateral.address, pieColors),
}}
>
Expand Down
6 changes: 5 additions & 1 deletion src/hooks/useUserPositions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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)) {
Expand All @@ -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);
Expand Down Expand Up @@ -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<string, PositionMarket>();
Expand All @@ -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 };
Expand Down