// Copyright 2017-2021 @polkadot/api-derive authors & contributors // SPDX-License-Identifier: Apache-2.0 import type { ApiInterfaceRx } from '@polkadot/api/types'; import type { Option, u64, Vec } from '@polkadot/types'; import type { BlockNumber, Hash, ReferendumIndex, Scheduled } from '@polkadot/types/interfaces'; import type { ITuple } from '@polkadot/types/types'; import type { Observable } from '@polkadot/x-rxjs'; import type { DeriveDispatch, DeriveProposalImage } from '../types'; import { isFunction, stringToHex } from '@polkadot/util'; import { combineLatest, of } from '@polkadot/x-rxjs'; import { map, switchMap } from '@polkadot/x-rxjs/operators'; import { memo } from '../util'; const DEMOCRACY_ID = stringToHex('democrac'); interface SchedulerInfo { at: BlockNumber; imageHash: Hash; index: ReferendumIndex; } function queryQueue (api: ApiInterfaceRx): Observable { return api.query.democracy.dispatchQueue>>().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: ApiInterfaceRx): Observable<[BlockNumber[], Option[][]]> { // 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 combineLatest([ of(blockNumbers), api.query.scheduler.agenda.multi>>(blockNumbers) ]); }) ); } function queryScheduler (api: ApiInterfaceRx): Observable { return schedulerEntries(api).pipe( switchMap(([blockNumbers, agendas]): Observable<[SchedulerInfo[], (DeriveProposalImage | undefined)[]]> => { const result: SchedulerInfo[] = []; blockNumbers.forEach((at, index): void => { agendas[index].filter((optScheduled) => optScheduled.isSome).forEach((optScheduled): void => { const scheduled = optScheduled.unwrap(); if (scheduled.maybeId.isSome) { const id = scheduled.maybeId.unwrap().toHex(); if (id.startsWith(DEMOCRACY_ID)) { const [, index] = api.registry.createType('(u64, ReferendumIndex)' as any, id) as ITuple<[u64, ReferendumIndex]>; const imageHash = scheduled.call.args[0] as Hash; result.push({ at, imageHash, index }); } } }); }); return combineLatest([ of(result), api.derive.democracy.preimages(result.map(({ imageHash }) => imageHash)) ]); }), map(([infos, images]): DeriveDispatch[] => infos.map((info, index) => ({ ...info, image: images[index] })) ) ); } export function dispatchQueue (instanceId: string, api: ApiInterfaceRx): () => Observable { return memo(instanceId, (): Observable => isFunction(api.query.scheduler?.agenda) ? queryScheduler(api) : api.query.democracy.dispatchQueue ? queryQueue(api) : of([]) ); }