diff --git a/packages/types/src/create/lazy.ts b/packages/types/src/create/lazy.ts index 9eb209ef25..8f9494f289 100644 --- a/packages/types/src/create/lazy.ts +++ b/packages/types/src/create/lazy.ts @@ -18,9 +18,7 @@ export function lazyMethod (result: Record, item: K, creator: }); } -export function lazyMethods (items: K[], creator: (v: K) => T, getName?: (m: K) => string): Record { - const result: Record = {}; - +export function lazyMethods (items: K[], creator: (v: K) => T, getName?: (m: K) => string, result: Record = {}): Record { for (let i = 0; i < items.length; i++) { lazyMethod(result, items[i], creator, getName); } diff --git a/packages/types/src/metadata/decorate/constants/index.ts b/packages/types/src/metadata/decorate/constants/index.ts index ddc5a4959a..37ac98c8b2 100644 --- a/packages/types/src/metadata/decorate/constants/index.ts +++ b/packages/types/src/metadata/decorate/constants/index.ts @@ -1,33 +1,48 @@ // Copyright 2017-2021 @polkadot/types authors & contributors // SPDX-License-Identifier: Apache-2.0 -import type { MetadataLatest } from '../../../interfaces'; +import type { MetadataLatest, PalletConstantMetadataLatest, PalletMetadataLatest } from '../../../interfaces'; import type { Registry } from '../../../types'; -import type { ConstantCodec, Constants, ModuleConstants } from '../types'; +import type { ConstantCodec, Constants } from '../types'; import { hexToU8a, stringCamelCase } from '@polkadot/util'; +import { lazyMethod, lazyMethods } from '../../../create/lazy'; +import { objectNameToCamel } from '../util'; + +function createConstantCodec (registry: Registry, constant: PalletConstantMetadataLatest): ConstantCodec { + const codec = registry.createTypeUnsafe(registry.createLookupType(constant.type), [hexToU8a(constant.value.toHex())]) as ConstantCodec; + + (codec as unknown as Record).meta = constant; + + return codec; +} + /** @internal */ // eslint-disable-next-line @typescript-eslint/no-unused-vars -export function decorateConstants (registry: Registry, { pallets }: MetadataLatest, _metaVersion: number): Constants { +export function decorateConstants (registry: Registry, { pallets }: MetadataLatest, _version: number): Constants { const result: Constants = {}; + const lazySection = ({ constants, name }: PalletMetadataLatest): void => { + lazyMethod( + result, + constants, + (constants: PalletConstantMetadataLatest[]) => + lazyMethods( + constants, + (constant: PalletConstantMetadataLatest) => + createConstantCodec(registry, constant), + objectNameToCamel + ), + () => stringCamelCase(name) + ); + }; + for (let p = 0; p < pallets.length; p++) { - const { constants, name } = pallets[p]; + const pallet = pallets[p]; - if (!constants.isEmpty) { - const newModule: ModuleConstants = {}; - - for (let c = 0; c < constants.length; c++) { - const meta = constants[c]; - const codec = registry.createTypeUnsafe(registry.createLookupType(meta.type), [hexToU8a(meta.value.toHex())]) as unknown; - - (codec as Record).meta = meta; - newModule[stringCamelCase(meta.name)] = codec as ConstantCodec; - } - - // For access, we change the index names, i.e. Democracy.EnactmentPeriod -> democracy.enactmentPeriod - result[stringCamelCase(name)] = newModule; + if (!pallet.constants.isEmpty) { + lazySection(pallet); } } diff --git a/packages/types/src/metadata/decorate/errors/index.spec.ts b/packages/types/src/metadata/decorate/errors/index.spec.ts new file mode 100644 index 0000000000..c84a13a657 --- /dev/null +++ b/packages/types/src/metadata/decorate/errors/index.spec.ts @@ -0,0 +1,44 @@ +// Copyright 2017-2021 @polkadot/types authors & contributors +// SPDX-License-Identifier: Apache-2.0 + +import rpcMetadata from '@polkadot/types-support/metadata/static-substrate'; + +import { TypeRegistry } from '../../../create'; +import { Metadata } from '../../Metadata'; +import { decorateErrors } from '..'; + +const registry = new TypeRegistry(); +const metadata = new Metadata(registry, rpcMetadata); + +registry.setMetadata(metadata); + +const errors = decorateErrors(registry, metadata.asLatest, metadata.version); + +describe('decorateErrors', (): void => { + it('should return known errors', (): void => { + expect(errors.balances.InsufficientBalance).toBeDefined(); + }); + + it('has the correct metadata for known errors', (): void => { + expect( + errors.proxy.NotProxy.meta.toJSON() + ).toEqual({ + args: [], + docs: ['Sender is not a proxy of the account to be proxied.'], + fields: [], + index: 2, + name: 'NotProxy' + }); + }); + + it('should check against a specific error', (): void => { + expect( + errors.system.InvalidSpecName.is( + registry.createType('DispatchErrorModule', { + error: 0, + index: 0 + }) + ) + ).toBe(true); + }); +}); diff --git a/packages/types/src/metadata/decorate/errors/index.ts b/packages/types/src/metadata/decorate/errors/index.ts index 67bdbb3ca5..0c64087338 100644 --- a/packages/types/src/metadata/decorate/errors/index.ts +++ b/packages/types/src/metadata/decorate/errors/index.ts @@ -1,13 +1,16 @@ // Copyright 2017-2021 @polkadot/types authors & contributors // SPDX-License-Identifier: Apache-2.0 -import type { DispatchErrorModule, MetadataLatest, PortableRegistry, SiField, SiVariant } from '../../../interfaces'; +import type { DispatchErrorModule, MetadataLatest, PalletMetadataLatest, PortableRegistry, SiField, SiVariant } from '../../../interfaces'; import type { Text, u8 } from '../../../primitive'; import type { Registry } from '../../../types'; -import type { Errors, ModuleErrors } from '../types'; +import type { Errors, IsError } from '../types'; import { stringCamelCase } from '@polkadot/util'; +import { lazyMethod, lazyMethods } from '../../../create/lazy'; +import { objectNameToString } from '../util'; + interface ItemMeta { args: string[]; name: Text; @@ -25,33 +28,39 @@ export function variantToMeta (lookup: PortableRegistry, variant: SiVariant): It }; } +function createIsError (registry: Registry, lookup: PortableRegistry, variant: SiVariant, sectionIndex: number): IsError { + return { + is: ({ error, index }: DispatchErrorModule) => + index.eq(sectionIndex) && + error.eq(variant.index), + meta: registry.createType('ErrorMetadataLatest', variantToMeta(lookup, variant)) + }; +} + /** @internal */ -export function decorateErrors (registry: Registry, { lookup, pallets }: MetadataLatest, metaVersion: number): Errors { +export function decorateErrors (registry: Registry, { lookup, pallets }: MetadataLatest, version: number): Errors { const result: Errors = {}; + const lazySection = ({ errors, name }: PalletMetadataLatest, sectionIndex: number): void => { + lazyMethod( + result, + lookup.getSiType(errors.unwrap().type).def.asVariant.variants, + (variants: SiVariant[]) => + lazyMethods( + variants, + (variant: SiVariant) => + createIsError(registry, lookup, variant, sectionIndex), + objectNameToString + ), + () => stringCamelCase(name) + ); + }; + for (let p = 0; p < pallets.length; p++) { - const { errors, index, name } = pallets[p]; + const pallet = pallets[p]; - if (errors.isSome) { - const sectionIndex = metaVersion >= 12 - ? index.toNumber() - : p; - const newModule: ModuleErrors = {}; - const { variants } = lookup.getSiType(errors.unwrap().type).def.asVariant; - - for (let v = 0; v < variants.length; v++) { - const variant = variants[v]; - - // we don't camelCase the error name - newModule[variant.name.toString()] = { - is: ({ error, index }: DispatchErrorModule) => - index.eq(sectionIndex) && - error.eq(variant.index), - meta: registry.createType('ErrorMetadataLatest', variantToMeta(lookup, variant)) - }; - } - - result[stringCamelCase(name)] = newModule; + if (pallet.errors.isSome) { + lazySection(pallet, version >= 12 ? pallet.index.toNumber() : p); } } diff --git a/packages/types/src/metadata/decorate/events/index.spec.ts b/packages/types/src/metadata/decorate/events/index.spec.ts new file mode 100644 index 0000000000..e69dd968b5 --- /dev/null +++ b/packages/types/src/metadata/decorate/events/index.spec.ts @@ -0,0 +1,46 @@ +// Copyright 2017-2021 @polkadot/types authors & contributors +// SPDX-License-Identifier: Apache-2.0 + +import type { AnyTuple, IEvent } from '../../../types'; + +import rpcMetadata from '@polkadot/types-support/metadata/static-substrate'; + +import { TypeRegistry } from '../../../create'; +import { Metadata } from '../../Metadata'; +import { decorateEvents } from '..'; + +const registry = new TypeRegistry(); +const metadata = new Metadata(registry, rpcMetadata); + +registry.setMetadata(metadata); + +const events = decorateEvents(registry, metadata.asLatest, metadata.version); + +describe('decorateEvents', (): void => { + it('should return known errors', (): void => { + expect(events.balances.Transfer).toBeDefined(); + }); + + it('has the correct metadata for known errors', (): void => { + expect( + events.staking.Rewarded.meta.toJSON() + ).toEqual({ + args: ['AccountId32', 'u128'], + docs: [ + 'The nominator has been rewarded by this amount. \\[stash, amount\\]' + ], + fields: [ + { docs: [], name: null, type: 0, typeName: 'T::AccountId' }, + { docs: [], name: null, type: 6, typeName: 'BalanceOf' } + ], + index: 1, + name: 'Rewarded' + }); + }); + + it('should check against a specific error', (): void => { + expect( + events.system.ExtrinsicSuccess.is({ index: [0, 0] } as unknown as IEvent) + ).toBe(true); + }); +}); diff --git a/packages/types/src/metadata/decorate/events/index.ts b/packages/types/src/metadata/decorate/events/index.ts index d06a855576..7e7408cef4 100644 --- a/packages/types/src/metadata/decorate/events/index.ts +++ b/packages/types/src/metadata/decorate/events/index.ts @@ -1,44 +1,54 @@ // Copyright 2017-2021 @polkadot/types authors & contributors // SPDX-License-Identifier: Apache-2.0 -import type { MetadataLatest, PalletMetadataV14 } from '../../../interfaces'; +import type { MetadataLatest, PalletMetadataLatest, PortableRegistry, SiVariant } from '../../../interfaces'; import type { AnyTuple, IEvent, Registry } from '../../../types'; -import type { Events, ModuleEvents } from '../types'; +import type { Events, IsEvent } from '../types'; import { stringCamelCase } from '@polkadot/util'; +import { lazyMethod, lazyMethods } from '../../../create/lazy'; import { variantToMeta } from '../errors'; +import { objectNameToString } from '../util'; -export function filterEventsSome ({ events }: PalletMetadataV14): boolean { +export function filterEventsSome ({ events }: PalletMetadataLatest): boolean { return events.isSome; } +function createIsEvent (registry: Registry, lookup: PortableRegistry, variant: SiVariant, sectionIndex: number): IsEvent { + return { + is: (eventRecord: IEvent): eventRecord is IEvent => + sectionIndex === eventRecord.index[0] && + variant.index.eq(eventRecord.index[1]), + meta: registry.createType('EventMetadataLatest', variantToMeta(lookup, variant)) + }; +} + /** @internal */ -export function decorateEvents (registry: Registry, { lookup, pallets }: MetadataLatest, metaVersion: number): Events { - const filtered = pallets.filter(filterEventsSome); +export function decorateEvents (registry: Registry, { lookup, pallets }: MetadataLatest, version: number): Events { const result: Events = {}; + const lazySection = ({ events, name }: PalletMetadataLatest, sectionIndex: number): void => { + lazyMethod( + result, + lookup.getSiType(events.unwrap().type).def.asVariant.variants, + (variants: SiVariant[]) => + lazyMethods( + variants, + (variant: SiVariant) => + createIsEvent(registry, lookup, variant, sectionIndex), + objectNameToString + ), + () => stringCamelCase(name) + ); + }; + + const filtered = pallets.filter(filterEventsSome); + for (let p = 0; p < filtered.length; p++) { - const { events, index, name } = filtered[p]; - const sectionIndex = metaVersion >= 12 - ? index.toNumber() - : p; - const newModule: ModuleEvents = {}; - const { variants } = lookup.getSiType(events.unwrap().type).def.asVariant; + const pallet = filtered[p]; - for (let v = 0; v < variants.length; v++) { - const variant = variants[v]; - - // we don't camelCase the event name - newModule[variant.name.toString()] = { - is: (eventRecord: IEvent): eventRecord is IEvent => - eventRecord.index[0] === sectionIndex && - variant.index.eq(eventRecord.index[1]), - meta: registry.createType('EventMetadataLatest', variantToMeta(lookup, variant)) - }; - } - - result[stringCamelCase(name)] = newModule; + lazySection(pallet, version >= 12 ? pallet.index.toNumber() : p); } return result; diff --git a/packages/types/src/metadata/decorate/extrinsics/index.ts b/packages/types/src/metadata/decorate/extrinsics/index.ts index 317fd070fa..57e2098318 100644 --- a/packages/types/src/metadata/decorate/extrinsics/index.ts +++ b/packages/types/src/metadata/decorate/extrinsics/index.ts @@ -1,56 +1,73 @@ // Copyright 2017-2021 @polkadot/types authors & contributors // SPDX-License-Identifier: Apache-2.0 -import type { MetadataLatest, PalletMetadataV14 } from '../../../interfaces'; -import type { Registry } from '../../../types'; -import type { Extrinsics, ModuleExtrinsics } from '../types'; +import type { MetadataLatest, PalletMetadataLatest, PortableRegistry, SiVariant } from '../../../interfaces'; +import type { CallFunction, Registry } from '../../../types'; +import type { Extrinsics } from '../types'; import { stringCamelCase } from '@polkadot/util'; +import { lazyMethod, lazyMethods } from '../../../create/lazy'; import { getSiName } from '../../util'; +import { objectNameToCamel } from '../util'; import { createUnchecked } from './createUnchecked'; -export function filterCallsSome ({ calls }: PalletMetadataV14): boolean { +export function filterCallsSome ({ calls }: PalletMetadataLatest): boolean { return calls.isSome; } +function createCallFunction (registry: Registry, lookup: PortableRegistry, variant: SiVariant, sectionIndex: number, sectionName: string): CallFunction { + const { fields, index } = variant; + const args = new Array>(fields.length); + + for (let a = 0; a < fields.length; a++) { + const { name, type, typeName } = fields[a]; + + args[a] = { + name: stringCamelCase(name.unwrapOr(`param${a}`)), + type: getSiName(lookup, type), + ...(typeName.isSome + ? { typeName: typeName.unwrap() } + : {} + ) + }; + } + + return createUnchecked( + registry, + sectionName, + new Uint8Array([sectionIndex, index.toNumber()]), + registry.createType('FunctionMetadataLatest', { ...variant, args }) + ); +} + /** @internal */ -export function decorateExtrinsics (registry: Registry, { lookup, pallets }: MetadataLatest, metaVersion: number): Extrinsics { - const filtered = pallets.filter(filterCallsSome); +export function decorateExtrinsics (registry: Registry, { lookup, pallets }: MetadataLatest, version: number): Extrinsics { const result: Extrinsics = {}; - for (let p = 0; p < filtered.length; p++) { - const { calls, index, name } = filtered[p]; + const lazySection = ({ calls, name }: PalletMetadataLatest, sectionIndex: number): void => { const sectionName = stringCamelCase(name); - const sectionIndex = metaVersion >= 12 - ? index.toNumber() - : p; - const newModule: ModuleExtrinsics = {}; - const { variants } = lookup.getSiType(calls.unwrap().type).def.asVariant; - for (let v = 0; v < variants.length; v++) { - const variant = variants[v]; - const args = new Array>(variant.fields.length); + lazyMethod( + result, + lookup.getSiType(calls.unwrap().type).def.asVariant.variants, + (variants: SiVariant[]) => + lazyMethods( + variants, + (variant: SiVariant) => + createCallFunction(registry, lookup, variant, sectionIndex, sectionName), + objectNameToCamel + ), + () => sectionName + ); + }; - for (let a = 0; a < variant.fields.length; a++) { - const { name, type, typeName } = variant.fields[a]; + const filtered = pallets.filter(filterCallsSome); - args[a] = { - name: stringCamelCase(name.unwrapOr(`param${a}`)), - type: getSiName(lookup, type), - ...(typeName.isSome - ? { typeName: typeName.unwrap() } - : {} - ) - }; - } + for (let p = 0; p < filtered.length; p++) { + const pallet = filtered[p]; - const callMetadata = registry.createType('FunctionMetadataLatest', { ...variant, args }); - - newModule[stringCamelCase(callMetadata.name)] = createUnchecked(registry, sectionName, new Uint8Array([sectionIndex, callMetadata.index.toNumber()]), callMetadata); - } - - result[sectionName] = newModule; + lazySection(pallet, version >= 12 ? pallet.index.toNumber() : p); } return result; diff --git a/packages/types/src/metadata/decorate/storage/index.ts b/packages/types/src/metadata/decorate/storage/index.ts index 9028f3cd9c..26ece7b26d 100644 --- a/packages/types/src/metadata/decorate/storage/index.ts +++ b/packages/types/src/metadata/decorate/storage/index.ts @@ -1,12 +1,14 @@ // Copyright 2017-2021 @polkadot/types authors & contributors // SPDX-License-Identifier: Apache-2.0 -import type { MetadataLatest } from '../../../interfaces'; +import type { MetadataLatest, PalletMetadataLatest, StorageEntryMetadataLatest } from '../../../interfaces'; import type { Registry } from '../../../types'; -import type { ModuleStorage, Storage } from '../types'; +import type { Storage } from '../types'; -import { stringCamelCase, stringLowerFirst } from '@polkadot/util'; +import { stringCamelCase } from '@polkadot/util'; +import { lazyMethod, lazyMethods } from '../../../create/lazy'; +import { objectNameFirstLower } from '../util'; import { createFunction, createKeyRaw } from './createFunction'; import { getStorage } from './getStorage'; import { createRuntimeFunction } from './util'; @@ -16,35 +18,37 @@ import { createRuntimeFunction } from './util'; export function decorateStorage (registry: Registry, { pallets }: MetadataLatest, _metaVersion: number): Storage { const result: Storage = getStorage(registry); + const lazySection = ({ name, storage }: PalletMetadataLatest): void => { + const section = stringCamelCase(name); + const { items, prefix: _prefix } = storage.unwrap(); + const prefix = _prefix.toString(); + + lazyMethod( + result, + items, + (items: StorageEntryMetadataLatest[]) => + lazyMethods( + items, + (meta: StorageEntryMetadataLatest) => + createFunction(registry, { meta, method: meta.name.toString(), prefix, section }, {}), + objectNameFirstLower, + { + palletVersion: createRuntimeFunction( + { method: 'palletVersion', prefix, section }, + createKeyRaw(registry, { method: ':__STORAGE_VERSION__:', prefix: name.toString() }, [], [], []), + { docs: 'Returns the current pallet version from storage', type: 'u16' } + )(registry) + } + ), + () => section + ); + }; + for (let p = 0; p < pallets.length; p++) { - const { name, storage } = pallets[p]; + const pallet = pallets[p]; - if (storage.isSome) { - const section = stringCamelCase(name); - const { items, prefix: _prefix } = storage.unwrap(); - const prefix = _prefix.toString(); - const newModule: ModuleStorage = { - palletVersion: createRuntimeFunction( - { method: 'palletVersion', prefix, section }, - createKeyRaw(registry, { method: ':__STORAGE_VERSION__:', prefix: name.toString() }, [], [], []), - { docs: 'Returns the current pallet version from storage', type: 'u16' } - )(registry) - }; - - for (let i = 0; i < items.length; i++) { - const meta = items[i]; - const method = meta.name.toString(); - - // For access, we change the index names, i.e. System.Account -> system.account - newModule[stringLowerFirst(method)] = createFunction(registry, { - meta, - method, - prefix, - section - }, {}); - } - - result[section] = newModule; + if (pallet.storage.isSome) { + lazySection(pallet); } } diff --git a/packages/types/src/metadata/decorate/util.ts b/packages/types/src/metadata/decorate/util.ts new file mode 100644 index 0000000000..c432379312 --- /dev/null +++ b/packages/types/src/metadata/decorate/util.ts @@ -0,0 +1,18 @@ +// Copyright 2017-2021 @polkadot/types authors & contributors +// SPDX-License-Identifier: Apache-2.0 + +import type { Text } from '../../primitive'; + +import { stringCamelCase, stringLowerFirst } from '@polkadot/util'; + +export function objectNameFirstLower ({ name }: { name: string | Text }): string { + return stringLowerFirst(name); +} + +export function objectNameToCamel ({ name }: { name: string | Text }): string { + return stringCamelCase(name); +} + +export function objectNameToString ({ name }: { name: string | Text }): string { + return name.toString(); +}