Feedback/#11253 (#11304)
* countdown component * filtering by parachain id in workplan and workload * lint * rename chainName to relayName, fixed subscan block link * always showing price even if the cores are sold out * moved some calculations to the coretime context * added broker context for page-broker, fix for wrong dates * showing time with the estimated date in places * removed extra code when calculating renewal status * tsc for page-broker * page-coretime tsc * fix showing correct dates for fixed price phase in sale history * minified workinforow as all rows show the same information now * default values for fn input * showing start price of the sale on the sale tab * replaced custom Countdown element with existing implementation * adjusted estimateTime function for broker * adjusted estimateTime function for coretime page * lint * added Renewal link for RegionX
This commit is contained in:
parent
bd71a8e9b8
commit
468a3d5107
83
packages/page-broker/src/BrokerContext.tsx
Normal file
83
packages/page-broker/src/BrokerContext.tsx
Normal file
@ -0,0 +1,83 @@
|
||||
// Copyright 2017-2025 @polkadot/app-broker authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import type { ReactNode } from 'react';
|
||||
import type { ApiPromise } from '@polkadot/api';
|
||||
import type { BrokerStatus, ChainConstants, PalletBrokerConfigRecord, PalletBrokerSaleInfoRecord } from '@polkadot/react-hooks/types';
|
||||
import type { CurrentRegion } from './types.js';
|
||||
|
||||
import React, { createContext, useContext, useMemo } from 'react';
|
||||
|
||||
import { useBrokerConfig, useBrokerSalesInfo, useBrokerStatus, useCoretimeConsts } from '@polkadot/react-hooks';
|
||||
|
||||
import { estimateTime } from './utils.js';
|
||||
|
||||
interface BrokerProviderProps {
|
||||
children: ReactNode;
|
||||
api: ApiPromise;
|
||||
isApiReady: boolean;
|
||||
}
|
||||
|
||||
interface BrokerContextProps {
|
||||
config: PalletBrokerConfigRecord | null,
|
||||
coretimeConsts: ChainConstants | null,
|
||||
currentRegion: CurrentRegion,
|
||||
saleInfo: PalletBrokerSaleInfoRecord | null,
|
||||
status: BrokerStatus | null,
|
||||
}
|
||||
|
||||
const initialState = {
|
||||
config: null,
|
||||
coretimeConsts: null,
|
||||
currentRegion: {
|
||||
begin: null,
|
||||
beginDate: null,
|
||||
end: null,
|
||||
endDate: null
|
||||
},
|
||||
saleInfo: null,
|
||||
status: null
|
||||
};
|
||||
|
||||
export const BrokerContext = createContext<BrokerContextProps>(initialState);
|
||||
|
||||
export const BrokerProvider = ({ api, children, isApiReady }: BrokerProviderProps) => {
|
||||
const coretimeConsts = useCoretimeConsts();
|
||||
const config = useBrokerConfig(api, isApiReady);
|
||||
const saleInfo = useBrokerSalesInfo(api, isApiReady);
|
||||
const status = useBrokerStatus(api, isApiReady);
|
||||
|
||||
const currentRegionEnd = useMemo(() => saleInfo?.regionBegin, [saleInfo]);
|
||||
const currentRegionBegin = useMemo(() => saleInfo && config && saleInfo?.regionBegin - config?.regionLength, [saleInfo, config]);
|
||||
const lastBlock = useMemo(() => status && coretimeConsts && status.lastTimeslice * coretimeConsts.relay.blocksPerTimeslice, [status, coretimeConsts]);
|
||||
|
||||
const currentRegionEndDate = useMemo(() => currentRegionEnd && lastBlock && estimateTime(Number(currentRegionEnd), lastBlock)?.formattedDate, [currentRegionEnd, lastBlock]);
|
||||
const currentRegionBeginDate = useMemo(() => currentRegionBegin && lastBlock && estimateTime(Number(currentRegionBegin), lastBlock)?.formattedDate, [currentRegionBegin, lastBlock]);
|
||||
|
||||
const value = useMemo(() => {
|
||||
if (!config || !saleInfo || !status || !currentRegionBegin || !currentRegionBeginDate || !currentRegionEnd || !currentRegionEndDate || !coretimeConsts) {
|
||||
return initialState;
|
||||
}
|
||||
|
||||
return ({
|
||||
config,
|
||||
coretimeConsts,
|
||||
currentRegion: {
|
||||
begin: currentRegionBegin,
|
||||
beginDate: currentRegionBeginDate,
|
||||
end: currentRegionEnd,
|
||||
endDate: currentRegionEndDate
|
||||
},
|
||||
saleInfo,
|
||||
status
|
||||
});
|
||||
}, [currentRegionBegin, currentRegionEnd, currentRegionBeginDate, currentRegionEndDate, config, saleInfo, status, coretimeConsts]);
|
||||
|
||||
return (
|
||||
<BrokerContext.Provider value={value}>
|
||||
{children}
|
||||
</BrokerContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
export const useBrokerContext = () => useContext(BrokerContext);
|
||||
@ -27,7 +27,7 @@ function CoreTable ({ api, config, core, workload, workplan }: Props): React.Rea
|
||||
[
|
||||
<div key={`header${core}`}>{headerRef.current} {core} <span></span></div>,
|
||||
'core',
|
||||
8,
|
||||
9,
|
||||
undefined
|
||||
]
|
||||
];
|
||||
|
||||
@ -6,8 +6,7 @@ import type { CoreInfo } from '../types.js';
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import { useBrokerConfig } from '@polkadot/react-hooks';
|
||||
|
||||
import { useBrokerContext } from '../BrokerContext.js';
|
||||
import CoreTable from './CoreTable.js';
|
||||
|
||||
interface Props {
|
||||
@ -16,8 +15,8 @@ interface Props {
|
||||
data: CoreInfo[];
|
||||
}
|
||||
|
||||
function CoresTable ({ api, data, isApiReady }: Props): React.ReactElement<Props> {
|
||||
const config = useBrokerConfig(api, isApiReady);
|
||||
function CoresTable ({ api, data }: Props): React.ReactElement<Props> {
|
||||
const { config } = useBrokerContext();
|
||||
|
||||
return (
|
||||
<>
|
||||
|
||||
@ -23,7 +23,7 @@ interface Props {
|
||||
|
||||
const filterLoad = (parachainId: string, data: CoreInfo[], workloadCoreSelected: number): CoreInfo[] => {
|
||||
if (parachainId) {
|
||||
return data.filter(({ workload }) => !!workload?.filter(({ info }) => info.task === parachainId).length);
|
||||
return data.filter(({ workload, workplan }) => !!workload?.filter(({ info }) => info.task === parachainId).length || !!workplan?.filter(({ info }) => info.task === parachainId).length);
|
||||
}
|
||||
|
||||
if (workloadCoreSelected === -1) {
|
||||
|
||||
@ -4,17 +4,17 @@
|
||||
import type { LinkOption } from '@polkadot/apps-config/endpoints/types';
|
||||
import type { statsType } from '../types.js';
|
||||
|
||||
import React, { useMemo } from 'react';
|
||||
import React from 'react';
|
||||
|
||||
import { CardSummary, styled, SummaryBox, UsageBar } from '@polkadot/react-components';
|
||||
import { defaultHighlight } from '@polkadot/react-components/styles';
|
||||
import { useApi, useBrokerConfig, useBrokerSalesInfo, useBrokerStatus } from '@polkadot/react-hooks';
|
||||
import { useApi } from '@polkadot/react-hooks';
|
||||
import { type CoreWorkload } from '@polkadot/react-hooks/types';
|
||||
import { useCoretimeConsts } from '@polkadot/react-hooks/useCoretimeConsts';
|
||||
import { BN, BN_ZERO } from '@polkadot/util';
|
||||
|
||||
import { useBrokerContext } from '../BrokerContext.js';
|
||||
import { useTranslation } from '../translate.js';
|
||||
import { estimateTime, getStats } from '../utils.js';
|
||||
import { getStats } from '../utils.js';
|
||||
import RegionLength from './Summary/RegionLength.js';
|
||||
import Timeslice from './Summary/Timeslice.js';
|
||||
import TimeslicePeriod from './Summary/TimeslicePeriod.js';
|
||||
@ -35,23 +35,17 @@ const StyledSection = styled.section`
|
||||
`;
|
||||
|
||||
interface Props {
|
||||
coreCount?: string
|
||||
apiEndpoint?: LinkOption | null;
|
||||
coreCount?: string
|
||||
workloadInfos?: CoreWorkload[]
|
||||
}
|
||||
|
||||
function Summary ({ coreCount, workloadInfos }: Props): React.ReactElement {
|
||||
const { t } = useTranslation();
|
||||
const { api, apiEndpoint, isApiReady } = useApi();
|
||||
const coretimeConstants = useCoretimeConsts();
|
||||
const { api, apiEndpoint } = useApi();
|
||||
const uiHighlight = apiEndpoint?.ui.color || defaultHighlight;
|
||||
const { idles, pools, tasks }: statsType = React.useMemo(() => getStats(coreCount, workloadInfos), [coreCount, workloadInfos]);
|
||||
|
||||
const saleInfo = useBrokerSalesInfo(api, isApiReady);
|
||||
const config = useBrokerConfig(api, isApiReady);
|
||||
const status = useBrokerStatus(api, isApiReady);
|
||||
const currentRegionEnd = useMemo(() => saleInfo && config && saleInfo?.regionEnd - config?.regionLength, [saleInfo, config]);
|
||||
const currentRegionStart = useMemo(() => currentRegionEnd && config && currentRegionEnd - config?.regionLength, [currentRegionEnd, config]);
|
||||
const { config, currentRegion, saleInfo, status } = useBrokerContext();
|
||||
|
||||
return (
|
||||
<SummaryBox>
|
||||
@ -81,7 +75,7 @@ function Summary ({ coreCount, workloadInfos }: Props): React.ReactElement {
|
||||
progress={{
|
||||
isBlurred: false,
|
||||
total: new BN(config?.regionLength || 0),
|
||||
value: (config?.regionLength && currentRegionEnd && status && new BN(config?.regionLength - (currentRegionEnd - status?.lastTimeslice))) || BN_ZERO,
|
||||
value: (config?.regionLength && currentRegion.end && status && new BN(config?.regionLength - (currentRegion.end - status?.lastTimeslice))) || BN_ZERO,
|
||||
withTime: false
|
||||
}}
|
||||
/>
|
||||
@ -104,7 +98,7 @@ function Summary ({ coreCount, workloadInfos }: Props): React.ReactElement {
|
||||
</div>
|
||||
</StyledSection>
|
||||
<section>
|
||||
{status && currentRegionStart && currentRegionEnd &&
|
||||
{currentRegion.begin && currentRegion.end &&
|
||||
(
|
||||
<>
|
||||
<CardSummary
|
||||
@ -112,8 +106,8 @@ function Summary ({ coreCount, workloadInfos }: Props): React.ReactElement {
|
||||
label={t('sale dates')}
|
||||
>
|
||||
<div>
|
||||
<div style={{ fontSize: '14px' }}>{estimateTime(currentRegionStart, status?.lastTimeslice * 80, coretimeConstants?.relay)}</div>
|
||||
<div style={{ fontSize: '14px' }}>{estimateTime(currentRegionEnd, status?.lastTimeslice * 80, coretimeConstants?.relay)}</div>
|
||||
<div style={{ fontSize: '14px' }}>{currentRegion.beginDate}</div>
|
||||
<div style={{ fontSize: '14px' }}>{currentRegion.endDate}</div>
|
||||
</div>
|
||||
</CardSummary>
|
||||
<CardSummary
|
||||
@ -121,8 +115,8 @@ function Summary ({ coreCount, workloadInfos }: Props): React.ReactElement {
|
||||
label={t('sale ts')}
|
||||
>
|
||||
<div>
|
||||
<div style={{ fontSize: '14px' }}>{currentRegionStart}</div>
|
||||
<div style={{ fontSize: '14px' }}>{currentRegionEnd}</div>
|
||||
<div style={{ fontSize: '14px' }}>{currentRegion.begin}</div>
|
||||
<div style={{ fontSize: '14px' }}>{currentRegion.end}</div>
|
||||
</div>
|
||||
</CardSummary>
|
||||
</>
|
||||
|
||||
@ -50,150 +50,62 @@ function WorkInfoRow ({ data }: { data: InfoRow }): React.ReactElement {
|
||||
return (
|
||||
<>
|
||||
<td style={{ width: 200 }}>no task</td>
|
||||
<td colSpan={6} />
|
||||
<td colSpan={7} />
|
||||
</>);
|
||||
}
|
||||
|
||||
switch (data.type) {
|
||||
case (CoreTimeTypes.Reservation): {
|
||||
return (
|
||||
<>
|
||||
<TableCol
|
||||
header='Task'
|
||||
value={data.task}
|
||||
/>
|
||||
<TableCol
|
||||
header='Blocks per timeslice'
|
||||
value={data.maskBits}
|
||||
/>
|
||||
<td>
|
||||
return (
|
||||
<>
|
||||
<TableCol
|
||||
header='Task'
|
||||
value={data.task}
|
||||
/>
|
||||
<TableCol
|
||||
header='Blocks per timeslice'
|
||||
value={data.maskBits}
|
||||
/>
|
||||
<TableCol
|
||||
header='Start ts'
|
||||
hide='both'
|
||||
value={data.startTimeslice}
|
||||
/>
|
||||
<TableCol
|
||||
header='Start date'
|
||||
hide='both'
|
||||
value={data.start}
|
||||
/>
|
||||
<TableCol
|
||||
header='End date'
|
||||
hide='both'
|
||||
value={data.end}
|
||||
/>
|
||||
<TableCol
|
||||
header='Last block (relay)'
|
||||
value={data.endBlock}
|
||||
/>
|
||||
<StyledTableCol hide={'mobile'}>
|
||||
<h5 style={{ opacity: '0.6' }}>type</h5>
|
||||
{data.type &&
|
||||
(
|
||||
<Tag
|
||||
color={colours[data.type] as FlagColor}
|
||||
label={Object.values(CoreTimeTypes)[data.type]}
|
||||
/>
|
||||
</td>
|
||||
<td colSpan={4} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
)
|
||||
}
|
||||
</StyledTableCol>
|
||||
{data.owner
|
||||
? <StyledTableCol hide='mobile'>
|
||||
<h5 style={{ opacity: '0.6' }}>{'Owner'}</h5>
|
||||
<AddressMini
|
||||
isPadded={false}
|
||||
key={data.owner}
|
||||
value={data.owner}
|
||||
/>
|
||||
|
||||
case (CoreTimeTypes.Lease):
|
||||
return (
|
||||
<>
|
||||
<TableCol
|
||||
header='Task'
|
||||
value={data.task}
|
||||
/>
|
||||
<TableCol
|
||||
header='Blocks per timeslice'
|
||||
value={data.maskBits}
|
||||
/>
|
||||
<TableCol
|
||||
header='Start'
|
||||
hide='both'
|
||||
value={data.start}
|
||||
/>
|
||||
<TableCol
|
||||
header='End'
|
||||
hide='both'
|
||||
value={data.end}
|
||||
/>
|
||||
<TableCol
|
||||
header='Last block'
|
||||
value={data.endBlock}
|
||||
/>
|
||||
<StyledTableCol hide={'mobile'}>
|
||||
<h5 style={{ opacity: '0.6' }}>type</h5>
|
||||
<Tag
|
||||
color={colours[data.type] as FlagColor}
|
||||
label={Object.values(CoreTimeTypes)[data.type]}
|
||||
/>
|
||||
</StyledTableCol>
|
||||
<td colSpan={1} />
|
||||
</>);
|
||||
|
||||
case (CoreTimeTypes['On Demand']): {
|
||||
return (
|
||||
<>
|
||||
<TableCol
|
||||
header='Task'
|
||||
value={data.task}
|
||||
/>
|
||||
<TableCol
|
||||
header='Blocks per timeslice'
|
||||
value={data.maskBits}
|
||||
/>
|
||||
<TableCol
|
||||
header='Start'
|
||||
hide='both'
|
||||
value={data.start}
|
||||
/>
|
||||
<TableCol
|
||||
header='End'
|
||||
hide='both'
|
||||
value={data.end}
|
||||
/>
|
||||
<TableCol
|
||||
header='Last block'
|
||||
value={data.endBlock}
|
||||
/>
|
||||
<StyledTableCol hide={'mobile'}>
|
||||
<h5 style={{ opacity: '0.6' }}>type</h5>
|
||||
<Tag
|
||||
color={colours[data.type] as FlagColor}
|
||||
label={Object.values(CoreTimeTypes)[data.type]}
|
||||
/>
|
||||
</StyledTableCol>
|
||||
<td colSpan={1} />
|
||||
</>);
|
||||
}
|
||||
|
||||
default: {
|
||||
return (
|
||||
<>
|
||||
<TableCol
|
||||
header='Task'
|
||||
value={data.task}
|
||||
/>
|
||||
<TableCol
|
||||
header='Blocks per timeslice'
|
||||
value={data.maskBits}
|
||||
/>
|
||||
<TableCol
|
||||
header='Start'
|
||||
hide='both'
|
||||
value={data.start}
|
||||
/>
|
||||
<TableCol
|
||||
header='End'
|
||||
hide='both'
|
||||
value={data.end}
|
||||
/>
|
||||
<TableCol
|
||||
header='Last block'
|
||||
value={data.endBlock}
|
||||
/>
|
||||
<StyledTableCol hide={'mobile'}>
|
||||
<h5 style={{ opacity: '0.6' }}>type</h5>
|
||||
<Tag
|
||||
color={colours[CoreTimeTypes['Bulk Coretime']] as FlagColor}
|
||||
label={'Bulk Coretime'}
|
||||
/>
|
||||
</StyledTableCol>
|
||||
{data.owner
|
||||
? <StyledTableCol hide='mobile'>
|
||||
<h5 style={{ opacity: '0.6' }}>{'Owner'}</h5>
|
||||
<AddressMini
|
||||
isPadded={false}
|
||||
key={data.owner}
|
||||
value={data.owner}
|
||||
/>
|
||||
|
||||
</StyledTableCol>
|
||||
: <td></td>}
|
||||
</>);
|
||||
}
|
||||
}
|
||||
</StyledTableCol>
|
||||
: <td></td>}
|
||||
</>);
|
||||
}
|
||||
|
||||
export default React.memo(WorkInfoRow);
|
||||
|
||||
@ -3,17 +3,16 @@
|
||||
|
||||
import type { ApiPromise } from '@polkadot/api';
|
||||
import type { PalletBrokerConfigRecord, RegionInfo } from '@polkadot/react-hooks/types';
|
||||
import type { Option } from '@polkadot/types';
|
||||
import type { PalletBrokerStatusRecord } from '@polkadot/types/lookup';
|
||||
import type { CoreWorkloadType, CoreWorkplanType, InfoRow } from '../types.js';
|
||||
|
||||
import React, { useEffect, useMemo, useState } from 'react';
|
||||
|
||||
import { ExpandButton } from '@polkadot/react-components';
|
||||
import { useApi, useBrokerSalesInfo, useCall, useRegions, useToggle } from '@polkadot/react-hooks';
|
||||
import { useRegions, useToggle } from '@polkadot/react-hooks';
|
||||
import { useCoretimeConsts } from '@polkadot/react-hooks/useCoretimeConsts';
|
||||
|
||||
import { formatRowInfo } from '../utils.js';
|
||||
import { useBrokerContext } from '../BrokerContext.js';
|
||||
import { estimateTime, formatRowInfo } from '../utils.js';
|
||||
import WorkInfoRow from './WorkInfoRow.js';
|
||||
import Workplan from './Workplan.js';
|
||||
|
||||
@ -26,57 +25,65 @@ interface Props {
|
||||
}
|
||||
|
||||
function Workload ({ api, config, core, workload, workplan }: Props): React.ReactElement<Props> {
|
||||
const { isApiReady } = useApi();
|
||||
const salesInfo = useBrokerSalesInfo(api, isApiReady);
|
||||
const coretimeConstants = useCoretimeConsts();
|
||||
|
||||
const status = useCall<Option<PalletBrokerStatusRecord>>(isApiReady && api.query.broker?.status);
|
||||
const [isExpanded, toggleIsExpanded] = useToggle(false);
|
||||
const [workloadData, setWorkloadData] = useState<InfoRow[]>();
|
||||
const [workplanData, setWorkplanData] = useState<InfoRow[]>();
|
||||
|
||||
const currentTimeSlice = useMemo(() => {
|
||||
if (status?.isSome) {
|
||||
return status.unwrap().lastCommittedTimeslice.toNumber();
|
||||
}
|
||||
const { currentRegion, status } = useBrokerContext();
|
||||
|
||||
return 0;
|
||||
}, [status]);
|
||||
const currentTimeSlice = useMemo(() =>
|
||||
status?.lastTimeslice ?? 0
|
||||
, [status]);
|
||||
|
||||
const regionInfo = useRegions(api);
|
||||
const region: RegionInfo | undefined = useMemo(() => regionInfo?.find((v) => v.core === core && v.start <= currentTimeSlice && v.end > currentTimeSlice), [regionInfo, core, currentTimeSlice]);
|
||||
const regionOwnerInfo: RegionInfo | undefined = useMemo(() => regionInfo?.find((v) => v.core === core && v.start <= currentTimeSlice && v.end > currentTimeSlice), [regionInfo, core, currentTimeSlice]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!!workload?.length && !!salesInfo) {
|
||||
if (!!workload?.length && currentTimeSlice > 0) {
|
||||
// saleInfo points to a regionEnd and regionBeing in the next cycle, but we want the start and end of the current cycle
|
||||
setWorkloadData(formatRowInfo(
|
||||
workload,
|
||||
core,
|
||||
region,
|
||||
regionOwnerInfo,
|
||||
currentTimeSlice,
|
||||
{ regionBegin: salesInfo.regionBegin - config.regionLength, regionEnd: salesInfo.regionEnd - config.regionLength },
|
||||
{
|
||||
begin: currentRegion.begin || 0,
|
||||
beginDate: currentRegion.beginDate || '',
|
||||
end: currentRegion.end || 0,
|
||||
endDate: currentRegion.endDate || ''
|
||||
},
|
||||
config.regionLength,
|
||||
coretimeConstants?.relay
|
||||
));
|
||||
} else {
|
||||
return setWorkloadData([{ core }]);
|
||||
}
|
||||
}, [workload, region, currentTimeSlice, core, salesInfo, config, coretimeConstants]);
|
||||
}, [workload, regionOwnerInfo, currentTimeSlice, core, config, coretimeConstants, currentRegion]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!!workplan?.length && !!salesInfo) {
|
||||
if (workplan?.length && status && coretimeConstants && currentRegion.endDate) {
|
||||
const futureRegionStart = currentRegion.end || 0;
|
||||
const futureRegionEnd = futureRegionStart + config.regionLength;
|
||||
const lastBlock = status.lastTimeslice * coretimeConstants?.relay.blocksPerTimeslice;
|
||||
|
||||
setWorkplanData(formatRowInfo(
|
||||
workplan,
|
||||
core,
|
||||
region,
|
||||
currentTimeSlice,
|
||||
{ regionBegin: salesInfo.regionBegin - config.regionLength, regionEnd: salesInfo.regionEnd - config.regionLength },
|
||||
regionOwnerInfo,
|
||||
status.lastTimeslice,
|
||||
{
|
||||
begin: futureRegionStart,
|
||||
beginDate: currentRegion.endDate,
|
||||
end: futureRegionEnd,
|
||||
endDate: estimateTime(futureRegionEnd, lastBlock)?.formattedDate ?? ''
|
||||
},
|
||||
config.regionLength,
|
||||
coretimeConstants?.relay
|
||||
));
|
||||
}
|
||||
}
|
||||
, [workplan, region, currentTimeSlice, core, salesInfo, config, coretimeConstants]);
|
||||
}, [workplan, regionOwnerInfo, status, core, config, coretimeConstants, currentRegion]);
|
||||
|
||||
const hasWorkplan = workplan?.length;
|
||||
|
||||
@ -113,14 +120,12 @@ function Workload ({ api, config, core, workload, workplan }: Props): React.Reac
|
||||
<>
|
||||
<tr>
|
||||
<td style={{ fontWeight: 700, paddingTop: '2rem', width: 150 }}>workplans</td>
|
||||
<td colSpan={7}></td>
|
||||
<td colSpan={8}></td>
|
||||
</tr>
|
||||
{workplanData?.map((workplanInfo) => (
|
||||
<Workplan
|
||||
currentTimeSlice={currentTimeSlice}
|
||||
isExpanded={isExpanded}
|
||||
key={workplanInfo.core}
|
||||
region={region}
|
||||
workplanData={workplanInfo}
|
||||
/>
|
||||
))}
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
// Copyright 2017-2025 @polkadot/app-broker authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import type { RegionInfo } from '@polkadot/react-hooks/types';
|
||||
import type { InfoRow } from '../types.js';
|
||||
|
||||
import React from 'react';
|
||||
@ -13,9 +12,7 @@ import WorkInfoRow from './WorkInfoRow.js';
|
||||
interface Props {
|
||||
className?: string;
|
||||
workplanData: InfoRow;
|
||||
currentTimeSlice: number
|
||||
isExpanded: boolean
|
||||
region: RegionInfo | undefined
|
||||
}
|
||||
|
||||
function Workplan ({ isExpanded, workplanData }: Props): React.ReactElement<Props> {
|
||||
@ -26,7 +23,7 @@ function Workplan ({ isExpanded, workplanData }: Props): React.ReactElement<Prop
|
||||
style={{ minHeight: '100px' }}
|
||||
>
|
||||
<td> <Spinner /> </td>
|
||||
<td colSpan={6}></td>
|
||||
<td colSpan={7}></td>
|
||||
</tr>
|
||||
);
|
||||
}
|
||||
|
||||
@ -6,8 +6,9 @@ import type { CoreInfo } from '../types.js';
|
||||
|
||||
import React, { useEffect, useMemo, useState } from 'react';
|
||||
|
||||
import { useApi, useBrokerLeases, useBrokerReservations, useBrokerStatus, useWorkloadInfos, useWorkplanInfos } from '@polkadot/react-hooks';
|
||||
import { useApi, useBrokerLeases, useBrokerReservations, useWorkloadInfos, useWorkplanInfos } from '@polkadot/react-hooks';
|
||||
|
||||
import { useBrokerContext } from '../BrokerContext.js';
|
||||
import { createTaskMap, getOccupancyType } from '../utils.js';
|
||||
import CoresTable from './CoresTables.js';
|
||||
import Filters from './Filters.js';
|
||||
@ -53,10 +54,10 @@ const formatData = (coreCount: number, workplan: CoreWorkplan[], workload: CoreW
|
||||
|
||||
function Overview ({ className }: Props): React.ReactElement<Props> {
|
||||
const { api, apiEndpoint, isApiReady } = useApi();
|
||||
const [data, setData] = useState<CoreInfo[]>([]);
|
||||
const { currentRegion, status } = useBrokerContext();
|
||||
|
||||
const [data, setData] = useState<CoreInfo[]>([]);
|
||||
const [filtered, setFiltered] = useState<CoreInfo[]>();
|
||||
const status = useBrokerStatus(api, isApiReady);
|
||||
|
||||
const workloadInfos: CoreWorkload[] | undefined = useWorkloadInfos(api, isApiReady);
|
||||
const workplanInfos: CoreWorkplan[] | undefined = useWorkplanInfos(api, isApiReady);
|
||||
@ -73,11 +74,12 @@ function Overview ({ className }: Props): React.ReactElement<Props> {
|
||||
|
||||
return (
|
||||
<div className={className}>
|
||||
<Summary
|
||||
apiEndpoint={apiEndpoint}
|
||||
coreCount={status?.coreCount.toString() || '-'}
|
||||
workloadInfos={workloadInfos}
|
||||
></Summary>
|
||||
{!!currentRegion.beginDate && !!currentRegion.endDate &&
|
||||
<Summary
|
||||
apiEndpoint={apiEndpoint}
|
||||
coreCount={status?.coreCount.toString() || '-'}
|
||||
workloadInfos={workloadInfos}
|
||||
/>}
|
||||
{!!data?.length &&
|
||||
(<>
|
||||
<Filters
|
||||
|
||||
@ -6,8 +6,10 @@ import type { TabItem } from '@polkadot/react-components/types';
|
||||
import React, { useRef } from 'react';
|
||||
|
||||
import { Tabs } from '@polkadot/react-components';
|
||||
import { useApi } from '@polkadot/react-hooks';
|
||||
|
||||
import Overview from './Overview/index.js';
|
||||
import { BrokerProvider } from './BrokerContext.js';
|
||||
import { useTranslation } from './translate.js';
|
||||
|
||||
interface Props {
|
||||
@ -28,14 +30,20 @@ function createItemsRef (t: (key: string, options?: { replace: Record<string, un
|
||||
function BrokerApp ({ basePath, className }: Props): React.ReactElement<Props> {
|
||||
const { t } = useTranslation();
|
||||
const itemsRef = useRef(createItemsRef(t));
|
||||
const { api, isApiReady } = useApi();
|
||||
|
||||
return (
|
||||
<main className={className}>
|
||||
<Tabs
|
||||
basePath={basePath}
|
||||
items={itemsRef.current}
|
||||
/>
|
||||
<Overview />
|
||||
<BrokerProvider
|
||||
api={api}
|
||||
isApiReady={isApiReady}
|
||||
>
|
||||
<Tabs
|
||||
basePath={basePath}
|
||||
items={itemsRef.current}
|
||||
/>
|
||||
<Overview />
|
||||
</BrokerProvider>
|
||||
</main>
|
||||
);
|
||||
}
|
||||
|
||||
@ -10,6 +10,7 @@ export interface InfoRow {
|
||||
core: number
|
||||
mask?: string
|
||||
start?: string | null,
|
||||
startTimeslice?: number | null
|
||||
end?: string | null
|
||||
owner?: string
|
||||
leaseLength?: number
|
||||
@ -38,3 +39,10 @@ export interface CoreWorkloadType extends CoreWorkload {
|
||||
lastBlock: number,
|
||||
type: CoreTimeTypes
|
||||
}
|
||||
|
||||
export interface CurrentRegion {
|
||||
begin: number | null,
|
||||
beginDate: string | null,
|
||||
end: number | null,
|
||||
endDate: string | null
|
||||
}
|
||||
|
||||
@ -16,9 +16,11 @@ function formatDate (date: Date) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculation on the Relay chain
|
||||
*
|
||||
* blockTime = 6000 ms
|
||||
* BlocksPerTimeslice = 80
|
||||
* Default Regoin = 5040 timeslices
|
||||
* Default Region = 5040 timeslices
|
||||
* TargetBlock = TargetTimeslice * BlocksPerTimeslice
|
||||
* Block Time Difference = |TargetBlock - latest Block| * blockTime
|
||||
*
|
||||
@ -31,26 +33,29 @@ function formatDate (date: Date) {
|
||||
export const estimateTime = (
|
||||
targetTimeslice: string | number,
|
||||
latestBlock: number,
|
||||
{ blocksPerTimeslice: blocksPerTs, blocktimeMs }: ChainBlockConstants = { blocksPerTimeslice: 0, blocktimeMs: 0 }
|
||||
): string | null => {
|
||||
{ blocksPerTimeslice: blocksPerTs, blocktimeMs }: ChainBlockConstants = { blocksPerTimeslice: 80, blocktimeMs: 6000 }
|
||||
): { timestamp: number, formattedDate: string } | null => {
|
||||
if (!latestBlock || !targetTimeslice) {
|
||||
console.error('Invalid input: one or more inputs are missing');
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
const now = new Date().getTime();
|
||||
const now = new BN(Date.now());
|
||||
|
||||
try {
|
||||
const blockTime = new BN(blocktimeMs); // Average block time in milliseconds (6 seconds)
|
||||
const timeSlice = new BN(blocksPerTs);
|
||||
const blocksPerTimeslice = new BN(blocksPerTs);
|
||||
const targetBlock = new BN(Number(targetTimeslice)).mul(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);
|
||||
const blockDifference = targetBlock.sub(latestBlockBN);
|
||||
const timeDifference = blockDifference.mul(blockTime);
|
||||
const estTimestamp = now.add(timeDifference);
|
||||
|
||||
return formatDate(new Date(estTimestamp.toNumber()));
|
||||
return {
|
||||
formattedDate: formatDate(new Date(estTimestamp.toNumber())),
|
||||
timestamp: estTimestamp.toNumber()
|
||||
};
|
||||
} catch (error) {
|
||||
console.error('Error in calculation:', error);
|
||||
|
||||
@ -70,36 +75,38 @@ export const estimateTime = (
|
||||
export function formatRowInfo (
|
||||
data: CoreWorkloadType[] | CoreWorkplanType[],
|
||||
core: number,
|
||||
currentRegion: RegionInfo | undefined,
|
||||
regionOwnerInfo: RegionInfo | undefined,
|
||||
currentTimeSlice: number,
|
||||
{ regionBegin, regionEnd }: { regionBegin: number, regionEnd: number },
|
||||
currentRegion: { begin: number, beginDate: string, end: number, endDate: string },
|
||||
regionLength: number,
|
||||
coretimeRelayConstants: ChainBlockConstants = { blocksPerTimeslice: 0, blocktimeMs: 0 }
|
||||
): InfoRow[] {
|
||||
const blockNumberNow = currentTimeSlice * coretimeRelayConstants.blocksPerTimeslice;
|
||||
|
||||
return data.map((one: CoreWorkloadType | CoreWorkplanType) => {
|
||||
const item: InfoRow = { core, maskBits: one?.info?.maskBits, task: one?.info?.task, type: one?.type };
|
||||
const blockNumberNow = currentTimeSlice * 80;
|
||||
const item: InfoRow = {
|
||||
core,
|
||||
maskBits: one?.info?.maskBits,
|
||||
task: one?.info?.task,
|
||||
type: one?.type
|
||||
};
|
||||
|
||||
item.type = one.type;
|
||||
let end; let start = null;
|
||||
|
||||
if (one.type === CoreTimeTypes.Lease) {
|
||||
// For region-based types, use the provided dates
|
||||
if ([CoreTimeTypes['Bulk Coretime'], CoreTimeTypes.Reservation, CoreTimeTypes['On Demand']].includes(one.type)) {
|
||||
item.start = currentRegion.beginDate;
|
||||
item.end = currentRegion.endDate;
|
||||
item.startTimeslice = currentRegion.begin;
|
||||
item.endBlock = currentRegion.end * coretimeRelayConstants.blocksPerTimeslice;
|
||||
} else if (one.type === CoreTimeTypes.Lease) { // For lease type, calculate the end
|
||||
const period = Math.floor(one.lastBlock / regionLength);
|
||||
const endTs = period * regionLength;
|
||||
const endEstimate = estimateTime(endTs, blockNumberNow, coretimeRelayConstants);
|
||||
|
||||
end = period * regionLength;
|
||||
} else {
|
||||
start = currentRegion?.start?.toString() ?? regionBegin;
|
||||
end = currentRegion?.end?.toString() ?? regionEnd;
|
||||
item.end = endEstimate?.formattedDate ?? null;
|
||||
item.endBlock = endTs * coretimeRelayConstants.blocksPerTimeslice;
|
||||
}
|
||||
|
||||
item.owner = currentRegion?.owner.toString();
|
||||
item.start = start ? estimateTime(start, blockNumberNow, coretimeRelayConstants) : null;
|
||||
item.end = end ? estimateTime(end, blockNumberNow, coretimeRelayConstants) : null;
|
||||
item.endBlock = end ? Number(end) * 80 : 0;
|
||||
|
||||
if ('timeslice' in one && !start) {
|
||||
start = estimateTime(one.timeslice, blockNumberNow, coretimeRelayConstants) ?? null;
|
||||
}
|
||||
item.owner = regionOwnerInfo?.owner.toString();
|
||||
|
||||
return item;
|
||||
});
|
||||
|
||||
@ -9,17 +9,27 @@ import React, { createContext, useContext, useMemo } from 'react';
|
||||
|
||||
import { useCoretimeInformation } from '@polkadot/react-hooks';
|
||||
|
||||
import { createGet } from './utils/index.js';
|
||||
import { createGet, estimateTime } from './utils/index.js';
|
||||
|
||||
interface CoretimeContextProps {
|
||||
coretimeInfo: CoretimeInformation | null;
|
||||
get: ReturnType<typeof createGet> | null;
|
||||
saleStartDate: string | null
|
||||
saleEndDate: string | null
|
||||
currentRegionEnd: number | null
|
||||
currentRegionStart: number | null
|
||||
}
|
||||
|
||||
const CoretimeContext = createContext<CoretimeContextProps>({
|
||||
const initState = {
|
||||
coretimeInfo: null,
|
||||
get: null
|
||||
});
|
||||
currentRegionEnd: null,
|
||||
currentRegionStart: null,
|
||||
get: null,
|
||||
saleEndDate: null,
|
||||
saleStartDate: null
|
||||
};
|
||||
|
||||
const CoretimeContext = createContext<CoretimeContextProps>(initState);
|
||||
|
||||
export const CoretimeProvider = ({ api,
|
||||
children,
|
||||
@ -37,7 +47,26 @@ export const CoretimeProvider = ({ api,
|
||||
return null;
|
||||
}, [coretimeInfo?.constants]);
|
||||
|
||||
const value = useMemo(() => ({ coretimeInfo: coretimeInfo ?? null, get }), [coretimeInfo, get]);
|
||||
const currentRegionEnd = useMemo(() => coretimeInfo ? coretimeInfo?.salesInfo.regionEnd - coretimeInfo?.config.regionLength : 0, [coretimeInfo]);
|
||||
const currentRegionStart = useMemo(() => coretimeInfo ? coretimeInfo.salesInfo.regionEnd - coretimeInfo?.config.regionLength * 2 : 0, [coretimeInfo]);
|
||||
|
||||
const saleStartDate = useMemo(() => get && coretimeInfo && estimateTime(currentRegionStart, get.blocks.relay(coretimeInfo?.status?.lastTimeslice), coretimeInfo.constants.relay)?.formattedDate, [currentRegionStart, coretimeInfo, get]);
|
||||
const saleEndDate = useMemo(() => get && coretimeInfo && estimateTime(currentRegionEnd, get.blocks.relay(coretimeInfo?.status?.lastTimeslice), coretimeInfo.constants.relay)?.formattedDate, [currentRegionEnd, coretimeInfo, get]);
|
||||
|
||||
const value = useMemo(() => {
|
||||
if (!coretimeInfo || !currentRegionEnd || !currentRegionStart || !get || !saleEndDate || !saleStartDate) {
|
||||
return initState;
|
||||
}
|
||||
|
||||
return {
|
||||
coretimeInfo: coretimeInfo ?? null,
|
||||
currentRegionEnd,
|
||||
currentRegionStart,
|
||||
get,
|
||||
saleEndDate,
|
||||
saleStartDate
|
||||
};
|
||||
}, [coretimeInfo, currentRegionEnd, currentRegionStart, get, saleEndDate, saleStartDate]);
|
||||
|
||||
return (
|
||||
<CoretimeContext.Provider value={value}>
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
|
||||
import type { ApiPromise } from '@polkadot/api';
|
||||
import type { TabItem } from '@polkadot/react-components/types';
|
||||
import type { ChainName } from './types.js';
|
||||
import type { RelayName } from './types.js';
|
||||
|
||||
import React, { useRef } from 'react';
|
||||
import { Route, Routes } from 'react-router-dom';
|
||||
@ -39,7 +39,7 @@ function createItemsRef (t: (key: string, options?: { replace: Record<string, un
|
||||
function CoretimePage ({ api, basePath, className, isApiReady }: Props): React.ReactElement<Props> {
|
||||
const { t } = useTranslation();
|
||||
const itemsRef = useRef(createItemsRef(t));
|
||||
const chainName = useCall<string>(isApiReady && api?.rpc.system.chain)?.toString().toLowerCase() as ChainName;
|
||||
const relayName = useCall<string>(isApiReady && api?.rpc.system.chain)?.toString().toLowerCase() as RelayName;
|
||||
|
||||
return (
|
||||
<main className={className}>
|
||||
@ -47,16 +47,16 @@ function CoretimePage ({ api, basePath, className, isApiReady }: Props): React.R
|
||||
basePath={basePath}
|
||||
items={itemsRef.current}
|
||||
/>
|
||||
{!chainName
|
||||
{!relayName
|
||||
? <div><Spinner /></div>
|
||||
: <Routes>
|
||||
<Route path={basePath}>
|
||||
<Route
|
||||
element={<Overview chainName={chainName} />}
|
||||
element={<Overview relayName={relayName} />}
|
||||
index
|
||||
/>
|
||||
<Route
|
||||
element={<Sale chainName={chainName} />}
|
||||
element={<Sale relayName={relayName} />}
|
||||
path='sale'
|
||||
/>
|
||||
</Route>
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
// Copyright 2017-2025 @polkadot/app-coretime authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import type { BrokerStatus, ChainConstants, CoreDescription, PalletBrokerConfigRecord, PalletBrokerSaleInfoRecord, RegionInfo } from '@polkadot/react-hooks/types';
|
||||
import type { ChainName } from '../types.js';
|
||||
import type { BrokerStatus, CoreDescription, PalletBrokerConfigRecord, PalletBrokerSaleInfoRecord, RegionInfo } from '@polkadot/react-hooks/types';
|
||||
import type { RelayName } from '../types.js';
|
||||
|
||||
import React, { useMemo } from 'react';
|
||||
|
||||
@ -11,7 +11,7 @@ import { BN } from '@polkadot/util';
|
||||
|
||||
import { useCoretimeContext } from '../CoretimeContext.js';
|
||||
import { useTranslation } from '../translate.js';
|
||||
import { estimateTime, FirstCycleStart } from '../utils/index.js';
|
||||
import { FirstCycleStart } from '../utils/index.js';
|
||||
|
||||
interface Props {
|
||||
coreDscriptors?: CoreDescription[];
|
||||
@ -20,25 +20,24 @@ interface Props {
|
||||
region: RegionInfo[],
|
||||
status: BrokerStatus,
|
||||
parachainCount: number
|
||||
chainName: ChainName,
|
||||
constants: ChainConstants
|
||||
relayName: RelayName,
|
||||
}
|
||||
|
||||
function Summary ({ chainName, config, constants, parachainCount, saleInfo, status }: Props): React.ReactElement<Props> {
|
||||
function Summary ({ config, parachainCount, relayName, status }: Props): React.ReactElement<Props> {
|
||||
const { t } = useTranslation();
|
||||
const currentRegionEnd = saleInfo.regionEnd - config.regionLength;
|
||||
const currentRegionStart = saleInfo.regionEnd - config.regionLength * 2;
|
||||
const { get } = useCoretimeContext();
|
||||
const { coretimeInfo, currentRegionEnd, currentRegionStart, saleEndDate, saleStartDate } = useCoretimeContext();
|
||||
|
||||
const saleNumber = useMemo(() => {
|
||||
if (chainName && currentRegionEnd) {
|
||||
if (relayName && currentRegionEnd) {
|
||||
return Math.floor(
|
||||
(currentRegionEnd - FirstCycleStart.timeslice.coretime[chainName]) / config.regionLength
|
||||
(currentRegionEnd - FirstCycleStart.timeslice.coretime[relayName]) / config.regionLength
|
||||
);
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}, [currentRegionEnd, chainName, config]);
|
||||
}, [currentRegionEnd, relayName, config]);
|
||||
|
||||
const timeslicesSinceCycleStart = useMemo(() => currentRegionEnd && new BN(config?.regionLength).sub((new BN(currentRegionEnd)).sub(new BN(status.lastTimeslice))), [status, config, currentRegionEnd]);
|
||||
|
||||
return (
|
||||
<SummaryBox>
|
||||
@ -56,36 +55,46 @@ function Summary ({ chainName, config, constants, parachainCount, saleInfo, stat
|
||||
<CardSummary label={t('parachains')}>
|
||||
{parachainCount && parachainCount}
|
||||
</CardSummary>
|
||||
{config && status &&
|
||||
<CardSummary
|
||||
className='media--800'
|
||||
label={t('cycle progress')}
|
||||
progress={{
|
||||
isBlurred: false,
|
||||
total: new BN(config?.regionLength),
|
||||
value: new BN(config?.regionLength - (currentRegionEnd - status.lastTimeslice)),
|
||||
withTime: false
|
||||
}}
|
||||
/>
|
||||
{config && status && currentRegionEnd && saleEndDate && saleStartDate && timeslicesSinceCycleStart && coretimeInfo?.constants &&
|
||||
<>
|
||||
<CardSummary
|
||||
className='media--800'
|
||||
label={t('timeslice progress')}
|
||||
progress={{
|
||||
hideGraph: true,
|
||||
hideValue: false,
|
||||
isBlurred: false,
|
||||
total: new BN(config?.regionLength),
|
||||
value: timeslicesSinceCycleStart,
|
||||
withTime: false
|
||||
}}
|
||||
/>
|
||||
<CardSummary
|
||||
label={t('cycle')}
|
||||
progress={{
|
||||
total: new BN(config.regionLength).mul(new BN(coretimeInfo?.constants.relay.blocksPerTimeslice)),
|
||||
value: timeslicesSinceCycleStart.mul(new BN(coretimeInfo?.constants.relay.blocksPerTimeslice)),
|
||||
withTime: true
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
|
||||
}
|
||||
</section>
|
||||
<section className='media--1200'>
|
||||
{status &&
|
||||
(<CardSummary label={t('sale dates')}>
|
||||
<div>
|
||||
<div style={{ fontSize: '14px' }}>{get && estimateTime(currentRegionStart, get.blocks.relay(status?.lastTimeslice), constants.relay)}</div>
|
||||
<div style={{ fontSize: '14px' }}>{get && estimateTime(currentRegionEnd, get.blocks.relay(status?.lastTimeslice), constants.relay)}</div>
|
||||
</div>
|
||||
</CardSummary>)
|
||||
}
|
||||
{status &&
|
||||
<CardSummary label={t('sale ts')}>
|
||||
<div>
|
||||
<div style={{ fontSize: '14px' }}>{currentRegionStart}</div>
|
||||
<div style={{ fontSize: '14px' }}>{currentRegionEnd}</div>
|
||||
</div>
|
||||
</CardSummary>
|
||||
}
|
||||
<CardSummary label={t('sale dates')}>
|
||||
<div>
|
||||
<div style={{ fontSize: '14px' }}>{saleStartDate}</div>
|
||||
<div style={{ fontSize: '14px' }}>{saleEndDate}</div>
|
||||
</div>
|
||||
</CardSummary>
|
||||
<CardSummary label={t('sale ts')}>
|
||||
<div>
|
||||
<div style={{ fontSize: '14px' }}>{currentRegionStart}</div>
|
||||
<div style={{ fontSize: '14px' }}>{currentRegionEnd}</div>
|
||||
</div>
|
||||
</CardSummary>
|
||||
|
||||
</section>
|
||||
</SummaryBox>
|
||||
);
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
// Copyright 2017-2025 @polkadot/app-coretime authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import type { ChainName } from '../types.js';
|
||||
import type { RelayName } from '../types.js';
|
||||
|
||||
import React from 'react';
|
||||
|
||||
@ -11,21 +11,20 @@ import Summary from './Summary.js';
|
||||
|
||||
interface Props {
|
||||
className?: string;
|
||||
chainName: ChainName
|
||||
relayName: RelayName
|
||||
}
|
||||
|
||||
function Overview ({ chainName, className }: Props): React.ReactElement<Props> {
|
||||
function Overview ({ className, relayName }: Props): React.ReactElement<Props> {
|
||||
const { coretimeInfo } = useCoretimeContext();
|
||||
|
||||
return (
|
||||
<main className={className}>
|
||||
{coretimeInfo && (
|
||||
<Summary
|
||||
chainName={chainName}
|
||||
config={coretimeInfo?.config}
|
||||
constants={coretimeInfo?.constants}
|
||||
parachainCount={coretimeInfo.taskIds?.length || 0}
|
||||
region={coretimeInfo?.region}
|
||||
relayName={relayName}
|
||||
saleInfo={coretimeInfo?.salesInfo}
|
||||
status={coretimeInfo?.status}
|
||||
/>
|
||||
@ -33,6 +32,7 @@ function Overview ({ chainName, className }: Props): React.ReactElement<Props> {
|
||||
{!!coretimeInfo &&
|
||||
<ParachainsTable
|
||||
coretimeInfo={coretimeInfo}
|
||||
relayName={relayName}
|
||||
/>
|
||||
}
|
||||
</main>
|
||||
|
||||
@ -2,6 +2,7 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import type { ChainInformation, ChainWorkTaskInformation } from '@polkadot/react-hooks/types';
|
||||
import type { RelayName } from './types.js';
|
||||
|
||||
import React from 'react';
|
||||
|
||||
@ -15,9 +16,10 @@ interface Props {
|
||||
regionEnd: number
|
||||
regionBegin: number
|
||||
lastCommittedTimeslice: number
|
||||
relayName: RelayName
|
||||
}
|
||||
|
||||
function ParachainTableRow ({ chain, lastCommittedTimeslice, regionBegin, regionEnd }: Props): React.ReactElement<Props> {
|
||||
function ParachainTableRow ({ chain, lastCommittedTimeslice, regionBegin, regionEnd, relayName }: Props): React.ReactElement<Props> {
|
||||
const [isExpanded, toggleIsExpanded] = useToggle(false);
|
||||
const info = chain.workTaskInfo;
|
||||
const firstRecord = chain.workTaskInfo[0];
|
||||
@ -38,6 +40,7 @@ function ParachainTableRow ({ chain, lastCommittedTimeslice, regionBegin, region
|
||||
lease={chain.lease}
|
||||
regionBegin={regionBegin}
|
||||
regionEnd={regionEnd}
|
||||
relayName={relayName}
|
||||
/>
|
||||
{idx === 0 && <td style={{ paddingRight: '2rem', textAlign: 'right', verticalAlign: 'top' }}>
|
||||
|
||||
|
||||
@ -1,6 +1,8 @@
|
||||
// Copyright 2017-2025 @polkadot/app-coretime authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import type { RelayName } from './types.js';
|
||||
|
||||
import React, { useCallback, useEffect, useRef, useState } from 'react';
|
||||
|
||||
import { Table } from '@polkadot/react-components';
|
||||
@ -12,9 +14,10 @@ import { useTranslation } from './translate.js';
|
||||
|
||||
interface Props {
|
||||
coretimeInfo: CoretimeInformation
|
||||
relayName: RelayName
|
||||
}
|
||||
|
||||
function ParachainsTable ({ coretimeInfo }: Props): React.ReactElement<Props> {
|
||||
function ParachainsTable ({ coretimeInfo, relayName }: Props): React.ReactElement<Props> {
|
||||
const { t } = useTranslation();
|
||||
const headerRef = useRef<([React.ReactNode?, string?, number?] | false)[]>([
|
||||
[t('parachains'), 'start'],
|
||||
@ -22,7 +25,7 @@ function ParachainsTable ({ coretimeInfo }: Props): React.ReactElement<Props> {
|
||||
[t('core number'), 'start'],
|
||||
[t('type'), 'start'],
|
||||
[t('last block'), 'start media--800'],
|
||||
[t('end'), 'start media--1000'],
|
||||
[t('end date (approx)'), 'start media--1000'],
|
||||
[t('renewal'), 'start media--1200'],
|
||||
[t('renewal price'), 'start media--1200'],
|
||||
[t('links'), 'start media--800'],
|
||||
@ -67,6 +70,7 @@ function ParachainsTable ({ coretimeInfo }: Props): React.ReactElement<Props> {
|
||||
lastCommittedTimeslice={coretimeInfo.status.lastCommittedTimeslice}
|
||||
regionBegin={coretimeInfo.salesInfo.regionBegin}
|
||||
regionEnd={coretimeInfo.salesInfo.regionEnd}
|
||||
relayName={relayName}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
|
||||
@ -3,6 +3,7 @@
|
||||
|
||||
import type { FlagColor } from '@polkadot/react-components/types';
|
||||
import type { ChainWorkTaskInformation, LegacyLease } from '@polkadot/react-hooks/types';
|
||||
import type { RelayName } from './types.js';
|
||||
|
||||
import React from 'react';
|
||||
|
||||
@ -22,6 +23,7 @@ interface Props {
|
||||
lastCommittedTimeslice: number
|
||||
lease: LegacyLease | undefined
|
||||
highlight?: boolean
|
||||
relayName: RelayName
|
||||
}
|
||||
|
||||
interface StyledCellProps {
|
||||
@ -50,16 +52,34 @@ const StyledMarkWarning = styled(MarkWarning)`
|
||||
|
||||
const EXPIRES_IN_DAYS = 7;
|
||||
|
||||
function Row ({ chainRecord, highlight = false, id, lastCommittedTimeslice, lease, regionBegin, regionEnd }: Props): React.ReactElement<Props> {
|
||||
const chainRegionEnd = (chainRecord.renewalStatus === ChainRenewalStatus.Renewed ? regionEnd : regionBegin);
|
||||
function Row ({ chainRecord, highlight = false, id, lastCommittedTimeslice, lease, regionBegin, regionEnd, relayName }: Props): React.ReactElement<Props> {
|
||||
// Group status checks
|
||||
const { renewalStatus } = chainRecord;
|
||||
const isRenewed = renewalStatus === ChainRenewalStatus.Renewed;
|
||||
const isEligible = renewalStatus === ChainRenewalStatus.Eligible;
|
||||
const chainRegionEnd = isRenewed ? regionEnd : regionBegin;
|
||||
|
||||
const renewalLink = isEligible && (
|
||||
<a
|
||||
href={`https://app.regionx.tech/renew?network=${relayName}¶Id=${id}&core=${chainRecord?.workload?.core}`}
|
||||
rel='noopener noreferrer'
|
||||
target='_blank'
|
||||
>
|
||||
Renew on RegionX
|
||||
</a>
|
||||
);
|
||||
|
||||
const renewalValue = isRenewed
|
||||
? chainRecord.renewalStatusMessage
|
||||
: (isEligible ? renewalLink : '-');
|
||||
|
||||
const targetTimeslice = lease?.until || chainRegionEnd;
|
||||
const showEstimates = !!targetTimeslice && Object.values(CoreTimeTypes)[chainRecord.type] !== CoreTimeTypes.Reservation;
|
||||
const { coretimeInfo, get } = useCoretimeContext();
|
||||
|
||||
const estimatedTime = showEstimates && get && coretimeInfo &&
|
||||
estimateTime(targetTimeslice, get.blocks.relay(lastCommittedTimeslice), coretimeInfo?.constants?.relay);
|
||||
const estimatedTime = showEstimates && get && coretimeInfo && estimateTime(targetTimeslice, get.blocks.relay(lastCommittedTimeslice), coretimeInfo?.constants?.relay);
|
||||
|
||||
const isWithinWeek = estimatedTime && new Date(estimatedTime).getTime() - Date.now() < EXPIRES_IN_DAYS * 24 * 60 * 60 * 1000;
|
||||
const isWithinWeek = !!estimatedTime && new Date(estimatedTime.timestamp).getTime() - Date.now() < EXPIRES_IN_DAYS * 24 * 60 * 60 * 1000;
|
||||
const isReservation = chainRecord.type === CoreTimeTypes.Reservation;
|
||||
|
||||
return (
|
||||
@ -83,9 +103,9 @@ function Row ({ chainRecord, highlight = false, id, lastCommittedTimeslice, leas
|
||||
<StyledCell
|
||||
$p={highlight}
|
||||
className='media--800'
|
||||
>{showEstimates && chainRecord?.lastBlock &&
|
||||
>{showEstimates && chainRecord?.lastBlock && relayName &&
|
||||
<a
|
||||
href={`https://polkadot.subscan.io/block/${chainRecord?.lastBlock}`}
|
||||
href={`https://${relayName.split(' ')[0]}.subscan.io/block/${chainRecord?.lastBlock}`}
|
||||
rel='noreferrer'
|
||||
target='_blank'
|
||||
>{formatNumber(chainRecord?.lastBlock)}
|
||||
@ -97,7 +117,7 @@ function Row ({ chainRecord, highlight = false, id, lastCommittedTimeslice, leas
|
||||
style={{ whiteSpace: 'nowrap' }}
|
||||
>
|
||||
<span>
|
||||
{estimatedTime}
|
||||
{estimatedTime && estimatedTime?.formattedDate}
|
||||
{!!isWithinWeek && !isReservation && chainRecord?.renewalStatus !== ChainRenewalStatus.Renewed && (
|
||||
<StyledMarkWarning
|
||||
content='Expires Soon'
|
||||
@ -108,7 +128,7 @@ function Row ({ chainRecord, highlight = false, id, lastCommittedTimeslice, leas
|
||||
<StyledCell
|
||||
$p={highlight}
|
||||
className='media--1200'
|
||||
>{chainRecord?.renewalStatus === ChainRenewalStatus.Renewed ? chainRecord.renewalStatusMessage : chainRecord.renewalStatus}</StyledCell>
|
||||
>{renewalValue}</StyledCell>
|
||||
<StyledCell
|
||||
$p={highlight}
|
||||
className='media--1200'
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
// Copyright 2017-2025 @polkadot/app-coretime authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import type { ChainName, SaleParameters } from '../types.js';
|
||||
import type { RelayName, SaleParameters } from '../types.js';
|
||||
|
||||
import React, { useMemo } from 'react';
|
||||
|
||||
@ -80,12 +80,12 @@ const phases = {
|
||||
|
||||
const dotLakeUrl = 'https://data.parity.io/coretime';
|
||||
|
||||
const SaleDetailsView = ({ chainName, chosenSaleNumber, saleParams }: { saleParams: SaleParameters, chosenSaleNumber: number, chainName: ChainName }) => {
|
||||
const SaleDetailsView = ({ chosenSaleNumber, relayName, saleParams }: { saleParams: SaleParameters, chosenSaleNumber: number, relayName: RelayName }) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const subscanPriceGraphUrl = useMemo(() =>
|
||||
`https://coretime-${chainName}.subscan.io/coretime_dashboard`
|
||||
, [chainName]);
|
||||
`https://coretime-${relayName}.subscan.io/coretime_dashboard`
|
||||
, [relayName]);
|
||||
|
||||
if (chosenSaleNumber === -1 || !saleParams) {
|
||||
return null;
|
||||
@ -127,14 +127,14 @@ const SaleDetailsView = ({ chainName, chosenSaleNumber, saleParams }: { salePara
|
||||
target='_blank'
|
||||
>Sale Purchase Graph</a>
|
||||
<SubscanModuleCallUrl
|
||||
chainName={chainName}
|
||||
chainName={relayName}
|
||||
chosenSaleNumber={chosenSaleNumber}
|
||||
currentRegion={saleParams.currentRegion}
|
||||
urlTitle='Sale Purchase Transactions'
|
||||
/>
|
||||
<SubscanModuleCallUrl
|
||||
call={'renew'}
|
||||
chainName={chainName}
|
||||
chainName={relayName}
|
||||
chosenSaleNumber={chosenSaleNumber}
|
||||
currentRegion={saleParams.currentRegion}
|
||||
urlTitle='Sale Renewal Transactions'
|
||||
@ -151,7 +151,7 @@ const SaleDetailsView = ({ chainName, chosenSaleNumber, saleParams }: { salePara
|
||||
{Object.entries(providers).map(([provider, { alt, href, logo }]) => (
|
||||
<LinkWithLogo
|
||||
alt={alt}
|
||||
href={href(chainName)}
|
||||
href={href(relayName)}
|
||||
key={provider}
|
||||
logo={logo}
|
||||
/>
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import type { ApiPromise } from '@polkadot/api';
|
||||
import type { BrokerStatus, ChainConstants, CoreDescription, PalletBrokerConfigRecord, PalletBrokerSaleInfoRecord, RegionInfo } from '@polkadot/react-hooks/types';
|
||||
import type { BrokerStatus, CoreDescription, PalletBrokerConfigRecord, PalletBrokerSaleInfoRecord, RegionInfo } from '@polkadot/react-hooks/types';
|
||||
|
||||
import React from 'react';
|
||||
|
||||
@ -11,7 +11,7 @@ import { BN, formatNumber } from '@polkadot/util';
|
||||
|
||||
import { useCoretimeContext } from '../CoretimeContext.js';
|
||||
import { useTranslation } from '../translate.js';
|
||||
import { estimateTime, getCurrentRegionStartEndTs } from '../utils/index.js';
|
||||
import { getCurrentRegionStartEndTs } from '../utils/index.js';
|
||||
|
||||
interface Props {
|
||||
api: ApiPromise | null,
|
||||
@ -21,15 +21,12 @@ interface Props {
|
||||
region: RegionInfo[],
|
||||
status: BrokerStatus,
|
||||
saleNumber: number,
|
||||
constants: ChainConstants
|
||||
}
|
||||
|
||||
function Summary ({ config, constants, saleInfo, saleNumber, status }: Props): React.ReactElement<Props> {
|
||||
function Summary ({ config, saleInfo, saleNumber, status }: Props): React.ReactElement<Props> {
|
||||
const { t } = useTranslation();
|
||||
const { currentRegionEndTs, currentRegionStartTs } = getCurrentRegionStartEndTs(saleInfo, config.regionLength);
|
||||
const { get } = useCoretimeContext();
|
||||
const cycleEnd = get && estimateTime(currentRegionEndTs, get.blocks.relay(status?.lastTimeslice), constants.relay);
|
||||
const cycleStart = get && estimateTime(currentRegionStartTs, get.blocks.relay(status?.lastTimeslice), constants.relay);
|
||||
const { get, saleEndDate, saleStartDate } = useCoretimeContext();
|
||||
|
||||
return (
|
||||
<SummaryBox>
|
||||
@ -45,7 +42,7 @@ function Summary ({ config, constants, saleInfo, saleNumber, status }: Props): R
|
||||
{`${saleInfo?.coresSold} / ${saleInfo?.coresOffered}`}
|
||||
</CardSummary>
|
||||
<CardSummary label={t('sale end')}>
|
||||
<div>{cycleEnd}</div>
|
||||
<div>{saleEndDate}</div>
|
||||
</CardSummary>
|
||||
<CardSummary label={t('last block')}>
|
||||
<div>{get && formatNumber(get.blocks.relay(currentRegionEndTs))}</div>
|
||||
@ -70,8 +67,8 @@ function Summary ({ config, constants, saleInfo, saleNumber, status }: Props): R
|
||||
{status &&
|
||||
(<CardSummary label={t('current region dates')}>
|
||||
<div>
|
||||
<div style={{ fontSize: '14px' }}>{cycleStart}</div>
|
||||
<div style={{ fontSize: '14px' }}>{cycleEnd}</div>
|
||||
<div style={{ fontSize: '14px' }}>{saleStartDate}</div>
|
||||
<div style={{ fontSize: '14px' }}>{saleEndDate}</div>
|
||||
</div>
|
||||
</CardSummary>)
|
||||
}
|
||||
|
||||
@ -19,8 +19,7 @@ export const Cores = ({ color, phaseName, salesInfo }: { phaseName: string, sale
|
||||
const { t } = useTranslation();
|
||||
const { apiCoretime } = useApi();
|
||||
const bestNumberFinalized = useCall<BlockNumber>(apiCoretime?.derive.chain.bestNumberFinalized);
|
||||
const soldOut = useMemo(() => salesInfo.coresOffered === salesInfo.coresSold, [salesInfo.coresOffered, salesInfo.coresSold]);
|
||||
const coretimePrice = useMemo(() => bestNumberFinalized && getCorePriceAt(bestNumberFinalized.toNumber(), salesInfo), [salesInfo, bestNumberFinalized]);
|
||||
const coretimePrice = useMemo(() => bestNumberFinalized && salesInfo && getCorePriceAt(bestNumberFinalized.toNumber(), salesInfo), [salesInfo, bestNumberFinalized]);
|
||||
|
||||
const CoresWrapper = styled(WhiteBox)`
|
||||
justify-self: flex-end;
|
||||
@ -39,10 +38,7 @@ export const Cores = ({ color, phaseName, salesInfo }: { phaseName: string, sale
|
||||
)
|
||||
: (
|
||||
<div style={{ display: 'flex', flexDirection: 'column' }}>
|
||||
{soldOut && (
|
||||
<h4>{t('All cores are sold out')}</h4>
|
||||
)}
|
||||
{!soldOut && (
|
||||
{
|
||||
<div style={{ display: 'flex', flexDirection: 'column', gap: '1rem' }}>
|
||||
<div>
|
||||
<p style={{ fontSize: '14px', marginBottom: '0.15rem', opacity: '0.8' }}>{t('current price')}</p>
|
||||
@ -53,7 +49,7 @@ export const Cores = ({ color, phaseName, salesInfo }: { phaseName: string, sale
|
||||
<p style={{ fontSize: '20px' }}> {salesInfo.coresOffered - salesInfo.coresSold}</p>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
}
|
||||
</div>
|
||||
)}
|
||||
|
||||
|
||||
@ -11,7 +11,7 @@ import { CardSummary, ProgressBar, styled, SummaryBox } from '@polkadot/react-co
|
||||
import { formatBalance, formatNumber } from '@polkadot/util';
|
||||
|
||||
import { useTranslation } from '../../translate.js';
|
||||
import { getSaleProgress } from '../../utils/sale.js';
|
||||
import { getCorePriceAt, getSaleProgress } from '../../utils/sale.js';
|
||||
import { WhiteBox } from '../../WhiteBox.js';
|
||||
|
||||
const TimelineWrapper = styled(WhiteBox)`
|
||||
@ -43,6 +43,12 @@ export const Timeline = ({ color, coretimeInfo: { salesInfo, status }, phaseName
|
||||
salesInfo.regionBegin),
|
||||
[saleParams, status.lastTimeslice, salesInfo.regionBegin]);
|
||||
|
||||
const coretimePriceStart = useMemo(() => salesInfo && getCorePriceAt(salesInfo.saleStart, salesInfo), [salesInfo]);
|
||||
|
||||
const endPrice = useMemo(() => salesInfo.endPrice.toNumber() < 10 ** formatBalance.getDefaults().decimals
|
||||
? `${salesInfo.endPrice.toNumber() / 10 ** formatBalance.getDefaults().decimals} ${formatBalance.getDefaults().unit}`
|
||||
: formatBalance(salesInfo.endPrice), [salesInfo.endPrice]);
|
||||
|
||||
return (
|
||||
<TimelineWrapper>
|
||||
<p style={{ fontSize: '16px', fontWeight: 'bold' }}>{t('Sale timeline')}</p>
|
||||
@ -53,7 +59,8 @@ export const Timeline = ({ color, coretimeInfo: { salesInfo, status }, phaseName
|
||||
<CardSummary label='current phase end'>{saleParams?.phaseConfig?.config[phaseName as keyof typeof saleParams.phaseConfig.config].end.date}</CardSummary>
|
||||
<CardSummary label='last phase block'>{formatNumber(saleParams?.phaseConfig?.config[phaseName as keyof typeof saleParams.phaseConfig.config].end.blocks.relay)}</CardSummary>
|
||||
</>}
|
||||
<CardSummary label='fixed price'>{formatBalance(salesInfo.endPrice)}</CardSummary>
|
||||
<CardSummary label='start price'>{formatBalance(coretimePriceStart)}</CardSummary>
|
||||
<CardSummary label='fixed price'>{endPrice}</CardSummary>
|
||||
</section>
|
||||
<section>
|
||||
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
// Copyright 2017-2025 @polkadot/app-coretime authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import type { ChainName, SaleParameters } from '../types.js';
|
||||
import type { RelayName, SaleParameters } from '../types.js';
|
||||
|
||||
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
|
||||
@ -19,7 +19,7 @@ import SaleDetailsView from './SaleDetailsView.js';
|
||||
import Summary from './Summary.js';
|
||||
|
||||
interface Props {
|
||||
chainName: ChainName
|
||||
relayName: RelayName
|
||||
}
|
||||
|
||||
const ResponsiveGrid = styled.div`
|
||||
@ -46,7 +46,7 @@ const ResponsiveGrid = styled.div`
|
||||
}
|
||||
`;
|
||||
|
||||
function Sale ({ chainName }: Props): React.ReactElement<Props> {
|
||||
function Sale ({ relayName }: Props): React.ReactElement<Props> {
|
||||
const { coretimeInfo } = useCoretimeContext();
|
||||
const { api, apiEndpoint, isApiReady } = useApi();
|
||||
const { t } = useTranslation();
|
||||
@ -71,11 +71,11 @@ function Sale ({ chainName }: Props): React.ReactElement<Props> {
|
||||
if (coretimeInfo && !saleParams) {
|
||||
setSaleParams(getSaleParameters(
|
||||
coretimeInfo,
|
||||
chainName,
|
||||
relayName,
|
||||
lastCommittedTimeslice ?? 0
|
||||
));
|
||||
}
|
||||
}, [coretimeInfo, saleParams, lastCommittedTimeslice, chainName]);
|
||||
}, [coretimeInfo, saleParams, lastCommittedTimeslice, relayName]);
|
||||
|
||||
const phaseName = useMemo(() => saleParams?.phaseConfig?.currentPhaseName, [saleParams]);
|
||||
|
||||
@ -87,9 +87,9 @@ function Sale ({ chainName }: Props): React.ReactElement<Props> {
|
||||
return;
|
||||
}
|
||||
|
||||
setSelectedSaleParams(getSaleParameters(coretimeInfo, chainName, lastCommittedTimeslice ?? 0, value));
|
||||
setSelectedSaleParams(getSaleParameters(coretimeInfo, relayName, lastCommittedTimeslice ?? 0, value));
|
||||
}
|
||||
}, [coretimeInfo, chainName, lastCommittedTimeslice]);
|
||||
}, [coretimeInfo, relayName, lastCommittedTimeslice]);
|
||||
|
||||
return (
|
||||
<div>
|
||||
@ -97,7 +97,6 @@ function Sale ({ chainName }: Props): React.ReactElement<Props> {
|
||||
<Summary
|
||||
api={isApiReady ? api : null}
|
||||
config={coretimeInfo?.config}
|
||||
constants={coretimeInfo?.constants}
|
||||
region={coretimeInfo?.region}
|
||||
saleInfo={coretimeInfo?.salesInfo}
|
||||
saleNumber={saleParams?.saleNumber}
|
||||
@ -132,8 +131,8 @@ function Sale ({ chainName }: Props): React.ReactElement<Props> {
|
||||
</div>
|
||||
{saleParams && selectedSaleParams &&
|
||||
<SaleDetailsView
|
||||
chainName={chainName}
|
||||
chosenSaleNumber={chosenSaleNumber}
|
||||
relayName={relayName}
|
||||
saleParams={selectedSaleParams}
|
||||
/>}
|
||||
</div>
|
||||
|
||||
@ -83,7 +83,7 @@ export interface RegionInfo {
|
||||
regionEnd: number;
|
||||
}
|
||||
|
||||
export type ChainName = 'kusama' | 'polkadot' | 'paseo testnet' | 'westend'
|
||||
export type RelayName = 'kusama' | 'polkadot' | 'paseo testnet' | 'westend'
|
||||
|
||||
export interface GetResponse {
|
||||
blocks: {
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import type { ChainBlockConstants, ChainConstants, CoretimeInformation } from '@polkadot/react-hooks/types';
|
||||
import type { ChainName, GetResponse, RegionInfo } from '../types.js';
|
||||
import type { GetResponse, RegionInfo, RelayName } from '../types.js';
|
||||
|
||||
import { CoreTimeTypes } from '@polkadot/react-hooks/constants';
|
||||
import { BN } from '@polkadot/util';
|
||||
@ -11,7 +11,7 @@ type FirstCycleStartType = Record<
|
||||
'block' | 'timeslice',
|
||||
Record<
|
||||
'coretime',
|
||||
Record<ChainName, number>
|
||||
Record<RelayName, number>
|
||||
>
|
||||
>;
|
||||
|
||||
@ -41,11 +41,18 @@ export const coretimeTypeColours: Record<string, string> = {
|
||||
[CoreTimeTypes['Bulk Coretime']]: 'pink'
|
||||
};
|
||||
|
||||
export function formatDate (date: Date) {
|
||||
export function formatDate (date: Date, time = false) {
|
||||
const day = date.getDate();
|
||||
const month = date.toLocaleString('default', { month: 'short' });
|
||||
const year = date.getFullYear();
|
||||
|
||||
if (time) {
|
||||
const hours = date.getHours().toString().padStart(2, '0');
|
||||
const minutes = date.getMinutes().toString().padStart(2, '0');
|
||||
|
||||
return `${day} ${month} ${year}, ${hours}:${minutes}`;
|
||||
}
|
||||
|
||||
return `${day} ${month} ${year}`;
|
||||
}
|
||||
|
||||
@ -71,7 +78,7 @@ export const estimateTime = (
|
||||
targetTimeslice: string | number,
|
||||
latestBlock: number,
|
||||
{ blocksPerTimeslice: blocksPerTs, blocktimeMs }: ChainBlockConstants
|
||||
): string | null => {
|
||||
): { timestamp: number, formattedDate: string } | null => {
|
||||
if (!latestBlock || !targetTimeslice) {
|
||||
console.error('Invalid input: one or more inputs are missing');
|
||||
|
||||
@ -88,7 +95,10 @@ export const estimateTime = (
|
||||
const timeDifference = blockDifference.mul(blockTime);
|
||||
const estTimestamp = now.add(timeDifference);
|
||||
|
||||
return formatDate(new Date(estTimestamp.toNumber()));
|
||||
return {
|
||||
formattedDate: formatDate(new Date(estTimestamp.toNumber())),
|
||||
timestamp: estTimestamp.toNumber()
|
||||
};
|
||||
} catch (error) {
|
||||
console.error('Error in calculation:', error);
|
||||
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import type { ChainConstants, PalletBrokerConfigRecord, PalletBrokerSaleInfoRecord } from '@polkadot/react-hooks/types';
|
||||
import type { ChainName, GetResponse, PhaseConfig, RegionInfo, SaleParameters } from '../types.js';
|
||||
import type { GetResponse, PhaseConfig, RegionInfo, RelayName, SaleParameters } from '../types.js';
|
||||
|
||||
import { type ProgressBarSection } from '@polkadot/react-components/types';
|
||||
import { BN } from '@polkadot/util';
|
||||
@ -69,14 +69,14 @@ export const getCorePriceAt = (blockNow: number | null, saleInfo: PalletBrokerSa
|
||||
*/
|
||||
export const getCurrentSaleNumber = (
|
||||
currentRegionEnd: number,
|
||||
chainName: ChainName,
|
||||
relayName: RelayName,
|
||||
config: Pick<PalletBrokerConfigRecord, 'interludeLength' | 'leadinLength' | 'regionLength'>
|
||||
): number => {
|
||||
if (!chainName || !currentRegionEnd) {
|
||||
if (!relayName || !currentRegionEnd) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return Math.ceil((currentRegionEnd - FirstCycleStart.timeslice.coretime[chainName]) / config.regionLength);
|
||||
return Math.ceil((currentRegionEnd - FirstCycleStart.timeslice.coretime[relayName]) / config.regionLength);
|
||||
};
|
||||
|
||||
export const determinePhaseName = (
|
||||
@ -164,11 +164,11 @@ const getPhaseConfiguration = (
|
||||
const fixedPriceLengthTs = regionLength - interludeLengthTs - leadInLengthTs;
|
||||
const fixedPriceEndTs = priceDiscoveryEndTs + fixedPriceLengthTs;
|
||||
const get = createGet(constants);
|
||||
const getDate = (ts: number) => estimateTime(ts, get.blocks.relay(lastCommittedTimeslice) ?? 0, constants.relay);
|
||||
const getDate = (ts: number) => estimateTime(ts, get.blocks.relay(lastCommittedTimeslice), constants.relay)?.formattedDate ?? null;
|
||||
|
||||
return {
|
||||
config: {
|
||||
[PhaseName.FixedPrice]: makeConfig(fixedPriceEndTs, fixedPriceEndTs, get, getDate, PhaseName.FixedPrice),
|
||||
[PhaseName.FixedPrice]: makeConfig(priceDiscoveryEndTs, fixedPriceEndTs, get, getDate, PhaseName.FixedPrice),
|
||||
[PhaseName.PriceDiscovery]: makeConfig(renewalsEndTs, priceDiscoveryEndTs, get, getDate, PhaseName.PriceDiscovery),
|
||||
[PhaseName.Renewals]: makeConfig(currentRegionStart, renewalsEndTs, get, getDate, PhaseName.Renewals)
|
||||
},
|
||||
@ -178,7 +178,7 @@ const getPhaseConfiguration = (
|
||||
|
||||
export const getSaleParameters = (
|
||||
{ config, constants, salesInfo }: {salesInfo: RegionInfo, config: Pick<PalletBrokerConfigRecord, 'interludeLength' | 'leadinLength' | 'regionLength'>, constants: ChainConstants},
|
||||
chainName: ChainName,
|
||||
relayName: RelayName,
|
||||
lastCommittedTimeslice: number,
|
||||
chosenSaleNumber = -1
|
||||
): SaleParameters => {
|
||||
@ -186,30 +186,30 @@ export const getSaleParameters = (
|
||||
const interludeLengthTs = get.timeslices.coretime(config.interludeLength);
|
||||
const leadInLengthTs = get.timeslices.coretime(config.leadinLength);
|
||||
let { currentRegionEndTs, currentRegionStartTs } = getCurrentRegionStartEndTs(salesInfo, config.regionLength);
|
||||
const getDate = (ts: number) => estimateTime(ts, get.blocks.relay(lastCommittedTimeslice), constants.relay) || '';
|
||||
const saleNumber = getCurrentSaleNumber(currentRegionEndTs, chainName, config);
|
||||
const getDate = (ts: number) => estimateTime(ts, get.blocks.relay(lastCommittedTimeslice), constants.relay)?.formattedDate ?? null;
|
||||
const saleNumber = getCurrentSaleNumber(currentRegionEndTs, relayName, config);
|
||||
|
||||
let currentRegionInfo: SaleParameters['currentRegion'];
|
||||
|
||||
if (chosenSaleNumber !== -1) {
|
||||
// A hack for Kusama as one of the sales had an unusual length
|
||||
// checked against Subscan historical sales
|
||||
if (chainName === 'kusama') {
|
||||
if (relayName === 'kusama') {
|
||||
const irregularRegionLength = 848;
|
||||
|
||||
if (chosenSaleNumber === 0) {
|
||||
currentRegionStartTs = FirstCycleStart.timeslice.coretime[chainName];
|
||||
currentRegionStartTs = FirstCycleStart.timeslice.coretime[relayName];
|
||||
currentRegionEndTs = currentRegionStartTs + config.regionLength;
|
||||
} else if (chosenSaleNumber === 1) {
|
||||
currentRegionStartTs = FirstCycleStart.timeslice.coretime[chainName] + config.regionLength;
|
||||
currentRegionStartTs = FirstCycleStart.timeslice.coretime[relayName] + config.regionLength;
|
||||
// that particular sale #2 was only 848 blocks long
|
||||
currentRegionEndTs = currentRegionStartTs + irregularRegionLength;
|
||||
} else {
|
||||
currentRegionStartTs = FirstCycleStart.timeslice.coretime[chainName] + config.regionLength * (chosenSaleNumber - 1) + irregularRegionLength;
|
||||
currentRegionStartTs = FirstCycleStart.timeslice.coretime[relayName] + config.regionLength * (chosenSaleNumber - 1) + irregularRegionLength;
|
||||
currentRegionEndTs = currentRegionStartTs + config.regionLength;
|
||||
}
|
||||
} else {
|
||||
currentRegionStartTs = FirstCycleStart.timeslice.coretime[chainName] + config.regionLength * chosenSaleNumber;
|
||||
currentRegionStartTs = FirstCycleStart.timeslice.coretime[relayName] + config.regionLength * chosenSaleNumber;
|
||||
currentRegionEndTs = currentRegionStartTs + config.regionLength;
|
||||
}
|
||||
|
||||
@ -220,7 +220,7 @@ export const getSaleParameters = (
|
||||
coretime: 0,
|
||||
relay: get.blocks.relay(currentRegionEndTs)
|
||||
},
|
||||
date: getDate(currentRegionEndTs),
|
||||
date: getDate(currentRegionEndTs) ?? '',
|
||||
ts: currentRegionEndTs
|
||||
},
|
||||
start: {
|
||||
@ -229,7 +229,7 @@ export const getSaleParameters = (
|
||||
coretime: 0,
|
||||
relay: get.blocks.relay(currentRegionStartTs)
|
||||
},
|
||||
date: getDate(currentRegionStartTs),
|
||||
date: getDate(currentRegionStartTs) ?? '',
|
||||
ts: currentRegionStartTs
|
||||
}
|
||||
};
|
||||
@ -267,7 +267,7 @@ export const getSaleParameters = (
|
||||
coretime: 0,
|
||||
relay: currentRegionInfo.end.blocks.relay + get.blocks.relay(config.regionLength)
|
||||
},
|
||||
date: estimateTime(currentRegionInfo.end.ts + config.regionLength, get.blocks.relay(lastCommittedTimeslice), constants.relay),
|
||||
date: estimateTime(currentRegionInfo.end.ts + config.regionLength, get.blocks.relay(lastCommittedTimeslice), constants.relay)?.formattedDate ?? null,
|
||||
ts: currentRegionInfo.end.ts + config.regionLength
|
||||
},
|
||||
start: {
|
||||
|
||||
@ -35,6 +35,7 @@ export { useCallMulti } from './useCallMulti.js';
|
||||
export { useCollectiveInstance } from './useCollectiveInstance.js';
|
||||
export { useCollectiveMembers } from './useCollectiveMembers.js';
|
||||
export { useCoreDescriptor } from './useCoreDescriptor.js';
|
||||
export { useCoretimeConsts } from './useCoretimeConsts.js';
|
||||
export { useCoretimeEndpoint } from './useCoretimeEndpoint.js';
|
||||
export { useCoretimeInformation } from './useCoretimeInformation.js';
|
||||
export { useDebounce } from './useDebounce.js';
|
||||
|
||||
Loading…
Reference in New Issue
Block a user