diff --git a/packages/types/src/codec/U8aFixed.spec.ts b/packages/types/src/codec/U8aFixed.spec.ts index 03ce28c09d..0d1ca3c6ce 100644 --- a/packages/types/src/codec/U8aFixed.spec.ts +++ b/packages/types/src/codec/U8aFixed.spec.ts @@ -30,4 +30,10 @@ describe('U8aFixed', () => { new U8aFixed().toHex() ).toEqual('0x0000000000000000000000000000000000000000000000000000000000000000'); }); + + it('allows construction via with', () => { + expect( + new (U8aFixed.with(64))().bitLength() + ).toEqual(64); + }); }); diff --git a/packages/types/src/codec/U8aFixed.ts b/packages/types/src/codec/U8aFixed.ts index a9358151a3..896fda332c 100644 --- a/packages/types/src/codec/U8aFixed.ts +++ b/packages/types/src/codec/U8aFixed.ts @@ -4,11 +4,11 @@ import { isString, isU8a, u8aToU8a } from '@polkadot/util'; -import { AnyU8a } from '../types'; +import { AnyU8a, Constructor } from '../types'; import U8a from './U8a'; -type BitLength = 8 | 16 | 32 | 64 | 128 | 160 | 256 | 512; +export type BitLength = 8 | 16 | 32 | 64 | 128 | 160 | 256 | 512 | 1024 | 2048; /** * @name U8aFixed @@ -46,6 +46,14 @@ export default class U8aFixed extends U8a { return value; } + static with (bitLength: BitLength): Constructor { + return class extends U8aFixed { + constructor (value?: any) { + super(value, bitLength); + } + }; + } + /** * @description Returns the number of bits in the value */ diff --git a/packages/types/src/codec/createType.spec.ts b/packages/types/src/codec/createType.spec.ts index 0d239d7224..134c2f6a08 100644 --- a/packages/types/src/codec/createType.spec.ts +++ b/packages/types/src/codec/createType.spec.ts @@ -233,8 +233,15 @@ describe('createType', () => { ).toEqual({ B: 0 }); }); + it('allows creation of a [u8; 8]', () => { + expect( + createType('[u8; 8]', [0x12, 0x00, 0x23, 0x00, 0x45, 0x00, 0x67, 0x00]).toHex() + ).toEqual('0x1200230045006700'); + }); + it('throw error when create base is a StorageData with null value and isPedantic is true' , () => { const base = createType('StorageData', null); + expect( () => createType('DoubleMap>', base, true) ).toThrow(/Encoding for input doesn't match output, created 0x00 from 0x/); @@ -242,6 +249,7 @@ describe('createType', () => { it('throw error when create base is a StorageData with null value and isPedantic is true' , () => { const base = createType('StorageData', null); + expect( () => createType('Vec<(BlockNumber,EventIndex)>', base, true) ).toThrow(/Encoding for input doesn't match output, created 0x00 from 0x/); diff --git a/packages/types/src/codec/createType.ts b/packages/types/src/codec/createType.ts index b8987a8895..85c33a4931 100644 --- a/packages/types/src/codec/createType.ts +++ b/packages/types/src/codec/createType.ts @@ -13,26 +13,34 @@ import Linkage from './Linkage'; import Option from './Option'; import Struct from './Struct'; import Tuple from './Tuple'; +import U8aFixed, { BitLength as U8aFixedBitLength } from './U8aFixed'; import UInt from './UInt'; import Vector from './Vector'; import getRegistry from './typeRegistry'; export enum TypeDefInfo { Compact, + DoubleMap, Enum, + Linkage, Option, Plain, Struct, Tuple, Vector, - Linkage, - DoubleMap, + VectorFixed, // anything not full supported (keep this as the last entry) Null } +export type TypeDefExtVecFixed = { + length: number, + type: string +}; + export type TypeDef = { info: TypeDefInfo, + ext?: TypeDefExtVecFixed, // add additional here as required name?: string, type: string, sub?: TypeDef | Array @@ -40,9 +48,10 @@ export type TypeDef = { // safely split a string on ', ' while taking care of any nested occurences export function typeSplit (type: string): Array { - let sDepth = 0; - let tDepth = 0; - let vDepth = 0; + let cDepth = 0; // compact/doublemap/linkedmap/option/vector depth + let fDepth = 0; // vector (fixed) depth + let sDepth = 0; // struct depth + let tDepth = 0; // tuple depth let start = 0; const result = []; @@ -50,36 +59,34 @@ export function typeSplit (type: string): Array { switch (type[index]) { case ',': // we are not nested, add the type - if (sDepth === 0 && tDepth === 0 && vDepth === 0) { + if (cDepth === 0 && fDepth === 0 && sDepth === 0 && tDepth === 0) { result.push(type.substr(start, index - start).trim()); start = index + 1; } break; - // inc struct depth, start found - case '{': sDepth++; break; + // adjust compact/vec (and friends) depth + case '<': cDepth++; break; + case '>': cDepth--; break; - // dec struct depth, end found + // adjust fixed vec depths + case '[': fDepth++; break; + case ']': fDepth--; break; + + // adjust struct depth + case '{': sDepth++; break; case '}': sDepth--; break; - // inc tuple depth, start found + // adjusttuple depth case '(': tDepth++; break; - - // dec tuple depth, end found case ')': tDepth--; break; - // inc compact/vec depth, start found - case '<': vDepth++; break; - - // dec compact/vec depth, end found - case '>': vDepth--; break; - // normal character default: break; } } - assert(!sDepth && !tDepth && !vDepth, `Invalid defintion (missing terminators) found in ${type}`); + assert(!cDepth && !fDepth && !sDepth && !tDepth, `Invalid defintion (missing terminators) found in ${type}`); // the final leg of the journey result.push(type.substr(start, type.length - start).trim()); @@ -111,6 +118,16 @@ export function getTypeDef (_type: Text | string, name?: string): TypeDef { if (startingWith(type, '(', ')')) { value.info = TypeDefInfo.Tuple; value.sub = typeSplit(subType).map((inner) => getTypeDef(inner)); + } else if (startingWith(type, '[', ']')) { + // this handles e.g. [u8;32] + const [vecType, _vecLen] = type.substr(1, type.length - 2).split(';'); + const vecLen = parseInt(_vecLen.trim(), 10); + + // as a first round, only u8 via u8aFixed, we can add more support + assert(vecLen <= 256 && ['u8'].includes(vecType), `Only support for [u8; ], where length <= 256`); + + value.info = TypeDefInfo.VectorFixed; + value.ext = { length: vecLen, type: 'u8a' } as TypeDefExtVecFixed; } else if (startingWith(type, '{', '}')) { const parsed = JSON.parse(type); const keys = Object.keys(parsed); @@ -208,6 +225,10 @@ export function getTypeClass (value: TypeDef): Constructor { return Vector.with( getTypeClass(value.sub as TypeDef) ); + case TypeDefInfo.VectorFixed: + assert(value.ext, 'Expected length & type information for fixed vector'); + + return U8aFixed.with(((value.ext as TypeDefExtVecFixed).length * 8) as U8aFixedBitLength); case TypeDefInfo.Linkage: assert(value.sub && !Array.isArray(value.sub), 'Expected subtype for Linkage');