123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184 |
- import fs from "fs";
- const exec = require("util").promisify(require("child_process").exec);
- import { ApiPromise } from "@polkadot/api";
- import { connectApi, getHead, getCouncils, getCouncilRound } from "./lib/api";
- import { StatisticsCollector } from "./tokenomics";
- import { generateReportData } from "./council";
- import { Config } from "./types/tokenomics";
- // types
- import { Round } from "./lib/types";
- import { types as joyTypes } from "@joystream/types";
- import { Hash, Moment } from "@polkadot/types/interfaces";
- import {
- BlockRange,
- CouncilMemberInfo,
- CouncilRoundInfo,
- ProposalFailedReason,
- ProposalInfo,
- ProposalStatus,
- ProposalType,
- ReportData,
- } from "./types/council";
- import { Seats } from "@joystream/types/council";
- import { MemberId, Membership } from "@joystream/types/members";
- import { StorageKey, u32, U32, Vec } from "@polkadot/types";
- import { Mint, MintId } from "@joystream/types/mint";
- import { ProposalDetailsOf, ProposalOf } from "@joystream/types/augment/types";
- const CONFIG: Config = {
- repoDir: __dirname + "/../../../",
- reportsDir: "council/tokenomics",
- spendingCategoriesFile: "council/spending_proposal_categories.csv",
- councilTemplate: __dirname + "/../templates/council.md",
- tokenomicsTemplate: __dirname + "/../templates/tokenomics.md",
- providerUrl: "ws://127.0.0.1:9944",
- proposalUrl: "https://testnet.joystream.org/#/proposals/",
- statusUrl: "https://status.joystream.org/status",
- burnAddress: "5D5PhZQNJzcJXVBxwJxZcsutjKPqUPydrvpu6HeiBfMaeKQu",
- cacheDir: "cache",
- councilRoundOffset: 2,
- videoClassId: 10,
- channelClassId: 1,
- };
- const createDir = (dir: string) => {
- try {
- fs.statSync(dir);
- } catch (e) {
- fs.mkdirSync(dir, { recursive: true });
- }
- };
- const councilReport = async (
- startBlock: number,
- endBlock: number,
- config: Config
- ) => {
- const { repoDir, councilTemplate, providerUrl } = config;
- const api: ApiPromise = await connectApi(providerUrl);
- await api.isReady;
- // council report
- const startHash: Hash = await api.rpc.chain.getBlockHash(startBlock);
- const endHash: Hash = await api.rpc.chain.getBlockHash(endBlock);
- const blockRange = new BlockRange(startBlock, startHash, endBlock, endHash);
- const data = await generateReportData(api, blockRange);
- const report = await generateCouncilReport(data, councilTemplate);
- const term = data.councilTerm;
- let version = "giza";
- if (endBlock < 4191207) version = "sumer";
- if (startBlock < 717987) "antioch";
- const versionStr = version[0].toUpperCase() + version.slice(1);
- const dir = repoDir + `council/reports/` + version + `/`;
- const filename = `${versionStr}_Council${term}_Report.md`;
- createDir(dir);
- fs.writeFileSync(dir + filename, report);
- console.log(`-> Wrote ${dir + filename}`);
- api.disconnect();
- };
- const generateCouncilReport = async (
- data: ReportData,
- templateFile: string
- ) => {
- try {
- let fileData = await fs.readFileSync(templateFile, "utf8");
- let entries = Object.entries(data);
- for (let entry of entries) {
- let regex = new RegExp("{" + entry[0] + "}", "g");
- fileData = fileData.replace(regex, entry[1].toString());
- }
- return fileData;
- } catch (e) {
- console.error(e);
- return "";
- }
- };
- const tokenomicsReport = async (
- startBlock: number,
- endBlock: number,
- config: Config
- ): Promise<boolean> => {
- const { tokenomicsTemplate, repoDir, reportsDir } = config;
- let fileData = fs.readFileSync(tokenomicsTemplate, "utf8");
- let statsCollecttor = new StatisticsCollector();
- console.log(`-> Collecting stats from ${startBlock} to ${endBlock}`);
- const stats = await statsCollecttor.getStats(startBlock, endBlock, config);
- if (!stats.dateStart) return false;
- const round = stats.councilRound || 1;
- const fileName = `Council_Round${round}_${startBlock}-${endBlock}_Tokenomics_Report.md`;
- // antioch was updated to sumer at 717987
- const version =
- startBlock < 717987 ? "antioch-3" : endBlock > 4191207 ? "giza" : "sumer-4";
- const dir = `${repoDir}${reportsDir}/${version}`;
- createDir(dir);
- console.log(`-> Writing report to ${fileName}`);
- for (const entry of Object.entries(stats)) {
- if (entry[1] !== null) {
- let regex = new RegExp("{" + entry[0] + "}", "g");
- fileData = fileData.replace(regex, entry[1]?.toString());
- } else console.warn(`empty value:`, entry[0]);
- }
- fs.writeFileSync(`${dir}/${fileName}`, fileData);
- return true;
- };
- const updateReports = async (config: Config, round?: number) => {
- const { providerUrl } = config;
- console.debug(`Connecting to ${providerUrl}`);
- const api: ApiPromise = await connectApi(providerUrl);
- await api.isReady;
- console.log(`-> Fetching councils`);
- const head = await getHead(api);
- getCouncils(api, +head).then(async (councils: Round[]) => {
- api.disconnect();
- if (round === null || isNaN(round)) {
- console.log(`-> Updating reports`);
- await Promise.all(
- councils.map(({ start, end }) => createReports(start, end, config))
- );
- } else {
- const council = councils.find((c) => c.round === round);
- if (!council) return console.warn(`Round ${round} not found:`, councils);
- console.log(
- `-> Updating round ${round} (${council.start}-${council.end})`
- );
- await createReports(council.start, council.end, config);
- }
- process.exit();
- });
- };
- const createReports = (
- startBlock: number,
- endBlock: number,
- config: Config
- ) => {
- councilReport(startBlock, endBlock, config);
- return tokenomicsReport(startBlock, endBlock, config);
- };
- const main = async (config: Config) => {
- const args = process.argv.slice(2);
- if (args.length < 2) return updateReports(config, Number(args[0]));
- const startBlock = Number(args[0]);
- const endBlock = Number(args[1]);
- if (isNaN(startBlock) || isNaN(endBlock) || startBlock >= endBlock) {
- console.error("Invalid block range.");
- process.exit(1);
- }
- createReports(startBlock, endBlock, config);
- };
- main(CONFIG);
|