diff --git a/packages/api-derive/src/democracy/index.ts b/packages/api-derive/src/democracy/index.ts index 7c595c9dff..9ddc1a60df 100644 --- a/packages/api-derive/src/democracy/index.ts +++ b/packages/api-derive/src/democracy/index.ts @@ -4,3 +4,4 @@ export * from './proposals'; export * from './referendums'; +export * from './referendumsActive'; diff --git a/packages/api-derive/src/democracy/referendums.ts b/packages/api-derive/src/democracy/referendums.ts index 3c51769845..2ede05561a 100644 --- a/packages/api-derive/src/democracy/referendums.ts +++ b/packages/api-derive/src/democracy/referendums.ts @@ -3,18 +3,17 @@ // of the Apache-2.0 license. See the LICENSE file for details. import { ApiInterfaceRx } from '@polkadot/api/types'; -import { AccountId, Balance, ReferendumIndex, ReferendumInfo, ReferendumInfoTo239, ReferendumStatus, Vote, Voting, VotingDirectVote } from '@polkadot/types/interfaces'; +import { AccountId, Vote, Voting, VotingDirectVote } from '@polkadot/types/interfaces'; import { DerivedBalancesAccount, DerivedReferendum, DerivedReferendumExt, DerivedReferendumVote } from '../types'; import BN from 'bn.js'; import { Observable, of, combineLatest } from 'rxjs'; import { map, switchMap } from 'rxjs/operators'; -import { Option, Vec } from '@polkadot/types'; +import { Vec } from '@polkadot/types'; import { bnSqrt } from '@polkadot/util'; import { memo } from '../util'; -import { PreImage } from './proposals'; -import { calcVotes, getStatus } from './util'; +import { calcVotes } from './util'; function votesPrev (api: ApiInterfaceRx, referendumId: BN): Observable { return api.query.democracy.votersFor>(referendumId).pipe( @@ -70,101 +69,42 @@ function votesCurr (api: ApiInterfaceRx, referendumId: BN): Observable): Observable { - const status = getStatus(info); - - return status - ? api.query.democracy.preimages(status.proposalHash).pipe( - map((preimage?: PreImage): DerivedReferendum | null => - constructInfo(api, index, status, preimage) - ) +function retrieveVotes (api: ApiInterfaceRx, sqrtElectorate: BN, referendums: DerivedReferendum[]): Observable { + return combineLatest( + referendums.map((referendum): Observable => + api.query.democracy.votingOf + ? votesCurr(api, referendum.index) + : votesPrev(api, referendum.index) + ) + ).pipe( + map((votes) => + referendums.map((referendum, index): DerivedReferendumExt => ({ + ...referendum, + ...calcVotes(sqrtElectorate, referendum, votes[index]) + })) ) - : of(null); -} - -function referendumInfos (api: ApiInterfaceRx, ids: BN[]): Observable { - return api.query.democracy.referendumInfoOf.multi>(ids).pipe( - switchMap((infos): Observable<(DerivedReferendum | null)[]> => - combineLatest( - ids.map((id, index): Observable => - retrieveInfo(api, id, infos[index]) - ) - ) - ), - map((infos) => infos.filter((referendum): referendum is DerivedReferendum => !!referendum)) ); } -function retrieveDerived (api: ApiInterfaceRx, totalIssuance: Balance, earliest: ReferendumIndex, referendumCount: ReferendumIndex): Observable<[BN, DerivedReferendum[]]> { - return combineLatest([ - of(bnSqrt(totalIssuance)), - referendumCount.gt(earliest) - ? referendumInfos(api, [ - ...Array(referendumCount.sub(earliest).toNumber()) - ].map((_, i): BN => earliest.addn(i))) - : of([]) - ]); -} - -function retrieveVotes (api: ApiInterfaceRx, sqrtElectorate: BN, referendums: DerivedReferendum[]): Observable<[BN, DerivedReferendum[], DerivedReferendumVote[][]]> { - return combineLatest([ - of(sqrtElectorate), - of(referendums), - referendums.length - ? combineLatest( - referendums.map((referendum): Observable => - api.query.democracy.votingOf - ? votesCurr(api, referendum.index) - : votesPrev(api, referendum.index) - ) - ) - : of([]) - ]); +function sqrtElectorate (api: ApiInterfaceRx): Observable { + return api.query.balances.totalIssuance().pipe( + map((totalIssuance) => + bnSqrt(totalIssuance) + ) + ); } export function referendums (api: ApiInterfaceRx): () => Observable { return memo((): Observable => - api.query.democracy?.lowestUnbaked - ? api.queryMulti<[Balance, ReferendumIndex, ReferendumIndex]>([ - api.query.balances.totalIssuance, - api.query.democracy.lowestUnbaked, - api.query.democracy.referendumCount - ]).pipe( - switchMap(([totalIssuance, earliest, referendumCount]) => - retrieveDerived(api, totalIssuance, earliest, referendumCount) - ), - switchMap(([sqrtElectorate, referendums]) => - retrieveVotes(api, sqrtElectorate, referendums) - ), - map(([sqrtElectorate, referendums, votes]) => - referendums.map((referendum, index): DerivedReferendumExt => ({ - ...referendum, - ...calcVotes(sqrtElectorate, referendum, votes[index]) - })) - ) + combineLatest([ + sqrtElectorate(api), + api.derive.democracy.referendumsActive() + ]).pipe( + switchMap(([sqrtElectorate, referendums]) => + referendums.length + ? retrieveVotes(api, sqrtElectorate, referendums) + : of([]) ) - : of([]) + ) ); } diff --git a/packages/api-derive/src/democracy/referendumsActive.ts b/packages/api-derive/src/democracy/referendumsActive.ts new file mode 100644 index 0000000000..1433a15ed6 --- /dev/null +++ b/packages/api-derive/src/democracy/referendumsActive.ts @@ -0,0 +1,88 @@ +// 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 { ReferendumIndex, ReferendumInfo, ReferendumInfoTo239, ReferendumStatus } from '@polkadot/types/interfaces'; +import { DerivedReferendum } from '../types'; + +import BN from 'bn.js'; +import { Observable, of, combineLatest } from 'rxjs'; +import { map, switchMap } from 'rxjs/operators'; +import { Option } from '@polkadot/types'; + +import { memo } from '../util'; +import { PreImage } from './proposals'; +import { getStatus } from './util'; + +function constructInfo (api: ApiInterfaceRx, index: BN | number, status: ReferendumStatus | ReferendumInfoTo239, _preimage?: PreImage): DerivedReferendum | null { + const preImage = _preimage?.isSome + ? _preimage.unwrap() + : null; + + return { + index: api.registry.createType('PropIndex', index), + hash: status.proposalHash, + proposal: preImage + ? api.registry.createType('Proposal', preImage[0].toU8a(true)) + : undefined, + preimage: preImage + ? { + at: preImage[3], + balance: preImage[2], + proposer: preImage[1] + } + : undefined, + status + }; +} + +function retrieveInfo (api: ApiInterfaceRx, index: BN | number, info: Option): Observable { + const status = getStatus(info); + + return status + ? api.query.democracy.preimages(status.proposalHash).pipe( + map((preimage?: PreImage): DerivedReferendum | null => + constructInfo(api, index, status, preimage) + ) + ) + : of(null); +} + +function referendumInfos (api: ApiInterfaceRx, ids: BN[]): Observable { + return api.query.democracy.referendumInfoOf.multi>(ids).pipe( + switchMap((infos): Observable<(DerivedReferendum | null)[]> => + combineLatest( + ids.map((id, index): Observable => + retrieveInfo(api, id, infos[index]) + ) + ) + ), + map((infos) => + infos.filter((referendum): referendum is DerivedReferendum => !!referendum) + ) + ); +} + +function retrieveDerived (api: ApiInterfaceRx, earliest: ReferendumIndex, referendumCount: ReferendumIndex): Observable { + const indexes = [...Array(referendumCount.sub(earliest).toNumber())].map((_, i): BN => earliest.addn(i)); + + return referendumInfos(api, indexes); +} + +export function referendumsActive (api: ApiInterfaceRx): () => Observable { + return memo((): Observable => + api.query.democracy?.lowestUnbaked + ? api.queryMulti<[ReferendumIndex, ReferendumIndex]>([ + api.query.democracy.lowestUnbaked, + api.query.democracy.referendumCount + ]).pipe( + switchMap(([earliest, referendumCount]) => + referendumCount.gt(earliest) + ? retrieveDerived(api, earliest, referendumCount) + : of([]) + ) + ) + : of([]) + ); +}