Cleanup staking info (#1532)
* Cleanup staking info * Cache calls * V1, V2 (Aura & Babe) tested & working * Hopefully pacify docs build * Second time lucky? * Third time lucky? * Fouth time
This commit is contained in:
@@ -0,0 +1,31 @@
|
||||
// Copyright 2017-2019 @polkadot/api-derive 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 { DerivedStaking } from '../types';
|
||||
|
||||
import { Observable, combineLatest } from 'rxjs';
|
||||
import { switchMap } from 'rxjs/operators';
|
||||
import { ApiInterfaceRx } from '@polkadot/api/types';
|
||||
|
||||
import { drr } from '../util/drr';
|
||||
import { controllers } from './controllers';
|
||||
import { info } from './info';
|
||||
|
||||
/**
|
||||
* @description Retrieve all the staking info available for the chain
|
||||
*/
|
||||
export function all (api: ApiInterfaceRx): () => Observable<DerivedStaking[]> {
|
||||
const infoCall = info(api);
|
||||
const controllersCall = controllers(api);
|
||||
|
||||
return (): Observable<DerivedStaking[]> =>
|
||||
controllersCall().pipe(
|
||||
switchMap(([accountIds]): Observable<DerivedStaking[]> =>
|
||||
combineLatest(
|
||||
accountIds.map((accountId): Observable<DerivedStaking> => infoCall(accountId))
|
||||
)
|
||||
),
|
||||
drr()
|
||||
);
|
||||
}
|
||||
@@ -2,6 +2,7 @@
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
export * from './all';
|
||||
export * from './controllers';
|
||||
export * from './info';
|
||||
export * from './recentlyOffline';
|
||||
|
||||
@@ -8,7 +8,7 @@ import { Exposure, RewardDestination, StakingLedger, UnlockChunk, ValidatorPrefs
|
||||
import { Codec } from '@polkadot/types/types';
|
||||
|
||||
import { ApiInterfaceRx } from '@polkadot/api/types';
|
||||
import { DerivedStaking, DerivedUnlocking } from '../types';
|
||||
import { DerivedRecentlyOffline, DerivedStaking, DerivedUnlocking } from '../types';
|
||||
|
||||
import BN from 'bn.js';
|
||||
import { combineLatest, Observable, of } from 'rxjs';
|
||||
@@ -22,7 +22,28 @@ import { eraLength } from '../session/eraLength';
|
||||
import { recentlyOffline } from './recentlyOffline';
|
||||
import { drr } from '../util/drr';
|
||||
|
||||
type MultiResult = [Option<AccountId> | Vec<[AccountId, Keys] & Codec>, Option<StakingLedger>, [AccountId[]], RewardDestination, Exposure, [ValidatorPrefs], Option<Keys>?];
|
||||
interface Calls {
|
||||
bestNumberCall (): Observable<BlockNumber>;
|
||||
eraLengthCall (): Observable<BlockNumber>;
|
||||
recentlyOfflineCall (): Observable<DerivedRecentlyOffline>;
|
||||
}
|
||||
|
||||
interface ParseInput {
|
||||
accountId: AccountId;
|
||||
bestNumber: BlockNumber;
|
||||
controllerId: AccountId;
|
||||
eraLength: BlockNumber;
|
||||
recentlyOffline?: DerivedRecentlyOffline;
|
||||
nominators: AccountId[];
|
||||
rewardDestination: RewardDestination;
|
||||
queuedKeys?: [AccountId, Keys][];
|
||||
stakers: Exposure;
|
||||
stakingLedger: Option<StakingLedger>;
|
||||
stashId: AccountId;
|
||||
validatorPrefs: ValidatorPrefs;
|
||||
nextKeys?: Option<Keys>;
|
||||
nextKeyFor?: Option<AccountId>;
|
||||
}
|
||||
|
||||
// groups the supplied chunks by era, i.e. { [era]: BN(total of values) }
|
||||
function groupByEra (list: UnlockChunk[]): Record<string, BN> {
|
||||
@@ -82,7 +103,7 @@ function redeemableSum (stakingLedger: StakingLedger | undefined, eraLength: BN,
|
||||
}, new BN(0)));
|
||||
}
|
||||
|
||||
function unwrapSessionIds (stashId: AccountId, queuedKeys: Option<AccountId> | Vec<[AccountId, Keys] & Codec>, nextKeys: Option<Keys>): { nextSessionIds: AccountId[]; nextSessionId?: AccountId; sessionIds: AccountId[]; sessionId?: AccountId } {
|
||||
function unwrapSessionIds (stashId: AccountId, queuedKeys: Option<AccountId> | [AccountId, Keys][], nextKeys: Option<Keys>): { nextSessionIds: AccountId[]; nextSessionId?: AccountId; sessionIds: AccountId[]; sessionId?: AccountId } {
|
||||
// for 2.x we have a Vec<(ValidatorId,Keys)> of the keys
|
||||
if (Array.isArray(queuedKeys)) {
|
||||
const sessionIds = (queuedKeys.find(([currentId]): boolean =>
|
||||
@@ -111,80 +132,116 @@ function unwrapSessionIds (stashId: AccountId, queuedKeys: Option<AccountId> | V
|
||||
};
|
||||
}
|
||||
|
||||
function retrieveMulti (api: ApiInterfaceRx, stashId: AccountId, controllerId: AccountId): Observable<MultiResult> {
|
||||
// A bit horrible, since we push below to forge everything into 1 query - we do this we we end up with one
|
||||
// query to retrieve everything
|
||||
const queries: Array<any> = [
|
||||
api.query.session.queuedKeys
|
||||
? [api.query.session.queuedKeys]
|
||||
: [api.query.session.nextKeyFor, controllerId],
|
||||
[api.query.staking.ledger, controllerId],
|
||||
[api.query.staking.nominators, stashId],
|
||||
[api.query.staking.payee, stashId],
|
||||
[api.query.staking.stakers, stashId],
|
||||
[api.query.staking.validators, stashId]
|
||||
];
|
||||
function parseResult ({ accountId, controllerId, stashId, eraLength, bestNumber, recentlyOffline = {}, nextKeyFor = createType('Option<AccountId>', null), queuedKeys, stakingLedger, nominators, rewardDestination, stakers, validatorPrefs, nextKeys = createType('Option<Keys>', null) }: ParseInput): DerivedStaking {
|
||||
const _stakingLedger = stakingLedger.unwrapOr(undefined);
|
||||
|
||||
//
|
||||
if (api.query.session.nextKeys) {
|
||||
queries.push([api.query.session.nextKeys, [api.consts.session.dedupKeyPrefix, stashId]]);
|
||||
}
|
||||
|
||||
return api.queryMulti(queries) as Observable<MultiResult>;
|
||||
return {
|
||||
accountId,
|
||||
controllerId,
|
||||
nominators,
|
||||
offline: recentlyOffline[stashId.toString()],
|
||||
redeemable: redeemableSum(_stakingLedger, eraLength, bestNumber),
|
||||
rewardDestination,
|
||||
stakers,
|
||||
stakingLedger: _stakingLedger,
|
||||
stashId,
|
||||
unlocking: calculateUnlocking(_stakingLedger, eraLength, bestNumber),
|
||||
validatorPrefs,
|
||||
...unwrapSessionIds(stashId, queuedKeys || nextKeyFor, nextKeys)
|
||||
};
|
||||
}
|
||||
|
||||
function retrieveInfo (api: ApiInterfaceRx, accountId: AccountId, stashId: AccountId, controllerId: AccountId): Observable<DerivedStaking> {
|
||||
return combineLatest([
|
||||
eraLength(api)(),
|
||||
bestNumber(api)(),
|
||||
recentlyOffline(api)(),
|
||||
retrieveMulti(api, stashId, controllerId)
|
||||
]).pipe(
|
||||
map(([eraLength, bestNumber, recentlyOffline, [queuedKeys, _stakingLedger, [nominators], rewardDestination, stakers, [validatorPrefs], nextKeys = createType('Option<Keys>', null)]]): DerivedStaking => {
|
||||
const stakingLedger = _stakingLedger.unwrapOr(undefined);
|
||||
type MultiResultV1 = [Option<AccountId>, Option<StakingLedger>, [Vec<AccountId>], RewardDestination, Exposure, [ValidatorPrefs] & Codec];
|
||||
|
||||
return {
|
||||
accountId,
|
||||
controllerId,
|
||||
nominators,
|
||||
offline: recentlyOffline[stashId.toString()],
|
||||
redeemable: redeemableSum(stakingLedger, eraLength, bestNumber),
|
||||
rewardDestination,
|
||||
stakers,
|
||||
stakingLedger,
|
||||
stashId,
|
||||
unlocking: calculateUnlocking(stakingLedger, eraLength, bestNumber),
|
||||
validatorPrefs,
|
||||
...unwrapSessionIds(stashId, queuedKeys, nextKeys)
|
||||
};
|
||||
function retrieveInfoV1 (api: ApiInterfaceRx, { bestNumberCall, eraLengthCall, recentlyOfflineCall }: Calls, accountId: AccountId, stashId: AccountId, controllerId: AccountId): Observable<DerivedStaking> {
|
||||
return combineLatest([
|
||||
bestNumberCall(),
|
||||
eraLengthCall(),
|
||||
recentlyOfflineCall(),
|
||||
api.queryMulti([
|
||||
[api.query.session.nextKeyFor, controllerId],
|
||||
[api.query.staking.ledger, controllerId],
|
||||
[api.query.staking.nominators, stashId],
|
||||
[api.query.staking.payee, stashId],
|
||||
[api.query.staking.stakers, stashId],
|
||||
[api.query.staking.validators, stashId]
|
||||
]) as Observable<MultiResultV1>
|
||||
]).pipe(map(([
|
||||
bestNumber, eraLength, recentlyOffline,
|
||||
[nextKeyFor, stakingLedger, [nominators], rewardDestination, stakers, [validatorPrefs]]
|
||||
]: [BlockNumber, BlockNumber, DerivedRecentlyOffline, MultiResultV1]): DerivedStaking =>
|
||||
parseResult({
|
||||
accountId, controllerId, stashId, eraLength, bestNumber, recentlyOffline, nextKeyFor, stakingLedger, nominators, rewardDestination, stakers, validatorPrefs
|
||||
})
|
||||
);
|
||||
));
|
||||
}
|
||||
|
||||
type MultiResultV2 = [Option<StakingLedger>, [Vec<AccountId>], RewardDestination, Exposure, [ValidatorPrefs], Option<Keys>];
|
||||
|
||||
function retrieveInfoV2 (api: ApiInterfaceRx, { bestNumberCall, eraLengthCall }: Calls, accountId: AccountId, stashId: AccountId, controllerId: AccountId): Observable<DerivedStaking> {
|
||||
return combineLatest([
|
||||
bestNumberCall(),
|
||||
eraLengthCall(),
|
||||
api.query.session.queuedKeys<Vec<[AccountId, Keys] & Codec>>(),
|
||||
api.queryMulti([
|
||||
[api.query.staking.ledger, controllerId],
|
||||
[api.query.staking.nominators, stashId],
|
||||
[api.query.staking.payee, stashId],
|
||||
[api.query.staking.stakers, stashId],
|
||||
[api.query.staking.validators, stashId],
|
||||
[api.query.session.nextKeys, [api.consts.session.dedupKeyPrefix, stashId]]
|
||||
]) as Observable<MultiResultV2>
|
||||
]).pipe(map(([
|
||||
bestNumber, eraLength, queuedKeys,
|
||||
[stakingLedger, [nominators], rewardDestination, stakers, [validatorPrefs], nextKeys]
|
||||
]: [BlockNumber, BlockNumber, [AccountId, Keys][], MultiResultV2]): DerivedStaking =>
|
||||
parseResult({
|
||||
accountId, controllerId, stashId, eraLength, bestNumber, queuedKeys, stakingLedger, nominators, rewardDestination, stakers, validatorPrefs, nextKeys
|
||||
})
|
||||
));
|
||||
}
|
||||
|
||||
function retrieveV1 (api: ApiInterfaceRx, calls: Calls, controllerId: AccountId): Observable<DerivedStaking> {
|
||||
return api.query.staking
|
||||
.ledger<Option<StakingLedger>>(controllerId)
|
||||
.pipe(
|
||||
switchMap((stakingLedger): Observable<DerivedStaking> =>
|
||||
stakingLedger.isSome
|
||||
? retrieveInfoV1(api, calls, controllerId, stakingLedger.unwrap().stash, controllerId)
|
||||
: of({ accountId: controllerId, nextSessionIds: [], sessionIds: [] })
|
||||
),
|
||||
drr()
|
||||
);
|
||||
}
|
||||
|
||||
function retrieveV2 (api: ApiInterfaceRx, calls: Calls, stashId: AccountId): Observable<DerivedStaking> {
|
||||
return api.query.staking
|
||||
.bonded<Option<AccountId>>(stashId)
|
||||
.pipe(
|
||||
switchMap((controllerId): Observable<DerivedStaking> =>
|
||||
controllerId.isSome
|
||||
? retrieveInfoV2(api, calls, stashId, stashId, controllerId.unwrap())
|
||||
: of({ accountId: stashId, nextSessionIds: [], sessionIds: [] })
|
||||
),
|
||||
drr()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @description From a stash, retrieve the controllerId and fill in all the relevant staking details
|
||||
*/
|
||||
export function info (api: ApiInterfaceRx): (_accountId: Uint8Array | string) => Observable<DerivedStaking> {
|
||||
const calls = {
|
||||
eraLengthCall: eraLength(api),
|
||||
bestNumberCall: bestNumber(api),
|
||||
recentlyOfflineCall: recentlyOffline(api)
|
||||
};
|
||||
|
||||
return (_accountId: Uint8Array | string): Observable<DerivedStaking> => {
|
||||
const accountId = createType('AccountId', _accountId);
|
||||
|
||||
return (
|
||||
// NOTE For 2.x-only support, only the first path is required, therefore we
|
||||
// can replace this with `.bonded<Option<AccountId>>(accountId)` - in 2.x
|
||||
// the session.validators return the stashes (as expected)
|
||||
api.queryMulti<[Option<AccountId>, Option<StakingLedger>]>([
|
||||
[api.query.staking.bonded, accountId], // try to map to controller
|
||||
[api.query.staking.ledger, accountId] // try to map to stash (1.x only)
|
||||
])
|
||||
).pipe(
|
||||
switchMap(([controllerId, stakingLedger]): Observable<DerivedStaking> =>
|
||||
controllerId.isSome
|
||||
? retrieveInfo(api, accountId, accountId, controllerId.unwrap())
|
||||
: stakingLedger.isSome
|
||||
? retrieveInfo(api, accountId, stakingLedger.unwrap().stash, accountId)
|
||||
: of({ accountId, nextSessionIds: [], sessionIds: [] })
|
||||
),
|
||||
drr()
|
||||
);
|
||||
return api.consts.session
|
||||
? retrieveV2(api, calls, accountId)
|
||||
: retrieveV1(api, calls, accountId);
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user