From d5b5e701949569af2147d69c5925d2e8d1855d8d Mon Sep 17 00:00:00 2001 From: Jaco Date: Tue, 23 May 2023 20:25:37 +0300 Subject: [PATCH 1/6] Add disputes overview --- .../page-parachains/src/Disputes/index.tsx | 49 +++++++++++++++ .../page-parachains/src/Disputes/types.ts | 15 +++++ .../src/Disputes/useSessionDisputes.ts | 60 +++++++++++++++++++ .../src/Disputes/useSessionInfo.ts | 32 ++++++++++ packages/page-parachains/src/index.tsx | 7 +++ 5 files changed, 163 insertions(+) create mode 100644 packages/page-parachains/src/Disputes/index.tsx create mode 100644 packages/page-parachains/src/Disputes/types.ts create mode 100644 packages/page-parachains/src/Disputes/useSessionDisputes.ts create mode 100644 packages/page-parachains/src/Disputes/useSessionInfo.ts diff --git a/packages/page-parachains/src/Disputes/index.tsx b/packages/page-parachains/src/Disputes/index.tsx new file mode 100644 index 000000000000..249d45ae2581 --- /dev/null +++ b/packages/page-parachains/src/Disputes/index.tsx @@ -0,0 +1,49 @@ +// Copyright 2017-2023 @polkadot/app-parachains authors & contributors +// SPDX-License-Identifier: Apache-2.0 + +import React, { useRef } from 'react'; + +import { AddressMini, Table } from '@polkadot/react-components'; + +import { useTranslation } from '../translate.js'; +import useSessionDisputes from './useSessionDisputes.js'; + +interface Props { + className?: string; +} + +function Disputes ({ className }: Props): React.ReactElement { + const { t } = useTranslation(); + const disputeInfo = useSessionDisputes(); + + const headerRef = useRef<[React.ReactNode?, string?, number?][]>([ + [t('disputes'), 'start', 2] + ]); + + return ( + ('No ongoing disputes found')} + header={headerRef.current} + > + {disputeInfo?.disputes && Object + .entries(disputeInfo?.disputes) + .map(([d, v]) => ( + + + + + )) + } +
{d} + {v.map((v) => ( + + ))} +
+ ); +} + +export default React.memo(Disputes); diff --git a/packages/page-parachains/src/Disputes/types.ts b/packages/page-parachains/src/Disputes/types.ts new file mode 100644 index 000000000000..05bf8309ee4d --- /dev/null +++ b/packages/page-parachains/src/Disputes/types.ts @@ -0,0 +1,15 @@ +// Copyright 2017-2023 @polkadot/app-parachains authors & contributors +// SPDX-License-Identifier: Apache-2.0 + +import type { u32 } from '@polkadot/types'; + +export interface SessionInfo { + paraValidators: string[]; + sessionIndex: u32; + sessionValidators: string[]; +} + +export interface DisputeInfo { + disputes?: Record; + sessionInfo: SessionInfo; +} diff --git a/packages/page-parachains/src/Disputes/useSessionDisputes.ts b/packages/page-parachains/src/Disputes/useSessionDisputes.ts new file mode 100644 index 000000000000..91fb81aa25d2 --- /dev/null +++ b/packages/page-parachains/src/Disputes/useSessionDisputes.ts @@ -0,0 +1,60 @@ +// Copyright 2017-2023 @polkadot/app-parachains authors & contributors +// SPDX-License-Identifier: Apache-2.0 + +import type { Option, StorageKey } from '@polkadot/types'; +import type { PolkadotPrimitivesV4DisputeState } from '@polkadot/types/lookup'; +import type { Codec } from '@polkadot/types/types'; +import type { HexString } from '@polkadot/util/types'; +import type { DisputeInfo, SessionInfo } from './types.js'; + +import { useMemo } from 'react'; + +import { createNamedHook, useApi, useMapEntries } from '@polkadot/react-hooks'; + +import useSessionInfo from './useSessionInfo.js'; + +const OPT_ENTRIES = { + transform: (entries: [StorageKey<[Codec, Codec]>, Option][]): [HexString, boolean[]][] => + entries + .map(([{ args: [, id] }, optInfo]): [HexString, PolkadotPrimitivesV4DisputeState | null] => [id.toHex(), optInfo.unwrapOr(null)]) + .filter((d): d is [HexString, PolkadotPrimitivesV4DisputeState] => !!d[1]) + .map(([k, d]) => [k, d.validatorsAgainst.toBoolArray()]) +}; + +function extractDisputes (sessionInfo?: SessionInfo, disputes?: [HexString, boolean[]][]): DisputeInfo | undefined { + if (!sessionInfo) { + return undefined; + } else if (!disputes) { + return { sessionInfo }; + } + + console.log(disputes); + + return { + disputes: disputes.reduce>((all, [k, v]) => { + all[k] = v + .map((f, index) => + f + ? sessionInfo.paraValidators[index] + : null + ) + .filter((v): v is string => !!v); + + return all; + }, {}), + sessionInfo + }; +} + +function useSessionDisputesImpl (): DisputeInfo | undefined { + const { api } = useApi(); + const session = useSessionInfo(); + const disputedBv = useMapEntries(session && api.query.parasDisputes?.disputes, [session?.sessionIndex], OPT_ENTRIES); + + return useMemo( + () => extractDisputes(session, disputedBv), + [disputedBv, session] + ); +} + +export default createNamedHook('useSessionDisputes', useSessionDisputesImpl); diff --git a/packages/page-parachains/src/Disputes/useSessionInfo.ts b/packages/page-parachains/src/Disputes/useSessionInfo.ts new file mode 100644 index 000000000000..f195d7c9cce3 --- /dev/null +++ b/packages/page-parachains/src/Disputes/useSessionInfo.ts @@ -0,0 +1,32 @@ +// Copyright 2017-2023 @polkadot/app-parachains authors & contributors +// SPDX-License-Identifier: Apache-2.0 + +import type { u32 } from '@polkadot/types'; +import type { AccountId } from '@polkadot/types/interfaces'; +import type { SessionInfo } from './types.js'; + +import { createNamedHook, useApi, useCallMulti } from '@polkadot/react-hooks'; + +const OPT_MULTI = { + transform: ([sessionIndex, validators, activeValidatorIndices]: [u32, AccountId[], u32[]]): SessionInfo => { + const sessionValidators = validators.map((v) => v.toString()); + + return { + paraValidators: activeValidatorIndices.map((i) => sessionValidators[i.toNumber()]), + sessionIndex, + sessionValidators + }; + } +}; + +function useSessionInfoImpl (): SessionInfo | undefined { + const { api } = useApi(); + + return useCallMulti([ + api.query.session?.currentIndex, + api.query.session?.validators, + api.query.parasShared?.activeValidatorIndices + ], OPT_MULTI); +} + +export default createNamedHook('useSessionInfo', useSessionInfoImpl); diff --git a/packages/page-parachains/src/index.tsx b/packages/page-parachains/src/index.tsx index aef7186219be..c18b33a58bf4 100644 --- a/packages/page-parachains/src/index.tsx +++ b/packages/page-parachains/src/index.tsx @@ -14,6 +14,7 @@ import { useApi, useCall } from '@polkadot/react-hooks'; import Auctions from './Auctions/index.js'; import Crowdloan from './Crowdloan/index.js'; +import Disputes from './Disputes/index.js'; import Overview from './Overview/index.js'; import Parathreads from './Parathreads/index.js'; import Proposals from './Proposals/index.js'; @@ -100,6 +101,12 @@ function ParachainsApp ({ basePath, className }: Props): React.ReactElement + + } + path='disputes' + /> From aeee43bd00838701c5149634a7ede86af70fdb00 Mon Sep 17 00:00:00 2001 From: Jaco Date: Wed, 24 May 2023 09:05:29 +0300 Subject: [PATCH 2/6] Include sessionIndex --- .../page-parachains/src/Disputes/index.tsx | 64 ++++++++++++++----- .../page-parachains/src/Disputes/types.ts | 10 ++- .../src/Disputes/useSessionDisputes.ts | 25 ++++---- .../src/Disputes/useSessionInfo.ts | 21 +++++- 4 files changed, 86 insertions(+), 34 deletions(-) diff --git a/packages/page-parachains/src/Disputes/index.tsx b/packages/page-parachains/src/Disputes/index.tsx index 249d45ae2581..e9ad0efc064c 100644 --- a/packages/page-parachains/src/Disputes/index.tsx +++ b/packages/page-parachains/src/Disputes/index.tsx @@ -1,7 +1,9 @@ // Copyright 2017-2023 @polkadot/app-parachains authors & contributors // SPDX-License-Identifier: Apache-2.0 -import React, { useRef } from 'react'; +import type { DisputeRecord } from './types.js'; + +import React, { useMemo, useRef } from 'react'; import { AddressMini, Table } from '@polkadot/react-components'; @@ -12,6 +14,44 @@ interface Props { className?: string; } +function transposeDisputes (disputes: DisputeRecord): React.ReactNode[] { + let lastSession = ''; + + return Object + .entries(disputes) + .reduce((flattened: [string, string, string[]][], [s, r]) => + Object + .entries(r) + .reduce((flattened, [k, vals]) => { + flattened.push([s, k, vals]); + + return flattened; + }, flattened), [] + ) + .map(([s, k, vals], index) => { + let session = ''; + + if (lastSession !== s) { + session = lastSession = s; + } + + return ( + + {session} + {k} + + {vals.map((v) => ( + + ))} + + + ); + }); +} + function Disputes ({ className }: Props): React.ReactElement { const { t } = useTranslation(); const disputeInfo = useSessionDisputes(); @@ -20,28 +60,18 @@ function Disputes ({ className }: Props): React.ReactElement { [t('disputes'), 'start', 2] ]); + const rows = useMemo( + () => disputeInfo?.disputes && transposeDisputes(disputeInfo.disputes), + [disputeInfo] + ); + return ( ('No ongoing disputes found')} header={headerRef.current} > - {disputeInfo?.disputes && Object - .entries(disputeInfo?.disputes) - .map(([d, v]) => ( - - - - - )) - } + {rows}
{d} - {v.map((v) => ( - - ))} -
); } diff --git a/packages/page-parachains/src/Disputes/types.ts b/packages/page-parachains/src/Disputes/types.ts index 05bf8309ee4d..5d23436f3b7c 100644 --- a/packages/page-parachains/src/Disputes/types.ts +++ b/packages/page-parachains/src/Disputes/types.ts @@ -1,15 +1,19 @@ // Copyright 2017-2023 @polkadot/app-parachains authors & contributors // SPDX-License-Identifier: Apache-2.0 -import type { u32 } from '@polkadot/types'; +import type { BN } from '@polkadot/util'; +import type { HexString } from '@polkadot/util/types'; export interface SessionInfo { paraValidators: string[]; - sessionIndex: u32; + sessionCurrentIndex: BN; + sessionIndexes: BN[]; sessionValidators: string[]; } +export type DisputeRecord = Record>; + export interface DisputeInfo { - disputes?: Record; + disputes?: DisputeRecord; sessionInfo: SessionInfo; } diff --git a/packages/page-parachains/src/Disputes/useSessionDisputes.ts b/packages/page-parachains/src/Disputes/useSessionDisputes.ts index 91fb81aa25d2..144c442475d4 100644 --- a/packages/page-parachains/src/Disputes/useSessionDisputes.ts +++ b/packages/page-parachains/src/Disputes/useSessionDisputes.ts @@ -1,7 +1,7 @@ // Copyright 2017-2023 @polkadot/app-parachains authors & contributors // SPDX-License-Identifier: Apache-2.0 -import type { Option, StorageKey } from '@polkadot/types'; +import type { Option, StorageKey, u32 } from '@polkadot/types'; import type { PolkadotPrimitivesV4DisputeState } from '@polkadot/types/lookup'; import type { Codec } from '@polkadot/types/types'; import type { HexString } from '@polkadot/util/types'; @@ -10,29 +10,32 @@ import type { DisputeInfo, SessionInfo } from './types.js'; import { useMemo } from 'react'; import { createNamedHook, useApi, useMapEntries } from '@polkadot/react-hooks'; +import { formatNumber } from '@polkadot/util'; import useSessionInfo from './useSessionInfo.js'; const OPT_ENTRIES = { - transform: (entries: [StorageKey<[Codec, Codec]>, Option][]): [HexString, boolean[]][] => + transform: (entries: [StorageKey<[u32, Codec]>, Option][]): [string, HexString, boolean[]][] => entries - .map(([{ args: [, id] }, optInfo]): [HexString, PolkadotPrimitivesV4DisputeState | null] => [id.toHex(), optInfo.unwrapOr(null)]) - .filter((d): d is [HexString, PolkadotPrimitivesV4DisputeState] => !!d[1]) - .map(([k, d]) => [k, d.validatorsAgainst.toBoolArray()]) + .map(([{ args: [session, id] }, optInfo]): [string, HexString, PolkadotPrimitivesV4DisputeState | null] => [formatNumber(session), id.toHex(), optInfo.unwrapOr(null)]) + .filter((d): d is [string, HexString, PolkadotPrimitivesV4DisputeState] => !!d[2]) + .map(([s, k, d]) => [s, k, d.validatorsAgainst.toBoolArray()]) }; -function extractDisputes (sessionInfo?: SessionInfo, disputes?: [HexString, boolean[]][]): DisputeInfo | undefined { +function extractDisputes (sessionInfo?: SessionInfo, disputes?: [string, HexString, boolean[]][]): DisputeInfo | undefined { if (!sessionInfo) { return undefined; } else if (!disputes) { return { sessionInfo }; } - console.log(disputes); - return { - disputes: disputes.reduce>((all, [k, v]) => { - all[k] = v + disputes: disputes.reduce>>((all, [s, k, v]) => { + if (!all[s]) { + all[s] = {}; + } + + all[s][k] = v .map((f, index) => f ? sessionInfo.paraValidators[index] @@ -49,7 +52,7 @@ function extractDisputes (sessionInfo?: SessionInfo, disputes?: [HexString, bool function useSessionDisputesImpl (): DisputeInfo | undefined { const { api } = useApi(); const session = useSessionInfo(); - const disputedBv = useMapEntries(session && api.query.parasDisputes?.disputes, [session?.sessionIndex], OPT_ENTRIES); + const disputedBv = useMapEntries(session && api.query.parasDisputes?.disputes, [session?.sessionCurrentIndex], OPT_ENTRIES); return useMemo( () => extractDisputes(session, disputedBv), diff --git a/packages/page-parachains/src/Disputes/useSessionInfo.ts b/packages/page-parachains/src/Disputes/useSessionInfo.ts index f195d7c9cce3..49b0ae023b18 100644 --- a/packages/page-parachains/src/Disputes/useSessionInfo.ts +++ b/packages/page-parachains/src/Disputes/useSessionInfo.ts @@ -1,19 +1,33 @@ // Copyright 2017-2023 @polkadot/app-parachains authors & contributors // SPDX-License-Identifier: Apache-2.0 -import type { u32 } from '@polkadot/types'; +import type { Option, u32 } from '@polkadot/types'; import type { AccountId } from '@polkadot/types/interfaces'; +import type { BN } from '@polkadot/util'; import type { SessionInfo } from './types.js'; import { createNamedHook, useApi, useCallMulti } from '@polkadot/react-hooks'; +import { BN_ONE } from '@polkadot/util'; const OPT_MULTI = { - transform: ([sessionIndex, validators, activeValidatorIndices]: [u32, AccountId[], u32[]]): SessionInfo => { + transform: ([sessionCurrentIndex, validators, optLastPruned, activeValidatorIndices]: [u32, AccountId[], Option, u32[]]): SessionInfo => { const sessionValidators = validators.map((v) => v.toString()); + const sessionIndexes: BN[] = [sessionCurrentIndex]; + + if (optLastPruned.isSome) { + const lastPruned = optLastPruned.unwrap(); + const nextIndex = sessionCurrentIndex.sub(BN_ONE); + + while (nextIndex.gt(lastPruned)) { + sessionIndexes.push(nextIndex); + nextIndex.isub(BN_ONE); + } + } return { paraValidators: activeValidatorIndices.map((i) => sessionValidators[i.toNumber()]), - sessionIndex, + sessionCurrentIndex, + sessionIndexes, sessionValidators }; } @@ -25,6 +39,7 @@ function useSessionInfoImpl (): SessionInfo | undefined { return useCallMulti([ api.query.session?.currentIndex, api.query.session?.validators, + api.query.parasDisputes?.lastPrunedSession, api.query.parasShared?.activeValidatorIndices ], OPT_MULTI); } From 96eb0dc8d6c10cdf1a78f1c7d4b2f22afb138f7f Mon Sep 17 00:00:00 2001 From: Jaco Date: Wed, 24 May 2023 09:14:07 +0300 Subject: [PATCH 3/6] Add session indicator --- packages/page-parachains/src/Disputes/index.tsx | 6 +++--- packages/react-components/src/Table/Column/Id.tsx | 12 +++++++++--- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/packages/page-parachains/src/Disputes/index.tsx b/packages/page-parachains/src/Disputes/index.tsx index e9ad0efc064c..6ad07ecc385c 100644 --- a/packages/page-parachains/src/Disputes/index.tsx +++ b/packages/page-parachains/src/Disputes/index.tsx @@ -37,7 +37,7 @@ function transposeDisputes (disputes: DisputeRecord): React.ReactNode[] { return ( - {session} + {k} {vals.map((v) => ( @@ -57,7 +57,7 @@ function Disputes ({ className }: Props): React.ReactElement { const disputeInfo = useSessionDisputes(); const headerRef = useRef<[React.ReactNode?, string?, number?][]>([ - [t('disputes'), 'start', 2] + [t('disputes'), 'start', 3] ]); const rows = useMemo( @@ -68,7 +68,7 @@ function Disputes ({ className }: Props): React.ReactElement { return ( ('No ongoing disputes found')} + empty={rows && t('No ongoing disputes found')} header={headerRef.current} > {rows} diff --git a/packages/react-components/src/Table/Column/Id.tsx b/packages/react-components/src/Table/Column/Id.tsx index a97d7dc7fa91..10d3372482f1 100644 --- a/packages/react-components/src/Table/Column/Id.tsx +++ b/packages/react-components/src/Table/Column/Id.tsx @@ -5,7 +5,7 @@ import type { BN } from '@polkadot/util'; import React from 'react'; -import { formatNumber } from '@polkadot/util'; +import { formatNumber, isString } from '@polkadot/util'; import { styled } from '../../styled.js'; @@ -14,7 +14,7 @@ export interface Props { className?: string; colSpan?: number; rowSpan?: number; - value: BN | number; + value: BN | number | string; } function Id ({ children, className = '', colSpan, rowSpan, value }: Props): React.ReactElement { @@ -24,7 +24,13 @@ function Id ({ children, className = '', colSpan, rowSpan, value }: Props): Reac colSpan={colSpan} rowSpan={rowSpan} > -

{formatNumber(value)}

+

+ { + isString(value) + ? value + : formatNumber(value) + } +

{children} ); From ca70001874ecbfe718ce56b16a5ce8bc80bfdf7e Mon Sep 17 00:00:00 2001 From: Jaco Date: Wed, 24 May 2023 09:19:00 +0300 Subject: [PATCH 4/6] Adjust display of session --- packages/page-parachains/src/Disputes/index.tsx | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/packages/page-parachains/src/Disputes/index.tsx b/packages/page-parachains/src/Disputes/index.tsx index 6ad07ecc385c..46c78d246c46 100644 --- a/packages/page-parachains/src/Disputes/index.tsx +++ b/packages/page-parachains/src/Disputes/index.tsx @@ -16,6 +16,7 @@ interface Props { function transposeDisputes (disputes: DisputeRecord): React.ReactNode[] { let lastSession = ''; + let inclSession = true; return Object .entries(disputes) @@ -29,15 +30,20 @@ function transposeDisputes (disputes: DisputeRecord): React.ReactNode[] { }, flattened), [] ) .map(([s, k, vals], index) => { - let session = ''; - if (lastSession !== s) { - session = lastSession = s; + lastSession = s; + inclSession = true; + } else { + inclSession = false; } return ( -
- + + { + inclSession + ? + : + +
+ } {k} {vals.map((v) => ( From 163d518980b1fe33b0606ad127891675f8753fba Mon Sep 17 00:00:00 2001 From: Jaco Date: Thu, 25 May 2023 12:05:27 +0300 Subject: [PATCH 5/6] All non-pruned sessions --- .../page-parachains/src/Disputes/index.tsx | 2 +- .../src/Disputes/useSessionDisputes.ts | 88 ++++++++++--------- 2 files changed, 49 insertions(+), 41 deletions(-) diff --git a/packages/page-parachains/src/Disputes/index.tsx b/packages/page-parachains/src/Disputes/index.tsx index 46c78d246c46..8066dc3b8c48 100644 --- a/packages/page-parachains/src/Disputes/index.tsx +++ b/packages/page-parachains/src/Disputes/index.tsx @@ -44,7 +44,7 @@ function transposeDisputes (disputes: DisputeRecord): React.ReactNode[] { ? : } - {k}{k} {vals.map((v) => ( , Option][]): [string, HexString, boolean[]][] => - entries - .map(([{ args: [session, id] }, optInfo]): [string, HexString, PolkadotPrimitivesV4DisputeState | null] => [formatNumber(session), id.toHex(), optInfo.unwrapOr(null)]) - .filter((d): d is [string, HexString, PolkadotPrimitivesV4DisputeState] => !!d[2]) - .map(([s, k, d]) => [s, k, d.validatorsAgainst.toBoolArray()]) -}; - -function extractDisputes (sessionInfo?: SessionInfo, disputes?: [string, HexString, boolean[]][]): DisputeInfo | undefined { - if (!sessionInfo) { - return undefined; - } else if (!disputes) { - return { sessionInfo }; - } - - return { - disputes: disputes.reduce>>((all, [s, k, v]) => { - if (!all[s]) { - all[s] = {}; - } +function queryDisputes (api: ApiPromise, sessionInfo: SessionInfo): Promise { + return Promise + .all( + sessionInfo.sessionIndexes.map((index) => + api.query.parasDisputes.disputes.entries(index) as unknown as Promise<[StorageKey<[u32, Codec]>, Option][]> + ) + ) + .then((entries) => ({ + disputes: entries.reduce>>((all, list) => { + list + .map(([{ args: [session, id] }, optInfo]): [string, HexString, PolkadotPrimitivesV4DisputeState | null] => [formatNumber(session), id.toHex(), optInfo.unwrapOr(null)]) + .filter((d): d is [string, HexString, PolkadotPrimitivesV4DisputeState] => !!d[2]) + .map(([s, k, d]): [string, HexString, boolean[]] => [s, k, d.validatorsAgainst.toBoolArray()]) + .forEach(([s, k, v]) => { + if (!all[s]) { + all[s] = {}; + } + + all[s][k] = v + .map((f, index) => + f + ? sessionInfo.paraValidators[index] + : null + ) + .filter((v): v is string => !!v); + }); - all[s][k] = v - .map((f, index) => - f - ? sessionInfo.paraValidators[index] - : null - ) - .filter((v): v is string => !!v); - - return all; - }, {}), - sessionInfo - }; + return all; + }, {}), + sessionInfo + })); } function useSessionDisputesImpl (): DisputeInfo | undefined { const { api } = useApi(); - const session = useSessionInfo(); - const disputedBv = useMapEntries(session && api.query.parasDisputes?.disputes, [session?.sessionCurrentIndex], OPT_ENTRIES); + const [state, setState] = useState(); + const sessionInfo = useSessionInfo(); + + useEffect((): void => { + if (sessionInfo) { + if (sessionInfo.sessionIndexes) { + queryDisputes(api, sessionInfo) + .then(setState) + .catch(console.error); + } else { + setState({ sessionInfo }); + } + } + }, [api, sessionInfo]); - return useMemo( - () => extractDisputes(session, disputedBv), - [disputedBv, session] - ); + return state; } export default createNamedHook('useSessionDisputes', useSessionDisputesImpl); From b02ffe23918670f44c069940d2238958b5a3014f Mon Sep 17 00:00:00 2001 From: Jaco Date: Thu, 25 May 2023 13:53:46 +0300 Subject: [PATCH 6/6] Adjust for current session only --- .../page-parachains/src/Disputes/index.tsx | 2 +- .../src/Disputes/useSessionDisputes.ts | 48 +++++++++++-------- 2 files changed, 28 insertions(+), 22 deletions(-) diff --git a/packages/page-parachains/src/Disputes/index.tsx b/packages/page-parachains/src/Disputes/index.tsx index 8066dc3b8c48..00e7c925b3da 100644 --- a/packages/page-parachains/src/Disputes/index.tsx +++ b/packages/page-parachains/src/Disputes/index.tsx @@ -44,7 +44,7 @@ function transposeDisputes (disputes: DisputeRecord): React.ReactNode[] { ? : } - {k}{k} {vals.map((v) => ( { return Promise .all( - sessionInfo.sessionIndexes.map((index) => - api.query.parasDisputes.disputes.entries(index) as unknown as Promise<[StorageKey<[u32, Codec]>, Option][]> + // FIXME We would need to pull the list of validators alongside + // sessionInfo.sessionIndexes.map((index) => + [sessionInfo.sessionCurrentIndex].map((index) => + api.query.parasDisputes.disputes.entries(index) as unknown as Promise<[StorageKey<[u32, Hash]>, Option][]> + ) + ) + .then((entries) => + // TODO Here we wish to extract the actual sessionValidators as well + entries.map((list) => + list.map(([{ args: [session, id] }, optInfo]): [BN, Hash, boolean[]] => [session, id, optInfo.isSome ? optInfo.unwrap().validatorsAgainst.toBoolArray() : []]) ) ) .then((entries) => ({ - disputes: entries.reduce>>((all, list) => { - list - .map(([{ args: [session, id] }, optInfo]): [string, HexString, PolkadotPrimitivesV4DisputeState | null] => [formatNumber(session), id.toHex(), optInfo.unwrapOr(null)]) - .filter((d): d is [string, HexString, PolkadotPrimitivesV4DisputeState] => !!d[2]) - .map(([s, k, d]): [string, HexString, boolean[]] => [s, k, d.validatorsAgainst.toBoolArray()]) - .forEach(([s, k, v]) => { - if (!all[s]) { - all[s] = {}; + disputes: entries.reduce>>((all, list) => + list.reduce((all, [sessionIndex, hash, flags]) => { + const s = formatNumber(sessionIndex); + + if (!all[s]) { + all[s] = {}; + } + + all[s][hash.toHex()] = flags.reduce((vals, flag, index) => { + if (flag) { + vals.push(sessionInfo.paraValidators[index]); } - all[s][k] = v - .map((f, index) => - f - ? sessionInfo.paraValidators[index] - : null - ) - .filter((v): v is string => !!v); - }); + return vals; + }, []); - return all; - }, {}), + return all; + }, all), {}), sessionInfo })); }