refactor: add backup data source #140
Conversation
|
The latest updates on your projects. Learn more about Vercel for Git ↗︎
|
📝 WalkthroughWalkthroughThis update introduces a new data sourcing system for market and historical data, supporting both Morpho API and subgraph endpoints. Several new modules fetch and transform data from these sources, with types and utilities for subgraph integration. The market and volume chart components are refactored to use unified timeframe handling and improved type safety. The main market content component is updated to use consolidated state and hooks for fetching data. Numerous imports are redirected to new query files, and some obsolete utility and hook files are removed. Types are updated to match the new data structure. Changes
Sequence Diagram(s)sequenceDiagram
participant UI
participant MarketContent
participant useMarketData
participant useMarketHistoricalData
participant MorphoAPI
participant Subgraph
UI->>MarketContent: Render with selectedTimeframe
MarketContent->>useMarketData: Fetch market (uniqueKey, network)
useMarketData->>MorphoAPI: If source is Morpho, fetch market
useMarketData->>Subgraph: If source is Subgraph, fetch market
MarketContent->>useMarketHistoricalData: Fetch historical data (uniqueKey, network, options)
useMarketHistoricalData->>MorphoAPI: If source is Morpho, fetch historical data
useMarketHistoricalData->>Subgraph: If source is Subgraph, fetch historical data
MarketContent->>UI: Render charts with fetched data
Possibly related PRs
Poem
Tip ⚡💬 Agentic Chat (Pro Plan, General Availability)
✨ Finishing Touches
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Actionable comments posted: 7
🔭 Outside diff range comments (4)
src/hooks/useMarketBorrows.ts (1)
40-42:⚠️ Potential issueFix comment vs. pagination limit
The comment says “Limit to 100” butfirstis set to1000. Update the comment or the value to match the intended limit.src/hooks/useMarketLiquidations.ts (1)
37-40:⚠️ Potential issueAdd pagination to liquidations
Unlike borrows and supplies, this query lacksfirst/skipvariables—consider adding them or confirming the default server behavior to avoid unbounded results.src/hooks/useMarketSupplies.ts (1)
40-42:⚠️ Potential issueFix comment vs. pagination limit
The inline note says “Limit to 100” but the code usesfirst: 1000. Align the comment or adjust the limit.src/hooks/useUserRebalancerInfo.ts (1)
19-27: 🛠️ Refactor suggestionAdd response.ok check and error throw
You parseresponse.json()without verifyingresponse.ok. Wrap the fetch in anif (!response.ok) throw new Error(...)to catch HTTP errors early.
🧹 Nitpick comments (26)
src/utils/subgraph-types.ts (4)
4-10: Check SubgraphToken types
Thedecimalsfield is typed asnumber, but subgraph decimals are often strings to preserve precision. Consider using a string alias for all numeric fields.
13-19: ValidateSubgraphOracle.idtype
You’ve typed theidasstring, but if it’s always an on‑chain address you might use the sameAddresstype for consistency.
30-34: Improve protocol network typing
networkandprotocolare raw strings—consider defining a literal union of known networks ('MAINNET'|'BASE'|…) and protocols for stronger type safety.
37-69: Extract numeric string aliases
You have many fields typed asstringrepresenting bigints or decimals. Introducing type aliases liketype BigDecimal = stringandtype BigIntString = stringcan improve readability and catch copy‑paste errors.src/hooks/useMarketBorrows.ts (1)
23-28: Consider consolidating fetch logic
This hook’s pattern repeats across borrows, supplies, and liquidations. Extract a genericuseFetchTransactionshook to reduce duplication.src/hooks/useMarketLiquidations.ts (1)
22-28: Consider consolidating fetch logic
Repeating fetch pattern across hooks can be abstracted into a reusable fetch hook with generics.src/hooks/useMarketSupplies.ts (1)
23-28: Consider consolidating fetch logic
This hook shares structure with borrows and liquidations. A generic data‑fetch hook could DRY up the code.src/data-sources/morpho-api/fetchers.ts (1)
20-29: Consider improving error handling with more details.The error message "Unknown GraphQL error" is generic. Including specific error details in the message would help debugging.
- throw new Error('Unknown GraphQL error from Morpho API'); + throw new Error(`GraphQL error from Morpho API: ${(result as any).errors[0]?.message || 'Unknown error'}`);src/hooks/useLiquidations.ts (2)
4-36: Move query to dedicated file.The GraphQL query is defined directly in this file, while other queries are imported from central files.
Consider moving this query to the centralized query file:
+// In @/graphql/morpho-api-queries.ts +export const liquidationsQuery = ` + query getLiquidations($first: Int, $skip: Int) { + transactions( + where: { type_in: [MarketLiquidation] } + first: $first + skip: $skip + ) { + items { + id + type + data { + ... on MarketLiquidationTransactionData { + market { + id + uniqueKey + } + repaidAssets + } + } + hash + chain { + id + } + } + pageInfo { + countTotal + count + limit + skip + } + } + } +`; +// In this file +import { liquidationsQuery } from '@/graphql/morpho-api-queries'; -const liquidationsQuery = ` - query getLiquidations($first: Int, $skip: Int) { - transactions( - where: { type_in: [MarketLiquidation] } - first: $first - skip: $skip - ) { - items { - id - type - data { - ... on MarketLiquidationTransactionData { - market { - id - uniqueKey - } - repaidAssets - } - } - hash - chain { - id - } - } - pageInfo { - countTotal - count - limit - skip - } - } - } -`;
88-109: Consider using morphoGraphqlFetcher.This hook has direct fetch logic that duplicates the new morphoGraphqlFetcher functionality.
Replace this fetch implementation with the new fetcher:
do { - const response = await fetch(URLS.MORPHO_BLUE_API, { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ - query: liquidationsQuery, - variables: { first: pageSize, skip }, - }), - }); - const result = (await response.json()) as QueryResult; + const result = await morphoGraphqlFetcher<QueryResult>( + liquidationsQuery, + { first: pageSize, skip } + ); const liquidations = result.data.transactions.items; const pageInfo = result.data.transactions.pageInfo;src/data-sources/subgraph/fetchers.ts (1)
1-5: Type inconsistency with morpho-api fetcher.The generic type constraint is different from the morphoGraphqlFetcher, which uses
Record<string, any>.-export const subgraphGraphqlFetcher = async <T extends object>( +export const subgraphGraphqlFetcher = async <T extends Record<string, any>>( apiUrl: string, // Subgraph URL can vary query: string, variables: Record<string, unknown>, ): Promise<T> => {src/contexts/MarketsContext.tsx (1)
63-70: Direct API URL hardcoding.The component makes API requests directly using a hardcoded URL instead of using the new fetcher.
- const marketsResponse = await fetch('https://blue-api.morpho.org/graphql', { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ - query: marketsQuery, - variables: { first: 1000, where: { whitelisted: true } }, - }), - }); - - const marketsResult = (await marketsResponse.json()) as MarketResponse; + const marketsResult = await morphoGraphqlFetcher<MarketResponse>( + marketsQuery, + { first: 1000, where: { whitelisted: true } } + );Also, import the required function:
+import { morphoGraphqlFetcher } from '@/data-sources/morpho-api/fetchers';src/config/dataSources.ts (1)
16-25: Historical data source implementation is limitedUnlike
getMarketDataSourcefunction, there are no commented-out cases for specific networks. Consider applying the same pattern if subgraph support for historical data is planned.export const getHistoricalDataSource = (network: SupportedNetworks): 'morpho' | 'subgraph' => { switch (network) { + // case SupportedNetworks.Mainnet: + // case SupportedNetworks.Base: + // return 'subgraph'; default: return 'morpho'; } };src/utils/subgraph-urls.ts (1)
15-18: Remove outdated TODO commentThe TODO comment suggests replacing a placeholder ID, but the code already appears to have a valid subgraph ID.
-// TODO: Replace 'YOUR_MAINNET_SUBGRAPH_ID' with the actual Mainnet Subgraph ID +// Mainnet subgraph endpoint const mainnetSubgraphUrl = apiKey ? `https://gateway.thegraph.com/api/${apiKey}/subgraphs/id/8Lz789DP5VKLXumTMTgygjU2xtuzx8AhbaacgN5PYCAs` : undefined;src/hooks/useMarketData.ts (2)
26-26: Remove or conditionalize debug logProduction code shouldn't include debug logging statements. Consider removing this or conditionalizing it based on environment.
- console.log(`Fetching market data for ${uniqueKey} on ${network} via ${dataSource}`); + if (process.env.NODE_ENV === 'development') { + console.log(`Fetching market data for ${uniqueKey} on ${network} via ${dataSource}`); + }
29-35: Handle unknown data source within try blockThe code has a fallback for unknown data sources after the try/catch, but it would be more consistent to handle this case within the try block.
try { if (dataSource === 'morpho') { return await fetchMorphoMarket(uniqueKey, network); } else if (dataSource === 'subgraph') { // fetchSubgraphMarket already handles potential null return return await fetchSubgraphMarket(uniqueKey, network); + } else { + console.warn('Unknown market data source determined'); + return null; } } catch (fetchError) { console.error(`Failed to fetch market data via ${dataSource}:`, fetchError); return null; // Return null on fetch error } - // Fallback if dataSource logic is somehow incorrect - console.warn('Unknown market data source determined'); - return null;src/data-sources/morpho-api/market.ts (2)
7-8: Remove implementation commentsThese comments about removed or moved code should be removed before the final PR merge.
-// Removed historical types (MarketRates, MarketVolumes, etc.) -// Moved HistoricalDataResult to historical.ts
27-39: Improve error message specificityAdd the unique key to the error message when market data is not found to make debugging easier.
if (!response.data || !response.data.marketByUniqueKey) { - throw new Error('Market data not found in Morpho API response'); + throw new Error(`Market data not found in Morpho API response for key: ${uniqueKey}`); }src/hooks/useMarketHistoricalData.ts (2)
27-59: Remove console logs before productionThe implementation logic looks solid, but there are several console.log statements that should be removed before deploying to production.
- console.log('Historical data prerequisites not met or source unavailable.', { - uniqueKey, - network, - options, - dataSource, - }); + // Prerequisites not met - console.log(`Fetching historical data for ${uniqueKey} on ${network} via ${dataSource}`); + // Fetching data - console.log('res morpho', res); + // Data fetched from Morpho - console.log('res', res); + // Data fetched from subgraph
61-68: Return object uses shorthand notationConsider using object shorthand notation for cleaner code since property names match variable names.
- return { - data: data, - isLoading: isLoading, - error: error, - refetch: refetch, - dataSource: dataSource, - }; + return { data, isLoading, error, refetch, dataSource };src/data-sources/subgraph/market.ts (1)
139-156: Consider retrying failed requestsThe function doesn't include retry logic for failed network requests, which could be useful for intermittent failures.
Consider adding retry logic for transient network issues by wrapping the fetch call in a retry mechanism.
src/data-sources/morpho-api/historical.ts (1)
64-73: Too‑verbose console logs – gate behind DEBUG flagDumping the full GraphQL response on every request will flood the console and, in production builds, bloat bundle size.
Wrap theseconsole.logcalls in an env / debug guard or remove them.-console.log('[fetchMorphoMarketHistoricalData] Raw API Response:', …) -… +if (process.env.NEXT_PUBLIC_DEBUG === 'true') { + // eslint-disable-next-line no-console + console.debug('[Morpho‑historical] resp', response); +}app/market/[chainId]/[marketid]/content.tsx (1)
44-56: Redundantdefaultclause
case '7d'falls straight through to the same block asdefault, making the latter useless (Biome warning).
Either remove the explicit'7d'case or dropdefault:- case '7d': - default: + default: // treat unknown or '7d' as 7‑day🧰 Tools
🪛 Biome (1.9.4)
[error] 53-53: Useless case clause.
because the default clause is present:
Unsafe fix: Remove the useless case.
(lint/complexity/noUselessSwitchCase)
src/graphql/morpho-subgraph-queries.ts (1)
84-84: Remove or expand the TODO comment.This comment doesn't provide actionable information. Either remove it or expand with specific queries that might be needed.
-// Add other queries as needed, e.g., for user positions based on subgraph schema +// TODO: Add user position queries once we implement position tracking with the subgraphapp/market/[chainId]/[marketid]/VolumeChart.tsx (1)
92-94: Consider extracting the large number check to a constant.The 100B threshold is hardcoded without explanation. Extract this to a named constant for better readability.
+const MAX_DISPLAY_USD_VALUE = 100_000_000_000; // 100B USD, filter out likely erroneous data points // Check if any timestamps has USD value exceeds 100B -if (historicalData.supplyAssetsUsd[index].y >= 100_000_000_000) { +if (historicalData.supplyAssetsUsd[index].y >= MAX_DISPLAY_USD_VALUE) { return null; }app/market/[chainId]/[marketid]/RateChart.tsx (1)
176-182: Consider reusing the formatPercentage function in tooltip.The tooltip formatter duplicates the percentage formatting logic already defined in formatPercentage. You should reuse this function for consistency.
<Tooltip labelFormatter={(unixTime) => new Date(unixTime * 1000).toLocaleString()} - formatter={(value: number) => `${(value * 100).toFixed(2)}%`} + formatter={(value: number) => formatPercentage(value)} contentStyle={{ backgroundColor: 'var(--color-background)', }} />
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (29)
app/market/[chainId]/[marketid]/RateChart.tsx(5 hunks)app/market/[chainId]/[marketid]/VolumeChart.tsx(8 hunks)app/market/[chainId]/[marketid]/content.tsx(6 hunks)src/config/dataSources.ts(1 hunks)src/contexts/MarketsContext.tsx(4 hunks)src/data-sources/morpho-api/fetchers.ts(1 hunks)src/data-sources/morpho-api/historical.ts(1 hunks)src/data-sources/morpho-api/market.ts(1 hunks)src/data-sources/subgraph/fetchers.ts(1 hunks)src/data-sources/subgraph/historical.ts(1 hunks)src/data-sources/subgraph/market.ts(1 hunks)src/graphql/morpho-api-queries.ts(1 hunks)src/graphql/morpho-subgraph-queries.ts(1 hunks)src/hooks/useLiquidations.ts(4 hunks)src/hooks/useMarket.ts(0 hunks)src/hooks/useMarketBorrows.ts(1 hunks)src/hooks/useMarketData.ts(1 hunks)src/hooks/useMarketHistoricalData.ts(1 hunks)src/hooks/useMarketLiquidations.ts(1 hunks)src/hooks/useMarketSupplies.ts(1 hunks)src/hooks/useUserPosition.ts(2 hunks)src/hooks/useUserPositions.ts(1 hunks)src/hooks/useUserRebalancerInfo.ts(1 hunks)src/hooks/useUserTransactions.ts(1 hunks)src/services/statsService.ts(1 hunks)src/utils/morpho.ts(0 hunks)src/utils/subgraph-types.ts(1 hunks)src/utils/subgraph-urls.ts(1 hunks)src/utils/types.ts(3 hunks)
💤 Files with no reviewable changes (2)
- src/utils/morpho.ts
- src/hooks/useMarket.ts
🧰 Additional context used
🧬 Code Graph Analysis (10)
src/utils/subgraph-urls.ts (1)
src/utils/networks.ts (1)
SupportedNetworks(33-33)
src/hooks/useMarketData.ts (5)
src/utils/networks.ts (1)
SupportedNetworks(33-33)src/config/dataSources.ts (1)
getMarketDataSource(6-14)src/utils/types.ts (1)
Market(261-312)src/data-sources/morpho-api/market.ts (1)
fetchMorphoMarket(27-39)src/data-sources/subgraph/market.ts (1)
fetchSubgraphMarket(129-156)
src/data-sources/morpho-api/fetchers.ts (1)
src/utils/urls.ts (1)
URLS(1-5)
src/hooks/useMarketHistoricalData.ts (4)
src/utils/networks.ts (1)
SupportedNetworks(33-33)src/data-sources/morpho-api/historical.ts (3)
TimeseriesOptions(8-8)HistoricalDataSuccessResult(40-43)fetchMorphoMarketHistoricalData(47-117)src/config/dataSources.ts (1)
getHistoricalDataSource(20-25)src/data-sources/subgraph/historical.ts (1)
fetchSubgraphMarketHistoricalData(104-151)
src/data-sources/morpho-api/market.ts (5)
src/utils/types.ts (1)
Market(261-312)src/utils/warnings.ts (1)
getMarketWarningsWithDetail(95-131)src/utils/networks.ts (1)
SupportedNetworks(33-33)src/data-sources/morpho-api/fetchers.ts (1)
morphoGraphqlFetcher(4-32)src/graphql/morpho-api-queries.ts (1)
marketDetailQuery(181-188)
src/data-sources/subgraph/market.ts (5)
src/utils/subgraph-types.ts (3)
SubgraphMarket(37-69)SubgraphToken(4-10)SubgraphMarketQueryResponse(80-85)src/utils/networks.ts (1)
SupportedNetworks(33-33)src/utils/types.ts (3)
Market(261-312)MorphoChainlinkOracleData(253-258)WarningWithDetail(80-85)src/utils/subgraph-urls.ts (1)
getSubgraphUrl(27-29)src/data-sources/subgraph/fetchers.ts (1)
subgraphGraphqlFetcher(1-31)
src/config/dataSources.ts (1)
src/utils/networks.ts (1)
SupportedNetworks(33-33)
app/market/[chainId]/[marketid]/content.tsx (4)
src/utils/types.ts (1)
TimeseriesOptions(319-323)src/hooks/useMarketData.ts (1)
useMarketData(8-59)src/hooks/useMarketHistoricalData.ts (1)
useMarketHistoricalData(11-68)src/components/common/Spinner.tsx (1)
Spinner(24-57)
src/graphql/morpho-subgraph-queries.ts (1)
src/graphql/morpho-api-queries.ts (2)
marketFragment(17-119)marketsQuery(121-138)
src/data-sources/subgraph/historical.ts (4)
src/data-sources/morpho-api/historical.ts (5)
HistoricalDataSuccessResult(40-43)MarketRates(10-15)TimeseriesDataPoint(8-8)MarketVolumes(17-24)TimeseriesOptions(8-8)src/utils/subgraph-urls.ts (1)
getSubgraphUrl(27-29)src/data-sources/subgraph/fetchers.ts (1)
subgraphGraphqlFetcher(1-31)src/graphql/morpho-subgraph-queries.ts (1)
marketHourlySnapshotsQuery(129-146)
🪛 Biome (1.9.4)
app/market/[chainId]/[marketid]/content.tsx
[error] 53-53: Useless case clause.
because the default clause is present:
Unsafe fix: Remove the useless case.
(lint/complexity/noUselessSwitchCase)
🔇 Additional comments (65)
src/utils/subgraph-types.ts (2)
22-27: Confirm interest ratetypeunion
The'FIXED'option isn’t used in Morpho’s rate settings (onlySTABLE/VARIABLE). If the subgraph never returnsFIXED, drop it to tighten the union.
72-77: Approve response wrapper types
TheSubgraphMarketsQueryResponseandSubgraphMarketQueryResponsematch the expected GraphQL structure. Good job including optionalerrors.Also applies to: 80-85
src/hooks/useMarketBorrows.ts (1)
2-2: Approve updated import path
Switching to@/graphql/morpho-api-queriesaligns with the new Morpho API module.src/hooks/useMarketLiquidations.ts (1)
2-2: Approve updated import path
The@/graphql/morpho-api-queriesimport correctly syncs with the Morpho API queries.src/hooks/useMarketSupplies.ts (1)
2-2: Approve updated import path
Consistent switch to@/graphql/morpho-api-queriesis correct.src/hooks/useUserRebalancerInfo.ts (1)
2-2: Approve updated import path
Switching to@/graphql/morpho-api-queriesfits the overall query migration.src/services/statsService.ts (1)
2-2: Import source updated for stats queries.The import source for the GraphQL queries has been updated to use the Monarch-specific stats queries module.
src/hooks/useUserTransactions.ts (1)
2-2: Updated import to use Morpho API queries.This change aligns with the migration to Morpho-specific API query modules across the codebase.
src/hooks/useUserPositions.ts (1)
6-6: Updated import to use Morpho API queries.Import now points to the dedicated Morpho API queries module, consistent with other hook changes.
src/hooks/useUserPosition.ts (2)
3-3: Updated import to use Morpho API queries.Import source updated to the Morpho-specific API queries module.
63-63: Simplified state assignment.The position state assignment has been streamlined to directly use the currentSnapshot without additional processing.
src/data-sources/morpho-api/fetchers.ts (1)
1-32: Well-structured generic fetcher for GraphQL queries.The implementation is clean and handles errors appropriately. The type safety with generics is good for ensuring correct data handling.
src/hooks/useLiquidations.ts (5)
73-73: Appropriate state variable rename.Changing from IDs to unique keys is consistent with the broader refactoring.
83-83: Concise local set declaration.Good naming that matches its usage.
103-103: Updated property access is correct.Now using market.uniqueKey instead of market.id as identifier.
111-111: State updater renamed consistently.Correctly using the new state variable name.
128-128: Updated returned property name.Return value is updated to match new naming.
src/data-sources/subgraph/fetchers.ts (2)
1-31: Clean implementation of subgraph fetcher.The fetcher correctly handles API interactions and error cases with appropriate error messages.
12-15: Better error logging than morpho-api fetcher.This implementation includes status details in error logs, which is more helpful than the morpho-api version.
src/contexts/MarketsContext.tsx (4)
12-12: Updated import path is correct.Changed to use the new location of marketsQuery.
47-52: Property name update is consistent.Updated destructured property from useLiquidations to match the hook's changes.
84-84: Updated marker check logic.Now correctly checking uniqueKey instead of id.
104-104: Updated dependency correctly.The dependency array now references the renamed state variable.
src/hooks/useMarketData.ts (1)
17-50: Query implementation is thorough and robustThe query implementation handles errors gracefully, includes proper caching with staleTime, and enables the query only when all prerequisites are met. Good job!
src/hooks/useMarketHistoricalData.ts (3)
1-7: Clean imports organized by package and local modulesThe imports are well organized, starting with external libraries and then local modules. This makes it easier to understand dependencies.
11-24: Query key structure ensures proper cachingThe hook parameters are well-typed with undefined handling, and the query key includes all variables that could affect the result.
25-26: Straightforward data source selectionData source determination is concise and follows the pattern of returning null when network is undefined.
src/data-sources/subgraph/market.ts (5)
1-8: Imports correctly structuredImports are properly organized and all necessary dependencies are included.
9-26: Well-implemented safe parsing helpersThese utility functions properly handle null/undefined values and parsing errors, providing good fallbacks.
28-58: Handling nullable subgraph fields correctlyThe function properly handles potentially missing fields by providing default values, which is essential when working with external APIs.
128-138: Good error handling for missing subgraph URLThe function properly checks for missing URL and throws a meaningful error message.
72-74:⚠️ Potential issueVerify rates array exists before accessing
The code assumes that subgraphMarket.rates is an array, but if it's undefined, the find operation will throw an error.
- const supplyApy = Number(subgraphMarket.rates?.find((r) => r.side === 'LENDER')?.rate ?? 0); - const borrowApy = Number(subgraphMarket.rates?.find((r) => r.side === 'BORROWER')?.rate ?? 0); + const supplyApy = Number(subgraphMarket.rates ? subgraphMarket.rates.find((r) => r.side === 'LENDER')?.rate ?? 0 : 0); + const borrowApy = Number(subgraphMarket.rates ? subgraphMarket.rates.find((r) => r.side === 'BORROWER')?.rate ?? 0 : 0);Likely an incorrect or invalid review comment.
src/graphql/morpho-api-queries.ts (3)
1-2: Helpful API reference commentAdding the API reference URL is useful for developers who need to understand the API schema.
32-45: Clean and focused token fields selectionThe removal of
priceUsdaligns with the type changes in types.ts, maintaining consistency.
46-63: Streamlined state fieldsThe state fields now focus only on essential data, which helps reduce payload size.
src/utils/types.ts (3)
308-312: Oracle data structure improvementThe type structure is now cleaner and more focused, with correct nesting of oracle data.
322-322: Narrowed interval type increases type safetyRestricting the interval options to specific values improves type safety and prevents potential errors.
341-343: Renamed type reflects its purpose betterRenaming
MarketDetailtoHistoricalDatabetter represents what the type contains.src/data-sources/morpho-api/historical.ts (1)
75-82: “Essential data” check is brittleOnly
supplyApyis treated as mandatory. If the API ever omits that field (e.g. empty array) the whole result becomesnull, even though the remaining series could be useful.
Consider checking for any non‑empty timeseries instead of a single field, or let the caller decide what is “usable”.src/graphql/morpho-subgraph-queries.ts (7)
1-9: Well-defined token fragment.The TokenFields fragment captures essential token information, including price data.
11-19: Good structure for oracle fragment.This fragment properly encapsulates oracle information needed for market data.
21-73: Market fragment provides comprehensive data schema.The fragment includes all necessary market fields with proper nesting and fragment composition. Comments on lines 26 and 30 helpfully map fields to application terminology.
75-82: Markets query looks good.This query properly reuses the market fragment and includes sorting by TVL, which is a sensible default.
86-93: Single market query looks correct.The query properly takes an ID parameter and reuses the market fragment.
97-127: Good historical data fragment.This fragment captures the time series data needed for charts, with helpful comments about limitations at lines 124-125.
129-146: Historical data query is well structured.The query handles time ranges properly and includes the necessary fragments. The query limit is explicit with a comment explaining the subgraph constraint.
app/market/[chainId]/[marketid]/VolumeChart.tsx (9)
19-21: Updated imports to match new data structure.The imports now reference the correct types from the new data sources.
23-32: Props updated for unified timeframe handling.Props now accept standardized timeframe structures and delegate state management to parent components, improving consistency.
34-43: Component parameters aligned with new props structure.Properly destructures the new props.
52-58: Time formatting now uses the range object.Uses the provided timestamp range for formatting decisions rather than hardcoded values.
74-76: Improved type safety for data points.Better handling of potentially undefined values in the data arrays.
141-142: Added type annotation to reduce accumulator.Explicitly typing the accumulator improves code clarity and prevents potential type errors.
157-160: Standardized timeframe option values.Changed to shorter format ('1d' instead of '1day') for consistency across components.
180-184: Updated ButtonGroup to use the new timeframe props.Properly delegates timeframe state management to parent component.
331-332: Updated timeframe display reference.Now correctly references the new selectedTimeframe prop.
app/market/[chainId]/[marketid]/RateChart.tsx (10)
19-20: Updated imports to match new data structure.Imports now reference the correct types from the new data sources.
22-29: Props updated for unified timeframe handling.Props now accept standardized timeframe structures and delegate state management to parent components.
31-38: Component parameters aligned with new props structure.Properly destructures the new props.
66-69: Added null check to prevent errors with empty data.Ensures the function handles empty arrays gracefully.
76-83: Improved safety for null data handling.Better handling of potentially undefined or empty historicalData arrays.
90-97: Added null and empty array checks.Similar safety improvements for utilization data.
99-105: Time formatting now uses the range object.Uses the provided timestamp range for formatting decisions rather than hardcoded values.
107-111: Standardized timeframe option values.Changed to shorter format ('1d' instead of '1day') for consistency.
117-124: Updated ButtonGroup to use the new timeframe props.Properly delegates timeframe state management to parent component.
279-280: Updated timeframe display reference.Now correctly references the new selectedTimeframe prop.
| const liquidityAssets = (BigInt(supplyAssets) - BigInt(borrowAssets)).toString(); | ||
| const liquidityAssetsUsd = | ||
| safeParseFloat(totalDepositBalanceUSD) - safeParseFloat(totalBorrowBalanceUSD); |
There was a problem hiding this comment.
Ensure safe BigInt operations
Using BigInt directly on potentially undefined values can cause runtime errors.
- const liquidityAssets = (BigInt(supplyAssets) - BigInt(borrowAssets)).toString();
+ const supplyAssetsBigInt = BigInt(supplyAssets || '0');
+ const borrowAssetsBigInt = BigInt(borrowAssets || '0');
+ const liquidityAssets = (supplyAssetsBigInt - borrowAssetsBigInt).toString();📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const liquidityAssets = (BigInt(supplyAssets) - BigInt(borrowAssets)).toString(); | |
| const liquidityAssetsUsd = | |
| safeParseFloat(totalDepositBalanceUSD) - safeParseFloat(totalBorrowBalanceUSD); | |
| const supplyAssetsBigInt = BigInt(supplyAssets || '0'); | |
| const borrowAssetsBigInt = BigInt(borrowAssets || '0'); | |
| const liquidityAssets = (supplyAssetsBigInt - borrowAssetsBigInt).toString(); | |
| const liquidityAssetsUsd = | |
| safeParseFloat(totalDepositBalanceUSD) - safeParseFloat(totalBorrowBalanceUSD); |
| utilization: utilization, | ||
| supplyApy: supplyApy, | ||
| borrowApy: borrowApy, | ||
| fee: safeParseFloat(fee) / 10000, // Subgraph fee is likely basis points (needs verification) |
There was a problem hiding this comment.
💡 Verification agent
🧩 Analysis chain
Verify fee conversion assumption
The comment indicates uncertainty about fee conversion. This should be verified to ensure correct calculations.
🌐 Web query:
What is the format of fee values in Morpho subgraph data? Are they in basis points?
💡 Result:
Format of Fee Values in Morpho Subgraph Data
The Morpho subgraph provides detailed protocol data via GraphQL, but the specific format for fee values is not explicitly described in the official documentation or open-source subgraph repositories included in the search results. However, some inferences and conventions in DeFi data formatting, as well as observed examples and related context, can clarify how fee values typically appear.
Interest Rates and Fee Representation
- Interest rates in Morpho subgraphs are presented as decimal values representing annualized rates. For example, a borrow APY might be given as
"rate": "0.205574598544752", which corresponds to a 20.56% annual rate[9]. - There is no evidence in the documentation or code examples that Morpho subgraph data expresses fee values in basis points (where 1 basis point = 0.01% or 0.0001 as a decimal value).
- Instead, rates and likely fee values are stored as a human-readable decimal fraction (e.g.,
0.1for 10%,0.0015for 0.15%) rather than an integer representing basis points[9].
Field Naming and Raw Values
- Morpho subgraph entities sometimes include “internal” data fields, which may not be human-readable and are meant for internal calculations. These fields are usually prefixed with an underscore and may use different scaling or representations[3].
- For user-facing or documented fields relating to rates and protocol fees, the prevailing pattern is to use decimal fractions, not basis points[9].
Example: Interest Rate Formatting
"rate": "0.19011573722135125872777079486656"
This value should be interpreted as a decimal fraction, where0.19...represents a 19% annualized rate, not 1,900 basis points[9].
Points and Precision
- In the context of "points" for Morpho Blue and MetaMorpho, precision is increased by adding decimals to represent partial shares, but this is a separate mechanism not directly related to fee value formatting[6].
Summary Table
| Value Type | Example | Format | Unit |
|---|---|---|---|
| Interest Rate | 0.2055745985 | Decimal fraction | Annualized rate |
| Fee (inferred) | 0.01 | Decimal fraction | Percentage (1%) |
| Basis Points | not used |
Conclusion
Fee values in Morpho subgraph data are expressed as decimal fractions (e.g., 0.01 for 1%) and not in basis points. This matches the pattern for other rate fields in Morpho subgraphs and follows standard DeFi GraphQL conventions[9].
If you require further confirmation for a specific fee-related field, checking the field's raw value or documentation for that subgraph entity is recommended. However, all available public evidence strongly indicates decimals, not basis points, are the default.
Citations:
- 1: https://docs.morpho.org/apis/subgraphs/overview
- 2: https://developers.morpho.org/subgraphs/subgraphs-documentation
- 3: https://github.com/morpho-org/morpho-optimizers-subgraph
- 4: https://www.npmjs.com/package/@morpholabs/morpho-js
- 5: https://www.npmjs.com/package/@morpho-labs/morpho-js?activeTab=dependencies
- 6: https://github.com/morpho-org/blue-points-subgraph
- 7: https://docs.morpho.org/morpho/contracts/morpho
- 8: https://github.com/morpho-org/morpho-aave-v3-sdk
- 9: https://docs.morpho.org/apis/subgraphs/morpho/
- 10: https://pmc.ncbi.nlm.nih.gov/articles/PMC8567831/
Adjust fee parsing: remove basis‑points scaling
Web data shows Morpho fees are decimal fractions (e.g. 0.01 for 1%), not basis points. Dividing by 10 000 will understate the fee.
Please update:
• File: src/data-sources/subgraph/market.ts (around line 103)
• Replace:
- fee: safeParseFloat(fee) / 10000, // was treating as bps
+ fee: safeParseFloat(fee), // fee is already a decimal fraction📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| fee: safeParseFloat(fee) / 10000, // Subgraph fee is likely basis points (needs verification) | |
| // was treating fee as bps, but subgraph already returns a decimal fraction | |
| fee: safeParseFloat(fee), // fee is already a decimal fraction |
| const NOW = Math.floor(Date.now() / 1000); | ||
| const WEEK_IN_SECONDS = 7 * 24 * 60 * 60; | ||
| const DAY_IN_SECONDS = 24 * 60 * 60; | ||
| const WEEK_IN_SECONDS = 7 * DAY_IN_SECONDS; | ||
|
|
||
| // Helper to calculate time range based on timeframe string | ||
| const calculateTimeRange = (timeframe: '1d' | '7d' | '30d'): TimeseriesOptions => { | ||
| const endTimestamp = NOW; | ||
| let startTimestamp; | ||
| let interval: TimeseriesOptions['interval'] = 'HOUR'; | ||
| switch (timeframe) { |
There was a problem hiding this comment.
🛠️ Refactor suggestion
NOW is static – data goes stale after first render
NOW is evaluated at module load, so every later timeframe change still uses the old timestamp.
Compute endTimestamp inside calculateTimeRange instead:
-const NOW = Math.floor(Date.now() / 1000);
…
-const calculateTimeRange = (timeframe: '1d' | '7d' | '30d'): TimeseriesOptions => {
- const endTimestamp = NOW;
+const calculateTimeRange = (timeframe: '1d' | '7d' | '30d'): TimeseriesOptions => {
+ const endTimestamp = Math.floor(Date.now() / 1000);📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const NOW = Math.floor(Date.now() / 1000); | |
| const WEEK_IN_SECONDS = 7 * 24 * 60 * 60; | |
| const DAY_IN_SECONDS = 24 * 60 * 60; | |
| const WEEK_IN_SECONDS = 7 * DAY_IN_SECONDS; | |
| // Helper to calculate time range based on timeframe string | |
| const calculateTimeRange = (timeframe: '1d' | '7d' | '30d'): TimeseriesOptions => { | |
| const endTimestamp = NOW; | |
| let startTimestamp; | |
| let interval: TimeseriesOptions['interval'] = 'HOUR'; | |
| switch (timeframe) { | |
| const DAY_IN_SECONDS = 24 * 60 * 60; | |
| const WEEK_IN_SECONDS = 7 * DAY_IN_SECONDS; | |
| // Helper to calculate time range based on timeframe string | |
| const calculateTimeRange = (timeframe: '1d' | '7d' | '30d'): TimeseriesOptions => { | |
| const endTimestamp = Math.floor(Date.now() / 1000); | |
| let startTimestamp; | |
| let interval: TimeseriesOptions['interval'] = 'HOUR'; | |
| switch (timeframe) { |
| const supplyNative = BigInt(snapshot.inputTokenBalance ?? '0'); | ||
| const borrowNative = BigInt(snapshot.variableBorrowedTokenBalance ?? '0'); | ||
| const liquidityNative = supplyNative - borrowNative; | ||
|
|
||
| volumes.supplyAssetsUsd.push({ x: timestamp, y: 0 }); | ||
| volumes.borrowAssetsUsd.push({ x: timestamp, y: 0 }); | ||
| volumes.liquidityAssetsUsd.push({ x: timestamp, y: 0 }); | ||
|
|
||
| volumes.supplyAssets.push({ x: timestamp, y: Number(supplyNative) }); | ||
| volumes.borrowAssets.push({ x: timestamp, y: Number(borrowNative) }); | ||
| volumes.liquidityAssets.push({ x: timestamp, y: Number(liquidityNative) }); | ||
| }); |
There was a problem hiding this comment.
BigInt → Number may overflow
Number(supplyNative) (and similar) loses precision once values exceed 2^53‑1. Large markets will hit this quickly.
- volumes.supplyAssets.push({ x: timestamp, y: Number(supplyNative) });
+ volumes.supplyAssets.push({ x: timestamp, y: parseFloat(supplyNative.toString()) });Or keep the value as string/BigInt and convert only in the chart layer.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const supplyNative = BigInt(snapshot.inputTokenBalance ?? '0'); | |
| const borrowNative = BigInt(snapshot.variableBorrowedTokenBalance ?? '0'); | |
| const liquidityNative = supplyNative - borrowNative; | |
| volumes.supplyAssetsUsd.push({ x: timestamp, y: 0 }); | |
| volumes.borrowAssetsUsd.push({ x: timestamp, y: 0 }); | |
| volumes.liquidityAssetsUsd.push({ x: timestamp, y: 0 }); | |
| volumes.supplyAssets.push({ x: timestamp, y: Number(supplyNative) }); | |
| volumes.borrowAssets.push({ x: timestamp, y: Number(borrowNative) }); | |
| volumes.liquidityAssets.push({ x: timestamp, y: Number(liquidityNative) }); | |
| }); | |
| const supplyNative = BigInt(snapshot.inputTokenBalance ?? '0'); | |
| const borrowNative = BigInt(snapshot.variableBorrowedTokenBalance ?? '0'); | |
| const liquidityNative = supplyNative - borrowNative; | |
| volumes.supplyAssetsUsd.push({ x: timestamp, y: 0 }); | |
| volumes.borrowAssetsUsd.push({ x: timestamp, y: 0 }); | |
| volumes.liquidityAssetsUsd.push({ x: timestamp, y: 0 }); | |
| - volumes.supplyAssets.push({ x: timestamp, y: Number(supplyNative) }); | |
| + volumes.supplyAssets.push({ x: timestamp, y: parseFloat(supplyNative.toString()) }); | |
| volumes.borrowAssets.push({ x: timestamp, y: Number(borrowNative) }); | |
| volumes.liquidityAssets.push({ x: timestamp, y: Number(liquidityNative) }); | |
| }); |
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (2)
app/market/[chainId]/[marketid]/RateChart.tsx (2)
45-55: Consider memoizing chart data calculation.The
getChartDatafunction rebuilds the chart data on every render. Consider using React'suseMemoto cache this calculation and prevent unnecessary work.- const getChartData = () => { + const chartData = React.useMemo(() => { if (!historicalData) return []; const { supplyApy, borrowApy, rateAtUTarget } = historicalData; return supplyApy.map((point: TimeseriesDataPoint, index: number) => ({ x: point.x, supplyApy: point.y, borrowApy: borrowApy[index]?.y || 0, rateAtUTarget: rateAtUTarget[index]?.y || 0, })); - }; + }, [historicalData]);
107-111: Define timeframe values as constants.Using magic strings for timeframe values ('1d', '7d', '30d') could lead to typos. Consider defining these as constants in a separate file.
+// In a constants file +export const TIMEFRAMES = { + ONE_DAY: '1d', + ONE_WEEK: '7d', + ONE_MONTH: '30d', +} as const; + +export type Timeframe = typeof TIMEFRAMES[keyof typeof TIMEFRAMES]; // Then in this file const timeframeOptions = [ - { key: '1d', label: '1D', value: '1d' }, - { key: '7d', label: '7D', value: '7d' }, - { key: '30d', label: '30D', value: '30d' }, + { key: TIMEFRAMES.ONE_DAY, label: '1D', value: TIMEFRAMES.ONE_DAY }, + { key: TIMEFRAMES.ONE_WEEK, label: '7D', value: TIMEFRAMES.ONE_WEEK }, + { key: TIMEFRAMES.ONE_MONTH, label: '30D', value: TIMEFRAMES.ONE_MONTH }, ];
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (6)
app/market/[chainId]/[marketid]/RateChart.tsx(5 hunks)app/market/[chainId]/[marketid]/VolumeChart.tsx(8 hunks)src/data-sources/morpho-api/historical.ts(1 hunks)src/data-sources/morpho-api/market.ts(1 hunks)src/data-sources/subgraph/historical.ts(1 hunks)src/utils/types.ts(3 hunks)
🚧 Files skipped from review as they are similar to previous changes (5)
- src/data-sources/morpho-api/market.ts
- src/data-sources/subgraph/historical.ts
- src/data-sources/morpho-api/historical.ts
- src/utils/types.ts
- app/market/[chainId]/[marketid]/VolumeChart.tsx
🧰 Additional context used
🧬 Code Graph Analysis (1)
app/market/[chainId]/[marketid]/RateChart.tsx (3)
src/utils/types.ts (4)
MarketRates(326-331)Market(261-312)TimeseriesOptions(319-323)TimeseriesDataPoint(314-317)src/data-sources/morpho-api/historical.ts (2)
TimeseriesOptions(14-14)TimeseriesDataPoint(14-14)src/components/ButtonGroup.tsx (1)
ButtonGroup(51-97)
🔇 Additional comments (10)
app/market/[chainId]/[marketid]/RateChart.tsx (10)
3-3: Simplified imports with better type specificity.Updated imports now directly reference
MarketRatestype, which aligns with the new data source refactoring.Also applies to: 19-20
22-29: Props interface improved for unified timeframe handling.The component now uses a consistent timeframe format ('1d'|'7d'|'30d') and a single callback handler, which simplifies the API and aligns with other components.
31-38: Clean prop destructuring with updated parameter names.Updated parameter names match the interface changes, maintaining code consistency.
63-69: Added null safety check for empty arrays.Good defensive programming - checking for empty arrays before reduction prevents potential runtime errors.
75-83: Improved typesafety in rate calculation.Added null check for empty data and explicitly typed reducer parameters for better type safety.
89-97: Enhanced null checking for utilization rate calculation.Similar improvements to type safety with explicit null checking and typed reducer parameters.
99-105: Updated time formatting logic for consistency.Now uses
selectedTimeRangeinstead of the removedrateTimeRangefor determining timestamp formatting.
107-111: Simplified timeframe options with shorter keys.Updated timeframe option values from longer format ('1day') to shorter format ('1d'), improving consistency.
117-123: Streamlined timeframe selection handler.The ButtonGroup now directly calls the passed handler with proper type casting, eliminating an unnecessary wrapper function.
276-280: Updated timeframe display in UI.The historical averages section now correctly references the updated timeframe format.
Summary by CodeRabbit
New Features
Improvements
Bug Fixes
Refactor
Chores