diff --git a/packages/page-parachains/src/Auctions/Auction.tsx b/packages/page-parachains/src/Auctions/Auction.tsx index 71895b5194..8ce34bfbe2 100644 --- a/packages/page-parachains/src/Auctions/Auction.tsx +++ b/packages/page-parachains/src/Auctions/Auction.tsx @@ -1,8 +1,7 @@ // Copyright 2017-2021 @polkadot/app-parachains authors & contributors // SPDX-License-Identifier: Apache-2.0 -import type { AuctionInfo } from '../types'; -import type { Winning } from './types'; +import type { AuctionInfo, Winning } from '../types'; import React, { useRef } from 'react'; @@ -12,9 +11,9 @@ import { useTranslation } from '../translate'; import WinRange from './WinRange'; interface Props { - auctionInfo: AuctionInfo; + auctionInfo?: AuctionInfo; className?: string; - winningData: Winning[] | null; + winningData?: Winning[]; } function Auction ({ auctionInfo, className, winningData }: Props): React.ReactElement { @@ -30,7 +29,7 @@ function Auction ({ auctionInfo, className, winningData }: Props): React.ReactEl ('No winners in this auction') : t('No ongoing auction') diff --git a/packages/page-parachains/src/Auctions/Bid.tsx b/packages/page-parachains/src/Auctions/Bid.tsx index 46c352f00d..babc8aab95 100644 --- a/packages/page-parachains/src/Auctions/Bid.tsx +++ b/packages/page-parachains/src/Auctions/Bid.tsx @@ -3,8 +3,7 @@ import type BN from 'bn.js'; import type { BlockNumber } from '@polkadot/types/interfaces'; -import type { AuctionInfo, OwnedId, OwnerInfo } from '../types'; -import type { Winning } from './types'; +import type { AuctionInfo, OwnedId, OwnerInfo, Winning } from '../types'; import React, { useMemo, useState } from 'react'; @@ -12,14 +11,14 @@ import { Button, Dropdown, InputBalance, Modal, TxButton } from '@polkadot/react import { useAccounts, useApi, useBestNumber, useToggle } from '@polkadot/react-hooks'; import { BN_ZERO, formatNumber } from '@polkadot/util'; +import { RANGES } from '../constants'; import InputOwner from '../InputOwner'; import { useTranslation } from '../translate'; -import { RANGES } from './constants'; interface Props { - auctionInfo: AuctionInfo; + auctionInfo?: AuctionInfo; className?: string; - lastWinners: Winning | null; + lastWinners?: Winning; ownedIds: OwnedId[]; } @@ -30,19 +29,21 @@ interface Option { value: number; } +const EMPTY_OWNER: OwnerInfo = { accountId: null, paraId: 0 }; + function Bid ({ auctionInfo, className, lastWinners, ownedIds }: Props): React.ReactElement { const { t } = useTranslation(); const { api } = useApi(); const { hasAccounts } = useAccounts(); const bestNumber = useBestNumber(); - const [{ accountId, paraId }, setOwnerInfo] = useState({ accountId: null, paraId: 0 }); + const [{ accountId, paraId }, setOwnerInfo] = useState(EMPTY_OWNER); const [amount, setAmount] = useState(BN_ZERO); const [range, setRange] = useState(0); const [isOpen, toggleOpen] = useToggle(); const rangeOpts = useMemo( (): Option[] => { - if (!auctionInfo.leasePeriod) { + if (!auctionInfo?.leasePeriod) { return []; } @@ -73,7 +74,7 @@ function Bid ({ auctionInfo, className, lastWinners, ownedIds }: Props): React.R <>
- {leasePeriod && leasePeriod.currentPeriod && leases && ( + {leasePeriod && leases && ( leases.length ? ( <> diff --git a/packages/page-parachains/src/Overview/Upcomings.tsx b/packages/page-parachains/src/Overview/Upcomings.tsx index 07226df7f3..e68ec3ba2d 100644 --- a/packages/page-parachains/src/Overview/Upcomings.tsx +++ b/packages/page-parachains/src/Overview/Upcomings.tsx @@ -15,14 +15,14 @@ import Upcoming from './Upcoming'; interface Props { actionsQueue: QueuedAction[]; ids?: ParaId[]; - leasePeriod: LeasePeriod | null; + leasePeriod?: LeasePeriod; } function Upcomings ({ actionsQueue, ids, leasePeriod }: Props): React.ReactElement { const { t } = useTranslation(); const headerRef = useRef([ - [t('upcoming'), 'start', 3], + [t('parathreads'), 'start', 3], [t('head'), 'start'], [t('lifecycle'), 'start'], [t('leases')] @@ -30,10 +30,10 @@ function Upcomings ({ actionsQueue, ids, leasePeriod }: Props): React.ReactEleme return ( ('There are no upcoming parachains')} + empty={leasePeriod && ids && t('There are no upcoming parachains')} header={headerRef.current} > - {ids?.map((id): React.ReactNode => ( + {leasePeriod && ids?.map((id): React.ReactNode => ( diff --git a/packages/page-parachains/src/types.ts b/packages/page-parachains/src/types.ts index b42cb60345..ddd3ca1b61 100644 --- a/packages/page-parachains/src/types.ts +++ b/packages/page-parachains/src/types.ts @@ -2,12 +2,12 @@ // SPDX-License-Identifier: Apache-2.0 import type BN from 'bn.js'; -import type { AuctionIndex, BlockNumber, LeasePeriodOf, ParachainProposal, ParaId, ParaInfo, SessionIndex } from '@polkadot/types/interfaces'; +import type { AccountId, AuctionIndex, BalanceOf, BlockNumber, FundInfo, LeasePeriodOf, ParachainProposal, ParaId, ParaInfo, SessionIndex } from '@polkadot/types/interfaces'; export interface AuctionInfo { endBlock: BlockNumber | null; leasePeriod: LeasePeriodOf | null; - numAuctions: AuctionIndex | null; + numAuctions: AuctionIndex; } export interface ProposalExt { @@ -22,6 +22,25 @@ export interface ScheduledProposals { sessionIndex: SessionIndex; } +export interface Campaigns { + activeCap: BN; + activeRaised: BN; + funds: Campaign[] | null; + totalCap: BN; + totalRaised: BN; +} + +export interface Campaign { + info: FundInfo; + isCapped?: boolean; + isEnded?: boolean; + isRetired?: boolean; + isWinner?: boolean; + key: string; + paraId: ParaId; + retireEnd?: BN; +} + export interface LeasePeriod { currentPeriod: BN; length: BN; @@ -45,3 +64,18 @@ export interface OwnerInfo { accountId: string | null; paraId: number; } + +export interface WinnerData { + accountId: AccountId; + firstSlot: number; + lastSlot: number; + paraId: ParaId; + value: BalanceOf; +} + +export interface Winning { + blockNumber: BN; + blockOffset: BN; + total: BN; + winners: WinnerData[]; +} diff --git a/packages/page-parachains/src/useAuctionInfo.tsx b/packages/page-parachains/src/useAuctionInfo.tsx index 2a088501b0..29b99c9480 100644 --- a/packages/page-parachains/src/useAuctionInfo.tsx +++ b/packages/page-parachains/src/useAuctionInfo.tsx @@ -8,14 +8,7 @@ import type { AuctionInfo } from './types'; import { useApi, useCallMulti } from '@polkadot/react-hooks'; -const defaultValue: AuctionInfo = { - endBlock: null, - leasePeriod: null, - numAuctions: null -}; - const optionsMulti = { - defaultValue, transform: ([numAuctions, optInfo]: [AuctionIndex, Option>]): AuctionInfo => { const [leasePeriod, endBlock] = optInfo.unwrapOr([null, null]); @@ -27,7 +20,7 @@ const optionsMulti = { } }; -export default function useAuctionInfo (): AuctionInfo { +export default function useAuctionInfo (): AuctionInfo | undefined { const { api } = useApi(); return useCallMulti([ diff --git a/packages/page-parachains/src/Crowdloan/useFunds.ts b/packages/page-parachains/src/useFunds.ts similarity index 93% rename from packages/page-parachains/src/Crowdloan/useFunds.ts rename to packages/page-parachains/src/useFunds.ts index 108e881a1c..cea32350a4 100644 --- a/packages/page-parachains/src/Crowdloan/useFunds.ts +++ b/packages/page-parachains/src/useFunds.ts @@ -4,7 +4,7 @@ import type { Option } from '@polkadot/types'; import type { AccountId, BalanceOf, BlockNumber, FundInfo, ParaId } from '@polkadot/types/interfaces'; import type { ITuple } from '@polkadot/types/types'; -import type { Campaign } from './types'; +import type { Campaign, Campaigns } from './types'; import BN from 'bn.js'; import { useEffect, useState } from 'react'; @@ -12,15 +12,7 @@ import { useEffect, useState } from 'react'; import { useApi, useBestNumber, useCall, useEventTrigger } from '@polkadot/react-hooks'; import { BN_ZERO, stringToU8a, u8aConcat } from '@polkadot/util'; -interface FundResult { - activeCap: BN; - activeRaised: BN; - funds: Campaign[] | null; - totalCap: BN; - totalRaised: BN; -} - -const EMPTY: FundResult = { +const EMPTY: Campaigns = { activeCap: BN_ZERO, activeRaised: BN_ZERO, funds: null, @@ -50,7 +42,7 @@ function updateFund (bestNumber: BN, minContribution: BN, retirementPeriod: BN, return data; } -function isFundUpdated (bestNumber: BlockNumber, minContribution: BN, retirementPeriod: BN, { info: { cap, end, raised }, paraId }: Campaign, leased: ParaId[], allPrev: FundResult): boolean { +function isFundUpdated (bestNumber: BlockNumber, minContribution: BN, retirementPeriod: BN, { info: { cap, end, raised }, paraId }: Campaign, leased: ParaId[], allPrev: Campaigns): boolean { const prev = allPrev.funds?.find((p) => p.paraId.eq(paraId)); return !prev || @@ -61,7 +53,7 @@ function isFundUpdated (bestNumber: BlockNumber, minContribution: BN, retirement } // compare the current campaigns against the previous, manually adding ending and calculating the new totals -function createResult (bestNumber: BlockNumber, minContribution: BN, retirementPeriod: BN, funds: Campaign[], leased: ParaId[], prev: FundResult): FundResult { +function createResult (bestNumber: BlockNumber, minContribution: BN, retirementPeriod: BN, funds: Campaign[], leased: ParaId[], prev: Campaigns): Campaigns { const [activeRaised, activeCap, totalRaised, totalCap] = funds.reduce(([ar, ac, tr, tc], { info: { cap, end, raised } }) => [ bestNumber.gt(end) ? ar : ar.iadd(raised), bestNumber.gt(end) ? ac : ac.iadd(cap), @@ -149,14 +141,14 @@ const optLeaseMulti = { withParamsTransform: true }; -export default function useFunds (): FundResult { +export default function useFunds (): Campaigns { const { api } = useApi(); const bestNumber = useBestNumber(); const [paraIds, setParaIds] = useState([]); const trigger = useEventTrigger([api.events.crowdloan.Created]); const campaigns = useCall(api.query.crowdloan.funds.multi, [paraIds], optFundMulti); const leases = useCall(api.query.slots.leases.multi, [paraIds], optLeaseMulti); - const [result, setResult] = useState(EMPTY); + const [result, setResult] = useState(EMPTY); // on event triggers, update the available paraIds useEffect((): void => { diff --git a/packages/page-parachains/src/useLeasePeriod.ts b/packages/page-parachains/src/useLeasePeriod.ts index 982022a4e4..b660dbe118 100644 --- a/packages/page-parachains/src/useLeasePeriod.ts +++ b/packages/page-parachains/src/useLeasePeriod.ts @@ -8,23 +8,23 @@ import { useMemo } from 'react'; import { useApi, useBestNumber } from '@polkadot/react-hooks'; -export default function useLeasePeriod (): LeasePeriod | null { +export default function useLeasePeriod (): LeasePeriod | undefined { const { api } = useApi(); const bestNumber = useBestNumber(); - return useMemo((): LeasePeriod | null => { - if (api.consts.slots?.leasePeriod && bestNumber) { - const length = api.consts.slots.leasePeriod as BlockNumber; - const progress = bestNumber.mod(length); - - return { - currentPeriod: bestNumber.div(length), - length, - progress, - remainder: length.sub(progress) - }; + return useMemo((): LeasePeriod | undefined => { + if (!api.consts.slots?.leasePeriod || !bestNumber) { + return; } - return null; + const length = api.consts.slots.leasePeriod as BlockNumber; + const progress = bestNumber.mod(length); + + return { + currentPeriod: bestNumber.div(length), + length, + progress, + remainder: length.sub(progress) + }; }, [api, bestNumber]); } diff --git a/packages/page-parachains/src/Auctions/useWinningData.ts b/packages/page-parachains/src/useWinningData.ts similarity index 88% rename from packages/page-parachains/src/Auctions/useWinningData.ts rename to packages/page-parachains/src/useWinningData.ts index 7935a2dd34..16e4b51d0c 100644 --- a/packages/page-parachains/src/Auctions/useWinningData.ts +++ b/packages/page-parachains/src/useWinningData.ts @@ -3,8 +3,7 @@ import type { Option, StorageKey } from '@polkadot/types'; import type { BlockNumber, WinningData } from '@polkadot/types/interfaces'; -import type { AuctionInfo } from '../types'; -import type { WinnerData, Winning } from './types'; +import type { AuctionInfo, WinnerData, Winning } from './types'; import BN from 'bn.js'; import { useEffect, useRef, useState } from 'react'; @@ -71,7 +70,7 @@ function extractData (auctionInfo: AuctionInfo, values: [StorageKey<[BlockNumber .reverse(); } -function mergeCurrent (auctionInfo: AuctionInfo, prev: Winning[] | null, optCurrent: Option, blockOffset: BN): Winning[] | null { +function mergeCurrent (auctionInfo: AuctionInfo, prev: Winning[] | undefined, optCurrent: Option, blockOffset: BN): Winning[] | undefined { const current = createWinning(auctionInfo, blockOffset, extractWinners(auctionInfo, optCurrent)); if (current.winners.length) { @@ -93,7 +92,7 @@ function mergeCurrent (auctionInfo: AuctionInfo, prev: Winning[] | null, optCurr return prev; } -function mergeFirst (auctionInfo: AuctionInfo, prev: Winning[] | null, optFirstData: Option): Winning[] | null { +function mergeFirst (auctionInfo: AuctionInfo, prev: Winning[] | undefined, optFirstData: Option): Winning[] | undefined { if (prev && prev.length <= 1) { const updated: Winning[] = prev || []; const firstEntry = createWinning(auctionInfo, null, extractWinners(auctionInfo, optFirstData)); @@ -112,9 +111,9 @@ function mergeFirst (auctionInfo: AuctionInfo, prev: Winning[] | null, optFirstD return prev; } -export default function useWinningData (auctionInfo: AuctionInfo): Winning[] | null { +export default function useWinningData (auctionInfo?: AuctionInfo): Winning[] | undefined { const { api } = useApi(); - const [result, setResult] = useState(null); + const [result, setResult] = useState(); const bestNumber = useBestNumber(); const trigger = useEventTrigger([api.events.auctions?.BidAccepted]); const triggerRef = useRef(trigger); @@ -123,14 +122,14 @@ export default function useWinningData (auctionInfo: AuctionInfo): Winning[] | n // should be fired once, all entries as an initial round useEffect((): void => { - allEntries && setResult( + auctionInfo && allEntries && setResult( extractData(auctionInfo, allEntries) ); }, [allEntries, auctionInfo]); // when block 0 changes, update (typically in non-ending-period, static otherwise) useEffect((): void => { - optFirstData && setResult((prev) => + auctionInfo && optFirstData && setResult((prev) => mergeFirst(auctionInfo, prev, optFirstData) ); }, [auctionInfo, optFirstData]); @@ -139,9 +138,7 @@ export default function useWinningData (auctionInfo: AuctionInfo): Winning[] | n // and add it to the list when not duplicated. Additionally we cleanup after ourselves when endBlock // gets cleared useEffect((): void => { - if (!auctionInfo.endBlock) { - setResult((prev) => prev && prev.length ? [] : prev); - } else if (bestNumber && bestNumber.gt(auctionInfo.endBlock) && triggerRef.current !== trigger) { + if (auctionInfo?.endBlock && bestNumber && bestNumber.gt(auctionInfo.endBlock) && triggerRef.current !== trigger) { const blockOffset = bestNumber.sub(auctionInfo.endBlock).iadd(BN_ONE); triggerRef.current = trigger;