CreateType is typed, add createTypeUnsafe just in case (#1199)

* Use unsafe internally

* Add comments

* Remove <> in createType

* Fix lint

* Fix classOf

* Remove createType from Text

* Fix lint

* Fix Fallback type

* Fix fallback in createType
This commit is contained in:
Amaury Martiny
2019-07-26 14:00:25 +02:00
committed by Jaco Greeff
parent ec0b9a84a3
commit 441148889d
24 changed files with 112 additions and 104 deletions
+3 -3
View File
@@ -20,9 +20,9 @@ import { drr } from '../util/drr';
type Result = [AccountId | undefined, BlockNumber | undefined, [Balance?, Balance?, BalanceLock[]?, Option<VestingSchedule>?, Index?]];
const EMPTY_ACCOUNT = new AccountId();
const ZERO = createType<Balance>('Balance', 0);
const ZERO = createType('Balance', 0);
function calcBalances ([accountId = EMPTY_ACCOUNT, bestNumber = ZERO, [freeBalance = ZERO, reservedBalance = ZERO, locks = [], vesting = new Option<VestingSchedule>(ClassOf<VestingSchedule>('VestingSchedule'), null), accountNonce = ZERO]]: Result): DerivedBalances {
function calcBalances ([accountId = EMPTY_ACCOUNT, bestNumber = ZERO, [freeBalance = ZERO, reservedBalance = ZERO, locks = [], vesting = new Option<VestingSchedule>(ClassOf('VestingSchedule'), null), accountNonce = ZERO]]: Result): DerivedBalances {
let lockedBalance = ZERO;
if (Array.isArray(locks)) {
@@ -35,7 +35,7 @@ function calcBalances ([accountId = EMPTY_ACCOUNT, bestNumber = ZERO, [freeBalan
}
// offset = balance locked at genesis, perBlock is the unlock amount
const { offset, perBlock } = vesting.unwrapOr(createType<VestingSchedule>('VestingSchedule'));
const { offset, perBlock } = vesting.unwrapOr(createType('VestingSchedule'));
const vestedNow: BN = perBlock.mul(bestNumber);
const vestedBalance: BN = vestedNow.gt(offset)
? freeBalance
@@ -2,8 +2,6 @@
// This software may be modified and distributed under the terms
// of the Apache-2.0 license. See the LICENSE file for details.
import { Balance } from '@polkadot/types/interfaces';
import BN from 'bn.js';
import { combineLatest, Observable, of } from 'rxjs';
import { map, switchMap } from 'rxjs/operators';
@@ -28,7 +26,7 @@ export function referendumVotesFor (api: ApiInterfaceRx): (referendumId: BN | nu
map(([votersFor, votes, balances]): DerivedReferendumVote[] =>
votersFor.map((accountId, index): DerivedReferendumVote => ({
accountId,
balance: balances[index].votingBalance || createType<Balance>('Balance', 0),
balance: balances[index].votingBalance || createType('Balance', 0),
vote: votes[index] || new Vote(0)
} as unknown as DerivedReferendumVote))
),
@@ -44,7 +44,7 @@ export function voterPositions (api: ApiInterfaceRx): () => Observable<DerivedVo
result[accountId.toString()] = {
globalIndex: setSize.muln(setIndex).addn(index),
index: new BN(index),
setIndex: createType<SetIndex>('SetIndex', setIndex)
setIndex: createType('SetIndex', setIndex)
};
}
});
+1 -1
View File
@@ -58,7 +58,7 @@ function calculateUnlocking (stakingLedger: StakingLedger | undefined, eraLength
const groupedResult = groupByEra(unlockingChunks);
const results = Object.entries(groupedResult).map(([eraString, value]): { value: BN; remainingBlocks: BN } => ({
value,
remainingBlocks: remainingBlocks(createType<BlockNumber>('BlockNumber', eraString), eraLength, bestNumber)
remainingBlocks: remainingBlocks(createType('BlockNumber', eraString), eraLength, bestNumber)
}));
return results.length ? results : undefined;
@@ -13,7 +13,7 @@ import { AnyJsonObject } from '@polkadot/types/types';
* @description
* A [[ReferendumInfo]] with an additional `index` field
*/
export default class ReferendumInfoExtended extends ClassOf<ReferendumInfo>('ReferendumInfo') {
export default class ReferendumInfoExtended extends ClassOf('ReferendumInfo') {
private _index: ReferendumIndex;
public constructor (value: ReferendumInfo | ReferendumInfoExtended, index?: BN | number) {
@@ -21,7 +21,7 @@ export default class ReferendumInfoExtended extends ClassOf<ReferendumInfo>('Ref
this._index = value instanceof ReferendumInfoExtended
? value.index
: createType<ReferendumIndex>('ReferendumIndex', index);
: createType('ReferendumIndex', index);
}
/**
@@ -4,7 +4,7 @@
import { Constants, ConstantCodec, ModuleConstants } from '../types';
import { createType } from '@polkadot/types';
import { createTypeUnsafe } from '@polkadot/types/codec/createType';
import Metadata from '@polkadot/types/Metadata';
import { stringCamelCase } from '@polkadot/util';
@@ -23,7 +23,7 @@ export default function fromMetadata (metadata: Metadata): Constants {
// For access, we change the index names, i.e. Democracy.EnactmentPeriod -> democracy.enactmentPeriod
result[stringCamelCase(name.toString())] = moduleMetadata.constants.reduce((newModule, meta): ModuleConstants => {
const codec = createType(meta.type, meta.value);
const codec = createTypeUnsafe(meta.type.toString(), meta.value);
// This is not a perfect idea, however as it stands with number-only constants on the metadata
// does not have any effect. However, this could become problematic in cases where items are
@@ -5,7 +5,8 @@
import { Codec } from '@polkadot/types/types';
import BN from 'bn.js';
import { createType, Bytes, Compact, StorageKey, U8a } from '@polkadot/types';
import { Bytes, Compact, StorageKey, U8a } from '@polkadot/types';
import { createTypeUnsafe } from '@polkadot/types/codec/createType';
import { PlainType, StorageEntryMetadata, StorageEntryModifier, StorageEntryType } from '@polkadot/types/Metadata/v6/Storage';
import { StorageEntry } from '@polkadot/types/primitive/StorageKey';
import { assert, isNull, isUndefined, stringLowerFirst, stringToU8a, u8aConcat } from '@polkadot/util';
@@ -69,9 +70,9 @@ export default function createFunction ({ meta, method, prefix, section }: Creat
const [key1, key2] = arg as [CreateArgType, CreateArgType];
const type1 = meta.type.asDoubleMap.key1.toString();
const type2 = meta.type.asDoubleMap.key2.toString();
const param1Encoded = u8aConcat(key, createType(type1, key1).toU8a(true));
const param1Encoded = u8aConcat(key, createTypeUnsafe(type1, key1).toU8a(true));
const param1Hashed = hasher(param1Encoded);
const param2Hashed = key2Hasher(createType(type2, key2).toU8a(true));
const param2Hashed = key2Hasher(createTypeUnsafe(type2, key2).toU8a(true));
return Compact.addLengthPrefix(u8aConcat(param1Hashed, param2Hashed));
}
@@ -80,7 +81,7 @@ export default function createFunction ({ meta, method, prefix, section }: Creat
assert(!isUndefined(arg) && !isNull(arg), `${meta.name} expects one argument`);
const type = meta.type.asMap.key.toString();
const param = createType(type, arg).toU8a();
const param = createTypeUnsafe(type, arg).toU8a();
key = u8aConcat(key, param);
}
@@ -111,7 +112,7 @@ export default function createFunction ({ meta, method, prefix, section }: Creat
name: meta.name,
modifier: new StorageEntryModifier('Required'),
type: new StorageEntryType(new PlainType(meta.type.asMap.key), 0),
fallback: new Bytes(createType(meta.type.asMap.key).toHex()),
fallback: new Bytes(createTypeUnsafe(meta.type.asMap.key.toString()).toHex()),
documentation: meta.documentation
});
+8 -8
View File
@@ -24,7 +24,7 @@ export interface ISubmittableResult {
readonly isError: boolean;
readonly isFinalized: boolean;
findRecord (section: string, method: string): EventRecord | undefined;
findRecord(section: string, method: string): EventRecord | undefined;
}
export type SumbitableResultResult<ApiType> =
@@ -93,17 +93,17 @@ export class SubmittableResult implements ISubmittableResult {
}
export interface SubmittableExtrinsic<ApiType> extends IExtrinsic {
send (): SumbitableResultResult<ApiType>;
send(): SumbitableResultResult<ApiType>;
send (statusCb: Callback<ISubmittableResult>): SumbitableResultSubscription<ApiType>;
send(statusCb: Callback<ISubmittableResult>): SumbitableResultSubscription<ApiType>;
sign (account: IKeyringPair, _options: Partial<SignatureOptions>): this;
sign(account: IKeyringPair, _options: Partial<SignatureOptions>): this;
signAndSend (account: IKeyringPair | string | AccountId | Address, options?: Partial<SignerOptions>): SumbitableResultResult<ApiType>;
signAndSend(account: IKeyringPair | string | AccountId | Address, options?: Partial<SignerOptions>): SumbitableResultResult<ApiType>;
signAndSend (account: IKeyringPair | string | AccountId | Address, statusCb: Callback<ISubmittableResult>): SumbitableResultSubscription<ApiType>;
signAndSend(account: IKeyringPair | string | AccountId | Address, statusCb: Callback<ISubmittableResult>): SumbitableResultSubscription<ApiType>;
signAndSend (account: IKeyringPair | string | AccountId | Address, options: Partial<SignerOptions>, statusCb?: Callback<ISubmittableResult>): SumbitableResultSubscription<ApiType>;
signAndSend(account: IKeyringPair | string | AccountId | Address, options: Partial<SignerOptions>, statusCb?: Callback<ISubmittableResult>): SumbitableResultSubscription<ApiType>;
}
export default function createSubmittableExtrinsic<ApiType> (
@@ -251,7 +251,7 @@ export default function createSubmittableExtrinsic<ApiType> (
// if we have a nonce already, don't retrieve the latest, use what is there
isUndefined(options.nonce)
? api.query.system.accountNonce<Index>(address)
: of(createType<Index>('Index', options.nonce)),
: of(createType('Index', options.nonce)),
// if we have an era provided already or eraLength is <= 0 (immortal)
// don't get the latest block, just pass null, handle in mergeMap
(isUndefined(options.era) || (isNumber(options.era) && options.era > 0))
+7 -3
View File
@@ -12,6 +12,7 @@ import { ConstantCodec } from '@polkadot/api-metadata/consts/types';
import testKeyring from '@polkadot/keyring/testingPairs';
import { IExtrinsic, IMethod } from '@polkadot/types/types';
import { createType, Header } from '@polkadot/types';
import { createTypeUnsafe } from '@polkadot/types/codec/createType';
import { SubmittableResult } from './';
@@ -95,8 +96,11 @@ export default async function test (): Promise<void> {
// check correct types with `createType`
const balance = createType('Balance', 2);
const overridden = createType<Index>('Gas', 2);
const gas = createType('Gas', 2);
const vec = createType('Vec<u32>', 2);
console.log(balance, overridden, gas, vec);
const compact = createType('Compact<u32>', 2);
// const random = createType('RandomType', 2); // This one should deliberately show a TS error
const gasUnsafe = createTypeUnsafe('Gas', 2);
const overriddenUnsafe = createTypeUnsafe<Header>('Gas', 2);
console.log(balance, gas, compact, gasUnsafe, overriddenUnsafe);
}
@@ -159,7 +159,7 @@ describeE2E({
// assume the account Alice is only used in test(the balance of Alice does not change in this test case)
const key = api.query.balances.freeBalance.key(keyring.alice_stash.address);
const balanceData = await api.rpc.state.getStorage(key) as Option<any>;
const balanceRPC = createType<Balance>('Balance', balanceData.unwrapOr(undefined));
const balanceRPC = createType('Balance', balanceData.unwrapOr(undefined));
const balance = await api.query.balances.freeBalance(keyring.alice_stash.address);
@@ -128,7 +128,7 @@ describeE2E()('Promise e2e queries', (wsUrl): void => {
it('gets correct key', async (): Promise<void> => {
const key = api.query.session.currentIndex.key();
const sessionIndexData = await api.rpc.state.getStorage(key) as Option<any>;
const sessionIndexRPC = createType<SessionIndex>('SessionIndex', sessionIndexData.unwrapOr(undefined));
const sessionIndexRPC = createType('SessionIndex', sessionIndexData.unwrapOr(undefined));
expect(sessionIndexRPC.toNumber()).toBeGreaterThanOrEqual(0);
});
+8 -7
View File
@@ -12,7 +12,8 @@ import memoizee from 'memoizee';
import { combineLatest, from, Observable, Observer, of, throwError } from 'rxjs';
import { catchError, map, publishReplay, refCount, switchMap } from 'rxjs/operators';
import interfaces from '@polkadot/jsonrpc';
import { ClassOf, Option, StorageKey, Vec, createClass, createType } from '@polkadot/types';
import { ClassOf, Option, StorageKey, Vec, createClass } from '@polkadot/types';
import { createTypeUnsafe } from '@polkadot/types/codec/createType';
import { ExtError, assert, isFunction, isNull, isNumber, logger } from '@polkadot/util';
const l = logger('rpc-core');
@@ -261,12 +262,12 @@ export default class Rpc implements RpcInterface {
assert(inputs.length >= reqArgCount && inputs.length <= method.params.length, `Expected ${method.params.length} parameters${optText}, ${inputs.length} found instead`);
return inputs.map((input, index): Codec =>
createType(method.params[index].type, input)
createTypeUnsafe(method.params[index].type, input)
);
}
private formatOutput (method: RpcMethod, params: Codec[], result?: any): Codec | (Codec | null | undefined)[] {
const base = createType(method.type as string, result);
const base = createTypeUnsafe(method.type as string, result);
if (method.type === 'StorageData') {
const key = params[0] as StorageKey;
@@ -309,11 +310,11 @@ export default class Rpc implements RpcInterface {
if (meta.modifier.isOptional) {
return new Option(
createClass(type),
isNull ? null : createType(type, base, true)
isNull ? null : createTypeUnsafe(type, base, true)
);
}
return createType(type, isNull ? meta.fallback : base, true);
return createTypeUnsafe(type, isNull ? meta.fallback : base, true);
}
private formatStorageSet (key: StorageKey, base: StorageChangeSet): Codec {
@@ -336,10 +337,10 @@ export default class Rpc implements RpcInterface {
if (meta.modifier.isOptional) {
return new Option(
createClass(type),
value.isNone ? null : createType(type, value.unwrap(), true)
value.isNone ? null : createTypeUnsafe(type, value.unwrap(), true)
);
}
return createType(type, value.unwrapOr(meta.fallback), true);
return createTypeUnsafe(type, value.unwrapOr(meta.fallback), true);
}
}
+2 -2
View File
@@ -6,7 +6,7 @@ import '../../injector';
import extrinsicsFromMeta from '@polkadot/api-metadata/extrinsics/fromMetadata';
import createType from '../../codec/createType';
import { createTypeUnsafe } from '../../codec/createType';
import Metadata from '../Metadata';
import Method from '../../primitive/Method';
import { MetadataInterface } from '../types';
@@ -62,7 +62,7 @@ export function defaultValues (rpcData: string): void {
mod.storage.unwrap().items.forEach(({ fallback, name, type }): void => {
it(`creates default types for ${mod.name}.${name}, type ${type}`, (): void => {
expect(
(): Codec => createType(type.toString(), fallback)
(): Codec => createTypeUnsafe(type.toString(), fallback)
).not.toThrow();
});
});
+3 -5
View File
@@ -4,8 +4,6 @@
import '../injector';
import { Balance, BlockNumber } from '../interfaces/runtime';
import BN from 'bn.js';
import createType, { ClassOf } from '../codec/createType';
@@ -66,7 +64,7 @@ describe('Compact', (): void => {
it('encondes a large balance', (): void => {
expect(
Compact.encodeU8a(createType<Balance>('Balance', '0x5af3107a4000'))
Compact.encodeU8a(createType('Balance', '0x5af3107a4000'))
).toEqual(
new Uint8Array([
3 + ((6 - 4) << 2),
@@ -111,13 +109,13 @@ describe('Compact', (): void => {
describe('constructor', (): void => {
it('has the correct bitLength for constructor values (BlockNumber)', (): void => {
expect(
new Compact(ClassOf<BlockNumber>('BlockNumber'), 0xfffffff9).bitLength()
new Compact(ClassOf('BlockNumber'), 0xfffffff9).bitLength()
).toEqual(64);
});
it('has the correct encodedLength for constructor values (BlockNumber)', (): void => {
expect(
new Compact(ClassOf<BlockNumber>('BlockNumber'), 0xfffffff9).encodedLength
new Compact(ClassOf('BlockNumber'), 0xfffffff9).encodedLength
).toEqual(5);
});
+2 -2
View File
@@ -10,7 +10,7 @@ import extrinsics from '@polkadot/api-metadata/extrinsics/static';
import AccountId from '../primitive/AccountId';
import Method from '../primitive/Method';
import Text from '../primitive/Text';
import createType from './createType';
import { createTypeUnsafe } from './createType';
import Vec from './Vec';
import Tuple from './Tuple';
@@ -53,7 +53,7 @@ describe('Vec', (): void => {
});
it.skip('decodes a complex type via construction', (): void => {
const test = createType('Vec<(PropIndex, Proposal, AccountId)>', new Uint8Array([
const test = createTypeUnsafe('Vec<(PropIndex, Proposal, AccountId)>', new Uint8Array([
4, 10, 0, 0, 0, 0, 3, 80, 123, 10, 9, 34, 48, 120, 52, 50, 34, 58, 32, 34, 48, 120, 52, 51, 34, 10, 125, 10, 209, 114, 167, 76, 218, 76, 134, 89, 18, 195, 43, 160, 168, 10, 87, 174, 105, 171, 174, 65, 14, 92, 203, 89, 222, 232, 78, 47, 68, 50, 219, 79
]));
const first = (test as Vec<Codec>)[0] as Tuple;
+10 -25
View File
@@ -6,8 +6,7 @@ import '../injector';
import { Codec, Constructor } from '../types';
import createType, { TypeDef, TypeDefInfo, createClass, getTypeClass, getTypeDef, typeSplit, ClassOf } from './createType';
import Text from '../primitive/Text';
import createType, { TypeDef, TypeDefInfo, createClass, createTypeUnsafe, getTypeClass, getTypeDef, typeSplit, ClassOf } from './createType';
import CodecSet from './Set';
describe('typeSplit', (): void => {
@@ -221,26 +220,12 @@ describe('createClass', (): void => {
expect(a).toBe(b);
});
it('should memoize from Text', (): void => {
const a = createClass(new Text('TrieId'));
const b = createClass(new Text('TrieId'));
expect(a).toBe(b);
});
it('should memoize from string/Text combo', (): void => {
const a = createClass(new Text('AuthorityWeight'));
const b = createClass('AuthorityWeight');
expect(a).toBe(b);
});
});
describe('createType', (): void => {
it('allows creation of a Struct', (): void => {
expect(
createType('{"balance":"Balance","index":"u32"}', {
createTypeUnsafe('{"balance":"Balance","index":"u32"}', {
balance: 1234,
index: '0x10'
}).toJSON()
@@ -252,31 +237,31 @@ describe('createType', (): void => {
it('allows creation of a Enum (simple)', (): void => {
expect(
createType('{"_enum": ["A", "B", "C"]}', 1).toJSON()
createTypeUnsafe('{"_enum": ["A", "B", "C"]}', 1).toJSON()
).toEqual({ B: null });
});
it('allows creation of a Enum (parametrised)', (): void => {
expect(
createType('{"_enum": {"A": null, "B": "u32", "C": null} }', 1).toJSON()
createTypeUnsafe('{"_enum": {"A": null, "B": "u32", "C": null} }', 1).toJSON()
).toEqual({ B: 0 });
});
it('allows creation of a Set', (): void => {
expect(
createType<CodecSet>('{"_set": { "A": 1, "B": 2, "C": 4, "D": 8, "E": 16, "G": 32, "H": 64, "I": 128 } }', 1 + 4 + 16 + 64).strings
createTypeUnsafe<CodecSet>('{"_set": { "A": 1, "B": 2, "C": 4, "D": 8, "E": 16, "G": 32, "H": 64, "I": 128 } }', 1 + 4 + 16 + 64).strings
).toEqual(['A', 'C', 'E', 'H']);
});
it('allows creation of a [u8; 8]', (): void => {
expect(
createType('[u8; 8]', [0x12, 0x00, 0x23, 0x00, 0x45, 0x00, 0x67, 0x00]).toHex()
createTypeUnsafe('[u8; 8]', [0x12, 0x00, 0x23, 0x00, 0x45, 0x00, 0x67, 0x00]).toHex()
).toEqual('0x1200230045006700');
});
it('allows creation of a [u16; 4]', (): void => {
expect(
createType('[u16; 4]', [0x1200, 0x2300, 0x4500, 0x6700]).toU8a()
createTypeUnsafe('[u16; 4]', [0x1200, 0x2300, 0x4500, 0x6700]).toU8a()
).toEqual(new Uint8Array([0x00, 0x12, 0x00, 0x23, 0x00, 0x45, 0x00, 0x67]));
});
@@ -284,7 +269,7 @@ describe('createType', (): void => {
const base = createType('StorageData', null);
expect(
(): Codec => createType('DoubleMap<Vec<(BlockNumber,EventIndex)>>', base, true)
(): Codec => createTypeUnsafe('DoubleMap<Vec<(BlockNumber,EventIndex)>>', base, true)
).toThrow(/ Input doesn't match output, received 0x, created 0x00/);
});
@@ -292,7 +277,7 @@ describe('createType', (): void => {
const base = createType('StorageData', null);
expect(
(): Codec => createType('Vec<(BlockNumber,EventIndex)>', base, true)
(): Codec => createTypeUnsafe('Vec<(BlockNumber,EventIndex)>', base, true)
).toThrow(/Input doesn't match output, received 0x, created 0x00/);
});
@@ -313,7 +298,7 @@ describe('createType', (): void => {
it('instanceof should work (complex type)', (): void => {
const complexType = '{"balance":"Balance","account_id":"AccountId","log":"(u64, u32)","fromSrml":"Gas"}';
const value = createType(complexType, {
const value = createTypeUnsafe(complexType, {
balance: 123,
accountId: '',
log: [456, 789],
+46 -23
View File
@@ -8,7 +8,6 @@ import { assert } from '@polkadot/util';
import { InterfaceRegistry } from '../interfaceRegistry';
import { Codec, Constructor } from '../types';
import Null from '../primitive/Null';
import Text from '../primitive/Text';
import Compact from './Compact';
import Enum from './Enum';
import Linkage from './Linkage';
@@ -22,6 +21,9 @@ import Vec from './Vec';
import VecFixed from './VecFixed';
import getRegistry from './typeRegistry';
// Type which says: if `K` is in the InterfaceRegistry, then return InterfaceRegistry[K], else fallback to T
type FromReg<T extends Codec, K extends string> = K extends keyof InterfaceRegistry ? InterfaceRegistry[K] : T
export enum TypeDefInfo {
Compact,
DoubleMap,
@@ -135,7 +137,7 @@ function _decodeSet (value: TypeDef, details: Record<string, number>): TypeDef {
return value;
}
export function getTypeDef (_type: Text | string, name?: string): TypeDef {
export function getTypeDef (_type: string, name?: string): TypeDef {
const type = _type.toString().trim();
const value: TypeDef = {
info: TypeDefInfo.Plain,
@@ -202,27 +204,33 @@ export function getTypeDef (_type: Text | string, name?: string): TypeDef {
}
// Memoized helper of the `createClass` function below
const memoizedCreateClass = memoizee(<T extends Codec = Codec, K extends Text | string = Text | string>(
const memoizedCreateClass = memoizee(<T extends Codec = Codec, K extends string = string>(
type: K
): Constructor<K extends keyof InterfaceRegistry ? InterfaceRegistry[K] : T> => {
): Constructor<FromReg<T, K>> => {
// eslint-disable-next-line @typescript-eslint/no-use-before-define
return getTypeClass<K extends keyof InterfaceRegistry ? InterfaceRegistry[K] : T>(
return getTypeClass<FromReg<T, K>>(
getTypeDef(type)
);
}, {
length: 1,
// Normalize args so that different args that should be cached
// together are cached together.
// E.g.: `createClass('abc') === createClass(new Text('abc'));`
normalizer: JSON.stringify
});
}, { length: 1 });
export function createClass<T extends Codec = Codec, K extends Text | string = Text | string> (
export function createClass<T extends Codec = Codec, K extends string = string> (
type: K
): Constructor<K extends keyof InterfaceRegistry ? InterfaceRegistry[K] : T> {
): Constructor<FromReg<T, K>> {
return memoizedCreateClass(type);
}
// An unsafe version of the `createType` below. It's unsafe because the `type`
// argument here can be any string, which, if not parseable, will yield a
// runtime error.
export function ClassOfUnsafe<T extends Codec = Codec, K extends string = string> (name: K): Constructor<FromReg<T, K>> {
return createClass<T, K>(name);
}
// alias for createClass
export function ClassOf<K extends keyof InterfaceRegistry> (name: K): Constructor<InterfaceRegistry[K]> {
return ClassOfUnsafe<Codec, K>(name) as Constructor<InterfaceRegistry[K]>;
}
// create an array of constructors from the input
export function getTypeClassMap (defs: TypeDef[]): { [index: string]: Constructor } {
return defs.reduce((result, sub): Record<string, Constructor> => {
@@ -320,12 +328,7 @@ export function getTypeClass<T extends Codec = Codec> (value: TypeDef, Fallback?
throw new Error(`Unable to determine type from ${JSON.stringify(value)}`);
}
// alias for createClass
export function ClassOf<T extends Codec = Codec, K extends Text | string = Text | string> (name: string): Constructor<T> {
return createClass<T>(name);
}
function initType<T extends Codec = Codec, K extends Text | string = Text | string> (Type: Constructor<T>, value?: any, isPedantic?: boolean): T {
function initType<T extends Codec = Codec, K extends string = string> (Type: Constructor<FromReg<T, K>>, value?: any, isPedantic?: boolean): FromReg<T, K> {
try {
const created = new Type(value);
@@ -349,21 +352,41 @@ function initType<T extends Codec = Codec, K extends Text | string = Text | stri
return created;
} catch (error) {
if (Type.Fallback) {
return initType(Type.Fallback, value, isPedantic);
return initType(Type.Fallback as Constructor<FromReg<T, K>>, value, isPedantic);
}
throw error;
}
}
export default function createType<T extends Codec = Codec, K extends Text | string = Text | string> (
// An unsafe version of the `createType` below. It's unsafe because the `type`
// argument here can be any string, which, if not parseable, will yield a
// runtime error.
export function createTypeUnsafe<T extends Codec = Codec, K extends string = string> (
type: K,
value?: any,
isPedantic?: boolean
): K extends keyof InterfaceRegistry ? InterfaceRegistry[K] : T {
): FromReg<T, K> {
try {
return initType(createClass<T, K>(type), value, isPedantic);
} catch (error) {
throw new Error(`createType(${type}):: ${error.message}`);
}
}
/**
* Create an instance of a `type` with a given `value`.
* @param type - A recognizable string representing the type to create an
* instance from
* @param value - The value to instantiate the type with
* @param isPedantic - Double-check that the input's encoding matches the
* output's one. Slower, but ensures that we have a 100% grasp on the actual
* provided value
*/
export default function createType<K extends keyof InterfaceRegistry> (
type: K,
value?: any,
isPedantic?: boolean
): InterfaceRegistry[K] {
return createTypeUnsafe<Codec, K>(type, value, isPedantic) as InterfaceRegistry[K];
}
@@ -4,8 +4,6 @@
import '../../injector';
import { KeyValue } from './types';
import createType from '../../codec/createType';
describe('KeyValue', (): void => {
@@ -34,7 +32,7 @@ describe('KeyValue', (): void => {
});
it('exposes the properties for key/value', (): void => {
const [key, value] = createType<KeyValue>('KeyValue', ['0x11223344', '0x998877665544332211']);
const [key, value] = createType('KeyValue', ['0x11223344', '0x998877665544332211']);
expect(key.toHex()).toEqual('0x11223344');
expect(value.toHex()).toEqual('0x998877665544332211');
+1 -1
View File
@@ -119,7 +119,7 @@ export default class Event extends Struct {
section.events.unwrap().forEach((meta, methodIndex): void => {
const methodName = meta.name.toString();
const eventIndex = new Uint8Array([sectionIndex, methodIndex]);
const typeDef = meta.args.map((arg): TypeDef => getTypeDef(arg));
const typeDef = meta.args.map((arg): TypeDef => getTypeDef(arg.toString()));
const Types = typeDef.map((typeDef): Constructor<Codec> => getTypeClass(typeDef, Unconstructable.with(typeDef)));
EventTypes[eventIndex.toString()] = class extends EventData {
@@ -123,7 +123,7 @@ export default class ExtrinsicSignatureV1 extends Struct implements IExtrinsicSi
era: era || IMMORTAL_ERA,
blockHash
});
const signature = createType<Signature>('Signature', payload.sign(account));
const signature = createType('Signature', payload.sign(account));
return this.injectSignature(signer, signature, payload);
}
@@ -128,7 +128,7 @@ export default class ExtrinsicSignatureV2 extends Struct implements IExtrinsicSi
nonce,
tip: tip || 0
});
const signature = createType<Signature>('Signature', payload.sign(account));
const signature = createType('Signature', payload.sign(account));
return this.injectSignature(signer, signature, payload);
}
+1 -1
View File
@@ -157,7 +157,7 @@ export default class Method extends Struct implements IMethod {
private static getArgsDef (meta: FunctionMetadataV7): ArgsDef {
return Method.filterOrigin(meta).reduce((result, { name, type }): ArgsDef => {
const Type = getTypeClass(
getTypeDef(type)
getTypeDef(type.toString())
);
result[name.toString()] = Type;
+2 -2
View File
@@ -32,7 +32,7 @@ export default class Vote extends U8a {
const conviction = decoded[0] & 0b01111111;
this._aye = new Bool(msb);
this._conviction = createType<Conviction>('Conviction', conviction);
this._conviction = createType('Conviction', conviction);
}
private static decodeVote (value?: any): Uint8Array {
@@ -48,7 +48,7 @@ export default class Vote extends U8a {
: new Uint8Array([0b0]);
} else if (isObject(value) && !isUndefined(value.aye) && !isUndefined(value.conviction)) {
const aye = new Bool(value.aye);
const convictionIndex = createType<Conviction>('Conviction', value.conviction).index;
const convictionIndex = createType('Conviction', value.conviction).index;
const result = convictionIndex | (aye.eq(true) ? 0b1 << 7 : 0b0);
+1 -1
View File
@@ -105,7 +105,7 @@ export interface IHash extends U8a { }
export type CodecTo = 'toHex' | 'toJSON' | 'toString' | 'toU8a';
export interface Constructor<T = Codec> {
Fallback?: Constructor<T>;
Fallback?: Constructor<Codec>;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
new(...value: any[]): T;