diff --git a/CHANGELOG.md b/CHANGELOG.md index 2c9c06cba7..92a12e7b7a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ - Cleanups to derives for democracy & staking to be more performant - Add Centrifuge base types to `@polkadot/types-known` - Adjust with all the latest substrate master types +- Internally all protected methods/variables are now indicated with `_` (Aligning with the `protected` indicators in the TS definitions) # 1.9.1 Apr 1, 2020 diff --git a/packages/api-derive/src/democracy/_referendumInfo.ts b/packages/api-derive/src/democracy/_referendumInfo.ts deleted file mode 100644 index e7b98e790f..0000000000 --- a/packages/api-derive/src/democracy/_referendumInfo.ts +++ /dev/null @@ -1,32 +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 { ReferendumInfo, ReferendumInfoTo239 } from '@polkadot/types/interfaces'; -import { DeriveReferendum } from '../types'; - -import BN from 'bn.js'; -import { Observable, of } from 'rxjs'; -import { map } from 'rxjs/operators'; -import { Option } from '@polkadot/types'; - -import { memo } from '../util'; -import { getStatus, parseImage } from './util'; - -export function _referendumInfo (api: ApiInterfaceRx): (index: BN, info: Option) => Observable { - return memo((index: BN, info: Option): Observable => { - const status = getStatus(info); - - return status - ? api.query.democracy.preimages(status.proposalHash).pipe( - map((preimage): DeriveReferendum => ({ - image: parseImage(api, preimage), - imageHash: status.proposalHash, - index: api.registry.createType('ReferendumIndex', index), - status - })) - ) - : of(null); - }); -} diff --git a/packages/api-derive/src/democracy/_referendumVotes.ts b/packages/api-derive/src/democracy/_referendumVotes.ts deleted file mode 100644 index 7ccdf8550d..0000000000 --- a/packages/api-derive/src/democracy/_referendumVotes.ts +++ /dev/null @@ -1,84 +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 { AccountId, Vote, Voting, VotingDirectVote } from '@polkadot/types/interfaces'; -import { DeriveBalancesAccount, DeriveReferendum, DeriveReferendumVote, DeriveReferendumVotes } from '../types'; - -import BN from 'bn.js'; -import { Observable, of, combineLatest } from 'rxjs'; -import { map, switchMap } from 'rxjs/operators'; -import { Vec } from '@polkadot/types'; - -import { memo } from '../util'; -import { calcVotes } from './util'; - -function votesPrev (api: ApiInterfaceRx, referendumId: BN): Observable { - return api.query.democracy.votersFor>(referendumId).pipe( - switchMap((votersFor): Observable<[Vec, Vote[], DeriveBalancesAccount[]]> => - combineLatest([ - of(votersFor), - votersFor.length - ? api.query.democracy.voteOf.multi( - votersFor.map((accountId): [BN | number, AccountId] => - [referendumId, accountId] - ) - ) - : of([]), - api.derive.balances.votingBalances(votersFor) - ]) - ), - map(([votersFor, votes, balances]): DeriveReferendumVote[] => - votersFor.map((accountId, index): DeriveReferendumVote => ({ - accountId, - balance: balances[index].votingBalance || api.registry.createType('Balance'), - vote: votes[index] || api.registry.createType('Vote') - })) - ) - ); -} - -function votesCurr (api: ApiInterfaceRx, referendumId: BN): Observable { - return api.query.democracy.votingOf.entries().pipe( - map((allVoting): DeriveReferendumVote[] => - allVoting - .map(([key, voting]): [AccountId, Voting] => [key.args[0] as AccountId, voting]) - // FIXME We are ignoring delegated votes - .filter(([, voting]) => voting.isDirect) - .map(([accountId, voting]): [AccountId, VotingDirectVote[]] => [ - accountId, - voting.asDirect.votes.filter(([idx]) => idx.eq(referendumId)) - ]) - .filter(([, directVotes]) => !!directVotes.length) - .reduce((result: DeriveReferendumVote[], [accountId, votes]) => - // FIXME We are ignoring split votes - votes.reduce((result: DeriveReferendumVote[], [, vote]): DeriveReferendumVote[] => { - if (vote.isStandard) { - result.push({ - accountId, - ...vote.asStandard - }); - } - - return result; - }, result), [] - ) - ) - ); -} - -export function _referendumVotes (api: ApiInterfaceRx): (referendum: DeriveReferendum) => Observable { - return memo((referendum: DeriveReferendum): Observable => - combineLatest([ - api.derive.democracy.sqrtElectorate(), - api.query.democracy.votingOf - ? votesCurr(api, referendum.index) - : votesPrev(api, referendum.index) - ]).pipe( - map(([sqrtElectorate, votes]) => - calcVotes(sqrtElectorate, referendum, votes) - ) - ) - ); -} diff --git a/packages/api-derive/src/democracy/_referendumsVotes.ts b/packages/api-derive/src/democracy/_referendumsVotes.ts deleted file mode 100644 index 112203b2b9..0000000000 --- a/packages/api-derive/src/democracy/_referendumsVotes.ts +++ /dev/null @@ -1,22 +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 { DeriveReferendum, DeriveReferendumVotes } from '../types'; - -import { Observable, of, combineLatest } from 'rxjs'; - -import { memo } from '../util'; - -export function _referendumsVotes (api: ApiInterfaceRx): (referendums: DeriveReferendum[]) => Observable { - return memo((referendums: DeriveReferendum[]): Observable => - referendums.length - ? combineLatest( - referendums.map((referendum): Observable => - api.derive.democracy._referendumVotes(referendum) - ) - ) - : of([]) - ); -} diff --git a/packages/api-derive/src/democracy/index.ts b/packages/api-derive/src/democracy/index.ts index f5524f7c90..fcf55d96a1 100644 --- a/packages/api-derive/src/democracy/index.ts +++ b/packages/api-derive/src/democracy/index.ts @@ -2,9 +2,6 @@ // This software may be modified and distributed under the terms // of the Apache-2.0 license. See the LICENSE file for details. -export * from './_referendumsVotes'; -export * from './_referendumVotes'; -export * from './_referendumInfo'; export * from './dispatchQueue'; export * from './nextExternal'; export * from './preimage'; diff --git a/packages/api-derive/src/democracy/referendumsInfo.ts b/packages/api-derive/src/democracy/referendumsInfo.ts index e7398a0329..0d1c6c6435 100644 --- a/packages/api-derive/src/democracy/referendumsInfo.ts +++ b/packages/api-derive/src/democracy/referendumsInfo.ts @@ -3,15 +3,114 @@ // of the Apache-2.0 license. See the LICENSE file for details. import { ApiInterfaceRx } from '@polkadot/api/types'; -import { ReferendumInfo } from '@polkadot/types/interfaces'; -import { DeriveReferendum } from '../types'; +import { AccountId, ReferendumInfo, ReferendumInfoTo239, Vote, Voting, VotingDirectVote } from '@polkadot/types/interfaces'; +import { DeriveBalancesAccount, DeriveReferendum, DeriveReferendumVote, DeriveReferendumVotes } from '../types'; import BN from 'bn.js'; import { Observable, of, combineLatest } from 'rxjs'; import { map, switchMap } from 'rxjs/operators'; -import { Option } from '@polkadot/types'; +import { Option, Vec } from '@polkadot/types'; import { memo } from '../util'; +import { calcVotes, getStatus, parseImage } from './util'; + +function votesPrev (api: ApiInterfaceRx, referendumId: BN): Observable { + return api.query.democracy.votersFor>(referendumId).pipe( + switchMap((votersFor): Observable<[Vec, Vote[], DeriveBalancesAccount[]]> => + combineLatest([ + of(votersFor), + votersFor.length + ? api.query.democracy.voteOf.multi( + votersFor.map((accountId): [BN | number, AccountId] => + [referendumId, accountId] + ) + ) + : of([]), + api.derive.balances.votingBalances(votersFor) + ]) + ), + map(([votersFor, votes, balances]): DeriveReferendumVote[] => + votersFor.map((accountId, index): DeriveReferendumVote => ({ + accountId, + balance: balances[index].votingBalance || api.registry.createType('Balance'), + vote: votes[index] || api.registry.createType('Vote') + })) + ) + ); +} + +function votesCurr (api: ApiInterfaceRx, referendumId: BN): Observable { + return api.query.democracy.votingOf.entries().pipe( + map((allVoting): DeriveReferendumVote[] => + allVoting + .map(([key, voting]): [AccountId, Voting] => [key.args[0] as AccountId, voting]) + // FIXME We are ignoring delegated votes + .filter(([, voting]) => voting.isDirect) + .map(([accountId, voting]): [AccountId, VotingDirectVote[]] => [ + accountId, + voting.asDirect.votes.filter(([idx]) => idx.eq(referendumId)) + ]) + .filter(([, directVotes]) => !!directVotes.length) + .reduce((result: DeriveReferendumVote[], [accountId, votes]) => + // FIXME We are ignoring split votes + votes.reduce((result: DeriveReferendumVote[], [, vote]): DeriveReferendumVote[] => { + if (vote.isStandard) { + result.push({ + accountId, + ...vote.asStandard + }); + } + + return result; + }, result), [] + ) + ) + ); +} + +export function _referendumVotes (api: ApiInterfaceRx): (referendum: DeriveReferendum) => Observable { + return memo((referendum: DeriveReferendum): Observable => + combineLatest([ + api.derive.democracy.sqrtElectorate(), + api.query.democracy.votingOf + ? votesCurr(api, referendum.index) + : votesPrev(api, referendum.index) + ]).pipe( + map(([sqrtElectorate, votes]) => + calcVotes(sqrtElectorate, referendum, votes) + ) + ) + ); +} + +export function _referendumsVotes (api: ApiInterfaceRx): (referendums: DeriveReferendum[]) => Observable { + return memo((referendums: DeriveReferendum[]): Observable => + referendums.length + ? combineLatest( + referendums.map((referendum): Observable => + api.derive.democracy._referendumVotes(referendum) + ) + ) + : of([]) + ); +} + +export function _referendumInfo (api: ApiInterfaceRx): (index: BN, info: Option) => Observable { + return memo((index: BN, info: Option): Observable => { + const status = getStatus(info); + + return status + ? api.query.democracy.preimages(status.proposalHash).pipe( + map((preimage): DeriveReferendum => ({ + image: parseImage(api, preimage), + imageHash: status.proposalHash, + index: api.registry.createType('ReferendumIndex', index), + status + })) + ) + : of(null); + }); +} export function referendumsInfo (api: ApiInterfaceRx): (ids: BN[]) => Observable { return memo((ids: BN[]): Observable => diff --git a/packages/api-derive/src/staking/erasExposure.ts b/packages/api-derive/src/staking/erasExposure.ts index 3c459ec5ea..88835ff8fb 100644 --- a/packages/api-derive/src/staking/erasExposure.ts +++ b/packages/api-derive/src/staking/erasExposure.ts @@ -42,7 +42,7 @@ export function eraExposure (api: ApiInterfaceRx): (era: EraIndex) => Observable ); } -export function erasExposureOver (api: ApiInterfaceRx): (eras: EraIndex[]) => Observable { +export function _erasExposure (api: ApiInterfaceRx): (eras: EraIndex[]) => Observable { return memo((eras: EraIndex[]): Observable => eras.length ? combineLatest(eras.map((era) => api.derive.staking.eraExposure(era))) @@ -53,7 +53,7 @@ export function erasExposureOver (api: ApiInterfaceRx): (eras: EraIndex[]) => Ob export function erasExposure (api: ApiInterfaceRx): (withActive?: boolean) => Observable { return memo((withActive?: boolean): Observable => api.derive.staking.erasHistoric(withActive).pipe( - switchMap((eras) => api.derive.staking.erasExposureOver(eras)) + switchMap((eras) => api.derive.staking._erasExposure(eras)) ) ); } diff --git a/packages/api-derive/src/staking/erasPoints.ts b/packages/api-derive/src/staking/erasPoints.ts index c3bdd47905..494d78d71d 100644 --- a/packages/api-derive/src/staking/erasPoints.ts +++ b/packages/api-derive/src/staking/erasPoints.ts @@ -21,7 +21,7 @@ function mapValidators ({ individual }: EraRewardPoints): DeriveEraValPoints { }, {}); } -export function erasPointsOver (api: ApiInterfaceRx): (eras: EraIndex[]) => Observable { +export function _erasPoints (api: ApiInterfaceRx): (eras: EraIndex[]) => Observable { return memo((eras: EraIndex[]): Observable => eras.length ? api.query.staking.erasRewardPoints.multi(eras).pipe( @@ -40,7 +40,7 @@ export function erasPointsOver (api: ApiInterfaceRx): (eras: EraIndex[]) => Obse export function erasPoints (api: ApiInterfaceRx): (withActive?: boolean) => Observable { return memo((withActive?: boolean): Observable => api.derive.staking.erasHistoric(withActive).pipe( - switchMap((eras) => api.derive.staking.erasPointsOver(eras)) + switchMap((eras) => api.derive.staking._erasPoints(eras)) ) ); } diff --git a/packages/api-derive/src/staking/erasPrefs.ts b/packages/api-derive/src/staking/erasPrefs.ts index 41bbbca2d4..8471b05de6 100644 --- a/packages/api-derive/src/staking/erasPrefs.ts +++ b/packages/api-derive/src/staking/erasPrefs.ts @@ -30,7 +30,7 @@ export function eraPrefs (api: ApiInterfaceRx): (era: EraIndex) => Observable Observable { +export function _erasPrefs (api: ApiInterfaceRx): (eras: EraIndex[]) => Observable { return memo((eras: EraIndex[]): Observable => eras.length ? combineLatest(eras.map((era) => api.derive.staking.eraPrefs(era))) @@ -41,7 +41,7 @@ export function erasPrefsOver (api: ApiInterfaceRx): (eras: EraIndex[]) => Obser export function erasPrefs (api: ApiInterfaceRx): (withActive?: boolean) => Observable { return memo((withActive?: boolean): Observable => api.derive.staking.erasHistoric(withActive).pipe( - switchMap((eras) => api.derive.staking.erasPrefsOver(eras)) + switchMap((eras) => api.derive.staking._erasPrefs(eras)) ) ); } diff --git a/packages/api-derive/src/staking/erasRewards.ts b/packages/api-derive/src/staking/erasRewards.ts index ccf615fbf8..5f50320a37 100644 --- a/packages/api-derive/src/staking/erasRewards.ts +++ b/packages/api-derive/src/staking/erasRewards.ts @@ -12,7 +12,7 @@ import { Option } from '@polkadot/types'; import { memo } from '../util'; -export function erasRewardsOver (api: ApiInterfaceRx): (eras: EraIndex[]) => Observable { +export function _erasRewards (api: ApiInterfaceRx): (eras: EraIndex[]) => Observable { return memo((eras: EraIndex[]): Observable => eras.length ? api.query.staking.erasValidatorReward.multi>(eras).pipe( @@ -28,7 +28,7 @@ export function erasRewardsOver (api: ApiInterfaceRx): (eras: EraIndex[]) => Obs export function erasRewards (api: ApiInterfaceRx): (withActive?: boolean) => Observable { return memo((withActive?: boolean): Observable => api.derive.staking.erasHistoric(withActive).pipe( - switchMap((eras) => api.derive.staking.erasRewardsOver(eras)) + switchMap((eras) => api.derive.staking._erasRewards(eras)) ) ); } diff --git a/packages/api-derive/src/staking/erasSlashes.ts b/packages/api-derive/src/staking/erasSlashes.ts index 0723a431d5..e8ec396c5f 100644 --- a/packages/api-derive/src/staking/erasSlashes.ts +++ b/packages/api-derive/src/staking/erasSlashes.ts @@ -39,7 +39,7 @@ export function eraSlashes (api: ApiInterfaceRx): (era: EraIndex) => Observable< ); } -export function erasSlashesOver (api: ApiInterfaceRx): (eras: EraIndex[]) => Observable { +export function _erasSlashes (api: ApiInterfaceRx): (eras: EraIndex[]) => Observable { return memo((eras: EraIndex[]): Observable => eras.length ? combineLatest( @@ -52,7 +52,7 @@ export function erasSlashesOver (api: ApiInterfaceRx): (eras: EraIndex[]) => Obs export function erasSlashes (api: ApiInterfaceRx): (withActive?: boolean) => Observable { return memo((withActive?: boolean): Observable => api.derive.staking.erasHistoric(withActive).pipe( - switchMap((eras) => api.derive.staking.erasSlashesOver(eras)) + switchMap((eras) => api.derive.staking._erasSlashes(eras)) ) ); } diff --git a/packages/api-derive/src/staking/query.ts b/packages/api-derive/src/staking/query.ts index 8854068aee..927f726718 100644 --- a/packages/api-derive/src/staking/query.ts +++ b/packages/api-derive/src/staking/query.ts @@ -55,7 +55,7 @@ function retrieveCurr (api: ApiInterfaceRx, stashId: AccountId): Observable>, MultiResult]): Observable { +function retrieveController (api: ApiInterfaceRx, stashId: AccountId, queuedKeys: Vec>, [controllerIdOpt, nominatorsOpt, rewardDestination, validatorPrefs, nextKeys, exposure]: MultiResult): Observable { const controllerId = controllerIdOpt.unwrapOr(null); const nominators = nominatorsOpt.unwrapOr(null); @@ -82,30 +82,41 @@ function retrieveController (api: ApiInterfaceRx, stashId: AccountId, [queuedKey : of({ accountId: stashId, nextSessionIds: [], sessionIds: [] }); } -/** - * @description From a stash, retrieve the controllerId and all relevant details - */ -export function query (api: ApiInterfaceRx): (accountId: Uint8Array | string) => Observable { - return memo((accountId: Uint8Array | string): Observable => { +export function queryWithQueued (api: ApiInterfaceRx): (accountId: Uint8Array | string, queuedKeys: Vec>) => Observable { + return memo((accountId: Uint8Array | string, queuedKeys: Vec>): Observable => { const stashId = api.registry.createType('AccountId', accountId); - return combineLatest([ - api.query.session.queuedKeys>>(), + return ( api.query.staking.erasStakers ? retrieveCurr(api, stashId) : retrievePrev(api, stashId) - ]).pipe( + ).pipe( switchMap((result): Observable => - retrieveController(api, stashId, result) + retrieveController(api, stashId, queuedKeys, result) ) ); }); } +/** + * @description From a stash, retrieve the controllerId and all relevant details + */ +export function query (api: ApiInterfaceRx): (accountId: Uint8Array | string) => Observable { + return memo((accountId: Uint8Array | string): Observable => + api.query.session.queuedKeys>>().pipe( + switchMap((queuedKeys) => api.derive.staking.queryWithQueued(accountId, queuedKeys)) + ) + ); +} + export function queryMulti (api: ApiInterfaceRx): (accountIds: (Uint8Array | string)[]) => Observable { return memo((accountIds: (Uint8Array | string)[]): Observable => accountIds.length - ? combineLatest(accountIds.map((acc) => api.derive.staking.query(acc))) + ? api.query.session.queuedKeys>>().pipe( + switchMap((queuedKeys) => combineLatest( + accountIds.map((acc) => api.derive.staking.queryWithQueued(acc, queuedKeys)) + )) + ) : of([]) ); } diff --git a/packages/api-derive/src/staking/stakerExposure.ts b/packages/api-derive/src/staking/stakerExposure.ts index 2b1d04c691..a5865b4cd2 100644 --- a/packages/api-derive/src/staking/stakerExposure.ts +++ b/packages/api-derive/src/staking/stakerExposure.ts @@ -11,11 +11,11 @@ import { map, switchMap } from 'rxjs/operators'; import { memo } from '../util'; -export function stakerExposureOver (api: ApiInterfaceRx): (accountId: Uint8Array | string, eras: EraIndex[]) => Observable { +export function _stakerExposure (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.erasExposureOver(eras).pipe( + return api.derive.staking._erasExposure(eras).pipe( map((exposures): DeriveStakerExposure[] => exposures.map(({ era, nominators: allNominators, validators: allValidators }): DeriveStakerExposure => { const isValidator = !!allValidators[stakerId]; @@ -40,7 +40,7 @@ export function stakerExposureOver (api: ApiInterfaceRx): (accountId: Uint8Array 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)) + switchMap((eras) => api.derive.staking._stakerExposure(accountId, eras)) ) ); } diff --git a/packages/api-derive/src/staking/stakerPoints.ts b/packages/api-derive/src/staking/stakerPoints.ts index 590b75073b..38972e9d56 100644 --- a/packages/api-derive/src/staking/stakerPoints.ts +++ b/packages/api-derive/src/staking/stakerPoints.ts @@ -11,11 +11,11 @@ import { map, switchMap } from 'rxjs/operators'; import { memo } from '../util'; -export function stakerPointsOver (api: ApiInterfaceRx): (accountId: Uint8Array | string, eras: EraIndex[]) => Observable { +export function _stakerPoints (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.erasPointsOver(eras).pipe( + return api.derive.staking._erasPoints(eras).pipe( map((points): DeriveStakerPoints[] => points.map(({ era, eraPoints, validators }): DeriveStakerPoints => ({ era, @@ -30,7 +30,7 @@ export function stakerPointsOver (api: ApiInterfaceRx): (accountId: Uint8Array | 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)) + switchMap((eras) => api.derive.staking._stakerPoints(accountId, eras)) ) ); } diff --git a/packages/api-derive/src/staking/stakerRewards.ts b/packages/api-derive/src/staking/stakerRewards.ts index 66c8b6f869..f4e6af2c8a 100644 --- a/packages/api-derive/src/staking/stakerRewards.ts +++ b/packages/api-derive/src/staking/stakerRewards.ts @@ -16,7 +16,9 @@ import { memo } from '../util'; type Result = [DeriveEraPoints[], DeriveEraPrefs[], DeriveEraRewards[], DeriveStakerExposure[]]; const ZERO = new BN(0); +const MIN_ONE = new BN(-1); const COMM_DIV = new BN(1_000_000_000); +const MAX_ERAS = new BN(1_000_000_000); function parseRewards (api: ApiInterfaceRx, stashId: AccountId, [erasPoints, erasPrefs, erasRewards, exposures]: Result): DeriveStakerReward[] { return exposures.map(({ era, isEmpty, isValidator, nominating, validators: eraValidators }): DeriveStakerReward => { @@ -25,7 +27,6 @@ function parseRewards (api: ApiInterfaceRx, stashId: AccountId, [erasPoints, era 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 => { const valPoints = allValPoints[validatorId] || ZERO; @@ -49,7 +50,6 @@ function parseRewards (api: ApiInterfaceRx, stashId: AccountId, [erasPoints, era } value = avail.sub(valCut).mul(staked).div(expTotal).add(validatorId === stakerId ? valCut : ZERO); - total = total.add(value); } validators[validatorId] = { @@ -64,7 +64,6 @@ function parseRewards (api: ApiInterfaceRx, stashId: AccountId, [erasPoints, era isEmpty, isValidator, nominating, - total: api.registry.createType('Balance', total), validators }; }); @@ -88,7 +87,17 @@ function isOldLedger (ledger?: StakingLedger | StakingLedgerTo240): ledger is St return !!(ledger as StakingLedgerTo240)?.lastReward; } -function filterRewards (api: ApiInterfaceRx, rewards: DeriveStakerReward[], withActive?: boolean): Observable { +function filterEra (era: EraIndex, stakingLedger: StakingLedger): boolean { + return isOldLedger(stakingLedger) + ? era.gt(stakingLedger.lastReward.unwrapOr(MIN_ONE)) + : !stakingLedger.claimedRewards.some((e) => e.eq(era)); +} + +function filterEras (eras: EraIndex[], stakingLedger: StakingLedger): EraIndex[] { + return eras.filter((era) => filterEra(era, stakingLedger)); +} + +function filterRewards (api: ApiInterfaceRx, rewards: DeriveStakerReward[], stakingLedger: StakingLedger, withActive?: boolean): Observable { if (withActive) { return of(rewards); } @@ -98,20 +107,20 @@ function filterRewards (api: ApiInterfaceRx, rewards: DeriveStakerReward[], with return combineLatest([ api.query.staking.migrateEra ? api.query.staking.migrateEra>() - : of(api.registry.createType('Option', api.tx.staking.payoutStakers ? 0 : 1_000_000_000)), + : of({ unwrapOr: () => api.tx.staking.payoutStakers ? MIN_ONE : MAX_ERAS }), api.tx.staking.payoutStakers ? api.derive.staking.queryMulti(validators) : of([]) ]).pipe( map(([optMigrate, queryValidators]): DeriveStakerReward[] => { - const migrateEra = optMigrate.unwrapOr(new BN(-1)); + const migrateEra: BN = optMigrate.unwrapOr(MIN_ONE); return rewards .filter(({ isEmpty }) => !isEmpty) .filter((reward): boolean => { if (reward.era.lte(migrateEra)) { - // we have already filtered these inside the stakerRewardsOver query, so all are valid - return true; + // we filter again here, the actual ledger may have changed, e.g. something has been claimed + return filterEra(reward.era, stakingLedger); } reward.isStakerPayout = true; @@ -145,19 +154,7 @@ function filterRewards (api: ApiInterfaceRx, rewards: DeriveStakerReward[], with ); } -function filterEras (eras: EraIndex[], stakingLedger: StakingLedger): EraIndex[] { - if (!stakingLedger) { - return eras; - } else if (isOldLedger(stakingLedger)) { - const lastReward = stakingLedger.lastReward.unwrapOr(new BN(-1)); - - 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 { +export function _stakerRewards (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 => { @@ -170,13 +167,13 @@ export function stakerRewardsOver (api: ApiInterfaceRx): (accountId: Uint8Array : 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) + api.derive.staking._erasPoints(eras), + api.derive.staking._erasPrefs(eras), + api.derive.staking._erasRewards(eras), + api.derive.staking._stakerExposure(stashId, eras) ]).pipe( switchMap((result): Observable => - filterRewards(api, parseRewards(api, stashId, result), withActive) + filterRewards(api, parseRewards(api, stashId, result), stakingLedger, withActive) ) ); }) @@ -187,7 +184,7 @@ export function stakerRewardsOver (api: ApiInterfaceRx): (accountId: Uint8Array 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)) + switchMap((eras) => api.derive.staking._stakerRewards(accountId, eras, withActive)) ) ); } @@ -197,7 +194,7 @@ export function stakerRewardsMulti (api: ApiInterfaceRx): (accountIds: (Uint8Arr accountIds.length ? api.derive.staking.erasHistoric(withActive).pipe( switchMap((eras) => - combineLatest(accountIds.map((acc) => api.derive.staking.stakerRewardsOver(acc, eras, withActive))) + combineLatest(accountIds.map((acc) => api.derive.staking._stakerRewards(acc, eras, withActive))) ) ) : of([]) diff --git a/packages/api-derive/src/staking/stakerSlashes.ts b/packages/api-derive/src/staking/stakerSlashes.ts index 1bf0d4a604..657624daf3 100644 --- a/packages/api-derive/src/staking/stakerSlashes.ts +++ b/packages/api-derive/src/staking/stakerSlashes.ts @@ -11,11 +11,11 @@ import { map, switchMap } from 'rxjs/operators'; import { memo } from '../util'; -export function stakerSlashesOver (api: ApiInterfaceRx): (accountId: Uint8Array | string, eras: EraIndex[]) => Observable { +export function _stakerSlashes (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.erasSlashesOver(eras).pipe( + return api.derive.staking._erasSlashes(eras).pipe( map((slashes): DeriveStakerSlashes[] => slashes.map(({ era, nominators, validators }): DeriveStakerSlashes => ({ era, @@ -29,7 +29,7 @@ export function stakerSlashesOver (api: ApiInterfaceRx): (accountId: Uint8Array 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)) + switchMap((eras) => api.derive.staking._stakerSlashes(accountId, eras)) ) ); } diff --git a/packages/api-derive/src/staking/types.ts b/packages/api-derive/src/staking/types.ts index df1bbb24da..1edb22e619 100644 --- a/packages/api-derive/src/staking/types.ts +++ b/packages/api-derive/src/staking/types.ts @@ -84,7 +84,6 @@ export interface DeriveStakerReward { isEmpty: boolean; isValidator: boolean; nominating: DeriveEraExposureNominating[]; - total: Balance; validators: Record; }