rewards.ts 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171
  1. import { ApiPromise } from "@polkadot/api";
  2. // types
  3. import { Bounty, CacheEvent, WorkerReward, SpendingProposal } from "./types";
  4. import { AccountId, Balance } from "@polkadot/types/interfaces";
  5. import { Hash } from "@polkadot/types/interfaces";
  6. import { Membership } from "@joystream/types/members";
  7. import {
  8. Proposal,
  9. ProposalId,
  10. SpendingParams,
  11. } from "@joystream/types/proposals";
  12. import { ProposalDetails, ProposalOf } from "@joystream/types/augment/types";
  13. // lib
  14. import { getPercent } from "./";
  15. import {
  16. getBlock,
  17. getBlockHash,
  18. getNextWorker,
  19. getMember,
  20. getWorker,
  21. getProposalInfo,
  22. getProposalDetails,
  23. getStake,
  24. getValidators,
  25. getValidatorCount,
  26. } from "./api";
  27. import { Worker } from "@joystream/types/augment-codec/all";
  28. import { ProposalDetailsOf } from "@joystream/types/augment/types";
  29. export const filterMethods = {
  30. getBurnedTokens: ({ section, method }: CacheEvent) =>
  31. section === "balances" && method === "Transfer",
  32. newValidatorsRewards: ({ section, method }: CacheEvent) =>
  33. section === "staking" && method === "Reward",
  34. finalizedSpendingProposals: ({ section, method }: CacheEvent) =>
  35. section === "proposalsEngine" && method === "ProposalStatusUpdated",
  36. sudoSetBalance: ({ section, method }: CacheEvent) =>
  37. section == "balances" && method == "BalanceSet",
  38. };
  39. export const getWorkerRewards = async (
  40. api: ApiPromise,
  41. group: string,
  42. hash: Hash
  43. ): Promise<WorkerReward[]> => {
  44. let workers = Array<WorkerReward>();
  45. const nextWorkerId = await getNextWorker(api, group, hash);
  46. for (let id = 0; id < nextWorkerId; ++id) {
  47. const worker: Worker = await getWorker(api, group, hash, id);
  48. const account = worker.role_account_id as AccountId;
  49. const memberId = worker.member_id;
  50. const member: Membership = await getMember(api, memberId, hash);
  51. const handle = member ? String(member.handle_hash) : account.toString();
  52. const status = worker.is_active ? `active` : `inactive`;
  53. // fetch reward and stake
  54. const w: WorkerReward = { id, status, handle, account, memberId };
  55. /**
  56. if (worker.role_stake_profile.isSome) {
  57. const roleStakeProfile = worker.role_stake_profile.unwrap();
  58. w.stake = await getStake(api, roleStakeProfile.stake_id);
  59. }
  60. if (worker.reward_relationship.isSome) {
  61. const id: RewardRelationshipId = worker.reward_relationship.unwrap();
  62. w.reward = await getWorkerReward(api, hash, id);
  63. } **/
  64. workers.push(w);
  65. }
  66. return workers;
  67. };
  68. export const getWorkerRow = (
  69. worker: WorkerReward,
  70. earnedStart: number
  71. ): string => {
  72. const mtjoy = (mtjoy: number): string => (mtjoy / 1000000).toFixed(1);
  73. const { id, memberId, account, handle, status, reward } = worker;
  74. if (!reward) return ``;
  75. const earnedEnd = Number(reward.total_reward_received.toBigInt());
  76. if (!earnedEnd) return ``;
  77. const totalEarned = mtjoy(earnedEnd);
  78. const earnedTerm = mtjoy(earnedEnd - earnedStart);
  79. const amount = Number(reward.amount_per_payout.toBigInt());
  80. const rewardPerBlock = (amount / Number(reward.payout_interval)).toFixed();
  81. const url = `https://pioneer.joystreamstats.live/#/members/${handle}`; // TODO
  82. return `| ${id} | [@${handle}](${url}) | ${status} | ${rewardPerBlock} | ${earnedTerm} | ${totalEarned} |\n`;
  83. };
  84. export const getBurnedTokens = (
  85. burnAddress: string,
  86. blocks: [number, CacheEvent[]][]
  87. ): number => {
  88. let tokensBurned = 0;
  89. blocks.forEach(([key, transfers]) =>
  90. transfers.forEach((transfer) => {
  91. let receiver = transfer.data[1] as AccountId;
  92. let amount = transfer.data[2] as Balance;
  93. if (receiver.toString() === burnAddress) tokensBurned += Number(amount);
  94. })
  95. );
  96. return tokensBurned;
  97. };
  98. export const getFinalizedSpendingProposals = async (
  99. api: ApiPromise,
  100. blocks: [number, CacheEvent[]][]
  101. ): Promise<SpendingProposal[]> => {
  102. const spendingProposals: SpendingProposal[] = [];
  103. for (const [key, events] of blocks) {
  104. for (const event of events) {
  105. let statusUpdateData = event.data[1] as any;
  106. const finalizedAt = statusUpdateData.finalized.finalizedAt;
  107. if (!(statusUpdateData.finalized && finalizedAt)) continue;
  108. const proposalId = event.data[0] as ProposalId;
  109. const id = +proposalId;
  110. const proposalInfo: ProposalOf = await getProposalInfo(api, proposalId);
  111. const details: ProposalDetailsOf = await getProposalDetails(
  112. api,
  113. proposalId
  114. );
  115. if (!proposalInfo.status || !details.isFundingRequest) continue;
  116. if (spendingProposals.some((proposal) => +proposal.id === +id)) continue;
  117. const title = ``; //String(proposalInfo.title.toHuman());
  118. const amount = 0;
  119. console.log(`Spent ${amount.toFixed().padStart(8)} on ${id} ${title}`);
  120. spendingProposals.push({ id, title, amount });
  121. }
  122. }
  123. return spendingProposals;
  124. };
  125. export const getValidatorsRewards = (
  126. blocks: [number, CacheEvent[]][]
  127. ): number => {
  128. let newValidatorRewards = 0;
  129. blocks.forEach(([key, validatorRewards]) =>
  130. validatorRewards.forEach(
  131. (reward: CacheEvent) => (newValidatorRewards += Number(reward.data[1]))
  132. )
  133. );
  134. return newValidatorRewards;
  135. };
  136. export const getActiveValidators = async (
  137. api: ApiPromise,
  138. hash: Hash,
  139. searchPreviousBlocks: boolean = false
  140. ): Promise<AccountId[]> => {
  141. const block = await getBlock(api, hash);
  142. let currentBlockNr = block.block.header.number.toNumber();
  143. let activeValidators: AccountId[] = [];
  144. while (!activeValidators.length) {
  145. const hash: Hash = await getBlockHash(api, currentBlockNr);
  146. const validators: AccountId[] = await getValidators(api, hash);
  147. if (validators.length) {
  148. let max = await getValidatorCount(api, hash);
  149. activeValidators = validators.slice(0, max);
  150. }
  151. if (searchPreviousBlocks) --currentBlockNr;
  152. else ++currentBlockNr;
  153. }
  154. return activeValidators;
  155. };