Bug fix/ne snag list (#1741)
* use uncapped saturation * display uncapped saturation update profit margin tooltip update operating cost update rewards tooltip update stake saturation tooltip update reward tooltips update profit margin tooltip * allow full gateway field to be clickable use uncapped saturation on mixnode details page
This commit is contained in:
@@ -41,6 +41,8 @@ pub(crate) async fn retrieve_mixnode_econ_stats(
|
||||
|
||||
Some(EconomicDynamicsStats {
|
||||
stake_saturation: best_effort_small_dec_to_f64(stake_saturation.saturation) as f32,
|
||||
uncapped_saturation: best_effort_small_dec_to_f64(stake_saturation.uncapped_saturation)
|
||||
as f32,
|
||||
active_set_inclusion_probability: inclusion_probability.in_active,
|
||||
reserve_set_inclusion_probability: inclusion_probability.in_reserve,
|
||||
// drop precision for compatibility sake
|
||||
|
||||
@@ -34,6 +34,7 @@ pub(crate) struct PrettyDetailedMixNodeBond {
|
||||
pub layer: Layer,
|
||||
pub mix_node: MixNode,
|
||||
pub stake_saturation: f32,
|
||||
pub uncapped_saturation: f32,
|
||||
pub avg_uptime: u8,
|
||||
pub estimated_operator_apy: f64,
|
||||
pub estimated_delegators_apy: f64,
|
||||
@@ -153,6 +154,7 @@ pub(crate) struct NodeStats {
|
||||
#[derive(Clone, Serialize, Deserialize, JsonSchema)]
|
||||
pub(crate) struct EconomicDynamicsStats {
|
||||
pub(crate) stake_saturation: f32,
|
||||
pub(crate) uncapped_saturation: f32,
|
||||
|
||||
pub(crate) active_set_inclusion_probability: SelectionChance,
|
||||
pub(crate) reserve_set_inclusion_probability: SelectionChance,
|
||||
|
||||
@@ -151,6 +151,8 @@ impl ThreadsafeMixNodesCache {
|
||||
mix_node: node.mixnode_details.bond_information.mix_node.clone(),
|
||||
avg_uptime: node.performance.round_to_integer(),
|
||||
stake_saturation: best_effort_small_dec_to_f64(node.stake_saturation) as f32,
|
||||
uncapped_saturation: best_effort_small_dec_to_f64(node.uncapped_stake_saturation)
|
||||
as f32,
|
||||
estimated_operator_apy: best_effort_small_dec_to_f64(node.estimated_operator_apy),
|
||||
estimated_delegators_apy: best_effort_small_dec_to_f64(node.estimated_delegators_apy),
|
||||
operating_cost: rewarding_info.cost_params.interval_operating_cost.clone(),
|
||||
|
||||
@@ -6,14 +6,16 @@ export const EconomicsInfoColumns: ColumnsType[] = [
|
||||
title: 'Estimated Total Reward',
|
||||
flex: 1,
|
||||
headerAlign: 'left',
|
||||
tooltipInfo: 'Estimated reward per epoch for this profit margin if your node is selected in the active set.',
|
||||
tooltipInfo:
|
||||
'Estimated node reward (total for the operator and delegators) in the current epoch. There are roughly 24 epochs in a day.',
|
||||
},
|
||||
{
|
||||
field: 'estimatedOperatorReward',
|
||||
title: 'Estimated Operator Reward',
|
||||
flex: 1,
|
||||
headerAlign: 'left',
|
||||
tooltipInfo: 'Estimated reward per epoch for this profit margin if your node is selected in the active set.',
|
||||
tooltipInfo:
|
||||
"Estimated operator's reward (including PM and Operating Cost) in the current epoch. There are roughly 24 epochs in a day.",
|
||||
},
|
||||
{
|
||||
field: 'selectionChance',
|
||||
@@ -29,7 +31,7 @@ export const EconomicsInfoColumns: ColumnsType[] = [
|
||||
flex: 1,
|
||||
headerAlign: 'left',
|
||||
tooltipInfo:
|
||||
'Level of stake saturation for this node. Nodes receive more rewards the higher their saturation level, up to 100%. Beyond 100% no additional rewards are granted. The current stake saturation level is: 1 million NYM, computed as S/K where S is total amount of tokens available to stakeholders and K is the number of nodes in the reward set.',
|
||||
'Level of stake saturation for this node. Nodes receive more rewards the higher their saturation level, up to 100%. Beyond 100% no additional rewards are granted. The current stake saturation level is: 750k NYM, computed as S/K where S is total amount of tokens available to stakeholders and K is the number of nodes in the reward set.',
|
||||
},
|
||||
{
|
||||
field: 'profitMargin',
|
||||
@@ -37,7 +39,7 @@ export const EconomicsInfoColumns: ColumnsType[] = [
|
||||
flex: 1,
|
||||
headerAlign: 'left',
|
||||
tooltipInfo:
|
||||
'Percentage of the delegates rewards that the operator takes as fee before rewards are distributed to the delegates.',
|
||||
'Percentage of the delegators rewards that the operator takes as fee before rewards are distributed to the delegators.',
|
||||
},
|
||||
{
|
||||
field: 'operatingCost',
|
||||
|
||||
@@ -4,23 +4,8 @@ import { ApiState, MixNodeEconomicDynamicsStatsResponse } from '../../../typeDef
|
||||
import { EconomicsInfoRowWithIndex } from './types';
|
||||
import { toPercentIntegerString } from '../../../utils';
|
||||
|
||||
const selectionChance = (economicDynamicsStats: ApiState<MixNodeEconomicDynamicsStatsResponse> | undefined) => {
|
||||
const inclusionProbability = economicDynamicsStats?.data?.active_set_inclusion_probability;
|
||||
// TODO: when v2 will be deployed, remove cases: VeryHigh, Moderate and VeryLow
|
||||
switch (inclusionProbability) {
|
||||
case 'VeryLow':
|
||||
return 'Very Low';
|
||||
case 'VeryHigh':
|
||||
return 'Very High';
|
||||
case 'High':
|
||||
case 'Good':
|
||||
case 'Low':
|
||||
case 'Moderate':
|
||||
return inclusionProbability;
|
||||
default:
|
||||
return '-';
|
||||
}
|
||||
};
|
||||
const selectionChance = (economicDynamicsStats: ApiState<MixNodeEconomicDynamicsStatsResponse> | undefined) =>
|
||||
economicDynamicsStats?.data?.active_set_inclusion_probability || '-';
|
||||
|
||||
export const EconomicsInfoRows = (): EconomicsInfoRowWithIndex => {
|
||||
const { economicDynamicsStats, mixNode } = useMixnodeContext();
|
||||
@@ -29,7 +14,7 @@ export const EconomicsInfoRows = (): EconomicsInfoRowWithIndex => {
|
||||
currencyToString((economicDynamicsStats?.data?.estimated_total_node_reward || '').toString()) || '-';
|
||||
const estimatedOperatorRewards =
|
||||
currencyToString((economicDynamicsStats?.data?.estimated_operator_reward || '').toString()) || '-';
|
||||
const stakeSaturation = economicDynamicsStats?.data?.stake_saturation || '-';
|
||||
const stakeSaturation = economicDynamicsStats?.data?.uncapped_saturation || '-';
|
||||
const profitMargin = mixNode?.data?.profit_margin_percent
|
||||
? toPercentIntegerString(mixNode?.data?.profit_margin_percent)
|
||||
: '-';
|
||||
|
||||
@@ -31,7 +31,7 @@ export function mixNodeResponseItemToMixnodeRowType(item: MixNodeResponseItem):
|
||||
const totalBond = pledge + delegations;
|
||||
const selfPercentage = ((pledge * 100) / totalBond).toFixed(2);
|
||||
const profitPercentage = toPercentIntegerString(item.profit_margin_percent) || 0;
|
||||
const stakeSaturation = typeof item.stake_saturation === 'number' ? item.stake_saturation * 100 : 0;
|
||||
const uncappedSaturation = typeof item.uncapped_saturation === 'number' ? item.uncapped_saturation * 100 : 0;
|
||||
|
||||
return {
|
||||
mix_id: item.mix_id,
|
||||
@@ -47,7 +47,7 @@ export function mixNodeResponseItemToMixnodeRowType(item: MixNodeResponseItem):
|
||||
layer: item?.layer || '',
|
||||
profit_percentage: `${profitPercentage}%`,
|
||||
avg_uptime: `${item.avg_uptime}%` || '-',
|
||||
stake_saturation: stakeSaturation,
|
||||
operating_cost: `${unymToNym(item.operating_cost.amount, 6)} NYM`,
|
||||
stake_saturation: uncappedSaturation,
|
||||
operating_cost: `${unymToNym(item.operating_cost?.amount, 6)} NYM`,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import * as React from 'react';
|
||||
import { Link as RRDLink } from 'react-router-dom';
|
||||
import { Box, Button, Card, Grid, Typography, Link as MuiLink } from '@mui/material';
|
||||
import { Box, Button, Card, Grid, Link as MuiLink } from '@mui/material';
|
||||
import { GridColDef, GridRenderCellParams } from '@mui/x-data-grid';
|
||||
import { SelectChangeEvent } from '@mui/material/Select';
|
||||
import { useMainContext } from '../../context/main';
|
||||
@@ -12,6 +12,8 @@ import { Title } from '../../components/Title';
|
||||
import { cellStyles, UniversalDataGrid } from '../../components/Universal-DataGrid';
|
||||
import { currencyToString } from '../../utils/currency';
|
||||
import { Tooltip } from '../../components/Tooltip';
|
||||
import { BIG_DIPPER } from '../../api/constants';
|
||||
import { splice } from '../../utils';
|
||||
|
||||
export const PageGateways: React.FC = () => {
|
||||
const { gateways } = useMainContext();
|
||||
@@ -69,9 +71,14 @@ export const PageGateways: React.FC = () => {
|
||||
headerClassName: 'MuiDataGrid-header-override',
|
||||
headerAlign: 'left',
|
||||
renderCell: (params: GridRenderCellParams) => (
|
||||
<Typography sx={cellStyles} data-testid="pledge-amount">
|
||||
<MuiLink
|
||||
sx={{ ...cellStyles }}
|
||||
component={RRDLink}
|
||||
to={`/network-components/gateway/${params.row.identityKey}`}
|
||||
data-testid="pledge-amount"
|
||||
>
|
||||
{currencyToString(params.value)}
|
||||
</Typography>
|
||||
</MuiLink>
|
||||
),
|
||||
},
|
||||
{
|
||||
@@ -81,9 +88,14 @@ export const PageGateways: React.FC = () => {
|
||||
headerAlign: 'left',
|
||||
headerClassName: 'MuiDataGrid-header-override',
|
||||
renderCell: (params: GridRenderCellParams) => (
|
||||
<Typography sx={cellStyles} data-testid="host">
|
||||
<MuiLink
|
||||
sx={{ ...cellStyles }}
|
||||
component={RRDLink}
|
||||
to={`/network-components/gateway/${params.row.identityKey}`}
|
||||
data-testid="host"
|
||||
>
|
||||
{params.value}
|
||||
</Typography>
|
||||
</MuiLink>
|
||||
),
|
||||
},
|
||||
{
|
||||
@@ -120,9 +132,14 @@ export const PageGateways: React.FC = () => {
|
||||
headerAlign: 'left',
|
||||
headerClassName: 'MuiDataGrid-header-override',
|
||||
renderCell: (params: GridRenderCellParams) => (
|
||||
<Typography sx={cellStyles} data-testid="owner">
|
||||
{params.value}
|
||||
</Typography>
|
||||
<MuiLink
|
||||
sx={{ ...cellStyles }}
|
||||
href={`${BIG_DIPPER}/account/${params.value}`}
|
||||
target="_blank"
|
||||
data-testid="owner"
|
||||
>
|
||||
{splice(7, 29, params.value)}
|
||||
</MuiLink>
|
||||
),
|
||||
},
|
||||
];
|
||||
|
||||
@@ -35,7 +35,7 @@ const columns: ColumnsType[] = [
|
||||
},
|
||||
{
|
||||
field: 'self_percentage',
|
||||
title: 'Self %',
|
||||
title: 'Bond %',
|
||||
headerAlign: 'left',
|
||||
width: 99,
|
||||
},
|
||||
|
||||
@@ -149,7 +149,7 @@ export const PageMixnodes: React.FC = () => {
|
||||
renderHeader: () => (
|
||||
<CustomColumnHeading
|
||||
headingTitle="Stake Saturation"
|
||||
tooltipInfo="Level of stake saturation for this node. Nodes receive more rewards the higher their saturation level, up to 100%. Beyond 100% no additional rewards are granted. The current stake saturation level is: 1 million NYM, computed as S/K where S is total amount of tokens available to stakeholders and K is the number of nodes in the reward set."
|
||||
tooltipInfo="Level of stake saturation for this node. Nodes receive more rewards the higher their saturation level, up to 100%. Beyond 100% no additional rewards are granted. The current stake saturation level is: 750k NYM, computed as S/K where S is total amount of tokens available to stakeholders and K is the number of nodes in the reward set."
|
||||
/>
|
||||
),
|
||||
headerClassName: 'MuiDataGrid-header-override',
|
||||
@@ -192,7 +192,7 @@ export const PageMixnodes: React.FC = () => {
|
||||
renderHeader: () => (
|
||||
<CustomColumnHeading
|
||||
headingTitle="Profit Margin"
|
||||
tooltipInfo="Percentage of the delegates rewards that the operator takes as fee before rewards are distributed to the delegates."
|
||||
tooltipInfo="Percentage of the delegators rewards that the operator takes as fee before rewards are distributed to the delegators."
|
||||
/>
|
||||
),
|
||||
headerClassName: 'MuiDataGrid-header-override',
|
||||
@@ -210,10 +210,10 @@ export const PageMixnodes: React.FC = () => {
|
||||
},
|
||||
{
|
||||
field: 'operating_cost',
|
||||
headerName: 'Operating cost',
|
||||
headerName: 'Operating Cost',
|
||||
renderHeader: () => (
|
||||
<CustomColumnHeading
|
||||
headingTitle="Operating cost"
|
||||
headingTitle="Operating Cost"
|
||||
tooltipInfo="Monthly operational cost of running this node. This cost is set by the operator and it influences how the rewards are split between the operator and delegators."
|
||||
/>
|
||||
),
|
||||
|
||||
@@ -85,6 +85,7 @@ export interface MixNodeResponseItem {
|
||||
mix_node: MixNode;
|
||||
avg_uptime: number;
|
||||
stake_saturation: number;
|
||||
uncapped_saturation: number;
|
||||
operating_cost: Amount;
|
||||
profit_margin_percent: string;
|
||||
}
|
||||
@@ -214,8 +215,9 @@ export type UptimeStoryResponse = {
|
||||
|
||||
export type MixNodeEconomicDynamicsStatsResponse = {
|
||||
stake_saturation: number;
|
||||
uncapped_saturation: number;
|
||||
// TODO: when v2 will be deployed, remove cases: VeryHigh, Moderate and VeryLow
|
||||
active_set_inclusion_probability: 'High' | 'Good' | 'Low' | 'VeryLow' | 'Moderate' | 'VeryHigh';
|
||||
active_set_inclusion_probability: 'High' | 'Good' | 'Low';
|
||||
reserve_set_inclusion_probability: 'High' | 'Good' | 'Low';
|
||||
estimated_total_node_reward: number;
|
||||
estimated_operator_reward: number;
|
||||
|
||||
@@ -179,7 +179,7 @@ const AmountFormData = ({
|
||||
<CurrencyFormField
|
||||
required
|
||||
fullWidth
|
||||
label="Operator cost"
|
||||
label="Operator Cost"
|
||||
autoFocus
|
||||
onChanged={(newValue) => setValue('operatorCost', newValue, { shouldValidate: true })}
|
||||
validationError={errors.operatorCost?.amount?.message}
|
||||
|
||||
@@ -152,7 +152,7 @@ export const SystemVariables = ({
|
||||
|
||||
<DataField
|
||||
title="Node stake saturation"
|
||||
info="Level of stake saturation for this node. Nodes receive more rewards the higher their saturation level, up to 100%. Beyond 100% no additional rewards are granted. The current stake saturation level is: 1 million NYM, computed as S/K where S is the total amount of tokens available to stakeholders and K is the number of nodes in the reward set."
|
||||
info="Level of stake saturation for this node. Nodes receive more rewards the higher their saturation level, up to 100%. Beyond 100% no additional rewards are granted. The current stake saturation level is: 750k NYM, computed as S/K where S is the total amount of tokens available to stakeholders and K is the number of nodes in the reward set."
|
||||
Indicator={<PercentIndicator value={saturation} warning={saturation >= 100} />}
|
||||
/>
|
||||
</Stack>
|
||||
|
||||
Reference in New Issue
Block a user