From 8a668027d6d953d4bef3badadc265937ba933158 Mon Sep 17 00:00:00 2001 From: Daria Mikhailova Date: Wed, 13 Nov 2024 18:36:05 +0100 Subject: [PATCH 01/14] added sales tab --- packages/page-coretime/src/Overview/index.tsx | 41 ++++++++++++++++ packages/page-coretime/src/Sale/index.tsx | 1 + packages/page-coretime/src/index.tsx | 49 ++++++++++--------- 3 files changed, 67 insertions(+), 24 deletions(-) create mode 100644 packages/page-coretime/src/Overview/index.tsx create mode 100644 packages/page-coretime/src/Sale/index.tsx diff --git a/packages/page-coretime/src/Overview/index.tsx b/packages/page-coretime/src/Overview/index.tsx new file mode 100644 index 00000000000..1ced393d94d --- /dev/null +++ b/packages/page-coretime/src/Overview/index.tsx @@ -0,0 +1,41 @@ +// Copyright 2017-2024 @polkadot/app-coretime authors & contributors +// SPDX-License-Identifier: Apache-2.0 + + +import React from 'react'; + +import { useApi, useCoretimeInformation } from '@polkadot/react-hooks'; + +import Summary from './Summary.js'; +import ParachainsTable from '../ParachainsTable.js'; + +interface Props { + className?: string; +} + +function Overview({ className }: Props): React.ReactElement { + const { api, isApiReady } = useApi(); + const coretimeInfo = useCoretimeInformation(api, isApiReady); + return ( +
+ {coretimeInfo && ( + + )} + {!!coretimeInfo && + + } + +
+ ); +} + +export default React.memo(Overview); diff --git a/packages/page-coretime/src/Sale/index.tsx b/packages/page-coretime/src/Sale/index.tsx new file mode 100644 index 00000000000..e3742fb86ea --- /dev/null +++ b/packages/page-coretime/src/Sale/index.tsx @@ -0,0 +1 @@ +export const Sale = () =>

I am sale

\ No newline at end of file diff --git a/packages/page-coretime/src/index.tsx b/packages/page-coretime/src/index.tsx index 3125e2b3276..bcc588ef262 100644 --- a/packages/page-coretime/src/index.tsx +++ b/packages/page-coretime/src/index.tsx @@ -6,32 +6,33 @@ import type { TabItem } from '@polkadot/react-components/types'; import React, { useRef } from 'react'; import { Tabs } from '@polkadot/react-components'; -import { useApi, useCoretimeInformation } from '@polkadot/react-hooks'; - -import Summary from './Overview/Summary.js'; -import ParachainsTable from './ParachainsTable.js'; import { useTranslation } from './translate.js'; +import { Route, Routes } from 'react-router-dom'; +import Overview from './Overview/index.js'; +import { Sale } from './Sale/index.js'; interface Props { basePath: string; className?: string; } -function createItemsRef (t: (key: string, options?: { replace: Record }) => string): TabItem[] { +function createItemsRef(t: (key: string, options?: { replace: Record }) => string): TabItem[] { return [ { isRoot: true, name: 'overview', text: t('Overview') + }, + { + name: 'sale', + text: t('Sale') } ]; } -function CoretimeApp ({ basePath, className }: Props): React.ReactElement { +function CoretimeApp({ basePath, className }: Props): React.ReactElement { const { t } = useTranslation(); - const { api, isApiReady } = useApi(); const itemsRef = useRef(createItemsRef(t)); - const coretimeInfo = useCoretimeInformation(api, isApiReady); return (
@@ -39,22 +40,22 @@ function CoretimeApp ({ basePath, className }: Props): React.ReactElement basePath={basePath} items={itemsRef.current} /> - {coretimeInfo && ( - - )} - {!!coretimeInfo && - - } - + + + + } + index + /> + + } + path='sale' + /> + +
); } From 3224f745b48c4e4f18f26f4c561741cf239f7ebd Mon Sep 17 00:00:00 2001 From: Daria Mikhailova Date: Wed, 13 Nov 2024 20:03:19 +0100 Subject: [PATCH 02/14] draft for the progress bar --- .../page-coretime/src/Overview/Summary.tsx | 22 ++--- packages/page-coretime/src/Overview/index.tsx | 8 +- packages/page-coretime/src/Sale/Summary.tsx | 98 +++++++++++++++++++ packages/page-coretime/src/Sale/index.tsx | 79 ++++++++++++++- packages/page-coretime/src/index.tsx | 9 +- packages/react-components/src/ProgresBar.tsx | 89 +++++++++++++++++ 6 files changed, 285 insertions(+), 20 deletions(-) create mode 100644 packages/page-coretime/src/Sale/Summary.tsx create mode 100644 packages/react-components/src/ProgresBar.tsx diff --git a/packages/page-coretime/src/Overview/Summary.tsx b/packages/page-coretime/src/Overview/Summary.tsx index fa570454940..60cb7fda1d1 100644 --- a/packages/page-coretime/src/Overview/Summary.tsx +++ b/packages/page-coretime/src/Overview/Summary.tsx @@ -23,7 +23,7 @@ interface Props { parachainCount: number } -function Summary ({ api, config, parachainCount, saleInfo, status }: Props): React.ReactElement { +function Summary({ api, config, parachainCount, saleInfo, status }: Props): React.ReactElement { const { t } = useTranslation(); const currentRegionEnd = saleInfo.regionEnd - config.regionLength; const currentRegionStart = saleInfo.regionEnd - config.regionLength * 2; @@ -31,27 +31,25 @@ function Summary ({ api, config, parachainCount, saleInfo, status }: Props): Rea const cycleNumber = useMemo(() => chainName && currentRegionEnd && Math.floor((currentRegionEnd - FirstCycleStart[chainName]) / config.regionLength) - , [currentRegionEnd, chainName, config]); + , [currentRegionEnd, chainName, config]); return (
- - {status?.lastTimeslice} - - - {`${saleInfo?.coresSold} / ${saleInfo?.coresOffered}`} - - - {parachainCount && parachainCount} - {status && - +
{cycleNumber}
} + + {status?.lastTimeslice} + + + {parachainCount && parachainCount} + + {config && status && { +function Overview({ className, coretimeInfo }: Props): React.ReactElement { const { api, isApiReady } = useApi(); - const coretimeInfo = useCoretimeInformation(api, isApiReady); return (
{coretimeInfo && ( @@ -33,7 +34,6 @@ function Overview({ className }: Props): React.ReactElement { coretimeInfo={coretimeInfo} /> } -
); } diff --git a/packages/page-coretime/src/Sale/Summary.tsx b/packages/page-coretime/src/Sale/Summary.tsx new file mode 100644 index 00000000000..ed55ed3789e --- /dev/null +++ b/packages/page-coretime/src/Sale/Summary.tsx @@ -0,0 +1,98 @@ +// Copyright 2017-2024 @polkadot/app-coretime authors & contributors +// SPDX-License-Identifier: Apache-2.0 + +import type { ApiPromise } from '@polkadot/api'; +import type { BrokerStatus, CoreDescription, PalletBrokerConfigRecord, PalletBrokerSaleInfoRecord, RegionInfo } from '@polkadot/react-hooks/types'; + +import React, { useMemo } from 'react'; + +import { CardSummary, SummaryBox } from '@polkadot/react-components'; +import { useCall } from '@polkadot/react-hooks'; +import { BN } from '@polkadot/util'; + +import { useTranslation } from '../translate.js'; +import { estimateTime, FirstCycleStart } from '../utils.js'; + +interface Props { + api: ApiPromise | null, + coreDscriptors?: CoreDescription[]; + saleInfo: PalletBrokerSaleInfoRecord + config: PalletBrokerConfigRecord, + region: RegionInfo[], + status: BrokerStatus, +} + +function Summary({ api, config, saleInfo, status }: Props): React.ReactElement { + const { t } = useTranslation(); + const currentRegionEnd = saleInfo.regionEnd - config.regionLength; + const currentRegionStart = saleInfo.regionEnd - config.regionLength * 2; + const chainName = useCall(api?.rpc.system.chain)?.toString().toLowerCase(); + + const cycleNumber = useMemo(() => + chainName && currentRegionEnd && Math.floor((currentRegionEnd - FirstCycleStart[chainName]) / config.regionLength) + , [currentRegionEnd, chainName, config]); + + return ( + +
+ {status && + +
+ {cycleNumber} +
+
+ } + + {`${saleInfo?.coresSold} / ${saleInfo?.coresOffered}`} + + + + +
{estimateTime(currentRegionEnd, status?.lastTimeslice * 80)}
+
+ + + +
{currentRegionEnd * 80}
+
+ + +
{currentRegionEnd}
+
+ + {config && status && + + } +
+
+ {status && + ( +
+
{estimateTime(currentRegionStart, status?.lastTimeslice * 80)}
+
{estimateTime(currentRegionEnd, status?.lastTimeslice * 80)}
+
+
) + } + {status && + +
+
{currentRegionStart}
+
{currentRegionEnd}
+
+
+ } +
+
+ ); +} + +export default React.memo(Summary); diff --git a/packages/page-coretime/src/Sale/index.tsx b/packages/page-coretime/src/Sale/index.tsx index e3742fb86ea..894ff6dcfde 100644 --- a/packages/page-coretime/src/Sale/index.tsx +++ b/packages/page-coretime/src/Sale/index.tsx @@ -1 +1,78 @@ -export const Sale = () =>

I am sale

\ No newline at end of file +import { useApi } from "@polkadot/react-hooks"; +import Summary from "./Summary.js" +import { CoretimeInformation } from "@polkadot/react-hooks/types"; +import { Button, CardSummary, Progress, SummaryBox } from "@polkadot/react-components"; +import { useTranslation } from "../translate.js"; +import { formatNumber } from '@polkadot/util'; +import ProgresBar from "@polkadot/react-components/ProgresBar"; + +interface Props { + coretimeInfo: CoretimeInformation +} + +function Sale({ coretimeInfo }: Props): React.ReactElement { + const { api, isApiReady } = useApi(); + const { t } = useTranslation(); + // const available = Number(coretimeInfo?.salesInfo?.coresOffered) - Number(coretimeInfo?.salesInfo.coresSold) + const available = 1 + return ( +
+ {coretimeInfo && + } +
+ +
+ + 100 DOT + {/* {!!available && {available}} + {!available &&

Currently there are no cores available for purchase

} */} + +
+ {!!available &&
+
} +
+
+ +
+ Renewals + + 11th Nov 2024 + 12313123123 + {formatNumber(coretimeInfo?.salesInfo.endPrice)} + {formatNumber(coretimeInfo?.salesInfo.selloutPrice)} +
+
+ +
+
+ +
+
+
+ ) +} + +export default Sale \ No newline at end of file diff --git a/packages/page-coretime/src/index.tsx b/packages/page-coretime/src/index.tsx index bcc588ef262..46f396613eb 100644 --- a/packages/page-coretime/src/index.tsx +++ b/packages/page-coretime/src/index.tsx @@ -9,7 +9,8 @@ import { Tabs } from '@polkadot/react-components'; import { useTranslation } from './translate.js'; import { Route, Routes } from 'react-router-dom'; import Overview from './Overview/index.js'; -import { Sale } from './Sale/index.js'; +import { useApi, useCoretimeInformation } from '@polkadot/react-hooks'; +import Sale from './Sale/index.js'; interface Props { basePath: string; @@ -33,6 +34,8 @@ function createItemsRef(t: (key: string, options?: { replace: Record { const { t } = useTranslation(); const itemsRef = useRef(createItemsRef(t)); + const { api, isApiReady } = useApi(); + const coretimeInfo = useCoretimeInformation(api, isApiReady); return (
@@ -44,13 +47,13 @@ function CoretimeApp({ basePath, className }: Props): React.ReactElement + } index /> + } path='sale' /> diff --git a/packages/react-components/src/ProgresBar.tsx b/packages/react-components/src/ProgresBar.tsx new file mode 100644 index 00000000000..fb6fa97bba6 --- /dev/null +++ b/packages/react-components/src/ProgresBar.tsx @@ -0,0 +1,89 @@ +import React from 'react'; +import { styled } from './styled.js'; + +interface Section { + value: number; + total: number; + label: string; +} + +interface Props { + sections: Section[]; +} + +function ProgressBar({ sections }: Props): React.ReactElement | null { + const overallTotal = sections.reduce((sum, section) => sum + section.total, 0); + console.log('overall total ', overallTotal) + return ( + +
+ {sections.map((section, index) => { + const sectionWidth = (section.total / overallTotal) * 100; + const sectionProgress = (section.value / section.total) * 100; + + return ( +
+
+
+
+ ); + })} +
+
+ {sections.map((section, index) => ( +
+ {section.label} +
+ ))} +
+ + ); +} + +const StyledDiv = styled.div` + width: 100%; + .progress-container { + display: flex; + height: 1rem; + background-color: #e0e0e0; + border-radius: 0.5rem; + overflow: hidden; + position: relative; + } + + .progress-segment { + position: relative; + background-color: transparent; + height: 100%; + display: flex; + align-items: center; + } + + .progress-bar { + background-color: #007bff; + height: 100%; + transition: width 0.3s ease; + } + + .marker { + position: absolute; + left: 0; + top: -0.5rem; + width: 2px; + height: 1.5rem; + background-color: #000000; + } + + .labels { + display: flex; + justify-content: space-between; + margin-top: 0.5rem; + } + + .label { + text-align: center; + font-size: 0.875rem; + } +`; + +export default React.memo(ProgressBar); From 1eef2e81f1335339a4ed8106f643b6f7d27fa9ec Mon Sep 17 00:00:00 2001 From: Daria Mikhailova Date: Thu, 14 Nov 2024 21:15:10 +0100 Subject: [PATCH 03/14] draft for calculating values for the coretime progress timeline --- packages/page-coretime/src/Sale/index.tsx | 100 ++++++++++++++---- packages/page-coretime/src/index.tsx | 7 +- packages/page-coretime/src/types.ts | 12 +++ packages/page-coretime/src/utils.ts | 4 + packages/react-hooks/src/types.ts | 1 + .../react-hooks/src/useCoretimeInformation.ts | 12 ++- 6 files changed, 106 insertions(+), 30 deletions(-) diff --git a/packages/page-coretime/src/Sale/index.tsx b/packages/page-coretime/src/Sale/index.tsx index 894ff6dcfde..e66d33dfaef 100644 --- a/packages/page-coretime/src/Sale/index.tsx +++ b/packages/page-coretime/src/Sale/index.tsx @@ -1,10 +1,13 @@ import { useApi } from "@polkadot/react-hooks"; import Summary from "./Summary.js" -import { CoretimeInformation } from "@polkadot/react-hooks/types"; -import { Button, CardSummary, Progress, SummaryBox } from "@polkadot/react-components"; +import { CoreTimeChainConsts, CoretimeInformation } from "@polkadot/react-hooks/types"; +import { Button, CardSummary, SummaryBox } from "@polkadot/react-components"; import { useTranslation } from "../translate.js"; import { formatNumber } from '@polkadot/util'; import ProgresBar from "@polkadot/react-components/ProgresBar"; +import { useMemo } from "react"; +import { PhaseName } from "../types.js"; +import { estimateTime } from "../utils.js"; interface Props { coretimeInfo: CoretimeInformation @@ -13,6 +16,74 @@ interface Props { function Sale({ coretimeInfo }: Props): React.ReactElement { const { api, isApiReady } = useApi(); const { t } = useTranslation(); + const { + salesInfo: { regionBegin }, + config: { regionLength, interludeLength, leadinLength }, + status: { lastCommittedTimeslice } + } = coretimeInfo; + + const interludeLengthTs = interludeLength / CoreTimeChainConsts.BlocksPerTimeslice; + const leadInLengthTs = leadinLength / CoreTimeChainConsts.BlocksPerTimeslice; + const currentRegionStart = regionBegin - regionLength; + + const phaseConfig = useMemo(() => { + if (!coretimeInfo || !coretimeInfo.blockTimeMs) { + return undefined; + } + return { + [PhaseName.Renewals]: { + lastTimeslice: currentRegionStart + interludeLengthTs, + lastBlock: (currentRegionStart + interludeLengthTs) * 80 + }, + [PhaseName.PriceDiscovery]: { + lastTimeslice: currentRegionStart + interludeLengthTs + leadInLengthTs, + lastBlock: (currentRegionStart + interludeLengthTs + leadInLengthTs) * 80 + }, + [PhaseName.FixedPrice]: { + lastTimeslice: regionBegin, + lastBlock: regionBegin * 80 + } + } + }, [coretimeInfo]) + + + const currentPhase = useMemo(() => { + const progress = lastCommittedTimeslice - currentRegionStart; + if (progress < interludeLengthTs) { + return PhaseName.Renewals + } + if (progress < interludeLengthTs + leadInLengthTs) { + return PhaseName.PriceDiscovery; + } + return PhaseName.FixedPrice + }, [interludeLengthTs, leadInLengthTs]) + + console.log('currentPhase ', currentPhase) + + + const progressValues = useMemo(() => { + const progress = lastCommittedTimeslice - currentRegionStart; + return [ + { + value: Math.min(progress, interludeLengthTs), + total: interludeLengthTs, + label: PhaseName.Renewals + }, + { + value: Math.min(Math.max(progress - interludeLengthTs, 0), leadInLengthTs), + total: leadInLengthTs, + label: PhaseName.PriceDiscovery + }, + { + value: Math.max(progress - interludeLengthTs - leadInLengthTs, 0), + total: regionBegin - currentRegionStart - interludeLengthTs - leadInLengthTs, + label: PhaseName.FixedPrice + } + ]; + }, [interludeLengthTs, leadInLengthTs, lastCommittedTimeslice, currentRegionStart, regionBegin]); + + + // const available = Number(coretimeInfo?.salesInfo?.coresOffered) - Number(coretimeInfo?.salesInfo.coresSold) const available = 1 return ( @@ -41,10 +112,9 @@ function Sale({ coretimeInfo }: Props): React.ReactElement {
- Renewals - - 11th Nov 2024 - 12313123123 + {currentPhase && currentPhase} + {currentPhase && phaseConfig && estimateTime(phaseConfig[currentPhase].lastTimeslice, coretimeInfo.status.lastTimeslice * 40)} + {currentPhase && phaseConfig && phaseConfig[currentPhase].lastBlock} {formatNumber(coretimeInfo?.salesInfo.endPrice)} {formatNumber(coretimeInfo?.salesInfo.selloutPrice)}
@@ -52,23 +122,7 @@ function Sale({ coretimeInfo }: Props): React.ReactElement {
- + diff --git a/packages/page-coretime/src/index.tsx b/packages/page-coretime/src/index.tsx index 46f396613eb..9415fac0cff 100644 --- a/packages/page-coretime/src/index.tsx +++ b/packages/page-coretime/src/index.tsx @@ -3,7 +3,7 @@ import type { TabItem } from '@polkadot/react-components/types'; -import React, { useRef } from 'react'; +import React, { useMemo, useRef } from 'react'; import { Tabs } from '@polkadot/react-components'; import { useTranslation } from './translate.js'; @@ -11,6 +11,7 @@ import { Route, Routes } from 'react-router-dom'; import Overview from './Overview/index.js'; import { useApi, useCoretimeInformation } from '@polkadot/react-hooks'; import Sale from './Sale/index.js'; +import { CoreTimeChainConsts } from '@polkadot/react-hooks/types'; interface Props { basePath: string; @@ -47,13 +48,13 @@ function CoretimeApp({ basePath, className }: Props): React.ReactElement + coretimeInfo && } index /> + coretimeInfo && } path='sale' /> diff --git a/packages/page-coretime/src/types.ts b/packages/page-coretime/src/types.ts index b5afab7d6a1..0dbd8bc25a7 100644 --- a/packages/page-coretime/src/types.ts +++ b/packages/page-coretime/src/types.ts @@ -7,3 +7,15 @@ export enum CoreTimeTypes { 'Bulk Coretime', 'On Demand' } + +export type PhaseInfo = { + name: string; + lastBlock: number; + lastTimeslice: number +}; + +export const PhaseName = { + Renewals: 'Renewals', + PriceDiscovery: 'Price Discovery', + FixedPrice: 'Fixed Price' +} \ No newline at end of file diff --git a/packages/page-coretime/src/utils.ts b/packages/page-coretime/src/utils.ts index 432f4cc56a6..6fa4fb37846 100644 --- a/packages/page-coretime/src/utils.ts +++ b/packages/page-coretime/src/utils.ts @@ -65,3 +65,7 @@ export const getOccupancyType = (lease: LegacyLease | undefined, reservation: Re return reservation ? CoreTimeTypes.Reservation : lease ? CoreTimeTypes.Lease : CoreTimeTypes['Bulk Coretime']; }; + +export const getCurrentPhase = () => { + +} diff --git a/packages/react-hooks/src/types.ts b/packages/react-hooks/src/types.ts index dd4317627b2..5698f68dc38 100644 --- a/packages/react-hooks/src/types.ts +++ b/packages/react-hooks/src/types.ts @@ -344,6 +344,7 @@ export interface CoretimeInformation { region: RegionInfo[], config: PalletBrokerConfigRecord taskIds: number[] + blockTimeMs: number } export interface BrokerStatus { diff --git a/packages/react-hooks/src/useCoretimeInformation.ts b/packages/react-hooks/src/useCoretimeInformation.ts index dcd34566865..a33c4be0ea1 100644 --- a/packages/react-hooks/src/useCoretimeInformation.ts +++ b/packages/react-hooks/src/useCoretimeInformation.ts @@ -6,8 +6,8 @@ import type { ChainInformation, CoretimeInformation, CoreWorkload, CoreWorkloadI import { useEffect, useMemo, useState } from 'react'; -import { createNamedHook, useApi, useBrokerConfig, useBrokerLeases, useBrokerReservations, useBrokerSalesInfo, useBrokerStatus, useCoreDescriptor, useRegions, useWorkloadInfos, useWorkplanInfos } from '@polkadot/react-hooks'; -import { BN } from '@polkadot/util'; +import { createNamedHook, useApi, useBlockTime, useBrokerConfig, useBrokerLeases, useBrokerReservations, useBrokerSalesInfo, useBrokerStatus, useCoreDescriptor, useRegions, useWorkloadInfos, useWorkplanInfos } from '@polkadot/react-hooks'; +import { BN, BN_ONE } from '@polkadot/util'; import { ChainRenewalStatus, CoreTimeChainConsts, CoreTimeTypes } from './types.js'; import { useBrokerPotentialRenewals } from './useBrokerPotentialRenewals.js'; @@ -38,6 +38,9 @@ function useCoretimeInformationImpl (api: ApiPromise, ready: boolean): CoretimeI const region = useRegions(apiCoretime); /** Other APIs */ + const [blockTimeMs] = useBlockTime(BN_ONE, apiCoretime); + console.log('res ', blockTimeMs) + // const blockTimeMs = 1 const coreInfos = useCoreDescriptor(api, ready); const paraIds = useMemo(() => coreInfos && [...new Set(coreInfos?.map((a) => a.info.currentWork.assignments.map((ass) => ass.task)).flat().filter((id) => id !== 'Pool'))], [coreInfos]); @@ -146,10 +149,11 @@ function useCoretimeInformationImpl (api: ApiPromise, ready: boolean): CoretimeI region, salesInfo, status, - taskIds + taskIds, + blockTimeMs }); } - }, [taskIds, workloadData, potentialRenewalsCurrentRegion, salesInfo, leases, reservations, region, status, config, workplans]); + }, [taskIds, workloadData, potentialRenewalsCurrentRegion, salesInfo, leases, reservations, region, status, config, workplans, blockTimeMs]); return state; } From a66a409ec383180d45659bc3064e7e4c6b5e891c Mon Sep 17 00:00:00 2001 From: Daria Mikhailova Date: Thu, 14 Nov 2024 21:32:18 +0100 Subject: [PATCH 04/14] lint --- .../page-coretime/src/Overview/Summary.tsx | 5 +- packages/page-coretime/src/Overview/index.tsx | 7 +- packages/page-coretime/src/Sale/Summary.tsx | 12 +- packages/page-coretime/src/Sale/index.tsx | 250 +++++++++--------- packages/page-coretime/src/index.tsx | 13 +- packages/page-coretime/src/types.ts | 6 +- packages/page-coretime/src/utils.ts | 4 +- packages/react-components/src/ProgresBar.tsx | 33 ++- .../react-hooks/src/useCoretimeInformation.ts | 3 +- 9 files changed, 179 insertions(+), 154 deletions(-) diff --git a/packages/page-coretime/src/Overview/Summary.tsx b/packages/page-coretime/src/Overview/Summary.tsx index 60cb7fda1d1..1b22b05824e 100644 --- a/packages/page-coretime/src/Overview/Summary.tsx +++ b/packages/page-coretime/src/Overview/Summary.tsx @@ -23,7 +23,7 @@ interface Props { parachainCount: number } -function Summary({ api, config, parachainCount, saleInfo, status }: Props): React.ReactElement { +function Summary ({ api, config, parachainCount, saleInfo, status }: Props): React.ReactElement { const { t } = useTranslation(); const currentRegionEnd = saleInfo.regionEnd - config.regionLength; const currentRegionStart = saleInfo.regionEnd - config.regionLength * 2; @@ -31,7 +31,7 @@ function Summary({ api, config, parachainCount, saleInfo, status }: Props): Reac const cycleNumber = useMemo(() => chainName && currentRegionEnd && Math.floor((currentRegionEnd - FirstCycleStart[chainName]) / config.regionLength) - , [currentRegionEnd, chainName, config]); + , [currentRegionEnd, chainName, config]); return ( @@ -49,7 +49,6 @@ function Summary({ api, config, parachainCount, saleInfo, status }: Props): Reac {parachainCount && parachainCount} - {config && status && { +function Overview ({ className, coretimeInfo }: Props): React.ReactElement { const { api, isApiReady } = useApi(); + return (
{coretimeInfo && ( diff --git a/packages/page-coretime/src/Sale/Summary.tsx b/packages/page-coretime/src/Sale/Summary.tsx index ed55ed3789e..85554e23136 100644 --- a/packages/page-coretime/src/Sale/Summary.tsx +++ b/packages/page-coretime/src/Sale/Summary.tsx @@ -22,7 +22,7 @@ interface Props { status: BrokerStatus, } -function Summary({ api, config, saleInfo, status }: Props): React.ReactElement { +function Summary ({ api, config, saleInfo, status }: Props): React.ReactElement { const { t } = useTranslation(); const currentRegionEnd = saleInfo.regionEnd - config.regionLength; const currentRegionStart = saleInfo.regionEnd - config.regionLength * 2; @@ -30,7 +30,7 @@ function Summary({ api, config, saleInfo, status }: Props): React.ReactElement

chainName && currentRegionEnd && Math.floor((currentRegionEnd - FirstCycleStart[chainName]) / config.regionLength) - , [currentRegionEnd, chainName, config]); + , [currentRegionEnd, chainName, config]); return ( @@ -45,21 +45,15 @@ function Summary({ api, config, saleInfo, status }: Props): React.ReactElement

{`${saleInfo?.coresSold} / ${saleInfo?.coresOffered}`} - -

{estimateTime(currentRegionEnd, status?.lastTimeslice * 80)}
- -
{currentRegionEnd * 80}
-
{currentRegionEnd}
- {config && status && } - + ); } diff --git a/packages/page-coretime/src/Sale/index.tsx b/packages/page-coretime/src/Sale/index.tsx index e66d33dfaef..e1504890c35 100644 --- a/packages/page-coretime/src/Sale/index.tsx +++ b/packages/page-coretime/src/Sale/index.tsx @@ -1,132 +1,144 @@ -import { useApi } from "@polkadot/react-hooks"; -import Summary from "./Summary.js" -import { CoreTimeChainConsts, CoretimeInformation } from "@polkadot/react-hooks/types"; -import { Button, CardSummary, SummaryBox } from "@polkadot/react-components"; -import { useTranslation } from "../translate.js"; +// [object Object] +// SPDX-License-Identifier: Apache-2.0 + +import type { CoretimeInformation } from '@polkadot/react-hooks/types'; + +import { useMemo } from 'react'; + +import { Button, CardSummary, SummaryBox } from '@polkadot/react-components'; +import ProgresBar from '@polkadot/react-components/ProgresBar'; +import { useApi } from '@polkadot/react-hooks'; +import { CoreTimeChainConsts } from '@polkadot/react-hooks/types'; import { formatNumber } from '@polkadot/util'; -import ProgresBar from "@polkadot/react-components/ProgresBar"; -import { useMemo } from "react"; -import { PhaseName } from "../types.js"; -import { estimateTime } from "../utils.js"; + +import { useTranslation } from '../translate.js'; +import { PhaseName } from '../types.js'; +import { estimateTime } from '../utils.js'; +import Summary from './Summary.js'; interface Props { - coretimeInfo: CoretimeInformation + coretimeInfo: CoretimeInformation } -function Sale({ coretimeInfo }: Props): React.ReactElement { - const { api, isApiReady } = useApi(); - const { t } = useTranslation(); - const { - salesInfo: { regionBegin }, - config: { regionLength, interludeLength, leadinLength }, - status: { lastCommittedTimeslice } - } = coretimeInfo; - - const interludeLengthTs = interludeLength / CoreTimeChainConsts.BlocksPerTimeslice; - const leadInLengthTs = leadinLength / CoreTimeChainConsts.BlocksPerTimeslice; - const currentRegionStart = regionBegin - regionLength; - - const phaseConfig = useMemo(() => { - if (!coretimeInfo || !coretimeInfo.blockTimeMs) { - return undefined; - } - return { - [PhaseName.Renewals]: { - lastTimeslice: currentRegionStart + interludeLengthTs, - lastBlock: (currentRegionStart + interludeLengthTs) * 80 - }, - [PhaseName.PriceDiscovery]: { - lastTimeslice: currentRegionStart + interludeLengthTs + leadInLengthTs, - lastBlock: (currentRegionStart + interludeLengthTs + leadInLengthTs) * 80 - }, - [PhaseName.FixedPrice]: { - lastTimeslice: regionBegin, - lastBlock: regionBegin * 80 - } - } - }, [coretimeInfo]) - - - const currentPhase = useMemo(() => { - const progress = lastCommittedTimeslice - currentRegionStart; - if (progress < interludeLengthTs) { - return PhaseName.Renewals - } - if (progress < interludeLengthTs + leadInLengthTs) { - return PhaseName.PriceDiscovery; - } - return PhaseName.FixedPrice - }, [interludeLengthTs, leadInLengthTs]) - - console.log('currentPhase ', currentPhase) - - - const progressValues = useMemo(() => { - const progress = lastCommittedTimeslice - currentRegionStart; - return [ - { - value: Math.min(progress, interludeLengthTs), - total: interludeLengthTs, - label: PhaseName.Renewals - }, - { - value: Math.min(Math.max(progress - interludeLengthTs, 0), leadInLengthTs), - total: leadInLengthTs, - label: PhaseName.PriceDiscovery - }, - { - value: Math.max(progress - interludeLengthTs - leadInLengthTs, 0), - total: regionBegin - currentRegionStart - interludeLengthTs - leadInLengthTs, - label: PhaseName.FixedPrice - } - ]; - }, [interludeLengthTs, leadInLengthTs, lastCommittedTimeslice, currentRegionStart, regionBegin]); - - - - // const available = Number(coretimeInfo?.salesInfo?.coresOffered) - Number(coretimeInfo?.salesInfo.coresSold) - const available = 1 - return ( -
- {coretimeInfo && +function Sale ({ coretimeInfo }: Props): React.ReactElement { + const { api, isApiReady } = useApi(); + const { t } = useTranslation(); + const { config: { interludeLength, leadinLength, regionLength }, + salesInfo: { regionBegin }, + status: { lastCommittedTimeslice } } = coretimeInfo; + + const interludeLengthTs = interludeLength / CoreTimeChainConsts.BlocksPerTimeslice; + const leadInLengthTs = leadinLength / CoreTimeChainConsts.BlocksPerTimeslice; + const currentRegionStart = regionBegin - regionLength; + + const phaseConfig = useMemo(() => { + if (!coretimeInfo?.blockTimeMs) { + return undefined; + } + + return { + [PhaseName.Renewals]: { + lastTimeslice: currentRegionStart + interludeLengthTs, + lastBlock: (currentRegionStart + interludeLengthTs) * 80 + }, + [PhaseName.PriceDiscovery]: { + lastTimeslice: currentRegionStart + interludeLengthTs + leadInLengthTs, + lastBlock: (currentRegionStart + interludeLengthTs + leadInLengthTs) * 80 + }, + [PhaseName.FixedPrice]: { + lastTimeslice: regionBegin, + lastBlock: regionBegin * 80 + } + }; + }, [coretimeInfo]); + + const currentPhase = useMemo(() => { + const progress = lastCommittedTimeslice - currentRegionStart; + + if (progress < interludeLengthTs) { + return PhaseName.Renewals; + } + + if (progress < interludeLengthTs + leadInLengthTs) { + return PhaseName.PriceDiscovery; + } + + return PhaseName.FixedPrice; + }, [interludeLengthTs, leadInLengthTs]); + + console.log('currentPhase ', currentPhase); + + const progressValues = useMemo(() => { + const progress = lastCommittedTimeslice - currentRegionStart; + + return [ + { + value: Math.min(progress, interludeLengthTs), + total: interludeLengthTs, + label: PhaseName.Renewals + }, + { + value: Math.min(Math.max(progress - interludeLengthTs, 0), leadInLengthTs), + total: leadInLengthTs, + label: PhaseName.PriceDiscovery + }, + { + value: Math.max(progress - interludeLengthTs - leadInLengthTs, 0), + total: regionBegin - currentRegionStart - interludeLengthTs - leadInLengthTs, + label: PhaseName.FixedPrice + } + ]; + }, [interludeLengthTs, leadInLengthTs, lastCommittedTimeslice, currentRegionStart, regionBegin]); + + // const available = Number(coretimeInfo?.salesInfo?.coresOffered) - Number(coretimeInfo?.salesInfo.coresSold) + const available = 1; + + return ( +
+ {coretimeInfo && } -
+
-
- - 100 DOT - {/* {!!available && {available}} +
+ + 100 DOT + {/* {!!available && {available}} {!available &&

Currently there are no cores available for purchase

} */} -
- {!!available &&
-
} -
-
- -
- {currentPhase && currentPhase} - {currentPhase && phaseConfig && estimateTime(phaseConfig[currentPhase].lastTimeslice, coretimeInfo.status.lastTimeslice * 40)} - {currentPhase && phaseConfig && phaseConfig[currentPhase].lastBlock} - {formatNumber(coretimeInfo?.salesInfo.endPrice)} - {formatNumber(coretimeInfo?.salesInfo.selloutPrice)} -
-
- -
-
- -
-
+ + {!!available &&
+
} +
+
+ +
+ {currentPhase && currentPhase} + {currentPhase && phaseConfig && estimateTime(phaseConfig[currentPhase].lastTimeslice, coretimeInfo.status.lastTimeslice * 40)} + {currentPhase && phaseConfig?.[currentPhase].lastBlock} + {formatNumber(coretimeInfo?.salesInfo.endPrice)} + {formatNumber(coretimeInfo?.salesInfo.selloutPrice)} +
+
+ +
+
+
- ) +
+
+ ); } -export default Sale \ No newline at end of file +export default Sale; diff --git a/packages/page-coretime/src/index.tsx b/packages/page-coretime/src/index.tsx index 9415fac0cff..62269b9899e 100644 --- a/packages/page-coretime/src/index.tsx +++ b/packages/page-coretime/src/index.tsx @@ -4,21 +4,22 @@ import type { TabItem } from '@polkadot/react-components/types'; import React, { useMemo, useRef } from 'react'; +import { Route, Routes } from 'react-router-dom'; import { Tabs } from '@polkadot/react-components'; -import { useTranslation } from './translate.js'; -import { Route, Routes } from 'react-router-dom'; -import Overview from './Overview/index.js'; import { useApi, useCoretimeInformation } from '@polkadot/react-hooks'; -import Sale from './Sale/index.js'; import { CoreTimeChainConsts } from '@polkadot/react-hooks/types'; +import Overview from './Overview/index.js'; +import Sale from './Sale/index.js'; +import { useTranslation } from './translate.js'; + interface Props { basePath: string; className?: string; } -function createItemsRef(t: (key: string, options?: { replace: Record }) => string): TabItem[] { +function createItemsRef (t: (key: string, options?: { replace: Record }) => string): TabItem[] { return [ { isRoot: true, @@ -32,7 +33,7 @@ function createItemsRef(t: (key: string, options?: { replace: Record { +function CoretimeApp ({ basePath, className }: Props): React.ReactElement { const { t } = useTranslation(); const itemsRef = useRef(createItemsRef(t)); const { api, isApiReady } = useApi(); diff --git a/packages/page-coretime/src/types.ts b/packages/page-coretime/src/types.ts index 0dbd8bc25a7..d80dc66410f 100644 --- a/packages/page-coretime/src/types.ts +++ b/packages/page-coretime/src/types.ts @@ -8,14 +8,14 @@ export enum CoreTimeTypes { 'On Demand' } -export type PhaseInfo = { +export interface PhaseInfo { name: string; lastBlock: number; lastTimeslice: number -}; +} export const PhaseName = { Renewals: 'Renewals', PriceDiscovery: 'Price Discovery', FixedPrice: 'Fixed Price' -} \ No newline at end of file +}; diff --git a/packages/page-coretime/src/utils.ts b/packages/page-coretime/src/utils.ts index 6fa4fb37846..fdc9001416e 100644 --- a/packages/page-coretime/src/utils.ts +++ b/packages/page-coretime/src/utils.ts @@ -67,5 +67,5 @@ export const getOccupancyType = (lease: LegacyLease | undefined, reservation: Re }; export const getCurrentPhase = () => { - -} + +}; diff --git a/packages/react-components/src/ProgresBar.tsx b/packages/react-components/src/ProgresBar.tsx index fb6fa97bba6..f265e618aeb 100644 --- a/packages/react-components/src/ProgresBar.tsx +++ b/packages/react-components/src/ProgresBar.tsx @@ -1,4 +1,8 @@ +// [object Object] +// SPDX-License-Identifier: Apache-2.0 + import React from 'react'; + import { styled } from './styled.js'; interface Section { @@ -11,27 +15,40 @@ interface Props { sections: Section[]; } -function ProgressBar({ sections }: Props): React.ReactElement | null { +function ProgressBar ({ sections }: Props): React.ReactElement | null { const overallTotal = sections.reduce((sum, section) => sum + section.total, 0); - console.log('overall total ', overallTotal) + + console.log('overall total ', overallTotal); + return ( -
+
{sections.map((section, index) => { const sectionWidth = (section.total / overallTotal) * 100; const sectionProgress = (section.value / section.total) * 100; return ( -
-
-
+
+
+
); })}
-
+
{sections.map((section, index) => ( -
+
{section.label}
))} diff --git a/packages/react-hooks/src/useCoretimeInformation.ts b/packages/react-hooks/src/useCoretimeInformation.ts index a33c4be0ea1..c240c89c634 100644 --- a/packages/react-hooks/src/useCoretimeInformation.ts +++ b/packages/react-hooks/src/useCoretimeInformation.ts @@ -39,7 +39,8 @@ function useCoretimeInformationImpl (api: ApiPromise, ready: boolean): CoretimeI /** Other APIs */ const [blockTimeMs] = useBlockTime(BN_ONE, apiCoretime); - console.log('res ', blockTimeMs) + + console.log('res ', blockTimeMs); // const blockTimeMs = 1 const coreInfos = useCoreDescriptor(api, ready); const paraIds = useMemo(() => coreInfos && [...new Set(coreInfos?.map((a) => a.info.currentWork.assignments.map((ass) => ass.task)).flat().filter((id) => id !== 'Pool'))], [coreInfos]); From d69054c591a62a73107151a6aad67af1513a9c3b Mon Sep 17 00:00:00 2001 From: Daria Mikhailova Date: Tue, 19 Nov 2024 14:10:00 +0100 Subject: [PATCH 05/14] added historical data on sales, button to see transactions on subscan --- .../page-coretime/src/Overview/Summary.tsx | 5 +- packages/page-coretime/src/Overview/index.tsx | 4 +- packages/page-coretime/src/Sale/SaleTable.tsx | 54 ++++++++++++ packages/page-coretime/src/Sale/Summary.tsx | 16 ++-- packages/page-coretime/src/Sale/index.tsx | 86 ++++++++++++++++--- packages/page-coretime/src/index.tsx | 14 ++- packages/page-coretime/src/utils.ts | 77 ++++++++++++++++- .../react-hooks/src/useCoretimeInformation.ts | 2 - 8 files changed, 223 insertions(+), 35 deletions(-) create mode 100644 packages/page-coretime/src/Sale/SaleTable.tsx diff --git a/packages/page-coretime/src/Overview/Summary.tsx b/packages/page-coretime/src/Overview/Summary.tsx index 1b22b05824e..a0ec5daff26 100644 --- a/packages/page-coretime/src/Overview/Summary.tsx +++ b/packages/page-coretime/src/Overview/Summary.tsx @@ -7,7 +7,6 @@ import type { BrokerStatus, CoreDescription, PalletBrokerConfigRecord, PalletBro import React, { useMemo } from 'react'; import { CardSummary, SummaryBox } from '@polkadot/react-components'; -import { useCall } from '@polkadot/react-hooks'; import { BN } from '@polkadot/util'; import { useTranslation } from '../translate.js'; @@ -21,13 +20,13 @@ interface Props { region: RegionInfo[], status: BrokerStatus, parachainCount: number + chainName: string } -function Summary ({ api, config, parachainCount, saleInfo, status }: Props): React.ReactElement { +function Summary ({ api, chainName, config, parachainCount, saleInfo, status }: Props): React.ReactElement { const { t } = useTranslation(); const currentRegionEnd = saleInfo.regionEnd - config.regionLength; const currentRegionStart = saleInfo.regionEnd - config.regionLength * 2; - const chainName = useCall(api?.rpc.system.chain)?.toString().toLowerCase(); const cycleNumber = useMemo(() => chainName && currentRegionEnd && Math.floor((currentRegionEnd - FirstCycleStart[chainName]) / config.regionLength) diff --git a/packages/page-coretime/src/Overview/index.tsx b/packages/page-coretime/src/Overview/index.tsx index 3070db7c02b..525809c7737 100644 --- a/packages/page-coretime/src/Overview/index.tsx +++ b/packages/page-coretime/src/Overview/index.tsx @@ -13,9 +13,10 @@ import Summary from './Summary.js'; interface Props { className?: string; coretimeInfo: CoretimeInformation + chainName: string } -function Overview ({ className, coretimeInfo }: Props): React.ReactElement { +function Overview ({ chainName, className, coretimeInfo }: Props): React.ReactElement { const { api, isApiReady } = useApi(); return ( @@ -23,6 +24,7 @@ function Overview ({ className, coretimeInfo }: Props): React.ReactElement { + const { t } = useTranslation(); + const headerRef = useRef<([React.ReactNode?, string?, number?] | false)[]>([ + [t(`details for sale ${saleNumber}`), 'start'], + [t('start'), 'start media--800'], + [t('end'), 'start'] + ]); + + return ( + + + + + + + + + + + + + + + + + + + + + + +
Dates{saleDetails.saleStartDate}{saleDetails.saleEndDate}
Blocks(relay){saleDetails.saleStartBlock}{saleDetails.saleEndBlock}
Blocks(coretime){saleDetails.coretime.startBlock}{saleDetails.coretime.endBlock}
Timeslices(coretime){saleDetails.saleStartTimeslice}{saleDetails.saleEndTimeslice}
+ ); +} + +export default React.memo(SaleTable); diff --git a/packages/page-coretime/src/Sale/Summary.tsx b/packages/page-coretime/src/Sale/Summary.tsx index 85554e23136..814572dc6ce 100644 --- a/packages/page-coretime/src/Sale/Summary.tsx +++ b/packages/page-coretime/src/Sale/Summary.tsx @@ -4,14 +4,13 @@ import type { ApiPromise } from '@polkadot/api'; import type { BrokerStatus, CoreDescription, PalletBrokerConfigRecord, PalletBrokerSaleInfoRecord, RegionInfo } from '@polkadot/react-hooks/types'; -import React, { useMemo } from 'react'; +import React from 'react'; import { CardSummary, SummaryBox } from '@polkadot/react-components'; -import { useCall } from '@polkadot/react-hooks'; import { BN } from '@polkadot/util'; import { useTranslation } from '../translate.js'; -import { estimateTime, FirstCycleStart } from '../utils.js'; +import { estimateTime, getRegionStartEndTs } from '../utils.js'; interface Props { api: ApiPromise | null, @@ -20,17 +19,12 @@ interface Props { config: PalletBrokerConfigRecord, region: RegionInfo[], status: BrokerStatus, + cycleNumber: number } -function Summary ({ api, config, saleInfo, status }: Props): React.ReactElement { +function Summary ({ api, config, cycleNumber, saleInfo, status }: Props): React.ReactElement { const { t } = useTranslation(); - const currentRegionEnd = saleInfo.regionEnd - config.regionLength; - const currentRegionStart = saleInfo.regionEnd - config.regionLength * 2; - const chainName = useCall(api?.rpc.system.chain)?.toString().toLowerCase(); - - const cycleNumber = useMemo(() => - chainName && currentRegionEnd && Math.floor((currentRegionEnd - FirstCycleStart[chainName]) / config.regionLength) - , [currentRegionEnd, chainName, config]); + const { currentRegionEnd, currentRegionStart } = getRegionStartEndTs(saleInfo, config); return ( diff --git a/packages/page-coretime/src/Sale/index.tsx b/packages/page-coretime/src/Sale/index.tsx index e1504890c35..24199fc2912 100644 --- a/packages/page-coretime/src/Sale/index.tsx +++ b/packages/page-coretime/src/Sale/index.tsx @@ -3,9 +3,9 @@ import type { CoretimeInformation } from '@polkadot/react-hooks/types'; -import { useMemo } from 'react'; +import { useCallback, useMemo, useState } from 'react'; -import { Button, CardSummary, SummaryBox } from '@polkadot/react-components'; +import { Button, CardSummary, Dropdown, Input, SummaryBox, Table } from '@polkadot/react-components'; import ProgresBar from '@polkadot/react-components/ProgresBar'; import { useApi } from '@polkadot/react-hooks'; import { CoreTimeChainConsts } from '@polkadot/react-hooks/types'; @@ -13,23 +13,32 @@ import { formatNumber } from '@polkadot/util'; import { useTranslation } from '../translate.js'; import { PhaseName } from '../types.js'; -import { estimateTime } from '../utils.js'; +import { calculateSaleDetails, constructSubscanQuery, estimateTime, getCurrentSaleNumber, getRegionStartEndTs } from '../utils.js'; +import SaleTable from './SaleTable.js'; import Summary from './Summary.js'; interface Props { coretimeInfo: CoretimeInformation + chainName: string } -function Sale ({ coretimeInfo }: Props): React.ReactElement { +function Sale ({ chainName, coretimeInfo }: Props): React.ReactElement { const { api, isApiReady } = useApi(); const { t } = useTranslation(); - const { config: { interludeLength, leadinLength, regionLength }, + const [saleNumber, setSaleNumber] = useState(-1); + const [saleDetails, setSaleDetails] = useState(); + + const { config: { interludeLength, leadinLength }, salesInfo: { regionBegin }, status: { lastCommittedTimeslice } } = coretimeInfo; const interludeLengthTs = interludeLength / CoreTimeChainConsts.BlocksPerTimeslice; const leadInLengthTs = leadinLength / CoreTimeChainConsts.BlocksPerTimeslice; - const currentRegionStart = regionBegin - regionLength; + const { currentRegionEnd, currentRegionStart } = getRegionStartEndTs(coretimeInfo.salesInfo, coretimeInfo.config); + + const cycleNumber = useMemo(() => + chainName && getCurrentSaleNumber(currentRegionEnd, chainName, coretimeInfo.config) + , [currentRegionEnd, chainName, coretimeInfo.config]); const phaseConfig = useMemo(() => { if (!coretimeInfo?.blockTimeMs) { @@ -66,8 +75,6 @@ function Sale ({ coretimeInfo }: Props): React.ReactElement { return PhaseName.FixedPrice; }, [interludeLengthTs, leadInLengthTs]); - console.log('currentPhase ', currentPhase); - const progressValues = useMemo(() => { const progress = lastCommittedTimeslice - currentRegionStart; @@ -90,8 +97,15 @@ function Sale ({ coretimeInfo }: Props): React.ReactElement { ]; }, [interludeLengthTs, leadInLengthTs, lastCommittedTimeslice, currentRegionStart, regionBegin]); - // const available = Number(coretimeInfo?.salesInfo?.coresOffered) - Number(coretimeInfo?.salesInfo.coresSold) - const available = 1; + const available = Number(coretimeInfo?.salesInfo?.coresOffered) - Number(coretimeInfo?.salesInfo.coresSold); + + const onDropDownChange = useCallback((v: number) => { + console.log(v); + setSaleNumber(v); + const details = calculateSaleDetails(v, cycleNumber, currentRegionStart, coretimeInfo.status.lastTimeslice * 80, chainName); + + setSaleDetails(details); + }, []); return (
@@ -99,11 +113,12 @@ function Sale ({ coretimeInfo }: Props): React.ReactElement { } -
+
@@ -125,7 +140,7 @@ function Sale ({ coretimeInfo }: Props): React.ReactElement {
{currentPhase && currentPhase} - {currentPhase && phaseConfig && estimateTime(phaseConfig[currentPhase].lastTimeslice, coretimeInfo.status.lastTimeslice * 40)} + {currentPhase && phaseConfig && estimateTime(phaseConfig[currentPhase].lastTimeslice, coretimeInfo.status.lastTimeslice * 80)} {currentPhase && phaseConfig?.[currentPhase].lastBlock} {formatNumber(coretimeInfo?.salesInfo.endPrice)} {formatNumber(coretimeInfo?.salesInfo.selloutPrice)} @@ -136,6 +151,53 @@ function Sale ({ coretimeInfo }: Props): React.ReactElement {
+
+

Historical data

+
+
+ + ({ + text: `sale #${i}`, + value: i + })).reverse() + ]} + value={-1} + /> + +
+ {saleNumber > -1 && !!saleDetails &&
+ + +
} + {saleNumber > -1 && !!saleDetails &&
+
+ + } +
+
); diff --git a/packages/page-coretime/src/index.tsx b/packages/page-coretime/src/index.tsx index 62269b9899e..de71deaccc0 100644 --- a/packages/page-coretime/src/index.tsx +++ b/packages/page-coretime/src/index.tsx @@ -7,8 +7,7 @@ import React, { useMemo, useRef } from 'react'; import { Route, Routes } from 'react-router-dom'; import { Tabs } from '@polkadot/react-components'; -import { useApi, useCoretimeInformation } from '@polkadot/react-hooks'; -import { CoreTimeChainConsts } from '@polkadot/react-hooks/types'; +import { useApi, useCall, useCoretimeInformation } from '@polkadot/react-hooks'; import Overview from './Overview/index.js'; import Sale from './Sale/index.js'; @@ -38,6 +37,7 @@ function CoretimeApp ({ basePath, className }: Props): React.ReactElement const itemsRef = useRef(createItemsRef(t)); const { api, isApiReady } = useApi(); const coretimeInfo = useCoretimeInformation(api, isApiReady); + const chainName = useCall(api?.rpc.system.chain)?.toString().toLowerCase(); return (
@@ -49,13 +49,19 @@ function CoretimeApp ({ basePath, className }: Props): React.ReactElement + coretimeInfo && } index /> + coretimeInfo && } path='sale' /> diff --git a/packages/page-coretime/src/utils.ts b/packages/page-coretime/src/utils.ts index fdc9001416e..b293d1733e6 100644 --- a/packages/page-coretime/src/utils.ts +++ b/packages/page-coretime/src/utils.ts @@ -1,7 +1,7 @@ // Copyright 2017-2024 @polkadot/app-coretime authors & contributors // SPDX-License-Identifier: Apache-2.0 -import { CoreTimeConsts, type LegacyLease, type Reservation } from '@polkadot/react-hooks/types'; +import { CoreTimeChainConsts, CoreTimeConsts, type LegacyLease, type Reservation } from '@polkadot/react-hooks/types'; import { BN } from '@polkadot/util'; import { CoreTimeTypes } from './types.js'; @@ -66,6 +66,79 @@ export const getOccupancyType = (lease: LegacyLease | undefined, reservation: Re return reservation ? CoreTimeTypes.Reservation : lease ? CoreTimeTypes.Lease : CoreTimeTypes['Bulk Coretime']; }; -export const getCurrentPhase = () => { +export function calculateSaleDetails (saleNumber: number, currentSaleNumber: number, currentRegionStart: number, latestBlock: number, chainName: string) { + // @todo get that from the chain! + const blocksPerTs = 80; + const blocksPerSaleRelay = blocksPerTs * 5040; + + console.log('blocksPerSale:', blocksPerSaleRelay); + + const currentSaleStartBlock = currentRegionStart * blocksPerTs; + + console.log('currentSaleStartBlock:', currentSaleStartBlock); + + const saleStartBlock = currentSaleStartBlock - blocksPerSaleRelay * (currentSaleNumber - saleNumber); + + // CORETIME + const saleStartBlockCoretime = FirstCycleStart[chainName] + (saleNumber) * CoreTimeChainConsts.BlocksPerTimeslice; + const saleEndBlockCoretime = saleStartBlockCoretime + 5040 * CoreTimeChainConsts.BlocksPerTimeslice; + + console.log('saleStartBlock:', saleStartBlock); + + const saleStartTimeslice = saleStartBlock / blocksPerTs; + + console.log('saleStartTimeslice:', saleStartTimeslice); + + const saleEndBlock = saleStartBlock + blocksPerSaleRelay; + + console.log('saleEndBlock:', saleEndBlock); + + // Calculate timeslices in the sale period + const timeslicesPerSale = blocksPerSaleRelay / CoreTimeChainConsts.BlocksPerTimeslice; + + console.log('timeslicesPerSale:', timeslicesPerSale); + + // Convert block numbers to approximate dates (optional) + const saleStartDate = estimateTime(saleStartTimeslice, latestBlock); + + console.log('saleStartDate:', saleStartDate); + + const saleEndDate = estimateTime(saleStartTimeslice + 5040, latestBlock); + + console.log('saleEndDate:', saleEndDate); + + // Output results + return { + saleStartBlock, + saleEndBlock, + timeslicesPerSale, + saleStartDate, + saleEndDate, + saleStartTimeslice, + saleEndTimeslice: saleStartTimeslice + 5040, + coretime: { + startBlock: saleStartBlockCoretime, + endBlock: saleEndBlockCoretime + } + }; +} + +export const constructSubscanQuery = (blockStart: number, blockEnd: number, module = 'broker', call = 'purchase') => { + const page = 1; + const pageSize = 25; + const signed = 'all'; + const baseURL = 'https://coretime-polkadot.subscan.io/extrinsic'; + + return `${baseURL}?page=${page}&time_dimension=block&page_size=${pageSize}&module=${module}&signed=${signed}&call=${call}&block_start=${blockStart}&block_end=${blockEnd}`; +}; + +export const getCurrentSaleNumber = (currentRegionEnd, chainName: string, config) => + chainName && currentRegionEnd && Math.floor((currentRegionEnd - FirstCycleStart[chainName]) / config.regionLength); + +export const getRegionStartEndTs = (saleInfo, config) => { + return { + currentRegionEnd: saleInfo.regionEnd - config.regionLength, + currentRegionStart: saleInfo.regionEnd - config.regionLength * 2 + }; }; diff --git a/packages/react-hooks/src/useCoretimeInformation.ts b/packages/react-hooks/src/useCoretimeInformation.ts index c240c89c634..2b1ae49a7c0 100644 --- a/packages/react-hooks/src/useCoretimeInformation.ts +++ b/packages/react-hooks/src/useCoretimeInformation.ts @@ -40,8 +40,6 @@ function useCoretimeInformationImpl (api: ApiPromise, ready: boolean): CoretimeI /** Other APIs */ const [blockTimeMs] = useBlockTime(BN_ONE, apiCoretime); - console.log('res ', blockTimeMs); - // const blockTimeMs = 1 const coreInfos = useCoreDescriptor(api, ready); const paraIds = useMemo(() => coreInfos && [...new Set(coreInfos?.map((a) => a.info.currentWork.assignments.map((ass) => ass.task)).flat().filter((id) => id !== 'Pool'))], [coreInfos]); From 2ca3e3744f6a14e4f2202b714a22c5b4532d5755 Mon Sep 17 00:00:00 2001 From: Daria Mikhailova Date: Tue, 10 Dec 2024 22:00:42 +0800 Subject: [PATCH 06/14] breaking down utils file logically --- .../page-coretime/src/Overview/Summary.tsx | 7 +- packages/page-coretime/src/Row.tsx | 4 +- packages/page-coretime/src/utils.ts | 267 ++++++++++-------- packages/page-coretime/src/utils/index.ts | 120 ++++++++ packages/page-coretime/src/utils/sale.ts | 2 + 5 files changed, 276 insertions(+), 124 deletions(-) create mode 100644 packages/page-coretime/src/utils/index.ts create mode 100644 packages/page-coretime/src/utils/sale.ts diff --git a/packages/page-coretime/src/Overview/Summary.tsx b/packages/page-coretime/src/Overview/Summary.tsx index a0ec5daff26..6cb6f888ccf 100644 --- a/packages/page-coretime/src/Overview/Summary.tsx +++ b/packages/page-coretime/src/Overview/Summary.tsx @@ -10,7 +10,8 @@ import { CardSummary, SummaryBox } from '@polkadot/react-components'; import { BN } from '@polkadot/util'; import { useTranslation } from '../translate.js'; -import { estimateTime, FirstCycleStart } from '../utils.js'; +import { estimateTime } from '../utils/index.js'; +import { FirstCycleStart } from '../utils/index.js'; interface Props { api: ApiPromise | null, @@ -23,14 +24,14 @@ interface Props { chainName: string } -function Summary ({ api, chainName, config, parachainCount, saleInfo, status }: Props): React.ReactElement { +function Summary({ api, chainName, config, parachainCount, saleInfo, status }: Props): React.ReactElement { const { t } = useTranslation(); const currentRegionEnd = saleInfo.regionEnd - config.regionLength; const currentRegionStart = saleInfo.regionEnd - config.regionLength * 2; const cycleNumber = useMemo(() => chainName && currentRegionEnd && Math.floor((currentRegionEnd - FirstCycleStart[chainName]) / config.regionLength) - , [currentRegionEnd, chainName, config]); + , [currentRegionEnd, chainName, config]); return ( diff --git a/packages/page-coretime/src/Row.tsx b/packages/page-coretime/src/Row.tsx index b032ecb508f..a7d881c117c 100644 --- a/packages/page-coretime/src/Row.tsx +++ b/packages/page-coretime/src/Row.tsx @@ -11,7 +11,7 @@ import { ChainRenewalStatus } from '@polkadot/react-hooks/types'; import { BN, formatBalance, formatNumber } from '@polkadot/util'; import { CoreTimeTypes } from './types.js'; -import { estimateTime } from './utils.js'; +import { estimateTime } from './utils/index.js'; interface Props { id: number @@ -35,7 +35,7 @@ const StyledCell = styled.td<{ $p: boolean }>` } `; -function Row ({ chainRecord, highlight = false, id, lastCommittedTimeslice, lease, regionBegin, regionEnd }: Props): React.ReactElement { +function Row({ chainRecord, highlight = false, id, lastCommittedTimeslice, lease, regionBegin, regionEnd }: Props): React.ReactElement { const chainRegionEnd = (chainRecord.renewalStatus === ChainRenewalStatus.Renewed ? regionEnd : regionBegin); const targetTimeslice = lease?.until || chainRegionEnd; const showEstimates = !!targetTimeslice && Object.values(CoreTimeTypes)[chainRecord.type] !== CoreTimeTypes.Reservation; diff --git a/packages/page-coretime/src/utils.ts b/packages/page-coretime/src/utils.ts index b293d1733e6..484a83ca53e 100644 --- a/packages/page-coretime/src/utils.ts +++ b/packages/page-coretime/src/utils.ts @@ -1,144 +1,173 @@ // Copyright 2017-2024 @polkadot/app-coretime authors & contributors // SPDX-License-Identifier: Apache-2.0 -import { CoreTimeChainConsts, CoreTimeConsts, type LegacyLease, type Reservation } from '@polkadot/react-hooks/types'; -import { BN } from '@polkadot/util'; - -import { CoreTimeTypes } from './types.js'; - -export const FirstCycleStart: Record = { - kusama: 285768, - polkadot: 282525 -}; - -function formatDate (date: Date) { - const day = date.getDate(); - const month = date.toLocaleString('default', { month: 'short' }); - const year = date.getFullYear(); - - return `${day} ${month} ${year}`; -} - -/** - * blockTime = 6000 ms - * BlocksPerTimeslice = 80 - * Default Regoin = 5040 timeslices - * TargetBlock = TargetTimeslice * BlocksPerTimeslice - * Block Time Difference = |TargetBlock - latest Block| * blockTime - * - * Estimate timestamp = - * if targetBlock is before the latestBlock - * now minus block time difference - * else - * now plus block time difference - */ -export const estimateTime = (targetTimeslice: string | number, latestBlock: number): string | null => { - if (!latestBlock || !targetTimeslice) { - console.error('Invalid input: one or more inputs are missing'); - - return null; - } - - const now = new Date().getTime(); - - try { - const blockTime = new BN(CoreTimeConsts.BlockTime); // Average block time in milliseconds (6 seconds) - const timeSlice = new BN(CoreTimeConsts.BlocksPerTimeslice); - const latestBlockBN = new BN(latestBlock); - const timestampBN = new BN(now); - const targetBlockBN = new BN(targetTimeslice).mul(timeSlice); - const blockTimeDifference = targetBlockBN.sub(latestBlockBN).mul(blockTime); - const estTimestamp = timestampBN.add(blockTimeDifference); - - return formatDate(new Date(estTimestamp.toNumber())); - } catch (error) { - console.error('Error in calculation:', error); - - return null; - } -}; - -export const getOccupancyType = (lease: LegacyLease | undefined, reservation: Reservation | undefined, isPool: boolean): CoreTimeTypes => { - if (isPool) { - return CoreTimeTypes['On Demand']; - } - - return reservation ? CoreTimeTypes.Reservation : lease ? CoreTimeTypes.Lease : CoreTimeTypes['Bulk Coretime']; -}; - -export function calculateSaleDetails (saleNumber: number, currentSaleNumber: number, currentRegionStart: number, latestBlock: number, chainName: string) { - // @todo get that from the chain! - const blocksPerTs = 80; - - const blocksPerSaleRelay = blocksPerTs * 5040; - - console.log('blocksPerSale:', blocksPerSaleRelay); - - const currentSaleStartBlock = currentRegionStart * blocksPerTs; - - console.log('currentSaleStartBlock:', currentSaleStartBlock); - - const saleStartBlock = currentSaleStartBlock - blocksPerSaleRelay * (currentSaleNumber - saleNumber); - - // CORETIME - const saleStartBlockCoretime = FirstCycleStart[chainName] + (saleNumber) * CoreTimeChainConsts.BlocksPerTimeslice; - const saleEndBlockCoretime = saleStartBlockCoretime + 5040 * CoreTimeChainConsts.BlocksPerTimeslice; - - console.log('saleStartBlock:', saleStartBlock); - - const saleStartTimeslice = saleStartBlock / blocksPerTs; - - console.log('saleStartTimeslice:', saleStartTimeslice); +import { PhaseConfig, PhaseName, PhaseProgress, RegionInfo, SaleDetails } from './types.js'; +import { estimateTime, FirstCycleStart, get, getCurrentSaleNumber, getCurrentRegionStartEndTs } from './utils/index.js'; + +export function calculateSaleDetails( + saleNumber: number, + currentSaleNumber: number, + latestBlock: number, + chainName: string, + regionLength: number, + saleParams: any +): SaleDetails { + const blocksPerSaleRelay = get.blocks.relay(regionLength); + const saleStartBlock = saleParams.currentRegion.start.blocks - blocksPerSaleRelay * (currentSaleNumber - saleNumber); const saleEndBlock = saleStartBlock + blocksPerSaleRelay; - console.log('saleEndBlock:', saleEndBlock); - - // Calculate timeslices in the sale period - const timeslicesPerSale = blocksPerSaleRelay / CoreTimeChainConsts.BlocksPerTimeslice; - console.log('timeslicesPerSale:', timeslicesPerSale); + const saleStartTs = get.timeslices.relay(saleStartBlock); + const saleEndTs = get.timeslices.relay(saleEndBlock); - // Convert block numbers to approximate dates (optional) - const saleStartDate = estimateTime(saleStartTimeslice, latestBlock); - console.log('saleStartDate:', saleStartDate); + const saleStartBlockCoretime = FirstCycleStart[chainName] + get.blocks.coretime((saleNumber) * regionLength) + const saleEndBlockCoretime = saleStartBlockCoretime + get.blocks.coretime(regionLength) - const saleEndDate = estimateTime(saleStartTimeslice + 5040, latestBlock); - - console.log('saleEndDate:', saleEndDate); - - // Output results - return { - saleStartBlock, - saleEndBlock, - timeslicesPerSale, - saleStartDate, - saleEndDate, - saleStartTimeslice, - saleEndTimeslice: saleStartTimeslice + 5040, + const data = { + saleNumber, + relay: { + start: { + block: saleStartBlock, + ts: saleStartTs, + }, + end: { + block: saleEndBlock, + ts: saleEndTs, + }, + }, coretime: { - startBlock: saleStartBlockCoretime, - endBlock: saleEndBlockCoretime + start: { block: saleStartBlockCoretime }, + end: { block: saleEndBlockCoretime }, + }, + date: { + start: estimateTime(saleStartTs, latestBlock), + end: estimateTime(saleEndTs, latestBlock), } - }; + } + return data; } -export const constructSubscanQuery = (blockStart: number, blockEnd: number, module = 'broker', call = 'purchase') => { +export const constructSubscanQuery = (blockStart: number, blockEnd: number,chainName: string, module = 'broker', call = 'purchase', ) => { const page = 1; const pageSize = 25; const signed = 'all'; - const baseURL = 'https://coretime-polkadot.subscan.io/extrinsic'; + const baseURL = `https://coretime-${chainName}.subscan.io/extrinsic`; return `${baseURL}?page=${page}&time_dimension=block&page_size=${pageSize}&module=${module}&signed=${signed}&call=${call}&block_start=${blockStart}&block_end=${blockEnd}`; }; -export const getCurrentSaleNumber = (currentRegionEnd, chainName: string, config) => - chainName && currentRegionEnd && Math.floor((currentRegionEnd - FirstCycleStart[chainName]) / config.regionLength); +export const getSaleParameters = ( + salesInfo: RegionInfo, + config: { interludeLength: number; leadinLength: number; regionLength: number }, + chainName: string, + lastCommittedTimeslice: number) => { + // The sale is happening on the coretime chain, so we need to convert the timeslices to blocks (40 blocks per timeslice) + const interludeLengthTs = get.timeslices.coretime(config.interludeLength); + const leadInLengthTs = get.timeslices.coretime(config.leadinLength); + + const { currentRegionEnd, currentRegionStart } = getCurrentRegionStartEndTs(salesInfo, config); + const phaseConfig = getPhaseConfiguration(currentRegionStart, config.regionLength, interludeLengthTs, leadInLengthTs, lastCommittedTimeslice); -export const getRegionStartEndTs = (saleInfo, config) => { + const saleNumber = getCurrentSaleNumber(currentRegionEnd, chainName, config); return { - currentRegionEnd: saleInfo.regionEnd - config.regionLength, - currentRegionStart: saleInfo.regionEnd - config.regionLength * 2 + cycleNumber: getCurrentSaleNumber(currentRegionEnd, chainName, config), + regionNumber: saleNumber - 1, + leadin: { + ts: leadInLengthTs, + blocks: config.leadinLength + }, + interlude: { + ts: interludeLengthTs, + blocks: config.interludeLength + }, + currentRegion: { + start: { + ts: currentRegionStart, + blocks: get.blocks.relay(currentRegionStart) + }, + end: { + ts: currentRegionEnd, + blocks: get.blocks.relay(currentRegionEnd) + } + }, + phaseConfig }; }; + +export const getPhaseConfiguration = ( + currentRegionStart: number, + regionLength: number, + interludeLengthTs: number, + leadInLengthTs: number, + lastCommittedTimeslice: number): PhaseConfig | undefined => { + + const renewalsEndTs = currentRegionStart + interludeLengthTs + const priceDiscoveryEndTs = renewalsEndTs + leadInLengthTs + const fixedPriceLenght = regionLength - interludeLengthTs - leadInLengthTs + const fixedPriceEndTs = priceDiscoveryEndTs + fixedPriceLenght + + return { + currentPhaseName: determinePhaseName(lastCommittedTimeslice, currentRegionStart, interludeLengthTs, leadInLengthTs), + config: { + [PhaseName.Renewals]: { + lastTimeslice: renewalsEndTs, + lastBlock: get.blocks.relay(renewalsEndTs) + }, + [PhaseName.PriceDiscovery]: { + lastTimeslice: priceDiscoveryEndTs, + lastBlock: get.blocks.relay(priceDiscoveryEndTs) + }, + [PhaseName.FixedPrice]: { + lastTimeslice: fixedPriceEndTs, + lastBlock: get.blocks.relay(fixedPriceEndTs) + } + } + } +}; + +export const determinePhaseName = ( + lastCommittedTimeslice: number, + currentRegionStart: number, + interludeLengthTs: number, + leadInLengthTs: number): typeof PhaseName[keyof typeof PhaseName] => { + const progress = lastCommittedTimeslice - currentRegionStart; + + if (progress < interludeLengthTs) { + return PhaseName.Renewals; + } + + if (progress < interludeLengthTs + leadInLengthTs) { + return PhaseName.PriceDiscovery; + } + + return PhaseName.FixedPrice; +} + +export const getSaleProgress = ( + lastCommittedTimeslice: number, + currentRegionStart: number, + interludeLengthTs: number, + leadInLengthTs: number, + regionBegin: number): PhaseProgress[] => { + const progress = lastCommittedTimeslice - currentRegionStart; + + return [ + { + value: Math.min(progress, interludeLengthTs), + total: interludeLengthTs, + label: PhaseName.Renewals + }, + { + value: Math.min(Math.max(progress - interludeLengthTs, 0), leadInLengthTs), + total: leadInLengthTs, + label: PhaseName.PriceDiscovery + }, + { + value: Math.max(progress - interludeLengthTs - leadInLengthTs, 0), + total: regionBegin - currentRegionStart - interludeLengthTs - leadInLengthTs, + label: PhaseName.FixedPrice + } + ]; +} \ No newline at end of file diff --git a/packages/page-coretime/src/utils/index.ts b/packages/page-coretime/src/utils/index.ts new file mode 100644 index 00000000000..890470fbd2b --- /dev/null +++ b/packages/page-coretime/src/utils/index.ts @@ -0,0 +1,120 @@ +// Copyright 2017-2024 @polkadot/app-coretime authors & contributors +// SPDX-License-Identifier: Apache-2.0 + +import { BN } from '@polkadot/util'; +import { CoreTimeChainConsts, CoreTimeConsts, CoretimeInformation, PalletBrokerConfigRecord } from "@polkadot/react-hooks/types"; +import { RegionInfo } from '../types.js'; + +// Blocks on the Coretime Chain +export const FirstCycleStart: Record = { + kusama: 285768, + polkadot: 282525 + }; + + + +export function formatDate(date: Date) { + const day = date.getDate(); + const month = date.toLocaleString('default', { month: 'short' }); + const year = date.getFullYear(); + + return `${day} ${month} ${year}`; +} + +/** + * Gives you a date for the traget timeslice + * + * Relay chain info: + * blockTime = 6000 ms + * BlocksPerTimeslice = 80 + * Default Regoin = 5040 timeslices + * + * Calculation: + * TargetBlock = TargetTimeslice * BlocksPerTimeslice + * Block Time Difference = |TargetBlock - latest Block| * blockTime + * + * Estimate timestamp = + * if targetBlock is before the latestBlock + * now minus block time difference + * else + * now plus block time difference + */ +export const estimateTime = (targetTimeslice: string | number, latestBlock: number): string | null => { + if (!latestBlock || !targetTimeslice) { + console.error('Invalid input: one or more inputs are missing'); + return null; + } + + try { + const now = new BN(Date.now()); + const blockTime = new BN(CoreTimeConsts.BlockTime); // Average block time in milliseconds (6 seconds) + const blocksPerTimeslice = new BN(CoreTimeConsts.BlocksPerTimeslice); + const targetBlock = new BN(Number(targetTimeslice)).mul(blocksPerTimeslice); + const latestBlockBN = new BN(latestBlock); + const blockDifference = targetBlock.sub(latestBlockBN); + const timeDifference = blockDifference.mul(blockTime); + const estTimestamp = now.add(timeDifference); + + return formatDate(new Date(estTimestamp.toNumber())); + } catch (error) { + console.error('Error in calculation:', error); + return null; + } +}; + + +/** + * Get the current sale number + * + * @param currentRegionEnd - The end of the current region + * @param chainName - The name of the chain + * @param config - broker.configuration call response + * + * @returns The current sale number + */ +export const getCurrentSaleNumber = (currentRegionEnd: number, chainName: string, config: any) => + chainName && currentRegionEnd && Math.floor((currentRegionEnd - FirstCycleStart[chainName]) / config.regionLength); + + +/** + * Helper functions to convert timeslices to blocks and vice versa + */ +export const get = { + blocks: { + coretime: (ts: number) => { + return ts * CoreTimeChainConsts.BlocksPerTimeslice; + }, + relay: (ts: number) => { + return ts * CoreTimeConsts.BlocksPerTimeslice; + } + }, + timeslices: { + coretime: (blocks: number) => { + return blocks / CoreTimeChainConsts.BlocksPerTimeslice; + }, + relay: (blocks: number) => { + return blocks / CoreTimeConsts.BlocksPerTimeslice; + } + } +} + +/** + * Get the start and end of the current region + * broker.saleInfo call returns the start/end of the next region always + * + * The end of the current region is the start of the next region, which is returned by broker.saleInfo call + * + * @param saleInfo - The sale information + * @param config - The broker configuration + * + * @returns The start and end of the current region + */ +export const getCurrentRegionStartEndTs = (saleInfo: RegionInfo, config: PalletBrokerConfigRecord) => { + return { + currentRegionEnd: saleInfo.regionBegin, + currentRegionStart: saleInfo.regionBegin - config.regionLength + }; +} + +export const getAvailableNumberOfCores = (coretimeInfo: CoretimeInformation) => + Number(coretimeInfo?.salesInfo?.coresOffered) - Number(coretimeInfo?.salesInfo.coresSold); diff --git a/packages/page-coretime/src/utils/sale.ts b/packages/page-coretime/src/utils/sale.ts new file mode 100644 index 00000000000..a9ed33ff673 --- /dev/null +++ b/packages/page-coretime/src/utils/sale.ts @@ -0,0 +1,2 @@ +// Copyright 2017-2024 @polkadot/app-coretime authors & contributors +// SPDX-License-Identifier: Apache-2.0 \ No newline at end of file From fba3d708a655af2b367cdf96221b41ad4b175ecc Mon Sep 17 00:00:00 2001 From: Daria Mikhailova Date: Tue, 10 Dec 2024 22:01:11 +0800 Subject: [PATCH 07/14] improving calculations of phase configuration, sale details, etc --- packages/page-coretime/src/Sale/SaleTable.tsx | 38 +-- packages/page-coretime/src/Sale/Summary.tsx | 7 +- packages/page-coretime/src/Sale/index.tsx | 302 +++++++----------- packages/page-coretime/src/index.tsx | 6 +- packages/page-coretime/src/types.ts | 44 ++- 5 files changed, 188 insertions(+), 209 deletions(-) diff --git a/packages/page-coretime/src/Sale/SaleTable.tsx b/packages/page-coretime/src/Sale/SaleTable.tsx index 28cb5a05ccb..38a9f1d46c0 100644 --- a/packages/page-coretime/src/Sale/SaleTable.tsx +++ b/packages/page-coretime/src/Sale/SaleTable.tsx @@ -9,15 +9,16 @@ import { useTranslation } from '../translate.js'; interface Props { saleDetails: any - saleNumber: number } -function SaleTable ({ saleDetails, saleNumber }: Props): React.ReactElement { +function SaleTable({ saleDetails }: Props): React.ReactElement { const { t } = useTranslation(); const headerRef = useRef<([React.ReactNode?, string?, number?] | false)[]>([ - [t(`details for sale ${saleNumber}`), 'start'], - [t('start'), 'start media--800'], - [t('end'), 'start'] + [t(`details for sale ${saleDetails.saleNumber}`), 'start'], + [t('Dates'), 'start media--800'], + [t('Blocks (relay)'), 'start'], + [t('Blocks(coretime)'), 'start'], + [t('Timeslices'), 'start'], ]); return ( @@ -27,26 +28,21 @@ function SaleTable ({ saleDetails, saleNumber }: Props): React.ReactElement - Dates - {saleDetails.saleStartDate} - {saleDetails.saleEndDate} + Start + {saleDetails.date.start} + {saleDetails.relay.start.block} + {saleDetails.coretime.start.block} + {saleDetails.relay.start.ts} - Blocks(relay) - {saleDetails.saleStartBlock} - {saleDetails.saleEndBlock} - - - Blocks(coretime) - {saleDetails.coretime.startBlock} - {saleDetails.coretime.endBlock} - - - Timeslices(coretime) - {saleDetails.saleStartTimeslice} - {saleDetails.saleEndTimeslice} + End + {saleDetails.date.end} + {saleDetails.relay.end.block} + {saleDetails.coretime.end.block} + {saleDetails.relay.end.ts} + ); } diff --git a/packages/page-coretime/src/Sale/Summary.tsx b/packages/page-coretime/src/Sale/Summary.tsx index 814572dc6ce..b42eab5ac34 100644 --- a/packages/page-coretime/src/Sale/Summary.tsx +++ b/packages/page-coretime/src/Sale/Summary.tsx @@ -10,7 +10,8 @@ import { CardSummary, SummaryBox } from '@polkadot/react-components'; import { BN } from '@polkadot/util'; import { useTranslation } from '../translate.js'; -import { estimateTime, getRegionStartEndTs } from '../utils.js'; +import { getCurrentRegionStartEndTs } from '../utils/index.js'; +import { estimateTime } from '../utils/index.js'; interface Props { api: ApiPromise | null, @@ -22,9 +23,9 @@ interface Props { cycleNumber: number } -function Summary ({ api, config, cycleNumber, saleInfo, status }: Props): React.ReactElement { +function Summary({ api, config, cycleNumber, saleInfo, status }: Props): React.ReactElement { const { t } = useTranslation(); - const { currentRegionEnd, currentRegionStart } = getRegionStartEndTs(saleInfo, config); + const { currentRegionEnd, currentRegionStart } = getCurrentRegionStartEndTs(saleInfo, config); return ( diff --git a/packages/page-coretime/src/Sale/index.tsx b/packages/page-coretime/src/Sale/index.tsx index 24199fc2912..23aae172b0f 100644 --- a/packages/page-coretime/src/Sale/index.tsx +++ b/packages/page-coretime/src/Sale/index.tsx @@ -1,206 +1,146 @@ -// [object Object] +// Copyright 2017-2024 @polkadot/app-coretime authors & contributors // SPDX-License-Identifier: Apache-2.0 import type { CoretimeInformation } from '@polkadot/react-hooks/types'; -import { useCallback, useMemo, useState } from 'react'; +import { useMemo, useState } from 'react'; -import { Button, CardSummary, Dropdown, Input, SummaryBox, Table } from '@polkadot/react-components'; +import { Button, CardSummary, Dropdown, SummaryBox } from '@polkadot/react-components'; import ProgresBar from '@polkadot/react-components/ProgresBar'; import { useApi } from '@polkadot/react-hooks'; -import { CoreTimeChainConsts } from '@polkadot/react-hooks/types'; import { formatNumber } from '@polkadot/util'; import { useTranslation } from '../translate.js'; -import { PhaseName } from '../types.js'; -import { calculateSaleDetails, constructSubscanQuery, estimateTime, getCurrentSaleNumber, getRegionStartEndTs } from '../utils.js'; +import { calculateSaleDetails, constructSubscanQuery, getSaleParameters, getSaleProgress } from '../utils.js'; + +import { estimateTime, getAvailableNumberOfCores } from '../utils/index.js'; import SaleTable from './SaleTable.js'; import Summary from './Summary.js'; interface Props { - coretimeInfo: CoretimeInformation - chainName: string + coretimeInfo: CoretimeInformation + chainName: string } -function Sale ({ chainName, coretimeInfo }: Props): React.ReactElement { - const { api, isApiReady } = useApi(); - const { t } = useTranslation(); - const [saleNumber, setSaleNumber] = useState(-1); - const [saleDetails, setSaleDetails] = useState(); - - const { config: { interludeLength, leadinLength }, - salesInfo: { regionBegin }, - status: { lastCommittedTimeslice } } = coretimeInfo; - - const interludeLengthTs = interludeLength / CoreTimeChainConsts.BlocksPerTimeslice; - const leadInLengthTs = leadinLength / CoreTimeChainConsts.BlocksPerTimeslice; - const { currentRegionEnd, currentRegionStart } = getRegionStartEndTs(coretimeInfo.salesInfo, coretimeInfo.config); - - const cycleNumber = useMemo(() => - chainName && getCurrentSaleNumber(currentRegionEnd, chainName, coretimeInfo.config) - , [currentRegionEnd, chainName, coretimeInfo.config]); - - const phaseConfig = useMemo(() => { - if (!coretimeInfo?.blockTimeMs) { - return undefined; - } - - return { - [PhaseName.Renewals]: { - lastTimeslice: currentRegionStart + interludeLengthTs, - lastBlock: (currentRegionStart + interludeLengthTs) * 80 - }, - [PhaseName.PriceDiscovery]: { - lastTimeslice: currentRegionStart + interludeLengthTs + leadInLengthTs, - lastBlock: (currentRegionStart + interludeLengthTs + leadInLengthTs) * 80 - }, - [PhaseName.FixedPrice]: { - lastTimeslice: regionBegin, - lastBlock: regionBegin * 80 - } - }; - }, [coretimeInfo]); - - const currentPhase = useMemo(() => { - const progress = lastCommittedTimeslice - currentRegionStart; - - if (progress < interludeLengthTs) { - return PhaseName.Renewals; - } - - if (progress < interludeLengthTs + leadInLengthTs) { - return PhaseName.PriceDiscovery; - } - - return PhaseName.FixedPrice; - }, [interludeLengthTs, leadInLengthTs]); - - const progressValues = useMemo(() => { - const progress = lastCommittedTimeslice - currentRegionStart; - - return [ - { - value: Math.min(progress, interludeLengthTs), - total: interludeLengthTs, - label: PhaseName.Renewals - }, - { - value: Math.min(Math.max(progress - interludeLengthTs, 0), leadInLengthTs), - total: leadInLengthTs, - label: PhaseName.PriceDiscovery - }, - { - value: Math.max(progress - interludeLengthTs - leadInLengthTs, 0), - total: regionBegin - currentRegionStart - interludeLengthTs - leadInLengthTs, - label: PhaseName.FixedPrice - } - ]; - }, [interludeLengthTs, leadInLengthTs, lastCommittedTimeslice, currentRegionStart, regionBegin]); - - const available = Number(coretimeInfo?.salesInfo?.coresOffered) - Number(coretimeInfo?.salesInfo.coresSold); - - const onDropDownChange = useCallback((v: number) => { - console.log(v); - setSaleNumber(v); - const details = calculateSaleDetails(v, cycleNumber, currentRegionStart, coretimeInfo.status.lastTimeslice * 80, chainName); - - setSaleDetails(details); - }, []); - - return ( -
- {coretimeInfo && +function Sale({ chainName, coretimeInfo }: Props): React.ReactElement { + const { api, isApiReady } = useApi(); + const { t } = useTranslation(); + const { + salesInfo: { regionBegin }, + status: { lastCommittedTimeslice } } = coretimeInfo; + + const saleParams = coretimeInfo && getSaleParameters(coretimeInfo.salesInfo, coretimeInfo.config, chainName, lastCommittedTimeslice) + const phaseName = useMemo(() => saleParams?.phaseConfig?.currentPhaseName, [saleParams]) + const [chosenSaleNumber, setChosenSaleNumber] = useState(-1); + const saleNumberOptions = useMemo(() => + [ + { + text: t('Pick a sale number'), + value: -1 + }, + ...Array.from({ length: saleParams?.cycleNumber }, (_, i) => ({ + text: `sale #${i + 1}`, + value: i + })).reverse() + + ] + , [saleParams]); + + const saleDetails = useMemo(() => calculateSaleDetails(chosenSaleNumber, saleParams?.cycleNumber, coretimeInfo.status.lastTimeslice * 80, chainName, coretimeInfo.config.regionLength, saleParams), [chosenSaleNumber, saleParams, coretimeInfo]); + const progressValues = useMemo(() => getSaleProgress(lastCommittedTimeslice, saleParams.currentRegion.start.ts, saleParams.interlude.ts, saleParams.leadin.ts, regionBegin), + [saleParams, lastCommittedTimeslice, regionBegin]); + + const available = getAvailableNumberOfCores(coretimeInfo); + + const onDropDownChange = (v: number) => setChosenSaleNumber(v) + + return ( +
+ {coretimeInfo && } -
+
-
- - 100 DOT - {/* {!!available && {available}} +
+ + 100 DOT + {/* {!!available && {available}} {!available &&

Currently there are no cores available for purchase

} */} -
- {!!available &&
-
} -
-
- -
- {currentPhase && currentPhase} - {currentPhase && phaseConfig && estimateTime(phaseConfig[currentPhase].lastTimeslice, coretimeInfo.status.lastTimeslice * 80)} - {currentPhase && phaseConfig?.[currentPhase].lastBlock} - {formatNumber(coretimeInfo?.salesInfo.endPrice)} - {formatNumber(coretimeInfo?.salesInfo.selloutPrice)} -
-
- -
-
- -
-
-

Historical data

-
-
- - ({ - text: `sale #${i}`, - value: i - })).reverse() - ]} - value={-1} - /> - + + {!!available &&
+
} +
+
+ +
+ {phaseName && phaseName} + {phaseName && saleParams?.phaseConfig && estimateTime(saleParams.phaseConfig.config[phaseName].lastTimeslice, coretimeInfo.status.lastTimeslice * 80)} + {phaseName && saleParams?.phaseConfig && saleParams?.phaseConfig?.config[phaseName].lastBlock} + {formatNumber(coretimeInfo?.salesInfo.endPrice)} + {formatNumber(coretimeInfo?.salesInfo.selloutPrice)} +
+
+ +
+
+ +
+
+

Interlude + sale data

+
+
+ + + +
+ {chosenSaleNumber > -1 && !!saleDetails &&
+ + +
} + {chosenSaleNumber > -1 && !!saleDetails &&
+

Query sale transactions

+

We do not index transactions ourselves but you can query historical transactions on Subscan. < br /> + The button below will open a new tab with the correct query parameters for the chosen sale.

+ +
+ + } +
+
- {saleNumber > -1 && !!saleDetails &&
- - -
} - {saleNumber > -1 && !!saleDetails &&
-
- - } -
-
-
-
- ); +
+ ); } export default Sale; diff --git a/packages/page-coretime/src/index.tsx b/packages/page-coretime/src/index.tsx index de71deaccc0..bd428a5c00b 100644 --- a/packages/page-coretime/src/index.tsx +++ b/packages/page-coretime/src/index.tsx @@ -3,7 +3,7 @@ import type { TabItem } from '@polkadot/react-components/types'; -import React, { useMemo, useRef } from 'react'; +import React, { useRef } from 'react'; import { Route, Routes } from 'react-router-dom'; import { Tabs } from '@polkadot/react-components'; @@ -18,7 +18,7 @@ interface Props { className?: string; } -function createItemsRef (t: (key: string, options?: { replace: Record }) => string): TabItem[] { +function createItemsRef(t: (key: string, options?: { replace: Record }) => string): TabItem[] { return [ { isRoot: true, @@ -32,7 +32,7 @@ function createItemsRef (t: (key: string, options?: { replace: Record { +function CoretimeApp({ basePath, className }: Props): React.ReactElement { const { t } = useTranslation(); const itemsRef = useRef(createItemsRef(t)); const { api, isApiReady } = useApi(); diff --git a/packages/page-coretime/src/types.ts b/packages/page-coretime/src/types.ts index d80dc66410f..fadc8bad077 100644 --- a/packages/page-coretime/src/types.ts +++ b/packages/page-coretime/src/types.ts @@ -18,4 +18,46 @@ export const PhaseName = { Renewals: 'Renewals', PriceDiscovery: 'Price Discovery', FixedPrice: 'Fixed Price' -}; +} as const; + +export type PhaseConfig = { + currentPhaseName: string; + config: Record +} + +export type PhaseProgress = { + value: number; + total: number; + label: string; +} + +export type SaleDetails = { + saleNumber: number; + relay: { + start: { + block: number; + ts: number; + }, + end: { + block: number; + ts: number; + }, + }, + coretime: { + start: { + block: number; + }, + end: { + block: number; + } + } + date: { + start: string | null; + end: string | null; + } +} + +export interface RegionInfo { + regionEnd: number; + regionBegin: number; +} From 86cd14510bd3f9a58b53b36b05e3be36322bc43e Mon Sep 17 00:00:00 2001 From: Daria Mikhailova Date: Wed, 11 Dec 2024 00:30:46 +0800 Subject: [PATCH 08/14] lint --- .../page-coretime/src/Overview/Summary.tsx | 9 +- packages/page-coretime/src/Overview/index.tsx | 5 - packages/page-coretime/src/Row.tsx | 4 +- packages/page-coretime/src/Sale/SaleTable.tsx | 9 +- packages/page-coretime/src/Sale/Summary.tsx | 5 +- packages/page-coretime/src/Sale/index.tsx | 231 +++++++++--------- packages/page-coretime/src/index.tsx | 24 +- packages/page-coretime/src/types.ts | 30 ++- packages/page-coretime/src/utils.ts | 148 +++++------ packages/page-coretime/src/utils/index.ts | 133 +++++----- packages/page-coretime/src/utils/sale.ts | 2 +- packages/react-components/src/ProgresBar.tsx | 2 +- packages/react-hooks/src/types.ts | 1 + .../react-hooks/src/useCoretimeInformation.ts | 4 +- 14 files changed, 314 insertions(+), 293 deletions(-) diff --git a/packages/page-coretime/src/Overview/Summary.tsx b/packages/page-coretime/src/Overview/Summary.tsx index 6cb6f888ccf..dcc94d1c8d2 100644 --- a/packages/page-coretime/src/Overview/Summary.tsx +++ b/packages/page-coretime/src/Overview/Summary.tsx @@ -1,7 +1,6 @@ // Copyright 2017-2024 @polkadot/app-coretime authors & contributors // SPDX-License-Identifier: Apache-2.0 -import type { ApiPromise } from '@polkadot/api'; import type { BrokerStatus, CoreDescription, PalletBrokerConfigRecord, PalletBrokerSaleInfoRecord, RegionInfo } from '@polkadot/react-hooks/types'; import React, { useMemo } from 'react'; @@ -10,11 +9,9 @@ import { CardSummary, SummaryBox } from '@polkadot/react-components'; import { BN } from '@polkadot/util'; import { useTranslation } from '../translate.js'; -import { estimateTime } from '../utils/index.js'; -import { FirstCycleStart } from '../utils/index.js'; +import { estimateTime, FirstCycleStart } from '../utils/index.js'; interface Props { - api: ApiPromise | null, coreDscriptors?: CoreDescription[]; saleInfo: PalletBrokerSaleInfoRecord config: PalletBrokerConfigRecord, @@ -24,14 +21,14 @@ interface Props { chainName: string } -function Summary({ api, chainName, config, parachainCount, saleInfo, status }: Props): React.ReactElement { +function Summary ({ chainName, config, parachainCount, saleInfo, status }: Props): React.ReactElement { const { t } = useTranslation(); const currentRegionEnd = saleInfo.regionEnd - config.regionLength; const currentRegionStart = saleInfo.regionEnd - config.regionLength * 2; const cycleNumber = useMemo(() => chainName && currentRegionEnd && Math.floor((currentRegionEnd - FirstCycleStart[chainName]) / config.regionLength) - , [currentRegionEnd, chainName, config]); + , [currentRegionEnd, chainName, config]); return ( diff --git a/packages/page-coretime/src/Overview/index.tsx b/packages/page-coretime/src/Overview/index.tsx index 525809c7737..1f0e2b8c465 100644 --- a/packages/page-coretime/src/Overview/index.tsx +++ b/packages/page-coretime/src/Overview/index.tsx @@ -5,8 +5,6 @@ import type { CoretimeInformation } from '@polkadot/react-hooks/types'; import React from 'react'; -import { useApi } from '@polkadot/react-hooks'; - import ParachainsTable from '../ParachainsTable.js'; import Summary from './Summary.js'; @@ -17,13 +15,10 @@ interface Props { } function Overview ({ chainName, className, coretimeInfo }: Props): React.ReactElement { - const { api, isApiReady } = useApi(); - return (
{coretimeInfo && ( ` } `; -function Row({ chainRecord, highlight = false, id, lastCommittedTimeslice, lease, regionBegin, regionEnd }: Props): React.ReactElement { +function Row ({ chainRecord, highlight = false, id, lastCommittedTimeslice, lease, regionBegin, regionEnd }: Props): React.ReactElement { const chainRegionEnd = (chainRecord.renewalStatus === ChainRenewalStatus.Renewed ? regionEnd : regionBegin); const targetTimeslice = lease?.until || chainRegionEnd; const showEstimates = !!targetTimeslice && Object.values(CoreTimeTypes)[chainRecord.type] !== CoreTimeTypes.Reservation; diff --git a/packages/page-coretime/src/Sale/SaleTable.tsx b/packages/page-coretime/src/Sale/SaleTable.tsx index 38a9f1d46c0..b4b67ff3b3e 100644 --- a/packages/page-coretime/src/Sale/SaleTable.tsx +++ b/packages/page-coretime/src/Sale/SaleTable.tsx @@ -1,6 +1,8 @@ // Copyright 2017-2024 @polkadot/app-coretime authors & contributors // SPDX-License-Identifier: Apache-2.0 +import type { SaleDetails } from '../types.js'; + import React, { useRef } from 'react'; import { Table } from '@polkadot/react-components'; @@ -8,17 +10,17 @@ import { Table } from '@polkadot/react-components'; import { useTranslation } from '../translate.js'; interface Props { - saleDetails: any + saleDetails: SaleDetails } -function SaleTable({ saleDetails }: Props): React.ReactElement { +function SaleTable ({ saleDetails }: Props): React.ReactElement { const { t } = useTranslation(); const headerRef = useRef<([React.ReactNode?, string?, number?] | false)[]>([ [t(`details for sale ${saleDetails.saleNumber}`), 'start'], [t('Dates'), 'start media--800'], [t('Blocks (relay)'), 'start'], [t('Blocks(coretime)'), 'start'], - [t('Timeslices'), 'start'], + [t('Timeslices'), 'start'] ]); return ( @@ -42,7 +44,6 @@ function SaleTable({ saleDetails }: Props): React.ReactElement { {saleDetails.relay.end.ts} - ); } diff --git a/packages/page-coretime/src/Sale/Summary.tsx b/packages/page-coretime/src/Sale/Summary.tsx index b42eab5ac34..29d5708325c 100644 --- a/packages/page-coretime/src/Sale/Summary.tsx +++ b/packages/page-coretime/src/Sale/Summary.tsx @@ -10,8 +10,7 @@ import { CardSummary, SummaryBox } from '@polkadot/react-components'; import { BN } from '@polkadot/util'; import { useTranslation } from '../translate.js'; -import { getCurrentRegionStartEndTs } from '../utils/index.js'; -import { estimateTime } from '../utils/index.js'; +import { estimateTime, getCurrentRegionStartEndTs } from '../utils/index.js'; interface Props { api: ApiPromise | null, @@ -23,7 +22,7 @@ interface Props { cycleNumber: number } -function Summary({ api, config, cycleNumber, saleInfo, status }: Props): React.ReactElement { +function Summary ({ config, cycleNumber, saleInfo, status }: Props): React.ReactElement { const { t } = useTranslation(); const { currentRegionEnd, currentRegionStart } = getCurrentRegionStartEndTs(saleInfo, config); diff --git a/packages/page-coretime/src/Sale/index.tsx b/packages/page-coretime/src/Sale/index.tsx index 23aae172b0f..728416cd474 100644 --- a/packages/page-coretime/src/Sale/index.tsx +++ b/packages/page-coretime/src/Sale/index.tsx @@ -3,7 +3,7 @@ import type { CoretimeInformation } from '@polkadot/react-hooks/types'; -import { useMemo, useState } from 'react'; +import React, { useCallback, useMemo, useState } from 'react'; import { Button, CardSummary, Dropdown, SummaryBox } from '@polkadot/react-components'; import ProgresBar from '@polkadot/react-components/ProgresBar'; @@ -12,135 +12,134 @@ import { formatNumber } from '@polkadot/util'; import { useTranslation } from '../translate.js'; import { calculateSaleDetails, constructSubscanQuery, getSaleParameters, getSaleProgress } from '../utils.js'; - import { estimateTime, getAvailableNumberOfCores } from '../utils/index.js'; import SaleTable from './SaleTable.js'; import Summary from './Summary.js'; interface Props { - coretimeInfo: CoretimeInformation - chainName: string + coretimeInfo: CoretimeInformation + chainName: string } -function Sale({ chainName, coretimeInfo }: Props): React.ReactElement { - const { api, isApiReady } = useApi(); - const { t } = useTranslation(); - const { - salesInfo: { regionBegin }, - status: { lastCommittedTimeslice } } = coretimeInfo; - - const saleParams = coretimeInfo && getSaleParameters(coretimeInfo.salesInfo, coretimeInfo.config, chainName, lastCommittedTimeslice) - const phaseName = useMemo(() => saleParams?.phaseConfig?.currentPhaseName, [saleParams]) - const [chosenSaleNumber, setChosenSaleNumber] = useState(-1); - const saleNumberOptions = useMemo(() => - [ - { - text: t('Pick a sale number'), - value: -1 - }, - ...Array.from({ length: saleParams?.cycleNumber }, (_, i) => ({ - text: `sale #${i + 1}`, - value: i - })).reverse() - - ] - , [saleParams]); - - const saleDetails = useMemo(() => calculateSaleDetails(chosenSaleNumber, saleParams?.cycleNumber, coretimeInfo.status.lastTimeslice * 80, chainName, coretimeInfo.config.regionLength, saleParams), [chosenSaleNumber, saleParams, coretimeInfo]); - const progressValues = useMemo(() => getSaleProgress(lastCommittedTimeslice, saleParams.currentRegion.start.ts, saleParams.interlude.ts, saleParams.leadin.ts, regionBegin), - [saleParams, lastCommittedTimeslice, regionBegin]); - - const available = getAvailableNumberOfCores(coretimeInfo); - - const onDropDownChange = (v: number) => setChosenSaleNumber(v) - - return ( -
- {coretimeInfo && +function Sale ({ chainName, coretimeInfo }: Props): React.ReactElement { + const { api, isApiReady } = useApi(); + const { t } = useTranslation(); + const { salesInfo: { regionBegin }, + status: { lastCommittedTimeslice } } = coretimeInfo; + + const saleParams = coretimeInfo && getSaleParameters(coretimeInfo.salesInfo, coretimeInfo.config, chainName, lastCommittedTimeslice); + const phaseName = useMemo(() => saleParams?.phaseConfig?.currentPhaseName, [saleParams]); + const [chosenSaleNumber, setChosenSaleNumber] = useState(-1); + const saleNumberOptions = useMemo(() => + [ + { + text: t('Pick a sale number'), + value: -1 + }, + ...Array.from({ length: saleParams?.cycleNumber }, (_, i) => ({ + text: `sale #${i + 1}`, + value: i + })).reverse() + + ] + , [saleParams, t]); + + const saleDetails = useMemo(() => calculateSaleDetails(chosenSaleNumber, saleParams?.cycleNumber, coretimeInfo.status.lastTimeslice * 80, chainName, coretimeInfo.config.regionLength, saleParams), [chosenSaleNumber, saleParams, coretimeInfo, chainName]); + const progressValues = useMemo(() => getSaleProgress(lastCommittedTimeslice, saleParams.currentRegion.start.ts, saleParams.interlude.ts, saleParams.leadin.ts, regionBegin), + [saleParams, lastCommittedTimeslice, regionBegin]); + + const available = getAvailableNumberOfCores(coretimeInfo); + + const onDropDownChange = useCallback((value: number) => { + setChosenSaleNumber(value); + }, []); + + const onQuerySaleClick = useCallback(() => { + if (saleDetails) { + window.open(constructSubscanQuery(saleDetails.coretime.start.block, saleDetails.coretime.end.block, chainName)); + } + }, [saleDetails, chainName]); + + return ( +
+ {coretimeInfo && } -
+
-
- - 100 DOT - {/* {!!available && {available}} +
+ + 100 DOT + {/* {!!available && {available}} {!available &&

Currently there are no cores available for purchase

} */} -
- {!!available &&
-
} -
-
- -
- {phaseName && phaseName} - {phaseName && saleParams?.phaseConfig && estimateTime(saleParams.phaseConfig.config[phaseName].lastTimeslice, coretimeInfo.status.lastTimeslice * 80)} - {phaseName && saleParams?.phaseConfig && saleParams?.phaseConfig?.config[phaseName].lastBlock} - {formatNumber(coretimeInfo?.salesInfo.endPrice)} - {formatNumber(coretimeInfo?.salesInfo.selloutPrice)} -
-
- -
-
- -
-
-

Interlude + sale data

-
-
- - - -
- {chosenSaleNumber > -1 && !!saleDetails &&
- - -
} - {chosenSaleNumber > -1 && !!saleDetails &&
-

Query sale transactions

-

We do not index transactions ourselves but you can query historical transactions on Subscan. < br /> - The button below will open a new tab with the correct query parameters for the chosen sale.

+ + {!!available &&
+
} +
+
+ +
+ {phaseName && phaseName} + {phaseName && saleParams?.phaseConfig && estimateTime(saleParams.phaseConfig.config[phaseName].lastTimeslice, coretimeInfo.status.lastTimeslice * 80)} + {phaseName && saleParams?.phaseConfig?.config[phaseName].lastBlock} + {formatNumber(coretimeInfo?.salesInfo.endPrice)} + {formatNumber(coretimeInfo?.salesInfo.selloutPrice)} +
+
+ +
+
+ +
+
+

Interlude + sale data

+
+
+ + -
- - } -
-
-
- ); + {chosenSaleNumber > -1 && !!saleDetails &&
+ + +
} + {chosenSaleNumber > -1 && !!saleDetails &&
+

Query sale transactions

+

We do not index transactions ourselves but you can query historical transactions on Subscan.
+ The button below will open a new tab with the correct query parameters for the chosen sale.

+
+ + } +
+
+
+
+ ); } export default Sale; diff --git a/packages/page-coretime/src/index.tsx b/packages/page-coretime/src/index.tsx index bd428a5c00b..8b04217e7c3 100644 --- a/packages/page-coretime/src/index.tsx +++ b/packages/page-coretime/src/index.tsx @@ -18,7 +18,7 @@ interface Props { className?: string; } -function createItemsRef(t: (key: string, options?: { replace: Record }) => string): TabItem[] { +function createItemsRef (t: (key: string, options?: { replace: Record }) => string): TabItem[] { return [ { isRoot: true, @@ -32,7 +32,7 @@ function createItemsRef(t: (key: string, options?: { replace: Record { +function CoretimeApp ({ basePath, className }: Props): React.ReactElement { const { t } = useTranslation(); const itemsRef = useRef(createItemsRef(t)); const { api, isApiReady } = useApi(); @@ -49,19 +49,23 @@ function CoretimeApp({ basePath, className }: Props): React.ReactElement + coretimeInfo && ( + + ) } index /> + coretimeInfo && ( + + ) } path='sale' /> diff --git a/packages/page-coretime/src/types.ts b/packages/page-coretime/src/types.ts index fadc8bad077..575de2341e1 100644 --- a/packages/page-coretime/src/types.ts +++ b/packages/page-coretime/src/types.ts @@ -15,23 +15,37 @@ export interface PhaseInfo { } export const PhaseName = { - Renewals: 'Renewals', + FixedPrice: 'Fixed Price', PriceDiscovery: 'Price Discovery', - FixedPrice: 'Fixed Price' + Renewals: 'Renewals' } as const; -export type PhaseConfig = { - currentPhaseName: string; - config: Record +type PhaseNameType = typeof PhaseName[keyof typeof PhaseName]; + +export interface PhaseConfig { + currentPhaseName: PhaseNameType; + config: Record; } -export type PhaseProgress = { +export interface PhaseProgress { value: number; total: number; label: string; } -export type SaleDetails = { +export interface SaleParameters { + currentRegion: { + start: { ts: number; blocks: number }; + end: { ts: number; blocks: number }; + }; + cycleNumber: number; + interlude: { ts: number; blocks: number }; + leadin: { ts: number; blocks: number }; + phaseConfig: PhaseConfig; + regionNumber: number; +} + +export interface SaleDetails { saleNumber: number; relay: { start: { @@ -58,6 +72,6 @@ export type SaleDetails = { } export interface RegionInfo { - regionEnd: number; regionBegin: number; + regionEnd: number; } diff --git a/packages/page-coretime/src/utils.ts b/packages/page-coretime/src/utils.ts index 484a83ca53e..d8983cdb90d 100644 --- a/packages/page-coretime/src/utils.ts +++ b/packages/page-coretime/src/utils.ts @@ -1,11 +1,13 @@ // Copyright 2017-2024 @polkadot/app-coretime authors & contributors // SPDX-License-Identifier: Apache-2.0 +import type { PalletBrokerConfigRecord } from '@polkadot/react-hooks/types'; +import type { PhaseConfig, PhaseProgress, RegionInfo, SaleDetails, SaleParameters } from './types.js'; -import { PhaseConfig, PhaseName, PhaseProgress, RegionInfo, SaleDetails } from './types.js'; -import { estimateTime, FirstCycleStart, get, getCurrentSaleNumber, getCurrentRegionStartEndTs } from './utils/index.js'; +import { estimateTime, FirstCycleStart, get, getCurrentRegionStartEndTs, getCurrentSaleNumber } from './utils/index.js'; +import { PhaseName } from './types.js'; -export function calculateSaleDetails( +export function calculateSaleDetails ( saleNumber: number, currentSaleNumber: number, latestBlock: number, @@ -17,39 +19,38 @@ export function calculateSaleDetails( const saleStartBlock = saleParams.currentRegion.start.blocks - blocksPerSaleRelay * (currentSaleNumber - saleNumber); const saleEndBlock = saleStartBlock + blocksPerSaleRelay; - const saleStartTs = get.timeslices.relay(saleStartBlock); const saleEndTs = get.timeslices.relay(saleEndBlock); - - const saleStartBlockCoretime = FirstCycleStart[chainName] + get.blocks.coretime((saleNumber) * regionLength) - const saleEndBlockCoretime = saleStartBlockCoretime + get.blocks.coretime(regionLength) + const saleStartBlockCoretime = FirstCycleStart[chainName] + get.blocks.coretime((saleNumber) * regionLength); + const saleEndBlockCoretime = saleStartBlockCoretime + get.blocks.coretime(regionLength); const data = { - saleNumber, - relay: { - start: { - block: saleStartBlock, - ts: saleStartTs, - }, - end: { - block: saleEndBlock, - ts: saleEndTs, - }, - }, coretime: { - start: { block: saleStartBlockCoretime }, end: { block: saleEndBlockCoretime }, + start: { block: saleStartBlockCoretime } }, date: { - start: estimateTime(saleStartTs, latestBlock), end: estimateTime(saleEndTs, latestBlock), - } - } + start: estimateTime(saleStartTs, latestBlock) + }, + relay: { + end: { + block: saleEndBlock, + ts: saleEndTs + }, + start: { + block: saleStartBlock, + ts: saleStartTs + } + }, + saleNumber + }; + return data; } -export const constructSubscanQuery = (blockStart: number, blockEnd: number,chainName: string, module = 'broker', call = 'purchase', ) => { +export const constructSubscanQuery = (blockStart: number, blockEnd: number, chainName: string, module = 'broker', call = 'purchase') => { const page = 1; const pageSize = 25; const signed = 'all'; @@ -59,11 +60,12 @@ export const constructSubscanQuery = (blockStart: number, blockEnd: number,chain }; export const getSaleParameters = ( - salesInfo: RegionInfo, - config: { interludeLength: number; leadinLength: number; regionLength: number }, - chainName: string, - lastCommittedTimeslice: number) => { - // The sale is happening on the coretime chain, so we need to convert the timeslices to blocks (40 blocks per timeslice) + salesInfo: RegionInfo, + config: Pick, + chainName: string, + lastCommittedTimeslice: number +): SaleParameters => { + // The sale is happening on the coretime chain, so we need to convert the timeslices to blocks (40 blocks per timeslice) const interludeLengthTs = get.timeslices.coretime(config.interludeLength); const leadInLengthTs = get.timeslices.coretime(config.leadinLength); @@ -71,28 +73,29 @@ export const getSaleParameters = ( const phaseConfig = getPhaseConfiguration(currentRegionStart, config.regionLength, interludeLengthTs, leadInLengthTs, lastCommittedTimeslice); const saleNumber = getCurrentSaleNumber(currentRegionEnd, chainName, config); + return { - cycleNumber: getCurrentSaleNumber(currentRegionEnd, chainName, config), - regionNumber: saleNumber - 1, - leadin: { - ts: leadInLengthTs, - blocks: config.leadinLength - }, - interlude: { - ts: interludeLengthTs, - blocks: config.interludeLength - }, currentRegion: { - start: { - ts: currentRegionStart, - blocks: get.blocks.relay(currentRegionStart) - }, end: { - ts: currentRegionEnd, - blocks: get.blocks.relay(currentRegionEnd) + blocks: get.blocks.relay(currentRegionEnd), + ts: currentRegionEnd + }, + start: { + blocks: get.blocks.relay(currentRegionStart), + ts: currentRegionStart } }, - phaseConfig + cycleNumber: getCurrentSaleNumber(currentRegionEnd, chainName, config), + interlude: { + blocks: config.interludeLength, + ts: interludeLengthTs + }, + leadin: { + blocks: config.leadinLength, + ts: leadInLengthTs + }, + phaseConfig, + regionNumber: saleNumber - 1 }; }; @@ -102,35 +105,34 @@ export const getPhaseConfiguration = ( interludeLengthTs: number, leadInLengthTs: number, lastCommittedTimeslice: number): PhaseConfig | undefined => { - - const renewalsEndTs = currentRegionStart + interludeLengthTs - const priceDiscoveryEndTs = renewalsEndTs + leadInLengthTs - const fixedPriceLenght = regionLength - interludeLengthTs - leadInLengthTs - const fixedPriceEndTs = priceDiscoveryEndTs + fixedPriceLenght + const renewalsEndTs = currentRegionStart + interludeLengthTs; + const priceDiscoveryEndTs = renewalsEndTs + leadInLengthTs; + const fixedPriceLenght = regionLength - interludeLengthTs - leadInLengthTs; + const fixedPriceEndTs = priceDiscoveryEndTs + fixedPriceLenght; return { - currentPhaseName: determinePhaseName(lastCommittedTimeslice, currentRegionStart, interludeLengthTs, leadInLengthTs), config: { [PhaseName.Renewals]: { - lastTimeslice: renewalsEndTs, - lastBlock: get.blocks.relay(renewalsEndTs) + lastBlock: get.blocks.relay(renewalsEndTs), + lastTimeslice: renewalsEndTs }, [PhaseName.PriceDiscovery]: { - lastTimeslice: priceDiscoveryEndTs, - lastBlock: get.blocks.relay(priceDiscoveryEndTs) + lastBlock: get.blocks.relay(priceDiscoveryEndTs), + lastTimeslice: priceDiscoveryEndTs }, [PhaseName.FixedPrice]: { - lastTimeslice: fixedPriceEndTs, - lastBlock: get.blocks.relay(fixedPriceEndTs) + lastBlock: get.blocks.relay(fixedPriceEndTs), + lastTimeslice: fixedPriceEndTs } - } - } + }, + currentPhaseName: determinePhaseName(lastCommittedTimeslice, currentRegionStart, interludeLengthTs, leadInLengthTs) + }; }; export const determinePhaseName = ( - lastCommittedTimeslice: number, + lastCommittedTimeslice: number, currentRegionStart: number, - interludeLengthTs: number, + interludeLengthTs: number, leadInLengthTs: number): typeof PhaseName[keyof typeof PhaseName] => { const progress = lastCommittedTimeslice - currentRegionStart; @@ -143,31 +145,31 @@ export const determinePhaseName = ( } return PhaseName.FixedPrice; -} +}; export const getSaleProgress = ( - lastCommittedTimeslice: number, - currentRegionStart: number, - interludeLengthTs: number, - leadInLengthTs: number, + lastCommittedTimeslice: number, + currentRegionStart: number, + interludeLengthTs: number, + leadInLengthTs: number, regionBegin: number): PhaseProgress[] => { const progress = lastCommittedTimeslice - currentRegionStart; return [ { - value: Math.min(progress, interludeLengthTs), + label: PhaseName.Renewals, total: interludeLengthTs, - label: PhaseName.Renewals + value: Math.min(progress, interludeLengthTs) }, { - value: Math.min(Math.max(progress - interludeLengthTs, 0), leadInLengthTs), + label: PhaseName.PriceDiscovery, total: leadInLengthTs, - label: PhaseName.PriceDiscovery + value: Math.min(Math.max(progress - interludeLengthTs, 0), leadInLengthTs) }, { - value: Math.max(progress - interludeLengthTs - leadInLengthTs, 0), + label: PhaseName.FixedPrice, total: regionBegin - currentRegionStart - interludeLengthTs - leadInLengthTs, - label: PhaseName.FixedPrice + value: Math.max(progress - interludeLengthTs - leadInLengthTs, 0) } ]; -} \ No newline at end of file +}; diff --git a/packages/page-coretime/src/utils/index.ts b/packages/page-coretime/src/utils/index.ts index 890470fbd2b..b512261c440 100644 --- a/packages/page-coretime/src/utils/index.ts +++ b/packages/page-coretime/src/utils/index.ts @@ -1,34 +1,34 @@ // Copyright 2017-2024 @polkadot/app-coretime authors & contributors // SPDX-License-Identifier: Apache-2.0 +import type { CoretimeInformation, PalletBrokerConfigRecord } from '@polkadot/react-hooks/types'; +import type { RegionInfo } from '../types.js'; + +import { CoreTimeChainConsts, CoreTimeConsts } from '@polkadot/react-hooks/types'; import { BN } from '@polkadot/util'; -import { CoreTimeChainConsts, CoreTimeConsts, CoretimeInformation, PalletBrokerConfigRecord } from "@polkadot/react-hooks/types"; -import { RegionInfo } from '../types.js'; // Blocks on the Coretime Chain export const FirstCycleStart: Record = { - kusama: 285768, - polkadot: 282525 - }; - + kusama: 285768, + polkadot: 282525 +}; - -export function formatDate(date: Date) { - const day = date.getDate(); - const month = date.toLocaleString('default', { month: 'short' }); - const year = date.getFullYear(); +export function formatDate (date: Date) { + const day = date.getDate(); + const month = date.toLocaleString('default', { month: 'short' }); + const year = date.getFullYear(); - return `${day} ${month} ${year}`; + return `${day} ${month} ${year}`; } /** * Gives you a date for the traget timeslice - * + * * Relay chain info: * blockTime = 6000 ms * BlocksPerTimeslice = 80 * Default Regoin = 5040 timeslices - * + * * Calculation: * TargetBlock = TargetTimeslice * BlocksPerTimeslice * Block Time Difference = |TargetBlock - latest Block| * blockTime @@ -40,81 +40,90 @@ export function formatDate(date: Date) { * now plus block time difference */ export const estimateTime = (targetTimeslice: string | number, latestBlock: number): string | null => { - if (!latestBlock || !targetTimeslice) { - console.error('Invalid input: one or more inputs are missing'); - return null; - } + if (!latestBlock || !targetTimeslice) { + console.error('Invalid input: one or more inputs are missing'); - try { - const now = new BN(Date.now()); - const blockTime = new BN(CoreTimeConsts.BlockTime); // Average block time in milliseconds (6 seconds) - const blocksPerTimeslice = new BN(CoreTimeConsts.BlocksPerTimeslice); - const targetBlock = new BN(Number(targetTimeslice)).mul(blocksPerTimeslice); - const latestBlockBN = new BN(latestBlock); - const blockDifference = targetBlock.sub(latestBlockBN); - const timeDifference = blockDifference.mul(blockTime); - const estTimestamp = now.add(timeDifference); + return null; + } - return formatDate(new Date(estTimestamp.toNumber())); - } catch (error) { - console.error('Error in calculation:', error); - return null; - } -}; + try { + const now = new BN(Date.now()); + const blockTime = new BN(CoreTimeConsts.BlockTime); // Average block time in milliseconds (6 seconds) + const blocksPerTimeslice = new BN(CoreTimeConsts.BlocksPerTimeslice); + const targetBlock = new BN(Number(targetTimeslice)).mul(blocksPerTimeslice); + const latestBlockBN = new BN(latestBlock); + const blockDifference = targetBlock.sub(latestBlockBN); + const timeDifference = blockDifference.mul(blockTime); + const estTimestamp = now.add(timeDifference); + return formatDate(new Date(estTimestamp.toNumber())); + } catch (error) { + console.error('Error in calculation:', error); + + return null; + } +}; /** * Get the current sale number - * + * * @param currentRegionEnd - The end of the current region * @param chainName - The name of the chain * @param config - broker.configuration call response - * + * * @returns The current sale number */ -export const getCurrentSaleNumber = (currentRegionEnd: number, chainName: string, config: any) => - chainName && currentRegionEnd && Math.floor((currentRegionEnd - FirstCycleStart[chainName]) / config.regionLength); +export const getCurrentSaleNumber = ( + currentRegionEnd: number, + chainName: string, + config: Pick +): number => { + if (!chainName || !currentRegionEnd) { + return 0; + } + return Math.floor((currentRegionEnd - FirstCycleStart[chainName]) / config.regionLength) + 1; +}; /** * Helper functions to convert timeslices to blocks and vice versa */ export const get = { - blocks: { - coretime: (ts: number) => { - return ts * CoreTimeChainConsts.BlocksPerTimeslice; - }, - relay: (ts: number) => { - return ts * CoreTimeConsts.BlocksPerTimeslice; - } + blocks: { + coretime: (ts: number) => { + return ts * CoreTimeChainConsts.BlocksPerTimeslice; }, - timeslices: { - coretime: (blocks: number) => { - return blocks / CoreTimeChainConsts.BlocksPerTimeslice; - }, - relay: (blocks: number) => { - return blocks / CoreTimeConsts.BlocksPerTimeslice; - } + relay: (ts: number) => { + return ts * CoreTimeConsts.BlocksPerTimeslice; } -} + }, + timeslices: { + coretime: (blocks: number) => { + return blocks / CoreTimeChainConsts.BlocksPerTimeslice; + }, + relay: (blocks: number) => { + return blocks / CoreTimeConsts.BlocksPerTimeslice; + } + } +}; /** * Get the start and end of the current region * broker.saleInfo call returns the start/end of the next region always - * + * * The end of the current region is the start of the next region, which is returned by broker.saleInfo call - * + * * @param saleInfo - The sale information * @param config - The broker configuration - * + * * @returns The start and end of the current region */ export const getCurrentRegionStartEndTs = (saleInfo: RegionInfo, config: PalletBrokerConfigRecord) => { - return { - currentRegionEnd: saleInfo.regionBegin, - currentRegionStart: saleInfo.regionBegin - config.regionLength - }; -} + return { + currentRegionEnd: saleInfo.regionBegin, + currentRegionStart: saleInfo.regionBegin - config.regionLength + }; +}; -export const getAvailableNumberOfCores = (coretimeInfo: CoretimeInformation) => - Number(coretimeInfo?.salesInfo?.coresOffered) - Number(coretimeInfo?.salesInfo.coresSold); +export const getAvailableNumberOfCores = (coretimeInfo: CoretimeInformation) => + Number(coretimeInfo?.salesInfo?.coresOffered) - Number(coretimeInfo?.salesInfo.coresSold); diff --git a/packages/page-coretime/src/utils/sale.ts b/packages/page-coretime/src/utils/sale.ts index a9ed33ff673..b5147766e39 100644 --- a/packages/page-coretime/src/utils/sale.ts +++ b/packages/page-coretime/src/utils/sale.ts @@ -1,2 +1,2 @@ // Copyright 2017-2024 @polkadot/app-coretime authors & contributors -// SPDX-License-Identifier: Apache-2.0 \ No newline at end of file +// SPDX-License-Identifier: Apache-2.0 diff --git a/packages/react-components/src/ProgresBar.tsx b/packages/react-components/src/ProgresBar.tsx index f265e618aeb..dc1ea6f7d73 100644 --- a/packages/react-components/src/ProgresBar.tsx +++ b/packages/react-components/src/ProgresBar.tsx @@ -1,4 +1,4 @@ -// [object Object] +// Copyright 2017-2024 @polkadot/react-components authors & contributors // SPDX-License-Identifier: Apache-2.0 import React from 'react'; diff --git a/packages/react-hooks/src/types.ts b/packages/react-hooks/src/types.ts index 5698f68dc38..ba0a0657079 100644 --- a/packages/react-hooks/src/types.ts +++ b/packages/react-hooks/src/types.ts @@ -385,5 +385,6 @@ export const CoreTimeConsts = { }; export const CoreTimeChainConsts = { + BlockTime: 12000, BlocksPerTimeslice: 40 }; diff --git a/packages/react-hooks/src/useCoretimeInformation.ts b/packages/react-hooks/src/useCoretimeInformation.ts index 2b1ae49a7c0..669a0a8decb 100644 --- a/packages/react-hooks/src/useCoretimeInformation.ts +++ b/packages/react-hooks/src/useCoretimeInformation.ts @@ -143,13 +143,13 @@ function useCoretimeInformationImpl (api: ApiPromise, ready: boolean): CoretimeI if (chainInfo && config && region && salesInfo && status) { setState({ + blockTimeMs, chainInfo, config, region, salesInfo, status, - taskIds, - blockTimeMs + taskIds }); } }, [taskIds, workloadData, potentialRenewalsCurrentRegion, salesInfo, leases, reservations, region, status, config, workplans, blockTimeMs]); From 6910751f774433f3804916abe19faeb2c1abf30a Mon Sep 17 00:00:00 2001 From: Daria Mikhailova Date: Wed, 11 Dec 2024 20:52:02 +0800 Subject: [PATCH 09/14] correctly calculated sale start/end blocks, dates and ts --- .../page-coretime/src/Overview/Summary.tsx | 15 ++++++++--- packages/page-coretime/src/Sale/SaleTable.tsx | 2 +- packages/page-coretime/src/Sale/Summary.tsx | 2 +- packages/page-coretime/src/Sale/index.tsx | 7 ++--- packages/page-coretime/src/types.ts | 2 ++ packages/page-coretime/src/utils.ts | 18 ++++++++----- packages/page-coretime/src/utils/index.ts | 26 ++++++++++++++----- 7 files changed, 49 insertions(+), 23 deletions(-) diff --git a/packages/page-coretime/src/Overview/Summary.tsx b/packages/page-coretime/src/Overview/Summary.tsx index dcc94d1c8d2..88675d89b7c 100644 --- a/packages/page-coretime/src/Overview/Summary.tsx +++ b/packages/page-coretime/src/Overview/Summary.tsx @@ -2,6 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 import type { BrokerStatus, CoreDescription, PalletBrokerConfigRecord, PalletBrokerSaleInfoRecord, RegionInfo } from '@polkadot/react-hooks/types'; +import type { ChainName } from '../types.js'; import React, { useMemo } from 'react'; @@ -18,7 +19,7 @@ interface Props { region: RegionInfo[], status: BrokerStatus, parachainCount: number - chainName: string + chainName: ChainName } function Summary ({ chainName, config, parachainCount, saleInfo, status }: Props): React.ReactElement { @@ -26,9 +27,15 @@ function Summary ({ chainName, config, parachainCount, saleInfo, status }: Props const currentRegionEnd = saleInfo.regionEnd - config.regionLength; const currentRegionStart = saleInfo.regionEnd - config.regionLength * 2; - const cycleNumber = useMemo(() => - chainName && currentRegionEnd && Math.floor((currentRegionEnd - FirstCycleStart[chainName]) / config.regionLength) - , [currentRegionEnd, chainName, config]); + const cycleNumber = useMemo(() => { + if (chainName && currentRegionEnd) { + return Math.floor( + (currentRegionEnd - FirstCycleStart.timeslice.coretime[chainName]) / config.regionLength + ); + } + + return undefined; + }, [currentRegionEnd, chainName, config]); return ( diff --git a/packages/page-coretime/src/Sale/SaleTable.tsx b/packages/page-coretime/src/Sale/SaleTable.tsx index b4b67ff3b3e..9addc70eb30 100644 --- a/packages/page-coretime/src/Sale/SaleTable.tsx +++ b/packages/page-coretime/src/Sale/SaleTable.tsx @@ -16,7 +16,7 @@ interface Props { function SaleTable ({ saleDetails }: Props): React.ReactElement { const { t } = useTranslation(); const headerRef = useRef<([React.ReactNode?, string?, number?] | false)[]>([ - [t(`details for sale ${saleDetails.saleNumber}`), 'start'], + [], [t('Dates'), 'start media--800'], [t('Blocks (relay)'), 'start'], [t('Blocks(coretime)'), 'start'], diff --git a/packages/page-coretime/src/Sale/Summary.tsx b/packages/page-coretime/src/Sale/Summary.tsx index 29d5708325c..b43df829a15 100644 --- a/packages/page-coretime/src/Sale/Summary.tsx +++ b/packages/page-coretime/src/Sale/Summary.tsx @@ -32,7 +32,7 @@ function Summary ({ config, cycleNumber, saleInfo, status }: Props): React.React {status &&
- {cycleNumber} + {cycleNumber > -1 ? cycleNumber : '-'}
} diff --git a/packages/page-coretime/src/Sale/index.tsx b/packages/page-coretime/src/Sale/index.tsx index 728416cd474..1b1ed52b6e2 100644 --- a/packages/page-coretime/src/Sale/index.tsx +++ b/packages/page-coretime/src/Sale/index.tsx @@ -2,6 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 import type { CoretimeInformation } from '@polkadot/react-hooks/types'; +import type { ChainName } from '../types.js'; import React, { useCallback, useMemo, useState } from 'react'; @@ -18,7 +19,7 @@ import Summary from './Summary.js'; interface Props { coretimeInfo: CoretimeInformation - chainName: string + chainName: ChainName } function Sale ({ chainName, coretimeInfo }: Props): React.ReactElement { @@ -44,7 +45,7 @@ function Sale ({ chainName, coretimeInfo }: Props): React.ReactElement { ] , [saleParams, t]); - const saleDetails = useMemo(() => calculateSaleDetails(chosenSaleNumber, saleParams?.cycleNumber, coretimeInfo.status.lastTimeslice * 80, chainName, coretimeInfo.config.regionLength, saleParams), [chosenSaleNumber, saleParams, coretimeInfo, chainName]); + const saleDetails = useMemo(() => chosenSaleNumber !== -1 ? calculateSaleDetails(chosenSaleNumber, saleParams?.cycleNumber, coretimeInfo.status.lastTimeslice * 80, chainName, coretimeInfo.config.regionLength, saleParams) : null, [chosenSaleNumber, saleParams, coretimeInfo, chainName]); const progressValues = useMemo(() => getSaleProgress(lastCommittedTimeslice, saleParams.currentRegion.start.ts, saleParams.interlude.ts, saleParams.leadin.ts, regionBegin), [saleParams, lastCommittedTimeslice, regionBegin]); @@ -129,7 +130,7 @@ function Sale ({ chainName, coretimeInfo }: Props): React.ReactElement { The button below will open a new tab with the correct query parameters for the chosen sale.

diff --git a/packages/page-coretime/src/types.ts b/packages/page-coretime/src/types.ts index 575de2341e1..4b393eb2d3b 100644 --- a/packages/page-coretime/src/types.ts +++ b/packages/page-coretime/src/types.ts @@ -75,3 +75,5 @@ export interface RegionInfo { regionBegin: number; regionEnd: number; } + +export type ChainName = 'kusama' | 'polkadot'; diff --git a/packages/page-coretime/src/utils.ts b/packages/page-coretime/src/utils.ts index d8983cdb90d..b5fe55ef01b 100644 --- a/packages/page-coretime/src/utils.ts +++ b/packages/page-coretime/src/utils.ts @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 import type { PalletBrokerConfigRecord } from '@polkadot/react-hooks/types'; -import type { PhaseConfig, PhaseProgress, RegionInfo, SaleDetails, SaleParameters } from './types.js'; +import type { ChainName, PhaseConfig, PhaseProgress, RegionInfo, SaleDetails, SaleParameters } from './types.js'; import { estimateTime, FirstCycleStart, get, getCurrentRegionStartEndTs, getCurrentSaleNumber } from './utils/index.js'; import { PhaseName } from './types.js'; @@ -11,18 +11,22 @@ export function calculateSaleDetails ( saleNumber: number, currentSaleNumber: number, latestBlock: number, - chainName: string, + chainName: ChainName, regionLength: number, - saleParams: any -): SaleDetails { + saleParams: SaleParameters +): SaleDetails | null { + if (saleNumber === -1) { + return null; + } + const blocksPerSaleRelay = get.blocks.relay(regionLength); - const saleStartBlock = saleParams.currentRegion.start.blocks - blocksPerSaleRelay * (currentSaleNumber - saleNumber); + const saleStartBlock = saleParams.currentRegion.start.blocks - blocksPerSaleRelay * (currentSaleNumber - saleNumber - 1); const saleEndBlock = saleStartBlock + blocksPerSaleRelay; const saleStartTs = get.timeslices.relay(saleStartBlock); const saleEndTs = get.timeslices.relay(saleEndBlock); - const saleStartBlockCoretime = FirstCycleStart[chainName] + get.blocks.coretime((saleNumber) * regionLength); + const saleStartBlockCoretime = FirstCycleStart.block.coretime[chainName] + get.blocks.coretime((saleNumber) * regionLength); const saleEndBlockCoretime = saleStartBlockCoretime + get.blocks.coretime(regionLength); const data = { @@ -62,7 +66,7 @@ export const constructSubscanQuery = (blockStart: number, blockEnd: number, chai export const getSaleParameters = ( salesInfo: RegionInfo, config: Pick, - chainName: string, + chainName: ChainName, lastCommittedTimeslice: number ): SaleParameters => { // The sale is happening on the coretime chain, so we need to convert the timeslices to blocks (40 blocks per timeslice) diff --git a/packages/page-coretime/src/utils/index.ts b/packages/page-coretime/src/utils/index.ts index b512261c440..fdfa6b57bd0 100644 --- a/packages/page-coretime/src/utils/index.ts +++ b/packages/page-coretime/src/utils/index.ts @@ -2,15 +2,27 @@ // SPDX-License-Identifier: Apache-2.0 import type { CoretimeInformation, PalletBrokerConfigRecord } from '@polkadot/react-hooks/types'; -import type { RegionInfo } from '../types.js'; +import type { ChainName, RegionInfo } from '../types.js'; import { CoreTimeChainConsts, CoreTimeConsts } from '@polkadot/react-hooks/types'; import { BN } from '@polkadot/util'; +type FirstCycleStartType = Record<'block' | 'timeslice', Record<'coretime', Record<'kusama' | 'polkadot', number>>>; + // Blocks on the Coretime Chain -export const FirstCycleStart: Record = { - kusama: 285768, - polkadot: 282525 +export const FirstCycleStart: FirstCycleStartType = { + block: { + coretime: { + kusama: 86947, + polkadot: 100988 + } + }, + timeslice: { + coretime: { + kusama: 285768, + polkadot: 282525 + } + } }; export function formatDate (date: Date) { @@ -75,14 +87,14 @@ export const estimateTime = (targetTimeslice: string | number, latestBlock: numb */ export const getCurrentSaleNumber = ( currentRegionEnd: number, - chainName: string, + chainName: ChainName, config: Pick ): number => { if (!chainName || !currentRegionEnd) { - return 0; + return -1; } - return Math.floor((currentRegionEnd - FirstCycleStart[chainName]) / config.regionLength) + 1; + return Math.ceil((currentRegionEnd - FirstCycleStart.timeslice.coretime[chainName]) / config.regionLength); }; /** From 2f6abed7cc548073890048794a467e710fbc9f21 Mon Sep 17 00:00:00 2001 From: Daria Mikhailova Date: Wed, 11 Dec 2024 21:19:59 +0800 Subject: [PATCH 10/14] added core price calculation --- packages/page-coretime/src/Sale/Summary.tsx | 7 +-- packages/page-coretime/src/Sale/index.tsx | 24 +++++++---- packages/page-coretime/src/utils/sale.ts | 48 +++++++++++++++++++++ 3 files changed, 68 insertions(+), 11 deletions(-) diff --git a/packages/page-coretime/src/Sale/Summary.tsx b/packages/page-coretime/src/Sale/Summary.tsx index b43df829a15..ff91ddb3aa6 100644 --- a/packages/page-coretime/src/Sale/Summary.tsx +++ b/packages/page-coretime/src/Sale/Summary.tsx @@ -4,6 +4,7 @@ import type { ApiPromise } from '@polkadot/api'; import type { BrokerStatus, CoreDescription, PalletBrokerConfigRecord, PalletBrokerSaleInfoRecord, RegionInfo } from '@polkadot/react-hooks/types'; +import { formatNumber } from 'chart.js/helpers'; import React from 'react'; import { CardSummary, SummaryBox } from '@polkadot/react-components'; @@ -22,7 +23,7 @@ interface Props { cycleNumber: number } -function Summary ({ config, cycleNumber, saleInfo, status }: Props): React.ReactElement { +function Summary({ config, cycleNumber, saleInfo, status }: Props): React.ReactElement { const { t } = useTranslation(); const { currentRegionEnd, currentRegionStart } = getCurrentRegionStartEndTs(saleInfo, config); @@ -43,10 +44,10 @@ function Summary ({ config, cycleNumber, saleInfo, status }: Props): React.React
{estimateTime(currentRegionEnd, status?.lastTimeslice * 80)}
-
{currentRegionEnd * 80}
+
{formatNumber(currentRegionEnd * 80)}
-
{currentRegionEnd}
+
{formatNumber(currentRegionEnd)}
{config && status && { const { salesInfo: { regionBegin }, status: { lastCommittedTimeslice } } = coretimeInfo; + const coretimePrice = useMemo(() => getCorePriceAt(lastCommittedTimeslice * 80, coretimeInfo.salesInfo), [lastCommittedTimeslice, coretimeInfo.salesInfo]); const saleParams = coretimeInfo && getSaleParameters(coretimeInfo.salesInfo, coretimeInfo.config, chainName, lastCommittedTimeslice); const phaseName = useMemo(() => saleParams?.phaseConfig?.currentPhaseName, [saleParams]); const [chosenSaleNumber, setChosenSaleNumber] = useState(-1); @@ -75,8 +77,14 @@ function Sale ({ chainName, coretimeInfo }: Props): React.ReactElement {
- - 100 DOT + + {phaseName === PhaseName.Renewals + ? ( + Cores cannot be purchased now + ) + : ( + {formatBalance(coretimePrice)} + )} {/* {!!available && {available}} {!available &&

Currently there are no cores available for purchase

} */} @@ -84,7 +92,7 @@ function Sale ({ chainName, coretimeInfo }: Props): React.ReactElement { {!!available &&
} + {/* TODO: Add core purchase functionality */} + {/* {
+
} */}
diff --git a/packages/page-coretime/src/index.tsx b/packages/page-coretime/src/index.tsx index 8b04217e7c3..b5076034f10 100644 --- a/packages/page-coretime/src/index.tsx +++ b/packages/page-coretime/src/index.tsx @@ -2,6 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 import type { TabItem } from '@polkadot/react-components/types'; +import type { ChainName } from './types.js'; import React, { useRef } from 'react'; import { Route, Routes } from 'react-router-dom'; @@ -37,7 +38,8 @@ function CoretimeApp ({ basePath, className }: Props): React.ReactElement const itemsRef = useRef(createItemsRef(t)); const { api, isApiReady } = useApi(); const coretimeInfo = useCoretimeInformation(api, isApiReady); - const chainName = useCall(api?.rpc.system.chain)?.toString().toLowerCase(); + + const chainName = useCall(api?.rpc.system.chain)?.toString().toLowerCase() as ChainName; return (
diff --git a/packages/page-coretime/src/types.ts b/packages/page-coretime/src/types.ts index 4b393eb2d3b..f731da1c492 100644 --- a/packages/page-coretime/src/types.ts +++ b/packages/page-coretime/src/types.ts @@ -76,4 +76,4 @@ export interface RegionInfo { regionEnd: number; } -export type ChainName = 'kusama' | 'polkadot'; +export type ChainName = 'kusama' | 'polkadot' | 'paseo testnet' | 'westend' diff --git a/packages/page-coretime/src/utils/index.ts b/packages/page-coretime/src/utils/index.ts index fdfa6b57bd0..35b9ed133ec 100644 --- a/packages/page-coretime/src/utils/index.ts +++ b/packages/page-coretime/src/utils/index.ts @@ -7,20 +7,30 @@ import type { ChainName, RegionInfo } from '../types.js'; import { CoreTimeChainConsts, CoreTimeConsts } from '@polkadot/react-hooks/types'; import { BN } from '@polkadot/util'; -type FirstCycleStartType = Record<'block' | 'timeslice', Record<'coretime', Record<'kusama' | 'polkadot', number>>>; +type FirstCycleStartType = Record< +'block' | 'timeslice', +Record< +'coretime', +Record +> +>; // Blocks on the Coretime Chain export const FirstCycleStart: FirstCycleStartType = { block: { coretime: { kusama: 86947, - polkadot: 100988 + 'paseo testnet': 22316, + polkadot: 100988, + westend: 7363 } }, timeslice: { coretime: { kusama: 285768, - polkadot: 282525 + 'paseo testnet': 38469, + polkadot: 282525, + westend: 245402 } } }; diff --git a/packages/react-hooks/src/useCoretimeInformation.ts b/packages/react-hooks/src/useCoretimeInformation.ts index 669a0a8decb..50c74a98370 100644 --- a/packages/react-hooks/src/useCoretimeInformation.ts +++ b/packages/react-hooks/src/useCoretimeInformation.ts @@ -67,16 +67,21 @@ function useCoretimeInformationImpl (api: ApiPromise, ready: boolean): CoretimeI const [state, setState] = useState(); useEffect(() => { - if (paraIds?.length && potentialRenewals?.length && !taskIds.length) { + if (paraIds?.length && !taskIds.length) { const simpleIds = paraIds.map((p) => Number(p)); const renewalIds = potentialRenewals?.map((r) => Number(r.task)); - const numbers = [...new Set(simpleIds.concat(renewalIds))]; - if (numbers?.length > simpleIds.length) { - setTaskIds(numbers.sort((a, b) => a - b)); - } else { - setTaskIds(simpleIds); + if (renewalIds) { + const numbers = [...new Set(simpleIds.concat(renewalIds))]; + + if (numbers?.length > simpleIds.length) { + setTaskIds(numbers.sort((a, b) => a - b)); + + return; + } } + + setTaskIds(simpleIds); } }, [potentialRenewals, paraIds, taskIds]); @@ -104,7 +109,7 @@ function useCoretimeInformationImpl (api: ApiPromise, ready: boolean): CoretimeI }, [workloads, coreInfos]); useEffect((): void => { - if (!workloadData?.length || !leases?.length || !reservations?.length) { + if (!workloadData?.length || !reservations?.length) { return; } @@ -112,7 +117,7 @@ function useCoretimeInformationImpl (api: ApiPromise, ready: boolean): CoretimeI taskIds?.forEach((id) => { const taskId = id.toString(); - const lease = leases?.find((lease) => lease.task === taskId); + const lease = leases?.length ? leases?.find((lease) => lease.task === taskId) : undefined; const reservation = reservations?.find((reservation) => reservation.task === taskId); const workloads = workloadData?.filter((one) => one.info.task === taskId); From 54c4090efbd13aeb8ec2b433692f94dcf8a1de1b Mon Sep 17 00:00:00 2001 From: Daria Mikhailova Date: Thu, 12 Dec 2024 01:34:28 +0800 Subject: [PATCH 12/14] ts fixes --- packages/page-coretime/src/Sale/Summary.tsx | 2 +- packages/page-coretime/src/utils.ts | 12 +++++++++--- packages/page-coretime/src/utils/index.ts | 4 ++-- 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/packages/page-coretime/src/Sale/Summary.tsx b/packages/page-coretime/src/Sale/Summary.tsx index a851e894f57..3e32c8b9578 100644 --- a/packages/page-coretime/src/Sale/Summary.tsx +++ b/packages/page-coretime/src/Sale/Summary.tsx @@ -24,7 +24,7 @@ interface Props { function Summary ({ config, cycleNumber, saleInfo, status }: Props): React.ReactElement { const { t } = useTranslation(); - const { currentRegionEnd, currentRegionStart } = getCurrentRegionStartEndTs(saleInfo, config); + const { currentRegionEnd, currentRegionStart } = getCurrentRegionStartEndTs(saleInfo, config.regionLength); return ( diff --git a/packages/page-coretime/src/utils.ts b/packages/page-coretime/src/utils.ts index b5fe55ef01b..01dccce769b 100644 --- a/packages/page-coretime/src/utils.ts +++ b/packages/page-coretime/src/utils.ts @@ -73,8 +73,14 @@ export const getSaleParameters = ( const interludeLengthTs = get.timeslices.coretime(config.interludeLength); const leadInLengthTs = get.timeslices.coretime(config.leadinLength); - const { currentRegionEnd, currentRegionStart } = getCurrentRegionStartEndTs(salesInfo, config); - const phaseConfig = getPhaseConfiguration(currentRegionStart, config.regionLength, interludeLengthTs, leadInLengthTs, lastCommittedTimeslice); + const { currentRegionEnd, currentRegionStart } = getCurrentRegionStartEndTs(salesInfo, config.regionLength); + const phaseConfig = getPhaseConfiguration( + currentRegionStart, + config.regionLength, + interludeLengthTs, + leadInLengthTs, + lastCommittedTimeslice + ); const saleNumber = getCurrentSaleNumber(currentRegionEnd, chainName, config); @@ -108,7 +114,7 @@ export const getPhaseConfiguration = ( regionLength: number, interludeLengthTs: number, leadInLengthTs: number, - lastCommittedTimeslice: number): PhaseConfig | undefined => { + lastCommittedTimeslice: number): PhaseConfig => { const renewalsEndTs = currentRegionStart + interludeLengthTs; const priceDiscoveryEndTs = renewalsEndTs + leadInLengthTs; const fixedPriceLenght = regionLength - interludeLengthTs - leadInLengthTs; diff --git a/packages/page-coretime/src/utils/index.ts b/packages/page-coretime/src/utils/index.ts index 35b9ed133ec..cc0141e3bdf 100644 --- a/packages/page-coretime/src/utils/index.ts +++ b/packages/page-coretime/src/utils/index.ts @@ -140,10 +140,10 @@ export const get = { * * @returns The start and end of the current region */ -export const getCurrentRegionStartEndTs = (saleInfo: RegionInfo, config: PalletBrokerConfigRecord) => { +export const getCurrentRegionStartEndTs = (saleInfo: RegionInfo, regionLength: number) => { return { currentRegionEnd: saleInfo.regionBegin, - currentRegionStart: saleInfo.regionBegin - config.regionLength + currentRegionStart: saleInfo.regionBegin - regionLength }; }; From dce5cd2f5413c2368c93601f8cbc6dae1ab42094 Mon Sep 17 00:00:00 2001 From: Daria Mikhailova Date: Thu, 12 Dec 2024 01:41:33 +0800 Subject: [PATCH 13/14] used helper funcitons for getting relay blocks --- packages/page-coretime/src/Overview/Summary.tsx | 6 +++--- packages/page-coretime/src/Row.tsx | 6 +++--- packages/page-coretime/src/Sale/Summary.tsx | 10 +++++----- packages/page-coretime/src/Sale/index.tsx | 8 ++++---- 4 files changed, 15 insertions(+), 15 deletions(-) diff --git a/packages/page-coretime/src/Overview/Summary.tsx b/packages/page-coretime/src/Overview/Summary.tsx index 88675d89b7c..89de17387b3 100644 --- a/packages/page-coretime/src/Overview/Summary.tsx +++ b/packages/page-coretime/src/Overview/Summary.tsx @@ -10,7 +10,7 @@ import { CardSummary, SummaryBox } from '@polkadot/react-components'; import { BN } from '@polkadot/util'; import { useTranslation } from '../translate.js'; -import { estimateTime, FirstCycleStart } from '../utils/index.js'; +import { estimateTime, FirstCycleStart, get } from '../utils/index.js'; interface Props { coreDscriptors?: CoreDescription[]; @@ -70,8 +70,8 @@ function Summary ({ chainName, config, parachainCount, saleInfo, status }: Props {status && (
-
{estimateTime(currentRegionStart, status?.lastTimeslice * 80)}
-
{estimateTime(currentRegionEnd, status?.lastTimeslice * 80)}
+
{estimateTime(currentRegionStart, get.blocks.relay(status?.lastTimeslice))}
+
{estimateTime(currentRegionEnd, get.blocks.relay(status?.lastTimeslice))}
) } diff --git a/packages/page-coretime/src/Row.tsx b/packages/page-coretime/src/Row.tsx index 5b9e3f5fa32..4c7e16486a9 100644 --- a/packages/page-coretime/src/Row.tsx +++ b/packages/page-coretime/src/Row.tsx @@ -10,7 +10,7 @@ import { ParaLink, styled, Tag } from '@polkadot/react-components'; import { ChainRenewalStatus } from '@polkadot/react-hooks/types'; import { BN, formatBalance, formatNumber } from '@polkadot/util'; -import { estimateTime } from './utils/index.js'; +import { estimateTime, get } from './utils/index.js'; import { CoreTimeTypes } from './types.js'; interface Props { @@ -57,11 +57,11 @@ function Row ({ chainRecord, highlight = false, id, lastCommittedTimeslice, leas {showEstimates && formatNumber(targetTimeslice * 80).toString()} + >{showEstimates && formatNumber(get.blocks.relay(targetTimeslice)).toString()} {showEstimates && estimateTime(targetTimeslice, lastCommittedTimeslice * 80)} + >{showEstimates && estimateTime(targetTimeslice, get.blocks.relay(lastCommittedTimeslice))} -
{estimateTime(currentRegionEnd, status?.lastTimeslice * 80)}
+
{estimateTime(currentRegionEnd, get.blocks.relay(status?.lastTimeslice))}
-
{formatNumber(currentRegionEnd * 80)}
+
{formatNumber(get.blocks.relay(currentRegionEnd))}
{formatNumber(currentRegionEnd)}
@@ -65,8 +65,8 @@ function Summary ({ config, cycleNumber, saleInfo, status }: Props): React.React {status && (
-
{estimateTime(currentRegionStart, status?.lastTimeslice * 80)}
-
{estimateTime(currentRegionEnd, status?.lastTimeslice * 80)}
+
{estimateTime(currentRegionStart, get.blocks.relay(status?.lastTimeslice))}
+
{estimateTime(currentRegionEnd, get.blocks.relay(status?.lastTimeslice))}
) } diff --git a/packages/page-coretime/src/Sale/index.tsx b/packages/page-coretime/src/Sale/index.tsx index 36d0919f1ca..9d60a0645b3 100644 --- a/packages/page-coretime/src/Sale/index.tsx +++ b/packages/page-coretime/src/Sale/index.tsx @@ -13,7 +13,7 @@ import { formatBalance, formatNumber } from '@polkadot/util'; import { useTranslation } from '../translate.js'; import { type ChainName, PhaseName } from '../types.js'; import { calculateSaleDetails, constructSubscanQuery, getSaleParameters, getSaleProgress } from '../utils.js'; -import { estimateTime } from '../utils/index.js'; +import { estimateTime, get } from '../utils/index.js'; import { getCorePriceAt } from '../utils/sale.js'; import SaleTable from './SaleTable.js'; import Summary from './Summary.js'; @@ -29,7 +29,7 @@ function Sale ({ chainName, coretimeInfo }: Props): React.ReactElement { const { salesInfo: { regionBegin }, status: { lastCommittedTimeslice } } = coretimeInfo; - const coretimePrice = useMemo(() => getCorePriceAt(lastCommittedTimeslice * 80, coretimeInfo.salesInfo), [lastCommittedTimeslice, coretimeInfo.salesInfo]); + const coretimePrice = useMemo(() => getCorePriceAt(get.blocks.relay(lastCommittedTimeslice), coretimeInfo.salesInfo), [lastCommittedTimeslice, coretimeInfo.salesInfo]); const saleParams = coretimeInfo && getSaleParameters(coretimeInfo.salesInfo, coretimeInfo.config, chainName, lastCommittedTimeslice); const phaseName = useMemo(() => saleParams?.phaseConfig?.currentPhaseName, [saleParams]); const [chosenSaleNumber, setChosenSaleNumber] = useState(-1); @@ -47,7 +47,7 @@ function Sale ({ chainName, coretimeInfo }: Props): React.ReactElement { ] , [saleParams, t]); - const saleDetails = useMemo(() => chosenSaleNumber !== -1 ? calculateSaleDetails(chosenSaleNumber, saleParams?.cycleNumber, coretimeInfo.status.lastTimeslice * 80, chainName, coretimeInfo.config.regionLength, saleParams) : null, [chosenSaleNumber, saleParams, coretimeInfo, chainName]); + const saleDetails = useMemo(() => chosenSaleNumber !== -1 ? calculateSaleDetails(chosenSaleNumber, saleParams?.cycleNumber, get.blocks.relay(coretimeInfo.status.lastTimeslice), chainName, coretimeInfo.config.regionLength, saleParams) : null, [chosenSaleNumber, saleParams, coretimeInfo, chainName]); const progressValues = useMemo(() => getSaleProgress(lastCommittedTimeslice, saleParams.currentRegion.start.ts, saleParams.interlude.ts, saleParams.leadin.ts, regionBegin), [saleParams, lastCommittedTimeslice, regionBegin]); @@ -102,7 +102,7 @@ function Sale ({ chainName, coretimeInfo }: Props): React.ReactElement {
{phaseName && phaseName} - {phaseName && saleParams?.phaseConfig && estimateTime(saleParams.phaseConfig.config[phaseName].lastTimeslice, coretimeInfo.status.lastTimeslice * 80)} + {phaseName && saleParams?.phaseConfig && estimateTime(saleParams.phaseConfig.config[phaseName].lastTimeslice, get.blocks.relay(coretimeInfo.status.lastTimeslice))} {phaseName && formatNumber(saleParams?.phaseConfig?.config[phaseName].lastBlock)} {formatBalance(coretimeInfo?.salesInfo.endPrice)} {formatBalance(coretimeInfo?.salesInfo.selloutPrice)} From f73cf98d5bc3cac73cff80fb1ffe06df32267753 Mon Sep 17 00:00:00 2001 From: Daria Mikhailova Date: Thu, 12 Dec 2024 01:54:04 +0800 Subject: [PATCH 14/14] moved functionality to a new helper --- packages/page-coretime/src/Sale/index.tsx | 5 +- packages/page-coretime/src/utils.ts | 185 -------------------- packages/page-coretime/src/utils/index.ts | 32 ++-- packages/page-coretime/src/utils/sale.ts | 196 +++++++++++++++++++++- 4 files changed, 207 insertions(+), 211 deletions(-) delete mode 100644 packages/page-coretime/src/utils.ts diff --git a/packages/page-coretime/src/Sale/index.tsx b/packages/page-coretime/src/Sale/index.tsx index 9d60a0645b3..630afa61150 100644 --- a/packages/page-coretime/src/Sale/index.tsx +++ b/packages/page-coretime/src/Sale/index.tsx @@ -12,9 +12,8 @@ import { formatBalance, formatNumber } from '@polkadot/util'; import { useTranslation } from '../translate.js'; import { type ChainName, PhaseName } from '../types.js'; -import { calculateSaleDetails, constructSubscanQuery, getSaleParameters, getSaleProgress } from '../utils.js'; -import { estimateTime, get } from '../utils/index.js'; -import { getCorePriceAt } from '../utils/sale.js'; +import { constructSubscanQuery, estimateTime, get } from '../utils/index.js'; +import { calculateSaleDetails, getCorePriceAt, getSaleParameters, getSaleProgress } from '../utils/sale.js'; import SaleTable from './SaleTable.js'; import Summary from './Summary.js'; diff --git a/packages/page-coretime/src/utils.ts b/packages/page-coretime/src/utils.ts deleted file mode 100644 index 01dccce769b..00000000000 --- a/packages/page-coretime/src/utils.ts +++ /dev/null @@ -1,185 +0,0 @@ -// Copyright 2017-2024 @polkadot/app-coretime authors & contributors -// SPDX-License-Identifier: Apache-2.0 - -import type { PalletBrokerConfigRecord } from '@polkadot/react-hooks/types'; -import type { ChainName, PhaseConfig, PhaseProgress, RegionInfo, SaleDetails, SaleParameters } from './types.js'; - -import { estimateTime, FirstCycleStart, get, getCurrentRegionStartEndTs, getCurrentSaleNumber } from './utils/index.js'; -import { PhaseName } from './types.js'; - -export function calculateSaleDetails ( - saleNumber: number, - currentSaleNumber: number, - latestBlock: number, - chainName: ChainName, - regionLength: number, - saleParams: SaleParameters -): SaleDetails | null { - if (saleNumber === -1) { - return null; - } - - const blocksPerSaleRelay = get.blocks.relay(regionLength); - const saleStartBlock = saleParams.currentRegion.start.blocks - blocksPerSaleRelay * (currentSaleNumber - saleNumber - 1); - const saleEndBlock = saleStartBlock + blocksPerSaleRelay; - - const saleStartTs = get.timeslices.relay(saleStartBlock); - const saleEndTs = get.timeslices.relay(saleEndBlock); - - const saleStartBlockCoretime = FirstCycleStart.block.coretime[chainName] + get.blocks.coretime((saleNumber) * regionLength); - const saleEndBlockCoretime = saleStartBlockCoretime + get.blocks.coretime(regionLength); - - const data = { - coretime: { - end: { block: saleEndBlockCoretime }, - start: { block: saleStartBlockCoretime } - }, - date: { - end: estimateTime(saleEndTs, latestBlock), - start: estimateTime(saleStartTs, latestBlock) - }, - relay: { - end: { - block: saleEndBlock, - ts: saleEndTs - }, - start: { - block: saleStartBlock, - ts: saleStartTs - } - }, - saleNumber - }; - - return data; -} - -export const constructSubscanQuery = (blockStart: number, blockEnd: number, chainName: string, module = 'broker', call = 'purchase') => { - const page = 1; - const pageSize = 25; - const signed = 'all'; - const baseURL = `https://coretime-${chainName}.subscan.io/extrinsic`; - - return `${baseURL}?page=${page}&time_dimension=block&page_size=${pageSize}&module=${module}&signed=${signed}&call=${call}&block_start=${blockStart}&block_end=${blockEnd}`; -}; - -export const getSaleParameters = ( - salesInfo: RegionInfo, - config: Pick, - chainName: ChainName, - lastCommittedTimeslice: number -): SaleParameters => { - // The sale is happening on the coretime chain, so we need to convert the timeslices to blocks (40 blocks per timeslice) - const interludeLengthTs = get.timeslices.coretime(config.interludeLength); - const leadInLengthTs = get.timeslices.coretime(config.leadinLength); - - const { currentRegionEnd, currentRegionStart } = getCurrentRegionStartEndTs(salesInfo, config.regionLength); - const phaseConfig = getPhaseConfiguration( - currentRegionStart, - config.regionLength, - interludeLengthTs, - leadInLengthTs, - lastCommittedTimeslice - ); - - const saleNumber = getCurrentSaleNumber(currentRegionEnd, chainName, config); - - return { - currentRegion: { - end: { - blocks: get.blocks.relay(currentRegionEnd), - ts: currentRegionEnd - }, - start: { - blocks: get.blocks.relay(currentRegionStart), - ts: currentRegionStart - } - }, - cycleNumber: getCurrentSaleNumber(currentRegionEnd, chainName, config), - interlude: { - blocks: config.interludeLength, - ts: interludeLengthTs - }, - leadin: { - blocks: config.leadinLength, - ts: leadInLengthTs - }, - phaseConfig, - regionNumber: saleNumber - 1 - }; -}; - -export const getPhaseConfiguration = ( - currentRegionStart: number, - regionLength: number, - interludeLengthTs: number, - leadInLengthTs: number, - lastCommittedTimeslice: number): PhaseConfig => { - const renewalsEndTs = currentRegionStart + interludeLengthTs; - const priceDiscoveryEndTs = renewalsEndTs + leadInLengthTs; - const fixedPriceLenght = regionLength - interludeLengthTs - leadInLengthTs; - const fixedPriceEndTs = priceDiscoveryEndTs + fixedPriceLenght; - - return { - config: { - [PhaseName.Renewals]: { - lastBlock: get.blocks.relay(renewalsEndTs), - lastTimeslice: renewalsEndTs - }, - [PhaseName.PriceDiscovery]: { - lastBlock: get.blocks.relay(priceDiscoveryEndTs), - lastTimeslice: priceDiscoveryEndTs - }, - [PhaseName.FixedPrice]: { - lastBlock: get.blocks.relay(fixedPriceEndTs), - lastTimeslice: fixedPriceEndTs - } - }, - currentPhaseName: determinePhaseName(lastCommittedTimeslice, currentRegionStart, interludeLengthTs, leadInLengthTs) - }; -}; - -export const determinePhaseName = ( - lastCommittedTimeslice: number, - currentRegionStart: number, - interludeLengthTs: number, - leadInLengthTs: number): typeof PhaseName[keyof typeof PhaseName] => { - const progress = lastCommittedTimeslice - currentRegionStart; - - if (progress < interludeLengthTs) { - return PhaseName.Renewals; - } - - if (progress < interludeLengthTs + leadInLengthTs) { - return PhaseName.PriceDiscovery; - } - - return PhaseName.FixedPrice; -}; - -export const getSaleProgress = ( - lastCommittedTimeslice: number, - currentRegionStart: number, - interludeLengthTs: number, - leadInLengthTs: number, - regionBegin: number): PhaseProgress[] => { - const progress = lastCommittedTimeslice - currentRegionStart; - - return [ - { - label: PhaseName.Renewals, - total: interludeLengthTs, - value: Math.min(progress, interludeLengthTs) - }, - { - label: PhaseName.PriceDiscovery, - total: leadInLengthTs, - value: Math.min(Math.max(progress - interludeLengthTs, 0), leadInLengthTs) - }, - { - label: PhaseName.FixedPrice, - total: regionBegin - currentRegionStart - interludeLengthTs - leadInLengthTs, - value: Math.max(progress - interludeLengthTs - leadInLengthTs, 0) - } - ]; -}; diff --git a/packages/page-coretime/src/utils/index.ts b/packages/page-coretime/src/utils/index.ts index cc0141e3bdf..75ca5015abe 100644 --- a/packages/page-coretime/src/utils/index.ts +++ b/packages/page-coretime/src/utils/index.ts @@ -1,7 +1,7 @@ // Copyright 2017-2024 @polkadot/app-coretime authors & contributors // SPDX-License-Identifier: Apache-2.0 -import type { CoretimeInformation, PalletBrokerConfigRecord } from '@polkadot/react-hooks/types'; +import type { CoretimeInformation } from '@polkadot/react-hooks/types'; import type { ChainName, RegionInfo } from '../types.js'; import { CoreTimeChainConsts, CoreTimeConsts } from '@polkadot/react-hooks/types'; @@ -86,27 +86,6 @@ export const estimateTime = (targetTimeslice: string | number, latestBlock: numb } }; -/** - * Get the current sale number - * - * @param currentRegionEnd - The end of the current region - * @param chainName - The name of the chain - * @param config - broker.configuration call response - * - * @returns The current sale number - */ -export const getCurrentSaleNumber = ( - currentRegionEnd: number, - chainName: ChainName, - config: Pick -): number => { - if (!chainName || !currentRegionEnd) { - return -1; - } - - return Math.ceil((currentRegionEnd - FirstCycleStart.timeslice.coretime[chainName]) / config.regionLength); -}; - /** * Helper functions to convert timeslices to blocks and vice versa */ @@ -149,3 +128,12 @@ export const getCurrentRegionStartEndTs = (saleInfo: RegionInfo, regionLength: n export const getAvailableNumberOfCores = (coretimeInfo: CoretimeInformation) => Number(coretimeInfo?.salesInfo?.coresOffered) - Number(coretimeInfo?.salesInfo.coresSold); + +export const constructSubscanQuery = (blockStart: number, blockEnd: number, chainName: string, module = 'broker', call = 'purchase') => { + const page = 1; + const pageSize = 25; + const signed = 'all'; + const baseURL = `https://coretime-${chainName}.subscan.io/extrinsic`; + + return `${baseURL}?page=${page}&time_dimension=block&page_size=${pageSize}&module=${module}&signed=${signed}&call=${call}&block_start=${blockStart}&block_end=${blockEnd}`; +}; diff --git a/packages/page-coretime/src/utils/sale.ts b/packages/page-coretime/src/utils/sale.ts index 0c597c3ab58..2700702738c 100644 --- a/packages/page-coretime/src/utils/sale.ts +++ b/packages/page-coretime/src/utils/sale.ts @@ -1,10 +1,14 @@ // Copyright 2017-2024 @polkadot/app-coretime authors & contributors // SPDX-License-Identifier: Apache-2.0 -import type { PalletBrokerSaleInfoRecord } from '@polkadot/react-hooks/types'; +import type { PalletBrokerConfigRecord, PalletBrokerSaleInfoRecord } from '@polkadot/react-hooks/types'; +import type { ChainName, PhaseConfig, PhaseProgress, RegionInfo, SaleDetails, SaleParameters } from '../types.js'; import { BN } from '@polkadot/util'; +import { PhaseName } from '../types.js'; +import { estimateTime, FirstCycleStart, get, getCurrentRegionStartEndTs } from './index.js'; + // We are scaling everything to avoid floating point precision issues. const SCALE = new BN(10000); @@ -48,3 +52,193 @@ export const getCorePriceAt = (blockNow: number, saleInfo: PalletBrokerSaleInfoR return scaledPrice; }; + +/** + * Get the current sale number + * + * @param currentRegionEnd - The end of the current region + * @param chainName - The name of the chain + * @param config - broker.configuration call response + * + * @returns The current sale number + */ +export const getCurrentSaleNumber = ( + currentRegionEnd: number, + chainName: ChainName, + config: Pick +): number => { + if (!chainName || !currentRegionEnd) { + return -1; + } + + return Math.ceil((currentRegionEnd - FirstCycleStart.timeslice.coretime[chainName]) / config.regionLength); +}; + +export const determinePhaseName = ( + lastCommittedTimeslice: number, + currentRegionStart: number, + interludeLengthTs: number, + leadInLengthTs: number): typeof PhaseName[keyof typeof PhaseName] => { + const progress = lastCommittedTimeslice - currentRegionStart; + + if (progress < interludeLengthTs) { + return PhaseName.Renewals; + } + + if (progress < interludeLengthTs + leadInLengthTs) { + return PhaseName.PriceDiscovery; + } + + return PhaseName.FixedPrice; +}; + +export const getSaleProgress = ( + lastCommittedTimeslice: number, + currentRegionStart: number, + interludeLengthTs: number, + leadInLengthTs: number, + regionBegin: number): PhaseProgress[] => { + const progress = lastCommittedTimeslice - currentRegionStart; + + return [ + { + label: PhaseName.Renewals, + total: interludeLengthTs, + value: Math.min(progress, interludeLengthTs) + }, + { + label: PhaseName.PriceDiscovery, + total: leadInLengthTs, + value: Math.min(Math.max(progress - interludeLengthTs, 0), leadInLengthTs) + }, + { + label: PhaseName.FixedPrice, + total: regionBegin - currentRegionStart - interludeLengthTs - leadInLengthTs, + value: Math.max(progress - interludeLengthTs - leadInLengthTs, 0) + } + ]; +}; + +const getPhaseConfiguration = ( + currentRegionStart: number, + regionLength: number, + interludeLengthTs: number, + leadInLengthTs: number, + lastCommittedTimeslice: number +): PhaseConfig => { + const renewalsEndTs = currentRegionStart + interludeLengthTs; + const priceDiscoveryEndTs = renewalsEndTs + leadInLengthTs; + const fixedPriceLenght = regionLength - interludeLengthTs - leadInLengthTs; + const fixedPriceEndTs = priceDiscoveryEndTs + fixedPriceLenght; + + return { + config: { + [PhaseName.Renewals]: { + lastBlock: get.blocks.relay(renewalsEndTs), + lastTimeslice: renewalsEndTs + }, + [PhaseName.PriceDiscovery]: { + lastBlock: get.blocks.relay(priceDiscoveryEndTs), + lastTimeslice: priceDiscoveryEndTs + }, + [PhaseName.FixedPrice]: { + lastBlock: get.blocks.relay(fixedPriceEndTs), + lastTimeslice: fixedPriceEndTs + } + }, + currentPhaseName: determinePhaseName(lastCommittedTimeslice, currentRegionStart, interludeLengthTs, leadInLengthTs) + }; +}; + +export const getSaleParameters = ( + salesInfo: RegionInfo, + config: Pick, + chainName: ChainName, + lastCommittedTimeslice: number +): SaleParameters => { + // The sale is happening on the coretime chain, so we need to convert the timeslices to blocks (40 blocks per timeslice) + const interludeLengthTs = get.timeslices.coretime(config.interludeLength); + const leadInLengthTs = get.timeslices.coretime(config.leadinLength); + + const { currentRegionEnd, currentRegionStart } = getCurrentRegionStartEndTs(salesInfo, config.regionLength); + const phaseConfig = getPhaseConfiguration( + currentRegionStart, + config.regionLength, + interludeLengthTs, + leadInLengthTs, + lastCommittedTimeslice + ); + + const saleNumber = getCurrentSaleNumber(currentRegionEnd, chainName, config); + + return { + currentRegion: { + end: { + blocks: get.blocks.relay(currentRegionEnd), + ts: currentRegionEnd + }, + start: { + blocks: get.blocks.relay(currentRegionStart), + ts: currentRegionStart + } + }, + cycleNumber: getCurrentSaleNumber(currentRegionEnd, chainName, config), + interlude: { + blocks: config.interludeLength, + ts: interludeLengthTs + }, + leadin: { + blocks: config.leadinLength, + ts: leadInLengthTs + }, + phaseConfig, + regionNumber: saleNumber - 1 + }; +}; + +export function calculateSaleDetails ( + saleNumber: number, + currentSaleNumber: number, + latestBlock: number, + chainName: ChainName, + regionLength: number, + saleParams: SaleParameters +): SaleDetails | null { + if (saleNumber === -1) { + return null; + } + + const blocksPerSaleRelay = get.blocks.relay(regionLength); + const saleStartBlock = saleParams.currentRegion.start.blocks - blocksPerSaleRelay * (currentSaleNumber - saleNumber - 1); + const saleEndBlock = saleStartBlock + blocksPerSaleRelay; + + const saleStartTs = get.timeslices.relay(saleStartBlock); + const saleEndTs = get.timeslices.relay(saleEndBlock); + + const saleStartBlockCoretime = FirstCycleStart.block.coretime[chainName] + get.blocks.coretime((saleNumber) * regionLength); + const saleEndBlockCoretime = saleStartBlockCoretime + get.blocks.coretime(regionLength); + + const data = { + coretime: { + end: { block: saleEndBlockCoretime }, + start: { block: saleStartBlockCoretime } + }, + date: { + end: estimateTime(saleEndTs, latestBlock), + start: estimateTime(saleStartTs, latestBlock) + }, + relay: { + end: { + block: saleEndBlock, + ts: saleEndTs + }, + start: { + block: saleStartBlock, + ts: saleStartTs + } + }, + saleNumber + }; + + return data; +}