diff --git a/packages/api-derive/src/staking/erasExposure.ts b/packages/api-derive/src/staking/erasExposure.ts index 31144180ee..4df9a2d0da 100644 --- a/packages/api-derive/src/staking/erasExposure.ts +++ b/packages/api-derive/src/staking/erasExposure.ts @@ -41,20 +41,22 @@ function mapStakers (stakers: [StorageKey, Exposure][]): Mapped { export function erasExposure (api: ApiInterfaceRx): (withActive?: boolean | BN) => Observable { return memo((withActive?: boolean | BN): Observable => api.derive.staking.erasHistoric(withActive).pipe( - switchMap((allEras): Observable<[EraIndex[], [StorageKey, Exposure][][]]> => + switchMap((eras): Observable<[EraIndex[], [StorageKey, Exposure][][]]> => combineLatest([ - of(allEras), - combineLatest( - // we could just do entries over the full set, however the set can be quite large - split it into - // batches - may need to re-visit this, or alternatively use pages keys for exceptionally large sets - allEras.map((era): Observable<[StorageKey, Exposure][]> => - api.query.staking.erasStakers.entries(era).pipe(take(1)) + of(eras), + eras.length + ? combineLatest( + // we could just do entries over the full set, however the set can be quite large - split it into + // batches - may need to re-visit this, or alternatively use pages keys for exceptionally large sets + eras.map((era): Observable<[StorageKey, Exposure][]> => + api.query.staking.erasStakers.entries(era).pipe(take(1)) + ) ) - ) + : of([]) ]) ), - map(([allEras, erasStakers]): DeriveEraExposure[] => - allEras.map((era, index): DeriveEraExposure => ({ + map(([eras, erasStakers]): DeriveEraExposure[] => + eras.map((era, index): DeriveEraExposure => ({ era, ...mapStakers(erasStakers[index]) })) diff --git a/packages/api-derive/src/staking/erasHistoric.ts b/packages/api-derive/src/staking/erasHistoric.ts index dce285fbb8..fa759fa479 100644 --- a/packages/api-derive/src/staking/erasHistoric.ts +++ b/packages/api-derive/src/staking/erasHistoric.ts @@ -21,22 +21,29 @@ export function erasHistoric (api: ApiInterfaceRx): (withActive?: boolean | BN) api.query.staking.historyDepth ]).pipe( map(([activeEraOpt, historyDepth]): EraIndex[] => { - const result: EraIndex[] = []; + const result: (EraIndex | null)[] = []; const max = historyDepth.toNumber(); - let lastEra = activeEraOpt.unwrapOrDefault().index.subn(withActive === true ? 0 : 1); + const activeEra = activeEraOpt.unwrapOrDefault().index.toBn(); + let lastEra = activeEra; - while (lastEra.gten(0) && result.length < max) { - result.push(api.registry.createType('EraIndex', lastEra)); + while (lastEra.gten(0) && (result.length < max)) { + result.push( + ((lastEra !== activeEra) || (withActive === true)) + ? api.registry.createType('EraIndex', lastEra) + : null + ); lastEra = lastEra.subn(1); } // go from oldest to newest return result - .filter((era): boolean => - isUndefined(withActive) || isBoolean(withActive) - ? true - : era.gte(withActive) + .filter((era): era is EraIndex => + era + ? isUndefined(withActive) || isBoolean(withActive) + ? true + : era.gte(withActive) + : false ) .reverse(); }) diff --git a/packages/api-derive/src/staking/erasPoints.ts b/packages/api-derive/src/staking/erasPoints.ts index 37e5d9254f..721c2f1a3f 100644 --- a/packages/api-derive/src/staking/erasPoints.ts +++ b/packages/api-derive/src/staking/erasPoints.ts @@ -6,8 +6,9 @@ import { ApiInterfaceRx } from '@polkadot/api/types'; import { EraIndex, EraRewardPoints } from '@polkadot/types/interfaces'; import { DeriveEraPoints, DeriveEraValPoints } from '../types'; +import BN from 'bn.js'; import { Observable, combineLatest, of } from 'rxjs'; -import { map, switchMap, take } from 'rxjs/operators'; +import { map, switchMap } from 'rxjs/operators'; import { memo } from '../util'; @@ -21,14 +22,14 @@ function mapValidators ({ individual }: EraRewardPoints): DeriveEraValPoints { }, {}); } -export function erasPoints (api: ApiInterfaceRx): (withActive?: boolean) => Observable { +export function erasPoints (api: ApiInterfaceRx): (withActive?: boolean | BN) => Observable { return memo((withActive?: boolean): Observable => api.derive.staking.erasHistoric(withActive).pipe( - switchMap((indexes): Observable<[EraIndex[], EraRewardPoints[]]> => + switchMap((eras): Observable<[EraIndex[], EraRewardPoints[]]> => combineLatest([ - of(indexes), - indexes.length - ? api.query.staking.erasRewardPoints.multi(indexes).pipe(take(1)) + of(eras), + eras.length + ? api.query.staking.erasRewardPoints.multi(eras) : of([]) ]) ), diff --git a/packages/api-derive/src/staking/erasRewards.ts b/packages/api-derive/src/staking/erasRewards.ts new file mode 100644 index 0000000000..e89d34e207 --- /dev/null +++ b/packages/api-derive/src/staking/erasRewards.ts @@ -0,0 +1,35 @@ +// Copyright 2017-2020 @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 { ApiInterfaceRx } from '@polkadot/api/types'; +import { Balance, EraIndex } from '@polkadot/types/interfaces'; +import { DeriveEraRewards } from '../types'; + +import BN from 'bn.js'; +import { Observable, combineLatest, of } from 'rxjs'; +import { map, switchMap } from 'rxjs/operators'; +import { Option } from '@polkadot/types'; + +import { memo } from '../util'; + +export function erasRewards (api: ApiInterfaceRx): (withActive?: boolean | BN) => Observable { + return memo((withActive?: boolean | BN): Observable => + api.derive.staking.erasHistoric(withActive).pipe( + switchMap((eras): Observable<[EraIndex[], Option[]]> => + combineLatest([ + of(eras), + eras.length + ? api.query.staking.erasValidatorReward.multi>(eras) + : of([]) + ]) + ), + map(([eras, rewards]): DeriveEraRewards[] => + eras.map((era, index): DeriveEraRewards => ({ + era, + eraReward: rewards[index].unwrapOrDefault() + })) + ) + ) + ); +} diff --git a/packages/api-derive/src/staking/erasRewards.ts-unused b/packages/api-derive/src/staking/erasRewards.ts-unused deleted file mode 100644 index 5dc79f68a9..0000000000 --- a/packages/api-derive/src/staking/erasRewards.ts-unused +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright 2017-2020 @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 { ApiInterfaceRx } from '@polkadot/api/types'; -import { Exposure } from '@polkadot/types/interfaces'; -import { DeriveEraRewardsAll } from '../types'; - -import { Observable } from 'rxjs'; -import { map } from 'rxjs/operators'; - -import { memo } from '../util'; - -function includeExposure (validatorId: string, { others }: Exposure, nominators: Record): void { - others.forEach(({ who }, index): void => { - const nominatorId = who.toString(); - - nominators[nominatorId] = nominators[nominatorId] || []; - nominators[nominatorId].push([validatorId, index]); - }); -} - -// , -// indexes.length -// ? api.query.staking.erasValidatorReward.multi>(indexes).pipe(take(1)) -// : of([]) - -export function erasRewards (api: ApiInterfaceRx): (withActive?: boolean) => Observable { - return memo((withActive?: boolean): Observable => - api.derive.staking.erasExposure(withActive).pipe( - map((exposures): DeriveEraRewardsAll[] => - exposures.map(({ era, eraPoints, eraReward, validators }): DeriveEraRewardsAll => - Object - .entries(validators) - .reduce((rewards: DeriveEraRewardsAll, [validatorId, { exposure }]): DeriveEraRewardsAll => { - includeExposure(validatorId, exposure, rewards.nominators); - - return rewards; - }, { era, eraPoints, eraReward, nominators: {}, validators }) - ) - ) - ) - ); -} diff --git a/packages/api-derive/src/staking/index.ts b/packages/api-derive/src/staking/index.ts index d57c490448..4272ef938d 100644 --- a/packages/api-derive/src/staking/index.ts +++ b/packages/api-derive/src/staking/index.ts @@ -6,10 +6,11 @@ export * from './account'; export * from './erasExposure'; export * from './erasHistoric'; export * from './erasPoints'; -// export * from './erasRewards'; +export * from './erasRewards'; export * from './controllers'; export * from './electedInfo'; export * from './overview'; export * from './query'; export * from './stakerExposure'; +export * from './stakerPoints'; export * from './validators'; diff --git a/packages/api-derive/src/staking/stakerPoints.ts b/packages/api-derive/src/staking/stakerPoints.ts new file mode 100644 index 0000000000..3386d473eb --- /dev/null +++ b/packages/api-derive/src/staking/stakerPoints.ts @@ -0,0 +1,30 @@ +// Copyright 2017-2020 @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 { ApiInterfaceRx } from '@polkadot/api/types'; +import { DeriveStakerPoints } from '../types'; + +import BN from 'bn.js'; +import { Observable } from 'rxjs'; +import { map } from 'rxjs/operators'; + +import { memo } from '../util'; + +export function stakerPoints (api: ApiInterfaceRx): (accountId: Uint8Array | string, withActive?: boolean | BN) => Observable { + return memo((accountId: Uint8Array | string, withActive?: boolean | BN): Observable => { + const stakerId = api.registry.createType('AccountId', accountId).toString(); + + return api.derive.staking.erasPoints(withActive).pipe( + map((points): DeriveStakerPoints[] => + points.map(({ era, eraPoints, validators }): DeriveStakerPoints => ({ + era, + eraPoints, + points: validators[stakerId] + ? validators[stakerId] + : api.registry.createType('RewardPoint') + })) + ) + ); + }); +} diff --git a/packages/api-derive/src/staking/types.ts b/packages/api-derive/src/staking/types.ts index d14426eefc..e91fe27a3b 100644 --- a/packages/api-derive/src/staking/types.ts +++ b/packages/api-derive/src/staking/types.ts @@ -13,10 +13,15 @@ export interface DeriveEraPoints { validators: DeriveEraValPoints; } -export interface DeriveStakersPoints { +export interface DeriveEraRewards { + era: EraIndex; + eraReward: Balance; +} + +export interface DeriveStakerPoints { era: EraIndex; eraPoints: RewardPoint; - own: RewardPoint; + points: RewardPoint; } export type DeriveEraNominatorExposure = Record;