Adjust createType with inference parser (#3866)
* Adjust createType with inference parser * Santize fixed vecs * __Tokenize * Shortcut ending sequences * Update tests & checks * greedy matching * Additional linting * s/extends Codec/extends Codec | undefined/ * Adjust * Shortcut interfacetypes * Cleanups * set & enum * Adjust getTypeClass fallback set * CC * Adjust * Adjust createTypeUnsafe
This commit is contained in:
@@ -73,7 +73,8 @@ function calcBalances (api: ApiInterfaceRx, [data, bestNumber, [vesting, allLock
|
||||
// Calculate the vesting balances,
|
||||
// - offset = balance locked at startingBlock
|
||||
// - perBlock is the unlock amount
|
||||
const { locked: vestingTotal, perBlock, startingBlock } = vesting || api.registry.createType('VestingInfo');
|
||||
const emptyVest = api.registry.createType('VestingInfo');
|
||||
const { locked: vestingTotal, perBlock, startingBlock } = vesting || emptyVest;
|
||||
const isStarted = bestNumber.gt(startingBlock);
|
||||
const vestedNow = isStarted ? perBlock.mul(bestNumber.sub(startingBlock)) : new BN(0);
|
||||
const vestedBalance = vestedNow.gt(vestingTotal) ? vestingTotal : api.registry.createType('Balance', vestedNow);
|
||||
@@ -117,33 +118,29 @@ const isNonNullable = <T>(nullable: T): nullable is NonNullable<T> => !!nullable
|
||||
|
||||
// current (balances, vesting)
|
||||
function queryCurrent (api: ApiInterfaceRx, accountId: AccountId, balanceInstances: string[] = ['balances']): Observable<ResultBalance> {
|
||||
const lockCalls = balanceInstances.map(
|
||||
(m): ApiInterfaceRx['query']['balances']['locks'] | undefined =>
|
||||
(api.derive as DeriveCustomLocks)[m]?.customLocks || api.query[m as 'balances']?.locks
|
||||
const calls = balanceInstances.map((m): ApiInterfaceRx['query']['balances']['locks'] | undefined =>
|
||||
(api.derive as DeriveCustomLocks)[m]?.customLocks || api.query[m as 'balances']?.locks
|
||||
);
|
||||
|
||||
const lockEmpty = lockCalls.map((c) => !c);
|
||||
const lockQueries = lockCalls.filter(isNonNullable).map((c): QueryableStorageMultiArg<'rxjs'> => [c, accountId]);
|
||||
const lockEmpty = calls.map((c) => !c);
|
||||
const queries = calls.filter(isNonNullable).map((c): QueryableStorageMultiArg<'rxjs'> => [c, accountId]);
|
||||
|
||||
return (
|
||||
api.query.vesting?.vesting
|
||||
? api.queryMulti<[Option<VestingInfo>, ...BalanceLocks]>([
|
||||
[api.query.vesting.vesting, accountId],
|
||||
...lockQueries
|
||||
])
|
||||
? api.queryMulti<[Option<VestingInfo>, ...BalanceLocks]>([[api.query.vesting.vesting, accountId], ...queries])
|
||||
// TODO We need to check module instances here as well, not only the balances module
|
||||
: lockQueries.length
|
||||
? api.queryMulti<[...(Vec<BalanceLock>)[]]>(lockQueries).pipe(
|
||||
map((locks): [Option<VestingInfo>, ...BalanceLocks] =>
|
||||
[api.registry.createType('Option<VestingInfo>'), ...locks]
|
||||
)
|
||||
: queries.length
|
||||
? api.queryMulti<[...(Vec<BalanceLock>)[]]>(queries).pipe(
|
||||
map((r): [Option<VestingInfo>, ...BalanceLocks] => [api.registry.createType('Option<VestingInfo>'), ...r])
|
||||
)
|
||||
: of([api.registry.createType('Option<VestingInfo>')] as [Option<VestingInfo>])
|
||||
).pipe(
|
||||
map(([optVesting, ...locks]): ResultBalance => {
|
||||
map(([opt, ...locks]): ResultBalance => {
|
||||
let offset = -1;
|
||||
|
||||
return [optVesting.unwrapOr(null), lockEmpty.map((e) => e ? api.registry.createType('Vec<BalanceLock>') : locks[++offset])];
|
||||
return [
|
||||
opt.unwrapOr(null),
|
||||
lockEmpty.map((e) => e ? api.registry.createType('Vec<BalanceLock>') : locks[++offset])
|
||||
];
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
@@ -29,8 +29,10 @@ assert(oo0.divn(123) && [...oo1.values()] && oo2[6].isAscii && oo3.divn(3) && oo
|
||||
const aa0 = registry.createType(' AccountId');
|
||||
const aa1 = registry.createType('BlockAttestations');
|
||||
const aa2 = registry.createType('ExtrinsicEra');
|
||||
const aa3 = registry.createType('VestingInfo');
|
||||
const aa4 = registry.createType('(Vec<ValidatorIndex>, CompactAssignmentsTo257, PhragmenScore, EraIndex)');
|
||||
|
||||
assert(aa0.isAscii && aa1.receipt && aa2.isMortalEra, 'All ok');
|
||||
assert(aa0.isAscii && aa1.receipt && aa2.isMortalEra && aa3.toHuman() && aa4[3].toNumber(), 'All ok');
|
||||
|
||||
// Should be Codec, we don't know this one
|
||||
const bb = registry.createType('Something');
|
||||
@@ -47,13 +49,24 @@ const vv = registry.createType('Vec< Vec< Vec< Vec<u8>> > >');
|
||||
const vt = registry.createType('Vec<(u8, u16)>');
|
||||
// nested stuff from all-over
|
||||
const vn = registry.createType('Vec<(u32, (u32, u64), Vec<u8>, Vec<(u32, u64)>, [u8;32], [u128;32])>');
|
||||
// nested fixed
|
||||
const nf = registry.createType('[[[u8;32];5];3]');
|
||||
|
||||
assert(ee[0].unwrap().unwrap().divn(123) && vb.unwrap().bitLength() && vv.toHuman() && vn.toHuman() && vt.toHuman(), 'All ok');
|
||||
assert(ee[0].unwrap().unwrap().divn(123) && vb.unwrap().bitLength() && vv.toHuman() && vn.toHuman() && vt.toHuman() && nf.toHuman(), 'All ok');
|
||||
|
||||
// tuple & struct
|
||||
const vs = registry.createType('(u8, {"a":"u32","b":"(u32,u64)"},(u8,u16),{"foo":"Bar"},u16)');
|
||||
// set
|
||||
const st = registry.createType('{"_set": { "A": 1, "B": 2, "C": 4 } }');
|
||||
// enum
|
||||
const en = registry.createType('{"_enum": { "A": 1, "B": 2, "C": 4 } }');
|
||||
|
||||
assert(vs.toHuman() && st.strings && en.index, 'All ok');
|
||||
|
||||
// Should end up as Raw
|
||||
const gg = registry.createType('[ u8 ;678]');
|
||||
|
||||
assert(gg.bitLength, 'All ok');
|
||||
assert(gg.subarray(1), 'All ok');
|
||||
|
||||
// Should end up as VecFixed<u128>
|
||||
const hh = registry.createType('[u128; 32]');
|
||||
@@ -61,20 +74,22 @@ const hh = registry.createType('[u128; 32]');
|
||||
assert(hh[0].bitLength(), 'All ok');
|
||||
|
||||
// tuple! ITuple<[u32, Compact<u64>, u128, Codec]>
|
||||
const tt1 = registry.createType('(u32, Compact< u64 >, u128 , Something)');
|
||||
const tt1 = registry.createType('(u32, Compact<u64>, u128 , Something)');
|
||||
// unwraps into a u32
|
||||
const tt2 = registry.createType('( u32 )');
|
||||
// lots and lots of params
|
||||
const tt4 = registry.createType('(u8,u16,u32,u64,u128,u256,Break,u256,u128,u64,u32,u16,u8)');
|
||||
const tt2 = registry.createType('(((u32)))');
|
||||
// lots and lots of params (indicates recursion limit)
|
||||
const tt4 = registry.createType('(u8,u16,u32,u64,u128,u256,u8,u16,u32,u64,u128,u256,u8,u16,u32,u64,u128,u256)');
|
||||
// empty
|
||||
const tt5 = registry.createType('()');
|
||||
// nested tuples
|
||||
const tt6 = registry.createType('(u8, (u16, (u32, u64, u32)), (u64, u64))');
|
||||
const tt6 = registry.createType('(u8, (u16, (u32, u64, u128)), (u64, u128))');
|
||||
// more nested tuples
|
||||
const tt7 = registry.createType('(((u8, u16, u32), (u32, u16, u8)), u128, u256)');
|
||||
// nested tuples with a wrapper
|
||||
const tt8 = registry.createType('(u8, Vec<(u16, u32)>, Option<(u128, u128)>)');
|
||||
// same example as above
|
||||
const tt9 = registry.createType('(u32, (u32, u64), Vec<u8>, Vec<(u32, u64)>, [u8;32], [u128;32])');
|
||||
// tuple with nested fixed
|
||||
const tta = registry.createType('([u8;32], [u16;5])');
|
||||
|
||||
assert(tt1[2].bitLength() && tt2.bitLength() && tt4[3].bitLength() && tt5.isEmpty && tt6[1].toHuman() && tt7.toHuman() && tt8.toHuman() && tt9.toHuman(), 'All ok');
|
||||
assert(tt1[2].bitLength() && tt2.bitLength() && tt4[3].bitLength() && tt5.isEmpty && tt6[1].toHuman() && tt7.toHuman() && tt8.toHuman() && tt9.toHuman() && tta[1][1].bitLength(), 'All ok');
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
// Copyright 2017-2021 @polkadot/types authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import type { Codec, ITuple } from '../types';
|
||||
|
||||
import { TypeRegistry } from '../create/registry';
|
||||
import { Linkage } from '.';
|
||||
|
||||
@@ -32,7 +34,7 @@ describe('Linkage', (): void => {
|
||||
|
||||
// actual check
|
||||
expect(
|
||||
registry.createType(
|
||||
registry.createType<ITuple<[Codec, Linkage<Codec>]>>(
|
||||
'(ValidatorPrefsWithCommission, Linkage<AccountId>)',
|
||||
'0x0284d7170001da30b68f54f686f586ddb29de12b682dd8bd1404566fb8a8db5dec20aa5b6b36'
|
||||
).toHuman()
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import type { CodecHash, Hash } from '../interfaces/runtime';
|
||||
import type { Codec, Constructor, Registry } from '../types';
|
||||
import type { Constructor, ISet, Registry } from '../types';
|
||||
|
||||
import { assert, BN, bnToBn, bnToU8a, isBn, isNumber, isString, isU8a, isUndefined, stringCamelCase, stringify, stringUpperFirst, u8aToBn, u8aToHex, u8aToU8a } from '@polkadot/util';
|
||||
|
||||
@@ -75,7 +75,7 @@ function decodeSet (setValues: SetValues, value: string[] | Set<string> | Uint8A
|
||||
* a bitwise representation of the values.
|
||||
*/
|
||||
// FIXME This is a prime candidate to extend the JavaScript built-in Set
|
||||
export class CodecSet extends Set<string> implements Codec {
|
||||
export class CodecSet extends Set<string> implements ISet {
|
||||
public readonly registry: Registry;
|
||||
|
||||
public createdAtHash?: Hash;
|
||||
|
||||
@@ -136,7 +136,7 @@ export class Struct<
|
||||
// input values, mapped by key can be anything (construction)
|
||||
V extends { [K in keyof S]: any } = { [K in keyof S]: any },
|
||||
// type names, mapped by key, name of Class in S
|
||||
E extends { [K in keyof S]: string } = { [K in keyof S]: string }> extends Map<keyof S, Codec> implements IStruct {
|
||||
E extends { [K in keyof S]: string } = { [K in keyof S]: string }> extends Map<keyof S, Codec> implements IStruct<keyof S> {
|
||||
public readonly registry: Registry;
|
||||
|
||||
public createdAtHash?: Hash;
|
||||
|
||||
@@ -12,15 +12,6 @@ import { Bytes, Null } from '../primitive';
|
||||
import { getTypeDef } from './getTypeDef';
|
||||
import { TypeDefInfo } from './types';
|
||||
|
||||
export function createClass<T extends Codec = Codec, K extends string = string> (registry: Registry, type: K): DetectConstructor<T, K> {
|
||||
return getTypeClass(
|
||||
registry,
|
||||
registry.isLookupType(type)
|
||||
? registry.lookup.getTypeDef(type)
|
||||
: getTypeDef(type)
|
||||
);
|
||||
}
|
||||
|
||||
function getSubDefArray (value: TypeDef): TypeDef[] {
|
||||
assert(value.sub && Array.isArray(value.sub), () => `Expected subtype as TypeDef[] in ${stringify(value)}`);
|
||||
|
||||
@@ -180,14 +171,14 @@ const infoMapping: Record<TypeDefInfo, (registry: Registry, value: TypeDef) => C
|
||||
|
||||
// Returns the type Class for construction
|
||||
export function getTypeClass<T extends Codec = Codec> (registry: Registry, typeDef: TypeDef): Constructor<T> {
|
||||
let Type = registry.get<T>(typeDef.type) as Constructor<T> | undefined;
|
||||
let Type = registry.get(typeDef.type);
|
||||
|
||||
if (Type) {
|
||||
return Type;
|
||||
return Type as Constructor<T>;
|
||||
}
|
||||
|
||||
try {
|
||||
Type = infoMapping[typeDef.info](registry, typeDef) as Constructor<T>;
|
||||
Type = infoMapping[typeDef.info](registry, typeDef);
|
||||
|
||||
assert(Type, 'No class created');
|
||||
|
||||
@@ -198,8 +189,17 @@ export function getTypeClass<T extends Codec = Codec> (registry: Registry, typeD
|
||||
Type.__fallbackType = typeDef.fallbackType;
|
||||
}
|
||||
|
||||
return Type;
|
||||
return Type as Constructor<T>;
|
||||
} catch (error) {
|
||||
throw new Error(`Unable to construct class from ${stringify(typeDef)}: ${(error as Error).message}`);
|
||||
}
|
||||
}
|
||||
|
||||
export function createClass<T extends Codec = Codec, K extends string = string> (registry: Registry, type: K): DetectConstructor<T, K> {
|
||||
return getTypeClass(
|
||||
registry,
|
||||
registry.isLookupType(type)
|
||||
? registry.lookup.getTypeDef(type)
|
||||
: getTypeDef(type)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import { CodecSet, Int } from '../codec';
|
||||
import { createClass, createTypeUnsafe, TypeRegistry } from '.';
|
||||
import { createClass, TypeRegistry } from '.';
|
||||
|
||||
describe('createType', (): void => {
|
||||
const registry = new TypeRegistry();
|
||||
@@ -27,7 +27,7 @@ describe('createType', (): void => {
|
||||
|
||||
it('allows creation of a Struct', (): void => {
|
||||
const raw = '{"balance":"Balance","index":"u32"}';
|
||||
const struct = createTypeUnsafe(registry, raw, [{
|
||||
const struct = registry.createTypeUnsafe(raw, [{
|
||||
balance: 1234,
|
||||
index: '0x10'
|
||||
}]);
|
||||
@@ -41,49 +41,49 @@ describe('createType', (): void => {
|
||||
|
||||
it('allows creation of a BTreeMap', (): void => {
|
||||
expect(
|
||||
createTypeUnsafe(registry, 'BTreeMap<Text,u32>', ['0x041c62617a7a696e6745000000']).toString()
|
||||
registry.createTypeUnsafe('BTreeMap<Text,u32>', ['0x041c62617a7a696e6745000000']).toString()
|
||||
).toEqual('{"bazzing":69}');
|
||||
});
|
||||
|
||||
it('allows creation of a BTreeSet', (): void => {
|
||||
expect(
|
||||
createTypeUnsafe(registry, 'BTreeSet<u32>', ['0x1002000000180000001e00000050000000']).toString()
|
||||
registry.createTypeUnsafe('BTreeSet<u32>', ['0x1002000000180000001e00000050000000']).toString()
|
||||
).toEqual('[2,24,30,80]');
|
||||
});
|
||||
|
||||
it('allows creation of a Enum (simple)', (): void => {
|
||||
expect(
|
||||
createTypeUnsafe(registry, '{"_enum": ["A", "B", "C"]}', [1]).toJSON()
|
||||
registry.createTypeUnsafe('{"_enum": ["A", "B", "C"]}', [1]).toJSON()
|
||||
).toEqual('B');
|
||||
});
|
||||
|
||||
it('allows creation of a Enum (with types)', (): void => {
|
||||
expect(
|
||||
createTypeUnsafe(registry, '{"_enum": {"A": null, "B": "u32", "C": null} }', [1]).toJSON()
|
||||
registry.createTypeUnsafe('{"_enum": {"A": null, "B": "u32", "C": null} }', [1]).toJSON()
|
||||
).toEqual({ b: 0 });
|
||||
});
|
||||
|
||||
it('allows creation of a Enum (with indexes)', (): void => {
|
||||
expect(
|
||||
createTypeUnsafe(registry, '{"_enum": {"A": 42, "B": 69, "C": 255} }', [69]).toJSON()
|
||||
registry.createTypeUnsafe('{"_enum": {"A": 42, "B": 69, "C": 255} }', [69]).toJSON()
|
||||
).toEqual('B');
|
||||
});
|
||||
|
||||
it('allows creation of a Result', (): void => {
|
||||
expect(
|
||||
createTypeUnsafe(registry, 'Result<u32,Text>', ['0x011064656667']).toJSON()
|
||||
registry.createTypeUnsafe('Result<u32,Text>', ['0x011064656667']).toJSON()
|
||||
).toEqual({ err: 'defg' });
|
||||
});
|
||||
|
||||
it('allows creation of a Set', (): void => {
|
||||
expect(
|
||||
createTypeUnsafe<CodecSet>(registry, '{"_set": { "A": 1, "B": 2, "C": 4, "D": 8, "E": 16, "G": 32, "H": 64, "I": 128 } }', [1 + 4 + 16 + 64]).strings
|
||||
registry.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 Tuple', (): void => {
|
||||
expect(
|
||||
createTypeUnsafe(registry, '(Balance,u32)', [[1234, 5678]]).toJSON()
|
||||
registry.createTypeUnsafe('(Balance,u32)', [[1234, 5678]]).toJSON()
|
||||
).toEqual([
|
||||
1234, // '0x000000000000000000000000000004d2',
|
||||
5678
|
||||
@@ -92,13 +92,13 @@ describe('createType', (): void => {
|
||||
|
||||
it('allows creation for a UInt<bitLength>', (): void => {
|
||||
expect(
|
||||
createTypeUnsafe(registry, 'UInt<2048>').toRawType()
|
||||
registry.createType('UInt<2048>').toRawType()
|
||||
).toEqual('u2048');
|
||||
});
|
||||
|
||||
it('fails creation for a UInt<bitLength> where bitLength is not power of 8', (): void => {
|
||||
expect(
|
||||
() => createTypeUnsafe(registry, 'UInt<20>').toRawType()
|
||||
() => registry.createType('UInt<20>').toRawType()
|
||||
).toThrow('UInt<20>: Only support for UInt<bitLength>, where length <= 8192 and a power of 8');
|
||||
});
|
||||
|
||||
@@ -112,44 +112,44 @@ describe('createType', (): void => {
|
||||
|
||||
it('allows creation of a [u8; 8]', (): void => {
|
||||
expect(
|
||||
createTypeUnsafe(registry, '[u8; 8]', [[0x12, 0x00, 0x23, 0x00, 0x45, 0x00, 0x67, 0x00]]).toHex()
|
||||
registry.createTypeUnsafe('[u8; 8]', [[0x12, 0x00, 0x23, 0x00, 0x45, 0x00, 0x67, 0x00]]).toHex()
|
||||
).toEqual('0x1200230045006700');
|
||||
});
|
||||
|
||||
it('allows creation of a [u16; 4]', (): void => {
|
||||
expect(
|
||||
createTypeUnsafe(registry, '[u16; 4]', [[0x1200, 0x2300, 0x4500, 0x6700]]).toU8a()
|
||||
registry.createTypeUnsafe('[u16; 4]', [[0x1200, 0x2300, 0x4500, 0x6700]]).toU8a()
|
||||
).toEqual(new Uint8Array([0x00, 0x12, 0x00, 0x23, 0x00, 0x45, 0x00, 0x67]));
|
||||
});
|
||||
|
||||
describe('isPedantic', (): void => {
|
||||
it('correctly decodes Bytes', (): void => {
|
||||
expect(
|
||||
createTypeUnsafe(registry, 'Bytes', ['0x12345678'], { isPedantic: true }).toHex()
|
||||
registry.createTypeUnsafe('Bytes', ['0x12345678'], { isPedantic: true }).toHex()
|
||||
).toEqual('0x12345678');
|
||||
});
|
||||
|
||||
it('correctly decodes Bytes (prefixed)', (): void => {
|
||||
expect(
|
||||
createTypeUnsafe(registry, 'Bytes', [new Uint8Array([4 << 2, 0x12, 0x34, 0x56, 0x78])], { isPedantic: true }).toHex()
|
||||
registry.createTypeUnsafe('Bytes', [new Uint8Array([4 << 2, 0x12, 0x34, 0x56, 0x78])], { isPedantic: true }).toHex()
|
||||
).toEqual('0x12345678');
|
||||
});
|
||||
|
||||
it('correctly decodes Text', (): void => {
|
||||
expect(
|
||||
createTypeUnsafe(registry, 'Text', ['0x70726f7669646572'], { isPedantic: true }).toString()
|
||||
registry.createTypeUnsafe('Text', ['0x70726f7669646572'], { isPedantic: true }).toString()
|
||||
).toEqual('provider');
|
||||
});
|
||||
|
||||
it('correctly decodes Type', (): void => {
|
||||
expect(
|
||||
createTypeUnsafe(registry, 'Type', ['0x5665633c75383e'], { isPedantic: true }).toString()
|
||||
registry.createTypeUnsafe('Type', ['0x5665633c75383e'], { isPedantic: true }).toString()
|
||||
).toEqual('Bytes'); // Vec<u8> -> Bytes
|
||||
});
|
||||
|
||||
it('correctly decodes [u16; 4]', (): void => {
|
||||
expect(
|
||||
createTypeUnsafe(registry, '[u16; 4]', ['0x0012002300450067'], { isPedantic: true }).toHex()
|
||||
registry.createTypeUnsafe('[u16; 4]', ['0x0012002300450067'], { isPedantic: true }).toHex()
|
||||
).toEqual('0x0012002300450067');
|
||||
});
|
||||
});
|
||||
@@ -180,12 +180,12 @@ describe('createType', (): void => {
|
||||
}
|
||||
});
|
||||
|
||||
const value = createTypeUnsafe(registry, 'TestComplex', [{
|
||||
const value = registry.createType('TestComplex', {
|
||||
accountId: '0x1234567812345678123456781234567812345678123456781234567812345678',
|
||||
balance: 123,
|
||||
fromSrml: 0,
|
||||
log: [456, 789]
|
||||
}]);
|
||||
});
|
||||
|
||||
expect(value instanceof createClass(registry, 'TestComplex')).toBe(true);
|
||||
});
|
||||
@@ -218,7 +218,7 @@ describe('createType', (): void => {
|
||||
});
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||
const cmpDef: any = createTypeUnsafe(registry, 'TestComplex');
|
||||
const cmpDef: any = registry.createType('TestComplex');
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access,@typescript-eslint/no-unsafe-call
|
||||
expect(cmpDef.balance.bitLength()).toEqual(128);
|
||||
@@ -226,7 +226,7 @@ describe('createType', (): void => {
|
||||
registry.register({ Balance: 'u32' });
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||
const cmpu32: any = createTypeUnsafe(registry, 'TestComplex');
|
||||
const cmpu32: any = registry.createType('TestComplex');
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access,@typescript-eslint/no-unsafe-call
|
||||
expect(cmpu32.balance.bitLength()).toEqual(32);
|
||||
|
||||
@@ -38,7 +38,7 @@ function checkPedantic (created: Codec, [value]: unknown[], isPedantic = false):
|
||||
|
||||
// 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> (registry: Registry, Type: Constructor, params: unknown[] = [], { blockHash, isOptional, isPedantic }: CreateOptions = {}): DetectCodec<T, K> {
|
||||
function initType<T extends Codec> (registry: Registry, Type: Constructor, params: unknown[] = [], { blockHash, isOptional, isPedantic }: CreateOptions = {}): T {
|
||||
const created = new (isOptional ? Option.with(Type) : Type)(registry, ...params);
|
||||
|
||||
checkPedantic(created, params, isPedantic);
|
||||
@@ -59,7 +59,7 @@ export function createTypeUnsafe<T extends Codec = Codec, K extends string = str
|
||||
let firstError: Error | null = null;
|
||||
|
||||
try {
|
||||
Clazz = createClass<T, K>(registry, type);
|
||||
Clazz = createClass(registry, type);
|
||||
|
||||
return initType(registry, Clazz, params, options);
|
||||
} catch (error) {
|
||||
@@ -68,7 +68,7 @@ export function createTypeUnsafe<T extends Codec = Codec, K extends string = str
|
||||
|
||||
if (Clazz && Clazz.__fallbackType) {
|
||||
try {
|
||||
Clazz = createClass<T, K>(registry, Clazz.__fallbackType as unknown as K);
|
||||
Clazz = createClass(registry, Clazz.__fallbackType as unknown as K);
|
||||
|
||||
return initType(registry, Clazz, params, options);
|
||||
} catch {
|
||||
@@ -86,5 +86,5 @@ export function createTypeUnsafe<T extends Codec = Codec, K extends string = str
|
||||
* @param params - The value to instantiate the type with
|
||||
*/
|
||||
export function createType<T extends Codec = Codec, K extends string = string> (registry: Registry, type: K, ...params: unknown[]): DetectCodec<T, K> {
|
||||
return createTypeUnsafe<T, K>(registry, type, params);
|
||||
return createTypeUnsafe(registry, type, params);
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ export const allExtensions: ExtDef = {
|
||||
// This may not match 100% with the current defaults and are used when not specified
|
||||
// in the metadata (which is for very old chains). The order is important here, as
|
||||
// applied by default
|
||||
export const fallbackExtensions: Array<keyof typeof allExtensions> = [
|
||||
export const fallbackExtensions = [
|
||||
'CheckVersion',
|
||||
'CheckGenesis',
|
||||
'CheckEra',
|
||||
|
||||
@@ -6,7 +6,7 @@ import type { Codec } from './types';
|
||||
import metadataStatic from '@polkadot/types-support/metadata/static-substrate';
|
||||
|
||||
import * as definitions from './interfaces/definitions';
|
||||
import { createTypeUnsafe, TypeRegistry } from './create';
|
||||
import { TypeRegistry } from './create';
|
||||
import * as exported from './index.types';
|
||||
import { Metadata } from './metadata';
|
||||
|
||||
@@ -34,7 +34,7 @@ function testTypes (type: string, typeNames: string[]): void {
|
||||
typeNames.forEach((name): void => {
|
||||
it(`creates an empty ${name}`, (): void => {
|
||||
const constructFn = (): Codec =>
|
||||
createTypeUnsafe(registry, name);
|
||||
registry.createType(name);
|
||||
|
||||
if (UNCONSTRUCTABLE.includes(name.toLowerCase())) {
|
||||
expect(constructFn).toThrow();
|
||||
@@ -49,7 +49,7 @@ function testTypes (type: string, typeNames: string[]): void {
|
||||
typeNames.forEach((name): void => {
|
||||
it(`creates an empty ${name} (from empty bytes)`, (): void => {
|
||||
const constructFn = (): Codec =>
|
||||
createTypeUnsafe(registry, name, [createTypeUnsafe(registry, 'Bytes')]);
|
||||
registry.createType(name, registry.createType('Bytes'));
|
||||
|
||||
if (UNCONSTRUCTABLE.includes(name.toLowerCase())) {
|
||||
expect(constructFn).toThrow();
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// Copyright 2017-2021 @polkadot/types authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import { createTypeUnsafe, TypeRegistry } from '../../create';
|
||||
import { TypeRegistry } from '../../create';
|
||||
|
||||
const ACTUAL = '0x' +
|
||||
// Vec<ValidatorIndex>
|
||||
@@ -181,15 +181,15 @@ describe('CompactAssignments', (): void => {
|
||||
|
||||
it('has a valid vote1 ([Type; <number>] equivalency)', (): void => {
|
||||
expect(test.votes1.toHex()).toEqual(
|
||||
createTypeUnsafe(registry, 'Vec<(u32, u16)>', [[
|
||||
registry.createType('Vec<(u32, u16)>', [
|
||||
[1, 3]
|
||||
]]).toHex()
|
||||
]).toHex()
|
||||
);
|
||||
});
|
||||
|
||||
it('hash valid vote2 (actual tuple values)', (): void => {
|
||||
expect(test.votes2.toHex()).toEqual(
|
||||
createTypeUnsafe(registry, 'Vec<(u32, [(u16, u16); 1], u16)>', [votes2]).toHex()
|
||||
registry.createType('Vec<(u32, [(u16, u16); 1], u16)>', votes2).toHex()
|
||||
);
|
||||
});
|
||||
|
||||
@@ -199,7 +199,7 @@ describe('CompactAssignments', (): void => {
|
||||
|
||||
it('decodes an actual solution from the chain', (): void => {
|
||||
expect(
|
||||
createTypeUnsafe(registry, '(Vec<ValidatorIndex>, CompactAssignmentsTo257, PhragmenScore, EraIndex)', [ACTUAL]).toHex()
|
||||
registry.createType('(Vec<ValidatorIndex>, CompactAssignmentsTo257, PhragmenScore, EraIndex)', ACTUAL).toHex()
|
||||
).toEqual(ACTUAL);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -2,110 +2,145 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import type { Compact, Option, Raw, Vec, VecFixed } from '../codec';
|
||||
import type { Bytes } from '../primitive';
|
||||
import type { Bytes, Null } from '../primitive';
|
||||
import type { Codec, Constructor } from './codec';
|
||||
import type { ICompact, IEnum, INumber, IOption, IStruct, ITuple, IU8a, IVec } from './interfaces';
|
||||
import type { ICompact, IEnum, INumber, IOption, ISet, IStruct, ITuple, IU8a, IVec } from './interfaces';
|
||||
import type { InterfaceTypes } from './registry';
|
||||
|
||||
export type DetectCodec<T extends Codec, K extends string> = __Expand<__Unspace<K>, T>;
|
||||
export type DetectCodec<T extends Codec, K extends string> =
|
||||
// This is weird - it looks the wrong way around (i.e. T check should be done first), however
|
||||
// it does work (as evident with checkTypes) and is problematic the other way around
|
||||
K extends keyof InterfaceTypes
|
||||
? InterfaceTypes[K]
|
||||
: T extends ICompact | IEnum | INumber | IOption | ISet | IStruct | ITuple | IU8a | IVec
|
||||
? T
|
||||
: __ExtractCodec<__ToCodecs<__Tokenize<__Sanitize<K>>[0]>>;
|
||||
|
||||
export type DetectConstructor<T extends Codec, K extends string> = Constructor<DetectCodec<T, K>>;
|
||||
|
||||
// trim leading and trailing spaces
|
||||
export type __Unspace<K extends string> =
|
||||
K extends ` ${infer X} ` | ` ${infer X}` | `${infer X} `
|
||||
? __Unspace<X>
|
||||
: K extends `${infer A} ${infer B}`
|
||||
? __Unspace<`${A}${B}`>
|
||||
: K;
|
||||
export type __ExtractCodec<V extends Codec[]> =
|
||||
V[0] extends Codec
|
||||
? V[0]
|
||||
: Codec;
|
||||
|
||||
export type __Expand<K extends string, T extends Codec = Codec> =
|
||||
// trim leading and trailing spaces and unused portions, e.g `...;<length>]`
|
||||
export type __Sanitize<K extends string> =
|
||||
K extends ` ${infer X}` | `${infer X} ` | ` ${infer X} `
|
||||
? __Sanitize<X>
|
||||
: K extends `${infer A} ${infer B}`
|
||||
? __Sanitize<`${A}${B}`>
|
||||
: K extends `${infer A};${string}]${infer B}`
|
||||
? __Sanitize<`${A}${B}`>
|
||||
: K extends `${infer X};${string}]`
|
||||
? __Sanitize<X>
|
||||
: K;
|
||||
|
||||
export type __Value = string | Record<string, unknown> | __Value[];
|
||||
|
||||
export type __ToCodec<K extends unknown, C extends Codec[], N extends unknown[]> =
|
||||
K extends keyof InterfaceTypes
|
||||
? InterfaceTypes[K]
|
||||
: T extends ICompact | IEnum | INumber | IOption | IStruct | ITuple | IU8a | IVec
|
||||
? T
|
||||
: __Unwrap<K, T>;
|
||||
: K extends unknown[]
|
||||
? __ToTuple<__ToCodecs<K>>
|
||||
: K extends Record<string, unknown>
|
||||
? K['_enum'] extends Record<string, unknown>
|
||||
? IEnum
|
||||
: K['_set'] extends Record<string, unknown>
|
||||
? ISet
|
||||
: IStruct
|
||||
: K extends Set<unknown>
|
||||
? ISet
|
||||
: K extends '['
|
||||
? N[0] extends 'u8'
|
||||
? Raw
|
||||
: VecFixed<C[0]>
|
||||
: K extends 'Vec<'
|
||||
? N[0] extends 'u8'
|
||||
? Bytes
|
||||
: Vec<C[0]>
|
||||
: K extends 'Option<'
|
||||
? Option<C[0]>
|
||||
: K extends 'Compact<'
|
||||
? C[0] extends INumber
|
||||
? Compact<C[0]>
|
||||
: Codec
|
||||
: Codec;
|
||||
|
||||
// ensure whatever we wrap is always Compact-capable
|
||||
export type __Compact<X extends string> = __CompactImpl<__Expand<X>>;
|
||||
export type __CompactImpl<T extends Codec> =
|
||||
T extends INumber
|
||||
? Compact<T>
|
||||
: Codec;
|
||||
export type __ToTuple<O extends Codec[]> =
|
||||
O[0] extends Codec
|
||||
? O[1] extends Codec
|
||||
? ITuple<O>
|
||||
: O[0]
|
||||
: Null;
|
||||
|
||||
// handle option types
|
||||
export type __Option<X extends string> = __OptionImpl<__Expand<X>>;
|
||||
export type __OptionImpl<T extends Codec> =
|
||||
T extends Codec
|
||||
? Option<T>
|
||||
: Codec;
|
||||
export type __ToCodecsInner<K extends unknown, N extends unknown[], C extends Codec[]> =
|
||||
K extends '[' | 'Vec<' | 'Option<' | 'Compact<'
|
||||
? C extends [Codec, ...infer X]
|
||||
? [__ToCodec<K, C, N>, ...X]
|
||||
: never
|
||||
: [__ToCodec<K, C, N>, ...C];
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
export type __Struct<_ extends string> = Codec;
|
||||
export type __ToCodecs<T extends unknown[]> =
|
||||
T extends [infer K, ...infer O]
|
||||
? __ToCodecsInner<K, O, __ToCodecs<O>>
|
||||
: [];
|
||||
|
||||
export type __TupleParams<X extends string> =
|
||||
X extends `Vec<${infer A}>,${infer B}`
|
||||
? [__Vec<A>, ...__TupleParams<B>]
|
||||
: X extends `Vec<${infer A}>`
|
||||
? [__Vec<A>]
|
||||
: X extends `Option<${infer A}>,${infer B}`
|
||||
? [__Option<A>, ...__TupleParams<B>]
|
||||
: X extends `Option<${infer A}>`
|
||||
? [__Option<A>]
|
||||
: X extends `Compact<${infer A}>,${infer B}`
|
||||
? [__Compact<A>, ...__TupleParams<B>]
|
||||
: X extends `Compact<${infer A}>`
|
||||
? [__Compact<A>]
|
||||
: X extends `[${infer A}],${infer B}`
|
||||
? [__VecFixed<A>, ...__TupleParams<B>]
|
||||
: X extends `[${infer A}]`
|
||||
? [__VecFixed<A>]
|
||||
: X extends `{${infer A}},${infer B}`
|
||||
? [__Struct<A>, ...__TupleParams<B>]
|
||||
: X extends `{${infer A}}`
|
||||
? [__Struct<A>]
|
||||
// FIXME This also matches ((x, y), ... as (x, y
|
||||
: X extends `(${infer A}),${infer B}`
|
||||
? [__Tuple<A>, ...__TupleParams<B>]
|
||||
: X extends `(${infer A})`
|
||||
? [__Tuple<A>]
|
||||
: X extends `${infer A},${infer B}`
|
||||
? [__Expand<A>, ...__TupleParams<B>]
|
||||
: [__Expand<X>];
|
||||
export type __RemoveEmpty<T extends string> =
|
||||
T extends ''
|
||||
? []
|
||||
: [T];
|
||||
|
||||
export type __Tuple<X extends string> = __TupleWrap<__TupleParams<X>>;
|
||||
export type __Combine<V extends __Value[], I extends string = '', T extends string = ''> =
|
||||
[...V, ...__RemoveEmpty<I>, ...__RemoveEmpty<T>];
|
||||
|
||||
export type __TupleWrap<X extends Codec[]> =
|
||||
X[1] extends Codec
|
||||
? ITuple<X>
|
||||
: X[0];
|
||||
export type __CombineInner<V extends __Value[], I extends string, X extends __Value> =
|
||||
[...__RemoveEmpty<I>, ...V, X];
|
||||
|
||||
// vec support with short-circuit for u8
|
||||
export type __Vec<X extends string> =
|
||||
X extends 'u8'
|
||||
? Bytes
|
||||
: Vec<__Expand<X>>;
|
||||
// TODO At this point structs are fully empty, no field indicators
|
||||
export type __TokenizeStruct<T extends [__Value[], string], V extends __Value[], I extends string, R extends string> =
|
||||
__Tokenize<T[1], __CombineInner<V, I,
|
||||
R extends `"_set"${string}`
|
||||
// eslint-disable-next-line @typescript-eslint/ban-types
|
||||
? { _set: {} }
|
||||
: R extends `"_enum"${string}`
|
||||
// eslint-disable-next-line @typescript-eslint/ban-types
|
||||
? { _enum: {} }
|
||||
// eslint-disable-next-line @typescript-eslint/ban-types
|
||||
: { data: R }
|
||||
>>;
|
||||
|
||||
// fixed vec support
|
||||
export type __VecFixed<X extends string> =
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
X extends `${infer T};${infer _}`
|
||||
? T extends 'u8'
|
||||
? Raw
|
||||
: VecFixed<__Expand<T>>
|
||||
: Codec;
|
||||
export type __TokenizeTuple<T extends [__Value[], string], V extends __Value[], I extends string> =
|
||||
__Tokenize<T[1], __CombineInner<V, I, T[0]>>;
|
||||
|
||||
export type __Unwrap<K extends string, T extends Codec> =
|
||||
K extends `Vec<${infer X}>`
|
||||
? __Vec<X>
|
||||
: K extends `Option<${infer X}>`
|
||||
? __Option<X>
|
||||
: K extends `Compact<${infer X}>`
|
||||
? __Compact<X>
|
||||
: K extends `[${infer X}]`
|
||||
? __VecFixed<X>
|
||||
: K extends `(${infer X})`
|
||||
? __Tuple<X>
|
||||
: K extends `{${infer X}}`
|
||||
? __Struct<X>
|
||||
: T;
|
||||
export type __TokenizeWrapper<K extends string, V extends __Value[], I extends string, R extends string> =
|
||||
K extends `${infer X}${R}`
|
||||
? __Tokenize<R, __Combine<V, `${I}${X}`>>
|
||||
: never;
|
||||
|
||||
export type __TokenizeKnown<K extends string, V extends __Value[], I extends string, R extends string> =
|
||||
K extends `${infer X}${',' | '>'}${R}`
|
||||
? __Tokenize<R, __Combine<V, `${I}${X}`>>
|
||||
: never;
|
||||
|
||||
// NOTE For recursion limits, it is more optimal to use __Sanitize with conjunction with __Tokenize
|
||||
// below, even while we do more matching (Number of characters iterated through is the most problematic)
|
||||
export type __Tokenize<K extends string, V extends __Value[] = [], I extends string = ''> =
|
||||
K extends '' | ')' | '>' | '}'
|
||||
? [__Combine<V, I>, '']
|
||||
: K extends `${'Vec<' | 'Option<' | 'Compact<'}${infer R}`
|
||||
? __TokenizeWrapper<K, V, I, R>
|
||||
: K extends `${',' | '>'}${infer R}`
|
||||
? __Tokenize<R, __Combine<V, I>>
|
||||
: K extends `[${infer R}`
|
||||
? __Tokenize<R, __Combine<V, I, '['>>
|
||||
: K extends `)${infer R}` | `}${infer R}`
|
||||
? [__Combine<V, I>, R]
|
||||
: K extends `(${infer R}`
|
||||
? __TokenizeTuple<__Tokenize<R>, V, I>
|
||||
: K extends `{${infer R}`
|
||||
? __TokenizeStruct<__Tokenize<R>, V, I, R>
|
||||
: K extends `${keyof InterfaceTypes}${',' | '>'}${infer R}`
|
||||
? __TokenizeKnown<K, V, I, R>
|
||||
: K extends `${infer C}${infer R}`
|
||||
? __Tokenize<R, V, `${I}${C}`>
|
||||
: [__Combine<V, I>, ''];
|
||||
|
||||
@@ -15,10 +15,10 @@ export interface ICompact<T extends INumber = INumber> extends Codec {
|
||||
}
|
||||
|
||||
export interface IEnum extends Codec {
|
||||
index: number;
|
||||
isBasic: boolean
|
||||
type: string;
|
||||
value: Codec;
|
||||
readonly index: number;
|
||||
readonly isBasic: boolean
|
||||
readonly type: string;
|
||||
readonly value: Codec;
|
||||
|
||||
toNumber (): number;
|
||||
}
|
||||
@@ -31,16 +31,21 @@ export interface INumber extends Codec {
|
||||
}
|
||||
|
||||
export interface IOption<T extends Codec = Codec> extends Codec {
|
||||
isNone: boolean;
|
||||
isSome: boolean;
|
||||
value: Codec;
|
||||
readonly isNone: boolean;
|
||||
readonly isSome: boolean;
|
||||
readonly value: Codec;
|
||||
|
||||
unwrap (): T;
|
||||
unwrapOr <O>(other: O): T | O;
|
||||
unwrapOrDefault (): T;
|
||||
}
|
||||
|
||||
export interface IStruct extends Map<unknown, Codec>, Codec {}
|
||||
export interface ISet<K extends string = string> extends Set<K>, Codec {
|
||||
readonly strings: string[];
|
||||
readonly valueEncoded: BN;
|
||||
}
|
||||
|
||||
export interface IStruct<K extends string | number | symbol = string, V extends Codec = Codec> extends Map<K, V>, Codec {}
|
||||
|
||||
// A type alias for [Type1, Type2] & Codec, representing a tuple (Type1, Type2)
|
||||
// FIXME Implement this generic <Sub> on Tuple.ts itself.
|
||||
@@ -55,9 +60,9 @@ export interface IU8a extends Uint8Array, Codec {
|
||||
export interface IVec<S extends Codec = Codec> extends Array<S>, Codec {}
|
||||
|
||||
export interface IKeyringPair {
|
||||
address: string;
|
||||
addressRaw: Uint8Array;
|
||||
publicKey: Uint8Array;
|
||||
readonly address: string;
|
||||
readonly addressRaw: Uint8Array;
|
||||
readonly publicKey: Uint8Array;
|
||||
|
||||
sign: (data: Uint8Array, options?: SignOptions) => Uint8Array;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user