Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 31 additions & 15 deletions packages/api/src/base/Decorate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,13 @@ export default abstract class Decorate<ApiType> extends Events {
this._isConnected = new BehaviorSubject(this._rpcCore.provider.isConnected());
}

/**
* @returns `true` if the API operates with subscriptions
*/
get hasSubscriptions (): boolean {
return this._rpcCore.provider.hasSubscriptions;
}

private decorateFunctionMeta (input: MetaDecoration, output: MetaDecoration): MetaDecoration {
output.meta = input.meta;
output.method = input.method;
Expand Down Expand Up @@ -185,10 +192,13 @@ export default abstract class Decorate<ApiType> extends Events {

// out and section here are horrors to get right from a typing perspective :()
(out as any)[sectionName] = Object.entries(rpc[sectionName]).reduce((section, [methodName, method]): DecoratedRpcSection<ApiType, RpcInterface[typeof sectionName]> => {
(section as any)[methodName] = decorateMethod(method, { methodName });
// skip subscriptions where we have a non-subscribable interface
if (this.hasSubscriptions || !(methodName.startsWith('subscribe') || methodName.startsWith('unsubscribe'))) {
(section as any)[methodName] = decorateMethod(method, { methodName });

// add this endpoint mapping to our internal map - we use this for filters
this._rpcMap.set(`${sectionName}_${methodName}`, jsonrpc[sectionName].methods[methodName]);
// add this endpoint mapping to our internal map - we use this for filters
this._rpcMap.set(`${sectionName}_${methodName}`, jsonrpc[sectionName].methods[methodName]);
}

return section;
}, {} as unknown as DecoratedRpcSection<ApiType, RpcInterface[typeof sectionName]>);
Expand Down Expand Up @@ -246,15 +256,19 @@ export default abstract class Decorate<ApiType> extends Events {
? [creator, args]
: [creator, ...args];

const decorated = creator.headKey
// FIXME We probably want to be able to query the full list with non-subs as well
const decorated = this.hasSubscriptions && creator.headKey
? this.decorateStorageEntryLinked(creator, decorateMethod)
: decorateMethod((...args: any[]): Observable<Codec> =>
this._rpcCore.state
// Unfortunately for one-shot calls we also use .subscribeStorage here
.subscribeStorage<[Codec]>([getArgs(...args)])
// 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
.pipe(map(([data]): Codec => data)), { methodName: creator.method });
: decorateMethod((...args: any[]): Observable<Codec> => (
this.hasSubscriptions
? this._rpcCore.state
// Unfortunately for one-shot calls we also use .subscribeStorage here
.subscribeStorage<[Codec]>([getArgs(...args)])
// 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
.pipe(map(([data]): Codec => data))
: this._rpcCore.state.getStorage(getArgs(...args))
), { methodName: creator.method });

decorated.creator = creator;

Expand All @@ -267,10 +281,12 @@ export default abstract class Decorate<ApiType> extends Events {
decorated.key = (arg1?: Arg, arg2?: Arg): string =>
u8aToHex(compactStripLength(creator(creator.meta.type.isDoubleMap ? [arg1, arg2] : arg1))[1]);

// When using double map storage function, user need to pass double map key as an array
decorated.multi = decorateMethod((args: (Arg | Arg[])[]): Observable<Codec[]> =>
this._rpcCore.state.subscribeStorage(
args.map((arg: Arg[] | Arg): [StorageEntry, Arg | Arg[]] => [creator, arg])));
if (this.hasSubscriptions) {
// When using double map storage function, user need to pass double map key as an array
decorated.multi = decorateMethod((args: (Arg | Arg[])[]): Observable<Codec[]> =>
this._rpcCore.state.subscribeStorage(
args.map((arg: Arg[] | Arg): [StorageEntry, Arg | Arg[]] => [creator, arg])));
}

decorated.size = decorateMethod((arg1?: Arg, arg2?: Arg): Observable<u64> =>
this._rpcCore.state.getStorageSize(getArgs(arg1, arg2)));
Expand Down
6 changes: 4 additions & 2 deletions packages/api/src/base/Init.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import extrinsicsFromMeta from '@polkadot/api-metadata/extrinsics/fromMetadata';
import storageFromMeta from '@polkadot/api-metadata/storage/fromMetadata';
import { GenericCall, GenericEvent, Metadata, u32 as U32 } from '@polkadot/types';
import { LATEST_VERSION as EXTRINSIC_LATEST_VERSION } from '@polkadot/types/primitive/Extrinsic/constants';
import { assert, logger } from '@polkadot/util';
import { logger } from '@polkadot/util';
import { cryptoWaitReady, setSS58Format } from '@polkadot/util-crypto';
import addressDefaults from '@polkadot/util-crypto/address/defaults';

Expand Down Expand Up @@ -59,7 +59,9 @@ export default abstract class Init<ApiType> extends Decorate<ApiType> {
public constructor (options: ApiOptions, type: ApiTypes, decorateMethod: DecorateMethod<ApiType>) {
super(options, type, decorateMethod);

assert(this._rpcCore.provider.hasSubscriptions, 'Api can only be used with a provider supporting subscriptions');
if (!this.hasSubscriptions) {
console.warn('Api will be available in a limited mode since the provider does not support subscriptions');
}

// We only register the types (global) if this is not a cloned instance.
// Do right up-front, so we get in the user types before we are actually
Expand Down
6 changes: 3 additions & 3 deletions packages/api/src/base/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ try {
}

function assertResult<T> (value: T | undefined): T {
assert(!isUndefined(value), 'Api needs to be initialised before using, listen on \'ready\'');
assert(!isUndefined(value), 'Api needs to be initialized before using, listen on \'ready\'');

return value as T;
}
Expand Down Expand Up @@ -163,7 +163,7 @@ export default abstract class ApiBase<ApiType> extends Init<ApiType> {
/**
* @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.
*
* RPC endpoints available here allow for the query of chain, node and system information, in addition to providing interfaces for the raw queries of state (usine known keys) and the submission of transactions.
* RPC endpoints available here allow for the query of chain, node and system information, in addition to providing interfaces for the raw queries of state (using known keys) and the submission of transactions.
*
* @example
* <BR>
Expand Down Expand Up @@ -225,7 +225,7 @@ export default abstract class ApiBase<ApiType> extends Init<ApiType> {
}

/**
* @description Disconnect from the underlying provider, halting all comms
* @description Disconnect from the underlying provider, halting all network traffic
*/
public disconnect (): void {
this._rpcCore.disconnect();
Expand Down
4 changes: 2 additions & 2 deletions packages/api/src/submittable/Submittable.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ export default class Submittable<ApiType> extends _Extrinsic implements Submitta
// signAndSend implementation for all 3 cases above
public signAndSend (account: IKeyringPair | string | AccountId | Address, optionsOrStatus?: Partial<SignerOptions> | Callback<SubmittableResultImpl>, optionalStatusCb?: Callback<SubmittableResultImpl>): SubmitableResultResult<ApiType> | SubmitableResultSubscription<ApiType> {
const [options, statusCb] = this._makeSignAndSendOptions(optionsOrStatus, optionalStatusCb);
const isSubscription = this._ignoreStatusCb || !!statusCb;
const isSubscription = this._api.hasSubscriptions && (this._ignoreStatusCb || !!statusCb);
const address = isKeyringPair(account) ? account.address : account.toString();
let updateId: number | undefined;

Expand Down Expand Up @@ -106,7 +106,7 @@ export default class Submittable<ApiType> extends _Extrinsic implements Submitta

// send implementation for both immediate Hash and statusCb variants
public send (statusCb?: Callback<SubmittableResultImpl>): SubmitableResultResult<ApiType> | SubmitableResultSubscription<ApiType> {
const isSubscription = this._ignoreStatusCb || !!statusCb;
const isSubscription = this._api.hasSubscriptions && (this._ignoreStatusCb || !!statusCb);

return this._decorateMethod(
isSubscription
Expand Down
2 changes: 1 addition & 1 deletion packages/types/src/codec/UInt.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ export default class UInt extends AbstractInt {
*/
public toRawType (): string {
// NOTE In the case of balances, which have a special meaning on the UI
// and can be interpreted differently, return a specifc value for it so
// and can be interpreted differently, return a specific value for it so
// underlying it always matches (no matter which length it actually is)
return this instanceof ClassOf('Balance')
? 'Balance'
Expand Down