diff --git a/packages/extension-polkagate/src/hooks/useBlockInterval.ts b/packages/extension-polkagate/src/hooks/useBlockInterval.ts index a15cd613f..34c25c859 100644 --- a/packages/extension-polkagate/src/hooks/useBlockInterval.ts +++ b/packages/extension-polkagate/src/hooks/useBlockInterval.ts @@ -1,6 +1,5 @@ // Copyright 2019-2024 @polkadot/extension-polkagate authors & contributors // SPDX-License-Identifier: Apache-2.0 -// @ts-nocheck import type { ApiPromise } from '@polkadot/api'; @@ -17,29 +16,34 @@ export const A_DAY = new BN(24 * 60 * 60 * 1000); const THRESHOLD = BN_THOUSAND.div(BN_TWO); const DEFAULT_TIME = new BN(6_000); -function calcInterval(api: ApiPromise): BN { - return bnMin(A_DAY, ( - // Babe, e.g. Relay chains (Substrate defaults) - api.consts.babe?.expectedBlockTime || - // POW, eg. Kulupu - api.consts.difficulty?.targetBlockTime || - // Subspace - api.consts.subspace?.expectedBlockTime || ( - // Check against threshold to determine value validity - api.consts.timestamp?.minimumPeriod.gte(THRESHOLD) - // Default minimum period config - ? api.consts.timestamp.minimumPeriod.mul(BN_TWO) - : api.query.parachainSystem - // default guess for a parachain - ? DEFAULT_TIME.mul(BN_TWO) - // default guess for others - : DEFAULT_TIME - ) - )); +export function calcInterval (api: ApiPromise | undefined): BN { + if (!api) { + return DEFAULT_TIME; + } + + return bnMin(A_DAY, ( + // Babe, e.g. Relay chains (Substrate defaults) + api.consts['babe']?.['expectedBlockTime'] as unknown as BN || + // POW, eg. Kulupu + api.consts['difficulty']?.['targetBlockTime'] as unknown as BN || + // Subspace + // Subspace + api.consts['subspace']?.['expectedBlockTime'] || ( + // Check against threshold to determine value validity + (api.consts['timestamp']?.['minimumPeriod'] as unknown as BN).gte(THRESHOLD) + // Default minimum period config + ? (api.consts['timestamp']['minimumPeriod'] as unknown as BN).mul(BN_TWO) + : api.query['parachainSystem'] + // default guess for a parachain + ? DEFAULT_TIME.mul(BN_TWO) + // default guess for others + : DEFAULT_TIME + ) + )); } -export default function useBlockInterval(address: string | undefined): BN | undefined { - const api = useApi(address); +export default function useBlockInterval (address: string | undefined): BN | undefined { + const api = useApi(address); - return useMemo(() => api && calcInterval(api), [api]); + return useMemo(() => api && calcInterval(api), [api]); } diff --git a/packages/extension-polkagate/src/hooks/useValidatorApy.ts b/packages/extension-polkagate/src/hooks/useValidatorApy.ts index 6e45f083a..b8a3e47d7 100644 --- a/packages/extension-polkagate/src/hooks/useValidatorApy.ts +++ b/packages/extension-polkagate/src/hooks/useValidatorApy.ts @@ -3,38 +3,46 @@ import type { ApiPromise } from '@polkadot/api'; import type { Option, u128 } from '@polkadot/types'; -//@ts-ignore +// @ts-ignore import type { PalletStakingActiveEraInfo, PalletStakingEraRewardPoints, PalletStakingValidatorPrefs, SpStakingPagedExposureMetadata } from '@polkadot/types/lookup'; import { useCallback, useEffect, useState } from 'react'; -import { BN, BN_HUNDRED, BN_ZERO } from '@polkadot/util'; +import { BN, BN_ZERO } from '@polkadot/util'; + +import { calcInterval } from './useBlockInterval'; interface ValidatorEraInfo { - netReward: BN; + netReward: number; total: BN; } export default function useValidatorApy (api: ApiPromise | undefined, validatorAddress: string, isElected?: boolean): string | undefined | null { const [apy, setApy] = useState(); + const blockInterval = calcInterval(api); + const blockIntervalInSec = blockInterval.toNumber() / 1000; const calculateValidatorAPY = useCallback(async (validatorAddress: string) => { if (!api) { return; } - // Define the number of past eras you want to check (e.g., last 10 eras) - const eraDepth = 10; const decimal = new BN(10 ** api.registry.chainDecimals[0]); - let totalRewards = BN_ZERO; + let totalRewards = 0; let totalPoints = BN_ZERO; let validatorPoints = BN_ZERO; let totalStaked = BN_ZERO; const validatorEraInfo: ValidatorEraInfo[] = []; + const { eraLength } = await api.derive.session.progress(); + const currentEra = ((await api.query['staking']['activeEra']()) as Option).unwrap().index.toNumber(); const { commission } = await api.query['staking']['validators'](validatorAddress) as PalletStakingValidatorPrefs; + const eraLengthInHrs = eraLength.toNumber() * blockIntervalInSec / 3600; // 3600 = 1hr in seconds + const eraPerDay = 24 / eraLengthInHrs; + const eraDepth = 10 * eraPerDay; // eras to calculate + // Loop over the past eras to calculate rewards for the validator for (let eraIndex = currentEra - eraDepth; eraIndex <= currentEra; eraIndex++) { let netReward; @@ -61,7 +69,7 @@ export default function useValidatorApy (api: ApiPromise | undefined, validatorA totalPoints = totalPoints.add(eraPoints.total); const _eraReward = eraReward.unwrap(); - netReward = _eraReward.mul(validatorPoints).div(totalPoints).muln(100 - (commission.toNumber() / 1e7)).div(BN_HUNDRED); + netReward = _eraReward.toNumber() * (validatorPoints.toNumber() / totalPoints.toNumber()) * (100 - (commission.toNumber() / 1e7)) / 100; } else { continue; } @@ -92,7 +100,7 @@ export default function useValidatorApy (api: ApiPromise | undefined, validatorA } validatorEraInfo.forEach(({ netReward, total }) => { - totalRewards = totalRewards.add(netReward); + totalRewards += netReward; totalStaked = totalStaked.add(total); }); @@ -100,11 +108,8 @@ export default function useValidatorApy (api: ApiPromise | undefined, validatorA totalStaked = totalStaked.div(decimal).divn(actualDepth); - const dailyReward = totalRewards.div(decimal).divn(actualDepth); - - // Calculate daily return as a fraction of the staked amount - const dailyReturn = dailyReward.toNumber() / totalStaked.toNumber(); - + const dailyReward = (totalRewards / decimal.toNumber() / actualDepth) * eraPerDay; + const dailyReturn = dailyReward / totalStaked.toNumber(); const APY = (dailyReturn * 365 * 100).toFixed(2); if (!isFinite(+APY) || isNaN(+APY)) { diff --git a/packages/extension/public/locales/en/translation.json b/packages/extension/public/locales/en/translation.json index a84eeac19..dead63b83 100644 --- a/packages/extension/public/locales/en/translation.json +++ b/packages/extension/public/locales/en/translation.json @@ -1467,4 +1467,4 @@ "Menu options": "", "PolkaGate logo": "", "Total balance": "" -} \ No newline at end of file +}