From 8119ab76dd9b32db33b4bd556f7b482ca2aa2985 Mon Sep 17 00:00:00 2001 From: Jay Date: Tue, 28 Nov 2023 13:20:22 +0800 Subject: [PATCH] Fix collator power (#70) * Fix exposureCache index * Adapt commission weighted power * Power tooltips --- src/components/bond-more-deposit-modal.tsx | 15 ++- src/components/bond-more-kton-modal.tsx | 6 +- src/components/bond-more-ring-modal.tsx | 6 +- src/components/collator-select-modal.tsx | 35 +++++- src/components/do-stake.tsx | 37 ++++-- src/components/power.tsx | 33 ++++- src/components/records-bonded-tokens.tsx | 36 +++--- src/components/staking-records.tsx | 10 +- src/components/unbond-deposit-modal.tsx | 15 ++- src/components/unbond-kton-modal.tsx | 6 +- src/components/unbond-ring-modal.tsx | 6 +- src/hooks/use-collator-power.ts | 139 +++++++++++---------- src/types/data-source.tsx | 1 + src/utils/misc.ts | 5 + 14 files changed, 232 insertions(+), 118 deletions(-) diff --git a/src/components/bond-more-deposit-modal.tsx b/src/components/bond-more-deposit-modal.tsx index c2ac437..f1ae3a3 100644 --- a/src/components/bond-more-deposit-modal.tsx +++ b/src/components/bond-more-deposit-modal.tsx @@ -1,16 +1,18 @@ import { Key, useCallback, useMemo, useState } from "react"; import Modal from "./modal"; import CheckboxGroup from "./checkbox-group"; -import { formatBlanace, getChainConfig, notifyTransaction } from "@/utils"; +import { commissionWeightedPower, formatBlanace, getChainConfig, notifyTransaction } from "@/utils"; import { ExtraPower } from "./balance-input"; import { useApp, useStaking } from "@/hooks"; import { notification } from "./notification"; import { writeContract, waitForTransaction } from "@wagmi/core"; export default function BondMoreDepositModal({ + commission, isOpen, onClose = () => undefined, }: { + commission: string; isOpen: boolean; onClose?: () => void; }) { @@ -22,11 +24,14 @@ export default function BondMoreDepositModal({ const extraPower = useMemo( () => - calcExtraPower( - deposits.filter(({ id }) => checkedDeposits.includes(id)).reduce((acc, cur) => acc + cur.value, 0n), - 0n + commissionWeightedPower( + calcExtraPower( + deposits.filter(({ id }) => checkedDeposits.includes(id)).reduce((acc, cur) => acc + cur.value, 0n), + 0n + ), + commission ), - [deposits, checkedDeposits, calcExtraPower] + [deposits, commission, checkedDeposits, calcExtraPower] ); const availableDeposits = deposits.filter(({ id }) => !stakedDeposits.includes(id)); diff --git a/src/components/bond-more-kton-modal.tsx b/src/components/bond-more-kton-modal.tsx index d731eed..98c91f7 100644 --- a/src/components/bond-more-kton-modal.tsx +++ b/src/components/bond-more-kton-modal.tsx @@ -1,4 +1,4 @@ -import { getChainConfig, notifyTransaction } from "@/utils"; +import { commissionWeightedPower, getChainConfig, notifyTransaction } from "@/utils"; import BondMoreTokenModal from "./bond-more-token-modal"; import { useApp, useStaking } from "@/hooks"; import { useAccount, useBalance } from "wagmi"; @@ -7,9 +7,11 @@ import { notification } from "./notification"; import { writeContract, waitForTransaction } from "@wagmi/core"; export default function BondMoreKtonModal({ + commission, isOpen, onClose = () => undefined, }: { + commission: string; isOpen: boolean; onClose?: () => void; }) { @@ -60,7 +62,7 @@ export default function BondMoreKtonModal({ isOpen={isOpen} symbol={ktonToken.symbol} decimals={ktonToken.decimals} - power={calcExtraPower(0n, inputAmount)} + power={commissionWeightedPower(calcExtraPower(0n, inputAmount), commission)} balance={ktonBalance?.value || 0n} busy={busy} disabled={inputAmount <= 0n} diff --git a/src/components/bond-more-ring-modal.tsx b/src/components/bond-more-ring-modal.tsx index 9f5d678..1d5f1c8 100644 --- a/src/components/bond-more-ring-modal.tsx +++ b/src/components/bond-more-ring-modal.tsx @@ -1,4 +1,4 @@ -import { getChainConfig, notifyTransaction } from "@/utils"; +import { commissionWeightedPower, getChainConfig, notifyTransaction } from "@/utils"; import BondMoreTokenModal from "./bond-more-token-modal"; import { useAccount, useBalance } from "wagmi"; import { useApp, useStaking } from "@/hooks"; @@ -7,9 +7,11 @@ import { notification } from "./notification"; import { writeContract, waitForTransaction } from "@wagmi/core"; export default function BondMoreRingModal({ + commission, isOpen, onClose = () => undefined, }: { + commission: string; isOpen: boolean; onClose?: () => void; }) { @@ -58,7 +60,7 @@ export default function BondMoreRingModal({ isOpen={isOpen} symbol={nativeToken.symbol} decimals={nativeToken.decimals} - power={calcExtraPower(inputAmount, 0n)} + power={commissionWeightedPower(calcExtraPower(inputAmount, 0n), commission)} balance={ringBalance?.value || 0n} busy={busy} disabled={inputAmount <= 0n} diff --git a/src/components/collator-select-modal.tsx b/src/components/collator-select-modal.tsx index 8310dde..3433778 100644 --- a/src/components/collator-select-modal.tsx +++ b/src/components/collator-select-modal.tsx @@ -5,10 +5,11 @@ import Image from "next/image"; import { useAccount } from "wagmi"; import Table, { ColumnType } from "./table"; import Jazzicon from "./jazzicon"; -import { prettyNumber } from "@/utils"; +import { commissionWeightedPower, prettyNumber } from "@/utils"; import { notification } from "./notification"; import DisplayAccountName from "./display-account-name"; import { useStaking } from "@/hooks"; +import Tooltip from "./tooltip"; type TabKey = "active" | "waiting"; @@ -59,10 +60,38 @@ const columns: ColumnType[] = [ title: (
Total-staked - (Power) +
+ (Power) + + {`The Collator's total-staked power is a dynamic value, inversely proportional to the commission set by + the Collator. Higher commission results in lower total-staked power and vice versa. `} + + Learn More + +
+ } + enabledSafePolygon + contentClassName="w-80" + > + Info + +
), - render: (row) => {prettyNumber(row.power)}, + render: (row) => {prettyNumber(commissionWeightedPower(row.power, row.commission))}, }, { key: "commission", diff --git a/src/components/do-stake.tsx b/src/components/do-stake.tsx index 6a72191..706995b 100644 --- a/src/components/do-stake.tsx +++ b/src/components/do-stake.tsx @@ -1,4 +1,4 @@ -import { getChainConfig, notifyTransaction } from "@/utils"; +import { commissionWeightedPower, getChainConfig, notifyTransaction } from "@/utils"; import ActiveDepositSelector from "./active-deposit-selector"; import CollatorSelector from "./collator-selector"; import BalanceInput, { ExtraPower } from "./balance-input"; @@ -11,8 +11,14 @@ import { ChainID } from "@/types"; import { notification } from "./notification"; export default function DoStake() { - const { deposits, nominatorCollators, isNominatorCollatorsLoading, calcExtraPower, updateNominatorCollators } = - useStaking(); + const { + deposits, + nominatorCollators, + collatorCommission, + isNominatorCollatorsLoading, + calcExtraPower, + updateNominatorCollators, + } = useStaking(); const [delegateCollator, setDelegateCollator] = useState(undefined); const [delegateRing, setDelegateRing] = useState(0n); const [delegateKton, setDelegateKton] = useState(0n); @@ -28,15 +34,28 @@ export default function DoStake() { const { data: ringBalance } = useBalance({ address, watch: true }); const { data: ktonBalance } = useBalance({ address, watch: true, token: ktonToken?.address }); - const ringExtraPower = useMemo(() => calcExtraPower(delegateRing, 0n), [delegateRing, calcExtraPower]); - const ktonExtraPower = useMemo(() => calcExtraPower(0n, delegateKton), [delegateKton, calcExtraPower]); + const commission = useMemo(() => { + return (delegateCollator && collatorCommission[delegateCollator]) || "0.00%"; + }, [delegateCollator, collatorCommission]); + + const ringExtraPower = useMemo( + () => commissionWeightedPower(calcExtraPower(delegateRing, 0n), commission), + [commission, delegateRing, calcExtraPower] + ); + const ktonExtraPower = useMemo( + () => commissionWeightedPower(calcExtraPower(0n, delegateKton), commission), + [commission, delegateKton, calcExtraPower] + ); const depositsExtraPower = useMemo( () => - calcExtraPower( - deposits.filter(({ id }) => delegateDeposits.includes(id)).reduce((acc, cur) => acc + cur.value, 0n), - 0n + commissionWeightedPower( + calcExtraPower( + deposits.filter(({ id }) => delegateDeposits.includes(id)).reduce((acc, cur) => acc + cur.value, 0n), + 0n + ), + commission ), - [delegateDeposits, deposits, calcExtraPower] + [delegateDeposits, commission, deposits, calcExtraPower] ); const handleStake = useCallback(async () => { diff --git a/src/components/power.tsx b/src/components/power.tsx index e7ce7e5..a28b120 100644 --- a/src/components/power.tsx +++ b/src/components/power.tsx @@ -1,12 +1,12 @@ import { GET_LATEST_STAKING_REWARDS } from "@/config"; import { useApp, useStaking } from "@/hooks"; -import { formatBlanace, getChainConfig, prettyNumber } from "@/utils"; +import { commissionWeightedPower, formatBlanace, getChainConfig, prettyNumber } from "@/utils"; import { formatDistanceStrict } from "date-fns"; import Image from "next/image"; import { getAddress } from "viem"; import { useAccount } from "wagmi"; import CountLoading from "./count-loading"; -import { useRef } from "react"; +import { useMemo, useRef } from "react"; import { CSSTransition } from "react-transition-group"; import { useQuery } from "@apollo/client"; @@ -36,7 +36,16 @@ interface QueryResult { export default function Power() { const loadingRef = useRef(null); - const { power, isLedgersInitialized, isRingPoolInitialized, isKtonPoolInitialized } = useStaking(); + const { + power, + nominatorCollators, + collatorCommission, + isNominatorCollatorsInitialized, + isCollatorCommissionInitialized, + isLedgersInitialized, + isRingPoolInitialized, + isKtonPoolInitialized, + } = useStaking(); const { activeChain } = useApp(); const { address } = useAccount(); const { data: rewardData, loading: rewardLoading } = useQuery( @@ -46,6 +55,16 @@ export default function Power() { } ); + const thePower = useMemo(() => { + const isCollator = + address && Object.keys(collatorCommission).some((addr) => addr.toLowerCase() === address.toLowerCase()) + ? true + : false; + const collator = isCollator ? address : address ? nominatorCollators[address]?.at(0) : undefined; + const commission = collator ? collatorCommission[collator] : undefined; + return commissionWeightedPower(power, commission ?? "0.00%"); + }, [address, power, collatorCommission, nominatorCollators]); + const chainConfig = getChainConfig(activeChain); return ( @@ -56,8 +75,12 @@ export default function Power() { Icon of Power Power - {isLedgersInitialized && isRingPoolInitialized && isKtonPoolInitialized ? ( - {prettyNumber(power)} + {isLedgersInitialized && + isRingPoolInitialized && + isKtonPoolInitialized && + isNominatorCollatorsInitialized && + isCollatorCommissionInitialized ? ( + {prettyNumber(thePower)} ) : ( )} diff --git a/src/components/records-bonded-tokens.tsx b/src/components/records-bonded-tokens.tsx index 4ec4f70..e30bfb9 100644 --- a/src/components/records-bonded-tokens.tsx +++ b/src/components/records-bonded-tokens.tsx @@ -126,8 +126,8 @@ export default function RecordsBondedTokens({ row }: { row: StakingRecordsDataSo {row.collator.length > 0 && ( <> - - + + )} @@ -155,8 +155,8 @@ export default function RecordsBondedTokens({ row }: { row: StakingRecordsDataSo {row.collator.length > 0 && ( <> - - + + )} @@ -183,8 +183,8 @@ export default function RecordsBondedTokens({ row }: { row: StakingRecordsDataSo {row.collator.length > 0 && ( <> - - + + )} @@ -192,62 +192,62 @@ export default function RecordsBondedTokens({ row }: { row: StakingRecordsDataSo ); } -function BondMoreRing() { +function BondMoreRing({ commission }: { commission: string }) { const [isOpen, setIsOpen] = useState(false); return ( <> setIsOpen(true)} /> - setIsOpen(false)} /> + setIsOpen(false)} /> ); } -function BondMoreKton() { +function BondMoreKton({ commission }: { commission: string }) { const [isOpen, setIsOpen] = useState(false); return ( <> setIsOpen(true)} /> - setIsOpen(false)} /> + setIsOpen(false)} /> ); } -function BondMoreDeposit() { +function BondMoreDeposit({ commission }: { commission: string }) { const [isOpen, setIsOpen] = useState(false); return ( <> setIsOpen(true)} /> - setIsOpen(false)} /> + setIsOpen(false)} /> ); } -function UnbondRing() { +function UnbondRing({ commission }: { commission: string }) { const [isOpen, setIsOpen] = useState(false); return ( <> setIsOpen(true)} /> - setIsOpen(false)} /> + setIsOpen(false)} /> ); } -function UnbondKton() { +function UnbondKton({ commission }: { commission: string }) { const [isOpen, setIsOpen] = useState(false); return ( <> setIsOpen(true)} /> - setIsOpen(false)} /> + setIsOpen(false)} /> ); } -function UnbondDeposit() { +function UnbondDeposit({ commission }: { commission: string }) { const [isOpen, setIsOpen] = useState(false); return ( <> setIsOpen(true)} /> - setIsOpen(false)} /> + setIsOpen(false)} /> ); } diff --git a/src/components/staking-records.tsx b/src/components/staking-records.tsx index b20831b..9883b75 100644 --- a/src/components/staking-records.tsx +++ b/src/components/staking-records.tsx @@ -1,6 +1,6 @@ import { useMemo } from "react"; import Table, { ColumnType } from "./table"; -import { prettyNumber } from "@/utils"; +import { commissionWeightedPower, prettyNumber } from "@/utils"; import Jazzicon from "./jazzicon"; import Image from "next/image"; import { useStaking } from "@/hooks"; @@ -25,8 +25,10 @@ export default function StakingRecords() { unbondingKton, unbondingDeposits, nominatorCollators, + collatorCommission, activeCollators, isNominatorCollatorsInitialized, + isCollatorCommissionInitialized, isActiveCollatorsInitialized, isLedgersInitialized, isNominatorCollatorsLoading, @@ -143,11 +145,13 @@ export default function StakingRecords() { unbondingDeposits.length > 0; if (address && (collator || hasStaking)) { + const commission = (collator && collatorCommission[collator]) || "0.00%"; return [ { key: collator || "0", collator: collator || "", - stakedPower: power, + commission, + stakedPower: commissionWeightedPower(power, commission), bondedTokens: { stakedRing, stakedKton, @@ -173,6 +177,7 @@ export default function StakingRecords() { unbondingKton, unbondingDeposits, nominatorCollators, + collatorCommission, activeCollators, ]); @@ -184,6 +189,7 @@ export default function StakingRecords() { dataSource={dataSource} loading={ !isActiveCollatorsInitialized || + !isCollatorCommissionInitialized || !isNominatorCollatorsInitialized || !isLedgersInitialized || isNominatorCollatorsLoading diff --git a/src/components/unbond-deposit-modal.tsx b/src/components/unbond-deposit-modal.tsx index 29a822f..93e8fa2 100644 --- a/src/components/unbond-deposit-modal.tsx +++ b/src/components/unbond-deposit-modal.tsx @@ -1,16 +1,18 @@ import { Key, useCallback, useMemo, useState } from "react"; import Modal from "./modal"; import CheckboxGroup from "./checkbox-group"; -import { formatBlanace, getChainConfig, notifyTransaction } from "@/utils"; +import { commissionWeightedPower, formatBlanace, getChainConfig, notifyTransaction } from "@/utils"; import { ExtraPower } from "./balance-input"; import { useApp, useStaking } from "@/hooks"; import { notification } from "./notification"; import { writeContract, waitForTransaction } from "@wagmi/core"; export default function UnbondDepositModal({ + commission, isOpen, onClose = () => undefined, }: { + commission: string; isOpen: boolean; onClose?: () => void; }) { @@ -22,11 +24,14 @@ export default function UnbondDepositModal({ const extraPower = useMemo( () => - calcExtraPower( - deposits.filter(({ id }) => checkedDeposits.includes(id)).reduce((acc, cur) => acc + cur.value, 0n), - 0n + commissionWeightedPower( + calcExtraPower( + deposits.filter(({ id }) => checkedDeposits.includes(id)).reduce((acc, cur) => acc + cur.value, 0n), + 0n + ), + commission ), - [deposits, checkedDeposits, calcExtraPower] + [deposits, commission, checkedDeposits, calcExtraPower] ); const availableDeposits = deposits.filter(({ id }) => stakedDeposits.includes(id)); diff --git a/src/components/unbond-kton-modal.tsx b/src/components/unbond-kton-modal.tsx index b6d5ec4..8fb15a9 100644 --- a/src/components/unbond-kton-modal.tsx +++ b/src/components/unbond-kton-modal.tsx @@ -1,4 +1,4 @@ -import { formatBlanace, getChainConfig, notifyTransaction } from "@/utils"; +import { commissionWeightedPower, formatBlanace, getChainConfig, notifyTransaction } from "@/utils"; import UnbondTokenModal from "./unbond-token-modal"; import { useApp, useStaking } from "@/hooks"; import { useCallback, useState } from "react"; @@ -6,9 +6,11 @@ import { notification } from "./notification"; import { writeContract, waitForTransaction } from "@wagmi/core"; export default function UnbondKtonModal({ + commission, isOpen, onClose = () => undefined, }: { + commission: string; isOpen: boolean; onClose?: () => void; }) { @@ -62,7 +64,7 @@ export default function UnbondKtonModal({ isOpen={isOpen} symbol={ktonToken.symbol} decimals={ktonToken.decimals} - power={calcExtraPower(0n, inputAmount)} + power={commissionWeightedPower(calcExtraPower(0n, inputAmount), commission)} balance={stakedKton} busy={busy} disabled={inputAmount <= 0n} diff --git a/src/components/unbond-ring-modal.tsx b/src/components/unbond-ring-modal.tsx index 96345d7..80c009c 100644 --- a/src/components/unbond-ring-modal.tsx +++ b/src/components/unbond-ring-modal.tsx @@ -1,4 +1,4 @@ -import { formatBlanace, getChainConfig, notifyTransaction } from "@/utils"; +import { commissionWeightedPower, formatBlanace, getChainConfig, notifyTransaction } from "@/utils"; import UnbondTokenModal from "./unbond-token-modal"; import { useApp, useStaking } from "@/hooks"; import { useCallback, useState } from "react"; @@ -6,9 +6,11 @@ import { notification } from "./notification"; import { writeContract, waitForTransaction } from "@wagmi/core"; export default function UnbondRingModal({ + commission, isOpen, onClose = () => undefined, }: { + commission: string; isOpen: boolean; onClose?: () => void; }) { @@ -61,7 +63,7 @@ export default function UnbondRingModal({ isOpen={isOpen} symbol={nativeToken.symbol} decimals={nativeToken.decimals} - power={calcExtraPower(inputAmount, 0n)} + power={commissionWeightedPower(calcExtraPower(inputAmount, 0n), commission)} balance={stakedRing} busy={busy} disabled={inputAmount <= 0n} diff --git a/src/hooks/use-collator-power.ts b/src/hooks/use-collator-power.ts index a96a157..2197f9f 100644 --- a/src/hooks/use-collator-power.ts +++ b/src/hooks/use-collator-power.ts @@ -1,5 +1,5 @@ import { useEffect, useState } from "react"; -import { forkJoin, Subscription } from "rxjs"; +import { from, of, forkJoin, switchMap, Subscription } from "rxjs"; import { useApi } from "./use-api"; import { DarwiniaStakingLedger } from "@/types"; import { stakingToPower } from "@/utils"; @@ -27,6 +27,8 @@ interface DefaultValue { isCollatorPowerInitialized: boolean; } +type ExposureCacheState = "Previous" | "Current" | "Next"; + function isExposuresJsonCache(data: any): data is ExposuresJsonCache { return data.vote; } @@ -56,69 +58,80 @@ export const useCollatorPower = ( let sub$$: Subscription | undefined; if (polkadotApi) { - sub$$ = forkJoin([ - polkadotApi.query.darwiniaStaking.exposures - ? polkadotApi.query.darwiniaStaking.exposures.entries() - : polkadotApi.query.darwiniaStaking.exposureCache1.entries(), - polkadotApi.query.darwiniaStaking.ledgers.entries(), - polkadotApi.query.deposit.deposits.entries(), - ]).subscribe({ - next: ([exposures, ledgers, deposits]) => { - const parsedExposures = exposures.reduce((acc, cur) => { - const address = (cur[0].toHuman() as string[])[0]; - const data = formatExposuresData(cur[1].toJSON() as unknown); - return { ...acc, [address]: data }; - }, {} as { [address: string]: ExposuresJson | undefined }); - - const parsedLedgers = ledgers.reduce((acc, cur) => { - const address = cur[0].toHuman() as string; - const data = cur[1].toJSON() as unknown as DarwiniaStakingLedger; - return { ...acc, [address]: data }; - }, {} as { [address: string]: DarwiniaStakingLedger | undefined }); - - const parsedDeposits = deposits.reduce((acc, cur) => { - const address = cur[0].toHuman() as string; - const data = cur[1].toJSON() as unknown as DepositJson[]; - return { ...acc, [address]: data }; - }, {} as { [address: string]: DepositJson[] | undefined }); - - const collators = Object.keys(collatorNominators); - setCollatorPower( - collators.reduce((acc, cur) => { - if (parsedExposures[cur]) { - // active collator - return { ...acc, [cur]: BigInt(parsedExposures[cur]?.total || 0) }; - } - - const nominators = collatorNominators[cur] || []; - const power = nominators.reduce((acc, cur) => { - const ledger = parsedLedgers[cur]; - const deposits = parsedDeposits[cur] || []; - - if (ledger) { - const stakedDeposit = deposits - .filter(({ id }) => ledger.stakedDeposits?.includes(id)) - .reduce((acc, cur) => acc + BigInt(cur.value), 0n); - return ( - acc + - stakingToPower( - BigInt(ledger.stakedRing) + stakedDeposit, - BigInt(ledger.stakedKton), - ringPool, - ktonPool - ) - ); + sub$$ = from(polkadotApi.query.darwiniaStaking.exposureCacheStates()) + .pipe( + switchMap((cacheStates) => { + const index = (cacheStates.toJSON() as ExposureCacheState[]).findIndex((cs) => cs === "Current"); + const exposureCache = [ + polkadotApi.query.darwiniaStaking.exposureCache0, + polkadotApi.query.darwiniaStaking.exposureCache1, + polkadotApi.query.darwiniaStaking.exposureCache2, + ].at(index); + + return forkJoin([ + exposureCache ? exposureCache.entries() : of([]), + polkadotApi.query.darwiniaStaking.ledgers.entries(), + polkadotApi.query.deposit.deposits.entries(), + ]); + }) + ) + .subscribe({ + next: ([exposures, ledgers, deposits]) => { + const parsedExposures = exposures.reduce((acc, cur) => { + const address = (cur[0].toHuman() as string[])[0]; + const data = formatExposuresData(cur[1].toJSON() as unknown); + return { ...acc, [address]: data }; + }, {} as { [address: string]: ExposuresJson | undefined }); + + const parsedLedgers = ledgers.reduce((acc, cur) => { + const address = cur[0].toHuman() as string; + const data = cur[1].toJSON() as unknown as DarwiniaStakingLedger; + return { ...acc, [address]: data }; + }, {} as { [address: string]: DarwiniaStakingLedger | undefined }); + + const parsedDeposits = deposits.reduce((acc, cur) => { + const address = cur[0].toHuman() as string; + const data = cur[1].toJSON() as unknown as DepositJson[]; + return { ...acc, [address]: data }; + }, {} as { [address: string]: DepositJson[] | undefined }); + + const collators = Object.keys(collatorNominators); + setCollatorPower( + collators.reduce((acc, cur) => { + if (parsedExposures[cur]) { + // active collator + return { ...acc, [cur]: BigInt(parsedExposures[cur]?.total || 0) }; } - return acc; - }, 0n); - - return { ...acc, [cur]: power }; - }, {} as { [collator: string]: bigint | undefined }) - ); - }, - error: console.error, - complete: () => setIsCollatorPowerInitialized(true), - }); + + const nominators = collatorNominators[cur] || []; + const power = nominators.reduce((acc, cur) => { + const ledger = parsedLedgers[cur]; + const deposits = parsedDeposits[cur] || []; + + if (ledger) { + const stakedDeposit = deposits + .filter(({ id }) => ledger.stakedDeposits?.includes(id)) + .reduce((acc, cur) => acc + BigInt(cur.value), 0n); + return ( + acc + + stakingToPower( + BigInt(ledger.stakedRing) + stakedDeposit, + BigInt(ledger.stakedKton), + ringPool, + ktonPool + ) + ); + } + return acc; + }, 0n); + + return { ...acc, [cur]: power }; + }, {} as { [collator: string]: bigint | undefined }) + ); + }, + error: console.error, + complete: () => setIsCollatorPowerInitialized(true), + }); } else { setCollatorPower({}); } diff --git a/src/types/data-source.tsx b/src/types/data-source.tsx index 4373e60..48a0c4b 100644 --- a/src/types/data-source.tsx +++ b/src/types/data-source.tsx @@ -4,6 +4,7 @@ import { Deposit, UnbondingInfo } from "./staking"; export interface StakingRecordsDataSource { key: Key; collator: string; + commission: string; stakedPower: bigint; bondedTokens: { stakedRing: bigint; diff --git a/src/utils/misc.ts b/src/utils/misc.ts index 8bf8dc5..8142b8d 100644 --- a/src/utils/misc.ts +++ b/src/utils/misc.ts @@ -35,3 +35,8 @@ export const calcKtonReward = (depositRing: bigint, depositMonths: number) => { return 0n; }; + +export const commissionWeightedPower = (originPower: bigint, commission: string) => { + const c = Number(commission.replace(/,/g, "").split("%")[0]); + return (originPower * BigInt(100 - (Number.isNaN(c) ? 100 : c))) / 100n; +};