Cleanup & split createType (#1307)

* Cleanup & split createType

* createType import

* Reduce complexity somewhat

* Simplify wrapper check

* Revert "Simplify wrapper check"

This reverts commit e072c7e05e30e68994152af0b5d7b624dc38bba1.

* de-dup getTypeClass

* Explicit extraction, cleanup getTypeDef

* De-dup subType extraction

* createType move
This commit is contained in:
Jaco Greeff
2019-08-14 14:06:45 +02:00
committed by GitHub
parent 2cdf2923d1
commit 4115b8a4cc
62 changed files with 907 additions and 797 deletions
+1 -1
View File
@@ -6,7 +6,7 @@ import '../../injector';
import extrinsicsFromMeta from '@polkadot/api-metadata/extrinsics/fromMetadata';
import { createTypeUnsafe } from '../../codec/createType';
import { createTypeUnsafe } from '../../codec/create';
import Metadata from '../Metadata';
import Call from '../../primitive/Generic/Call';
import { MetadataInterface } from '../types';
@@ -4,7 +4,7 @@
import { TypeDef, TypeDefInfo, TypeDefExtVecFixed } from '../../codec/types';
import { getTypeDef } from '../../codec/createType';
import { getTypeDef } from '../../codec/create';
import flattenUniq from './flattenUniq';
import { getTypeRegistry } from '../../codec';
+1 -1
View File
@@ -6,7 +6,7 @@ import '../injector';
import BN from 'bn.js';
import createType, { ClassOf } from '../codec/createType';
import { createType, ClassOf } from './create';
import Moment from './Date';
import U32 from '../primitive/U32';
import Compact from './Compact';
+1 -1
View File
@@ -8,7 +8,7 @@ import BN from 'bn.js';
import { bnToBn, bnToHex, bnToU8a, isString, isU8a, u8aToBn } from '@polkadot/util';
import { blake2AsU8a } from '@polkadot/util-crypto';
import createType from './createType';
import { createType } from './create';
import { UIntBitLength } from './AbstractInt';
const BITLENGTH: UIntBitLength = 64;
+1 -1
View File
@@ -4,7 +4,7 @@
import '../injector';
import { ClassOf } from '../codec/createType';
import { ClassOf } from './create';
import AccountId from '../primitive/Generic/AccountId';
import Text from '../primitive/Text';
import U32 from '../primitive/U32';
+1 -1
View File
@@ -10,7 +10,7 @@ import { CodecTo } from '../types';
import extrinsics from '@polkadot/api-metadata/extrinsics/static';
import { ClassOf } from '../codec/createType';
import { ClassOf } from './create';
import Call from '../primitive/Generic/Call';
import Text from '../primitive/Text';
import U32 from '../primitive/U32';
+1 -1
View File
@@ -6,7 +6,7 @@ import { AnyNumber } from '../types';
import { bnToHex, bnToU8a } from '@polkadot/util';
import { ClassOf } from './createType';
import { ClassOf } from './create';
import AbstractInt, { DEFAULT_UINT_BITS, UIntBitLength } from './AbstractInt';
/**
+1 -1
View File
@@ -10,7 +10,7 @@ import extrinsics from '@polkadot/api-metadata/extrinsics/static';
import AccountId from '../primitive/Generic/AccountId';
import Call from '../primitive/Generic/Call';
import Text from '../primitive/Text';
import { createTypeUnsafe } from './createType';
import { createTypeUnsafe } from './create';
import Vec from './Vec';
import Tuple from './Tuple';
+1 -1
View File
@@ -42,7 +42,7 @@ export default class VecFixed<T extends Codec> extends AbstractArray<T> {
return values;
}
public static with<O extends Codec> (Type: Constructor<O>, length: number): Constructor<VecFixed<O>> {
public static with<O extends Codec> (Type: Constructor<O> | InterfaceTypes, length: number): Constructor<VecFixed<O>> {
return class extends VecFixed<O> {
public constructor (value?: any[]) {
super(Type, length, value);
@@ -0,0 +1,23 @@
// 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 '../../injector';
import { createClass } from '.';
describe('createClass', (): void => {
it('should memoize from strings', (): void => {
const a = createClass('BabeWeight');
const b = createClass('BabeWeight');
expect(a).toBe(b);
});
it('should return equivalents for Bytes & Vec<u8>', (): void => {
const A = createClass('Vec<u8>');
const B = createClass('Bytes');
expect(new A() instanceof B).toBe(true);
});
});
@@ -0,0 +1,28 @@
// 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 { FromReg } from './types';
import { InterfaceRegistry } from '../../interfaceRegistry';
import { getTypeClass } from './getTypeClass';
import { getTypeDef } from './getTypeDef';
export function createClass<T extends Codec = Codec, K extends string = string> (
type: K
): Constructor<FromReg<T, K>> {
return getTypeClass<FromReg<T, K>>(getTypeDef(type));
}
// An unsafe version of the `createType` below. It's unsafe because the `type`
// argument here can be any string, which, if not parseable, will yield a
// runtime error.
export function ClassOfUnsafe<T extends Codec = Codec, K extends string = string> (name: K): Constructor<FromReg<T, K>> {
return createClass<T, K>(name);
}
// alias for createClass
export function ClassOf<K extends InterfaceTypes> (name: K): Constructor<InterfaceRegistry[K]> {
return ClassOfUnsafe<Codec, K>(name) as Constructor<InterfaceRegistry[K]>;
}
@@ -0,0 +1,129 @@
// 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 '../../injector';
import { createClass, createType, createTypeUnsafe, getTypeRegistry, ClassOf } from '.';
import CodecSet from '../Set';
describe('createType', (): void => {
it('allows creation of a Struct', (): void => {
expect(
createTypeUnsafe('{"balance":"Balance","index":"u32"}', [{
balance: 1234,
index: '0x10'
}]).toJSON()
).toEqual({
balance: 1234,
index: 16
});
});
it('allows creation of a Tuple', (): void => {
expect(
createTypeUnsafe('(Balance,u32)', [[1234, 5678]]).toJSON()
).toEqual([1234, 5678]);
});
it('allows creation of a Enum (simple)', (): void => {
expect(
createTypeUnsafe('{"_enum": ["A", "B", "C"]}', [1]).toJSON()
).toEqual({ B: null });
});
it('allows creation of a Enum (parametrised)', (): void => {
expect(
createTypeUnsafe('{"_enum": {"A": null, "B": "u32", "C": null} }', [1]).toJSON()
).toEqual({ B: 0 });
});
it('allows creation of a Set', (): void => {
expect(
createTypeUnsafe<CodecSet>('{"_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 [u8; 8]', (): void => {
expect(
createTypeUnsafe('[u8; 8]', [[0x12, 0x00, 0x23, 0x00, 0x45, 0x00, 0x67, 0x00]]).toHex()
).toEqual('0x1200230045006700');
});
it('allows creation of a [u16; 4]', (): void => {
expect(
createTypeUnsafe('[u16; 4]', [[0x1200, 0x2300, 0x4500, 0x6700]]).toU8a()
).toEqual(new Uint8Array([0x00, 0x12, 0x00, 0x23, 0x00, 0x45, 0x00, 0x67]));
});
describe('instanceof', (): void => {
it('instanceof should work (primitive type)', (): void => {
const value = createType('Balance', 1234);
expect(value instanceof ClassOf('Balance')).toBe(true);
});
it('instanceof should work (srml type)', (): void => {
const value = createType('Gas', 1234);
const Gas = ClassOf('Gas');
expect(value instanceof Gas).toBe(true);
});
it('instanceof should work (complex type)', (): void => {
getTypeRegistry().register({
TestComplex: {
balance: 'Balance',
accountId: 'AccountId',
log: '(u64, u32)',
fromSrml: 'Gas'
}
});
const value = createTypeUnsafe('TestComplex', [{
balance: 123,
accountId: '0x1234567812345678123456781234567812345678123456781234567812345678',
log: [456, 789],
fromSrml: 0
}]);
expect(value instanceof createClass('TestComplex')).toBe(true);
});
it('allows for re-registration of a type', (): void => {
const balDef = createType('Balance');
expect(balDef instanceof ClassOf('Balance'));
expect(balDef.bitLength()).toEqual(128);
getTypeRegistry().register({ Balance: 'u32' });
const balu32 = createType('Balance');
expect(balu32 instanceof ClassOf('Balance'));
expect(balu32.bitLength()).toEqual(32);
});
it('allows for re-registration of a type (affecting derives)', (): void => {
getTypeRegistry().register({
Balance: 'u128',
TestComplex: {
balance: 'Balance',
accountId: 'AccountId',
log: '(u64, u32)',
fromSrml: 'Gas'
}
});
const cmpDef: any = createTypeUnsafe('TestComplex');
expect(cmpDef.balance.bitLength()).toEqual(128);
getTypeRegistry().register({ Balance: 'u32' });
const cmpu32: any = createTypeUnsafe('TestComplex');
expect(cmpu32.balance.bitLength()).toEqual(32);
});
});
});
@@ -0,0 +1,61 @@
// 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 { FromReg } from './types';
import { InterfaceRegistry } from '../../interfaceRegistry';
import { createClass } from './createClass';
// With isPedantic, actually check that the encoding matches that supplied. This
// is much slower, but verifies that we have the correct types defined
function checkInstance<T extends Codec = Codec, K extends string = string> (value: any, created: FromReg<T, K>): void {
const inHex = value.toHex(true);
const crHex = created.toHex(true);
const hasMatch = (inHex === crHex) || (value.toU8a(true).toString() === (
created instanceof Uint8Array
// strip the input length
? created.toU8a().toString()
// compare raw. without additions
: created.toU8a(true).toString()
));
if (!hasMatch) {
console.warn(`${created.toRawType()}:: Input doesn't match output, received ${inHex}, created ${crHex}`);
}
}
// Initializes a type with a value. This also checks for fallbacks and in the cases
// where isPedantic is specified (storage decoding), also check the format/structure
function initType<T extends Codec = Codec, K extends string = string> (Type: Constructor<FromReg<T, K>>, params: any[] = [], isPedantic?: boolean): FromReg<T, K> {
const created = new Type(...params);
const [value] = params;
if (isPedantic && value && value.toU8a && !value.isEmpty) {
checkInstance(value, created);
}
return created;
}
// An unsafe version of the `createType` below. It's unsafe because the `type`
// argument here can be any string, which, if not parseable, will yield a
// runtime error.
export function createTypeUnsafe<T extends Codec = Codec, K extends string = string> (type: K, params: any[] = [], isPedantic?: boolean): FromReg<T, K> {
try {
return initType(createClass<T, K>(type), params, isPedantic);
} catch (error) {
throw new Error(`createType(${type}):: ${error.message}`);
}
}
/**
* Create an instance of a `type` with a given `params`.
* @param type - A recognizable string representing the type to create an
* instance from
* @param params - The value to instantiate the type with
*/
export function createType<K extends InterfaceTypes> (type: K, ...params: any[]): InterfaceRegistry[K] {
return createTypeUnsafe<Codec, K>(type, params) as InterfaceRegistry[K];
}
@@ -0,0 +1,17 @@
// 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 '../../injector';
import { Codec, Constructor } from '../../types';
import { getTypeClass } from '.';
describe('getTypeClass', (): void => {
it('does not allow invalid types', (): void => {
expect(
(): Constructor<Codec> => getTypeClass('SomethingInvalid' as any)
).toThrow(/determine type/);
});
});
@@ -0,0 +1,130 @@
// 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 { TypeDef, TypeDefExtVecFixed, TypeDefInfo } from './types';
import { assert } from '@polkadot/util';
import Compact from '../Compact';
import Enum from '../Enum';
import Linkage from '../Linkage';
import Option from '../Option';
import CodecSet from '../Set';
import Struct from '../Struct';
import Tuple from '../Tuple';
import U8aFixed, { BitLength as U8aFixedBitLength } from '../U8aFixed';
import Vec from '../Vec';
import VecFixed from '../VecFixed';
import { ClassOf } from './createClass';
import { getTypeRegistry } from './registry';
function getSubDefArray (value: TypeDef): TypeDef[] {
assert(value.sub && Array.isArray(value.sub), `Expected subtype as TypeDef[] in ${JSON.stringify(value)}`);
return value.sub as TypeDef[];
}
function getSubDef (value: TypeDef): TypeDef {
assert(value.sub && !Array.isArray(value.sub), `Expected subtype as TypeDef in ${JSON.stringify(value)}`);
return value.sub as TypeDef;
}
function getSubType (value: TypeDef): InterfaceTypes {
return getSubDef(value).type as InterfaceTypes;
}
// create a maps of type string constructors from the input
function getTypeClassMap (value: TypeDef): Record<string, InterfaceTypes> {
const result: Record<string, InterfaceTypes> = {};
return getSubDefArray(value).reduce((result, sub): Record<string, InterfaceTypes> => {
result[sub.name as string] = sub.type as any;
return result;
}, result);
}
// create an array of type string constructors from the input
function getTypeClassArray (value: TypeDef): (InterfaceTypes)[] {
return getSubDefArray(value).map(({ type }): InterfaceTypes =>
type as InterfaceTypes
);
}
const infoMapping: Record<TypeDefInfo, (value: TypeDef) => Constructor> = {
[TypeDefInfo.Compact]: (value: TypeDef): Constructor => Compact.with(getSubType(value)),
// eslint-disable-next-line @typescript-eslint/no-use-before-define
[TypeDefInfo.DoubleMap]: (value: TypeDef): Constructor => getTypeClass(getSubDef(value)),
[TypeDefInfo.Enum]: (value: TypeDef): Constructor => Enum.with(getTypeClassMap(value)),
[TypeDefInfo.Linkage]: (value: TypeDef): Constructor => Linkage.withKey(getSubType(value)),
// eslint-disable-next-line @typescript-eslint/no-unused-vars
[TypeDefInfo.Null]: (value: TypeDef): Constructor => ClassOf('Null'),
[TypeDefInfo.Option]: (value: TypeDef): Constructor => Option.with(getSubType(value)),
[TypeDefInfo.Plain]: (value: TypeDef): Constructor => {
throw new Error(`Unable to find plain type for ${JSON.stringify(value)}`);
},
[TypeDefInfo.Set]: (value: TypeDef): Constructor => {
const result: Record<string, number> = {};
return CodecSet.with(
getSubDefArray(value).reduce((result, { name, index }): Record<string, number> => {
result[name as string] = index as number;
return result;
}, result)
);
},
[TypeDefInfo.Struct]: (value: TypeDef): Constructor => Struct.with(getTypeClassMap(value)),
[TypeDefInfo.Tuple]: (value: TypeDef): Constructor => Tuple.with(getTypeClassArray(value)),
[TypeDefInfo.Vec]: (value: TypeDef): Constructor => {
const subType = getSubType(value);
return (
subType === 'u8'
? ClassOf('Bytes')
: Vec.with(subType)
);
},
[TypeDefInfo.VecFixed]: (value: TypeDef): Constructor => {
assert(value.ext, 'Expected length & type information for fixed vector');
const ext = value.ext as TypeDefExtVecFixed;
return (
ext.type === 'u8'
? U8aFixed.with((ext.length * 8) as U8aFixedBitLength)
: VecFixed.with(ext.type as InterfaceTypes, ext.length)
);
}
};
// Returns the type Class for construction
export function getTypeClass<T extends Codec = Codec> (value: TypeDef): Constructor<T> {
const Type = getTypeRegistry().get<T>(value.type);
if (Type) {
return Type;
}
const getFn = infoMapping[value.info];
if (!getFn) {
throw new Error(`Unable to determine type from ${JSON.stringify(value)}`);
}
return getFn(value) as Constructor<T>;
}
@@ -0,0 +1,161 @@
// 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 '../../injector';
import { TypeDef, TypeDefInfo } from '../types';
import { getTypeDef } from '.';
describe('getTypeDef', (): void => {
it('does not allow invalid tuples, end )', (): void => {
expect(
(): TypeDef => getTypeDef('(u64, u32')
).toThrow(/Expected '\(' closing with '\)'/);
});
it('does not allow invalid vectors, end >', (): void => {
expect(
(): TypeDef => getTypeDef('Vec<u64')
).toThrow(/Expected 'Vec<' closing with '>'/);
});
it('returns a type structure', (): void => {
expect(
getTypeDef('(u32, Compact<u32>, Vec<u64>, Option<u128>, DoubleMap<u128>, (Text, Vec<(Bool, u128)>))')
).toEqual({
info: TypeDefInfo.Tuple,
type: '(u32, Compact<u32>, Vec<u64>, Option<u128>, DoubleMap<u128>, (Text, Vec<(Bool, u128)>))',
sub: [
{
info: TypeDefInfo.Plain,
type: 'u32'
},
{
info: TypeDefInfo.Compact,
type: 'Compact<u32>',
sub: {
info: TypeDefInfo.Plain,
type: 'u32'
}
},
{
info: TypeDefInfo.Vec,
type: 'Vec<u64>',
sub: {
info: TypeDefInfo.Plain,
type: 'u64'
}
},
{
info: TypeDefInfo.Option,
type: 'Option<u128>',
sub: {
info: TypeDefInfo.Plain,
type: 'u128'
}
},
{
info: TypeDefInfo.DoubleMap,
type: 'DoubleMap<u128>',
sub: {
info: TypeDefInfo.Plain,
type: 'u128'
}
},
{
info: TypeDefInfo.Tuple,
type: '(Text, Vec<(Bool, u128)>)',
sub: [
{
info: TypeDefInfo.Plain,
type: 'Text'
},
{
info: TypeDefInfo.Vec,
type: 'Vec<(Bool, u128)>',
sub: {
info: TypeDefInfo.Tuple,
type: '(Bool, u128)',
sub: [
{
info: TypeDefInfo.Plain,
type: 'Bool'
},
{
info: TypeDefInfo.Plain,
type: 'u128'
}
]
}
}
]
}
]
});
});
it('returns a type structure (actual)', (): void => {
expect(
getTypeDef('Vec<(PropIndex, Proposal, AccountId)>')
).toEqual({
info: TypeDefInfo.Vec,
type: 'Vec<(PropIndex, Proposal, AccountId)>',
sub: {
info: TypeDefInfo.Tuple,
type: '(PropIndex, Proposal, AccountId)',
sub: [
{
info: TypeDefInfo.Plain,
type: 'PropIndex'
},
{
info: TypeDefInfo.Plain,
type: 'Proposal'
},
{
info: TypeDefInfo.Plain,
type: 'AccountId'
}
]
}
});
});
it('returns an actual Struct', (): void => {
expect(
getTypeDef('{"balance":"Balance","account_id":"AccountId","log":"(u64, Signature)"}')
).toEqual({
info: TypeDefInfo.Struct,
type: '{"balance":"Balance","account_id":"AccountId","log":"(u64, Signature)"}',
sub: [
{
info: TypeDefInfo.Plain,
name: 'balance',
type: 'Balance'
},
{
info: TypeDefInfo.Plain,
name: 'account_id',
type: 'AccountId'
},
{
info: TypeDefInfo.Tuple,
name: 'log',
type: '(u64, Signature)',
sub: [
{
info: TypeDefInfo.Plain,
type: 'u64'
},
{
info: TypeDefInfo.Plain,
type: 'Signature'
}
]
}
]
});
});
});
@@ -0,0 +1,143 @@
// 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 { TypeDef, TypeDefExtVecFixed, TypeDefInfo } from './types';
import { assert } from '@polkadot/util';
import { typeSplit } from './typeSplit';
// decode an enum of either of the following forms
// { _enum: ['A', 'B', 'C'] }
// { _enum: { A: AccountId, B: Balance, C: u32 } }
function _decodeEnum (value: TypeDef, details: string[] | Record<string, string>): TypeDef {
value.info = TypeDefInfo.Enum;
// not as pretty, but remain compatible with oo7 for both struct and Array types
value.sub = Array.isArray(details)
? details.map((name): TypeDef => ({
info: TypeDefInfo.Plain,
name,
type: 'Null'
}))
: Object.entries(details).map(([name, type]): TypeDef =>
// eslint-disable-next-line @typescript-eslint/no-use-before-define
getTypeDef(type || 'Null', name)
);
return value;
}
// decode a set of the form
// { _set: { A: 0b0001, B: 0b0010, C: 0b0100 } }
function _decodeSet (value: TypeDef, details: Record<string, number>): TypeDef {
value.info = TypeDefInfo.Set;
value.sub = Object.entries(details).map(([name, index]): TypeDef => ({
index,
info: TypeDefInfo.Plain,
name,
type: name
}));
return value;
}
// decode a struct, set or enum
// eslint-disable-next-line @typescript-eslint/no-unused-vars
function _decodeStruct (value: TypeDef, type: string, subType: string): TypeDef {
const parsed = JSON.parse(type);
const keys = Object.keys(parsed);
if (keys.length === 1 && keys[0] === '_enum') {
return _decodeEnum(value, parsed[keys[0]]);
} else if (keys.length === 1 && keys[0] === '_set') {
return _decodeSet(value, parsed[keys[0]]);
}
value.info = TypeDefInfo.Struct;
value.sub = keys.map((name): TypeDef =>
// eslint-disable-next-line @typescript-eslint/no-use-before-define
getTypeDef(parsed[name], name)
);
return value;
}
// decode a fixed vector, e.g. [u8;32]
// eslint-disable-next-line @typescript-eslint/no-unused-vars
function _decodeFixedVec (value: TypeDef, type: string, subType: string): TypeDef {
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, `${type}: Only support for [Type; <length>], where length <= 256`);
value.info = TypeDefInfo.VecFixed;
value.ext = { length: vecLen, type: vecType } as unknown as TypeDefExtVecFixed;
return value;
}
// decode a tuple
function _decodeTuple (value: TypeDef, type: string, subType: string): TypeDef {
value.info = TypeDefInfo.Tuple;
value.sub = typeSplit(subType).map((inner): TypeDef =>
// eslint-disable-next-line @typescript-eslint/no-use-before-define
getTypeDef(inner)
);
return value;
}
function hasWrapper (type: string, [start, end]: [string, string, any]): boolean {
if (type.substr(0, start.length) !== start) {
return false;
}
assert(type[type.length - 1] === end, `Expected '${start}' closing with '${end}'`);
return true;
}
const nestedExtraction: [string, string, (value: TypeDef, type: string, subType: string) => TypeDef][] = [
['[', ']', _decodeFixedVec],
['{', '}', _decodeStruct],
['(', ')', _decodeTuple]
];
const wrappedExtraction: [string, string, TypeDefInfo][] = [
['Compact<', '>', TypeDefInfo.Compact],
['Option<', '>', TypeDefInfo.Option],
['Vec<', '>', TypeDefInfo.Vec],
['Linkage<', '>', TypeDefInfo.Linkage],
['DoubleMap<', '>', TypeDefInfo.DoubleMap]
];
function extractSubType (type: string, [start, end]: [string, string, any]): string {
return type.substr(start.length, type.length - start.length - end.length);
}
export function getTypeDef (_type: string, name?: string): TypeDef {
const type = _type.toString().trim();
const value: TypeDef = { info: TypeDefInfo.Plain, name, type };
const nested = nestedExtraction.find((nested): boolean =>
hasWrapper(type, nested)
);
if (nested) {
return nested[2](value, type, extractSubType(type, nested));
}
const wrapped = wrappedExtraction.find((wrapped): boolean =>
hasWrapper(type, wrapped)
);
if (wrapped) {
value.info = wrapped[2];
value.sub = getTypeDef(extractSubType(type, wrapped));
}
return value;
}
+14
View File
@@ -0,0 +1,14 @@
// 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 { createType } from './createType';
export * from './createClass';
export * from './createType';
export * from './getTypeClass';
export * from './getTypeDef';
export * from './registry';
export * from './typeSplit';
export default createType;
@@ -2,14 +2,14 @@
// This software may be modified and distributed under the terms
// of the Apache-2.0 license. See the LICENSE file for details.
import '../injector';
import '../../injector';
import { Constructor } from '../types';
import { Constructor } from '../../types';
import { TypeRegistry } from './typeRegistry';
import Struct from '../codec/Struct';
import Text from '../primitive/Text';
import U32 from '../primitive/U32';
import { TypeRegistry } from './registry';
import Struct from '../Struct';
import Text from '../../primitive/Text';
import U32 from '../../primitive/U32';
// Copied from interfacesTs
function isChildClass (Parent: Constructor<any>, Child?: Constructor<any>): boolean {
@@ -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, RegistryTypes } from '../types';
import { Codec, Constructor, RegistryTypes } from '../../types';
import { isFunction, isString, isUndefined } from '@polkadot/util';
import { createClass } from './createType';
import { createClass } from './createClass';
export class TypeRegistry {
public static readonly defaultRegistry: TypeRegistry = new TypeRegistry();
@@ -99,6 +99,6 @@ export class TypeRegistry {
}
}
export default function getDefaultRegistry (): TypeRegistry {
export function getTypeRegistry (): TypeRegistry {
return TypeRegistry.defaultRegistry;
}
@@ -0,0 +1,51 @@
// 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 '../../injector';
import { typeSplit } from '.';
describe('typeSplit', (): void => {
it('splits simple types into an array', (): void => {
expect(
typeSplit('Text, u32, u64')
).toEqual(['Text', 'u32', 'u64']);
});
it('splits nested combinations', (): void => {
expect(
typeSplit('Text, (u32), Vec<u64>')
).toEqual(['Text', '(u32)', 'Vec<u64>']);
});
it('keeps nested tuples together', (): void => {
expect(
typeSplit('Text, (u32, u128), Vec<u64>')
).toEqual(['Text', '(u32, u128)', 'Vec<u64>']);
});
it('keeps nested vector tuples together', (): void => {
expect(
typeSplit('Text, (u32, u128), Vec<(u64, u32)>')
).toEqual(['Text', '(u32, u128)', 'Vec<(u64, u32)>']);
});
it('allows for deep nesting', (): void => {
expect(
typeSplit('Text, (u32, (u128, u8)), Vec<(u64, (u32, u32))>')
).toEqual(['Text', '(u32, (u128, u8))', 'Vec<(u64, (u32, u32))>']);
});
it('checks for unclosed vec', (): void => {
expect(
(): string[] => typeSplit('Text, Vec<u64')
).toThrow(/Invalid defintion/);
});
it('checks for unclosed tuple', (): void => {
expect(
(): string[] => typeSplit('Text, (u64, u32')
).toThrow(/Invalid defintion/);
});
});
@@ -0,0 +1,51 @@
// 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 { assert } from '@polkadot/util';
function isNotNested (...counters: number[]): boolean {
return !counters.some((counter): boolean => counter !== 0);
}
// safely split a string on ', ' while taking care of any nested occurences
export function typeSplit (type: string): string[] {
let [cDepth, fDepth, sDepth, tDepth, start] = [0, 0, 0, 0, 0];
const result = [];
const extract = (index: number): void => {
if (isNotNested(cDepth, fDepth, sDepth, tDepth)) {
result.push(type.substr(start, index - start).trim());
start = index + 1;
}
};
for (let index = 0; index < type.length; index++) {
switch (type[index]) {
// if we are not nested, add the type
case ',': extract(index); break;
// adjust compact/vec (and friends) depth
case '<': cDepth++; break;
case '>': cDepth--; break;
// adjust fixed vec depths
case '[': fDepth++; break;
case ']': fDepth--; break;
// adjust struct depth
case '{': sDepth++; break;
case '}': sDepth--; break;
// adjust tuple depth
case '(': tDepth++; break;
case ')': tDepth--; break;
}
}
assert(isNotNested(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());
return result;
}
+42
View File
@@ -0,0 +1,42 @@
// 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, InterfaceTypes } from '../../types';
import { InterfaceRegistry } from '../../interfaceRegistry';
// Type which says: if `K` is in the InterfaceRegistry, then return InterfaceRegistry[K], else fallback to T
export type FromReg<T extends Codec, K extends string> = K extends InterfaceTypes
? InterfaceRegistry[K]
: T;
export enum TypeDefInfo {
Compact,
DoubleMap,
Enum,
Linkage,
Option,
Plain,
Set,
Struct,
Tuple,
Vec,
VecFixed,
// anything not fully supported (keep this as the last entry)
Null
}
export interface TypeDefExtVecFixed {
length: number;
type: string;
}
export interface TypeDef {
info: TypeDefInfo;
index?: number;
ext?: TypeDefExtVecFixed; // add additional here as required
name?: string;
type: string;
sub?: TypeDef | TypeDef[];
}
-353
View File
@@ -1,353 +0,0 @@
// 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 '../injector';
import { Codec, Constructor } from '../types';
import { TypeDef, TypeDefInfo } from './types';
import createType, { createClass, createTypeUnsafe, getTypeClass, getTypeDef, typeSplit, ClassOf } from './createType';
import getTypeRegistry from './typeRegistry';
import CodecSet from './Set';
describe('typeSplit', (): void => {
it('splits simple types into an array', (): void => {
expect(
typeSplit('Text, u32, u64')
).toEqual(['Text', 'u32', 'u64']);
});
it('splits nested combinations', (): void => {
expect(
typeSplit('Text, (u32), Vec<u64>')
).toEqual(['Text', '(u32)', 'Vec<u64>']);
});
it('keeps nested tuples together', (): void => {
expect(
typeSplit('Text, (u32, u128), Vec<u64>')
).toEqual(['Text', '(u32, u128)', 'Vec<u64>']);
});
it('keeps nested vector tuples together', (): void => {
expect(
typeSplit('Text, (u32, u128), Vec<(u64, u32)>')
).toEqual(['Text', '(u32, u128)', 'Vec<(u64, u32)>']);
});
it('allows for deep nesting', (): void => {
expect(
typeSplit('Text, (u32, (u128, u8)), Vec<(u64, (u32, u32))>')
).toEqual(['Text', '(u32, (u128, u8))', 'Vec<(u64, (u32, u32))>']);
});
it('checks for unclosed vec', (): void => {
expect(
(): string[] => typeSplit('Text, Vec<u64')
).toThrow(/Invalid defintion/);
});
it('checks for unclosed tuple', (): void => {
expect(
(): string[] => typeSplit('Text, (u64, u32')
).toThrow(/Invalid defintion/);
});
});
describe('getTypeValue', (): void => {
it('does not allow invalid tuples, end )', (): void => {
expect(
(): TypeDef => getTypeDef('(u64, u32')
).toThrow(/Expected '\(' closing with '\)'/);
});
it('does not allow invalid vectors, end >', (): void => {
expect(
(): TypeDef => getTypeDef('Vec<u64')
).toThrow(/Expected 'Vec<' closing with '>'/);
});
it('returns a type structure', (): void => {
expect(
getTypeDef('(u32, Compact<u32>, Vec<u64>, Option<u128>, DoubleMap<u128>, (Text, Vec<(Bool, u128)>))')
).toEqual({
info: TypeDefInfo.Tuple,
type: '(u32, Compact<u32>, Vec<u64>, Option<u128>, DoubleMap<u128>, (Text, Vec<(Bool, u128)>))',
sub: [
{
info: TypeDefInfo.Plain,
type: 'u32'
},
{
info: TypeDefInfo.Compact,
type: 'Compact<u32>',
sub: {
info: TypeDefInfo.Plain,
type: 'u32'
}
},
{
info: TypeDefInfo.Vec,
type: 'Vec<u64>',
sub: {
info: TypeDefInfo.Plain,
type: 'u64'
}
},
{
info: TypeDefInfo.Option,
type: 'Option<u128>',
sub: {
info: TypeDefInfo.Plain,
type: 'u128'
}
},
{
info: TypeDefInfo.DoubleMap,
type: 'DoubleMap<u128>',
sub: {
info: TypeDefInfo.Plain,
type: 'u128'
}
},
{
info: TypeDefInfo.Tuple,
type: '(Text, Vec<(Bool, u128)>)',
sub: [
{
info: TypeDefInfo.Plain,
type: 'Text'
},
{
info: TypeDefInfo.Vec,
type: 'Vec<(Bool, u128)>',
sub: {
info: TypeDefInfo.Tuple,
type: '(Bool, u128)',
sub: [
{
info: TypeDefInfo.Plain,
type: 'Bool'
},
{
info: TypeDefInfo.Plain,
type: 'u128'
}
]
}
}
]
}
]
});
});
it('returns a type structure (actual)', (): void => {
expect(
getTypeDef('Vec<(PropIndex, Proposal, AccountId)>')
).toEqual({
info: TypeDefInfo.Vec,
type: 'Vec<(PropIndex, Proposal, AccountId)>',
sub: {
info: TypeDefInfo.Tuple,
type: '(PropIndex, Proposal, AccountId)',
sub: [
{
info: TypeDefInfo.Plain,
type: 'PropIndex'
},
{
info: TypeDefInfo.Plain,
type: 'Proposal'
},
{
info: TypeDefInfo.Plain,
type: 'AccountId'
}
]
}
});
});
it('returns an actual Struct', (): void => {
expect(
getTypeDef('{"balance":"Balance","account_id":"AccountId","log":"(u64, Signature)"}')
).toEqual({
info: TypeDefInfo.Struct,
type: '{"balance":"Balance","account_id":"AccountId","log":"(u64, Signature)"}',
sub: [
{
info: TypeDefInfo.Plain,
name: 'balance',
type: 'Balance'
},
{
info: TypeDefInfo.Plain,
name: 'account_id',
type: 'AccountId'
},
{
info: TypeDefInfo.Tuple,
name: 'log',
type: '(u64, Signature)',
sub: [
{
info: TypeDefInfo.Plain,
type: 'u64'
},
{
info: TypeDefInfo.Plain,
type: 'Signature'
}
]
}
]
});
});
});
describe('getTypeClass', (): void => {
it('does not allow invalid types', (): void => {
expect(
(): Constructor<Codec> => getTypeClass('SomethingInvalid' as any)
).toThrow(/determine type/);
});
});
describe('createClass', (): void => {
it('should memoize from strings', (): void => {
const a = createClass('BabeWeight');
const b = createClass('BabeWeight');
expect(a).toBe(b);
});
it('should return equivalents for Bytes & Vec<u8>', (): void => {
const A = createClass('Vec<u8>');
const B = createClass('Bytes');
expect(new A() instanceof B).toBe(true);
});
});
describe('createType', (): void => {
it('allows creation of a Struct', (): void => {
expect(
createTypeUnsafe('{"balance":"Balance","index":"u32"}', [{
balance: 1234,
index: '0x10'
}]).toJSON()
).toEqual({
balance: 1234,
index: 16
});
});
it('allows creation of a Tuple', (): void => {
expect(
createTypeUnsafe('(Balance,u32)', [[1234, 5678]]).toJSON()
).toEqual([1234, 5678]);
});
it('allows creation of a Enum (simple)', (): void => {
expect(
createTypeUnsafe('{"_enum": ["A", "B", "C"]}', [1]).toJSON()
).toEqual({ B: null });
});
it('allows creation of a Enum (parametrised)', (): void => {
expect(
createTypeUnsafe('{"_enum": {"A": null, "B": "u32", "C": null} }', [1]).toJSON()
).toEqual({ B: 0 });
});
it('allows creation of a Set', (): void => {
expect(
createTypeUnsafe<CodecSet>('{"_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 [u8; 8]', (): void => {
expect(
createTypeUnsafe('[u8; 8]', [[0x12, 0x00, 0x23, 0x00, 0x45, 0x00, 0x67, 0x00]]).toHex()
).toEqual('0x1200230045006700');
});
it('allows creation of a [u16; 4]', (): void => {
expect(
createTypeUnsafe('[u16; 4]', [[0x1200, 0x2300, 0x4500, 0x6700]]).toU8a()
).toEqual(new Uint8Array([0x00, 0x12, 0x00, 0x23, 0x00, 0x45, 0x00, 0x67]));
});
describe('instanceof', (): void => {
it('instanceof should work (primitive type)', (): void => {
const value = createType('Balance', 1234);
expect(value instanceof ClassOf('Balance')).toBe(true);
});
it('instanceof should work (srml type)', (): void => {
const value = createType('Gas', 1234);
const Gas = ClassOf('Gas');
expect(value instanceof Gas).toBe(true);
});
it('instanceof should work (complex type)', (): void => {
getTypeRegistry().register({
TestComplex: {
balance: 'Balance',
accountId: 'AccountId',
log: '(u64, u32)',
fromSrml: 'Gas'
}
});
const value = createTypeUnsafe('TestComplex', [{
balance: 123,
accountId: '0x1234567812345678123456781234567812345678123456781234567812345678',
log: [456, 789],
fromSrml: 0
}]);
expect(value instanceof createClass('TestComplex')).toBe(true);
});
it('allows for re-registration of a type', (): void => {
const balDef = createType('Balance');
expect(balDef instanceof ClassOf('Balance'));
expect(balDef.bitLength()).toEqual(128);
getTypeRegistry().register({ Balance: 'u32' });
const balu32 = createType('Balance');
expect(balu32 instanceof ClassOf('Balance'));
expect(balu32.bitLength()).toEqual(32);
});
it('allows for re-registration of a type (affecting derives)', (): void => {
getTypeRegistry().register({
Balance: 'u128',
TestComplex: {
balance: 'Balance',
accountId: 'AccountId',
log: '(u64, u32)',
fromSrml: 'Gas'
}
});
const cmpDef: any = createTypeUnsafe('TestComplex');
expect(cmpDef.balance.bitLength()).toEqual(128);
getTypeRegistry().register({ Balance: 'u32' });
const cmpu32: any = createTypeUnsafe('TestComplex');
expect(cmpu32.balance.bitLength()).toEqual(32);
});
});
});
-359
View File
@@ -1,359 +0,0 @@
// 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 { TypeDef, TypeDefInfo, TypeDefExtVecFixed } from './types';
import { assert } from '@polkadot/util';
import { InterfaceRegistry } from '../interfaceRegistry';
import Compact from './Compact';
import Enum from './Enum';
import Linkage from './Linkage';
import Option from './Option';
import CodecSet from './Set';
import Struct from './Struct';
import Tuple from './Tuple';
import U8aFixed, { BitLength as U8aFixedBitLength } from './U8aFixed';
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<T extends Codec, K extends string> = K extends InterfaceTypes ? InterfaceRegistry[K] : T
// safely split a string on ', ' while taking care of any nested occurences
export function typeSplit (type: string): string[] {
let [cDepth, fDepth, sDepth, tDepth, start] = [0, 0, 0, 0, 0];
const result = [];
for (let index = 0; index < type.length; index++) {
switch (type[index]) {
case ',':
// we are not nested, add the type
if (cDepth === 0 && fDepth === 0 && sDepth === 0 && tDepth === 0) {
result.push(type.substr(start, index - start).trim());
start = index + 1;
}
break;
// adjust compact/vec (and friends) depth
case '<': cDepth++; break;
case '>': cDepth--; break;
// adjust fixed vec depths
case '[': fDepth++; break;
case ']': fDepth--; break;
// adjust struct depth
case '{': sDepth++; break;
case '}': sDepth--; break;
// adjust tuple depth
case '(': tDepth++; break;
case ')': tDepth--; break;
}
}
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());
return result;
}
// decode an enum of either of the following forms
// { _enum: ['A', 'B', 'C'] }
// { _enum: { A: AccountId, B: Balance, C: u32 } }
function _decodeEnum (value: TypeDef, details: string[] | Record<string, string>): TypeDef {
value.info = TypeDefInfo.Enum;
// not as pretty, but remain compatible with oo7 for both struct and Array types
value.sub = Array.isArray(details)
? details.map((name): TypeDef => ({
info: TypeDefInfo.Plain,
name,
type: 'Null'
}))
: Object.entries(details).map(([name, type]): TypeDef =>
// eslint-disable-next-line @typescript-eslint/no-use-before-define
getTypeDef(type || 'Null', name)
);
return value;
}
// decode a set of the form
// { _set: { A: 0b0001, B: 0b0010, C: 0b0100 } }
function _decodeSet (value: TypeDef, details: Record<string, number>): TypeDef {
value.info = TypeDefInfo.Set;
value.sub = Object.entries(details).map(([name, index]): TypeDef => ({
index,
info: TypeDefInfo.Plain,
name,
type: name
}));
return value;
}
// decode a struct, set or enum
function _decodeStruct (type: string, value: TypeDef): TypeDef {
const parsed = JSON.parse(type);
const keys = Object.keys(parsed);
if (keys.length === 1 && keys[0] === '_enum') {
return _decodeEnum(value, parsed[keys[0]]);
} else if (keys.length === 1 && keys[0] === '_set') {
return _decodeSet(value, parsed[keys[0]]);
}
value.info = TypeDefInfo.Struct;
value.sub = keys.map((name): TypeDef =>
// eslint-disable-next-line @typescript-eslint/no-use-before-define
getTypeDef(parsed[name], name)
);
return value;
}
// decode a fixed vector, e.g. [u8;32]
function _decodeFixedVec (type: string, value: TypeDef): TypeDef {
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, `${type}: Only support for [Type; <length>], where length <= 256`);
value.info = TypeDefInfo.VecFixed;
value.ext = { length: vecLen, type: vecType } as unknown as TypeDefExtVecFixed;
return value;
}
// decode a tuple
function _decodeTuple (subType: string, value: TypeDef): TypeDef {
value.info = TypeDefInfo.Tuple;
value.sub = typeSplit(subType).map((inner): TypeDef =>
// eslint-disable-next-line @typescript-eslint/no-use-before-define
getTypeDef(inner)
);
return value;
}
const simpleExtraction: [string, string, TypeDefInfo][] = [
['Compact<', '>', TypeDefInfo.Compact],
['Option<', '>', TypeDefInfo.Option],
['Vec<', '>', TypeDefInfo.Vec],
['Linkage<', '>', TypeDefInfo.Linkage],
['DoubleMap<', '>', TypeDefInfo.DoubleMap]
];
export function getTypeDef (_type: string, name?: string): TypeDef {
const type = _type.toString().trim();
const value: TypeDef = { info: TypeDefInfo.Plain, name, type };
let subType = '';
const _hasWrapper = (type: string, start: string, end: string): boolean => {
if (type.substr(0, start.length) !== start) {
return false;
}
assert(type[type.length - 1] === end, `Expected '${start}' closing with '${end}'`);
subType = type.substr(start.length, type.length - start.length - 1);
return true;
};
if (_hasWrapper(type, '[', ']')) {
return _decodeFixedVec(type, value);
} else if (_hasWrapper(type, '{', '}')) {
return _decodeStruct(type, value);
} else if (_hasWrapper(type, '(', ')')) {
return _decodeTuple(subType, value);
}
const extracted = simpleExtraction.find(([start, end]): boolean => _hasWrapper(type, start, end));
if (extracted) {
value.info = extracted[2];
value.sub = getTypeDef(subType);
}
return value;
}
export function createClass<T extends Codec = Codec, K extends string = string> (
type: K
): Constructor<FromReg<T, K>> {
// eslint-disable-next-line @typescript-eslint/no-use-before-define
return getTypeClass<FromReg<T, K>>(getTypeDef(type));
}
// An unsafe version of the `createType` below. It's unsafe because the `type`
// argument here can be any string, which, if not parseable, will yield a
// runtime error.
export function ClassOfUnsafe<T extends Codec = Codec, K extends string = string> (name: K): Constructor<FromReg<T, K>> {
return createClass<T, K>(name);
}
// alias for createClass
export function ClassOf<K extends InterfaceTypes> (name: K): Constructor<InterfaceRegistry[K]> {
return ClassOfUnsafe<Codec, K>(name) as Constructor<InterfaceRegistry[K]>;
}
// create a maps of type string constructors from the input
export function getTypeClassMap (defs: TypeDef[]): Record<string, InterfaceTypes> {
return defs.reduce((result, sub): Record<string, InterfaceTypes> => {
result[sub.name as string] = sub.type as any;
return result;
}, {} as unknown as Record<string, InterfaceTypes>);
}
// 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
export function getTypeClass<T extends Codec = Codec> (value: TypeDef): Constructor<T> {
const Type = getTypeRegistry().get<T>(value.type);
if (Type) {
return Type;
}
switch (value.info) {
case TypeDefInfo.Compact:
assert(value.sub && !Array.isArray(value.sub), 'Expected subtype for Compact');
return Compact.with(
(value.sub as TypeDef).type as any
) as unknown as Constructor<T>;
case TypeDefInfo.Enum:
assert(value.sub && Array.isArray(value.sub), 'Expected subtype for Enum');
return Enum.with(
getTypeClassMap(value.sub as TypeDef[]) as any
) as unknown as Constructor<T>;
case TypeDefInfo.Option:
assert(value.sub && !Array.isArray(value.sub), 'Expected subtype for Option');
return Option.with(
(value.sub as TypeDef).type as any
) as unknown as Constructor<T>;
case TypeDefInfo.Set:
assert(Array.isArray(value.sub), 'Expected nested info for Set');
return CodecSet.with(
(value.sub as TypeDef[]).reduce((result, { name, index }): Record<string, number> => {
result[name as string] = index as number;
return result;
}, {} as unknown as Record<string, number>)
) as unknown as Constructor<T>;
case TypeDefInfo.Struct:
assert(Array.isArray(value.sub), 'Expected nested subtypes for Struct');
return Struct.with(
getTypeClassMap(value.sub as TypeDef[])
) as unknown as Constructor<T>;
case TypeDefInfo.Tuple:
assert(Array.isArray(value.sub), 'Expected nested subtypes for Tuple');
return Tuple.with(
getTypeClassArray(value.sub as TypeDef[])
) as unknown as Constructor<T>;
case TypeDefInfo.Vec:
assert(value.sub && !Array.isArray(value.sub), 'Expected subtype for Vec');
const vsub = value.sub as TypeDef;
return (
vsub.type === 'u8'
? ClassOf('Bytes')
: Vec.with(vsub.type as any)
) as unknown as Constructor<T>;
case TypeDefInfo.VecFixed:
assert(value.ext, 'Expected length & type information for fixed vector');
const ext = value.ext as TypeDefExtVecFixed;
return (
ext.type === 'u8'
? U8aFixed.with((ext.length * 8) as U8aFixedBitLength)
: VecFixed.with(ext.type as any, ext.length)
) as unknown as Constructor<T>;
case TypeDefInfo.Linkage:
assert(value.sub && !Array.isArray(value.sub), 'Expected subtype for Linkage');
return Linkage.withKey(
(value.sub as TypeDef).type as any
) as unknown as Constructor<T>;
case TypeDefInfo.DoubleMap:
assert(value.sub && !Array.isArray(value.sub), 'Expected subtype for DoubleMap');
return getTypeClass(value.sub as TypeDef);
case TypeDefInfo.Null:
return ClassOf('Null') as unknown as Constructor<T>;
}
throw new Error(`Unable to determine type from ${JSON.stringify(value)}`);
}
// Initializes a type with a value. This also checks for fallbacks and in the cases
// where isPedantic is specified (storage decoding), also check the format/structure
function initType<T extends Codec = Codec, K extends string = string> (Type: Constructor<FromReg<T, K>>, params: any[] = [], isPedantic?: boolean): FromReg<T, K> {
const created = new Type(...params);
const [value] = params;
// With isPedantic, actually check that the encoding matches that supplied. This
// is much slower, but verifies that we have the correct types defined
if (isPedantic && value && value.toU8a && !value.isEmpty) {
const inHex = value.toHex(true);
const crHex = created.toHex(true);
const hasMatch = inHex === crHex || (
created instanceof Uint8Array
// strip the input length
? (value.toU8a(true).toString() === created.toU8a().toString())
// compare raw. without additions
: (value.toU8a(true).toString() === created.toU8a(true).toString())
);
if (!hasMatch) {
console.warn(`${created.toRawType()}:: Input doesn't match output, received ${inHex}, created ${crHex}`);
}
}
return created;
}
// An unsafe version of the `createType` below. It's unsafe because the `type`
// argument here can be any string, which, if not parseable, will yield a
// runtime error.
export function createTypeUnsafe<T extends Codec = Codec, K extends string = string> (type: K, params: any[] = [], isPedantic?: boolean): FromReg<T, K> {
try {
return initType(createClass<T, K>(type), params, isPedantic);
} catch (error) {
throw new Error(`createType(${type}):: ${error.message}`);
}
}
/**
* Create an instance of a `type` with a given `params`.
* @param type - A recognizable string representing the type to create an
* instance from
* @param params - The value to instantiate the type with
*/
export default function createType<K extends InterfaceTypes> (type: K, ...params: any[]): InterfaceRegistry[K] {
return createTypeUnsafe<Codec, K>(type, params) as InterfaceRegistry[K];
}
+1 -2
View File
@@ -24,5 +24,4 @@ export { default as U8a } from './U8a';
export { default as UInt } from './UInt';
// Type management helper functions
export { default as createType, createClass, getTypeClass, getTypeDef, ClassOf } from './createType';
export { default as getTypeRegistry } from './typeRegistry';
export * from './create';
+1 -29
View File
@@ -2,32 +2,4 @@
// This software may be modified and distributed under the terms
// of the Apache-2.0 license. See the LICENSE file for details.
export enum TypeDefInfo {
Compact,
DoubleMap,
Enum,
Linkage,
Option,
Plain,
Set,
Struct,
Tuple,
Vec,
VecFixed,
// anything not fully supported (keep this as the last entry)
Null
}
export interface TypeDefExtVecFixed {
length: number;
type: string;
}
export interface TypeDef {
info: TypeDefInfo;
index?: number;
ext?: TypeDefExtVecFixed; // add additional here as required
name?: string;
type: string;
sub?: TypeDef | TypeDef[];
}
export * from './create/types';
@@ -6,7 +6,7 @@ import { Codec, Constructor, InterfaceTypes } from '../../types';
import { isString } from '@polkadot/util';
import { ClassOf } from '../createType';
import { ClassOf } from '../create';
export function typeToConstructor <T = Codec> (type: InterfaceTypes | Constructor<T>): Constructor<T> {
return (
+1 -1
View File
@@ -6,7 +6,7 @@ import './injector';
import extrinsics from '@polkadot/api-metadata/extrinsics/static';
import { createTypeUnsafe } from './codec/createType';
import { createTypeUnsafe } from './codec/create';
import GenericCall from './primitive/Generic/Call';
import { Codec } from './types';
import * as exported from './index.types';
+1 -1
View File
@@ -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 getTypeRegistry from './codec/typeRegistry';
import { getTypeRegistry } from './codec/create';
import * as definitions from './interfaces/definitions';
import * as baseTypes from './index.types';
@@ -4,7 +4,7 @@
import '../../injector';
import createType from '../../codec/createType';
import { createType } from '../../codec/create';
describe('ChainProperties', (): void => {
it('decodes from a null value', (): void => {
@@ -6,7 +6,7 @@ import '../../injector';
import { ExtrinsicStatus } from './types';
import createType from '../../codec/createType';
import { createType } from '../../codec/create';
import rpc from '../../json/ExtrinsicStatus.001.json';
describe('ExtrinsicStatus', (): void => {
@@ -4,7 +4,7 @@
import '../../injector';
import createType from '../../codec/createType';
import { createType } from '../../codec/create';
describe('KeyValueOption', (): void => {
it('exposes the properties for key/value', (): void => {
@@ -6,7 +6,7 @@ import '../../injector';
import { RuntimeVersion } from './types';
import createType from '../../codec/createType';
import { createType } from '../../codec/create';
import rpc from '../../json/RuntimeVersion.002.json';
describe('RuntimeVersion', (): void => {
@@ -6,7 +6,7 @@ import '../../injector';
import BN from 'bn.js';
import createType from '../../codec/createType';
import { createType } from '../../codec/create';
import json1 from '../../json/Header.001.json';
import json2 from '../../json/Header.002.json';
import json3 from '../../json/Header.003.json';
@@ -4,7 +4,7 @@
import '../../injector';
import createType from '../../codec/createType';
import { createType } from '../../codec/create';
describe('KeyValue', (): void => {
it('decodes KeyValue from u8a', (): void => {
@@ -6,7 +6,7 @@ import '../../injector';
import extrinsics from '@polkadot/api-metadata/extrinsics/static';
import createType from '../../codec/createType';
import { createType } from '../../codec/create';
import Call from '../../primitive/Generic/Call';
import events from '../../json/SignedBlock.002.json';
import immortalTxs from '../../json/SignedBlock.004.immortal.json';
@@ -6,7 +6,7 @@ import '../../injector';
import { EventRecord } from './types';
import createType from '../../codec/createType';
import { createType } from '../../codec/create';
import Vec from '../../codec/Vec';
import json1 from '../../json/EventRecord.001.json';
import json3 from '../../json/EventRecord.003.json';
+1 -1
View File
@@ -7,7 +7,7 @@ import { Codec, IHash } from '../types';
import { isU8a, u8aToHex } from '@polkadot/util';
import { blake2AsU8a } from '@polkadot/util-crypto';
import createType from '../codec/createType';
import { createType } from '../codec/create';
/**
* @name Bool
+1 -1
View File
@@ -4,7 +4,7 @@
import '../injector';
import createType from '../codec/createType';
import { createType } from '../codec/create';
import Bytes from './Bytes';
describe('Bytes', (): void => {
+1 -1
View File
@@ -6,7 +6,7 @@ import { AnyU8a } from '../types';
import { assert, isString, isU8a, u8aToU8a } from '@polkadot/util';
import { ClassOf } from '../codec/createType';
import { ClassOf } from '../codec/create';
import Compact from '../codec/Compact';
import U8a from '../codec/U8a';
@@ -7,7 +7,7 @@ import { ExtrinsicPayloadValue, IKeyringPair } from '../../types';
import { u8aToHex } from '@polkadot/util';
import createType from '../../codec/createType';
import { createType } from '../../codec/create';
import Base from '../../codec/Base';
import Compact from '../../codec/Compact';
import U8a from '../../codec/U8a';
@@ -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 from '../../../codec/createType';
import { createType } from '../../../codec/create';
import Compact from '../../../codec/Compact';
import Struct from '../../../codec/Struct';
import Call from '../../Generic/Call';
@@ -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 from '../../../codec/createType';
import { createType } from '../../../codec/create';
import Compact from '../../../codec/Compact';
import Struct from '../../../codec/Struct';
import Call from '../../Generic/Call';
@@ -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 from '../../../codec/createType';
import { createType } from '../../../codec/create';
import Compact from '../../../codec/Compact';
import Struct from '../../../codec/Struct';
import Call from '../../Generic/Call';
@@ -6,7 +6,7 @@ import '../../injector';
import { setAddressPrefix } from '@polkadot/util-crypto';
import createType from '../../codec/createType';
import { createType } from '../../codec/create';
import U8a from '../../codec/U8a';
import jsonVec from '../../json/AccountIdVec.001.json';
import AccountId from './AccountId';
@@ -6,7 +6,7 @@ import '../../injector';
import BN from 'bn.js';
import createType from '../../codec/createType';
import { createType } from '../../codec/create';
import AccountIndex from './AccountIndex';
describe('AccountIndex', (): void => {
@@ -8,7 +8,7 @@ import BN from 'bn.js';
import { bnToBn, isBn, isNumber, isU8a, isHex } from '@polkadot/util';
import { decodeAddress, encodeAddress } from '@polkadot/util-crypto';
import createType from '../../codec/createType';
import { createType } from '../../codec/create';
import U32 from '../U32';
export const ENUMSET_SIZE = new BN(64);
@@ -4,7 +4,7 @@
import '../../injector';
import createType from '../../codec/createType';
import { createType } from '../../codec/create';
import AccountId from './AccountId';
import AccountIndex from './AccountIndex';
import Address from './Address';
@@ -6,7 +6,7 @@ import BN from 'bn.js';
import { isBn, isHex, isNumber, isU8a, u8aConcat, u8aToHex, u8aToU8a, u8aToBn } from '@polkadot/util';
import { decodeAddress } from '@polkadot/util-crypto';
import createType from '../../codec/createType';
import { createType } from '../../codec/create';
import Base from '../../codec/Base';
import AccountId from './AccountId';
import AccountIndex from './AccountIndex';
@@ -7,7 +7,7 @@ import { AnyNumber, AnyU8a } from '../../types';
import { blake2AsU8a } from '@polkadot/util-crypto';
import createType from '../../codec/createType';
import { createType } from '../../codec/create';
import Extrinsic from '../Extrinsic/Extrinsic';
import Struct from '../../codec/Struct';
import Vec from '../../codec/Vec';
+1 -1
View File
@@ -6,7 +6,7 @@ import { AnyU8a, ArgsDef, CallFunction, Codec, IMethod, ModulesWithCalls } from
import { assert, isHex, isObject, isU8a, hexToU8a } from '@polkadot/util';
import { getTypeDef, getTypeClass } from '../../codec/createType';
import { getTypeDef, getTypeClass } from '../../codec/create';
import Struct from '../../codec/Struct';
import U8aFixed from '../../codec/U8aFixed';
import { FunctionMetadata as FunctionMetadataV7, FunctionArgumentMetadata } from '../../Metadata/v7/Calls';
@@ -7,7 +7,7 @@ import { AccountId } from '../../interfaces/runtime';
import BN from 'bn.js';
import { bnToBn } from '@polkadot/util';
import createType from '../../codec/createType';
import { createType } from '../../codec/create';
import Bytes from '../Bytes';
import U32 from '../U32';
@@ -8,7 +8,7 @@ import { Constructor, Codec } from '../../types';
import { assert, isUndefined, stringCamelCase, u8aToHex } from '@polkadot/util';
import { getTypeClass, getTypeDef } from '../../codec/createType';
import { getTypeClass, getTypeDef } from '../../codec/create';
import Struct from '../../codec/Struct';
import Tuple from '../../codec/Tuple';
import Metadata from '../../Metadata';
+1 -1
View File
@@ -6,7 +6,7 @@ import { Conviction } from '../../interfaces/democracy';
import { isBoolean, isNumber, isObject, isU8a, isUndefined } from '@polkadot/util';
import createType from '../../codec/createType';
import { createType } from '../../codec/create';
import U8aFixed from '../../codec/U8aFixed';
import Bool from '../Bool';
+1 -1
View File
@@ -7,7 +7,7 @@ import { AnyU8a, Codec, IHash } from '../types';
import { assert, hexToU8a, isHex, isString, stringToU8a, u8aToString, u8aToHex } from '@polkadot/util';
import { blake2AsU8a } from '@polkadot/util-crypto';
import createType from '../codec/createType';
import { createType } from '../codec/create';
import Compact from '../codec/Compact';
/**
+1 -1
View File
@@ -9,7 +9,7 @@ import fs from 'fs';
import { isString, stringCamelCase, stringUpperFirst } from '@polkadot/util';
import interfaces from '../../../type-jsonrpc/src';
import { ClassOfUnsafe, getTypeDef } from '../codec/createType';
import { ClassOfUnsafe, getTypeDef } from '../codec/create';
import * as codecClasses from '../codec';
import AbstractInt from '../codec/AbstractInt';
import UInt from '../codec/UInt';