diff --git a/CHANGELOG.md b/CHANGELOG.md index 23772d292b..d4e6a8da7d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ # 1.14.0-beta.x -- Align imOnline `Heartbeats` with Substrate (Thanks to https://github.com/arjanz) +- Align imOnline `Heartbeat` with Substrate (Thanks to https://github.com/arjanz) +- Add `derive.democracy.locks` to return extended lock info based on votes - Ensure metadata is only requested once upon startup (bypass on upgrade subscriptions) - Expand testing around `ExtrinsicEra` construction diff --git a/packages/api-derive/src/democracy/index.ts b/packages/api-derive/src/democracy/index.ts index fcf55d96a1..1e04d600e3 100644 --- a/packages/api-derive/src/democracy/index.ts +++ b/packages/api-derive/src/democracy/index.ts @@ -3,6 +3,7 @@ // of the Apache-2.0 license. See the LICENSE file for details. export * from './dispatchQueue'; +export * from './locks'; export * from './nextExternal'; export * from './preimage'; export * from './preimages'; diff --git a/packages/api-derive/src/democracy/locks.ts b/packages/api-derive/src/democracy/locks.ts new file mode 100644 index 0000000000..51104a8d8e --- /dev/null +++ b/packages/api-derive/src/democracy/locks.ts @@ -0,0 +1,66 @@ +// 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 { AccountId, ReferendumInfo, ReferendumInfoTo239, VotingDirect, VotingDirectVote } from '@polkadot/types/interfaces'; +import { ApiInterfaceRx } from '@polkadot/api/types'; +import { DeriveDemocracyLock } from '../types'; + +import BN from 'bn.js'; +import { Observable, of } from 'rxjs'; +import { map, switchMap } from 'rxjs/operators'; +import { Option } from '@polkadot/types'; +import { isUndefined } from '@polkadot/util'; + +import { memo } from '../util'; + +const LOCKUPS = [0, 1, 2, 4, 8, 16, 32]; +const ZERO = new BN(0); + +function parseLock (api: ApiInterfaceRx, [referendumId, accountVote]: VotingDirectVote, referendum: ReferendumInfo): DeriveDemocracyLock { + const { balance, vote } = accountVote.asStandard; + let unlockAt = ZERO; + + if (referendum.isFinished) { + const { approved, end } = referendum.asFinished; + + if ((approved.isTrue && vote.isAye) || (approved.isFalse && vote.isNay)) { + unlockAt = end.add(api.consts.democracy.enactmentPeriod.muln(LOCKUPS[vote.conviction.index])); + } + } + + return { balance, isFinished: referendum.isFinished, referendumId, unlockAt, vote }; +} + +function directLocks (api: ApiInterfaceRx, { votes }: VotingDirect): Observable { + if (!votes.length) { + return of([]); + } + + return api.query.democracy.referendumInfoOf.multi>(votes.map(([referendumId]) => referendumId)).pipe( + map((referendums) => + votes + .map((vote, index): [VotingDirectVote, ReferendumInfo | ReferendumInfoTo239 | null] => + [vote, referendums[index].unwrapOr(null)] + ) + .filter((item): item is [VotingDirectVote, ReferendumInfo] => + !!item[1] && isUndefined((item[1] as ReferendumInfoTo239).end) && item[0][1].isStandard + ) + .map(([directVote, referendum]) => + parseLock(api, directVote, referendum) + ) + ) + ); +} + +export function locks (api: ApiInterfaceRx): (accountId: string | AccountId) => Observable { + return memo((accountId: string | AccountId): Observable => + api.query.democracy.votingOf(accountId).pipe( + switchMap((voting): Observable => + voting.isDirect + ? directLocks(api, voting.asDirect) + : of([]) + ) + ) + ); +} diff --git a/packages/api-derive/src/democracy/types.ts b/packages/api-derive/src/democracy/types.ts index 9811040d40..281cd93286 100644 --- a/packages/api-derive/src/democracy/types.ts +++ b/packages/api-derive/src/democracy/types.ts @@ -7,6 +7,14 @@ import { AccountId, Balance, BlockNumber, Hash, Proposal, PropIndex, ReferendumI import { Vec } from '@polkadot/types'; +export interface DeriveDemocracyLock { + balance: Balance; + isFinished: boolean; + referendumId: ReferendumIndex; + unlockAt: BN; + vote: Vote; +} + export interface DeriveDispatch { at: BlockNumber; index: ReferendumIndex;