Additional referendum derive cleanups (#2093)

This commit is contained in:
Jaco Greeff
2020-03-25 11:06:16 +01:00
committed by GitHub
parent 5c7f9f1c8a
commit 852e0d4f0b
9 changed files with 242 additions and 159 deletions
+3 -1
View File
@@ -1,6 +1,8 @@
# 1.9.0-beta.x
- Additional cleanups of democracy derives, including support for new Polkadot/Substrate vote retrievals
- Adjust `Error.message` on RPC failures to not include the (already known) method signature
- Additional cleanups and rework of democracy derives, including support for new Polkadot/Substrate vote retrievals
- Internal adjustment of interface types to align with modules (no external impact via `@polkadot/types/interfaces`)
# 1.8.1 Mar 22, 2020
@@ -0,0 +1,51 @@
// 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 { ReferendumInfo, ReferendumInfoTo239, ReferendumStatus } from '@polkadot/types/interfaces';
import { DerivedReferendum } from '../types';
import BN from 'bn.js';
import { Observable, of } from 'rxjs';
import { map } 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
};
}
export function _referendumInfo (api: ApiInterfaceRx): (id: BN, info: Option<ReferendumInfo | ReferendumInfoTo239>) => Observable<DerivedReferendum | null> {
return memo((id: BN, info: Option<ReferendumInfo | ReferendumInfoTo239>): Observable<DerivedReferendum | null> => {
const status = getStatus(info);
return status
? api.query.democracy.preimages(status.proposalHash).pipe(
map((preimage?: PreImage): DerivedReferendum | null =>
constructInfo(api, id, status, preimage)
)
)
: of(null);
});
}
@@ -0,0 +1,84 @@
// 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 { AccountId, Vote, Voting, VotingDirectVote } from '@polkadot/types/interfaces';
import { DerivedBalancesAccount, DerivedReferendum, DerivedReferendumVote, DerivedReferendumVotes } from '../types';
import BN from 'bn.js';
import { Observable, of, combineLatest } from 'rxjs';
import { map, switchMap } from 'rxjs/operators';
import { Vec } from '@polkadot/types';
import { memo } from '../util';
import { calcVotes } from './util';
function votesPrev (api: ApiInterfaceRx, referendumId: BN): Observable<DerivedReferendumVote[]> {
return api.query.democracy.votersFor<Vec<AccountId>>(referendumId).pipe(
switchMap((votersFor): Observable<[Vec<AccountId>, Vote[], DerivedBalancesAccount[]]> =>
combineLatest([
of(votersFor),
votersFor.length
? api.query.democracy.voteOf.multi<Vote>(
votersFor.map((accountId): [BN | number, AccountId] =>
[referendumId, accountId]
)
)
: of([]),
api.derive.balances.votingBalances(votersFor)
])
),
map(([votersFor, votes, balances]): DerivedReferendumVote[] =>
votersFor.map((accountId, index): DerivedReferendumVote => ({
accountId,
balance: balances[index].votingBalance || api.registry.createType('Balance'),
vote: votes[index] || api.registry.createType('Vote')
}))
)
);
}
function votesCurr (api: ApiInterfaceRx, referendumId: BN): Observable<DerivedReferendumVote[]> {
return api.query.democracy.votingOf.entries<Voting>().pipe(
map((allVoting): DerivedReferendumVote[] =>
allVoting
.map(([key, voting]): [AccountId, Voting] => [key.args[0] as AccountId, voting])
// FIXME We are ignoring delegated votes
.filter(([, voting]) => voting.isDirect)
.map(([accountId, voting]): [AccountId, VotingDirectVote[]] => [
accountId,
voting.asDirect.votes.filter(([idx]) => idx.eq(referendumId))
])
.filter(([, directVotes]) => !!directVotes.length)
.reduce((result: DerivedReferendumVote[], [accountId, votes]) =>
// FIXME We are ignoring split votes
votes.reduce((result: DerivedReferendumVote[], [, vote]): DerivedReferendumVote[] => {
if (vote.isStandard) {
result.push({
accountId,
...vote.asStandard
});
}
return result;
}, result), []
)
)
);
}
export function _referendumVotes (api: ApiInterfaceRx): (referendum: DerivedReferendum) => Observable<DerivedReferendumVotes> {
return memo((referendum: DerivedReferendum): Observable<DerivedReferendumVotes> =>
combineLatest([
api.derive.democracy.sqrtElectorate(),
api.query.democracy.votingOf
? votesCurr(api, referendum.index)
: votesPrev(api, referendum.index)
]).pipe(
map(([sqrtElectorate, votes]) =>
calcVotes(sqrtElectorate, referendum, votes)
)
)
);
}
@@ -0,0 +1,22 @@
// 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 { DerivedReferendum, DerivedReferendumVotes } from '../types';
import { Observable, of, combineLatest } from 'rxjs';
import { memo } from '../util';
export function _referendumsVotes (api: ApiInterfaceRx): (referendums: DerivedReferendum[]) => Observable<DerivedReferendumVotes[]> {
return memo((referendums: DerivedReferendum[]): Observable<DerivedReferendumVotes[]> =>
referendums.length
? combineLatest(
referendums.map((referendum): Observable<DerivedReferendumVotes> =>
api.derive.democracy._referendumVotes(referendum)
)
)
: of([])
);
}
@@ -2,6 +2,11 @@
// This software may be modified and distributed under the terms
// of the Apache-2.0 license. See the LICENSE file for details.
export * from './_referendumsVotes';
export * from './_referendumVotes';
export * from './_referendumInfo';
export * from './proposals';
export * from './referendums';
export * from './referendumsActive';
export * from './referendumsInfo';
export * from './sqrtElectorate';
@@ -3,107 +3,27 @@
// of the Apache-2.0 license. See the LICENSE file for details.
import { ApiInterfaceRx } from '@polkadot/api/types';
import { AccountId, Vote, Voting, VotingDirectVote } from '@polkadot/types/interfaces';
import { DerivedBalancesAccount, DerivedReferendum, DerivedReferendumExt, DerivedReferendumVote } from '../types';
import { DerivedReferendumExt } from '../types';
import BN from 'bn.js';
import { Observable, of, combineLatest } from 'rxjs';
import { Observable, combineLatest, of } from 'rxjs';
import { map, switchMap } from 'rxjs/operators';
import { Vec } from '@polkadot/types';
import { bnSqrt } from '@polkadot/util';
import { memo } from '../util';
import { calcVotes } from './util';
function votesPrev (api: ApiInterfaceRx, referendumId: BN): Observable<DerivedReferendumVote[]> {
return api.query.democracy.votersFor<Vec<AccountId>>(referendumId).pipe(
switchMap((votersFor): Observable<[Vec<AccountId>, Vote[], DerivedBalancesAccount[]]> =>
combineLatest([
of(votersFor),
votersFor.length
? api.query.democracy.voteOf.multi<Vote>(
votersFor.map((accountId): [BN | number, AccountId] =>
[referendumId, accountId]
)
)
: of([]),
api.derive.balances.votingBalances(votersFor)
])
),
map(([votersFor, votes, balances]): DerivedReferendumVote[] =>
votersFor.map((accountId, index): DerivedReferendumVote => ({
accountId,
balance: balances[index].votingBalance || api.registry.createType('Balance'),
vote: votes[index] || api.registry.createType('Vote')
}))
)
);
}
function votesCurr (api: ApiInterfaceRx, referendumId: BN): Observable<DerivedReferendumVote[]> {
return api.query.democracy.votingOf.entries<Voting>().pipe(
map((allVoting): DerivedReferendumVote[] =>
allVoting
.map(([key, voting]): [AccountId, Voting] => [key.args[0] as AccountId, voting])
// FIXME We are ignoring delegated votes
.filter(([, voting]) => voting.isDirect)
.map(([accountId, voting]): [AccountId, VotingDirectVote[]] => [
accountId,
voting.asDirect.votes.filter(([idx]) => idx.eq(referendumId))
])
.filter(([, directVotes]) => !!directVotes.length)
.reduce((result: DerivedReferendumVote[], [accountId, votes]) =>
// FIXME We are ignoring split votes
votes.reduce((result: DerivedReferendumVote[], [, vote]): DerivedReferendumVote[] => {
if (vote.isStandard) {
result.push({
accountId,
...vote.asStandard
});
}
return result;
}, result), []
)
)
);
}
function retrieveVotes (api: ApiInterfaceRx, sqrtElectorate: BN, referendums: DerivedReferendum[]): Observable<DerivedReferendumExt[]> {
return combineLatest(
referendums.map((referendum): Observable<DerivedReferendumVote[]> =>
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])
}))
)
);
}
function sqrtElectorate (api: ApiInterfaceRx): Observable<BN> {
return api.query.balances.totalIssuance().pipe(
map((totalIssuance) =>
bnSqrt(totalIssuance)
)
);
}
export function referendums (api: ApiInterfaceRx): () => Observable<DerivedReferendumExt[]> {
return memo((): Observable<DerivedReferendumExt[]> =>
combineLatest([
sqrtElectorate(api),
api.derive.democracy.referendumsActive()
]).pipe(
switchMap(([sqrtElectorate, referendums]) =>
referendums.length
? retrieveVotes(api, sqrtElectorate, referendums)
: of([])
api.derive.democracy.referendumsActive().pipe(
switchMap((referendums) =>
combineLatest([
of(referendums),
api.derive.democracy._referendumsVotes(referendums)
])
),
map(([referendums, votes]) =>
referendums.map((referendum, index): DerivedReferendumExt => ({
...referendum,
...votes[index]
}))
)
)
);
@@ -3,72 +3,14 @@
// 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 { ReferendumIndex } 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 { Observable, of } from 'rxjs';
import { switchMap } from 'rxjs/operators';
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<ReferendumInfo | ReferendumInfoTo239>): Observable<DerivedReferendum | null> {
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<DerivedReferendum[]> {
return api.query.democracy.referendumInfoOf.multi<Option<ReferendumInfo>>(ids).pipe(
switchMap((infos): Observable<(DerivedReferendum | null)[]> =>
combineLatest(
ids.map((id, index): Observable<DerivedReferendum | null> =>
retrieveInfo(api, id, infos[index])
)
)
),
map((infos) =>
infos.filter((referendum): referendum is DerivedReferendum => !!referendum)
)
);
}
function retrieveDerived (api: ApiInterfaceRx, earliest: ReferendumIndex, referendumCount: ReferendumIndex): Observable<DerivedReferendum[]> {
const indexes = [...Array(referendumCount.sub(earliest).toNumber())].map((_, i): BN => earliest.addn(i));
return referendumInfos(api, indexes);
}
export function referendumsActive (api: ApiInterfaceRx): () => Observable<DerivedReferendum[]> {
return memo((): Observable<DerivedReferendum[]> =>
@@ -77,9 +19,11 @@ export function referendumsActive (api: ApiInterfaceRx): () => Observable<Derive
api.query.democracy.lowestUnbaked,
api.query.democracy.referendumCount
]).pipe(
switchMap(([earliest, referendumCount]) =>
referendumCount.gt(earliest)
? retrieveDerived(api, earliest, referendumCount)
switchMap(([first, total]): Observable<DerivedReferendum[]> =>
total.gt(first)
? api.derive.democracy.referendumsInfo(
[...Array(total.sub(first).toNumber())].map((_, i): BN => first.addn(i))
)
: of([])
)
)
@@ -0,0 +1,33 @@
// 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 { ReferendumInfo } 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';
export function referendumsInfo (api: ApiInterfaceRx): (ids: BN[]) => Observable<DerivedReferendum[]> {
return memo((ids: BN[]): Observable<DerivedReferendum[]> =>
ids.length
? api.query.democracy.referendumInfoOf.multi<Option<ReferendumInfo>>(ids).pipe(
switchMap((infos): Observable<(DerivedReferendum | null)[]> =>
combineLatest(
ids.map((id, index): Observable<DerivedReferendum | null> =>
api.derive.democracy._referendumInfo(id, infos[index])
)
)
),
map((infos) =>
infos.filter((referendum): referendum is DerivedReferendum => !!referendum)
)
)
: of([])
);
}
@@ -0,0 +1,22 @@
// 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 BN from 'bn.js';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';
import { bnSqrt } from '@polkadot/util';
import { memo } from '../util';
export function sqrtElectorate (api: ApiInterfaceRx): () => Observable<BN> {
return memo((): Observable<BN> =>
api.query.balances.totalIssuance().pipe(
map((totalIssuance) =>
bnSqrt(totalIssuance)
)
)
);
}