Skip to content

Commit 22794fb

Browse files
authored
Merge pull request #13395 from ethereum/eth-staked-dune-api
Dune API to fetch total eth staked
2 parents 343efc9 + a7607b3 commit 22794fb

File tree

5 files changed

+44
-57
lines changed

5 files changed

+44
-57
lines changed

.env.example

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@
1616
# GOOGLE_API_KEY=
1717
# GOOGLE_CALENDAR_ID=
1818

19+
# Dune Analytics API key (required for total eth staked)
20+
# DUNE_API_KEY=
21+
1922
# Matomo environment (URL and site ID required for analytics)
2023
NEXT_PUBLIC_MATOMO_URL=
2124
NEXT_PUBLIC_MATOMO_SITE_ID=

src/components/StatsBoxGrid/useStatsBoxGrid.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -96,8 +96,8 @@ export const useStatsBoxGrid = ({
9696

9797
const metrics: StatsBoxMetric[] = [
9898
{
99-
apiProvider: "Beaconcha.in",
100-
apiUrl: "https://beaconcha.in/",
99+
apiProvider: "Dune Analytics",
100+
apiUrl: "https://dune.com/",
101101
title: t("page-index-network-stats-total-eth-staked"),
102102
description: t("page-index-network-stats-total-eth-staked-explainer"),
103103
buttonContainer: (

src/lib/api/fetchTotalEthStaked.ts

Lines changed: 29 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -1,73 +1,47 @@
1-
import type {
2-
EthStoreResponse,
3-
MetricReturnData,
4-
TimestampedData,
5-
} from "@/lib/types"
1+
import type { EthStakedResponse, MetricReturnData } from "@/lib/types"
62

7-
import { weiToRoundedEther } from "@/lib/utils/weiToRoundedEther"
3+
import { DUNE_API_URL } from "../constants"
84

9-
import { BEACONCHA_IN_URL, DAYS_TO_FETCH } from "@/lib/constants"
10-
11-
const MS_PER_DAY = 1000 * 60 * 60 * 24
12-
const DAY_DELTA = 5
5+
const DUNE_API_KEY = process.env.DUNE_API_KEY
136

147
export const fetchTotalEthStaked = async (): Promise<MetricReturnData> => {
15-
const { href: ethstoreLatest } = new URL(
16-
"api/v1/ethstore/latest",
17-
BEACONCHA_IN_URL
8+
if (!DUNE_API_KEY) {
9+
console.error("Dune API key not found")
10+
return { error: "Dune API key not found" }
11+
}
12+
13+
const url = new URL(
14+
"api/v1/endpoints/pablop/eth-staked/results",
15+
DUNE_API_URL
1816
)
1917

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

31-
const ethstoreJson: EthStoreResponse = await ethstoreLatestResponse.json()
27+
const ethStakedJson: EthStakedResponse = await ethStakedResponse.json()
3228
const {
33-
data: { day, effective_balances_sum_wei },
34-
} = ethstoreJson
35-
const valueTotalEth = weiToRoundedEther(effective_balances_sum_wei)
36-
37-
const data: TimestampedData<number>[] = [
38-
{ timestamp: new Date().getTime(), value: valueTotalEth },
39-
]
40-
41-
// 2- Perform multiple API calls to fetch data for the last 90 days, `getData` for caching
42-
for (let i = DAY_DELTA; i <= DAYS_TO_FETCH; i += DAY_DELTA) {
43-
const lookupDay = day - i
44-
const timestamp = new Date().getTime() - i * MS_PER_DAY
29+
result: { rows = [] },
30+
} = ethStakedJson
4531

46-
const { href: ethstoreDay } = new URL(
47-
`api/v1/ethstore/${lookupDay}`,
48-
BEACONCHA_IN_URL
49-
)
50-
51-
const ethstoreDayResponse = await fetch(ethstoreDay)
52-
if (!ethstoreDayResponse.ok) {
53-
console.log(ethstoreDayResponse.status, ethstoreDayResponse.statusText)
54-
throw new Error("Failed to fetch Ethstore day data")
55-
}
56-
57-
const ethstoreDayJson: EthStoreResponse = await ethstoreDayResponse.json()
58-
const {
59-
data: { effective_balances_sum_wei: sumWei },
60-
} = ethstoreDayJson
61-
const value = weiToRoundedEther(sumWei)
62-
63-
data.push({ timestamp, value })
64-
}
32+
const data = rows.map((row) => ({
33+
timestamp: new Date(row.time).getTime(),
34+
value: row.cum_deposited_eth,
35+
}))
6536

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

40+
const { value } = data[data.length - 1]
41+
6842
return {
69-
data, // historical data: { timestamp: unix-milliseconds, value }
70-
value: valueTotalEth, // current value (number, unformatted)
43+
data,
44+
value,
7145
}
7246
} catch (error: unknown) {
7347
console.error((error as Error).message)

src/lib/constants.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ export const DAYS_TO_FETCH = 90
7474
export const RANGES = ["30d", "90d"] as const
7575
export const BEACONCHA_IN_URL = "https://beaconcha.in/"
7676
export const ETHERSCAN_API_URL = "https://api.etherscan.io"
77+
export const DUNE_API_URL = "https://api.dune.com"
7778

7879
// Wallets
7980
export const NUMBER_OF_SUPPORTED_LANGUAGES_SHOWN = 5

src/lib/types.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -485,6 +485,15 @@ export type EthStoreResponse = Data<{
485485
effective_balances_sum_wei: number
486486
}>
487487

488+
export type EthStakedResponse = {
489+
result: {
490+
rows?: {
491+
cum_deposited_eth: number
492+
time: string
493+
}[]
494+
}
495+
}
496+
488497
export type EpochResponse = Data<{
489498
validatorscount: number
490499
}>

0 commit comments

Comments
 (0)