Skip to content

Commit

Permalink
Merge pull request #12 from Cerebellum-Network/release/1.1.0
Browse files Browse the repository at this point in the history
Release 1.1.0
  • Loading branch information
shamilkhan authored May 31, 2023
2 parents 87b6081 + b3771d2 commit 00878c1
Show file tree
Hide file tree
Showing 17 changed files with 367 additions and 26 deletions.
15 changes: 15 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,21 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [1.1.0] - 2023-05-31

## Added

- Added `CereStatsProvider` feature to fetch data from CereStats API, providing reward history and era points for validators.
- Implemented custom hooks, `useFetchEraPoints` and `usePayouts`, to manage data from CereStats API. `useFetchEraPoints` retrieves era points for a specific validator, while `usePayouts` manages reward history.

## Changed

- The `useSubscan` hook has been replaced with the `useCereStats` hook for the Reward History and Era Point Charts data source.

## Removed

- Removed dependency on `Subscan API` and simplified data management strategy by using `CereStats` API as primary data source.

## [1.0.1] - 2023-05-18

## Added
Expand Down
5 changes: 4 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
{
"name": "polkadot-staking-dashboard",
"version": "1.0.1",
"version": "1.1.0",
"license": "apache-2.0",
"private": false,
"dependencies": {
"@apollo/client": "^3.7.14",
"@fortawesome/fontawesome-svg-core": "6.1.0",
"@fortawesome/free-brands-svg-icons": "^6.1.1",
"@fortawesome/free-regular-svg-icons": "^5.15.4",
Expand Down Expand Up @@ -31,6 +32,7 @@
"downshift": "^6.1.7",
"follow-redirects": ">=1.14.8",
"framer-motion": "^6.2.4",
"graphql": "^16.6.0",
"lodash.throttle": "^4.1.1",
"moment": "^2.29.4",
"nth-check": ">=2.0.1",
Expand All @@ -46,6 +48,7 @@
"react-scroll": "^1.8.6",
"styled-components": "^5.3.3",
"styled-theming": "^2.2.0",
"subscriptions-transport-ws": "^0.11.0",
"typescript": "^4.5.5",
"web-vitals": "^2.1.4",
"window-or-global": "^1.0.1",
Expand Down
4 changes: 2 additions & 2 deletions src/Providers.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import { PoolMembershipsProvider } from './contexts/Pools/PoolMemberships';
import { ActivePoolProvider } from './contexts/Pools/ActivePool';
import { SideBarProvider } from './contexts/SideBar';
import { StakingProvider } from './contexts/Staking';
import { SubscanProvider } from './contexts/Subscan';
import { CereStatsProvider } from './contexts/CereStats';
import { ValidatorsProvider } from './contexts/Validators';
import { UIProvider } from './contexts/UI';
import { useTheme } from './contexts/Themes';
Expand Down Expand Up @@ -62,7 +62,7 @@ export const Providers = withProviders(
ActivePoolProvider,
ValidatorsProvider,
UIProvider,
SubscanProvider,
CereStatsProvider,
MenuProvider,
TooltipProvider,
PaletteProvider,
Expand Down
2 changes: 2 additions & 0 deletions src/config/networks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ const cereMainnet = {
},
endpoint: 'wss://archive.mainnet.cere.network/ws',
subscanEndpoint: '',
cereStatsEndpoint: 'wss://hasura.stats.cere.network/v1/graphql',
unit: 'CERE',
units: 10,
ss58: 54,
Expand Down Expand Up @@ -51,6 +52,7 @@ const cereTestnet = {
...cereMainnet,
name: 'Cere Testnet',
endpoint: 'wss://archive.testnet.cere.network/ws',
cereStatsEndpoint: 'wss://hasura.stats.dev.cere.network/v1/graphql',
};

// Determine if the testnet should be included based on the REACT_APP_INCLUDE_TESTNET environment variable
Expand Down
2 changes: 1 addition & 1 deletion src/consts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ export const MEDIUM_FONT_SiZE_MAX_WIDTH = 1600;
/*
* Toggle-able services
*/
export const SERVICES = [];
export const SERVICES = ['cereStats'];

/*
* Fallback config values
Expand Down
7 changes: 7 additions & 0 deletions src/contexts/CereStats/defaults.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { CereStatsContextInterface } from './types';

export const defaultCereStatsContext: CereStatsContextInterface = {
fetchEraPoints: (v, e) => {},
payouts: [],
poolClaims: [],
};
180 changes: 180 additions & 0 deletions src/contexts/CereStats/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
import React, { useState, useEffect, createContext } from 'react';
import {
ApolloClient,
NormalizedCacheObject,
InMemoryCache,
gql,
} from '@apollo/client';
import { WebSocketLink } from '@apollo/client/link/ws';
import { defaultCereStatsContext } from './defaults';
import { CereStatsContextInterface } from './types';
import { useConnect } from '../Connect';
import { useApi } from '../Api';
import { Network } from '../../types';

const useApolloClient = (endpoint: Network['cereStatsEndpoint']) => {
const [client, setClient] =
useState<ApolloClient<NormalizedCacheObject> | null>(null);

useEffect(() => {
const wsLink = new WebSocketLink({
uri: endpoint,
options: {
reconnect: true,
},
});

const _client = new ApolloClient({
link: wsLink,
cache: new InMemoryCache(),
});

setClient(_client);
}, [endpoint]);

return client;
};

const useFetchEraPoints = (
client: ApolloClient<NormalizedCacheObject> | null
) => {
const fetchEraPoints = async (address: string, activeEraIndex: number) => {
if (!address || !client) {
return [];
}

const { data } = await client.query({
query: gql`
query EraPoints($stashAddress: String) {
era_points(where: { stash_address: { _eq: $stashAddress } }) {
era
points
}
}
`,
variables: { stashAddress: address },
});

if (data?.era_points !== null) {
const list = [];
// Set a constant for the number of eras we want to display
const ERAS_TO_SHOW = 100;

for (let i = activeEraIndex; i > activeEraIndex - ERAS_TO_SHOW; i--) {
list.push({
era: i,
reward_point:
data.era_points.find((item: any) => item.era === i)?.points ?? 0,
});
}
// removes last zero item and returns
return list.reverse().splice(0, list.length - 1);
}

return [];
};

return fetchEraPoints;
};

// Fetch Payouts Hook
const usePayouts = (
client: ApolloClient<NormalizedCacheObject> | null,
activeAccount: string | null
) => {
const [payouts, setPayouts] = useState([]);

const normalizePayouts = (
payoutData: { block_number: number; data: string; timestamp: number }[]
) => {
return payoutData
.map(({ block_number, data, timestamp }) => {
let amount = 0;

// Using regex to extract the second parameter from the data string
const match = data.match(/,\s*(\d+)\s*\]/);

if (match && match[1]) {
amount = parseInt(match[1], 10);
}

return {
amount,
block_num: block_number,
block_timestamp: timestamp,
};
})
.sort((a, b) => b.block_timestamp - a.block_timestamp);
};

const fetchPayouts = async () => {
if (!activeAccount || !client) {
return;
}

const { data } = await client.query({
query: gql`
query RewardEvents($activeAccount: String) {
event(
where: {
section: { _eq: "staking" }
method: { _like: "Reward%" }
data: { _like: $activeAccount }
}
) {
block_number
data
timestamp
}
}
`,
variables: {
activeAccount: `%${activeAccount}%`,
},
});

// @ts-ignore
setPayouts(normalizePayouts(data.event));
};

useEffect(() => {
fetchPayouts();
}, [client, activeAccount]);

return payouts;
};

const CereStatsContext = createContext<CereStatsContextInterface>(
defaultCereStatsContext
);

export const useCereStats = () => React.useContext(CereStatsContext);

export const CereStatsProvider = ({
children,
}: {
children: React.ReactNode;
}) => {
const { network } = useApi();
const { activeAccount } = useConnect();

const client = useApolloClient(network.cereStatsEndpoint);
const fetchEraPoints = useFetchEraPoints(client);
const payouts = usePayouts(client, activeAccount);

if (!client) {
return null;
}

return (
<CereStatsContext.Provider
value={{
fetchEraPoints,
payouts,
poolClaims: [],
}}
>
{children}
</CereStatsContext.Provider>
);
};
7 changes: 7 additions & 0 deletions src/contexts/CereStats/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export interface CereStatsContextInterface {
fetchEraPoints: (v: string, e: number) => void;
payouts: any;
// The Cere Stats does not currently support `poolClaims`.
// We need it to maintain consistency with the `useSubscan` hook and for possible future support of `poolClaims`.
poolClaims: [];
}
4 changes: 2 additions & 2 deletions src/library/Graphs/PayoutBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ import { useUi } from 'contexts/UI';
import { useStaking } from 'contexts/Staking';
import { AnySubscan } from 'types';
import { usePoolMemberships } from 'contexts/Pools/PoolMemberships';
import { useSubscan } from 'contexts/Subscan';
import { useCereStats } from 'contexts/CereStats';
import { PayoutBarProps } from './types';
import { formatRewardsForGraphs } from './Utils';

Expand All @@ -50,7 +50,7 @@ export const PayoutBar = (props: PayoutBarProps) => {
const { isSyncing } = useUi();
const { inSetup } = useStaking();
const { membership } = usePoolMemberships();
const { payouts, poolClaims } = useSubscan();
const { payouts, poolClaims } = useCereStats();

const { units } = network;
const notStaking = !isSyncing && inSetup() && !membership;
Expand Down
4 changes: 2 additions & 2 deletions src/library/Graphs/PayoutLine.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import { useUi } from 'contexts/UI';
import { useStaking } from 'contexts/Staking';
import { usePoolMemberships } from 'contexts/Pools/PoolMemberships';
import { AnySubscan } from 'types';
import { useSubscan } from 'contexts/Subscan';
import { useCereStats } from 'contexts/CereStats';
import { PayoutLineProps } from './types';
import { combineRewardsByDay, formatRewardsForGraphs } from './Utils';

Expand All @@ -47,7 +47,7 @@ export const PayoutLine = (props: PayoutLineProps) => {
const { isSyncing } = useUi();
const { inSetup } = useStaking();
const { membership: poolMembership } = usePoolMemberships();
const { payouts, poolClaims } = useSubscan();
const { payouts, poolClaims } = useCereStats();

const { units } = network;
const notStaking = !isSyncing && inSetup() && !poolMembership;
Expand Down
6 changes: 3 additions & 3 deletions src/library/SubscanButton/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,18 +30,18 @@ export const SubscanButton = () => {
return (
<Wrapper
color={
services.includes('subscan')
services.includes('cereStats')
? networkColors[`${network.name}-${mode}`]
: defaultThemes.text.secondary[mode]
}
opacity={services.includes('subscan') ? 1 : 0.5}
opacity={services.includes('cereStats') ? 1 : 0.5}
>
<FontAwesomeIcon
icon={faProjectDiagram}
transform="shrink-2"
style={{ marginRight: '0.3rem' }}
/>
Subscan
Cere Stats
</Wrapper>
);
};
Expand Down
8 changes: 4 additions & 4 deletions src/modals/ValidatorMetrics/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

import React, { useState, useEffect } from 'react';
import { useModal } from 'contexts/Modal';
import { useSubscan } from 'contexts/Subscan';
import { useCereStats } from 'contexts/CereStats';
import { EraPoints as EraPointsGraph } from 'library/Graphs/EraPoints';
import { SubscanButton } from 'library/SubscanButton';
import { GraphWrapper } from 'library/Graphs/Wrappers';
Expand All @@ -16,7 +16,7 @@ import { StatusLabel } from 'library/StatusLabel';
export const ValidatorMetrics = () => {
const { config } = useModal();
const { address, identity } = config;
const { fetchEraPoints }: any = useSubscan();
const { fetchEraPoints }: any = useCereStats();
const { metrics } = useNetworkMetrics();

const [list, setList] = useState([]);
Expand Down Expand Up @@ -58,8 +58,8 @@ export const ValidatorMetrics = () => {
<div className="inner" ref={ref} style={{ minHeight }}>
<StatusLabel
status="active_service"
statusFor="subscan"
title="Subscan Disabled"
statusFor="cereStats"
title="Cere Stats Disabled"
/>
<div
className="graph"
Expand Down
6 changes: 3 additions & 3 deletions src/pages/Overview/Payouts.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,11 @@ export const Payouts = () => {

return (
<div className="inner" ref={ref} style={{ minHeight }}>
{!services.includes('subscan') ? (
{!services.includes('cereStats') ? (
<StatusLabel
status="active_service"
statusFor="subscan"
title="Subscan Disabled"
statusFor="cereStats"
title="Cere Stats Disabled"
topOffset="37%"
/>
) : (
Expand Down
Loading

0 comments on commit 00878c1

Please sign in to comment.