Update api.derive to use api.queryMulti (#911)

* session api.queryMulti

* Make a single subscription to retrieve all session info

* Adjustments

* WIP

* Seems mostly comprehensive, need to full test e2e

* e2e tests for operations

* Allow "create from empty"

* skip

* Remove comment

* Remove ancient ?'s

* Update types.ts
This commit is contained in:
Jaco Greeff
2019-05-16 11:36:41 +02:00
committed by GitHub
parent bafda6dc93
commit 2dfc55cb5c
16 changed files with 237 additions and 132 deletions
+42 -38
View File
@@ -12,8 +12,41 @@ import { idAndIndex } from '../accounts/idAndIndex';
import { DerivedBalances } from '../types';
import { drr } from '../util/drr';
type Result = [AccountId | undefined, BlockNumber | undefined, [Balance?, Balance?, Array<BalanceLock>?, Option<VestingSchedule>?]];
const EMPTY_ACCOUNT = new AccountId();
function calcBalances ([accountId = EMPTY_ACCOUNT, bestNumber = new BlockNumber(0), [freeBalance = new Balance(0), reservedBalance = new Balance(0), locks = [], vesting = new Option<VestingSchedule>(VestingSchedule, null)]]: Result): DerivedBalances {
let lockedBalance = new Balance(0);
if (Array.isArray(locks)) {
// only get the locks that are valid until passed the current block
const totals = locks.filter((value) => bestNumber && value.until.gt(bestNumber));
// get the maximum of the locks according to https://github.com/paritytech/substrate/blob/master/srml/balances/src/lib.rs#L699
lockedBalance = totals[0] ? bnMax(...totals.map(({ amount }) => amount)) as Balance : new Balance(0);
}
const { offset, perBlock } = vesting.unwrapOr({
offset: new Balance(0),
perBlock: new Balance(0)
} as VestingSchedule);
const vestedBalance = new Balance(perBlock.mul(bestNumber).add(freeBalance.sub(offset)));
const availableBalance = new Balance(vestedBalance.sub(lockedBalance));
return {
accountId,
freeBalance,
reservedBalance,
votingBalance: new Balance(
freeBalance.add(reservedBalance)
),
availableBalance,
lockedBalance,
vestedBalance: vestedBalance.lt(new Balance(0)) ? new Balance(0) : vestedBalance
};
}
/**
* @name all
* @param {( ccountIndex | AccountId | Address | string )} address - An accounts Id in different formats.
@@ -38,46 +71,17 @@ export function all (api: ApiInterface$Rx) {
? combineLatest([
of(accountId),
api.derive.chain.bestNumber(),
api.query.balances.freeBalance(accountId),
api.query.balances.reservedBalance(accountId),
api.query.balances.locks(accountId),
api.query.balances.vesting(accountId)
api.queryMulti([
[api.query.balances.freeBalance, accountId],
[api.query.balances.reservedBalance, accountId],
[api.query.balances.locks, accountId],
[api.query.balances.vesting, accountId]
])
])
: of([undefined, undefined, undefined, undefined, undefined, undefined])
) as Observable<[AccountId?, BlockNumber?, Balance?, Balance?, Array<BalanceLock>?, Option<VestingSchedule>?]>
: of([undefined, undefined, [undefined, undefined, undefined, undefined]])
) as any as Observable<Result>
),
map(([accountId = EMPTY_ACCOUNT, bestNumber = new BlockNumber(0), freeBalance = new Balance(0), reservedBalance = new Balance(0), locks = [], vesting = new Option<VestingSchedule>(VestingSchedule, null)]) => {
let lockedBalance = new Balance(0);
if (Array.isArray(locks)) {
// only get the locks that are valid until passed the current block
const totals = locks.filter((value) => bestNumber && value.until.gt(bestNumber));
// get the maximum of the locks according to https://github.com/paritytech/substrate/blob/master/srml/balances/src/lib.rs#L699
lockedBalance = totals[0] ? bnMax(...totals.map(({ amount }) => amount)) as Balance : new Balance(0);
}
const { offset, perBlock } = vesting.unwrapOr({
offset: new Balance(0),
perBlock: new Balance(0)
} as VestingSchedule);
const vestedBalance = new Balance(perBlock.mul(bestNumber).add(freeBalance.sub(offset)));
// const availableBalance = lockedBalance.eq(new BlockNumber('0xffffffffffffffffffffffffffffffff')) ? new Balance(0) : freeBalance && new Balance(freeBalance.sub(lockedBalance));
const availableBalance = new Balance(vestedBalance.sub(lockedBalance));
return {
accountId,
freeBalance,
reservedBalance,
votingBalance: new Balance(
freeBalance.add(reservedBalance)
),
availableBalance,
lockedBalance,
vestedBalance: vestedBalance.lt(new Balance(0)) ? new Balance(0) : vestedBalance
};
}),
map(calcBalances),
drr()
);
};
@@ -45,20 +45,26 @@ export class ReferendumInfoExtended extends ReferendumInfo {
}
}
export function constructInfo (index: BN | number, optionInfo?: Option<ReferendumInfo>): Option<ReferendumInfoExtended> {
const info = optionInfo
? optionInfo.unwrapOr(null)
: null;
return new Option<ReferendumInfoExtended>(
ReferendumInfoExtended,
isNull(info)
? null
: new ReferendumInfoExtended(info, index)
);
}
export function referendumInfo (api: ApiInterface$Rx) {
return (index: BN | number): Observable<Option<ReferendumInfoExtended>> => {
return (api.query.democracy.referendumInfoOf(index) as Observable<Option<ReferendumInfo>>)
.pipe(
map((optionInfo) => {
const info = optionInfo.unwrapOr(null);
return new Option<ReferendumInfoExtended>(
ReferendumInfoExtended,
isNull(info)
? null
: new ReferendumInfoExtended(info, index)
);
}),
map((optionInfo) =>
constructInfo(index, optionInfo)
),
drr()
);
};
@@ -3,22 +3,26 @@
// of the Apache-2.0 license. See the LICENSE file for details.
import BN from 'bn.js';
import { combineLatest, Observable, of } from 'rxjs';
import { Observable, of } from 'rxjs';
import { map } from 'rxjs/operators';
import { ApiInterface$Rx } from '@polkadot/api/types';
import { Option } from '@polkadot/types';
import { Option, ReferendumInfo } from '@polkadot/types';
import { drr } from '../util/drr';
import { referendumInfo, ReferendumInfoExtended } from './referendumInfo';
import { constructInfo, ReferendumInfoExtended } from './referendumInfo';
export function referendumInfos (api: ApiInterface$Rx) {
const referendumInfoOf = referendumInfo(api);
return (ids: Array<BN | number> = []): Observable<Array<Option<ReferendumInfoExtended>>> => {
return !ids || !ids.length
? of([]).pipe(drr())
: combineLatest(
ids.map((id) => referendumInfoOf(id))
).pipe(
return (
!ids || !ids.length
? of([])
: api.query.democracy.referendumInfoOf.multi(ids) as any as Observable<Array<Option<ReferendumInfo>>>
).pipe(
map((infos) =>
ids.map((id, index) =>
constructInfo(id, infos[index])
)
),
drr()
);
};
@@ -2,7 +2,7 @@
// This software may be modified and distributed under the terms
// of the Apache-2.0 license. See the LICENSE file for details.
import { combineLatest, Observable, of } from 'rxjs';
import { Observable, of } from 'rxjs';
import { switchMap } from 'rxjs/operators';
import { ApiInterface$Rx } from '@polkadot/api/types';
import { Option, ReferendumIndex } from '@polkadot/types';
@@ -13,10 +13,10 @@ import { referendumInfos } from './referendumInfos';
export function referendums (api: ApiInterface$Rx) {
return (): Observable<Array<Option<ReferendumInfoExtended>>> =>
(combineLatest([
api.query.democracy.nextTally(),
api.query.democracy.referendumCount()
]) as Observable<[ReferendumIndex?, ReferendumIndex?]>).pipe(
(api.queryMulti([
api.query.democracy.nextTally,
api.query.democracy.referendumCount
]) as any as Observable<[ReferendumIndex?, ReferendumIndex?]>).pipe(
switchMap(([nextTally, referendumCount]) =>
referendumCount && nextTally && referendumCount.gt(nextTally) && referendumCount.gtn(0)
? referendumInfos(api)(
+4 -10
View File
@@ -3,23 +3,17 @@
// of the Apache-2.0 license. See the LICENSE file for details.
import BN from 'bn.js';
import { combineLatest, Observable } from 'rxjs';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';
import { ApiInterface$Rx } from '@polkadot/api/types';
import { BlockNumber } from '@polkadot/types';
import { drr } from '../util/drr';
import { info } from './info';
export function eraLength (api: ApiInterface$Rx) {
return (): Observable<BN> =>
(combineLatest([
api.query.session.sessionLength(),
api.query.staking.sessionsPerEra()
]) as Observable<[BlockNumber?, BlockNumber?]>).pipe(
map(
([sessionLength, sessionsPerEra]) =>
(sessionLength || new BN(1)).mul(sessionsPerEra || new BN(1))
),
info(api)().pipe(
map(({ eraLength }) => eraLength),
drr()
);
}
+4 -20
View File
@@ -3,33 +3,17 @@
// of the Apache-2.0 license. See the LICENSE file for details.
import BN from 'bn.js';
import { combineLatest, Observable } from 'rxjs';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';
import { ApiInterface$Rx } from '@polkadot/api/types';
import { BlockNumber } from '@polkadot/types';
import { sessionProgress } from './sessionProgress';
import { drr } from '../util/drr';
import { info } from './info';
export function eraProgress (api: ApiInterface$Rx) {
return (): Observable<BN> =>
(combineLatest([
sessionProgress(api)(),
api.query.session.currentIndex(),
api.query.session.sessionLength(),
api.query.staking.lastEraLengthChange(),
api.query.staking.sessionsPerEra()
]) as Observable<[BN, BlockNumber?, BlockNumber?, BlockNumber?, BlockNumber?]>).pipe(
map(
([sessionProgress, currentIndex, sessionLength, lastEraLengthChange, sessionsPerEra]) =>
(currentIndex || new BN(0))
.sub(lastEraLengthChange || new BN(0))
.mod(sessionsPerEra || new BN(1))
.mul(sessionLength || new BN(1))
.add(sessionProgress || new BN(0)
)
),
info(api)().pipe(
map(({ eraProgress }) => eraProgress),
drr()
);
}
+1
View File
@@ -4,4 +4,5 @@
export * from './eraLength';
export * from './eraProgress';
export * from './info';
export * from './sessionProgress';
+68
View File
@@ -0,0 +1,68 @@
// Copyright 2017-2019 @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 { DerivedSessionInfo } from '../types';
import BN from 'bn.js';
import { Observable, combineLatest } from 'rxjs';
import { map } from 'rxjs/operators';
import { ApiInterface$Rx } from '@polkadot/api/types';
import { BlockNumber, Option } from '@polkadot/types';
import { drr } from '../util/drr';
import { bestNumber } from '../chain';
type Result = [BN, [BlockNumber, Option<BlockNumber>, BlockNumber, BlockNumber, BlockNumber]];
const ZERO = new BN(0);
// internal helper to just split the logic - take all inputs, do the calculations and combine
function createDerived ([bestNumber, [currentIndex, _lastLengthChange, sessionLength, lastEraLengthChange, sessionsPerEra]]: Result): DerivedSessionInfo {
const eraLength = sessionLength.mul(sessionsPerEra);
const lastLengthChange = _lastLengthChange
? _lastLengthChange.unwrapOr(ZERO)
: ZERO;
const sessionProgress = bestNumber
.sub(lastLengthChange)
.add(sessionLength)
.mod(sessionLength);
const eraProgress = (currentIndex)
.sub(lastEraLengthChange)
.mod(sessionsPerEra)
.mul(sessionLength)
.add(sessionProgress);
return {
currentIndex,
eraLength,
eraProgress,
lastEraLengthChange,
lastLengthChange,
sessionLength,
sessionsPerEra,
sessionProgress
};
}
/**
* @description Retrieves all the session and era info and calculates specific valus on it sunh as the length of the session and eras
*/
export function info (api: ApiInterface$Rx) {
return (): Observable<DerivedSessionInfo> =>
// This is a much more optimal way to calculate since we only make a single call to the RPC backend
// instead of making a subscription for each of the params (this means all others in session use)
(combineLatest([
bestNumber(api)(),
api.queryMulti([
api.query.session.currentIndex,
api.query.session.lastLengthChange,
api.query.session.sessionLength,
api.query.staking.lastEraLengthChange,
api.query.staking.sessionsPerEra
])
]) as any as Observable<Result>).pipe(
map(createDerived),
drr()
);
}
@@ -3,28 +3,17 @@
// of the Apache-2.0 license. See the LICENSE file for details.
import BN from 'bn.js';
import { combineLatest, Observable } from 'rxjs';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';
import { ApiInterface$Rx } from '@polkadot/api/types';
import { BlockNumber, Option } from '@polkadot/types';
import { bestNumber } from '../chain';
import { drr } from '../util/drr';
import { info } from './info';
export function sessionProgress (api: ApiInterface$Rx) {
return (): Observable<BN> =>
combineLatest([
bestNumber(api)(),
api.query.session.sessionLength(),
api.query.session.lastLengthChange()
]).pipe(
map(
([bestNumber, sessionLength, lastLengthChange]) =>
(bestNumber || new BN(0))
.sub((lastLengthChange as Option<BlockNumber>).unwrapOr(new BN(0)))
.add(sessionLength as BlockNumber || new BN(1))
.mod(sessionLength as BlockNumber || new BN(1))
),
info(api)().pipe(
map(({ sessionProgress }) => sessionProgress),
drr()
);
}
+12 -1
View File
@@ -3,7 +3,7 @@
// of the Apache-2.0 license. See the LICENSE file for details.
import BN from 'bn.js';
import { AccountId, Balance, Vote } from '@polkadot/types';
import { AccountId, Balance, BlockNumber, Vote } from '@polkadot/types';
export type DerivedBalances = {
accountId: AccountId,
@@ -45,3 +45,14 @@ export type DerivedContractFees = {
transferFee: BN,
tombstoneDeposit: BN
};
export type DerivedSessionInfo = {
currentIndex: BlockNumber,
eraLength: BN,
eraProgress: BN,
lastEraLengthChange: BlockNumber,
lastLengthChange: BN,
sessionLength: BlockNumber,
sessionsPerEra: BlockNumber,
sessionProgress: BN
};