diff --git a/packages/api/src/base/Decorate.ts b/packages/api/src/base/Decorate.ts index b5be9f1109..57d998b2c0 100644 --- a/packages/api/src/base/Decorate.ts +++ b/packages/api/src/base/Decorate.ts @@ -6,7 +6,7 @@ import type { RpcInterface } from '@polkadot/rpc-core/types'; import type { Option, Raw, StorageKey, Text, u64 } from '@polkadot/types'; import type { Call, Hash, RuntimeVersion } from '@polkadot/types/interfaces'; import type { StorageEntry } from '@polkadot/types/primitive/types'; -import type { AnyFunction, CallFunction, Codec, CodecArg as Arg, InterfaceTypes, Registry, RegistryTypes } from '@polkadot/types/types'; +import type { AnyFunction, CallFunction, Codec, CodecArg as Arg, DefinitionRpc, DefinitionRpcSub, InterfaceTypes, Registry, RegistryTypes } from '@polkadot/types/types'; import type { SubmittableExtrinsic } from '../submittable/types'; import type { ApiInterfaceRx, ApiOptions, ApiTypes, DecoratedRpc, DecoratedRpcSection, DecorateMethod, PaginationOptions, QueryableConsts, QueryableModuleStorage, QueryableStorage, QueryableStorageEntry, QueryableStorageMulti, QueryableStorageMultiArg, SubmittableExtrinsicFunction, SubmittableExtrinsics, SubmittableModuleExtrinsics } from '../types'; @@ -211,7 +211,7 @@ export abstract class Decorate extends Events { // manner to cater for both old and new: // - when the number of entries are 0, only remove the ones with isOptional (account & contracts) // - when non-zero, remove anything that is not in the array (we don't do this) - protected async _filterRpc (): Promise { + protected async _filterRpc (additional: Record>): Promise { let methods: string[]; try { @@ -222,6 +222,15 @@ export abstract class Decorate extends Events { methods = []; } + // add any specific user-base RPCs + if (Object.keys(additional).length !== 0) { + this._rpcCore.addUserInterfaces(additional); + + // re-decorate, only adding any new additional interfaces + this._decorateRpc(this._rpcCore, this._decorateMethod, this._rpc); + this._decorateRpc(this._rpcCore, this._rxDecorateMethod, this._rx.rpc); + } + this._filterRpcMethods(methods); } @@ -267,26 +276,28 @@ export abstract class Decorate extends Events { }); } - protected _decorateRpc (rpc: RpcCore, decorateMethod: DecorateMethod): DecoratedRpc { + protected _decorateRpc (rpc: RpcCore, decorateMethod: DecorateMethod, input: Partial> = {}): DecoratedRpc { return rpc.sections.reduce((out, _sectionName): DecoratedRpc => { const sectionName = _sectionName as keyof DecoratedRpc; - // out and section here are horrors to get right from a typing perspective :( - (out as Record)[sectionName] = Object.entries(rpc[sectionName]).reduce((section, [methodName, method]): DecoratedRpcSection => { - // skip subscriptions where we have a non-subscribe interface - if (this.hasSubscriptions || !(methodName.startsWith('subscribe') || methodName.startsWith('unsubscribe'))) { - (section as Record)[methodName] = decorateMethod(method, { methodName }) as unknown; - // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access - (section as Record)[methodName].json = decorateMethod(method.json, { methodName }) as unknown; - // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access - (section as Record)[methodName].raw = decorateMethod(method.raw, { methodName }) as unknown; - } + if (!(out as Record)[sectionName]) { + // out and section here are horrors to get right from a typing perspective :( + (out as Record)[sectionName] = Object.entries(rpc[sectionName]).reduce((section, [methodName, method]): DecoratedRpcSection => { + // skip subscriptions where we have a non-subscribe interface + if (this.hasSubscriptions || !(methodName.startsWith('subscribe') || methodName.startsWith('unsubscribe'))) { + (section as Record)[methodName] = decorateMethod(method, { methodName }) as unknown; + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access + (section as Record)[methodName].json = decorateMethod(method.json, { methodName }) as unknown; + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access + (section as Record)[methodName].raw = decorateMethod(method.raw, { methodName }) as unknown; + } - return section; - }, {} as DecoratedRpcSection); + return section; + }, {} as DecoratedRpcSection); + } return out; - }, {} as DecoratedRpc); + }, input as DecoratedRpc); } protected _decorateMulti (decorateMethod: DecorateMethod): QueryableStorageMulti { diff --git a/packages/api/src/base/Init.ts b/packages/api/src/base/Init.ts index d12e048141..4117776fb2 100644 --- a/packages/api/src/base/Init.ts +++ b/packages/api/src/base/Init.ts @@ -15,7 +15,7 @@ import { map, switchMap } from 'rxjs/operators'; import { Metadata } from '@polkadot/metadata'; import { TypeRegistry } from '@polkadot/types/create'; import { LATEST_EXTRINSIC_VERSION } from '@polkadot/types/extrinsic/Extrinsic'; -import { getSpecAlias, getSpecTypes, getUpgradeVersion } from '@polkadot/types-known'; +import { getSpecAlias, getSpecRpc, getSpecTypes, getUpgradeVersion } from '@polkadot/types-known'; import { assert, BN_ZERO, logger, u8aEq, u8aToU8a } from '@polkadot/util'; import { cryptoWaitReady } from '@polkadot/util-crypto'; @@ -80,7 +80,7 @@ export abstract class Init extends Decorate { registry.setKnownTypes(this._options); registry.register(getSpecTypes(registry, chain, version.specName, version.specVersion)); - // for bundled types, pull through the aliasses defined + // for bundled types, pull through the aliases defined if (registry.knownTypes.typesBundle) { registry.knownTypes.typesAlias = getSpecAlias(registry, chain, version.specName); } @@ -246,7 +246,7 @@ export abstract class Init extends Decorate { this._subscribeUpdates(); // filter the RPC methods (this does an rpc-methods call) - await this._filterRpc(); + await this._filterRpc(getSpecRpc(this.registry, chain, runtimeVersion.specName)); // retrieve metadata, either from chain or as pass-in via options const metadataKey = `${this._genesisHash?.toHex() || '0x'}-${runtimeVersion.specVersion.toString()}`;