From 480f8a0a53aeae94f94895a82f7417df4a33addc Mon Sep 17 00:00:00 2001 From: fmtabbara Date: Wed, 21 Sep 2022 09:49:10 +0100 Subject: [PATCH] update validation for rewards playground --- .../InclusionProbability.tsx | 23 +++ .../components/RewardsPlayground/Inputs.tsx | 60 ++++++ .../RewardsPlayground/NodeDetail.tsx | 26 +++ .../RewardsPlayground/ResultsTable.tsx | 75 +++++++ .../inputsValidationSchema.ts | 39 ++++ .../node-settings/apy-playground/index.tsx | 183 ++---------------- 6 files changed, 241 insertions(+), 165 deletions(-) create mode 100644 nym-wallet/src/components/RewardsPlayground/InclusionProbability.tsx create mode 100644 nym-wallet/src/components/RewardsPlayground/Inputs.tsx create mode 100644 nym-wallet/src/components/RewardsPlayground/NodeDetail.tsx create mode 100644 nym-wallet/src/components/RewardsPlayground/ResultsTable.tsx create mode 100644 nym-wallet/src/components/RewardsPlayground/inputsValidationSchema.ts diff --git a/nym-wallet/src/components/RewardsPlayground/InclusionProbability.tsx b/nym-wallet/src/components/RewardsPlayground/InclusionProbability.tsx new file mode 100644 index 0000000000..7cef38a4d4 --- /dev/null +++ b/nym-wallet/src/components/RewardsPlayground/InclusionProbability.tsx @@ -0,0 +1,23 @@ +import React from 'react'; +import { Typography } from '@mui/material'; +import { SelectionChance } from '@nymproject/types'; + +const colorMap: { [key in SelectionChance]: string } = { + VeryLow: 'error.main', + Low: 'error.main', + Moderate: 'warning.main', + High: 'success.main', + VeryHigh: 'success.main', +}; + +const textMap: { [key in SelectionChance]: string } = { + VeryLow: 'VeryLow', + Low: 'Low', + Moderate: 'Moderate', + High: 'High', + VeryHigh: 'Very high', +}; + +export const InclusionProbability = ({ probability }: { probability: SelectionChance }) => ( + {textMap[probability]} +); diff --git a/nym-wallet/src/components/RewardsPlayground/Inputs.tsx b/nym-wallet/src/components/RewardsPlayground/Inputs.tsx new file mode 100644 index 0000000000..851d02de4f --- /dev/null +++ b/nym-wallet/src/components/RewardsPlayground/Inputs.tsx @@ -0,0 +1,60 @@ +import React from 'react'; +import { yupResolver } from '@hookform/resolvers/yup'; +import { Button, Grid, TextField, Typography } from '@mui/material'; +import { useForm } from 'react-hook-form'; +import { inputValidationSchema } from './inputsValidationSchema'; + +export type InputValues = { + label: string; + name: 'profitMargin' | 'uptime' | 'bond' | 'delegations' | 'operatorCost'; + isPercentage?: boolean; +}[]; + +export const Inputs = ({ + inputValues, + onCalculate, +}: { + inputValues: InputValues; + onCalculate: () => Promise; +}) => { + const { + register, + handleSubmit, + formState: { errors }, + } = useForm({ + resolver: yupResolver(inputValidationSchema), + defaultValues: { profitMargin: '', uptime: '', bond: '', delegations: '', operatorCost: '' }, + }); + + return ( + + {inputValues.map((input) => ( + + {input.isPercentage ? '%' : 'NYM'}, + }} + /> + + ))}{' '} + + + + + ); +}; diff --git a/nym-wallet/src/components/RewardsPlayground/NodeDetail.tsx b/nym-wallet/src/components/RewardsPlayground/NodeDetail.tsx new file mode 100644 index 0000000000..95f4bd993f --- /dev/null +++ b/nym-wallet/src/components/RewardsPlayground/NodeDetail.tsx @@ -0,0 +1,26 @@ +import React from 'react'; +import { Card, CardContent, Divider, Stack, Typography } from '@mui/material'; +import { SelectionChance } from '@nymproject/types'; +import { InclusionProbability } from './InclusionProbability'; + +export const NodeDetails = ({ + saturation, + selectionProbability, +}: { + saturation: number; + selectionProbability: SelectionChance; +}) => ( + + + + Stake saturation + {saturation}% + + + + Selection probability + + + + +); diff --git a/nym-wallet/src/components/RewardsPlayground/ResultsTable.tsx b/nym-wallet/src/components/RewardsPlayground/ResultsTable.tsx new file mode 100644 index 0000000000..19196c176d --- /dev/null +++ b/nym-wallet/src/components/RewardsPlayground/ResultsTable.tsx @@ -0,0 +1,75 @@ +import React from 'react'; +import { + Card, + CardContent, + Table, + TableBody, + TableCell, + TableContainer, + TableHead, + TableRow, + Typography, +} from '@mui/material'; + +export type Results = { + operator: { + daily: string; + monthly: string; + yearly: string; + }; + delegator: { + daily: string; + monthly: string; + yearly: string; + }; + total: { + daily: string; + monthly: string; + yearly: string; + }; +}; + +const tableHeader = [ + { title: 'Estimated rewards', bold: true }, + { title: 'Per day' }, + { title: 'Per month' }, + { title: 'Per year' }, +]; + +export const ResultsTable = ({ results }: { results: Results }) => { + const tableRows = [ + { title: 'Total node reward', ...results.total }, + { title: 'Operator rewards', ...results.operator }, + { title: 'Delegator rewards', ...results.delegator }, + ]; + + return ( + + + + + + + {tableHeader.map((header) => ( + + {header.title} + + ))} + + + + {tableRows.map((row) => ( + + {row.title} + {row.daily} + {row.monthly} + {row.yearly} + + ))} + +
+
+
+
+ ); +}; diff --git a/nym-wallet/src/components/RewardsPlayground/inputsValidationSchema.ts b/nym-wallet/src/components/RewardsPlayground/inputsValidationSchema.ts new file mode 100644 index 0000000000..9b383cc46e --- /dev/null +++ b/nym-wallet/src/components/RewardsPlayground/inputsValidationSchema.ts @@ -0,0 +1,39 @@ +import * as Yup from 'yup'; +import { isGreaterThan, isLessThan } from 'src/utils'; + +export const inputValidationSchema = Yup.object().shape({ + profitMargin: Yup.string() + .required() + .test('Profit margin must be a number between 0 and 100', (value) => { + const stringValueToNumber = Math.round(Number(value)); + console.log(isLessThan(stringValueToNumber, 101)); + if (stringValueToNumber && isGreaterThan(stringValueToNumber, -1) && isLessThan(stringValueToNumber, 101)) + return true; + return false; + }), + uptime: Yup.string() + .required() + .test('Uptime must be a number between 0 and 100', (value) => { + const stringValueToNumber = Math.round(Number(value)); + if (stringValueToNumber && isGreaterThan(stringValueToNumber, 0)) return true; + return false; + }), + bond: Yup.string() + .required() + .test('Bond must be a valid number', (value) => { + if (Number(value)) return true; + return false; + }), + delegations: Yup.string() + .required() + .test('Delegations must be a valid number', (value) => { + if (Number(value)) return true; + return false; + }), + operatorCost: Yup.string() + .required() + .test('Operator cost must be a valid number', (value) => { + if (Number(value)) return true; + return false; + }), +}); diff --git a/nym-wallet/src/pages/bonding/node-settings/apy-playground/index.tsx b/nym-wallet/src/pages/bonding/node-settings/apy-playground/index.tsx index 5cff5a8e89..2ececfbe81 100644 --- a/nym-wallet/src/pages/bonding/node-settings/apy-playground/index.tsx +++ b/nym-wallet/src/pages/bonding/node-settings/apy-playground/index.tsx @@ -1,169 +1,44 @@ import React, { useState } from 'react'; -import { - Box, - Button, - Card, - CardContent, - CardHeader, - Divider, - Grid, - Stack, - Table, - TableBody, - TableCell, - TableContainer, - TableHead, - TableRow, - TextField, - Typography, -} from '@mui/material'; +import { Box, Button, Card, CardContent, CardHeader, Grid, TextField, Typography } from '@mui/material'; +import { ResultsTable } from 'src/components/RewardsPlayground/ResultsTable'; import { computeMixnodeRewardEstimation } from 'src/requests'; -import { SelectionChance } from '@nymproject/types'; +import { NodeDetails } from 'src/components/RewardsPlayground/NodeDetail'; +import { Inputs, InputValues } from 'src/components/RewardsPlayground/Inputs'; const MAJOR_AMOUNT_FOR_CALCS = 1000; -const tableHeader = [ - { title: 'Estimated rewards', bold: true }, - { title: 'Per day' }, - { title: 'Per month' }, - { title: 'Per year' }, -]; - -const colorMap: { [key in SelectionChance]: string } = { - VeryLow: 'error.main', - Low: 'error.main', - Moderate: 'warning.main', - High: 'success.main', - VeryHigh: 'success.main', -}; - -const textMap: { [key in SelectionChance]: string } = { - VeryLow: 'VeryLow', - Low: 'Low', - Moderate: 'Moderate', - High: 'High', - VeryHigh: 'Very high', -}; - -type Results = { - operator: { - daily: string; - monthly: string; - yearly: string; - }; - delegator: { - daily: string; - monthly: string; - yearly: string; - }; - total: { - daily: string; - monthly: string; - yearly: string; - }; -}; - -const InclusionProbability = ({ probability }: { probability: SelectionChance }) => ( - {textMap[probability]} -); - -const ResultsTable = ({ results }: { results: Results }) => { - const tableRows = [ - { title: 'Total node reward', ...results.total }, - { title: 'Operator rewards', ...results.operator }, - { title: 'Delegator rewards', ...results.delegator }, - ]; - - return ( - - - - - - - {tableHeader.map((header) => ( - - {header.title} - - ))} - - - - {tableRows.map((row) => ( - - {row.title} - {row.daily} - {row.monthly} - {row.yearly} - - ))} - -
-
-
-
- ); -}; - -const NodeDetails = ({ - saturation, - selectionProbability, -}: { - saturation: number; - selectionProbability: SelectionChance; -}) => ( - - - - Stake saturation - {saturation}% - - - - Selection probability - - - - -); - export const ApyPlayground = () => { - const [inputValues, setInputValues] = useState([ - { label: 'Profit margin', isPercentage: true, value: '0' }, - { label: 'Operator cost', value: '0' }, - { label: 'Bond', value: '0' }, - { label: 'Delegations', value: '0' }, - { label: 'Uptime', isPercentage: true, value: '0' }, + const [inputValues, setInputValues] = useState([ + { label: 'Profit margin', name: 'profitMargin', isPercentage: true }, + { label: 'Operator cost', name: 'operatorCost' }, + { label: 'Bond', name: 'bond' }, + { label: 'Delegations', name: 'delegations' }, + { label: 'Uptime', name: 'uptime', isPercentage: true }, ]); + const [results, setResults] = useState({ total: { daily: '-', monthly: '-', yearly: '-' }, operator: { daily: '-', monthly: '-', yearly: '-' }, delegator: { daily: '-', monthly: '-', yearly: '-' }, }); - const handleInputChange = (e: React.ChangeEvent) => { - setInputValues((current) => [ - ...current.map((input) => (input.label === e.target.name ? { ...input, value: e.target.value } : input)), - ]); - }; - - const getInputValue = (inputName: string) => inputValues.find((input) => input.label === inputName); + const getInputValue = (inputName: string) => inputValues.find((input) => input.name === inputName); const handleCalculate = async () => { try { const res = await computeMixnodeRewardEstimation({ identity: 'DLdMKLPywEy1vnu3yPrtXvzY7fw1puiiHpA9n9UQatiQ', - uptime: +getInputValue('Uptime')!, + uptime: 0, isActive: true, - pledgeAmount: Math.floor(+getInputValue('Bond')! * 1_000_000), - totalDelegation: Math.floor(+getInputValue('Delegations')! * 1_000_000), + pledgeAmount: Math.floor(0 * 1_000_000), + totalDelegation: Math.floor(0 * 1_000_000), }); const operatorReward = (res.estimated_operator_reward / 1_000_000) * 24; // epoch_reward * 1 epoch_per_hour * 24 hours const delegatorsReward = (res.estimated_delegators_reward / 1_000_000) * 24; - const operatorRewardScaled = MAJOR_AMOUNT_FOR_CALCS * (operatorReward / +getInputValue('Bond')!.value); - const delegatorReward = MAJOR_AMOUNT_FOR_CALCS * (delegatorsReward / +getInputValue('Delegations')!.value!); + const operatorRewardScaled = MAJOR_AMOUNT_FOR_CALCS * (operatorReward / 0); + const delegatorReward = MAJOR_AMOUNT_FOR_CALCS * (delegatorsReward / 0); setResults({ total: { @@ -205,29 +80,7 @@ export const ApyPlayground = () => { } /> - - {inputValues.map((input) => ( - - {input.isPercentage ? '%' : 'NYM'} - ), - }} - /> - - ))} - - - - +