fix up operating cost (#1698)
This commit is contained in:
@@ -33,7 +33,7 @@ const headers: Header[] = [
|
||||
'The percentage of the node rewards that you as the node operator will take before the rest of the reward is shared between you and the delegators.',
|
||||
},
|
||||
{
|
||||
header: 'Operator cost',
|
||||
header: 'Operating cost',
|
||||
id: 'operator-cost',
|
||||
// tooltipText: 'TODO', // TODO
|
||||
},
|
||||
@@ -94,7 +94,7 @@ export const BondedMixnode = ({
|
||||
id: 'pm-cell',
|
||||
},
|
||||
{
|
||||
cell: operatorCost ? `${operatorCost} NYM` : '-',
|
||||
cell: operatorCost ? `${operatorCost.amount} ${operatorCost.denom}` : '-',
|
||||
id: 'operator-cost-cell',
|
||||
},
|
||||
{
|
||||
|
||||
@@ -37,15 +37,17 @@ export const mixnodeValidationSchema = Yup.object().shape({
|
||||
|
||||
const operatingCostAndPmValidation = {
|
||||
profitMargin: Yup.number().required('Profit Percentage is required').min(0).max(100),
|
||||
operatorCost: Yup.string()
|
||||
.required('An operating cost is required')
|
||||
.test('valid-operating-cost', 'A valid amount is required (min 40)', async function isValidAmount(this, value) {
|
||||
if (value && (!Number(value) || isLessThan(+value, 40))) {
|
||||
return false;
|
||||
}
|
||||
operatorCost: Yup.object().shape({
|
||||
amount: Yup.string()
|
||||
.required('An operating cost is required')
|
||||
.test('valid-operating-cost', 'A valid amount is required (min 40)', async function isValidAmount(this, value) {
|
||||
if (value && (!Number(value) || isLessThan(+value, 40))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}),
|
||||
return true;
|
||||
}),
|
||||
}),
|
||||
};
|
||||
|
||||
export const amountSchema = Yup.object().shape({
|
||||
|
||||
@@ -54,7 +54,7 @@ export type TBondedMixnode = {
|
||||
delegators: number;
|
||||
status: MixnodeStatus;
|
||||
proxy?: string;
|
||||
operatorCost?: string;
|
||||
operatorCost: DecCoin;
|
||||
host: string;
|
||||
estimatedRewards?: DecCoin;
|
||||
activeSetProbability?: SelectionChance;
|
||||
@@ -141,7 +141,6 @@ export const BondingContextProvider = ({ children }: { children?: React.ReactNod
|
||||
const additionalDetails: {
|
||||
status: MixnodeStatus;
|
||||
stakeSaturation: string;
|
||||
operatorCost?: string;
|
||||
estimatedRewards?: DecCoin;
|
||||
} = {
|
||||
status: 'not_found',
|
||||
@@ -163,7 +162,6 @@ export const BondingContextProvider = ({ children }: { children?: React.ReactNod
|
||||
}
|
||||
try {
|
||||
const rewardEstimation = await getMixnodeRewardEstimation(mixId);
|
||||
additionalDetails.operatorCost = unymToNym(rewardEstimation.estimation.operating_cost);
|
||||
const estimatedRewards = unymToNym(rewardEstimation.estimation.total_node_reward);
|
||||
if (estimatedRewards) {
|
||||
additionalDetails.estimatedRewards = {
|
||||
@@ -240,7 +238,7 @@ export const BondingContextProvider = ({ children }: { children?: React.ReactNod
|
||||
bond_information: { mix_id },
|
||||
} = data;
|
||||
|
||||
const { status, stakeSaturation, operatorCost, estimatedRewards } = await getAdditionalMixnodeDetails(mix_id);
|
||||
const { status, stakeSaturation, estimatedRewards } = await getAdditionalMixnodeDetails(mix_id);
|
||||
const setProbabilities = await getSetProbabilities(mix_id);
|
||||
const nodeDescription = await getNodeDescription(
|
||||
bond_information.mix_node.host,
|
||||
@@ -261,7 +259,7 @@ export const BondingContextProvider = ({ children }: { children?: React.ReactNod
|
||||
operatorRewards,
|
||||
status,
|
||||
stakeSaturation,
|
||||
operatorCost,
|
||||
operatorCost: rewarding_details.cost_params.interval_operating_cost,
|
||||
host: bond_information.mix_node.host.replace(/\s/g, ''),
|
||||
routingScore,
|
||||
activeSetProbability: setProbabilities?.in_active,
|
||||
|
||||
@@ -16,7 +16,7 @@ const bondedMixnodeMock: TBondedMixnode = {
|
||||
operatorRewards: { denom: 'nym', amount: '1234' },
|
||||
delegators: 5423,
|
||||
status: 'active',
|
||||
operatorCost: '0.22',
|
||||
operatorCost: { denom: 'nym', amount: '1234' },
|
||||
host: '1.2.3.4',
|
||||
routingScore: 75,
|
||||
activeSetProbability: 'High',
|
||||
|
||||
+26
-26
@@ -1,4 +1,4 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import React, { useContext, useEffect, useState } from 'react';
|
||||
import { useForm } from 'react-hook-form';
|
||||
import { yupResolver } from '@hookform/resolvers/yup';
|
||||
import {
|
||||
@@ -13,38 +13,42 @@ import {
|
||||
FormHelperText,
|
||||
} from '@mui/material';
|
||||
import { useTheme } from '@mui/material/styles';
|
||||
import { CurrencyDenom, MixNodeCostParams } from '@nymproject/types';
|
||||
import { add, format, fromUnixTime } from 'date-fns';
|
||||
import { isMixnode } from 'src/types';
|
||||
import { getCurrentInterval, getPendingIntervalEvents, updateMixnodeCostParams } from 'src/requests';
|
||||
import { TBondedMixnode, TBondedGateway } from 'src/context/bonding';
|
||||
import { SimpleModal } from 'src/components/Modals/SimpleModal';
|
||||
import { bondedNodeParametersValidationSchema } from 'src/components/Bonding/forms/mixnodeValidationSchema';
|
||||
import { Console } from 'src/utils/console';
|
||||
import { add, format, fromUnixTime } from 'date-fns';
|
||||
import { Alert } from 'src/components/Alert';
|
||||
import { ChangeMixCostParams } from 'src/pages/bonding/types';
|
||||
import { MixNodeCostParams } from '@nymproject/types';
|
||||
import { AppContext } from 'src/context';
|
||||
import { CurrencyFormField } from '@nymproject/react/currency/CurrencyFormField';
|
||||
|
||||
export const ParametersSettings = ({ bondedNode }: { bondedNode: TBondedMixnode | TBondedGateway }): JSX.Element => {
|
||||
export const ParametersSettings = ({ bondedNode }: { bondedNode: TBondedMixnode }): JSX.Element => {
|
||||
const [openConfirmationModal, setOpenConfirmationModal] = useState<boolean>(false);
|
||||
const [intervalTime, setIntervalTime] = useState<string>();
|
||||
const [nextEpoch, setNextEpoch] = useState<string>();
|
||||
const [pendingUpdates, setPendingUpdates] = useState<MixNodeCostParams>();
|
||||
const { clientDetails } = useContext(AppContext);
|
||||
const theme = useTheme();
|
||||
|
||||
const defaultValues = {
|
||||
operatorCost: bondedNode.operatorCost,
|
||||
profitMargin: bondedNode.profitMargin,
|
||||
};
|
||||
|
||||
const {
|
||||
register,
|
||||
handleSubmit,
|
||||
reset,
|
||||
setValue,
|
||||
formState: { errors, isSubmitting, isDirty, isValid },
|
||||
} = useForm({
|
||||
resolver: yupResolver(bondedNodeParametersValidationSchema),
|
||||
mode: 'onChange',
|
||||
defaultValues: isMixnode(bondedNode)
|
||||
? {
|
||||
operatorCost: bondedNode.operatorCost,
|
||||
profitMargin: bondedNode.profitMargin,
|
||||
}
|
||||
: {},
|
||||
defaultValues,
|
||||
});
|
||||
|
||||
const getIntervalAsDate = async () => {
|
||||
@@ -91,13 +95,13 @@ export const ParametersSettings = ({ bondedNode }: { bondedNode: TBondedMixnode
|
||||
getPendingEvents();
|
||||
}, []);
|
||||
|
||||
const onSubmit = async (data: { operatorCost?: string; profitMargin?: string }) => {
|
||||
const onSubmit = async (data: { operatorCost: { amount: string; denom: CurrencyDenom }; profitMargin: string }) => {
|
||||
if (data.operatorCost && data.profitMargin) {
|
||||
const MixNodeCostParams = {
|
||||
profit_margin_percent: (+data.profitMargin / 100).toString(),
|
||||
interval_operating_cost: {
|
||||
denom: bondedNode.bond.denom,
|
||||
amount: data.operatorCost.toString(),
|
||||
amount: data.operatorCost.amount,
|
||||
denom: data.operatorCost.denom,
|
||||
},
|
||||
};
|
||||
try {
|
||||
@@ -188,20 +192,16 @@ export const ParametersSettings = ({ bondedNode }: { bondedNode: TBondedMixnode
|
||||
</Grid>
|
||||
<Grid spacing={3} container item alignItems="center" xs={12} md={6}>
|
||||
<Grid item width={1}>
|
||||
<TextField
|
||||
{...register('operatorCost')}
|
||||
name="operatorCost"
|
||||
label="Operating cost"
|
||||
<CurrencyFormField
|
||||
required
|
||||
fullWidth
|
||||
error={!!errors.operatorCost}
|
||||
helperText={errors?.operatorCost?.message}
|
||||
InputProps={{
|
||||
endAdornment: (
|
||||
<InputAdornment position="end">
|
||||
<Box>{bondedNode.bond.denom.toUpperCase()}</Box>
|
||||
</InputAdornment>
|
||||
),
|
||||
label="Operating cost"
|
||||
onChanged={(newValue) => {
|
||||
setValue('operatorCost', newValue, { shouldValidate: true, shouldDirty: true });
|
||||
}}
|
||||
validationError={errors.operatorCost?.amount?.message}
|
||||
denom={clientDetails?.display_mix_denom || 'nym'}
|
||||
initialValue={defaultValues.operatorCost.amount}
|
||||
/>
|
||||
{pendingUpdates && (
|
||||
<FormHelperText>
|
||||
@@ -222,7 +222,7 @@ export const ParametersSettings = ({ bondedNode }: { bondedNode: TBondedMixnode
|
||||
size="large"
|
||||
variant="contained"
|
||||
disabled={isSubmitting || !isDirty || !isValid}
|
||||
onClick={handleSubmit((d) => onSubmit(d))}
|
||||
onClick={handleSubmit(onSubmit)}
|
||||
type="submit"
|
||||
sx={{ m: 3, width: '320px' }}
|
||||
endIcon={isSubmitting && <CircularProgress size={20} />}
|
||||
|
||||
+2
-1
@@ -3,6 +3,7 @@ import { Box, Button, Divider, Grid } from '@mui/material';
|
||||
import { TBondedMixnode, TBondedGateway } from '../../../../../context/bonding';
|
||||
import { InfoSettings } from './InfoSettings';
|
||||
import { ParametersSettings } from './ParametersSettings';
|
||||
import { isMixnode } from 'src/types';
|
||||
|
||||
const nodeGeneralNav = ['Info', 'Parameters'];
|
||||
|
||||
@@ -34,7 +35,7 @@ export const NodeGeneralSettings = ({ bondedNode }: { bondedNode: TBondedMixnode
|
||||
</Grid>
|
||||
<Divider orientation="vertical" flexItem />
|
||||
{settingsCard === nodeGeneralNav[0] && <InfoSettings bondedNode={bondedNode} />}
|
||||
{settingsCard === nodeGeneralNav[1] && <ParametersSettings bondedNode={bondedNode} />}
|
||||
{settingsCard === nodeGeneralNav[1] && isMixnode(bondedNode) && <ParametersSettings bondedNode={bondedNode} />}
|
||||
</Grid>
|
||||
</Box>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user