123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184 |
- /**
- * autopayout.js
- *
- * Claim and distribute validator staking rewards for your stakers
- *
- * https://github.com/Colm3na/polkadot-auto-payout
- *
- * Author: Mario Pino | @mariopino:matrix.org
- */
- const BigNumber = require("bignumber.js");
- const { ApiPromise, WsProvider } = require("@polkadot/api");
- const { types } = require("@joystream/types");
- const keyring = require("@polkadot/ui-keyring").default;
- keyring.initKeyring({
- isDevelopment: false,
- });
- const fs = require("fs");
- const prompts = require("prompts");
- const yargs = require("yargs");
- const config = require("./config.js");
- const argv = yargs
- .scriptName("autopayout.js")
- .option("dryrun", {
- alias: "d",
- description: "Only show info",
- type: "boolean",
- })
- .option("stash", {
- alias: "s",
- description: "Stash account json file path",
- type: "string",
- })
- .option("controller", {
- alias: "c",
- description: "Controller account json file path",
- type: "string",
- })
- .option("password", {
- alias: "p",
- description: "Account password, or stdin if this is not set",
- type: "string",
- })
- .option("log", {
- alias: "l",
- description: "log (append) to autopayout.log file",
- type: "boolean",
- })
- .usage(
- "node adjustStake.js -s keystores/stash.json -c keystores/controller.json -p password"
- )
- .help()
- .alias("help", "h")
- .version()
- .alias("version", "V").argv;
- // Exported account json file param
- const stashJSON = argv.stash || config.accountJSON;
- const controllerJSON = argv.controller || config.controllerJSON;
- // Password param
- let password = argv.password || config.password;
- // Logging to file param
- const log = argv.log || config.log;
- // Node websocket
- const wsProvider = config.nodeWS;
- const main = async () => {
- console.log(
- "\x1b[1m - Source at https://github.com/Colm3na/substrate-auto-payout\x1b[0m"
- );
- let json = [];
- try {
- json[0] = fs.readFileSync(stashJSON, { encoding: "utf-8" });
- } catch (err) {
- console.log(`\x1b[31m\x1b[1mError! Can't open ${stashJSON}\x1b[0m\n`, err);
- process.exit(1);
- }
- try {
- json[1] = fs.readFileSync(controllerJSON, { encoding: "utf-8" });
- } catch (err) {
- console.log(
- `\x1b[31m\x1b[1mError! Can't open ${controllerJSON}\x1b[0m\n`,
- err
- );
- process.exit(1);
- }
- const stash = JSON.parse(json[0]);
- const controller = JSON.parse(json[1]);
- // Prompt user to enter password
- if (!password) {
- const response = await prompts({
- type: "password",
- name: "password",
- message: `Enter password for ${address}:`,
- });
- password = response.password;
- }
- if (!password) {
- console.log(`No pasword. Bye!`);
- process.exit(0);
- }
- // Connect to node
- console.log(`\x1b[1m -> Connecting to\x1b[0m`, wsProvider);
- const provider = new WsProvider(wsProvider);
- const api = await ApiPromise.create({ provider, types });
- // Get session progress info
- const chainActiveEra = await api.query.staking.activeEra();
- const activeEra = JSON.parse(JSON.stringify(chainActiveEra)).index;
- console.log(`\x1b[1m -> Active era is ${activeEra}\x1b[0m`);
- const formatM = (n) => (n / 1000000).toFixed(3);
- const issuance = await api.query.balances.totalIssuance();
- const staked = await api.query.staking.erasTotalStake(activeEra);
- const quarter = issuance / 4;
- const percent = staked / issuance;
- console.log(
- `\x1b[1m -> Issued: ${formatM(issuance)} M\n\r`,
- `\x1b[1m-> Quarter: ${formatM(quarter)} M\n`,
- `\x1b[1m-> Staked: ${formatM(staked)} M (${(100 * percent).toFixed(2)}%)`
- );
- if (argv.dryrun) process.exit(0);
- let transactions = [];
- let addess = ``;
- let signer;
- if (percent < 0.25) {
- const missing = 1000 * formatM(quarter - staked);
- const ledger = await api.query.staking.ledger(controller.address);
- const unbonding = ledger.toJSON().unlocking[0].value;
- if (unbonding) {
- // rebond! signed by controller
- const rebond = unbonding > missing ? missing : unbonding;
- console.log(`\x1b[1m-> Rebonding ${rebond} K`);
- transactions.push(api.tx.staking.rebond(rebond));
- signer = keyring.restoreAccount(controller, password);
- address = controller.address;
- } else {
- // bondExtra! signed by stash
- console.log(`\x1b[1m-> Bonding ${missing} K`);
- transactions.push(api.tx.staking.bondExtra(missing));
- signer = keyring.restoreAccount(stash, password);
- address = stash.address;
- }
- } else if (percent > 0.25) {
- const spare = 1000 * formatM(staked - quarter);
- console.log(`\x1b[1m -> Unbonding ${spare} K`);
- // unbond! singed by controller
- transactions.push(api.tx.staking.unbond(spare));
- signer = keyring.restoreAccount(controller, password);
- address = controller.address;
- }
- if (!transactions.length) process.exit(0);
- console.log(`\x1b[1m -> Importing account\x1b[0m`, address);
- signer.decodePkcs8(password);
- processTransactions(api, signer, address, transactions);
- };
- const processTransactions = async (api, signer, address, transactions) => {
- const nonce = (await api.derive.balances.account(address)).accountNonce;
- const hash = await api.tx.utility
- .batch(transactions.slice(0, 40))
- .signAndSend(signer, { nonce });
- console.log(
- `\n\x1b[32m\x1b[1mSuccess! \x1b[37mtx hash: ${hash.toString()}\x1b[0m\n`
- );
- process.exit(0);
- };
- try {
- main();
- } catch (error) {
- console.error(error);
- }
|