diff --git a/packages/types/src/create/registry.ts b/packages/types/src/create/registry.ts index a3f9e1701d..4ec489a612 100644 --- a/packages/types/src/create/registry.ts +++ b/packages/types/src/create/registry.ts @@ -2,7 +2,8 @@ // SPDX-License-Identifier: Apache-2.0 import type { ExtDef } from '../extrinsic/signedExtensions/types'; -import type { ChainProperties, CodecHash, DispatchErrorModule, Hash, MetadataLatest, PortableRegistry, SiLookupTypeId } from '../interfaces/types'; +import type { ChainProperties, CodecHash, DispatchErrorModule, Hash, MetadataLatest, PortableRegistry, SiLookupTypeId, SiVariant } from '../interfaces/types'; +import type { Text } from '../primitive/Text'; import type { CallFunction, Codec, CodecHasher, Constructor, DetectCodec, DetectConstructor, RegisteredTypes, Registry, RegistryError, RegistryTypes } from '../types'; import type { CreateOptions } from './types'; @@ -16,89 +17,104 @@ import { expandExtensionTypes, fallbackExtensions, findUnknownExtensions } from import { GenericEventData } from '../generic/Event'; import * as baseTypes from '../index.types'; import * as definitions from '../interfaces/definitions'; -import { decorateConstants, decorateExtrinsics } from '../metadata/decorate'; +import { decorateConstants, decorateExtrinsics, filterEventsSome } from '../metadata/decorate'; import { Metadata } from '../metadata/Metadata'; import { createClass } from './createClass'; import { createTypeUnsafe } from './createType'; const l = logger('registry'); +function getVariantArgs (lookup: PortableRegistry, { fields }: SiVariant): string[] { + const args = new Array(fields.length); + + for (let i = 0; i < fields.length; i++) { + args[i] = lookup.getTypeDef(fields[i].type).type; + } + + return args; +} + +function valueToString (v: Text): string { + return v.toString(); +} + // create error mapping from metadata function injectErrors (_: Registry, metadata: Metadata, metadataErrors: Record): void { const { lookup, pallets } = metadata.asLatest; - // decorate the errors - pallets.forEach(({ errors, index, name }, _sectionIndex): void => { - if (errors.isNone) { - return; + for (let i = 0; i < pallets.length; i++) { + const { errors, index, name } = pallets[i]; + + if (errors.isSome) { + const sectionIndex = metadata.version >= 12 + ? index.toNumber() + : i; + const sectionName = stringCamelCase(name); + const { variants } = lookup.getSiType(errors.unwrap().type).def.asVariant; + + for (let i = 0; i < variants.length; i++) { + const { docs, fields, index, name } = variants[i]; + const variantIndex = index.toNumber(); + + metadataErrors[u8aToHex(new Uint8Array([sectionIndex, variantIndex]))] = { + args: getVariantArgs(lookup, variants[i]), + docs: docs.map(valueToString), + fields, + index: variantIndex, + method: name.toString(), + name: name.toString(), + section: sectionName + }; + } } - - const sectionIndex = metadata.version >= 12 - ? index.toNumber() - : _sectionIndex; - const sectionName = stringCamelCase(name); - - lookup.getSiType(errors.unwrap().type).def.asVariant.variants.forEach(({ docs, fields, index, name }): void => { - const variantIndex = index.toNumber(); - const eventIndex = new Uint8Array([sectionIndex, variantIndex]); - - metadataErrors[u8aToHex(eventIndex)] = { - args: fields.map(({ type }) => - lookup.getTypeDef(type).type - ), - docs: docs.map((d) => d.toString()), - fields, - index: variantIndex, - method: name.toString(), - name: name.toString(), - section: sectionName - }; - }); - }); + } } // create event classes from metadata function injectEvents (registry: Registry, metadata: Metadata, metadataEvents: Record>): void { const { lookup, pallets } = metadata.asLatest; + const filtered = pallets.filter(filterEventsSome); - // decorate the events - pallets - .filter(({ events }) => events.isSome) - .forEach(({ events, index, name }, _sectionIndex): void => { - const sectionIndex = metadata.version >= 12 - ? index.toNumber() - : _sectionIndex; - const sectionName = stringCamelCase(name); + for (let i = 0; i < filtered.length; i++) { + const { events, index, name } = filtered[i]; - lookup.getSiType(events.unwrap().type).def.asVariant.variants.forEach((variant): void => { - const variantIndex = variant.index.toNumber(); - const eventIndex = new Uint8Array([sectionIndex, variantIndex]); - const meta = registry.createType('EventMetadataLatest', { - ...variant, - args: variant.fields.map(({ type }) => - lookup.getTypeDef(type).type - ) - }); + const sectionIndex = metadata.version >= 12 + ? index.toNumber() + : i; + const sectionName = stringCamelCase(name); + const { variants } = lookup.getSiType(events.unwrap().type).def.asVariant; - metadataEvents[u8aToHex(eventIndex)] = class extends GenericEventData { - constructor (registry: Registry, value: Uint8Array) { - super(registry, value, meta, sectionName, variant.name.toString()); - } - }; + for (let i = 0; i < variants.length; i++) { + const { index, name } = variants[i]; + const meta = registry.createType('EventMetadataLatest', { + ...variants[i], + args: getVariantArgs(lookup, variants[i]) }); - }); + + metadataEvents[u8aToHex(new Uint8Array([sectionIndex, index.toNumber()]))] = class extends GenericEventData { + constructor (registry: Registry, value: Uint8Array) { + super(registry, value, meta, sectionName, name.toString()); + } + }; + } + } } // create extrinsic mapping from metadata function injectExtrinsics (registry: Registry, metadata: Metadata, metadataCalls: Record): void { const extrinsics = decorateExtrinsics(registry, metadata.asLatest, metadata.version); + const sections = Object.values(extrinsics); // decorate the extrinsics - Object.values(extrinsics).forEach((methods): void => - Object.values(methods).forEach((method): void => { + for (let i = 0; i < sections.length; i++) { + const methods = Object.values(sections[i]); + + for (let j = 0; j < methods.length; j++) { + const method = methods[j]; + metadataCalls[u8aToHex(method.callIndex)] = method; - }) - ); + } + } } // extract additional properties from the metadata @@ -169,9 +185,12 @@ export class TypeRegistry implements Registry { // register know, first classes then on-demand-created definitions this.register(this.#knownDefaults); - Object.values(this.#knownDefinitions).forEach(({ types }): void => - this.register(types) - ); + + const allKnown = Object.values(this.#knownDefinitions); + + for (let i = 0; i < allKnown.length; i++) { + this.register(allKnown[i].types); + } return this; } @@ -199,7 +218,7 @@ export class TypeRegistry implements Registry { const allTokens = this.#chainProperties.tokenSymbol.unwrap(); if (allTokens.length) { - return allTokens.map((s) => s.toString()); + return allTokens.map(valueToString); } } @@ -332,11 +351,16 @@ export class TypeRegistry implements Registry { // we cannot rely on export order (anymore, since babel/core 7.15.8), so in the case of // items such as u32 & U32, we get the lowercase versions here... not quite as optimal // (previously this used to be a simple find & return) - const names = [...this.#classes.entries()] - .filter(([, Clazz]) => Type === Clazz) - .map(([name]) => name) - .sort() - .reverse(); + const names: string[] = []; + + for (const [name, Clazz] of this.#classes.entries()) { + if (Type === Clazz) { + names.push(name); + } + } + + // both sort and reverse are done in-place + names.sort().reverse(); return names.length ? names[0] @@ -408,7 +432,11 @@ export class TypeRegistry implements Registry { } private _registerObject (obj: RegistryTypes): void { - Object.entries(obj).forEach(([name, type]): void => { + const entries = Object.entries(obj); + + for (let e = 0; e < entries.length; e++) { + const [name, type] = entries[e]; + if (isFunction(type)) { // This _looks_ a bit funny, but `typeof Clazz === 'function' this.#classes.set(name, type); @@ -426,7 +454,7 @@ export class TypeRegistry implements Registry { this.#definitions.set(name, def); } - }); + } } // sets the chain properties diff --git a/packages/types/src/metadata/decorate/constants/index.ts b/packages/types/src/metadata/decorate/constants/index.ts index c2d6c36270..ddc5a4959a 100644 --- a/packages/types/src/metadata/decorate/constants/index.ts +++ b/packages/types/src/metadata/decorate/constants/index.ts @@ -10,21 +10,26 @@ import { hexToU8a, stringCamelCase } from '@polkadot/util'; /** @internal */ // eslint-disable-next-line @typescript-eslint/no-unused-vars export function decorateConstants (registry: Registry, { pallets }: MetadataLatest, _metaVersion: number): Constants { - return pallets.reduce((result: Constants, { constants, name }): Constants => { - if (constants.isEmpty) { - return result; + const result: Constants = {}; + + for (let p = 0; p < pallets.length; p++) { + const { constants, name } = 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; } + } - // For access, we change the index names, i.e. Democracy.EnactmentPeriod -> democracy.enactmentPeriod - result[stringCamelCase(name)] = constants.reduce((newModule: ModuleConstants, meta): ModuleConstants => { - 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; - - return newModule; - }, {} as ModuleConstants); - - return result; - }, {} as Constants); + return result; } diff --git a/packages/types/src/metadata/decorate/errors/index.ts b/packages/types/src/metadata/decorate/errors/index.ts index 306278374d..67bdbb3ca5 100644 --- a/packages/types/src/metadata/decorate/errors/index.ts +++ b/packages/types/src/metadata/decorate/errors/index.ts @@ -27,27 +27,33 @@ export function variantToMeta (lookup: PortableRegistry, variant: SiVariant): It /** @internal */ export function decorateErrors (registry: Registry, { lookup, pallets }: MetadataLatest, metaVersion: number): Errors { - return pallets.reduce((result: Errors, { errors, index, name }, _sectionIndex): Errors => { - if (!errors.isSome) { - return result; + const result: Errors = {}; + + for (let p = 0; p < pallets.length; p++) { + const { errors, index, name } = 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; } + } - const sectionIndex = metaVersion >= 12 - ? index.toNumber() - : _sectionIndex; - - result[stringCamelCase(name)] = lookup.getSiType(errors.unwrap().type).def.asVariant.variants.reduce((newModule: ModuleErrors, variant): ModuleErrors => { - // 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)) - }; - - return newModule; - }, {} as ModuleErrors); - - return result; - }, {} as Errors); + return result; } diff --git a/packages/types/src/metadata/decorate/events/index.ts b/packages/types/src/metadata/decorate/events/index.ts index cdf6e422c2..d06a855576 100644 --- a/packages/types/src/metadata/decorate/events/index.ts +++ b/packages/types/src/metadata/decorate/events/index.ts @@ -1,7 +1,7 @@ // Copyright 2017-2021 @polkadot/types authors & contributors // SPDX-License-Identifier: Apache-2.0 -import type { MetadataLatest } from '../../../interfaces'; +import type { MetadataLatest, PalletMetadataV14 } from '../../../interfaces'; import type { AnyTuple, IEvent, Registry } from '../../../types'; import type { Events, ModuleEvents } from '../types'; @@ -9,27 +9,37 @@ import { stringCamelCase } from '@polkadot/util'; import { variantToMeta } from '../errors'; +export function filterEventsSome ({ events }: PalletMetadataV14): boolean { + return events.isSome; +} + /** @internal */ export function decorateEvents (registry: Registry, { lookup, pallets }: MetadataLatest, metaVersion: number): Events { - return pallets - .filter(({ events }) => events.isSome) - .reduce((result: Events, { events, index, name }, _sectionIndex): Events => { - const sectionIndex = metaVersion >= 12 - ? index.toNumber() - : _sectionIndex; + const filtered = pallets.filter(filterEventsSome); + const result: Events = {}; - result[stringCamelCase(name)] = lookup.getSiType(events.unwrap().type).def.asVariant.variants.reduce((newModule: ModuleEvents, variant): ModuleEvents => { - // 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)) - }; + 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; - return newModule; - }, {} as ModuleEvents); + for (let v = 0; v < variants.length; v++) { + const variant = variants[v]; - return result; - }, {} as Events); + // 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; + } + + return result; } diff --git a/packages/types/src/metadata/decorate/extrinsics/index.ts b/packages/types/src/metadata/decorate/extrinsics/index.ts index 2201239b49..317fd070fa 100644 --- a/packages/types/src/metadata/decorate/extrinsics/index.ts +++ b/packages/types/src/metadata/decorate/extrinsics/index.ts @@ -1,7 +1,7 @@ // Copyright 2017-2021 @polkadot/types authors & contributors // SPDX-License-Identifier: Apache-2.0 -import type { MetadataLatest } from '../../../interfaces'; +import type { MetadataLatest, PalletMetadataV14 } from '../../../interfaces'; import type { Registry } from '../../../types'; import type { Extrinsics, ModuleExtrinsics } from '../types'; @@ -10,35 +10,48 @@ import { stringCamelCase } from '@polkadot/util'; import { getSiName } from '../../util'; import { createUnchecked } from './createUnchecked'; +export function filterCallsSome ({ calls }: PalletMetadataV14): boolean { + return calls.isSome; +} + /** @internal */ export function decorateExtrinsics (registry: Registry, { lookup, pallets }: MetadataLatest, metaVersion: number): Extrinsics { - return pallets - .filter(({ calls }) => calls.isSome) - .reduce((result: Extrinsics, { calls, index, name }, _sectionIndex): Extrinsics => { - const sectionName = stringCamelCase(name); - const sectionIndex = metaVersion >= 12 - ? index.toNumber() - : _sectionIndex; + const filtered = pallets.filter(filterCallsSome); + const result: Extrinsics = {}; - result[sectionName] = lookup.getSiType(calls.unwrap().type).def.asVariant.variants - .reduce((newModule: ModuleExtrinsics, variant): ModuleExtrinsics => { - const callMetadata = registry.createType('FunctionMetadataLatest', { - ...variant, - args: variant.fields.map(({ name, type, typeName }, index) => ({ - name: stringCamelCase(name.unwrapOr(`param${index}`)), - type: getSiName(lookup, type), - ...(typeName.isSome - ? { typeName: typeName.unwrap() } - : {} - ) - })) - }); + for (let p = 0; p < filtered.length; p++) { + const { calls, index, name } = filtered[p]; + const sectionName = stringCamelCase(name); + const sectionIndex = metaVersion >= 12 + ? index.toNumber() + : p; + const newModule: ModuleExtrinsics = {}; + const { variants } = lookup.getSiType(calls.unwrap().type).def.asVariant; - newModule[stringCamelCase(callMetadata.name)] = createUnchecked(registry, sectionName, new Uint8Array([sectionIndex, callMetadata.index.toNumber()]), callMetadata); + for (let v = 0; v < variants.length; v++) { + const variant = variants[v]; + const args = new Array>(variant.fields.length); - return newModule; - }, {}); + for (let a = 0; a < variant.fields.length; a++) { + const { name, type, typeName } = variant.fields[a]; - return result; - }, {}); + args[a] = { + name: stringCamelCase(name.unwrapOr(`param${a}`)), + type: getSiName(lookup, type), + ...(typeName.isSome + ? { typeName: typeName.unwrap() } + : {} + ) + }; + } + + const callMetadata = registry.createType('FunctionMetadataLatest', { ...variant, args }); + + newModule[stringCamelCase(callMetadata.name)] = createUnchecked(registry, sectionName, new Uint8Array([sectionIndex, callMetadata.index.toNumber()]), callMetadata); + } + + result[sectionName] = newModule; + } + + return result; } diff --git a/packages/types/src/metadata/decorate/index.ts b/packages/types/src/metadata/decorate/index.ts index 397e4b18fb..d314621d40 100644 --- a/packages/types/src/metadata/decorate/index.ts +++ b/packages/types/src/metadata/decorate/index.ts @@ -9,8 +9,8 @@ import { assert } from '@polkadot/util'; import { Metadata } from '../Metadata'; import { decorateConstants } from './constants'; import { decorateErrors } from './errors'; -import { decorateEvents } from './events'; -import { decorateExtrinsics } from './extrinsics'; +import { decorateEvents, filterEventsSome } from './events'; +import { decorateExtrinsics, filterCallsSome } from './extrinsics'; import { decorateStorage } from './storage'; /** @@ -32,4 +32,4 @@ export function expandMetadata (registry: Registry, metadata: Metadata): Decorat }; } -export { decorateConstants, decorateErrors, decorateEvents, decorateExtrinsics, decorateStorage }; +export { decorateConstants, decorateErrors, decorateEvents, decorateExtrinsics, decorateStorage, filterCallsSome, filterEventsSome }; diff --git a/packages/types/src/metadata/decorate/storage/createFunction.ts b/packages/types/src/metadata/decorate/storage/createFunction.ts index 9093d3d4cd..16446da078 100644 --- a/packages/types/src/metadata/decorate/storage/createFunction.ts +++ b/packages/types/src/metadata/decorate/storage/createFunction.ts @@ -46,12 +46,17 @@ export function createKeyRaw (registry: Registry, itemFn: CreateItemBase, keys: ); } +/** @internal */ +function filterDefined (a: unknown): boolean { + return !isUndefined(a); +} + /** @internal */ function createKey (registry: Registry, itemFn: CreateItemFn, keys: SiLookupTypeId[], hashers: StorageHasher[], args: unknown[]): Uint8Array { const { method, section } = itemFn; assert(Array.isArray(args), () => `Call to ${stringCamelCase(section || 'unknown')}.${stringCamelCase(method || 'unknown')} needs ${keys.length} arguments`); - assert(args.filter((a) => !isUndefined(a)).length === keys.length, () => `Call to ${stringCamelCase(section || 'unknown')}.${stringCamelCase(method || 'unknown')} needs ${keys.length} arguments, found [${args.join(', ')}]`); + assert(args.filter(filterDefined).length === keys.length, () => `Call to ${stringCamelCase(section || 'unknown')}.${stringCamelCase(method || 'unknown')} needs ${keys.length} arguments, found [${args.join(', ')}]`); // as per createKey, always add the length prefix (underlying it is Bytes) return compactAddLength( diff --git a/packages/types/src/metadata/decorate/storage/getStorage.ts b/packages/types/src/metadata/decorate/storage/getStorage.ts index 59a200ceab..d7da3d242e 100644 --- a/packages/types/src/metadata/decorate/storage/getStorage.ts +++ b/packages/types/src/metadata/decorate/storage/getStorage.ts @@ -9,13 +9,12 @@ import { substrate } from './substrate'; /** @internal */ export function getStorage (registry: Registry): Storage { - return { - substrate: Object - .entries(substrate) - .reduce((storage: Record, [key, fn]): Record => { - storage[key] = fn(registry); + const storage: Record = {}; + const entries = Object.entries(substrate); - return storage; - }, {}) - }; + for (let e = 0; e < entries.length; e++) { + storage[entries[e][0]] = entries[e][1](registry); + } + + return { substrate: storage }; } diff --git a/packages/types/src/metadata/decorate/storage/index.ts b/packages/types/src/metadata/decorate/storage/index.ts index 11b1d3b3df..9028f3cd9c 100644 --- a/packages/types/src/metadata/decorate/storage/index.ts +++ b/packages/types/src/metadata/decorate/storage/index.ts @@ -14,36 +14,39 @@ import { createRuntimeFunction } from './util'; /** @internal */ // eslint-disable-next-line @typescript-eslint/no-unused-vars export function decorateStorage (registry: Registry, { pallets }: MetadataLatest, _metaVersion: number): Storage { - return pallets.reduce((result: Storage, moduleMetadata): Storage => { - if (moduleMetadata.storage.isNone) { - return result; + const result: Storage = getStorage(registry); + + for (let p = 0; p < pallets.length; p++) { + const { name, storage } = 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; } + } - const { name } = moduleMetadata; - const section = stringCamelCase(name); - const unwrapped = moduleMetadata.storage.unwrap(); - const prefix = unwrapped.prefix.toString(); - - // For access, we change the index names, i.e. System.Account -> system.account - result[section] = unwrapped.items.reduce((newModule, meta): ModuleStorage => { - const method = meta.name.toString(); - - newModule[stringLowerFirst(method)] = createFunction(registry, { - meta, - method, - prefix, - section - }, {}); - - return newModule; - }, { - 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) - } as ModuleStorage); - - return result; - }, { ...getStorage(registry) }); + return result; }