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'}
- ),
- }}
- />
-
- ))}
-
-
-
-
+