Add global reward alert for payouts (#2370)
This commit is contained in:
parent
5c42dd69d6
commit
ac8c7cc410
@ -8,10 +8,10 @@
|
||||
"packages/*"
|
||||
],
|
||||
"resolutions": {
|
||||
"@polkadot/api": "^1.6.0-beta.26",
|
||||
"@polkadot/api-contract": "^1.6.0-beta.26",
|
||||
"@polkadot/api": "^1.6.0-beta.28",
|
||||
"@polkadot/api-contract": "^1.6.0-beta.28",
|
||||
"@polkadot/keyring": "^2.6.2",
|
||||
"@polkadot/types": "^1.6.0-beta.26",
|
||||
"@polkadot/types": "^1.6.0-beta.28",
|
||||
"@polkadot/util": "^2.6.2",
|
||||
"@polkadot/util-crypto": "^2.6.2",
|
||||
"babel-core": "^7.0.0-bridge.0",
|
||||
|
||||
@ -4,11 +4,12 @@
|
||||
|
||||
import { Routes } from './types';
|
||||
|
||||
import Staking from '@polkadot/app-staking';
|
||||
import Staking, { useCounter } from '@polkadot/app-staking';
|
||||
|
||||
export default ([
|
||||
{
|
||||
Component: Staking,
|
||||
useCounter,
|
||||
display: {
|
||||
needsApi: [
|
||||
[
|
||||
|
||||
@ -12,6 +12,6 @@
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.8.7",
|
||||
"@polkadot/api-contract": "^1.6.0-beta.26"
|
||||
"@polkadot/api-contract": "^1.6.0-beta.28"
|
||||
}
|
||||
}
|
||||
|
||||
@ -11,6 +11,7 @@ import BN from 'bn.js';
|
||||
import React, { useContext, useEffect, useState } from 'react';
|
||||
import { Trans } from 'react-i18next';
|
||||
import styled from 'styled-components';
|
||||
import { ApiPromise } from '@polkadot/api';
|
||||
import { AddressInfo, AddressMini, AddressSmall, Badge, Button, Menu, Popup, Spinner, StatusContext, TxButton } from '@polkadot/react-components';
|
||||
import { useAccounts, useApi, useCall, useToggle } from '@polkadot/react-hooks';
|
||||
import { FormatBalance } from '@polkadot/react-query';
|
||||
@ -37,9 +38,9 @@ interface Props {
|
||||
isOwnStash: boolean;
|
||||
isVisible: boolean;
|
||||
next?: string[];
|
||||
onUpdatePayout: (stashId: string, count: number) => void;
|
||||
onUpdateType: (stashId: string, type: 'validator' | 'nominator' | 'started' | 'other') => void;
|
||||
recentlyOnline?: DerivedHeartbeats;
|
||||
rewards?: DeriveStakerReward[];
|
||||
stakingOverview?: DerivedStakingOverview;
|
||||
stashId: string;
|
||||
}
|
||||
@ -95,16 +96,28 @@ function getStakeState (allAccounts: string[], allStashes: string[] | undefined,
|
||||
};
|
||||
}
|
||||
|
||||
function Account ({ allStashes, className, isOwnStash, next, onUpdatePayout, onUpdateType, stakingOverview, stashId }: Props): React.ReactElement<Props> {
|
||||
function createPayout (api: ApiPromise, payoutRewards: DeriveStakerReward[]): SubmittableExtrinsic<'promise'> {
|
||||
return payoutRewards.length === 1
|
||||
? payoutRewards[0].isValidator
|
||||
? api.tx.staking.payoutValidator(payoutRewards[0].era)
|
||||
: api.tx.staking.payoutNominator(payoutRewards[0].era, payoutRewards[0].nominating)
|
||||
: api.tx.utility.batch(
|
||||
payoutRewards.map(({ era, isValidator, nominating }): SubmittableExtrinsic<'promise'> =>
|
||||
isValidator
|
||||
? api.tx.staking.payoutValidator(era)
|
||||
: api.tx.staking.payoutNominator(era, nominating)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
function Account ({ allStashes, className, isOwnStash, next, onUpdateType, rewards, stakingOverview, stashId }: Props): React.ReactElement<Props> {
|
||||
const { t } = useTranslation();
|
||||
const { queueExtrinsic } = useContext(StatusContext);
|
||||
const { api } = useApi();
|
||||
const { allAccounts } = useAccounts();
|
||||
const [firstClaim, setFirstClaim] = useState<BN | undefined>();
|
||||
const validateInfo = useCall<ValidatorInfo>(api.query.staking.validators, [stashId]);
|
||||
const balancesAll = useCall<DerivedBalancesAll>(api.derive.balances.all as any, [stashId]);
|
||||
const stakingAccount = useCall<DerivedStakingAccount>(api.derive.staking.account as any, [stashId]);
|
||||
const stakerRewards = useCall<DeriveStakerReward[]>(firstClaim && api.derive.staking.stakerRewards as any, [stashId, firstClaim]);
|
||||
const [[payoutRewards, payoutEras, payoutTotal], setStakingRewards] = useState<[DeriveStakerReward[], EraIndex[], BN]>([[], [], new BN(0)]);
|
||||
const [{ controllerId, destination, hexSessionIdQueue, hexSessionIdNext, isLoading, isOwnController, isStashNominating, isStashValidating, nominees, sessionIds, validatorPrefs }, setStakeState] = useState<StakeState>({ controllerId: null, destination: 0, hexSessionIdNext: null, hexSessionIdQueue: null, isLoading: true, isOwnController: false, isStashNominating: false, isStashValidating: false, sessionIds: [] });
|
||||
const [activeNoms, setActiveNoms] = useState<string[]>([]);
|
||||
@ -120,12 +133,6 @@ function Account ({ allStashes, className, isOwnStash, next, onUpdatePayout, onU
|
||||
const [isUnbondOpen, toggleUnbond] = useToggle();
|
||||
const [isValidateOpen, toggleValidate] = useToggle();
|
||||
|
||||
useEffect((): void => {
|
||||
if (stakingAccount?.stakingLedger?.lastReward) {
|
||||
setFirstClaim(stakingAccount.stakingLedger.lastReward.unwrapOr(new BN(-1)).addn(1));
|
||||
}
|
||||
}, [stakingAccount]);
|
||||
|
||||
useEffect((): void => {
|
||||
if (stakingAccount && validateInfo) {
|
||||
const state = getStakeState(allAccounts, allStashes, stakingAccount, stashId, validateInfo);
|
||||
@ -143,46 +150,30 @@ function Account ({ allStashes, className, isOwnStash, next, onUpdatePayout, onU
|
||||
}, [allStashes, stakingAccount, stashId, validateInfo]);
|
||||
|
||||
useEffect((): void => {
|
||||
if (nominees) {
|
||||
setActiveNoms(nominees.filter((id): boolean => !inactiveNoms.includes(id)));
|
||||
}
|
||||
nominees && setActiveNoms(
|
||||
nominees.filter((id): boolean => !inactiveNoms.includes(id))
|
||||
);
|
||||
}, [inactiveNoms, nominees]);
|
||||
|
||||
useEffect((): void => {
|
||||
if (stakerRewards && firstClaim) {
|
||||
const payoutRewards = stakerRewards.filter(({ isEmpty }): boolean => !isEmpty);
|
||||
rewards && setStakingRewards([
|
||||
rewards,
|
||||
rewards.map(({ era }): EraIndex => era),
|
||||
rewards.reduce((result, { total }) => result.iadd(total), new BN(0))
|
||||
]);
|
||||
}, [rewards]);
|
||||
|
||||
setStakingRewards([
|
||||
payoutRewards,
|
||||
payoutRewards.map(({ era }): EraIndex => era),
|
||||
payoutRewards.reduce((result, { total }) => result.iadd(total), new BN(0))
|
||||
]);
|
||||
onUpdatePayout(stashId, payoutRewards.length);
|
||||
}
|
||||
}, [firstClaim, stakingAccount, stakerRewards, stashId]);
|
||||
|
||||
const _doPayout = (): void => {
|
||||
const _doPayout = (): void =>
|
||||
queueExtrinsic({
|
||||
accountId: controllerId,
|
||||
extrinsic: payoutRewards.length === 1
|
||||
? payoutRewards[0].isValidator
|
||||
? api.tx.staking.payoutValidator(payoutRewards[0].era)
|
||||
: api.tx.staking.payoutNominator(payoutRewards[0].era, payoutRewards[0].nominating)
|
||||
: api.tx.utility.batch(
|
||||
payoutRewards.map(({ era, isValidator, nominating }): SubmittableExtrinsic<'promise'> =>
|
||||
isValidator
|
||||
? api.tx.staking.payoutValidator(era)
|
||||
: api.tx.staking.payoutNominator(era, nominating)
|
||||
)
|
||||
)
|
||||
extrinsic: createPayout(api, payoutRewards)
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<tr className={className}>
|
||||
{api.query.staking.activeEra && (
|
||||
<td>
|
||||
{!stakerRewards
|
||||
{!rewards
|
||||
? <Spinner variant='mini' />
|
||||
: !!payoutEras.length && (
|
||||
<Badge
|
||||
|
||||
@ -2,12 +2,12 @@
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
import { DerivedHeartbeats, DerivedStakingOverview } from '@polkadot/api-derive/types';
|
||||
import { AccountId, ActiveEraInfo, EraIndex, StakingLedger } from '@polkadot/types/interfaces';
|
||||
import { DerivedHeartbeats, DerivedStakingOverview, DeriveStakerReward } from '@polkadot/api-derive/types';
|
||||
import { ActiveEraInfo, EraIndex } from '@polkadot/types/interfaces';
|
||||
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { Button, Table } from '@polkadot/react-components';
|
||||
import { useCall, useApi, useAccounts } from '@polkadot/react-hooks';
|
||||
import { useCall, useApi, useOwnStashes } from '@polkadot/react-hooks';
|
||||
import { Option } from '@polkadot/types';
|
||||
|
||||
import Account from './Account';
|
||||
@ -15,66 +15,38 @@ import StartStaking from './NewStake';
|
||||
import { useTranslation } from '../translate';
|
||||
|
||||
interface Props {
|
||||
allRewards?: Record<string, DeriveStakerReward[]>;
|
||||
allStashes?: string[];
|
||||
className?: string;
|
||||
isVisible: boolean;
|
||||
recentlyOnline?: DerivedHeartbeats;
|
||||
next?: string[];
|
||||
stakingOverview?: DerivedStakingOverview;
|
||||
onUpdatePending: (count: number) => void;
|
||||
}
|
||||
|
||||
function getStashes (allAccounts: string[], stashTypes: Record<string, number>, queryBonded?: Option<AccountId>[], queryLedger?: Option<StakingLedger>[]): [string, boolean][] | null {
|
||||
const result: [string, boolean][] = [];
|
||||
|
||||
if (!queryBonded || !queryLedger) {
|
||||
return null;
|
||||
}
|
||||
|
||||
queryBonded.forEach((value, index): void => {
|
||||
value.isSome && result.push([allAccounts[index], true]);
|
||||
});
|
||||
|
||||
queryLedger.forEach((ledger): void => {
|
||||
if (ledger.isSome) {
|
||||
const stashId = ledger.unwrap().stash.toString();
|
||||
|
||||
!result.some(([accountId]): boolean => accountId === stashId) && result.push([stashId, false]);
|
||||
}
|
||||
});
|
||||
|
||||
return result.sort((a, b): number =>
|
||||
(stashTypes[a[0]] || 99) - (stashTypes[b[0]] || 99)
|
||||
);
|
||||
}
|
||||
|
||||
export default function Actions ({ allStashes, className, isVisible, next, onUpdatePending, recentlyOnline, stakingOverview }: Props): React.ReactElement<Props> {
|
||||
export default function Actions ({ allRewards, allStashes, className, isVisible, next, recentlyOnline, stakingOverview }: Props): React.ReactElement<Props> {
|
||||
const { t } = useTranslation();
|
||||
const { api } = useApi();
|
||||
const { allAccounts } = useAccounts();
|
||||
const activeEra = useCall<EraIndex | undefined>(api.query.staking.activeEra, [], {
|
||||
const activeEra = useCall<EraIndex | undefined>(api.query.staking?.activeEra, [], {
|
||||
transform: (activeEra: Option<ActiveEraInfo>): EraIndex | undefined =>
|
||||
activeEra.isSome
|
||||
? activeEra.unwrap().index
|
||||
: undefined
|
||||
});
|
||||
const queryBonded = useCall<Option<AccountId>[]>(api.query.staking.bonded.multi as any, [allAccounts]);
|
||||
const queryLedger = useCall<Option<StakingLedger>[]>(api.query.staking.ledger.multi as any, [allAccounts]);
|
||||
const [pendingPayouts, setPendingPayouts] = useState<Record<string, number>>({});
|
||||
const ownStashes = useOwnStashes();
|
||||
const [isNewStakeOpen, setIsNewStateOpen] = useState(false);
|
||||
const [foundStashes, setFoundStashes] = useState<[string, boolean][] | null>(null);
|
||||
const [stashTypes, setStashTypes] = useState<Record<string, number>>({});
|
||||
|
||||
useEffect((): void => {
|
||||
setFoundStashes(getStashes(allAccounts, stashTypes, queryBonded, queryLedger));
|
||||
}, [allAccounts, queryBonded, queryLedger, stashTypes]);
|
||||
|
||||
useEffect((): void => {
|
||||
onUpdatePending(Object.values(pendingPayouts).filter((count) => !!count).length);
|
||||
}, [pendingPayouts]);
|
||||
ownStashes && setFoundStashes(
|
||||
ownStashes.sort((a, b): number =>
|
||||
(stashTypes[a[0]] || 99) - (stashTypes[b[0]] || 99)
|
||||
)
|
||||
);
|
||||
}, [ownStashes, stashTypes]);
|
||||
|
||||
const _toggleNewStake = (): void => setIsNewStateOpen(!isNewStakeOpen);
|
||||
const _onUpdatePayout = (stashId: string, count: number): void => setPendingPayouts({ ...pendingPayouts, [stashId]: count });
|
||||
const _onUpdateType = (stashId: string, type: 'validator' | 'nominator' | 'started' | 'other'): void =>
|
||||
setStashTypes({
|
||||
...stashTypes,
|
||||
@ -111,9 +83,9 @@ export default function Actions ({ allStashes, className, isVisible, next, onUpd
|
||||
isVisible={isVisible}
|
||||
key={stashId}
|
||||
next={next}
|
||||
onUpdatePayout={_onUpdatePayout}
|
||||
onUpdateType={_onUpdateType}
|
||||
recentlyOnline={recentlyOnline}
|
||||
rewards={allRewards && allRewards[stashId]}
|
||||
stakingOverview={stakingOverview}
|
||||
stashId={stashId}
|
||||
/>
|
||||
|
||||
@ -12,7 +12,7 @@ import { useLocation } from 'react-router-dom';
|
||||
import styled from 'styled-components';
|
||||
import { HelpOverlay } from '@polkadot/react-components';
|
||||
import Tabs from '@polkadot/react-components/Tabs';
|
||||
import { useCall, useAccounts, useApi } from '@polkadot/react-hooks';
|
||||
import { useAccounts, useApi, useCall, useOwnEraRewards } from '@polkadot/react-hooks';
|
||||
|
||||
import basicMd from './md/basic.md';
|
||||
import Actions from './Actions';
|
||||
@ -22,6 +22,8 @@ import Query from './Query';
|
||||
import Targets from './Targets';
|
||||
import { useTranslation } from './translate';
|
||||
|
||||
export { default as useCounter } from './useCounter';
|
||||
|
||||
function reduceNominators (nominators: string[], additional: string[]): string[] {
|
||||
return nominators.concat(...additional.filter((nominator): boolean => !nominators.includes(nominator)));
|
||||
}
|
||||
@ -31,8 +33,8 @@ function StakingApp ({ basePath, className }: Props): React.ReactElement<Props>
|
||||
const { api } = useApi();
|
||||
const { hasAccounts } = useAccounts();
|
||||
const { pathname } = useLocation();
|
||||
const { allRewards, rewardCount } = useOwnEraRewards();
|
||||
const [next, setNext] = useState<string[] | undefined>();
|
||||
const [payoutCount, setPayoutCount] = useState(0);
|
||||
const allStashes = useCall<string[]>(api.derive.staking.controllers, [], {
|
||||
transform: ([stashes]: [AccountId[]]): string[] =>
|
||||
stashes.map((accountId): string => accountId.toString())
|
||||
@ -61,8 +63,8 @@ function StakingApp ({ basePath, className }: Props): React.ReactElement<Props>
|
||||
name: 'actions',
|
||||
text: t('Account actions{{count}}', {
|
||||
replace: {
|
||||
count: payoutCount
|
||||
? ` (${payoutCount})`
|
||||
count: rewardCount
|
||||
? ` (${rewardCount})`
|
||||
: ''
|
||||
}
|
||||
})
|
||||
@ -72,7 +74,7 @@ function StakingApp ({ basePath, className }: Props): React.ReactElement<Props>
|
||||
name: 'query',
|
||||
text: t('Validator stats')
|
||||
}
|
||||
], [payoutCount, t]);
|
||||
], [rewardCount, t]);
|
||||
const hiddenTabs = useMemo((): string[] => {
|
||||
const result = next ? [] : ['waiting'];
|
||||
|
||||
@ -116,11 +118,11 @@ function StakingApp ({ basePath, className }: Props): React.ReactElement<Props>
|
||||
</Route>
|
||||
</Switch>
|
||||
<Actions
|
||||
allRewards={allRewards}
|
||||
allStashes={allStashes}
|
||||
isVisible={pathname === `${basePath}/actions`}
|
||||
recentlyOnline={recentlyOnline}
|
||||
next={next}
|
||||
onUpdatePending={setPayoutCount}
|
||||
stakingOverview={stakingOverview}
|
||||
/>
|
||||
<Overview
|
||||
|
||||
11
packages/page-staking/src/useCounter.ts
Normal file
11
packages/page-staking/src/useCounter.ts
Normal file
@ -0,0 +1,11 @@
|
||||
// Copyright 2017-2020 @polkadot/app-democracy authors & contributors
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
import { useOwnEraRewards } from '@polkadot/react-hooks';
|
||||
|
||||
export default function useCounter (): number {
|
||||
const { rewardCount } = useOwnEraRewards();
|
||||
|
||||
return rewardCount;
|
||||
}
|
||||
@ -31,7 +31,7 @@
|
||||
"homepage": "https://github.com/polkadot-js/ui/tree/master/packages/ui-reactive#readme",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.8.7",
|
||||
"@polkadot/api": "^1.6.0-beta.26",
|
||||
"@polkadot/api": "^1.6.0-beta.28",
|
||||
"@polkadot/extension-dapp": "^0.22.1",
|
||||
"rxjs-compat": "^6.5.4"
|
||||
}
|
||||
|
||||
@ -296,7 +296,7 @@ function renderBalances (props: Props, allAccounts: string[], t: (key: string) =
|
||||
text={balancesAll.lockedBreakdown.map(({ amount, reasons }, index): React.ReactNode => (
|
||||
<div key={index}>
|
||||
{amount.isMax()
|
||||
? t('all available')
|
||||
? t('everything')
|
||||
: formatBalance(amount, { forceUnit: '-' })
|
||||
}<div className='faded'>{reasons.toString()}</div>
|
||||
</div>
|
||||
|
||||
@ -14,6 +14,8 @@ export { default as useFavorites } from './useFavorites';
|
||||
export { default as useIsMountedRef } from './useIsMountedRef';
|
||||
export { default as useMembers } from './useMembers';
|
||||
export { default as useModal } from './useModal';
|
||||
export { default as useOwnEraRewards } from './useOwnEraRewards';
|
||||
export { default as useOwnStashes, useOwnStashIds } from './useOwnStashes';
|
||||
export { default as usePassword } from './usePassword';
|
||||
export { default as useSudo } from './useSudo';
|
||||
export { default as useToggle } from './useToggle';
|
||||
|
||||
@ -9,13 +9,14 @@ import { SubmittableExtrinsic } from '@polkadot/api/promise/types';
|
||||
|
||||
export type CallParam = any;
|
||||
|
||||
export type CallParams = [] | [CallParam] | [CallParam, CallParam] | [CallParam, CallParam, CallParam];
|
||||
export type CallParams = [] | [CallParam] | [CallParam, CallParam] | [CallParam, CallParam, CallParam] | any[];
|
||||
|
||||
export interface CallOptions <T> {
|
||||
defaultValue?: T;
|
||||
isSingle?: boolean;
|
||||
paramMap?: (params: any) => CallParams;
|
||||
transform?: (value: any) => T;
|
||||
withParams?: boolean;
|
||||
}
|
||||
|
||||
export type TxDef = [string, any[] | ConstructTxFn];
|
||||
|
||||
@ -27,7 +27,7 @@ export default function useAccounts (): UseAccounts {
|
||||
});
|
||||
|
||||
return (): void => {
|
||||
subscription.unsubscribe();
|
||||
setTimeout(() => subscription.unsubscribe(), 0);
|
||||
};
|
||||
}, []);
|
||||
|
||||
|
||||
@ -27,7 +27,7 @@ export default function useAccounts (): UseAccounts {
|
||||
});
|
||||
|
||||
return (): void => {
|
||||
subscription.unsubscribe();
|
||||
setTimeout(() => subscription.unsubscribe(), 0);
|
||||
};
|
||||
}, []);
|
||||
|
||||
|
||||
@ -59,13 +59,15 @@ function unsubscribe (tracker: TrackerRef): void {
|
||||
tracker.current.isActive = false;
|
||||
|
||||
if (tracker.current.subscriber) {
|
||||
tracker.current.subscriber.then((unsubFn): void => unsubFn());
|
||||
tracker.current.subscriber.then((unsubFn): void => {
|
||||
setTimeout(unsubFn, 0);
|
||||
});
|
||||
tracker.current.subscriber = null;
|
||||
}
|
||||
}
|
||||
|
||||
// subscribe, tyring to play nice with the browser threads
|
||||
function subscribe <T> (mounted: MountedRef, tracker: TrackerRef, fn: TrackFn | undefined, params: CallParams, setValue: (value: T) => void, { isSingle, transform = transformIdentity }: CallOptions<T>): void {
|
||||
// subscribe, trying to play nice with the browser threads
|
||||
function subscribe <T> (mounted: MountedRef, tracker: TrackerRef, fn: TrackFn | undefined, params: CallParams, setValue: (value: T) => void, { isSingle, transform = transformIdentity, withParams }: CallOptions<T>): void {
|
||||
const validParams = params.filter((p): boolean => !isUndefined(p));
|
||||
|
||||
unsubscribe(tracker);
|
||||
@ -84,7 +86,10 @@ function subscribe <T> (mounted: MountedRef, tracker: TrackerRef, fn: TrackFn |
|
||||
// since .subscriber may not be set on immeditae callback)
|
||||
if (mounted.current && tracker.current.isActive && (!isSingle || !tracker.current.count)) {
|
||||
tracker.current.count++;
|
||||
setValue(transform(value));
|
||||
|
||||
((transformed: any): void => {
|
||||
setTimeout(() => setValue(transformed), 0);
|
||||
})(withParams ? [params, transform(value)] : transform(value));
|
||||
}
|
||||
});
|
||||
} else {
|
||||
@ -103,10 +108,10 @@ export default function useCall <T> (fn: TrackFn | undefined | null | false, par
|
||||
const tracker = useRef<Tracker>({ isActive: false, count: 0, serialized: null, subscriber: null });
|
||||
const [value, setValue] = useState<T | undefined>(options.defaultValue);
|
||||
|
||||
// initial effect, we need an unsubscription
|
||||
// initial effect, we need an un-subscription
|
||||
useEffect((): () => void => {
|
||||
return (): void => {
|
||||
unsubscribe(tracker);
|
||||
setTimeout(() => unsubscribe(tracker), 0);
|
||||
};
|
||||
}, []);
|
||||
|
||||
|
||||
@ -29,7 +29,7 @@ export default function useMembers (collective: 'council' | 'technicalCommittee'
|
||||
})
|
||||
: useCall<Result>(api.query.technicalCommittee.members, [], {
|
||||
transform: (accounts: AccountId[]): Result =>
|
||||
getResult(allAccounts, accounts.map(accountId => accountId.toString()))
|
||||
getResult(allAccounts, accounts.map((accountId) => accountId.toString()))
|
||||
})
|
||||
) || { isMember: false, members: [] };
|
||||
|
||||
|
||||
68
packages/react-hooks/src/useOwnEraRewards.tsx
Normal file
68
packages/react-hooks/src/useOwnEraRewards.tsx
Normal file
@ -0,0 +1,68 @@
|
||||
// Copyright 2017-2020 @polkadot/react-hooks authors & contributors
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
import { DerivedStakingQuery, DeriveStakerReward } from '@polkadot/api-derive/types';
|
||||
import { EraIndex } from '@polkadot/types/interfaces';
|
||||
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
import BN from 'bn.js';
|
||||
import useApi from './useApi';
|
||||
import useCall from './useCall';
|
||||
import { useOwnStashIds } from './useOwnStashes';
|
||||
|
||||
interface OwnRewards {
|
||||
allRewards?: Record<string, DeriveStakerReward[]>;
|
||||
rewardCount: number;
|
||||
}
|
||||
|
||||
function useNextPayouts (): [string, EraIndex][] | undefined {
|
||||
const { api } = useApi();
|
||||
const stashIds = useOwnStashIds();
|
||||
const allInfo = useCall<DerivedStakingQuery[]>(stashIds && api.derive.staking?.queryMulti, stashIds);
|
||||
const [rewards, setRewards] = useState<[string, EraIndex][] | undefined>();
|
||||
|
||||
useEffect((): void => {
|
||||
stashIds && allInfo && setRewards(
|
||||
allInfo
|
||||
.map(({ stakingLedger }, index) => [stashIds[index], stakingLedger?.lastReward?.unwrapOr(new BN(-1)).addn(1)])
|
||||
.filter((value): value is [string, EraIndex] => !!value[1])
|
||||
);
|
||||
}, [allInfo, stashIds]);
|
||||
|
||||
return rewards;
|
||||
}
|
||||
|
||||
function getRewards ([theseParams, theseRewards]: [[string, EraIndex][], DeriveStakerReward[][]], nextParams: [string, EraIndex][]): OwnRewards {
|
||||
const allRewards = theseRewards.reduce((result: Record<string, DeriveStakerReward[]>, rewards, index): Record<string, DeriveStakerReward[]> => {
|
||||
const [stashId] = theseParams[index];
|
||||
const nextPayout = nextParams.find(([thisId]) => thisId === stashId);
|
||||
|
||||
if (nextPayout) {
|
||||
result[stashId] = rewards.filter(({ era, isEmpty }) => !isEmpty && era.gte(nextPayout[1]));
|
||||
}
|
||||
|
||||
return result;
|
||||
}, {});
|
||||
|
||||
return {
|
||||
allRewards,
|
||||
rewardCount: Object.values(allRewards).filter((rewards) => !!rewards.length).length
|
||||
};
|
||||
}
|
||||
|
||||
export default function useOwnEraRewards (): OwnRewards {
|
||||
const { api } = useApi();
|
||||
const nextParams = useNextPayouts();
|
||||
const available = useCall<[[string, EraIndex][], DeriveStakerReward[][]]>(nextParams && api.derive.staking?.stakerRewardsMulti, nextParams, { withParams: true });
|
||||
const [state, setState] = useState<OwnRewards>({ rewardCount: 0 });
|
||||
|
||||
useEffect((): void => {
|
||||
if (available && nextParams) {
|
||||
setState(getRewards(available, nextParams));
|
||||
}
|
||||
}, [available, nextParams]);
|
||||
|
||||
return state;
|
||||
}
|
||||
61
packages/react-hooks/src/useOwnStashes.tsx
Normal file
61
packages/react-hooks/src/useOwnStashes.tsx
Normal file
@ -0,0 +1,61 @@
|
||||
// Copyright 2017-2020 @polkadot/react-hooks authors & contributors
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
import { AccountId, StakingLedger } from '@polkadot/types/interfaces';
|
||||
|
||||
import { useEffect, useState } from 'react';
|
||||
import { Option } from '@polkadot/types';
|
||||
|
||||
import useAccounts from './useAccounts';
|
||||
import useApi from './useApi';
|
||||
import useCall from './useCall';
|
||||
|
||||
type IsInKeyring = boolean;
|
||||
|
||||
function getStashes (allAccounts: string[], ownBonded: Option<AccountId>[], ownLedger: Option<StakingLedger>[]): [string, IsInKeyring][] {
|
||||
const result: [string, IsInKeyring][] = [];
|
||||
|
||||
ownBonded.forEach((value, index): void => {
|
||||
value.isSome && result.push([allAccounts[index], true]);
|
||||
});
|
||||
|
||||
ownLedger.forEach((ledger): void => {
|
||||
if (ledger.isSome) {
|
||||
const stashId = ledger.unwrap().stash.toString();
|
||||
|
||||
!result.some(([accountId]): boolean => accountId === stashId) && result.push([stashId, false]);
|
||||
}
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
export default function useOwnStashes (): [string, IsInKeyring][] | undefined {
|
||||
const { allAccounts } = useAccounts();
|
||||
const { api } = useApi();
|
||||
const ownBonded = useCall<Option<AccountId>[]>(api.query.staking?.bonded.multi as any, [allAccounts]);
|
||||
const ownLedger = useCall<Option<StakingLedger>[]>(api.query.staking?.ledger.multi as any, [allAccounts]);
|
||||
const [state, setState] = useState<[string, IsInKeyring][] | undefined>();
|
||||
|
||||
useEffect((): void => {
|
||||
allAccounts && ownBonded && ownLedger && setState(
|
||||
getStashes(allAccounts, ownBonded, ownLedger)
|
||||
);
|
||||
}, [allAccounts, ownBonded, ownLedger]);
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
export function useOwnStashIds (): string[] | undefined {
|
||||
const ownStashes = useOwnStashes();
|
||||
const [stashIds, setStashIds] = useState<string[] | undefined>();
|
||||
|
||||
useEffect((): void => {
|
||||
ownStashes && setStashIds(
|
||||
ownStashes.map(([stashId]) => stashId)
|
||||
);
|
||||
}, [ownStashes]);
|
||||
|
||||
return stashIds;
|
||||
}
|
||||
@ -56,7 +56,7 @@ function FormatBalance ({ children, className, label, value, withSi }: Props): R
|
||||
{label || ''}{
|
||||
value
|
||||
? value === 'all'
|
||||
? t('all available')
|
||||
? t('everything')
|
||||
: format(value, currency, withSi)
|
||||
: '-'
|
||||
}{children}
|
||||
|
||||
112
yarn.lock
112
yarn.lock
@ -2765,56 +2765,56 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@polkadot/api-contract@npm:^1.6.0-beta.26":
|
||||
version: 1.6.0-beta.26
|
||||
resolution: "@polkadot/api-contract@npm:1.6.0-beta.26"
|
||||
"@polkadot/api-contract@npm:^1.6.0-beta.28":
|
||||
version: 1.6.0-beta.28
|
||||
resolution: "@polkadot/api-contract@npm:1.6.0-beta.28"
|
||||
dependencies:
|
||||
"@babel/runtime": ^7.8.4
|
||||
"@polkadot/api": 1.6.0-beta.26
|
||||
"@polkadot/rpc-core": 1.6.0-beta.26
|
||||
"@polkadot/types": 1.6.0-beta.26
|
||||
"@polkadot/api": 1.6.0-beta.28
|
||||
"@polkadot/rpc-core": 1.6.0-beta.28
|
||||
"@polkadot/types": 1.6.0-beta.28
|
||||
"@polkadot/util": ^2.6.2
|
||||
bn.js: ^5.1.1
|
||||
rxjs: ^6.5.4
|
||||
checksum: 2/04d88c7d980fad55da69b5714a949818d06e7a8af7020df8fb52eed7b93279cf3cddc68fada89b1b308db63c6dcc436093566e6be1d399b0f5f91b7276f7a4a8
|
||||
checksum: 2/f199f996c521be7c88a5754565842b6f30029bcca41a5a19c91f667fe458543d052c854d19ee9b4c991991e2d4a39df4c79d2c10d45592fabc875e1b043cd29b
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@polkadot/api-derive@npm:1.6.0-beta.26":
|
||||
version: 1.6.0-beta.26
|
||||
resolution: "@polkadot/api-derive@npm:1.6.0-beta.26"
|
||||
"@polkadot/api-derive@npm:1.6.0-beta.28":
|
||||
version: 1.6.0-beta.28
|
||||
resolution: "@polkadot/api-derive@npm:1.6.0-beta.28"
|
||||
dependencies:
|
||||
"@babel/runtime": ^7.8.4
|
||||
"@polkadot/api": 1.6.0-beta.26
|
||||
"@polkadot/rpc-core": 1.6.0-beta.26
|
||||
"@polkadot/rpc-provider": 1.6.0-beta.26
|
||||
"@polkadot/types": 1.6.0-beta.26
|
||||
"@polkadot/api": 1.6.0-beta.28
|
||||
"@polkadot/rpc-core": 1.6.0-beta.28
|
||||
"@polkadot/rpc-provider": 1.6.0-beta.28
|
||||
"@polkadot/types": 1.6.0-beta.28
|
||||
"@polkadot/util": ^2.6.2
|
||||
"@polkadot/util-crypto": ^2.6.2
|
||||
bn.js: ^5.1.1
|
||||
memoizee: ^0.4.14
|
||||
rxjs: ^6.5.4
|
||||
checksum: 2/03dcebaf7cbc88dfbc6f7e90d9a24d6cdbbf8f789e8ff3c4fc2c01c67e259da9ea28ace7f5d6f0e3d266557c0c5e4e619bc0fe83d55ad4da8946d4433b6a3933
|
||||
checksum: 2/0bf002a4e2a3e4e1cae760e50980a54974b026271421bfb2436ac3505c9802319b72818592ff79893abb65e09c4e835df89f8b9d8486f93a80dc9737dd527c87
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@polkadot/api@npm:^1.6.0-beta.26":
|
||||
version: 1.6.0-beta.26
|
||||
resolution: "@polkadot/api@npm:1.6.0-beta.26"
|
||||
"@polkadot/api@npm:^1.6.0-beta.28":
|
||||
version: 1.6.0-beta.28
|
||||
resolution: "@polkadot/api@npm:1.6.0-beta.28"
|
||||
dependencies:
|
||||
"@babel/runtime": ^7.8.4
|
||||
"@polkadot/api-derive": 1.6.0-beta.26
|
||||
"@polkadot/api-derive": 1.6.0-beta.28
|
||||
"@polkadot/keyring": ^2.6.2
|
||||
"@polkadot/metadata": 1.6.0-beta.26
|
||||
"@polkadot/rpc-core": 1.6.0-beta.26
|
||||
"@polkadot/rpc-provider": 1.6.0-beta.26
|
||||
"@polkadot/types": 1.6.0-beta.26
|
||||
"@polkadot/metadata": 1.6.0-beta.28
|
||||
"@polkadot/rpc-core": 1.6.0-beta.28
|
||||
"@polkadot/rpc-provider": 1.6.0-beta.28
|
||||
"@polkadot/types": 1.6.0-beta.28
|
||||
"@polkadot/util": ^2.6.2
|
||||
"@polkadot/util-crypto": ^2.6.2
|
||||
bn.js: ^5.1.1
|
||||
eventemitter3: ^4.0.0
|
||||
rxjs: ^6.5.4
|
||||
checksum: 2/3906cf905c8ad268bc5d70766c0b5ae14422337367fc05e2e7de3269b472e218191b3b63af5f4bc001af921dfd9750b78322253f1807096a7f8eeaaf44c25912
|
||||
checksum: 2/b539969aa9a502da96f52042d8a1a5041d10c9bb18f3a3275ecba2d85daade5bf6b63c70d3e0ae8723af6cf3e2e80f54a4eb983572c32b63cb660abc53f86a59
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@ -2864,7 +2864,7 @@ __metadata:
|
||||
resolution: "@polkadot/app-contracts@workspace:packages/page-contracts"
|
||||
dependencies:
|
||||
"@babel/runtime": ^7.8.7
|
||||
"@polkadot/api-contract": ^1.6.0-beta.26
|
||||
"@polkadot/api-contract": ^1.6.0-beta.28
|
||||
languageName: unknown
|
||||
linkType: soft
|
||||
|
||||
@ -3176,14 +3176,14 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@polkadot/jsonrpc@npm:1.6.0-beta.26":
|
||||
version: 1.6.0-beta.26
|
||||
resolution: "@polkadot/jsonrpc@npm:1.6.0-beta.26"
|
||||
"@polkadot/jsonrpc@npm:1.6.0-beta.28":
|
||||
version: 1.6.0-beta.28
|
||||
resolution: "@polkadot/jsonrpc@npm:1.6.0-beta.28"
|
||||
dependencies:
|
||||
"@babel/runtime": ^7.8.4
|
||||
"@polkadot/types": 1.6.0-beta.26
|
||||
"@polkadot/types": 1.6.0-beta.28
|
||||
"@polkadot/util": ^2.6.2
|
||||
checksum: 2/b82d18c3d444557993e97cf9a0751f50b76c4176337b21bf76ffdfd3cb48ce9e17e9c404930c3ca5b4feb3e475edcc2f249c8851d3b6ca7381108039b28abe40
|
||||
checksum: 2/fef65b236a5deb741bd022b8c45422133785957b0e1f50cd8596bdc6026430df7c3e32847e20e09a07daf6494bca67f4c79ed5b0213cb9aaf1ad91fba19313a6
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@ -3198,16 +3198,16 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@polkadot/metadata@npm:1.6.0-beta.26":
|
||||
version: 1.6.0-beta.26
|
||||
resolution: "@polkadot/metadata@npm:1.6.0-beta.26"
|
||||
"@polkadot/metadata@npm:1.6.0-beta.28":
|
||||
version: 1.6.0-beta.28
|
||||
resolution: "@polkadot/metadata@npm:1.6.0-beta.28"
|
||||
dependencies:
|
||||
"@babel/runtime": ^7.8.4
|
||||
"@polkadot/types": 1.6.0-beta.26
|
||||
"@polkadot/types": 1.6.0-beta.28
|
||||
"@polkadot/util": ^2.6.2
|
||||
"@polkadot/util-crypto": ^2.6.2
|
||||
bn.js: ^5.1.1
|
||||
checksum: 2/390b0d6a3f0b1a22504ada19b1b2993c98146acf79fa1714b524e8e7b91d1b4235d3e111924a48dc7bd5d4023e0bd611e5fee7b74785323a843faf3dc664d636
|
||||
checksum: 2/1cceff15745c5a8b81fff1731f7cff69db624dc42fb593e969b68e2313531141700d64dfb05ce8469fe281e9dd94f17e5268cd4b3b2866a01da971134ca7d779
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@ -3216,7 +3216,7 @@ __metadata:
|
||||
resolution: "@polkadot/react-api@workspace:packages/react-api"
|
||||
dependencies:
|
||||
"@babel/runtime": ^7.8.7
|
||||
"@polkadot/api": ^1.6.0-beta.26
|
||||
"@polkadot/api": ^1.6.0-beta.28
|
||||
"@polkadot/extension-dapp": ^0.22.1
|
||||
rxjs-compat: ^6.5.4
|
||||
languageName: unknown
|
||||
@ -3328,37 +3328,37 @@ __metadata:
|
||||
languageName: unknown
|
||||
linkType: soft
|
||||
|
||||
"@polkadot/rpc-core@npm:1.6.0-beta.26":
|
||||
version: 1.6.0-beta.26
|
||||
resolution: "@polkadot/rpc-core@npm:1.6.0-beta.26"
|
||||
"@polkadot/rpc-core@npm:1.6.0-beta.28":
|
||||
version: 1.6.0-beta.28
|
||||
resolution: "@polkadot/rpc-core@npm:1.6.0-beta.28"
|
||||
dependencies:
|
||||
"@babel/runtime": ^7.8.4
|
||||
"@polkadot/jsonrpc": 1.6.0-beta.26
|
||||
"@polkadot/metadata": 1.6.0-beta.26
|
||||
"@polkadot/rpc-provider": 1.6.0-beta.26
|
||||
"@polkadot/types": 1.6.0-beta.26
|
||||
"@polkadot/jsonrpc": 1.6.0-beta.28
|
||||
"@polkadot/metadata": 1.6.0-beta.28
|
||||
"@polkadot/rpc-provider": 1.6.0-beta.28
|
||||
"@polkadot/types": 1.6.0-beta.28
|
||||
"@polkadot/util": ^2.6.2
|
||||
memoizee: ^0.4.14
|
||||
rxjs: ^6.5.4
|
||||
checksum: 2/be942ae0416f89ea224d8e74434b3482e42af282996cb5d6ad5c25307185a2b76b2662adcf9d165201df4b3db816074ea74a640d7def2dd8e89e2ff3de369d24
|
||||
checksum: 2/70f0c58a0b17dd64ac721d2687fc1a3c958d751123a1486ade7a820c1f202d8809c412e5f6f4f5fa3d51ec8cae4350f361086f7a1e0a4097947315ae9c4af17d
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@polkadot/rpc-provider@npm:1.6.0-beta.26":
|
||||
version: 1.6.0-beta.26
|
||||
resolution: "@polkadot/rpc-provider@npm:1.6.0-beta.26"
|
||||
"@polkadot/rpc-provider@npm:1.6.0-beta.28":
|
||||
version: 1.6.0-beta.28
|
||||
resolution: "@polkadot/rpc-provider@npm:1.6.0-beta.28"
|
||||
dependencies:
|
||||
"@babel/runtime": ^7.8.4
|
||||
"@polkadot/jsonrpc": 1.6.0-beta.26
|
||||
"@polkadot/metadata": 1.6.0-beta.26
|
||||
"@polkadot/types": 1.6.0-beta.26
|
||||
"@polkadot/jsonrpc": 1.6.0-beta.28
|
||||
"@polkadot/metadata": 1.6.0-beta.28
|
||||
"@polkadot/types": 1.6.0-beta.28
|
||||
"@polkadot/util": ^2.6.2
|
||||
"@polkadot/util-crypto": ^2.6.2
|
||||
bn.js: ^5.1.1
|
||||
eventemitter3: ^4.0.0
|
||||
isomorphic-fetch: ^2.2.1
|
||||
websocket: ^1.0.31
|
||||
checksum: 2/0bbde7518b9b5f12e2f5247e1eb4ad55e6a61f6920ae7838d15f6bb423105425ede34fc60d5e8f043f3ee32614132040094925a2dd9cc16129935e75eac893a6
|
||||
checksum: 2/ed3c826c87a38d6e147398d1ab997ea8acd72bdde260c668f0370ef49b56f54bf7d37575ba1d3e9d12149730e5b824f957e1e060959ff3d8d14765cebc10fdd2
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@ -3371,19 +3371,19 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@polkadot/types@npm:^1.6.0-beta.26":
|
||||
version: 1.6.0-beta.26
|
||||
resolution: "@polkadot/types@npm:1.6.0-beta.26"
|
||||
"@polkadot/types@npm:^1.6.0-beta.28":
|
||||
version: 1.6.0-beta.28
|
||||
resolution: "@polkadot/types@npm:1.6.0-beta.28"
|
||||
dependencies:
|
||||
"@babel/runtime": ^7.8.4
|
||||
"@polkadot/metadata": 1.6.0-beta.26
|
||||
"@polkadot/metadata": 1.6.0-beta.28
|
||||
"@polkadot/util": ^2.6.2
|
||||
"@polkadot/util-crypto": ^2.6.2
|
||||
"@types/bn.js": ^4.11.6
|
||||
bn.js: ^5.1.1
|
||||
memoizee: ^0.4.14
|
||||
rxjs: ^6.5.4
|
||||
checksum: 2/1318129dabcfd973e2d7e0f3062cefff26504c11c0c2aa3e02858362c21ebb15d01c3f02177ba2ec7ccb9e7c304485c0860929e7b7811de54ed0327ce026a7be
|
||||
checksum: 2/58faa8d0ce629037a61d16029aa80cca9775dc181f7e5480daef2a5665eb8a53ed811afd66fd4a47acf70add4337e8bcbbaba92a69935fde5e0abbf85558c07e
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user