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:
@@ -30,6 +30,9 @@ export type AccountIdAndIndex = [AccountId?, AccountIndex?];
|
||||
* ```
|
||||
*/
|
||||
export function idAndIndex (api: ApiInterfaceRx): (address?: Address | AccountId | AccountIndex | string | null) => Observable<AccountIdAndIndex> {
|
||||
const idToIndexCall = idToIndex(api);
|
||||
const indexToIdCall = indexToId(api);
|
||||
|
||||
return (address?: Address | AccountId | AccountIndex | string | null): Observable<AccountIdAndIndex> => {
|
||||
try {
|
||||
// yes, this can fail, don't care too much, catch will catch it
|
||||
@@ -40,7 +43,7 @@ export function idAndIndex (api: ApiInterfaceRx): (address?: Address | AccountId
|
||||
if (decoded.length === 32) {
|
||||
const accountId = createType('AccountId', decoded);
|
||||
|
||||
return idToIndex(api)(accountId).pipe(
|
||||
return idToIndexCall(accountId).pipe(
|
||||
startWith(undefined),
|
||||
map((accountIndex): AccountIdAndIndex => [accountId, accountIndex] as AccountIdAndIndex),
|
||||
drr()
|
||||
@@ -49,7 +52,7 @@ export function idAndIndex (api: ApiInterfaceRx): (address?: Address | AccountId
|
||||
|
||||
const accountIndex = createType('AccountIndex', decoded);
|
||||
|
||||
return indexToId(api)(accountIndex).pipe(
|
||||
return indexToIdCall(accountIndex).pipe(
|
||||
startWith(undefined),
|
||||
map((accountId): AccountIdAndIndex => [accountId, accountIndex] as AccountIdAndIndex),
|
||||
drr()
|
||||
|
||||
@@ -26,8 +26,10 @@ import { drr } from '../util/drr';
|
||||
* ```
|
||||
*/
|
||||
export function idToIndex (api: ApiInterfaceRx): (accountId: AccountId | string) => Observable<AccountIndex | undefined> {
|
||||
const indexesCall = indexes(api);
|
||||
|
||||
return (accountId: AccountId | string): Observable<AccountIndex | undefined> =>
|
||||
indexes(api)().pipe(
|
||||
indexesCall().pipe(
|
||||
startWith({}),
|
||||
map((indexes: AccountIndexes): AccountIndex | undefined =>
|
||||
(indexes || {})[accountId.toString()]
|
||||
|
||||
@@ -83,13 +83,16 @@ function queryBalances (api: ApiInterfaceRx, accountId: AccountId): Observable<R
|
||||
* ```
|
||||
*/
|
||||
export function all (api: ApiInterfaceRx): (address: AccountIndex | AccountId | Address | string) => Observable<DerivedBalances> {
|
||||
const bestNumberCall = bestNumber(api);
|
||||
const idAndIndexCall = idAndIndex(api);
|
||||
|
||||
return (address: AccountIndex | AccountId | Address | string): Observable<DerivedBalances> => {
|
||||
return idAndIndex(api)(address).pipe(
|
||||
return idAndIndexCall(address).pipe(
|
||||
switchMap(([accountId]): Observable<Result> =>
|
||||
(accountId
|
||||
? combineLatest([
|
||||
of(accountId),
|
||||
bestNumber(api)(),
|
||||
bestNumberCall(),
|
||||
queryBalances(api, accountId),
|
||||
// FIXME This is having issues with Kusama, only use accountNonce atm
|
||||
// api.rpc.account && api.rpc.account.nextIndex
|
||||
|
||||
@@ -15,8 +15,10 @@ import { drr } from '../util/drr';
|
||||
import { votingBalances } from './votingBalances';
|
||||
|
||||
export function votingBalancesNominatorsFor (api: ApiInterfaceRx): (address: AccountId | AccountIndex | Address | string) => Observable<DerivedBalances[]> {
|
||||
const idAndIndexCall = idAndIndex(api);
|
||||
|
||||
return (address: AccountId | AccountIndex | Address | string): Observable<DerivedBalances[]> => {
|
||||
return idAndIndex(api)(address).pipe(
|
||||
return idAndIndexCall(address).pipe(
|
||||
switchMap(([accountId]): Observable<AccountId[]> =>
|
||||
accountId
|
||||
? (api.query.staking.nominatorsFor<Vec<AccountId>>(accountId))
|
||||
|
||||
@@ -27,10 +27,13 @@ import { bestNumberFinalized } from './bestNumberFinalized';
|
||||
* ```
|
||||
*/
|
||||
export function bestNumberLag (api: ApiInterfaceRx): () => Observable<BlockNumber> {
|
||||
const bestNumberCall = bestNumber(api);
|
||||
const bestNumberFinalizedCall = bestNumberFinalized(api);
|
||||
|
||||
return (): Observable<BlockNumber> =>
|
||||
combineLatest([
|
||||
bestNumber(api)(),
|
||||
bestNumberFinalized(api)()
|
||||
bestNumberCall(),
|
||||
bestNumberFinalizedCall()
|
||||
]).pipe(
|
||||
map(([bestNumber, bestNumberFinalized]): BlockNumber =>
|
||||
createType('BlockNumber', bestNumber.sub(bestNumberFinalized))
|
||||
|
||||
@@ -16,13 +16,16 @@ import { votes } from './votes';
|
||||
import { votingBalances } from '../balances/votingBalances';
|
||||
|
||||
export function referendumVotesFor (api: ApiInterfaceRx): (referendumId: BN | number) => Observable<DerivedReferendumVote[]> {
|
||||
const votesCall = votes(api);
|
||||
const votingBalancesCall = votingBalances(api);
|
||||
|
||||
return (referendumId: BN | number): Observable<DerivedReferendumVote[]> =>
|
||||
(api.query.democracy.votersFor<Vec<AccountId>>(referendumId)).pipe(
|
||||
switchMap((votersFor): Observable<[Vec<AccountId>, Vote[], DerivedBalances[]]> =>
|
||||
combineLatest([
|
||||
of(votersFor),
|
||||
votes(api)(referendumId as BN, votersFor),
|
||||
votingBalances(api)(votersFor)
|
||||
votesCall(referendumId as BN, votersFor),
|
||||
votingBalancesCall(votersFor)
|
||||
])
|
||||
),
|
||||
map(([votersFor, votes, balances]): DerivedReferendumVote[] =>
|
||||
|
||||
@@ -15,6 +15,8 @@ import { drr } from '../util/drr';
|
||||
import { referendumInfos } from './referendumInfos';
|
||||
|
||||
export function referendums (api: ApiInterfaceRx): () => Observable<Option<ReferendumInfoExtended>[]> {
|
||||
const referendumInfosCall = referendumInfos(api);
|
||||
|
||||
return (): Observable<Option<ReferendumInfoExtended>[]> =>
|
||||
(api.queryMulti([
|
||||
api.query.democracy.nextTally,
|
||||
@@ -22,7 +24,7 @@ export function referendums (api: ApiInterfaceRx): () => Observable<Option<Refer
|
||||
]) as Observable<[ReferendumIndex?, ReferendumIndex?]>).pipe(
|
||||
switchMap(([nextTally, referendumCount]): Observable<Option<ReferendumInfoExtended>[]> =>
|
||||
referendumCount && nextTally && referendumCount.gt(nextTally) && referendumCount.gtn(0)
|
||||
? referendumInfos(api)(
|
||||
? referendumInfosCall(
|
||||
[...Array(referendumCount.sub(nextTally).toNumber())].map((_, i): BN =>
|
||||
nextTally.addn(i)
|
||||
)
|
||||
|
||||
@@ -58,7 +58,8 @@ function derivePhragmen (candidates: Vec<AccountId>, members: Vec<[AccountId, Ba
|
||||
}
|
||||
|
||||
function queryPhragmen (api: ApiInterfaceRx): Observable<DerivedElectionsInfo> {
|
||||
// NOTE We have an issue where candidates can return `null` for an empty array
|
||||
// NOTE We have an issue where candidates can return `null` for an empty array, hence
|
||||
// we are not using multi queries here, so empty array is empty (instead of space-filled)
|
||||
return combineLatest([
|
||||
api.query.electionsPhragmen.candidates<Vec<AccountId>>(),
|
||||
api.query.electionsPhragmen.members<Vec<[AccountId, Balance] & Codec>>()
|
||||
|
||||
@@ -26,8 +26,10 @@ import { voterPositions } from './voterPositions';
|
||||
* ```
|
||||
*/
|
||||
export function voters (api: ApiInterfaceRx): () => Observable<Vec<AccountId>> {
|
||||
const voterPositionsCall = voterPositions(api);
|
||||
|
||||
return (): Observable<Vec<AccountId>> =>
|
||||
voterPositions(api)().pipe(
|
||||
voterPositionsCall().pipe(
|
||||
map(
|
||||
(voterPositions: DerivedVoterPositions): Vec<AccountId> =>
|
||||
createType(
|
||||
|
||||
@@ -2,7 +2,8 @@
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
import BN from 'bn.js';
|
||||
import { BlockNumber } from '@polkadot/types/interfaces';
|
||||
|
||||
import { Observable } from 'rxjs';
|
||||
import { map } from 'rxjs/operators';
|
||||
import { ApiInterfaceRx } from '@polkadot/api/types';
|
||||
@@ -10,10 +11,12 @@ import { ApiInterfaceRx } from '@polkadot/api/types';
|
||||
import { drr } from '../util/drr';
|
||||
import { info } from './info';
|
||||
|
||||
export function eraLength (api: ApiInterfaceRx): () => Observable<BN> {
|
||||
return (): Observable<BN> =>
|
||||
info(api)().pipe(
|
||||
map(({ eraLength }): BN => eraLength),
|
||||
export function eraLength (api: ApiInterfaceRx): () => Observable<BlockNumber> {
|
||||
const infoCall = info(api);
|
||||
|
||||
return (): Observable<BlockNumber> =>
|
||||
infoCall().pipe(
|
||||
map(({ eraLength }): BlockNumber => eraLength),
|
||||
drr()
|
||||
);
|
||||
}
|
||||
|
||||
@@ -11,8 +11,10 @@ import { drr } from '../util/drr';
|
||||
import { info } from './info';
|
||||
|
||||
export function eraProgress (api: ApiInterfaceRx): () => Observable<BN> {
|
||||
const infoCall = info(api);
|
||||
|
||||
return (): Observable<BN> =>
|
||||
info(api)().pipe(
|
||||
infoCall().pipe(
|
||||
map(({ eraProgress }): BN => eraProgress),
|
||||
drr()
|
||||
);
|
||||
|
||||
@@ -13,8 +13,8 @@ import { Option, u64, createType } from '@polkadot/types';
|
||||
import { drr } from '../util/drr';
|
||||
import { bestNumber } from '../chain';
|
||||
|
||||
type Result94Session = [SessionIndex, Option<BlockNumber>, BlockNumber, BlockNumber, SessionIndex];
|
||||
type Result94 = [BlockNumber, Result94Session];
|
||||
type ResultV1Session = [SessionIndex, Option<BlockNumber>, BlockNumber, BlockNumber, SessionIndex];
|
||||
type ResultV1 = [BlockNumber, ResultV1Session];
|
||||
|
||||
type ResultIndex = [SessionIndex, EraIndex];
|
||||
type ResultSlots = [u64, u64, u64, SessionIndex, EraIndex, SessionIndex];
|
||||
@@ -22,7 +22,7 @@ type ResultType = [boolean, u64, SessionIndex];
|
||||
type Result = [ResultType, ResultSlots];
|
||||
|
||||
// internal helper to just split the logic - take all inputs, do the calculations and combine
|
||||
function createDerived94 ([bestNumber, [currentIndex, _lastLengthChange, sessionLength, lastEraLengthChange, sessionsPerEra]]: Result94): DerivedSessionInfo {
|
||||
function createDerivedV1 ([bestNumber, [currentIndex, _lastLengthChange, sessionLength, lastEraLengthChange, sessionsPerEra]]: ResultV1): DerivedSessionInfo {
|
||||
const lastLengthChange = (_lastLengthChange && _lastLengthChange.unwrapOr(null)) || createType('BlockNumber');
|
||||
const sessionProgress = bestNumber
|
||||
.sub(lastLengthChange)
|
||||
@@ -68,10 +68,10 @@ function createDerivedLatest ([[hasBabe, epochDuration, sessionsPerEra], [curren
|
||||
};
|
||||
}
|
||||
|
||||
function info94 (api: ApiInterfaceRx): Observable<DerivedSessionInfo> {
|
||||
function infoV1 (api: ApiInterfaceRx, bestNumberCall: () => Observable<BlockNumber>): Observable<DerivedSessionInfo> {
|
||||
return combineLatest([
|
||||
bestNumber(api)(),
|
||||
api.queryMulti<Result94Session>([
|
||||
bestNumberCall(),
|
||||
api.queryMulti<ResultV1Session>([
|
||||
api.query.session.currentIndex,
|
||||
api.query.session.lastLengthChange,
|
||||
api.query.session.sessionLength,
|
||||
@@ -79,7 +79,7 @@ function info94 (api: ApiInterfaceRx): Observable<DerivedSessionInfo> {
|
||||
api.query.staking.sessionsPerEra
|
||||
])
|
||||
]).pipe(
|
||||
map(createDerived94),
|
||||
map(createDerivedV1),
|
||||
drr()
|
||||
);
|
||||
}
|
||||
@@ -122,14 +122,13 @@ function infoLatestBabe (api: ApiInterfaceRx): Observable<DerivedSessionInfo> {
|
||||
* @description Retrieves all the session and era info and calculates specific values on it as the length of the session and eras
|
||||
*/
|
||||
export function info (api: ApiInterfaceRx): () => Observable<DerivedSessionInfo> {
|
||||
const bestNumberCall = bestNumber(api);
|
||||
|
||||
return (): Observable<DerivedSessionInfo> => {
|
||||
// With substrate `spec_version 94`, the era and session has been explicitly exposed as `parameter_types`.
|
||||
// pre-94 we had more info and needed to calculate (handle old/Alex first)
|
||||
// https://github.com/paritytech/substrate/commit/dbf322620948935d2bbae214504e6c668c3073ed#diff-c29f42d6b931fa93ba038dbbbfec3055
|
||||
return api.query.session.lastLengthChange
|
||||
? info94(api) // 1.x
|
||||
: api.consts.babe
|
||||
return api.consts.staking
|
||||
? api.consts.babe
|
||||
? infoLatestBabe(api) // 2.x with Babe
|
||||
: infoLatestAura(api); // 2.x with Aura (not all info there)
|
||||
: infoLatestAura(api) // 2.x with Aura (not all info there)
|
||||
: infoV1(api, bestNumberCall); // 1.x
|
||||
};
|
||||
}
|
||||
|
||||
@@ -12,8 +12,10 @@ import { drr } from '../util/drr';
|
||||
import { info } from './info';
|
||||
|
||||
export function sessionProgress (api: ApiInterfaceRx): () => Observable<BlockNumber> {
|
||||
const infoCall = info(api);
|
||||
|
||||
return (): Observable<BlockNumber> =>
|
||||
info(api)().pipe(
|
||||
infoCall().pipe(
|
||||
map(({ sessionProgress }): BlockNumber => sessionProgress),
|
||||
drr()
|
||||
);
|
||||
|
||||
@@ -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);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -17,13 +17,27 @@ function u8aHasValue (value: Uint8Array): boolean {
|
||||
// With isPedantic, actually check that the encoding matches that supplied. This
|
||||
// is much slower, but verifies that we have the correct types defined
|
||||
function checkInstance<T extends Codec = Codec, K extends string = string> (value: Uint8Array, created: FromReg<T, K>): void {
|
||||
// For option, we are not adding the initial byte (this is via storage)
|
||||
const crHex = created.toHex(true);
|
||||
// the underlying type created.toRawType()
|
||||
const rawType = created.toRawType();
|
||||
|
||||
// ignore bytes completely - this is probably a FIXME, since these are somewhat
|
||||
// breaking for at least online queries - not quite sure wtf is going wrong here
|
||||
if (rawType === 'Bytes') {
|
||||
return;
|
||||
}
|
||||
|
||||
// the hex values for what we have
|
||||
const inHex = u8aToHex(value);
|
||||
const crHex = created.toHex();
|
||||
|
||||
// Check equality, based on some different approaches (as decoded)
|
||||
const isEqual = inHex === crHex || // raw hex values, quick path
|
||||
inHex === created.toHex(true) || // wrapped options
|
||||
u8aToHex(value.reverse()) === crHex; // reverse (for numbers, which are BE)
|
||||
|
||||
// if the hex doesn't match and the value for both is non-empty, complain... bitterly
|
||||
if (inHex !== crHex && (u8aHasValue(value) || u8aHasValue(created.toU8a(true)))) {
|
||||
console.warn(`${created.toRawType()}:: Input doesn't match output, received ${inHex}, created ${crHex}`);
|
||||
if (!isEqual && (u8aHasValue(value) || u8aHasValue(created.toU8a(true)))) {
|
||||
console.warn(`${rawType}:: Input doesn't match output, received ${u8aToHex(value)}, created ${crHex}`);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user