Skip to content

Commit

Permalink
Node Providers rewards XDR (#949)
Browse files Browse the repository at this point in the history
  • Loading branch information
pietrodimarco-dfinity authored Sep 20, 2024
1 parent 9a0cbe2 commit f985daa
Show file tree
Hide file tree
Showing 13 changed files with 382 additions and 150 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { ChartData, formatDateToUTC, generateChartData, LoadingIndicator, NodeMe
import { PeriodFilter } from './FilterBar';
import { Box, Grid } from '@mui/material';
import PerformanceChart from './PerformanceChart';
import { NodeRewardsResponse } from '../../../declarations/trustworthy-node-metrics/trustworthy-node-metrics.did';
import { NodeRewards } from '../../../declarations/trustworthy-node-metrics/trustworthy-node-metrics.did';
import { ExportTable } from './ExportTable';
import { GridColDef, GridRowsProp } from '@mui/x-data-grid';
import { Principal } from '@dfinity/principal';
Expand All @@ -16,27 +16,29 @@ export interface NodePerformanceChartProps {
}

export const NodePerformanceChart: React.FC<NodePerformanceChartProps> = ({ node, periodFilter }) => {
const [performanceData, setPerformanceData] = useState<NodeRewardsResponse[]>([]);
const [performanceData, setPerformanceData] = useState<NodeRewards | null>(null);
const [isLoading, setIsLoading] = useState(true);

useEffect(() => {
if (node) {
setNodeRewardsData(periodFilter, [Principal.fromText(node)], [], setPerformanceData, setIsLoading);
setNodeRewardsData(periodFilter, Principal.fromText(node), setPerformanceData, setIsLoading);
}
}, [node, periodFilter]);

if (isLoading) {
return <LoadingIndicator />;
}

if (performanceData.length == 0 || !node) {
if (!performanceData) {
return <p>No metrics for the time period selected</p>;
}
if (!node) {
return <p>No metrics for the time period selected</p>;
}

const performanceDailyData: ChartData[] = generateChartData(periodFilter, performanceData[0].daily_node_metrics);
const failureRateAvg = Math.round(performanceData[0].rewards_computation.failure_rate * 100);
const performanceDailyData: ChartData[] = generateChartData(periodFilter, performanceData.daily_node_metrics);
const failureRateAvg = Math.round(performanceData.rewards_computation.failure_rate * 100);

const rows: GridRowsProp = performanceData[0].daily_node_metrics.map((data, index) => {
const rows: GridRowsProp = performanceData.daily_node_metrics.map((data, index) => {
return {
id: index + 1,
col1: new Date(Number(data.ts) / 1000000),
Expand All @@ -60,12 +62,12 @@ export const NodePerformanceChart: React.FC<NodePerformanceChartProps> = ({ node
return (
<>
<Grid item xs={12} md={6}>
<NodeMetricsStats stats={performanceData[0].rewards_computation} />
<NodeMetricsStats stats={performanceData.rewards_computation} />
</Grid>
<Grid item xs={12} md={6}>
<Box sx={boxStyleWidget('right')}>
<WidgetNumber value={failureRateAvg.toString().concat("%")} title="Average Failure Rate" />
</Box>
</Box>
</Grid>
<Grid item xs={12} md={12}>
<PerformanceChart chartDailyData={performanceDailyData} />
Expand Down
Original file line number Diff line number Diff line change
@@ -1,36 +1,57 @@
import React, { useEffect, useState } from 'react';
import { Grid } from '@mui/material';
import { axisClasses, BarChart } from '@mui/x-charts';
import { formatDateToUTC, generateChartData, getFormattedDates, LoadingIndicator, setNodeRewardsData } from '../utils/utils';
import { dateToNanoseconds, formatDateToUTC, generateChartData, getFormattedDates, LoadingIndicator } from '../utils/utils';
import { PeriodFilter } from './FilterBar';
import { NodeRewardsResponse } from '../../../declarations/trustworthy-node-metrics/trustworthy-node-metrics.did';
import { NodeProviderRewards, NodeProviderRewardsArgs } from '../../../declarations/trustworthy-node-metrics/trustworthy-node-metrics.did';
import { ExportTable } from './ExportTable';
import { GridColDef, GridRowsProp } from '@mui/x-data-grid';
import { Principal } from '@dfinity/principal';
import { trustworthy_node_metrics } from '../../../declarations/trustworthy-node-metrics';

export interface NodeProviderChartProps {
provider: string,
periodFilter: PeriodFilter
}

export const NodeProviderChart: React.FC<NodeProviderChartProps> = ({ provider, periodFilter }) => {
const [providerNodeMetrics, setProviderNodeMetrics] = useState<NodeRewardsResponse[]>([]);
const [providerRewards, setProviderRewards] = useState<NodeProviderRewards | null>(null);
const [isLoading, setIsLoading] = useState(true);

useEffect(() => {
if (provider) {
setNodeRewardsData(periodFilter, [], [Principal.fromText(provider)], setProviderNodeMetrics, setIsLoading);
const updateNodeProviderRewards = async () => {
try {
setIsLoading(true);

const request: NodeProviderRewardsArgs = {
from_ts: dateToNanoseconds(periodFilter.dateStart),
to_ts: dateToNanoseconds(periodFilter.dateEnd),
node_provider_id: Principal.from(provider),
};
const nodeRewardsResponse = await trustworthy_node_metrics.node_provider_rewards(request);

setProviderRewards(nodeRewardsResponse);
} catch (error) {
console.error("Error fetching node:", error);
} finally {
setIsLoading(false);
}
};
updateNodeProviderRewards();
}
}, [periodFilter, provider]);

if (isLoading) {
return <LoadingIndicator />;
}

if (providerNodeMetrics.length == 0) {
if (!providerRewards) {
return <p>No metrics for the time period selected</p>;
}

const providerNodeMetrics = providerRewards.nodes_rewards;

const highFailureRateChart = providerNodeMetrics
.sort((a, b) => b.rewards_computation.failure_rate - a.rewards_computation.failure_rate)
.slice(0, 3)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,10 @@ import { Link, useParams } from 'react-router-dom';
import { getDateRange } from '../utils/utils';
import FilterBar, { PeriodFilter } from './FilterBar';
import { NodeMetadata } from '../../../declarations/trustworthy-node-metrics/trustworthy-node-metrics.did';
import { paperStyle } from '../Styles';
import InfoFormatter from './NodeInfo';
import { NodeProviderChart } from './NodeProviderChart';
import { NodeProviderRewardsChart } from './NodeProviderRewards';
import { paperStyle } from '../Styles';

export interface NodeProviderPageProps {
nodeMetadata: NodeMetadata[]
Expand Down Expand Up @@ -47,7 +48,7 @@ export const NodeProviderPage: React.FC<NodeProviderPageProps> = ({ nodeMetadata
<InfoFormatter name={"Provider ID"} value={provider ? provider : "Anonym"} />
<InfoFormatter name={"Provider Name"} value={providerName ? providerName : "Anonym"} />
</Grid>
<Grid item xs={12} md={6}>
<Grid item xs={12}>
<Typography gutterBottom variant="subtitle1" component="div">
Node Machines
</Typography>
Expand All @@ -60,6 +61,13 @@ export const NodeProviderPage: React.FC<NodeProviderPageProps> = ({ nodeMetadata
))}

</Grid>
<Grid item xs={12}>
<Typography variant="h6" component="div" >
Last Provider Rewards
</Typography>
<Divider/>
</Grid>
<NodeProviderRewardsChart provider={provider} />
<Grid item xs={12}>
<Typography variant="h6" component="div">
Daily Failure Rate
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import React, { useEffect, useState } from 'react';
import { getLatestRewardRange, LoadingIndicator, setNodeProviderRewardsData } from '../utils/utils';
import { Box, Grid } from '@mui/material';
import { Principal } from '@dfinity/principal';
import { NodeProviderRewards } from '../../../declarations/trustworthy-node-metrics/trustworthy-node-metrics.did';
import { WidgetNumber } from './Widgets';
import { boxStyleWidget } from '../Styles';

export interface NodeProviderRewardsChartProps {
provider: string;
}

export const NodeProviderRewardsChart: React.FC<NodeProviderRewardsChartProps> = ({ provider }) => {
const latestRewardRange = getLatestRewardRange();
const [latestProviderRewards, setLatestProviderRewards] = useState<NodeProviderRewards | null>(null);
const [isLoading, setIsLoading] = useState(true);

useEffect(() => {
if (provider) {
setNodeProviderRewardsData(latestRewardRange, Principal.fromText(provider), setLatestProviderRewards, setIsLoading);
}
}, [provider]);

if (isLoading) {
return <LoadingIndicator />;
}

if (!latestProviderRewards) {
return <p>No latestNodeRewards</p>;
}

return (
<Grid item xs={12}>
<Box sx={boxStyleWidget('right')}>
<WidgetNumber value={Math.round(latestProviderRewards.rewards_xdr).toString()} title="Rewards XDR" sxValue={{ color: '#FFCC00' }} />
</Box>
</Grid>
);
};

export default NodeProviderRewardsChart;
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import React, { useEffect, useState } from 'react';
import { ChartData, generateChartData, getLatestRewardRange, LoadingIndicator, NodeMetricsStats, NodePerformanceStats, setNodeRewardsData } from '../utils/utils';
import { Grid } from '@mui/material';
import PerformanceChart from './PerformanceChart';
import { NodeRewardsResponse } from '../../../declarations/trustworthy-node-metrics/trustworthy-node-metrics.did';
import { NodeRewards } from '../../../declarations/trustworthy-node-metrics/trustworthy-node-metrics.did';
import RewardsInfo, { LinearReductionChart } from './RewardsInfo';
import { Principal } from '@dfinity/principal';

Expand All @@ -12,12 +12,12 @@ export interface NodeRewardsChartProps {

export const NodeRewardsChart: React.FC<NodeRewardsChartProps> = ({ node }) => {
const latestRewardRange = getLatestRewardRange();
const [latestNodeRewards, setLatestNodeRewards] = useState<NodeRewardsResponse[]>([]);
const [latestNodeRewards, setLatestNodeRewards] = useState<NodeRewards | null>(null);
const [isLoading, setIsLoading] = useState(true);

useEffect(() => {
if (node) {
setNodeRewardsData(latestRewardRange, [Principal.fromText(node)], [], setLatestNodeRewards, setIsLoading);
setNodeRewardsData(latestRewardRange, Principal.fromText(node), setLatestNodeRewards, setIsLoading);
}
}, [node]);

Expand All @@ -26,11 +26,14 @@ export const NodeRewardsChart: React.FC<NodeRewardsChartProps> = ({ node }) => {
return <LoadingIndicator />;
}

const rewards = latestNodeRewards[0];
const rewardsDailyData: ChartData[] = generateChartData(latestRewardRange, rewards ? rewards.daily_node_metrics : []);
const daysAssigned = rewards ? rewards.daily_node_metrics.length : 0;
const failureRateAvg = Math.round((rewards ? rewards.rewards_computation.failure_rate : 0) * 100)
const rewardsPercent = Math.round((rewards ? rewards.rewards_computation.rewards_percent : 1) * 100);
if (!latestNodeRewards) {
return <p>No latestNodeRewards</p>;
}

const rewardsDailyData: ChartData[] = generateChartData(latestRewardRange, latestNodeRewards.daily_node_metrics);
const daysAssigned = latestNodeRewards.daily_node_metrics.length;
const failureRateAvg = Math.round((latestNodeRewards.rewards_computation.failure_rate) * 100)
const rewardsPercent = Math.round((latestNodeRewards.rewards_computation.rewards_percent) * 100);
const rewardsReduction = 100 - rewardsPercent;
const millisecondsPerDay = 24 * 60 * 60 * 1000;
const daysTotal = Math.round((latestRewardRange.dateEnd.getTime() - latestRewardRange.dateStart.getTime()) / millisecondsPerDay);
Expand All @@ -39,10 +42,13 @@ export const NodeRewardsChart: React.FC<NodeRewardsChartProps> = ({ node }) => {
return (
<>
<Grid item xs={12} md={6}>
<NodeMetricsStats stats={rewards ? rewards.rewards_computation : null} />
<NodeMetricsStats stats={latestNodeRewards.rewards_computation} />
</Grid>
<Grid item xs={12} md={6}>
<NodePerformanceStats failureRateAvg={failureRateAvg.toString().concat("%")} rewardMultiplier={rewardMultiplier.toString().concat("%")} />
<NodePerformanceStats
failureRateAvg={failureRateAvg.toString().concat("%")}
rewardMultiplier={rewardMultiplier.toString().concat("%")}
baseRewardsXDR={latestNodeRewards.node_rate.xdr_permyriad_per_node_per_month.toString()} />
</Grid>
<Grid item xs={12} md={6}>
<PerformanceChart chartDailyData={rewardsDailyData} />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@ import TableHead from '@mui/material/TableHead';
import TableRow from '@mui/material/TableRow';
import Paper from '@mui/material/Paper';
import { SxProps, Theme } from '@mui/material';
import { NodeRewardsResponse } from '../../../declarations/trustworthy-node-metrics/trustworthy-node-metrics.did';
import { NodeRewards } from '../../../declarations/trustworthy-node-metrics/trustworthy-node-metrics.did';

interface RewardTableProps {
nodeRewards: NodeRewardsResponse[],
nodeRewards: NodeRewards[],
sx?: SxProps<Theme>;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React from 'react';
import { Principal } from "@dfinity/principal";
import { trustworthy_node_metrics } from "../../../declarations/trustworthy-node-metrics";
import { DailyNodeMetrics, NodeRewardsArgs, NodeRewardsResponse } from "../../../declarations/trustworthy-node-metrics/trustworthy-node-metrics.did";
import { DailyNodeMetrics, NodeRewardsArgs, NodeRewards, NodeProviderRewardsArgs, NodeProviderRewards } from "../../../declarations/trustworthy-node-metrics/trustworthy-node-metrics.did";
import { PeriodFilter } from "../components/FilterBar";
import { Box, CircularProgress } from "@mui/material";
import { WidgetNumber } from '../components/Widgets';
Expand Down Expand Up @@ -129,9 +129,8 @@ export const getLatestRewardRange = () => {

export const setNodeRewardsData = async (
periodFilter: PeriodFilter,
node_id: [] | [Principal],
node_provider_id: [] | [Principal],
setNodeRewards: React.Dispatch<React.SetStateAction<NodeRewardsResponse[]>>,
node_id: Principal,
setNodeRewards: React.Dispatch<React.SetStateAction<NodeRewards | null>>,
setIsLoading: React.Dispatch<React.SetStateAction<boolean>>) => {
try {
setIsLoading(true);
Expand All @@ -140,7 +139,6 @@ export const setNodeRewardsData = async (
from_ts: dateToNanoseconds(periodFilter.dateStart),
to_ts: dateToNanoseconds(periodFilter.dateEnd),
node_id: node_id,
node_provider_id: node_provider_id,
};
const nodeRewardsResponse = await trustworthy_node_metrics.node_rewards(request);

Expand All @@ -152,6 +150,29 @@ export const setNodeRewardsData = async (
}
};

export const setNodeProviderRewardsData = async (
periodFilter: PeriodFilter,
provider: Principal,
setProviderRewards: React.Dispatch<React.SetStateAction<NodeProviderRewards | null>>,
setIsLoading: React.Dispatch<React.SetStateAction<boolean>>) => {
try {
setIsLoading(true);

const request: NodeProviderRewardsArgs = {
from_ts: dateToNanoseconds(periodFilter.dateStart),
to_ts: dateToNanoseconds(periodFilter.dateEnd),
node_provider_id: Principal.from(provider),
};
const nodeRewardsResponse = await trustworthy_node_metrics.node_provider_rewards(request);

setProviderRewards(nodeRewardsResponse);
} catch (error) {
console.error("Error fetching node:", error);
} finally {
setIsLoading(false);
}
};

export const LoadingIndicator: React.FC = () => (
<Box
sx={{
Expand All @@ -165,17 +186,18 @@ export const LoadingIndicator: React.FC = () => (
</Box>
);

export const NodeMetricsStats: React.FC<{ stats: NodeRewardsResponse['rewards_computation'] | null }> = ({ stats }) => (
export const NodeMetricsStats: React.FC<{ stats: NodeRewards['rewards_computation'] | null }> = ({ stats }) => (
<Box sx={boxStyleWidget('left')}>
<WidgetNumber value={stats ? stats.blocks_proposed.toString() : "0"} title="Blocks Proposed Total" />
<WidgetNumber value={stats ? stats.blocks_failed.toString() : "0"} title="Blocks Failed Total" />
</Box>
);

export const NodePerformanceStats: React.FC<{ failureRateAvg: string, rewardMultiplier: string }> = ({ failureRateAvg, rewardMultiplier }) => (
export const NodePerformanceStats: React.FC<{ failureRateAvg: string, rewardMultiplier: string , baseRewardsXDR: string}> = ({ failureRateAvg, rewardMultiplier, baseRewardsXDR }) => (
<Box sx={boxStyleWidget('right')}>
<WidgetNumber value={failureRateAvg} title="Average Failure Rate" />
<WidgetNumber value={rewardMultiplier} title="Reward multiplier" sxValue={{ color: '#FFCC00' }} />
<WidgetNumber value={rewardMultiplier} title="Reward Multiplier" sxValue={{ color: '#FFCC00' }} />
<WidgetNumber value={baseRewardsXDR} title="Base Monthly Rewards XDR" sxValue={{ color: '#FFCC00' }} />
</Box>
);

Expand Down
Loading

0 comments on commit f985daa

Please sign in to comment.