Skip to content

Commit

Permalink
feat: Mana calculator units (#1324)
Browse files Browse the repository at this point in the history
* feat: Flexible network selector in the mana calculator

* feat: Improved user flow for the Mana calculator

* typo

* chore: Refactor the Mana Calculator

* ✨

* clean up

* clean up

* feat: Add user total holding input

* feat: Mana accumulation graph

* feat: Block issuance rate graph

* clean up

* network params

* clean up

* clean up

* fmt

* prettify

* typo

* fix

* fix

* fix

* improve graph

* tweak

* enhancement

* Update src/components/ManaCalculator/hooks/useManaState.ts

Co-authored-by: Dr-Electron <[email protected]>

* Update src/components/ManaCalculator/hooks/useManaState.ts

Co-authored-by: Dr-Electron <[email protected]>

* fmt

* clean up

* fmt

* feat: UI Polish

* ui polish

* feat: Display units in Mana Calculator

* Add humanizer config

* clean up styles

* Update src/components/ManaCalculator/components/AdvancedSettingsValidator.tsx

Co-authored-by: Dr-Electron <[email protected]>

* fixes

* clean up

* more units

* more decimals

* smarter rounding

---------

Co-authored-by: Begoña Alvarez <[email protected]>
Co-authored-by: Dr-Electron <[email protected]>
  • Loading branch information
3 people authored Nov 8, 2023
1 parent e73ce44 commit b7d163f
Show file tree
Hide file tree
Showing 14 changed files with 136 additions and 37 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,7 @@ import { useManaState } from '../hooks';
import { ValidatorCard } from './ValidatorCard';

export function AdvancedSettingsValidator() {
const {
handleAddValidator,
state: { validators },
} = useManaState();
const { handleAddValidator, state } = useManaState();

function onAddValidator() {
handleAddValidator({
Expand All @@ -24,11 +21,13 @@ export function AdvancedSettingsValidator() {
className='mana_calculator__card mana_calculator_inner__card table'
>
<div className='table'>
<div className='row'>
<div className='row small-row-head'>
<div className='col col--2 text--center'>ID</div>
<div className='col col--2 horizontal-spaced text--center'>Stake</div>
<div className='col col--2 horizontal-spaced text--center'>
Stake ({state.network})
</div>
<div className='col col--2 horizontal-space text--center'>
Delegated
Delegated ({state.network})
</div>
<div className='col col--2 horizontal-spaced text--center'>
Performance factor
Expand All @@ -38,7 +37,7 @@ export function AdvancedSettingsValidator() {
</div>
<div className='col col--1'></div>
</div>
{validators.map((validator, i) => (
{state.validators.map((validator, i) => (
<div className='row row--centered' key={i}>
<ValidatorCard validator={validator} id={i} />
</div>
Expand Down
12 changes: 10 additions & 2 deletions src/components/ManaCalculator/components/BlocksAllowance.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,17 @@ import {
XAxis,
YAxis,
} from 'recharts';
import { Unit } from '../hooks';
import { EpochReward } from '../types';
import { chartTooltip } from './CharTooltip';

export function BlocksAllowance({ results }: { results: EpochReward[] }) {
export function BlocksAllowance({
results,
unit,
}: {
results: EpochReward[];
unit: Unit;
}) {
return (
<>
<h3>Blocks Allowance</h3>
Expand All @@ -32,12 +39,13 @@ export function BlocksAllowance({ results }: { results: EpochReward[] }) {
width={100}
label={{ value: 'Blocks', angle: -90, position: 'insideLeft' }}
color='rgb(169, 184, 214)'
unit={unit}
/>
<CartesianGrid
strokeDasharray='3 3'
stroke='rgb(255, 255, 255, 0.15)'
/>
<Tooltip content={chartTooltip('Epoch', 'Blocks')} />
<Tooltip content={chartTooltip('Epoch', 'Blocks', unit)} />
<Area
type='monotone'
dataKey='totalTps'
Expand Down
10 changes: 4 additions & 6 deletions src/components/ManaCalculator/components/CharTooltip.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
import React from 'react';
import { getSizeOfUnit, Unit } from '../hooks';

export function chartTooltip(
xLabel: string,
yLabel: string,
formatter: (value: number) => string = (v) => v.toString(),
) {
export function chartTooltip(xLabel: string, yLabel: string, unit: Unit) {
const unitSize = getSizeOfUnit(unit);
return ({ active, payload, label }) => {

Check failure on line 6 in src/components/ManaCalculator/components/CharTooltip.tsx

View workflow job for this annotation

GitHub Actions / consistency

Component definition is missing display name

Check failure on line 6 in src/components/ManaCalculator/components/CharTooltip.tsx

View workflow job for this annotation

GitHub Actions / consistency

'active' is missing in props validation

Check failure on line 6 in src/components/ManaCalculator/components/CharTooltip.tsx

View workflow job for this annotation

GitHub Actions / consistency

'payload' is missing in props validation

Check failure on line 6 in src/components/ManaCalculator/components/CharTooltip.tsx

View workflow job for this annotation

GitHub Actions / consistency

'label' is missing in props validation
if (active && payload && payload.length) {

Check failure on line 7 in src/components/ManaCalculator/components/CharTooltip.tsx

View workflow job for this annotation

GitHub Actions / consistency

'payload.length' is missing in props validation
const value = formatter(payload[0].value);
const value = payload[0].value * unitSize;

Check failure on line 8 in src/components/ManaCalculator/components/CharTooltip.tsx

View workflow job for this annotation

GitHub Actions / consistency

'payload[].value' is missing in props validation
return (
<div className='mana-calculator__tooltip'>
<p>{`${xLabel}: ${label}`}</p>
Expand Down
17 changes: 10 additions & 7 deletions src/components/ManaCalculator/components/ManaAcculation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,17 @@ import {
XAxis,
YAxis,
} from 'recharts';
import { Unit } from '../hooks';
import { EpochReward } from '../types';
import { chartTooltip } from './CharTooltip';

export function ManaAccumulation({ results }: { results: EpochReward[] }) {
export function ManaAccumulation({
results,
unit,
}: {
results: EpochReward[];
unit: Unit;
}) {
return (
<>
<h3>Mana Accumulation</h3>
Expand All @@ -31,17 +38,13 @@ export function ManaAccumulation({ results }: { results: EpochReward[] }) {
<YAxis
width={100}
label={{ value: 'Mana', angle: -90, position: 'insideLeft' }}
unit='M'
unit={unit}
/>
<CartesianGrid
strokeDasharray='3 3'
stroke='rgb(255, 255, 255, 0.15)'
/>
<Tooltip
content={chartTooltip('Epoch', 'Mana', (v) =>
(v * 1_000_000).toString(),
)}
/>
<Tooltip content={chartTooltip('Epoch', 'Mana', unit)} />
<Area
type='monotone'
dataKey='mana'
Expand Down
7 changes: 4 additions & 3 deletions src/components/ManaCalculator/components/ManaCalculator.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
getDefaultParameters,
ManaStateContext,
useResultsPerEpoch,
useResultsWithUnit,
} from '../hooks';
import {
OutputForm,
Expand All @@ -19,7 +20,7 @@ import {
export function ManaCalculator() {
const [state, setState] = useState(getDefaultParameters(NetworkType.IOTA));
const results = useResultsPerEpoch(state);

const { data, manaUnit, blocksUnit } = useResultsWithUnit(results);
return (
<ManaStateContext.Provider value={{ state, setState }}>
<h3>Configuration</h3>
Expand All @@ -29,8 +30,8 @@ export function ManaCalculator() {
<br />
<h3>Results</h3>
<OutputForm />
<ManaAccumulation results={results} />
<BlocksAllowance results={results} />
<ManaAccumulation results={data} unit={manaUnit} />
<BlocksAllowance results={data} unit={blocksUnit} />
</ManaStateContext.Provider>
);
}
8 changes: 4 additions & 4 deletions src/components/ManaCalculator/components/OutputForm.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import React from 'react';
import { useManaState, useResults } from '../hooks';
import { fromMicro } from '../utils';
import { fromMicro, roundMax } from '../utils';
import humanizeDuration from 'humanize-duration';

export function OutputForm() {
const { state } = useManaState();
const results = useResults(state);
const passiveRewards = fromMicro(results.passiveRewards).toFixed(2);
const manaGenerated = fromMicro(results.generatedRewards).toFixed(2);
const totalTPS = results.totalTPS.toFixed(2);
const passiveRewards = roundMax(fromMicro(results.passiveRewards), 6);
const manaGenerated = roundMax(fromMicro(results.generatedRewards), 6);
const totalTPS = roundMax(results.totalTPS, 2);
const humanizer = humanizeDuration.humanizer({
units: ['y', 'mo', 'w', 'd', 'h', 'm', 's', 'ms'],
maxDecimalPoints: 3,
Expand Down
8 changes: 5 additions & 3 deletions src/components/ManaCalculator/components/RoleSection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ export function RoleSection() {
]}
/>
<br />
<label className='inlined-label'>Held amount</label>
<label className='inlined-label'>Held amount ({state.network})</label>
<input
className='mana_calculator__compact inlined'
value={fromMicro(state.heldTokens)}
Expand All @@ -43,7 +43,7 @@ export function RoleSection() {
<br />
{state.userType === UserType.VALIDATOR ? (
<>
<label className='inlined-label'>Stake</label>
<label className='inlined-label'>Stake ({state.network})</label>
<input
className='mana_calculator__compact inlined'
value={fromMicro(state.stakedOrDelegatedTokens)}
Expand All @@ -67,7 +67,9 @@ export function RoleSection() {
options={validatorOptions}
/>
<br />
<label className='inlined-label'>Delegated amount</label>
<label className='inlined-label'>
Delegated amount ({state.network})
</label>
<input
className='mana_calculator__compact inlined'
value={fromMicro(state.stakedOrDelegatedTokens)}
Expand Down
4 changes: 2 additions & 2 deletions src/components/ManaCalculator/enums/parameters.enum.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ export enum UserType {
}

export enum NetworkType {
IOTA,
SHIMMER,
IOTA = 'IOTA',
SHIMMER = 'SMR',
}

export enum CongestionType {
Expand Down
1 change: 1 addition & 0 deletions src/components/ManaCalculator/hooks/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export * from './useManaState';
export * from './useResults';
export * from './useResultsPerEpoch';
export * from './useResultsWithUnit';
2 changes: 1 addition & 1 deletion src/components/ManaCalculator/hooks/useResultsPerEpoch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ export function useResultsPerEpoch(state: ManaCalculatorProps): EpochReward[] {

results.push({
epoch: i,
mana: fromMicro(mana) / 1_000_000,
mana: fromMicro(mana),
totalTps,
});
}
Expand Down
78 changes: 78 additions & 0 deletions src/components/ManaCalculator/hooks/useResultsWithUnit.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import { EpochReward } from '../types/epoch-reward.type';

export enum Unit {
default = 'default',
K = 'K',
M = 'M',
G = 'G',
T = 'T',
P = 'P',
}

const UNIT_MAP: { [unit in Unit]: { val: number; dp: number } } = {
default: { val: 1, dp: 0 },
K: { val: 1000, dp: 3 },
M: { val: 1000000, dp: 6 },
G: { val: 1000000000, dp: 9 },
T: { val: 1000000000000, dp: 12 },
P: { val: 1000000000000000, dp: 15 },
};

const getUnit = (value: number): Unit => {
let bestUnits: Unit = Unit.default;
if (!value || value === 0) {
return Unit.M;
}
const checkLength = Math.abs(value).toString().length;

if (checkLength > UNIT_MAP.M.dp) {
bestUnits = Unit.M;
} else if (checkLength > UNIT_MAP.K.dp) {
bestUnits = Unit.K;
} else if (checkLength > UNIT_MAP.G.dp) {
bestUnits = Unit.G;
} else if (checkLength > UNIT_MAP.M.dp) {
bestUnits = Unit.M;
} else if (checkLength > UNIT_MAP.K.dp) {
bestUnits = Unit.K;
}

return bestUnits;
};

interface UseResultsWithUnit {
data: EpochReward[];
manaUnit: Unit;
blocksUnit: Unit;
}

export function getSizeOfUnit(unit: Unit): number {
return UNIT_MAP[unit].val;
}

export function useResultsWithUnit(results: EpochReward[]): UseResultsWithUnit {
const averageMana =
results.reduce((acc, reward) => acc + reward.mana, 0) / results.length;
const averageBlocks =
results.reduce((acc, reward) => acc + reward.totalTps, 0) / results.length;

const manaUnit = getUnit(averageMana);
const blocksUnit = getUnit(averageBlocks);

const manaSize = getSizeOfUnit(manaUnit);
const blocksSize = getSizeOfUnit(blocksUnit);

const data = results.map<EpochReward>((reward) => {
return {
...reward,
mana: reward.mana / manaSize,
totalTps: reward.totalTps / blocksSize,
};
});

return {
data,
manaUnit,
blocksUnit,
};
}
6 changes: 5 additions & 1 deletion src/components/ManaCalculator/styles.css
Original file line number Diff line number Diff line change
Expand Up @@ -95,14 +95,18 @@

.inlined-label {
display: inline-block;
width: 150px;
width: 200px;
}

.inlined-long-label {
display: inline-block;
width: 350px;
}

.small-row-head {
font-size: 15px;
}

.mana_calculator__compact {
width: 200px;
margin-bottom: 1rem !important;
Expand Down
1 change: 1 addition & 0 deletions src/components/ManaCalculator/types/epoch-reward.type.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export interface EpochReward {
epoch: number;
mana: number;
totalTps: number;
}
4 changes: 4 additions & 0 deletions src/components/ManaCalculator/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,7 @@ export function getNetworkSupply(network: NetworkType): number {
return SHIMMER_SUPPLY;
}
}

export const roundMax = function (num: number, places: number) {
return +(Math.round(Number(num + 'e+' + places)) + 'e-' + places);
};

0 comments on commit b7d163f

Please sign in to comment.