Allow construction of HashMap (#1955)
* Allow construction of HashMap * WIP... add Map to codec, extend BTreeMap & HashMap from it * Rework various map tests * registry.createClass * createClass in tests
This commit is contained in:
@@ -9,12 +9,12 @@ import { Observable, of } from 'rxjs';
|
||||
import { map, startWith } from 'rxjs/operators';
|
||||
import { ApiInterfaceRx } from '@polkadot/api/types';
|
||||
import { ENUMSET_SIZE } from '@polkadot/types/generic/AccountIndex';
|
||||
import { ClassOf, Option, Vec, createType } from '@polkadot/types';
|
||||
import { Option, Vec, createType } from '@polkadot/types';
|
||||
|
||||
import { memo } from '../util';
|
||||
|
||||
function queryEnumSet (api: ApiInterfaceRx, _accountIndex: AccountIndex | string): Observable<AccountId | undefined> {
|
||||
const accountIndex = _accountIndex instanceof ClassOf(api.registry, 'AccountIndex')
|
||||
const accountIndex = _accountIndex instanceof api.registry.createClass('AccountIndex')
|
||||
? _accountIndex
|
||||
: createType(api.registry, 'AccountIndex', _accountIndex);
|
||||
|
||||
|
||||
@@ -10,7 +10,6 @@ import { AddressOrPair, SignerOptions, SubmittableExtrinsic, SubmittablePaymentR
|
||||
|
||||
import { Observable, combineLatest, of } from 'rxjs';
|
||||
import { first, map, mapTo, mergeMap, switchMap, tap } from 'rxjs/operators';
|
||||
import { ClassOf } from '@polkadot/types';
|
||||
import { assert, isBn, isFunction, isNumber, isUndefined } from '@polkadot/util';
|
||||
|
||||
import { filterEvents, isKeyringPair } from '../util';
|
||||
@@ -32,7 +31,7 @@ const DEFAULT_MORTAL_LENGTH = 5 * ONE_MINUTE;
|
||||
|
||||
export default function createClass <ApiType extends ApiTypes> ({ api, apiType, decorateMethod }: SubmittableOptions<ApiType>): Constructor<SubmittableExtrinsic<ApiType>> {
|
||||
// an instance of the base extrinsic for us to extend
|
||||
const Extrinsic = ClassOf(api.registry, 'Extrinsic');
|
||||
const Extrinsic = api.registry.createClass('Extrinsic');
|
||||
|
||||
return class Submittable extends Extrinsic implements SubmittableExtrinsic<ApiType> {
|
||||
readonly #ignoreStatusCb: boolean;
|
||||
|
||||
@@ -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 { ClassOf, TypeRegistry } from '@polkadot/types/create';
|
||||
import { TypeRegistry } from '@polkadot/types/create';
|
||||
import { u8aToHex } from '@polkadot/util';
|
||||
|
||||
import Decorated from './Decorated';
|
||||
@@ -40,7 +40,7 @@ describe('Decorated', () => {
|
||||
});
|
||||
|
||||
it('should return constants with the correct type and value', (): void => {
|
||||
expect(decorated.consts.democracy.cooloffPeriod).toBeInstanceOf(ClassOf(registry, 'BlockNumber'));
|
||||
expect(decorated.consts.democracy.cooloffPeriod).toBeInstanceOf(registry.createClass('BlockNumber'));
|
||||
expect(decorated.consts.democracy.cooloffPeriod.toHex()).toEqual('0x000c4e00');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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 { ClassOf, Metadata, TypeRegistry } from '@polkadot/types';
|
||||
import { Metadata, TypeRegistry } from '@polkadot/types';
|
||||
|
||||
import rpcMetadata from '../../../Metadata/static';
|
||||
import fromMetadata from '../fromMetadata';
|
||||
@@ -14,7 +14,7 @@ const consts = fromMetadata(registry, metadata);
|
||||
|
||||
describe('fromMetadata', (): void => {
|
||||
it('should return constants with the correct type and value', (): void => {
|
||||
expect(consts.democracy.cooloffPeriod).toBeInstanceOf(ClassOf(registry, 'BlockNumber'));
|
||||
expect(consts.democracy.cooloffPeriod).toBeInstanceOf(registry.createClass('BlockNumber'));
|
||||
// 3 second blocks, 28 days
|
||||
expect(consts.democracy.cooloffPeriod.toNumber()).toEqual(28 * 24 * 60 * (60 / 3));
|
||||
});
|
||||
|
||||
@@ -38,6 +38,7 @@ function tsExport (definitions: object, def: TypeDef, imports: TypeImports): str
|
||||
const tsBTreeMap = tsExport;
|
||||
const tsBTreeSet = tsExport;
|
||||
const tsCompact = tsExport;
|
||||
const tsHashMap = tsExport;
|
||||
const tsOption = tsExport;
|
||||
const tsPlain = tsExport;
|
||||
const tsTuple = tsExport;
|
||||
@@ -156,6 +157,7 @@ function generateInterfaces (definitions: object, { types }: { types: Record<str
|
||||
[TypeDefInfo.BTreeSet]: tsBTreeSet,
|
||||
[TypeDefInfo.Compact]: tsCompact,
|
||||
[TypeDefInfo.Enum]: tsEnum,
|
||||
[TypeDefInfo.HashMap]: tsHashMap,
|
||||
[TypeDefInfo.Int]: tsInt,
|
||||
[TypeDefInfo.Linkage]: errorUnhandled,
|
||||
[TypeDefInfo.Null]: errorUnhandled,
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
import { TypeDef, TypeDefInfo } from '@polkadot/types/create/types';
|
||||
import { Constructor, Registry } from '@polkadot/types/types';
|
||||
|
||||
import { ClassOf, ClassOfUnsafe, getTypeDef } from '@polkadot/types/create';
|
||||
import { ClassOfUnsafe, getTypeDef } from '@polkadot/types/create';
|
||||
import AbstractInt from '@polkadot/types/codec/AbstractInt';
|
||||
import Compact from '@polkadot/types/codec/Compact';
|
||||
import Enum from '@polkadot/types/codec/Enum';
|
||||
@@ -118,7 +118,7 @@ export function getSimilarTypes (definitions: object, registry: Registry, _type:
|
||||
possibleTypes.push('Address', 'AccountId', 'AccountIndex', 'string', 'Uint8Array');
|
||||
} else if (isChildClass(GenericAccountId, Clazz)) {
|
||||
possibleTypes.push('string', 'Uint8Array');
|
||||
} else if (isChildClass(ClassOf(registry, 'bool'), Clazz)) {
|
||||
} else if (isChildClass(registry.createClass('bool'), Clazz)) {
|
||||
possibleTypes.push('boolean', 'Uint8Array');
|
||||
} else if (isChildClass(Null, Clazz)) {
|
||||
possibleTypes.push('null');
|
||||
|
||||
@@ -97,6 +97,14 @@ function formatCompact (inner: string): string {
|
||||
return paramsNotation('Compact', inner);
|
||||
}
|
||||
|
||||
/**
|
||||
* Given the inner `K` & `V`, return a `BTreeMap<K, V>` string
|
||||
*/
|
||||
/** @internal */
|
||||
function formatHashMap (key: string, val: string): string {
|
||||
return `HashMap<${key}, ${val}>`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Given the inner `O` & `E`, return a `Result<O, E>` string
|
||||
*/
|
||||
@@ -150,13 +158,16 @@ export function formatType (definitions: object, type: string | TypeDef, imports
|
||||
|
||||
setImports(definitions, imports, [typeDef.type]);
|
||||
|
||||
// FIXME Swap to Record<TypeDefInfo, fn> to check all types
|
||||
switch (typeDef.info) {
|
||||
case TypeDefInfo.Compact: {
|
||||
setImports(definitions, imports, ['Compact']);
|
||||
|
||||
return formatCompact(formatType(definitions, (typeDef.sub as TypeDef).type, imports));
|
||||
}
|
||||
case TypeDefInfo.Option: {
|
||||
setImports(definitions, imports, ['Option']);
|
||||
|
||||
return formatOption(formatType(definitions, (typeDef.sub as TypeDef).type, imports));
|
||||
}
|
||||
case TypeDefInfo.Plain: {
|
||||
@@ -164,6 +175,7 @@ export function formatType (definitions: object, type: string | TypeDef, imports
|
||||
}
|
||||
case TypeDefInfo.Vec: {
|
||||
setImports(definitions, imports, ['Vec']);
|
||||
|
||||
return formatVec(formatType(definitions, (typeDef.sub as TypeDef).type, imports));
|
||||
}
|
||||
case TypeDefInfo.Tuple: {
|
||||
@@ -190,21 +202,34 @@ export function formatType (definitions: object, type: string | TypeDef, imports
|
||||
}
|
||||
case TypeDefInfo.BTreeMap: {
|
||||
setImports(definitions, imports, ['BTreeMap']);
|
||||
|
||||
const [keyDef, valDef] = (typeDef.sub as TypeDef[]);
|
||||
|
||||
return formatBTreeMap(formatType(definitions, keyDef.type, imports), formatType(definitions, valDef.type, imports));
|
||||
}
|
||||
case TypeDefInfo.BTreeSet: {
|
||||
setImports(definitions, imports, ['BTreeSet']);
|
||||
|
||||
const valDef = typeDef.sub as TypeDef;
|
||||
|
||||
return formatBTreeSet(formatType(definitions, valDef.type, imports));
|
||||
}
|
||||
case TypeDefInfo.HashMap: {
|
||||
setImports(definitions, imports, ['HashMap']);
|
||||
|
||||
const [keyDef, valDef] = (typeDef.sub as TypeDef[]);
|
||||
|
||||
return formatHashMap(formatType(definitions, keyDef.type, imports), formatType(definitions, valDef.type, imports));
|
||||
}
|
||||
case TypeDefInfo.Result: {
|
||||
setImports(definitions, imports, ['Result']);
|
||||
|
||||
const [okDef, errorDef] = (typeDef.sub as TypeDef[]);
|
||||
|
||||
return formatResult(formatType(definitions, okDef.type, imports), formatType(definitions, errorDef.type, imports));
|
||||
}
|
||||
default: {
|
||||
throw new Error(`Cannot format ${type}.`);
|
||||
throw new Error(`Cannot format ${JSON.stringify(type)}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,72 +6,16 @@ import { TypeRegistry } from '../create';
|
||||
import Text from '../primitive/Text';
|
||||
import U32 from '../primitive/U32';
|
||||
import Struct from './Struct';
|
||||
import { CodecTo } from '../types';
|
||||
|
||||
import BTreeMap from './BTreeMap';
|
||||
|
||||
const registry = new TypeRegistry();
|
||||
const mockU32TextMap = new Map<Text, U32>();
|
||||
mockU32TextMap.set(new Text(registry, 'bazzing'), new U32(registry, 69));
|
||||
const mockU32TextMapString = '{"bazzing":69}';
|
||||
const mockU32TextMapObject = { bazzing: 69 };
|
||||
const mockU32TextMapHexString = '0x041c62617a7a696e6745000000';
|
||||
const mockU32TextMapUint8Array = Uint8Array.from([4, 28, 98, 97, 122, 122, 105, 110, 103, 69, 0, 0, 0]);
|
||||
|
||||
const mockU32U32Map = new Map<U32, U32>();
|
||||
mockU32U32Map.set(new U32(registry, 1), new U32(registry, 2));
|
||||
mockU32U32Map.set(new U32(registry, 23), new U32(registry, 24));
|
||||
mockU32U32Map.set(new U32(registry, 28), new U32(registry, 30));
|
||||
mockU32U32Map.set(new U32(registry, 45), new U32(registry, 80));
|
||||
const mockU32U32MapString = '{"1":2,"23":24,"28":30,"45":80}';
|
||||
const mockU32U32MapObject = { 1: 2, 23: 24, 28: 30, 45: 80 };
|
||||
const mockU32U32MapHexString = '0x10043102000000083233180000000832381e00000008343550000000';
|
||||
const mockU32U32MapUint8Array = Uint8Array.from([16, 4, 49, 2, 0, 0, 0, 8, 50, 51, 24, 0, 0, 0, 8, 50, 56, 30, 0, 0, 0, 8, 52, 53, 80, 0, 0, 0]);
|
||||
const mockU32TextMap = new Map<Text, U32>();
|
||||
|
||||
mockU32TextMap.set(new Text(registry, 'bazzing'), new U32(registry, 69));
|
||||
|
||||
describe('BTreeMap', (): void => {
|
||||
describe('decoding', (): void => {
|
||||
const testDecode = (type: string, input: any, output: string): void =>
|
||||
it(`can decode from ${type}`, (): void => {
|
||||
const s = new BTreeMap(registry, Text, U32, input);
|
||||
|
||||
expect(s.toString()).toBe(output);
|
||||
});
|
||||
|
||||
testDecode('map', mockU32TextMap, mockU32TextMapString);
|
||||
testDecode('hex', mockU32TextMapHexString, mockU32TextMapString);
|
||||
testDecode('Uint8Array', mockU32TextMapUint8Array, mockU32TextMapString);
|
||||
|
||||
testDecode('map', mockU32U32Map, mockU32U32MapString);
|
||||
testDecode('hex', mockU32U32MapHexString, mockU32U32MapString);
|
||||
testDecode('Uint8Array', mockU32U32MapUint8Array, mockU32U32MapString);
|
||||
});
|
||||
|
||||
describe('encoding', (): void => {
|
||||
const testEncode = (to: CodecTo, expected: any): void =>
|
||||
it(`can encode ${to}`, (): void => {
|
||||
const s = new BTreeMap(registry, Text, U32, mockU32TextMap);
|
||||
expect(s[to]()).toEqual(expected);
|
||||
});
|
||||
|
||||
testEncode('toHex', mockU32TextMapHexString);
|
||||
testEncode('toJSON', mockU32TextMapObject);
|
||||
testEncode('toU8a', mockU32TextMapUint8Array);
|
||||
testEncode('toString', mockU32TextMapString);
|
||||
});
|
||||
|
||||
describe('encoding muple values', (): void => {
|
||||
const testEncode = (to: CodecTo, expected: any): void =>
|
||||
it(`can encode ${to}`, (): void => {
|
||||
const s = new BTreeMap(registry, Text, U32, mockU32U32Map);
|
||||
expect(s[to]()).toEqual(expected);
|
||||
});
|
||||
|
||||
testEncode('toHex', mockU32U32MapHexString);
|
||||
testEncode('toJSON', mockU32U32MapObject);
|
||||
testEncode('toU8a', mockU32U32MapUint8Array);
|
||||
testEncode('toString', mockU32U32MapString);
|
||||
});
|
||||
|
||||
it('decodes null', (): void => {
|
||||
expect(
|
||||
new (
|
||||
@@ -83,9 +27,9 @@ describe('BTreeMap', (): void => {
|
||||
it('decodes within more complicated types', (): void => {
|
||||
const s = new Struct(registry, {
|
||||
placeholder: U32,
|
||||
value: BTreeMap.with(Text, U32)
|
||||
value: 'BTreeMap<Text, U32>' as any
|
||||
});
|
||||
s.set('value', new BTreeMap(registry, Text, U32, mockU32TextMap));
|
||||
s.set('value', new (BTreeMap.with(Text, U32))(registry, mockU32TextMap));
|
||||
expect(s.toString()).toBe('{"placeholder":0,"value":{"bazzing":69}}');
|
||||
});
|
||||
|
||||
@@ -94,7 +38,7 @@ describe('BTreeMap', (): void => {
|
||||
(): BTreeMap<Text, U32> => new (
|
||||
BTreeMap.with(Text, U32)
|
||||
)(registry, 'ABC')
|
||||
).toThrowError(/BTreeMap: cannot decode type/);
|
||||
).toThrowError(/Map: cannot decode type/);
|
||||
});
|
||||
|
||||
it('correctly encodes length', (): void => {
|
||||
|
||||
@@ -2,212 +2,16 @@
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
import { H256 } from '../interfaces/runtime';
|
||||
import { AnyJson, Constructor, Codec, InterfaceTypes, Registry } from '../types';
|
||||
import { Constructor, Codec, InterfaceTypes, Registry } from '../types';
|
||||
|
||||
import { isHex, hexToU8a, isObject, isU8a, u8aConcat, u8aToHex, u8aToU8a } from '@polkadot/util';
|
||||
import { blake2AsU8a } from '@polkadot/util-crypto';
|
||||
import CodecMap from './Map';
|
||||
|
||||
import Compact from './Compact';
|
||||
import Raw from './Raw';
|
||||
import { compareMap, decodeU8a, typeToConstructor } from './utils';
|
||||
|
||||
/** @internal */
|
||||
function decodeBTreeMapFromU8a<K extends Codec = Codec, V extends Codec = Codec> (registry: Registry, KeyClass: Constructor<K>, ValClass: Constructor<V>, u8a: Uint8Array): Map<K, V> {
|
||||
const output = new Map<K, V>();
|
||||
const [offset, length] = Compact.decodeU8a(u8a);
|
||||
const types = [];
|
||||
|
||||
for (let i = 0; i < length.toNumber(); i++) {
|
||||
types.push(KeyClass, ValClass);
|
||||
}
|
||||
|
||||
const values = decodeU8a(registry, u8a.subarray(offset), types);
|
||||
|
||||
for (let i = 0; i < values.length; i += 2) {
|
||||
output.set(values[i] as K, values[i + 1] as V);
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
function decodeBTreeMapFromMap<K extends Codec = Codec, V extends Codec = Codec> (registry: Registry, KeyClass: Constructor<K>, ValClass: Constructor<V>, value: Map<any, any>): Map<K, V> {
|
||||
const output = new Map<K, V>();
|
||||
|
||||
value.forEach((val: any, key: any) => {
|
||||
try {
|
||||
output.set(
|
||||
key instanceof KeyClass
|
||||
? key
|
||||
: new KeyClass(registry, key),
|
||||
val instanceof ValClass
|
||||
? val
|
||||
: new ValClass(registry, val)
|
||||
);
|
||||
} catch (error) {
|
||||
console.error('Failed to decode BTreeMap key or value:', error.message);
|
||||
|
||||
throw error;
|
||||
}
|
||||
});
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode input to pass into constructor.
|
||||
*
|
||||
* @param KeyClass - Type of the map key
|
||||
* @param ValClass - Type of the map value
|
||||
* @param value - Value to decode, one of:
|
||||
* - null
|
||||
* - undefined
|
||||
* - hex
|
||||
* - Uint8Array
|
||||
* - Map<any, any>, where both key and value types are either
|
||||
* constructors or decodeable values for their types.
|
||||
* @param jsonMap
|
||||
* @internal
|
||||
*/
|
||||
function decodeBTreeMap<K extends Codec = Codec, V extends Codec = Codec> (registry: Registry, keyType: Constructor<K> | keyof InterfaceTypes, valType: Constructor<V> | keyof InterfaceTypes, value: Uint8Array | string | Map<any, any>): Map<K, V> {
|
||||
const KeyClass = typeToConstructor(registry, keyType);
|
||||
const ValClass = typeToConstructor(registry, valType);
|
||||
|
||||
if (!value) {
|
||||
return new Map<K, V>();
|
||||
} else if (isHex(value)) {
|
||||
return decodeBTreeMap(registry, KeyClass, ValClass, hexToU8a(value));
|
||||
} else if (isU8a(value)) {
|
||||
return decodeBTreeMapFromU8a<K, V>(registry, KeyClass, ValClass, u8aToU8a(value));
|
||||
} else if (value instanceof Map) {
|
||||
return decodeBTreeMapFromMap<K, V>(registry, KeyClass, ValClass, value);
|
||||
} else if (isObject(value)) {
|
||||
return decodeBTreeMapFromMap<K, V>(registry, KeyClass, ValClass, new Map(Object.entries(value)));
|
||||
}
|
||||
|
||||
throw new Error('BTreeMap: cannot decode type');
|
||||
}
|
||||
|
||||
export default class BTreeMap<K extends Codec = Codec, V extends Codec = Codec> extends Map<K, V> implements Codec {
|
||||
public readonly registry: Registry;
|
||||
|
||||
readonly #KeyClass: Constructor<K>;
|
||||
|
||||
readonly #ValClass: Constructor<V>;
|
||||
|
||||
constructor (registry: Registry, keyType: Constructor<K> | keyof InterfaceTypes, valType: Constructor<V> | keyof InterfaceTypes, rawValue: any) {
|
||||
super(decodeBTreeMap(registry, keyType, valType, rawValue));
|
||||
|
||||
this.registry = registry;
|
||||
this.#KeyClass = typeToConstructor(registry, keyType);
|
||||
this.#ValClass = typeToConstructor(registry, valType);
|
||||
}
|
||||
|
||||
public static with<K extends Codec, V extends Codec> (keyType: Constructor<K> | keyof InterfaceTypes, valType: Constructor<V> | keyof InterfaceTypes): Constructor<BTreeMap<K, V>> {
|
||||
export default class BTreeMap<K extends Codec = Codec, V extends Codec = Codec> extends CodecMap<K, V> {
|
||||
public static with<K extends Codec, V extends Codec> (keyType: Constructor<K> | keyof InterfaceTypes, valType: Constructor<V> | keyof InterfaceTypes): Constructor<CodecMap<K, V>> {
|
||||
return class extends BTreeMap<K, V> {
|
||||
constructor (registry: Registry, value?: any) {
|
||||
super(registry, keyType, valType, value);
|
||||
super(registry, 'BTreeMap', keyType, valType, value);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @description The length of the value when encoded as a Uint8Array
|
||||
*/
|
||||
public get encodedLength (): number {
|
||||
let len = Compact.encodeU8a(this.size).length;
|
||||
|
||||
this.forEach((v: V, k: K) => {
|
||||
len += v.encodedLength + k.encodedLength;
|
||||
});
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Returns a hash of the value
|
||||
*/
|
||||
public get hash (): H256 {
|
||||
return new Raw(this.registry, blake2AsU8a(this.toU8a(), 256));
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Checks if the value is an empty value
|
||||
*/
|
||||
public get isEmpty (): boolean {
|
||||
return this.size === 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Compares the value of the input to see if there is a match
|
||||
*/
|
||||
public eq (other?: any): boolean {
|
||||
return compareMap(this, other);
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Returns a hex string representation of the value. isLe returns a LE (number-only) representation
|
||||
*/
|
||||
public toHex (): string {
|
||||
return u8aToHex(this.toU8a());
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Converts the Object to to a human-friendly JSON, with additional fields, expansion and formatting of information
|
||||
*/
|
||||
public toHuman (isExtended?: boolean): AnyJson {
|
||||
const json: any = {};
|
||||
|
||||
this.forEach((v: V, k: K) => {
|
||||
json[k.toString()] = v.toHuman(isExtended);
|
||||
});
|
||||
|
||||
return json;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Converts the Object to JSON, typically used for RPC transfers
|
||||
*/
|
||||
public toJSON (): AnyJson {
|
||||
const json: any = {};
|
||||
|
||||
this.forEach((v: V, k: K) => {
|
||||
json[k.toString()] = v.toJSON();
|
||||
});
|
||||
|
||||
return json;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Returns the base runtime type name for this instance
|
||||
*/
|
||||
public toRawType (): string {
|
||||
return `BTreeMap<${new this.#KeyClass(this.registry).toRawType()},${new this.#ValClass(this.registry).toRawType()}>`;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Returns the string representation of the value
|
||||
*/
|
||||
public toString (): string {
|
||||
return JSON.stringify(this.toJSON());
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Encodes the value as a Uint8Array as per the SCALE specifications
|
||||
* @param isBare true when the value has none of the type-specific prefixes (internal)
|
||||
*/
|
||||
public toU8a (isBare?: boolean): Uint8Array {
|
||||
const encoded = new Array<Uint8Array>();
|
||||
|
||||
if (!isBare) {
|
||||
encoded.push(Compact.encodeU8a(this.size));
|
||||
}
|
||||
|
||||
this.forEach((v: V, k: K) => {
|
||||
encoded.push(k.toU8a(isBare), v.toU8a(isBare));
|
||||
});
|
||||
|
||||
return u8aConcat(...encoded);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ import Raw from './Raw';
|
||||
import { compareSet, decodeU8a, typeToConstructor } from './utils';
|
||||
|
||||
/** @internal */
|
||||
function decodeBTreeSetFromU8a<V extends Codec = Codec> (registry: Registry, ValClass: Constructor<V>, u8a: Uint8Array): Set<V> {
|
||||
function decodeSetFromU8a<V extends Codec = Codec> (registry: Registry, ValClass: Constructor<V>, u8a: Uint8Array): Set<V> {
|
||||
const output = new Set<V>();
|
||||
const [offset, length] = Compact.decodeU8a(u8a);
|
||||
const types = [];
|
||||
@@ -32,19 +32,16 @@ function decodeBTreeSetFromU8a<V extends Codec = Codec> (registry: Registry, Val
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
function decodeBTreeSetFromSet<V extends Codec = Codec> (registry: Registry, ValClass: Constructor<V>, value: Set<any>): Set<V> {
|
||||
function decodeSetFromSet<V extends Codec = Codec> (registry: Registry, ValClass: Constructor<V>, value: Set<any>): Set<V> {
|
||||
const output = new Set<V>();
|
||||
|
||||
value.forEach((v: any) => {
|
||||
let val;
|
||||
value.forEach((val: any) => {
|
||||
try {
|
||||
val = (v instanceof ValClass) ? v : new ValClass(registry, v);
|
||||
output.add((val instanceof ValClass) ? val : new ValClass(registry, val));
|
||||
} catch (error) {
|
||||
console.error('Failed to decode BTreeSet key or value:', error.message);
|
||||
throw error;
|
||||
}
|
||||
|
||||
output.add(val);
|
||||
});
|
||||
|
||||
return output;
|
||||
@@ -64,7 +61,7 @@ function decodeBTreeSetFromSet<V extends Codec = Codec> (registry: Registry, Val
|
||||
* @param jsonSet
|
||||
* @internal
|
||||
*/
|
||||
function decodeBTreeSet<V extends Codec = Codec> (registry: Registry, valType: Constructor<V> | keyof InterfaceTypes, value: Uint8Array | string | Set<any>): Set<V> {
|
||||
function decodeSet<V extends Codec = Codec> (registry: Registry, valType: Constructor<V> | keyof InterfaceTypes, value: Uint8Array | string | Set<any>): Set<V> {
|
||||
if (!value) {
|
||||
return new Set<V>();
|
||||
}
|
||||
@@ -72,11 +69,11 @@ function decodeBTreeSet<V extends Codec = Codec> (registry: Registry, valType: C
|
||||
const ValClass = typeToConstructor(registry, valType);
|
||||
|
||||
if (isHex(value)) {
|
||||
return decodeBTreeSet(registry, ValClass, hexToU8a(value));
|
||||
return decodeSet(registry, ValClass, hexToU8a(value));
|
||||
} else if (isU8a(value)) {
|
||||
return decodeBTreeSetFromU8a<V>(registry, ValClass, u8aToU8a(value));
|
||||
return decodeSetFromU8a<V>(registry, ValClass, u8aToU8a(value));
|
||||
} else if (value instanceof Set) {
|
||||
return decodeBTreeSetFromSet<V>(registry, ValClass, value);
|
||||
return decodeSetFromSet<V>(registry, ValClass, value);
|
||||
}
|
||||
|
||||
throw new Error('BTreeSet: cannot decode type');
|
||||
@@ -88,7 +85,7 @@ export default class BTreeSet<V extends Codec = Codec> extends Set<V> implements
|
||||
readonly #ValClass: Constructor<V>;
|
||||
|
||||
constructor (registry: Registry, valType: Constructor<V> | keyof InterfaceTypes, rawValue: any) {
|
||||
super(decodeBTreeSet(registry, valType, rawValue));
|
||||
super(decodeSet(registry, valType, rawValue));
|
||||
|
||||
this.registry = registry;
|
||||
this.#ValClass = typeToConstructor(registry, valType);
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
import BN from 'bn.js';
|
||||
|
||||
import { ClassOf, TypeRegistry } from '../create';
|
||||
import { TypeRegistry } from '../create';
|
||||
import Moment from './Date';
|
||||
import U32 from '../primitive/U32';
|
||||
import Compact from './Compact';
|
||||
@@ -115,7 +115,7 @@ describe('Compact', (): void => {
|
||||
|
||||
it('has the correct bitLength for constructor values (BlockNumber)', (): void => {
|
||||
expect(
|
||||
new (Compact.with(ClassOf(registry, 'BlockNumber')))(registry, 0xfffffff9).bitLength()
|
||||
new (Compact.with(registry.createClass('BlockNumber')))(registry, 0xfffffff9).bitLength()
|
||||
).toEqual(32);
|
||||
});
|
||||
|
||||
@@ -127,7 +127,7 @@ describe('Compact', (): void => {
|
||||
|
||||
it('has the correct encodedLength for constructor values (class BlockNumber)', (): void => {
|
||||
expect(
|
||||
new (Compact.with(ClassOf(registry, 'BlockNumber')))(registry, 0xfffffff9).encodedLength
|
||||
new (Compact.with(registry.createClass('BlockNumber')))(registry, 0xfffffff9).encodedLength
|
||||
).toEqual(5);
|
||||
});
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ import BN from 'bn.js';
|
||||
import { compactAddLength, compactFromU8a, compactStripLength, compactToU8a, isBigInt, isBn, isNumber, isString } from '@polkadot/util';
|
||||
import { DEFAULT_BITLENGTH } from '@polkadot/util/compact/defaults';
|
||||
|
||||
import { typeToConstructor } from './utils';
|
||||
import typeToConstructor from './utils/typeToConstructor';
|
||||
import { UIntBitLength } from './AbstractInt';
|
||||
import Base from './Base';
|
||||
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
// Copyright 2017-2020 @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 { TypeRegistry } from '../create';
|
||||
import Text from '../primitive/Text';
|
||||
import U32 from '../primitive/U32';
|
||||
import Struct from './Struct';
|
||||
|
||||
import HashMap from './HashMap';
|
||||
|
||||
const registry = new TypeRegistry();
|
||||
|
||||
describe('HashMap', (): void => {
|
||||
it('generates sane toRawTypes', (): void => {
|
||||
expect(new (HashMap.with(Text, U32))(registry).toRawType()).toBe('HashMap<Text,u32>');
|
||||
expect(new (HashMap.with(Text, Text))(registry).toRawType()).toBe('HashMap<Text,Text>');
|
||||
expect(new (HashMap.with(Text, Struct.with({ a: U32, b: Text })))(registry).toRawType())
|
||||
.toBe('HashMap<Text,{"a":"u32","b":"Text"}>');
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,17 @@
|
||||
// Copyright 2017-2020 @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 { Constructor, Codec, InterfaceTypes, Registry } from '../types';
|
||||
|
||||
import CodecMap from './Map';
|
||||
|
||||
export default class HashMap<K extends Codec = Codec, V extends Codec = Codec> extends CodecMap<K, V> {
|
||||
public static with<K extends Codec, V extends Codec> (keyType: Constructor<K> | keyof InterfaceTypes, valType: Constructor<V> | keyof InterfaceTypes): Constructor<CodecMap<K, V>> {
|
||||
return class extends HashMap<K, V> {
|
||||
constructor (registry: Registry, value?: any) {
|
||||
super(registry, 'HashMap', keyType, valType, value);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
// Copyright 2017-2020 @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 { TypeRegistry } from '../create';
|
||||
import Text from '../primitive/Text';
|
||||
import U32 from '../primitive/U32';
|
||||
import { CodecTo } from '../types';
|
||||
|
||||
import CodecMap from './Map';
|
||||
|
||||
const registry = new TypeRegistry();
|
||||
const mockU32TextMap = new Map<Text, U32>();
|
||||
mockU32TextMap.set(new Text(registry, 'bazzing'), new U32(registry, 69));
|
||||
const mockU32TextMapString = '{"bazzing":69}';
|
||||
const mockU32TextMapObject = { bazzing: 69 };
|
||||
const mockU32TextMapHexString = '0x041c62617a7a696e6745000000';
|
||||
const mockU32TextMapUint8Array = Uint8Array.from([4, 28, 98, 97, 122, 122, 105, 110, 103, 69, 0, 0, 0]);
|
||||
|
||||
const mockU32U32Map = new Map<U32, U32>();
|
||||
mockU32U32Map.set(new U32(registry, 1), new U32(registry, 2));
|
||||
mockU32U32Map.set(new U32(registry, 23), new U32(registry, 24));
|
||||
mockU32U32Map.set(new U32(registry, 28), new U32(registry, 30));
|
||||
mockU32U32Map.set(new U32(registry, 45), new U32(registry, 80));
|
||||
const mockU32U32MapString = '{"1":2,"23":24,"28":30,"45":80}';
|
||||
const mockU32U32MapObject = { 1: 2, 23: 24, 28: 30, 45: 80 };
|
||||
const mockU32U32MapHexString = '0x10043102000000083233180000000832381e00000008343550000000';
|
||||
const mockU32U32MapUint8Array = Uint8Array.from([16, 4, 49, 2, 0, 0, 0, 8, 50, 51, 24, 0, 0, 0, 8, 50, 56, 30, 0, 0, 0, 8, 52, 53, 80, 0, 0, 0]);
|
||||
|
||||
describe('CodecMap', (): void => {
|
||||
describe('decoding', (): void => {
|
||||
const testDecode = (type: string, input: any, output: string): void =>
|
||||
it(`can decode from ${type}`, (): void => {
|
||||
const s = new CodecMap(registry, 'HashMap', Text, U32, input);
|
||||
|
||||
expect(s.toString()).toBe(output);
|
||||
});
|
||||
|
||||
testDecode('map', mockU32TextMap, mockU32TextMapString);
|
||||
testDecode('hex', mockU32TextMapHexString, mockU32TextMapString);
|
||||
testDecode('Uint8Array', mockU32TextMapUint8Array, mockU32TextMapString);
|
||||
|
||||
testDecode('map', mockU32U32Map, mockU32U32MapString);
|
||||
testDecode('hex', mockU32U32MapHexString, mockU32U32MapString);
|
||||
testDecode('Uint8Array', mockU32U32MapUint8Array, mockU32U32MapString);
|
||||
});
|
||||
|
||||
describe('encoding', (): void => {
|
||||
const testEncode = (to: CodecTo, expected: any): void =>
|
||||
it(`can encode ${to}`, (): void => {
|
||||
const s = new CodecMap(registry, 'BTreeMap', Text, U32, mockU32TextMap);
|
||||
expect(s[to]()).toEqual(expected);
|
||||
});
|
||||
|
||||
testEncode('toHex', mockU32TextMapHexString);
|
||||
testEncode('toJSON', mockU32TextMapObject);
|
||||
testEncode('toU8a', mockU32TextMapUint8Array);
|
||||
testEncode('toString', mockU32TextMapString);
|
||||
});
|
||||
|
||||
describe('encoding muple values', (): void => {
|
||||
const testEncode = (to: CodecTo, expected: any): void =>
|
||||
it(`can encode ${to}`, (): void => {
|
||||
const s = new CodecMap(registry, 'BTreeMap', Text, U32, mockU32U32Map);
|
||||
expect(s[to]()).toEqual(expected);
|
||||
});
|
||||
|
||||
testEncode('toHex', mockU32U32MapHexString);
|
||||
testEncode('toJSON', mockU32U32MapObject);
|
||||
testEncode('toU8a', mockU32U32MapUint8Array);
|
||||
testEncode('toString', mockU32U32MapString);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,210 @@
|
||||
// Copyright 2017-2020 @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 { H256 } from '../interfaces/runtime';
|
||||
import { AnyJson, Constructor, Codec, InterfaceTypes, Registry } from '../types';
|
||||
|
||||
import { isHex, hexToU8a, isObject, isU8a, u8aConcat, u8aToHex, u8aToU8a } from '@polkadot/util';
|
||||
import { blake2AsU8a } from '@polkadot/util-crypto';
|
||||
|
||||
import Compact from './Compact';
|
||||
import Raw from './Raw';
|
||||
import compareMap from './utils/compareMap';
|
||||
import decodeU8a from './utils/decodeU8a';
|
||||
import typeToConstructor from './utils/typeToConstructor';
|
||||
|
||||
/** @internal */
|
||||
function decodeMapFromU8a<K extends Codec = Codec, V extends Codec = Codec> (registry: Registry, KeyClass: Constructor<K>, ValClass: Constructor<V>, u8a: Uint8Array): Map<K, V> {
|
||||
const output = new Map<K, V>();
|
||||
const [offset, length] = Compact.decodeU8a(u8a);
|
||||
const types = [];
|
||||
|
||||
for (let i = 0; i < length.toNumber(); i++) {
|
||||
types.push(KeyClass, ValClass);
|
||||
}
|
||||
|
||||
const values = decodeU8a(registry, u8a.subarray(offset), types);
|
||||
|
||||
for (let i = 0; i < values.length; i += 2) {
|
||||
output.set(values[i] as K, values[i + 1] as V);
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
function decodeMapFromMap<K extends Codec = Codec, V extends Codec = Codec> (registry: Registry, KeyClass: Constructor<K>, ValClass: Constructor<V>, value: Map<any, any>): Map<K, V> {
|
||||
const output = new Map<K, V>();
|
||||
|
||||
value.forEach((val: any, key: any) => {
|
||||
try {
|
||||
output.set(
|
||||
key instanceof KeyClass
|
||||
? key
|
||||
: new KeyClass(registry, key),
|
||||
val instanceof ValClass
|
||||
? val
|
||||
: new ValClass(registry, val)
|
||||
);
|
||||
} catch (error) {
|
||||
console.error('Failed to decode Map key or value:', error.message);
|
||||
|
||||
throw error;
|
||||
}
|
||||
});
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode input to pass into constructor.
|
||||
*
|
||||
* @param KeyClass - Type of the map key
|
||||
* @param ValClass - Type of the map value
|
||||
* @param value - Value to decode, one of:
|
||||
* - null
|
||||
* - undefined
|
||||
* - hex
|
||||
* - Uint8Array
|
||||
* - Map<any, any>, where both key and value types are either
|
||||
* constructors or decodeable values for their types.
|
||||
* @param jsonMap
|
||||
* @internal
|
||||
*/
|
||||
function decodeMap<K extends Codec = Codec, V extends Codec = Codec> (registry: Registry, keyType: Constructor<K> | keyof InterfaceTypes, valType: Constructor<V> | keyof InterfaceTypes, value: Uint8Array | string | Map<any, any>): Map<K, V> {
|
||||
const KeyClass = typeToConstructor(registry, keyType);
|
||||
const ValClass = typeToConstructor(registry, valType);
|
||||
|
||||
if (!value) {
|
||||
return new Map<K, V>();
|
||||
} else if (isHex(value)) {
|
||||
return decodeMap(registry, KeyClass, ValClass, hexToU8a(value));
|
||||
} else if (isU8a(value)) {
|
||||
return decodeMapFromU8a<K, V>(registry, KeyClass, ValClass, u8aToU8a(value));
|
||||
} else if (value instanceof Map) {
|
||||
return decodeMapFromMap<K, V>(registry, KeyClass, ValClass, value);
|
||||
} else if (isObject(value)) {
|
||||
return decodeMapFromMap<K, V>(registry, KeyClass, ValClass, new Map(Object.entries(value)));
|
||||
}
|
||||
|
||||
throw new Error('Map: cannot decode type');
|
||||
}
|
||||
|
||||
export default class CodecMap<K extends Codec = Codec, V extends Codec = Codec> extends Map<K, V> implements Codec {
|
||||
public readonly registry: Registry;
|
||||
|
||||
readonly #KeyClass: Constructor<K>;
|
||||
|
||||
readonly #ValClass: Constructor<V>;
|
||||
|
||||
readonly #type: string;
|
||||
|
||||
constructor (registry: Registry, type: 'BTreeMap' | 'HashMap', keyType: Constructor<K> | keyof InterfaceTypes, valType: Constructor<V> | keyof InterfaceTypes, rawValue: any) {
|
||||
super(decodeMap(registry, keyType, valType, rawValue));
|
||||
|
||||
this.registry = registry;
|
||||
this.#KeyClass = typeToConstructor(registry, keyType);
|
||||
this.#ValClass = typeToConstructor(registry, valType);
|
||||
this.#type = type;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description The length of the value when encoded as a Uint8Array
|
||||
*/
|
||||
public get encodedLength (): number {
|
||||
let len = Compact.encodeU8a(this.size).length;
|
||||
|
||||
this.forEach((v: V, k: K) => {
|
||||
len += v.encodedLength + k.encodedLength;
|
||||
});
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Returns a hash of the value
|
||||
*/
|
||||
public get hash (): H256 {
|
||||
return new Raw(this.registry, blake2AsU8a(this.toU8a(), 256));
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Checks if the value is an empty value
|
||||
*/
|
||||
public get isEmpty (): boolean {
|
||||
return this.size === 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Compares the value of the input to see if there is a match
|
||||
*/
|
||||
public eq (other?: any): boolean {
|
||||
return compareMap(this, other);
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Returns a hex string representation of the value. isLe returns a LE (number-only) representation
|
||||
*/
|
||||
public toHex (): string {
|
||||
return u8aToHex(this.toU8a());
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Converts the Object to to a human-friendly JSON, with additional fields, expansion and formatting of information
|
||||
*/
|
||||
public toHuman (isExtended?: boolean): AnyJson {
|
||||
const json: any = {};
|
||||
|
||||
this.forEach((v: V, k: K) => {
|
||||
json[k.toString()] = v.toHuman(isExtended);
|
||||
});
|
||||
|
||||
return json;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Converts the Object to JSON, typically used for RPC transfers
|
||||
*/
|
||||
public toJSON (): AnyJson {
|
||||
const json: any = {};
|
||||
|
||||
this.forEach((v: V, k: K) => {
|
||||
json[k.toString()] = v.toJSON();
|
||||
});
|
||||
|
||||
return json;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Returns the base runtime type name for this instance
|
||||
*/
|
||||
public toRawType (): string {
|
||||
return `${this.#type}<${new this.#KeyClass(this.registry).toRawType()},${new this.#ValClass(this.registry).toRawType()}>`;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Returns the string representation of the value
|
||||
*/
|
||||
public toString (): string {
|
||||
return JSON.stringify(this.toJSON());
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Encodes the value as a Uint8Array as per the SCALE specifications
|
||||
* @param isBare true when the value has none of the type-specific prefixes (internal)
|
||||
*/
|
||||
public toU8a (isBare?: boolean): Uint8Array {
|
||||
const encoded = new Array<Uint8Array>();
|
||||
|
||||
if (!isBare) {
|
||||
encoded.push(Compact.encodeU8a(this.size));
|
||||
}
|
||||
|
||||
this.forEach((v: V, k: K) => {
|
||||
encoded.push(k.toU8a(isBare), v.toU8a(isBare));
|
||||
});
|
||||
|
||||
return u8aConcat(...encoded);
|
||||
}
|
||||
}
|
||||
@@ -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 { ClassOf, TypeRegistry } from '../create';
|
||||
import { TypeRegistry } from '../create';
|
||||
import AccountId from '../generic/AccountId';
|
||||
import Text from '../primitive/Text';
|
||||
import U32 from '../primitive/U32';
|
||||
@@ -193,7 +193,7 @@ describe('Struct', (): void => {
|
||||
// replicate https://github.com/polkadot-js/api/issues/640
|
||||
expect(
|
||||
new Struct(registry, {
|
||||
blockNumber: ClassOf(registry, 'Option<BlockNumber>')
|
||||
blockNumber: registry.createClass('Option<BlockNumber>')
|
||||
}, { blockNumber: '0x0000000010abcdef' }).toString()
|
||||
).toEqual('{"blockNumber":279694831}');
|
||||
});
|
||||
@@ -202,10 +202,10 @@ describe('Struct', (): void => {
|
||||
expect(
|
||||
new Struct(registry, {
|
||||
accountId: AccountId,
|
||||
balanceCompact: ClassOf(registry, 'Compact<Balance>'),
|
||||
blockNumber: ClassOf(registry, 'BlockNumber'),
|
||||
compactNumber: ClassOf(registry, 'Compact<BlockNumber>'),
|
||||
optionNumber: ClassOf(registry, 'Option<BlockNumber>'),
|
||||
balanceCompact: registry.createClass('Compact<Balance>'),
|
||||
blockNumber: registry.createClass('BlockNumber'),
|
||||
compactNumber: registry.createClass('Compact<BlockNumber>'),
|
||||
optionNumber: registry.createClass('Option<BlockNumber>'),
|
||||
counter: U32,
|
||||
vector: Vec.with(AccountId)
|
||||
}).toRawType()
|
||||
@@ -223,7 +223,7 @@ describe('Struct', (): void => {
|
||||
it('generates sane toRawType (via with)', (): void => {
|
||||
const Type = Struct.with({
|
||||
accountId: AccountId,
|
||||
balance: ClassOf(registry, 'Balance')
|
||||
balance: registry.createClass('Balance')
|
||||
});
|
||||
|
||||
expect(
|
||||
|
||||
@@ -9,7 +9,7 @@ import { CodecTo } from '../types';
|
||||
import Metadata from '@polkadot/metadata/Metadata';
|
||||
import rpcMetadata from '@polkadot/metadata/Metadata/static';
|
||||
|
||||
import { ClassOf, TypeRegistry } from '../create';
|
||||
import { TypeRegistry } from '../create';
|
||||
import Text from '../primitive/Text';
|
||||
import U32 from '../primitive/U32';
|
||||
import U128 from '../primitive/U128';
|
||||
@@ -71,7 +71,7 @@ describe('Tuple', (): void => {
|
||||
new Metadata(registry, rpcMetadata);
|
||||
|
||||
const test = new (Tuple.with([
|
||||
ClassOf(registry, 'BlockNumber'), ClassOf(registry, 'VoteThreshold')
|
||||
registry.createClass('BlockNumber'), registry.createClass('VoteThreshold')
|
||||
]
|
||||
))(registry, '0x6219000001');
|
||||
|
||||
@@ -85,8 +85,8 @@ describe('Tuple', (): void => {
|
||||
|
||||
it('exposes the Types (object creation)', (): void => {
|
||||
const test = new Tuple(registry, {
|
||||
BlockNumber: ClassOf(registry, 'BlockNumber'),
|
||||
VoteThreshold: ClassOf(registry, 'VoteThreshold')
|
||||
BlockNumber: registry.createClass('BlockNumber'),
|
||||
VoteThreshold: registry.createClass('VoteThreshold')
|
||||
}, []);
|
||||
|
||||
expect(test.Types).toEqual(['BlockNumber', 'VoteThreshold']);
|
||||
@@ -113,13 +113,13 @@ describe('Tuple', (): void => {
|
||||
describe('toRawType', (): void => {
|
||||
it('generates sane value with array types', (): void => {
|
||||
expect(
|
||||
new Tuple(registry, [U128, ClassOf(registry, 'BlockNumber')]).toRawType()
|
||||
new Tuple(registry, [U128, registry.createClass('BlockNumber')]).toRawType()
|
||||
).toEqual('(u128,u32)');
|
||||
});
|
||||
|
||||
it('generates sane value with object types', (): void => {
|
||||
expect(
|
||||
new Tuple(registry, { number: U128, blockNumber: ClassOf(registry, 'BlockNumber') }).toRawType()
|
||||
new Tuple(registry, { number: U128, blockNumber: registry.createClass('BlockNumber') }).toRawType()
|
||||
).toEqual('(u128,u32)');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -6,7 +6,6 @@ import { AnyNumber, Constructor, Registry } from '../types';
|
||||
|
||||
import { bnToHex, bnToU8a, formatBalance } from '@polkadot/util';
|
||||
|
||||
import { ClassOf } from '../create/createClass';
|
||||
import AbstractInt, { DEFAULT_UINT_BITS, UIntBitLength } from './AbstractInt';
|
||||
|
||||
/**
|
||||
@@ -53,7 +52,7 @@ export default class UInt extends AbstractInt {
|
||||
*/
|
||||
public toHuman (isExpanded?: boolean): any {
|
||||
// FIXME we need proper expansion here
|
||||
return this instanceof ClassOf(this.registry, 'Balance')
|
||||
return this instanceof this.registry.createClass('Balance')
|
||||
? formatBalance(this, { decimals: this.registry.chainDecimals, withSi: true, withUnit: this.registry.chainToken })
|
||||
: super.toHuman(isExpanded);
|
||||
}
|
||||
@@ -65,7 +64,7 @@ export default class UInt extends AbstractInt {
|
||||
// NOTE In the case of balances, which have a special meaning on the UI
|
||||
// and can be interpreted differently, return a specific value for it so
|
||||
// underlying it always matches (no matter which length it actually is)
|
||||
return this instanceof ClassOf(this.registry, 'Balance')
|
||||
return this instanceof this.registry.createClass('Balance')
|
||||
? 'Balance'
|
||||
: `u${this.bitLength()}`;
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ export { default as BTreeSet } from './BTreeSet';
|
||||
export { default as Compact } from './Compact';
|
||||
// export { default as Date } from './Date';
|
||||
export { default as Enum } from './Enum';
|
||||
export { default as HashMap } from './HashMap';
|
||||
export { default as Option } from './Option';
|
||||
export { default as Result } from './Result';
|
||||
export { default as Set } from './Set';
|
||||
|
||||
@@ -27,6 +27,7 @@ function encodeWithParams (typeDef: Pick<TypeDef, any>, outer = typeDef.displayN
|
||||
case TypeDefInfo.BTreeMap:
|
||||
case TypeDefInfo.BTreeSet:
|
||||
case TypeDefInfo.Compact:
|
||||
case TypeDefInfo.HashMap:
|
||||
case TypeDefInfo.Linkage:
|
||||
case TypeDefInfo.Option:
|
||||
case TypeDefInfo.Result:
|
||||
@@ -122,6 +123,7 @@ const encoders: Record<TypeDefInfo, (typeDef: TypeDef) => string> = {
|
||||
[TypeDefInfo.BTreeSet]: (typeDef: TypeDef): string => encodeWithParams(typeDef, 'BTreeSet'),
|
||||
[TypeDefInfo.Compact]: (typeDef: TypeDef): string => encodeWithParams(typeDef, 'Compact'),
|
||||
[TypeDefInfo.Enum]: (typeDef: TypeDef): string => encodeEnum(typeDef),
|
||||
[TypeDefInfo.HashMap]: (typeDef: TypeDef): string => encodeWithParams(typeDef, 'HashMap'),
|
||||
[TypeDefInfo.Int]: (typeDef: TypeDef): string => encodeUInt(typeDef, 'Int'),
|
||||
[TypeDefInfo.Linkage]: (typeDef: TypeDef): string => encodeWithParams(typeDef, 'Linkage'),
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
|
||||
@@ -6,5 +6,7 @@ export { default as compareArray } from './compareArray';
|
||||
export { default as compareMap } from './compareMap';
|
||||
export { default as compareSet } from './compareSet';
|
||||
export { default as decodeU8a } from './decodeU8a';
|
||||
export { default as mapToTypeMap } from './mapToTypeMap';
|
||||
export { default as typeToConstructor } from './typeToConstructor';
|
||||
|
||||
export * from './encodeTypes';
|
||||
export * from './mapToTypeMap';
|
||||
|
||||
@@ -2,24 +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 { Codec, Constructor, InterfaceTypes, Registry } from '../../types';
|
||||
import { Constructor, InterfaceTypes, Registry } from '../../types';
|
||||
|
||||
import { isString } from '@polkadot/util';
|
||||
|
||||
import { ClassOf } from '../../create/createClass';
|
||||
|
||||
export function typeToConstructor <T = Codec> (registry: Registry, type: keyof InterfaceTypes | Constructor<T>): Constructor<T> {
|
||||
return (
|
||||
isString(type)
|
||||
? ClassOf(registry, type)
|
||||
: type
|
||||
) as Constructor<T>;
|
||||
}
|
||||
import typeToConstructor from './typeToConstructor';
|
||||
|
||||
/**
|
||||
* @description takes an input map of the form `{ [string]: string | Constructor }` and returns a map of `{ [string]: Constructor }`
|
||||
*/
|
||||
export function mapToTypeMap (registry: Registry, input: Record<string, keyof InterfaceTypes | Constructor>): Record<string, Constructor> {
|
||||
export default function mapToTypeMap (registry: Registry, input: Record<string, keyof InterfaceTypes | Constructor>): Record<string, Constructor> {
|
||||
return Object
|
||||
.entries(input)
|
||||
.reduce((output: Record<string, Constructor>, [key, type]): Record<string, Constructor> => {
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
// Copyright 2017-2020 @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, Registry } from '../../types';
|
||||
|
||||
import { isString } from '@polkadot/util';
|
||||
|
||||
export default function typeToConstructor <T = Codec> (registry: Registry, type: keyof InterfaceTypes | Constructor<T>): Constructor<T> {
|
||||
return (
|
||||
isString(type)
|
||||
? registry.createClass(type)
|
||||
: type
|
||||
) as Constructor<T>;
|
||||
}
|
||||
@@ -12,6 +12,7 @@ import BTreeMap from '../codec/BTreeMap';
|
||||
import BTreeSet from '../codec/BTreeSet';
|
||||
import Compact from '../codec/Compact';
|
||||
import Enum from '../codec/Enum';
|
||||
import HashMap from '../codec/HashMap';
|
||||
import Int from '../codec/Int';
|
||||
import Option from '../codec/Option';
|
||||
import Result from '../codec/Result';
|
||||
@@ -83,13 +84,14 @@ function createInt ({ displayName, length }: TypeDef, Clazz: typeof Int | typeof
|
||||
return Clazz.with(length as UIntBitLength, displayName);
|
||||
}
|
||||
|
||||
const infoMapping: Record<TypeDefInfo, (registry: Registry, value: TypeDef) => Constructor> = {
|
||||
[TypeDefInfo.BTreeMap]: (registry: Registry, value: TypeDef): Constructor => {
|
||||
const [keyType, valueType] = getTypeClassArray(value);
|
||||
function createHashMap (value: TypeDef, Clazz: typeof BTreeMap | typeof HashMap): Constructor {
|
||||
const [keyType, valueType] = getTypeClassArray(value);
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-use-before-define
|
||||
return BTreeMap.with(keyType, valueType);
|
||||
},
|
||||
return Clazz.with(keyType, valueType);
|
||||
}
|
||||
|
||||
const infoMapping: Record<TypeDefInfo, (registry: Registry, value: TypeDef) => Constructor> = {
|
||||
[TypeDefInfo.BTreeMap]: (registry: Registry, value: TypeDef): Constructor => createHashMap(value, BTreeMap),
|
||||
|
||||
[TypeDefInfo.BTreeSet]: (registry: Registry, value: TypeDef): Constructor => BTreeSet.with(getSubType(value)),
|
||||
|
||||
@@ -97,6 +99,8 @@ const infoMapping: Record<TypeDefInfo, (registry: Registry, value: TypeDef) => C
|
||||
|
||||
[TypeDefInfo.Enum]: (registry: Registry, value: TypeDef): Constructor => Enum.with(getTypeClassMap(value)),
|
||||
|
||||
[TypeDefInfo.HashMap]: (registry: Registry, value: TypeDef): Constructor => createHashMap(value, HashMap),
|
||||
|
||||
[TypeDefInfo.Int]: (registry: Registry, value: TypeDef): Constructor => createInt(value, Int),
|
||||
|
||||
// We have circular deps between Linkage & Struct
|
||||
@@ -112,7 +116,7 @@ const infoMapping: Record<TypeDefInfo, (registry: Registry, value: TypeDef) => C
|
||||
},
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
[TypeDefInfo.Null]: (registry: Registry, _: TypeDef): Constructor => ClassOf(registry, 'Null'),
|
||||
[TypeDefInfo.Null]: (registry: Registry, _: TypeDef): Constructor => createClass(registry, 'Null'),
|
||||
|
||||
[TypeDefInfo.Option]: (registry: Registry, value: TypeDef): Constructor => Option.with(getSubType(value)),
|
||||
|
||||
@@ -150,7 +154,7 @@ const infoMapping: Record<TypeDefInfo, (registry: Registry, value: TypeDef) => C
|
||||
|
||||
return (
|
||||
subType === 'u8'
|
||||
? ClassOf(registry, 'Bytes')
|
||||
? createClass(registry, 'Bytes')
|
||||
: Vec.with(subType)
|
||||
);
|
||||
},
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
import Int from '../codec/Int';
|
||||
import CodecSet from '../codec/Set';
|
||||
import { createClass, createTypeUnsafe, ClassOf, TypeRegistry } from '.';
|
||||
import { createClass, createTypeUnsafe, TypeRegistry } from '.';
|
||||
|
||||
describe('createType', (): void => {
|
||||
const registry = new TypeRegistry();
|
||||
@@ -111,12 +111,12 @@ describe('createType', (): void => {
|
||||
it('instanceof should work (primitive type)', (): void => {
|
||||
const value = registry.createType('Balance', 1234);
|
||||
|
||||
expect(value instanceof ClassOf(registry, 'Balance')).toBe(true);
|
||||
expect(value instanceof registry.createClass('Balance')).toBe(true);
|
||||
});
|
||||
|
||||
it('instanceof should work (srml type)', (): void => {
|
||||
const value = registry.createType('Gas', 1234);
|
||||
const Gas = ClassOf(registry, 'Gas');
|
||||
const Gas = registry.createClass('Gas');
|
||||
|
||||
expect(value instanceof Gas).toBe(true);
|
||||
});
|
||||
@@ -144,14 +144,14 @@ describe('createType', (): void => {
|
||||
it('allows for re-registration of a type', (): void => {
|
||||
const balDef = registry.createType('Balance');
|
||||
|
||||
expect(balDef instanceof ClassOf(registry, 'Balance'));
|
||||
expect(balDef instanceof registry.createClass('Balance'));
|
||||
expect(balDef.bitLength()).toEqual(128);
|
||||
|
||||
registry.register({ Balance: 'u32' });
|
||||
|
||||
const balu32 = registry.createType('Balance');
|
||||
|
||||
expect(balu32 instanceof ClassOf(registry, 'Balance'));
|
||||
expect(balu32 instanceof registry.createClass('Balance'));
|
||||
expect(balu32.bitLength()).toEqual(32);
|
||||
});
|
||||
|
||||
|
||||
@@ -135,6 +135,7 @@ 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],
|
||||
['HashMap<', '>', TypeDefInfo.HashMap, _decodeTuple],
|
||||
['Int<', '>', TypeDefInfo.Int, _decodeInt],
|
||||
['Result<', '>', TypeDefInfo.Result, _decodeTuple],
|
||||
['UInt<', '>', TypeDefInfo.UInt, _decodeUInt]
|
||||
|
||||
@@ -128,6 +128,13 @@ export class TypeRegistry implements Registry {
|
||||
: formatBalance.getDefaults().unit;
|
||||
}
|
||||
|
||||
/**
|
||||
* @describe Creates an instance of the class
|
||||
*/
|
||||
public createClass <K extends keyof InterfaceTypes> (type: K): Constructor<InterfaceTypes[K]> {
|
||||
return createClass(this, type) as any;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Creates an instance of a type as registered
|
||||
*/
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
type Mapper = (value: string) => string;
|
||||
|
||||
const ALLOWED_BOXES = ['BTreeMap', 'BTreeSet', 'Compact', 'Int', 'Linkage', 'Result', 'Option', 'UInt', 'Vec'];
|
||||
const ALLOWED_BOXES = ['BTreeMap', 'BTreeSet', 'Compact', 'HashMap', 'Int', 'Linkage', 'Result', 'Option', 'UInt', 'Vec'];
|
||||
const BOX_PRECEDING = ['<', '(', '[', '"', ',', ' ']; // start of vec, tuple, fixed array, part of struct def or in tuple
|
||||
|
||||
const mappings: Mapper[] = [
|
||||
|
||||
@@ -23,6 +23,7 @@ export enum TypeDefInfo {
|
||||
Tuple,
|
||||
Vec,
|
||||
VecFixed,
|
||||
HashMap,
|
||||
Int,
|
||||
UInt,
|
||||
// anything not fully supported (keep this as the last entry)
|
||||
|
||||
@@ -9,7 +9,6 @@ import { AnyJson, AnyU8a, ArgsDef, Codec, ExtrinsicPayloadValue, IExtrinsic, IKe
|
||||
|
||||
import { assert, isHex, isU8a, u8aConcat, u8aToHex, u8aToU8a } from '@polkadot/util';
|
||||
|
||||
import { ClassOf } from '../create';
|
||||
import Base from '../codec/Base';
|
||||
import Compact from '../codec/Compact';
|
||||
import { ExtrinsicValueV1 } from './v1/Extrinsic';
|
||||
@@ -74,7 +73,7 @@ export default class Extrinsic extends Base<ExtrinsicVx | ExtrinsicUnknown> impl
|
||||
public static decodeExtrinsic (registry: Registry, value: Extrinsic | ExtrinsicValue | AnyU8a | Call | undefined, version: number = DEFAULT_VERSION): ExtrinsicVx | ExtrinsicUnknown {
|
||||
if (isU8a(value) || Array.isArray(value) || isHex(value)) {
|
||||
return Extrinsic.decodeU8a(registry, u8aToU8a(value), version);
|
||||
} else if (value instanceof ClassOf(registry, 'Call')) {
|
||||
} else if (value instanceof registry.createClass('Call')) {
|
||||
return Extrinsic.newFromValue(registry, { method: value }, version);
|
||||
}
|
||||
|
||||
|
||||
@@ -9,7 +9,6 @@ import { ExtrinsicOptions } from '../types';
|
||||
|
||||
import { isU8a } from '@polkadot/util';
|
||||
|
||||
import { ClassOf } from '../../create';
|
||||
import Struct from '../../codec/Struct';
|
||||
|
||||
const TRANSACTION_VERSION = 2;
|
||||
@@ -36,7 +35,7 @@ export default class ExtrinsicV2 extends Struct implements IExtrinsicImpl {
|
||||
public static decodeExtrinsic (registry: Registry, value?: Call | Uint8Array | ExtrinsicValueV2, isSigned = false): ExtrinsicValueV2 {
|
||||
if (value instanceof ExtrinsicV2) {
|
||||
return value;
|
||||
} else if (value instanceof ClassOf(registry, 'Call')) {
|
||||
} else if (value instanceof registry.createClass('Call')) {
|
||||
return { method: value };
|
||||
} else if (isU8a(value)) {
|
||||
// here we decode manually since we need to pull through the version information
|
||||
|
||||
@@ -9,7 +9,6 @@ import { ExtrinsicOptions } from '../types';
|
||||
|
||||
import { isU8a } from '@polkadot/util';
|
||||
|
||||
import { ClassOf } from '../../create';
|
||||
import Struct from '../../codec/Struct';
|
||||
|
||||
const TRANSACTION_VERSION = 3;
|
||||
@@ -36,7 +35,7 @@ export default class ExtrinsicV3 extends Struct implements IExtrinsicImpl {
|
||||
public static decodeExtrinsic (registry: Registry, value?: Call | Uint8Array | ExtrinsicValueV3, isSigned = false): ExtrinsicValueV3 {
|
||||
if (value instanceof ExtrinsicV3) {
|
||||
return value;
|
||||
} else if (value instanceof ClassOf(registry, 'Call')) {
|
||||
} else if (value instanceof registry.createClass('Call')) {
|
||||
return { method: value };
|
||||
} else if (isU8a(value)) {
|
||||
// here we decode manually since we need to pull through the version information
|
||||
|
||||
@@ -9,7 +9,6 @@ import { ExtrinsicOptions } from '../types';
|
||||
|
||||
import { isU8a } from '@polkadot/util';
|
||||
|
||||
import { ClassOf } from '../../create';
|
||||
import Struct from '../../codec/Struct';
|
||||
|
||||
export const TRANSACTION_VERSION = 4;
|
||||
@@ -36,7 +35,7 @@ export default class ExtrinsicV4 extends Struct implements IExtrinsicImpl {
|
||||
public static decodeExtrinsic (registry: Registry, value?: Call | Uint8Array | ExtrinsicValueV4, isSigned = false): ExtrinsicValueV4 {
|
||||
if (value instanceof ExtrinsicV4) {
|
||||
return value;
|
||||
} else if (value instanceof ClassOf(registry, 'Call')) {
|
||||
} else if (value instanceof registry.createClass('Call')) {
|
||||
return { method: value };
|
||||
} else if (isU8a(value)) {
|
||||
// here we decode manually since we need to pull through the version information
|
||||
|
||||
@@ -41,9 +41,8 @@ export default {
|
||||
peerId: 'Text',
|
||||
listenedAddresses: 'Vec<Text>',
|
||||
externalAddresses: 'Vec<Text>',
|
||||
// FIXME These are HashMap
|
||||
connectedPeers: 'BTreeMap<Text, Peer>',
|
||||
notConnectedPeers: 'BTreeMap<Text, NotConnectedPeer>',
|
||||
connectedPeers: 'HashMap<Text, Peer>',
|
||||
notConnectedPeers: 'HashMap<Text, NotConnectedPeer>',
|
||||
averageDownloadPerSec: 'u64',
|
||||
averageUploadPerSec: 'u64',
|
||||
peerset: 'NetworkStatePeerset'
|
||||
@@ -78,8 +77,7 @@ export default {
|
||||
},
|
||||
NetworkStatePeerset: {
|
||||
messageQueue: 'u64',
|
||||
// FIXME This is HashMap
|
||||
nodes: 'BTreeMap<Text, NetworkStatePeersetInfo>'
|
||||
nodes: 'HashMap<Text, NetworkStatePeersetInfo>'
|
||||
},
|
||||
NetworkStatePeersetInfo: {
|
||||
connected: 'bool',
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
/* eslint-disable @typescript-eslint/no-empty-interface */
|
||||
|
||||
import { ITuple } from '@polkadot/types/types';
|
||||
import { BTreeMap, Enum, Option, Struct, U8aFixed, Vec } from '@polkadot/types/codec';
|
||||
import { Enum, HashMap, Option, Struct, U8aFixed, Vec } from '@polkadot/types/codec';
|
||||
import { Bytes, StorageKey, Text, bool, u32, u64, u8 } from '@polkadot/types/primitive';
|
||||
import { Balance, BlockNumber, DispatchClass, Hash, StorageData, Weight } from '@polkadot/types/interfaces/runtime';
|
||||
|
||||
@@ -62,8 +62,8 @@ export interface NetworkState extends Struct {
|
||||
readonly peerId: Text;
|
||||
readonly listenedAddresses: Vec<Text>;
|
||||
readonly externalAddresses: Vec<Text>;
|
||||
readonly connectedPeers: BTreeMap<Text, Peer>;
|
||||
readonly notConnectedPeers: BTreeMap<Text, NotConnectedPeer>;
|
||||
readonly connectedPeers: HashMap<Text, Peer>;
|
||||
readonly notConnectedPeers: HashMap<Text, NotConnectedPeer>;
|
||||
readonly averageDownloadPerSec: u64;
|
||||
readonly averageUploadPerSec: u64;
|
||||
readonly peerset: NetworkStatePeerset;
|
||||
@@ -72,7 +72,7 @@ export interface NetworkState extends Struct {
|
||||
/** @name NetworkStatePeerset */
|
||||
export interface NetworkStatePeerset extends Struct {
|
||||
readonly messageQueue: u64;
|
||||
readonly nodes: BTreeMap<Text, NetworkStatePeersetInfo>;
|
||||
readonly nodes: HashMap<Text, NetworkStatePeersetInfo>;
|
||||
}
|
||||
|
||||
/** @name NetworkStatePeersetInfo */
|
||||
|
||||
@@ -92,6 +92,7 @@ export interface Registry {
|
||||
// keep this as a generic Codec, however the actual impl. returns the correct
|
||||
findMetaEvent (eventIndex: Uint8Array): Constructor<any>;
|
||||
|
||||
createClass <K extends keyof InterfaceTypes> (type: K): Constructor<InterfaceTypes[K]>;
|
||||
createType <K extends keyof InterfaceTypes> (type: K, ...params: any[]): InterfaceTypes[K];
|
||||
get <T extends Codec = Codec> (name: string, withUnknown?: boolean): Constructor<T> | undefined;
|
||||
getChainProperties (): ChainProperties | undefined;
|
||||
|
||||
Reference in New Issue
Block a user