Explorar el Código

Add spending proposal values, roles occupants & bounties paid value

Ricardo Maltez hace 3 años
padre
commit
746df51fcf

+ 42 - 0
community-contributions/report-generator/bounties.csv

@@ -0,0 +1,42 @@
+Spending Proposal Categories,,,,,
+,,,,,
+Proposal #,Title,Status,Amount Asked,Amount Minted,Category
+1,,,,,
+2,,,,,
+3,,,,,
+4,,,,,
+5,,,,,
+6,Resub: Com Repo Banner Competition Prop,Approved,1500000,1500000,Competitions
+7,Antioch - KPI 0.2 - Council Secretary,Approved,1500000,1500000,Informal Roles
+8,Resub: Hire for role: Bounty 10 Admin,Approved,540000,540000,Bounty Managers
+9,Success Event 1 for Boutny 13,Approved,2440000,2440000,Bounties
+10,Bounty #12: Endpoints April/2021,Expired,5000000,0,Bounties
+11,,,,,
+12,,,,,
+13,,,,,
+14,Forum Rewards Funding,Expired,3000000,0,Competitions
+15,,,,,
+16,Bounty #12: Endpoints April/2021,Approved,5000000,5000000,Bounties
+17,,,,,
+18,,,,,
+19,KPI 00.1 - Tokenomics Report,Approved,390000,390000,KPIs
+20,,,,,
+21,Antioch KPI 1.7,Cancelled,1800000,0,KPIs
+22,,,,,
+23,,,,,
+24,,,,,
+25,,,,,
+26,,,,,
+27,Bounty #13: Example Discord Bot,Approved,2500000,2500000,Bounties
+28,,,,,
+29,,,,,
+30,,,,,
+31,,,,,
+32,,,,,
+33,,,,,
+34,Bounty 14: Submission 1,Approved,1290000,1290000,Bounties
+35,Bounty 14: Submission 2,Pending,1300000,1300000,Bounties
+36,,,,,
+37,,,,,
+38,,,,,
+39,,,,,

+ 3 - 2
community-contributions/report-generator/package.json

@@ -10,14 +10,15 @@
   "dependencies": {
     "@joystream/types": "^0.15.0",
     "@polkadot/api": "4.2.1",
+    "@polkadot/api-contract": "4.2.1",
     "@polkadot/keyring": "^6.0.5",
     "@polkadot/types": "4.2.1",
     "@polkadot/util": "^6.0.5",
     "@polkadot/util-crypto": "^6.0.5",
-    "@polkadot/api-contract": "4.2.1",
     "@polkadot/wasm-crypto": "^4.0.2",
     "@types/bn.js": "^4.11.6",
-    "bn.js": "^5.1.2"
+    "bn.js": "^5.1.2",
+    "csv-parse": "^4.15.4"
   },
   "devDependencies": {
     "@polkadot/ts": "^0.3.62",

+ 9 - 4
community-contributions/report-generator/report-template.md

@@ -14,15 +14,16 @@ This is a report which explains the current state of the Joystream network in nu
 
 | Property            | Value        |
 |---------------------|--------------|
-| Total Tokens Burned | {newTokensBurn} | 
+| Total Tokens Burned | {newTokensBurn}           | 
+| Spending Proposals (Executed)   |  {spendingProposalsTotal}                  |
+| Bounties paid       |  {bountiesTotalPaid}                           |
 | Validator Role      |  {newValidatorRewards}            | 
 | Council Role        | {newCouncilRewards}             | 
 | Storage Role        | {newStorageProviderReward}             | 
-| Curator Role        | {newCuratorRewards}             | 
+| Curator Role        | {newCuratorRewards}             |
 
 
-
-### 2.2 Mints 
+### 2.3 Mints 
 | Property                    | Start Block           | End Block | % Change |
 |-----------------------------|-----------------------|--------------|----------|
 | Council Mint Total Minted   | {startCouncilMinted}  |  {endCouncilMinted} |{percNewCouncilMinted}          |
@@ -57,11 +58,15 @@ This is a report which explains the current state of the Joystream network in nu
 |-------------------------|--------------|--------------|----------|
 | Number of Storage Workers | {startStorageProviders}  |  {endStorageProviders} | {percNewStorageProviders} |
 | Total Storage Stake (workers + lead)  | {startStorageProvidersStake} |  {endStorageProvidersStake} | {percNewStorageProviderStake} |
+Role occupants:  
+{storageProviders}
 
 ### 4.3 Curator Role
 | Property                | Start Block | End Block | % Change |
 |-------------------------|--------------|--------------|----------|
 | Number of Curators      | {startCurators} | {endCurators} | {percNewCurators} |
+Role occupants:  
+{curators}
 
 ## 5.0 User Generated Content
 ### 5.1 Membership Information

+ 99 - 24
community-contributions/report-generator/src/StatisticsCollector.ts

@@ -16,9 +16,9 @@ import {
     Exchange,
     Media,
     MintStatistics,
-    StatisticsData,
-    ValidatorReward, WorkersInfo, Channel
-} from "./StatisticsData";
+    Statistics,
+    ValidatorReward, WorkersInfo, Channel, SpendingProposals, Bounty
+} from "./types";
 
 import {Option, u32, Vec} from "@polkadot/types";
 import {ElectionStake, SealedVote, Seats} from "@joystream/types/council";
@@ -30,7 +30,7 @@ import Linkage from "@polkadot/types/codec/Linkage";
 import {PostId, ThreadId} from "@joystream/types/common";
 import {CategoryId} from "@joystream/types/forum";
 
-import {MemberId} from "@joystream/types/members";
+import {MemberId, Membership} from "@joystream/types/members";
 import {RewardRelationship, RewardRelationshipId} from "@joystream/types/recurring-rewards";
 
 import workingGroup from "@joystream/types/src/working-group/index";
@@ -38,11 +38,13 @@ import {Stake} from "@joystream/types/stake";
 import {ChannelId} from "@joystream/types/content-working-group";
 import {WorkerId} from "@joystream/types/working-group";
 import {Entity, EntityId, PropertyType} from "@joystream/types/content-directory";
-import {WorkerOf} from "@joystream/types/augment-codec/all";
-
+import {ProposalDetails, ProposalId, WorkerOf} from "@joystream/types/augment-codec/all";
+import {SpendingParams} from "@joystream/types/proposals";
+import * as constants from "constants";
 
 const fsSync = require('fs');
 const fs = fsSync.promises;
+const parse = require('csv-parse/lib/sync');
 
 const BURN_ADDRESS = '5D5PhZQNJzcJXVBxwJxZcsutjKPqUPydrvpu6HeiBfMaeKQu';
 
@@ -59,14 +61,14 @@ export class StatisticsCollector {
 
     private api?: ApiPromise;
     private blocksEventsCache: Map<number, CacheEvent[]>;
-    private statistics: StatisticsData;
+    private statistics: Statistics;
 
     constructor() {
         this.blocksEventsCache = new Map<number, CacheEvent[]>();
-        this.statistics = new StatisticsData();
+        this.statistics = new Statistics();
     }
 
-    async getStatistics(startBlock: number, endBlock: number): Promise<StatisticsData> {
+    async getStatistics(startBlock: number, endBlock: number): Promise<Statistics> {
         this.api = await StatisticsCollector.connectApi();
 
         let startHash = (await this.api.rpc.chain.getBlockHash(startBlock)) as Hash;
@@ -88,23 +90,32 @@ export class StatisticsCollector {
         await this.fillMembershipInfo(startHash, endHash);
         await this.fillMediaUploadInfo(startHash, endHash);
         await this.fillForumInfo(startHash, endHash);
+
         await this.api.disconnect();
         return this.statistics;
     }
 
-    async fillBasicInfo(startHash: Hash, endHash: Hash) {
-        let startDate = (await this.api.query.timestamp.now.at(startHash)) as Moment;
-        let endDate = (await this.api.query.timestamp.now.at(endHash)) as Moment;
-        this.statistics.dateStart = new Date(startDate.toNumber()).toLocaleDateString("en-US");
-        this.statistics.dateEnd = new Date(endDate.toNumber()).toLocaleDateString("en-US");
-    }
+    async getApprovedBounties() {
+        let bountiesFilePath = __dirname + '/../bounties.csv';
+        try {
+            await fs.access(bountiesFilePath, constants.R_OK);
+        } catch {
+            throw new Error('Bounties CSV file not found');
+        }
 
-    async fillTokenGenerationInfo(startBlock: number, endBlock: number, startHash: Hash, endHash: Hash) {
-        this.statistics.startIssuance = (await this.api.query.balances.totalIssuance.at(startHash) as Balance).toNumber();
-        this.statistics.endIssuance = (await this.api.query.balances.totalIssuance.at(endHash) as Balance).toNumber();
-        this.statistics.newIssuance = this.statistics.endIssuance - this.statistics.startIssuance;
-        this.statistics.percNewIssuance = StatisticsCollector.convertToPercentage(this.statistics.startIssuance, this.statistics.endIssuance);
+        const fileContent = await fs.readFile(bountiesFilePath);
+        const rawBounties = parse(fileContent);
+        rawBounties.shift();
+
+        let bounties = rawBounties.map((rawBounty: any) => {
+            return new Bounty(rawBounty[0], rawBounty[1], rawBounty[2], rawBounty[3], rawBounty[4]);
+        });
 
+        return bounties.filter((bounty: Bounty) => bounty.status == "Approved");
+    }
+
+    async getSpendingProposals() : Promise<Array<SpendingProposals>>{
+        let spendingProposals = new Array<SpendingProposals>();
         for (let [key, blockEvents] of this.blocksEventsCache) {
             let validatorRewards = blockEvents.filter((event) => {
                 return event.section == "staking" && event.method == "Reward";
@@ -123,7 +134,54 @@ export class StatisticsCollector {
                     this.statistics.newTokensBurn = Number(amount);
                 }
             }
+
+            let proposalEvents = blockEvents.filter((event) => {
+                return event.section == "proposalsEngine" && event.method == "ProposalStatusUpdated";
+            });
+
+            for (let proposalEvent of proposalEvents) {
+                let statusUpdateData = proposalEvent.data[1] as any;
+                if (!(statusUpdateData.finalized && statusUpdateData.finalized.finalizedAt)) {
+                    continue;
+                }
+                let proposalId = proposalEvent.data[0] as ProposalId;
+                let proposalDetail = await this.api.query.proposalsCodex.proposalDetailsByProposalId(proposalId) as ProposalDetails;
+                if (!proposalDetail.isOfType("Spending")) {
+                    continue;
+                }
+                let spendingParams = Array.from(proposalDetail.asType("Spending") as SpendingParams);
+                spendingProposals.push(new SpendingProposals(Number(proposalId), Number(spendingParams[0])));
+            }
         }
+        return spendingProposals;
+    }
+
+    async fillBasicInfo(startHash: Hash, endHash: Hash) {
+        let startDate = (await this.api.query.timestamp.now.at(startHash)) as Moment;
+        let endDate = (await this.api.query.timestamp.now.at(endHash)) as Moment;
+        this.statistics.dateStart = new Date(startDate.toNumber()).toLocaleDateString("en-US");
+        this.statistics.dateEnd = new Date(endDate.toNumber()).toLocaleDateString("en-US");
+    }
+
+    async fillTokenGenerationInfo(startBlock: number, endBlock: number, startHash: Hash, endHash: Hash) {
+        this.statistics.startIssuance = (await this.api.query.balances.totalIssuance.at(startHash) as Balance).toNumber();
+        this.statistics.endIssuance = (await this.api.query.balances.totalIssuance.at(endHash) as Balance).toNumber();
+        this.statistics.newIssuance = this.statistics.endIssuance - this.statistics.startIssuance;
+        this.statistics.percNewIssuance = StatisticsCollector.convertToPercentage(this.statistics.startIssuance, this.statistics.endIssuance);
+
+        let bounties = await this.getApprovedBounties();
+        let spendingProposals = await this.getSpendingProposals();
+
+        this.statistics.bountiesTotalPaid = 0;
+        for (let bounty of bounties){
+            let bountySpendingProposal = spendingProposals.find((spendingProposal) => spendingProposal.id == bounty.proposalId);
+            if (bountySpendingProposal){
+                this.statistics.bountiesTotalPaid += bountySpendingProposal.spentAmount;
+            }
+        }
+
+        this.statistics.spendingProposalsTotal = spendingProposals.reduce((n, spendingProposal) => n + spendingProposal.spentAmount, 0);
+
         let roundNrBlocks = endBlock - startBlock;
         this.statistics.newCouncilRewards = await this.computeCouncilReward(roundNrBlocks, endHash);
         this.statistics.newCouncilRewards = Number(this.statistics.newCouncilRewards.toFixed(2));
@@ -414,16 +472,33 @@ export class StatisticsCollector {
         this.statistics.endStorageProvidersStake = storageProvidersRewards.endStake;
         this.statistics.percNewStorageProviderStake = StatisticsCollector.convertToPercentage(this.statistics.startStorageProvidersStake, this.statistics.endStorageProvidersStake);
 
-        this.statistics.startStorageProviders = (await this.api.query.storageWorkingGroup.nextWorkerId.at(startHash) as WorkerId).toNumber() - WORKER_ID_OFFSET;
-        this.statistics.endStorageProviders = (await this.api.query.storageWorkingGroup.nextWorkerId.at(endHash) as WorkerId).toNumber() - WORKER_ID_OFFSET;
+        this.statistics.startStorageProviders = await this.api.query.storageWorkingGroup.activeWorkerCount.at(startHash);
+        this.statistics.endStorageProviders = await this.api.query.storageWorkingGroup.activeWorkerCount.at(endHash);
         this.statistics.percNewStorageProviders = StatisticsCollector.convertToPercentage(this.statistics.startStorageProviders, this.statistics.endStorageProviders);
 
+        let lastStorageProviderId = Number(await this.api.query.storageWorkingGroup.nextWorkerId.at(endHash)) - 1;
+        this.statistics.storageProviders = "";
+        for (let i = lastStorageProviderId, storageProviderCount = 0; storageProviderCount < this.statistics.endStorageProviders; --i, ++storageProviderCount){
+            let storageProvider = await this.api.query.storageWorkingGroup.workerById.at(endHash, i) as WorkerOf;
+            let membership = await this.api.query.members.membershipById.at(endHash, storageProvider.member_id) as Membership;
+            this.statistics.storageProviders += "@" + membership.handle + " | (" + membership.root_account  +")  \n";
+        }
+
     }
 
     async fillCuratorInfo(startHash: Hash, endHash: Hash) {
-        this.statistics.startCurators = (await this.api.query.contentDirectoryWorkingGroup.nextWorkerId.at(startHash));
-        this.statistics.endCurators = (await this.api.query.contentDirectoryWorkingGroup.nextWorkerId.at(endHash));
+        this.statistics.startCurators = Number(await this.api.query.contentDirectoryWorkingGroup.activeWorkerCount.at(startHash));
+        this.statistics.endCurators = Number(await this.api.query.contentDirectoryWorkingGroup.activeWorkerCount.at(endHash));
         this.statistics.percNewCurators = StatisticsCollector.convertToPercentage(this.statistics.startCurators, this.statistics.endCurators);
+
+        let lastCuratorId = Number(await this.api.query.contentDirectoryWorkingGroup.nextWorkerId.at(endHash)) - 1;
+        this.statistics.curators = "";
+        for (let i = lastCuratorId, curatorCount = 0; curatorCount < this.statistics.endCurators; --i, ++curatorCount){
+            let curator = await this.api.query.contentDirectoryWorkingGroup.workerById.at(endHash, i) as WorkerOf;
+            let curatorMembership = await this.api.query.members.membershipById.at(endHash, curator.member_id) as Membership;
+            this.statistics.curators += "@" + curatorMembership.handle + " | (" + curatorMembership.root_account  +")  \n";
+        }
+
     }
 
     async fillMembershipInfo(startHash: Hash, endHash: Hash) {

+ 105 - 65
community-contributions/report-generator/src/block-interval.ts

@@ -1,85 +1,125 @@
-// import {ApiPromise, WsProvider} from "@polkadot/api";
-// import {Hash, Header} from "@polkadot/types/interfaces/runtime";
-// import { types } from '@joystream/types'
+import {ApiPromise, WsProvider} from "@polkadot/api";
+import {types} from '@joystream/types';
+
+async function main() {
+
+    const provider = new WsProvider('ws://localhost:9944');
+    const api = await ApiPromise.create({provider, types});
+    const hash = await api.rpc.chain.getBlockHash(155528);
+    console.log(hash.toHuman());
+}
+main();
+
+//
+
+// import {Moment} from "@polkadot/types/interfaces";
+// //
+// const fs = require('fs');
 //
 // async function main() {
-//     let startDate = new Date(2020, 4, 20, 13, 0);
-//     console.log(startDate);
-//     let endDate = new Date(2020, 4, 29, 23, 59);
+//     // let startDate = new Date(2020, 4, 20, 13, 0);
+//     // console.log(startDate);
+//     // let endDate = new Date(2020, 4, 29, 23, 59);
 //
 //     // Initialise the provider to connect to the local node
-//     const provider = new WsProvider('wss://rome-rpc-endpoint.joystream.org:9944');
+//     const provider = new WsProvider('wss://babylon.joystreamstats.live:9945');
 //
 //     // Create the API and wait until ready
 //     const api = await ApiPromise.create({provider, types});
 //
-//     let blockInterval = await getBlockInterval(api, startDate.getTime(), endDate.getTime());
-//     console.log(blockInterval);
-// }
-//
-// async function getBlockInterval(api: ApiPromise, startTimestamp: number, endTimestamp: number) {
-//
-//     let approximateStartBlockHash = await getApproximatedBlockHash(api, startTimestamp);
-//     let startBlock = await adjustApproximatedBlockHash(api, startTimestamp, approximateStartBlockHash);
-//
-//     let approximateEndBlockHash = await getApproximatedBlockHash(api, endTimestamp);
-//     let endBlock = await adjustApproximatedBlockHash(api, endTimestamp, approximateEndBlockHash);
-//
-//     let startBlockHeader = await api.rpc.chain.getHeader(startBlock) as Header;
-//     let endBlockHeader = await api.rpc.chain.getHeader(endBlock) as Header;
+//     const hash = await api.rpc.chain.getBlockHash(2528244);
 //
-//     return {
-//         'startBlock':
-//             startBlockHeader.number.unwrap().toNumber(),
-//         'endBlock':
-//             endBlockHeader.number.unwrap().toNumber()
-//     };
-// }
-//
-// async function getApproximatedBlockHash(api: ApiPromise, timestampToFound: number): Promise<Hash> {
-//     let lastHeader = await api.rpc.chain.getHeader();
-//     let lastHash = lastHeader.hash.toString();
-//     let lastTimestamp = parseInt((await api.query.timestamp.now.at(lastHash)).toString());
-//
-//     let prevousBlockHash = lastHeader.parentHash;
-//     let previousBlockTimestamp = parseInt((await api.query.timestamp.now.at(prevousBlockHash)).toString());
+//     const block = await api.rpc.chain.getBlock(hash);
 //
-//     let secondsPerBlock = lastTimestamp - previousBlockTimestamp;
-//
-//     let blocksDiff = Math.floor((lastTimestamp - timestampToFound) / secondsPerBlock);
-//     let lastBlockNumber = lastHeader.number.unwrap();
-//     let approximatedBlockNr = lastBlockNumber.toNumber() - blocksDiff;
-//     return await api.rpc.chain.getBlockHash(approximatedBlockNr);
-// }
+//     let startDate = (await api.query.timestamp.now.at(hash)) as Moment;
+//     console.log(startDate.toString());
 //
-// async function adjustApproximatedBlockHash(api: ApiPromise, timestamp: number, hash: Hash) {
-//     let approximatedBlockTimestamp = parseInt((await api.query.timestamp.now.at(hash)).toString());
+//     // console.log(block.toJSON());
 //
-//     if (timestamp == approximatedBlockTimestamp) {
-//         return hash;
-//     }
+//     // let ext = block.block.extrinsics[1];
+//     // let json = ext.toHuman() as any;
+//     // let buffer = Buffer.from(json.method.args[0].replace(/0x/g, ''), "hex");
+//     // fs.writeFile('image.png', ext.data, (err: any) => {
+//     //     console.log(err);
+//     // });
+//     // // }
 //
-//     let step = 1;
-//     if (timestamp < approximatedBlockTimestamp) {
-//         step = -1;
-//     }
 //
-//     let approximatedBlockHeader = await api.rpc.chain.getHeader(hash);
-//     let blockNumber = approximatedBlockHeader.number.unwrap().toNumber();
-//     let lastHashFound = hash;
-//     do {
-//         blockNumber += step;
-//         let nextBlockHash = await api.rpc.chain.getBlockHash(blockNumber);
-//         let nextBlockTimeStamp = parseInt((await api.query.timestamp.now.at(nextBlockHash)).toString());
+//     // const events = await api.query.system.events.at(hash);
 //
-//         if (Math.abs(approximatedBlockTimestamp - timestamp) < Math.abs(nextBlockTimeStamp - timestamp)) {
-//             return lastHashFound;
-//         }
+//     // for(let event of events){
+//     //     console.log(event.event.data);
+//     // }
 //
-//         approximatedBlockTimestamp = nextBlockTimeStamp;
-//         lastHashFound = nextBlockHash;
 //
-//     } while (true);
+//     // let blockInterval = await getBlockInterval(api, startDate.getTime(), endDate.getTime());
+//     // console.log(blockInterval);
 // }
 //
+// //
+// // async function getBlockInterval(api: ApiPromise, startTimestamp: number, endTimestamp: number) {
+// //
+// //     let approximateStartBlockHash = await getApproximatedBlockHash(api, startTimestamp);
+// //     let startBlock = await adjustApproximatedBlockHash(api, startTimestamp, approximateStartBlockHash);
+// //
+// //     let approximateEndBlockHash = await getApproximatedBlockHash(api, endTimestamp);
+// //     let endBlock = await adjustApproximatedBlockHash(api, endTimestamp, approximateEndBlockHash);
+// //
+// //     let startBlockHeader = await api.rpc.chain.getHeader(startBlock) as Header;
+// //     let endBlockHeader = await api.rpc.chain.getHeader(endBlock) as Header;
+// //
+// //     return {
+// //         'startBlock':
+// //             startBlockHeader.number.unwrap().toNumber(),
+// //         'endBlock':
+// //             endBlockHeader.number.unwrap().toNumber()
+// //     };
+// // }
+// //
+// // async function getApproximatedBlockHash(api: ApiPromise, timestampToFound: number): Promise<Hash> {
+// //     let lastHeader = await api.rpc.chain.getHeader();
+// //     let lastHash = lastHeader.hash.toString();
+// //     let lastTimestamp = parseInt((await api.query.timestamp.now.at(lastHash)).toString());
+// //
+// //     let prevousBlockHash = lastHeader.parentHash;
+// //     let previousBlockTimestamp = parseInt((await api.query.timestamp.now.at(prevousBlockHash)).toString());
+// //
+// //     let secondsPerBlock = lastTimestamp - previousBlockTimestamp;
+// //
+// //     let blocksDiff = Math.floor((lastTimestamp - timestampToFound) / secondsPerBlock);
+// //     let lastBlockNumber = lastHeader.number.unwrap();
+// //     let approximatedBlockNr = lastBlockNumber.toNumber() - blocksDiff;
+// //     return await api.rpc.chain.getBlockHash(approximatedBlockNr);
+// // }
+// //
+// // async function adjustApproximatedBlockHash(api: ApiPromise, timestamp: number, hash: Hash) {
+// //     let approximatedBlockTimestamp = parseInt((await api.query.timestamp.now.at(hash)).toString());
+// //
+// //     if (timestamp == approximatedBlockTimestamp) {
+// //         return hash;
+// //     }
+// //
+// //     let step = 1;
+// //     if (timestamp < approximatedBlockTimestamp) {
+// //         step = -1;
+// //     }
+// //
+// //     let approximatedBlockHeader = await api.rpc.chain.getHeader(hash);
+// //     let blockNumber = approximatedBlockHeader.number.unwrap().toNumber();
+// //     let lastHashFound = hash;
+// //     do {
+// //         blockNumber += step;
+// //         let nextBlockHash = await api.rpc.chain.getBlockHash(blockNumber);
+// //         let nextBlockTimeStamp = parseInt((await api.query.timestamp.now.at(nextBlockHash)).toString());
+// //
+// //         if (Math.abs(approximatedBlockTimestamp - timestamp) < Math.abs(nextBlockTimeStamp - timestamp)) {
+// //             return lastHashFound;
+// //         }
+// //
+// //         approximatedBlockTimestamp = nextBlockTimeStamp;
+// //         lastHashFound = nextBlockHash;
+// //
+// //     } while (true);
+// // }
+// //
 // main();

+ 19 - 2
community-contributions/report-generator/src/StatisticsData.ts → community-contributions/report-generator/src/types.ts

@@ -1,6 +1,6 @@
 import {GenericEventData} from "@polkadot/types/generic/Event";
 
-export class StatisticsData {
+export class Statistics {
     councilRound: number = 0;
     councilMembers: number = 0;
 
@@ -128,13 +128,18 @@ export class StatisticsData {
     newTextProposals: number = 0;
     newRuntimeUpgradeProposal: number = 0;
     newSetElectionParametersProposal: number = 0;
-    newSpendingProposal: number = 0;
+
+    spendingProposalsTotal: number = 0;
+    bountiesTotalPaid: number = 0;
+
     newSetLeadProposal: number = 0;
     newSetContentWorkingGroupMintCapacityProposal: number = 0;
     newEvictStorageProviderProposal: number = 0;
     newSetValidatorCountProposal: number = 0;
     newSetStorageRoleParametersProposal: number = 0;
 
+    storageProviders: string;
+    curators: string;
 
     constructor() {
     }
@@ -176,6 +181,13 @@ export enum ProposalTypes {
     SetStorageRoleParameters = "SetStorageRoleParameters",
 }
 
+export class SpendingProposals {
+
+    constructor(public id: number, public spentAmount: number) {
+    }
+
+}
+
 export class MintStatistics {
     startMinted: number;
     endMinted: number;
@@ -201,6 +213,11 @@ export class Channel {
     }
 }
 
+export class Bounty {
+    constructor(public proposalId: number, public title: string, public status: string, public amountAsked: number, public amountMinted: number) {
+    }
+}
+
 export class CacheEvent {
 
     constructor(public section: string, public method: string, public data: GenericEventData) {