diff --git a/packages/apps-config/src/api/params/inflation.ts b/packages/apps-config/src/api/params/inflation.ts index 3567832d6e47..952b0e1f34df 100644 --- a/packages/apps-config/src/api/params/inflation.ts +++ b/packages/apps-config/src/api/params/inflation.ts @@ -3,8 +3,6 @@ import type { ApiPromise } from '@polkadot/api'; -import { BN, BN_MILLION } from '@polkadot/util'; - import { ALEPHZERO_MAINNET_GENESIS, ALEPHZERO_TESTNET_GENESIS, CERE_NETWORK_GENESIS, CERE_NETWORK_TESTNET_GENESIS, DOCK_POS_TESTNET_GENESIS, KUSAMA_GENESIS, NEATCOIN_GENESIS, NFTMART_GENESIS, POLKADOT_GENESIS, VARA_NETWORK_GENESIS, VARA_NETWORK_TESTNET_GENESIS } from '../constants.js'; export interface InflationParams { @@ -16,10 +14,6 @@ export interface InflationParams { stakeTarget: number; } -interface UniformEraPayoutInflationParams extends InflationParams { - yearlyInflationInTokens: BN; -} - const DEFAULT_PARAMS: InflationParams = { auctionAdjust: 0, auctionMax: 0, @@ -33,18 +27,13 @@ const DEFAULT_PARAMS: InflationParams = { stakeTarget: 0.5 }; -const DEFAULT_UNIFORM_ERA_PAYOUT_PARAMS: UniformEraPayoutInflationParams = { - ...DEFAULT_PARAMS, - yearlyInflationInTokens: BN_MILLION.mul(new BN(30)) -}; - const CERE_NETWORK_INFLATION_PARAMS = { ...DEFAULT_PARAMS, maxInflation: 0.05, minInflation: 0.0001, stakeTarget: 0.2 }; const VARA_NETWORK_INFLATION_PARAMS = { ...DEFAULT_PARAMS, maxInflation: 0, minInflation: 0.0001, stakeTarget: 0.85 }; const KNOWN_PARAMS: Record = { - [ALEPHZERO_MAINNET_GENESIS]: DEFAULT_UNIFORM_ERA_PAYOUT_PARAMS, - [ALEPHZERO_TESTNET_GENESIS]: DEFAULT_UNIFORM_ERA_PAYOUT_PARAMS, + [ALEPHZERO_MAINNET_GENESIS]: DEFAULT_PARAMS, + [ALEPHZERO_TESTNET_GENESIS]: DEFAULT_PARAMS, [CERE_NETWORK_GENESIS]: CERE_NETWORK_INFLATION_PARAMS, [CERE_NETWORK_TESTNET_GENESIS]: CERE_NETWORK_INFLATION_PARAMS, [DOCK_POS_TESTNET_GENESIS]: { ...DEFAULT_PARAMS, stakeTarget: 0.75 }, @@ -60,8 +49,8 @@ const KNOWN_PARAMS: Record = { [VARA_NETWORK_TESTNET_GENESIS]: VARA_NETWORK_INFLATION_PARAMS }; -export function getInflationParams (api: ApiPromise): InflationParams | UniformEraPayoutInflationParams { +export function getInflationParams (api: ApiPromise): InflationParams { // below behaviour is different between our fork and upstream, that by default we are operating // in uniform era payout model, rather than Polkadot-js's RewardCurve model - return KNOWN_PARAMS[api.genesisHash.toHex()] || DEFAULT_UNIFORM_ERA_PAYOUT_PARAMS; + return KNOWN_PARAMS[api.genesisHash.toHex()] || DEFAULT_PARAMS; } diff --git a/packages/apps/public/locales/en/app-explorer.json b/packages/apps/public/locales/en/app-explorer.json index be74201bb1e7..2a2f545ee3ed 100644 --- a/packages/apps/public/locales/en/app-explorer.json +++ b/packages/apps/public/locales/en/app-explorer.json @@ -49,6 +49,7 @@ "last events": "last events", "logs": "logs", "max": "max", + "max issuance": "max issuance", "min": "min", "mortal, valid from #{{startAt}} to #{{endsAt}}": "mortal, valid from #{{startAt}} to #{{endsAt}}", "no": "no", diff --git a/packages/apps/public/locales/en/translation.json b/packages/apps/public/locales/en/translation.json index d3e7a7c4203c..48994610d5ee 100644 --- a/packages/apps/public/locales/en/translation.json +++ b/packages/apps/public/locales/en/translation.json @@ -1500,6 +1500,7 @@ "max RefTime allowed (M, {{estimatedRefTime}} estimated)": "", "max gas allowed (M)": "", "max gas allowed (M, {{estimatedMg}} estimated)": "", + "max issuance": "", "max read gas": "", "max. members": "", "max. members / pool": "", @@ -1899,4 +1900,4 @@ "{{value}}x voting balance, locked for {{duration}}x duration{{period}}": "", "{{when}} (est.)": "", "⚡️ Thunder Gateway": "" -} +} \ No newline at end of file diff --git a/packages/react-hooks/src/types.ts b/packages/react-hooks/src/types.ts index a04bf14786f0..de2c6dd2a370 100644 --- a/packages/react-hooks/src/types.ts +++ b/packages/react-hooks/src/types.ts @@ -44,8 +44,6 @@ export interface ModalState { } export interface Inflation { - idealStake: number; - idealInterest: number; inflation: number; stakedFraction: number; stakedReturn: number; diff --git a/packages/react-hooks/src/useInflation.ts b/packages/react-hooks/src/useInflation.ts index a67a5ab05c7c..61cbb7396317 100644 --- a/packages/react-hooks/src/useInflation.ts +++ b/packages/react-hooks/src/useInflation.ts @@ -8,20 +8,13 @@ import type { Inflation } from './types.js'; import { useEffect, useState } from 'react'; import { getInflationParams } from '@polkadot/apps-config'; -import { BN_BILLION, BN_MILLION, BN_ZERO } from '@polkadot/util'; -import { BN_THOUSAND } from '@polkadot/util/bn/consts'; +import { BN_MILLION, BN_ZERO, isFunction } from '@polkadot/util'; import { createNamedHook } from './createNamedHook.js'; import { useApi } from './useApi.js'; import { useCall } from './useCall.js'; -const EMPTY: Inflation = { idealInterest: 0, idealStake: 0, inflation: 0, stakedFraction: 0, stakedReturn: 0 }; - -function calcInflationUniformEraPayout (totalIssuance: BN, yearlyInflationInTokens: BN): number { - const totalIssuanceInTokens = totalIssuance.div(BN_BILLION).div(BN_THOUSAND); - - return (totalIssuanceInTokens.isZero() ? 0.0 : yearlyInflationInTokens.toNumber() / totalIssuanceInTokens.toNumber()); -} +const EMPTY: Inflation = { inflation: 0, stakedFraction: 0, stakedReturn: 0 }; function calcInflationRewardCurve (minInflation: number, stakedFraction: number, idealStake: number, idealInterest: number, falloff: number) { return (minInflation + ( @@ -31,57 +24,91 @@ function calcInflationRewardCurve (minInflation: number, stakedFraction: number, )); } -function calcInflation (api: ApiPromise, totalStaked: BN, totalIssuance: BN, numAuctions: BN): Inflation { - const inflationParams = getInflationParams(api); - const { auctionAdjust, auctionMax, falloff, maxInflation, minInflation, stakeTarget } = inflationParams; - const stakedFraction = totalStaked.isZero() || totalIssuance.isZero() - ? 0 - : totalStaked.mul(BN_MILLION).div(totalIssuance).toNumber() / BN_MILLION.toNumber(); +function calcInflationOnNonAleph (api: ApiPromise, stakedFraction: number, numAuctions: BN): Inflation { + const { auctionAdjust, auctionMax, falloff, maxInflation, minInflation, stakeTarget } = getInflationParams(api); + const idealStake = stakeTarget - (Math.min(auctionMax, numAuctions.toNumber()) * auctionAdjust); const idealInterest = maxInflation / idealStake; - let inflationInPercentage = 0; - if ('yearlyInflationInTokens' in inflationParams) { - inflationInPercentage = 100 * calcInflationUniformEraPayout(totalIssuance, inflationParams.yearlyInflationInTokens); - } else { - inflationInPercentage = 100 * calcInflationRewardCurve(minInflation, stakedFraction, idealStake, idealInterest, falloff); - } + const inflationInPercentage = 100 * calcInflationRewardCurve(minInflation, stakedFraction, idealStake, idealInterest, falloff); - let stakedReturn = stakedFraction + const stakedReturn = stakedFraction ? (inflationInPercentage / stakedFraction) : 0; + return { + inflation: inflationInPercentage, + stakedFraction, + stakedReturn + }; +} + +function calcInflationOnAleph (yearlyInflationInPercentage: number, stakedFraction: number) { + const baseStakedReturn = stakedFraction !== 0 + ? (yearlyInflationInPercentage / stakedFraction) + : 0; + // Here we multiply stakedReturn by 0.9, as in case of Aleph Zero chain 10% of return goes to treasury - if ('yearlyInflationInTokens' in inflationParams) { - stakedReturn *= 0.9; - } + const stakedReturn = baseStakedReturn * 0.9; return { - idealInterest, - idealStake, - inflation: inflationInPercentage, + inflation: yearlyInflationInPercentage, stakedFraction, stakedReturn }; } +function useYearlyInflation () { + const { api } = useApi(); + + const [yearlyInflation, setYearlyInflation] = useState(); + + const getYearlyInflation = api.call?.alephSessionApi?.yearlyInflation; + + useEffect(() => { + getYearlyInflation?.().then((val) => { + setYearlyInflation(val.toNumber() / 1_000_000_000); + }).catch(console.error); + }, [getYearlyInflation]); + + return { isSupported: isFunction(getYearlyInflation), yearlyInflation }; +} + function useInflationImpl (totalStaked?: BN): Inflation { const { api } = useApi(); + const totalIssuance = useCall(api.query.balances?.totalIssuance); const auctionCounter = useCall(api.query.auctions?.auctionCounter); - const [state, setState] = useState(EMPTY); + const { isSupported: isYearlyInflationApiSupported, yearlyInflation } = useYearlyInflation(); + + const [inflation, setInflation] = useState(EMPTY); useEffect((): void => { const numAuctions = api.query.auctions ? auctionCounter : BN_ZERO; - numAuctions && totalIssuance && totalStaked && setState( - calcInflation(api, totalStaked, totalIssuance, numAuctions) - ); - }, [api, auctionCounter, totalIssuance, totalStaked]); + if ( + numAuctions === undefined || + totalStaked === undefined || + totalIssuance === undefined || + (isYearlyInflationApiSupported && yearlyInflation === undefined) + ) { + return; + } + + const stakedFraction = totalStaked.isZero() || totalIssuance.isZero() + ? 0 + : totalStaked.mul(BN_MILLION).div(totalIssuance).toNumber() / BN_MILLION.toNumber(); + + const inflation = isYearlyInflationApiSupported && yearlyInflation !== undefined + ? calcInflationOnAleph(yearlyInflation * 100, stakedFraction) + : calcInflationOnNonAleph(api, stakedFraction, numAuctions); + + setInflation(inflation); + }, [api, auctionCounter, isYearlyInflationApiSupported, totalIssuance, totalStaked, yearlyInflation]); - return state; + return inflation; } export const useInflation = createNamedHook('useInflation', useInflationImpl);