From bbc474432ec236275536e0374b534f4ce9e06ab7 Mon Sep 17 00:00:00 2001 From: Jaco Greeff Date: Wed, 15 May 2019 10:41:48 +0200 Subject: [PATCH] Expose queryMulti on api (#909) * Expose queryMulti on api * Convert contract fees to queryMulti --- packages/api-derive/src/balances/fees.ts | 14 +++--- packages/api-derive/src/contract/fees.ts | 40 ++++++--------- packages/api-derive/test/e2e/promise.spec.ts | 12 ++++- packages/api/src/Base.ts | 51 +++++++++++++++++--- packages/api/src/types.ts | 20 ++++++++ 5 files changed, 96 insertions(+), 41 deletions(-) diff --git a/packages/api-derive/src/balances/fees.ts b/packages/api-derive/src/balances/fees.ts index c313b3cbf1..2e466a4c10 100644 --- a/packages/api-derive/src/balances/fees.ts +++ b/packages/api-derive/src/balances/fees.ts @@ -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 { Observable, combineLatest } from 'rxjs'; +import { Observable } from 'rxjs'; import { map } from 'rxjs/operators'; import { ApiInterface$Rx } from '@plugnet/api/types'; @@ -12,12 +12,12 @@ import { drr } from '../util/drr'; export function fees (api: ApiInterface$Rx) { return (): Observable => { - return (combineLatest([ - api.query.balances.creationFee(), - api.query.balances.existentialDeposit(), - api.query.balances.transactionBaseFee(), - api.query.balances.transactionByteFee(), - api.query.balances.transferFee() + return (api.queryMulti([ + api.query.balances.creationFee, + api.query.balances.existentialDeposit, + api.query.balances.transactionBaseFee, + api.query.balances.transactionByteFee, + api.query.balances.transferFee ]) as any as Observable<[BN, BN, BN, BN, BN]>).pipe( map(([creationFee, existentialDeposit, transactionBaseFee, transactionByteFee, transferFee]) => ({ creationFee, diff --git a/packages/api-derive/src/contract/fees.ts b/packages/api-derive/src/contract/fees.ts index 726fb187ad..54fbc8a3b8 100644 --- a/packages/api-derive/src/contract/fees.ts +++ b/packages/api-derive/src/contract/fees.ts @@ -2,43 +2,31 @@ // This software may be modified and distributed under the terms // of the Apache-2.0 license. See the LICENSE file for details. -import { ApiInterface$Rx, QueryableStorageFunctionBase } from '@plugnet/api/types'; -import { Codec } from '@plugnet/types/types'; +import { ApiInterface$Rx } from '@plugnet/api/types'; import { DerivedContractFees } from '../types'; import BN from 'bn.js'; -import { Observable, combineLatest, of } from 'rxjs'; +import { Observable } from 'rxjs'; import { map } from 'rxjs/operators'; import { drr } from '../util/drr'; -// TODO The contracts API is still in flux, so some of these may disappear or re-appear or get replaced. Instead of -// failing here, do best effort - if something does not exist, return 0 for the specific value. Should be removed -// when actually stable and no more changes expected -function nonexistentZero (fn: QueryableStorageFunctionBase, Observable>): Observable { - return fn - ? fn() as any as Observable - : of(new BN(0)); -} - /** - * Get the balances for all intentions and their nominators + * Retrieves all the contract fees */ export function fees (api: ApiInterface$Rx) { - // TODO Once we have muti support for disjointed queries, add here (so we make a single - // query for all these to the node). return (): Observable => { - return (combineLatest([ - nonexistentZero(api.query.contract.callBaseFee), - nonexistentZero(api.query.contract.contractFee), - nonexistentZero(api.query.contract.createBaseFee), - nonexistentZero(api.query.contract.creationFee), - nonexistentZero(api.query.contract.rentByteFee), - nonexistentZero(api.query.contract.rentDepositOffset), - nonexistentZero(api.query.contract.transactionBaseFee), - nonexistentZero(api.query.contract.transactionByteFee), - nonexistentZero(api.query.contract.transferFee), - nonexistentZero(api.query.contract.tombstoneDeposit) + return (api.queryMulti([ + api.query.contract.callBaseFee, + api.query.contract.contractFee, + api.query.contract.createBaseFee, + api.query.contract.creationFee, + api.query.contract.rentByteFee, + api.query.contract.rentDepositOffset, + api.query.contract.transactionBaseFee, + api.query.contract.transactionByteFee, + api.query.contract.transferFee, + api.query.contract.tombstoneDeposit ]) as any as Observable>).pipe( map(([callBaseFee, contractFee, createBaseFee, creationFee, rentByteFee, rentDepositOffset, transactionBaseFee, transactionByteFee, transferFee, tombstoneDeposit]) => { return { diff --git a/packages/api-derive/test/e2e/promise.spec.ts b/packages/api-derive/test/e2e/promise.spec.ts index ecbbd8c3d6..ad086ceb6b 100644 --- a/packages/api-derive/test/e2e/promise.spec.ts +++ b/packages/api-derive/test/e2e/promise.spec.ts @@ -6,8 +6,8 @@ import ApiPromise from '@plugnet/api/promise/Api'; import { BlockNumber } from '@plugnet/types'; import { WsProvider } from '@plugnet/rpc-provider'; -// const WS = 'ws://127.0.0.1:9944/'; -const WS = 'wss://poc3-rpc.polkadot.io/'; +const WS = 'ws://127.0.0.1:9944/'; +// const WS = 'wss://poc3-rpc.polkadot.io/'; describe.skip('derive e2e', () => { let api: ApiPromise; @@ -39,4 +39,12 @@ describe.skip('derive e2e', () => { } }); }); + + it('retrieves the fees (api.queryMulti)', (done) => { + return api.derive.balances.fees((fees) => { + console.error('fees', JSON.stringify(fees)); + + done(); + }); + }); }); diff --git a/packages/api/src/Base.ts b/packages/api/src/Base.ts index 794f69ced8..9ac1b244b0 100644 --- a/packages/api/src/Base.ts +++ b/packages/api/src/Base.ts @@ -11,7 +11,7 @@ import { DecoratedRpc, DecoratedRpc$Method, DecoratedRpc$Section, Derive, DeriveSection, HashResult, U64Result, OnCallDefinition, OnCallFunction, - QueryableModuleStorage, QueryableStorage, QueryableStorageFunction, + QueryableModuleStorage, QueryableStorage, QueryableStorageFunction, QueryableStorageMulti, QueryableStorageMultiArg, QueryableStorageMultiArgs, SubmittableExtrinsicFunction, SubmittableExtrinsics, SubmittableModuleExtrinsics, Signer } from './types'; @@ -59,11 +59,7 @@ try { * Put the `this.onCall` function of ApiRx here, because it is needed by * `api._rx`. */ -function rxOnCall ( - method: OnCallFunction, - params: Array = [], - _callback?: CodecCallback -): RxResult { +function rxOnCall (method: OnCallFunction, params: Array = [], _callback?: CodecCallback): RxResult { return method(...params); } @@ -75,6 +71,7 @@ export default abstract class ApiBase implement private _isReady: boolean = false; protected readonly _options: ApiOptions; private _query?: QueryableStorage; + private _queryMulti: QueryableStorageMulti; private _rpc: DecoratedRpc; protected _rpcBase: RpcBase; // FIXME These two could be merged protected _rpcRx: RpcRx; // FIXME These two could be merged @@ -119,6 +116,8 @@ export default abstract class ApiBase implement this._rpcRx = new RpcRx(thisProvider); this._rpc = this.decorateRpc(this._rpcRx, this.onCall) as any; // FIXME 3.4.1 this._rx.rpc = this.decorateRpc(this._rpcRx, rxOnCall); + this._queryMulti = this.decorateMulti(this.onCall) as any; // as above :( + this._rx.queryMulti = this.decorateMulti(rxOnCall); this._rx.signer = options.signer; // we only re-register the types (global) if this is not a cloned instance @@ -222,6 +221,30 @@ export default abstract class ApiBase implement return this._query as QueryableStorage; } + /** + * @description Allows for the querying of multiple storage entries and the combination thereof into a single result. This is a very optimal way to make multiple queries since it only makes a single connection to the node and retrieves the data over one subscription. + * + * @example + *
+ * + * ```javascript + * api.queryMulti( + * [ + * // you can include the storage without any parameters + * api.query.balances.existentialDeposit, + * // or you can pass parameters to the storage query + * [api.query.balances.freeBalance, '5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY'] + * ], + * ([existential, balance]) => { + * console.log(`You have ${balance.sub(existential)} more than the existential deposit`); + * } + * ); + * ``` + */ + get queryMulti (): QueryableStorageMulti { + return this._queryMulti; + } + /** * @description Contains all the raw rpc sections and their subsequent methods in the API as defined by the jsonrpc interface definitions. Unlike the dynamic `api.query` and `api.tx` sections, these methods are fixed (although extensible with node upgrades) and not determined by the runtime. * @@ -469,6 +492,22 @@ export default abstract class ApiBase implement }, {} as DecoratedRpc); } + private decorateMulti (onCall: OnCallDefinition): QueryableStorageMulti { + return ((calls: QueryableStorageMultiArgs, callback?: CodecCallback): S => { + const mapped = calls.map((arg: QueryableStorageMultiArg): [QueryableStorageFunction, ...Array] => + Array.isArray(arg) + ? arg + : [arg] as any + ); + + return onCall( + () => this._rpcRx.state.subscribeStorage(mapped), + [], + callback + ) as S; + }) as QueryableStorageMulti; + } + private decorateExtrinsics (extrinsics: ModulesWithMethods, onCall: OnCallDefinition): SubmittableExtrinsics { return Object.keys(extrinsics).reduce((result, sectionName) => { const section = extrinsics[sectionName]; diff --git a/packages/api/src/types.ts b/packages/api/src/types.ts index 7119bae340..dd741271fa 100644 --- a/packages/api/src/types.ts +++ b/packages/api/src/types.ts @@ -70,6 +70,25 @@ export interface QueryableModuleStorage { [index: string]: QueryableStorageFunction; } +export type QueryableStorageMultiArg = + QueryableStorageFunction | + [QueryableStorageFunction, ...Array]; + +export type QueryableStorageMultiArgs = Array>; + +export interface QueryableStorageMultiBase { + (calls: QueryableStorageMultiArgs): SubscriptionResult; +} + +export interface QueryableStorageMultiPromise { + (calls: QueryableStorageMultiArgs, callback: CodecCallback): SubscriptionResult; +} + +export type QueryableStorageMulti = + CodecResult extends Observable + ? QueryableStorageMultiBase + : QueryableStorageMultiPromise; + export interface QueryableStorage { [index: string]: QueryableModuleStorage; } @@ -149,6 +168,7 @@ export interface ApiInterface$Decorated { runtimeVersion: RuntimeVersion; derive: Derive; query: QueryableStorage; + queryMulti: QueryableStorageMulti; rpc: DecoratedRpc; tx: SubmittableExtrinsics; signer?: Signer;