Skip to content

Commit

Permalink
Add claimedRewardsEra to api.derive.staking.query for compatibility w…
Browse files Browse the repository at this point in the history
…ith legacyClaimedRewards (#5862)

* Fix ClaimedRewards breaking change

* fix some naming

* remove consoles

* Fix logic

* fix output type

* Fix StakerRewards

* Fix electedInfo
  • Loading branch information
TarikGul committed Apr 23, 2024
1 parent 6e4dc0b commit b4ebdc2
Show file tree
Hide file tree
Showing 4 changed files with 67 additions and 24 deletions.
6 changes: 3 additions & 3 deletions packages/api-derive/src/staking/electedInfo.ts
Expand Up @@ -17,11 +17,11 @@ function combineAccounts (nextElected: AccountId[], validators: AccountId[]): Ac
return arrayFlatten([nextElected, validators.filter((v) => !nextElected.find((n) => n.eq(v)))]);
}

export function electedInfo (instanceId: string, api: DeriveApi): (flags?: StakingQueryFlags) => Observable<DeriveStakingElected> {
return memo(instanceId, (flags: StakingQueryFlags = DEFAULT_FLAGS): Observable<DeriveStakingElected> =>
export function electedInfo (instanceId: string, api: DeriveApi): (flags?: StakingQueryFlags, page?: number) => Observable<DeriveStakingElected> {
return memo(instanceId, (flags: StakingQueryFlags = DEFAULT_FLAGS, page = 0): Observable<DeriveStakingElected> =>
api.derive.staking.validators().pipe(
switchMap(({ nextElected, validators }): Observable<DeriveStakingElected> =>
api.derive.staking.queryMulti(combineAccounts(nextElected, validators), flags).pipe(
api.derive.staking.queryMulti(combineAccounts(nextElected, validators), flags, page).pipe(
map((info): DeriveStakingElected => ({
info,
nextElected,
Expand Down
50 changes: 44 additions & 6 deletions packages/api-derive/src/staking/query.ts
Expand Up @@ -2,7 +2,7 @@
// SPDX-License-Identifier: Apache-2.0

import type { Observable } from 'rxjs';
import type { Option, u32 } from '@polkadot/types';
import type { Option, u32, Vec } from '@polkadot/types';
import type { AccountId, EraIndex } from '@polkadot/types/interfaces';
import type { PalletStakingNominations, PalletStakingRewardDestination, PalletStakingStakingLedger, PalletStakingValidatorPrefs, SpStakingExposurePage, SpStakingPagedExposureMetadata } from '@polkadot/types/lookup';
import type { AnyNumber } from '@polkadot/types-codec/types';
Expand All @@ -20,9 +20,14 @@ function rewardDestinationCompat (rewardDestination: PalletStakingRewardDestinat
: (rewardDestination as PalletStakingRewardDestination);
}

function parseDetails (stashId: AccountId, controllerIdOpt: Option<AccountId> | null, nominatorsOpt: Option<PalletStakingNominations>, rewardDestinationOpts: Option<PalletStakingRewardDestination> | PalletStakingRewardDestination, validatorPrefs: PalletStakingValidatorPrefs, exposure: Option<SpStakingExposurePage>, stakingLedgerOpt: Option<PalletStakingStakingLedger>, exposureMeta: Option<SpStakingPagedExposureMetadata>): DeriveStakingQuery {
function filterClaimedRewards (api: DeriveApi, cl: number[]): Vec<u32> {
return api.registry.createType('Vec<u32>', cl.filter((c) => c !== -1));
}

function parseDetails (api: DeriveApi, stashId: AccountId, controllerIdOpt: Option<AccountId> | null, nominatorsOpt: Option<PalletStakingNominations>, rewardDestinationOpts: Option<PalletStakingRewardDestination> | PalletStakingRewardDestination, validatorPrefs: PalletStakingValidatorPrefs, exposure: Option<SpStakingExposurePage>, stakingLedgerOpt: Option<PalletStakingStakingLedger>, exposureMeta: Option<SpStakingPagedExposureMetadata>, claimedRewards: number[]): DeriveStakingQuery {
return {
accountId: stashId,
claimedRewardsEras: filterClaimedRewards(api, claimedRewards),
controllerId: controllerIdOpt?.unwrapOr(null) || null,
exposureMeta,
exposurePaged: exposure,
Expand Down Expand Up @@ -59,12 +64,22 @@ function getLedgers (api: DeriveApi, optIds: (Option<AccountId> | null)[], { wit
);
}

function getStashInfo (api: DeriveApi, stashIds: AccountId[], activeEra: EraIndex, { withController, withDestination, withExposure, withExposureMeta, withLedger, withNominations, withPrefs }: StakingQueryFlags, page: u32 | AnyNumber): Observable<[(Option<AccountId> | null)[], Option<PalletStakingNominations>[], Option<PalletStakingRewardDestination>[], PalletStakingValidatorPrefs[], Option<SpStakingExposurePage>[], Option<SpStakingPagedExposureMetadata>[]]> {
function getStashInfo (api: DeriveApi, stashIds: AccountId[], activeEra: EraIndex, { withClaimedRewardsEras, withController, withDestination, withExposure, withExposureMeta, withLedger, withNominations, withPrefs }: StakingQueryFlags, page: u32 | AnyNumber): Observable<[(Option<AccountId> | null)[], Option<PalletStakingNominations>[], Option<PalletStakingRewardDestination>[], PalletStakingValidatorPrefs[], Option<SpStakingExposurePage>[], Option<SpStakingPagedExposureMetadata>[], number[][]]> {
const emptyNoms = api.registry.createType<Option<PalletStakingNominations>>('Option<Nominations>');
const emptyRewa = api.registry.createType<Option<PalletStakingRewardDestination>>('RewardDestination');
const emptyExpo = api.registry.createType<Option<SpStakingExposurePage>>('Option<SpStakingExposurePage>');
const emptyPrefs = api.registry.createType<PalletStakingValidatorPrefs>('ValidatorPrefs');
const emptyExpoMeta = api.registry.createType<Option<SpStakingPagedExposureMetadata>>('Option<SpStakingPagedExposureMetadata>');
const emptyClaimedRewards = [-1];

const depth = Number(api.consts.staking.historyDepth.toNumber());
const eras = new Array(depth).fill(0).map((_, idx) => {
if (idx === 0) {
return activeEra.toNumber() - 1;
}

return activeEra.toNumber() - idx - 1;
});

return combineLatest([
withController || withLedger
Expand All @@ -84,17 +99,40 @@ function getStashInfo (api: DeriveApi, stashIds: AccountId[], activeEra: EraInde
: of(stashIds.map(() => emptyExpo)),
withExposureMeta
? combineLatest(stashIds.map((s) => api.query.staking.erasStakersOverview(activeEra, s)))
: of(stashIds.map(() => emptyExpoMeta))
: of(stashIds.map(() => emptyExpoMeta)),
withClaimedRewardsEras
? combineLatest(stashIds.map((s) =>
combineLatest([
combineLatest(eras.map((e) => api.query.staking.claimedRewards(e, s))),
combineLatest(eras.map((e) => api.query.staking.erasStakersOverview(e, s)))
]))
).pipe(
map((r) => {
return r.map(([stashClaimedEras, overview]) => {
// stashClaimedEras length will match the length of eras
return stashClaimedEras.map((claimedReward, idx) => {
const o = overview[idx].isSome && overview[idx].unwrap();

if (claimedReward.length === (o && o.pageCount.toNumber())) {
return eras[idx];
}

return -1;
});
});
})
)
: of(stashIds.map(() => emptyClaimedRewards))
]);
}

function getBatch (api: DeriveApi, activeEra: EraIndex, stashIds: AccountId[], flags: StakingQueryFlags, page: u32 | AnyNumber): Observable<DeriveStakingQuery[]> {
return getStashInfo(api, stashIds, activeEra, flags, page).pipe(
switchMap(([controllerIdOpt, nominatorsOpt, rewardDestination, validatorPrefs, exposure, exposureMeta]): Observable<DeriveStakingQuery[]> =>
switchMap(([controllerIdOpt, nominatorsOpt, rewardDestination, validatorPrefs, exposure, exposureMeta, claimedRewardsEras]): Observable<DeriveStakingQuery[]> =>
getLedgers(api, controllerIdOpt, flags).pipe(
map((stakingLedgerOpts) =>
stashIds.map((stashId, index) =>
parseDetails(stashId, controllerIdOpt[index], nominatorsOpt[index], rewardDestination[index], validatorPrefs[index], exposure[index], stakingLedgerOpts[index], exposureMeta[index])
parseDetails(api, stashId, controllerIdOpt[index], nominatorsOpt[index], rewardDestination[index], validatorPrefs[index], exposure[index], stakingLedgerOpts[index], exposureMeta[index], claimedRewardsEras[index])
)
)
)
Expand Down
31 changes: 17 additions & 14 deletions packages/api-derive/src/staking/stakerRewards.ts
Expand Up @@ -18,13 +18,15 @@ import { firstMemo, memo } from '../util/index.js';
type ErasResult = [DeriveEraPoints[], DeriveEraPrefs[], DeriveEraRewards[]];

// handle compatibility between generations of structures
function extractCompatRewards (ledger?: PalletStakingStakingLedger): u32[] {
return ledger
function extractCompatRewards (claimedRewardsEras: Vec<u32>, ledger?: PalletStakingStakingLedger): u32[] {
const l = ledger
? (
ledger.legacyClaimedRewards ||
(ledger as PalletStakingStakingLedger & { claimedRewards: Vec<u32> }).claimedRewards
)
: [];
(ledger as PalletStakingStakingLedger & { claimedRewards: Vec<u32> }).claimedRewards
).toArray()
: [] as unknown as Vec<u32>;

return claimedRewardsEras.toArray().concat(l);
}

function parseRewards (api: DeriveApi, stashId: AccountId, [erasPoints, erasPrefs, erasRewards]: ErasResult, exposures: DeriveStakerExposure[]): DeriveStakerReward[] {
Expand Down Expand Up @@ -111,7 +113,7 @@ function allUniqValidators (rewards: DeriveStakerReward[][]): [string[], string[
}, [[], []]);
}

function removeClaimed (validators: string[], queryValidators: DeriveStakingQuery[], reward: DeriveStakerReward): void {
function removeClaimed (validators: string[], queryValidators: DeriveStakingQuery[], reward: DeriveStakerReward, claimedRewardsEras: Vec<u32>): void {
const rm: string[] = [];

Object.keys(reward.validators).forEach((validatorId): void => {
Expand All @@ -120,7 +122,7 @@ function removeClaimed (validators: string[], queryValidators: DeriveStakingQuer
if (index !== -1) {
const valLedger = queryValidators[index].stakingLedger;

if (extractCompatRewards(valLedger).some((e) => reward.era.eq(e))) {
if (extractCompatRewards(claimedRewardsEras, valLedger).some((e) => reward.era.eq(e))) {
rm.push(validatorId);
}
}
Expand All @@ -131,8 +133,8 @@ function removeClaimed (validators: string[], queryValidators: DeriveStakingQuer
});
}

function filterRewards (eras: EraIndex[], valInfo: [string, DeriveStakingQuery][], { rewards, stakingLedger }: { rewards: DeriveStakerReward[]; stakingLedger: PalletStakingStakingLedger }): DeriveStakerReward[] {
const filter = eras.filter((e) => !extractCompatRewards(stakingLedger).some((s) => s.eq(e)));
function filterRewards (eras: EraIndex[], valInfo: [string, DeriveStakingQuery][], { claimedRewardsEras, rewards, stakingLedger }: { rewards: DeriveStakerReward[]; stakingLedger: PalletStakingStakingLedger, claimedRewardsEras: Vec<u32> }): DeriveStakerReward[] {
const filter = eras.filter((e) => !extractCompatRewards(claimedRewardsEras, stakingLedger).some((s) => s.eq(e)));
const validators = valInfo.map(([v]) => v);
const queryValidators = valInfo.map(([, q]) => q);

Expand All @@ -143,7 +145,7 @@ function filterRewards (eras: EraIndex[], valInfo: [string, DeriveStakingQuery][
return false;
}

removeClaimed(validators, queryValidators, reward);
removeClaimed(validators, queryValidators, reward, claimedRewardsEras);

return true;
})
Expand Down Expand Up @@ -173,8 +175,8 @@ export function _stakerRewards (instanceId: string, api: DeriveApi): (accountIds
api.derive.staking._stakerRewardsEras(eras, withActive)
]).pipe(
switchMap(([queries, exposures, erasResult]): Observable<DeriveStakerReward[][]> => {
const allRewards = queries.map(({ stakingLedger, stashId }, index): DeriveStakerReward[] =>
(!stashId || !stakingLedger)
const allRewards = queries.map(({ claimedRewardsEras, stakingLedger, stashId }, index): DeriveStakerReward[] =>
(!stashId || (!stakingLedger && !claimedRewardsEras))
? []
: parseRewards(api, stashId, erasResult, exposures[index])
);
Expand All @@ -185,9 +187,9 @@ export function _stakerRewards (instanceId: string, api: DeriveApi): (accountIds

const [allValidators, stashValidators] = allUniqValidators(allRewards);

return api.derive.staking.queryMulti(allValidators, { withLedger: true }).pipe(
return api.derive.staking.queryMulti(allValidators, { withClaimedRewardsEras: true, withLedger: true }).pipe(
map((queriedVals): DeriveStakerReward[][] =>
queries.map(({ stakingLedger }, index): DeriveStakerReward[] =>
queries.map(({ claimedRewardsEras, stakingLedger }, index): DeriveStakerReward[] =>
filterRewards(
eras,
stashValidators[index]
Expand All @@ -197,6 +199,7 @@ export function _stakerRewards (instanceId: string, api: DeriveApi): (accountIds
])
.filter((v): v is [string, DeriveStakingQuery] => !!v[1]),
{
claimedRewardsEras,
rewards: allRewards[index],
stakingLedger
}
Expand Down
4 changes: 3 additions & 1 deletion packages/api-derive/src/staking/types.ts
@@ -1,7 +1,7 @@
// Copyright 2017-2024 @polkadot/api-derive authors & contributors
// SPDX-License-Identifier: Apache-2.0

import type { Option } from '@polkadot/types';
import type { Option, u32, Vec } from '@polkadot/types';
import type { AccountId, Balance, EraIndex, RewardPoint } from '@polkadot/types/interfaces';
import type { PalletStakingRewardDestination, PalletStakingStakingLedger, PalletStakingValidatorPrefs, SpStakingExposure, SpStakingExposurePage, SpStakingPagedExposureMetadata } from '@polkadot/types/lookup';
import type { BN } from '@polkadot/util';
Expand Down Expand Up @@ -124,6 +124,7 @@ export interface DeriveStakingStash {
rewardDestination: PalletStakingRewardDestination | null;
stashId: AccountId;
validatorPrefs: PalletStakingValidatorPrefs;
claimedRewardsEras: Vec<u32>
}

export interface DeriveStakingQuery extends DeriveStakingStash {
Expand Down Expand Up @@ -165,4 +166,5 @@ export interface StakingQueryFlags {
withNominations?: boolean;
withPrefs?: boolean;
withExposureMeta?: boolean;
withClaimedRewardsEras?: boolean;
}

0 comments on commit b4ebdc2

Please sign in to comment.