Internal adjustments (part 5) (#4137)

This commit is contained in:
Jaco
2021-11-01 10:56:16 +02:00
committed by GitHub
parent 71a0a84f26
commit 4a893b9feb
9 changed files with 259 additions and 190 deletions
+95 -67
View File
@@ -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<string>(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<string, RegistryError>): 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<string, Constructor<GenericEventData>>): 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<string, CallFunction>): 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
@@ -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<string, unknown>).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<string, unknown>).meta = meta;
newModule[stringCamelCase(meta.name)] = codec as ConstantCodec;
return newModule;
}, {} as ModuleConstants);
return result;
}, {} as Constants);
return result;
}
@@ -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;
}
@@ -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: <T extends AnyTuple> (eventRecord: IEvent<AnyTuple>): eventRecord is IEvent<T> =>
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: <T extends AnyTuple> (eventRecord: IEvent<AnyTuple>): eventRecord is IEvent<T> =>
eventRecord.index[0] === sectionIndex &&
variant.index.eq(eventRecord.index[1]),
meta: registry.createType('EventMetadataLatest', variantToMeta(lookup, variant))
};
}
result[stringCamelCase(name)] = newModule;
}
return result;
}
@@ -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<Record<string, unknown>>(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;
}
@@ -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 };
@@ -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(
@@ -9,13 +9,12 @@ import { substrate } from './substrate';
/** @internal */
export function getStorage (registry: Registry): Storage {
return {
substrate: Object
.entries(substrate)
.reduce((storage: Record<string, StorageEntry>, [key, fn]): Record<string, StorageEntry> => {
storage[key] = fn(registry);
const storage: Record<string, StorageEntry> = {};
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 };
}
@@ -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;
}