Skip to content

Commit

Permalink
Add minStake info for Validators (w3f#3626)
Browse files Browse the repository at this point in the history
* Add minStake info for Validators

Asynchronously populate minStake of validator in the active set. Default to historic values when it is loading

* address PR feedback

* break-out min stake logic into a new component (equal functionality)

* add TODO note

* apply human readable filter to default & on-chain result

* organization

* expose default values via component attributes

* improve calculation time significantly

* formatting

* remove duplicate state setting action

Co-authored-by: alfarok <[email protected]>
  • Loading branch information
DrW3RK and alfarok authored Aug 11, 2022
1 parent c35445b commit 10f034d
Show file tree
Hide file tree
Showing 4 changed files with 121 additions and 42 deletions.
51 changes: 51 additions & 0 deletions components/Minimum-Stake.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { useState, useEffect } from "react";
import React from "react";
import { ApiPromise, WsProvider } from "@polkadot/api";
import { HumanReadable } from "./utilities/filters";

function MinimumStake({ network, defaultValue }) {
const [returnValue, setReturnValue] = useState('');

useEffect(async () => {
// Set defaults based on network
let wsUrl = undefined;
if (network === "polkadot") { wsUrl = "wss://rpc.polkadot.io" }
else if (network === "kusama") { wsUrl = "wss://kusama-rpc.polkadot.io/" }
else { return (<div />) }

// Set default value to render on component
HumanReadable(defaultValue, network, setReturnValue);
// Calculate a more accurate approximation using on-chain data
await CalcValidatorMinStake(network, wsUrl, setReturnValue);
}, []);

return (returnValue);
}

async function CalcValidatorMinStake(network, wsUrl, setReturnValue) {
const wsProvider = new WsProvider(wsUrl);
const api = await ApiPromise.create({ provider: wsProvider })

const [currentValidators, currentEra] = await Promise.all([
api.query.session.validators(),
api.query.staking.currentEra(),
]);

// Get validators stake for current error and first validator
const validatorStake = await api.query.staking.erasStakers(currentEra.toString(), currentValidators[0])
let validatorMinStake = parseInt(validatorStake['total'].toString())

// Iterate era validators
const validators = await api.query.staking.erasStakers.entries(currentEra.toString());
validators.forEach(([key, validator]) => {
const validatorTotalStake = parseInt(validator.total);
if (validatorTotalStake < validatorMinStake) {
validatorMinStake = validatorTotalStake;
}
});

const result = validatorMinStake.toString();
HumanReadable(result, network, setReturnValue);
}

export default MinimumStake;
47 changes: 6 additions & 41 deletions components/RPC-Connection.jsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { useState, useEffect } from "react";
import { ApiPromise, WsProvider } from "@polkadot/api";
import { HumanReadable, BlocksToDays} from "./utilities/filters";

/*
This component connects to the Polkadot/Kusama APIs and renders the response data.
Expand Down Expand Up @@ -74,6 +75,7 @@ function RPC({ network, path, defaultValue, filter=undefined }) {
return (returnValue)
}

// Fetch chain data
async function syncData(network, path, setReturnValue) {
let wsUrl = undefined;
let chainValue = undefined;
Expand Down Expand Up @@ -128,56 +130,19 @@ async function syncData(network, path, setReturnValue) {
}
}

// Post-processing functions
function applyFilter(value, filter, network, setReturnValue) {
//console.log(`Applying ${filter} to ${network} value ${value}`);
const values = {
polkadot: {
precision: 1e10,
symbol: "DOT",
},
kusama: {
precision: 1e12,
symbol: "KSM",
},
statemint: {
precision: 1e10,
symbol: "DOT",
},
statemine: {
precision: 1e12,
symbol: "KSM",
},
};

switch (filter) {
case "humanReadable":
let decimals = undefined;
if (network === Polkadot || network === Statemint) {
decimals = 3;
} else if (network === Kusama || network === Statemine) {
decimals = 6;
} else {
console.log("Unknown network type found when attempting to apply 'Human Readable' filter");
return;
}
// String to number
value = parseFloat(value);
// Apply precision
if (Number.isInteger(value / values[network].precision)) {
value = `${value / values[network].precision} ${values[network].symbol}`;
} else {
value = `${(value / values[network].precision).toFixed(decimals)} ${values[network].symbol}`;
}
break;
HumanReadable(value, network, setReturnValue)
break;
case "blocksToDays":
value = (value * 6) / 86400;
BlocksToDays(value, setReturnValue);
break;
default:
console.log("Ignoring unknown filter type");
return;
}

setReturnValue(value.toString());
}

export default RPC;
54 changes: 54 additions & 0 deletions components/utilities/filters.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
const Polkadot = "polkadot";
const Kusama = "kusama";
const Statemine = "statemine";
const Statemint = "statemint";

const values = {
polkadot: {
precision: 1e10,
symbol: "DOT",
},
kusama: {
precision: 1e12,
symbol: "KSM",
},
statemint: {
precision: 1e10,
symbol: "DOT",
},
statemine: {
precision: 1e12,
symbol: "KSM",
},
};

module.exports = {

HumanReadable: function (value, network, setReturnValue) {
let decimals = undefined;
if (network === Polkadot || network === Statemint) {
decimals = 3;
} else if (network === Kusama || network === Statemine) {
decimals = 6;
} else {
console.log("Unknown network type found when attempting to apply 'Human Readable' filter");
return;
}
// String to number
value = parseFloat(value);
// Apply precision
if (Number.isInteger(value / values[network].precision)) {
value = `${value / values[network].precision} ${values[network].symbol}`;
} else {
value = `${(value / values[network].precision).toFixed(decimals)} ${values[network].symbol}`;
}
// Update value
setReturnValue(value.toString());
},

BlocksToDays: function (value, setReturnValue) {
value = (value * 6) / 86400;
// Update value
setReturnValue(value.toString());
}
}
11 changes: 10 additions & 1 deletion docs/maintain/maintain-guides-how-to-validate-polkadot.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ description: The fundamentals for running a Polkadot validator.
keywords: [validator setup, validator, validate, binary, runtime]
slug: ../maintain-guides-how-to-validate-polkadot
---
import RPC from "./../../components/RPC-Connection"
import MinimumStake from "./../../components/Minimum-Stake"

:::info

Expand Down Expand Up @@ -56,6 +58,13 @@ Stash and Controller [accounts](../learn/learn-keys.md) with the existential dep
transaction fees. The rest can come from nominators. To understand how validators are elected, check the
[NPoS Election algorithms](../learn/learn-phragmen.md) page.


:::info On-Chain Data for Reference
On Polkadot, the minimum stake backing a validator in the active set is <MinimumStake network="polkadot" defaultValue={18684315524834056}/> in the era <RPC network="polkadot" path="query.staking.currentEra" defaultValue="799"/>.

On Kusama, the minimum stake backing a validator in the active set is <MinimumStake network="kusama" defaultValue={5367388652143741} /> in the era <RPC network="kusama" path="query.staking.currentEra" defaultValue="4058"/>.
:::

**Warning:** Any DOT that you stake for your validator is liable to be slashed, meaning that an
insecure or improper setup may result in loss of DOT tokens! If you are not confident in your
ability to run a validator node, it is recommended to nominate your DOT to a trusted validator node
Expand Down Expand Up @@ -736,4 +745,4 @@ the binary. You can do this with a simple one line command:

```sh
$ docker run parity/polkadot:latest --validator --name "name on telemetry"
```
```

0 comments on commit 10f034d

Please sign in to comment.