1
- import { ReactNode , useEffect , useState } from "react"
2
1
import { useRouter } from "next/router"
3
2
import { useTranslation } from "next-i18next"
4
3
import { MdInfoOutline } from "react-icons/md"
5
- import { Code , Flex , Icon , Spinner , VStack } from "@chakra-ui/react"
4
+ import { Code , Flex , Icon , VStack } from "@chakra-ui/react"
6
5
7
- import type { Lang } from "@/lib/types"
6
+ import type { BeaconchainData , ChildOnlyProp , Lang } from "@/lib/types"
8
7
9
8
import InlineLink from "@/components/Link"
10
9
import Text from "@/components/OldText"
11
10
import Tooltip from "@/components/Tooltip"
12
11
13
12
import { getLocaleForNumberFormat } from "@/lib/utils/translations"
14
13
15
- const NA_ERROR = "n/a"
16
- const ZERO = "0"
17
-
18
- const Cell : React . FC < { children : ReactNode } > = ( { children } ) => {
19
- return (
20
- < VStack
21
- spacing = { 2 }
22
- py = { 4 }
23
- px = { 8 }
24
- borderInlineStart = { { md : "1px" } }
25
- borderTop = { { base : "1px" , md : "none" } }
26
- // `!important` needed to force an override of the user-agent
27
- borderColor = "preBorder !important"
28
- _first = { {
29
- borderInlineStart : "none" ,
30
- borderTop : "none" ,
31
- } }
32
- >
33
- { children }
34
- </ VStack >
35
- )
36
- }
14
+ const Cell = ( { children } : ChildOnlyProp ) => (
15
+ < VStack
16
+ spacing = { 2 }
17
+ py = { 4 }
18
+ px = { 8 }
19
+ borderInlineStart = { { md : "1px" } }
20
+ borderTop = { { base : "1px" , md : "none" } }
21
+ // `!important` needed to force an override of the user-agent
22
+ borderColor = "preBorder !important"
23
+ _first = { {
24
+ borderInlineStart : "none" ,
25
+ borderTop : "none" ,
26
+ } }
27
+ >
28
+ { children }
29
+ </ VStack >
30
+ )
37
31
38
- const Value : React . FC < { children : ReactNode ; title : string } > = ( {
39
- children,
40
- title,
41
- } ) => {
42
- return (
43
- < Code
44
- title = { title }
45
- fontWeight = "bold"
46
- fontSize = "2rem"
47
- background = "none"
48
- color = "primary.base"
49
- p = { 0 }
50
- >
51
- { children }
52
- </ Code >
53
- )
54
- }
32
+ const Value = ( { children } : ChildOnlyProp ) => (
33
+ < Code
34
+ fontWeight = "bold"
35
+ fontSize = "2rem"
36
+ background = "none"
37
+ color = "primary.base"
38
+ p = { 0 }
39
+ >
40
+ { children }
41
+ </ Code >
42
+ )
55
43
56
- const Label : React . FC < { children : ReactNode } > = ( { children } ) => {
57
- return (
58
- < Flex alignItems = "center" textTransform = "uppercase" fontSize = "sm" >
59
- { children }
60
- </ Flex >
61
- )
62
- }
44
+ const Label = ( { children } : ChildOnlyProp ) => (
45
+ < Flex alignItems = "center" textTransform = "uppercase" fontSize = "sm" >
46
+ { children }
47
+ </ Flex >
48
+ )
63
49
64
50
// BeaconchainTooltip component
65
- interface BeaconchainTooltipProps {
66
- children : ReactNode
67
- }
68
- const BeaconchainTooltip : React . FC < BeaconchainTooltipProps > = ( {
69
- children,
70
- } ) => (
51
+ const BeaconchainTooltip = ( { children } : ChildOnlyProp ) => (
71
52
< Tooltip content = { children } >
72
53
< Icon
73
54
as = { MdInfoOutline }
@@ -81,101 +62,35 @@ const BeaconchainTooltip: React.FC<BeaconchainTooltipProps> = ({
81
62
</ Tooltip >
82
63
)
83
64
84
- // Interfaces
85
- interface EthStoreResponse {
86
- data : {
87
- apr : number
88
- effective_balances_sum_wei : number
89
- }
90
- }
91
-
92
- interface EpochResponse {
93
- data : {
94
- validatorscount : number
95
- }
96
- }
97
-
98
65
// StatsBox component
99
- const StakingStatsBox = ( ) => {
66
+ type StakingStatsBoxProps = {
67
+ data : BeaconchainData
68
+ }
69
+ const StakingStatsBox = ( { data } : StakingStatsBoxProps ) => {
100
70
const { locale } = useRouter ( )
101
71
const { t } = useTranslation ( "page-staking" )
102
- /**
103
- * State variables:
104
- * - ZERO is default string, "0", representing loading state
105
- * - null is error state
106
- */
107
- const [ totalEth , setTotalEth ] = useState < string | null > ( ZERO )
108
- const [ totalValidators , setTotalValidators ] = useState < string | null > ( ZERO )
109
- const [ currentApr , setCurrentApr ] = useState < string | null > ( ZERO )
110
72
111
- // TODO: Confirm data fetch approach/frequency
112
- useEffect ( ( ) => {
113
- const localeForStatsBoxNumbers = getLocaleForNumberFormat ( locale ! as Lang )
73
+ const localeForStatsBoxNumbers = getLocaleForNumberFormat ( locale ! as Lang )
114
74
115
- // Helper functions
116
- const formatInteger = ( amount : number ) : string =>
117
- new Intl . NumberFormat ( localeForStatsBoxNumbers ) . format ( amount )
75
+ // Helper functions
76
+ const formatInteger = ( amount : number ) : string =>
77
+ new Intl . NumberFormat ( localeForStatsBoxNumbers ) . format ( amount )
118
78
119
- const formatPercentage = ( amount : number ) : string =>
120
- new Intl . NumberFormat ( localeForStatsBoxNumbers , {
121
- style : "percent" ,
122
- minimumSignificantDigits : 2 ,
123
- maximumSignificantDigits : 2 ,
124
- } ) . format ( amount )
79
+ const formatPercentage = ( amount : number ) : string =>
80
+ new Intl . NumberFormat ( localeForStatsBoxNumbers , {
81
+ style : "percent" ,
82
+ minimumSignificantDigits : 2 ,
83
+ maximumSignificantDigits : 2 ,
84
+ } ) . format ( amount )
125
85
126
- // API calls, data formatting, and state setting
127
- const base = "https://beaconcha.in"
128
- const { href : ethstore } = new URL ( "api/v1/ethstore/latest" , base )
129
- const { href : epoch } = new URL ( "api/v1/epoch/latest" , base )
130
- // Get total ETH staked and current APR from ethstore endpoint
131
- ; ( async ( ) => {
132
- try {
133
- const ethStoreResponse = await fetch ( ethstore )
134
- if ( ! ethStoreResponse . ok )
135
- throw new Error (
136
- "Network response from Beaconcha.in ETHSTORE was not ok"
137
- )
138
- const ethStoreResponseJson : EthStoreResponse =
139
- await ethStoreResponse . json ( )
140
- const {
141
- data : { apr, effective_balances_sum_wei } ,
142
- } = ethStoreResponseJson
143
- const totalEffectiveBalance : number = effective_balances_sum_wei * 1e-18
144
- const valueTotalEth = formatInteger ( Math . floor ( totalEffectiveBalance ) )
145
- const valueCurrentApr = formatPercentage ( apr )
146
- setTotalEth ( valueTotalEth )
147
- setCurrentApr ( valueCurrentApr )
148
- } catch ( error ) {
149
- setTotalEth ( null )
150
- setCurrentApr ( null )
151
- }
152
- } ) ( )
153
- // Get total active validators from latest epoch endpoint
154
- ; ( async ( ) => {
155
- try {
156
- const epochResponse = await fetch ( epoch )
157
- if ( ! epochResponse . ok )
158
- throw new Error ( "Network response from Beaconcha.in EPOCH was not ok" )
159
- const epochResponseJson : EpochResponse = await epochResponse . json ( )
160
- const {
161
- data : { validatorscount } ,
162
- } = epochResponseJson
163
- const valueTotalValidators = formatInteger ( validatorscount )
164
- setTotalValidators ( valueTotalValidators )
165
- } catch ( error ) {
166
- setTotalValidators ( null )
167
- }
168
- } ) ( )
169
- } , [ locale ] )
86
+ const totalEth = formatInteger ( data . totalEthStaked )
87
+ const totalValidators = formatInteger ( data . validatorscount )
88
+ const currentApr = formatPercentage ( data . apr )
170
89
171
90
return (
172
91
< Flex direction = { { base : "column" , md : "row" } } >
173
92
< Cell >
174
- { totalEth === ZERO ? (
175
- < Spinner />
176
- ) : (
177
- < Value title = { totalEth ? "" : NA_ERROR } > { totalEth || NA_ERROR } </ Value >
178
- ) }
93
+ < Value > { totalEth } </ Value >
179
94
< Label >
180
95
{ t ( "page-staking-stats-box-metric-1" ) }
181
96
< BeaconchainTooltip >
@@ -186,13 +101,7 @@ const StakingStatsBox = () => {
186
101
</ Label >
187
102
</ Cell >
188
103
< Cell >
189
- { totalValidators === ZERO ? (
190
- < Spinner />
191
- ) : (
192
- < Value title = { totalValidators ? "" : NA_ERROR } >
193
- { totalValidators || NA_ERROR }
194
- </ Value >
195
- ) }
104
+ < Value > { totalValidators } </ Value >
196
105
< Label >
197
106
{ t ( "page-staking-stats-box-metric-2" ) }
198
107
< BeaconchainTooltip >
@@ -203,13 +112,7 @@ const StakingStatsBox = () => {
203
112
</ Label >
204
113
</ Cell >
205
114
< Cell >
206
- { currentApr === ZERO ? (
207
- < Spinner />
208
- ) : (
209
- < Value title = { currentApr ? "" : NA_ERROR } >
210
- { currentApr || NA_ERROR }
211
- </ Value >
212
- ) }
115
+ < Value > { currentApr } </ Value >
213
116
< Label >
214
117
{ t ( "page-staking-stats-box-metric-3" ) }
215
118
< BeaconchainTooltip >
0 commit comments