Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Dune API to fetch total eth staked #13395

Merged
merged 1 commit into from
Jul 17, 2024
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
3 changes: 3 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@
# GOOGLE_API_KEY=
# GOOGLE_CALENDAR_ID=

# Dune Analytics API key (required for total eth staked)
# DUNE_API_KEY=

# Matomo environment (URL and site ID required for analytics)
NEXT_PUBLIC_MATOMO_URL=
NEXT_PUBLIC_MATOMO_SITE_ID=
Expand Down
4 changes: 2 additions & 2 deletions src/components/StatsBoxGrid/useStatsBoxGrid.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -96,8 +96,8 @@ export const useStatsBoxGrid = ({

const metrics: StatsBoxMetric[] = [
{
apiProvider: "Beaconcha.in",
apiUrl: "https://beaconcha.in/",
apiProvider: "Dune Analytics",
apiUrl: "https://dune.com/",
title: t("page-index-network-stats-total-eth-staked"),
description: t("page-index-network-stats-total-eth-staked-explainer"),
buttonContainer: (
Expand Down
84 changes: 29 additions & 55 deletions src/lib/api/fetchTotalEthStaked.ts
Original file line number Diff line number Diff line change
@@ -1,73 +1,47 @@
import type {
EthStoreResponse,
MetricReturnData,
TimestampedData,
} from "@/lib/types"
import type { EthStakedResponse, MetricReturnData } from "@/lib/types"

import { weiToRoundedEther } from "@/lib/utils/weiToRoundedEther"
import { DUNE_API_URL } from "../constants"

import { BEACONCHA_IN_URL, DAYS_TO_FETCH } from "@/lib/constants"

const MS_PER_DAY = 1000 * 60 * 60 * 24
const DAY_DELTA = 5
const DUNE_API_KEY = process.env.DUNE_API_KEY

export const fetchTotalEthStaked = async (): Promise<MetricReturnData> => {
const { href: ethstoreLatest } = new URL(
"api/v1/ethstore/latest",
BEACONCHA_IN_URL
if (!DUNE_API_KEY) {
console.error("Dune API key not found")
return { error: "Dune API key not found" }
}

const url = new URL(
"api/v1/endpoints/pablop/eth-staked/results",
DUNE_API_URL
)

try {
// 1- Use initial call to `latest` to fetch current Beacon Chain "day" (for use in secondary fetches)
const ethstoreLatestResponse = await fetch(ethstoreLatest)
if (!ethstoreLatestResponse.ok) {
console.log(
ethstoreLatestResponse.status,
ethstoreLatestResponse.statusText
)
throw new Error("Failed to fetch Ethstore latest data")
const ethStakedResponse = await fetch(url, {
headers: { "X-Dune-API-Key": DUNE_API_KEY },
})
if (!ethStakedResponse.ok) {
console.log(ethStakedResponse.status, ethStakedResponse.statusText)
throw new Error("Failed to fetch eth staked data")
}

const ethstoreJson: EthStoreResponse = await ethstoreLatestResponse.json()
const ethStakedJson: EthStakedResponse = await ethStakedResponse.json()
const {
data: { day, effective_balances_sum_wei },
} = ethstoreJson
const valueTotalEth = weiToRoundedEther(effective_balances_sum_wei)

const data: TimestampedData<number>[] = [
{ timestamp: new Date().getTime(), value: valueTotalEth },
]

// 2- Perform multiple API calls to fetch data for the last 90 days, `getData` for caching
for (let i = DAY_DELTA; i <= DAYS_TO_FETCH; i += DAY_DELTA) {
const lookupDay = day - i
const timestamp = new Date().getTime() - i * MS_PER_DAY
result: { rows = [] },
} = ethStakedJson

const { href: ethstoreDay } = new URL(
`api/v1/ethstore/${lookupDay}`,
BEACONCHA_IN_URL
)

const ethstoreDayResponse = await fetch(ethstoreDay)
if (!ethstoreDayResponse.ok) {
console.log(ethstoreDayResponse.status, ethstoreDayResponse.statusText)
throw new Error("Failed to fetch Ethstore day data")
}

const ethstoreDayJson: EthStoreResponse = await ethstoreDayResponse.json()
const {
data: { effective_balances_sum_wei: sumWei },
} = ethstoreDayJson
const value = weiToRoundedEther(sumWei)

data.push({ timestamp, value })
}
const data = rows.map((row) => ({
timestamp: new Date(row.time).getTime(),
value: row.cum_deposited_eth,
}))

// data is already sorted...but just in case
data.sort((a, b) => a.timestamp - b.timestamp)

const { value } = data[data.length - 1]

return {
data, // historical data: { timestamp: unix-milliseconds, value }
value: valueTotalEth, // current value (number, unformatted)
data,
value,
}
} catch (error: unknown) {
console.error((error as Error).message)
Expand Down
3 changes: 2 additions & 1 deletion src/lib/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ export const DAYS_TO_FETCH = 90
export const RANGES = ["30d", "90d"] as const
export const BEACONCHA_IN_URL = "https://beaconcha.in/"
export const ETHERSCAN_API_URL = "https://api.etherscan.io"
export const DUNE_API_URL = "https://api.dune.com"

// Wallets
export const NUMBER_OF_SUPPORTED_LANGUAGES_SHOWN = 5
Expand Down Expand Up @@ -152,4 +153,4 @@ export const MOBILE_LANGUAGE_BUTTON_NAME = "mobile-language-button"
export const DESKTOP_LANGUAGE_BUTTON_NAME = "desktop-language-button"

// Codeblock
export const LINES_BEFORE_COLLAPSABLE = 8
export const LINES_BEFORE_COLLAPSABLE = 8
11 changes: 10 additions & 1 deletion src/lib/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -417,7 +417,7 @@ type HeroButtonProps = Omit<CallToActionProps, "index">
* or a string. (defaults to `StaticImageData`)
*/
export type CommonHeroProps<
HeroImg extends StaticImageData | string = StaticImageData
HeroImg extends StaticImageData | string = StaticImageData,
> = {
/**
* Decorative image displayed as the full background or an aside to
Expand Down Expand Up @@ -485,6 +485,15 @@ export type EthStoreResponse = Data<{
effective_balances_sum_wei: number
}>

export type EthStakedResponse = {
result: {
rows?: {
cum_deposited_eth: number
time: string
}[]
}
}

export type EpochResponse = Data<{
validatorscount: number
}>
Expand Down
Loading