Move api-derive to correct location (#4343)
* Move api-derive to correct location * Adjust (linting ok) * No composite * ApiDerive split * Adjust * Still WIP, getting closer * It lints * derive build issues sorted * It builds :) * Add system_health to mock * api-augment usage * Cleanups * Adjust paths * linting * Some <'s * Revert <>'s * linting * ordering * Augmentation tweaks * yarn.lock * export
This commit is contained in:
@@ -0,0 +1,110 @@
|
||||
// Copyright 2017-2021 @polkadot/api-derive authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import type { Observable } from 'rxjs';
|
||||
import type { Option, Vec } from '@polkadot/types';
|
||||
import type { BlockNumber, Hash, ReferendumIndex } from '@polkadot/types/interfaces';
|
||||
import type { PalletSchedulerScheduledV2 } from '@polkadot/types/lookup';
|
||||
import type { ITuple } from '@polkadot/types/types';
|
||||
import type { DeriveApi, DeriveDispatch, DeriveProposalImage } from '../types';
|
||||
|
||||
import { catchError, combineLatest, map, of, switchMap } from 'rxjs';
|
||||
|
||||
import { isFunction, stringToHex } from '@polkadot/util';
|
||||
|
||||
import { memo } from '../util';
|
||||
|
||||
const DEMOCRACY_ID = stringToHex('democrac');
|
||||
|
||||
interface SchedulerInfo {
|
||||
at: BlockNumber;
|
||||
imageHash: Hash;
|
||||
index: ReferendumIndex;
|
||||
}
|
||||
|
||||
function queryQueue (api: DeriveApi): Observable<DeriveDispatch[]> {
|
||||
return api.query.democracy.dispatchQueue<Vec<ITuple<[BlockNumber, Hash, ReferendumIndex]>>>().pipe(
|
||||
switchMap((dispatches) =>
|
||||
combineLatest([
|
||||
of(dispatches),
|
||||
api.derive.democracy.preimages(
|
||||
dispatches.map(([, hash]) => hash))
|
||||
])
|
||||
),
|
||||
map(([dispatches, images]) =>
|
||||
dispatches.map(([at, imageHash, index], dispatchIndex): DeriveDispatch => ({
|
||||
at,
|
||||
image: images[dispatchIndex],
|
||||
imageHash,
|
||||
index
|
||||
}))
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
function schedulerEntries (api: DeriveApi): Observable<[BlockNumber[], (Option<PalletSchedulerScheduledV2>[] | null)[]]> {
|
||||
// We don't get entries, but rather we get the keys (triggered via finished referendums) and
|
||||
// the subscribe to those keys - this means we pickup when the schedulers actually executes
|
||||
// at a block, the entry for that block will become empty
|
||||
return api.derive.democracy.referendumsFinished().pipe(
|
||||
switchMap(() =>
|
||||
api.query.scheduler.agenda.keys()
|
||||
),
|
||||
switchMap((keys) => {
|
||||
const blockNumbers = keys.map(({ args: [blockNumber] }) => blockNumber);
|
||||
|
||||
return blockNumbers.length
|
||||
? combineLatest([
|
||||
of(blockNumbers),
|
||||
// this should simply be api.query.scheduler.agenda.multi,
|
||||
// however we have had cases on Darwinia where the indices have moved around after an
|
||||
// upgrade, which results in invalid on-chain data
|
||||
combineLatest(blockNumbers.map((blockNumber) =>
|
||||
// this does create an issue since it discards all at that block
|
||||
api.query.scheduler.agenda(blockNumber).pipe(catchError(() => of(null)))
|
||||
))
|
||||
])
|
||||
: of<[BlockNumber[], null[]]>([[], []]);
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
function queryScheduler (api: DeriveApi): Observable<DeriveDispatch[]> {
|
||||
return schedulerEntries(api).pipe(
|
||||
switchMap(([blockNumbers, agendas]): Observable<[SchedulerInfo[], (DeriveProposalImage | undefined)[]]> => {
|
||||
const result: SchedulerInfo[] = [];
|
||||
|
||||
blockNumbers.forEach((at, index): void => {
|
||||
(agendas[index] || []).filter((o) => o.isSome).forEach((o): void => {
|
||||
const scheduled = o.unwrap();
|
||||
|
||||
if (scheduled.maybeId.isSome) {
|
||||
const id = scheduled.maybeId.unwrap().toHex();
|
||||
|
||||
id.startsWith(DEMOCRACY_ID) && result.push({ at, imageHash: scheduled.call.args[0] as Hash, index: api.registry.createType('(u64, ReferendumIndex)', id)[1] });
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
return combineLatest([
|
||||
of(result),
|
||||
result.length
|
||||
? api.derive.democracy.preimages(result.map(({ imageHash }) => imageHash))
|
||||
: of([])
|
||||
]);
|
||||
}),
|
||||
map(([infos, images]): DeriveDispatch[] =>
|
||||
infos.map((info, index) => ({ ...info, image: images[index] }))
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
export function dispatchQueue (instanceId: string, api: DeriveApi): () => Observable<DeriveDispatch[]> {
|
||||
return memo(instanceId, (): Observable<DeriveDispatch[]> =>
|
||||
isFunction(api.query.scheduler?.agenda)
|
||||
? queryScheduler(api)
|
||||
: api.query.democracy.dispatchQueue
|
||||
? queryQueue(api)
|
||||
: of([])
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
// Copyright 2017-2021 @polkadot/api-derive authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
export * from './dispatchQueue';
|
||||
export * from './locks';
|
||||
export * from './nextExternal';
|
||||
export * from './preimage';
|
||||
export * from './preimages';
|
||||
export * from './proposals';
|
||||
export * from './referendumIds';
|
||||
export * from './referendums';
|
||||
export * from './referendumsActive';
|
||||
export * from './referendumsFinished';
|
||||
export * from './referendumsInfo';
|
||||
export * from './sqrtElectorate';
|
||||
@@ -0,0 +1,104 @@
|
||||
// Copyright 2017-2021 @polkadot/api-derive authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import type { Observable } from 'rxjs';
|
||||
import type { AccountId, ReferendumInfoTo239, Vote } from '@polkadot/types/interfaces';
|
||||
import type { PalletDemocracyReferendumInfo, PalletDemocracyVoteVoting } from '@polkadot/types/lookup';
|
||||
import type { BN } from '@polkadot/util';
|
||||
import type { DeriveApi, DeriveDemocracyLock } from '../types';
|
||||
|
||||
import { map, of, switchMap } from 'rxjs';
|
||||
|
||||
import { BN_ZERO, isUndefined } from '@polkadot/util';
|
||||
|
||||
import { memo } from '../util';
|
||||
|
||||
type ReferendumInfoFinished = PalletDemocracyReferendumInfo['asFinished'];
|
||||
type VotingDelegating = PalletDemocracyVoteVoting['asDelegating'];
|
||||
type VotingDirect = PalletDemocracyVoteVoting['asDirect'];
|
||||
type VotingDirectVote = VotingDirect['votes'][0];
|
||||
|
||||
const LOCKUPS = [0, 1, 2, 4, 8, 16, 32];
|
||||
|
||||
function parseEnd (api: DeriveApi, vote: Vote, { approved, end }: ReferendumInfoFinished): [BN, BN] {
|
||||
return [
|
||||
end,
|
||||
(approved.isTrue && vote.isAye) || (approved.isFalse && vote.isNay)
|
||||
? end.add(
|
||||
(
|
||||
api.consts.democracy.voteLockingPeriod ||
|
||||
api.consts.democracy.enactmentPeriod
|
||||
).muln(LOCKUPS[vote.conviction.index])
|
||||
)
|
||||
: BN_ZERO
|
||||
];
|
||||
}
|
||||
|
||||
function parseLock (api: DeriveApi, [referendumId, accountVote]: VotingDirectVote, referendum: PalletDemocracyReferendumInfo): DeriveDemocracyLock {
|
||||
const { balance, vote } = accountVote.asStandard;
|
||||
const [referendumEnd, unlockAt] = referendum.isFinished
|
||||
? parseEnd(api, vote, referendum.asFinished)
|
||||
: [BN_ZERO, BN_ZERO];
|
||||
|
||||
return { balance, isDelegated: false, isFinished: referendum.isFinished, referendumEnd, referendumId, unlockAt, vote };
|
||||
}
|
||||
|
||||
function delegateLocks (api: DeriveApi, { balance, conviction, target }: VotingDelegating): Observable<DeriveDemocracyLock[]> {
|
||||
return api.derive.democracy.locks(target).pipe(
|
||||
map((available): DeriveDemocracyLock[] =>
|
||||
available.map(({ isFinished, referendumEnd, referendumId, unlockAt, vote }): DeriveDemocracyLock => ({
|
||||
balance,
|
||||
isDelegated: true,
|
||||
isFinished,
|
||||
referendumEnd,
|
||||
referendumId,
|
||||
unlockAt: unlockAt.isZero()
|
||||
? unlockAt
|
||||
: referendumEnd.add(
|
||||
(
|
||||
api.consts.democracy.voteLockingPeriod ||
|
||||
api.consts.democracy.enactmentPeriod
|
||||
).muln(LOCKUPS[conviction.index])
|
||||
),
|
||||
vote: api.registry.createType('Vote', { aye: vote.isAye, conviction })
|
||||
}))
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
function directLocks (api: DeriveApi, { votes }: VotingDirect): Observable<DeriveDemocracyLock[]> {
|
||||
if (!votes.length) {
|
||||
return of([]);
|
||||
}
|
||||
|
||||
return api.query.democracy.referendumInfoOf.multi(votes.map(([referendumId]) => referendumId)).pipe(
|
||||
map((referendums) =>
|
||||
votes
|
||||
.map((vote, index): [VotingDirectVote, PalletDemocracyReferendumInfo | ReferendumInfoTo239 | null] =>
|
||||
[vote, referendums[index].unwrapOr(null)]
|
||||
)
|
||||
.filter((item): item is [VotingDirectVote, PalletDemocracyReferendumInfo] =>
|
||||
!!item[1] && isUndefined((item[1] as ReferendumInfoTo239).end) && item[0][1].isStandard
|
||||
)
|
||||
.map(([directVote, referendum]) =>
|
||||
parseLock(api, directVote, referendum)
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
export function locks (instanceId: string, api: DeriveApi): (accountId: string | AccountId) => Observable<DeriveDemocracyLock[]> {
|
||||
return memo(instanceId, (accountId: string | AccountId): Observable<DeriveDemocracyLock[]> =>
|
||||
api.query.democracy.votingOf
|
||||
? api.query.democracy.votingOf(accountId).pipe(
|
||||
switchMap((voting): Observable<DeriveDemocracyLock[]> =>
|
||||
voting.isDirect
|
||||
? directLocks(api, voting.asDirect)
|
||||
: voting.isDelegating
|
||||
? delegateLocks(api, voting.asDelegating)
|
||||
: of([])
|
||||
)
|
||||
)
|
||||
: of([])
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
// Copyright 2017-2021 @polkadot/api-derive authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import type { Observable } from 'rxjs';
|
||||
import type { Option } from '@polkadot/types';
|
||||
import type { H256 } from '@polkadot/types/interfaces';
|
||||
import type { PalletDemocracyVoteThreshold } from '@polkadot/types/lookup';
|
||||
import type { ITuple } from '@polkadot/types/types';
|
||||
import type { DeriveApi, DeriveProposalExternal } from '../types';
|
||||
|
||||
import { map, of, switchMap } from 'rxjs';
|
||||
|
||||
import { memo } from '../util';
|
||||
|
||||
function withImage (api: DeriveApi, nextOpt: Option<ITuple<[H256, PalletDemocracyVoteThreshold]>>): Observable<DeriveProposalExternal | null> {
|
||||
if (nextOpt.isNone) {
|
||||
return of(null);
|
||||
}
|
||||
|
||||
const [imageHash, threshold] = nextOpt.unwrap();
|
||||
|
||||
return api.derive.democracy.preimage(imageHash).pipe(
|
||||
map((image): DeriveProposalExternal => ({
|
||||
image,
|
||||
imageHash,
|
||||
threshold
|
||||
}))
|
||||
);
|
||||
}
|
||||
|
||||
export function nextExternal (instanceId: string, api: DeriveApi): () => Observable<DeriveProposalExternal | null> {
|
||||
return memo(instanceId, (): Observable<DeriveProposalExternal | null> =>
|
||||
api.query.democracy?.nextExternal
|
||||
? api.query.democracy.nextExternal().pipe(
|
||||
switchMap((nextOpt) => withImage(api, nextOpt))
|
||||
)
|
||||
: of(null)
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
// Copyright 2017-2021 @polkadot/api-derive authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import type { Observable } from 'rxjs';
|
||||
import type { Hash } from '@polkadot/types/interfaces';
|
||||
import type { DeriveApi, DeriveProposalImage } from '../types';
|
||||
|
||||
import { map } from 'rxjs';
|
||||
|
||||
import { memo } from '../util';
|
||||
import { parseImage } from './util';
|
||||
|
||||
export function preimage (instanceId: string, api: DeriveApi): (hash: Hash) => Observable<DeriveProposalImage | undefined> {
|
||||
return memo(instanceId, (hash: Hash): Observable<DeriveProposalImage | undefined> =>
|
||||
api.query.democracy.preimages(hash).pipe(
|
||||
map((imageOpt) => parseImage(api, imageOpt))
|
||||
)
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
// Copyright 2017-2021 @polkadot/api-derive authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import type { Observable } from 'rxjs';
|
||||
import type { Hash } from '@polkadot/types/interfaces';
|
||||
import type { DeriveApi, DeriveProposalImage } from '../types';
|
||||
|
||||
import { map, of } from 'rxjs';
|
||||
|
||||
import { memo } from '../util';
|
||||
import { parseImage } from './util';
|
||||
|
||||
export function preimages (instanceId: string, api: DeriveApi): (hashes: Hash[]) => Observable<(DeriveProposalImage | undefined)[]> {
|
||||
return memo(instanceId, (hashes: Hash[]): Observable<(DeriveProposalImage | undefined)[]> =>
|
||||
hashes.length
|
||||
? api.query.democracy.preimages.multi(hashes).pipe(
|
||||
map((images): (DeriveProposalImage | undefined)[] =>
|
||||
images.map((imageOpt) => parseImage(api, imageOpt))
|
||||
)
|
||||
)
|
||||
: of([])
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
// Copyright 2017-2021 @polkadot/api-derive authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import type { Observable } from 'rxjs';
|
||||
import type { Option, Vec } from '@polkadot/types';
|
||||
import type { AccountId, Balance, Hash, PropIndex } from '@polkadot/types/interfaces';
|
||||
import type { ITuple } from '@polkadot/types/types';
|
||||
import type { DeriveApi, DeriveProposal, DeriveProposalImage } from '../types';
|
||||
|
||||
import { combineLatest, map, of, switchMap } from 'rxjs';
|
||||
|
||||
import { isFunction } from '@polkadot/util';
|
||||
|
||||
import { memo } from '../util';
|
||||
|
||||
type DepositorsNew = Option<ITuple<[Vec<AccountId>, Balance]>>;
|
||||
type DepositorsOld = Option<ITuple<[Balance, Vec<AccountId>]>>;
|
||||
type Depositors = DepositorsNew | DepositorsOld;
|
||||
type Proposals = ITuple<[PropIndex, Hash, AccountId]>[];
|
||||
type Result = [Proposals, (DeriveProposalImage | undefined)[], Depositors[]];
|
||||
|
||||
function isNewDepositors (depositors: ITuple<[Vec<AccountId>, Balance]> | ITuple<[Balance, Vec<AccountId>]>): depositors is ITuple<[Vec<AccountId>, Balance]> {
|
||||
// Detect balance...
|
||||
// eslint-disable-next-line @typescript-eslint/unbound-method
|
||||
return isFunction((depositors[1] as Balance).mul);
|
||||
}
|
||||
|
||||
function parse ([proposals, images, optDepositors]: Result): DeriveProposal[] {
|
||||
return proposals
|
||||
.filter(([, , proposer], index): boolean =>
|
||||
!!(optDepositors[index]?.isSome) && !proposer.isEmpty
|
||||
)
|
||||
.map(([index, imageHash, proposer], proposalIndex): DeriveProposal => {
|
||||
const depositors = optDepositors[proposalIndex].unwrap();
|
||||
|
||||
return {
|
||||
...(
|
||||
isNewDepositors(depositors)
|
||||
? { balance: depositors[1], seconds: depositors[0] }
|
||||
: { balance: depositors[0], seconds: depositors[1] }
|
||||
),
|
||||
image: images[proposalIndex],
|
||||
imageHash,
|
||||
index,
|
||||
proposer
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
export function proposals (instanceId: string, api: DeriveApi): () => Observable<DeriveProposal[]> {
|
||||
return memo(instanceId, (): Observable<DeriveProposal[]> =>
|
||||
isFunction(api.query.democracy?.publicProps) && isFunction(api.query.democracy?.preimages)
|
||||
? api.query.democracy.publicProps().pipe(
|
||||
switchMap((proposals) =>
|
||||
proposals.length
|
||||
? combineLatest([
|
||||
of(proposals),
|
||||
api.derive.democracy.preimages(
|
||||
proposals.map(([, hash]) => hash)),
|
||||
api.query.democracy.depositOf.multi(
|
||||
proposals.map(([index]) => index))
|
||||
])
|
||||
: of<Result>([[], [], []])
|
||||
),
|
||||
map(parse)
|
||||
)
|
||||
: of([])
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
// Copyright 2017-2021 @polkadot/api-derive authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import type { Observable } from 'rxjs';
|
||||
import type { ReferendumIndex } from '@polkadot/types/interfaces';
|
||||
import type { BN } from '@polkadot/util';
|
||||
import type { DeriveApi } from '../types';
|
||||
|
||||
import { map, of } from 'rxjs';
|
||||
|
||||
import { memo } from '../util';
|
||||
|
||||
export function referendumIds (instanceId: string, api: DeriveApi): () => Observable<BN[]> {
|
||||
return memo(instanceId, (): Observable<BN[]> =>
|
||||
api.query.democracy?.lowestUnbaked
|
||||
? api.queryMulti<[ReferendumIndex, ReferendumIndex]>([
|
||||
api.query.democracy.lowestUnbaked,
|
||||
api.query.democracy.referendumCount
|
||||
]).pipe(
|
||||
map(([first, total]): BN[] =>
|
||||
total.gt(first)
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||
? [...Array(total.sub(first).toNumber())].map((_, i): BN => first.addn(i))
|
||||
: []
|
||||
)
|
||||
)
|
||||
: of([])
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
// Copyright 2017-2021 @polkadot/api-derive authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import type { Observable } from 'rxjs';
|
||||
import type { DeriveApi, DeriveReferendumExt } from '../types';
|
||||
|
||||
import { combineLatest, map, of, switchMap } from 'rxjs';
|
||||
|
||||
import { memo } from '../util';
|
||||
|
||||
export function referendums (instanceId: string, api: DeriveApi): () => Observable<DeriveReferendumExt[]> {
|
||||
return memo(instanceId, (): Observable<DeriveReferendumExt[]> =>
|
||||
api.derive.democracy.referendumsActive().pipe(
|
||||
switchMap((referendums) =>
|
||||
referendums.length
|
||||
? combineLatest([
|
||||
of(referendums),
|
||||
api.derive.democracy._referendumsVotes(referendums)
|
||||
])
|
||||
: of([[], []])
|
||||
),
|
||||
map(([referendums, votes]) =>
|
||||
referendums.map((referendum, index): DeriveReferendumExt => ({
|
||||
...referendum,
|
||||
...votes[index]
|
||||
}))
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
// Copyright 2017-2021 @polkadot/api-derive authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import type { Observable } from 'rxjs';
|
||||
import type { DeriveApi, DeriveReferendum } from '../types';
|
||||
|
||||
import { of, switchMap } from 'rxjs';
|
||||
|
||||
import { memo } from '../util';
|
||||
|
||||
export function referendumsActive (instanceId: string, api: DeriveApi): () => Observable<DeriveReferendum[]> {
|
||||
return memo(instanceId, (): Observable<DeriveReferendum[]> =>
|
||||
api.derive.democracy.referendumIds().pipe(
|
||||
switchMap((ids): Observable<DeriveReferendum[]> =>
|
||||
ids.length
|
||||
? api.derive.democracy.referendumsInfo(ids)
|
||||
: of([])
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
// Copyright 2017-2021 @polkadot/api-derive authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import type { Observable } from 'rxjs';
|
||||
import type { PalletDemocracyReferendumInfo } from '@polkadot/types/lookup';
|
||||
import type { DeriveApi } from '../types';
|
||||
|
||||
import { map, switchMap } from 'rxjs';
|
||||
|
||||
import { memo } from '../util';
|
||||
|
||||
type ReferendumInfoFinished = PalletDemocracyReferendumInfo['asFinished'];
|
||||
|
||||
export function referendumsFinished (instanceId: string, api: DeriveApi): () => Observable<ReferendumInfoFinished[]> {
|
||||
return memo(instanceId, (): Observable<ReferendumInfoFinished[]> =>
|
||||
api.derive.democracy.referendumIds().pipe(
|
||||
switchMap((ids) =>
|
||||
api.query.democracy.referendumInfoOf.multi(ids)
|
||||
),
|
||||
map((infos): ReferendumInfoFinished[] =>
|
||||
infos
|
||||
.map((o) => o.unwrapOr(null))
|
||||
.filter((info): info is PalletDemocracyReferendumInfo => !!info && info.isFinished)
|
||||
.map((info) => info.asFinished)
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,164 @@
|
||||
// Copyright 2017-2021 @polkadot/api-derive authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import type { Observable } from 'rxjs';
|
||||
import type { Option, Vec } from '@polkadot/types';
|
||||
import type { AccountId, ReferendumInfoTo239, Vote } from '@polkadot/types/interfaces';
|
||||
import type { PalletDemocracyReferendumInfo, PalletDemocracyVoteVoting } from '@polkadot/types/lookup';
|
||||
import type { BN } from '@polkadot/util';
|
||||
import type { DeriveApi, DeriveBalancesAccount, DeriveReferendum, DeriveReferendumVote, DeriveReferendumVotes } from '../types';
|
||||
|
||||
import { combineLatest, map, of, switchMap } from 'rxjs';
|
||||
|
||||
import { isFunction } from '@polkadot/util';
|
||||
|
||||
import { memo } from '../util';
|
||||
import { calcVotes, getStatus, parseImage } from './util';
|
||||
|
||||
type VotingDelegating = PalletDemocracyVoteVoting['asDelegating'];
|
||||
type VotingDirect = PalletDemocracyVoteVoting['asDirect'];
|
||||
type VotingDirectVote = VotingDirect['votes'][0];
|
||||
|
||||
function votesPrev (api: DeriveApi, referendumId: BN): Observable<DeriveReferendumVote[]> {
|
||||
return api.query.democracy.votersFor<Vec<AccountId>>(referendumId).pipe(
|
||||
switchMap((votersFor): Observable<[Vec<AccountId>, Vote[], DeriveBalancesAccount[]]> =>
|
||||
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]): DeriveReferendumVote[] =>
|
||||
votersFor.map((accountId, index): DeriveReferendumVote => ({
|
||||
accountId,
|
||||
balance: balances[index].votingBalance || api.registry.createType('Balance'),
|
||||
isDelegating: false,
|
||||
vote: votes[index] || api.registry.createType('Vote')
|
||||
}))
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
function extractVotes (mapped: [AccountId, PalletDemocracyVoteVoting][], referendumId: BN): DeriveReferendumVote[] {
|
||||
return mapped
|
||||
.filter(([, voting]) => voting.isDirect)
|
||||
.map(([accountId, voting]): [AccountId, VotingDirectVote[]] => [
|
||||
accountId,
|
||||
voting.asDirect.votes.filter(([idx]) => idx.eq(referendumId))
|
||||
])
|
||||
.filter(([, directVotes]) => !!directVotes.length)
|
||||
.reduce((result: DeriveReferendumVote[], [accountId, votes]) =>
|
||||
// FIXME We are ignoring split votes
|
||||
votes.reduce((result: DeriveReferendumVote[], [, vote]): DeriveReferendumVote[] => {
|
||||
if (vote.isStandard) {
|
||||
result.push({
|
||||
accountId,
|
||||
isDelegating: false,
|
||||
...vote.asStandard
|
||||
});
|
||||
}
|
||||
|
||||
return result;
|
||||
}, result), []
|
||||
);
|
||||
}
|
||||
|
||||
function votesCurr (api: DeriveApi, referendumId: BN): Observable<DeriveReferendumVote[]> {
|
||||
return api.query.democracy.votingOf.entries().pipe(
|
||||
map((allVoting): DeriveReferendumVote[] => {
|
||||
const mapped = allVoting.map(([{ args: [accountId] }, voting]): [AccountId, PalletDemocracyVoteVoting] => [accountId, voting]);
|
||||
const votes = extractVotes(mapped, referendumId);
|
||||
const delegations = mapped
|
||||
.filter(([, voting]) => voting.isDelegating)
|
||||
.map(([accountId, voting]): [AccountId, VotingDelegating] => [accountId, voting.asDelegating]);
|
||||
|
||||
// add delegations
|
||||
delegations.forEach(([accountId, { balance, conviction, target }]): void => {
|
||||
// Are we delegating to a delegator
|
||||
const toDelegator = delegations.find(([accountId]) => accountId.eq(target));
|
||||
const to = votes.find(({ accountId }) => accountId.eq(toDelegator ? toDelegator[0] : target));
|
||||
|
||||
// this delegation has a target
|
||||
if (to) {
|
||||
votes.push({
|
||||
accountId,
|
||||
balance,
|
||||
isDelegating: true,
|
||||
vote: api.registry.createType('Vote', { aye: to.vote.isAye, conviction })
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return votes;
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
export function _referendumVotes (instanceId: string, api: DeriveApi): (referendum: DeriveReferendum) => Observable<DeriveReferendumVotes> {
|
||||
return memo(instanceId, (referendum: DeriveReferendum): Observable<DeriveReferendumVotes> =>
|
||||
combineLatest([
|
||||
api.derive.democracy.sqrtElectorate(),
|
||||
isFunction(api.query.democracy.votingOf)
|
||||
? votesCurr(api, referendum.index)
|
||||
: votesPrev(api, referendum.index)
|
||||
]).pipe(
|
||||
map(([sqrtElectorate, votes]) =>
|
||||
calcVotes(sqrtElectorate, referendum, votes)
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
export function _referendumsVotes (instanceId: string, api: DeriveApi): (referendums: DeriveReferendum[]) => Observable<DeriveReferendumVotes[]> {
|
||||
return memo(instanceId, (referendums: DeriveReferendum[]): Observable<DeriveReferendumVotes[]> =>
|
||||
referendums.length
|
||||
? combineLatest(
|
||||
referendums.map((referendum): Observable<DeriveReferendumVotes> =>
|
||||
api.derive.democracy._referendumVotes(referendum)
|
||||
)
|
||||
)
|
||||
: of([])
|
||||
);
|
||||
}
|
||||
|
||||
export function _referendumInfo (instanceId: string, api: DeriveApi): (index: BN, info: Option<PalletDemocracyReferendumInfo | ReferendumInfoTo239>) => Observable<DeriveReferendum | null> {
|
||||
return memo(instanceId, (index: BN, info: Option<PalletDemocracyReferendumInfo | ReferendumInfoTo239>): Observable<DeriveReferendum | null> => {
|
||||
const status = getStatus(info);
|
||||
|
||||
return status
|
||||
? api.query.democracy.preimages(status.proposalHash).pipe(
|
||||
map((preimage): DeriveReferendum => ({
|
||||
image: parseImage(api, preimage),
|
||||
imageHash: status.proposalHash,
|
||||
index: api.registry.createType('ReferendumIndex', index),
|
||||
status
|
||||
}))
|
||||
)
|
||||
: of(null);
|
||||
});
|
||||
}
|
||||
|
||||
export function referendumsInfo (instanceId: string, api: DeriveApi): (ids: BN[]) => Observable<DeriveReferendum[]> {
|
||||
return memo(instanceId, (ids: BN[]): Observable<DeriveReferendum[]> =>
|
||||
ids.length
|
||||
? api.query.democracy.referendumInfoOf.multi(ids).pipe(
|
||||
switchMap((infos): Observable<(DeriveReferendum | null)[]> =>
|
||||
combineLatest(
|
||||
ids.map((id, index): Observable<DeriveReferendum | null> =>
|
||||
api.derive.democracy._referendumInfo(id, infos[index])
|
||||
)
|
||||
)
|
||||
),
|
||||
map((infos) =>
|
||||
infos.filter((referendum): referendum is DeriveReferendum => !!referendum)
|
||||
)
|
||||
)
|
||||
: of([])
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
// Copyright 2017-2021 @polkadot/api-derive authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import type { Observable } from 'rxjs';
|
||||
import type { BN } from '@polkadot/util';
|
||||
import type { DeriveApi } from '../types';
|
||||
|
||||
import { map } from 'rxjs';
|
||||
|
||||
import { bnSqrt } from '@polkadot/util';
|
||||
|
||||
import { memo } from '../util';
|
||||
|
||||
export function sqrtElectorate (instanceId: string, api: DeriveApi): () => Observable<BN> {
|
||||
return memo(instanceId, (): Observable<BN> =>
|
||||
api.query.balances.totalIssuance().pipe(
|
||||
map(bnSqrt)
|
||||
)
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
// Copyright 2017-2021 @polkadot/api-derive authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import type { Vec } from '@polkadot/types';
|
||||
import type { AccountId, Balance, BlockNumber, Hash, PropIndex, Proposal, ReferendumIndex, ReferendumInfoTo239, Vote } from '@polkadot/types/interfaces';
|
||||
import type { PalletDemocracyReferendumStatus, PalletDemocracyVoteThreshold } from '@polkadot/types/lookup';
|
||||
import type { BN } from '@polkadot/util';
|
||||
|
||||
export interface AtBlock {
|
||||
at: BlockNumber;
|
||||
}
|
||||
|
||||
export interface DeriveDemocracyLock {
|
||||
balance: Balance;
|
||||
isDelegated: boolean;
|
||||
isFinished: boolean;
|
||||
referendumEnd: BN;
|
||||
referendumId: ReferendumIndex;
|
||||
unlockAt: BN;
|
||||
vote: Vote;
|
||||
}
|
||||
|
||||
export interface DeriveProposalImage extends AtBlock {
|
||||
balance: Balance;
|
||||
proposal?: Proposal;
|
||||
proposer: AccountId;
|
||||
}
|
||||
|
||||
export interface DeriveDispatch extends AtBlock {
|
||||
index: ReferendumIndex;
|
||||
imageHash: Hash;
|
||||
image?: DeriveProposalImage;
|
||||
}
|
||||
|
||||
export interface DeriveProposal {
|
||||
balance?: Balance;
|
||||
index: PropIndex;
|
||||
image?: DeriveProposalImage;
|
||||
imageHash: Hash;
|
||||
proposer: AccountId;
|
||||
seconds: Vec<AccountId>;
|
||||
}
|
||||
|
||||
export interface DeriveProposalExternal {
|
||||
image?: DeriveProposalImage;
|
||||
imageHash: Hash;
|
||||
threshold: PalletDemocracyVoteThreshold;
|
||||
}
|
||||
|
||||
export interface DeriveReferendum {
|
||||
index: ReferendumIndex;
|
||||
image?: DeriveProposalImage;
|
||||
imageHash: Hash;
|
||||
status: PalletDemocracyReferendumStatus | ReferendumInfoTo239;
|
||||
}
|
||||
|
||||
export interface DeriveReferendumVote {
|
||||
accountId: AccountId;
|
||||
balance: Balance;
|
||||
isDelegating: boolean;
|
||||
vote: Vote;
|
||||
}
|
||||
|
||||
export interface DeriveReferendumVoteState {
|
||||
allAye: DeriveReferendumVote[];
|
||||
allNay: DeriveReferendumVote[];
|
||||
voteCount: number;
|
||||
voteCountAye: number;
|
||||
voteCountNay: number;
|
||||
votedAye: BN;
|
||||
votedNay: BN;
|
||||
votedTotal: BN;
|
||||
}
|
||||
|
||||
export interface DeriveReferendumVotes extends DeriveReferendumVoteState {
|
||||
isPassing: boolean;
|
||||
votes: DeriveReferendumVote[];
|
||||
}
|
||||
|
||||
export interface DeriveReferendumExt extends DeriveReferendum, DeriveReferendumVotes {
|
||||
}
|
||||
@@ -0,0 +1,184 @@
|
||||
// Copyright 2017-2021 @polkadot/api-derive authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import type { Bytes, Option } from '@polkadot/types';
|
||||
import type { AccountId, Balance, BlockNumber, PreimageStatus, Proposal, ReferendumInfoTo239, Tally } from '@polkadot/types/interfaces';
|
||||
import type { PalletDemocracyReferendumInfo, PalletDemocracyReferendumStatus, PalletDemocracyVoteThreshold } from '@polkadot/types/lookup';
|
||||
import type { ITuple } from '@polkadot/types/types';
|
||||
import type { DeriveApi, DeriveProposalImage, DeriveReferendum, DeriveReferendumVote, DeriveReferendumVotes, DeriveReferendumVoteState } from '../types';
|
||||
|
||||
import { BN, bnSqrt } from '@polkadot/util';
|
||||
|
||||
type PreimageInfo = [Bytes, AccountId, Balance, BlockNumber];
|
||||
type OldPreimage = ITuple<PreimageInfo>;
|
||||
|
||||
interface ApproxState {
|
||||
votedAye: BN;
|
||||
votedNay: BN;
|
||||
votedTotal: BN;
|
||||
}
|
||||
|
||||
function isOldInfo (info: PalletDemocracyReferendumInfo | ReferendumInfoTo239): info is ReferendumInfoTo239 {
|
||||
return !!(info as ReferendumInfoTo239).proposalHash;
|
||||
}
|
||||
|
||||
function isCurrentStatus (status: PalletDemocracyReferendumStatus | ReferendumInfoTo239): status is PalletDemocracyReferendumStatus {
|
||||
return !!(status as PalletDemocracyReferendumStatus).tally;
|
||||
}
|
||||
|
||||
function isCurrentPreimage (api: DeriveApi, imageOpt: Option<OldPreimage> | Option<PreimageStatus>): imageOpt is Option<PreimageStatus> {
|
||||
return !!imageOpt && !api.query.democracy.dispatchQueue;
|
||||
}
|
||||
|
||||
export function compareRationals (n1: BN, d1: BN, n2: BN, d2: BN): boolean {
|
||||
while (true) {
|
||||
const q1 = n1.div(d1);
|
||||
const q2 = n2.div(d2);
|
||||
|
||||
if (q1.lt(q2)) {
|
||||
return true;
|
||||
} else if (q2.lt(q1)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const r1 = n1.mod(d1);
|
||||
const r2 = n2.mod(d2);
|
||||
|
||||
if (r2.isZero()) {
|
||||
return false;
|
||||
} else if (r1.isZero()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
n1 = d2;
|
||||
n2 = d1;
|
||||
d1 = r2;
|
||||
d2 = r1;
|
||||
}
|
||||
}
|
||||
|
||||
function calcPassingOther (threshold: PalletDemocracyVoteThreshold, sqrtElectorate: BN, { votedAye, votedNay, votedTotal }: ApproxState): boolean {
|
||||
const sqrtVoters = bnSqrt(votedTotal);
|
||||
|
||||
return sqrtVoters.isZero()
|
||||
? false
|
||||
: threshold.isSuperMajorityApprove
|
||||
? compareRationals(votedNay, sqrtVoters, votedAye, sqrtElectorate)
|
||||
: compareRationals(votedNay, sqrtElectorate, votedAye, sqrtVoters);
|
||||
}
|
||||
|
||||
export function calcPassing (threshold: PalletDemocracyVoteThreshold, sqrtElectorate: BN, state: ApproxState): boolean {
|
||||
return threshold.isSimpleMajority
|
||||
? state.votedAye.gt(state.votedNay)
|
||||
: calcPassingOther(threshold, sqrtElectorate, state);
|
||||
}
|
||||
|
||||
function calcVotesPrev (votesFor: DeriveReferendumVote[]): DeriveReferendumVoteState {
|
||||
return votesFor.reduce((state: DeriveReferendumVoteState, derived): DeriveReferendumVoteState => {
|
||||
const { balance, vote } = derived;
|
||||
const isDefault = vote.conviction.index === 0;
|
||||
const counted = balance
|
||||
.muln(isDefault ? 1 : vote.conviction.index)
|
||||
.divn(isDefault ? 10 : 1);
|
||||
|
||||
if (vote.isAye) {
|
||||
state.allAye.push(derived);
|
||||
state.voteCountAye++;
|
||||
state.votedAye.iadd(counted);
|
||||
} else {
|
||||
state.allNay.push(derived);
|
||||
state.voteCountNay++;
|
||||
state.votedNay.iadd(counted);
|
||||
}
|
||||
|
||||
state.voteCount++;
|
||||
state.votedTotal.iadd(counted);
|
||||
|
||||
return state;
|
||||
}, { allAye: [], allNay: [], voteCount: 0, voteCountAye: 0, voteCountNay: 0, votedAye: new BN(0), votedNay: new BN(0), votedTotal: new BN(0) });
|
||||
}
|
||||
|
||||
function calcVotesCurrent (tally: Tally, votes: DeriveReferendumVote[]): DeriveReferendumVoteState {
|
||||
const allAye: DeriveReferendumVote[] = [];
|
||||
const allNay: DeriveReferendumVote[] = [];
|
||||
|
||||
votes.forEach((derived): void => {
|
||||
if (derived.vote.isAye) {
|
||||
allAye.push(derived);
|
||||
} else {
|
||||
allNay.push(derived);
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
allAye,
|
||||
allNay,
|
||||
voteCount: allAye.length + allNay.length,
|
||||
voteCountAye: allAye.length,
|
||||
voteCountNay: allNay.length,
|
||||
votedAye: tally.ayes,
|
||||
votedNay: tally.nays,
|
||||
votedTotal: tally.turnout
|
||||
};
|
||||
}
|
||||
|
||||
export function calcVotes (sqrtElectorate: BN, referendum: DeriveReferendum, votes: DeriveReferendumVote[]): DeriveReferendumVotes {
|
||||
const state = isCurrentStatus(referendum.status)
|
||||
? calcVotesCurrent(referendum.status.tally, votes)
|
||||
: calcVotesPrev(votes);
|
||||
|
||||
return {
|
||||
...state,
|
||||
isPassing: calcPassing(referendum.status.threshold, sqrtElectorate, state),
|
||||
votes
|
||||
};
|
||||
}
|
||||
|
||||
export function getStatus (info: Option<PalletDemocracyReferendumInfo | ReferendumInfoTo239>): PalletDemocracyReferendumStatus | ReferendumInfoTo239 | null {
|
||||
if (info.isNone) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const unwrapped = info.unwrap();
|
||||
|
||||
if (isOldInfo(unwrapped)) {
|
||||
return unwrapped;
|
||||
} else if (unwrapped.isOngoing) {
|
||||
return unwrapped.asOngoing;
|
||||
}
|
||||
|
||||
// done, we don't include it here... only currently active
|
||||
return null;
|
||||
}
|
||||
|
||||
function constructProposal (api: DeriveApi, [bytes, proposer, balance, at]: PreimageInfo): DeriveProposalImage {
|
||||
let proposal: Proposal | undefined;
|
||||
|
||||
try {
|
||||
proposal = api.registry.createType('Proposal', bytes.toU8a(true));
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
|
||||
return { at, balance, proposal, proposer };
|
||||
}
|
||||
|
||||
export function parseImage (api: DeriveApi, imageOpt: Option<OldPreimage> | Option<PreimageStatus>): DeriveProposalImage | undefined {
|
||||
if (imageOpt.isNone) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (isCurrentPreimage(api, imageOpt)) {
|
||||
const status = imageOpt.unwrap();
|
||||
|
||||
if (status.isMissing) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { data, deposit, provider, since } = status.asAvailable;
|
||||
|
||||
return constructProposal(api, [data, provider, deposit, since]);
|
||||
}
|
||||
|
||||
return constructProposal(api, imageOpt.unwrap());
|
||||
}
|
||||
Reference in New Issue
Block a user