Add stakerRewards for available reward calcs (#2028)

This commit is contained in:
Jaco Greeff
2020-03-11 12:33:12 +01:00
committed by GitHub
parent 1d8b6a79ef
commit a09fc51693
9 changed files with 107 additions and 20 deletions
@@ -38,8 +38,8 @@ function mapStakers (stakers: [StorageKey, Exposure][]): Mapped {
return { nominators, validators };
}
export function erasExposure (api: ApiInterfaceRx): (withActive?: boolean | BN) => Observable<DeriveEraExposure[]> {
return memo((withActive?: boolean | BN): Observable<DeriveEraExposure[]> =>
export function erasExposure (api: ApiInterfaceRx): (withActive?: boolean | BN | number) => Observable<DeriveEraExposure[]> {
return memo((withActive?: boolean | BN | number): Observable<DeriveEraExposure[]> =>
api.derive.staking.erasHistoric(withActive).pipe(
switchMap((eras): Observable<[EraIndex[], [StorageKey, Exposure][][]]> =>
combineLatest([
@@ -9,12 +9,12 @@ import BN from 'bn.js';
import { Observable, of } from 'rxjs';
import { map } from 'rxjs/operators';
import { Option, u32 } from '@polkadot/types';
import { isBoolean, isUndefined } from '@polkadot/util';
import { isBoolean, isUndefined, bnToBn } from '@polkadot/util';
import { memo } from '../util';
export function erasHistoric (api: ApiInterfaceRx): (withActive?: boolean | BN) => Observable<EraIndex[]> {
return memo((withActive?: boolean | BN): Observable<EraIndex[]> =>
export function erasHistoric (api: ApiInterfaceRx): (withActive?: boolean | BN | number) => Observable<EraIndex[]> {
return memo((withActive?: boolean | BN | number): Observable<EraIndex[]> =>
api.query.staking?.activeEra
? api.queryMulti<[Option<ActiveEraInfo>, u32]>([
api.query.staking.activeEra,
@@ -36,13 +36,15 @@ export function erasHistoric (api: ApiInterfaceRx): (withActive?: boolean | BN)
lastEra = lastEra.subn(1);
}
const startEra = isUndefined(withActive) || isBoolean(withActive)
? new BN(0)
: bnToBn(withActive);
// go from oldest to newest
return result
.filter((era): era is EraIndex =>
era
? isUndefined(withActive) || isBoolean(withActive)
? true
: era.gte(withActive)
? era.gte(startEra)
: false
)
.reverse();
@@ -22,8 +22,8 @@ function mapValidators ({ individual }: EraRewardPoints): DeriveEraValPoints {
}, {});
}
export function erasPoints (api: ApiInterfaceRx): (withActive?: boolean | BN) => Observable<DeriveEraPoints[]> {
return memo((withActive?: boolean): Observable<DeriveEraPoints[]> =>
export function erasPoints (api: ApiInterfaceRx): (withActive?: boolean | BN | number) => Observable<DeriveEraPoints[]> {
return memo((withActive?: boolean | number): Observable<DeriveEraPoints[]> =>
api.derive.staking.erasHistoric(withActive).pipe(
switchMap((eras): Observable<[EraIndex[], EraRewardPoints[]]> =>
combineLatest([
@@ -13,8 +13,8 @@ import { Option } from '@polkadot/types';
import { memo } from '../util';
export function erasRewards (api: ApiInterfaceRx): (withActive?: boolean | BN) => Observable<DeriveEraRewards[]> {
return memo((withActive?: boolean | BN): Observable<DeriveEraRewards[]> =>
export function erasRewards (api: ApiInterfaceRx): (withActive?: boolean | BN | number) => Observable<DeriveEraRewards[]> {
return memo((withActive?: boolean | BN | number): Observable<DeriveEraRewards[]> =>
api.derive.staking.erasHistoric(withActive).pipe(
switchMap((eras): Observable<[EraIndex[], Option<Balance>[]]> =>
combineLatest([
+1
View File
@@ -13,4 +13,5 @@ export * from './overview';
export * from './query';
export * from './stakerExposure';
export * from './stakerPoints';
export * from './stakerRewards';
export * from './validators';
@@ -3,7 +3,7 @@
// of the Apache-2.0 license. See the LICENSE file for details.
import { ApiInterfaceRx } from '@polkadot/api/types';
import { DeriveStakerExpoure, DeriveEraValidatorExposure } from '../types';
import { DeriveStakerExposure, DeriveEraValidatorExposure } from '../types';
import BN from 'bn.js';
import { Observable } from 'rxjs';
@@ -11,13 +11,13 @@ import { map } from 'rxjs/operators';
import { memo } from '../util';
export function stakerExposure (api: ApiInterfaceRx): (accountId: Uint8Array | string, withActive?: boolean | BN) => Observable<DeriveStakerExpoure[]> {
return memo((accountId: Uint8Array | string, withActive?: boolean | BN): Observable<DeriveStakerExpoure[]> => {
export function stakerExposure (api: ApiInterfaceRx): (accountId: Uint8Array | string, withActive?: boolean | BN | number) => Observable<DeriveStakerExposure[]> {
return memo((accountId: Uint8Array | string, withActive?: boolean | BN | number): Observable<DeriveStakerExposure[]> => {
const stakerId = api.registry.createType('AccountId', accountId).toString();
return api.derive.staking.erasExposure(withActive).pipe(
map((exposures): DeriveStakerExpoure[] =>
exposures.map(({ era, nominators: allNominators, validators: allValidators }): DeriveStakerExpoure => {
map((exposures): DeriveStakerExposure[] =>
exposures.map(({ era, nominators: allNominators, validators: allValidators }): DeriveStakerExposure => {
const isValidator = !!allValidators[stakerId];
const validators: DeriveEraValidatorExposure = {};
let nominating: [string, number][] = [];
@@ -11,8 +11,8 @@ import { map } from 'rxjs/operators';
import { memo } from '../util';
export function stakerPoints (api: ApiInterfaceRx): (accountId: Uint8Array | string, withActive?: boolean | BN) => Observable<DeriveStakerPoints[]> {
return memo((accountId: Uint8Array | string, withActive?: boolean | BN): Observable<DeriveStakerPoints[]> => {
export function stakerPoints (api: ApiInterfaceRx): (accountId: Uint8Array | string, withActive?: boolean | BN | number) => Observable<DeriveStakerPoints[]> {
return memo((accountId: Uint8Array | string, withActive?: boolean | BN | number): Observable<DeriveStakerPoints[]> => {
const stakerId = api.registry.createType('AccountId', accountId).toString();
return api.derive.staking.erasPoints(withActive).pipe(
@@ -0,0 +1,75 @@
// 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 } from '@polkadot/types/interfaces';
import { DeriveEraPoints, DeriveEraRewards, DeriveStakerExposure, DeriveStakerReward } from '../types';
import BN from 'bn.js';
import { Observable, combineLatest } from 'rxjs';
import { map } from 'rxjs/operators';
import { memo } from '../util';
type Result = [DeriveEraPoints[], DeriveEraRewards[], DeriveStakerExposure[]];
const ZERO = new BN(0);
function parseRewards (api: ApiInterfaceRx, stakerId: string, [erasPoints, 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: new BN(0), validators: {} };
const { eraReward } = erasRewards.find((r) => r.era.eq(era)) || { eraReward: new BN(0) };
const validators: Record<string, Balance> = {};
let total = ZERO;
Object.entries(eraValidators).forEach(([validatorId, exposure]): void => {
const valPoints = allValPoints[validatorId] || new BN(0);
const expTotal = exposure.total.unwrap();
let value: BN | undefined;
if (!expTotal.isZero() && !valPoints.isZero()) {
let staked: BN;
if (validatorId === stakerId) {
staked = exposure.own.unwrap();
} else {
const stakerExp = exposure.others.find(({ who }): boolean => who.eq(stakerId));
staked = stakerExp
? stakerExp.value.unwrap()
: ZERO;
}
value = eraReward.mul(valPoints).mul(staked).div(eraPoints).div(expTotal);
total = total.add(value);
}
validators[validatorId] = api.registry.createType('Balance', value);
});
return {
era,
isEmpty,
isValidator,
nominating,
total: api.registry.createType('Balance', total),
validators
};
});
}
export function stakerRewards (api: ApiInterfaceRx): (accountId: Uint8Array | string, startEra: BN | number) => Observable<DeriveStakerReward[]> {
return memo((accountId: Uint8Array | string, startEra: BN | number): Observable<DeriveStakerReward[]> => {
const stakerId = api.registry.createType('AccountId', accountId).toString();
return combineLatest([
api.derive.staking.erasPoints(startEra),
api.derive.staking.erasRewards(startEra),
api.derive.staking.stakerExposure(stakerId, startEra)
]).pipe(
map((result): DeriveStakerReward[] =>
parseRewards(api, stakerId, result)
)
);
});
}
+10 -1
View File
@@ -34,7 +34,7 @@ export interface DeriveEraExposure {
validators: DeriveEraValidatorExposure;
}
export interface DeriveStakerExpoure {
export interface DeriveStakerExposure {
era: EraIndex;
isEmpty: boolean;
isValidator: boolean;
@@ -42,6 +42,15 @@ export interface DeriveStakerExpoure {
validators: DeriveEraValidatorExposure;
}
export interface DeriveStakerReward {
era: EraIndex;
isEmpty: boolean;
isValidator: boolean;
nominating: [string, number][];
validators: Record<string, Balance>;
total: Balance;
}
export interface DerivedStakingElected {
nextElected: AccountId[];
info: DerivedStakingQuery[];