Skip to content
Closed
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
87 changes: 60 additions & 27 deletions app/[locale]/layer-2/networks/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,68 +6,98 @@ import {
} from "next-intl/server"

import type { CommitHistory, Lang, PageParams } from "@/lib/types"
import type { GrowThePieData } from "@/lib/types"
import type { MetricReturnData } from "@/lib/types"

import I18nProvider from "@/components/I18nProvider"

import { getAppPageContributorInfo } from "@/lib/utils/contributors"
import { dataLoader } from "@/lib/utils/data/dataLoader"
import { getMetadata } from "@/lib/utils/metadata"
import { networkMaturity } from "@/lib/utils/networkMaturity"
import { getRequiredNamespacesForPage } from "@/lib/utils/translations"

import { ethereumNetworkData, layer2Data } from "@/data/networks/networks"
import { walletsData } from "@/data/wallets/wallet-data"
import { FETCH_ETHEREUM_MARKETCAP_TASK_ID } from "@/data-layer/api/fetchEthereumMarketcap"
import { FETCH_GROW_THE_PIE_TASK_ID } from "@/data-layer/api/fetchGrowThePie"
import { FETCH_GROW_THE_PIE_BLOCKSPACE_TASK_ID } from "@/data-layer/api/fetchGrowThePieBlockspace"
import { FETCH_GROW_THE_PIE_MASTER_TASK_ID } from "@/data-layer/api/fetchGrowThePieMaster"
import { FETCH_L2BEAT_TASK_ID } from "@/data-layer/api/fetchL2beat"
import { getCachedData } from "@/data-layer/storage/cachedGetter"

import { BASE_TIME_UNIT } from "@/lib/constants"

import Layer2Networks from "./_components/networks"
import Layer2NetworksPageJsonLD from "./page-jsonld"

import { fetchEthereumMarketcap } from "@/lib/api/fetchEthereumMarketcap"
import { fetchGrowThePie } from "@/lib/api/fetchGrowThePie"
import { fetchGrowThePieBlockspace } from "@/lib/api/fetchGrowThePieBlockspace"
import { fetchGrowThePieMaster } from "@/lib/api/fetchGrowThePieMaster"
import { fetchL2beat } from "@/lib/api/fetchL2beat"

// In seconds
const REVALIDATE_TIME = BASE_TIME_UNIT * 1

const loadData = dataLoader(
[
["ethereumMarketcapData", fetchEthereumMarketcap],
["growThePieData", fetchGrowThePie],
["growThePieBlockspaceData", fetchGrowThePieBlockspace],
["growThePieMasterData", fetchGrowThePieMaster],
["l2beatData", fetchL2beat],
],
REVALIDATE_TIME * 1000
)

const Page = async ({ params }: { params: PageParams }) => {
const { locale } = params

setRequestLocale(locale)

// Fetch data from data layer with Next.js caching
const [
ethereumMarketcapData,
growThePieData,
growThePieBlockspaceData,
growThePieMasterData,
l2beatData,
] = await loadData()
] = await Promise.all([
getCachedData<MetricReturnData>(
FETCH_ETHEREUM_MARKETCAP_TASK_ID,
REVALIDATE_TIME
),
getCachedData<GrowThePieData>(FETCH_GROW_THE_PIE_TASK_ID, REVALIDATE_TIME),
getCachedData<Record<string, unknown>>(
FETCH_GROW_THE_PIE_BLOCKSPACE_TASK_ID,
REVALIDATE_TIME
),
getCachedData<{ launchDates: Record<string, string> }>(
FETCH_GROW_THE_PIE_MASTER_TASK_ID,
REVALIDATE_TIME
),
getCachedData<{
projects: Record<
string,
{
tvs: { breakdown: { total: number } }
}
>
}>(FETCH_L2BEAT_TASK_ID, REVALIDATE_TIME),
])

// Handle missing data gracefully
const safeGrowThePieData: GrowThePieData = growThePieData || {
dailyTxCosts: {},
activeAddresses: {},
txCount: { value: 0, timestamp: Date.now() },
txCostsMedianUsd: { value: 0, timestamp: Date.now() },
}
const safeL2beatData = l2beatData || { projects: {} }
const safeGrowThePieBlockspaceData = growThePieBlockspaceData || {}
const safeGrowThePieMasterData = growThePieMasterData || { launchDates: {} }

const layer2DataCompiled = layer2Data
.map((network) => {
return {
...network,
txCosts: growThePieData.dailyTxCosts[network.growthepieID],
tvl: l2beatData.projects[network.l2beatID].tvs.breakdown.total,
networkMaturity: networkMaturity(l2beatData.projects[network.l2beatID]),
activeAddresses: growThePieData.activeAddresses[network.growthepieID],
txCosts: safeGrowThePieData.dailyTxCosts[network.growthepieID],
tvl:
safeL2beatData.projects[network.l2beatID]?.tvs?.breakdown?.total || 0,
networkMaturity: safeL2beatData.projects[network.l2beatID]
? networkMaturity(safeL2beatData.projects[network.l2beatID])
: "emerging",
activeAddresses:
safeGrowThePieData.activeAddresses[network.growthepieID],
blockspaceData:
(growThePieBlockspaceData || {})[network.growthepieID] || null,
(safeGrowThePieBlockspaceData as Record<string, unknown>)[
network.growthepieID
] || null,
launchDate:
(growThePieMasterData?.launchDates || {})[
safeGrowThePieMasterData.launchDates[
network.growthepieID.replace(/_/g, "-")
] || null,
walletsSupported: walletsData
Expand Down Expand Up @@ -115,8 +145,11 @@ const Page = async ({ params }: { params: PageParams }) => {
layer2Data: layer2DataCompiled,
mainnetData: {
...ethereumNetworkData,
txCosts: growThePieData.dailyTxCosts.ethereum,
tvl: "value" in ethereumMarketcapData ? ethereumMarketcapData.value : 0,
txCosts: safeGrowThePieData.dailyTxCosts.ethereum,
tvl:
(ethereumMarketcapData && "value" in ethereumMarketcapData
? ethereumMarketcapData.value
: null) || 0,
walletsSupported: walletsData
.filter((wallet) =>
wallet.supported_chains.includes("Ethereum Mainnet")
Expand Down
60 changes: 60 additions & 0 deletions src/data-layer/storage/cachedGetter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
/**
* Cached getter wrapper for Next.js integration.
*
* Wraps the data layer getter with Next.js caching and revalidation support.
* This allows pages to use the data layer while maintaining Next.js cache control.
*/

import { unstable_cache } from "next/cache"

import type { tasks } from "../registry"

import { getData } from "./getter"

type TaskId = (typeof tasks)[number]["id"]

/**
* Get data from storage with Next.js caching and revalidation.
*
* @param taskId - The task ID to retrieve data for
* @param revalidate - Revalidation time in seconds (default: 60)
* @returns The stored data, or null if not found
*/
export async function getCachedData<T>(
taskId: TaskId,
revalidate: number = 60
): Promise<T | null> {
return unstable_cache(
async () => {
return await getData<T>(taskId)
},
[taskId],
{
revalidate,
tags: [`data-layer-${taskId}`],
}
)()
}

/**
* Get data from storage with metadata and Next.js caching.
*
* @param taskId - The task ID to retrieve data for
* @param revalidate - Revalidation time in seconds (default: 60)
* @returns The stored data with metadata, or null if not found
*/
export async function getCachedDataWithMetadata<T>(
taskId: TaskId,
revalidate: number = 60
): Promise<{ data: T; metadata: { storedAt: string } } | null> {
return unstable_cache(
async () => {
return await getData<T>(taskId, { withMetadata: true })
},
[taskId],
{
revalidate,
tags: [`data-layer-${taskId}`],
}
)()
}