proposals.ts 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131
  1. import { WsProvider, ApiPromise } from "@polkadot/api";
  2. import { types } from "@joystream/types";
  3. import { Active, Approved, ExecutionFailed, Finalized, Proposal, ProposalDetails, ProposalId } from "@joystream/types/proposals";
  4. import { StorageKey, Vec } from "@polkadot/types";
  5. import { Membership } from "@joystream/types/members";
  6. import { Stake, Staked, StakeId } from "@joystream/types/stake";
  7. import { AnyJson } from "@polkadot/types/types";
  8. interface ProposalOverview {
  9. id: number,
  10. type: string,
  11. title: string,
  12. createdBy: [number,string],
  13. stakeId: number,
  14. stake: number,
  15. created: number,
  16. status: string,
  17. finalizedAt?: number,
  18. result?: string,
  19. feePaid?: number,
  20. executionStatus?: string
  21. executeAt?: number,
  22. voteResult?: AnyJson,
  23. execution?: AnyJson,
  24. }
  25. async function main() {
  26. // Initialise the provider to connect to the local node
  27. const provider = new WsProvider('ws://127.0.0.1:9944');
  28. //If you want to play around on our staging network, go ahead and connect to this staging network instead.
  29. //const provider = new WsProvider('wss://testnet-rpc-2-singapore.joystream.org');
  30. // Create the API and wait until ready
  31. const api = await ApiPromise.create({ provider, types })
  32. const allProposals: ProposalOverview[] = []
  33. // get all proposalIds
  34. // sort them by id
  35. const proposalKeys = await api.query.proposalsEngine.proposals.keys()
  36. const proposalIds = proposalKeys.map(({ args: [proposalId]}) => proposalId) as Vec<ProposalId>
  37. proposalIds.sort((a,b) => a.toNumber()-b.toNumber())
  38. console.log("number of proposals", proposalIds.length);
  39. // get all stakeIds associated with proposalIds
  40. const stakeIdOfProposalId = await api.query.proposalsEngine.stakesProposals.keys() as StorageKey[]
  41. const stakeIdsOfProposalIds = stakeIdOfProposalId.map(({ args: [stakeId]}) => stakeId) as Vec<StakeId>
  42. stakeIdsOfProposalIds.sort((a,b) => a.toNumber()-b.toNumber())
  43. console.log("number of stakeIdsOfProposalIds:", stakeIdsOfProposalIds.length);
  44. for (let id of proposalIds) {
  45. const proposal = await api.query.proposalsEngine.proposals(id) as Proposal
  46. const proposerHandle = (await api.query.members.membershipById(proposal.proposerId) as Membership).handle.toString()
  47. const proposalStatus = proposal.status.value
  48. const proposalDetails = await api.query.proposalsCodex.proposalDetailsByProposalId(id) as ProposalDetails
  49. let stakeId = (stakeIdsOfProposalIds[proposalIds.indexOf(id)]).toNumber()
  50. let stake = 0
  51. if ((await api.query.proposalsEngine.stakesProposals(stakeId) as ProposalId).toNumber() === id.toNumber()) {
  52. const blockHash = await api.rpc.chain.getBlockHash(proposal.createdAt)
  53. const proposalStake = await api.query.stake.stakes.at(blockHash,stakeId) as Stake
  54. if (proposalStake.staking_status instanceof Staked) {
  55. stake = proposalStake.staking_status.staked_amount.toNumber()
  56. }
  57. } else {
  58. // This should never be the case...
  59. stakeId = -1
  60. }
  61. const proposalData: ProposalOverview = {
  62. id: id.toNumber(),
  63. type: proposalDetails.type.toString(),
  64. title: proposal.title.toString(),
  65. createdBy: [proposal.proposerId.toNumber(),proposerHandle],
  66. stakeId,
  67. stake,
  68. created: proposal.createdAt.toNumber(),
  69. status: proposal.status.value.toString(),
  70. }
  71. // these proposals will have an annoyngly large 'execution'
  72. if (proposalDetails.type != "Text" && proposalDetails.type!= "RuntimeUpgrade") {
  73. proposalData.execution = proposalDetails.value.toHuman()
  74. }
  75. // check if the proposal is "Active"
  76. if (proposalStatus instanceof Active) {
  77. proposalData.status = proposalStatus.value.toString()
  78. } else {
  79. // There really isn't any other options here...
  80. if (proposalStatus instanceof Finalized) {
  81. proposalData.status = proposalStatus.proposalStatus.type
  82. proposalData.finalizedAt = proposalStatus.finalizedAt.toNumber()
  83. proposalData.voteResult = proposal.votingResults.toHuman()
  84. const proposalResult = proposalStatus.proposalStatus.value
  85. // check if the proposal is "Approved"
  86. if (proposalResult instanceof Approved) {
  87. proposalData.feePaid = 0
  88. const gracePeriod = proposal.parameters.gracePeriod.toNumber()
  89. proposalData.executeAt = proposalStatus.finalizedAt.toNumber() + gracePeriod
  90. proposalData.executionStatus = proposalResult.type
  91. // "Executed" and "PendingExecution" works differently than "ExecutionFailed"
  92. // the latter will have some information on what went wrong
  93. if (proposalResult.isOfType("Executed")) {
  94. } else if (proposalResult.isOfType("PendingExecution")) {
  95. } else if (proposalResult.value instanceof ExecutionFailed) {
  96. proposalData.executionStatus = proposalResult.type + `:` + proposalResult.value.error.toString()
  97. }
  98. } else {
  99. // If not finalized, but not approved, it must be one of...
  100. if (proposalStatus.proposalStatus.isOfType("Canceled")) {
  101. proposalData.feePaid = 10000
  102. } else if (proposalStatus.proposalStatus.isOfType("Slashed")) {
  103. proposalData.feePaid = stake
  104. } else if (proposalStatus.proposalStatus.isOfType("Vetoed")) {
  105. proposalData.feePaid = 0
  106. // .. "Expired" or "Rejected", which are treated as the same.
  107. } else {
  108. proposalData.feePaid = 5000
  109. }
  110. }
  111. }
  112. }
  113. allProposals.push(proposalData)
  114. }
  115. console.log("allProposals",JSON.stringify(allProposals, null, 4))
  116. api.disconnect()
  117. }
  118. main()