Staking pools (#7089)
* Staking pools * Fly-by comment fix * Some infrastructure * multi * Create value * Retrieve singular params instance * defaultValue for multi query * Retrieve list of pool ids * Drive-by param formatting * Pool list retrieval * Retrieve pool info * Adjust trigger placement * Dump info * Join modal * Fix build * Allow creation & joining * caveman typing * Adjust disabled state * Adjust * cowboy types * Some basic stats * Created/Destoyed events * Adjust for latest * Set metadata on creation * Basic pools
This commit is contained in:
parent
105c484e8a
commit
2a7af81d16
119
packages/page-staking/src/Pools/Create.tsx
Normal file
119
packages/page-staking/src/Pools/Create.tsx
Normal file
@ -0,0 +1,119 @@
|
||||
// Copyright 2017-2022 @polkadot/app-staking authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import type { BN } from '@polkadot/util';
|
||||
import type { Params } from './types';
|
||||
|
||||
import React, { useMemo, useState } from 'react';
|
||||
|
||||
import { Button, Input, InputAddress, InputBalance, InputNumber, Modal, TxButton } from '@polkadot/react-components';
|
||||
import { useApi, useCall, useToggle } from '@polkadot/react-hooks';
|
||||
import { BN_ONE, bnMax } from '@polkadot/util';
|
||||
|
||||
import { useTranslation } from '../translate';
|
||||
|
||||
interface Props {
|
||||
className?: string;
|
||||
isDisabled?: boolean;
|
||||
params: Params;
|
||||
}
|
||||
|
||||
function Create ({ className, isDisabled, params: { minCreateBond, minNominatorBond } }: Props): React.ReactElement<Props> {
|
||||
const { t } = useTranslation();
|
||||
const { api } = useApi();
|
||||
const lastPoolId = useCall<BN>(api.query.nominationPools.lastPoolId);
|
||||
const [isOpen, toggleOpen] = useToggle();
|
||||
const [accountId, setAccount] = useState<string | null>(null);
|
||||
const [amount, setAmount] = useState<BN | undefined>();
|
||||
const [metadata, setMetadata] = useState('');
|
||||
|
||||
const minValue = useMemo(
|
||||
() => minCreateBond && minNominatorBond && bnMax(minCreateBond, minNominatorBond),
|
||||
[minCreateBond, minNominatorBond]
|
||||
);
|
||||
|
||||
const nextPoolId = useMemo(
|
||||
() => lastPoolId && lastPoolId.add(BN_ONE),
|
||||
[lastPoolId]
|
||||
);
|
||||
|
||||
const isAmountError = useMemo(
|
||||
() => !amount || !minValue || amount.lt(minValue),
|
||||
[amount, minValue]
|
||||
);
|
||||
|
||||
const extrinsic = useMemo(
|
||||
() => accountId && !isAmountError
|
||||
? api.tx.utility.batch([
|
||||
api.tx.nominationPools.create(amount, accountId, accountId, accountId),
|
||||
api.tx.nominationPools.setMetadata(nextPoolId, metadata)
|
||||
])
|
||||
: null,
|
||||
[api, accountId, amount, isAmountError, metadata, nextPoolId]
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Button
|
||||
icon='plus'
|
||||
isDisabled={isDisabled || !minValue}
|
||||
label={t<string>('Add pool')}
|
||||
onClick={toggleOpen}
|
||||
/>
|
||||
{isOpen && (
|
||||
<Modal
|
||||
className={className}
|
||||
header={t<string>('Create nomination pool')}
|
||||
onClose={toggleOpen}
|
||||
size='large'
|
||||
>
|
||||
<Modal.Content>
|
||||
<Modal.Columns hint={t<string>('The origin account will also be set as the pool admin, nominator and state toggler.')}>
|
||||
<InputAddress
|
||||
label={t<string>('create pool from')}
|
||||
onChange={setAccount}
|
||||
type='account'
|
||||
value={accountId}
|
||||
/>
|
||||
</Modal.Columns>
|
||||
<Modal.Columns hint={t<string>('The initial value to assign to the pool. It is set the the maximum of the minimum bond and the minium nomination value.')}>
|
||||
<InputBalance
|
||||
autoFocus
|
||||
defaultValue={minValue}
|
||||
isError={isAmountError}
|
||||
label={t<string>('initial value')}
|
||||
onChange={setAmount}
|
||||
/>
|
||||
</Modal.Columns>
|
||||
<Modal.Columns hint={t<string>('The metadata to set for this pool')}>
|
||||
<Input
|
||||
label={t<string>('metadata')}
|
||||
maxLength={32}
|
||||
onChange={setMetadata}
|
||||
/>
|
||||
</Modal.Columns>
|
||||
<Modal.Columns hint={t<string>('The id that will be assigned to this nomination pool.')}>
|
||||
<InputNumber
|
||||
defaultValue={nextPoolId}
|
||||
isDisabled
|
||||
label={t<string>('pool id')}
|
||||
/>
|
||||
</Modal.Columns>
|
||||
</Modal.Content>
|
||||
<Modal.Actions>
|
||||
<TxButton
|
||||
accountId={accountId}
|
||||
extrinsic={extrinsic}
|
||||
icon='plus'
|
||||
isDisabled={!accountId || isAmountError}
|
||||
label={t<string>('Create')}
|
||||
onStart={toggleOpen}
|
||||
/>
|
||||
</Modal.Actions>
|
||||
</Modal>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default React.memo(Create);
|
||||
94
packages/page-staking/src/Pools/Join.tsx
Normal file
94
packages/page-staking/src/Pools/Join.tsx
Normal file
@ -0,0 +1,94 @@
|
||||
// Copyright 2017-2022 @polkadot/app-staking authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import type { BN } from '@polkadot/util';
|
||||
import type { Params } from './types';
|
||||
|
||||
import React, { useMemo, useState } from 'react';
|
||||
|
||||
import { Button, InputAddress, InputBalance, Modal, TxButton } from '@polkadot/react-components';
|
||||
import { useApi, useToggle } from '@polkadot/react-hooks';
|
||||
import { bnMax } from '@polkadot/util';
|
||||
|
||||
import { useTranslation } from '../translate';
|
||||
|
||||
interface Props {
|
||||
className?: string;
|
||||
id: BN;
|
||||
isDisabled?: boolean;
|
||||
params: Params;
|
||||
}
|
||||
|
||||
function Join ({ className, id, isDisabled, params: { minJoinBond, minNominatorBond } }: Props): React.ReactElement<Props> | null {
|
||||
const { t } = useTranslation();
|
||||
const { api } = useApi();
|
||||
const [isOpen, toggleOpen] = useToggle();
|
||||
const [accountId, setAccount] = useState<string | null>(null);
|
||||
const [amount, setAmount] = useState<BN | undefined>();
|
||||
|
||||
const minAmount = useMemo(
|
||||
() => minJoinBond && minNominatorBond && bnMax(minJoinBond, minNominatorBond),
|
||||
[minJoinBond, minNominatorBond]
|
||||
);
|
||||
|
||||
const isAmountError = useMemo(
|
||||
() => !amount || !minAmount || amount.lt(minAmount),
|
||||
[amount, minAmount]
|
||||
);
|
||||
|
||||
if (isDisabled) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Button
|
||||
icon='plus'
|
||||
isDisabled={!minAmount}
|
||||
label={t<string>('Join')}
|
||||
onClick={toggleOpen}
|
||||
/>
|
||||
{isOpen && (
|
||||
<Modal
|
||||
className={className}
|
||||
header={t<string>('Join nomination pool')}
|
||||
onClose={toggleOpen}
|
||||
size='large'
|
||||
>
|
||||
<Modal.Content>
|
||||
<Modal.Columns hint={t<string>('The account that is to join the pool.')}>
|
||||
<InputAddress
|
||||
label={t<string>('join pool from')}
|
||||
onChange={setAccount}
|
||||
type='account'
|
||||
value={accountId}
|
||||
/>
|
||||
</Modal.Columns>
|
||||
<Modal.Columns hint={t<string>('The initial value to assign to the pool. It is set the the maximum of the minimum bond and the minium nomination value.')}>
|
||||
<InputBalance
|
||||
autoFocus
|
||||
defaultValue={minAmount}
|
||||
isError={isAmountError}
|
||||
label={t<string>('initial value')}
|
||||
onChange={setAmount}
|
||||
/>
|
||||
</Modal.Columns>
|
||||
</Modal.Content>
|
||||
<Modal.Actions>
|
||||
<TxButton
|
||||
accountId={accountId}
|
||||
icon='plus'
|
||||
isDisabled={!accountId || isAmountError}
|
||||
label={t<string>('Join')}
|
||||
onStart={toggleOpen}
|
||||
params={[amount, id]}
|
||||
tx={api.tx.nominationPools.join}
|
||||
/>
|
||||
</Modal.Actions>
|
||||
</Modal>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default React.memo(Join);
|
||||
55
packages/page-staking/src/Pools/Pool.tsx
Normal file
55
packages/page-staking/src/Pools/Pool.tsx
Normal file
@ -0,0 +1,55 @@
|
||||
// Copyright 2017-2022 @polkadot/app-staking authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import type { BN } from '@polkadot/util';
|
||||
import type { Params } from './types';
|
||||
|
||||
import React, { useMemo } from 'react';
|
||||
|
||||
import { FormatBalance } from '@polkadot/react-query';
|
||||
import { formatNumber, stringify } from '@polkadot/util';
|
||||
|
||||
import Join from './Join';
|
||||
import usePoolInfo from './usePoolInfo';
|
||||
|
||||
interface Props {
|
||||
className?: string;
|
||||
id: BN;
|
||||
params: Params;
|
||||
}
|
||||
|
||||
function Pool ({ className, id, params }: Props): React.ReactElement<Props> | null {
|
||||
const info = usePoolInfo(id);
|
||||
const metadata = useMemo(
|
||||
() => info && info.metadata && info.metadata.length
|
||||
? info.metadata.isUtf8
|
||||
? info.metadata.toUtf8()
|
||||
: info.metadata.toString()
|
||||
: null,
|
||||
[info]
|
||||
);
|
||||
|
||||
if (!info) {
|
||||
return null;
|
||||
}
|
||||
|
||||
console.log(stringify(info, 2));
|
||||
|
||||
return (
|
||||
<tr className={className}>
|
||||
<td className='number'><h1>{formatNumber(id)}</h1></td>
|
||||
<td className='start'>{metadata}</td>
|
||||
<td className='number'><FormatBalance value={info.bonded?.points} /></td>
|
||||
<td className='number'>{formatNumber(info.bonded?.delegatorCounter)}</td>
|
||||
<td className='button'>
|
||||
<Join
|
||||
id={id}
|
||||
isDisabled={!info.bonded?.state.isOpen}
|
||||
params={params}
|
||||
/>
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
}
|
||||
|
||||
export default React.memo(Pool);
|
||||
47
packages/page-staking/src/Pools/Pools.tsx
Normal file
47
packages/page-staking/src/Pools/Pools.tsx
Normal file
@ -0,0 +1,47 @@
|
||||
// Copyright 2017-2022 @polkadot/app-staking authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import type { BN } from '@polkadot/util';
|
||||
import type { Params } from './types';
|
||||
|
||||
import React, { useMemo } from 'react';
|
||||
|
||||
import { Table } from '@polkadot/react-components';
|
||||
|
||||
import { useTranslation } from '../translate';
|
||||
import Pool from './Pool';
|
||||
|
||||
interface Props {
|
||||
className?: string;
|
||||
ids?: BN[];
|
||||
params: Params;
|
||||
}
|
||||
|
||||
function Pools ({ className, ids, params }: Props): React.ReactElement<Props> {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const header = useMemo(() => [
|
||||
[t('pools'), 'start', 2],
|
||||
[t('points')],
|
||||
[t('delegators')]
|
||||
], [t]);
|
||||
|
||||
return (
|
||||
<Table
|
||||
className={className}
|
||||
empty={ids && t<string>('No available nomination pools')}
|
||||
emptySpinner={t<string>('Retrieving nomination pools')}
|
||||
header={header}
|
||||
>
|
||||
{ids?.map((id) => (
|
||||
<Pool
|
||||
id={id}
|
||||
key={id.toString()}
|
||||
params={params}
|
||||
/>
|
||||
))}
|
||||
</Table>
|
||||
);
|
||||
}
|
||||
|
||||
export default React.memo(Pools);
|
||||
45
packages/page-staking/src/Pools/Summary.tsx
Normal file
45
packages/page-staking/src/Pools/Summary.tsx
Normal file
@ -0,0 +1,45 @@
|
||||
// Copyright 2017-2022 @polkadot/app-staking authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import type { Params } from './types';
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import { CardSummary, SummaryBox } from '@polkadot/react-components';
|
||||
import { formatNumber } from '@polkadot/util';
|
||||
|
||||
import { useTranslation } from '../translate';
|
||||
|
||||
interface Props {
|
||||
className?: string;
|
||||
params: Params;
|
||||
poolCount?: number;
|
||||
}
|
||||
|
||||
function Summary ({ className, params: { maxDelegators, maxDelegatorsPool, maxPools }, poolCount }: Props): React.ReactElement<Props> | null {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<SummaryBox className={className}>
|
||||
{maxDelegators && (
|
||||
<CardSummary label={t<string>('pools')}>
|
||||
{poolCount && <>{formatNumber(poolCount)} / </>}{formatNumber(maxPools)}
|
||||
</CardSummary>
|
||||
)}
|
||||
<section>
|
||||
{maxDelegatorsPool && (
|
||||
<CardSummary label={t<string>('max. delegators')}>
|
||||
{formatNumber(maxDelegators)}
|
||||
</CardSummary>
|
||||
)}
|
||||
{maxPools && (
|
||||
<CardSummary label={t<string>('max. delegators / pool')}>
|
||||
{formatNumber(maxDelegatorsPool)}
|
||||
</CardSummary>
|
||||
)}
|
||||
</section>
|
||||
</SummaryBox>
|
||||
);
|
||||
}
|
||||
|
||||
export default React.memo(Summary);
|
||||
47
packages/page-staking/src/Pools/index.tsx
Normal file
47
packages/page-staking/src/Pools/index.tsx
Normal file
@ -0,0 +1,47 @@
|
||||
// Copyright 2017-2022 @polkadot/app-staking authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import React, { useMemo } from 'react';
|
||||
|
||||
import { Button } from '@polkadot/react-components';
|
||||
|
||||
import Create from './Create';
|
||||
import Pools from './Pools';
|
||||
import Summary from './Summary';
|
||||
import useParams from './useParams';
|
||||
import usePoolIds from './usePoolIds';
|
||||
|
||||
interface Props {
|
||||
className?: string;
|
||||
}
|
||||
|
||||
function NominationPools ({ className }: Props): React.ReactElement<Props> {
|
||||
const ids = usePoolIds();
|
||||
const params = useParams();
|
||||
|
||||
const noCreate = useMemo(
|
||||
() => !ids || (!!params.maxPools && (ids.length > params.maxPools)),
|
||||
[ids, params]
|
||||
);
|
||||
|
||||
return (
|
||||
<div className={className}>
|
||||
<Summary
|
||||
params={params}
|
||||
poolCount={ids?.length}
|
||||
/>
|
||||
<Button.Group>
|
||||
<Create
|
||||
isDisabled={noCreate}
|
||||
params={params}
|
||||
/>
|
||||
</Button.Group>
|
||||
<Pools
|
||||
ids={ids}
|
||||
params={params}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default React.memo(NominationPools);
|
||||
38
packages/page-staking/src/Pools/types.ts
Normal file
38
packages/page-staking/src/Pools/types.ts
Normal file
@ -0,0 +1,38 @@
|
||||
// Copyright 2017-2022 @polkadot/app-staking authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import type { Bytes } from '@polkadot/types';
|
||||
import type { AccountId } from '@polkadot/types/interfaces';
|
||||
import type { BN } from '@polkadot/util';
|
||||
|
||||
export interface Params {
|
||||
maxDelegators: number;
|
||||
maxDelegatorsPool: number;
|
||||
maxPools: number;
|
||||
minCreateBond?: BN;
|
||||
minJoinBond?: BN;
|
||||
minNominatorBond?: BN;
|
||||
}
|
||||
|
||||
// FIXME Once available, use types from lookup
|
||||
export interface PoolInfo {
|
||||
bonded: {
|
||||
points: BN;
|
||||
state: {
|
||||
isOpen: boolean;
|
||||
}
|
||||
delegatorCounter: BN;
|
||||
roles: {
|
||||
depositor: AccountId;
|
||||
root: AccountId;
|
||||
nominator: AccountId;
|
||||
stateToggler: AccountId;
|
||||
};
|
||||
} | null;
|
||||
reward: {
|
||||
balance: BN;
|
||||
totalEarnings: BN;
|
||||
points: BN;
|
||||
} | null;
|
||||
metadata: Bytes;
|
||||
}
|
||||
40
packages/page-staking/src/Pools/useParams.ts
Normal file
40
packages/page-staking/src/Pools/useParams.ts
Normal file
@ -0,0 +1,40 @@
|
||||
// Copyright 2017-2022 @polkadot/app-staking authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import type { Option, u32 } from '@polkadot/types';
|
||||
import type { BN } from '@polkadot/util';
|
||||
import type { Params } from './types';
|
||||
|
||||
import { createNamedHook, useApi, useCallMulti } from '@polkadot/react-hooks';
|
||||
import { BN_ZERO } from '@polkadot/util';
|
||||
|
||||
const queryOptions = {
|
||||
defaultValue: {
|
||||
maxDelegators: 0,
|
||||
maxDelegatorsPool: 0,
|
||||
maxPools: 0
|
||||
},
|
||||
transform: ([maxDelegators, maxDelegatorsPerPool, maxPools, minCreateBond, minJoinBond, minNominatorBond]: [Option<u32>, Option<u32>, Option<u32>, BN, BN, BN]): Params => ({
|
||||
maxDelegators: maxDelegators.unwrapOr(BN_ZERO).toNumber(),
|
||||
maxDelegatorsPool: maxDelegatorsPerPool.unwrapOr(BN_ZERO).toNumber(),
|
||||
maxPools: maxPools.unwrapOr(BN_ZERO).toNumber(),
|
||||
minCreateBond,
|
||||
minJoinBond,
|
||||
minNominatorBond
|
||||
})
|
||||
};
|
||||
|
||||
function useParamsImpl (): Params {
|
||||
const { api } = useApi();
|
||||
|
||||
return useCallMulti<Params>([
|
||||
api.query.nominationPools.maxDelegators,
|
||||
api.query.nominationPools.maxDelegatorsPerPool,
|
||||
api.query.nominationPools.maxPools,
|
||||
api.query.nominationPools.minCreateBond,
|
||||
api.query.nominationPools.minJoinBond,
|
||||
api.query.staking.minNominatorBond
|
||||
], queryOptions);
|
||||
}
|
||||
|
||||
export default createNamedHook('useParams', useParamsImpl);
|
||||
42
packages/page-staking/src/Pools/usePoolIds.ts
Normal file
42
packages/page-staking/src/Pools/usePoolIds.ts
Normal file
@ -0,0 +1,42 @@
|
||||
// Copyright 2017-2022 @polkadot/app-staking authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import type { Changes } from '@polkadot/react-hooks/useEventChanges';
|
||||
import type { StorageKey, u32 } from '@polkadot/types';
|
||||
import type { EventRecord } from '@polkadot/types/interfaces';
|
||||
|
||||
import { createNamedHook, useApi, useEventChanges, useMapKeys } from '@polkadot/react-hooks';
|
||||
|
||||
const keyOptions = {
|
||||
transform: (keys: StorageKey<[u32]>[]): u32[] =>
|
||||
keys
|
||||
.map(({ args: [poolId] }) => poolId)
|
||||
.sort((a, b) => a.cmp(b))
|
||||
};
|
||||
|
||||
function filter (records: EventRecord[]): Changes<u32> {
|
||||
const added: u32[] = [];
|
||||
const removed: u32[] = [];
|
||||
|
||||
records.forEach(({ event: { data, method } }): void => {
|
||||
if (method === 'Created') {
|
||||
added.push(data[1] as u32);
|
||||
} else {
|
||||
removed.push(data[0] as u32);
|
||||
}
|
||||
});
|
||||
|
||||
return { added, removed };
|
||||
}
|
||||
|
||||
function usePoolIdsImpl (): u32[] | undefined {
|
||||
const { api } = useApi();
|
||||
const startValue = useMapKeys(api.query.nominationPools.bondedPools, keyOptions);
|
||||
|
||||
return useEventChanges([
|
||||
api.events.nominationPools.Created,
|
||||
api.events.nominationPools.Destroyed
|
||||
], filter, startValue);
|
||||
}
|
||||
|
||||
export default createNamedHook('usePoolIds', usePoolIdsImpl);
|
||||
29
packages/page-staking/src/Pools/usePoolInfo.ts
Normal file
29
packages/page-staking/src/Pools/usePoolInfo.ts
Normal file
@ -0,0 +1,29 @@
|
||||
// Copyright 2017-2022 @polkadot/app-staking authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import type { Option } from '@polkadot/types';
|
||||
import type { Codec } from '@polkadot/types/types';
|
||||
import type { BN } from '@polkadot/util';
|
||||
import type { PoolInfo } from './types';
|
||||
|
||||
import { createNamedHook, useApi, useCallMulti } from '@polkadot/react-hooks';
|
||||
|
||||
const options = {
|
||||
transform: ([bonded, metadata, reward]: [Option<PoolInfo['bonded'] & Codec>, PoolInfo['metadata'], Option<PoolInfo['reward'] & Codec>]): PoolInfo => ({
|
||||
bonded: bonded.unwrapOr(null),
|
||||
metadata,
|
||||
reward: reward.unwrapOr(null)
|
||||
})
|
||||
};
|
||||
|
||||
function usePoolInfoImpl (id: BN): PoolInfo {
|
||||
const { api } = useApi();
|
||||
|
||||
return useCallMulti([
|
||||
[api.query.nominationPools.bondedPools, id],
|
||||
[api.query.nominationPools.metadata, id],
|
||||
[api.query.nominationPools.rewardPools, id]
|
||||
], options);
|
||||
}
|
||||
|
||||
export default createNamedHook('usePoolInfo', usePoolInfoImpl);
|
||||
@ -17,7 +17,7 @@ interface Props {
|
||||
slash: SlashEra;
|
||||
}
|
||||
|
||||
function Header ({ slash: { era, nominators, reporters, total, validators } }: Props): React.ReactElement<Props> | null {
|
||||
function Summary ({ slash: { era, nominators, reporters, total, validators } }: Props): React.ReactElement<Props> | null {
|
||||
const { t } = useTranslation();
|
||||
const { api } = useApi();
|
||||
const sessionInfo = useCall<DeriveSessionProgress>(api.derive.session?.progress);
|
||||
@ -62,4 +62,4 @@ function Header ({ slash: { era, nominators, reporters, total, validators } }: P
|
||||
);
|
||||
}
|
||||
|
||||
export default React.memo(Header);
|
||||
export default React.memo(Summary);
|
||||
|
||||
@ -23,6 +23,7 @@ import Bags from './Bags';
|
||||
import { STORE_FAVS_BASE } from './constants';
|
||||
import Overview from './Overview';
|
||||
import Payouts from './Payouts';
|
||||
import Pools from './Pools';
|
||||
import Query from './Query';
|
||||
import Slashes from './Slashes';
|
||||
import Targets from './Targets';
|
||||
@ -67,6 +68,11 @@ function StakingApp ({ basePath, className = '' }: Props): React.ReactElement<Pr
|
||||
[api, hasAccounts]
|
||||
);
|
||||
|
||||
const hasStashes = useMemo(
|
||||
() => hasAccounts && !!ownStashes && (ownStashes.length !== 0),
|
||||
[hasAccounts, ownStashes]
|
||||
);
|
||||
|
||||
const ownValidators = useMemo(
|
||||
() => (ownStashes || []).filter(({ isStashValidating }) => isStashValidating),
|
||||
[ownStashes]
|
||||
@ -87,10 +93,14 @@ function StakingApp ({ basePath, className = '' }: Props): React.ReactElement<Pr
|
||||
name: 'actions',
|
||||
text: t<string>('Account actions')
|
||||
},
|
||||
isFunction(api.query.staking.activeEra) && hasAccounts && ownStashes && (ownStashes.length !== 0) && {
|
||||
hasStashes && isFunction(api.query.staking.activeEra) && {
|
||||
name: 'payout',
|
||||
text: t<string>('Payouts')
|
||||
},
|
||||
// hasStashes && isFunction(api.query.nominationPools?.minCreateBond) && {
|
||||
// name: 'pools',
|
||||
// text: t<string>('Pools')
|
||||
// },
|
||||
{
|
||||
alias: 'returns',
|
||||
name: 'targets',
|
||||
@ -110,7 +120,7 @@ function StakingApp ({ basePath, className = '' }: Props): React.ReactElement<Pr
|
||||
name: 'query',
|
||||
text: t<string>('Validator stats')
|
||||
}
|
||||
].filter((q): q is { name: string; text: string } => !!q), [api, hasAccounts, ownStashes, slashes, t]);
|
||||
].filter((q): q is { name: string; text: string } => !!q), [api, hasStashes, slashes, t]);
|
||||
|
||||
return (
|
||||
<main className={`staking--App ${className}`}>
|
||||
@ -139,6 +149,9 @@ function StakingApp ({ basePath, className = '' }: Props): React.ReactElement<Pr
|
||||
ownValidators={ownValidators}
|
||||
/>
|
||||
</Route>
|
||||
<Route path={`${basePath}/pools`}>
|
||||
<Pools />
|
||||
</Route>
|
||||
<Route path={[`${basePath}/query/:value`, `${basePath}/query`]}>
|
||||
<Query />
|
||||
</Route>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user