Преглед изворни кода

Merge pull request #1 from Lezek123/tokenomics-minor-fixes

Tokenomics page - minor fixes
DzhideX пре 4 година
родитељ
комит
730895d7e1

+ 3 - 3
pioneer/packages/joy-tokenomics/package.json

@@ -1,6 +1,6 @@
 {
   "name": "@polkadot/joy-tokenomics",
-  "version": "0.37.0-beta.63",
+  "version": "0.1.1",
   "description": "Tokenomics page, basic overview of data from the whole website.",
   "main": "index.js",
   "scripts": {},
@@ -10,7 +10,7 @@
   ],
   "license": "Apache-2.0",
   "dependencies": {
-    "@babel/runtime": "^7.7.1",
-    "@polkadot/react-components": "^0.37.0-beta.63"
+    "@babel/runtime": "^7.10.5",
+    "@polkadot/react-components": "0.51.1"
   }
 }

+ 4 - 4
pioneer/packages/joy-tokenomics/src/Overview/OverviewTable.tsx

@@ -58,7 +58,7 @@ const OverviewTable: React.FC<{data?: TokenomicsData; statusData?: StatusServerD
         <OverviewTableRow
           item='Fiat Pool'
           help='The current value of the Fiat Pool.'
-          value={displayStatusData(`${statusData?.dollarPool.size.toFixed(2)}`, 'USD')}
+          value={displayStatusData(statusData?.dollarPool.size.toFixed(2) || '', 'USD')}
         />
         <OverviewTableRow
           item='Currently Staked Tokens'
@@ -67,7 +67,7 @@ const OverviewTable: React.FC<{data?: TokenomicsData; statusData?: StatusServerD
         />
         <OverviewTableRow
           item='Currently Staked Value'
-          value={statusData === null ? 'Data currently unavailable..' : (data && statusData) ? `${(data.currentlyStakedTokens * Number(statusData.price)).toFixed(2)} USD` : 'Loading...'}
+          value={ data ? displayStatusData(`${(data.currentlyStakedTokens * Number(statusData?.price)).toFixed(2)}`, 'USD') : 'Loading...' }
           help='The value of all tokens currently staked for active roles.'
         />
         <OverviewTableRow
@@ -88,12 +88,12 @@ const OverviewTable: React.FC<{data?: TokenomicsData; statusData?: StatusServerD
         />
         <OverviewTableRow
           item='Projected Weekly Value Of Mint'
-          value={statusData === null ? 'Data currently unavailable..' : (data && statusData) ? `${(data.totalWeeklySpending * Number(statusData.price)).toFixed(2)} USD` : 'Loading...'}
+          value={ data ? displayStatusData(`${(data.totalWeeklySpending * Number(statusData?.price)).toFixed(2)}`, 'USD') : 'Loading...'}
           help={'Based on \'Projected Weekly Token Mint Rate\', and current \'Exchange Rate\'.'}
         />
         <OverviewTableRow
           item='Weekly Top Ups'
-          value={displayStatusData(`${statusData?.dollarPool.replenishAmount}`, 'USD')}
+          value={displayStatusData(statusData?.dollarPool.replenishAmount.toFixed(2) || '', 'USD')}
           help={'The current weekly \'Fiat Pool\' replenishment amount. Does not include KPIs, or other potential top ups.'}
         />
       </Table.Body>

+ 2 - 2
pioneer/packages/joy-tokenomics/src/Overview/SpendingAndStakeDistributionTable.tsx

@@ -31,7 +31,7 @@ const StyledTable = styled(Table)`
   & tr {
     td:nth-of-type(1),
     th:nth-of-type(1),
-    ${(props): string => applyCss(props.dividecolumnsat)} {
+    ${(props): string => applyCss(props.divideColumnsAt)} {
       border-left: 0.12rem solid rgba(20,20,20,0.3) !important;
     }
     td:nth-of-type(1){
@@ -131,7 +131,7 @@ const SpendingAndStakeDistributionTable: React.FC<{data?: TokenomicsData; status
   };
 
   return (
-    <StyledTable dividecolumnsat={[3, 6, 9]} celled>
+    <StyledTable divideColumnsAt={[3, 6, 9]} celled>
       <Table.Header>
         <Table.Row>
           <Table.HeaderCell width={4}>Group/Role</Table.HeaderCell>

+ 19 - 22
pioneer/packages/joy-tokenomics/src/index.tsx

@@ -3,34 +3,31 @@ import { useTranslation } from './translate';
 import { Route, Switch } from 'react-router';
 import { Tabs } from '@polkadot/react-components';
 import Overview from './Overview';
-import { AppProps, I18nProps } from '@polkadot/react-components/types';
-import { TransportProvider } from '@polkadot/joy-utils/react/context';
+import { AppProps } from '@polkadot/react-components/types';
 
-interface Props extends AppProps, I18nProps {}
+type Props = AppProps
 
 function App ({ basePath }: Props): React.ReactElement<Props> {
   const { t } = useTranslation();
 
   return (
-    <TransportProvider>
-      <main>
-        <header>
-          <Tabs
-            basePath={basePath}
-            items={[
-              {
-                isRoot: true,
-                name: 'overview',
-                text: t('Tokenomics')
-              }
-            ]}
-          />
-        </header>
-        <Switch>
-          <Route component={Overview} />
-        </Switch>
-      </main>
-    </TransportProvider>
+    <main>
+      <header>
+        <Tabs
+          basePath={basePath}
+          items={[
+            {
+              isRoot: true,
+              name: 'overview',
+              text: t('Tokenomics')
+            }
+          ]}
+        />
+      </header>
+      <Switch>
+        <Route component={Overview} />
+      </Switch>
+    </main>
   );
 }
 

+ 5 - 0
pioneer/packages/joy-utils/src/consts/staking.ts

@@ -0,0 +1,5 @@
+// Values based on REWARD_CURVE const in /runtime/src/lib.rs
+export const IDEAL_STAKING_RATE = 0.25;
+export const MIN_INFLATION_RATE = 0.05;
+export const MAX_INFLATION_RATE = 0.75;
+export const FALL_OFF_RATE = 0.05;

+ 32 - 0
pioneer/packages/joy-utils/src/functions/staking.ts

@@ -0,0 +1,32 @@
+import { IDEAL_STAKING_RATE, MIN_INFLATION_RATE, MAX_INFLATION_RATE, FALL_OFF_RATE } from '../consts/staking';
+
+// See: https://github.com/Joystream/helpdesk/tree/master/roles/validators#rewards-on-joystream for reference
+export function calculateValidatorsRewardsPerEra (
+  totalValidatorsStake: number,
+  totalIssuance: number,
+  minutesPerEra = 60
+): number {
+  let validatorsRewardsPerYear = 0;
+  const stakingRate = totalValidatorsStake / totalIssuance;
+  const minutesPerYear = 365.2425 * 24 * 60;
+
+  if (stakingRate > IDEAL_STAKING_RATE) {
+    validatorsRewardsPerYear =
+      totalIssuance * (
+        MIN_INFLATION_RATE + (
+          (MAX_INFLATION_RATE - MIN_INFLATION_RATE) *
+          (2 ** ((IDEAL_STAKING_RATE - stakingRate) / FALL_OFF_RATE))
+        )
+      );
+  } else if (stakingRate === IDEAL_STAKING_RATE) {
+    validatorsRewardsPerYear = totalIssuance * MAX_INFLATION_RATE;
+  } else {
+    validatorsRewardsPerYear =
+      totalIssuance * (
+        MIN_INFLATION_RATE +
+        (MAX_INFLATION_RATE - MIN_INFLATION_RATE) * (stakingRate / IDEAL_STAKING_RATE)
+      );
+  }
+
+  return validatorsRewardsPerYear / minutesPerYear * minutesPerEra;
+}

+ 43 - 30
pioneer/packages/joy-utils/src/transport/tokenomics.ts

@@ -11,6 +11,7 @@ import { RewardRelationshipId, RewardRelationship } from '@joystream/types/recur
 import { StakeId, Stake } from '@joystream/types/stake';
 import { CuratorId, Curator, LeadId } from '@joystream/types/content-working-group';
 import { TokenomicsData } from '@polkadot/joy-utils/src/types/tokenomics';
+import { calculateValidatorsRewardsPerEra } from '../functions/staking';
 
 export default class TokenomicsTransport extends BaseTransport {
   private councilT: CouncilTransport;
@@ -24,7 +25,7 @@ export default class TokenomicsTransport extends BaseTransport {
 
   async getCouncilMembers () {
     let totalCouncilStake = 0;
-    const activeCouncil = await this.api.query.council.activeCouncil() as Seats;
+    const activeCouncil = await this.council.activeCouncil() as Seats;
 
     activeCouncil.map((member) => {
       let stakeAmount = 0;
@@ -43,13 +44,20 @@ export default class TokenomicsTransport extends BaseTransport {
   }
 
   async calculateCouncilRewards (numberOfCouncilMembers: number) {
-    let weekInBlocks = 100800; let councilRewardsInOneWeek = 0; let totalCouncilRewardsPerBlock = 0;
+    let weekInBlocks = 100800;
+    let councilRewardsInOneWeek = 0;
+    let totalCouncilRewardsPerBlock = 0;
     const payoutInterval = Number((await this.api.query.council.payoutInterval() as Option<BlockNumber>).unwrapOr(0));
     const amountPerPayout = (await this.api.query.council.amountPerPayout() as BalanceOf).toNumber();
 
-    totalCouncilRewardsPerBlock = (amountPerPayout && payoutInterval) ? (amountPerPayout * numberOfCouncilMembers) / payoutInterval : 0;
+    totalCouncilRewardsPerBlock = (amountPerPayout && payoutInterval)
+      ? (amountPerPayout * numberOfCouncilMembers) / payoutInterval
+      : 0;
     const { new_term_duration, voting_period, revealing_period, announcing_period } = await this.councilT.electionParameters();
-    const termDuration = new_term_duration.toNumber(); const votingPeriod = voting_period.toNumber(); const revealingPeriod = revealing_period.toNumber(); const announcingPeriod = announcing_period.toNumber();
+    const termDuration = new_term_duration.toNumber();
+    const votingPeriod = voting_period.toNumber();
+    const revealingPeriod = revealing_period.toNumber();
+    const announcingPeriod = announcing_period.toNumber();
 
     while (weekInBlocks > 0) {
       if (weekInBlocks > termDuration) {
@@ -98,7 +106,12 @@ export default class TokenomicsTransport extends BaseTransport {
   }
 
   async getStorageProviders () {
-    const stakeIds: StakeId[] = []; const rewardIds: RewardRelationshipId[] = []; let leadStakeId = null as (StakeId | null); let leadRewardId = null as (RewardRelationshipId | null); let numberOfStorageProviders = 0; let leadNumber = 0;
+    const stakeIds: StakeId[] = [];
+    const rewardIds: RewardRelationshipId[] = [];
+    let leadStakeId: StakeId | null = null;
+    let leadRewardId: RewardRelationshipId | null = null;
+    let numberOfStorageProviders = 0;
+    let leadNumber = 0;
     const allWorkers = await this.workingGroupT.allWorkers('Storage');
     const currentLeadId = (await this.api.query.storageWorkingGroup.currentLead() as Option<WorkerId>).unwrapOr(null)?.toNumber();
 
@@ -133,15 +146,25 @@ export default class TokenomicsTransport extends BaseTransport {
     };
   }
 
-  async calcuateStorageProvider (stakeIds: StakeId[], leadStakeId: StakeId | null, rewardIds: RewardRelationshipId[], leadRewardId: RewardRelationshipId | null) {
-    let totalStorageProviderStake = 0; let leadStake = 0; let storageProviderRewardsPerBlock = 0; let storageLeadRewardsPerBlock = 0;
+  async calcuateStorageProvider (
+    stakeIds: StakeId[],
+    leadStakeId: StakeId | null,
+    rewardIds: RewardRelationshipId[],
+    leadRewardId: RewardRelationshipId | null
+  ) {
+    let totalStorageProviderStake = 0;
+    let leadStake = 0;
+    let storageProviderRewardsPerBlock = 0;
+    let storageLeadRewardsPerBlock = 0;
 
     (await this.api.query.stake.stakes.multi<Stake>(stakeIds)).forEach((stake) => {
       totalStorageProviderStake += stake.value.toNumber();
     });
     (await this.api.query.recurringRewards.rewardRelationships.multi<RewardRelationship>(rewardIds)).map((rewardRelationship) => {
       const amount = rewardRelationship.amount_per_payout.toNumber();
-      const payoutInterval = rewardRelationship.payout_interval.isSome ? rewardRelationship.payout_interval.unwrap().toNumber() : null;
+      const payoutInterval = rewardRelationship.payout_interval.isSome
+        ? rewardRelationship.payout_interval.unwrap().toNumber()
+        : null;
 
       if (amount && payoutInterval) {
         storageProviderRewardsPerBlock += amount / payoutInterval;
@@ -172,7 +195,8 @@ export default class TokenomicsTransport extends BaseTransport {
 
   async getStorageProviderData () {
     const { numberOfStorageProviders, leadNumber, stakeIds, rewardIds, leadRewardId, leadStakeId } = await this.getStorageProviders();
-    const { totalStorageProviderStake, leadStake, storageProviderRewardsPerWeek, storageProviderLeadRewardsPerWeek } = await this.calcuateStorageProvider(stakeIds, leadStakeId, rewardIds, leadRewardId);
+    const { totalStorageProviderStake, leadStake, storageProviderRewardsPerWeek, storageProviderLeadRewardsPerWeek } =
+      await this.calcuateStorageProvider(stakeIds, leadStakeId, rewardIds, leadRewardId);
 
     return {
       numberOfStorageProviders,
@@ -215,14 +239,17 @@ export default class TokenomicsTransport extends BaseTransport {
   }
 
   async calculateContentCurator (stakeIds: StakeId[], rewardIds: RewardRelationshipId[]) {
-    let totalContentCuratorStake = 0; let contentCuratorRewardsPerBlock = 0;
+    let totalContentCuratorStake = 0;
+    let contentCuratorRewardsPerBlock = 0;
 
     (await this.api.query.stake.stakes.multi<Stake>(stakeIds)).forEach((stake) => {
       totalContentCuratorStake += stake.value.toNumber();
     });
     (await this.api.query.recurringRewards.rewardRelationships.multi<RewardRelationship>(rewardIds)).map((rewardRelationship) => {
       const amount = rewardRelationship.amount_per_payout.toNumber();
-      const payoutInterval = rewardRelationship.payout_interval.isSome ? rewardRelationship.payout_interval.unwrap().toNumber() : null;
+      const payoutInterval = rewardRelationship.payout_interval.isSome
+        ? rewardRelationship.payout_interval.unwrap().toNumber()
+        : null;
 
       if (amount && payoutInterval) {
         contentCuratorRewardsPerBlock += amount / payoutInterval;
@@ -253,7 +280,9 @@ export default class TokenomicsTransport extends BaseTransport {
     let totalValidatorStake = 0; let numberOfNominators = 0;
 
     if (currentEra !== null) {
-      const validatorStakeData = await this.api.query.staking.erasStakers.multi<Exposure>(validatorIds.map((validatorId) => [currentEra, validatorId]));
+      const validatorStakeData = await this.api.query.staking.erasStakers.multi<Exposure>(
+        validatorIds.map((validatorId) => [currentEra, validatorId])
+      );
 
       validatorStakeData.forEach((data) => {
         if (!data.total.isEmpty) {
@@ -273,31 +302,15 @@ export default class TokenomicsTransport extends BaseTransport {
     };
   }
 
-  calculateValidators (totalValidatorStake: number, totalIssuance: number) {
-    let validatorRewards = 0;
-    const [idealStakingRate, minimumInflation, maximumInflation, fallOff, eraLength, year] = [0.25, 0.05, 0.75, 0.05, 3600, (365.2425 * 24 * 60 * 60)];
-    const stakingRate = totalValidatorStake / totalIssuance;
-
-    if (stakingRate > idealStakingRate) {
-      validatorRewards = totalIssuance * (minimumInflation + (maximumInflation - minimumInflation) * 2 ** ((idealStakingRate - stakingRate) / fallOff)) * eraLength / year;
-    } else if (stakingRate === idealStakingRate) {
-      validatorRewards = (totalIssuance * maximumInflation * eraLength) / year;
-    } else {
-      validatorRewards = (totalIssuance * minimumInflation + totalIssuance * (maximumInflation - minimumInflation) * (stakingRate / idealStakingRate)) * eraLength / year;
-    }
-
-    return validatorRewards;
-  }
-
   async getValidatorData (totalIssuance: number) {
     const { numberOfValidators, numberOfNominators, totalValidatorStake } = await this.getValidators();
-    const validatorRewardsPerEra = this.calculateValidators(totalValidatorStake, totalIssuance);
+    const validatorRewardsPerEra = calculateValidatorsRewardsPerEra(totalValidatorStake, totalIssuance);
 
     return {
       numberOfValidators,
       numberOfNominators,
       totalValidatorStake,
-      validatorRewardsPerWeek: validatorRewardsPerEra * 168
+      validatorRewardsPerWeek: validatorRewardsPerEra * 168 // Assuming 1 era = 1h
     };
   }