From 1aa7899a7a0a425c84bb5d5a9b4aff734a529fd2 Mon Sep 17 00:00:00 2001 From: Jaco Greeff Date: Sun, 11 Aug 2019 14:01:20 +0200 Subject: [PATCH] Allow string types inputs into codec (#1292) * Allow string types inputs into codec * Linkage allows string constructors * Import case * Cleanup keyof imports * Attempt linkage doc fix --- packages/types/src/codec/Compact.spec.ts | 8 +++- packages/types/src/codec/Compact.ts | 9 ++-- packages/types/src/codec/EnumType.spec.ts | 9 ++++ packages/types/src/codec/EnumType.ts | 11 +++-- packages/types/src/codec/Linkage.ts | 20 ++++++-- packages/types/src/codec/Option.spec.ts | 6 +++ packages/types/src/codec/Option.ts | 18 +++---- packages/types/src/codec/Struct.spec.ts | 12 +++++ packages/types/src/codec/Struct.ts | 45 +++++++----------- packages/types/src/codec/Tuple.spec.ts | 9 ++++ packages/types/src/codec/Tuple.ts | 22 +++++---- packages/types/src/codec/Vec.spec.ts | 6 +++ packages/types/src/codec/Vec.ts | 19 ++++---- packages/types/src/codec/VecFixed.ts | 14 +++--- packages/types/src/codec/createType.ts | 47 ++++++++++--------- packages/types/src/codec/utils/index.ts | 1 + .../types/src/codec/utils/mapToTypeMap.ts | 30 ++++++++++++ .../Extrinsic/v1/ExtrinsicPayload.ts | 5 +- .../Extrinsic/v1/ExtrinsicSignature.ts | 8 ++-- .../primitive/Extrinsic/v2/ExtrinsicExtra.ts | 5 +- .../Extrinsic/v2/ExtrinsicPayload.ts | 7 ++- .../Extrinsic/v2/ExtrinsicSignature.ts | 6 +-- .../Extrinsic/v3/ExtrinsicPayload.ts | 9 ++-- .../Extrinsic/v3/ExtrinsicSignature.ts | 6 +-- packages/types/src/primitive/Generic/Block.ts | 6 +-- .../types/src/primitive/Generic/Digest.ts | 15 +++--- packages/types/src/primitive/Generic/Event.ts | 4 +- packages/types/src/types.ts | 3 ++ 28 files changed, 228 insertions(+), 132 deletions(-) create mode 100644 packages/types/src/codec/utils/mapToTypeMap.ts diff --git a/packages/types/src/codec/Compact.spec.ts b/packages/types/src/codec/Compact.spec.ts index 9859df2ad5..24937534b6 100644 --- a/packages/types/src/codec/Compact.spec.ts +++ b/packages/types/src/codec/Compact.spec.ts @@ -113,7 +113,13 @@ describe('Compact', (): void => { ).toEqual(64); }); - it('has the correct encodedLength for constructor values (BlockNumber)', (): void => { + it('has the correct encodedLength for constructor values (string BlockNumber)', (): void => { + expect( + new Compact('BlockNumber', 0xfffffff9).encodedLength + ).toEqual(5); + }); + + it('has the correct encodedLength for constructor values (class BlockNumber)', (): void => { expect( new Compact(ClassOf('BlockNumber'), 0xfffffff9).encodedLength ).toEqual(5); diff --git a/packages/types/src/codec/Compact.ts b/packages/types/src/codec/Compact.ts index 6a58aa67fa..a40209022e 100644 --- a/packages/types/src/codec/Compact.ts +++ b/packages/types/src/codec/Compact.ts @@ -2,12 +2,13 @@ // This software may be modified and distributed under the terms // of the Apache-2.0 license. See the LICENSE file for details. -import { AnyNumber, Constructor } from '../types'; +import { AnyNumber, Constructor, InterfaceTypes } from '../types'; import BN from 'bn.js'; import { bnToBn, compactAddLength, compactFromU8a, compactStripLength, compactToU8a, hexToBn, isBn, isHex, isNumber, isString } from '@polkadot/util'; import { DEFAULT_BITLENGTH } from '@polkadot/util/compact/defaults'; +import { typeToConstructor } from './utils'; import { UIntBitLength } from './AbstractInt'; import CodecDate from './Date'; import UInt from './UInt'; @@ -26,11 +27,11 @@ export type CompactEncodable = UInt | CodecDate; // FIXME is there a way to do i * a number and making the compact representation thereof */ export default class Compact extends Base { - public constructor (Type: Constructor, value: Compact | AnyNumber = 0) { - super(Compact.decodeCompact(Type, value)); + public constructor (Type: Constructor | InterfaceTypes, value: Compact | AnyNumber = 0) { + super(Compact.decodeCompact(typeToConstructor(Type), value)); } - public static with (Type: Constructor): Constructor> { + public static with (Type: Constructor | InterfaceTypes): Constructor> { return class extends Compact { public constructor (value?: any) { super(Type, value); diff --git a/packages/types/src/codec/EnumType.spec.ts b/packages/types/src/codec/EnumType.spec.ts index 8700da68a6..559fcfa600 100644 --- a/packages/types/src/codec/EnumType.spec.ts +++ b/packages/types/src/codec/EnumType.spec.ts @@ -47,6 +47,15 @@ describe('Enum', (): void => { ).toEqual('4660'); // 0x1234 in decimal }); + it('decodes from hex (string types)', (): void => { + expect( + new Enum( + { foo: 'Text', bar: 'u32' }, + '0x0134120000' + ).value.toString() + ).toEqual('4660'); // 0x1234 in decimal + }); + it('decodes from a JSON input (mixed case)', (): void => { expect( new Enum( diff --git a/packages/types/src/codec/EnumType.ts b/packages/types/src/codec/EnumType.ts index 8b9ebee434..68ab66da33 100644 --- a/packages/types/src/codec/EnumType.ts +++ b/packages/types/src/codec/EnumType.ts @@ -2,11 +2,12 @@ // This software may be modified and distributed under the terms // of the Apache-2.0 license. See the LICENSE file for details. -import { AnyJson, Codec, Constructor } from '../types'; +import { AnyJson, Codec, Constructor, InterfaceTypes } from '../types'; import { assert, hexToU8a, isHex, isNumber, isObject, isString, isU8a, isUndefined, stringCamelCase, stringUpperFirst, u8aConcat, u8aToHex } from '@polkadot/util'; import Null from '../primitive/Null'; +import { mapToTypeMap } from './utils'; import Base from './Base'; interface EnumConstructor { @@ -38,7 +39,7 @@ export default class Enum extends Base { private _isBasic: boolean; - public constructor (def: TypesDef | string[], value?: any, index?: number | Enum) { + public constructor (def: Record | string[], value?: any, index?: number | Enum) { const defInfo = Enum.extractDef(def); const decoded = Enum.decodeEnum(defInfo.def, value, index); @@ -50,10 +51,10 @@ export default class Enum extends Base { this._index = this._indexes.indexOf(decoded.index) || 0; } - private static extractDef (def: TypesDef | string[]): { def: TypesDef; isBasic: boolean } { + private static extractDef (def: Record | string[]): { def: TypesDef; isBasic: boolean } { if (!Array.isArray(def)) { return { - def, + def: mapToTypeMap(def), isBasic: false }; } @@ -126,7 +127,7 @@ export default class Enum extends Base { }; } - public static with (Types: TypesDef | string[]): EnumConstructor { + public static with (Types: Record | string[]): EnumConstructor { return class extends Enum { public constructor (value?: any, index?: number) { super(Types, value, index); diff --git a/packages/types/src/codec/Linkage.ts b/packages/types/src/codec/Linkage.ts index adcfcd98c9..4ef4f35547 100644 --- a/packages/types/src/codec/Linkage.ts +++ b/packages/types/src/codec/Linkage.ts @@ -2,22 +2,30 @@ // This software may be modified and distributed under the terms // of the Apache-2.0 license. See the LICENSE file for details. -import { Struct, Option, Tuple, Vec } from '.'; -import { Constructor, Codec } from '../types'; +import { Constructor, Codec, InterfaceTypes } from '../types'; + +import Option from './Option'; +import Struct from './Struct'; +import Tuple from './Tuple'; +import Vec from './Vec'; type TypeWithValues = [Constructor, any[]]; const EMPTY = new Uint8Array(); +/** + * @name Linkage + * @description The wrapper for the result from a LinkedMap + */ export default class Linkage extends Struct { - public constructor (Type: Constructor, value?: any) { + public constructor (Type: Constructor | InterfaceTypes, value?: any) { super({ previous: Option.with(Type), next: Option.with(Type) }, value); } - public static withKey (Type: Constructor): Constructor> { + public static withKey (Type: Constructor | InterfaceTypes): Constructor> { return class extends Linkage { public constructor (value?: any) { super(Type, value); @@ -50,6 +58,10 @@ export default class Linkage extends Struct { } } +/** + * @name LinkageResult + * @description A Linkage keys/Values tuple + */ export class LinkageResult extends Tuple { public constructor ([TypeKey, keys]: TypeWithValues, [TypeValue, values]: TypeWithValues) { super({ diff --git a/packages/types/src/codec/Option.spec.ts b/packages/types/src/codec/Option.spec.ts index dbc09935a1..4734251a0e 100644 --- a/packages/types/src/codec/Option.spec.ts +++ b/packages/types/src/codec/Option.spec.ts @@ -37,6 +37,12 @@ describe('Option', (): void => { ).toEqual('hello'); }); + it('converts an option to an option (strings)', (): void => { + expect( + new Option('Text', new Option('Text', 'hello')).toString() + ).toEqual('hello'); + }); + it('converts correctly from hex with toHex (Bytes)', (): void => { // Option for a parachain head, however, this is effectively an // Option> (hence the length, since it is from storage) diff --git a/packages/types/src/codec/Option.ts b/packages/types/src/codec/Option.ts index 9a46189860..f10328f9f7 100644 --- a/packages/types/src/codec/Option.ts +++ b/packages/types/src/codec/Option.ts @@ -2,11 +2,13 @@ // This software may be modified and distributed under the terms // of the Apache-2.0 license. See the LICENSE file for details. +import { Codec, Constructor, InterfaceTypes } from '../types'; + import { isNull, isU8a, isUndefined, u8aToHex } from '@polkadot/util'; -import Base from './Base'; -import { Codec, Constructor } from '../types'; import Null from '../primitive/Null'; +import { typeToConstructor } from './utils'; +import Base from './Base'; /** * @name Option @@ -19,12 +21,12 @@ import Null from '../primitive/Null'; export default class Option extends Base { private _Type: Constructor; - public constructor (Type: Constructor, value?: any) { - super( - Option.decodeOption(Type, value) - ); + public constructor (Type: Constructor | InterfaceTypes, value?: any) { + const Clazz = typeToConstructor(Type); - this._Type = Type; + super(Option.decodeOption(Clazz, value)); + + this._Type = Clazz; } public static decodeOption (Type: Constructor, value?: any): Codec { @@ -46,7 +48,7 @@ export default class Option extends Base { return new Type(value); } - public static with (Type: Constructor): Constructor> { + public static with (Type: Constructor | InterfaceTypes): Constructor> { return class extends Option { public constructor (value?: any) { super(Type, value); diff --git a/packages/types/src/codec/Struct.spec.ts b/packages/types/src/codec/Struct.spec.ts index 2571a8762c..5139447f66 100644 --- a/packages/types/src/codec/Struct.spec.ts +++ b/packages/types/src/codec/Struct.spec.ts @@ -105,6 +105,18 @@ describe('Struct', (): void => { ).toEqual('{"txt":"foo","u32":1193046}'); }); + it('provides a clean toString() (string types)', (): void => { + expect( + new ( + Struct.with({ + txt: 'Text', + num: 'u32', + cls: U32 + }) + )({ txt: 'foo', num: 0x123456, cls: 123 }).toString() + ).toEqual('{"txt":"foo","num":1193046,"cls":123}'); + }); + it('exposes the properties on the object', (): void => { const struct = new ( Struct.with({ diff --git a/packages/types/src/codec/Struct.ts b/packages/types/src/codec/Struct.ts index 4831f8b3d9..8dce4cea18 100644 --- a/packages/types/src/codec/Struct.ts +++ b/packages/types/src/codec/Struct.ts @@ -2,13 +2,15 @@ // This software may be modified and distributed under the terms // of the Apache-2.0 license. See the LICENSE file for details. -import { AnyJsonObject, Codec, Constructor, ConstructorDef, IHash } from '../types'; +import { AnyJsonObject, Codec, Constructor, ConstructorDef, IHash, InterfaceTypes } from '../types'; import { hexToU8a, isHex, isObject, isU8a, isUndefined, u8aConcat, u8aToHex } from '@polkadot/util'; import { blake2AsU8a } from '@polkadot/util-crypto'; import U8a from './U8a'; -import { compareMap, decodeU8a } from './utils'; +import { compareMap, decodeU8a, mapToTypeMap } from './utils'; + +type TypesDef = Record>; /** * @name Struct @@ -22,7 +24,7 @@ import { compareMap, decodeU8a } from './utils'; */ export default class Struct< // The actual Class structure, i.e. key -> Class - S extends ConstructorDef = ConstructorDef, + S extends TypesDef = TypesDef, // internal type, instance of classes mapped by key T extends { [K in keyof S]: Codec } = { [K in keyof S]: Codec }, // input values, mapped by key can be anything (construction) @@ -31,17 +33,16 @@ export default class Struct< E extends { [K in keyof S]: string } = { [K in keyof S]: string }> extends Map implements Codec { protected _jsonMap: Map; - protected _Types: S; + protected _Types: ConstructorDef; public constructor (Types: S, value: V | Map | any[] | string = {} as unknown as V, jsonMap: Map = new Map()) { - const decoded = Struct.decodeStruct(Types, value, jsonMap); + const Clazzes = mapToTypeMap(Types); + const decoded = Struct.decodeStruct(Clazzes, value, jsonMap); - super( - Object.entries(decoded) - ); + super(Object.entries(decoded as any)); this._jsonMap = jsonMap; - this._Types = Types; + this._Types = Clazzes; } /** @@ -59,11 +60,7 @@ export default class Struct< * `Object.keys(Types)` * @param jsonMap */ - private static decodeStruct< - S extends ConstructorDef, - _, - T extends { [K in keyof S]: Codec } - > (Types: S, value: any, jsonMap: Map): T { + private static decodeStruct (Types: ConstructorDef, value: any, jsonMap: Map): T { // l.debug(() => ['Struct.decode', { Types, value }]); if (isHex(value)) { @@ -72,7 +69,7 @@ export default class Struct< const values = decodeU8a(value, Object.values(Types)); // Transform array of values to {key: value} mapping - return Object.keys(Types).reduce((raw: T, key: keyof S, index): T => { + return Object.keys(Types).reduce((raw, key, index): T => { // TS2322: Type 'Codec' is not assignable to type 'T[keyof S]'. (raw as any)[key] = values[index]; @@ -86,30 +83,26 @@ export default class Struct< return Struct.decodeStructFromObject(Types, value, jsonMap); } - private static decodeStructFromObject< - S extends ConstructorDef, - _, - T extends { [K in keyof S]: Codec } - > (Types: S, value: any, jsonMap: Map): T { - return Object.keys(Types).reduce((raw: T, key: keyof S, index): T => { + private static decodeStructFromObject (Types: ConstructorDef, value: any, jsonMap: Map): T { + return Object.keys(Types).reduce((raw, key, index): T => { // The key in the JSON can be snake_case (or other cases), but in our // Types, result or any other maps, it's camelCase const jsonKey = (jsonMap.get(key as any) && !value[key]) ? jsonMap.get(key as any) : key; try { if (Array.isArray(value)) { - raw[key] = value[index] instanceof Types[key] + // TS2322: Type 'Codec' is not assignable to type 'T[keyof S]'. + (raw as any)[key] = value[index] instanceof Types[key] ? value[index] : new Types[key](value[index]); } else if (value instanceof Map) { const mapped = value.get(jsonKey); - // TS2322: Type 'Codec' is not assignable to type 'T[keyof S]'. (raw as any)[key] = mapped instanceof Types[key] ? mapped : new Types[key](mapped); } else if (isObject(value)) { - raw[key] = value[jsonKey as string] instanceof Types[key] + (raw as any)[key] = value[jsonKey as string] instanceof Types[key] ? value[jsonKey as string] : new Types[key](value[jsonKey as string]); } else { @@ -123,9 +116,7 @@ export default class Struct< }, {} as unknown as T); } - public static with< - S extends ConstructorDef - > (Types: S): Constructor> { + public static with (Types: S): Constructor> { return class extends Struct { public constructor (value?: any, jsonMap?: Map) { super(Types, value, jsonMap); diff --git a/packages/types/src/codec/Tuple.spec.ts b/packages/types/src/codec/Tuple.spec.ts index fba4e92c6f..7b82517d7f 100644 --- a/packages/types/src/codec/Tuple.spec.ts +++ b/packages/types/src/codec/Tuple.spec.ts @@ -55,6 +55,15 @@ describe('Tuple', (): void => { testEncode('toString', '["bazzing",69]'); }); + it('creates from string types', (): void => { + expect( + new Tuple( + ['Text', 'u32', U32], + ['foo', 69, 42] + ).toString() + ).toEqual('["foo",69,42]'); + }); + it.skip('creates properly via actual hex string', (): void => { Call.injectMethods(extrinsics); diff --git a/packages/types/src/codec/Tuple.ts b/packages/types/src/codec/Tuple.ts index 12f9963d92..e0fe45ca5d 100644 --- a/packages/types/src/codec/Tuple.ts +++ b/packages/types/src/codec/Tuple.ts @@ -2,17 +2,21 @@ // This software may be modified and distributed under the terms // of the Apache-2.0 license. See the LICENSE file for details. -import { AnyNumber, AnyU8a, AnyString, Codec, Constructor } from '../types'; +import { AnyNumber, AnyU8a, AnyString, Codec, Constructor, InterfaceTypes } from '../types'; import { isU8a, u8aConcat, isHex, hexToU8a } from '@polkadot/util'; -import { decodeU8a } from './utils'; +import { decodeU8a, mapToTypeMap, typeToConstructor } from './utils'; import AbstractArray from './AbstractArray'; type TupleConstructors = Constructor[] | { [index: string]: Constructor; }; +type TupleTypes = (Constructor | InterfaceTypes)[] | { + [index: string]: Constructor | InterfaceTypes; +}; + /** * @name Tuple * @description @@ -22,12 +26,14 @@ type TupleConstructors = Constructor[] | { export default class Tuple extends AbstractArray { private _Types: TupleConstructors; - public constructor (Types: TupleConstructors, value?: any) { - super( - ...Tuple.decodeTuple(Types, value) - ); + public constructor (Types: TupleTypes, value?: any) { + const Clazzes = Array.isArray(Types) + ? Types.map((type): Constructor => typeToConstructor(type)) + : mapToTypeMap(Types); - this._Types = Types; + super(...Tuple.decodeTuple(Clazzes, value)); + + this._Types = Clazzes; } private static decodeTuple (_Types: TupleConstructors, value: AnyU8a | string | (AnyU8a | AnyNumber | AnyString | undefined | null)[]): Codec[] { @@ -50,7 +56,7 @@ export default class Tuple extends AbstractArray { }); } - public static with (Types: TupleConstructors): Constructor { + public static with (Types: TupleTypes): Constructor { return class extends Tuple { public constructor (value?: any) { super(Types, value); diff --git a/packages/types/src/codec/Vec.spec.ts b/packages/types/src/codec/Vec.spec.ts index b1e6e2255a..3a45430182 100644 --- a/packages/types/src/codec/Vec.spec.ts +++ b/packages/types/src/codec/Vec.spec.ts @@ -48,6 +48,12 @@ describe('Vec', (): void => { ).toEqual(['6', '7']); }); + it('allows contruction via JSON (string type)', (): void => { + expect( + new Vec('u32', ['6', '7']).toJSON() + ).toEqual([6, 7]); + }); + it('exposes the type', (): void => { expect(vector.Type).toEqual('Text'); }); diff --git a/packages/types/src/codec/Vec.ts b/packages/types/src/codec/Vec.ts index 039030bdc3..83fdcc6da7 100644 --- a/packages/types/src/codec/Vec.ts +++ b/packages/types/src/codec/Vec.ts @@ -2,11 +2,12 @@ // This software may be modified and distributed under the terms // of the Apache-2.0 license. See the LICENSE file for details. +import { Codec, Constructor, InterfaceTypes } from '../types'; + import { u8aToU8a, assert } from '@polkadot/util'; import Compact from './Compact'; -import { Codec, Constructor } from '../types'; -import { decodeU8a } from './utils'; +import { decodeU8a, typeToConstructor } from './utils'; import AbstractArray from './AbstractArray'; const MAX_LENGTH = 32768; @@ -21,12 +22,12 @@ const MAX_LENGTH = 32768; export default class Vec extends AbstractArray { private _Type: Constructor; - public constructor (Type: Constructor, value: Vec | Uint8Array | string | any[] = [] as any[]) { - super( - ...Vec.decodeVec(Type, value) - ); + public constructor (Type: Constructor | InterfaceTypes, value: Vec | Uint8Array | string | any[] = [] as any[]) { + const Clazz = typeToConstructor(Type); - this._Type = Type; + super(...Vec.decodeVec(Clazz, value)); + + this._Type = Clazz; } public static decodeVec (Type: Constructor, value: Vec | Uint8Array | string | any[]): T[] { @@ -52,13 +53,15 @@ export default class Vec extends AbstractArray { return decodeU8a(u8a.subarray(offset), new Array(length.toNumber()).fill(Type)) as T[]; } - public static with (Type: Constructor): Constructor> { + public static with (Type: Constructor | InterfaceTypes): Constructor> { return class extends Vec { public constructor (value?: any[]) { super(Type, value); } + // @ts-ignore public static Fallback = Type.Fallback + // @ts-ignore ? Vec.with(Type.Fallback) : undefined; }; diff --git a/packages/types/src/codec/VecFixed.ts b/packages/types/src/codec/VecFixed.ts index 34336747bc..ec33ac7983 100644 --- a/packages/types/src/codec/VecFixed.ts +++ b/packages/types/src/codec/VecFixed.ts @@ -2,11 +2,11 @@ // This software may be modified and distributed under the terms // of the Apache-2.0 license. See the LICENSE file for details. -import { Codec, Constructor } from '../types'; +import { Codec, Constructor, InterfaceTypes } from '../types'; import { assert, isU8a, u8aConcat, compactToU8a } from '@polkadot/util'; - import AbstractArray from './AbstractArray'; +import { typeToConstructor } from './utils'; import Vec from './Vec'; /** @@ -17,12 +17,12 @@ import Vec from './Vec'; export default class VecFixed extends AbstractArray { private _Type: Constructor; - public constructor (Type: Constructor, length: number, value: VecFixed | Uint8Array | string | any[] = [] as any[]) { - super( - ...VecFixed.decodeVecFixed(Type, length, value) - ); + public constructor (Type: Constructor | InterfaceTypes, length: number, value: VecFixed | Uint8Array | string | any[] = [] as any[]) { + const Clazz = typeToConstructor(Type); - this._Type = Type; + super(...VecFixed.decodeVecFixed(Clazz, length, value)); + + this._Type = Clazz; } public static decodeVecFixed (Type: Constructor, allocLength: number, value: VecFixed | Uint8Array | string | any[]): T[] { diff --git a/packages/types/src/codec/createType.ts b/packages/types/src/codec/createType.ts index 499715415b..6722daf23b 100644 --- a/packages/types/src/codec/createType.ts +++ b/packages/types/src/codec/createType.ts @@ -2,15 +2,13 @@ // This software may be modified and distributed under the terms // of the Apache-2.0 license. See the LICENSE file for details. +import { Codec, Constructor, InterfaceTypes } from '../types'; import { TypeDef, TypeDefInfo, TypeDefExtVecFixed } from './types'; import memoizee from 'memoizee'; import { assert } from '@polkadot/util'; import { InterfaceRegistry } from '../interfaceRegistry'; -import { Codec, Constructor } from '../types'; -import Bytes from '../primitive/Bytes'; -import Null from '../primitive/Null'; import Compact from './Compact'; import Enum from './Enum'; import Linkage from './Linkage'; @@ -19,13 +17,12 @@ import CodecSet from './Set'; import Struct from './Struct'; import Tuple from './Tuple'; import U8aFixed, { BitLength as U8aFixedBitLength } from './U8aFixed'; -import UInt from './UInt'; import Vec from './Vec'; import VecFixed from './VecFixed'; import getTypeRegistry from './typeRegistry'; // Type which says: if `K` is in the InterfaceRegistry, then return InterfaceRegistry[K], else fallback to T -type FromReg = K extends keyof InterfaceRegistry ? InterfaceRegistry[K] : T +type FromReg = K extends InterfaceTypes ? InterfaceRegistry[K] : T // safely split a string on ', ' while taking care of any nested occurences export function typeSplit (type: string): string[] { @@ -198,18 +195,24 @@ export function ClassOfUnsafe (name: K): Constructor { +export function ClassOf (name: K): Constructor { return ClassOfUnsafe(name) as Constructor; } -// create an array of constructors from the input -export function getTypeClassMap (defs: TypeDef[]): { [index: string]: Constructor } { - return defs.reduce((result, sub): Record => { - // eslint-disable-next-line @typescript-eslint/no-use-before-define - result[sub.name as string] = getTypeClass(sub); +// create a maps of type string constructors from the input +export function getTypeClassMap (defs: TypeDef[]): Record { + return defs.reduce((result, sub): Record => { + result[sub.name as string] = sub.type as any; return result; - }, {} as unknown as Record); + }, {} as unknown as Record); +} + +// create an array of type string constructors from the input +export function getTypeClassArray (defs: TypeDef[]): (InterfaceTypes)[] { + return defs.map(({ type }): InterfaceTypes => + type as any + ); } // Returns the type Class for construction @@ -224,19 +227,19 @@ export function getTypeClass (value: TypeDef, Fallback? case TypeDefInfo.Compact: assert(value.sub && !Array.isArray(value.sub), 'Expected subtype for Compact'); return Compact.with( - getTypeClass(value.sub as TypeDef) + (value.sub as TypeDef).type as any ) as unknown as Constructor; case TypeDefInfo.Enum: assert(value.sub && Array.isArray(value.sub), 'Expected subtype for Enum'); return Enum.with( - getTypeClassMap(value.sub as TypeDef[]) + getTypeClassMap(value.sub as TypeDef[]) as any ) as unknown as Constructor; case TypeDefInfo.Option: assert(value.sub && !Array.isArray(value.sub), 'Expected subtype for Option'); return Option.with( - getTypeClass(value.sub as TypeDef) + (value.sub as TypeDef).type as any ) as unknown as Constructor; case TypeDefInfo.Set: @@ -258,7 +261,7 @@ export function getTypeClass (value: TypeDef, Fallback? case TypeDefInfo.Tuple: assert(Array.isArray(value.sub), 'Expected nested subtypes for Tuple'); return Tuple.with( - (value.sub as TypeDef[]).map((Type): Constructor => getTypeClass(Type)) + getTypeClassArray(value.sub as TypeDef[]) ) as unknown as Constructor; case TypeDefInfo.Vec: @@ -268,8 +271,8 @@ export function getTypeClass (value: TypeDef, Fallback? return ( vsub.type === 'u8' - ? Bytes - : Vec.with(getTypeClass(vsub)) + ? ClassOf('Bytes') + : Vec.with(vsub.type as any) ) as unknown as Constructor; case TypeDefInfo.VecFixed: @@ -280,13 +283,13 @@ export function getTypeClass (value: TypeDef, Fallback? return ( ext.type === 'u8' ? U8aFixed.with((ext.length * 8) as U8aFixedBitLength) - : VecFixed.with(createClass(ext.type), ext.length) + : VecFixed.with(ext.type as any, ext.length) ) as unknown as Constructor; case TypeDefInfo.Linkage: assert(value.sub && !Array.isArray(value.sub), 'Expected subtype for Linkage'); return Linkage.withKey( - getTypeClass(value.sub as TypeDef) + (value.sub as TypeDef).type as any ) as unknown as Constructor; case TypeDefInfo.DoubleMap: @@ -294,7 +297,7 @@ export function getTypeClass (value: TypeDef, Fallback? return getTypeClass(value.sub as TypeDef); case TypeDefInfo.Null: - return Null as unknown as Constructor; + return ClassOf('Null') as unknown as Constructor; } if (Fallback) { @@ -367,7 +370,7 @@ export function createTypeUnsafe ( +export default function createType ( type: K, ...params: any[] ): InterfaceRegistry[K] { diff --git a/packages/types/src/codec/utils/index.ts b/packages/types/src/codec/utils/index.ts index 94fe457179..afdbb4fa77 100644 --- a/packages/types/src/codec/utils/index.ts +++ b/packages/types/src/codec/utils/index.ts @@ -5,3 +5,4 @@ export { default as compareArray } from './compareArray'; export { default as compareMap } from './compareMap'; export { default as decodeU8a } from './decodeU8a'; +export * from './mapToTypeMap'; diff --git a/packages/types/src/codec/utils/mapToTypeMap.ts b/packages/types/src/codec/utils/mapToTypeMap.ts new file mode 100644 index 0000000000..3b6597dd1d --- /dev/null +++ b/packages/types/src/codec/utils/mapToTypeMap.ts @@ -0,0 +1,30 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { Codec, Constructor, InterfaceTypes } from '../../types'; + +import { isString } from '@polkadot/util'; + +import { ClassOf } from '../createType'; + +export function typeToConstructor (type: InterfaceTypes | Constructor): Constructor { + return ( + isString(type) + ? ClassOf(type as InterfaceTypes) + : type + ) as Constructor; +} + +/** + * @description takes an input map of the form `{ [string]: string | Constructor }` and returns a map of `{ [string]: Conbstructor }` + */ +export function mapToTypeMap (input: Record): Record { + const output: Record = {}; + + Object.entries(input).forEach(([key, type]): void => { + output[key] = typeToConstructor(type); + }); + + return output; +} diff --git a/packages/types/src/primitive/Extrinsic/v1/ExtrinsicPayload.ts b/packages/types/src/primitive/Extrinsic/v1/ExtrinsicPayload.ts index 9ab7778820..b611acab63 100644 --- a/packages/types/src/primitive/Extrinsic/v1/ExtrinsicPayload.ts +++ b/packages/types/src/primitive/Extrinsic/v1/ExtrinsicPayload.ts @@ -5,7 +5,6 @@ import { Hash, Index } from '../../../interfaces/runtime'; import { ExtrinsicPayloadValue, IKeyringPair } from '../../../types'; -import { ClassOf } from '../../../codec/createType'; import Compact from '../../../codec/Compact'; import Struct from '../../../codec/Struct'; import U8a from '../../../codec/U8a'; @@ -26,10 +25,10 @@ import { sign } from '../util'; export default class ExtrinsicPayloadV1 extends Struct { public constructor (value?: ExtrinsicPayloadValue | Uint8Array | string) { super({ - nonce: ClassOf('Compact'), + nonce: 'Compact', method: U8a, era: ExtrinsicEra, - blockHash: ClassOf('Hash') + blockHash: 'Hash' }, value); } diff --git a/packages/types/src/primitive/Extrinsic/v1/ExtrinsicSignature.ts b/packages/types/src/primitive/Extrinsic/v1/ExtrinsicSignature.ts index 9fd903fbbb..6a7f0781ab 100644 --- a/packages/types/src/primitive/Extrinsic/v1/ExtrinsicSignature.ts +++ b/packages/types/src/primitive/Extrinsic/v1/ExtrinsicSignature.ts @@ -6,7 +6,7 @@ import { Address, Balance, Index, Signature } from '../../../interfaces/runtime' import { ExtrinsicPayloadValue, IExtrinsicSignature, IKeyringPair, SignatureOptions } from '../../../types'; import { ExtrinsicSignatureOptions } from '../types'; -import createType, { ClassOf } from '../../../codec/createType'; +import createType from '../../../codec/createType'; import Compact from '../../../codec/Compact'; import Struct from '../../../codec/Struct'; import Call from '../../Generic/Call'; @@ -27,9 +27,9 @@ export default class ExtrinsicSignatureV1 extends Struct implements IExtrinsicSi // 1/2 bytes: The Transaction Era public constructor (value?: ExtrinsicSignatureV1 | Uint8Array, { isSigned }: ExtrinsicSignatureOptions = {}) { super({ - signer: ClassOf('Address'), - signature: ClassOf('Signature'), - nonce: ClassOf('Compact'), + signer: 'Address', + signature: 'Signature', + nonce: 'Compact', era: ExtrinsicEra }, ExtrinsicSignatureV1.decodeExtrinsicSignature(value, isSigned)); } diff --git a/packages/types/src/primitive/Extrinsic/v2/ExtrinsicExtra.ts b/packages/types/src/primitive/Extrinsic/v2/ExtrinsicExtra.ts index 117db22889..241b9ab372 100644 --- a/packages/types/src/primitive/Extrinsic/v2/ExtrinsicExtra.ts +++ b/packages/types/src/primitive/Extrinsic/v2/ExtrinsicExtra.ts @@ -5,7 +5,6 @@ import { Balance, Index } from '../../../interfaces/runtime'; import { ExtrinsicExtraValue } from '../types'; -import { ClassOf } from '../../../codec/createType'; import Compact from '../../../codec/Compact'; import Struct from '../../../codec/Struct'; import Address from '../../Generic/Address'; @@ -20,8 +19,8 @@ export default class ExtrinsicExtraV2 extends Struct { public constructor (value?: ExtrinsicExtraValue | Uint8Array) { super({ era: ExtrinsicEra, - nonce: ClassOf('Compact'), - tip: ClassOf('Compact') + nonce: 'Compact', + tip: 'Compact' }, value); } diff --git a/packages/types/src/primitive/Extrinsic/v2/ExtrinsicPayload.ts b/packages/types/src/primitive/Extrinsic/v2/ExtrinsicPayload.ts index c8b88e5d53..23e351b312 100644 --- a/packages/types/src/primitive/Extrinsic/v2/ExtrinsicPayload.ts +++ b/packages/types/src/primitive/Extrinsic/v2/ExtrinsicPayload.ts @@ -5,7 +5,6 @@ import { Balance, Hash, Index } from '../../../interfaces/runtime'; import { ExtrinsicPayloadValue, IKeyringPair } from '../../../types'; -import { ClassOf } from '../../../codec/createType'; import Compact from '../../../codec/Compact'; import Struct from '../../../codec/Struct'; import U8a from '../../../codec/U8a'; @@ -23,9 +22,9 @@ export default class ExtrinsicPayloadV2 extends Struct { super({ method: U8a, era: ExtrinsicEra, - nonce: ClassOf('Compact'), - tip: ClassOf('Compact'), - blockHash: ClassOf('Hash') + nonce: 'Compact', + tip: 'Compact', + blockHash: 'Hash' }, value); } diff --git a/packages/types/src/primitive/Extrinsic/v2/ExtrinsicSignature.ts b/packages/types/src/primitive/Extrinsic/v2/ExtrinsicSignature.ts index 27614c0c6e..c8bab38df5 100644 --- a/packages/types/src/primitive/Extrinsic/v2/ExtrinsicSignature.ts +++ b/packages/types/src/primitive/Extrinsic/v2/ExtrinsicSignature.ts @@ -6,7 +6,7 @@ import { Address, Balance, Index, Signature } from '../../../interfaces/runtime' import { ExtrinsicPayloadValue, IExtrinsicSignature, IKeyringPair, SignatureOptions } from '../../../types'; import { ExtrinsicSignatureOptions } from '../types'; -import createType, { ClassOf } from '../../../codec/createType'; +import createType from '../../../codec/createType'; import Compact from '../../../codec/Compact'; import Struct from '../../../codec/Struct'; import Call from '../../Generic/Call'; @@ -23,8 +23,8 @@ import ExtrinsicExtra from '../v2/ExtrinsicExtra'; export default class ExtrinsicSignatureV2 extends Struct implements IExtrinsicSignature { public constructor (value: ExtrinsicSignatureV2 | Uint8Array | undefined, { isSigned }: ExtrinsicSignatureOptions = {}) { super({ - signer: ClassOf('Address'), - signature: ClassOf('Signature'), + signer: 'Address', + signature: 'Signature', extra: ExtrinsicExtra }, ExtrinsicSignatureV2.decodeExtrinsicSignature(value, isSigned)); } diff --git a/packages/types/src/primitive/Extrinsic/v3/ExtrinsicPayload.ts b/packages/types/src/primitive/Extrinsic/v3/ExtrinsicPayload.ts index 788d8fe442..44a5d1f25d 100644 --- a/packages/types/src/primitive/Extrinsic/v3/ExtrinsicPayload.ts +++ b/packages/types/src/primitive/Extrinsic/v3/ExtrinsicPayload.ts @@ -5,7 +5,6 @@ import { Balance, Hash, Index } from '../../../interfaces/runtime'; import { ExtrinsicPayloadValue, IKeyringPair } from '../../../types'; -import { ClassOf } from '../../../codec/createType'; import Compact from '../../../codec/Compact'; import Struct from '../../../codec/Struct'; import U8a from '../../../codec/U8a'; @@ -23,10 +22,10 @@ export default class ExtrinsicPayloadV3 extends Struct { super({ method: U8a, era: ExtrinsicEra, - nonce: ClassOf('Compact'), - tip: ClassOf('Compact'), - genesisHash: ClassOf('Hash'), - blockHash: ClassOf('Hash') + nonce: 'Compact', + tip: 'Compact', + genesisHash: 'Hash', + blockHash: 'Hash' }, value); } diff --git a/packages/types/src/primitive/Extrinsic/v3/ExtrinsicSignature.ts b/packages/types/src/primitive/Extrinsic/v3/ExtrinsicSignature.ts index e446de6470..33126dc927 100644 --- a/packages/types/src/primitive/Extrinsic/v3/ExtrinsicSignature.ts +++ b/packages/types/src/primitive/Extrinsic/v3/ExtrinsicSignature.ts @@ -6,7 +6,7 @@ import { Address, Balance, Index, Signature } from '../../../interfaces/runtime' import { ExtrinsicPayloadValue, IExtrinsicSignature, IKeyringPair, SignatureOptions } from '../../../types'; import { ExtrinsicSignatureOptions } from '../types'; -import createType, { ClassOf } from '../../../codec/createType'; +import createType from '../../../codec/createType'; import Compact from '../../../codec/Compact'; import Struct from '../../../codec/Struct'; import Call from '../../Generic/Call'; @@ -23,8 +23,8 @@ import ExtrinsicExtra from '../v2/ExtrinsicExtra'; export default class ExtrinsicSignatureV3 extends Struct implements IExtrinsicSignature { public constructor (value: ExtrinsicSignatureV3 | Uint8Array | undefined, { isSigned }: ExtrinsicSignatureOptions = {}) { super({ - signer: ClassOf('Address'), - signature: ClassOf('Signature'), + signer: 'Address', + signature: 'Signature', extra: ExtrinsicExtra }, ExtrinsicSignatureV3.decodeExtrinsicSignature(value, isSigned)); } diff --git a/packages/types/src/primitive/Generic/Block.ts b/packages/types/src/primitive/Generic/Block.ts index 840d1b84e5..6c1849d255 100644 --- a/packages/types/src/primitive/Generic/Block.ts +++ b/packages/types/src/primitive/Generic/Block.ts @@ -7,7 +7,7 @@ import { AnyNumber, AnyU8a } from '../../types'; import { blake2AsU8a } from '@polkadot/util-crypto'; -import createType, { ClassOf } from '../../codec/createType'; +import createType from '../../codec/createType'; import Extrinsic from '../Extrinsic/Extrinsic'; import Struct from '../../codec/Struct'; import Vec from '../../codec/Vec'; @@ -33,8 +33,8 @@ export interface BlockValue { export default class Block extends Struct { public constructor (value?: BlockValue | Uint8Array) { super({ - header: ClassOf('Header'), - extrinsics: ClassOf('Vec') + header: 'Header', + extrinsics: 'Vec' }, value); } diff --git a/packages/types/src/primitive/Generic/Digest.ts b/packages/types/src/primitive/Generic/Digest.ts index 80621a0dd0..94374b2e35 100644 --- a/packages/types/src/primitive/Generic/Digest.ts +++ b/packages/types/src/primitive/Generic/Digest.ts @@ -7,7 +7,6 @@ import { Consensus, Hash, PreRuntime, Seal, SealV0 } from '../../interfaces/runt import { assert } from '@polkadot/util'; -import { ClassOf } from '../../codec/createType'; import Enum from '../../codec/Enum'; import Struct from '../../codec/Struct'; import Vec from '../../codec/Vec'; @@ -21,13 +20,13 @@ import Bytes from '../Bytes'; export class DigestItem extends Enum { public constructor (value: any) { super({ - Other: ClassOf('Bytes'), // 0 - AuthoritiesChange: ClassOf('Vec'), // 1 - ChangesTrieRoot: ClassOf('Hash'), // 2 - SealV0: ClassOf('SealV0'), // 3 - Consensus: ClassOf('Consensus'), // 4 - Seal: ClassOf('Seal'), // 5 - PreRuntime: ClassOf('PreRuntime') // 6 + Other: 'Bytes', // 0 + AuthoritiesChange: 'Vec', // 1 + ChangesTrieRoot: 'Hash', // 2 + SealV0: 'SealV0', // 3 + Consensus: 'Consensus', // 4 + Seal: 'Seal', // 5 + PreRuntime: 'PreRuntime' // 6 }, value); } diff --git a/packages/types/src/primitive/Generic/Event.ts b/packages/types/src/primitive/Generic/Event.ts index d1dd984522..2db7b629af 100644 --- a/packages/types/src/primitive/Generic/Event.ts +++ b/packages/types/src/primitive/Generic/Event.ts @@ -8,7 +8,7 @@ import { Constructor, Codec } from '../../types'; import { assert, isUndefined, stringCamelCase, u8aToHex } from '@polkadot/util'; -import { ClassOf, getTypeClass, getTypeDef } from '../../codec/createType'; +import { getTypeClass, getTypeDef } from '../../codec/createType'; import Struct from '../../codec/Struct'; import Tuple from '../../codec/Tuple'; import Metadata from '../../Metadata'; @@ -83,7 +83,7 @@ export default class Event extends Struct { const { DataType, value } = Event.decodeEvent(_value); super({ - index: ClassOf('EventId'), + index: 'EventId', data: DataType }, value); } diff --git a/packages/types/src/types.ts b/packages/types/src/types.ts index 4406e4c97e..ef5a3018c2 100644 --- a/packages/types/src/types.ts +++ b/packages/types/src/types.ts @@ -8,12 +8,15 @@ import BN from 'bn.js'; import Compact from './codec/Compact'; import U8a from './codec/U8a'; +import { InterfaceRegistry } from './interfaceRegistry'; import { FunctionMetadata } from './Metadata/v7/Calls'; import Call from './primitive/Generic/Call'; import Address from './primitive/Generic/Address'; export * from './codec/types'; +export type InterfaceTypes = keyof InterfaceRegistry; + export interface CallFunction { (...args: any[]): Call; callIndex: Uint8Array;