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:
@@ -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)(
|
||||
|
||||
@@ -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()
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
);
|
||||
}
|
||||
|
||||
@@ -4,4 +4,5 @@
|
||||
|
||||
export * from './eraLength';
|
||||
export * from './eraProgress';
|
||||
export * from './info';
|
||||
export * from './sessionProgress';
|
||||
|
||||
@@ -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()
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
};
|
||||
|
||||
@@ -47,4 +47,27 @@ describe.skip('derive e2e', () => {
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('retrieves all session info', (done) => {
|
||||
let count = 0;
|
||||
|
||||
return api.derive.session.info((info) => {
|
||||
console.error(JSON.stringify(info));
|
||||
|
||||
// 5 blocks only, then go our merry way
|
||||
if (++count === 5) {
|
||||
done();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it('retrieves the balances', (done) => {
|
||||
return api.derive.balances.all('5FLSigC9HGRKVhB9FiEo4Y3koPsNmBmLJbpXg2mp1hXcS59Y', (balance) => {
|
||||
console.error(JSON.stringify(balance));
|
||||
|
||||
if (balance.freeBalance.gtn(1)) {
|
||||
done();
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -31,7 +31,6 @@ describe.skip('derive e2e', () => {
|
||||
// These derive.accounts tests only work on localhost, not the poc-3 URL
|
||||
// (and it is assuming it sent at least 1 tx)
|
||||
describe('derive.accounts', () => {
|
||||
|
||||
describe('idAndIndex', () => {
|
||||
it('looks up AccountId & AccountIndex from AccountId', async (done) => {
|
||||
// @ts-ignore silence warning until we have static types here
|
||||
|
||||
+15
-12
@@ -495,9 +495,10 @@ export default abstract class ApiBase<CodecResult, SubscriptionResult> implement
|
||||
private decorateMulti<C, S> (onCall: OnCallDefinition<C, S>): QueryableStorageMulti<C, S> {
|
||||
return ((calls: QueryableStorageMultiArgs<C, S>, callback?: CodecCallback): S => {
|
||||
const mapped = calls.map((arg: QueryableStorageMultiArg<C, S>): [QueryableStorageFunction<CodecResult, SubscriptionResult>, ...Array<CodecArg>] =>
|
||||
// the input is a QueryableStorageFunction, convert to StorageFunction
|
||||
Array.isArray(arg)
|
||||
? arg
|
||||
: [arg] as any
|
||||
? [arg[0].creator, ...arg.slice(1)]
|
||||
: [arg.creator] as any
|
||||
);
|
||||
|
||||
return onCall(
|
||||
@@ -543,7 +544,7 @@ export default abstract class ApiBase<CodecResult, SubscriptionResult> implement
|
||||
}, {} as QueryableStorage<C, S>);
|
||||
}
|
||||
|
||||
private decorateStorageEntry<C, S> (method: StorageFunction, onCall: OnCallDefinition<C, S>): QueryableStorageFunction<C, S> {
|
||||
private decorateStorageEntry<C, S> (creator: StorageFunction, onCall: OnCallDefinition<C, S>): QueryableStorageFunction<C, S> {
|
||||
// These signatures are allowed and exposed here -
|
||||
// (arg?: CodecArg): CodecResult;
|
||||
// (arg: CodecArg, callback: CodecCallback): SubscriptionResult;
|
||||
@@ -557,13 +558,13 @@ export default abstract class ApiBase<CodecResult, SubscriptionResult> implement
|
||||
params = args.slice(0, args.length - 1);
|
||||
}
|
||||
|
||||
if (method.headKey && params.length === 0) {
|
||||
return this.decorateStorageEntryLinked(method, onCall, callback);
|
||||
if (creator.headKey && params.length === 0) {
|
||||
return this.decorateStorageEntryLinked(creator, onCall, callback);
|
||||
}
|
||||
|
||||
return onCall(
|
||||
(arg: CodecArg) => this._rpcRx.state
|
||||
.subscribeStorage([[method, arg]])
|
||||
.subscribeStorage([[creator, arg]])
|
||||
.pipe(
|
||||
// state_storage returns an array of values, since we have just subscribed to
|
||||
// a single entry, we pull that from the array and return it as-is
|
||||
@@ -576,9 +577,11 @@ export default abstract class ApiBase<CodecResult, SubscriptionResult> implement
|
||||
);
|
||||
}) as QueryableStorageFunction<C, S>;
|
||||
|
||||
decorated.creator = creator;
|
||||
|
||||
decorated.at = (hash: Hash | Uint8Array | string, arg?: CodecArg): C =>
|
||||
onCall(
|
||||
(arg: CodecArg) => this._rpcRx.state.getStorage([method, arg], hash),
|
||||
(arg: CodecArg) => this._rpcRx.state.getStorage([creator, arg], hash),
|
||||
[arg]
|
||||
) as C;
|
||||
|
||||
@@ -586,27 +589,27 @@ export default abstract class ApiBase<CodecResult, SubscriptionResult> implement
|
||||
// be converted from C to the actual result required
|
||||
decorated.hash = (arg?: CodecArg): HashResult<C, S> =>
|
||||
onCall(
|
||||
(arg: CodecArg) => this._rpcRx.state.getStorageHash([method, arg]),
|
||||
(arg: CodecArg) => this._rpcRx.state.getStorageHash([creator, arg]),
|
||||
[arg]
|
||||
) as unknown as HashResult<C, S>;
|
||||
|
||||
decorated.key = (arg?: CodecArg): string =>
|
||||
u8aToHex(compactStripLength(method(arg))[1]);
|
||||
u8aToHex(compactStripLength(creator(arg))[1]);
|
||||
|
||||
decorated.multi = (args: Array<CodecArg>, callback?: CodecCallback): S =>
|
||||
onCall(
|
||||
() => this._rpcRx.state.subscribeStorage(args.map((arg) => [method, arg])),
|
||||
() => this._rpcRx.state.subscribeStorage(args.map((arg) => [creator, arg])),
|
||||
[],
|
||||
callback
|
||||
) as unknown as S;
|
||||
|
||||
decorated.size = (arg?: CodecArg): U64Result<C, S> =>
|
||||
onCall(
|
||||
(arg: CodecArg) => this._rpcRx.state.getStorageSize([method, arg]),
|
||||
(arg: CodecArg) => this._rpcRx.state.getStorageSize([creator, arg]),
|
||||
[arg]
|
||||
) as unknown as U64Result<C, S>;
|
||||
|
||||
return this.decorateFunctionMeta(method, decorated) as QueryableStorageFunction<C, S>;
|
||||
return this.decorateFunctionMeta(creator, decorated) as QueryableStorageFunction<C, S>;
|
||||
}
|
||||
|
||||
private decorateStorageEntryLinked<C, S> (method: StorageFunction, onCall: OnCallDefinition<C, S>, callback: CodecCallback | undefined): C | S {
|
||||
|
||||
@@ -50,6 +50,7 @@ export type U64Result<CodecResult, SubscriptionResult> =
|
||||
export interface QueryableStorageFunctionBase<CodecResult, SubscriptionResult> extends StorageFunction {
|
||||
(arg?: CodecArg): CodecResult;
|
||||
at: (hash: Hash | Uint8Array | string, arg?: CodecArg) => CodecResult;
|
||||
creator: StorageFunction;
|
||||
hash: (arg?: CodecArg) => HashResult<CodecResult, SubscriptionResult>;
|
||||
key: (arg?: CodecArg) => string;
|
||||
multi: (args: Array<CodecArg>, callback?: CodecCallback) => SubscriptionResult;
|
||||
|
||||
@@ -98,13 +98,28 @@ describe.skip('e2e queries', () => {
|
||||
api.query.balances.freeBalance.multi([
|
||||
keyring.alice.address(),
|
||||
keyring.bob.address(),
|
||||
keyring.ferdie.address(),
|
||||
'5DTestUPts3kjeXSTMyerHihn1uwMfLj8vU8sqF7qYrFabHE'
|
||||
'5FLSigC9HGRKVhB9FiEo4Y3koPsNmBmLJbpXg2mp1hXcS59Y',
|
||||
keyring.ferdie.address()
|
||||
], (balances) => {
|
||||
console.error(balances);
|
||||
|
||||
expect(balances).toHaveLength(4);
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('subscribes to multiple results (api.queryMulti)', (done) => {
|
||||
return api.queryMulti([
|
||||
[api.query.balances.freeBalance, keyring.alice.address()],
|
||||
[api.query.balances.freeBalance, keyring.bob.address()],
|
||||
[api.query.balances.freeBalance, '5FLSigC9HGRKVhB9FiEo4Y3koPsNmBmLJbpXg2mp1hXcS59Y'],
|
||||
[api.query.balances.freeBalance, keyring.ferdie.address()]
|
||||
], (balances) => {
|
||||
console.error(balances);
|
||||
|
||||
expect(balances).toHaveLength(4);
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
import { AnyU8a } from '../types';
|
||||
|
||||
import { isFunction } from '@polkadot/util';
|
||||
import { assert, isFunction, isString, isU8a } from '@polkadot/util';
|
||||
|
||||
import Bytes from './Bytes';
|
||||
import { StorageFunctionMetadata as MetaV0 } from '../Metadata/v0/Storage';
|
||||
@@ -29,25 +29,28 @@ export default class StorageKey extends Bytes {
|
||||
private _meta: MetaV0 | MetaV4 | null;
|
||||
private _outputType: string | null;
|
||||
|
||||
constructor (value: AnyU8a | StorageKey | StorageFunction | [StorageFunction, any]) {
|
||||
constructor (value?: AnyU8a | StorageKey | StorageFunction | [StorageFunction, any]) {
|
||||
super(StorageKey.decodeStorageKey(value));
|
||||
|
||||
this._meta = StorageKey.getMeta(value as StorageKey);
|
||||
this._outputType = StorageKey.getType(value as StorageKey);
|
||||
}
|
||||
|
||||
static decodeStorageKey (value: AnyU8a | StorageKey | StorageFunction | [StorageFunction, any]): Uint8Array {
|
||||
if (isFunction(value)) {
|
||||
static decodeStorageKey (value?: AnyU8a | StorageKey | StorageFunction | [StorageFunction, any]): Uint8Array | string | undefined {
|
||||
if (!value || isU8a(value) || isString(value)) {
|
||||
// let Bytes handle these inputs
|
||||
return value;
|
||||
} else if (isFunction(value)) {
|
||||
return value();
|
||||
} else if (Array.isArray(value)) {
|
||||
const [fn, ...arg] = value;
|
||||
|
||||
if (isFunction(fn)) {
|
||||
return fn(...arg);
|
||||
}
|
||||
assert(isFunction(fn), 'Expected function input for key construction');
|
||||
|
||||
return (fn as Function)(...arg);
|
||||
}
|
||||
|
||||
return value as Uint8Array;
|
||||
throw new Error(`Unable to convert input ${value} to StorageKey`);
|
||||
}
|
||||
|
||||
static getMeta (value: StorageKey | StorageFunction | [StorageFunction, any]): MetaV0 | MetaV4 | null {
|
||||
|
||||
Reference in New Issue
Block a user