Skip to content

Commit

Permalink
feat(refactor): Use Staking API for validator era points (#2417)
Browse files Browse the repository at this point in the history
  • Loading branch information
rossbulat authored Jan 8, 2025
1 parent b9f42d2 commit 9e1b37f
Show file tree
Hide file tree
Showing 14 changed files with 141 additions and 174 deletions.
69 changes: 36 additions & 33 deletions packages/app/src/Router.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import { SideMenu } from 'library/SideMenu'
import { Tooltip } from 'library/Tooltip'
import { Offline } from 'Offline'
import { Overlays } from 'overlay'
import { ApolloProvider, client } from 'plugin-staking-api'
import { useEffect, useRef } from 'react'
import { ErrorBoundary } from 'react-error-boundary'
import { HelmetProvider } from 'react-helmet-async'
Expand Down Expand Up @@ -59,41 +60,43 @@ const RouterInner = () => {

return (
<ErrorBoundary FallbackComponent={ErrorFallbackApp}>
{pluginEnabled('staking_api') && activeAccount && (
<StakingApi activeAccount={activeAccount} network={network} />
)}
<NotificationPrompts />
<Body>
<Help />
<Overlays />
<Menu />
<Tooltip />
<Prompt />
<SideMenu />
<Main ref={mainInterfaceRef}>
<HelmetProvider>
<Headers />
<ErrorBoundary FallbackComponent={ErrorFallbackRoutes}>
<Routes>
{PagesConfig.map((page, i) => (
<ApolloProvider client={client}>
{pluginEnabled('staking_api') && activeAccount && (
<StakingApi activeAccount={activeAccount} network={network} />
)}
<NotificationPrompts />
<Body>
<Help />
<Overlays />
<Menu />
<Tooltip />
<Prompt />
<SideMenu />
<Main ref={mainInterfaceRef}>
<HelmetProvider>
<Headers />
<ErrorBoundary FallbackComponent={ErrorFallbackRoutes}>
<Routes>
{PagesConfig.map((page, i) => (
<Route
key={`main_interface_page_${i}`}
path={page.hash}
element={<PageWithTitle page={page} />}
/>
))}
<Route
key={`main_interface_page_${i}`}
path={page.hash}
element={<PageWithTitle page={page} />}
key="main_interface_navigate"
path="*"
element={<Navigate to="/overview" />}
/>
))}
<Route
key="main_interface_navigate"
path="*"
element={<Navigate to="/overview" />}
/>
</Routes>
</ErrorBoundary>
<MainFooter />
</HelmetProvider>
</Main>
</Body>
<Offline />
</Routes>
</ErrorBoundary>
<MainFooter />
</HelmetProvider>
</Main>
</Body>
<Offline />
</ApolloProvider>
</ErrorBoundary>
)
}
Expand Down
5 changes: 2 additions & 3 deletions packages/app/src/StakingApi/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@

import { useFastUnstake } from 'contexts/FastUnstake'
import { useStaking } from 'contexts/Staking'
import { ApolloProvider, client } from 'plugin-staking-api'
import { useEffect } from 'react'
import { FastUnstakeApi } from './FastUnstakeApi'
import type { Props } from './types'
Expand All @@ -20,9 +19,9 @@ export const StakingApi = (props: Props) => {
}, [isBonding()])

return (
<ApolloProvider client={client}>
<>
<UnclaimedRewardsApi {...props} />
{isBonding() && <FastUnstakeApi {...props} />}
</ApolloProvider>
</>
)
}
54 changes: 1 addition & 53 deletions packages/app/src/controllers/Subscan/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,10 @@

import { poolMembersPerPage } from 'library/List/defaults'
import type { PoolMember } from 'types'
import type {
SubscanEraPoints,
SubscanPoolMember,
SubscanRequestBody,
} from './types'
import type { SubscanPoolMember, SubscanRequestBody } from './types'

export class Subscan {
// List of endpoints to be used for Subscan API calls
static ENDPOINTS = {
eraStat: '/api/scan/staking/era_stat',
poolMembers: '/api/scan/nomination_pool/pool/members',
}

Expand All @@ -22,9 +16,6 @@ export class Subscan {
// Subscan pool data, keyed by `<network>-<poolId>-<key1>-<key2>...`
static poolData: Record<string, PoolMember[]> = {}

// Subscan era points data, keyed by `<network>-<address>-<era>`
static eraPointsData: Record<string, SubscanEraPoints[]> = {}

// Set the network to use for Subscan API calls
set network(network: string) {
Subscan.network = network
Expand Down Expand Up @@ -52,35 +43,6 @@ export class Subscan {
.reverse()
}

// Fetch a pool's era points from Subscan
static fetchEraPoints = async (
address: string,
era: number
): Promise<SubscanEraPoints[]> => {
const result = await this.makeRequest(this.ENDPOINTS.eraStat, {
page: 0,
row: 100,
address,
})
if (!result) {
return []
}

// Format list to just contain reward points
const list = []
for (let i = era; i > era - 100; i--) {
list.push({
era: i,
reward_point:
result.list.find(
({ era: resultEra }: { era: number }) => resultEra === i
)?.reward_point ?? 0,
})
}
// Removes last zero item and return
return list.reverse().splice(0, list.length - 1)
}

// Handle fetching pool members
static handleFetchPoolMembers = async (poolId: number, page: number) => {
const dataKey = `${this.network}-${poolId}-${page}-members}`
Expand All @@ -96,20 +58,6 @@ export class Subscan {
}
}

// Handle fetching era point history
static handleFetchEraPoints = async (address: string, era: number) => {
const dataKey = `${this.network}-${address}-${era}}`
const currentValue = this.eraPointsData[dataKey]

if (currentValue) {
return currentValue
} else {
const result = await this.fetchEraPoints(address, era)
this.eraPointsData[dataKey] = result
return result
}
}

// Get the public Subscan endpoint
static getEndpoint = () => `https://${this.network}.api.subscan.io`

Expand Down
7 changes: 3 additions & 4 deletions packages/app/src/library/Graphs/EraPoints.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ export const EraPoints = ({ items = [], height }: EraPointsProps) => {
grid: {
color: graphColors.grid[mode],
},
min: 0,
ticks: {
display: true,
beginAtZero: false,
Expand Down Expand Up @@ -100,13 +101,11 @@ export const EraPoints = ({ items = [], height }: EraPointsProps) => {
}

const data = {
labels: items.map(({ era }: { era: string }) => era),
labels: items.map(({ era }) => era),
datasets: [
{
label: t('points'),
data: items.map(
({ reward_point }: { reward_point: string }) => reward_point
),
data: items.map(({ points }) => points),
borderColor: colors.primary[mode],
backgroundColor: colors.primary[mode],
pointStyle: undefined,
Expand Down
8 changes: 6 additions & 2 deletions packages/app/src/library/Graphs/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,11 @@

import type BigNumber from 'bignumber.js'
import type { AnyApi } from 'common-types'
import type { NominatorReward, PoolReward } from 'plugin-staking-api/types'
import type {
NominatorReward,
PoolReward,
ValidatorEraPoints,
} from 'plugin-staking-api/types'

export interface BondedProps {
active: BigNumber
Expand All @@ -14,7 +18,7 @@ export interface BondedProps {
}

export interface EraPointsProps {
items: AnyApi
items: ValidatorEraPoints[]
height: number
}

Expand Down
15 changes: 2 additions & 13 deletions packages/app/src/library/MainFooter/TokenPrice.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,11 @@
import { useEffectIgnoreInitial } from '@w3ux/hooks'
import { useNetwork } from 'contexts/Network'
import { isCustomEvent } from 'controllers/utils'
import {
ApolloProvider,
client,
formatTokenPrice,
useTokenPrice,
} from 'plugin-staking-api'
import { formatTokenPrice, useTokenPrice } from 'plugin-staking-api'
import { useRef } from 'react'
import { useEventListener } from 'usehooks-ts'

export const TokenPriceInner = () => {
export const TokenPrice = () => {
const {
networkData: {
api: { unit },
Expand Down Expand Up @@ -68,9 +63,3 @@ export const TokenPriceInner = () => {
</>
)
}

export const TokenPrice = () => (
<ApolloProvider client={client}>
<TokenPriceInner />
</ApolloProvider>
)
28 changes: 28 additions & 0 deletions packages/app/src/overlay/modals/ValidatorMetrics/ActiveGraph.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// Copyright 2024 @polkadot-cloud/polkadot-staking-dashboard authors & contributors
// SPDX-License-Identifier: GPL-3.0-only

import type { NetworkId } from 'common-types'
import { EraPoints } from 'library/Graphs/EraPoints'
import { useValidatorEraPoints } from 'plugin-staking-api'

interface Props {
network: NetworkId
validator: string
fromEra: number
}
export const ActiveGraph = ({ network, validator, fromEra }: Props) => {
const { data, loading, error } = useValidatorEraPoints({
chain: network,
validator,
fromEra,
})

const list =
loading || error || data?.validatorEraPoints === undefined
? []
: data.validatorEraPoints

const sorted = [...list].sort((a, b) => a.era - b.era)

return <EraPoints items={sorted} height={250} />
}
31 changes: 8 additions & 23 deletions packages/app/src/overlay/modals/ValidatorMetrics/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,38 +3,34 @@

import { useSize } from '@w3ux/hooks'
import { Polkicon } from '@w3ux/react-polkicon'
import type { AnyJson } from '@w3ux/types'
import { ellipsisFn } from '@w3ux/utils'
import BigNumber from 'bignumber.js'
import { useApi } from 'contexts/Api'
import { useHelp } from 'contexts/Help'
import { useNetwork } from 'contexts/Network'
import { usePlugins } from 'contexts/Plugins'
import { useStaking } from 'contexts/Staking'
import { useUi } from 'contexts/UI'
import { Subscan } from 'controllers/Subscan'
import { CardHeaderWrapper, CardWrapper } from 'library/Card/Wrappers'
import { EraPoints as EraPointsGraph } from 'library/Graphs/EraPoints'
import { formatSize } from 'library/Graphs/Utils'
import { GraphWrapper } from 'library/Graphs/Wrapper'
import { Title } from 'library/Modal/Title'
import { StatWrapper, StatsWrapper } from 'library/Modal/Wrappers'
import { PluginLabel } from 'library/PluginLabel'
import { StatusLabel } from 'library/StatusLabel'
import { useEffect, useRef, useState } from 'react'
import { useRef } from 'react'
import { useTranslation } from 'react-i18next'
import { ButtonHelp } from 'ui-buttons'
import { AddressHeader, Padding } from 'ui-core/modal'
import { useOverlay } from 'ui-overlay'
import { planckToUnitBn } from 'utils'
import { ActiveGraph } from './ActiveGraph'

export const ValidatorMetrics = () => {
const { t } = useTranslation('modals')
const {
network,
networkData: { units, unit },
} = useNetwork()
const { activeEra } = useApi()
const { plugins } = usePlugins()
const { containerRefs } = useUi()
const { options } = useOverlay().modal.config
const { address, identity } = options
Expand All @@ -58,27 +54,13 @@ export const ValidatorMetrics = () => {
validatorOwnStake = new BigNumber(own)
}
}
const [list, setList] = useState<AnyJson[]>([])

const ref = useRef<HTMLDivElement>(null)
const size = useSize(ref, {
outerElement: containerRefs?.mainInterface,
})
const { width, height, minHeight } = formatSize(size, 300)

const handleEraPoints = async () => {
if (!plugins.includes('subscan')) {
return
}
setList(
await Subscan.handleFetchEraPoints(address, activeEra.index.toNumber())
)
}

useEffect(() => {
handleEraPoints()
}, [])

const stats = [
{
label: t('selfStake'),
Expand Down Expand Up @@ -120,7 +102,6 @@ export const ValidatorMetrics = () => {
<div
style={{ position: 'relative', marginTop: '0.5rem', padding: '1rem' }}
>
<PluginLabel plugin="subscan" />
<CardWrapper
className="transparent"
style={{
Expand All @@ -146,7 +127,11 @@ export const ValidatorMetrics = () => {
width: `${width}px`,
}}
>
<EraPointsGraph items={list} height={250} />
<ActiveGraph
network={network}
validator={address}
fromEra={BigNumber.max(activeEra.index.minus(1), 0).toNumber()}
/>
</GraphWrapper>
</div>
</CardWrapper>
Expand Down
Loading

0 comments on commit 9e1b37f

Please sign in to comment.