From 9ba91a3851e3a89db37150d7b1039ea86166828c Mon Sep 17 00:00:00 2001 From: Jaco Greeff Date: Fri, 10 Apr 2020 12:12:10 +0200 Subject: [PATCH] Use forkJoin for entries() retrieval (#2152) * Use forkJoin for entries() retrieval * Some current and validator retrieval opts * Remove duplicate filtering --- docs/start/types.basics.md | 3 +- .../api-derive/src/collective/proposals.ts | 7 +- packages/api-derive/src/council/votes.ts | 8 +- packages/api-derive/src/council/votesOf.ts | 7 +- .../api-derive/src/staking/erasExposure.ts | 14 ++-- packages/api-derive/src/staking/erasPoints.ts | 34 ++++---- packages/api-derive/src/staking/erasPrefs.ts | 14 ++-- .../api-derive/src/staking/erasRewards.ts | 30 +++---- .../api-derive/src/staking/erasSlashes.ts | 18 ++-- .../api-derive/src/staking/stakerExposure.ts | 17 +++- .../api-derive/src/staking/stakerPoints.ts | 17 +++- .../api-derive/src/staking/stakerRewards.ts | 83 +++++++++++++------ .../api-derive/src/staking/stakerSlashes.ts | 17 +++- packages/api/src/base/Decorate.ts | 47 ++++++----- packages/rpc-core/src/index.ts | 2 +- 15 files changed, 201 insertions(+), 117 deletions(-) diff --git a/docs/start/types.basics.md b/docs/start/types.basics.md index 716b6a0d91..4778af8abf 100644 --- a/docs/start/types.basics.md +++ b/docs/start/types.basics.md @@ -57,7 +57,7 @@ Be aware that in the JS version naming defaults to `camelCase` where names of fi Each enum has additional getters which are injected based on the fields wrapped. These take the form of `.is` and `.as` to allow you to check is the enum is a certain value or to retrieve the underlying value as a specific type. -As a real-world example, when an extrinsic is applied, the `Phase` enum has one of two states, `ApplyExtrinsic(u32)` or `Finalization`. In this case `.isApplyExtrinsic` would be `true` when an extrinsic is being applied, and `.asApplyExtrinsic` would return the value as a `u32` (which is the index of the extrinsic in the block, as it is being applied). When `isisApplyExtrinsic` is `false` and `asApplyExtrinsic` is called, the getter will throw. +As a real-world example, when an extrinsic is applied, the `Phase` enum has one of two states, `ApplyExtrinsic(u32)` or `Finalization`. In this case `.isApplyExtrinsic` would be `true` when an extrinsic is being applied, and `.asApplyExtrinsic` would return the value as a `u32` (which is the index of the extrinsic in the block, as it is being applied). When `isApplyExtrinsic` is `false` and `asApplyExtrinsic` is called, the getter will throw. ## Working with Option<Type> @@ -67,6 +67,7 @@ An `Option` attempts to mimic the Rust approach of having `None` and `Some - `.isSome` - this is `true` is a value is wrapped, i.e. if a `Option` has an actual underlying `u32` - `.unwrap()` - when `isSome`, this will return the wrapped value, i.e. for `Option`, this would return the `u32`. When the value is `isNone`, this call will throw an exception. - `.unwrapOr()` - this extends `unwrap()`, returning the wrapped value when `isSome` and in the case of `isNone` it will return the `` passed. +- `.unwrapOrDefault()` - returns either the rapped value when `isSome`, or the default for the type when `isNone` ## Working with Tuples diff --git a/packages/api-derive/src/collective/proposals.ts b/packages/api-derive/src/collective/proposals.ts index 52c3b78baa..7e2a9acdd0 100644 --- a/packages/api-derive/src/collective/proposals.ts +++ b/packages/api-derive/src/collective/proposals.ts @@ -10,6 +10,8 @@ import { Observable, combineLatest, of } from 'rxjs'; import { map, switchMap } from 'rxjs/operators'; import { Option } from '@polkadot/types'; +import { memo } from '../util'; + type Result = [Hash[], Option[], Option[]]; function parse ([hashes, proposals, votes]: Result): DeriveCollectiveProposal[] { @@ -27,7 +29,7 @@ function parse ([hashes, proposals, votes]: Result): DeriveCollectiveProposal[] } export function proposals (api: ApiInterfaceRx, section: 'council' | 'technicalCommittee'): () => Observable { - return (): Observable => + return memo((): Observable => api.query[section]?.proposals ? api.query[section].proposals().pipe( switchMap((hashes: Hash[]): Observable => @@ -41,5 +43,6 @@ export function proposals (api: ApiInterfaceRx, section: 'council' | 'technicalC ), map(parse) ) - : of([] as DeriveCollectiveProposal[]); + : of([] as DeriveCollectiveProposal[]) + ); } diff --git a/packages/api-derive/src/council/votes.ts b/packages/api-derive/src/council/votes.ts index c28b170d24..c248130fb9 100644 --- a/packages/api-derive/src/council/votes.ts +++ b/packages/api-derive/src/council/votes.ts @@ -11,6 +11,8 @@ import { Observable, combineLatest } from 'rxjs'; import { map } from 'rxjs/operators'; import { Vec } from '@polkadot/types'; +import { memo } from '../util'; + function retrieveStakeOf (api: ApiInterfaceRx): Observable<[AccountId, Balance][]> { const elections = (api.query.electionsPhragmen || api.query.elections); @@ -80,9 +82,9 @@ function retrieveCurrent (api: ApiInterfaceRx): Observable { } export function votes (api: ApiInterfaceRx): () => Observable { - // since we are using entries, we are not adding a memo - return (): Observable => + return memo((): Observable => (api.query.electionsPhragmen || api.query.elections).stakeOf ? retrievePrev(api) - : retrieveCurrent(api); + : retrieveCurrent(api) + ); } diff --git a/packages/api-derive/src/council/votesOf.ts b/packages/api-derive/src/council/votesOf.ts index 981ab042c3..88d6581f02 100644 --- a/packages/api-derive/src/council/votesOf.ts +++ b/packages/api-derive/src/council/votesOf.ts @@ -8,10 +8,10 @@ import { DeriveCouncilVote } from '../types'; import { Observable } from 'rxjs'; import { map } from 'rxjs/operators'; +import { memo } from '../util'; export function votesOf (api: ApiInterfaceRx): (accountId: string | Uint8Array | AccountId) => Observable { - // no memo, votes actually pulls the entries without a memo (using entries) - return (accountId: string | Uint8Array | AccountId): Observable => + return memo((accountId: string | Uint8Array | AccountId): Observable => api.derive.council.votes().pipe( map((votes): DeriveCouncilVote => ( @@ -19,5 +19,6 @@ export function votesOf (api: ApiInterfaceRx): (accountId: string | Uint8Array | [null, { stake: api.registry.createType('Balance'), votes: [] as AccountId[] }] )[1] ) - ); + ) + ); } diff --git a/packages/api-derive/src/staking/erasExposure.ts b/packages/api-derive/src/staking/erasExposure.ts index 8d7173d525..3c459ec5ea 100644 --- a/packages/api-derive/src/staking/erasExposure.ts +++ b/packages/api-derive/src/staking/erasExposure.ts @@ -42,14 +42,18 @@ export function eraExposure (api: ApiInterfaceRx): (era: EraIndex) => Observable ); } +export function erasExposureOver (api: ApiInterfaceRx): (eras: EraIndex[]) => Observable { + return memo((eras: EraIndex[]): Observable => + eras.length + ? combineLatest(eras.map((era) => api.derive.staking.eraExposure(era))) + : of([]) + ); +} + export function erasExposure (api: ApiInterfaceRx): (withActive?: boolean) => Observable { return memo((withActive?: boolean): Observable => api.derive.staking.erasHistoric(withActive).pipe( - switchMap((eras): Observable => - eras.length - ? combineLatest(eras.map((era) => api.derive.staking.eraExposure(era))) - : of([]) - ) + switchMap((eras) => api.derive.staking.erasExposureOver(eras)) ) ); } diff --git a/packages/api-derive/src/staking/erasPoints.ts b/packages/api-derive/src/staking/erasPoints.ts index 0d0d64e05e..c3bdd47905 100644 --- a/packages/api-derive/src/staking/erasPoints.ts +++ b/packages/api-derive/src/staking/erasPoints.ts @@ -6,7 +6,7 @@ import { ApiInterfaceRx } from '@polkadot/api/types'; import { EraIndex, EraRewardPoints } from '@polkadot/types/interfaces'; import { DeriveEraPoints, DeriveEraValPoints } from '../types'; -import { Observable, combineLatest, of } from 'rxjs'; +import { Observable, of } from 'rxjs'; import { map, switchMap } from 'rxjs/operators'; import { memo } from '../util'; @@ -21,24 +21,26 @@ function mapValidators ({ individual }: EraRewardPoints): DeriveEraValPoints { }, {}); } +export function erasPointsOver (api: ApiInterfaceRx): (eras: EraIndex[]) => Observable { + return memo((eras: EraIndex[]): Observable => + eras.length + ? api.query.staking.erasRewardPoints.multi(eras).pipe( + map((points) => + eras.map((era, index): DeriveEraPoints => ({ + era, + eraPoints: points[index].total, + validators: mapValidators(points[index]) + })) + ) + ) + : of([]) + ); +} + export function erasPoints (api: ApiInterfaceRx): (withActive?: boolean) => Observable { return memo((withActive?: boolean): Observable => api.derive.staking.erasHistoric(withActive).pipe( - switchMap((eras): Observable<[EraIndex[], EraRewardPoints[]]> => - combineLatest([ - of(eras), - eras.length - ? api.query.staking.erasRewardPoints.multi(eras) - : of([]) - ]) - ), - map(([eras, points]): DeriveEraPoints[] => - eras.map((era, index): DeriveEraPoints => ({ - era, - eraPoints: points[index].total, - validators: mapValidators(points[index]) - })) - ) + switchMap((eras) => api.derive.staking.erasPointsOver(eras)) ) ); } diff --git a/packages/api-derive/src/staking/erasPrefs.ts b/packages/api-derive/src/staking/erasPrefs.ts index 903a77c7d8..41bbbca2d4 100644 --- a/packages/api-derive/src/staking/erasPrefs.ts +++ b/packages/api-derive/src/staking/erasPrefs.ts @@ -30,14 +30,18 @@ export function eraPrefs (api: ApiInterfaceRx): (era: EraIndex) => Observable Observable { + return memo((eras: EraIndex[]): Observable => + eras.length + ? combineLatest(eras.map((era) => api.derive.staking.eraPrefs(era))) + : of([]) + ); +} + export function erasPrefs (api: ApiInterfaceRx): (withActive?: boolean) => Observable { return memo((withActive?: boolean): Observable => api.derive.staking.erasHistoric(withActive).pipe( - switchMap((eras): Observable => - eras.length - ? combineLatest(eras.map((era) => api.derive.staking.eraPrefs(era))) - : of([]) - ) + switchMap((eras) => api.derive.staking.erasPrefsOver(eras)) ) ); } diff --git a/packages/api-derive/src/staking/erasRewards.ts b/packages/api-derive/src/staking/erasRewards.ts index 4be943aab7..ccf615fbf8 100644 --- a/packages/api-derive/src/staking/erasRewards.ts +++ b/packages/api-derive/src/staking/erasRewards.ts @@ -6,29 +6,29 @@ import { ApiInterfaceRx } from '@polkadot/api/types'; import { Balance, EraIndex } from '@polkadot/types/interfaces'; import { DeriveEraRewards } from '../types'; -import { Observable, combineLatest, of } from 'rxjs'; +import { Observable, of } from 'rxjs'; import { map, switchMap } from 'rxjs/operators'; import { Option } from '@polkadot/types'; import { memo } from '../util'; +export function erasRewardsOver (api: ApiInterfaceRx): (eras: EraIndex[]) => Observable { + return memo((eras: EraIndex[]): Observable => + eras.length + ? api.query.staking.erasValidatorReward.multi>(eras).pipe( + map((rewards) => eras.map((era, index): DeriveEraRewards => ({ + era, + eraReward: rewards[index].unwrapOrDefault() + }))) + ) + : of([]) + ); +} + export function erasRewards (api: ApiInterfaceRx): (withActive?: boolean) => Observable { return memo((withActive?: boolean): 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() - })) - ) + switchMap((eras) => api.derive.staking.erasRewardsOver(eras)) ) ); } diff --git a/packages/api-derive/src/staking/erasSlashes.ts b/packages/api-derive/src/staking/erasSlashes.ts index 053adcf215..0723a431d5 100644 --- a/packages/api-derive/src/staking/erasSlashes.ts +++ b/packages/api-derive/src/staking/erasSlashes.ts @@ -39,16 +39,20 @@ export function eraSlashes (api: ApiInterfaceRx): (era: EraIndex) => Observable< ); } +export function erasSlashesOver (api: ApiInterfaceRx): (eras: EraIndex[]) => Observable { + return memo((eras: EraIndex[]): Observable => + eras.length + ? combineLatest( + eras.map((era) => api.derive.staking.eraSlashes(era)) + ) + : of([]) + ); +} + export function erasSlashes (api: ApiInterfaceRx): (withActive?: boolean) => Observable { return memo((withActive?: boolean): Observable => api.derive.staking.erasHistoric(withActive).pipe( - switchMap((eras): Observable => - eras.length - ? combineLatest( - eras.map((era) => api.derive.staking.eraSlashes(era)) - ) - : of([]) - ) + switchMap((eras) => api.derive.staking.erasSlashesOver(eras)) ) ); } diff --git a/packages/api-derive/src/staking/stakerExposure.ts b/packages/api-derive/src/staking/stakerExposure.ts index 49a246af3e..2b1d04c691 100644 --- a/packages/api-derive/src/staking/stakerExposure.ts +++ b/packages/api-derive/src/staking/stakerExposure.ts @@ -3,18 +3,19 @@ // of the Apache-2.0 license. See the LICENSE file for details. import { ApiInterfaceRx } from '@polkadot/api/types'; +import { EraIndex } from '@polkadot/types/interfaces'; import { DeriveStakerExposure, DeriveEraValidatorExposure } from '../types'; import { Observable } from 'rxjs'; -import { map } from 'rxjs/operators'; +import { map, switchMap } from 'rxjs/operators'; import { memo } from '../util'; -export function stakerExposure (api: ApiInterfaceRx): (accountId: Uint8Array | string, withActive?: boolean) => Observable { - return memo((accountId: Uint8Array | string, withActive?: boolean): Observable => { +export function stakerExposureOver (api: ApiInterfaceRx): (accountId: Uint8Array | string, eras: EraIndex[]) => Observable { + return memo((accountId: Uint8Array | string, eras: EraIndex[]): Observable => { const stakerId = api.registry.createType('AccountId', accountId).toString(); - return api.derive.staking.erasExposure(withActive).pipe( + return api.derive.staking.erasExposureOver(eras).pipe( map((exposures): DeriveStakerExposure[] => exposures.map(({ era, nominators: allNominators, validators: allValidators }): DeriveStakerExposure => { const isValidator = !!allValidators[stakerId]; @@ -35,3 +36,11 @@ export function stakerExposure (api: ApiInterfaceRx): (accountId: Uint8Array | s ); }); } + +export function stakerExposure (api: ApiInterfaceRx): (accountId: Uint8Array | string, withActive?: boolean) => Observable { + return memo((accountId: Uint8Array | string, withActive?: boolean): Observable => + api.derive.staking.erasHistoric(withActive).pipe( + switchMap((eras) => api.derive.staking.stakerExposureOver(accountId, eras)) + ) + ); +} diff --git a/packages/api-derive/src/staking/stakerPoints.ts b/packages/api-derive/src/staking/stakerPoints.ts index fefd0672d5..590b75073b 100644 --- a/packages/api-derive/src/staking/stakerPoints.ts +++ b/packages/api-derive/src/staking/stakerPoints.ts @@ -3,18 +3,19 @@ // of the Apache-2.0 license. See the LICENSE file for details. import { ApiInterfaceRx } from '@polkadot/api/types'; +import { EraIndex } from '@polkadot/types/interfaces'; import { DeriveStakerPoints } from '../types'; import { Observable } from 'rxjs'; -import { map } from 'rxjs/operators'; +import { map, switchMap } from 'rxjs/operators'; import { memo } from '../util'; -export function stakerPoints (api: ApiInterfaceRx): (accountId: Uint8Array | string, withActive?: boolean) => Observable { - return memo((accountId: Uint8Array | string, withActive?: boolean): Observable => { +export function stakerPointsOver (api: ApiInterfaceRx): (accountId: Uint8Array | string, eras: EraIndex[]) => Observable { + return memo((accountId: Uint8Array | string, eras: EraIndex[]): Observable => { const stakerId = api.registry.createType('AccountId', accountId).toString(); - return api.derive.staking.erasPoints(withActive).pipe( + return api.derive.staking.erasPointsOver(eras).pipe( map((points): DeriveStakerPoints[] => points.map(({ era, eraPoints, validators }): DeriveStakerPoints => ({ era, @@ -25,3 +26,11 @@ export function stakerPoints (api: ApiInterfaceRx): (accountId: Uint8Array | str ); }); } + +export function stakerPoints (api: ApiInterfaceRx): (accountId: Uint8Array | string, withActive?: boolean) => Observable { + return memo((accountId: Uint8Array | string, withActive?: boolean): Observable => + api.derive.staking.erasHistoric(withActive).pipe( + switchMap((eras) => api.derive.staking.stakerPointsOver(accountId, eras)) + ) + ); +} diff --git a/packages/api-derive/src/staking/stakerRewards.ts b/packages/api-derive/src/staking/stakerRewards.ts index d35fa56e41..66c8b6f869 100644 --- a/packages/api-derive/src/staking/stakerRewards.ts +++ b/packages/api-derive/src/staking/stakerRewards.ts @@ -3,7 +3,7 @@ // of the Apache-2.0 license. See the LICENSE file for details. import { ApiInterfaceRx } from '@polkadot/api/types'; -import { EraIndex, StakingLedger, StakingLedgerTo240 } from '@polkadot/types/interfaces'; +import { AccountId, EraIndex, StakingLedger, StakingLedgerTo240 } from '@polkadot/types/interfaces'; import { DeriveEraPoints, DeriveEraPrefs, DeriveEraRewards, DeriveEraValPrefs, DeriveStakerExposure, DeriveStakerReward, DeriveStakerRewardValidator } from '../types'; import BN from 'bn.js'; @@ -18,12 +18,13 @@ type Result = [DeriveEraPoints[], DeriveEraPrefs[], DeriveEraRewards[], DeriveSt const ZERO = new BN(0); const COMM_DIV = new BN(1_000_000_000); -function parseRewards (api: ApiInterfaceRx, stakerId: string, [erasPoints, erasPrefs, erasRewards, exposures]: Result): DeriveStakerReward[] { +function parseRewards (api: ApiInterfaceRx, stashId: AccountId, [erasPoints, erasPrefs, erasRewards, exposures]: Result): DeriveStakerReward[] { return exposures.map(({ era, isEmpty, isValidator, nominating, validators: eraValidators }): DeriveStakerReward => { const { eraPoints, validators: allValPoints } = erasPoints.find((p) => p.era.eq(era)) || { eraPoints: ZERO, validators: {} }; const { eraReward } = erasRewards.find((r) => r.era.eq(era)) || { eraReward: api.registry.createType('Balance') }; const { validators: allValPrefs } = erasPrefs.find((p) => p.era.eq(era)) || { validators: {} as DeriveEraValPrefs }; const validators: Record = {}; + const stakerId = stashId.toString(); let total = ZERO; Object.entries(eraValidators).forEach(([validatorId, exposure]): void => { @@ -83,11 +84,11 @@ function uniqValidators (rewards: DeriveStakerReward[]): string[] { return uniq; } -function isOldLedger (ledger: StakingLedger | StakingLedgerTo240): ledger is StakingLedgerTo240 { - return !!(ledger as StakingLedgerTo240).lastReward; +function isOldLedger (ledger?: StakingLedger | StakingLedgerTo240): ledger is StakingLedgerTo240 { + return !!(ledger as StakingLedgerTo240)?.lastReward; } -function filterRewards (api: ApiInterfaceRx, accountId: Uint8Array | string, rewards: DeriveStakerReward[], withActive?: boolean): Observable { +function filterRewards (api: ApiInterfaceRx, rewards: DeriveStakerReward[], withActive?: boolean): Observable { if (withActive) { return of(rewards); } @@ -98,23 +99,19 @@ function filterRewards (api: ApiInterfaceRx, accountId: Uint8Array | string, rew api.query.staking.migrateEra ? api.query.staking.migrateEra>() : of(api.registry.createType('Option', api.tx.staking.payoutStakers ? 0 : 1_000_000_000)), - api.derive.staking.query(accountId), api.tx.staking.payoutStakers ? api.derive.staking.queryMulti(validators) : of([]) ]).pipe( - map(([optMigrate, { stakingLedger }, queryValidators]): DeriveStakerReward[] => { + map(([optMigrate, queryValidators]): DeriveStakerReward[] => { const migrateEra = optMigrate.unwrapOr(new BN(-1)); return rewards .filter(({ isEmpty }) => !isEmpty) .filter((reward): boolean => { if (reward.era.lte(migrateEra)) { - return !stakingLedger - ? true - : isOldLedger(stakingLedger) - ? reward.era.gt(stakingLedger.lastReward.unwrapOr(new BN(-1))) - : !stakingLedger.claimedRewards.some((claimEra) => reward.era.eq(claimEra)); + // we have already filtered these inside the stakerRewardsOver query, so all are valid + return true; } reward.isStakerPayout = true; @@ -148,25 +145,61 @@ function filterRewards (api: ApiInterfaceRx, accountId: Uint8Array | string, rew ); } -export function stakerRewards (api: ApiInterfaceRx): (accountId: Uint8Array | string, withActive?: boolean) => Observable { - return memo((accountId: Uint8Array | string, withActive?: boolean): Observable => { - const stakerId = api.registry.createType('AccountId', accountId).toString(); +function filterEras (eras: EraIndex[], stakingLedger: StakingLedger): EraIndex[] { + if (!stakingLedger) { + return eras; + } else if (isOldLedger(stakingLedger)) { + const lastReward = stakingLedger.lastReward.unwrapOr(new BN(-1)); - return combineLatest([ - api.derive.staking.erasPoints(withActive), - api.derive.staking.erasPrefs(withActive), - api.derive.staking.erasRewards(withActive), - api.derive.staking.stakerExposure(stakerId, withActive) - ]).pipe( - switchMap((result) => filterRewards(api, accountId, parseRewards(api, stakerId, result), withActive)) - ); - }); + return eras.filter((era) => era.gt(lastReward)); + } + + return eras.filter((era) => !stakingLedger.claimedRewards.some((e) => e.eq(era))); +} + +export function stakerRewardsOver (api: ApiInterfaceRx): (accountId: Uint8Array | string, eras: EraIndex[], withActive?: boolean) => Observable { + return memo((accountId: Uint8Array | string, _eras: EraIndex[], withActive?: boolean): Observable => + api.derive.staking.query(accountId).pipe( + switchMap(({ stakingLedger, stashId }): Observable => { + if (!stashId || !stakingLedger) { + return of([]); + } + + const eras = withActive + ? _eras + : filterEras(_eras, stakingLedger); + + return combineLatest([ + api.derive.staking.erasPointsOver(eras), + api.derive.staking.erasPrefsOver(eras), + api.derive.staking.erasRewardsOver(eras), + api.derive.staking.stakerExposureOver(stashId, eras) + ]).pipe( + switchMap((result): Observable => + filterRewards(api, parseRewards(api, stashId, result), withActive) + ) + ); + }) + ) + ); +} + +export function stakerRewards (api: ApiInterfaceRx): (accountId: Uint8Array | string, withActive?: boolean) => Observable { + return memo((accountId: Uint8Array | string, withActive?: boolean): Observable => + api.derive.staking.erasHistoric(withActive).pipe( + switchMap((eras) => api.derive.staking.stakerRewardsOver(accountId, eras, withActive)) + ) + ); } export function stakerRewardsMulti (api: ApiInterfaceRx): (accountIds: (Uint8Array | string)[], withActive?: boolean) => Observable { return memo((accountIds: (Uint8Array | string)[], withActive?: boolean): Observable => accountIds.length - ? combineLatest(accountIds.map((acc) => api.derive.staking.stakerRewards(acc, withActive))) + ? api.derive.staking.erasHistoric(withActive).pipe( + switchMap((eras) => + combineLatest(accountIds.map((acc) => api.derive.staking.stakerRewardsOver(acc, eras, withActive))) + ) + ) : of([]) ); } diff --git a/packages/api-derive/src/staking/stakerSlashes.ts b/packages/api-derive/src/staking/stakerSlashes.ts index 34f90bdcee..1bf0d4a604 100644 --- a/packages/api-derive/src/staking/stakerSlashes.ts +++ b/packages/api-derive/src/staking/stakerSlashes.ts @@ -3,18 +3,19 @@ // of the Apache-2.0 license. See the LICENSE file for details. import { ApiInterfaceRx } from '@polkadot/api/types'; +import { EraIndex } from '@polkadot/types/interfaces'; import { DeriveStakerSlashes } from '../types'; import { Observable } from 'rxjs'; -import { map } from 'rxjs/operators'; +import { map, switchMap } from 'rxjs/operators'; import { memo } from '../util'; -export function stakerSlashes (api: ApiInterfaceRx): (accountId: Uint8Array | string, withActive?: boolean) => Observable { - return memo((accountId: Uint8Array | string, withActive?: boolean): Observable => { +export function stakerSlashesOver (api: ApiInterfaceRx): (accountId: Uint8Array | string, eras: EraIndex[]) => Observable { + return memo((accountId: Uint8Array | string, eras: EraIndex[]): Observable => { const stakerId = api.registry.createType('AccountId', accountId).toString(); - return api.derive.staking.erasSlashes(withActive).pipe( + return api.derive.staking.erasSlashesOver(eras).pipe( map((slashes): DeriveStakerSlashes[] => slashes.map(({ era, nominators, validators }): DeriveStakerSlashes => ({ era, @@ -24,3 +25,11 @@ export function stakerSlashes (api: ApiInterfaceRx): (accountId: Uint8Array | st ); }); } + +export function stakerSlashes (api: ApiInterfaceRx): (accountId: Uint8Array | string, withActive?: boolean) => Observable { + return memo((accountId: Uint8Array | string, withActive?: boolean): Observable => + api.derive.staking.erasHistoric(withActive).pipe( + switchMap((eras) => api.derive.staking.stakerSlashesOver(accountId, eras)) + ) + ); +} diff --git a/packages/api/src/base/Decorate.ts b/packages/api/src/base/Decorate.ts index eda3189a1c..f762f36c57 100644 --- a/packages/api/src/base/Decorate.ts +++ b/packages/api/src/base/Decorate.ts @@ -10,9 +10,10 @@ import { SubmittableExtrinsic } from '../submittable/types'; import { ApiInterfaceRx, ApiOptions, ApiTypes, DecorateMethod, DecoratedRpc, DecoratedRpcSection, QueryableModuleStorage, QueryableStorage, QueryableStorageEntry, QueryableStorageMulti, QueryableStorageMultiArg, SubmittableExtrinsicFunction, SubmittableExtrinsics, SubmittableModuleExtrinsics } from '../types'; import BN from 'bn.js'; -import { BehaviorSubject, Observable, combineLatest, from, of } from 'rxjs'; -import { concatMap, map, switchMap, take, tap, toArray } from 'rxjs/operators'; +import { BehaviorSubject, Observable, forkJoin, combineLatest, of } from 'rxjs'; +import { map, switchMap, take, tap, toArray } from 'rxjs/operators'; import decorateDerive, { ExactDerive } from '@polkadot/api-derive'; +import { memo } from '@polkadot/api-derive/util'; import DecoratedMeta from '@polkadot/metadata/Decorated'; import getHasher from '@polkadot/metadata/Decorated/storage/fromMetadata/getHasher'; import RpcCore from '@polkadot/rpc-core'; @@ -354,11 +355,13 @@ export default abstract class Decorate extends Events // .keys() & .entries() only available on map types if (creator.iterKey && (creator.meta.type.isMap || creator.meta.type.isDoubleMap)) { - decorated.entries = decorateMethod((doubleMapArg?: Arg): Observable<[StorageKey, Codec][]> => - this.retrieveMapEntries(creator, doubleMapArg)); + decorated.entries = decorateMethod( + memo((doubleMapArg?: Arg): Observable<[StorageKey, Codec][]> => + this.retrieveMapEntries(creator, doubleMapArg))); - decorated.keys = decorateMethod((doubleMapArg?: Arg): Observable => - this.retrieveMapKeys(creator, doubleMapArg)); + decorated.keys = decorateMethod( + memo((doubleMapArg?: Arg): Observable => + this.retrieveMapKeys(creator, doubleMapArg))); } // only support multi where subs are available @@ -494,26 +497,26 @@ export default abstract class Decorate extends Events const outputType = unwrapStorageType(entry.meta.type, entry.meta.modifier.isOptional); return this.retrieveMapKeys(entry, arg).pipe( - switchMap((keys): Observable<[StorageKey[], Option[]]> => + switchMap((keys): Observable<[StorageKey[], Option[][]]> => combineLatest([ of(keys), - from(Array(Math.ceil(keys.length / PAGE_SIZE_VALS)).fill(0)).pipe( - concatMap((_, index): Observable[]> => { - const keyset = keys.slice(index * PAGE_SIZE_VALS, (index * PAGE_SIZE_VALS) + PAGE_SIZE_VALS); + forkJoin( + Array(Math.ceil(keys.length / PAGE_SIZE_VALS)) + .fill(0) + .map((_, index): Observable[]> => { + const keyset = keys.slice(index * PAGE_SIZE_VALS, (index * PAGE_SIZE_VALS) + PAGE_SIZE_VALS); - return this._rpcCore.state.queryStorageAt - ? this._rpcCore.state.queryStorageAt[]>(keyset) - : this._rpcCore.state.subscribeStorage[]>(keyset).pipe(take(1)); - }), - toArray(), - map((valsArr: Option[][]): Option[] => - valsArr.reduce((result: Option[], vals) => result.concat(vals), []) - ) + return this._rpcCore.state.queryStorageAt + ? this._rpcCore.state.queryStorageAt[]>(keyset) + : this._rpcCore.state.subscribeStorage[]>(keyset).pipe(take(1)); + }) ) ]) ), - map(([keys, vals]): [StorageKey, Codec][] => - keys.map((key, index): [StorageKey, Codec] => [ + map(([keys, valsArr]): [StorageKey, Codec][] => { + const vals = valsArr.reduce((result: Option[], vals) => result.concat(vals), []); + + return keys.map((key, index): [StorageKey, Codec] => [ key, this.createType( outputType, @@ -521,8 +524,8 @@ export default abstract class Decorate extends Events ? vals[index].unwrap().toHex() : undefined ) - ]) - ) + ]); + }) ); } diff --git a/packages/rpc-core/src/index.ts b/packages/rpc-core/src/index.ts index 5d534585eb..552e40ebff 100644 --- a/packages/rpc-core/src/index.ts +++ b/packages/rpc-core/src/index.ts @@ -188,7 +188,7 @@ export default class Rpc implements RpcInterface { from(this.provider.send(rpcName, params.map((param): AnyJson => param.toJSON()))) ]) ), - map(([params, result]): any => + map(([params, result]) => isRaw ? this.registry.createType('Raw', result) : this.formatOutput(method, def, params, result)