diff --git a/CHANGELOG.md b/CHANGELOG.md index d532f2dd6689..c0b322c1c136 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ - Move `H160`, `H256` & `H512` to interfaces in `@polkadot/types/interfaces` - Align construction of unknown types via `registry.get` e.g. in Events, warn on detection, throw on use - Expose static with on `UInt`, `Int` & `U8aFixed` classes with optional type name override +- Support for arbitrary UInt types via `UInt` definitions # 1.3.1 Feb 18, 2020 diff --git a/packages/typegen/src/generate/tsDef.ts b/packages/typegen/src/generate/tsDef.ts index 05616903bca9..8644a1e914f1 100644 --- a/packages/typegen/src/generate/tsDef.ts +++ b/packages/typegen/src/generate/tsDef.ts @@ -122,6 +122,13 @@ function tsStruct (definitions: object, { name: structName, sub }: TypeDef, impo return exportInterface(structName, 'Struct', keys.join('')); } +/** @internal */ +function tsUInt (definitions: object, def: TypeDef, imports: TypeImports): string { + setImports(definitions, imports, ['UInt']); + + return exportInterface(def.name, 'UInt'); +} + /** @internal */ function tsVec (definitions: object, def: TypeDef, imports: TypeImports): string { const type = def.info === TypeDefInfo.VecFixed @@ -155,6 +162,7 @@ function generateInterfaces (definitions: object, { types }: { types: Record): string { })`; } +function encodeUInt (typeDef: Pick): string { + assert(typeDef.ext, 'Unable to encode VecFixed type'); + + const { length } = typeDef.ext as TypeDefExtUInt; + + return `UInt<${length}>`; +} + function encodeVecFixed (typeDef: Pick): string { assert(typeDef.ext, 'Unable to encode VecFixed type'); @@ -127,6 +135,7 @@ const encoders: Record string> = { [TypeDefInfo.Set]: (typeDef: TypeDef): string => typeDef.type, [TypeDefInfo.Struct]: (typeDef: TypeDef): string => encodeStruct(typeDef), [TypeDefInfo.Tuple]: (typeDef: TypeDef): string => encodeTuple(typeDef), + [TypeDefInfo.UInt]: (typeDef: TypeDef): string => encodeUInt(typeDef), [TypeDefInfo.Vec]: (typeDef: TypeDef): string => encodeWithParams(typeDef, 'Vec'), [TypeDefInfo.VecFixed]: (typeDef: TypeDef): string => encodeVecFixed(typeDef) }; diff --git a/packages/types/src/create/createClass.ts b/packages/types/src/create/createClass.ts index e7094716d661..8b8c0a2eb39b 100644 --- a/packages/types/src/create/createClass.ts +++ b/packages/types/src/create/createClass.ts @@ -3,7 +3,7 @@ // of the Apache-2.0 license. See the LICENSE file for details. import { Codec, Constructor, InterfaceTypes, Registry } from '../types'; -import { FromReg, TypeDef, TypeDefExtVecFixed, TypeDefInfo } from './types'; +import { FromReg, TypeDef, TypeDefExtUInt, TypeDefExtVecFixed, TypeDefInfo } from './types'; import { assert } from '@polkadot/util'; @@ -17,6 +17,7 @@ import CodecSet from '../codec/Set'; import Struct from '../codec/Struct'; import Tuple from '../codec/Tuple'; import U8aFixed, { BitLength as U8aFixedBitLength } from '../codec/U8aFixed'; +import UInt from '../codec/UInt'; import Vec from '../codec/Vec'; import VecFixed from '../codec/VecFixed'; import { InterfaceRegistry } from '../interfaceRegistry'; @@ -132,6 +133,14 @@ const infoMapping: Record C [TypeDefInfo.Tuple]: (registry: Registry, value: TypeDef): Constructor => Tuple.with(getTypeClassArray(value)), + [TypeDefInfo.UInt]: (registry: Registry, value: TypeDef): Constructor => { + assert(value.ext, 'Expected bitLength information for UInt'); + + const ext = value.ext as TypeDefExtUInt; + + return UInt.with(ext.length, ext.typeName); + }, + [TypeDefInfo.Vec]: (registry: Registry, value: TypeDef): Constructor => { const subType = getSubType(value); diff --git a/packages/types/src/create/createType.spec.ts b/packages/types/src/create/createType.spec.ts index 8040f66e8f6d..b5e992f01378 100644 --- a/packages/types/src/create/createType.spec.ts +++ b/packages/types/src/create/createType.spec.ts @@ -18,15 +18,17 @@ describe('createType', (): void => { }); it('allows creation of a Struct', (): void => { - expect( - createTypeUnsafe(registry, '{"balance":"Balance","index":"u32"}', [{ - balance: 1234, - index: '0x10' - }]).toJSON() - ).toEqual({ + const raw = '{"balance":"Balance","index":"u32"}'; + const struct = createTypeUnsafe(registry, raw, [{ + balance: 1234, + index: '0x10' + }]); + + expect(struct.toJSON()).toEqual({ balance: 1234, index: 16 }); + expect(struct.toRawType()).toEqual(raw); }); it('allows creation of a BTreeMap', (): void => { @@ -41,18 +43,6 @@ describe('createType', (): void => { ).toEqual('[2,24,30,80]'); }); - it('allows creation of a Result', (): void => { - expect( - createTypeUnsafe(registry, 'Result', ['0x011064656667']).toJSON() - ).toEqual({ Error: 'defg' }); - }); - - it('allows creation of a Tuple', (): void => { - expect( - createTypeUnsafe(registry, '(Balance,u32)', [[1234, 5678]]).toJSON() - ).toEqual([1234, 5678]); - }); - it('allows creation of a Enum (simple)', (): void => { expect( createTypeUnsafe(registry, '{"_enum": ["A", "B", "C"]}', [1]).toJSON() @@ -65,12 +55,36 @@ describe('createType', (): void => { ).toEqual({ B: 0 }); }); + it('allows creation of a Result', (): void => { + expect( + createTypeUnsafe(registry, 'Result', ['0x011064656667']).toJSON() + ).toEqual({ Error: 'defg' }); + }); + it('allows creation of a Set', (): void => { expect( createTypeUnsafe(registry, '{"_set": { "A": 1, "B": 2, "C": 4, "D": 8, "E": 16, "G": 32, "H": 64, "I": 128 } }', [1 + 4 + 16 + 64]).strings ).toEqual(['A', 'C', 'E', 'H']); }); + it('allows creation of a Tuple', (): void => { + expect( + createTypeUnsafe(registry, '(Balance,u32)', [[1234, 5678]]).toJSON() + ).toEqual([1234, 5678]); + }); + + it('allows creation for a UInt', (): void => { + expect( + createTypeUnsafe(registry, 'UInt<2048>').toRawType() + ).toEqual('u2048'); + }); + + it('fails creation for a UInt where bitLength is not power of 8', (): void => { + expect( + () => createTypeUnsafe(registry, 'UInt<20>').toRawType() + ).toThrow('UInt<20>: Only support for UInt, where length <= 8192 and a power of 8'); + }); + it('allows creation of a [u8; 8]', (): void => { expect( createTypeUnsafe(registry, '[u8; 8]', [[0x12, 0x00, 0x23, 0x00, 0x45, 0x00, 0x67, 0x00]]).toHex() diff --git a/packages/types/src/create/getTypeDef.ts b/packages/types/src/create/getTypeDef.ts index 50cd964a0045..5fce6cf5e4b8 100644 --- a/packages/types/src/create/getTypeDef.ts +++ b/packages/types/src/create/getTypeDef.ts @@ -2,7 +2,7 @@ // This software may be modified and distributed under the terms // of the Apache-2.0 license. See the LICENSE file for details. -import { TypeDef, TypeDefExtVecFixed, TypeDefInfo } from './types'; +import { TypeDef, TypeDefExtUInt, TypeDefExtVecFixed, TypeDefInfo } from './types'; import { assert } from '@polkadot/util'; @@ -90,6 +90,20 @@ function _decodeTuple (value: TypeDef, _: string, subType: string): TypeDef { return value; } +// decode a fixed vector, e.g. [u8;32] +// eslint-disable-next-line @typescript-eslint/no-unused-vars +function _decodeUInt (value: TypeDef, type: string, _: string): TypeDef { + const _bitLength = type.substr(5, type.length - 6); + const length = parseInt(_bitLength.trim(), 10); + + // as a first round, only u8 via u8aFixed, we can add more support + assert(length <= 8192 && (length % 8) === 0, `${type}: Only support for UInt, where length <= 8192 and a power of 8`); + + value.ext = { length } as TypeDefExtUInt; + + return value; +} + function hasWrapper (type: string, [start, end]: [string, string, TypeDefInfo, any?]): boolean { if (type.substr(0, start.length) !== start) { return false; @@ -106,7 +120,8 @@ const nestedExtraction: [string, string, TypeDefInfo, (value: TypeDef, type: str ['(', ')', TypeDefInfo.Tuple, _decodeTuple], // the inner for these are the same as tuple, multiple values ['BTreeMap<', '>', TypeDefInfo.BTreeMap, _decodeTuple], - ['Result<', '>', TypeDefInfo.Result, _decodeTuple] + ['Result<', '>', TypeDefInfo.Result, _decodeTuple], + ['UInt<', '>', TypeDefInfo.UInt, _decodeUInt] ]; const wrappedExtraction: [string, string, TypeDefInfo][] = [ diff --git a/packages/types/src/create/sanitize.ts b/packages/types/src/create/sanitize.ts index 885cb3a52161..0ee323a780c5 100644 --- a/packages/types/src/create/sanitize.ts +++ b/packages/types/src/create/sanitize.ts @@ -5,7 +5,7 @@ type Mapper = (value: string) => string; -const ALLOWED_BOXES = ['BTreeMap', 'BTreeSet', 'Compact', 'Linkage', 'Result', 'Option', 'Vec']; +const ALLOWED_BOXES = ['BTreeMap', 'BTreeSet', 'Compact', 'Linkage', 'Result', 'Option', 'UInt', 'Vec']; const BOX_PRECEDING = ['<', '(', '[', '"', ',', ' ']; // start of vec, tuple, fixed array, part of struct def or in tuple const mappings: Mapper[] = [ diff --git a/packages/types/src/create/types.ts b/packages/types/src/create/types.ts index aab8d0cc912d..dc2ac4980877 100644 --- a/packages/types/src/create/types.ts +++ b/packages/types/src/create/types.ts @@ -2,6 +2,7 @@ // This software may be modified and distributed under the terms // of the Apache-2.0 license. See the LICENSE file for details. +import { UIntBitLength } from '../codec/AbstractInt'; import { Codec, InterfaceTypes } from '../types'; import { InterfaceRegistry } from '../interfaceRegistry'; @@ -25,10 +26,16 @@ export enum TypeDefInfo { Tuple, Vec, VecFixed, + UInt, // anything not fully supported (keep this as the last entry) Null } +export interface TypeDefExtUInt { + length: UIntBitLength; + typeName?: string; +} + export interface TypeDefExtVecFixed { length: number; rawName?: string; @@ -43,7 +50,7 @@ export interface TypeDef { info: TypeDefInfo; index?: number; displayName?: string; - ext?: TypeDefExtVecFixed | TypeDefExtEnumDiscriminant; // add additional here as required + ext?: TypeDefExtEnumDiscriminant | TypeDefExtUInt | TypeDefExtVecFixed; // add additional here as required name?: string; namespace?: string; params?: TypeDef[];