diff --git a/packages/api-derive/src/accounts/idAndIndex.ts b/packages/api-derive/src/accounts/idAndIndex.ts index d3f7a69778..ac5d98f072 100644 --- a/packages/api-derive/src/accounts/idAndIndex.ts +++ b/packages/api-derive/src/accounts/idAndIndex.ts @@ -11,18 +11,11 @@ import { isU8a } from '@polkadot/util'; import { decodeAddress } from '@polkadot/util-crypto'; import { createType } from '@polkadot/types'; -import { idToIndex } from './idToIndex'; -import { indexToId } from './indexToId'; import { drr } from '../util'; -interface Calls { - idToIndexCall (accountId: string | AccountId): Observable; - indexToIdCall (accountIndex: string | AccountIndex): Observable; -} - export type AccountIdAndIndex = [AccountId?, AccountIndex?]; -function retrieve (address: Address | AccountId | AccountIndex | string | null | undefined, { idToIndexCall, indexToIdCall }: Calls): Observable { +function retrieve (api: ApiInterfaceRx, address: Address | AccountId | AccountIndex | string | null | undefined): Observable { try { // yes, this can fail, don't care too much, catch will catch it const decoded = isU8a(address) @@ -32,14 +25,14 @@ function retrieve (address: Address | AccountId | AccountIndex | string | null | if (decoded.length === 32) { const accountId = createType('AccountId', decoded); - return idToIndexCall(accountId).pipe( + return api.derive.accounts.idToIndex(accountId).pipe( map((accountIndex): AccountIdAndIndex => [accountId, accountIndex] as AccountIdAndIndex) ); } const accountIndex = createType('AccountIndex', decoded); - return indexToIdCall(accountIndex).pipe( + return api.derive.accounts.indexToId(accountIndex).pipe( map((accountId): AccountIdAndIndex => [accountId, accountIndex] as AccountIdAndIndex) ); } catch (error) { @@ -61,11 +54,6 @@ function retrieve (address: Address | AccountId | AccountIndex | string | null | * ``` */ export function idAndIndex (api: ApiInterfaceRx): (address?: Address | AccountId | AccountIndex | string | null) => Observable { - const calls = { - idToIndexCall: idToIndex(api), - indexToIdCall: indexToId(api) - }; - return (address?: Address | AccountId | AccountIndex | string | null): Observable => - retrieve(address, calls).pipe(drr()); + retrieve(api, address).pipe(drr()); } diff --git a/packages/api-derive/src/accounts/idToIndex.ts b/packages/api-derive/src/accounts/idToIndex.ts index a294a85fff..f767e55476 100644 --- a/packages/api-derive/src/accounts/idToIndex.ts +++ b/packages/api-derive/src/accounts/idToIndex.ts @@ -3,12 +3,12 @@ // of the Apache-2.0 license. See the LICENSE file for details. import { AccountId, AccountIndex } from '@polkadot/types/interfaces'; +import { AccountIndexes } from '../types'; import { Observable } from 'rxjs'; import { map, startWith } from 'rxjs/operators'; import { ApiInterfaceRx } from '@polkadot/api/types'; -import { indexes, AccountIndexes } from './indexes'; import { drr } from '../util'; /** @@ -26,10 +26,8 @@ import { drr } from '../util'; * ``` */ export function idToIndex (api: ApiInterfaceRx): (accountId: AccountId | string) => Observable { - const indexesCall = indexes(api); - return (accountId: AccountId | string): Observable => - indexesCall().pipe( + api.derive.accounts.indexes().pipe( startWith({}), map((indexes: AccountIndexes): AccountIndex | undefined => (indexes || {})[accountId.toString()] diff --git a/packages/api-derive/src/accounts/indexes.ts b/packages/api-derive/src/accounts/indexes.ts index 6874b54db2..b1a753c013 100644 --- a/packages/api-derive/src/accounts/indexes.ts +++ b/packages/api-derive/src/accounts/indexes.ts @@ -4,6 +4,7 @@ import { ApiInterfaceRx } from '@polkadot/api/types'; import { AccountId, AccountIndex } from '@polkadot/types/interfaces'; +import { AccountIndexes } from '../types'; import { Observable } from 'rxjs'; import { map, switchMap } from 'rxjs/operators'; @@ -12,8 +13,6 @@ import { createType } from '@polkadot/types'; import { drr } from '../util'; -export type AccountIndexes = Record; - const enumsetSize = ENUMSET_SIZE.toNumber(); /** diff --git a/packages/api-derive/src/accounts/info.ts b/packages/api-derive/src/accounts/info.ts index 91b59348d4..c1115326c8 100644 --- a/packages/api-derive/src/accounts/info.ts +++ b/packages/api-derive/src/accounts/info.ts @@ -7,19 +7,25 @@ import { AccountId, AccountIndex, Address, Balance } from '@polkadot/types/inter import { Codec } from '@polkadot/types/types'; import { DeriveAccountInfo } from '../types'; -import { Observable, of, combineLatest } from 'rxjs'; +import { Observable, combineLatest, of } from 'rxjs'; import { map, switchMap } from 'rxjs/operators'; import { Bytes, Option, u32 } from '@polkadot/types'; import { u8aToString } from '@polkadot/util'; import { drr } from '../util'; -import { idAndIndex } from './idAndIndex'; -function retrieveNick (api: ApiInterfaceRx): (accountId?: AccountId) => Observable | undefined> { - return (accountId?: AccountId): Observable | undefined> => - accountId && api.query.nicks - ? api.query.nicks.nameOf>(accountId) - : of(undefined); +function retrieveNick (api: ApiInterfaceRx, accountId?: AccountId): Observable { + return accountId && api.query.nicks + ? api.query.nicks + .nameOf>(accountId) + .pipe( + map((nameOf): string | undefined => + nameOf?.isSome + ? u8aToString(nameOf.unwrap()[0]).substr(0, (api.consts.nicks.maxLength as u32).toNumber()) + : undefined + ) + ) + : of(undefined); } /** @@ -27,23 +33,16 @@ function retrieveNick (api: ApiInterfaceRx): (accountId?: AccountId) => Observab * @description Returns aux. info with regards to an account, current that includes the accountId, accountIndex and nickname */ export function info (api: ApiInterfaceRx): (address?: AccountIndex | AccountId | Address | string | null) => Observable { - const idAndIndexCall = idAndIndex(api); - const nickCall = retrieveNick(api); - return (address?: AccountIndex | AccountId | Address | string | null): Observable => - idAndIndexCall(address).pipe( - switchMap(([accountId, accountIndex]): Observable<[DeriveAccountInfo, Option<[Bytes, Balance] & Codec>?]> => + api.derive.accounts.idAndIndex(address).pipe( + switchMap(([accountId, accountIndex]): Observable<[DeriveAccountInfo, string?]> => combineLatest([ of({ accountId, accountIndex }), - nickCall(accountId) + retrieveNick(api, accountId) ]) ), - map(([{ accountId, accountIndex }, nameOf]): DeriveAccountInfo => ({ - accountId, - accountIndex, - nickname: nameOf && nameOf.isSome - ? u8aToString(nameOf.unwrap()[0]).substr(0, (api.consts.nicks.maxLength as u32).toNumber()) - : undefined + map(([{ accountId, accountIndex }, nickname]): DeriveAccountInfo => ({ + accountId, accountIndex, nickname })), drr() ); diff --git a/packages/api-derive/src/balances/all.ts b/packages/api-derive/src/balances/all.ts index 7fc33ff612..939debb847 100644 --- a/packages/api-derive/src/balances/all.ts +++ b/packages/api-derive/src/balances/all.ts @@ -3,6 +3,7 @@ // of the Apache-2.0 license. See the LICENSE file for details. import { AccountId, AccountIndex, Address, Balance, BalanceLock, BlockNumber, Index, VestingSchedule } from '@polkadot/types/interfaces'; +import { DerivedBalances } from '../types'; import BN from 'bn.js'; import { combineLatest, of, Observable } from 'rxjs'; @@ -11,9 +12,6 @@ import { ApiInterfaceRx } from '@polkadot/api/types'; import { Option, Vec, createType } from '@polkadot/types'; import { bnMax } from '@polkadot/util'; -import { info } from '../accounts/info'; -import { bestNumber } from '../chain/bestNumber'; -import { DerivedBalances } from '../types'; import { drr } from '../util'; type ResultBalance = [Balance, Balance, BalanceLock[], Option]; @@ -78,16 +76,13 @@ function queryBalances (api: ApiInterfaceRx, accountId: AccountId): Observable Observable { - const bestNumberCall = bestNumber(api); - const infoCall = info(api); - return (address: AccountIndex | AccountId | Address | string): Observable => - infoCall(address).pipe( + api.derive.accounts.info(address).pipe( switchMap(({ accountId }): Observable => (accountId ? combineLatest([ of(accountId), - bestNumberCall(), + api.derive.chain.bestNumber(), queryBalances(api, accountId), // FIXME This is having issues with Kusama, only use accountNonce atm // api.rpc.account && api.rpc.account.nextIndex diff --git a/packages/api-derive/src/balances/votingBalances.ts b/packages/api-derive/src/balances/votingBalances.ts index dc1f33b934..a38600a849 100644 --- a/packages/api-derive/src/balances/votingBalances.ts +++ b/packages/api-derive/src/balances/votingBalances.ts @@ -9,14 +9,16 @@ import { ApiInterfaceRx } from '@polkadot/api/types'; import { DerivedBalances } from '../types'; import { drr } from '../util'; -import { all } from './all'; export function votingBalances (api: ApiInterfaceRx): (addresses?: (AccountId | AccountIndex | Address | string)[]) => Observable { return (addresses?: (AccountId | AccountIndex | Address | string)[]): Observable => ( !addresses || !addresses.length ? of([] as DerivedBalances[]) - : combineLatest(addresses.map(all(api))) + : combineLatest( + addresses.map((accountId): Observable => + api.derive.balances.all(accountId)) + ) ).pipe( drr() ); diff --git a/packages/api-derive/src/balances/votingBalancesNominatorsFor.ts b/packages/api-derive/src/balances/votingBalancesNominatorsFor.ts index e7e50ecea3..bfe53015ec 100644 --- a/packages/api-derive/src/balances/votingBalancesNominatorsFor.ts +++ b/packages/api-derive/src/balances/votingBalancesNominatorsFor.ts @@ -3,28 +3,26 @@ // of the Apache-2.0 license. See the LICENSE file for details. import { AccountId, AccountIndex, Address } from '@polkadot/types/interfaces'; +import { DerivedBalances } from '../types'; import { Observable, of } from 'rxjs'; import { switchMap } from 'rxjs/operators'; import { ApiInterfaceRx } from '@polkadot/api/types'; import { Vec } from '@polkadot/types'; -import { info } from '../accounts/info'; -import { DerivedBalances } from '../types'; import { drr } from '../util'; -import { votingBalances } from './votingBalances'; export function votingBalancesNominatorsFor (api: ApiInterfaceRx): (address: AccountId | AccountIndex | Address | string) => Observable { - const infoCall = info(api); - return (address: AccountId | AccountIndex | Address | string): Observable => - infoCall(address).pipe( + api.derive.accounts.info(address).pipe( switchMap(({ accountId }): Observable => accountId - ? (api.query.staking.nominatorsFor>(accountId)) + ? api.query.staking.nominatorsFor>(accountId) : of([] as AccountId[]) ), - switchMap(votingBalances(api)), + switchMap((accounts): Observable => + api.derive.balances.votingBalances(accounts) + ), drr() ); } diff --git a/packages/api-derive/src/chain/bestNumber.ts b/packages/api-derive/src/chain/bestNumber.ts index fcf06ed9ab..15facce531 100644 --- a/packages/api-derive/src/chain/bestNumber.ts +++ b/packages/api-derive/src/chain/bestNumber.ts @@ -2,7 +2,7 @@ // This software may be modified and distributed under the terms // of the Apache-2.0 license. See the LICENSE file for details. -import { BlockNumber } from '@polkadot/types/interfaces'; +import { BlockNumber, Header } from '@polkadot/types/interfaces'; import { Observable } from 'rxjs'; import { map } from 'rxjs/operators'; @@ -24,8 +24,8 @@ import { drr } from '../util'; */ export function bestNumber (api: ApiInterfaceRx): () => Observable { return (): Observable => - api.rpc.chain.subscribeNewHeads().pipe( - map((header): BlockNumber => header.number.unwrap()), + api.derive.chain.subscribeNewHeads().pipe( + map((header: Header): BlockNumber => header.number.unwrap()), drr() ); } diff --git a/packages/api-derive/src/chain/bestNumberLag.ts b/packages/api-derive/src/chain/bestNumberLag.ts index 9be067e986..c9ad3af8a2 100644 --- a/packages/api-derive/src/chain/bestNumberLag.ts +++ b/packages/api-derive/src/chain/bestNumberLag.ts @@ -10,8 +10,6 @@ import { ApiInterfaceRx } from '@polkadot/api/types'; import { createType } from '@polkadot/types'; import { drr } from '../util'; -import { bestNumber } from './bestNumber'; -import { bestNumberFinalized } from './bestNumberFinalized'; /** * @name bestNumberLag @@ -27,13 +25,10 @@ import { bestNumberFinalized } from './bestNumberFinalized'; * ``` */ export function bestNumberLag (api: ApiInterfaceRx): () => Observable { - const bestNumberCall = bestNumber(api); - const bestNumberFinalizedCall = bestNumberFinalized(api); - return (): Observable => combineLatest([ - bestNumberCall(), - bestNumberFinalizedCall() + api.derive.chain.bestNumber(), + api.derive.chain.bestNumberFinalized() ]).pipe( map(([bestNumber, bestNumberFinalized]): BlockNumber => createType('BlockNumber', bestNumber.sub(bestNumberFinalized)) diff --git a/packages/api-derive/src/chain/getHeader.ts b/packages/api-derive/src/chain/getHeader.ts index 9e3460f853..0bb2741759 100644 --- a/packages/api-derive/src/chain/getHeader.ts +++ b/packages/api-derive/src/chain/getHeader.ts @@ -31,7 +31,9 @@ export function getHeader (api: ApiInterfaceRx): (hash: Uint8Array | string) => return (hash: Uint8Array | string): Observable => combineLatest([ api.rpc.chain.getHeader(hash), - api.query.session.validators.at(hash) as Observable> + api.query.session + ? api.query.session.validators.at(hash) as Observable> + : of([]) ]).pipe( map(([header, validators]): HeaderExtended => new HeaderExtended(header, validators) diff --git a/packages/api-derive/src/chain/subscribeNewHeads.ts b/packages/api-derive/src/chain/subscribeNewHeads.ts index 37cb7abec6..950589a421 100644 --- a/packages/api-derive/src/chain/subscribeNewHeads.ts +++ b/packages/api-derive/src/chain/subscribeNewHeads.ts @@ -2,12 +2,10 @@ // This software may be modified and distributed under the terms // of the Apache-2.0 license. See the LICENSE file for details. -import { AccountId } from '@polkadot/types/interfaces'; import { ApiInterfaceRx } from '@polkadot/api/types'; import { Observable, combineLatest } from 'rxjs'; import { map } from 'rxjs/operators'; -import { Vec } from '@polkadot/types'; import { HeaderExtended } from '../type'; import { drr } from '../util'; @@ -29,9 +27,9 @@ export function subscribeNewHeads (api: ApiInterfaceRx): () => Observable
=> combineLatest([ api.rpc.chain.subscribeNewHeads(), - api.query.session.validators>() + api.derive.staking.validators() ]).pipe( - map(([header, validators]): HeaderExtended => + map(([header, { validators }]): HeaderExtended => new HeaderExtended(header, validators) ), drr() diff --git a/packages/api-derive/src/democracy/referendumVotesFor.ts b/packages/api-derive/src/democracy/referendumVotesFor.ts index acf8762933..44f451800f 100644 --- a/packages/api-derive/src/democracy/referendumVotesFor.ts +++ b/packages/api-derive/src/democracy/referendumVotesFor.ts @@ -12,20 +12,15 @@ import { Vec, createType } from '@polkadot/types'; import { DerivedBalances, DerivedReferendumVote } from '../types'; import { drr } from '../util'; -import { votes } from './votes'; -import { votingBalances } from '../balances/votingBalances'; export function referendumVotesFor (api: ApiInterfaceRx): (referendumId: BN | number) => Observable { - const votesCall = votes(api); - const votingBalancesCall = votingBalances(api); - return (referendumId: BN | number): Observable => api.query.democracy.votersFor>(referendumId).pipe( switchMap((votersFor): Observable<[Vec, Vote[], DerivedBalances[]]> => combineLatest([ of(votersFor), - votesCall(referendumId as BN, votersFor), - votingBalancesCall(votersFor) + api.derive.democracy.votes(referendumId as BN, votersFor), + api.derive.balances.votingBalances(votersFor) ]) ), map(([votersFor, votes, balances]): DerivedReferendumVote[] => diff --git a/packages/api-derive/src/democracy/referendums.ts b/packages/api-derive/src/democracy/referendums.ts index edccf12e03..99355ca01a 100644 --- a/packages/api-derive/src/democracy/referendums.ts +++ b/packages/api-derive/src/democracy/referendums.ts @@ -12,11 +12,8 @@ import { Option } from '@polkadot/types'; import { ReferendumInfoExtended } from '../type'; import { drr } from '../util'; -import { referendumInfos } from './referendumInfos'; export function referendums (api: ApiInterfaceRx): () => Observable[]> { - const referendumInfosCall = referendumInfos(api); - return (): Observable[]> => api.queryMulti<[ReferendumIndex, ReferendumIndex]>([ api.query.democracy.nextTally, @@ -24,7 +21,7 @@ export function referendums (api: ApiInterfaceRx): () => Observable[]> => referendumCount && nextTally && referendumCount.gt(nextTally) && referendumCount.gtn(0) - ? referendumInfosCall( + ? api.derive.democracy.referendumInfos( [...Array(referendumCount.sub(nextTally).toNumber())].map((_, i): BN => nextTally.addn(i) ) diff --git a/packages/api-derive/src/elections/voterPositions.ts b/packages/api-derive/src/elections/voterPositions.ts index a734cfee14..d424964534 100644 --- a/packages/api-derive/src/elections/voterPositions.ts +++ b/packages/api-derive/src/elections/voterPositions.ts @@ -35,6 +35,7 @@ export function voterPositions (api: ApiInterfaceRx): () => Observable>[]]): DerivedVoterPositions => { const [setSize, voters] = result; + return voters.reduce((result: DerivedVoterPositions, vec, setIndex): DerivedVoterPositions => { vec.forEach((e, index): void => { // re-create the index based on position 0 is [0][0] and likewise diff --git a/packages/api-derive/src/elections/voters.ts b/packages/api-derive/src/elections/voters.ts index a327b901c1..c1083c0e25 100644 --- a/packages/api-derive/src/elections/voters.ts +++ b/packages/api-derive/src/elections/voters.ts @@ -11,7 +11,6 @@ import { map } from 'rxjs/operators'; import { createType, Vec } from '@polkadot/types'; import { drr } from '../util'; -import { voterPositions } from './voterPositions'; /** * @name voters @@ -26,10 +25,8 @@ import { voterPositions } from './voterPositions'; * ``` */ export function voters (api: ApiInterfaceRx): () => Observable> { - const voterPositionsCall = voterPositions(api); - return (): Observable> => - voterPositionsCall().pipe( + api.derive.elections.voterPositions().pipe( map((voterPositions: DerivedVoterPositions): Vec => createType( 'Vec', diff --git a/packages/api-derive/src/imOnline/receivedHeartbeats.ts b/packages/api-derive/src/imOnline/receivedHeartbeats.ts index 3128af4fad..00494c0faa 100644 --- a/packages/api-derive/src/imOnline/receivedHeartbeats.ts +++ b/packages/api-derive/src/imOnline/receivedHeartbeats.ts @@ -10,18 +10,15 @@ import { map, switchMap } from 'rxjs/operators'; import { ApiInterfaceRx } from '@polkadot/api/types'; import { Bytes, Option, u32 } from '@polkadot/types'; -import { overview as stakingOverview } from '../staking'; import { drr } from '../util'; /** * @description Return a boolean array indicating whether the passed accounts had received heartbeats in the current session */ export function receivedHeartbeats (api: ApiInterfaceRx): () => Observable { - const stakingOverviewCall = stakingOverview(api); - return (): Observable => - api.query.imOnline && api.query.imOnline.receivedHeartbeats && api.query.imOnline.authoredBlocks - ? stakingOverviewCall().pipe( + api.query.imOnline?.receivedHeartbeats && api.query.imOnline.authoredBlocks + ? api.derive.staking.overview().pipe( switchMap(({ currentIndex, validators }): Observable<[AccountId[], Option[], u32[]]> => combineLatest([ of(validators), diff --git a/packages/api-derive/src/session/eraLength.ts b/packages/api-derive/src/session/eraLength.ts index 26c4df595e..914d82a147 100644 --- a/packages/api-derive/src/session/eraLength.ts +++ b/packages/api-derive/src/session/eraLength.ts @@ -3,20 +3,18 @@ // of the Apache-2.0 license. See the LICENSE file for details. import { BlockNumber } from '@polkadot/types/interfaces'; +import { DerivedSessionInfo } from '../types'; import { Observable } from 'rxjs'; import { map } from 'rxjs/operators'; import { ApiInterfaceRx } from '@polkadot/api/types'; import { drr } from '../util'; -import { info } from './info'; export function eraLength (api: ApiInterfaceRx): () => Observable { - const infoCall = info(api); - return (): Observable => - infoCall().pipe( - map(({ eraLength }): BlockNumber => eraLength), + api.derive.session.info().pipe( + map(({ eraLength }: DerivedSessionInfo): BlockNumber => eraLength), drr() ); } diff --git a/packages/api-derive/src/session/eraProgress.ts b/packages/api-derive/src/session/eraProgress.ts index 7f790219f9..760123ee9f 100644 --- a/packages/api-derive/src/session/eraProgress.ts +++ b/packages/api-derive/src/session/eraProgress.ts @@ -3,20 +3,18 @@ // of the Apache-2.0 license. See the LICENSE file for details. import { BlockNumber } from '@polkadot/types/interfaces'; +import { DerivedSessionInfo } from '../types'; import { Observable } from 'rxjs'; import { map } from 'rxjs/operators'; import { ApiInterfaceRx } from '@polkadot/api/types'; import { drr } from '../util'; -import { info } from './info'; export function eraProgress (api: ApiInterfaceRx): () => Observable { - const infoCall = info(api); - return (): Observable => - infoCall().pipe( - map(({ eraProgress }): BlockNumber => eraProgress), + api.derive.session.info().pipe( + map(({ eraProgress }: DerivedSessionInfo): BlockNumber => eraProgress), drr() ); } diff --git a/packages/api-derive/src/session/info.ts b/packages/api-derive/src/session/info.ts index 709776f3b4..cbdbdc9e1d 100644 --- a/packages/api-derive/src/session/info.ts +++ b/packages/api-derive/src/session/info.ts @@ -11,8 +11,6 @@ import { ApiInterfaceRx } from '@polkadot/api/types'; import { Option, u64, createType } from '@polkadot/types'; import { drr } from '../util'; -import { bestNumber } from '../chain'; -import { indexes } from './indexes'; type ResultV1Session = [Option, BlockNumber, BlockNumber, SessionIndex]; type ResultV1 = [BlockNumber, DeriveSessionIndexes, ResultV1Session]; @@ -21,14 +19,9 @@ type ResultSlots = [u64, u64, u64, SessionIndex]; type ResultType = [boolean, u64, SessionIndex]; type Result = [ResultType, DeriveSessionIndexes, ResultSlots]; -interface Calls { - bestNumberCall (): Observable; - indexesCall (): Observable; -} - // internal helper to just split the logic - take all inputs, do the calculations and combine function createDerivedV1 ([bestNumber, { currentIndex, validatorCount }, [_lastLengthChange, sessionLength, lastEraLengthChange, sessionsPerEra]]: ResultV1): DerivedSessionInfo { - const lastLengthChange = (_lastLengthChange && _lastLengthChange.unwrapOr(null)) || createType('BlockNumber'); + const lastLengthChange = _lastLengthChange?.unwrapOr(null) || createType('BlockNumber'); const sessionProgress = bestNumber .sub(lastLengthChange) .add(sessionLength) @@ -77,10 +70,10 @@ function createDerivedLatest ([[hasBabe, epochDuration, sessionsPerEra], { curre }; } -function infoV1 (api: ApiInterfaceRx, { bestNumberCall, indexesCall }: Calls): Observable { +function infoV1 (api: ApiInterfaceRx): Observable { return combineLatest([ - bestNumberCall(), - indexesCall(), + api.derive.chain.bestNumber(), + api.derive.session.indexes(), api.queryMulti([ api.query.session.lastLengthChange, api.query.session.sessionLength, @@ -92,8 +85,8 @@ function infoV1 (api: ApiInterfaceRx, { bestNumberCall, indexesCall }: Calls): O ); } -function infoLatestAura (api: ApiInterfaceRx, { indexesCall }: Calls): Observable { - return indexesCall().pipe( +function infoLatestAura (api: ApiInterfaceRx): Observable { + return api.derive.session.indexes().pipe( map((indexes): DerivedSessionInfo => createDerivedLatest([ [false, createType('u64', 1), api.consts.staking.sessionsPerEra as SessionIndex], @@ -104,9 +97,9 @@ function infoLatestAura (api: ApiInterfaceRx, { indexesCall }: Calls): Observabl ); } -function infoLatestBabe (api: ApiInterfaceRx, { indexesCall }: Calls): Observable { +function infoLatestBabe (api: ApiInterfaceRx): Observable { return combineLatest([ - indexesCall(), + api.derive.session.indexes(), api.queryMulti([ api.query.babe.currentSlot, api.query.babe.epochIndex, @@ -128,10 +121,6 @@ function infoLatestBabe (api: ApiInterfaceRx, { indexesCall }: Calls): Observabl * @description Retrieves all the session and era info and calculates specific values on it as the length of the session and eras */ export function info (api: ApiInterfaceRx): () => Observable { - const calls = { - bestNumberCall: bestNumber(api), - indexesCall: indexes(api) - }; const query = api.consts.staking ? api.consts.babe ? infoLatestBabe // 2.x with Babe @@ -139,5 +128,5 @@ export function info (api: ApiInterfaceRx): () => Observable : infoV1; return (): Observable => - query(api, calls).pipe(drr()); + query(api).pipe(drr()); } diff --git a/packages/api-derive/src/session/sessionProgress.ts b/packages/api-derive/src/session/sessionProgress.ts index eded8c991a..88e27b80d4 100644 --- a/packages/api-derive/src/session/sessionProgress.ts +++ b/packages/api-derive/src/session/sessionProgress.ts @@ -3,20 +3,18 @@ // of the Apache-2.0 license. See the LICENSE file for details. import { BlockNumber } from '@polkadot/types/interfaces'; +import { DerivedSessionInfo } from '../types'; import { Observable } from 'rxjs'; import { map } from 'rxjs/operators'; import { ApiInterfaceRx } from '@polkadot/api/types'; import { drr } from '../util'; -import { info } from './info'; export function sessionProgress (api: ApiInterfaceRx): () => Observable { - const infoCall = info(api); - return (): Observable => - infoCall().pipe( - map(({ sessionProgress }): BlockNumber => sessionProgress), + api.derive.session.info().pipe( + map(({ sessionProgress }: DerivedSessionInfo): BlockNumber => sessionProgress), drr() ); } diff --git a/packages/api-derive/src/staking/info.ts b/packages/api-derive/src/staking/info.ts index a69dad5bb9..d084284c79 100644 --- a/packages/api-derive/src/staking/info.ts +++ b/packages/api-derive/src/staking/info.ts @@ -15,17 +15,8 @@ import { createType, Option, Vec } from '@polkadot/types'; import { isUndefined } from '@polkadot/util'; -import { bestNumber } from '../chain/bestNumber'; -import { eraLength } from '../session/eraLength'; -import { recentlyOffline } from './recentlyOffline'; import { drr } from '../util'; -interface Calls { - bestNumberCall (): Observable; - eraLengthCall (): Observable; - recentlyOfflineCall (): Observable; -} - interface ParseInput { accountId: AccountId; bestNumber: BlockNumber; @@ -147,11 +138,11 @@ function parseResult ({ accountId, controllerId, stashId, eraLength, bestNumber, type MultiResultV1 = [Option, Option, [Vec], RewardDestination, Exposure, [ValidatorPrefs] & Codec]; -function retrieveInfoV1 (api: ApiInterfaceRx, { bestNumberCall, eraLengthCall, recentlyOfflineCall }: Calls, accountId: AccountId, stashId: AccountId, controllerId: AccountId): Observable { +function retrieveInfoV1 (api: ApiInterfaceRx, accountId: AccountId, stashId: AccountId, controllerId: AccountId): Observable { return combineLatest([ - bestNumberCall(), - eraLengthCall(), - recentlyOfflineCall(), + api.derive.chain.bestNumber(), + api.derive.session.eraLength(), + api.derive.staking.recentlyOffline(), api.queryMulti([ [api.query.session.nextKeyFor, controllerId], [api.query.staking.ledger, controllerId], @@ -172,10 +163,10 @@ function retrieveInfoV1 (api: ApiInterfaceRx, { bestNumberCall, eraLengthCall, r type MultiResultV2 = [Option, [Vec], RewardDestination, Exposure, [ValidatorPrefs], Option]; -function retrieveInfoV2 (api: ApiInterfaceRx, { bestNumberCall, eraLengthCall }: Calls, accountId: AccountId, stashId: AccountId, controllerId: AccountId): Observable { +function retrieveInfoV2 (api: ApiInterfaceRx, accountId: AccountId, stashId: AccountId, controllerId: AccountId): Observable { return combineLatest([ - bestNumberCall(), - eraLengthCall(), + api.derive.chain.bestNumber(), + api.derive.session.eraLength(), api.query.session.queuedKeys>(), api.queryMulti([ [api.query.staking.ledger, controllerId], @@ -195,25 +186,25 @@ function retrieveInfoV2 (api: ApiInterfaceRx, { bestNumberCall, eraLengthCall }: )); } -function retrieveV1 (api: ApiInterfaceRx, calls: Calls, controllerId: AccountId): Observable { +function retrieveV1 (api: ApiInterfaceRx, controllerId: AccountId): Observable { return api.query.staking .ledger>(controllerId) .pipe( switchMap((stakingLedger): Observable => stakingLedger.isSome - ? retrieveInfoV1(api, calls, controllerId, stakingLedger.unwrap().stash, controllerId) + ? retrieveInfoV1(api, controllerId, stakingLedger.unwrap().stash, controllerId) : of({ accountId: controllerId, nextSessionIds: [], sessionIds: [] }) ) ); } -function retrieveV2 (api: ApiInterfaceRx, calls: Calls, stashId: AccountId): Observable { +function retrieveV2 (api: ApiInterfaceRx, stashId: AccountId): Observable { return api.query.staking .bonded>(stashId) .pipe( switchMap((controllerId): Observable => controllerId.isSome - ? retrieveInfoV2(api, calls, stashId, stashId, controllerId.unwrap()) + ? retrieveInfoV2(api, stashId, stashId, controllerId.unwrap()) : of({ accountId: stashId, nextSessionIds: [], sessionIds: [] }) ) ); @@ -223,15 +214,10 @@ function retrieveV2 (api: ApiInterfaceRx, calls: Calls, stashId: AccountId): Obs * @description From a stash, retrieve the controllerId and fill in all the relevant staking details */ export function info (api: ApiInterfaceRx): (_accountId: Uint8Array | string) => Observable { - const calls = { - eraLengthCall: eraLength(api), - bestNumberCall: bestNumber(api), - recentlyOfflineCall: recentlyOffline(api) - }; const query = api.consts.session ? retrieveV2 : retrieveV1; return (accountId: Uint8Array | string): Observable => - query(api, calls, createType('AccountId', accountId)).pipe(drr()); + query(api, createType('AccountId', accountId)).pipe(drr()); } diff --git a/packages/api-derive/src/staking/infoStash.ts b/packages/api-derive/src/staking/infoStash.ts index d5ae594c4e..eb3fe1eeaf 100644 --- a/packages/api-derive/src/staking/infoStash.ts +++ b/packages/api-derive/src/staking/infoStash.ts @@ -22,7 +22,7 @@ type ResultV2 = [Option, ITuple<[Vec]>, RewardDestination, function parse (stashId: AccountId, [_controllerId, [nominators], rewardDestination, stakers, validatorPrefs, _nextKeys]: Result): DerivedStakingStash { return { controllerId: _controllerId.unwrapOr(undefined), - nextKeys: _nextKeys && _nextKeys.unwrapOr(undefined), + nextKeys: _nextKeys?.unwrapOr(undefined), nominators, rewardDestination, stakers, diff --git a/packages/api-derive/src/staking/overview.ts b/packages/api-derive/src/staking/overview.ts index e3ca632cd2..5becb59928 100644 --- a/packages/api-derive/src/staking/overview.ts +++ b/packages/api-derive/src/staking/overview.ts @@ -11,20 +11,15 @@ import { map, switchMap } from 'rxjs/operators'; import { createType } from '@polkadot/types'; import { drr } from '../util'; -import { indexes as sessionIndexes } from '../session'; -import { validators } from './validators'; /** * @description Retrieve the staking overview, including elected and points earned */ export function overview (api: ApiInterfaceRx): () => Observable { - const sessionIndexesCall = sessionIndexes(api); - const validatorsCall = validators(api); - return (): Observable => combineLatest([ - sessionIndexesCall(), - validatorsCall() + api.derive.session.indexes(), + api.derive.staking.validators() ]).pipe( switchMap(([{ currentEra, currentIndex, validatorCount }, { currentElected, validators }]) => combineLatest([ diff --git a/packages/api-derive/src/types.ts b/packages/api-derive/src/types.ts index 232b7e4d9c..8c722c9a90 100644 --- a/packages/api-derive/src/types.ts +++ b/packages/api-derive/src/types.ts @@ -7,6 +7,8 @@ import { AccountId, AccountIndex, Balance, BalanceLock, BlockNumber, EraIndex, E import BN from 'bn.js'; import { u32 } from '@polkadot/types'; +export type AccountIndexes = Record; + export interface DeriveAccountInfo { accountId?: AccountId; accountIndex?: AccountIndex; diff --git a/packages/api/src/base/Decorate.ts b/packages/api/src/base/Decorate.ts index 60352fce2f..9426386d0a 100644 --- a/packages/api/src/base/Decorate.ts +++ b/packages/api/src/base/Decorate.ts @@ -356,11 +356,15 @@ export default abstract class Decorate extends Events ); } - protected decorateDerive (apiRx: ApiInterfaceRx, decorateMethod: DecorateMethod): DeriveAllSections { + protected decorateDeriveRx (decorateMethod: DecorateMethod): DeriveAllSections<'rxjs', ExactDerive> { // Pull in derive from api-derive - const derive = decorateDerive(apiRx, this._options.derives); + const derive = decorateDerive(this._rx as ApiInterfaceRx, this._options.derives); - return decorateSections(derive, decorateMethod); + return decorateSections<'rxjs', ExactDerive>(derive, decorateMethod); + } + + protected decorateDerive (decorateMethod: DecorateMethod): DeriveAllSections { + return decorateSections(this._rx.derive as DeriveAllSections<'rxjs', ExactDerive>, decorateMethod); } /** diff --git a/packages/api/src/base/Init.ts b/packages/api/src/base/Init.ts index fc9a87b54a..bf65db7bdc 100644 --- a/packages/api/src/base/Init.ts +++ b/packages/api/src/base/Init.ts @@ -4,7 +4,7 @@ import { SignedBlock } from '@polkadot/types/interfaces'; import { RegistryTypes } from '@polkadot/types/types'; -import { ApiBase, ApiInterfaceRx, ApiOptions, ApiTypes, DecorateMethod } from '../types'; +import { ApiBase, ApiOptions, ApiTypes, DecorateMethod } from '../types'; import constantsFromMeta from '@polkadot/api-metadata/consts/fromMetadata'; import extrinsicsFromMeta from '@polkadot/api-metadata/extrinsics/fromMetadata'; @@ -195,7 +195,8 @@ export default abstract class Init extends Decorate; extrinsicType: number; genesisHash: Hash; hasSubscriptions: boolean; diff --git a/packages/rpc-core/src/rxjs/drr.ts b/packages/rpc-core/src/rxjs/drr.ts index 7e67279044..565d1e31b9 100644 --- a/packages/rpc-core/src/rxjs/drr.ts +++ b/packages/rpc-core/src/rxjs/drr.ts @@ -33,7 +33,7 @@ const NOOP = (): void => {}; * * @ignore */ -export const drr = ({ skipChange, skipTimeout }: Options = {}): DrrResult => (source$: Observable): Observable => +export const drr = ({ skipChange = false, skipTimeout = false }: Options = {}): DrrResult => (source$: Observable): Observable => source$.pipe( catchError(ERR), skipChange diff --git a/packages/rpc-core/src/rxjs/refCountDelay.ts b/packages/rpc-core/src/rxjs/refCountDelay.ts index b7c99f6a8d..522097dc6a 100644 --- a/packages/rpc-core/src/rxjs/refCountDelay.ts +++ b/packages/rpc-core/src/rxjs/refCountDelay.ts @@ -2,54 +2,47 @@ // This software may be modified and distributed under the terms // of the Apache-2.0 license. See the LICENSE file for details. -// From https://medium.com/@volkeron/rxjs-unsubscribe-delay-218a9ab2672e (adapted for rxjs 6) -// v6 inspiration from https://github.com/cartant/rxjs-etc/blob/7d7cd21233f67c364fd246ef72077bbe494dd5c2/source/operators/refCountDelay.ts - -import { asapScheduler, ConnectableObservable, MonoTypeOperatorFunction, NEVER, Observable, Subject, Subscription, timer, using } from 'rxjs'; -import { scan, switchMap, tap } from 'rxjs/operators'; +import { asapScheduler, ConnectableObservable, MonoTypeOperatorFunction, Observable, Subscription } from 'rxjs'; const DELAY = 1750; -// subscribe to a source, tracker and increment the counter -function subscribe (source: Observable, counter: Subject, tracker: Observable): [Subscription, Subscription] { - const sourceConn = (source as ConnectableObservable).connect(); - const trackerConn = tracker.subscribe(); +function refCountDelayInner (source: Observable): Observable { + let connected = 0; // 0 = disconnected, 1 = disconnecting, 2 = connecting, 3 = connected + let refCount = 0; + let con = Subscription.EMPTY; + let sched = Subscription.EMPTY; - counter.next(1); + return new Observable((ob) => { + source.subscribe(ob); - return [sourceConn, trackerConn]; + if (refCount++ === 0) { + if (connected === 1) { + connected = 3; + sched.unsubscribe(); + } else { + con = (source as ConnectableObservable).connect(); + connected = 3; + } + } + + return (): void => { + if (--refCount === 0) { + if (connected === 2) { + connected = 0; + sched.unsubscribe(); + } else { + // connected === 3 + connected = 1; + sched = asapScheduler.schedule((): void => { + con.unsubscribe(); + connected = 0; + }, DELAY); + } + } + }; + }); } -// unsubscribe from both the tracker and the source -function unsubscribe (sourceConn: Subscription | undefined, trackerConn: Subscription | undefined): void { - sourceConn && sourceConn.unsubscribe(); - trackerConn && trackerConn.unsubscribe(); -} - -// keep track of references, but only unsubscribe after the configured delay export function refCountDelay (): MonoTypeOperatorFunction { - return (source: Observable): Observable => { - let sourceConn: Subscription | undefined; - let trackerConn: Subscription | undefined; - const counter = new Subject(); - const tracker = counter.pipe( - scan((total, change) => change + total, 0), - switchMap((count) => - count === 0 // when we have a zero count, schedule a unsubscribe - ? timer(DELAY, asapScheduler).pipe(tap((): void => unsubscribe(sourceConn, trackerConn))) - : NEVER - ) - ); - - return using( - () => { - [sourceConn, trackerConn] = subscribe(source, counter, tracker); - - return { - unsubscribe: (): void => counter.next(-1) - }; - }, - (): Observable => source - ); - }; + return refCountDelayInner; }