diff --git a/packages/api-derive/src/staking/erasPrefs.ts b/packages/api-derive/src/staking/erasPrefs.ts index 826737ed1e..215d908626 100644 --- a/packages/api-derive/src/staking/erasPrefs.ts +++ b/packages/api-derive/src/staking/erasPrefs.ts @@ -13,9 +13,7 @@ import { StorageKey } from '@polkadot/types'; import { memo } from '../util'; -type KeysAndPrefs = [StorageKey, ValidatorPrefs][]; - -function mapPrefs (era: EraIndex, all: KeysAndPrefs): DeriveEraPrefs { +function mapPrefs (era: EraIndex, all: [StorageKey, ValidatorPrefs][]): DeriveEraPrefs { const validators: DeriveEraValPrefs = {}; all.forEach(([key, prefs]): void => { diff --git a/packages/api-derive/src/staking/erasSlashes.ts b/packages/api-derive/src/staking/erasSlashes.ts new file mode 100644 index 0000000000..dda2bb2953 --- /dev/null +++ b/packages/api-derive/src/staking/erasSlashes.ts @@ -0,0 +1,55 @@ +// 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 { BalanceOf, EraIndex, Perbill } from '@polkadot/types/interfaces'; +import { ITuple } from '@polkadot/types/types'; +import { DeriveEraSlashes, DeriveEraValSlash } from '../types'; + +import BN from 'bn.js'; +import { Observable, combineLatest, of } from 'rxjs'; +import { map, switchMap } from 'rxjs/operators'; +import { Option, StorageKey } from '@polkadot/types'; + +import { memo } from '../util'; + +function mapSlashes (era: EraIndex, noms: [StorageKey, Option][], vals: [StorageKey, Option>][]): DeriveEraSlashes { + const nominators: DeriveEraValSlash = {}; + const validators: DeriveEraValSlash = {}; + + noms.forEach(([key, optBalance]): void => { + nominators[key.args[1].toString()] = optBalance.unwrap(); + }); + + vals.forEach(([key, optRes]): void => { + validators[key.args[1].toString()] = optRes.unwrapOrDefault()[1]; + }); + + return { era, nominators, validators }; +} + +export function eraSlashes (api: ApiInterfaceRx): (era: EraIndex) => Observable { + return memo((era: EraIndex): Observable => + combineLatest([ + api.query.staking.nominatorSlashInEra.entries(era), + api.query.staking.validatorSlashInEra.entries(era) + ]).pipe( + map(([noms, vals]) => mapSlashes(era, noms, vals)) + ) + ); +} + +export function erasSlashes (api: ApiInterfaceRx): (withActive?: boolean | BN | number) => Observable { + return memo((withActive?: boolean | BN | number): Observable => + api.derive.staking.erasHistoric(withActive).pipe( + switchMap((eras): Observable => + eras.length + ? combineLatest( + eras.map((era) => api.derive.staking.eraSlashes(era)) + ) + : of([]) + ) + ) + ); +} diff --git a/packages/api-derive/src/staking/index.ts b/packages/api-derive/src/staking/index.ts index 5f3967ef46..ebf4fc5859 100644 --- a/packages/api-derive/src/staking/index.ts +++ b/packages/api-derive/src/staking/index.ts @@ -8,6 +8,7 @@ export * from './erasHistoric'; export * from './erasPoints'; export * from './erasPrefs'; export * from './erasRewards'; +export * from './erasSlashes'; export * from './controllers'; export * from './electedInfo'; export * from './overview'; @@ -15,4 +16,5 @@ export * from './query'; export * from './stakerExposure'; export * from './stakerPoints'; export * from './stakerRewards'; +export * from './stakerSlashes'; export * from './validators'; diff --git a/packages/api-derive/src/staking/stakerSlashes.ts b/packages/api-derive/src/staking/stakerSlashes.ts new file mode 100644 index 0000000000..d0d24415b4 --- /dev/null +++ b/packages/api-derive/src/staking/stakerSlashes.ts @@ -0,0 +1,28 @@ +// 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 { DeriveStakerSlashes } from '../types'; + +import BN from 'bn.js'; +import { Observable } from 'rxjs'; +import { map } from 'rxjs/operators'; + +import { memo } from '../util'; + +export function stakerSlashes (api: ApiInterfaceRx): (accountId: Uint8Array | string, withActive?: boolean | BN | number) => Observable { + return memo((accountId: Uint8Array | string, withActive?: boolean | BN | number): Observable => { + const stakerId = api.registry.createType('AccountId', accountId).toString(); + + return api.derive.staking.erasSlashes(withActive).pipe( + map((slashes): DeriveStakerSlashes[] => + slashes.map(({ era, nominators, validators }): DeriveStakerSlashes => ({ + era, + isValidator: !!validators[stakerId], + total: nominators[stakerId] || validators[stakerId] || api.registry.createType('Balance') + })) + ) + ); + }); +} diff --git a/packages/api-derive/src/staking/types.ts b/packages/api-derive/src/staking/types.ts index 5a2bb6ec47..7492230834 100644 --- a/packages/api-derive/src/staking/types.ts +++ b/packages/api-derive/src/staking/types.ts @@ -9,6 +9,8 @@ export type DeriveEraValPoints = Record; export type DeriveEraValPrefs = Record; +export type DeriveEraValSlash = Record; + export interface DeriveEraPoints { era: EraIndex; eraPoints: RewardPoint; @@ -25,6 +27,12 @@ export interface DeriveEraRewards { eraReward: Balance; } +export interface DeriveEraSlashes { + era: EraIndex; + nominators: DeriveEraValSlash; + validators: DeriveEraValSlash; +} + export interface DeriveStakerPoints { era: EraIndex; eraPoints: RewardPoint; @@ -58,6 +66,12 @@ export interface DeriveStakerReward { total: Balance; } +export interface DeriveStakerSlashes { + era: EraIndex; + isValidator: boolean; + total: Balance; +} + export interface DerivedStakingElected { nextElected: AccountId[]; info: DerivedStakingQuery[];