From 97b4217610dc57240b2eba38e1a80e62a8a635cc Mon Sep 17 00:00:00 2001 From: Jaco Date: Sat, 2 Apr 2022 09:04:00 +0200 Subject: [PATCH] Perform per-loaded bag rebag calculations (#7288) --- packages/page-staking/src/Bags/Bag.tsx | 25 ++++++++++++++++++- packages/page-staking/src/Bags/index.tsx | 10 +++----- packages/page-staking/src/Bags/useBagsIds.ts | 4 +-- packages/page-staking/src/Bags/useBagsList.ts | 4 +-- .../page-staking/src/Bags/useBagsNodes.tsx | 4 +-- packages/page-staking/src/Bags/useBonded.ts | 4 +-- 6 files changed, 36 insertions(+), 15 deletions(-) diff --git a/packages/page-staking/src/Bags/Bag.tsx b/packages/page-staking/src/Bags/Bag.tsx index 31bc3a65d7..cbecfd0cdc 100644 --- a/packages/page-staking/src/Bags/Bag.tsx +++ b/packages/page-staking/src/Bags/Bag.tsx @@ -4,7 +4,7 @@ import type { AccountId32 } from '@polkadot/types/interfaces'; import type { PalletBagsListListBag } from '@polkadot/types/lookup'; import type { BN } from '@polkadot/util'; -import type { StashNode } from './types'; +import type { ListNode, StashNode } from './types'; import React, { useEffect, useState } from 'react'; @@ -24,8 +24,21 @@ interface Props { upper: BN; } +function getRebags (bonded: ListNode[], stashNodes: StashNode[], lower: BN, upper: BN): string[] { + return bonded + .filter(({ bonded, stashId }) => + ( + bonded.gt(upper) || + bonded.lt(lower) + ) && + stashNodes.every((n) => n.stashId !== stashId) + ) + .map(({ stashId }) => stashId); +} + function Bag ({ info, lower, stashNodes, upper }: Props): React.ReactElement { const [[headId, trigger], setHeadId] = useState<[AccountId32 | null, number]>([null, 0]); + const [, setRebags] = useState([]); const [isLoading, setLoading] = useState(true); const [isCompleted, list] = useBagEntries(headId, trigger); const bonded = useBonded(list); @@ -43,6 +56,16 @@ function Bag ({ info, lower, stashNodes, upper }: Props): React.ReactElement { + // NOTE: There may be some issues if these are indeed rebagged - we need + // the StashNode to see if this item is indeed still within this list + !isLoading && bonded && stashNodes && setRebags( + getRebags(bonded, stashNodes, lower, upper) + ); + }, [bonded, isLoading, lower, stashNodes, upper]); + return ( diff --git a/packages/page-staking/src/Bags/index.tsx b/packages/page-staking/src/Bags/index.tsx index 40aebf7d3f..a5db4db44f 100644 --- a/packages/page-staking/src/Bags/index.tsx +++ b/packages/page-staking/src/Bags/index.tsx @@ -70,12 +70,10 @@ function Bags ({ ownStashes }: Props): React.ReactElement { value={filterIndex} /> - {( -
-

{t('The All bags list is composed of bags that each describe a range of active bonded funds of the nominators. In each bag is a list of nodes that correspond to a nominator and their staked funds.')}

-

{t('Within the context of a single bag, nodes are not sorted by their stake, but instead placed in insertion order. In other words, the most recently inserted node will be the last node in the bag, regardless of stake. Events like staking rewards or slashes do not automatically put you in a different bag. The bags-list pallet comes with an important permissionless extrinsic: rebag. This allows anyone to specify another account that is in the wrong bag, and place it in the correct one.')}

-
- )} +
+

{t('The All bags list is composed of bags that each describe a range of active bonded funds of the nominators. In each bag is a list of nodes that correspond to a nominator and their staked funds.')}

+

{t('Within the context of a single bag, nodes are not sorted by their stake, but instead placed in insertion order. In other words, the most recently inserted node will be the last node in the bag, regardless of stake. Events like staking rewards or slashes do not automatically put you in a different bag. The bags-list pallet comes with an important permissionless extrinsic: rebag. This allows anyone to specify another account that is in the wrong bag, and place it in the correct one.')}

+
('No available bags')} emptySpinner={t('Retrieving all available bags, this will take some time')} diff --git a/packages/page-staking/src/Bags/useBagsIds.ts b/packages/page-staking/src/Bags/useBagsIds.ts index e7651a96ff..9ef7b18f9f 100644 --- a/packages/page-staking/src/Bags/useBagsIds.ts +++ b/packages/page-staking/src/Bags/useBagsIds.ts @@ -6,7 +6,7 @@ import type { BN } from '@polkadot/util'; import { createNamedHook, useApi, useMapKeys } from '@polkadot/react-hooks'; -const keyOptions = { +const KEY_OPTS = { transform: (keys: StorageKey<[u64]>[]): BN[] => keys.map(({ args: [id] }) => id) }; @@ -14,7 +14,7 @@ const keyOptions = { function useBagsIdsImpl (): BN[] | undefined { const { api } = useApi(); - return useMapKeys(api.query.bagsList.listBags, keyOptions); + return useMapKeys(api.query.bagsList.listBags, KEY_OPTS); } export default createNamedHook('useBagsIds', useBagsIdsImpl); diff --git a/packages/page-staking/src/Bags/useBagsList.ts b/packages/page-staking/src/Bags/useBagsList.ts index 3176b421b0..e4572065ce 100644 --- a/packages/page-staking/src/Bags/useBagsList.ts +++ b/packages/page-staking/src/Bags/useBagsList.ts @@ -11,7 +11,7 @@ import { useEffect, useState } from 'react'; import { createNamedHook, useApi, useCall } from '@polkadot/react-hooks'; import { BN_ZERO } from '@polkadot/util'; -const multiOptions = { +const MULTI_OPTS = { transform: ([[ids], opts]: [[BN[]], Option[]]): BagInfo[] => { const sorted = ids .map((id, index): [BN, Option] => [id, opts[index]]) @@ -48,7 +48,7 @@ function merge (prev: BagInfo[] | undefined, curr: BagInfo[]): BagInfo[] { function useBagsListImpl (ids?: BN[]): BagInfo[] | undefined { const { api } = useApi(); const [result, setResult] = useState(); - const query = useCall(ids && ids.length !== 0 && api.query.bagsList.listBags.multi, [ids], multiOptions); + const query = useCall(ids && ids.length !== 0 && api.query.bagsList.listBags.multi, [ids], MULTI_OPTS); useEffect((): void => { query && setResult((prev) => merge(prev, query)); diff --git a/packages/page-staking/src/Bags/useBagsNodes.tsx b/packages/page-staking/src/Bags/useBagsNodes.tsx index acb0b20dbc..88c5b54aac 100644 --- a/packages/page-staking/src/Bags/useBagsNodes.tsx +++ b/packages/page-staking/src/Bags/useBagsNodes.tsx @@ -9,7 +9,7 @@ import { useEffect, useState } from 'react'; import { createNamedHook, useApi, useCall } from '@polkadot/react-hooks'; -const multiOptions = { +const MULTI_OPTS = { transform: (opts: Option[]): BagMap => opts .filter((o) => o.isSome) @@ -42,7 +42,7 @@ function merge (prev: BagMap | undefined, curr: BagMap): BagMap { function useBagsNodesImpl (stashIds: string[]): BagMap | undefined { const { api } = useApi(); const [result, setResult] = useState(); - const query = useCall(api.query.bagsList.listNodes.multi, [stashIds], multiOptions); + const query = useCall(api.query.bagsList.listNodes.multi, [stashIds], MULTI_OPTS); useEffect((): void => { query && setResult((prev) => merge(prev, query)); diff --git a/packages/page-staking/src/Bags/useBonded.ts b/packages/page-staking/src/Bags/useBonded.ts index f74b37104a..a737afa023 100644 --- a/packages/page-staking/src/Bags/useBonded.ts +++ b/packages/page-staking/src/Bags/useBonded.ts @@ -7,7 +7,7 @@ import type { ListNode } from './types'; import { createNamedHook, useApi, useCall } from '@polkadot/react-hooks'; -const deriveOpts = { +const DERIVE_OPTS = { transform: (all: DeriveStakingAccount[]): ListNode[] => { const infos = all.map(({ stakingLedger, stashId }, index): ListNode => ({ bonded: stakingLedger.active.unwrap(), @@ -34,7 +34,7 @@ const deriveOpts = { function useBondedImpl (ids?: false | AccountId32[]): ListNode[] | undefined { const { api } = useApi(); - return useCall(ids && ids.length !== 0 && api.derive.staking.accounts, [ids], deriveOpts); + return useCall(ids && ids.length !== 0 && api.derive.staking.accounts, [ids], DERIVE_OPTS); } export default createNamedHook('useBonded', useBondedImpl);