diff --git a/packages/api-derive/src/session/eraProgress.ts b/packages/api-derive/src/session/eraProgress.ts index 01a2675a3c..ca39eeaae5 100644 --- a/packages/api-derive/src/session/eraProgress.ts +++ b/packages/api-derive/src/session/eraProgress.ts @@ -12,7 +12,7 @@ import { memo } from '../util'; export function eraProgress (api: ApiInterfaceRx): () => Observable { return memo((): Observable => - api.derive.session.info().pipe( + api.derive.session.progress().pipe( map((info) => info.eraProgress) )); } diff --git a/packages/api-derive/src/session/index.ts b/packages/api-derive/src/session/index.ts index cd85f3f175..d79da151c3 100644 --- a/packages/api-derive/src/session/index.ts +++ b/packages/api-derive/src/session/index.ts @@ -6,4 +6,5 @@ export * from './eraLength'; export * from './eraProgress'; export * from './indexes'; export * from './info'; +export * from './progress'; export * from './sessionProgress'; diff --git a/packages/api-derive/src/session/info.ts b/packages/api-derive/src/session/info.ts index b9f29bd4e5..3baf82b15d 100644 --- a/packages/api-derive/src/session/info.ts +++ b/packages/api-derive/src/session/info.ts @@ -5,34 +5,26 @@ import { SessionIndex } from '@polkadot/types/interfaces'; import { DeriveSessionInfo, DeriveSessionIndexes } from '../types'; -import { Observable, combineLatest, of } from 'rxjs'; -import { map, switchMap } from 'rxjs/operators'; +import { Observable } from 'rxjs'; +import { map } from 'rxjs/operators'; import { ApiInterfaceRx } from '@polkadot/api/types'; -import { Option, u64 } from '@polkadot/types'; +import { u64 } from '@polkadot/types'; import { memo } from '../util'; -type ResultSlots = [u64, u64, u64, Option]; -type ResultSlotsFlat = [u64, u64, u64, SessionIndex]; type ResultType = [boolean, u64, SessionIndex]; -type Result = [ResultType, DeriveSessionIndexes, ResultSlotsFlat]; - -function createDerive (api: ApiInterfaceRx, [[hasBabe, epochDuration, sessionsPerEra], { activeEra, activeEraStart, currentEra, currentIndex, validatorCount }, [currentSlot, epochIndex, epochOrGenesisStartSlot, activeEraStartSessionIndex]]: Result): DeriveSessionInfo { - const epochStartSlot = epochIndex.mul(epochDuration).add(epochOrGenesisStartSlot); - const sessionProgress = currentSlot.sub(epochStartSlot); - const eraProgress = currentIndex.sub(activeEraStartSessionIndex).mul(epochDuration).add(sessionProgress); +type Result = [ResultType, DeriveSessionIndexes]; +function createDerive (api: ApiInterfaceRx, [[hasBabe, epochDuration, sessionsPerEra], { activeEra, activeEraStart, currentEra, currentIndex, validatorCount }]: Result): DeriveSessionInfo { return { activeEra, activeEraStart, currentEra, currentIndex, eraLength: api.registry.createType('BlockNumber', sessionsPerEra.mul(epochDuration)), - eraProgress: api.registry.createType('BlockNumber', eraProgress), isEpoch: hasBabe, sessionLength: epochDuration, sessionsPerEra, - sessionProgress: api.registry.createType('BlockNumber', sessionProgress), validatorCount }; } @@ -46,67 +38,34 @@ function queryAura (api: ApiInterfaceRx): Observable { api.registry.createType('u64', 1), api.consts.staking?.sessionsPerEra || api.registry.createType('SessionIndex', 1) ], - indexes, - [ - api.registry.createType('u64', 1), - api.registry.createType('u64', 1), - api.registry.createType('u64', 1), - api.registry.createType('SessionIndex', 1) - ] + indexes ]) ) ); } -function queryBabe (api: ApiInterfaceRx): Observable<[DeriveSessionIndexes, ResultSlotsFlat]> { +function queryBabe (api: ApiInterfaceRx): Observable { return api.derive.session.indexes().pipe( - switchMap((indexes): Observable<[DeriveSessionIndexes, ResultSlots]> => - combineLatest([ - of(indexes), - api.queryMulti([ - api.query.babe.currentSlot, - api.query.babe.epochIndex, - api.query.babe.genesisSlot, - [api.query.staking.erasStartSessionIndex, indexes.activeEra] - ]) + map((indexes) => + createDerive(api, [ + [ + true, + api.consts.babe.epochDuration, + api.consts.staking.sessionsPerEra + ], + indexes ]) - ), - map(([indexes, [currentSlot, epochIndex, genesisSlot, optStartIndex]]): [DeriveSessionIndexes, ResultSlotsFlat] => [ - indexes, [currentSlot, epochIndex, genesisSlot, optStartIndex.unwrapOr(api.registry.createType('SessionIndex', 1))] - ]) + ) ); } -function queryBabeNoHistory (api: ApiInterfaceRx): Observable<[DeriveSessionIndexes, ResultSlotsFlat]> { - return combineLatest([ - api.derive.session.indexes(), - api.queryMulti([ - api.query.babe.currentSlot, - api.query.babe.epochIndex, - api.query.babe.genesisSlot, - api.query.staking.currentEraStartSessionIndex - ]) - ]); -} - /** * @description Retrieves all the session and era query and calculates specific values on it as the length of the session and eras */ export function info (api: ApiInterfaceRx): () => Observable { return memo((): Observable => api.consts.babe - ? (api.query.staking.erasStartSessionIndex - ? queryBabe(api) // 2.x with Babe - : queryBabeNoHistory(api) - ).pipe( - map(([indexes, slots]: [DeriveSessionIndexes, ResultSlotsFlat]): DeriveSessionInfo => - createDerive(api, [ - [true, api.consts.babe.epochDuration, api.consts.staking.sessionsPerEra], - indexes, - slots - ]) - ) - ) + ? queryBabe(api) : queryAura(api) ); } diff --git a/packages/api-derive/src/session/progress.ts b/packages/api-derive/src/session/progress.ts new file mode 100644 index 0000000000..3016d03904 --- /dev/null +++ b/packages/api-derive/src/session/progress.ts @@ -0,0 +1,90 @@ +// 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 { SessionIndex } from '@polkadot/types/interfaces'; +import { DeriveSessionInfo, DeriveSessionProgress } from '../types'; + +import { Observable, combineLatest, of } from 'rxjs'; +import { map, switchMap } from 'rxjs/operators'; +import { ApiInterfaceRx } from '@polkadot/api/types'; +import { Option, u64 } from '@polkadot/types'; + +import { memo } from '../util'; + +type ResultSlots = [u64, u64, u64, Option]; +type ResultSlotsFlat = [u64, u64, u64, SessionIndex]; + +function createDerive (api: ApiInterfaceRx, info: DeriveSessionInfo, [currentSlot, epochIndex, epochOrGenesisStartSlot, activeEraStartSessionIndex]: ResultSlotsFlat): DeriveSessionProgress { + const epochStartSlot = epochIndex.mul(info.sessionLength).add(epochOrGenesisStartSlot); + const sessionProgress = currentSlot.sub(epochStartSlot); + const eraProgress = info.currentIndex.sub(activeEraStartSessionIndex).mul(info.sessionLength).add(sessionProgress); + + return { + ...info, + eraProgress: api.registry.createType('BlockNumber', eraProgress), + sessionProgress: api.registry.createType('BlockNumber', sessionProgress) + }; +} + +function queryAura (api: ApiInterfaceRx): Observable { + return api.derive.session.info().pipe( + map((info): DeriveSessionProgress => + createDerive(api, info, [ + api.registry.createType('u64', 1), + api.registry.createType('u64', 1), + api.registry.createType('u64', 1), + api.registry.createType('SessionIndex', 1) + ]) + ) + ); +} + +function queryBabe (api: ApiInterfaceRx): Observable<[DeriveSessionInfo, ResultSlotsFlat]> { + return api.derive.session.info().pipe( + switchMap((info): Observable<[DeriveSessionInfo, ResultSlots]> => + combineLatest([ + of(info), + api.queryMulti([ + api.query.babe.currentSlot, + api.query.babe.epochIndex, + api.query.babe.genesisSlot, + [api.query.staking.erasStartSessionIndex, info.activeEra] + ]) + ]) + ), + map(([info, [currentSlot, epochIndex, genesisSlot, optStartIndex]]): [DeriveSessionInfo, ResultSlotsFlat] => [ + info, [currentSlot, epochIndex, genesisSlot, optStartIndex.unwrapOr(api.registry.createType('SessionIndex', 1))] + ]) + ); +} + +function queryBabeNoHistory (api: ApiInterfaceRx): Observable<[DeriveSessionInfo, ResultSlotsFlat]> { + return combineLatest([ + api.derive.session.info(), + api.queryMulti([ + api.query.babe.currentSlot, + api.query.babe.epochIndex, + api.query.babe.genesisSlot, + api.query.staking.currentEraStartSessionIndex + ]) + ]); +} + +/** + * @description Retrieves all the session and era query and calculates specific values on it as the length of the session and eras + */ +export function progress (api: ApiInterfaceRx): () => Observable { + return memo((): Observable => + api.consts.babe + ? (api.query.staking.erasStartSessionIndex + ? queryBabe(api) // 2.x with Babe + : queryBabeNoHistory(api) + ).pipe( + map(([info, slots]: [DeriveSessionInfo, ResultSlotsFlat]): DeriveSessionProgress => + createDerive(api, info, slots) + ) + ) + : queryAura(api) + ); +} diff --git a/packages/api-derive/src/session/sessionProgress.ts b/packages/api-derive/src/session/sessionProgress.ts index 3205077c8f..df804d4f9c 100644 --- a/packages/api-derive/src/session/sessionProgress.ts +++ b/packages/api-derive/src/session/sessionProgress.ts @@ -12,7 +12,7 @@ import { memo } from '../util'; export function sessionProgress (api: ApiInterfaceRx): () => Observable { return memo((): Observable => - api.derive.session.info().pipe( + api.derive.session.progress().pipe( map((info) => info.sessionProgress) )); } diff --git a/packages/api-derive/src/session/types.ts b/packages/api-derive/src/session/types.ts index b883d83e5a..ec6016012e 100644 --- a/packages/api-derive/src/session/types.ts +++ b/packages/api-derive/src/session/types.ts @@ -16,9 +16,12 @@ export interface DeriveSessionIndexes { export interface DeriveSessionInfo extends DeriveSessionIndexes { eraLength: BlockNumber; - eraProgress: BlockNumber; isEpoch: boolean; sessionLength: BlockNumber; sessionsPerEra: SessionIndex; +} + +export interface DeriveSessionProgress extends DeriveSessionInfo { + eraProgress: BlockNumber; sessionProgress: BlockNumber; } diff --git a/packages/api-derive/src/staking/account.ts b/packages/api-derive/src/staking/account.ts index 05fd9ac1b8..a24d9f0726 100644 --- a/packages/api-derive/src/staking/account.ts +++ b/packages/api-derive/src/staking/account.ts @@ -3,7 +3,7 @@ // of the Apache-2.0 license. See the LICENSE file for details. import { ApiInterfaceRx } from '@polkadot/api/types'; -import { Balance, BlockNumber, StakingLedger, UnlockChunk } from '@polkadot/types/interfaces'; +import { Balance, StakingLedger, UnlockChunk } from '@polkadot/types/interfaces'; import { DeriveSessionInfo, DeriveStakingAccount, DeriveStakingQuery, DeriveUnlocking } from '../types'; import BN from 'bn.js'; @@ -27,27 +27,13 @@ function groupByEra (list: UnlockChunk[]): Record { }, {}); } -// calculate the remaining blocks in a specific unlock era -function remainingBlocks (api: ApiInterfaceRx, era: BN, sessionInfo: DeriveSessionInfo): BlockNumber { - const remaining = era.sub(sessionInfo.currentEra); - - // on the Rust side the current-era >= era-for-unlock (removal done on >) - return api.registry.createType('BlockNumber', remaining.gtn(0) - ? remaining - .subn(1) - .mul(sessionInfo.eraLength) - .add(sessionInfo.eraLength.sub(sessionInfo.eraProgress)) - : 0 - ); -} - function calculateUnlocking (api: ApiInterfaceRx, stakingLedger: StakingLedger | undefined, sessionInfo: DeriveSessionInfo): DeriveUnlocking[] | undefined { if (isUndefined(stakingLedger)) { return undefined; } const unlockingChunks = stakingLedger.unlocking.filter(({ era }): boolean => - remainingBlocks(api, era.unwrap(), sessionInfo).gtn(0) + era.unwrap().sub(sessionInfo.activeEra).gtn(0) ); if (!unlockingChunks.length) { @@ -57,8 +43,8 @@ function calculateUnlocking (api: ApiInterfaceRx, stakingLedger: StakingLedger | // group the unlock chunks that have the same era and sum their values const groupedResult = groupByEra(unlockingChunks); const results = Object.entries(groupedResult).map(([eraString, value]): DeriveUnlocking => ({ - value: api.registry.createType('Balance', value), - remainingBlocks: remainingBlocks(api, new BN(eraString), sessionInfo) + remainingEras: new BN(eraString).sub(sessionInfo.activeEra), + value: api.registry.createType('Balance', value) })); return results.length ? results : undefined; @@ -70,7 +56,7 @@ function redeemableSum (api: ApiInterfaceRx, stakingLedger: StakingLedger | unde } return api.registry.createType('Balance', stakingLedger.unlocking.reduce((total, { era, value }): BN => { - return remainingBlocks(api, era.unwrap(), sessionInfo).eqn(0) + return sessionInfo.activeEra.gte(era.unwrap()) ? total.add(value.unwrap()) : total; }, new BN(0))); diff --git a/packages/api-derive/src/staking/types.ts b/packages/api-derive/src/staking/types.ts index 344f0f7955..d9ea1773e3 100644 --- a/packages/api-derive/src/staking/types.ts +++ b/packages/api-derive/src/staking/types.ts @@ -2,9 +2,11 @@ // This software may be modified and distributed under the terms // of the Apache-2.0 license. See the LICENSE file for details. -import { AccountId, Balance, BlockNumber, EraIndex, EraRewardPoints, Exposure, Keys, RewardDestination, RewardPoint, StakingLedger, ValidatorPrefs } from '@polkadot/types/interfaces'; +import { AccountId, Balance, EraIndex, EraRewardPoints, Exposure, Keys, RewardDestination, RewardPoint, StakingLedger, ValidatorPrefs } from '@polkadot/types/interfaces'; import { DeriveSessionIndexes } from '../session/types'; +import BN from 'bn.js'; + export type DeriveEraValPoints = Record; export type DeriveEraValPrefs = Record; @@ -119,6 +121,6 @@ export interface DeriveStakingOverview extends DeriveSessionIndexes { } export type DeriveUnlocking = { - remainingBlocks: BlockNumber; + remainingEras: BN; value: Balance; };